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