Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / media_galleries / media_file_system_registry.cc
blobdbec912bf44130ec99ddb833a50c8d42b1702665
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/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;
53 namespace {
55 class ShutdownNotifierFactory
56 : public BrowserContextKeyedServiceShutdownNotifierFactory {
57 public:
58 static ShutdownNotifierFactory* GetInstance() {
59 return Singleton<ShutdownNotifierFactory>::get();
62 private:
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 {
84 public:
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);
101 private:
102 class RPHWebContentsObserver : public content::WebContentsObserver {
103 public:
104 RPHWebContentsObserver(RPHReferenceManager* manager,
105 WebContents* web_contents);
107 private:
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 {
117 public:
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;
127 private:
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() {
155 Reset();
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;
165 } else {
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),
176 manager_(manager) {
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)
186 return;
188 manager_->OnWebContentsDestroyedOrNavigated(web_contents());
191 RPHReferenceManager::RPHObserver::RPHObserver(
192 RPHReferenceManager* manager, RenderProcessHost* host)
193 : manager_(manager),
194 host_(host) {
195 host->AddObserver(this);
198 RPHReferenceManager::RPHObserver::~RPHObserver() {
199 STLDeleteValues(&observed_web_contentses_);
200 if (host_)
201 host_->RemoveObserver(this);
204 void RPHReferenceManager::RPHObserver::AddWebContentsObserver(
205 WebContents* web_contents) {
206 if (ContainsKey(observed_web_contentses_, web_contents))
207 return;
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) {
225 host_ = NULL;
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
234 // renderer crashes.
235 if (rph_info == observer_map_.end()) {
236 NOTREACHED();
237 return;
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);
256 } // namespace
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,
263 bool removable,
264 bool media_device)
265 : name(fs_name),
266 path(fs_path),
267 fsid(filesystem_id),
268 pref_id(pref_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> {
282 public:
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();
312 ++id) {
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())
338 return;
340 file_system_context_->RevokeFileSystem(gallery->second.fsid);
341 pref_id_map_.erase(gallery);
343 if (pref_id_map_.empty()) {
344 rph_refs_.Reset();
345 CleanUp();
349 // Indicate that the passed |contents| will reference the file system ids
350 // created
351 // by this class.
352 void ReferenceFromWebContents(content::WebContents* web_contents) {
353 rph_refs_.ReferenceFromWebContents(web_contents);
356 private:
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);
379 return;
382 for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it =
383 galleries.begin();
384 pref_id_it != galleries.end();
385 ++pref_id_it) {
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))
391 continue;
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);
397 continue;
400 base::FilePath path = gallery_info.AbsolutePath();
401 if (!MediaStorageUtil::CanCreateFileSystem(device_id, path))
402 continue;
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))
407 continue;
409 MediaFileSystemInfo new_entry(
410 gallery_info.GetGalleryDisplayName(),
411 file_system_context_->GetRegisteredPath(fs_name),
412 fs_name,
413 pref_id,
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) {
422 rph_refs_.Reset();
423 CleanUp();
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,
449 path)) {
450 result = base::File::FILE_OK;
451 pref_id_map_[gallery.pref_id] = MediaFileSystemInfo(
452 gallery.GetGalleryDisplayName(),
453 file_system_context_->GetRegisteredPath(fs_name),
454 fs_name,
455 gallery.pref_id,
456 GetTransientIdForRemovableDeviceId(device_id),
457 StorageInfo::IsRemovableDevice(device_id),
458 StorageInfo::IsMediaDevice(device_id));
462 if (pref_id_map_.empty()) {
463 rph_refs_.Reset();
464 CleanUp();
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);
477 void CleanUp() {
478 DCHECK(rph_refs_.empty());
479 for (PrefIdFsInfoMap::const_iterator it = pref_id_map_.begin();
480 it != pref_id_map_.end();
481 ++it) {
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);
512 /******************
513 * Public methods
514 ******************/
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>());
530 return;
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(),
541 callback);
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));
564 return;
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(
578 Profile* profile) {
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(
584 profile,
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
614 // a second step.
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();
620 ++profile_it) {
621 MediaGalleriesPreferences* preferences = GetPreferences(profile_it->first);
622 // If |preferences| is not yet initialized, it won't contain any galleries.
623 if (!preferences->IsInitialized())
624 continue;
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();
649 ++pref_id_it) {
650 (*extension_host_it)->RevokeGalleryByPrefId(*pref_id_it);
656 /******************
657 * Private methods
658 ******************/
660 class MediaFileSystemRegistry::MediaFileSystemContextImpl
661 : public MediaFileSystemContext {
662 public:
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);
671 } else {
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))
680 return;
682 ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(fs_name);
684 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
685 &MTPDeviceMapService::RevokeMTPFileSystem,
686 base::Unretained(MTPDeviceMapService::GetInstance()),
687 fs_name));
690 base::FilePath GetRegisteredPath(const std::string& fs_name) const override {
691 base::FilePath result;
692 if (!ExternalMountPoints::GetSystemInstance()->GetRegisteredPath(fs_name,
693 &result)) {
694 return base::FilePath();
696 return result;
699 private:
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.
716 bool result = false;
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);
729 } else {
730 result = ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
731 fs_name,
732 storage::kFileSystemTypeNativeMedia,
733 storage::FileSystemMountOption(),
734 path);
736 return result;
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(
748 fs_name,
749 storage::kFileSystemTypeDeviceMedia,
750 storage::FileSystemMountOption(),
751 path);
752 CHECK(result);
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 */));
758 return result;
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())
790 return;
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
799 // |profile|.
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();
814 ++it) {
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))
820 break;
821 ExtensionHostMap::const_iterator gallery_host_it =
822 extension_host_map.find(extensions[i]->id());
823 if (gallery_host_it == extension_host_map.end())
824 continue;
825 gallery_host_it->second->RevokeGalleryByPrefId(pref_id);
829 ExtensionGalleriesHost* MediaFileSystemRegistry::GetExtensionGalleryHost(
830 Profile* profile,
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();
842 if (!result) {
843 result = new ExtensionGalleriesHost(
844 file_system_context_.get(),
845 profile->GetPath(),
846 extension_id,
847 base::Bind(&MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty,
848 base::Unretained(this),
849 profile,
850 extension_id));
851 extension_hosts_map_[profile][extension_id] = result;
853 return 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
870 // previously used.
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);