OLD | NEW |
| (Empty) |
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 storage { | |
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 storage | |
OLD | NEW |