- add sources.
[platform/framework/web/crosswalk.git] / src / webkit / browser / fileapi / copy_or_move_operation_delegate.cc
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.
4
5 #include "webkit/browser/fileapi/copy_or_move_operation_delegate.h"
6
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "net/base/io_buffer.h"
10 #include "net/base/net_errors.h"
11 #include "webkit/browser/blob/file_stream_reader.h"
12 #include "webkit/browser/fileapi/copy_or_move_file_validator.h"
13 #include "webkit/browser/fileapi/file_stream_writer.h"
14 #include "webkit/browser/fileapi/file_system_context.h"
15 #include "webkit/browser/fileapi/file_system_operation_runner.h"
16 #include "webkit/browser/fileapi/file_system_url.h"
17 #include "webkit/browser/fileapi/recursive_operation_delegate.h"
18 #include "webkit/common/blob/shareable_file_reference.h"
19 #include "webkit/common/fileapi/file_system_util.h"
20
21 namespace fileapi {
22
23 class CopyOrMoveOperationDelegate::CopyOrMoveImpl {
24  public:
25   virtual ~CopyOrMoveImpl() {}
26   virtual void Run(
27       const CopyOrMoveOperationDelegate::StatusCallback& callback) = 0;
28   virtual void Cancel() = 0;
29
30  protected:
31   CopyOrMoveImpl() {}
32
33  private:
34   DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl);
35 };
36
37 namespace {
38
39 // Copies a file on a (same) file system. Just delegate the operation to
40 // |operation_runner|.
41 class CopyOrMoveOnSameFileSystemImpl
42     : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
43  public:
44   CopyOrMoveOnSameFileSystemImpl(
45       FileSystemOperationRunner* operation_runner,
46       CopyOrMoveOperationDelegate::OperationType operation_type,
47       const FileSystemURL& src_url,
48       const FileSystemURL& dest_url,
49       CopyOrMoveOperationDelegate::CopyOrMoveOption option,
50       const FileSystemOperation::CopyFileProgressCallback&
51           file_progress_callback)
52       : operation_runner_(operation_runner),
53         operation_type_(operation_type),
54         src_url_(src_url),
55         dest_url_(dest_url),
56         option_(option),
57         file_progress_callback_(file_progress_callback) {
58   }
59
60   virtual void Run(
61       const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
62     if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_MOVE) {
63       operation_runner_->MoveFileLocal(src_url_, dest_url_, option_, callback);
64     } else {
65       operation_runner_->CopyFileLocal(
66           src_url_, dest_url_, option_, file_progress_callback_, callback);
67     }
68   }
69
70   virtual void Cancel() OVERRIDE {
71     // We can do nothing for the copy/move operation on a local file system.
72     // Assuming the operation is quickly done, it should be ok to just wait
73     // for the completion.
74   }
75
76  private:
77   FileSystemOperationRunner* operation_runner_;
78   CopyOrMoveOperationDelegate::OperationType operation_type_;
79   FileSystemURL src_url_;
80   FileSystemURL dest_url_;
81   CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
82   FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
83   DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOnSameFileSystemImpl);
84 };
85
86 // Specifically for cross file system copy/move operation, this class creates
87 // a snapshot file, validates it if necessary, runs copying process,
88 // validates the created file, and removes source file for move (noop for
89 // copy).
90 class SnapshotCopyOrMoveImpl
91     : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
92  public:
93   SnapshotCopyOrMoveImpl(
94       FileSystemOperationRunner* operation_runner,
95       CopyOrMoveOperationDelegate::OperationType operation_type,
96       const FileSystemURL& src_url,
97       const FileSystemURL& dest_url,
98       CopyOrMoveOperationDelegate::CopyOrMoveOption option,
99       CopyOrMoveFileValidatorFactory* validator_factory,
100       const FileSystemOperation::CopyFileProgressCallback&
101           file_progress_callback)
102       : operation_runner_(operation_runner),
103         operation_type_(operation_type),
104         src_url_(src_url),
105         dest_url_(dest_url),
106         option_(option),
107         validator_factory_(validator_factory),
108         file_progress_callback_(file_progress_callback),
109         cancel_requested_(false),
110         weak_factory_(this) {
111   }
112
113   virtual void Run(
114       const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
115     file_progress_callback_.Run(0);
116     operation_runner_->CreateSnapshotFile(
117         src_url_,
118         base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCreateSnapshot,
119                    weak_factory_.GetWeakPtr(), callback));
120   }
121
122   virtual void Cancel() OVERRIDE {
123     cancel_requested_ = true;
124   }
125
126  private:
127   void RunAfterCreateSnapshot(
128       const CopyOrMoveOperationDelegate::StatusCallback& callback,
129       base::PlatformFileError error,
130       const base::PlatformFileInfo& file_info,
131       const base::FilePath& platform_path,
132       const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) {
133     if (cancel_requested_)
134       error = base::PLATFORM_FILE_ERROR_ABORT;
135
136     if (error != base::PLATFORM_FILE_OK) {
137       callback.Run(error);
138       return;
139     }
140
141     // For now we assume CreateSnapshotFile always return a valid local file
142     // path.
143     DCHECK(!platform_path.empty());
144
145     if (!validator_factory_) {
146       // No validation is needed.
147       RunAfterPreWriteValidation(platform_path, file_info, file_ref, callback,
148                                  base::PLATFORM_FILE_OK);
149       return;
150     }
151
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));
158   }
159
160   void RunAfterPreWriteValidation(
161       const base::FilePath& platform_path,
162       const base::PlatformFileInfo& file_info,
163       const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref,
164       const CopyOrMoveOperationDelegate::StatusCallback& callback,
165       base::PlatformFileError error) {
166     if (cancel_requested_)
167       error = base::PLATFORM_FILE_ERROR_ABORT;
168
169     if (error != base::PLATFORM_FILE_OK) {
170       callback.Run(error);
171       return;
172     }
173
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));
180   }
181
182   void RunAfterCopyInForeignFile(
183       const base::PlatformFileInfo& file_info,
184       const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref,
185       const CopyOrMoveOperationDelegate::StatusCallback& callback,
186       base::PlatformFileError error) {
187     if (cancel_requested_)
188       error = base::PLATFORM_FILE_ERROR_ABORT;
189
190     if (error != base::PLATFORM_FILE_OK) {
191       callback.Run(error);
192       return;
193     }
194
195     file_progress_callback_.Run(file_info.size);
196
197     if (option_ == FileSystemOperation::OPTION_NONE) {
198       RunAfterTouchFile(callback, base::PLATFORM_FILE_OK);
199       return;
200     }
201
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));
207   }
208
209   void RunAfterTouchFile(
210       const CopyOrMoveOperationDelegate::StatusCallback& callback,
211       base::PlatformFileError error) {
212     // Even if TouchFile is failed, just ignore it.
213
214     if (cancel_requested_) {
215       callback.Run(base::PLATFORM_FILE_ERROR_ABORT);
216       return;
217     }
218
219     // |validator_| is NULL when the destination filesystem does not do
220     // validation.
221     if (!validator_) {
222       // No validation is needed.
223       RunAfterPostWriteValidation(callback, base::PLATFORM_FILE_OK);
224       return;
225     }
226
227     PostWriteValidation(
228         base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPostWriteValidation,
229                    weak_factory_.GetWeakPtr(), callback));
230   }
231
232   void RunAfterPostWriteValidation(
233       const CopyOrMoveOperationDelegate::StatusCallback& callback,
234       base::PlatformFileError error) {
235     if (cancel_requested_) {
236       callback.Run(base::PLATFORM_FILE_ERROR_ABORT);
237       return;
238     }
239
240     if (error != base::PLATFORM_FILE_OK) {
241       // Failed to validate. Remove the destination file.
242       operation_runner_->Remove(
243           dest_url_, true /* recursive */,
244           base::Bind(&SnapshotCopyOrMoveImpl::DidRemoveDestForError,
245                      weak_factory_.GetWeakPtr(), error, callback));
246       return;
247     }
248
249     if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
250       callback.Run(base::PLATFORM_FILE_OK);
251       return;
252     }
253
254     DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
255
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));
261   }
262
263   void RunAfterRemoveSourceForMove(
264       const CopyOrMoveOperationDelegate::StatusCallback& callback,
265       base::PlatformFileError error) {
266     if (cancel_requested_)
267       error = base::PLATFORM_FILE_ERROR_ABORT;
268
269     if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
270       error = base::PLATFORM_FILE_OK;
271     callback.Run(error);
272   }
273
274   void DidRemoveDestForError(
275       base::PlatformFileError prior_error,
276       const CopyOrMoveOperationDelegate::StatusCallback& callback,
277       base::PlatformFileError error) {
278     if (error != base::PLATFORM_FILE_OK) {
279       VLOG(1) << "Error removing destination file after validation error: "
280               << error;
281     }
282     callback.Run(prior_error);
283   }
284
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);
294   }
295
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));
304   }
305
306   void PostWriteValidationAfterCreateSnapshotFile(
307       const CopyOrMoveOperationDelegate::StatusCallback& callback,
308       base::PlatformFileError error,
309       const base::PlatformFileInfo& file_info,
310       const base::FilePath& platform_path,
311       const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) {
312     if (cancel_requested_)
313       error = base::PLATFORM_FILE_ERROR_ABORT;
314
315     if (error != base::PLATFORM_FILE_OK) {
316       callback.Run(error);
317       return;
318     }
319
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));
327   }
328
329   // |file_ref| is unused; it is passed here to make sure the reference is
330   // alive until after post-write validation is complete.
331   void DidPostWriteValidation(
332       const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref,
333       const CopyOrMoveOperationDelegate::StatusCallback& callback,
334       base::PlatformFileError error) {
335     callback.Run(error);
336   }
337
338   FileSystemOperationRunner* operation_runner_;
339   CopyOrMoveOperationDelegate::OperationType operation_type_;
340   FileSystemURL src_url_;
341   FileSystemURL dest_url_;
342
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);
350 };
351
352 // The size of buffer for StreamCopyHelper.
353 const int kReadBufferSize = 32768;
354
355 // To avoid too many progress callbacks, it should be called less
356 // frequently than 50ms.
357 const int kMinProgressCallbackInvocationSpanInMilliseconds = 50;
358
359 // Specifically for cross file system copy/move operation, this class uses
360 // stream reader and writer for copying. Validator is not supported, so if
361 // necessary SnapshotCopyOrMoveImpl should be used.
362 class StreamCopyOrMoveImpl
363     : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
364  public:
365   StreamCopyOrMoveImpl(
366       FileSystemOperationRunner* operation_runner,
367       CopyOrMoveOperationDelegate::OperationType operation_type,
368       const FileSystemURL& src_url,
369       const FileSystemURL& dest_url,
370       CopyOrMoveOperationDelegate::CopyOrMoveOption option,
371       scoped_ptr<webkit_blob::FileStreamReader> reader,
372       scoped_ptr<FileStreamWriter> writer,
373       const FileSystemOperation::CopyFileProgressCallback&
374           file_progress_callback)
375       : operation_runner_(operation_runner),
376         operation_type_(operation_type),
377         src_url_(src_url),
378         dest_url_(dest_url),
379         option_(option),
380         reader_(reader.Pass()),
381         writer_(writer.Pass()),
382         file_progress_callback_(file_progress_callback),
383         cancel_requested_(false),
384         weak_factory_(this) {
385   }
386
387   virtual void Run(
388       const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
389     // Reader can be created even if the entry does not exist or the entry is
390     // a directory. To check errors before destination file creation,
391     // check metadata first.
392     operation_runner_->GetMetadata(
393         src_url_,
394         base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource,
395                    weak_factory_.GetWeakPtr(), callback));
396   }
397
398   virtual void Cancel() OVERRIDE {
399     cancel_requested_ = true;
400     if (copy_helper_)
401       copy_helper_->Cancel();
402   }
403
404  private:
405   void RunAfterGetMetadataForSource(
406       const CopyOrMoveOperationDelegate::StatusCallback& callback,
407       base::PlatformFileError error,
408       const base::PlatformFileInfo& file_info) {
409     if (cancel_requested_)
410       error = base::PLATFORM_FILE_ERROR_ABORT;
411
412     if (error != base::PLATFORM_FILE_OK) {
413       callback.Run(error);
414       return;
415     }
416
417     if (file_info.is_directory) {
418       // If not a directory, failed with appropriate error code.
419       callback.Run(base::PLATFORM_FILE_ERROR_NOT_A_FILE);
420       return;
421     }
422
423     // To use FileStreamWriter, we need to ensure the destination file exists.
424     operation_runner_->CreateFile(
425         dest_url_, false /* exclusive */,
426         base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination,
427                    weak_factory_.GetWeakPtr(),
428                    callback, file_info.last_modified));
429   }
430
431   void RunAfterCreateFileForDestination(
432       const CopyOrMoveOperationDelegate::StatusCallback& callback,
433       const base::Time& last_modified,
434       base::PlatformFileError error) {
435     if (cancel_requested_)
436       error = base::PLATFORM_FILE_ERROR_ABORT;
437
438     if (error != base::PLATFORM_FILE_OK) {
439       callback.Run(error);
440       return;
441     }
442
443     DCHECK(!copy_helper_);
444     copy_helper_.reset(
445         new CopyOrMoveOperationDelegate::StreamCopyHelper(
446             reader_.Pass(), writer_.Pass(),
447             kReadBufferSize,
448             file_progress_callback_,
449             base::TimeDelta::FromMilliseconds(
450                 kMinProgressCallbackInvocationSpanInMilliseconds)));
451     copy_helper_->Run(
452         base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy,
453                    weak_factory_.GetWeakPtr(), callback, last_modified));
454   }
455
456   void RunAfterStreamCopy(
457       const CopyOrMoveOperationDelegate::StatusCallback& callback,
458       const base::Time& last_modified,
459       base::PlatformFileError error) {
460     if (cancel_requested_)
461       error = base::PLATFORM_FILE_ERROR_ABORT;
462
463     if (error != base::PLATFORM_FILE_OK) {
464       callback.Run(error);
465       return;
466     }
467
468     if (option_ == FileSystemOperation::OPTION_NONE) {
469       RunAfterTouchFile(callback, base::PLATFORM_FILE_OK);
470       return;
471     }
472
473     operation_runner_->TouchFile(
474         dest_url_, base::Time::Now() /* last_access */, last_modified,
475         base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile,
476                    weak_factory_.GetWeakPtr(), callback));
477   }
478
479   void RunAfterTouchFile(
480       const CopyOrMoveOperationDelegate::StatusCallback& callback,
481       base::PlatformFileError error) {
482     // Even if TouchFile is failed, just ignore it.
483     if (cancel_requested_) {
484       callback.Run(base::PLATFORM_FILE_ERROR_ABORT);
485       return;
486     }
487
488     if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
489       callback.Run(base::PLATFORM_FILE_OK);
490       return;
491     }
492
493     DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
494
495     // Remove the source for finalizing move operation.
496     operation_runner_->Remove(
497         src_url_, false /* recursive */,
498         base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove,
499                    weak_factory_.GetWeakPtr(), callback));
500   }
501
502   void RunAfterRemoveForMove(
503       const CopyOrMoveOperationDelegate::StatusCallback& callback,
504       base::PlatformFileError error) {
505     if (cancel_requested_)
506       error = base::PLATFORM_FILE_ERROR_ABORT;
507     if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
508       error = base::PLATFORM_FILE_OK;
509     callback.Run(error);
510   }
511
512   FileSystemOperationRunner* operation_runner_;
513   CopyOrMoveOperationDelegate::OperationType operation_type_;
514   FileSystemURL src_url_;
515   FileSystemURL dest_url_;
516   CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
517   scoped_ptr<webkit_blob::FileStreamReader> reader_;
518   scoped_ptr<FileStreamWriter> writer_;
519   FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
520   scoped_ptr<CopyOrMoveOperationDelegate::StreamCopyHelper> copy_helper_;
521   bool cancel_requested_;
522   base::WeakPtrFactory<StreamCopyOrMoveImpl> weak_factory_;
523   DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl);
524 };
525
526 }  // namespace
527
528 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper(
529     scoped_ptr<webkit_blob::FileStreamReader> reader,
530     scoped_ptr<FileStreamWriter> writer,
531     int buffer_size,
532     const FileSystemOperation::CopyFileProgressCallback&
533         file_progress_callback,
534     const base::TimeDelta& min_progress_callback_invocation_span)
535     : reader_(reader.Pass()),
536       writer_(writer.Pass()),
537       file_progress_callback_(file_progress_callback),
538       io_buffer_(new net::IOBufferWithSize(buffer_size)),
539       num_copied_bytes_(0),
540       min_progress_callback_invocation_span_(
541           min_progress_callback_invocation_span),
542       cancel_requested_(false),
543       weak_factory_(this) {
544 }
545
546 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() {
547 }
548
549 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run(
550     const StatusCallback& callback) {
551   file_progress_callback_.Run(0);
552   last_progress_callback_invocation_time_ = base::Time::Now();
553   Read(callback);
554 }
555
556 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() {
557   cancel_requested_ = true;
558 }
559
560 void CopyOrMoveOperationDelegate::StreamCopyHelper::Read(
561     const StatusCallback& callback) {
562   int result = reader_->Read(
563       io_buffer_.get(), io_buffer_->size(),
564       base::Bind(&StreamCopyHelper::DidRead,
565                  weak_factory_.GetWeakPtr(), callback));
566   if (result != net::ERR_IO_PENDING)
567     DidRead(callback, result);
568 }
569
570 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead(
571     const StatusCallback& callback, int result) {
572   if (cancel_requested_) {
573     callback.Run(base::PLATFORM_FILE_ERROR_ABORT);
574     return;
575   }
576
577   if (result < 0) {
578     callback.Run(NetErrorToPlatformFileError(result));
579     return;
580   }
581
582   if (result == 0) {
583     // Here is the EOF.
584     callback.Run(base::PLATFORM_FILE_OK);
585     return;
586   }
587
588   Write(callback, new net::DrainableIOBuffer(io_buffer_.get(), result));
589 }
590
591 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write(
592     const StatusCallback& callback,
593     scoped_refptr<net::DrainableIOBuffer> buffer) {
594   DCHECK_GT(buffer->BytesRemaining(), 0);
595
596   int result = writer_->Write(
597       buffer.get(), buffer->BytesRemaining(),
598       base::Bind(&StreamCopyHelper::DidWrite,
599                  weak_factory_.GetWeakPtr(), callback, buffer));
600   if (result != net::ERR_IO_PENDING)
601     DidWrite(callback, buffer, result);
602 }
603
604 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite(
605     const StatusCallback& callback,
606     scoped_refptr<net::DrainableIOBuffer> buffer,
607     int result) {
608   if (cancel_requested_) {
609     callback.Run(base::PLATFORM_FILE_ERROR_ABORT);
610     return;
611   }
612
613   if (result < 0) {
614     callback.Run(NetErrorToPlatformFileError(result));
615     return;
616   }
617
618   buffer->DidConsume(result);
619   num_copied_bytes_ += result;
620
621   // Check the elapsed time since last |file_progress_callback_| invocation.
622   base::Time now = base::Time::Now();
623   if (now - last_progress_callback_invocation_time_ >=
624       min_progress_callback_invocation_span_) {
625     file_progress_callback_.Run(num_copied_bytes_);
626     last_progress_callback_invocation_time_ = now;
627   }
628
629   if (buffer->BytesRemaining() > 0) {
630     Write(callback, buffer);
631     return;
632   }
633
634   Read(callback);
635 }
636
637 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate(
638     FileSystemContext* file_system_context,
639     const FileSystemURL& src_root,
640     const FileSystemURL& dest_root,
641     OperationType operation_type,
642     CopyOrMoveOption option,
643     const CopyProgressCallback& progress_callback,
644     const StatusCallback& callback)
645     : RecursiveOperationDelegate(file_system_context),
646       src_root_(src_root),
647       dest_root_(dest_root),
648       operation_type_(operation_type),
649       option_(option),
650       progress_callback_(progress_callback),
651       callback_(callback),
652       weak_factory_(this) {
653   same_file_system_ = src_root_.IsInSameFileSystem(dest_root_);
654 }
655
656 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
657   STLDeleteElements(&running_copy_set_);
658 }
659
660 void CopyOrMoveOperationDelegate::Run() {
661   // Not supported; this should never be called.
662   NOTREACHED();
663 }
664
665 void CopyOrMoveOperationDelegate::RunRecursively() {
666   // Perform light-weight checks first.
667
668   // It is an error to try to copy/move an entry into its child.
669   if (same_file_system_ && src_root_.path().IsParent(dest_root_.path())) {
670     callback_.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
671     return;
672   }
673
674   // It is an error to copy/move an entry into the same path.
675   if (same_file_system_ && src_root_.path() == dest_root_.path()) {
676     callback_.Run(base::PLATFORM_FILE_ERROR_EXISTS);
677     return;
678   }
679
680   // Start to process the source directory recursively.
681   // TODO(kinuko): This could be too expensive for same_file_system_==true
682   // and operation==MOVE case, probably we can just rename the root directory.
683   // http://crbug.com/172187
684   StartRecursiveOperation(src_root_, callback_);
685 }
686
687 void CopyOrMoveOperationDelegate::ProcessFile(
688     const FileSystemURL& src_url,
689     const StatusCallback& callback) {
690   if (!progress_callback_.is_null()) {
691     progress_callback_.Run(
692         FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
693   }
694
695   FileSystemURL dest_url = CreateDestURL(src_url);
696   CopyOrMoveImpl* impl = NULL;
697   if (same_file_system_) {
698     impl = new CopyOrMoveOnSameFileSystemImpl(
699         operation_runner(), operation_type_, src_url, dest_url, option_,
700         base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
701                    weak_factory_.GetWeakPtr(), src_url));
702   } else {
703     // Cross filesystem case.
704     base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
705     CopyOrMoveFileValidatorFactory* validator_factory =
706         file_system_context()->GetCopyOrMoveFileValidatorFactory(
707             dest_root_.type(), &error);
708     if (error != base::PLATFORM_FILE_OK) {
709       callback.Run(error);
710       return;
711     }
712
713     if (!validator_factory) {
714       scoped_ptr<webkit_blob::FileStreamReader> reader =
715           file_system_context()->CreateFileStreamReader(
716               src_url, 0, base::Time());
717       scoped_ptr<FileStreamWriter> writer =
718           file_system_context()->CreateFileStreamWriter(dest_url, 0);
719       if (reader && writer) {
720         impl = new StreamCopyOrMoveImpl(
721             operation_runner(), operation_type_, src_url, dest_url, option_,
722             reader.Pass(), writer.Pass(),
723             base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
724                        weak_factory_.GetWeakPtr(), src_url));
725       }
726     }
727
728     if (!impl) {
729       impl = new SnapshotCopyOrMoveImpl(
730           operation_runner(), operation_type_, src_url, dest_url, option_,
731           validator_factory,
732           base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
733                      weak_factory_.GetWeakPtr(), src_url));
734     }
735   }
736
737   // Register the running task.
738   running_copy_set_.insert(impl);
739   impl->Run(base::Bind(
740       &CopyOrMoveOperationDelegate::DidCopyOrMoveFile,
741       weak_factory_.GetWeakPtr(), src_url, dest_url, callback, impl));
742 }
743
744 void CopyOrMoveOperationDelegate::ProcessDirectory(
745     const FileSystemURL& src_url,
746     const StatusCallback& callback) {
747   if (src_url == src_root_) {
748     // The src_root_ looks to be a directory.
749     // Try removing the dest_root_ to see if it exists and/or it is an
750     // empty directory.
751     // We do not invoke |progress_callback_| for source root, because it is
752     // already called in ProcessFile().
753     operation_runner()->RemoveDirectory(
754         dest_root_,
755         base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot,
756                    weak_factory_.GetWeakPtr(), callback));
757     return;
758   }
759
760   if (!progress_callback_.is_null()) {
761     progress_callback_.Run(
762         FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
763   }
764
765   ProcessDirectoryInternal(src_url, CreateDestURL(src_url), callback);
766 }
767
768 void CopyOrMoveOperationDelegate::PostProcessDirectory(
769     const FileSystemURL& src_url,
770     const StatusCallback& callback) {
771   if (option_ == FileSystemOperation::OPTION_NONE) {
772     PostProcessDirectoryAfterTouchFile(
773         src_url, callback, base::PLATFORM_FILE_OK);
774     return;
775   }
776
777   operation_runner()->GetMetadata(
778       src_url,
779       base::Bind(
780           &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata,
781           weak_factory_.GetWeakPtr(), src_url, callback));
782 }
783
784 void CopyOrMoveOperationDelegate::OnCancel() {
785   // Request to cancel all running Copy/Move file.
786   for (std::set<CopyOrMoveImpl*>::iterator iter = running_copy_set_.begin();
787        iter != running_copy_set_.end(); ++iter)
788     (*iter)->Cancel();
789 }
790
791 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
792     const FileSystemURL& src_url,
793     const FileSystemURL& dest_url,
794     const StatusCallback& callback,
795     CopyOrMoveImpl* impl,
796     base::PlatformFileError error) {
797   running_copy_set_.erase(impl);
798   delete impl;
799
800   if (!progress_callback_.is_null() && error == base::PLATFORM_FILE_OK) {
801     progress_callback_.Run(
802         FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
803   }
804
805   callback.Run(error);
806 }
807
808 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
809     const StatusCallback& callback,
810     base::PlatformFileError error) {
811   if (error == base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY) {
812     callback_.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
813     return;
814   }
815   if (error != base::PLATFORM_FILE_OK &&
816       error != base::PLATFORM_FILE_ERROR_NOT_FOUND) {
817     callback_.Run(error);
818     return;
819   }
820
821   ProcessDirectoryInternal(src_root_, dest_root_, callback);
822 }
823
824 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal(
825     const FileSystemURL& src_url,
826     const FileSystemURL& dest_url,
827     const StatusCallback& callback) {
828   // If operation_type == Move we may need to record directories and
829   // restore directory timestamps in the end, though it may have
830   // negative performance impact.
831   // See http://crbug.com/171284 for more details.
832   operation_runner()->CreateDirectory(
833       dest_url, false /* exclusive */, false /* recursive */,
834       base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory,
835                  weak_factory_.GetWeakPtr(), src_url, dest_url, callback));
836 }
837
838 void CopyOrMoveOperationDelegate::DidCreateDirectory(
839     const FileSystemURL& src_url,
840     const FileSystemURL& dest_url,
841     const StatusCallback& callback,
842     base::PlatformFileError error) {
843   if (!progress_callback_.is_null() && error == base::PLATFORM_FILE_OK) {
844     progress_callback_.Run(
845         FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
846   }
847
848   callback.Run(error);
849 }
850
851 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata(
852     const FileSystemURL& src_url,
853     const StatusCallback& callback,
854     base::PlatformFileError error,
855     const base::PlatformFileInfo& file_info) {
856   if (error != base::PLATFORM_FILE_OK) {
857     // Ignore the error, and run post process which should run after TouchFile.
858     PostProcessDirectoryAfterTouchFile(
859         src_url, callback, base::PLATFORM_FILE_OK);
860     return;
861   }
862
863   operation_runner()->TouchFile(
864       CreateDestURL(src_url), base::Time::Now() /* last access */,
865       file_info.last_modified,
866       base::Bind(
867           &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile,
868           weak_factory_.GetWeakPtr(), src_url, callback));
869 }
870
871 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile(
872     const FileSystemURL& src_url,
873     const StatusCallback& callback,
874     base::PlatformFileError error) {
875   // Even if the TouchFile is failed, just ignore it.
876
877   if (operation_type_ == OPERATION_COPY) {
878     callback.Run(base::PLATFORM_FILE_OK);
879     return;
880   }
881
882   DCHECK_EQ(OPERATION_MOVE, operation_type_);
883
884   // All files and subdirectories in the directory should be moved here,
885   // so remove the source directory for finalizing move operation.
886   operation_runner()->Remove(
887       src_url, false /* recursive */,
888       base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove,
889                  weak_factory_.GetWeakPtr(), callback));
890 }
891
892 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
893     const StatusCallback& callback,
894     base::PlatformFileError error) {
895   if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
896     error = base::PLATFORM_FILE_OK;
897   callback.Run(error);
898 }
899
900 void CopyOrMoveOperationDelegate::OnCopyFileProgress(
901     const FileSystemURL& src_url, int64 size) {
902   if (!progress_callback_.is_null()) {
903     progress_callback_.Run(
904         FileSystemOperation::PROGRESS, src_url, FileSystemURL(), size);
905   }
906 }
907
908 FileSystemURL CopyOrMoveOperationDelegate::CreateDestURL(
909     const FileSystemURL& src_url) const {
910   DCHECK_EQ(src_root_.type(), src_url.type());
911   DCHECK_EQ(src_root_.origin(), src_url.origin());
912
913   base::FilePath relative = dest_root_.virtual_path();
914   src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(),
915                                               &relative);
916   return file_system_context()->CreateCrackedFileSystemURL(
917       dest_root_.origin(),
918       dest_root_.mount_type(),
919       relative);
920 }
921
922 }  // namespace fileapi