media_galleries_preferences.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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/path_service.h"
8#include "base/prefs/pref_service.h"
9#include "base/stl_util.h"
10#include "base/string16.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/utf_string_conversions.h"
13#include "base/values.h"
14#include "chrome/browser/browser_process.h"
15#include "chrome/browser/extensions/api/media_galleries_private/gallery_watch_state_tracker.h"
16#include "chrome/browser/extensions/api/media_galleries_private/media_galleries_private_api.h"
17#include "chrome/browser/extensions/extension_prefs.h"
18#include "chrome/browser/extensions/extension_service.h"
19#include "chrome/browser/extensions/extension_system.h"
20#include "chrome/browser/media_galleries/fileapi/itunes_finder.h"
21#include "chrome/browser/media_galleries/fileapi/picasa/picasa_finder.h"
22#include "chrome/browser/media_galleries/media_file_system_registry.h"
23#include "chrome/browser/prefs/scoped_user_pref_update.h"
24#include "chrome/browser/profiles/profile.h"
25#include "chrome/browser/storage_monitor/media_storage_util.h"
26#include "chrome/browser/storage_monitor/storage_monitor.h"
27#include "chrome/common/chrome_paths.h"
28#include "chrome/common/extensions/extension.h"
29#include "chrome/common/extensions/permissions/api_permission.h"
30#include "chrome/common/extensions/permissions/media_galleries_permission.h"
31#include "chrome/common/extensions/permissions/permissions_data.h"
32#include "chrome/common/pref_names.h"
33#include "components/user_prefs/pref_registry_syncable.h"
34
35namespace chrome {
36
37namespace {
38
39const char kMediaGalleriesDeviceIdKey[] = "deviceId";
40const char kMediaGalleriesDisplayNameKey[] = "displayName";
41const char kMediaGalleriesPathKey[] = "path";
42const char kMediaGalleriesPrefIdKey[] = "prefId";
43const char kMediaGalleriesTypeKey[] = "type";
44const char kMediaGalleriesVolumeLabelKey[] = "volumeLabel";
45const char kMediaGalleriesVendorNameKey[] = "vendorName";
46const char kMediaGalleriesModelNameKey[] = "modelName";
47const char kMediaGalleriesSizeKey[] = "totalSize";
48const char kMediaGalleriesLastAttachTimeKey[] = "lastAttachTime";
49const char kMediaGalleriesPrefsVersionKey[] = "preferencesVersion";
50
51const char kMediaGalleriesTypeAutoDetectedValue[] = "autoDetected";
52const char kMediaGalleriesTypeUserAddedValue[] = "userAdded";
53const char kMediaGalleriesTypeBlackListedValue[] = "blackListed";
54
55const char kITunesGalleryName[] = "iTunes";
56const char kPicasaGalleryName[] = "Picasa";
57
58bool GetPrefId(const DictionaryValue& dict, MediaGalleryPrefId* value) {
59  std::string string_id;
60  if (!dict.GetString(kMediaGalleriesPrefIdKey, &string_id) ||
61      !base::StringToUint64(string_id, value)) {
62    return false;
63  }
64
65  return true;
66}
67
68bool GetType(const DictionaryValue& dict, MediaGalleryPrefInfo::Type* type) {
69  std::string string_type;
70  if (!dict.GetString(kMediaGalleriesTypeKey, &string_type))
71    return false;
72
73  if (string_type == kMediaGalleriesTypeAutoDetectedValue) {
74    *type = MediaGalleryPrefInfo::kAutoDetected;
75    return true;
76  }
77  if (string_type == kMediaGalleriesTypeUserAddedValue) {
78    *type = MediaGalleryPrefInfo::kUserAdded;
79    return true;
80  }
81  if (string_type == kMediaGalleriesTypeBlackListedValue) {
82    *type = MediaGalleryPrefInfo::kBlackListed;
83    return true;
84  }
85
86  return false;
87}
88
89bool PopulateGalleryPrefInfoFromDictionary(
90    const DictionaryValue& dict, MediaGalleryPrefInfo* out_gallery_info) {
91  MediaGalleryPrefId pref_id;
92  string16 display_name;
93  std::string device_id;
94  base::FilePath::StringType path;
95  MediaGalleryPrefInfo::Type type = MediaGalleryPrefInfo::kAutoDetected;
96  string16 volume_label;
97  string16 vendor_name;
98  string16 model_name;
99  double total_size_in_bytes = 0.0;
100  double last_attach_time = 0.0;
101  bool volume_metadata_valid = false;
102  int prefs_version = 0;
103
104  if (!GetPrefId(dict, &pref_id) ||
105      !dict.GetString(kMediaGalleriesDeviceIdKey, &device_id) ||
106      !dict.GetString(kMediaGalleriesPathKey, &path) ||
107      !GetType(dict, &type)) {
108    return false;
109  }
110
111  dict.GetString(kMediaGalleriesDisplayNameKey, &display_name);
112  dict.GetInteger(kMediaGalleriesPrefsVersionKey, &prefs_version);
113
114  if (dict.GetString(kMediaGalleriesVolumeLabelKey, &volume_label) &&
115      dict.GetString(kMediaGalleriesVendorNameKey, &vendor_name) &&
116      dict.GetString(kMediaGalleriesModelNameKey, &model_name) &&
117      dict.GetDouble(kMediaGalleriesSizeKey, &total_size_in_bytes) &&
118      dict.GetDouble(kMediaGalleriesLastAttachTimeKey, &last_attach_time)) {
119    volume_metadata_valid = true;
120  }
121
122  out_gallery_info->pref_id = pref_id;
123  out_gallery_info->display_name = display_name;
124  out_gallery_info->device_id = device_id;
125  out_gallery_info->path = base::FilePath(path);
126  out_gallery_info->type = type;
127  out_gallery_info->volume_label = volume_label;
128  out_gallery_info->vendor_name = vendor_name;
129  out_gallery_info->model_name = model_name;
130  out_gallery_info->total_size_in_bytes = total_size_in_bytes;
131  out_gallery_info->last_attach_time =
132      base::Time::FromInternalValue(last_attach_time);
133  out_gallery_info->volume_metadata_valid = volume_metadata_valid;
134  out_gallery_info->prefs_version = prefs_version;
135
136  return true;
137}
138
139DictionaryValue* CreateGalleryPrefInfoDictionary(
140    const MediaGalleryPrefInfo& gallery) {
141  DictionaryValue* dict = new DictionaryValue();
142  dict->SetString(kMediaGalleriesPrefIdKey,
143                  base::Uint64ToString(gallery.pref_id));
144  if (!gallery.volume_metadata_valid)
145    dict->SetString(kMediaGalleriesDisplayNameKey, gallery.display_name);
146  dict->SetString(kMediaGalleriesDeviceIdKey, gallery.device_id);
147  dict->SetString(kMediaGalleriesPathKey, gallery.path.value());
148
149  const char* type = NULL;
150  switch (gallery.type) {
151    case MediaGalleryPrefInfo::kAutoDetected:
152      type = kMediaGalleriesTypeAutoDetectedValue;
153      break;
154    case MediaGalleryPrefInfo::kUserAdded:
155      type = kMediaGalleriesTypeUserAddedValue;
156      break;
157    case MediaGalleryPrefInfo::kBlackListed:
158      type = kMediaGalleriesTypeBlackListedValue;
159      break;
160    default:
161      NOTREACHED();
162      break;
163  }
164  dict->SetString(kMediaGalleriesTypeKey, type);
165
166  if (gallery.volume_metadata_valid) {
167    dict->SetString(kMediaGalleriesVolumeLabelKey, gallery.volume_label);
168    dict->SetString(kMediaGalleriesVendorNameKey, gallery.vendor_name);
169    dict->SetString(kMediaGalleriesModelNameKey, gallery.model_name);
170    dict->SetDouble(kMediaGalleriesSizeKey, gallery.total_size_in_bytes);
171    dict->SetDouble(kMediaGalleriesLastAttachTimeKey,
172                    gallery.last_attach_time.ToInternalValue());
173  }
174
175  // Version 0 of the prefs format was that the display_name was always
176  // used to show the user-visible name of the gallery. Version 1 means
177  // that there is an optional display_name, and when it is present, it
178  // overrides the name that would be built from the volume metadata, path,
179  // or whatever other data. So if we see a display_name with version 0, it
180  // means it may be overwritten simply by getting new volume metadata.
181  // A display_name with version 1 should not be overwritten.
182  dict->SetInteger(kMediaGalleriesPrefsVersionKey, gallery.prefs_version);
183
184  return dict;
185}
186
187bool HasAutoDetectedGalleryPermission(const extensions::Extension& extension) {
188  extensions::MediaGalleriesPermission::CheckParam param(
189      extensions::MediaGalleriesPermission::kAllAutoDetectedPermission);
190  return extensions::PermissionsData::CheckAPIPermissionWithParam(
191      &extension, extensions::APIPermission::kMediaGalleries, &param);
192}
193
194}  // namespace
195
196MediaGalleryPrefInfo::MediaGalleryPrefInfo()
197    : pref_id(kInvalidMediaGalleryPrefId),
198      type(kInvalidType),
199      total_size_in_bytes(0),
200      volume_metadata_valid(false),
201      prefs_version(0) {
202}
203
204MediaGalleryPrefInfo::~MediaGalleryPrefInfo() {}
205
206base::FilePath MediaGalleryPrefInfo::AbsolutePath() const {
207  base::FilePath base_path = MediaStorageUtil::FindDevicePathById(device_id);
208  DCHECK(!path.IsAbsolute());
209  return base_path.empty() ? base_path : base_path.Append(path);
210}
211
212MediaGalleriesPreferences::GalleryChangeObserver::~GalleryChangeObserver() {}
213
214MediaGalleriesPreferences::MediaGalleriesPreferences(Profile* profile)
215    : weak_factory_(this),
216      profile_(profile) {
217  AddDefaultGalleriesIfFreshProfile();
218
219  // TODO(vandebo) Turn this back on when the iTunes code is ready.
220  // Temporarily turned off because it adds an extra user-visible entry to the
221  // preferences that does not quite work.
222#if 0
223  // Look for optional default galleries every time.
224  itunes::ITunesFinder::FindITunesLibrary(
225      base::Bind(&MediaGalleriesPreferences::OnITunesDeviceID,
226                 weak_factory_.GetWeakPtr()));
227#endif
228
229  // TODO(tommycli): Turn on when Picasa code is ready.
230#if 0
231  picasa::PicasaFinder::FindPicasaDatabaseOnUIThread(
232      base::Bind(&MediaGalleriesPreferences::OnPicasaDeviceID,
233                 weak_factory_.GetWeakPtr()));
234#endif
235
236  InitFromPrefs(false /*no notification*/);
237
238  StorageMonitor* monitor = StorageMonitor::GetInstance();
239  if (monitor)
240    monitor->AddObserver(this);
241}
242
243MediaGalleriesPreferences::~MediaGalleriesPreferences() {
244  StorageMonitor* monitor = StorageMonitor::GetInstance();
245  if (monitor)
246    monitor->RemoveObserver(this);
247}
248
249void MediaGalleriesPreferences::AddDefaultGalleriesIfFreshProfile() {
250  // Only add defaults the first time.
251  if (APIHasBeenUsed(profile_))
252    return;
253
254  // Fresh profile case.
255  const int kDirectoryKeys[] = {
256    DIR_USER_MUSIC,
257    DIR_USER_PICTURES,
258    DIR_USER_VIDEOS,
259  };
260
261  for (size_t i = 0; i < arraysize(kDirectoryKeys); ++i) {
262    base::FilePath path;
263    if (!PathService::Get(kDirectoryKeys[i], &path))
264      continue;
265
266    base::FilePath relative_path;
267    StorageInfo info;
268    if (MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) {
269      // TODO(gbillock): Add in the volume metadata here when available.
270      AddGalleryWithName(info.device_id(), info.name(), relative_path,
271                         false /*user added*/);
272    }
273  }
274}
275
276void MediaGalleriesPreferences::OnITunesDeviceID(const std::string& device_id) {
277  DCHECK(!device_id.empty());
278  AddGalleryWithName(device_id, ASCIIToUTF16(kITunesGalleryName),
279                     base::FilePath(), false /*not user added*/);
280}
281
282void MediaGalleriesPreferences::OnPicasaDeviceID(const std::string& device_id) {
283  // TODO(tommycli): Implement support for location moves.
284  DCHECK(!device_id.empty());
285  AddGalleryWithName(device_id, ASCIIToUTF16(kPicasaGalleryName),
286                     base::FilePath(), false /*not user added*/);
287}
288
289void MediaGalleriesPreferences::InitFromPrefs(bool notify_observers) {
290  known_galleries_.clear();
291  device_map_.clear();
292
293  PrefService* prefs = profile_->GetPrefs();
294  const ListValue* list = prefs->GetList(
295      prefs::kMediaGalleriesRememberedGalleries);
296  if (list) {
297    for (ListValue::const_iterator it = list->begin();
298         it != list->end(); ++it) {
299      const DictionaryValue* dict = NULL;
300      if (!(*it)->GetAsDictionary(&dict))
301        continue;
302
303      MediaGalleryPrefInfo gallery_info;
304      if (!PopulateGalleryPrefInfoFromDictionary(*dict, &gallery_info))
305        continue;
306
307      known_galleries_[gallery_info.pref_id] = gallery_info;
308      device_map_[gallery_info.device_id].insert(gallery_info.pref_id);
309    }
310  }
311  if (notify_observers)
312    NotifyChangeObservers(std::string());
313}
314
315void MediaGalleriesPreferences::NotifyChangeObservers(
316    const std::string& extension_id) {
317  FOR_EACH_OBSERVER(GalleryChangeObserver,
318                    gallery_change_observers_,
319                    OnGalleryChanged(this, extension_id));
320}
321
322void MediaGalleriesPreferences::AddGalleryChangeObserver(
323    GalleryChangeObserver* observer) {
324  gallery_change_observers_.AddObserver(observer);
325}
326
327void MediaGalleriesPreferences::RemoveGalleryChangeObserver(
328    GalleryChangeObserver* observer) {
329  gallery_change_observers_.RemoveObserver(observer);
330}
331
332void MediaGalleriesPreferences::OnRemovableStorageAttached(
333    const StorageInfo& info) {
334  if (!StorageInfo::IsMediaDevice(info.device_id()))
335    return;
336
337  if (info.name().empty()) {
338    AddGallery(info.device_id(),
339               base::FilePath(),
340               false /*not user added*/,
341               info.storage_label(),
342               info.vendor_name(),
343               info.model_name(),
344               info.total_size_in_bytes(),
345               base::Time::Now());
346  } else {
347    // TODO(gbillock): get rid of this code path.
348    AddGalleryWithName(info.device_id(), info.name(), base::FilePath(), false);
349  }
350}
351
352bool MediaGalleriesPreferences::LookUpGalleryByPath(
353    const base::FilePath& path,
354    MediaGalleryPrefInfo* gallery_info) const {
355  StorageInfo info;
356  base::FilePath relative_path;
357  if (!MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) {
358    if (gallery_info)
359      *gallery_info = MediaGalleryPrefInfo();
360    return false;
361  }
362
363  relative_path = relative_path.NormalizePathSeparators();
364  MediaGalleryPrefIdSet galleries_on_device =
365      LookUpGalleriesByDeviceId(info.device_id());
366  for (MediaGalleryPrefIdSet::const_iterator it = galleries_on_device.begin();
367       it != galleries_on_device.end();
368       ++it) {
369    const MediaGalleryPrefInfo& gallery = known_galleries_.find(*it)->second;
370    if (gallery.path != relative_path)
371      continue;
372
373    if (gallery_info)
374      *gallery_info = gallery;
375    return true;
376  }
377
378  // This method is called by controller::FilesSelected when the user
379  // adds a new gallery. Control reaches here when the selected gallery is
380  // on a volume we know about, but have no gallery already for. Returns
381  // hypothetical data to the caller about what the prefs will look like
382  // if the gallery is added.
383  // TODO(gbillock): split this out into another function so it doesn't
384  // conflate LookUp.
385  if (gallery_info) {
386    gallery_info->pref_id = kInvalidMediaGalleryPrefId;
387    gallery_info->display_name = info.name();
388    gallery_info->device_id = info.device_id();
389    gallery_info->path = relative_path;
390    gallery_info->type = MediaGalleryPrefInfo::kUserAdded;
391    // TODO(gbillock): Need to add volume metadata here from |info|.
392  }
393  return false;
394}
395
396MediaGalleryPrefIdSet MediaGalleriesPreferences::LookUpGalleriesByDeviceId(
397    const std::string& device_id) const {
398  DeviceIdPrefIdsMap::const_iterator found = device_map_.find(device_id);
399  if (found == device_map_.end())
400    return MediaGalleryPrefIdSet();
401  return found->second;
402}
403
404base::FilePath MediaGalleriesPreferences::LookUpGalleryPathForExtension(
405    MediaGalleryPrefId gallery_id,
406    const extensions::Extension* extension,
407    bool include_unpermitted_galleries) {
408  DCHECK(extension);
409  if (!include_unpermitted_galleries &&
410      !ContainsKey(GalleriesForExtension(*extension), gallery_id))
411    return base::FilePath();
412
413  MediaGalleriesPrefInfoMap::const_iterator it =
414      known_galleries_.find(gallery_id);
415  if (it == known_galleries_.end())
416    return base::FilePath();
417  return MediaStorageUtil::FindDevicePathById(it->second.device_id);
418}
419
420MediaGalleryPrefId MediaGalleriesPreferences::AddGallery(
421    const std::string& device_id,
422    const base::FilePath& relative_path, bool user_added,
423    const string16& volume_label, const string16& vendor_name,
424    const string16& model_name, uint64 total_size_in_bytes,
425    base::Time last_attach_time) {
426  return AddGalleryInternal(device_id, string16(), relative_path, user_added,
427                            volume_label, vendor_name, model_name,
428                            total_size_in_bytes, last_attach_time, true, 1);
429}
430
431MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryWithName(
432    const std::string& device_id, const string16& display_name,
433    const base::FilePath& relative_path, bool user_added) {
434  return AddGalleryInternal(device_id, display_name, relative_path, user_added,
435                            string16(), string16(), string16(),
436                            0, base::Time(), false, 1);
437}
438
439MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryInternal(
440    const std::string& device_id, const string16& display_name,
441    const base::FilePath& relative_path, bool user_added,
442    const string16& volume_label, const string16& vendor_name,
443    const string16& model_name, uint64 total_size_in_bytes,
444    base::Time last_attach_time,
445    bool volume_metadata_valid,
446    int prefs_version) {
447  base::FilePath normalized_relative_path =
448      relative_path.NormalizePathSeparators();
449  MediaGalleryPrefIdSet galleries_on_device =
450    LookUpGalleriesByDeviceId(device_id);
451  for (MediaGalleryPrefIdSet::const_iterator it = galleries_on_device.begin();
452       it != galleries_on_device.end();
453       ++it) {
454    const MediaGalleryPrefInfo& existing = known_galleries_.find(*it)->second;
455    if (existing.path != normalized_relative_path)
456      continue;
457
458    bool update_gallery_type =
459        user_added && (existing.type == MediaGalleryPrefInfo::kBlackListed);
460    // TODO(gbillock): Once we have all updates adding the device metadata,
461    // we'll change this to always update the gallery name if we have device
462    // metadata.
463    // Status quo: In M27 and M28, galleries added manually use version 0,
464    // and galleries added automatically (including default galleries) use
465    // version 1. The name override is used by default galleries as well
466    // as all device attach events.
467    bool update_gallery_name = existing.display_name != display_name;
468    bool update_gallery_metadata = volume_metadata_valid &&
469        ((existing.volume_label != volume_label) ||
470         (existing.vendor_name != vendor_name) ||
471         (existing.model_name != model_name) ||
472         (existing.total_size_in_bytes != total_size_in_bytes) ||
473         (existing.last_attach_time != last_attach_time));
474
475    if (!update_gallery_name && !update_gallery_type &&
476        !update_gallery_metadata)
477      return *it;
478
479    PrefService* prefs = profile_->GetPrefs();
480    scoped_ptr<ListPrefUpdate> update(
481        new ListPrefUpdate(prefs, prefs::kMediaGalleriesRememberedGalleries));
482    ListValue* list = update->Get();
483
484    for (ListValue::const_iterator list_iter = list->begin();
485         list_iter != list->end();
486         ++list_iter) {
487      DictionaryValue* dict;
488      MediaGalleryPrefId iter_id;
489      if ((*list_iter)->GetAsDictionary(&dict) &&
490          GetPrefId(*dict, &iter_id) &&
491          *it == iter_id) {
492        if (update_gallery_type) {
493          dict->SetString(kMediaGalleriesTypeKey,
494                          kMediaGalleriesTypeAutoDetectedValue);
495        }
496        if (update_gallery_name)
497          dict->SetString(kMediaGalleriesDisplayNameKey, display_name);
498        if (update_gallery_metadata) {
499          dict->SetString(kMediaGalleriesVolumeLabelKey, volume_label);
500          dict->SetString(kMediaGalleriesVendorNameKey, vendor_name);
501          dict->SetString(kMediaGalleriesModelNameKey, model_name);
502          dict->SetDouble(kMediaGalleriesSizeKey, total_size_in_bytes);
503          dict->SetDouble(kMediaGalleriesLastAttachTimeKey,
504                          last_attach_time.ToInternalValue());
505        }
506        dict->SetInteger(kMediaGalleriesPrefsVersionKey, prefs_version);
507        break;
508      }
509    }
510
511    // Commits the prefs update.
512    update.reset();
513
514    if (update_gallery_name || update_gallery_metadata || update_gallery_type)
515      InitFromPrefs(true /* notify observers */);
516    return *it;
517  }
518
519  PrefService* prefs = profile_->GetPrefs();
520
521  MediaGalleryPrefInfo gallery_info;
522  gallery_info.pref_id = prefs->GetUint64(prefs::kMediaGalleriesUniqueId);
523  prefs->SetUint64(prefs::kMediaGalleriesUniqueId, gallery_info.pref_id + 1);
524  gallery_info.display_name = display_name;
525  gallery_info.device_id = device_id;
526  gallery_info.path = normalized_relative_path;
527  gallery_info.type = MediaGalleryPrefInfo::kAutoDetected;
528  if (user_added)
529    gallery_info.type = MediaGalleryPrefInfo::kUserAdded;
530  if (volume_metadata_valid) {
531    gallery_info.volume_label = volume_label;
532    gallery_info.vendor_name = vendor_name;
533    gallery_info.model_name = model_name;
534    gallery_info.total_size_in_bytes = total_size_in_bytes;
535    gallery_info.last_attach_time = last_attach_time;
536  }
537  gallery_info.volume_metadata_valid = volume_metadata_valid;
538  gallery_info.prefs_version = prefs_version;
539
540  {
541    ListPrefUpdate update(prefs, prefs::kMediaGalleriesRememberedGalleries);
542    ListValue* list = update.Get();
543    list->Append(CreateGalleryPrefInfoDictionary(gallery_info));
544  }
545  InitFromPrefs(true /* notify observers */);
546
547  return gallery_info.pref_id;
548}
549
550MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryByPath(
551    const base::FilePath& path) {
552  MediaGalleryPrefInfo gallery_info;
553  if (LookUpGalleryByPath(path, &gallery_info) &&
554      gallery_info.type != MediaGalleryPrefInfo::kBlackListed) {
555    return gallery_info.pref_id;
556  }
557  return AddGalleryInternal(gallery_info.device_id,
558                            gallery_info.display_name,
559                            gallery_info.path,
560                            true /*user added*/,
561                            gallery_info.volume_label,
562                            gallery_info.vendor_name,
563                            gallery_info.model_name,
564                            gallery_info.total_size_in_bytes,
565                            gallery_info.last_attach_time,
566                            gallery_info.volume_metadata_valid,
567                            gallery_info.prefs_version);
568}
569
570void MediaGalleriesPreferences::ForgetGalleryById(MediaGalleryPrefId pref_id) {
571  PrefService* prefs = profile_->GetPrefs();
572  scoped_ptr<ListPrefUpdate> update(new ListPrefUpdate(
573      prefs, prefs::kMediaGalleriesRememberedGalleries));
574  ListValue* list = update->Get();
575
576  if (!ContainsKey(known_galleries_, pref_id))
577    return;
578
579  for (ListValue::iterator iter = list->begin(); iter != list->end(); ++iter) {
580    DictionaryValue* dict;
581    MediaGalleryPrefId iter_id;
582    if ((*iter)->GetAsDictionary(&dict) && GetPrefId(*dict, &iter_id) &&
583        pref_id == iter_id) {
584      extensions::MediaGalleriesPrivateAPI::RemoveMediaGalleryPermissions(
585          GetExtensionPrefs(), pref_id);
586      MediaGalleryPrefInfo::Type type;
587      if (GetType(*dict, &type) &&
588          type == MediaGalleryPrefInfo::kAutoDetected) {
589        dict->SetString(kMediaGalleriesTypeKey,
590                        kMediaGalleriesTypeBlackListedValue);
591      } else {
592        list->Erase(iter, NULL);
593      }
594      update.reset(NULL);  // commits the update.
595
596      InitFromPrefs(true /* notify observers */);
597      return;
598    }
599  }
600}
601
602MediaGalleryPrefIdSet MediaGalleriesPreferences::GalleriesForExtension(
603    const extensions::Extension& extension) const {
604  MediaGalleryPrefIdSet result;
605
606  if (HasAutoDetectedGalleryPermission(extension)) {
607    for (MediaGalleriesPrefInfoMap::const_iterator it =
608             known_galleries_.begin(); it != known_galleries_.end(); ++it) {
609      if (it->second.type == MediaGalleryPrefInfo::kAutoDetected)
610        result.insert(it->second.pref_id);
611    }
612  }
613
614  std::vector<MediaGalleryPermission> stored_permissions =
615      extensions::MediaGalleriesPrivateAPI::GetMediaGalleryPermissions(
616          GetExtensionPrefs(), extension.id());
617  for (std::vector<MediaGalleryPermission>::const_iterator it =
618           stored_permissions.begin(); it != stored_permissions.end(); ++it) {
619    if (!it->has_permission) {
620      result.erase(it->pref_id);
621    } else {
622      MediaGalleriesPrefInfoMap::const_iterator gallery =
623          known_galleries_.find(it->pref_id);
624      DCHECK(gallery != known_galleries_.end());
625      if (gallery->second.type != MediaGalleryPrefInfo::kBlackListed) {
626        result.insert(it->pref_id);
627      } else {
628        NOTREACHED() << gallery->second.device_id;
629      }
630    }
631  }
632  return result;
633}
634
635void MediaGalleriesPreferences::SetGalleryPermissionForExtension(
636    const extensions::Extension& extension,
637    MediaGalleryPrefId pref_id,
638    bool has_permission) {
639  // The gallery may not exist anymore if the user opened a second config
640  // surface concurrently and removed it. Drop the permission update if so.
641  MediaGalleriesPrefInfoMap::const_iterator gallery_info =
642      known_galleries_.find(pref_id);
643  if (gallery_info == known_galleries_.end())
644    return;
645
646#if defined(ENABLE_EXTENSIONS)
647  extensions::GalleryWatchStateTracker* state_tracker =
648      extensions::GalleryWatchStateTracker::GetForProfile(profile_);
649#endif
650  bool all_permission = HasAutoDetectedGalleryPermission(extension);
651  if (has_permission && all_permission) {
652    if (gallery_info->second.type == MediaGalleryPrefInfo::kAutoDetected) {
653      extensions::MediaGalleriesPrivateAPI::UnsetMediaGalleryPermission(
654          GetExtensionPrefs(), extension.id(), pref_id);
655      NotifyChangeObservers(extension.id());
656#if defined(ENABLE_EXTENSIONS)
657      if (state_tracker) {
658        state_tracker->OnGalleryPermissionChanged(extension.id(), pref_id,
659                                                  true);
660      }
661#endif
662      return;
663    }
664  }
665
666  if (!has_permission && !all_permission) {
667    extensions::MediaGalleriesPrivateAPI::UnsetMediaGalleryPermission(
668        GetExtensionPrefs(), extension.id(), pref_id);
669  } else {
670    extensions::MediaGalleriesPrivateAPI::SetMediaGalleryPermission(
671        GetExtensionPrefs(), extension.id(), pref_id, has_permission);
672  }
673  NotifyChangeObservers(extension.id());
674#if defined(ENABLE_EXTENSIONS)
675  if (state_tracker) {
676    state_tracker->OnGalleryPermissionChanged(extension.id(), pref_id,
677                                              has_permission);
678  }
679#endif
680}
681
682void MediaGalleriesPreferences::Shutdown() {
683  weak_factory_.InvalidateWeakPtrs();
684  profile_ = NULL;
685}
686
687// static
688bool MediaGalleriesPreferences::APIHasBeenUsed(Profile* profile) {
689  MediaGalleryPrefId current_id =
690      profile->GetPrefs()->GetUint64(prefs::kMediaGalleriesUniqueId);
691  return current_id != kInvalidMediaGalleryPrefId + 1;
692}
693
694// static
695void MediaGalleriesPreferences::RegisterUserPrefs(
696    user_prefs::PrefRegistrySyncable* registry) {
697  registry->RegisterListPref(prefs::kMediaGalleriesRememberedGalleries,
698                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
699  registry->RegisterUint64Pref(
700      prefs::kMediaGalleriesUniqueId,
701      kInvalidMediaGalleryPrefId + 1,
702      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
703}
704
705extensions::ExtensionPrefs*
706MediaGalleriesPreferences::GetExtensionPrefs() const {
707  ExtensionService* extension_service =
708      extensions::ExtensionSystem::Get(profile_)->extension_service();
709  return extension_service->extension_prefs();
710}
711
712}  // namespace chrome
713