From 3dd54c536c0409993f947867068c408097fdb3cc Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Wed, 17 Nov 2010 17:28:32 +0000 Subject: [PATCH] - 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 --- cef.gyp | 11 + libcef/browser_database_system.cc | 8 +- libcef/browser_webkit_init.h | 20 +- libcef/browser_webstoragearea_impl.cc | 56 +++++ libcef/browser_webstoragearea_impl.h | 37 +++ libcef/browser_webstoragenamespace_impl.cc | 57 +++++ libcef/browser_webstoragenamespace_impl.h | 37 +++ libcef/browser_webview_delegate.cc | 4 +- libcef/cef_context.h | 11 +- libcef/cef_process_ui_thread.cc | 8 + libcef/dom_storage_area.cc | 91 ++++++++ libcef/dom_storage_area.h | 77 +++++++ libcef/dom_storage_common.h | 18 ++ libcef/dom_storage_context.cc | 250 +++++++++++++++++++++ libcef/dom_storage_context.h | 121 ++++++++++ libcef/dom_storage_namespace.cc | 110 +++++++++ libcef/dom_storage_namespace.h | 83 +++++++ tests/cefclient/cefclient.cpp | 5 + tests/cefclient/cefclient.h | 1 + tests/cefclient/cefclient.rc | 4 +- tests/cefclient/cefclient_win.cpp | 14 +- tests/cefclient/res/localstorage.html | 24 ++ tests/cefclient/resource.h | 2 + 23 files changed, 1035 insertions(+), 14 deletions(-) create mode 100644 libcef/browser_webstoragearea_impl.cc create mode 100644 libcef/browser_webstoragearea_impl.h create mode 100644 libcef/browser_webstoragenamespace_impl.cc create mode 100644 libcef/browser_webstoragenamespace_impl.h create mode 100644 libcef/dom_storage_area.cc create mode 100644 libcef/dom_storage_area.h create mode 100644 libcef/dom_storage_common.h create mode 100644 libcef/dom_storage_context.cc create mode 100644 libcef/dom_storage_context.h create mode 100644 libcef/dom_storage_namespace.cc create mode 100644 libcef/dom_storage_namespace.h create mode 100644 tests/cefclient/res/localstorage.html diff --git a/cef.gyp b/cef.gyp index ace318985..58602ebc1 100644 --- a/cef.gyp +++ b/cef.gyp @@ -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', diff --git a/libcef/browser_database_system.cc b/libcef/browser_database_system.cc index 3907ce735..ae60cd62a 100644 --- a/libcef/browser_database_system.cc +++ b/libcef/browser_database_system.cc @@ -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); } diff --git a/libcef/browser_webkit_init.h b/libcef/browser_webkit_init.h index 3622d20a2..9d5979e96 100644 --- a/libcef/browser_webkit_init.h +++ b/libcef/browser_webkit_init.h @@ -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); } diff --git a/libcef/browser_webstoragearea_impl.cc b/libcef/browser_webstoragearea_impl.cc new file mode 100644 index 000000000..9e7e3d79e --- /dev/null +++ b/libcef/browser_webstoragearea_impl.cc @@ -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(); +} diff --git a/libcef/browser_webstoragearea_impl.h b/libcef/browser_webstoragearea_impl.h new file mode 100644 index 000000000..8c0fb7c22 --- /dev/null +++ b/libcef/browser_webstoragearea_impl.h @@ -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 diff --git a/libcef/browser_webstoragenamespace_impl.cc b/libcef/browser_webstoragenamespace_impl.cc new file mode 100644 index 000000000..5be3b1cb5 --- /dev/null +++ b/libcef/browser_webstoragenamespace_impl.cc @@ -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); +} diff --git a/libcef/browser_webstoragenamespace_impl.h b/libcef/browser_webstoragenamespace_impl.h new file mode 100644 index 000000000..3c9dbc690 --- /dev/null +++ b/libcef/browser_webstoragenamespace_impl.h @@ -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 diff --git a/libcef/browser_webview_delegate.cc b/libcef/browser_webview_delegate.cc index e8ca6b427..46379fe31 100644 --- a/libcef/browser_webview_delegate.cc +++ b/libcef/browser_webview_delegate.cc @@ -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); } diff --git a/libcef/cef_context.h b/libcef/cef_context.h index 57c116e6b..8fa683ba4 100644 --- a/libcef/cef_context.h +++ b/libcef/cef_context.h @@ -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 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 process_; @@ -67,10 +73,11 @@ private: std::wstring cache_path_; WebPreferences* webprefs_; scoped_refptr request_context_; - + scoped_ptr storage_context_; + // Map of browsers that currently exist. BrowserList browserlist_; - + // Used for assigning unique IDs to browser instances. int next_browser_id_; }; diff --git a/libcef/cef_process_ui_thread.cc b/libcef/cef_process_ui_thread.cc index 8e1c56ac9..7f4ace82f 100644 --- a/libcef/cef_process_ui_thread.cc +++ b/libcef/cef_process_ui_thread.cc @@ -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_; diff --git a/libcef/dom_storage_area.cc b/libcef/dom_storage_area.cc new file mode 100644 index 000000000..34a1dc302 --- /dev/null +++ b/libcef/dom_storage_area.cc @@ -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; +} diff --git a/libcef/dom_storage_area.h b/libcef/dom_storage_area.h new file mode 100644 index 000000000..0868b67f4 --- /dev/null +++ b/libcef/dom_storage_area.h @@ -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 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 { + std::size_t operator()(DOMStorageArea* const& p) const { + return reinterpret_cast(p); + } +}; + +} // namespace __gnu_cxx +#endif + +#endif // _DOM_STORAGE_AREA_H diff --git a/libcef/dom_storage_common.h b/libcef/dom_storage_common.h new file mode 100644 index 000000000..3fcf4aa1b --- /dev/null +++ b/libcef/dom_storage_common.h @@ -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 diff --git a/libcef/dom_storage_context.cc b/libcef/dom_storage_context.cc new file mode 100644 index 000000000..51a5f2f96 --- /dev/null +++ b/libcef/dom_storage_context.cc @@ -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 + +#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& 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::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)); +} diff --git a/libcef/dom_storage_context.h b/libcef/dom_storage_context.h new file mode 100644 index 000000000..f2e3ebf9a --- /dev/null +++ b/libcef/dom_storage_context.h @@ -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 +#include + +#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& 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 StorageAreaMap; + StorageAreaMap storage_area_map_; + + // Maps ids to StorageNamespaces. We own these objects. + typedef std::map StorageNamespaceMap; + StorageNamespaceMap storage_namespace_map_; +}; + +#endif // _DOM_STORAGE_CONTEXT_H diff --git a/libcef/dom_storage_namespace.cc b/libcef/dom_storage_namespace.cc new file mode 100644 index 000000000..397dd8f33 --- /dev/null +++ b/libcef/dom_storage_namespace.cc @@ -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)); + } +} diff --git a/libcef/dom_storage_namespace.h b/libcef/dom_storage_namespace.h new file mode 100644 index 000000000..f6f8fdf15 --- /dev/null +++ b/libcef/dom_storage_namespace.h @@ -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 OriginToStorageAreaMap; + OriginToStorageAreaMap origin_to_storage_area_; + + // The DOMStorageContext that owns us. + DOMStorageContext* dom_storage_context_; + + // The WebKit storage namespace we manage. + scoped_ptr 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 diff --git a/tests/cefclient/cefclient.cpp b/tests/cefclient/cefclient.cpp index f0f4f4666..875bc5786 100644 --- a/tests/cefclient/cefclient.cpp +++ b/tests/cefclient/cefclient.cpp @@ -338,3 +338,8 @@ void RunPopupTest(CefRefPtr browser) browser->GetMainFrame()->ExecuteJavaScript( L"window.open('http://www.google.com');", L"about:blank", 0); } + +void RunLocalStorageTest(CefRefPtr browser) +{ + browser->GetMainFrame()->LoadURL(L"http://tests/localstorage"); +} diff --git a/tests/cefclient/cefclient.h b/tests/cefclient/cefclient.h index 2173ba24d..45b21a10c 100644 --- a/tests/cefclient/cefclient.h +++ b/tests/cefclient/cefclient.h @@ -361,5 +361,6 @@ void RunGetTextTest(CefRefPtr frame); void RunRequestTest(CefRefPtr browser); void RunJavaScriptExecuteTest(CefRefPtr browser); void RunPopupTest(CefRefPtr browser); +void RunLocalStorageTest(CefRefPtr browser); #endif // _CEFCLIENT_H diff --git a/tests/cefclient/cefclient.rc b/tests/cefclient/cefclient.rc index 0f8d49376..b694688ad 100644 --- a/tests/cefclient/cefclient.rc +++ b/tests/cefclient/cefclient.rc @@ -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 diff --git a/tests/cefclient/cefclient_win.cpp b/tests/cefclient/cefclient_win.cpp index 6ebd7ec88..51fe3458a 100644 --- a/tests/cefclient/cefclient_win.cpp +++ b/tests/cefclient/cefclient_win.cpp @@ -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)) { diff --git a/tests/cefclient/res/localstorage.html b/tests/cefclient/res/localstorage.html new file mode 100644 index 000000000..913d3ed9d --- /dev/null +++ b/tests/cefclient/res/localstorage.html @@ -0,0 +1,24 @@ + + + +Click the "Add Line" button to add a line or the "Clear" button to clear.
+This data will persist across sessions if a cache path was specified.
+ + +
+ + + diff --git a/tests/cefclient/resource.h b/tests/cefclient/resource.h index fd734a451..52df939ea 100644 --- a/tests/cefclient/resource.h +++ b/tests/cefclient/resource.h @@ -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_