Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / webkit / browser / fileapi / recursive_operation_delegate.cc
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.
4
5 #include "webkit/browser/fileapi/recursive_operation_delegate.h"
6
7 #include "base/bind.h"
8 #include "webkit/browser/fileapi/file_system_context.h"
9 #include "webkit/browser/fileapi/file_system_operation_runner.h"
10
11 namespace fileapi {
12
13 namespace {
14 // Don't start too many inflight operations.
15 const int kMaxInflightOperations = 5;
16 }
17
18 RecursiveOperationDelegate::RecursiveOperationDelegate(
19     FileSystemContext* file_system_context)
20     : file_system_context_(file_system_context),
21       inflight_operations_(0),
22       canceled_(false) {
23 }
24
25 RecursiveOperationDelegate::~RecursiveOperationDelegate() {
26 }
27
28 void RecursiveOperationDelegate::Cancel() {
29   canceled_ = true;
30   OnCancel();
31 }
32
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_);
39
40   callback_ = callback;
41   ++inflight_operations_;
42   ProcessFile(
43       root,
44       base::Bind(&RecursiveOperationDelegate::DidTryProcessFile,
45                  AsWeakPtr(), root));
46 }
47
48 FileSystemOperationRunner* RecursiveOperationDelegate::operation_runner() {
49   return file_system_context_->operation_runner();
50 }
51
52 void RecursiveOperationDelegate::OnCancel() {
53 }
54
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_);
61
62   --inflight_operations_;
63   if (canceled_ || error != base::File::FILE_ERROR_NOT_A_FILE) {
64     Done(error);
65     return;
66   }
67
68   pending_directory_stack_.push(std::queue<FileSystemURL>());
69   pending_directory_stack_.top().push(root);
70   ProcessNextDirectory();
71 }
72
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_);
78
79   const FileSystemURL& url = pending_directory_stack_.top().front();
80
81   ++inflight_operations_;
82   ProcessDirectory(
83       url,
84       base::Bind(
85           &RecursiveOperationDelegate::DidProcessDirectory, AsWeakPtr()));
86 }
87
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_);
94
95   --inflight_operations_;
96   if (canceled_ || error != base::File::FILE_OK) {
97     Done(error);
98     return;
99   }
100
101   const FileSystemURL& parent = pending_directory_stack_.top().front();
102   pending_directory_stack_.push(std::queue<FileSystemURL>());
103   operation_runner()->ReadDirectory(
104       parent,
105       base::Bind(&RecursiveOperationDelegate::DidReadDirectory,
106                  AsWeakPtr(), parent));
107 }
108
109 void RecursiveOperationDelegate::DidReadDirectory(
110     const FileSystemURL& parent,
111     base::File::Error error,
112     const FileEntryList& entries,
113     bool has_more) {
114   DCHECK(!pending_directory_stack_.empty());
115   DCHECK_EQ(0, inflight_operations_);
116
117   if (canceled_ || error != base::File::FILE_OK) {
118     Done(error);
119     return;
120   }
121
122   for (size_t i = 0; i < entries.size(); i++) {
123     FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
124         parent.origin(),
125         parent.mount_type(),
126         parent.virtual_path().Append(entries[i].name));
127     if (entries[i].is_directory)
128       pending_directory_stack_.top().push(url);
129     else
130       pending_files_.push(url);
131   }
132
133   // Wait for next entries.
134   if (has_more)
135     return;
136
137   ProcessPendingFiles();
138 }
139
140 void RecursiveOperationDelegate::ProcessPendingFiles() {
141   DCHECK(!pending_directory_stack_.empty());
142
143   if ((pending_files_.empty() || canceled_) && inflight_operations_ == 0) {
144     ProcessSubDirectory();
145     return;
146   }
147
148   // Do not post any new tasks.
149   if (canceled_)
150     return;
151
152   // Run ProcessFile in parallel (upto kMaxInflightOperations).
153   scoped_refptr<base::MessageLoopProxy> current_message_loop =
154       base::MessageLoopProxy::current();
155   while (!pending_files_.empty() &&
156          inflight_operations_ < kMaxInflightOperations) {
157     ++inflight_operations_;
158     current_message_loop->PostTask(
159         FROM_HERE,
160         base::Bind(&RecursiveOperationDelegate::ProcessFile,
161                    AsWeakPtr(), pending_files_.front(),
162                    base::Bind(&RecursiveOperationDelegate::DidProcessFile,
163                               AsWeakPtr())));
164     pending_files_.pop();
165   }
166 }
167
168 void RecursiveOperationDelegate::DidProcessFile(
169     base::File::Error error) {
170   --inflight_operations_;
171   if (error != base::File::FILE_OK) {
172     // If an error occurs, invoke Done immediately (even if there remain
173     // running operations). It is because in the callback, this instance is
174     // deleted.
175     Done(error);
176     return;
177   }
178
179   ProcessPendingFiles();
180 }
181
182 void RecursiveOperationDelegate::ProcessSubDirectory() {
183   DCHECK(pending_files_.empty());
184   DCHECK(!pending_directory_stack_.empty());
185   DCHECK_EQ(0, inflight_operations_);
186
187   if (canceled_) {
188     Done(base::File::FILE_ERROR_ABORT);
189     return;
190   }
191
192   if (!pending_directory_stack_.top().empty()) {
193     // There remain some sub directories. Process them first.
194     ProcessNextDirectory();
195     return;
196   }
197
198   // All subdirectories are processed.
199   pending_directory_stack_.pop();
200   if (pending_directory_stack_.empty()) {
201     // All files/directories are processed.
202     Done(base::File::FILE_OK);
203     return;
204   }
205
206   DCHECK(!pending_directory_stack_.top().empty());
207   ++inflight_operations_;
208   PostProcessDirectory(
209       pending_directory_stack_.top().front(),
210       base::Bind(&RecursiveOperationDelegate::DidPostProcessDirectory,
211                  AsWeakPtr()));
212 }
213
214 void RecursiveOperationDelegate::DidPostProcessDirectory(
215     base::File::Error error) {
216   DCHECK(pending_files_.empty());
217   DCHECK(!pending_directory_stack_.empty());
218   DCHECK(!pending_directory_stack_.top().empty());
219   DCHECK_EQ(1, inflight_operations_);
220
221   --inflight_operations_;
222   pending_directory_stack_.top().pop();
223   if (canceled_ || error != base::File::FILE_OK) {
224     Done(error);
225     return;
226   }
227
228   ProcessSubDirectory();
229 }
230
231 void RecursiveOperationDelegate::Done(base::File::Error error) {
232   if (canceled_ && error == base::File::FILE_OK) {
233     callback_.Run(base::File::FILE_ERROR_ABORT);
234   } else {
235     callback_.Run(error);
236   }
237 }
238
239 }  // namespace fileapi