1 // Copyright 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 "webkit/browser/fileapi/file_system_operation_runner.h"
8 #include "base/message_loop/message_loop_proxy.h"
9 #include "base/stl_util.h"
10 #include "net/url_request/url_request_context.h"
11 #include "webkit/browser/blob/blob_url_request_job_factory.h"
12 #include "webkit/browser/fileapi/file_observers.h"
13 #include "webkit/browser/fileapi/file_stream_writer.h"
14 #include "webkit/browser/fileapi/file_system_context.h"
15 #include "webkit/browser/fileapi/file_system_operation.h"
16 #include "webkit/browser/fileapi/file_writer_delegate.h"
17 #include "webkit/common/blob/shareable_file_reference.h"
21 typedef FileSystemOperationRunner::OperationID OperationID;
23 class FileSystemOperationRunner::BeginOperationScoper
24 : public base::SupportsWeakPtr<
25 FileSystemOperationRunner::BeginOperationScoper> {
27 BeginOperationScoper() {}
29 DISALLOW_COPY_AND_ASSIGN(BeginOperationScoper);
32 FileSystemOperationRunner::OperationHandle::OperationHandle() {}
33 FileSystemOperationRunner::OperationHandle::~OperationHandle() {}
35 FileSystemOperationRunner::~FileSystemOperationRunner() {
38 void FileSystemOperationRunner::Shutdown() {
42 OperationID FileSystemOperationRunner::CreateFile(
43 const FileSystemURL& url,
45 const StatusCallback& callback) {
46 base::File::Error error = base::File::FILE_OK;
47 FileSystemOperation* operation =
48 file_system_context_->CreateFileSystemOperation(url, &error);
50 BeginOperationScoper scope;
51 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
53 DidFinish(handle, callback, error);
56 PrepareForWrite(handle.id, url);
57 operation->CreateFile(
59 base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
64 OperationID FileSystemOperationRunner::CreateDirectory(
65 const FileSystemURL& url,
68 const StatusCallback& callback) {
69 base::File::Error error = base::File::FILE_OK;
70 FileSystemOperation* operation =
71 file_system_context_->CreateFileSystemOperation(url, &error);
72 BeginOperationScoper scope;
73 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
75 DidFinish(handle, callback, error);
78 PrepareForWrite(handle.id, url);
79 operation->CreateDirectory(
80 url, exclusive, recursive,
81 base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
86 OperationID FileSystemOperationRunner::Copy(
87 const FileSystemURL& src_url,
88 const FileSystemURL& dest_url,
89 CopyOrMoveOption option,
90 const CopyProgressCallback& progress_callback,
91 const StatusCallback& callback) {
92 base::File::Error error = base::File::FILE_OK;
93 FileSystemOperation* operation =
94 file_system_context_->CreateFileSystemOperation(dest_url, &error);
95 BeginOperationScoper scope;
96 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
98 DidFinish(handle, callback, error);
101 PrepareForWrite(handle.id, dest_url);
102 PrepareForRead(handle.id, src_url);
104 src_url, dest_url, option,
105 progress_callback.is_null() ?
106 CopyProgressCallback() :
107 base::Bind(&FileSystemOperationRunner::OnCopyProgress, AsWeakPtr(),
108 handle, progress_callback),
109 base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
114 OperationID FileSystemOperationRunner::Move(
115 const FileSystemURL& src_url,
116 const FileSystemURL& dest_url,
117 CopyOrMoveOption option,
118 const StatusCallback& callback) {
119 base::File::Error error = base::File::FILE_OK;
120 FileSystemOperation* operation =
121 file_system_context_->CreateFileSystemOperation(dest_url, &error);
122 BeginOperationScoper scope;
123 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
125 DidFinish(handle, callback, error);
128 PrepareForWrite(handle.id, dest_url);
129 PrepareForWrite(handle.id, src_url);
131 src_url, dest_url, option,
132 base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
137 OperationID FileSystemOperationRunner::DirectoryExists(
138 const FileSystemURL& url,
139 const StatusCallback& callback) {
140 base::File::Error error = base::File::FILE_OK;
141 FileSystemOperation* operation =
142 file_system_context_->CreateFileSystemOperation(url, &error);
143 BeginOperationScoper scope;
144 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
146 DidFinish(handle, callback, error);
149 PrepareForRead(handle.id, url);
150 operation->DirectoryExists(
152 base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
157 OperationID FileSystemOperationRunner::FileExists(
158 const FileSystemURL& url,
159 const StatusCallback& callback) {
160 base::File::Error error = base::File::FILE_OK;
161 FileSystemOperation* operation =
162 file_system_context_->CreateFileSystemOperation(url, &error);
163 BeginOperationScoper scope;
164 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
166 DidFinish(handle, callback, error);
169 PrepareForRead(handle.id, url);
170 operation->FileExists(
172 base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
177 OperationID FileSystemOperationRunner::GetMetadata(
178 const FileSystemURL& url,
179 const GetMetadataCallback& callback) {
180 base::File::Error error = base::File::FILE_OK;
181 FileSystemOperation* operation =
182 file_system_context_->CreateFileSystemOperation(url, &error);
183 BeginOperationScoper scope;
184 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
186 DidGetMetadata(handle, callback, error, base::File::Info());
189 PrepareForRead(handle.id, url);
190 operation->GetMetadata(
192 base::Bind(&FileSystemOperationRunner::DidGetMetadata, AsWeakPtr(),
197 OperationID FileSystemOperationRunner::ReadDirectory(
198 const FileSystemURL& url,
199 const ReadDirectoryCallback& callback) {
200 base::File::Error error = base::File::FILE_OK;
201 FileSystemOperation* operation =
202 file_system_context_->CreateFileSystemOperation(url, &error);
203 BeginOperationScoper scope;
204 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
206 DidReadDirectory(handle, callback, error, std::vector<DirectoryEntry>(),
210 PrepareForRead(handle.id, url);
211 operation->ReadDirectory(
213 base::Bind(&FileSystemOperationRunner::DidReadDirectory, AsWeakPtr(),
218 OperationID FileSystemOperationRunner::Remove(
219 const FileSystemURL& url, bool recursive,
220 const StatusCallback& callback) {
221 base::File::Error error = base::File::FILE_OK;
222 FileSystemOperation* operation =
223 file_system_context_->CreateFileSystemOperation(url, &error);
224 BeginOperationScoper scope;
225 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
227 DidFinish(handle, callback, error);
230 PrepareForWrite(handle.id, url);
233 base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
238 OperationID FileSystemOperationRunner::Write(
239 const net::URLRequestContext* url_request_context,
240 const FileSystemURL& url,
241 scoped_ptr<webkit_blob::BlobDataHandle> blob,
243 const WriteCallback& callback) {
244 base::File::Error error = base::File::FILE_OK;
245 FileSystemOperation* operation =
246 file_system_context_->CreateFileSystemOperation(url, &error);
248 BeginOperationScoper scope;
249 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
251 DidWrite(handle, callback, error, 0, true);
255 scoped_ptr<FileStreamWriter> writer(
256 file_system_context_->CreateFileStreamWriter(url, offset));
258 // Write is not supported.
259 DidWrite(handle, callback, base::File::FILE_ERROR_SECURITY, 0, true);
263 FileWriterDelegate::FlushPolicy flush_policy =
264 file_system_context_->ShouldFlushOnWriteCompletion(url.type())
265 ? FileWriterDelegate::FLUSH_ON_COMPLETION
266 : FileWriterDelegate::NO_FLUSH_ON_COMPLETION;
267 scoped_ptr<FileWriterDelegate> writer_delegate(
268 new FileWriterDelegate(writer.Pass(), flush_policy));
270 scoped_ptr<net::URLRequest> blob_request(
271 webkit_blob::BlobProtocolHandler::CreateBlobRequest(
274 writer_delegate.get()));
276 PrepareForWrite(handle.id, url);
278 url, writer_delegate.Pass(), blob_request.Pass(),
279 base::Bind(&FileSystemOperationRunner::DidWrite, AsWeakPtr(),
284 OperationID FileSystemOperationRunner::Truncate(
285 const FileSystemURL& url, int64 length,
286 const StatusCallback& callback) {
287 base::File::Error error = base::File::FILE_OK;
288 FileSystemOperation* operation =
289 file_system_context_->CreateFileSystemOperation(url, &error);
290 BeginOperationScoper scope;
291 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
293 DidFinish(handle, callback, error);
296 PrepareForWrite(handle.id, url);
299 base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
304 void FileSystemOperationRunner::Cancel(
306 const StatusCallback& callback) {
307 if (ContainsKey(finished_operations_, id)) {
308 DCHECK(!ContainsKey(stray_cancel_callbacks_, id));
309 stray_cancel_callbacks_[id] = callback;
312 FileSystemOperation* operation = operations_.Lookup(id);
314 // There is no operation with |id|.
315 callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
318 operation->Cancel(callback);
321 OperationID FileSystemOperationRunner::TouchFile(
322 const FileSystemURL& url,
323 const base::Time& last_access_time,
324 const base::Time& last_modified_time,
325 const StatusCallback& callback) {
326 base::File::Error error = base::File::FILE_OK;
327 FileSystemOperation* operation =
328 file_system_context_->CreateFileSystemOperation(url, &error);
329 BeginOperationScoper scope;
330 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
332 DidFinish(handle, callback, error);
335 PrepareForWrite(handle.id, url);
336 operation->TouchFile(
337 url, last_access_time, last_modified_time,
338 base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
343 OperationID FileSystemOperationRunner::OpenFile(
344 const FileSystemURL& url,
346 const OpenFileCallback& callback) {
347 base::File::Error error = base::File::FILE_OK;
348 FileSystemOperation* operation =
349 file_system_context_->CreateFileSystemOperation(url, &error);
350 BeginOperationScoper scope;
351 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
353 DidOpenFile(handle, callback, error, base::kInvalidPlatformFileValue,
358 (base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_OPEN_ALWAYS |
359 base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_OPEN_TRUNCATED |
360 base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_EXCLUSIVE_WRITE |
361 base::PLATFORM_FILE_DELETE_ON_CLOSE |
362 base::PLATFORM_FILE_WRITE_ATTRIBUTES)) {
363 PrepareForWrite(handle.id, url);
365 PrepareForRead(handle.id, url);
369 base::Bind(&FileSystemOperationRunner::DidOpenFile, AsWeakPtr(),
374 OperationID FileSystemOperationRunner::CreateSnapshotFile(
375 const FileSystemURL& url,
376 const SnapshotFileCallback& callback) {
377 base::File::Error error = base::File::FILE_OK;
378 FileSystemOperation* operation =
379 file_system_context_->CreateFileSystemOperation(url, &error);
380 BeginOperationScoper scope;
381 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
383 DidCreateSnapshot(handle, callback, error, base::File::Info(),
384 base::FilePath(), NULL);
387 PrepareForRead(handle.id, url);
388 operation->CreateSnapshotFile(
390 base::Bind(&FileSystemOperationRunner::DidCreateSnapshot, AsWeakPtr(),
395 OperationID FileSystemOperationRunner::CopyInForeignFile(
396 const base::FilePath& src_local_disk_path,
397 const FileSystemURL& dest_url,
398 const StatusCallback& callback) {
399 base::File::Error error = base::File::FILE_OK;
400 FileSystemOperation* operation =
401 file_system_context_->CreateFileSystemOperation(dest_url, &error);
402 BeginOperationScoper scope;
403 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
405 DidFinish(handle, callback, error);
408 operation->CopyInForeignFile(
409 src_local_disk_path, dest_url,
410 base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
415 OperationID FileSystemOperationRunner::RemoveFile(
416 const FileSystemURL& url,
417 const StatusCallback& callback) {
418 base::File::Error error = base::File::FILE_OK;
419 FileSystemOperation* operation =
420 file_system_context_->CreateFileSystemOperation(url, &error);
421 BeginOperationScoper scope;
422 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
424 DidFinish(handle, callback, error);
427 operation->RemoveFile(
429 base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
434 OperationID FileSystemOperationRunner::RemoveDirectory(
435 const FileSystemURL& url,
436 const StatusCallback& callback) {
437 base::File::Error error = base::File::FILE_OK;
438 FileSystemOperation* operation =
439 file_system_context_->CreateFileSystemOperation(url, &error);
440 BeginOperationScoper scope;
441 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
443 DidFinish(handle, callback, error);
446 operation->RemoveDirectory(
448 base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
453 OperationID FileSystemOperationRunner::CopyFileLocal(
454 const FileSystemURL& src_url,
455 const FileSystemURL& dest_url,
456 CopyOrMoveOption option,
457 const CopyFileProgressCallback& progress_callback,
458 const StatusCallback& callback) {
459 base::File::Error error = base::File::FILE_OK;
460 FileSystemOperation* operation =
461 file_system_context_->CreateFileSystemOperation(src_url, &error);
462 BeginOperationScoper scope;
463 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
465 DidFinish(handle, callback, error);
468 operation->CopyFileLocal(
469 src_url, dest_url, option, progress_callback,
470 base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
475 OperationID FileSystemOperationRunner::MoveFileLocal(
476 const FileSystemURL& src_url,
477 const FileSystemURL& dest_url,
478 CopyOrMoveOption option,
479 const StatusCallback& callback) {
480 base::File::Error error = base::File::FILE_OK;
481 FileSystemOperation* operation =
482 file_system_context_->CreateFileSystemOperation(src_url, &error);
483 BeginOperationScoper scope;
484 OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
486 DidFinish(handle, callback, error);
489 operation->MoveFileLocal(
490 src_url, dest_url, option,
491 base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
496 base::File::Error FileSystemOperationRunner::SyncGetPlatformPath(
497 const FileSystemURL& url,
498 base::FilePath* platform_path) {
499 base::File::Error error = base::File::FILE_OK;
500 scoped_ptr<FileSystemOperation> operation(
501 file_system_context_->CreateFileSystemOperation(url, &error));
502 if (!operation.get())
504 return operation->SyncGetPlatformPath(url, platform_path);
507 FileSystemOperationRunner::FileSystemOperationRunner(
508 FileSystemContext* file_system_context)
509 : file_system_context_(file_system_context) {}
511 void FileSystemOperationRunner::DidFinish(
512 const OperationHandle& handle,
513 const StatusCallback& callback,
514 base::File::Error rv) {
516 finished_operations_.insert(handle.id);
517 base::MessageLoopProxy::current()->PostTask(
518 FROM_HERE, base::Bind(&FileSystemOperationRunner::DidFinish,
519 AsWeakPtr(), handle, callback, rv));
523 FinishOperation(handle.id);
526 void FileSystemOperationRunner::DidGetMetadata(
527 const OperationHandle& handle,
528 const GetMetadataCallback& callback,
529 base::File::Error rv,
530 const base::File::Info& file_info) {
532 finished_operations_.insert(handle.id);
533 base::MessageLoopProxy::current()->PostTask(
534 FROM_HERE, base::Bind(&FileSystemOperationRunner::DidGetMetadata,
535 AsWeakPtr(), handle, callback, rv, file_info));
538 callback.Run(rv, file_info);
539 FinishOperation(handle.id);
542 void FileSystemOperationRunner::DidReadDirectory(
543 const OperationHandle& handle,
544 const ReadDirectoryCallback& callback,
545 base::File::Error rv,
546 const std::vector<DirectoryEntry>& entries,
549 finished_operations_.insert(handle.id);
550 base::MessageLoopProxy::current()->PostTask(
551 FROM_HERE, base::Bind(&FileSystemOperationRunner::DidReadDirectory,
552 AsWeakPtr(), handle, callback, rv,
556 callback.Run(rv, entries, has_more);
557 if (rv != base::File::FILE_OK || !has_more)
558 FinishOperation(handle.id);
561 void FileSystemOperationRunner::DidWrite(
562 const OperationHandle& handle,
563 const WriteCallback& callback,
564 base::File::Error rv,
568 finished_operations_.insert(handle.id);
569 base::MessageLoopProxy::current()->PostTask(
570 FROM_HERE, base::Bind(&FileSystemOperationRunner::DidWrite, AsWeakPtr(),
571 handle, callback, rv, bytes, complete));
574 callback.Run(rv, bytes, complete);
575 if (rv != base::File::FILE_OK || complete)
576 FinishOperation(handle.id);
579 void FileSystemOperationRunner::DidOpenFile(
580 const OperationHandle& handle,
581 const OpenFileCallback& callback,
582 base::File::Error rv,
583 base::PlatformFile file,
584 const base::Closure& on_close_callback) {
586 finished_operations_.insert(handle.id);
587 base::MessageLoopProxy::current()->PostTask(
588 FROM_HERE, base::Bind(&FileSystemOperationRunner::DidOpenFile,
589 AsWeakPtr(), handle, callback, rv, file,
593 callback.Run(rv, file, on_close_callback);
594 FinishOperation(handle.id);
597 void FileSystemOperationRunner::DidCreateSnapshot(
598 const OperationHandle& handle,
599 const SnapshotFileCallback& callback,
600 base::File::Error rv,
601 const base::File::Info& file_info,
602 const base::FilePath& platform_path,
603 const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) {
605 finished_operations_.insert(handle.id);
606 base::MessageLoopProxy::current()->PostTask(
607 FROM_HERE, base::Bind(&FileSystemOperationRunner::DidCreateSnapshot,
608 AsWeakPtr(), handle, callback, rv, file_info,
609 platform_path, file_ref));
612 callback.Run(rv, file_info, platform_path, file_ref);
613 FinishOperation(handle.id);
616 void FileSystemOperationRunner::OnCopyProgress(
617 const OperationHandle& handle,
618 const CopyProgressCallback& callback,
619 FileSystemOperation::CopyProgressType type,
620 const FileSystemURL& source_url,
621 const FileSystemURL& dest_url,
624 base::MessageLoopProxy::current()->PostTask(
625 FROM_HERE, base::Bind(
626 &FileSystemOperationRunner::OnCopyProgress,
627 AsWeakPtr(), handle, callback, type, source_url, dest_url, size));
630 callback.Run(type, source_url, dest_url, size);
633 void FileSystemOperationRunner::PrepareForWrite(OperationID id,
634 const FileSystemURL& url) {
635 if (file_system_context_->GetUpdateObservers(url.type())) {
636 file_system_context_->GetUpdateObservers(url.type())->Notify(
637 &FileUpdateObserver::OnStartUpdate, MakeTuple(url));
639 write_target_urls_[id].insert(url);
642 void FileSystemOperationRunner::PrepareForRead(OperationID id,
643 const FileSystemURL& url) {
644 if (file_system_context_->GetAccessObservers(url.type())) {
645 file_system_context_->GetAccessObservers(url.type())->Notify(
646 &FileAccessObserver::OnAccess, MakeTuple(url));
650 FileSystemOperationRunner::OperationHandle
651 FileSystemOperationRunner::BeginOperation(
652 FileSystemOperation* operation,
653 base::WeakPtr<BeginOperationScoper> scope) {
654 OperationHandle handle;
655 handle.id = operations_.Add(operation);
656 handle.scope = scope;
660 void FileSystemOperationRunner::FinishOperation(OperationID id) {
661 OperationToURLSet::iterator found = write_target_urls_.find(id);
662 if (found != write_target_urls_.end()) {
663 const FileSystemURLSet& urls = found->second;
664 for (FileSystemURLSet::const_iterator iter = urls.begin();
665 iter != urls.end(); ++iter) {
666 if (file_system_context_->GetUpdateObservers(iter->type())) {
667 file_system_context_->GetUpdateObservers(iter->type())->Notify(
668 &FileUpdateObserver::OnEndUpdate, MakeTuple(*iter));
671 write_target_urls_.erase(found);
674 // IDMap::Lookup fails if the operation is NULL, so we don't check
675 // operations_.Lookup(id) here.
677 operations_.Remove(id);
678 finished_operations_.erase(id);
680 // Dispatch stray cancel callback if exists.
681 std::map<OperationID, StatusCallback>::iterator found_cancel =
682 stray_cancel_callbacks_.find(id);
683 if (found_cancel != stray_cancel_callbacks_.end()) {
684 // This cancel has been requested after the operation has finished,
685 // so report that we failed to stop it.
686 found_cancel->second.Run(base::File::FILE_ERROR_INVALID_OPERATION);
687 stray_cancel_callbacks_.erase(found_cancel);
691 } // namespace fileapi