• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RecursiveOperationDelegate(FileSystemContext * file_system_context)18 RecursiveOperationDelegate::RecursiveOperationDelegate(
19     FileSystemContext* file_system_context)
20     : file_system_context_(file_system_context),
21       inflight_operations_(0),
22       canceled_(false) {
23 }
24 
~RecursiveOperationDelegate()25 RecursiveOperationDelegate::~RecursiveOperationDelegate() {
26 }
27 
Cancel()28 void RecursiveOperationDelegate::Cancel() {
29   canceled_ = true;
30   OnCancel();
31 }
32 
StartRecursiveOperation(const FileSystemURL & root,const StatusCallback & callback)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 
operation_runner()48 FileSystemOperationRunner* RecursiveOperationDelegate::operation_runner() {
49   return file_system_context_->operation_runner();
50 }
51 
OnCancel()52 void RecursiveOperationDelegate::OnCancel() {
53 }
54 
DidTryProcessFile(const FileSystemURL & root,base::PlatformFileError error)55 void RecursiveOperationDelegate::DidTryProcessFile(
56     const FileSystemURL& root,
57     base::PlatformFileError 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::PLATFORM_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 
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_);
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 
DidProcessDirectory(base::PlatformFileError error)88 void RecursiveOperationDelegate::DidProcessDirectory(
89     base::PlatformFileError 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::PLATFORM_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 
DidReadDirectory(const FileSystemURL & parent,base::PlatformFileError error,const FileEntryList & entries,bool has_more)109 void RecursiveOperationDelegate::DidReadDirectory(
110     const FileSystemURL& parent,
111     base::PlatformFileError error,
112     const FileEntryList& entries,
113     bool has_more) {
114   DCHECK(pending_files_.empty());
115   DCHECK(!pending_directory_stack_.empty());
116   DCHECK_EQ(0, inflight_operations_);
117 
118   if (canceled_ || error != base::PLATFORM_FILE_OK) {
119     Done(error);
120     return;
121   }
122 
123   for (size_t i = 0; i < entries.size(); i++) {
124     FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
125         parent.origin(),
126         parent.mount_type(),
127         parent.virtual_path().Append(entries[i].name));
128     if (entries[i].is_directory)
129       pending_directory_stack_.top().push(url);
130     else
131       pending_files_.push(url);
132   }
133 
134   // Wait for next entries.
135   if (has_more)
136     return;
137 
138   ProcessPendingFiles();
139 }
140 
ProcessPendingFiles()141 void RecursiveOperationDelegate::ProcessPendingFiles() {
142   DCHECK(!pending_directory_stack_.empty());
143 
144   if ((pending_files_.empty() || canceled_) && inflight_operations_ == 0) {
145     ProcessSubDirectory();
146     return;
147   }
148 
149   // Do not post any new tasks.
150   if (canceled_)
151     return;
152 
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(
160         FROM_HERE,
161         base::Bind(&RecursiveOperationDelegate::ProcessFile,
162                    AsWeakPtr(), pending_files_.front(),
163                    base::Bind(&RecursiveOperationDelegate::DidProcessFile,
164                               AsWeakPtr())));
165     pending_files_.pop();
166   }
167 }
168 
DidProcessFile(base::PlatformFileError error)169 void RecursiveOperationDelegate::DidProcessFile(
170     base::PlatformFileError error) {
171   --inflight_operations_;
172   if (error != base::PLATFORM_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
175     // deleted.
176     Done(error);
177     return;
178   }
179 
180   ProcessPendingFiles();
181 }
182 
ProcessSubDirectory()183 void RecursiveOperationDelegate::ProcessSubDirectory() {
184   DCHECK(pending_files_.empty());
185   DCHECK(!pending_directory_stack_.empty());
186   DCHECK_EQ(0, inflight_operations_);
187 
188   if (canceled_) {
189     Done(base::PLATFORM_FILE_ERROR_ABORT);
190     return;
191   }
192 
193   if (!pending_directory_stack_.top().empty()) {
194     // There remain some sub directories. Process them first.
195     ProcessNextDirectory();
196     return;
197   }
198 
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::PLATFORM_FILE_OK);
204     return;
205   }
206 
207   DCHECK(!pending_directory_stack_.top().empty());
208   ++inflight_operations_;
209   PostProcessDirectory(
210       pending_directory_stack_.top().front(),
211       base::Bind(&RecursiveOperationDelegate::DidPostProcessDirectory,
212                  AsWeakPtr()));
213 }
214 
DidPostProcessDirectory(base::PlatformFileError error)215 void RecursiveOperationDelegate::DidPostProcessDirectory(
216     base::PlatformFileError 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_);
221 
222   --inflight_operations_;
223   pending_directory_stack_.top().pop();
224   if (canceled_ || error != base::PLATFORM_FILE_OK) {
225     Done(error);
226     return;
227   }
228 
229   ProcessSubDirectory();
230 }
231 
Done(base::PlatformFileError error)232 void RecursiveOperationDelegate::Done(base::PlatformFileError error) {
233   if (canceled_ && error == base::PLATFORM_FILE_OK) {
234     callback_.Run(base::PLATFORM_FILE_ERROR_ABORT);
235   } else {
236     callback_.Run(error);
237   }
238 }
239 
240 }  // namespace fileapi
241