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