Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / media_galleries / media_file_system_registry.cc
blob85bd0954be337cf6d093c70d963f4696e2878e99
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.
5 #include "chrome/browser/media_galleries/media_file_system_registry.h"
7 #include <set>
8 #include <vector>
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/media_galleries/fileapi/media_file_system_backend.h"
16 #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h"
17 #include "chrome/browser/media_galleries/gallery_watch_manager.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 "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_registry.h"
37 #include "extensions/common/extension.h"
38 #include "extensions/common/extension_set.h"
39 #include "storage/browser/fileapi/external_mount_points.h"
40 #include "storage/common/fileapi/file_system_mount_option.h"
41 #include "storage/common/fileapi/file_system_types.h"
43 using content::BrowserThread;
44 using content::NavigationController;
45 using content::RenderProcessHost;
46 using content::WebContents;
47 using storage::ExternalMountPoints;
48 using storage_monitor::MediaStorageUtil;
49 using storage_monitor::StorageInfo;
50 using storage_monitor::StorageMonitor;
52 namespace {
54 struct InvalidatedGalleriesInfo {
55 std::set<ExtensionGalleriesHost*> extension_hosts;
56 std::set<MediaGalleryPrefId> pref_ids;
59 // Tracks the liveness of multiple RenderProcessHosts that the caller is
60 // interested in. Once all of the RPHs have closed or been destroyed a call
61 // back informs the caller.
62 class RPHReferenceManager {
63 public:
64 // |no_references_callback| is called when the last RenderViewHost reference
65 // goes away. RenderViewHost references are added through ReferenceFromRVH().
66 explicit RPHReferenceManager(const base::Closure& no_references_callback);
67 virtual ~RPHReferenceManager();
69 // Remove all references, but don't call |no_references_callback|.
70 void Reset() { STLDeleteValues(&observer_map_); }
72 // Returns true if there are no references;
73 bool empty() const { return observer_map_.empty(); }
75 // Adds a reference to the passed |rvh|. Calling this multiple times with
76 // the same |rvh| is a no-op.
77 void ReferenceFromRVH(const content::RenderViewHost* rvh);
79 private:
80 class RPHWebContentsObserver : public content::WebContentsObserver {
81 public:
82 RPHWebContentsObserver(RPHReferenceManager* manager,
83 WebContents* web_contents);
85 private:
86 // content::WebContentsObserver
87 void WebContentsDestroyed() override;
88 void NavigationEntryCommitted(
89 const content::LoadCommittedDetails& load_details) override;
91 RPHReferenceManager* manager_;
94 class RPHObserver : public content::RenderProcessHostObserver {
95 public:
96 RPHObserver(RPHReferenceManager* manager, RenderProcessHost* host);
97 ~RPHObserver() override;
99 void AddWebContentsObserver(WebContents* web_contents);
100 void RemoveWebContentsObserver(WebContents* web_contents);
101 bool HasWebContentsObservers() {
102 return observed_web_contentses_.size() > 0;
105 private:
106 void RenderProcessHostDestroyed(RenderProcessHost* host) override;
108 RPHReferenceManager* manager_;
109 RenderProcessHost* host_;
110 typedef std::map<WebContents*, RPHWebContentsObserver*> WCObserverMap;
111 WCObserverMap observed_web_contentses_;
113 typedef std::map<const RenderProcessHost*, RPHObserver*> RPHObserverMap;
115 // Handlers for observed events.
116 void OnRenderProcessHostDestroyed(RenderProcessHost* rph);
117 void OnWebContentsDestroyedOrNavigated(WebContents* contents);
119 // A callback to call when the last RVH reference goes away.
120 base::Closure no_references_callback_;
122 // The set of render processes and web contents that may have references to
123 // the file system ids this instance manages.
124 RPHObserverMap observer_map_;
127 RPHReferenceManager::RPHReferenceManager(
128 const base::Closure& no_references_callback)
129 : no_references_callback_(no_references_callback) {
132 RPHReferenceManager::~RPHReferenceManager() {
133 Reset();
136 void RPHReferenceManager::ReferenceFromRVH(const content::RenderViewHost* rvh) {
137 WebContents* contents = WebContents::FromRenderViewHost(rvh);
138 RenderProcessHost* rph = contents->GetRenderProcessHost();
139 RPHObserver* state = NULL;
140 if (!ContainsKey(observer_map_, rph)) {
141 state = new RPHObserver(this, rph);
142 observer_map_[rph] = state;
143 } else {
144 state = observer_map_[rph];
147 state->AddWebContentsObserver(contents);
150 RPHReferenceManager::RPHWebContentsObserver::RPHWebContentsObserver(
151 RPHReferenceManager* manager,
152 WebContents* web_contents)
153 : content::WebContentsObserver(web_contents),
154 manager_(manager) {
157 void RPHReferenceManager::RPHWebContentsObserver::WebContentsDestroyed() {
158 manager_->OnWebContentsDestroyedOrNavigated(web_contents());
161 void RPHReferenceManager::RPHWebContentsObserver::NavigationEntryCommitted(
162 const content::LoadCommittedDetails& load_details) {
163 if (load_details.is_in_page)
164 return;
166 manager_->OnWebContentsDestroyedOrNavigated(web_contents());
169 RPHReferenceManager::RPHObserver::RPHObserver(
170 RPHReferenceManager* manager, RenderProcessHost* host)
171 : manager_(manager),
172 host_(host) {
173 host->AddObserver(this);
176 RPHReferenceManager::RPHObserver::~RPHObserver() {
177 STLDeleteValues(&observed_web_contentses_);
178 if (host_)
179 host_->RemoveObserver(this);
182 void RPHReferenceManager::RPHObserver::AddWebContentsObserver(
183 WebContents* web_contents) {
184 if (ContainsKey(observed_web_contentses_, web_contents))
185 return;
187 RPHWebContentsObserver* observer =
188 new RPHWebContentsObserver(manager_, web_contents);
189 observed_web_contentses_[web_contents] = observer;
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);
201 void RPHReferenceManager::RPHObserver::RenderProcessHostDestroyed(
202 RenderProcessHost* host) {
203 host_ = NULL;
204 manager_->OnRenderProcessHostDestroyed(host);
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;
217 delete rph_info->second;
218 observer_map_.erase(rph_info);
219 if (observer_map_.empty())
220 no_references_callback_.Run();
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());
229 rph_info->second->RemoveWebContentsObserver(contents);
230 if (!rph_info->second->HasWebContentsObservers())
231 OnRenderProcessHostDestroyed(rph);
234 } // namespace
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) {
252 MediaFileSystemInfo::MediaFileSystemInfo() {}
253 MediaFileSystemInfo::~MediaFileSystemInfo() {}
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::FilePath& profile_path,
265 const std::string& extension_id,
266 const base::Closure& no_references_callback)
267 : file_system_context_(file_system_context),
268 profile_path_(profile_path),
269 extension_id_(extension_id),
270 no_references_callback_(no_references_callback),
271 rph_refs_(base::Bind(&ExtensionGalleriesHost::CleanUp,
272 base::Unretained(this))) {
275 // For each gallery in the list of permitted |galleries|, checks if the
276 // device is attached and if so looks up or creates a file system name and
277 // passes the information needed for the renderer to create those file
278 // system objects to the |callback|.
279 void GetMediaFileSystems(const MediaGalleryPrefIdSet& galleries,
280 const MediaGalleriesPrefInfoMap& galleries_info,
281 const MediaFileSystemsCallback& callback) {
282 DCHECK_CURRENTLY_ON(BrowserThread::UI);
284 // Extract all the device ids so we can make sure they are attached.
285 MediaStorageUtil::DeviceIdSet* device_ids =
286 new MediaStorageUtil::DeviceIdSet;
287 for (std::set<MediaGalleryPrefId>::const_iterator id = galleries.begin();
288 id != galleries.end();
289 ++id) {
290 device_ids->insert(galleries_info.find(*id)->second.device_id);
292 MediaStorageUtil::FilterAttachedDevices(device_ids, base::Bind(
293 &ExtensionGalleriesHost::GetMediaFileSystemsForAttachedDevices, this,
294 base::Owned(device_ids), galleries, galleries_info, callback));
297 // Checks if |gallery| is attached and if so, registers the file system and
298 // then calls |callback| with the result.
299 void RegisterMediaFileSystem(
300 const MediaGalleryPrefInfo& gallery,
301 const base::Callback<void(base::File::Error result)>& callback) {
302 // Extract all the device ids so we can make sure they are attached.
303 MediaStorageUtil::DeviceIdSet* device_ids =
304 new MediaStorageUtil::DeviceIdSet;
305 device_ids->insert(gallery.device_id);
306 MediaStorageUtil::FilterAttachedDevices(device_ids, base::Bind(
307 &ExtensionGalleriesHost::RegisterAttachedMediaFileSystem, this,
308 base::Owned(device_ids), gallery, callback));
311 // Revoke the file system for |id| if this extension has created one for |id|.
312 void RevokeGalleryByPrefId(MediaGalleryPrefId id) {
313 PrefIdFsInfoMap::iterator gallery = pref_id_map_.find(id);
314 if (gallery == pref_id_map_.end())
315 return;
317 file_system_context_->RevokeFileSystem(gallery->second.fsid);
318 pref_id_map_.erase(gallery);
320 if (pref_id_map_.empty()) {
321 rph_refs_.Reset();
322 CleanUp();
326 // Indicate that the passed |rvh| will reference the file system ids created
327 // by this class.
328 void ReferenceFromRVH(const content::RenderViewHost* rvh) {
329 rph_refs_.ReferenceFromRVH(rvh);
332 private:
333 typedef std::map<MediaGalleryPrefId, MediaFileSystemInfo> PrefIdFsInfoMap;
335 // Private destructor and friend declaration for ref counted implementation.
336 friend class base::RefCountedThreadSafe<ExtensionGalleriesHost>;
338 virtual ~ExtensionGalleriesHost() {
339 DCHECK(rph_refs_.empty());
340 DCHECK(pref_id_map_.empty());
343 void GetMediaFileSystemsForAttachedDevices(
344 const MediaStorageUtil::DeviceIdSet* attached_devices,
345 const MediaGalleryPrefIdSet& galleries,
346 const MediaGalleriesPrefInfoMap& galleries_info,
347 const MediaFileSystemsCallback& callback) {
348 std::vector<MediaFileSystemInfo> result;
350 if (rph_refs_.empty()) {
351 // We're actually in the middle of shutdown, and Filter...() lagging
352 // which can invoke this method interleaved in the destruction callback
353 // sequence and re-populate pref_id_map_.
354 callback.Run(result);
355 return;
358 for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it =
359 galleries.begin();
360 pref_id_it != galleries.end();
361 ++pref_id_it) {
362 const MediaGalleryPrefId& pref_id = *pref_id_it;
363 const MediaGalleryPrefInfo& gallery_info =
364 galleries_info.find(pref_id)->second;
365 const std::string& device_id = gallery_info.device_id;
366 if (!ContainsKey(*attached_devices, device_id))
367 continue;
369 PrefIdFsInfoMap::const_iterator existing_info =
370 pref_id_map_.find(pref_id);
371 if (existing_info != pref_id_map_.end()) {
372 result.push_back(existing_info->second);
373 continue;
376 base::FilePath path = gallery_info.AbsolutePath();
377 if (!MediaStorageUtil::CanCreateFileSystem(device_id, path))
378 continue;
380 std::string fs_name = MediaFileSystemBackend::ConstructMountName(
381 profile_path_, extension_id_, pref_id);
382 if (!file_system_context_->RegisterFileSystem(device_id, fs_name, path))
383 continue;
385 MediaFileSystemInfo new_entry(
386 gallery_info.GetGalleryDisplayName(),
387 file_system_context_->GetRegisteredPath(fs_name),
388 fs_name,
389 pref_id,
390 GetTransientIdForRemovableDeviceId(device_id),
391 StorageInfo::IsRemovableDevice(device_id),
392 StorageInfo::IsMediaDevice(device_id));
393 result.push_back(new_entry);
394 pref_id_map_[pref_id] = new_entry;
397 if (result.size() == 0) {
398 rph_refs_.Reset();
399 CleanUp();
402 DCHECK_EQ(pref_id_map_.size(), result.size());
403 callback.Run(result);
406 void RegisterAttachedMediaFileSystem(
407 const MediaStorageUtil::DeviceIdSet* attached_device,
408 const MediaGalleryPrefInfo& gallery,
409 const base::Callback<void(base::File::Error result)>& callback) {
410 base::File::Error result = base::File::FILE_ERROR_NOT_FOUND;
412 // If rph_refs is empty then we're actually in the middle of shutdown, and
413 // Filter...() lagging which can invoke this method interleaved in the
414 // destruction callback sequence and re-populate pref_id_map_.
415 if (!attached_device->empty() && !rph_refs_.empty()) {
416 std::string fs_name = MediaFileSystemBackend::ConstructMountName(
417 profile_path_, extension_id_, gallery.pref_id);
418 base::FilePath path = gallery.AbsolutePath();
419 const std::string& device_id = gallery.device_id;
421 if (ContainsKey(pref_id_map_, gallery.pref_id)) {
422 result = base::File::FILE_OK;
423 } else if (MediaStorageUtil::CanCreateFileSystem(device_id, path) &&
424 file_system_context_->RegisterFileSystem(device_id, fs_name,
425 path)) {
426 result = base::File::FILE_OK;
427 pref_id_map_[gallery.pref_id] = MediaFileSystemInfo(
428 gallery.GetGalleryDisplayName(),
429 file_system_context_->GetRegisteredPath(fs_name),
430 fs_name,
431 gallery.pref_id,
432 GetTransientIdForRemovableDeviceId(device_id),
433 StorageInfo::IsRemovableDevice(device_id),
434 StorageInfo::IsMediaDevice(device_id));
438 if (pref_id_map_.empty()) {
439 rph_refs_.Reset();
440 CleanUp();
442 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
443 base::Bind(callback, result));
446 std::string GetTransientIdForRemovableDeviceId(const std::string& device_id) {
447 if (!StorageInfo::IsRemovableDevice(device_id))
448 return std::string();
450 return StorageMonitor::GetInstance()->GetTransientIdForDeviceId(device_id);
453 void CleanUp() {
454 DCHECK(rph_refs_.empty());
455 for (PrefIdFsInfoMap::const_iterator it = pref_id_map_.begin();
456 it != pref_id_map_.end();
457 ++it) {
458 file_system_context_->RevokeFileSystem(it->second.fsid);
460 pref_id_map_.clear();
462 no_references_callback_.Run();
465 // MediaFileSystemRegistry owns |this| and |file_system_context_|, so it's
466 // safe to store a raw pointer.
467 MediaFileSystemContext* file_system_context_;
469 // Path for the active profile.
470 const base::FilePath profile_path_;
472 // Id of the extension this host belongs to.
473 const std::string extension_id_;
475 // A callback to call when the last RVH reference goes away.
476 base::Closure no_references_callback_;
478 // A map from the gallery preferences id to the file system information.
479 PrefIdFsInfoMap pref_id_map_;
481 // The set of render processes and web contents that may have references to
482 // the file system ids this instance manages.
483 RPHReferenceManager rph_refs_;
485 DISALLOW_COPY_AND_ASSIGN(ExtensionGalleriesHost);
488 /******************
489 * Public methods
490 ******************/
492 void MediaFileSystemRegistry::GetMediaFileSystemsForExtension(
493 const content::RenderViewHost* rvh,
494 const extensions::Extension* extension,
495 const MediaFileSystemsCallback& callback) {
496 // TODO(tommycli): Change to DCHECK after fixing http://crbug.com/374330.
497 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
499 Profile* profile =
500 Profile::FromBrowserContext(rvh->GetProcess()->GetBrowserContext());
501 MediaGalleriesPreferences* preferences = GetPreferences(profile);
502 MediaGalleryPrefIdSet galleries =
503 preferences->GalleriesForExtension(*extension);
505 if (galleries.empty()) {
506 callback.Run(std::vector<MediaFileSystemInfo>());
507 return;
510 ExtensionGalleriesHost* extension_host =
511 GetExtensionGalleryHost(profile, preferences, extension->id());
513 // This must come before the GetMediaFileSystems call to make sure the
514 // RVH of the context is referenced before the filesystems are retrieved.
515 extension_host->ReferenceFromRVH(rvh);
517 extension_host->GetMediaFileSystems(galleries, preferences->known_galleries(),
518 callback);
521 void MediaFileSystemRegistry::RegisterMediaFileSystemForExtension(
522 const content::RenderViewHost* rvh,
523 const extensions::Extension* extension,
524 MediaGalleryPrefId pref_id,
525 const base::Callback<void(base::File::Error result)>& callback) {
526 DCHECK_CURRENTLY_ON(BrowserThread::UI);
527 DCHECK_NE(kInvalidMediaGalleryPrefId, pref_id);
529 Profile* profile =
530 Profile::FromBrowserContext(rvh->GetProcess()->GetBrowserContext());
531 MediaGalleriesPreferences* preferences = GetPreferences(profile);
532 MediaGalleriesPrefInfoMap::const_iterator gallery =
533 preferences->known_galleries().find(pref_id);
534 MediaGalleryPrefIdSet permitted_galleries =
535 preferences->GalleriesForExtension(*extension);
537 if (gallery == preferences->known_galleries().end() ||
538 !ContainsKey(permitted_galleries, pref_id)) {
539 BrowserThread::PostTask(
540 BrowserThread::IO, FROM_HERE,
541 base::Bind(callback, base::File::FILE_ERROR_NOT_FOUND));
542 return;
545 ExtensionGalleriesHost* extension_host =
546 GetExtensionGalleryHost(profile, preferences, extension->id());
548 // This must come before the GetMediaFileSystems call to make sure the
549 // RVH of the context is referenced before the filesystems are retrieved.
550 extension_host->ReferenceFromRVH(rvh);
552 extension_host->RegisterMediaFileSystem(gallery->second, callback);
555 MediaGalleriesPreferences* MediaFileSystemRegistry::GetPreferences(
556 Profile* profile) {
557 // Create an empty ExtensionHostMap for this profile on first initialization.
558 if (!ContainsKey(extension_hosts_map_, profile)) {
559 extension_hosts_map_[profile] = ExtensionHostMap();
560 media_galleries::UsageCount(media_galleries::PROFILES_WITH_USAGE);
563 return MediaGalleriesPreferencesFactory::GetForProfile(profile);
566 MediaScanManager* MediaFileSystemRegistry::media_scan_manager() {
567 if (!media_scan_manager_)
568 media_scan_manager_.reset(new MediaScanManager);
569 return media_scan_manager_.get();
572 GalleryWatchManager* MediaFileSystemRegistry::gallery_watch_manager() {
573 if (!gallery_watch_manager_)
574 gallery_watch_manager_.reset(new GalleryWatchManager);
575 return gallery_watch_manager_.get();
578 void MediaFileSystemRegistry::OnRemovableStorageDetached(
579 const StorageInfo& info) {
580 DCHECK_CURRENTLY_ON(BrowserThread::UI);
582 // Since revoking a gallery in the ExtensionGalleriesHost may cause it
583 // to be removed from the map and therefore invalidate any iterator pointing
584 // to it, this code first copies all the invalid gallery ids and the
585 // extension hosts in which they may appear (per profile) and revoked it in
586 // a second step.
587 std::vector<InvalidatedGalleriesInfo> invalid_galleries_info;
589 for (ExtensionGalleriesHostMap::iterator profile_it =
590 extension_hosts_map_.begin();
591 profile_it != extension_hosts_map_.end();
592 ++profile_it) {
593 MediaGalleriesPreferences* preferences = GetPreferences(profile_it->first);
594 // If |preferences| is not yet initialized, it won't contain any galleries.
595 if (!preferences->IsInitialized())
596 continue;
598 InvalidatedGalleriesInfo invalid_galleries_in_profile;
599 invalid_galleries_in_profile.pref_ids =
600 preferences->LookUpGalleriesByDeviceId(info.device_id());
602 for (ExtensionHostMap::const_iterator extension_host_it =
603 profile_it->second.begin();
604 extension_host_it != profile_it->second.end();
605 ++extension_host_it) {
606 invalid_galleries_in_profile.extension_hosts.insert(
607 extension_host_it->second.get());
610 invalid_galleries_info.push_back(invalid_galleries_in_profile);
613 for (size_t i = 0; i < invalid_galleries_info.size(); i++) {
614 for (std::set<ExtensionGalleriesHost*>::const_iterator extension_host_it =
615 invalid_galleries_info[i].extension_hosts.begin();
616 extension_host_it != invalid_galleries_info[i].extension_hosts.end();
617 ++extension_host_it) {
618 for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it =
619 invalid_galleries_info[i].pref_ids.begin();
620 pref_id_it != invalid_galleries_info[i].pref_ids.end();
621 ++pref_id_it) {
622 (*extension_host_it)->RevokeGalleryByPrefId(*pref_id_it);
628 /******************
629 * Private methods
630 ******************/
632 class MediaFileSystemRegistry::MediaFileSystemContextImpl
633 : public MediaFileSystemContext {
634 public:
635 MediaFileSystemContextImpl() {}
636 ~MediaFileSystemContextImpl() override {}
638 bool RegisterFileSystem(const std::string& device_id,
639 const std::string& fs_name,
640 const base::FilePath& path) override {
641 if (StorageInfo::IsMassStorageDevice(device_id)) {
642 return RegisterFileSystemForMassStorage(device_id, fs_name, path);
643 } else {
644 return RegisterFileSystemForMTPDevice(device_id, fs_name, path);
648 void RevokeFileSystem(const std::string& fs_name) override {
649 ImportedMediaGalleryRegistry* imported_registry =
650 ImportedMediaGalleryRegistry::GetInstance();
651 if (imported_registry->RevokeImportedFilesystemOnUIThread(fs_name))
652 return;
654 ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(fs_name);
656 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
657 &MTPDeviceMapService::RevokeMTPFileSystem,
658 base::Unretained(MTPDeviceMapService::GetInstance()),
659 fs_name));
662 base::FilePath GetRegisteredPath(const std::string& fs_name) const override {
663 base::FilePath result;
664 if (!ExternalMountPoints::GetSystemInstance()->GetRegisteredPath(fs_name,
665 &result)) {
666 return base::FilePath();
668 return result;
671 private:
672 // Registers and returns the file system id for the mass storage device
673 // specified by |device_id| and |path|.
674 bool RegisterFileSystemForMassStorage(const std::string& device_id,
675 const std::string& fs_name,
676 const base::FilePath& path) {
677 DCHECK_CURRENTLY_ON(BrowserThread::UI);
678 DCHECK(StorageInfo::IsMassStorageDevice(device_id));
680 // Sanity checks for |path|.
681 CHECK(path.IsAbsolute());
682 CHECK(!path.ReferencesParent());
684 // TODO(gbillock): refactor ImportedMediaGalleryRegistry to delegate this
685 // call tree, probably by having it figure out by device id what
686 // registration is needed, or having per-device-type handlers at the
687 // next higher level.
688 bool result = false;
689 if (StorageInfo::IsITunesDevice(device_id)) {
690 ImportedMediaGalleryRegistry* registry =
691 ImportedMediaGalleryRegistry::GetInstance();
692 result = registry->RegisterITunesFilesystemOnUIThread(fs_name, path);
693 } else if (StorageInfo::IsPicasaDevice(device_id)) {
694 ImportedMediaGalleryRegistry* registry =
695 ImportedMediaGalleryRegistry::GetInstance();
696 result = registry->RegisterPicasaFilesystemOnUIThread(fs_name, path);
697 } else if (StorageInfo::IsIPhotoDevice(device_id)) {
698 ImportedMediaGalleryRegistry* registry =
699 ImportedMediaGalleryRegistry::GetInstance();
700 result = registry->RegisterIPhotoFilesystemOnUIThread(fs_name, path);
701 } else {
702 result = ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
703 fs_name,
704 storage::kFileSystemTypeNativeMedia,
705 storage::FileSystemMountOption(),
706 path);
708 return result;
711 bool RegisterFileSystemForMTPDevice(const std::string& device_id,
712 const std::string fs_name,
713 const base::FilePath& path) {
714 DCHECK_CURRENTLY_ON(BrowserThread::UI);
715 DCHECK(!StorageInfo::IsMassStorageDevice(device_id));
717 // Sanity checks for |path|.
718 CHECK(MediaStorageUtil::CanCreateFileSystem(device_id, path));
719 bool result = ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
720 fs_name,
721 storage::kFileSystemTypeDeviceMedia,
722 storage::FileSystemMountOption(),
723 path);
724 CHECK(result);
725 BrowserThread::PostTask(
726 BrowserThread::IO, FROM_HERE,
727 base::Bind(&MTPDeviceMapService::RegisterMTPFileSystem,
728 base::Unretained(MTPDeviceMapService::GetInstance()),
729 path.value(), fs_name, true /* read only */));
730 return result;
733 DISALLOW_COPY_AND_ASSIGN(MediaFileSystemContextImpl);
736 // Constructor in 'private' section because depends on private class definition.
737 MediaFileSystemRegistry::MediaFileSystemRegistry()
738 : file_system_context_(new MediaFileSystemContextImpl) {
739 StorageMonitor::GetInstance()->AddObserver(this);
742 MediaFileSystemRegistry::~MediaFileSystemRegistry() {
743 // TODO(gbillock): This is needed because the unit test uses the
744 // g_browser_process registry. We should create one in the unit test,
745 // and then can remove this.
746 if (StorageMonitor::GetInstance())
747 StorageMonitor::GetInstance()->RemoveObserver(this);
750 void MediaFileSystemRegistry::OnPermissionRemoved(
751 MediaGalleriesPreferences* prefs,
752 const std::string& extension_id,
753 MediaGalleryPrefId pref_id) {
754 Profile* profile = prefs->profile();
755 ExtensionGalleriesHostMap::const_iterator host_map_it =
756 extension_hosts_map_.find(profile);
757 DCHECK(host_map_it != extension_hosts_map_.end());
758 const ExtensionHostMap& extension_host_map = host_map_it->second;
759 ExtensionHostMap::const_iterator gallery_host_it =
760 extension_host_map.find(extension_id);
761 if (gallery_host_it == extension_host_map.end())
762 return;
763 gallery_host_it->second->RevokeGalleryByPrefId(pref_id);
766 void MediaFileSystemRegistry::OnGalleryRemoved(
767 MediaGalleriesPreferences* prefs,
768 MediaGalleryPrefId pref_id) {
769 Profile* profile = prefs->profile();
770 // Get the Extensions, MediaGalleriesPreferences and ExtensionHostMap for
771 // |profile|.
772 const extensions::ExtensionRegistry* extension_registry =
773 extensions::ExtensionRegistry::Get(profile);
774 ExtensionGalleriesHostMap::const_iterator host_map_it =
775 extension_hosts_map_.find(profile);
776 DCHECK(host_map_it != extension_hosts_map_.end());
777 const ExtensionHostMap& extension_host_map = host_map_it->second;
779 // Go through ExtensionHosts, and remove indicated gallery, if any.
780 // RevokeGalleryByPrefId() may end up deleting from |extension_host_map| and
781 // even delete |extension_host_map| altogether. So do this in two loops to
782 // avoid using an invalidated iterator or deleted map.
783 std::vector<const extensions::Extension*> extensions;
784 for (ExtensionHostMap::const_iterator it = extension_host_map.begin();
785 it != extension_host_map.end();
786 ++it) {
787 extensions.push_back(
788 extension_registry->enabled_extensions().GetByID(it->first));
790 for (size_t i = 0; i < extensions.size(); ++i) {
791 if (!ContainsKey(extension_hosts_map_, profile))
792 break;
793 ExtensionHostMap::const_iterator gallery_host_it =
794 extension_host_map.find(extensions[i]->id());
795 if (gallery_host_it == extension_host_map.end())
796 continue;
797 gallery_host_it->second->RevokeGalleryByPrefId(pref_id);
801 ExtensionGalleriesHost* MediaFileSystemRegistry::GetExtensionGalleryHost(
802 Profile* profile,
803 MediaGalleriesPreferences* preferences,
804 const std::string& extension_id) {
805 ExtensionGalleriesHostMap::iterator extension_hosts =
806 extension_hosts_map_.find(profile);
807 // GetPreferences(), which had to be called because preferences is an
808 // argument, ensures that profile is in the map.
809 DCHECK(extension_hosts != extension_hosts_map_.end());
810 if (extension_hosts->second.empty())
811 preferences->AddGalleryChangeObserver(this);
813 ExtensionGalleriesHost* result = extension_hosts->second[extension_id].get();
814 if (!result) {
815 result = new ExtensionGalleriesHost(
816 file_system_context_.get(),
817 profile->GetPath(),
818 extension_id,
819 base::Bind(&MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty,
820 base::Unretained(this),
821 profile,
822 extension_id));
823 extension_hosts_map_[profile][extension_id] = result;
825 return result;
828 void MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty(
829 Profile* profile, const std::string& extension_id) {
830 DCHECK_CURRENTLY_ON(BrowserThread::UI);
832 ExtensionGalleriesHostMap::iterator extension_hosts =
833 extension_hosts_map_.find(profile);
834 DCHECK(extension_hosts != extension_hosts_map_.end());
835 ExtensionHostMap::size_type erase_count =
836 extension_hosts->second.erase(extension_id);
837 DCHECK_EQ(1U, erase_count);
838 if (extension_hosts->second.empty()) {
839 // When a profile has no ExtensionGalleriesHosts left, remove the
840 // matching gallery-change-watcher since it is no longer needed. Leave the
841 // |extension_hosts| entry alone, since it indicates the profile has been
842 // previously used.
843 MediaGalleriesPreferences* preferences = GetPreferences(profile);
844 preferences->RemoveGalleryChangeObserver(this);