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// MediaFileSystemRegistry implementation.
6
7#include "chrome/browser/media_galleries/media_file_system_registry.h"
8
9#include <set>
10#include <vector>
11
12#include "base/bind.h"
13#include "base/callback.h"
14#include "base/files/file_path.h"
15#include "base/prefs/pref_service.h"
16#include "base/stl_util.h"
17#include "chrome/browser/chrome_notification_types.h"
18#include "chrome/browser/extensions/extension_service.h"
19#include "chrome/browser/extensions/extension_system.h"
20#include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
21#include "chrome/browser/media_galleries/media_file_system_context.h"
22#include "chrome/browser/media_galleries/media_galleries_dialog_controller.h"
23#include "chrome/browser/media_galleries/media_galleries_preferences_factory.h"
24#include "chrome/browser/media_galleries/scoped_mtp_device_map_entry.h"
25#include "chrome/browser/profiles/profile.h"
26#include "chrome/browser/storage_monitor/media_storage_util.h"
27#include "chrome/browser/storage_monitor/storage_monitor.h"
28#include "chrome/common/chrome_paths.h"
29#include "chrome/common/extensions/extension.h"
30#include "chrome/common/extensions/extension_constants.h"
31#include "chrome/common/extensions/extension_set.h"
32#include "chrome/common/pref_names.h"
33#include "content/public/browser/browser_thread.h"
34#include "content/public/browser/notification_details.h"
35#include "content/public/browser/notification_observer.h"
36#include "content/public/browser/notification_registrar.h"
37#include "content/public/browser/notification_source.h"
38#include "content/public/browser/notification_types.h"
39#include "content/public/browser/render_process_host.h"
40#include "content/public/browser/render_view_host.h"
41#include "content/public/browser/web_contents.h"
42#include "webkit/browser/fileapi/isolated_context.h"
43#include "webkit/common/fileapi/file_system_types.h"
44
45using content::BrowserThread;
46using content::NavigationController;
47using content::RenderProcessHost;
48using content::WebContents;
49using fileapi::IsolatedContext;
50
51namespace chrome {
52
53namespace {
54
55struct InvalidatedGalleriesInfo {
56  std::set<ExtensionGalleriesHost*> extension_hosts;
57  std::set<MediaGalleryPrefId> pref_ids;
58};
59
60}  // namespace
61
62MediaFileSystemInfo::MediaFileSystemInfo(const string16& fs_name,
63                                         const base::FilePath& fs_path,
64                                         const std::string& filesystem_id,
65                                         MediaGalleryPrefId pref_id,
66                                         const std::string& transient_device_id,
67                                         bool removable,
68                                         bool media_device)
69    : name(fs_name),
70      path(fs_path),
71      fsid(filesystem_id),
72      pref_id(pref_id),
73      transient_device_id(transient_device_id),
74      removable(removable),
75      media_device(media_device) {
76}
77
78MediaFileSystemInfo::MediaFileSystemInfo() {}
79MediaFileSystemInfo::~MediaFileSystemInfo() {}
80
81// Tracks the liveness of multiple RenderProcessHosts that the caller is
82// interested in. Once all of the RPHs have closed or been terminated a call
83// back informs the caller.
84class RPHReferenceManager : public content::NotificationObserver {
85 public:
86  // |no_references_callback| is called when the last RenderViewHost reference
87  // goes away. RenderViewHost references are added through ReferenceFromRVH().
88  explicit RPHReferenceManager(const base::Closure& no_references_callback)
89      : no_references_callback_(no_references_callback) {
90  }
91
92  virtual ~RPHReferenceManager() {
93    Reset();
94  }
95
96  // Remove all references, but don't call |no_references_callback|.
97  void Reset() {
98    STLDeleteValues(&refs_);
99  }
100
101  // Returns true if there are no references;
102  bool empty() const {
103    return refs_.empty();
104  }
105
106  // Adds a reference to the passed |rvh|. Calling this multiple times with
107  // the same |rvh| is a no-op.
108  void ReferenceFromRVH(const content::RenderViewHost* rvh) {
109    WebContents* contents = WebContents::FromRenderViewHost(rvh);
110    RenderProcessHost* rph = contents->GetRenderProcessHost();
111    RPHReferenceState* state = NULL;
112    if (!ContainsKey(refs_, rph)) {
113      state = new RPHReferenceState;
114      refs_[rph] = state;
115      state->registrar.Add(
116          this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
117          content::Source<RenderProcessHost>(rph));
118    } else {
119      state = refs_[rph];
120    }
121
122    if (state->web_contents_set.insert(contents).second) {
123      state->registrar.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
124          content::Source<WebContents>(contents));
125      state->registrar.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
126          content::Source<NavigationController>(&contents->GetController()));
127    }
128  }
129
130 private:
131  struct RPHReferenceState {
132    content::NotificationRegistrar registrar;
133    std::set<const WebContents*> web_contents_set;
134  };
135  typedef std::map<const RenderProcessHost*, RPHReferenceState*> RPHRefCount;
136
137  // NotificationObserver implementation.
138  virtual void Observe(int type,
139                       const content::NotificationSource& source,
140                       const content::NotificationDetails& details) OVERRIDE {
141    switch (type) {
142      case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
143        OnRendererProcessTerminated(
144            content::Source<RenderProcessHost>(source).ptr());
145        break;
146      }
147      case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: {
148        OnWebContentsDestroyedOrNavigated(
149            content::Source<WebContents>(source).ptr());
150        break;
151      }
152      case content::NOTIFICATION_NAV_ENTRY_COMMITTED: {
153        NavigationController* controller =
154            content::Source<NavigationController>(source).ptr();
155        WebContents* contents = controller->GetWebContents();
156        OnWebContentsDestroyedOrNavigated(contents);
157        break;
158      }
159      default: {
160        NOTREACHED();
161        break;
162      }
163    }
164  }
165
166  void OnRendererProcessTerminated(const RenderProcessHost* rph) {
167    RPHRefCount::iterator rph_info = refs_.find(rph);
168    DCHECK(rph_info != refs_.end());
169    delete rph_info->second;
170    refs_.erase(rph_info);
171    if (refs_.empty())
172      no_references_callback_.Run();
173  }
174
175  void OnWebContentsDestroyedOrNavigated(const WebContents* contents) {
176    RenderProcessHost* rph = contents->GetRenderProcessHost();
177    RPHRefCount::iterator rph_info = refs_.find(rph);
178    DCHECK(rph_info != refs_.end());
179
180    rph_info->second->registrar.Remove(
181        this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
182        content::Source<WebContents>(contents));
183    rph_info->second->registrar.Remove(
184        this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
185        content::Source<NavigationController>(&contents->GetController()));
186
187    rph_info->second->web_contents_set.erase(contents);
188    if (rph_info->second->web_contents_set.empty())
189      OnRendererProcessTerminated(rph);
190  }
191
192  // A callback to call when the last RVH reference goes away.
193  base::Closure no_references_callback_;
194
195  // The set of render processes and web contents that may have references to
196  // the file system ids this instance manages.
197  RPHRefCount refs_;
198};
199
200// The main owner of this class is
201// |MediaFileSystemRegistry::extension_hosts_map_|, but a callback may
202// temporarily hold a reference.
203class ExtensionGalleriesHost
204    : public base::RefCountedThreadSafe<ExtensionGalleriesHost> {
205 public:
206  // |no_references_callback| is called when the last RenderViewHost reference
207  // goes away. RenderViewHost references are added through ReferenceFromRVH().
208  ExtensionGalleriesHost(MediaFileSystemContext* file_system_context,
209                         const base::Closure& no_references_callback)
210      : file_system_context_(file_system_context),
211        no_references_callback_(no_references_callback),
212        rph_refs_(base::Bind(&ExtensionGalleriesHost::CleanUp,
213                             base::Unretained(this))) {
214  }
215
216  // For each gallery in the list of permitted |galleries|, checks if the
217  // device is attached and if so looks up or creates a file system id and
218  // passes the information needed for the renderer to create those file
219  // system objects to the |callback|.
220  void GetMediaFileSystems(const MediaGalleryPrefIdSet& galleries,
221                           const MediaGalleriesPrefInfoMap& galleries_info,
222                           const MediaFileSystemsCallback& callback) {
223    // Extract all the device ids so we can make sure they are attached.
224    MediaStorageUtil::DeviceIdSet* device_ids =
225        new MediaStorageUtil::DeviceIdSet;
226    for (std::set<MediaGalleryPrefId>::const_iterator id = galleries.begin();
227         id != galleries.end();
228         ++id) {
229      device_ids->insert(galleries_info.find(*id)->second.device_id);
230    }
231    MediaStorageUtil::FilterAttachedDevices(device_ids, base::Bind(
232        &ExtensionGalleriesHost::GetMediaFileSystemsForAttachedDevices, this,
233        base::Owned(device_ids), galleries, galleries_info, callback));
234  }
235
236  void RevokeOldGalleries(const MediaGalleryPrefIdSet& new_galleries) {
237    if (new_galleries.size() == pref_id_map_.size())
238      return;
239
240    MediaGalleryPrefIdSet old_galleries;
241    for (PrefIdFsInfoMap::const_iterator it = pref_id_map_.begin();
242         it != pref_id_map_.end();
243         ++it) {
244      old_galleries.insert(it->first);
245    }
246    MediaGalleryPrefIdSet invalid_galleries;
247    std::set_difference(old_galleries.begin(), old_galleries.end(),
248                        new_galleries.begin(), new_galleries.end(),
249                        std::inserter(invalid_galleries,
250                                      invalid_galleries.begin()));
251    for (MediaGalleryPrefIdSet::const_iterator it = invalid_galleries.begin();
252         it != invalid_galleries.end();
253         ++it) {
254      RevokeGalleryByPrefId(*it);
255    }
256  }
257
258  // Revoke the file system for |id| if this extension has created one for |id|.
259  void RevokeGalleryByPrefId(MediaGalleryPrefId id) {
260    PrefIdFsInfoMap::iterator gallery = pref_id_map_.find(id);
261    if (gallery == pref_id_map_.end())
262      return;
263
264    file_system_context_->RevokeFileSystem(gallery->second.fsid);
265    pref_id_map_.erase(gallery);
266
267    MediaDeviceEntryReferencesMap::iterator mtp_device_host =
268        media_device_map_references_.find(id);
269    if (mtp_device_host != media_device_map_references_.end())
270      media_device_map_references_.erase(mtp_device_host);
271
272    if (pref_id_map_.empty()) {
273      rph_refs_.Reset();
274      CleanUp();
275    }
276  }
277
278  // Indicate that the passed |rvh| will reference the file system ids created
279  // by this class.
280  void ReferenceFromRVH(const content::RenderViewHost* rvh) {
281    rph_refs_.ReferenceFromRVH(rvh);
282  }
283
284 private:
285  typedef std::map<MediaGalleryPrefId, MediaFileSystemInfo> PrefIdFsInfoMap;
286  typedef std::map<MediaGalleryPrefId, scoped_refptr<ScopedMTPDeviceMapEntry> >
287      MediaDeviceEntryReferencesMap;
288
289  // Private destructor and friend declaration for ref counted implementation.
290  friend class base::RefCountedThreadSafe<ExtensionGalleriesHost>;
291
292  virtual ~ExtensionGalleriesHost() {
293    DCHECK(rph_refs_.empty());
294    DCHECK(pref_id_map_.empty());
295
296    DCHECK(media_device_map_references_.empty());
297  }
298
299  void GetMediaFileSystemsForAttachedDevices(
300      const MediaStorageUtil::DeviceIdSet* attached_devices,
301      const MediaGalleryPrefIdSet& galleries,
302      const MediaGalleriesPrefInfoMap& galleries_info,
303      const MediaFileSystemsCallback& callback) {
304    std::vector<MediaFileSystemInfo> result;
305    MediaGalleryPrefIdSet new_galleries;
306    for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it =
307             galleries.begin();
308         pref_id_it != galleries.end();
309         ++pref_id_it) {
310      const MediaGalleryPrefId& pref_id = *pref_id_it;
311      const MediaGalleryPrefInfo& gallery_info =
312          galleries_info.find(pref_id)->second;
313      const std::string& device_id = gallery_info.device_id;
314      if (!ContainsKey(*attached_devices, device_id))
315        continue;
316
317      PrefIdFsInfoMap::const_iterator existing_info =
318          pref_id_map_.find(pref_id);
319      if (existing_info != pref_id_map_.end()) {
320        result.push_back(existing_info->second);
321        new_galleries.insert(pref_id);
322        continue;
323      }
324
325      base::FilePath path = gallery_info.AbsolutePath();
326      if (!MediaStorageUtil::CanCreateFileSystem(device_id, path))
327        continue;
328
329      std::string fsid;
330      if (StorageInfo::IsMassStorageDevice(device_id)) {
331        fsid = file_system_context_->RegisterFileSystemForMassStorage(
332            device_id, path);
333      } else {
334        scoped_refptr<ScopedMTPDeviceMapEntry> mtp_device_host;
335        fsid = file_system_context_->RegisterFileSystemForMTPDevice(
336            device_id, path, &mtp_device_host);
337        DCHECK(mtp_device_host.get());
338        media_device_map_references_[pref_id] = mtp_device_host;
339      }
340      if (fsid.empty())
341        continue;
342
343      MediaFileSystemInfo new_entry(
344          gallery_info.GetGalleryDisplayName(),
345          path,
346          fsid,
347          pref_id,
348          GetTransientIdForRemovableDeviceId(device_id),
349          StorageInfo::IsRemovableDevice(device_id),
350          StorageInfo::IsMediaDevice(device_id));
351      result.push_back(new_entry);
352      new_galleries.insert(pref_id);
353      pref_id_map_[pref_id] = new_entry;
354    }
355
356    if (result.size() == 0) {
357      rph_refs_.Reset();
358      CleanUp();
359    } else {
360      RevokeOldGalleries(new_galleries);
361    }
362
363    callback.Run(result);
364  }
365
366  std::string GetTransientIdForRemovableDeviceId(const std::string& device_id) {
367    if (!StorageInfo::IsRemovableDevice(device_id))
368      return std::string();
369
370    return StorageMonitor::GetInstance()->GetTransientIdForDeviceId(device_id);
371  }
372
373  void CleanUp() {
374    DCHECK(rph_refs_.empty());
375    for (PrefIdFsInfoMap::const_iterator it = pref_id_map_.begin();
376         it != pref_id_map_.end();
377         ++it) {
378      file_system_context_->RevokeFileSystem(it->second.fsid);
379    }
380    pref_id_map_.clear();
381
382    media_device_map_references_.clear();
383
384    no_references_callback_.Run();
385  }
386
387  // MediaFileSystemRegistry owns |this| and |file_system_context_|, so it's
388  // safe to store a raw pointer.
389  MediaFileSystemContext* file_system_context_;
390
391  // A callback to call when the last RVH reference goes away.
392  base::Closure no_references_callback_;
393
394  // A map from the gallery preferences id to the file system information.
395  PrefIdFsInfoMap pref_id_map_;
396
397  // A map from the gallery preferences id to the corresponding media device
398  // host object.
399  MediaDeviceEntryReferencesMap media_device_map_references_;
400
401  // The set of render processes and web contents that may have references to
402  // the file system ids this instance manages.
403  RPHReferenceManager rph_refs_;
404
405  DISALLOW_COPY_AND_ASSIGN(ExtensionGalleriesHost);
406};
407
408/******************
409 * Public methods
410 ******************/
411
412void MediaFileSystemRegistry::GetMediaFileSystemsForExtension(
413    const content::RenderViewHost* rvh,
414    const extensions::Extension* extension,
415    const MediaFileSystemsCallback& callback) {
416  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
417
418  Profile* profile =
419      Profile::FromBrowserContext(rvh->GetProcess()->GetBrowserContext());
420  MediaGalleriesPreferences* preferences = GetPreferences(profile);
421  MediaGalleryPrefIdSet galleries =
422      preferences->GalleriesForExtension(*extension);
423
424  if (galleries.empty()) {
425    callback.Run(std::vector<MediaFileSystemInfo>());
426    return;
427  }
428
429  if (!ContainsKey(pref_change_registrar_map_, profile)) {
430    PrefChangeRegistrar* pref_registrar = new PrefChangeRegistrar;
431    pref_registrar->Init(profile->GetPrefs());
432    pref_registrar->Add(
433        prefs::kMediaGalleriesRememberedGalleries,
434        base::Bind(&MediaFileSystemRegistry::OnRememberedGalleriesChanged,
435                   base::Unretained(this),
436                   pref_registrar->prefs()));
437    pref_change_registrar_map_[profile] = pref_registrar;
438  }
439
440  ExtensionGalleriesHost* extension_host =
441      extension_hosts_map_[profile][extension->id()].get();
442  if (!extension_host) {
443    extension_host = new ExtensionGalleriesHost(
444        file_system_context_.get(),
445        base::Bind(&MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty,
446                   base::Unretained(this), profile, extension->id()));
447    extension_hosts_map_[profile][extension->id()] = extension_host;
448  }
449  extension_host->ReferenceFromRVH(rvh);
450
451  extension_host->GetMediaFileSystems(galleries, preferences->known_galleries(),
452                                      callback);
453}
454
455MediaGalleriesPreferences* MediaFileSystemRegistry::GetPreferences(
456    Profile* profile) {
457  MediaGalleriesPreferences* preferences =
458      MediaGalleriesPreferencesFactory::GetForProfile(profile);
459  if (ContainsKey(extension_hosts_map_, profile))
460    return preferences;
461
462  // Create an empty entry so the initialization code below only gets called
463  // once per profile.
464  extension_hosts_map_[profile] = ExtensionHostMap();
465
466  // TODO(gbillock): Move this stanza to MediaGalleriesPreferences init code.
467  StorageMonitor* monitor = StorageMonitor::GetInstance();
468  DCHECK(monitor->IsInitialized());
469  std::vector<StorageInfo> existing_devices =
470      monitor->GetAllAvailableStorages();
471  for (size_t i = 0; i < existing_devices.size(); i++) {
472    if (!(StorageInfo::IsMediaDevice(existing_devices[i].device_id()) &&
473          StorageInfo::IsRemovableDevice(existing_devices[i].device_id())))
474      continue;
475    preferences->AddGallery(existing_devices[i].device_id(),
476                            base::FilePath(),
477                            false,
478                            existing_devices[i].storage_label(),
479                            existing_devices[i].vendor_name(),
480                            existing_devices[i].model_name(),
481                            existing_devices[i].total_size_in_bytes(),
482                            base::Time::Now());
483  }
484  return preferences;
485}
486
487void MediaFileSystemRegistry::OnRemovableStorageDetached(
488    const StorageInfo& info) {
489  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
490
491  // Since revoking a gallery in the ExtensionGalleriesHost may cause it
492  // to be removed from the map and therefore invalidate any iterator pointing
493  // to it, this code first copies all the invalid gallery ids and the
494  // extension hosts in which they may appear (per profile) and revoked it in
495  // a second step.
496  std::vector<InvalidatedGalleriesInfo> invalid_galleries_info;
497
498  for (ExtensionGalleriesHostMap::iterator profile_it =
499           extension_hosts_map_.begin();
500       profile_it != extension_hosts_map_.end();
501       ++profile_it) {
502    MediaGalleriesPreferences* preferences = GetPreferences(profile_it->first);
503    InvalidatedGalleriesInfo invalid_galleries_in_profile;
504    invalid_galleries_in_profile.pref_ids =
505        preferences->LookUpGalleriesByDeviceId(info.device_id());
506
507    for (ExtensionHostMap::const_iterator extension_host_it =
508             profile_it->second.begin();
509         extension_host_it != profile_it->second.end();
510         ++extension_host_it) {
511      invalid_galleries_in_profile.extension_hosts.insert(
512          extension_host_it->second.get());
513    }
514
515    invalid_galleries_info.push_back(invalid_galleries_in_profile);
516  }
517
518  for (size_t i = 0; i < invalid_galleries_info.size(); i++) {
519    for (std::set<ExtensionGalleriesHost*>::const_iterator extension_host_it =
520             invalid_galleries_info[i].extension_hosts.begin();
521         extension_host_it != invalid_galleries_info[i].extension_hosts.end();
522         ++extension_host_it) {
523      for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it =
524               invalid_galleries_info[i].pref_ids.begin();
525           pref_id_it != invalid_galleries_info[i].pref_ids.end();
526           ++pref_id_it) {
527        (*extension_host_it)->RevokeGalleryByPrefId(*pref_id_it);
528      }
529    }
530  }
531}
532
533/******************
534 * Private methods
535 ******************/
536
537class MediaFileSystemRegistry::MediaFileSystemContextImpl
538    : public MediaFileSystemContext {
539 public:
540  explicit MediaFileSystemContextImpl(MediaFileSystemRegistry* registry)
541      : registry_(registry) {
542    DCHECK(registry_);  // Suppresses unused warning on Android.
543  }
544  virtual ~MediaFileSystemContextImpl() {}
545
546  // Registers and returns the file system id for the mass storage device
547  // specified by |device_id| and |path|.
548  virtual std::string RegisterFileSystemForMassStorage(
549      const std::string& device_id, const base::FilePath& path) OVERRIDE {
550    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
551    DCHECK(StorageInfo::IsMassStorageDevice(device_id));
552
553    // Sanity checks for |path|.
554    CHECK(path.IsAbsolute());
555    CHECK(!path.ReferencesParent());
556
557    std::string fsid;
558    if (StorageInfo::IsITunesDevice(device_id)) {
559      ImportedMediaGalleryRegistry* imported_registry =
560          ImportedMediaGalleryRegistry::GetInstance();
561      fsid = imported_registry->RegisterITunesFilesystemOnUIThread(path);
562    } else if (StorageInfo::IsPicasaDevice(device_id)) {
563      ImportedMediaGalleryRegistry* imported_registry =
564          ImportedMediaGalleryRegistry::GetInstance();
565      fsid = imported_registry->RegisterPicasaFilesystemOnUIThread(
566          path);
567    } else {
568      std::string fs_name(extension_misc::kMediaFileSystemPathPart);
569      fsid = IsolatedContext::GetInstance()->RegisterFileSystemForPath(
570          fileapi::kFileSystemTypeNativeMedia, path, &fs_name);
571    }
572    return fsid;
573  }
574
575  virtual std::string RegisterFileSystemForMTPDevice(
576      const std::string& device_id, const base::FilePath& path,
577      scoped_refptr<ScopedMTPDeviceMapEntry>* entry) OVERRIDE {
578    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
579    DCHECK(!StorageInfo::IsMassStorageDevice(device_id));
580
581    // Sanity checks for |path|.
582    CHECK(MediaStorageUtil::CanCreateFileSystem(device_id, path));
583    std::string fs_name(extension_misc::kMediaFileSystemPathPart);
584    const std::string fsid =
585        IsolatedContext::GetInstance()->RegisterFileSystemForPath(
586            fileapi::kFileSystemTypeDeviceMedia, path, &fs_name);
587    CHECK(!fsid.empty());
588    DCHECK(entry);
589    *entry = registry_->GetOrCreateScopedMTPDeviceMapEntry(path.value());
590    return fsid;
591  }
592
593  virtual void RevokeFileSystem(const std::string& fsid) OVERRIDE {
594    ImportedMediaGalleryRegistry* imported_registry =
595        ImportedMediaGalleryRegistry::GetInstance();
596    if (imported_registry->RevokeImportedFilesystemOnUIThread(fsid))
597      return;
598
599    IsolatedContext::GetInstance()->RevokeFileSystem(fsid);
600  }
601
602 private:
603  MediaFileSystemRegistry* registry_;
604
605  DISALLOW_COPY_AND_ASSIGN(MediaFileSystemContextImpl);
606};
607
608MediaFileSystemRegistry::MediaFileSystemRegistry()
609    : file_system_context_(new MediaFileSystemContextImpl(this)) {
610  StorageMonitor::GetInstance()->AddObserver(this);
611}
612
613MediaFileSystemRegistry::~MediaFileSystemRegistry() {
614  // TODO(gbillock): This is needed because the unit test uses the
615  // g_browser_process registry. We should create one in the unit test,
616  // and then can remove this.
617  if (StorageMonitor::GetInstance())
618    StorageMonitor::GetInstance()->RemoveObserver(this);
619  DCHECK(mtp_device_delegate_map_.empty());
620}
621
622void MediaFileSystemRegistry::OnRememberedGalleriesChanged(
623    PrefService* prefs) {
624  // Find the Profile that contains the source PrefService.
625  PrefChangeRegistrarMap::iterator pref_change_it =
626      pref_change_registrar_map_.begin();
627  for (; pref_change_it != pref_change_registrar_map_.end(); ++pref_change_it) {
628    if (pref_change_it->first->GetPrefs() == prefs)
629      break;
630  }
631  DCHECK(pref_change_it != pref_change_registrar_map_.end());
632  Profile* profile = pref_change_it->first;
633
634  // Get the Extensions, MediaGalleriesPreferences and ExtensionHostMap for
635  // |profile|.
636  const ExtensionService* extension_service =
637      extensions::ExtensionSystem::Get(profile)->extension_service();
638  const ExtensionSet* extensions_set = extension_service->extensions();
639  const MediaGalleriesPreferences* preferences = GetPreferences(profile);
640  ExtensionGalleriesHostMap::const_iterator host_map_it =
641      extension_hosts_map_.find(profile);
642  DCHECK(host_map_it != extension_hosts_map_.end());
643  const ExtensionHostMap& extension_host_map = host_map_it->second;
644
645  // Go through ExtensionsHosts, get the updated galleries list and use it to
646  // revoke the old galleries.
647  // RevokeOldGalleries() may end up deleting from |extension_host_map| and
648  // even delete |extension_host_map| altogether. So do this in two loops to
649  // avoid using an invalidated iterator or deleted map.
650  std::vector<const extensions::Extension*> extensions;
651  for (ExtensionHostMap::const_iterator it = extension_host_map.begin();
652       it != extension_host_map.end();
653       ++it) {
654    extensions.push_back(extensions_set->GetByID(it->first));
655  }
656  for (size_t i = 0; i < extensions.size(); ++i) {
657    if (!ContainsKey(extension_hosts_map_, profile))
658      break;
659    ExtensionHostMap::const_iterator gallery_host_it =
660        extension_host_map.find(extensions[i]->id());
661    if (gallery_host_it == extension_host_map.end())
662      continue;
663    gallery_host_it->second->RevokeOldGalleries(
664        preferences->GalleriesForExtension(*extensions[i]));
665  }
666}
667
668scoped_refptr<ScopedMTPDeviceMapEntry>
669MediaFileSystemRegistry::GetOrCreateScopedMTPDeviceMapEntry(
670    const base::FilePath::StringType& device_location) {
671  MTPDeviceDelegateMap::iterator delegate_it =
672      mtp_device_delegate_map_.find(device_location);
673  if (delegate_it != mtp_device_delegate_map_.end())
674    return delegate_it->second;
675  scoped_refptr<ScopedMTPDeviceMapEntry> mtp_device_host =
676      new ScopedMTPDeviceMapEntry(
677          device_location,
678          base::Bind(&MediaFileSystemRegistry::RemoveScopedMTPDeviceMapEntry,
679                     base::Unretained(this),
680                     device_location));
681  mtp_device_host->Init();
682  mtp_device_delegate_map_[device_location] = mtp_device_host.get();
683  return mtp_device_host;
684}
685
686void MediaFileSystemRegistry::RemoveScopedMTPDeviceMapEntry(
687    const base::FilePath::StringType& device_location) {
688  MTPDeviceDelegateMap::iterator delegate_it =
689      mtp_device_delegate_map_.find(device_location);
690  DCHECK(delegate_it != mtp_device_delegate_map_.end());
691  mtp_device_delegate_map_.erase(delegate_it);
692}
693
694void MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty(
695    Profile* profile, const std::string& extension_id) {
696  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
697
698  ExtensionGalleriesHostMap::iterator extension_hosts =
699      extension_hosts_map_.find(profile);
700  DCHECK(extension_hosts != extension_hosts_map_.end());
701  ExtensionHostMap::size_type erase_count =
702      extension_hosts->second.erase(extension_id);
703  DCHECK_EQ(1U, erase_count);
704  if (extension_hosts->second.empty()) {
705    // When a profile has no ExtensionGalleriesHosts left, remove the
706    // matching PrefChangeRegistrar since it is no longer needed. Leave the
707    // |extension_hosts| entry alone, since it indicates the profile has been
708    // previously used.
709    PrefChangeRegistrarMap::iterator pref_it =
710        pref_change_registrar_map_.find(profile);
711    DCHECK(pref_it != pref_change_registrar_map_.end());
712    delete pref_it->second;
713    pref_change_registrar_map_.erase(pref_it);
714  }
715}
716
717}  // namespace chrome
718