Home | History | Annotate | Download | only in fileapi
      1 // Copyright 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/file_system_operation_impl.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/single_thread_task_runner.h"
      9 #include "base/strings/utf_string_conversions.h"
     10 #include "base/time/time.h"
     11 #include "net/base/escape.h"
     12 #include "net/url_request/url_request.h"
     13 #include "webkit/browser/fileapi/async_file_util.h"
     14 #include "webkit/browser/fileapi/copy_or_move_operation_delegate.h"
     15 #include "webkit/browser/fileapi/file_observers.h"
     16 #include "webkit/browser/fileapi/file_system_backend.h"
     17 #include "webkit/browser/fileapi/file_system_context.h"
     18 #include "webkit/browser/fileapi/file_system_file_util.h"
     19 #include "webkit/browser/fileapi/file_system_operation_context.h"
     20 #include "webkit/browser/fileapi/file_system_url.h"
     21 #include "webkit/browser/fileapi/file_writer_delegate.h"
     22 #include "webkit/browser/fileapi/remove_operation_delegate.h"
     23 #include "webkit/browser/fileapi/sandbox_file_system_backend.h"
     24 #include "webkit/browser/quota/quota_manager.h"
     25 #include "webkit/common/blob/shareable_file_reference.h"
     26 #include "webkit/common/fileapi/file_system_types.h"
     27 #include "webkit/common/fileapi/file_system_util.h"
     28 #include "webkit/common/quota/quota_types.h"
     29 
     30 using webkit_blob::ScopedFile;
     31 
     32 namespace fileapi {
     33 
     34 FileSystemOperation* FileSystemOperation::Create(
     35     const FileSystemURL& url,
     36     FileSystemContext* file_system_context,
     37     scoped_ptr<FileSystemOperationContext> operation_context) {
     38   return new FileSystemOperationImpl(url, file_system_context,
     39                                      operation_context.Pass());
     40 }
     41 
     42 FileSystemOperationImpl::~FileSystemOperationImpl() {
     43 }
     44 
     45 void FileSystemOperationImpl::CreateFile(const FileSystemURL& url,
     46                                          bool exclusive,
     47                                          const StatusCallback& callback) {
     48   DCHECK(SetPendingOperationType(kOperationCreateFile));
     49   GetUsageAndQuotaThenRunTask(
     50       url,
     51       base::Bind(&FileSystemOperationImpl::DoCreateFile,
     52                  weak_factory_.GetWeakPtr(), url, callback, exclusive),
     53       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
     54 }
     55 
     56 void FileSystemOperationImpl::CreateDirectory(const FileSystemURL& url,
     57                                               bool exclusive,
     58                                               bool recursive,
     59                                               const StatusCallback& callback) {
     60   DCHECK(SetPendingOperationType(kOperationCreateDirectory));
     61   GetUsageAndQuotaThenRunTask(
     62       url,
     63       base::Bind(&FileSystemOperationImpl::DoCreateDirectory,
     64                  weak_factory_.GetWeakPtr(), url, callback,
     65                  exclusive, recursive),
     66       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
     67 }
     68 
     69 void FileSystemOperationImpl::Copy(
     70     const FileSystemURL& src_url,
     71     const FileSystemURL& dest_url,
     72     CopyOrMoveOption option,
     73     const CopyProgressCallback& progress_callback,
     74     const StatusCallback& callback) {
     75   DCHECK(SetPendingOperationType(kOperationCopy));
     76   DCHECK(!recursive_operation_delegate_);
     77 
     78   // TODO(hidehiko): Support |progress_callback|. (crbug.com/278038).
     79   recursive_operation_delegate_.reset(
     80       new CopyOrMoveOperationDelegate(
     81           file_system_context(),
     82           src_url, dest_url,
     83           CopyOrMoveOperationDelegate::OPERATION_COPY,
     84           option,
     85           progress_callback,
     86           base::Bind(&FileSystemOperationImpl::DidFinishOperation,
     87                      weak_factory_.GetWeakPtr(), callback)));
     88   recursive_operation_delegate_->RunRecursively();
     89 }
     90 
     91 void FileSystemOperationImpl::Move(const FileSystemURL& src_url,
     92                                    const FileSystemURL& dest_url,
     93                                    CopyOrMoveOption option,
     94                                    const StatusCallback& callback) {
     95   DCHECK(SetPendingOperationType(kOperationMove));
     96   DCHECK(!recursive_operation_delegate_);
     97   recursive_operation_delegate_.reset(
     98       new CopyOrMoveOperationDelegate(
     99           file_system_context(),
    100           src_url, dest_url,
    101           CopyOrMoveOperationDelegate::OPERATION_MOVE,
    102           option,
    103           FileSystemOperation::CopyProgressCallback(),
    104           base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    105                      weak_factory_.GetWeakPtr(), callback)));
    106   recursive_operation_delegate_->RunRecursively();
    107 }
    108 
    109 void FileSystemOperationImpl::DirectoryExists(const FileSystemURL& url,
    110                                               const StatusCallback& callback) {
    111   DCHECK(SetPendingOperationType(kOperationDirectoryExists));
    112   async_file_util_->GetFileInfo(
    113       operation_context_.Pass(), url,
    114       base::Bind(&FileSystemOperationImpl::DidDirectoryExists,
    115                  weak_factory_.GetWeakPtr(), callback));
    116 }
    117 
    118 void FileSystemOperationImpl::FileExists(const FileSystemURL& url,
    119                                          const StatusCallback& callback) {
    120   DCHECK(SetPendingOperationType(kOperationFileExists));
    121   async_file_util_->GetFileInfo(
    122       operation_context_.Pass(), url,
    123       base::Bind(&FileSystemOperationImpl::DidFileExists,
    124                  weak_factory_.GetWeakPtr(), callback));
    125 }
    126 
    127 void FileSystemOperationImpl::GetMetadata(
    128     const FileSystemURL& url, const GetMetadataCallback& callback) {
    129   DCHECK(SetPendingOperationType(kOperationGetMetadata));
    130   async_file_util_->GetFileInfo(operation_context_.Pass(), url, callback);
    131 }
    132 
    133 void FileSystemOperationImpl::ReadDirectory(
    134     const FileSystemURL& url, const ReadDirectoryCallback& callback) {
    135   DCHECK(SetPendingOperationType(kOperationReadDirectory));
    136   async_file_util_->ReadDirectory(
    137       operation_context_.Pass(), url, callback);
    138 }
    139 
    140 void FileSystemOperationImpl::Remove(const FileSystemURL& url,
    141                                      bool recursive,
    142                                      const StatusCallback& callback) {
    143   DCHECK(SetPendingOperationType(kOperationRemove));
    144   DCHECK(!recursive_operation_delegate_);
    145 
    146   if (recursive) {
    147     // For recursive removal, try to delegate the operation to AsyncFileUtil
    148     // first. If not supported, it is delegated to RemoveOperationDelegate
    149     // in DidDeleteRecursively.
    150     async_file_util_->DeleteRecursively(
    151         operation_context_.Pass(), url,
    152         base::Bind(&FileSystemOperationImpl::DidDeleteRecursively,
    153                    weak_factory_.GetWeakPtr(), url, callback));
    154     return;
    155   }
    156 
    157   recursive_operation_delegate_.reset(
    158       new RemoveOperationDelegate(
    159           file_system_context(), url,
    160           base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    161                      weak_factory_.GetWeakPtr(), callback)));
    162   recursive_operation_delegate_->Run();
    163 }
    164 
    165 void FileSystemOperationImpl::Write(
    166     const FileSystemURL& url,
    167     scoped_ptr<FileWriterDelegate> writer_delegate,
    168     scoped_ptr<net::URLRequest> blob_request,
    169     const WriteCallback& callback) {
    170   DCHECK(SetPendingOperationType(kOperationWrite));
    171   file_writer_delegate_ = writer_delegate.Pass();
    172   file_writer_delegate_->Start(
    173       blob_request.Pass(),
    174       base::Bind(&FileSystemOperationImpl::DidWrite,
    175                  weak_factory_.GetWeakPtr(), url, callback));
    176 }
    177 
    178 void FileSystemOperationImpl::Truncate(const FileSystemURL& url, int64 length,
    179                                        const StatusCallback& callback) {
    180   DCHECK(SetPendingOperationType(kOperationTruncate));
    181   GetUsageAndQuotaThenRunTask(
    182       url,
    183       base::Bind(&FileSystemOperationImpl::DoTruncate,
    184                  weak_factory_.GetWeakPtr(), url, callback, length),
    185       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
    186 }
    187 
    188 void FileSystemOperationImpl::TouchFile(const FileSystemURL& url,
    189                                         const base::Time& last_access_time,
    190                                         const base::Time& last_modified_time,
    191                                         const StatusCallback& callback) {
    192   DCHECK(SetPendingOperationType(kOperationTouchFile));
    193   async_file_util_->Touch(
    194       operation_context_.Pass(), url,
    195       last_access_time, last_modified_time,
    196       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    197                  weak_factory_.GetWeakPtr(), callback));
    198 }
    199 
    200 void FileSystemOperationImpl::OpenFile(const FileSystemURL& url,
    201                                        int file_flags,
    202                                        const OpenFileCallback& callback) {
    203   DCHECK(SetPendingOperationType(kOperationOpenFile));
    204 
    205   if (file_flags &
    206       (base::PLATFORM_FILE_TEMPORARY | base::PLATFORM_FILE_HIDDEN)) {
    207     callback.Run(base::PLATFORM_FILE_ERROR_FAILED,
    208                  base::kInvalidPlatformFileValue,
    209                  base::Closure());
    210     return;
    211   }
    212   GetUsageAndQuotaThenRunTask(
    213       url,
    214       base::Bind(&FileSystemOperationImpl::DoOpenFile,
    215                  weak_factory_.GetWeakPtr(),
    216                  url, callback, file_flags),
    217       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED,
    218                  base::kInvalidPlatformFileValue,
    219                  base::Closure()));
    220 }
    221 
    222 // We can only get here on a write or truncate that's not yet completed.
    223 // We don't support cancelling any other operation at this time.
    224 void FileSystemOperationImpl::Cancel(const StatusCallback& cancel_callback) {
    225   DCHECK(cancel_callback_.is_null());
    226   cancel_callback_ = cancel_callback;
    227 
    228   if (file_writer_delegate_.get()) {
    229     DCHECK_EQ(kOperationWrite, pending_operation_);
    230     // This will call DidWrite() with ABORT status code.
    231     file_writer_delegate_->Cancel();
    232   } else if (recursive_operation_delegate_) {
    233     // This will call DidFinishOperation() with ABORT status code.
    234     recursive_operation_delegate_->Cancel();
    235   } else {
    236     // For truncate we have no way to cancel the inflight operation (for now).
    237     // Let it just run and dispatch cancel callback later.
    238     DCHECK_EQ(kOperationTruncate, pending_operation_);
    239   }
    240 }
    241 
    242 void FileSystemOperationImpl::CreateSnapshotFile(
    243     const FileSystemURL& url,
    244     const SnapshotFileCallback& callback) {
    245   DCHECK(SetPendingOperationType(kOperationCreateSnapshotFile));
    246   async_file_util_->CreateSnapshotFile(
    247       operation_context_.Pass(), url, callback);
    248 }
    249 
    250 void FileSystemOperationImpl::CopyInForeignFile(
    251     const base::FilePath& src_local_disk_file_path,
    252     const FileSystemURL& dest_url,
    253     const StatusCallback& callback) {
    254   DCHECK(SetPendingOperationType(kOperationCopyInForeignFile));
    255   GetUsageAndQuotaThenRunTask(
    256       dest_url,
    257       base::Bind(&FileSystemOperationImpl::DoCopyInForeignFile,
    258                  weak_factory_.GetWeakPtr(), src_local_disk_file_path, dest_url,
    259                  callback),
    260       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
    261 }
    262 
    263 void FileSystemOperationImpl::RemoveFile(
    264     const FileSystemURL& url,
    265     const StatusCallback& callback) {
    266   DCHECK(SetPendingOperationType(kOperationRemove));
    267   async_file_util_->DeleteFile(
    268       operation_context_.Pass(), url,
    269       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    270                  weak_factory_.GetWeakPtr(), callback));
    271 }
    272 
    273 void FileSystemOperationImpl::RemoveDirectory(
    274     const FileSystemURL& url,
    275     const StatusCallback& callback) {
    276   DCHECK(SetPendingOperationType(kOperationRemove));
    277   async_file_util_->DeleteDirectory(
    278       operation_context_.Pass(), url,
    279       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    280                  weak_factory_.GetWeakPtr(), callback));
    281 }
    282 
    283 void FileSystemOperationImpl::CopyFileLocal(
    284     const FileSystemURL& src_url,
    285     const FileSystemURL& dest_url,
    286     CopyOrMoveOption option,
    287     const CopyFileProgressCallback& progress_callback,
    288     const StatusCallback& callback) {
    289   DCHECK(SetPendingOperationType(kOperationCopy));
    290   DCHECK(src_url.IsInSameFileSystem(dest_url));
    291 
    292   GetUsageAndQuotaThenRunTask(
    293       dest_url,
    294       base::Bind(&FileSystemOperationImpl::DoCopyFileLocal,
    295                  weak_factory_.GetWeakPtr(), src_url, dest_url, option,
    296                  progress_callback, callback),
    297       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
    298 }
    299 
    300 void FileSystemOperationImpl::MoveFileLocal(
    301     const FileSystemURL& src_url,
    302     const FileSystemURL& dest_url,
    303     CopyOrMoveOption option,
    304     const StatusCallback& callback) {
    305   DCHECK(SetPendingOperationType(kOperationMove));
    306   DCHECK(src_url.IsInSameFileSystem(dest_url));
    307   GetUsageAndQuotaThenRunTask(
    308       dest_url,
    309       base::Bind(&FileSystemOperationImpl::DoMoveFileLocal,
    310                  weak_factory_.GetWeakPtr(),
    311                  src_url, dest_url, option, callback),
    312       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
    313 }
    314 
    315 base::PlatformFileError FileSystemOperationImpl::SyncGetPlatformPath(
    316     const FileSystemURL& url,
    317     base::FilePath* platform_path) {
    318   DCHECK(SetPendingOperationType(kOperationGetLocalPath));
    319   if (!file_system_context()->IsSandboxFileSystem(url.type()))
    320     return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
    321   FileSystemFileUtil* file_util =
    322       file_system_context()->sandbox_delegate()->sync_file_util();
    323   file_util->GetLocalFilePath(operation_context_.get(), url, platform_path);
    324   return base::PLATFORM_FILE_OK;
    325 }
    326 
    327 FileSystemOperationImpl::FileSystemOperationImpl(
    328     const FileSystemURL& url,
    329     FileSystemContext* file_system_context,
    330     scoped_ptr<FileSystemOperationContext> operation_context)
    331     : file_system_context_(file_system_context),
    332       operation_context_(operation_context.Pass()),
    333       async_file_util_(NULL),
    334       pending_operation_(kOperationNone),
    335       weak_factory_(this) {
    336   DCHECK(operation_context_.get());
    337   operation_context_->DetachUserDataThread();
    338   async_file_util_ = file_system_context_->GetAsyncFileUtil(url.type());
    339   DCHECK(async_file_util_);
    340 }
    341 
    342 void FileSystemOperationImpl::GetUsageAndQuotaThenRunTask(
    343     const FileSystemURL& url,
    344     const base::Closure& task,
    345     const base::Closure& error_callback) {
    346   quota::QuotaManagerProxy* quota_manager_proxy =
    347       file_system_context()->quota_manager_proxy();
    348   if (!quota_manager_proxy ||
    349       !file_system_context()->GetQuotaUtil(url.type())) {
    350     // If we don't have the quota manager or the requested filesystem type
    351     // does not support quota, we should be able to let it go.
    352     operation_context_->set_allowed_bytes_growth(kint64max);
    353     task.Run();
    354     return;
    355   }
    356 
    357   DCHECK(quota_manager_proxy);
    358   DCHECK(quota_manager_proxy->quota_manager());
    359   quota_manager_proxy->quota_manager()->GetUsageAndQuota(
    360       url.origin(),
    361       FileSystemTypeToQuotaStorageType(url.type()),
    362       base::Bind(&FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask,
    363                  weak_factory_.GetWeakPtr(), task, error_callback));
    364 }
    365 
    366 void FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask(
    367     const base::Closure& task,
    368     const base::Closure& error_callback,
    369     quota::QuotaStatusCode status,
    370     int64 usage, int64 quota) {
    371   if (status != quota::kQuotaStatusOk) {
    372     LOG(WARNING) << "Got unexpected quota error : " << status;
    373     error_callback.Run();
    374     return;
    375   }
    376 
    377   operation_context_->set_allowed_bytes_growth(quota - usage);
    378   task.Run();
    379 }
    380 
    381 void FileSystemOperationImpl::DoCreateFile(
    382     const FileSystemURL& url,
    383     const StatusCallback& callback,
    384     bool exclusive) {
    385   async_file_util_->EnsureFileExists(
    386       operation_context_.Pass(), url,
    387       base::Bind(
    388           exclusive ?
    389               &FileSystemOperationImpl::DidEnsureFileExistsExclusive :
    390               &FileSystemOperationImpl::DidEnsureFileExistsNonExclusive,
    391           weak_factory_.GetWeakPtr(), callback));
    392 }
    393 
    394 void FileSystemOperationImpl::DoCreateDirectory(
    395     const FileSystemURL& url,
    396     const StatusCallback& callback,
    397     bool exclusive, bool recursive) {
    398   async_file_util_->CreateDirectory(
    399       operation_context_.Pass(),
    400       url, exclusive, recursive,
    401       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    402                  weak_factory_.GetWeakPtr(), callback));
    403 }
    404 
    405 void FileSystemOperationImpl::DoCopyFileLocal(
    406     const FileSystemURL& src_url,
    407     const FileSystemURL& dest_url,
    408     CopyOrMoveOption option,
    409     const CopyFileProgressCallback& progress_callback,
    410     const StatusCallback& callback) {
    411   async_file_util_->CopyFileLocal(
    412       operation_context_.Pass(), src_url, dest_url, option, progress_callback,
    413       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    414                  weak_factory_.GetWeakPtr(), callback));
    415 }
    416 
    417 void FileSystemOperationImpl::DoMoveFileLocal(
    418     const FileSystemURL& src_url,
    419     const FileSystemURL& dest_url,
    420     CopyOrMoveOption option,
    421     const StatusCallback& callback) {
    422   async_file_util_->MoveFileLocal(
    423       operation_context_.Pass(), src_url, dest_url, option,
    424       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    425                  weak_factory_.GetWeakPtr(), callback));
    426 }
    427 
    428 void FileSystemOperationImpl::DoCopyInForeignFile(
    429     const base::FilePath& src_local_disk_file_path,
    430     const FileSystemURL& dest_url,
    431     const StatusCallback& callback) {
    432   async_file_util_->CopyInForeignFile(
    433       operation_context_.Pass(),
    434       src_local_disk_file_path, dest_url,
    435       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    436                  weak_factory_.GetWeakPtr(), callback));
    437 }
    438 
    439 void FileSystemOperationImpl::DoTruncate(const FileSystemURL& url,
    440                                          const StatusCallback& callback,
    441                                          int64 length) {
    442   async_file_util_->Truncate(
    443       operation_context_.Pass(), url, length,
    444       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    445                  weak_factory_.GetWeakPtr(), callback));
    446 }
    447 
    448 void FileSystemOperationImpl::DoOpenFile(const FileSystemURL& url,
    449                                          const OpenFileCallback& callback,
    450                                          int file_flags) {
    451   async_file_util_->CreateOrOpen(
    452       operation_context_.Pass(), url, file_flags,
    453       base::Bind(&FileSystemOperationImpl::DidOpenFile,
    454                  weak_factory_.GetWeakPtr(), callback));
    455 }
    456 
    457 void FileSystemOperationImpl::DidEnsureFileExistsExclusive(
    458     const StatusCallback& callback,
    459     base::PlatformFileError rv, bool created) {
    460   if (rv == base::PLATFORM_FILE_OK && !created) {
    461     callback.Run(base::PLATFORM_FILE_ERROR_EXISTS);
    462   } else {
    463     DidFinishOperation(callback, rv);
    464   }
    465 }
    466 
    467 void FileSystemOperationImpl::DidEnsureFileExistsNonExclusive(
    468     const StatusCallback& callback,
    469     base::PlatformFileError rv, bool /* created */) {
    470   DidFinishOperation(callback, rv);
    471 }
    472 
    473 void FileSystemOperationImpl::DidFinishOperation(
    474     const StatusCallback& callback,
    475     base::PlatformFileError rv) {
    476   if (!cancel_callback_.is_null()) {
    477     StatusCallback cancel_callback = cancel_callback_;
    478     callback.Run(rv);
    479 
    480     // Return OK only if we succeeded to stop the operation.
    481     cancel_callback.Run(rv == base::PLATFORM_FILE_ERROR_ABORT ?
    482                         base::PLATFORM_FILE_OK :
    483                         base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
    484   } else {
    485     callback.Run(rv);
    486   }
    487 }
    488 
    489 void FileSystemOperationImpl::DidDirectoryExists(
    490     const StatusCallback& callback,
    491     base::PlatformFileError rv,
    492     const base::PlatformFileInfo& file_info) {
    493   if (rv == base::PLATFORM_FILE_OK && !file_info.is_directory)
    494     rv = base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
    495   callback.Run(rv);
    496 }
    497 
    498 void FileSystemOperationImpl::DidFileExists(
    499     const StatusCallback& callback,
    500     base::PlatformFileError rv,
    501     const base::PlatformFileInfo& file_info) {
    502   if (rv == base::PLATFORM_FILE_OK && file_info.is_directory)
    503     rv = base::PLATFORM_FILE_ERROR_NOT_A_FILE;
    504   callback.Run(rv);
    505 }
    506 
    507 void FileSystemOperationImpl::DidDeleteRecursively(
    508     const FileSystemURL& url,
    509     const StatusCallback& callback,
    510     base::PlatformFileError rv) {
    511   if (rv == base::PLATFORM_FILE_ERROR_INVALID_OPERATION) {
    512     // Recursive removal is not supported on this platform.
    513     DCHECK(!recursive_operation_delegate_);
    514     recursive_operation_delegate_.reset(
    515         new RemoveOperationDelegate(
    516             file_system_context(), url,
    517             base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    518                        weak_factory_.GetWeakPtr(), callback)));
    519     recursive_operation_delegate_->RunRecursively();
    520     return;
    521   }
    522 
    523   callback.Run(rv);
    524 }
    525 
    526 void FileSystemOperationImpl::DidWrite(
    527     const FileSystemURL& url,
    528     const WriteCallback& write_callback,
    529     base::PlatformFileError rv,
    530     int64 bytes,
    531     FileWriterDelegate::WriteProgressStatus write_status) {
    532   const bool complete = (
    533       write_status != FileWriterDelegate::SUCCESS_IO_PENDING);
    534   if (complete && write_status != FileWriterDelegate::ERROR_WRITE_NOT_STARTED) {
    535     DCHECK(operation_context_);
    536     operation_context_->change_observers()->Notify(
    537         &FileChangeObserver::OnModifyFile, MakeTuple(url));
    538   }
    539 
    540   StatusCallback cancel_callback = cancel_callback_;
    541   write_callback.Run(rv, bytes, complete);
    542   if (!cancel_callback.is_null())
    543     cancel_callback.Run(base::PLATFORM_FILE_OK);
    544 }
    545 
    546 void FileSystemOperationImpl::DidOpenFile(
    547     const OpenFileCallback& callback,
    548     base::PlatformFileError rv,
    549     base::PassPlatformFile file,
    550     const base::Closure& on_close_callback) {
    551   callback.Run(rv, file.ReleaseValue(), on_close_callback);
    552 }
    553 
    554 bool FileSystemOperationImpl::SetPendingOperationType(OperationType type) {
    555   if (pending_operation_ != kOperationNone)
    556     return false;
    557   pending_operation_ = type;
    558   return true;
    559 }
    560 
    561 }  // namespace fileapi
    562