| 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 |