1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/media_galleries/media_file_system_registry.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/files/file_path.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/stl_util.h"
15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h"
17 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
18 #include "chrome/browser/media_galleries/media_file_system_context.h"
19 #include "chrome/browser/media_galleries/media_galleries_dialog_controller.h"
20 #include "chrome/browser/media_galleries/media_galleries_histograms.h"
21 #include "chrome/browser/media_galleries/media_galleries_preferences_factory.h"
22 #include "chrome/browser/media_galleries/media_scan_manager.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/common/chrome_paths.h"
25 #include "chrome/common/extensions/extension_constants.h"
26 #include "chrome/common/pref_names.h"
27 #include "components/storage_monitor/media_storage_util.h"
28 #include "components/storage_monitor/storage_monitor.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "content/public/browser/navigation_details.h"
31 #include "content/public/browser/render_process_host.h"
32 #include "content/public/browser/render_process_host_observer.h"
33 #include "content/public/browser/render_view_host.h"
34 #include "content/public/browser/web_contents.h"
35 #include "content/public/browser/web_contents_observer.h"
36 #include "extensions/browser/extension_system.h"
37 #include "extensions/common/extension.h"
38 #include "extensions/common/extension_set.h"
39 #include "webkit/browser/fileapi/isolated_context.h"
40 #include "webkit/common/fileapi/file_system_types.h"
42 using content::BrowserThread;
43 using content::NavigationController;
44 using content::RenderProcessHost;
45 using content::WebContents;
46 using fileapi::IsolatedContext;
47 using storage_monitor::MediaStorageUtil;
48 using storage_monitor::StorageInfo;
49 using storage_monitor::StorageMonitor;
53 struct InvalidatedGalleriesInfo {
54 std::set<ExtensionGalleriesHost*> extension_hosts;
55 std::set<MediaGalleryPrefId> pref_ids;
58 // Tracks the liveness of multiple RenderProcessHosts that the caller is
59 // interested in. Once all of the RPHs have closed or been destroyed a call
60 // back informs the caller.
61 class RPHReferenceManager {
63 // |no_references_callback| is called when the last RenderViewHost reference
64 // goes away. RenderViewHost references are added through ReferenceFromRVH().
65 explicit RPHReferenceManager(const base::Closure& no_references_callback);
66 virtual ~RPHReferenceManager();
68 // Remove all references, but don't call |no_references_callback|.
69 void Reset() { STLDeleteValues(&observer_map_); }
71 // Returns true if there are no references;
72 bool empty() const { return observer_map_.empty(); }
74 // Adds a reference to the passed |rvh|. Calling this multiple times with
75 // the same |rvh| is a no-op.
76 void ReferenceFromRVH(const content::RenderViewHost* rvh);
79 class RPHWebContentsObserver : public content::WebContentsObserver {
81 RPHWebContentsObserver(RPHReferenceManager* manager,
82 WebContents* web_contents);
85 // content::WebContentsObserver
86 virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
87 virtual void NavigationEntryCommitted(
88 const content::LoadCommittedDetails& load_details) OVERRIDE;
90 RPHReferenceManager* manager_;
93 class RPHObserver : public content::RenderProcessHostObserver {
95 RPHObserver(RPHReferenceManager* manager, RenderProcessHost* host);
96 virtual ~RPHObserver();
98 void AddWebContentsObserver(WebContents* web_contents);
99 void RemoveWebContentsObserver(WebContents* web_contents);
100 bool HasWebContentsObservers() {
101 return observed_web_contentses_.size() > 0;
105 virtual void RenderProcessHostDestroyed(RenderProcessHost* host) OVERRIDE;
107 RPHReferenceManager* manager_;
108 RenderProcessHost* host_;
109 typedef std::map<WebContents*, RPHWebContentsObserver*> WCObserverMap;
110 WCObserverMap observed_web_contentses_;
112 typedef std::map<const RenderProcessHost*, RPHObserver*> RPHObserverMap;
114 // Handlers for observed events.
115 void OnRenderProcessHostDestroyed(RenderProcessHost* rph);
116 void OnWebContentsDestroyedOrNavigated(WebContents* contents);
118 // A callback to call when the last RVH reference goes away.
119 base::Closure no_references_callback_;
121 // The set of render processes and web contents that may have references to
122 // the file system ids this instance manages.
123 RPHObserverMap observer_map_;
126 RPHReferenceManager::RPHReferenceManager(
127 const base::Closure& no_references_callback)
128 : no_references_callback_(no_references_callback) {
131 RPHReferenceManager::~RPHReferenceManager() {
135 void RPHReferenceManager::ReferenceFromRVH(const content::RenderViewHost* rvh) {
136 WebContents* contents = WebContents::FromRenderViewHost(rvh);
137 RenderProcessHost* rph = contents->GetRenderProcessHost();
138 RPHObserver* state = NULL;
139 if (!ContainsKey(observer_map_, rph)) {
140 state = new RPHObserver(this, rph);
141 observer_map_[rph] = state;
143 state = observer_map_[rph];
146 state->AddWebContentsObserver(contents);
149 RPHReferenceManager::RPHWebContentsObserver::RPHWebContentsObserver(
150 RPHReferenceManager* manager,
151 WebContents* web_contents)
152 : content::WebContentsObserver(web_contents),
156 void RPHReferenceManager::RPHWebContentsObserver::WebContentsDestroyed(
157 WebContents* web_contents) {
158 manager_->OnWebContentsDestroyedOrNavigated(web_contents);
161 void RPHReferenceManager::RPHWebContentsObserver::NavigationEntryCommitted(
162 const content::LoadCommittedDetails& load_details) {
163 if (load_details.is_in_page)
166 manager_->OnWebContentsDestroyedOrNavigated(web_contents());
169 RPHReferenceManager::RPHObserver::RPHObserver(
170 RPHReferenceManager* manager, RenderProcessHost* host)
173 host->AddObserver(this);
176 RPHReferenceManager::RPHObserver::~RPHObserver() {
177 STLDeleteValues(&observed_web_contentses_);
179 host_->RemoveObserver(this);
182 void RPHReferenceManager::RPHObserver::AddWebContentsObserver(
183 WebContents* web_contents) {
184 if (ContainsKey(observed_web_contentses_, web_contents))
187 RPHWebContentsObserver* observer =
188 new RPHWebContentsObserver(manager_, web_contents);
189 observed_web_contentses_[web_contents] = observer;
192 void RPHReferenceManager::RPHObserver::RemoveWebContentsObserver(
193 WebContents* web_contents) {
194 WCObserverMap::iterator wco_iter =
195 observed_web_contentses_.find(web_contents);
196 DCHECK(wco_iter != observed_web_contentses_.end());
197 delete wco_iter->second;
198 observed_web_contentses_.erase(wco_iter);
201 void RPHReferenceManager::RPHObserver::RenderProcessHostDestroyed(
202 RenderProcessHost* host) {
204 manager_->OnRenderProcessHostDestroyed(host);
207 void RPHReferenceManager::OnRenderProcessHostDestroyed(
208 RenderProcessHost* rph) {
209 RPHObserverMap::iterator rph_info = observer_map_.find(rph);
210 // This could be a potential problem if the RPH is navigated to a page on the
211 // same renderer (triggering OnWebContentsDestroyedOrNavigated()) and then the
213 if (rph_info == observer_map_.end()) {
217 delete rph_info->second;
218 observer_map_.erase(rph_info);
219 if (observer_map_.empty())
220 no_references_callback_.Run();
223 void RPHReferenceManager::OnWebContentsDestroyedOrNavigated(
224 WebContents* contents) {
225 RenderProcessHost* rph = contents->GetRenderProcessHost();
226 RPHObserverMap::iterator rph_info = observer_map_.find(rph);
227 DCHECK(rph_info != observer_map_.end());
229 rph_info->second->RemoveWebContentsObserver(contents);
230 if (!rph_info->second->HasWebContentsObservers())
231 OnRenderProcessHostDestroyed(rph);
236 MediaFileSystemInfo::MediaFileSystemInfo(const base::string16& fs_name,
237 const base::FilePath& fs_path,
238 const std::string& filesystem_id,
239 MediaGalleryPrefId pref_id,
240 const std::string& transient_device_id,
247 transient_device_id(transient_device_id),
248 removable(removable),
249 media_device(media_device) {
252 MediaFileSystemInfo::MediaFileSystemInfo() {}
253 MediaFileSystemInfo::~MediaFileSystemInfo() {}
255 // The main owner of this class is
256 // |MediaFileSystemRegistry::extension_hosts_map_|, but a callback may
257 // temporarily hold a reference.
258 class ExtensionGalleriesHost
259 : public base::RefCountedThreadSafe<ExtensionGalleriesHost> {
261 // |no_references_callback| is called when the last RenderViewHost reference
262 // goes away. RenderViewHost references are added through ReferenceFromRVH().
263 ExtensionGalleriesHost(MediaFileSystemContext* file_system_context,
264 const base::Closure& no_references_callback)
265 : file_system_context_(file_system_context),
266 no_references_callback_(no_references_callback),
267 rph_refs_(base::Bind(&ExtensionGalleriesHost::CleanUp,
268 base::Unretained(this))) {
271 // For each gallery in the list of permitted |galleries|, checks if the
272 // device is attached and if so looks up or creates a file system id and
273 // passes the information needed for the renderer to create those file
274 // system objects to the |callback|.
275 void GetMediaFileSystems(const MediaGalleryPrefIdSet& galleries,
276 const MediaGalleriesPrefInfoMap& galleries_info,
277 const MediaFileSystemsCallback& callback) {
278 // Extract all the device ids so we can make sure they are attached.
279 MediaStorageUtil::DeviceIdSet* device_ids =
280 new MediaStorageUtil::DeviceIdSet;
281 for (std::set<MediaGalleryPrefId>::const_iterator id = galleries.begin();
282 id != galleries.end();
284 device_ids->insert(galleries_info.find(*id)->second.device_id);
286 MediaStorageUtil::FilterAttachedDevices(device_ids, base::Bind(
287 &ExtensionGalleriesHost::GetMediaFileSystemsForAttachedDevices, this,
288 base::Owned(device_ids), galleries, galleries_info, callback));
291 // Revoke the file system for |id| if this extension has created one for |id|.
292 void RevokeGalleryByPrefId(MediaGalleryPrefId id) {
293 PrefIdFsInfoMap::iterator gallery = pref_id_map_.find(id);
294 if (gallery == pref_id_map_.end())
297 file_system_context_->RevokeFileSystem(gallery->second.fsid);
298 pref_id_map_.erase(gallery);
300 if (pref_id_map_.empty()) {
306 // Indicate that the passed |rvh| will reference the file system ids created
308 void ReferenceFromRVH(const content::RenderViewHost* rvh) {
309 rph_refs_.ReferenceFromRVH(rvh);
313 typedef std::map<MediaGalleryPrefId, MediaFileSystemInfo> PrefIdFsInfoMap;
315 // Private destructor and friend declaration for ref counted implementation.
316 friend class base::RefCountedThreadSafe<ExtensionGalleriesHost>;
318 virtual ~ExtensionGalleriesHost() {
319 DCHECK(rph_refs_.empty());
320 DCHECK(pref_id_map_.empty());
323 void GetMediaFileSystemsForAttachedDevices(
324 const MediaStorageUtil::DeviceIdSet* attached_devices,
325 const MediaGalleryPrefIdSet& galleries,
326 const MediaGalleriesPrefInfoMap& galleries_info,
327 const MediaFileSystemsCallback& callback) {
328 std::vector<MediaFileSystemInfo> result;
330 if (rph_refs_.empty()) {
331 // We're actually in the middle of shutdown, and Filter...() lagging
332 // which can invoke this method interleaved in the destruction callback
333 // sequence and re-populate pref_id_map_.
334 callback.Run(result);
338 for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it =
340 pref_id_it != galleries.end();
342 const MediaGalleryPrefId& pref_id = *pref_id_it;
343 const MediaGalleryPrefInfo& gallery_info =
344 galleries_info.find(pref_id)->second;
345 const std::string& device_id = gallery_info.device_id;
346 if (!ContainsKey(*attached_devices, device_id))
349 PrefIdFsInfoMap::const_iterator existing_info =
350 pref_id_map_.find(pref_id);
351 if (existing_info != pref_id_map_.end()) {
352 result.push_back(existing_info->second);
356 base::FilePath path = gallery_info.AbsolutePath();
357 if (!MediaStorageUtil::CanCreateFileSystem(device_id, path))
361 file_system_context_->RegisterFileSystem(device_id, path);
365 MediaFileSystemInfo new_entry(
366 gallery_info.GetGalleryDisplayName(),
370 GetTransientIdForRemovableDeviceId(device_id),
371 StorageInfo::IsRemovableDevice(device_id),
372 StorageInfo::IsMediaDevice(device_id));
373 result.push_back(new_entry);
374 pref_id_map_[pref_id] = new_entry;
377 if (result.size() == 0) {
382 DCHECK_EQ(pref_id_map_.size(), result.size());
383 callback.Run(result);
386 std::string GetTransientIdForRemovableDeviceId(const std::string& device_id) {
387 if (!StorageInfo::IsRemovableDevice(device_id))
388 return std::string();
390 return StorageMonitor::GetInstance()->GetTransientIdForDeviceId(device_id);
394 DCHECK(rph_refs_.empty());
395 for (PrefIdFsInfoMap::const_iterator it = pref_id_map_.begin();
396 it != pref_id_map_.end();
398 file_system_context_->RevokeFileSystem(it->second.fsid);
400 pref_id_map_.clear();
402 no_references_callback_.Run();
405 // MediaFileSystemRegistry owns |this| and |file_system_context_|, so it's
406 // safe to store a raw pointer.
407 MediaFileSystemContext* file_system_context_;
409 // A callback to call when the last RVH reference goes away.
410 base::Closure no_references_callback_;
412 // A map from the gallery preferences id to the file system information.
413 PrefIdFsInfoMap pref_id_map_;
415 // The set of render processes and web contents that may have references to
416 // the file system ids this instance manages.
417 RPHReferenceManager rph_refs_;
419 DISALLOW_COPY_AND_ASSIGN(ExtensionGalleriesHost);
426 void MediaFileSystemRegistry::GetMediaFileSystemsForExtension(
427 const content::RenderViewHost* rvh,
428 const extensions::Extension* extension,
429 const MediaFileSystemsCallback& callback) {
430 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
433 Profile::FromBrowserContext(rvh->GetProcess()->GetBrowserContext());
434 MediaGalleriesPreferences* preferences = GetPreferences(profile);
435 MediaGalleryPrefIdSet galleries =
436 preferences->GalleriesForExtension(*extension);
438 if (galleries.empty()) {
439 callback.Run(std::vector<MediaFileSystemInfo>());
443 ExtensionGalleriesHostMap::iterator extension_hosts =
444 extension_hosts_map_.find(profile);
445 if (extension_hosts->second.empty())
446 preferences->AddGalleryChangeObserver(this);
448 ExtensionGalleriesHost* extension_host =
449 extension_hosts->second[extension->id()].get();
450 if (!extension_host) {
451 extension_host = new ExtensionGalleriesHost(
452 file_system_context_.get(),
453 base::Bind(&MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty,
454 base::Unretained(this),
457 extension_hosts_map_[profile][extension->id()] = extension_host;
459 // This must come before the GetMediaFileSystems call to make sure the
460 // RVH of the context is referenced before the filesystems are retrieved.
461 extension_host->ReferenceFromRVH(rvh);
463 extension_host->GetMediaFileSystems(galleries, preferences->known_galleries(),
467 MediaGalleriesPreferences* MediaFileSystemRegistry::GetPreferences(
469 // Create an empty ExtensionHostMap for this profile on first initialization.
470 if (!ContainsKey(extension_hosts_map_, profile)) {
471 extension_hosts_map_[profile] = ExtensionHostMap();
472 media_galleries::UsageCount(media_galleries::PROFILES_WITH_USAGE);
475 return MediaGalleriesPreferencesFactory::GetForProfile(profile);
478 MediaScanManager* MediaFileSystemRegistry::media_scan_manager() {
479 if (!media_scan_manager_)
480 media_scan_manager_.reset(new MediaScanManager);
481 return media_scan_manager_.get();
484 void MediaFileSystemRegistry::OnRemovableStorageDetached(
485 const StorageInfo& info) {
486 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
488 // Since revoking a gallery in the ExtensionGalleriesHost may cause it
489 // to be removed from the map and therefore invalidate any iterator pointing
490 // to it, this code first copies all the invalid gallery ids and the
491 // extension hosts in which they may appear (per profile) and revoked it in
493 std::vector<InvalidatedGalleriesInfo> invalid_galleries_info;
495 for (ExtensionGalleriesHostMap::iterator profile_it =
496 extension_hosts_map_.begin();
497 profile_it != extension_hosts_map_.end();
499 MediaGalleriesPreferences* preferences = GetPreferences(profile_it->first);
500 // If |preferences| is not yet initialized, it won't contain any galleries.
501 if (!preferences->IsInitialized())
504 InvalidatedGalleriesInfo invalid_galleries_in_profile;
505 invalid_galleries_in_profile.pref_ids =
506 preferences->LookUpGalleriesByDeviceId(info.device_id());
508 for (ExtensionHostMap::const_iterator extension_host_it =
509 profile_it->second.begin();
510 extension_host_it != profile_it->second.end();
511 ++extension_host_it) {
512 invalid_galleries_in_profile.extension_hosts.insert(
513 extension_host_it->second.get());
516 invalid_galleries_info.push_back(invalid_galleries_in_profile);
519 for (size_t i = 0; i < invalid_galleries_info.size(); i++) {
520 for (std::set<ExtensionGalleriesHost*>::const_iterator extension_host_it =
521 invalid_galleries_info[i].extension_hosts.begin();
522 extension_host_it != invalid_galleries_info[i].extension_hosts.end();
523 ++extension_host_it) {
524 for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it =
525 invalid_galleries_info[i].pref_ids.begin();
526 pref_id_it != invalid_galleries_info[i].pref_ids.end();
528 (*extension_host_it)->RevokeGalleryByPrefId(*pref_id_it);
538 class MediaFileSystemRegistry::MediaFileSystemContextImpl
539 : public MediaFileSystemContext {
541 explicit MediaFileSystemContextImpl(MediaFileSystemRegistry* registry)
542 : registry_(registry) {
543 DCHECK(registry_); // Suppresses unused warning on Android.
545 virtual ~MediaFileSystemContextImpl() {}
547 virtual std::string RegisterFileSystem(
548 const std::string& device_id, const base::FilePath& path) OVERRIDE {
549 if (StorageInfo::IsMassStorageDevice(device_id)) {
550 return RegisterFileSystemForMassStorage(device_id, path);
552 return RegisterFileSystemForMTPDevice(device_id, path);
556 virtual void RevokeFileSystem(const std::string& fsid) OVERRIDE {
557 ImportedMediaGalleryRegistry* imported_registry =
558 ImportedMediaGalleryRegistry::GetInstance();
559 if (imported_registry->RevokeImportedFilesystemOnUIThread(fsid))
562 IsolatedContext::GetInstance()->RevokeFileSystem(fsid);
564 content::BrowserThread::PostTask(
565 content::BrowserThread::IO, FROM_HERE, base::Bind(
566 &MTPDeviceMapService::RevokeMTPFileSystem,
567 base::Unretained(MTPDeviceMapService::GetInstance()),
572 // Registers and returns the file system id for the mass storage device
573 // specified by |device_id| and |path|.
574 std::string RegisterFileSystemForMassStorage(
575 const std::string& device_id, const base::FilePath& path) {
576 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
577 DCHECK(StorageInfo::IsMassStorageDevice(device_id));
579 // Sanity checks for |path|.
580 CHECK(path.IsAbsolute());
581 CHECK(!path.ReferencesParent());
583 // TODO(gbillock): refactor ImportedMediaGalleryRegistry to delegate this
584 // call tree, probably by having it figure out by device id what
585 // registration is needed, or having per-device-type handlers at the
586 // next higher level.
588 if (StorageInfo::IsITunesDevice(device_id)) {
589 ImportedMediaGalleryRegistry* imported_registry =
590 ImportedMediaGalleryRegistry::GetInstance();
591 fsid = imported_registry->RegisterITunesFilesystemOnUIThread(path);
592 } else if (StorageInfo::IsPicasaDevice(device_id)) {
593 ImportedMediaGalleryRegistry* imported_registry =
594 ImportedMediaGalleryRegistry::GetInstance();
595 fsid = imported_registry->RegisterPicasaFilesystemOnUIThread(
597 } else if (StorageInfo::IsIPhotoDevice(device_id)) {
598 ImportedMediaGalleryRegistry* imported_registry =
599 ImportedMediaGalleryRegistry::GetInstance();
600 fsid = imported_registry->RegisterIPhotoFilesystemOnUIThread(
603 std::string fs_name(extension_misc::kMediaFileSystemPathPart);
604 fsid = IsolatedContext::GetInstance()->RegisterFileSystemForPath(
605 fileapi::kFileSystemTypeNativeMedia, path, &fs_name);
610 std::string RegisterFileSystemForMTPDevice(
611 const std::string& device_id, const base::FilePath& path) {
612 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
613 DCHECK(!StorageInfo::IsMassStorageDevice(device_id));
615 // Sanity checks for |path|.
616 CHECK(MediaStorageUtil::CanCreateFileSystem(device_id, path));
617 std::string fs_name(extension_misc::kMediaFileSystemPathPart);
618 const std::string fsid =
619 IsolatedContext::GetInstance()->RegisterFileSystemForPath(
620 fileapi::kFileSystemTypeDeviceMedia, path, &fs_name);
621 CHECK(!fsid.empty());
622 content::BrowserThread::PostTask(
623 content::BrowserThread::IO, FROM_HERE, base::Bind(
624 &MTPDeviceMapService::RegisterMTPFileSystem,
625 base::Unretained(MTPDeviceMapService::GetInstance()),
626 path.value(), fsid));
630 MediaFileSystemRegistry* registry_;
632 DISALLOW_COPY_AND_ASSIGN(MediaFileSystemContextImpl);
635 // Constructor in 'private' section because depends on private class definition.
636 MediaFileSystemRegistry::MediaFileSystemRegistry()
637 : file_system_context_(new MediaFileSystemContextImpl(this)) {
638 StorageMonitor::GetInstance()->AddObserver(this);
641 MediaFileSystemRegistry::~MediaFileSystemRegistry() {
642 // TODO(gbillock): This is needed because the unit test uses the
643 // g_browser_process registry. We should create one in the unit test,
644 // and then can remove this.
645 if (StorageMonitor::GetInstance())
646 StorageMonitor::GetInstance()->RemoveObserver(this);
649 void MediaFileSystemRegistry::OnPermissionRemoved(
650 MediaGalleriesPreferences* prefs,
651 const std::string& extension_id,
652 MediaGalleryPrefId pref_id) {
653 Profile* profile = prefs->profile();
654 ExtensionGalleriesHostMap::const_iterator host_map_it =
655 extension_hosts_map_.find(profile);
656 DCHECK(host_map_it != extension_hosts_map_.end());
657 const ExtensionHostMap& extension_host_map = host_map_it->second;
658 ExtensionHostMap::const_iterator gallery_host_it =
659 extension_host_map.find(extension_id);
660 if (gallery_host_it == extension_host_map.end())
662 gallery_host_it->second->RevokeGalleryByPrefId(pref_id);
665 void MediaFileSystemRegistry::OnGalleryRemoved(
666 MediaGalleriesPreferences* prefs,
667 MediaGalleryPrefId pref_id) {
668 Profile* profile = prefs->profile();
669 // Get the Extensions, MediaGalleriesPreferences and ExtensionHostMap for
671 const ExtensionService* extension_service =
672 extensions::ExtensionSystem::Get(profile)->extension_service();
673 const extensions::ExtensionSet* extensions_set =
674 extension_service->extensions();
675 ExtensionGalleriesHostMap::const_iterator host_map_it =
676 extension_hosts_map_.find(profile);
677 DCHECK(host_map_it != extension_hosts_map_.end());
678 const ExtensionHostMap& extension_host_map = host_map_it->second;
680 // Go through ExtensionHosts, and remove indicated gallery, if any.
681 // RevokeGalleryByPrefId() may end up deleting from |extension_host_map| and
682 // even delete |extension_host_map| altogether. So do this in two loops to
683 // avoid using an invalidated iterator or deleted map.
684 std::vector<const extensions::Extension*> extensions;
685 for (ExtensionHostMap::const_iterator it = extension_host_map.begin();
686 it != extension_host_map.end();
688 extensions.push_back(extensions_set->GetByID(it->first));
690 for (size_t i = 0; i < extensions.size(); ++i) {
691 if (!ContainsKey(extension_hosts_map_, profile))
693 ExtensionHostMap::const_iterator gallery_host_it =
694 extension_host_map.find(extensions[i]->id());
695 if (gallery_host_it == extension_host_map.end())
697 gallery_host_it->second->RevokeGalleryByPrefId(pref_id);
701 void MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty(
702 Profile* profile, const std::string& extension_id) {
703 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
705 ExtensionGalleriesHostMap::iterator extension_hosts =
706 extension_hosts_map_.find(profile);
707 DCHECK(extension_hosts != extension_hosts_map_.end());
708 ExtensionHostMap::size_type erase_count =
709 extension_hosts->second.erase(extension_id);
710 DCHECK_EQ(1U, erase_count);
711 if (extension_hosts->second.empty()) {
712 // When a profile has no ExtensionGalleriesHosts left, remove the
713 // matching gallery-change-watcher since it is no longer needed. Leave the
714 // |extension_hosts| entry alone, since it indicates the profile has been
716 MediaGalleriesPreferences* preferences = GetPreferences(profile);
717 preferences->RemoveGalleryChangeObserver(this);