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