diff --git a/cef.gyp b/cef.gyp index af3154920..a05fbd235 100644 --- a/cef.gyp +++ b/cef.gyp @@ -238,7 +238,9 @@ 'tests/unittests/scheme_handler_unittest.cc', 'tests/unittests/stream_unittest.cc', 'tests/unittests/string_unittest.cc', + 'tests/unittests/storage_unittest.cc', 'tests/unittests/test_handler.h', + 'tests/unittests/test_suite.cc', 'tests/unittests/test_suite.h', 'tests/unittests/url_unittest.cc', 'tests/unittests/v8_unittest.cc', @@ -390,6 +392,8 @@ 'libcef_dll/ctocpp/scheme_handler_ctocpp.h', 'libcef_dll/ctocpp/scheme_handler_factory_ctocpp.cc', 'libcef_dll/ctocpp/scheme_handler_factory_ctocpp.h', + 'libcef_dll/ctocpp/storage_visitor_ctocpp.cc', + 'libcef_dll/ctocpp/storage_visitor_ctocpp.h', 'libcef_dll/ctocpp/task_ctocpp.cc', 'libcef_dll/ctocpp/task_ctocpp.h', 'libcef_dll/ctocpp/v8accessor_ctocpp.cc', diff --git a/cef_paths.gypi b/cef_paths.gypi index 3a0466352..eecd672f9 100644 --- a/cef_paths.gypi +++ b/cef_paths.gypi @@ -158,6 +158,8 @@ 'libcef_dll/cpptoc/scheme_handler_cpptoc.h', 'libcef_dll/cpptoc/scheme_handler_factory_cpptoc.cc', 'libcef_dll/cpptoc/scheme_handler_factory_cpptoc.h', + 'libcef_dll/cpptoc/storage_visitor_cpptoc.cc', + 'libcef_dll/cpptoc/storage_visitor_cpptoc.h', 'libcef_dll/cpptoc/task_cpptoc.cc', 'libcef_dll/cpptoc/task_cpptoc.h', 'libcef_dll/cpptoc/v8accessor_cpptoc.cc', diff --git a/include/cef.h b/include/cef.h index 30d941288..8c14b9249 100644 --- a/include/cef.h +++ b/include/cef.h @@ -73,6 +73,7 @@ class CefRequest; class CefResponse; class CefSchemeHandler; class CefSchemeHandlerFactory; +class CefStorageVisitor; class CefStreamReader; class CefStreamWriter; class CefTask; @@ -389,6 +390,40 @@ bool CefSetCookie(const CefString& url, const CefCookie& cookie); bool CefDeleteCookies(const CefString& url, const CefString& cookie_name); +typedef cef_storage_type_t CefStorageType; + +/// +// Visit storage of the specified type. If |origin| is non-empty only data +// matching that origin will be visited. If |key| is non-empty only data +// matching that key will be visited. Otherwise, all data for the storage +// type will be visited. Returns false if the storage cannot be accessed. +// Origin should be of the form scheme://domain. +/// +/*--cef()--*/ +bool CefVisitStorage(CefStorageType type, const CefString& origin, + const CefString& key, + CefRefPtr visitor); + +/// +// Sets storage of the specified type, origin, key and value. Returns false if +// storage cannot be accessed. This method must be called on the UI thread. +/// +/*--cef()--*/ +bool CefSetStorage(CefStorageType type, const CefString& origin, + const CefString& key, const CefString& value); + +/// +// Deletes all storage of the specified type. If |origin| is non-empty only data +// matching that origin will be cleared. If |key| is non-empty only data +// matching that key will be cleared. Otherwise, all data for the storage type +// will be cleared. Returns false if storage cannot be accessed. This method +// must be called on the UI thread. +/// +/*--cef()--*/ +bool CefDeleteStorage(CefStorageType type, const CefString& origin, + const CefString& key); + + /// // Interface defining the reference count implementation methods. All framework // classes must extend the CefBase class. @@ -528,6 +563,28 @@ public: }; +/// +// Interface to implement for visiting storage. The methods of this class will +// always be called on the UI thread. +/// +/*--cef(source=client)--*/ +class CefStorageVisitor : public virtual CefBase +{ +public: + /// + // Method that will be called once for each key/value data pair in storage. + // |count| is the 0-based index for the current pair. |total| is the total + // number of pairs. Set |deleteData| to true to delete the pair currently + // being visited. Return false to stop visiting pairs. This method may never + // be called if no data is found. + /// + /*--cef()--*/ + virtual bool Visit(CefStorageType type, const CefString& origin, + const CefString& key, const CefString& value, int count, + int total, bool& deleteData) =0; +}; + + /// // Class used to represent a browser window. The methods of this class may be // called on any thread unless otherwise indicated in the comments. diff --git a/include/cef_capi.h b/include/cef_capi.h index c4e5bcb71..e998ad380 100644 --- a/include/cef_capi.h +++ b/include/cef_capi.h @@ -325,6 +325,35 @@ CEF_EXPORT int cef_set_cookie(const cef_string_t* url, CEF_EXPORT int cef_delete_cookies(const cef_string_t* url, const cef_string_t* cookie_name); +/// +// Visit storage of the specified type. If |origin| is non-NULL only data +// matching that origin will be visited. If |key| is non-NULL only data matching +// that key will be visited. Otherwise, all data for the storage type will be +// visited. Returns false (0) if the storage cannot be accessed. Origin should +// be of the form scheme://domain. +/// +CEF_EXPORT int cef_visit_storage(enum cef_storage_type_t type, + const cef_string_t* origin, const cef_string_t* key, + struct _cef_storage_visitor_t* visitor); + +/// +// Sets storage of the specified type, origin, key and value. Returns false (0) +// if storage cannot be accessed. This function must be called on the UI thread. +/// +CEF_EXPORT int cef_set_storage(enum cef_storage_type_t type, + const cef_string_t* origin, const cef_string_t* key, + const cef_string_t* value); + +/// +// Deletes all storage of the specified type. If |origin| is non-NULL only data +// matching that origin will be cleared. If |key| is non-NULL only data matching +// that key will be cleared. Otherwise, all data for the storage type will be +// cleared. Returns false (0) if storage cannot be accessed. This function must +// be called on the UI thread. +/// +CEF_EXPORT int cef_delete_storage(enum cef_storage_type_t type, + const cef_string_t* origin, const cef_string_t* key); + typedef struct _cef_base_t { // Size of the data structure. @@ -390,6 +419,30 @@ typedef struct _cef_cookie_visitor_t } cef_cookie_visitor_t; +/// +// Structure to implement for visiting storage. The functions of this structure +// will always be called on the UI thread. +/// +typedef struct _cef_storage_visitor_t +{ + // Base structure. + cef_base_t base; + + /// + // Method that will be called once for each key/value data pair in storage. + // |count| is the 0-based index for the current pair. |total| is the total + // number of pairs. Set |deleteData| to true (1) to delete the pair currently + // being visited. Return false (0) to stop visiting pairs. This function may + // never be called if no data is found. + /// + int (CEF_CALLBACK *visit)(struct _cef_storage_visitor_t* self, + enum cef_storage_type_t type, const cef_string_t* origin, + const cef_string_t* key, const cef_string_t* value, int count, int total, + int* deleteData); + +} cef_storage_visitor_t; + + /// // Structure used to represent a browser window. The functions of this structure // may be called on any thread unless otherwise indicated in the comments. diff --git a/include/internal/cef_types.h b/include/internal/cef_types.h index 695d2d8da..2ae3b20d7 100644 --- a/include/internal/cef_types.h +++ b/include/internal/cef_types.h @@ -141,6 +141,16 @@ typedef struct _cef_settings_t // content like WebGL, accelerated layers and 3D CSS. /// cef_graphics_implementation_t graphics_implementation; + + /// + // Quota limit for localStorage data across all origins. Default size is 5MB. + /// + unsigned int local_storage_quota; + + /// + // Quota limit for sessionStorage data per namespace. Default size is 5MB. + /// + unsigned int session_storage_quota; } cef_settings_t; /// @@ -469,6 +479,15 @@ typedef struct _cef_cookie_t cef_time_t expires; } cef_cookie_t; +/// +// Storage types. +/// +enum cef_storage_type_t +{ + ST_LOCALSTORAGE = 0, + ST_SESSIONSTORAGE, +}; + /// // Mouse button types. /// diff --git a/libcef/browser_webkit_init.cc b/libcef/browser_webkit_init.cc index ec3dd15b7..1fc1981bd 100644 --- a/libcef/browser_webkit_init.cc +++ b/libcef/browser_webkit_init.cc @@ -175,14 +175,7 @@ WebKit::WebString BrowserWebKitInit::defaultLocale() { WebKit::WebStorageNamespace* BrowserWebKitInit::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); + return new BrowserWebStorageNamespaceImpl(DOM_STORAGE_LOCAL); } void BrowserWebKitInit::dispatchStorageEvent(const WebKit::WebString& key, diff --git a/libcef/browser_webstoragearea_impl.cc b/libcef/browser_webstoragearea_impl.cc index 7e61c6c2b..ddf0cb4be 100644 --- a/libcef/browser_webstoragearea_impl.cc +++ b/libcef/browser_webstoragearea_impl.cc @@ -18,8 +18,8 @@ using WebKit::WebView; BrowserWebStorageAreaImpl::BrowserWebStorageAreaImpl( int64 namespace_id, const WebString& origin) { - area_ = _Context->storage_context()->GetStorageNamespace(namespace_id, true)-> - GetStorageArea(origin); + area_ = + _Context->storage_context()->GetStorageArea(namespace_id, origin, true); DCHECK(area_ != NULL); } diff --git a/libcef/browser_webstoragenamespace_impl.cc b/libcef/browser_webstoragenamespace_impl.cc index 5be3b1cb5..da01ddca6 100644 --- a/libcef/browser_webstoragenamespace_impl.cc +++ b/libcef/browser_webstoragenamespace_impl.cc @@ -16,14 +16,12 @@ 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() { @@ -50,8 +48,3 @@ 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 index 85d5d8d23..c956db2e8 100644 --- a/libcef/browser_webstoragenamespace_impl.h +++ b/libcef/browser_webstoragenamespace_impl.h @@ -23,9 +23,6 @@ class BrowserWebStorageNamespaceImpl : public WebKit::WebStorageNamespace { 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_; diff --git a/libcef/browser_webview_delegate.cc b/libcef/browser_webview_delegate.cc index 54adf04e6..d5f1c6509 100644 --- a/libcef/browser_webview_delegate.cc +++ b/libcef/browser_webview_delegate.cc @@ -14,6 +14,7 @@ #include "browser_navigation_controller.h" #include "browser_web_worker.h" #include "browser_webkit_glue.h" +#include "browser_webstoragenamespace_impl.h" #include "browser_zoom_map.h" #include "cef_context.h" #include "request_impl.h" @@ -180,11 +181,9 @@ WebWidget* BrowserWebViewDelegate::createPopupMenu(WebPopupType popup_type) { WebStorageNamespace* BrowserWebViewDelegate::createSessionStorageNamespace( unsigned quota) { - // 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); + // Ignore the quota parameter from WebCore as in Chrome. + return new BrowserWebStorageNamespaceImpl(DOM_STORAGE_SESSION, + kLocalStorageNamespaceId + 1); } void BrowserWebViewDelegate::didAddMessageToConsole( diff --git a/libcef/cef_context.cc b/libcef/cef_context.cc index 3a1ef1020..6a16a0345 100644 --- a/libcef/cef_context.cc +++ b/libcef/cef_context.cc @@ -2,14 +2,15 @@ // reserved. Use of this source code is governed by a BSD-style license that can // be found in the LICENSE file. -#include "cef_context.h" +#include "include/cef_nplugin.h" #include "browser_devtools_scheme_handler.h" #include "browser_impl.h" #include "browser_webkit_glue.h" -#include "cef_thread.h" +#include "cef_context.h" #include "cef_time_util.h" -#include "cef_process.h" -#include "../include/cef_nplugin.h" +#include "dom_storage_common.h" +#include "dom_storage_namespace.h" +#include "dom_storage_area.h" #include "base/bind.h" #include "base/file_util.h" @@ -169,6 +170,124 @@ private: base::WaitableEvent *event_; }; +void UIT_VisitStorage(int64 namespace_id, const CefString& origin, + const CefString& key, + CefRefPtr visitor) +{ + REQUIRE_UIT(); + + DOMStorageContext* context = _Context->storage_context(); + + DOMStorageNamespace* ns = + context->GetStorageNamespace(namespace_id, false); + if (!ns) + return; + + typedef std::vector AreaList; + AreaList areas; + + if (!origin.empty()) { + // Visit only the area with the specified origin. + DOMStorageArea* area = ns->GetStorageArea(origin, false); + if (area) + areas.push_back(area); + } else { + // Visit all areas. + ns->GetStorageAreas(areas, true); + } + + if (areas.empty()) + return; + + // Count the total number of matching keys. + unsigned int total = 0; + { + NullableString16 value; + AreaList::iterator it = areas.begin(); + for (; it != areas.end(); ) { + DOMStorageArea* area = (*it); + if (!key.empty()) { + value = area->GetItem(key); + if (value.is_null()) { + it = areas.erase(it); + // Don't increment the iterator. + continue; + } else { + total++; + } + } else { + total += area->Length(); + } + ++it; + } + } + + if (total == 0) + return; + + DOMStorageArea* area; + bool stop = false, deleteData; + unsigned int count = 0, i, len; + NullableString16 keyVal, valueVal; + string16 keyStr, valueStr; + typedef std::vector String16List; + String16List delete_keys; + + // Visit all matching pairs. + AreaList::iterator it = areas.begin(); + for (; it != areas.end() && !stop; ++it) { + // Each area. + area = *(it); + if (!key.empty()) { + // Visit only the matching key. + valueVal = area->GetItem(key); + if (valueVal.is_null()) + valueStr.clear(); + else + valueStr = valueVal.string(); + + deleteData = false; + stop = !visitor->Visit(static_cast(namespace_id), + area->origin(), key, valueStr, count, total, deleteData); + if (deleteData) + area->RemoveItem(key); + count++; + } else { + // Visit all keys. + len = area->Length(); + for(i = 0; i < len && !stop; ++i) { + keyVal = area->Key(i); + if (keyVal.is_null()) { + keyStr.clear(); + valueStr.clear(); + } else { + keyStr = keyVal.string(); + valueVal = area->GetItem(keyStr); + if (valueVal.is_null()) + valueStr.clear(); + else + valueStr = valueVal.string(); + } + + deleteData = false; + stop = !visitor->Visit(static_cast(namespace_id), + area->origin(), keyStr, valueStr, count, total, deleteData); + if (deleteData) + delete_keys.push_back(keyStr); + count++; + } + + // Delete the requested keys. + if (!delete_keys.empty()) { + String16List::const_iterator it = delete_keys.begin(); + for (; it != delete_keys.end(); ++it) + area->RemoveItem(*it); + delete_keys.clear(); + } + } + } +} + } // anonymous bool CefInitialize(const CefSettings& settings) @@ -178,7 +297,7 @@ bool CefInitialize(const CefSettings& settings) return true; if(settings.size != sizeof(cef_settings_t)) { - NOTREACHED(); + NOTREACHED() << "invalid CefSettings structure size"; return false; } @@ -193,13 +312,13 @@ void CefShutdown() { // Verify that the context is in a valid state. if (!CONTEXT_STATE_VALID()) { - NOTREACHED(); + NOTREACHED() << "context not valid"; return; } // Must always be called on the same thread as Initialize. if(!_Context->process()->CalledOnValidThread()) { - NOTREACHED(); + NOTREACHED() << "called on invalid thread"; return; } @@ -214,13 +333,13 @@ void CefDoMessageLoopWork() { // Verify that the context is in a valid state. if (!CONTEXT_STATE_VALID()) { - NOTREACHED(); + NOTREACHED() << "context not valid"; return; } // Must always be called on the same thread as Initialize. if(!_Context->process()->CalledOnValidThread()) { - NOTREACHED(); + NOTREACHED() << "called on invalid thread"; return; } @@ -231,13 +350,13 @@ void CefRunMessageLoop() { // Verify that the context is in a valid state. if (!CONTEXT_STATE_VALID()) { - NOTREACHED(); + NOTREACHED() << "context not valid"; return; } // Must always be called on the same thread as Initialize. if(!_Context->process()->CalledOnValidThread()) { - NOTREACHED(); + NOTREACHED() << "called on invalid thread"; return; } @@ -248,7 +367,7 @@ bool CefRegisterPlugin(const CefPluginInfo& plugin_info) { // Verify that the context is in a valid state. if (!CONTEXT_STATE_VALID()) { - NOTREACHED(); + NOTREACHED() << "context not valid"; return false; } @@ -367,7 +486,7 @@ bool CefVisitAllCookies(CefRefPtr visitor) { // Verify that the context is in a valid state. if (!CONTEXT_STATE_VALID()) { - NOTREACHED(); + NOTREACHED() << "context not valid"; return false; } @@ -380,7 +499,7 @@ bool CefVisitUrlCookies(const CefString& url, bool includeHttpOnly, { // Verify that the context is in a valid state. if (!CONTEXT_STATE_VALID()) { - NOTREACHED(); + NOTREACHED() << "context not valid"; return false; } @@ -397,13 +516,13 @@ bool CefSetCookie(const CefString& url, const CefCookie& cookie) { // Verify that the context is in a valid state. if (!CONTEXT_STATE_VALID()) { - NOTREACHED(); + NOTREACHED() << "context not valid"; return false; } // Verify that this function is being called on the IO thread. if (!CefThread::CurrentlyOn(CefThread::IO)) { - NOTREACHED(); + NOTREACHED() << "called on invalid thread"; return false; } @@ -436,13 +555,13 @@ bool CefDeleteCookies(const CefString& url, const CefString& cookie_name) { // Verify that the context is in a valid state. if (!CONTEXT_STATE_VALID()) { - NOTREACHED(); + NOTREACHED() << "context not valid"; return false; } // Verify that this function is being called on the IO thread. if (!CefThread::CurrentlyOn(CefThread::IO)) { - NOTREACHED(); + NOTREACHED() << "called on invalid thread"; return false; } @@ -473,6 +592,132 @@ bool CefDeleteCookies(const CefString& url, const CefString& cookie_name) return true; } +bool CefVisitStorage(CefStorageType type, const CefString& origin, + const CefString& key, + CefRefPtr visitor) +{ + // Verify that the context is in a valid state. + if (!CONTEXT_STATE_VALID()) { + NOTREACHED() << "context not valid"; + return false; + } + + int64 namespace_id; + if (type == ST_LOCALSTORAGE) { + namespace_id = kLocalStorageNamespaceId; + } else if(type == ST_SESSIONSTORAGE) { + namespace_id = kLocalStorageNamespaceId + 1; + } else { + NOTREACHED() << "invalid type"; + return false; + } + + if (CefThread::CurrentlyOn(CefThread::UI)) { + UIT_VisitStorage(namespace_id, origin, key, visitor); + } else { + CefThread::PostTask(CefThread::UI, FROM_HERE, + base::Bind(&UIT_VisitStorage, namespace_id, origin, key, visitor)); + } + + return true; +} + +bool CefSetStorage(CefStorageType type, const CefString& origin, + const CefString& key, const CefString& value) +{ + // Verify that the context is in a valid state. + if (!CONTEXT_STATE_VALID()) { + NOTREACHED() << "context not valid"; + return false; + } + + // Verify that this function is being called on the UI thread. + if (!CefThread::CurrentlyOn(CefThread::UI)) { + NOTREACHED() << "called on invalid thread"; + return false; + } + + int64 namespace_id; + if (type == ST_LOCALSTORAGE) { + namespace_id = kLocalStorageNamespaceId; + } else if(type == ST_SESSIONSTORAGE) { + namespace_id = kLocalStorageNamespaceId + 1; + } else { + NOTREACHED() << "invalid type"; + return false; + } + + if (origin.empty()) { + NOTREACHED() << "invalid origin"; + return false; + } + + DOMStorageArea* area = + _Context->storage_context()->GetStorageArea(namespace_id, origin, true); + if (!area) + return false; + + WebKit::WebStorageArea::Result result; + area->SetItem(key, value, &result); + return (result == WebKit::WebStorageArea::ResultOK); +} + +bool CefDeleteStorage(CefStorageType type, const CefString& origin, + const CefString& key) +{ + // Verify that the context is in a valid state. + if (!CONTEXT_STATE_VALID()) { + NOTREACHED() << "context not valid"; + return false; + } + + // Verify that this function is being called on the UI thread. + if (!CefThread::CurrentlyOn(CefThread::UI)) { + NOTREACHED() << "called on invalid thread"; + return false; + } + + int64 namespace_id; + if (type == ST_LOCALSTORAGE) { + namespace_id = kLocalStorageNamespaceId; + } else if(type == ST_SESSIONSTORAGE) { + namespace_id = kLocalStorageNamespaceId + 1; + } else { + NOTREACHED() << "invalid type"; + return false; + } + + DOMStorageContext* context = _Context->storage_context(); + + if (origin.empty()) { + // Delete all storage for the namespace. + if (namespace_id == kLocalStorageNamespaceId) + context->DeleteAllLocalStorageFiles(); + else + context->PurgeMemory(namespace_id); + } else if(key.empty()) { + // Clear the storage area for the specified origin. + if (namespace_id == kLocalStorageNamespaceId) { + context->DeleteLocalStorageForOrigin(origin); + } else { + DOMStorageArea* area = + context->GetStorageArea(namespace_id, origin, false); + if (area) { + // Calling Clear() is necessary to remove the data from the namespace. + area->Clear(); + area->PurgeMemory(); + } + } + } else { + // Delete the specified key. + DOMStorageArea* area = context->GetStorageArea(namespace_id, origin, false); + if (area) + area->RemoveItem(key); + } + + return true; +} + // CefContext diff --git a/libcef/cef_context.h b/libcef/cef_context.h index 3fec9c089..4a304a1be 100644 --- a/libcef/cef_context.h +++ b/libcef/cef_context.h @@ -5,22 +5,23 @@ #ifndef _CEF_CONTEXT_H #define _CEF_CONTEXT_H -#include "../include/cef.h" +#include "include/cef.h" #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" #include "base/memory/ref_counted.h" -#include "base/message_loop.h" #include -class BrowserRequestContext; class CefBrowserImpl; class WebViewHost; +namespace base { +class WaitableEvent; +} + class CefContext : public CefBase { public: diff --git a/libcef/cef_process_ui_thread.cc b/libcef/cef_process_ui_thread.cc index 6928500be..024171938 100644 --- a/libcef/cef_process_ui_thread.cc +++ b/libcef/cef_process_ui_thread.cc @@ -124,10 +124,14 @@ void CefProcessUIThread::Init() { gfx::InitializeGLBindings(gfx::kGLImplementationDesktopGL); #endif - if (!_Context->cache_path().empty()) { - // Create the storage context object. - _Context->set_storage_context(new DOMStorageContext()); - } + // Set storage quota limits. + if (settings.local_storage_quota != 0) + DOMStorageContext::set_local_storage_quota(settings.local_storage_quota); + if (settings.session_storage_quota != 0) + DOMStorageContext::set_session_storage_quota(settings.session_storage_quota); + + // Create the storage context object. + _Context->set_storage_context(new DOMStorageContext(_Context->cache_path())); if (settings.user_agent.length > 0) webkit_glue::SetUserAgent(CefString(&settings.user_agent)); diff --git a/libcef/dom_storage_area.h b/libcef/dom_storage_area.h index 4e5789f17..8c8b7d81b 100644 --- a/libcef/dom_storage_area.h +++ b/libcef/dom_storage_area.h @@ -37,6 +37,7 @@ class DOMStorageArea { int64 id() const { return id_; } DOMStorageNamespace* owner() const { return owner_; } + const string16& origin() const { return origin_; } private: // Creates the underlying WebStorageArea on demand. diff --git a/libcef/dom_storage_context.cc b/libcef/dom_storage_context.cc index 947bb41b7..f0070fcb1 100644 --- a/libcef/dom_storage_context.cc +++ b/libcef/dom_storage_context.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "dom_storage_context.h" -#include "cef_context.h" #include "cef_thread.h" #include "dom_storage_namespace.h" @@ -14,7 +13,9 @@ #include "base/string_util.h" #include "dom_storage_area.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebStorageNamespace.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" +#include "webkit/database/database_util.h" #include "webkit/glue/webkit_glue.h" const FilePath::CharType DOMStorageContext::kLocalStorageDirectory[] = @@ -23,8 +24,15 @@ const FilePath::CharType DOMStorageContext::kLocalStorageDirectory[] = const FilePath::CharType DOMStorageContext::kLocalStorageExtension[] = FILE_PATH_LITERAL(".localstorage"); -DOMStorageContext::DOMStorageContext() - : last_storage_area_id_(0), +// Use WebStorageNamespace quota sizes as the default. +unsigned int DOMStorageContext::local_storage_quota_ = + WebKit::WebStorageNamespace::m_localStorageQuota; +unsigned int DOMStorageContext::session_storage_quota_ = + WebKit::WebStorageNamespace::m_sessionStorageQuota; + +DOMStorageContext::DOMStorageContext(const FilePath& local_storage_path) + : local_storage_path_(local_storage_path), + last_storage_area_id_(0), last_session_storage_namespace_id_on_ui_thread_(kLocalStorageNamespaceId), last_session_storage_namespace_id_on_io_thread_(kLocalStorageNamespaceId){ } @@ -103,15 +111,20 @@ DOMStorageNamespace* DOMStorageContext::GetStorageNamespace( 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(); +DOMStorageArea* DOMStorageContext::GetStorageArea(int64 namespace_id, + const string16& origin, bool allocation_allowed) { + DCHECK(CefThread::CurrentlyOn(CefThread::UI)); + DOMStorageNamespace* ns = + GetStorageNamespace(namespace_id, allocation_allowed); + if (ns) + return ns->GetStorageArea(origin, allocation_allowed); + return NULL; +} + +void DOMStorageContext::PurgeMemory(int64 namespace_id) { + DOMStorageNamespace* ns = GetStorageNamespace(namespace_id, false); + if (ns) + ns->PurgeMemory(); } void DOMStorageContext::DeleteDataModifiedSince( @@ -120,11 +133,13 @@ void DOMStorageContext::DeleteDataModifiedSince( 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(); + PurgeMemory(kLocalStorageNamespaceId); + + if (local_storage_path_.empty()) + return; - FilePath data_path(_Context->cache_path()); file_util::FileEnumerator file_enumerator( - data_path.Append(kLocalStorageDirectory), false, + local_storage_path_.Append(kLocalStorageDirectory), false, file_util::FileEnumerator::FILES); for (FilePath path = file_enumerator.Next(); !path.value().empty(); path = file_enumerator.Next()) { @@ -147,21 +162,24 @@ void DOMStorageContext::DeleteDataModifiedSince( } } -void DOMStorageContext::DeleteLocalStorageFile(const FilePath& file_path) { +void DOMStorageContext::DeleteLocalStorageForOrigin(const string16& origin) { DCHECK(CefThread::CurrentlyOn(CefThread::UI)); + + DOMStorageArea* area = + GetStorageArea(kLocalStorageNamespaceId, origin, false); + if (!area) + return; - // 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); -} + // Calling Clear() is necessary to remove the data from the namespace. + area->Clear(); + area->PurgeMemory(); -void DOMStorageContext::DeleteLocalStorageForOrigin(const string16& origin_id) { - DCHECK(CefThread::CurrentlyOn(CefThread::UI)); - DeleteLocalStorageFile(GetLocalStorageFilePath(origin_id)); + if (local_storage_path_.empty()) + return; + + FilePath file_path = GetLocalStorageFilePath(origin); + if (!file_path.empty()) + file_util::Delete(file_path, false); } void DOMStorageContext::DeleteAllLocalStorageFiles() { @@ -169,11 +187,13 @@ void DOMStorageContext::DeleteAllLocalStorageFiles() { // Make sure that we don't delete a database that's currently being accessed // by unloading all of the databases temporarily. - PurgeMemory(); + PurgeMemory(kLocalStorageNamespaceId); + + if (local_storage_path_.empty()) + return; - FilePath data_path(_Context->cache_path()); file_util::FileEnumerator file_enumerator( - data_path.Append(kLocalStorageDirectory), false, + local_storage_path_.Append(kLocalStorageDirectory), false, file_util::FileEnumerator::FILES); for (FilePath file_path = file_enumerator.Next(); !file_path.empty(); file_path = file_enumerator.Next()) { @@ -183,11 +203,9 @@ void DOMStorageContext::DeleteAllLocalStorageFiles() { } DOMStorageNamespace* DOMStorageContext::CreateLocalStorage() { - FilePath data_path(_Context->cache_path()); FilePath dir_path; - if (!data_path.empty()) - dir_path = data_path.Append(kLocalStorageDirectory); - + if (!local_storage_path_.empty()) + dir_path = local_storage_path_.Append(kLocalStorageDirectory); DOMStorageNamespace* new_namespace = DOMStorageNamespace::CreateLocalStorageNamespace(this, dir_path); RegisterStorageNamespace(new_namespace); @@ -240,9 +258,13 @@ void DOMStorageContext::ClearLocalState(const FilePath& profile_path, } FilePath DOMStorageContext::GetLocalStorageFilePath( - const string16& origin_id) const { - FilePath data_path(_Context->cache_path()); - FilePath storageDir = data_path.Append( + const string16& origin) const { + DCHECK(!local_storage_path_.empty()); + + string16 origin_id = + webkit_database::DatabaseUtil::GetOriginIdentifier(GURL(origin)); + + FilePath storageDir = local_storage_path_.Append( DOMStorageContext::kLocalStorageDirectory); FilePath::StringType id = webkit_glue::WebStringToFilePathString(origin_id); diff --git a/libcef/dom_storage_context.h b/libcef/dom_storage_context.h index f2e3ebf9a..d71f17066 100644 --- a/libcef/dom_storage_context.h +++ b/libcef/dom_storage_context.h @@ -24,7 +24,7 @@ class DOMStorageNamespace; // NOTE: Virtual methods facilitate mocking functions for testing. class DOMStorageContext { public: - DOMStorageContext(); + DOMStorageContext(const FilePath& local_storage_path); virtual ~DOMStorageContext(); // Allocate a new storage area id. Only call on the WebKit thread. @@ -51,8 +51,14 @@ class DOMStorageContext { // namespace if it hasn't been already. DOMStorageNamespace* GetStorageNamespace(int64 id, bool allocation_allowed); + // Get a storage area with the specified namespace_id and origin. If + // allocation_allowed is true this function will create a new namespace and/or + // storage area if it doesn't already exist. + DOMStorageArea* GetStorageArea(int64 namespace_id, const string16& origin, + bool allocation_allowed); + // Tells storage namespaces to purge any memory they do not need. - virtual void PurgeMemory(); + virtual void PurgeMemory(int64 namespace_id); // Delete any local storage files that have been touched since the cutoff // date that's supplied. @@ -60,11 +66,8 @@ class DOMStorageContext { 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); + void DeleteLocalStorageForOrigin(const string16& origin); // Deletes all local storage files. void DeleteAllLocalStorageFiles(); @@ -80,7 +83,16 @@ class DOMStorageContext { 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; + FilePath GetLocalStorageFilePath(const string16& origin) const; + + // Set the quota limits for localStorage and sessionStorage respectively. + // Changes will only take affect if made before creation of the namespaces. + static void set_local_storage_quota(unsigned int quota) + { local_storage_quota_ = quota; } + static void set_session_storage_quota(unsigned int quota) + { session_storage_quota_ = quota; } + static unsigned int local_storage_quota() { return local_storage_quota_; } + static unsigned int session_storage_quota() { return session_storage_quota_; } private: // Get the local storage instance. The object is owned by this class. @@ -99,6 +111,10 @@ class DOMStorageContext { static void CompleteCloningSessionStorage(DOMStorageContext* context, int64 existing_id, int64 clone_id); + // Location where localStorage files will be stored on disk. This may be empty + // in which case localStorage data will be stored in-memory only. + FilePath local_storage_path_; + // 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 @@ -116,6 +132,10 @@ class DOMStorageContext { // Maps ids to StorageNamespaces. We own these objects. typedef std::map StorageNamespaceMap; StorageNamespaceMap storage_namespace_map_; + + // Quota limits for localStorage and sessionStorage respectively. + static unsigned int local_storage_quota_; + static unsigned int session_storage_quota_; }; #endif // _DOM_STORAGE_CONTEXT_H diff --git a/libcef/dom_storage_namespace.cc b/libcef/dom_storage_namespace.cc index 6e965cade..02d60c43c 100644 --- a/libcef/dom_storage_namespace.cc +++ b/libcef/dom_storage_namespace.cc @@ -21,8 +21,11 @@ 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); + WebString path; + if (!data_dir_path.empty()) + path = webkit_glue::FilePathToWebString(data_dir_path); + return new DOMStorageNamespace(dom_storage_context, id, path, + DOM_STORAGE_LOCAL); } /* static */ @@ -55,12 +58,15 @@ DOMStorageNamespace::~DOMStorageNamespace() { } DOMStorageArea* DOMStorageNamespace::GetStorageArea( - const string16& origin) { + const string16& origin, bool allocation_allowed) { // 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; + if (!allocation_allowed) + return NULL; + // We need to create a new one. int64 id = dom_storage_context_->AllocateStorageAreaId(); DCHECK(!dom_storage_context_->GetStorageArea(id)); @@ -81,8 +87,16 @@ DOMStorageNamespace* DOMStorageNamespace::Copy(int64 id) { return new_storage_namespace; } +void DOMStorageNamespace::GetStorageAreas(std::vector& areas, + bool skip_empty) const { + OriginToStorageAreaMap::const_iterator iter = origin_to_storage_area_.begin(); + for (; iter != origin_to_storage_area_.end(); ++iter) { + if (!skip_empty || iter->second->Length() > 0) + areas.push_back(iter->second); + } +} + 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(); @@ -102,9 +116,9 @@ void DOMStorageNamespace::CreateWebStorageNamespaceIfNecessary() { if (dom_storage_type_ == DOM_STORAGE_LOCAL) { storage_namespace_.reset( WebStorageNamespace::createLocalStorageNamespace(data_dir_path_, - WebStorageNamespace::m_localStorageQuota)); + DOMStorageContext::local_storage_quota())); } else { storage_namespace_.reset(WebStorageNamespace::createSessionStorageNamespace( - WebStorageNamespace::m_sessionStorageQuota)); + DOMStorageContext::session_storage_quota())); } } diff --git a/libcef/dom_storage_namespace.h b/libcef/dom_storage_namespace.h index 71e09b75d..1eccaf420 100644 --- a/libcef/dom_storage_namespace.h +++ b/libcef/dom_storage_namespace.h @@ -31,9 +31,13 @@ class DOMStorageNamespace { ~DOMStorageNamespace(); - DOMStorageArea* GetStorageArea(const string16& origin); + DOMStorageArea* GetStorageArea(const string16& origin, + bool allocation_allowed); DOMStorageNamespace* Copy(int64 clone_namespace_id); + void GetStorageAreas(std::vector& areas, + bool skip_empty) const; + void PurgeMemory(); const DOMStorageContext* dom_storage_context() const { diff --git a/libcef/origin_whitelist_impl.cc b/libcef/origin_whitelist_impl.cc index c98ef369e..d5c1991a6 100644 --- a/libcef/origin_whitelist_impl.cc +++ b/libcef/origin_whitelist_impl.cc @@ -4,6 +4,7 @@ #include "include/cef.h" #include "cef_context.h" +#include "cef_thread.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityPolicy.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" diff --git a/libcef/scheme_impl.cc b/libcef/scheme_impl.cc index e161f6247..9f5daebe0 100644 --- a/libcef/scheme_impl.cc +++ b/libcef/scheme_impl.cc @@ -5,6 +5,7 @@ #include "include/cef.h" #include "cef_context.h" +#include "cef_thread.h" #include "request_impl.h" #include "response_impl.h" diff --git a/libcef_dll/cpptoc/storage_visitor_cpptoc.cc b/libcef_dll/cpptoc/storage_visitor_cpptoc.cc new file mode 100644 index 000000000..fcb357acb --- /dev/null +++ b/libcef_dll/cpptoc/storage_visitor_cpptoc.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2011 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. +// +// --------------------------------------------------------------------------- +// +// A portion of this file was generated by the CEF translator tool. When +// making changes by hand only do so within the body of existing function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// + +#include "libcef_dll/cpptoc/storage_visitor_cpptoc.h" + + +// MEMBER FUNCTIONS - Body may be edited by hand. + +int CEF_CALLBACK storage_visitor_visit(struct _cef_storage_visitor_t* self, + enum cef_storage_type_t type, const cef_string_t* origin, + const cef_string_t* key, const cef_string_t* value, int count, int total, + int* deleteData) +{ + DCHECK(self); + DCHECK(origin); + DCHECK(deleteData); + if (!self || !origin || !deleteData) + return false; + + bool delVal = (*deleteData)?true:false; + bool retVal = CefStorageVisitorCppToC::Get(self)->Visit(type, origin, key, + value, count, total, delVal); + *deleteData = delVal; + + return retVal; +} + + +// CONSTRUCTOR - Do not edit by hand. + +CefStorageVisitorCppToC::CefStorageVisitorCppToC(CefStorageVisitor* cls) + : CefCppToC(cls) +{ + struct_.struct_.visit = storage_visitor_visit; +} + +#ifndef NDEBUG +template<> long CefCppToC::DebugObjCt = 0; +#endif + diff --git a/libcef_dll/cpptoc/storage_visitor_cpptoc.h b/libcef_dll/cpptoc/storage_visitor_cpptoc.h new file mode 100644 index 000000000..ee4474241 --- /dev/null +++ b/libcef_dll/cpptoc/storage_visitor_cpptoc.h @@ -0,0 +1,35 @@ +// Copyright (c) 2011 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. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool and should not edited +// by hand. See the translator.README.txt file in the tools directory for +// more information. +// +#ifndef _STORAGEVISITOR_CPPTOC_H +#define _STORAGEVISITOR_CPPTOC_H + +#ifndef USING_CEF_SHARED +#pragma message("Warning: "__FILE__" may be accessed wrapper-side only") +#else // USING_CEF_SHARED + +#include "include/cef.h" +#include "include/cef_capi.h" +#include "libcef_dll/cpptoc/cpptoc.h" + +// Wrap a C++ class with a C structure. +// This class may be instantiated and accessed wrapper-side only. +class CefStorageVisitorCppToC + : public CefCppToC +{ +public: + CefStorageVisitorCppToC(CefStorageVisitor* cls); + virtual ~CefStorageVisitorCppToC() {} +}; + +#endif // USING_CEF_SHARED +#endif // _STORAGEVISITOR_CPPTOC_H + diff --git a/libcef_dll/ctocpp/storage_visitor_ctocpp.cc b/libcef_dll/ctocpp/storage_visitor_ctocpp.cc new file mode 100644 index 000000000..91c2f4627 --- /dev/null +++ b/libcef_dll/ctocpp/storage_visitor_ctocpp.cc @@ -0,0 +1,39 @@ +// Copyright (c) 2011 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. +// +// --------------------------------------------------------------------------- +// +// A portion of this file was generated by the CEF translator tool. When +// making changes by hand only do so within the body of existing static and +// virtual method implementations. See the translator.README.txt file in the +// tools directory for more information. +// + +#include "libcef_dll/ctocpp/storage_visitor_ctocpp.h" + + +// VIRTUAL METHODS - Body may be edited by hand. + +bool CefStorageVisitorCToCpp::Visit(CefStorageType type, + const CefString& origin, const CefString& key, const CefString& value, + int count, int total, bool& deleteData) +{ + if (CEF_MEMBER_MISSING(struct_, visit)) + return false; + + int delVal = deleteData; + bool retVal = struct_->visit(struct_, type, origin.GetStruct(), + key.GetStruct(), value.GetStruct(), count, total, &delVal) ? + true : false; + deleteData = delVal?true:false; + + return retVal; +} + + +#ifndef NDEBUG +template<> long CefCToCpp::DebugObjCt = 0; +#endif + diff --git a/libcef_dll/ctocpp/storage_visitor_ctocpp.h b/libcef_dll/ctocpp/storage_visitor_ctocpp.h new file mode 100644 index 000000000..461b20276 --- /dev/null +++ b/libcef_dll/ctocpp/storage_visitor_ctocpp.h @@ -0,0 +1,43 @@ +// Copyright (c) 2011 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. +// +// ------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool and should not edited +// by hand. See the translator.README.txt file in the tools directory for +// more information. +// + +#ifndef _STORAGEVISITOR_CTOCPP_H +#define _STORAGEVISITOR_CTOCPP_H + +#ifndef BUILDING_CEF_SHARED +#pragma message("Warning: "__FILE__" may be accessed DLL-side only") +#else // BUILDING_CEF_SHARED + +#include "include/cef.h" +#include "include/cef_capi.h" +#include "libcef_dll/ctocpp/ctocpp.h" + +// Wrap a C structure with a C++ class. +// This class may be instantiated and accessed DLL-side only. +class CefStorageVisitorCToCpp + : public CefCToCpp +{ +public: + CefStorageVisitorCToCpp(cef_storage_visitor_t* str) + : CefCToCpp(str) {} + virtual ~CefStorageVisitorCToCpp() {} + + // CefStorageVisitor methods + virtual bool Visit(CefStorageType type, const CefString& origin, + const CefString& key, const CefString& value, int count, int total, + bool& deleteData) OVERRIDE; +}; + +#endif // BUILDING_CEF_SHARED +#endif // _STORAGEVISITOR_CTOCPP_H + diff --git a/libcef_dll/libcef_dll.cc b/libcef_dll/libcef_dll.cc index 212d01236..8379ca912 100644 --- a/libcef_dll/libcef_dll.cc +++ b/libcef_dll/libcef_dll.cc @@ -29,6 +29,7 @@ #include "ctocpp/read_handler_ctocpp.h" #include "ctocpp/scheme_handler_ctocpp.h" #include "ctocpp/scheme_handler_factory_ctocpp.h" +#include "ctocpp/storage_visitor_ctocpp.h" #include "ctocpp/task_ctocpp.h" #include "ctocpp/v8accessor_ctocpp.h" #include "ctocpp/v8handler_ctocpp.h" @@ -78,6 +79,7 @@ CEF_EXPORT void cef_shutdown() DCHECK(CefReadHandlerCToCpp::DebugObjCt == 0); DCHECK(CefSchemeHandlerCToCpp::DebugObjCt == 0); DCHECK(CefSchemeHandlerFactoryCToCpp::DebugObjCt == 0); + DCHECK(CefStorageVisitorCToCpp::DebugObjCt == 0); DCHECK(CefV8AccessorCToCpp::DebugObjCt == 0); DCHECK(CefV8HandlerCToCpp::DebugObjCt == 0); DCHECK(CefWebURLRequestClientCToCpp::DebugObjCt == 0); @@ -288,3 +290,47 @@ CEF_EXPORT int cef_delete_cookies(const cef_string_t* url, return CefDeleteCookies(urlStr, cookieNameStr); } + +CEF_EXPORT int cef_visit_storage(enum cef_storage_type_t type, + const cef_string_t* origin, const cef_string_t* key, + struct _cef_storage_visitor_t* visitor) +{ + CefString originStr, keyStr; + + if (origin) + originStr = origin; + if (key) + keyStr = key; + + return CefVisitStorage(type, originStr, keyStr, + CefStorageVisitorCToCpp::Wrap(visitor)); +} + +CEF_EXPORT int cef_set_storage(enum cef_storage_type_t type, + const cef_string_t* origin, const cef_string_t* key, + const cef_string_t* value) +{ + CefString originStr, keyStr, valueStr; + + if (origin) + originStr = origin; + if (key) + keyStr = key; + if (value) + valueStr = value; + + return CefSetStorage(type, originStr, keyStr, valueStr); +} + +CEF_EXPORT int cef_delete_storage(enum cef_storage_type_t type, + const cef_string_t* origin, const cef_string_t* key) +{ + CefString originStr, keyStr; + + if (origin) + originStr = origin; + if (key) + keyStr = key; + + return CefDeleteStorage(type, origin, key); +} diff --git a/libcef_dll/wrapper/libcef_dll_wrapper.cc b/libcef_dll/wrapper/libcef_dll_wrapper.cc index 81e600783..92a1458a0 100644 --- a/libcef_dll/wrapper/libcef_dll_wrapper.cc +++ b/libcef_dll/wrapper/libcef_dll_wrapper.cc @@ -14,6 +14,7 @@ #include "libcef_dll/cpptoc/read_handler_cpptoc.h" #include "libcef_dll/cpptoc/scheme_handler_cpptoc.h" #include "libcef_dll/cpptoc/scheme_handler_factory_cpptoc.h" +#include "libcef_dll/cpptoc/storage_visitor_cpptoc.h" #include "libcef_dll/cpptoc/task_cpptoc.h" #include "libcef_dll/cpptoc/v8accessor_cpptoc.h" #include "libcef_dll/cpptoc/v8handler_cpptoc.h" @@ -54,6 +55,7 @@ void CefShutdown() DCHECK(CefReadHandlerCppToC::DebugObjCt == 0); DCHECK(CefSchemeHandlerCppToC::DebugObjCt == 0); DCHECK(CefSchemeHandlerFactoryCppToC::DebugObjCt == 0); + DCHECK(CefStorageVisitorCppToC::DebugObjCt == 0); DCHECK(CefV8AccessorCppToC::DebugObjCt == 0); DCHECK(CefV8HandlerCppToC::DebugObjCt == 0); DCHECK(CefWebURLRequestClientCppToC::DebugObjCt == 0); @@ -202,3 +204,25 @@ bool CefDeleteCookies(const CefString& url, const CefString& cookie_name) return cef_delete_cookies(url.GetStruct(), cookie_name.GetStruct()) ? true : false; } + +bool CefVisitStorage(CefStorageType type, const CefString& origin, + const CefString& key, + CefRefPtr visitor) +{ + return cef_visit_storage(type, origin.GetStruct(), key.GetStruct(), + CefStorageVisitorCppToC::Wrap(visitor)) ? true : false; +} + +bool CefSetStorage(CefStorageType type, const CefString& origin, + const CefString& key, const CefString& value) +{ + return cef_set_storage(type, origin.GetStruct(), key.GetStruct(), + value.GetStruct()) ? true : false; +} + +bool CefDeleteStorage(CefStorageType type, const CefString& origin, + const CefString& key) +{ + return cef_delete_storage(type, origin.GetStruct(), key.GetStruct()) ? + true : false; +} diff --git a/tests/unittests/storage_unittest.cc b/tests/unittests/storage_unittest.cc new file mode 100644 index 000000000..3d15e986e --- /dev/null +++ b/tests/unittests/storage_unittest.cc @@ -0,0 +1,394 @@ +// Copyright (c) 2011 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 "include/cef.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "test_handler.h" + +namespace { + +static const char* kOrigin = "http://tests"; +static const char* kNav1 = "http://tests/nav1.html"; +static const char* kNav2 = "http://tests/nav2.html"; + +static const char* kKey1 = "foo"; +static const char* kVal1 = "bar"; +static const char* kKey2 = "choo"; +static const char* kVal2 = "whatzit"; + +class StorageTestHandler : public TestHandler +{ +public: + class V8Handler : public CefV8Handler + { + public: + V8Handler(CefRefPtr tester) + : tester_(tester) {} + + virtual bool Execute(const CefString& name, + CefRefPtr object, + const CefV8ValueList& arguments, + CefRefPtr& retval, + CefString& exception) OVERRIDE + { + if (arguments.size() != 2) + return false; + + std::string key = arguments[0]->GetStringValue(); + std::string val = arguments[1]->GetStringValue(); + + if (key == kKey1 && val == kVal1) + tester_->got_js_read1_.yes(); + else if (key == kKey2 && val == kVal2) + tester_->got_js_read2_.yes(); + + return true; + } + + CefRefPtr tester_; + + IMPLEMENT_REFCOUNTING(V8Handler); + }; + + class StorageVisitor : public CefStorageVisitor + { + public: + enum Mode { + VisitKey, + DeleteKey1, + DeleteKey2 + }; + + StorageVisitor(CefRefPtr tester, + const std::string& description, Mode mode, + TrackCallback* callback1, TrackCallback* callback2, + int expected_total) + : tester_(tester), description_(description), mode_(mode), + callback1_(callback1), callback2_(callback2), + expected_total_(expected_total), actual_total_(0) + { + } + virtual ~StorageVisitor() + { + EXPECT_EQ(expected_total_, actual_total_) << "test = "<< description_; + } + + virtual bool Visit(CefStorageType type, const CefString& origin, + const CefString& key, const CefString& value, int count, + int total, bool& deleteData) OVERRIDE + { + EXPECT_EQ(type, tester_->type_); + std::string originStr = origin; + EXPECT_EQ(originStr, kOrigin); + + std::string keyStr = key; + std::string valueStr = value; + if (keyStr == kKey1 && valueStr == kVal1) + callback1_->yes(); + else if(keyStr == kKey2 && valueStr == kVal2) + callback2_->yes(); + + EXPECT_EQ(expected_total_, total) << "test = "<< description_; + + if((mode_ == DeleteKey1 && keyStr == kKey1) || + (mode_ == DeleteKey2 && keyStr == kKey2)) + deleteData = true; + + actual_total_++; + + return true; + } + + CefRefPtr tester_; + std::string description_; + Mode mode_; + TrackCallback* callback1_; + TrackCallback* callback2_; + int expected_total_; + int actual_total_; + + IMPLEMENT_REFCOUNTING(StorageVisitor); + }; + + StorageTestHandler(CefStorageType type) + : type_(type), nav_(0) {} + + virtual void RunTest() OVERRIDE + { + std::stringstream ss; + + std::string func = (type_==ST_LOCALSTORAGE?"localStorage":"sessionStorage"); + + // Values will be set vis JS on page load. + ss << "Nav1"; + AddResource(kNav1, ss.str(), "text/html"); + ss.str(""); + + // Values will be verified vis JS on page load. + ss << "Nav2"; + AddResource(kNav2, ss.str(), "text/html"); + ss.str(""); + + // Create the browser. + CreateBrowser(kNav1); + } + + virtual void OnLoadEnd(CefRefPtr browser, + CefRefPtr frame, + int httpStatusCode) OVERRIDE + { + if (nav_ == 0) { + // Verify read all. + CefVisitStorage(type_, "", "", + new StorageVisitor(this, "all_read", + StorageVisitor::VisitKey, + &got_cpp_all_read1_, + &got_cpp_all_read2_, 2)); + + // Verify read origin. + CefVisitStorage(type_, kOrigin, "", + new StorageVisitor(this, "origin_read", + StorageVisitor::VisitKey, + &got_cpp_origin_read1_, + &got_cpp_origin_read2_, 2)); + + // Verify read key1. + CefVisitStorage(type_, kOrigin, kKey1, + new StorageVisitor(this, "key1_read", + StorageVisitor::VisitKey, + &got_cpp_key_read1_, + &got_cpp_key_read1_fail_, 1)); + + // Verify read key2. + CefVisitStorage(type_, kOrigin, kKey2, + new StorageVisitor(this, "key2_read", + StorageVisitor::VisitKey, + &got_cpp_key_read2_fail_, + &got_cpp_key_read2_, 1)); + + // Delete key1. Verify that key2 still gets read. + CefVisitStorage(type_, kOrigin, "", + new StorageVisitor(this, "key1_delete", + StorageVisitor::DeleteKey1, + &got_cpp_key_delete1_delete_, + &got_cpp_key_delete1_, 2)); + + // Verify that key1 was deleted. + CefVisitStorage(type_, kOrigin, "", + new StorageVisitor(this, "key1_delete_verify", + StorageVisitor::VisitKey, + &got_cpp_afterdeletevisit1_fail_, + &got_cpp_afterdeletevisit1_, 1)); + + // Delete key2. + CefVisitStorage(type_, kOrigin, "", + new StorageVisitor(this, "key2_delete", + StorageVisitor::DeleteKey2, + &got_cpp_key_delete2_fail_, + &got_cpp_key_delete2_delete_, 1)); + + // Verify that all keys have been deleted. + CefVisitStorage(type_, kOrigin, "", + new StorageVisitor(this, "key2_delete_verify", + StorageVisitor::VisitKey, + &got_cpp_afterdeletevisit2_fail_, + &got_cpp_afterdeletevisit2_fail_, 0)); + + // Reset the values. + CefSetStorage(type_, kOrigin, kKey1, kVal1); + CefSetStorage(type_, kOrigin, kKey2, kVal2); + + // Verify that all values have been reset. + CefVisitStorage(type_, "", "", + new StorageVisitor(this, "reset1a_verify", + StorageVisitor::VisitKey, + &got_cpp_all_reset1a_, + &got_cpp_all_reset2a_, 2)); + + // Delete all values. + CefDeleteStorage(type_, "", ""); + + // Verify that all values have been deleted. + CefVisitStorage(type_, "", "", + new StorageVisitor(this, "delete_all_verify", + StorageVisitor::VisitKey, + &got_cpp_afterdeleteall_fail_, + &got_cpp_afterdeleteall_fail_, 0)); + + // Reset all values. + CefSetStorage(type_, kOrigin, kKey1, kVal1); + CefSetStorage(type_, kOrigin, kKey2, kVal2); + + // Verify that all values have been reset. + CefVisitStorage(type_, "", "", + new StorageVisitor(this, "reset1b_verify", + StorageVisitor::VisitKey, + &got_cpp_all_reset1b_, + &got_cpp_all_reset2b_, 2)); + + // Delete all values by origin. + CefDeleteStorage(type_, kOrigin, ""); + + // Verify that all values have been deleted. + CefVisitStorage(type_, "", "", + new StorageVisitor(this, "delete_origin_verify", + StorageVisitor::VisitKey, + &got_cpp_afterdeleteorigin_fail_, + &got_cpp_afterdeleteorigin_fail_, 0)); + + // Reset the values. + CefSetStorage(type_, kOrigin, kKey1, kVal1); + CefSetStorage(type_, kOrigin, kKey2, kVal2); + + // Verify that all values have been reset. + CefVisitStorage(type_, "", "", + new StorageVisitor(this, "reset1c_verify", + StorageVisitor::VisitKey, + &got_cpp_all_reset1c_, + &got_cpp_all_reset2c_, 2)); + + // Delete key1. + CefDeleteStorage(type_, kOrigin, kKey1); + + // Verify that key1 has been deleted. + CefVisitStorage(type_, "", "", + new StorageVisitor(this, "direct_key1_delete_verify", + StorageVisitor::VisitKey, + &got_cpp_afterdeletekey1_fail_, + &got_cpp_afterdeletekey1_, 1)); + + // Delete key2. + CefDeleteStorage(type_, kOrigin, kKey2); + + // Verify that all values have been deleted. + CefVisitStorage(type_, "", "", + new StorageVisitor(this, "direct_key2_delete_verify", + StorageVisitor::VisitKey, + &got_cpp_afterdeletekey2_fail_, + &got_cpp_afterdeletekey2_fail_, 0)); + + // Reset all values. + CefSetStorage(type_, kOrigin, kKey1, kVal1); + CefSetStorage(type_, kOrigin, kKey2, kVal2); + + // Verify that all values have been reset. + CefVisitStorage(type_, "", "", + new StorageVisitor(this, "reset1d_verify", + StorageVisitor::VisitKey, + &got_cpp_all_reset1d_, + &got_cpp_all_reset2d_, 2)); + + nav_++; + // Verify JS read after navigation. + frame->LoadURL(kNav2); + } else { + DestroyTest(); + } + } + + virtual void OnJSBinding(CefRefPtr browser, + CefRefPtr frame, + CefRefPtr object) OVERRIDE + { + CefRefPtr handler = new V8Handler(this); + CefRefPtr testObj = CefV8Value::CreateObject(NULL, NULL); + testObj->SetValue("result", CefV8Value::CreateFunction("result", handler)); + object->SetValue("test", testObj); + } + + CefStorageType type_; + int nav_; + + TrackCallback got_cpp_all_read1_; + TrackCallback got_cpp_all_read2_; + TrackCallback got_cpp_origin_read1_; + TrackCallback got_cpp_origin_read2_; + TrackCallback got_cpp_key_read1_; + TrackCallback got_cpp_key_read1_fail_; + TrackCallback got_cpp_key_read2_; + TrackCallback got_cpp_key_read2_fail_; + TrackCallback got_cpp_key_delete1_; + TrackCallback got_cpp_key_delete1_delete_; + TrackCallback got_cpp_key_delete2_delete_; + TrackCallback got_cpp_key_delete2_fail_; + TrackCallback got_cpp_afterdeletevisit1_; + TrackCallback got_cpp_afterdeletevisit1_fail_; + TrackCallback got_cpp_afterdeletevisit2_fail_; + TrackCallback got_cpp_all_reset1a_; + TrackCallback got_cpp_all_reset2a_; + TrackCallback got_cpp_afterdeleteall_fail_; + TrackCallback got_cpp_all_reset1b_; + TrackCallback got_cpp_all_reset2b_; + TrackCallback got_cpp_afterdeleteorigin_fail_; + TrackCallback got_cpp_all_reset1c_; + TrackCallback got_cpp_all_reset2c_; + TrackCallback got_cpp_afterdeletekey1_; + TrackCallback got_cpp_afterdeletekey1_fail_; + TrackCallback got_cpp_afterdeletekey2_fail_; + TrackCallback got_cpp_all_reset1d_; + TrackCallback got_cpp_all_reset2d_; + TrackCallback got_js_read1_; + TrackCallback got_js_read2_; +}; + +void StorageTest(CefStorageType type) +{ + CefRefPtr handler = new StorageTestHandler(type); + handler->ExecuteTest(); + + EXPECT_TRUE(handler->got_cpp_all_read1_); + EXPECT_TRUE(handler->got_cpp_all_read2_); + EXPECT_TRUE(handler->got_cpp_origin_read1_); + EXPECT_TRUE(handler->got_cpp_origin_read2_); + EXPECT_TRUE(handler->got_cpp_key_read1_); + EXPECT_FALSE(handler->got_cpp_key_read1_fail_); + EXPECT_TRUE(handler->got_cpp_key_read2_); + EXPECT_FALSE(handler->got_cpp_key_read2_fail_); + EXPECT_TRUE(handler->got_cpp_key_delete1_); + EXPECT_TRUE(handler->got_cpp_key_delete1_delete_); + EXPECT_TRUE(handler->got_cpp_key_delete2_delete_); + EXPECT_FALSE(handler->got_cpp_key_delete2_fail_); + EXPECT_TRUE(handler->got_cpp_afterdeletevisit1_); + EXPECT_FALSE(handler->got_cpp_afterdeletevisit1_fail_); + EXPECT_FALSE(handler->got_cpp_afterdeletevisit2_fail_); + EXPECT_TRUE(handler->got_cpp_all_reset1a_); + EXPECT_TRUE(handler->got_cpp_all_reset2a_); + EXPECT_FALSE(handler->got_cpp_afterdeleteall_fail_); + EXPECT_TRUE(handler->got_cpp_all_reset1b_); + EXPECT_TRUE(handler->got_cpp_all_reset2b_); + EXPECT_FALSE(handler->got_cpp_afterdeleteorigin_fail_); + EXPECT_TRUE(handler->got_cpp_all_reset1c_); + EXPECT_TRUE(handler->got_cpp_all_reset2c_); + EXPECT_TRUE(handler->got_cpp_afterdeletekey1_); + EXPECT_FALSE(handler->got_cpp_afterdeletekey1_fail_); + EXPECT_FALSE(handler->got_cpp_afterdeletekey2_fail_); + EXPECT_TRUE(handler->got_cpp_all_reset1d_); + EXPECT_TRUE(handler->got_cpp_all_reset2d_); + EXPECT_TRUE(handler->got_js_read1_); + EXPECT_TRUE(handler->got_js_read2_); +} + +} // namespace + +// Test localStorage. +TEST(StorageTest, Local) +{ + StorageTest(ST_LOCALSTORAGE); +} + +// Test sessionStorage. +TEST(StorageTest, Session) +{ + StorageTest(ST_SESSIONSTORAGE); +} diff --git a/tests/unittests/test_suite.cc b/tests/unittests/test_suite.cc new file mode 100644 index 000000000..91a635721 --- /dev/null +++ b/tests/unittests/test_suite.cc @@ -0,0 +1,39 @@ +// Copyright (c) 2011 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 "include/cef.h" +#include "test_suite.h" +#include "base/command_line.h" +#include "build/build_config.h" +#include "base/threading/platform_thread.h" +#include "base/test/test_suite.h" + +CefTestSuite::CefTestSuite(int argc, char** argv) : TestSuite(argc, argv) { +} + +void CefTestSuite::Initialize() { + TestSuite::Initialize(); + + CefSettings settings; + settings.multi_threaded_message_loop = true; + + CommandLine* command_line = CommandLine::ForCurrentProcess(); + + if (command_line->HasSwitch("cache_path")) { + // Set the cache_path value. + std::string cache_path = command_line->GetSwitchValueASCII("cache_path"); + CefString(&settings.cache_path).FromASCII(cache_path.c_str()); + } + + CefInitialize(settings); +} + +void CefTestSuite::Shutdown() { + // Delay a bit so that the system has a chance to finish destroying windows + // before CefShutdown() checks for memory leaks. + base::PlatformThread::Sleep(500); + + CefShutdown(); + TestSuite::Shutdown(); +} diff --git a/tests/unittests/test_suite.h b/tests/unittests/test_suite.h index 425c0fcbc..7bf5f8857 100644 --- a/tests/unittests/test_suite.h +++ b/tests/unittests/test_suite.h @@ -1,38 +1,19 @@ -// Copyright (c) 2009 The Chromium Embedded Framework Authors. All rights +// Copyright (c) 2011 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 _CEF_TEST_SUITE_H #define _CEF_TEST_SUITE_H -#include "build/build_config.h" -#include "base/threading/platform_thread.h" #include "base/test/test_suite.h" -#include "include/cef.h" class CefTestSuite : public TestSuite { - public: - CefTestSuite(int argc, char** argv) : TestSuite(argc, argv) { - } +public: + CefTestSuite(int argc, char** argv); - protected: - - virtual void Initialize() { - TestSuite::Initialize(); - - CefSettings settings; - settings.multi_threaded_message_loop = true; - CefInitialize(settings); - } - - virtual void Shutdown() { - // Delay a bit so that the system has a chance to finish destroying windows - // before CefShutdown() checks for memory leaks. - base::PlatformThread::Sleep(500); - - CefShutdown(); - TestSuite::Shutdown(); - } +protected: + virtual void Initialize(); + virtual void Shutdown(); }; #endif // _CEF_TEST_SUITE_H