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