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