Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(491)

Side by Side Diff: chrome/browser/media_galleries/media_folder_finder.cc

Issue 1695563002: Media Galleries Partial Deprecation: Remove scan functionality. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 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_folder_finder.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <algorithm>
11 #include <set>
12
13 #include "base/files/file_enumerator.h"
14 #include "base/files/file_util.h"
15 #include "base/macros.h"
16 #include "base/path_service.h"
17 #include "base/sequence_checker.h"
18 #include "base/stl_util.h"
19 #include "base/strings/string_util.h"
20 #include "base/task_runner_util.h"
21 #include "base/threading/sequenced_worker_pool.h"
22 #include "build/build_config.h"
23 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
24 #include "chrome/browser/media_galleries/fileapi/media_path_filter.h"
25 #include "chrome/common/chrome_paths.h"
26 #include "components/storage_monitor/storage_monitor.h"
27 #include "content/public/browser/browser_thread.h"
28
29 #if defined(OS_CHROMEOS)
30 #include "chrome/common/chrome_paths.h"
31 #include "chromeos/dbus/cros_disks_client.h"
32 #endif
33
34 using storage_monitor::StorageInfo;
35 using storage_monitor::StorageMonitor;
36
37 typedef base::Callback<void(const std::vector<base::FilePath>& /*roots*/)>
38 DefaultScanRootsCallback;
39 using content::BrowserThread;
40
41 namespace {
42
43 const int64_t kMinimumImageSize = 200 * 1024; // 200 KB
44 const int64_t kMinimumAudioSize = 500 * 1024; // 500 KB
45 const int64_t kMinimumVideoSize = 1024 * 1024; // 1 MB
46
47 const int kPrunedPaths[] = {
48 #if defined(OS_WIN)
49 base::DIR_IE_INTERNET_CACHE,
50 base::DIR_PROGRAM_FILES,
51 base::DIR_PROGRAM_FILESX86,
52 base::DIR_WINDOWS,
53 #endif
54 #if defined(OS_MACOSX)
55 chrome::DIR_USER_APPLICATIONS,
56 chrome::DIR_USER_LIBRARY,
57 #endif
58 #if defined(OS_LINUX)
59 base::DIR_CACHE,
60 #endif
61 #if defined(OS_WIN) || defined(OS_LINUX)
62 base::DIR_TEMP,
63 #endif
64 };
65
66 bool IsValidScanPath(const base::FilePath& path) {
67 return !path.empty() && path.IsAbsolute();
68 }
69
70 void CountScanResult(MediaGalleryScanFileType type,
71 MediaGalleryScanResult* scan_result) {
72 if (type & MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE)
73 scan_result->image_count += 1;
74 if (type & MEDIA_GALLERY_SCAN_FILE_TYPE_AUDIO)
75 scan_result->audio_count += 1;
76 if (type & MEDIA_GALLERY_SCAN_FILE_TYPE_VIDEO)
77 scan_result->video_count += 1;
78 }
79
80 bool FileMeetsSizeRequirement(MediaGalleryScanFileType type, int64_t size) {
81 if (type & MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE)
82 if (size >= kMinimumImageSize)
83 return true;
84 if (type & MEDIA_GALLERY_SCAN_FILE_TYPE_AUDIO)
85 if (size >= kMinimumAudioSize)
86 return true;
87 if (type & MEDIA_GALLERY_SCAN_FILE_TYPE_VIDEO)
88 if (size >= kMinimumVideoSize)
89 return true;
90 return false;
91 }
92
93 // Return true if |path| should not be considered as the starting point for a
94 // media scan.
95 bool ShouldIgnoreScanRoot(const base::FilePath& path) {
96 #if defined(OS_MACOSX)
97 // Scanning root is of little value.
98 return (path.value() == "/");
99 #elif defined(OS_CHROMEOS)
100 // Sanity check to make sure mount points are where they should be.
101 base::FilePath mount_point =
102 chromeos::CrosDisksClient::GetRemovableDiskMountPoint();
103 return mount_point.IsParent(path);
104 #elif defined(OS_LINUX)
105 // /media and /mnt are likely the only places with interesting mount points.
106 if (base::StartsWith(path.value(), "/media", base::CompareCase::SENSITIVE) ||
107 base::StartsWith(path.value(), "/mnt", base::CompareCase::SENSITIVE)) {
108 return false;
109 }
110 return true;
111 #elif defined(OS_WIN)
112 return false;
113 #else
114 NOTIMPLEMENTED();
115 return false;
116 #endif
117 }
118
119 // Return a location that is likely to have user data to scan, if any.
120 base::FilePath GetPlatformSpecificDefaultScanRoot() {
121 base::FilePath root;
122 #if defined(OS_CHROMEOS)
123 PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS_SAFE, &root);
124 #elif defined(OS_MACOSX) || defined(OS_LINUX)
125 PathService::Get(base::DIR_HOME, &root);
126 #elif defined(OS_WIN)
127 // Nothing to add.
128 #else
129 NOTIMPLEMENTED();
130 #endif
131 return root;
132 }
133
134 // Find the likely locations with user media files and pass them to
135 // |callback|. Locations are platform specific.
136 void GetDefaultScanRoots(const DefaultScanRootsCallback& callback,
137 bool has_override,
138 const std::vector<base::FilePath>& override_paths) {
139 DCHECK_CURRENTLY_ON(BrowserThread::UI);
140
141 if (has_override) {
142 callback.Run(override_paths);
143 return;
144 }
145
146 StorageMonitor* monitor = StorageMonitor::GetInstance();
147 DCHECK(monitor->IsInitialized());
148
149 std::vector<base::FilePath> roots;
150 std::vector<StorageInfo> storages = monitor->GetAllAvailableStorages();
151 for (size_t i = 0; i < storages.size(); ++i) {
152 StorageInfo::Type type;
153 if (!StorageInfo::CrackDeviceId(storages[i].device_id(), &type, NULL) ||
154 (type != StorageInfo::FIXED_MASS_STORAGE &&
155 type != StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM)) {
156 continue;
157 }
158 base::FilePath path(storages[i].location());
159 if (ShouldIgnoreScanRoot(path))
160 continue;
161 roots.push_back(path);
162 }
163
164 base::FilePath platform_root = GetPlatformSpecificDefaultScanRoot();
165 if (!platform_root.empty())
166 roots.push_back(platform_root);
167 callback.Run(roots);
168 }
169
170 } // namespace
171
172 MediaFolderFinder::WorkerReply::WorkerReply() {}
173
174 MediaFolderFinder::WorkerReply::WorkerReply(const WorkerReply& other) = default;
175
176 MediaFolderFinder::WorkerReply::~WorkerReply() {}
177
178 // The Worker is created on the UI thread, but does all its work on a blocking
179 // SequencedTaskRunner.
180 class MediaFolderFinder::Worker {
181 public:
182 explicit Worker(const std::vector<base::FilePath>& graylisted_folders);
183 ~Worker();
184
185 // Scans |path| and return the results.
186 WorkerReply ScanFolder(const base::FilePath& path);
187
188 private:
189 void MakeFolderPathsAbsolute();
190
191 bool folder_paths_are_absolute_;
192 std::vector<base::FilePath> graylisted_folders_;
193 std::vector<base::FilePath> pruned_folders_;
194
195 scoped_ptr<MediaPathFilter> filter_;
196
197 base::SequenceChecker sequence_checker_;
198
199 DISALLOW_COPY_AND_ASSIGN(Worker);
200 };
201
202 MediaFolderFinder::Worker::Worker(
203 const std::vector<base::FilePath>& graylisted_folders)
204 : folder_paths_are_absolute_(false),
205 graylisted_folders_(graylisted_folders),
206 filter_(new MediaPathFilter) {
207 DCHECK_CURRENTLY_ON(BrowserThread::UI);
208
209 for (size_t i = 0; i < arraysize(kPrunedPaths); ++i) {
210 base::FilePath path;
211 if (PathService::Get(kPrunedPaths[i], &path))
212 pruned_folders_.push_back(path);
213 }
214
215 sequence_checker_.DetachFromSequence();
216 }
217
218 MediaFolderFinder::Worker::~Worker() {
219 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
220 }
221
222 MediaFolderFinder::WorkerReply MediaFolderFinder::Worker::ScanFolder(
223 const base::FilePath& path) {
224 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
225 CHECK(IsValidScanPath(path));
226
227 if (!folder_paths_are_absolute_)
228 MakeFolderPathsAbsolute();
229
230 WorkerReply reply;
231 bool folder_meets_size_requirement = false;
232 bool is_graylisted_folder = false;
233 base::FilePath abspath = base::MakeAbsoluteFilePath(path);
234 if (abspath.empty())
235 return reply;
236
237 for (size_t i = 0; i < graylisted_folders_.size(); ++i) {
238 if (abspath == graylisted_folders_[i] ||
239 abspath.IsParent(graylisted_folders_[i])) {
240 is_graylisted_folder = true;
241 break;
242 }
243 }
244
245 base::FileEnumerator enumerator(
246 path,
247 false, /* recursive? */
248 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES
249 #if defined(OS_POSIX)
250 | base::FileEnumerator::SHOW_SYM_LINKS // show symlinks, not follow.
251 #endif
252 ); // NOLINT
253 while (!enumerator.Next().empty()) {
254 base::FileEnumerator::FileInfo file_info = enumerator.GetInfo();
255 base::FilePath full_path = path.Append(file_info.GetName());
256 if (MediaPathFilter::ShouldSkip(full_path))
257 continue;
258
259 // Enumerating a directory.
260 if (file_info.IsDirectory()) {
261 bool is_pruned_folder = false;
262 base::FilePath abs_full_path = base::MakeAbsoluteFilePath(full_path);
263 if (abs_full_path.empty())
264 continue;
265 for (size_t i = 0; i < pruned_folders_.size(); ++i) {
266 if (abs_full_path == pruned_folders_[i]) {
267 is_pruned_folder = true;
268 break;
269 }
270 }
271
272 if (!is_pruned_folder)
273 reply.new_folders.push_back(full_path);
274 continue;
275 }
276
277 // Enumerating a file.
278 //
279 // Do not include scan results for graylisted folders.
280 if (is_graylisted_folder)
281 continue;
282
283 MediaGalleryScanFileType type = filter_->GetType(full_path);
284 if (type == MEDIA_GALLERY_SCAN_FILE_TYPE_UNKNOWN)
285 continue;
286
287 CountScanResult(type, &reply.scan_result);
288 if (!folder_meets_size_requirement) {
289 folder_meets_size_requirement =
290 FileMeetsSizeRequirement(type, file_info.GetSize());
291 }
292 }
293 // Make sure there is at least 1 file above a size threshold.
294 if (!folder_meets_size_requirement)
295 reply.scan_result = MediaGalleryScanResult();
296 return reply;
297 }
298
299 void MediaFolderFinder::Worker::MakeFolderPathsAbsolute() {
300 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
301 DCHECK(!folder_paths_are_absolute_);
302 folder_paths_are_absolute_ = true;
303
304 std::vector<base::FilePath> abs_paths;
305 for (size_t i = 0; i < graylisted_folders_.size(); ++i) {
306 base::FilePath path = base::MakeAbsoluteFilePath(graylisted_folders_[i]);
307 if (!path.empty())
308 abs_paths.push_back(path);
309 }
310 graylisted_folders_ = abs_paths;
311 abs_paths.clear();
312 for (size_t i = 0; i < pruned_folders_.size(); ++i) {
313 base::FilePath path = base::MakeAbsoluteFilePath(pruned_folders_[i]);
314 if (!path.empty())
315 abs_paths.push_back(path);
316 }
317 pruned_folders_ = abs_paths;
318 }
319
320 MediaFolderFinder::MediaFolderFinder(
321 const MediaFolderFinderResultsCallback& callback)
322 : results_callback_(callback),
323 graylisted_folders_(
324 extensions::file_system_api::GetGrayListedDirectories()),
325 scan_state_(SCAN_STATE_NOT_STARTED),
326 worker_(new Worker(graylisted_folders_)),
327 has_roots_for_testing_(false),
328 weak_factory_(this) {
329 DCHECK_CURRENTLY_ON(BrowserThread::UI);
330
331 base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
332 worker_task_runner_ = pool->GetSequencedTaskRunner(pool->GetSequenceToken());
333 }
334
335 MediaFolderFinder::~MediaFolderFinder() {
336 DCHECK_CURRENTLY_ON(BrowserThread::UI);
337
338 worker_task_runner_->DeleteSoon(FROM_HERE, worker_);
339
340 if (scan_state_ == SCAN_STATE_FINISHED)
341 return;
342
343 MediaFolderFinderResults empty_results;
344 results_callback_.Run(false /* success? */, empty_results);
345 }
346
347 void MediaFolderFinder::StartScan() {
348 DCHECK_CURRENTLY_ON(BrowserThread::UI);
349
350 if (scan_state_ != SCAN_STATE_NOT_STARTED)
351 return;
352
353 scan_state_ = SCAN_STATE_STARTED;
354 GetDefaultScanRoots(
355 base::Bind(&MediaFolderFinder::OnInitialized, weak_factory_.GetWeakPtr()),
356 has_roots_for_testing_,
357 roots_for_testing_);
358 }
359
360 const std::vector<base::FilePath>&
361 MediaFolderFinder::graylisted_folders() const {
362 return graylisted_folders_;
363 }
364
365 void MediaFolderFinder::SetRootsForTesting(
366 const std::vector<base::FilePath>& roots) {
367 DCHECK_CURRENTLY_ON(BrowserThread::UI);
368 DCHECK_EQ(SCAN_STATE_NOT_STARTED, scan_state_);
369
370 has_roots_for_testing_ = true;
371 roots_for_testing_ = roots;
372 }
373
374 void MediaFolderFinder::OnInitialized(
375 const std::vector<base::FilePath>& roots) {
376 DCHECK_EQ(SCAN_STATE_STARTED, scan_state_);
377
378 std::set<base::FilePath> valid_roots;
379 for (size_t i = 0; i < roots.size(); ++i) {
380 // Skip if |path| is invalid or redundant.
381 const base::FilePath& path = roots[i];
382 if (!IsValidScanPath(path))
383 continue;
384 if (ContainsKey(valid_roots, path))
385 continue;
386
387 // Check for overlap.
388 bool valid_roots_contains_path = false;
389 std::vector<base::FilePath> overlapping_paths_to_remove;
390 for (std::set<base::FilePath>::iterator it = valid_roots.begin();
391 it != valid_roots.end(); ++it) {
392 if (it->IsParent(path)) {
393 valid_roots_contains_path = true;
394 break;
395 }
396 const base::FilePath& other_path = *it;
397 if (path.IsParent(other_path))
398 overlapping_paths_to_remove.push_back(other_path);
399 }
400 if (valid_roots_contains_path)
401 continue;
402 // Remove anything |path| overlaps from |valid_roots|.
403 for (size_t i = 0; i < overlapping_paths_to_remove.size(); ++i)
404 valid_roots.erase(overlapping_paths_to_remove[i]);
405
406 valid_roots.insert(path);
407 }
408
409 std::copy(valid_roots.begin(), valid_roots.end(),
410 std::back_inserter(folders_to_scan_));
411 ScanFolder();
412 }
413
414 void MediaFolderFinder::ScanFolder() {
415 DCHECK_CURRENTLY_ON(BrowserThread::UI);
416 DCHECK_EQ(SCAN_STATE_STARTED, scan_state_);
417
418 if (folders_to_scan_.empty()) {
419 scan_state_ = SCAN_STATE_FINISHED;
420 results_callback_.Run(true /* success? */, results_);
421 return;
422 }
423
424 base::FilePath folder_to_scan = folders_to_scan_.back();
425 folders_to_scan_.pop_back();
426 base::PostTaskAndReplyWithResult(
427 worker_task_runner_.get(),
428 FROM_HERE,
429 base::Bind(
430 &Worker::ScanFolder, base::Unretained(worker_), folder_to_scan),
431 base::Bind(&MediaFolderFinder::GotScanResults,
432 weak_factory_.GetWeakPtr(),
433 folder_to_scan));
434 }
435
436 void MediaFolderFinder::GotScanResults(const base::FilePath& path,
437 const WorkerReply& reply) {
438 DCHECK_CURRENTLY_ON(BrowserThread::UI);
439 DCHECK_EQ(SCAN_STATE_STARTED, scan_state_);
440 DCHECK(!path.empty());
441 CHECK(!ContainsKey(results_, path));
442
443 if (!IsEmptyScanResult(reply.scan_result))
444 results_[path] = reply.scan_result;
445
446 // Push new folders to the |folders_to_scan_| in reverse order.
447 std::copy(reply.new_folders.rbegin(), reply.new_folders.rend(),
448 std::back_inserter(folders_to_scan_));
449
450 ScanFolder();
451 }
OLDNEW
« no previous file with comments | « chrome/browser/media_galleries/media_folder_finder.h ('k') | chrome/browser/media_galleries/media_folder_finder_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698