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