Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / media_galleries / media_galleries_preferences.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/media_galleries/media_galleries_preferences.h"
6
7 #include "base/base_paths_posix.h"
8 #include "base/callback.h"
9 #include "base/i18n/time_formatting.h"
10 #include "base/path_service.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/prefs/scoped_user_pref_update.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/values.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/extensions/api/media_galleries_private/media_galleries_private_api.h"
20 #include "chrome/browser/extensions/extension_service.h"
21 #include "chrome/browser/media_galleries/fileapi/iapps_finder.h"
22 #include "chrome/browser/media_galleries/fileapi/picasa_finder.h"
23 #include "chrome/browser/media_galleries/media_file_system_registry.h"
24 #include "chrome/browser/media_galleries/media_galleries_histograms.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/common/chrome_paths.h"
27 #include "chrome/common/extensions/permissions/media_galleries_permission.h"
28 #include "chrome/common/pref_names.h"
29 #include "components/storage_monitor/media_storage_util.h"
30 #include "components/storage_monitor/storage_monitor.h"
31 #include "components/user_prefs/pref_registry_syncable.h"
32 #include "content/public/browser/browser_thread.h"
33 #include "extensions/browser/extension_system.h"
34 #include "extensions/browser/pref_names.h"
35 #include "extensions/common/extension.h"
36 #include "extensions/common/extension_set.h"
37 #include "extensions/common/permissions/api_permission.h"
38 #include "extensions/common/permissions/permissions_data.h"
39 #include "grit/generated_resources.h"
40 #include "ui/base/l10n/l10n_util.h"
41 #include "ui/base/text/bytes_formatting.h"
42
43 using base::DictionaryValue;
44 using base::ListValue;
45 using extensions::ExtensionPrefs;
46
47 namespace {
48
49 // Pref key for the list of media gallery permissions.
50 const char kMediaGalleriesPermissions[] = "media_galleries_permissions";
51 // Pref key for Media Gallery ID.
52 const char kMediaGalleryIdKey[] = "id";
53 // Pref key for Media Gallery Permission Value.
54 const char kMediaGalleryHasPermissionKey[] = "has_permission";
55
56 const char kMediaGalleriesDeviceIdKey[] = "deviceId";
57 const char kMediaGalleriesDisplayNameKey[] = "displayName";
58 const char kMediaGalleriesPathKey[] = "path";
59 const char kMediaGalleriesPrefIdKey[] = "prefId";
60 const char kMediaGalleriesTypeKey[] = "type";
61 const char kMediaGalleriesVolumeLabelKey[] = "volumeLabel";
62 const char kMediaGalleriesVendorNameKey[] = "vendorName";
63 const char kMediaGalleriesModelNameKey[] = "modelName";
64 const char kMediaGalleriesSizeKey[] = "totalSize";
65 const char kMediaGalleriesLastAttachTimeKey[] = "lastAttachTime";
66 const char kMediaGalleriesScanAudioCountKey[] = "audioCount";
67 const char kMediaGalleriesScanImageCountKey[] = "imageCount";
68 const char kMediaGalleriesScanVideoCountKey[] = "videoCount";
69 const char kMediaGalleriesPrefsVersionKey[] = "preferencesVersion";
70
71 const char kMediaGalleriesTypeAutoDetectedValue[] = "autoDetected";
72 const char kMediaGalleriesTypeBlackListedValue[] = "blackListed";
73 const char kMediaGalleriesTypeRemovedScanValue[] = "removedScan";
74 const char kMediaGalleriesTypeScanResultValue[] = "scanResult";
75 const char kMediaGalleriesTypeUserAddedValue[] = "userAdded";
76
77 const char kIPhotoGalleryName[] = "iPhoto";
78 const char kITunesGalleryName[] = "iTunes";
79 const char kPicasaGalleryName[] = "Picasa";
80
81 const int kCurrentPrefsVersion = 2;
82
83 int NumberExtensionsUsingMediaGalleries(Profile* profile) {
84   int count = 0;
85   if (!profile)
86     return count;
87   ExtensionService* extension_service =
88       extensions::ExtensionSystem::Get(profile)->extension_service();
89   if (!extension_service)
90     return count;
91
92   const extensions::ExtensionSet* extensions = extension_service->extensions();
93   for (extensions::ExtensionSet::const_iterator i = extensions->begin();
94        i != extensions->end(); ++i) {
95     if (extensions::PermissionsData::HasAPIPermission(
96             *i, extensions::APIPermission::kMediaGalleries) ||
97         extensions::PermissionsData::HasAPIPermission(
98             *i, extensions::APIPermission::kMediaGalleriesPrivate)) {
99       count++;
100     }
101   }
102   return count;
103 }
104
105 bool GetPrefId(const base::DictionaryValue& dict, MediaGalleryPrefId* value) {
106   std::string string_id;
107   if (!dict.GetString(kMediaGalleriesPrefIdKey, &string_id) ||
108       !base::StringToUint64(string_id, value)) {
109     return false;
110   }
111
112   return true;
113 }
114
115 bool GetType(const base::DictionaryValue& dict,
116              MediaGalleryPrefInfo::Type* type) {
117   std::string string_type;
118   if (!dict.GetString(kMediaGalleriesTypeKey, &string_type))
119     return false;
120
121   if (string_type == kMediaGalleriesTypeUserAddedValue) {
122     *type = MediaGalleryPrefInfo::kUserAdded;
123     return true;
124   }
125   if (string_type == kMediaGalleriesTypeAutoDetectedValue) {
126     *type = MediaGalleryPrefInfo::kAutoDetected;
127     return true;
128   }
129   if (string_type == kMediaGalleriesTypeBlackListedValue) {
130     *type = MediaGalleryPrefInfo::kBlackListed;
131     return true;
132   }
133   if (string_type == kMediaGalleriesTypeScanResultValue) {
134     *type = MediaGalleryPrefInfo::kScanResult;
135     return true;
136   }
137   if (string_type == kMediaGalleriesTypeRemovedScanValue) {
138     *type = MediaGalleryPrefInfo::kRemovedScan;
139     return true;
140   }
141
142   return false;
143 }
144
145 const char* TypeToStringValue(MediaGalleryPrefInfo::Type type) {
146   const char* result = NULL;
147   switch (type) {
148     case MediaGalleryPrefInfo::kUserAdded:
149       result = kMediaGalleriesTypeUserAddedValue;
150       break;
151     case MediaGalleryPrefInfo::kAutoDetected:
152       result = kMediaGalleriesTypeAutoDetectedValue;
153       break;
154     case MediaGalleryPrefInfo::kBlackListed:
155       result = kMediaGalleriesTypeBlackListedValue;
156       break;
157     case MediaGalleryPrefInfo::kScanResult:
158       result = kMediaGalleriesTypeScanResultValue;
159       break;
160     case MediaGalleryPrefInfo::kRemovedScan:
161       result = kMediaGalleriesTypeRemovedScanValue;
162       break;
163     default:
164       NOTREACHED();
165       break;
166   }
167   return result;
168 }
169
170 bool PopulateGalleryPrefInfoFromDictionary(
171     const base::DictionaryValue& dict, MediaGalleryPrefInfo* out_gallery_info) {
172   MediaGalleryPrefId pref_id;
173   base::string16 display_name;
174   std::string device_id;
175   base::FilePath::StringType path;
176   MediaGalleryPrefInfo::Type type = MediaGalleryPrefInfo::kInvalidType;
177   base::string16 volume_label;
178   base::string16 vendor_name;
179   base::string16 model_name;
180   double total_size_in_bytes = 0.0;
181   double last_attach_time = 0.0;
182   bool volume_metadata_valid = false;
183   int audio_count = 0;
184   int image_count = 0;
185   int video_count = 0;
186   int prefs_version = 0;
187
188   if (!GetPrefId(dict, &pref_id) ||
189       !dict.GetString(kMediaGalleriesDeviceIdKey, &device_id) ||
190       !dict.GetString(kMediaGalleriesPathKey, &path) ||
191       !GetType(dict, &type)) {
192     return false;
193   }
194
195   dict.GetString(kMediaGalleriesDisplayNameKey, &display_name);
196   dict.GetInteger(kMediaGalleriesPrefsVersionKey, &prefs_version);
197
198   if (dict.GetString(kMediaGalleriesVolumeLabelKey, &volume_label) &&
199       dict.GetString(kMediaGalleriesVendorNameKey, &vendor_name) &&
200       dict.GetString(kMediaGalleriesModelNameKey, &model_name) &&
201       dict.GetDouble(kMediaGalleriesSizeKey, &total_size_in_bytes) &&
202       dict.GetDouble(kMediaGalleriesLastAttachTimeKey, &last_attach_time)) {
203     volume_metadata_valid = true;
204   }
205
206   if (dict.GetInteger(kMediaGalleriesScanAudioCountKey, &audio_count) &&
207       dict.GetInteger(kMediaGalleriesScanImageCountKey, &image_count) &&
208       dict.GetInteger(kMediaGalleriesScanVideoCountKey, &video_count)) {
209     out_gallery_info->audio_count = audio_count;
210     out_gallery_info->image_count = image_count;
211     out_gallery_info->video_count = video_count;
212   } else {
213     out_gallery_info->audio_count = 0;
214     out_gallery_info->image_count = 0;
215     out_gallery_info->video_count = 0;
216   }
217
218   out_gallery_info->pref_id = pref_id;
219   out_gallery_info->display_name = display_name;
220   out_gallery_info->device_id = device_id;
221   out_gallery_info->path = base::FilePath(path);
222   out_gallery_info->type = type;
223   out_gallery_info->volume_label = volume_label;
224   out_gallery_info->vendor_name = vendor_name;
225   out_gallery_info->model_name = model_name;
226   out_gallery_info->total_size_in_bytes = total_size_in_bytes;
227   out_gallery_info->last_attach_time =
228       base::Time::FromInternalValue(last_attach_time);
229   out_gallery_info->volume_metadata_valid = volume_metadata_valid;
230   out_gallery_info->prefs_version = prefs_version;
231
232   return true;
233 }
234
235 base::DictionaryValue* CreateGalleryPrefInfoDictionary(
236     const MediaGalleryPrefInfo& gallery) {
237   base::DictionaryValue* dict = new base::DictionaryValue();
238   dict->SetString(kMediaGalleriesPrefIdKey,
239                   base::Uint64ToString(gallery.pref_id));
240   dict->SetString(kMediaGalleriesDeviceIdKey, gallery.device_id);
241   dict->SetString(kMediaGalleriesPathKey, gallery.path.value());
242   dict->SetString(kMediaGalleriesTypeKey, TypeToStringValue(gallery.type));
243
244   if (gallery.volume_metadata_valid) {
245     dict->SetString(kMediaGalleriesVolumeLabelKey, gallery.volume_label);
246     dict->SetString(kMediaGalleriesVendorNameKey, gallery.vendor_name);
247     dict->SetString(kMediaGalleriesModelNameKey, gallery.model_name);
248     dict->SetDouble(kMediaGalleriesSizeKey, gallery.total_size_in_bytes);
249     dict->SetDouble(kMediaGalleriesLastAttachTimeKey,
250                     gallery.last_attach_time.ToInternalValue());
251   } else {
252     dict->SetString(kMediaGalleriesDisplayNameKey, gallery.display_name);
253   }
254
255   if (gallery.audio_count || gallery.image_count || gallery.video_count) {
256     dict->SetInteger(kMediaGalleriesScanAudioCountKey, gallery.audio_count);
257     dict->SetInteger(kMediaGalleriesScanImageCountKey, gallery.image_count);
258     dict->SetInteger(kMediaGalleriesScanVideoCountKey, gallery.video_count);
259   }
260
261   // Version 0 of the prefs format was that the display_name was always
262   // used to show the user-visible name of the gallery. Version 1 means
263   // that there is an optional display_name, and when it is present, it
264   // overrides the name that would be built from the volume metadata, path,
265   // or whatever other data. So if we see a display_name with version 0, it
266   // means it may be overwritten simply by getting new volume metadata.
267   // A display_name with version 1 should not be overwritten.
268   dict->SetInteger(kMediaGalleriesPrefsVersionKey, gallery.prefs_version);
269
270   return dict;
271 }
272
273 bool HasAutoDetectedGalleryPermission(const extensions::Extension& extension) {
274   extensions::MediaGalleriesPermission::CheckParam param(
275       extensions::MediaGalleriesPermission::kAllAutoDetectedPermission);
276   return extensions::PermissionsData::CheckAPIPermissionWithParam(
277       &extension, extensions::APIPermission::kMediaGalleries, &param);
278 }
279
280 // Retrieves the MediaGalleryPermission from the given dictionary; DCHECKs on
281 // failure.
282 bool GetMediaGalleryPermissionFromDictionary(
283     const base::DictionaryValue* dict,
284     MediaGalleryPermission* out_permission) {
285   std::string string_id;
286   if (dict->GetString(kMediaGalleryIdKey, &string_id) &&
287       base::StringToUint64(string_id, &out_permission->pref_id) &&
288       dict->GetBoolean(kMediaGalleryHasPermissionKey,
289                        &out_permission->has_permission)) {
290     return true;
291   }
292   NOTREACHED();
293   return false;
294 }
295
296 base::string16 GetDisplayNameForDevice(uint64 storage_size_in_bytes,
297                                        const base::string16& name) {
298   DCHECK(!name.empty());
299   return (storage_size_in_bytes == 0) ?
300       name :
301       ui::FormatBytes(storage_size_in_bytes) + base::ASCIIToUTF16(" ") + name;
302 }
303
304 // For a device with |device_name| and a relative path |sub_folder|, construct
305 // a display name. If |sub_folder| is empty, then just return |device_name|.
306 base::string16 GetDisplayNameForSubFolder(const base::string16& device_name,
307                                           const base::FilePath& sub_folder) {
308   if (sub_folder.empty())
309     return device_name;
310   return (sub_folder.BaseName().LossyDisplayName() +
311           base::ASCIIToUTF16(" - ") +
312           device_name);
313 }
314
315 base::string16 GetFullProductName(const base::string16& vendor_name,
316                                   const base::string16& model_name) {
317   if (vendor_name.empty() && model_name.empty())
318     return base::string16();
319
320   base::string16 product_name;
321   if (vendor_name.empty())
322     product_name = model_name;
323   else if (model_name.empty())
324     product_name = vendor_name;
325   else if (!vendor_name.empty() && !model_name.empty())
326     product_name = vendor_name + base::UTF8ToUTF16(", ") + model_name;
327
328   return product_name;
329 }
330
331 }  // namespace
332
333 MediaGalleryPrefInfo::MediaGalleryPrefInfo()
334     : pref_id(kInvalidMediaGalleryPrefId),
335       type(kInvalidType),
336       total_size_in_bytes(0),
337       volume_metadata_valid(false),
338       audio_count(0),
339       image_count(0),
340       video_count(0),
341       prefs_version(0) {
342 }
343
344 MediaGalleryPrefInfo::~MediaGalleryPrefInfo() {}
345
346 base::FilePath MediaGalleryPrefInfo::AbsolutePath() const {
347   base::FilePath base_path = MediaStorageUtil::FindDevicePathById(device_id);
348   DCHECK(!path.IsAbsolute());
349   return base_path.empty() ? base_path : base_path.Append(path);
350 }
351
352 bool MediaGalleryPrefInfo::IsBlackListedType() const {
353   return type == kBlackListed || type == kRemovedScan;
354 }
355
356 base::string16 MediaGalleryPrefInfo::GetGalleryDisplayName() const {
357   if (!StorageInfo::IsRemovableDevice(device_id)) {
358     // For fixed storage, the default name is the fully qualified directory
359     // name, or in the case of a root directory, the root directory name.
360     // Exception: ChromeOS -- the full pathname isn't visible there, so only
361     // the directory name is used.
362     base::FilePath path = AbsolutePath();
363     if (!display_name.empty())
364       return display_name;
365
366 #if defined(OS_CHROMEOS)
367     // See chrome/browser/chromeos/fileapi/file_system_backend.cc
368     base::FilePath download_path;
369     if (PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS_SAFE, &download_path)) {
370       base::FilePath relative;
371       if (download_path.AppendRelativePath(path, &relative))
372         return relative.LossyDisplayName();
373     }
374     return path.BaseName().LossyDisplayName();
375 #else
376     return path.LossyDisplayName();
377 #endif
378   }
379
380   base::string16 name = display_name;
381   if (name.empty())
382     name = volume_label;
383   if (name.empty())
384     name = GetFullProductName(vendor_name, model_name);
385   if (name.empty())
386     name = l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_UNLABELED_DEVICE);
387
388   name = GetDisplayNameForDevice(total_size_in_bytes, name);
389
390   if (!path.empty())
391     name = GetDisplayNameForSubFolder(name, path);
392
393   return name;
394 }
395
396 base::string16 MediaGalleryPrefInfo::GetGalleryTooltip() const {
397   return AbsolutePath().LossyDisplayName();
398 }
399
400 base::string16 MediaGalleryPrefInfo::GetGalleryAdditionalDetails() const {
401   base::string16 attached;
402   if (StorageInfo::IsRemovableDevice(device_id)) {
403     if (MediaStorageUtil::IsRemovableStorageAttached(device_id)) {
404       attached = l10n_util::GetStringUTF16(
405           IDS_MEDIA_GALLERIES_DIALOG_DEVICE_ATTACHED);
406     } else if (!last_attach_time.is_null()) {
407       attached = l10n_util::GetStringFUTF16(
408           IDS_MEDIA_GALLERIES_LAST_ATTACHED,
409           base::TimeFormatShortDateNumeric(last_attach_time));
410     } else {
411       attached = l10n_util::GetStringUTF16(
412           IDS_MEDIA_GALLERIES_DIALOG_DEVICE_NOT_ATTACHED);
413     }
414   }
415
416   return attached;
417 }
418
419 bool MediaGalleryPrefInfo::IsGalleryAvailable() const {
420   return !StorageInfo::IsRemovableDevice(device_id) ||
421          MediaStorageUtil::IsRemovableStorageAttached(device_id);
422 }
423
424 MediaGalleriesPreferences::GalleryChangeObserver::~GalleryChangeObserver() {}
425
426 MediaGalleriesPreferences::MediaGalleriesPreferences(Profile* profile)
427     : initialized_(false),
428       pre_initialization_callbacks_waiting_(0),
429       profile_(profile),
430       extension_prefs_for_testing_(NULL),
431       weak_factory_(this) {
432 }
433
434 MediaGalleriesPreferences::~MediaGalleriesPreferences() {
435   if (StorageMonitor::GetInstance())
436     StorageMonitor::GetInstance()->RemoveObserver(this);
437 }
438
439 void MediaGalleriesPreferences::EnsureInitialized(base::Closure callback) {
440   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
441
442   if (IsInitialized()) {
443     if (!callback.is_null())
444       callback.Run();
445     return;
446   }
447
448   on_initialize_callbacks_.push_back(callback);
449   if (on_initialize_callbacks_.size() > 1)
450     return;
451
452   // This counter must match the number of async methods dispatched below.
453   // It cannot be incremented inline with each callback, as some may return
454   // synchronously, decrement the counter to 0, and prematurely trigger
455   // FinishInitialization.
456   pre_initialization_callbacks_waiting_ = 4;
457
458   // Check whether we should be initializing -- are there any extensions that
459   // are using media galleries?
460   media_galleries::UsageCount(media_galleries::PREFS_INITIALIZED);
461   if (NumberExtensionsUsingMediaGalleries(profile_) == 0) {
462     media_galleries::UsageCount(media_galleries::PREFS_INITIALIZED_ERROR);
463   }
464
465   // We determine the freshness of the profile here, before any of the finders
466   // return and add media galleries to it.
467   StorageMonitor::GetInstance()->EnsureInitialized(
468       base::Bind(&MediaGalleriesPreferences::OnStorageMonitorInit,
469                  weak_factory_.GetWeakPtr(),
470                  !APIHasBeenUsed(profile_) /* add_default_galleries */));
471
472   // Look for optional default galleries every time.
473   iapps::FindITunesLibrary(
474       base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID,
475                  weak_factory_.GetWeakPtr()));
476
477   picasa::FindPicasaDatabase(
478       base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID,
479                  weak_factory_.GetWeakPtr()));
480
481   iapps::FindIPhotoLibrary(
482       base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID,
483                  weak_factory_.GetWeakPtr()));
484 }
485
486 bool MediaGalleriesPreferences::IsInitialized() const { return initialized_; }
487
488 Profile* MediaGalleriesPreferences::profile() { return profile_; }
489
490 void MediaGalleriesPreferences::OnInitializationCallbackReturned() {
491   DCHECK(!IsInitialized());
492   DCHECK_GT(pre_initialization_callbacks_waiting_, 0);
493   if (--pre_initialization_callbacks_waiting_ == 0)
494     FinishInitialization();
495 }
496
497 void MediaGalleriesPreferences::FinishInitialization() {
498   DCHECK(!IsInitialized());
499
500   initialized_ = true;
501
502   StorageMonitor* monitor = StorageMonitor::GetInstance();
503   DCHECK(monitor->IsInitialized());
504
505   InitFromPrefs();
506
507   StorageMonitor::GetInstance()->AddObserver(this);
508
509   std::vector<StorageInfo> existing_devices =
510       monitor->GetAllAvailableStorages();
511   for (size_t i = 0; i < existing_devices.size(); i++) {
512     if (!(StorageInfo::IsMediaDevice(existing_devices[i].device_id()) &&
513           StorageInfo::IsRemovableDevice(existing_devices[i].device_id())))
514       continue;
515     AddGallery(existing_devices[i].device_id(),
516                base::FilePath(),
517                MediaGalleryPrefInfo::kAutoDetected,
518                existing_devices[i].storage_label(),
519                existing_devices[i].vendor_name(),
520                existing_devices[i].model_name(),
521                existing_devices[i].total_size_in_bytes(),
522                base::Time::Now(), 0, 0, 0);
523   }
524
525   for (std::vector<base::Closure>::iterator iter =
526            on_initialize_callbacks_.begin();
527        iter != on_initialize_callbacks_.end();
528        ++iter) {
529     iter->Run();
530   }
531   on_initialize_callbacks_.clear();
532 }
533
534 void MediaGalleriesPreferences::AddDefaultGalleries() {
535   const int kDirectoryKeys[] = {
536     chrome::DIR_USER_MUSIC,
537     chrome::DIR_USER_PICTURES,
538     chrome::DIR_USER_VIDEOS,
539   };
540
541   for (size_t i = 0; i < arraysize(kDirectoryKeys); ++i) {
542     base::FilePath path;
543     if (!PathService::Get(kDirectoryKeys[i], &path))
544       continue;
545
546     base::FilePath relative_path;
547     StorageInfo info;
548     if (MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) {
549       AddGalleryInternal(info.device_id(), info.name(), relative_path,
550                          MediaGalleryPrefInfo::kAutoDetected,
551                          info.storage_label(), info.vendor_name(),
552                          info.model_name(), info.total_size_in_bytes(),
553                          base::Time(), true, 0, 0, 0, kCurrentPrefsVersion);
554     }
555   }
556 }
557
558 bool MediaGalleriesPreferences::UpdateDeviceIDForSingletonType(
559     const std::string& device_id) {
560   StorageInfo::Type singleton_type;
561   if (!StorageInfo::CrackDeviceId(device_id, &singleton_type, NULL))
562     return false;
563
564   PrefService* prefs = profile_->GetPrefs();
565   scoped_ptr<ListPrefUpdate> update(new ListPrefUpdate(
566       prefs, prefs::kMediaGalleriesRememberedGalleries));
567   base::ListValue* list = update->Get();
568   for (base::ListValue::iterator iter = list->begin();
569        iter != list->end(); ++iter) {
570     // All of these calls should succeed, but preferences file can be corrupt.
571     base::DictionaryValue* dict;
572     if (!(*iter)->GetAsDictionary(&dict))
573       continue;
574     std::string this_device_id;
575     if (!dict->GetString(kMediaGalleriesDeviceIdKey, &this_device_id))
576       continue;
577     if (this_device_id == device_id)
578       return true;  // No update is necessary.
579     StorageInfo::Type device_type;
580     if (!StorageInfo::CrackDeviceId(this_device_id, &device_type, NULL))
581       continue;
582
583     if (device_type == singleton_type) {
584       dict->SetString(kMediaGalleriesDeviceIdKey, device_id);
585       update.reset();  // commits the update.
586       InitFromPrefs();
587       MediaGalleryPrefId pref_id;
588       if (GetPrefId(*dict, &pref_id)) {
589         FOR_EACH_OBSERVER(GalleryChangeObserver,
590                           gallery_change_observers_,
591                           OnGalleryInfoUpdated(this, pref_id));
592       }
593       return true;
594     }
595   }
596   return false;
597 }
598
599 void MediaGalleriesPreferences::OnStorageMonitorInit(
600     bool add_default_galleries) {
601   if (add_default_galleries)
602     AddDefaultGalleries();
603   OnInitializationCallbackReturned();
604 }
605
606 void MediaGalleriesPreferences::OnFinderDeviceID(const std::string& device_id) {
607   if (!device_id.empty() && !UpdateDeviceIDForSingletonType(device_id)) {
608     std::string gallery_name;
609     if (StorageInfo::IsIPhotoDevice(device_id))
610       gallery_name = kIPhotoGalleryName;
611     else if (StorageInfo::IsITunesDevice(device_id))
612       gallery_name = kITunesGalleryName;
613     else if (StorageInfo::IsPicasaDevice(device_id))
614       gallery_name = kPicasaGalleryName;
615     else
616       NOTREACHED();
617
618     AddGalleryInternal(device_id, base::ASCIIToUTF16(gallery_name),
619                        base::FilePath(), MediaGalleryPrefInfo::kAutoDetected,
620                        base::string16(), base::string16(), base::string16(), 0,
621                        base::Time(), false, 0, 0, 0, kCurrentPrefsVersion);
622   }
623
624   OnInitializationCallbackReturned();
625 }
626
627 void MediaGalleriesPreferences::InitFromPrefs() {
628   known_galleries_.clear();
629   device_map_.clear();
630
631   PrefService* prefs = profile_->GetPrefs();
632   const base::ListValue* list = prefs->GetList(
633       prefs::kMediaGalleriesRememberedGalleries);
634   if (list) {
635     for (base::ListValue::const_iterator it = list->begin();
636          it != list->end(); ++it) {
637       const base::DictionaryValue* dict = NULL;
638       if (!(*it)->GetAsDictionary(&dict))
639         continue;
640
641       MediaGalleryPrefInfo gallery_info;
642       if (!PopulateGalleryPrefInfoFromDictionary(*dict, &gallery_info))
643         continue;
644
645       known_galleries_[gallery_info.pref_id] = gallery_info;
646       device_map_[gallery_info.device_id].insert(gallery_info.pref_id);
647     }
648   }
649 }
650
651 void MediaGalleriesPreferences::AddGalleryChangeObserver(
652     GalleryChangeObserver* observer) {
653   DCHECK(IsInitialized());
654   gallery_change_observers_.AddObserver(observer);
655 }
656
657 void MediaGalleriesPreferences::RemoveGalleryChangeObserver(
658     GalleryChangeObserver* observer) {
659   DCHECK(IsInitialized());
660   gallery_change_observers_.RemoveObserver(observer);
661 }
662
663 void MediaGalleriesPreferences::OnRemovableStorageAttached(
664     const StorageInfo& info) {
665   DCHECK(IsInitialized());
666   if (!StorageInfo::IsMediaDevice(info.device_id()))
667     return;
668
669   AddGallery(info.device_id(), base::FilePath(),
670              MediaGalleryPrefInfo::kAutoDetected, info.storage_label(),
671              info.vendor_name(), info.model_name(), info.total_size_in_bytes(),
672              base::Time::Now(), 0, 0, 0);
673 }
674
675 bool MediaGalleriesPreferences::LookUpGalleryByPath(
676     const base::FilePath& path,
677     MediaGalleryPrefInfo* gallery_info) const {
678   DCHECK(IsInitialized());
679   StorageInfo info;
680   base::FilePath relative_path;
681   if (!MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) {
682     if (gallery_info)
683       *gallery_info = MediaGalleryPrefInfo();
684     return false;
685   }
686
687   relative_path = relative_path.NormalizePathSeparators();
688   MediaGalleryPrefIdSet galleries_on_device =
689       LookUpGalleriesByDeviceId(info.device_id());
690   for (MediaGalleryPrefIdSet::const_iterator it = galleries_on_device.begin();
691        it != galleries_on_device.end();
692        ++it) {
693     const MediaGalleryPrefInfo& gallery = known_galleries_.find(*it)->second;
694     if (gallery.path != relative_path)
695       continue;
696
697     if (gallery_info)
698       *gallery_info = gallery;
699     return true;
700   }
701
702   // This method is called by controller::FilesSelected when the user
703   // adds a new gallery. Control reaches here when the selected gallery is
704   // on a volume we know about, but have no gallery already for. Returns
705   // hypothetical data to the caller about what the prefs will look like
706   // if the gallery is added.
707   // TODO(gbillock): split this out into another function so it doesn't
708   // conflate LookUp.
709   if (gallery_info) {
710     gallery_info->pref_id = kInvalidMediaGalleryPrefId;
711     gallery_info->device_id = info.device_id();
712     gallery_info->path = relative_path;
713     gallery_info->type = MediaGalleryPrefInfo::kInvalidType;
714     gallery_info->volume_label = info.storage_label();
715     gallery_info->vendor_name = info.vendor_name();
716     gallery_info->model_name = info.model_name();
717     gallery_info->total_size_in_bytes = info.total_size_in_bytes();
718     gallery_info->last_attach_time = base::Time::Now();
719     gallery_info->volume_metadata_valid = true;
720     gallery_info->prefs_version = kCurrentPrefsVersion;
721   }
722   return false;
723 }
724
725 MediaGalleryPrefIdSet MediaGalleriesPreferences::LookUpGalleriesByDeviceId(
726     const std::string& device_id) const {
727   DeviceIdPrefIdsMap::const_iterator found = device_map_.find(device_id);
728   if (found == device_map_.end())
729     return MediaGalleryPrefIdSet();
730   return found->second;
731 }
732
733 base::FilePath MediaGalleriesPreferences::LookUpGalleryPathForExtension(
734     MediaGalleryPrefId gallery_id,
735     const extensions::Extension* extension,
736     bool include_unpermitted_galleries) {
737   DCHECK(IsInitialized());
738   DCHECK(extension);
739   if (!include_unpermitted_galleries &&
740       !ContainsKey(GalleriesForExtension(*extension), gallery_id))
741     return base::FilePath();
742
743   MediaGalleriesPrefInfoMap::const_iterator it =
744       known_galleries_.find(gallery_id);
745   if (it == known_galleries_.end())
746     return base::FilePath();
747   return MediaStorageUtil::FindDevicePathById(it->second.device_id);
748 }
749
750 MediaGalleryPrefId MediaGalleriesPreferences::AddGallery(
751     const std::string& device_id,
752     const base::FilePath& relative_path,
753     MediaGalleryPrefInfo::Type type,
754     const base::string16& volume_label,
755     const base::string16& vendor_name,
756     const base::string16& model_name,
757     uint64 total_size_in_bytes,
758     base::Time last_attach_time,
759     int audio_count,
760     int image_count,
761     int video_count) {
762   DCHECK(IsInitialized());
763   return AddGalleryInternal(device_id, base::string16(), relative_path,
764                             type, volume_label, vendor_name, model_name,
765                             total_size_in_bytes, last_attach_time, true,
766                             audio_count, image_count, video_count,
767                             kCurrentPrefsVersion);
768 }
769
770 MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryInternal(
771     const std::string& device_id, const base::string16& display_name,
772     const base::FilePath& relative_path, MediaGalleryPrefInfo::Type type,
773     const base::string16& volume_label, const base::string16& vendor_name,
774     const base::string16& model_name, uint64 total_size_in_bytes,
775     base::Time last_attach_time, bool volume_metadata_valid,
776     int audio_count, int image_count, int video_count, int prefs_version) {
777   DCHECK(type == MediaGalleryPrefInfo::kUserAdded ||
778          type == MediaGalleryPrefInfo::kAutoDetected ||
779          type == MediaGalleryPrefInfo::kScanResult);
780   base::FilePath normalized_relative_path =
781       relative_path.NormalizePathSeparators();
782   MediaGalleryPrefIdSet galleries_on_device =
783     LookUpGalleriesByDeviceId(device_id);
784   for (MediaGalleryPrefIdSet::const_iterator pref_id_it =
785            galleries_on_device.begin();
786        pref_id_it != galleries_on_device.end();
787        ++pref_id_it) {
788     const MediaGalleryPrefInfo& existing =
789         known_galleries_.find(*pref_id_it)->second;
790     if (existing.path != normalized_relative_path)
791       continue;
792
793     bool update_gallery_type = false;
794     MediaGalleryPrefInfo::Type new_type = existing.type;
795     if (type == MediaGalleryPrefInfo::kUserAdded) {
796       if (existing.type == MediaGalleryPrefInfo::kBlackListed) {
797         new_type = MediaGalleryPrefInfo::kAutoDetected;
798         update_gallery_type = true;
799       }
800       if (existing.type == MediaGalleryPrefInfo::kRemovedScan) {
801         new_type = MediaGalleryPrefInfo::kUserAdded;
802         update_gallery_type = true;
803       }
804     }
805
806     // Status quo: In M27 and M28, galleries added manually use version 0,
807     // and galleries added automatically (including default galleries) use
808     // version 1. The name override is used by default galleries as well
809     // as all device attach events.
810     // We want to upgrade the name if the existing version is < 2. Leave it
811     // alone if the existing display name is set with version == 2 and the
812     // proposed new name is empty.
813     bool update_gallery_name = existing.display_name != display_name;
814     if (existing.prefs_version == 2 && !existing.display_name.empty() &&
815         display_name.empty()) {
816       update_gallery_name = false;
817     }
818     bool update_gallery_metadata = volume_metadata_valid &&
819         ((existing.volume_label != volume_label) ||
820          (existing.vendor_name != vendor_name) ||
821          (existing.model_name != model_name) ||
822          (existing.total_size_in_bytes != total_size_in_bytes) ||
823          (existing.last_attach_time != last_attach_time));
824
825     bool update_scan_counts =
826       new_type != MediaGalleryPrefInfo::kRemovedScan &&
827       new_type != MediaGalleryPrefInfo::kBlackListed &&
828       (audio_count > 0 || image_count > 0 || video_count > 0 ||
829        existing.audio_count || existing.image_count || existing.video_count);
830
831     if (!update_gallery_name && !update_gallery_type &&
832         !update_gallery_metadata && !update_scan_counts)
833       return *pref_id_it;
834
835     PrefService* prefs = profile_->GetPrefs();
836     scoped_ptr<ListPrefUpdate> update(
837         new ListPrefUpdate(prefs, prefs::kMediaGalleriesRememberedGalleries));
838     base::ListValue* list = update->Get();
839
840     for (base::ListValue::const_iterator list_iter = list->begin();
841          list_iter != list->end();
842          ++list_iter) {
843       base::DictionaryValue* dict;
844       MediaGalleryPrefId iter_id;
845       if ((*list_iter)->GetAsDictionary(&dict) &&
846           GetPrefId(*dict, &iter_id) &&
847           *pref_id_it == iter_id) {
848         if (update_gallery_type)
849           dict->SetString(kMediaGalleriesTypeKey, TypeToStringValue(new_type));
850         if (update_gallery_name)
851           dict->SetString(kMediaGalleriesDisplayNameKey, display_name);
852         if (update_gallery_metadata) {
853           dict->SetString(kMediaGalleriesVolumeLabelKey, volume_label);
854           dict->SetString(kMediaGalleriesVendorNameKey, vendor_name);
855           dict->SetString(kMediaGalleriesModelNameKey, model_name);
856           dict->SetDouble(kMediaGalleriesSizeKey, total_size_in_bytes);
857           dict->SetDouble(kMediaGalleriesLastAttachTimeKey,
858                           last_attach_time.ToInternalValue());
859         }
860         if (update_scan_counts) {
861           dict->SetInteger(kMediaGalleriesScanAudioCountKey, audio_count);
862           dict->SetInteger(kMediaGalleriesScanImageCountKey, image_count);
863           dict->SetInteger(kMediaGalleriesScanVideoCountKey, video_count);
864         }
865         dict->SetInteger(kMediaGalleriesPrefsVersionKey, prefs_version);
866         break;
867       }
868     }
869
870     // Commits the prefs update.
871     update.reset();
872
873     InitFromPrefs();
874     FOR_EACH_OBSERVER(GalleryChangeObserver, gallery_change_observers_,
875                       OnGalleryInfoUpdated(this, *pref_id_it));
876     return *pref_id_it;
877   }
878
879   PrefService* prefs = profile_->GetPrefs();
880
881   MediaGalleryPrefInfo gallery_info;
882   gallery_info.pref_id = prefs->GetUint64(prefs::kMediaGalleriesUniqueId);
883   prefs->SetUint64(prefs::kMediaGalleriesUniqueId, gallery_info.pref_id + 1);
884   gallery_info.display_name = display_name;
885   gallery_info.device_id = device_id;
886   gallery_info.path = normalized_relative_path;
887   gallery_info.type = type;
888   gallery_info.volume_label = volume_label;
889   gallery_info.vendor_name = vendor_name;
890   gallery_info.model_name = model_name;
891   gallery_info.total_size_in_bytes = total_size_in_bytes;
892   gallery_info.last_attach_time = last_attach_time;
893   gallery_info.volume_metadata_valid = volume_metadata_valid;
894   gallery_info.audio_count = audio_count;
895   gallery_info.image_count = image_count;
896   gallery_info.video_count = video_count;
897   gallery_info.prefs_version = prefs_version;
898
899   {
900     ListPrefUpdate update(prefs, prefs::kMediaGalleriesRememberedGalleries);
901     base::ListValue* list = update.Get();
902     list->Append(CreateGalleryPrefInfoDictionary(gallery_info));
903   }
904   InitFromPrefs();
905   FOR_EACH_OBSERVER(GalleryChangeObserver,
906                     gallery_change_observers_,
907                     OnGalleryAdded(this, gallery_info.pref_id));
908
909   return gallery_info.pref_id;
910 }
911
912 MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryByPath(
913     const base::FilePath& path, MediaGalleryPrefInfo::Type type) {
914   DCHECK(IsInitialized());
915   MediaGalleryPrefInfo gallery_info;
916   if (LookUpGalleryByPath(path, &gallery_info) &&
917       !gallery_info.IsBlackListedType()) {
918     return gallery_info.pref_id;
919   }
920   return AddGalleryInternal(gallery_info.device_id,
921                             gallery_info.display_name,
922                             gallery_info.path,
923                             type,
924                             gallery_info.volume_label,
925                             gallery_info.vendor_name,
926                             gallery_info.model_name,
927                             gallery_info.total_size_in_bytes,
928                             gallery_info.last_attach_time,
929                             gallery_info.volume_metadata_valid,
930                             0, 0, 0,
931                             kCurrentPrefsVersion);
932 }
933
934 void MediaGalleriesPreferences::ForgetGalleryById(MediaGalleryPrefId id) {
935   EraseOrBlacklistGalleryById(id, false);
936 }
937
938 void MediaGalleriesPreferences::EraseGalleryById(MediaGalleryPrefId id) {
939   EraseOrBlacklistGalleryById(id, true);
940 }
941
942 void MediaGalleriesPreferences::EraseOrBlacklistGalleryById(
943     MediaGalleryPrefId id, bool erase) {
944   DCHECK(IsInitialized());
945   PrefService* prefs = profile_->GetPrefs();
946   scoped_ptr<ListPrefUpdate> update(new ListPrefUpdate(
947       prefs, prefs::kMediaGalleriesRememberedGalleries));
948   base::ListValue* list = update->Get();
949
950   if (!ContainsKey(known_galleries_, id))
951     return;
952
953   for (base::ListValue::iterator iter = list->begin();
954        iter != list->end(); ++iter) {
955     base::DictionaryValue* dict;
956     MediaGalleryPrefId iter_id;
957     if ((*iter)->GetAsDictionary(&dict) && GetPrefId(*dict, &iter_id) &&
958         id == iter_id) {
959       RemoveGalleryPermissionsFromPrefs(id);
960       MediaGalleryPrefInfo::Type type;
961       if (!erase && GetType(*dict, &type) &&
962           (type == MediaGalleryPrefInfo::kAutoDetected ||
963            type == MediaGalleryPrefInfo::kScanResult)) {
964         if (type == MediaGalleryPrefInfo::kAutoDetected) {
965           dict->SetString(kMediaGalleriesTypeKey,
966                           kMediaGalleriesTypeBlackListedValue);
967         } else {
968           dict->SetString(kMediaGalleriesTypeKey,
969                           kMediaGalleriesTypeRemovedScanValue);
970           dict->SetInteger(kMediaGalleriesScanAudioCountKey, 0);
971           dict->SetInteger(kMediaGalleriesScanImageCountKey, 0);
972           dict->SetInteger(kMediaGalleriesScanVideoCountKey, 0);
973         }
974       } else {
975         list->Erase(iter, NULL);
976       }
977       update.reset(NULL);  // commits the update.
978
979       InitFromPrefs();
980       FOR_EACH_OBSERVER(GalleryChangeObserver,
981                         gallery_change_observers_,
982                         OnGalleryRemoved(this, id));
983       return;
984     }
985   }
986 }
987
988 bool MediaGalleriesPreferences::NonAutoGalleryHasPermission(
989     MediaGalleryPrefId id) const {
990   DCHECK(IsInitialized());
991   DCHECK(!ContainsKey(known_galleries_, id) ||
992          known_galleries_.find(id)->second.type !=
993              MediaGalleryPrefInfo::kAutoDetected);
994   ExtensionPrefs* prefs = GetExtensionPrefs();
995   const base::DictionaryValue* extensions =
996       prefs->pref_service()->GetDictionary(extensions::pref_names::kExtensions);
997   if (!extensions)
998     return true;
999
1000   for (base::DictionaryValue::Iterator iter(*extensions); !iter.IsAtEnd();
1001        iter.Advance()) {
1002     if (!extensions::Extension::IdIsValid(iter.key())) {
1003       NOTREACHED();
1004       continue;
1005     }
1006     std::vector<MediaGalleryPermission> permissions =
1007         GetGalleryPermissionsFromPrefs(iter.key());
1008     for (std::vector<MediaGalleryPermission>::const_iterator it =
1009              permissions.begin(); it != permissions.end(); ++it) {
1010       if (it->pref_id == id) {
1011         if (it->has_permission)
1012           return true;
1013         break;
1014       }
1015     }
1016   }
1017   return false;
1018 }
1019
1020 MediaGalleryPrefIdSet MediaGalleriesPreferences::GalleriesForExtension(
1021     const extensions::Extension& extension) const {
1022   DCHECK(IsInitialized());
1023   MediaGalleryPrefIdSet result;
1024
1025   if (HasAutoDetectedGalleryPermission(extension)) {
1026     for (MediaGalleriesPrefInfoMap::const_iterator it =
1027              known_galleries_.begin(); it != known_galleries_.end(); ++it) {
1028       if (it->second.type == MediaGalleryPrefInfo::kAutoDetected)
1029         result.insert(it->second.pref_id);
1030     }
1031   }
1032
1033   std::vector<MediaGalleryPermission> stored_permissions =
1034       GetGalleryPermissionsFromPrefs(extension.id());
1035   for (std::vector<MediaGalleryPermission>::const_iterator it =
1036            stored_permissions.begin(); it != stored_permissions.end(); ++it) {
1037     if (!it->has_permission) {
1038       result.erase(it->pref_id);
1039     } else {
1040       MediaGalleriesPrefInfoMap::const_iterator gallery =
1041           known_galleries_.find(it->pref_id);
1042       DCHECK(gallery != known_galleries_.end());
1043       if (!gallery->second.IsBlackListedType()) {
1044         result.insert(it->pref_id);
1045       } else {
1046         NOTREACHED() << gallery->second.device_id;
1047       }
1048     }
1049   }
1050   return result;
1051 }
1052
1053 bool MediaGalleriesPreferences::SetGalleryPermissionForExtension(
1054     const extensions::Extension& extension,
1055     MediaGalleryPrefId pref_id,
1056     bool has_permission) {
1057   DCHECK(IsInitialized());
1058   // The gallery may not exist anymore if the user opened a second config
1059   // surface concurrently and removed it. Drop the permission update if so.
1060   MediaGalleriesPrefInfoMap::const_iterator gallery_info =
1061       known_galleries_.find(pref_id);
1062   if (gallery_info == known_galleries_.end())
1063     return false;
1064
1065   bool default_permission = false;
1066   if (gallery_info->second.type == MediaGalleryPrefInfo::kAutoDetected)
1067     default_permission = HasAutoDetectedGalleryPermission(extension);
1068   // When the permission matches the default, we don't need to remember it.
1069   if (has_permission == default_permission) {
1070     if (!UnsetGalleryPermissionInPrefs(extension.id(), pref_id))
1071       // If permission wasn't set, assume nothing has changed.
1072       return false;
1073   } else {
1074     if (!SetGalleryPermissionInPrefs(extension.id(), pref_id, has_permission))
1075       return false;
1076   }
1077   if (has_permission)
1078     FOR_EACH_OBSERVER(GalleryChangeObserver,
1079                       gallery_change_observers_,
1080                       OnPermissionAdded(this, extension.id(), pref_id));
1081   else
1082     FOR_EACH_OBSERVER(GalleryChangeObserver,
1083                       gallery_change_observers_,
1084                       OnPermissionRemoved(this, extension.id(), pref_id));
1085   return true;
1086 }
1087
1088 const MediaGalleriesPrefInfoMap& MediaGalleriesPreferences::known_galleries()
1089     const {
1090   DCHECK(IsInitialized());
1091   return known_galleries_;
1092 }
1093
1094 base::Time MediaGalleriesPreferences::GetLastScanCompletionTime() const {
1095   int64 last_scan_time_internal =
1096       profile_->GetPrefs()->GetInt64(prefs::kMediaGalleriesLastScanTime);
1097   return base::Time::FromInternalValue(last_scan_time_internal);
1098 }
1099
1100 void MediaGalleriesPreferences::SetLastScanCompletionTime(
1101     const base::Time& time) {
1102   profile_->GetPrefs()->SetInt64(prefs::kMediaGalleriesLastScanTime,
1103                                  time.ToInternalValue());
1104 }
1105
1106 void MediaGalleriesPreferences::Shutdown() {
1107   weak_factory_.InvalidateWeakPtrs();
1108   profile_ = NULL;
1109 }
1110
1111 // static
1112 bool MediaGalleriesPreferences::APIHasBeenUsed(Profile* profile) {
1113   MediaGalleryPrefId current_id =
1114       profile->GetPrefs()->GetUint64(prefs::kMediaGalleriesUniqueId);
1115   return current_id != kInvalidMediaGalleryPrefId + 1;
1116 }
1117
1118 // static
1119 void MediaGalleriesPreferences::RegisterProfilePrefs(
1120     user_prefs::PrefRegistrySyncable* registry) {
1121   registry->RegisterListPref(prefs::kMediaGalleriesRememberedGalleries,
1122                              user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
1123   registry->RegisterUint64Pref(
1124       prefs::kMediaGalleriesUniqueId,
1125       kInvalidMediaGalleryPrefId + 1,
1126       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
1127   registry->RegisterInt64Pref(
1128       prefs::kMediaGalleriesLastScanTime,
1129       base::Time().ToInternalValue(),
1130       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
1131 }
1132
1133 bool MediaGalleriesPreferences::SetGalleryPermissionInPrefs(
1134     const std::string& extension_id,
1135     MediaGalleryPrefId gallery_id,
1136     bool has_access) {
1137   DCHECK(IsInitialized());
1138   ExtensionPrefs::ScopedListUpdate update(GetExtensionPrefs(),
1139                                           extension_id,
1140                                           kMediaGalleriesPermissions);
1141   base::ListValue* permissions = update.Get();
1142   if (!permissions) {
1143     permissions = update.Create();
1144   } else {
1145     // If the gallery is already in the list, update the permission...
1146     for (base::ListValue::iterator iter = permissions->begin();
1147          iter != permissions->end(); ++iter) {
1148       base::DictionaryValue* dict = NULL;
1149       if (!(*iter)->GetAsDictionary(&dict))
1150         continue;
1151       MediaGalleryPermission perm;
1152       if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
1153         continue;
1154       if (perm.pref_id == gallery_id) {
1155         if (has_access != perm.has_permission) {
1156           dict->SetBoolean(kMediaGalleryHasPermissionKey, has_access);
1157           return true;
1158         } else {
1159           return false;
1160         }
1161       }
1162     }
1163   }
1164   // ...Otherwise, add a new entry for the gallery.
1165   base::DictionaryValue* dict = new base::DictionaryValue;
1166   dict->SetString(kMediaGalleryIdKey, base::Uint64ToString(gallery_id));
1167   dict->SetBoolean(kMediaGalleryHasPermissionKey, has_access);
1168   permissions->Append(dict);
1169   return true;
1170 }
1171
1172 bool MediaGalleriesPreferences::UnsetGalleryPermissionInPrefs(
1173     const std::string& extension_id,
1174     MediaGalleryPrefId gallery_id) {
1175   DCHECK(IsInitialized());
1176   ExtensionPrefs::ScopedListUpdate update(GetExtensionPrefs(),
1177                                           extension_id,
1178                                           kMediaGalleriesPermissions);
1179   base::ListValue* permissions = update.Get();
1180   if (!permissions)
1181     return false;
1182
1183   for (base::ListValue::iterator iter = permissions->begin();
1184        iter != permissions->end(); ++iter) {
1185     const base::DictionaryValue* dict = NULL;
1186     if (!(*iter)->GetAsDictionary(&dict))
1187       continue;
1188     MediaGalleryPermission perm;
1189     if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
1190       continue;
1191     if (perm.pref_id == gallery_id) {
1192       permissions->Erase(iter, NULL);
1193       return true;
1194     }
1195   }
1196   return false;
1197 }
1198
1199 std::vector<MediaGalleryPermission>
1200 MediaGalleriesPreferences::GetGalleryPermissionsFromPrefs(
1201     const std::string& extension_id) const {
1202   DCHECK(IsInitialized());
1203   std::vector<MediaGalleryPermission> result;
1204   const base::ListValue* permissions;
1205   if (!GetExtensionPrefs()->ReadPrefAsList(extension_id,
1206                                            kMediaGalleriesPermissions,
1207                                            &permissions)) {
1208     return result;
1209   }
1210
1211   for (base::ListValue::const_iterator iter = permissions->begin();
1212        iter != permissions->end(); ++iter) {
1213     base::DictionaryValue* dict = NULL;
1214     if (!(*iter)->GetAsDictionary(&dict))
1215       continue;
1216     MediaGalleryPermission perm;
1217     if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
1218       continue;
1219     result.push_back(perm);
1220   }
1221
1222   return result;
1223 }
1224
1225 void MediaGalleriesPreferences::RemoveGalleryPermissionsFromPrefs(
1226     MediaGalleryPrefId gallery_id) {
1227   DCHECK(IsInitialized());
1228   ExtensionPrefs* prefs = GetExtensionPrefs();
1229   const base::DictionaryValue* extensions =
1230       prefs->pref_service()->GetDictionary(extensions::pref_names::kExtensions);
1231   if (!extensions)
1232     return;
1233
1234   for (base::DictionaryValue::Iterator iter(*extensions); !iter.IsAtEnd();
1235        iter.Advance()) {
1236     if (!extensions::Extension::IdIsValid(iter.key())) {
1237       NOTREACHED();
1238       continue;
1239     }
1240     UnsetGalleryPermissionInPrefs(iter.key(), gallery_id);
1241   }
1242 }
1243
1244 ExtensionPrefs* MediaGalleriesPreferences::GetExtensionPrefs() const {
1245   DCHECK(IsInitialized());
1246   if (extension_prefs_for_testing_)
1247     return extension_prefs_for_testing_;
1248   return extensions::ExtensionPrefs::Get(profile_);
1249 }
1250
1251 void MediaGalleriesPreferences::SetExtensionPrefsForTesting(
1252     extensions::ExtensionPrefs* extension_prefs) {
1253   DCHECK(IsInitialized());
1254   extension_prefs_for_testing_ = extension_prefs;
1255 }