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/recursive_operation_delegate.h"
      6 
      7 #include "base/bind.h"
      8 #include "webkit/browser/fileapi/file_system_context.h"
      9 #include "webkit/browser/fileapi/file_system_operation_runner.h"
     10 
     11 namespace fileapi {
     12 
     13 namespace {
     14 // Don't start too many inflight operations.
     15 const int kMaxInflightOperations = 5;
     16 }
     17 
     18 RecursiveOperationDelegate::RecursiveOperationDelegate(
     19     FileSystemContext* file_system_context)
     20     : file_system_context_(file_system_context),
     21       inflight_operations_(0),
     22       canceled_(false) {
     23 }
     24 
     25 RecursiveOperationDelegate::~RecursiveOperationDelegate() {
     26 }
     27 
     28 void RecursiveOperationDelegate::Cancel() {
     29   canceled_ = true;
     30   OnCancel();
     31 }
     32 
     33 void RecursiveOperationDelegate::StartRecursiveOperation(
     34     const FileSystemURL& root,
     35     const StatusCallback& callback) {
     36   DCHECK(pending_directory_stack_.empty());
     37   DCHECK(pending_files_.empty());
     38   DCHECK_EQ(0, inflight_operations_);
     39 
     40   callback_ = callback;
     41   ++inflight_operations_;
     42   ProcessFile(
     43       root,
     44       base::Bind(&RecursiveOperationDelegate::DidTryProcessFile,
     45                  AsWeakPtr(), root));
     46 }
     47 
     48 FileSystemOperationRunner* RecursiveOperationDelegate::operation_runner() {
     49   return file_system_context_->operation_runner();
     50 }
     51 
     52 void RecursiveOperationDelegate::OnCancel() {
     53 }
     54 
     55 void RecursiveOperationDelegate::DidTryProcessFile(
     56     const FileSystemURL& root,
     57     base::PlatformFileError error) {
     58   DCHECK(pending_directory_stack_.empty());
     59   DCHECK(pending_files_.empty());
     60   DCHECK_EQ(1, inflight_operations_);
     61 
     62   --inflight_operations_;
     63   if (canceled_ || error != base::PLATFORM_FILE_ERROR_NOT_A_FILE) {
     64     Done(error);
     65     return;
     66   }
     67 
     68   pending_directory_stack_.push(std::queue<FileSystemURL>());
     69   pending_directory_stack_.top().push(root);
     70   ProcessNextDirectory();
     71 }
     72 
     73 void RecursiveOperationDelegate::ProcessNextDirectory() {
     74   DCHECK(pending_files_.empty());
     75   DCHECK(!pending_directory_stack_.empty());
     76   DCHECK(!pending_directory_stack_.top().empty());
     77   DCHECK_EQ(0, inflight_operations_);
     78 
     79   const FileSystemURL& url = pending_directory_stack_.top().front();
     80 
     81   ++inflight_operations_;
     82   ProcessDirectory(
     83       url,
     84       base::Bind(
     85           &RecursiveOperationDelegate::DidProcessDirectory, AsWeakPtr()));
     86 }
     87 
     88 void RecursiveOperationDelegate::DidProcessDirectory(
     89     base::PlatformFileError error) {
     90   DCHECK(pending_files_.empty());
     91   DCHECK(!pending_directory_stack_.empty());
     92   DCHECK(!pending_directory_stack_.top().empty());
     93   DCHECK_EQ(1, inflight_operations_);
     94 
     95   --inflight_operations_;
     96   if (canceled_ || error != base::PLATFORM_FILE_OK) {
     97     Done(error);
     98     return;
     99   }
    100 
    101   const FileSystemURL& parent = pending_directory_stack_.top().front();
    102   pending_directory_stack_.push(std::queue<FileSystemURL>());
    103   operation_runner()->ReadDirectory(
    104       parent,
    105       base::Bind(&RecursiveOperationDelegate::DidReadDirectory,
    106                  AsWeakPtr(), parent));
    107 }
    108 
    109 void RecursiveOperationDelegate::DidReadDirectory(
    110     const FileSystemURL& parent,
    111     base::PlatformFileError error,
    112     const FileEntryList& entries,
    113     bool has_more) {
    114   DCHECK(pending_files_.empty());
    115   DCHECK(!pending_directory_stack_.empty());
    116   DCHECK_EQ(0, inflight_operations_);
    117 
    118   if (canceled_ || error != base::PLATFORM_FILE_OK) {
    119     Done(error);
    120     return;
    121   }
    122 
    123   for (size_t i = 0; i < entries.size(); i++) {
    124     FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
    125         parent.origin(),
    126         parent.mount_type(),
    127         parent.virtual_path().Append(entries[i].name));
    128     if (entries[i].is_directory)
    129       pending_directory_stack_.top().push(url);
    130     else
    131       pending_files_.push(url);
    132   }
    133 
    134   // Wait for next entries.
    135   if (has_more)
    136     return;
    137 
    138   ProcessPendingFiles();
    139 }
    140 
    141 void RecursiveOperationDelegate::ProcessPendingFiles() {
    142   DCHECK(!pending_directory_stack_.empty());
    143 
    144   if ((pending_files_.empty() || canceled_) && inflight_operations_ == 0) {
    145     ProcessSubDirectory();
    146     return;
    147   }
    148 
    149   // Do not post any new tasks.
    150   if (canceled_)
    151     return;
    152 
    153   // Run ProcessFile in parallel (upto kMaxInflightOperations).
    154   scoped_refptr<base::MessageLoopProxy> current_message_loop =
    155       base::MessageLoopProxy::current();
    156   while (!pending_files_.empty() &&
    157          inflight_operations_ < kMaxInflightOperations) {
    158     ++inflight_operations_;
    159     current_message_loop->PostTask(
    160         FROM_HERE,
    161         base::Bind(&RecursiveOperationDelegate::ProcessFile,
    162                    AsWeakPtr(), pending_files_.front(),
    163                    base::Bind(&RecursiveOperationDelegate::DidProcessFile,
    164                               AsWeakPtr())));
    165     pending_files_.pop();
    166   }
    167 }
    168 
    169 void RecursiveOperationDelegate::DidProcessFile(
    170     base::PlatformFileError error) {
    171   --inflight_operations_;
    172   if (error != base::PLATFORM_FILE_OK) {
    173     // If an error occurs, invoke Done immediately (even if there remain
    174     // running operations). It is because in the callback, this instance is
    175     // deleted.
    176     Done(error);
    177     return;
    178   }
    179 
    180   ProcessPendingFiles();
    181 }
    182 
    183 void RecursiveOperationDelegate::ProcessSubDirectory() {
    184   DCHECK(pending_files_.empty());
    185   DCHECK(!pending_directory_stack_.empty());
    186   DCHECK_EQ(0, inflight_operations_);
    187 
    188   if (canceled_) {
    189     Done(base::PLATFORM_FILE_ERROR_ABORT);
    190     return;
    191   }
    192 
    193   if (!pending_directory_stack_.top().empty()) {
    194     // There remain some sub directories. Process them first.
    195     ProcessNextDirectory();
    196     return;
    197   }
    198 
    199   // All subdirectories are processed.
    200   pending_directory_stack_.pop();
    201   if (pending_directory_stack_.empty()) {
    202     // All files/directories are processed.
    203     Done(base::PLATFORM_FILE_OK);
    204     return;
    205   }
    206 
    207   DCHECK(!pending_directory_stack_.top().empty());
    208   ++inflight_operations_;
    209   PostProcessDirectory(
    210       pending_directory_stack_.top().front(),
    211       base::Bind(&RecursiveOperationDelegate::DidPostProcessDirectory,
    212                  AsWeakPtr()));
    213 }
    214 
    215 void RecursiveOperationDelegate::DidPostProcessDirectory(
    216     base::PlatformFileError error) {
    217   DCHECK(pending_files_.empty());
    218   DCHECK(!pending_directory_stack_.empty());
    219   DCHECK(!pending_directory_stack_.top().empty());
    220   DCHECK_EQ(1, inflight_operations_);
    221 
    222   --inflight_operations_;
    223   pending_directory_stack_.top().pop();
    224   if (canceled_ || error != base::PLATFORM_FILE_OK) {
    225     Done(error);
    226     return;
    227   }
    228 
    229   ProcessSubDirectory();
    230 }
    231 
    232 void RecursiveOperationDelegate::Done(base::PlatformFileError error) {
    233   if (canceled_ && error == base::PLATFORM_FILE_OK) {
    234     callback_.Run(base::PLATFORM_FILE_ERROR_ABORT);
    235   } else {
    236     callback_.Run(error);
    237   }
    238 }
    239 
    240 }  // namespace fileapi
    241