summaryrefslogtreecommitdiffstats
path: root/webkit/browser
diff options
context:
space:
mode:
authortzik@chromium.org <tzik@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-06 02:49:11 +0000
committertzik@chromium.org <tzik@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-06 02:49:11 +0000
commitb2a091dcf3534128b01ddbf09d5b6e96a6339445 (patch)
tree2ab0de95b9c314934f338536212f5c0b91d11045 /webkit/browser
parent4db04605ae6215c1314e63daf56d77914866ab1f (diff)
downloadchromium_src-b2a091dcf3534128b01ddbf09d5b6e96a6339445.zip
chromium_src-b2a091dcf3534128b01ddbf09d5b6e96a6339445.tar.gz
chromium_src-b2a091dcf3534128b01ddbf09d5b6e96a6339445.tar.bz2
[FileAPI] Add QuotaReservationManager and related classes.
* Adding QuotaReservationManager. * Adding Reservation, ReservationPool, OpenFile, ActiveFile as its inner class. TEST=content_unittests:QuotaReservationManagerTest.* BUG=303443 TBR=avi NOTRY=true Review URL: https://codereview.chromium.org/43323002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@233199 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/browser')
-rw-r--r--webkit/browser/fileapi/quota/open_file_handle.cc40
-rw-r--r--webkit/browser/fileapi/quota/open_file_handle.h51
-rw-r--r--webkit/browser/fileapi/quota/open_file_handle_context.cc60
-rw-r--r--webkit/browser/fileapi/quota/open_file_handle_context.h59
-rw-r--r--webkit/browser/fileapi/quota/quota_reservation.cc113
-rw-r--r--webkit/browser/fileapi/quota/quota_reservation.h89
-rw-r--r--webkit/browser/fileapi/quota/quota_reservation_buffer.cc107
-rw-r--r--webkit/browser/fileapi/quota/quota_reservation_buffer.h86
-rw-r--r--webkit/browser/fileapi/quota/quota_reservation_manager.cc84
-rw-r--r--webkit/browser/fileapi/quota/quota_reservation_manager.h125
-rw-r--r--webkit/browser/fileapi/quota/quota_reservation_manager_unittest.cc365
11 files changed, 1179 insertions, 0 deletions
diff --git a/webkit/browser/fileapi/quota/open_file_handle.cc b/webkit/browser/fileapi/quota/open_file_handle.cc
new file mode 100644
index 0000000..f0f69c6
--- /dev/null
+++ b/webkit/browser/fileapi/quota/open_file_handle.cc
@@ -0,0 +1,40 @@
+// Copyright 2013 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/browser/fileapi/quota/open_file_handle.h"
+
+#include "webkit/browser/fileapi/quota/open_file_handle_context.h"
+#include "webkit/browser/fileapi/quota/quota_reservation.h"
+
+namespace fileapi {
+
+OpenFileHandle::~OpenFileHandle() {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+}
+
+int64 OpenFileHandle::UpdateMaxWrittenOffset(int64 offset) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+
+ int64 new_file_size = 0;
+ int64 growth = 0;
+ context_->UpdateMaxWrittenOffset(offset, &new_file_size, &growth);
+
+ if (growth > 0)
+ reservation_->ConsumeReservation(growth);
+
+ return new_file_size;
+}
+
+int64 OpenFileHandle::base_file_size() const {
+ return context_->base_file_size();
+}
+
+OpenFileHandle::OpenFileHandle(QuotaReservation* reservation,
+ OpenFileHandleContext* context)
+ : reservation_(reservation),
+ context_(context) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+}
+
+} // namespace fileapi
diff --git a/webkit/browser/fileapi/quota/open_file_handle.h b/webkit/browser/fileapi/quota/open_file_handle.h
new file mode 100644
index 0000000..79f1380
--- /dev/null
+++ b/webkit/browser/fileapi/quota/open_file_handle.h
@@ -0,0 +1,51 @@
+// Copyright 2013 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.
+
+#ifndef WEBKIT_BROWSER_FILEAPI_QUOTA_OPEN_FILE_HANDLE_H_
+#define WEBKIT_BROWSER_FILEAPI_QUOTA_OPEN_FILE_HANDLE_H_
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "webkit/browser/webkit_storage_browser_export.h"
+
+namespace fileapi {
+
+class QuotaReservation;
+class OpenFileHandleContext;
+class QuotaReservationBuffer;
+
+// Represents an open file like a file descriptor.
+// This should be alive while a consumer keeps a file opened and should be
+// deleted when the plugin closes the file.
+class WEBKIT_STORAGE_BROWSER_EXPORT OpenFileHandle {
+ public:
+ ~OpenFileHandle();
+
+ // Updates cached file size and consumes quota for that.
+ // This should be called for each modified file before calling RefreshQuota
+ // and file close.
+ // Returns updated base file size that should be used to measure quota
+ // consumption by difference to this.
+ int64 UpdateMaxWrittenOffset(int64 offset);
+
+ int64 base_file_size() const;
+
+ private:
+ friend class QuotaReservationBuffer;
+
+ OpenFileHandle(QuotaReservation* reservation,
+ OpenFileHandleContext* context);
+
+ scoped_refptr<QuotaReservation> reservation_;
+ scoped_refptr<OpenFileHandleContext> context_;
+
+ base::SequenceChecker sequence_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(OpenFileHandle);
+};
+
+} // namespace fileapi
+
+#endif // WEBKIT_BROWSER_FILEAPI_QUOTA_OPEN_FILE_HANDLE_H_
diff --git a/webkit/browser/fileapi/quota/open_file_handle_context.cc b/webkit/browser/fileapi/quota/open_file_handle_context.cc
new file mode 100644
index 0000000..a730f06
--- /dev/null
+++ b/webkit/browser/fileapi/quota/open_file_handle_context.cc
@@ -0,0 +1,60 @@
+// Copyright 2013 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/browser/fileapi/quota/open_file_handle_context.h"
+
+#include "base/file_util.h"
+#include "webkit/browser/fileapi/quota/quota_reservation_buffer.h"
+
+namespace fileapi {
+
+OpenFileHandleContext::OpenFileHandleContext(
+ const base::FilePath& platform_path,
+ QuotaReservationBuffer* reservation_buffer)
+ : initial_file_size_(0),
+ maximum_written_offset_(0),
+ platform_path_(platform_path),
+ reservation_buffer_(reservation_buffer) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+
+ file_util::GetFileSize(platform_path, &initial_file_size_);
+ maximum_written_offset_ = initial_file_size_;
+}
+
+void OpenFileHandleContext::UpdateMaxWrittenOffset(
+ int64 offset,
+ int64* new_file_size,
+ int64* growth) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ if (offset > maximum_written_offset_) {
+ *growth = offset - maximum_written_offset_;
+ maximum_written_offset_ = offset;
+ } else {
+ *growth = 0;
+ }
+
+ *new_file_size = maximum_written_offset_;
+}
+
+OpenFileHandleContext::~OpenFileHandleContext() {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+
+ // TODO(tzik): Optimize this for single operation.
+
+ int64 file_size = 0;
+ file_util::GetFileSize(platform_path_, &file_size);
+ int64 usage_delta = file_size - initial_file_size_;
+
+ // |quota_consumption| may be greater than the recorded file growth when a
+ // plugin crashed before reporting its consumption.
+ // In this case, the reserved quota for the plugin should be handled as
+ // consumed quota.
+ int64 quota_consumption =
+ std::max(maximum_written_offset_, file_size) - initial_file_size_;
+
+ reservation_buffer_->CommitFileGrowth(quota_consumption, usage_delta);
+ reservation_buffer_->DetachOpenFileHandleContext(this);
+}
+
+} // namespace fileapi
diff --git a/webkit/browser/fileapi/quota/open_file_handle_context.h b/webkit/browser/fileapi/quota/open_file_handle_context.h
new file mode 100644
index 0000000..4b5d3cc
--- /dev/null
+++ b/webkit/browser/fileapi/quota/open_file_handle_context.h
@@ -0,0 +1,59 @@
+// Copyright 2013 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.
+
+#ifndef WEBKIT_BROWSER_FILEAPI_OPEN_FILE_HANDLE_CONTEXT_H_
+#define WEBKIT_BROWSER_FILEAPI_OPEN_FILE_HANDLE_CONTEXT_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/platform_file.h"
+#include "url/gurl.h"
+#include "webkit/browser/webkit_storage_browser_export.h"
+#include "webkit/common/fileapi/file_system_types.h"
+
+namespace fileapi {
+
+class QuotaReservationBuffer;
+
+// This class represents a underlying file of a managed FileSystem file.
+// The instance keeps alive while at least one consumer keeps an open file
+// handle.
+// This class is usually manipulated only via OpenFileHandle.
+class OpenFileHandleContext : public base::RefCounted<OpenFileHandleContext> {
+ public:
+ OpenFileHandleContext(const base::FilePath& platform_path,
+ QuotaReservationBuffer* reservation_buffer);
+
+ void UpdateMaxWrittenOffset(int64 offset,
+ int64* new_file_size,
+ int64* growth);
+
+ const base::FilePath& platform_path() const {
+ return platform_path_;
+ }
+
+ int64 base_file_size() const { return maximum_written_offset_; }
+
+ private:
+ friend class base::RefCounted<OpenFileHandleContext>;
+ virtual ~OpenFileHandleContext();
+
+ int64 initial_file_size_;
+ int64 maximum_written_offset_;
+ base::FilePath platform_path_;
+
+ scoped_refptr<QuotaReservationBuffer> reservation_buffer_;
+
+ base::SequenceChecker sequence_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(OpenFileHandleContext);
+};
+
+} // namespace fileapi
+
+#endif // WEBKIT_BROWSER_FILEAPI_OPEN_FILE_HANDLE_CONTEXT_H_
diff --git a/webkit/browser/fileapi/quota/quota_reservation.cc b/webkit/browser/fileapi/quota/quota_reservation.cc
new file mode 100644
index 0000000..c5a22ab
--- /dev/null
+++ b/webkit/browser/fileapi/quota/quota_reservation.cc
@@ -0,0 +1,113 @@
+// Copyright 2013 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/browser/fileapi/quota/quota_reservation.h"
+
+#include "base/bind.h"
+#include "webkit/browser/fileapi/quota/open_file_handle.h"
+#include "webkit/browser/fileapi/quota/quota_reservation_buffer.h"
+
+namespace fileapi {
+
+void QuotaReservation::RefreshReservation(
+ int64 size,
+ const StatusCallback& callback) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK(!running_refresh_request_);
+ if (!reservation_manager())
+ return;
+
+ running_refresh_request_ = true;
+
+ reservation_manager()->ReserveQuota(
+ origin(), type(), size - remaining_quota_,
+ base::Bind(&QuotaReservation::AdaptDidUpdateReservedQuota,
+ weak_ptr_factory_.GetWeakPtr(),
+ size, callback));
+
+ if (running_refresh_request_)
+ remaining_quota_ = 0;
+}
+
+scoped_ptr<OpenFileHandle> QuotaReservation::GetOpenFileHandle(
+ const base::FilePath& platform_path) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ return reservation_buffer_->GetOpenFileHandle(this, platform_path);
+}
+
+void QuotaReservation::OnClientCrash() {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+
+ if (remaining_quota_) {
+ reservation_buffer_->PutReservationToBuffer(remaining_quota_);
+ remaining_quota_ = 0;
+ }
+}
+
+void QuotaReservation::ConsumeReservation(int64 size) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK_LT(0, size);
+ DCHECK_LT(size, remaining_quota_);
+
+ remaining_quota_ -= size;
+ reservation_buffer_->PutReservationToBuffer(size);
+}
+
+QuotaReservationManager* QuotaReservation::reservation_manager() {
+ return reservation_buffer_->reservation_manager();
+}
+
+const GURL& QuotaReservation::origin() const {
+ return reservation_buffer_->origin();
+}
+
+FileSystemType QuotaReservation::type() const {
+ return reservation_buffer_->type();
+}
+
+QuotaReservation::QuotaReservation(
+ QuotaReservationBuffer* reservation_buffer)
+ : running_refresh_request_(false),
+ remaining_quota_(0),
+ reservation_buffer_(reservation_buffer),
+ weak_ptr_factory_(this) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+}
+
+QuotaReservation::~QuotaReservation() {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+
+ if (remaining_quota_ && reservation_manager()) {
+ reservation_manager()->ReleaseReservedQuota(
+ origin(), type(), remaining_quota_);
+ }
+}
+
+// static
+bool QuotaReservation::AdaptDidUpdateReservedQuota(
+ const base::WeakPtr<QuotaReservation>& reservation,
+ int64 new_reserved_size,
+ const StatusCallback& callback,
+ base::PlatformFileError error) {
+ if (!reservation)
+ return false;
+
+ reservation->DidUpdateReservedQuota(new_reserved_size, callback, error);
+ return true;
+}
+
+void QuotaReservation::DidUpdateReservedQuota(
+ int64 new_reserved_size,
+ const StatusCallback& callback,
+ base::PlatformFileError error) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK(running_refresh_request_);
+ running_refresh_request_ = false;
+
+ if (error == base::PLATFORM_FILE_OK)
+ remaining_quota_ = new_reserved_size;
+ callback.Run(error);
+}
+
+} // namespace fileapi
diff --git a/webkit/browser/fileapi/quota/quota_reservation.h b/webkit/browser/fileapi/quota/quota_reservation.h
new file mode 100644
index 0000000..d534a0d
--- /dev/null
+++ b/webkit/browser/fileapi/quota/quota_reservation.h
@@ -0,0 +1,89 @@
+// Copyright 2013 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.
+
+#ifndef WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_RESERVATION_H_
+#define WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_RESERVATION_H_
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "webkit/browser/fileapi/quota/quota_reservation_manager.h"
+#include "webkit/browser/webkit_storage_browser_export.h"
+#include "webkit/common/fileapi/file_system_types.h"
+
+class GURL;
+
+namespace fileapi {
+
+class QuotaReservationBuffer;
+class OpenFileHandle;
+
+// Represents a unit of quota reservation.
+class WEBKIT_STORAGE_BROWSER_EXPORT QuotaReservation
+ : public base::RefCounted<QuotaReservation> {
+ public:
+ typedef base::Callback<void(base::PlatformFileError error)> StatusCallback;
+
+ // Reclaims unused quota and reserves another |size| of quota. So that the
+ // resulting new |remaining_quota| will be same as |size|.
+ // Invokes |callback| upon completion.
+ void RefreshReservation(int64 size, const StatusCallback& callback);
+
+ // Associates |platform_path| to the QuotaReservation instance.
+ // Returns an OpenFileHandle instance that represents a quota managed file.
+ scoped_ptr<OpenFileHandle> GetOpenFileHandle(
+ const base::FilePath& platform_path);
+
+ // Should be called when the associated client is crashed.
+ // This implies the client can no longer report its consumption of the
+ // reserved quota.
+ // QuotaReservation puts all remaining quota to the QuotaReservationBuffer, so
+ // that the remaining quota will be reclaimed after all open files associated
+ // to the origin and type.
+ void OnClientCrash();
+
+ // Consumes |size| of reserved quota for a associated file.
+ // Consumed quota is sent to associated QuotaReservationBuffer for staging.
+ void ConsumeReservation(int64 size);
+
+ // Returns amount of unused reserved quota.
+ int64 remaining_quota() const { return remaining_quota_; }
+
+ QuotaReservationManager* reservation_manager();
+ const GURL& origin() const;
+ FileSystemType type() const;
+
+ private:
+ friend class QuotaReservationBuffer;
+
+ // Use QuotaReservationManager as the entry point.
+ explicit QuotaReservation(QuotaReservationBuffer* reservation_buffer);
+
+ friend class base::RefCounted<QuotaReservation>;
+ virtual ~QuotaReservation();
+
+ static bool AdaptDidUpdateReservedQuota(
+ const base::WeakPtr<QuotaReservation>& reservation,
+ int64 new_reserved_size,
+ const StatusCallback& callback,
+ base::PlatformFileError error);
+ void DidUpdateReservedQuota(int64 new_reserved_size,
+ const StatusCallback& callback,
+ base::PlatformFileError error);
+
+ bool running_refresh_request_;
+ int64 remaining_quota_;
+
+ scoped_refptr<QuotaReservationBuffer> reservation_buffer_;
+
+ base::SequenceChecker sequence_checker_;
+ base::WeakPtrFactory<QuotaReservation> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuotaReservation);
+};
+
+} // namespace fileapi
+
+#endif // WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_RESERVATION_H_
diff --git a/webkit/browser/fileapi/quota/quota_reservation_buffer.cc b/webkit/browser/fileapi/quota/quota_reservation_buffer.cc
new file mode 100644
index 0000000..c83073e
--- /dev/null
+++ b/webkit/browser/fileapi/quota/quota_reservation_buffer.cc
@@ -0,0 +1,107 @@
+// Copyright 2013 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/browser/fileapi/quota/quota_reservation_buffer.h"
+
+#include "base/bind.h"
+#include "webkit/browser/fileapi/quota/open_file_handle.h"
+#include "webkit/browser/fileapi/quota/open_file_handle_context.h"
+#include "webkit/browser/fileapi/quota/quota_reservation.h"
+
+namespace fileapi {
+
+namespace {
+
+void IgnoreResult(base::PlatformFileError error) {}
+
+} // namespace
+
+QuotaReservationBuffer::QuotaReservationBuffer(
+ base::WeakPtr<QuotaReservationManager> reservation_manager,
+ const GURL& origin,
+ FileSystemType type)
+ : reservation_manager_(reservation_manager),
+ origin_(origin),
+ type_(type),
+ reserved_quota_(0) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ reservation_manager_->IncreaseDirtyCount(origin, type);
+}
+
+scoped_refptr<QuotaReservation> QuotaReservationBuffer::CreateReservation() {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ return make_scoped_refptr(new QuotaReservation(this));
+}
+
+scoped_ptr<OpenFileHandle> QuotaReservationBuffer::GetOpenFileHandle(
+ QuotaReservation* reservation,
+ const base::FilePath& platform_path) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ OpenFileHandleContext** open_file = &open_files_[platform_path];
+ if (!*open_file)
+ *open_file = new OpenFileHandleContext(platform_path, this);
+ return make_scoped_ptr(new OpenFileHandle(reservation, *open_file));
+}
+
+void QuotaReservationBuffer::CommitFileGrowth(int64 quota_consumption,
+ int64 usage_delta) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ if (!reservation_manager_)
+ return;
+
+ reservation_manager_->CommitQuotaUsage(
+ origin_, type_, usage_delta,
+ base::Bind(&IgnoreResult));
+
+ DCHECK_LE(quota_consumption, reserved_quota_);
+ if (quota_consumption > 0) {
+ reserved_quota_ -= quota_consumption;
+ reservation_manager_->ReleaseReservedQuota(
+ origin_, type_, quota_consumption);
+ }
+}
+
+void QuotaReservationBuffer::DetachOpenFileHandleContext(
+ OpenFileHandleContext* open_file) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK_EQ(open_file, open_files_[open_file->platform_path()]);
+ open_files_.erase(open_file->platform_path());
+}
+
+void QuotaReservationBuffer::PutReservationToBuffer(int64 reservation) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ DCHECK_LE(0, reservation);
+ reserved_quota_ += reservation;
+}
+
+QuotaReservationBuffer::~QuotaReservationBuffer() {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ if (!reservation_manager_)
+ return;
+
+ DCHECK_LE(0, reserved_quota_);
+ if (reserved_quota_ && reservation_manager_) {
+ reservation_manager_->ReserveQuota(
+ origin_, type_, -reserved_quota_,
+ base::Bind(&QuotaReservationBuffer::DecreaseDirtyCount,
+ reservation_manager_, origin_, type_));
+ }
+ reservation_manager_->ReleaseReservationBuffer(this);
+}
+
+// static
+bool QuotaReservationBuffer::DecreaseDirtyCount(
+ base::WeakPtr<QuotaReservationManager> reservation_manager,
+ const GURL& origin,
+ FileSystemType type,
+ base::PlatformFileError error) {
+ if (error == base::PLATFORM_FILE_OK && reservation_manager) {
+ reservation_manager->DecreaseDirtyCount(origin, type);
+ return true;
+ }
+ return false;
+}
+
+
+} // namespace fileapi
diff --git a/webkit/browser/fileapi/quota/quota_reservation_buffer.h b/webkit/browser/fileapi/quota/quota_reservation_buffer.h
new file mode 100644
index 0000000..1b27529
--- /dev/null
+++ b/webkit/browser/fileapi/quota/quota_reservation_buffer.h
@@ -0,0 +1,86 @@
+// Copyright 2013 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.
+
+#ifndef WEBKIT_BROWSER_FILEAPI_QUOTA_RESERVATION_BUFFER_H_
+#define WEBKIT_BROWSER_FILEAPI_QUOTA_RESERVATION_BUFFER_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/platform_file.h"
+#include "url/gurl.h"
+#include "webkit/browser/webkit_storage_browser_export.h"
+#include "webkit/common/fileapi/file_system_types.h"
+
+namespace fileapi {
+
+class QuotaReservation;
+class OpenFileHandle;
+class OpenFileHandleContext;
+class QuotaReservationManager;
+
+// QuotaReservationBuffer manages QuotaReservation instances. All consumed
+// quota and leaked quota by associated QuotaReservation will be staged in
+// QuotaReservationBuffer, and will be committed on a modified file is closed.
+// The instance keeps alive while any associated QuotaReservation or
+// OpenFileHandle alive.
+// This class is usually manipulated only via OpenFileHandle and
+// QuotaReservation.
+class QuotaReservationBuffer : public base::RefCounted<QuotaReservationBuffer> {
+ public:
+ QuotaReservationBuffer(
+ base::WeakPtr<QuotaReservationManager> reservation_manager,
+ const GURL& origin,
+ FileSystemType type);
+
+ scoped_refptr<QuotaReservation> CreateReservation();
+ scoped_ptr<OpenFileHandle> GetOpenFileHandle(
+ QuotaReservation* reservation,
+ const base::FilePath& platform_path);
+ void CommitFileGrowth(int64 quota_consumption, int64 usage_delta);
+ void DetachOpenFileHandleContext(OpenFileHandleContext* context);
+ void PutReservationToBuffer(int64 size);
+
+ QuotaReservationManager* reservation_manager() {
+ return reservation_manager_.get();
+ }
+
+ const GURL& origin() const { return origin_; }
+ FileSystemType type() const { return type_; }
+
+ private:
+ friend class base::RefCounted<QuotaReservationBuffer>;
+ virtual ~QuotaReservationBuffer();
+
+ static bool DecreaseDirtyCount(
+ base::WeakPtr<QuotaReservationManager> reservation_manager,
+ const GURL& origin,
+ FileSystemType type,
+ base::PlatformFileError error);
+
+ typedef std::map<base::FilePath, OpenFileHandleContext*>
+ OpenFileHandleContextByPath;
+
+ // Not owned. The destructor of OpenFileHandler should erase itself from
+ // |open_files_|.
+ OpenFileHandleContextByPath open_files_;
+
+ base::WeakPtr<QuotaReservationManager> reservation_manager_;
+
+ GURL origin_;
+ fileapi::FileSystemType type_;
+
+ int64 reserved_quota_;
+
+ base::SequenceChecker sequence_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuotaReservationBuffer);
+};
+
+} // namespace fileapi
+
+#endif // WEBKIT_BROWSER_FILEAPI_QUOTA_RESERVATION_BUFFER_H_
diff --git a/webkit/browser/fileapi/quota/quota_reservation_manager.cc b/webkit/browser/fileapi/quota/quota_reservation_manager.cc
new file mode 100644
index 0000000..0a02940
--- /dev/null
+++ b/webkit/browser/fileapi/quota/quota_reservation_manager.cc
@@ -0,0 +1,84 @@
+// Copyright 2013 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/browser/fileapi/quota/quota_reservation_manager.h"
+
+#include "webkit/browser/fileapi/quota/quota_reservation.h"
+#include "webkit/browser/fileapi/quota/quota_reservation_buffer.h"
+
+namespace fileapi {
+
+QuotaReservationManager::QuotaReservationManager(QuotaBackend* backend)
+ : backend_(backend),
+ weak_ptr_factory_(this) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+}
+
+QuotaReservationManager::~QuotaReservationManager() {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+}
+
+void QuotaReservationManager::ReserveQuota(
+ const GURL& origin,
+ FileSystemType type,
+ int64 size,
+ const ReserveQuotaCallback& callback) {
+ backend_->ReserveQuota(origin, type, size, callback);
+}
+
+void QuotaReservationManager::ReleaseReservedQuota(
+ const GURL& origin,
+ FileSystemType type,
+ int64 size) {
+ backend_->ReleaseReservedQuota(origin, type, size);
+}
+
+void QuotaReservationManager::CommitQuotaUsage(
+ const GURL& origin,
+ FileSystemType type,
+ int64 delta,
+ const StatusCallback& callback) {
+ backend_->CommitQuotaUsage(origin, type, delta, callback);
+}
+
+void QuotaReservationManager::IncreaseDirtyCount(const GURL& origin,
+ FileSystemType type) {
+ backend_->IncreaseDirtyCount(origin, type);
+}
+
+void QuotaReservationManager::DecreaseDirtyCount(const GURL& origin,
+ FileSystemType type) {
+ backend_->DecreaseDirtyCount(origin, type);
+}
+
+scoped_refptr<QuotaReservationBuffer>
+QuotaReservationManager::GetReservationBuffer(
+ const GURL& origin,
+ FileSystemType type) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ QuotaReservationBuffer** buffer =
+ &reservation_buffers_[std::make_pair(origin, type)];
+ if (!*buffer) {
+ *buffer = new QuotaReservationBuffer(
+ weak_ptr_factory_.GetWeakPtr(), origin, type);
+ }
+ return make_scoped_refptr(*buffer);
+}
+
+void QuotaReservationManager::ReleaseReservationBuffer(
+ QuotaReservationBuffer* reservation_buffer) {
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread());
+ std::pair<GURL, FileSystemType> key(reservation_buffer->origin(),
+ reservation_buffer->type());
+ DCHECK_EQ(reservation_buffers_[key], reservation_buffer);
+ reservation_buffers_.erase(key);
+}
+
+scoped_refptr<QuotaReservation> QuotaReservationManager::CreateReservation(
+ const GURL& origin,
+ FileSystemType type) {
+ return GetReservationBuffer(origin, type)->CreateReservation();;
+}
+
+} // namespace fileapi
diff --git a/webkit/browser/fileapi/quota/quota_reservation_manager.h b/webkit/browser/fileapi/quota/quota_reservation_manager.h
new file mode 100644
index 0000000..2e3ae93
--- /dev/null
+++ b/webkit/browser/fileapi/quota/quota_reservation_manager.h
@@ -0,0 +1,125 @@
+// Copyright 2013 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.
+
+#ifndef WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_RESERVATION_MANAGER_H_
+#define WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_RESERVATION_MANAGER_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/platform_file.h"
+#include "url/gurl.h"
+#include "webkit/browser/webkit_storage_browser_export.h"
+#include "webkit/common/fileapi/file_system_types.h"
+
+namespace fileapi {
+
+class QuotaReservation;
+class QuotaReservationBuffer;
+class OpenFileHandle;
+class OpenFileHandleContext;
+
+class WEBKIT_STORAGE_BROWSER_EXPORT QuotaReservationManager {
+ public:
+ typedef base::Callback<void(base::PlatformFileError error)> StatusCallback;
+ typedef base::Callback<bool(base::PlatformFileError error)>
+ ReserveQuotaCallback;
+
+ // An abstraction of backing quota system.
+ class QuotaBackend {
+ public:
+ // Reserves or reclaims |delta| of quota for |origin| and |type| pair.
+ // Reserved quota should be counted as usage, but it should be on-memory
+ // and be cleared by a browser restart.
+ // Invokes |callback| upon completion with an error code.
+ // |callback| should return false if it can't accept the reservation, in
+ // that case, the backend should roll back the reservation.
+ virtual void ReserveQuota(const GURL& origin,
+ FileSystemType type,
+ int64 delta,
+ const ReserveQuotaCallback& callback) = 0;
+
+ // Reclaims |size| of quota for |origin| and |type|.
+ virtual void ReleaseReservedQuota(const GURL& origin,
+ FileSystemType type,
+ int64 size) = 0;
+
+ // Updates disk usage of |origin| and |type|.
+ // Invokes |callback| upon completion with an error code.
+ virtual void CommitQuotaUsage(const GURL& origin,
+ FileSystemType type,
+ int64 delta,
+ const StatusCallback& callback) = 0;
+
+ virtual void IncreaseDirtyCount(const GURL& origin,
+ FileSystemType type) = 0;
+ virtual void DecreaseDirtyCount(const GURL& origin,
+ FileSystemType type) = 0;
+
+ protected:
+ QuotaBackend() {}
+ virtual ~QuotaBackend() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuotaBackend);
+ };
+
+ explicit QuotaReservationManager(QuotaBackend* backend);
+ ~QuotaReservationManager();
+
+ // The entry point of the quota reservation. Creates new reservation object
+ // for |origin| and |type|.
+ scoped_refptr<QuotaReservation> CreateReservation(
+ const GURL& origin,
+ FileSystemType type);
+
+ private:
+ typedef std::map<std::pair<GURL, FileSystemType>, QuotaReservationBuffer*>
+ ReservationBufferByOriginAndType;
+
+ friend class QuotaReservation;
+ friend class QuotaReservationBuffer;
+
+ void ReserveQuota(const GURL& origin,
+ FileSystemType type,
+ int64 delta,
+ const ReserveQuotaCallback& callback);
+
+ void ReleaseReservedQuota(const GURL& origin,
+ FileSystemType type,
+ int64 size);
+
+ void CommitQuotaUsage(const GURL& origin,
+ FileSystemType type,
+ int64 delta,
+ const StatusCallback& callback);
+
+ void IncreaseDirtyCount(const GURL& origin, FileSystemType type);
+ void DecreaseDirtyCount(const GURL& origin, FileSystemType type);
+
+ scoped_refptr<QuotaReservationBuffer> GetReservationBuffer(
+ const GURL& origin,
+ FileSystemType type);
+ void ReleaseReservationBuffer(QuotaReservationBuffer* reservation_pool);
+
+
+ // Not owned. |backend_| should outlive QuotaReservationManager
+ QuotaBackend* backend_;
+
+ // Not owned. The destructor of ReservationBuffer should erase itself from
+ // |reservation_buffers_| by calling ReleaseReservationBuffer.
+ ReservationBufferByOriginAndType reservation_buffers_;
+
+ base::SequenceChecker sequence_checker_;
+ base::WeakPtrFactory<QuotaReservationManager> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuotaReservationManager);
+};
+
+} // namespace fileapi
+
+#endif // WEBKIT_BROWSER_FILEAPI_QUOTA_QUOTA_RESERVATION_MANAGER_H_
diff --git a/webkit/browser/fileapi/quota/quota_reservation_manager_unittest.cc b/webkit/browser/fileapi/quota/quota_reservation_manager_unittest.cc
new file mode 100644
index 0000000..86590c7a
--- /dev/null
+++ b/webkit/browser/fileapi/quota/quota_reservation_manager_unittest.cc
@@ -0,0 +1,365 @@
+// Copyright 2013 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/browser/fileapi/quota/quota_reservation_manager.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/quota/open_file_handle.h"
+#include "webkit/browser/fileapi/quota/quota_reservation.h"
+
+namespace fileapi {
+
+namespace {
+
+const char kOrigin[] = "http://example.com";
+const FileSystemType kType = kFileSystemTypeTemporary;
+const int64 kInitialFileSize = 30;
+
+typedef QuotaReservationManager::ReserveQuotaCallback ReserveQuotaCallback;
+typedef QuotaReservationManager::StatusCallback StatusCallback;
+
+class FakeBackend : public QuotaReservationManager::QuotaBackend {
+ public:
+ FakeBackend()
+ : on_memory_usage_(0),
+ on_disk_usage_(0) {}
+ virtual ~FakeBackend() {}
+
+ virtual void ReserveQuota(const GURL& origin,
+ FileSystemType type,
+ int64 delta,
+ const ReserveQuotaCallback& callback) OVERRIDE {
+ EXPECT_EQ(GURL(kOrigin), origin);
+ EXPECT_EQ(kType, type);
+ on_memory_usage_ += delta;
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(base::IgnoreResult(callback), base::PLATFORM_FILE_OK));
+ }
+
+ virtual void ReleaseReservedQuota(const GURL& origin,
+ FileSystemType type,
+ int64 size) OVERRIDE {
+ EXPECT_LE(0, size);
+ EXPECT_EQ(GURL(kOrigin), origin);
+ EXPECT_EQ(kType, type);
+ on_memory_usage_ -= size;
+ }
+
+ virtual void CommitQuotaUsage(const GURL& origin,
+ FileSystemType type,
+ int64 delta,
+ const StatusCallback& callback) OVERRIDE {
+ EXPECT_EQ(GURL(kOrigin), origin);
+ EXPECT_EQ(kType, type);
+ on_disk_usage_ += delta;
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE, base::Bind(callback, base::PLATFORM_FILE_OK));
+ }
+
+ virtual void IncreaseDirtyCount(const GURL& origin,
+ FileSystemType type) OVERRIDE {}
+ virtual void DecreaseDirtyCount(const GURL& origin,
+ FileSystemType type) OVERRIDE {}
+
+ int64 on_memory_usage() { return on_memory_usage_; }
+ int64 on_disk_usage() { return on_disk_usage_; }
+
+ private:
+ int64 on_memory_usage_;
+ int64 on_disk_usage_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeBackend);
+};
+
+void ExpectSuccess(bool* done, base::PlatformFileError error) {
+ EXPECT_FALSE(*done);
+ *done = true;
+ EXPECT_EQ(base::PLATFORM_FILE_OK, error);
+}
+
+void RefreshQuota(QuotaReservation* reservation, int64 size) {
+ DCHECK(reservation);
+
+ bool done = false;
+ reservation->RefreshReservation(size, base::Bind(&ExpectSuccess, &done));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(done);
+}
+
+} // namespace
+
+class QuotaReservationManagerTest : public testing::Test {
+ public:
+ QuotaReservationManagerTest() {}
+ virtual ~QuotaReservationManagerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(work_dir_.CreateUniqueTempDir());
+ file_path_ = work_dir_.path().Append(FILE_PATH_LITERAL("hoge"));
+ SetFileSize(kInitialFileSize);
+
+ fake_backend_.reset(new FakeBackend);
+ reservation_manager_.reset(new QuotaReservationManager(
+ fake_backend_.get()));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ reservation_manager_.reset();
+ fake_backend_.reset();
+ }
+
+ int64 GetFileSize() {
+ int64 size = 0;
+ file_util::GetFileSize(file_path_, &size);
+ return size;
+ }
+
+ void SetFileSize(int64 size) {
+ bool created = false;
+ base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
+ base::PlatformFile file = CreatePlatformFile(
+ file_path_,
+ base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_WRITE,
+ &created, &error);
+ ASSERT_EQ(base::PLATFORM_FILE_OK, error);
+ ASSERT_TRUE(base::TruncatePlatformFile(file, size));
+ ASSERT_TRUE(base::ClosePlatformFile(file));
+ }
+
+ void ExtendFileTo(int64 size) {
+ if (GetFileSize() < size)
+ SetFileSize(size);
+ }
+
+ FakeBackend* fake_backend() {
+ return fake_backend_.get();
+ }
+
+ QuotaReservationManager* reservation_manager() {
+ return reservation_manager_.get();
+ }
+
+ const base::FilePath& file_path() const {
+ return file_path_;
+ }
+
+ private:
+ base::MessageLoop message_loop_;
+ base::ScopedTempDir work_dir_;
+ base::FilePath file_path_;
+ scoped_ptr<FakeBackend> fake_backend_;
+ scoped_ptr<QuotaReservationManager> reservation_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuotaReservationManagerTest);
+};
+
+TEST_F(QuotaReservationManagerTest, BasicTest) {
+ GURL origin(kOrigin);
+ FileSystemType type = kType;
+
+ // Create Reservation channel for the origin and type.
+ // Reservation holds remaining quota reservation and provides a method to
+ // refresh it.
+ scoped_refptr<QuotaReservation> reservation =
+ reservation_manager()->CreateReservation(origin, type);
+ EXPECT_EQ(0, reservation->remaining_quota());
+
+ RefreshQuota(reservation, 100);
+ EXPECT_EQ(100, reservation->remaining_quota());
+
+ {
+ // For each open file for write, the client should create OpenFileHandle
+ // object.
+ // It's OK to create multiple OpenFileHandle for single file.
+ scoped_ptr<OpenFileHandle> open_file =
+ reservation->GetOpenFileHandle(file_path());
+
+ // Before reserved quota ran out, the client can perform any number of
+ // operation for the file.
+ // The client should calculate how much quota is consumed by itself.
+ int64 remaining_quota = reservation->remaining_quota();
+ int64 base_file_size = open_file->base_file_size();
+ int64 max_written_offset = base_file_size;
+ ExtendFileTo(90);
+ max_written_offset = 90;
+ remaining_quota -= max_written_offset - base_file_size;
+
+ // When the reserved quota ran out, the client can request quota refresh
+ // through Reservation. Before requesting another portion of quota, the
+ // client should report maximum written offset for each modified files.
+ open_file->UpdateMaxWrittenOffset(max_written_offset);
+ EXPECT_EQ(remaining_quota, reservation->remaining_quota());
+
+ RefreshQuota(reservation, 100);
+ EXPECT_EQ(100, reservation->remaining_quota());
+ }
+
+ EXPECT_EQ(90, GetFileSize());
+ EXPECT_EQ(100, fake_backend()->on_memory_usage());
+ EXPECT_EQ(90 - kInitialFileSize, fake_backend()->on_disk_usage());
+
+ reservation = NULL;
+
+ EXPECT_EQ(90, GetFileSize());
+ EXPECT_EQ(0, fake_backend()->on_memory_usage());
+ EXPECT_EQ(90 - kInitialFileSize, fake_backend()->on_disk_usage());
+}
+
+TEST_F(QuotaReservationManagerTest, MultipleWriter) {
+ GURL origin(kOrigin);
+ FileSystemType type = kType;
+
+ scoped_refptr<QuotaReservation> reservation =
+ reservation_manager()->CreateReservation(origin, type);
+ EXPECT_EQ(0, reservation->remaining_quota());
+
+ RefreshQuota(reservation, 100);
+ EXPECT_EQ(100, reservation->remaining_quota());
+
+ {
+ scoped_ptr<OpenFileHandle> open_file1 =
+ reservation->GetOpenFileHandle(file_path());
+ scoped_ptr<OpenFileHandle> open_file2 =
+ reservation->GetOpenFileHandle(file_path());
+
+ int64 remaining_quota = reservation->remaining_quota();
+
+ int64 base_file_size_for_file1 = open_file1->base_file_size();
+ int64 max_written_offset_for_file1 = base_file_size_for_file1;
+
+ int64 base_file_size_for_file2 = open_file2->base_file_size();
+ int64 max_written_offset_for_file2 = base_file_size_for_file2;
+
+ // Each writer should maintain max_written_offset and base_file_size
+ // independently even if there are multiple writers for the same file.
+ max_written_offset_for_file1 = 50;
+ ExtendFileTo(max_written_offset_for_file1);
+ remaining_quota -= max_written_offset_for_file1 - base_file_size_for_file1;
+ base_file_size_for_file1 = max_written_offset_for_file1;
+
+ max_written_offset_for_file2 = 90;
+ ExtendFileTo(max_written_offset_for_file2);
+ remaining_quota -= max_written_offset_for_file2 - base_file_size_for_file2;
+ base_file_size_for_file2 = max_written_offset_for_file2;
+
+ // Before requesting quota refresh, each writer should report their
+ // maximum_written_offset. UpdateMaxWrittenOffset returns updated
+ // base_file_size that the writer should calculate quota consumption based
+ // on that.
+ base_file_size_for_file1 =
+ open_file1->UpdateMaxWrittenOffset(max_written_offset_for_file1);
+ max_written_offset_for_file1 = base_file_size_for_file1;
+ EXPECT_EQ(100 - (50 - kInitialFileSize), reservation->remaining_quota());
+
+ base_file_size_for_file2 =
+ open_file2->UpdateMaxWrittenOffset(max_written_offset_for_file2);
+ max_written_offset_for_file2 = base_file_size_for_file2;
+ EXPECT_EQ(100 - (50 - kInitialFileSize) - (90 - 50),
+ reservation->remaining_quota());
+
+ RefreshQuota(reservation, 100);
+ EXPECT_EQ(100, reservation->remaining_quota());
+ }
+
+ EXPECT_EQ(90, GetFileSize());
+ EXPECT_EQ(100, fake_backend()->on_memory_usage());
+ EXPECT_EQ(90 - kInitialFileSize, fake_backend()->on_disk_usage());
+
+ reservation = NULL;
+
+ EXPECT_EQ(90, GetFileSize());
+ EXPECT_EQ(0, fake_backend()->on_memory_usage());
+ EXPECT_EQ(90 - kInitialFileSize, fake_backend()->on_disk_usage());
+}
+
+TEST_F(QuotaReservationManagerTest, MultipleClient) {
+ GURL origin(kOrigin);
+ FileSystemType type = kType;
+
+ scoped_refptr<QuotaReservation> reservation1 =
+ reservation_manager()->CreateReservation(origin, type);
+ EXPECT_EQ(0, reservation1->remaining_quota());
+ RefreshQuota(reservation1, 100);
+ EXPECT_EQ(100, reservation1->remaining_quota());
+
+ scoped_refptr<QuotaReservation> reservation2 =
+ reservation_manager()->CreateReservation(origin, type);
+ EXPECT_EQ(0, reservation2->remaining_quota());
+ RefreshQuota(reservation2, 500);
+ EXPECT_EQ(500, reservation2->remaining_quota());
+
+ // Attach a file to both of two reservations.
+ scoped_ptr<OpenFileHandle> open_file1 =
+ reservation1->GetOpenFileHandle(file_path());
+ scoped_ptr<OpenFileHandle> open_file2 =
+ reservation2->GetOpenFileHandle(file_path());
+
+ // Each client should manage reserved quota and its consumption separately.
+ int64 remaining_quota1 = reservation1->remaining_quota();
+ int64 base_file_size1 = open_file1->base_file_size();
+ int64 max_written_offset1 = base_file_size1;
+
+ int64 remaining_quota2 = reservation2->remaining_quota();
+ int64 base_file_size2 = open_file2->base_file_size();
+ int64 max_written_offset2 = base_file_size2;
+
+ max_written_offset1 = 50;
+ remaining_quota1 -= max_written_offset1 - base_file_size1;
+ base_file_size1 = max_written_offset1;
+ ExtendFileTo(max_written_offset1);
+
+ max_written_offset2 = 400;
+ remaining_quota2 -= max_written_offset2 - base_file_size2;
+ base_file_size2 = max_written_offset2;
+ ExtendFileTo(max_written_offset2);
+
+ // For multiple Reservation case, RefreshQuota needs usage report only from
+ // associated OpenFile's.
+ base_file_size1 =
+ open_file1->UpdateMaxWrittenOffset(max_written_offset1);
+ max_written_offset1 = base_file_size1;
+ EXPECT_EQ(100 - (50 - kInitialFileSize), reservation1->remaining_quota());
+
+ RefreshQuota(reservation1, 200);
+ EXPECT_EQ(200, reservation1->remaining_quota());
+
+ base_file_size2 =
+ open_file2->UpdateMaxWrittenOffset(max_written_offset2);
+ max_written_offset2 = base_file_size2;
+ EXPECT_EQ(500 - (400 - 50), reservation2->remaining_quota());
+
+ RefreshQuota(reservation2, 150);
+ EXPECT_EQ(150, reservation2->remaining_quota());
+
+ open_file1.reset();
+ open_file2.reset();
+
+ EXPECT_EQ(400, GetFileSize());
+ EXPECT_EQ(200 + 150, fake_backend()->on_memory_usage());
+ EXPECT_EQ(400 - kInitialFileSize, fake_backend()->on_disk_usage());
+
+ reservation1 = NULL;
+
+ EXPECT_EQ(400, GetFileSize());
+ EXPECT_EQ(150, fake_backend()->on_memory_usage());
+ EXPECT_EQ(400 - kInitialFileSize, fake_backend()->on_disk_usage());
+
+ reservation2 = NULL;
+
+ EXPECT_EQ(400, GetFileSize());
+ EXPECT_EQ(0, fake_backend()->on_memory_usage());
+ EXPECT_EQ(400 - kInitialFileSize, fake_backend()->on_disk_usage());
+}
+
+// TODO(tzik): Add Truncate test.
+// TODO(tzik): Add PluginCrash test and DropReservationManager test.
+
+} // namespace fileapi