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