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 #include "chrome/browser/media_galleries/media_file_system_registry.h" 6 7 #include <set> 8 #include <vector> 9 10 #include "base/bind.h" 11 #include "base/callback.h" 12 #include "base/files/file_path.h" 13 #include "base/prefs/pref_service.h" 14 #include "base/stl_util.h" 15 #include "chrome/browser/extensions/extension_service.h" 16 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" 17 #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h" 18 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h" 19 #include "chrome/browser/media_galleries/media_file_system_context.h" 20 #include "chrome/browser/media_galleries/media_galleries_dialog_controller.h" 21 #include "chrome/browser/media_galleries/media_galleries_histograms.h" 22 #include "chrome/browser/media_galleries/media_galleries_preferences_factory.h" 23 #include "chrome/browser/media_galleries/media_scan_manager.h" 24 #include "chrome/browser/profiles/profile.h" 25 #include "chrome/common/chrome_paths.h" 26 #include "chrome/common/extensions/extension_constants.h" 27 #include "chrome/common/pref_names.h" 28 #include "components/storage_monitor/media_storage_util.h" 29 #include "components/storage_monitor/storage_monitor.h" 30 #include "content/public/browser/browser_thread.h" 31 #include "content/public/browser/navigation_details.h" 32 #include "content/public/browser/render_process_host.h" 33 #include "content/public/browser/render_process_host_observer.h" 34 #include "content/public/browser/render_view_host.h" 35 #include "content/public/browser/web_contents.h" 36 #include "content/public/browser/web_contents_observer.h" 37 #include "extensions/browser/extension_system.h" 38 #include "extensions/common/extension.h" 39 #include "extensions/common/extension_set.h" 40 #include "webkit/browser/fileapi/external_mount_points.h" 41 #include "webkit/common/fileapi/file_system_mount_option.h" 42 #include "webkit/common/fileapi/file_system_types.h" 43 44 using content::BrowserThread; 45 using content::NavigationController; 46 using content::RenderProcessHost; 47 using content::WebContents; 48 using fileapi::ExternalMountPoints; 49 using storage_monitor::MediaStorageUtil; 50 using storage_monitor::StorageInfo; 51 using storage_monitor::StorageMonitor; 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 destroyed a call 62 // back informs the caller. 63 class RPHReferenceManager { 64 public: 65 // |no_references_callback| is called when the last RenderViewHost reference 66 // goes away. RenderViewHost references are added through ReferenceFromRVH(). 67 explicit RPHReferenceManager(const base::Closure& no_references_callback); 68 virtual ~RPHReferenceManager(); 69 70 // Remove all references, but don't call |no_references_callback|. 71 void Reset() { STLDeleteValues(&observer_map_); } 72 73 // Returns true if there are no references; 74 bool empty() const { return observer_map_.empty(); } 75 76 // Adds a reference to the passed |rvh|. Calling this multiple times with 77 // the same |rvh| is a no-op. 78 void ReferenceFromRVH(const content::RenderViewHost* rvh); 79 80 private: 81 class RPHWebContentsObserver : public content::WebContentsObserver { 82 public: 83 RPHWebContentsObserver(RPHReferenceManager* manager, 84 WebContents* web_contents); 85 86 private: 87 // content::WebContentsObserver 88 virtual void WebContentsDestroyed() OVERRIDE; 89 virtual void NavigationEntryCommitted( 90 const content::LoadCommittedDetails& load_details) OVERRIDE; 91 92 RPHReferenceManager* manager_; 93 }; 94 95 class RPHObserver : public content::RenderProcessHostObserver { 96 public: 97 RPHObserver(RPHReferenceManager* manager, RenderProcessHost* host); 98 virtual ~RPHObserver(); 99 100 void AddWebContentsObserver(WebContents* web_contents); 101 void RemoveWebContentsObserver(WebContents* web_contents); 102 bool HasWebContentsObservers() { 103 return observed_web_contentses_.size() > 0; 104 } 105 106 private: 107 virtual void RenderProcessHostDestroyed(RenderProcessHost* host) OVERRIDE; 108 109 RPHReferenceManager* manager_; 110 RenderProcessHost* host_; 111 typedef std::map<WebContents*, RPHWebContentsObserver*> WCObserverMap; 112 WCObserverMap observed_web_contentses_; 113 }; 114 typedef std::map<const RenderProcessHost*, RPHObserver*> RPHObserverMap; 115 116 // Handlers for observed events. 117 void OnRenderProcessHostDestroyed(RenderProcessHost* rph); 118 void OnWebContentsDestroyedOrNavigated(WebContents* contents); 119 120 // A callback to call when the last RVH reference goes away. 121 base::Closure no_references_callback_; 122 123 // The set of render processes and web contents that may have references to 124 // the file system ids this instance manages. 125 RPHObserverMap observer_map_; 126 }; 127 128 RPHReferenceManager::RPHReferenceManager( 129 const base::Closure& no_references_callback) 130 : no_references_callback_(no_references_callback) { 131 } 132 133 RPHReferenceManager::~RPHReferenceManager() { 134 Reset(); 135 } 136 137 void RPHReferenceManager::ReferenceFromRVH(const content::RenderViewHost* rvh) { 138 WebContents* contents = WebContents::FromRenderViewHost(rvh); 139 RenderProcessHost* rph = contents->GetRenderProcessHost(); 140 RPHObserver* state = NULL; 141 if (!ContainsKey(observer_map_, rph)) { 142 state = new RPHObserver(this, rph); 143 observer_map_[rph] = state; 144 } else { 145 state = observer_map_[rph]; 146 } 147 148 state->AddWebContentsObserver(contents); 149 } 150 151 RPHReferenceManager::RPHWebContentsObserver::RPHWebContentsObserver( 152 RPHReferenceManager* manager, 153 WebContents* web_contents) 154 : content::WebContentsObserver(web_contents), 155 manager_(manager) { 156 } 157 158 void RPHReferenceManager::RPHWebContentsObserver::WebContentsDestroyed() { 159 manager_->OnWebContentsDestroyedOrNavigated(web_contents()); 160 } 161 162 void RPHReferenceManager::RPHWebContentsObserver::NavigationEntryCommitted( 163 const content::LoadCommittedDetails& load_details) { 164 if (load_details.is_in_page) 165 return; 166 167 manager_->OnWebContentsDestroyedOrNavigated(web_contents()); 168 } 169 170 RPHReferenceManager::RPHObserver::RPHObserver( 171 RPHReferenceManager* manager, RenderProcessHost* host) 172 : manager_(manager), 173 host_(host) { 174 host->AddObserver(this); 175 } 176 177 RPHReferenceManager::RPHObserver::~RPHObserver() { 178 STLDeleteValues(&observed_web_contentses_); 179 if (host_) 180 host_->RemoveObserver(this); 181 } 182 183 void RPHReferenceManager::RPHObserver::AddWebContentsObserver( 184 WebContents* web_contents) { 185 if (ContainsKey(observed_web_contentses_, web_contents)) 186 return; 187 188 RPHWebContentsObserver* observer = 189 new RPHWebContentsObserver(manager_, web_contents); 190 observed_web_contentses_[web_contents] = observer; 191 } 192 193 void RPHReferenceManager::RPHObserver::RemoveWebContentsObserver( 194 WebContents* web_contents) { 195 WCObserverMap::iterator wco_iter = 196 observed_web_contentses_.find(web_contents); 197 DCHECK(wco_iter != observed_web_contentses_.end()); 198 delete wco_iter->second; 199 observed_web_contentses_.erase(wco_iter); 200 } 201 202 void RPHReferenceManager::RPHObserver::RenderProcessHostDestroyed( 203 RenderProcessHost* host) { 204 host_ = NULL; 205 manager_->OnRenderProcessHostDestroyed(host); 206 } 207 208 void RPHReferenceManager::OnRenderProcessHostDestroyed( 209 RenderProcessHost* rph) { 210 RPHObserverMap::iterator rph_info = observer_map_.find(rph); 211 // This could be a potential problem if the RPH is navigated to a page on the 212 // same renderer (triggering OnWebContentsDestroyedOrNavigated()) and then the 213 // renderer crashes. 214 if (rph_info == observer_map_.end()) { 215 NOTREACHED(); 216 return; 217 } 218 delete rph_info->second; 219 observer_map_.erase(rph_info); 220 if (observer_map_.empty()) 221 no_references_callback_.Run(); 222 } 223 224 void RPHReferenceManager::OnWebContentsDestroyedOrNavigated( 225 WebContents* contents) { 226 RenderProcessHost* rph = contents->GetRenderProcessHost(); 227 RPHObserverMap::iterator rph_info = observer_map_.find(rph); 228 DCHECK(rph_info != observer_map_.end()); 229 230 rph_info->second->RemoveWebContentsObserver(contents); 231 if (!rph_info->second->HasWebContentsObservers()) 232 OnRenderProcessHostDestroyed(rph); 233 } 234 235 } // namespace 236 237 MediaFileSystemInfo::MediaFileSystemInfo(const base::string16& fs_name, 238 const base::FilePath& fs_path, 239 const std::string& filesystem_id, 240 MediaGalleryPrefId pref_id, 241 const std::string& transient_device_id, 242 bool removable, 243 bool media_device) 244 : name(fs_name), 245 path(fs_path), 246 fsid(filesystem_id), 247 pref_id(pref_id), 248 transient_device_id(transient_device_id), 249 removable(removable), 250 media_device(media_device) { 251 } 252 253 MediaFileSystemInfo::MediaFileSystemInfo() {} 254 MediaFileSystemInfo::~MediaFileSystemInfo() {} 255 256 // The main owner of this class is 257 // |MediaFileSystemRegistry::extension_hosts_map_|, but a callback may 258 // temporarily hold a reference. 259 class ExtensionGalleriesHost 260 : public base::RefCountedThreadSafe<ExtensionGalleriesHost> { 261 public: 262 // |no_references_callback| is called when the last RenderViewHost reference 263 // goes away. RenderViewHost references are added through ReferenceFromRVH(). 264 ExtensionGalleriesHost(MediaFileSystemContext* file_system_context, 265 const base::FilePath& profile_path, 266 const std::string& extension_id, 267 const base::Closure& no_references_callback) 268 : file_system_context_(file_system_context), 269 profile_path_(profile_path), 270 extension_id_(extension_id), 271 no_references_callback_(no_references_callback), 272 rph_refs_(base::Bind(&ExtensionGalleriesHost::CleanUp, 273 base::Unretained(this))) { 274 } 275 276 // For each gallery in the list of permitted |galleries|, checks if the 277 // device is attached and if so looks up or creates a file system name and 278 // passes the information needed for the renderer to create those file 279 // system objects to the |callback|. 280 void GetMediaFileSystems(const MediaGalleryPrefIdSet& galleries, 281 const MediaGalleriesPrefInfoMap& galleries_info, 282 const MediaFileSystemsCallback& callback) { 283 // Extract all the device ids so we can make sure they are attached. 284 MediaStorageUtil::DeviceIdSet* device_ids = 285 new MediaStorageUtil::DeviceIdSet; 286 for (std::set<MediaGalleryPrefId>::const_iterator id = galleries.begin(); 287 id != galleries.end(); 288 ++id) { 289 device_ids->insert(galleries_info.find(*id)->second.device_id); 290 } 291 MediaStorageUtil::FilterAttachedDevices(device_ids, base::Bind( 292 &ExtensionGalleriesHost::GetMediaFileSystemsForAttachedDevices, this, 293 base::Owned(device_ids), galleries, galleries_info, callback)); 294 } 295 296 // Checks if |gallery| is attached and if so, registers the file system and 297 // then calls |callback| with the result. 298 void RegisterMediaFileSystem( 299 const MediaGalleryPrefInfo& gallery, 300 const base::Callback<void(base::File::Error result)>& callback) { 301 // Extract all the device ids so we can make sure they are attached. 302 MediaStorageUtil::DeviceIdSet* device_ids = 303 new MediaStorageUtil::DeviceIdSet; 304 device_ids->insert(gallery.device_id); 305 MediaStorageUtil::FilterAttachedDevices(device_ids, base::Bind( 306 &ExtensionGalleriesHost::RegisterAttachedMediaFileSystem, this, 307 base::Owned(device_ids), gallery, callback)); 308 } 309 310 // Revoke the file system for |id| if this extension has created one for |id|. 311 void RevokeGalleryByPrefId(MediaGalleryPrefId id) { 312 PrefIdFsInfoMap::iterator gallery = pref_id_map_.find(id); 313 if (gallery == pref_id_map_.end()) 314 return; 315 316 file_system_context_->RevokeFileSystem(gallery->second.fsid); 317 pref_id_map_.erase(gallery); 318 319 if (pref_id_map_.empty()) { 320 rph_refs_.Reset(); 321 CleanUp(); 322 } 323 } 324 325 // Indicate that the passed |rvh| will reference the file system ids created 326 // by this class. 327 void ReferenceFromRVH(const content::RenderViewHost* rvh) { 328 rph_refs_.ReferenceFromRVH(rvh); 329 } 330 331 private: 332 typedef std::map<MediaGalleryPrefId, MediaFileSystemInfo> PrefIdFsInfoMap; 333 334 // Private destructor and friend declaration for ref counted implementation. 335 friend class base::RefCountedThreadSafe<ExtensionGalleriesHost>; 336 337 virtual ~ExtensionGalleriesHost() { 338 DCHECK(rph_refs_.empty()); 339 DCHECK(pref_id_map_.empty()); 340 } 341 342 void GetMediaFileSystemsForAttachedDevices( 343 const MediaStorageUtil::DeviceIdSet* attached_devices, 344 const MediaGalleryPrefIdSet& galleries, 345 const MediaGalleriesPrefInfoMap& galleries_info, 346 const MediaFileSystemsCallback& callback) { 347 std::vector<MediaFileSystemInfo> result; 348 349 if (rph_refs_.empty()) { 350 // We're actually in the middle of shutdown, and Filter...() lagging 351 // which can invoke this method interleaved in the destruction callback 352 // sequence and re-populate pref_id_map_. 353 callback.Run(result); 354 return; 355 } 356 357 for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it = 358 galleries.begin(); 359 pref_id_it != galleries.end(); 360 ++pref_id_it) { 361 const MediaGalleryPrefId& pref_id = *pref_id_it; 362 const MediaGalleryPrefInfo& gallery_info = 363 galleries_info.find(pref_id)->second; 364 const std::string& device_id = gallery_info.device_id; 365 if (!ContainsKey(*attached_devices, device_id)) 366 continue; 367 368 PrefIdFsInfoMap::const_iterator existing_info = 369 pref_id_map_.find(pref_id); 370 if (existing_info != pref_id_map_.end()) { 371 result.push_back(existing_info->second); 372 continue; 373 } 374 375 base::FilePath path = gallery_info.AbsolutePath(); 376 if (!MediaStorageUtil::CanCreateFileSystem(device_id, path)) 377 continue; 378 379 std::string fs_name = MediaFileSystemBackend::ConstructMountName( 380 profile_path_, extension_id_, pref_id); 381 if (!file_system_context_->RegisterFileSystem(device_id, fs_name, path)) 382 continue; 383 384 MediaFileSystemInfo new_entry( 385 gallery_info.GetGalleryDisplayName(), 386 file_system_context_->GetRegisteredPath(fs_name), 387 fs_name, 388 pref_id, 389 GetTransientIdForRemovableDeviceId(device_id), 390 StorageInfo::IsRemovableDevice(device_id), 391 StorageInfo::IsMediaDevice(device_id)); 392 result.push_back(new_entry); 393 pref_id_map_[pref_id] = new_entry; 394 } 395 396 if (result.size() == 0) { 397 rph_refs_.Reset(); 398 CleanUp(); 399 } 400 401 DCHECK_EQ(pref_id_map_.size(), result.size()); 402 callback.Run(result); 403 } 404 405 void RegisterAttachedMediaFileSystem( 406 const MediaStorageUtil::DeviceIdSet* attached_device, 407 const MediaGalleryPrefInfo& gallery, 408 const base::Callback<void(base::File::Error result)>& callback) { 409 base::File::Error result = base::File::FILE_ERROR_NOT_FOUND; 410 411 // If rph_refs is empty then we're actually in the middle of shutdown, and 412 // Filter...() lagging which can invoke this method interleaved in the 413 // destruction callback sequence and re-populate pref_id_map_. 414 if (!attached_device->empty() && !rph_refs_.empty()) { 415 std::string fs_name = MediaFileSystemBackend::ConstructMountName( 416 profile_path_, extension_id_, gallery.pref_id); 417 base::FilePath path = gallery.AbsolutePath(); 418 const std::string& device_id = gallery.device_id; 419 420 if (ContainsKey(pref_id_map_, gallery.pref_id)) { 421 result = base::File::FILE_OK; 422 } else if (MediaStorageUtil::CanCreateFileSystem(device_id, path) && 423 file_system_context_->RegisterFileSystem(device_id, fs_name, 424 path)) { 425 result = base::File::FILE_OK; 426 pref_id_map_[gallery.pref_id] = MediaFileSystemInfo( 427 gallery.GetGalleryDisplayName(), 428 file_system_context_->GetRegisteredPath(fs_name), 429 fs_name, 430 gallery.pref_id, 431 GetTransientIdForRemovableDeviceId(device_id), 432 StorageInfo::IsRemovableDevice(device_id), 433 StorageInfo::IsMediaDevice(device_id)); 434 } 435 } 436 437 if (pref_id_map_.empty()) { 438 rph_refs_.Reset(); 439 CleanUp(); 440 } 441 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 442 base::Bind(callback, result)); 443 } 444 445 std::string GetTransientIdForRemovableDeviceId(const std::string& device_id) { 446 if (!StorageInfo::IsRemovableDevice(device_id)) 447 return std::string(); 448 449 return StorageMonitor::GetInstance()->GetTransientIdForDeviceId(device_id); 450 } 451 452 void CleanUp() { 453 DCHECK(rph_refs_.empty()); 454 for (PrefIdFsInfoMap::const_iterator it = pref_id_map_.begin(); 455 it != pref_id_map_.end(); 456 ++it) { 457 file_system_context_->RevokeFileSystem(it->second.fsid); 458 } 459 pref_id_map_.clear(); 460 461 no_references_callback_.Run(); 462 } 463 464 // MediaFileSystemRegistry owns |this| and |file_system_context_|, so it's 465 // safe to store a raw pointer. 466 MediaFileSystemContext* file_system_context_; 467 468 // Path for the active profile. 469 const base::FilePath profile_path_; 470 471 // Id of the extension this host belongs to. 472 const std::string extension_id_; 473 474 // A callback to call when the last RVH reference goes away. 475 base::Closure no_references_callback_; 476 477 // A map from the gallery preferences id to the file system information. 478 PrefIdFsInfoMap pref_id_map_; 479 480 // The set of render processes and web contents that may have references to 481 // the file system ids this instance manages. 482 RPHReferenceManager rph_refs_; 483 484 DISALLOW_COPY_AND_ASSIGN(ExtensionGalleriesHost); 485 }; 486 487 /****************** 488 * Public methods 489 ******************/ 490 491 void MediaFileSystemRegistry::GetMediaFileSystemsForExtension( 492 const content::RenderViewHost* rvh, 493 const extensions::Extension* extension, 494 const MediaFileSystemsCallback& callback) { 495 DCHECK_CURRENTLY_ON(BrowserThread::UI); 496 497 Profile* profile = 498 Profile::FromBrowserContext(rvh->GetProcess()->GetBrowserContext()); 499 MediaGalleriesPreferences* preferences = GetPreferences(profile); 500 MediaGalleryPrefIdSet galleries = 501 preferences->GalleriesForExtension(*extension); 502 503 if (galleries.empty()) { 504 callback.Run(std::vector<MediaFileSystemInfo>()); 505 return; 506 } 507 508 ExtensionGalleriesHost* extension_host = 509 GetExtensionGalleryHost(profile, preferences, extension->id()); 510 511 // This must come before the GetMediaFileSystems call to make sure the 512 // RVH of the context is referenced before the filesystems are retrieved. 513 extension_host->ReferenceFromRVH(rvh); 514 515 extension_host->GetMediaFileSystems(galleries, preferences->known_galleries(), 516 callback); 517 } 518 519 void MediaFileSystemRegistry::RegisterMediaFileSystemForExtension( 520 const content::RenderViewHost* rvh, 521 const extensions::Extension* extension, 522 MediaGalleryPrefId pref_id, 523 const base::Callback<void(base::File::Error result)>& callback) { 524 DCHECK_CURRENTLY_ON(BrowserThread::UI); 525 DCHECK_NE(kInvalidMediaGalleryPrefId, pref_id); 526 527 Profile* profile = 528 Profile::FromBrowserContext(rvh->GetProcess()->GetBrowserContext()); 529 MediaGalleriesPreferences* preferences = GetPreferences(profile); 530 MediaGalleriesPrefInfoMap::const_iterator gallery = 531 preferences->known_galleries().find(pref_id); 532 MediaGalleryPrefIdSet permitted_galleries = 533 preferences->GalleriesForExtension(*extension); 534 535 if (gallery == preferences->known_galleries().end() || 536 !ContainsKey(permitted_galleries, pref_id)) { 537 BrowserThread::PostTask( 538 BrowserThread::IO, FROM_HERE, 539 base::Bind(callback, base::File::FILE_ERROR_NOT_FOUND)); 540 return; 541 } 542 543 ExtensionGalleriesHost* extension_host = 544 GetExtensionGalleryHost(profile, preferences, extension->id()); 545 546 // This must come before the GetMediaFileSystems call to make sure the 547 // RVH of the context is referenced before the filesystems are retrieved. 548 extension_host->ReferenceFromRVH(rvh); 549 550 extension_host->RegisterMediaFileSystem(gallery->second, callback); 551 } 552 553 MediaGalleriesPreferences* MediaFileSystemRegistry::GetPreferences( 554 Profile* profile) { 555 // Create an empty ExtensionHostMap for this profile on first initialization. 556 if (!ContainsKey(extension_hosts_map_, profile)) { 557 extension_hosts_map_[profile] = ExtensionHostMap(); 558 media_galleries::UsageCount(media_galleries::PROFILES_WITH_USAGE); 559 } 560 561 return MediaGalleriesPreferencesFactory::GetForProfile(profile); 562 } 563 564 MediaScanManager* MediaFileSystemRegistry::media_scan_manager() { 565 if (!media_scan_manager_) 566 media_scan_manager_.reset(new MediaScanManager); 567 return media_scan_manager_.get(); 568 } 569 570 void MediaFileSystemRegistry::OnRemovableStorageDetached( 571 const StorageInfo& info) { 572 DCHECK_CURRENTLY_ON(BrowserThread::UI); 573 574 // Since revoking a gallery in the ExtensionGalleriesHost may cause it 575 // to be removed from the map and therefore invalidate any iterator pointing 576 // to it, this code first copies all the invalid gallery ids and the 577 // extension hosts in which they may appear (per profile) and revoked it in 578 // a second step. 579 std::vector<InvalidatedGalleriesInfo> invalid_galleries_info; 580 581 for (ExtensionGalleriesHostMap::iterator profile_it = 582 extension_hosts_map_.begin(); 583 profile_it != extension_hosts_map_.end(); 584 ++profile_it) { 585 MediaGalleriesPreferences* preferences = GetPreferences(profile_it->first); 586 // If |preferences| is not yet initialized, it won't contain any galleries. 587 if (!preferences->IsInitialized()) 588 continue; 589 590 InvalidatedGalleriesInfo invalid_galleries_in_profile; 591 invalid_galleries_in_profile.pref_ids = 592 preferences->LookUpGalleriesByDeviceId(info.device_id()); 593 594 for (ExtensionHostMap::const_iterator extension_host_it = 595 profile_it->second.begin(); 596 extension_host_it != profile_it->second.end(); 597 ++extension_host_it) { 598 invalid_galleries_in_profile.extension_hosts.insert( 599 extension_host_it->second.get()); 600 } 601 602 invalid_galleries_info.push_back(invalid_galleries_in_profile); 603 } 604 605 for (size_t i = 0; i < invalid_galleries_info.size(); i++) { 606 for (std::set<ExtensionGalleriesHost*>::const_iterator extension_host_it = 607 invalid_galleries_info[i].extension_hosts.begin(); 608 extension_host_it != invalid_galleries_info[i].extension_hosts.end(); 609 ++extension_host_it) { 610 for (std::set<MediaGalleryPrefId>::const_iterator pref_id_it = 611 invalid_galleries_info[i].pref_ids.begin(); 612 pref_id_it != invalid_galleries_info[i].pref_ids.end(); 613 ++pref_id_it) { 614 (*extension_host_it)->RevokeGalleryByPrefId(*pref_id_it); 615 } 616 } 617 } 618 } 619 620 /****************** 621 * Private methods 622 ******************/ 623 624 class MediaFileSystemRegistry::MediaFileSystemContextImpl 625 : public MediaFileSystemContext { 626 public: 627 MediaFileSystemContextImpl() {} 628 virtual ~MediaFileSystemContextImpl() {} 629 630 virtual bool RegisterFileSystem(const std::string& device_id, 631 const std::string& fs_name, 632 const base::FilePath& path) OVERRIDE { 633 if (StorageInfo::IsMassStorageDevice(device_id)) { 634 return RegisterFileSystemForMassStorage(device_id, fs_name, path); 635 } else { 636 return RegisterFileSystemForMTPDevice(device_id, fs_name, path); 637 } 638 } 639 640 virtual void RevokeFileSystem(const std::string& fs_name) OVERRIDE { 641 ImportedMediaGalleryRegistry* imported_registry = 642 ImportedMediaGalleryRegistry::GetInstance(); 643 if (imported_registry->RevokeImportedFilesystemOnUIThread(fs_name)) 644 return; 645 646 ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(fs_name); 647 648 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( 649 &MTPDeviceMapService::RevokeMTPFileSystem, 650 base::Unretained(MTPDeviceMapService::GetInstance()), 651 fs_name)); 652 } 653 654 virtual base::FilePath GetRegisteredPath( 655 const std::string& fs_name) const OVERRIDE { 656 base::FilePath result; 657 if (!ExternalMountPoints::GetSystemInstance()->GetRegisteredPath(fs_name, 658 &result)) { 659 return base::FilePath(); 660 } 661 return result; 662 } 663 664 private: 665 // Registers and returns the file system id for the mass storage device 666 // specified by |device_id| and |path|. 667 bool RegisterFileSystemForMassStorage(const std::string& device_id, 668 const std::string& fs_name, 669 const base::FilePath& path) { 670 DCHECK_CURRENTLY_ON(BrowserThread::UI); 671 DCHECK(StorageInfo::IsMassStorageDevice(device_id)); 672 673 // Sanity checks for |path|. 674 CHECK(path.IsAbsolute()); 675 CHECK(!path.ReferencesParent()); 676 677 // TODO(gbillock): refactor ImportedMediaGalleryRegistry to delegate this 678 // call tree, probably by having it figure out by device id what 679 // registration is needed, or having per-device-type handlers at the 680 // next higher level. 681 bool result = false; 682 if (StorageInfo::IsITunesDevice(device_id)) { 683 ImportedMediaGalleryRegistry* registry = 684 ImportedMediaGalleryRegistry::GetInstance(); 685 result = registry->RegisterITunesFilesystemOnUIThread(fs_name, path); 686 } else if (StorageInfo::IsPicasaDevice(device_id)) { 687 ImportedMediaGalleryRegistry* registry = 688 ImportedMediaGalleryRegistry::GetInstance(); 689 result = registry->RegisterPicasaFilesystemOnUIThread(fs_name, path); 690 } else if (StorageInfo::IsIPhotoDevice(device_id)) { 691 ImportedMediaGalleryRegistry* registry = 692 ImportedMediaGalleryRegistry::GetInstance(); 693 result = registry->RegisterIPhotoFilesystemOnUIThread(fs_name, path); 694 } else { 695 result = ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( 696 fs_name, fileapi::kFileSystemTypeNativeMedia, 697 fileapi::FileSystemMountOption(), path); 698 } 699 return result; 700 } 701 702 bool RegisterFileSystemForMTPDevice(const std::string& device_id, 703 const std::string fs_name, 704 const base::FilePath& path) { 705 DCHECK_CURRENTLY_ON(BrowserThread::UI); 706 DCHECK(!StorageInfo::IsMassStorageDevice(device_id)); 707 708 // Sanity checks for |path|. 709 CHECK(MediaStorageUtil::CanCreateFileSystem(device_id, path)); 710 bool result = ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( 711 fs_name, fileapi::kFileSystemTypeDeviceMedia, 712 fileapi::FileSystemMountOption(), path); 713 CHECK(result); 714 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind( 715 &MTPDeviceMapService::RegisterMTPFileSystem, 716 base::Unretained(MTPDeviceMapService::GetInstance()), 717 path.value(), fs_name)); 718 return result; 719 } 720 721 DISALLOW_COPY_AND_ASSIGN(MediaFileSystemContextImpl); 722 }; 723 724 // Constructor in 'private' section because depends on private class definition. 725 MediaFileSystemRegistry::MediaFileSystemRegistry() 726 : file_system_context_(new MediaFileSystemContextImpl) { 727 StorageMonitor::GetInstance()->AddObserver(this); 728 } 729 730 MediaFileSystemRegistry::~MediaFileSystemRegistry() { 731 // TODO(gbillock): This is needed because the unit test uses the 732 // g_browser_process registry. We should create one in the unit test, 733 // and then can remove this. 734 if (StorageMonitor::GetInstance()) 735 StorageMonitor::GetInstance()->RemoveObserver(this); 736 } 737 738 void MediaFileSystemRegistry::OnPermissionRemoved( 739 MediaGalleriesPreferences* prefs, 740 const std::string& extension_id, 741 MediaGalleryPrefId pref_id) { 742 Profile* profile = prefs->profile(); 743 ExtensionGalleriesHostMap::const_iterator host_map_it = 744 extension_hosts_map_.find(profile); 745 DCHECK(host_map_it != extension_hosts_map_.end()); 746 const ExtensionHostMap& extension_host_map = host_map_it->second; 747 ExtensionHostMap::const_iterator gallery_host_it = 748 extension_host_map.find(extension_id); 749 if (gallery_host_it == extension_host_map.end()) 750 return; 751 gallery_host_it->second->RevokeGalleryByPrefId(pref_id); 752 } 753 754 void MediaFileSystemRegistry::OnGalleryRemoved( 755 MediaGalleriesPreferences* prefs, 756 MediaGalleryPrefId pref_id) { 757 Profile* profile = prefs->profile(); 758 // Get the Extensions, MediaGalleriesPreferences and ExtensionHostMap for 759 // |profile|. 760 const ExtensionService* extension_service = 761 extensions::ExtensionSystem::Get(profile)->extension_service(); 762 const extensions::ExtensionSet* extensions_set = 763 extension_service->extensions(); 764 ExtensionGalleriesHostMap::const_iterator host_map_it = 765 extension_hosts_map_.find(profile); 766 DCHECK(host_map_it != extension_hosts_map_.end()); 767 const ExtensionHostMap& extension_host_map = host_map_it->second; 768 769 // Go through ExtensionHosts, and remove indicated gallery, if any. 770 // RevokeGalleryByPrefId() may end up deleting from |extension_host_map| and 771 // even delete |extension_host_map| altogether. So do this in two loops to 772 // avoid using an invalidated iterator or deleted map. 773 std::vector<const extensions::Extension*> extensions; 774 for (ExtensionHostMap::const_iterator it = extension_host_map.begin(); 775 it != extension_host_map.end(); 776 ++it) { 777 extensions.push_back(extensions_set->GetByID(it->first)); 778 } 779 for (size_t i = 0; i < extensions.size(); ++i) { 780 if (!ContainsKey(extension_hosts_map_, profile)) 781 break; 782 ExtensionHostMap::const_iterator gallery_host_it = 783 extension_host_map.find(extensions[i]->id()); 784 if (gallery_host_it == extension_host_map.end()) 785 continue; 786 gallery_host_it->second->RevokeGalleryByPrefId(pref_id); 787 } 788 } 789 790 ExtensionGalleriesHost* MediaFileSystemRegistry::GetExtensionGalleryHost( 791 Profile* profile, 792 MediaGalleriesPreferences* preferences, 793 const std::string& extension_id) { 794 ExtensionGalleriesHostMap::iterator extension_hosts = 795 extension_hosts_map_.find(profile); 796 // GetPreferences(), which had to be called because preferences is an 797 // argument, ensures that profile is in the map. 798 DCHECK(extension_hosts != extension_hosts_map_.end()); 799 if (extension_hosts->second.empty()) 800 preferences->AddGalleryChangeObserver(this); 801 802 ExtensionGalleriesHost* result = extension_hosts->second[extension_id].get(); 803 if (!result) { 804 result = new ExtensionGalleriesHost( 805 file_system_context_.get(), 806 profile->GetPath(), 807 extension_id, 808 base::Bind(&MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty, 809 base::Unretained(this), 810 profile, 811 extension_id)); 812 extension_hosts_map_[profile][extension_id] = result; 813 } 814 return result; 815 } 816 817 void MediaFileSystemRegistry::OnExtensionGalleriesHostEmpty( 818 Profile* profile, const std::string& extension_id) { 819 DCHECK_CURRENTLY_ON(BrowserThread::UI); 820 821 ExtensionGalleriesHostMap::iterator extension_hosts = 822 extension_hosts_map_.find(profile); 823 DCHECK(extension_hosts != extension_hosts_map_.end()); 824 ExtensionHostMap::size_type erase_count = 825 extension_hosts->second.erase(extension_id); 826 DCHECK_EQ(1U, erase_count); 827 if (extension_hosts->second.empty()) { 828 // When a profile has no ExtensionGalleriesHosts left, remove the 829 // matching gallery-change-watcher since it is no longer needed. Leave the 830 // |extension_hosts| entry alone, since it indicates the profile has been 831 // previously used. 832 MediaGalleriesPreferences* preferences = GetPreferences(profile); 833 preferences->RemoveGalleryChangeObserver(this); 834 } 835 } 836