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