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/extensions/extension_system.h" 22 #include "chrome/browser/media_galleries/fileapi/iapps_finder.h" 23 #include "chrome/browser/media_galleries/fileapi/picasa_finder.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/browser/storage_monitor/media_storage_util.h" 28 #include "chrome/browser/storage_monitor/storage_monitor.h" 29 #include "chrome/common/chrome_paths.h" 30 #include "chrome/common/extensions/permissions/media_galleries_permission.h" 31 #include "chrome/common/pref_names.h" 32 #include "components/user_prefs/pref_registry_syncable.h" 33 #include "content/public/browser/browser_thread.h" 34 #include "extensions/common/extension.h" 35 #include "extensions/common/permissions/api_permission.h" 36 #include "extensions/common/permissions/permissions_data.h" 37 #include "grit/generated_resources.h" 38 #include "ui/base/l10n/l10n_util.h" 39 #include "ui/base/text/bytes_formatting.h" 40 41 using base::DictionaryValue; 42 using base::ListValue; 43 using extensions::ExtensionPrefs; 44 45 namespace { 46 47 // Pref key for the list of media gallery permissions. 48 const char kMediaGalleriesPermissions[] = "media_galleries_permissions"; 49 // Pref key for Media Gallery ID. 50 const char kMediaGalleryIdKey[] = "id"; 51 // Pref key for Media Gallery Permission Value. 52 const char kMediaGalleryHasPermissionKey[] = "has_permission"; 53 54 const char kMediaGalleriesDeviceIdKey[] = "deviceId"; 55 const char kMediaGalleriesDisplayNameKey[] = "displayName"; 56 const char kMediaGalleriesPathKey[] = "path"; 57 const char kMediaGalleriesPrefIdKey[] = "prefId"; 58 const char kMediaGalleriesTypeKey[] = "type"; 59 const char kMediaGalleriesVolumeLabelKey[] = "volumeLabel"; 60 const char kMediaGalleriesVendorNameKey[] = "vendorName"; 61 const char kMediaGalleriesModelNameKey[] = "modelName"; 62 const char kMediaGalleriesSizeKey[] = "totalSize"; 63 const char kMediaGalleriesLastAttachTimeKey[] = "lastAttachTime"; 64 const char kMediaGalleriesPrefsVersionKey[] = "preferencesVersion"; 65 66 const char kMediaGalleriesTypeAutoDetectedValue[] = "autoDetected"; 67 const char kMediaGalleriesTypeUserAddedValue[] = "userAdded"; 68 const char kMediaGalleriesTypeBlackListedValue[] = "blackListed"; 69 70 const char kIPhotoGalleryName[] = "iPhoto"; 71 const char kITunesGalleryName[] = "iTunes"; 72 const char kPicasaGalleryName[] = "Picasa"; 73 74 int NumberExtensionsUsingMediaGalleries(Profile* profile) { 75 int count = 0; 76 if (!profile) 77 return count; 78 ExtensionService* extension_service = 79 extensions::ExtensionSystem::Get(profile)->extension_service(); 80 if (!extension_service) 81 return count; 82 83 const ExtensionSet* extensions = extension_service->extensions(); 84 for (ExtensionSet::const_iterator i = extensions->begin(); 85 i != extensions->end(); ++i) { 86 if (extensions::PermissionsData::HasAPIPermission( 87 *i, extensions::APIPermission::kMediaGalleries) || 88 extensions::PermissionsData::HasAPIPermission( 89 *i, extensions::APIPermission::kMediaGalleriesPrivate)) { 90 count++; 91 } 92 } 93 return count; 94 } 95 96 bool GetPrefId(const DictionaryValue& dict, MediaGalleryPrefId* value) { 97 std::string string_id; 98 if (!dict.GetString(kMediaGalleriesPrefIdKey, &string_id) || 99 !base::StringToUint64(string_id, value)) { 100 return false; 101 } 102 103 return true; 104 } 105 106 bool GetType(const DictionaryValue& dict, MediaGalleryPrefInfo::Type* type) { 107 std::string string_type; 108 if (!dict.GetString(kMediaGalleriesTypeKey, &string_type)) 109 return false; 110 111 if (string_type == kMediaGalleriesTypeAutoDetectedValue) { 112 *type = MediaGalleryPrefInfo::kAutoDetected; 113 return true; 114 } 115 if (string_type == kMediaGalleriesTypeUserAddedValue) { 116 *type = MediaGalleryPrefInfo::kUserAdded; 117 return true; 118 } 119 if (string_type == kMediaGalleriesTypeBlackListedValue) { 120 *type = MediaGalleryPrefInfo::kBlackListed; 121 return true; 122 } 123 124 return false; 125 } 126 127 bool PopulateGalleryPrefInfoFromDictionary( 128 const DictionaryValue& dict, MediaGalleryPrefInfo* out_gallery_info) { 129 MediaGalleryPrefId pref_id; 130 base::string16 display_name; 131 std::string device_id; 132 base::FilePath::StringType path; 133 MediaGalleryPrefInfo::Type type = MediaGalleryPrefInfo::kAutoDetected; 134 base::string16 volume_label; 135 base::string16 vendor_name; 136 base::string16 model_name; 137 double total_size_in_bytes = 0.0; 138 double last_attach_time = 0.0; 139 bool volume_metadata_valid = false; 140 int prefs_version = 0; 141 142 if (!GetPrefId(dict, &pref_id) || 143 !dict.GetString(kMediaGalleriesDeviceIdKey, &device_id) || 144 !dict.GetString(kMediaGalleriesPathKey, &path) || 145 !GetType(dict, &type)) { 146 return false; 147 } 148 149 dict.GetString(kMediaGalleriesDisplayNameKey, &display_name); 150 dict.GetInteger(kMediaGalleriesPrefsVersionKey, &prefs_version); 151 152 if (dict.GetString(kMediaGalleriesVolumeLabelKey, &volume_label) && 153 dict.GetString(kMediaGalleriesVendorNameKey, &vendor_name) && 154 dict.GetString(kMediaGalleriesModelNameKey, &model_name) && 155 dict.GetDouble(kMediaGalleriesSizeKey, &total_size_in_bytes) && 156 dict.GetDouble(kMediaGalleriesLastAttachTimeKey, &last_attach_time)) { 157 volume_metadata_valid = true; 158 } 159 160 out_gallery_info->pref_id = pref_id; 161 out_gallery_info->display_name = display_name; 162 out_gallery_info->device_id = device_id; 163 out_gallery_info->path = base::FilePath(path); 164 out_gallery_info->type = type; 165 out_gallery_info->volume_label = volume_label; 166 out_gallery_info->vendor_name = vendor_name; 167 out_gallery_info->model_name = model_name; 168 out_gallery_info->total_size_in_bytes = total_size_in_bytes; 169 out_gallery_info->last_attach_time = 170 base::Time::FromInternalValue(last_attach_time); 171 out_gallery_info->volume_metadata_valid = volume_metadata_valid; 172 out_gallery_info->prefs_version = prefs_version; 173 174 return true; 175 } 176 177 DictionaryValue* CreateGalleryPrefInfoDictionary( 178 const MediaGalleryPrefInfo& gallery) { 179 DictionaryValue* dict = new DictionaryValue(); 180 dict->SetString(kMediaGalleriesPrefIdKey, 181 base::Uint64ToString(gallery.pref_id)); 182 if (!gallery.volume_metadata_valid) 183 dict->SetString(kMediaGalleriesDisplayNameKey, gallery.display_name); 184 dict->SetString(kMediaGalleriesDeviceIdKey, gallery.device_id); 185 dict->SetString(kMediaGalleriesPathKey, gallery.path.value()); 186 187 const char* type = NULL; 188 switch (gallery.type) { 189 case MediaGalleryPrefInfo::kAutoDetected: 190 type = kMediaGalleriesTypeAutoDetectedValue; 191 break; 192 case MediaGalleryPrefInfo::kUserAdded: 193 type = kMediaGalleriesTypeUserAddedValue; 194 break; 195 case MediaGalleryPrefInfo::kBlackListed: 196 type = kMediaGalleriesTypeBlackListedValue; 197 break; 198 default: 199 NOTREACHED(); 200 break; 201 } 202 dict->SetString(kMediaGalleriesTypeKey, type); 203 204 if (gallery.volume_metadata_valid) { 205 dict->SetString(kMediaGalleriesVolumeLabelKey, gallery.volume_label); 206 dict->SetString(kMediaGalleriesVendorNameKey, gallery.vendor_name); 207 dict->SetString(kMediaGalleriesModelNameKey, gallery.model_name); 208 dict->SetDouble(kMediaGalleriesSizeKey, gallery.total_size_in_bytes); 209 dict->SetDouble(kMediaGalleriesLastAttachTimeKey, 210 gallery.last_attach_time.ToInternalValue()); 211 } 212 213 // Version 0 of the prefs format was that the display_name was always 214 // used to show the user-visible name of the gallery. Version 1 means 215 // that there is an optional display_name, and when it is present, it 216 // overrides the name that would be built from the volume metadata, path, 217 // or whatever other data. So if we see a display_name with version 0, it 218 // means it may be overwritten simply by getting new volume metadata. 219 // A display_name with version 1 should not be overwritten. 220 dict->SetInteger(kMediaGalleriesPrefsVersionKey, gallery.prefs_version); 221 222 return dict; 223 } 224 225 bool HasAutoDetectedGalleryPermission(const extensions::Extension& extension) { 226 extensions::MediaGalleriesPermission::CheckParam param( 227 extensions::MediaGalleriesPermission::kAllAutoDetectedPermission); 228 return extensions::PermissionsData::CheckAPIPermissionWithParam( 229 &extension, extensions::APIPermission::kMediaGalleries, ¶m); 230 } 231 232 // Retrieves the MediaGalleryPermission from the given dictionary; DCHECKs on 233 // failure. 234 bool GetMediaGalleryPermissionFromDictionary( 235 const DictionaryValue* dict, 236 MediaGalleryPermission* out_permission) { 237 std::string string_id; 238 if (dict->GetString(kMediaGalleryIdKey, &string_id) && 239 base::StringToUint64(string_id, &out_permission->pref_id) && 240 dict->GetBoolean(kMediaGalleryHasPermissionKey, 241 &out_permission->has_permission)) { 242 return true; 243 } 244 NOTREACHED(); 245 return false; 246 } 247 248 base::string16 GetDisplayNameForDevice(uint64 storage_size_in_bytes, 249 const base::string16& name) { 250 DCHECK(!name.empty()); 251 return (storage_size_in_bytes == 0) ? 252 name : ui::FormatBytes(storage_size_in_bytes) + ASCIIToUTF16(" ") + name; 253 } 254 255 // For a device with |device_name| and a relative path |sub_folder|, construct 256 // a display name. If |sub_folder| is empty, then just return |device_name|. 257 base::string16 GetDisplayNameForSubFolder(const base::string16& device_name, 258 const base::FilePath& sub_folder) { 259 if (sub_folder.empty()) 260 return device_name; 261 return (sub_folder.BaseName().LossyDisplayName() + 262 ASCIIToUTF16(" - ") + 263 device_name); 264 } 265 266 base::string16 GetFullProductName(const base::string16& vendor_name, 267 const base::string16& model_name) { 268 if (vendor_name.empty() && model_name.empty()) 269 return base::string16(); 270 271 base::string16 product_name; 272 if (vendor_name.empty()) 273 product_name = model_name; 274 else if (model_name.empty()) 275 product_name = vendor_name; 276 else if (!vendor_name.empty() && !model_name.empty()) 277 product_name = vendor_name + UTF8ToUTF16(", ") + model_name; 278 279 return product_name; 280 } 281 282 } // namespace 283 284 MediaGalleryPrefInfo::MediaGalleryPrefInfo() 285 : pref_id(kInvalidMediaGalleryPrefId), 286 type(kInvalidType), 287 total_size_in_bytes(0), 288 volume_metadata_valid(false), 289 prefs_version(0) { 290 } 291 292 MediaGalleryPrefInfo::~MediaGalleryPrefInfo() {} 293 294 base::FilePath MediaGalleryPrefInfo::AbsolutePath() const { 295 base::FilePath base_path = MediaStorageUtil::FindDevicePathById(device_id); 296 DCHECK(!path.IsAbsolute()); 297 return base_path.empty() ? base_path : base_path.Append(path); 298 } 299 300 base::string16 MediaGalleryPrefInfo::GetGalleryDisplayName() const { 301 if (!StorageInfo::IsRemovableDevice(device_id)) { 302 // For fixed storage, the default name is the fully qualified directory 303 // name, or in the case of a root directory, the root directory name. 304 // Exception: ChromeOS -- the full pathname isn't visible there, so only 305 // the directory name is used. 306 base::FilePath path = AbsolutePath(); 307 if (!display_name.empty()) 308 return display_name; 309 310 #if defined(OS_CHROMEOS) 311 // See chrome/browser/chromeos/fileapi/file_system_backend.cc 312 base::FilePath home_path; 313 if (PathService::Get(base::DIR_HOME, &home_path)) { 314 home_path = home_path.AppendASCII("Downloads"); 315 base::FilePath relative; 316 if (home_path.AppendRelativePath(path, &relative)) 317 return relative.LossyDisplayName(); 318 } 319 return path.BaseName().LossyDisplayName(); 320 #else 321 return path.LossyDisplayName(); 322 #endif 323 } 324 325 base::string16 name = display_name; 326 if (name.empty()) 327 name = volume_label; 328 if (name.empty()) 329 name = GetFullProductName(vendor_name, model_name); 330 if (name.empty()) 331 name = l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_UNLABELED_DEVICE); 332 333 name = GetDisplayNameForDevice(total_size_in_bytes, name); 334 335 if (!path.empty()) 336 name = GetDisplayNameForSubFolder(name, path); 337 338 return name; 339 } 340 341 base::string16 MediaGalleryPrefInfo::GetGalleryTooltip() const { 342 return AbsolutePath().LossyDisplayName(); 343 } 344 345 base::string16 MediaGalleryPrefInfo::GetGalleryAdditionalDetails() const { 346 base::string16 attached; 347 if (StorageInfo::IsRemovableDevice(device_id)) { 348 if (MediaStorageUtil::IsRemovableStorageAttached(device_id)) { 349 attached = l10n_util::GetStringUTF16( 350 IDS_MEDIA_GALLERIES_DIALOG_DEVICE_ATTACHED); 351 } else if (!last_attach_time.is_null()) { 352 attached = l10n_util::GetStringFUTF16( 353 IDS_MEDIA_GALLERIES_LAST_ATTACHED, 354 base::TimeFormatShortDateNumeric(last_attach_time)); 355 } else { 356 attached = l10n_util::GetStringUTF16( 357 IDS_MEDIA_GALLERIES_DIALOG_DEVICE_NOT_ATTACHED); 358 } 359 } 360 361 return attached; 362 } 363 364 bool MediaGalleryPrefInfo::IsGalleryAvailable() const { 365 return !StorageInfo::IsRemovableDevice(device_id) || 366 MediaStorageUtil::IsRemovableStorageAttached(device_id); 367 } 368 369 MediaGalleriesPreferences::GalleryChangeObserver::~GalleryChangeObserver() {} 370 371 MediaGalleriesPreferences::MediaGalleriesPreferences(Profile* profile) 372 : initialized_(false), 373 pre_initialization_callbacks_waiting_(0), 374 profile_(profile), 375 extension_prefs_for_testing_(NULL), 376 weak_factory_(this) { 377 } 378 379 MediaGalleriesPreferences::~MediaGalleriesPreferences() { 380 if (StorageMonitor::GetInstance()) 381 StorageMonitor::GetInstance()->RemoveObserver(this); 382 } 383 384 void MediaGalleriesPreferences::EnsureInitialized(base::Closure callback) { 385 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 386 387 if (IsInitialized()) { 388 if (!callback.is_null()) 389 callback.Run(); 390 return; 391 } 392 393 on_initialize_callbacks_.push_back(callback); 394 if (on_initialize_callbacks_.size() > 1) 395 return; 396 397 // This counter must match the number of async methods dispatched below. 398 // It cannot be incremented inline with each callback, as some may return 399 // synchronously, decrement the counter to 0, and prematurely trigger 400 // FinishInitialization. 401 pre_initialization_callbacks_waiting_ = 3; 402 403 // Check whether we should be initializing -- are there any extensions that 404 // are using media galleries? 405 media_galleries::UsageCount(media_galleries::PREFS_INITIALIZED); 406 if (NumberExtensionsUsingMediaGalleries(profile_) == 0) { 407 media_galleries::UsageCount(media_galleries::PREFS_INITIALIZED_ERROR); 408 } 409 410 // We determine the freshness of the profile here, before any of the finders 411 // return and add media galleries to it. 412 StorageMonitor::GetInstance()->EnsureInitialized( 413 base::Bind(&MediaGalleriesPreferences::OnStorageMonitorInit, 414 weak_factory_.GetWeakPtr(), 415 !APIHasBeenUsed(profile_) /* add_default_galleries */)); 416 417 // Look for optional default galleries every time. 418 iapps::FindITunesLibrary( 419 base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID, 420 weak_factory_.GetWeakPtr())); 421 422 picasa::FindPicasaDatabase( 423 base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID, 424 weak_factory_.GetWeakPtr())); 425 426 #if 0 427 iapps::FindIPhotoLibrary( 428 base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID, 429 weak_factory_.GetWeakPtr())); 430 #endif 431 } 432 433 bool MediaGalleriesPreferences::IsInitialized() const { return initialized_; } 434 435 Profile* MediaGalleriesPreferences::profile() { return profile_; } 436 437 void MediaGalleriesPreferences::OnInitializationCallbackReturned() { 438 DCHECK(!IsInitialized()); 439 DCHECK(pre_initialization_callbacks_waiting_ > 0); 440 if (--pre_initialization_callbacks_waiting_ == 0) 441 FinishInitialization(); 442 } 443 444 void MediaGalleriesPreferences::FinishInitialization() { 445 DCHECK(!IsInitialized()); 446 447 initialized_ = true; 448 449 StorageMonitor* monitor = StorageMonitor::GetInstance(); 450 DCHECK(monitor->IsInitialized()); 451 452 InitFromPrefs(); 453 454 StorageMonitor::GetInstance()->AddObserver(this); 455 456 std::vector<StorageInfo> existing_devices = 457 monitor->GetAllAvailableStorages(); 458 for (size_t i = 0; i < existing_devices.size(); i++) { 459 if (!(StorageInfo::IsMediaDevice(existing_devices[i].device_id()) && 460 StorageInfo::IsRemovableDevice(existing_devices[i].device_id()))) 461 continue; 462 AddGallery(existing_devices[i].device_id(), 463 base::FilePath(), 464 false, 465 existing_devices[i].storage_label(), 466 existing_devices[i].vendor_name(), 467 existing_devices[i].model_name(), 468 existing_devices[i].total_size_in_bytes(), 469 base::Time::Now()); 470 } 471 472 for (std::vector<base::Closure>::iterator iter = 473 on_initialize_callbacks_.begin(); 474 iter != on_initialize_callbacks_.end(); 475 ++iter) { 476 iter->Run(); 477 } 478 on_initialize_callbacks_.clear(); 479 } 480 481 void MediaGalleriesPreferences::AddDefaultGalleries() { 482 const int kDirectoryKeys[] = { 483 chrome::DIR_USER_MUSIC, 484 chrome::DIR_USER_PICTURES, 485 chrome::DIR_USER_VIDEOS, 486 }; 487 488 for (size_t i = 0; i < arraysize(kDirectoryKeys); ++i) { 489 base::FilePath path; 490 if (!PathService::Get(kDirectoryKeys[i], &path)) 491 continue; 492 493 base::FilePath relative_path; 494 StorageInfo info; 495 if (MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) { 496 AddGalleryInternal(info.device_id(), info.name(), relative_path, false, 497 info.storage_label(), info.vendor_name(), 498 info.model_name(), info.total_size_in_bytes(), 499 base::Time(), true, 2); 500 } 501 } 502 } 503 504 bool MediaGalleriesPreferences::UpdateDeviceIDForSingletonType( 505 const std::string& device_id) { 506 StorageInfo::Type singleton_type; 507 if (!StorageInfo::CrackDeviceId(device_id, &singleton_type, NULL)) 508 return false; 509 510 PrefService* prefs = profile_->GetPrefs(); 511 scoped_ptr<ListPrefUpdate> update(new ListPrefUpdate( 512 prefs, prefs::kMediaGalleriesRememberedGalleries)); 513 ListValue* list = update->Get(); 514 for (ListValue::iterator iter = list->begin(); iter != list->end(); ++iter) { 515 // All of these calls should succeed, but preferences file can be corrupt. 516 DictionaryValue* dict; 517 if (!(*iter)->GetAsDictionary(&dict)) 518 continue; 519 std::string this_device_id; 520 if (!dict->GetString(kMediaGalleriesDeviceIdKey, &this_device_id)) 521 continue; 522 if (this_device_id == device_id) 523 return true; // No update is necessary. 524 StorageInfo::Type device_type; 525 if (!StorageInfo::CrackDeviceId(this_device_id, &device_type, NULL)) 526 continue; 527 528 if (device_type == singleton_type) { 529 dict->SetString(kMediaGalleriesDeviceIdKey, device_id); 530 update.reset(); // commits the update. 531 InitFromPrefs(); 532 MediaGalleryPrefId pref_id; 533 if (GetPrefId(*dict, &pref_id)) { 534 FOR_EACH_OBSERVER(GalleryChangeObserver, 535 gallery_change_observers_, 536 OnGalleryInfoUpdated(this, pref_id)); 537 } 538 return true; 539 } 540 } 541 return false; 542 } 543 544 void MediaGalleriesPreferences::OnStorageMonitorInit( 545 bool add_default_galleries) { 546 if (add_default_galleries) 547 AddDefaultGalleries(); 548 OnInitializationCallbackReturned(); 549 } 550 551 void MediaGalleriesPreferences::OnFinderDeviceID(const std::string& device_id) { 552 if (!device_id.empty() && !UpdateDeviceIDForSingletonType(device_id)) { 553 std::string gallery_name; 554 if (StorageInfo::IsIPhotoDevice(device_id)) 555 gallery_name = kIPhotoGalleryName; 556 else if (StorageInfo::IsITunesDevice(device_id)) 557 gallery_name = kITunesGalleryName; 558 else if (StorageInfo::IsPicasaDevice(device_id)) 559 gallery_name = kPicasaGalleryName; 560 else 561 NOTREACHED(); 562 563 AddGalleryInternal(device_id, ASCIIToUTF16(gallery_name), 564 base::FilePath(), false /*not user added*/, 565 base::string16(), base::string16(), base::string16(), 0, 566 base::Time(), false, 2); 567 } 568 569 OnInitializationCallbackReturned(); 570 } 571 572 void MediaGalleriesPreferences::InitFromPrefs() { 573 known_galleries_.clear(); 574 device_map_.clear(); 575 576 PrefService* prefs = profile_->GetPrefs(); 577 const ListValue* list = prefs->GetList( 578 prefs::kMediaGalleriesRememberedGalleries); 579 if (list) { 580 for (ListValue::const_iterator it = list->begin(); 581 it != list->end(); ++it) { 582 const DictionaryValue* dict = NULL; 583 if (!(*it)->GetAsDictionary(&dict)) 584 continue; 585 586 MediaGalleryPrefInfo gallery_info; 587 if (!PopulateGalleryPrefInfoFromDictionary(*dict, &gallery_info)) 588 continue; 589 590 known_galleries_[gallery_info.pref_id] = gallery_info; 591 device_map_[gallery_info.device_id].insert(gallery_info.pref_id); 592 } 593 } 594 } 595 596 void MediaGalleriesPreferences::AddGalleryChangeObserver( 597 GalleryChangeObserver* observer) { 598 DCHECK(IsInitialized()); 599 gallery_change_observers_.AddObserver(observer); 600 } 601 602 void MediaGalleriesPreferences::RemoveGalleryChangeObserver( 603 GalleryChangeObserver* observer) { 604 DCHECK(IsInitialized()); 605 gallery_change_observers_.RemoveObserver(observer); 606 } 607 608 void MediaGalleriesPreferences::OnRemovableStorageAttached( 609 const StorageInfo& info) { 610 DCHECK(IsInitialized()); 611 if (!StorageInfo::IsMediaDevice(info.device_id())) 612 return; 613 614 AddGallery(info.device_id(), base::FilePath(), 615 false /*not user added*/, 616 info.storage_label(), 617 info.vendor_name(), 618 info.model_name(), 619 info.total_size_in_bytes(), 620 base::Time::Now()); 621 } 622 623 bool MediaGalleriesPreferences::LookUpGalleryByPath( 624 const base::FilePath& path, 625 MediaGalleryPrefInfo* gallery_info) const { 626 DCHECK(IsInitialized()); 627 StorageInfo info; 628 base::FilePath relative_path; 629 if (!MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) { 630 if (gallery_info) 631 *gallery_info = MediaGalleryPrefInfo(); 632 return false; 633 } 634 635 relative_path = relative_path.NormalizePathSeparators(); 636 MediaGalleryPrefIdSet galleries_on_device = 637 LookUpGalleriesByDeviceId(info.device_id()); 638 for (MediaGalleryPrefIdSet::const_iterator it = galleries_on_device.begin(); 639 it != galleries_on_device.end(); 640 ++it) { 641 const MediaGalleryPrefInfo& gallery = known_galleries_.find(*it)->second; 642 if (gallery.path != relative_path) 643 continue; 644 645 if (gallery_info) 646 *gallery_info = gallery; 647 return true; 648 } 649 650 // This method is called by controller::FilesSelected when the user 651 // adds a new gallery. Control reaches here when the selected gallery is 652 // on a volume we know about, but have no gallery already for. Returns 653 // hypothetical data to the caller about what the prefs will look like 654 // if the gallery is added. 655 // TODO(gbillock): split this out into another function so it doesn't 656 // conflate LookUp. 657 if (gallery_info) { 658 gallery_info->pref_id = kInvalidMediaGalleryPrefId; 659 gallery_info->device_id = info.device_id(); 660 gallery_info->path = relative_path; 661 gallery_info->type = MediaGalleryPrefInfo::kUserAdded; 662 gallery_info->volume_label = info.storage_label(); 663 gallery_info->vendor_name = info.vendor_name(); 664 gallery_info->model_name = info.model_name(); 665 gallery_info->total_size_in_bytes = info.total_size_in_bytes(); 666 gallery_info->last_attach_time = base::Time::Now(); 667 gallery_info->volume_metadata_valid = true; 668 gallery_info->prefs_version = 2; 669 } 670 return false; 671 } 672 673 MediaGalleryPrefIdSet MediaGalleriesPreferences::LookUpGalleriesByDeviceId( 674 const std::string& device_id) const { 675 DeviceIdPrefIdsMap::const_iterator found = device_map_.find(device_id); 676 if (found == device_map_.end()) 677 return MediaGalleryPrefIdSet(); 678 return found->second; 679 } 680 681 base::FilePath MediaGalleriesPreferences::LookUpGalleryPathForExtension( 682 MediaGalleryPrefId gallery_id, 683 const extensions::Extension* extension, 684 bool include_unpermitted_galleries) { 685 DCHECK(IsInitialized()); 686 DCHECK(extension); 687 if (!include_unpermitted_galleries && 688 !ContainsKey(GalleriesForExtension(*extension), gallery_id)) 689 return base::FilePath(); 690 691 MediaGalleriesPrefInfoMap::const_iterator it = 692 known_galleries_.find(gallery_id); 693 if (it == known_galleries_.end()) 694 return base::FilePath(); 695 return MediaStorageUtil::FindDevicePathById(it->second.device_id); 696 } 697 698 MediaGalleryPrefId MediaGalleriesPreferences::AddGallery( 699 const std::string& device_id, 700 const base::FilePath& relative_path, bool user_added, 701 const base::string16& volume_label, const base::string16& vendor_name, 702 const base::string16& model_name, uint64 total_size_in_bytes, 703 base::Time last_attach_time) { 704 DCHECK(IsInitialized()); 705 return AddGalleryInternal(device_id, base::string16(), relative_path, 706 user_added, volume_label, vendor_name, model_name, 707 total_size_in_bytes, last_attach_time, true, 2); 708 } 709 710 MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryInternal( 711 const std::string& device_id, const base::string16& display_name, 712 const base::FilePath& relative_path, bool user_added, 713 const base::string16& volume_label, const base::string16& vendor_name, 714 const base::string16& model_name, uint64 total_size_in_bytes, 715 base::Time last_attach_time, 716 bool volume_metadata_valid, 717 int prefs_version) { 718 base::FilePath normalized_relative_path = 719 relative_path.NormalizePathSeparators(); 720 MediaGalleryPrefIdSet galleries_on_device = 721 LookUpGalleriesByDeviceId(device_id); 722 for (MediaGalleryPrefIdSet::const_iterator pref_id_it = 723 galleries_on_device.begin(); 724 pref_id_it != galleries_on_device.end(); 725 ++pref_id_it) { 726 const MediaGalleryPrefInfo& existing = 727 known_galleries_.find(*pref_id_it)->second; 728 if (existing.path != normalized_relative_path) 729 continue; 730 731 bool update_gallery_type = 732 user_added && (existing.type == MediaGalleryPrefInfo::kBlackListed); 733 // Status quo: In M27 and M28, galleries added manually use version 0, 734 // and galleries added automatically (including default galleries) use 735 // version 1. The name override is used by default galleries as well 736 // as all device attach events. 737 // We want to upgrade the name if the existing version is < 2. Leave it 738 // alone if the existing display name is set with version == 2 and the 739 // proposed new name is empty. 740 bool update_gallery_name = existing.display_name != display_name; 741 if (existing.prefs_version == 2 && !existing.display_name.empty() && 742 display_name.empty()) { 743 update_gallery_name = false; 744 } 745 bool update_gallery_metadata = volume_metadata_valid && 746 ((existing.volume_label != volume_label) || 747 (existing.vendor_name != vendor_name) || 748 (existing.model_name != model_name) || 749 (existing.total_size_in_bytes != total_size_in_bytes) || 750 (existing.last_attach_time != last_attach_time)); 751 752 if (!update_gallery_name && !update_gallery_type && 753 !update_gallery_metadata) 754 return *pref_id_it; 755 756 PrefService* prefs = profile_->GetPrefs(); 757 scoped_ptr<ListPrefUpdate> update( 758 new ListPrefUpdate(prefs, prefs::kMediaGalleriesRememberedGalleries)); 759 ListValue* list = update->Get(); 760 761 for (ListValue::const_iterator list_iter = list->begin(); 762 list_iter != list->end(); 763 ++list_iter) { 764 DictionaryValue* dict; 765 MediaGalleryPrefId iter_id; 766 if ((*list_iter)->GetAsDictionary(&dict) && 767 GetPrefId(*dict, &iter_id) && 768 *pref_id_it == iter_id) { 769 if (update_gallery_type) { 770 dict->SetString(kMediaGalleriesTypeKey, 771 kMediaGalleriesTypeAutoDetectedValue); 772 } 773 if (update_gallery_name) 774 dict->SetString(kMediaGalleriesDisplayNameKey, display_name); 775 if (update_gallery_metadata) { 776 dict->SetString(kMediaGalleriesVolumeLabelKey, volume_label); 777 dict->SetString(kMediaGalleriesVendorNameKey, vendor_name); 778 dict->SetString(kMediaGalleriesModelNameKey, model_name); 779 dict->SetDouble(kMediaGalleriesSizeKey, total_size_in_bytes); 780 dict->SetDouble(kMediaGalleriesLastAttachTimeKey, 781 last_attach_time.ToInternalValue()); 782 } 783 dict->SetInteger(kMediaGalleriesPrefsVersionKey, prefs_version); 784 break; 785 } 786 } 787 788 // Commits the prefs update. 789 update.reset(); 790 791 if (update_gallery_name || update_gallery_metadata || 792 update_gallery_type) { 793 InitFromPrefs(); 794 FOR_EACH_OBSERVER(GalleryChangeObserver, 795 gallery_change_observers_, 796 OnGalleryInfoUpdated(this, *pref_id_it)); 797 } 798 return *pref_id_it; 799 } 800 801 PrefService* prefs = profile_->GetPrefs(); 802 803 MediaGalleryPrefInfo gallery_info; 804 gallery_info.pref_id = prefs->GetUint64(prefs::kMediaGalleriesUniqueId); 805 prefs->SetUint64(prefs::kMediaGalleriesUniqueId, gallery_info.pref_id + 1); 806 gallery_info.display_name = display_name; 807 gallery_info.device_id = device_id; 808 gallery_info.path = normalized_relative_path; 809 gallery_info.type = MediaGalleryPrefInfo::kAutoDetected; 810 if (user_added) 811 gallery_info.type = MediaGalleryPrefInfo::kUserAdded; 812 if (volume_metadata_valid) { 813 gallery_info.volume_label = volume_label; 814 gallery_info.vendor_name = vendor_name; 815 gallery_info.model_name = model_name; 816 gallery_info.total_size_in_bytes = total_size_in_bytes; 817 gallery_info.last_attach_time = last_attach_time; 818 } 819 gallery_info.volume_metadata_valid = volume_metadata_valid; 820 gallery_info.prefs_version = prefs_version; 821 822 { 823 ListPrefUpdate update(prefs, prefs::kMediaGalleriesRememberedGalleries); 824 ListValue* list = update.Get(); 825 list->Append(CreateGalleryPrefInfoDictionary(gallery_info)); 826 } 827 InitFromPrefs(); 828 FOR_EACH_OBSERVER(GalleryChangeObserver, 829 gallery_change_observers_, 830 OnGalleryAdded(this, gallery_info.pref_id)); 831 832 return gallery_info.pref_id; 833 } 834 835 MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryByPath( 836 const base::FilePath& path) { 837 DCHECK(IsInitialized()); 838 MediaGalleryPrefInfo gallery_info; 839 if (LookUpGalleryByPath(path, &gallery_info) && 840 gallery_info.type != MediaGalleryPrefInfo::kBlackListed) { 841 return gallery_info.pref_id; 842 } 843 return AddGalleryInternal(gallery_info.device_id, 844 gallery_info.display_name, 845 gallery_info.path, 846 true /*user added*/, 847 gallery_info.volume_label, 848 gallery_info.vendor_name, 849 gallery_info.model_name, 850 gallery_info.total_size_in_bytes, 851 gallery_info.last_attach_time, 852 gallery_info.volume_metadata_valid, 853 gallery_info.prefs_version); 854 } 855 856 void MediaGalleriesPreferences::ForgetGalleryById(MediaGalleryPrefId pref_id) { 857 DCHECK(IsInitialized()); 858 PrefService* prefs = profile_->GetPrefs(); 859 scoped_ptr<ListPrefUpdate> update(new ListPrefUpdate( 860 prefs, prefs::kMediaGalleriesRememberedGalleries)); 861 ListValue* list = update->Get(); 862 863 if (!ContainsKey(known_galleries_, pref_id)) 864 return; 865 866 for (ListValue::iterator iter = list->begin(); iter != list->end(); ++iter) { 867 DictionaryValue* dict; 868 MediaGalleryPrefId iter_id; 869 if ((*iter)->GetAsDictionary(&dict) && GetPrefId(*dict, &iter_id) && 870 pref_id == iter_id) { 871 RemoveGalleryPermissionsFromPrefs(pref_id); 872 MediaGalleryPrefInfo::Type type; 873 if (GetType(*dict, &type) && 874 type == MediaGalleryPrefInfo::kAutoDetected) { 875 dict->SetString(kMediaGalleriesTypeKey, 876 kMediaGalleriesTypeBlackListedValue); 877 } else { 878 list->Erase(iter, NULL); 879 } 880 update.reset(NULL); // commits the update. 881 882 InitFromPrefs(); 883 FOR_EACH_OBSERVER(GalleryChangeObserver, 884 gallery_change_observers_, 885 OnGalleryRemoved(this, pref_id)); 886 return; 887 } 888 } 889 } 890 891 MediaGalleryPrefIdSet MediaGalleriesPreferences::GalleriesForExtension( 892 const extensions::Extension& extension) const { 893 DCHECK(IsInitialized()); 894 MediaGalleryPrefIdSet result; 895 896 if (HasAutoDetectedGalleryPermission(extension)) { 897 for (MediaGalleriesPrefInfoMap::const_iterator it = 898 known_galleries_.begin(); it != known_galleries_.end(); ++it) { 899 if (it->second.type == MediaGalleryPrefInfo::kAutoDetected) 900 result.insert(it->second.pref_id); 901 } 902 } 903 904 std::vector<MediaGalleryPermission> stored_permissions = 905 GetGalleryPermissionsFromPrefs(extension.id()); 906 for (std::vector<MediaGalleryPermission>::const_iterator it = 907 stored_permissions.begin(); it != stored_permissions.end(); ++it) { 908 if (!it->has_permission) { 909 result.erase(it->pref_id); 910 } else { 911 MediaGalleriesPrefInfoMap::const_iterator gallery = 912 known_galleries_.find(it->pref_id); 913 DCHECK(gallery != known_galleries_.end()); 914 if (gallery->second.type != MediaGalleryPrefInfo::kBlackListed) { 915 result.insert(it->pref_id); 916 } else { 917 NOTREACHED() << gallery->second.device_id; 918 } 919 } 920 } 921 return result; 922 } 923 924 bool MediaGalleriesPreferences::SetGalleryPermissionForExtension( 925 const extensions::Extension& extension, 926 MediaGalleryPrefId pref_id, 927 bool has_permission) { 928 DCHECK(IsInitialized()); 929 // The gallery may not exist anymore if the user opened a second config 930 // surface concurrently and removed it. Drop the permission update if so. 931 MediaGalleriesPrefInfoMap::const_iterator gallery_info = 932 known_galleries_.find(pref_id); 933 if (gallery_info == known_galleries_.end()) 934 return false; 935 936 bool default_permission = false; 937 if (gallery_info->second.type == MediaGalleryPrefInfo::kAutoDetected) 938 default_permission = HasAutoDetectedGalleryPermission(extension); 939 // When the permission matches the default, we don't need to remember it. 940 if (has_permission == default_permission) { 941 if (!UnsetGalleryPermissionInPrefs(extension.id(), pref_id)) 942 // If permission wasn't set, assume nothing has changed. 943 return false; 944 } else { 945 if (!SetGalleryPermissionInPrefs(extension.id(), pref_id, has_permission)) 946 return false; 947 } 948 if (has_permission) 949 FOR_EACH_OBSERVER(GalleryChangeObserver, 950 gallery_change_observers_, 951 OnPermissionAdded(this, extension.id(), pref_id)); 952 else 953 FOR_EACH_OBSERVER(GalleryChangeObserver, 954 gallery_change_observers_, 955 OnPermissionRemoved(this, extension.id(), pref_id)); 956 return true; 957 } 958 959 const MediaGalleriesPrefInfoMap& MediaGalleriesPreferences::known_galleries() 960 const { 961 DCHECK(IsInitialized()); 962 return known_galleries_; 963 } 964 965 void MediaGalleriesPreferences::Shutdown() { 966 weak_factory_.InvalidateWeakPtrs(); 967 profile_ = NULL; 968 } 969 970 // static 971 bool MediaGalleriesPreferences::APIHasBeenUsed(Profile* profile) { 972 MediaGalleryPrefId current_id = 973 profile->GetPrefs()->GetUint64(prefs::kMediaGalleriesUniqueId); 974 return current_id != kInvalidMediaGalleryPrefId + 1; 975 } 976 977 // static 978 void MediaGalleriesPreferences::RegisterProfilePrefs( 979 user_prefs::PrefRegistrySyncable* registry) { 980 registry->RegisterListPref(prefs::kMediaGalleriesRememberedGalleries, 981 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 982 registry->RegisterUint64Pref( 983 prefs::kMediaGalleriesUniqueId, 984 kInvalidMediaGalleryPrefId + 1, 985 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 986 } 987 988 bool MediaGalleriesPreferences::SetGalleryPermissionInPrefs( 989 const std::string& extension_id, 990 MediaGalleryPrefId gallery_id, 991 bool has_access) { 992 DCHECK(IsInitialized()); 993 ExtensionPrefs::ScopedListUpdate update(GetExtensionPrefs(), 994 extension_id, 995 kMediaGalleriesPermissions); 996 ListValue* permissions = update.Get(); 997 if (!permissions) { 998 permissions = update.Create(); 999 } else { 1000 // If the gallery is already in the list, update the permission... 1001 for (ListValue::iterator iter = permissions->begin(); 1002 iter != permissions->end(); ++iter) { 1003 DictionaryValue* dict = NULL; 1004 if (!(*iter)->GetAsDictionary(&dict)) 1005 continue; 1006 MediaGalleryPermission perm; 1007 if (!GetMediaGalleryPermissionFromDictionary(dict, &perm)) 1008 continue; 1009 if (perm.pref_id == gallery_id) { 1010 if (has_access != perm.has_permission) { 1011 dict->SetBoolean(kMediaGalleryHasPermissionKey, has_access); 1012 return true; 1013 } else { 1014 return false; 1015 } 1016 } 1017 } 1018 } 1019 // ...Otherwise, add a new entry for the gallery. 1020 DictionaryValue* dict = new DictionaryValue; 1021 dict->SetString(kMediaGalleryIdKey, base::Uint64ToString(gallery_id)); 1022 dict->SetBoolean(kMediaGalleryHasPermissionKey, has_access); 1023 permissions->Append(dict); 1024 return true; 1025 } 1026 1027 bool MediaGalleriesPreferences::UnsetGalleryPermissionInPrefs( 1028 const std::string& extension_id, 1029 MediaGalleryPrefId gallery_id) { 1030 DCHECK(IsInitialized()); 1031 ExtensionPrefs::ScopedListUpdate update(GetExtensionPrefs(), 1032 extension_id, 1033 kMediaGalleriesPermissions); 1034 ListValue* permissions = update.Get(); 1035 if (!permissions) 1036 return false; 1037 1038 for (ListValue::iterator iter = permissions->begin(); 1039 iter != permissions->end(); ++iter) { 1040 const DictionaryValue* dict = NULL; 1041 if (!(*iter)->GetAsDictionary(&dict)) 1042 continue; 1043 MediaGalleryPermission perm; 1044 if (!GetMediaGalleryPermissionFromDictionary(dict, &perm)) 1045 continue; 1046 if (perm.pref_id == gallery_id) { 1047 permissions->Erase(iter, NULL); 1048 return true; 1049 } 1050 } 1051 return false; 1052 } 1053 1054 std::vector<MediaGalleryPermission> 1055 MediaGalleriesPreferences::GetGalleryPermissionsFromPrefs( 1056 const std::string& extension_id) const { 1057 DCHECK(IsInitialized()); 1058 std::vector<MediaGalleryPermission> result; 1059 const ListValue* permissions; 1060 if (!GetExtensionPrefs()->ReadPrefAsList(extension_id, 1061 kMediaGalleriesPermissions, 1062 &permissions)) { 1063 return result; 1064 } 1065 1066 for (ListValue::const_iterator iter = permissions->begin(); 1067 iter != permissions->end(); ++iter) { 1068 DictionaryValue* dict = NULL; 1069 if (!(*iter)->GetAsDictionary(&dict)) 1070 continue; 1071 MediaGalleryPermission perm; 1072 if (!GetMediaGalleryPermissionFromDictionary(dict, &perm)) 1073 continue; 1074 result.push_back(perm); 1075 } 1076 1077 return result; 1078 } 1079 1080 void MediaGalleriesPreferences::RemoveGalleryPermissionsFromPrefs( 1081 MediaGalleryPrefId gallery_id) { 1082 DCHECK(IsInitialized()); 1083 ExtensionPrefs* prefs = GetExtensionPrefs(); 1084 const DictionaryValue* extensions = 1085 prefs->pref_service()->GetDictionary(prefs::kExtensionsPref); 1086 if (!extensions) 1087 return; 1088 1089 for (DictionaryValue::Iterator iter(*extensions); !iter.IsAtEnd(); 1090 iter.Advance()) { 1091 if (!extensions::Extension::IdIsValid(iter.key())) { 1092 NOTREACHED(); 1093 continue; 1094 } 1095 UnsetGalleryPermissionInPrefs(iter.key(), gallery_id); 1096 } 1097 } 1098 1099 ExtensionPrefs* MediaGalleriesPreferences::GetExtensionPrefs() const { 1100 DCHECK(IsInitialized()); 1101 if (extension_prefs_for_testing_) 1102 return extension_prefs_for_testing_; 1103 return extensions::ExtensionPrefs::Get(profile_); 1104 } 1105 1106 void MediaGalleriesPreferences::SetExtensionPrefsForTesting( 1107 extensions::ExtensionPrefs* extension_prefs) { 1108 DCHECK(IsInitialized()); 1109 extension_prefs_for_testing_ = extension_prefs; 1110 } 1111