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