Don't preload rarely seen large images
[chromium-blink-merge.git] / storage / browser / fileapi / copy_or_move_operation_delegate.cc
blobcf4692bfa5725ee731eddf9e15dda096c8eab9f6
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/shareable_file_reference.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_reader.h"
15 #include "storage/browser/fileapi/file_stream_writer.h"
16 #include "storage/browser/fileapi/file_system_context.h"
17 #include "storage/browser/fileapi/file_system_operation_runner.h"
18 #include "storage/browser/fileapi/file_system_url.h"
19 #include "storage/common/fileapi/file_system_util.h"
21 namespace storage {
23 const int64 kFlushIntervalInBytes = 10 << 20; // 10MB.
25 class CopyOrMoveOperationDelegate::CopyOrMoveImpl {
26 public:
27 virtual ~CopyOrMoveImpl() {}
28 virtual void Run(
29 const CopyOrMoveOperationDelegate::StatusCallback& callback) = 0;
30 virtual void Cancel() = 0;
32 protected:
33 CopyOrMoveImpl() {}
35 private:
36 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl);
39 namespace {
41 // Copies a file on a (same) file system. Just delegate the operation to
42 // |operation_runner|.
43 class CopyOrMoveOnSameFileSystemImpl
44 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
45 public:
46 CopyOrMoveOnSameFileSystemImpl(
47 FileSystemOperationRunner* operation_runner,
48 CopyOrMoveOperationDelegate::OperationType operation_type,
49 const FileSystemURL& src_url,
50 const FileSystemURL& dest_url,
51 CopyOrMoveOperationDelegate::CopyOrMoveOption option,
52 const FileSystemOperation::CopyFileProgressCallback&
53 file_progress_callback)
54 : operation_runner_(operation_runner),
55 operation_type_(operation_type),
56 src_url_(src_url),
57 dest_url_(dest_url),
58 option_(option),
59 file_progress_callback_(file_progress_callback) {
62 void Run(
63 const CopyOrMoveOperationDelegate::StatusCallback& callback) override {
64 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_MOVE) {
65 operation_runner_->MoveFileLocal(src_url_, dest_url_, option_, callback);
66 } else {
67 operation_runner_->CopyFileLocal(
68 src_url_, dest_url_, option_, file_progress_callback_, callback);
72 void Cancel() override {
73 // We can do nothing for the copy/move operation on a local file system.
74 // Assuming the operation is quickly done, it should be ok to just wait
75 // for the completion.
78 private:
79 FileSystemOperationRunner* operation_runner_;
80 CopyOrMoveOperationDelegate::OperationType operation_type_;
81 FileSystemURL src_url_;
82 FileSystemURL dest_url_;
83 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
84 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
85 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOnSameFileSystemImpl);
88 // Specifically for cross file system copy/move operation, this class creates
89 // a snapshot file, validates it if necessary, runs copying process,
90 // validates the created file, and removes source file for move (noop for
91 // copy).
92 class SnapshotCopyOrMoveImpl
93 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
94 public:
95 SnapshotCopyOrMoveImpl(
96 FileSystemOperationRunner* operation_runner,
97 CopyOrMoveOperationDelegate::OperationType operation_type,
98 const FileSystemURL& src_url,
99 const FileSystemURL& dest_url,
100 CopyOrMoveOperationDelegate::CopyOrMoveOption option,
101 CopyOrMoveFileValidatorFactory* validator_factory,
102 const FileSystemOperation::CopyFileProgressCallback&
103 file_progress_callback)
104 : operation_runner_(operation_runner),
105 operation_type_(operation_type),
106 src_url_(src_url),
107 dest_url_(dest_url),
108 option_(option),
109 validator_factory_(validator_factory),
110 file_progress_callback_(file_progress_callback),
111 cancel_requested_(false),
112 weak_factory_(this) {
115 void Run(
116 const CopyOrMoveOperationDelegate::StatusCallback& callback) override {
117 file_progress_callback_.Run(0);
118 operation_runner_->CreateSnapshotFile(
119 src_url_,
120 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCreateSnapshot,
121 weak_factory_.GetWeakPtr(), callback));
124 void Cancel() override { cancel_requested_ = true; }
126 private:
127 void RunAfterCreateSnapshot(
128 const CopyOrMoveOperationDelegate::StatusCallback& callback,
129 base::File::Error error,
130 const base::File::Info& file_info,
131 const base::FilePath& platform_path,
132 const scoped_refptr<storage::ShareableFileReference>& file_ref) {
133 if (cancel_requested_)
134 error = base::File::FILE_ERROR_ABORT;
136 if (error != base::File::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::File::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::File::Info& file_info,
163 const scoped_refptr<storage::ShareableFileReference>& file_ref,
164 const CopyOrMoveOperationDelegate::StatusCallback& callback,
165 base::File::Error error) {
166 if (cancel_requested_)
167 error = base::File::FILE_ERROR_ABORT;
169 if (error != base::File::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::File::Info& file_info,
184 const scoped_refptr<storage::ShareableFileReference>& file_ref,
185 const CopyOrMoveOperationDelegate::StatusCallback& callback,
186 base::File::Error error) {
187 if (cancel_requested_)
188 error = base::File::FILE_ERROR_ABORT;
190 if (error != base::File::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::File::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::File::Error error) {
212 // Even if TouchFile is failed, just ignore it.
214 if (cancel_requested_) {
215 callback.Run(base::File::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::File::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::File::Error error) {
235 if (cancel_requested_) {
236 callback.Run(base::File::FILE_ERROR_ABORT);
237 return;
240 if (error != base::File::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::File::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::File::Error error) {
266 if (cancel_requested_)
267 error = base::File::FILE_ERROR_ABORT;
269 if (error == base::File::FILE_ERROR_NOT_FOUND)
270 error = base::File::FILE_OK;
271 callback.Run(error);
274 void DidRemoveDestForError(
275 base::File::Error prior_error,
276 const CopyOrMoveOperationDelegate::StatusCallback& callback,
277 base::File::Error error) {
278 if (error != base::File::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::File::Error error,
309 const base::File::Info& file_info,
310 const base::FilePath& platform_path,
311 const scoped_refptr<storage::ShareableFileReference>& file_ref) {
312 if (cancel_requested_)
313 error = base::File::FILE_ERROR_ABORT;
315 if (error != base::File::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<storage::ShareableFileReference>& file_ref,
333 const CopyOrMoveOperationDelegate::StatusCallback& callback,
334 base::File::Error 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 FileSystemContext* file_system_context,
368 CopyOrMoveOperationDelegate::OperationType operation_type,
369 const FileSystemURL& src_url,
370 const FileSystemURL& dest_url,
371 CopyOrMoveOperationDelegate::CopyOrMoveOption option,
372 scoped_ptr<storage::FileStreamReader> reader,
373 scoped_ptr<FileStreamWriter> writer,
374 const FileSystemOperation::CopyFileProgressCallback&
375 file_progress_callback)
376 : operation_runner_(operation_runner),
377 file_system_context_(file_system_context),
378 operation_type_(operation_type),
379 src_url_(src_url),
380 dest_url_(dest_url),
381 option_(option),
382 reader_(reader.Pass()),
383 writer_(writer.Pass()),
384 file_progress_callback_(file_progress_callback),
385 cancel_requested_(false),
386 weak_factory_(this) {}
388 void Run(
389 const CopyOrMoveOperationDelegate::StatusCallback& callback) override {
390 // Reader can be created even if the entry does not exist or the entry is
391 // a directory. To check errors before destination file creation,
392 // check metadata first.
393 operation_runner_->GetMetadata(
394 src_url_,
395 base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource,
396 weak_factory_.GetWeakPtr(), callback));
399 void Cancel() override {
400 cancel_requested_ = true;
401 if (copy_helper_)
402 copy_helper_->Cancel();
405 private:
406 void NotifyOnStartUpdate(const FileSystemURL& url) {
407 if (file_system_context_->GetUpdateObservers(url.type())) {
408 file_system_context_->GetUpdateObservers(url.type())
409 ->Notify(&FileUpdateObserver::OnStartUpdate, base::MakeTuple(url));
413 void NotifyOnModifyFile(const FileSystemURL& url) {
414 if (file_system_context_->GetChangeObservers(url.type())) {
415 file_system_context_->GetChangeObservers(url.type())
416 ->Notify(&FileChangeObserver::OnModifyFile, base::MakeTuple(url));
420 void NotifyOnEndUpdate(const FileSystemURL& url) {
421 if (file_system_context_->GetUpdateObservers(url.type())) {
422 file_system_context_->GetUpdateObservers(url.type())
423 ->Notify(&FileUpdateObserver::OnEndUpdate, base::MakeTuple(url));
427 void RunAfterGetMetadataForSource(
428 const CopyOrMoveOperationDelegate::StatusCallback& callback,
429 base::File::Error error,
430 const base::File::Info& file_info) {
431 if (cancel_requested_)
432 error = base::File::FILE_ERROR_ABORT;
434 if (error != base::File::FILE_OK) {
435 callback.Run(error);
436 return;
439 if (file_info.is_directory) {
440 // If not a directory, failed with appropriate error code.
441 callback.Run(base::File::FILE_ERROR_NOT_A_FILE);
442 return;
445 // To use FileStreamWriter, we need to ensure the destination file exists.
446 operation_runner_->CreateFile(
447 dest_url_,
448 true /* exclusive */,
449 base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination,
450 weak_factory_.GetWeakPtr(),
451 callback,
452 file_info.last_modified));
455 void RunAfterCreateFileForDestination(
456 const CopyOrMoveOperationDelegate::StatusCallback& callback,
457 const base::Time& last_modified,
458 base::File::Error error) {
459 if (cancel_requested_)
460 error = base::File::FILE_ERROR_ABORT;
461 // This conversion is to return the consistent status code with
462 // FileSystemFileUtil::Copy.
463 if (error == base::File::FILE_ERROR_NOT_A_FILE)
464 error = base::File::FILE_ERROR_INVALID_OPERATION;
466 if (error != base::File::FILE_OK &&
467 error != base::File::FILE_ERROR_EXISTS) {
468 callback.Run(error);
469 return;
472 if (error == base::File::FILE_ERROR_EXISTS) {
473 operation_runner_->Truncate(
474 dest_url_,
475 0 /* length */,
476 base::Bind(&StreamCopyOrMoveImpl::RunAfterTruncateForDestination,
477 weak_factory_.GetWeakPtr(),
478 callback,
479 last_modified));
480 return;
482 RunAfterTruncateForDestination(
483 callback, last_modified, base::File::FILE_OK);
486 void RunAfterTruncateForDestination(
487 const CopyOrMoveOperationDelegate::StatusCallback& callback,
488 const base::Time& last_modified,
489 base::File::Error error) {
490 if (cancel_requested_)
491 error = base::File::FILE_ERROR_ABORT;
493 if (error != base::File::FILE_OK) {
494 callback.Run(error);
495 return;
498 NotifyOnStartUpdate(dest_url_);
499 DCHECK(!copy_helper_);
500 copy_helper_.reset(new CopyOrMoveOperationDelegate::StreamCopyHelper(
501 reader_.Pass(), writer_.Pass(), dest_url_.mount_option().flush_policy(),
502 kReadBufferSize, file_progress_callback_,
503 base::TimeDelta::FromMilliseconds(
504 kMinProgressCallbackInvocationSpanInMilliseconds)));
505 copy_helper_->Run(
506 base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy,
507 weak_factory_.GetWeakPtr(), callback, last_modified));
510 void RunAfterStreamCopy(
511 const CopyOrMoveOperationDelegate::StatusCallback& callback,
512 const base::Time& last_modified,
513 base::File::Error error) {
514 NotifyOnModifyFile(dest_url_);
515 NotifyOnEndUpdate(dest_url_);
516 if (cancel_requested_)
517 error = base::File::FILE_ERROR_ABORT;
519 if (error != base::File::FILE_OK) {
520 callback.Run(error);
521 return;
524 if (option_ == FileSystemOperation::OPTION_NONE) {
525 RunAfterTouchFile(callback, base::File::FILE_OK);
526 return;
529 operation_runner_->TouchFile(
530 dest_url_, base::Time::Now() /* last_access */, last_modified,
531 base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile,
532 weak_factory_.GetWeakPtr(), callback));
535 void RunAfterTouchFile(
536 const CopyOrMoveOperationDelegate::StatusCallback& callback,
537 base::File::Error error) {
538 // Even if TouchFile is failed, just ignore it.
539 if (cancel_requested_) {
540 callback.Run(base::File::FILE_ERROR_ABORT);
541 return;
544 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
545 callback.Run(base::File::FILE_OK);
546 return;
549 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
551 // Remove the source for finalizing move operation.
552 operation_runner_->Remove(
553 src_url_, false /* recursive */,
554 base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove,
555 weak_factory_.GetWeakPtr(), callback));
558 void RunAfterRemoveForMove(
559 const CopyOrMoveOperationDelegate::StatusCallback& callback,
560 base::File::Error error) {
561 if (cancel_requested_)
562 error = base::File::FILE_ERROR_ABORT;
563 if (error == base::File::FILE_ERROR_NOT_FOUND)
564 error = base::File::FILE_OK;
565 callback.Run(error);
568 FileSystemOperationRunner* operation_runner_;
569 scoped_refptr<FileSystemContext> file_system_context_;
570 CopyOrMoveOperationDelegate::OperationType operation_type_;
571 FileSystemURL src_url_;
572 FileSystemURL dest_url_;
573 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
574 scoped_ptr<storage::FileStreamReader> reader_;
575 scoped_ptr<FileStreamWriter> writer_;
576 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
577 scoped_ptr<CopyOrMoveOperationDelegate::StreamCopyHelper> copy_helper_;
578 bool cancel_requested_;
579 base::WeakPtrFactory<StreamCopyOrMoveImpl> weak_factory_;
580 DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl);
583 } // namespace
585 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper(
586 scoped_ptr<storage::FileStreamReader> reader,
587 scoped_ptr<FileStreamWriter> writer,
588 storage::FlushPolicy flush_policy,
589 int buffer_size,
590 const FileSystemOperation::CopyFileProgressCallback& file_progress_callback,
591 const base::TimeDelta& min_progress_callback_invocation_span)
592 : reader_(reader.Pass()),
593 writer_(writer.Pass()),
594 flush_policy_(flush_policy),
595 file_progress_callback_(file_progress_callback),
596 io_buffer_(new net::IOBufferWithSize(buffer_size)),
597 num_copied_bytes_(0),
598 previous_flush_offset_(0),
599 min_progress_callback_invocation_span_(
600 min_progress_callback_invocation_span),
601 cancel_requested_(false),
602 weak_factory_(this) {
605 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() {
608 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run(
609 const StatusCallback& callback) {
610 file_progress_callback_.Run(0);
611 last_progress_callback_invocation_time_ = base::Time::Now();
612 Read(callback);
615 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() {
616 cancel_requested_ = true;
619 void CopyOrMoveOperationDelegate::StreamCopyHelper::Read(
620 const StatusCallback& callback) {
621 int result = reader_->Read(
622 io_buffer_.get(), io_buffer_->size(),
623 base::Bind(&StreamCopyHelper::DidRead,
624 weak_factory_.GetWeakPtr(), callback));
625 if (result != net::ERR_IO_PENDING)
626 DidRead(callback, result);
629 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead(
630 const StatusCallback& callback, int result) {
631 if (cancel_requested_) {
632 callback.Run(base::File::FILE_ERROR_ABORT);
633 return;
636 if (result < 0) {
637 callback.Run(NetErrorToFileError(result));
638 return;
641 if (result == 0) {
642 // Here is the EOF.
643 if (flush_policy_ == storage::FlushPolicy::FLUSH_ON_COMPLETION)
644 Flush(callback, true /* is_eof */);
645 else
646 callback.Run(base::File::FILE_OK);
647 return;
650 Write(callback, new net::DrainableIOBuffer(io_buffer_.get(), result));
653 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write(
654 const StatusCallback& callback,
655 scoped_refptr<net::DrainableIOBuffer> buffer) {
656 DCHECK_GT(buffer->BytesRemaining(), 0);
658 int result = writer_->Write(
659 buffer.get(), buffer->BytesRemaining(),
660 base::Bind(&StreamCopyHelper::DidWrite,
661 weak_factory_.GetWeakPtr(), callback, buffer));
662 if (result != net::ERR_IO_PENDING)
663 DidWrite(callback, buffer, result);
666 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite(
667 const StatusCallback& callback,
668 scoped_refptr<net::DrainableIOBuffer> buffer,
669 int result) {
670 if (cancel_requested_) {
671 callback.Run(base::File::FILE_ERROR_ABORT);
672 return;
675 if (result < 0) {
676 callback.Run(NetErrorToFileError(result));
677 return;
680 buffer->DidConsume(result);
681 num_copied_bytes_ += result;
683 // Check the elapsed time since last |file_progress_callback_| invocation.
684 base::Time now = base::Time::Now();
685 if (now - last_progress_callback_invocation_time_ >=
686 min_progress_callback_invocation_span_) {
687 file_progress_callback_.Run(num_copied_bytes_);
688 last_progress_callback_invocation_time_ = now;
691 if (buffer->BytesRemaining() > 0) {
692 Write(callback, buffer);
693 return;
696 if (flush_policy_ == storage::FlushPolicy::FLUSH_ON_COMPLETION &&
697 (num_copied_bytes_ - previous_flush_offset_) > kFlushIntervalInBytes) {
698 Flush(callback, false /* not is_eof */);
699 } else {
700 Read(callback);
704 void CopyOrMoveOperationDelegate::StreamCopyHelper::Flush(
705 const StatusCallback& callback, bool is_eof) {
706 int result = writer_->Flush(
707 base::Bind(&StreamCopyHelper::DidFlush,
708 weak_factory_.GetWeakPtr(), callback, is_eof));
709 if (result != net::ERR_IO_PENDING)
710 DidFlush(callback, is_eof, result);
713 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidFlush(
714 const StatusCallback& callback, bool is_eof, int result) {
715 if (cancel_requested_) {
716 callback.Run(base::File::FILE_ERROR_ABORT);
717 return;
720 previous_flush_offset_ = num_copied_bytes_;
721 if (is_eof)
722 callback.Run(NetErrorToFileError(result));
723 else
724 Read(callback);
727 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate(
728 FileSystemContext* file_system_context,
729 const FileSystemURL& src_root,
730 const FileSystemURL& dest_root,
731 OperationType operation_type,
732 CopyOrMoveOption option,
733 ErrorBehavior error_behavior,
734 const CopyProgressCallback& progress_callback,
735 const StatusCallback& callback)
736 : RecursiveOperationDelegate(file_system_context),
737 src_root_(src_root),
738 dest_root_(dest_root),
739 operation_type_(operation_type),
740 option_(option),
741 error_behavior_(error_behavior),
742 progress_callback_(progress_callback),
743 callback_(callback),
744 weak_factory_(this) {
745 same_file_system_ = src_root_.IsInSameFileSystem(dest_root_);
748 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
749 STLDeleteElements(&running_copy_set_);
752 void CopyOrMoveOperationDelegate::Run() {
753 // Not supported; this should never be called.
754 NOTREACHED();
757 void CopyOrMoveOperationDelegate::RunRecursively() {
758 // Perform light-weight checks first.
760 // It is an error to try to copy/move an entry into its child.
761 if (same_file_system_ && src_root_.path().IsParent(dest_root_.path())) {
762 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
763 return;
766 if (same_file_system_ && src_root_.path() == dest_root_.path()) {
767 // In JS API this should return error, but we return success because Pepper
768 // wants to return success and we have a code path that returns error in
769 // Blink for JS (http://crbug.com/329517).
770 callback_.Run(base::File::FILE_OK);
771 return;
774 // Start to process the source directory recursively.
775 // TODO(kinuko): This could be too expensive for same_file_system_==true
776 // and operation==MOVE case, probably we can just rename the root directory.
777 // http://crbug.com/172187
778 StartRecursiveOperation(src_root_, error_behavior_, callback_);
781 void CopyOrMoveOperationDelegate::ProcessFile(
782 const FileSystemURL& src_url,
783 const StatusCallback& callback) {
784 if (!progress_callback_.is_null()) {
785 progress_callback_.Run(
786 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
789 FileSystemURL dest_url = CreateDestURL(src_url);
790 CopyOrMoveImpl* impl = NULL;
791 if (same_file_system_ &&
792 (file_system_context()
793 ->GetFileSystemBackend(src_url.type())
794 ->HasInplaceCopyImplementation(src_url.type()) ||
795 operation_type_ == OPERATION_MOVE)) {
796 impl = new CopyOrMoveOnSameFileSystemImpl(
797 operation_runner(), operation_type_, src_url, dest_url, option_,
798 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
799 weak_factory_.GetWeakPtr(), src_url));
800 } else {
801 // Cross filesystem case.
802 base::File::Error error = base::File::FILE_ERROR_FAILED;
803 CopyOrMoveFileValidatorFactory* validator_factory =
804 file_system_context()->GetCopyOrMoveFileValidatorFactory(
805 dest_root_.type(), &error);
806 if (error != base::File::FILE_OK) {
807 if (!progress_callback_.is_null())
808 progress_callback_.Run(FileSystemOperation::ERROR_COPY_ENTRY, src_url,
809 dest_url, 0);
811 callback.Run(error);
812 return;
815 if (!validator_factory) {
816 scoped_ptr<storage::FileStreamReader> reader =
817 file_system_context()->CreateFileStreamReader(
818 src_url, 0 /* offset */, storage::kMaximumLength, base::Time());
819 scoped_ptr<FileStreamWriter> writer =
820 file_system_context()->CreateFileStreamWriter(dest_url, 0);
821 if (reader && writer) {
822 impl = new StreamCopyOrMoveImpl(
823 operation_runner(),
824 file_system_context(),
825 operation_type_,
826 src_url,
827 dest_url,
828 option_,
829 reader.Pass(),
830 writer.Pass(),
831 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
832 weak_factory_.GetWeakPtr(),
833 src_url));
837 if (!impl) {
838 impl = new SnapshotCopyOrMoveImpl(
839 operation_runner(), operation_type_, src_url, dest_url, option_,
840 validator_factory,
841 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
842 weak_factory_.GetWeakPtr(), src_url));
846 // Register the running task.
847 running_copy_set_.insert(impl);
848 impl->Run(base::Bind(
849 &CopyOrMoveOperationDelegate::DidCopyOrMoveFile,
850 weak_factory_.GetWeakPtr(), src_url, dest_url, callback, impl));
853 void CopyOrMoveOperationDelegate::ProcessDirectory(
854 const FileSystemURL& src_url,
855 const StatusCallback& callback) {
856 if (src_url == src_root_) {
857 // The src_root_ looks to be a directory.
858 // Try removing the dest_root_ to see if it exists and/or it is an
859 // empty directory.
860 // We do not invoke |progress_callback_| for source root, because it is
861 // already called in ProcessFile().
862 operation_runner()->RemoveDirectory(
863 dest_root_,
864 base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot,
865 weak_factory_.GetWeakPtr(), callback));
866 return;
869 if (!progress_callback_.is_null()) {
870 progress_callback_.Run(
871 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
874 ProcessDirectoryInternal(src_url, CreateDestURL(src_url), callback);
877 void CopyOrMoveOperationDelegate::PostProcessDirectory(
878 const FileSystemURL& src_url,
879 const StatusCallback& callback) {
880 if (option_ == FileSystemOperation::OPTION_NONE) {
881 PostProcessDirectoryAfterTouchFile(
882 src_url, callback, base::File::FILE_OK);
883 return;
886 operation_runner()->GetMetadata(
887 src_url,
888 base::Bind(
889 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata,
890 weak_factory_.GetWeakPtr(), src_url, callback));
893 void CopyOrMoveOperationDelegate::OnCancel() {
894 // Request to cancel all running Copy/Move file.
895 for (std::set<CopyOrMoveImpl*>::iterator iter = running_copy_set_.begin();
896 iter != running_copy_set_.end(); ++iter)
897 (*iter)->Cancel();
900 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
901 const FileSystemURL& src_url,
902 const FileSystemURL& dest_url,
903 const StatusCallback& callback,
904 CopyOrMoveImpl* impl,
905 base::File::Error error) {
906 running_copy_set_.erase(impl);
907 delete impl;
909 if (!progress_callback_.is_null() && error != base::File::FILE_OK &&
910 error != base::File::FILE_ERROR_NOT_A_FILE)
911 progress_callback_.Run(FileSystemOperation::ERROR_COPY_ENTRY, src_url,
912 dest_url, 0);
914 if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
915 progress_callback_.Run(
916 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
919 callback.Run(error);
922 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
923 const StatusCallback& callback,
924 base::File::Error error) {
925 if (error == base::File::FILE_ERROR_NOT_A_DIRECTORY) {
926 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
927 return;
929 if (error != base::File::FILE_OK &&
930 error != base::File::FILE_ERROR_NOT_FOUND) {
931 callback_.Run(error);
932 return;
935 ProcessDirectoryInternal(src_root_, dest_root_, callback);
938 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal(
939 const FileSystemURL& src_url,
940 const FileSystemURL& dest_url,
941 const StatusCallback& callback) {
942 // If operation_type == Move we may need to record directories and
943 // restore directory timestamps in the end, though it may have
944 // negative performance impact.
945 // See http://crbug.com/171284 for more details.
946 operation_runner()->CreateDirectory(
947 dest_url, false /* exclusive */, false /* recursive */,
948 base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory,
949 weak_factory_.GetWeakPtr(), src_url, dest_url, callback));
952 void CopyOrMoveOperationDelegate::DidCreateDirectory(
953 const FileSystemURL& src_url,
954 const FileSystemURL& dest_url,
955 const StatusCallback& callback,
956 base::File::Error error) {
957 if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
958 progress_callback_.Run(
959 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
962 callback.Run(error);
965 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata(
966 const FileSystemURL& src_url,
967 const StatusCallback& callback,
968 base::File::Error error,
969 const base::File::Info& file_info) {
970 if (error != base::File::FILE_OK) {
971 // Ignore the error, and run post process which should run after TouchFile.
972 PostProcessDirectoryAfterTouchFile(
973 src_url, callback, base::File::FILE_OK);
974 return;
977 operation_runner()->TouchFile(
978 CreateDestURL(src_url), base::Time::Now() /* last access */,
979 file_info.last_modified,
980 base::Bind(
981 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile,
982 weak_factory_.GetWeakPtr(), src_url, callback));
985 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile(
986 const FileSystemURL& src_url,
987 const StatusCallback& callback,
988 base::File::Error error) {
989 // Even if the TouchFile is failed, just ignore it.
991 if (operation_type_ == OPERATION_COPY) {
992 callback.Run(base::File::FILE_OK);
993 return;
996 DCHECK_EQ(OPERATION_MOVE, operation_type_);
998 // All files and subdirectories in the directory should be moved here,
999 // so remove the source directory for finalizing move operation.
1000 operation_runner()->Remove(
1001 src_url, false /* recursive */,
1002 base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove,
1003 weak_factory_.GetWeakPtr(), callback));
1006 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
1007 const StatusCallback& callback,
1008 base::File::Error error) {
1009 if (error == base::File::FILE_ERROR_NOT_FOUND)
1010 error = base::File::FILE_OK;
1011 callback.Run(error);
1014 void CopyOrMoveOperationDelegate::OnCopyFileProgress(
1015 const FileSystemURL& src_url, int64 size) {
1016 if (!progress_callback_.is_null()) {
1017 progress_callback_.Run(
1018 FileSystemOperation::PROGRESS, src_url, FileSystemURL(), size);
1022 FileSystemURL CopyOrMoveOperationDelegate::CreateDestURL(
1023 const FileSystemURL& src_url) const {
1024 DCHECK_EQ(src_root_.type(), src_url.type());
1025 DCHECK_EQ(src_root_.origin(), src_url.origin());
1027 base::FilePath relative = dest_root_.virtual_path();
1028 src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(),
1029 &relative);
1030 return file_system_context()->CreateCrackedFileSystemURL(
1031 dest_root_.origin(),
1032 dest_root_.mount_type(),
1033 relative);
1036 } // namespace storage