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