зеркало из https://github.com/mozilla/gecko-dev.git
Bug 600307 - localStorage and sessionStorage implementation overhaul, r=mak77+smaug, sr=smaug
This commit is contained in:
Родитель
899ae8e46f
Коммит
26d97547b7
|
@ -25,9 +25,9 @@ let gTests = [
|
|||
desc: "Check that clearing cookies does not clear storage",
|
||||
setup: function ()
|
||||
{
|
||||
Cc["@mozilla.org/dom/storagemanager;1"]
|
||||
.getService(Ci.nsIObserver)
|
||||
.observe(null, "cookie-changed", "cleared");
|
||||
Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService)
|
||||
.notifyObservers(null, "cookie-changed", "cleared");
|
||||
},
|
||||
run: function (aSnippetsMap)
|
||||
{
|
||||
|
|
|
@ -85,7 +85,12 @@ let DomStorage = {
|
|||
for (let [host, data] in Iterator(aStorageData)) {
|
||||
let uri = Services.io.newURI(host, null, null);
|
||||
let principal = Services.scriptSecurityManager.getDocShellCodebasePrincipal(uri, aDocShell);
|
||||
let storage = aDocShell.getSessionStorageForPrincipal(principal, "", true);
|
||||
let storageManager = aDocShell.QueryInterface(Components.interfaces.nsIDOMStorageManager);
|
||||
|
||||
// There is no need to pass documentURI, it's only used to fill documentURI property of
|
||||
// domstorage event, which in this case has no consumer. Prevention of events in case
|
||||
// of missing documentURI will be solved in a followup bug to bug 600307.
|
||||
let storage = storageManager.createStorage(principal, "", aDocShell.usePrivateBrowsing);
|
||||
|
||||
for (let [key, value] in Iterator(data)) {
|
||||
try {
|
||||
|
@ -110,12 +115,8 @@ let DomStorage = {
|
|||
let storage;
|
||||
|
||||
try {
|
||||
// Using getSessionStorageForPrincipal instead of
|
||||
// getSessionStorageForURI just to be able to pass aCreate = false,
|
||||
// that avoids creation of the sessionStorage object for the page
|
||||
// earlier than the page really requires it. It was causing problems
|
||||
// while accessing a storage when a page later changed its domain.
|
||||
storage = aDocShell.getSessionStorageForPrincipal(aPrincipal, "", false);
|
||||
let storageManager = aDocShell.QueryInterface(Components.interfaces.nsIDOMStorageManager);
|
||||
storage = storageManager.getStorage(aPrincipal);
|
||||
} catch (e) {
|
||||
// sessionStorage might throw if it's turned off, see bug 458954
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include "nsPIDOMWindow.h"
|
||||
#include "nsFrameLoader.h"
|
||||
#include "nsDOMTouchEvent.h"
|
||||
#include "nsDOMStorage.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#include "GeneratedEvents.h"
|
||||
#include "mozilla/dom/EventTarget.h"
|
||||
|
|
|
@ -849,8 +849,6 @@ nsDocShell::Init()
|
|||
rv = mContentListener->Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mStorages.Init();
|
||||
|
||||
// We want to hold a strong ref to the loadgroup, so it better hold a weak
|
||||
// ref to us... use an InterfaceRequestorProxy to do this.
|
||||
nsCOMPtr<InterfaceRequestorProxy> proxy =
|
||||
|
@ -917,6 +915,7 @@ NS_INTERFACE_MAP_BEGIN(nsDocShell)
|
|||
NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
|
||||
NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
|
||||
NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
|
||||
|
||||
///*****************************************************************************
|
||||
|
@ -2596,199 +2595,70 @@ nsDocShell::HistoryTransactionRemoved(int32_t aIndex)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIDOMStorageManager*
|
||||
nsDocShell::TopSessionStorageManager()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeItem> topItem;
|
||||
rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!topItem) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
|
||||
if (topDocShell != this) {
|
||||
return topDocShell->TopSessionStorageManager();
|
||||
}
|
||||
|
||||
if (!mSessionStorageManager) {
|
||||
mSessionStorageManager =
|
||||
do_CreateInstance("@mozilla.org/dom/sessionStorage-manager;1");
|
||||
}
|
||||
|
||||
return mSessionStorageManager;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
|
||||
const nsAString& aDocumentURI,
|
||||
bool aCreate,
|
||||
nsIDOMStorage** aStorage)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aStorage);
|
||||
*aStorage = nullptr;
|
||||
|
||||
if (!aPrincipal)
|
||||
return NS_OK;
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeItem> topItem;
|
||||
rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (!topItem)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
|
||||
if (topDocShell != this)
|
||||
return topDocShell->GetSessionStorageForPrincipal(aPrincipal,
|
||||
aDocumentURI,
|
||||
aCreate,
|
||||
aStorage);
|
||||
|
||||
nsXPIDLCString origin;
|
||||
rv = aPrincipal->GetOrigin(getter_Copies(origin));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (origin.IsEmpty())
|
||||
return NS_OK;
|
||||
|
||||
if (!mStorages.Get(origin, aStorage) && aCreate) {
|
||||
nsCOMPtr<nsIDOMStorage> newstorage =
|
||||
do_CreateInstance("@mozilla.org/dom/storage;2");
|
||||
if (!newstorage)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(newstorage);
|
||||
if (!pistorage)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
rv = pistorage->InitAsSessionStorage(aPrincipal, aDocumentURI, mInPrivateBrowsing);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
mStorages.Put(origin, newstorage);
|
||||
|
||||
newstorage.swap(*aStorage);
|
||||
#if defined(PR_LOGGING) && defined(DEBUG)
|
||||
PR_LOG(gDocShellLog, PR_LOG_DEBUG,
|
||||
("nsDocShell[%p]: created a new sessionStorage %p",
|
||||
this, *aStorage));
|
||||
#endif
|
||||
}
|
||||
else if (*aStorage) {
|
||||
nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(*aStorage);
|
||||
if (piStorage) {
|
||||
nsCOMPtr<nsIPrincipal> storagePrincipal = piStorage->Principal();
|
||||
|
||||
// The origin string used to map items in the hash table is
|
||||
// an implicit security check. That check is double-confirmed
|
||||
// by checking the principal a storage was demanded for
|
||||
// really is the principal for which that storage was originally
|
||||
// created. Originally, the check was hidden in the CanAccess
|
||||
// method but it's implementation has changed.
|
||||
bool equals;
|
||||
nsresult rv = aPrincipal->EqualsIgnoringDomain(storagePrincipal, &equals);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv) && equals,
|
||||
"GetSessionStorageForPrincipal got a storage "
|
||||
"that could not be accessed!");
|
||||
|
||||
if (NS_FAILED(rv) || !equals) {
|
||||
NS_RELEASE(*aStorage);
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(PR_LOGGING) && defined(DEBUG)
|
||||
PR_LOG(gDocShellLog, PR_LOG_DEBUG,
|
||||
("nsDocShell[%p]: returns existing sessionStorage %p",
|
||||
this, *aStorage));
|
||||
#endif
|
||||
nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
|
||||
if (!manager) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (aCreate) {
|
||||
// We are asked to create a new storage object. This indicates
|
||||
// that a new windows wants it. At this moment we "fork" the existing
|
||||
// storage object (what it means is described in the paragraph bellow).
|
||||
// We must create a single object per a single window to distinguish
|
||||
// a window originating oparations on the storage object to succesfully
|
||||
// prevent dispatch of a storage event to this same window that ivoked
|
||||
// a change in its storage. We also do this to correctly fill
|
||||
// documentURI property in the storage event.
|
||||
//
|
||||
// The difference between clone and fork is that clone creates
|
||||
// a completelly new and independent storage, but fork only creates
|
||||
// a new object wrapping the storage implementation and data and
|
||||
// the forked storage then behaves completelly the same way as
|
||||
// the storage it has been forked of, all such forked storage objects
|
||||
// shares their state and data and change on one such object affects
|
||||
// all others the same way.
|
||||
nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(*aStorage);
|
||||
nsCOMPtr<nsIDOMStorage> fork = piStorage->Fork(aDocumentURI);
|
||||
#if defined(PR_LOGGING) && defined(DEBUG)
|
||||
PR_LOG(gDocShellLog, PR_LOG_DEBUG,
|
||||
("nsDocShell[%p]: forked sessionStorage %p to %p",
|
||||
this, *aStorage, fork.get()));
|
||||
#endif
|
||||
fork.swap(*aStorage);
|
||||
return manager->CreateStorage(aPrincipal, aDocumentURI,
|
||||
mInPrivateBrowsing, aStorage);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return manager->GetStorage(aPrincipal, mInPrivateBrowsing, aStorage);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal,
|
||||
nsIDOMStorage* aStorage)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aStorage);
|
||||
|
||||
if (!aPrincipal)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeItem> topItem;
|
||||
nsresult rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (topItem) {
|
||||
nsCOMPtr<nsIDocShell> topDocShell = do_QueryInterface(topItem);
|
||||
if (topDocShell == this) {
|
||||
nsXPIDLCString origin;
|
||||
rv = aPrincipal->GetOrigin(getter_Copies(origin));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (origin.IsEmpty())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// Do not replace an existing session storage.
|
||||
if (mStorages.GetWeak(origin))
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
#if defined(PR_LOGGING) && defined(DEBUG)
|
||||
PR_LOG(gDocShellLog, PR_LOG_DEBUG,
|
||||
("nsDocShell[%p]: was added a sessionStorage %p",
|
||||
this, aStorage));
|
||||
#endif
|
||||
mStorages.Put(origin, aStorage);
|
||||
}
|
||||
else {
|
||||
return topDocShell->AddSessionStorage(aPrincipal, aStorage);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
CloneSessionStorages(nsCStringHashKey::KeyType aKey, nsIDOMStorage* aStorage,
|
||||
void* aUserArg)
|
||||
{
|
||||
nsIDocShell *docShell = static_cast<nsIDocShell*>(aUserArg);
|
||||
nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(aStorage);
|
||||
|
||||
if (pistorage) {
|
||||
nsCOMPtr<nsIDOMStorage> storage = pistorage->Clone();
|
||||
docShell->AddSessionStorage(pistorage->Principal(), storage);
|
||||
nsIPrincipal* storagePrincipal = pistorage->GetPrincipal();
|
||||
if (storagePrincipal != aPrincipal) {
|
||||
NS_ERROR("Wanting to add a sessionStorage for different principal");
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
|
||||
if (!manager) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::CloneSessionStoragesTo(nsIDocShell* aDocShell)
|
||||
{
|
||||
aDocShell->ClearSessionStorages();
|
||||
mStorages.EnumerateRead(CloneSessionStorages, aDocShell);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::ClearSessionStorages()
|
||||
{
|
||||
mStorages.Clear();
|
||||
return NS_OK;
|
||||
return manager->CloneStorage(aStorage);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "nsITextScroll.h"
|
||||
#include "nsIDocShellTreeOwner.h"
|
||||
#include "nsIContentViewerContainer.h"
|
||||
#include "nsIDOMStorageManager.h"
|
||||
|
||||
#include "nsDocLoader.h"
|
||||
#include "nsIURILoader.h"
|
||||
|
@ -146,7 +147,8 @@ class nsDocShell : public nsDocLoader,
|
|||
public nsILoadContext,
|
||||
public nsIWebShellServices,
|
||||
public nsILinkHandler,
|
||||
public nsIClipboardCommands
|
||||
public nsIClipboardCommands,
|
||||
public nsIDOMStorageManager
|
||||
{
|
||||
friend class nsDSURIContentListener;
|
||||
|
||||
|
@ -178,6 +180,7 @@ public:
|
|||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSICLIPBOARDCOMMANDS
|
||||
NS_DECL_NSIWEBSHELLSERVICES
|
||||
NS_FORWARD_SAFE_NSIDOMSTORAGEMANAGER(TopSessionStorageManager())
|
||||
|
||||
NS_IMETHOD Stop() {
|
||||
// Need this here because otherwise nsIWebNavigation::Stop
|
||||
|
@ -628,10 +631,8 @@ protected:
|
|||
|
||||
void ReattachEditorToWindow(nsISHEntry *aSHEntry);
|
||||
|
||||
nsresult GetSessionStorageForURI(nsIURI* aURI,
|
||||
const nsSubstring& aDocumentURI,
|
||||
bool create,
|
||||
nsIDOMStorage** aStorage);
|
||||
nsCOMPtr<nsIDOMStorageManager> mSessionStorageManager;
|
||||
nsIDOMStorageManager* TopSessionStorageManager();
|
||||
|
||||
// helpers for executing commands
|
||||
nsresult GetControllerForCommand(const char *inCommand,
|
||||
|
@ -676,9 +677,6 @@ protected:
|
|||
|
||||
bool HasUnloadedParent();
|
||||
|
||||
// hash of session storages, keyed by domain
|
||||
nsInterfaceHashtable<nsCStringHashKey, nsIDOMStorage> mStorages;
|
||||
|
||||
// Dimensions of the docshell
|
||||
nsIntRect mBounds;
|
||||
nsString mName;
|
||||
|
|
|
@ -40,7 +40,7 @@ interface nsIWebBrowserPrint;
|
|||
interface nsIVariant;
|
||||
interface nsIPrivacyTransitionObserver;
|
||||
|
||||
[scriptable, builtinclass, uuid(4277354d-5069-4278-935a-5d596ce9bfbf)]
|
||||
[scriptable, builtinclass, uuid(2b192c9c-dea4-4696-a445-1bef7bc0db6d)]
|
||||
interface nsIDocShell : nsIDocShellTreeItem
|
||||
{
|
||||
/**
|
||||
|
@ -407,6 +407,8 @@ interface nsIDocShell : nsIDocShellTreeItem
|
|||
void historyPurged(in long numEntries);
|
||||
|
||||
/*
|
||||
* @deprecated, use nsIDocShell.QueryInterface(nsIDOMStorageManager) instead.
|
||||
*
|
||||
* Retrieves the WebApps session storage object for the supplied principal.
|
||||
*
|
||||
* @param principal returns a storage for this principal
|
||||
|
@ -420,6 +422,8 @@ interface nsIDocShell : nsIDocShellTreeItem
|
|||
in boolean create);
|
||||
|
||||
/*
|
||||
* @deprecated, use nsIDocShell.QueryInterface(nsIDOMStorageManager) instead.
|
||||
*
|
||||
* Add a WebApps session storage object to the docshell.
|
||||
*
|
||||
* @param principal the principal the storage object is associated with
|
||||
|
@ -427,19 +431,6 @@ interface nsIDocShell : nsIDocShellTreeItem
|
|||
*/
|
||||
void addSessionStorage(in nsIPrincipal principal, in nsIDOMStorage storage);
|
||||
|
||||
/**
|
||||
* Clones all session storage objects and attaches them to the given docshell.
|
||||
* Useful when duplicating tabs and their states.
|
||||
*
|
||||
* @param docShell the docshell to clone the sessionstorage objects to
|
||||
*/
|
||||
void cloneSessionStoragesTo(in nsIDocShell docShell);
|
||||
|
||||
/**
|
||||
* Removes all WebApps session storage objects attached to the docshell.
|
||||
*/
|
||||
void clearSessionStorages();
|
||||
|
||||
/**
|
||||
* Gets the channel for the currently loaded document, if any.
|
||||
* For a new document load, this will be the channel of the previous document
|
||||
|
|
|
@ -250,7 +250,7 @@
|
|||
#include "nsIImageDocument.h"
|
||||
|
||||
// Storage includes
|
||||
#include "nsDOMStorage.h"
|
||||
#include "DOMStorage.h"
|
||||
|
||||
// Device Storage
|
||||
#include "nsIDOMDeviceStorage.h"
|
||||
|
@ -815,9 +815,6 @@ static nsDOMClassInfoData sClassInfoData[] = {
|
|||
// since a call to addProperty() is always followed by a call to
|
||||
// setProperty(), except in the case when a getter or setter is set
|
||||
// for a property. But we don't care about getters or setters here.
|
||||
NS_DEFINE_CLASSINFO_DATA(StorageObsolete, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
|
||||
NS_DEFINE_CLASSINFO_DATA(Storage, nsStorage2SH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS |
|
||||
nsIXPCScriptable::WANT_NEWRESOLVE |
|
||||
|
@ -826,8 +823,6 @@ static nsDOMClassInfoData sClassInfoData[] = {
|
|||
nsIXPCScriptable::WANT_DELPROPERTY |
|
||||
nsIXPCScriptable::DONT_ENUM_STATIC_PROPS |
|
||||
nsIXPCScriptable::WANT_NEWENUMERATE)
|
||||
NS_DEFINE_CLASSINFO_DATA(StorageItem, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
|
||||
NS_DEFINE_CLASSINFO_DATA(XULCommandEvent, nsEventSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
|
@ -2203,19 +2198,10 @@ nsDOMClassInfo::Init()
|
|||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMXPathResult)
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(StorageObsolete, nsIDOMStorageObsolete)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageObsolete)
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(Storage, nsIDOMStorage)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorage)
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(StorageItem, nsIDOMStorageItem)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageItem)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMToString)
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(XULCommandEvent, nsIDOMXULCommandEvent)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULCommandEvent)
|
||||
DOM_CLASSINFO_UI_EVENT_MAP_ENTRIES
|
||||
|
|
|
@ -150,9 +150,7 @@ DOMCI_CLASS(XPathNSResolver)
|
|||
DOMCI_CLASS(XPathResult)
|
||||
|
||||
// WhatWG WebApps Objects
|
||||
DOMCI_CLASS(StorageObsolete)
|
||||
DOMCI_CLASS(Storage)
|
||||
DOMCI_CLASS(StorageItem)
|
||||
|
||||
DOMCI_CLASS(XULCommandEvent)
|
||||
DOMCI_CLASS(CommandEvent)
|
||||
|
|
|
@ -18,12 +18,15 @@
|
|||
#include "nsPerformance.h"
|
||||
#include "nsDOMNavigationTiming.h"
|
||||
#include "nsBarProps.h"
|
||||
#include "nsDOMStorage.h"
|
||||
#include "nsIDOMStorage.h"
|
||||
#include "nsIDOMStorageManager.h"
|
||||
#include "DOMStorage.h"
|
||||
#include "nsDOMOfflineResourceList.h"
|
||||
#include "nsError.h"
|
||||
#include "nsIIdleService.h"
|
||||
#include "nsIPowerManagerService.h"
|
||||
#include "nsISizeOfEventTarget.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#ifdef GetClassName
|
||||
|
@ -2503,9 +2506,38 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
|||
}
|
||||
}
|
||||
|
||||
PreloadLocalStorage();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::PreloadLocalStorage()
|
||||
{
|
||||
if (!Preferences::GetBool(kStorageEnabled)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsChromeWindow()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIPrincipal* principal = GetPrincipal();
|
||||
if (!principal) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIDOMStorageManager> storageManager =
|
||||
do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
storageManager->PrecacheStorage(principal);
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::DispatchDOMWindowCreated()
|
||||
{
|
||||
|
@ -2691,19 +2723,6 @@ nsGlobalWindow::DetachFromDocShell()
|
|||
|
||||
MaybeForgiveSpamCount();
|
||||
CleanUp(false);
|
||||
|
||||
if (mLocalStorage) {
|
||||
nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_GetInterface(mLocalStorage);
|
||||
if (obs) {
|
||||
mDocShell->AddWeakPrivacyTransitionObserver(obs);
|
||||
}
|
||||
}
|
||||
if (mSessionStorage) {
|
||||
nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_GetInterface(mSessionStorage);
|
||||
if (obs) {
|
||||
mDocShell->AddWeakPrivacyTransitionObserver(obs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -8809,7 +8828,7 @@ nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
|
|||
"window %x owned sessionStorage "
|
||||
"that could not be accessed!");
|
||||
if (!canAccess) {
|
||||
mSessionStorage = nullptr;
|
||||
mSessionStorage = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8823,7 +8842,7 @@ nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
|
|||
}
|
||||
|
||||
// If the document has the sandboxed origin flag set
|
||||
// don't allow access to localStorage.
|
||||
// don't allow access to sessionStorage.
|
||||
if (!mDoc) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -8832,10 +8851,17 @@ nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
|
|||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsresult rv = docShell->GetSessionStorageForPrincipal(principal,
|
||||
documentURI,
|
||||
true,
|
||||
getter_AddRefs(mSessionStorage));
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(docShell, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
|
||||
|
||||
rv = storageManager->CreateStorage(principal,
|
||||
documentURI,
|
||||
loadContext && loadContext->UsePrivateBrowsing(),
|
||||
getter_AddRefs(mSessionStorage));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
|
@ -8847,17 +8873,12 @@ nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
|
|||
if (!mSessionStorage) {
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_GetInterface(mSessionStorage);
|
||||
if (obs) {
|
||||
docShell->AddWeakPrivacyTransitionObserver(obs);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
|
||||
PR_LogPrint("nsGlobalWindow %p returns %p sessionStorage", this, mSessionStorage.get());
|
||||
}
|
||||
if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
|
||||
PR_LogPrint("nsGlobalWindow %p returns %p sessionStorage", this, mSessionStorage.get());
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_ADDREF(*aSessionStorage = mSessionStorage);
|
||||
|
@ -8881,41 +8902,38 @@ nsGlobalWindow::GetLocalStorage(nsIDOMStorage ** aLocalStorage)
|
|||
|
||||
nsresult rv;
|
||||
|
||||
if (!nsDOMStorage::CanUseStorage())
|
||||
if (!DOMStorage::CanUseStorage()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsIPrincipal *principal = GetPrincipal();
|
||||
if (!principal)
|
||||
if (!principal) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMStorageManager> storageManager =
|
||||
do_GetService("@mozilla.org/dom/storagemanager;1", &rv);
|
||||
do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsString documentURI;
|
||||
if (mDocument) {
|
||||
mDocument->GetDocumentURI(documentURI);
|
||||
}
|
||||
|
||||
// If the document has the sandboxed origin flag set
|
||||
// don't allow access to localStorage.
|
||||
if (mDoc && (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsString documentURI;
|
||||
if (mDocument) {
|
||||
mDocument->GetDocumentURI(documentURI);
|
||||
}
|
||||
|
||||
nsIDocShell* docShell = GetDocShell();
|
||||
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
|
||||
|
||||
rv = storageManager->GetLocalStorageForPrincipal(principal,
|
||||
documentURI,
|
||||
loadContext && loadContext->UsePrivateBrowsing(),
|
||||
getter_AddRefs(mLocalStorage));
|
||||
rv = storageManager->CreateStorage(principal,
|
||||
documentURI,
|
||||
loadContext && loadContext->UsePrivateBrowsing(),
|
||||
getter_AddRefs(mLocalStorage));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_GetInterface(mLocalStorage);
|
||||
if (obs && docShell) {
|
||||
docShell->AddWeakPrivacyTransitionObserver(obs);
|
||||
}
|
||||
}
|
||||
|
||||
NS_ADDREF(*aLocalStorage = mLocalStorage);
|
||||
|
@ -9489,8 +9507,13 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
rv = event->GetStorageArea(getter_AddRefs(changingStorage));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool fireMozStorageChanged = false;
|
||||
principal = GetPrincipal();
|
||||
if (!principal) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(changingStorage);
|
||||
nsPIDOMStorage::nsDOMStorageType storageType = pistorage->StorageType();
|
||||
|
||||
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell());
|
||||
bool isPrivate = loadContext && loadContext->UsePrivateBrowsing();
|
||||
|
@ -9498,28 +9521,21 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
bool fireMozStorageChanged = false;
|
||||
principal = GetPrincipal();
|
||||
switch (storageType)
|
||||
switch (pistorage->GetType())
|
||||
{
|
||||
case nsPIDOMStorage::SessionStorage:
|
||||
{
|
||||
nsCOMPtr<nsIDOMStorage> storage = mSessionStorage;
|
||||
if (!storage) {
|
||||
nsIDocShell* docShell = GetDocShell();
|
||||
if (principal && docShell) {
|
||||
// No need to pass documentURI here, it's only needed when we want
|
||||
// to create a new storage, the third paramater would be true
|
||||
docShell->GetSessionStorageForPrincipal(principal,
|
||||
EmptyString(),
|
||||
false,
|
||||
getter_AddRefs(storage));
|
||||
}
|
||||
bool check = false;
|
||||
|
||||
nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(GetDocShell());
|
||||
if (storageManager) {
|
||||
rv = storageManager->CheckStorage(principal, changingStorage, &check);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (!pistorage->IsForkOf(storage)) {
|
||||
// This storage event is coming from a different doc shell,
|
||||
// i.e. it is a clone, ignore this event.
|
||||
if (!check) {
|
||||
// This storage event is not coming from our storage or is coming
|
||||
// from a different docshell, i.e. it is a clone, ignore this event.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -9532,13 +9548,14 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
fireMozStorageChanged = SameCOMIdentity(mSessionStorage, changingStorage);
|
||||
break;
|
||||
}
|
||||
|
||||
case nsPIDOMStorage::LocalStorage:
|
||||
{
|
||||
// Allow event fire only for the same principal storages
|
||||
// XXX We have to use EqualsIgnoreDomain after bug 495337 lands
|
||||
nsIPrincipal *storagePrincipal = pistorage->Principal();
|
||||
bool equals;
|
||||
nsIPrincipal* storagePrincipal = pistorage->GetPrincipal();
|
||||
|
||||
bool equals = false;
|
||||
rv = storagePrincipal->Equals(principal, &equals);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
|
|
@ -46,7 +46,6 @@
|
|||
#include "nsRect.h"
|
||||
#include "mozFlushType.h"
|
||||
#include "prclist.h"
|
||||
#include "nsIDOMStorageObsolete.h"
|
||||
#include "nsIDOMStorageEvent.h"
|
||||
#include "nsIDOMStorageIndexedDB.h"
|
||||
#include "nsIDOMOfflineResourceList.h"
|
||||
|
@ -1022,6 +1021,8 @@ protected:
|
|||
bool aDefaultStylesOnly,
|
||||
nsIDOMCSSStyleDeclaration** aReturn);
|
||||
|
||||
void PreloadLocalStorage();
|
||||
|
||||
// When adding new member variables, be careful not to create cycles
|
||||
// through JavaScript. If there is any chance that a member variable
|
||||
// could own objects that are implemented in JavaScript, then those
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
#include "CheckQuotaHelper.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDOMStorage.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/dom/quota/QuotaManager.h"
|
||||
|
|
|
@ -8,9 +8,7 @@ XPIDL_SOURCES += [
|
|||
'nsIDOMStorage.idl',
|
||||
'nsIDOMStorageEvent.idl',
|
||||
'nsIDOMStorageIndexedDB.idl',
|
||||
'nsIDOMStorageItem.idl',
|
||||
'nsIDOMStorageManager.idl',
|
||||
'nsIDOMStorageObsolete.idl',
|
||||
'nsIDOMToString.idl',
|
||||
]
|
||||
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "domstubs.idl"
|
||||
|
||||
/**
|
||||
* Interface for a client side storage item. See
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/#scs-client-side
|
||||
* for more information.
|
||||
*
|
||||
* A respresentation of a storage object item.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(0CC37C78-4C5F-48E1-ADFC-7480B8FE9DC4)]
|
||||
interface nsIDOMStorageItem : nsISupports
|
||||
{
|
||||
/**
|
||||
* Indicates whether a key is available only in a secure context.
|
||||
*/
|
||||
attribute boolean secure;
|
||||
|
||||
/**
|
||||
* The value associated with the item.
|
||||
*/
|
||||
attribute DOMString value;
|
||||
};
|
|
@ -8,23 +8,89 @@
|
|||
interface nsIDOMStorage;
|
||||
interface nsIPrincipal;
|
||||
|
||||
[scriptable, uuid(b16b207c-d883-43f5-a27e-548e7f2f5c20)]
|
||||
/**
|
||||
* General purpose interface that has two implementations, for localStorage
|
||||
* resp. sessionStorage with "@mozilla.org/dom/localStorage-manager;1" resp.
|
||||
* "@mozilla.org/dom/sessionStorage-manager;1" contract IDs.
|
||||
*/
|
||||
[scriptable, uuid(8096f9ea-fa61-4960-b5d7-fb30ac42c8d8)]
|
||||
interface nsIDOMStorageManager : nsISupports
|
||||
{
|
||||
/**
|
||||
* Return the amount of disk space used by a domain. Usage is checked
|
||||
* against the domain of the page that set the key (the owner domain), not
|
||||
* the domain of the storage object.
|
||||
*
|
||||
* @param aOwnerDomain The domain to check.
|
||||
* @returns the space usage of the domain, in bytes.
|
||||
* This starts async preloading of a storage cache for scope
|
||||
* defined by the principal.
|
||||
*/
|
||||
long getUsage(in AString aOwnerDomain);
|
||||
void precacheStorage(in nsIPrincipal aPrincipal);
|
||||
|
||||
/**
|
||||
* Returns instance of DOM storage object for given principal.
|
||||
* A new object is always returned and it is ensured there is
|
||||
* a storage for the scope created.
|
||||
*
|
||||
* @param aPrincipal
|
||||
* Principal to bound storage to.
|
||||
* @param aDocumentURI
|
||||
* URL of the demanding document, used for DOM storage event only.
|
||||
* @param aPrivate
|
||||
* Whether the demanding document is running in Private Browsing mode or not.
|
||||
*/
|
||||
nsIDOMStorage createStorage(in nsIPrincipal aPrincipal,
|
||||
in DOMString aDocumentURI,
|
||||
[optional] in bool aPrivate);
|
||||
/**
|
||||
* Returns instance of DOM storage object for given principal.
|
||||
* If there is no storage managed for the scope, then null is returned and
|
||||
* no object is created. Otherwise, an object (new) for the existing storage
|
||||
* scope is returned.
|
||||
*
|
||||
* @param aPrincipal
|
||||
* Principal to bound storage to.
|
||||
* @param aPrivate
|
||||
* Whether the demanding document is running in Private Browsing mode or not.
|
||||
*/
|
||||
nsIDOMStorage getStorage(in nsIPrincipal aPrincipal,
|
||||
[optional] in bool aPrivate);
|
||||
|
||||
/**
|
||||
* Clones given storage into this storage manager.
|
||||
*
|
||||
* @param aStorageToCloneFrom
|
||||
* The storage to copy all items from into this manager. Manager will then
|
||||
* return a new and independent object that contains snapshot of data from
|
||||
* the moment this method was called. Modification to this new object will
|
||||
* not affect the original storage content we cloned from and vice versa.
|
||||
*/
|
||||
void cloneStorage(in nsIDOMStorage aStorageToCloneFrom);
|
||||
|
||||
/**
|
||||
* Returns true if the storage belongs to the given principal and is managed
|
||||
* (i.e. has been created and is cached) by this storage manager.
|
||||
*
|
||||
* @param aPrincipal
|
||||
* Principal to check the storage against.
|
||||
* @param aStorage
|
||||
* The storage object to examine.
|
||||
*
|
||||
* @result
|
||||
* true when the storage object is bound with the principal and is managed
|
||||
* by this storage manager.
|
||||
* false otherwise
|
||||
*/
|
||||
bool checkStorage(in nsIPrincipal aPrincipal,
|
||||
in nsIDOMStorage aStorage);
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* Returns instance of localStorage object for aURI's origin.
|
||||
* This method ensures there is always only a single instance
|
||||
* for a single origin.
|
||||
*
|
||||
* Currently just forwards to the createStorage method of this
|
||||
* interface.
|
||||
*
|
||||
* Extension developers are strongly encouraged to use getStorage
|
||||
* or createStorage method instead.
|
||||
*/
|
||||
nsIDOMStorage getLocalStorageForPrincipal(in nsIPrincipal aPrincipal,
|
||||
in DOMString aDocumentURI,
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "domstubs.idl"
|
||||
|
||||
/**
|
||||
* Interface for client side storage. See
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/#scs-client-side
|
||||
* for more information.
|
||||
*
|
||||
* A storage object stores an arbitrary set of key-value pairs, which
|
||||
* may be retrieved, modified and removed as needed. A key may only
|
||||
* exist once within a storage object, and only one value may be
|
||||
* associated with a particular key. Keys are stored in a particular
|
||||
* order with the condition that this order not change by merely changing
|
||||
* the value associated with a key, but the order may change when a
|
||||
* key is added or removed.
|
||||
*/
|
||||
|
||||
interface nsIDOMStorageItem;
|
||||
|
||||
[scriptable, uuid(18013CF9-B104-49cf-9484-C2A7A845457E)]
|
||||
interface nsIDOMStorageObsolete : nsISupports
|
||||
{
|
||||
/**
|
||||
* The number of keys stored.
|
||||
*/
|
||||
readonly attribute unsigned long length;
|
||||
|
||||
/**
|
||||
* Retrieve the name of the key at a particular index.
|
||||
*
|
||||
* @param index index of the item to retrieve
|
||||
* @returns the key at index
|
||||
* @throws INDEX_SIZE_ERR if there is no key at that index
|
||||
*/
|
||||
DOMString key(in unsigned long index);
|
||||
|
||||
/**
|
||||
* Retrieve an item with a given key
|
||||
*
|
||||
* @param key key to retrieve
|
||||
* @returns found item or null if the key was not found
|
||||
*/
|
||||
nsIDOMStorageItem getItem(in DOMString key);
|
||||
|
||||
/**
|
||||
* Assign a value with a key. If the key does not exist already, a new
|
||||
* key is added associated with that value. If the key already exists,
|
||||
* then the existing value is replaced with a new value.
|
||||
*
|
||||
* @param key key to set
|
||||
* @param data data to associate with the key
|
||||
*/
|
||||
void setItem(in DOMString key, in DOMString data);
|
||||
|
||||
/**
|
||||
* Remove a key and its corresponding value.
|
||||
*
|
||||
* @param key key to remove
|
||||
*/
|
||||
void removeItem(in DOMString key);
|
||||
};
|
|
@ -8,43 +8,44 @@
|
|||
#define __nsPIDOMStorage_h_
|
||||
|
||||
#include "nsISupports.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsIDOMStorageObsolete;
|
||||
class nsIURI;
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class DOMStorageCache;
|
||||
class DOMStorageManager;
|
||||
|
||||
} // ::dom
|
||||
} // ::mozilla
|
||||
|
||||
// {09198A51-5D27-4992-97E4-38A9CEA2A65D}
|
||||
#define NS_PIDOMSTORAGE_IID \
|
||||
{ 0x9c292365, 0x6ae4, 0x461b, \
|
||||
{ 0x86, 0x98, 0xf5, 0x23, 0x6f, 0xfa, 0xd2, 0x30 } }
|
||||
{ 0x9198a51, 0x5d27, 0x4992, \
|
||||
{ 0x97, 0xe4, 0x38, 0xa9, 0xce, 0xa2, 0xa6, 0x5d } }
|
||||
|
||||
class nsPIDOMStorage : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMSTORAGE_IID)
|
||||
|
||||
typedef enum {
|
||||
Unknown = 0,
|
||||
enum StorageType {
|
||||
LocalStorage = 1,
|
||||
SessionStorage = 2
|
||||
} nsDOMStorageType;
|
||||
};
|
||||
|
||||
virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
|
||||
bool aPrivate) = 0;
|
||||
virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
|
||||
bool aPrivate) = 0;
|
||||
virtual StorageType GetType() const = 0;
|
||||
virtual mozilla::dom::DOMStorageManager* GetManager() const = 0;
|
||||
virtual const mozilla::dom::DOMStorageCache* GetCache() const = 0;
|
||||
|
||||
virtual already_AddRefed<nsIDOMStorage> Clone() = 0;
|
||||
virtual already_AddRefed<nsIDOMStorage> Fork(const nsSubstring &aDocumentURI) = 0;
|
||||
virtual bool IsForkOf(nsIDOMStorage* aThat) = 0;
|
||||
virtual nsTArray<nsString>* GetKeys() = 0;
|
||||
|
||||
virtual nsTArray<nsString> *GetKeys() = 0;
|
||||
|
||||
virtual nsIPrincipal* Principal() = 0;
|
||||
virtual nsIPrincipal* GetPrincipal() = 0;
|
||||
virtual bool PrincipalEquals(nsIPrincipal* principal) = 0;
|
||||
virtual bool CanAccess(nsIPrincipal *aPrincipal) = 0;
|
||||
|
||||
virtual nsDOMStorageType StorageType() = 0;
|
||||
|
||||
virtual bool IsPrivate() = 0;
|
||||
};
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/dom/ExternalHelperAppChild.h"
|
||||
#include "mozilla/dom/PCrashReporterChild.h"
|
||||
#include "mozilla/dom/StorageChild.h"
|
||||
#include "mozilla/dom/DOMStorageIPC.h"
|
||||
#include "mozilla/Hal.h"
|
||||
#include "mozilla/hal_sandbox/PHalChild.h"
|
||||
#include "mozilla/ipc/GeckoChildProcessHost.h"
|
||||
|
@ -839,7 +839,7 @@ ContentChild::DeallocPSms(PSmsChild* aSms)
|
|||
}
|
||||
|
||||
PStorageChild*
|
||||
ContentChild::AllocPStorage(const StorageConstructData& aData)
|
||||
ContentChild::AllocPStorage()
|
||||
{
|
||||
NS_NOTREACHED("We should never be manually allocating PStorageChild actors");
|
||||
return nullptr;
|
||||
|
@ -848,7 +848,7 @@ ContentChild::AllocPStorage(const StorageConstructData& aData)
|
|||
bool
|
||||
ContentChild::DeallocPStorage(PStorageChild* aActor)
|
||||
{
|
||||
StorageChild* child = static_cast<StorageChild*>(aActor);
|
||||
DOMStorageDBChild* child = static_cast<DOMStorageDBChild*>(aActor);
|
||||
child->ReleaseIPDLReference();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -142,7 +142,7 @@ public:
|
|||
virtual PSmsChild* AllocPSms();
|
||||
virtual bool DeallocPSms(PSmsChild*);
|
||||
|
||||
virtual PStorageChild* AllocPStorage(const StorageConstructData& aData);
|
||||
virtual PStorageChild* AllocPStorage();
|
||||
virtual bool DeallocPStorage(PStorageChild* aActor);
|
||||
|
||||
virtual PBluetoothChild* AllocPBluetooth();
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
#include "mozilla/dom/ExternalHelperAppParent.h"
|
||||
#include "mozilla/dom/PMemoryReportRequestParent.h"
|
||||
#include "mozilla/dom/power/PowerManagerService.h"
|
||||
#include "mozilla/dom/StorageParent.h"
|
||||
#include "mozilla/dom/DOMStorageIPC.h"
|
||||
#include "mozilla/dom/bluetooth/PBluetoothParent.h"
|
||||
#include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
|
||||
#include "SmsParent.h"
|
||||
|
@ -1970,15 +1970,16 @@ ContentParent::DeallocPSms(PSmsParent* aSms)
|
|||
}
|
||||
|
||||
PStorageParent*
|
||||
ContentParent::AllocPStorage(const StorageConstructData& aData)
|
||||
ContentParent::AllocPStorage()
|
||||
{
|
||||
return new StorageParent(aData);
|
||||
return new DOMStorageDBParent();
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::DeallocPStorage(PStorageParent* aActor)
|
||||
{
|
||||
delete aActor;
|
||||
DOMStorageDBParent* child = static_cast<DOMStorageDBParent*>(aActor);
|
||||
child->ReleaseIPDLReference();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -285,7 +285,7 @@ private:
|
|||
virtual PSmsParent* AllocPSms();
|
||||
virtual bool DeallocPSms(PSmsParent*);
|
||||
|
||||
virtual PStorageParent* AllocPStorage(const StorageConstructData& aData);
|
||||
virtual PStorageParent* AllocPStorage();
|
||||
virtual bool DeallocPStorage(PStorageParent* aActor);
|
||||
|
||||
virtual PBluetoothParent* AllocPBluetooth();
|
||||
|
|
|
@ -50,23 +50,6 @@ using gfxIntSize;
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// Data required to clone an existing DOMStorageImpl in the parent
|
||||
struct StorageClone
|
||||
{
|
||||
// Existing cross-process storage actor to clone
|
||||
PStorage actor;
|
||||
// Result of calling IsCallerSecure() in the child
|
||||
bool callerSecure;
|
||||
};
|
||||
|
||||
// When creating a new PStorage protocol, an existing one can be
|
||||
// cloned (see nsDOMStorage2::Clone)
|
||||
union StorageConstructData
|
||||
{
|
||||
null_t;
|
||||
StorageClone;
|
||||
};
|
||||
|
||||
struct FontListEntry {
|
||||
nsString familyName;
|
||||
nsString faceName;
|
||||
|
@ -401,7 +384,7 @@ parent:
|
|||
|
||||
PSpeechSynthesis();
|
||||
|
||||
PStorage(StorageConstructData data);
|
||||
PStorage();
|
||||
|
||||
PBluetooth();
|
||||
|
||||
|
|
|
@ -0,0 +1,353 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DOMStorage.h"
|
||||
#include "DOMStorageCache.h"
|
||||
#include "DOMStorageManager.h"
|
||||
|
||||
#include "nsIDOMStorageEvent.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsICookiePermission.h"
|
||||
|
||||
#include "nsDOMClassInfoID.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "GeneratedEvents.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
DOMCI_DATA(Storage, mozilla::dom::DOMStorage)
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ADDREF(DOMStorage)
|
||||
NS_IMPL_RELEASE(DOMStorage)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(DOMStorage)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorage)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMStorage)
|
||||
NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
DOMStorage::DOMStorage(DOMStorageManager* aManager,
|
||||
DOMStorageCache* aCache,
|
||||
const nsAString& aDocumentURI,
|
||||
nsIPrincipal* aPrincipal,
|
||||
bool aIsPrivate)
|
||||
: mManager(aManager)
|
||||
, mCache(aCache)
|
||||
, mDocumentURI(aDocumentURI)
|
||||
, mPrincipal(aPrincipal)
|
||||
, mIsPrivate(aIsPrivate)
|
||||
, mIsSessionOnly(false)
|
||||
{
|
||||
mCache->Preload();
|
||||
}
|
||||
|
||||
DOMStorage::~DOMStorage()
|
||||
{
|
||||
mCache->KeepAlive();
|
||||
}
|
||||
|
||||
// nsIDOMStorage (web content public API implementation)
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMStorage::GetLength(uint32_t* aLength)
|
||||
{
|
||||
if (!CanUseStorage(this)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
return mCache->GetLength(this, aLength);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMStorage::Key(uint32_t aIndex, nsAString& aRetval)
|
||||
{
|
||||
if (!CanUseStorage(this)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
return mCache->GetKey(this, aIndex, aRetval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMStorage::GetItem(const nsAString& aKey, nsAString& aRetval)
|
||||
{
|
||||
if (!CanUseStorage(this)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
return mCache->GetItem(this, aKey, aRetval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMStorage::SetItem(const nsAString& aKey, const nsAString& aData)
|
||||
{
|
||||
if (!CanUseStorage(this)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
Telemetry::Accumulate(GetType() == LocalStorage
|
||||
? Telemetry::LOCALDOMSTORAGE_KEY_SIZE_BYTES
|
||||
: Telemetry::SESSIONDOMSTORAGE_KEY_SIZE_BYTES, aKey.Length());
|
||||
Telemetry::Accumulate(GetType() == LocalStorage
|
||||
? Telemetry::LOCALDOMSTORAGE_VALUE_SIZE_BYTES
|
||||
: Telemetry::SESSIONDOMSTORAGE_VALUE_SIZE_BYTES, aData.Length());
|
||||
|
||||
nsString old;
|
||||
nsresult rv = mCache->SetItem(this, aKey, nsString(aData), old);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
|
||||
BroadcastChangeNotification(aKey, old, aData);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMStorage::RemoveItem(const nsAString& aKey)
|
||||
{
|
||||
if (!CanUseStorage(this)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsAutoString old;
|
||||
nsresult rv = mCache->RemoveItem(this, aKey, old);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
|
||||
BroadcastChangeNotification(aKey, old, NullString());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMStorage::Clear()
|
||||
{
|
||||
if (!CanUseStorage(this)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsresult rv = mCache->Clear(this);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
|
||||
BroadcastChangeNotification(NullString(), NullString(), NullString());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class StorageNotifierRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
StorageNotifierRunnable(nsISupports* aSubject)
|
||||
: mSubject(aSubject)
|
||||
{ }
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsISupports> mSubject;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
StorageNotifierRunnable::Run()
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
if (observerService) {
|
||||
observerService->NotifyObservers(mSubject, "dom-storage2-changed", nullptr);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
DOMStorage::BroadcastChangeNotification(const nsSubstring& aKey,
|
||||
const nsSubstring& aOldValue,
|
||||
const nsSubstring& aNewValue)
|
||||
{
|
||||
nsCOMPtr<nsIDOMEvent> domEvent;
|
||||
// Note, this DOM event should never reach JS. It is cloned later in
|
||||
// nsGlobalWindow.
|
||||
NS_NewDOMStorageEvent(getter_AddRefs(domEvent), nullptr, nullptr, nullptr);
|
||||
|
||||
nsCOMPtr<nsIDOMStorageEvent> event = do_QueryInterface(domEvent);
|
||||
nsresult rv = event->InitStorageEvent(NS_LITERAL_STRING("storage"),
|
||||
false,
|
||||
false,
|
||||
aKey,
|
||||
aOldValue,
|
||||
aNewValue,
|
||||
mDocumentURI,
|
||||
static_cast<nsIDOMStorage*>(this));
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<StorageNotifierRunnable> r = new StorageNotifierRunnable(event);
|
||||
NS_DispatchToMainThread(r);
|
||||
}
|
||||
|
||||
static const uint32_t ASK_BEFORE_ACCEPT = 1;
|
||||
static const uint32_t ACCEPT_SESSION = 2;
|
||||
static const uint32_t BEHAVIOR_REJECT = 2;
|
||||
|
||||
static const char kPermissionType[] = "cookie";
|
||||
static const char kStorageEnabled[] = "dom.storage.enabled";
|
||||
static const char kCookiesBehavior[] = "network.cookie.cookieBehavior";
|
||||
static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy";
|
||||
|
||||
// static, public
|
||||
bool
|
||||
DOMStorage::CanUseStorage(DOMStorage* aStorage)
|
||||
{
|
||||
// This method is responsible for correct setting of mIsSessionOnly.
|
||||
// It doesn't work with mIsPrivate flag at all, since it is checked
|
||||
// regardless mIsSessionOnly flag in DOMStorageCache code.
|
||||
if (aStorage) {
|
||||
aStorage->mIsSessionOnly = false;
|
||||
}
|
||||
|
||||
if (!mozilla::Preferences::GetBool(kStorageEnabled)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// chrome can always use aStorage regardless of permission preferences
|
||||
if (nsContentUtils::IsCallerChrome()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> subjectPrincipal;
|
||||
nsresult rv = nsContentUtils::GetSecurityManager()->
|
||||
GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
// if subjectPrincipal were null we'd have returned after
|
||||
// IsCallerChrome().
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permissionManager =
|
||||
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
|
||||
if (!permissionManager) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t perm;
|
||||
permissionManager->TestPermissionFromPrincipal(subjectPrincipal,
|
||||
kPermissionType, &perm);
|
||||
|
||||
if (perm == nsIPermissionManager::DENY_ACTION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (perm == nsICookiePermission::ACCESS_SESSION) {
|
||||
if (aStorage) {
|
||||
aStorage->mIsSessionOnly = true;
|
||||
}
|
||||
} else if (perm != nsIPermissionManager::ALLOW_ACTION) {
|
||||
uint32_t cookieBehavior = Preferences::GetUint(kCookiesBehavior);
|
||||
uint32_t lifetimePolicy = Preferences::GetUint(kCookiesLifetimePolicy);
|
||||
|
||||
// Treat "ask every time" as "reject always".
|
||||
if ((cookieBehavior == BEHAVIOR_REJECT || lifetimePolicy == ASK_BEFORE_ACCEPT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lifetimePolicy == ACCEPT_SESSION && aStorage) {
|
||||
aStorage->mIsSessionOnly = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (aStorage) {
|
||||
return aStorage->CanAccess(subjectPrincipal);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// nsPIDOMStorage
|
||||
|
||||
nsPIDOMStorage::StorageType
|
||||
DOMStorage::GetType() const
|
||||
{
|
||||
return mManager->Type();
|
||||
}
|
||||
|
||||
nsIPrincipal*
|
||||
DOMStorage::GetPrincipal()
|
||||
{
|
||||
return mPrincipal;
|
||||
}
|
||||
|
||||
// Defined in DOMStorageManager.cpp
|
||||
extern bool
|
||||
PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal);
|
||||
|
||||
bool
|
||||
DOMStorage::PrincipalEquals(nsIPrincipal* aPrincipal)
|
||||
{
|
||||
return PrincipalsEqual(mPrincipal, aPrincipal);
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorage::CanAccess(nsIPrincipal* aPrincipal)
|
||||
{
|
||||
// Allow C++ callers to access the storage
|
||||
if (!aPrincipal) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// For content, either the code base or domain must be the same. When code
|
||||
// base is the same, this is enough to say it is safe for a page to access
|
||||
// this storage.
|
||||
|
||||
bool subsumes;
|
||||
nsresult rv = aPrincipal->SubsumesIgnoringDomain(mPrincipal, &subsumes);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!subsumes) {
|
||||
nsresult rv = aPrincipal->Subsumes(mPrincipal, &subsumes);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return subsumes;
|
||||
}
|
||||
|
||||
nsTArray<nsString>*
|
||||
DOMStorage::GetKeys()
|
||||
{
|
||||
if (!CanUseStorage(this)) {
|
||||
return new nsTArray<nsString>(); // return just an empty array
|
||||
}
|
||||
|
||||
return mCache->GetKeys(this);
|
||||
}
|
||||
|
||||
} // ::dom
|
||||
} // ::mozilla
|
|
@ -0,0 +1,85 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsDOMStorage_h___
|
||||
#define nsDOMStorage_h___
|
||||
|
||||
#include "nsIDOMStorage.h"
|
||||
#include "nsPIDOMStorage.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class DOMStorageManager;
|
||||
class DOMStorageCache;
|
||||
|
||||
class DOMStorage MOZ_FINAL : public nsIDOMStorage
|
||||
, public nsPIDOMStorage
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMSTORAGE
|
||||
|
||||
// nsPIDOMStorage
|
||||
virtual StorageType GetType() const;
|
||||
virtual DOMStorageManager* GetManager() const { return mManager; }
|
||||
virtual const DOMStorageCache* GetCache() const { return mCache; }
|
||||
|
||||
virtual nsTArray<nsString>* GetKeys();
|
||||
virtual nsIPrincipal* GetPrincipal();
|
||||
virtual bool PrincipalEquals(nsIPrincipal* aPrincipal);
|
||||
virtual bool CanAccess(nsIPrincipal* aPrincipal);
|
||||
virtual bool IsPrivate() { return mIsPrivate; }
|
||||
|
||||
DOMStorage(DOMStorageManager* aManager,
|
||||
DOMStorageCache* aCache,
|
||||
const nsAString& aDocumentURI,
|
||||
nsIPrincipal* aPrincipal,
|
||||
bool aIsPrivate);
|
||||
~DOMStorage();
|
||||
|
||||
// The method checks whether the caller can use a storage.
|
||||
// CanUseStorage is called before any DOM initiated operation
|
||||
// on a storage is about to happen and ensures that the storage's
|
||||
// session-only flag is properly set according the current settings.
|
||||
// It is an optimization since the privileges check and session only
|
||||
// state determination are complex and share the code (comes hand in
|
||||
// hand together).
|
||||
static bool CanUseStorage(DOMStorage* aStorage = nullptr);
|
||||
|
||||
bool IsPrivate() const { return mIsPrivate; }
|
||||
bool IsSessionOnly() const { return mIsSessionOnly; }
|
||||
|
||||
private:
|
||||
friend class DOMStorageManager;
|
||||
friend class DOMStorageCache;
|
||||
|
||||
nsRefPtr<DOMStorageManager> mManager;
|
||||
nsRefPtr<DOMStorageCache> mCache;
|
||||
nsString mDocumentURI;
|
||||
|
||||
// Principal this DOMStorage (i.e. localStorage or sessionStorage) has
|
||||
// been created for
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
||||
// Whether this storage is running in private-browsing window.
|
||||
bool mIsPrivate : 1;
|
||||
|
||||
// Whether storage is set to persist data only per session, may change
|
||||
// dynamically and is set by CanUseStorage function that is called
|
||||
// before any operation on the storage.
|
||||
bool mIsSessionOnly : 1;
|
||||
|
||||
void BroadcastChangeNotification(const nsSubstring& aKey,
|
||||
const nsSubstring& aOldValue,
|
||||
const nsSubstring& aNewValue);
|
||||
};
|
||||
|
||||
} // ::dom
|
||||
} // ::mozilla
|
||||
|
||||
#endif /* nsDOMStorage_h___ */
|
|
@ -0,0 +1,801 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DOMStorageCache.h"
|
||||
|
||||
#include "DOMStorage.h"
|
||||
#include "DOMStorageDBThread.h"
|
||||
#include "DOMStorageIPC.h"
|
||||
#include "DOMStorageManager.h"
|
||||
|
||||
#include "nsDOMString.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "nsProxyRelease.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
#define DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS 20000
|
||||
|
||||
// static
|
||||
DOMStorageDBBridge* DOMStorageCache::sDatabase = nullptr;
|
||||
|
||||
namespace { // anon
|
||||
|
||||
const uint32_t kDefaultSet = 0;
|
||||
const uint32_t kPrivateSet = 1;
|
||||
const uint32_t kSessionSet = 2;
|
||||
|
||||
inline uint32_t
|
||||
GetDataSetIndex(bool aPrivate, bool aSessionOnly)
|
||||
{
|
||||
if (aPrivate) {
|
||||
return kPrivateSet;
|
||||
}
|
||||
|
||||
if (aSessionOnly) {
|
||||
return kSessionSet;
|
||||
}
|
||||
|
||||
return kDefaultSet;
|
||||
}
|
||||
|
||||
inline uint32_t
|
||||
GetDataSetIndex(const DOMStorage* aStorage)
|
||||
{
|
||||
return GetDataSetIndex(aStorage->IsPrivate(), aStorage->IsSessionOnly());
|
||||
}
|
||||
|
||||
} // anon
|
||||
|
||||
// DOMStorageCacheBridge
|
||||
|
||||
NS_IMPL_THREADSAFE_ADDREF(DOMStorageCacheBridge)
|
||||
|
||||
// Since there is no consumer of return value of Release, we can turn this
|
||||
// method to void to make implementation of asynchronous DOMStorageCache::Release
|
||||
// much simpler.
|
||||
NS_IMETHODIMP_(void) DOMStorageCacheBridge::Release(void)
|
||||
{
|
||||
MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
|
||||
nsrefcnt count = NS_AtomicDecrementRefcnt(mRefCnt);
|
||||
NS_LOG_RELEASE(this, count, "DOMStorageCacheBridge");
|
||||
if (0 == count) {
|
||||
mRefCnt = 1; /* stabilize */
|
||||
/* enable this to find non-threadsafe destructors: */
|
||||
/* NS_ASSERT_OWNINGTHREAD(_class); */
|
||||
delete (this);
|
||||
}
|
||||
}
|
||||
|
||||
// DOMStorageCache
|
||||
|
||||
DOMStorageCache::DOMStorageCache(const nsACString* aScope)
|
||||
: mManager(nullptr)
|
||||
, mScope(*aScope)
|
||||
, mMonitor("DOMStorageCache")
|
||||
, mLoaded(false)
|
||||
, mLoadResult(NS_OK)
|
||||
, mInitialized(false)
|
||||
, mSessionOnlyDataSetActive(false)
|
||||
, mPreloadTelemetryRecorded(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DOMStorageCache);
|
||||
}
|
||||
|
||||
DOMStorageCache::~DOMStorageCache()
|
||||
{
|
||||
if (mManager) {
|
||||
mManager->DropCache(this);
|
||||
}
|
||||
|
||||
MOZ_COUNT_DTOR(DOMStorageCache);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(void)
|
||||
DOMStorageCache::Release(void)
|
||||
{
|
||||
// We must actually release on the main thread since the cache removes it
|
||||
// self from the manager's hash table. And we don't want to lock access to
|
||||
// that hash table.
|
||||
if (NS_IsMainThread()) {
|
||||
DOMStorageCacheBridge::Release();
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<nsRunnableMethod<DOMStorageCacheBridge, void, false> > event =
|
||||
NS_NewNonOwningRunnableMethod(static_cast<DOMStorageCacheBridge*>(this),
|
||||
&DOMStorageCacheBridge::Release);
|
||||
|
||||
nsresult rv = NS_DispatchToMainThread(event);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("DOMStorageCache::Release() on a non-main thread");
|
||||
DOMStorageCacheBridge::Release();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DOMStorageCache::Init(DOMStorageManager* aManager,
|
||||
bool aPersistent,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsACString& aQuotaScope)
|
||||
{
|
||||
if (mInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
mManager = aManager;
|
||||
mInitialized = true;
|
||||
mPrincipal = aPrincipal;
|
||||
mPersistent = aPersistent;
|
||||
mQuotaScope = aQuotaScope.IsEmpty() ? mScope : aQuotaScope;
|
||||
|
||||
if (mPersistent) {
|
||||
Preload();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool
|
||||
DOMStorageCache::Persist(const DOMStorage* aStorage) const
|
||||
{
|
||||
return mPersistent &&
|
||||
!aStorage->IsSessionOnly() &&
|
||||
!aStorage->IsPrivate();
|
||||
}
|
||||
|
||||
namespace { // anon
|
||||
|
||||
PLDHashOperator
|
||||
CloneSetData(const nsAString& aKey, const nsString aValue, void* aArg)
|
||||
{
|
||||
DOMStorageCache::Data* target = static_cast<DOMStorageCache::Data*>(aArg);
|
||||
target->mKeys.Put(aKey, aValue);
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
} // anon
|
||||
|
||||
DOMStorageCache::Data&
|
||||
DOMStorageCache::DataSet(const DOMStorage* aStorage)
|
||||
{
|
||||
uint32_t index = GetDataSetIndex(aStorage);
|
||||
|
||||
if (index == kSessionSet && !mSessionOnlyDataSetActive) {
|
||||
// Session only data set is demanded but not filled with
|
||||
// current data set, copy to session only set now.
|
||||
|
||||
WaitForPreload(Telemetry::LOCALDOMSTORAGE_SESSIONONLY_PRELOAD_BLOCKING_MS);
|
||||
|
||||
Data& defaultSet = mData[kDefaultSet];
|
||||
Data& sessionSet = mData[kSessionSet];
|
||||
|
||||
defaultSet.mKeys.EnumerateRead(CloneSetData, &sessionSet);
|
||||
|
||||
mSessionOnlyDataSetActive = true;
|
||||
|
||||
// This updates sessionSet.mOriginQuotaUsage and also updates global usage
|
||||
// for all session only data
|
||||
ProcessUsageDelta(kSessionSet, defaultSet.mOriginQuotaUsage);
|
||||
}
|
||||
|
||||
return mData[index];
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageCache::ProcessUsageDelta(const DOMStorage* aStorage, int64_t aDelta)
|
||||
{
|
||||
return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta);
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageCache::ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta)
|
||||
{
|
||||
// Check limit per this origin
|
||||
Data& data = mData[aGetDataSetIndex];
|
||||
uint64_t newOriginUsage = data.mOriginQuotaUsage + aDelta;
|
||||
if (aDelta > 0 && newOriginUsage > DOMStorageManager::GetQuota()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now check eTLD+1 limit
|
||||
GetDatabase();
|
||||
if (sDatabase) {
|
||||
DOMStorageUsage* usage = sDatabase->GetScopeUsage(mQuotaScope);
|
||||
if (!usage->CheckAndSetETLD1UsageDelta(aGetDataSetIndex, aDelta)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update size in our data set
|
||||
data.mOriginQuotaUsage = newOriginUsage;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DOMStorageCache::Preload()
|
||||
{
|
||||
if (mLoaded || !mPersistent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!StartDatabase()) {
|
||||
mLoaded = true;
|
||||
mLoadResult = NS_ERROR_FAILURE;
|
||||
return;
|
||||
}
|
||||
|
||||
sDatabase->AsyncPreload(this);
|
||||
sDatabase->GetScopeUsage(mQuotaScope);
|
||||
}
|
||||
|
||||
namespace { // anon
|
||||
|
||||
// This class is passed to timer as a tick observer. It refers the cache
|
||||
// and keeps it alive for a time.
|
||||
class DOMStorageCacheHolder : public nsITimerCallback
|
||||
{
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHODIMP
|
||||
Notify(nsITimer* aTimer)
|
||||
{
|
||||
mCache = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
virtual ~DOMStorageCacheHolder() {}
|
||||
|
||||
nsRefPtr<DOMStorageCache> mCache;
|
||||
|
||||
public:
|
||||
DOMStorageCacheHolder(DOMStorageCache* aCache) : mCache(aCache) {}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(DOMStorageCacheHolder, nsITimerCallback)
|
||||
|
||||
} // anon
|
||||
|
||||
void
|
||||
DOMStorageCache::KeepAlive()
|
||||
{
|
||||
// Missing reference back to the manager means the cache is not responsible
|
||||
// for its lifetime. Used for keeping sessionStorage live forever.
|
||||
if (!mManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
// Timer and the holder must be initialized on the main thread.
|
||||
nsRefPtr<nsRunnableMethod<DOMStorageCache> > event =
|
||||
NS_NewRunnableMethod(this, &DOMStorageCache::KeepAlive);
|
||||
|
||||
NS_DispatchToMainThread(event);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
if (!timer) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<DOMStorageCacheHolder> holder = new DOMStorageCacheHolder(this);
|
||||
timer->InitWithCallback(holder, DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
|
||||
mKeepAliveTimer.swap(timer);
|
||||
}
|
||||
|
||||
namespace { // anon
|
||||
|
||||
// The AutoTimer provided by telemetry headers is only using static,
|
||||
// i.e. compile time known ID, but here we know the ID only at run time.
|
||||
// Hence a new class.
|
||||
class TelemetryAutoTimer
|
||||
{
|
||||
public:
|
||||
TelemetryAutoTimer(Telemetry::ID aId)
|
||||
: id(aId), start(TimeStamp::Now()) {}
|
||||
~TelemetryAutoTimer()
|
||||
{ Telemetry::AccumulateDelta_impl<Telemetry::Millisecond>::compute(id, start); }
|
||||
private:
|
||||
Telemetry::ID id;
|
||||
const TimeStamp start;
|
||||
};
|
||||
|
||||
} // anon
|
||||
|
||||
void
|
||||
DOMStorageCache::WaitForPreload(Telemetry::ID aTelemetryID)
|
||||
{
|
||||
if (!mPersistent) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool loaded = mLoaded;
|
||||
|
||||
// Telemetry of rates of pending preloads
|
||||
if (!mPreloadTelemetryRecorded) {
|
||||
mPreloadTelemetryRecorded = true;
|
||||
Telemetry::Accumulate(
|
||||
Telemetry::LOCALDOMSTORAGE_PRELOAD_PENDING_ON_FIRST_ACCESS,
|
||||
!loaded);
|
||||
}
|
||||
|
||||
if (loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Measure which operation blocks and for how long
|
||||
TelemetryAutoTimer timer(aTelemetryID);
|
||||
|
||||
// If preload already started (i.e. we got some first data, but not all)
|
||||
// SyncPreload will just wait for it to finish rather then synchronously
|
||||
// read from the database. It seems to me more optimal.
|
||||
|
||||
// TODO place for A/B testing (force main thread load vs. let preload finish)
|
||||
sDatabase->SyncPreload(this);
|
||||
}
|
||||
|
||||
nsresult
|
||||
DOMStorageCache::GetLength(const DOMStorage* aStorage, uint32_t* aRetval)
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETLENGTH_MS> autoTimer;
|
||||
|
||||
if (Persist(aStorage)) {
|
||||
WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETLENGTH_BLOCKING_MS);
|
||||
if (NS_FAILED(mLoadResult)) {
|
||||
return mLoadResult;
|
||||
}
|
||||
}
|
||||
|
||||
*aRetval = DataSet(aStorage).mKeys.Count();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
namespace { // anon
|
||||
|
||||
class IndexFinderData
|
||||
{
|
||||
public:
|
||||
IndexFinderData(uint32_t aIndex, nsAString& aRetval)
|
||||
: mIndex(aIndex), mKey(aRetval)
|
||||
{
|
||||
mKey.SetIsVoid(true);
|
||||
}
|
||||
|
||||
uint32_t mIndex;
|
||||
nsAString& mKey;
|
||||
};
|
||||
|
||||
PLDHashOperator
|
||||
FindKeyOrder(const nsAString& aKey, const nsString aValue, void* aArg)
|
||||
{
|
||||
IndexFinderData* data = static_cast<IndexFinderData*>(aArg);
|
||||
|
||||
if (data->mIndex--) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
data->mKey = aKey;
|
||||
return PL_DHASH_STOP;
|
||||
}
|
||||
|
||||
} // anon
|
||||
|
||||
nsresult
|
||||
DOMStorageCache::GetKey(const DOMStorage* aStorage, uint32_t aIndex, nsAString& aRetval)
|
||||
{
|
||||
// XXX: This does a linear search for the key at index, which would
|
||||
// suck if there's a large numer of indexes. Do we care? If so,
|
||||
// maybe we need to have a lazily populated key array here or
|
||||
// something?
|
||||
Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETKEY_MS> autoTimer;
|
||||
|
||||
if (Persist(aStorage)) {
|
||||
WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETKEY_BLOCKING_MS);
|
||||
if (NS_FAILED(mLoadResult)) {
|
||||
return mLoadResult;
|
||||
}
|
||||
}
|
||||
|
||||
IndexFinderData data(aIndex, aRetval);
|
||||
DataSet(aStorage).mKeys.EnumerateRead(FindKeyOrder, &data);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
namespace { // anon
|
||||
|
||||
static PLDHashOperator
|
||||
KeysArrayBuilder(const nsAString& aKey, const nsString aValue, void* aArg)
|
||||
{
|
||||
nsTArray<nsString>* keys = static_cast<nsTArray<nsString>* >(aArg);
|
||||
|
||||
keys->AppendElement(aKey);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
} // anon
|
||||
|
||||
nsTArray<nsString>*
|
||||
DOMStorageCache::GetKeys(const DOMStorage* aStorage)
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETALLKEYS_MS> autoTimer;
|
||||
|
||||
if (Persist(aStorage)) {
|
||||
WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETALLKEYS_BLOCKING_MS);
|
||||
}
|
||||
|
||||
nsTArray<nsString>* result = new nsTArray<nsString>();
|
||||
if (NS_SUCCEEDED(mLoadResult)) {
|
||||
DataSet(aStorage).mKeys.EnumerateRead(KeysArrayBuilder, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DOMStorageCache::GetItem(const DOMStorage* aStorage, const nsAString& aKey,
|
||||
nsAString& aRetval)
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETVALUE_MS> autoTimer;
|
||||
|
||||
if (Persist(aStorage)) {
|
||||
WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETVALUE_BLOCKING_MS);
|
||||
if (NS_FAILED(mLoadResult)) {
|
||||
return mLoadResult;
|
||||
}
|
||||
}
|
||||
|
||||
// not using AutoString since we don't want to copy buffer to result
|
||||
nsString value;
|
||||
if (!DataSet(aStorage).mKeys.Get(aKey, &value)) {
|
||||
SetDOMStringToNull(value);
|
||||
}
|
||||
|
||||
aRetval = value;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DOMStorageCache::SetItem(const DOMStorage* aStorage, const nsAString& aKey,
|
||||
const nsString& aValue, nsString& aOld)
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_SETVALUE_MS> autoTimer;
|
||||
|
||||
if (Persist(aStorage)) {
|
||||
WaitForPreload(Telemetry::LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS);
|
||||
if (NS_FAILED(mLoadResult)) {
|
||||
return mLoadResult;
|
||||
}
|
||||
}
|
||||
|
||||
Data& data = DataSet(aStorage);
|
||||
if (!data.mKeys.Get(aKey, &aOld)) {
|
||||
SetDOMStringToNull(aOld);
|
||||
}
|
||||
|
||||
// Check the quota first
|
||||
const int64_t delta = static_cast<int64_t>(aValue.Length()) -
|
||||
static_cast<int64_t>(aOld.Length());
|
||||
if (!ProcessUsageDelta(aStorage, delta)) {
|
||||
return NS_ERROR_DOM_QUOTA_REACHED;
|
||||
}
|
||||
|
||||
if (aValue == aOld && DOMStringIsNull(aValue) == DOMStringIsNull(aOld)) {
|
||||
return NS_SUCCESS_DOM_NO_OPERATION;
|
||||
}
|
||||
|
||||
data.mKeys.Put(aKey, aValue);
|
||||
|
||||
if (Persist(aStorage)) {
|
||||
if (DOMStringIsNull(aOld)) {
|
||||
return sDatabase->AsyncAddItem(this, aKey, aValue);
|
||||
}
|
||||
|
||||
return sDatabase->AsyncUpdateItem(this, aKey, aValue);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DOMStorageCache::RemoveItem(const DOMStorage* aStorage, const nsAString& aKey,
|
||||
nsString& aOld)
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_REMOVEKEY_MS> autoTimer;
|
||||
|
||||
if (Persist(aStorage)) {
|
||||
WaitForPreload(Telemetry::LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS);
|
||||
if (NS_FAILED(mLoadResult)) {
|
||||
return mLoadResult;
|
||||
}
|
||||
}
|
||||
|
||||
Data& data = DataSet(aStorage);
|
||||
if (!data.mKeys.Get(aKey, &aOld)) {
|
||||
SetDOMStringToNull(aOld);
|
||||
return NS_SUCCESS_DOM_NO_OPERATION;
|
||||
}
|
||||
|
||||
// Recalculate the cached data size
|
||||
const int64_t delta = -(static_cast<int64_t>(aOld.Length()));
|
||||
unused << ProcessUsageDelta(aStorage, delta);
|
||||
data.mKeys.Remove(aKey);
|
||||
|
||||
if (Persist(aStorage)) {
|
||||
return sDatabase->AsyncRemoveItem(this, aKey);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DOMStorageCache::Clear(const DOMStorage* aStorage)
|
||||
{
|
||||
Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_CLEAR_MS> autoTimer;
|
||||
|
||||
bool refresh = false;
|
||||
if (Persist(aStorage)) {
|
||||
// We need to preload all data (know the size) before we can proceeed
|
||||
// to correctly decrease cached usage number.
|
||||
// XXX as in case of unload, this is not technically needed now, but
|
||||
// after super-scope quota introduction we have to do this. Get telemetry
|
||||
// right now.
|
||||
WaitForPreload(Telemetry::LOCALDOMSTORAGE_CLEAR_BLOCKING_MS);
|
||||
if (NS_FAILED(mLoadResult)) {
|
||||
// When we failed to load data from the database, force delete of the
|
||||
// scope data and make use of the storage possible again.
|
||||
refresh = true;
|
||||
mLoadResult = NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
Data& data = DataSet(aStorage);
|
||||
bool hadData = !!data.mKeys.Count();
|
||||
|
||||
if (hadData) {
|
||||
unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage);
|
||||
data.mKeys.Clear();
|
||||
}
|
||||
|
||||
if (Persist(aStorage) && (refresh || hadData)) {
|
||||
return sDatabase->AsyncClear(this);
|
||||
}
|
||||
|
||||
return hadData ? NS_OK : NS_SUCCESS_DOM_NO_OPERATION;
|
||||
}
|
||||
|
||||
void
|
||||
DOMStorageCache::CloneFrom(const DOMStorageCache* aThat)
|
||||
{
|
||||
mLoaded = aThat->mLoaded;
|
||||
mInitialized = aThat->mInitialized;
|
||||
mPersistent = aThat->mPersistent;
|
||||
mSessionOnlyDataSetActive = aThat->mSessionOnlyDataSetActive;
|
||||
|
||||
for (uint32_t i = 0; i < kDataSetCount; ++i) {
|
||||
aThat->mData[i].mKeys.EnumerateRead(CloneSetData, &mData[i]);
|
||||
ProcessUsageDelta(i, aThat->mData[i].mOriginQuotaUsage);
|
||||
}
|
||||
}
|
||||
|
||||
// Defined in DOMStorageManager.cpp
|
||||
extern bool
|
||||
PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal);
|
||||
|
||||
bool
|
||||
DOMStorageCache::CheckPrincipal(nsIPrincipal* aPrincipal) const
|
||||
{
|
||||
return PrincipalsEqual(mPrincipal, aPrincipal);
|
||||
}
|
||||
|
||||
void
|
||||
DOMStorageCache::UnloadItems(uint32_t aUnloadFlags)
|
||||
{
|
||||
if (aUnloadFlags & kUnloadDefault) {
|
||||
// Must wait for preload to pass correct usage to ProcessUsageDelta
|
||||
// XXX this is not technically needed right now since there is just
|
||||
// per-origin isolated quota handling, but when we introduce super-
|
||||
// -scope quotas, we have to do this. Better to start getting
|
||||
// telemetry right now.
|
||||
WaitForPreload(Telemetry::LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS);
|
||||
|
||||
mData[kDefaultSet].mKeys.Clear();
|
||||
ProcessUsageDelta(kDefaultSet, -mData[kDefaultSet].mOriginQuotaUsage);
|
||||
}
|
||||
|
||||
if (aUnloadFlags & kUnloadPrivate) {
|
||||
mData[kPrivateSet].mKeys.Clear();
|
||||
ProcessUsageDelta(kPrivateSet, -mData[kPrivateSet].mOriginQuotaUsage);
|
||||
}
|
||||
|
||||
if (aUnloadFlags & kUnloadSession) {
|
||||
mData[kSessionSet].mKeys.Clear();
|
||||
ProcessUsageDelta(kSessionSet, -mData[kSessionSet].mOriginQuotaUsage);
|
||||
mSessionOnlyDataSetActive = false;
|
||||
}
|
||||
|
||||
#ifdef DOM_STORAGE_TESTS
|
||||
if (aUnloadFlags & kTestReload) {
|
||||
WaitForPreload(Telemetry::LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS);
|
||||
|
||||
mData[kDefaultSet].mKeys.Clear();
|
||||
mLoaded = false; // This is only used in testing code
|
||||
Preload();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// DOMStorageCacheBridge
|
||||
|
||||
uint32_t
|
||||
DOMStorageCache::LoadedCount()
|
||||
{
|
||||
MonitorAutoLock monitor(mMonitor);
|
||||
Data& data = mData[kDefaultSet];
|
||||
return data.mKeys.Count();
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageCache::LoadItem(const nsAString& aKey, const nsString& aValue)
|
||||
{
|
||||
MonitorAutoLock monitor(mMonitor);
|
||||
if (mLoaded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Data& data = mData[kDefaultSet];
|
||||
if (data.mKeys.Get(aKey, nullptr)) {
|
||||
return true; // don't stop, just don't override
|
||||
}
|
||||
|
||||
data.mKeys.Put(aKey, aValue);
|
||||
data.mOriginQuotaUsage += aKey.Length() + aValue.Length();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DOMStorageCache::LoadDone(nsresult aRv)
|
||||
{
|
||||
// Keep the preloaded cache alive for a time
|
||||
KeepAlive();
|
||||
|
||||
MonitorAutoLock monitor(mMonitor);
|
||||
mLoadResult = aRv;
|
||||
mLoaded = true;
|
||||
monitor.Notify();
|
||||
}
|
||||
|
||||
void
|
||||
DOMStorageCache::LoadWait()
|
||||
{
|
||||
MonitorAutoLock monitor(mMonitor);
|
||||
while (!mLoaded) {
|
||||
monitor.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
// DOMStorageUsage
|
||||
|
||||
DOMStorageUsage::DOMStorageUsage(const nsACString& aScope)
|
||||
: mScope(aScope)
|
||||
{
|
||||
mUsage[kDefaultSet] = mUsage[kPrivateSet] = mUsage[kSessionSet] = 0LL;
|
||||
}
|
||||
|
||||
namespace { // anon
|
||||
|
||||
class LoadUsageRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
LoadUsageRunnable(int64_t* aUsage, const int64_t aDelta)
|
||||
: mTarget(aUsage)
|
||||
, mDelta(aDelta)
|
||||
{}
|
||||
|
||||
private:
|
||||
int64_t* mTarget;
|
||||
int64_t mDelta;
|
||||
|
||||
NS_IMETHOD Run() { *mTarget = mDelta; return NS_OK; }
|
||||
};
|
||||
|
||||
} // anon
|
||||
|
||||
void
|
||||
DOMStorageUsage::LoadUsage(const int64_t aUsage)
|
||||
{
|
||||
// Using kDefaultSet index since it is the index for the persitent data
|
||||
// stored in the database we have just loaded usage for.
|
||||
if (!NS_IsMainThread()) {
|
||||
// In single process scenario we get this call from the DB thread
|
||||
nsRefPtr<LoadUsageRunnable> r =
|
||||
new LoadUsageRunnable(mUsage + kDefaultSet, aUsage);
|
||||
NS_DispatchToMainThread(r);
|
||||
} else {
|
||||
// On a child process we get this on the main thread already
|
||||
mUsage[kDefaultSet] += aUsage;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageUsage::CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, const int64_t aDelta)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
int64_t newUsage = mUsage[aDataSetIndex] + aDelta;
|
||||
if (aDelta > 0 && newUsage > DOMStorageManager::GetQuota()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mUsage[aDataSetIndex] = newUsage;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
DOMStorageDBBridge*
|
||||
DOMStorageCache::StartDatabase()
|
||||
{
|
||||
if (sDatabase) {
|
||||
return sDatabase;
|
||||
}
|
||||
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Default) {
|
||||
nsAutoPtr<DOMStorageDBThread> db(new DOMStorageDBThread());
|
||||
|
||||
nsresult rv = db->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sDatabase = db.forget();
|
||||
} else {
|
||||
nsRefPtr<DOMStorageDBChild> db = new DOMStorageDBChild(
|
||||
DOMLocalStorageManager::Self());
|
||||
|
||||
nsresult rv = db->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
db.forget(&sDatabase);
|
||||
}
|
||||
|
||||
return sDatabase;
|
||||
}
|
||||
|
||||
// static
|
||||
DOMStorageDBBridge*
|
||||
DOMStorageCache::GetDatabase()
|
||||
{
|
||||
return sDatabase;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
DOMStorageCache::StopDatabase()
|
||||
{
|
||||
if (!sDatabase) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = sDatabase->Shutdown();
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Default) {
|
||||
delete sDatabase;
|
||||
} else {
|
||||
DOMStorageDBChild* child = static_cast<DOMStorageDBChild*>(sDatabase);
|
||||
NS_RELEASE(child);
|
||||
}
|
||||
|
||||
sDatabase = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // ::dom
|
||||
} // ::mozilla
|
|
@ -0,0 +1,253 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsDOMStorageCache_h___
|
||||
#define nsDOMStorageCache_h___
|
||||
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsITimer.h"
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class DOMStorage;
|
||||
class DOMStorageManager;
|
||||
class DOMStorageDBBridge;
|
||||
|
||||
// Interface class on which only the database or IPC may call.
|
||||
// Used to populate the cache with DB data.
|
||||
class DOMStorageCacheBridge
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD_(nsrefcnt) AddRef(void);
|
||||
NS_IMETHOD_(void) Release(void);
|
||||
|
||||
virtual ~DOMStorageCacheBridge() {}
|
||||
|
||||
// The scope (origin) in the database usage format (reversed)
|
||||
virtual const nsCString& Scope() const = 0;
|
||||
|
||||
// Whether the cache is already fully loaded
|
||||
virtual bool Loaded() = 0;
|
||||
|
||||
// How many items has so far been loaded into the cache, used
|
||||
// for optimization purposes
|
||||
virtual uint32_t LoadedCount() = 0;
|
||||
|
||||
// Called by the database to load a key and its value to the cache
|
||||
virtual bool LoadItem(const nsAString& aKey, const nsString& aValue) = 0;
|
||||
|
||||
// Called by the database after all keys and values has been loaded
|
||||
// to this cache
|
||||
virtual void LoadDone(nsresult aRv) = 0;
|
||||
|
||||
// Use to synchronously wait until the cache gets fully loaded with data,
|
||||
// this method exits after LoadDone has been called
|
||||
virtual void LoadWait() = 0;
|
||||
|
||||
protected:
|
||||
nsAutoRefCnt mRefCnt;
|
||||
};
|
||||
|
||||
// Implementation of scope cache that is responsible for preloading data
|
||||
// for persistent storage (localStorage) and hold data for non-private,
|
||||
// private and session-only cookie modes. It is also responsible for
|
||||
// persisting data changes using the database, works as a write-back cache.
|
||||
class DOMStorageCache : public DOMStorageCacheBridge
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD_(void) Release(void);
|
||||
|
||||
DOMStorageCache(const nsACString* aScope);
|
||||
virtual ~DOMStorageCache();
|
||||
|
||||
void Init(DOMStorageManager* aManager, bool aPersistent, nsIPrincipal* aPrincipal,
|
||||
const nsACString& aQuotaScope);
|
||||
|
||||
// Copies all data from the other storage.
|
||||
void CloneFrom(const DOMStorageCache* aThat);
|
||||
|
||||
// Starts async preload of this cache if it persistent and not loaded.
|
||||
void Preload();
|
||||
|
||||
// Keeps the cache alive (i.e. present in the manager's hash table) for a time.
|
||||
void KeepAlive();
|
||||
|
||||
// The set of methods that are invoked by DOM storage web API.
|
||||
// We are passing the DOMStorage object just to let the cache
|
||||
// read properties like mPrivate and mSessionOnly.
|
||||
// Get* methods return error when load from the database has failed.
|
||||
nsresult GetLength(const DOMStorage* aStorage, uint32_t* aRetval);
|
||||
nsresult GetKey(const DOMStorage* aStorage, uint32_t index, nsAString& aRetval);
|
||||
nsresult GetItem(const DOMStorage* aStorage, const nsAString& aKey, nsAString& aRetval);
|
||||
nsresult SetItem(const DOMStorage* aStorage, const nsAString& aKey, const nsString& aValue, nsString& aOld);
|
||||
nsresult RemoveItem(const DOMStorage* aStorage, const nsAString& aKey, nsString& aOld);
|
||||
nsresult Clear(const DOMStorage* aStorage);
|
||||
|
||||
nsTArray<nsString>* GetKeys(const DOMStorage* aStorage);
|
||||
|
||||
// Whether the principal equals principal the cache was created for
|
||||
bool CheckPrincipal(nsIPrincipal* aPrincipal) const;
|
||||
nsIPrincipal* Principal() const { return mPrincipal; }
|
||||
|
||||
// Starts the database engine thread or the IPC bridge
|
||||
static DOMStorageDBBridge* StartDatabase();
|
||||
static DOMStorageDBBridge* GetDatabase();
|
||||
|
||||
// Stops the thread and flushes all uncommited data
|
||||
static nsresult StopDatabase();
|
||||
|
||||
// DOMStorageCacheBridge
|
||||
|
||||
virtual const nsCString& Scope() const { return mScope; }
|
||||
virtual bool Loaded() { return mLoaded; }
|
||||
virtual uint32_t LoadedCount();
|
||||
virtual bool LoadItem(const nsAString& aKey, const nsString& aValue);
|
||||
virtual void LoadDone(nsresult aRv);
|
||||
virtual void LoadWait();
|
||||
|
||||
// Cache keeps 3 sets of data: regular, private and session-only.
|
||||
// This class keeps keys and values for a set and also caches
|
||||
// size of the data for quick per-origin quota checking.
|
||||
class Data
|
||||
{
|
||||
public:
|
||||
Data() : mOriginQuotaUsage(0) { mKeys.Init(); }
|
||||
int64_t mOriginQuotaUsage;
|
||||
nsDataHashtable<nsStringHashKey, nsString> mKeys;
|
||||
};
|
||||
|
||||
public:
|
||||
// Number of data sets we keep: default, private, session
|
||||
static const uint32_t kDataSetCount = 3;
|
||||
|
||||
private:
|
||||
// API to clear the cache data, this is invoked by chrome operations
|
||||
// like cookie deletion.
|
||||
friend class DOMStorageManager;
|
||||
|
||||
static const uint32_t kUnloadDefault = 1 << 0;
|
||||
static const uint32_t kUnloadPrivate = 1 << 1;
|
||||
static const uint32_t kUnloadSession = 1 << 2;
|
||||
static const uint32_t kUnloadComplete =
|
||||
kUnloadDefault | kUnloadPrivate | kUnloadSession;
|
||||
|
||||
#ifdef DOM_STORAGE_TESTS
|
||||
static const uint32_t kTestReload = 1 << 15;
|
||||
#endif
|
||||
|
||||
void UnloadItems(uint32_t aUnloadFlags);
|
||||
|
||||
private:
|
||||
// Synchronously blocks until the cache is fully loaded from the database
|
||||
void WaitForPreload(mozilla::Telemetry::ID aTelemetryID);
|
||||
|
||||
// Helper to get one of the 3 data sets (regular, private, session)
|
||||
Data& DataSet(const DOMStorage* aStorage);
|
||||
|
||||
// Whether the storage change is about to persist
|
||||
bool Persist(const DOMStorage* aStorage) const;
|
||||
|
||||
// Changes the quota usage on the given data set if it fits the quota.
|
||||
// If not, then false is returned and no change to the set must be done.
|
||||
bool ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta);
|
||||
bool ProcessUsageDelta(const DOMStorage* aStorage, const int64_t aDelta);
|
||||
|
||||
private:
|
||||
// When a cache is reponsible for its life time (in case of localStorage data
|
||||
// cache) we need to refer our manager since removal of the cache from the hash
|
||||
// table is handled in the destructor by call to the manager.
|
||||
// Cache could potentially overlive the manager, hence the hard ref.
|
||||
nsRefPtr<DOMStorageManager> mManager;
|
||||
|
||||
// Timer that holds this cache alive for a while after it has been preloaded.
|
||||
nsCOMPtr<nsITimer> mKeepAliveTimer;
|
||||
|
||||
// Principal the cache has been initially created for, this is used only
|
||||
// for sessionStorage access checks since sessionStorage objects are strictly
|
||||
// scoped by a principal. localStorage objects on the other hand are scoped by
|
||||
// origin only.
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
||||
// The scope this cache belongs to in the "DB format", i.e. reversed
|
||||
nsCString mScope;
|
||||
|
||||
// The eTLD+1 scope used to count quota usage.
|
||||
nsCString mQuotaScope;
|
||||
|
||||
// Non-private Browsing, Private Browsing and Session Only sets.
|
||||
Data mData[kDataSetCount];
|
||||
|
||||
// This monitor is used to wait for full load of data.
|
||||
mozilla::Monitor mMonitor;
|
||||
|
||||
// Flag that is initially false. When the cache is about to work with
|
||||
// the database (i.e. it is persistent) this flags is set to true after
|
||||
// all keys and coresponding values are loaded from the database.
|
||||
// This flag never goes from true back to false.
|
||||
bool mLoaded;
|
||||
|
||||
// Result of load from the database. Valid after mLoaded flag has been set.
|
||||
nsresult mLoadResult;
|
||||
|
||||
// Init() method has been called
|
||||
bool mInitialized : 1;
|
||||
|
||||
// This cache is about to be bound with the database (i.e. it has
|
||||
// to load from the DB first and has to persist when modifying the
|
||||
// default data set.)
|
||||
bool mPersistent : 1;
|
||||
|
||||
// - False when the session-only data set was never used.
|
||||
// - True after access to session-only data has been made for the first time.
|
||||
// We also fill session-only data set with the default one at that moment.
|
||||
// Drops back to false when session-only data are cleared from chrome.
|
||||
bool mSessionOnlyDataSetActive : 1;
|
||||
|
||||
// Whether we have already captured state of the cache preload on our first access.
|
||||
bool mPreloadTelemetryRecorded : 1;
|
||||
|
||||
// DOMStorageDBThread on the parent or single process,
|
||||
// DOMStorageDBChild on the child process.
|
||||
static DOMStorageDBBridge* sDatabase;
|
||||
};
|
||||
|
||||
// DOMStorageUsage
|
||||
// Infrastructure to manage and check eTLD+1 quota
|
||||
class DOMStorageUsageBridge
|
||||
{
|
||||
public:
|
||||
virtual ~DOMStorageUsageBridge() {}
|
||||
|
||||
virtual const nsCString& Scope() = 0;
|
||||
virtual void LoadUsage(const int64_t aUsage) = 0;
|
||||
};
|
||||
|
||||
class DOMStorageUsage : public DOMStorageUsageBridge
|
||||
{
|
||||
public:
|
||||
DOMStorageUsage(const nsACString& aScope);
|
||||
|
||||
bool CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, int64_t aUsageDelta);
|
||||
|
||||
private:
|
||||
virtual const nsCString& Scope() { return mScope; }
|
||||
virtual void LoadUsage(const int64_t aUsage);
|
||||
|
||||
nsCString mScope;
|
||||
int64_t mUsage[DOMStorageCache::kDataSetCount];
|
||||
};
|
||||
|
||||
} // ::dom
|
||||
} // ::mozilla
|
||||
|
||||
#endif
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,348 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DOMStorageDBThread_h___
|
||||
#define DOMStorageDBThread_h___
|
||||
|
||||
#include "prthread.h"
|
||||
#include "prinrval.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/storage/StatementCache.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsIFile.h"
|
||||
|
||||
class mozIStorageConnection;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class DOMStorageCacheBridge;
|
||||
class DOMStorageUsageBridge;
|
||||
class DOMStorageUsage;
|
||||
|
||||
typedef mozilla::storage::StatementCache<mozIStorageStatement> StatementCache;
|
||||
|
||||
// Interface used by the cache to post operations to the asynchronous
|
||||
// database thread or process.
|
||||
class DOMStorageDBBridge
|
||||
{
|
||||
public:
|
||||
DOMStorageDBBridge();
|
||||
virtual ~DOMStorageDBBridge() {}
|
||||
|
||||
// Ensures the database engine is started
|
||||
virtual nsresult Init() = 0;
|
||||
|
||||
// Releases the database and disallows its usage
|
||||
virtual nsresult Shutdown() = 0;
|
||||
|
||||
// Asynchronously fills the cache with data from the database for first use
|
||||
// When |aPriority| is true, the preload operation is scheduled as the first one
|
||||
virtual void AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority = false) = 0;
|
||||
|
||||
// Asynchronously fill the |usage| object with actual usage of data by its scope.
|
||||
// The scope is eTLD+1 tops, never deeper subdomains.
|
||||
virtual void AsyncGetUsage(DOMStorageUsageBridge* aUsage) = 0;
|
||||
|
||||
// Synchronously fills the cache, when |aForceSync| is false and cache already got some
|
||||
// data before, the method waits for the running preload to finish
|
||||
virtual void SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync = false) = 0;
|
||||
|
||||
// Called when an existing key is modified in the storage, schedules update to the database
|
||||
virtual nsresult AsyncAddItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) = 0;
|
||||
|
||||
// Called when an existing key is modified in the storage, schedules update to the database
|
||||
virtual nsresult AsyncUpdateItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) = 0;
|
||||
|
||||
// Called when an item is removed from the storage, schedules delete of the key
|
||||
virtual nsresult AsyncRemoveItem(DOMStorageCacheBridge* aCache, const nsAString& aKey) = 0;
|
||||
|
||||
// Called when the whole storage is cleared by the DOM API, schedules delete of the scope
|
||||
virtual nsresult AsyncClear(DOMStorageCacheBridge* aCache) = 0;
|
||||
|
||||
// Called when chrome deletes e.g. cookies, schedules delete of the whole database
|
||||
virtual void AsyncClearAll() = 0;
|
||||
|
||||
// Called when only a domain and its subdomains or an app data is about to clear
|
||||
virtual void AsyncClearMatchingScope(const nsACString& aScope) = 0;
|
||||
|
||||
// Forces scheduled DB operations to be early flushed to the disk
|
||||
virtual void AsyncFlush() = 0;
|
||||
|
||||
// Check whether the scope has any data stored on disk and is thus allowed to preload
|
||||
virtual bool ShouldPreloadScope(const nsACString& aScope) = 0;
|
||||
|
||||
// Get the complete list of scopes having data
|
||||
virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes) = 0;
|
||||
|
||||
// Returns object keeping usage cache for the scope.
|
||||
DOMStorageUsage* GetScopeUsage(const nsACString& aScope);
|
||||
|
||||
protected:
|
||||
// Keeps usage cache objects for eTLD+1 scopes we have touched.
|
||||
nsClassHashtable<nsCStringHashKey, DOMStorageUsage> mUsages;
|
||||
};
|
||||
|
||||
// The implementation of the the database engine, this directly works
|
||||
// with the sqlite or any other db API we are based on
|
||||
// This class is resposible for collecting and processing asynchronous
|
||||
// DB operations over caches (DOMStorageCache) communicating though
|
||||
// DOMStorageCacheBridge interface class
|
||||
class DOMStorageDBThread MOZ_FINAL : public DOMStorageDBBridge
|
||||
{
|
||||
public:
|
||||
class PendingOperations;
|
||||
|
||||
// Representation of a singe database task, like adding and removing keys,
|
||||
// (pre)loading the whole origin data, cleaning.
|
||||
class DBOperation
|
||||
{
|
||||
public:
|
||||
typedef enum {
|
||||
// Only operation that reads data from the database
|
||||
opPreload,
|
||||
// The same as opPreload, just executed with highest priority
|
||||
opPreloadUrgent,
|
||||
|
||||
// Load usage of a scope
|
||||
opGetUsage,
|
||||
|
||||
// Operations invoked by the DOM content API
|
||||
opAddItem,
|
||||
opUpdateItem,
|
||||
opRemoveItem,
|
||||
opClear,
|
||||
|
||||
// Operations invoked by chrome
|
||||
opClearAll,
|
||||
opClearMatchingScope,
|
||||
} OperationType;
|
||||
|
||||
DBOperation(const OperationType aType,
|
||||
DOMStorageCacheBridge* aCache = nullptr,
|
||||
const nsAString& aKey = EmptyString(),
|
||||
const nsAString& aValue = EmptyString());
|
||||
DBOperation(const OperationType aType,
|
||||
DOMStorageUsageBridge* aUsage);
|
||||
DBOperation(const OperationType aType,
|
||||
const nsACString& aScope);
|
||||
~DBOperation();
|
||||
|
||||
// Executes the operation, doesn't necessarity have to be called on the I/O thread
|
||||
void PerformAndFinalize(DOMStorageDBThread* aThread);
|
||||
|
||||
// Finalize the operation, i.e. do any internal cleanup and finish calls
|
||||
void Finalize(nsresult aRv);
|
||||
|
||||
// The operation type
|
||||
OperationType Type() { return mType; }
|
||||
|
||||
// The operation scope (=origin)
|
||||
const nsCString Scope();
|
||||
|
||||
// |Scope + key| the operation is working with
|
||||
const nsCString Target();
|
||||
|
||||
private:
|
||||
// The operation implementation body
|
||||
nsresult Perform(DOMStorageDBThread* aThread);
|
||||
|
||||
friend class PendingOperations;
|
||||
OperationType mType;
|
||||
nsRefPtr<DOMStorageCacheBridge> mCache;
|
||||
DOMStorageUsageBridge* mUsage;
|
||||
nsString mKey;
|
||||
nsString mValue;
|
||||
nsCString mScope;
|
||||
};
|
||||
|
||||
// Encapsulation of collective and coalescing logic for all pending operations
|
||||
// except preloads that are handled separately as priority operations
|
||||
class PendingOperations {
|
||||
public:
|
||||
PendingOperations();
|
||||
|
||||
// Method responsible for coalescing redundant update operations with the same
|
||||
// |Target()| or clear operations with the same or matching |Scope()|
|
||||
void Add(DBOperation* aOperation);
|
||||
|
||||
// True when there are some scheduled operations to flush on disk
|
||||
bool HasTasks();
|
||||
|
||||
// Moves collected operations to a local flat list to allow execution of the operation
|
||||
// list out of the thread lock
|
||||
bool Prepare();
|
||||
|
||||
// Executes the previously |Prepared()'ed| list of operations, retuns result, but doesn't
|
||||
// handle it in any way in case of a failure
|
||||
nsresult Execute(DOMStorageDBThread* aThread);
|
||||
|
||||
// Finalizes the pending operation list, returns false when too many operations failed
|
||||
// to flush what indicates a long standing issue with the database access.
|
||||
bool Finalize(nsresult aRv);
|
||||
|
||||
// true when a clear that deletes the given |scope| is among the pending operations;
|
||||
// when a preload for that scope is being scheduled, it must be finished right away
|
||||
bool IsScopeClearPending(const nsACString& aScope);
|
||||
|
||||
// Checks whether there is a pending update (or clear, actually) operation for this scope.
|
||||
bool IsScopeUpdatePending(const nsACString& aScope);
|
||||
|
||||
private:
|
||||
// Returns true iff new operation is of type newType and there is a pending
|
||||
// operation of type pendingType for the same key (target).
|
||||
bool CheckForCoalesceOpportunity(DBOperation* aNewOp,
|
||||
DBOperation::OperationType aPendingType,
|
||||
DBOperation::OperationType aNewType);
|
||||
|
||||
// List of all clearing operations, executed first
|
||||
nsClassHashtable<nsCStringHashKey, DBOperation> mClears;
|
||||
|
||||
// List of all update/insert operations, executed as second
|
||||
nsClassHashtable<nsCStringHashKey, DBOperation> mUpdates;
|
||||
|
||||
// Collection of all tasks, valid only between Prepare() and Execute()
|
||||
nsTArray<nsAutoPtr<DBOperation> > mExecList;
|
||||
|
||||
// Number of failing flush attempts
|
||||
uint32_t mFlushFailureCount;
|
||||
};
|
||||
|
||||
public:
|
||||
DOMStorageDBThread();
|
||||
virtual ~DOMStorageDBThread() {}
|
||||
|
||||
virtual nsresult Init();
|
||||
virtual nsresult Shutdown();
|
||||
|
||||
virtual void AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority = false)
|
||||
{ InsertDBOp(new DBOperation(aPriority ? DBOperation::opPreloadUrgent : DBOperation::opPreload, aCache)); }
|
||||
|
||||
virtual void SyncPreload(DOMStorageCacheBridge* aCache, bool aForce = false);
|
||||
|
||||
virtual void AsyncGetUsage(DOMStorageUsageBridge * aUsage)
|
||||
{ InsertDBOp(new DBOperation(DBOperation::opGetUsage, aUsage)); }
|
||||
|
||||
virtual nsresult AsyncAddItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue)
|
||||
{ return InsertDBOp(new DBOperation(DBOperation::opAddItem, aCache, aKey, aValue)); }
|
||||
|
||||
virtual nsresult AsyncUpdateItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue)
|
||||
{ return InsertDBOp(new DBOperation(DBOperation::opUpdateItem, aCache, aKey, aValue)); }
|
||||
|
||||
virtual nsresult AsyncRemoveItem(DOMStorageCacheBridge* aCache, const nsAString& aKey)
|
||||
{ return InsertDBOp(new DBOperation(DBOperation::opRemoveItem, aCache, aKey)); }
|
||||
|
||||
virtual nsresult AsyncClear(DOMStorageCacheBridge* aCache)
|
||||
{ return InsertDBOp(new DBOperation(DBOperation::opClear, aCache)); }
|
||||
|
||||
virtual void AsyncClearAll()
|
||||
{ InsertDBOp(new DBOperation(DBOperation::opClearAll)); }
|
||||
|
||||
virtual void AsyncClearMatchingScope(const nsACString& aScope)
|
||||
{ InsertDBOp(new DBOperation(DBOperation::opClearMatchingScope, aScope)); }
|
||||
|
||||
virtual void AsyncFlush();
|
||||
|
||||
virtual bool ShouldPreloadScope(const nsACString& aScope);
|
||||
virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIFile> mDatabaseFile;
|
||||
PRThread* mThread;
|
||||
|
||||
// The monitor we drive the thread with
|
||||
Monitor mMonitor;
|
||||
|
||||
// Flag to stop, protected by the monitor
|
||||
bool mStopIOThread;
|
||||
|
||||
// Whether WAL is enabled
|
||||
bool mWALModeEnabled;
|
||||
|
||||
// Whether DB has already been open, avoid races between main thread reads
|
||||
// and pending DB init in the background I/O thread
|
||||
bool mDBReady;
|
||||
|
||||
// State of the database initiation
|
||||
nsresult mStatus;
|
||||
|
||||
// List of scopes having data, for optimization purposes only
|
||||
nsTHashtable<nsCStringHashKey> mScopesHavingData;
|
||||
|
||||
StatementCache mWorkerStatements;
|
||||
StatementCache mReaderStatements;
|
||||
|
||||
// Connection used by the worker thread for all read and write ops
|
||||
nsCOMPtr<mozIStorageConnection> mWorkerConnection;
|
||||
|
||||
// Connection used only on the main thread for sync read operations
|
||||
nsCOMPtr<mozIStorageConnection> mReaderConnection;
|
||||
|
||||
// Time the first pending operation has been added to the pending operations
|
||||
// list
|
||||
PRIntervalTime mDirtyEpoch;
|
||||
|
||||
// Flag to force immediate flush of all pending operations
|
||||
bool mFlushImmediately;
|
||||
|
||||
// List of preloading operations, in chronological or priority order.
|
||||
// Executed prioritly over pending update operations.
|
||||
nsTArray<DBOperation*> mPreloads;
|
||||
|
||||
// Collector of pending update operations
|
||||
PendingOperations mPendingTasks;
|
||||
|
||||
// Counter of calls for thread priority rising.
|
||||
int32_t mPriorityCounter;
|
||||
|
||||
// Helper to direct an operation to one of the arrays above;
|
||||
// also checks IsScopeClearPending for preloads
|
||||
nsresult InsertDBOp(DBOperation* aOperation);
|
||||
|
||||
// Opens the database, first thing we do after start of the thread.
|
||||
nsresult OpenDatabaseConnection();
|
||||
nsresult InitDatabase();
|
||||
nsresult ShutdownDatabase();
|
||||
|
||||
// Tries to establish WAL mode
|
||||
nsresult SetJournalMode(bool aIsWal);
|
||||
nsresult TryJournalMode();
|
||||
|
||||
void SetHigherPriority();
|
||||
void SetDefaultPriority();
|
||||
|
||||
// Ensures we flush pending tasks in some reasonble time
|
||||
void ScheduleFlush();
|
||||
|
||||
// Called when flush of pending tasks is being executed
|
||||
void UnscheduleFlush();
|
||||
|
||||
// This method is used for two purposes:
|
||||
// 1. as a value passed to monitor.Wait() method
|
||||
// 2. as in indicator that flush has to be performed
|
||||
//
|
||||
// Return:
|
||||
// - PR_INTERVAL_NO_TIMEOUT when no pending tasks are scheduled
|
||||
// - larger then zero when tasks have been scheduled, but it is
|
||||
// still not time to perform the flush ; it is actual interval
|
||||
// time to wait until the flush has to happen
|
||||
// - 0 when it is time to do the flush
|
||||
PRIntervalTime TimeUntilFlush();
|
||||
|
||||
// Notifies to the main thread that flush has completed
|
||||
void NotifyFlushCompletion();
|
||||
|
||||
// Thread loop
|
||||
static void ThreadFunc(void* aArg);
|
||||
void ThreadFunc();
|
||||
};
|
||||
|
||||
} // ::dom
|
||||
} // ::mozilla
|
||||
|
||||
#endif /* DOMStorageDBThread_h___ */
|
|
@ -0,0 +1,670 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DOMStorageIPC.h"
|
||||
|
||||
#include "DOMStorageManager.h"
|
||||
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/unused.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Child
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_THREADSAFE_ADDREF(DOMStorageDBChild)
|
||||
|
||||
NS_IMETHODIMP_(nsrefcnt) DOMStorageDBChild::Release(void)
|
||||
{
|
||||
NS_PRECONDITION(0 != mRefCnt, "dup release");
|
||||
nsrefcnt count = --mRefCnt;
|
||||
NS_LOG_RELEASE(this, count, "DOMStorageDBChild");
|
||||
if (count == 1 && mIPCOpen) {
|
||||
Send__delete__(this);
|
||||
return 0;
|
||||
}
|
||||
if (count == 0) {
|
||||
mRefCnt = 1;
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void
|
||||
DOMStorageDBChild::AddIPDLReference()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(!mIPCOpen, "Attempting to retain multiple IPDL references");
|
||||
mIPCOpen = true;
|
||||
AddRef();
|
||||
}
|
||||
|
||||
void
|
||||
DOMStorageDBChild::ReleaseIPDLReference()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mIPCOpen, "Attempting to release non-existent IPDL reference");
|
||||
mIPCOpen = false;
|
||||
Release();
|
||||
}
|
||||
|
||||
DOMStorageDBChild::DOMStorageDBChild(DOMLocalStorageManager* aManager)
|
||||
: mManager(aManager)
|
||||
, mStatus(NS_OK)
|
||||
, mIPCOpen(false)
|
||||
{
|
||||
}
|
||||
|
||||
DOMStorageDBChild::~DOMStorageDBChild()
|
||||
{
|
||||
}
|
||||
|
||||
nsTHashtable<nsCStringHashKey>&
|
||||
DOMStorageDBChild::ScopesHavingData()
|
||||
{
|
||||
if (!mScopesHavingData.IsInitialized()) {
|
||||
mScopesHavingData.Init();
|
||||
}
|
||||
|
||||
return mScopesHavingData;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DOMStorageDBChild::Init()
|
||||
{
|
||||
ContentChild* child = ContentChild::GetSingleton();
|
||||
AddIPDLReference();
|
||||
child->SendPStorageConstructor(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DOMStorageDBChild::Shutdown()
|
||||
{
|
||||
// There is nothing to do here, IPC will release automatically and
|
||||
// the actual thread running on the parent process will also stop
|
||||
// automatically in profile-before-change topic observer.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DOMStorageDBChild::SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync)
|
||||
{
|
||||
if (NS_FAILED(mStatus)) {
|
||||
aCache->LoadDone(mStatus);
|
||||
return;
|
||||
}
|
||||
|
||||
// There is no way to put the child process to a wait state to receive all
|
||||
// incoming async responses from the parent, hence we have to do a sync preload
|
||||
// instead. We are smart though, we only demand keys that are left to load in
|
||||
// case the async preload has already loaded some keys.
|
||||
InfallibleTArray<nsString> keys, values;
|
||||
nsresult rv;
|
||||
SendPreload(aCache->Scope(), aCache->LoadedCount(), &keys, &values, &rv);
|
||||
|
||||
for (uint32_t i = 0; i < keys.Length(); ++i) {
|
||||
aCache->LoadItem(keys[i], values[i]);
|
||||
}
|
||||
|
||||
aCache->LoadDone(rv);
|
||||
}
|
||||
|
||||
nsresult
|
||||
DOMStorageDBChild::AsyncAddItem(DOMStorageCacheBridge* aCache,
|
||||
const nsAString& aKey,
|
||||
const nsAString& aValue)
|
||||
{
|
||||
if (NS_FAILED(mStatus)) {
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
SendAsyncAddItem(aCache->Scope(), nsString(aKey), nsString(aValue));
|
||||
ScopesHavingData().PutEntry(aCache->Scope());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DOMStorageDBChild::AsyncUpdateItem(DOMStorageCacheBridge* aCache,
|
||||
const nsAString& aKey,
|
||||
const nsAString& aValue)
|
||||
{
|
||||
if (NS_FAILED(mStatus)) {
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
SendAsyncUpdateItem(aCache->Scope(), nsString(aKey), nsString(aValue));
|
||||
ScopesHavingData().PutEntry(aCache->Scope());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DOMStorageDBChild::AsyncRemoveItem(DOMStorageCacheBridge* aCache,
|
||||
const nsAString& aKey)
|
||||
{
|
||||
if (NS_FAILED(mStatus)) {
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
SendAsyncRemoveItem(aCache->Scope(), nsString(aKey));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DOMStorageDBChild::AsyncClear(DOMStorageCacheBridge* aCache)
|
||||
{
|
||||
if (NS_FAILED(mStatus)) {
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
SendAsyncClear(aCache->Scope());
|
||||
ScopesHavingData().RemoveEntry(aCache->Scope());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageDBChild::ShouldPreloadScope(const nsACString& aScope)
|
||||
{
|
||||
// Return true if we didn't receive the aScope list yet.
|
||||
// I tend to rather preserve a bit of early-after-start performance
|
||||
// then a bit of memory here.
|
||||
return !mScopesHavingData.IsInitialized() ||
|
||||
mScopesHavingData.Contains(aScope);
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageDBChild::RecvObserve(const nsCString& aTopic,
|
||||
const nsCString& aScopePrefix)
|
||||
{
|
||||
DOMStorageObserver::Self()->Notify(aTopic.get(), aScopePrefix);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageDBChild::RecvScopesHavingData(const InfallibleTArray<nsCString>& aScopes)
|
||||
{
|
||||
for (uint32_t i = 0; i < aScopes.Length(); ++i) {
|
||||
ScopesHavingData().PutEntry(aScopes[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageDBChild::RecvLoadItem(const nsCString& aScope,
|
||||
const nsString& aKey,
|
||||
const nsString& aValue)
|
||||
{
|
||||
DOMStorageCache* aCache = mManager->GetCache(aScope);
|
||||
if (aCache) {
|
||||
aCache->LoadItem(aKey, aValue);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageDBChild::RecvLoadDone(const nsCString& aScope, const nsresult& aRv)
|
||||
{
|
||||
DOMStorageCache* aCache = mManager->GetCache(aScope);
|
||||
if (aCache) {
|
||||
aCache->LoadDone(aRv);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageDBChild::RecvLoadUsage(const nsCString& aScope, const int64_t& aUsage)
|
||||
{
|
||||
DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
|
||||
if (!db) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DOMStorageUsageBridge* scopeUsage = db->GetScopeUsage(aScope);
|
||||
scopeUsage->LoadUsage(aUsage);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageDBChild::RecvError(const nsresult& aRv)
|
||||
{
|
||||
mStatus = aRv;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Parent
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_THREADSAFE_ADDREF(DOMStorageDBParent)
|
||||
NS_IMPL_THREADSAFE_RELEASE(DOMStorageDBParent)
|
||||
|
||||
void
|
||||
DOMStorageDBParent::AddIPDLReference()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(!mIPCOpen, "Attempting to retain multiple IPDL references");
|
||||
mIPCOpen = true;
|
||||
AddRef();
|
||||
}
|
||||
|
||||
void
|
||||
DOMStorageDBParent::ReleaseIPDLReference()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mIPCOpen, "Attempting to release non-existent IPDL reference");
|
||||
mIPCOpen = false;
|
||||
Release();
|
||||
}
|
||||
|
||||
namespace { // anon
|
||||
|
||||
class SendScopesHavingDataRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
SendScopesHavingDataRunnable(DOMStorageDBParent* aParent)
|
||||
: mParent(aParent)
|
||||
{}
|
||||
|
||||
private:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
|
||||
if (db) {
|
||||
InfallibleTArray<nsCString> scopes;
|
||||
db->GetScopesHavingData(&scopes);
|
||||
mozilla::unused << mParent->SendScopesHavingData(scopes);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<DOMStorageDBParent> mParent;
|
||||
};
|
||||
|
||||
} // anon
|
||||
|
||||
DOMStorageDBParent::DOMStorageDBParent()
|
||||
: mIPCOpen(false)
|
||||
{
|
||||
DOMStorageObserver* observer = DOMStorageObserver::Self();
|
||||
if (observer) {
|
||||
observer->AddSink(this);
|
||||
}
|
||||
|
||||
// We are always open by IPC only
|
||||
AddIPDLReference();
|
||||
|
||||
// Cannot send directly from here since the channel
|
||||
// is not completely built at this moment.
|
||||
nsRefPtr<SendScopesHavingDataRunnable> r =
|
||||
new SendScopesHavingDataRunnable(this);
|
||||
NS_DispatchToCurrentThread(r);
|
||||
}
|
||||
|
||||
DOMStorageDBParent::~DOMStorageDBParent()
|
||||
{
|
||||
DOMStorageObserver* observer = DOMStorageObserver::Self();
|
||||
if (observer) {
|
||||
observer->RemoveSink(this);
|
||||
}
|
||||
}
|
||||
|
||||
DOMStorageDBParent::CacheParentBridge*
|
||||
DOMStorageDBParent::NewCache(const nsACString& aScope)
|
||||
{
|
||||
return new CacheParentBridge(this, aScope);
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageDBParent::RecvAsyncPreload(const nsCString& aScope, const bool& aPriority)
|
||||
{
|
||||
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
|
||||
if (!db) {
|
||||
return false;
|
||||
}
|
||||
|
||||
db->AsyncPreload(NewCache(aScope), aPriority);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageDBParent::RecvAsyncGetUsage(const nsCString& aScope)
|
||||
{
|
||||
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
|
||||
if (!db) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The object releases it self in LoadUsage method
|
||||
UsageParentBridge* usage = new UsageParentBridge(this, aScope);
|
||||
db->AsyncGetUsage(usage);
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace { // anon
|
||||
|
||||
// We need another implementation of DOMStorageCacheBridge to do
|
||||
// synchronous IPC preload. This class just receives Load* notifications
|
||||
// and fills the returning arguments of RecvPreload with the database
|
||||
// values for us.
|
||||
class SyncLoadCacheHelper : public DOMStorageCacheBridge
|
||||
{
|
||||
public:
|
||||
SyncLoadCacheHelper(const nsCString& aScope,
|
||||
uint32_t aAlreadyLoadedCount,
|
||||
InfallibleTArray<nsString>* aKeys,
|
||||
InfallibleTArray<nsString>* aValues,
|
||||
nsresult* rv)
|
||||
: mMonitor("DOM Storage SyncLoad IPC")
|
||||
, mScope(aScope)
|
||||
, mKeys(aKeys)
|
||||
, mValues(aValues)
|
||||
, mRv(rv)
|
||||
, mLoaded(false)
|
||||
, mLoadedCount(aAlreadyLoadedCount)
|
||||
{
|
||||
// Precaution
|
||||
*mRv = NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
virtual const nsCString& Scope() const { return mScope; }
|
||||
virtual bool Loaded() { return mLoaded; }
|
||||
virtual uint32_t LoadedCount() { return mLoadedCount; }
|
||||
virtual bool LoadItem(const nsAString& aKey, const nsString& aValue)
|
||||
{
|
||||
// Called on the aCache background thread
|
||||
if (mLoaded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
++mLoadedCount;
|
||||
mKeys->AppendElement(aKey);
|
||||
mValues->AppendElement(aValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void LoadDone(nsresult aRv)
|
||||
{
|
||||
// Called on the aCache background thread
|
||||
MonitorAutoLock monitor(mMonitor);
|
||||
mLoaded = true;
|
||||
*mRv = aRv;
|
||||
monitor.Notify();
|
||||
}
|
||||
|
||||
virtual void LoadWait()
|
||||
{
|
||||
// Called on the main thread, exits after LoadDone() call
|
||||
MonitorAutoLock monitor(mMonitor);
|
||||
while (!mLoaded) {
|
||||
monitor.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Monitor mMonitor;
|
||||
nsCString mScope;
|
||||
InfallibleTArray<nsString>* mKeys;
|
||||
InfallibleTArray<nsString>* mValues;
|
||||
nsresult* mRv;
|
||||
bool mLoaded;
|
||||
uint32_t mLoadedCount;
|
||||
};
|
||||
|
||||
} // anon
|
||||
|
||||
bool
|
||||
DOMStorageDBParent::RecvPreload(const nsCString& aScope,
|
||||
const uint32_t& aAlreadyLoadedCount,
|
||||
InfallibleTArray<nsString>* aKeys,
|
||||
InfallibleTArray<nsString>* aValues,
|
||||
nsresult* aRv)
|
||||
{
|
||||
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
|
||||
if (!db) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsRefPtr<SyncLoadCacheHelper> aCache(
|
||||
new SyncLoadCacheHelper(aScope, aAlreadyLoadedCount, aKeys, aValues, aRv));
|
||||
|
||||
db->SyncPreload(aCache, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageDBParent::RecvAsyncAddItem(const nsCString& aScope,
|
||||
const nsString& aKey,
|
||||
const nsString& aValue)
|
||||
{
|
||||
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
|
||||
if (!db) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult rv = db->AsyncAddItem(NewCache(aScope), aKey, aValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
mozilla::unused << SendError(rv);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageDBParent::RecvAsyncUpdateItem(const nsCString& aScope,
|
||||
const nsString& aKey,
|
||||
const nsString& aValue)
|
||||
{
|
||||
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
|
||||
if (!db) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult rv = db->AsyncUpdateItem(NewCache(aScope), aKey, aValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
mozilla::unused << SendError(rv);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageDBParent::RecvAsyncRemoveItem(const nsCString& aScope,
|
||||
const nsString& aKey)
|
||||
{
|
||||
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
|
||||
if (!db) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult rv = db->AsyncRemoveItem(NewCache(aScope), aKey);
|
||||
if (NS_FAILED(rv)) {
|
||||
mozilla::unused << SendError(rv);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageDBParent::RecvAsyncClear(const nsCString& aScope)
|
||||
{
|
||||
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
|
||||
if (!db) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult rv = db->AsyncClear(NewCache(aScope));
|
||||
if (NS_FAILED(rv)) {
|
||||
mozilla::unused << SendError(rv);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DOMStorageDBParent::RecvAsyncFlush()
|
||||
{
|
||||
DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
|
||||
if (!db) {
|
||||
return false;
|
||||
}
|
||||
|
||||
db->AsyncFlush();
|
||||
return true;
|
||||
}
|
||||
|
||||
// DOMStorageObserverSink
|
||||
|
||||
nsresult
|
||||
DOMStorageDBParent::Observe(const char* aTopic,
|
||||
const nsACString& aScopePrefix)
|
||||
{
|
||||
mozilla::unused << SendObserve(nsDependentCString(aTopic),
|
||||
nsCString(aScopePrefix));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
namespace { // anon
|
||||
|
||||
// Results must be sent back on the main thread
|
||||
class LoadRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
enum TaskType {
|
||||
loadItem,
|
||||
loadDone
|
||||
};
|
||||
|
||||
LoadRunnable(DOMStorageDBParent* aParent,
|
||||
TaskType aType,
|
||||
const nsACString& aScope,
|
||||
const nsAString& aKey = EmptyString(),
|
||||
const nsAString& aValue = EmptyString())
|
||||
: mParent(aParent)
|
||||
, mType(aType)
|
||||
, mScope(aScope)
|
||||
, mKey(aKey)
|
||||
, mValue(aValue)
|
||||
{ }
|
||||
|
||||
LoadRunnable(DOMStorageDBParent* aParent,
|
||||
TaskType aType,
|
||||
const nsACString& aScope,
|
||||
nsresult aRv)
|
||||
: mParent(aParent)
|
||||
, mType(aType)
|
||||
, mScope(aScope)
|
||||
, mRv(aRv)
|
||||
{ }
|
||||
|
||||
private:
|
||||
nsRefPtr<DOMStorageDBParent> mParent;
|
||||
TaskType mType;
|
||||
nsCString mScope;
|
||||
nsString mKey;
|
||||
nsString mValue;
|
||||
nsresult mRv;
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
if (!mParent->IPCOpen()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
switch (mType)
|
||||
{
|
||||
case loadItem:
|
||||
mozilla::unused << mParent->SendLoadItem(mScope, mKey, mValue);
|
||||
break;
|
||||
case loadDone:
|
||||
mozilla::unused << mParent->SendLoadDone(mScope, mRv);
|
||||
break;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
} // anon
|
||||
|
||||
// DOMStorageDBParent::CacheParentBridge
|
||||
|
||||
bool
|
||||
DOMStorageDBParent::CacheParentBridge::LoadItem(const nsAString& aKey, const nsString& aValue)
|
||||
{
|
||||
if (mLoaded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
++mLoadedCount;
|
||||
|
||||
nsRefPtr<LoadRunnable> r =
|
||||
new LoadRunnable(mParent, LoadRunnable::loadItem, mScope, aKey, aValue);
|
||||
NS_DispatchToMainThread(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DOMStorageDBParent::CacheParentBridge::LoadDone(nsresult aRv)
|
||||
{
|
||||
// Prevent send of duplicate LoadDone.
|
||||
if (mLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
mLoaded = true;
|
||||
|
||||
nsRefPtr<LoadRunnable> r =
|
||||
new LoadRunnable(mParent, LoadRunnable::loadDone, mScope, aRv);
|
||||
NS_DispatchToMainThread(r);
|
||||
}
|
||||
|
||||
void
|
||||
DOMStorageDBParent::CacheParentBridge::LoadWait()
|
||||
{
|
||||
// Should never be called on this implementation
|
||||
MOZ_ASSERT(false);
|
||||
}
|
||||
|
||||
// DOMStorageDBParent::UsageParentBridge
|
||||
|
||||
namespace { // anon
|
||||
|
||||
class UsageRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
UsageRunnable(DOMStorageDBParent* aParent, const nsACString& aScope, const int64_t& aUsage)
|
||||
: mParent(aParent)
|
||||
, mScope(aScope)
|
||||
, mUsage(aUsage)
|
||||
{}
|
||||
|
||||
private:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
mozilla::unused << mParent->SendLoadUsage(mScope, mUsage);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<DOMStorageDBParent> mParent;
|
||||
nsCString mScope;
|
||||
int64_t mUsage;
|
||||
};
|
||||
|
||||
} // anon
|
||||
|
||||
void
|
||||
DOMStorageDBParent::UsageParentBridge::LoadUsage(const int64_t aUsage)
|
||||
{
|
||||
nsRefPtr<UsageRunnable> r = new UsageRunnable(mParent, mScope, aUsage);
|
||||
NS_DispatchToMainThread(r);
|
||||
delete this;
|
||||
}
|
||||
|
||||
} // ::dom
|
||||
} // ::mozilla
|
|
@ -0,0 +1,187 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsDOMStorageIPC_h___
|
||||
#define nsDOMStorageIPC_h___
|
||||
|
||||
#include "mozilla/dom/PStorageChild.h"
|
||||
#include "mozilla/dom/PStorageParent.h"
|
||||
#include "DOMStorageDBThread.h"
|
||||
#include "DOMStorageCache.h"
|
||||
#include "DOMStorageObserver.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class DOMLocalStorageManager;
|
||||
|
||||
// Child side of the IPC protocol, exposes as DB interface but
|
||||
// is responsible to send all requests to the parent process
|
||||
// and expects asynchronous answers. Those are then transparently
|
||||
// forwarded back to consumers on the child process.
|
||||
class DOMStorageDBChild MOZ_FINAL : public DOMStorageDBBridge
|
||||
, public PStorageChild
|
||||
{
|
||||
public:
|
||||
DOMStorageDBChild(DOMLocalStorageManager* aManager);
|
||||
virtual ~DOMStorageDBChild();
|
||||
|
||||
NS_IMETHOD_(nsrefcnt) AddRef(void);
|
||||
NS_IMETHOD_(nsrefcnt) Release(void);
|
||||
|
||||
void AddIPDLReference();
|
||||
void ReleaseIPDLReference();
|
||||
|
||||
virtual nsresult Init();
|
||||
virtual nsresult Shutdown();
|
||||
|
||||
virtual void AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority = false)
|
||||
{ SendAsyncPreload(aCache->Scope(), aPriority); }
|
||||
virtual void AsyncGetUsage(DOMStorageUsageBridge* aUsage)
|
||||
{ SendAsyncGetUsage(aUsage->Scope()); }
|
||||
|
||||
virtual void SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync = false);
|
||||
|
||||
virtual nsresult AsyncAddItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue);
|
||||
virtual nsresult AsyncUpdateItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue);
|
||||
virtual nsresult AsyncRemoveItem(DOMStorageCacheBridge* aCache, const nsAString& aKey);
|
||||
virtual nsresult AsyncClear(DOMStorageCacheBridge* aCache);
|
||||
|
||||
virtual void AsyncClearAll()
|
||||
{ mScopesHavingData.Clear(); /* NO-OP on the child process otherwise */ }
|
||||
|
||||
virtual void AsyncClearMatchingScope(const nsACString& aScope)
|
||||
{ /* NO-OP on the child process */ }
|
||||
|
||||
virtual void AsyncFlush()
|
||||
{ SendAsyncFlush(); }
|
||||
|
||||
virtual bool ShouldPreloadScope(const nsACString& aScope);
|
||||
virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes)
|
||||
{ NS_NOTREACHED("Not implemented for child process"); }
|
||||
|
||||
private:
|
||||
bool RecvObserve(const nsCString& aTopic,
|
||||
const nsCString& aScopePrefix);
|
||||
bool RecvLoadItem(const nsCString& aScope,
|
||||
const nsString& aKey,
|
||||
const nsString& aValue);
|
||||
bool RecvLoadDone(const nsCString& aScope,
|
||||
const nsresult& aRv);
|
||||
bool RecvScopesHavingData(const InfallibleTArray<nsCString>& aScopes);
|
||||
bool RecvLoadUsage(const nsCString& aScope,
|
||||
const int64_t& aUsage);
|
||||
bool RecvError(const nsresult& aRv);
|
||||
|
||||
nsTHashtable<nsCStringHashKey>& ScopesHavingData();
|
||||
|
||||
nsAutoRefCnt mRefCnt;
|
||||
|
||||
// Held to get caches to forward answers to.
|
||||
nsRefPtr<DOMLocalStorageManager> mManager;
|
||||
|
||||
// Scopes having data hash, for optimization purposes only
|
||||
nsTHashtable<nsCStringHashKey> mScopesHavingData;
|
||||
|
||||
// Status of the remote database
|
||||
nsresult mStatus;
|
||||
|
||||
bool mIPCOpen;
|
||||
};
|
||||
|
||||
|
||||
// Receives async requests from child processes and is responsible
|
||||
// to send back responses from the DB thread. Exposes as a fake
|
||||
// DOMStorageCache consumer.
|
||||
// Also responsible for forwardning all chrome operation notifications
|
||||
// such as cookie cleaning etc to the child process.
|
||||
class DOMStorageDBParent MOZ_FINAL : public PStorageParent
|
||||
, public DOMStorageObserverSink
|
||||
{
|
||||
public:
|
||||
DOMStorageDBParent();
|
||||
virtual ~DOMStorageDBParent();
|
||||
|
||||
NS_IMETHOD_(nsrefcnt) AddRef(void);
|
||||
NS_IMETHOD_(nsrefcnt) Release(void);
|
||||
|
||||
void AddIPDLReference();
|
||||
void ReleaseIPDLReference();
|
||||
|
||||
bool IPCOpen() { return mIPCOpen; }
|
||||
|
||||
public:
|
||||
// Fake cache class receiving async callbacks from DB thread, sending
|
||||
// them back to appropriate cache object on the child process.
|
||||
class CacheParentBridge : public DOMStorageCacheBridge {
|
||||
public:
|
||||
CacheParentBridge(DOMStorageDBParent* aParentDB, const nsACString& aScope)
|
||||
: mParent(aParentDB), mScope(aScope), mLoaded(false), mLoadedCount(0) {}
|
||||
virtual ~CacheParentBridge() {}
|
||||
|
||||
// DOMStorageCacheBridge
|
||||
virtual const nsCString& Scope() const
|
||||
{ return mScope; }
|
||||
virtual bool Loaded()
|
||||
{ return mLoaded; }
|
||||
virtual uint32_t LoadedCount()
|
||||
{ return mLoadedCount; }
|
||||
|
||||
virtual bool LoadItem(const nsAString& aKey, const nsString& aValue);
|
||||
virtual void LoadDone(nsresult aRv);
|
||||
virtual void LoadWait();
|
||||
|
||||
private:
|
||||
DOMStorageDBParent* mParent;
|
||||
nsCString mScope;
|
||||
bool mLoaded;
|
||||
uint32_t mLoadedCount;
|
||||
};
|
||||
|
||||
// Fake usage class receiving async callbacks from DB thread
|
||||
class UsageParentBridge : public DOMStorageUsageBridge
|
||||
{
|
||||
public:
|
||||
UsageParentBridge(DOMStorageDBParent* aParentDB, const nsACString& aScope)
|
||||
: mParent(aParentDB), mScope(aScope) {}
|
||||
virtual ~UsageParentBridge() {}
|
||||
|
||||
// DOMStorageUsageBridge
|
||||
virtual const nsCString& Scope() { return mScope; }
|
||||
virtual void LoadUsage(const int64_t usage);
|
||||
|
||||
private:
|
||||
DOMStorageDBParent* mParent;
|
||||
nsCString mScope;
|
||||
};
|
||||
|
||||
private:
|
||||
// IPC
|
||||
bool RecvAsyncPreload(const nsCString& aScope, const bool& aPriority);
|
||||
bool RecvPreload(const nsCString& aScope, const uint32_t& aAlreadyLoadedCount,
|
||||
InfallibleTArray<nsString>* aKeys, InfallibleTArray<nsString>* aValues,
|
||||
nsresult* aRv);
|
||||
bool RecvAsyncGetUsage(const nsCString& aScope);
|
||||
bool RecvAsyncAddItem(const nsCString& aScope, const nsString& aKey, const nsString& aValue);
|
||||
bool RecvAsyncUpdateItem(const nsCString& aScope, const nsString& aKey, const nsString& aValue);
|
||||
bool RecvAsyncRemoveItem(const nsCString& aScope, const nsString& aKey);
|
||||
bool RecvAsyncClear(const nsCString& aScope);
|
||||
bool RecvAsyncFlush();
|
||||
|
||||
// DOMStorageObserverSink
|
||||
virtual nsresult Observe(const char* aTopic, const nsACString& aScopePrefix);
|
||||
|
||||
private:
|
||||
CacheParentBridge* NewCache(const nsACString& aScope);
|
||||
|
||||
nsAutoRefCnt mRefCnt;
|
||||
bool mIPCOpen;
|
||||
};
|
||||
|
||||
} // ::dom
|
||||
} // ::mozilla
|
||||
|
||||
#endif
|
|
@ -0,0 +1,625 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DOMStorageManager.h"
|
||||
#include "DOMStorage.h"
|
||||
#include "DOMStorageDBThread.h"
|
||||
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIEffectiveTLDService.h"
|
||||
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
// Only allow relatively small amounts of data since performance of
|
||||
// the synchronous IO is very bad.
|
||||
// We are enforcing simple per-origin quota only.
|
||||
#define DEFAULT_QUOTA_LIMIT (5 * 1024)
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace { // anon
|
||||
|
||||
int32_t gQuotaLimit = DEFAULT_QUOTA_LIMIT;
|
||||
|
||||
} // anon
|
||||
|
||||
DOMLocalStorageManager*
|
||||
DOMLocalStorageManager::sSelf = nullptr;
|
||||
|
||||
// static
|
||||
uint32_t
|
||||
DOMStorageManager::GetQuota()
|
||||
{
|
||||
static bool preferencesInitialized = false;
|
||||
if (!preferencesInitialized) {
|
||||
mozilla::Preferences::AddIntVarCache(&gQuotaLimit, "dom.storage.default_quota",
|
||||
DEFAULT_QUOTA_LIMIT);
|
||||
preferencesInitialized = true;
|
||||
}
|
||||
|
||||
return gQuotaLimit * 1024; // pref is in kBs
|
||||
}
|
||||
|
||||
void
|
||||
ReverseString(const nsCSubstring& aSource, nsCSubstring& aResult)
|
||||
{
|
||||
nsACString::const_iterator sourceBegin, sourceEnd;
|
||||
aSource.BeginReading(sourceBegin);
|
||||
aSource.EndReading(sourceEnd);
|
||||
|
||||
aResult.SetLength(aSource.Length());
|
||||
nsACString::iterator destEnd;
|
||||
aResult.EndWriting(destEnd);
|
||||
|
||||
while (sourceBegin != sourceEnd) {
|
||||
*(--destEnd) = *sourceBegin;
|
||||
++sourceBegin;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
CreateReversedDomain(const nsACString& aAsciiDomain,
|
||||
nsACString& aKey)
|
||||
{
|
||||
if (aAsciiDomain.IsEmpty()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
ReverseString(aAsciiDomain, aKey);
|
||||
|
||||
aKey.AppendLiteral(".");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal)
|
||||
{
|
||||
if (!aSubjectPrincipal) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!aObjectPrincipal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool equals;
|
||||
nsresult rv = aSubjectPrincipal->EqualsIgnoringDomain(aObjectPrincipal, &equals);
|
||||
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv) && equals,
|
||||
"Trying to get DOM storage for wrong principal!");
|
||||
|
||||
if (NS_FAILED(rv) || !equals) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(DOMStorageManager,
|
||||
nsIDOMStorageManager)
|
||||
|
||||
DOMStorageManager::DOMStorageManager(nsPIDOMStorage::StorageType aType)
|
||||
: mType(aType)
|
||||
{
|
||||
mCaches.Init(10);
|
||||
DOMStorageObserver* observer = DOMStorageObserver::Self();
|
||||
NS_ASSERTION(observer, "No DOMStorageObserver, cannot observe private data delete notifications!");
|
||||
|
||||
if (observer) {
|
||||
observer->AddSink(this);
|
||||
}
|
||||
}
|
||||
|
||||
DOMStorageManager::~DOMStorageManager()
|
||||
{
|
||||
DOMStorageObserver* observer = DOMStorageObserver::Self();
|
||||
if (observer) {
|
||||
observer->RemoveSink(this);
|
||||
}
|
||||
}
|
||||
|
||||
namespace { // anon
|
||||
|
||||
nsresult
|
||||
CreateScopeKey(nsIPrincipal* aPrincipal,
|
||||
nsACString& aKey)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!uri) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsAutoCString domainScope;
|
||||
rv = uri->GetAsciiHost(domainScope);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (domainScope.IsEmpty()) {
|
||||
// About pages have an empty host but a valid path. Since they are handled
|
||||
// internally by our own redirector, we can trust them and use path as key.
|
||||
// if file:/// protocol, let's make the exact directory the domain
|
||||
bool isScheme = false;
|
||||
if ((NS_SUCCEEDED(uri->SchemeIs("about", &isScheme)) && isScheme) ||
|
||||
(NS_SUCCEEDED(uri->SchemeIs("moz-safe-about", &isScheme)) && isScheme)) {
|
||||
rv = uri->GetPath(domainScope);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// While the host is always canonicalized to lowercase, the path is not,
|
||||
// thus need to force the casing.
|
||||
ToLowerCase(domainScope);
|
||||
} else if (NS_SUCCEEDED(uri->SchemeIs("file", &isScheme)) && isScheme) {
|
||||
nsCOMPtr<nsIURL> url = do_QueryInterface(uri, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = url->GetDirectory(domainScope);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoCString key;
|
||||
|
||||
rv = CreateReversedDomain(domainScope, key);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsAutoCString scheme;
|
||||
rv = uri->GetScheme(scheme);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
key.Append(NS_LITERAL_CSTRING(":") + scheme);
|
||||
|
||||
int32_t port = NS_GetRealPort(uri);
|
||||
if (port != -1) {
|
||||
key.Append(nsPrintfCString(":%d", port));
|
||||
}
|
||||
|
||||
bool unknownAppId;
|
||||
rv = aPrincipal->GetUnknownAppId(&unknownAppId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!unknownAppId) {
|
||||
uint32_t appId;
|
||||
rv = aPrincipal->GetAppId(&appId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool isInBrowserElement;
|
||||
rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
|
||||
aKey.Assign(key);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
aKey.Truncate();
|
||||
aKey.AppendInt(appId);
|
||||
aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
|
||||
NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
|
||||
NS_LITERAL_CSTRING(":") + key);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CreateQuotaDBKey(nsIPrincipal* aPrincipal,
|
||||
nsACString& aKey)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsAutoCString subdomainsDBKey;
|
||||
nsCOMPtr<nsIEffectiveTLDService> eTLDService(do_GetService(
|
||||
NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = aPrincipal->GetURI(getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsAutoCString eTLDplusOne;
|
||||
rv = eTLDService->GetBaseDomain(uri, 0, eTLDplusOne);
|
||||
if (NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS == rv) {
|
||||
// XXX bug 357323 - what to do for localhost/file exactly?
|
||||
rv = uri->GetAsciiHost(eTLDplusOne);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
CreateReversedDomain(eTLDplusOne, subdomainsDBKey);
|
||||
|
||||
bool unknownAppId;
|
||||
rv = aPrincipal->GetUnknownAppId(&unknownAppId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!unknownAppId) {
|
||||
uint32_t appId;
|
||||
rv = aPrincipal->GetAppId(&appId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool isInBrowserElement;
|
||||
rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
|
||||
aKey.Assign(subdomainsDBKey);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
aKey.Truncate();
|
||||
aKey.AppendInt(appId);
|
||||
aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
|
||||
NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
|
||||
NS_LITERAL_CSTRING(":") + subdomainsDBKey);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // anon
|
||||
|
||||
DOMStorageCache*
|
||||
DOMStorageManager::GetCache(const nsACString& aScope) const
|
||||
{
|
||||
DOMStorageCacheHashKey* entry = mCaches.GetEntry(aScope);
|
||||
if (!entry) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return entry->cache();
|
||||
}
|
||||
|
||||
DOMStorageCache*
|
||||
DOMStorageManager::PutCache(const nsACString& aScope,
|
||||
nsIPrincipal* aPrincipal)
|
||||
{
|
||||
DOMStorageCacheHashKey* entry = mCaches.PutEntry(aScope);
|
||||
DOMStorageCache* cache = entry->cache();
|
||||
|
||||
nsAutoCString quotaScope;
|
||||
CreateQuotaDBKey(aPrincipal, quotaScope);
|
||||
|
||||
switch (mType) {
|
||||
case SessionStorage:
|
||||
// Lifetime handled by the manager, don't persist
|
||||
entry->HardRef();
|
||||
cache->Init(nullptr, false, aPrincipal, quotaScope);
|
||||
break;
|
||||
|
||||
case LocalStorage:
|
||||
// Lifetime handled by the cache, do persist
|
||||
cache->Init(this, true, aPrincipal, quotaScope);
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSERT(false);
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
void
|
||||
DOMStorageManager::DropCache(DOMStorageCache* aCache)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
NS_WARNING("DOMStorageManager::DropCache called on a non-main thread, shutting down?");
|
||||
}
|
||||
|
||||
mCaches.RemoveEntry(aCache->Scope());
|
||||
}
|
||||
|
||||
nsresult
|
||||
DOMStorageManager::GetStorageInternal(bool aCreate,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsAString& aDocumentURI,
|
||||
bool aPrivate,
|
||||
nsIDOMStorage** aRetval)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsAutoCString scope;
|
||||
rv = CreateScopeKey(aPrincipal, scope);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
DOMStorageCache* cache = GetCache(scope);
|
||||
|
||||
// Get or create a cache for the given scope
|
||||
if (!cache) {
|
||||
if (!aCreate) {
|
||||
*aRetval = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!aRetval) {
|
||||
// This is demand to just preload the cache, if the scope has
|
||||
// no data stored, bypass creation and preload of the cache.
|
||||
DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
|
||||
if (db) {
|
||||
if (!db->ShouldPreloadScope(scope)) {
|
||||
return NS_OK;
|
||||
}
|
||||
} else {
|
||||
if (scope.Equals(NS_LITERAL_CSTRING("knalb.:about"))) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// There is always a single instance of a cache per scope
|
||||
// in a single instance of a DOM storage manager.
|
||||
cache = PutCache(scope, aPrincipal);
|
||||
} else if (mType == SessionStorage) {
|
||||
if (!cache->CheckPrincipal(aPrincipal)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
if (aRetval) {
|
||||
*aRetval = new DOMStorage(this, cache, aDocumentURI, aPrincipal, aPrivate);
|
||||
NS_ADDREF(*aRetval);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal)
|
||||
{
|
||||
return GetStorageInternal(true, aPrincipal, EmptyString(), false, nullptr);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMStorageManager::CreateStorage(nsIPrincipal* aPrincipal,
|
||||
const nsAString& aDocumentURI,
|
||||
bool aPrivate,
|
||||
nsIDOMStorage** aRetval)
|
||||
{
|
||||
return GetStorageInternal(true, aPrincipal, aDocumentURI, aPrivate, aRetval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMStorageManager::GetStorage(nsIPrincipal* aPrincipal,
|
||||
bool aPrivate,
|
||||
nsIDOMStorage** aRetval)
|
||||
{
|
||||
return GetStorageInternal(false, aPrincipal, EmptyString(), aPrivate, aRetval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMStorageManager::CloneStorage(nsIDOMStorage* aStorage)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMStorage> pstorage = do_QueryInterface(aStorage);
|
||||
if (!pstorage) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
const DOMStorageCache* origCache = pstorage->GetCache();
|
||||
|
||||
DOMStorageCache* existingCache = GetCache(origCache->Scope());
|
||||
if (existingCache) {
|
||||
// Do not replace an existing session storage.
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
DOMStorageCache* newCache = PutCache(origCache->Scope(),
|
||||
origCache->Principal());
|
||||
|
||||
newCache->CloneFrom(origCache);
|
||||
newCache->KeepAlive();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMStorageManager::CheckStorage(nsIPrincipal* aPrincipal,
|
||||
nsIDOMStorage* aStorage,
|
||||
bool* aRetval)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMStorage> pstorage = do_QueryInterface(aStorage);
|
||||
if (!pstorage) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
*aRetval = false;
|
||||
|
||||
if (!aPrincipal) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsAutoCString scope;
|
||||
nsresult rv = CreateScopeKey(aPrincipal, scope);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
DOMStorageCache* cache = GetCache(scope);
|
||||
if (cache != pstorage->GetCache()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!pstorage->PrincipalEquals(aPrincipal)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
*aRetval = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Obsolete nsIDOMStorageManager methods
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMStorageManager::GetLocalStorageForPrincipal(nsIPrincipal* aPrincipal,
|
||||
const nsAString& aDocumentURI,
|
||||
bool aPrivate,
|
||||
nsIDOMStorage** aRetval)
|
||||
{
|
||||
if (mType != LocalStorage) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
return CreateStorage(aPrincipal, aDocumentURI, aPrivate, aRetval);
|
||||
}
|
||||
|
||||
namespace { // anon
|
||||
|
||||
class ClearCacheEnumeratorData
|
||||
{
|
||||
public:
|
||||
ClearCacheEnumeratorData(uint32_t aFlags)
|
||||
: mUnloadFlags(aFlags)
|
||||
{}
|
||||
|
||||
uint32_t mUnloadFlags;
|
||||
nsCString mKeyPrefix;
|
||||
};
|
||||
|
||||
} // anon
|
||||
|
||||
PLDHashOperator
|
||||
DOMStorageManager::ClearCacheEnumerator(DOMStorageCacheHashKey* aEntry, void* aClosure)
|
||||
{
|
||||
DOMStorageCache* cache = aEntry->cache();
|
||||
nsCString& key = const_cast<nsCString&>(cache->Scope());
|
||||
|
||||
ClearCacheEnumeratorData* data = static_cast<ClearCacheEnumeratorData*>(aClosure);
|
||||
|
||||
if (data->mKeyPrefix.IsEmpty() || StringBeginsWith(key, data->mKeyPrefix)) {
|
||||
cache->UnloadItems(data->mUnloadFlags);
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsresult
|
||||
DOMStorageManager::Observe(const char* aTopic, const nsACString& aScopePrefix)
|
||||
{
|
||||
// Clear everything, caches + database
|
||||
if (!strcmp(aTopic, "cookie-cleared")) {
|
||||
ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
|
||||
mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Clear from caches everything that has been stored
|
||||
// while in session-only mode
|
||||
if (!strcmp(aTopic, "session-only-cleared")) {
|
||||
ClearCacheEnumeratorData data(DOMStorageCache::kUnloadSession);
|
||||
data.mKeyPrefix = aScopePrefix;
|
||||
mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Clear everything (including so and pb data) from caches and database
|
||||
// for the gived domain and subdomains.
|
||||
if (!strcmp(aTopic, "domain-data-cleared")) {
|
||||
ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
|
||||
data.mKeyPrefix = aScopePrefix;
|
||||
mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Clear all private-browsing caches
|
||||
if (!strcmp(aTopic, "private-browsing-data-cleared")) {
|
||||
ClearCacheEnumeratorData data(DOMStorageCache::kUnloadPrivate);
|
||||
mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Clear localStorage data beloging to an app.
|
||||
if (!strcmp(aTopic, "app-data-cleared")) {
|
||||
|
||||
// sessionStorage is expected to stay
|
||||
if (mType == SessionStorage) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
|
||||
data.mKeyPrefix = aScopePrefix;
|
||||
mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, "profile-change")) {
|
||||
// For case caches are still referenced - clear them completely
|
||||
ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
|
||||
mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
|
||||
|
||||
mCaches.Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef DOM_STORAGE_TESTS
|
||||
if (!strcmp(aTopic, "test-reload")) {
|
||||
if (mType != LocalStorage) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// This immediately completely reloads all caches from the database.
|
||||
ClearCacheEnumeratorData data(DOMStorageCache::kTestReload);
|
||||
mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, "test-flushed")) {
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->NotifyObservers(nullptr, "domstorage-test-flushed", nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_ERROR("Unexpected topic");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// DOMLocalStorageManager
|
||||
|
||||
DOMLocalStorageManager::DOMLocalStorageManager()
|
||||
: DOMStorageManager(LocalStorage)
|
||||
{
|
||||
NS_ASSERTION(!sSelf, "Somebody is trying to do_CreateInstance(\"@mozilla/dom/localStorage-manager;1\"");
|
||||
sSelf = this;
|
||||
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
// Do this only on the child process. The thread IPC bridge
|
||||
// is also used to communicate chrome observer notifications.
|
||||
// Note: must be called after we set sSelf
|
||||
DOMStorageCache::StartDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
DOMLocalStorageManager::~DOMLocalStorageManager()
|
||||
{
|
||||
sSelf = nullptr;
|
||||
}
|
||||
|
||||
// DOMSessionStorageManager
|
||||
|
||||
DOMSessionStorageManager::DOMSessionStorageManager()
|
||||
: DOMStorageManager(SessionStorage)
|
||||
{
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
// Do this only on the child process. The thread IPC bridge
|
||||
// is also used to communicate chrome observer notifications.
|
||||
DOMStorageCache::StartDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
} // ::dom
|
||||
} // ::mozilla
|
|
@ -0,0 +1,127 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsDOMStorageManager_h__
|
||||
#define nsDOMStorageManager_h__
|
||||
|
||||
#include "nsIDOMStorageManager.h"
|
||||
#include "DOMStorageObserver.h"
|
||||
|
||||
#include "nsPIDOMStorage.h"
|
||||
#include "DOMStorageCache.h"
|
||||
|
||||
#include "nsTHashtable.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
const nsPIDOMStorage::StorageType SessionStorage = nsPIDOMStorage::SessionStorage;
|
||||
const nsPIDOMStorage::StorageType LocalStorage = nsPIDOMStorage::LocalStorage;
|
||||
|
||||
class DOMStorage;
|
||||
|
||||
class DOMStorageManager : public nsIDOMStorageManager
|
||||
, public DOMStorageObserverSink
|
||||
{
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMSTORAGEMANAGER
|
||||
|
||||
public:
|
||||
virtual nsPIDOMStorage::StorageType Type() { return mType; }
|
||||
|
||||
// Reads the preference for DOM storage quota
|
||||
static uint32_t GetQuota();
|
||||
// Gets (but not ensures) cache for the given scope
|
||||
DOMStorageCache* GetCache(const nsACString& aScope) const;
|
||||
|
||||
protected:
|
||||
DOMStorageManager(nsPIDOMStorage::StorageType aType);
|
||||
virtual ~DOMStorageManager();
|
||||
|
||||
private:
|
||||
// DOMStorageObserverSink, handler to various chrome clearing notification
|
||||
virtual nsresult Observe(const char* aTopic, const nsACString& aScopePrefix);
|
||||
|
||||
// Since nsTHashtable doesn't like multiple inheritance, we have to aggregate
|
||||
// DOMStorageCache into the entry.
|
||||
class DOMStorageCacheHashKey : public nsCStringHashKey
|
||||
{
|
||||
public:
|
||||
DOMStorageCacheHashKey(const nsACString* aKey)
|
||||
: nsCStringHashKey(aKey)
|
||||
, mCache(new DOMStorageCache(aKey))
|
||||
{}
|
||||
|
||||
DOMStorageCacheHashKey(const DOMStorageCacheHashKey& aOther)
|
||||
: nsCStringHashKey(aOther)
|
||||
{
|
||||
NS_ERROR("Shouldn't be called");
|
||||
}
|
||||
|
||||
DOMStorageCache* cache() { return mCache; }
|
||||
// Keep the cache referenced forever, used for sessionStorage.
|
||||
void HardRef() { mCacheRef = mCache; }
|
||||
|
||||
private:
|
||||
// weak ref only since cache references its manager.
|
||||
DOMStorageCache* mCache;
|
||||
// hard ref when this is sessionStorage to keep it alive forever.
|
||||
nsRefPtr<DOMStorageCache> mCacheRef;
|
||||
};
|
||||
|
||||
// Ensures cache for a scope, when it doesn't exist it is created and initalized,
|
||||
// this also starts preload of persistent data.
|
||||
DOMStorageCache* PutCache(const nsACString& aScope,
|
||||
nsIPrincipal* aPrincipal);
|
||||
|
||||
// Helper for creation of DOM storage objects
|
||||
nsresult GetStorageInternal(bool aCreate,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsAString& aDocumentURI,
|
||||
bool aPrivate,
|
||||
nsIDOMStorage** aRetval);
|
||||
|
||||
// Scope->cache map
|
||||
nsTHashtable<DOMStorageCacheHashKey> mCaches;
|
||||
const nsPIDOMStorage::StorageType mType;
|
||||
|
||||
static PLDHashOperator ClearCacheEnumerator(DOMStorageCacheHashKey* aCache,
|
||||
void* aClosure);
|
||||
|
||||
protected:
|
||||
friend class DOMStorageCache;
|
||||
// Releases cache since it is no longer referrered by any DOMStorage object.
|
||||
virtual void DropCache(DOMStorageCache* aCache);
|
||||
};
|
||||
|
||||
// Derived classes to allow two different contract ids, one for localStorage and
|
||||
// one for sessionStorage management. localStorage manager is used as service
|
||||
// scoped to the application while sessionStorage managers are instantiated by each
|
||||
// top doc shell in the application since sessionStorages are isolated per top level
|
||||
// browsing context. The code may easily by shared by both.
|
||||
|
||||
class DOMLocalStorageManager MOZ_FINAL : public DOMStorageManager
|
||||
{
|
||||
public:
|
||||
DOMLocalStorageManager();
|
||||
virtual ~DOMLocalStorageManager();
|
||||
|
||||
// Global getter of localStorage manager service
|
||||
static DOMLocalStorageManager* Self() { return sSelf; }
|
||||
|
||||
private:
|
||||
static DOMLocalStorageManager* sSelf;
|
||||
};
|
||||
|
||||
class DOMSessionStorageManager MOZ_FINAL : public DOMStorageManager
|
||||
{
|
||||
public:
|
||||
DOMSessionStorageManager();
|
||||
};
|
||||
|
||||
} // ::dom
|
||||
} // ::mozilla
|
||||
|
||||
#endif /* nsDOMStorageManager_h__ */
|
|
@ -0,0 +1,332 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "DOMStorageObserver.h"
|
||||
|
||||
#include "DOMStorageDBThread.h"
|
||||
#include "DOMStorageCache.h"
|
||||
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIURL.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIPermission.h"
|
||||
#include "nsIIDNService.h"
|
||||
#include "mozIApplicationClearPrivateDataParams.h"
|
||||
#include "nsICookiePermission.h"
|
||||
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "nsEscape.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
static const char kStartupTopic[] = "sessionstore-windows-restored";
|
||||
static const uint32_t kStartupDelay = 0;
|
||||
|
||||
NS_IMPL_ISUPPORTS2(DOMStorageObserver,
|
||||
nsIObserver,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
DOMStorageObserver* DOMStorageObserver::sSelf = nullptr;
|
||||
|
||||
extern nsresult
|
||||
CreateReversedDomain(const nsACString& aAsciiDomain, nsACString& aKey);
|
||||
|
||||
// static
|
||||
nsresult
|
||||
DOMStorageObserver::Init()
|
||||
{
|
||||
if (sSelf) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (!obs) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
sSelf = new DOMStorageObserver();
|
||||
NS_ADDREF(sSelf);
|
||||
|
||||
// Chrome clear operations.
|
||||
obs->AddObserver(sSelf, kStartupTopic, true);
|
||||
obs->AddObserver(sSelf, "cookie-changed", true);
|
||||
obs->AddObserver(sSelf, "perm-changed", true);
|
||||
obs->AddObserver(sSelf, "browser:purge-domain-data", true);
|
||||
obs->AddObserver(sSelf, "last-pb-context-exited", true);
|
||||
obs->AddObserver(sSelf, "webapps-clear-data", true);
|
||||
|
||||
// Shutdown
|
||||
obs->AddObserver(sSelf, "profile-after-change", true);
|
||||
obs->AddObserver(sSelf, "profile-before-change", true);
|
||||
obs->AddObserver(sSelf, "xpcom-shutdown", true);
|
||||
|
||||
#ifdef DOM_STORAGE_TESTS
|
||||
// Testing
|
||||
obs->AddObserver(sSelf, "domstorage-test-flush-force", true);
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Default) {
|
||||
// Only to forward to child process.
|
||||
obs->AddObserver(sSelf, "domstorage-test-flushed", true);
|
||||
}
|
||||
|
||||
obs->AddObserver(sSelf, "domstorage-test-reload", true);
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
DOMStorageObserver::Shutdown()
|
||||
{
|
||||
if (!sSelf) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
NS_RELEASE(sSelf);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DOMStorageObserver::AddSink(DOMStorageObserverSink* aObs)
|
||||
{
|
||||
mSinks.AppendElement(aObs);
|
||||
}
|
||||
|
||||
void
|
||||
DOMStorageObserver::RemoveSink(DOMStorageObserverSink* aObs)
|
||||
{
|
||||
mSinks.RemoveElement(aObs);
|
||||
}
|
||||
|
||||
void
|
||||
DOMStorageObserver::Notify(const char* aTopic, const nsACString& aData)
|
||||
{
|
||||
for (uint32_t i = 0; i < mSinks.Length(); ++i) {
|
||||
DOMStorageObserverSink* sink = mSinks[i];
|
||||
sink->Observe(aTopic, aData);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMStorageObserver::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const PRUnichar* aData)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Start the thread that opens the database.
|
||||
if (!strcmp(aTopic, kStartupTopic)) {
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
obs->RemoveObserver(this, kStartupTopic);
|
||||
|
||||
mDBThreadStartDelayTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
if (!mDBThreadStartDelayTimer) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
mDBThreadStartDelayTimer->Init(this, nsITimer::TYPE_ONE_SHOT, kStartupDelay);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Timer callback used to start the database a short timer after startup
|
||||
if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
|
||||
nsCOMPtr<nsITimer> timer = do_QueryInterface(aSubject);
|
||||
if (!timer) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (timer == mDBThreadStartDelayTimer) {
|
||||
mDBThreadStartDelayTimer = nullptr;
|
||||
|
||||
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
|
||||
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Clear everything, caches + database
|
||||
if (!strcmp(aTopic, "cookie-changed")) {
|
||||
if (!NS_LITERAL_STRING("cleared").Equals(aData)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
|
||||
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
|
||||
|
||||
db->AsyncClearAll();
|
||||
|
||||
Notify("cookie-cleared");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Clear from caches everything that has been stored
|
||||
// while in session-only mode
|
||||
if (!strcmp(aTopic, "perm-changed")) {
|
||||
// Check for cookie permission change
|
||||
nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject));
|
||||
if (!perm) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoCString type;
|
||||
perm->GetType(type);
|
||||
if (type != NS_LITERAL_CSTRING("cookie")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t cap = 0;
|
||||
perm->GetCapability(&cap);
|
||||
if (!(cap & nsICookiePermission::ACCESS_SESSION) ||
|
||||
!NS_LITERAL_STRING("deleted").Equals(nsDependentString(aData))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoCString host;
|
||||
perm->GetHost(host);
|
||||
if (host.IsEmpty()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoCString scope;
|
||||
rv = CreateReversedDomain(host, scope);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
Notify("session-only-cleared", scope);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Clear everything (including so and pb data) from caches and database
|
||||
// for the gived domain and subdomains.
|
||||
if (!strcmp(aTopic, "browser:purge-domain-data")) {
|
||||
// Convert the domain name to the ACE format
|
||||
nsAutoCString aceDomain;
|
||||
nsCOMPtr<nsIIDNService> converter = do_GetService(NS_IDNSERVICE_CONTRACTID);
|
||||
if (converter) {
|
||||
rv = converter->ConvertUTF8toACE(NS_ConvertUTF16toUTF8(aData), aceDomain);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
// In case the IDN service is not available, this is the best we can come up with!
|
||||
NS_EscapeURL(NS_ConvertUTF16toUTF8(aData),
|
||||
esc_OnlyNonASCII | esc_AlwaysCopy,
|
||||
aceDomain);
|
||||
}
|
||||
|
||||
nsAutoCString scopePrefix;
|
||||
rv = CreateReversedDomain(aceDomain, scopePrefix);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
|
||||
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
|
||||
|
||||
db->AsyncClearMatchingScope(scopePrefix);
|
||||
|
||||
Notify("domain-data-cleared", scopePrefix);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Clear all private-browsing caches
|
||||
if (!strcmp(aTopic, "last-pb-context-exited")) {
|
||||
Notify("private-browsing-data-cleared");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Clear data beloging to an app.
|
||||
if (!strcmp(aTopic, "webapps-clear-data")) {
|
||||
nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
|
||||
do_QueryInterface(aSubject);
|
||||
if (!params) {
|
||||
NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
uint32_t appId;
|
||||
bool browserOnly;
|
||||
|
||||
rv = params->GetAppId(&appId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = params->GetBrowserOnly(&browserOnly);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
|
||||
|
||||
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
|
||||
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
|
||||
|
||||
nsAutoCString scope;
|
||||
scope.AppendInt(appId);
|
||||
scope.Append(NS_LITERAL_CSTRING(":t:"));
|
||||
db->AsyncClearMatchingScope(scope);
|
||||
Notify("app-data-cleared", scope);
|
||||
|
||||
if (!browserOnly) {
|
||||
scope.Truncate();
|
||||
scope.AppendInt(appId);
|
||||
scope.Append(NS_LITERAL_CSTRING(":f:"));
|
||||
db->AsyncClearMatchingScope(scope);
|
||||
Notify("app-data-cleared", scope);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, "profile-after-change")) {
|
||||
Notify("profile-change");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, "profile-before-change") ||
|
||||
!strcmp(aTopic, "xpcom-shutdown")) {
|
||||
rv = DOMStorageCache::StopDatabase();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Error while stopping DOMStorage DB background thread");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef DOM_STORAGE_TESTS
|
||||
if (!strcmp(aTopic, "domstorage-test-flush-force")) {
|
||||
DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
|
||||
if (db) {
|
||||
db->AsyncFlush();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, "domstorage-test-flushed")) {
|
||||
// Only used to propagate to IPC children
|
||||
Notify("test-flushed");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, "domstorage-test-reload")) {
|
||||
Notify("test-reload");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_ERROR("Unexpected topic");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
} // ::dom
|
||||
} // ::mozilla
|
|
@ -0,0 +1,62 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsIDOMStorageObserver_h__
|
||||
#define nsIDOMStorageObserver_h__
|
||||
|
||||
#include "nsIObserver.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class DOMStorageObserver;
|
||||
|
||||
// Implementers are DOMStorageManager and DOMStorageDBParent to forward to
|
||||
// child processes.
|
||||
class DOMStorageObserverSink
|
||||
{
|
||||
public:
|
||||
virtual ~DOMStorageObserverSink() {}
|
||||
|
||||
private:
|
||||
friend class DOMStorageObserver;
|
||||
virtual nsresult Observe(const char* aTopic, const nsACString& aScopePrefix) = 0;
|
||||
};
|
||||
|
||||
// Statically (though layout statics) initialized observer receiving and processing
|
||||
// chrome clearing notifications, such as cookie deletion etc.
|
||||
class DOMStorageObserver : public nsIObserver
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
virtual ~DOMStorageObserver() {}
|
||||
|
||||
static nsresult Init();
|
||||
static nsresult Shutdown();
|
||||
static DOMStorageObserver* Self() { return sSelf; }
|
||||
|
||||
void AddSink(DOMStorageObserverSink* aObs);
|
||||
void RemoveSink(DOMStorageObserverSink* aObs);
|
||||
void Notify(const char* aTopic, const nsACString& aData = EmptyCString());
|
||||
|
||||
private:
|
||||
static DOMStorageObserver* sSelf;
|
||||
|
||||
// Weak references
|
||||
nsTArray<DOMStorageObserverSink*> mSinks;
|
||||
nsCOMPtr<nsITimer> mDBThreadStartDelayTimer;
|
||||
};
|
||||
|
||||
} // ::dom
|
||||
} // ::mozilla
|
||||
|
||||
#endif
|
|
@ -15,18 +15,16 @@ LIBRARY_NAME = jsdomstorage_s
|
|||
LIBXUL_LIBRARY = 1
|
||||
|
||||
CPPSRCS = \
|
||||
nsDOMStorage.cpp \
|
||||
nsDOMStorageBaseDB.cpp \
|
||||
nsDOMStorageDBWrapper.cpp \
|
||||
nsLocalStorageCache.cpp \
|
||||
nsDOMStoragePersistentDB.cpp \
|
||||
nsDOMStorageMemoryDB.cpp \
|
||||
StorageChild.cpp \
|
||||
StorageParent.cpp \
|
||||
DOMStorage.cpp \
|
||||
DOMStorageCache.cpp \
|
||||
DOMStorageDBThread.cpp \
|
||||
DOMStorageObserver.cpp \
|
||||
DOMStorageManager.cpp \
|
||||
DOMStorageIPC.cpp \
|
||||
$(NULL)
|
||||
|
||||
EXPORTS_NAMESPACES = mozilla/dom
|
||||
EXPORTS_mozilla/dom = StorageChild.h StorageParent.h
|
||||
EXPORTS_mozilla/dom = DOMStorageIPC.h
|
||||
|
||||
# we don't want the shared lib, but we want to force the creation of a static lib.
|
||||
FORCE_STATIC_LIB = 1
|
||||
|
@ -37,6 +35,10 @@ LOCAL_INCLUDES = \
|
|||
|
||||
DEFINES += -D_IMPL_NS_LAYOUT
|
||||
|
||||
ifdef ENABLE_TESTS
|
||||
DEFINES += -DDOM_STORAGE_TESTS
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/config.mk
|
||||
include $(topsrcdir)/ipc/chromium/chromium-config.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
|
|
@ -6,61 +6,37 @@
|
|||
|
||||
include protocol PContent;
|
||||
|
||||
using mozilla::null_t;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct ItemData
|
||||
{
|
||||
nsString value;
|
||||
bool secure;
|
||||
};
|
||||
|
||||
// A cross-process GetValue result -- either null, or containing the parameters
|
||||
// with which to initialize an nsIDOMStorageItem.
|
||||
union StorageItem
|
||||
{
|
||||
null_t;
|
||||
ItemData;
|
||||
};
|
||||
|
||||
// This protocol is little more than a thin wrapper around the DOMStorageBase
|
||||
// class in nsDOMStorage.h. The child implementation simply forwards the
|
||||
// arguments for any given call to the parent, and returns the result.
|
||||
/* This protocol bridges async access to the database thread running on the parent process
|
||||
* and caches running on the child process.
|
||||
*/
|
||||
sync protocol PStorage
|
||||
{
|
||||
manager PContent;
|
||||
|
||||
parent:
|
||||
__delete__();
|
||||
async __delete__();
|
||||
|
||||
Init(bool useDB, bool sessionOnly, bool isPrivate,
|
||||
nsCString scopeDBKey, nsCString quotaDBKey, uint32_t storageType);
|
||||
sync Preload(nsCString scope, uint32_t alreadyLoadedCount)
|
||||
returns (nsString[] keys, nsString[] values, nsresult rv);
|
||||
|
||||
async AsyncPreload(nsCString scope, bool priority);
|
||||
async AsyncGetUsage(nsCString scope);
|
||||
async AsyncAddItem(nsCString scope, nsString key, nsString value);
|
||||
async AsyncUpdateItem(nsCString scope, nsString key, nsString value);
|
||||
async AsyncRemoveItem(nsCString scope, nsString key);
|
||||
async AsyncClear(nsCString scope);
|
||||
async AsyncFlush();
|
||||
|
||||
sync GetKeys(bool callerSecure)
|
||||
returns (nsString[] keys);
|
||||
sync GetLength(bool callerSecure, bool sessionOnly)
|
||||
returns (uint32_t length, nsresult rv);
|
||||
sync GetKey(bool callerSecure, bool sessionOnly, uint32_t index)
|
||||
returns (nsString key, nsresult rv);
|
||||
sync GetValue(bool callerSecure, bool sessionOnly, nsString key)
|
||||
returns (StorageItem item, nsresult rv);
|
||||
sync SetValue(bool callerSecure, bool sessionOnly, nsString key, nsString data)
|
||||
returns (nsString oldValue, nsresult rv);
|
||||
sync RemoveValue(bool callerSecure, bool sessionOnly, nsString key)
|
||||
returns (nsString oldValue, nsresult rv);
|
||||
sync Clear(bool callerSecure, bool sessionOnly)
|
||||
returns (int32_t oldCount, nsresult rv);
|
||||
|
||||
sync GetDBValue(nsString key)
|
||||
returns (nsString value, bool secure, nsresult rv);
|
||||
sync SetDBValue(nsString key, nsString value, bool secure)
|
||||
returns (nsresult rv);
|
||||
sync SetSecure(nsString key, bool secure)
|
||||
returns (nsresult rv);
|
||||
|
||||
UpdatePrivateState(bool enabled);
|
||||
child:
|
||||
async Observe(nsCString topic, nsCString scopePrefix);
|
||||
async ScopesHavingData(nsCString[] scopes);
|
||||
async LoadItem(nsCString scope, nsString key, nsString value);
|
||||
async LoadDone(nsCString scope, nsresult rv);
|
||||
async LoadUsage(nsCString scope, int64_t usage);
|
||||
async Error(nsresult rv);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,250 +0,0 @@
|
|||
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "StorageChild.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "nsError.h"
|
||||
|
||||
#include "GeckoProfiler.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_1(StorageChild, mStorage)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(StorageChild)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StorageChild)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIPrivacyTransitionObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPrivacyTransitionObserver)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMETHODIMP_(nsrefcnt) StorageChild::Release(void)
|
||||
{
|
||||
NS_PRECONDITION(0 != mRefCnt, "dup release");
|
||||
NS_ASSERT_OWNINGTHREAD(StorageChild);
|
||||
nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(StorageChild)::Upcast(this);
|
||||
nsrefcnt count = mRefCnt.decr(base);
|
||||
NS_LOG_RELEASE(this, count, "StorageChild");
|
||||
if (count == 1 && mIPCOpen) {
|
||||
Send__delete__(this);
|
||||
return 0;
|
||||
}
|
||||
if (count == 0) {
|
||||
mRefCnt.stabilizeForDeletion();
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
StorageChild::StorageChild(nsDOMStorage* aOwner)
|
||||
: mStorage(aOwner)
|
||||
, mIPCOpen(false)
|
||||
{
|
||||
}
|
||||
|
||||
StorageChild::StorageChild(nsDOMStorage* aOwner, StorageChild& aOther)
|
||||
: DOMStorageBase(aOther)
|
||||
, mStorage(aOwner)
|
||||
, mIPCOpen(false)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
StorageChild::AddIPDLReference()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(!mIPCOpen, "Attempting to retain multiple IPDL references");
|
||||
mIPCOpen = true;
|
||||
AddRef();
|
||||
}
|
||||
|
||||
void
|
||||
StorageChild::ReleaseIPDLReference()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mIPCOpen, "Attempting to release non-existent IPDL reference");
|
||||
mIPCOpen = false;
|
||||
Release();
|
||||
}
|
||||
|
||||
bool
|
||||
StorageChild::CacheStoragePermissions()
|
||||
{
|
||||
nsDOMStorage* storage = static_cast<nsDOMStorage*>(mStorage.get());
|
||||
return storage->CacheStoragePermissions();
|
||||
}
|
||||
|
||||
void
|
||||
StorageChild::InitRemote()
|
||||
{
|
||||
ContentChild* child = ContentChild::GetSingleton();
|
||||
AddIPDLReference();
|
||||
child->SendPStorageConstructor(this, null_t());
|
||||
SendInit(mUseDB, mSessionOnly, mInPrivateBrowsing, mScopeDBKey,
|
||||
mQuotaDBKey, mStorageType);
|
||||
}
|
||||
|
||||
void
|
||||
StorageChild::InitAsSessionStorage(nsIPrincipal* aPrincipal, bool aPrivate)
|
||||
{
|
||||
DOMStorageBase::InitAsSessionStorage(aPrincipal, aPrivate);
|
||||
InitRemote();
|
||||
}
|
||||
|
||||
void
|
||||
StorageChild::InitAsLocalStorage(nsIPrincipal* aPrincipal, bool aPrivate)
|
||||
{
|
||||
DOMStorageBase::InitAsLocalStorage(aPrincipal, aPrivate);
|
||||
InitRemote();
|
||||
}
|
||||
|
||||
nsTArray<nsString>*
|
||||
StorageChild::GetKeys(bool aCallerSecure)
|
||||
{
|
||||
InfallibleTArray<nsString> remoteKeys;
|
||||
SendGetKeys(aCallerSecure, &remoteKeys);
|
||||
nsTArray<nsString>* keys = new nsTArray<nsString>;
|
||||
*keys = remoteKeys;
|
||||
return keys;
|
||||
}
|
||||
|
||||
nsresult
|
||||
StorageChild::GetLength(bool aCallerSecure, uint32_t* aLength)
|
||||
{
|
||||
nsresult rv;
|
||||
SendGetLength(aCallerSecure, mSessionOnly, aLength, &rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
StorageChild::GetKey(bool aCallerSecure, uint32_t aIndex, nsAString& aKey)
|
||||
{
|
||||
nsresult rv;
|
||||
nsString key;
|
||||
SendGetKey(aCallerSecure, mSessionOnly, aIndex, &key, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
aKey = key;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Unlike other cross-process forwarding methods, GetValue needs to replicate
|
||||
// the following behaviour of DOMStorageImpl::GetValue:
|
||||
//
|
||||
// - if a security error occurs, or the item isn't found, return null without
|
||||
// propogating the error.
|
||||
//
|
||||
// If DOMStorageImpl::GetValue ever changes its behaviour, this should be kept
|
||||
// in sync.
|
||||
nsIDOMStorageItem*
|
||||
StorageChild::GetValue(bool aCallerSecure, const nsAString& aKey, nsresult* rv)
|
||||
{
|
||||
PROFILER_LABEL("StorageChild", "GetValue");
|
||||
nsresult rv2 = *rv = NS_OK;
|
||||
StorageItem storageItem;
|
||||
SendGetValue(aCallerSecure, mSessionOnly, nsString(aKey), &storageItem, &rv2);
|
||||
if (rv2 == NS_ERROR_DOM_SECURITY_ERR || rv2 == NS_ERROR_DOM_NOT_FOUND_ERR)
|
||||
return nullptr;
|
||||
*rv = rv2;
|
||||
if (NS_FAILED(*rv) || storageItem.type() == StorageItem::Tnull_t)
|
||||
return nullptr;
|
||||
const ItemData& data = storageItem.get_ItemData();
|
||||
nsIDOMStorageItem* item = new nsDOMStorageItem(this, aKey, data.value(),
|
||||
data.secure());
|
||||
return item;
|
||||
}
|
||||
|
||||
nsresult
|
||||
StorageChild::SetValue(bool aCallerSecure, const nsAString& aKey,
|
||||
const nsAString& aData, nsAString& aOldData)
|
||||
{
|
||||
nsresult rv;
|
||||
nsString oldData;
|
||||
SendSetValue(aCallerSecure, mSessionOnly, nsString(aKey), nsString(aData),
|
||||
&oldData, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
aOldData = oldData;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
StorageChild::RemoveValue(bool aCallerSecure, const nsAString& aKey,
|
||||
nsAString& aOldData)
|
||||
{
|
||||
nsresult rv;
|
||||
nsString oldData;
|
||||
SendRemoveValue(aCallerSecure, mSessionOnly, nsString(aKey), &oldData, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
aOldData = oldData;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
StorageChild::Clear(bool aCallerSecure, int32_t* aOldCount)
|
||||
{
|
||||
nsresult rv;
|
||||
int32_t oldCount;
|
||||
SendClear(aCallerSecure, mSessionOnly, &oldCount, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
*aOldCount = oldCount;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
StorageChild::GetDBValue(const nsAString& aKey, nsAString& aValue,
|
||||
bool* aSecure)
|
||||
{
|
||||
nsresult rv;
|
||||
nsString value;
|
||||
SendGetDBValue(nsString(aKey), &value, aSecure, &rv);
|
||||
aValue = value;
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
StorageChild::SetDBValue(const nsAString& aKey,
|
||||
const nsAString& aValue,
|
||||
bool aSecure)
|
||||
{
|
||||
nsresult rv;
|
||||
SendSetDBValue(nsString(aKey), nsString(aValue), aSecure, &rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
StorageChild::SetSecure(const nsAString& aKey, bool aSecure)
|
||||
{
|
||||
nsresult rv;
|
||||
SendSetSecure(nsString(aKey), aSecure, &rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
StorageChild::CloneFrom(bool aCallerSecure, DOMStorageBase* aThat)
|
||||
{
|
||||
StorageChild* other = static_cast<StorageChild*>(aThat);
|
||||
ContentChild* child = ContentChild::GetSingleton();
|
||||
StorageClone clone(nullptr, other, aCallerSecure);
|
||||
AddIPDLReference();
|
||||
child->SendPStorageConstructor(this, clone);
|
||||
SendInit(mUseDB, mSessionOnly, mInPrivateBrowsing,
|
||||
mScopeDBKey, mQuotaDBKey, mStorageType);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StorageChild::PrivateModeChanged(bool enabled)
|
||||
{
|
||||
mInPrivateBrowsing = enabled;
|
||||
SendUpdatePrivateState(enabled);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_StorageChild_h
|
||||
#define mozilla_dom_StorageChild_h
|
||||
|
||||
#include "mozilla/dom/PStorageChild.h"
|
||||
#include "nsDOMStorage.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class StorageChild : public PStorageChild
|
||||
, public DOMStorageBase
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(StorageChild, nsIPrivacyTransitionObserver)
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_NSIPRIVACYTRANSITIONOBSERVER
|
||||
|
||||
StorageChild(nsDOMStorage* aOwner);
|
||||
StorageChild(nsDOMStorage* aOwner, StorageChild& aOther);
|
||||
|
||||
virtual void InitAsSessionStorage(nsIPrincipal* aPrincipal, bool aPrivate);
|
||||
virtual void InitAsLocalStorage(nsIPrincipal* aPrincipal, bool aPrivate);
|
||||
|
||||
virtual bool CacheStoragePermissions();
|
||||
|
||||
virtual nsTArray<nsString>* GetKeys(bool aCallerSecure);
|
||||
virtual nsresult GetLength(bool aCallerSecure, uint32_t* aLength);
|
||||
virtual nsresult GetKey(bool aCallerSecure, uint32_t aIndex, nsAString& aKey);
|
||||
virtual nsIDOMStorageItem* GetValue(bool aCallerSecure, const nsAString& aKey,
|
||||
nsresult* rv);
|
||||
virtual nsresult SetValue(bool aCallerSecure, const nsAString& aKey,
|
||||
const nsAString& aData, nsAString& aOldValue);
|
||||
virtual nsresult RemoveValue(bool aCallerSecure, const nsAString& aKey,
|
||||
nsAString& aOldValue);
|
||||
virtual nsresult Clear(bool aCallerSecure, int32_t* aOldCount);
|
||||
|
||||
virtual nsresult GetDBValue(const nsAString& aKey,
|
||||
nsAString& aValue,
|
||||
bool* aSecure);
|
||||
virtual nsresult SetDBValue(const nsAString& aKey,
|
||||
const nsAString& aValue,
|
||||
bool aSecure);
|
||||
virtual nsresult SetSecure(const nsAString& aKey, bool aSecure);
|
||||
|
||||
virtual nsresult CloneFrom(bool aCallerSecure, DOMStorageBase* aThat);
|
||||
|
||||
void AddIPDLReference();
|
||||
void ReleaseIPDLReference();
|
||||
|
||||
private:
|
||||
void InitRemote();
|
||||
|
||||
// Unimplemented
|
||||
StorageChild(const StorageChild&);
|
||||
|
||||
nsCOMPtr<nsIDOMStorageObsolete> mStorage;
|
||||
bool mIPCOpen;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,157 +0,0 @@
|
|||
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "StorageParent.h"
|
||||
#include "mozilla/dom/PContentParent.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "nsDOMString.h"
|
||||
|
||||
using mozilla::unused;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
StorageParent::StorageParent(const StorageConstructData& aData)
|
||||
{
|
||||
if (aData.type() == StorageConstructData::Tnull_t) {
|
||||
mStorage = new DOMStorageImpl(nullptr);
|
||||
} else {
|
||||
const StorageClone& clone = aData.get_StorageClone();
|
||||
StorageParent* other = static_cast<StorageParent*>(clone.actorParent());
|
||||
mStorage = new DOMStorageImpl(nullptr, *other->mStorage.get());
|
||||
mStorage->CloneFrom(clone.callerSecure(), other->mStorage);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
StorageParent::RecvInit(const bool& aUseDB,
|
||||
const bool& aSessionOnly,
|
||||
const bool& aPrivate,
|
||||
const nsCString& aScopeDBKey,
|
||||
const nsCString& aQuotaDBKey,
|
||||
const uint32_t& aStorageType)
|
||||
{
|
||||
mStorage->InitFromChild(aUseDB, aSessionOnly, aPrivate,
|
||||
aScopeDBKey, aQuotaDBKey,
|
||||
aStorageType);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
StorageParent::RecvUpdatePrivateState(const bool& aEnabled)
|
||||
{
|
||||
mStorage->PrivateModeChanged(aEnabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
StorageParent::RecvGetKeys(const bool& aCallerSecure, InfallibleTArray<nsString>* aKeys)
|
||||
{
|
||||
// Callers are responsible for deallocating the array returned by mStorage->GetKeys
|
||||
nsAutoPtr<nsTArray<nsString> > keys(mStorage->GetKeys(aCallerSecure));
|
||||
aKeys->SwapElements(*keys);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
StorageParent::RecvGetLength(const bool& aCallerSecure, const bool& aSessionOnly,
|
||||
uint32_t* aLength, nsresult* rv)
|
||||
{
|
||||
mStorage->SetSessionOnly(aSessionOnly);
|
||||
*rv = mStorage->GetLength(aCallerSecure, aLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
StorageParent::RecvGetKey(const bool& aCallerSecure, const bool& aSessionOnly,
|
||||
const uint32_t& aIndex, nsString* aKey, nsresult* rv)
|
||||
{
|
||||
mStorage->SetSessionOnly(aSessionOnly);
|
||||
*rv = mStorage->GetKey(aCallerSecure, aIndex, *aKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
StorageParent::RecvGetValue(const bool& aCallerSecure, const bool& aSessionOnly,
|
||||
const nsString& aKey, StorageItem* aItem,
|
||||
nsresult* rv)
|
||||
{
|
||||
mStorage->SetSessionOnly(aSessionOnly);
|
||||
|
||||
// We need to ensure that a proper null representation is sent to the child
|
||||
// if no item is found or an error occurs.
|
||||
|
||||
*rv = NS_OK;
|
||||
nsCOMPtr<nsIDOMStorageItem> item = mStorage->GetValue(aCallerSecure, aKey, rv);
|
||||
if (NS_FAILED(*rv) || !item) {
|
||||
*aItem = null_t();
|
||||
return true;
|
||||
}
|
||||
|
||||
ItemData data(EmptyString(), false);
|
||||
nsDOMStorageItem* internalItem = static_cast<nsDOMStorageItem*>(item.get());
|
||||
data.value() = internalItem->GetValueInternal();
|
||||
if (aCallerSecure)
|
||||
data.secure() = internalItem->IsSecure();
|
||||
*aItem = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
StorageParent::RecvSetValue(const bool& aCallerSecure, const bool& aSessionOnly,
|
||||
const nsString& aKey, const nsString& aData,
|
||||
nsString* aOldValue, nsresult* rv)
|
||||
{
|
||||
mStorage->SetSessionOnly(aSessionOnly);
|
||||
*rv = mStorage->SetValue(aCallerSecure, aKey, aData, *aOldValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
StorageParent::RecvRemoveValue(const bool& aCallerSecure, const bool& aSessionOnly,
|
||||
const nsString& aKey, nsString* aOldValue,
|
||||
nsresult* rv)
|
||||
{
|
||||
mStorage->SetSessionOnly(aSessionOnly);
|
||||
*rv = mStorage->RemoveValue(aCallerSecure, aKey, *aOldValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
StorageParent::RecvClear(const bool& aCallerSecure, const bool& aSessionOnly,
|
||||
int32_t* aOldCount, nsresult* rv)
|
||||
{
|
||||
mStorage->SetSessionOnly(aSessionOnly);
|
||||
*rv = mStorage->Clear(aCallerSecure, aOldCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
StorageParent::RecvGetDBValue(const nsString& aKey, nsString* aValue,
|
||||
bool* aSecure, nsresult* rv)
|
||||
{
|
||||
*rv = mStorage->GetDBValue(aKey, *aValue, aSecure);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
StorageParent::RecvSetDBValue(const nsString& aKey, const nsString& aValue,
|
||||
const bool& aSecure, nsresult* rv)
|
||||
{
|
||||
*rv = mStorage->SetDBValue(aKey, aValue, aSecure);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
StorageParent::RecvSetSecure(const nsString& aKey, const bool& aSecure,
|
||||
nsresult* rv)
|
||||
{
|
||||
*rv = mStorage->SetSecure(aKey, aSecure);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_StorageParent_h
|
||||
#define mozilla_dom_StorageParent_h
|
||||
|
||||
#include "mozilla/dom/PStorageParent.h"
|
||||
#include "nsDOMStorage.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class StorageConstructData;
|
||||
|
||||
class StorageParent : public PStorageParent
|
||||
{
|
||||
public:
|
||||
StorageParent(const StorageConstructData& aData);
|
||||
|
||||
private:
|
||||
bool RecvGetKeys(const bool& aCallerSecure, InfallibleTArray<nsString>* aKeys);
|
||||
bool RecvGetLength(const bool& aCallerSecure, const bool& aSessionOnly,
|
||||
uint32_t* aLength, nsresult* rv);
|
||||
bool RecvGetKey(const bool& aCallerSecure, const bool& aSessionOnly,
|
||||
const uint32_t& aIndex,nsString* aKey, nsresult* rv);
|
||||
bool RecvGetValue(const bool& aCallerSecure, const bool& aSessionOnly,
|
||||
const nsString& aKey, StorageItem* aItem, nsresult* rv);
|
||||
bool RecvSetValue(const bool& aCallerSecure, const bool& aSessionOnly,
|
||||
const nsString& aKey, const nsString& aData,
|
||||
nsString* aOldValue, nsresult* rv);
|
||||
bool RecvRemoveValue(const bool& aCallerSecure, const bool& aSessionOnly,
|
||||
const nsString& aKey, nsString* aOldData, nsresult* rv);
|
||||
bool RecvClear(const bool& aCallerSecure, const bool& aSessionOnly,
|
||||
int32_t* aOldCount, nsresult* rv);
|
||||
|
||||
bool RecvGetDBValue(const nsString& aKey, nsString* aValue, bool* aSecure,
|
||||
nsresult* rv);
|
||||
bool RecvSetDBValue(const nsString& aKey, const nsString& aValue,
|
||||
const bool& aSecure, nsresult* rv);
|
||||
bool RecvSetSecure(const nsString& aKey, const bool& aSecure, nsresult* rv);
|
||||
|
||||
bool RecvInit(const bool& aUseDB,
|
||||
const bool& aSessionOnly,
|
||||
const bool& aPrivate,
|
||||
const nsCString& aScopeDBKey,
|
||||
const nsCString& aQuotaDBKey,
|
||||
const uint32_t& aStorageType);
|
||||
|
||||
bool RecvUpdatePrivateState(const bool& aEnabled);
|
||||
|
||||
nsRefPtr<DOMStorageImpl> mStorage;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,486 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsDOMStorage_h___
|
||||
#define nsDOMStorage_h___
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIDOMStorageObsolete.h"
|
||||
#include "nsIDOMStorage.h"
|
||||
#include "nsIDOMStorageItem.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsIPrivacyTransitionObserver.h"
|
||||
#include "nsInterfaceHashtable.h"
|
||||
#include "nsVoidArray.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsPIDOMStorage.h"
|
||||
#include "nsIDOMToString.h"
|
||||
#include "nsDOMEvent.h"
|
||||
#include "nsIDOMStorageEvent.h"
|
||||
#include "nsIDOMStorageManager.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#include "nsDOMStorageDBWrapper.h"
|
||||
|
||||
class nsDOMStorage;
|
||||
class nsIDOMStorage;
|
||||
class nsDOMStorageItem;
|
||||
class nsDOMStoragePersistentDB;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class StorageParent;
|
||||
}
|
||||
}
|
||||
using mozilla::dom::StorageParent;
|
||||
|
||||
class DOMStorageImpl;
|
||||
|
||||
class nsDOMStorageEntry : public nsPtrHashKey<const void>
|
||||
{
|
||||
public:
|
||||
nsDOMStorageEntry(KeyTypePointer aStr);
|
||||
nsDOMStorageEntry(const nsDOMStorageEntry& aToCopy);
|
||||
~nsDOMStorageEntry();
|
||||
|
||||
// weak reference so that it can be deleted when no longer used
|
||||
DOMStorageImpl* mStorage;
|
||||
};
|
||||
|
||||
class nsSessionStorageEntry : public nsStringHashKey
|
||||
{
|
||||
public:
|
||||
nsSessionStorageEntry(KeyTypePointer aStr);
|
||||
nsSessionStorageEntry(const nsSessionStorageEntry& aToCopy);
|
||||
~nsSessionStorageEntry();
|
||||
|
||||
nsRefPtr<nsDOMStorageItem> mItem;
|
||||
};
|
||||
|
||||
class nsDOMStorageManager MOZ_FINAL : public nsIDOMStorageManager
|
||||
, public nsIObserver
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
// nsISupports
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIDOMStorageManager
|
||||
NS_DECL_NSIDOMSTORAGEMANAGER
|
||||
|
||||
// nsIObserver
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
nsDOMStorageManager();
|
||||
|
||||
void AddToStoragesHash(DOMStorageImpl* aStorage);
|
||||
void RemoveFromStoragesHash(DOMStorageImpl* aStorage);
|
||||
|
||||
nsresult ClearAllStorages();
|
||||
|
||||
static nsresult Initialize();
|
||||
static nsDOMStorageManager* GetInstance();
|
||||
static void Shutdown();
|
||||
static void ShutdownDB();
|
||||
|
||||
/**
|
||||
* Checks whether there is any data waiting to be flushed from a temp table.
|
||||
*/
|
||||
bool UnflushedDataExists();
|
||||
|
||||
static nsDOMStorageManager* gStorageManager;
|
||||
|
||||
protected:
|
||||
|
||||
nsTHashtable<nsDOMStorageEntry> mStorages;
|
||||
};
|
||||
|
||||
class DOMStorageBase : public nsIPrivacyTransitionObserver
|
||||
{
|
||||
public:
|
||||
DOMStorageBase();
|
||||
DOMStorageBase(DOMStorageBase&);
|
||||
|
||||
virtual void InitAsSessionStorage(nsIPrincipal* aPrincipal, bool aPrivate);
|
||||
virtual void InitAsLocalStorage(nsIPrincipal* aPrincipal, bool aPrivate);
|
||||
|
||||
virtual nsTArray<nsString>* GetKeys(bool aCallerSecure) = 0;
|
||||
virtual nsresult GetLength(bool aCallerSecure, uint32_t* aLength) = 0;
|
||||
virtual nsresult GetKey(bool aCallerSecure, uint32_t aIndex, nsAString& aKey) = 0;
|
||||
virtual nsIDOMStorageItem* GetValue(bool aCallerSecure, const nsAString& aKey,
|
||||
nsresult* rv) = 0;
|
||||
virtual nsresult SetValue(bool aCallerSecure, const nsAString& aKey,
|
||||
const nsAString& aData, nsAString& aOldValue) = 0;
|
||||
virtual nsresult RemoveValue(bool aCallerSecure, const nsAString& aKey,
|
||||
nsAString& aOldValue) = 0;
|
||||
virtual nsresult Clear(bool aCallerSecure, int32_t* aOldCount) = 0;
|
||||
|
||||
// Call nsDOMStorage::CanUseStorage with |this|
|
||||
bool CanUseStorage();
|
||||
|
||||
// If true, the contents of the storage should be stored in the
|
||||
// database, otherwise this storage should act like a session
|
||||
// storage.
|
||||
// This call relies on mSessionOnly, and should only be used
|
||||
// after a CacheStoragePermissions() call. See the comments
|
||||
// for mSessionOnly below.
|
||||
bool UseDB() {
|
||||
return mUseDB;
|
||||
}
|
||||
|
||||
bool IsPrivate() {
|
||||
return mInPrivateBrowsing;
|
||||
}
|
||||
|
||||
// retrieve the value and secure state corresponding to a key out of storage.
|
||||
virtual nsresult
|
||||
GetDBValue(const nsAString& aKey,
|
||||
nsAString& aValue,
|
||||
bool* aSecure) = 0;
|
||||
|
||||
// set the value corresponding to a key in the storage. If
|
||||
// aSecure is false, then attempts to modify a secure value
|
||||
// throw NS_ERROR_DOM_INVALID_ACCESS_ERR
|
||||
virtual nsresult
|
||||
SetDBValue(const nsAString& aKey,
|
||||
const nsAString& aValue,
|
||||
bool aSecure) = 0;
|
||||
|
||||
// set the value corresponding to a key as secure.
|
||||
virtual nsresult
|
||||
SetSecure(const nsAString& aKey, bool aSecure) = 0;
|
||||
|
||||
virtual nsresult
|
||||
CloneFrom(bool aCallerSecure, DOMStorageBase* aThat) = 0;
|
||||
|
||||
// e.g. "moc.rab.oof.:" or "moc.rab.oof.:http:80" depending
|
||||
// on association with a domain (globalStorage) or
|
||||
// an origin (localStorage).
|
||||
nsCString& GetScopeDBKey() {return mScopeDBKey;}
|
||||
|
||||
// e.g. "moc.rab.%" - reversed eTLD+1 subpart of the domain.
|
||||
nsCString& GetQuotaDBKey()
|
||||
{
|
||||
return mQuotaDBKey;
|
||||
}
|
||||
|
||||
virtual bool CacheStoragePermissions() = 0;
|
||||
|
||||
protected:
|
||||
friend class nsDOMStorageManager;
|
||||
friend class nsDOMStorage;
|
||||
|
||||
nsPIDOMStorage::nsDOMStorageType mStorageType;
|
||||
|
||||
// true if the storage database should be used for values
|
||||
bool mUseDB;
|
||||
|
||||
// true if the preferences indicates that this storage should be
|
||||
// session only. This member is updated by
|
||||
// CacheStoragePermissions(), using the current principal.
|
||||
// CacheStoragePermissions() must be called at each entry point to
|
||||
// make sure this stays up to date.
|
||||
bool mSessionOnly;
|
||||
|
||||
// keys are used for database queries.
|
||||
// see comments of the getters bellow.
|
||||
nsCString mScopeDBKey;
|
||||
nsCString mQuotaDBKey;
|
||||
|
||||
bool mInPrivateBrowsing;
|
||||
};
|
||||
|
||||
class DOMStorageImpl MOZ_FINAL : public DOMStorageBase
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DOMStorageImpl, nsIPrivacyTransitionObserver)
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_NSIPRIVACYTRANSITIONOBSERVER
|
||||
|
||||
DOMStorageImpl(nsDOMStorage*);
|
||||
DOMStorageImpl(nsDOMStorage*, DOMStorageImpl&);
|
||||
~DOMStorageImpl();
|
||||
|
||||
bool SessionOnly() {
|
||||
return mSessionOnly;
|
||||
}
|
||||
|
||||
virtual nsTArray<nsString>* GetKeys(bool aCallerSecure);
|
||||
virtual nsresult GetLength(bool aCallerSecure, uint32_t* aLength);
|
||||
virtual nsresult GetKey(bool aCallerSecure, uint32_t aIndex, nsAString& aKey);
|
||||
virtual nsIDOMStorageItem* GetValue(bool aCallerSecure, const nsAString& aKey,
|
||||
nsresult* rv);
|
||||
virtual nsresult SetValue(bool aCallerSecure, const nsAString& aKey,
|
||||
const nsAString& aData, nsAString& aOldValue);
|
||||
virtual nsresult RemoveValue(bool aCallerSecure, const nsAString& aKey,
|
||||
nsAString& aOldValue);
|
||||
virtual nsresult Clear(bool aCallerSecure, int32_t* aOldCount);
|
||||
|
||||
// cache the keys from the database for faster lookup
|
||||
nsresult CacheKeysFromDB();
|
||||
|
||||
uint64_t CachedVersion() { return mItemsCachedVersion; }
|
||||
void SetCachedVersion(uint64_t version) { mItemsCachedVersion = version; }
|
||||
|
||||
// retrieve the value and secure state corresponding to a key out of storage
|
||||
// that has been cached in mItems hash table.
|
||||
nsresult
|
||||
GetCachedValue(const nsAString& aKey,
|
||||
nsAString& aValue,
|
||||
bool* aSecure);
|
||||
|
||||
// retrieve the value and secure state corresponding to a key out of storage.
|
||||
virtual nsresult
|
||||
GetDBValue(const nsAString& aKey,
|
||||
nsAString& aValue,
|
||||
bool* aSecure);
|
||||
|
||||
// set the value corresponding to a key in the storage. If
|
||||
// aSecure is false, then attempts to modify a secure value
|
||||
// throw NS_ERROR_DOM_INVALID_ACCESS_ERR
|
||||
virtual nsresult
|
||||
SetDBValue(const nsAString& aKey,
|
||||
const nsAString& aValue,
|
||||
bool aSecure);
|
||||
|
||||
// set the value corresponding to a key as secure.
|
||||
virtual nsresult
|
||||
SetSecure(const nsAString& aKey, bool aSecure);
|
||||
|
||||
// clear all values from the store
|
||||
void ClearAll();
|
||||
|
||||
virtual nsresult
|
||||
CloneFrom(bool aCallerSecure, DOMStorageBase* aThat);
|
||||
|
||||
virtual bool CacheStoragePermissions();
|
||||
|
||||
private:
|
||||
static nsDOMStorageDBWrapper* gStorageDB;
|
||||
friend class nsDOMStorageManager;
|
||||
friend class nsDOMStoragePersistentDB;
|
||||
friend class StorageParent;
|
||||
|
||||
void Init(nsDOMStorage*);
|
||||
|
||||
// Cross-process storage implementations never have InitAs(Session|Local|Global)Storage
|
||||
// called, so the appropriate initialization needs to happen from the child.
|
||||
void InitFromChild(bool aUseDB, bool aSessionOnly,
|
||||
bool aPrivate,
|
||||
const nsACString& aScopeDBKey,
|
||||
const nsACString& aQuotaDBKey,
|
||||
uint32_t aStorageType);
|
||||
void SetSessionOnly(bool aSessionOnly);
|
||||
|
||||
static nsresult InitDB();
|
||||
|
||||
// 0 initially or a positive data version number assigned by gStorageDB
|
||||
// after keys have been cached from the database
|
||||
uint64_t mItemsCachedVersion;
|
||||
|
||||
// the key->value item pairs
|
||||
nsTHashtable<nsSessionStorageEntry> mItems;
|
||||
|
||||
// Weak reference to the owning storage instance
|
||||
nsDOMStorage* mOwner;
|
||||
};
|
||||
|
||||
class nsDOMStorage2;
|
||||
|
||||
class nsDOMStorage : public nsIDOMStorageObsolete,
|
||||
public nsPIDOMStorage,
|
||||
public nsIInterfaceRequestor
|
||||
{
|
||||
public:
|
||||
nsDOMStorage();
|
||||
nsDOMStorage(nsDOMStorage& aThat);
|
||||
virtual ~nsDOMStorage();
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMStorage, nsIDOMStorageObsolete)
|
||||
|
||||
NS_DECL_NSIDOMSTORAGEOBSOLETE
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
|
||||
// Helpers for implementing nsIDOMStorage
|
||||
nsresult GetItem(const nsAString& key, nsAString& aData);
|
||||
nsresult Clear();
|
||||
|
||||
// nsPIDOMStorage
|
||||
virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
|
||||
bool aPrivate);
|
||||
virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
|
||||
bool aPrivate);
|
||||
virtual already_AddRefed<nsIDOMStorage> Clone();
|
||||
virtual already_AddRefed<nsIDOMStorage> Fork(const nsSubstring &aDocumentURI);
|
||||
virtual bool IsForkOf(nsIDOMStorage* aThat);
|
||||
virtual nsTArray<nsString> *GetKeys();
|
||||
virtual nsIPrincipal* Principal();
|
||||
virtual bool CanAccess(nsIPrincipal *aPrincipal);
|
||||
virtual nsDOMStorageType StorageType();
|
||||
virtual bool IsPrivate() {
|
||||
return mStorageImpl && mStorageImpl->IsPrivate();
|
||||
}
|
||||
|
||||
// Check whether storage may be used by the caller, and whether it
|
||||
// is session only. Returns true if storage may be used.
|
||||
static bool
|
||||
CanUseStorage(DOMStorageBase* aStorage = nullptr);
|
||||
|
||||
// Check whether storage may be used. Updates mSessionOnly based on
|
||||
// the result of CanUseStorage.
|
||||
bool
|
||||
CacheStoragePermissions();
|
||||
|
||||
nsIDOMStorageItem* GetNamedItem(const nsAString& aKey, nsresult* aResult);
|
||||
|
||||
nsresult SetSecure(const nsAString& aKey, bool aSecure)
|
||||
{
|
||||
return mStorageImpl->SetSecure(aKey, aSecure);
|
||||
}
|
||||
|
||||
nsresult CloneFrom(nsDOMStorage* aThat);
|
||||
|
||||
protected:
|
||||
friend class nsDOMStorage2;
|
||||
friend class nsDOMStoragePersistentDB;
|
||||
|
||||
nsRefPtr<DOMStorageBase> mStorageImpl;
|
||||
|
||||
bool CanAccessSystem(nsIPrincipal *aPrincipal);
|
||||
|
||||
// document URI of the document this storage is bound to
|
||||
nsString mDocumentURI;
|
||||
|
||||
// true if this storage was initialized as a localStorage object. localStorage
|
||||
// objects are scoped to scheme/host/port in the database, while globalStorage
|
||||
// objects are scoped just to host. this flag also tells the manager to map
|
||||
// this storage also in mLocalStorages hash table.
|
||||
nsDOMStorageType mStorageType;
|
||||
|
||||
friend class nsIDOMStorage2;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsDOMStorage2* mEventBroadcaster;
|
||||
};
|
||||
|
||||
class nsDOMStorage2 MOZ_FINAL : public nsIDOMStorage,
|
||||
public nsPIDOMStorage,
|
||||
public nsIInterfaceRequestor
|
||||
{
|
||||
public:
|
||||
// nsISupports
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMStorage2, nsIDOMStorage)
|
||||
|
||||
nsDOMStorage2(nsDOMStorage2& aThat);
|
||||
nsDOMStorage2();
|
||||
|
||||
NS_DECL_NSIDOMSTORAGE
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
|
||||
// nsPIDOMStorage
|
||||
virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
|
||||
bool aPrivate);
|
||||
virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
|
||||
bool aPrivate);
|
||||
virtual already_AddRefed<nsIDOMStorage> Clone();
|
||||
virtual already_AddRefed<nsIDOMStorage> Fork(const nsSubstring &aDocumentURI);
|
||||
virtual bool IsForkOf(nsIDOMStorage* aThat);
|
||||
virtual nsTArray<nsString> *GetKeys();
|
||||
virtual nsIPrincipal* Principal();
|
||||
virtual bool CanAccess(nsIPrincipal *aPrincipal);
|
||||
virtual nsDOMStorageType StorageType();
|
||||
virtual bool IsPrivate();
|
||||
|
||||
void BroadcastChangeNotification(const nsSubstring &aKey,
|
||||
const nsSubstring &aOldValue,
|
||||
const nsSubstring &aNewValue);
|
||||
void InitAsSessionStorageFork(nsIPrincipal *aPrincipal,
|
||||
const nsSubstring &aDocumentURI,
|
||||
nsDOMStorage* aStorage);
|
||||
|
||||
private:
|
||||
// storages bound to an origin hold the principal to
|
||||
// make security checks against it
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
||||
// Needed for the storage event, this is address of the document this storage
|
||||
// is bound to
|
||||
nsString mDocumentURI;
|
||||
nsRefPtr<nsDOMStorage> mStorage;
|
||||
};
|
||||
|
||||
class nsDOMStorageItem : public nsIDOMStorageItem,
|
||||
public nsIDOMToString
|
||||
{
|
||||
public:
|
||||
nsDOMStorageItem(DOMStorageBase* aStorage,
|
||||
const nsAString& aKey,
|
||||
const nsAString& aValue,
|
||||
bool aSecure);
|
||||
virtual ~nsDOMStorageItem();
|
||||
|
||||
// nsISupports
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMStorageItem, nsIDOMStorageItem)
|
||||
|
||||
// nsIDOMStorageObsolete
|
||||
NS_DECL_NSIDOMSTORAGEITEM
|
||||
|
||||
// nsIDOMToString
|
||||
NS_DECL_NSIDOMTOSTRING
|
||||
|
||||
bool IsSecure()
|
||||
{
|
||||
return mSecure;
|
||||
}
|
||||
|
||||
void SetSecureInternal(bool aSecure)
|
||||
{
|
||||
mSecure = aSecure;
|
||||
}
|
||||
|
||||
const nsAString& GetValueInternal()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
const void SetValueInternal(const nsAString& aValue)
|
||||
{
|
||||
mValue = aValue;
|
||||
}
|
||||
|
||||
void ClearValue()
|
||||
{
|
||||
mValue.Truncate();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// true if this value is for secure sites only
|
||||
bool mSecure;
|
||||
|
||||
// key for the item
|
||||
nsString mKey;
|
||||
|
||||
// value of the item
|
||||
nsString mValue;
|
||||
|
||||
// If this item came from the db, mStorage points to the storage
|
||||
// object where this item came from.
|
||||
nsRefPtr<DOMStorageBase> mStorage;
|
||||
};
|
||||
|
||||
nsresult
|
||||
NS_NewDOMStorage2(nsISupports* aOuter, REFNSIID aIID, void** aResult);
|
||||
|
||||
#endif /* nsDOMStorage_h___ */
|
|
@ -1,86 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsDOMStorageBaseDB.h"
|
||||
#include "nsDOMStorage.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
// Only allow relatively small amounts of data since performance of
|
||||
// the synchronous IO is very bad.
|
||||
#define DEFAULT_QUOTA_LIMIT (5 * 1024)
|
||||
|
||||
uint64_t nsDOMStorageBaseDB::sGlobalVersion = 1;
|
||||
int32_t nsDOMStorageBaseDB::gQuotaLimit = DEFAULT_QUOTA_LIMIT * 1024;
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsDOMStorageBaseDB::Init()
|
||||
{
|
||||
Preferences::AddIntVarCache(&gQuotaLimit, "dom.storage.default_quota",
|
||||
DEFAULT_QUOTA_LIMIT);
|
||||
}
|
||||
|
||||
nsDOMStorageBaseDB::nsDOMStorageBaseDB()
|
||||
{
|
||||
mScopesVersion.Init(8);
|
||||
}
|
||||
|
||||
// public
|
||||
|
||||
void
|
||||
nsDOMStorageBaseDB::MarkScopeCached(DOMStorageImpl* aStorage)
|
||||
{
|
||||
aStorage->SetCachedVersion(CachedScopeVersion(aStorage));
|
||||
}
|
||||
|
||||
bool
|
||||
nsDOMStorageBaseDB::IsScopeDirty(DOMStorageImpl* aStorage)
|
||||
{
|
||||
return !aStorage->CachedVersion() ||
|
||||
(aStorage->CachedVersion() != CachedScopeVersion(aStorage));
|
||||
}
|
||||
|
||||
// protected
|
||||
|
||||
// static
|
||||
uint64_t
|
||||
nsDOMStorageBaseDB::NextGlobalVersion()
|
||||
{
|
||||
sGlobalVersion++;
|
||||
if (sGlobalVersion == 0) // Control overlap, never return 0
|
||||
sGlobalVersion = 1;
|
||||
return sGlobalVersion;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
nsDOMStorageBaseDB::CachedScopeVersion(DOMStorageImpl* aStorage)
|
||||
{
|
||||
uint64_t currentVersion;
|
||||
if (mScopesVersion.Get(aStorage->GetScopeDBKey(), ¤tVersion))
|
||||
return currentVersion;
|
||||
|
||||
mScopesVersion.Put(aStorage->GetScopeDBKey(), sGlobalVersion);
|
||||
return sGlobalVersion;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMStorageBaseDB::MarkScopeDirty(DOMStorageImpl* aStorage)
|
||||
{
|
||||
uint64_t nextVersion = NextGlobalVersion();
|
||||
mScopesVersion.Put(aStorage->GetScopeDBKey(), nextVersion);
|
||||
|
||||
// We may do this because the storage updates its cache along with
|
||||
// updating the database.
|
||||
aStorage->SetCachedVersion(nextVersion);
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMStorageBaseDB::MarkAllScopesDirty()
|
||||
{
|
||||
mScopesVersion.Clear();
|
||||
NextGlobalVersion();
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsDOMStorageBaseDB_h___
|
||||
#define nsDOMStorageBaseDB_h___
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
||||
class DOMStorageImpl;
|
||||
|
||||
class nsDOMStorageBaseDB
|
||||
{
|
||||
public:
|
||||
static void Init();
|
||||
|
||||
nsDOMStorageBaseDB();
|
||||
virtual ~nsDOMStorageBaseDB() {}
|
||||
|
||||
/**
|
||||
* Marks the storage as "cached" after the DOMStorageImpl object has loaded
|
||||
* all items to its memory copy of the entries - IsScopeDirty returns false
|
||||
* after call of this method for this storage.
|
||||
*
|
||||
* When a key is changed or deleted in the storage, the storage scope is
|
||||
* marked as "dirty" again and makes the DOMStorageImpl object recache its
|
||||
* keys on next access, because IsScopeDirty returns true again.
|
||||
*/
|
||||
void MarkScopeCached(DOMStorageImpl* aStorage);
|
||||
|
||||
/**
|
||||
* Test whether the storage for the scope (i.e. origin or host) has been
|
||||
* changed since the last MarkScopeCached call.
|
||||
*/
|
||||
bool IsScopeDirty(DOMStorageImpl* aStorage);
|
||||
|
||||
int32_t GetQuota() {
|
||||
return gQuotaLimit * 1024;
|
||||
}
|
||||
|
||||
protected:
|
||||
nsDataHashtable<nsCStringHashKey, uint64_t> mScopesVersion;
|
||||
|
||||
static uint64_t NextGlobalVersion();
|
||||
uint64_t CachedScopeVersion(DOMStorageImpl* aStorage);
|
||||
|
||||
void MarkScopeDirty(DOMStorageImpl* aStorage);
|
||||
void MarkAllScopesDirty();
|
||||
|
||||
private:
|
||||
static uint64_t sGlobalVersion;
|
||||
|
||||
static int32_t gQuotaLimit;
|
||||
};
|
||||
|
||||
#endif /* nsDOMStorageDB_h___ */
|
|
@ -1,397 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsError.h"
|
||||
#include "nsDOMStorage.h"
|
||||
#include "nsDOMStorageDBWrapper.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIURL.h"
|
||||
#include "nsIVariant.h"
|
||||
#include "nsIEffectiveTLDService.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "mozStorageCID.h"
|
||||
#include "mozStorageHelper.h"
|
||||
#include "mozIStorageService.h"
|
||||
#include "mozIStorageValueArray.h"
|
||||
#include "mozIStorageFunction.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIPrincipal.h"
|
||||
|
||||
void ReverseString(const nsCSubstring& source, nsCSubstring& result)
|
||||
{
|
||||
nsACString::const_iterator sourceBegin, sourceEnd;
|
||||
source.BeginReading(sourceBegin);
|
||||
source.EndReading(sourceEnd);
|
||||
|
||||
result.SetLength(source.Length());
|
||||
nsACString::iterator destEnd;
|
||||
result.EndWriting(destEnd);
|
||||
|
||||
while (sourceBegin != sourceEnd) {
|
||||
*(--destEnd) = *sourceBegin;
|
||||
++sourceBegin;
|
||||
}
|
||||
}
|
||||
|
||||
nsDOMStorageDBWrapper::nsDOMStorageDBWrapper()
|
||||
{
|
||||
}
|
||||
|
||||
nsDOMStorageDBWrapper::~nsDOMStorageDBWrapper()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMStorageDBWrapper::Close()
|
||||
{
|
||||
mPersistentDB.Close();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::Init()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
rv = mPersistentDB.Init(NS_LITERAL_STRING("webappsstore.sqlite"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mSessionOnlyDB.Init(&mPersistentDB);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mPrivateBrowsingDB.Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::FlushAndEvictFromCache(bool aIsShuttingDown)
|
||||
{
|
||||
nsresult rv = mPersistentDB.FlushAndEvictFromCache(aIsShuttingDown);
|
||||
|
||||
// Nothing in the cache? Then no need for a timer.
|
||||
if (!mPersistentDB.IsFlushTimerNeeded()) {
|
||||
StopCacheFlushTimer();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
#define IMPL_FORWARDER_GUTS(_return, _code) \
|
||||
PR_BEGIN_MACRO \
|
||||
if (aStorage->IsPrivate()) \
|
||||
_return mPrivateBrowsingDB._code; \
|
||||
if (aStorage->SessionOnly()) \
|
||||
_return mSessionOnlyDB._code; \
|
||||
_return mPersistentDB._code; \
|
||||
PR_END_MACRO
|
||||
|
||||
#define IMPL_FORWARDER(_code) \
|
||||
IMPL_FORWARDER_GUTS(return, _code)
|
||||
|
||||
#define IMPL_VOID_FORWARDER(_code) \
|
||||
IMPL_FORWARDER_GUTS((void), _code)
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::GetAllKeys(DOMStorageImpl* aStorage,
|
||||
nsTHashtable<nsSessionStorageEntry>* aKeys)
|
||||
{
|
||||
IMPL_FORWARDER(GetAllKeys(aStorage, aKeys));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::GetKeyValue(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey,
|
||||
nsAString& aValue,
|
||||
bool* aSecure)
|
||||
{
|
||||
IMPL_FORWARDER(GetKeyValue(aStorage, aKey, aValue, aSecure));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::SetKey(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey,
|
||||
const nsAString& aValue,
|
||||
bool aSecure)
|
||||
{
|
||||
IMPL_FORWARDER(SetKey(aStorage, aKey, aValue, aSecure));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::SetSecure(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey,
|
||||
const bool aSecure)
|
||||
{
|
||||
IMPL_FORWARDER(SetSecure(aStorage, aKey, aSecure));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::RemoveKey(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey)
|
||||
{
|
||||
IMPL_FORWARDER(RemoveKey(aStorage, aKey));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::ClearStorage(DOMStorageImpl* aStorage)
|
||||
{
|
||||
IMPL_FORWARDER(ClearStorage(aStorage));
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMStorageDBWrapper::MarkScopeCached(DOMStorageImpl* aStorage)
|
||||
{
|
||||
IMPL_VOID_FORWARDER(MarkScopeCached(aStorage));
|
||||
}
|
||||
|
||||
bool
|
||||
nsDOMStorageDBWrapper::IsScopeDirty(DOMStorageImpl* aStorage)
|
||||
{
|
||||
IMPL_FORWARDER(IsScopeDirty(aStorage));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::DropSessionOnlyStoragesForHost(const nsACString& aHostName)
|
||||
{
|
||||
return mSessionOnlyDB.RemoveOwner(aHostName);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::DropPrivateBrowsingStorages()
|
||||
{
|
||||
return mPrivateBrowsingDB.RemoveAll();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::RemoveOwner(const nsACString& aOwner)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
rv = mPrivateBrowsingDB.RemoveOwner(aOwner);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mSessionOnlyDB.RemoveOwner(aOwner);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mPersistentDB.RemoveOwner(aOwner);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::RemoveAll()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
rv = mPrivateBrowsingDB.RemoveAll();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mSessionOnlyDB.RemoveAll();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mPersistentDB.RemoveAll();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::RemoveAllForApp(uint32_t aAppId, bool aOnlyBrowserElement)
|
||||
{
|
||||
// We only care about removing the permament storage. Temporary storage such
|
||||
// as session storage or private browsing storage will not be re-used anyway
|
||||
// and will be automatically deleted at some point.
|
||||
return mPersistentDB.RemoveAllForApp(aAppId, aOnlyBrowserElement);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::GetUsage(DOMStorageImpl* aStorage, int32_t *aUsage)
|
||||
{
|
||||
IMPL_FORWARDER(GetUsage(aStorage, aUsage));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::GetUsage(const nsACString& aDomain,
|
||||
int32_t *aUsage, bool aPrivate)
|
||||
{
|
||||
if (aPrivate)
|
||||
return mPrivateBrowsingDB.GetUsage(aDomain, aUsage);
|
||||
|
||||
#if 0
|
||||
// XXX Check where from all this method gets called, not sure this should
|
||||
// include any potential session-only data
|
||||
nsresult rv;
|
||||
rv = mSessionOnlyDB.GetUsage(aDomain, aUsage);
|
||||
if (NS_SUECEEDED(rv))
|
||||
return rv;
|
||||
#endif
|
||||
|
||||
return mPersistentDB.GetUsage(aDomain, aUsage);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::CreateScopeDBKey(nsIPrincipal* aPrincipal,
|
||||
nsACString& aKey)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsAutoCString domainScope;
|
||||
rv = uri->GetAsciiHost(domainScope);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (domainScope.IsEmpty()) {
|
||||
// About pages have an empty host but a valid path. Since they are handled
|
||||
// internally by our own redirector, we can trust them and use path as key.
|
||||
// if file:/// protocol, let's make the exact directory the domain
|
||||
bool isScheme = false;
|
||||
if ((NS_SUCCEEDED(uri->SchemeIs("about", &isScheme)) && isScheme) ||
|
||||
(NS_SUCCEEDED(uri->SchemeIs("moz-safe-about", &isScheme)) && isScheme)) {
|
||||
rv = uri->GetPath(domainScope);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// While the host is always canonicalized to lowercase, the path is not,
|
||||
// thus need to force the casing.
|
||||
ToLowerCase(domainScope);
|
||||
}
|
||||
else if (NS_SUCCEEDED(uri->SchemeIs("file", &isScheme)) && isScheme) {
|
||||
nsCOMPtr<nsIURL> url = do_QueryInterface(uri, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = url->GetDirectory(domainScope);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoCString key;
|
||||
|
||||
rv = CreateReversedDomain(domainScope, key);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCString scheme;
|
||||
rv = uri->GetScheme(scheme);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
key.Append(NS_LITERAL_CSTRING(":") + scheme);
|
||||
|
||||
int32_t port = NS_GetRealPort(uri);
|
||||
if (port != -1) {
|
||||
key.Append(nsPrintfCString(":%d", port));
|
||||
}
|
||||
|
||||
uint32_t appId;
|
||||
rv = aPrincipal->GetAppId(&appId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool isInBrowserElement;
|
||||
rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
|
||||
aKey.Assign(key);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
aKey.Truncate();
|
||||
aKey.AppendInt(appId);
|
||||
aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
|
||||
NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
|
||||
NS_LITERAL_CSTRING(":") + key);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::CreateReversedDomain(const nsACString& aAsciiDomain,
|
||||
nsACString& aKey)
|
||||
{
|
||||
if (aAsciiDomain.IsEmpty())
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
ReverseString(aAsciiDomain, aKey);
|
||||
|
||||
aKey.AppendLiteral(".");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDBWrapper::CreateQuotaDBKey(nsIPrincipal* aPrincipal,
|
||||
nsACString& aKey)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsAutoCString subdomainsDBKey;
|
||||
nsCOMPtr<nsIEffectiveTLDService> eTLDService(do_GetService(
|
||||
NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = aPrincipal->GetURI(getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsAutoCString eTLDplusOne;
|
||||
rv = eTLDService->GetBaseDomain(uri, 0, eTLDplusOne);
|
||||
if (NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS == rv) {
|
||||
// XXX bug 357323 - what to do for localhost/file exactly?
|
||||
rv = uri->GetAsciiHost(eTLDplusOne);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
CreateReversedDomain(eTLDplusOne, subdomainsDBKey);
|
||||
|
||||
uint32_t appId;
|
||||
rv = aPrincipal->GetAppId(&appId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool isInBrowserElement;
|
||||
rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
|
||||
aKey.Assign(subdomainsDBKey);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
aKey.Truncate();
|
||||
aKey.AppendInt(appId);
|
||||
aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
|
||||
NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
|
||||
NS_LITERAL_CSTRING(":") + subdomainsDBKey);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMStorageDBWrapper::EnsureCacheFlushTimer()
|
||||
{
|
||||
if (!mCacheFlushTimer) {
|
||||
nsresult rv;
|
||||
mCacheFlushTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
|
||||
|
||||
if (!NS_SUCCEEDED(rv)) {
|
||||
mCacheFlushTimer = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
mCacheFlushTimer->Init(nsDOMStorageManager::gStorageManager, 5000,
|
||||
nsITimer::TYPE_REPEATING_SLACK);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMStorageDBWrapper::StopCacheFlushTimer()
|
||||
{
|
||||
if (mCacheFlushTimer) {
|
||||
mCacheFlushTimer->Cancel();
|
||||
mCacheFlushTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,238 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsDOMStorageDB_h___
|
||||
#define nsDOMStorageDB_h___
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsTHashtable.h"
|
||||
|
||||
#include "nsDOMStoragePersistentDB.h"
|
||||
#include "nsDOMStorageMemoryDB.h"
|
||||
|
||||
extern void ReverseString(const nsCSubstring& source, nsCSubstring& result);
|
||||
|
||||
class nsDOMStorage;
|
||||
class nsSessionStorageEntry;
|
||||
|
||||
/**
|
||||
* For the purposes of quota checking, we want to be able to efficiently
|
||||
* reference data items that belong to a host or its subhosts. We do this by
|
||||
* using a reversed domain name as the key for an item. For example, a
|
||||
* storage for foo.bar.com would use a key of 'moc.rab.oof.".
|
||||
*
|
||||
* Additionally, globalStorage and localStorage items must be distinguished.
|
||||
* globalStorage items are scoped to the host, and localStorage are items are
|
||||
* scoped to the scheme/host/port. To scope localStorage data, its port and
|
||||
* scheme are appended to its key. http://foo.bar.com is stored as
|
||||
* moc.rab.foo.:http:80.
|
||||
*
|
||||
* So the following queries can be used, for http://foo.bar.com:
|
||||
*
|
||||
* All data owned by globalStorage["foo.bar.com"] -> SELECT * WHERE Domain =
|
||||
* "moc.rab.foo.:"
|
||||
*
|
||||
* All data owned by localStorage -> SELECT * WHERE Domain =
|
||||
* "moc.rab.foo.:http:80"
|
||||
*
|
||||
* All data owned by foo.bar.com, in any storage ->
|
||||
* SELECT * WHERE Domain GLOB "moc.rab.foo.:*"
|
||||
*
|
||||
* All data owned by foo.bar.com or any subdomain, in any storage ->
|
||||
* SELECT * WHERE Domain GLOB "moc.rab.foo.*".
|
||||
*
|
||||
* This key is called the "scope DB key" throughout the code. So the scope DB
|
||||
* key for localStorage at http://foo.bar.com is "moc.rab.foo.:http:80".
|
||||
*
|
||||
* When calculating quotas, we want to lump together everything in an ETLD+1.
|
||||
* So we use a "quota key" during lookups to calculate the quota. So the
|
||||
* quota key for localStorage at http://foo.bar.com is "moc.rab.". */
|
||||
|
||||
class nsDOMStorageDBWrapper
|
||||
{
|
||||
public:
|
||||
nsDOMStorageDBWrapper();
|
||||
~nsDOMStorageDBWrapper();
|
||||
|
||||
/**
|
||||
* Close the connections, finalizing all the cached statements.
|
||||
*/
|
||||
void Close();
|
||||
|
||||
nsresult
|
||||
Init();
|
||||
|
||||
/**
|
||||
* Retrieve a list of all the keys associated with a particular domain.
|
||||
*/
|
||||
nsresult
|
||||
GetAllKeys(DOMStorageImpl* aStorage,
|
||||
nsTHashtable<nsSessionStorageEntry>* aKeys);
|
||||
|
||||
/**
|
||||
* Retrieve a value and secure flag for a key from storage.
|
||||
*
|
||||
* @throws NS_ERROR_DOM_NOT_FOUND_ERR if key not found
|
||||
*/
|
||||
nsresult
|
||||
GetKeyValue(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey,
|
||||
nsAString& aValue,
|
||||
bool* aSecure);
|
||||
|
||||
/**
|
||||
* Set the value and secure flag for a key in storage.
|
||||
*/
|
||||
nsresult
|
||||
SetKey(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey,
|
||||
const nsAString& aValue,
|
||||
bool aSecure);
|
||||
|
||||
/**
|
||||
* Set the secure flag for a key in storage. Does nothing if the key was
|
||||
* not found.
|
||||
*/
|
||||
nsresult
|
||||
SetSecure(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey,
|
||||
const bool aSecure);
|
||||
|
||||
/**
|
||||
* Removes a key from storage.
|
||||
*/
|
||||
nsresult
|
||||
RemoveKey(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey);
|
||||
|
||||
/**
|
||||
* Remove all keys belonging to this storage.
|
||||
*/
|
||||
nsresult
|
||||
ClearStorage(DOMStorageImpl* aStorage);
|
||||
|
||||
/**
|
||||
* Drop session-only storage for a specific host and all it's subdomains
|
||||
*/
|
||||
nsresult
|
||||
DropSessionOnlyStoragesForHost(const nsACString& aHostName);
|
||||
|
||||
/**
|
||||
* Drop everything we gathered to private browsing in-memory database
|
||||
*/
|
||||
nsresult
|
||||
DropPrivateBrowsingStorages();
|
||||
|
||||
/**
|
||||
* Removes all keys added by a given domain.
|
||||
*/
|
||||
nsresult
|
||||
RemoveOwner(const nsACString& aOwner);
|
||||
|
||||
/**
|
||||
* Removes all keys from storage. Used when clearing storage.
|
||||
*/
|
||||
nsresult
|
||||
RemoveAll();
|
||||
|
||||
/**
|
||||
* Removes all keys from storage for a specific app.
|
||||
* If aOnlyBrowserElement is true, it will remove only keys with the
|
||||
* browserElement flag set.
|
||||
* aAppId has to be a valid app id. It can't be NO_APP_ID or UNKNOWN_APP_ID.
|
||||
*/
|
||||
nsresult
|
||||
RemoveAllForApp(uint32_t aAppId, bool aOnlyBrowserElement);
|
||||
|
||||
/**
|
||||
* Returns usage for a storage using its GetQuotaDBKey() as a key.
|
||||
*/
|
||||
nsresult
|
||||
GetUsage(DOMStorageImpl* aStorage, int32_t *aUsage);
|
||||
|
||||
/**
|
||||
* Returns usage of the domain and optionaly by any subdomain.
|
||||
*/
|
||||
nsresult
|
||||
GetUsage(const nsACString& aDomain, int32_t *aUsage, bool aPrivate);
|
||||
|
||||
/**
|
||||
* Marks the storage as "cached" after the DOMStorageImpl object has loaded
|
||||
* all items to its memory copy of the entries - IsScopeDirty returns false
|
||||
* after call of this method for this storage.
|
||||
*
|
||||
* When a key is changed or deleted in the storage, the storage scope is
|
||||
* marked as "dirty" again and makes the DOMStorageImpl object recache its
|
||||
* keys on next access, because IsScopeDirty returns true again.
|
||||
*/
|
||||
void
|
||||
MarkScopeCached(DOMStorageImpl* aStorage);
|
||||
|
||||
/**
|
||||
* Test whether the storage for the scope (i.e. origin or host) has been
|
||||
* changed since the last MarkScopeCached call.
|
||||
*/
|
||||
bool
|
||||
IsScopeDirty(DOMStorageImpl* aStorage);
|
||||
|
||||
/**
|
||||
* Turns "http://foo.bar.com:80" to "moc.rab.oof.:http:80",
|
||||
* i.e. reverses the host, appends a dot, appends the schema
|
||||
* and a port number.
|
||||
*/
|
||||
static nsresult CreateScopeDBKey(nsIPrincipal* aPrincipal, nsACString& aKey);
|
||||
|
||||
/**
|
||||
* Turns "http://foo.bar.com" to "moc.rab.oof.",
|
||||
* i.e. reverses the host and appends a dot.
|
||||
*/
|
||||
static nsresult CreateReversedDomain(nsIURI* aUri, nsACString& aKey);
|
||||
static nsresult CreateReversedDomain(const nsACString& aAsciiDomain, nsACString& aKey);
|
||||
|
||||
/**
|
||||
* Turns "foo.bar.com" to "moc.rab.",
|
||||
* i.e. extracts eTLD+1 from the host, reverses the result
|
||||
* and appends a dot.
|
||||
*/
|
||||
static nsresult CreateQuotaDBKey(nsIPrincipal* aPrincipal,
|
||||
nsACString& aKey);
|
||||
|
||||
/**
|
||||
* Turns "foo.bar.com" to "moc.rab.",
|
||||
* i.e. extracts eTLD+1 from the host, reverses the result
|
||||
* and appends a dot.
|
||||
*/
|
||||
static nsresult CreateQuotaDBKey(const nsACString& aDomain,
|
||||
nsACString& aKey)
|
||||
{
|
||||
return CreateReversedDomain(aDomain, aKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the cache flush timer is running. This is called when we add
|
||||
* data that will need to be flushed.
|
||||
*/
|
||||
void EnsureCacheFlushTimer();
|
||||
|
||||
/**
|
||||
* Called by the timer or on shutdown/profile change to evict scopes
|
||||
* that have not been used in a long time and to flush new data to disk.
|
||||
*/
|
||||
nsresult FlushAndEvictFromCache(bool aIsShuttingDown);
|
||||
|
||||
/**
|
||||
* Stops the cache flush timer.
|
||||
*/
|
||||
void StopCacheFlushTimer();
|
||||
|
||||
protected:
|
||||
nsDOMStoragePersistentDB mPersistentDB;
|
||||
nsDOMStorageMemoryDB mSessionOnlyDB;
|
||||
nsDOMStorageMemoryDB mPrivateBrowsingDB;
|
||||
|
||||
nsCOMPtr<nsITimer> mCacheFlushTimer;
|
||||
};
|
||||
|
||||
#endif /* nsDOMStorageDB_h___ */
|
|
@ -1,383 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsError.h"
|
||||
#include "nsDOMStorage.h"
|
||||
#include "nsDOMStorageMemoryDB.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
nsresult
|
||||
nsDOMStorageMemoryDB::Init(nsDOMStoragePersistentDB* aPreloadDB)
|
||||
{
|
||||
mData.Init(20);
|
||||
|
||||
mPreloadDB = aPreloadDB;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
AllKeyEnum(nsSessionStorageEntry* aEntry, void* userArg)
|
||||
{
|
||||
nsDOMStorageMemoryDB::nsStorageItemsTable *target =
|
||||
(nsDOMStorageMemoryDB::nsStorageItemsTable *)userArg;
|
||||
|
||||
nsDOMStorageMemoryDB::nsInMemoryItem* item =
|
||||
new nsDOMStorageMemoryDB::nsInMemoryItem();
|
||||
if (!item)
|
||||
return PL_DHASH_STOP;
|
||||
|
||||
aEntry->mItem->GetValue(item->mValue);
|
||||
nsresult rv = aEntry->mItem->GetSecure(&item->mSecure);
|
||||
if (NS_FAILED(rv))
|
||||
item->mSecure = false;
|
||||
|
||||
target->Put(aEntry->GetKey(), item);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageMemoryDB::GetItemsTable(DOMStorageImpl* aStorage,
|
||||
nsInMemoryStorage** aMemoryStorage)
|
||||
{
|
||||
if (mData.Get(aStorage->GetScopeDBKey(), aMemoryStorage))
|
||||
return NS_OK;
|
||||
|
||||
*aMemoryStorage = nullptr;
|
||||
|
||||
nsInMemoryStorage* storageData = new nsInMemoryStorage();
|
||||
if (!storageData)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
storageData->mTable.Init();
|
||||
|
||||
if (mPreloadDB) {
|
||||
nsresult rv;
|
||||
|
||||
nsTHashtable<nsSessionStorageEntry> keys;
|
||||
keys.Init();
|
||||
|
||||
rv = mPreloadDB->GetAllKeys(aStorage, &keys);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mPreloading = true;
|
||||
keys.EnumerateEntries(AllKeyEnum, &storageData->mTable);
|
||||
mPreloading = false;
|
||||
}
|
||||
|
||||
mData.Put(aStorage->GetScopeDBKey(), storageData);
|
||||
*aMemoryStorage = storageData;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
struct GetAllKeysEnumStruc
|
||||
{
|
||||
nsTHashtable<nsSessionStorageEntry>* mTarget;
|
||||
DOMStorageImpl* mStorage;
|
||||
};
|
||||
|
||||
static PLDHashOperator
|
||||
GetAllKeysEnum(const nsAString& keyname,
|
||||
nsDOMStorageMemoryDB::nsInMemoryItem* item,
|
||||
void *closure)
|
||||
{
|
||||
GetAllKeysEnumStruc* struc = (GetAllKeysEnumStruc*)closure;
|
||||
|
||||
nsSessionStorageEntry* entry = struc->mTarget->PutEntry(keyname);
|
||||
if (!entry)
|
||||
return PL_DHASH_STOP;
|
||||
|
||||
entry->mItem = new nsDOMStorageItem(struc->mStorage,
|
||||
keyname,
|
||||
EmptyString(),
|
||||
item->mSecure);
|
||||
if (!entry->mItem)
|
||||
return PL_DHASH_STOP;
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageMemoryDB::GetAllKeys(DOMStorageImpl* aStorage,
|
||||
nsTHashtable<nsSessionStorageEntry>* aKeys)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsInMemoryStorage* storage;
|
||||
rv = GetItemsTable(aStorage, &storage);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
GetAllKeysEnumStruc struc;
|
||||
struc.mTarget = aKeys;
|
||||
struc.mStorage = aStorage;
|
||||
storage->mTable.EnumerateRead(GetAllKeysEnum, &struc);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageMemoryDB::GetKeyValue(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey,
|
||||
nsAString& aValue,
|
||||
bool* aSecure)
|
||||
{
|
||||
if (mPreloading) {
|
||||
NS_PRECONDITION(mPreloadDB, "Must have a preload DB set when preloading");
|
||||
return mPreloadDB->GetKeyValue(aStorage, aKey, aValue, aSecure);
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsInMemoryStorage* storage;
|
||||
rv = GetItemsTable(aStorage, &storage);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsInMemoryItem* item;
|
||||
if (!storage->mTable.Get(aKey, &item))
|
||||
return NS_ERROR_DOM_NOT_FOUND_ERR;
|
||||
|
||||
aValue = item->mValue;
|
||||
*aSecure = item->mSecure;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageMemoryDB::SetKey(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey,
|
||||
const nsAString& aValue,
|
||||
bool aSecure)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsInMemoryStorage* storage;
|
||||
rv = GetItemsTable(aStorage, &storage);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
int32_t usage = 0;
|
||||
if (!aStorage->GetQuotaDBKey().IsEmpty()) {
|
||||
rv = GetUsage(aStorage, &usage);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
usage += aKey.Length() + aValue.Length();
|
||||
|
||||
nsInMemoryItem* item;
|
||||
if (!storage->mTable.Get(aKey, &item)) {
|
||||
if (usage > GetQuota()) {
|
||||
return NS_ERROR_DOM_QUOTA_REACHED;
|
||||
}
|
||||
|
||||
item = new nsInMemoryItem();
|
||||
if (!item)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
storage->mTable.Put(aKey, item);
|
||||
storage->mUsageDelta += aKey.Length();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!aSecure && item->mSecure)
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
usage -= aKey.Length() + item->mValue.Length();
|
||||
if (usage > GetQuota()) {
|
||||
return NS_ERROR_DOM_QUOTA_REACHED;
|
||||
}
|
||||
}
|
||||
|
||||
storage->mUsageDelta += aValue.Length() - item->mValue.Length();
|
||||
|
||||
item->mValue = aValue;
|
||||
item->mSecure = aSecure;
|
||||
|
||||
MarkScopeDirty(aStorage);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageMemoryDB::SetSecure(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey,
|
||||
const bool aSecure)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsInMemoryStorage* storage;
|
||||
rv = GetItemsTable(aStorage, &storage);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsInMemoryItem* item;
|
||||
if (!storage->mTable.Get(aKey, &item))
|
||||
return NS_ERROR_DOM_NOT_FOUND_ERR;
|
||||
|
||||
item->mSecure = aSecure;
|
||||
|
||||
MarkScopeDirty(aStorage);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageMemoryDB::RemoveKey(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsInMemoryStorage* storage;
|
||||
rv = GetItemsTable(aStorage, &storage);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsInMemoryItem* item;
|
||||
if (!storage->mTable.Get(aKey, &item))
|
||||
return NS_ERROR_DOM_NOT_FOUND_ERR;
|
||||
|
||||
storage->mUsageDelta -= aKey.Length() + item->mValue.Length();
|
||||
storage->mTable.Remove(aKey);
|
||||
|
||||
MarkScopeDirty(aStorage);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
RemoveAllKeysEnum(const nsAString& keyname,
|
||||
nsAutoPtr<nsDOMStorageMemoryDB::nsInMemoryItem>& item,
|
||||
void *closure)
|
||||
{
|
||||
nsDOMStorageMemoryDB::nsInMemoryStorage* storage =
|
||||
(nsDOMStorageMemoryDB::nsInMemoryStorage*)closure;
|
||||
|
||||
storage->mUsageDelta -= keyname.Length() + item->mValue.Length();
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageMemoryDB::ClearStorage(DOMStorageImpl* aStorage)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsInMemoryStorage* storage;
|
||||
rv = GetItemsTable(aStorage, &storage);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
storage->mTable.Enumerate(RemoveAllKeysEnum, storage);
|
||||
|
||||
MarkScopeDirty(aStorage);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageMemoryDB::DropStorage(DOMStorageImpl* aStorage)
|
||||
{
|
||||
mData.Remove(aStorage->GetScopeDBKey());
|
||||
MarkScopeDirty(aStorage);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
struct RemoveOwnersStruc
|
||||
{
|
||||
nsCString* mSubDomain;
|
||||
bool mMatch;
|
||||
};
|
||||
|
||||
static PLDHashOperator
|
||||
RemoveOwnersEnum(const nsACString& key,
|
||||
nsAutoPtr<nsDOMStorageMemoryDB::nsInMemoryStorage>& storage,
|
||||
void *closure)
|
||||
{
|
||||
RemoveOwnersStruc* struc = (RemoveOwnersStruc*)closure;
|
||||
|
||||
if (StringBeginsWith(key, *(struc->mSubDomain)) == struc->mMatch)
|
||||
return PL_DHASH_REMOVE;
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageMemoryDB::RemoveOwner(const nsACString& aOwner)
|
||||
{
|
||||
nsAutoCString subdomainsDBKey;
|
||||
nsDOMStorageDBWrapper::CreateReversedDomain(aOwner, subdomainsDBKey);
|
||||
|
||||
RemoveOwnersStruc struc;
|
||||
struc.mSubDomain = &subdomainsDBKey;
|
||||
struc.mMatch = true;
|
||||
mData.Enumerate(RemoveOwnersEnum, &struc);
|
||||
|
||||
MarkAllScopesDirty();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageMemoryDB::RemoveAll()
|
||||
{
|
||||
mData.Clear(); // XXX Check this releases all instances
|
||||
|
||||
MarkAllScopesDirty();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageMemoryDB::GetUsage(DOMStorageImpl* aStorage, int32_t *aUsage)
|
||||
{
|
||||
return GetUsageInternal(aStorage->GetQuotaDBKey(), aUsage);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageMemoryDB::GetUsage(const nsACString& aDomain,
|
||||
int32_t *aUsage)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsAutoCString quotaDBKey;
|
||||
rv = nsDOMStorageDBWrapper::CreateQuotaDBKey(aDomain, quotaDBKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return GetUsageInternal(quotaDBKey, aUsage);
|
||||
}
|
||||
|
||||
struct GetUsageEnumStruc
|
||||
{
|
||||
int32_t mUsage;
|
||||
nsCString mSubdomain;
|
||||
};
|
||||
|
||||
static PLDHashOperator
|
||||
GetUsageEnum(const nsACString& key,
|
||||
nsDOMStorageMemoryDB::nsInMemoryStorage* storageData,
|
||||
void *closure)
|
||||
{
|
||||
GetUsageEnumStruc* struc = (GetUsageEnumStruc*)closure;
|
||||
|
||||
if (StringBeginsWith(key, struc->mSubdomain)) {
|
||||
struc->mUsage += storageData->mUsageDelta;
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageMemoryDB::GetUsageInternal(const nsACString& aQuotaDBKey,
|
||||
int32_t *aUsage)
|
||||
{
|
||||
GetUsageEnumStruc struc;
|
||||
struc.mUsage = 0;
|
||||
struc.mSubdomain = aQuotaDBKey;
|
||||
|
||||
if (mPreloadDB) {
|
||||
nsresult rv;
|
||||
|
||||
rv = mPreloadDB->GetUsageInternal(aQuotaDBKey, &struc.mUsage);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
mData.EnumerateRead(GetUsageEnum, &struc);
|
||||
|
||||
*aUsage = struc.mUsage;
|
||||
return NS_OK;
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsDOMStorageMemoryDB_h___
|
||||
#define nsDOMStorageMemoryDB_h___
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsDOMStorageBaseDB.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
||||
class nsDOMStoragePersistentDB;
|
||||
|
||||
class nsDOMStorageMemoryDB : public nsDOMStorageBaseDB
|
||||
{
|
||||
public:
|
||||
nsDOMStorageMemoryDB() : mPreloading(false) {}
|
||||
~nsDOMStorageMemoryDB() {}
|
||||
|
||||
class nsInMemoryItem
|
||||
{
|
||||
public:
|
||||
bool mSecure;
|
||||
nsString mValue;
|
||||
};
|
||||
|
||||
typedef nsClassHashtable<nsStringHashKey, nsInMemoryItem> nsStorageItemsTable;
|
||||
|
||||
class nsInMemoryStorage
|
||||
{
|
||||
public:
|
||||
nsStorageItemsTable mTable;
|
||||
int32_t mUsageDelta;
|
||||
|
||||
nsInMemoryStorage() : mUsageDelta(0) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param aPreloadDB
|
||||
* If non-null, data for a domain/origin will be preloaded from
|
||||
* the provided database. Used for session-only cookies mode to
|
||||
* provide existing data from the persistent database.
|
||||
*/
|
||||
nsresult
|
||||
Init(nsDOMStoragePersistentDB* aPreloadDB = nullptr);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
nsresult
|
||||
GetItemsTable(DOMStorageImpl* aStorage,
|
||||
nsInMemoryStorage** aMemoryStorage);
|
||||
|
||||
/**
|
||||
* Retrieve a list of all the keys associated with a particular domain.
|
||||
*/
|
||||
nsresult
|
||||
GetAllKeys(DOMStorageImpl* aStorage,
|
||||
nsTHashtable<nsSessionStorageEntry>* aKeys);
|
||||
|
||||
/**
|
||||
* Retrieve a value and secure flag for a key from storage.
|
||||
*
|
||||
* @throws NS_ERROR_DOM_NOT_FOUND_ERR if key not found
|
||||
*/
|
||||
nsresult
|
||||
GetKeyValue(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey,
|
||||
nsAString& aValue,
|
||||
bool* aSecure);
|
||||
|
||||
/**
|
||||
* Set the value and secure flag for a key in storage.
|
||||
*/
|
||||
nsresult
|
||||
SetKey(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey,
|
||||
const nsAString& aValue,
|
||||
bool aSecure);
|
||||
|
||||
/**
|
||||
* Set the secure flag for a key in storage. Does nothing if the key was
|
||||
* not found.
|
||||
*/
|
||||
nsresult
|
||||
SetSecure(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey,
|
||||
const bool aSecure);
|
||||
|
||||
/**
|
||||
* Removes a key from storage.
|
||||
*/
|
||||
nsresult
|
||||
RemoveKey(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey);
|
||||
|
||||
/**
|
||||
* Remove all keys belonging to this storage.
|
||||
*/
|
||||
nsresult
|
||||
ClearStorage(DOMStorageImpl* aStorage);
|
||||
|
||||
/**
|
||||
* If we have changed the persistent storage, drop any potential session storages
|
||||
*/
|
||||
nsresult
|
||||
DropStorage(DOMStorageImpl* aStorage);
|
||||
|
||||
/**
|
||||
* Removes all keys added by a given domain.
|
||||
*/
|
||||
nsresult
|
||||
RemoveOwner(const nsACString& aOwner);
|
||||
|
||||
/**
|
||||
* Removes all keys from storage. Used when clearing storage.
|
||||
*/
|
||||
nsresult
|
||||
RemoveAll();
|
||||
|
||||
/**
|
||||
* Returns usage for a storage using its GetQuotaDBKey() as a key.
|
||||
*/
|
||||
nsresult
|
||||
GetUsage(DOMStorageImpl* aStorage, int32_t *aUsage);
|
||||
|
||||
/**
|
||||
* Returns usage of the domain and optionaly by any subdomain.
|
||||
*/
|
||||
nsresult
|
||||
GetUsage(const nsACString& aDomain, int32_t *aUsage);
|
||||
|
||||
protected:
|
||||
|
||||
nsClassHashtable<nsCStringHashKey, nsInMemoryStorage> mData;
|
||||
nsDOMStoragePersistentDB* mPreloadDB;
|
||||
bool mPreloading;
|
||||
|
||||
nsresult
|
||||
GetUsageInternal(const nsACString& aQuotaDBKey, int32_t *aUsage);
|
||||
};
|
||||
|
||||
#endif
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,265 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsDOMStoragePersistentDB_h___
|
||||
#define nsDOMStoragePersistentDB_h___
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsDOMStorageBaseDB.h"
|
||||
#include "mozIStorageConnection.h"
|
||||
#include "mozIStorageStatement.h"
|
||||
#include "mozilla/storage/StatementCache.h"
|
||||
#include "mozIStorageBindingParamsArray.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsIThread.h"
|
||||
|
||||
#include "nsLocalStorageCache.h"
|
||||
|
||||
class DOMStorageImpl;
|
||||
class nsSessionStorageEntry;
|
||||
|
||||
class nsDOMStoragePersistentDB : public nsDOMStorageBaseDB
|
||||
{
|
||||
typedef mozilla::storage::StatementCache<mozIStorageStatement> StatementCache;
|
||||
typedef nsLocalStorageCache::FlushData FlushData;
|
||||
|
||||
public:
|
||||
nsDOMStoragePersistentDB();
|
||||
~nsDOMStoragePersistentDB() {}
|
||||
|
||||
nsresult
|
||||
Init(const nsString& aDatabaseName);
|
||||
|
||||
/**
|
||||
* Close the connection, finalizing all the cached statements.
|
||||
*/
|
||||
void
|
||||
Close();
|
||||
|
||||
/**
|
||||
* Indicates whether any data is cached that might need to be
|
||||
* flushed or evicted.
|
||||
*/
|
||||
bool
|
||||
IsFlushTimerNeeded() const;
|
||||
|
||||
/**
|
||||
* Retrieve a list of all the keys associated with a particular domain.
|
||||
*/
|
||||
nsresult
|
||||
GetAllKeys(DOMStorageImpl* aStorage,
|
||||
nsTHashtable<nsSessionStorageEntry>* aKeys);
|
||||
|
||||
/**
|
||||
* Retrieve a value and secure flag for a key from storage.
|
||||
*
|
||||
* @throws NS_ERROR_DOM_NOT_FOUND_ERR if key not found
|
||||
*/
|
||||
nsresult
|
||||
GetKeyValue(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey,
|
||||
nsAString& aValue,
|
||||
bool* aSecure);
|
||||
|
||||
/**
|
||||
* Set the value and secure flag for a key in storage.
|
||||
*/
|
||||
nsresult
|
||||
SetKey(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey,
|
||||
const nsAString& aValue,
|
||||
bool aSecure);
|
||||
|
||||
/**
|
||||
* Set the secure flag for a key in storage. Does nothing if the key was
|
||||
* not found.
|
||||
*/
|
||||
nsresult
|
||||
SetSecure(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey,
|
||||
const bool aSecure);
|
||||
|
||||
/**
|
||||
* Removes a key from storage.
|
||||
*/
|
||||
nsresult
|
||||
RemoveKey(DOMStorageImpl* aStorage,
|
||||
const nsAString& aKey);
|
||||
|
||||
/**
|
||||
* Remove all keys belonging to this storage.
|
||||
*/
|
||||
nsresult
|
||||
ClearStorage(DOMStorageImpl* aStorage);
|
||||
|
||||
/**
|
||||
* Removes all keys added by a given domain.
|
||||
*/
|
||||
nsresult
|
||||
RemoveOwner(const nsACString& aOwner);
|
||||
|
||||
/**
|
||||
* Removes all keys from storage. Used when clearing storage.
|
||||
*/
|
||||
nsresult
|
||||
RemoveAll();
|
||||
|
||||
/**
|
||||
* Removes all keys from storage for a specific app.
|
||||
* If aOnlyBrowserElement is true, it will remove only keys with the
|
||||
* browserElement flag set.
|
||||
* aAppId has to be a valid app id. It can't be NO_APP_ID or UNKNOWN_APP_ID.
|
||||
*/
|
||||
nsresult
|
||||
RemoveAllForApp(uint32_t aAppId, bool aOnlyBrowserElement);
|
||||
|
||||
/**
|
||||
* Returns usage for a storage using its GetQuotaDBKey() as a key.
|
||||
*/
|
||||
nsresult
|
||||
GetUsage(DOMStorageImpl* aStorage, int32_t *aUsage);
|
||||
|
||||
/**
|
||||
* Returns usage of the domain and optionaly by any subdomain.
|
||||
*/
|
||||
nsresult
|
||||
GetUsage(const nsACString& aDomain, int32_t *aUsage);
|
||||
|
||||
/**
|
||||
* Clears all in-memory data from private browsing mode
|
||||
*/
|
||||
nsresult
|
||||
ClearAllPrivateBrowsingData();
|
||||
|
||||
/**
|
||||
* This is the top-level method called by timer & shutdown code.
|
||||
*
|
||||
* LocalStorage now flushes dirty items off the main thread to reduce
|
||||
* main thread jank.
|
||||
*
|
||||
* When the flush timer fires, pointers to changed data are retrieved from
|
||||
* nsLocalStorageCache on the main thread and are used to build an SQLite
|
||||
* statement array with bound parameters that is then executed on a
|
||||
* background thread. Only one flush operation is outstanding at once.
|
||||
* After the flush operation completes, a notification task
|
||||
* is enqueued on the main thread which updates the cache state.
|
||||
*
|
||||
* Cached scopes are evicted if they aren't dirty and haven't been used for
|
||||
* the maximum idle time.
|
||||
*/
|
||||
nsresult
|
||||
FlushAndEvictFromCache(bool aMainThread);
|
||||
|
||||
/**
|
||||
* Executes SQL statements for flushing dirty data to disk.
|
||||
*/
|
||||
nsresult
|
||||
Flush();
|
||||
|
||||
/**
|
||||
* The notifier task calls this method to update cache state after a
|
||||
* flush operation completes.
|
||||
*/
|
||||
void
|
||||
HandleFlushComplete(bool aSucceeded);
|
||||
|
||||
private:
|
||||
|
||||
friend class nsDOMStorageMemoryDB;
|
||||
|
||||
/**
|
||||
* Sets the database's journal mode to WAL or TRUNCATE.
|
||||
*/
|
||||
nsresult
|
||||
SetJournalMode(bool aIsWal);
|
||||
|
||||
/**
|
||||
* Sets the threshold for auto-checkpointing the WAL.
|
||||
*/
|
||||
nsresult
|
||||
ConfigureWalBehavior();
|
||||
|
||||
/**
|
||||
* Ensures that the scope's keys are cached.
|
||||
*/
|
||||
nsresult
|
||||
EnsureScopeLoaded(DOMStorageImpl* aStorage);
|
||||
|
||||
/**
|
||||
* Fetches the scope's data from the database.
|
||||
*/
|
||||
nsresult
|
||||
FetchScope(DOMStorageImpl* aStorage, nsScopeCache* aScopeCache);
|
||||
|
||||
/**
|
||||
* Ensures that the quota usage information for the site is known.
|
||||
*
|
||||
* The current quota usage is calculated from the space occupied by
|
||||
* the cached scopes + the size of the uncached scopes on disk.
|
||||
* This method ensures that the size of the site's uncached scopes
|
||||
* on disk is known.
|
||||
*/
|
||||
nsresult
|
||||
EnsureQuotaUsageLoaded(const nsACString& aQuotaKey);
|
||||
|
||||
/**
|
||||
* Fetches the size of scopes matching aQuotaDBKey from the database.
|
||||
*/
|
||||
nsresult
|
||||
FetchQuotaUsage(const nsACString& aQuotaDBKey);
|
||||
|
||||
nsresult
|
||||
GetUsageInternal(const nsACString& aQuotaDBKey, int32_t *aUsage);
|
||||
|
||||
/**
|
||||
* Helper function for RemoveOwner and RemoveAllForApp.
|
||||
*/
|
||||
nsresult
|
||||
FetchMatchingScopeNames(const nsACString& aPattern);
|
||||
|
||||
nsresult
|
||||
PrepareFlushStatements(const FlushData& aFlushData);
|
||||
|
||||
/**
|
||||
* Gathers the dirty data from the cache, prepares SQL statements and
|
||||
* updates state flags
|
||||
*/
|
||||
nsresult
|
||||
PrepareForFlush();
|
||||
|
||||
/**
|
||||
* Removes non-dirty scopes that haven't been used in X seconds.
|
||||
*/
|
||||
void
|
||||
EvictUnusedScopes();
|
||||
|
||||
/**
|
||||
* DB data structures
|
||||
*/
|
||||
nsTArray<nsCOMPtr<mozIStorageStatement> > mFlushStatements;
|
||||
nsTArray<nsCOMPtr<mozIStorageBindingParamsArray> > mFlushStatementParams;
|
||||
StatementCache mReadStatements;
|
||||
StatementCache mWriteStatements;
|
||||
nsCOMPtr<mozIStorageConnection> mReadConnection;
|
||||
nsCOMPtr<mozIStorageConnection> mWriteConnection;
|
||||
|
||||
/**
|
||||
* Cache state data
|
||||
*/
|
||||
nsLocalStorageCache mCache;
|
||||
nsDataHashtable<nsCStringHashKey, int32_t> mQuotaUseByUncached;
|
||||
// Set if the DB needs to be emptied on next flush
|
||||
bool mWasRemoveAllCalled;
|
||||
// Set if the DB is currently getting emptied in an async flush
|
||||
bool mIsRemoveAllPending;
|
||||
// Set if a flush operation has been enqueued in the async thread
|
||||
bool mIsFlushPending;
|
||||
|
||||
// Helper thread for flushing
|
||||
nsCOMPtr<nsIThread> mFlushThread;
|
||||
};
|
||||
|
||||
#endif /* nsDOMStorageDB_h___ */
|
|
@ -1,416 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsDOMStorage.h"
|
||||
|
||||
#include "nsLocalStorageCache.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
// Scopes will be evicted from memory if not accessed (read or written)
|
||||
// for MAX_IDLE_TIME seconds
|
||||
#define MAX_IDLE_TIME (30) // seconds
|
||||
|
||||
/**
|
||||
* Anonymous namespace holding helper enum functions
|
||||
*/
|
||||
namespace {
|
||||
|
||||
struct GetUsageEnumData
|
||||
{
|
||||
const nsACString& mQuotaKey;
|
||||
int32_t mUsage;
|
||||
};
|
||||
|
||||
PLDHashOperator
|
||||
GetUsageEnum(const nsACString& aScopeName,
|
||||
nsScopeCache* aScopeCache,
|
||||
void* aParams)
|
||||
{
|
||||
GetUsageEnumData* data = static_cast<GetUsageEnumData*>(aParams);
|
||||
if (StringBeginsWith(aScopeName, data->mQuotaKey)) {
|
||||
data->mUsage += aScopeCache->GetQuotaUsage();
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
struct EvictEnumData
|
||||
{
|
||||
nsTArray<nsCString>& mEvicted;
|
||||
nsTArray<int32_t>& mEvictedSize;
|
||||
};
|
||||
|
||||
struct GetAllKeysEnumData
|
||||
{
|
||||
nsTHashtable<nsSessionStorageEntry>& mKeys;
|
||||
DOMStorageImpl* mStorage;
|
||||
};
|
||||
|
||||
PLDHashOperator
|
||||
GetAllKeysEnum(const nsAString& aKey,
|
||||
nsScopeCache::KeyEntry* aEntry,
|
||||
void* aParams)
|
||||
{
|
||||
GetAllKeysEnumData* data = static_cast<GetAllKeysEnumData*>(aParams);
|
||||
nsSessionStorageEntry* keyEntry = data->mKeys.PutEntry(aKey);
|
||||
keyEntry->mItem = new nsDOMStorageItem(data->mStorage,
|
||||
aKey,
|
||||
aEntry->mValue,
|
||||
aEntry->mIsSecure);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
MarkMatchingDeletedEnum(const nsACString& aScopeName,
|
||||
nsAutoPtr<nsScopeCache>& aScopeCache,
|
||||
void* aPattern)
|
||||
{
|
||||
const nsACString* pattern = static_cast<const nsACString*>(aPattern);
|
||||
if (!StringBeginsWith(aScopeName, *pattern)) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
aScopeCache->DeleteScope();
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
MarkKeysEnum(const nsAString& aKey,
|
||||
nsAutoPtr<nsScopeCache::KeyEntry>& aEntry,
|
||||
void* aDirtyState)
|
||||
{
|
||||
aEntry->mIsDirty = *(static_cast<bool*>(aDirtyState));
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
GetChangedKeysEnum(const nsAString& aKey,
|
||||
nsAutoPtr<nsScopeCache::KeyEntry>& aEntry,
|
||||
void* aParams)
|
||||
{
|
||||
if (!aEntry->mIsDirty) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsLocalStorageCache::FlushData::ChangeSet* changeSet =
|
||||
static_cast<nsLocalStorageCache::FlushData::ChangeSet*>(aParams);
|
||||
|
||||
changeSet->mDirtyKeys.AppendElement(aKey);
|
||||
changeSet->mDirtyValues.AppendElement(aEntry.get());
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
GetEntrySize(const nsAString& aKey,
|
||||
nsScopeCache::KeyEntry* aEntry,
|
||||
void* aParam)
|
||||
{
|
||||
int32_t* usage = static_cast<int32_t*>(aParam);
|
||||
*usage += aKey.Length() + aEntry->mValue.Length();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
nsLocalStorageCache::nsLocalStorageCache()
|
||||
{
|
||||
mScopeCaches.Init(16);
|
||||
}
|
||||
|
||||
nsScopeCache*
|
||||
nsLocalStorageCache::GetScope(const nsACString& aScopeName)
|
||||
{
|
||||
nsScopeCache* scopeCache = nullptr;
|
||||
if (mScopeCaches.Get(aScopeName, &scopeCache)) {
|
||||
scopeCache->mAccessTime = PR_IntervalNow();
|
||||
}
|
||||
return scopeCache;
|
||||
}
|
||||
|
||||
void
|
||||
nsLocalStorageCache::AddScope(const nsACString& aScopeName,
|
||||
nsScopeCache* aScopeCache)
|
||||
{
|
||||
aScopeCache->mAccessTime = PR_IntervalNow();
|
||||
mScopeCaches.Put(aScopeName, aScopeCache);
|
||||
}
|
||||
|
||||
bool
|
||||
nsLocalStorageCache::IsScopeCached(const nsACString& aScopeName) const
|
||||
{
|
||||
return mScopeCaches.Get(aScopeName, nullptr);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsLocalStorageCache::Count() const
|
||||
{
|
||||
return mScopeCaches.Count();
|
||||
}
|
||||
|
||||
int32_t
|
||||
nsLocalStorageCache::GetQuotaUsage(const nsACString& aQuotaKey) const
|
||||
{
|
||||
GetUsageEnumData data = { aQuotaKey , 0 };
|
||||
mScopeCaches.EnumerateRead(GetUsageEnum, &data);
|
||||
return data.mUsage;
|
||||
}
|
||||
|
||||
void
|
||||
nsLocalStorageCache::MarkMatchingScopesDeleted(const nsACString& aPattern)
|
||||
{
|
||||
mScopeCaches.Enumerate(MarkMatchingDeletedEnum, (void*)&aPattern);
|
||||
}
|
||||
|
||||
void
|
||||
nsLocalStorageCache::ForgetAllScopes()
|
||||
{
|
||||
mScopeCaches.Clear();
|
||||
}
|
||||
|
||||
/* static */
|
||||
PLDHashOperator
|
||||
nsLocalStorageCache::GetDirtyDataEnum(const nsACString& aScopeName,
|
||||
nsScopeCache* aScopeCache,
|
||||
void* aParams)
|
||||
{
|
||||
if (!aScopeCache->mIsDirty) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
FlushData* flushData = static_cast<FlushData*>(aParams);
|
||||
FlushData::ChangeSet changeSet;
|
||||
|
||||
changeSet.mWasDeleted = aScopeCache->mWasScopeDeleted;
|
||||
changeSet.mDeletedKeys = &aScopeCache->mDeletedKeys;
|
||||
|
||||
aScopeCache->mTable.Enumerate(GetChangedKeysEnum, &changeSet);
|
||||
|
||||
flushData->mScopeNames.AppendElement(aScopeName);
|
||||
flushData->mChanged.AppendElement(changeSet);
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsLocalStorageCache::GetFlushData(FlushData* aData) const
|
||||
{
|
||||
mScopeCaches.EnumerateRead(GetDirtyDataEnum, aData);
|
||||
}
|
||||
|
||||
/* static */
|
||||
PLDHashOperator
|
||||
nsLocalStorageCache::SetFlushStateEnum(const nsACString& aScopeName,
|
||||
nsAutoPtr<nsScopeCache>& aScopeCache,
|
||||
void* aParams)
|
||||
{
|
||||
FlushState* newState = static_cast<FlushState*>(aParams);
|
||||
if (*newState == FLUSH_PENDING && aScopeCache->mIsDirty) {
|
||||
// Mark all data clean before a flush to allow further changes during flush
|
||||
bool isDirty = false;
|
||||
aScopeCache->mTable.Enumerate(MarkKeysEnum, &isDirty);
|
||||
aScopeCache->mDeletedKeys.Clear();
|
||||
aScopeCache->mWasScopeDeleted = false;
|
||||
aScopeCache->mIsDirty = false;
|
||||
aScopeCache->mIsFlushPending = true;
|
||||
} else if (*newState == FLUSHED && aScopeCache->mIsFlushPending) {
|
||||
aScopeCache->mIsFlushPending = false;
|
||||
} else if (*newState == FLUSH_FAILED && aScopeCache->mIsFlushPending) {
|
||||
// After a flush fails, disk state is unchanged (thanks to transaction
|
||||
// rollback) and the cache contains the correct current state.
|
||||
// So to bring disk back in sync with cache, we simply delete all scope
|
||||
// data from disk on next flush and write out *ALL* the scope's cached keys.
|
||||
// To trigger this behavior, we mark mWasScopeDeleted = true and mark all
|
||||
// keys dirty. Note that scope cache will not be evicted or re-fetched since
|
||||
// it's marked dirty.
|
||||
bool isDirty = true;
|
||||
aScopeCache->mWasScopeDeleted = true;
|
||||
aScopeCache->mTable.Enumerate(MarkKeysEnum, &isDirty);
|
||||
aScopeCache->mIsFlushPending = false;
|
||||
aScopeCache->mIsDirty = true;
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsLocalStorageCache::MarkScopesPending()
|
||||
{
|
||||
FlushState newState = FLUSH_PENDING;
|
||||
mScopeCaches.Enumerate(SetFlushStateEnum, &newState);
|
||||
}
|
||||
|
||||
void
|
||||
nsLocalStorageCache::MarkScopesFlushed()
|
||||
{
|
||||
FlushState newState = FLUSHED;
|
||||
mScopeCaches.Enumerate(SetFlushStateEnum, &newState);
|
||||
}
|
||||
|
||||
void
|
||||
nsLocalStorageCache::MarkFlushFailed()
|
||||
{
|
||||
FlushState newState = FLUSH_FAILED;
|
||||
mScopeCaches.Enumerate(SetFlushStateEnum, &newState);
|
||||
}
|
||||
|
||||
/* static */
|
||||
PLDHashOperator
|
||||
nsLocalStorageCache::EvictEnum(const nsACString& aScopeName,
|
||||
nsAutoPtr<nsScopeCache>& aScopeCache,
|
||||
void* aParams)
|
||||
{
|
||||
EvictEnumData* data = static_cast<EvictEnumData*>(aParams);
|
||||
|
||||
// We can't evict scopes currently getting flushed in case the flush fails
|
||||
if (aScopeCache->mIsDirty || aScopeCache->mIsFlushPending) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static const PRIntervalTime kMaxIdleTime = PR_SecondsToInterval(MAX_IDLE_TIME);
|
||||
bool evict = (PR_IntervalNow() - aScopeCache->mAccessTime) > kMaxIdleTime;
|
||||
|
||||
if (evict) {
|
||||
data->mEvicted.AppendElement(aScopeName);
|
||||
data->mEvictedSize.AppendElement(aScopeCache->GetQuotaUsage());
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsLocalStorageCache::EvictScopes(nsTArray<nsCString>& aEvicted,
|
||||
nsTArray<int32_t>& aEvictedSize)
|
||||
{
|
||||
EvictEnumData data = { aEvicted, aEvictedSize };
|
||||
mScopeCaches.Enumerate(EvictEnum, &data);
|
||||
}
|
||||
|
||||
///////////////////////////////////
|
||||
|
||||
nsScopeCache::nsScopeCache()
|
||||
: mWasScopeDeleted(false), mIsDirty(false), mIsFlushPending(false)
|
||||
{
|
||||
mTable.Init();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsScopeCache::AddEntry(const nsAString& aKey,
|
||||
const nsAString& aValue,
|
||||
bool aSecure)
|
||||
{
|
||||
KeyEntry* entry = new KeyEntry();
|
||||
entry->mValue = aValue;
|
||||
entry->mIsSecure = aSecure;
|
||||
entry->mIsDirty = false;
|
||||
|
||||
mTable.Put(aKey, entry);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsScopeCache::GetAllKeys(DOMStorageImpl* aStorage,
|
||||
nsTHashtable<nsSessionStorageEntry>* aKeys) const
|
||||
{
|
||||
GetAllKeysEnumData data = { *aKeys, aStorage };
|
||||
mTable.EnumerateRead(GetAllKeysEnum, &data);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsScopeCache::GetKey(const nsAString& aKey,
|
||||
nsAString& aValue,
|
||||
bool* aSecure) const
|
||||
{
|
||||
KeyEntry* entry = nullptr;
|
||||
if (mTable.Get(aKey, &entry)) {
|
||||
aValue = entry->mValue;
|
||||
*aSecure = entry->mIsSecure;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsScopeCache::SetKey(const nsAString& aKey,
|
||||
const nsAString& aValue,
|
||||
bool aSecure)
|
||||
{
|
||||
KeyEntry* entry = nullptr;
|
||||
if (!mTable.Get(aKey, &entry)) {
|
||||
entry = new KeyEntry();
|
||||
mTable.Put(aKey, entry);
|
||||
} else if (entry->mValue == aValue) {
|
||||
// Don't mark the cache dirty if nothing changed
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
entry->mValue = aValue;
|
||||
entry->mIsSecure = aSecure;
|
||||
entry->mIsDirty = true;
|
||||
|
||||
mDeletedKeys.RemoveElement(aKey);
|
||||
|
||||
mIsDirty = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsScopeCache::SetSecure(const nsAString& aKey,
|
||||
bool aSecure)
|
||||
{
|
||||
KeyEntry* entry = nullptr;
|
||||
if (!mTable.Get(aKey, &entry)) {
|
||||
return;
|
||||
}
|
||||
|
||||
entry->mIsSecure = aSecure;
|
||||
entry->mIsDirty = true;
|
||||
|
||||
mIsDirty = true;
|
||||
}
|
||||
|
||||
void
|
||||
nsScopeCache::RemoveKey(const nsAString& aKey)
|
||||
{
|
||||
KeyEntry* entry = nullptr;
|
||||
if (!mTable.Get(aKey, &entry)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString key(aKey);
|
||||
if (!mWasScopeDeleted) {
|
||||
if (!mDeletedKeys.Contains(key)) {
|
||||
mDeletedKeys.AppendElement(key);
|
||||
}
|
||||
}
|
||||
mTable.Remove(key);
|
||||
|
||||
mIsDirty = true;
|
||||
}
|
||||
|
||||
void
|
||||
nsScopeCache::DeleteScope()
|
||||
{
|
||||
mTable.Clear();
|
||||
mDeletedKeys.Clear();
|
||||
mWasScopeDeleted = true;
|
||||
|
||||
mIsDirty = true;
|
||||
}
|
||||
|
||||
int32_t
|
||||
nsScopeCache::GetQuotaUsage() const
|
||||
{
|
||||
int32_t usage = 0;
|
||||
mTable.EnumerateRead(GetEntrySize, &usage);
|
||||
return usage;
|
||||
}
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsLocalStorageCache_h___
|
||||
#define nsLocalStorageCache_h___
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class DOMStorageImpl;
|
||||
class nsSessionStorageEntry;
|
||||
|
||||
/**
|
||||
* nsScopeCache is the representation of a single scope's contents
|
||||
* and its flush state.
|
||||
*
|
||||
* Typical state progression for a scope:
|
||||
*
|
||||
* 1. Scope is loaded into memory from disk:
|
||||
* mIsDirty = false;
|
||||
* mIsFlushPending = false;
|
||||
*
|
||||
* 2. A key is modified/deleted in the scope, or the entire scope is deleted:
|
||||
* mIsDirty = *true*;
|
||||
* mIsFlushPending = false;
|
||||
*
|
||||
* 3. Flush timer fires and dirty data is collected:
|
||||
* mIsDirty = *false*;
|
||||
* mIsFlushPending = *true*;
|
||||
*
|
||||
* 4a. If the flush succeds:
|
||||
* mIsDirty = false;
|
||||
* mIsFlushPending = *false*;
|
||||
*
|
||||
* 4b. If the flush fails for some reason
|
||||
* e.g. profile dir is on network storage and the network is flaky
|
||||
*
|
||||
* mIsDirty = *true*;
|
||||
* mIsFlushPending = *false*;
|
||||
*
|
||||
* Note that in this case, no data would be lost.
|
||||
* On next flush, the scope would get deleted from the DB to bring
|
||||
* the DB back to a known state and then ALL the cached keys would
|
||||
* get written out.
|
||||
* Failed flushes ought to be very rare.
|
||||
*/
|
||||
class nsScopeCache
|
||||
{
|
||||
public:
|
||||
nsScopeCache();
|
||||
nsresult AddEntry(const nsAString& aKey,
|
||||
const nsAString& aValue,
|
||||
bool aSecure);
|
||||
nsresult GetAllKeys(DOMStorageImpl* aStorage,
|
||||
nsTHashtable<nsSessionStorageEntry>* aKeys) const;
|
||||
bool GetKey(const nsAString& aKey, nsAString& aValue, bool* aSecure) const;
|
||||
nsresult SetKey(const nsAString& aKey, const nsAString& aValue, bool aSecure);
|
||||
void SetSecure(const nsAString& aKey, bool aSecure);
|
||||
void RemoveKey(const nsAString& aKey);
|
||||
void DeleteScope();
|
||||
int32_t GetQuotaUsage() const;
|
||||
|
||||
/**
|
||||
* A single key/value pair in a scope.
|
||||
*/
|
||||
class KeyEntry
|
||||
{
|
||||
public:
|
||||
KeyEntry() : mIsSecure(false), mIsDirty(false) {}
|
||||
nsString mValue;
|
||||
bool mIsSecure;
|
||||
bool mIsDirty;
|
||||
};
|
||||
|
||||
private:
|
||||
friend class nsLocalStorageCache;
|
||||
|
||||
// Scope data
|
||||
// -- Whether a scope should be deleted from DB first before any DB inserts
|
||||
bool mWasScopeDeleted;
|
||||
// -- Keys to be deleted from DB on next flush
|
||||
nsTArray<nsString> mDeletedKeys;
|
||||
// -- Mapping of scope keys to scope values
|
||||
nsClassHashtable<nsStringHashKey, KeyEntry> mTable;
|
||||
|
||||
// Scope state:
|
||||
// -- The last time the scope was used, helps decide whether to evict scope
|
||||
PRIntervalTime mAccessTime;
|
||||
// -- Whether the scope needs to be flushed to disk
|
||||
// Note that dirty scopes can't be evicted
|
||||
bool mIsDirty;
|
||||
// -- Prevents eviction + is used to recover from bad flushes
|
||||
bool mIsFlushPending;
|
||||
};
|
||||
|
||||
/**
|
||||
* nsLocalStorageCache manages all the cached LocalStorage scopes.
|
||||
*/
|
||||
class nsLocalStorageCache
|
||||
{
|
||||
public:
|
||||
nsLocalStorageCache();
|
||||
|
||||
nsScopeCache* GetScope(const nsACString& aScopeName);
|
||||
void AddScope(const nsACString& aScopeName, nsScopeCache* aScopeCache);
|
||||
bool IsScopeCached(const nsACString& aScopeName) const;
|
||||
uint32_t Count() const;
|
||||
|
||||
int32_t GetQuotaUsage(const nsACString& aQuotaKey) const;
|
||||
void MarkMatchingScopesDeleted(const nsACString& aPattern);
|
||||
void ForgetAllScopes();
|
||||
|
||||
/**
|
||||
* Collection of all the dirty data across all scopes.
|
||||
*/
|
||||
struct FlushData
|
||||
{
|
||||
public:
|
||||
struct ChangeSet
|
||||
{
|
||||
bool mWasDeleted;
|
||||
nsTArray<nsString>* mDeletedKeys;
|
||||
nsTArray<nsString> mDirtyKeys;
|
||||
nsTArray<nsScopeCache::KeyEntry*> mDirtyValues;
|
||||
};
|
||||
// A scope's name in mScopeNames has the same index as the
|
||||
// scope's ChangeSet in mChanged
|
||||
nsTArray<nsCString> mScopeNames;
|
||||
nsTArray<ChangeSet> mChanged;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fills the FlushData structure with the dirty items in the cache.
|
||||
*/
|
||||
void GetFlushData(FlushData* aData) const;
|
||||
|
||||
/**
|
||||
* These methods update the state of each cached scope and
|
||||
* its keys. See description in nsScopeCache.
|
||||
*/
|
||||
void MarkScopesPending();
|
||||
void MarkScopesFlushed();
|
||||
void MarkFlushFailed();
|
||||
|
||||
/**
|
||||
* Evicts scopes not accessed since aMinAccessTime.
|
||||
* Returns info about evicted scopes in aEvicted & aEvictedSize.
|
||||
*/
|
||||
void EvictScopes(nsTArray<nsCString>& aEvicted,
|
||||
nsTArray<int32_t>& aEvictedSize);
|
||||
|
||||
private:
|
||||
|
||||
static PLDHashOperator
|
||||
GetDirtyDataEnum(const nsACString& aScopeName,
|
||||
nsScopeCache* aScopeCache,
|
||||
void* aParams);
|
||||
|
||||
enum FlushState {
|
||||
FLUSH_PENDING,
|
||||
FLUSHED,
|
||||
FLUSH_FAILED
|
||||
};
|
||||
static PLDHashOperator
|
||||
SetFlushStateEnum(const nsACString& aScopeName,
|
||||
nsAutoPtr<nsScopeCache>& aScopeCache,
|
||||
void* aParams);
|
||||
|
||||
static PLDHashOperator
|
||||
EvictEnum(const nsACString& aScopeName,
|
||||
nsAutoPtr<nsScopeCache>& aScopeCache,
|
||||
void* aParams);
|
||||
|
||||
private:
|
||||
// Scopes currently cached in memory
|
||||
// Maps: scope name -> scope's cache info
|
||||
nsClassHashtable<nsCStringHashKey, nsScopeCache> mScopeCaches;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -13,7 +13,6 @@ include $(DEPTH)/config/autoconf.mk
|
|||
|
||||
MOCHITEST_FILES = \
|
||||
frameAppIsolation.html \
|
||||
frameBug624047.html \
|
||||
frameChromeSlave.html \
|
||||
frameKeySync.html \
|
||||
frameMasterEqual.html \
|
||||
|
@ -27,9 +26,10 @@ MOCHITEST_FILES = \
|
|||
interOriginFrame.js \
|
||||
interOriginTest.js \
|
||||
interOriginTest2.js \
|
||||
localStorageCommon.js \
|
||||
test_appIsolation.html \
|
||||
test_brokenUTF-16.html \
|
||||
test_bug624047.html \
|
||||
test_bug600307-DBOps.html \
|
||||
test_bug746272-1.html \
|
||||
test_bug746272-2.html \
|
||||
test_cookieBlock.html \
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>slave for bug 624047 test</title>
|
||||
|
||||
<script type="text/javascript" src="interOriginFrame.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
function doStep()
|
||||
{
|
||||
localStorage.name = 1;
|
||||
var timer = setInterval(function() {
|
||||
is(localStorage.name, 1, "Value is still present");
|
||||
}, 1000);
|
||||
|
||||
setTimeout(function() {
|
||||
clearTimeout(timer);
|
||||
localStorage.clear();
|
||||
postMsg("done");
|
||||
}, 12000);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="postMsg('frame loaded');">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,46 @@
|
|||
function localStorageFlush(cb)
|
||||
{
|
||||
var ob = {
|
||||
observe : function(sub, top, dat)
|
||||
{
|
||||
os().removeObserver(ob, "domstorage-test-flushed");
|
||||
cb();
|
||||
}
|
||||
};
|
||||
os().addObserver(ob, "domstorage-test-flushed", false);
|
||||
notify("domstorage-test-flush-force");
|
||||
}
|
||||
|
||||
function localStorageReload()
|
||||
{
|
||||
notify("domstorage-test-reload");
|
||||
}
|
||||
|
||||
function localStorageFlushAndReload(cb)
|
||||
{
|
||||
localStorageFlush(function() {
|
||||
localStorageReload();
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
function localStorageClearAll()
|
||||
{
|
||||
os().notifyObservers(null, "cookie-changed", "cleared");
|
||||
}
|
||||
|
||||
function localStorageClearDomain(domain)
|
||||
{
|
||||
os().notifyObservers(null, "browser:purge-domain-data", domain);
|
||||
}
|
||||
|
||||
function os()
|
||||
{
|
||||
return SpecialPowers.Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(SpecialPowers.Ci.nsIObserverService);
|
||||
}
|
||||
|
||||
function notify(top)
|
||||
{
|
||||
os().notifyObservers(null, top, null);
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>bug 600307</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="localStorageCommon.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
/*
|
||||
This is strictly implementation specific test for dom storage code from bug 600307.
|
||||
It exercises code for asynchronous data flushing with regard to cache life time and its preload.
|
||||
|
||||
"flush + reload" means to tell the database to store all pending data then wipe the cached content and reload it from the db
|
||||
|
||||
"reload" only means to simulate situation when there is a pending flush for an origin but a new cache is about to preload
|
||||
which is a corner case happening rarely ; this helps check the next preload operation will load correct data from the database anyway
|
||||
|
||||
Case 1: set | flush + reload | remove | set | remove | flush + reload | get ?= null
|
||||
checks coalescing optimization for single key changes does work correctly for repeated removals of a key
|
||||
|
||||
Case 2: set | set | clear | flush + reload | count ?= 0
|
||||
checks whether clear operation superseeds setting keys, the database must be empty for the origin
|
||||
|
||||
Case 3: set | set | clear | reload | count ?= 0
|
||||
check the corner case when a data clear flush is pending but a new cache is about to preload
|
||||
*/
|
||||
|
||||
function startTest()
|
||||
{
|
||||
// Have an untouched land
|
||||
localStorage.clear();
|
||||
|
||||
// Initial flush
|
||||
localStorageFlush(function() {
|
||||
is(localStorage.length, 0, "localStorage empty at the test start");
|
||||
|
||||
// Basic test 1 (set a key, check presence after reload):
|
||||
localStorage.setItem("item", "value");
|
||||
localStorageFlushAndReload(function() {
|
||||
is(localStorage.getItem("item"), "value", "Basic persistance works");
|
||||
is(localStorage.length, 1, "1 item in localStorage");
|
||||
|
||||
// Basic test 2 (set a key twice, check presence of the last value):
|
||||
localStorage.setItem("item", "value2");
|
||||
localStorage.setItem("item", "value3");
|
||||
localStorageFlushAndReload(function() {
|
||||
is(localStorage.getItem("item"), "value3", "Basic persistance 2 works");
|
||||
|
||||
// Basic test 3 (remove a key, check it has been deleted):
|
||||
localStorage.removeItem("item");
|
||||
localStorageFlushAndReload(function() {
|
||||
is(localStorage.getItem("item"), null, "Basic delete works");
|
||||
|
||||
// Basic test 4 (set and remove a key, check it is not present):
|
||||
localStorage.setItem("item", "value4");
|
||||
localStorage.removeItem("item");
|
||||
localStorageFlushAndReload(function() {
|
||||
is(localStorage.getItem("item"), null, "Basic delete 2 works");
|
||||
|
||||
|
||||
// Case 1:
|
||||
localStorage.setItem("item", "value");
|
||||
localStorageFlushAndReload(function() {
|
||||
localStorage.removeItem("item");
|
||||
localStorage.setItem("item", "value2");
|
||||
localStorage.removeItem("item");
|
||||
localStorageFlushAndReload(function() {
|
||||
is(localStorage.getItem("item"), null, "Item deleted in case 1");
|
||||
|
||||
// Case 2:
|
||||
localStorage.setItem("item", "value");
|
||||
localStorage.setItem("item2", "value2");
|
||||
localStorage.clear();
|
||||
localStorageFlushAndReload(function() {
|
||||
is(localStorage.length, 0, "localStorage clean in case 2");
|
||||
|
||||
// Case 3:
|
||||
localStorageFlush(function() {
|
||||
localStorage.setItem("item", "value");
|
||||
localStorage.setItem("item2", "value2");
|
||||
localStorage.clear();
|
||||
localStorageReload();
|
||||
is(localStorage.length, 0, "localStorage clean in case 4");
|
||||
|
||||
if (SpecialPowers.Cc["@mozilla.org/xre/app-info;1"].getService(
|
||||
SpecialPowers.Ci.nsIXULRuntime).processType != SpecialPowers.Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
|
||||
// Following tests cannot be run in a child/plugin process type
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Cookies clean 1
|
||||
localStorageFlush(function() {
|
||||
localStorage.setItem("item", "value");
|
||||
localStorageClearAll();
|
||||
is(localStorage.length, 0, "localStorage clean after cookies deletion");
|
||||
localStorage.setItem("item2", "value2");
|
||||
is(localStorage.getItem("item"), null, "Unexpected key 1, cookies delete");
|
||||
is(localStorage.getItem("item2"), "value2", "Expected key 2, cookies delete");
|
||||
localStorageFlushAndReload(function() {
|
||||
is(localStorage.getItem("item"), null, "Unexpected key 1, cookies delete");
|
||||
is(localStorage.getItem("item2"), "value2", "Expected key 2, cookies delete");
|
||||
|
||||
// Cookies clean 2
|
||||
localStorage.clear();
|
||||
localStorageFlush(function() {
|
||||
localStorage.setItem("item", "value");
|
||||
localStorageClearAll();
|
||||
is(localStorage.length, 0, "localStorage clean after cookies deletion 2");
|
||||
localStorageFlushAndReload(function() {
|
||||
is(localStorage.length, 0, "localStorage clean after cookies deletion 2");
|
||||
|
||||
|
||||
// Domain clean 1
|
||||
localStorageFlush(function() {
|
||||
localStorage.setItem("item", "value");
|
||||
localStorageClearDomain("test");
|
||||
is(localStorage.length, 0, "localStorage clean after domain deletion");
|
||||
localStorage.setItem("item2", "value2");
|
||||
is(localStorage.getItem("item"), null, "Unexpected key 1, domain delete");
|
||||
is(localStorage.getItem("item2"), "value2", "Expected key 2, domain delete");
|
||||
localStorageFlushAndReload(function() {
|
||||
is(localStorage.getItem("item"), null, "Unexpected key 1, domain delete");
|
||||
is(localStorage.getItem("item2"), "value2", "Expected key 2, domain delete");
|
||||
|
||||
// Domain clean 2
|
||||
localStorage.clear();
|
||||
localStorageFlush(function() {
|
||||
localStorage.setItem("item", "value");
|
||||
localStorageClearDomain("test");
|
||||
is(localStorage.length, 0, "localStorage clean after domain deletion 2");
|
||||
localStorageFlushAndReload(function() {
|
||||
is(localStorage.length, 0, "localStorage clean after domain deletion 2");
|
||||
|
||||
SimpleTest.finish();
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="startTest();">
|
||||
</body>
|
||||
</html>
|
|
@ -1,64 +0,0 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>bug 624047</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="interOriginTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
/*
|
||||
This test does the folliwing:
|
||||
- loads a page in an iframe that stores a value to localStorage
|
||||
- sooner then in 5 seconds reloads the page
|
||||
- now the page later then 5 seconds after load in the first step checks the
|
||||
value is still present in localStorage (what is expected)
|
||||
- if not, the bug is still present
|
||||
*/
|
||||
|
||||
function flushTables()
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var storageManager = Components.classes["@mozilla.org/dom/storagemanager;1"]
|
||||
.getService(Components.interfaces.nsIObserver);
|
||||
storageManager.observe(null, "domstorage-flush-force", null);
|
||||
}
|
||||
|
||||
function startTest()
|
||||
{
|
||||
slaveOrigin = "http://sub2.test2.example.org";
|
||||
slave = document.getElementById("__test_frame").contentWindow;
|
||||
|
||||
flushTables();
|
||||
slave.location = slaveOrigin + slavePath + "frameBug624047.html";
|
||||
setTimeout(function() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
slaveLoadsPending = 1;
|
||||
slave.location.reload();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function doNextTest()
|
||||
{
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function doStep()
|
||||
{
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="startTest();">
|
||||
This test takes about 15s to complete... Please wait...
|
||||
<br/>
|
||||
<iframe src="" id="__test_frame"></iframe>
|
||||
</body>
|
||||
</html>
|
|
@ -7,7 +7,31 @@
|
|||
|
||||
<script type="text/javascript">
|
||||
|
||||
var MOZ_STORAGE_CHANGED_COUNT = 21;
|
||||
var expectedEvents = [
|
||||
"empty,null,",
|
||||
"empty,,null",
|
||||
"key1,null,value1",
|
||||
"key1,value1,null",
|
||||
"key1,null,value1",
|
||||
"key2,null,value2",
|
||||
"key2,value2,value2-2",
|
||||
"key1,value1,value1-2",
|
||||
"key2,value2-2,null",
|
||||
"testA,null,valueA",
|
||||
"testA,valueA,valueA2",
|
||||
"testB,null,valueB",
|
||||
"testB,valueB,valueB2",
|
||||
"testC,null,valueC",
|
||||
"testC,valueC,valueC2",
|
||||
"testC,valueC2,null",
|
||||
"testC,null,null",
|
||||
"testC,null,null",
|
||||
"null,null,test",
|
||||
"null,test,null",
|
||||
"null,null,test",
|
||||
"null,test,null",
|
||||
"null,null,null"
|
||||
];
|
||||
|
||||
function startTest()
|
||||
{
|
||||
|
@ -28,12 +52,14 @@ function startTest()
|
|||
is(typeof localStorage["nonexisting2"], "undefined", "['nonexisting2'] is undefined");
|
||||
is(typeof localStorage.nonexisting2, "undefined", "nonexisting2 is undefined");
|
||||
|
||||
var mozStorageChangedReceived = 0;
|
||||
var localStorageCopy = localStorage;
|
||||
|
||||
function onStorageChanged(e) {
|
||||
if (e.storageArea == localStorageCopy)
|
||||
mozStorageChangedReceived++;
|
||||
if (e.storageArea == localStorageCopy) {
|
||||
ok(expectedEvents.length > 0, "Not more then expected events encountered");
|
||||
var receivedEvent = e.key + "," + e.oldValue + "," + e.newValue;
|
||||
is(receivedEvent, expectedEvents.shift(), "Expected event data: " + receivedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for MozStorageChanged
|
||||
|
@ -204,8 +230,7 @@ function startTest()
|
|||
|
||||
SimpleTest.executeSoon(function () {
|
||||
SpecialPowers.removeChromeEventListener("MozStorageChanged", onStorageChanged, false);
|
||||
is(mozStorageChangedReceived, MOZ_STORAGE_CHANGED_COUNT,
|
||||
"received the correct number of events");
|
||||
is(expectedEvents.length, 0, "received the correct number of events");
|
||||
|
||||
localStorage.clear();
|
||||
SimpleTest.finish();
|
||||
|
|
|
@ -14,12 +14,12 @@ function startTest()
|
|||
.getService(Components.interfaces.nsIIOService);
|
||||
var ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Components.interfaces.nsIScriptSecurityManager);
|
||||
var dsm = Components.classes["@mozilla.org/dom/storagemanager;1"]
|
||||
var dsm = Components.classes["@mozilla.org/dom/localStorage-manager;1"]
|
||||
.getService(Components.interfaces.nsIDOMStorageManager);
|
||||
|
||||
var uri = ios.newURI(url, "", null);
|
||||
var principal = ssm.getNoAppCodebasePrincipal(uri);
|
||||
var storage = dsm.getLocalStorageForPrincipal(principal, "");
|
||||
var storage = dsm.createStorage(principal, "");
|
||||
|
||||
storage.setItem("chromekey", "chromevalue");
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ MOCHITEST_FILES = \
|
|||
frameNotEqual.html \
|
||||
file_http.html \
|
||||
file_https.html \
|
||||
test_cookieSession.html \
|
||||
test_sessionStorageBaseSessionOnly.html \
|
||||
test_sessionStorageBase.html \
|
||||
test_sessionStorageClone.html \
|
||||
test_sessionStorageReplace.html \
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>cookie per-session only test</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
/*
|
||||
Set cookie access to be just per session and store to the sessionStorage.
|
||||
Content stored must prevail only for session of the browser, so it must
|
||||
be accessible in another window we try to access that key in the same
|
||||
storage.
|
||||
*/
|
||||
|
||||
function startTest()
|
||||
{
|
||||
var io = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(SpecialPowers.Ci.nsIIOService);
|
||||
var uri = io.newURI(window.location, "", null);
|
||||
var cp = SpecialPowers.Cc["@mozilla.org/cookie/permission;1"]
|
||||
.getService(SpecialPowers.Ci.nsICookiePermission);
|
||||
|
||||
// ==================== start of the test ============================
|
||||
|
||||
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_DEFAULT);
|
||||
|
||||
sessionStorage.setItem("persistent1", "persistent value 1");
|
||||
sessionStorage.setItem("persistent2", "persistent value 2");
|
||||
|
||||
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_SESSION);
|
||||
|
||||
sessionStorage.setItem("session only", "session value");
|
||||
is(sessionStorage.getItem("session only"), "session value", "Value present when cookies in session-only mode");
|
||||
is(sessionStorage.getItem("persistent1"), "persistent value 1", "Persistent value present");
|
||||
is(sessionStorage.getItem("persistent2"), "persistent value 2", "Persistent value present");
|
||||
|
||||
sessionStorage.setItem("persistent1", "changed persistent value 1");
|
||||
sessionStorage.removeItem("persistent2");
|
||||
|
||||
is(sessionStorage.getItem("session only"), "session value", "Value present when cookies in session-only mode");
|
||||
is(sessionStorage.getItem("persistent1"), "changed persistent value 1", "Persistent value present");
|
||||
is(sessionStorage.getItem("persistent2"), null, "Persistent value removed");
|
||||
|
||||
// This clear has to delete only changes made in session only mode
|
||||
sessionStorage.clear();
|
||||
|
||||
is(sessionStorage.getItem("session only"), null, "Value not present when cookies in session-only mode after delete");
|
||||
is(sessionStorage.getItem("persistent1"), null, "Persistent value not present in session only after delete");
|
||||
is(sessionStorage.getItem("persistent2"), null, "Persistent value not present in session only after delete");
|
||||
|
||||
sessionStorage.setItem("session only 2", "must be deleted on drop of session-only cookies permissions");
|
||||
|
||||
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_DEFAULT);
|
||||
|
||||
is(sessionStorage.getItem("session only"), null, "No value when cookies are in default mode");
|
||||
is(sessionStorage.getItem("session only 2"), null, "No value when cookies are in default mode");
|
||||
is(sessionStorage.getItem("persistent1"), "persistent value 1", "Persistent value present");
|
||||
is(sessionStorage.getItem("persistent2"), "persistent value 2", "Persistent value present");
|
||||
|
||||
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_SESSION);
|
||||
|
||||
is(sessionStorage.getItem("session only"), null, "Value not present when cookies in session-only mode after delete");
|
||||
is(sessionStorage.getItem("session only 2"), null, "Value not present when cookies in session-only mode after delete");
|
||||
is(sessionStorage.getItem("persistent1"), "persistent value 1", "Persistent value present again");
|
||||
is(sessionStorage.getItem("persistent2"), "persistent value 2", "Persistent value present again");
|
||||
|
||||
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_DEFAULT);
|
||||
|
||||
sessionStorage.clear();
|
||||
|
||||
is(sessionStorage.getItem("session only"), null, "No value when cookies are in default mode");
|
||||
is(sessionStorage.getItem("persistent1"), null, "Persistent value not present after delete");
|
||||
is(sessionStorage.getItem("persistent2"), null, "Persistent value not present after delete");
|
||||
|
||||
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_SESSION);
|
||||
|
||||
is(sessionStorage.getItem("session only"), null, "Value not present when cookies in session-only mode after delete");
|
||||
is(sessionStorage.getItem("session only 2"), null, "No value when cookies are in default mode");
|
||||
is(sessionStorage.getItem("persistent1"), null, "Persistent value not present in session only after delete");
|
||||
is(sessionStorage.getItem("persistent2"), null, "Persistent value not present in session only after delete");
|
||||
|
||||
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_DEFAULT);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="startTest();">
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -7,7 +7,18 @@
|
|||
|
||||
<script type="text/javascript">
|
||||
|
||||
var MOZ_STORAGE_CHANGED_COUNT = 8;
|
||||
var expectedEvents = [
|
||||
"empty,null,",
|
||||
"empty,,null",
|
||||
"key1,null,value1",
|
||||
"key1,value1,null",
|
||||
"key1,null,value1",
|
||||
"key2,null,value2",
|
||||
"key2,value2,value2-2",
|
||||
"key1,value1,value1-2",
|
||||
"key2,value2-2,null",
|
||||
"null,null,null"
|
||||
];
|
||||
|
||||
function setup() {
|
||||
sessionStorage.clear();
|
||||
|
@ -37,8 +48,11 @@ function startTest()
|
|||
var sessionStorageCopy = sessionStorage;
|
||||
|
||||
function onStorageChanged(e) {
|
||||
if (e.storageArea == sessionStorageCopy)
|
||||
mozStorageChangedReceived++;
|
||||
if (e.storageArea == sessionStorageCopy) {
|
||||
ok(expectedEvents.length > 0, "Not more then expected events encountered");
|
||||
var receivedEvent = e.key + "," + e.oldValue + "," + e.newValue;
|
||||
is(receivedEvent, expectedEvents.shift(), "Expected event data: " + receivedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for MozStorageChanged
|
||||
|
@ -148,8 +162,7 @@ function startTest()
|
|||
|
||||
SimpleTest.executeSoon(function () {
|
||||
SpecialPowers.removeChromeEventListener("MozStorageChanged", onStorageChanged, false);
|
||||
is(mozStorageChangedReceived, MOZ_STORAGE_CHANGED_COUNT,
|
||||
"received the correct number of events");
|
||||
is(expectedEvents.length, 0, "received the correct number of events");
|
||||
|
||||
sessionStorage.clear();
|
||||
SimpleTest.finish();
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>sessionStorage basic test, while in sesison only mode</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function startTest()
|
||||
{
|
||||
var io = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(SpecialPowers.Ci.nsIIOService);
|
||||
var uri = io.newURI(window.location, "", null);
|
||||
var cp = SpecialPowers.Cc["@mozilla.org/cookie/permission;1"]
|
||||
.getService(SpecialPowers.Ci.nsICookiePermission);
|
||||
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_SESSION);
|
||||
|
||||
|
||||
// Initially check the sessionStorage is empty
|
||||
is(sessionStorage.length, 0, "The storage is empty [1]");
|
||||
is(sessionStorage.key(0), null, "key() should return null for out-of-bounds access");
|
||||
is(sessionStorage.key(-1), null, "key() should return null for out-of-bounds access");
|
||||
is(sessionStorage.key(1), null, "key() should return null for out-of-bounds access");
|
||||
is(sessionStorage.getItem("nonexisting"), null, "Nonexisting item is null (getItem())");
|
||||
is(sessionStorage["nonexisting"], undefined, "Nonexisting item is undefined (array access)");
|
||||
is(sessionStorage.nonexisting, undefined, "Nonexisting item is undefined (property access)");
|
||||
sessionStorage.removeItem("nonexisting"); // Just check there is no exception
|
||||
|
||||
is(typeof sessionStorage.getItem("nonexisting"), "object", "getItem('nonexisting') is object");
|
||||
is(typeof sessionStorage["nonexisting"], "undefined", "['nonexisting'] is undefined");
|
||||
is(typeof sessionStorage.nonexisting, "undefined", "nonexisting is undefined");
|
||||
is(typeof sessionStorage.getItem("nonexisting2"), "object", "getItem('nonexisting2') is object");
|
||||
is(typeof sessionStorage["nonexisting2"], "undefined", "['nonexisting2'] is undefined");
|
||||
is(typeof sessionStorage.nonexisting2, "undefined", "nonexisting2 is undefined");
|
||||
|
||||
// add an empty-value key
|
||||
sessionStorage.setItem("empty", "");
|
||||
is(sessionStorage.getItem("empty"), "", "Empty value (getItem())");
|
||||
is(sessionStorage["empty"], "", "Empty value (array access)");
|
||||
is(sessionStorage.empty, "", "Empty value (property access)");
|
||||
is(typeof sessionStorage.getItem("empty"), "string", "getItem('empty') is string");
|
||||
is(typeof sessionStorage["empty"], "string", "['empty'] is string");
|
||||
is(typeof sessionStorage.empty, "string", "empty is string");
|
||||
sessionStorage.removeItem("empty");
|
||||
is(sessionStorage.length, 0, "The storage has no keys");
|
||||
is(sessionStorage.getItem("empty"), null, "empty item is null (getItem())");
|
||||
is(sessionStorage["empty"], undefined, "empty item is undefined (array access)");
|
||||
is(sessionStorage.empty, undefined, "empty item is undefined (property access)");
|
||||
is(typeof sessionStorage.getItem("empty"), "object", "getItem('empty') is object");
|
||||
is(typeof sessionStorage["empty"], "undefined", "['empty'] is undefined");
|
||||
is(typeof sessionStorage.empty, "undefined", "empty is undefined");
|
||||
|
||||
// add one key, check it is there
|
||||
sessionStorage.setItem("key1", "value1");
|
||||
is(sessionStorage.length, 1, "The storage has one key-value pair");
|
||||
is(sessionStorage.key(0), "key1");
|
||||
is(sessionStorage.key(-1), null, "key() should return null for out-of-bounds access");
|
||||
is(sessionStorage.key(1), null, "key() should return null for out-of-bounds access");
|
||||
|
||||
// check all access method give the correct result
|
||||
// and are of the correct type
|
||||
is(sessionStorage.getItem("key1"), "value1", "getItem('key1') == value1");
|
||||
is(sessionStorage["key1"], "value1", "['key1'] == value1");
|
||||
is(sessionStorage.key1, "value1", "key1 == value1");
|
||||
|
||||
is(typeof sessionStorage.getItem("key1"), "string", "getItem('key1') is string");
|
||||
is(typeof sessionStorage["key1"], "string", "['key1'] is string");
|
||||
is(typeof sessionStorage.key1, "string", "key1 is string");
|
||||
|
||||
// remove the previously added key and check the storage is empty
|
||||
sessionStorage.removeItem("key1");
|
||||
is(sessionStorage.length, 0, "The storage is empty [2]");
|
||||
is(sessionStorage.key(0), null, "key() should return null for out-of-bounds access");
|
||||
is(sessionStorage.getItem("key1"), null, "\'key1\' removed");
|
||||
|
||||
is(typeof sessionStorage.getItem("key1"), "object", "getItem('key1') is object");
|
||||
is(typeof sessionStorage["key1"], "undefined", "['key1'] is undefined");
|
||||
is(typeof sessionStorage.key1, "undefined", "key1 is undefined");
|
||||
|
||||
// add one key, check it is there
|
||||
sessionStorage.setItem("key1", "value1");
|
||||
is(sessionStorage.length, 1, "The storage has one key-value pair");
|
||||
is(sessionStorage.key(0), "key1");
|
||||
is(sessionStorage.getItem("key1"), "value1");
|
||||
|
||||
// add a second key
|
||||
sessionStorage.setItem("key2", "value2");
|
||||
is(sessionStorage.length, 2, "The storage has two key-value pairs");
|
||||
is(sessionStorage.getItem("key1"), "value1");
|
||||
is(sessionStorage.getItem("key2"), "value2");
|
||||
var firstKey = sessionStorage.key(0);
|
||||
var secondKey = sessionStorage.key(1);
|
||||
ok((firstKey == 'key1' && secondKey == 'key2') ||
|
||||
(firstKey == 'key2' && secondKey == 'key1'),
|
||||
'key() API works.');
|
||||
|
||||
// change the second key
|
||||
sessionStorage.setItem("key2", "value2-2");
|
||||
is(sessionStorage.length, 2, "The storage has two key-value pairs");
|
||||
is(sessionStorage.key(0), firstKey); // After key value changes the order must be preserved
|
||||
is(sessionStorage.key(1), secondKey);
|
||||
is(sessionStorage.key(-1), null, "key() should return null for out-of-bounds access");
|
||||
is(sessionStorage.key(2), null, "key() should return null for out-of-bounds access");
|
||||
is(sessionStorage.getItem("key1"), "value1");
|
||||
is(sessionStorage.getItem("key2"), "value2-2");
|
||||
|
||||
// change the first key
|
||||
sessionStorage.setItem("key1", "value1-2");
|
||||
is(sessionStorage.length, 2, "The storage has two key-value pairs");
|
||||
is(sessionStorage.key(0), firstKey); // After key value changes the order must be preserved
|
||||
is(sessionStorage.key(1), secondKey);
|
||||
is(sessionStorage.key(-1), null, "key() should return null for out-of-bounds access");
|
||||
is(sessionStorage.key(2), null, "key() should return null for out-of-bounds access");
|
||||
is(sessionStorage.getItem("key1"), "value1-2");
|
||||
is(sessionStorage.getItem("key2"), "value2-2");
|
||||
|
||||
// remove the second key
|
||||
sessionStorage.removeItem("key2");
|
||||
is(sessionStorage.length, 1, "The storage has one key-value pair");
|
||||
is(sessionStorage.key(0), "key1");
|
||||
is(sessionStorage.key(-1), null, "key() should return null for out-of-bounds access");
|
||||
is(sessionStorage.key(1), null, "key() should return null for out-of-bounds access");
|
||||
is(sessionStorage.getItem("key1"), "value1-2");
|
||||
|
||||
// JS property test
|
||||
sessionStorage.testA = "valueA";
|
||||
is(sessionStorage.testA, "valueA");
|
||||
is(sessionStorage["testA"], "valueA");
|
||||
is(sessionStorage.getItem("testA"), "valueA");
|
||||
|
||||
sessionStorage.testA = "valueA2";
|
||||
is(sessionStorage.testA, "valueA2");
|
||||
is(sessionStorage["testA"], "valueA2");
|
||||
is(sessionStorage.getItem("testA"), "valueA2");
|
||||
|
||||
sessionStorage["testB"] = "valueB";
|
||||
is(sessionStorage.testB, "valueB");
|
||||
is(sessionStorage["testB"], "valueB");
|
||||
is(sessionStorage.getItem("testB"), "valueB");
|
||||
|
||||
sessionStorage["testB"] = "valueB2";
|
||||
is(sessionStorage.testB, "valueB2");
|
||||
is(sessionStorage["testB"], "valueB2");
|
||||
is(sessionStorage.getItem("testB"), "valueB2");
|
||||
|
||||
sessionStorage.setItem("testC", "valueC");
|
||||
is(sessionStorage.testC, "valueC");
|
||||
is(sessionStorage["testC"], "valueC");
|
||||
is(sessionStorage.getItem("testC"), "valueC");
|
||||
|
||||
sessionStorage.setItem("testC", "valueC2");
|
||||
is(sessionStorage.testC, "valueC2");
|
||||
is(sessionStorage["testC"], "valueC2");
|
||||
is(sessionStorage.getItem("testC"), "valueC2");
|
||||
|
||||
sessionStorage.setItem("testC", null);
|
||||
is("testC" in sessionStorage, true);
|
||||
is(sessionStorage.getItem("testC"), "null");
|
||||
is(sessionStorage["testC"], "null");
|
||||
is(sessionStorage.testC, "null");
|
||||
|
||||
sessionStorage.removeItem("testC");
|
||||
sessionStorage["testC"] = null;
|
||||
is("testC" in sessionStorage, true);
|
||||
is(sessionStorage.getItem("testC"), "null");
|
||||
is(sessionStorage["testC"], "null");
|
||||
is(sessionStorage.testC, "null");
|
||||
|
||||
sessionStorage.setItem(null, "test");
|
||||
is("null" in sessionStorage, true);
|
||||
is(sessionStorage.getItem("null"), "test");
|
||||
is(sessionStorage.getItem(null), "test");
|
||||
is(sessionStorage["null"], "test");
|
||||
sessionStorage.removeItem(null, "test");
|
||||
// bug 350023
|
||||
todo_is("null" in sessionStorage, false);
|
||||
|
||||
sessionStorage.setItem(null, "test");
|
||||
is("null" in sessionStorage, true);
|
||||
sessionStorage.removeItem("null", "test");
|
||||
// bug 350023
|
||||
todo_is("null" in sessionStorage, false);
|
||||
|
||||
// Clear the storage
|
||||
sessionStorage.clear();
|
||||
is(sessionStorage.length, 0, "The storage is empty [3]");
|
||||
is(sessionStorage.key(0), null, "key() should return null for out-of-bounds access");
|
||||
is(sessionStorage.key(-1), null, "key() should return null for out-of-bounds access");
|
||||
is(sessionStorage.key(1), null, "key() should return null for out-of-bounds access");
|
||||
is(sessionStorage.getItem("nonexisting"), null, "Nonexisting item is null");
|
||||
is(sessionStorage.getItem("key1"), null, "key1 removed");
|
||||
is(sessionStorage.getItem("key2"), null, "key2 removed");
|
||||
sessionStorage.removeItem("nonexisting"); // Just check there is no exception
|
||||
sessionStorage.removeItem("key1"); // Just check there is no exception
|
||||
sessionStorage.removeItem("key2"); // Just check there is no exception
|
||||
|
||||
|
||||
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_DEFAULT);
|
||||
|
||||
sessionStorage.clear();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="startTest();">
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -48,9 +48,8 @@
|
|||
#include "nsIWindowProvider.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsISupportsArray.h"
|
||||
#include "nsIDOMStorageObsolete.h"
|
||||
#include "nsIDOMStorage.h"
|
||||
#include "nsPIDOMStorage.h"
|
||||
#include "nsIDOMStorageManager.h"
|
||||
#include "nsIWidget.h"
|
||||
#include "nsFocusManager.h"
|
||||
#include "nsIPresShell.h"
|
||||
|
@ -1000,17 +999,14 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow *aParent,
|
|||
parentDocShell = piWindow->GetDocShell();
|
||||
|
||||
if (subjectPrincipal && parentDocShell) {
|
||||
nsCOMPtr<nsIDOMStorage> storage;
|
||||
parentDocShell->GetSessionStorageForPrincipal(subjectPrincipal,
|
||||
EmptyString(), false,
|
||||
getter_AddRefs(storage));
|
||||
nsCOMPtr<nsPIDOMStorage> piStorage =
|
||||
do_QueryInterface(storage);
|
||||
if (piStorage){
|
||||
storage = piStorage->Clone();
|
||||
newDocShell->AddSessionStorage(
|
||||
piStorage->Principal(),
|
||||
storage);
|
||||
nsCOMPtr<nsIDOMStorageManager> parentStorageManager = do_QueryInterface(parentDocShell);
|
||||
nsCOMPtr<nsIDOMStorageManager> newStorageManager = do_QueryInterface(newDocShell);
|
||||
|
||||
if (parentStorageManager && newStorageManager) {
|
||||
nsCOMPtr<nsIDOMStorage> storage;
|
||||
parentStorageManager->GetStorage(subjectPrincipal, isPrivateBrowsingWindow, getter_AddRefs(storage));
|
||||
if (storage)
|
||||
newStorageManager->CloneStorage(storage);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -119,7 +119,6 @@ members = [
|
|||
'nsIDOMStorage.key',
|
||||
'nsIDOMStorage.removeItem',
|
||||
'nsIDOMStorage.clear',
|
||||
'nsIDOMStorageItem.value',
|
||||
|
||||
# dom/interfaces/xpath
|
||||
'nsIDOMXPathExpression.evaluate',
|
||||
|
|
|
@ -63,17 +63,13 @@
|
|||
#define NS_CANVASRENDERINGCONTEXTWEBGL_CID \
|
||||
{ 0x2fe88332, 0x31c6, 0x4829, { 0xb2, 0x47, 0xa0, 0x7d, 0x8a, 0x7e, 0xe8, 0x0fe } }
|
||||
|
||||
// {8b449142-1eab-4bfa-9830-fab6ebb09774}
|
||||
#define NS_DOMSTORAGE_CID \
|
||||
{ 0x8b449142, 0x1eab, 0x4bfa, { 0x98, 0x30, 0xfa, 0xb6, 0xeb, 0xb0, 0x97, 0x74 } }
|
||||
// {A746DECD-AE74-4d86-8E75-4FDA81A9BE90}
|
||||
#define NS_DOMSESSIONSTORAGEMANAGER_CID \
|
||||
{ 0xa746decd, 0xae74, 0x4d86, { 0x8e, 0x75, 0x4f, 0xda, 0x81, 0xa9, 0xbe, 0x90 } }
|
||||
|
||||
// {27AECC62-7777-428e-B34C-5973A47B8298}
|
||||
#define NS_DOMSTORAGE2_CID \
|
||||
{ 0x27aecc62, 0x7777, 0x428e, { 0xb3, 0x4c, 0x59, 0x73, 0xa4, 0x7b, 0x82, 0x98 } }
|
||||
|
||||
// {b88a4712-eb52-4c10-9b85-bf5894b510f0}
|
||||
#define NS_DOMSTORAGEMANAGER_CID \
|
||||
{ 0xb88a4712, 0xeb52, 0x4c10, { 0x9b, 0x85, 0xbf, 0x58, 0x94, 0xb5, 0x10, 0xf0 } }
|
||||
// {656DB07C-AA80-49e4-BCE8-E431BAAE697D}
|
||||
#define NS_DOMLOCALSTORAGEMANAGER_CID \
|
||||
{ 0x656db07c, 0xaa80, 0x49e4, { 0xbc, 0xe8, 0xe4, 0x31, 0xba, 0xae, 0x69, 0x7d } }
|
||||
|
||||
// {93ad72a6-02cd-4716-9626-d47d5ec275ec}
|
||||
#define NS_DOMJSON_CID \
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
#include "nsScriptNameSpaceManager.h"
|
||||
#include "nsIControllerContext.h"
|
||||
#include "nsDOMScriptObjectFactory.h"
|
||||
#include "nsDOMStorage.h"
|
||||
#include "DOMStorageManager.h"
|
||||
#include "nsJSON.h"
|
||||
#include "mozIApplicationClearPrivateDataParams.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
@ -275,8 +275,8 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsBlobProtocolHandler)
|
|||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsMediaStreamProtocolHandler)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsHostObjectURI)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMParser)
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsDOMStorageManager,
|
||||
nsDOMStorageManager::GetInstance)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(DOMSessionStorageManager)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(DOMLocalStorageManager)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsChannelPolicy)
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(IndexedDatabaseManager,
|
||||
IndexedDatabaseManager::FactoryCreate)
|
||||
|
@ -768,8 +768,8 @@ NS_DEFINE_NAMED_CID(NS_XMLHTTPREQUEST_CID);
|
|||
NS_DEFINE_NAMED_CID(NS_EVENTSOURCE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_DOMACTIVITY_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_DOMPARSER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_DOMSTORAGE2_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_DOMSTORAGEMANAGER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_DOMSESSIONSTORAGEMANAGER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_DOMLOCALSTORAGEMANAGER_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_DOMJSON_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_TEXTEDITOR_CID);
|
||||
NS_DEFINE_NAMED_CID(INDEXEDDB_MANAGER_CID);
|
||||
|
@ -1054,8 +1054,8 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
|
|||
{ &kNS_EVENTSOURCE_CID, false, NULL, EventSourceConstructor },
|
||||
{ &kNS_DOMACTIVITY_CID, false, NULL, ActivityConstructor },
|
||||
{ &kNS_DOMPARSER_CID, false, NULL, nsDOMParserConstructor },
|
||||
{ &kNS_DOMSTORAGE2_CID, false, NULL, NS_NewDOMStorage2 },
|
||||
{ &kNS_DOMSTORAGEMANAGER_CID, false, NULL, nsDOMStorageManagerConstructor },
|
||||
{ &kNS_DOMSESSIONSTORAGEMANAGER_CID, false, NULL, DOMSessionStorageManagerConstructor },
|
||||
{ &kNS_DOMLOCALSTORAGEMANAGER_CID, false, NULL, DOMLocalStorageManagerConstructor },
|
||||
{ &kNS_DOMJSON_CID, false, NULL, NS_NewJSON },
|
||||
{ &kNS_TEXTEDITOR_CID, false, NULL, nsPlaintextEditorConstructor },
|
||||
{ &kINDEXEDDB_MANAGER_CID, false, NULL, IndexedDatabaseManagerConstructor },
|
||||
|
@ -1204,8 +1204,8 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
|
|||
{ NS_EVENTSOURCE_CONTRACTID, &kNS_EVENTSOURCE_CID },
|
||||
{ NS_DOMACTIVITY_CONTRACTID, &kNS_DOMACTIVITY_CID },
|
||||
{ NS_DOMPARSER_CONTRACTID, &kNS_DOMPARSER_CID },
|
||||
{ "@mozilla.org/dom/storage;2", &kNS_DOMSTORAGE2_CID },
|
||||
{ "@mozilla.org/dom/storagemanager;1", &kNS_DOMSTORAGEMANAGER_CID },
|
||||
{ "@mozilla.org/dom/localStorage-manager;1", &kNS_DOMLOCALSTORAGEMANAGER_CID },
|
||||
{ "@mozilla.org/dom/sessionStorage-manager;1", &kNS_DOMSESSIONSTORAGEMANAGER_CID },
|
||||
{ "@mozilla.org/dom/json;1", &kNS_DOMJSON_CID },
|
||||
{ "@mozilla.org/editor/texteditor;1", &kNS_TEXTEDITOR_CID },
|
||||
{ INDEXEDDB_MANAGER_CONTRACTID, &kINDEXEDDB_MANAGER_CID },
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
#include "nsXBLWindowKeyHandler.h"
|
||||
#include "nsXBLService.h"
|
||||
#include "txMozillaXSLTProcessor.h"
|
||||
#include "nsDOMStorage.h"
|
||||
#include "nsTreeSanitizer.h"
|
||||
#include "nsCellMap.h"
|
||||
#include "nsTextFrameTextRunCache.h"
|
||||
|
@ -59,7 +58,7 @@
|
|||
#include "nsMathMLAtoms.h"
|
||||
#include "nsMathMLOperators.h"
|
||||
#include "Navigator.h"
|
||||
#include "nsDOMStorageBaseDB.h"
|
||||
#include "DOMStorageObserver.h"
|
||||
#include "DisplayItemClip.h"
|
||||
|
||||
#include "AudioChannelService.h"
|
||||
|
@ -217,9 +216,9 @@ nsLayoutStatics::Initialize()
|
|||
return rv;
|
||||
}
|
||||
|
||||
rv = nsDOMStorageManager::Initialize();
|
||||
rv = DOMStorageObserver::Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ERROR("Could not initialize nsDOMStorageManager");
|
||||
NS_ERROR("Could not initialize DOMStorageObserver");
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -272,8 +271,6 @@ nsLayoutStatics::Initialize()
|
|||
nsCookieService::AppClearDataObserverInit();
|
||||
nsApplicationCacheService::AppClearDataObserverInit();
|
||||
|
||||
nsDOMStorageBaseDB::Init();
|
||||
|
||||
InitializeDateCacheCleaner();
|
||||
|
||||
return NS_OK;
|
||||
|
@ -290,7 +287,7 @@ nsLayoutStatics::Shutdown()
|
|||
#ifdef MOZ_XUL
|
||||
nsXULPopupManager::Shutdown();
|
||||
#endif
|
||||
nsDOMStorageManager::Shutdown();
|
||||
DOMStorageObserver::Shutdown();
|
||||
txMozillaXSLTProcessor::Shutdown();
|
||||
Attr::Shutdown();
|
||||
nsEventListenerManager::Shutdown();
|
||||
|
|
|
@ -411,7 +411,9 @@
|
|||
"dom/tests/mochitest/localstorage/test_localStorageReplace.html":"",
|
||||
|
||||
"dom/tests/mochitest/pointerlock/test_pointerlock-api.html":"",
|
||||
"dom/tests/mochitest/sessionstorage/test_cookieSession.html":"",
|
||||
"dom/tests/mochitest/sessionstorage/test_sessionStorageBase.html":"",
|
||||
"dom/tests/mochitest/sessionstorage/test_sessionStorageBaseSessionOnly.html":"",
|
||||
"dom/tests/mochitest/sessionstorage/test_sessionStorageClone.html":"",
|
||||
"dom/tests/mochitest/sessionstorage/test_sessionStorageHttpHttps.html":"",
|
||||
"dom/tests/mochitest/sessionstorage/test_sessionStorageReplace.html":"",
|
||||
|
|
|
@ -2550,6 +2550,24 @@
|
|||
"kind": "boolean",
|
||||
"description": "DOM: Ranges that are detached on destruction (bug 702948)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_INIT_DATABASE_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
"n_buckets": 10,
|
||||
"extended_statistics_ok": true,
|
||||
"description": "Time to open the localStorage database (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_SHUTDOWN_DATABASE_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
"n_buckets": 10,
|
||||
"extended_statistics_ok": true,
|
||||
"description": "Time to flush and close the localStorage database (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_PRELOAD_PENDING_ON_FIRST_ACCESS": {
|
||||
"kind": "boolean",
|
||||
"description": "True when we had to wait for a pending preload on first access to localStorage data, false otherwise"
|
||||
},
|
||||
"LOCALDOMSTORAGE_GETALLKEYS_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
|
@ -2557,6 +2575,41 @@
|
|||
"extended_statistics_ok": true,
|
||||
"description": "Time to return a list of all keys in domain's LocalStorage (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_GETALLKEYS_BLOCKING_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
"n_buckets": 10,
|
||||
"extended_statistics_ok": true,
|
||||
"description": "Time to block before we return a list of all keys in domain's LocalStorage (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_GETKEY_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
"n_buckets": 10,
|
||||
"extended_statistics_ok": true,
|
||||
"description": "Time to return a key name in domain's LocalStorage (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_GETKEY_BLOCKING_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
"n_buckets": 10,
|
||||
"extended_statistics_ok": true,
|
||||
"description": "Time to block before we return a key name in domain's LocalStorage (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_GETLENGTH_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
"n_buckets": 10,
|
||||
"extended_statistics_ok": true,
|
||||
"description": "Time to return number of keys in domain's LocalStorage (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_GETLENGTH_BLOCKING_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
"n_buckets": 10,
|
||||
"extended_statistics_ok": true,
|
||||
"description": "Time to block before we return number of keys in domain's LocalStorage (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_GETVALUE_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
|
@ -2564,6 +2617,13 @@
|
|||
"extended_statistics_ok": true,
|
||||
"description": "Time to return a value for a key in LocalStorage (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_GETVALUE_BLOCKING_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
"n_buckets": 10,
|
||||
"extended_statistics_ok": true,
|
||||
"description": "Time to block before we return a value for a key in LocalStorage (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_SETVALUE_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
|
@ -2571,6 +2631,13 @@
|
|||
"extended_statistics_ok": true,
|
||||
"description": "Time to set a single key's value in LocalStorage (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
"n_buckets": 10,
|
||||
"extended_statistics_ok": true,
|
||||
"description": "Time to block before we set a single key's value in LocalStorage (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_REMOVEKEY_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
|
@ -2578,33 +2645,40 @@
|
|||
"extended_statistics_ok": true,
|
||||
"description": "Time to remove a single key from LocalStorage (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_REMOVEALL_MS": {
|
||||
"LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
"n_buckets": 10,
|
||||
"extended_statistics_ok": true,
|
||||
"description": "Time to block before we remove a single key from LocalStorage (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_CLEAR_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
"n_buckets": 10,
|
||||
"extended_statistics_ok": true,
|
||||
"description": "Time to clear LocalStorage for all domains (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_FETCH_DOMAIN_MS": {
|
||||
"LOCALDOMSTORAGE_CLEAR_BLOCKING_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
"n_buckets": 10,
|
||||
"extended_statistics_ok": true,
|
||||
"description": "Time to fetch LocalStorage data for a domain (ms)"
|
||||
"description": "Time to block before we clear LocalStorage for all domains (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_FETCH_QUOTA_USE_MS": {
|
||||
"LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
"n_buckets": 10,
|
||||
"extended_statistics_ok": true,
|
||||
"description": "Time to fetch quota use stats for a TLD (ms)"
|
||||
"description": "Time to fetch LocalStorage data before we can clean the cache (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_TIMER_FLUSH_MS": {
|
||||
"LOCALDOMSTORAGE_SESSIONONLY_PRELOAD_BLOCKING_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "3000",
|
||||
"n_buckets": 10,
|
||||
"extended_statistics_ok": true,
|
||||
"description": "Time to flush dirty entries from the cache (ms)"
|
||||
"description": "Time to fetch LocalStorage data before we can expose them as session only data (ms)"
|
||||
},
|
||||
"LOCALDOMSTORAGE_KEY_SIZE_BYTES": {
|
||||
"kind": "exponential",
|
||||
|
|
|
@ -50,7 +50,7 @@ let initTable = [
|
|||
["search", "@mozilla.org/browser/search-service;1", "nsIBrowserSearchService"],
|
||||
#endif
|
||||
["storage", "@mozilla.org/storage/service;1", "mozIStorageService"],
|
||||
["domStorageManager", "@mozilla.org/dom/storagemanager;1", "nsIDOMStorageManager"],
|
||||
["domStorageManager", "@mozilla.org/dom/localStorage-manager;1", "nsIDOMStorageManager"],
|
||||
["strings", "@mozilla.org/intl/stringbundle;1", "nsIStringBundleService"],
|
||||
["telemetry", "@mozilla.org/base/telemetry;1", "nsITelemetry"],
|
||||
["tm", "@mozilla.org/thread-manager;1", "nsIThreadManager"],
|
||||
|
|
|
@ -659,9 +659,9 @@ function test_storage_cleared()
|
|||
let principal = Cc["@mozilla.org/scriptsecuritymanager;1"].
|
||||
getService(Ci.nsIScriptSecurityManager).
|
||||
getNoAppCodebasePrincipal(aURI);
|
||||
let dsm = Cc["@mozilla.org/dom/storagemanager;1"].
|
||||
let dsm = Cc["@mozilla.org/dom/localStorage-manager;1"].
|
||||
getService(Ci.nsIDOMStorageManager);
|
||||
return dsm.getLocalStorageForPrincipal(principal, "");
|
||||
return dsm.createStorage(principal, "");
|
||||
}
|
||||
|
||||
let s = [
|
||||
|
|
|
@ -500,6 +500,12 @@
|
|||
ERROR(NS_ERROR_DOM_RETVAL_UNDEFINED, FAILURE(1013)),
|
||||
ERROR(NS_ERROR_DOM_QUOTA_REACHED, FAILURE(1014)),
|
||||
ERROR(NS_ERROR_DOM_JS_EXCEPTION, FAILURE(1015)),
|
||||
|
||||
/* May be used to indicate when e.g. setting a property value didn't
|
||||
* actually change the value, like for obj.foo = "bar"; obj.foo = "bar";
|
||||
* the second assignment throws NS_SUCCESS_DOM_NO_OPERATION.
|
||||
*/
|
||||
ERROR(NS_SUCCESS_DOM_NO_OPERATION, SUCCESS(1)),
|
||||
#undef MODULE
|
||||
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче