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