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 #include "chrome/browser/media_galleries/media_file_system_registry.h"
      6 
      7 #include <set>
      8 #include <vector>
      9 
     10 #include "base/bind.h"
     11 #include "base/callback.h"
     12 #include "base/files/file_path.h"
     13 #include "base/prefs/pref_service.h"
     14 #include "base/stl_util.h"
     15 #include "chrome/browser/extensions/extension_service.h"
     16 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
     17 #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h"
     18 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
     19 #include "chrome/browser/media_galleries/media_file_system_context.h"
     20 #include "chrome/browser/media_galleries/media_galleries_dialog_controller.h"
     21 #include "chrome/browser/media_galleries/media_galleries_histograms.h"
     22 #include "chrome/browser/media_galleries/media_galleries_preferences_factory.h"
     23 #include "chrome/browser/media_galleries/media_scan_manager.h"
     24 #include "chrome/browser/profiles/profile.h"
     25 #include "chrome/common/chrome_paths.h"
     26 #include "chrome/common/extensions/extension_constants.h"
     27 #include "chrome/common/pref_names.h"
     28 #include "components/storage_monitor/media_storage_util.h"
     29 #include "components/storage_monitor/storage_monitor.h"
     30 #include "content/public/browser/browser_thread.h"
     31 #include "content/public/browser/navigation_details.h"
     32 #include "content/public/browser/render_process_host.h"
     33 #include "content/public/browser/render_process_host_observer.h"
     34 #include "content/public/browser/render_view_host.h"
     35 #include "content/public/browser/web_contents.h"
     36 #include "content/public/browser/web_contents_observer.h"
     37 #include "extensions/browser/extension_system.h"
     38 #include "extensions/common/extension.h"
     39 #include "extensions/common/extension_set.h"
     40 #include "webkit/browser/fileapi/external_mount_points.h"
     41 #include "webkit/common/fileapi/file_system_mount_option.h"
     42 #include "webkit/common/fileapi/file_system_types.h"
     43 
     44 using content::BrowserThread;
     45 using content::NavigationController;
     46 using content::RenderProcessHost;
     47 using content::WebContents;
     48 using fileapi::ExternalMountPoints;
     49 using storage_monitor::MediaStorageUtil;
     50 using storage_monitor::StorageInfo;
     51 using storage_monitor::StorageMonitor;
     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 destroyed a call
     62 // back informs the caller.
     63 class RPHReferenceManager {
     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   virtual ~RPHReferenceManager();
     69 
     70   // Remove all references, but don't call |no_references_callback|.
     71   void Reset() { STLDeleteValues(&observer_map_); }
     72 
     73   // Returns true if there are no references;
     74   bool empty() const { return observer_map_.empty(); }
     75 
     76   // Adds a reference to the passed |rvh|. Calling this multiple times with
     77   // the same |rvh| is a no-op.
     78   void ReferenceFromRVH(const content::RenderViewHost* rvh);
     79 
     80  private:
     81   class RPHWebContentsObserver : public content::WebContentsObserver {
     82    public:
     83     RPHWebContentsObserver(RPHReferenceManager* manager,
     84                            WebContents* web_contents);
     85 
     86    private:
     87     // content::WebContentsObserver
     88     virtual void WebContentsDestroyed() OVERRIDE;
     89     virtual void NavigationEntryCommitted(
     90         const content::LoadCommittedDetails& load_details) OVERRIDE;
     91 
     92     RPHReferenceManager* manager_;
     93   };
     94 
     95   class RPHObserver : public content::RenderProcessHostObserver {
     96    public:
     97     RPHObserver(RPHReferenceManager* manager, RenderProcessHost* host);
     98     virtual ~RPHObserver();
     99 
    100     void AddWebContentsObserver(WebContents* web_contents);
    101     void RemoveWebContentsObserver(WebContents* web_contents);
    102     bool HasWebContentsObservers() {
    103       return observed_web_contentses_.size() > 0;
    104     }
    105 
    106    private:
    107     virtual void RenderProcessHostDestroyed(RenderProcessHost* host) OVERRIDE;
    108 
    109     RPHReferenceManager* manager_;
    110     RenderProcessHost* host_;
    111     typedef std::map<WebContents*, RPHWebContentsObserver*> WCObserverMap;
    112     WCObserverMap observed_web_contentses_;
    113   };
    114   typedef std::map<const RenderProcessHost*, RPHObserver*> RPHObserverMap;
    115 
    116   // Handlers for observed events.
    117   void OnRenderProcessHostDestroyed(RenderProcessHost* rph);
    118   void OnWebContentsDestroyedOrNavigated(WebContents* contents);
    119 
    120   // A callback to call when the last RVH reference goes away.
    121   base::Closure no_references_callback_;
    122 
    123   // The set of render processes and web contents that may have references to
    124   // the file system ids this instance manages.
    125   RPHObserverMap observer_map_;
    126 };
    127 
    128 RPHReferenceManager::RPHReferenceManager(
    129     const base::Closure& no_references_callback)
    130     : no_references_callback_(no_references_callback) {
    131 }
    132 
    133 RPHReferenceManager::~RPHReferenceManager() {
    134   Reset();
    135 }
    136 
    137 void RPHReferenceManager::ReferenceFromRVH(const content::RenderViewHost* rvh) {
    138   WebContents* contents = WebContents::FromRenderViewHost(rvh);
    139   RenderProcessHost* rph = contents->GetRenderProcessHost();
    140   RPHObserver* state = NULL;
    141   if (!ContainsKey(observer_map_, rph)) {
    142     state = new RPHObserver(this, rph);
    143     observer_map_[rph] = state;
    144   } else {
    145     state = observer_map_[rph];
    146   }
    147 
    148   state->AddWebContentsObserver(contents);
    149 }
    150 
    151 RPHReferenceManager::RPHWebContentsObserver::RPHWebContentsObserver(
    152     RPHReferenceManager* manager,
    153     WebContents* web_contents)
    154     : content::WebContentsObserver(web_contents),
    155       manager_(manager) {
    156 }
    157 
    158 void RPHReferenceManager::RPHWebContentsObserver::WebContentsDestroyed() {
    159   manager_->OnWebContentsDestroyedOrNavigated(web_contents());
    160 }
    161 
    162 void RPHReferenceManager::RPHWebContentsObserver::NavigationEntryCommitted(
    163     const content::LoadCommittedDetails& load_details) {
    164   if (load_details.is_in_page)
    165     return;
    166 
    167   manager_->OnWebContentsDestroyedOrNavigated(web_contents());
    168 }
    169 
    170 RPHReferenceManager::RPHObserver::RPHObserver(
    171     RPHReferenceManager* manager, RenderProcessHost* host)
    172     : manager_(manager),
    173       host_(host) {
    174   host->AddObserver(this);
    175 }
    176 
    177 RPHReferenceManager::RPHObserver::~RPHObserver() {
    178   STLDeleteValues(&observed_web_contentses_);
    179   if (host_)
    180     host_->RemoveObserver(this);
    181 }
    182 
    183 void RPHReferenceManager::RPHObserver::AddWebContentsObserver(
    184     WebContents* web_contents) {
    185   if (ContainsKey(observed_web_contentses_, web_contents))
    186     return;
    187 
    188   RPHWebContentsObserver* observer =
    189     new RPHWebContentsObserver(manager_, web_contents);
    190   observed_web_contentses_[web_contents] = observer;
    191 }
    192 
    193 void RPHReferenceManager::RPHObserver::RemoveWebContentsObserver(
    194     WebContents* web_contents) {
    195   WCObserverMap::iterator wco_iter =
    196       observed_web_contentses_.find(web_contents);
    197   DCHECK(wco_iter != observed_web_contentses_.end());
    198   delete wco_iter->second;
    199   observed_web_contentses_.erase(wco_iter);
    200 }
    201 
    202 void RPHReferenceManager::RPHObserver::RenderProcessHostDestroyed(
    203     RenderProcessHost* host) {
    204   host_ = NULL;
    205   manager_->OnRenderProcessHostDestroyed(host);
    206 }
    207 
    208 void RPHReferenceManager::OnRenderProcessHostDestroyed(
    209     RenderProcessHost* rph) {
    210   RPHObserverMap::iterator rph_info = observer_map_.find(rph);
    211   // This could be a potential problem if the RPH is navigated to a page on the
    212   // same renderer (triggering OnWebContentsDestroyedOrNavigated()) and then the
    213   // renderer crashes.
    214   if (rph_info == observer_map_.end()) {
    215     NOTREACHED();
    216     return;
    217   }
    218   delete rph_info->second;
    219   observer_map_.erase(rph_info);
    220   if (observer_map_.empty())
    221     no_references_callback_.Run();
    222 }
    223 
    224 void RPHReferenceManager::OnWebContentsDestroyedOrNavigated(
    225     WebContents* contents) {
    226   RenderProcessHost* rph = contents->GetRenderProcessHost();
    227   RPHObserverMap::iterator rph_info = observer_map_.find(rph);
    228   DCHECK(rph_info != observer_map_.end());
    229 
    230   rph_info->second->RemoveWebContentsObserver(contents);
    231   if (!rph_info->second->HasWebContentsObservers())
    232     OnRenderProcessHostDestroyed(rph);
    233 }
    234 
    235 }  // namespace
    236 
    237 MediaFileSystemInfo::MediaFileSystemInfo(const base::string16& fs_name,
    238                                          const base::FilePath& fs_path,
    239                                          const std::string& filesystem_id,
    240                                          MediaGalleryPrefId pref_id,
    241                                          const std::string& transient_device_id,
    242                                          bool removable,
    243                                          bool media_device)
    244     : name(fs_name),
    245       path(fs_path),
    246       fsid(filesystem_id),
    247       pref_id(pref_id),
    248       transient_device_id(transient_device_id),
    249       removable(removable),
    250       media_device(media_device) {
    251 }
    252 
    253 MediaFileSystemInfo::MediaFileSystemInfo() {}
    254 MediaFileSystemInfo::~MediaFileSystemInfo() {}
    255 
    256 // The main owner of this class is
    257 // |MediaFileSystemRegistry::extension_hosts_map_|, but a callback may
    258 // temporarily hold a reference.
    259 class ExtensionGalleriesHost
    260     : public base::RefCountedThreadSafe<ExtensionGalleriesHost> {
    261  public:
    262   // |no_references_callback| is called when the last RenderViewHost reference
    263   // goes away. RenderViewHost references are added through ReferenceFromRVH().
    264   ExtensionGalleriesHost(MediaFileSystemContext* file_system_context,
    265                          const base::FilePath& profile_path,
    266                          const std::string& extension_id,
    267                          const base::Closure& no_references_callback)
    268       : file_system_context_(file_system_context),
    269         profile_path_(profile_path),
    270         extension_id_(extension_id),
    271         no_references_callback_(no_references_callback),
    272         rph_refs_(base::Bind(&ExtensionGalleriesHost::CleanUp,
    273                              base::Unretained(this))) {
    274   }
    275 
    276   // For each gallery in the list of permitted |galleries|, checks if the
    277   // device is attached and if so looks up or creates a file system name and
    278   // passes the information needed for the renderer to create those file
    279   // system objects to the |callback|.
    280   void GetMediaFileSystems(const MediaGalleryPrefIdSet& galleries,
    281                            const MediaGalleriesPrefInfoMap& galleries_info,
    282                            const MediaFileSystemsCallback& callback) {
    283     // Extract all the device ids so we can make sure they are attached.
    284     MediaStorageUtil::DeviceIdSet* device_ids =
    285         new MediaStorageUtil::DeviceIdSet;
    286     for (std::set<MediaGalleryPrefId>::const_iterator id = galleries.begin();
    287          id != galleries.end();
    288          ++id) {
    289       device_ids->insert(galleries_info.find(*id)->second.device_id);
    290     }
    291     MediaStorageUtil::FilterAttachedDevices(device_ids, base::Bind(
    292         &ExtensionGalleriesHost::GetMediaFileSystemsForAttachedDevices, this,
    293         base::Owned(device_ids), galleries, galleries_info, callback));
    294   }
    295 
    296   // Checks if |gallery| is attached and if so, registers the file system and
    297   // then calls |callback| with the result.
    298   void RegisterMediaFileSystem(
    299       const MediaGalleryPrefInfo& gallery,
    300       const base::Callback<void(base::File::Error result)>& callback) {
    301     // Extract all the device ids so we can make sure they are attached.
    302     MediaStorageUtil::DeviceIdSet* device_ids =
    303         new MediaStorageUtil::DeviceIdSet;
    304     device_ids->insert(gallery.device_id);
    305     MediaStorageUtil::FilterAttachedDevices(device_ids, base::Bind(
    306         &ExtensionGalleriesHost::RegisterAttachedMediaFileSystem, this,
    307         base::Owned(device_ids), gallery, callback));
    308   }
    309 
    310   // Revoke the file system for |id| if this extension has created one for |id|.
    311   void RevokeGalleryByPrefId(MediaGalleryPrefId id) {
    312     PrefIdFsInfoMap::iterator gallery = pref_id_map_.find(id);
    313     if (gallery == pref_id_map_.end())
    314       return;
    315 
    316     file_system_context_->RevokeFileSystem(gallery->second.fsid);
    317     pref_id_map_.erase(gallery);
    318 
    319     if (pref_id_map_.empty()) {
    320       rph_refs_.Reset();
    321       CleanUp();
    322     }
    323   }
    324 
    325   // Indicate that the passed |rvh| will reference the file system ids created
    326   // by this class.
    327   void ReferenceFromRVH(const content::RenderViewHost* rvh) {
    328     rph_refs_.ReferenceFromRVH(rvh);
    329   }
    330 
    331  private:
    332   typedef std::map<MediaGalleryPrefId, MediaFileSystemInfo> PrefIdFsInfoMap;
    333 
    334   // Private destructor and friend declaration for ref counted implementation.
    335   friend class base::RefCountedThreadSafe<ExtensionGalleriesHost>;
    336 
    337   virtual ~ExtensionGalleriesHost() {
    338     DCHECK(rph_refs_.empty());
    339     DCHECK(pref_id_map_.empty());
    340   }
    341 
    342   void GetMediaFileSystemsForAttachedDevices(
    343       const MediaStorageUtil::DeviceIdSet* attached_devices,
    344       const MediaGalleryPrefIdSet& galleries,
    345       const MediaGalleriesPrefInfoMap& galleries_info,
    346       const MediaFileSystemsCallback& callback) {
    347     std::vector<MediaFileSystemInfo> result;
    348 
    349     if (rph_refs_.empty()) {
    350       // We're actually in the middle of shutdown, and Filter...() lagging
    351       // which can invoke this method interleaved in the destruction callback
    352       // sequence and re-populate pref_id_map_.
    353       callback.Run(result);
    354       return;
    355     }
    356 
    357     for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it =
    358              galleries.begin();
    359          pref_id_it != galleries.end();
    360          ++pref_id_it) {
    361       const MediaGalleryPrefId& pref_id = *pref_id_it;
    362       const MediaGalleryPrefInfo& gallery_info =
    363           galleries_info.find(pref_id)->second;
    364       const std::string& device_id = gallery_info.device_id;
    365       if (!ContainsKey(*attached_devices, device_id))
    366         continue;
    367 
    368       PrefIdFsInfoMap::const_iterator existing_info =
    369           pref_id_map_.find(pref_id);
    370       if (existing_info != pref_id_map_.end()) {
    371         result.push_back(existing_info->second);
    372         continue;
    373       }
    374 
    375       base::FilePath path = gallery_info.AbsolutePath();
    376       if (!MediaStorageUtil::CanCreateFileSystem(device_id, path))
    377         continue;
    378 
    379       std::string fs_name = MediaFileSystemBackend::ConstructMountName(
    380           profile_path_, extension_id_, pref_id);
    381       if (!file_system_context_->RegisterFileSystem(device_id, fs_name, path))
    382         continue;
    383 
    384       MediaFileSystemInfo new_entry(
    385           gallery_info.GetGalleryDisplayName(),
    386           file_system_context_->GetRegisteredPath(fs_name),
    387           fs_name,
    388           pref_id,
    389           GetTransientIdForRemovableDeviceId(device_id),
    390           StorageInfo::IsRemovableDevice(device_id),
    391           StorageInfo::IsMediaDevice(device_id));
    392       result.push_back(new_entry);
    393       pref_id_map_[pref_id] = new_entry;
    394     }
    395 
    396     if (result.size() == 0) {
    397       rph_refs_.Reset();
    398       CleanUp();
    399     }
    400 
    401     DCHECK_EQ(pref_id_map_.size(), result.size());
    402     callback.Run(result);
    403   }
    404 
    405   void RegisterAttachedMediaFileSystem(
    406       const MediaStorageUtil::DeviceIdSet* attached_device,
    407       const MediaGalleryPrefInfo& gallery,
    408       const base::Callback<void(base::File::Error result)>& callback) {
    409     base::File::Error result = base::File::FILE_ERROR_NOT_FOUND;
    410 
    411     // If rph_refs is empty then we're actually in the middle of shutdown, and
    412     // Filter...() lagging which can invoke this method interleaved in the
    413     // destruction callback sequence and re-populate pref_id_map_.
    414     if (!attached_device->empty() && !rph_refs_.empty()) {
    415       std::string fs_name = MediaFileSystemBackend::ConstructMountName(
    416           profile_path_, extension_id_, gallery.pref_id);
    417       base::FilePath path = gallery.AbsolutePath();
    418       const std::string& device_id = gallery.device_id;
    419 
    420       if (ContainsKey(pref_id_map_, gallery.pref_id)) {
    421         result = base::File::FILE_OK;
    422       } else if (MediaStorageUtil::CanCreateFileSystem(device_id, path) &&
    423                  file_system_context_->RegisterFileSystem(device_id, fs_name,
    424                                                           path)) {
    425         result = base::File::FILE_OK;
    426         pref_id_map_[gallery.pref_id] = MediaFileSystemInfo(
    427             gallery.GetGalleryDisplayName(),
    428             file_system_context_->GetRegisteredPath(fs_name),
    429             fs_name,
    430             gallery.pref_id,
    431             GetTransientIdForRemovableDeviceId(device_id),
    432             StorageInfo::IsRemovableDevice(device_id),
    433             StorageInfo::IsMediaDevice(device_id));
    434       }
    435     }
    436 
    437     if (pref_id_map_.empty()) {
    438       rph_refs_.Reset();
    439       CleanUp();
    440     }
    441     BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
    442                             base::Bind(callback, result));
    443   }
    444 
    445   std::string GetTransientIdForRemovableDeviceId(const std::string& device_id) {
    446     if (!StorageInfo::IsRemovableDevice(device_id))
    447       return std::string();
    448 
    449     return StorageMonitor::GetInstance()->GetTransientIdForDeviceId(device_id);
    450   }
    451 
    452   void CleanUp() {
    453     DCHECK(rph_refs_.empty());
    454     for (PrefIdFsInfoMap::const_iterator it = pref_id_map_.begin();
    455          it != pref_id_map_.end();
    456          ++it) {
    457       file_system_context_->RevokeFileSystem(it->second.fsid);
    458     }
    459     pref_id_map_.clear();
    460 
    461     no_references_callback_.Run();
    462   }
    463 
    464   // MediaFileSystemRegistry owns |this| and |file_system_context_|, so it's
    465   // safe to store a raw pointer.
    466   MediaFileSystemContext* file_system_context_;
    467 
    468   // Path for the active profile.
    469   const base::FilePath profile_path_;
    470 
    471   // Id of the extension this host belongs to.
    472   const std::string extension_id_;
    473 
    474   // A callback to call when the last RVH reference goes away.
    475   base::Closure no_references_callback_;
    476 
    477   // A map from the gallery preferences id to the file system information.
    478   PrefIdFsInfoMap pref_id_map_;
    479 
    480   // The set of render processes and web contents that may have references to
    481   // the file system ids this instance manages.
    482   RPHReferenceManager rph_refs_;
    483 
    484   DISALLOW_COPY_AND_ASSIGN(ExtensionGalleriesHost);
    485 };
    486 
    487 /******************
    488  * Public methods
    489  ******************/
    490 
    491 void MediaFileSystemRegistry::GetMediaFileSystemsForExtension(
    492     const content::RenderViewHost* rvh,
    493     const extensions::Extension* extension,
    494     const MediaFileSystemsCallback& callback) {
    495   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    496 
    497   Profile* profile =
    498       Profile::FromBrowserContext(rvh->GetProcess()->GetBrowserContext());
    499   MediaGalleriesPreferences* preferences = GetPreferences(profile);
    500   MediaGalleryPrefIdSet galleries =
    501       preferences->GalleriesForExtension(*extension);
    502 
    503   if (galleries.empty()) {
    504     callback.Run(std::vector<MediaFileSystemInfo>());
    505     return;
    506   }
    507 
    508   ExtensionGalleriesHost* extension_host =
    509       GetExtensionGalleryHost(profile, preferences, extension->id());
    510 
    511   // This must come before the GetMediaFileSystems call to make sure the
    512   // RVH of the context is referenced before the filesystems are retrieved.
    513   extension_host->ReferenceFromRVH(rvh);
    514 
    515   extension_host->GetMediaFileSystems(galleries, preferences->known_galleries(),
    516                                       callback);
    517 }
    518 
    519 void MediaFileSystemRegistry::RegisterMediaFileSystemForExtension(
    520     const content::RenderViewHost* rvh,
    521     const extensions::Extension* extension,
    522     MediaGalleryPrefId pref_id,
    523     const base::Callback<void(base::File::Error result)>& callback) {
    524   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    525   DCHECK_NE(kInvalidMediaGalleryPrefId, pref_id);
    526 
    527   Profile* profile =
    528       Profile::FromBrowserContext(rvh->GetProcess()->GetBrowserContext());
    529   MediaGalleriesPreferences* preferences = GetPreferences(profile);
    530   MediaGalleriesPrefInfoMap::const_iterator gallery =
    531       preferences->known_galleries().find(pref_id);
    532   MediaGalleryPrefIdSet permitted_galleries =
    533       preferences->GalleriesForExtension(*extension);
    534 
    535   if (gallery == preferences->known_galleries().end() ||
    536       !ContainsKey(permitted_galleries, pref_id)) {
    537     BrowserThread::PostTask(
    538         BrowserThread::IO, FROM_HERE,
    539         base::Bind(callback, base::File::FILE_ERROR_NOT_FOUND));
    540     return;
    541   }
    542 
    543   ExtensionGalleriesHost* extension_host =
    544       GetExtensionGalleryHost(profile, preferences, extension->id());
    545 
    546   // This must come before the GetMediaFileSystems call to make sure the
    547   // RVH of the context is referenced before the filesystems are retrieved.
    548   extension_host->ReferenceFromRVH(rvh);
    549 
    550   extension_host->RegisterMediaFileSystem(gallery->second, callback);
    551 }
    552 
    553 MediaGalleriesPreferences* MediaFileSystemRegistry::GetPreferences(
    554     Profile* profile) {
    555   // Create an empty ExtensionHostMap for this profile on first initialization.
    556   if (!ContainsKey(extension_hosts_map_, profile)) {
    557     extension_hosts_map_[profile] = ExtensionHostMap();
    558     media_galleries::UsageCount(media_galleries::PROFILES_WITH_USAGE);
    559   }
    560 
    561   return MediaGalleriesPreferencesFactory::GetForProfile(profile);
    562 }
    563 
    564 MediaScanManager* MediaFileSystemRegistry::media_scan_manager() {
    565   if (!media_scan_manager_)
    566     media_scan_manager_.reset(new MediaScanManager);
    567   return media_scan_manager_.get();
    568 }
    569 
    570 void MediaFileSystemRegistry::OnRemovableStorageDetached(
    571     const StorageInfo& info) {
    572   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    573 
    574   // Since revoking a gallery in the ExtensionGalleriesHost may cause it
    575   // to be removed from the map and therefore invalidate any iterator pointing
    576   // to it, this code first copies all the invalid gallery ids and the
    577   // extension hosts in which they may appear (per profile) and revoked it in
    578   // a second step.
    579   std::vector<InvalidatedGalleriesInfo> invalid_galleries_info;
    580 
    581   for (ExtensionGalleriesHostMap::iterator profile_it =
    582            extension_hosts_map_.begin();
    583        profile_it != extension_hosts_map_.end();
    584        ++profile_it) {
    585     MediaGalleriesPreferences* preferences = GetPreferences(profile_it->first);
    586     // If |preferences| is not yet initialized, it won't contain any galleries.
    587     if (!preferences->IsInitialized())
    588       continue;
    589 
    590     InvalidatedGalleriesInfo invalid_galleries_in_profile;
    591     invalid_galleries_in_profile.pref_ids =
    592         preferences->LookUpGalleriesByDeviceId(info.device_id());
    593 
    594     for (ExtensionHostMap::const_iterator extension_host_it =
    595              profile_it->second.begin();
    596          extension_host_it != profile_it->second.end();
    597          ++extension_host_it) {
    598       invalid_galleries_in_profile.extension_hosts.insert(
    599           extension_host_it->second.get());
    600     }
    601 
    602     invalid_galleries_info.push_back(invalid_galleries_in_profile);
    603   }
    604 
    605   for (size_t i = 0; i < invalid_galleries_info.size(); i++) {
    606     for (std::set<ExtensionGalleriesHost*>::const_iterator extension_host_it =
    607              invalid_galleries_info[i].extension_hosts.begin();
    608          extension_host_it != invalid_galleries_info[i].extension_hosts.end();
    609          ++extension_host_it) {
    610       for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it =
    611                invalid_galleries_info[i].pref_ids.begin();
    612            pref_id_it != invalid_galleries_info[i].pref_ids.end();
    613            ++pref_id_it) {
    614         (*extension_host_it)->RevokeGalleryByPrefId(*pref_id_it);
    615       }
    616     }
    617   }
    618 }
    619 
    620 /******************
    621  * Private methods
    622  ******************/
    623 
    624 class MediaFileSystemRegistry::MediaFileSystemContextImpl
    625     : public MediaFileSystemContext {
    626  public:
    627   MediaFileSystemContextImpl() {}
    628   virtual ~MediaFileSystemContextImpl() {}
    629 
    630   virtual bool RegisterFileSystem(const std::string& device_id,
    631                                   const std::string& fs_name,
    632                                   const base::FilePath& path) OVERRIDE {
    633     if (StorageInfo::IsMassStorageDevice(device_id)) {
    634       return RegisterFileSystemForMassStorage(device_id, fs_name, path);
    635     } else {
    636       return RegisterFileSystemForMTPDevice(device_id, fs_name, path);
    637     }
    638   }
    639 
    640   virtual void RevokeFileSystem(const std::string& fs_name) OVERRIDE {
    641     ImportedMediaGalleryRegistry* imported_registry =
    642         ImportedMediaGalleryRegistry::GetInstance();
    643     if (imported_registry->RevokeImportedFilesystemOnUIThread(fs_name))
    644       return;
    645 
    646     ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(fs_name);
    647 
    648     BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
    649         &MTPDeviceMapService::RevokeMTPFileSystem,
    650         base::Unretained(MTPDeviceMapService::GetInstance()),
    651         fs_name));
    652   }
    653 
    654   virtual base::FilePath GetRegisteredPath(
    655       const std::string& fs_name) const OVERRIDE {
    656     base::FilePath result;
    657     if (!ExternalMountPoints::GetSystemInstance()->GetRegisteredPath(fs_name,
    658                                                                      &result)) {
    659       return base::FilePath();
    660     }
    661     return result;
    662   }
    663 
    664  private:
    665   // Registers and returns the file system id for the mass storage device
    666   // specified by |device_id| and |path|.
    667   bool RegisterFileSystemForMassStorage(const std::string& device_id,
    668                                         const std::string& fs_name,
    669                                         const base::FilePath& path) {
    670     DCHECK_CURRENTLY_ON(BrowserThread::UI);
    671     DCHECK(StorageInfo::IsMassStorageDevice(device_id));
    672 
    673     // Sanity checks for |path|.
    674     CHECK(path.IsAbsolute());
    675     CHECK(!path.ReferencesParent());
    676 
    677     // TODO(gbillock): refactor ImportedMediaGalleryRegistry to delegate this
    678     // call tree, probably by having it figure out by device id what
    679     // registration is needed, or having per-device-type handlers at the
    680     // next higher level.
    681     bool result = false;
    682     if (StorageInfo::IsITunesDevice(device_id)) {
    683       ImportedMediaGalleryRegistry* registry =
    684           ImportedMediaGalleryRegistry::GetInstance();
    685       result = registry->RegisterITunesFilesystemOnUIThread(fs_name, path);
    686     } else if (StorageInfo::IsPicasaDevice(device_id)) {
    687       ImportedMediaGalleryRegistry* registry =
    688           ImportedMediaGalleryRegistry::GetInstance();
    689       result = registry->RegisterPicasaFilesystemOnUIThread(fs_name, path);
    690     } else if (StorageInfo::IsIPhotoDevice(device_id)) {
    691       ImportedMediaGalleryRegistry* registry =
    692           ImportedMediaGalleryRegistry::GetInstance();
    693       result = registry->RegisterIPhotoFilesystemOnUIThread(fs_name, path);
    694     } else {
    695       result = ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
    696           fs_name, fileapi::kFileSystemTypeNativeMedia,
    697           fileapi::FileSystemMountOption(), path);
    698     }
    699     return result;
    700   }
    701 
    702   bool RegisterFileSystemForMTPDevice(const std::string& device_id,
    703                                       const std::string fs_name,
    704                                       const base::FilePath& path) {
    705     DCHECK_CURRENTLY_ON(BrowserThread::UI);
    706     DCHECK(!StorageInfo::IsMassStorageDevice(device_id));
    707 
    708     // Sanity checks for |path|.
    709     CHECK(MediaStorageUtil::CanCreateFileSystem(device_id, path));
    710     bool result = ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
    711         fs_name, fileapi::kFileSystemTypeDeviceMedia,
    712         fileapi::FileSystemMountOption(), path);
    713     CHECK(result);
    714     BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
    715         &MTPDeviceMapService::RegisterMTPFileSystem,
    716         base::Unretained(MTPDeviceMapService::GetInstance()),
    717         path.value(), fs_name));
    718     return result;
    719   }
    720 
    721   DISALLOW_COPY_AND_ASSIGN(MediaFileSystemContextImpl);
    722 };
    723 
    724 // Constructor in 'private' section because depends on private class definition.
    725 MediaFileSystemRegistry::MediaFileSystemRegistry()
    726     : file_system_context_(new MediaFileSystemContextImpl) {
    727   StorageMonitor::GetInstance()->AddObserver(this);
    728 }
    729 
    730 MediaFileSystemRegistry::~MediaFileSystemRegistry() {
    731   // TODO(gbillock): This is needed because the unit test uses the
    732   // g_browser_process registry. We should create one in the unit test,
    733   // and then can remove this.
    734   if (StorageMonitor::GetInstance())
    735     StorageMonitor::GetInstance()->RemoveObserver(this);
    736 }
    737 
    738 void MediaFileSystemRegistry::OnPermissionRemoved(
    739     MediaGalleriesPreferences* prefs,
    740     const std::string& extension_id,
    741     MediaGalleryPrefId pref_id) {
    742   Profile* profile = prefs->profile();
    743   ExtensionGalleriesHostMap::const_iterator host_map_it =
    744       extension_hosts_map_.find(profile);
    745   DCHECK(host_map_it != extension_hosts_map_.end());
    746   const ExtensionHostMap& extension_host_map = host_map_it->second;
    747   ExtensionHostMap::const_iterator gallery_host_it =
    748       extension_host_map.find(extension_id);
    749   if (gallery_host_it == extension_host_map.end())
    750     return;
    751   gallery_host_it->second->RevokeGalleryByPrefId(pref_id);
    752 }
    753 
    754 void MediaFileSystemRegistry::OnGalleryRemoved(
    755     MediaGalleriesPreferences* prefs,
    756     MediaGalleryPrefId pref_id) {
    757   Profile* profile = prefs->profile();
    758   // Get the Extensions, MediaGalleriesPreferences and ExtensionHostMap for
    759   // |profile|.
    760   const ExtensionService* extension_service =
    761       extensions::ExtensionSystem::Get(profile)->extension_service();
    762   const extensions::ExtensionSet* extensions_set =
    763       extension_service->extensions();
    764   ExtensionGalleriesHostMap::const_iterator host_map_it =
    765       extension_hosts_map_.find(profile);
    766   DCHECK(host_map_it != extension_hosts_map_.end());
    767   const ExtensionHostMap& extension_host_map = host_map_it->second;
    768 
    769   // Go through ExtensionHosts, and remove indicated gallery, if any.
    770   // RevokeGalleryByPrefId() may end up deleting from |extension_host_map| and
    771   // even delete |extension_host_map| altogether. So do this in two loops to
    772   // avoid using an invalidated iterator or deleted map.
    773   std::vector<const extensions::Extension*> extensions;
    774   for (ExtensionHostMap::const_iterator it = extension_host_map.begin();
    775        it != extension_host_map.end();
    776        ++it) {
    777     extensions.push_back(extensions_set->GetByID(it->first));
    778   }
    779   for (size_t i = 0; i < extensions.size(); ++i) {
    780     if (!ContainsKey(extension_hosts_map_, profile))
    781       break;
    782     ExtensionHostMap::const_iterator gallery_host_it =
    783         extension_host_map.find(extensions[i]->id());
    784     if (gallery_host_it == extension_host_map.end())
    785       continue;
    786     gallery_host_it->second->RevokeGalleryByPrefId(pref_id);
    787   }
    788 }
    789 
    790 ExtensionGalleriesHost* MediaFileSystemRegistry::GetExtensionGalleryHost(
    791     Profile* profile,
    792     MediaGalleriesPreferences* preferences,
    793     const std::string& extension_id) {
    794   ExtensionGalleriesHostMap::iterator extension_hosts =
    795       extension_hosts_map_.find(profile);
    796   // GetPreferences(), which had to be called because preferences is an
    797   // argument, ensures that profile is in the map.
    798   DCHECK(extension_hosts != extension_hosts_map_.end());
    799   if (extension_hosts->second.empty())
    800     preferences->AddGalleryChangeObserver(this);
    801 
    802   ExtensionGalleriesHost* result = extension_hosts->second[extension_id].get();
    803   if (!result) {
    804     result = new ExtensionGalleriesHost(
    805         file_system_context_.get(),
    806         profile->GetPath(),
    807         extension_id,
    808         base::Bind(&MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty,
    809                    base::Unretained(this),
    810                    profile,
    811                    extension_id));
    812     extension_hosts_map_[profile][extension_id] = result;
    813   }
    814   return result;
    815 }
    816 
    817 void MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty(
    818     Profile* profile, const std::string& extension_id) {
    819   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    820 
    821   ExtensionGalleriesHostMap::iterator extension_hosts =
    822       extension_hosts_map_.find(profile);
    823   DCHECK(extension_hosts != extension_hosts_map_.end());
    824   ExtensionHostMap::size_type erase_count =
    825       extension_hosts->second.erase(extension_id);
    826   DCHECK_EQ(1U, erase_count);
    827   if (extension_hosts->second.empty()) {
    828     // When a profile has no ExtensionGalleriesHosts left, remove the
    829     // matching gallery-change-watcher since it is no longer needed. Leave the
    830     // |extension_hosts| entry alone, since it indicates the profile has been
    831     // previously used.
    832     MediaGalleriesPreferences* preferences = GetPreferences(profile);
    833     preferences->RemoveGalleryChangeObserver(this);
    834   }
    835 }
    836