summaryrefslogtreecommitdiffstats
path: root/content/browser/in_process_webkit/indexed_db_context_impl.cc
diff options
context:
space:
mode:
authorjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-23 18:19:28 +0000
committerjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-23 18:19:28 +0000
commit35cc399e87932dea35a3cc7eb156cacbbff06b40 (patch)
treed7de7d46dd3eb7004a5c36060c03dedaac4cca7f /content/browser/in_process_webkit/indexed_db_context_impl.cc
parent76d192b7c4ac31481129253c82aff596cf7c4932 (diff)
downloadchromium_src-35cc399e87932dea35a3cc7eb156cacbbff06b40.zip
chromium_src-35cc399e87932dea35a3cc7eb156cacbbff06b40.tar.gz
chromium_src-35cc399e87932dea35a3cc7eb156cacbbff06b40.tar.bz2
Add Content API around DOMStorageContext and IndexedDBContext. In a followup change I will remove WebKitContext usage from chrome, and then in another change probably remove it altogether.
BUG=98716 Review URL: https://chromiumcodereview.appspot.com/9447010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@123277 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/browser/in_process_webkit/indexed_db_context_impl.cc')
-rw-r--r--content/browser/in_process_webkit/indexed_db_context_impl.cc365
1 files changed, 365 insertions, 0 deletions
diff --git a/content/browser/in_process_webkit/indexed_db_context_impl.cc b/content/browser/in_process_webkit/indexed_db_context_impl.cc
new file mode 100644
index 0000000..4c841a0
--- /dev/null
+++ b/content/browser/in_process_webkit/indexed_db_context_impl.cc
@@ -0,0 +1,365 @@
+// 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 "content/browser/in_process_webkit/indexed_db_context_impl.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/message_loop_proxy.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "content/browser/in_process_webkit/indexed_db_quota_client.h"
+#include "content/browser/in_process_webkit/webkit_context.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/common/content_switches.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCString.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebIDBDatabase.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/platform/WebString.h"
+#include "webkit/database/database_util.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/quota/quota_manager.h"
+#include "webkit/quota/special_storage_policy.h"
+
+using content::BrowserContext;
+using content::BrowserThread;
+using content::IndexedDBContext;
+using webkit_database::DatabaseUtil;
+using WebKit::WebIDBDatabase;
+using WebKit::WebIDBFactory;
+using WebKit::WebSecurityOrigin;
+
+const FilePath::CharType IndexedDBContextImpl::kIndexedDBDirectory[] =
+ FILE_PATH_LITERAL("IndexedDB");
+
+const FilePath::CharType IndexedDBContextImpl::kIndexedDBExtension[] =
+ FILE_PATH_LITERAL(".leveldb");
+
+namespace {
+
+void GetAllOriginsAndPaths(
+ const FilePath& indexeddb_path,
+ std::vector<GURL>* origins,
+ std::vector<FilePath>* file_paths) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
+ if (indexeddb_path.empty())
+ return;
+ file_util::FileEnumerator file_enumerator(indexeddb_path,
+ false, file_util::FileEnumerator::DIRECTORIES);
+ for (FilePath file_path = file_enumerator.Next(); !file_path.empty();
+ file_path = file_enumerator.Next()) {
+ if (file_path.Extension() == IndexedDBContextImpl::kIndexedDBExtension) {
+ WebKit::WebString origin_id_webstring =
+ webkit_glue::FilePathToWebString(file_path.BaseName());
+ origins->push_back(
+ DatabaseUtil::GetOriginFromIdentifier(origin_id_webstring));
+ if (file_paths)
+ file_paths->push_back(file_path);
+ }
+ }
+}
+
+// If clear_all_databases is true, deletes all databases not protected by
+// special storage policy. Otherwise deletes session-only databases.
+void ClearLocalState(
+ const FilePath& indexeddb_path,
+ bool clear_all_databases,
+ scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
+ std::vector<GURL> origins;
+ std::vector<FilePath> file_paths;
+ GetAllOriginsAndPaths(indexeddb_path, &origins, &file_paths);
+ DCHECK_EQ(origins.size(), file_paths.size());
+ std::vector<FilePath>::const_iterator file_path_iter = file_paths.begin();
+ for (std::vector<GURL>::const_iterator iter = origins.begin();
+ iter != origins.end(); ++iter, ++file_path_iter) {
+ if (!clear_all_databases &&
+ !special_storage_policy->IsStorageSessionOnly(*iter)) {
+ continue;
+ }
+ if (special_storage_policy.get() &&
+ special_storage_policy->IsStorageProtected(*iter))
+ continue;
+ file_util::Delete(*file_path_iter, true);
+ }
+}
+
+} // namespace
+
+IndexedDBContext* IndexedDBContext::GetForBrowserContext(
+ BrowserContext* context) {
+ return BrowserContext::GetWebKitContext(context)->indexed_db_context();
+}
+
+IndexedDBContextImpl::IndexedDBContextImpl(
+ WebKitContext* webkit_context,
+ quota::SpecialStoragePolicy* special_storage_policy,
+ quota::QuotaManagerProxy* quota_manager_proxy,
+ base::MessageLoopProxy* webkit_thread_loop)
+ : clear_local_state_on_exit_(false),
+ save_session_state_(false),
+ special_storage_policy_(special_storage_policy),
+ quota_manager_proxy_(quota_manager_proxy) {
+ if (!webkit_context->is_incognito())
+ data_path_ = webkit_context->data_path().Append(kIndexedDBDirectory);
+ if (quota_manager_proxy &&
+ !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess)) {
+ quota_manager_proxy->RegisterClient(
+ new IndexedDBQuotaClient(webkit_thread_loop, this));
+ }
+}
+
+IndexedDBContextImpl::~IndexedDBContextImpl() {
+ WebKit::WebIDBFactory* factory = idb_factory_.release();
+ if (factory) {
+ if (!BrowserThread::DeleteSoon(BrowserThread::WEBKIT_DEPRECATED,
+ FROM_HERE, factory))
+ delete factory;
+ }
+
+ if (data_path_.empty())
+ return;
+
+ if (save_session_state_)
+ return;
+
+ bool has_session_only_databases =
+ special_storage_policy_.get() &&
+ special_storage_policy_->HasSessionOnlyOrigins();
+
+ // Clearning only session-only databases, and there are none.
+ if (!clear_local_state_on_exit_ && !has_session_only_databases)
+ return;
+
+ // No WEBKIT thread here means we are running in a unit test where no clean
+ // up is needed.
+ BrowserThread::PostTask(
+ BrowserThread::WEBKIT_DEPRECATED, FROM_HERE,
+ base::Bind(&ClearLocalState, data_path_, clear_local_state_on_exit_,
+ special_storage_policy_));
+}
+
+WebIDBFactory* IndexedDBContextImpl::GetIDBFactory() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
+ if (!idb_factory_.get()) {
+ // Prime our cache of origins with existing databases so we can
+ // detect when dbs are newly created.
+ GetOriginSet();
+ idb_factory_.reset(WebIDBFactory::create());
+ }
+ return idb_factory_.get();
+}
+
+void IndexedDBContextImpl::DeleteForOrigin(const GURL& origin_url) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
+ if (data_path_.empty() || !IsInOriginSet(origin_url))
+ return;
+ // TODO(michaeln): When asked to delete an origin with open connections,
+ // forcibly close those connections then delete.
+ if (connection_count_.find(origin_url) == connection_count_.end()) {
+ string16 origin_id = DatabaseUtil::GetOriginIdentifier(origin_url);
+ FilePath idb_directory = GetIndexedDBFilePath(origin_id);
+ EnsureDiskUsageCacheInitialized(origin_url);
+ bool deleted = file_util::Delete(idb_directory, true /*recursive*/);
+ QueryDiskAndUpdateQuotaUsage(origin_url);
+ if (deleted) {
+ RemoveFromOriginSet(origin_url);
+ origin_size_map_.erase(origin_url);
+ space_available_map_.erase(origin_url);
+ }
+ }
+}
+
+FilePath IndexedDBContextImpl::GetFilePathForTesting(
+ const string16& origin_id) const {
+ return GetIndexedDBFilePath(origin_id);
+}
+
+std::vector<GURL> IndexedDBContextImpl::GetAllOrigins() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
+ std::vector<GURL> origins;
+ std::set<GURL>* origins_set = GetOriginSet();
+ for (std::set<GURL>::const_iterator iter = origins_set->begin();
+ iter != origins_set->end(); ++iter) {
+ origins.push_back(*iter);
+ }
+ return origins;
+}
+
+int64 IndexedDBContextImpl::GetOriginDiskUsage(const GURL& origin_url) {
+ if (data_path_.empty() || !IsInOriginSet(origin_url))
+ return 0;
+ EnsureDiskUsageCacheInitialized(origin_url);
+ return origin_size_map_[origin_url];
+}
+
+base::Time IndexedDBContextImpl::GetOriginLastModified(const GURL& origin_url) {
+ if (data_path_.empty() || !IsInOriginSet(origin_url))
+ return base::Time();
+ string16 origin_id = DatabaseUtil::GetOriginIdentifier(origin_url);
+ FilePath idb_directory = GetIndexedDBFilePath(origin_id);
+ base::PlatformFileInfo file_info;
+ if (!file_util::GetFileInfo(idb_directory, &file_info))
+ return base::Time();
+ return file_info.last_modified;
+}
+
+void IndexedDBContextImpl::ConnectionOpened(const GURL& origin_url) {
+ if (quota_manager_proxy()) {
+ quota_manager_proxy()->NotifyStorageAccessed(
+ quota::QuotaClient::kIndexedDatabase, origin_url,
+ quota::kStorageTypeTemporary);
+ }
+ connection_count_[origin_url]++;
+ if (AddToOriginSet(origin_url)) {
+ // A newly created db, notify the quota system.
+ QueryDiskAndUpdateQuotaUsage(origin_url);
+ } else {
+ EnsureDiskUsageCacheInitialized(origin_url);
+ }
+ QueryAvailableQuota(origin_url);
+}
+
+void IndexedDBContextImpl::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 IndexedDBContextImpl::TransactionComplete(const GURL& origin_url) {
+ DCHECK(connection_count_[origin_url] > 0);
+ QueryDiskAndUpdateQuotaUsage(origin_url);
+ QueryAvailableQuota(origin_url);
+}
+
+FilePath IndexedDBContextImpl::GetIndexedDBFilePath(
+ const string16& origin_id) const {
+ DCHECK(!data_path_.empty());
+ FilePath::StringType id =
+ webkit_glue::WebStringToFilePathString(origin_id).append(
+ FILE_PATH_LITERAL(".indexeddb"));
+ return data_path_.Append(id.append(kIndexedDBExtension));
+}
+
+bool IndexedDBContextImpl::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 IndexedDBContextImpl::IsOverQuota(const GURL& origin_url) {
+ const int kOneAdditionalByte = 1;
+ return WouldBeOverQuota(origin_url, kOneAdditionalByte);
+}
+
+quota::QuotaManagerProxy* IndexedDBContextImpl::quota_manager_proxy() {
+ return quota_manager_proxy_;
+}
+
+int64 IndexedDBContextImpl::ReadUsageFromDisk(const GURL& origin_url) const {
+ if (data_path_.empty())
+ return 0;
+ string16 origin_id = DatabaseUtil::GetOriginIdentifier(origin_url);
+ FilePath file_path = GetIndexedDBFilePath(origin_id);
+ return file_util::ComputeDirectorySize(file_path);
+}
+
+void IndexedDBContextImpl::EnsureDiskUsageCacheInitialized(
+ const GURL& origin_url) {
+ if (origin_size_map_.find(origin_url) == origin_size_map_.end())
+ origin_size_map_[origin_url] = ReadUsageFromDisk(origin_url);
+}
+
+void IndexedDBContextImpl::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 IndexedDBContextImpl::GotUsageAndQuota(const GURL& origin_url,
+ 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_DEPRECATED, FROM_HERE,
+ base::Bind(&IndexedDBContextImpl::GotUpdatedQuota, this, origin_url,
+ usage, quota));
+}
+
+void IndexedDBContextImpl::GotUpdatedQuota(const GURL& origin_url, int64 usage,
+ int64 quota) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
+ space_available_map_[origin_url] = quota - usage;
+}
+
+void IndexedDBContextImpl::QueryAvailableQuota(const GURL& origin_url) {
+ if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT_DEPRECATED));
+ if (quota_manager_proxy())
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&IndexedDBContextImpl::QueryAvailableQuota, this,
+ origin_url));
+ return;
+ }
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (!quota_manager_proxy()->quota_manager())
+ return;
+ quota_manager_proxy()->quota_manager()->GetUsageAndQuota(
+ origin_url,
+ quota::kStorageTypeTemporary,
+ base::Bind(&IndexedDBContextImpl::GotUsageAndQuota, this, origin_url));
+}
+
+std::set<GURL>* IndexedDBContextImpl::GetOriginSet() {
+ if (!origin_set_.get()) {
+ origin_set_.reset(new std::set<GURL>);
+ std::vector<GURL> origins;
+ GetAllOriginsAndPaths(data_path_, &origins, NULL);
+ for (std::vector<GURL>::const_iterator iter = origins.begin();
+ iter != origins.end(); ++iter) {
+ origin_set_->insert(*iter);
+ }
+ }
+ return origin_set_.get();
+}
+
+void IndexedDBContextImpl::ResetCaches() {
+ origin_set_.reset();
+ origin_size_map_.clear();
+ space_available_map_.clear();
+}