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