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 "webkit/browser/fileapi/recursive_operation_delegate.h"
8 #include "webkit/browser/fileapi/file_system_context.h"
9 #include "webkit/browser/fileapi/file_system_operation_runner.h"
14 // Don't start too many inflight operations.
15 const int kMaxInflightOperations = 5;
18 RecursiveOperationDelegate::RecursiveOperationDelegate(
19 FileSystemContext* file_system_context)
20 : file_system_context_(file_system_context),
21 inflight_operations_(0),
25 RecursiveOperationDelegate::~RecursiveOperationDelegate() {
28 void RecursiveOperationDelegate::Cancel() {
33 void RecursiveOperationDelegate::StartRecursiveOperation(
34 const FileSystemURL& root,
35 const StatusCallback& callback) {
36 DCHECK(pending_directory_stack_.empty());
37 DCHECK(pending_files_.empty());
38 DCHECK_EQ(0, inflight_operations_);
41 ++inflight_operations_;
44 base::Bind(&RecursiveOperationDelegate::DidTryProcessFile,
48 FileSystemOperationRunner* RecursiveOperationDelegate::operation_runner() {
49 return file_system_context_->operation_runner();
52 void RecursiveOperationDelegate::OnCancel() {
55 void RecursiveOperationDelegate::DidTryProcessFile(
56 const FileSystemURL& root,
57 base::File::Error error) {
58 DCHECK(pending_directory_stack_.empty());
59 DCHECK(pending_files_.empty());
60 DCHECK_EQ(1, inflight_operations_);
62 --inflight_operations_;
63 if (canceled_ || error != base::File::FILE_ERROR_NOT_A_FILE) {
68 pending_directory_stack_.push(std::queue<FileSystemURL>());
69 pending_directory_stack_.top().push(root);
70 ProcessNextDirectory();
73 void RecursiveOperationDelegate::ProcessNextDirectory() {
74 DCHECK(pending_files_.empty());
75 DCHECK(!pending_directory_stack_.empty());
76 DCHECK(!pending_directory_stack_.top().empty());
77 DCHECK_EQ(0, inflight_operations_);
79 const FileSystemURL& url = pending_directory_stack_.top().front();
81 ++inflight_operations_;
85 &RecursiveOperationDelegate::DidProcessDirectory, AsWeakPtr()));
88 void RecursiveOperationDelegate::DidProcessDirectory(
89 base::File::Error error) {
90 DCHECK(pending_files_.empty());
91 DCHECK(!pending_directory_stack_.empty());
92 DCHECK(!pending_directory_stack_.top().empty());
93 DCHECK_EQ(1, inflight_operations_);
95 --inflight_operations_;
96 if (canceled_ || error != base::File::FILE_OK) {
101 const FileSystemURL& parent = pending_directory_stack_.top().front();
102 pending_directory_stack_.push(std::queue<FileSystemURL>());
103 operation_runner()->ReadDirectory(
105 base::Bind(&RecursiveOperationDelegate::DidReadDirectory,
106 AsWeakPtr(), parent));
109 void RecursiveOperationDelegate::DidReadDirectory(
110 const FileSystemURL& parent,
111 base::File::Error error,
112 const FileEntryList& entries,
114 DCHECK(pending_files_.empty());
115 DCHECK(!pending_directory_stack_.empty());
116 DCHECK_EQ(0, inflight_operations_);
118 if (canceled_ || error != base::File::FILE_OK) {
123 for (size_t i = 0; i < entries.size(); i++) {
124 FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
127 parent.virtual_path().Append(entries[i].name));
128 if (entries[i].is_directory)
129 pending_directory_stack_.top().push(url);
131 pending_files_.push(url);
134 // Wait for next entries.
138 ProcessPendingFiles();
141 void RecursiveOperationDelegate::ProcessPendingFiles() {
142 DCHECK(!pending_directory_stack_.empty());
144 if ((pending_files_.empty() || canceled_) && inflight_operations_ == 0) {
145 ProcessSubDirectory();
149 // Do not post any new tasks.
153 // Run ProcessFile in parallel (upto kMaxInflightOperations).
154 scoped_refptr<base::MessageLoopProxy> current_message_loop =
155 base::MessageLoopProxy::current();
156 while (!pending_files_.empty() &&
157 inflight_operations_ < kMaxInflightOperations) {
158 ++inflight_operations_;
159 current_message_loop->PostTask(
161 base::Bind(&RecursiveOperationDelegate::ProcessFile,
162 AsWeakPtr(), pending_files_.front(),
163 base::Bind(&RecursiveOperationDelegate::DidProcessFile,
165 pending_files_.pop();
169 void RecursiveOperationDelegate::DidProcessFile(
170 base::File::Error error) {
171 --inflight_operations_;
172 if (error != base::File::FILE_OK) {
173 // If an error occurs, invoke Done immediately (even if there remain
174 // running operations). It is because in the callback, this instance is
180 ProcessPendingFiles();
183 void RecursiveOperationDelegate::ProcessSubDirectory() {
184 DCHECK(pending_files_.empty());
185 DCHECK(!pending_directory_stack_.empty());
186 DCHECK_EQ(0, inflight_operations_);
189 Done(base::File::FILE_ERROR_ABORT);
193 if (!pending_directory_stack_.top().empty()) {
194 // There remain some sub directories. Process them first.
195 ProcessNextDirectory();
199 // All subdirectories are processed.
200 pending_directory_stack_.pop();
201 if (pending_directory_stack_.empty()) {
202 // All files/directories are processed.
203 Done(base::File::FILE_OK);
207 DCHECK(!pending_directory_stack_.top().empty());
208 ++inflight_operations_;
209 PostProcessDirectory(
210 pending_directory_stack_.top().front(),
211 base::Bind(&RecursiveOperationDelegate::DidPostProcessDirectory,
215 void RecursiveOperationDelegate::DidPostProcessDirectory(
216 base::File::Error error) {
217 DCHECK(pending_files_.empty());
218 DCHECK(!pending_directory_stack_.empty());
219 DCHECK(!pending_directory_stack_.top().empty());
220 DCHECK_EQ(1, inflight_operations_);
222 --inflight_operations_;
223 pending_directory_stack_.top().pop();
224 if (canceled_ || error != base::File::FILE_OK) {
229 ProcessSubDirectory();
232 void RecursiveOperationDelegate::Done(base::File::Error error) {
233 if (canceled_ && error == base::File::FILE_OK) {
234 callback_.Run(base::File::FILE_ERROR_ABORT);
236 callback_.Run(error);
240 } // namespace fileapi