Home | History | Annotate | Download | only in pepper
      1 // Copyright (c) 2012 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 "content/renderer/pepper/quota_file_io.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/bind.h"
     10 #include "base/location.h"
     11 #include "base/memory/weak_ptr.h"
     12 #include "base/message_loop/message_loop_proxy.h"
     13 #include "base/stl_util.h"
     14 #include "base/task_runner_util.h"
     15 #include "content/renderer/pepper/host_globals.h"
     16 
     17 using base::PlatformFile;
     18 using base::PlatformFileError;
     19 using quota::StorageType;
     20 
     21 namespace content {
     22 
     23 namespace {
     24 StorageType PPFileSystemTypeToQuotaStorageType(PP_FileSystemType type) {
     25   switch (type) {
     26     case PP_FILESYSTEMTYPE_LOCALPERSISTENT:
     27       return quota::kStorageTypePersistent;
     28     case PP_FILESYSTEMTYPE_LOCALTEMPORARY:
     29       return quota::kStorageTypeTemporary;
     30     default:
     31       return quota::kStorageTypeUnknown;
     32   }
     33   NOTREACHED();
     34   return quota::kStorageTypeUnknown;
     35 }
     36 
     37 int WriteAdapter(PlatformFile file, int64 offset,
     38                  scoped_ptr<char[]> data, int size) {
     39   return base::WritePlatformFile(file, offset, data.get(), size);
     40 }
     41 
     42 }  // namespace
     43 
     44 class QuotaFileIO::PendingOperationBase {
     45  public:
     46   virtual ~PendingOperationBase() {}
     47 
     48   // Either one of Run() or DidFail() is called (the latter is called when
     49   // there was more than one error during quota queries).
     50   virtual void Run() = 0;
     51   virtual void DidFail(PlatformFileError error) = 0;
     52 
     53  protected:
     54   PendingOperationBase(QuotaFileIO* quota_io, bool is_will_operation)
     55       : quota_io_(quota_io), is_will_operation_(is_will_operation) {
     56     DCHECK(quota_io_);
     57     quota_io_->WillUpdate();
     58   }
     59 
     60   QuotaFileIO* quota_io_;
     61   const bool is_will_operation_;
     62 };
     63 
     64 class QuotaFileIO::WriteOperation : public PendingOperationBase {
     65  public:
     66   WriteOperation(QuotaFileIO* quota_io,
     67                  bool is_will_operation,
     68                  int64_t offset,
     69                  const char* buffer,
     70                  int32_t bytes_to_write,
     71                  const WriteCallback& callback)
     72       : PendingOperationBase(quota_io, is_will_operation),
     73         offset_(offset),
     74         bytes_to_write_(bytes_to_write),
     75         callback_(callback),
     76         finished_(false),
     77         status_(base::PLATFORM_FILE_OK),
     78         bytes_written_(0),
     79         weak_factory_(this) {
     80     if (!is_will_operation) {
     81       // TODO(kinuko): Check the API convention if we really need to keep a copy
     82       // of the buffer during the async write operations.
     83       buffer_.reset(new char[bytes_to_write]);
     84       memcpy(buffer_.get(), buffer, bytes_to_write);
     85     }
     86   }
     87   virtual ~WriteOperation() {}
     88   virtual void Run() OVERRIDE {
     89     DCHECK(quota_io_);
     90     if (quota_io_->CheckIfExceedsQuota(offset_ + bytes_to_write_)) {
     91       DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE);
     92       return;
     93     }
     94     if (is_will_operation_) {
     95       // Assuming the write will succeed.
     96       DidFinish(base::PLATFORM_FILE_OK, bytes_to_write_);
     97       return;
     98     }
     99     DCHECK(buffer_.get());
    100 
    101     if (!base::PostTaskAndReplyWithResult(
    102             quota_io_->delegate()->GetFileThreadMessageLoopProxy().get(),
    103             FROM_HERE,
    104             base::Bind(&WriteAdapter,
    105                        quota_io_->file_,
    106                        offset_,
    107                        base::Passed(&buffer_),
    108                        bytes_to_write_),
    109             base::Bind(&WriteOperation::DidWrite,
    110                        weak_factory_.GetWeakPtr()))) {
    111       DidFail(base::PLATFORM_FILE_ERROR_FAILED);
    112       return;
    113     }
    114   }
    115 
    116   virtual void DidFail(PlatformFileError error) OVERRIDE {
    117     DidFinish(error, 0);
    118   }
    119 
    120   bool finished() const { return finished_; }
    121 
    122   virtual void WillRunCallback() {
    123     base::MessageLoopProxy::current()->PostTask(
    124         FROM_HERE,
    125         base::Bind(&WriteOperation::RunCallback, weak_factory_.GetWeakPtr()));
    126   }
    127 
    128  private:
    129   void DidWrite(int bytes_written) {
    130     base::PlatformFileError error = bytes_written > 0 ?
    131         base::PLATFORM_FILE_OK : base::PLATFORM_FILE_ERROR_FAILED;
    132     DidFinish(error, bytes_written);
    133   }
    134 
    135   void DidFinish(PlatformFileError status, int bytes_written) {
    136     finished_ = true;
    137     status_ = status;
    138     bytes_written_ = bytes_written;
    139     int64_t max_offset =
    140         (status != base::PLATFORM_FILE_OK) ? 0 : offset_ + bytes_written;
    141     // This may delete itself by calling RunCallback.
    142     quota_io_->DidWrite(this, max_offset);
    143   }
    144 
    145   virtual void RunCallback() {
    146     DCHECK_EQ(false, callback_.is_null());
    147     callback_.Run(status_, bytes_written_);
    148     delete this;
    149   }
    150 
    151   const int64_t offset_;
    152   scoped_ptr<char[]> buffer_;
    153   const int32_t bytes_to_write_;
    154   WriteCallback callback_;
    155   bool finished_;
    156   PlatformFileError status_;
    157   int64_t bytes_written_;
    158   base::WeakPtrFactory<WriteOperation> weak_factory_;
    159 };
    160 
    161 class QuotaFileIO::SetLengthOperation : public PendingOperationBase {
    162  public:
    163   SetLengthOperation(QuotaFileIO* quota_io,
    164                      bool is_will_operation,
    165                      int64_t length,
    166                      const StatusCallback& callback)
    167       : PendingOperationBase(quota_io, is_will_operation),
    168         length_(length),
    169         callback_(callback),
    170         weak_factory_(this) {}
    171 
    172   virtual ~SetLengthOperation() {}
    173 
    174   virtual void Run() OVERRIDE {
    175     DCHECK(quota_io_);
    176     if (quota_io_->CheckIfExceedsQuota(length_)) {
    177       DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE);
    178       return;
    179     }
    180     if (is_will_operation_) {
    181       DidFinish(base::PLATFORM_FILE_OK);
    182       return;
    183     }
    184 
    185     if (!base::FileUtilProxy::Truncate(
    186             quota_io_->delegate()->GetFileThreadMessageLoopProxy().get(),
    187             quota_io_->file_,
    188             length_,
    189             base::Bind(&SetLengthOperation::DidFinish,
    190                        weak_factory_.GetWeakPtr()))) {
    191       DidFail(base::PLATFORM_FILE_ERROR_FAILED);
    192       return;
    193     }
    194   }
    195 
    196   virtual void DidFail(PlatformFileError error) OVERRIDE {
    197     DidFinish(error);
    198   }
    199 
    200  private:
    201   void DidFinish(PlatformFileError status) {
    202     quota_io_->DidSetLength(status, length_);
    203     DCHECK_EQ(false, callback_.is_null());
    204     callback_.Run(status);
    205     delete this;
    206   }
    207 
    208   int64_t length_;
    209   StatusCallback callback_;
    210   base::WeakPtrFactory<SetLengthOperation> weak_factory_;
    211 };
    212 
    213 // QuotaFileIO --------------------------------------------------------------
    214 
    215 QuotaFileIO::QuotaFileIO(
    216     Delegate* delegate,
    217     PlatformFile file,
    218     const GURL& file_url,
    219     PP_FileSystemType type)
    220     : delegate_(delegate),
    221       file_(file),
    222       file_url_(file_url),
    223       storage_type_(PPFileSystemTypeToQuotaStorageType(type)),
    224       cached_file_size_(0),
    225       cached_available_space_(0),
    226       outstanding_quota_queries_(0),
    227       outstanding_errors_(0),
    228       max_written_offset_(0),
    229       inflight_operations_(0),
    230       weak_factory_(this) {
    231   DCHECK_NE(base::kInvalidPlatformFileValue, file_);
    232   DCHECK_NE(quota::kStorageTypeUnknown, storage_type_);
    233 }
    234 
    235 QuotaFileIO::~QuotaFileIO() {
    236   // Note that this doesn't dispatch pending callbacks.
    237   STLDeleteContainerPointers(pending_operations_.begin(),
    238                              pending_operations_.end());
    239   STLDeleteContainerPointers(pending_callbacks_.begin(),
    240                              pending_callbacks_.end());
    241 }
    242 
    243 bool QuotaFileIO::Write(
    244     int64_t offset, const char* buffer, int32_t bytes_to_write,
    245     const WriteCallback& callback) {
    246   if (bytes_to_write <= 0)
    247     return false;
    248 
    249   WriteOperation* op = new WriteOperation(
    250       this, false, offset, buffer, bytes_to_write, callback);
    251   return RegisterOperationForQuotaChecks(op);
    252 }
    253 
    254 bool QuotaFileIO::SetLength(int64_t length, const StatusCallback& callback) {
    255   DCHECK(pending_operations_.empty());
    256   SetLengthOperation* op = new SetLengthOperation(
    257       this, false, length, callback);
    258   return RegisterOperationForQuotaChecks(op);
    259 }
    260 
    261 bool QuotaFileIO::WillWrite(
    262     int64_t offset, int32_t bytes_to_write, const WriteCallback& callback) {
    263   WriteOperation* op = new WriteOperation(
    264       this, true, offset, NULL, bytes_to_write, callback);
    265   return RegisterOperationForQuotaChecks(op);
    266 }
    267 
    268 bool QuotaFileIO::WillSetLength(int64_t length,
    269                                 const StatusCallback& callback) {
    270   DCHECK(pending_operations_.empty());
    271   SetLengthOperation* op = new SetLengthOperation(this, true, length, callback);
    272   return RegisterOperationForQuotaChecks(op);
    273 }
    274 
    275 bool QuotaFileIO::RegisterOperationForQuotaChecks(
    276     PendingOperationBase* op_ptr) {
    277   scoped_ptr<PendingOperationBase> op(op_ptr);
    278   if (pending_operations_.empty()) {
    279     // This is the first pending quota check. Run querying the file size
    280     // and available space.
    281     outstanding_quota_queries_ = 0;
    282     outstanding_errors_ = 0;
    283 
    284     // Query the file size.
    285     ++outstanding_quota_queries_;
    286     if (!base::FileUtilProxy::GetFileInfoFromPlatformFile(
    287             delegate_->GetFileThreadMessageLoopProxy().get(),
    288             file_,
    289             base::Bind(&QuotaFileIO::DidQueryInfoForQuota,
    290                        weak_factory_.GetWeakPtr()))) {
    291       // This makes the call fail synchronously; we do not fire the callback
    292       // here but just delete the operation and return false.
    293       return false;
    294     }
    295 
    296     // Query the current available space.
    297     ++outstanding_quota_queries_;
    298     delegate_->QueryAvailableSpace(
    299         file_url_.GetOrigin(), storage_type_,
    300         base::Bind(&QuotaFileIO::DidQueryAvailableSpace,
    301                    weak_factory_.GetWeakPtr()));
    302   }
    303   pending_operations_.push_back(op.release());
    304   return true;
    305 }
    306 
    307 void QuotaFileIO::DidQueryInfoForQuota(
    308     base::PlatformFileError error_code,
    309     const base::PlatformFileInfo& file_info) {
    310   if (error_code != base::PLATFORM_FILE_OK)
    311     ++outstanding_errors_;
    312   cached_file_size_ = file_info.size;
    313   DCHECK_GT(outstanding_quota_queries_, 0);
    314   if (--outstanding_quota_queries_ == 0)
    315     DidQueryForQuotaCheck();
    316 }
    317 
    318 void QuotaFileIO::DidQueryAvailableSpace(int64_t avail_space) {
    319   cached_available_space_ = avail_space;
    320   DCHECK_GT(outstanding_quota_queries_, 0);
    321   if (--outstanding_quota_queries_ == 0)
    322     DidQueryForQuotaCheck();
    323 }
    324 
    325 void QuotaFileIO::DidQueryForQuotaCheck() {
    326   DCHECK(!pending_operations_.empty());
    327   DCHECK_GT(inflight_operations_, 0);
    328   while (!pending_operations_.empty()) {
    329     PendingOperationBase* op = pending_operations_.front();
    330     pending_operations_.pop_front();
    331     pending_callbacks_.push_back(op);
    332     if (outstanding_errors_ > 0) {
    333       op->DidFail(base::PLATFORM_FILE_ERROR_FAILED);
    334       continue;
    335     }
    336     op->Run();
    337   }
    338 }
    339 
    340 bool QuotaFileIO::CheckIfExceedsQuota(int64_t new_file_size) const {
    341   DCHECK_GE(cached_file_size_, 0);
    342   DCHECK_GE(cached_available_space_, 0);
    343   return new_file_size - cached_file_size_ > cached_available_space_;
    344 }
    345 
    346 void QuotaFileIO::WillUpdate() {
    347   if (inflight_operations_++ == 0) {
    348     delegate_->WillUpdateFile(file_url_);
    349     DCHECK_EQ(0, max_written_offset_);
    350   }
    351 }
    352 
    353 void QuotaFileIO::DidWrite(WriteOperation* op,
    354                            int64_t written_offset_end) {
    355   max_written_offset_ = std::max(max_written_offset_, written_offset_end);
    356   DCHECK_GT(inflight_operations_, 0);
    357   DCHECK(!pending_callbacks_.empty());
    358   // Fire callbacks for finished operations.
    359   while (!pending_callbacks_.empty()) {
    360     WriteOperation* op = static_cast<WriteOperation*>(
    361         pending_callbacks_.front());
    362     if (!op->finished())
    363       break;
    364     pending_callbacks_.pop_front();
    365     op->WillRunCallback();
    366   }
    367   // If we have no more pending writes, notify the browser that we did
    368   // update the file.
    369   if (--inflight_operations_ == 0) {
    370     DCHECK(pending_operations_.empty());
    371     int64_t growth = max_written_offset_ - cached_file_size_;
    372     growth = growth < 0 ? 0 : growth;
    373 
    374     delegate_->DidUpdateFile(file_url_, growth);
    375     max_written_offset_ = 0;
    376   }
    377 }
    378 
    379 void QuotaFileIO::DidSetLength(PlatformFileError error, int64_t new_file_size) {
    380   DCHECK_EQ(1, inflight_operations_);
    381   pending_callbacks_.pop_front();
    382   DCHECK(pending_callbacks_.empty());
    383   int64_t delta = (error != base::PLATFORM_FILE_OK) ? 0 :
    384       new_file_size - cached_file_size_;
    385 
    386   delegate_->DidUpdateFile(file_url_, delta);
    387   inflight_operations_ = 0;
    388 }
    389 
    390 }  // namespace content
    391