- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / media_galleries / media_file_system_registry.cc
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.
4
5 // MediaFileSystemRegistry implementation.
6
7 #include "chrome/browser/media_galleries/media_file_system_registry.h"
8
9 #include <set>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/callback.h"
14 #include "base/files/file_path.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/stl_util.h"
17 #include "chrome/browser/chrome_notification_types.h"
18 #include "chrome/browser/extensions/extension_service.h"
19 #include "chrome/browser/extensions/extension_system.h"
20 #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h"
21 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
22 #include "chrome/browser/media_galleries/media_file_system_context.h"
23 #include "chrome/browser/media_galleries/media_galleries_dialog_controller.h"
24 #include "chrome/browser/media_galleries/media_galleries_histograms.h"
25 #include "chrome/browser/media_galleries/media_galleries_preferences_factory.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/storage_monitor/media_storage_util.h"
28 #include "chrome/browser/storage_monitor/storage_monitor.h"
29 #include "chrome/common/chrome_paths.h"
30 #include "chrome/common/extensions/extension.h"
31 #include "chrome/common/extensions/extension_constants.h"
32 #include "chrome/common/extensions/extension_set.h"
33 #include "chrome/common/pref_names.h"
34 #include "content/public/browser/browser_thread.h"
35 #include "content/public/browser/navigation_details.h"
36 #include "content/public/browser/notification_details.h"
37 #include "content/public/browser/notification_observer.h"
38 #include "content/public/browser/notification_registrar.h"
39 #include "content/public/browser/notification_source.h"
40 #include "content/public/browser/notification_types.h"
41 #include "content/public/browser/render_process_host.h"
42 #include "content/public/browser/render_view_host.h"
43 #include "content/public/browser/web_contents.h"
44 #include "webkit/browser/fileapi/isolated_context.h"
45 #include "webkit/common/fileapi/file_system_types.h"
46
47 using content::BrowserThread;
48 using content::NavigationController;
49 using content::RenderProcessHost;
50 using content::WebContents;
51 using fileapi::IsolatedContext;
52
53 namespace {
54
55 struct InvalidatedGalleriesInfo {
56   std::set<ExtensionGalleriesHost*> extension_hosts;
57   std::set<MediaGalleryPrefId> pref_ids;
58 };
59
60 // Tracks the liveness of multiple RenderProcessHosts that the caller is
61 // interested in. Once all of the RPHs have closed or been terminated a call
62 // back informs the caller.
63 class RPHReferenceManager : public content::NotificationObserver {
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       : no_references_callback_(no_references_callback) {
69   }
70
71   virtual ~RPHReferenceManager() {
72     Reset();
73   }
74
75   // Remove all references, but don't call |no_references_callback|.
76   void Reset() {
77     STLDeleteValues(&refs_);
78   }
79
80   // Returns true if there are no references;
81   bool empty() const {
82     return refs_.empty();
83   }
84
85   // Adds a reference to the passed |rvh|. Calling this multiple times with
86   // the same |rvh| is a no-op.
87   void ReferenceFromRVH(const content::RenderViewHost* rvh) {
88     WebContents* contents = WebContents::FromRenderViewHost(rvh);
89     RenderProcessHost* rph = contents->GetRenderProcessHost();
90     RPHReferenceState* state = NULL;
91     if (!ContainsKey(refs_, rph)) {
92       state = new RPHReferenceState;
93       refs_[rph] = state;
94       state->registrar.Add(
95           this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
96           content::Source<RenderProcessHost>(rph));
97     } else {
98       state = refs_[rph];
99     }
100
101     if (state->web_contents_set.insert(contents).second) {
102       state->registrar.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
103           content::Source<WebContents>(contents));
104       state->registrar.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
105           content::Source<NavigationController>(&contents->GetController()));
106     }
107   }
108
109  private:
110   struct RPHReferenceState {
111     content::NotificationRegistrar registrar;
112     std::set<const WebContents*> web_contents_set;
113   };
114   typedef std::map<const RenderProcessHost*, RPHReferenceState*> RPHRefCount;
115
116   // NotificationObserver implementation.
117   virtual void Observe(int type,
118                        const content::NotificationSource& source,
119                        const content::NotificationDetails& details) OVERRIDE {
120     switch (type) {
121       case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
122         OnRendererProcessTerminated(
123             content::Source<RenderProcessHost>(source).ptr());
124         break;
125       }
126       case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: {
127         OnWebContentsDestroyedOrNavigated(
128             content::Source<WebContents>(source).ptr());
129         break;
130       }
131       case content::NOTIFICATION_NAV_ENTRY_COMMITTED: {
132         content::LoadCommittedDetails* load_details =
133             content::Details<content::LoadCommittedDetails>(details).ptr();
134         if (load_details->is_in_page)
135           break;
136         NavigationController* controller =
137             content::Source<NavigationController>(source).ptr();
138         WebContents* contents = controller->GetWebContents();
139         OnWebContentsDestroyedOrNavigated(contents);
140         break;
141       }
142       default: {
143         NOTREACHED();
144         break;
145       }
146     }
147   }
148
149   void OnRendererProcessTerminated(const RenderProcessHost* rph) {
150     RPHRefCount::iterator rph_info = refs_.find(rph);
151     DCHECK(rph_info != refs_.end());
152     delete rph_info->second;
153     refs_.erase(rph_info);
154     if (refs_.empty())
155       no_references_callback_.Run();
156   }
157
158   void OnWebContentsDestroyedOrNavigated(const WebContents* contents) {
159     RenderProcessHost* rph = contents->GetRenderProcessHost();
160     RPHRefCount::iterator rph_info = refs_.find(rph);
161     DCHECK(rph_info != refs_.end());
162
163     rph_info->second->registrar.Remove(
164         this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
165         content::Source<WebContents>(contents));
166     rph_info->second->registrar.Remove(
167         this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
168         content::Source<NavigationController>(&contents->GetController()));
169
170     rph_info->second->web_contents_set.erase(contents);
171     if (rph_info->second->web_contents_set.empty())
172       OnRendererProcessTerminated(rph);
173   }
174
175   // A callback to call when the last RVH reference goes away.
176   base::Closure no_references_callback_;
177
178   // The set of render processes and web contents that may have references to
179   // the file system ids this instance manages.
180   RPHRefCount refs_;
181 };
182
183 }  // namespace
184
185 MediaFileSystemInfo::MediaFileSystemInfo(const string16& fs_name,
186                                          const base::FilePath& fs_path,
187                                          const std::string& filesystem_id,
188                                          MediaGalleryPrefId pref_id,
189                                          const std::string& transient_device_id,
190                                          bool removable,
191                                          bool media_device)
192     : name(fs_name),
193       path(fs_path),
194       fsid(filesystem_id),
195       pref_id(pref_id),
196       transient_device_id(transient_device_id),
197       removable(removable),
198       media_device(media_device) {
199 }
200
201 MediaFileSystemInfo::MediaFileSystemInfo() {}
202 MediaFileSystemInfo::~MediaFileSystemInfo() {}
203
204 // The main owner of this class is
205 // |MediaFileSystemRegistry::extension_hosts_map_|, but a callback may
206 // temporarily hold a reference.
207 class ExtensionGalleriesHost
208     : public base::RefCountedThreadSafe<ExtensionGalleriesHost> {
209  public:
210   // |no_references_callback| is called when the last RenderViewHost reference
211   // goes away. RenderViewHost references are added through ReferenceFromRVH().
212   ExtensionGalleriesHost(MediaFileSystemContext* file_system_context,
213                          const base::Closure& no_references_callback)
214       : file_system_context_(file_system_context),
215         no_references_callback_(no_references_callback),
216         rph_refs_(base::Bind(&ExtensionGalleriesHost::CleanUp,
217                              base::Unretained(this))) {
218   }
219
220   // For each gallery in the list of permitted |galleries|, checks if the
221   // device is attached and if so looks up or creates a file system id and
222   // passes the information needed for the renderer to create those file
223   // system objects to the |callback|.
224   void GetMediaFileSystems(const MediaGalleryPrefIdSet& galleries,
225                            const MediaGalleriesPrefInfoMap& galleries_info,
226                            const MediaFileSystemsCallback& callback) {
227     // Extract all the device ids so we can make sure they are attached.
228     MediaStorageUtil::DeviceIdSet* device_ids =
229         new MediaStorageUtil::DeviceIdSet;
230     for (std::set<MediaGalleryPrefId>::const_iterator id = galleries.begin();
231          id != galleries.end();
232          ++id) {
233       device_ids->insert(galleries_info.find(*id)->second.device_id);
234     }
235     MediaStorageUtil::FilterAttachedDevices(device_ids, base::Bind(
236         &ExtensionGalleriesHost::GetMediaFileSystemsForAttachedDevices, this,
237         base::Owned(device_ids), galleries, galleries_info, callback));
238   }
239
240   void RevokeOldGalleries(const MediaGalleryPrefIdSet& new_galleries) {
241     if (new_galleries.size() == pref_id_map_.size())
242       return;
243
244     MediaGalleryPrefIdSet old_galleries;
245     for (PrefIdFsInfoMap::const_iterator it = pref_id_map_.begin();
246          it != pref_id_map_.end();
247          ++it) {
248       old_galleries.insert(it->first);
249     }
250     MediaGalleryPrefIdSet invalid_galleries =
251         base::STLSetDifference<MediaGalleryPrefIdSet>(old_galleries,
252                                                       new_galleries);
253     for (MediaGalleryPrefIdSet::const_iterator it = invalid_galleries.begin();
254          it != invalid_galleries.end();
255          ++it) {
256       RevokeGalleryByPrefId(*it);
257     }
258   }
259
260   // Revoke the file system for |id| if this extension has created one for |id|.
261   void RevokeGalleryByPrefId(MediaGalleryPrefId id) {
262     PrefIdFsInfoMap::iterator gallery = pref_id_map_.find(id);
263     if (gallery == pref_id_map_.end())
264       return;
265
266     file_system_context_->RevokeFileSystem(gallery->second.fsid);
267     pref_id_map_.erase(gallery);
268
269     if (pref_id_map_.empty()) {
270       rph_refs_.Reset();
271       CleanUp();
272     }
273   }
274
275   // Indicate that the passed |rvh| will reference the file system ids created
276   // by this class.
277   void ReferenceFromRVH(const content::RenderViewHost* rvh) {
278     rph_refs_.ReferenceFromRVH(rvh);
279   }
280
281  private:
282   typedef std::map<MediaGalleryPrefId, MediaFileSystemInfo> PrefIdFsInfoMap;
283
284   // Private destructor and friend declaration for ref counted implementation.
285   friend class base::RefCountedThreadSafe<ExtensionGalleriesHost>;
286
287   virtual ~ExtensionGalleriesHost() {
288     DCHECK(rph_refs_.empty());
289     DCHECK(pref_id_map_.empty());
290
291   }
292
293   void GetMediaFileSystemsForAttachedDevices(
294       const MediaStorageUtil::DeviceIdSet* attached_devices,
295       const MediaGalleryPrefIdSet& galleries,
296       const MediaGalleriesPrefInfoMap& galleries_info,
297       const MediaFileSystemsCallback& callback) {
298     std::vector<MediaFileSystemInfo> result;
299     MediaGalleryPrefIdSet new_galleries;
300     for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it =
301              galleries.begin();
302          pref_id_it != galleries.end();
303          ++pref_id_it) {
304       const MediaGalleryPrefId& pref_id = *pref_id_it;
305       const MediaGalleryPrefInfo& gallery_info =
306           galleries_info.find(pref_id)->second;
307       const std::string& device_id = gallery_info.device_id;
308       if (!ContainsKey(*attached_devices, device_id))
309         continue;
310
311       PrefIdFsInfoMap::const_iterator existing_info =
312           pref_id_map_.find(pref_id);
313       if (existing_info != pref_id_map_.end()) {
314         result.push_back(existing_info->second);
315         new_galleries.insert(pref_id);
316         continue;
317       }
318
319       base::FilePath path = gallery_info.AbsolutePath();
320       if (!MediaStorageUtil::CanCreateFileSystem(device_id, path))
321         continue;
322
323       std::string fsid =
324           file_system_context_->RegisterFileSystem(device_id, path);
325       if (fsid.empty())
326         continue;
327
328       MediaFileSystemInfo new_entry(
329           gallery_info.GetGalleryDisplayName(),
330           path,
331           fsid,
332           pref_id,
333           GetTransientIdForRemovableDeviceId(device_id),
334           StorageInfo::IsRemovableDevice(device_id),
335           StorageInfo::IsMediaDevice(device_id));
336       result.push_back(new_entry);
337       new_galleries.insert(pref_id);
338       pref_id_map_[pref_id] = new_entry;
339     }
340
341     if (result.size() == 0) {
342       rph_refs_.Reset();
343       CleanUp();
344     } else {
345       RevokeOldGalleries(new_galleries);
346     }
347
348     callback.Run(result);
349   }
350
351   std::string GetTransientIdForRemovableDeviceId(const std::string& device_id) {
352     if (!StorageInfo::IsRemovableDevice(device_id))
353       return std::string();
354
355     return StorageMonitor::GetInstance()->GetTransientIdForDeviceId(device_id);
356   }
357
358   void CleanUp() {
359     DCHECK(rph_refs_.empty());
360     for (PrefIdFsInfoMap::const_iterator it = pref_id_map_.begin();
361          it != pref_id_map_.end();
362          ++it) {
363       file_system_context_->RevokeFileSystem(it->second.fsid);
364     }
365     pref_id_map_.clear();
366
367     no_references_callback_.Run();
368   }
369
370   // MediaFileSystemRegistry owns |this| and |file_system_context_|, so it's
371   // safe to store a raw pointer.
372   MediaFileSystemContext* file_system_context_;
373
374   // A callback to call when the last RVH reference goes away.
375   base::Closure no_references_callback_;
376
377   // A map from the gallery preferences id to the file system information.
378   PrefIdFsInfoMap pref_id_map_;
379
380   // The set of render processes and web contents that may have references to
381   // the file system ids this instance manages.
382   RPHReferenceManager rph_refs_;
383
384   DISALLOW_COPY_AND_ASSIGN(ExtensionGalleriesHost);
385 };
386
387 /******************
388  * Public methods
389  ******************/
390
391 void MediaFileSystemRegistry::GetMediaFileSystemsForExtension(
392     const content::RenderViewHost* rvh,
393     const extensions::Extension* extension,
394     const MediaFileSystemsCallback& callback) {
395   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
396
397   Profile* profile =
398       Profile::FromBrowserContext(rvh->GetProcess()->GetBrowserContext());
399   MediaGalleriesPreferences* preferences = GetPreferences(profile);
400   MediaGalleryPrefIdSet galleries =
401       preferences->GalleriesForExtension(*extension);
402
403   if (galleries.empty()) {
404     callback.Run(std::vector<MediaFileSystemInfo>());
405     return;
406   }
407
408   ExtensionGalleriesHostMap::iterator extension_hosts =
409       extension_hosts_map_.find(profile);
410   if (extension_hosts->second.empty())
411     preferences->AddGalleryChangeObserver(this);
412
413   ExtensionGalleriesHost* extension_host =
414       extension_hosts->second[extension->id()].get();
415   if (!extension_host) {
416     extension_host = new ExtensionGalleriesHost(
417         file_system_context_.get(),
418         base::Bind(&MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty,
419                    base::Unretained(this),
420                    profile,
421                    extension->id()));
422     extension_hosts_map_[profile][extension->id()] = extension_host;
423   }
424   extension_host->ReferenceFromRVH(rvh);
425
426   extension_host->GetMediaFileSystems(galleries, preferences->known_galleries(),
427                                       callback);
428 }
429
430 MediaGalleriesPreferences* MediaFileSystemRegistry::GetPreferences(
431     Profile* profile) {
432   // Create an empty ExtensionHostMap for this profile on first initialization.
433   if (!ContainsKey(extension_hosts_map_, profile))
434     extension_hosts_map_[profile] = ExtensionHostMap();
435   media_galleries::UsageCount(media_galleries::PROFILES_WITH_USAGE);
436
437   return MediaGalleriesPreferencesFactory::GetForProfile(profile);
438 }
439
440 void MediaFileSystemRegistry::OnRemovableStorageDetached(
441     const StorageInfo& info) {
442   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
443
444   // Since revoking a gallery in the ExtensionGalleriesHost may cause it
445   // to be removed from the map and therefore invalidate any iterator pointing
446   // to it, this code first copies all the invalid gallery ids and the
447   // extension hosts in which they may appear (per profile) and revoked it in
448   // a second step.
449   std::vector<InvalidatedGalleriesInfo> invalid_galleries_info;
450
451   for (ExtensionGalleriesHostMap::iterator profile_it =
452            extension_hosts_map_.begin();
453        profile_it != extension_hosts_map_.end();
454        ++profile_it) {
455     MediaGalleriesPreferences* preferences = GetPreferences(profile_it->first);
456     // If |preferences| is not yet initialized, it won't contain any galleries.
457     if (!preferences->IsInitialized())
458       continue;
459
460     InvalidatedGalleriesInfo invalid_galleries_in_profile;
461     invalid_galleries_in_profile.pref_ids =
462         preferences->LookUpGalleriesByDeviceId(info.device_id());
463
464     for (ExtensionHostMap::const_iterator extension_host_it =
465              profile_it->second.begin();
466          extension_host_it != profile_it->second.end();
467          ++extension_host_it) {
468       invalid_galleries_in_profile.extension_hosts.insert(
469           extension_host_it->second.get());
470     }
471
472     invalid_galleries_info.push_back(invalid_galleries_in_profile);
473   }
474
475   for (size_t i = 0; i < invalid_galleries_info.size(); i++) {
476     for (std::set<ExtensionGalleriesHost*>::const_iterator extension_host_it =
477              invalid_galleries_info[i].extension_hosts.begin();
478          extension_host_it != invalid_galleries_info[i].extension_hosts.end();
479          ++extension_host_it) {
480       for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it =
481                invalid_galleries_info[i].pref_ids.begin();
482            pref_id_it != invalid_galleries_info[i].pref_ids.end();
483            ++pref_id_it) {
484         (*extension_host_it)->RevokeGalleryByPrefId(*pref_id_it);
485       }
486     }
487   }
488 }
489
490 /******************
491  * Private methods
492  ******************/
493
494 class MediaFileSystemRegistry::MediaFileSystemContextImpl
495     : public MediaFileSystemContext {
496  public:
497   explicit MediaFileSystemContextImpl(MediaFileSystemRegistry* registry)
498       : registry_(registry) {
499     DCHECK(registry_);  // Suppresses unused warning on Android.
500   }
501   virtual ~MediaFileSystemContextImpl() {}
502
503   virtual std::string RegisterFileSystem(
504       const std::string& device_id, const base::FilePath& path) OVERRIDE {
505     if (StorageInfo::IsMassStorageDevice(device_id)) {
506       return RegisterFileSystemForMassStorage(device_id, path);
507     } else {
508       return RegisterFileSystemForMTPDevice(device_id, path);
509     }
510   }
511
512   virtual void RevokeFileSystem(const std::string& fsid) OVERRIDE {
513     ImportedMediaGalleryRegistry* imported_registry =
514         ImportedMediaGalleryRegistry::GetInstance();
515     if (imported_registry->RevokeImportedFilesystemOnUIThread(fsid))
516       return;
517
518     IsolatedContext::GetInstance()->RevokeFileSystem(fsid);
519
520     content::BrowserThread::PostTask(
521         content::BrowserThread::IO, FROM_HERE, base::Bind(
522             &MTPDeviceMapService::RevokeMTPFileSystem,
523             base::Unretained(MTPDeviceMapService::GetInstance()),
524             fsid));
525   }
526
527  private:
528   // Registers and returns the file system id for the mass storage device
529   // specified by |device_id| and |path|.
530   std::string RegisterFileSystemForMassStorage(
531       const std::string& device_id, const base::FilePath& path) {
532     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
533     DCHECK(StorageInfo::IsMassStorageDevice(device_id));
534
535     // Sanity checks for |path|.
536     CHECK(path.IsAbsolute());
537     CHECK(!path.ReferencesParent());
538
539     // TODO(gbillock): refactor ImportedMediaGalleryRegistry to delegate this
540     // call tree, probably by having it figure out by device id what
541     // registration is needed, or having per-device-type handlers at the
542     // next higher level.
543     std::string fsid;
544     if (StorageInfo::IsITunesDevice(device_id)) {
545       ImportedMediaGalleryRegistry* imported_registry =
546           ImportedMediaGalleryRegistry::GetInstance();
547       fsid = imported_registry->RegisterITunesFilesystemOnUIThread(path);
548     } else if (StorageInfo::IsPicasaDevice(device_id)) {
549       ImportedMediaGalleryRegistry* imported_registry =
550           ImportedMediaGalleryRegistry::GetInstance();
551       fsid = imported_registry->RegisterPicasaFilesystemOnUIThread(
552           path);
553     } else if (StorageInfo::IsIPhotoDevice(device_id)) {
554       ImportedMediaGalleryRegistry* imported_registry =
555           ImportedMediaGalleryRegistry::GetInstance();
556       fsid = imported_registry->RegisterIPhotoFilesystemOnUIThread(
557           path);
558     } else {
559       std::string fs_name(extension_misc::kMediaFileSystemPathPart);
560       fsid = IsolatedContext::GetInstance()->RegisterFileSystemForPath(
561           fileapi::kFileSystemTypeNativeMedia, path, &fs_name);
562     }
563     return fsid;
564   }
565
566   std::string RegisterFileSystemForMTPDevice(
567       const std::string& device_id, const base::FilePath& path) {
568     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
569     DCHECK(!StorageInfo::IsMassStorageDevice(device_id));
570
571     // Sanity checks for |path|.
572     CHECK(MediaStorageUtil::CanCreateFileSystem(device_id, path));
573     std::string fs_name(extension_misc::kMediaFileSystemPathPart);
574     const std::string fsid =
575         IsolatedContext::GetInstance()->RegisterFileSystemForVirtualPath(
576             fileapi::kFileSystemTypeDeviceMedia, fs_name, path);
577     CHECK(!fsid.empty());
578     content::BrowserThread::PostTask(
579         content::BrowserThread::IO, FROM_HERE, base::Bind(
580             &MTPDeviceMapService::RegisterMTPFileSystem,
581             base::Unretained(MTPDeviceMapService::GetInstance()),
582             path.value(), fsid));
583     return fsid;
584   }
585
586   MediaFileSystemRegistry* registry_;
587
588   DISALLOW_COPY_AND_ASSIGN(MediaFileSystemContextImpl);
589 };
590
591 // Constructor in 'private' section because depends on private class definition.
592 MediaFileSystemRegistry::MediaFileSystemRegistry()
593     : file_system_context_(new MediaFileSystemContextImpl(this)) {
594   StorageMonitor::GetInstance()->AddObserver(this);
595 }
596
597 MediaFileSystemRegistry::~MediaFileSystemRegistry() {
598   // TODO(gbillock): This is needed because the unit test uses the
599   // g_browser_process registry. We should create one in the unit test,
600   // and then can remove this.
601   if (StorageMonitor::GetInstance())
602     StorageMonitor::GetInstance()->RemoveObserver(this);
603 }
604
605 void MediaFileSystemRegistry::OnPermissionRemoved(
606     MediaGalleriesPreferences* prefs,
607     const std::string& extension_id,
608     MediaGalleryPrefId pref_id) {
609   Profile* profile = prefs->profile();
610   ExtensionGalleriesHostMap::const_iterator host_map_it =
611       extension_hosts_map_.find(profile);
612   DCHECK(host_map_it != extension_hosts_map_.end());
613   const ExtensionHostMap& extension_host_map = host_map_it->second;
614   ExtensionHostMap::const_iterator gallery_host_it =
615       extension_host_map.find(extension_id);
616   if (gallery_host_it == extension_host_map.end())
617     return;
618   gallery_host_it->second->RevokeGalleryByPrefId(pref_id);
619 }
620
621 void MediaFileSystemRegistry::OnGalleryRemoved(
622     MediaGalleriesPreferences* prefs,
623     MediaGalleryPrefId pref_id) {
624   Profile* profile = prefs->profile();
625   // Get the Extensions, MediaGalleriesPreferences and ExtensionHostMap for
626   // |profile|.
627   const ExtensionService* extension_service =
628       extensions::ExtensionSystem::Get(profile)->extension_service();
629   const ExtensionSet* extensions_set = extension_service->extensions();
630   ExtensionGalleriesHostMap::const_iterator host_map_it =
631       extension_hosts_map_.find(profile);
632   DCHECK(host_map_it != extension_hosts_map_.end());
633   const ExtensionHostMap& extension_host_map = host_map_it->second;
634
635   // Go through ExtensionHosts, and remove indicated gallery, if any.
636   // RevokeGalleryByPrefId() may end up deleting from |extension_host_map| and
637   // even delete |extension_host_map| altogether. So do this in two loops to
638   // avoid using an invalidated iterator or deleted map.
639   std::vector<const extensions::Extension*> extensions;
640   for (ExtensionHostMap::const_iterator it = extension_host_map.begin();
641        it != extension_host_map.end();
642        ++it) {
643     extensions.push_back(extensions_set->GetByID(it->first));
644   }
645   for (size_t i = 0; i < extensions.size(); ++i) {
646     if (!ContainsKey(extension_hosts_map_, profile))
647       break;
648     ExtensionHostMap::const_iterator gallery_host_it =
649         extension_host_map.find(extensions[i]->id());
650     if (gallery_host_it == extension_host_map.end())
651       continue;
652     gallery_host_it->second->RevokeGalleryByPrefId(pref_id);
653   }
654 }
655
656 void MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty(
657     Profile* profile, const std::string& extension_id) {
658   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
659
660   ExtensionGalleriesHostMap::iterator extension_hosts =
661       extension_hosts_map_.find(profile);
662   DCHECK(extension_hosts != extension_hosts_map_.end());
663   ExtensionHostMap::size_type erase_count =
664       extension_hosts->second.erase(extension_id);
665   DCHECK_EQ(1U, erase_count);
666   if (extension_hosts->second.empty()) {
667     // When a profile has no ExtensionGalleriesHosts left, remove the
668     // matching gallery-change-watcher since it is no longer needed. Leave the
669     // |extension_hosts| entry alone, since it indicates the profile has been
670     // previously used.
671     MediaGalleriesPreferences* preferences = GetPreferences(profile);
672     preferences->RemoveGalleryChangeObserver(this);
673   }
674 }