Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / media_galleries / fileapi / device_media_async_file_util.cc
blobdce3a583e734f42bb05713413017a874879d22de
1 // Copyright (c) 2013 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.
5 #include "chrome/browser/media_galleries/fileapi/device_media_async_file_util.h"
7 #include "base/callback.h"
8 #include "base/files/file_util.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/task_runner_util.h"
11 #include "chrome/browser/media_galleries/fileapi/media_path_filter.h"
12 #include "chrome/browser/media_galleries/fileapi/mtp_device_async_delegate.h"
13 #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h"
14 #include "chrome/browser/media_galleries/fileapi/mtp_file_stream_reader.h"
15 #include "chrome/browser/media_galleries/fileapi/native_media_file_util.h"
16 #include "chrome/browser/media_galleries/fileapi/readahead_file_stream_reader.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "storage/browser/blob/file_stream_reader.h"
19 #include "storage/browser/fileapi/file_system_context.h"
20 #include "storage/browser/fileapi/file_system_operation_context.h"
21 #include "storage/browser/fileapi/file_system_url.h"
22 #include "storage/browser/fileapi/native_file_util.h"
23 #include "storage/common/blob/shareable_file_reference.h"
25 using storage::AsyncFileUtil;
26 using storage::FileSystemOperationContext;
27 using storage::FileSystemURL;
28 using storage::ShareableFileReference;
30 namespace {
32 const char kDeviceMediaAsyncFileUtilTempDir[] = "DeviceMediaFileSystem";
34 MTPDeviceAsyncDelegate* GetMTPDeviceDelegate(const FileSystemURL& url) {
35 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
36 return MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(
37 url.filesystem_id());
40 // Called when GetFileInfo method call failed to get the details of file
41 // specified by the requested url. |callback| is invoked to notify the
42 // caller about the file |error|.
43 void OnGetFileInfoError(const AsyncFileUtil::GetFileInfoCallback& callback,
44 base::File::Error error) {
45 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
46 callback.Run(error, base::File::Info());
49 // Called after OnDidGetFileInfo finishes media check.
50 // |callback| is invoked to complete the GetFileInfo request.
51 void OnDidCheckMediaForGetFileInfo(
52 const AsyncFileUtil::GetFileInfoCallback& callback,
53 const base::File::Info& file_info,
54 bool is_valid_file) {
55 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
56 if (!is_valid_file) {
57 OnGetFileInfoError(callback, base::File::FILE_ERROR_NOT_FOUND);
58 return;
60 callback.Run(base::File::FILE_OK, file_info);
63 // Called after OnDidReadDirectory finishes media check.
64 // |callback| is invoked to complete the ReadDirectory request.
65 void OnDidCheckMediaForReadDirectory(
66 const AsyncFileUtil::ReadDirectoryCallback& callback,
67 bool has_more,
68 const AsyncFileUtil::EntryList& file_list) {
69 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
70 callback.Run(base::File::FILE_OK, file_list, has_more);
73 // Called when ReadDirectory method call failed to enumerate the directory
74 // objects. |callback| is invoked to notify the caller about the |error|
75 // that occured while reading the directory objects.
76 void OnReadDirectoryError(const AsyncFileUtil::ReadDirectoryCallback& callback,
77 base::File::Error error) {
78 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
79 callback.Run(error, AsyncFileUtil::EntryList(), false /*no more*/);
82 // Called on a blocking pool thread to create a snapshot file to hold the
83 // contents of |device_file_path|. The snapshot file is created in the
84 // "profile_path/kDeviceMediaAsyncFileUtilTempDir" directory. Return the
85 // snapshot file path or an empty path on failure.
86 base::FilePath CreateSnapshotFileOnBlockingPool(
87 const base::FilePath& device_file_path,
88 const base::FilePath& profile_path) {
89 base::FilePath snapshot_file_path;
90 base::FilePath media_file_system_dir_path =
91 profile_path.AppendASCII(kDeviceMediaAsyncFileUtilTempDir);
92 if (!base::CreateDirectory(media_file_system_dir_path) ||
93 !base::CreateTemporaryFileInDir(media_file_system_dir_path,
94 &snapshot_file_path)) {
95 LOG(WARNING) << "Could not create media snapshot file "
96 << media_file_system_dir_path.value();
97 snapshot_file_path = base::FilePath();
99 return snapshot_file_path;
102 // Called after OnDidCreateSnapshotFile finishes media check.
103 // |callback| is invoked to complete the CreateSnapshotFile request.
104 void OnDidCheckMediaForCreateSnapshotFile(
105 const AsyncFileUtil::CreateSnapshotFileCallback& callback,
106 const base::File::Info& file_info,
107 scoped_refptr<storage::ShareableFileReference> platform_file,
108 base::File::Error error) {
109 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
110 base::FilePath platform_path(platform_file.get()->path());
111 if (error != base::File::FILE_OK)
112 platform_file = NULL;
113 callback.Run(error, file_info, platform_path, platform_file);
116 // Called when the snapshot file specified by the |platform_path| is
117 // successfully created. |file_info| contains the device media file details
118 // for which the snapshot file is created.
119 void OnDidCreateSnapshotFile(
120 const AsyncFileUtil::CreateSnapshotFileCallback& callback,
121 base::SequencedTaskRunner* media_task_runner,
122 bool validate_media_files,
123 const base::File::Info& file_info,
124 const base::FilePath& platform_path) {
125 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
126 scoped_refptr<storage::ShareableFileReference> file =
127 ShareableFileReference::GetOrCreate(
128 platform_path,
129 ShareableFileReference::DELETE_ON_FINAL_RELEASE,
130 media_task_runner);
132 if (validate_media_files) {
133 base::PostTaskAndReplyWithResult(
134 media_task_runner,
135 FROM_HERE,
136 base::Bind(&NativeMediaFileUtil::IsMediaFile, platform_path),
137 base::Bind(&OnDidCheckMediaForCreateSnapshotFile,
138 callback,
139 file_info,
140 file));
141 } else {
142 OnDidCheckMediaForCreateSnapshotFile(callback, file_info, file,
143 base::File::FILE_OK);
147 // Called when CreateSnapshotFile method call fails. |callback| is invoked to
148 // notify the caller about the |error|.
149 void OnCreateSnapshotFileError(
150 const AsyncFileUtil::CreateSnapshotFileCallback& callback,
151 base::File::Error error) {
152 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
153 callback.Run(error, base::File::Info(), base::FilePath(),
154 scoped_refptr<ShareableFileReference>());
157 // Called when the snapshot file specified by the |snapshot_file_path| is
158 // created to hold the contents of the url.path(). If the snapshot
159 // file is successfully created, |snapshot_file_path| will be an non-empty
160 // file path. In case of failure, |snapshot_file_path| will be an empty file
161 // path. Forwards the CreateSnapshot request to the delegate to copy the
162 // contents of url.path() to |snapshot_file_path|.
163 void OnSnapshotFileCreatedRunTask(
164 scoped_ptr<FileSystemOperationContext> context,
165 const AsyncFileUtil::CreateSnapshotFileCallback& callback,
166 const FileSystemURL& url,
167 bool validate_media_files,
168 const base::FilePath& snapshot_file_path) {
169 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
170 if (snapshot_file_path.empty()) {
171 OnCreateSnapshotFileError(callback, base::File::FILE_ERROR_FAILED);
172 return;
174 MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url);
175 if (!delegate) {
176 OnCreateSnapshotFileError(callback, base::File::FILE_ERROR_NOT_FOUND);
177 return;
179 delegate->CreateSnapshotFile(
180 url.path(), // device file path
181 snapshot_file_path,
182 base::Bind(&OnDidCreateSnapshotFile,
183 callback,
184 make_scoped_refptr(context->task_runner()),
185 validate_media_files),
186 base::Bind(&OnCreateSnapshotFileError, callback));
189 } // namespace
191 class DeviceMediaAsyncFileUtil::MediaPathFilterWrapper
192 : public base::RefCountedThreadSafe<MediaPathFilterWrapper> {
193 public:
194 MediaPathFilterWrapper();
196 // Check if entries in |file_list| look like media files.
197 // Append the ones that look like media files to |results|.
198 // Should run on a media task runner.
199 AsyncFileUtil::EntryList FilterMediaEntries(
200 const AsyncFileUtil::EntryList& file_list);
202 // Check if |path| looks like a media file.
203 bool CheckFilePath(const base::FilePath& path);
205 private:
206 friend class base::RefCountedThreadSafe<MediaPathFilterWrapper>;
208 virtual ~MediaPathFilterWrapper();
210 scoped_ptr<MediaPathFilter> media_path_filter_;
212 DISALLOW_COPY_AND_ASSIGN(MediaPathFilterWrapper);
215 DeviceMediaAsyncFileUtil::MediaPathFilterWrapper::MediaPathFilterWrapper()
216 : media_path_filter_(new MediaPathFilter) {
219 DeviceMediaAsyncFileUtil::MediaPathFilterWrapper::~MediaPathFilterWrapper() {
222 AsyncFileUtil::EntryList
223 DeviceMediaAsyncFileUtil::MediaPathFilterWrapper::FilterMediaEntries(
224 const AsyncFileUtil::EntryList& file_list) {
225 AsyncFileUtil::EntryList results;
226 for (size_t i = 0; i < file_list.size(); ++i) {
227 const storage::DirectoryEntry& entry = file_list[i];
228 if (entry.is_directory || CheckFilePath(base::FilePath(entry.name))) {
229 results.push_back(entry);
232 return results;
235 bool DeviceMediaAsyncFileUtil::MediaPathFilterWrapper::CheckFilePath(
236 const base::FilePath& path) {
237 return media_path_filter_->Match(path);
240 DeviceMediaAsyncFileUtil::~DeviceMediaAsyncFileUtil() {
243 // static
244 scoped_ptr<DeviceMediaAsyncFileUtil> DeviceMediaAsyncFileUtil::Create(
245 const base::FilePath& profile_path,
246 MediaFileValidationType validation_type) {
247 DCHECK(!profile_path.empty());
248 return make_scoped_ptr(
249 new DeviceMediaAsyncFileUtil(profile_path, validation_type));
252 bool DeviceMediaAsyncFileUtil::SupportsStreaming(
253 const storage::FileSystemURL& url) {
254 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
255 MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url);
256 if (!delegate)
257 return false;
258 return delegate->IsStreaming();
261 void DeviceMediaAsyncFileUtil::CreateOrOpen(
262 scoped_ptr<FileSystemOperationContext> context,
263 const FileSystemURL& url,
264 int file_flags,
265 const CreateOrOpenCallback& callback) {
266 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
267 // Returns an error if any unsupported flag is found.
268 if (file_flags & ~(base::File::FLAG_OPEN |
269 base::File::FLAG_READ |
270 base::File::FLAG_WRITE_ATTRIBUTES)) {
271 callback.Run(base::File(base::File::FILE_ERROR_SECURITY), base::Closure());
272 return;
274 CreateSnapshotFile(
275 context.Pass(),
276 url,
277 base::Bind(&NativeMediaFileUtil::CreatedSnapshotFileForCreateOrOpen,
278 make_scoped_refptr(context->task_runner()),
279 file_flags,
280 callback));
283 void DeviceMediaAsyncFileUtil::EnsureFileExists(
284 scoped_ptr<FileSystemOperationContext> context,
285 const FileSystemURL& url,
286 const EnsureFileExistsCallback& callback) {
287 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
288 NOTIMPLEMENTED();
289 callback.Run(base::File::FILE_ERROR_SECURITY, false);
292 void DeviceMediaAsyncFileUtil::CreateDirectory(
293 scoped_ptr<FileSystemOperationContext> context,
294 const FileSystemURL& url,
295 bool exclusive,
296 bool recursive,
297 const StatusCallback& callback) {
298 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
299 NOTIMPLEMENTED();
300 callback.Run(base::File::FILE_ERROR_SECURITY);
303 void DeviceMediaAsyncFileUtil::GetFileInfo(
304 scoped_ptr<FileSystemOperationContext> context,
305 const FileSystemURL& url,
306 const GetFileInfoCallback& callback) {
307 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
308 MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url);
309 if (!delegate) {
310 OnGetFileInfoError(callback, base::File::FILE_ERROR_NOT_FOUND);
311 return;
313 delegate->GetFileInfo(
314 url.path(),
315 base::Bind(&DeviceMediaAsyncFileUtil::OnDidGetFileInfo,
316 weak_ptr_factory_.GetWeakPtr(),
317 make_scoped_refptr(context->task_runner()),
318 url.path(),
319 callback),
320 base::Bind(&OnGetFileInfoError, callback));
323 void DeviceMediaAsyncFileUtil::ReadDirectory(
324 scoped_ptr<FileSystemOperationContext> context,
325 const FileSystemURL& url,
326 const ReadDirectoryCallback& callback) {
327 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
328 MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url);
329 if (!delegate) {
330 OnReadDirectoryError(callback, base::File::FILE_ERROR_NOT_FOUND);
331 return;
333 delegate->ReadDirectory(
334 url.path(),
335 base::Bind(&DeviceMediaAsyncFileUtil::OnDidReadDirectory,
336 weak_ptr_factory_.GetWeakPtr(),
337 make_scoped_refptr(context->task_runner()),
338 callback),
339 base::Bind(&OnReadDirectoryError, callback));
342 void DeviceMediaAsyncFileUtil::Touch(
343 scoped_ptr<FileSystemOperationContext> context,
344 const FileSystemURL& url,
345 const base::Time& last_access_time,
346 const base::Time& last_modified_time,
347 const StatusCallback& callback) {
348 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
349 NOTIMPLEMENTED();
350 callback.Run(base::File::FILE_ERROR_SECURITY);
353 void DeviceMediaAsyncFileUtil::Truncate(
354 scoped_ptr<FileSystemOperationContext> context,
355 const FileSystemURL& url,
356 int64 length,
357 const StatusCallback& callback) {
358 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
359 NOTIMPLEMENTED();
360 callback.Run(base::File::FILE_ERROR_SECURITY);
363 void DeviceMediaAsyncFileUtil::CopyFileLocal(
364 scoped_ptr<FileSystemOperationContext> context,
365 const FileSystemURL& src_url,
366 const FileSystemURL& dest_url,
367 CopyOrMoveOption option,
368 const CopyFileProgressCallback& progress_callback,
369 const StatusCallback& callback) {
370 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
371 NOTIMPLEMENTED();
372 callback.Run(base::File::FILE_ERROR_SECURITY);
375 void DeviceMediaAsyncFileUtil::MoveFileLocal(
376 scoped_ptr<FileSystemOperationContext> context,
377 const FileSystemURL& src_url,
378 const FileSystemURL& dest_url,
379 CopyOrMoveOption option,
380 const StatusCallback& callback) {
381 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
382 NOTIMPLEMENTED();
383 callback.Run(base::File::FILE_ERROR_SECURITY);
386 void DeviceMediaAsyncFileUtil::CopyInForeignFile(
387 scoped_ptr<FileSystemOperationContext> context,
388 const base::FilePath& src_file_path,
389 const FileSystemURL& dest_url,
390 const StatusCallback& callback) {
391 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
392 NOTIMPLEMENTED();
393 callback.Run(base::File::FILE_ERROR_SECURITY);
396 void DeviceMediaAsyncFileUtil::DeleteFile(
397 scoped_ptr<FileSystemOperationContext> context,
398 const FileSystemURL& url,
399 const StatusCallback& callback) {
400 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
401 NOTIMPLEMENTED();
402 callback.Run(base::File::FILE_ERROR_SECURITY);
405 void DeviceMediaAsyncFileUtil::DeleteDirectory(
406 scoped_ptr<FileSystemOperationContext> context,
407 const FileSystemURL& url,
408 const StatusCallback& callback) {
409 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
410 NOTIMPLEMENTED();
411 callback.Run(base::File::FILE_ERROR_SECURITY);
414 void DeviceMediaAsyncFileUtil::DeleteRecursively(
415 scoped_ptr<FileSystemOperationContext> context,
416 const FileSystemURL& url,
417 const StatusCallback& callback) {
418 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
419 callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
422 void DeviceMediaAsyncFileUtil::CreateSnapshotFile(
423 scoped_ptr<FileSystemOperationContext> context,
424 const FileSystemURL& url,
425 const CreateSnapshotFileCallback& callback) {
426 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
427 MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url);
428 if (!delegate) {
429 OnCreateSnapshotFileError(callback, base::File::FILE_ERROR_NOT_FOUND);
430 return;
433 scoped_refptr<base::SequencedTaskRunner> task_runner(context->task_runner());
434 base::PostTaskAndReplyWithResult(
435 task_runner.get(),
436 FROM_HERE,
437 base::Bind(&CreateSnapshotFileOnBlockingPool, url.path(), profile_path_),
438 base::Bind(&OnSnapshotFileCreatedRunTask,
439 base::Passed(&context),
440 callback,
441 url,
442 validate_media_files()));
445 scoped_ptr<storage::FileStreamReader>
446 DeviceMediaAsyncFileUtil::GetFileStreamReader(
447 const FileSystemURL& url,
448 int64 offset,
449 const base::Time& expected_modification_time,
450 storage::FileSystemContext* context) {
451 MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url);
452 if (!delegate)
453 return scoped_ptr<storage::FileStreamReader>();
455 DCHECK(delegate->IsStreaming());
456 return scoped_ptr<storage::FileStreamReader>(new ReadaheadFileStreamReader(
457 new MTPFileStreamReader(context,
458 url,
459 offset,
460 expected_modification_time,
461 validate_media_files())));
464 DeviceMediaAsyncFileUtil::DeviceMediaAsyncFileUtil(
465 const base::FilePath& profile_path,
466 MediaFileValidationType validation_type)
467 : profile_path_(profile_path),
468 weak_ptr_factory_(this) {
469 if (validation_type == APPLY_MEDIA_FILE_VALIDATION) {
470 media_path_filter_wrapper_ = new MediaPathFilterWrapper;
474 void DeviceMediaAsyncFileUtil::OnDidGetFileInfo(
475 base::SequencedTaskRunner* task_runner,
476 const base::FilePath& path,
477 const AsyncFileUtil::GetFileInfoCallback& callback,
478 const base::File::Info& file_info) {
479 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
480 if (file_info.is_directory || !validate_media_files()) {
481 OnDidCheckMediaForGetFileInfo(callback, file_info, true /* valid */);
482 return;
485 base::PostTaskAndReplyWithResult(
486 task_runner,
487 FROM_HERE,
488 base::Bind(&MediaPathFilterWrapper::CheckFilePath,
489 media_path_filter_wrapper_,
490 path),
491 base::Bind(&OnDidCheckMediaForGetFileInfo, callback, file_info));
494 void DeviceMediaAsyncFileUtil::OnDidReadDirectory(
495 base::SequencedTaskRunner* task_runner,
496 const AsyncFileUtil::ReadDirectoryCallback& callback,
497 const AsyncFileUtil::EntryList& file_list,
498 bool has_more) {
499 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
500 if (!validate_media_files()) {
501 OnDidCheckMediaForReadDirectory(callback, has_more, file_list);
502 return;
505 base::PostTaskAndReplyWithResult(
506 task_runner,
507 FROM_HERE,
508 base::Bind(&MediaPathFilterWrapper::FilterMediaEntries,
509 media_path_filter_wrapper_,
510 file_list),
511 base::Bind(&OnDidCheckMediaForReadDirectory, callback, has_more));
514 bool DeviceMediaAsyncFileUtil::validate_media_files() const {
515 return media_path_filter_wrapper_.get() != NULL;