Index: chrome/browser/media_galleries/media_scan_manager.cc |
diff --git a/chrome/browser/media_galleries/media_scan_manager.cc b/chrome/browser/media_galleries/media_scan_manager.cc |
deleted file mode 100644 |
index 93fffb45736cd2e7392950cc012537a3aacc0ebf..0000000000000000000000000000000000000000 |
--- a/chrome/browser/media_galleries/media_scan_manager.cc |
+++ /dev/null |
@@ -1,558 +0,0 @@ |
-// Copyright 2014 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "chrome/browser/media_galleries/media_scan_manager.h" |
- |
-#include <stddef.h> |
- |
-#include "base/files/file_enumerator.h" |
-#include "base/files/file_util.h" |
-#include "base/logging.h" |
-#include "base/metrics/histogram.h" |
-#include "base/time/time.h" |
-#include "chrome/browser/extensions/extension_service.h" |
-#include "chrome/browser/media_galleries/media_galleries_preferences.h" |
-#include "chrome/browser/media_galleries/media_galleries_preferences_factory.h" |
-#include "chrome/browser/media_galleries/media_scan_manager_observer.h" |
-#include "chrome/browser/profiles/profile.h" |
-#include "chrome/common/extensions/api/media_galleries.h" |
-#include "content/public/browser/browser_thread.h" |
-#include "extensions/browser/extension_registry.h" |
-#include "extensions/browser/extension_system.h" |
-#include "extensions/common/extension.h" |
- |
-using extensions::ExtensionRegistry; |
- |
-namespace media_galleries = extensions::api::media_galleries; |
- |
-namespace { |
- |
-typedef std::set<std::string /*extension id*/> ScanningExtensionIdSet; |
- |
-// When multiple scan results have the same parent, sometimes it makes sense |
-// to combine them into a single scan result at the parent. This constant |
-// governs when that happens; kContainerDirectoryMinimumPercent percent of the |
-// directories in the parent directory must be scan results. |
-const int kContainerDirectoryMinimumPercent = 80; |
- |
-// How long after a completed media scan can we provide the cached results. |
-const int kScanResultsExpiryTimeInHours = 24; |
- |
-struct LocationInfo { |
- LocationInfo() |
- : pref_id(kInvalidMediaGalleryPrefId), |
- type(MediaGalleryPrefInfo::kInvalidType) {} |
- LocationInfo(MediaGalleryPrefId pref_id, MediaGalleryPrefInfo::Type type, |
- base::FilePath path) |
- : pref_id(pref_id), type(type), path(path) {} |
- // Highest priority comparison by path, next by type (scan result last), |
- // then by pref id (invalid last). |
- bool operator<(const LocationInfo& rhs) const { |
- if (path.value() == rhs.path.value()) { |
- if (type == rhs.type) { |
- return pref_id > rhs.pref_id; |
- } |
- return rhs.type == MediaGalleryPrefInfo::kScanResult; |
- } |
- return path.value() < rhs.path.value(); |
- } |
- |
- MediaGalleryPrefId pref_id; |
- MediaGalleryPrefInfo::Type type; |
- base::FilePath path; |
- MediaGalleryScanResult file_counts; |
-}; |
- |
-// Finds new scan results that are shadowed (the same location, or a child) by |
-// existing locations and moves them from |found_folders| to |child_folders|. |
-// Also moves new scan results that are shadowed by other new scan results |
-// to |child_folders|. |
-void PartitionChildScanResults( |
- MediaGalleriesPreferences* preferences, |
- MediaFolderFinder::MediaFolderFinderResults* found_folders, |
- MediaFolderFinder::MediaFolderFinderResults* child_folders) { |
- // Construct a list with everything in it. |
- std::vector<LocationInfo> all_locations; |
- for (MediaFolderFinder::MediaFolderFinderResults::const_iterator it = |
- found_folders->begin(); it != found_folders->end(); ++it) { |
- all_locations.push_back(LocationInfo(kInvalidMediaGalleryPrefId, |
- MediaGalleryPrefInfo::kScanResult, |
- it->first)); |
- all_locations.back().file_counts = it->second; |
- } |
- const MediaGalleriesPrefInfoMap& known_galleries = |
- preferences->known_galleries(); |
- for (MediaGalleriesPrefInfoMap::const_iterator it = known_galleries.begin(); |
- it != known_galleries.end(); |
- ++it) { |
- all_locations.push_back(LocationInfo(it->second.pref_id, it->second.type, |
- it->second.AbsolutePath())); |
- } |
- // Sorting on path should put all paths that are prefixes of other paths |
- // next to each other, with the shortest one first. |
- std::sort(all_locations.begin(), all_locations.end()); |
- |
- size_t previous_parent_index = 0; |
- for (size_t i = 1; i < all_locations.size(); i++) { |
- const LocationInfo& current = all_locations[i]; |
- const LocationInfo& previous_parent = all_locations[previous_parent_index]; |
- bool is_child = previous_parent.path.IsParent(current.path); |
- if (current.type == MediaGalleryPrefInfo::kScanResult && |
- current.pref_id == kInvalidMediaGalleryPrefId && |
- (is_child || previous_parent.path == current.path)) { |
- // Move new scan results that are shadowed. |
- (*child_folders)[current.path] = current.file_counts; |
- found_folders->erase(current.path); |
- } else if (!is_child) { |
- previous_parent_index = i; |
- } |
- } |
-} |
- |
-MediaGalleryScanResult SumFilesUnderPath( |
- const base::FilePath& path, |
- const MediaFolderFinder::MediaFolderFinderResults& candidates) { |
- MediaGalleryScanResult results; |
- for (MediaFolderFinder::MediaFolderFinderResults::const_iterator it = |
- candidates.begin(); it != candidates.end(); ++it) { |
- if (it->first == path || path.IsParent(it->first)) { |
- results.audio_count += it->second.audio_count; |
- results.image_count += it->second.image_count; |
- results.video_count += it->second.video_count; |
- } |
- } |
- return results; |
-} |
- |
-void AddScanResultsForProfile( |
- MediaGalleriesPreferences* preferences, |
- const MediaFolderFinder::MediaFolderFinderResults& found_folders) { |
- // First, remove any existing scan results where no app has been granted |
- // permission - either it is gone, or is already in the new scan results. |
- // This burns some pref ids, but not at an appreciable rate. |
- MediaGalleryPrefIdSet to_remove; |
- const MediaGalleriesPrefInfoMap& known_galleries = |
- preferences->known_galleries(); |
- for (MediaGalleriesPrefInfoMap::const_iterator it = known_galleries.begin(); |
- it != known_galleries.end(); |
- ++it) { |
- if (it->second.type == MediaGalleryPrefInfo::kScanResult && |
- !preferences->NonAutoGalleryHasPermission(it->first)) { |
- to_remove.insert(it->first); |
- } |
- } |
- for (MediaGalleryPrefIdSet::const_iterator it = to_remove.begin(); |
- it != to_remove.end(); |
- ++it) { |
- preferences->EraseGalleryById(*it); |
- } |
- |
- MediaFolderFinder::MediaFolderFinderResults child_folders; |
- MediaFolderFinder::MediaFolderFinderResults |
- unique_found_folders(found_folders); |
- PartitionChildScanResults(preferences, &unique_found_folders, &child_folders); |
- |
- // Updating prefs while iterating them will invalidate the pointer, so |
- // calculate the changes first and then apply them. |
- std::map<MediaGalleryPrefId, MediaGalleryScanResult> to_update; |
- for (MediaGalleriesPrefInfoMap::const_iterator it = known_galleries.begin(); |
- it != known_galleries.end(); |
- ++it) { |
- const MediaGalleryPrefInfo& gallery = it->second; |
- if (!gallery.IsBlackListedType()) { |
- MediaGalleryScanResult file_counts = |
- SumFilesUnderPath(gallery.AbsolutePath(), child_folders); |
- if (gallery.audio_count != file_counts.audio_count || |
- gallery.image_count != file_counts.image_count || |
- gallery.video_count != file_counts.video_count) { |
- to_update[it->first] = file_counts; |
- } |
- } |
- } |
- |
- for (std::map<MediaGalleryPrefId, |
- MediaGalleryScanResult>::const_iterator it = to_update.begin(); |
- it != to_update.end(); |
- ++it) { |
- const MediaGalleryPrefInfo& gallery = |
- preferences->known_galleries().find(it->first)->second; |
- preferences->AddGallery(gallery.device_id, gallery.path, gallery.type, |
- gallery.volume_label, gallery.vendor_name, |
- gallery.model_name, gallery.total_size_in_bytes, |
- gallery.last_attach_time, |
- it->second.audio_count, |
- it->second.image_count, |
- it->second.video_count); |
- } |
- |
- // Add new scan results. |
- for (MediaFolderFinder::MediaFolderFinderResults::const_iterator it = |
- unique_found_folders.begin(); |
- it != unique_found_folders.end(); |
- ++it) { |
- MediaGalleryScanResult file_counts = |
- SumFilesUnderPath(it->first, child_folders); |
- // The top level scan result is not in |child_folders|. Add it in as well. |
- file_counts.audio_count += it->second.audio_count; |
- file_counts.image_count += it->second.image_count; |
- file_counts.video_count += it->second.video_count; |
- |
- MediaGalleryPrefInfo gallery; |
- bool existing = preferences->LookUpGalleryByPath(it->first, &gallery); |
- DCHECK(!existing); |
- preferences->AddGallery(gallery.device_id, gallery.path, |
- MediaGalleryPrefInfo::kScanResult, |
- gallery.volume_label, gallery.vendor_name, |
- gallery.model_name, gallery.total_size_in_bytes, |
- gallery.last_attach_time, file_counts.audio_count, |
- file_counts.image_count, file_counts.video_count); |
- } |
- UMA_HISTOGRAM_COUNTS_10000("MediaGalleries.ScanGalleriesPopulated", |
- unique_found_folders.size() + to_update.size()); |
-} |
- |
-int CountScanResultsForExtension(MediaGalleriesPreferences* preferences, |
- const extensions::Extension* extension, |
- MediaGalleryScanResult* file_counts) { |
- int gallery_count = 0; |
- |
- MediaGalleryPrefIdSet permitted_galleries = |
- preferences->GalleriesForExtension(*extension); |
- const MediaGalleriesPrefInfoMap& known_galleries = |
- preferences->known_galleries(); |
- for (MediaGalleriesPrefInfoMap::const_iterator it = known_galleries.begin(); |
- it != known_galleries.end(); |
- ++it) { |
- if (it->second.type == MediaGalleryPrefInfo::kScanResult && |
- !ContainsKey(permitted_galleries, it->first)) { |
- gallery_count++; |
- file_counts->audio_count += it->second.audio_count; |
- file_counts->image_count += it->second.image_count; |
- file_counts->video_count += it->second.video_count; |
- } |
- } |
- return gallery_count; |
-} |
- |
-int CountDirectoryEntries(const base::FilePath& path) { |
- base::FileEnumerator dir_counter( |
- path, false /*recursive*/, base::FileEnumerator::DIRECTORIES); |
- int count = 0; |
- base::FileEnumerator::FileInfo info; |
- for (base::FilePath name = dir_counter.Next(); !name.empty(); |
- name = dir_counter.Next()) { |
- if (!base::IsLink(name)) |
- ++count; |
- } |
- return count; |
-} |
- |
-struct ContainerCount { |
- int seen_count, entries_count; |
- bool is_qualified; |
- |
- ContainerCount() : seen_count(0), entries_count(-1), is_qualified(false) {} |
-}; |
- |
-typedef std::map<base::FilePath, ContainerCount> ContainerCandidates; |
- |
-} // namespace |
- |
-MediaScanManager::MediaScanManager() |
- : scoped_extension_registry_observer_(this), |
- weak_factory_(this) { |
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
-} |
- |
-MediaScanManager::~MediaScanManager() { |
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
-} |
- |
-void MediaScanManager::AddObserver(Profile* profile, |
- MediaScanManagerObserver* observer) { |
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
- DCHECK(!ContainsKey(observers_, profile)); |
- observers_[profile].observer = observer; |
-} |
- |
-void MediaScanManager::RemoveObserver(Profile* profile) { |
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
- bool scan_in_progress = ScanInProgress(); |
- observers_.erase(profile); |
- DCHECK_EQ(scan_in_progress, ScanInProgress()); |
-} |
- |
-void MediaScanManager::CancelScansForProfile(Profile* profile) { |
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
- observers_[profile].scanning_extensions.clear(); |
- |
- if (!ScanInProgress()) |
- folder_finder_.reset(); |
-} |
- |
-void MediaScanManager::StartScan(Profile* profile, |
- const extensions::Extension* extension, |
- bool user_gesture) { |
- DCHECK(extension); |
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
- |
- ScanObserverMap::iterator scans_for_profile = observers_.find(profile); |
- // We expect that an MediaScanManagerObserver has already been registered. |
- DCHECK(scans_for_profile != observers_.end()); |
- bool scan_in_progress = ScanInProgress(); |
- // Ignore requests for extensions that are already scanning. |
- ScanningExtensionIdSet* scanning_extensions; |
- scanning_extensions = &scans_for_profile->second.scanning_extensions; |
- if (scan_in_progress && ContainsKey(*scanning_extensions, extension->id())) |
- return; |
- |
- // Provide cached result if there is not already a scan in progress, |
- // there is no user gesture, and the previous results are unexpired. |
- MediaGalleriesPreferences* preferences = |
- MediaGalleriesPreferencesFactory::GetForProfile(profile); |
- base::TimeDelta time_since_last_scan = |
- base::Time::Now() - preferences->GetLastScanCompletionTime(); |
- if (!scan_in_progress && !user_gesture && time_since_last_scan < |
- base::TimeDelta::FromHours(kScanResultsExpiryTimeInHours)) { |
- MediaGalleryScanResult file_counts; |
- int gallery_count = |
- CountScanResultsForExtension(preferences, extension, &file_counts); |
- scans_for_profile->second.observer->OnScanStarted(extension->id()); |
- scans_for_profile->second.observer->OnScanFinished(extension->id(), |
- gallery_count, |
- file_counts); |
- return; |
- } |
- |
- // On first scan for the |profile|, register to listen for extension unload. |
- if (scanning_extensions->empty()) |
- scoped_extension_registry_observer_.Add(ExtensionRegistry::Get(profile)); |
- |
- scanning_extensions->insert(extension->id()); |
- scans_for_profile->second.observer->OnScanStarted(extension->id()); |
- |
- if (folder_finder_) |
- return; |
- |
- MediaFolderFinder::MediaFolderFinderResultsCallback callback = |
- base::Bind(&MediaScanManager::OnScanCompleted, |
- weak_factory_.GetWeakPtr()); |
- if (testing_folder_finder_factory_.is_null()) { |
- folder_finder_.reset(new MediaFolderFinder(callback)); |
- } else { |
- folder_finder_.reset(testing_folder_finder_factory_.Run(callback)); |
- } |
- scan_start_time_ = base::Time::Now(); |
- folder_finder_->StartScan(); |
-} |
- |
-void MediaScanManager::CancelScan(Profile* profile, |
- const extensions::Extension* extension) { |
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
- |
- // Erases the logical scan if found, early exit otherwise. |
- ScanObserverMap::iterator scans_for_profile = observers_.find(profile); |
- if (scans_for_profile == observers_.end() || |
- !scans_for_profile->second.scanning_extensions.erase(extension->id())) { |
- return; |
- } |
- |
- scans_for_profile->second.observer->OnScanCancelled(extension->id()); |
- |
- // No more scanning extensions for |profile|, so stop listening for unloads. |
- if (scans_for_profile->second.scanning_extensions.empty()) |
- scoped_extension_registry_observer_.Remove(ExtensionRegistry::Get(profile)); |
- |
- if (!ScanInProgress()) { |
- folder_finder_.reset(); |
- DCHECK(!scan_start_time_.is_null()); |
- UMA_HISTOGRAM_LONG_TIMES("MediaGalleries.ScanCancelTime", |
- base::Time::Now() - scan_start_time_); |
- scan_start_time_ = base::Time(); |
- } |
-} |
- |
-void MediaScanManager::SetMediaFolderFinderFactory( |
- const MediaFolderFinderFactory& factory) { |
- testing_folder_finder_factory_ = factory; |
-} |
- |
-// A single directory may contain many folders with media in them, without |
-// containing any media itself. In fact, the primary purpose of that directory |
-// may be to contain media directories. This function tries to find those |
-// container directories. |
-MediaFolderFinder::MediaFolderFinderResults |
-MediaScanManager::FindContainerScanResults( |
- const MediaFolderFinder::MediaFolderFinderResults& found_folders, |
- const std::vector<base::FilePath>& sensitive_locations) { |
- DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); |
- std::vector<base::FilePath> abs_sensitive_locations; |
- for (size_t i = 0; i < sensitive_locations.size(); ++i) { |
- base::FilePath path = base::MakeAbsoluteFilePath(sensitive_locations[i]); |
- if (!path.empty()) |
- abs_sensitive_locations.push_back(path); |
- } |
- // Recursively find parent directories with majority of media directories, |
- // or container directories. |
- // |candidates| keeps track of directories which might have enough |
- // such directories to have us return them. |
- typedef std::map<base::FilePath, ContainerCount> ContainerCandidates; |
- ContainerCandidates candidates; |
- for (MediaFolderFinder::MediaFolderFinderResults::const_iterator it = |
- found_folders.begin(); |
- it != found_folders.end(); |
- ++it) { |
- base::FilePath child_directory = it->first; |
- base::FilePath parent_directory = child_directory.DirName(); |
- |
- // Parent of root is root. |
- while (!parent_directory.empty() && child_directory != parent_directory) { |
- // Skip sensitive folders and their ancestors. |
- base::FilePath abs_parent_directory = |
- base::MakeAbsoluteFilePath(parent_directory); |
- if (abs_parent_directory.empty()) |
- break; |
- bool is_sensitive = false; |
- for (size_t i = 0; i < abs_sensitive_locations.size(); ++i) { |
- if (abs_parent_directory == abs_sensitive_locations[i] || |
- abs_parent_directory.IsParent(abs_sensitive_locations[i])) { |
- is_sensitive = true; |
- break; |
- } |
- } |
- if (is_sensitive) |
- break; |
- |
- // Don't bother with ones we already have. |
- if (found_folders.find(parent_directory) != found_folders.end()) |
- continue; |
- |
- ContainerCandidates::iterator parent_it = |
- candidates.find(parent_directory); |
- if (parent_it == candidates.end()) { |
- ContainerCount count; |
- count.seen_count = 1; |
- count.entries_count = CountDirectoryEntries(parent_directory); |
- parent_it = |
- candidates.insert(std::make_pair(parent_directory, count)).first; |
- } else { |
- ++candidates[parent_directory].seen_count; |
- } |
- // If previously sufficient, or not sufficient, bail. |
- if (parent_it->second.is_qualified || |
- parent_it->second.seen_count * 100 / parent_it->second.entries_count < |
- kContainerDirectoryMinimumPercent) { |
- break; |
- } |
- // Otherwise, mark qualified and check parent. |
- parent_it->second.is_qualified = true; |
- child_directory = parent_directory; |
- parent_directory = child_directory.DirName(); |
- } |
- } |
- MediaFolderFinder::MediaFolderFinderResults result; |
- // Copy and return worthy results. |
- for (ContainerCandidates::const_iterator it = candidates.begin(); |
- it != candidates.end(); |
- ++it) { |
- if (it->second.is_qualified && it->second.seen_count >= 2) |
- result[it->first] = MediaGalleryScanResult(); |
- } |
- return result; |
-} |
- |
-MediaScanManager::ScanObservers::ScanObservers() : observer(NULL) {} |
-MediaScanManager::ScanObservers::ScanObservers(const ScanObservers& other) = |
- default; |
-MediaScanManager::ScanObservers::~ScanObservers() {} |
- |
-void MediaScanManager::OnExtensionUnloaded( |
- content::BrowserContext* browser_context, |
- const extensions::Extension* extension, |
- extensions::UnloadedExtensionInfo::Reason reason) { |
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
- CancelScan(Profile::FromBrowserContext(browser_context), extension); |
-} |
- |
-bool MediaScanManager::ScanInProgress() const { |
- for (ScanObserverMap::const_iterator it = observers_.begin(); |
- it != observers_.end(); |
- ++it) { |
- if (!it->second.scanning_extensions.empty()) |
- return true; |
- } |
- return false; |
-} |
- |
-void MediaScanManager::OnScanCompleted( |
- bool success, |
- const MediaFolderFinder::MediaFolderFinderResults& found_folders) { |
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
- if (!folder_finder_ || !success) { |
- folder_finder_.reset(); |
- return; |
- } |
- |
- UMA_HISTOGRAM_COUNTS_10000("MediaGalleries.ScanDirectoriesFound", |
- found_folders.size()); |
- DCHECK(!scan_start_time_.is_null()); |
- UMA_HISTOGRAM_LONG_TIMES("MediaGalleries.ScanFinishedTime", |
- base::Time::Now() - scan_start_time_); |
- scan_start_time_ = base::Time(); |
- |
- content::BrowserThread::PostTaskAndReplyWithResult( |
- content::BrowserThread::FILE, FROM_HERE, |
- base::Bind(FindContainerScanResults, |
- found_folders, |
- folder_finder_->graylisted_folders()), |
- base::Bind(&MediaScanManager::OnFoundContainerDirectories, |
- weak_factory_.GetWeakPtr(), |
- found_folders)); |
-} |
- |
-void MediaScanManager::OnFoundContainerDirectories( |
- const MediaFolderFinder::MediaFolderFinderResults& found_folders, |
- const MediaFolderFinder::MediaFolderFinderResults& container_folders) { |
- MediaFolderFinder::MediaFolderFinderResults folders; |
- folders.insert(found_folders.begin(), found_folders.end()); |
- folders.insert(container_folders.begin(), container_folders.end()); |
- |
- for (ScanObserverMap::iterator scans_for_profile = observers_.begin(); |
- scans_for_profile != observers_.end(); |
- ++scans_for_profile) { |
- if (scans_for_profile->second.scanning_extensions.empty()) |
- continue; |
- Profile* profile = scans_for_profile->first; |
- MediaGalleriesPreferences* preferences = |
- MediaGalleriesPreferencesFactory::GetForProfile(profile); |
- ExtensionService* extension_service = |
- extensions::ExtensionSystem::Get(profile)->extension_service(); |
- if (!extension_service) |
- continue; |
- |
- AddScanResultsForProfile(preferences, folders); |
- |
- ScanningExtensionIdSet* scanning_extensions = |
- &scans_for_profile->second.scanning_extensions; |
- for (ScanningExtensionIdSet::const_iterator extension_id_it = |
- scanning_extensions->begin(); |
- extension_id_it != scanning_extensions->end(); |
- ++extension_id_it) { |
- const extensions::Extension* extension = |
- extension_service->GetExtensionById(*extension_id_it, false); |
- if (extension) { |
- MediaGalleryScanResult file_counts; |
- int gallery_count = CountScanResultsForExtension(preferences, extension, |
- &file_counts); |
- scans_for_profile->second.observer->OnScanFinished(*extension_id_it, |
- gallery_count, |
- file_counts); |
- } |
- } |
- scanning_extensions->clear(); |
- preferences->SetLastScanCompletionTime(base::Time::Now()); |
- } |
- scoped_extension_registry_observer_.RemoveAll(); |
- folder_finder_.reset(); |
-} |