Refactor RecursiveOperationDelegate to support PostProcessDirectory.
[chromium-blink-merge.git] / webkit / browser / fileapi / copy_or_move_operation_delegate.cc
blob90787732a633aace432151e3d638dfa0dcfbee23
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/copy_or_move_operation_delegate.h"
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "webkit/browser/fileapi/copy_or_move_file_validator.h"
10 #include "webkit/browser/fileapi/file_system_context.h"
11 #include "webkit/browser/fileapi/file_system_operation_runner.h"
12 #include "webkit/browser/fileapi/file_system_url.h"
13 #include "webkit/browser/fileapi/recursive_operation_delegate.h"
14 #include "webkit/common/blob/shareable_file_reference.h"
15 #include "webkit/common/fileapi/file_system_util.h"
17 namespace fileapi {
19 class CopyOrMoveOperationDelegate::CopyOrMoveImpl {
20 public:
21 virtual ~CopyOrMoveImpl() {}
22 virtual void Run(
23 const CopyOrMoveOperationDelegate::StatusCallback& callback) = 0;
24 protected:
25 CopyOrMoveImpl() {}
26 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl);
29 namespace {
31 // Copies a file on a (same) file system. Just delegate the operation to
32 // |operation_runner|.
33 class CopyOrMoveOnSameFileSystemImpl
34 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
35 public:
36 CopyOrMoveOnSameFileSystemImpl(
37 FileSystemOperationRunner* operation_runner,
38 CopyOrMoveOperationDelegate::OperationType operation_type,
39 const FileSystemURL& src_url,
40 const FileSystemURL& dest_url,
41 const FileSystemOperation::CopyFileProgressCallback&
42 file_progress_callback)
43 : operation_runner_(operation_runner),
44 operation_type_(operation_type),
45 src_url_(src_url),
46 dest_url_(dest_url),
47 file_progress_callback_(file_progress_callback) {
50 virtual void Run(
51 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
52 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_MOVE) {
53 operation_runner_->MoveFileLocal(src_url_, dest_url_, callback);
54 } else {
55 operation_runner_->CopyFileLocal(
56 src_url_, dest_url_, file_progress_callback_, callback);
60 private:
61 FileSystemOperationRunner* operation_runner_;
62 CopyOrMoveOperationDelegate::OperationType operation_type_;
63 FileSystemURL src_url_;
64 FileSystemURL dest_url_;
65 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
66 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOnSameFileSystemImpl);
69 // Specifically for cross file system copy/move operation, this class creates
70 // a snapshot file, validates it if necessary, runs copying process,
71 // validates the created file, and removes source file for move (noop for
72 // copy).
73 class SnapshotCopyOrMoveImpl
74 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
75 public:
76 SnapshotCopyOrMoveImpl(
77 FileSystemOperationRunner* operation_runner,
78 CopyOrMoveOperationDelegate::OperationType operation_type,
79 const FileSystemURL& src_url,
80 const FileSystemURL& dest_url,
81 CopyOrMoveFileValidatorFactory* validator_factory,
82 const FileSystemOperation::CopyFileProgressCallback&
83 file_progress_callback)
84 : operation_runner_(operation_runner),
85 operation_type_(operation_type),
86 src_url_(src_url),
87 dest_url_(dest_url),
88 validator_factory_(validator_factory),
89 file_progress_callback_(file_progress_callback),
90 weak_factory_(this) {
93 virtual void Run(
94 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
95 file_progress_callback_.Run(0);
96 operation_runner_->CreateSnapshotFile(
97 src_url_,
98 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCreateSnapshot,
99 weak_factory_.GetWeakPtr(), callback));
102 private:
103 void RunAfterCreateSnapshot(
104 const CopyOrMoveOperationDelegate::StatusCallback& callback,
105 base::PlatformFileError error,
106 const base::PlatformFileInfo& file_info,
107 const base::FilePath& platform_path,
108 const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) {
109 if (error != base::PLATFORM_FILE_OK) {
110 callback.Run(error);
111 return;
114 // For now we assume CreateSnapshotFile always return a valid local file
115 // path.
116 DCHECK(!platform_path.empty());
118 if (!validator_factory_) {
119 // No validation is needed.
120 RunAfterPreWriteValidation(platform_path, file_info, file_ref, callback,
121 base::PLATFORM_FILE_OK);
122 return;
125 // Run pre write validation.
126 PreWriteValidation(
127 platform_path,
128 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPreWriteValidation,
129 weak_factory_.GetWeakPtr(),
130 platform_path, file_info, file_ref, callback));
133 void RunAfterPreWriteValidation(
134 const base::FilePath& platform_path,
135 const base::PlatformFileInfo& file_info,
136 const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref,
137 const CopyOrMoveOperationDelegate::StatusCallback& callback,
138 base::PlatformFileError error) {
139 if (error != base::PLATFORM_FILE_OK) {
140 callback.Run(error);
141 return;
144 // |file_ref| is unused but necessary to keep the file alive until
145 // CopyInForeignFile() is completed.
146 operation_runner_->CopyInForeignFile(
147 platform_path, dest_url_,
148 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCopyInForeignFile,
149 weak_factory_.GetWeakPtr(), file_info, file_ref, callback));
152 void RunAfterCopyInForeignFile(
153 const base::PlatformFileInfo& file_info,
154 const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref,
155 const CopyOrMoveOperationDelegate::StatusCallback& callback,
156 base::PlatformFileError error) {
157 if (error != base::PLATFORM_FILE_OK) {
158 callback.Run(error);
159 return;
162 file_progress_callback_.Run(file_info.size);
164 // |validator_| is NULL when the destination filesystem does not do
165 // validation.
166 if (!validator_) {
167 // No validation is needed.
168 RunAfterPostWriteValidation(callback, base::PLATFORM_FILE_OK);
169 return;
172 PostWriteValidation(
173 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPostWriteValidation,
174 weak_factory_.GetWeakPtr(), callback));
177 void RunAfterPostWriteValidation(
178 const CopyOrMoveOperationDelegate::StatusCallback& callback,
179 base::PlatformFileError error) {
180 if (error != base::PLATFORM_FILE_OK) {
181 // Failed to validate. Remove the destination file.
182 operation_runner_->Remove(
183 dest_url_, true /* recursive */,
184 base::Bind(&SnapshotCopyOrMoveImpl::DidRemoveDestForError,
185 weak_factory_.GetWeakPtr(), error, callback));
186 return;
189 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
190 callback.Run(base::PLATFORM_FILE_OK);
191 return;
194 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
196 // Remove the source for finalizing move operation.
197 operation_runner_->Remove(
198 src_url_, true /* recursive */,
199 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterRemoveSourceForMove,
200 weak_factory_.GetWeakPtr(), callback));
203 void RunAfterRemoveSourceForMove(
204 const CopyOrMoveOperationDelegate::StatusCallback& callback,
205 base::PlatformFileError error) {
206 if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
207 error = base::PLATFORM_FILE_OK;
208 callback.Run(error);
211 void DidRemoveDestForError(
212 base::PlatformFileError prior_error,
213 const CopyOrMoveOperationDelegate::StatusCallback& callback,
214 base::PlatformFileError error) {
215 if (error != base::PLATFORM_FILE_OK) {
216 VLOG(1) << "Error removing destination file after validation error: "
217 << error;
219 callback.Run(prior_error);
222 // Runs pre-write validation.
223 void PreWriteValidation(
224 const base::FilePath& platform_path,
225 const CopyOrMoveOperationDelegate::StatusCallback& callback) {
226 DCHECK(validator_factory_);
227 validator_.reset(
228 validator_factory_->CreateCopyOrMoveFileValidator(
229 src_url_, platform_path));
230 validator_->StartPreWriteValidation(callback);
233 // Runs post-write validation.
234 void PostWriteValidation(
235 const CopyOrMoveOperationDelegate::StatusCallback& callback) {
236 operation_runner_->CreateSnapshotFile(
237 dest_url_,
238 base::Bind(
239 &SnapshotCopyOrMoveImpl::PostWriteValidationAfterCreateSnapshotFile,
240 weak_factory_.GetWeakPtr(), callback));
243 void PostWriteValidationAfterCreateSnapshotFile(
244 const CopyOrMoveOperationDelegate::StatusCallback& callback,
245 base::PlatformFileError error,
246 const base::PlatformFileInfo& file_info,
247 const base::FilePath& platform_path,
248 const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) {
249 if (error != base::PLATFORM_FILE_OK) {
250 callback.Run(error);
251 return;
254 DCHECK(validator_);
255 // Note: file_ref passed here to keep the file alive until after
256 // the StartPostWriteValidation operation finishes.
257 validator_->StartPostWriteValidation(
258 platform_path,
259 base::Bind(&SnapshotCopyOrMoveImpl::DidPostWriteValidation,
260 weak_factory_.GetWeakPtr(), file_ref, callback));
263 // |file_ref| is unused; it is passed here to make sure the reference is
264 // alive until after post-write validation is complete.
265 void DidPostWriteValidation(
266 const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref,
267 const CopyOrMoveOperationDelegate::StatusCallback& callback,
268 base::PlatformFileError error) {
269 callback.Run(error);
272 FileSystemOperationRunner* operation_runner_;
273 CopyOrMoveOperationDelegate::OperationType operation_type_;
274 FileSystemURL src_url_;
275 FileSystemURL dest_url_;
276 CopyOrMoveFileValidatorFactory* validator_factory_;
277 scoped_ptr<CopyOrMoveFileValidator> validator_;
278 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
280 base::WeakPtrFactory<SnapshotCopyOrMoveImpl> weak_factory_;
281 DISALLOW_COPY_AND_ASSIGN(SnapshotCopyOrMoveImpl);
284 } // namespace
287 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate(
288 FileSystemContext* file_system_context,
289 const FileSystemURL& src_root,
290 const FileSystemURL& dest_root,
291 OperationType operation_type,
292 const CopyProgressCallback& progress_callback,
293 const StatusCallback& callback)
294 : RecursiveOperationDelegate(file_system_context),
295 src_root_(src_root),
296 dest_root_(dest_root),
297 operation_type_(operation_type),
298 progress_callback_(progress_callback),
299 callback_(callback),
300 weak_factory_(this) {
301 same_file_system_ = src_root_.IsInSameFileSystem(dest_root_);
304 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
305 STLDeleteElements(&running_copy_set_);
308 void CopyOrMoveOperationDelegate::Run() {
309 // Not supported; this should never be called.
310 NOTREACHED();
313 void CopyOrMoveOperationDelegate::RunRecursively() {
314 // Perform light-weight checks first.
316 // It is an error to try to copy/move an entry into its child.
317 if (same_file_system_ && src_root_.path().IsParent(dest_root_.path())) {
318 callback_.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
319 return;
322 // It is an error to copy/move an entry into the same path.
323 if (same_file_system_ && src_root_.path() == dest_root_.path()) {
324 callback_.Run(base::PLATFORM_FILE_ERROR_EXISTS);
325 return;
328 if (!progress_callback_.is_null()) {
329 progress_callback_.Run(
330 FileSystemOperation::BEGIN_COPY_ENTRY, src_root_, 0);
333 // First try to copy/move it as a file.
334 CopyOrMoveFile(src_root_, dest_root_,
335 base::Bind(&CopyOrMoveOperationDelegate::DidTryCopyOrMoveFile,
336 weak_factory_.GetWeakPtr()));
339 void CopyOrMoveOperationDelegate::ProcessFile(
340 const FileSystemURL& src_url,
341 const StatusCallback& callback) {
342 if (!progress_callback_.is_null())
343 progress_callback_.Run(FileSystemOperation::BEGIN_COPY_ENTRY, src_url, 0);
345 CopyOrMoveFile(src_url, CreateDestURL(src_url),
346 base::Bind(&CopyOrMoveOperationDelegate::DidCopyEntry,
347 weak_factory_.GetWeakPtr(), src_url, callback));
350 void CopyOrMoveOperationDelegate::ProcessDirectory(
351 const FileSystemURL& src_url,
352 const StatusCallback& callback) {
353 FileSystemURL dest_url = CreateDestURL(src_url);
355 if (!progress_callback_.is_null() && src_url != src_root_) {
356 // We do not invoke |progress_callback_| for source root, because it is
357 // already called in RunRecursively().
358 progress_callback_.Run(FileSystemOperation::BEGIN_COPY_ENTRY, src_url, 0);
361 // If operation_type == Move we may need to record directories and
362 // restore directory timestamps in the end, though it may have
363 // negative performance impact.
364 // See http://crbug.com/171284 for more details.
365 operation_runner()->CreateDirectory(
366 dest_url, false /* exclusive */, false /* recursive */,
367 base::Bind(&CopyOrMoveOperationDelegate::DidCopyEntry,
368 weak_factory_.GetWeakPtr(), src_url, callback));
371 void CopyOrMoveOperationDelegate::PostProcessDirectory(
372 const FileSystemURL& src_url,
373 const StatusCallback& callback) {
374 callback.Run(base::PLATFORM_FILE_OK);
377 void CopyOrMoveOperationDelegate::DidTryCopyOrMoveFile(
378 base::PlatformFileError error) {
379 if (error != base::PLATFORM_FILE_ERROR_NOT_A_FILE) {
380 if (error == base::PLATFORM_FILE_OK && !progress_callback_.is_null()) {
381 progress_callback_.Run(
382 FileSystemOperation::END_COPY_ENTRY, src_root_, 0);
385 callback_.Run(error);
386 return;
389 // The src_root_ looks to be a directory.
390 // Try removing the dest_root_ to see if it exists and/or it is an
391 // empty directory.
392 operation_runner()->RemoveDirectory(
393 dest_root_,
394 base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot,
395 weak_factory_.GetWeakPtr()));
398 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
399 base::PlatformFileError error) {
400 if (error == base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY) {
401 callback_.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
402 return;
404 if (error != base::PLATFORM_FILE_OK &&
405 error != base::PLATFORM_FILE_ERROR_NOT_FOUND) {
406 callback_.Run(error);
407 return;
410 // Start to process the source directory recursively.
411 // TODO(kinuko): This could be too expensive for same_file_system_==true
412 // and operation==MOVE case, probably we can just rename the root directory.
413 // http://crbug.com/172187
414 StartRecursiveOperation(
415 src_root_,
416 base::Bind(&CopyOrMoveOperationDelegate::DidFinishRecursiveCopyDir,
417 weak_factory_.GetWeakPtr(), src_root_, callback_));
420 void CopyOrMoveOperationDelegate::DidFinishRecursiveCopyDir(
421 const FileSystemURL& src,
422 const StatusCallback& callback,
423 base::PlatformFileError error) {
424 if (error != base::PLATFORM_FILE_OK ||
425 operation_type_ == OPERATION_COPY) {
426 callback.Run(error);
427 return;
430 DCHECK_EQ(OPERATION_MOVE, operation_type_);
432 // Remove the source for finalizing move operation.
433 operation_runner()->Remove(
434 src, true /* recursive */,
435 base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove,
436 weak_factory_.GetWeakPtr(), callback));
439 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
440 const StatusCallback& callback,
441 base::PlatformFileError error) {
442 if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
443 error = base::PLATFORM_FILE_OK;
444 callback.Run(error);
447 void CopyOrMoveOperationDelegate::CopyOrMoveFile(
448 const FileSystemURL& src_url,
449 const FileSystemURL& dest_url,
450 const StatusCallback& callback) {
451 CopyOrMoveImpl* impl = NULL;
452 if (same_file_system_) {
453 impl = new CopyOrMoveOnSameFileSystemImpl(
454 operation_runner(), operation_type_, src_url, dest_url,
455 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
456 weak_factory_.GetWeakPtr(), src_url));
457 } else {
458 // Cross filesystem case.
459 // TODO(hidehiko): Support stream based copy. crbug.com/279287.
460 base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
461 CopyOrMoveFileValidatorFactory* validator_factory =
462 file_system_context()->GetCopyOrMoveFileValidatorFactory(
463 dest_root_.type(), &error);
464 if (error != base::PLATFORM_FILE_OK) {
465 callback.Run(error);
466 return;
469 impl = new SnapshotCopyOrMoveImpl(
470 operation_runner(), operation_type_, src_url, dest_url,
471 validator_factory,
472 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
473 weak_factory_.GetWeakPtr(), src_url));
476 // Register the running task.
477 running_copy_set_.insert(impl);
478 impl->Run(base::Bind(&CopyOrMoveOperationDelegate::DidCopyOrMoveFile,
479 weak_factory_.GetWeakPtr(), impl, callback));
482 void CopyOrMoveOperationDelegate::DidCopyEntry(
483 const FileSystemURL& src_url,
484 const StatusCallback& callback,
485 base::PlatformFileError error) {
486 if (!progress_callback_.is_null() && error == base::PLATFORM_FILE_OK)
487 progress_callback_.Run(FileSystemOperation::END_COPY_ENTRY, src_url, 0);
489 callback.Run(error);
492 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
493 CopyOrMoveImpl* impl,
494 const StatusCallback& callback,
495 base::PlatformFileError error) {
496 running_copy_set_.erase(impl);
497 delete impl;
498 callback.Run(error);
501 void CopyOrMoveOperationDelegate::OnCopyFileProgress(
502 const FileSystemURL& src_url, int64 size) {
503 if (!progress_callback_.is_null())
504 progress_callback_.Run(FileSystemOperation::PROGRESS, src_url, size);
507 FileSystemURL CopyOrMoveOperationDelegate::CreateDestURL(
508 const FileSystemURL& src_url) const {
509 DCHECK_EQ(src_root_.type(), src_url.type());
510 DCHECK_EQ(src_root_.origin(), src_url.origin());
512 base::FilePath relative = dest_root_.virtual_path();
513 src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(),
514 &relative);
515 return file_system_context()->CreateCrackedFileSystemURL(
516 dest_root_.origin(),
517 dest_root_.mount_type(),
518 relative);
521 } // namespace fileapi