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