Home | History | Annotate | Download | only in fileapi
      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 const int64 kFlushIntervalInBytes = 10 << 20;  // 10MB.
     24 
     25 class CopyOrMoveOperationDelegate::CopyOrMoveImpl {
     26  public:
     27   virtual ~CopyOrMoveImpl() {}
     28   virtual void Run(
     29       const CopyOrMoveOperationDelegate::StatusCallback& callback) = 0;
     30   virtual void Cancel() = 0;
     31 
     32  protected:
     33   CopyOrMoveImpl() {}
     34 
     35  private:
     36   DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl);
     37 };
     38 
     39 namespace {
     40 
     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) {
     60   }
     61 
     62   virtual 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);
     69     }
     70   }
     71 
     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.
     76   }
     77 
     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);
     86 };
     87 
     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) {
    113   }
    114 
    115   virtual 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));
    122   }
    123 
    124   virtual void Cancel() OVERRIDE {
    125     cancel_requested_ = true;
    126   }
    127 
    128  private:
    129   void RunAfterCreateSnapshot(
    130       const CopyOrMoveOperationDelegate::StatusCallback& callback,
    131       base::PlatformFileError error,
    132       const base::PlatformFileInfo& file_info,
    133       const base::FilePath& platform_path,
    134       const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) {
    135     if (cancel_requested_)
    136       error = base::PLATFORM_FILE_ERROR_ABORT;
    137 
    138     if (error != base::PLATFORM_FILE_OK) {
    139       callback.Run(error);
    140       return;
    141     }
    142 
    143     // For now we assume CreateSnapshotFile always return a valid local file
    144     // path.
    145     DCHECK(!platform_path.empty());
    146 
    147     if (!validator_factory_) {
    148       // No validation is needed.
    149       RunAfterPreWriteValidation(platform_path, file_info, file_ref, callback,
    150                                  base::PLATFORM_FILE_OK);
    151       return;
    152     }
    153 
    154     // Run pre write validation.
    155     PreWriteValidation(
    156         platform_path,
    157         base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPreWriteValidation,
    158                    weak_factory_.GetWeakPtr(),
    159                    platform_path, file_info, file_ref, callback));
    160   }
    161 
    162   void RunAfterPreWriteValidation(
    163       const base::FilePath& platform_path,
    164       const base::PlatformFileInfo& file_info,
    165       const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref,
    166       const CopyOrMoveOperationDelegate::StatusCallback& callback,
    167       base::PlatformFileError error) {
    168     if (cancel_requested_)
    169       error = base::PLATFORM_FILE_ERROR_ABORT;
    170 
    171     if (error != base::PLATFORM_FILE_OK) {
    172       callback.Run(error);
    173       return;
    174     }
    175 
    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));
    182   }
    183 
    184   void RunAfterCopyInForeignFile(
    185       const base::PlatformFileInfo& file_info,
    186       const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref,
    187       const CopyOrMoveOperationDelegate::StatusCallback& callback,
    188       base::PlatformFileError error) {
    189     if (cancel_requested_)
    190       error = base::PLATFORM_FILE_ERROR_ABORT;
    191 
    192     if (error != base::PLATFORM_FILE_OK) {
    193       callback.Run(error);
    194       return;
    195     }
    196 
    197     file_progress_callback_.Run(file_info.size);
    198 
    199     if (option_ == FileSystemOperation::OPTION_NONE) {
    200       RunAfterTouchFile(callback, base::PLATFORM_FILE_OK);
    201       return;
    202     }
    203 
    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));
    209   }
    210 
    211   void RunAfterTouchFile(
    212       const CopyOrMoveOperationDelegate::StatusCallback& callback,
    213       base::PlatformFileError error) {
    214     // Even if TouchFile is failed, just ignore it.
    215 
    216     if (cancel_requested_) {
    217       callback.Run(base::PLATFORM_FILE_ERROR_ABORT);
    218       return;
    219     }
    220 
    221     // |validator_| is NULL when the destination filesystem does not do
    222     // validation.
    223     if (!validator_) {
    224       // No validation is needed.
    225       RunAfterPostWriteValidation(callback, base::PLATFORM_FILE_OK);
    226       return;
    227     }
    228 
    229     PostWriteValidation(
    230         base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPostWriteValidation,
    231                    weak_factory_.GetWeakPtr(), callback));
    232   }
    233 
    234   void RunAfterPostWriteValidation(
    235       const CopyOrMoveOperationDelegate::StatusCallback& callback,
    236       base::PlatformFileError error) {
    237     if (cancel_requested_) {
    238       callback.Run(base::PLATFORM_FILE_ERROR_ABORT);
    239       return;
    240     }
    241 
    242     if (error != base::PLATFORM_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));
    248       return;
    249     }
    250 
    251     if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
    252       callback.Run(base::PLATFORM_FILE_OK);
    253       return;
    254     }
    255 
    256     DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
    257 
    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));
    263   }
    264 
    265   void RunAfterRemoveSourceForMove(
    266       const CopyOrMoveOperationDelegate::StatusCallback& callback,
    267       base::PlatformFileError error) {
    268     if (cancel_requested_)
    269       error = base::PLATFORM_FILE_ERROR_ABORT;
    270 
    271     if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
    272       error = base::PLATFORM_FILE_OK;
    273     callback.Run(error);
    274   }
    275 
    276   void DidRemoveDestForError(
    277       base::PlatformFileError prior_error,
    278       const CopyOrMoveOperationDelegate::StatusCallback& callback,
    279       base::PlatformFileError error) {
    280     if (error != base::PLATFORM_FILE_OK) {
    281       VLOG(1) << "Error removing destination file after validation error: "
    282               << error;
    283     }
    284     callback.Run(prior_error);
    285   }
    286 
    287   // Runs pre-write validation.
    288   void PreWriteValidation(
    289       const base::FilePath& platform_path,
    290       const CopyOrMoveOperationDelegate::StatusCallback& callback) {
    291     DCHECK(validator_factory_);
    292     validator_.reset(
    293         validator_factory_->CreateCopyOrMoveFileValidator(
    294             src_url_, platform_path));
    295     validator_->StartPreWriteValidation(callback);
    296   }
    297 
    298   // Runs post-write validation.
    299   void PostWriteValidation(
    300       const CopyOrMoveOperationDelegate::StatusCallback& callback) {
    301     operation_runner_->CreateSnapshotFile(
    302         dest_url_,
    303         base::Bind(
    304             &SnapshotCopyOrMoveImpl::PostWriteValidationAfterCreateSnapshotFile,
    305             weak_factory_.GetWeakPtr(), callback));
    306   }
    307 
    308   void PostWriteValidationAfterCreateSnapshotFile(
    309       const CopyOrMoveOperationDelegate::StatusCallback& callback,
    310       base::PlatformFileError error,
    311       const base::PlatformFileInfo& file_info,
    312       const base::FilePath& platform_path,
    313       const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) {
    314     if (cancel_requested_)
    315       error = base::PLATFORM_FILE_ERROR_ABORT;
    316 
    317     if (error != base::PLATFORM_FILE_OK) {
    318       callback.Run(error);
    319       return;
    320     }
    321 
    322     DCHECK(validator_);
    323     // Note: file_ref passed here to keep the file alive until after
    324     // the StartPostWriteValidation operation finishes.
    325     validator_->StartPostWriteValidation(
    326         platform_path,
    327         base::Bind(&SnapshotCopyOrMoveImpl::DidPostWriteValidation,
    328                    weak_factory_.GetWeakPtr(), file_ref, callback));
    329   }
    330 
    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::PlatformFileError error) {
    337     callback.Run(error);
    338   }
    339 
    340   FileSystemOperationRunner* operation_runner_;
    341   CopyOrMoveOperationDelegate::OperationType operation_type_;
    342   FileSystemURL src_url_;
    343   FileSystemURL dest_url_;
    344 
    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);
    352 };
    353 
    354 // The size of buffer for StreamCopyHelper.
    355 const int kReadBufferSize = 32768;
    356 
    357 // To avoid too many progress callbacks, it should be called less
    358 // frequently than 50ms.
    359 const int kMinProgressCallbackInvocationSpanInMilliseconds = 50;
    360 
    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 {
    366  public:
    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),
    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) {
    387   }
    388 
    389   virtual void Run(
    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(
    395         src_url_,
    396         base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource,
    397                    weak_factory_.GetWeakPtr(), callback));
    398   }
    399 
    400   virtual void Cancel() OVERRIDE {
    401     cancel_requested_ = true;
    402     if (copy_helper_)
    403       copy_helper_->Cancel();
    404   }
    405 
    406  private:
    407   void RunAfterGetMetadataForSource(
    408       const CopyOrMoveOperationDelegate::StatusCallback& callback,
    409       base::PlatformFileError error,
    410       const base::PlatformFileInfo& file_info) {
    411     if (cancel_requested_)
    412       error = base::PLATFORM_FILE_ERROR_ABORT;
    413 
    414     if (error != base::PLATFORM_FILE_OK) {
    415       callback.Run(error);
    416       return;
    417     }
    418 
    419     if (file_info.is_directory) {
    420       // If not a directory, failed with appropriate error code.
    421       callback.Run(base::PLATFORM_FILE_ERROR_NOT_A_FILE);
    422       return;
    423     }
    424 
    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));
    431   }
    432 
    433   void RunAfterCreateFileForDestination(
    434       const CopyOrMoveOperationDelegate::StatusCallback& callback,
    435       const base::Time& last_modified,
    436       base::PlatformFileError error) {
    437     if (cancel_requested_)
    438       error = base::PLATFORM_FILE_ERROR_ABORT;
    439 
    440     if (error != base::PLATFORM_FILE_OK) {
    441       callback.Run(error);
    442       return;
    443     }
    444 
    445     const bool need_flush = dest_url_.mount_option().copy_sync_option() ==
    446         fileapi::COPY_SYNC_OPTION_SYNC;
    447 
    448     DCHECK(!copy_helper_);
    449     copy_helper_.reset(
    450         new CopyOrMoveOperationDelegate::StreamCopyHelper(
    451             reader_.Pass(), writer_.Pass(),
    452             need_flush,
    453             kReadBufferSize,
    454             file_progress_callback_,
    455             base::TimeDelta::FromMilliseconds(
    456                 kMinProgressCallbackInvocationSpanInMilliseconds)));
    457     copy_helper_->Run(
    458         base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy,
    459                    weak_factory_.GetWeakPtr(), callback, last_modified));
    460   }
    461 
    462   void RunAfterStreamCopy(
    463       const CopyOrMoveOperationDelegate::StatusCallback& callback,
    464       const base::Time& last_modified,
    465       base::PlatformFileError error) {
    466     if (cancel_requested_)
    467       error = base::PLATFORM_FILE_ERROR_ABORT;
    468 
    469     if (error != base::PLATFORM_FILE_OK) {
    470       callback.Run(error);
    471       return;
    472     }
    473 
    474     if (option_ == FileSystemOperation::OPTION_NONE) {
    475       RunAfterTouchFile(callback, base::PLATFORM_FILE_OK);
    476       return;
    477     }
    478 
    479     operation_runner_->TouchFile(
    480         dest_url_, base::Time::Now() /* last_access */, last_modified,
    481         base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile,
    482                    weak_factory_.GetWeakPtr(), callback));
    483   }
    484 
    485   void RunAfterTouchFile(
    486       const CopyOrMoveOperationDelegate::StatusCallback& callback,
    487       base::PlatformFileError error) {
    488     // Even if TouchFile is failed, just ignore it.
    489     if (cancel_requested_) {
    490       callback.Run(base::PLATFORM_FILE_ERROR_ABORT);
    491       return;
    492     }
    493 
    494     if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
    495       callback.Run(base::PLATFORM_FILE_OK);
    496       return;
    497     }
    498 
    499     DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
    500 
    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));
    506   }
    507 
    508   void RunAfterRemoveForMove(
    509       const CopyOrMoveOperationDelegate::StatusCallback& callback,
    510       base::PlatformFileError error) {
    511     if (cancel_requested_)
    512       error = base::PLATFORM_FILE_ERROR_ABORT;
    513     if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
    514       error = base::PLATFORM_FILE_OK;
    515     callback.Run(error);
    516   }
    517 
    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);
    530 };
    531 
    532 }  // namespace
    533 
    534 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper(
    535     scoped_ptr<webkit_blob::FileStreamReader> reader,
    536     scoped_ptr<FileStreamWriter> writer,
    537     bool need_flush,
    538     int buffer_size,
    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) {
    553 }
    554 
    555 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() {
    556 }
    557 
    558 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run(
    559     const StatusCallback& callback) {
    560   file_progress_callback_.Run(0);
    561   last_progress_callback_invocation_time_ = base::Time::Now();
    562   Read(callback);
    563 }
    564 
    565 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() {
    566   cancel_requested_ = true;
    567 }
    568 
    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);
    577 }
    578 
    579 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead(
    580     const StatusCallback& callback, int result) {
    581   if (cancel_requested_) {
    582     callback.Run(base::PLATFORM_FILE_ERROR_ABORT);
    583     return;
    584   }
    585 
    586   if (result < 0) {
    587     callback.Run(NetErrorToPlatformFileError(result));
    588     return;
    589   }
    590 
    591   if (result == 0) {
    592     // Here is the EOF.
    593     if (need_flush_)
    594       Flush(callback, true /* is_eof */);
    595     else
    596       callback.Run(base::PLATFORM_FILE_OK);
    597     return;
    598   }
    599 
    600   Write(callback, new net::DrainableIOBuffer(io_buffer_.get(), result));
    601 }
    602 
    603 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write(
    604     const StatusCallback& callback,
    605     scoped_refptr<net::DrainableIOBuffer> buffer) {
    606   DCHECK_GT(buffer->BytesRemaining(), 0);
    607 
    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);
    614 }
    615 
    616 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite(
    617     const StatusCallback& callback,
    618     scoped_refptr<net::DrainableIOBuffer> buffer,
    619     int result) {
    620   if (cancel_requested_) {
    621     callback.Run(base::PLATFORM_FILE_ERROR_ABORT);
    622     return;
    623   }
    624 
    625   if (result < 0) {
    626     callback.Run(NetErrorToPlatformFileError(result));
    627     return;
    628   }
    629 
    630   buffer->DidConsume(result);
    631   num_copied_bytes_ += result;
    632 
    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;
    639   }
    640 
    641   if (buffer->BytesRemaining() > 0) {
    642     Write(callback, buffer);
    643     return;
    644   }
    645 
    646   if (need_flush_ &&
    647       (num_copied_bytes_ - previous_flush_offset_) > kFlushIntervalInBytes) {
    648     Flush(callback, false /* not is_eof */);
    649   } else {
    650     Read(callback);
    651   }
    652 }
    653 
    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);
    661 }
    662 
    663 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidFlush(
    664     const StatusCallback& callback, bool is_eof, int result) {
    665   if (cancel_requested_) {
    666     callback.Run(base::PLATFORM_FILE_ERROR_ABORT);
    667     return;
    668   }
    669 
    670   previous_flush_offset_ = num_copied_bytes_;
    671   if (is_eof)
    672     callback.Run(NetErrorToPlatformFileError(result));
    673   else
    674     Read(callback);
    675 }
    676 
    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),
    686       src_root_(src_root),
    687       dest_root_(dest_root),
    688       operation_type_(operation_type),
    689       option_(option),
    690       progress_callback_(progress_callback),
    691       callback_(callback),
    692       weak_factory_(this) {
    693   same_file_system_ = src_root_.IsInSameFileSystem(dest_root_);
    694 }
    695 
    696 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
    697   STLDeleteElements(&running_copy_set_);
    698 }
    699 
    700 void CopyOrMoveOperationDelegate::Run() {
    701   // Not supported; this should never be called.
    702   NOTREACHED();
    703 }
    704 
    705 void CopyOrMoveOperationDelegate::RunRecursively() {
    706   // Perform light-weight checks first.
    707 
    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::PLATFORM_FILE_ERROR_INVALID_OPERATION);
    711     return;
    712   }
    713 
    714   // It is an error to copy/move an entry into the same path.
    715   if (same_file_system_ && src_root_.path() == dest_root_.path()) {
    716     callback_.Run(base::PLATFORM_FILE_ERROR_EXISTS);
    717     return;
    718   }
    719 
    720   // Start to process the source directory recursively.
    721   // TODO(kinuko): This could be too expensive for same_file_system_==true
    722   // and operation==MOVE case, probably we can just rename the root directory.
    723   // http://crbug.com/172187
    724   StartRecursiveOperation(src_root_, callback_);
    725 }
    726 
    727 void CopyOrMoveOperationDelegate::ProcessFile(
    728     const FileSystemURL& src_url,
    729     const StatusCallback& callback) {
    730   if (!progress_callback_.is_null()) {
    731     progress_callback_.Run(
    732         FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
    733   }
    734 
    735   FileSystemURL dest_url = CreateDestURL(src_url);
    736   CopyOrMoveImpl* impl = NULL;
    737   if (same_file_system_) {
    738     impl = new CopyOrMoveOnSameFileSystemImpl(
    739         operation_runner(), operation_type_, src_url, dest_url, option_,
    740         base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
    741                    weak_factory_.GetWeakPtr(), src_url));
    742   } else {
    743     // Cross filesystem case.
    744     base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
    745     CopyOrMoveFileValidatorFactory* validator_factory =
    746         file_system_context()->GetCopyOrMoveFileValidatorFactory(
    747             dest_root_.type(), &error);
    748     if (error != base::PLATFORM_FILE_OK) {
    749       callback.Run(error);
    750       return;
    751     }
    752 
    753     if (!validator_factory) {
    754       scoped_ptr<webkit_blob::FileStreamReader> reader =
    755           file_system_context()->CreateFileStreamReader(
    756               src_url, 0, base::Time());
    757       scoped_ptr<FileStreamWriter> writer =
    758           file_system_context()->CreateFileStreamWriter(dest_url, 0);
    759       if (reader && writer) {
    760         impl = new StreamCopyOrMoveImpl(
    761             operation_runner(), operation_type_, src_url, dest_url, option_,
    762             reader.Pass(), writer.Pass(),
    763             base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
    764                        weak_factory_.GetWeakPtr(), src_url));
    765       }
    766     }
    767 
    768     if (!impl) {
    769       impl = new SnapshotCopyOrMoveImpl(
    770           operation_runner(), operation_type_, src_url, dest_url, option_,
    771           validator_factory,
    772           base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
    773                      weak_factory_.GetWeakPtr(), src_url));
    774     }
    775   }
    776 
    777   // Register the running task.
    778   running_copy_set_.insert(impl);
    779   impl->Run(base::Bind(
    780       &CopyOrMoveOperationDelegate::DidCopyOrMoveFile,
    781       weak_factory_.GetWeakPtr(), src_url, dest_url, callback, impl));
    782 }
    783 
    784 void CopyOrMoveOperationDelegate::ProcessDirectory(
    785     const FileSystemURL& src_url,
    786     const StatusCallback& callback) {
    787   if (src_url == src_root_) {
    788     // The src_root_ looks to be a directory.
    789     // Try removing the dest_root_ to see if it exists and/or it is an
    790     // empty directory.
    791     // We do not invoke |progress_callback_| for source root, because it is
    792     // already called in ProcessFile().
    793     operation_runner()->RemoveDirectory(
    794         dest_root_,
    795         base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot,
    796                    weak_factory_.GetWeakPtr(), callback));
    797     return;
    798   }
    799 
    800   if (!progress_callback_.is_null()) {
    801     progress_callback_.Run(
    802         FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
    803   }
    804 
    805   ProcessDirectoryInternal(src_url, CreateDestURL(src_url), callback);
    806 }
    807 
    808 void CopyOrMoveOperationDelegate::PostProcessDirectory(
    809     const FileSystemURL& src_url,
    810     const StatusCallback& callback) {
    811   if (option_ == FileSystemOperation::OPTION_NONE) {
    812     PostProcessDirectoryAfterTouchFile(
    813         src_url, callback, base::PLATFORM_FILE_OK);
    814     return;
    815   }
    816 
    817   operation_runner()->GetMetadata(
    818       src_url,
    819       base::Bind(
    820           &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata,
    821           weak_factory_.GetWeakPtr(), src_url, callback));
    822 }
    823 
    824 void CopyOrMoveOperationDelegate::OnCancel() {
    825   // Request to cancel all running Copy/Move file.
    826   for (std::set<CopyOrMoveImpl*>::iterator iter = running_copy_set_.begin();
    827        iter != running_copy_set_.end(); ++iter)
    828     (*iter)->Cancel();
    829 }
    830 
    831 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
    832     const FileSystemURL& src_url,
    833     const FileSystemURL& dest_url,
    834     const StatusCallback& callback,
    835     CopyOrMoveImpl* impl,
    836     base::PlatformFileError error) {
    837   running_copy_set_.erase(impl);
    838   delete impl;
    839 
    840   if (!progress_callback_.is_null() && error == base::PLATFORM_FILE_OK) {
    841     progress_callback_.Run(
    842         FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
    843   }
    844 
    845   callback.Run(error);
    846 }
    847 
    848 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
    849     const StatusCallback& callback,
    850     base::PlatformFileError error) {
    851   if (error == base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY) {
    852     callback_.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
    853     return;
    854   }
    855   if (error != base::PLATFORM_FILE_OK &&
    856       error != base::PLATFORM_FILE_ERROR_NOT_FOUND) {
    857     callback_.Run(error);
    858     return;
    859   }
    860 
    861   ProcessDirectoryInternal(src_root_, dest_root_, callback);
    862 }
    863 
    864 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal(
    865     const FileSystemURL& src_url,
    866     const FileSystemURL& dest_url,
    867     const StatusCallback& callback) {
    868   // If operation_type == Move we may need to record directories and
    869   // restore directory timestamps in the end, though it may have
    870   // negative performance impact.
    871   // See http://crbug.com/171284 for more details.
    872   operation_runner()->CreateDirectory(
    873       dest_url, false /* exclusive */, false /* recursive */,
    874       base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory,
    875                  weak_factory_.GetWeakPtr(), src_url, dest_url, callback));
    876 }
    877 
    878 void CopyOrMoveOperationDelegate::DidCreateDirectory(
    879     const FileSystemURL& src_url,
    880     const FileSystemURL& dest_url,
    881     const StatusCallback& callback,
    882     base::PlatformFileError error) {
    883   if (!progress_callback_.is_null() && error == base::PLATFORM_FILE_OK) {
    884     progress_callback_.Run(
    885         FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
    886   }
    887 
    888   callback.Run(error);
    889 }
    890 
    891 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata(
    892     const FileSystemURL& src_url,
    893     const StatusCallback& callback,
    894     base::PlatformFileError error,
    895     const base::PlatformFileInfo& file_info) {
    896   if (error != base::PLATFORM_FILE_OK) {
    897     // Ignore the error, and run post process which should run after TouchFile.
    898     PostProcessDirectoryAfterTouchFile(
    899         src_url, callback, base::PLATFORM_FILE_OK);
    900     return;
    901   }
    902 
    903   operation_runner()->TouchFile(
    904       CreateDestURL(src_url), base::Time::Now() /* last access */,
    905       file_info.last_modified,
    906       base::Bind(
    907           &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile,
    908           weak_factory_.GetWeakPtr(), src_url, callback));
    909 }
    910 
    911 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile(
    912     const FileSystemURL& src_url,
    913     const StatusCallback& callback,
    914     base::PlatformFileError error) {
    915   // Even if the TouchFile is failed, just ignore it.
    916 
    917   if (operation_type_ == OPERATION_COPY) {
    918     callback.Run(base::PLATFORM_FILE_OK);
    919     return;
    920   }
    921 
    922   DCHECK_EQ(OPERATION_MOVE, operation_type_);
    923 
    924   // All files and subdirectories in the directory should be moved here,
    925   // so remove the source directory for finalizing move operation.
    926   operation_runner()->Remove(
    927       src_url, false /* recursive */,
    928       base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove,
    929                  weak_factory_.GetWeakPtr(), callback));
    930 }
    931 
    932 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
    933     const StatusCallback& callback,
    934     base::PlatformFileError error) {
    935   if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
    936     error = base::PLATFORM_FILE_OK;
    937   callback.Run(error);
    938 }
    939 
    940 void CopyOrMoveOperationDelegate::OnCopyFileProgress(
    941     const FileSystemURL& src_url, int64 size) {
    942   if (!progress_callback_.is_null()) {
    943     progress_callback_.Run(
    944         FileSystemOperation::PROGRESS, src_url, FileSystemURL(), size);
    945   }
    946 }
    947 
    948 FileSystemURL CopyOrMoveOperationDelegate::CreateDestURL(
    949     const FileSystemURL& src_url) const {
    950   DCHECK_EQ(src_root_.type(), src_url.type());
    951   DCHECK_EQ(src_root_.origin(), src_url.origin());
    952 
    953   base::FilePath relative = dest_root_.virtual_path();
    954   src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(),
    955                                               &relative);
    956   return file_system_context()->CreateCrackedFileSystemURL(
    957       dest_root_.origin(),
    958       dest_root_.mount_type(),
    959       relative);
    960 }
    961 
    962 }  // namespace fileapi
    963