Commit 682394a2 authored by dgrogan@chromium.org's avatar dgrogan@chromium.org

Improve IndexedDB's quota support

* Check available quota before storing anything
* Inform quota manager of storage updates
* Evict an origin when quota manager requests

BUG=83652
TEST=llvm/Debug/unit_tests --gtest_filter=IndexedDBQuotaClientTest.* && llvm/Debug/browser_tests --gtest_filter=IndexedDBBrowser*

Review URL: http://codereview.chromium.org/7470008

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@95691 0039d316-1c4b-4281-b951-d872f2087c98
parent a4f10d1e
...@@ -82,7 +82,8 @@ void ExtensionDataDeleter::DeleteLocalStorageOnWebkitThread() { ...@@ -82,7 +82,8 @@ void ExtensionDataDeleter::DeleteLocalStorageOnWebkitThread() {
void ExtensionDataDeleter::DeleteIndexedDBOnWebkitThread() { void ExtensionDataDeleter::DeleteIndexedDBOnWebkitThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT));
webkit_context_->indexed_db_context()->DeleteIndexedDBForOrigin(origin_id_); webkit_context_->indexed_db_context()->DeleteIndexedDBForOrigin(
extension_url_);
} }
void ExtensionDataDeleter::DeleteFileSystemOnFileThread() { void ExtensionDataDeleter::DeleteFileSystemOnFileThread() {
......
...@@ -28,15 +28,18 @@ void IndexedDBCallbacksBase::onBlocked() { ...@@ -28,15 +28,18 @@ void IndexedDBCallbacksBase::onBlocked() {
void IndexedDBCallbacks<WebKit::WebIDBDatabase>::onSuccess( void IndexedDBCallbacks<WebKit::WebIDBDatabase>::onSuccess(
WebKit::WebIDBDatabase* idb_object) { WebKit::WebIDBDatabase* idb_object) {
int32 object_id = dispatcher_host()->Add(idb_object, origin_url()); int32 object_id = dispatcher_host()->Add(idb_object, origin_url());
if (dispatcher_host()->Context()->quota_manager_proxy()) {
dispatcher_host()->Context()->quota_manager_proxy()->NotifyStorageAccessed(
quota::QuotaClient::kIndexedDatabase, origin_url(),
quota::kStorageTypeTemporary);
}
dispatcher_host()->Send( dispatcher_host()->Send(
new IndexedDBMsg_CallbacksSuccessIDBDatabase(response_id(), object_id)); new IndexedDBMsg_CallbacksSuccessIDBDatabase(response_id(), object_id));
} }
void IndexedDBCallbacks<WebKit::WebIDBTransaction>::onSuccess(
WebKit::WebIDBTransaction* idb_object) {
int32 object_id = dispatcher_host()->Add(idb_object, origin_url());
dispatcher_host()->Send(
new IndexedDBMsg_CallbacksSuccessIDBTransaction(response_id(),
object_id));
}
void IndexedDBCallbacks<WebKit::WebIDBCursor>::onSuccess( void IndexedDBCallbacks<WebKit::WebIDBCursor>::onSuccess(
WebKit::WebIDBCursor* idb_object) { WebKit::WebIDBCursor* idb_object) {
int32 object_id = dispatcher_host()->Add(idb_object); int32 object_id = dispatcher_host()->Add(idb_object);
......
...@@ -25,9 +25,6 @@ template <class Type> struct WebIDBToMsgHelper { }; ...@@ -25,9 +25,6 @@ template <class Type> struct WebIDBToMsgHelper { };
template <> struct WebIDBToMsgHelper<WebKit::WebIDBIndex> { template <> struct WebIDBToMsgHelper<WebKit::WebIDBIndex> {
typedef IndexedDBMsg_CallbacksSuccessIDBIndex MsgType; typedef IndexedDBMsg_CallbacksSuccessIDBIndex MsgType;
}; };
template <> struct WebIDBToMsgHelper<WebKit::WebIDBTransaction> {
typedef IndexedDBMsg_CallbacksSuccessIDBTransaction MsgType;
};
// The code the following two classes share. // The code the following two classes share.
class IndexedDBCallbacksBase : public WebKit::WebIDBCallbacks { class IndexedDBCallbacksBase : public WebKit::WebIDBCallbacks {
...@@ -72,6 +69,25 @@ class IndexedDBCallbacks : public IndexedDBCallbacksBase { ...@@ -72,6 +69,25 @@ class IndexedDBCallbacks : public IndexedDBCallbacksBase {
DISALLOW_IMPLICIT_CONSTRUCTORS(IndexedDBCallbacks); DISALLOW_IMPLICIT_CONSTRUCTORS(IndexedDBCallbacks);
}; };
template <>
class IndexedDBCallbacks<WebKit::WebIDBTransaction>
: public IndexedDBCallbacksBase {
public:
IndexedDBCallbacks(
IndexedDBDispatcherHost* dispatcher_host, int32 response_id,
const GURL& origin_url)
: IndexedDBCallbacksBase(dispatcher_host, response_id),
origin_url_(origin_url) {
}
virtual void onSuccess(WebKit::WebIDBTransaction* idb_object);
const GURL& origin_url() const { return origin_url_; }
private:
const GURL& origin_url_;
DISALLOW_IMPLICIT_CONSTRUCTORS(IndexedDBCallbacks);
};
template <> template <>
class IndexedDBCallbacks<WebKit::WebIDBDatabase> class IndexedDBCallbacks<WebKit::WebIDBDatabase>
: public IndexedDBCallbacksBase { : public IndexedDBCallbacksBase {
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/message_loop_proxy.h" #include "base/message_loop_proxy.h"
#include "base/string_util.h" #include "base/string_util.h"
#include "base/task.h"
#include "base/utf_string_conversions.h" #include "base/utf_string_conversions.h"
#include "content/browser/browser_thread.h" #include "content/browser/browser_thread.h"
#include "content/browser/in_process_webkit/indexed_db_quota_client.h" #include "content/browser/in_process_webkit/indexed_db_quota_client.h"
...@@ -18,10 +19,12 @@ ...@@ -18,10 +19,12 @@
#include "third_party/WebKit/Source/WebKit/chromium/public/WebIDBFactory.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebIDBFactory.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h"
#include "webkit/database/database_util.h"
#include "webkit/glue/webkit_glue.h" #include "webkit/glue/webkit_glue.h"
#include "webkit/quota/quota_manager.h" #include "webkit/quota/quota_manager.h"
#include "webkit/quota/special_storage_policy.h" #include "webkit/quota/special_storage_policy.h"
using webkit_database::DatabaseUtil;
using WebKit::WebIDBDatabase; using WebKit::WebIDBDatabase;
using WebKit::WebIDBFactory; using WebKit::WebIDBFactory;
using WebKit::WebSecurityOrigin; using WebKit::WebSecurityOrigin;
...@@ -56,6 +59,41 @@ const FilePath::CharType IndexedDBContext::kIndexedDBDirectory[] = ...@@ -56,6 +59,41 @@ const FilePath::CharType IndexedDBContext::kIndexedDBDirectory[] =
const FilePath::CharType IndexedDBContext::kIndexedDBExtension[] = const FilePath::CharType IndexedDBContext::kIndexedDBExtension[] =
FILE_PATH_LITERAL(".leveldb"); FILE_PATH_LITERAL(".leveldb");
class IndexedDBContext::IndexedDBGetUsageAndQuotaCallback :
public quota::QuotaManager::GetUsageAndQuotaCallback {
public:
IndexedDBGetUsageAndQuotaCallback(IndexedDBContext* context,
const GURL& origin_url)
: context_(context),
origin_url_(origin_url) {
}
void Run(quota::QuotaStatusCode status, int64 usage, int64 quota) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(status == quota::kQuotaStatusOk || status == quota::kQuotaErrorAbort)
<< "status was " << status;
if (status == quota::kQuotaErrorAbort) {
// We seem to no longer care to wait around for the answer.
return;
}
BrowserThread::PostTask(BrowserThread::WEBKIT, FROM_HERE,
NewRunnableMethod(context_.get(),
&IndexedDBContext::GotUpdatedQuota,
origin_url_,
usage,
quota));
}
virtual void RunWithParams(
const Tuple3<quota::QuotaStatusCode, int64, int64>& params) {
Run(params.a, params.b, params.c);
}
private:
scoped_refptr<IndexedDBContext> context_;
const GURL origin_url_;
};
IndexedDBContext::IndexedDBContext( IndexedDBContext::IndexedDBContext(
WebKitContext* webkit_context, WebKitContext* webkit_context,
quota::SpecialStoragePolicy* special_storage_policy, quota::SpecialStoragePolicy* special_storage_policy,
...@@ -100,19 +138,19 @@ FilePath IndexedDBContext::GetIndexedDBFilePath( ...@@ -100,19 +138,19 @@ FilePath IndexedDBContext::GetIndexedDBFilePath(
return data_path_.Append(id.append(kIndexedDBExtension)); return data_path_.Append(id.append(kIndexedDBExtension));
} }
void IndexedDBContext::DeleteIndexedDBFile(const FilePath& file_path) { // Note: This is not called in response to a UI action in Content Settings. Only
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT)); // extension data deleter and quota manager currently call this.
// TODO(pastarmovj): Close all database connections that use that file. void IndexedDBContext::DeleteIndexedDBForOrigin(const GURL& origin_url) {
file_util::Delete(file_path, false);
}
void IndexedDBContext::DeleteIndexedDBForOrigin(const string16& origin_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT));
// TODO(pastarmovj): Remove this check once we are safe to delete any time. string16 origin_id = DatabaseUtil::GetOriginIdentifier(origin_url);
FilePath idb_file = GetIndexedDBFilePath(origin_id); FilePath idb_directory = GetIndexedDBFilePath(origin_id);
DCHECK_EQ(idb_file.BaseName().value().substr(0, strlen("chrome-extension")), if (idb_directory.BaseName().value().substr(0, strlen("chrome-extension")) ==
FILE_PATH_LITERAL("chrome-extension")); FILE_PATH_LITERAL("chrome-extension") ||
DeleteIndexedDBFile(GetIndexedDBFilePath(origin_id)); connection_count_.find(origin_url) == connection_count_.end()) {
EnsureDiskUsageCacheInitialized(origin_url);
file_util::Delete(idb_directory, true /*recursive*/);
QueryDiskAndUpdateQuotaUsage(origin_url);
}
} }
bool IndexedDBContext::IsUnlimitedStorageGranted( bool IndexedDBContext::IsUnlimitedStorageGranted(
...@@ -137,6 +175,114 @@ void IndexedDBContext::GetAllOriginIdentifiers( ...@@ -137,6 +175,114 @@ void IndexedDBContext::GetAllOriginIdentifiers(
} }
} }
int64 IndexedDBContext::GetOriginDiskUsage(const GURL& origin_url) {
return ResetDiskUsageCache(origin_url);
}
void IndexedDBContext::ConnectionOpened(const GURL& origin_url) {
if (quota_manager_proxy()) {
quota_manager_proxy()->NotifyStorageAccessed(
quota::QuotaClient::kIndexedDatabase, origin_url,
quota::kStorageTypeTemporary);
}
connection_count_[origin_url]++;
QueryAvailableQuota(origin_url);
EnsureDiskUsageCacheInitialized(origin_url);
}
void IndexedDBContext::ConnectionClosed(const GURL& origin_url) {
DCHECK(connection_count_[origin_url] > 0);
if (quota_manager_proxy()) {
quota_manager_proxy()->NotifyStorageAccessed(
quota::QuotaClient::kIndexedDatabase, origin_url,
quota::kStorageTypeTemporary);
}
connection_count_[origin_url]--;
if (connection_count_[origin_url] == 0) {
QueryDiskAndUpdateQuotaUsage(origin_url);
connection_count_.erase(origin_url);
}
}
void IndexedDBContext::TransactionComplete(const GURL& origin_url) {
DCHECK(connection_count_[origin_url] > 0);
QueryDiskAndUpdateQuotaUsage(origin_url);
QueryAvailableQuota(origin_url);
}
bool IndexedDBContext::WouldBeOverQuota(const GURL& origin_url,
int64 additional_bytes) {
if (space_available_map_.find(origin_url) == space_available_map_.end()) {
// We haven't heard back from the QuotaManager yet, just let it through.
return false;
}
bool over_quota = additional_bytes > space_available_map_[origin_url];
return over_quota;
}
bool IndexedDBContext::IsOverQuota(const GURL& origin_url) {
const int kOneAdditionalByte = 1;
return WouldBeOverQuota(origin_url, kOneAdditionalByte);
}
quota::QuotaManagerProxy* IndexedDBContext::quota_manager_proxy() { quota::QuotaManagerProxy* IndexedDBContext::quota_manager_proxy() {
return quota_manager_proxy_; return quota_manager_proxy_;
} }
int64 IndexedDBContext::ReadUsageFromDisk(const GURL& origin_url) const {
string16 origin_id = DatabaseUtil::GetOriginIdentifier(origin_url);
FilePath file_path = GetIndexedDBFilePath(origin_id);
return file_util::ComputeDirectorySize(file_path);
}
void IndexedDBContext::EnsureDiskUsageCacheInitialized(const GURL& origin_url) {
if (origin_size_map_.find(origin_url) == origin_size_map_.end())
ResetDiskUsageCache(origin_url);
}
void IndexedDBContext::QueryDiskAndUpdateQuotaUsage(const GURL& origin_url) {
int64 former_disk_usage = origin_size_map_[origin_url];
int64 current_disk_usage = ReadUsageFromDisk(origin_url);
int64 difference = current_disk_usage - former_disk_usage;
if (difference) {
origin_size_map_[origin_url] = current_disk_usage;
// quota_manager_proxy() is NULL in unit tests.
if (quota_manager_proxy())
quota_manager_proxy()->NotifyStorageModified(
quota::QuotaClient::kIndexedDatabase,
origin_url,
quota::kStorageTypeTemporary,
difference);
}
}
void IndexedDBContext::GotUpdatedQuota(const GURL& origin_url, int64 usage,
int64 quota) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT));
space_available_map_[origin_url] = quota - usage;
}
void IndexedDBContext::QueryAvailableQuota(const GURL& origin_url) {
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT));
if (quota_manager_proxy())
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
NewRunnableMethod(this, &IndexedDBContext::QueryAvailableQuota,
origin_url));
return;
}
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!quota_manager_proxy()->quota_manager())
return;
IndexedDBGetUsageAndQuotaCallback* callback =
new IndexedDBGetUsageAndQuotaCallback(this, origin_url);
quota_manager_proxy()->quota_manager()->GetUsageAndQuota(
origin_url,
quota::kStorageTypeTemporary,
callback);
}
int64 IndexedDBContext::ResetDiskUsageCache(const GURL& origin_url) {
origin_size_map_[origin_url] = ReadUsageFromDisk(origin_url);
return origin_size_map_[origin_url];
}
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#define CONTENT_BROWSER_IN_PROCESS_WEBKIT_INDEXED_DB_CONTEXT_H_ #define CONTENT_BROWSER_IN_PROCESS_WEBKIT_INDEXED_DB_CONTEXT_H_
#pragma once #pragma once
#include <map>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/file_path.h" #include "base/file_path.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
...@@ -53,16 +55,22 @@ class IndexedDBContext : public base::RefCountedThreadSafe<IndexedDBContext> { ...@@ -53,16 +55,22 @@ class IndexedDBContext : public base::RefCountedThreadSafe<IndexedDBContext> {
clear_local_state_on_exit_ = clear_local_state; clear_local_state_on_exit_ = clear_local_state;
} }
// Deletes a single indexed db file.
void DeleteIndexedDBFile(const FilePath& file_path);
// Deletes all indexed db files for the given origin. // Deletes all indexed db files for the given origin.
void DeleteIndexedDBForOrigin(const string16& origin_id); void DeleteIndexedDBForOrigin(const GURL& origin_url);
// Does a particular origin get unlimited storage? // Does a particular origin get unlimited storage?
bool IsUnlimitedStorageGranted(const GURL& origin) const; bool IsUnlimitedStorageGranted(const GURL& origin) const;
// Methods used in response to QuotaManager requests.
void GetAllOriginIdentifiers(std::vector<string16>* origin_ids); void GetAllOriginIdentifiers(std::vector<string16>* origin_ids);
int64 GetOriginDiskUsage(const GURL& origin_url);
// Methods called by IndexedDBDispatcherHost for quota support.
void ConnectionOpened(const GURL& origin_url);
void ConnectionClosed(const GURL& origin_url);
void TransactionComplete(const GURL& origin_url);
bool WouldBeOverQuota(const GURL& origin_url, int64 additional_bytes);
bool IsOverQuota(const GURL& origin_url);
quota::QuotaManagerProxy* quota_manager_proxy(); quota::QuotaManagerProxy* quota_manager_proxy();
...@@ -72,6 +80,16 @@ class IndexedDBContext : public base::RefCountedThreadSafe<IndexedDBContext> { ...@@ -72,6 +80,16 @@ class IndexedDBContext : public base::RefCountedThreadSafe<IndexedDBContext> {
#endif #endif
private: private:
typedef std::map<GURL, int64> OriginToSizeMap;
class IndexedDBGetUsageAndQuotaCallback;
int64 ReadUsageFromDisk(const GURL& origin_url) const;
void EnsureDiskUsageCacheInitialized(const GURL& origin_url);
void QueryDiskAndUpdateQuotaUsage(const GURL& origin_url);
void GotUpdatedQuota(const GURL& origin_url, int64 usage, int64 quota);
void QueryAvailableQuota(const GURL& origin_url);
int64 ResetDiskUsageCache(const GURL& origin_url);
scoped_ptr<WebKit::WebIDBFactory> idb_factory_; scoped_ptr<WebKit::WebIDBFactory> idb_factory_;
// Path where the indexed db data is stored // Path where the indexed db data is stored
...@@ -84,6 +102,10 @@ class IndexedDBContext : public base::RefCountedThreadSafe<IndexedDBContext> { ...@@ -84,6 +102,10 @@ class IndexedDBContext : public base::RefCountedThreadSafe<IndexedDBContext> {
scoped_refptr<quota::QuotaManagerProxy> quota_manager_proxy_; scoped_refptr<quota::QuotaManagerProxy> quota_manager_proxy_;
OriginToSizeMap origin_size_map_;
OriginToSizeMap space_available_map_;
std::map<GURL, unsigned int> connection_count_;
DISALLOW_COPY_AND_ASSIGN(IndexedDBContext); DISALLOW_COPY_AND_ASSIGN(IndexedDBContext);
}; };
......
...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebVector.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebVector.h"
#include "webkit/glue/webkit_glue.h" #include "webkit/glue/webkit_glue.h"
#include "webkit/quota/quota_manager.h"
using WebKit::WebDOMStringList; using WebKit::WebDOMStringList;
using WebKit::WebExceptionCode; using WebKit::WebExceptionCode;
...@@ -155,7 +154,8 @@ int32 IndexedDBDispatcherHost::Add(WebIDBDatabase* idb_database, ...@@ -155,7 +154,8 @@ int32 IndexedDBDispatcherHost::Add(WebIDBDatabase* idb_database,
return 0; return 0;
} }
int32 idb_database_id = database_dispatcher_host_->map_.Add(idb_database); int32 idb_database_id = database_dispatcher_host_->map_.Add(idb_database);
database_dispatcher_host_->url_map_[idb_database_id] = origin_url; Context()->ConnectionOpened(origin_url);
database_dispatcher_host_->database_url_map_[idb_database_id] = origin_url;
return idb_database_id; return idb_database_id;
} }
...@@ -179,18 +179,21 @@ int32 IndexedDBDispatcherHost::Add(WebIDBObjectStore* idb_object_store) { ...@@ -179,18 +179,21 @@ int32 IndexedDBDispatcherHost::Add(WebIDBObjectStore* idb_object_store) {
return object_store_dispatcher_host_->map_.Add(idb_object_store); return object_store_dispatcher_host_->map_.Add(idb_object_store);
} }
int32 IndexedDBDispatcherHost::Add(WebIDBTransaction* idb_transaction) { int32 IndexedDBDispatcherHost::Add(WebIDBTransaction* idb_transaction,
const GURL& url) {
if (!transaction_dispatcher_host_.get()) { if (!transaction_dispatcher_host_.get()) {
delete idb_transaction; delete idb_transaction;
return 0; return 0;
} }
int32 id = transaction_dispatcher_host_->map_.Add(idb_transaction); int32 id = transaction_dispatcher_host_->map_.Add(idb_transaction);
idb_transaction->setCallbacks(new IndexedDBTransactionCallbacks(this, id)); idb_transaction->setCallbacks(new IndexedDBTransactionCallbacks(this, id));
transaction_dispatcher_host_->transaction_url_map_[id] = url;
return id; return id;
} }
void IndexedDBDispatcherHost::OnIDBFactoryOpen( void IndexedDBDispatcherHost::OnIDBFactoryOpen(
const IndexedDBHostMsg_FactoryOpen_Params& params) { const IndexedDBHostMsg_FactoryOpen_Params& params) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT));
FilePath base_path = webkit_context_->data_path(); FilePath base_path = webkit_context_->data_path();
FilePath indexed_db_path; FilePath indexed_db_path;
if (!base_path.empty()) { if (!base_path.empty()) {
...@@ -226,6 +229,8 @@ void IndexedDBDispatcherHost::OnIDBFactoryOpen( ...@@ -226,6 +229,8 @@ void IndexedDBDispatcherHost::OnIDBFactoryOpen(
backingStoreType = WebKit::WebIDBFactory::SQLiteBackingStore; backingStoreType = WebKit::WebIDBFactory::SQLiteBackingStore;
} }
// TODO(dgrogan): Don't let a non-existing database be opened (and therefore
// created) if this origin is already over quota.
Context()->GetIDBFactory()->open( Context()->GetIDBFactory()->open(
params.name, params.name,
new IndexedDBCallbacks<WebIDBDatabase>(this, params.response_id, new IndexedDBCallbacks<WebIDBDatabase>(this, params.response_id,
...@@ -255,6 +260,11 @@ void IndexedDBDispatcherHost::OnIDBFactoryDeleteDatabase( ...@@ -255,6 +260,11 @@ void IndexedDBDispatcherHost::OnIDBFactoryDeleteDatabase(
webkit_glue::FilePathToWebString(indexed_db_path)); webkit_glue::FilePathToWebString(indexed_db_path));
} }
void IndexedDBDispatcherHost::TransactionComplete(int32 transaction_id) {
Context()->TransactionComplete(
transaction_dispatcher_host_->transaction_url_map_[transaction_id]);
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Helper templates. // Helper templates.
// //
...@@ -301,6 +311,10 @@ IndexedDBDispatcherHost::DatabaseDispatcherHost::DatabaseDispatcherHost( ...@@ -301,6 +311,10 @@ IndexedDBDispatcherHost::DatabaseDispatcherHost::DatabaseDispatcherHost(
} }
IndexedDBDispatcherHost::DatabaseDispatcherHost::~DatabaseDispatcherHost() { IndexedDBDispatcherHost::DatabaseDispatcherHost::~DatabaseDispatcherHost() {
for (WebIDBObjectIDToURLMap::iterator iter = database_url_map_.begin();
iter != database_url_map_.end(); iter++) {
parent_->Context()->ConnectionClosed(iter->second);
}
} }
bool IndexedDBDispatcherHost::DatabaseDispatcherHost::OnMessageReceived( bool IndexedDBDispatcherHost::DatabaseDispatcherHost::OnMessageReceived(
...@@ -371,6 +385,10 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateObjectStore( ...@@ -371,6 +385,10 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnCreateObjectStore(
params.name, params.key_path, params.auto_increment, params.name, params.key_path, params.auto_increment,
*idb_transaction, *ec); *idb_transaction, *ec);
*object_store_id = *ec ? 0 : parent_->Add(object_store); *object_store_id = *ec ? 0 : parent_->Add(object_store);
if (parent_->Context()->IsOverQuota(
database_url_map_[params.idb_database_id])) {
idb_transaction->abort();
}
} }
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteObjectStore( void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDeleteObjectStore(
...@@ -404,7 +422,8 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetVersion( ...@@ -404,7 +422,8 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnSetVersion(
*ec = 0; *ec = 0;
idb_database->setVersion( idb_database->setVersion(
version, version,
new IndexedDBCallbacks<WebIDBTransaction>(parent_, response_id), new IndexedDBCallbacks<WebIDBTransaction>(parent_, response_id,
database_url_map_[idb_database_id]),
*ec); *ec);
} }
...@@ -430,7 +449,8 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnTransaction( ...@@ -430,7 +449,8 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnTransaction(
WebIDBTransaction* transaction = database->transaction( WebIDBTransaction* transaction = database->transaction(
object_stores, mode, timeout, *ec); object_stores, mode, timeout, *ec);
DCHECK(!transaction != !*ec); DCHECK(!transaction != !*ec);
*idb_transaction_id = *ec ? 0 : parent_->Add(transaction); *idb_transaction_id =
*ec ? 0 : parent_->Add(transaction, database_url_map_[idb_database_id]);
} }
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnOpen( void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnOpen(
...@@ -445,14 +465,12 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnClose( ...@@ -445,14 +465,12 @@ void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnClose(
WebIDBDatabase* database = parent_->GetOrTerminateProcess( WebIDBDatabase* database = parent_->GetOrTerminateProcess(
&map_, idb_database_id); &map_, idb_database_id);
database->close(); database->close();
parent_->Context()->quota_manager_proxy()->NotifyStorageAccessed(
quota::QuotaClient::kIndexedDatabase, url_map_[idb_database_id],
quota::kStorageTypeTemporary);
url_map_.erase(idb_database_id);
} }
void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDestroyed( void IndexedDBDispatcherHost::DatabaseDispatcherHost::OnDestroyed(
int32 object_id) { int32 object_id) {
parent_->Context()->ConnectionClosed(database_url_map_[object_id]);
database_url_map_.erase(object_id);
parent_->DestroyObject(&map_, object_id); parent_->DestroyObject(&map_, object_id);
} }
...@@ -704,6 +722,12 @@ void IndexedDBDispatcherHost::ObjectStoreDispatcherHost::OnPut( ...@@ -704,6 +722,12 @@ void IndexedDBDispatcherHost::ObjectStoreDispatcherHost::OnPut(
new IndexedDBCallbacks<WebIDBKey>(parent_, params.response_id)); new IndexedDBCallbacks<WebIDBKey>(parent_, params.response_id));
idb_object_store->put(params.serialized_value, params.key, params.put_mode, idb_object_store->put(params.serialized_value, params.key, params.put_mode,
callbacks.release(), *idb_transaction, *ec); callbacks.release(), *idb_transaction, *ec);
if (*ec)
return;
int64 size = UTF16ToUTF8(params.serialized_value.data()).size();
WebIDBTransactionIDToSizeMap* map =
&parent_->transaction_dispatcher_host_->transaction_size_map_;
(*map)[params.transaction_id] += size;
} }
void IndexedDBDispatcherHost::ObjectStoreDispatcherHost::OnDelete( void IndexedDBDispatcherHost::ObjectStoreDispatcherHost::OnDelete(
...@@ -761,6 +785,12 @@ void IndexedDBDispatcherHost::ObjectStoreDispatcherHost::OnCreateIndex( ...@@ -761,6 +785,12 @@ void IndexedDBDispatcherHost::ObjectStoreDispatcherHost::OnCreateIndex(
WebIDBIndex* index = idb_object_store->createIndex( WebIDBIndex* index = idb_object_store->createIndex(
params.name, params.key_path, params.unique, *idb_transaction, *ec); params.name, params.key_path, params.unique, *idb_transaction, *ec);
*index_id = *ec ? 0 : parent_->Add(index); *index_id = *ec ? 0 : parent_->Add(index);
WebIDBObjectIDToURLMap* transaction_url_map =
&parent_->transaction_dispatcher_host_->transaction_url_map_;
if (parent_->Context()->IsOverQuota(
(*transaction_url_map)[params.transaction_id])) {
idb_transaction->abort();
}
} }
void IndexedDBDispatcherHost::ObjectStoreDispatcherHost::OnIndex( void IndexedDBDispatcherHost::ObjectStoreDispatcherHost::OnIndex(
...@@ -1033,10 +1063,19 @@ void IndexedDBDispatcherHost:: ...@@ -1033,10 +1063,19 @@ void IndexedDBDispatcherHost::
if (!idb_transaction) if (!idb_transaction)
return; return;
// TODO(dgrogan): Tell the page the transaction aborted because of quota.
if (parent_->Context()->WouldBeOverQuota(
transaction_url_map_[transaction_id],
transaction_size_map_[transaction_id])) {
idb_transaction->abort();
return;
}
idb_transaction->didCompleteTaskEvents(); idb_transaction->didCompleteTaskEvents();
} }
void IndexedDBDispatcherHost::TransactionDispatcherHost::OnDestroyed( void IndexedDBDispatcherHost::TransactionDispatcherHost::OnDestroyed(
int32 object_id) { int32 object_id) {
transaction_size_map_.erase(object_id);
transaction_url_map_.erase(object_id);
parent_->DestroyObject(&map_, object_id); parent_->DestroyObject(&map_, object_id);
} }
...@@ -45,6 +45,8 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter { ...@@ -45,6 +45,8 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
virtual bool OnMessageReceived(const IPC::Message& message, virtual bool OnMessageReceived(const IPC::Message& message,
bool* message_was_ok); bool* message_was_ok);
void TransactionComplete(int32 transaction_id);
// A shortcut for accessing our context. // A shortcut for accessing our context.
IndexedDBContext* Context() { IndexedDBContext* Context() {
return webkit_context_->indexed_db_context(); return webkit_context_->indexed_db_context();
...@@ -56,7 +58,7 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter { ...@@ -56,7 +58,7 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
int32 Add(WebKit::WebIDBDatabase* idb_database, const GURL& origin_url); int32 Add(WebKit::WebIDBDatabase* idb_database, const GURL& origin_url);
int32 Add(WebKit::WebIDBIndex* idb_index); int32 Add(WebKit::WebIDBIndex* idb_index);
int32 Add(WebKit::WebIDBObjectStore* idb_object_store); int32 Add(WebKit::WebIDBObjectStore* idb_object_store);
int32 Add(WebKit::WebIDBTransaction* idb_transaction); int32 Add(WebKit::WebIDBTransaction* idb_transaction, const GURL& origin_url);
private: private:
virtual ~IndexedDBDispatcherHost(); virtual ~IndexedDBDispatcherHost();
...@@ -82,6 +84,10 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter { ...@@ -82,6 +84,10 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
template <typename ObjectType> template <typename ObjectType>
void DestroyObject(IDMap<ObjectType, IDMapOwnPointer>* map, int32 object_id); void DestroyObject(IDMap<ObjectType, IDMapOwnPointer>* map, int32 object_id);
// Used in nested classes.
typedef std::map<int32, GURL> WebIDBObjectIDToURLMap;
typedef std::map<int32, int64> WebIDBTransactionIDToSizeMap;
class DatabaseDispatcherHost { class DatabaseDispatcherHost {
public: public:
explicit DatabaseDispatcherHost(IndexedDBDispatcherHost* parent); explicit DatabaseDispatcherHost(IndexedDBDispatcherHost* parent);
...@@ -116,8 +122,7 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter { ...@@ -116,8 +122,7 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
IndexedDBDispatcherHost* parent_; IndexedDBDispatcherHost* parent_;
IDMap<WebKit::WebIDBDatabase, IDMapOwnPointer> map_; IDMap<WebKit::WebIDBDatabase, IDMapOwnPointer> map_;
typedef std::map<int32, GURL> WebIDBDatabaseIDToURLMap; WebIDBObjectIDToURLMap database_url_map_;
WebIDBDatabaseIDToURLMap url_map_;
}; };
class IndexDispatcherHost { class IndexDispatcherHost {
...@@ -254,6 +259,8 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter { ...@@ -254,6 +259,8 @@ class IndexedDBDispatcherHost : public BrowserMessageFilter {
IndexedDBDispatcherHost* parent_; IndexedDBDispatcherHost* parent_;
typedef IDMap<WebKit::WebIDBTransaction, IDMapOwnPointer> MapType; typedef IDMap<WebKit::WebIDBTransaction, IDMapOwnPointer> MapType;
MapType map_; MapType map_;
WebIDBObjectIDToURLMap transaction_url_map_;
WebIDBTransactionIDToSizeMap transaction_size_map_;
}; };
// Data shared between renderer processes with the same profile. // Data shared between renderer processes with the same profile.
......
...@@ -31,6 +31,30 @@ class IndexedDBQuotaClient::HelperTask : public quota::QuotaThreadTask { ...@@ -31,6 +31,30 @@ class IndexedDBQuotaClient::HelperTask : public quota::QuotaThreadTask {
scoped_refptr<IndexedDBContext> indexed_db_context_; scoped_refptr<IndexedDBContext> indexed_db_context_;
}; };
class IndexedDBQuotaClient::DeleteOriginTask : public HelperTask {
public:
DeleteOriginTask(IndexedDBQuotaClient* client,
base::MessageLoopProxy* webkit_thread_message_loop,
const GURL& origin_url,
DeletionCallback* callback)
: HelperTask(client, webkit_thread_message_loop),
origin_url_(origin_url), callback_(callback) {
}
private:
virtual void RunOnTargetThread() OVERRIDE {
indexed_db_context_->DeleteIndexedDBForOrigin(origin_url_);
}
virtual void Aborted() OVERRIDE {
callback_.reset();
}
virtual void Completed() OVERRIDE {
callback_->Run(quota::kQuotaStatusOk);
callback_.reset();
}
GURL origin_url_;
scoped_ptr<DeletionCallback> callback_;
};
class IndexedDBQuotaClient::GetOriginUsageTask : public HelperTask { class IndexedDBQuotaClient::GetOriginUsageTask : public HelperTask {
public: public:
GetOriginUsageTask( GetOriginUsageTask(
...@@ -43,12 +67,10 @@ class IndexedDBQuotaClient::GetOriginUsageTask : public HelperTask { ...@@ -43,12 +67,10 @@ class IndexedDBQuotaClient::GetOriginUsageTask : public HelperTask {
private: private:
virtual void RunOnTargetThread() OVERRIDE { virtual void RunOnTargetThread() OVERRIDE {
string16 origin_id = DatabaseUtil::GetOriginIdentifier(origin_url_); usage_ = indexed_db_context_->GetOriginDiskUsage(origin_url_);
FilePath file_path = indexed_db_context_->GetIndexedDBFilePath(origin_id);
usage_ = 0;
usage_ = file_util::ComputeDirectorySize(file_path);
} }
virtual void Completed() OVERRIDE { virtual void Completed() OVERRIDE {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
client_->DidGetOriginUsage(origin_url_, usage_); client_->DidGetOriginUsage(origin_url_, usage_);
} }
GURL origin_url_; GURL origin_url_;
...@@ -211,9 +233,16 @@ void IndexedDBQuotaClient::GetOriginsForHost( ...@@ -211,9 +233,16 @@ void IndexedDBQuotaClient::GetOriginsForHost(
void IndexedDBQuotaClient::DeleteOriginData(const GURL& origin, void IndexedDBQuotaClient::DeleteOriginData(const GURL& origin,
quota::StorageType type, quota::StorageType type,
DeletionCallback* callback) { DeletionCallback* callback) {
// TODO(tzik): implement me if (type != quota::kStorageTypeTemporary) {
callback->Run(quota::kQuotaErrorNotSupported); callback->Run(quota::kQuotaErrorNotSupported);
delete callback; return;
}
scoped_refptr<DeleteOriginTask> task(
new DeleteOriginTask(this,
webkit_thread_message_loop_,
origin,
callback));
task->Start();
} }
void IndexedDBQuotaClient::DidGetOriginUsage( void IndexedDBQuotaClient::DidGetOriginUsage(
......
...@@ -47,6 +47,7 @@ class IndexedDBQuotaClient : public quota::QuotaClient, ...@@ -47,6 +47,7 @@ class IndexedDBQuotaClient : public quota::QuotaClient,
class GetOriginsTaskBase; class GetOriginsTaskBase;
class GetAllOriginsTask; class GetAllOriginsTask;
class GetOriginsForHostTask; class GetOriginsForHostTask;
class DeleteOriginTask;
typedef quota::CallbackQueueMap1 typedef quota::CallbackQueueMap1
<GetUsageCallback*, <GetUsageCallback*,
......
...@@ -37,7 +37,8 @@ class IndexedDBQuotaClientTest : public TestingBrowserProcessTest { ...@@ -37,7 +37,8 @@ class IndexedDBQuotaClientTest : public TestingBrowserProcessTest {
usage_(0), usage_(0),
callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
message_loop_(MessageLoop::TYPE_IO), message_loop_(MessageLoop::TYPE_IO),
webkit_thread_(BrowserThread::WEBKIT, &message_loop_) { webkit_thread_(BrowserThread::WEBKIT, &message_loop_),
io_thread_(BrowserThread::IO, &message_loop_) {
TestingProfile profile; TestingProfile profile;
idb_context_ = profile.GetWebKitContext()->indexed_db_context(); idb_context_ = profile.GetWebKitContext()->indexed_db_context();
setup_temp_dir(); setup_temp_dir();
...@@ -96,6 +97,15 @@ class IndexedDBQuotaClientTest : public TestingBrowserProcessTest { ...@@ -96,6 +97,15 @@ class IndexedDBQuotaClientTest : public TestingBrowserProcessTest {
return origins_; return origins_;
} }
quota::QuotaStatusCode DeleteOrigin(quota::QuotaClient* client,
const GURL& origin_url) {
delete_status_ = quota::kQuotaStatusUnknown;
client->DeleteOriginData(origin_url, kTemp, callback_factory_.NewCallback(
&IndexedDBQuotaClientTest::OnDeleteOriginComplete));
MessageLoop::current()->RunAllPending();
return delete_status_;
}
IndexedDBContext* idb_context() { return idb_context_.get(); } IndexedDBContext* idb_context() { return idb_context_.get(); }
void SetFileSizeTo(const FilePath& path, int size) { void SetFileSizeTo(const FilePath& path, int size) {
...@@ -125,6 +135,10 @@ class IndexedDBQuotaClientTest : public TestingBrowserProcessTest { ...@@ -125,6 +135,10 @@ class IndexedDBQuotaClientTest : public TestingBrowserProcessTest {
type_ = type_; type_ = type_;
} }
void OnDeleteOriginComplete(quota::QuotaStatusCode code) {
delete_status_ = code;
}
ScopedTempDir temp_dir_; ScopedTempDir temp_dir_;
int64 usage_; int64 usage_;
std::set<GURL> origins_; std::set<GURL> origins_;
...@@ -133,6 +147,8 @@ class IndexedDBQuotaClientTest : public TestingBrowserProcessTest { ...@@ -133,6 +147,8 @@ class IndexedDBQuotaClientTest : public TestingBrowserProcessTest {
base::ScopedCallbackFactory<IndexedDBQuotaClientTest> callback_factory_; base::ScopedCallbackFactory<IndexedDBQuotaClientTest> callback_factory_;
MessageLoop message_loop_; MessageLoop message_loop_;
BrowserThread webkit_thread_; BrowserThread webkit_thread_;
BrowserThread io_thread_;
quota::QuotaStatusCode delete_status_;
}; };
...@@ -196,3 +212,19 @@ TEST_F(IndexedDBQuotaClientTest, GetOriginsForType) { ...@@ -196,3 +212,19 @@ TEST_F(IndexedDBQuotaClientTest, GetOriginsForType) {
EXPECT_TRUE(GetOriginsForType(&client, kPerm).empty()); EXPECT_TRUE(GetOriginsForType(&client, kPerm).empty());
} }
TEST_F(IndexedDBQuotaClientTest, DeleteOrigin) {
IndexedDBQuotaClient client(
base::MessageLoopProxy::CreateForCurrentThread(),
idb_context());
AddFakeIndexedDB(kOriginA, 1000);
AddFakeIndexedDB(kOriginB, 50);
EXPECT_EQ(1000, GetOriginUsage(&client, kOriginA, kTemp));
EXPECT_EQ(50, GetOriginUsage(&client, kOriginB, kTemp));
quota::QuotaStatusCode delete_status = DeleteOrigin(&client, kOriginA);
EXPECT_EQ(quota::kQuotaStatusOk, delete_status);
EXPECT_EQ(0, GetOriginUsage(&client, kOriginA, kTemp));
EXPECT_EQ(50, GetOriginUsage(&client, kOriginB, kTemp));
}
...@@ -23,6 +23,7 @@ void IndexedDBTransactionCallbacks::onAbort() { ...@@ -23,6 +23,7 @@ void IndexedDBTransactionCallbacks::onAbort() {
} }
void IndexedDBTransactionCallbacks::onComplete() { void IndexedDBTransactionCallbacks::onComplete() {
dispatcher_host_->TransactionComplete(transaction_id_);
dispatcher_host_->Send( dispatcher_host_->Send(
new IndexedDBMsg_TransactionCallbacksComplete(transaction_id_)); new IndexedDBMsg_TransactionCallbacksComplete(transaction_id_));
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment