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