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, ¶m);
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