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