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