| // Copyright (c) 2013 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 "webkit/browser/fileapi/recursive_operation_delegate.h" |
| |
| #include "base/bind.h" |
| #include "webkit/browser/fileapi/file_system_context.h" |
| #include "webkit/browser/fileapi/file_system_operation_runner.h" |
| |
| namespace fileapi { |
| |
| namespace { |
| // Don't start too many inflight operations. |
| const int kMaxInflightOperations = 5; |
| } |
| |
| RecursiveOperationDelegate::RecursiveOperationDelegate( |
| FileSystemContext* file_system_context) |
| : file_system_context_(file_system_context), |
| inflight_operations_(0), |
| canceled_(false) { |
| } |
| |
| RecursiveOperationDelegate::~RecursiveOperationDelegate() { |
| } |
| |
| void RecursiveOperationDelegate::Cancel() { |
| canceled_ = true; |
| OnCancel(); |
| } |
| |
| void RecursiveOperationDelegate::StartRecursiveOperation( |
| const FileSystemURL& root, |
| const StatusCallback& callback) { |
| DCHECK(pending_directory_stack_.empty()); |
| DCHECK(pending_files_.empty()); |
| DCHECK_EQ(0, inflight_operations_); |
| |
| callback_ = callback; |
| ++inflight_operations_; |
| ProcessFile( |
| root, |
| base::Bind(&RecursiveOperationDelegate::DidTryProcessFile, |
| AsWeakPtr(), root)); |
| } |
| |
| FileSystemOperationRunner* RecursiveOperationDelegate::operation_runner() { |
| return file_system_context_->operation_runner(); |
| } |
| |
| void RecursiveOperationDelegate::OnCancel() { |
| } |
| |
| void RecursiveOperationDelegate::DidTryProcessFile( |
| const FileSystemURL& root, |
| base::PlatformFileError error) { |
| DCHECK(pending_directory_stack_.empty()); |
| DCHECK(pending_files_.empty()); |
| DCHECK_EQ(1, inflight_operations_); |
| |
| --inflight_operations_; |
| if (canceled_ || error != base::PLATFORM_FILE_ERROR_NOT_A_FILE) { |
| Done(error); |
| return; |
| } |
| |
| pending_directory_stack_.push(std::queue<FileSystemURL>()); |
| pending_directory_stack_.top().push(root); |
| ProcessNextDirectory(); |
| } |
| |
| void RecursiveOperationDelegate::ProcessNextDirectory() { |
| DCHECK(pending_files_.empty()); |
| DCHECK(!pending_directory_stack_.empty()); |
| DCHECK(!pending_directory_stack_.top().empty()); |
| DCHECK_EQ(0, inflight_operations_); |
| |
| const FileSystemURL& url = pending_directory_stack_.top().front(); |
| |
| ++inflight_operations_; |
| ProcessDirectory( |
| url, |
| base::Bind( |
| &RecursiveOperationDelegate::DidProcessDirectory, AsWeakPtr())); |
| } |
| |
| void RecursiveOperationDelegate::DidProcessDirectory( |
| base::PlatformFileError error) { |
| DCHECK(pending_files_.empty()); |
| DCHECK(!pending_directory_stack_.empty()); |
| DCHECK(!pending_directory_stack_.top().empty()); |
| DCHECK_EQ(1, inflight_operations_); |
| |
| --inflight_operations_; |
| if (canceled_ || error != base::PLATFORM_FILE_OK) { |
| Done(error); |
| return; |
| } |
| |
| const FileSystemURL& parent = pending_directory_stack_.top().front(); |
| pending_directory_stack_.push(std::queue<FileSystemURL>()); |
| operation_runner()->ReadDirectory( |
| parent, |
| base::Bind(&RecursiveOperationDelegate::DidReadDirectory, |
| AsWeakPtr(), parent)); |
| } |
| |
| void RecursiveOperationDelegate::DidReadDirectory( |
| const FileSystemURL& parent, |
| base::PlatformFileError error, |
| const FileEntryList& entries, |
| bool has_more) { |
| DCHECK(pending_files_.empty()); |
| DCHECK(!pending_directory_stack_.empty()); |
| DCHECK_EQ(0, inflight_operations_); |
| |
| if (canceled_ || error != base::PLATFORM_FILE_OK) { |
| Done(error); |
| return; |
| } |
| |
| for (size_t i = 0; i < entries.size(); i++) { |
| FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL( |
| parent.origin(), |
| parent.mount_type(), |
| parent.virtual_path().Append(entries[i].name)); |
| if (entries[i].is_directory) |
| pending_directory_stack_.top().push(url); |
| else |
| pending_files_.push(url); |
| } |
| |
| // Wait for next entries. |
| if (has_more) |
| return; |
| |
| ProcessPendingFiles(); |
| } |
| |
| void RecursiveOperationDelegate::ProcessPendingFiles() { |
| DCHECK(!pending_directory_stack_.empty()); |
| |
| if ((pending_files_.empty() || canceled_) && inflight_operations_ == 0) { |
| ProcessSubDirectory(); |
| return; |
| } |
| |
| // Do not post any new tasks. |
| if (canceled_) |
| return; |
| |
| // Run ProcessFile in parallel (upto kMaxInflightOperations). |
| scoped_refptr<base::MessageLoopProxy> current_message_loop = |
| base::MessageLoopProxy::current(); |
| while (!pending_files_.empty() && |
| inflight_operations_ < kMaxInflightOperations) { |
| ++inflight_operations_; |
| current_message_loop->PostTask( |
| FROM_HERE, |
| base::Bind(&RecursiveOperationDelegate::ProcessFile, |
| AsWeakPtr(), pending_files_.front(), |
| base::Bind(&RecursiveOperationDelegate::DidProcessFile, |
| AsWeakPtr()))); |
| pending_files_.pop(); |
| } |
| } |
| |
| void RecursiveOperationDelegate::DidProcessFile( |
| base::PlatformFileError error) { |
| --inflight_operations_; |
| if (error != base::PLATFORM_FILE_OK) { |
| // If an error occurs, invoke Done immediately (even if there remain |
| // running operations). It is because in the callback, this instance is |
| // deleted. |
| Done(error); |
| return; |
| } |
| |
| ProcessPendingFiles(); |
| } |
| |
| void RecursiveOperationDelegate::ProcessSubDirectory() { |
| DCHECK(pending_files_.empty()); |
| DCHECK(!pending_directory_stack_.empty()); |
| DCHECK_EQ(0, inflight_operations_); |
| |
| if (canceled_) { |
| Done(base::PLATFORM_FILE_ERROR_ABORT); |
| return; |
| } |
| |
| if (!pending_directory_stack_.top().empty()) { |
| // There remain some sub directories. Process them first. |
| ProcessNextDirectory(); |
| return; |
| } |
| |
| // All subdirectories are processed. |
| pending_directory_stack_.pop(); |
| if (pending_directory_stack_.empty()) { |
| // All files/directories are processed. |
| Done(base::PLATFORM_FILE_OK); |
| return; |
| } |
| |
| DCHECK(!pending_directory_stack_.top().empty()); |
| ++inflight_operations_; |
| PostProcessDirectory( |
| pending_directory_stack_.top().front(), |
| base::Bind(&RecursiveOperationDelegate::DidPostProcessDirectory, |
| AsWeakPtr())); |
| } |
| |
| void RecursiveOperationDelegate::DidPostProcessDirectory( |
| base::PlatformFileError error) { |
| DCHECK(pending_files_.empty()); |
| DCHECK(!pending_directory_stack_.empty()); |
| DCHECK(!pending_directory_stack_.top().empty()); |
| DCHECK_EQ(1, inflight_operations_); |
| |
| --inflight_operations_; |
| pending_directory_stack_.top().pop(); |
| if (canceled_ || error != base::PLATFORM_FILE_OK) { |
| Done(error); |
| return; |
| } |
| |
| ProcessSubDirectory(); |
| } |
| |
| void RecursiveOperationDelegate::Done(base::PlatformFileError error) { |
| if (canceled_ && error == base::PLATFORM_FILE_OK) { |
| callback_.Run(base::PLATFORM_FILE_ERROR_ABORT); |
| } else { |
| callback_.Run(error); |
| } |
| } |
| |
| } // namespace fileapi |