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