- Persist localStorage data across sessions when a cache path is specified (issue #139).

- Add a localStorage test to cefclient.

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@140 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt 2010-11-17 17:28:32 +00:00
parent 6ad535823a
commit 3dd54c536c
23 changed files with 1035 additions and 14 deletions

11
cef.gyp
View File

@ -493,6 +493,10 @@
'libcef/browser_webcookiejar_impl.h',
'libcef/browser_webblobregistry_impl.cc',
'libcef/browser_webblobregistry_impl.h',
'libcef/browser_webstoragearea_impl.cc',
'libcef/browser_webstoragearea_impl.h',
'libcef/browser_webstoragenamespace_impl.cc',
'libcef/browser_webstoragenamespace_impl.h',
'libcef/browser_webkit_glue.cc',
'libcef/browser_webkit_glue.h',
'libcef/browser_webkit_init.h',
@ -513,6 +517,13 @@
'libcef/cef_string_map.cc',
'libcef/cef_thread.cc',
'libcef/cef_thread.h',
'libcef/dom_storage_area.cc',
'libcef/dom_storage_area.h',
'libcef/dom_storage_common.h',
'libcef/dom_storage_context.cc',
'libcef/dom_storage_context.h',
'libcef/dom_storage_namespace.cc',
'libcef/dom_storage_namespace.h',
'libcef/request_impl.cc',
'libcef/request_impl.h',
'libcef/scheme_impl.cc',

View File

@ -187,6 +187,10 @@ FilePath BrowserDatabaseSystem::GetFullFilePathForVfsFile(
return FilePath();
AutoLock file_names_auto_lock(file_names_lock_);
DCHECK(file_names_.find(vfs_file_name) != file_names_.end());
return file_names_[vfs_file_name];
if(file_names_.find(vfs_file_name) != file_names_.end())
return file_names_[vfs_file_name];
// This method is getting called when an empty localStorage database is
// deleted. In that case, just return the path.
return FilePath(vfs_file_name);
}

View File

@ -6,6 +6,14 @@
#ifndef _BROWSER_WEBKIT_INIT_H
#define _BROWSER_WEBKIT_INIT_H
#include "browser_appcache_system.h"
#include "browser_database_system.h"
#include "browser_file_system.h"
#include "browser_resource_loader_bridge.h"
#include "browser_webblobregistry_impl.h"
#include "browser_webcookiejar_impl.h"
#include "browser_webstoragenamespace_impl.h"
#include "base/file_util.h"
#include "base/metrics/stats_counters.h"
#include "base/path_service.h"
@ -35,12 +43,6 @@
#include "webkit/glue/webfileutilities_impl.h"
#include "webkit/glue/webkit_glue.h"
#include "webkit/glue/webkitclient_impl.h"
#include "browser_appcache_system.h"
#include "browser_database_system.h"
#include "browser_file_system.h"
#include "browser_resource_loader_bridge.h"
#include "browser_webblobregistry_impl.h"
#include "browser_webcookiejar_impl.h"
class BrowserWebKitInit : public webkit_glue::WebKitClientImpl {
@ -193,6 +195,12 @@ class BrowserWebKitInit : public webkit_glue::WebKitClientImpl {
virtual WebKit::WebStorageNamespace* createLocalStorageNamespace(
const WebKit::WebString& path, unsigned quota) {
if (BrowserWebStorageNamespaceImpl::IsStorageActive()) {
// Use the localStorage implementation that writes data to disk.
return new BrowserWebStorageNamespaceImpl(DOM_STORAGE_LOCAL);
}
// Use the default localStorage implementation.
return WebKit::WebStorageNamespace::createLocalStorageNamespace(path,
WebKit::WebStorageNamespace::m_localStorageQuota);
}

View File

@ -0,0 +1,56 @@
// Copyright (c) 2010 The Chromium Embedded Framework 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 "browser_webstoragearea_impl.h"
#include "cef_context.h"
#include "dom_storage_area.h"
#include "dom_storage_namespace.h"
#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
using WebKit::WebFrame;
using WebKit::WebString;
using WebKit::WebURL;
using WebKit::WebView;
BrowserWebStorageAreaImpl::BrowserWebStorageAreaImpl(
int64 namespace_id, const WebString& origin) {
area_ = _Context->storage_context()->GetStorageNamespace(namespace_id, true)->
GetStorageArea(origin);
DCHECK(area_ != NULL);
}
BrowserWebStorageAreaImpl::~BrowserWebStorageAreaImpl() {
}
unsigned BrowserWebStorageAreaImpl::length() {
return area_->Length();
}
WebString BrowserWebStorageAreaImpl::key(unsigned index) {
return area_->Key(index);
}
WebString BrowserWebStorageAreaImpl::getItem(const WebString& key) {
return area_->GetItem(key);
}
void BrowserWebStorageAreaImpl::setItem(
const WebString& key, const WebString& value, const WebURL& url,
WebStorageArea::Result& result, WebString& old_value_webkit,
WebFrame* web_frame) {
old_value_webkit = area_->SetItem(key, value, &result);
}
void BrowserWebStorageAreaImpl::removeItem(
const WebString& key, const WebURL& url, WebString& old_value_webkit) {
old_value_webkit = area_->RemoveItem(key);
}
void BrowserWebStorageAreaImpl::clear(
const WebURL& url, bool& cleared_something) {
cleared_something = area_->Clear();
}

View File

@ -0,0 +1,37 @@
// Copyright (c) 2010 The Chromium Embedded Framework 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 _BROWSER_WEBSTORAGEAREA_IMPL_H
#define _BROWSER_WEBSTORAGEAREA_IMPL_H
#include "base/basictypes.h"
#include "third_party/WebKit/WebKit/chromium/public/WebStorageArea.h"
#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
class DOMStorageArea;
class BrowserWebStorageAreaImpl : public WebKit::WebStorageArea {
public:
BrowserWebStorageAreaImpl(int64 namespace_id, const WebKit::WebString& origin);
virtual ~BrowserWebStorageAreaImpl();
// See WebStorageArea.h for documentation on these functions.
virtual unsigned length();
virtual WebKit::WebString key(unsigned index);
virtual WebKit::WebString getItem(const WebKit::WebString& key);
virtual void setItem(
const WebKit::WebString& key, const WebKit::WebString& value,
const WebKit::WebURL& url, WebStorageArea::Result& result,
WebKit::WebString& old_value, WebKit::WebFrame* web_view);
virtual void removeItem(
const WebKit::WebString& key, const WebKit::WebURL& url,
WebKit::WebString& old_value);
virtual void clear(const WebKit::WebURL& url, bool& cleared_something);
private:
// The object is owned by DOMStorageNamespace.
DOMStorageArea* area_;
};
#endif // _BROWSER_WEBSTORAGEAREA_IMPL_H

View File

@ -0,0 +1,57 @@
// Copyright (c) 2010 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 "browser_webstoragenamespace_impl.h"
#include "browser_webstoragearea_impl.h"
#include "cef_context.h"
#include "base/logging.h"
using WebKit::WebStorageArea;
using WebKit::WebStorageNamespace;
using WebKit::WebString;
BrowserWebStorageNamespaceImpl::BrowserWebStorageNamespaceImpl(
DOMStorageType storage_type)
: storage_type_(storage_type),
namespace_id_(kLocalStorageNamespaceId) {
DCHECK(storage_type == DOM_STORAGE_LOCAL);
}
BrowserWebStorageNamespaceImpl::BrowserWebStorageNamespaceImpl(
DOMStorageType storage_type, int64 namespace_id)
: storage_type_(storage_type),
namespace_id_(namespace_id) {
DCHECK(storage_type == DOM_STORAGE_SESSION);
}
BrowserWebStorageNamespaceImpl::~BrowserWebStorageNamespaceImpl() {
}
WebStorageArea* BrowserWebStorageNamespaceImpl::createStorageArea(
const WebString& origin) {
// Ideally, we'd keep a hash map of origin to these objects. Unfortunately
// this doesn't seem practical because there's no good way to ref-count these
// objects, and it'd be unclear who owned them. So, instead, we'll pay the
// price in terms of wasted memory.
return new BrowserWebStorageAreaImpl(namespace_id_, origin);
}
WebStorageNamespace* BrowserWebStorageNamespaceImpl::copy() {
// By returning NULL, we're telling WebKit to lazily fetch it the next time
// session storage is used. In the WebViewClient::createView, we do the
// book-keeping necessary to make it a true copy-on-write despite not doing
// anything here, now.
return NULL;
}
void BrowserWebStorageNamespaceImpl::close() {
// This is called only on LocalStorage namespaces when WebKit thinks its
// shutting down. This has no impact on Chromium.
}
//static
bool BrowserWebStorageNamespaceImpl::IsStorageActive() {
return (_Context->storage_context() != NULL);
}

View File

@ -0,0 +1,37 @@
// Copyright (c) 2010 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 _BROWSER_WEBSTORAGENAMESPACE_IMPL_H
#define _BROWSER_WEBSTORAGENAMESPACE_IMPL_H
#include "dom_storage_common.h"
#include "base/basictypes.h"
#include "third_party/WebKit/WebKit/chromium/public/WebStorageNamespace.h"
class BrowserWebStorageNamespaceImpl : public WebKit::WebStorageNamespace {
public:
explicit BrowserWebStorageNamespaceImpl(DOMStorageType storage_type);
BrowserWebStorageNamespaceImpl(DOMStorageType storage_type,
int64 namespace_id);
// See WebStorageNamespace.h for documentation on these functions.
virtual ~BrowserWebStorageNamespaceImpl();
virtual WebKit::WebStorageArea* createStorageArea(
const WebKit::WebString& origin);
virtual WebKit::WebStorageNamespace* copy();
virtual void close();
// Returns true if storage data is being cached to disk.
static bool IsStorageActive();
private:
// Used during lazy initialization of namespace_id_.
const DOMStorageType storage_type_;
// Our namespace ID.
int64 namespace_id_;
};
#endif // _BROWSER_WEBSTORAGENAMESPACE_IMPL_H

View File

@ -198,7 +198,9 @@ WebWidget* BrowserWebViewDelegate::createPopupMenu(WebPopupType popup_type) {
WebStorageNamespace* BrowserWebViewDelegate::createSessionStorageNamespace(
unsigned quota) {
// Enforce quota, ignoring the parameter from WebCore as in Chrome.
// Enforce quota, ignoring the parameter from WebCore as in Chrome. We could
// potentially use DOMStorageContext to manage session storage but there's
// currently no need since session storage data is not written to disk.
return WebKit::WebStorageNamespace::createSessionStorageNamespace(
WebStorageNamespace::m_sessionStorageQuota);
}

View File

@ -9,6 +9,7 @@
#include "browser_request_context.h"
#include "cef_process.h"
#include "cef_thread.h"
#include "dom_storage_context.h"
#include "base/at_exit.h"
#include "base/file_path.h"
@ -56,6 +57,11 @@ public:
scoped_refptr<BrowserRequestContext> request_context()
{ return request_context_; }
// The DOMStorageContext object is managed by CefProcessUIThread.
void set_storage_context(DOMStorageContext* storage_context)
{ storage_context_.reset(storage_context); }
DOMStorageContext* storage_context() { return storage_context_.get(); }
private:
// Manages the various process threads.
scoped_refptr<CefProcess> process_;
@ -67,10 +73,11 @@ private:
std::wstring cache_path_;
WebPreferences* webprefs_;
scoped_refptr<BrowserRequestContext> request_context_;
scoped_ptr<DOMStorageContext> storage_context_;
// Map of browsers that currently exist.
BrowserList browserlist_;
// Used for assigning unique IDs to browser instances.
int next_browser_id_;
};

View File

@ -116,6 +116,11 @@ void CefProcessUIThread::Init() {
gfx::InitializeGLBindings(gfx::kGLImplementationDesktopGL);
URLRequest::RegisterProtocolFactory("blob", &BlobURLRequestJobFactory);
if(!_Context->cache_path().empty()) {
// Create the storage context object.
_Context->set_storage_context(new DOMStorageContext());
}
}
void CefProcessUIThread::CleanUp() {
@ -124,6 +129,9 @@ void CefProcessUIThread::CleanUp() {
// purify leak-test results.
MessageLoop::current()->RunAllPending();
// Destroy the storage context object.
_Context->set_storage_context(NULL);
// Tear down the shared StatsTable.
base::StatsTable::set_current(NULL);
delete statstable_;

View File

@ -0,0 +1,91 @@
// Copyright (c) 2010 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 "dom_storage_area.h"
#include "dom_storage_context.h"
#include "dom_storage_namespace.h"
#include "base/task.h"
#include "third_party/WebKit/WebKit/chromium/public/WebSecurityOrigin.h"
#include "third_party/WebKit/WebKit/chromium/public/WebStorageArea.h"
#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
#include "webkit/glue/webkit_glue.h"
using WebKit::WebSecurityOrigin;
using WebKit::WebStorageArea;
using WebKit::WebString;
using WebKit::WebURL;
DOMStorageArea::DOMStorageArea(
const string16& origin,
int64 id,
DOMStorageNamespace* owner)
: origin_(origin),
origin_url_(origin),
id_(id),
owner_(owner) {
DCHECK(owner_);
}
DOMStorageArea::~DOMStorageArea() {
}
unsigned DOMStorageArea::Length() {
CreateWebStorageAreaIfNecessary();
return storage_area_->length();
}
NullableString16 DOMStorageArea::Key(unsigned index) {
CreateWebStorageAreaIfNecessary();
return storage_area_->key(index);
}
NullableString16 DOMStorageArea::GetItem(const string16& key) {
CreateWebStorageAreaIfNecessary();
return storage_area_->getItem(key);
}
NullableString16 DOMStorageArea::SetItem(
const string16& key, const string16& value,
WebStorageArea::Result* result) {
if (!CheckContentSetting(key, value)) {
*result = WebStorageArea::ResultBlockedByPolicy;
return NullableString16(true); // Ignored if the content was blocked.
}
CreateWebStorageAreaIfNecessary();
WebString old_value;
storage_area_->setItem(key, value, WebURL(), *result, old_value);
return old_value;
}
NullableString16 DOMStorageArea::RemoveItem(const string16& key) {
CreateWebStorageAreaIfNecessary();
WebString old_value;
storage_area_->removeItem(key, WebURL(), old_value);
return old_value;
}
bool DOMStorageArea::Clear() {
CreateWebStorageAreaIfNecessary();
bool somethingCleared;
storage_area_->clear(WebURL(), somethingCleared);
return somethingCleared;
}
void DOMStorageArea::PurgeMemory() {
storage_area_.reset();
}
void DOMStorageArea::CreateWebStorageAreaIfNecessary() {
if (!storage_area_.get())
storage_area_.reset(owner_->CreateWebStorageArea(origin_));
}
bool DOMStorageArea::CheckContentSetting(
const string16& key, const string16& value) {
// TODO(cef): Potentially give the host an option to deny write access.
return true;
}

77
libcef/dom_storage_area.h Normal file
View File

@ -0,0 +1,77 @@
// Copyright (c) 2010 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 _DOM_STORAGE_AREA_H
#define _DOM_STORAGE_AREA_H
#include "base/hash_tables.h"
#include "base/nullable_string16.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
#include "base/string16.h"
#include "googleurl/src/gurl.h"
#include "third_party/WebKit/WebKit/chromium/public/WebStorageArea.h"
class DOMStorageNamespace;
// Only use on the WebKit thread. DOMStorageNamespace manages our registration
// with DOMStorageContext.
class DOMStorageArea {
public:
DOMStorageArea(const string16& origin,
int64 id,
DOMStorageNamespace* owner);
~DOMStorageArea();
unsigned Length();
NullableString16 Key(unsigned index);
NullableString16 GetItem(const string16& key);
NullableString16 SetItem(
const string16& key, const string16& value,
WebKit::WebStorageArea::Result* result);
NullableString16 RemoveItem(const string16& key);
bool Clear();
void PurgeMemory();
int64 id() const { return id_; }
DOMStorageNamespace* owner() const { return owner_; }
private:
// Creates the underlying WebStorageArea on demand.
void CreateWebStorageAreaIfNecessary();
// Used to see if setItem has permission to do its thing.
bool CheckContentSetting(const string16& key, const string16& value);
// The origin this storage area represents.
string16 origin_;
GURL origin_url_;
// The storage area we wrap.
scoped_ptr<WebKit::WebStorageArea> storage_area_;
// Our storage area id. Unique to our parent context.
int64 id_;
// The DOMStorageNamespace that owns us.
DOMStorageNamespace* owner_;
DISALLOW_IMPLICIT_CONSTRUCTORS(DOMStorageArea);
};
#if defined(COMPILER_GCC)
namespace __gnu_cxx {
template<>
struct hash<DOMStorageArea*> {
std::size_t operator()(DOMStorageArea* const& p) const {
return reinterpret_cast<std::size_t>(p);
}
};
} // namespace __gnu_cxx
#endif
#endif // _DOM_STORAGE_AREA_H

View File

@ -0,0 +1,18 @@
// Copyright (c) 2010 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 _DOM_STORAGE_COMMON_H
#define _DOM_STORAGE_COMMON_H
#include "base/basictypes.h"
const int64 kLocalStorageNamespaceId = 0;
const int64 kInvalidSessionStorageNamespaceId = kLocalStorageNamespaceId;
enum DOMStorageType {
DOM_STORAGE_LOCAL = 0,
DOM_STORAGE_SESSION
};
#endif // _DOM_STORAGE_COMMON_H

View File

@ -0,0 +1,250 @@
// Copyright (c) 2010 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 "dom_storage_context.h"
#include "cef_context.h"
#include "cef_thread.h"
#include "dom_storage_namespace.h"
#include <algorithm>
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/string_util.h"
#include "dom_storage_area.h"
#include "third_party/WebKit/WebKit/chromium/public/WebSecurityOrigin.h"
#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
#include "webkit/glue/webkit_glue.h"
const FilePath::CharType DOMStorageContext::kLocalStorageDirectory[] =
FILE_PATH_LITERAL("Local Storage");
const FilePath::CharType DOMStorageContext::kLocalStorageExtension[] =
FILE_PATH_LITERAL(".localstorage");
DOMStorageContext::DOMStorageContext()
: last_storage_area_id_(0),
last_session_storage_namespace_id_on_ui_thread_(kLocalStorageNamespaceId),
last_session_storage_namespace_id_on_io_thread_(kLocalStorageNamespaceId){
}
DOMStorageContext::~DOMStorageContext() {
for (StorageNamespaceMap::iterator iter(storage_namespace_map_.begin());
iter != storage_namespace_map_.end(); ++iter) {
delete iter->second;
}
}
int64 DOMStorageContext::AllocateStorageAreaId() {
DCHECK(CefThread::CurrentlyOn(CefThread::UI));
return ++last_storage_area_id_;
}
int64 DOMStorageContext::AllocateSessionStorageNamespaceId() {
if (CefThread::CurrentlyOn(CefThread::UI))
return ++last_session_storage_namespace_id_on_ui_thread_;
return --last_session_storage_namespace_id_on_io_thread_;
}
int64 DOMStorageContext::CloneSessionStorage(int64 original_id) {
DCHECK(!CefThread::CurrentlyOn(CefThread::UI));
int64 clone_id = AllocateSessionStorageNamespaceId();
CefThread::PostTask(
CefThread::UI, FROM_HERE, NewRunnableFunction(
&DOMStorageContext::CompleteCloningSessionStorage,
this, original_id, clone_id));
return clone_id;
}
void DOMStorageContext::RegisterStorageArea(DOMStorageArea* storage_area) {
DCHECK(CefThread::CurrentlyOn(CefThread::UI));
int64 id = storage_area->id();
DCHECK(!GetStorageArea(id));
storage_area_map_[id] = storage_area;
}
void DOMStorageContext::UnregisterStorageArea(DOMStorageArea* storage_area) {
DCHECK(CefThread::CurrentlyOn(CefThread::UI));
int64 id = storage_area->id();
DCHECK(GetStorageArea(id));
storage_area_map_.erase(id);
}
DOMStorageArea* DOMStorageContext::GetStorageArea(int64 id) {
DCHECK(CefThread::CurrentlyOn(CefThread::UI));
StorageAreaMap::iterator iter = storage_area_map_.find(id);
if (iter == storage_area_map_.end())
return NULL;
return iter->second;
}
void DOMStorageContext::DeleteSessionStorageNamespace(int64 namespace_id) {
DCHECK(CefThread::CurrentlyOn(CefThread::UI));
StorageNamespaceMap::iterator iter =
storage_namespace_map_.find(namespace_id);
if (iter == storage_namespace_map_.end())
return;
DCHECK(iter->second->dom_storage_type() == DOM_STORAGE_SESSION);
delete iter->second;
storage_namespace_map_.erase(iter);
}
DOMStorageNamespace* DOMStorageContext::GetStorageNamespace(
int64 id, bool allocation_allowed) {
DCHECK(CefThread::CurrentlyOn(CefThread::UI));
StorageNamespaceMap::iterator iter = storage_namespace_map_.find(id);
if (iter != storage_namespace_map_.end())
return iter->second;
if (!allocation_allowed)
return NULL;
if (id == kLocalStorageNamespaceId)
return CreateLocalStorage();
return CreateSessionStorage(id);
}
void DOMStorageContext::PurgeMemory() {
// It is only safe to purge the memory from the LocalStorage namespace,
// because it is backed by disk and can be reloaded later. If we purge a
// SessionStorage namespace, its data will be gone forever, because it isn't
// currently backed by disk.
DOMStorageNamespace* local_storage =
GetStorageNamespace(kLocalStorageNamespaceId, false);
if (local_storage)
local_storage->PurgeMemory();
}
void DOMStorageContext::DeleteDataModifiedSince(
const base::Time& cutoff,
const char* url_scheme_to_be_skipped,
const std::vector<string16>& protected_origins) {
// Make sure that we don't delete a database that's currently being accessed
// by unloading all of the databases temporarily.
PurgeMemory();
FilePath data_path(_Context->cache_path());
file_util::FileEnumerator file_enumerator(
data_path.Append(kLocalStorageDirectory), false,
file_util::FileEnumerator::FILES);
for (FilePath path = file_enumerator.Next(); !path.value().empty();
path = file_enumerator.Next()) {
WebKit::WebSecurityOrigin web_security_origin =
WebKit::WebSecurityOrigin::createFromDatabaseIdentifier(
webkit_glue::FilePathToWebString(path.BaseName()));
if (EqualsASCII(web_security_origin.protocol(), url_scheme_to_be_skipped))
continue;
std::vector<string16>::const_iterator find_iter =
std::find(protected_origins.begin(), protected_origins.end(),
web_security_origin.databaseIdentifier());
if (find_iter != protected_origins.end())
continue;
file_util::FileEnumerator::FindInfo find_info;
file_enumerator.GetFindInfo(&find_info);
if (file_util::HasFileBeenModifiedSince(find_info, cutoff))
file_util::Delete(path, false);
}
}
void DOMStorageContext::DeleteLocalStorageFile(const FilePath& file_path) {
DCHECK(CefThread::CurrentlyOn(CefThread::UI));
// Make sure that we don't delete a database that's currently being accessed
// by unloading all of the databases temporarily.
// TODO(bulach): both this method and DeleteDataModifiedSince could purge
// only the memory used by the specific file instead of all memory at once.
// See http://crbug.com/32000
PurgeMemory();
file_util::Delete(file_path, false);
}
void DOMStorageContext::DeleteLocalStorageForOrigin(const string16& origin_id) {
DCHECK(CefThread::CurrentlyOn(CefThread::UI));
DeleteLocalStorageFile(GetLocalStorageFilePath(origin_id));
}
void DOMStorageContext::DeleteAllLocalStorageFiles() {
DCHECK(CefThread::CurrentlyOn(CefThread::UI));
// Make sure that we don't delete a database that's currently being accessed
// by unloading all of the databases temporarily.
PurgeMemory();
FilePath data_path(_Context->cache_path());
file_util::FileEnumerator file_enumerator(
data_path.Append(kLocalStorageDirectory), false,
file_util::FileEnumerator::FILES);
for (FilePath file_path = file_enumerator.Next(); !file_path.empty();
file_path = file_enumerator.Next()) {
if (file_path.Extension() == kLocalStorageExtension)
file_util::Delete(file_path, false);
}
}
DOMStorageNamespace* DOMStorageContext::CreateLocalStorage() {
FilePath data_path(_Context->cache_path());
FilePath dir_path;
if (!data_path.empty())
dir_path = data_path.Append(kLocalStorageDirectory);
DOMStorageNamespace* new_namespace =
DOMStorageNamespace::CreateLocalStorageNamespace(this, dir_path);
RegisterStorageNamespace(new_namespace);
return new_namespace;
}
DOMStorageNamespace* DOMStorageContext::CreateSessionStorage(
int64 namespace_id) {
DOMStorageNamespace* new_namespace =
DOMStorageNamespace::CreateSessionStorageNamespace(this, namespace_id);
RegisterStorageNamespace(new_namespace);
return new_namespace;
}
void DOMStorageContext::RegisterStorageNamespace(
DOMStorageNamespace* storage_namespace) {
DCHECK(CefThread::CurrentlyOn(CefThread::UI));
int64 id = storage_namespace->id();
DCHECK(!GetStorageNamespace(id, false));
storage_namespace_map_[id] = storage_namespace;
}
/* static */
void DOMStorageContext::CompleteCloningSessionStorage(
DOMStorageContext* context, int64 existing_id, int64 clone_id) {
DCHECK(CefThread::CurrentlyOn(CefThread::UI));
DOMStorageNamespace* existing_namespace =
context->GetStorageNamespace(existing_id, false);
// If nothing exists, then there's nothing to clone.
if (existing_namespace)
context->RegisterStorageNamespace(existing_namespace->Copy(clone_id));
}
// static
void DOMStorageContext::ClearLocalState(const FilePath& profile_path,
const char* url_scheme_to_be_skipped) {
file_util::FileEnumerator file_enumerator(profile_path.Append(
kLocalStorageDirectory), false, file_util::FileEnumerator::FILES);
for (FilePath file_path = file_enumerator.Next(); !file_path.empty();
file_path = file_enumerator.Next()) {
if (file_path.Extension() == kLocalStorageExtension) {
WebKit::WebSecurityOrigin web_security_origin =
WebKit::WebSecurityOrigin::createFromDatabaseIdentifier(
webkit_glue::FilePathToWebString(file_path.BaseName()));
if (!EqualsASCII(web_security_origin.protocol(),
url_scheme_to_be_skipped))
file_util::Delete(file_path, false);
}
}
}
FilePath DOMStorageContext::GetLocalStorageFilePath(
const string16& origin_id) const {
FilePath data_path(_Context->cache_path());
FilePath storageDir = data_path.Append(
DOMStorageContext::kLocalStorageDirectory);
FilePath::StringType id =
webkit_glue::WebStringToFilePathString(origin_id);
return storageDir.Append(id.append(kLocalStorageExtension));
}

View File

@ -0,0 +1,121 @@
// Copyright (c) 2010 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 _DOM_STORAGE_CONTEXT_H
#define _DOM_STORAGE_CONTEXT_H
#include <map>
#include <set>
#include "base/file_path.h"
#include "base/string16.h"
#include "base/time.h"
class DOMStorageArea;
class DOMStorageNamespace;
// This is owned by CefContext and is all the dom storage information that's
// shared by all of the browser windows. The specifics of responsibilities are
// fairly well documented here and in StorageNamespace and StorageArea.
// Everything is only to be accessed on the WebKit thread unless noted
// otherwise.
//
// NOTE: Virtual methods facilitate mocking functions for testing.
class DOMStorageContext {
public:
DOMStorageContext();
virtual ~DOMStorageContext();
// Allocate a new storage area id. Only call on the WebKit thread.
int64 AllocateStorageAreaId();
// Allocate a new session storage id. Only call on the UI or IO thread.
int64 AllocateSessionStorageNamespaceId();
// Clones a session storage namespace and returns the cloned namespaces' id.
// Only call on the IO thread.
int64 CloneSessionStorage(int64 original_id);
// Various storage area methods. The storage area is owned by one of the
// namespaces that's owned by this class.
void RegisterStorageArea(DOMStorageArea* storage_area);
void UnregisterStorageArea(DOMStorageArea* storage_area);
DOMStorageArea* GetStorageArea(int64 id);
// Called on WebKit thread when a session storage namespace can be deleted.
void DeleteSessionStorageNamespace(int64 namespace_id);
// Get a namespace from an id. What's returned is owned by this class. If
// allocation_allowed is true, then this function will create the storage
// namespace if it hasn't been already.
DOMStorageNamespace* GetStorageNamespace(int64 id, bool allocation_allowed);
// Tells storage namespaces to purge any memory they do not need.
virtual void PurgeMemory();
// Delete any local storage files that have been touched since the cutoff
// date that's supplied.
void DeleteDataModifiedSince(const base::Time& cutoff,
const char* url_scheme_to_be_skipped,
const std::vector<string16>& protected_origins);
// Deletes a single local storage file.
void DeleteLocalStorageFile(const FilePath& file_path);
// Deletes the local storage file for the given origin.
void DeleteLocalStorageForOrigin(const string16& origin_id);
// Deletes all local storage files.
void DeleteAllLocalStorageFiles();
// The local storage directory.
static const FilePath::CharType kLocalStorageDirectory[];
// The local storage file extension.
static const FilePath::CharType kLocalStorageExtension[];
// Delete all non-extension local storage files.
static void ClearLocalState(const FilePath& profile_path,
const char* url_scheme_to_be_skipped);
// Get the file name of the local storage file for the given origin.
FilePath GetLocalStorageFilePath(const string16& origin_id) const;
private:
// Get the local storage instance. The object is owned by this class.
DOMStorageNamespace* CreateLocalStorage();
// Get a new session storage namespace. The object is owned by this class.
DOMStorageNamespace* CreateSessionStorage(int64 namespace_id);
// Used internally to register storage namespaces we create.
void RegisterStorageNamespace(DOMStorageNamespace* storage_namespace);
// The WebKit thread half of CloneSessionStorage above. Static because
// DOMStorageContext isn't ref counted thus we can't use a runnable method.
// That said, we know this is safe because this class is destroyed on the
// WebKit thread, so there's no way it could be destroyed before this is run.
static void CompleteCloningSessionStorage(DOMStorageContext* context,
int64 existing_id, int64 clone_id);
// The last used storage_area_id and storage_namespace_id's. For the storage
// namespaces, IDs allocated on the UI thread are positive and count up while
// IDs allocated on the IO thread are negative and count down. This allows us
// to allocate unique IDs on both without any locking. All storage area ids
// are allocated on the WebKit thread.
int64 last_storage_area_id_;
int64 last_session_storage_namespace_id_on_ui_thread_;
int64 last_session_storage_namespace_id_on_io_thread_;
// Maps ids to StorageAreas. We do NOT own these objects. StorageNamespace
// (which does own them) will notify us when we should remove the entries.
typedef std::map<int64, DOMStorageArea*> StorageAreaMap;
StorageAreaMap storage_area_map_;
// Maps ids to StorageNamespaces. We own these objects.
typedef std::map<int64, DOMStorageNamespace*> StorageNamespaceMap;
StorageNamespaceMap storage_namespace_map_;
};
#endif // _DOM_STORAGE_CONTEXT_H

View File

@ -0,0 +1,110 @@
// Copyright (c) 2010 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 "dom_storage_namespace.h"
#include "dom_storage_area.h"
#include "dom_storage_context.h"
#include "base/file_path.h"
#include "base/logging.h"
#include "third_party/WebKit/WebKit/chromium/public/WebStorageArea.h"
#include "third_party/WebKit/WebKit/chromium/public/WebStorageNamespace.h"
#include "webkit/glue/webkit_glue.h"
using WebKit::WebStorageArea;
using WebKit::WebStorageNamespace;
using WebKit::WebString;
/* static */
DOMStorageNamespace* DOMStorageNamespace::CreateLocalStorageNamespace(
DOMStorageContext* dom_storage_context, const FilePath& data_dir_path) {
int64 id = kLocalStorageNamespaceId;
DCHECK(!dom_storage_context->GetStorageNamespace(id, false));
return new DOMStorageNamespace(dom_storage_context, id,
webkit_glue::FilePathToWebString(data_dir_path), DOM_STORAGE_LOCAL);
}
/* static */
DOMStorageNamespace* DOMStorageNamespace::CreateSessionStorageNamespace(
DOMStorageContext* dom_storage_context, int64 id) {
DCHECK(!dom_storage_context->GetStorageNamespace(id, false));
return new DOMStorageNamespace(dom_storage_context, id, WebString(),
DOM_STORAGE_SESSION);
}
DOMStorageNamespace::DOMStorageNamespace(DOMStorageContext* dom_storage_context,
int64 id,
const WebString& data_dir_path,
DOMStorageType dom_storage_type)
: dom_storage_context_(dom_storage_context),
id_(id),
data_dir_path_(data_dir_path),
dom_storage_type_(dom_storage_type) {
DCHECK(dom_storage_context_);
}
DOMStorageNamespace::~DOMStorageNamespace() {
// TODO(jorlow): If the DOMStorageContext is being destructed, there's no need
// to do these calls. Maybe we should add a fast path?
for (OriginToStorageAreaMap::iterator iter(origin_to_storage_area_.begin());
iter != origin_to_storage_area_.end(); ++iter) {
dom_storage_context_->UnregisterStorageArea(iter->second);
delete iter->second;
}
}
DOMStorageArea* DOMStorageNamespace::GetStorageArea(
const string16& origin) {
// We may have already created it for another dispatcher host.
OriginToStorageAreaMap::iterator iter = origin_to_storage_area_.find(origin);
if (iter != origin_to_storage_area_.end())
return iter->second;
// We need to create a new one.
int64 id = dom_storage_context_->AllocateStorageAreaId();
DCHECK(!dom_storage_context_->GetStorageArea(id));
DOMStorageArea* storage_area = new DOMStorageArea(origin, id, this);
origin_to_storage_area_[origin] = storage_area;
dom_storage_context_->RegisterStorageArea(storage_area);
return storage_area;
}
DOMStorageNamespace* DOMStorageNamespace::Copy(int64 id) {
DCHECK(dom_storage_type_ == DOM_STORAGE_SESSION);
DCHECK(!dom_storage_context_->GetStorageNamespace(id, false));
DOMStorageNamespace* new_storage_namespace = new DOMStorageNamespace(
dom_storage_context_, id, data_dir_path_, dom_storage_type_);
// If we haven't used the namespace yet, there's nothing to copy.
if (storage_namespace_.get())
new_storage_namespace->storage_namespace_.reset(storage_namespace_->copy());
return new_storage_namespace;
}
void DOMStorageNamespace::PurgeMemory() {
DCHECK(dom_storage_type_ == DOM_STORAGE_LOCAL);
for (OriginToStorageAreaMap::iterator iter(origin_to_storage_area_.begin());
iter != origin_to_storage_area_.end(); ++iter)
iter->second->PurgeMemory();
storage_namespace_.reset();
}
WebStorageArea* DOMStorageNamespace::CreateWebStorageArea(
const string16& origin) {
CreateWebStorageNamespaceIfNecessary();
return storage_namespace_->createStorageArea(origin);
}
void DOMStorageNamespace::CreateWebStorageNamespaceIfNecessary() {
if (storage_namespace_.get())
return;
if (dom_storage_type_ == DOM_STORAGE_LOCAL) {
storage_namespace_.reset(
WebStorageNamespace::createLocalStorageNamespace(data_dir_path_,
WebStorageNamespace::m_localStorageQuota));
} else {
storage_namespace_.reset(WebStorageNamespace::createSessionStorageNamespace(
WebStorageNamespace::m_sessionStorageQuota));
}
}

View File

@ -0,0 +1,83 @@
// Copyright (c) 2010 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 _DOM_STORAGE_NAMESPACE_H
#define _DOM_STORAGE_NAMESPACE_H
#include "dom_storage_common.h"
#include "base/hash_tables.h"
#include "base/scoped_ptr.h"
#include "base/string16.h"
#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
class DOMStorageArea;
class DOMStorageContext;
class FilePath;
namespace WebKit {
class WebStorageArea;
class WebStorageNamespace;
}
// Only to be used on the WebKit thread.
class DOMStorageNamespace {
public:
static DOMStorageNamespace* CreateLocalStorageNamespace(
DOMStorageContext* dom_storage_context, const FilePath& data_dir_path);
static DOMStorageNamespace* CreateSessionStorageNamespace(
DOMStorageContext* dom_storage_context, int64 namespace_id);
~DOMStorageNamespace();
DOMStorageArea* GetStorageArea(const string16& origin);
DOMStorageNamespace* Copy(int64 clone_namespace_id);
void PurgeMemory();
const DOMStorageContext* dom_storage_context() const {
return dom_storage_context_;
}
int64 id() const { return id_; }
const WebKit::WebString& data_dir_path() const { return data_dir_path_; }
DOMStorageType dom_storage_type() const { return dom_storage_type_; }
// Creates a WebStorageArea for the given origin. This should only be called
// by an owned DOMStorageArea.
WebKit::WebStorageArea* CreateWebStorageArea(const string16& origin);
private:
// Called by the static factory methods above.
DOMStorageNamespace(DOMStorageContext* dom_storage_context,
int64 id,
const WebKit::WebString& data_dir_path,
DOMStorageType storage_type);
// Creates the underlying WebStorageNamespace on demand.
void CreateWebStorageNamespaceIfNecessary();
// All the storage areas we own.
typedef base::hash_map<string16, DOMStorageArea*> OriginToStorageAreaMap;
OriginToStorageAreaMap origin_to_storage_area_;
// The DOMStorageContext that owns us.
DOMStorageContext* dom_storage_context_;
// The WebKit storage namespace we manage.
scoped_ptr<WebKit::WebStorageNamespace> storage_namespace_;
// Our id. Unique to our parent context class.
int64 id_;
// The path used to create us, so we can recreate our WebStorageNamespace on
// demand.
WebKit::WebString data_dir_path_;
// SessionStorage vs. LocalStorage.
const DOMStorageType dom_storage_type_;
DISALLOW_IMPLICIT_CONSTRUCTORS(DOMStorageNamespace);
};
#endif // _DOM_STORAGE_NAMESPACE_H

View File

@ -338,3 +338,8 @@ void RunPopupTest(CefRefPtr<CefBrowser> browser)
browser->GetMainFrame()->ExecuteJavaScript(
L"window.open('http://www.google.com');", L"about:blank", 0);
}
void RunLocalStorageTest(CefRefPtr<CefBrowser> browser)
{
browser->GetMainFrame()->LoadURL(L"http://tests/localstorage");
}

View File

@ -361,5 +361,6 @@ void RunGetTextTest(CefRefPtr<CefFrame> frame);
void RunRequestTest(CefRefPtr<CefBrowser> browser);
void RunJavaScriptExecuteTest(CefRefPtr<CefBrowser> browser);
void RunPopupTest(CefRefPtr<CefBrowser> browser);
void RunLocalStorageTest(CefRefPtr<CefBrowser> browser);
#endif // _CEFCLIENT_H

View File

@ -31,6 +31,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
IDS_LOGO BINARY "res\logo.png"
IDS_UIPLUGIN BINARY "res\uiplugin.html"
IDS_LOGOBALL BINARY "res\logoball.png"
IDS_LOCALSTORAGE BINARY "res\localstorage.html"
/////////////////////////////////////////////////////////////////////////////
//
@ -73,7 +74,8 @@ BEGIN
MENUITEM "Request", ID_TESTS_REQUEST
MENUITEM "Scheme Handler", ID_TESTS_SCHEME_HANDLER
MENUITEM "UI App Example", ID_TESTS_UIAPP
END
MENUITEM "Local Storage", ID_TESTS_LOCALSTORAGE
END
END

View File

@ -533,6 +533,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
if(browser.get())
RunUIPluginTest(browser);
return 0;
case ID_TESTS_LOCALSTORAGE: // Test localStorage
if(browser.get())
RunLocalStorageTest(browser);
return 0;
}
}
break;
@ -656,14 +660,20 @@ CefHandler::RetVal ClientHandler::HandleBeforeResourceLoad(
resourceStream = CefStreamReader::CreateForData(
(void*)dump.c_str(), dump.size() * sizeof(wchar_t));
mimeType = L"text/plain";
}
else if(url == L"http://tests/uiapp") {
} else if(url == L"http://tests/uiapp") {
// Show the uiapp contents
if(LoadBinaryResource(IDS_UIPLUGIN, dwSize, pBytes)) {
resourceStream = CefStreamReader::CreateForHandler(
new CefByteReadHandler(pBytes, dwSize, NULL));
mimeType = L"text/html";
}
} else if(url == L"http://tests/localstorage") {
// Show the localstorage contents
if(LoadBinaryResource(IDS_LOCALSTORAGE, dwSize, pBytes)) {
resourceStream = CefStreamReader::CreateForHandler(
new CefByteReadHandler(pBytes, dwSize, NULL));
mimeType = L"text/html";
}
} else if(wcsstr(url.c_str(), L"/ps_logo2.png") != NULL) {
// Any time we find "ps_logo2.png" in the URL substitute in our own image
if(LoadBinaryResource(IDS_LOGO, dwSize, pBytes)) {

View File

@ -0,0 +1,24 @@
<html>
<body>
<script language="JavaScript">
var val = window.localStorage.getItem('val');
function addLine() {
if(val == null)
val = '<br/>One Line.';
else
val += '<br/>Another Line.';
window.localStorage.setItem('val', val);
document.getElementById('out').innerHTML = val;
}
</script>
Click the "Add Line" button to add a line or the "Clear" button to clear.<br/>
This data will persist across sessions if a cache path was specified.<br/>
<input type="button" value="Add Line" onClick="addLine();"/>
<input type="button" value="Clear" onClick="window.localStorage.removeItem('val'); window.location.reload();"/>
<div id="out"></div>
<script language="JavaScript">
if(val != null)
document.getElementById('out').innerHTML = val;
</script>
</body>
</html>

View File

@ -36,10 +36,12 @@
#define ID_TESTS_REQUEST 32776
#define ID_TESTS_SCHEME_HANDLER 32777
#define ID_TESTS_UIAPP 32778
#define ID_TESTS_LOCALSTORAGE 32779
#define IDC_STATIC -1
#define IDS_LOGO 1000
#define IDS_UIPLUGIN 1001
#define IDS_LOGOBALL 1002
#define IDS_LOCALSTORAGE 1003
// Avoid files associated with MacOS
#define _X86_