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