1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/media_galleries/media_galleries_preferences.h" 6 7 #include "base/base_paths_posix.h" 8 #include "base/callback.h" 9 #include "base/i18n/time_formatting.h" 10 #include "base/path_service.h" 11 #include "base/prefs/pref_service.h" 12 #include "base/prefs/scoped_user_pref_update.h" 13 #include "base/stl_util.h" 14 #include "base/strings/string16.h" 15 #include "base/strings/string_number_conversions.h" 16 #include "base/strings/utf_string_conversions.h" 17 #include "base/values.h" 18 #include "chrome/browser/browser_process.h" 19 #include "chrome/browser/extensions/api/media_galleries_private/media_galleries_private_api.h" 20 #include "chrome/browser/extensions/extension_service.h" 21 #include "chrome/browser/media_galleries/fileapi/iapps_finder.h" 22 #include "chrome/browser/media_galleries/fileapi/picasa_finder.h" 23 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h" 24 #include "chrome/browser/media_galleries/media_file_system_registry.h" 25 #include "chrome/browser/media_galleries/media_galleries_histograms.h" 26 #include "chrome/browser/profiles/profile.h" 27 #include "chrome/common/chrome_paths.h" 28 #include "chrome/common/pref_names.h" 29 #include "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" 43 44 using base::DictionaryValue; 45 using base::ListValue; 46 using extensions::ExtensionPrefs; 47 using storage_monitor::MediaStorageUtil; 48 using storage_monitor::StorageInfo; 49 using storage_monitor::StorageMonitor; 50 51 namespace { 52 53 // Pref key for the list of media gallery permissions. 54 const char kMediaGalleriesPermissions[] = "media_galleries_permissions"; 55 // Pref key for Media Gallery ID. 56 const char kMediaGalleryIdKey[] = "id"; 57 // Pref key for Media Gallery Permission Value. 58 const char kMediaGalleryHasPermissionKey[] = "has_permission"; 59 60 const char kMediaGalleriesDeviceIdKey[] = "deviceId"; 61 const char kMediaGalleriesDisplayNameKey[] = "displayName"; 62 const char kMediaGalleriesPathKey[] = "path"; 63 const char kMediaGalleriesPrefIdKey[] = "prefId"; 64 const char kMediaGalleriesTypeKey[] = "type"; 65 const char kMediaGalleriesVolumeLabelKey[] = "volumeLabel"; 66 const char kMediaGalleriesVendorNameKey[] = "vendorName"; 67 const char kMediaGalleriesModelNameKey[] = "modelName"; 68 const char kMediaGalleriesSizeKey[] = "totalSize"; 69 const char kMediaGalleriesLastAttachTimeKey[] = "lastAttachTime"; 70 const char kMediaGalleriesScanAudioCountKey[] = "audioCount"; 71 const char kMediaGalleriesScanImageCountKey[] = "imageCount"; 72 const char kMediaGalleriesScanVideoCountKey[] = "videoCount"; 73 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"; 79 80 const char kMediaGalleriesDefaultGalleryTypeNotDefaultValue[] = "notDefault"; 81 const char kMediaGalleriesDefaultGalleryTypeMusicDefaultValue[] = "music"; 82 const char kMediaGalleriesDefaultGalleryTypePicturesDefaultValue[] = "pictures"; 83 const char kMediaGalleriesDefaultGalleryTypeVideosDefaultValue[] = "videos"; 84 85 const char kIPhotoGalleryName[] = "iPhoto"; 86 const char kITunesGalleryName[] = "iTunes"; 87 const char kPicasaGalleryName[] = "Picasa"; 88 89 const int kCurrentPrefsVersion = 3; 90 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; 99 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++; 110 } 111 } 112 return count; 113 } 114 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; 120 } 121 122 return true; 123 } 124 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; 130 131 if (string_type == kMediaGalleriesTypeUserAddedValue) { 132 *type = MediaGalleryPrefInfo::kUserAdded; 133 return true; 134 } 135 if (string_type == kMediaGalleriesTypeAutoDetectedValue) { 136 *type = MediaGalleryPrefInfo::kAutoDetected; 137 return true; 138 } 139 if (string_type == kMediaGalleriesTypeBlackListedValue) { 140 *type = MediaGalleryPrefInfo::kBlackListed; 141 return true; 142 } 143 if (string_type == kMediaGalleriesTypeScanResultValue) { 144 *type = MediaGalleryPrefInfo::kScanResult; 145 return true; 146 } 147 if (string_type == kMediaGalleriesTypeRemovedScanValue) { 148 *type = MediaGalleryPrefInfo::kRemovedScan; 149 return true; 150 } 151 152 return false; 153 } 154 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; 176 } 177 return result; 178 } 179 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; 186 187 if (default_gallery_type_string == 188 kMediaGalleriesDefaultGalleryTypeMusicDefaultValue) { 189 return MediaGalleryPrefInfo::kMusicDefault; 190 } 191 if (default_gallery_type_string == 192 kMediaGalleriesDefaultGalleryTypePicturesDefaultValue) { 193 return MediaGalleryPrefInfo::kPicturesDefault; 194 } 195 if (default_gallery_type_string == 196 kMediaGalleriesDefaultGalleryTypeVideosDefaultValue) { 197 return MediaGalleryPrefInfo::kVideosDefault; 198 } 199 return MediaGalleryPrefInfo::kNotDefault; 200 } 201 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; 221 } 222 return result; 223 } 224 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; 242 243 if (!GetPrefId(dict, &pref_id) || 244 !dict.GetString(kMediaGalleriesDeviceIdKey, &device_id) || 245 !dict.GetString(kMediaGalleriesPathKey, &path) || 246 !GetType(dict, &type)) { 247 return false; 248 } 249 250 dict.GetString(kMediaGalleriesDisplayNameKey, &display_name); 251 dict.GetInteger(kMediaGalleriesPrefsVersionKey, &prefs_version); 252 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; 259 } 260 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; 271 } 272 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; 288 } 289 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)); 298 299 if (gallery.default_gallery_type != MediaGalleryPrefInfo::kNotDefault) { 300 dict->SetString(kMediaGalleriesDefaultGalleryTypeKey, 301 DefaultGalleryTypeToStringValue( 302 gallery.default_gallery_type)); 303 } 304 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); 314 } 315 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); 320 } 321 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); 330 331 return dict; 332 } 333 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, ¶m); 339 } 340 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; 352 } 353 NOTREACHED(); 354 return false; 355 } 356 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); 366 } 367 368 void InitializeImportedMediaGalleryRegistryOnFileThread() { 369 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); 370 ImportedMediaGalleryRegistry::GetInstance()->Initialize(); 371 } 372 373 } // namespace 374 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) { 385 } 386 387 MediaGalleryPrefInfo::~MediaGalleryPrefInfo() {} 388 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); 393 } 394 395 bool MediaGalleryPrefInfo::IsBlackListedType() const { 396 return type == kBlackListed || type == kRemovedScan; 397 } 398 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; 408 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(); 416 } 417 return path.BaseName().LossyDisplayName(); 418 #else 419 return path.LossyDisplayName(); 420 #endif 421 } 422 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; 430 } 431 432 base::string16 MediaGalleryPrefInfo::GetGalleryTooltip() const { 433 return AbsolutePath().LossyDisplayName(); 434 } 435 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); 449 } 450 } 451 452 return attached; 453 } 454 455 bool MediaGalleryPrefInfo::IsGalleryAvailable() const { 456 return !StorageInfo::IsRemovableDevice(device_id) || 457 MediaStorageUtil::IsRemovableStorageAttached(device_id); 458 } 459 460 MediaGalleriesPreferences::GalleryChangeObserver::~GalleryChangeObserver() {} 461 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) { 468 } 469 470 MediaGalleriesPreferences::~MediaGalleriesPreferences() { 471 if (StorageMonitor::GetInstance()) 472 StorageMonitor::GetInstance()->RemoveObserver(this); 473 } 474 475 void MediaGalleriesPreferences::EnsureInitialized(base::Closure callback) { 476 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 477 478 if (IsInitialized()) { 479 if (!callback.is_null()) 480 callback.Run(); 481 return; 482 } 483 484 on_initialize_callbacks_.push_back(callback); 485 if (on_initialize_callbacks_.size() > 1) 486 return; 487 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; 493 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); 499 } 500 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_))); 508 509 // Look for optional default galleries every time. 510 iapps::FindITunesLibrary( 511 base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID, 512 weak_factory_.GetWeakPtr())); 513 514 picasa::FindPicasaDatabase( 515 base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID, 516 weak_factory_.GetWeakPtr())); 517 518 iapps::FindIPhotoLibrary( 519 base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID, 520 weak_factory_.GetWeakPtr())); 521 } 522 523 bool MediaGalleriesPreferences::IsInitialized() const { return initialized_; } 524 525 Profile* MediaGalleriesPreferences::profile() { return profile_; } 526 527 void MediaGalleriesPreferences::OnInitializationCallbackReturned() { 528 DCHECK(!IsInitialized()); 529 DCHECK_GT(pre_initialization_callbacks_waiting_, 0); 530 if (--pre_initialization_callbacks_waiting_ == 0) 531 FinishInitialization(); 532 } 533 534 void MediaGalleriesPreferences::FinishInitialization() { 535 DCHECK(!IsInitialized()); 536 537 initialized_ = true; 538 539 StorageMonitor* monitor = StorageMonitor::GetInstance(); 540 DCHECK(monitor->IsInitialized()); 541 542 InitFromPrefs(); 543 544 StorageMonitor::GetInstance()->AddObserver(this); 545 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); 560 } 561 562 for (std::vector<base::Closure>::iterator iter = 563 on_initialize_callbacks_.begin(); 564 iter != on_initialize_callbacks_.end(); 565 ++iter) { 566 iter->Run(); 567 } 568 on_initialize_callbacks_.clear(); 569 } 570 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}, 579 }; 580 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; 585 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); 592 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, 604 0, 605 0, 606 0, 607 kCurrentPrefsVersion, 608 default_gallery_type); 609 } 610 } 611 } 612 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; 618 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; 637 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)); 647 } 648 return true; 649 } 650 } 651 return false; 652 } 653 654 void MediaGalleriesPreferences::OnStorageMonitorInit( 655 bool api_has_been_used) { 656 if (api_has_been_used) 657 UpdateDefaultGalleriesPaths(); 658 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(); 662 663 OnInitializationCallbackReturned(); 664 } 665 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; 675 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())); 685 } 686 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(), 697 0, 698 base::Time(), 699 false, 700 0, 701 0, 702 0, 703 kCurrentPrefsVersion, 704 MediaGalleryPrefInfo::kNotDefault); 705 } 706 } 707 708 OnInitializationCallbackReturned(); 709 } 710 711 void MediaGalleriesPreferences::InitFromPrefs() { 712 known_galleries_.clear(); 713 device_map_.clear(); 714 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; 724 725 MediaGalleryPrefInfo gallery_info; 726 if (!PopulateGalleryPrefInfoFromDictionary(*dict, &gallery_info)) 727 continue; 728 729 known_galleries_[gallery_info.pref_id] = gallery_info; 730 device_map_[gallery_info.device_id].insert(gallery_info.pref_id); 731 } 732 } 733 } 734 735 void MediaGalleriesPreferences::AddGalleryChangeObserver( 736 GalleryChangeObserver* observer) { 737 DCHECK(IsInitialized()); 738 gallery_change_observers_.AddObserver(observer); 739 } 740 741 void MediaGalleriesPreferences::RemoveGalleryChangeObserver( 742 GalleryChangeObserver* observer) { 743 DCHECK(IsInitialized()); 744 gallery_change_observers_.RemoveObserver(observer); 745 } 746 747 void MediaGalleriesPreferences::OnRemovableStorageAttached( 748 const StorageInfo& info) { 749 DCHECK(IsInitialized()); 750 if (!StorageInfo::IsMediaDevice(info.device_id())) 751 return; 752 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); 757 } 758 759 bool MediaGalleriesPreferences::LookUpGalleryByPath( 760 const base::FilePath& path, 761 MediaGalleryPrefInfo* gallery_info) const { 762 DCHECK(IsInitialized()); 763 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; 772 } 773 } 774 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; 781 } 782 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; 792 793 if (gallery_info) 794 *gallery_info = gallery; 795 return true; 796 } 797 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; 817 } 818 return false; 819 } 820 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; 827 } 828 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(); 838 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); 844 } 845 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); 875 } 876 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); 892 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; 901 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; 908 } 909 if (existing.type == MediaGalleryPrefInfo::kRemovedScan) { 910 new_type = MediaGalleryPrefInfo::kUserAdded; 911 update_gallery_type = true; 912 } 913 } 914 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; 926 } 927 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; 932 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)); 939 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); 945 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; 950 951 PrefService* prefs = profile_->GetPrefs(); 952 scoped_ptr<ListPrefUpdate> update( 953 new ListPrefUpdate(prefs, prefs::kMediaGalleriesRememberedGalleries)); 954 base::ListValue* list = update->Get(); 955 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()); 975 } 976 if (update_scan_counts) { 977 dict->SetInteger(kMediaGalleriesScanAudioCountKey, audio_count); 978 dict->SetInteger(kMediaGalleriesScanImageCountKey, image_count); 979 dict->SetInteger(kMediaGalleriesScanVideoCountKey, video_count); 980 } 981 if (update_default_gallery_type) { 982 dict->SetString( 983 kMediaGalleriesDefaultGalleryTypeKey, 984 DefaultGalleryTypeToStringValue(default_gallery_type)); 985 } 986 dict->SetInteger(kMediaGalleriesPrefsVersionKey, prefs_version); 987 break; 988 } 989 } 990 991 // Commits the prefs update. 992 update.reset(); 993 994 InitFromPrefs(); 995 FOR_EACH_OBSERVER(GalleryChangeObserver, gallery_change_observers_, 996 OnGalleryInfoUpdated(this, *pref_id_it)); 997 return *pref_id_it; 998 } 999 1000 PrefService* prefs = profile_->GetPrefs(); 1001 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; 1020 1021 { 1022 ListPrefUpdate update(prefs, prefs::kMediaGalleriesRememberedGalleries); 1023 base::ListValue* list = update.Get(); 1024 list->Append(CreateGalleryPrefInfoDictionary(gallery_info)); 1025 } 1026 InitFromPrefs(); 1027 FOR_EACH_OBSERVER(GalleryChangeObserver, 1028 gallery_change_observers_, 1029 OnGalleryAdded(this, gallery_info.pref_id)); 1030 1031 return gallery_info.pref_id; 1032 } 1033 1034 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); 1044 1045 PrefService* prefs = profile_->GetPrefs(); 1046 scoped_ptr<ListPrefUpdate> update(new ListPrefUpdate( 1047 prefs, prefs::kMediaGalleriesRememberedGalleries)); 1048 base::ListValue* list = update->Get(); 1049 1050 std::vector<MediaGalleryPrefId> pref_ids; 1051 1052 for (base::ListValue::iterator iter = list->begin(); 1053 iter != list->end(); 1054 ++iter) { 1055 base::DictionaryValue* dict; 1056 MediaGalleryPrefId pref_id; 1057 1058 if (!((*iter)->GetAsDictionary(&dict) && GetPrefId(*dict, &pref_id))) 1059 continue; 1060 1061 std::string default_gallery_type_string; 1062 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()); 1087 } 1088 1089 if (!device_id.empty()) 1090 dict->SetString(kMediaGalleriesDeviceIdKey, device_id); 1091 } 1092 1093 pref_ids.push_back(pref_id); 1094 } 1095 1096 // Commit the prefs update. 1097 update.reset(); 1098 InitFromPrefs(); 1099 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)); 1106 } 1107 } 1108 1109 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; 1117 } 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); 1131 } 1132 1133 void MediaGalleriesPreferences::ForgetGalleryById(MediaGalleryPrefId id) { 1134 EraseOrBlacklistGalleryById(id, false); 1135 } 1136 1137 void MediaGalleriesPreferences::EraseGalleryById(MediaGalleryPrefId id) { 1138 EraseOrBlacklistGalleryById(id, true); 1139 } 1140 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(); 1148 1149 if (!ContainsKey(known_galleries_, id)) 1150 return; 1151 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); 1172 } 1173 } else { 1174 list->Erase(iter, NULL); 1175 } 1176 update.reset(NULL); // commits the update. 1177 1178 InitFromPrefs(); 1179 FOR_EACH_OBSERVER(GalleryChangeObserver, 1180 gallery_change_observers_, 1181 OnGalleryRemoved(this, id)); 1182 return; 1183 } 1184 } 1185 } 1186 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; 1198 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; 1204 } 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; 1213 } 1214 } 1215 } 1216 return false; 1217 } 1218 1219 MediaGalleryPrefIdSet MediaGalleriesPreferences::GalleriesForExtension( 1220 const extensions::Extension& extension) { 1221 DCHECK(IsInitialized()); 1222 MediaGalleryPrefIdSet result; 1223 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); 1229 } 1230 } 1231 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); 1241 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; 1247 } 1248 1249 if (!gallery->second.IsBlackListedType()) { 1250 result.insert(it->pref_id); 1251 } else { 1252 NOTREACHED() << gallery->second.device_id; 1253 } 1254 } 1255 } 1256 return result; 1257 } 1258 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; 1270 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; 1282 } 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; 1292 } 1293 1294 const MediaGalleriesPrefInfoMap& MediaGalleriesPreferences::known_galleries() 1295 const { 1296 DCHECK(IsInitialized()); 1297 return known_galleries_; 1298 } 1299 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); 1304 } 1305 1306 void MediaGalleriesPreferences::SetLastScanCompletionTime( 1307 const base::Time& time) { 1308 profile_->GetPrefs()->SetInt64(prefs::kMediaGalleriesLastScanTime, 1309 time.ToInternalValue()); 1310 } 1311 1312 void MediaGalleriesPreferences::Shutdown() { 1313 weak_factory_.InvalidateWeakPtrs(); 1314 profile_ = NULL; 1315 } 1316 1317 // static 1318 bool MediaGalleriesPreferences::APIHasBeenUsed(Profile* profile) { 1319 MediaGalleryPrefId current_id = 1320 profile->GetPrefs()->GetUint64(prefs::kMediaGalleriesUniqueId); 1321 return current_id != kInvalidMediaGalleryPrefId + 1; 1322 } 1323 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); 1337 } 1338 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; 1366 } 1367 } 1368 } 1369 } 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; 1376 } 1377 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; 1388 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; 1400 } 1401 } 1402 return false; 1403 } 1404 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; 1415 } 1416 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); 1426 } 1427 1428 return result; 1429 } 1430 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; 1439 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; 1445 } 1446 UnsetGalleryPermissionInPrefs(iter.key(), gallery_id); 1447 } 1448 } 1449 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_); 1455 } 1456 1457 void MediaGalleriesPreferences::SetExtensionPrefsForTesting( 1458 extensions::ExtensionPrefs* extension_prefs) { 1459 DCHECK(IsInitialized()); 1460 extension_prefs_for_testing_ = extension_prefs; 1461 } 1462