PPAPI: Add C++ wrapper for MessageHandler stuff.
[chromium-blink-merge.git] / storage / browser / fileapi / copy_or_move_operation_delegate.cc
blobaa6b059387a27a7b5030b9c806ddeb19473e670d
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 "storage/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 "storage/browser/blob/file_stream_reader.h"
12 #include "storage/browser/fileapi/copy_or_move_file_validator.h"
13 #include "storage/browser/fileapi/file_observers.h"
14 #include "storage/browser/fileapi/file_stream_writer.h"
15 #include "storage/browser/fileapi/file_system_context.h"
16 #include "storage/browser/fileapi/file_system_operation_runner.h"
17 #include "storage/browser/fileapi/file_system_url.h"
18 #include "storage/browser/fileapi/recursive_operation_delegate.h"
19 #include "storage/common/blob/shareable_file_reference.h"
20 #include "storage/common/fileapi/file_system_util.h"
22 namespace storage {
24 const int64 kFlushIntervalInBytes = 10 << 20; // 10MB.
26 class CopyOrMoveOperationDelegate::CopyOrMoveImpl {
27 public:
28 virtual ~CopyOrMoveImpl() {}
29 virtual void Run(
30 const CopyOrMoveOperationDelegate::StatusCallback& callback) = 0;
31 virtual void Cancel() = 0;
33 protected:
34 CopyOrMoveImpl() {}
36 private:
37 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl);
40 namespace {
42 // Copies a file on a (same) file system. Just delegate the operation to
43 // |operation_runner|.
44 class CopyOrMoveOnSameFileSystemImpl
45 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
46 public:
47 CopyOrMoveOnSameFileSystemImpl(
48 FileSystemOperationRunner* operation_runner,
49 CopyOrMoveOperationDelegate::OperationType operation_type,
50 const FileSystemURL& src_url,
51 const FileSystemURL& dest_url,
52 CopyOrMoveOperationDelegate::CopyOrMoveOption option,
53 const FileSystemOperation::CopyFileProgressCallback&
54 file_progress_callback)
55 : operation_runner_(operation_runner),
56 operation_type_(operation_type),
57 src_url_(src_url),
58 dest_url_(dest_url),
59 option_(option),
60 file_progress_callback_(file_progress_callback) {
63 virtual void Run(
64 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
65 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_MOVE) {
66 operation_runner_->MoveFileLocal(src_url_, dest_url_, option_, callback);
67 } else {
68 operation_runner_->CopyFileLocal(
69 src_url_, dest_url_, option_, file_progress_callback_, callback);
73 virtual void Cancel() OVERRIDE {
74 // We can do nothing for the copy/move operation on a local file system.
75 // Assuming the operation is quickly done, it should be ok to just wait
76 // for the completion.
79 private:
80 FileSystemOperationRunner* operation_runner_;
81 CopyOrMoveOperationDelegate::OperationType operation_type_;
82 FileSystemURL src_url_;
83 FileSystemURL dest_url_;
84 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
85 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
86 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOnSameFileSystemImpl);
89 // Specifically for cross file system copy/move operation, this class creates
90 // a snapshot file, validates it if necessary, runs copying process,
91 // validates the created file, and removes source file for move (noop for
92 // copy).
93 class SnapshotCopyOrMoveImpl
94 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
95 public:
96 SnapshotCopyOrMoveImpl(
97 FileSystemOperationRunner* operation_runner,
98 CopyOrMoveOperationDelegate::OperationType operation_type,
99 const FileSystemURL& src_url,
100 const FileSystemURL& dest_url,
101 CopyOrMoveOperationDelegate::CopyOrMoveOption option,
102 CopyOrMoveFileValidatorFactory* validator_factory,
103 const FileSystemOperation::CopyFileProgressCallback&
104 file_progress_callback)
105 : operation_runner_(operation_runner),
106 operation_type_(operation_type),
107 src_url_(src_url),
108 dest_url_(dest_url),
109 option_(option),
110 validator_factory_(validator_factory),
111 file_progress_callback_(file_progress_callback),
112 cancel_requested_(false),
113 weak_factory_(this) {
116 virtual void Run(
117 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
118 file_progress_callback_.Run(0);
119 operation_runner_->CreateSnapshotFile(
120 src_url_,
121 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCreateSnapshot,
122 weak_factory_.GetWeakPtr(), callback));
125 virtual void Cancel() OVERRIDE {
126 cancel_requested_ = true;
129 private:
130 void RunAfterCreateSnapshot(
131 const CopyOrMoveOperationDelegate::StatusCallback& callback,
132 base::File::Error error,
133 const base::File::Info& file_info,
134 const base::FilePath& platform_path,
135 const scoped_refptr<storage::ShareableFileReference>& file_ref) {
136 if (cancel_requested_)
137 error = base::File::FILE_ERROR_ABORT;
139 if (error != base::File::FILE_OK) {
140 callback.Run(error);
141 return;
144 // For now we assume CreateSnapshotFile always return a valid local file
145 // path.
146 DCHECK(!platform_path.empty());
148 if (!validator_factory_) {
149 // No validation is needed.
150 RunAfterPreWriteValidation(platform_path, file_info, file_ref, callback,
151 base::File::FILE_OK);
152 return;
155 // Run pre write validation.
156 PreWriteValidation(
157 platform_path,
158 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPreWriteValidation,
159 weak_factory_.GetWeakPtr(),
160 platform_path, file_info, file_ref, callback));
163 void RunAfterPreWriteValidation(
164 const base::FilePath& platform_path,
165 const base::File::Info& file_info,
166 const scoped_refptr<storage::ShareableFileReference>& file_ref,
167 const CopyOrMoveOperationDelegate::StatusCallback& callback,
168 base::File::Error error) {
169 if (cancel_requested_)
170 error = base::File::FILE_ERROR_ABORT;
172 if (error != base::File::FILE_OK) {
173 callback.Run(error);
174 return;
177 // |file_ref| is unused but necessary to keep the file alive until
178 // CopyInForeignFile() is completed.
179 operation_runner_->CopyInForeignFile(
180 platform_path, dest_url_,
181 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCopyInForeignFile,
182 weak_factory_.GetWeakPtr(), file_info, file_ref, callback));
185 void RunAfterCopyInForeignFile(
186 const base::File::Info& file_info,
187 const scoped_refptr<storage::ShareableFileReference>& file_ref,
188 const CopyOrMoveOperationDelegate::StatusCallback& callback,
189 base::File::Error error) {
190 if (cancel_requested_)
191 error = base::File::FILE_ERROR_ABORT;
193 if (error != base::File::FILE_OK) {
194 callback.Run(error);
195 return;
198 file_progress_callback_.Run(file_info.size);
200 if (option_ == FileSystemOperation::OPTION_NONE) {
201 RunAfterTouchFile(callback, base::File::FILE_OK);
202 return;
205 operation_runner_->TouchFile(
206 dest_url_, base::Time::Now() /* last_access */,
207 file_info.last_modified,
208 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterTouchFile,
209 weak_factory_.GetWeakPtr(), callback));
212 void RunAfterTouchFile(
213 const CopyOrMoveOperationDelegate::StatusCallback& callback,
214 base::File::Error error) {
215 // Even if TouchFile is failed, just ignore it.
217 if (cancel_requested_) {
218 callback.Run(base::File::FILE_ERROR_ABORT);
219 return;
222 // |validator_| is NULL when the destination filesystem does not do
223 // validation.
224 if (!validator_) {
225 // No validation is needed.
226 RunAfterPostWriteValidation(callback, base::File::FILE_OK);
227 return;
230 PostWriteValidation(
231 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPostWriteValidation,
232 weak_factory_.GetWeakPtr(), callback));
235 void RunAfterPostWriteValidation(
236 const CopyOrMoveOperationDelegate::StatusCallback& callback,
237 base::File::Error error) {
238 if (cancel_requested_) {
239 callback.Run(base::File::FILE_ERROR_ABORT);
240 return;
243 if (error != base::File::FILE_OK) {
244 // Failed to validate. Remove the destination file.
245 operation_runner_->Remove(
246 dest_url_, true /* recursive */,
247 base::Bind(&SnapshotCopyOrMoveImpl::DidRemoveDestForError,
248 weak_factory_.GetWeakPtr(), error, callback));
249 return;
252 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
253 callback.Run(base::File::FILE_OK);
254 return;
257 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
259 // Remove the source for finalizing move operation.
260 operation_runner_->Remove(
261 src_url_, true /* recursive */,
262 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterRemoveSourceForMove,
263 weak_factory_.GetWeakPtr(), callback));
266 void RunAfterRemoveSourceForMove(
267 const CopyOrMoveOperationDelegate::StatusCallback& callback,
268 base::File::Error error) {
269 if (cancel_requested_)
270 error = base::File::FILE_ERROR_ABORT;
272 if (error == base::File::FILE_ERROR_NOT_FOUND)
273 error = base::File::FILE_OK;
274 callback.Run(error);
277 void DidRemoveDestForError(
278 base::File::Error prior_error,
279 const CopyOrMoveOperationDelegate::StatusCallback& callback,
280 base::File::Error error) {
281 if (error != base::File::FILE_OK) {
282 VLOG(1) << "Error removing destination file after validation error: "
283 << error;
285 callback.Run(prior_error);
288 // Runs pre-write validation.
289 void PreWriteValidation(
290 const base::FilePath& platform_path,
291 const CopyOrMoveOperationDelegate::StatusCallback& callback) {
292 DCHECK(validator_factory_);
293 validator_.reset(
294 validator_factory_->CreateCopyOrMoveFileValidator(
295 src_url_, platform_path));
296 validator_->StartPreWriteValidation(callback);
299 // Runs post-write validation.
300 void PostWriteValidation(
301 const CopyOrMoveOperationDelegate::StatusCallback& callback) {
302 operation_runner_->CreateSnapshotFile(
303 dest_url_,
304 base::Bind(
305 &SnapshotCopyOrMoveImpl::PostWriteValidationAfterCreateSnapshotFile,
306 weak_factory_.GetWeakPtr(), callback));
309 void PostWriteValidationAfterCreateSnapshotFile(
310 const CopyOrMoveOperationDelegate::StatusCallback& callback,
311 base::File::Error error,
312 const base::File::Info& file_info,
313 const base::FilePath& platform_path,
314 const scoped_refptr<storage::ShareableFileReference>& file_ref) {
315 if (cancel_requested_)
316 error = base::File::FILE_ERROR_ABORT;
318 if (error != base::File::FILE_OK) {
319 callback.Run(error);
320 return;
323 DCHECK(validator_);
324 // Note: file_ref passed here to keep the file alive until after
325 // the StartPostWriteValidation operation finishes.
326 validator_->StartPostWriteValidation(
327 platform_path,
328 base::Bind(&SnapshotCopyOrMoveImpl::DidPostWriteValidation,
329 weak_factory_.GetWeakPtr(), file_ref, callback));
332 // |file_ref| is unused; it is passed here to make sure the reference is
333 // alive until after post-write validation is complete.
334 void DidPostWriteValidation(
335 const scoped_refptr<storage::ShareableFileReference>& file_ref,
336 const CopyOrMoveOperationDelegate::StatusCallback& callback,
337 base::File::Error error) {
338 callback.Run(error);
341 FileSystemOperationRunner* operation_runner_;
342 CopyOrMoveOperationDelegate::OperationType operation_type_;
343 FileSystemURL src_url_;
344 FileSystemURL dest_url_;
346 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
347 CopyOrMoveFileValidatorFactory* validator_factory_;
348 scoped_ptr<CopyOrMoveFileValidator> validator_;
349 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
350 bool cancel_requested_;
351 base::WeakPtrFactory<SnapshotCopyOrMoveImpl> weak_factory_;
352 DISALLOW_COPY_AND_ASSIGN(SnapshotCopyOrMoveImpl);
355 // The size of buffer for StreamCopyHelper.
356 const int kReadBufferSize = 32768;
358 // To avoid too many progress callbacks, it should be called less
359 // frequently than 50ms.
360 const int kMinProgressCallbackInvocationSpanInMilliseconds = 50;
362 // Specifically for cross file system copy/move operation, this class uses
363 // stream reader and writer for copying. Validator is not supported, so if
364 // necessary SnapshotCopyOrMoveImpl should be used.
365 class StreamCopyOrMoveImpl
366 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
367 public:
368 StreamCopyOrMoveImpl(
369 FileSystemOperationRunner* operation_runner,
370 FileSystemContext* file_system_context,
371 CopyOrMoveOperationDelegate::OperationType operation_type,
372 const FileSystemURL& src_url,
373 const FileSystemURL& dest_url,
374 CopyOrMoveOperationDelegate::CopyOrMoveOption option,
375 scoped_ptr<storage::FileStreamReader> reader,
376 scoped_ptr<FileStreamWriter> writer,
377 const FileSystemOperation::CopyFileProgressCallback&
378 file_progress_callback)
379 : operation_runner_(operation_runner),
380 file_system_context_(file_system_context),
381 operation_type_(operation_type),
382 src_url_(src_url),
383 dest_url_(dest_url),
384 option_(option),
385 reader_(reader.Pass()),
386 writer_(writer.Pass()),
387 file_progress_callback_(file_progress_callback),
388 cancel_requested_(false),
389 weak_factory_(this) {}
391 virtual void Run(
392 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
393 // Reader can be created even if the entry does not exist or the entry is
394 // a directory. To check errors before destination file creation,
395 // check metadata first.
396 operation_runner_->GetMetadata(
397 src_url_,
398 base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource,
399 weak_factory_.GetWeakPtr(), callback));
402 virtual void Cancel() OVERRIDE {
403 cancel_requested_ = true;
404 if (copy_helper_)
405 copy_helper_->Cancel();
408 private:
409 void NotifyOnStartUpdate(const FileSystemURL& url) {
410 if (file_system_context_->GetUpdateObservers(url.type())) {
411 file_system_context_->GetUpdateObservers(url.type())
412 ->Notify(&FileUpdateObserver::OnStartUpdate, MakeTuple(url));
416 void NotifyOnModifyFile(const FileSystemURL& url) {
417 if (file_system_context_->GetChangeObservers(url.type())) {
418 file_system_context_->GetChangeObservers(url.type())
419 ->Notify(&FileChangeObserver::OnModifyFile, MakeTuple(url));
423 void NotifyOnEndUpdate(const FileSystemURL& url) {
424 if (file_system_context_->GetUpdateObservers(url.type())) {
425 file_system_context_->GetUpdateObservers(url.type())
426 ->Notify(&FileUpdateObserver::OnEndUpdate, MakeTuple(url));
430 void RunAfterGetMetadataForSource(
431 const CopyOrMoveOperationDelegate::StatusCallback& callback,
432 base::File::Error error,
433 const base::File::Info& file_info) {
434 if (cancel_requested_)
435 error = base::File::FILE_ERROR_ABORT;
437 if (error != base::File::FILE_OK) {
438 callback.Run(error);
439 return;
442 if (file_info.is_directory) {
443 // If not a directory, failed with appropriate error code.
444 callback.Run(base::File::FILE_ERROR_NOT_A_FILE);
445 return;
448 // To use FileStreamWriter, we need to ensure the destination file exists.
449 operation_runner_->CreateFile(
450 dest_url_,
451 true /* exclusive */,
452 base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination,
453 weak_factory_.GetWeakPtr(),
454 callback,
455 file_info.last_modified));
458 void RunAfterCreateFileForDestination(
459 const CopyOrMoveOperationDelegate::StatusCallback& callback,
460 const base::Time& last_modified,
461 base::File::Error error) {
462 if (cancel_requested_)
463 error = base::File::FILE_ERROR_ABORT;
464 // This conversion is to return the consistent status code with
465 // FileSystemFileUtil::Copy.
466 if (error == base::File::FILE_ERROR_NOT_A_FILE)
467 error = base::File::FILE_ERROR_INVALID_OPERATION;
469 if (error != base::File::FILE_OK &&
470 error != base::File::FILE_ERROR_EXISTS) {
471 callback.Run(error);
472 return;
475 if (error == base::File::FILE_ERROR_EXISTS) {
476 operation_runner_->Truncate(
477 dest_url_,
478 0 /* length */,
479 base::Bind(&StreamCopyOrMoveImpl::RunAfterTruncateForDestination,
480 weak_factory_.GetWeakPtr(),
481 callback,
482 last_modified));
483 return;
485 RunAfterTruncateForDestination(
486 callback, last_modified, base::File::FILE_OK);
489 void RunAfterTruncateForDestination(
490 const CopyOrMoveOperationDelegate::StatusCallback& callback,
491 const base::Time& last_modified,
492 base::File::Error error) {
493 if (cancel_requested_)
494 error = base::File::FILE_ERROR_ABORT;
496 if (error != base::File::FILE_OK) {
497 callback.Run(error);
498 return;
501 const bool need_flush = dest_url_.mount_option().copy_sync_option() ==
502 storage::COPY_SYNC_OPTION_SYNC;
504 NotifyOnStartUpdate(dest_url_);
505 DCHECK(!copy_helper_);
506 copy_helper_.reset(
507 new CopyOrMoveOperationDelegate::StreamCopyHelper(
508 reader_.Pass(), writer_.Pass(),
509 need_flush,
510 kReadBufferSize,
511 file_progress_callback_,
512 base::TimeDelta::FromMilliseconds(
513 kMinProgressCallbackInvocationSpanInMilliseconds)));
514 copy_helper_->Run(
515 base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy,
516 weak_factory_.GetWeakPtr(), callback, last_modified));
519 void RunAfterStreamCopy(
520 const CopyOrMoveOperationDelegate::StatusCallback& callback,
521 const base::Time& last_modified,
522 base::File::Error error) {
523 NotifyOnModifyFile(dest_url_);
524 NotifyOnEndUpdate(dest_url_);
525 if (cancel_requested_)
526 error = base::File::FILE_ERROR_ABORT;
528 if (error != base::File::FILE_OK) {
529 callback.Run(error);
530 return;
533 if (option_ == FileSystemOperation::OPTION_NONE) {
534 RunAfterTouchFile(callback, base::File::FILE_OK);
535 return;
538 operation_runner_->TouchFile(
539 dest_url_, base::Time::Now() /* last_access */, last_modified,
540 base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile,
541 weak_factory_.GetWeakPtr(), callback));
544 void RunAfterTouchFile(
545 const CopyOrMoveOperationDelegate::StatusCallback& callback,
546 base::File::Error error) {
547 // Even if TouchFile is failed, just ignore it.
548 if (cancel_requested_) {
549 callback.Run(base::File::FILE_ERROR_ABORT);
550 return;
553 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
554 callback.Run(base::File::FILE_OK);
555 return;
558 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
560 // Remove the source for finalizing move operation.
561 operation_runner_->Remove(
562 src_url_, false /* recursive */,
563 base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove,
564 weak_factory_.GetWeakPtr(), callback));
567 void RunAfterRemoveForMove(
568 const CopyOrMoveOperationDelegate::StatusCallback& callback,
569 base::File::Error error) {
570 if (cancel_requested_)
571 error = base::File::FILE_ERROR_ABORT;
572 if (error == base::File::FILE_ERROR_NOT_FOUND)
573 error = base::File::FILE_OK;
574 callback.Run(error);
577 FileSystemOperationRunner* operation_runner_;
578 scoped_refptr<FileSystemContext> file_system_context_;
579 CopyOrMoveOperationDelegate::OperationType operation_type_;
580 FileSystemURL src_url_;
581 FileSystemURL dest_url_;
582 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
583 scoped_ptr<storage::FileStreamReader> reader_;
584 scoped_ptr<FileStreamWriter> writer_;
585 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
586 scoped_ptr<CopyOrMoveOperationDelegate::StreamCopyHelper> copy_helper_;
587 bool cancel_requested_;
588 base::WeakPtrFactory<StreamCopyOrMoveImpl> weak_factory_;
589 DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl);
592 } // namespace
594 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper(
595 scoped_ptr<storage::FileStreamReader> reader,
596 scoped_ptr<FileStreamWriter> writer,
597 bool need_flush,
598 int buffer_size,
599 const FileSystemOperation::CopyFileProgressCallback& file_progress_callback,
600 const base::TimeDelta& min_progress_callback_invocation_span)
601 : reader_(reader.Pass()),
602 writer_(writer.Pass()),
603 need_flush_(need_flush),
604 file_progress_callback_(file_progress_callback),
605 io_buffer_(new net::IOBufferWithSize(buffer_size)),
606 num_copied_bytes_(0),
607 previous_flush_offset_(0),
608 min_progress_callback_invocation_span_(
609 min_progress_callback_invocation_span),
610 cancel_requested_(false),
611 weak_factory_(this) {
614 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() {
617 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run(
618 const StatusCallback& callback) {
619 file_progress_callback_.Run(0);
620 last_progress_callback_invocation_time_ = base::Time::Now();
621 Read(callback);
624 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() {
625 cancel_requested_ = true;
628 void CopyOrMoveOperationDelegate::StreamCopyHelper::Read(
629 const StatusCallback& callback) {
630 int result = reader_->Read(
631 io_buffer_.get(), io_buffer_->size(),
632 base::Bind(&StreamCopyHelper::DidRead,
633 weak_factory_.GetWeakPtr(), callback));
634 if (result != net::ERR_IO_PENDING)
635 DidRead(callback, result);
638 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead(
639 const StatusCallback& callback, int result) {
640 if (cancel_requested_) {
641 callback.Run(base::File::FILE_ERROR_ABORT);
642 return;
645 if (result < 0) {
646 callback.Run(NetErrorToFileError(result));
647 return;
650 if (result == 0) {
651 // Here is the EOF.
652 if (need_flush_)
653 Flush(callback, true /* is_eof */);
654 else
655 callback.Run(base::File::FILE_OK);
656 return;
659 Write(callback, new net::DrainableIOBuffer(io_buffer_.get(), result));
662 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write(
663 const StatusCallback& callback,
664 scoped_refptr<net::DrainableIOBuffer> buffer) {
665 DCHECK_GT(buffer->BytesRemaining(), 0);
667 int result = writer_->Write(
668 buffer.get(), buffer->BytesRemaining(),
669 base::Bind(&StreamCopyHelper::DidWrite,
670 weak_factory_.GetWeakPtr(), callback, buffer));
671 if (result != net::ERR_IO_PENDING)
672 DidWrite(callback, buffer, result);
675 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite(
676 const StatusCallback& callback,
677 scoped_refptr<net::DrainableIOBuffer> buffer,
678 int result) {
679 if (cancel_requested_) {
680 callback.Run(base::File::FILE_ERROR_ABORT);
681 return;
684 if (result < 0) {
685 callback.Run(NetErrorToFileError(result));
686 return;
689 buffer->DidConsume(result);
690 num_copied_bytes_ += result;
692 // Check the elapsed time since last |file_progress_callback_| invocation.
693 base::Time now = base::Time::Now();
694 if (now - last_progress_callback_invocation_time_ >=
695 min_progress_callback_invocation_span_) {
696 file_progress_callback_.Run(num_copied_bytes_);
697 last_progress_callback_invocation_time_ = now;
700 if (buffer->BytesRemaining() > 0) {
701 Write(callback, buffer);
702 return;
705 if (need_flush_ &&
706 (num_copied_bytes_ - previous_flush_offset_) > kFlushIntervalInBytes) {
707 Flush(callback, false /* not is_eof */);
708 } else {
709 Read(callback);
713 void CopyOrMoveOperationDelegate::StreamCopyHelper::Flush(
714 const StatusCallback& callback, bool is_eof) {
715 int result = writer_->Flush(
716 base::Bind(&StreamCopyHelper::DidFlush,
717 weak_factory_.GetWeakPtr(), callback, is_eof));
718 if (result != net::ERR_IO_PENDING)
719 DidFlush(callback, is_eof, result);
722 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidFlush(
723 const StatusCallback& callback, bool is_eof, int result) {
724 if (cancel_requested_) {
725 callback.Run(base::File::FILE_ERROR_ABORT);
726 return;
729 previous_flush_offset_ = num_copied_bytes_;
730 if (is_eof)
731 callback.Run(NetErrorToFileError(result));
732 else
733 Read(callback);
736 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate(
737 FileSystemContext* file_system_context,
738 const FileSystemURL& src_root,
739 const FileSystemURL& dest_root,
740 OperationType operation_type,
741 CopyOrMoveOption option,
742 const CopyProgressCallback& progress_callback,
743 const StatusCallback& callback)
744 : RecursiveOperationDelegate(file_system_context),
745 src_root_(src_root),
746 dest_root_(dest_root),
747 operation_type_(operation_type),
748 option_(option),
749 progress_callback_(progress_callback),
750 callback_(callback),
751 weak_factory_(this) {
752 same_file_system_ = src_root_.IsInSameFileSystem(dest_root_);
755 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
756 STLDeleteElements(&running_copy_set_);
759 void CopyOrMoveOperationDelegate::Run() {
760 // Not supported; this should never be called.
761 NOTREACHED();
764 void CopyOrMoveOperationDelegate::RunRecursively() {
765 // Perform light-weight checks first.
767 // It is an error to try to copy/move an entry into its child.
768 if (same_file_system_ && src_root_.path().IsParent(dest_root_.path())) {
769 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
770 return;
773 if (same_file_system_ && src_root_.path() == dest_root_.path()) {
774 // In JS API this should return error, but we return success because Pepper
775 // wants to return success and we have a code path that returns error in
776 // Blink for JS (http://crbug.com/329517).
777 callback_.Run(base::File::FILE_OK);
778 return;
781 // Start to process the source directory recursively.
782 // TODO(kinuko): This could be too expensive for same_file_system_==true
783 // and operation==MOVE case, probably we can just rename the root directory.
784 // http://crbug.com/172187
785 StartRecursiveOperation(src_root_, callback_);
788 void CopyOrMoveOperationDelegate::ProcessFile(
789 const FileSystemURL& src_url,
790 const StatusCallback& callback) {
791 if (!progress_callback_.is_null()) {
792 progress_callback_.Run(
793 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
796 FileSystemURL dest_url = CreateDestURL(src_url);
797 CopyOrMoveImpl* impl = NULL;
798 if (same_file_system_ &&
799 (file_system_context()
800 ->GetFileSystemBackend(src_url.type())
801 ->HasInplaceCopyImplementation(src_url.type()) ||
802 operation_type_ == OPERATION_MOVE)) {
803 impl = new CopyOrMoveOnSameFileSystemImpl(
804 operation_runner(), operation_type_, src_url, dest_url, option_,
805 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
806 weak_factory_.GetWeakPtr(), src_url));
807 } else {
808 // Cross filesystem case.
809 base::File::Error error = base::File::FILE_ERROR_FAILED;
810 CopyOrMoveFileValidatorFactory* validator_factory =
811 file_system_context()->GetCopyOrMoveFileValidatorFactory(
812 dest_root_.type(), &error);
813 if (error != base::File::FILE_OK) {
814 callback.Run(error);
815 return;
818 if (!validator_factory) {
819 scoped_ptr<storage::FileStreamReader> reader =
820 file_system_context()->CreateFileStreamReader(
821 src_url, 0 /* offset */, storage::kMaximumLength, base::Time());
822 scoped_ptr<FileStreamWriter> writer =
823 file_system_context()->CreateFileStreamWriter(dest_url, 0);
824 if (reader && writer) {
825 impl = new StreamCopyOrMoveImpl(
826 operation_runner(),
827 file_system_context(),
828 operation_type_,
829 src_url,
830 dest_url,
831 option_,
832 reader.Pass(),
833 writer.Pass(),
834 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
835 weak_factory_.GetWeakPtr(),
836 src_url));
840 if (!impl) {
841 impl = new SnapshotCopyOrMoveImpl(
842 operation_runner(), operation_type_, src_url, dest_url, option_,
843 validator_factory,
844 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
845 weak_factory_.GetWeakPtr(), src_url));
849 // Register the running task.
850 running_copy_set_.insert(impl);
851 impl->Run(base::Bind(
852 &CopyOrMoveOperationDelegate::DidCopyOrMoveFile,
853 weak_factory_.GetWeakPtr(), src_url, dest_url, callback, impl));
856 void CopyOrMoveOperationDelegate::ProcessDirectory(
857 const FileSystemURL& src_url,
858 const StatusCallback& callback) {
859 if (src_url == src_root_) {
860 // The src_root_ looks to be a directory.
861 // Try removing the dest_root_ to see if it exists and/or it is an
862 // empty directory.
863 // We do not invoke |progress_callback_| for source root, because it is
864 // already called in ProcessFile().
865 operation_runner()->RemoveDirectory(
866 dest_root_,
867 base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot,
868 weak_factory_.GetWeakPtr(), callback));
869 return;
872 if (!progress_callback_.is_null()) {
873 progress_callback_.Run(
874 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
877 ProcessDirectoryInternal(src_url, CreateDestURL(src_url), callback);
880 void CopyOrMoveOperationDelegate::PostProcessDirectory(
881 const FileSystemURL& src_url,
882 const StatusCallback& callback) {
883 if (option_ == FileSystemOperation::OPTION_NONE) {
884 PostProcessDirectoryAfterTouchFile(
885 src_url, callback, base::File::FILE_OK);
886 return;
889 operation_runner()->GetMetadata(
890 src_url,
891 base::Bind(
892 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata,
893 weak_factory_.GetWeakPtr(), src_url, callback));
896 void CopyOrMoveOperationDelegate::OnCancel() {
897 // Request to cancel all running Copy/Move file.
898 for (std::set<CopyOrMoveImpl*>::iterator iter = running_copy_set_.begin();
899 iter != running_copy_set_.end(); ++iter)
900 (*iter)->Cancel();
903 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
904 const FileSystemURL& src_url,
905 const FileSystemURL& dest_url,
906 const StatusCallback& callback,
907 CopyOrMoveImpl* impl,
908 base::File::Error error) {
909 running_copy_set_.erase(impl);
910 delete impl;
912 if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
913 progress_callback_.Run(
914 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
917 callback.Run(error);
920 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
921 const StatusCallback& callback,
922 base::File::Error error) {
923 if (error == base::File::FILE_ERROR_NOT_A_DIRECTORY) {
924 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
925 return;
927 if (error != base::File::FILE_OK &&
928 error != base::File::FILE_ERROR_NOT_FOUND) {
929 callback_.Run(error);
930 return;
933 ProcessDirectoryInternal(src_root_, dest_root_, callback);
936 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal(
937 const FileSystemURL& src_url,
938 const FileSystemURL& dest_url,
939 const StatusCallback& callback) {
940 // If operation_type == Move we may need to record directories and
941 // restore directory timestamps in the end, though it may have
942 // negative performance impact.
943 // See http://crbug.com/171284 for more details.
944 operation_runner()->CreateDirectory(
945 dest_url, false /* exclusive */, false /* recursive */,
946 base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory,
947 weak_factory_.GetWeakPtr(), src_url, dest_url, callback));
950 void CopyOrMoveOperationDelegate::DidCreateDirectory(
951 const FileSystemURL& src_url,
952 const FileSystemURL& dest_url,
953 const StatusCallback& callback,
954 base::File::Error error) {
955 if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
956 progress_callback_.Run(
957 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
960 callback.Run(error);
963 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata(
964 const FileSystemURL& src_url,
965 const StatusCallback& callback,
966 base::File::Error error,
967 const base::File::Info& file_info) {
968 if (error != base::File::FILE_OK) {
969 // Ignore the error, and run post process which should run after TouchFile.
970 PostProcessDirectoryAfterTouchFile(
971 src_url, callback, base::File::FILE_OK);
972 return;
975 operation_runner()->TouchFile(
976 CreateDestURL(src_url), base::Time::Now() /* last access */,
977 file_info.last_modified,
978 base::Bind(
979 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile,
980 weak_factory_.GetWeakPtr(), src_url, callback));
983 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile(
984 const FileSystemURL& src_url,
985 const StatusCallback& callback,
986 base::File::Error error) {
987 // Even if the TouchFile is failed, just ignore it.
989 if (operation_type_ == OPERATION_COPY) {
990 callback.Run(base::File::FILE_OK);
991 return;
994 DCHECK_EQ(OPERATION_MOVE, operation_type_);
996 // All files and subdirectories in the directory should be moved here,
997 // so remove the source directory for finalizing move operation.
998 operation_runner()->Remove(
999 src_url, false /* recursive */,
1000 base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove,
1001 weak_factory_.GetWeakPtr(), callback));
1004 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
1005 const StatusCallback& callback,
1006 base::File::Error error) {
1007 if (error == base::File::FILE_ERROR_NOT_FOUND)
1008 error = base::File::FILE_OK;
1009 callback.Run(error);
1012 void CopyOrMoveOperationDelegate::OnCopyFileProgress(
1013 const FileSystemURL& src_url, int64 size) {
1014 if (!progress_callback_.is_null()) {
1015 progress_callback_.Run(
1016 FileSystemOperation::PROGRESS, src_url, FileSystemURL(), size);
1020 FileSystemURL CopyOrMoveOperationDelegate::CreateDestURL(
1021 const FileSystemURL& src_url) const {
1022 DCHECK_EQ(src_root_.type(), src_url.type());
1023 DCHECK_EQ(src_root_.origin(), src_url.origin());
1025 base::FilePath relative = dest_root_.virtual_path();
1026 src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(),
1027 &relative);
1028 return file_system_context()->CreateCrackedFileSystemURL(
1029 dest_root_.origin(),
1030 dest_root_.mount_type(),
1031 relative);
1034 } // namespace storage