[Media Router] Add integration tests and e2e tests for media router and presentation...
[chromium-blink-merge.git] / chrome / browser / media_galleries / media_file_system_registry.cc
blob8d3bab8e1cdbeb13c8fe8ef58facdbe80708d20b
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 WebContents reference
65 // goes away. WebContents references are added through
66 // ReferenceFromWebContents().
67 explicit RPHReferenceManager(const base::Closure& no_references_callback);
68 virtual ~RPHReferenceManager();
70 // Remove all references, but don't call |no_references_callback|.
71 void Reset() { STLDeleteValues(&observer_map_); }
73 // Returns true if there are no references;
74 bool empty() const { return observer_map_.empty(); }
76 // Adds a reference to the passed |contents|. Calling this multiple times with
77 // the same |contents| is a no-op.
78 void ReferenceFromWebContents(content::WebContents* contents);
80 private:
81 class RPHWebContentsObserver : public content::WebContentsObserver {
82 public:
83 RPHWebContentsObserver(RPHReferenceManager* manager,
84 WebContents* web_contents);
86 private:
87 // content::WebContentsObserver
88 void WebContentsDestroyed() override;
89 void NavigationEntryCommitted(
90 const content::LoadCommittedDetails& load_details) override;
92 RPHReferenceManager* manager_;
95 class RPHObserver : public content::RenderProcessHostObserver {
96 public:
97 RPHObserver(RPHReferenceManager* manager, RenderProcessHost* host);
98 ~RPHObserver() override;
100 void AddWebContentsObserver(WebContents* web_contents);
101 void RemoveWebContentsObserver(WebContents* web_contents);
102 bool HasWebContentsObservers() {
103 return observed_web_contentses_.size() > 0;
106 private:
107 void RenderProcessHostDestroyed(RenderProcessHost* host) override;
109 RPHReferenceManager* manager_;
110 RenderProcessHost* host_;
111 typedef std::map<WebContents*, RPHWebContentsObserver*> WCObserverMap;
112 WCObserverMap observed_web_contentses_;
114 typedef std::map<const RenderProcessHost*, RPHObserver*> RPHObserverMap;
116 // Handlers for observed events.
117 void OnRenderProcessHostDestroyed(RenderProcessHost* rph);
118 void OnWebContentsDestroyedOrNavigated(WebContents* contents);
120 // A callback to call when the last RVH reference goes away.
121 base::Closure no_references_callback_;
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_;
128 RPHReferenceManager::RPHReferenceManager(
129 const base::Closure& no_references_callback)
130 : no_references_callback_(no_references_callback) {
133 RPHReferenceManager::~RPHReferenceManager() {
134 Reset();
137 void RPHReferenceManager::ReferenceFromWebContents(
138 content::WebContents* contents) {
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];
148 state->AddWebContentsObserver(contents);
151 RPHReferenceManager::RPHWebContentsObserver::RPHWebContentsObserver(
152 RPHReferenceManager* manager,
153 WebContents* web_contents)
154 : content::WebContentsObserver(web_contents),
155 manager_(manager) {
158 void RPHReferenceManager::RPHWebContentsObserver::WebContentsDestroyed() {
159 manager_->OnWebContentsDestroyedOrNavigated(web_contents());
162 void RPHReferenceManager::RPHWebContentsObserver::NavigationEntryCommitted(
163 const content::LoadCommittedDetails& load_details) {
164 if (load_details.is_in_page)
165 return;
167 manager_->OnWebContentsDestroyedOrNavigated(web_contents());
170 RPHReferenceManager::RPHObserver::RPHObserver(
171 RPHReferenceManager* manager, RenderProcessHost* host)
172 : manager_(manager),
173 host_(host) {
174 host->AddObserver(this);
177 RPHReferenceManager::RPHObserver::~RPHObserver() {
178 STLDeleteValues(&observed_web_contentses_);
179 if (host_)
180 host_->RemoveObserver(this);
183 void RPHReferenceManager::RPHObserver::AddWebContentsObserver(
184 WebContents* web_contents) {
185 if (ContainsKey(observed_web_contentses_, web_contents))
186 return;
188 RPHWebContentsObserver* observer =
189 new RPHWebContentsObserver(manager_, web_contents);
190 observed_web_contentses_[web_contents] = observer;
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);
202 void RPHReferenceManager::RPHObserver::RenderProcessHostDestroyed(
203 RenderProcessHost* host) {
204 host_ = NULL;
205 manager_->OnRenderProcessHostDestroyed(host);
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;
218 delete rph_info->second;
219 observer_map_.erase(rph_info);
220 if (observer_map_.empty())
221 no_references_callback_.Run();
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());
230 rph_info->second->RemoveWebContentsObserver(contents);
231 if (!rph_info->second->HasWebContentsObservers())
232 OnRenderProcessHostDestroyed(rph);
235 } // namespace
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) {
253 MediaFileSystemInfo::MediaFileSystemInfo() {}
254 MediaFileSystemInfo::~MediaFileSystemInfo() {}
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 WebContents reference
263 // goes away. WebContents references are added through
264 // ReferenceFromWebContents().
265 ExtensionGalleriesHost(MediaFileSystemContext* file_system_context,
266 const base::FilePath& profile_path,
267 const std::string& extension_id,
268 const base::Closure& no_references_callback)
269 : file_system_context_(file_system_context),
270 profile_path_(profile_path),
271 extension_id_(extension_id),
272 no_references_callback_(no_references_callback),
273 rph_refs_(base::Bind(&ExtensionGalleriesHost::CleanUp,
274 base::Unretained(this))) {
277 // For each gallery in the list of permitted |galleries|, checks if the
278 // device is attached and if so looks up or creates a file system name and
279 // passes the information needed for the renderer to create those file
280 // system objects to the |callback|.
281 void GetMediaFileSystems(const MediaGalleryPrefIdSet& galleries,
282 const MediaGalleriesPrefInfoMap& galleries_info,
283 const MediaFileSystemsCallback& callback) {
284 DCHECK_CURRENTLY_ON(BrowserThread::UI);
286 // Extract all the device ids so we can make sure they are attached.
287 MediaStorageUtil::DeviceIdSet* device_ids =
288 new MediaStorageUtil::DeviceIdSet;
289 for (std::set<MediaGalleryPrefId>::const_iterator id = galleries.begin();
290 id != galleries.end();
291 ++id) {
292 device_ids->insert(galleries_info.find(*id)->second.device_id);
294 MediaStorageUtil::FilterAttachedDevices(device_ids, base::Bind(
295 &ExtensionGalleriesHost::GetMediaFileSystemsForAttachedDevices, this,
296 base::Owned(device_ids), galleries, galleries_info, callback));
299 // Checks if |gallery| is attached and if so, registers the file system and
300 // then calls |callback| with the result.
301 void RegisterMediaFileSystem(
302 const MediaGalleryPrefInfo& gallery,
303 const base::Callback<void(base::File::Error result)>& callback) {
304 // Extract all the device ids so we can make sure they are attached.
305 MediaStorageUtil::DeviceIdSet* device_ids =
306 new MediaStorageUtil::DeviceIdSet;
307 device_ids->insert(gallery.device_id);
308 MediaStorageUtil::FilterAttachedDevices(device_ids, base::Bind(
309 &ExtensionGalleriesHost::RegisterAttachedMediaFileSystem, this,
310 base::Owned(device_ids), gallery, callback));
313 // Revoke the file system for |id| if this extension has created one for |id|.
314 void RevokeGalleryByPrefId(MediaGalleryPrefId id) {
315 PrefIdFsInfoMap::iterator gallery = pref_id_map_.find(id);
316 if (gallery == pref_id_map_.end())
317 return;
319 file_system_context_->RevokeFileSystem(gallery->second.fsid);
320 pref_id_map_.erase(gallery);
322 if (pref_id_map_.empty()) {
323 rph_refs_.Reset();
324 CleanUp();
328 // Indicate that the passed |contents| will reference the file system ids
329 // created
330 // by this class.
331 void ReferenceFromWebContents(content::WebContents* web_contents) {
332 rph_refs_.ReferenceFromWebContents(web_contents);
335 private:
336 typedef std::map<MediaGalleryPrefId, MediaFileSystemInfo> PrefIdFsInfoMap;
338 // Private destructor and friend declaration for ref counted implementation.
339 friend class base::RefCountedThreadSafe<ExtensionGalleriesHost>;
341 virtual ~ExtensionGalleriesHost() {
342 DCHECK(rph_refs_.empty());
343 DCHECK(pref_id_map_.empty());
346 void GetMediaFileSystemsForAttachedDevices(
347 const MediaStorageUtil::DeviceIdSet* attached_devices,
348 const MediaGalleryPrefIdSet& galleries,
349 const MediaGalleriesPrefInfoMap& galleries_info,
350 const MediaFileSystemsCallback& callback) {
351 std::vector<MediaFileSystemInfo> result;
353 if (rph_refs_.empty()) {
354 // We're actually in the middle of shutdown, and Filter...() lagging
355 // which can invoke this method interleaved in the destruction callback
356 // sequence and re-populate pref_id_map_.
357 callback.Run(result);
358 return;
361 for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it =
362 galleries.begin();
363 pref_id_it != galleries.end();
364 ++pref_id_it) {
365 const MediaGalleryPrefId& pref_id = *pref_id_it;
366 const MediaGalleryPrefInfo& gallery_info =
367 galleries_info.find(pref_id)->second;
368 const std::string& device_id = gallery_info.device_id;
369 if (!ContainsKey(*attached_devices, device_id))
370 continue;
372 PrefIdFsInfoMap::const_iterator existing_info =
373 pref_id_map_.find(pref_id);
374 if (existing_info != pref_id_map_.end()) {
375 result.push_back(existing_info->second);
376 continue;
379 base::FilePath path = gallery_info.AbsolutePath();
380 if (!MediaStorageUtil::CanCreateFileSystem(device_id, path))
381 continue;
383 std::string fs_name = MediaFileSystemBackend::ConstructMountName(
384 profile_path_, extension_id_, pref_id);
385 if (!file_system_context_->RegisterFileSystem(device_id, fs_name, path))
386 continue;
388 MediaFileSystemInfo new_entry(
389 gallery_info.GetGalleryDisplayName(),
390 file_system_context_->GetRegisteredPath(fs_name),
391 fs_name,
392 pref_id,
393 GetTransientIdForRemovableDeviceId(device_id),
394 StorageInfo::IsRemovableDevice(device_id),
395 StorageInfo::IsMediaDevice(device_id));
396 result.push_back(new_entry);
397 pref_id_map_[pref_id] = new_entry;
400 if (result.size() == 0) {
401 rph_refs_.Reset();
402 CleanUp();
405 DCHECK_EQ(pref_id_map_.size(), result.size());
406 callback.Run(result);
409 void RegisterAttachedMediaFileSystem(
410 const MediaStorageUtil::DeviceIdSet* attached_device,
411 const MediaGalleryPrefInfo& gallery,
412 const base::Callback<void(base::File::Error result)>& callback) {
413 base::File::Error result = base::File::FILE_ERROR_NOT_FOUND;
415 // If rph_refs is empty then we're actually in the middle of shutdown, and
416 // Filter...() lagging which can invoke this method interleaved in the
417 // destruction callback sequence and re-populate pref_id_map_.
418 if (!attached_device->empty() && !rph_refs_.empty()) {
419 std::string fs_name = MediaFileSystemBackend::ConstructMountName(
420 profile_path_, extension_id_, gallery.pref_id);
421 base::FilePath path = gallery.AbsolutePath();
422 const std::string& device_id = gallery.device_id;
424 if (ContainsKey(pref_id_map_, gallery.pref_id)) {
425 result = base::File::FILE_OK;
426 } else if (MediaStorageUtil::CanCreateFileSystem(device_id, path) &&
427 file_system_context_->RegisterFileSystem(device_id, fs_name,
428 path)) {
429 result = base::File::FILE_OK;
430 pref_id_map_[gallery.pref_id] = MediaFileSystemInfo(
431 gallery.GetGalleryDisplayName(),
432 file_system_context_->GetRegisteredPath(fs_name),
433 fs_name,
434 gallery.pref_id,
435 GetTransientIdForRemovableDeviceId(device_id),
436 StorageInfo::IsRemovableDevice(device_id),
437 StorageInfo::IsMediaDevice(device_id));
441 if (pref_id_map_.empty()) {
442 rph_refs_.Reset();
443 CleanUp();
445 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
446 base::Bind(callback, result));
449 std::string GetTransientIdForRemovableDeviceId(const std::string& device_id) {
450 if (!StorageInfo::IsRemovableDevice(device_id))
451 return std::string();
453 return StorageMonitor::GetInstance()->GetTransientIdForDeviceId(device_id);
456 void CleanUp() {
457 DCHECK(rph_refs_.empty());
458 for (PrefIdFsInfoMap::const_iterator it = pref_id_map_.begin();
459 it != pref_id_map_.end();
460 ++it) {
461 file_system_context_->RevokeFileSystem(it->second.fsid);
463 pref_id_map_.clear();
465 no_references_callback_.Run();
468 // MediaFileSystemRegistry owns |this| and |file_system_context_|, so it's
469 // safe to store a raw pointer.
470 MediaFileSystemContext* file_system_context_;
472 // Path for the active profile.
473 const base::FilePath profile_path_;
475 // Id of the extension this host belongs to.
476 const std::string extension_id_;
478 // A callback to call when the last WebContents reference goes away.
479 base::Closure no_references_callback_;
481 // A map from the gallery preferences id to the file system information.
482 PrefIdFsInfoMap pref_id_map_;
484 // The set of render processes and web contents that may have references to
485 // the file system ids this instance manages.
486 RPHReferenceManager rph_refs_;
488 DISALLOW_COPY_AND_ASSIGN(ExtensionGalleriesHost);
491 /******************
492 * Public methods
493 ******************/
495 void MediaFileSystemRegistry::GetMediaFileSystemsForExtension(
496 content::WebContents* contents,
497 const extensions::Extension* extension,
498 const MediaFileSystemsCallback& callback) {
499 // TODO(tommycli): Change to DCHECK after fixing http://crbug.com/374330.
500 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
502 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
503 MediaGalleriesPreferences* preferences = GetPreferences(profile);
504 MediaGalleryPrefIdSet galleries =
505 preferences->GalleriesForExtension(*extension);
507 if (galleries.empty()) {
508 callback.Run(std::vector<MediaFileSystemInfo>());
509 return;
512 ExtensionGalleriesHost* extension_host =
513 GetExtensionGalleryHost(profile, preferences, extension->id());
515 // This must come before the GetMediaFileSystems call to make sure the
516 // contents of the context is referenced before the filesystems are retrieved.
517 extension_host->ReferenceFromWebContents(contents);
519 extension_host->GetMediaFileSystems(galleries, preferences->known_galleries(),
520 callback);
523 void MediaFileSystemRegistry::RegisterMediaFileSystemForExtension(
524 content::WebContents* contents,
525 const extensions::Extension* extension,
526 MediaGalleryPrefId pref_id,
527 const base::Callback<void(base::File::Error result)>& callback) {
528 DCHECK_CURRENTLY_ON(BrowserThread::UI);
529 DCHECK_NE(kInvalidMediaGalleryPrefId, pref_id);
531 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
532 MediaGalleriesPreferences* preferences = GetPreferences(profile);
533 MediaGalleriesPrefInfoMap::const_iterator gallery =
534 preferences->known_galleries().find(pref_id);
535 MediaGalleryPrefIdSet permitted_galleries =
536 preferences->GalleriesForExtension(*extension);
538 if (gallery == preferences->known_galleries().end() ||
539 !ContainsKey(permitted_galleries, pref_id)) {
540 BrowserThread::PostTask(
541 BrowserThread::IO, FROM_HERE,
542 base::Bind(callback, base::File::FILE_ERROR_NOT_FOUND));
543 return;
546 ExtensionGalleriesHost* extension_host =
547 GetExtensionGalleryHost(profile, preferences, extension->id());
549 // This must come before the GetMediaFileSystems call to make sure the
550 // contents of the context is referenced before the filesystems are retrieved.
551 extension_host->ReferenceFromWebContents(contents);
553 extension_host->RegisterMediaFileSystem(gallery->second, callback);
556 MediaGalleriesPreferences* MediaFileSystemRegistry::GetPreferences(
557 Profile* profile) {
558 // Create an empty ExtensionHostMap for this profile on first initialization.
559 if (!ContainsKey(extension_hosts_map_, profile)) {
560 extension_hosts_map_[profile] = ExtensionHostMap();
561 media_galleries::UsageCount(media_galleries::PROFILES_WITH_USAGE);
564 return MediaGalleriesPreferencesFactory::GetForProfile(profile);
567 MediaScanManager* MediaFileSystemRegistry::media_scan_manager() {
568 if (!media_scan_manager_)
569 media_scan_manager_.reset(new MediaScanManager);
570 return media_scan_manager_.get();
573 GalleryWatchManager* MediaFileSystemRegistry::gallery_watch_manager() {
574 if (!gallery_watch_manager_)
575 gallery_watch_manager_.reset(new GalleryWatchManager);
576 return gallery_watch_manager_.get();
579 void MediaFileSystemRegistry::OnRemovableStorageDetached(
580 const StorageInfo& info) {
581 DCHECK_CURRENTLY_ON(BrowserThread::UI);
583 // Since revoking a gallery in the ExtensionGalleriesHost may cause it
584 // to be removed from the map and therefore invalidate any iterator pointing
585 // to it, this code first copies all the invalid gallery ids and the
586 // extension hosts in which they may appear (per profile) and revoked it in
587 // a second step.
588 std::vector<InvalidatedGalleriesInfo> invalid_galleries_info;
590 for (ExtensionGalleriesHostMap::iterator profile_it =
591 extension_hosts_map_.begin();
592 profile_it != extension_hosts_map_.end();
593 ++profile_it) {
594 MediaGalleriesPreferences* preferences = GetPreferences(profile_it->first);
595 // If |preferences| is not yet initialized, it won't contain any galleries.
596 if (!preferences->IsInitialized())
597 continue;
599 InvalidatedGalleriesInfo invalid_galleries_in_profile;
600 invalid_galleries_in_profile.pref_ids =
601 preferences->LookUpGalleriesByDeviceId(info.device_id());
603 for (ExtensionHostMap::const_iterator extension_host_it =
604 profile_it->second.begin();
605 extension_host_it != profile_it->second.end();
606 ++extension_host_it) {
607 invalid_galleries_in_profile.extension_hosts.insert(
608 extension_host_it->second.get());
611 invalid_galleries_info.push_back(invalid_galleries_in_profile);
614 for (size_t i = 0; i < invalid_galleries_info.size(); i++) {
615 for (std::set<ExtensionGalleriesHost*>::const_iterator extension_host_it =
616 invalid_galleries_info[i].extension_hosts.begin();
617 extension_host_it != invalid_galleries_info[i].extension_hosts.end();
618 ++extension_host_it) {
619 for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it =
620 invalid_galleries_info[i].pref_ids.begin();
621 pref_id_it != invalid_galleries_info[i].pref_ids.end();
622 ++pref_id_it) {
623 (*extension_host_it)->RevokeGalleryByPrefId(*pref_id_it);
629 /******************
630 * Private methods
631 ******************/
633 class MediaFileSystemRegistry::MediaFileSystemContextImpl
634 : public MediaFileSystemContext {
635 public:
636 MediaFileSystemContextImpl() {}
637 ~MediaFileSystemContextImpl() override {}
639 bool RegisterFileSystem(const std::string& device_id,
640 const std::string& fs_name,
641 const base::FilePath& path) override {
642 if (StorageInfo::IsMassStorageDevice(device_id)) {
643 return RegisterFileSystemForMassStorage(device_id, fs_name, path);
644 } else {
645 return RegisterFileSystemForMTPDevice(device_id, fs_name, path);
649 void RevokeFileSystem(const std::string& fs_name) override {
650 ImportedMediaGalleryRegistry* imported_registry =
651 ImportedMediaGalleryRegistry::GetInstance();
652 if (imported_registry->RevokeImportedFilesystemOnUIThread(fs_name))
653 return;
655 ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(fs_name);
657 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
658 &MTPDeviceMapService::RevokeMTPFileSystem,
659 base::Unretained(MTPDeviceMapService::GetInstance()),
660 fs_name));
663 base::FilePath GetRegisteredPath(const std::string& fs_name) const override {
664 base::FilePath result;
665 if (!ExternalMountPoints::GetSystemInstance()->GetRegisteredPath(fs_name,
666 &result)) {
667 return base::FilePath();
669 return result;
672 private:
673 // Registers and returns the file system id for the mass storage device
674 // specified by |device_id| and |path|.
675 bool RegisterFileSystemForMassStorage(const std::string& device_id,
676 const std::string& fs_name,
677 const base::FilePath& path) {
678 DCHECK_CURRENTLY_ON(BrowserThread::UI);
679 DCHECK(StorageInfo::IsMassStorageDevice(device_id));
681 // Sanity checks for |path|.
682 CHECK(path.IsAbsolute());
683 CHECK(!path.ReferencesParent());
685 // TODO(gbillock): refactor ImportedMediaGalleryRegistry to delegate this
686 // call tree, probably by having it figure out by device id what
687 // registration is needed, or having per-device-type handlers at the
688 // next higher level.
689 bool result = false;
690 if (StorageInfo::IsITunesDevice(device_id)) {
691 ImportedMediaGalleryRegistry* registry =
692 ImportedMediaGalleryRegistry::GetInstance();
693 result = registry->RegisterITunesFilesystemOnUIThread(fs_name, path);
694 } else if (StorageInfo::IsPicasaDevice(device_id)) {
695 ImportedMediaGalleryRegistry* registry =
696 ImportedMediaGalleryRegistry::GetInstance();
697 result = registry->RegisterPicasaFilesystemOnUIThread(fs_name, path);
698 } else if (StorageInfo::IsIPhotoDevice(device_id)) {
699 ImportedMediaGalleryRegistry* registry =
700 ImportedMediaGalleryRegistry::GetInstance();
701 result = registry->RegisterIPhotoFilesystemOnUIThread(fs_name, path);
702 } else {
703 result = ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
704 fs_name,
705 storage::kFileSystemTypeNativeMedia,
706 storage::FileSystemMountOption(),
707 path);
709 return result;
712 bool RegisterFileSystemForMTPDevice(const std::string& device_id,
713 const std::string fs_name,
714 const base::FilePath& path) {
715 DCHECK_CURRENTLY_ON(BrowserThread::UI);
716 DCHECK(!StorageInfo::IsMassStorageDevice(device_id));
718 // Sanity checks for |path|.
719 CHECK(MediaStorageUtil::CanCreateFileSystem(device_id, path));
720 bool result = ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
721 fs_name,
722 storage::kFileSystemTypeDeviceMedia,
723 storage::FileSystemMountOption(),
724 path);
725 CHECK(result);
726 BrowserThread::PostTask(
727 BrowserThread::IO, FROM_HERE,
728 base::Bind(&MTPDeviceMapService::RegisterMTPFileSystem,
729 base::Unretained(MTPDeviceMapService::GetInstance()),
730 path.value(), fs_name, true /* read only */));
731 return result;
734 DISALLOW_COPY_AND_ASSIGN(MediaFileSystemContextImpl);
737 // Constructor in 'private' section because depends on private class definition.
738 MediaFileSystemRegistry::MediaFileSystemRegistry()
739 : file_system_context_(new MediaFileSystemContextImpl) {
740 StorageMonitor::GetInstance()->AddObserver(this);
743 MediaFileSystemRegistry::~MediaFileSystemRegistry() {
744 // TODO(gbillock): This is needed because the unit test uses the
745 // g_browser_process registry. We should create one in the unit test,
746 // and then can remove this.
747 if (StorageMonitor::GetInstance())
748 StorageMonitor::GetInstance()->RemoveObserver(this);
751 void MediaFileSystemRegistry::OnPermissionRemoved(
752 MediaGalleriesPreferences* prefs,
753 const std::string& extension_id,
754 MediaGalleryPrefId pref_id) {
755 Profile* profile = prefs->profile();
756 ExtensionGalleriesHostMap::const_iterator host_map_it =
757 extension_hosts_map_.find(profile);
758 DCHECK(host_map_it != extension_hosts_map_.end());
759 const ExtensionHostMap& extension_host_map = host_map_it->second;
760 ExtensionHostMap::const_iterator gallery_host_it =
761 extension_host_map.find(extension_id);
762 if (gallery_host_it == extension_host_map.end())
763 return;
764 gallery_host_it->second->RevokeGalleryByPrefId(pref_id);
767 void MediaFileSystemRegistry::OnGalleryRemoved(
768 MediaGalleriesPreferences* prefs,
769 MediaGalleryPrefId pref_id) {
770 Profile* profile = prefs->profile();
771 // Get the Extensions, MediaGalleriesPreferences and ExtensionHostMap for
772 // |profile|.
773 const extensions::ExtensionRegistry* extension_registry =
774 extensions::ExtensionRegistry::Get(profile);
775 ExtensionGalleriesHostMap::const_iterator host_map_it =
776 extension_hosts_map_.find(profile);
777 DCHECK(host_map_it != extension_hosts_map_.end());
778 const ExtensionHostMap& extension_host_map = host_map_it->second;
780 // Go through ExtensionHosts, and remove indicated gallery, if any.
781 // RevokeGalleryByPrefId() may end up deleting from |extension_host_map| and
782 // even delete |extension_host_map| altogether. So do this in two loops to
783 // avoid using an invalidated iterator or deleted map.
784 std::vector<const extensions::Extension*> extensions;
785 for (ExtensionHostMap::const_iterator it = extension_host_map.begin();
786 it != extension_host_map.end();
787 ++it) {
788 extensions.push_back(
789 extension_registry->enabled_extensions().GetByID(it->first));
791 for (size_t i = 0; i < extensions.size(); ++i) {
792 if (!ContainsKey(extension_hosts_map_, profile))
793 break;
794 ExtensionHostMap::const_iterator gallery_host_it =
795 extension_host_map.find(extensions[i]->id());
796 if (gallery_host_it == extension_host_map.end())
797 continue;
798 gallery_host_it->second->RevokeGalleryByPrefId(pref_id);
802 ExtensionGalleriesHost* MediaFileSystemRegistry::GetExtensionGalleryHost(
803 Profile* profile,
804 MediaGalleriesPreferences* preferences,
805 const std::string& extension_id) {
806 ExtensionGalleriesHostMap::iterator extension_hosts =
807 extension_hosts_map_.find(profile);
808 // GetPreferences(), which had to be called because preferences is an
809 // argument, ensures that profile is in the map.
810 DCHECK(extension_hosts != extension_hosts_map_.end());
811 if (extension_hosts->second.empty())
812 preferences->AddGalleryChangeObserver(this);
814 ExtensionGalleriesHost* result = extension_hosts->second[extension_id].get();
815 if (!result) {
816 result = new ExtensionGalleriesHost(
817 file_system_context_.get(),
818 profile->GetPath(),
819 extension_id,
820 base::Bind(&MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty,
821 base::Unretained(this),
822 profile,
823 extension_id));
824 extension_hosts_map_[profile][extension_id] = result;
826 return result;
829 void MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty(
830 Profile* profile, const std::string& extension_id) {
831 DCHECK_CURRENTLY_ON(BrowserThread::UI);
833 ExtensionGalleriesHostMap::iterator extension_hosts =
834 extension_hosts_map_.find(profile);
835 DCHECK(extension_hosts != extension_hosts_map_.end());
836 ExtensionHostMap::size_type erase_count =
837 extension_hosts->second.erase(extension_id);
838 DCHECK_EQ(1U, erase_count);
839 if (extension_hosts->second.empty()) {
840 // When a profile has no ExtensionGalleriesHosts left, remove the
841 // matching gallery-change-watcher since it is no longer needed. Leave the
842 // |extension_hosts| entry alone, since it indicates the profile has been
843 // previously used.
844 MediaGalleriesPreferences* preferences = GetPreferences(profile);
845 preferences->RemoveGalleryChangeObserver(this);