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_constants.h" 31 #include "chrome/common/extensions/extension_set.h" 32 #include "chrome/common/pref_names.h" 33 #include "content/public/browser/browser_thread.h" 34 #include "content/public/browser/navigation_details.h" 35 #include "content/public/browser/notification_details.h" 36 #include "content/public/browser/notification_observer.h" 37 #include "content/public/browser/notification_registrar.h" 38 #include "content/public/browser/notification_source.h" 39 #include "content/public/browser/notification_types.h" 40 #include "content/public/browser/render_process_host.h" 41 #include "content/public/browser/render_view_host.h" 42 #include "content/public/browser/web_contents.h" 43 #include "extensions/common/extension.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 base::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()->RegisterFileSystemForPath( 576 fileapi::kFileSystemTypeDeviceMedia, path, &fs_name); 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 } 675