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"
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
;
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
{
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
);
80 class RPHWebContentsObserver
: public content::WebContentsObserver
{
82 RPHWebContentsObserver(RPHReferenceManager
* manager
,
83 WebContents
* web_contents
);
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
{
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;
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() {
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
;
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
),
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
)
166 manager_
->OnWebContentsDestroyedOrNavigated(web_contents());
169 RPHReferenceManager::RPHObserver::RPHObserver(
170 RPHReferenceManager
* manager
, RenderProcessHost
* host
)
173 host
->AddObserver(this);
176 RPHReferenceManager::RPHObserver::~RPHObserver() {
177 STLDeleteValues(&observed_web_contentses_
);
179 host_
->RemoveObserver(this);
182 void RPHReferenceManager::RPHObserver::AddWebContentsObserver(
183 WebContents
* web_contents
) {
184 if (ContainsKey(observed_web_contentses_
, web_contents
))
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
) {
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
213 if (rph_info
== observer_map_
.end()) {
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
);
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
,
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
> {
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();
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())
317 file_system_context_
->RevokeFileSystem(gallery
->second
.fsid
);
318 pref_id_map_
.erase(gallery
);
320 if (pref_id_map_
.empty()) {
326 // Indicate that the passed |rvh| will reference the file system ids created
328 void ReferenceFromRVH(const content::RenderViewHost
* rvh
) {
329 rph_refs_
.ReferenceFromRVH(rvh
);
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
);
358 for (std::set
<MediaGalleryPrefId
>::const_iterator pref_id_it
=
360 pref_id_it
!= galleries
.end();
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
))
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
);
376 base::FilePath path
= gallery_info
.AbsolutePath();
377 if (!MediaStorageUtil::CanCreateFileSystem(device_id
, path
))
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
))
385 MediaFileSystemInfo
new_entry(
386 gallery_info
.GetGalleryDisplayName(),
387 file_system_context_
->GetRegisteredPath(fs_name
),
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) {
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
,
426 result
= base::File::FILE_OK
;
427 pref_id_map_
[gallery
.pref_id
] = MediaFileSystemInfo(
428 gallery
.GetGalleryDisplayName(),
429 file_system_context_
->GetRegisteredPath(fs_name
),
432 GetTransientIdForRemovableDeviceId(device_id
),
433 StorageInfo::IsRemovableDevice(device_id
),
434 StorageInfo::IsMediaDevice(device_id
));
438 if (pref_id_map_
.empty()) {
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
);
454 DCHECK(rph_refs_
.empty());
455 for (PrefIdFsInfoMap::const_iterator it
= pref_id_map_
.begin();
456 it
!= pref_id_map_
.end();
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
);
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
));
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
>());
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(),
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
);
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
));
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(
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
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();
593 MediaGalleriesPreferences
* preferences
= GetPreferences(profile_it
->first
);
594 // If |preferences| is not yet initialized, it won't contain any galleries.
595 if (!preferences
->IsInitialized())
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();
622 (*extension_host_it
)->RevokeGalleryByPrefId(*pref_id_it
);
632 class MediaFileSystemRegistry::MediaFileSystemContextImpl
633 : public MediaFileSystemContext
{
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
);
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
))
654 ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(fs_name
);
656 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
, base::Bind(
657 &MTPDeviceMapService::RevokeMTPFileSystem
,
658 base::Unretained(MTPDeviceMapService::GetInstance()),
662 base::FilePath
GetRegisteredPath(const std::string
& fs_name
) const override
{
663 base::FilePath result
;
664 if (!ExternalMountPoints::GetSystemInstance()->GetRegisteredPath(fs_name
,
666 return base::FilePath();
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.
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
);
702 result
= ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
704 storage::kFileSystemTypeNativeMedia
,
705 storage::FileSystemMountOption(),
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(
721 storage::kFileSystemTypeDeviceMedia
,
722 storage::FileSystemMountOption(),
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 */));
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())
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
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();
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
))
793 ExtensionHostMap::const_iterator gallery_host_it
=
794 extension_host_map
.find(extensions
[i
]->id());
795 if (gallery_host_it
== extension_host_map
.end())
797 gallery_host_it
->second
->RevokeGalleryByPrefId(pref_id
);
801 ExtensionGalleriesHost
* MediaFileSystemRegistry::GetExtensionGalleryHost(
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();
815 result
= new ExtensionGalleriesHost(
816 file_system_context_
.get(),
819 base::Bind(&MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty
,
820 base::Unretained(this),
823 extension_hosts_map_
[profile
][extension_id
] = 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
843 MediaGalleriesPreferences
* preferences
= GetPreferences(profile
);
844 preferences
->RemoveGalleryChangeObserver(this);