Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(109)

Unified Diff: webkit/plugins/ppapi/quota_file_io.cc

Issue 7433006: Pepper quota support (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: '' Created 9 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « webkit/plugins/ppapi/quota_file_io.h ('k') | webkit/plugins/ppapi/quota_file_io_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: webkit/plugins/ppapi/quota_file_io.cc
diff --git a/webkit/plugins/ppapi/quota_file_io.cc b/webkit/plugins/ppapi/quota_file_io.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2a618a55fd7627ca805e0fc52dd250d4e18df8bb
--- /dev/null
+++ b/webkit/plugins/ppapi/quota_file_io.cc
@@ -0,0 +1,367 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/plugins/ppapi/quota_file_io.h"
+
+#include "base/stl_util.h"
+#include "base/message_loop_proxy.h"
+#include "base/task.h"
+#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
+
+using base::PlatformFile;
+using base::PlatformFileError;
+using quota::StorageType;
+
+namespace webkit {
+namespace ppapi {
+
+namespace {
+StorageType PPFileSystemTypeToQuotaStorageType(PP_FileSystemType type) {
+ switch (type) {
+ case PP_FILESYSTEMTYPE_LOCALPERSISTENT:
+ return quota::kStorageTypePersistent;
+ case PP_FILESYSTEMTYPE_LOCALTEMPORARY:
+ return quota::kStorageTypeTemporary;
+ default:
+ return quota::kStorageTypeUnknown;
+ }
+ NOTREACHED();
+}
+} // namespace
+
+class QuotaFileIO::PendingOperationBase {
+ public:
+ virtual ~PendingOperationBase() {}
+
+ // Either one of Run() or DidFail() is called (the latter is called when
+ // there was more than one error during quota queries).
+ virtual void Run() = 0;
+ virtual void DidFail(PlatformFileError error) = 0;
+
+ protected:
+ PendingOperationBase(QuotaFileIO* quota_io, bool is_will_operation)
+ : quota_io_(quota_io), is_will_operation_(is_will_operation) {
+ DCHECK(quota_io_);
+ quota_io_->WillUpdate();
+ }
+
+ QuotaFileIO* quota_io_;
+ const bool is_will_operation_;
+};
+
+class QuotaFileIO::WriteOperation : public PendingOperationBase {
+ public:
+ WriteOperation(QuotaFileIO* quota_io,
+ bool is_will_operation,
+ int64_t offset,
+ const char* buffer,
+ int32_t bytes_to_write,
+ WriteCallback* callback)
+ : PendingOperationBase(quota_io, is_will_operation),
+ offset_(offset),
+ bytes_to_write_(bytes_to_write),
+ callback_(callback),
+ finished_(false),
+ status_(base::PLATFORM_FILE_OK),
+ bytes_written_(0),
+ callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
+ runnable_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
+ if (!is_will_operation) {
+ // TODO(kinuko): check the API convention if we really need to keep a
+ // copy of the buffer during the async write operations.
+ buffer_.reset(new char[bytes_to_write]);
+ memcpy(buffer_.get(), buffer, bytes_to_write);
+ }
+ }
+ virtual ~WriteOperation() {}
+ virtual void Run() OVERRIDE {
+ DCHECK(quota_io_);
+ if (quota_io_->CheckIfExceedsQuota(offset_ + bytes_to_write_)) {
+ DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE);
+ return;
+ }
+ if (is_will_operation_) {
+ // Assuming the write will succeed.
+ base::MessageLoopProxy::CreateForCurrentThread()->PostTask(
+ FROM_HERE, runnable_factory_.NewRunnableMethod(
+ &WriteOperation::DidFinish,
+ base::PLATFORM_FILE_OK, bytes_to_write_));
+ return;
+ }
+ DCHECK(buffer_.get());
+ if (!base::FileUtilProxy::Write(
+ quota_io_->instance_->delegate()->GetFileThreadMessageLoopProxy(),
+ quota_io_->file_, offset_, buffer_.get(), bytes_to_write_,
+ callback_factory_.NewCallback(&WriteOperation::DidFinish))) {
+ DidFail(base::PLATFORM_FILE_ERROR_FAILED);
+ return;
+ }
+ }
+
+ virtual void DidFail(PlatformFileError error) OVERRIDE {
+ base::MessageLoopProxy::CreateForCurrentThread()->PostTask(
+ FROM_HERE, runnable_factory_.NewRunnableMethod(
+ &WriteOperation::DidFinish, error, 0));
+ }
+
+ bool finished() const { return finished_; }
+
+ void RunCallback() {
+ DCHECK(callback_.get());
+ callback_->Run(status_, bytes_written_);
+ callback_.reset();
+ delete this;
+ }
+
+ private:
+ void DidFinish(PlatformFileError status, int bytes_written) {
+ finished_ = true;
+ status_ = status;
+ bytes_written_ = bytes_written;
+ int64_t max_offset =
+ (status != base::PLATFORM_FILE_OK) ? 0 : offset_ + bytes_written;
+ // This may delete itself by calling RunCallback.
+ quota_io_->DidWrite(this, max_offset);
+ }
+
+ const int64_t offset_;
+ scoped_array<char> buffer_;
+ const int32_t bytes_to_write_;
+ scoped_ptr<WriteCallback> callback_;
+ bool finished_;
+ PlatformFileError status_;
+ int64_t bytes_written_;
+ base::ScopedCallbackFactory<QuotaFileIO::WriteOperation> callback_factory_;
+ ScopedRunnableMethodFactory<QuotaFileIO::WriteOperation> runnable_factory_;
+};
+
+class QuotaFileIO::SetLengthOperation : public PendingOperationBase {
+ public:
+ SetLengthOperation(QuotaFileIO* quota_io,
+ bool is_will_operation,
+ int64_t length,
+ StatusCallback* callback)
+ : PendingOperationBase(quota_io, is_will_operation),
+ length_(length),
+ callback_(callback),
+ callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
+ runnable_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {}
+ virtual ~SetLengthOperation() {}
+
+ virtual void Run() OVERRIDE {
+ DCHECK(quota_io_);
+ if (quota_io_->CheckIfExceedsQuota(length_)) {
+ DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE);
+ return;
+ }
+ if (is_will_operation_) {
+ base::MessageLoopProxy::CreateForCurrentThread()->PostTask(
+ FROM_HERE, runnable_factory_.NewRunnableMethod(
+ &SetLengthOperation::DidFinish,
+ base::PLATFORM_FILE_OK));
+ return;
+ }
+ if (!base::FileUtilProxy::Truncate(
+ quota_io_->instance_->delegate()->GetFileThreadMessageLoopProxy(),
+ quota_io_->file_, length_,
+ callback_factory_.NewCallback(&SetLengthOperation::DidFinish))) {
+ DidFail(base::PLATFORM_FILE_ERROR_FAILED);
+ return;
+ }
+ }
+
+ virtual void DidFail(PlatformFileError error) OVERRIDE {
+ base::MessageLoopProxy::CreateForCurrentThread()->PostTask(
+ FROM_HERE, runnable_factory_.NewRunnableMethod(
+ &SetLengthOperation::DidFinish, error));
+ }
+
+ private:
+ void DidFinish(PlatformFileError status) {
+ quota_io_->DidSetLength(status, length_);
+ DCHECK(callback_.get());
+ callback_->Run(status);
+ callback_.reset();
+ delete this;
+ }
+
+ int64_t length_;
+ scoped_ptr<StatusCallback> callback_;
+ base::ScopedCallbackFactory<QuotaFileIO::SetLengthOperation>
+ callback_factory_;
+ ScopedRunnableMethodFactory<QuotaFileIO::SetLengthOperation>
+ runnable_factory_;
+};
+
+// QuotaFileIO --------------------------------------------------------------
+
+QuotaFileIO::QuotaFileIO(
+ PluginInstance* instance,
+ PlatformFile file,
+ const GURL& file_url,
+ PP_FileSystemType type)
+ : instance_(instance),
+ file_(file),
+ file_url_(file_url),
+ storage_type_(PPFileSystemTypeToQuotaStorageType(type)),
+ cached_file_size_(0),
+ cached_available_space_(0),
+ outstanding_quota_queries_(0),
+ outstanding_errors_(0),
+ max_written_offset_(0),
+ inflight_operations_(0),
+ callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
+ DCHECK(instance_);
+ DCHECK_NE(base::kInvalidPlatformFileValue, file_);
+ DCHECK_NE(quota::kStorageTypeUnknown, storage_type_);
+}
+
+QuotaFileIO::~QuotaFileIO() {
+ // Note that this doesn't dispatch pending callbacks.
+ STLDeleteContainerPointers(pending_operations_.begin(),
+ pending_operations_.end());
+}
+
+bool QuotaFileIO::Write(
+ int64_t offset, const char* buffer, int32_t bytes_to_write,
+ WriteCallback* callback) {
+ WriteOperation* op = new WriteOperation(
+ this, false, offset, buffer, bytes_to_write, callback);
+ return RegisterOperationForQuotaChecks(op);
+}
+
+bool QuotaFileIO::SetLength(int64_t length, StatusCallback* callback) {
+ DCHECK(pending_operations_.empty());
+ SetLengthOperation* op = new SetLengthOperation(
+ this, false, length, callback);
+ return RegisterOperationForQuotaChecks(op);
+}
+
+bool QuotaFileIO::WillWrite(
+ int64_t offset, int32_t bytes_to_write, WriteCallback* callback) {
+ WriteOperation* op = new WriteOperation(
+ this, true, offset, NULL, bytes_to_write, callback);
+ return RegisterOperationForQuotaChecks(op);
+}
+
+bool QuotaFileIO::WillSetLength(int64_t length, StatusCallback* callback) {
+ DCHECK(pending_operations_.empty());
+ SetLengthOperation* op = new SetLengthOperation(this, true, length, callback);
+ return RegisterOperationForQuotaChecks(op);
+}
+
+bool QuotaFileIO::RegisterOperationForQuotaChecks(
+ PendingOperationBase* op_ptr) {
+ scoped_ptr<PendingOperationBase> op(op_ptr);
+ if (pending_operations_.empty()) {
+ // This is the first pending quota check. Run querying the file size
+ // and available space.
+ outstanding_quota_queries_ = 0;
+ outstanding_errors_ = 0;
+
+ // Query the file size.
+ ++outstanding_quota_queries_;
+ if (!base::FileUtilProxy::GetFileInfoFromPlatformFile(
+ instance_->delegate()->GetFileThreadMessageLoopProxy(), file_,
+ callback_factory_.NewCallback(
+ &QuotaFileIO::DidQueryInfoForQuota))) {
+ // This makes the call fail synchronously; we do not fire the callback
+ // here but just delete the operation and return false.
+ return false;
+ }
+
+ // Query the current available space.
+ ++outstanding_quota_queries_;
+ instance_->delegate()->QueryAvailableSpace(
+ GURL(file_url_.path()).GetOrigin(), storage_type_,
+ callback_factory_.NewCallback(&QuotaFileIO::DidQueryAvailableSpace));
+ }
+ pending_operations_.push_back(op.release());
+ return true;
+}
+
+void QuotaFileIO::DidQueryInfoForQuota(
+ base::PlatformFileError error_code,
+ const base::PlatformFileInfo& file_info) {
+ if (error_code != base::PLATFORM_FILE_OK)
+ ++outstanding_errors_;
+ cached_file_size_ = file_info.size;
+ DCHECK_GT(outstanding_quota_queries_, 0);
+ if (--outstanding_quota_queries_ == 0)
+ DidQueryForQuotaCheck();
+}
+
+void QuotaFileIO::DidQueryAvailableSpace(int64_t avail_space) {
+ cached_available_space_ = avail_space;
+ DCHECK_GT(outstanding_quota_queries_, 0);
+ if (--outstanding_quota_queries_ == 0)
+ DidQueryForQuotaCheck();
+}
+
+void QuotaFileIO::DidQueryForQuotaCheck() {
+ DCHECK(!pending_operations_.empty());
+ DCHECK_GT(inflight_operations_, 0);
+ for (std::deque<PendingOperationBase*>::iterator iter =
+ pending_operations_.begin();
+ iter != pending_operations_.end();
+ ++iter) {
+ PendingOperationBase* op = *iter;
+ if (outstanding_errors_ > 0) {
+ op->DidFail(base::PLATFORM_FILE_ERROR_FAILED);
+ continue;
+ }
+ op->Run();
+ }
+}
+
+bool QuotaFileIO::CheckIfExceedsQuota(int64_t new_file_size) const {
+ DCHECK_GE(cached_file_size_, 0);
+ DCHECK_GE(cached_available_space_, 0);
+ return new_file_size - cached_file_size_ > cached_available_space_;
+}
+
+void QuotaFileIO::WillUpdate() {
+ if (inflight_operations_++ == 0) {
+ instance_->delegate()->WillUpdateFile(file_url_);
+ DCHECK_EQ(0, max_written_offset_);
+ }
+}
+
+void QuotaFileIO::DidWrite(WriteOperation* op,
+ int64_t written_offset_end) {
+ max_written_offset_ = std::max(max_written_offset_, written_offset_end);
+ DCHECK_GT(inflight_operations_, 0);
+ DCHECK(!pending_operations_.empty());
+ // Fire callbacks for finished operations.
+ while (!pending_operations_.empty()) {
+ WriteOperation* op = static_cast<WriteOperation*>(
+ pending_operations_.front());
+ if (!op->finished())
+ break;
+ op->RunCallback();
+ pending_operations_.pop_front();
+ }
+ // If we have no more pending writes, notify the browser that we did
+ // update the file.
+ if (--inflight_operations_ == 0) {
+ DCHECK(pending_operations_.empty());
+ int64_t growth = max_written_offset_ - cached_file_size_;
+ growth = growth < 0 ? 0 : growth;
+ instance_->delegate()->DidUpdateFile(file_url_, growth);
+ max_written_offset_ = 0;
+ }
+}
+
+void QuotaFileIO::DidSetLength(PlatformFileError error, int64_t new_file_size) {
+ DCHECK_EQ(1, inflight_operations_);
+ pending_operations_.pop_front();
+ DCHECK(pending_operations_.empty());
+ int64_t delta = (error != base::PLATFORM_FILE_OK) ? 0 :
+ new_file_size - cached_file_size_;
+ instance_->delegate()->DidUpdateFile(file_url_, delta);
+ inflight_operations_ = 0;
+}
+
+} // namespace ppapi
+} // namespace webkit
« no previous file with comments | « webkit/plugins/ppapi/quota_file_io.h ('k') | webkit/plugins/ppapi/quota_file_io_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698