Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / media_galleries / media_file_system_registry.cc
blobcd4cd13beed28661e4fd1cb0733d3f915c415575
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/extensions/extension_service.h"
16 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
17 #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h"
18 #include "chrome/browser/media_galleries/gallery_watch_manager.h"
19 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
20 #include "chrome/browser/media_galleries/media_file_system_context.h"
21 #include "chrome/browser/media_galleries/media_galleries_dialog_controller.h"
22 #include "chrome/browser/media_galleries/media_galleries_histograms.h"
23 #include "chrome/browser/media_galleries/media_galleries_preferences_factory.h"
24 #include "chrome/browser/media_galleries/media_scan_manager.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/common/chrome_paths.h"
27 #include "chrome/common/extensions/extension_constants.h"
28 #include "chrome/common/pref_names.h"
29 #include "components/storage_monitor/media_storage_util.h"
30 #include "components/storage_monitor/storage_monitor.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "content/public/browser/navigation_details.h"
33 #include "content/public/browser/render_process_host.h"
34 #include "content/public/browser/render_process_host_observer.h"
35 #include "content/public/browser/render_view_host.h"
36 #include "content/public/browser/web_contents.h"
37 #include "content/public/browser/web_contents_observer.h"
38 #include "extensions/browser/extension_system.h"
39 #include "extensions/common/extension.h"
40 #include "extensions/common/extension_set.h"
41 #include "storage/browser/fileapi/external_mount_points.h"
42 #include "storage/common/fileapi/file_system_mount_option.h"
43 #include "storage/common/fileapi/file_system_types.h"
45 using content::BrowserThread;
46 using content::NavigationController;
47 using content::RenderProcessHost;
48 using content::WebContents;
49 using storage::ExternalMountPoints;
50 using storage_monitor::MediaStorageUtil;
51 using storage_monitor::StorageInfo;
52 using storage_monitor::StorageMonitor;
54 namespace {
56 struct InvalidatedGalleriesInfo {
57 std::set<ExtensionGalleriesHost*> extension_hosts;
58 std::set<MediaGalleryPrefId> pref_ids;
61 // Tracks the liveness of multiple RenderProcessHosts that the caller is
62 // interested in. Once all of the RPHs have closed or been destroyed a call
63 // back informs the caller.
64 class RPHReferenceManager {
65 public:
66 // |no_references_callback| is called when the last RenderViewHost reference
67 // goes away. RenderViewHost references are added through ReferenceFromRVH().
68 explicit RPHReferenceManager(const base::Closure& no_references_callback);
69 virtual ~RPHReferenceManager();
71 // Remove all references, but don't call |no_references_callback|.
72 void Reset() { STLDeleteValues(&observer_map_); }
74 // Returns true if there are no references;
75 bool empty() const { return observer_map_.empty(); }
77 // Adds a reference to the passed |rvh|. Calling this multiple times with
78 // the same |rvh| is a no-op.
79 void ReferenceFromRVH(const content::RenderViewHost* rvh);
81 private:
82 class RPHWebContentsObserver : public content::WebContentsObserver {
83 public:
84 RPHWebContentsObserver(RPHReferenceManager* manager,
85 WebContents* web_contents);
87 private:
88 // content::WebContentsObserver
89 virtual void WebContentsDestroyed() override;
90 virtual void NavigationEntryCommitted(
91 const content::LoadCommittedDetails& load_details) override;
93 RPHReferenceManager* manager_;
96 class RPHObserver : public content::RenderProcessHostObserver {
97 public:
98 RPHObserver(RPHReferenceManager* manager, RenderProcessHost* host);
99 virtual ~RPHObserver();
101 void AddWebContentsObserver(WebContents* web_contents);
102 void RemoveWebContentsObserver(WebContents* web_contents);
103 bool HasWebContentsObservers() {
104 return observed_web_contentses_.size() > 0;
107 private:
108 virtual void RenderProcessHostDestroyed(RenderProcessHost* host) override;
110 RPHReferenceManager* manager_;
111 RenderProcessHost* host_;
112 typedef std::map<WebContents*, RPHWebContentsObserver*> WCObserverMap;
113 WCObserverMap observed_web_contentses_;
115 typedef std::map<const RenderProcessHost*, RPHObserver*> RPHObserverMap;
117 // Handlers for observed events.
118 void OnRenderProcessHostDestroyed(RenderProcessHost* rph);
119 void OnWebContentsDestroyedOrNavigated(WebContents* contents);
121 // A callback to call when the last RVH reference goes away.
122 base::Closure no_references_callback_;
124 // The set of render processes and web contents that may have references to
125 // the file system ids this instance manages.
126 RPHObserverMap observer_map_;
129 RPHReferenceManager::RPHReferenceManager(
130 const base::Closure& no_references_callback)
131 : no_references_callback_(no_references_callback) {
134 RPHReferenceManager::~RPHReferenceManager() {
135 Reset();
138 void RPHReferenceManager::ReferenceFromRVH(const content::RenderViewHost* rvh) {
139 WebContents* contents = WebContents::FromRenderViewHost(rvh);
140 RenderProcessHost* rph = contents->GetRenderProcessHost();
141 RPHObserver* state = NULL;
142 if (!ContainsKey(observer_map_, rph)) {
143 state = new RPHObserver(this, rph);
144 observer_map_[rph] = state;
145 } else {
146 state = observer_map_[rph];
149 state->AddWebContentsObserver(contents);
152 RPHReferenceManager::RPHWebContentsObserver::RPHWebContentsObserver(
153 RPHReferenceManager* manager,
154 WebContents* web_contents)
155 : content::WebContentsObserver(web_contents),
156 manager_(manager) {
159 void RPHReferenceManager::RPHWebContentsObserver::WebContentsDestroyed() {
160 manager_->OnWebContentsDestroyedOrNavigated(web_contents());
163 void RPHReferenceManager::RPHWebContentsObserver::NavigationEntryCommitted(
164 const content::LoadCommittedDetails& load_details) {
165 if (load_details.is_in_page)
166 return;
168 manager_->OnWebContentsDestroyedOrNavigated(web_contents());
171 RPHReferenceManager::RPHObserver::RPHObserver(
172 RPHReferenceManager* manager, RenderProcessHost* host)
173 : manager_(manager),
174 host_(host) {
175 host->AddObserver(this);
178 RPHReferenceManager::RPHObserver::~RPHObserver() {
179 STLDeleteValues(&observed_web_contentses_);
180 if (host_)
181 host_->RemoveObserver(this);
184 void RPHReferenceManager::RPHObserver::AddWebContentsObserver(
185 WebContents* web_contents) {
186 if (ContainsKey(observed_web_contentses_, web_contents))
187 return;
189 RPHWebContentsObserver* observer =
190 new RPHWebContentsObserver(manager_, web_contents);
191 observed_web_contentses_[web_contents] = observer;
194 void RPHReferenceManager::RPHObserver::RemoveWebContentsObserver(
195 WebContents* web_contents) {
196 WCObserverMap::iterator wco_iter =
197 observed_web_contentses_.find(web_contents);
198 DCHECK(wco_iter != observed_web_contentses_.end());
199 delete wco_iter->second;
200 observed_web_contentses_.erase(wco_iter);
203 void RPHReferenceManager::RPHObserver::RenderProcessHostDestroyed(
204 RenderProcessHost* host) {
205 host_ = NULL;
206 manager_->OnRenderProcessHostDestroyed(host);
209 void RPHReferenceManager::OnRenderProcessHostDestroyed(
210 RenderProcessHost* rph) {
211 RPHObserverMap::iterator rph_info = observer_map_.find(rph);
212 // This could be a potential problem if the RPH is navigated to a page on the
213 // same renderer (triggering OnWebContentsDestroyedOrNavigated()) and then the
214 // renderer crashes.
215 if (rph_info == observer_map_.end()) {
216 NOTREACHED();
217 return;
219 delete rph_info->second;
220 observer_map_.erase(rph_info);
221 if (observer_map_.empty())
222 no_references_callback_.Run();
225 void RPHReferenceManager::OnWebContentsDestroyedOrNavigated(
226 WebContents* contents) {
227 RenderProcessHost* rph = contents->GetRenderProcessHost();
228 RPHObserverMap::iterator rph_info = observer_map_.find(rph);
229 DCHECK(rph_info != observer_map_.end());
231 rph_info->second->RemoveWebContentsObserver(contents);
232 if (!rph_info->second->HasWebContentsObservers())
233 OnRenderProcessHostDestroyed(rph);
236 } // namespace
238 MediaFileSystemInfo::MediaFileSystemInfo(const base::string16& fs_name,
239 const base::FilePath& fs_path,
240 const std::string& filesystem_id,
241 MediaGalleryPrefId pref_id,
242 const std::string& transient_device_id,
243 bool removable,
244 bool media_device)
245 : name(fs_name),
246 path(fs_path),
247 fsid(filesystem_id),
248 pref_id(pref_id),
249 transient_device_id(transient_device_id),
250 removable(removable),
251 media_device(media_device) {
254 MediaFileSystemInfo::MediaFileSystemInfo() {}
255 MediaFileSystemInfo::~MediaFileSystemInfo() {}
257 // The main owner of this class is
258 // |MediaFileSystemRegistry::extension_hosts_map_|, but a callback may
259 // temporarily hold a reference.
260 class ExtensionGalleriesHost
261 : public base::RefCountedThreadSafe<ExtensionGalleriesHost> {
262 public:
263 // |no_references_callback| is called when the last RenderViewHost reference
264 // goes away. RenderViewHost references are added through ReferenceFromRVH().
265 ExtensionGalleriesHost(MediaFileSystemContext* file_system_context,
266 const base::FilePath& profile_path,
267 const std::string& extension_id,
268 const base::Closure& no_references_callback)
269 : file_system_context_(file_system_context),
270 profile_path_(profile_path),
271 extension_id_(extension_id),
272 no_references_callback_(no_references_callback),
273 rph_refs_(base::Bind(&ExtensionGalleriesHost::CleanUp,
274 base::Unretained(this))) {
277 // For each gallery in the list of permitted |galleries|, checks if the
278 // device is attached and if so looks up or creates a file system name and
279 // passes the information needed for the renderer to create those file
280 // system objects to the |callback|.
281 void GetMediaFileSystems(const MediaGalleryPrefIdSet& galleries,
282 const MediaGalleriesPrefInfoMap& galleries_info,
283 const MediaFileSystemsCallback& callback) {
284 DCHECK_CURRENTLY_ON(BrowserThread::UI);
286 // Extract all the device ids so we can make sure they are attached.
287 MediaStorageUtil::DeviceIdSet* device_ids =
288 new MediaStorageUtil::DeviceIdSet;
289 for (std::set<MediaGalleryPrefId>::const_iterator id = galleries.begin();
290 id != galleries.end();
291 ++id) {
292 device_ids->insert(galleries_info.find(*id)->second.device_id);
294 MediaStorageUtil::FilterAttachedDevices(device_ids, base::Bind(
295 &ExtensionGalleriesHost::GetMediaFileSystemsForAttachedDevices, this,
296 base::Owned(device_ids), galleries, galleries_info, callback));
299 // Checks if |gallery| is attached and if so, registers the file system and
300 // then calls |callback| with the result.
301 void RegisterMediaFileSystem(
302 const MediaGalleryPrefInfo& gallery,
303 const base::Callback<void(base::File::Error result)>& callback) {
304 // Extract all the device ids so we can make sure they are attached.
305 MediaStorageUtil::DeviceIdSet* device_ids =
306 new MediaStorageUtil::DeviceIdSet;
307 device_ids->insert(gallery.device_id);
308 MediaStorageUtil::FilterAttachedDevices(device_ids, base::Bind(
309 &ExtensionGalleriesHost::RegisterAttachedMediaFileSystem, this,
310 base::Owned(device_ids), gallery, callback));
313 // Revoke the file system for |id| if this extension has created one for |id|.
314 void RevokeGalleryByPrefId(MediaGalleryPrefId id) {
315 PrefIdFsInfoMap::iterator gallery = pref_id_map_.find(id);
316 if (gallery == pref_id_map_.end())
317 return;
319 file_system_context_->RevokeFileSystem(gallery->second.fsid);
320 pref_id_map_.erase(gallery);
322 if (pref_id_map_.empty()) {
323 rph_refs_.Reset();
324 CleanUp();
328 // Indicate that the passed |rvh| will reference the file system ids created
329 // by this class.
330 void ReferenceFromRVH(const content::RenderViewHost* rvh) {
331 rph_refs_.ReferenceFromRVH(rvh);
334 private:
335 typedef std::map<MediaGalleryPrefId, MediaFileSystemInfo> PrefIdFsInfoMap;
337 // Private destructor and friend declaration for ref counted implementation.
338 friend class base::RefCountedThreadSafe<ExtensionGalleriesHost>;
340 virtual ~ExtensionGalleriesHost() {
341 DCHECK(rph_refs_.empty());
342 DCHECK(pref_id_map_.empty());
345 void GetMediaFileSystemsForAttachedDevices(
346 const MediaStorageUtil::DeviceIdSet* attached_devices,
347 const MediaGalleryPrefIdSet& galleries,
348 const MediaGalleriesPrefInfoMap& galleries_info,
349 const MediaFileSystemsCallback& callback) {
350 std::vector<MediaFileSystemInfo> result;
352 if (rph_refs_.empty()) {
353 // We're actually in the middle of shutdown, and Filter...() lagging
354 // which can invoke this method interleaved in the destruction callback
355 // sequence and re-populate pref_id_map_.
356 callback.Run(result);
357 return;
360 for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it =
361 galleries.begin();
362 pref_id_it != galleries.end();
363 ++pref_id_it) {
364 const MediaGalleryPrefId& pref_id = *pref_id_it;
365 const MediaGalleryPrefInfo& gallery_info =
366 galleries_info.find(pref_id)->second;
367 const std::string& device_id = gallery_info.device_id;
368 if (!ContainsKey(*attached_devices, device_id))
369 continue;
371 PrefIdFsInfoMap::const_iterator existing_info =
372 pref_id_map_.find(pref_id);
373 if (existing_info != pref_id_map_.end()) {
374 result.push_back(existing_info->second);
375 continue;
378 base::FilePath path = gallery_info.AbsolutePath();
379 if (!MediaStorageUtil::CanCreateFileSystem(device_id, path))
380 continue;
382 std::string fs_name = MediaFileSystemBackend::ConstructMountName(
383 profile_path_, extension_id_, pref_id);
384 if (!file_system_context_->RegisterFileSystem(device_id, fs_name, path))
385 continue;
387 MediaFileSystemInfo new_entry(
388 gallery_info.GetGalleryDisplayName(),
389 file_system_context_->GetRegisteredPath(fs_name),
390 fs_name,
391 pref_id,
392 GetTransientIdForRemovableDeviceId(device_id),
393 StorageInfo::IsRemovableDevice(device_id),
394 StorageInfo::IsMediaDevice(device_id));
395 result.push_back(new_entry);
396 pref_id_map_[pref_id] = new_entry;
399 if (result.size() == 0) {
400 rph_refs_.Reset();
401 CleanUp();
404 DCHECK_EQ(pref_id_map_.size(), result.size());
405 callback.Run(result);
408 void RegisterAttachedMediaFileSystem(
409 const MediaStorageUtil::DeviceIdSet* attached_device,
410 const MediaGalleryPrefInfo& gallery,
411 const base::Callback<void(base::File::Error result)>& callback) {
412 base::File::Error result = base::File::FILE_ERROR_NOT_FOUND;
414 // If rph_refs is empty then we're actually in the middle of shutdown, and
415 // Filter...() lagging which can invoke this method interleaved in the
416 // destruction callback sequence and re-populate pref_id_map_.
417 if (!attached_device->empty() && !rph_refs_.empty()) {
418 std::string fs_name = MediaFileSystemBackend::ConstructMountName(
419 profile_path_, extension_id_, gallery.pref_id);
420 base::FilePath path = gallery.AbsolutePath();
421 const std::string& device_id = gallery.device_id;
423 if (ContainsKey(pref_id_map_, gallery.pref_id)) {
424 result = base::File::FILE_OK;
425 } else if (MediaStorageUtil::CanCreateFileSystem(device_id, path) &&
426 file_system_context_->RegisterFileSystem(device_id, fs_name,
427 path)) {
428 result = base::File::FILE_OK;
429 pref_id_map_[gallery.pref_id] = MediaFileSystemInfo(
430 gallery.GetGalleryDisplayName(),
431 file_system_context_->GetRegisteredPath(fs_name),
432 fs_name,
433 gallery.pref_id,
434 GetTransientIdForRemovableDeviceId(device_id),
435 StorageInfo::IsRemovableDevice(device_id),
436 StorageInfo::IsMediaDevice(device_id));
440 if (pref_id_map_.empty()) {
441 rph_refs_.Reset();
442 CleanUp();
444 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
445 base::Bind(callback, result));
448 std::string GetTransientIdForRemovableDeviceId(const std::string& device_id) {
449 if (!StorageInfo::IsRemovableDevice(device_id))
450 return std::string();
452 return StorageMonitor::GetInstance()->GetTransientIdForDeviceId(device_id);
455 void CleanUp() {
456 DCHECK(rph_refs_.empty());
457 for (PrefIdFsInfoMap::const_iterator it = pref_id_map_.begin();
458 it != pref_id_map_.end();
459 ++it) {
460 file_system_context_->RevokeFileSystem(it->second.fsid);
462 pref_id_map_.clear();
464 no_references_callback_.Run();
467 // MediaFileSystemRegistry owns |this| and |file_system_context_|, so it's
468 // safe to store a raw pointer.
469 MediaFileSystemContext* file_system_context_;
471 // Path for the active profile.
472 const base::FilePath profile_path_;
474 // Id of the extension this host belongs to.
475 const std::string extension_id_;
477 // A callback to call when the last RVH reference goes away.
478 base::Closure no_references_callback_;
480 // A map from the gallery preferences id to the file system information.
481 PrefIdFsInfoMap pref_id_map_;
483 // The set of render processes and web contents that may have references to
484 // the file system ids this instance manages.
485 RPHReferenceManager rph_refs_;
487 DISALLOW_COPY_AND_ASSIGN(ExtensionGalleriesHost);
490 /******************
491 * Public methods
492 ******************/
494 void MediaFileSystemRegistry::GetMediaFileSystemsForExtension(
495 const content::RenderViewHost* rvh,
496 const extensions::Extension* extension,
497 const MediaFileSystemsCallback& callback) {
498 // TODO(tommycli): Change to DCHECK after fixing http://crbug.com/374330.
499 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
501 Profile* profile =
502 Profile::FromBrowserContext(rvh->GetProcess()->GetBrowserContext());
503 MediaGalleriesPreferences* preferences = GetPreferences(profile);
504 MediaGalleryPrefIdSet galleries =
505 preferences->GalleriesForExtension(*extension);
507 if (galleries.empty()) {
508 callback.Run(std::vector<MediaFileSystemInfo>());
509 return;
512 ExtensionGalleriesHost* extension_host =
513 GetExtensionGalleryHost(profile, preferences, extension->id());
515 // This must come before the GetMediaFileSystems call to make sure the
516 // RVH of the context is referenced before the filesystems are retrieved.
517 extension_host->ReferenceFromRVH(rvh);
519 extension_host->GetMediaFileSystems(galleries, preferences->known_galleries(),
520 callback);
523 void MediaFileSystemRegistry::RegisterMediaFileSystemForExtension(
524 const content::RenderViewHost* rvh,
525 const extensions::Extension* extension,
526 MediaGalleryPrefId pref_id,
527 const base::Callback<void(base::File::Error result)>& callback) {
528 DCHECK_CURRENTLY_ON(BrowserThread::UI);
529 DCHECK_NE(kInvalidMediaGalleryPrefId, pref_id);
531 Profile* profile =
532 Profile::FromBrowserContext(rvh->GetProcess()->GetBrowserContext());
533 MediaGalleriesPreferences* preferences = GetPreferences(profile);
534 MediaGalleriesPrefInfoMap::const_iterator gallery =
535 preferences->known_galleries().find(pref_id);
536 MediaGalleryPrefIdSet permitted_galleries =
537 preferences->GalleriesForExtension(*extension);
539 if (gallery == preferences->known_galleries().end() ||
540 !ContainsKey(permitted_galleries, pref_id)) {
541 BrowserThread::PostTask(
542 BrowserThread::IO, FROM_HERE,
543 base::Bind(callback, base::File::FILE_ERROR_NOT_FOUND));
544 return;
547 ExtensionGalleriesHost* extension_host =
548 GetExtensionGalleryHost(profile, preferences, extension->id());
550 // This must come before the GetMediaFileSystems call to make sure the
551 // RVH of the context is referenced before the filesystems are retrieved.
552 extension_host->ReferenceFromRVH(rvh);
554 extension_host->RegisterMediaFileSystem(gallery->second, callback);
557 MediaGalleriesPreferences* MediaFileSystemRegistry::GetPreferences(
558 Profile* profile) {
559 // Create an empty ExtensionHostMap for this profile on first initialization.
560 if (!ContainsKey(extension_hosts_map_, profile)) {
561 extension_hosts_map_[profile] = ExtensionHostMap();
562 media_galleries::UsageCount(media_galleries::PROFILES_WITH_USAGE);
565 return MediaGalleriesPreferencesFactory::GetForProfile(profile);
568 MediaScanManager* MediaFileSystemRegistry::media_scan_manager() {
569 if (!media_scan_manager_)
570 media_scan_manager_.reset(new MediaScanManager);
571 return media_scan_manager_.get();
574 GalleryWatchManager* MediaFileSystemRegistry::gallery_watch_manager() {
575 if (!gallery_watch_manager_)
576 gallery_watch_manager_.reset(new GalleryWatchManager);
577 return gallery_watch_manager_.get();
580 void MediaFileSystemRegistry::OnRemovableStorageDetached(
581 const StorageInfo& info) {
582 DCHECK_CURRENTLY_ON(BrowserThread::UI);
584 // Since revoking a gallery in the ExtensionGalleriesHost may cause it
585 // to be removed from the map and therefore invalidate any iterator pointing
586 // to it, this code first copies all the invalid gallery ids and the
587 // extension hosts in which they may appear (per profile) and revoked it in
588 // a second step.
589 std::vector<InvalidatedGalleriesInfo> invalid_galleries_info;
591 for (ExtensionGalleriesHostMap::iterator profile_it =
592 extension_hosts_map_.begin();
593 profile_it != extension_hosts_map_.end();
594 ++profile_it) {
595 MediaGalleriesPreferences* preferences = GetPreferences(profile_it->first);
596 // If |preferences| is not yet initialized, it won't contain any galleries.
597 if (!preferences->IsInitialized())
598 continue;
600 InvalidatedGalleriesInfo invalid_galleries_in_profile;
601 invalid_galleries_in_profile.pref_ids =
602 preferences->LookUpGalleriesByDeviceId(info.device_id());
604 for (ExtensionHostMap::const_iterator extension_host_it =
605 profile_it->second.begin();
606 extension_host_it != profile_it->second.end();
607 ++extension_host_it) {
608 invalid_galleries_in_profile.extension_hosts.insert(
609 extension_host_it->second.get());
612 invalid_galleries_info.push_back(invalid_galleries_in_profile);
615 for (size_t i = 0; i < invalid_galleries_info.size(); i++) {
616 for (std::set<ExtensionGalleriesHost*>::const_iterator extension_host_it =
617 invalid_galleries_info[i].extension_hosts.begin();
618 extension_host_it != invalid_galleries_info[i].extension_hosts.end();
619 ++extension_host_it) {
620 for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it =
621 invalid_galleries_info[i].pref_ids.begin();
622 pref_id_it != invalid_galleries_info[i].pref_ids.end();
623 ++pref_id_it) {
624 (*extension_host_it)->RevokeGalleryByPrefId(*pref_id_it);
630 /******************
631 * Private methods
632 ******************/
634 class MediaFileSystemRegistry::MediaFileSystemContextImpl
635 : public MediaFileSystemContext {
636 public:
637 MediaFileSystemContextImpl() {}
638 virtual ~MediaFileSystemContextImpl() {}
640 virtual bool RegisterFileSystem(const std::string& device_id,
641 const std::string& fs_name,
642 const base::FilePath& path) override {
643 if (StorageInfo::IsMassStorageDevice(device_id)) {
644 return RegisterFileSystemForMassStorage(device_id, fs_name, path);
645 } else {
646 return RegisterFileSystemForMTPDevice(device_id, fs_name, path);
650 virtual void RevokeFileSystem(const std::string& fs_name) override {
651 ImportedMediaGalleryRegistry* imported_registry =
652 ImportedMediaGalleryRegistry::GetInstance();
653 if (imported_registry->RevokeImportedFilesystemOnUIThread(fs_name))
654 return;
656 ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(fs_name);
658 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
659 &MTPDeviceMapService::RevokeMTPFileSystem,
660 base::Unretained(MTPDeviceMapService::GetInstance()),
661 fs_name));
664 virtual base::FilePath GetRegisteredPath(
665 const std::string& fs_name) const override {
666 base::FilePath result;
667 if (!ExternalMountPoints::GetSystemInstance()->GetRegisteredPath(fs_name,
668 &result)) {
669 return base::FilePath();
671 return result;
674 private:
675 // Registers and returns the file system id for the mass storage device
676 // specified by |device_id| and |path|.
677 bool RegisterFileSystemForMassStorage(const std::string& device_id,
678 const std::string& fs_name,
679 const base::FilePath& path) {
680 DCHECK_CURRENTLY_ON(BrowserThread::UI);
681 DCHECK(StorageInfo::IsMassStorageDevice(device_id));
683 // Sanity checks for |path|.
684 CHECK(path.IsAbsolute());
685 CHECK(!path.ReferencesParent());
687 // TODO(gbillock): refactor ImportedMediaGalleryRegistry to delegate this
688 // call tree, probably by having it figure out by device id what
689 // registration is needed, or having per-device-type handlers at the
690 // next higher level.
691 bool result = false;
692 if (StorageInfo::IsITunesDevice(device_id)) {
693 ImportedMediaGalleryRegistry* registry =
694 ImportedMediaGalleryRegistry::GetInstance();
695 result = registry->RegisterITunesFilesystemOnUIThread(fs_name, path);
696 } else if (StorageInfo::IsPicasaDevice(device_id)) {
697 ImportedMediaGalleryRegistry* registry =
698 ImportedMediaGalleryRegistry::GetInstance();
699 result = registry->RegisterPicasaFilesystemOnUIThread(fs_name, path);
700 } else if (StorageInfo::IsIPhotoDevice(device_id)) {
701 ImportedMediaGalleryRegistry* registry =
702 ImportedMediaGalleryRegistry::GetInstance();
703 result = registry->RegisterIPhotoFilesystemOnUIThread(fs_name, path);
704 } else {
705 result = ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
706 fs_name,
707 storage::kFileSystemTypeNativeMedia,
708 storage::FileSystemMountOption(),
709 path);
711 return result;
714 bool RegisterFileSystemForMTPDevice(const std::string& device_id,
715 const std::string fs_name,
716 const base::FilePath& path) {
717 DCHECK_CURRENTLY_ON(BrowserThread::UI);
718 DCHECK(!StorageInfo::IsMassStorageDevice(device_id));
720 // Sanity checks for |path|.
721 CHECK(MediaStorageUtil::CanCreateFileSystem(device_id, path));
722 bool result = ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
723 fs_name,
724 storage::kFileSystemTypeDeviceMedia,
725 storage::FileSystemMountOption(),
726 path);
727 CHECK(result);
728 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
729 &MTPDeviceMapService::RegisterMTPFileSystem,
730 base::Unretained(MTPDeviceMapService::GetInstance()),
731 path.value(), fs_name));
732 return result;
735 DISALLOW_COPY_AND_ASSIGN(MediaFileSystemContextImpl);
738 // Constructor in 'private' section because depends on private class definition.
739 MediaFileSystemRegistry::MediaFileSystemRegistry()
740 : file_system_context_(new MediaFileSystemContextImpl) {
741 StorageMonitor::GetInstance()->AddObserver(this);
744 MediaFileSystemRegistry::~MediaFileSystemRegistry() {
745 // TODO(gbillock): This is needed because the unit test uses the
746 // g_browser_process registry. We should create one in the unit test,
747 // and then can remove this.
748 if (StorageMonitor::GetInstance())
749 StorageMonitor::GetInstance()->RemoveObserver(this);
752 void MediaFileSystemRegistry::OnPermissionRemoved(
753 MediaGalleriesPreferences* prefs,
754 const std::string& extension_id,
755 MediaGalleryPrefId pref_id) {
756 Profile* profile = prefs->profile();
757 ExtensionGalleriesHostMap::const_iterator host_map_it =
758 extension_hosts_map_.find(profile);
759 DCHECK(host_map_it != extension_hosts_map_.end());
760 const ExtensionHostMap& extension_host_map = host_map_it->second;
761 ExtensionHostMap::const_iterator gallery_host_it =
762 extension_host_map.find(extension_id);
763 if (gallery_host_it == extension_host_map.end())
764 return;
765 gallery_host_it->second->RevokeGalleryByPrefId(pref_id);
768 void MediaFileSystemRegistry::OnGalleryRemoved(
769 MediaGalleriesPreferences* prefs,
770 MediaGalleryPrefId pref_id) {
771 Profile* profile = prefs->profile();
772 // Get the Extensions, MediaGalleriesPreferences and ExtensionHostMap for
773 // |profile|.
774 const ExtensionService* extension_service =
775 extensions::ExtensionSystem::Get(profile)->extension_service();
776 const extensions::ExtensionSet* extensions_set =
777 extension_service->extensions();
778 ExtensionGalleriesHostMap::const_iterator host_map_it =
779 extension_hosts_map_.find(profile);
780 DCHECK(host_map_it != extension_hosts_map_.end());
781 const ExtensionHostMap& extension_host_map = host_map_it->second;
783 // Go through ExtensionHosts, and remove indicated gallery, if any.
784 // RevokeGalleryByPrefId() may end up deleting from |extension_host_map| and
785 // even delete |extension_host_map| altogether. So do this in two loops to
786 // avoid using an invalidated iterator or deleted map.
787 std::vector<const extensions::Extension*> extensions;
788 for (ExtensionHostMap::const_iterator it = extension_host_map.begin();
789 it != extension_host_map.end();
790 ++it) {
791 extensions.push_back(extensions_set->GetByID(it->first));
793 for (size_t i = 0; i < extensions.size(); ++i) {
794 if (!ContainsKey(extension_hosts_map_, profile))
795 break;
796 ExtensionHostMap::const_iterator gallery_host_it =
797 extension_host_map.find(extensions[i]->id());
798 if (gallery_host_it == extension_host_map.end())
799 continue;
800 gallery_host_it->second->RevokeGalleryByPrefId(pref_id);
804 ExtensionGalleriesHost* MediaFileSystemRegistry::GetExtensionGalleryHost(
805 Profile* profile,
806 MediaGalleriesPreferences* preferences,
807 const std::string& extension_id) {
808 ExtensionGalleriesHostMap::iterator extension_hosts =
809 extension_hosts_map_.find(profile);
810 // GetPreferences(), which had to be called because preferences is an
811 // argument, ensures that profile is in the map.
812 DCHECK(extension_hosts != extension_hosts_map_.end());
813 if (extension_hosts->second.empty())
814 preferences->AddGalleryChangeObserver(this);
816 ExtensionGalleriesHost* result = extension_hosts->second[extension_id].get();
817 if (!result) {
818 result = new ExtensionGalleriesHost(
819 file_system_context_.get(),
820 profile->GetPath(),
821 extension_id,
822 base::Bind(&MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty,
823 base::Unretained(this),
824 profile,
825 extension_id));
826 extension_hosts_map_[profile][extension_id] = result;
828 return result;
831 void MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty(
832 Profile* profile, const std::string& extension_id) {
833 DCHECK_CURRENTLY_ON(BrowserThread::UI);
835 ExtensionGalleriesHostMap::iterator extension_hosts =
836 extension_hosts_map_.find(profile);
837 DCHECK(extension_hosts != extension_hosts_map_.end());
838 ExtensionHostMap::size_type erase_count =
839 extension_hosts->second.erase(extension_id);
840 DCHECK_EQ(1U, erase_count);
841 if (extension_hosts->second.empty()) {
842 // When a profile has no ExtensionGalleriesHosts left, remove the
843 // matching gallery-change-watcher since it is no longer needed. Leave the
844 // |extension_hosts| entry alone, since it indicates the profile has been
845 // previously used.
846 MediaGalleriesPreferences* preferences = GetPreferences(profile);
847 preferences->RemoveGalleryChangeObserver(this);