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"
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"
23 const int64 kFlushIntervalInBytes
= 10 << 20; // 10MB.
25 class CopyOrMoveOperationDelegate::CopyOrMoveImpl
{
27 virtual ~CopyOrMoveImpl() {}
29 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) = 0;
30 virtual void Cancel() = 0;
36 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl
);
41 // Copies a file on a (same) file system. Just delegate the operation to
42 // |operation_runner|.
43 class CopyOrMoveOnSameFileSystemImpl
44 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl
{
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
),
59 file_progress_callback_(file_progress_callback
) {
63 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) OVERRIDE
{
64 if (operation_type_
== CopyOrMoveOperationDelegate::OPERATION_MOVE
) {
65 operation_runner_
->MoveFileLocal(src_url_
, dest_url_
, option_
, callback
);
67 operation_runner_
->CopyFileLocal(
68 src_url_
, dest_url_
, option_
, file_progress_callback_
, callback
);
72 virtual 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.
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
92 class SnapshotCopyOrMoveImpl
93 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl
{
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
),
109 validator_factory_(validator_factory
),
110 file_progress_callback_(file_progress_callback
),
111 cancel_requested_(false),
112 weak_factory_(this) {
116 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) OVERRIDE
{
117 file_progress_callback_
.Run(0);
118 operation_runner_
->CreateSnapshotFile(
120 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCreateSnapshot
,
121 weak_factory_
.GetWeakPtr(), callback
));
124 virtual void Cancel() OVERRIDE
{
125 cancel_requested_
= true;
129 void RunAfterCreateSnapshot(
130 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
131 base::File::Error error
,
132 const base::File::Info
& file_info
,
133 const base::FilePath
& platform_path
,
134 const scoped_refptr
<webkit_blob::ShareableFileReference
>& file_ref
) {
135 if (cancel_requested_
)
136 error
= base::File::FILE_ERROR_ABORT
;
138 if (error
!= base::File::FILE_OK
) {
143 // For now we assume CreateSnapshotFile always return a valid local file
145 DCHECK(!platform_path
.empty());
147 if (!validator_factory_
) {
148 // No validation is needed.
149 RunAfterPreWriteValidation(platform_path
, file_info
, file_ref
, callback
,
150 base::File::FILE_OK
);
154 // Run pre write validation.
157 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPreWriteValidation
,
158 weak_factory_
.GetWeakPtr(),
159 platform_path
, file_info
, file_ref
, callback
));
162 void RunAfterPreWriteValidation(
163 const base::FilePath
& platform_path
,
164 const base::File::Info
& file_info
,
165 const scoped_refptr
<webkit_blob::ShareableFileReference
>& file_ref
,
166 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
167 base::File::Error error
) {
168 if (cancel_requested_
)
169 error
= base::File::FILE_ERROR_ABORT
;
171 if (error
!= base::File::FILE_OK
) {
176 // |file_ref| is unused but necessary to keep the file alive until
177 // CopyInForeignFile() is completed.
178 operation_runner_
->CopyInForeignFile(
179 platform_path
, dest_url_
,
180 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCopyInForeignFile
,
181 weak_factory_
.GetWeakPtr(), file_info
, file_ref
, callback
));
184 void RunAfterCopyInForeignFile(
185 const base::File::Info
& file_info
,
186 const scoped_refptr
<webkit_blob::ShareableFileReference
>& file_ref
,
187 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
188 base::File::Error error
) {
189 if (cancel_requested_
)
190 error
= base::File::FILE_ERROR_ABORT
;
192 if (error
!= base::File::FILE_OK
) {
197 file_progress_callback_
.Run(file_info
.size
);
199 if (option_
== FileSystemOperation::OPTION_NONE
) {
200 RunAfterTouchFile(callback
, base::File::FILE_OK
);
204 operation_runner_
->TouchFile(
205 dest_url_
, base::Time::Now() /* last_access */,
206 file_info
.last_modified
,
207 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterTouchFile
,
208 weak_factory_
.GetWeakPtr(), callback
));
211 void RunAfterTouchFile(
212 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
213 base::File::Error error
) {
214 // Even if TouchFile is failed, just ignore it.
216 if (cancel_requested_
) {
217 callback
.Run(base::File::FILE_ERROR_ABORT
);
221 // |validator_| is NULL when the destination filesystem does not do
224 // No validation is needed.
225 RunAfterPostWriteValidation(callback
, base::File::FILE_OK
);
230 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPostWriteValidation
,
231 weak_factory_
.GetWeakPtr(), callback
));
234 void RunAfterPostWriteValidation(
235 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
236 base::File::Error error
) {
237 if (cancel_requested_
) {
238 callback
.Run(base::File::FILE_ERROR_ABORT
);
242 if (error
!= base::File::FILE_OK
) {
243 // Failed to validate. Remove the destination file.
244 operation_runner_
->Remove(
245 dest_url_
, true /* recursive */,
246 base::Bind(&SnapshotCopyOrMoveImpl::DidRemoveDestForError
,
247 weak_factory_
.GetWeakPtr(), error
, callback
));
251 if (operation_type_
== CopyOrMoveOperationDelegate::OPERATION_COPY
) {
252 callback
.Run(base::File::FILE_OK
);
256 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE
, operation_type_
);
258 // Remove the source for finalizing move operation.
259 operation_runner_
->Remove(
260 src_url_
, true /* recursive */,
261 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterRemoveSourceForMove
,
262 weak_factory_
.GetWeakPtr(), callback
));
265 void RunAfterRemoveSourceForMove(
266 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
267 base::File::Error error
) {
268 if (cancel_requested_
)
269 error
= base::File::FILE_ERROR_ABORT
;
271 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
272 error
= base::File::FILE_OK
;
276 void DidRemoveDestForError(
277 base::File::Error prior_error
,
278 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
279 base::File::Error error
) {
280 if (error
!= base::File::FILE_OK
) {
281 VLOG(1) << "Error removing destination file after validation error: "
284 callback
.Run(prior_error
);
287 // Runs pre-write validation.
288 void PreWriteValidation(
289 const base::FilePath
& platform_path
,
290 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) {
291 DCHECK(validator_factory_
);
293 validator_factory_
->CreateCopyOrMoveFileValidator(
294 src_url_
, platform_path
));
295 validator_
->StartPreWriteValidation(callback
);
298 // Runs post-write validation.
299 void PostWriteValidation(
300 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) {
301 operation_runner_
->CreateSnapshotFile(
304 &SnapshotCopyOrMoveImpl::PostWriteValidationAfterCreateSnapshotFile
,
305 weak_factory_
.GetWeakPtr(), callback
));
308 void PostWriteValidationAfterCreateSnapshotFile(
309 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
310 base::File::Error error
,
311 const base::File::Info
& file_info
,
312 const base::FilePath
& platform_path
,
313 const scoped_refptr
<webkit_blob::ShareableFileReference
>& file_ref
) {
314 if (cancel_requested_
)
315 error
= base::File::FILE_ERROR_ABORT
;
317 if (error
!= base::File::FILE_OK
) {
323 // Note: file_ref passed here to keep the file alive until after
324 // the StartPostWriteValidation operation finishes.
325 validator_
->StartPostWriteValidation(
327 base::Bind(&SnapshotCopyOrMoveImpl::DidPostWriteValidation
,
328 weak_factory_
.GetWeakPtr(), file_ref
, callback
));
331 // |file_ref| is unused; it is passed here to make sure the reference is
332 // alive until after post-write validation is complete.
333 void DidPostWriteValidation(
334 const scoped_refptr
<webkit_blob::ShareableFileReference
>& file_ref
,
335 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
336 base::File::Error error
) {
340 FileSystemOperationRunner
* operation_runner_
;
341 CopyOrMoveOperationDelegate::OperationType operation_type_
;
342 FileSystemURL src_url_
;
343 FileSystemURL dest_url_
;
345 CopyOrMoveOperationDelegate::CopyOrMoveOption option_
;
346 CopyOrMoveFileValidatorFactory
* validator_factory_
;
347 scoped_ptr
<CopyOrMoveFileValidator
> validator_
;
348 FileSystemOperation::CopyFileProgressCallback file_progress_callback_
;
349 bool cancel_requested_
;
350 base::WeakPtrFactory
<SnapshotCopyOrMoveImpl
> weak_factory_
;
351 DISALLOW_COPY_AND_ASSIGN(SnapshotCopyOrMoveImpl
);
354 // The size of buffer for StreamCopyHelper.
355 const int kReadBufferSize
= 32768;
357 // To avoid too many progress callbacks, it should be called less
358 // frequently than 50ms.
359 const int kMinProgressCallbackInvocationSpanInMilliseconds
= 50;
361 // Specifically for cross file system copy/move operation, this class uses
362 // stream reader and writer for copying. Validator is not supported, so if
363 // necessary SnapshotCopyOrMoveImpl should be used.
364 class StreamCopyOrMoveImpl
365 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl
{
367 StreamCopyOrMoveImpl(
368 FileSystemOperationRunner
* operation_runner
,
369 CopyOrMoveOperationDelegate::OperationType operation_type
,
370 const FileSystemURL
& src_url
,
371 const FileSystemURL
& dest_url
,
372 CopyOrMoveOperationDelegate::CopyOrMoveOption option
,
373 scoped_ptr
<webkit_blob::FileStreamReader
> reader
,
374 scoped_ptr
<FileStreamWriter
> writer
,
375 const FileSystemOperation::CopyFileProgressCallback
&
376 file_progress_callback
)
377 : operation_runner_(operation_runner
),
378 operation_type_(operation_type
),
382 reader_(reader
.Pass()),
383 writer_(writer
.Pass()),
384 file_progress_callback_(file_progress_callback
),
385 cancel_requested_(false),
386 weak_factory_(this) {
390 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) OVERRIDE
{
391 // Reader can be created even if the entry does not exist or the entry is
392 // a directory. To check errors before destination file creation,
393 // check metadata first.
394 operation_runner_
->GetMetadata(
396 base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource
,
397 weak_factory_
.GetWeakPtr(), callback
));
400 virtual void Cancel() OVERRIDE
{
401 cancel_requested_
= true;
403 copy_helper_
->Cancel();
407 void RunAfterGetMetadataForSource(
408 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
409 base::File::Error error
,
410 const base::File::Info
& file_info
) {
411 if (cancel_requested_
)
412 error
= base::File::FILE_ERROR_ABORT
;
414 if (error
!= base::File::FILE_OK
) {
419 if (file_info
.is_directory
) {
420 // If not a directory, failed with appropriate error code.
421 callback
.Run(base::File::FILE_ERROR_NOT_A_FILE
);
425 // To use FileStreamWriter, we need to ensure the destination file exists.
426 operation_runner_
->CreateFile(
427 dest_url_
, false /* exclusive */,
428 base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination
,
429 weak_factory_
.GetWeakPtr(),
430 callback
, file_info
.last_modified
));
433 void RunAfterCreateFileForDestination(
434 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
435 const base::Time
& last_modified
,
436 base::File::Error error
) {
437 if (cancel_requested_
)
438 error
= base::File::FILE_ERROR_ABORT
;
440 if (error
!= base::File::FILE_OK
) {
445 const bool need_flush
= dest_url_
.mount_option().copy_sync_option() ==
446 fileapi::COPY_SYNC_OPTION_SYNC
;
448 DCHECK(!copy_helper_
);
450 new CopyOrMoveOperationDelegate::StreamCopyHelper(
451 reader_
.Pass(), writer_
.Pass(),
454 file_progress_callback_
,
455 base::TimeDelta::FromMilliseconds(
456 kMinProgressCallbackInvocationSpanInMilliseconds
)));
458 base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy
,
459 weak_factory_
.GetWeakPtr(), callback
, last_modified
));
462 void RunAfterStreamCopy(
463 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
464 const base::Time
& last_modified
,
465 base::File::Error error
) {
466 if (cancel_requested_
)
467 error
= base::File::FILE_ERROR_ABORT
;
469 if (error
!= base::File::FILE_OK
) {
474 if (option_
== FileSystemOperation::OPTION_NONE
) {
475 RunAfterTouchFile(callback
, base::File::FILE_OK
);
479 operation_runner_
->TouchFile(
480 dest_url_
, base::Time::Now() /* last_access */, last_modified
,
481 base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile
,
482 weak_factory_
.GetWeakPtr(), callback
));
485 void RunAfterTouchFile(
486 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
487 base::File::Error error
) {
488 // Even if TouchFile is failed, just ignore it.
489 if (cancel_requested_
) {
490 callback
.Run(base::File::FILE_ERROR_ABORT
);
494 if (operation_type_
== CopyOrMoveOperationDelegate::OPERATION_COPY
) {
495 callback
.Run(base::File::FILE_OK
);
499 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE
, operation_type_
);
501 // Remove the source for finalizing move operation.
502 operation_runner_
->Remove(
503 src_url_
, false /* recursive */,
504 base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove
,
505 weak_factory_
.GetWeakPtr(), callback
));
508 void RunAfterRemoveForMove(
509 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
510 base::File::Error error
) {
511 if (cancel_requested_
)
512 error
= base::File::FILE_ERROR_ABORT
;
513 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
514 error
= base::File::FILE_OK
;
518 FileSystemOperationRunner
* operation_runner_
;
519 CopyOrMoveOperationDelegate::OperationType operation_type_
;
520 FileSystemURL src_url_
;
521 FileSystemURL dest_url_
;
522 CopyOrMoveOperationDelegate::CopyOrMoveOption option_
;
523 scoped_ptr
<webkit_blob::FileStreamReader
> reader_
;
524 scoped_ptr
<FileStreamWriter
> writer_
;
525 FileSystemOperation::CopyFileProgressCallback file_progress_callback_
;
526 scoped_ptr
<CopyOrMoveOperationDelegate::StreamCopyHelper
> copy_helper_
;
527 bool cancel_requested_
;
528 base::WeakPtrFactory
<StreamCopyOrMoveImpl
> weak_factory_
;
529 DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl
);
534 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper(
535 scoped_ptr
<webkit_blob::FileStreamReader
> reader
,
536 scoped_ptr
<FileStreamWriter
> writer
,
539 const FileSystemOperation::CopyFileProgressCallback
&
540 file_progress_callback
,
541 const base::TimeDelta
& min_progress_callback_invocation_span
)
542 : reader_(reader
.Pass()),
543 writer_(writer
.Pass()),
544 need_flush_(need_flush
),
545 file_progress_callback_(file_progress_callback
),
546 io_buffer_(new net::IOBufferWithSize(buffer_size
)),
547 num_copied_bytes_(0),
548 previous_flush_offset_(0),
549 min_progress_callback_invocation_span_(
550 min_progress_callback_invocation_span
),
551 cancel_requested_(false),
552 weak_factory_(this) {
555 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() {
558 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run(
559 const StatusCallback
& callback
) {
560 file_progress_callback_
.Run(0);
561 last_progress_callback_invocation_time_
= base::Time::Now();
565 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() {
566 cancel_requested_
= true;
569 void CopyOrMoveOperationDelegate::StreamCopyHelper::Read(
570 const StatusCallback
& callback
) {
571 int result
= reader_
->Read(
572 io_buffer_
.get(), io_buffer_
->size(),
573 base::Bind(&StreamCopyHelper::DidRead
,
574 weak_factory_
.GetWeakPtr(), callback
));
575 if (result
!= net::ERR_IO_PENDING
)
576 DidRead(callback
, result
);
579 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead(
580 const StatusCallback
& callback
, int result
) {
581 if (cancel_requested_
) {
582 callback
.Run(base::File::FILE_ERROR_ABORT
);
587 callback
.Run(NetErrorToFileError(result
));
594 Flush(callback
, true /* is_eof */);
596 callback
.Run(base::File::FILE_OK
);
600 Write(callback
, new net::DrainableIOBuffer(io_buffer_
.get(), result
));
603 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write(
604 const StatusCallback
& callback
,
605 scoped_refptr
<net::DrainableIOBuffer
> buffer
) {
606 DCHECK_GT(buffer
->BytesRemaining(), 0);
608 int result
= writer_
->Write(
609 buffer
.get(), buffer
->BytesRemaining(),
610 base::Bind(&StreamCopyHelper::DidWrite
,
611 weak_factory_
.GetWeakPtr(), callback
, buffer
));
612 if (result
!= net::ERR_IO_PENDING
)
613 DidWrite(callback
, buffer
, result
);
616 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite(
617 const StatusCallback
& callback
,
618 scoped_refptr
<net::DrainableIOBuffer
> buffer
,
620 if (cancel_requested_
) {
621 callback
.Run(base::File::FILE_ERROR_ABORT
);
626 callback
.Run(NetErrorToFileError(result
));
630 buffer
->DidConsume(result
);
631 num_copied_bytes_
+= result
;
633 // Check the elapsed time since last |file_progress_callback_| invocation.
634 base::Time now
= base::Time::Now();
635 if (now
- last_progress_callback_invocation_time_
>=
636 min_progress_callback_invocation_span_
) {
637 file_progress_callback_
.Run(num_copied_bytes_
);
638 last_progress_callback_invocation_time_
= now
;
641 if (buffer
->BytesRemaining() > 0) {
642 Write(callback
, buffer
);
647 (num_copied_bytes_
- previous_flush_offset_
) > kFlushIntervalInBytes
) {
648 Flush(callback
, false /* not is_eof */);
654 void CopyOrMoveOperationDelegate::StreamCopyHelper::Flush(
655 const StatusCallback
& callback
, bool is_eof
) {
656 int result
= writer_
->Flush(
657 base::Bind(&StreamCopyHelper::DidFlush
,
658 weak_factory_
.GetWeakPtr(), callback
, is_eof
));
659 if (result
!= net::ERR_IO_PENDING
)
660 DidFlush(callback
, is_eof
, result
);
663 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidFlush(
664 const StatusCallback
& callback
, bool is_eof
, int result
) {
665 if (cancel_requested_
) {
666 callback
.Run(base::File::FILE_ERROR_ABORT
);
670 previous_flush_offset_
= num_copied_bytes_
;
672 callback
.Run(NetErrorToFileError(result
));
677 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate(
678 FileSystemContext
* file_system_context
,
679 const FileSystemURL
& src_root
,
680 const FileSystemURL
& dest_root
,
681 OperationType operation_type
,
682 CopyOrMoveOption option
,
683 const CopyProgressCallback
& progress_callback
,
684 const StatusCallback
& callback
)
685 : RecursiveOperationDelegate(file_system_context
),
687 dest_root_(dest_root
),
688 operation_type_(operation_type
),
690 progress_callback_(progress_callback
),
692 weak_factory_(this) {
693 same_file_system_
= src_root_
.IsInSameFileSystem(dest_root_
);
696 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
697 STLDeleteElements(&running_copy_set_
);
700 void CopyOrMoveOperationDelegate::Run() {
701 // Not supported; this should never be called.
705 void CopyOrMoveOperationDelegate::RunRecursively() {
706 // Perform light-weight checks first.
708 // It is an error to try to copy/move an entry into its child.
709 if (same_file_system_
&& src_root_
.path().IsParent(dest_root_
.path())) {
710 callback_
.Run(base::File::FILE_ERROR_INVALID_OPERATION
);
714 if (same_file_system_
&& src_root_
.path() == dest_root_
.path()) {
715 // In JS API this should return error, but we return success because Pepper
716 // wants to return success and we have a code path that returns error in
717 // Blink for JS (http://crbug.com/329517).
718 callback_
.Run(base::File::FILE_OK
);
722 // Start to process the source directory recursively.
723 // TODO(kinuko): This could be too expensive for same_file_system_==true
724 // and operation==MOVE case, probably we can just rename the root directory.
725 // http://crbug.com/172187
726 StartRecursiveOperation(src_root_
, callback_
);
729 void CopyOrMoveOperationDelegate::ProcessFile(
730 const FileSystemURL
& src_url
,
731 const StatusCallback
& callback
) {
732 if (!progress_callback_
.is_null()) {
733 progress_callback_
.Run(
734 FileSystemOperation::BEGIN_COPY_ENTRY
, src_url
, FileSystemURL(), 0);
737 FileSystemURL dest_url
= CreateDestURL(src_url
);
738 CopyOrMoveImpl
* impl
= NULL
;
739 if (same_file_system_
) {
740 impl
= new CopyOrMoveOnSameFileSystemImpl(
741 operation_runner(), operation_type_
, src_url
, dest_url
, option_
,
742 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress
,
743 weak_factory_
.GetWeakPtr(), src_url
));
745 // Cross filesystem case.
746 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
747 CopyOrMoveFileValidatorFactory
* validator_factory
=
748 file_system_context()->GetCopyOrMoveFileValidatorFactory(
749 dest_root_
.type(), &error
);
750 if (error
!= base::File::FILE_OK
) {
755 if (!validator_factory
) {
756 scoped_ptr
<webkit_blob::FileStreamReader
> reader
=
757 file_system_context()->CreateFileStreamReader(
758 src_url
, 0, base::Time());
759 scoped_ptr
<FileStreamWriter
> writer
=
760 file_system_context()->CreateFileStreamWriter(dest_url
, 0);
761 if (reader
&& writer
) {
762 impl
= new StreamCopyOrMoveImpl(
763 operation_runner(), operation_type_
, src_url
, dest_url
, option_
,
764 reader
.Pass(), writer
.Pass(),
765 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress
,
766 weak_factory_
.GetWeakPtr(), src_url
));
771 impl
= new SnapshotCopyOrMoveImpl(
772 operation_runner(), operation_type_
, src_url
, dest_url
, option_
,
774 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress
,
775 weak_factory_
.GetWeakPtr(), src_url
));
779 // Register the running task.
780 running_copy_set_
.insert(impl
);
781 impl
->Run(base::Bind(
782 &CopyOrMoveOperationDelegate::DidCopyOrMoveFile
,
783 weak_factory_
.GetWeakPtr(), src_url
, dest_url
, callback
, impl
));
786 void CopyOrMoveOperationDelegate::ProcessDirectory(
787 const FileSystemURL
& src_url
,
788 const StatusCallback
& callback
) {
789 if (src_url
== src_root_
) {
790 // The src_root_ looks to be a directory.
791 // Try removing the dest_root_ to see if it exists and/or it is an
793 // We do not invoke |progress_callback_| for source root, because it is
794 // already called in ProcessFile().
795 operation_runner()->RemoveDirectory(
797 base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot
,
798 weak_factory_
.GetWeakPtr(), callback
));
802 if (!progress_callback_
.is_null()) {
803 progress_callback_
.Run(
804 FileSystemOperation::BEGIN_COPY_ENTRY
, src_url
, FileSystemURL(), 0);
807 ProcessDirectoryInternal(src_url
, CreateDestURL(src_url
), callback
);
810 void CopyOrMoveOperationDelegate::PostProcessDirectory(
811 const FileSystemURL
& src_url
,
812 const StatusCallback
& callback
) {
813 if (option_
== FileSystemOperation::OPTION_NONE
) {
814 PostProcessDirectoryAfterTouchFile(
815 src_url
, callback
, base::File::FILE_OK
);
819 operation_runner()->GetMetadata(
822 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata
,
823 weak_factory_
.GetWeakPtr(), src_url
, callback
));
826 void CopyOrMoveOperationDelegate::OnCancel() {
827 // Request to cancel all running Copy/Move file.
828 for (std::set
<CopyOrMoveImpl
*>::iterator iter
= running_copy_set_
.begin();
829 iter
!= running_copy_set_
.end(); ++iter
)
833 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
834 const FileSystemURL
& src_url
,
835 const FileSystemURL
& dest_url
,
836 const StatusCallback
& callback
,
837 CopyOrMoveImpl
* impl
,
838 base::File::Error error
) {
839 running_copy_set_
.erase(impl
);
842 if (!progress_callback_
.is_null() && error
== base::File::FILE_OK
) {
843 progress_callback_
.Run(
844 FileSystemOperation::END_COPY_ENTRY
, src_url
, dest_url
, 0);
850 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
851 const StatusCallback
& callback
,
852 base::File::Error error
) {
853 if (error
== base::File::FILE_ERROR_NOT_A_DIRECTORY
) {
854 callback_
.Run(base::File::FILE_ERROR_INVALID_OPERATION
);
857 if (error
!= base::File::FILE_OK
&&
858 error
!= base::File::FILE_ERROR_NOT_FOUND
) {
859 callback_
.Run(error
);
863 ProcessDirectoryInternal(src_root_
, dest_root_
, callback
);
866 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal(
867 const FileSystemURL
& src_url
,
868 const FileSystemURL
& dest_url
,
869 const StatusCallback
& callback
) {
870 // If operation_type == Move we may need to record directories and
871 // restore directory timestamps in the end, though it may have
872 // negative performance impact.
873 // See http://crbug.com/171284 for more details.
874 operation_runner()->CreateDirectory(
875 dest_url
, false /* exclusive */, false /* recursive */,
876 base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory
,
877 weak_factory_
.GetWeakPtr(), src_url
, dest_url
, callback
));
880 void CopyOrMoveOperationDelegate::DidCreateDirectory(
881 const FileSystemURL
& src_url
,
882 const FileSystemURL
& dest_url
,
883 const StatusCallback
& callback
,
884 base::File::Error error
) {
885 if (!progress_callback_
.is_null() && error
== base::File::FILE_OK
) {
886 progress_callback_
.Run(
887 FileSystemOperation::END_COPY_ENTRY
, src_url
, dest_url
, 0);
893 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata(
894 const FileSystemURL
& src_url
,
895 const StatusCallback
& callback
,
896 base::File::Error error
,
897 const base::File::Info
& file_info
) {
898 if (error
!= base::File::FILE_OK
) {
899 // Ignore the error, and run post process which should run after TouchFile.
900 PostProcessDirectoryAfterTouchFile(
901 src_url
, callback
, base::File::FILE_OK
);
905 operation_runner()->TouchFile(
906 CreateDestURL(src_url
), base::Time::Now() /* last access */,
907 file_info
.last_modified
,
909 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile
,
910 weak_factory_
.GetWeakPtr(), src_url
, callback
));
913 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile(
914 const FileSystemURL
& src_url
,
915 const StatusCallback
& callback
,
916 base::File::Error error
) {
917 // Even if the TouchFile is failed, just ignore it.
919 if (operation_type_
== OPERATION_COPY
) {
920 callback
.Run(base::File::FILE_OK
);
924 DCHECK_EQ(OPERATION_MOVE
, operation_type_
);
926 // All files and subdirectories in the directory should be moved here,
927 // so remove the source directory for finalizing move operation.
928 operation_runner()->Remove(
929 src_url
, false /* recursive */,
930 base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove
,
931 weak_factory_
.GetWeakPtr(), callback
));
934 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
935 const StatusCallback
& callback
,
936 base::File::Error error
) {
937 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
938 error
= base::File::FILE_OK
;
942 void CopyOrMoveOperationDelegate::OnCopyFileProgress(
943 const FileSystemURL
& src_url
, int64 size
) {
944 if (!progress_callback_
.is_null()) {
945 progress_callback_
.Run(
946 FileSystemOperation::PROGRESS
, src_url
, FileSystemURL(), size
);
950 FileSystemURL
CopyOrMoveOperationDelegate::CreateDestURL(
951 const FileSystemURL
& src_url
) const {
952 DCHECK_EQ(src_root_
.type(), src_url
.type());
953 DCHECK_EQ(src_root_
.origin(), src_url
.origin());
955 base::FilePath relative
= dest_root_
.virtual_path();
956 src_root_
.virtual_path().AppendRelativePath(src_url
.virtual_path(),
958 return file_system_context()->CreateCrackedFileSystemURL(
960 dest_root_
.mount_type(),
964 } // namespace fileapi