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