зеркало из 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",
|
desc: "Check that clearing cookies does not clear storage",
|
||||||
setup: function ()
|
setup: function ()
|
||||||
{
|
{
|
||||||
Cc["@mozilla.org/dom/storagemanager;1"]
|
Cc["@mozilla.org/observer-service;1"]
|
||||||
.getService(Ci.nsIObserver)
|
.getService(Ci.nsIObserverService)
|
||||||
.observe(null, "cookie-changed", "cleared");
|
.notifyObservers(null, "cookie-changed", "cleared");
|
||||||
},
|
},
|
||||||
run: function (aSnippetsMap)
|
run: function (aSnippetsMap)
|
||||||
{
|
{
|
||||||
|
|
|
@ -85,7 +85,12 @@ let DomStorage = {
|
||||||
for (let [host, data] in Iterator(aStorageData)) {
|
for (let [host, data] in Iterator(aStorageData)) {
|
||||||
let uri = Services.io.newURI(host, null, null);
|
let uri = Services.io.newURI(host, null, null);
|
||||||
let principal = Services.scriptSecurityManager.getDocShellCodebasePrincipal(uri, aDocShell);
|
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)) {
|
for (let [key, value] in Iterator(data)) {
|
||||||
try {
|
try {
|
||||||
|
@ -110,12 +115,8 @@ let DomStorage = {
|
||||||
let storage;
|
let storage;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Using getSessionStorageForPrincipal instead of
|
let storageManager = aDocShell.QueryInterface(Components.interfaces.nsIDOMStorageManager);
|
||||||
// getSessionStorageForURI just to be able to pass aCreate = false,
|
storage = storageManager.getStorage(aPrincipal);
|
||||||
// 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);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// sessionStorage might throw if it's turned off, see bug 458954
|
// sessionStorage might throw if it's turned off, see bug 458954
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#include "nsPIDOMWindow.h"
|
#include "nsPIDOMWindow.h"
|
||||||
#include "nsFrameLoader.h"
|
#include "nsFrameLoader.h"
|
||||||
#include "nsDOMTouchEvent.h"
|
#include "nsDOMTouchEvent.h"
|
||||||
#include "nsDOMStorage.h"
|
|
||||||
#include "GeckoProfiler.h"
|
#include "GeckoProfiler.h"
|
||||||
#include "GeneratedEvents.h"
|
#include "GeneratedEvents.h"
|
||||||
#include "mozilla/dom/EventTarget.h"
|
#include "mozilla/dom/EventTarget.h"
|
||||||
|
|
|
@ -849,8 +849,6 @@ nsDocShell::Init()
|
||||||
rv = mContentListener->Init();
|
rv = mContentListener->Init();
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
mStorages.Init();
|
|
||||||
|
|
||||||
// We want to hold a strong ref to the loadgroup, so it better hold a weak
|
// 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.
|
// ref to us... use an InterfaceRequestorProxy to do this.
|
||||||
nsCOMPtr<InterfaceRequestorProxy> proxy =
|
nsCOMPtr<InterfaceRequestorProxy> proxy =
|
||||||
|
@ -917,6 +915,7 @@ NS_INTERFACE_MAP_BEGIN(nsDocShell)
|
||||||
NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
|
NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
|
||||||
NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
|
NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
|
||||||
NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
|
NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
|
||||||
|
NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
|
||||||
NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
|
NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
|
||||||
|
|
||||||
///*****************************************************************************
|
///*****************************************************************************
|
||||||
|
@ -2596,199 +2595,70 @@ nsDocShell::HistoryTransactionRemoved(int32_t aIndex)
|
||||||
return NS_OK;
|
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
|
NS_IMETHODIMP
|
||||||
nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
|
nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
|
||||||
const nsAString& aDocumentURI,
|
const nsAString& aDocumentURI,
|
||||||
bool aCreate,
|
bool aCreate,
|
||||||
nsIDOMStorage** aStorage)
|
nsIDOMStorage** aStorage)
|
||||||
{
|
{
|
||||||
NS_ENSURE_ARG_POINTER(aStorage);
|
nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
|
||||||
*aStorage = nullptr;
|
if (!manager) {
|
||||||
|
return NS_ERROR_UNEXPECTED;
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aCreate) {
|
if (aCreate) {
|
||||||
// We are asked to create a new storage object. This indicates
|
return manager->CreateStorage(aPrincipal, aDocumentURI,
|
||||||
// that a new windows wants it. At this moment we "fork" the existing
|
mInPrivateBrowsing, aStorage);
|
||||||
// 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 NS_OK;
|
return manager->GetStorage(aPrincipal, mInPrivateBrowsing, aStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal,
|
nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal,
|
||||||
nsIDOMStorage* aStorage)
|
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);
|
nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(aStorage);
|
||||||
|
nsIPrincipal* storagePrincipal = pistorage->GetPrincipal();
|
||||||
if (pistorage) {
|
if (storagePrincipal != aPrincipal) {
|
||||||
nsCOMPtr<nsIDOMStorage> storage = pistorage->Clone();
|
NS_ERROR("Wanting to add a sessionStorage for different principal");
|
||||||
docShell->AddSessionStorage(pistorage->Principal(), storage);
|
return NS_ERROR_DOM_SECURITY_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PL_DHASH_NEXT;
|
nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
|
||||||
}
|
if (!manager) {
|
||||||
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
return manager->CloneStorage(aStorage);
|
||||||
nsDocShell::CloneSessionStoragesTo(nsIDocShell* aDocShell)
|
|
||||||
{
|
|
||||||
aDocShell->ClearSessionStorages();
|
|
||||||
mStorages.EnumerateRead(CloneSessionStorages, aDocShell);
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
nsDocShell::ClearSessionStorages()
|
|
||||||
{
|
|
||||||
mStorages.Clear();
|
|
||||||
return NS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "nsITextScroll.h"
|
#include "nsITextScroll.h"
|
||||||
#include "nsIDocShellTreeOwner.h"
|
#include "nsIDocShellTreeOwner.h"
|
||||||
#include "nsIContentViewerContainer.h"
|
#include "nsIContentViewerContainer.h"
|
||||||
|
#include "nsIDOMStorageManager.h"
|
||||||
|
|
||||||
#include "nsDocLoader.h"
|
#include "nsDocLoader.h"
|
||||||
#include "nsIURILoader.h"
|
#include "nsIURILoader.h"
|
||||||
|
@ -146,7 +147,8 @@ class nsDocShell : public nsDocLoader,
|
||||||
public nsILoadContext,
|
public nsILoadContext,
|
||||||
public nsIWebShellServices,
|
public nsIWebShellServices,
|
||||||
public nsILinkHandler,
|
public nsILinkHandler,
|
||||||
public nsIClipboardCommands
|
public nsIClipboardCommands,
|
||||||
|
public nsIDOMStorageManager
|
||||||
{
|
{
|
||||||
friend class nsDSURIContentListener;
|
friend class nsDSURIContentListener;
|
||||||
|
|
||||||
|
@ -178,6 +180,7 @@ public:
|
||||||
NS_DECL_NSIOBSERVER
|
NS_DECL_NSIOBSERVER
|
||||||
NS_DECL_NSICLIPBOARDCOMMANDS
|
NS_DECL_NSICLIPBOARDCOMMANDS
|
||||||
NS_DECL_NSIWEBSHELLSERVICES
|
NS_DECL_NSIWEBSHELLSERVICES
|
||||||
|
NS_FORWARD_SAFE_NSIDOMSTORAGEMANAGER(TopSessionStorageManager())
|
||||||
|
|
||||||
NS_IMETHOD Stop() {
|
NS_IMETHOD Stop() {
|
||||||
// Need this here because otherwise nsIWebNavigation::Stop
|
// Need this here because otherwise nsIWebNavigation::Stop
|
||||||
|
@ -628,10 +631,8 @@ protected:
|
||||||
|
|
||||||
void ReattachEditorToWindow(nsISHEntry *aSHEntry);
|
void ReattachEditorToWindow(nsISHEntry *aSHEntry);
|
||||||
|
|
||||||
nsresult GetSessionStorageForURI(nsIURI* aURI,
|
nsCOMPtr<nsIDOMStorageManager> mSessionStorageManager;
|
||||||
const nsSubstring& aDocumentURI,
|
nsIDOMStorageManager* TopSessionStorageManager();
|
||||||
bool create,
|
|
||||||
nsIDOMStorage** aStorage);
|
|
||||||
|
|
||||||
// helpers for executing commands
|
// helpers for executing commands
|
||||||
nsresult GetControllerForCommand(const char *inCommand,
|
nsresult GetControllerForCommand(const char *inCommand,
|
||||||
|
@ -676,9 +677,6 @@ protected:
|
||||||
|
|
||||||
bool HasUnloadedParent();
|
bool HasUnloadedParent();
|
||||||
|
|
||||||
// hash of session storages, keyed by domain
|
|
||||||
nsInterfaceHashtable<nsCStringHashKey, nsIDOMStorage> mStorages;
|
|
||||||
|
|
||||||
// Dimensions of the docshell
|
// Dimensions of the docshell
|
||||||
nsIntRect mBounds;
|
nsIntRect mBounds;
|
||||||
nsString mName;
|
nsString mName;
|
||||||
|
|
|
@ -40,7 +40,7 @@ interface nsIWebBrowserPrint;
|
||||||
interface nsIVariant;
|
interface nsIVariant;
|
||||||
interface nsIPrivacyTransitionObserver;
|
interface nsIPrivacyTransitionObserver;
|
||||||
|
|
||||||
[scriptable, builtinclass, uuid(4277354d-5069-4278-935a-5d596ce9bfbf)]
|
[scriptable, builtinclass, uuid(2b192c9c-dea4-4696-a445-1bef7bc0db6d)]
|
||||||
interface nsIDocShell : nsIDocShellTreeItem
|
interface nsIDocShell : nsIDocShellTreeItem
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -407,6 +407,8 @@ interface nsIDocShell : nsIDocShellTreeItem
|
||||||
void historyPurged(in long numEntries);
|
void historyPurged(in long numEntries);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* @deprecated, use nsIDocShell.QueryInterface(nsIDOMStorageManager) instead.
|
||||||
|
*
|
||||||
* Retrieves the WebApps session storage object for the supplied principal.
|
* Retrieves the WebApps session storage object for the supplied principal.
|
||||||
*
|
*
|
||||||
* @param principal returns a storage for this principal
|
* @param principal returns a storage for this principal
|
||||||
|
@ -420,6 +422,8 @@ interface nsIDocShell : nsIDocShellTreeItem
|
||||||
in boolean create);
|
in boolean create);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* @deprecated, use nsIDocShell.QueryInterface(nsIDOMStorageManager) instead.
|
||||||
|
*
|
||||||
* Add a WebApps session storage object to the docshell.
|
* Add a WebApps session storage object to the docshell.
|
||||||
*
|
*
|
||||||
* @param principal the principal the storage object is associated with
|
* @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);
|
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.
|
* Gets the channel for the currently loaded document, if any.
|
||||||
* For a new document load, this will be the channel of the previous document
|
* For a new document load, this will be the channel of the previous document
|
||||||
|
|
|
@ -250,7 +250,7 @@
|
||||||
#include "nsIImageDocument.h"
|
#include "nsIImageDocument.h"
|
||||||
|
|
||||||
// Storage includes
|
// Storage includes
|
||||||
#include "nsDOMStorage.h"
|
#include "DOMStorage.h"
|
||||||
|
|
||||||
// Device Storage
|
// Device Storage
|
||||||
#include "nsIDOMDeviceStorage.h"
|
#include "nsIDOMDeviceStorage.h"
|
||||||
|
@ -815,9 +815,6 @@ static nsDOMClassInfoData sClassInfoData[] = {
|
||||||
// since a call to addProperty() is always followed by a call to
|
// since a call to addProperty() is always followed by a call to
|
||||||
// setProperty(), except in the case when a getter or setter is set
|
// 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.
|
// 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,
|
NS_DEFINE_CLASSINFO_DATA(Storage, nsStorage2SH,
|
||||||
DOM_DEFAULT_SCRIPTABLE_FLAGS |
|
DOM_DEFAULT_SCRIPTABLE_FLAGS |
|
||||||
nsIXPCScriptable::WANT_NEWRESOLVE |
|
nsIXPCScriptable::WANT_NEWRESOLVE |
|
||||||
|
@ -826,8 +823,6 @@ static nsDOMClassInfoData sClassInfoData[] = {
|
||||||
nsIXPCScriptable::WANT_DELPROPERTY |
|
nsIXPCScriptable::WANT_DELPROPERTY |
|
||||||
nsIXPCScriptable::DONT_ENUM_STATIC_PROPS |
|
nsIXPCScriptable::DONT_ENUM_STATIC_PROPS |
|
||||||
nsIXPCScriptable::WANT_NEWENUMERATE)
|
nsIXPCScriptable::WANT_NEWENUMERATE)
|
||||||
NS_DEFINE_CLASSINFO_DATA(StorageItem, nsDOMGenericSH,
|
|
||||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
|
||||||
|
|
||||||
NS_DEFINE_CLASSINFO_DATA(XULCommandEvent, nsEventSH,
|
NS_DEFINE_CLASSINFO_DATA(XULCommandEvent, nsEventSH,
|
||||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||||
|
@ -2203,19 +2198,10 @@ nsDOMClassInfo::Init()
|
||||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMXPathResult)
|
DOM_CLASSINFO_MAP_ENTRY(nsIDOMXPathResult)
|
||||||
DOM_CLASSINFO_MAP_END
|
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_BEGIN(Storage, nsIDOMStorage)
|
||||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorage)
|
DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorage)
|
||||||
DOM_CLASSINFO_MAP_END
|
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_BEGIN(XULCommandEvent, nsIDOMXULCommandEvent)
|
||||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULCommandEvent)
|
DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULCommandEvent)
|
||||||
DOM_CLASSINFO_UI_EVENT_MAP_ENTRIES
|
DOM_CLASSINFO_UI_EVENT_MAP_ENTRIES
|
||||||
|
|
|
@ -150,9 +150,7 @@ DOMCI_CLASS(XPathNSResolver)
|
||||||
DOMCI_CLASS(XPathResult)
|
DOMCI_CLASS(XPathResult)
|
||||||
|
|
||||||
// WhatWG WebApps Objects
|
// WhatWG WebApps Objects
|
||||||
DOMCI_CLASS(StorageObsolete)
|
|
||||||
DOMCI_CLASS(Storage)
|
DOMCI_CLASS(Storage)
|
||||||
DOMCI_CLASS(StorageItem)
|
|
||||||
|
|
||||||
DOMCI_CLASS(XULCommandEvent)
|
DOMCI_CLASS(XULCommandEvent)
|
||||||
DOMCI_CLASS(CommandEvent)
|
DOMCI_CLASS(CommandEvent)
|
||||||
|
|
|
@ -18,12 +18,15 @@
|
||||||
#include "nsPerformance.h"
|
#include "nsPerformance.h"
|
||||||
#include "nsDOMNavigationTiming.h"
|
#include "nsDOMNavigationTiming.h"
|
||||||
#include "nsBarProps.h"
|
#include "nsBarProps.h"
|
||||||
#include "nsDOMStorage.h"
|
#include "nsIDOMStorage.h"
|
||||||
|
#include "nsIDOMStorageManager.h"
|
||||||
|
#include "DOMStorage.h"
|
||||||
#include "nsDOMOfflineResourceList.h"
|
#include "nsDOMOfflineResourceList.h"
|
||||||
#include "nsError.h"
|
#include "nsError.h"
|
||||||
#include "nsIIdleService.h"
|
#include "nsIIdleService.h"
|
||||||
#include "nsIPowerManagerService.h"
|
#include "nsIPowerManagerService.h"
|
||||||
#include "nsISizeOfEventTarget.h"
|
#include "nsISizeOfEventTarget.h"
|
||||||
|
#include "nsIPermissionManager.h"
|
||||||
|
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
#ifdef GetClassName
|
#ifdef GetClassName
|
||||||
|
@ -2503,9 +2506,38 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PreloadLocalStorage();
|
||||||
|
|
||||||
return NS_OK;
|
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
|
void
|
||||||
nsGlobalWindow::DispatchDOMWindowCreated()
|
nsGlobalWindow::DispatchDOMWindowCreated()
|
||||||
{
|
{
|
||||||
|
@ -2691,19 +2723,6 @@ nsGlobalWindow::DetachFromDocShell()
|
||||||
|
|
||||||
MaybeForgiveSpamCount();
|
MaybeForgiveSpamCount();
|
||||||
CleanUp(false);
|
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
|
void
|
||||||
|
@ -8809,7 +8828,7 @@ nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
|
||||||
"window %x owned sessionStorage "
|
"window %x owned sessionStorage "
|
||||||
"that could not be accessed!");
|
"that could not be accessed!");
|
||||||
if (!canAccess) {
|
if (!canAccess) {
|
||||||
mSessionStorage = nullptr;
|
mSessionStorage = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8823,7 +8842,7 @@ nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the document has the sandboxed origin flag set
|
// If the document has the sandboxed origin flag set
|
||||||
// don't allow access to localStorage.
|
// don't allow access to sessionStorage.
|
||||||
if (!mDoc) {
|
if (!mDoc) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -8832,10 +8851,17 @@ nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
|
||||||
return NS_ERROR_DOM_SECURITY_ERR;
|
return NS_ERROR_DOM_SECURITY_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult rv = docShell->GetSessionStorageForPrincipal(principal,
|
nsresult rv;
|
||||||
documentURI,
|
|
||||||
true,
|
nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(docShell, &rv);
|
||||||
getter_AddRefs(mSessionStorage));
|
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);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
#ifdef PR_LOGGING
|
#ifdef PR_LOGGING
|
||||||
|
@ -8847,17 +8873,12 @@ nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
|
||||||
if (!mSessionStorage) {
|
if (!mSessionStorage) {
|
||||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_GetInterface(mSessionStorage);
|
|
||||||
if (obs) {
|
|
||||||
docShell->AddWeakPrivacyTransitionObserver(obs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef PR_LOGGING
|
#ifdef PR_LOGGING
|
||||||
if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
|
if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
|
||||||
PR_LogPrint("nsGlobalWindow %p returns %p sessionStorage", this, mSessionStorage.get());
|
PR_LogPrint("nsGlobalWindow %p returns %p sessionStorage", this, mSessionStorage.get());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
NS_ADDREF(*aSessionStorage = mSessionStorage);
|
NS_ADDREF(*aSessionStorage = mSessionStorage);
|
||||||
|
@ -8881,41 +8902,38 @@ nsGlobalWindow::GetLocalStorage(nsIDOMStorage ** aLocalStorage)
|
||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
if (!nsDOMStorage::CanUseStorage())
|
if (!DOMStorage::CanUseStorage()) {
|
||||||
return NS_ERROR_DOM_SECURITY_ERR;
|
return NS_ERROR_DOM_SECURITY_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
nsIPrincipal *principal = GetPrincipal();
|
nsIPrincipal *principal = GetPrincipal();
|
||||||
if (!principal)
|
if (!principal) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMStorageManager> storageManager =
|
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);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
nsString documentURI;
|
|
||||||
if (mDocument) {
|
|
||||||
mDocument->GetDocumentURI(documentURI);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the document has the sandboxed origin flag set
|
// If the document has the sandboxed origin flag set
|
||||||
// don't allow access to localStorage.
|
// don't allow access to localStorage.
|
||||||
if (mDoc && (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
|
if (mDoc && (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
|
||||||
return NS_ERROR_DOM_SECURITY_ERR;
|
return NS_ERROR_DOM_SECURITY_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsString documentURI;
|
||||||
|
if (mDocument) {
|
||||||
|
mDocument->GetDocumentURI(documentURI);
|
||||||
|
}
|
||||||
|
|
||||||
nsIDocShell* docShell = GetDocShell();
|
nsIDocShell* docShell = GetDocShell();
|
||||||
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
|
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
|
||||||
|
|
||||||
rv = storageManager->GetLocalStorageForPrincipal(principal,
|
rv = storageManager->CreateStorage(principal,
|
||||||
documentURI,
|
documentURI,
|
||||||
loadContext && loadContext->UsePrivateBrowsing(),
|
loadContext && loadContext->UsePrivateBrowsing(),
|
||||||
getter_AddRefs(mLocalStorage));
|
getter_AddRefs(mLocalStorage));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_GetInterface(mLocalStorage);
|
|
||||||
if (obs && docShell) {
|
|
||||||
docShell->AddWeakPrivacyTransitionObserver(obs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_ADDREF(*aLocalStorage = mLocalStorage);
|
NS_ADDREF(*aLocalStorage = mLocalStorage);
|
||||||
|
@ -9489,8 +9507,13 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
|
||||||
rv = event->GetStorageArea(getter_AddRefs(changingStorage));
|
rv = event->GetStorageArea(getter_AddRefs(changingStorage));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
bool fireMozStorageChanged = false;
|
||||||
|
principal = GetPrincipal();
|
||||||
|
if (!principal) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(changingStorage);
|
nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(changingStorage);
|
||||||
nsPIDOMStorage::nsDOMStorageType storageType = pistorage->StorageType();
|
|
||||||
|
|
||||||
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell());
|
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell());
|
||||||
bool isPrivate = loadContext && loadContext->UsePrivateBrowsing();
|
bool isPrivate = loadContext && loadContext->UsePrivateBrowsing();
|
||||||
|
@ -9498,28 +9521,21 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fireMozStorageChanged = false;
|
switch (pistorage->GetType())
|
||||||
principal = GetPrincipal();
|
|
||||||
switch (storageType)
|
|
||||||
{
|
{
|
||||||
case nsPIDOMStorage::SessionStorage:
|
case nsPIDOMStorage::SessionStorage:
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsIDOMStorage> storage = mSessionStorage;
|
bool check = false;
|
||||||
if (!storage) {
|
|
||||||
nsIDocShell* docShell = GetDocShell();
|
nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(GetDocShell());
|
||||||
if (principal && docShell) {
|
if (storageManager) {
|
||||||
// No need to pass documentURI here, it's only needed when we want
|
rv = storageManager->CheckStorage(principal, changingStorage, &check);
|
||||||
// to create a new storage, the third paramater would be true
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
docShell->GetSessionStorageForPrincipal(principal,
|
|
||||||
EmptyString(),
|
|
||||||
false,
|
|
||||||
getter_AddRefs(storage));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pistorage->IsForkOf(storage)) {
|
if (!check) {
|
||||||
// This storage event is coming from a different doc shell,
|
// This storage event is not coming from our storage or is coming
|
||||||
// i.e. it is a clone, ignore this event.
|
// from a different docshell, i.e. it is a clone, ignore this event.
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9532,13 +9548,14 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
|
||||||
fireMozStorageChanged = SameCOMIdentity(mSessionStorage, changingStorage);
|
fireMozStorageChanged = SameCOMIdentity(mSessionStorage, changingStorage);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case nsPIDOMStorage::LocalStorage:
|
case nsPIDOMStorage::LocalStorage:
|
||||||
{
|
{
|
||||||
// Allow event fire only for the same principal storages
|
// Allow event fire only for the same principal storages
|
||||||
// XXX We have to use EqualsIgnoreDomain after bug 495337 lands
|
// XXX We have to use EqualsIgnoreDomain after bug 495337 lands
|
||||||
nsIPrincipal *storagePrincipal = pistorage->Principal();
|
nsIPrincipal* storagePrincipal = pistorage->GetPrincipal();
|
||||||
bool equals;
|
|
||||||
|
|
||||||
|
bool equals = false;
|
||||||
rv = storagePrincipal->Equals(principal, &equals);
|
rv = storagePrincipal->Equals(principal, &equals);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,6 @@
|
||||||
#include "nsRect.h"
|
#include "nsRect.h"
|
||||||
#include "mozFlushType.h"
|
#include "mozFlushType.h"
|
||||||
#include "prclist.h"
|
#include "prclist.h"
|
||||||
#include "nsIDOMStorageObsolete.h"
|
|
||||||
#include "nsIDOMStorageEvent.h"
|
#include "nsIDOMStorageEvent.h"
|
||||||
#include "nsIDOMStorageIndexedDB.h"
|
#include "nsIDOMStorageIndexedDB.h"
|
||||||
#include "nsIDOMOfflineResourceList.h"
|
#include "nsIDOMOfflineResourceList.h"
|
||||||
|
@ -1022,6 +1021,8 @@ protected:
|
||||||
bool aDefaultStylesOnly,
|
bool aDefaultStylesOnly,
|
||||||
nsIDOMCSSStyleDeclaration** aReturn);
|
nsIDOMCSSStyleDeclaration** aReturn);
|
||||||
|
|
||||||
|
void PreloadLocalStorage();
|
||||||
|
|
||||||
// When adding new member variables, be careful not to create cycles
|
// When adding new member variables, be careful not to create cycles
|
||||||
// through JavaScript. If there is any chance that a member variable
|
// through JavaScript. If there is any chance that a member variable
|
||||||
// could own objects that are implemented in JavaScript, then those
|
// could own objects that are implemented in JavaScript, then those
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
#include "CheckQuotaHelper.h"
|
#include "CheckQuotaHelper.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "nsDOMStorage.h"
|
|
||||||
#include "nsNetUtil.h"
|
#include "nsNetUtil.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "mozilla/dom/quota/QuotaManager.h"
|
#include "mozilla/dom/quota/QuotaManager.h"
|
||||||
|
|
|
@ -8,9 +8,7 @@ XPIDL_SOURCES += [
|
||||||
'nsIDOMStorage.idl',
|
'nsIDOMStorage.idl',
|
||||||
'nsIDOMStorageEvent.idl',
|
'nsIDOMStorageEvent.idl',
|
||||||
'nsIDOMStorageIndexedDB.idl',
|
'nsIDOMStorageIndexedDB.idl',
|
||||||
'nsIDOMStorageItem.idl',
|
|
||||||
'nsIDOMStorageManager.idl',
|
'nsIDOMStorageManager.idl',
|
||||||
'nsIDOMStorageObsolete.idl',
|
|
||||||
'nsIDOMToString.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 nsIDOMStorage;
|
||||||
interface nsIPrincipal;
|
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
|
interface nsIDOMStorageManager : nsISupports
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Return the amount of disk space used by a domain. Usage is checked
|
* This starts async preloading of a storage cache for scope
|
||||||
* against the domain of the page that set the key (the owner domain), not
|
* defined by the principal.
|
||||||
* the domain of the storage object.
|
|
||||||
*
|
|
||||||
* @param aOwnerDomain The domain to check.
|
|
||||||
* @returns the space usage of the domain, in bytes.
|
|
||||||
*/
|
*/
|
||||||
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.
|
* Returns instance of localStorage object for aURI's origin.
|
||||||
* This method ensures there is always only a single instance
|
* This method ensures there is always only a single instance
|
||||||
* for a single origin.
|
* 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,
|
nsIDOMStorage getLocalStorageForPrincipal(in nsIPrincipal aPrincipal,
|
||||||
in DOMString aDocumentURI,
|
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_
|
#define __nsPIDOMStorage_h_
|
||||||
|
|
||||||
#include "nsISupports.h"
|
#include "nsISupports.h"
|
||||||
|
#include "nsString.h"
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
|
|
||||||
class nsIDOMStorageObsolete;
|
|
||||||
class nsIURI;
|
|
||||||
class nsIPrincipal;
|
class nsIPrincipal;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class DOMStorageCache;
|
||||||
|
class DOMStorageManager;
|
||||||
|
|
||||||
|
} // ::dom
|
||||||
|
} // ::mozilla
|
||||||
|
|
||||||
|
// {09198A51-5D27-4992-97E4-38A9CEA2A65D}
|
||||||
#define NS_PIDOMSTORAGE_IID \
|
#define NS_PIDOMSTORAGE_IID \
|
||||||
{ 0x9c292365, 0x6ae4, 0x461b, \
|
{ 0x9198a51, 0x5d27, 0x4992, \
|
||||||
{ 0x86, 0x98, 0xf5, 0x23, 0x6f, 0xfa, 0xd2, 0x30 } }
|
{ 0x97, 0xe4, 0x38, 0xa9, 0xce, 0xa2, 0xa6, 0x5d } }
|
||||||
|
|
||||||
class nsPIDOMStorage : public nsISupports
|
class nsPIDOMStorage : public nsISupports
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMSTORAGE_IID)
|
NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMSTORAGE_IID)
|
||||||
|
|
||||||
typedef enum {
|
enum StorageType {
|
||||||
Unknown = 0,
|
|
||||||
LocalStorage = 1,
|
LocalStorage = 1,
|
||||||
SessionStorage = 2
|
SessionStorage = 2
|
||||||
} nsDOMStorageType;
|
};
|
||||||
|
|
||||||
virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
|
virtual StorageType GetType() const = 0;
|
||||||
bool aPrivate) = 0;
|
virtual mozilla::dom::DOMStorageManager* GetManager() const = 0;
|
||||||
virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
|
virtual const mozilla::dom::DOMStorageCache* GetCache() const = 0;
|
||||||
bool aPrivate) = 0;
|
|
||||||
|
|
||||||
virtual already_AddRefed<nsIDOMStorage> Clone() = 0;
|
virtual nsTArray<nsString>* GetKeys() = 0;
|
||||||
virtual already_AddRefed<nsIDOMStorage> Fork(const nsSubstring &aDocumentURI) = 0;
|
|
||||||
virtual bool IsForkOf(nsIDOMStorage* aThat) = 0;
|
|
||||||
|
|
||||||
virtual nsTArray<nsString> *GetKeys() = 0;
|
virtual nsIPrincipal* GetPrincipal() = 0;
|
||||||
|
virtual bool PrincipalEquals(nsIPrincipal* principal) = 0;
|
||||||
virtual nsIPrincipal* Principal() = 0;
|
|
||||||
virtual bool CanAccess(nsIPrincipal *aPrincipal) = 0;
|
virtual bool CanAccess(nsIPrincipal *aPrincipal) = 0;
|
||||||
|
|
||||||
virtual nsDOMStorageType StorageType() = 0;
|
|
||||||
|
|
||||||
virtual bool IsPrivate() = 0;
|
virtual bool IsPrivate() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
#include "mozilla/dom/ExternalHelperAppChild.h"
|
#include "mozilla/dom/ExternalHelperAppChild.h"
|
||||||
#include "mozilla/dom/PCrashReporterChild.h"
|
#include "mozilla/dom/PCrashReporterChild.h"
|
||||||
#include "mozilla/dom/StorageChild.h"
|
#include "mozilla/dom/DOMStorageIPC.h"
|
||||||
#include "mozilla/Hal.h"
|
#include "mozilla/Hal.h"
|
||||||
#include "mozilla/hal_sandbox/PHalChild.h"
|
#include "mozilla/hal_sandbox/PHalChild.h"
|
||||||
#include "mozilla/ipc/GeckoChildProcessHost.h"
|
#include "mozilla/ipc/GeckoChildProcessHost.h"
|
||||||
|
@ -839,7 +839,7 @@ ContentChild::DeallocPSms(PSmsChild* aSms)
|
||||||
}
|
}
|
||||||
|
|
||||||
PStorageChild*
|
PStorageChild*
|
||||||
ContentChild::AllocPStorage(const StorageConstructData& aData)
|
ContentChild::AllocPStorage()
|
||||||
{
|
{
|
||||||
NS_NOTREACHED("We should never be manually allocating PStorageChild actors");
|
NS_NOTREACHED("We should never be manually allocating PStorageChild actors");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -848,7 +848,7 @@ ContentChild::AllocPStorage(const StorageConstructData& aData)
|
||||||
bool
|
bool
|
||||||
ContentChild::DeallocPStorage(PStorageChild* aActor)
|
ContentChild::DeallocPStorage(PStorageChild* aActor)
|
||||||
{
|
{
|
||||||
StorageChild* child = static_cast<StorageChild*>(aActor);
|
DOMStorageDBChild* child = static_cast<DOMStorageDBChild*>(aActor);
|
||||||
child->ReleaseIPDLReference();
|
child->ReleaseIPDLReference();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,7 +142,7 @@ public:
|
||||||
virtual PSmsChild* AllocPSms();
|
virtual PSmsChild* AllocPSms();
|
||||||
virtual bool DeallocPSms(PSmsChild*);
|
virtual bool DeallocPSms(PSmsChild*);
|
||||||
|
|
||||||
virtual PStorageChild* AllocPStorage(const StorageConstructData& aData);
|
virtual PStorageChild* AllocPStorage();
|
||||||
virtual bool DeallocPStorage(PStorageChild* aActor);
|
virtual bool DeallocPStorage(PStorageChild* aActor);
|
||||||
|
|
||||||
virtual PBluetoothChild* AllocPBluetooth();
|
virtual PBluetoothChild* AllocPBluetooth();
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
#include "mozilla/dom/ExternalHelperAppParent.h"
|
#include "mozilla/dom/ExternalHelperAppParent.h"
|
||||||
#include "mozilla/dom/PMemoryReportRequestParent.h"
|
#include "mozilla/dom/PMemoryReportRequestParent.h"
|
||||||
#include "mozilla/dom/power/PowerManagerService.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/bluetooth/PBluetoothParent.h"
|
||||||
#include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
|
#include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
|
||||||
#include "SmsParent.h"
|
#include "SmsParent.h"
|
||||||
|
@ -1970,15 +1970,16 @@ ContentParent::DeallocPSms(PSmsParent* aSms)
|
||||||
}
|
}
|
||||||
|
|
||||||
PStorageParent*
|
PStorageParent*
|
||||||
ContentParent::AllocPStorage(const StorageConstructData& aData)
|
ContentParent::AllocPStorage()
|
||||||
{
|
{
|
||||||
return new StorageParent(aData);
|
return new DOMStorageDBParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ContentParent::DeallocPStorage(PStorageParent* aActor)
|
ContentParent::DeallocPStorage(PStorageParent* aActor)
|
||||||
{
|
{
|
||||||
delete aActor;
|
DOMStorageDBParent* child = static_cast<DOMStorageDBParent*>(aActor);
|
||||||
|
child->ReleaseIPDLReference();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -285,7 +285,7 @@ private:
|
||||||
virtual PSmsParent* AllocPSms();
|
virtual PSmsParent* AllocPSms();
|
||||||
virtual bool DeallocPSms(PSmsParent*);
|
virtual bool DeallocPSms(PSmsParent*);
|
||||||
|
|
||||||
virtual PStorageParent* AllocPStorage(const StorageConstructData& aData);
|
virtual PStorageParent* AllocPStorage();
|
||||||
virtual bool DeallocPStorage(PStorageParent* aActor);
|
virtual bool DeallocPStorage(PStorageParent* aActor);
|
||||||
|
|
||||||
virtual PBluetoothParent* AllocPBluetooth();
|
virtual PBluetoothParent* AllocPBluetooth();
|
||||||
|
|
|
@ -50,23 +50,6 @@ using gfxIntSize;
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
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 {
|
struct FontListEntry {
|
||||||
nsString familyName;
|
nsString familyName;
|
||||||
nsString faceName;
|
nsString faceName;
|
||||||
|
@ -401,7 +384,7 @@ parent:
|
||||||
|
|
||||||
PSpeechSynthesis();
|
PSpeechSynthesis();
|
||||||
|
|
||||||
PStorage(StorageConstructData data);
|
PStorage();
|
||||||
|
|
||||||
PBluetooth();
|
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
|
LIBXUL_LIBRARY = 1
|
||||||
|
|
||||||
CPPSRCS = \
|
CPPSRCS = \
|
||||||
nsDOMStorage.cpp \
|
DOMStorage.cpp \
|
||||||
nsDOMStorageBaseDB.cpp \
|
DOMStorageCache.cpp \
|
||||||
nsDOMStorageDBWrapper.cpp \
|
DOMStorageDBThread.cpp \
|
||||||
nsLocalStorageCache.cpp \
|
DOMStorageObserver.cpp \
|
||||||
nsDOMStoragePersistentDB.cpp \
|
DOMStorageManager.cpp \
|
||||||
nsDOMStorageMemoryDB.cpp \
|
DOMStorageIPC.cpp \
|
||||||
StorageChild.cpp \
|
|
||||||
StorageParent.cpp \
|
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
EXPORTS_NAMESPACES = mozilla/dom
|
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.
|
# we don't want the shared lib, but we want to force the creation of a static lib.
|
||||||
FORCE_STATIC_LIB = 1
|
FORCE_STATIC_LIB = 1
|
||||||
|
@ -37,6 +35,10 @@ LOCAL_INCLUDES = \
|
||||||
|
|
||||||
DEFINES += -D_IMPL_NS_LAYOUT
|
DEFINES += -D_IMPL_NS_LAYOUT
|
||||||
|
|
||||||
|
ifdef ENABLE_TESTS
|
||||||
|
DEFINES += -DDOM_STORAGE_TESTS
|
||||||
|
endif
|
||||||
|
|
||||||
include $(topsrcdir)/config/config.mk
|
include $(topsrcdir)/config/config.mk
|
||||||
include $(topsrcdir)/ipc/chromium/chromium-config.mk
|
include $(topsrcdir)/ipc/chromium/chromium-config.mk
|
||||||
include $(topsrcdir)/config/rules.mk
|
include $(topsrcdir)/config/rules.mk
|
||||||
|
|
|
@ -6,61 +6,37 @@
|
||||||
|
|
||||||
include protocol PContent;
|
include protocol PContent;
|
||||||
|
|
||||||
using mozilla::null_t;
|
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
struct ItemData
|
/* This protocol bridges async access to the database thread running on the parent process
|
||||||
{
|
* and caches running on the child process.
|
||||||
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.
|
|
||||||
sync protocol PStorage
|
sync protocol PStorage
|
||||||
{
|
{
|
||||||
manager PContent;
|
manager PContent;
|
||||||
|
|
||||||
parent:
|
parent:
|
||||||
__delete__();
|
async __delete__();
|
||||||
|
|
||||||
Init(bool useDB, bool sessionOnly, bool isPrivate,
|
sync Preload(nsCString scope, uint32_t alreadyLoadedCount)
|
||||||
nsCString scopeDBKey, nsCString quotaDBKey, uint32_t storageType);
|
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)
|
child:
|
||||||
returns (nsString[] keys);
|
async Observe(nsCString topic, nsCString scopePrefix);
|
||||||
sync GetLength(bool callerSecure, bool sessionOnly)
|
async ScopesHavingData(nsCString[] scopes);
|
||||||
returns (uint32_t length, nsresult rv);
|
async LoadItem(nsCString scope, nsString key, nsString value);
|
||||||
sync GetKey(bool callerSecure, bool sessionOnly, uint32_t index)
|
async LoadDone(nsCString scope, nsresult rv);
|
||||||
returns (nsString key, nsresult rv);
|
async LoadUsage(nsCString scope, int64_t usage);
|
||||||
sync GetValue(bool callerSecure, bool sessionOnly, nsString key)
|
async Error(nsresult rv);
|
||||||
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);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 = \
|
MOCHITEST_FILES = \
|
||||||
frameAppIsolation.html \
|
frameAppIsolation.html \
|
||||||
frameBug624047.html \
|
|
||||||
frameChromeSlave.html \
|
frameChromeSlave.html \
|
||||||
frameKeySync.html \
|
frameKeySync.html \
|
||||||
frameMasterEqual.html \
|
frameMasterEqual.html \
|
||||||
|
@ -27,9 +26,10 @@ MOCHITEST_FILES = \
|
||||||
interOriginFrame.js \
|
interOriginFrame.js \
|
||||||
interOriginTest.js \
|
interOriginTest.js \
|
||||||
interOriginTest2.js \
|
interOriginTest2.js \
|
||||||
|
localStorageCommon.js \
|
||||||
test_appIsolation.html \
|
test_appIsolation.html \
|
||||||
test_brokenUTF-16.html \
|
test_brokenUTF-16.html \
|
||||||
test_bug624047.html \
|
test_bug600307-DBOps.html \
|
||||||
test_bug746272-1.html \
|
test_bug746272-1.html \
|
||||||
test_bug746272-2.html \
|
test_bug746272-2.html \
|
||||||
test_cookieBlock.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">
|
<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()
|
function startTest()
|
||||||
{
|
{
|
||||||
|
@ -28,12 +52,14 @@ function startTest()
|
||||||
is(typeof localStorage["nonexisting2"], "undefined", "['nonexisting2'] is undefined");
|
is(typeof localStorage["nonexisting2"], "undefined", "['nonexisting2'] is undefined");
|
||||||
is(typeof localStorage.nonexisting2, "undefined", "nonexisting2 is undefined");
|
is(typeof localStorage.nonexisting2, "undefined", "nonexisting2 is undefined");
|
||||||
|
|
||||||
var mozStorageChangedReceived = 0;
|
|
||||||
var localStorageCopy = localStorage;
|
var localStorageCopy = localStorage;
|
||||||
|
|
||||||
function onStorageChanged(e) {
|
function onStorageChanged(e) {
|
||||||
if (e.storageArea == localStorageCopy)
|
if (e.storageArea == localStorageCopy) {
|
||||||
mozStorageChangedReceived++;
|
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
|
// Listen for MozStorageChanged
|
||||||
|
@ -204,8 +230,7 @@ function startTest()
|
||||||
|
|
||||||
SimpleTest.executeSoon(function () {
|
SimpleTest.executeSoon(function () {
|
||||||
SpecialPowers.removeChromeEventListener("MozStorageChanged", onStorageChanged, false);
|
SpecialPowers.removeChromeEventListener("MozStorageChanged", onStorageChanged, false);
|
||||||
is(mozStorageChangedReceived, MOZ_STORAGE_CHANGED_COUNT,
|
is(expectedEvents.length, 0, "received the correct number of events");
|
||||||
"received the correct number of events");
|
|
||||||
|
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
|
|
|
@ -14,12 +14,12 @@ function startTest()
|
||||||
.getService(Components.interfaces.nsIIOService);
|
.getService(Components.interfaces.nsIIOService);
|
||||||
var ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
var ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
|
||||||
.getService(Components.interfaces.nsIScriptSecurityManager);
|
.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);
|
.getService(Components.interfaces.nsIDOMStorageManager);
|
||||||
|
|
||||||
var uri = ios.newURI(url, "", null);
|
var uri = ios.newURI(url, "", null);
|
||||||
var principal = ssm.getNoAppCodebasePrincipal(uri);
|
var principal = ssm.getNoAppCodebasePrincipal(uri);
|
||||||
var storage = dsm.getLocalStorageForPrincipal(principal, "");
|
var storage = dsm.createStorage(principal, "");
|
||||||
|
|
||||||
storage.setItem("chromekey", "chromevalue");
|
storage.setItem("chromekey", "chromevalue");
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@ MOCHITEST_FILES = \
|
||||||
frameNotEqual.html \
|
frameNotEqual.html \
|
||||||
file_http.html \
|
file_http.html \
|
||||||
file_https.html \
|
file_https.html \
|
||||||
|
test_cookieSession.html \
|
||||||
|
test_sessionStorageBaseSessionOnly.html \
|
||||||
test_sessionStorageBase.html \
|
test_sessionStorageBase.html \
|
||||||
test_sessionStorageClone.html \
|
test_sessionStorageClone.html \
|
||||||
test_sessionStorageReplace.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">
|
<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() {
|
function setup() {
|
||||||
sessionStorage.clear();
|
sessionStorage.clear();
|
||||||
|
@ -37,8 +48,11 @@ function startTest()
|
||||||
var sessionStorageCopy = sessionStorage;
|
var sessionStorageCopy = sessionStorage;
|
||||||
|
|
||||||
function onStorageChanged(e) {
|
function onStorageChanged(e) {
|
||||||
if (e.storageArea == sessionStorageCopy)
|
if (e.storageArea == sessionStorageCopy) {
|
||||||
mozStorageChangedReceived++;
|
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
|
// Listen for MozStorageChanged
|
||||||
|
@ -148,8 +162,7 @@ function startTest()
|
||||||
|
|
||||||
SimpleTest.executeSoon(function () {
|
SimpleTest.executeSoon(function () {
|
||||||
SpecialPowers.removeChromeEventListener("MozStorageChanged", onStorageChanged, false);
|
SpecialPowers.removeChromeEventListener("MozStorageChanged", onStorageChanged, false);
|
||||||
is(mozStorageChangedReceived, MOZ_STORAGE_CHANGED_COUNT,
|
is(expectedEvents.length, 0, "received the correct number of events");
|
||||||
"received the correct number of events");
|
|
||||||
|
|
||||||
sessionStorage.clear();
|
sessionStorage.clear();
|
||||||
SimpleTest.finish();
|
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 "nsIWindowProvider.h"
|
||||||
#include "nsIMutableArray.h"
|
#include "nsIMutableArray.h"
|
||||||
#include "nsISupportsArray.h"
|
#include "nsISupportsArray.h"
|
||||||
#include "nsIDOMStorageObsolete.h"
|
|
||||||
#include "nsIDOMStorage.h"
|
#include "nsIDOMStorage.h"
|
||||||
#include "nsPIDOMStorage.h"
|
#include "nsIDOMStorageManager.h"
|
||||||
#include "nsIWidget.h"
|
#include "nsIWidget.h"
|
||||||
#include "nsFocusManager.h"
|
#include "nsFocusManager.h"
|
||||||
#include "nsIPresShell.h"
|
#include "nsIPresShell.h"
|
||||||
|
@ -1000,17 +999,14 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow *aParent,
|
||||||
parentDocShell = piWindow->GetDocShell();
|
parentDocShell = piWindow->GetDocShell();
|
||||||
|
|
||||||
if (subjectPrincipal && parentDocShell) {
|
if (subjectPrincipal && parentDocShell) {
|
||||||
nsCOMPtr<nsIDOMStorage> storage;
|
nsCOMPtr<nsIDOMStorageManager> parentStorageManager = do_QueryInterface(parentDocShell);
|
||||||
parentDocShell->GetSessionStorageForPrincipal(subjectPrincipal,
|
nsCOMPtr<nsIDOMStorageManager> newStorageManager = do_QueryInterface(newDocShell);
|
||||||
EmptyString(), false,
|
|
||||||
getter_AddRefs(storage));
|
if (parentStorageManager && newStorageManager) {
|
||||||
nsCOMPtr<nsPIDOMStorage> piStorage =
|
nsCOMPtr<nsIDOMStorage> storage;
|
||||||
do_QueryInterface(storage);
|
parentStorageManager->GetStorage(subjectPrincipal, isPrivateBrowsingWindow, getter_AddRefs(storage));
|
||||||
if (piStorage){
|
if (storage)
|
||||||
storage = piStorage->Clone();
|
newStorageManager->CloneStorage(storage);
|
||||||
newDocShell->AddSessionStorage(
|
|
||||||
piStorage->Principal(),
|
|
||||||
storage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -119,7 +119,6 @@ members = [
|
||||||
'nsIDOMStorage.key',
|
'nsIDOMStorage.key',
|
||||||
'nsIDOMStorage.removeItem',
|
'nsIDOMStorage.removeItem',
|
||||||
'nsIDOMStorage.clear',
|
'nsIDOMStorage.clear',
|
||||||
'nsIDOMStorageItem.value',
|
|
||||||
|
|
||||||
# dom/interfaces/xpath
|
# dom/interfaces/xpath
|
||||||
'nsIDOMXPathExpression.evaluate',
|
'nsIDOMXPathExpression.evaluate',
|
||||||
|
|
|
@ -63,17 +63,13 @@
|
||||||
#define NS_CANVASRENDERINGCONTEXTWEBGL_CID \
|
#define NS_CANVASRENDERINGCONTEXTWEBGL_CID \
|
||||||
{ 0x2fe88332, 0x31c6, 0x4829, { 0xb2, 0x47, 0xa0, 0x7d, 0x8a, 0x7e, 0xe8, 0x0fe } }
|
{ 0x2fe88332, 0x31c6, 0x4829, { 0xb2, 0x47, 0xa0, 0x7d, 0x8a, 0x7e, 0xe8, 0x0fe } }
|
||||||
|
|
||||||
// {8b449142-1eab-4bfa-9830-fab6ebb09774}
|
// {A746DECD-AE74-4d86-8E75-4FDA81A9BE90}
|
||||||
#define NS_DOMSTORAGE_CID \
|
#define NS_DOMSESSIONSTORAGEMANAGER_CID \
|
||||||
{ 0x8b449142, 0x1eab, 0x4bfa, { 0x98, 0x30, 0xfa, 0xb6, 0xeb, 0xb0, 0x97, 0x74 } }
|
{ 0xa746decd, 0xae74, 0x4d86, { 0x8e, 0x75, 0x4f, 0xda, 0x81, 0xa9, 0xbe, 0x90 } }
|
||||||
|
|
||||||
// {27AECC62-7777-428e-B34C-5973A47B8298}
|
// {656DB07C-AA80-49e4-BCE8-E431BAAE697D}
|
||||||
#define NS_DOMSTORAGE2_CID \
|
#define NS_DOMLOCALSTORAGEMANAGER_CID \
|
||||||
{ 0x27aecc62, 0x7777, 0x428e, { 0xb3, 0x4c, 0x59, 0x73, 0xa4, 0x7b, 0x82, 0x98 } }
|
{ 0x656db07c, 0xaa80, 0x49e4, { 0xbc, 0xe8, 0xe4, 0x31, 0xba, 0xae, 0x69, 0x7d } }
|
||||||
|
|
||||||
// {b88a4712-eb52-4c10-9b85-bf5894b510f0}
|
|
||||||
#define NS_DOMSTORAGEMANAGER_CID \
|
|
||||||
{ 0xb88a4712, 0xeb52, 0x4c10, { 0x9b, 0x85, 0xbf, 0x58, 0x94, 0xb5, 0x10, 0xf0 } }
|
|
||||||
|
|
||||||
// {93ad72a6-02cd-4716-9626-d47d5ec275ec}
|
// {93ad72a6-02cd-4716-9626-d47d5ec275ec}
|
||||||
#define NS_DOMJSON_CID \
|
#define NS_DOMJSON_CID \
|
||||||
|
|
|
@ -80,7 +80,7 @@
|
||||||
#include "nsScriptNameSpaceManager.h"
|
#include "nsScriptNameSpaceManager.h"
|
||||||
#include "nsIControllerContext.h"
|
#include "nsIControllerContext.h"
|
||||||
#include "nsDOMScriptObjectFactory.h"
|
#include "nsDOMScriptObjectFactory.h"
|
||||||
#include "nsDOMStorage.h"
|
#include "DOMStorageManager.h"
|
||||||
#include "nsJSON.h"
|
#include "nsJSON.h"
|
||||||
#include "mozIApplicationClearPrivateDataParams.h"
|
#include "mozIApplicationClearPrivateDataParams.h"
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
|
@ -275,8 +275,8 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsBlobProtocolHandler)
|
||||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsMediaStreamProtocolHandler)
|
NS_GENERIC_FACTORY_CONSTRUCTOR(nsMediaStreamProtocolHandler)
|
||||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsHostObjectURI)
|
NS_GENERIC_FACTORY_CONSTRUCTOR(nsHostObjectURI)
|
||||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMParser)
|
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMParser)
|
||||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsDOMStorageManager,
|
NS_GENERIC_FACTORY_CONSTRUCTOR(DOMSessionStorageManager)
|
||||||
nsDOMStorageManager::GetInstance)
|
NS_GENERIC_FACTORY_CONSTRUCTOR(DOMLocalStorageManager)
|
||||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsChannelPolicy)
|
NS_GENERIC_FACTORY_CONSTRUCTOR(nsChannelPolicy)
|
||||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(IndexedDatabaseManager,
|
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(IndexedDatabaseManager,
|
||||||
IndexedDatabaseManager::FactoryCreate)
|
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_EVENTSOURCE_CID);
|
||||||
NS_DEFINE_NAMED_CID(NS_DOMACTIVITY_CID);
|
NS_DEFINE_NAMED_CID(NS_DOMACTIVITY_CID);
|
||||||
NS_DEFINE_NAMED_CID(NS_DOMPARSER_CID);
|
NS_DEFINE_NAMED_CID(NS_DOMPARSER_CID);
|
||||||
NS_DEFINE_NAMED_CID(NS_DOMSTORAGE2_CID);
|
NS_DEFINE_NAMED_CID(NS_DOMSESSIONSTORAGEMANAGER_CID);
|
||||||
NS_DEFINE_NAMED_CID(NS_DOMSTORAGEMANAGER_CID);
|
NS_DEFINE_NAMED_CID(NS_DOMLOCALSTORAGEMANAGER_CID);
|
||||||
NS_DEFINE_NAMED_CID(NS_DOMJSON_CID);
|
NS_DEFINE_NAMED_CID(NS_DOMJSON_CID);
|
||||||
NS_DEFINE_NAMED_CID(NS_TEXTEDITOR_CID);
|
NS_DEFINE_NAMED_CID(NS_TEXTEDITOR_CID);
|
||||||
NS_DEFINE_NAMED_CID(INDEXEDDB_MANAGER_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_EVENTSOURCE_CID, false, NULL, EventSourceConstructor },
|
||||||
{ &kNS_DOMACTIVITY_CID, false, NULL, ActivityConstructor },
|
{ &kNS_DOMACTIVITY_CID, false, NULL, ActivityConstructor },
|
||||||
{ &kNS_DOMPARSER_CID, false, NULL, nsDOMParserConstructor },
|
{ &kNS_DOMPARSER_CID, false, NULL, nsDOMParserConstructor },
|
||||||
{ &kNS_DOMSTORAGE2_CID, false, NULL, NS_NewDOMStorage2 },
|
{ &kNS_DOMSESSIONSTORAGEMANAGER_CID, false, NULL, DOMSessionStorageManagerConstructor },
|
||||||
{ &kNS_DOMSTORAGEMANAGER_CID, false, NULL, nsDOMStorageManagerConstructor },
|
{ &kNS_DOMLOCALSTORAGEMANAGER_CID, false, NULL, DOMLocalStorageManagerConstructor },
|
||||||
{ &kNS_DOMJSON_CID, false, NULL, NS_NewJSON },
|
{ &kNS_DOMJSON_CID, false, NULL, NS_NewJSON },
|
||||||
{ &kNS_TEXTEDITOR_CID, false, NULL, nsPlaintextEditorConstructor },
|
{ &kNS_TEXTEDITOR_CID, false, NULL, nsPlaintextEditorConstructor },
|
||||||
{ &kINDEXEDDB_MANAGER_CID, false, NULL, IndexedDatabaseManagerConstructor },
|
{ &kINDEXEDDB_MANAGER_CID, false, NULL, IndexedDatabaseManagerConstructor },
|
||||||
|
@ -1204,8 +1204,8 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
|
||||||
{ NS_EVENTSOURCE_CONTRACTID, &kNS_EVENTSOURCE_CID },
|
{ NS_EVENTSOURCE_CONTRACTID, &kNS_EVENTSOURCE_CID },
|
||||||
{ NS_DOMACTIVITY_CONTRACTID, &kNS_DOMACTIVITY_CID },
|
{ NS_DOMACTIVITY_CONTRACTID, &kNS_DOMACTIVITY_CID },
|
||||||
{ NS_DOMPARSER_CONTRACTID, &kNS_DOMPARSER_CID },
|
{ NS_DOMPARSER_CONTRACTID, &kNS_DOMPARSER_CID },
|
||||||
{ "@mozilla.org/dom/storage;2", &kNS_DOMSTORAGE2_CID },
|
{ "@mozilla.org/dom/localStorage-manager;1", &kNS_DOMLOCALSTORAGEMANAGER_CID },
|
||||||
{ "@mozilla.org/dom/storagemanager;1", &kNS_DOMSTORAGEMANAGER_CID },
|
{ "@mozilla.org/dom/sessionStorage-manager;1", &kNS_DOMSESSIONSTORAGEMANAGER_CID },
|
||||||
{ "@mozilla.org/dom/json;1", &kNS_DOMJSON_CID },
|
{ "@mozilla.org/dom/json;1", &kNS_DOMJSON_CID },
|
||||||
{ "@mozilla.org/editor/texteditor;1", &kNS_TEXTEDITOR_CID },
|
{ "@mozilla.org/editor/texteditor;1", &kNS_TEXTEDITOR_CID },
|
||||||
{ INDEXEDDB_MANAGER_CONTRACTID, &kINDEXEDDB_MANAGER_CID },
|
{ INDEXEDDB_MANAGER_CONTRACTID, &kINDEXEDDB_MANAGER_CID },
|
||||||
|
|
|
@ -41,7 +41,6 @@
|
||||||
#include "nsXBLWindowKeyHandler.h"
|
#include "nsXBLWindowKeyHandler.h"
|
||||||
#include "nsXBLService.h"
|
#include "nsXBLService.h"
|
||||||
#include "txMozillaXSLTProcessor.h"
|
#include "txMozillaXSLTProcessor.h"
|
||||||
#include "nsDOMStorage.h"
|
|
||||||
#include "nsTreeSanitizer.h"
|
#include "nsTreeSanitizer.h"
|
||||||
#include "nsCellMap.h"
|
#include "nsCellMap.h"
|
||||||
#include "nsTextFrameTextRunCache.h"
|
#include "nsTextFrameTextRunCache.h"
|
||||||
|
@ -59,7 +58,7 @@
|
||||||
#include "nsMathMLAtoms.h"
|
#include "nsMathMLAtoms.h"
|
||||||
#include "nsMathMLOperators.h"
|
#include "nsMathMLOperators.h"
|
||||||
#include "Navigator.h"
|
#include "Navigator.h"
|
||||||
#include "nsDOMStorageBaseDB.h"
|
#include "DOMStorageObserver.h"
|
||||||
#include "DisplayItemClip.h"
|
#include "DisplayItemClip.h"
|
||||||
|
|
||||||
#include "AudioChannelService.h"
|
#include "AudioChannelService.h"
|
||||||
|
@ -217,9 +216,9 @@ nsLayoutStatics::Initialize()
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = nsDOMStorageManager::Initialize();
|
rv = DOMStorageObserver::Init();
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
NS_ERROR("Could not initialize nsDOMStorageManager");
|
NS_ERROR("Could not initialize DOMStorageObserver");
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,8 +271,6 @@ nsLayoutStatics::Initialize()
|
||||||
nsCookieService::AppClearDataObserverInit();
|
nsCookieService::AppClearDataObserverInit();
|
||||||
nsApplicationCacheService::AppClearDataObserverInit();
|
nsApplicationCacheService::AppClearDataObserverInit();
|
||||||
|
|
||||||
nsDOMStorageBaseDB::Init();
|
|
||||||
|
|
||||||
InitializeDateCacheCleaner();
|
InitializeDateCacheCleaner();
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -290,7 +287,7 @@ nsLayoutStatics::Shutdown()
|
||||||
#ifdef MOZ_XUL
|
#ifdef MOZ_XUL
|
||||||
nsXULPopupManager::Shutdown();
|
nsXULPopupManager::Shutdown();
|
||||||
#endif
|
#endif
|
||||||
nsDOMStorageManager::Shutdown();
|
DOMStorageObserver::Shutdown();
|
||||||
txMozillaXSLTProcessor::Shutdown();
|
txMozillaXSLTProcessor::Shutdown();
|
||||||
Attr::Shutdown();
|
Attr::Shutdown();
|
||||||
nsEventListenerManager::Shutdown();
|
nsEventListenerManager::Shutdown();
|
||||||
|
|
|
@ -411,7 +411,9 @@
|
||||||
"dom/tests/mochitest/localstorage/test_localStorageReplace.html":"",
|
"dom/tests/mochitest/localstorage/test_localStorageReplace.html":"",
|
||||||
|
|
||||||
"dom/tests/mochitest/pointerlock/test_pointerlock-api.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_sessionStorageBase.html":"",
|
||||||
|
"dom/tests/mochitest/sessionstorage/test_sessionStorageBaseSessionOnly.html":"",
|
||||||
"dom/tests/mochitest/sessionstorage/test_sessionStorageClone.html":"",
|
"dom/tests/mochitest/sessionstorage/test_sessionStorageClone.html":"",
|
||||||
"dom/tests/mochitest/sessionstorage/test_sessionStorageHttpHttps.html":"",
|
"dom/tests/mochitest/sessionstorage/test_sessionStorageHttpHttps.html":"",
|
||||||
"dom/tests/mochitest/sessionstorage/test_sessionStorageReplace.html":"",
|
"dom/tests/mochitest/sessionstorage/test_sessionStorageReplace.html":"",
|
||||||
|
|
|
@ -2550,6 +2550,24 @@
|
||||||
"kind": "boolean",
|
"kind": "boolean",
|
||||||
"description": "DOM: Ranges that are detached on destruction (bug 702948)"
|
"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": {
|
"LOCALDOMSTORAGE_GETALLKEYS_MS": {
|
||||||
"kind": "exponential",
|
"kind": "exponential",
|
||||||
"high": "3000",
|
"high": "3000",
|
||||||
|
@ -2557,6 +2575,41 @@
|
||||||
"extended_statistics_ok": true,
|
"extended_statistics_ok": true,
|
||||||
"description": "Time to return a list of all keys in domain's LocalStorage (ms)"
|
"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": {
|
"LOCALDOMSTORAGE_GETVALUE_MS": {
|
||||||
"kind": "exponential",
|
"kind": "exponential",
|
||||||
"high": "3000",
|
"high": "3000",
|
||||||
|
@ -2564,6 +2617,13 @@
|
||||||
"extended_statistics_ok": true,
|
"extended_statistics_ok": true,
|
||||||
"description": "Time to return a value for a key in LocalStorage (ms)"
|
"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": {
|
"LOCALDOMSTORAGE_SETVALUE_MS": {
|
||||||
"kind": "exponential",
|
"kind": "exponential",
|
||||||
"high": "3000",
|
"high": "3000",
|
||||||
|
@ -2571,6 +2631,13 @@
|
||||||
"extended_statistics_ok": true,
|
"extended_statistics_ok": true,
|
||||||
"description": "Time to set a single key's value in LocalStorage (ms)"
|
"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": {
|
"LOCALDOMSTORAGE_REMOVEKEY_MS": {
|
||||||
"kind": "exponential",
|
"kind": "exponential",
|
||||||
"high": "3000",
|
"high": "3000",
|
||||||
|
@ -2578,33 +2645,40 @@
|
||||||
"extended_statistics_ok": true,
|
"extended_statistics_ok": true,
|
||||||
"description": "Time to remove a single key from LocalStorage (ms)"
|
"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",
|
"kind": "exponential",
|
||||||
"high": "3000",
|
"high": "3000",
|
||||||
"n_buckets": 10,
|
"n_buckets": 10,
|
||||||
"extended_statistics_ok": true,
|
"extended_statistics_ok": true,
|
||||||
"description": "Time to clear LocalStorage for all domains (ms)"
|
"description": "Time to clear LocalStorage for all domains (ms)"
|
||||||
},
|
},
|
||||||
"LOCALDOMSTORAGE_FETCH_DOMAIN_MS": {
|
"LOCALDOMSTORAGE_CLEAR_BLOCKING_MS": {
|
||||||
"kind": "exponential",
|
"kind": "exponential",
|
||||||
"high": "3000",
|
"high": "3000",
|
||||||
"n_buckets": 10,
|
"n_buckets": 10,
|
||||||
"extended_statistics_ok": true,
|
"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",
|
"kind": "exponential",
|
||||||
"high": "3000",
|
"high": "3000",
|
||||||
"n_buckets": 10,
|
"n_buckets": 10,
|
||||||
"extended_statistics_ok": true,
|
"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",
|
"kind": "exponential",
|
||||||
"high": "3000",
|
"high": "3000",
|
||||||
"n_buckets": 10,
|
"n_buckets": 10,
|
||||||
"extended_statistics_ok": true,
|
"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": {
|
"LOCALDOMSTORAGE_KEY_SIZE_BYTES": {
|
||||||
"kind": "exponential",
|
"kind": "exponential",
|
||||||
|
|
|
@ -50,7 +50,7 @@ let initTable = [
|
||||||
["search", "@mozilla.org/browser/search-service;1", "nsIBrowserSearchService"],
|
["search", "@mozilla.org/browser/search-service;1", "nsIBrowserSearchService"],
|
||||||
#endif
|
#endif
|
||||||
["storage", "@mozilla.org/storage/service;1", "mozIStorageService"],
|
["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"],
|
["strings", "@mozilla.org/intl/stringbundle;1", "nsIStringBundleService"],
|
||||||
["telemetry", "@mozilla.org/base/telemetry;1", "nsITelemetry"],
|
["telemetry", "@mozilla.org/base/telemetry;1", "nsITelemetry"],
|
||||||
["tm", "@mozilla.org/thread-manager;1", "nsIThreadManager"],
|
["tm", "@mozilla.org/thread-manager;1", "nsIThreadManager"],
|
||||||
|
|
|
@ -659,9 +659,9 @@ function test_storage_cleared()
|
||||||
let principal = Cc["@mozilla.org/scriptsecuritymanager;1"].
|
let principal = Cc["@mozilla.org/scriptsecuritymanager;1"].
|
||||||
getService(Ci.nsIScriptSecurityManager).
|
getService(Ci.nsIScriptSecurityManager).
|
||||||
getNoAppCodebasePrincipal(aURI);
|
getNoAppCodebasePrincipal(aURI);
|
||||||
let dsm = Cc["@mozilla.org/dom/storagemanager;1"].
|
let dsm = Cc["@mozilla.org/dom/localStorage-manager;1"].
|
||||||
getService(Ci.nsIDOMStorageManager);
|
getService(Ci.nsIDOMStorageManager);
|
||||||
return dsm.getLocalStorageForPrincipal(principal, "");
|
return dsm.createStorage(principal, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
let s = [
|
let s = [
|
||||||
|
|
|
@ -500,6 +500,12 @@
|
||||||
ERROR(NS_ERROR_DOM_RETVAL_UNDEFINED, FAILURE(1013)),
|
ERROR(NS_ERROR_DOM_RETVAL_UNDEFINED, FAILURE(1013)),
|
||||||
ERROR(NS_ERROR_DOM_QUOTA_REACHED, FAILURE(1014)),
|
ERROR(NS_ERROR_DOM_QUOTA_REACHED, FAILURE(1014)),
|
||||||
ERROR(NS_ERROR_DOM_JS_EXCEPTION, FAILURE(1015)),
|
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
|
#undef MODULE
|
||||||
|
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче