Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / media_galleries / media_file_system_registry.cc
blob08cecd1cb07571e8c52e11a20a6d7baa1cd09462
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/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 "chrome/common/pref_names.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_system.h"
38 #include "extensions/common/extension.h"
39 #include "extensions/common/extension_set.h"
40 #include "webkit/browser/fileapi/external_mount_points.h"
41 #include "webkit/common/fileapi/file_system_mount_option.h"
42 #include "webkit/common/fileapi/file_system_types.h"
44 using content::BrowserThread;
45 using content::NavigationController;
46 using content::RenderProcessHost;
47 using content::WebContents;
48 using fileapi::ExternalMountPoints;
49 using storage_monitor::MediaStorageUtil;
50 using storage_monitor::StorageInfo;
51 using storage_monitor::StorageMonitor;
53 namespace {
55 struct InvalidatedGalleriesInfo {
56 std::set<ExtensionGalleriesHost*> extension_hosts;
57 std::set<MediaGalleryPrefId> pref_ids;
60 // Tracks the liveness of multiple RenderProcessHosts that the caller is
61 // interested in. Once all of the RPHs have closed or been destroyed a call
62 // back informs the caller.
63 class RPHReferenceManager {
64 public:
65 // |no_references_callback| is called when the last RenderViewHost reference
66 // goes away. RenderViewHost references are added through ReferenceFromRVH().
67 explicit RPHReferenceManager(const base::Closure& no_references_callback);
68 virtual ~RPHReferenceManager();
70 // Remove all references, but don't call |no_references_callback|.
71 void Reset() { STLDeleteValues(&observer_map_); }
73 // Returns true if there are no references;
74 bool empty() const { return observer_map_.empty(); }
76 // Adds a reference to the passed |rvh|. Calling this multiple times with
77 // the same |rvh| is a no-op.
78 void ReferenceFromRVH(const content::RenderViewHost* rvh);
80 private:
81 class RPHWebContentsObserver : public content::WebContentsObserver {
82 public:
83 RPHWebContentsObserver(RPHReferenceManager* manager,
84 WebContents* web_contents);
86 private:
87 // content::WebContentsObserver
88 virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
89 virtual void NavigationEntryCommitted(
90 const content::LoadCommittedDetails& load_details) OVERRIDE;
92 RPHReferenceManager* manager_;
95 class RPHObserver : public content::RenderProcessHostObserver {
96 public:
97 RPHObserver(RPHReferenceManager* manager, RenderProcessHost* host);
98 virtual ~RPHObserver();
100 void AddWebContentsObserver(WebContents* web_contents);
101 void RemoveWebContentsObserver(WebContents* web_contents);
102 bool HasWebContentsObservers() {
103 return observed_web_contentses_.size() > 0;
106 private:
107 virtual void RenderProcessHostDestroyed(RenderProcessHost* host) OVERRIDE;
109 RPHReferenceManager* manager_;
110 RenderProcessHost* host_;
111 typedef std::map<WebContents*, RPHWebContentsObserver*> WCObserverMap;
112 WCObserverMap observed_web_contentses_;
114 typedef std::map<const RenderProcessHost*, RPHObserver*> RPHObserverMap;
116 // Handlers for observed events.
117 void OnRenderProcessHostDestroyed(RenderProcessHost* rph);
118 void OnWebContentsDestroyedOrNavigated(WebContents* contents);
120 // A callback to call when the last RVH reference goes away.
121 base::Closure no_references_callback_;
123 // The set of render processes and web contents that may have references to
124 // the file system ids this instance manages.
125 RPHObserverMap observer_map_;
128 RPHReferenceManager::RPHReferenceManager(
129 const base::Closure& no_references_callback)
130 : no_references_callback_(no_references_callback) {
133 RPHReferenceManager::~RPHReferenceManager() {
134 Reset();
137 void RPHReferenceManager::ReferenceFromRVH(const content::RenderViewHost* rvh) {
138 WebContents* contents = WebContents::FromRenderViewHost(rvh);
139 RenderProcessHost* rph = contents->GetRenderProcessHost();
140 RPHObserver* state = NULL;
141 if (!ContainsKey(observer_map_, rph)) {
142 state = new RPHObserver(this, rph);
143 observer_map_[rph] = state;
144 } else {
145 state = observer_map_[rph];
148 state->AddWebContentsObserver(contents);
151 RPHReferenceManager::RPHWebContentsObserver::RPHWebContentsObserver(
152 RPHReferenceManager* manager,
153 WebContents* web_contents)
154 : content::WebContentsObserver(web_contents),
155 manager_(manager) {
158 void RPHReferenceManager::RPHWebContentsObserver::WebContentsDestroyed(
159 WebContents* web_contents) {
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 // Extract all the device ids so we can make sure they are attached.
285 MediaStorageUtil::DeviceIdSet* device_ids =
286 new MediaStorageUtil::DeviceIdSet;
287 for (std::set<MediaGalleryPrefId>::const_iterator id = galleries.begin();
288 id != galleries.end();
289 ++id) {
290 device_ids->insert(galleries_info.find(*id)->second.device_id);
292 MediaStorageUtil::FilterAttachedDevices(device_ids, base::Bind(
293 &ExtensionGalleriesHost::GetMediaFileSystemsForAttachedDevices, this,
294 base::Owned(device_ids), galleries, galleries_info, callback));
297 // Checks if |gallery| is attached and if so, registers the file system and
298 // then calls |callback| with the result.
299 void RegisterMediaFileSystem(
300 const MediaGalleryPrefInfo& gallery,
301 const base::Callback<void(base::File::Error result)>& callback) {
302 // Extract all the device ids so we can make sure they are attached.
303 MediaStorageUtil::DeviceIdSet* device_ids =
304 new MediaStorageUtil::DeviceIdSet;
305 device_ids->insert(gallery.device_id);
306 MediaStorageUtil::FilterAttachedDevices(device_ids, base::Bind(
307 &ExtensionGalleriesHost::RegisterAttachedMediaFileSystem, this,
308 base::Owned(device_ids), gallery, callback));
311 // Revoke the file system for |id| if this extension has created one for |id|.
312 void RevokeGalleryByPrefId(MediaGalleryPrefId id) {
313 PrefIdFsInfoMap::iterator gallery = pref_id_map_.find(id);
314 if (gallery == pref_id_map_.end())
315 return;
317 file_system_context_->RevokeFileSystem(gallery->second.fsid);
318 pref_id_map_.erase(gallery);
320 if (pref_id_map_.empty()) {
321 rph_refs_.Reset();
322 CleanUp();
326 // Indicate that the passed |rvh| will reference the file system ids created
327 // by this class.
328 void ReferenceFromRVH(const content::RenderViewHost* rvh) {
329 rph_refs_.ReferenceFromRVH(rvh);
332 private:
333 typedef std::map<MediaGalleryPrefId, MediaFileSystemInfo> PrefIdFsInfoMap;
335 // Private destructor and friend declaration for ref counted implementation.
336 friend class base::RefCountedThreadSafe<ExtensionGalleriesHost>;
338 virtual ~ExtensionGalleriesHost() {
339 DCHECK(rph_refs_.empty());
340 DCHECK(pref_id_map_.empty());
343 void GetMediaFileSystemsForAttachedDevices(
344 const MediaStorageUtil::DeviceIdSet* attached_devices,
345 const MediaGalleryPrefIdSet& galleries,
346 const MediaGalleriesPrefInfoMap& galleries_info,
347 const MediaFileSystemsCallback& callback) {
348 std::vector<MediaFileSystemInfo> result;
350 if (rph_refs_.empty()) {
351 // We're actually in the middle of shutdown, and Filter...() lagging
352 // which can invoke this method interleaved in the destruction callback
353 // sequence and re-populate pref_id_map_.
354 callback.Run(result);
355 return;
358 for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it =
359 galleries.begin();
360 pref_id_it != galleries.end();
361 ++pref_id_it) {
362 const MediaGalleryPrefId& pref_id = *pref_id_it;
363 const MediaGalleryPrefInfo& gallery_info =
364 galleries_info.find(pref_id)->second;
365 const std::string& device_id = gallery_info.device_id;
366 if (!ContainsKey(*attached_devices, device_id))
367 continue;
369 PrefIdFsInfoMap::const_iterator existing_info =
370 pref_id_map_.find(pref_id);
371 if (existing_info != pref_id_map_.end()) {
372 result.push_back(existing_info->second);
373 continue;
376 base::FilePath path = gallery_info.AbsolutePath();
377 if (!MediaStorageUtil::CanCreateFileSystem(device_id, path))
378 continue;
380 std::string fs_name = MediaFileSystemBackend::ConstructMountName(
381 profile_path_, extension_id_, pref_id);
382 if (!file_system_context_->RegisterFileSystem(device_id, fs_name, path))
383 continue;
385 MediaFileSystemInfo new_entry(
386 gallery_info.GetGalleryDisplayName(),
387 file_system_context_->GetRegisteredPath(fs_name),
388 fs_name,
389 pref_id,
390 GetTransientIdForRemovableDeviceId(device_id),
391 StorageInfo::IsRemovableDevice(device_id),
392 StorageInfo::IsMediaDevice(device_id));
393 result.push_back(new_entry);
394 pref_id_map_[pref_id] = new_entry;
397 if (result.size() == 0) {
398 rph_refs_.Reset();
399 CleanUp();
402 DCHECK_EQ(pref_id_map_.size(), result.size());
403 callback.Run(result);
406 void RegisterAttachedMediaFileSystem(
407 const MediaStorageUtil::DeviceIdSet* attached_device,
408 const MediaGalleryPrefInfo& gallery,
409 const base::Callback<void(base::File::Error result)>& callback) {
410 base::File::Error result = base::File::FILE_ERROR_NOT_FOUND;
412 // If rph_refs is empty then we're actually in the middle of shutdown, and
413 // Filter...() lagging which can invoke this method interleaved in the
414 // destruction callback sequence and re-populate pref_id_map_.
415 if (!attached_device->empty() && !rph_refs_.empty()) {
416 std::string fs_name = MediaFileSystemBackend::ConstructMountName(
417 profile_path_, extension_id_, gallery.pref_id);
418 base::FilePath path = gallery.AbsolutePath();
419 const std::string& device_id = gallery.device_id;
421 if (ContainsKey(pref_id_map_, gallery.pref_id)) {
422 result = base::File::FILE_OK;
423 } else if (MediaStorageUtil::CanCreateFileSystem(device_id, path) &&
424 file_system_context_->RegisterFileSystem(device_id, fs_name,
425 path)) {
426 result = base::File::FILE_OK;
427 pref_id_map_[gallery.pref_id] = MediaFileSystemInfo(
428 gallery.GetGalleryDisplayName(),
429 file_system_context_->GetRegisteredPath(fs_name),
430 fs_name,
431 gallery.pref_id,
432 GetTransientIdForRemovableDeviceId(device_id),
433 StorageInfo::IsRemovableDevice(device_id),
434 StorageInfo::IsMediaDevice(device_id));
438 if (pref_id_map_.empty()) {
439 rph_refs_.Reset();
440 CleanUp();
442 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
443 base::Bind(callback, result));
446 std::string GetTransientIdForRemovableDeviceId(const std::string& device_id) {
447 if (!StorageInfo::IsRemovableDevice(device_id))
448 return std::string();
450 return StorageMonitor::GetInstance()->GetTransientIdForDeviceId(device_id);
453 void CleanUp() {
454 DCHECK(rph_refs_.empty());
455 for (PrefIdFsInfoMap::const_iterator it = pref_id_map_.begin();
456 it != pref_id_map_.end();
457 ++it) {
458 file_system_context_->RevokeFileSystem(it->second.fsid);
460 pref_id_map_.clear();
462 no_references_callback_.Run();
465 // MediaFileSystemRegistry owns |this| and |file_system_context_|, so it's
466 // safe to store a raw pointer.
467 MediaFileSystemContext* file_system_context_;
469 // Path for the active profile.
470 const base::FilePath profile_path_;
472 // Id of the extension this host belongs to.
473 const std::string extension_id_;
475 // A callback to call when the last RVH reference goes away.
476 base::Closure no_references_callback_;
478 // A map from the gallery preferences id to the file system information.
479 PrefIdFsInfoMap pref_id_map_;
481 // The set of render processes and web contents that may have references to
482 // the file system ids this instance manages.
483 RPHReferenceManager rph_refs_;
485 DISALLOW_COPY_AND_ASSIGN(ExtensionGalleriesHost);
488 /******************
489 * Public methods
490 ******************/
492 void MediaFileSystemRegistry::GetMediaFileSystemsForExtension(
493 const content::RenderViewHost* rvh,
494 const extensions::Extension* extension,
495 const MediaFileSystemsCallback& callback) {
496 DCHECK_CURRENTLY_ON(BrowserThread::UI);
498 Profile* profile =
499 Profile::FromBrowserContext(rvh->GetProcess()->GetBrowserContext());
500 MediaGalleriesPreferences* preferences = GetPreferences(profile);
501 MediaGalleryPrefIdSet galleries =
502 preferences->GalleriesForExtension(*extension);
504 if (galleries.empty()) {
505 callback.Run(std::vector<MediaFileSystemInfo>());
506 return;
509 ExtensionGalleriesHost* extension_host =
510 GetExtensionGalleryHost(profile, preferences, extension->id());
512 // This must come before the GetMediaFileSystems call to make sure the
513 // RVH of the context is referenced before the filesystems are retrieved.
514 extension_host->ReferenceFromRVH(rvh);
516 extension_host->GetMediaFileSystems(galleries, preferences->known_galleries(),
517 callback);
520 void MediaFileSystemRegistry::RegisterMediaFileSystemForExtension(
521 const content::RenderViewHost* rvh,
522 const extensions::Extension* extension,
523 MediaGalleryPrefId pref_id,
524 const base::Callback<void(base::File::Error result)>& callback) {
525 DCHECK_CURRENTLY_ON(BrowserThread::UI);
526 DCHECK_NE(kInvalidMediaGalleryPrefId, pref_id);
528 Profile* profile =
529 Profile::FromBrowserContext(rvh->GetProcess()->GetBrowserContext());
530 MediaGalleriesPreferences* preferences = GetPreferences(profile);
531 MediaGalleriesPrefInfoMap::const_iterator gallery =
532 preferences->known_galleries().find(pref_id);
533 MediaGalleryPrefIdSet permitted_galleries =
534 preferences->GalleriesForExtension(*extension);
536 if (gallery == preferences->known_galleries().end() ||
537 !ContainsKey(permitted_galleries, pref_id)) {
538 BrowserThread::PostTask(
539 BrowserThread::IO, FROM_HERE,
540 base::Bind(callback, base::File::FILE_ERROR_NOT_FOUND));
541 return;
544 ExtensionGalleriesHost* extension_host =
545 GetExtensionGalleryHost(profile, preferences, extension->id());
547 // This must come before the GetMediaFileSystems call to make sure the
548 // RVH of the context is referenced before the filesystems are retrieved.
549 extension_host->ReferenceFromRVH(rvh);
551 extension_host->RegisterMediaFileSystem(gallery->second, callback);
554 MediaGalleriesPreferences* MediaFileSystemRegistry::GetPreferences(
555 Profile* profile) {
556 // Create an empty ExtensionHostMap for this profile on first initialization.
557 if (!ContainsKey(extension_hosts_map_, profile)) {
558 extension_hosts_map_[profile] = ExtensionHostMap();
559 media_galleries::UsageCount(media_galleries::PROFILES_WITH_USAGE);
562 return MediaGalleriesPreferencesFactory::GetForProfile(profile);
565 MediaScanManager* MediaFileSystemRegistry::media_scan_manager() {
566 if (!media_scan_manager_)
567 media_scan_manager_.reset(new MediaScanManager);
568 return media_scan_manager_.get();
571 void MediaFileSystemRegistry::OnRemovableStorageDetached(
572 const StorageInfo& info) {
573 DCHECK_CURRENTLY_ON(BrowserThread::UI);
575 // Since revoking a gallery in the ExtensionGalleriesHost may cause it
576 // to be removed from the map and therefore invalidate any iterator pointing
577 // to it, this code first copies all the invalid gallery ids and the
578 // extension hosts in which they may appear (per profile) and revoked it in
579 // a second step.
580 std::vector<InvalidatedGalleriesInfo> invalid_galleries_info;
582 for (ExtensionGalleriesHostMap::iterator profile_it =
583 extension_hosts_map_.begin();
584 profile_it != extension_hosts_map_.end();
585 ++profile_it) {
586 MediaGalleriesPreferences* preferences = GetPreferences(profile_it->first);
587 // If |preferences| is not yet initialized, it won't contain any galleries.
588 if (!preferences->IsInitialized())
589 continue;
591 InvalidatedGalleriesInfo invalid_galleries_in_profile;
592 invalid_galleries_in_profile.pref_ids =
593 preferences->LookUpGalleriesByDeviceId(info.device_id());
595 for (ExtensionHostMap::const_iterator extension_host_it =
596 profile_it->second.begin();
597 extension_host_it != profile_it->second.end();
598 ++extension_host_it) {
599 invalid_galleries_in_profile.extension_hosts.insert(
600 extension_host_it->second.get());
603 invalid_galleries_info.push_back(invalid_galleries_in_profile);
606 for (size_t i = 0; i < invalid_galleries_info.size(); i++) {
607 for (std::set<ExtensionGalleriesHost*>::const_iterator extension_host_it =
608 invalid_galleries_info[i].extension_hosts.begin();
609 extension_host_it != invalid_galleries_info[i].extension_hosts.end();
610 ++extension_host_it) {
611 for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it =
612 invalid_galleries_info[i].pref_ids.begin();
613 pref_id_it != invalid_galleries_info[i].pref_ids.end();
614 ++pref_id_it) {
615 (*extension_host_it)->RevokeGalleryByPrefId(*pref_id_it);
621 /******************
622 * Private methods
623 ******************/
625 class MediaFileSystemRegistry::MediaFileSystemContextImpl
626 : public MediaFileSystemContext {
627 public:
628 MediaFileSystemContextImpl() {}
629 virtual ~MediaFileSystemContextImpl() {}
631 virtual bool RegisterFileSystem(const std::string& device_id,
632 const std::string& fs_name,
633 const base::FilePath& path) OVERRIDE {
634 if (StorageInfo::IsMassStorageDevice(device_id)) {
635 return RegisterFileSystemForMassStorage(device_id, fs_name, path);
636 } else {
637 return RegisterFileSystemForMTPDevice(device_id, fs_name, path);
641 virtual void RevokeFileSystem(const std::string& fs_name) OVERRIDE {
642 ImportedMediaGalleryRegistry* imported_registry =
643 ImportedMediaGalleryRegistry::GetInstance();
644 if (imported_registry->RevokeImportedFilesystemOnUIThread(fs_name))
645 return;
647 ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(fs_name);
649 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
650 &MTPDeviceMapService::RevokeMTPFileSystem,
651 base::Unretained(MTPDeviceMapService::GetInstance()),
652 fs_name));
655 virtual base::FilePath GetRegisteredPath(
656 const std::string& fs_name) const OVERRIDE {
657 base::FilePath result;
658 if (!ExternalMountPoints::GetSystemInstance()->GetRegisteredPath(fs_name,
659 &result)) {
660 return base::FilePath();
662 return result;
665 private:
666 // Registers and returns the file system id for the mass storage device
667 // specified by |device_id| and |path|.
668 bool RegisterFileSystemForMassStorage(const std::string& device_id,
669 const std::string& fs_name,
670 const base::FilePath& path) {
671 DCHECK_CURRENTLY_ON(BrowserThread::UI);
672 DCHECK(StorageInfo::IsMassStorageDevice(device_id));
674 // Sanity checks for |path|.
675 CHECK(path.IsAbsolute());
676 CHECK(!path.ReferencesParent());
678 // TODO(gbillock): refactor ImportedMediaGalleryRegistry to delegate this
679 // call tree, probably by having it figure out by device id what
680 // registration is needed, or having per-device-type handlers at the
681 // next higher level.
682 bool result = false;
683 if (StorageInfo::IsITunesDevice(device_id)) {
684 ImportedMediaGalleryRegistry* registry =
685 ImportedMediaGalleryRegistry::GetInstance();
686 result = registry->RegisterITunesFilesystemOnUIThread(fs_name, path);
687 } else if (StorageInfo::IsPicasaDevice(device_id)) {
688 ImportedMediaGalleryRegistry* registry =
689 ImportedMediaGalleryRegistry::GetInstance();
690 result = registry->RegisterPicasaFilesystemOnUIThread(fs_name, path);
691 } else if (StorageInfo::IsIPhotoDevice(device_id)) {
692 ImportedMediaGalleryRegistry* registry =
693 ImportedMediaGalleryRegistry::GetInstance();
694 result = registry->RegisterIPhotoFilesystemOnUIThread(fs_name, path);
695 } else {
696 result = ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
697 fs_name, fileapi::kFileSystemTypeNativeMedia,
698 fileapi::FileSystemMountOption(), path);
700 return result;
703 bool RegisterFileSystemForMTPDevice(const std::string& device_id,
704 const std::string fs_name,
705 const base::FilePath& path) {
706 DCHECK_CURRENTLY_ON(BrowserThread::UI);
707 DCHECK(!StorageInfo::IsMassStorageDevice(device_id));
709 // Sanity checks for |path|.
710 CHECK(MediaStorageUtil::CanCreateFileSystem(device_id, path));
711 bool result = ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
712 fs_name, fileapi::kFileSystemTypeDeviceMedia,
713 fileapi::FileSystemMountOption(), path);
714 CHECK(result);
715 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
716 &MTPDeviceMapService::RegisterMTPFileSystem,
717 base::Unretained(MTPDeviceMapService::GetInstance()),
718 path.value(), fs_name));
719 return result;
722 DISALLOW_COPY_AND_ASSIGN(MediaFileSystemContextImpl);
725 // Constructor in 'private' section because depends on private class definition.
726 MediaFileSystemRegistry::MediaFileSystemRegistry()
727 : file_system_context_(new MediaFileSystemContextImpl) {
728 StorageMonitor::GetInstance()->AddObserver(this);
731 MediaFileSystemRegistry::~MediaFileSystemRegistry() {
732 // TODO(gbillock): This is needed because the unit test uses the
733 // g_browser_process registry. We should create one in the unit test,
734 // and then can remove this.
735 if (StorageMonitor::GetInstance())
736 StorageMonitor::GetInstance()->RemoveObserver(this);
739 void MediaFileSystemRegistry::OnPermissionRemoved(
740 MediaGalleriesPreferences* prefs,
741 const std::string& extension_id,
742 MediaGalleryPrefId pref_id) {
743 Profile* profile = prefs->profile();
744 ExtensionGalleriesHostMap::const_iterator host_map_it =
745 extension_hosts_map_.find(profile);
746 DCHECK(host_map_it != extension_hosts_map_.end());
747 const ExtensionHostMap& extension_host_map = host_map_it->second;
748 ExtensionHostMap::const_iterator gallery_host_it =
749 extension_host_map.find(extension_id);
750 if (gallery_host_it == extension_host_map.end())
751 return;
752 gallery_host_it->second->RevokeGalleryByPrefId(pref_id);
755 void MediaFileSystemRegistry::OnGalleryRemoved(
756 MediaGalleriesPreferences* prefs,
757 MediaGalleryPrefId pref_id) {
758 Profile* profile = prefs->profile();
759 // Get the Extensions, MediaGalleriesPreferences and ExtensionHostMap for
760 // |profile|.
761 const ExtensionService* extension_service =
762 extensions::ExtensionSystem::Get(profile)->extension_service();
763 const extensions::ExtensionSet* extensions_set =
764 extension_service->extensions();
765 ExtensionGalleriesHostMap::const_iterator host_map_it =
766 extension_hosts_map_.find(profile);
767 DCHECK(host_map_it != extension_hosts_map_.end());
768 const ExtensionHostMap& extension_host_map = host_map_it->second;
770 // Go through ExtensionHosts, and remove indicated gallery, if any.
771 // RevokeGalleryByPrefId() may end up deleting from |extension_host_map| and
772 // even delete |extension_host_map| altogether. So do this in two loops to
773 // avoid using an invalidated iterator or deleted map.
774 std::vector<const extensions::Extension*> extensions;
775 for (ExtensionHostMap::const_iterator it = extension_host_map.begin();
776 it != extension_host_map.end();
777 ++it) {
778 extensions.push_back(extensions_set->GetByID(it->first));
780 for (size_t i = 0; i < extensions.size(); ++i) {
781 if (!ContainsKey(extension_hosts_map_, profile))
782 break;
783 ExtensionHostMap::const_iterator gallery_host_it =
784 extension_host_map.find(extensions[i]->id());
785 if (gallery_host_it == extension_host_map.end())
786 continue;
787 gallery_host_it->second->RevokeGalleryByPrefId(pref_id);
791 ExtensionGalleriesHost* MediaFileSystemRegistry::GetExtensionGalleryHost(
792 Profile* profile,
793 MediaGalleriesPreferences* preferences,
794 const std::string& extension_id) {
795 ExtensionGalleriesHostMap::iterator extension_hosts =
796 extension_hosts_map_.find(profile);
797 // GetPreferences(), which had to be called because preferences is an
798 // argument, ensures that profile is in the map.
799 DCHECK(extension_hosts != extension_hosts_map_.end());
800 if (extension_hosts->second.empty())
801 preferences->AddGalleryChangeObserver(this);
803 ExtensionGalleriesHost* result = extension_hosts->second[extension_id].get();
804 if (!result) {
805 result = new ExtensionGalleriesHost(
806 file_system_context_.get(),
807 profile->GetPath(),
808 extension_id,
809 base::Bind(&MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty,
810 base::Unretained(this),
811 profile,
812 extension_id));
813 extension_hosts_map_[profile][extension_id] = result;
815 return result;
818 void MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty(
819 Profile* profile, const std::string& extension_id) {
820 DCHECK_CURRENTLY_ON(BrowserThread::UI);
822 ExtensionGalleriesHostMap::iterator extension_hosts =
823 extension_hosts_map_.find(profile);
824 DCHECK(extension_hosts != extension_hosts_map_.end());
825 ExtensionHostMap::size_type erase_count =
826 extension_hosts->second.erase(extension_id);
827 DCHECK_EQ(1U, erase_count);
828 if (extension_hosts->second.empty()) {
829 // When a profile has no ExtensionGalleriesHosts left, remove the
830 // matching gallery-change-watcher since it is no longer needed. Leave the
831 // |extension_hosts| entry alone, since it indicates the profile has been
832 // previously used.
833 MediaGalleriesPreferences* preferences = GetPreferences(profile);
834 preferences->RemoveGalleryChangeObserver(this);