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/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
28 #include "components/storage_monitor/media_storage_util.h"
29 #include "components/storage_monitor/storage_monitor.h"
30 #include "content/public/browser/browser_thread.h"
31 #include "content/public/browser/navigation_details.h"
32 #include "content/public/browser/render_process_host.h"
33 #include "content/public/browser/render_process_host_observer.h"
34 #include "content/public/browser/render_view_host.h"
35 #include "content/public/browser/web_contents.h"
36 #include "content/public/browser/web_contents_observer.h"
37 #include "extensions/browser/extension_registry.h"
38 #include "extensions/common/extension.h"
39 #include "extensions/common/extension_set.h"
40 #include "storage/browser/fileapi/external_mount_points.h"
41 #include "storage/common/fileapi/file_system_mount_option.h"
42 #include "storage/common/fileapi/file_system_types.h"
44 using content::BrowserThread
;
45 using content::NavigationController
;
46 using content::RenderProcessHost
;
47 using content::WebContents
;
48 using storage::ExternalMountPoints
;
49 using storage_monitor::MediaStorageUtil
;
50 using storage_monitor::StorageInfo
;
51 using storage_monitor::StorageMonitor
;
55 class ShutdownNotifierFactory
56 : public BrowserContextKeyedServiceShutdownNotifierFactory
{
58 static ShutdownNotifierFactory
* GetInstance() {
59 return Singleton
<ShutdownNotifierFactory
>::get();
63 friend struct DefaultSingletonTraits
<ShutdownNotifierFactory
>;
65 ShutdownNotifierFactory()
66 : BrowserContextKeyedServiceShutdownNotifierFactory(
67 "MediaFileSystemRegistry") {
68 DependsOn(MediaGalleriesPreferencesFactory::GetInstance());
70 ~ShutdownNotifierFactory() override
{}
72 DISALLOW_COPY_AND_ASSIGN(ShutdownNotifierFactory
);
75 struct InvalidatedGalleriesInfo
{
76 std::set
<ExtensionGalleriesHost
*> extension_hosts
;
77 std::set
<MediaGalleryPrefId
> pref_ids
;
80 // Tracks the liveness of multiple RenderProcessHosts that the caller is
81 // interested in. Once all of the RPHs have closed or been destroyed a call
82 // back informs the caller.
83 class RPHReferenceManager
{
85 // |no_references_callback| is called when the last WebContents reference
86 // goes away. WebContents references are added through
87 // ReferenceFromWebContents().
88 explicit RPHReferenceManager(const base::Closure
& no_references_callback
);
89 virtual ~RPHReferenceManager();
91 // Remove all references, but don't call |no_references_callback|.
92 void Reset() { STLDeleteValues(&observer_map_
); }
94 // Returns true if there are no references;
95 bool empty() const { return observer_map_
.empty(); }
97 // Adds a reference to the passed |contents|. Calling this multiple times with
98 // the same |contents| is a no-op.
99 void ReferenceFromWebContents(content::WebContents
* contents
);
102 class RPHWebContentsObserver
: public content::WebContentsObserver
{
104 RPHWebContentsObserver(RPHReferenceManager
* manager
,
105 WebContents
* web_contents
);
108 // content::WebContentsObserver
109 void WebContentsDestroyed() override
;
110 void NavigationEntryCommitted(
111 const content::LoadCommittedDetails
& load_details
) override
;
113 RPHReferenceManager
* manager_
;
116 class RPHObserver
: public content::RenderProcessHostObserver
{
118 RPHObserver(RPHReferenceManager
* manager
, RenderProcessHost
* host
);
119 ~RPHObserver() override
;
121 void AddWebContentsObserver(WebContents
* web_contents
);
122 void RemoveWebContentsObserver(WebContents
* web_contents
);
123 bool HasWebContentsObservers() {
124 return observed_web_contentses_
.size() > 0;
128 void RenderProcessHostDestroyed(RenderProcessHost
* host
) override
;
130 RPHReferenceManager
* manager_
;
131 RenderProcessHost
* host_
;
132 typedef std::map
<WebContents
*, RPHWebContentsObserver
*> WCObserverMap
;
133 WCObserverMap observed_web_contentses_
;
135 typedef std::map
<const RenderProcessHost
*, RPHObserver
*> RPHObserverMap
;
137 // Handlers for observed events.
138 void OnRenderProcessHostDestroyed(RenderProcessHost
* rph
);
139 void OnWebContentsDestroyedOrNavigated(WebContents
* contents
);
141 // A callback to call when the last RVH reference goes away.
142 base::Closure no_references_callback_
;
144 // The set of render processes and web contents that may have references to
145 // the file system ids this instance manages.
146 RPHObserverMap observer_map_
;
149 RPHReferenceManager::RPHReferenceManager(
150 const base::Closure
& no_references_callback
)
151 : no_references_callback_(no_references_callback
) {
154 RPHReferenceManager::~RPHReferenceManager() {
158 void RPHReferenceManager::ReferenceFromWebContents(
159 content::WebContents
* contents
) {
160 RenderProcessHost
* rph
= contents
->GetRenderProcessHost();
161 RPHObserver
* state
= NULL
;
162 if (!ContainsKey(observer_map_
, rph
)) {
163 state
= new RPHObserver(this, rph
);
164 observer_map_
[rph
] = state
;
166 state
= observer_map_
[rph
];
169 state
->AddWebContentsObserver(contents
);
172 RPHReferenceManager::RPHWebContentsObserver::RPHWebContentsObserver(
173 RPHReferenceManager
* manager
,
174 WebContents
* web_contents
)
175 : content::WebContentsObserver(web_contents
),
179 void RPHReferenceManager::RPHWebContentsObserver::WebContentsDestroyed() {
180 manager_
->OnWebContentsDestroyedOrNavigated(web_contents());
183 void RPHReferenceManager::RPHWebContentsObserver::NavigationEntryCommitted(
184 const content::LoadCommittedDetails
& load_details
) {
185 if (load_details
.is_in_page
)
188 manager_
->OnWebContentsDestroyedOrNavigated(web_contents());
191 RPHReferenceManager::RPHObserver::RPHObserver(
192 RPHReferenceManager
* manager
, RenderProcessHost
* host
)
195 host
->AddObserver(this);
198 RPHReferenceManager::RPHObserver::~RPHObserver() {
199 STLDeleteValues(&observed_web_contentses_
);
201 host_
->RemoveObserver(this);
204 void RPHReferenceManager::RPHObserver::AddWebContentsObserver(
205 WebContents
* web_contents
) {
206 if (ContainsKey(observed_web_contentses_
, web_contents
))
209 RPHWebContentsObserver
* observer
=
210 new RPHWebContentsObserver(manager_
, web_contents
);
211 observed_web_contentses_
[web_contents
] = observer
;
214 void RPHReferenceManager::RPHObserver::RemoveWebContentsObserver(
215 WebContents
* web_contents
) {
216 WCObserverMap::iterator wco_iter
=
217 observed_web_contentses_
.find(web_contents
);
218 DCHECK(wco_iter
!= observed_web_contentses_
.end());
219 delete wco_iter
->second
;
220 observed_web_contentses_
.erase(wco_iter
);
223 void RPHReferenceManager::RPHObserver::RenderProcessHostDestroyed(
224 RenderProcessHost
* host
) {
226 manager_
->OnRenderProcessHostDestroyed(host
);
229 void RPHReferenceManager::OnRenderProcessHostDestroyed(
230 RenderProcessHost
* rph
) {
231 RPHObserverMap::iterator rph_info
= observer_map_
.find(rph
);
232 // This could be a potential problem if the RPH is navigated to a page on the
233 // same renderer (triggering OnWebContentsDestroyedOrNavigated()) and then the
235 if (rph_info
== observer_map_
.end()) {
239 delete rph_info
->second
;
240 observer_map_
.erase(rph_info
);
241 if (observer_map_
.empty())
242 no_references_callback_
.Run();
245 void RPHReferenceManager::OnWebContentsDestroyedOrNavigated(
246 WebContents
* contents
) {
247 RenderProcessHost
* rph
= contents
->GetRenderProcessHost();
248 RPHObserverMap::iterator rph_info
= observer_map_
.find(rph
);
249 DCHECK(rph_info
!= observer_map_
.end());
251 rph_info
->second
->RemoveWebContentsObserver(contents
);
252 if (!rph_info
->second
->HasWebContentsObservers())
253 OnRenderProcessHostDestroyed(rph
);
258 MediaFileSystemInfo::MediaFileSystemInfo(const base::string16
& fs_name
,
259 const base::FilePath
& fs_path
,
260 const std::string
& filesystem_id
,
261 MediaGalleryPrefId pref_id
,
262 const std::string
& transient_device_id
,
269 transient_device_id(transient_device_id
),
270 removable(removable
),
271 media_device(media_device
) {
274 MediaFileSystemInfo::MediaFileSystemInfo() {}
275 MediaFileSystemInfo::~MediaFileSystemInfo() {}
277 // The main owner of this class is
278 // |MediaFileSystemRegistry::extension_hosts_map_|, but a callback may
279 // temporarily hold a reference.
280 class ExtensionGalleriesHost
281 : public base::RefCountedThreadSafe
<ExtensionGalleriesHost
> {
283 // |no_references_callback| is called when the last WebContents reference
284 // goes away. WebContents references are added through
285 // ReferenceFromWebContents().
286 ExtensionGalleriesHost(MediaFileSystemContext
* file_system_context
,
287 const base::FilePath
& profile_path
,
288 const std::string
& extension_id
,
289 const base::Closure
& no_references_callback
)
290 : file_system_context_(file_system_context
),
291 profile_path_(profile_path
),
292 extension_id_(extension_id
),
293 no_references_callback_(no_references_callback
),
294 rph_refs_(base::Bind(&ExtensionGalleriesHost::CleanUp
,
295 base::Unretained(this))) {
298 // For each gallery in the list of permitted |galleries|, checks if the
299 // device is attached and if so looks up or creates a file system name and
300 // passes the information needed for the renderer to create those file
301 // system objects to the |callback|.
302 void GetMediaFileSystems(const MediaGalleryPrefIdSet
& galleries
,
303 const MediaGalleriesPrefInfoMap
& galleries_info
,
304 const MediaFileSystemsCallback
& callback
) {
305 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
307 // Extract all the device ids so we can make sure they are attached.
308 MediaStorageUtil::DeviceIdSet
* device_ids
=
309 new MediaStorageUtil::DeviceIdSet
;
310 for (std::set
<MediaGalleryPrefId
>::const_iterator id
= galleries
.begin();
311 id
!= galleries
.end();
313 device_ids
->insert(galleries_info
.find(*id
)->second
.device_id
);
315 MediaStorageUtil::FilterAttachedDevices(device_ids
, base::Bind(
316 &ExtensionGalleriesHost::GetMediaFileSystemsForAttachedDevices
, this,
317 base::Owned(device_ids
), galleries
, galleries_info
, callback
));
320 // Checks if |gallery| is attached and if so, registers the file system and
321 // then calls |callback| with the result.
322 void RegisterMediaFileSystem(
323 const MediaGalleryPrefInfo
& gallery
,
324 const base::Callback
<void(base::File::Error result
)>& callback
) {
325 // Extract all the device ids so we can make sure they are attached.
326 MediaStorageUtil::DeviceIdSet
* device_ids
=
327 new MediaStorageUtil::DeviceIdSet
;
328 device_ids
->insert(gallery
.device_id
);
329 MediaStorageUtil::FilterAttachedDevices(device_ids
, base::Bind(
330 &ExtensionGalleriesHost::RegisterAttachedMediaFileSystem
, this,
331 base::Owned(device_ids
), gallery
, callback
));
334 // Revoke the file system for |id| if this extension has created one for |id|.
335 void RevokeGalleryByPrefId(MediaGalleryPrefId id
) {
336 PrefIdFsInfoMap::iterator gallery
= pref_id_map_
.find(id
);
337 if (gallery
== pref_id_map_
.end())
340 file_system_context_
->RevokeFileSystem(gallery
->second
.fsid
);
341 pref_id_map_
.erase(gallery
);
343 if (pref_id_map_
.empty()) {
349 // Indicate that the passed |contents| will reference the file system ids
352 void ReferenceFromWebContents(content::WebContents
* web_contents
) {
353 rph_refs_
.ReferenceFromWebContents(web_contents
);
357 typedef std::map
<MediaGalleryPrefId
, MediaFileSystemInfo
> PrefIdFsInfoMap
;
359 // Private destructor and friend declaration for ref counted implementation.
360 friend class base::RefCountedThreadSafe
<ExtensionGalleriesHost
>;
362 virtual ~ExtensionGalleriesHost() {
363 DCHECK(rph_refs_
.empty());
364 DCHECK(pref_id_map_
.empty());
367 void GetMediaFileSystemsForAttachedDevices(
368 const MediaStorageUtil::DeviceIdSet
* attached_devices
,
369 const MediaGalleryPrefIdSet
& galleries
,
370 const MediaGalleriesPrefInfoMap
& galleries_info
,
371 const MediaFileSystemsCallback
& callback
) {
372 std::vector
<MediaFileSystemInfo
> result
;
374 if (rph_refs_
.empty()) {
375 // We're actually in the middle of shutdown, and Filter...() lagging
376 // which can invoke this method interleaved in the destruction callback
377 // sequence and re-populate pref_id_map_.
378 callback
.Run(result
);
382 for (std::set
<MediaGalleryPrefId
>::const_iterator pref_id_it
=
384 pref_id_it
!= galleries
.end();
386 const MediaGalleryPrefId
& pref_id
= *pref_id_it
;
387 const MediaGalleryPrefInfo
& gallery_info
=
388 galleries_info
.find(pref_id
)->second
;
389 const std::string
& device_id
= gallery_info
.device_id
;
390 if (!ContainsKey(*attached_devices
, device_id
))
393 PrefIdFsInfoMap::const_iterator existing_info
=
394 pref_id_map_
.find(pref_id
);
395 if (existing_info
!= pref_id_map_
.end()) {
396 result
.push_back(existing_info
->second
);
400 base::FilePath path
= gallery_info
.AbsolutePath();
401 if (!MediaStorageUtil::CanCreateFileSystem(device_id
, path
))
404 std::string fs_name
= MediaFileSystemBackend::ConstructMountName(
405 profile_path_
, extension_id_
, pref_id
);
406 if (!file_system_context_
->RegisterFileSystem(device_id
, fs_name
, path
))
409 MediaFileSystemInfo
new_entry(
410 gallery_info
.GetGalleryDisplayName(),
411 file_system_context_
->GetRegisteredPath(fs_name
),
414 GetTransientIdForRemovableDeviceId(device_id
),
415 StorageInfo::IsRemovableDevice(device_id
),
416 StorageInfo::IsMediaDevice(device_id
));
417 result
.push_back(new_entry
);
418 pref_id_map_
[pref_id
] = new_entry
;
421 if (result
.size() == 0) {
426 DCHECK_EQ(pref_id_map_
.size(), result
.size());
427 callback
.Run(result
);
430 void RegisterAttachedMediaFileSystem(
431 const MediaStorageUtil::DeviceIdSet
* attached_device
,
432 const MediaGalleryPrefInfo
& gallery
,
433 const base::Callback
<void(base::File::Error result
)>& callback
) {
434 base::File::Error result
= base::File::FILE_ERROR_NOT_FOUND
;
436 // If rph_refs is empty then we're actually in the middle of shutdown, and
437 // Filter...() lagging which can invoke this method interleaved in the
438 // destruction callback sequence and re-populate pref_id_map_.
439 if (!attached_device
->empty() && !rph_refs_
.empty()) {
440 std::string fs_name
= MediaFileSystemBackend::ConstructMountName(
441 profile_path_
, extension_id_
, gallery
.pref_id
);
442 base::FilePath path
= gallery
.AbsolutePath();
443 const std::string
& device_id
= gallery
.device_id
;
445 if (ContainsKey(pref_id_map_
, gallery
.pref_id
)) {
446 result
= base::File::FILE_OK
;
447 } else if (MediaStorageUtil::CanCreateFileSystem(device_id
, path
) &&
448 file_system_context_
->RegisterFileSystem(device_id
, fs_name
,
450 result
= base::File::FILE_OK
;
451 pref_id_map_
[gallery
.pref_id
] = MediaFileSystemInfo(
452 gallery
.GetGalleryDisplayName(),
453 file_system_context_
->GetRegisteredPath(fs_name
),
456 GetTransientIdForRemovableDeviceId(device_id
),
457 StorageInfo::IsRemovableDevice(device_id
),
458 StorageInfo::IsMediaDevice(device_id
));
462 if (pref_id_map_
.empty()) {
466 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
467 base::Bind(callback
, result
));
470 std::string
GetTransientIdForRemovableDeviceId(const std::string
& device_id
) {
471 if (!StorageInfo::IsRemovableDevice(device_id
))
472 return std::string();
474 return StorageMonitor::GetInstance()->GetTransientIdForDeviceId(device_id
);
478 DCHECK(rph_refs_
.empty());
479 for (PrefIdFsInfoMap::const_iterator it
= pref_id_map_
.begin();
480 it
!= pref_id_map_
.end();
482 file_system_context_
->RevokeFileSystem(it
->second
.fsid
);
484 pref_id_map_
.clear();
486 no_references_callback_
.Run();
489 // MediaFileSystemRegistry owns |this| and |file_system_context_|, so it's
490 // safe to store a raw pointer.
491 MediaFileSystemContext
* file_system_context_
;
493 // Path for the active profile.
494 const base::FilePath profile_path_
;
496 // Id of the extension this host belongs to.
497 const std::string extension_id_
;
499 // A callback to call when the last WebContents reference goes away.
500 base::Closure no_references_callback_
;
502 // A map from the gallery preferences id to the file system information.
503 PrefIdFsInfoMap pref_id_map_
;
505 // The set of render processes and web contents that may have references to
506 // the file system ids this instance manages.
507 RPHReferenceManager rph_refs_
;
509 DISALLOW_COPY_AND_ASSIGN(ExtensionGalleriesHost
);
516 void MediaFileSystemRegistry::GetMediaFileSystemsForExtension(
517 content::WebContents
* contents
,
518 const extensions::Extension
* extension
,
519 const MediaFileSystemsCallback
& callback
) {
520 // TODO(tommycli): Change to DCHECK after fixing http://crbug.com/374330.
521 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
523 Profile
* profile
= Profile::FromBrowserContext(contents
->GetBrowserContext());
524 MediaGalleriesPreferences
* preferences
= GetPreferences(profile
);
525 MediaGalleryPrefIdSet galleries
=
526 preferences
->GalleriesForExtension(*extension
);
528 if (galleries
.empty()) {
529 callback
.Run(std::vector
<MediaFileSystemInfo
>());
533 ExtensionGalleriesHost
* extension_host
=
534 GetExtensionGalleryHost(profile
, preferences
, extension
->id());
536 // This must come before the GetMediaFileSystems call to make sure the
537 // contents of the context is referenced before the filesystems are retrieved.
538 extension_host
->ReferenceFromWebContents(contents
);
540 extension_host
->GetMediaFileSystems(galleries
, preferences
->known_galleries(),
544 void MediaFileSystemRegistry::RegisterMediaFileSystemForExtension(
545 content::WebContents
* contents
,
546 const extensions::Extension
* extension
,
547 MediaGalleryPrefId pref_id
,
548 const base::Callback
<void(base::File::Error result
)>& callback
) {
549 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
550 DCHECK_NE(kInvalidMediaGalleryPrefId
, pref_id
);
552 Profile
* profile
= Profile::FromBrowserContext(contents
->GetBrowserContext());
553 MediaGalleriesPreferences
* preferences
= GetPreferences(profile
);
554 MediaGalleriesPrefInfoMap::const_iterator gallery
=
555 preferences
->known_galleries().find(pref_id
);
556 MediaGalleryPrefIdSet permitted_galleries
=
557 preferences
->GalleriesForExtension(*extension
);
559 if (gallery
== preferences
->known_galleries().end() ||
560 !ContainsKey(permitted_galleries
, pref_id
)) {
561 BrowserThread::PostTask(
562 BrowserThread::IO
, FROM_HERE
,
563 base::Bind(callback
, base::File::FILE_ERROR_NOT_FOUND
));
567 ExtensionGalleriesHost
* extension_host
=
568 GetExtensionGalleryHost(profile
, preferences
, extension
->id());
570 // This must come before the GetMediaFileSystems call to make sure the
571 // contents of the context is referenced before the filesystems are retrieved.
572 extension_host
->ReferenceFromWebContents(contents
);
574 extension_host
->RegisterMediaFileSystem(gallery
->second
, callback
);
577 MediaGalleriesPreferences
* MediaFileSystemRegistry::GetPreferences(
579 // Create an empty ExtensionHostMap for this profile on first initialization.
580 if (!ContainsKey(extension_hosts_map_
, profile
)) {
581 extension_hosts_map_
[profile
] = ExtensionHostMap();
582 DCHECK(!ContainsKey(profile_subscription_map_
, profile
));
583 profile_subscription_map_
.set(
585 ShutdownNotifierFactory::GetInstance()->Get(profile
)->Subscribe(
586 base::Bind(&MediaFileSystemRegistry::OnProfileShutdown
,
587 base::Unretained(this), profile
)));
588 media_galleries::UsageCount(media_galleries::PROFILES_WITH_USAGE
);
591 return MediaGalleriesPreferencesFactory::GetForProfile(profile
);
594 MediaScanManager
* MediaFileSystemRegistry::media_scan_manager() {
595 if (!media_scan_manager_
)
596 media_scan_manager_
.reset(new MediaScanManager
);
597 return media_scan_manager_
.get();
600 GalleryWatchManager
* MediaFileSystemRegistry::gallery_watch_manager() {
601 if (!gallery_watch_manager_
)
602 gallery_watch_manager_
.reset(new GalleryWatchManager
);
603 return gallery_watch_manager_
.get();
606 void MediaFileSystemRegistry::OnRemovableStorageDetached(
607 const StorageInfo
& info
) {
608 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
610 // Since revoking a gallery in the ExtensionGalleriesHost may cause it
611 // to be removed from the map and therefore invalidate any iterator pointing
612 // to it, this code first copies all the invalid gallery ids and the
613 // extension hosts in which they may appear (per profile) and revoked it in
615 std::vector
<InvalidatedGalleriesInfo
> invalid_galleries_info
;
617 for (ExtensionGalleriesHostMap::iterator profile_it
=
618 extension_hosts_map_
.begin();
619 profile_it
!= extension_hosts_map_
.end();
621 MediaGalleriesPreferences
* preferences
= GetPreferences(profile_it
->first
);
622 // If |preferences| is not yet initialized, it won't contain any galleries.
623 if (!preferences
->IsInitialized())
626 InvalidatedGalleriesInfo invalid_galleries_in_profile
;
627 invalid_galleries_in_profile
.pref_ids
=
628 preferences
->LookUpGalleriesByDeviceId(info
.device_id());
630 for (ExtensionHostMap::const_iterator extension_host_it
=
631 profile_it
->second
.begin();
632 extension_host_it
!= profile_it
->second
.end();
633 ++extension_host_it
) {
634 invalid_galleries_in_profile
.extension_hosts
.insert(
635 extension_host_it
->second
.get());
638 invalid_galleries_info
.push_back(invalid_galleries_in_profile
);
641 for (size_t i
= 0; i
< invalid_galleries_info
.size(); i
++) {
642 for (std::set
<ExtensionGalleriesHost
*>::const_iterator extension_host_it
=
643 invalid_galleries_info
[i
].extension_hosts
.begin();
644 extension_host_it
!= invalid_galleries_info
[i
].extension_hosts
.end();
645 ++extension_host_it
) {
646 for (std::set
<MediaGalleryPrefId
>::const_iterator pref_id_it
=
647 invalid_galleries_info
[i
].pref_ids
.begin();
648 pref_id_it
!= invalid_galleries_info
[i
].pref_ids
.end();
650 (*extension_host_it
)->RevokeGalleryByPrefId(*pref_id_it
);
660 class MediaFileSystemRegistry::MediaFileSystemContextImpl
661 : public MediaFileSystemContext
{
663 MediaFileSystemContextImpl() {}
664 ~MediaFileSystemContextImpl() override
{}
666 bool RegisterFileSystem(const std::string
& device_id
,
667 const std::string
& fs_name
,
668 const base::FilePath
& path
) override
{
669 if (StorageInfo::IsMassStorageDevice(device_id
)) {
670 return RegisterFileSystemForMassStorage(device_id
, fs_name
, path
);
672 return RegisterFileSystemForMTPDevice(device_id
, fs_name
, path
);
676 void RevokeFileSystem(const std::string
& fs_name
) override
{
677 ImportedMediaGalleryRegistry
* imported_registry
=
678 ImportedMediaGalleryRegistry::GetInstance();
679 if (imported_registry
->RevokeImportedFilesystemOnUIThread(fs_name
))
682 ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(fs_name
);
684 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
, base::Bind(
685 &MTPDeviceMapService::RevokeMTPFileSystem
,
686 base::Unretained(MTPDeviceMapService::GetInstance()),
690 base::FilePath
GetRegisteredPath(const std::string
& fs_name
) const override
{
691 base::FilePath result
;
692 if (!ExternalMountPoints::GetSystemInstance()->GetRegisteredPath(fs_name
,
694 return base::FilePath();
700 // Registers and returns the file system id for the mass storage device
701 // specified by |device_id| and |path|.
702 bool RegisterFileSystemForMassStorage(const std::string
& device_id
,
703 const std::string
& fs_name
,
704 const base::FilePath
& path
) {
705 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
706 DCHECK(StorageInfo::IsMassStorageDevice(device_id
));
708 // Sanity checks for |path|.
709 CHECK(path
.IsAbsolute());
710 CHECK(!path
.ReferencesParent());
712 // TODO(gbillock): refactor ImportedMediaGalleryRegistry to delegate this
713 // call tree, probably by having it figure out by device id what
714 // registration is needed, or having per-device-type handlers at the
715 // next higher level.
717 if (StorageInfo::IsITunesDevice(device_id
)) {
718 ImportedMediaGalleryRegistry
* registry
=
719 ImportedMediaGalleryRegistry::GetInstance();
720 result
= registry
->RegisterITunesFilesystemOnUIThread(fs_name
, path
);
721 } else if (StorageInfo::IsPicasaDevice(device_id
)) {
722 ImportedMediaGalleryRegistry
* registry
=
723 ImportedMediaGalleryRegistry::GetInstance();
724 result
= registry
->RegisterPicasaFilesystemOnUIThread(fs_name
, path
);
725 } else if (StorageInfo::IsIPhotoDevice(device_id
)) {
726 ImportedMediaGalleryRegistry
* registry
=
727 ImportedMediaGalleryRegistry::GetInstance();
728 result
= registry
->RegisterIPhotoFilesystemOnUIThread(fs_name
, path
);
730 result
= ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
732 storage::kFileSystemTypeNativeMedia
,
733 storage::FileSystemMountOption(),
739 bool RegisterFileSystemForMTPDevice(const std::string
& device_id
,
740 const std::string fs_name
,
741 const base::FilePath
& path
) {
742 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
743 DCHECK(!StorageInfo::IsMassStorageDevice(device_id
));
745 // Sanity checks for |path|.
746 CHECK(MediaStorageUtil::CanCreateFileSystem(device_id
, path
));
747 bool result
= ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
749 storage::kFileSystemTypeDeviceMedia
,
750 storage::FileSystemMountOption(),
753 BrowserThread::PostTask(
754 BrowserThread::IO
, FROM_HERE
,
755 base::Bind(&MTPDeviceMapService::RegisterMTPFileSystem
,
756 base::Unretained(MTPDeviceMapService::GetInstance()),
757 path
.value(), fs_name
, true /* read only */));
761 DISALLOW_COPY_AND_ASSIGN(MediaFileSystemContextImpl
);
764 // Constructor in 'private' section because depends on private class definition.
765 MediaFileSystemRegistry::MediaFileSystemRegistry()
766 : file_system_context_(new MediaFileSystemContextImpl
) {
767 StorageMonitor::GetInstance()->AddObserver(this);
770 MediaFileSystemRegistry::~MediaFileSystemRegistry() {
771 // TODO(gbillock): This is needed because the unit test uses the
772 // g_browser_process registry. We should create one in the unit test,
773 // and then can remove this.
774 if (StorageMonitor::GetInstance())
775 StorageMonitor::GetInstance()->RemoveObserver(this);
778 void MediaFileSystemRegistry::OnPermissionRemoved(
779 MediaGalleriesPreferences
* prefs
,
780 const std::string
& extension_id
,
781 MediaGalleryPrefId pref_id
) {
782 Profile
* profile
= prefs
->profile();
783 ExtensionGalleriesHostMap::const_iterator host_map_it
=
784 extension_hosts_map_
.find(profile
);
785 DCHECK(host_map_it
!= extension_hosts_map_
.end());
786 const ExtensionHostMap
& extension_host_map
= host_map_it
->second
;
787 ExtensionHostMap::const_iterator gallery_host_it
=
788 extension_host_map
.find(extension_id
);
789 if (gallery_host_it
== extension_host_map
.end())
791 gallery_host_it
->second
->RevokeGalleryByPrefId(pref_id
);
794 void MediaFileSystemRegistry::OnGalleryRemoved(
795 MediaGalleriesPreferences
* prefs
,
796 MediaGalleryPrefId pref_id
) {
797 Profile
* profile
= prefs
->profile();
798 // Get the Extensions, MediaGalleriesPreferences and ExtensionHostMap for
800 const extensions::ExtensionRegistry
* extension_registry
=
801 extensions::ExtensionRegistry::Get(profile
);
802 ExtensionGalleriesHostMap::const_iterator host_map_it
=
803 extension_hosts_map_
.find(profile
);
804 DCHECK(host_map_it
!= extension_hosts_map_
.end());
805 const ExtensionHostMap
& extension_host_map
= host_map_it
->second
;
807 // Go through ExtensionHosts, and remove indicated gallery, if any.
808 // RevokeGalleryByPrefId() may end up deleting from |extension_host_map| and
809 // even delete |extension_host_map| altogether. So do this in two loops to
810 // avoid using an invalidated iterator or deleted map.
811 std::vector
<const extensions::Extension
*> extensions
;
812 for (ExtensionHostMap::const_iterator it
= extension_host_map
.begin();
813 it
!= extension_host_map
.end();
815 extensions
.push_back(
816 extension_registry
->enabled_extensions().GetByID(it
->first
));
818 for (size_t i
= 0; i
< extensions
.size(); ++i
) {
819 if (!ContainsKey(extension_hosts_map_
, profile
))
821 ExtensionHostMap::const_iterator gallery_host_it
=
822 extension_host_map
.find(extensions
[i
]->id());
823 if (gallery_host_it
== extension_host_map
.end())
825 gallery_host_it
->second
->RevokeGalleryByPrefId(pref_id
);
829 ExtensionGalleriesHost
* MediaFileSystemRegistry::GetExtensionGalleryHost(
831 MediaGalleriesPreferences
* preferences
,
832 const std::string
& extension_id
) {
833 ExtensionGalleriesHostMap::iterator extension_hosts
=
834 extension_hosts_map_
.find(profile
);
835 // GetPreferences(), which had to be called because preferences is an
836 // argument, ensures that profile is in the map.
837 DCHECK(extension_hosts
!= extension_hosts_map_
.end());
838 if (extension_hosts
->second
.empty())
839 preferences
->AddGalleryChangeObserver(this);
841 ExtensionGalleriesHost
* result
= extension_hosts
->second
[extension_id
].get();
843 result
= new ExtensionGalleriesHost(
844 file_system_context_
.get(),
847 base::Bind(&MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty
,
848 base::Unretained(this),
851 extension_hosts_map_
[profile
][extension_id
] = result
;
856 void MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty(
857 Profile
* profile
, const std::string
& extension_id
) {
858 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
860 ExtensionGalleriesHostMap::iterator extension_hosts
=
861 extension_hosts_map_
.find(profile
);
862 DCHECK(extension_hosts
!= extension_hosts_map_
.end());
863 ExtensionHostMap::size_type erase_count
=
864 extension_hosts
->second
.erase(extension_id
);
865 DCHECK_EQ(1U, erase_count
);
866 if (extension_hosts
->second
.empty()) {
867 // When a profile has no ExtensionGalleriesHosts left, remove the
868 // matching gallery-change-watcher since it is no longer needed. Leave the
869 // |extension_hosts| entry alone, since it indicates the profile has been
871 MediaGalleriesPreferences
* preferences
= GetPreferences(profile
);
872 preferences
->RemoveGalleryChangeObserver(this);
876 void MediaFileSystemRegistry::OnProfileShutdown(Profile
* profile
) {
877 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
879 auto extension_hosts_it
= extension_hosts_map_
.find(profile
);
880 DCHECK(extension_hosts_it
!= extension_hosts_map_
.end());
881 extension_hosts_map_
.erase(extension_hosts_it
);
883 auto profile_subscription_it
= profile_subscription_map_
.find(profile
);
884 DCHECK(profile_subscription_it
!= profile_subscription_map_
.end());
885 profile_subscription_map_
.erase(profile_subscription_it
);