Add entry to histograms.xml for OverlyLargeOriginLength.
[chromium-blink-merge.git] / webkit / browser / fileapi / copy_or_move_operation_delegate.cc
blobd83dc992f09ce1eb4decfb62012ec55b6f5453ff
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 "net/base/io_buffer.h"
10 #include "net/base/net_errors.h"
11 #include "webkit/browser/blob/file_stream_reader.h"
12 #include "webkit/browser/fileapi/copy_or_move_file_validator.h"
13 #include "webkit/browser/fileapi/file_stream_writer.h"
14 #include "webkit/browser/fileapi/file_system_context.h"
15 #include "webkit/browser/fileapi/file_system_operation_runner.h"
16 #include "webkit/browser/fileapi/file_system_url.h"
17 #include "webkit/browser/fileapi/recursive_operation_delegate.h"
18 #include "webkit/common/blob/shareable_file_reference.h"
19 #include "webkit/common/fileapi/file_system_util.h"
21 namespace fileapi {
23 class CopyOrMoveOperationDelegate::CopyOrMoveImpl {
24 public:
25 virtual ~CopyOrMoveImpl() {}
26 virtual void Run(
27 const CopyOrMoveOperationDelegate::StatusCallback& callback) = 0;
28 virtual void Cancel() = 0;
30 protected:
31 CopyOrMoveImpl() {}
33 private:
34 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl);
37 namespace {
39 // Copies a file on a (same) file system. Just delegate the operation to
40 // |operation_runner|.
41 class CopyOrMoveOnSameFileSystemImpl
42 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
43 public:
44 CopyOrMoveOnSameFileSystemImpl(
45 FileSystemOperationRunner* operation_runner,
46 CopyOrMoveOperationDelegate::OperationType operation_type,
47 const FileSystemURL& src_url,
48 const FileSystemURL& dest_url,
49 CopyOrMoveOperationDelegate::CopyOrMoveOption option,
50 const FileSystemOperation::CopyFileProgressCallback&
51 file_progress_callback)
52 : operation_runner_(operation_runner),
53 operation_type_(operation_type),
54 src_url_(src_url),
55 dest_url_(dest_url),
56 option_(option),
57 file_progress_callback_(file_progress_callback) {
60 virtual void Run(
61 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
62 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_MOVE) {
63 operation_runner_->MoveFileLocal(src_url_, dest_url_, option_, callback);
64 } else {
65 operation_runner_->CopyFileLocal(
66 src_url_, dest_url_, option_, file_progress_callback_, callback);
70 virtual void Cancel() OVERRIDE {
71 // We can do nothing for the copy/move operation on a local file system.
72 // Assuming the operation is quickly done, it should be ok to just wait
73 // for the completion.
76 private:
77 FileSystemOperationRunner* operation_runner_;
78 CopyOrMoveOperationDelegate::OperationType operation_type_;
79 FileSystemURL src_url_;
80 FileSystemURL dest_url_;
81 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
82 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
83 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOnSameFileSystemImpl);
86 // Specifically for cross file system copy/move operation, this class creates
87 // a snapshot file, validates it if necessary, runs copying process,
88 // validates the created file, and removes source file for move (noop for
89 // copy).
90 class SnapshotCopyOrMoveImpl
91 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
92 public:
93 SnapshotCopyOrMoveImpl(
94 FileSystemOperationRunner* operation_runner,
95 CopyOrMoveOperationDelegate::OperationType operation_type,
96 const FileSystemURL& src_url,
97 const FileSystemURL& dest_url,
98 CopyOrMoveOperationDelegate::CopyOrMoveOption option,
99 CopyOrMoveFileValidatorFactory* validator_factory,
100 const FileSystemOperation::CopyFileProgressCallback&
101 file_progress_callback)
102 : operation_runner_(operation_runner),
103 operation_type_(operation_type),
104 src_url_(src_url),
105 dest_url_(dest_url),
106 option_(option),
107 validator_factory_(validator_factory),
108 file_progress_callback_(file_progress_callback),
109 cancel_requested_(false),
110 weak_factory_(this) {
113 virtual void Run(
114 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
115 file_progress_callback_.Run(0);
116 operation_runner_->CreateSnapshotFile(
117 src_url_,
118 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCreateSnapshot,
119 weak_factory_.GetWeakPtr(), callback));
122 virtual void Cancel() OVERRIDE {
123 cancel_requested_ = true;
126 private:
127 void RunAfterCreateSnapshot(
128 const CopyOrMoveOperationDelegate::StatusCallback& callback,
129 base::PlatformFileError error,
130 const base::PlatformFileInfo& file_info,
131 const base::FilePath& platform_path,
132 const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) {
133 if (cancel_requested_)
134 error = base::PLATFORM_FILE_ERROR_ABORT;
136 if (error != base::PLATFORM_FILE_OK) {
137 callback.Run(error);
138 return;
141 // For now we assume CreateSnapshotFile always return a valid local file
142 // path.
143 DCHECK(!platform_path.empty());
145 if (!validator_factory_) {
146 // No validation is needed.
147 RunAfterPreWriteValidation(platform_path, file_info, file_ref, callback,
148 base::PLATFORM_FILE_OK);
149 return;
152 // Run pre write validation.
153 PreWriteValidation(
154 platform_path,
155 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPreWriteValidation,
156 weak_factory_.GetWeakPtr(),
157 platform_path, file_info, file_ref, callback));
160 void RunAfterPreWriteValidation(
161 const base::FilePath& platform_path,
162 const base::PlatformFileInfo& file_info,
163 const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref,
164 const CopyOrMoveOperationDelegate::StatusCallback& callback,
165 base::PlatformFileError error) {
166 if (cancel_requested_)
167 error = base::PLATFORM_FILE_ERROR_ABORT;
169 if (error != base::PLATFORM_FILE_OK) {
170 callback.Run(error);
171 return;
174 // |file_ref| is unused but necessary to keep the file alive until
175 // CopyInForeignFile() is completed.
176 operation_runner_->CopyInForeignFile(
177 platform_path, dest_url_,
178 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCopyInForeignFile,
179 weak_factory_.GetWeakPtr(), file_info, file_ref, callback));
182 void RunAfterCopyInForeignFile(
183 const base::PlatformFileInfo& file_info,
184 const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref,
185 const CopyOrMoveOperationDelegate::StatusCallback& callback,
186 base::PlatformFileError error) {
187 if (cancel_requested_)
188 error = base::PLATFORM_FILE_ERROR_ABORT;
190 if (error != base::PLATFORM_FILE_OK) {
191 callback.Run(error);
192 return;
195 file_progress_callback_.Run(file_info.size);
197 if (option_ == FileSystemOperation::OPTION_NONE) {
198 RunAfterTouchFile(callback, base::PLATFORM_FILE_OK);
199 return;
202 operation_runner_->TouchFile(
203 dest_url_, base::Time::Now() /* last_access */,
204 file_info.last_modified,
205 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterTouchFile,
206 weak_factory_.GetWeakPtr(), callback));
209 void RunAfterTouchFile(
210 const CopyOrMoveOperationDelegate::StatusCallback& callback,
211 base::PlatformFileError error) {
212 // Even if TouchFile is failed, just ignore it.
214 if (cancel_requested_) {
215 callback.Run(base::PLATFORM_FILE_ERROR_ABORT);
216 return;
219 // |validator_| is NULL when the destination filesystem does not do
220 // validation.
221 if (!validator_) {
222 // No validation is needed.
223 RunAfterPostWriteValidation(callback, base::PLATFORM_FILE_OK);
224 return;
227 PostWriteValidation(
228 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPostWriteValidation,
229 weak_factory_.GetWeakPtr(), callback));
232 void RunAfterPostWriteValidation(
233 const CopyOrMoveOperationDelegate::StatusCallback& callback,
234 base::PlatformFileError error) {
235 if (cancel_requested_) {
236 callback.Run(base::PLATFORM_FILE_ERROR_ABORT);
237 return;
240 if (error != base::PLATFORM_FILE_OK) {
241 // Failed to validate. Remove the destination file.
242 operation_runner_->Remove(
243 dest_url_, true /* recursive */,
244 base::Bind(&SnapshotCopyOrMoveImpl::DidRemoveDestForError,
245 weak_factory_.GetWeakPtr(), error, callback));
246 return;
249 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
250 callback.Run(base::PLATFORM_FILE_OK);
251 return;
254 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
256 // Remove the source for finalizing move operation.
257 operation_runner_->Remove(
258 src_url_, true /* recursive */,
259 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterRemoveSourceForMove,
260 weak_factory_.GetWeakPtr(), callback));
263 void RunAfterRemoveSourceForMove(
264 const CopyOrMoveOperationDelegate::StatusCallback& callback,
265 base::PlatformFileError error) {
266 if (cancel_requested_)
267 error = base::PLATFORM_FILE_ERROR_ABORT;
269 if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
270 error = base::PLATFORM_FILE_OK;
271 callback.Run(error);
274 void DidRemoveDestForError(
275 base::PlatformFileError prior_error,
276 const CopyOrMoveOperationDelegate::StatusCallback& callback,
277 base::PlatformFileError error) {
278 if (error != base::PLATFORM_FILE_OK) {
279 VLOG(1) << "Error removing destination file after validation error: "
280 << error;
282 callback.Run(prior_error);
285 // Runs pre-write validation.
286 void PreWriteValidation(
287 const base::FilePath& platform_path,
288 const CopyOrMoveOperationDelegate::StatusCallback& callback) {
289 DCHECK(validator_factory_);
290 validator_.reset(
291 validator_factory_->CreateCopyOrMoveFileValidator(
292 src_url_, platform_path));
293 validator_->StartPreWriteValidation(callback);
296 // Runs post-write validation.
297 void PostWriteValidation(
298 const CopyOrMoveOperationDelegate::StatusCallback& callback) {
299 operation_runner_->CreateSnapshotFile(
300 dest_url_,
301 base::Bind(
302 &SnapshotCopyOrMoveImpl::PostWriteValidationAfterCreateSnapshotFile,
303 weak_factory_.GetWeakPtr(), callback));
306 void PostWriteValidationAfterCreateSnapshotFile(
307 const CopyOrMoveOperationDelegate::StatusCallback& callback,
308 base::PlatformFileError error,
309 const base::PlatformFileInfo& file_info,
310 const base::FilePath& platform_path,
311 const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) {
312 if (cancel_requested_)
313 error = base::PLATFORM_FILE_ERROR_ABORT;
315 if (error != base::PLATFORM_FILE_OK) {
316 callback.Run(error);
317 return;
320 DCHECK(validator_);
321 // Note: file_ref passed here to keep the file alive until after
322 // the StartPostWriteValidation operation finishes.
323 validator_->StartPostWriteValidation(
324 platform_path,
325 base::Bind(&SnapshotCopyOrMoveImpl::DidPostWriteValidation,
326 weak_factory_.GetWeakPtr(), file_ref, callback));
329 // |file_ref| is unused; it is passed here to make sure the reference is
330 // alive until after post-write validation is complete.
331 void DidPostWriteValidation(
332 const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref,
333 const CopyOrMoveOperationDelegate::StatusCallback& callback,
334 base::PlatformFileError error) {
335 callback.Run(error);
338 FileSystemOperationRunner* operation_runner_;
339 CopyOrMoveOperationDelegate::OperationType operation_type_;
340 FileSystemURL src_url_;
341 FileSystemURL dest_url_;
343 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
344 CopyOrMoveFileValidatorFactory* validator_factory_;
345 scoped_ptr<CopyOrMoveFileValidator> validator_;
346 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
347 bool cancel_requested_;
348 base::WeakPtrFactory<SnapshotCopyOrMoveImpl> weak_factory_;
349 DISALLOW_COPY_AND_ASSIGN(SnapshotCopyOrMoveImpl);
352 // The size of buffer for StreamCopyHelper.
353 const int kReadBufferSize = 32768;
355 // To avoid too many progress callbacks, it should be called less
356 // frequently than 50ms.
357 const int kMinProgressCallbackInvocationSpanInMilliseconds = 50;
359 // Specifically for cross file system copy/move operation, this class uses
360 // stream reader and writer for copying. Validator is not supported, so if
361 // necessary SnapshotCopyOrMoveImpl should be used.
362 class StreamCopyOrMoveImpl
363 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
364 public:
365 StreamCopyOrMoveImpl(
366 FileSystemOperationRunner* operation_runner,
367 CopyOrMoveOperationDelegate::OperationType operation_type,
368 const FileSystemURL& src_url,
369 const FileSystemURL& dest_url,
370 CopyOrMoveOperationDelegate::CopyOrMoveOption option,
371 scoped_ptr<webkit_blob::FileStreamReader> reader,
372 scoped_ptr<FileStreamWriter> writer,
373 const FileSystemOperation::CopyFileProgressCallback&
374 file_progress_callback)
375 : operation_runner_(operation_runner),
376 operation_type_(operation_type),
377 src_url_(src_url),
378 dest_url_(dest_url),
379 option_(option),
380 reader_(reader.Pass()),
381 writer_(writer.Pass()),
382 file_progress_callback_(file_progress_callback),
383 cancel_requested_(false),
384 weak_factory_(this) {
387 virtual void Run(
388 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
389 // Reader can be created even if the entry does not exist or the entry is
390 // a directory. To check errors before destination file creation,
391 // check metadata first.
392 operation_runner_->GetMetadata(
393 src_url_,
394 base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource,
395 weak_factory_.GetWeakPtr(), callback));
398 virtual void Cancel() OVERRIDE {
399 cancel_requested_ = true;
400 if (copy_helper_)
401 copy_helper_->Cancel();
404 private:
405 void RunAfterGetMetadataForSource(
406 const CopyOrMoveOperationDelegate::StatusCallback& callback,
407 base::PlatformFileError error,
408 const base::PlatformFileInfo& file_info) {
409 if (cancel_requested_)
410 error = base::PLATFORM_FILE_ERROR_ABORT;
412 if (error != base::PLATFORM_FILE_OK) {
413 callback.Run(error);
414 return;
417 if (file_info.is_directory) {
418 // If not a directory, failed with appropriate error code.
419 callback.Run(base::PLATFORM_FILE_ERROR_NOT_A_FILE);
420 return;
423 // To use FileStreamWriter, we need to ensure the destination file exists.
424 operation_runner_->CreateFile(
425 dest_url_, false /* exclusive */,
426 base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination,
427 weak_factory_.GetWeakPtr(),
428 callback, file_info.last_modified));
431 void RunAfterCreateFileForDestination(
432 const CopyOrMoveOperationDelegate::StatusCallback& callback,
433 const base::Time& last_modified,
434 base::PlatformFileError error) {
435 if (cancel_requested_)
436 error = base::PLATFORM_FILE_ERROR_ABORT;
438 if (error != base::PLATFORM_FILE_OK) {
439 callback.Run(error);
440 return;
443 DCHECK(!copy_helper_);
444 copy_helper_.reset(
445 new CopyOrMoveOperationDelegate::StreamCopyHelper(
446 reader_.Pass(), writer_.Pass(),
447 kReadBufferSize,
448 file_progress_callback_,
449 base::TimeDelta::FromMilliseconds(
450 kMinProgressCallbackInvocationSpanInMilliseconds)));
451 copy_helper_->Run(
452 base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy,
453 weak_factory_.GetWeakPtr(), callback, last_modified));
456 void RunAfterStreamCopy(
457 const CopyOrMoveOperationDelegate::StatusCallback& callback,
458 const base::Time& last_modified,
459 base::PlatformFileError error) {
460 if (cancel_requested_)
461 error = base::PLATFORM_FILE_ERROR_ABORT;
463 if (error != base::PLATFORM_FILE_OK) {
464 callback.Run(error);
465 return;
468 if (option_ == FileSystemOperation::OPTION_NONE) {
469 RunAfterTouchFile(callback, base::PLATFORM_FILE_OK);
470 return;
473 operation_runner_->TouchFile(
474 dest_url_, base::Time::Now() /* last_access */, last_modified,
475 base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile,
476 weak_factory_.GetWeakPtr(), callback));
479 void RunAfterTouchFile(
480 const CopyOrMoveOperationDelegate::StatusCallback& callback,
481 base::PlatformFileError error) {
482 // Even if TouchFile is failed, just ignore it.
483 if (cancel_requested_) {
484 callback.Run(base::PLATFORM_FILE_ERROR_ABORT);
485 return;
488 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
489 callback.Run(base::PLATFORM_FILE_OK);
490 return;
493 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
495 // Remove the source for finalizing move operation.
496 operation_runner_->Remove(
497 src_url_, false /* recursive */,
498 base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove,
499 weak_factory_.GetWeakPtr(), callback));
502 void RunAfterRemoveForMove(
503 const CopyOrMoveOperationDelegate::StatusCallback& callback,
504 base::PlatformFileError error) {
505 if (cancel_requested_)
506 error = base::PLATFORM_FILE_ERROR_ABORT;
507 if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
508 error = base::PLATFORM_FILE_OK;
509 callback.Run(error);
512 FileSystemOperationRunner* operation_runner_;
513 CopyOrMoveOperationDelegate::OperationType operation_type_;
514 FileSystemURL src_url_;
515 FileSystemURL dest_url_;
516 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
517 scoped_ptr<webkit_blob::FileStreamReader> reader_;
518 scoped_ptr<FileStreamWriter> writer_;
519 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
520 scoped_ptr<CopyOrMoveOperationDelegate::StreamCopyHelper> copy_helper_;
521 bool cancel_requested_;
522 base::WeakPtrFactory<StreamCopyOrMoveImpl> weak_factory_;
523 DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl);
526 } // namespace
528 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper(
529 scoped_ptr<webkit_blob::FileStreamReader> reader,
530 scoped_ptr<FileStreamWriter> writer,
531 int buffer_size,
532 const FileSystemOperation::CopyFileProgressCallback&
533 file_progress_callback,
534 const base::TimeDelta& min_progress_callback_invocation_span)
535 : reader_(reader.Pass()),
536 writer_(writer.Pass()),
537 file_progress_callback_(file_progress_callback),
538 io_buffer_(new net::IOBufferWithSize(buffer_size)),
539 num_copied_bytes_(0),
540 min_progress_callback_invocation_span_(
541 min_progress_callback_invocation_span),
542 cancel_requested_(false),
543 weak_factory_(this) {
546 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() {
549 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run(
550 const StatusCallback& callback) {
551 file_progress_callback_.Run(0);
552 last_progress_callback_invocation_time_ = base::Time::Now();
553 Read(callback);
556 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() {
557 cancel_requested_ = true;
560 void CopyOrMoveOperationDelegate::StreamCopyHelper::Read(
561 const StatusCallback& callback) {
562 int result = reader_->Read(
563 io_buffer_.get(), io_buffer_->size(),
564 base::Bind(&StreamCopyHelper::DidRead,
565 weak_factory_.GetWeakPtr(), callback));
566 if (result != net::ERR_IO_PENDING)
567 DidRead(callback, result);
570 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead(
571 const StatusCallback& callback, int result) {
572 if (cancel_requested_) {
573 callback.Run(base::PLATFORM_FILE_ERROR_ABORT);
574 return;
577 if (result < 0) {
578 callback.Run(NetErrorToPlatformFileError(result));
579 return;
582 if (result == 0) {
583 // Here is the EOF.
584 callback.Run(base::PLATFORM_FILE_OK);
585 return;
588 Write(callback, new net::DrainableIOBuffer(io_buffer_.get(), result));
591 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write(
592 const StatusCallback& callback,
593 scoped_refptr<net::DrainableIOBuffer> buffer) {
594 DCHECK_GT(buffer->BytesRemaining(), 0);
596 int result = writer_->Write(
597 buffer.get(), buffer->BytesRemaining(),
598 base::Bind(&StreamCopyHelper::DidWrite,
599 weak_factory_.GetWeakPtr(), callback, buffer));
600 if (result != net::ERR_IO_PENDING)
601 DidWrite(callback, buffer, result);
604 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite(
605 const StatusCallback& callback,
606 scoped_refptr<net::DrainableIOBuffer> buffer,
607 int result) {
608 if (cancel_requested_) {
609 callback.Run(base::PLATFORM_FILE_ERROR_ABORT);
610 return;
613 if (result < 0) {
614 callback.Run(NetErrorToPlatformFileError(result));
615 return;
618 buffer->DidConsume(result);
619 num_copied_bytes_ += result;
621 // Check the elapsed time since last |file_progress_callback_| invocation.
622 base::Time now = base::Time::Now();
623 if (now - last_progress_callback_invocation_time_ >=
624 min_progress_callback_invocation_span_) {
625 file_progress_callback_.Run(num_copied_bytes_);
626 last_progress_callback_invocation_time_ = now;
629 if (buffer->BytesRemaining() > 0) {
630 Write(callback, buffer);
631 return;
634 Read(callback);
637 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate(
638 FileSystemContext* file_system_context,
639 const FileSystemURL& src_root,
640 const FileSystemURL& dest_root,
641 OperationType operation_type,
642 CopyOrMoveOption option,
643 const CopyProgressCallback& progress_callback,
644 const StatusCallback& callback)
645 : RecursiveOperationDelegate(file_system_context),
646 src_root_(src_root),
647 dest_root_(dest_root),
648 operation_type_(operation_type),
649 option_(option),
650 progress_callback_(progress_callback),
651 callback_(callback),
652 weak_factory_(this) {
653 same_file_system_ = src_root_.IsInSameFileSystem(dest_root_);
656 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
657 STLDeleteElements(&running_copy_set_);
660 void CopyOrMoveOperationDelegate::Run() {
661 // Not supported; this should never be called.
662 NOTREACHED();
665 void CopyOrMoveOperationDelegate::RunRecursively() {
666 // Perform light-weight checks first.
668 // It is an error to try to copy/move an entry into its child.
669 if (same_file_system_ && src_root_.path().IsParent(dest_root_.path())) {
670 callback_.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
671 return;
674 // It is an error to copy/move an entry into the same path.
675 if (same_file_system_ && src_root_.path() == dest_root_.path()) {
676 callback_.Run(base::PLATFORM_FILE_ERROR_EXISTS);
677 return;
680 // Start to process the source directory recursively.
681 // TODO(kinuko): This could be too expensive for same_file_system_==true
682 // and operation==MOVE case, probably we can just rename the root directory.
683 // http://crbug.com/172187
684 StartRecursiveOperation(src_root_, callback_);
687 void CopyOrMoveOperationDelegate::ProcessFile(
688 const FileSystemURL& src_url,
689 const StatusCallback& callback) {
690 if (!progress_callback_.is_null()) {
691 progress_callback_.Run(
692 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
695 FileSystemURL dest_url = CreateDestURL(src_url);
696 CopyOrMoveImpl* impl = NULL;
697 if (same_file_system_) {
698 impl = new CopyOrMoveOnSameFileSystemImpl(
699 operation_runner(), operation_type_, src_url, dest_url, option_,
700 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
701 weak_factory_.GetWeakPtr(), src_url));
702 } else {
703 // Cross filesystem case.
704 base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
705 CopyOrMoveFileValidatorFactory* validator_factory =
706 file_system_context()->GetCopyOrMoveFileValidatorFactory(
707 dest_root_.type(), &error);
708 if (error != base::PLATFORM_FILE_OK) {
709 callback.Run(error);
710 return;
713 if (!validator_factory) {
714 scoped_ptr<webkit_blob::FileStreamReader> reader =
715 file_system_context()->CreateFileStreamReader(
716 src_url, 0, base::Time());
717 scoped_ptr<FileStreamWriter> writer =
718 file_system_context()->CreateFileStreamWriter(dest_url, 0);
719 if (reader && writer) {
720 impl = new StreamCopyOrMoveImpl(
721 operation_runner(), operation_type_, src_url, dest_url, option_,
722 reader.Pass(), writer.Pass(),
723 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
724 weak_factory_.GetWeakPtr(), src_url));
728 if (!impl) {
729 impl = new SnapshotCopyOrMoveImpl(
730 operation_runner(), operation_type_, src_url, dest_url, option_,
731 validator_factory,
732 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
733 weak_factory_.GetWeakPtr(), src_url));
737 // Register the running task.
738 running_copy_set_.insert(impl);
739 impl->Run(base::Bind(
740 &CopyOrMoveOperationDelegate::DidCopyOrMoveFile,
741 weak_factory_.GetWeakPtr(), src_url, dest_url, callback, impl));
744 void CopyOrMoveOperationDelegate::ProcessDirectory(
745 const FileSystemURL& src_url,
746 const StatusCallback& callback) {
747 if (src_url == src_root_) {
748 // The src_root_ looks to be a directory.
749 // Try removing the dest_root_ to see if it exists and/or it is an
750 // empty directory.
751 // We do not invoke |progress_callback_| for source root, because it is
752 // already called in ProcessFile().
753 operation_runner()->RemoveDirectory(
754 dest_root_,
755 base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot,
756 weak_factory_.GetWeakPtr(), callback));
757 return;
760 if (!progress_callback_.is_null()) {
761 progress_callback_.Run(
762 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
765 ProcessDirectoryInternal(src_url, CreateDestURL(src_url), callback);
768 void CopyOrMoveOperationDelegate::PostProcessDirectory(
769 const FileSystemURL& src_url,
770 const StatusCallback& callback) {
771 if (option_ == FileSystemOperation::OPTION_NONE) {
772 PostProcessDirectoryAfterTouchFile(
773 src_url, callback, base::PLATFORM_FILE_OK);
774 return;
777 operation_runner()->GetMetadata(
778 src_url,
779 base::Bind(
780 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata,
781 weak_factory_.GetWeakPtr(), src_url, callback));
784 void CopyOrMoveOperationDelegate::OnCancel() {
785 // Request to cancel all running Copy/Move file.
786 for (std::set<CopyOrMoveImpl*>::iterator iter = running_copy_set_.begin();
787 iter != running_copy_set_.end(); ++iter)
788 (*iter)->Cancel();
791 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
792 const FileSystemURL& src_url,
793 const FileSystemURL& dest_url,
794 const StatusCallback& callback,
795 CopyOrMoveImpl* impl,
796 base::PlatformFileError error) {
797 running_copy_set_.erase(impl);
798 delete impl;
800 if (!progress_callback_.is_null() && error == base::PLATFORM_FILE_OK) {
801 progress_callback_.Run(
802 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
805 callback.Run(error);
808 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
809 const StatusCallback& callback,
810 base::PlatformFileError error) {
811 if (error == base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY) {
812 callback_.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
813 return;
815 if (error != base::PLATFORM_FILE_OK &&
816 error != base::PLATFORM_FILE_ERROR_NOT_FOUND) {
817 callback_.Run(error);
818 return;
821 ProcessDirectoryInternal(src_root_, dest_root_, callback);
824 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal(
825 const FileSystemURL& src_url,
826 const FileSystemURL& dest_url,
827 const StatusCallback& callback) {
828 // If operation_type == Move we may need to record directories and
829 // restore directory timestamps in the end, though it may have
830 // negative performance impact.
831 // See http://crbug.com/171284 for more details.
832 operation_runner()->CreateDirectory(
833 dest_url, false /* exclusive */, false /* recursive */,
834 base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory,
835 weak_factory_.GetWeakPtr(), src_url, dest_url, callback));
838 void CopyOrMoveOperationDelegate::DidCreateDirectory(
839 const FileSystemURL& src_url,
840 const FileSystemURL& dest_url,
841 const StatusCallback& callback,
842 base::PlatformFileError error) {
843 if (!progress_callback_.is_null() && error == base::PLATFORM_FILE_OK) {
844 progress_callback_.Run(
845 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
848 callback.Run(error);
851 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata(
852 const FileSystemURL& src_url,
853 const StatusCallback& callback,
854 base::PlatformFileError error,
855 const base::PlatformFileInfo& file_info) {
856 if (error != base::PLATFORM_FILE_OK) {
857 // Ignore the error, and run post process which should run after TouchFile.
858 PostProcessDirectoryAfterTouchFile(
859 src_url, callback, base::PLATFORM_FILE_OK);
860 return;
863 operation_runner()->TouchFile(
864 CreateDestURL(src_url), base::Time::Now() /* last access */,
865 file_info.last_modified,
866 base::Bind(
867 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile,
868 weak_factory_.GetWeakPtr(), src_url, callback));
871 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile(
872 const FileSystemURL& src_url,
873 const StatusCallback& callback,
874 base::PlatformFileError error) {
875 // Even if the TouchFile is failed, just ignore it.
877 if (operation_type_ == OPERATION_COPY) {
878 callback.Run(base::PLATFORM_FILE_OK);
879 return;
882 DCHECK_EQ(OPERATION_MOVE, operation_type_);
884 // All files and subdirectories in the directory should be moved here,
885 // so remove the source directory for finalizing move operation.
886 operation_runner()->Remove(
887 src_url, false /* recursive */,
888 base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove,
889 weak_factory_.GetWeakPtr(), callback));
892 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
893 const StatusCallback& callback,
894 base::PlatformFileError error) {
895 if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
896 error = base::PLATFORM_FILE_OK;
897 callback.Run(error);
900 void CopyOrMoveOperationDelegate::OnCopyFileProgress(
901 const FileSystemURL& src_url, int64 size) {
902 if (!progress_callback_.is_null()) {
903 progress_callback_.Run(
904 FileSystemOperation::PROGRESS, src_url, FileSystemURL(), size);
908 FileSystemURL CopyOrMoveOperationDelegate::CreateDestURL(
909 const FileSystemURL& src_url) const {
910 DCHECK_EQ(src_root_.type(), src_url.type());
911 DCHECK_EQ(src_root_.origin(), src_url.origin());
913 base::FilePath relative = dest_root_.virtual_path();
914 src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(),
915 &relative);
916 return file_system_context()->CreateCrackedFileSystemURL(
917 dest_root_.origin(),
918 dest_root_.mount_type(),
919 relative);
922 } // namespace fileapi