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/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 "content/public/browser/browser_thread.h"
17 #include "net/base/io_buffer.h"
18 #include "net/base/mime_sniffer.h"
19 #include "webkit/browser/blob/file_stream_reader.h"
20 #include "webkit/browser/fileapi/file_system_context.h"
21 #include "webkit/browser/fileapi/file_system_operation_context.h"
22 #include "webkit/browser/fileapi/file_system_url.h"
23 #include "webkit/common/blob/shareable_file_reference.h"
25 using fileapi::FileSystemOperationContext
;
26 using fileapi::FileSystemURL
;
27 using webkit_blob::ShareableFileReference
;
31 const char kDeviceMediaAsyncFileUtilTempDir
[] = "DeviceMediaFileSystem";
33 // Called on the IO thread.
34 MTPDeviceAsyncDelegate
* GetMTPDeviceDelegate(const FileSystemURL
& url
) {
35 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
36 return MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(
40 // Called on a blocking pool thread to create a snapshot file to hold the
41 // contents of |device_file_path|. The snapshot file is created in
42 // "profile_path/kDeviceMediaAsyncFileUtilTempDir" directory. If the snapshot
43 // file is created successfully, |snapshot_file_path| will be a non-empty file
44 // path. In case of failure, the |snapshot_file_path| will be an empty file
46 void CreateSnapshotFileOnBlockingPool(
47 const base::FilePath
& device_file_path
,
48 const base::FilePath
& profile_path
,
49 base::FilePath
* snapshot_file_path
) {
50 DCHECK(snapshot_file_path
);
51 base::FilePath isolated_media_file_system_dir_path
=
52 profile_path
.AppendASCII(kDeviceMediaAsyncFileUtilTempDir
);
53 if (!base::CreateDirectory(isolated_media_file_system_dir_path
) ||
54 !base::CreateTemporaryFileInDir(isolated_media_file_system_dir_path
,
55 snapshot_file_path
)) {
56 LOG(WARNING
) << "Could not create media snapshot file "
57 << isolated_media_file_system_dir_path
.value();
58 *snapshot_file_path
= base::FilePath();
64 DeviceMediaAsyncFileUtil::~DeviceMediaAsyncFileUtil() {
68 DeviceMediaAsyncFileUtil
* DeviceMediaAsyncFileUtil::Create(
69 const base::FilePath
& profile_path
) {
70 DCHECK(!profile_path
.empty());
71 return new DeviceMediaAsyncFileUtil(profile_path
);
74 void DeviceMediaAsyncFileUtil::CreateOrOpen(
75 scoped_ptr
<FileSystemOperationContext
> context
,
76 const FileSystemURL
& url
,
78 const CreateOrOpenCallback
& callback
) {
79 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
81 base::PlatformFile invalid_file
= base::kInvalidPlatformFileValue
;
82 callback
.Run(base::File::FILE_ERROR_SECURITY
,
83 base::PassPlatformFile(&invalid_file
),
87 void DeviceMediaAsyncFileUtil::EnsureFileExists(
88 scoped_ptr
<FileSystemOperationContext
> context
,
89 const FileSystemURL
& url
,
90 const EnsureFileExistsCallback
& callback
) {
91 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
93 callback
.Run(base::File::FILE_ERROR_SECURITY
, false);
96 void DeviceMediaAsyncFileUtil::CreateDirectory(
97 scoped_ptr
<FileSystemOperationContext
> context
,
98 const FileSystemURL
& url
,
101 const StatusCallback
& callback
) {
102 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
104 callback
.Run(base::File::FILE_ERROR_SECURITY
);
107 void DeviceMediaAsyncFileUtil::GetFileInfo(
108 scoped_ptr
<FileSystemOperationContext
> context
,
109 const FileSystemURL
& url
,
110 const GetFileInfoCallback
& callback
) {
111 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
112 MTPDeviceAsyncDelegate
* delegate
= GetMTPDeviceDelegate(url
);
114 OnGetFileInfoError(callback
, base::File::FILE_ERROR_NOT_FOUND
);
117 delegate
->GetFileInfo(
119 base::Bind(&DeviceMediaAsyncFileUtil::OnDidGetFileInfo
,
120 weak_ptr_factory_
.GetWeakPtr(),
122 base::Bind(&DeviceMediaAsyncFileUtil::OnGetFileInfoError
,
123 weak_ptr_factory_
.GetWeakPtr(),
127 void DeviceMediaAsyncFileUtil::ReadDirectory(
128 scoped_ptr
<FileSystemOperationContext
> context
,
129 const FileSystemURL
& url
,
130 const ReadDirectoryCallback
& callback
) {
131 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
132 MTPDeviceAsyncDelegate
* delegate
= GetMTPDeviceDelegate(url
);
134 OnReadDirectoryError(callback
, base::File::FILE_ERROR_NOT_FOUND
);
137 delegate
->ReadDirectory(
139 base::Bind(&DeviceMediaAsyncFileUtil::OnDidReadDirectory
,
140 weak_ptr_factory_
.GetWeakPtr(),
142 base::Bind(&DeviceMediaAsyncFileUtil::OnReadDirectoryError
,
143 weak_ptr_factory_
.GetWeakPtr(),
147 void DeviceMediaAsyncFileUtil::Touch(
148 scoped_ptr
<FileSystemOperationContext
> context
,
149 const FileSystemURL
& url
,
150 const base::Time
& last_access_time
,
151 const base::Time
& last_modified_time
,
152 const StatusCallback
& callback
) {
153 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
155 callback
.Run(base::File::FILE_ERROR_SECURITY
);
158 void DeviceMediaAsyncFileUtil::Truncate(
159 scoped_ptr
<FileSystemOperationContext
> context
,
160 const FileSystemURL
& url
,
162 const StatusCallback
& callback
) {
163 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
165 callback
.Run(base::File::FILE_ERROR_SECURITY
);
168 void DeviceMediaAsyncFileUtil::CopyFileLocal(
169 scoped_ptr
<FileSystemOperationContext
> context
,
170 const FileSystemURL
& src_url
,
171 const FileSystemURL
& dest_url
,
172 CopyOrMoveOption option
,
173 const CopyFileProgressCallback
& progress_callback
,
174 const StatusCallback
& callback
) {
175 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
177 callback
.Run(base::File::FILE_ERROR_SECURITY
);
180 void DeviceMediaAsyncFileUtil::MoveFileLocal(
181 scoped_ptr
<FileSystemOperationContext
> context
,
182 const FileSystemURL
& src_url
,
183 const FileSystemURL
& dest_url
,
184 CopyOrMoveOption option
,
185 const StatusCallback
& callback
) {
186 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
188 callback
.Run(base::File::FILE_ERROR_SECURITY
);
191 void DeviceMediaAsyncFileUtil::CopyInForeignFile(
192 scoped_ptr
<FileSystemOperationContext
> context
,
193 const base::FilePath
& src_file_path
,
194 const FileSystemURL
& dest_url
,
195 const StatusCallback
& callback
) {
196 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
198 callback
.Run(base::File::FILE_ERROR_SECURITY
);
201 void DeviceMediaAsyncFileUtil::DeleteFile(
202 scoped_ptr
<FileSystemOperationContext
> context
,
203 const FileSystemURL
& url
,
204 const StatusCallback
& callback
) {
205 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
207 callback
.Run(base::File::FILE_ERROR_SECURITY
);
210 void DeviceMediaAsyncFileUtil::DeleteDirectory(
211 scoped_ptr
<FileSystemOperationContext
> context
,
212 const FileSystemURL
& url
,
213 const StatusCallback
& callback
) {
214 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
216 callback
.Run(base::File::FILE_ERROR_SECURITY
);
219 void DeviceMediaAsyncFileUtil::DeleteRecursively(
220 scoped_ptr
<FileSystemOperationContext
> context
,
221 const FileSystemURL
& url
,
222 const StatusCallback
& callback
) {
223 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
224 callback
.Run(base::File::FILE_ERROR_INVALID_OPERATION
);
227 void DeviceMediaAsyncFileUtil::CreateSnapshotFile(
228 scoped_ptr
<FileSystemOperationContext
> context
,
229 const FileSystemURL
& url
,
230 const CreateSnapshotFileCallback
& callback
) {
231 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
232 MTPDeviceAsyncDelegate
* delegate
= GetMTPDeviceDelegate(url
);
234 OnCreateSnapshotFileError(callback
, base::File::FILE_ERROR_NOT_FOUND
);
238 scoped_refptr
<base::SequencedTaskRunner
> task_runner
= context
->task_runner();
240 if (delegate
->IsStreaming()) {
244 base::Bind(&DeviceMediaAsyncFileUtil::GetHeaderBytesForMIMESniffing
,
245 weak_ptr_factory_
.GetWeakPtr(), url
, task_runner
, callback
));
249 base::FilePath
* snapshot_file_path
= new base::FilePath
;
250 const bool success
= task_runner
->PostTaskAndReply(
252 base::Bind(&CreateSnapshotFileOnBlockingPool
,
255 base::Unretained(snapshot_file_path
)),
256 base::Bind(&DeviceMediaAsyncFileUtil::OnSnapshotFileCreatedRunTask
,
257 weak_ptr_factory_
.GetWeakPtr(),
258 base::Passed(&context
),
261 base::Owned(snapshot_file_path
)));
265 scoped_ptr
<webkit_blob::FileStreamReader
>
266 DeviceMediaAsyncFileUtil::GetFileStreamReader(
267 const FileSystemURL
& url
,
269 const base::Time
& expected_modification_time
,
270 fileapi::FileSystemContext
* context
) {
271 MTPDeviceAsyncDelegate
* delegate
= GetMTPDeviceDelegate(url
);
273 return scoped_ptr
<webkit_blob::FileStreamReader
>();
275 DCHECK(delegate
->IsStreaming());
276 return scoped_ptr
<webkit_blob::FileStreamReader
>(new MTPFileStreamReader(
277 context
, url
, offset
, expected_modification_time
));
280 DeviceMediaAsyncFileUtil::DeviceMediaAsyncFileUtil(
281 const base::FilePath
& profile_path
)
282 : profile_path_(profile_path
),
283 weak_ptr_factory_(this) {
286 void DeviceMediaAsyncFileUtil::OnDidGetFileInfo(
287 const AsyncFileUtil::GetFileInfoCallback
& callback
,
288 const base::File::Info
& file_info
) {
289 callback
.Run(base::File::FILE_OK
, file_info
);
292 void DeviceMediaAsyncFileUtil::OnGetFileInfoError(
293 const AsyncFileUtil::GetFileInfoCallback
& callback
,
294 base::File::Error error
) {
295 callback
.Run(error
, base::File::Info());
298 void DeviceMediaAsyncFileUtil::OnDidReadDirectory(
299 const AsyncFileUtil::ReadDirectoryCallback
& callback
,
300 const AsyncFileUtil::EntryList
& file_list
,
302 callback
.Run(base::File::FILE_OK
, file_list
, has_more
);
305 void DeviceMediaAsyncFileUtil::OnReadDirectoryError(
306 const AsyncFileUtil::ReadDirectoryCallback
& callback
,
307 base::File::Error error
) {
308 callback
.Run(error
, AsyncFileUtil::EntryList(), false /*no more*/);
311 void DeviceMediaAsyncFileUtil::OnDidCreateSnapshotFile(
312 const AsyncFileUtil::CreateSnapshotFileCallback
& callback
,
313 base::SequencedTaskRunner
* media_task_runner
,
314 const base::File::Info
& file_info
,
315 const base::FilePath
& platform_path
) {
316 base::PostTaskAndReplyWithResult(
319 base::Bind(&NativeMediaFileUtil::IsMediaFile
, platform_path
),
320 base::Bind(&DeviceMediaAsyncFileUtil::OnDidCheckMedia
,
321 weak_ptr_factory_
.GetWeakPtr(),
324 ShareableFileReference::GetOrCreate(
326 ShareableFileReference::DELETE_ON_FINAL_RELEASE
,
327 media_task_runner
)));
330 void DeviceMediaAsyncFileUtil::OnDidCheckMedia(
331 const AsyncFileUtil::CreateSnapshotFileCallback
& callback
,
332 const base::File::Info
& file_info
,
333 scoped_refptr
<webkit_blob::ShareableFileReference
> platform_file
,
334 base::File::Error error
) {
335 base::FilePath
platform_path(platform_file
.get()->path());
336 if (error
!= base::File::FILE_OK
)
337 platform_file
= NULL
;
338 callback
.Run(error
, file_info
, platform_path
, platform_file
);
341 void DeviceMediaAsyncFileUtil::GetHeaderBytesForMIMESniffing(
342 const fileapi::FileSystemURL
& url
,
343 base::SequencedTaskRunner
* media_task_runner
,
344 const AsyncFileUtil::CreateSnapshotFileCallback
& callback
,
345 base::File::Error error
,
346 const base::File::Info
& file_info
) {
347 MTPDeviceAsyncDelegate
* delegate
= GetMTPDeviceDelegate(url
);
349 OnCreateSnapshotFileError(callback
, base::File::FILE_ERROR_NOT_FOUND
);
353 scoped_refptr
<net::IOBuffer
> buffer(new net::IOBuffer(net::kMaxBytesToSniff
));
358 net::kMaxBytesToSniff
,
359 base::Bind(&DeviceMediaAsyncFileUtil::FinishStreamingSnapshotFile
,
360 weak_ptr_factory_
.GetWeakPtr(), url
,
361 make_scoped_refptr(media_task_runner
), callback
, file_info
,
363 base::Bind(&DeviceMediaAsyncFileUtil::OnCreateSnapshotFileError
,
364 weak_ptr_factory_
.GetWeakPtr(), callback
));
367 void DeviceMediaAsyncFileUtil::FinishStreamingSnapshotFile(
368 const fileapi::FileSystemURL
& url
,
369 base::SequencedTaskRunner
* media_task_runner
,
370 const AsyncFileUtil::CreateSnapshotFileCallback
& callback
,
371 const base::File::Info
& file_info
,
372 net::IOBuffer
* buffer
,
374 base::File::Error error
=
375 NativeMediaFileUtil::BufferIsMediaHeader(buffer
, buffer_size
);
376 if (error
!= base::File::FILE_OK
) {
377 OnCreateSnapshotFileError(callback
, error
);
380 callback
.Run(base::File::FILE_OK
, file_info
, base::FilePath(),
381 scoped_refptr
<ShareableFileReference
>());
384 void DeviceMediaAsyncFileUtil::OnCreateSnapshotFileError(
385 const AsyncFileUtil::CreateSnapshotFileCallback
& callback
,
386 base::File::Error error
) {
387 callback
.Run(error
, base::File::Info(), base::FilePath(),
388 scoped_refptr
<ShareableFileReference
>());
391 void DeviceMediaAsyncFileUtil::OnSnapshotFileCreatedRunTask(
392 scoped_ptr
<FileSystemOperationContext
> context
,
393 const AsyncFileUtil::CreateSnapshotFileCallback
& callback
,
394 const FileSystemURL
& url
,
395 base::FilePath
* snapshot_file_path
) {
396 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
397 if (!snapshot_file_path
|| snapshot_file_path
->empty()) {
398 OnCreateSnapshotFileError(callback
, base::File::FILE_ERROR_FAILED
);
401 MTPDeviceAsyncDelegate
* delegate
= GetMTPDeviceDelegate(url
);
403 OnCreateSnapshotFileError(callback
, base::File::FILE_ERROR_NOT_FOUND
);
406 delegate
->CreateSnapshotFile(
407 url
.path(), // device file path
409 base::Bind(&DeviceMediaAsyncFileUtil::OnDidCreateSnapshotFile
,
410 weak_ptr_factory_
.GetWeakPtr(),
412 make_scoped_refptr(context
->task_runner())),
413 base::Bind(&DeviceMediaAsyncFileUtil::OnCreateSnapshotFileError
,
414 weak_ptr_factory_
.GetWeakPtr(),