Bug 600307 - localStorage and sessionStorage implementation overhaul, r=mak77+smaug, sr=smaug

This commit is contained in:
Honza Bambas 2013-04-11 18:26:06 +02:00
Родитель 899ae8e46f
Коммит 26d97547b7
72 изменённых файлов: 6127 добавлений и 6801 удалений

Просмотреть файл

@ -25,9 +25,9 @@ let gTests = [
desc: "Check that clearing cookies does not clear storage",
setup: function ()
{
Cc["@mozilla.org/dom/storagemanager;1"]
.getService(Ci.nsIObserver)
.observe(null, "cookie-changed", "cleared");
Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService)
.notifyObservers(null, "cookie-changed", "cleared");
},
run: function (aSnippetsMap)
{

Просмотреть файл

@ -85,7 +85,12 @@ let DomStorage = {
for (let [host, data] in Iterator(aStorageData)) {
let uri = Services.io.newURI(host, null, null);
let principal = Services.scriptSecurityManager.getDocShellCodebasePrincipal(uri, aDocShell);
let storage = aDocShell.getSessionStorageForPrincipal(principal, "", true);
let storageManager = aDocShell.QueryInterface(Components.interfaces.nsIDOMStorageManager);
// There is no need to pass documentURI, it's only used to fill documentURI property of
// domstorage event, which in this case has no consumer. Prevention of events in case
// of missing documentURI will be solved in a followup bug to bug 600307.
let storage = storageManager.createStorage(principal, "", aDocShell.usePrivateBrowsing);
for (let [key, value] in Iterator(data)) {
try {
@ -110,12 +115,8 @@ let DomStorage = {
let storage;
try {
// Using getSessionStorageForPrincipal instead of
// getSessionStorageForURI just to be able to pass aCreate = false,
// that avoids creation of the sessionStorage object for the page
// earlier than the page really requires it. It was causing problems
// while accessing a storage when a page later changed its domain.
storage = aDocShell.getSessionStorageForPrincipal(aPrincipal, "", false);
let storageManager = aDocShell.QueryInterface(Components.interfaces.nsIDOMStorageManager);
storage = storageManager.getStorage(aPrincipal);
} catch (e) {
// sessionStorage might throw if it's turned off, see bug 458954
}

Просмотреть файл

@ -16,7 +16,6 @@
#include "nsPIDOMWindow.h"
#include "nsFrameLoader.h"
#include "nsDOMTouchEvent.h"
#include "nsDOMStorage.h"
#include "GeckoProfiler.h"
#include "GeneratedEvents.h"
#include "mozilla/dom/EventTarget.h"

Просмотреть файл

@ -849,8 +849,6 @@ nsDocShell::Init()
rv = mContentListener->Init();
NS_ENSURE_SUCCESS(rv, rv);
mStorages.Init();
// We want to hold a strong ref to the loadgroup, so it better hold a weak
// ref to us... use an InterfaceRequestorProxy to do this.
nsCOMPtr<InterfaceRequestorProxy> proxy =
@ -917,6 +915,7 @@ NS_INTERFACE_MAP_BEGIN(nsDocShell)
NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
///*****************************************************************************
@ -2596,199 +2595,70 @@ nsDocShell::HistoryTransactionRemoved(int32_t aIndex)
return NS_OK;
}
nsIDOMStorageManager*
nsDocShell::TopSessionStorageManager()
{
nsresult rv;
nsCOMPtr<nsIDocShellTreeItem> topItem;
rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
if (NS_FAILED(rv)) {
return nullptr;
}
if (!topItem) {
return nullptr;
}
nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
if (topDocShell != this) {
return topDocShell->TopSessionStorageManager();
}
if (!mSessionStorageManager) {
mSessionStorageManager =
do_CreateInstance("@mozilla.org/dom/sessionStorage-manager;1");
}
return mSessionStorageManager;
}
NS_IMETHODIMP
nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
const nsAString& aDocumentURI,
bool aCreate,
nsIDOMStorage** aStorage)
{
NS_ENSURE_ARG_POINTER(aStorage);
*aStorage = nullptr;
if (!aPrincipal)
return NS_OK;
nsresult rv;
nsCOMPtr<nsIDocShellTreeItem> topItem;
rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
if (NS_FAILED(rv))
return rv;
if (!topItem)
return NS_ERROR_FAILURE;
nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
if (topDocShell != this)
return topDocShell->GetSessionStorageForPrincipal(aPrincipal,
aDocumentURI,
aCreate,
aStorage);
nsXPIDLCString origin;
rv = aPrincipal->GetOrigin(getter_Copies(origin));
if (NS_FAILED(rv))
return rv;
if (origin.IsEmpty())
return NS_OK;
if (!mStorages.Get(origin, aStorage) && aCreate) {
nsCOMPtr<nsIDOMStorage> newstorage =
do_CreateInstance("@mozilla.org/dom/storage;2");
if (!newstorage)
return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(newstorage);
if (!pistorage)
return NS_ERROR_FAILURE;
rv = pistorage->InitAsSessionStorage(aPrincipal, aDocumentURI, mInPrivateBrowsing);
if (NS_FAILED(rv))
return rv;
mStorages.Put(origin, newstorage);
newstorage.swap(*aStorage);
#if defined(PR_LOGGING) && defined(DEBUG)
PR_LOG(gDocShellLog, PR_LOG_DEBUG,
("nsDocShell[%p]: created a new sessionStorage %p",
this, *aStorage));
#endif
}
else if (*aStorage) {
nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(*aStorage);
if (piStorage) {
nsCOMPtr<nsIPrincipal> storagePrincipal = piStorage->Principal();
// The origin string used to map items in the hash table is
// an implicit security check. That check is double-confirmed
// by checking the principal a storage was demanded for
// really is the principal for which that storage was originally
// created. Originally, the check was hidden in the CanAccess
// method but it's implementation has changed.
bool equals;
nsresult rv = aPrincipal->EqualsIgnoringDomain(storagePrincipal, &equals);
NS_ASSERTION(NS_SUCCEEDED(rv) && equals,
"GetSessionStorageForPrincipal got a storage "
"that could not be accessed!");
if (NS_FAILED(rv) || !equals) {
NS_RELEASE(*aStorage);
return NS_ERROR_DOM_SECURITY_ERR;
}
}
#if defined(PR_LOGGING) && defined(DEBUG)
PR_LOG(gDocShellLog, PR_LOG_DEBUG,
("nsDocShell[%p]: returns existing sessionStorage %p",
this, *aStorage));
#endif
nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
if (!manager) {
return NS_ERROR_UNEXPECTED;
}
if (aCreate) {
// We are asked to create a new storage object. This indicates
// that a new windows wants it. At this moment we "fork" the existing
// storage object (what it means is described in the paragraph bellow).
// We must create a single object per a single window to distinguish
// a window originating oparations on the storage object to succesfully
// prevent dispatch of a storage event to this same window that ivoked
// a change in its storage. We also do this to correctly fill
// documentURI property in the storage event.
//
// The difference between clone and fork is that clone creates
// a completelly new and independent storage, but fork only creates
// a new object wrapping the storage implementation and data and
// the forked storage then behaves completelly the same way as
// the storage it has been forked of, all such forked storage objects
// shares their state and data and change on one such object affects
// all others the same way.
nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(*aStorage);
nsCOMPtr<nsIDOMStorage> fork = piStorage->Fork(aDocumentURI);
#if defined(PR_LOGGING) && defined(DEBUG)
PR_LOG(gDocShellLog, PR_LOG_DEBUG,
("nsDocShell[%p]: forked sessionStorage %p to %p",
this, *aStorage, fork.get()));
#endif
fork.swap(*aStorage);
return manager->CreateStorage(aPrincipal, aDocumentURI,
mInPrivateBrowsing, aStorage);
}
return NS_OK;
return manager->GetStorage(aPrincipal, mInPrivateBrowsing, aStorage);
}
nsresult
nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal,
nsIDOMStorage* aStorage)
{
NS_ENSURE_ARG_POINTER(aStorage);
if (!aPrincipal)
return NS_OK;
nsCOMPtr<nsIDocShellTreeItem> topItem;
nsresult rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
if (NS_FAILED(rv))
return rv;
if (topItem) {
nsCOMPtr<nsIDocShell> topDocShell = do_QueryInterface(topItem);
if (topDocShell == this) {
nsXPIDLCString origin;
rv = aPrincipal->GetOrigin(getter_Copies(origin));
if (NS_FAILED(rv))
return rv;
if (origin.IsEmpty())
return NS_ERROR_FAILURE;
// Do not replace an existing session storage.
if (mStorages.GetWeak(origin))
return NS_ERROR_NOT_AVAILABLE;
#if defined(PR_LOGGING) && defined(DEBUG)
PR_LOG(gDocShellLog, PR_LOG_DEBUG,
("nsDocShell[%p]: was added a sessionStorage %p",
this, aStorage));
#endif
mStorages.Put(origin, aStorage);
}
else {
return topDocShell->AddSessionStorage(aPrincipal, aStorage);
}
}
return NS_OK;
}
static PLDHashOperator
CloneSessionStorages(nsCStringHashKey::KeyType aKey, nsIDOMStorage* aStorage,
void* aUserArg)
{
nsIDocShell *docShell = static_cast<nsIDocShell*>(aUserArg);
nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(aStorage);
if (pistorage) {
nsCOMPtr<nsIDOMStorage> storage = pistorage->Clone();
docShell->AddSessionStorage(pistorage->Principal(), storage);
nsIPrincipal* storagePrincipal = pistorage->GetPrincipal();
if (storagePrincipal != aPrincipal) {
NS_ERROR("Wanting to add a sessionStorage for different principal");
return NS_ERROR_DOM_SECURITY_ERR;
}
return PL_DHASH_NEXT;
}
nsCOMPtr<nsIDOMStorageManager> manager = TopSessionStorageManager();
if (!manager) {
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
nsDocShell::CloneSessionStoragesTo(nsIDocShell* aDocShell)
{
aDocShell->ClearSessionStorages();
mStorages.EnumerateRead(CloneSessionStorages, aDocShell);
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::ClearSessionStorages()
{
mStorages.Clear();
return NS_OK;
return manager->CloneStorage(aStorage);
}
NS_IMETHODIMP

Просмотреть файл

@ -23,6 +23,7 @@
#include "nsITextScroll.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIContentViewerContainer.h"
#include "nsIDOMStorageManager.h"
#include "nsDocLoader.h"
#include "nsIURILoader.h"
@ -146,7 +147,8 @@ class nsDocShell : public nsDocLoader,
public nsILoadContext,
public nsIWebShellServices,
public nsILinkHandler,
public nsIClipboardCommands
public nsIClipboardCommands,
public nsIDOMStorageManager
{
friend class nsDSURIContentListener;
@ -178,6 +180,7 @@ public:
NS_DECL_NSIOBSERVER
NS_DECL_NSICLIPBOARDCOMMANDS
NS_DECL_NSIWEBSHELLSERVICES
NS_FORWARD_SAFE_NSIDOMSTORAGEMANAGER(TopSessionStorageManager())
NS_IMETHOD Stop() {
// Need this here because otherwise nsIWebNavigation::Stop
@ -628,10 +631,8 @@ protected:
void ReattachEditorToWindow(nsISHEntry *aSHEntry);
nsresult GetSessionStorageForURI(nsIURI* aURI,
const nsSubstring& aDocumentURI,
bool create,
nsIDOMStorage** aStorage);
nsCOMPtr<nsIDOMStorageManager> mSessionStorageManager;
nsIDOMStorageManager* TopSessionStorageManager();
// helpers for executing commands
nsresult GetControllerForCommand(const char *inCommand,
@ -676,9 +677,6 @@ protected:
bool HasUnloadedParent();
// hash of session storages, keyed by domain
nsInterfaceHashtable<nsCStringHashKey, nsIDOMStorage> mStorages;
// Dimensions of the docshell
nsIntRect mBounds;
nsString mName;

Просмотреть файл

@ -40,7 +40,7 @@ interface nsIWebBrowserPrint;
interface nsIVariant;
interface nsIPrivacyTransitionObserver;
[scriptable, builtinclass, uuid(4277354d-5069-4278-935a-5d596ce9bfbf)]
[scriptable, builtinclass, uuid(2b192c9c-dea4-4696-a445-1bef7bc0db6d)]
interface nsIDocShell : nsIDocShellTreeItem
{
/**
@ -407,6 +407,8 @@ interface nsIDocShell : nsIDocShellTreeItem
void historyPurged(in long numEntries);
/*
* @deprecated, use nsIDocShell.QueryInterface(nsIDOMStorageManager) instead.
*
* Retrieves the WebApps session storage object for the supplied principal.
*
* @param principal returns a storage for this principal
@ -420,6 +422,8 @@ interface nsIDocShell : nsIDocShellTreeItem
in boolean create);
/*
* @deprecated, use nsIDocShell.QueryInterface(nsIDOMStorageManager) instead.
*
* Add a WebApps session storage object to the docshell.
*
* @param principal the principal the storage object is associated with
@ -427,19 +431,6 @@ interface nsIDocShell : nsIDocShellTreeItem
*/
void addSessionStorage(in nsIPrincipal principal, in nsIDOMStorage storage);
/**
* Clones all session storage objects and attaches them to the given docshell.
* Useful when duplicating tabs and their states.
*
* @param docShell the docshell to clone the sessionstorage objects to
*/
void cloneSessionStoragesTo(in nsIDocShell docShell);
/**
* Removes all WebApps session storage objects attached to the docshell.
*/
void clearSessionStorages();
/**
* Gets the channel for the currently loaded document, if any.
* For a new document load, this will be the channel of the previous document

Просмотреть файл

@ -250,7 +250,7 @@
#include "nsIImageDocument.h"
// Storage includes
#include "nsDOMStorage.h"
#include "DOMStorage.h"
// Device Storage
#include "nsIDOMDeviceStorage.h"
@ -815,9 +815,6 @@ static nsDOMClassInfoData sClassInfoData[] = {
// since a call to addProperty() is always followed by a call to
// setProperty(), except in the case when a getter or setter is set
// for a property. But we don't care about getters or setters here.
NS_DEFINE_CLASSINFO_DATA(StorageObsolete, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(Storage, nsStorage2SH,
DOM_DEFAULT_SCRIPTABLE_FLAGS |
nsIXPCScriptable::WANT_NEWRESOLVE |
@ -826,8 +823,6 @@ static nsDOMClassInfoData sClassInfoData[] = {
nsIXPCScriptable::WANT_DELPROPERTY |
nsIXPCScriptable::DONT_ENUM_STATIC_PROPS |
nsIXPCScriptable::WANT_NEWENUMERATE)
NS_DEFINE_CLASSINFO_DATA(StorageItem, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(XULCommandEvent, nsEventSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
@ -2203,19 +2198,10 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMXPathResult)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(StorageObsolete, nsIDOMStorageObsolete)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageObsolete)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(Storage, nsIDOMStorage)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorage)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(StorageItem, nsIDOMStorageItem)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageItem)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMToString)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(XULCommandEvent, nsIDOMXULCommandEvent)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULCommandEvent)
DOM_CLASSINFO_UI_EVENT_MAP_ENTRIES

Просмотреть файл

@ -150,9 +150,7 @@ DOMCI_CLASS(XPathNSResolver)
DOMCI_CLASS(XPathResult)
// WhatWG WebApps Objects
DOMCI_CLASS(StorageObsolete)
DOMCI_CLASS(Storage)
DOMCI_CLASS(StorageItem)
DOMCI_CLASS(XULCommandEvent)
DOMCI_CLASS(CommandEvent)

Просмотреть файл

@ -18,12 +18,15 @@
#include "nsPerformance.h"
#include "nsDOMNavigationTiming.h"
#include "nsBarProps.h"
#include "nsDOMStorage.h"
#include "nsIDOMStorage.h"
#include "nsIDOMStorageManager.h"
#include "DOMStorage.h"
#include "nsDOMOfflineResourceList.h"
#include "nsError.h"
#include "nsIIdleService.h"
#include "nsIPowerManagerService.h"
#include "nsISizeOfEventTarget.h"
#include "nsIPermissionManager.h"
#ifdef XP_WIN
#ifdef GetClassName
@ -2503,9 +2506,38 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
}
}
PreloadLocalStorage();
return NS_OK;
}
void
nsGlobalWindow::PreloadLocalStorage()
{
if (!Preferences::GetBool(kStorageEnabled)) {
return;
}
if (IsChromeWindow()) {
return;
}
nsIPrincipal* principal = GetPrincipal();
if (!principal) {
return;
}
nsresult rv;
nsCOMPtr<nsIDOMStorageManager> storageManager =
do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
if (NS_FAILED(rv)) {
return;
}
storageManager->PrecacheStorage(principal);
}
void
nsGlobalWindow::DispatchDOMWindowCreated()
{
@ -2691,19 +2723,6 @@ nsGlobalWindow::DetachFromDocShell()
MaybeForgiveSpamCount();
CleanUp(false);
if (mLocalStorage) {
nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_GetInterface(mLocalStorage);
if (obs) {
mDocShell->AddWeakPrivacyTransitionObserver(obs);
}
}
if (mSessionStorage) {
nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_GetInterface(mSessionStorage);
if (obs) {
mDocShell->AddWeakPrivacyTransitionObserver(obs);
}
}
}
void
@ -8809,7 +8828,7 @@ nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
"window %x owned sessionStorage "
"that could not be accessed!");
if (!canAccess) {
mSessionStorage = nullptr;
mSessionStorage = nullptr;
}
}
}
@ -8823,7 +8842,7 @@ nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
}
// If the document has the sandboxed origin flag set
// don't allow access to localStorage.
// don't allow access to sessionStorage.
if (!mDoc) {
return NS_ERROR_FAILURE;
}
@ -8832,10 +8851,17 @@ nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
return NS_ERROR_DOM_SECURITY_ERR;
}
nsresult rv = docShell->GetSessionStorageForPrincipal(principal,
documentURI,
true,
getter_AddRefs(mSessionStorage));
nsresult rv;
nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(docShell, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
rv = storageManager->CreateStorage(principal,
documentURI,
loadContext && loadContext->UsePrivateBrowsing(),
getter_AddRefs(mSessionStorage));
NS_ENSURE_SUCCESS(rv, rv);
#ifdef PR_LOGGING
@ -8847,17 +8873,12 @@ nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
if (!mSessionStorage) {
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_GetInterface(mSessionStorage);
if (obs) {
docShell->AddWeakPrivacyTransitionObserver(obs);
}
}
#ifdef PR_LOGGING
if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
PR_LogPrint("nsGlobalWindow %p returns %p sessionStorage", this, mSessionStorage.get());
}
if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
PR_LogPrint("nsGlobalWindow %p returns %p sessionStorage", this, mSessionStorage.get());
}
#endif
NS_ADDREF(*aSessionStorage = mSessionStorage);
@ -8881,41 +8902,38 @@ nsGlobalWindow::GetLocalStorage(nsIDOMStorage ** aLocalStorage)
nsresult rv;
if (!nsDOMStorage::CanUseStorage())
if (!DOMStorage::CanUseStorage()) {
return NS_ERROR_DOM_SECURITY_ERR;
}
nsIPrincipal *principal = GetPrincipal();
if (!principal)
if (!principal) {
return NS_OK;
}
nsCOMPtr<nsIDOMStorageManager> storageManager =
do_GetService("@mozilla.org/dom/storagemanager;1", &rv);
do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsString documentURI;
if (mDocument) {
mDocument->GetDocumentURI(documentURI);
}
// If the document has the sandboxed origin flag set
// don't allow access to localStorage.
if (mDoc && (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
nsString documentURI;
if (mDocument) {
mDocument->GetDocumentURI(documentURI);
}
nsIDocShell* docShell = GetDocShell();
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
rv = storageManager->GetLocalStorageForPrincipal(principal,
documentURI,
loadContext && loadContext->UsePrivateBrowsing(),
getter_AddRefs(mLocalStorage));
rv = storageManager->CreateStorage(principal,
documentURI,
loadContext && loadContext->UsePrivateBrowsing(),
getter_AddRefs(mLocalStorage));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_GetInterface(mLocalStorage);
if (obs && docShell) {
docShell->AddWeakPrivacyTransitionObserver(obs);
}
}
NS_ADDREF(*aLocalStorage = mLocalStorage);
@ -9489,8 +9507,13 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
rv = event->GetStorageArea(getter_AddRefs(changingStorage));
NS_ENSURE_SUCCESS(rv, rv);
bool fireMozStorageChanged = false;
principal = GetPrincipal();
if (!principal) {
return NS_OK;
}
nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(changingStorage);
nsPIDOMStorage::nsDOMStorageType storageType = pistorage->StorageType();
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell());
bool isPrivate = loadContext && loadContext->UsePrivateBrowsing();
@ -9498,28 +9521,21 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
return NS_OK;
}
bool fireMozStorageChanged = false;
principal = GetPrincipal();
switch (storageType)
switch (pistorage->GetType())
{
case nsPIDOMStorage::SessionStorage:
{
nsCOMPtr<nsIDOMStorage> storage = mSessionStorage;
if (!storage) {
nsIDocShell* docShell = GetDocShell();
if (principal && docShell) {
// No need to pass documentURI here, it's only needed when we want
// to create a new storage, the third paramater would be true
docShell->GetSessionStorageForPrincipal(principal,
EmptyString(),
false,
getter_AddRefs(storage));
}
bool check = false;
nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(GetDocShell());
if (storageManager) {
rv = storageManager->CheckStorage(principal, changingStorage, &check);
NS_ENSURE_SUCCESS(rv, rv);
}
if (!pistorage->IsForkOf(storage)) {
// This storage event is coming from a different doc shell,
// i.e. it is a clone, ignore this event.
if (!check) {
// This storage event is not coming from our storage or is coming
// from a different docshell, i.e. it is a clone, ignore this event.
return NS_OK;
}
@ -9532,13 +9548,14 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
fireMozStorageChanged = SameCOMIdentity(mSessionStorage, changingStorage);
break;
}
case nsPIDOMStorage::LocalStorage:
{
// Allow event fire only for the same principal storages
// XXX We have to use EqualsIgnoreDomain after bug 495337 lands
nsIPrincipal *storagePrincipal = pistorage->Principal();
bool equals;
nsIPrincipal* storagePrincipal = pistorage->GetPrincipal();
bool equals = false;
rv = storagePrincipal->Equals(principal, &equals);
NS_ENSURE_SUCCESS(rv, rv);

Просмотреть файл

@ -46,7 +46,6 @@
#include "nsRect.h"
#include "mozFlushType.h"
#include "prclist.h"
#include "nsIDOMStorageObsolete.h"
#include "nsIDOMStorageEvent.h"
#include "nsIDOMStorageIndexedDB.h"
#include "nsIDOMOfflineResourceList.h"
@ -1022,6 +1021,8 @@ protected:
bool aDefaultStylesOnly,
nsIDOMCSSStyleDeclaration** aReturn);
void PreloadLocalStorage();
// When adding new member variables, be careful not to create cycles
// through JavaScript. If there is any chance that a member variable
// could own objects that are implemented in JavaScript, then those

Просмотреть файл

@ -17,7 +17,6 @@
#include "CheckQuotaHelper.h"
#include "nsContentUtils.h"
#include "nsDOMStorage.h"
#include "nsNetUtil.h"
#include "nsThreadUtils.h"
#include "mozilla/dom/quota/QuotaManager.h"

Просмотреть файл

@ -8,9 +8,7 @@ XPIDL_SOURCES += [
'nsIDOMStorage.idl',
'nsIDOMStorageEvent.idl',
'nsIDOMStorageIndexedDB.idl',
'nsIDOMStorageItem.idl',
'nsIDOMStorageManager.idl',
'nsIDOMStorageObsolete.idl',
'nsIDOMToString.idl',
]

Просмотреть файл

@ -1,28 +0,0 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "domstubs.idl"
/**
* Interface for a client side storage item. See
* http://www.whatwg.org/specs/web-apps/current-work/#scs-client-side
* for more information.
*
* A respresentation of a storage object item.
*/
[scriptable, uuid(0CC37C78-4C5F-48E1-ADFC-7480B8FE9DC4)]
interface nsIDOMStorageItem : nsISupports
{
/**
* Indicates whether a key is available only in a secure context.
*/
attribute boolean secure;
/**
* The value associated with the item.
*/
attribute DOMString value;
};

Просмотреть файл

@ -8,23 +8,89 @@
interface nsIDOMStorage;
interface nsIPrincipal;
[scriptable, uuid(b16b207c-d883-43f5-a27e-548e7f2f5c20)]
/**
* General purpose interface that has two implementations, for localStorage
* resp. sessionStorage with "@mozilla.org/dom/localStorage-manager;1" resp.
* "@mozilla.org/dom/sessionStorage-manager;1" contract IDs.
*/
[scriptable, uuid(8096f9ea-fa61-4960-b5d7-fb30ac42c8d8)]
interface nsIDOMStorageManager : nsISupports
{
/**
* Return the amount of disk space used by a domain. Usage is checked
* against the domain of the page that set the key (the owner domain), not
* the domain of the storage object.
*
* @param aOwnerDomain The domain to check.
* @returns the space usage of the domain, in bytes.
* This starts async preloading of a storage cache for scope
* defined by the principal.
*/
long getUsage(in AString aOwnerDomain);
void precacheStorage(in nsIPrincipal aPrincipal);
/**
* Returns instance of DOM storage object for given principal.
* A new object is always returned and it is ensured there is
* a storage for the scope created.
*
* @param aPrincipal
* Principal to bound storage to.
* @param aDocumentURI
* URL of the demanding document, used for DOM storage event only.
* @param aPrivate
* Whether the demanding document is running in Private Browsing mode or not.
*/
nsIDOMStorage createStorage(in nsIPrincipal aPrincipal,
in DOMString aDocumentURI,
[optional] in bool aPrivate);
/**
* Returns instance of DOM storage object for given principal.
* If there is no storage managed for the scope, then null is returned and
* no object is created. Otherwise, an object (new) for the existing storage
* scope is returned.
*
* @param aPrincipal
* Principal to bound storage to.
* @param aPrivate
* Whether the demanding document is running in Private Browsing mode or not.
*/
nsIDOMStorage getStorage(in nsIPrincipal aPrincipal,
[optional] in bool aPrivate);
/**
* Clones given storage into this storage manager.
*
* @param aStorageToCloneFrom
* The storage to copy all items from into this manager. Manager will then
* return a new and independent object that contains snapshot of data from
* the moment this method was called. Modification to this new object will
* not affect the original storage content we cloned from and vice versa.
*/
void cloneStorage(in nsIDOMStorage aStorageToCloneFrom);
/**
* Returns true if the storage belongs to the given principal and is managed
* (i.e. has been created and is cached) by this storage manager.
*
* @param aPrincipal
* Principal to check the storage against.
* @param aStorage
* The storage object to examine.
*
* @result
* true when the storage object is bound with the principal and is managed
* by this storage manager.
* false otherwise
*/
bool checkStorage(in nsIPrincipal aPrincipal,
in nsIDOMStorage aStorage);
/**
* @deprecated
*
* Returns instance of localStorage object for aURI's origin.
* This method ensures there is always only a single instance
* for a single origin.
*
* Currently just forwards to the createStorage method of this
* interface.
*
* Extension developers are strongly encouraged to use getStorage
* or createStorage method instead.
*/
nsIDOMStorage getLocalStorageForPrincipal(in nsIPrincipal aPrincipal,
in DOMString aDocumentURI,

Просмотреть файл

@ -1,65 +0,0 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "domstubs.idl"
/**
* Interface for client side storage. See
* http://www.whatwg.org/specs/web-apps/current-work/#scs-client-side
* for more information.
*
* A storage object stores an arbitrary set of key-value pairs, which
* may be retrieved, modified and removed as needed. A key may only
* exist once within a storage object, and only one value may be
* associated with a particular key. Keys are stored in a particular
* order with the condition that this order not change by merely changing
* the value associated with a key, but the order may change when a
* key is added or removed.
*/
interface nsIDOMStorageItem;
[scriptable, uuid(18013CF9-B104-49cf-9484-C2A7A845457E)]
interface nsIDOMStorageObsolete : nsISupports
{
/**
* The number of keys stored.
*/
readonly attribute unsigned long length;
/**
* Retrieve the name of the key at a particular index.
*
* @param index index of the item to retrieve
* @returns the key at index
* @throws INDEX_SIZE_ERR if there is no key at that index
*/
DOMString key(in unsigned long index);
/**
* Retrieve an item with a given key
*
* @param key key to retrieve
* @returns found item or null if the key was not found
*/
nsIDOMStorageItem getItem(in DOMString key);
/**
* Assign a value with a key. If the key does not exist already, a new
* key is added associated with that value. If the key already exists,
* then the existing value is replaced with a new value.
*
* @param key key to set
* @param data data to associate with the key
*/
void setItem(in DOMString key, in DOMString data);
/**
* Remove a key and its corresponding value.
*
* @param key key to remove
*/
void removeItem(in DOMString key);
};

Просмотреть файл

@ -8,43 +8,44 @@
#define __nsPIDOMStorage_h_
#include "nsISupports.h"
#include "nsString.h"
#include "nsTArray.h"
class nsIDOMStorageObsolete;
class nsIURI;
class nsIPrincipal;
namespace mozilla {
namespace dom {
class DOMStorageCache;
class DOMStorageManager;
} // ::dom
} // ::mozilla
// {09198A51-5D27-4992-97E4-38A9CEA2A65D}
#define NS_PIDOMSTORAGE_IID \
{ 0x9c292365, 0x6ae4, 0x461b, \
{ 0x86, 0x98, 0xf5, 0x23, 0x6f, 0xfa, 0xd2, 0x30 } }
{ 0x9198a51, 0x5d27, 0x4992, \
{ 0x97, 0xe4, 0x38, 0xa9, 0xce, 0xa2, 0xa6, 0x5d } }
class nsPIDOMStorage : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMSTORAGE_IID)
typedef enum {
Unknown = 0,
enum StorageType {
LocalStorage = 1,
SessionStorage = 2
} nsDOMStorageType;
};
virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
bool aPrivate) = 0;
virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
bool aPrivate) = 0;
virtual StorageType GetType() const = 0;
virtual mozilla::dom::DOMStorageManager* GetManager() const = 0;
virtual const mozilla::dom::DOMStorageCache* GetCache() const = 0;
virtual already_AddRefed<nsIDOMStorage> Clone() = 0;
virtual already_AddRefed<nsIDOMStorage> Fork(const nsSubstring &aDocumentURI) = 0;
virtual bool IsForkOf(nsIDOMStorage* aThat) = 0;
virtual nsTArray<nsString>* GetKeys() = 0;
virtual nsTArray<nsString> *GetKeys() = 0;
virtual nsIPrincipal* Principal() = 0;
virtual nsIPrincipal* GetPrincipal() = 0;
virtual bool PrincipalEquals(nsIPrincipal* principal) = 0;
virtual bool CanAccess(nsIPrincipal *aPrincipal) = 0;
virtual nsDOMStorageType StorageType() = 0;
virtual bool IsPrivate() = 0;
};

Просмотреть файл

@ -19,7 +19,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/dom/ExternalHelperAppChild.h"
#include "mozilla/dom/PCrashReporterChild.h"
#include "mozilla/dom/StorageChild.h"
#include "mozilla/dom/DOMStorageIPC.h"
#include "mozilla/Hal.h"
#include "mozilla/hal_sandbox/PHalChild.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
@ -839,7 +839,7 @@ ContentChild::DeallocPSms(PSmsChild* aSms)
}
PStorageChild*
ContentChild::AllocPStorage(const StorageConstructData& aData)
ContentChild::AllocPStorage()
{
NS_NOTREACHED("We should never be manually allocating PStorageChild actors");
return nullptr;
@ -848,7 +848,7 @@ ContentChild::AllocPStorage(const StorageConstructData& aData)
bool
ContentChild::DeallocPStorage(PStorageChild* aActor)
{
StorageChild* child = static_cast<StorageChild*>(aActor);
DOMStorageDBChild* child = static_cast<DOMStorageDBChild*>(aActor);
child->ReleaseIPDLReference();
return true;
}

Просмотреть файл

@ -142,7 +142,7 @@ public:
virtual PSmsChild* AllocPSms();
virtual bool DeallocPSms(PSmsChild*);
virtual PStorageChild* AllocPStorage(const StorageConstructData& aData);
virtual PStorageChild* AllocPStorage();
virtual bool DeallocPStorage(PStorageChild* aActor);
virtual PBluetoothChild* AllocPBluetooth();

Просмотреть файл

@ -29,7 +29,7 @@
#include "mozilla/dom/ExternalHelperAppParent.h"
#include "mozilla/dom/PMemoryReportRequestParent.h"
#include "mozilla/dom/power/PowerManagerService.h"
#include "mozilla/dom/StorageParent.h"
#include "mozilla/dom/DOMStorageIPC.h"
#include "mozilla/dom/bluetooth/PBluetoothParent.h"
#include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
#include "SmsParent.h"
@ -1970,15 +1970,16 @@ ContentParent::DeallocPSms(PSmsParent* aSms)
}
PStorageParent*
ContentParent::AllocPStorage(const StorageConstructData& aData)
ContentParent::AllocPStorage()
{
return new StorageParent(aData);
return new DOMStorageDBParent();
}
bool
ContentParent::DeallocPStorage(PStorageParent* aActor)
{
delete aActor;
DOMStorageDBParent* child = static_cast<DOMStorageDBParent*>(aActor);
child->ReleaseIPDLReference();
return true;
}

Просмотреть файл

@ -285,7 +285,7 @@ private:
virtual PSmsParent* AllocPSms();
virtual bool DeallocPSms(PSmsParent*);
virtual PStorageParent* AllocPStorage(const StorageConstructData& aData);
virtual PStorageParent* AllocPStorage();
virtual bool DeallocPStorage(PStorageParent* aActor);
virtual PBluetoothParent* AllocPBluetooth();

Просмотреть файл

@ -50,23 +50,6 @@ using gfxIntSize;
namespace mozilla {
namespace dom {
// Data required to clone an existing DOMStorageImpl in the parent
struct StorageClone
{
// Existing cross-process storage actor to clone
PStorage actor;
// Result of calling IsCallerSecure() in the child
bool callerSecure;
};
// When creating a new PStorage protocol, an existing one can be
// cloned (see nsDOMStorage2::Clone)
union StorageConstructData
{
null_t;
StorageClone;
};
struct FontListEntry {
nsString familyName;
nsString faceName;
@ -401,7 +384,7 @@ parent:
PSpeechSynthesis();
PStorage(StorageConstructData data);
PStorage();
PBluetooth();

Просмотреть файл

@ -0,0 +1,353 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DOMStorage.h"
#include "DOMStorageCache.h"
#include "DOMStorageManager.h"
#include "nsIDOMStorageEvent.h"
#include "nsIObserverService.h"
#include "nsIScriptSecurityManager.h"
#include "nsIPermissionManager.h"
#include "nsIPrincipal.h"
#include "nsICookiePermission.h"
#include "nsDOMClassInfoID.h"
#include "mozilla/Services.h"
#include "mozilla/Preferences.h"
#include "GeneratedEvents.h"
#include "nsThreadUtils.h"
#include "nsContentUtils.h"
#include "nsServiceManagerUtils.h"
DOMCI_DATA(Storage, mozilla::dom::DOMStorage)
namespace mozilla {
namespace dom {
NS_IMPL_ADDREF(DOMStorage)
NS_IMPL_RELEASE(DOMStorage)
NS_INTERFACE_MAP_BEGIN(DOMStorage)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorage)
NS_INTERFACE_MAP_ENTRY(nsIDOMStorage)
NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage)
NS_INTERFACE_MAP_END
DOMStorage::DOMStorage(DOMStorageManager* aManager,
DOMStorageCache* aCache,
const nsAString& aDocumentURI,
nsIPrincipal* aPrincipal,
bool aIsPrivate)
: mManager(aManager)
, mCache(aCache)
, mDocumentURI(aDocumentURI)
, mPrincipal(aPrincipal)
, mIsPrivate(aIsPrivate)
, mIsSessionOnly(false)
{
mCache->Preload();
}
DOMStorage::~DOMStorage()
{
mCache->KeepAlive();
}
// nsIDOMStorage (web content public API implementation)
NS_IMETHODIMP
DOMStorage::GetLength(uint32_t* aLength)
{
if (!CanUseStorage(this)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
return mCache->GetLength(this, aLength);
}
NS_IMETHODIMP
DOMStorage::Key(uint32_t aIndex, nsAString& aRetval)
{
if (!CanUseStorage(this)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
return mCache->GetKey(this, aIndex, aRetval);
}
NS_IMETHODIMP
DOMStorage::GetItem(const nsAString& aKey, nsAString& aRetval)
{
if (!CanUseStorage(this)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
return mCache->GetItem(this, aKey, aRetval);
}
NS_IMETHODIMP
DOMStorage::SetItem(const nsAString& aKey, const nsAString& aData)
{
if (!CanUseStorage(this)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
Telemetry::Accumulate(GetType() == LocalStorage
? Telemetry::LOCALDOMSTORAGE_KEY_SIZE_BYTES
: Telemetry::SESSIONDOMSTORAGE_KEY_SIZE_BYTES, aKey.Length());
Telemetry::Accumulate(GetType() == LocalStorage
? Telemetry::LOCALDOMSTORAGE_VALUE_SIZE_BYTES
: Telemetry::SESSIONDOMSTORAGE_VALUE_SIZE_BYTES, aData.Length());
nsString old;
nsresult rv = mCache->SetItem(this, aKey, nsString(aData), old);
if (NS_FAILED(rv)) {
return rv;
}
if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
BroadcastChangeNotification(aKey, old, aData);
}
return NS_OK;
}
NS_IMETHODIMP
DOMStorage::RemoveItem(const nsAString& aKey)
{
if (!CanUseStorage(this)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
nsAutoString old;
nsresult rv = mCache->RemoveItem(this, aKey, old);
if (NS_FAILED(rv)) {
return rv;
}
if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
BroadcastChangeNotification(aKey, old, NullString());
}
return NS_OK;
}
NS_IMETHODIMP
DOMStorage::Clear()
{
if (!CanUseStorage(this)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
nsresult rv = mCache->Clear(this);
if (NS_FAILED(rv)) {
return rv;
}
if (rv != NS_SUCCESS_DOM_NO_OPERATION) {
BroadcastChangeNotification(NullString(), NullString(), NullString());
}
return NS_OK;
}
namespace {
class StorageNotifierRunnable : public nsRunnable
{
public:
StorageNotifierRunnable(nsISupports* aSubject)
: mSubject(aSubject)
{ }
NS_DECL_NSIRUNNABLE
private:
nsCOMPtr<nsISupports> mSubject;
};
NS_IMETHODIMP
StorageNotifierRunnable::Run()
{
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->NotifyObservers(mSubject, "dom-storage2-changed", nullptr);
}
return NS_OK;
}
} // anonymous namespace
void
DOMStorage::BroadcastChangeNotification(const nsSubstring& aKey,
const nsSubstring& aOldValue,
const nsSubstring& aNewValue)
{
nsCOMPtr<nsIDOMEvent> domEvent;
// Note, this DOM event should never reach JS. It is cloned later in
// nsGlobalWindow.
NS_NewDOMStorageEvent(getter_AddRefs(domEvent), nullptr, nullptr, nullptr);
nsCOMPtr<nsIDOMStorageEvent> event = do_QueryInterface(domEvent);
nsresult rv = event->InitStorageEvent(NS_LITERAL_STRING("storage"),
false,
false,
aKey,
aOldValue,
aNewValue,
mDocumentURI,
static_cast<nsIDOMStorage*>(this));
if (NS_FAILED(rv)) {
return;
}
nsRefPtr<StorageNotifierRunnable> r = new StorageNotifierRunnable(event);
NS_DispatchToMainThread(r);
}
static const uint32_t ASK_BEFORE_ACCEPT = 1;
static const uint32_t ACCEPT_SESSION = 2;
static const uint32_t BEHAVIOR_REJECT = 2;
static const char kPermissionType[] = "cookie";
static const char kStorageEnabled[] = "dom.storage.enabled";
static const char kCookiesBehavior[] = "network.cookie.cookieBehavior";
static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy";
// static, public
bool
DOMStorage::CanUseStorage(DOMStorage* aStorage)
{
// This method is responsible for correct setting of mIsSessionOnly.
// It doesn't work with mIsPrivate flag at all, since it is checked
// regardless mIsSessionOnly flag in DOMStorageCache code.
if (aStorage) {
aStorage->mIsSessionOnly = false;
}
if (!mozilla::Preferences::GetBool(kStorageEnabled)) {
return false;
}
// chrome can always use aStorage regardless of permission preferences
if (nsContentUtils::IsCallerChrome()) {
return true;
}
nsCOMPtr<nsIPrincipal> subjectPrincipal;
nsresult rv = nsContentUtils::GetSecurityManager()->
GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
NS_ENSURE_SUCCESS(rv, false);
// if subjectPrincipal were null we'd have returned after
// IsCallerChrome().
nsCOMPtr<nsIPermissionManager> permissionManager =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
if (!permissionManager) {
return false;
}
uint32_t perm;
permissionManager->TestPermissionFromPrincipal(subjectPrincipal,
kPermissionType, &perm);
if (perm == nsIPermissionManager::DENY_ACTION) {
return false;
}
if (perm == nsICookiePermission::ACCESS_SESSION) {
if (aStorage) {
aStorage->mIsSessionOnly = true;
}
} else if (perm != nsIPermissionManager::ALLOW_ACTION) {
uint32_t cookieBehavior = Preferences::GetUint(kCookiesBehavior);
uint32_t lifetimePolicy = Preferences::GetUint(kCookiesLifetimePolicy);
// Treat "ask every time" as "reject always".
if ((cookieBehavior == BEHAVIOR_REJECT || lifetimePolicy == ASK_BEFORE_ACCEPT)) {
return false;
}
if (lifetimePolicy == ACCEPT_SESSION && aStorage) {
aStorage->mIsSessionOnly = true;
}
}
if (aStorage) {
return aStorage->CanAccess(subjectPrincipal);
}
return true;
}
// nsPIDOMStorage
nsPIDOMStorage::StorageType
DOMStorage::GetType() const
{
return mManager->Type();
}
nsIPrincipal*
DOMStorage::GetPrincipal()
{
return mPrincipal;
}
// Defined in DOMStorageManager.cpp
extern bool
PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal);
bool
DOMStorage::PrincipalEquals(nsIPrincipal* aPrincipal)
{
return PrincipalsEqual(mPrincipal, aPrincipal);
}
bool
DOMStorage::CanAccess(nsIPrincipal* aPrincipal)
{
// Allow C++ callers to access the storage
if (!aPrincipal) {
return true;
}
// For content, either the code base or domain must be the same. When code
// base is the same, this is enough to say it is safe for a page to access
// this storage.
bool subsumes;
nsresult rv = aPrincipal->SubsumesIgnoringDomain(mPrincipal, &subsumes);
if (NS_FAILED(rv)) {
return false;
}
if (!subsumes) {
nsresult rv = aPrincipal->Subsumes(mPrincipal, &subsumes);
if (NS_FAILED(rv)) {
return false;
}
}
return subsumes;
}
nsTArray<nsString>*
DOMStorage::GetKeys()
{
if (!CanUseStorage(this)) {
return new nsTArray<nsString>(); // return just an empty array
}
return mCache->GetKeys(this);
}
} // ::dom
} // ::mozilla

Просмотреть файл

@ -0,0 +1,85 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsDOMStorage_h___
#define nsDOMStorage_h___
#include "nsIDOMStorage.h"
#include "nsPIDOMStorage.h"
#include "nsWeakReference.h"
#include "nsAutoPtr.h"
namespace mozilla {
namespace dom {
class DOMStorageManager;
class DOMStorageCache;
class DOMStorage MOZ_FINAL : public nsIDOMStorage
, public nsPIDOMStorage
, public nsSupportsWeakReference
{
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMSTORAGE
// nsPIDOMStorage
virtual StorageType GetType() const;
virtual DOMStorageManager* GetManager() const { return mManager; }
virtual const DOMStorageCache* GetCache() const { return mCache; }
virtual nsTArray<nsString>* GetKeys();
virtual nsIPrincipal* GetPrincipal();
virtual bool PrincipalEquals(nsIPrincipal* aPrincipal);
virtual bool CanAccess(nsIPrincipal* aPrincipal);
virtual bool IsPrivate() { return mIsPrivate; }
DOMStorage(DOMStorageManager* aManager,
DOMStorageCache* aCache,
const nsAString& aDocumentURI,
nsIPrincipal* aPrincipal,
bool aIsPrivate);
~DOMStorage();
// The method checks whether the caller can use a storage.
// CanUseStorage is called before any DOM initiated operation
// on a storage is about to happen and ensures that the storage's
// session-only flag is properly set according the current settings.
// It is an optimization since the privileges check and session only
// state determination are complex and share the code (comes hand in
// hand together).
static bool CanUseStorage(DOMStorage* aStorage = nullptr);
bool IsPrivate() const { return mIsPrivate; }
bool IsSessionOnly() const { return mIsSessionOnly; }
private:
friend class DOMStorageManager;
friend class DOMStorageCache;
nsRefPtr<DOMStorageManager> mManager;
nsRefPtr<DOMStorageCache> mCache;
nsString mDocumentURI;
// Principal this DOMStorage (i.e. localStorage or sessionStorage) has
// been created for
nsCOMPtr<nsIPrincipal> mPrincipal;
// Whether this storage is running in private-browsing window.
bool mIsPrivate : 1;
// Whether storage is set to persist data only per session, may change
// dynamically and is set by CanUseStorage function that is called
// before any operation on the storage.
bool mIsSessionOnly : 1;
void BroadcastChangeNotification(const nsSubstring& aKey,
const nsSubstring& aOldValue,
const nsSubstring& aNewValue);
};
} // ::dom
} // ::mozilla
#endif /* nsDOMStorage_h___ */

Просмотреть файл

@ -0,0 +1,801 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DOMStorageCache.h"
#include "DOMStorage.h"
#include "DOMStorageDBThread.h"
#include "DOMStorageIPC.h"
#include "DOMStorageManager.h"
#include "nsDOMString.h"
#include "nsXULAppAPI.h"
#include "mozilla/unused.h"
#include "nsProxyRelease.h"
namespace mozilla {
namespace dom {
#define DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS 20000
// static
DOMStorageDBBridge* DOMStorageCache::sDatabase = nullptr;
namespace { // anon
const uint32_t kDefaultSet = 0;
const uint32_t kPrivateSet = 1;
const uint32_t kSessionSet = 2;
inline uint32_t
GetDataSetIndex(bool aPrivate, bool aSessionOnly)
{
if (aPrivate) {
return kPrivateSet;
}
if (aSessionOnly) {
return kSessionSet;
}
return kDefaultSet;
}
inline uint32_t
GetDataSetIndex(const DOMStorage* aStorage)
{
return GetDataSetIndex(aStorage->IsPrivate(), aStorage->IsSessionOnly());
}
} // anon
// DOMStorageCacheBridge
NS_IMPL_THREADSAFE_ADDREF(DOMStorageCacheBridge)
// Since there is no consumer of return value of Release, we can turn this
// method to void to make implementation of asynchronous DOMStorageCache::Release
// much simpler.
NS_IMETHODIMP_(void) DOMStorageCacheBridge::Release(void)
{
MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
nsrefcnt count = NS_AtomicDecrementRefcnt(mRefCnt);
NS_LOG_RELEASE(this, count, "DOMStorageCacheBridge");
if (0 == count) {
mRefCnt = 1; /* stabilize */
/* enable this to find non-threadsafe destructors: */
/* NS_ASSERT_OWNINGTHREAD(_class); */
delete (this);
}
}
// DOMStorageCache
DOMStorageCache::DOMStorageCache(const nsACString* aScope)
: mManager(nullptr)
, mScope(*aScope)
, mMonitor("DOMStorageCache")
, mLoaded(false)
, mLoadResult(NS_OK)
, mInitialized(false)
, mSessionOnlyDataSetActive(false)
, mPreloadTelemetryRecorded(false)
{
MOZ_COUNT_CTOR(DOMStorageCache);
}
DOMStorageCache::~DOMStorageCache()
{
if (mManager) {
mManager->DropCache(this);
}
MOZ_COUNT_DTOR(DOMStorageCache);
}
NS_IMETHODIMP_(void)
DOMStorageCache::Release(void)
{
// We must actually release on the main thread since the cache removes it
// self from the manager's hash table. And we don't want to lock access to
// that hash table.
if (NS_IsMainThread()) {
DOMStorageCacheBridge::Release();
return;
}
nsRefPtr<nsRunnableMethod<DOMStorageCacheBridge, void, false> > event =
NS_NewNonOwningRunnableMethod(static_cast<DOMStorageCacheBridge*>(this),
&DOMStorageCacheBridge::Release);
nsresult rv = NS_DispatchToMainThread(event);
if (NS_FAILED(rv)) {
NS_WARNING("DOMStorageCache::Release() on a non-main thread");
DOMStorageCacheBridge::Release();
}
}
void
DOMStorageCache::Init(DOMStorageManager* aManager,
bool aPersistent,
nsIPrincipal* aPrincipal,
const nsACString& aQuotaScope)
{
if (mInitialized) {
return;
}
mManager = aManager;
mInitialized = true;
mPrincipal = aPrincipal;
mPersistent = aPersistent;
mQuotaScope = aQuotaScope.IsEmpty() ? mScope : aQuotaScope;
if (mPersistent) {
Preload();
}
}
inline bool
DOMStorageCache::Persist(const DOMStorage* aStorage) const
{
return mPersistent &&
!aStorage->IsSessionOnly() &&
!aStorage->IsPrivate();
}
namespace { // anon
PLDHashOperator
CloneSetData(const nsAString& aKey, const nsString aValue, void* aArg)
{
DOMStorageCache::Data* target = static_cast<DOMStorageCache::Data*>(aArg);
target->mKeys.Put(aKey, aValue);
return PL_DHASH_NEXT;
}
} // anon
DOMStorageCache::Data&
DOMStorageCache::DataSet(const DOMStorage* aStorage)
{
uint32_t index = GetDataSetIndex(aStorage);
if (index == kSessionSet && !mSessionOnlyDataSetActive) {
// Session only data set is demanded but not filled with
// current data set, copy to session only set now.
WaitForPreload(Telemetry::LOCALDOMSTORAGE_SESSIONONLY_PRELOAD_BLOCKING_MS);
Data& defaultSet = mData[kDefaultSet];
Data& sessionSet = mData[kSessionSet];
defaultSet.mKeys.EnumerateRead(CloneSetData, &sessionSet);
mSessionOnlyDataSetActive = true;
// This updates sessionSet.mOriginQuotaUsage and also updates global usage
// for all session only data
ProcessUsageDelta(kSessionSet, defaultSet.mOriginQuotaUsage);
}
return mData[index];
}
bool
DOMStorageCache::ProcessUsageDelta(const DOMStorage* aStorage, int64_t aDelta)
{
return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta);
}
bool
DOMStorageCache::ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta)
{
// Check limit per this origin
Data& data = mData[aGetDataSetIndex];
uint64_t newOriginUsage = data.mOriginQuotaUsage + aDelta;
if (aDelta > 0 && newOriginUsage > DOMStorageManager::GetQuota()) {
return false;
}
// Now check eTLD+1 limit
GetDatabase();
if (sDatabase) {
DOMStorageUsage* usage = sDatabase->GetScopeUsage(mQuotaScope);
if (!usage->CheckAndSetETLD1UsageDelta(aGetDataSetIndex, aDelta)) {
return false;
}
}
// Update size in our data set
data.mOriginQuotaUsage = newOriginUsage;
return true;
}
void
DOMStorageCache::Preload()
{
if (mLoaded || !mPersistent) {
return;
}
if (!StartDatabase()) {
mLoaded = true;
mLoadResult = NS_ERROR_FAILURE;
return;
}
sDatabase->AsyncPreload(this);
sDatabase->GetScopeUsage(mQuotaScope);
}
namespace { // anon
// This class is passed to timer as a tick observer. It refers the cache
// and keeps it alive for a time.
class DOMStorageCacheHolder : public nsITimerCallback
{
NS_DECL_ISUPPORTS
NS_IMETHODIMP
Notify(nsITimer* aTimer)
{
mCache = nullptr;
return NS_OK;
}
virtual ~DOMStorageCacheHolder() {}
nsRefPtr<DOMStorageCache> mCache;
public:
DOMStorageCacheHolder(DOMStorageCache* aCache) : mCache(aCache) {}
};
NS_IMPL_ISUPPORTS1(DOMStorageCacheHolder, nsITimerCallback)
} // anon
void
DOMStorageCache::KeepAlive()
{
// Missing reference back to the manager means the cache is not responsible
// for its lifetime. Used for keeping sessionStorage live forever.
if (!mManager) {
return;
}
if (!NS_IsMainThread()) {
// Timer and the holder must be initialized on the main thread.
nsRefPtr<nsRunnableMethod<DOMStorageCache> > event =
NS_NewRunnableMethod(this, &DOMStorageCache::KeepAlive);
NS_DispatchToMainThread(event);
return;
}
nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
if (!timer) {
return;
}
nsRefPtr<DOMStorageCacheHolder> holder = new DOMStorageCacheHolder(this);
timer->InitWithCallback(holder, DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS,
nsITimer::TYPE_ONE_SHOT);
mKeepAliveTimer.swap(timer);
}
namespace { // anon
// The AutoTimer provided by telemetry headers is only using static,
// i.e. compile time known ID, but here we know the ID only at run time.
// Hence a new class.
class TelemetryAutoTimer
{
public:
TelemetryAutoTimer(Telemetry::ID aId)
: id(aId), start(TimeStamp::Now()) {}
~TelemetryAutoTimer()
{ Telemetry::AccumulateDelta_impl<Telemetry::Millisecond>::compute(id, start); }
private:
Telemetry::ID id;
const TimeStamp start;
};
} // anon
void
DOMStorageCache::WaitForPreload(Telemetry::ID aTelemetryID)
{
if (!mPersistent) {
return;
}
bool loaded = mLoaded;
// Telemetry of rates of pending preloads
if (!mPreloadTelemetryRecorded) {
mPreloadTelemetryRecorded = true;
Telemetry::Accumulate(
Telemetry::LOCALDOMSTORAGE_PRELOAD_PENDING_ON_FIRST_ACCESS,
!loaded);
}
if (loaded) {
return;
}
// Measure which operation blocks and for how long
TelemetryAutoTimer timer(aTelemetryID);
// If preload already started (i.e. we got some first data, but not all)
// SyncPreload will just wait for it to finish rather then synchronously
// read from the database. It seems to me more optimal.
// TODO place for A/B testing (force main thread load vs. let preload finish)
sDatabase->SyncPreload(this);
}
nsresult
DOMStorageCache::GetLength(const DOMStorage* aStorage, uint32_t* aRetval)
{
Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETLENGTH_MS> autoTimer;
if (Persist(aStorage)) {
WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETLENGTH_BLOCKING_MS);
if (NS_FAILED(mLoadResult)) {
return mLoadResult;
}
}
*aRetval = DataSet(aStorage).mKeys.Count();
return NS_OK;
}
namespace { // anon
class IndexFinderData
{
public:
IndexFinderData(uint32_t aIndex, nsAString& aRetval)
: mIndex(aIndex), mKey(aRetval)
{
mKey.SetIsVoid(true);
}
uint32_t mIndex;
nsAString& mKey;
};
PLDHashOperator
FindKeyOrder(const nsAString& aKey, const nsString aValue, void* aArg)
{
IndexFinderData* data = static_cast<IndexFinderData*>(aArg);
if (data->mIndex--) {
return PL_DHASH_NEXT;
}
data->mKey = aKey;
return PL_DHASH_STOP;
}
} // anon
nsresult
DOMStorageCache::GetKey(const DOMStorage* aStorage, uint32_t aIndex, nsAString& aRetval)
{
// XXX: This does a linear search for the key at index, which would
// suck if there's a large numer of indexes. Do we care? If so,
// maybe we need to have a lazily populated key array here or
// something?
Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETKEY_MS> autoTimer;
if (Persist(aStorage)) {
WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETKEY_BLOCKING_MS);
if (NS_FAILED(mLoadResult)) {
return mLoadResult;
}
}
IndexFinderData data(aIndex, aRetval);
DataSet(aStorage).mKeys.EnumerateRead(FindKeyOrder, &data);
return NS_OK;
}
namespace { // anon
static PLDHashOperator
KeysArrayBuilder(const nsAString& aKey, const nsString aValue, void* aArg)
{
nsTArray<nsString>* keys = static_cast<nsTArray<nsString>* >(aArg);
keys->AppendElement(aKey);
return PL_DHASH_NEXT;
}
} // anon
nsTArray<nsString>*
DOMStorageCache::GetKeys(const DOMStorage* aStorage)
{
Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETALLKEYS_MS> autoTimer;
if (Persist(aStorage)) {
WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETALLKEYS_BLOCKING_MS);
}
nsTArray<nsString>* result = new nsTArray<nsString>();
if (NS_SUCCEEDED(mLoadResult)) {
DataSet(aStorage).mKeys.EnumerateRead(KeysArrayBuilder, result);
}
return result;
}
nsresult
DOMStorageCache::GetItem(const DOMStorage* aStorage, const nsAString& aKey,
nsAString& aRetval)
{
Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETVALUE_MS> autoTimer;
if (Persist(aStorage)) {
WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETVALUE_BLOCKING_MS);
if (NS_FAILED(mLoadResult)) {
return mLoadResult;
}
}
// not using AutoString since we don't want to copy buffer to result
nsString value;
if (!DataSet(aStorage).mKeys.Get(aKey, &value)) {
SetDOMStringToNull(value);
}
aRetval = value;
return NS_OK;
}
nsresult
DOMStorageCache::SetItem(const DOMStorage* aStorage, const nsAString& aKey,
const nsString& aValue, nsString& aOld)
{
Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_SETVALUE_MS> autoTimer;
if (Persist(aStorage)) {
WaitForPreload(Telemetry::LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS);
if (NS_FAILED(mLoadResult)) {
return mLoadResult;
}
}
Data& data = DataSet(aStorage);
if (!data.mKeys.Get(aKey, &aOld)) {
SetDOMStringToNull(aOld);
}
// Check the quota first
const int64_t delta = static_cast<int64_t>(aValue.Length()) -
static_cast<int64_t>(aOld.Length());
if (!ProcessUsageDelta(aStorage, delta)) {
return NS_ERROR_DOM_QUOTA_REACHED;
}
if (aValue == aOld && DOMStringIsNull(aValue) == DOMStringIsNull(aOld)) {
return NS_SUCCESS_DOM_NO_OPERATION;
}
data.mKeys.Put(aKey, aValue);
if (Persist(aStorage)) {
if (DOMStringIsNull(aOld)) {
return sDatabase->AsyncAddItem(this, aKey, aValue);
}
return sDatabase->AsyncUpdateItem(this, aKey, aValue);
}
return NS_OK;
}
nsresult
DOMStorageCache::RemoveItem(const DOMStorage* aStorage, const nsAString& aKey,
nsString& aOld)
{
Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_REMOVEKEY_MS> autoTimer;
if (Persist(aStorage)) {
WaitForPreload(Telemetry::LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS);
if (NS_FAILED(mLoadResult)) {
return mLoadResult;
}
}
Data& data = DataSet(aStorage);
if (!data.mKeys.Get(aKey, &aOld)) {
SetDOMStringToNull(aOld);
return NS_SUCCESS_DOM_NO_OPERATION;
}
// Recalculate the cached data size
const int64_t delta = -(static_cast<int64_t>(aOld.Length()));
unused << ProcessUsageDelta(aStorage, delta);
data.mKeys.Remove(aKey);
if (Persist(aStorage)) {
return sDatabase->AsyncRemoveItem(this, aKey);
}
return NS_OK;
}
nsresult
DOMStorageCache::Clear(const DOMStorage* aStorage)
{
Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_CLEAR_MS> autoTimer;
bool refresh = false;
if (Persist(aStorage)) {
// We need to preload all data (know the size) before we can proceeed
// to correctly decrease cached usage number.
// XXX as in case of unload, this is not technically needed now, but
// after super-scope quota introduction we have to do this. Get telemetry
// right now.
WaitForPreload(Telemetry::LOCALDOMSTORAGE_CLEAR_BLOCKING_MS);
if (NS_FAILED(mLoadResult)) {
// When we failed to load data from the database, force delete of the
// scope data and make use of the storage possible again.
refresh = true;
mLoadResult = NS_OK;
}
}
Data& data = DataSet(aStorage);
bool hadData = !!data.mKeys.Count();
if (hadData) {
unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage);
data.mKeys.Clear();
}
if (Persist(aStorage) && (refresh || hadData)) {
return sDatabase->AsyncClear(this);
}
return hadData ? NS_OK : NS_SUCCESS_DOM_NO_OPERATION;
}
void
DOMStorageCache::CloneFrom(const DOMStorageCache* aThat)
{
mLoaded = aThat->mLoaded;
mInitialized = aThat->mInitialized;
mPersistent = aThat->mPersistent;
mSessionOnlyDataSetActive = aThat->mSessionOnlyDataSetActive;
for (uint32_t i = 0; i < kDataSetCount; ++i) {
aThat->mData[i].mKeys.EnumerateRead(CloneSetData, &mData[i]);
ProcessUsageDelta(i, aThat->mData[i].mOriginQuotaUsage);
}
}
// Defined in DOMStorageManager.cpp
extern bool
PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal);
bool
DOMStorageCache::CheckPrincipal(nsIPrincipal* aPrincipal) const
{
return PrincipalsEqual(mPrincipal, aPrincipal);
}
void
DOMStorageCache::UnloadItems(uint32_t aUnloadFlags)
{
if (aUnloadFlags & kUnloadDefault) {
// Must wait for preload to pass correct usage to ProcessUsageDelta
// XXX this is not technically needed right now since there is just
// per-origin isolated quota handling, but when we introduce super-
// -scope quotas, we have to do this. Better to start getting
// telemetry right now.
WaitForPreload(Telemetry::LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS);
mData[kDefaultSet].mKeys.Clear();
ProcessUsageDelta(kDefaultSet, -mData[kDefaultSet].mOriginQuotaUsage);
}
if (aUnloadFlags & kUnloadPrivate) {
mData[kPrivateSet].mKeys.Clear();
ProcessUsageDelta(kPrivateSet, -mData[kPrivateSet].mOriginQuotaUsage);
}
if (aUnloadFlags & kUnloadSession) {
mData[kSessionSet].mKeys.Clear();
ProcessUsageDelta(kSessionSet, -mData[kSessionSet].mOriginQuotaUsage);
mSessionOnlyDataSetActive = false;
}
#ifdef DOM_STORAGE_TESTS
if (aUnloadFlags & kTestReload) {
WaitForPreload(Telemetry::LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS);
mData[kDefaultSet].mKeys.Clear();
mLoaded = false; // This is only used in testing code
Preload();
}
#endif
}
// DOMStorageCacheBridge
uint32_t
DOMStorageCache::LoadedCount()
{
MonitorAutoLock monitor(mMonitor);
Data& data = mData[kDefaultSet];
return data.mKeys.Count();
}
bool
DOMStorageCache::LoadItem(const nsAString& aKey, const nsString& aValue)
{
MonitorAutoLock monitor(mMonitor);
if (mLoaded) {
return false;
}
Data& data = mData[kDefaultSet];
if (data.mKeys.Get(aKey, nullptr)) {
return true; // don't stop, just don't override
}
data.mKeys.Put(aKey, aValue);
data.mOriginQuotaUsage += aKey.Length() + aValue.Length();
return true;
}
void
DOMStorageCache::LoadDone(nsresult aRv)
{
// Keep the preloaded cache alive for a time
KeepAlive();
MonitorAutoLock monitor(mMonitor);
mLoadResult = aRv;
mLoaded = true;
monitor.Notify();
}
void
DOMStorageCache::LoadWait()
{
MonitorAutoLock monitor(mMonitor);
while (!mLoaded) {
monitor.Wait();
}
}
// DOMStorageUsage
DOMStorageUsage::DOMStorageUsage(const nsACString& aScope)
: mScope(aScope)
{
mUsage[kDefaultSet] = mUsage[kPrivateSet] = mUsage[kSessionSet] = 0LL;
}
namespace { // anon
class LoadUsageRunnable : public nsRunnable
{
public:
LoadUsageRunnable(int64_t* aUsage, const int64_t aDelta)
: mTarget(aUsage)
, mDelta(aDelta)
{}
private:
int64_t* mTarget;
int64_t mDelta;
NS_IMETHOD Run() { *mTarget = mDelta; return NS_OK; }
};
} // anon
void
DOMStorageUsage::LoadUsage(const int64_t aUsage)
{
// Using kDefaultSet index since it is the index for the persitent data
// stored in the database we have just loaded usage for.
if (!NS_IsMainThread()) {
// In single process scenario we get this call from the DB thread
nsRefPtr<LoadUsageRunnable> r =
new LoadUsageRunnable(mUsage + kDefaultSet, aUsage);
NS_DispatchToMainThread(r);
} else {
// On a child process we get this on the main thread already
mUsage[kDefaultSet] += aUsage;
}
}
bool
DOMStorageUsage::CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, const int64_t aDelta)
{
MOZ_ASSERT(NS_IsMainThread());
int64_t newUsage = mUsage[aDataSetIndex] + aDelta;
if (aDelta > 0 && newUsage > DOMStorageManager::GetQuota()) {
return false;
}
mUsage[aDataSetIndex] = newUsage;
return true;
}
// static
DOMStorageDBBridge*
DOMStorageCache::StartDatabase()
{
if (sDatabase) {
return sDatabase;
}
if (XRE_GetProcessType() == GeckoProcessType_Default) {
nsAutoPtr<DOMStorageDBThread> db(new DOMStorageDBThread());
nsresult rv = db->Init();
if (NS_FAILED(rv)) {
return nullptr;
}
sDatabase = db.forget();
} else {
nsRefPtr<DOMStorageDBChild> db = new DOMStorageDBChild(
DOMLocalStorageManager::Self());
nsresult rv = db->Init();
if (NS_FAILED(rv)) {
return nullptr;
}
db.forget(&sDatabase);
}
return sDatabase;
}
// static
DOMStorageDBBridge*
DOMStorageCache::GetDatabase()
{
return sDatabase;
}
// static
nsresult
DOMStorageCache::StopDatabase()
{
if (!sDatabase) {
return NS_OK;
}
nsresult rv = sDatabase->Shutdown();
if (XRE_GetProcessType() == GeckoProcessType_Default) {
delete sDatabase;
} else {
DOMStorageDBChild* child = static_cast<DOMStorageDBChild*>(sDatabase);
NS_RELEASE(child);
}
sDatabase = nullptr;
return rv;
}
} // ::dom
} // ::mozilla

Просмотреть файл

@ -0,0 +1,253 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsDOMStorageCache_h___
#define nsDOMStorageCache_h___
#include "nsIPrincipal.h"
#include "nsITimer.h"
#include "nsString.h"
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "mozilla/Monitor.h"
#include "mozilla/Telemetry.h"
#include "nsAutoPtr.h"
namespace mozilla {
namespace dom {
class DOMStorage;
class DOMStorageManager;
class DOMStorageDBBridge;
// Interface class on which only the database or IPC may call.
// Used to populate the cache with DB data.
class DOMStorageCacheBridge
{
public:
NS_IMETHOD_(nsrefcnt) AddRef(void);
NS_IMETHOD_(void) Release(void);
virtual ~DOMStorageCacheBridge() {}
// The scope (origin) in the database usage format (reversed)
virtual const nsCString& Scope() const = 0;
// Whether the cache is already fully loaded
virtual bool Loaded() = 0;
// How many items has so far been loaded into the cache, used
// for optimization purposes
virtual uint32_t LoadedCount() = 0;
// Called by the database to load a key and its value to the cache
virtual bool LoadItem(const nsAString& aKey, const nsString& aValue) = 0;
// Called by the database after all keys and values has been loaded
// to this cache
virtual void LoadDone(nsresult aRv) = 0;
// Use to synchronously wait until the cache gets fully loaded with data,
// this method exits after LoadDone has been called
virtual void LoadWait() = 0;
protected:
nsAutoRefCnt mRefCnt;
};
// Implementation of scope cache that is responsible for preloading data
// for persistent storage (localStorage) and hold data for non-private,
// private and session-only cookie modes. It is also responsible for
// persisting data changes using the database, works as a write-back cache.
class DOMStorageCache : public DOMStorageCacheBridge
{
public:
NS_IMETHOD_(void) Release(void);
DOMStorageCache(const nsACString* aScope);
virtual ~DOMStorageCache();
void Init(DOMStorageManager* aManager, bool aPersistent, nsIPrincipal* aPrincipal,
const nsACString& aQuotaScope);
// Copies all data from the other storage.
void CloneFrom(const DOMStorageCache* aThat);
// Starts async preload of this cache if it persistent and not loaded.
void Preload();
// Keeps the cache alive (i.e. present in the manager's hash table) for a time.
void KeepAlive();
// The set of methods that are invoked by DOM storage web API.
// We are passing the DOMStorage object just to let the cache
// read properties like mPrivate and mSessionOnly.
// Get* methods return error when load from the database has failed.
nsresult GetLength(const DOMStorage* aStorage, uint32_t* aRetval);
nsresult GetKey(const DOMStorage* aStorage, uint32_t index, nsAString& aRetval);
nsresult GetItem(const DOMStorage* aStorage, const nsAString& aKey, nsAString& aRetval);
nsresult SetItem(const DOMStorage* aStorage, const nsAString& aKey, const nsString& aValue, nsString& aOld);
nsresult RemoveItem(const DOMStorage* aStorage, const nsAString& aKey, nsString& aOld);
nsresult Clear(const DOMStorage* aStorage);
nsTArray<nsString>* GetKeys(const DOMStorage* aStorage);
// Whether the principal equals principal the cache was created for
bool CheckPrincipal(nsIPrincipal* aPrincipal) const;
nsIPrincipal* Principal() const { return mPrincipal; }
// Starts the database engine thread or the IPC bridge
static DOMStorageDBBridge* StartDatabase();
static DOMStorageDBBridge* GetDatabase();
// Stops the thread and flushes all uncommited data
static nsresult StopDatabase();
// DOMStorageCacheBridge
virtual const nsCString& Scope() const { return mScope; }
virtual bool Loaded() { return mLoaded; }
virtual uint32_t LoadedCount();
virtual bool LoadItem(const nsAString& aKey, const nsString& aValue);
virtual void LoadDone(nsresult aRv);
virtual void LoadWait();
// Cache keeps 3 sets of data: regular, private and session-only.
// This class keeps keys and values for a set and also caches
// size of the data for quick per-origin quota checking.
class Data
{
public:
Data() : mOriginQuotaUsage(0) { mKeys.Init(); }
int64_t mOriginQuotaUsage;
nsDataHashtable<nsStringHashKey, nsString> mKeys;
};
public:
// Number of data sets we keep: default, private, session
static const uint32_t kDataSetCount = 3;
private:
// API to clear the cache data, this is invoked by chrome operations
// like cookie deletion.
friend class DOMStorageManager;
static const uint32_t kUnloadDefault = 1 << 0;
static const uint32_t kUnloadPrivate = 1 << 1;
static const uint32_t kUnloadSession = 1 << 2;
static const uint32_t kUnloadComplete =
kUnloadDefault | kUnloadPrivate | kUnloadSession;
#ifdef DOM_STORAGE_TESTS
static const uint32_t kTestReload = 1 << 15;
#endif
void UnloadItems(uint32_t aUnloadFlags);
private:
// Synchronously blocks until the cache is fully loaded from the database
void WaitForPreload(mozilla::Telemetry::ID aTelemetryID);
// Helper to get one of the 3 data sets (regular, private, session)
Data& DataSet(const DOMStorage* aStorage);
// Whether the storage change is about to persist
bool Persist(const DOMStorage* aStorage) const;
// Changes the quota usage on the given data set if it fits the quota.
// If not, then false is returned and no change to the set must be done.
bool ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta);
bool ProcessUsageDelta(const DOMStorage* aStorage, const int64_t aDelta);
private:
// When a cache is reponsible for its life time (in case of localStorage data
// cache) we need to refer our manager since removal of the cache from the hash
// table is handled in the destructor by call to the manager.
// Cache could potentially overlive the manager, hence the hard ref.
nsRefPtr<DOMStorageManager> mManager;
// Timer that holds this cache alive for a while after it has been preloaded.
nsCOMPtr<nsITimer> mKeepAliveTimer;
// Principal the cache has been initially created for, this is used only
// for sessionStorage access checks since sessionStorage objects are strictly
// scoped by a principal. localStorage objects on the other hand are scoped by
// origin only.
nsCOMPtr<nsIPrincipal> mPrincipal;
// The scope this cache belongs to in the "DB format", i.e. reversed
nsCString mScope;
// The eTLD+1 scope used to count quota usage.
nsCString mQuotaScope;
// Non-private Browsing, Private Browsing and Session Only sets.
Data mData[kDataSetCount];
// This monitor is used to wait for full load of data.
mozilla::Monitor mMonitor;
// Flag that is initially false. When the cache is about to work with
// the database (i.e. it is persistent) this flags is set to true after
// all keys and coresponding values are loaded from the database.
// This flag never goes from true back to false.
bool mLoaded;
// Result of load from the database. Valid after mLoaded flag has been set.
nsresult mLoadResult;
// Init() method has been called
bool mInitialized : 1;
// This cache is about to be bound with the database (i.e. it has
// to load from the DB first and has to persist when modifying the
// default data set.)
bool mPersistent : 1;
// - False when the session-only data set was never used.
// - True after access to session-only data has been made for the first time.
// We also fill session-only data set with the default one at that moment.
// Drops back to false when session-only data are cleared from chrome.
bool mSessionOnlyDataSetActive : 1;
// Whether we have already captured state of the cache preload on our first access.
bool mPreloadTelemetryRecorded : 1;
// DOMStorageDBThread on the parent or single process,
// DOMStorageDBChild on the child process.
static DOMStorageDBBridge* sDatabase;
};
// DOMStorageUsage
// Infrastructure to manage and check eTLD+1 quota
class DOMStorageUsageBridge
{
public:
virtual ~DOMStorageUsageBridge() {}
virtual const nsCString& Scope() = 0;
virtual void LoadUsage(const int64_t aUsage) = 0;
};
class DOMStorageUsage : public DOMStorageUsageBridge
{
public:
DOMStorageUsage(const nsACString& aScope);
bool CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, int64_t aUsageDelta);
private:
virtual const nsCString& Scope() { return mScope; }
virtual void LoadUsage(const int64_t aUsage);
nsCString mScope;
int64_t mUsage[DOMStorageCache::kDataSetCount];
};
} // ::dom
} // ::mozilla
#endif

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,348 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef DOMStorageDBThread_h___
#define DOMStorageDBThread_h___
#include "prthread.h"
#include "prinrval.h"
#include "nsTArray.h"
#include "mozilla/Monitor.h"
#include "mozilla/storage/StatementCache.h"
#include "nsString.h"
#include "nsCOMPtr.h"
#include "nsClassHashtable.h"
#include "nsIFile.h"
class mozIStorageConnection;
namespace mozilla {
namespace dom {
class DOMStorageCacheBridge;
class DOMStorageUsageBridge;
class DOMStorageUsage;
typedef mozilla::storage::StatementCache<mozIStorageStatement> StatementCache;
// Interface used by the cache to post operations to the asynchronous
// database thread or process.
class DOMStorageDBBridge
{
public:
DOMStorageDBBridge();
virtual ~DOMStorageDBBridge() {}
// Ensures the database engine is started
virtual nsresult Init() = 0;
// Releases the database and disallows its usage
virtual nsresult Shutdown() = 0;
// Asynchronously fills the cache with data from the database for first use
// When |aPriority| is true, the preload operation is scheduled as the first one
virtual void AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority = false) = 0;
// Asynchronously fill the |usage| object with actual usage of data by its scope.
// The scope is eTLD+1 tops, never deeper subdomains.
virtual void AsyncGetUsage(DOMStorageUsageBridge* aUsage) = 0;
// Synchronously fills the cache, when |aForceSync| is false and cache already got some
// data before, the method waits for the running preload to finish
virtual void SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync = false) = 0;
// Called when an existing key is modified in the storage, schedules update to the database
virtual nsresult AsyncAddItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) = 0;
// Called when an existing key is modified in the storage, schedules update to the database
virtual nsresult AsyncUpdateItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) = 0;
// Called when an item is removed from the storage, schedules delete of the key
virtual nsresult AsyncRemoveItem(DOMStorageCacheBridge* aCache, const nsAString& aKey) = 0;
// Called when the whole storage is cleared by the DOM API, schedules delete of the scope
virtual nsresult AsyncClear(DOMStorageCacheBridge* aCache) = 0;
// Called when chrome deletes e.g. cookies, schedules delete of the whole database
virtual void AsyncClearAll() = 0;
// Called when only a domain and its subdomains or an app data is about to clear
virtual void AsyncClearMatchingScope(const nsACString& aScope) = 0;
// Forces scheduled DB operations to be early flushed to the disk
virtual void AsyncFlush() = 0;
// Check whether the scope has any data stored on disk and is thus allowed to preload
virtual bool ShouldPreloadScope(const nsACString& aScope) = 0;
// Get the complete list of scopes having data
virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes) = 0;
// Returns object keeping usage cache for the scope.
DOMStorageUsage* GetScopeUsage(const nsACString& aScope);
protected:
// Keeps usage cache objects for eTLD+1 scopes we have touched.
nsClassHashtable<nsCStringHashKey, DOMStorageUsage> mUsages;
};
// The implementation of the the database engine, this directly works
// with the sqlite or any other db API we are based on
// This class is resposible for collecting and processing asynchronous
// DB operations over caches (DOMStorageCache) communicating though
// DOMStorageCacheBridge interface class
class DOMStorageDBThread MOZ_FINAL : public DOMStorageDBBridge
{
public:
class PendingOperations;
// Representation of a singe database task, like adding and removing keys,
// (pre)loading the whole origin data, cleaning.
class DBOperation
{
public:
typedef enum {
// Only operation that reads data from the database
opPreload,
// The same as opPreload, just executed with highest priority
opPreloadUrgent,
// Load usage of a scope
opGetUsage,
// Operations invoked by the DOM content API
opAddItem,
opUpdateItem,
opRemoveItem,
opClear,
// Operations invoked by chrome
opClearAll,
opClearMatchingScope,
} OperationType;
DBOperation(const OperationType aType,
DOMStorageCacheBridge* aCache = nullptr,
const nsAString& aKey = EmptyString(),
const nsAString& aValue = EmptyString());
DBOperation(const OperationType aType,
DOMStorageUsageBridge* aUsage);
DBOperation(const OperationType aType,
const nsACString& aScope);
~DBOperation();
// Executes the operation, doesn't necessarity have to be called on the I/O thread
void PerformAndFinalize(DOMStorageDBThread* aThread);
// Finalize the operation, i.e. do any internal cleanup and finish calls
void Finalize(nsresult aRv);
// The operation type
OperationType Type() { return mType; }
// The operation scope (=origin)
const nsCString Scope();
// |Scope + key| the operation is working with
const nsCString Target();
private:
// The operation implementation body
nsresult Perform(DOMStorageDBThread* aThread);
friend class PendingOperations;
OperationType mType;
nsRefPtr<DOMStorageCacheBridge> mCache;
DOMStorageUsageBridge* mUsage;
nsString mKey;
nsString mValue;
nsCString mScope;
};
// Encapsulation of collective and coalescing logic for all pending operations
// except preloads that are handled separately as priority operations
class PendingOperations {
public:
PendingOperations();
// Method responsible for coalescing redundant update operations with the same
// |Target()| or clear operations with the same or matching |Scope()|
void Add(DBOperation* aOperation);
// True when there are some scheduled operations to flush on disk
bool HasTasks();
// Moves collected operations to a local flat list to allow execution of the operation
// list out of the thread lock
bool Prepare();
// Executes the previously |Prepared()'ed| list of operations, retuns result, but doesn't
// handle it in any way in case of a failure
nsresult Execute(DOMStorageDBThread* aThread);
// Finalizes the pending operation list, returns false when too many operations failed
// to flush what indicates a long standing issue with the database access.
bool Finalize(nsresult aRv);
// true when a clear that deletes the given |scope| is among the pending operations;
// when a preload for that scope is being scheduled, it must be finished right away
bool IsScopeClearPending(const nsACString& aScope);
// Checks whether there is a pending update (or clear, actually) operation for this scope.
bool IsScopeUpdatePending(const nsACString& aScope);
private:
// Returns true iff new operation is of type newType and there is a pending
// operation of type pendingType for the same key (target).
bool CheckForCoalesceOpportunity(DBOperation* aNewOp,
DBOperation::OperationType aPendingType,
DBOperation::OperationType aNewType);
// List of all clearing operations, executed first
nsClassHashtable<nsCStringHashKey, DBOperation> mClears;
// List of all update/insert operations, executed as second
nsClassHashtable<nsCStringHashKey, DBOperation> mUpdates;
// Collection of all tasks, valid only between Prepare() and Execute()
nsTArray<nsAutoPtr<DBOperation> > mExecList;
// Number of failing flush attempts
uint32_t mFlushFailureCount;
};
public:
DOMStorageDBThread();
virtual ~DOMStorageDBThread() {}
virtual nsresult Init();
virtual nsresult Shutdown();
virtual void AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority = false)
{ InsertDBOp(new DBOperation(aPriority ? DBOperation::opPreloadUrgent : DBOperation::opPreload, aCache)); }
virtual void SyncPreload(DOMStorageCacheBridge* aCache, bool aForce = false);
virtual void AsyncGetUsage(DOMStorageUsageBridge * aUsage)
{ InsertDBOp(new DBOperation(DBOperation::opGetUsage, aUsage)); }
virtual nsresult AsyncAddItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue)
{ return InsertDBOp(new DBOperation(DBOperation::opAddItem, aCache, aKey, aValue)); }
virtual nsresult AsyncUpdateItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue)
{ return InsertDBOp(new DBOperation(DBOperation::opUpdateItem, aCache, aKey, aValue)); }
virtual nsresult AsyncRemoveItem(DOMStorageCacheBridge* aCache, const nsAString& aKey)
{ return InsertDBOp(new DBOperation(DBOperation::opRemoveItem, aCache, aKey)); }
virtual nsresult AsyncClear(DOMStorageCacheBridge* aCache)
{ return InsertDBOp(new DBOperation(DBOperation::opClear, aCache)); }
virtual void AsyncClearAll()
{ InsertDBOp(new DBOperation(DBOperation::opClearAll)); }
virtual void AsyncClearMatchingScope(const nsACString& aScope)
{ InsertDBOp(new DBOperation(DBOperation::opClearMatchingScope, aScope)); }
virtual void AsyncFlush();
virtual bool ShouldPreloadScope(const nsACString& aScope);
virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes);
private:
nsCOMPtr<nsIFile> mDatabaseFile;
PRThread* mThread;
// The monitor we drive the thread with
Monitor mMonitor;
// Flag to stop, protected by the monitor
bool mStopIOThread;
// Whether WAL is enabled
bool mWALModeEnabled;
// Whether DB has already been open, avoid races between main thread reads
// and pending DB init in the background I/O thread
bool mDBReady;
// State of the database initiation
nsresult mStatus;
// List of scopes having data, for optimization purposes only
nsTHashtable<nsCStringHashKey> mScopesHavingData;
StatementCache mWorkerStatements;
StatementCache mReaderStatements;
// Connection used by the worker thread for all read and write ops
nsCOMPtr<mozIStorageConnection> mWorkerConnection;
// Connection used only on the main thread for sync read operations
nsCOMPtr<mozIStorageConnection> mReaderConnection;
// Time the first pending operation has been added to the pending operations
// list
PRIntervalTime mDirtyEpoch;
// Flag to force immediate flush of all pending operations
bool mFlushImmediately;
// List of preloading operations, in chronological or priority order.
// Executed prioritly over pending update operations.
nsTArray<DBOperation*> mPreloads;
// Collector of pending update operations
PendingOperations mPendingTasks;
// Counter of calls for thread priority rising.
int32_t mPriorityCounter;
// Helper to direct an operation to one of the arrays above;
// also checks IsScopeClearPending for preloads
nsresult InsertDBOp(DBOperation* aOperation);
// Opens the database, first thing we do after start of the thread.
nsresult OpenDatabaseConnection();
nsresult InitDatabase();
nsresult ShutdownDatabase();
// Tries to establish WAL mode
nsresult SetJournalMode(bool aIsWal);
nsresult TryJournalMode();
void SetHigherPriority();
void SetDefaultPriority();
// Ensures we flush pending tasks in some reasonble time
void ScheduleFlush();
// Called when flush of pending tasks is being executed
void UnscheduleFlush();
// This method is used for two purposes:
// 1. as a value passed to monitor.Wait() method
// 2. as in indicator that flush has to be performed
//
// Return:
// - PR_INTERVAL_NO_TIMEOUT when no pending tasks are scheduled
// - larger then zero when tasks have been scheduled, but it is
// still not time to perform the flush ; it is actual interval
// time to wait until the flush has to happen
// - 0 when it is time to do the flush
PRIntervalTime TimeUntilFlush();
// Notifies to the main thread that flush has completed
void NotifyFlushCompletion();
// Thread loop
static void ThreadFunc(void* aArg);
void ThreadFunc();
};
} // ::dom
} // ::mozilla
#endif /* DOMStorageDBThread_h___ */

Просмотреть файл

@ -0,0 +1,670 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DOMStorageIPC.h"
#include "DOMStorageManager.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/unused.h"
namespace mozilla {
namespace dom {
// ----------------------------------------------------------------------------
// Child
// ----------------------------------------------------------------------------
NS_IMPL_THREADSAFE_ADDREF(DOMStorageDBChild)
NS_IMETHODIMP_(nsrefcnt) DOMStorageDBChild::Release(void)
{
NS_PRECONDITION(0 != mRefCnt, "dup release");
nsrefcnt count = --mRefCnt;
NS_LOG_RELEASE(this, count, "DOMStorageDBChild");
if (count == 1 && mIPCOpen) {
Send__delete__(this);
return 0;
}
if (count == 0) {
mRefCnt = 1;
delete this;
return 0;
}
return count;
}
void
DOMStorageDBChild::AddIPDLReference()
{
NS_ABORT_IF_FALSE(!mIPCOpen, "Attempting to retain multiple IPDL references");
mIPCOpen = true;
AddRef();
}
void
DOMStorageDBChild::ReleaseIPDLReference()
{
NS_ABORT_IF_FALSE(mIPCOpen, "Attempting to release non-existent IPDL reference");
mIPCOpen = false;
Release();
}
DOMStorageDBChild::DOMStorageDBChild(DOMLocalStorageManager* aManager)
: mManager(aManager)
, mStatus(NS_OK)
, mIPCOpen(false)
{
}
DOMStorageDBChild::~DOMStorageDBChild()
{
}
nsTHashtable<nsCStringHashKey>&
DOMStorageDBChild::ScopesHavingData()
{
if (!mScopesHavingData.IsInitialized()) {
mScopesHavingData.Init();
}
return mScopesHavingData;
}
nsresult
DOMStorageDBChild::Init()
{
ContentChild* child = ContentChild::GetSingleton();
AddIPDLReference();
child->SendPStorageConstructor(this);
return NS_OK;
}
nsresult
DOMStorageDBChild::Shutdown()
{
// There is nothing to do here, IPC will release automatically and
// the actual thread running on the parent process will also stop
// automatically in profile-before-change topic observer.
return NS_OK;
}
void
DOMStorageDBChild::SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync)
{
if (NS_FAILED(mStatus)) {
aCache->LoadDone(mStatus);
return;
}
// There is no way to put the child process to a wait state to receive all
// incoming async responses from the parent, hence we have to do a sync preload
// instead. We are smart though, we only demand keys that are left to load in
// case the async preload has already loaded some keys.
InfallibleTArray<nsString> keys, values;
nsresult rv;
SendPreload(aCache->Scope(), aCache->LoadedCount(), &keys, &values, &rv);
for (uint32_t i = 0; i < keys.Length(); ++i) {
aCache->LoadItem(keys[i], values[i]);
}
aCache->LoadDone(rv);
}
nsresult
DOMStorageDBChild::AsyncAddItem(DOMStorageCacheBridge* aCache,
const nsAString& aKey,
const nsAString& aValue)
{
if (NS_FAILED(mStatus)) {
return mStatus;
}
SendAsyncAddItem(aCache->Scope(), nsString(aKey), nsString(aValue));
ScopesHavingData().PutEntry(aCache->Scope());
return NS_OK;
}
nsresult
DOMStorageDBChild::AsyncUpdateItem(DOMStorageCacheBridge* aCache,
const nsAString& aKey,
const nsAString& aValue)
{
if (NS_FAILED(mStatus)) {
return mStatus;
}
SendAsyncUpdateItem(aCache->Scope(), nsString(aKey), nsString(aValue));
ScopesHavingData().PutEntry(aCache->Scope());
return NS_OK;
}
nsresult
DOMStorageDBChild::AsyncRemoveItem(DOMStorageCacheBridge* aCache,
const nsAString& aKey)
{
if (NS_FAILED(mStatus)) {
return mStatus;
}
SendAsyncRemoveItem(aCache->Scope(), nsString(aKey));
return NS_OK;
}
nsresult
DOMStorageDBChild::AsyncClear(DOMStorageCacheBridge* aCache)
{
if (NS_FAILED(mStatus)) {
return mStatus;
}
SendAsyncClear(aCache->Scope());
ScopesHavingData().RemoveEntry(aCache->Scope());
return NS_OK;
}
bool
DOMStorageDBChild::ShouldPreloadScope(const nsACString& aScope)
{
// Return true if we didn't receive the aScope list yet.
// I tend to rather preserve a bit of early-after-start performance
// then a bit of memory here.
return !mScopesHavingData.IsInitialized() ||
mScopesHavingData.Contains(aScope);
}
bool
DOMStorageDBChild::RecvObserve(const nsCString& aTopic,
const nsCString& aScopePrefix)
{
DOMStorageObserver::Self()->Notify(aTopic.get(), aScopePrefix);
return true;
}
bool
DOMStorageDBChild::RecvScopesHavingData(const InfallibleTArray<nsCString>& aScopes)
{
for (uint32_t i = 0; i < aScopes.Length(); ++i) {
ScopesHavingData().PutEntry(aScopes[i]);
}
return true;
}
bool
DOMStorageDBChild::RecvLoadItem(const nsCString& aScope,
const nsString& aKey,
const nsString& aValue)
{
DOMStorageCache* aCache = mManager->GetCache(aScope);
if (aCache) {
aCache->LoadItem(aKey, aValue);
}
return true;
}
bool
DOMStorageDBChild::RecvLoadDone(const nsCString& aScope, const nsresult& aRv)
{
DOMStorageCache* aCache = mManager->GetCache(aScope);
if (aCache) {
aCache->LoadDone(aRv);
}
return true;
}
bool
DOMStorageDBChild::RecvLoadUsage(const nsCString& aScope, const int64_t& aUsage)
{
DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
if (!db) {
return false;
}
DOMStorageUsageBridge* scopeUsage = db->GetScopeUsage(aScope);
scopeUsage->LoadUsage(aUsage);
return true;
}
bool
DOMStorageDBChild::RecvError(const nsresult& aRv)
{
mStatus = aRv;
return true;
}
// ----------------------------------------------------------------------------
// Parent
// ----------------------------------------------------------------------------
NS_IMPL_THREADSAFE_ADDREF(DOMStorageDBParent)
NS_IMPL_THREADSAFE_RELEASE(DOMStorageDBParent)
void
DOMStorageDBParent::AddIPDLReference()
{
NS_ABORT_IF_FALSE(!mIPCOpen, "Attempting to retain multiple IPDL references");
mIPCOpen = true;
AddRef();
}
void
DOMStorageDBParent::ReleaseIPDLReference()
{
NS_ABORT_IF_FALSE(mIPCOpen, "Attempting to release non-existent IPDL reference");
mIPCOpen = false;
Release();
}
namespace { // anon
class SendScopesHavingDataRunnable : public nsRunnable
{
public:
SendScopesHavingDataRunnable(DOMStorageDBParent* aParent)
: mParent(aParent)
{}
private:
NS_IMETHOD Run()
{
DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
if (db) {
InfallibleTArray<nsCString> scopes;
db->GetScopesHavingData(&scopes);
mozilla::unused << mParent->SendScopesHavingData(scopes);
}
return NS_OK;
}
nsRefPtr<DOMStorageDBParent> mParent;
};
} // anon
DOMStorageDBParent::DOMStorageDBParent()
: mIPCOpen(false)
{
DOMStorageObserver* observer = DOMStorageObserver::Self();
if (observer) {
observer->AddSink(this);
}
// We are always open by IPC only
AddIPDLReference();
// Cannot send directly from here since the channel
// is not completely built at this moment.
nsRefPtr<SendScopesHavingDataRunnable> r =
new SendScopesHavingDataRunnable(this);
NS_DispatchToCurrentThread(r);
}
DOMStorageDBParent::~DOMStorageDBParent()
{
DOMStorageObserver* observer = DOMStorageObserver::Self();
if (observer) {
observer->RemoveSink(this);
}
}
DOMStorageDBParent::CacheParentBridge*
DOMStorageDBParent::NewCache(const nsACString& aScope)
{
return new CacheParentBridge(this, aScope);
}
bool
DOMStorageDBParent::RecvAsyncPreload(const nsCString& aScope, const bool& aPriority)
{
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
if (!db) {
return false;
}
db->AsyncPreload(NewCache(aScope), aPriority);
return true;
}
bool
DOMStorageDBParent::RecvAsyncGetUsage(const nsCString& aScope)
{
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
if (!db) {
return false;
}
// The object releases it self in LoadUsage method
UsageParentBridge* usage = new UsageParentBridge(this, aScope);
db->AsyncGetUsage(usage);
return true;
}
namespace { // anon
// We need another implementation of DOMStorageCacheBridge to do
// synchronous IPC preload. This class just receives Load* notifications
// and fills the returning arguments of RecvPreload with the database
// values for us.
class SyncLoadCacheHelper : public DOMStorageCacheBridge
{
public:
SyncLoadCacheHelper(const nsCString& aScope,
uint32_t aAlreadyLoadedCount,
InfallibleTArray<nsString>* aKeys,
InfallibleTArray<nsString>* aValues,
nsresult* rv)
: mMonitor("DOM Storage SyncLoad IPC")
, mScope(aScope)
, mKeys(aKeys)
, mValues(aValues)
, mRv(rv)
, mLoaded(false)
, mLoadedCount(aAlreadyLoadedCount)
{
// Precaution
*mRv = NS_ERROR_UNEXPECTED;
}
virtual const nsCString& Scope() const { return mScope; }
virtual bool Loaded() { return mLoaded; }
virtual uint32_t LoadedCount() { return mLoadedCount; }
virtual bool LoadItem(const nsAString& aKey, const nsString& aValue)
{
// Called on the aCache background thread
if (mLoaded) {
return false;
}
++mLoadedCount;
mKeys->AppendElement(aKey);
mValues->AppendElement(aValue);
return true;
}
virtual void LoadDone(nsresult aRv)
{
// Called on the aCache background thread
MonitorAutoLock monitor(mMonitor);
mLoaded = true;
*mRv = aRv;
monitor.Notify();
}
virtual void LoadWait()
{
// Called on the main thread, exits after LoadDone() call
MonitorAutoLock monitor(mMonitor);
while (!mLoaded) {
monitor.Wait();
}
}
private:
Monitor mMonitor;
nsCString mScope;
InfallibleTArray<nsString>* mKeys;
InfallibleTArray<nsString>* mValues;
nsresult* mRv;
bool mLoaded;
uint32_t mLoadedCount;
};
} // anon
bool
DOMStorageDBParent::RecvPreload(const nsCString& aScope,
const uint32_t& aAlreadyLoadedCount,
InfallibleTArray<nsString>* aKeys,
InfallibleTArray<nsString>* aValues,
nsresult* aRv)
{
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
if (!db) {
return false;
}
nsRefPtr<SyncLoadCacheHelper> aCache(
new SyncLoadCacheHelper(aScope, aAlreadyLoadedCount, aKeys, aValues, aRv));
db->SyncPreload(aCache, true);
return true;
}
bool
DOMStorageDBParent::RecvAsyncAddItem(const nsCString& aScope,
const nsString& aKey,
const nsString& aValue)
{
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
if (!db) {
return false;
}
nsresult rv = db->AsyncAddItem(NewCache(aScope), aKey, aValue);
if (NS_FAILED(rv)) {
mozilla::unused << SendError(rv);
}
return true;
}
bool
DOMStorageDBParent::RecvAsyncUpdateItem(const nsCString& aScope,
const nsString& aKey,
const nsString& aValue)
{
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
if (!db) {
return false;
}
nsresult rv = db->AsyncUpdateItem(NewCache(aScope), aKey, aValue);
if (NS_FAILED(rv)) {
mozilla::unused << SendError(rv);
}
return true;
}
bool
DOMStorageDBParent::RecvAsyncRemoveItem(const nsCString& aScope,
const nsString& aKey)
{
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
if (!db) {
return false;
}
nsresult rv = db->AsyncRemoveItem(NewCache(aScope), aKey);
if (NS_FAILED(rv)) {
mozilla::unused << SendError(rv);
}
return true;
}
bool
DOMStorageDBParent::RecvAsyncClear(const nsCString& aScope)
{
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
if (!db) {
return false;
}
nsresult rv = db->AsyncClear(NewCache(aScope));
if (NS_FAILED(rv)) {
mozilla::unused << SendError(rv);
}
return true;
}
bool
DOMStorageDBParent::RecvAsyncFlush()
{
DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
if (!db) {
return false;
}
db->AsyncFlush();
return true;
}
// DOMStorageObserverSink
nsresult
DOMStorageDBParent::Observe(const char* aTopic,
const nsACString& aScopePrefix)
{
mozilla::unused << SendObserve(nsDependentCString(aTopic),
nsCString(aScopePrefix));
return NS_OK;
}
namespace { // anon
// Results must be sent back on the main thread
class LoadRunnable : public nsRunnable
{
public:
enum TaskType {
loadItem,
loadDone
};
LoadRunnable(DOMStorageDBParent* aParent,
TaskType aType,
const nsACString& aScope,
const nsAString& aKey = EmptyString(),
const nsAString& aValue = EmptyString())
: mParent(aParent)
, mType(aType)
, mScope(aScope)
, mKey(aKey)
, mValue(aValue)
{ }
LoadRunnable(DOMStorageDBParent* aParent,
TaskType aType,
const nsACString& aScope,
nsresult aRv)
: mParent(aParent)
, mType(aType)
, mScope(aScope)
, mRv(aRv)
{ }
private:
nsRefPtr<DOMStorageDBParent> mParent;
TaskType mType;
nsCString mScope;
nsString mKey;
nsString mValue;
nsresult mRv;
NS_IMETHOD Run()
{
if (!mParent->IPCOpen()) {
return NS_OK;
}
switch (mType)
{
case loadItem:
mozilla::unused << mParent->SendLoadItem(mScope, mKey, mValue);
break;
case loadDone:
mozilla::unused << mParent->SendLoadDone(mScope, mRv);
break;
}
return NS_OK;
}
};
} // anon
// DOMStorageDBParent::CacheParentBridge
bool
DOMStorageDBParent::CacheParentBridge::LoadItem(const nsAString& aKey, const nsString& aValue)
{
if (mLoaded) {
return false;
}
++mLoadedCount;
nsRefPtr<LoadRunnable> r =
new LoadRunnable(mParent, LoadRunnable::loadItem, mScope, aKey, aValue);
NS_DispatchToMainThread(r);
return true;
}
void
DOMStorageDBParent::CacheParentBridge::LoadDone(nsresult aRv)
{
// Prevent send of duplicate LoadDone.
if (mLoaded) {
return;
}
mLoaded = true;
nsRefPtr<LoadRunnable> r =
new LoadRunnable(mParent, LoadRunnable::loadDone, mScope, aRv);
NS_DispatchToMainThread(r);
}
void
DOMStorageDBParent::CacheParentBridge::LoadWait()
{
// Should never be called on this implementation
MOZ_ASSERT(false);
}
// DOMStorageDBParent::UsageParentBridge
namespace { // anon
class UsageRunnable : public nsRunnable
{
public:
UsageRunnable(DOMStorageDBParent* aParent, const nsACString& aScope, const int64_t& aUsage)
: mParent(aParent)
, mScope(aScope)
, mUsage(aUsage)
{}
private:
NS_IMETHOD Run()
{
mozilla::unused << mParent->SendLoadUsage(mScope, mUsage);
return NS_OK;
}
nsRefPtr<DOMStorageDBParent> mParent;
nsCString mScope;
int64_t mUsage;
};
} // anon
void
DOMStorageDBParent::UsageParentBridge::LoadUsage(const int64_t aUsage)
{
nsRefPtr<UsageRunnable> r = new UsageRunnable(mParent, mScope, aUsage);
NS_DispatchToMainThread(r);
delete this;
}
} // ::dom
} // ::mozilla

Просмотреть файл

@ -0,0 +1,187 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsDOMStorageIPC_h___
#define nsDOMStorageIPC_h___
#include "mozilla/dom/PStorageChild.h"
#include "mozilla/dom/PStorageParent.h"
#include "DOMStorageDBThread.h"
#include "DOMStorageCache.h"
#include "DOMStorageObserver.h"
#include "mozilla/Mutex.h"
namespace mozilla {
namespace dom {
class DOMLocalStorageManager;
// Child side of the IPC protocol, exposes as DB interface but
// is responsible to send all requests to the parent process
// and expects asynchronous answers. Those are then transparently
// forwarded back to consumers on the child process.
class DOMStorageDBChild MOZ_FINAL : public DOMStorageDBBridge
, public PStorageChild
{
public:
DOMStorageDBChild(DOMLocalStorageManager* aManager);
virtual ~DOMStorageDBChild();
NS_IMETHOD_(nsrefcnt) AddRef(void);
NS_IMETHOD_(nsrefcnt) Release(void);
void AddIPDLReference();
void ReleaseIPDLReference();
virtual nsresult Init();
virtual nsresult Shutdown();
virtual void AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority = false)
{ SendAsyncPreload(aCache->Scope(), aPriority); }
virtual void AsyncGetUsage(DOMStorageUsageBridge* aUsage)
{ SendAsyncGetUsage(aUsage->Scope()); }
virtual void SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync = false);
virtual nsresult AsyncAddItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue);
virtual nsresult AsyncUpdateItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue);
virtual nsresult AsyncRemoveItem(DOMStorageCacheBridge* aCache, const nsAString& aKey);
virtual nsresult AsyncClear(DOMStorageCacheBridge* aCache);
virtual void AsyncClearAll()
{ mScopesHavingData.Clear(); /* NO-OP on the child process otherwise */ }
virtual void AsyncClearMatchingScope(const nsACString& aScope)
{ /* NO-OP on the child process */ }
virtual void AsyncFlush()
{ SendAsyncFlush(); }
virtual bool ShouldPreloadScope(const nsACString& aScope);
virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes)
{ NS_NOTREACHED("Not implemented for child process"); }
private:
bool RecvObserve(const nsCString& aTopic,
const nsCString& aScopePrefix);
bool RecvLoadItem(const nsCString& aScope,
const nsString& aKey,
const nsString& aValue);
bool RecvLoadDone(const nsCString& aScope,
const nsresult& aRv);
bool RecvScopesHavingData(const InfallibleTArray<nsCString>& aScopes);
bool RecvLoadUsage(const nsCString& aScope,
const int64_t& aUsage);
bool RecvError(const nsresult& aRv);
nsTHashtable<nsCStringHashKey>& ScopesHavingData();
nsAutoRefCnt mRefCnt;
// Held to get caches to forward answers to.
nsRefPtr<DOMLocalStorageManager> mManager;
// Scopes having data hash, for optimization purposes only
nsTHashtable<nsCStringHashKey> mScopesHavingData;
// Status of the remote database
nsresult mStatus;
bool mIPCOpen;
};
// Receives async requests from child processes and is responsible
// to send back responses from the DB thread. Exposes as a fake
// DOMStorageCache consumer.
// Also responsible for forwardning all chrome operation notifications
// such as cookie cleaning etc to the child process.
class DOMStorageDBParent MOZ_FINAL : public PStorageParent
, public DOMStorageObserverSink
{
public:
DOMStorageDBParent();
virtual ~DOMStorageDBParent();
NS_IMETHOD_(nsrefcnt) AddRef(void);
NS_IMETHOD_(nsrefcnt) Release(void);
void AddIPDLReference();
void ReleaseIPDLReference();
bool IPCOpen() { return mIPCOpen; }
public:
// Fake cache class receiving async callbacks from DB thread, sending
// them back to appropriate cache object on the child process.
class CacheParentBridge : public DOMStorageCacheBridge {
public:
CacheParentBridge(DOMStorageDBParent* aParentDB, const nsACString& aScope)
: mParent(aParentDB), mScope(aScope), mLoaded(false), mLoadedCount(0) {}
virtual ~CacheParentBridge() {}
// DOMStorageCacheBridge
virtual const nsCString& Scope() const
{ return mScope; }
virtual bool Loaded()
{ return mLoaded; }
virtual uint32_t LoadedCount()
{ return mLoadedCount; }
virtual bool LoadItem(const nsAString& aKey, const nsString& aValue);
virtual void LoadDone(nsresult aRv);
virtual void LoadWait();
private:
DOMStorageDBParent* mParent;
nsCString mScope;
bool mLoaded;
uint32_t mLoadedCount;
};
// Fake usage class receiving async callbacks from DB thread
class UsageParentBridge : public DOMStorageUsageBridge
{
public:
UsageParentBridge(DOMStorageDBParent* aParentDB, const nsACString& aScope)
: mParent(aParentDB), mScope(aScope) {}
virtual ~UsageParentBridge() {}
// DOMStorageUsageBridge
virtual const nsCString& Scope() { return mScope; }
virtual void LoadUsage(const int64_t usage);
private:
DOMStorageDBParent* mParent;
nsCString mScope;
};
private:
// IPC
bool RecvAsyncPreload(const nsCString& aScope, const bool& aPriority);
bool RecvPreload(const nsCString& aScope, const uint32_t& aAlreadyLoadedCount,
InfallibleTArray<nsString>* aKeys, InfallibleTArray<nsString>* aValues,
nsresult* aRv);
bool RecvAsyncGetUsage(const nsCString& aScope);
bool RecvAsyncAddItem(const nsCString& aScope, const nsString& aKey, const nsString& aValue);
bool RecvAsyncUpdateItem(const nsCString& aScope, const nsString& aKey, const nsString& aValue);
bool RecvAsyncRemoveItem(const nsCString& aScope, const nsString& aKey);
bool RecvAsyncClear(const nsCString& aScope);
bool RecvAsyncFlush();
// DOMStorageObserverSink
virtual nsresult Observe(const char* aTopic, const nsACString& aScopePrefix);
private:
CacheParentBridge* NewCache(const nsACString& aScope);
nsAutoRefCnt mRefCnt;
bool mIPCOpen;
};
} // ::dom
} // ::mozilla
#endif

Просмотреть файл

@ -0,0 +1,625 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DOMStorageManager.h"
#include "DOMStorage.h"
#include "DOMStorageDBThread.h"
#include "nsIScriptSecurityManager.h"
#include "nsIEffectiveTLDService.h"
#include "nsNetUtil.h"
#include "nsPrintfCString.h"
#include "nsXULAppAPI.h"
#include "nsThreadUtils.h"
#include "nsIObserverService.h"
#include "mozilla/Services.h"
#include "mozilla/Preferences.h"
// Only allow relatively small amounts of data since performance of
// the synchronous IO is very bad.
// We are enforcing simple per-origin quota only.
#define DEFAULT_QUOTA_LIMIT (5 * 1024)
namespace mozilla {
namespace dom {
namespace { // anon
int32_t gQuotaLimit = DEFAULT_QUOTA_LIMIT;
} // anon
DOMLocalStorageManager*
DOMLocalStorageManager::sSelf = nullptr;
// static
uint32_t
DOMStorageManager::GetQuota()
{
static bool preferencesInitialized = false;
if (!preferencesInitialized) {
mozilla::Preferences::AddIntVarCache(&gQuotaLimit, "dom.storage.default_quota",
DEFAULT_QUOTA_LIMIT);
preferencesInitialized = true;
}
return gQuotaLimit * 1024; // pref is in kBs
}
void
ReverseString(const nsCSubstring& aSource, nsCSubstring& aResult)
{
nsACString::const_iterator sourceBegin, sourceEnd;
aSource.BeginReading(sourceBegin);
aSource.EndReading(sourceEnd);
aResult.SetLength(aSource.Length());
nsACString::iterator destEnd;
aResult.EndWriting(destEnd);
while (sourceBegin != sourceEnd) {
*(--destEnd) = *sourceBegin;
++sourceBegin;
}
}
nsresult
CreateReversedDomain(const nsACString& aAsciiDomain,
nsACString& aKey)
{
if (aAsciiDomain.IsEmpty()) {
return NS_ERROR_NOT_AVAILABLE;
}
ReverseString(aAsciiDomain, aKey);
aKey.AppendLiteral(".");
return NS_OK;
}
bool
PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal)
{
if (!aSubjectPrincipal) {
return true;
}
if (!aObjectPrincipal) {
return false;
}
bool equals;
nsresult rv = aSubjectPrincipal->EqualsIgnoringDomain(aObjectPrincipal, &equals);
NS_ASSERTION(NS_SUCCEEDED(rv) && equals,
"Trying to get DOM storage for wrong principal!");
if (NS_FAILED(rv) || !equals) {
return false;
}
return true;
}
NS_IMPL_ISUPPORTS1(DOMStorageManager,
nsIDOMStorageManager)
DOMStorageManager::DOMStorageManager(nsPIDOMStorage::StorageType aType)
: mType(aType)
{
mCaches.Init(10);
DOMStorageObserver* observer = DOMStorageObserver::Self();
NS_ASSERTION(observer, "No DOMStorageObserver, cannot observe private data delete notifications!");
if (observer) {
observer->AddSink(this);
}
}
DOMStorageManager::~DOMStorageManager()
{
DOMStorageObserver* observer = DOMStorageObserver::Self();
if (observer) {
observer->RemoveSink(this);
}
}
namespace { // anon
nsresult
CreateScopeKey(nsIPrincipal* aPrincipal,
nsACString& aKey)
{
nsCOMPtr<nsIURI> uri;
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
if (!uri) {
return NS_ERROR_UNEXPECTED;
}
nsAutoCString domainScope;
rv = uri->GetAsciiHost(domainScope);
NS_ENSURE_SUCCESS(rv, rv);
if (domainScope.IsEmpty()) {
// About pages have an empty host but a valid path. Since they are handled
// internally by our own redirector, we can trust them and use path as key.
// if file:/// protocol, let's make the exact directory the domain
bool isScheme = false;
if ((NS_SUCCEEDED(uri->SchemeIs("about", &isScheme)) && isScheme) ||
(NS_SUCCEEDED(uri->SchemeIs("moz-safe-about", &isScheme)) && isScheme)) {
rv = uri->GetPath(domainScope);
NS_ENSURE_SUCCESS(rv, rv);
// While the host is always canonicalized to lowercase, the path is not,
// thus need to force the casing.
ToLowerCase(domainScope);
} else if (NS_SUCCEEDED(uri->SchemeIs("file", &isScheme)) && isScheme) {
nsCOMPtr<nsIURL> url = do_QueryInterface(uri, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = url->GetDirectory(domainScope);
NS_ENSURE_SUCCESS(rv, rv);
}
}
nsAutoCString key;
rv = CreateReversedDomain(domainScope, key);
if (NS_FAILED(rv)) {
return rv;
}
nsAutoCString scheme;
rv = uri->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);
key.Append(NS_LITERAL_CSTRING(":") + scheme);
int32_t port = NS_GetRealPort(uri);
if (port != -1) {
key.Append(nsPrintfCString(":%d", port));
}
bool unknownAppId;
rv = aPrincipal->GetUnknownAppId(&unknownAppId);
NS_ENSURE_SUCCESS(rv, rv);
if (!unknownAppId) {
uint32_t appId;
rv = aPrincipal->GetAppId(&appId);
NS_ENSURE_SUCCESS(rv, rv);
bool isInBrowserElement;
rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
NS_ENSURE_SUCCESS(rv, rv);
if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
aKey.Assign(key);
return NS_OK;
}
aKey.Truncate();
aKey.AppendInt(appId);
aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
NS_LITERAL_CSTRING(":") + key);
}
return NS_OK;
}
nsresult
CreateQuotaDBKey(nsIPrincipal* aPrincipal,
nsACString& aKey)
{
nsresult rv;
nsAutoCString subdomainsDBKey;
nsCOMPtr<nsIEffectiveTLDService> eTLDService(do_GetService(
NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> uri;
rv = aPrincipal->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
nsAutoCString eTLDplusOne;
rv = eTLDService->GetBaseDomain(uri, 0, eTLDplusOne);
if (NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS == rv) {
// XXX bug 357323 - what to do for localhost/file exactly?
rv = uri->GetAsciiHost(eTLDplusOne);
}
NS_ENSURE_SUCCESS(rv, rv);
CreateReversedDomain(eTLDplusOne, subdomainsDBKey);
bool unknownAppId;
rv = aPrincipal->GetUnknownAppId(&unknownAppId);
NS_ENSURE_SUCCESS(rv, rv);
if (!unknownAppId) {
uint32_t appId;
rv = aPrincipal->GetAppId(&appId);
NS_ENSURE_SUCCESS(rv, rv);
bool isInBrowserElement;
rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
NS_ENSURE_SUCCESS(rv, rv);
if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
aKey.Assign(subdomainsDBKey);
return NS_OK;
}
aKey.Truncate();
aKey.AppendInt(appId);
aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
NS_LITERAL_CSTRING(":") + subdomainsDBKey);
}
return NS_OK;
}
} // anon
DOMStorageCache*
DOMStorageManager::GetCache(const nsACString& aScope) const
{
DOMStorageCacheHashKey* entry = mCaches.GetEntry(aScope);
if (!entry) {
return nullptr;
}
return entry->cache();
}
DOMStorageCache*
DOMStorageManager::PutCache(const nsACString& aScope,
nsIPrincipal* aPrincipal)
{
DOMStorageCacheHashKey* entry = mCaches.PutEntry(aScope);
DOMStorageCache* cache = entry->cache();
nsAutoCString quotaScope;
CreateQuotaDBKey(aPrincipal, quotaScope);
switch (mType) {
case SessionStorage:
// Lifetime handled by the manager, don't persist
entry->HardRef();
cache->Init(nullptr, false, aPrincipal, quotaScope);
break;
case LocalStorage:
// Lifetime handled by the cache, do persist
cache->Init(this, true, aPrincipal, quotaScope);
break;
default:
MOZ_ASSERT(false);
}
return cache;
}
void
DOMStorageManager::DropCache(DOMStorageCache* aCache)
{
if (!NS_IsMainThread()) {
NS_WARNING("DOMStorageManager::DropCache called on a non-main thread, shutting down?");
}
mCaches.RemoveEntry(aCache->Scope());
}
nsresult
DOMStorageManager::GetStorageInternal(bool aCreate,
nsIPrincipal* aPrincipal,
const nsAString& aDocumentURI,
bool aPrivate,
nsIDOMStorage** aRetval)
{
nsresult rv;
nsAutoCString scope;
rv = CreateScopeKey(aPrincipal, scope);
if (NS_FAILED(rv)) {
return NS_ERROR_NOT_AVAILABLE;
}
DOMStorageCache* cache = GetCache(scope);
// Get or create a cache for the given scope
if (!cache) {
if (!aCreate) {
*aRetval = nullptr;
return NS_OK;
}
if (!aRetval) {
// This is demand to just preload the cache, if the scope has
// no data stored, bypass creation and preload of the cache.
DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
if (db) {
if (!db->ShouldPreloadScope(scope)) {
return NS_OK;
}
} else {
if (scope.Equals(NS_LITERAL_CSTRING("knalb.:about"))) {
return NS_OK;
}
}
}
// There is always a single instance of a cache per scope
// in a single instance of a DOM storage manager.
cache = PutCache(scope, aPrincipal);
} else if (mType == SessionStorage) {
if (!cache->CheckPrincipal(aPrincipal)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
}
if (aRetval) {
*aRetval = new DOMStorage(this, cache, aDocumentURI, aPrincipal, aPrivate);
NS_ADDREF(*aRetval);
}
return NS_OK;
}
NS_IMETHODIMP
DOMStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal)
{
return GetStorageInternal(true, aPrincipal, EmptyString(), false, nullptr);
}
NS_IMETHODIMP
DOMStorageManager::CreateStorage(nsIPrincipal* aPrincipal,
const nsAString& aDocumentURI,
bool aPrivate,
nsIDOMStorage** aRetval)
{
return GetStorageInternal(true, aPrincipal, aDocumentURI, aPrivate, aRetval);
}
NS_IMETHODIMP
DOMStorageManager::GetStorage(nsIPrincipal* aPrincipal,
bool aPrivate,
nsIDOMStorage** aRetval)
{
return GetStorageInternal(false, aPrincipal, EmptyString(), aPrivate, aRetval);
}
NS_IMETHODIMP
DOMStorageManager::CloneStorage(nsIDOMStorage* aStorage)
{
nsCOMPtr<nsPIDOMStorage> pstorage = do_QueryInterface(aStorage);
if (!pstorage) {
return NS_ERROR_UNEXPECTED;
}
const DOMStorageCache* origCache = pstorage->GetCache();
DOMStorageCache* existingCache = GetCache(origCache->Scope());
if (existingCache) {
// Do not replace an existing session storage.
return NS_ERROR_NOT_AVAILABLE;
}
DOMStorageCache* newCache = PutCache(origCache->Scope(),
origCache->Principal());
newCache->CloneFrom(origCache);
newCache->KeepAlive();
return NS_OK;
}
NS_IMETHODIMP
DOMStorageManager::CheckStorage(nsIPrincipal* aPrincipal,
nsIDOMStorage* aStorage,
bool* aRetval)
{
nsCOMPtr<nsPIDOMStorage> pstorage = do_QueryInterface(aStorage);
if (!pstorage) {
return NS_ERROR_UNEXPECTED;
}
*aRetval = false;
if (!aPrincipal) {
return NS_ERROR_NOT_AVAILABLE;
}
nsAutoCString scope;
nsresult rv = CreateScopeKey(aPrincipal, scope);
NS_ENSURE_SUCCESS(rv, rv);
DOMStorageCache* cache = GetCache(scope);
if (cache != pstorage->GetCache()) {
return NS_OK;
}
if (!pstorage->PrincipalEquals(aPrincipal)) {
return NS_OK;
}
*aRetval = true;
return NS_OK;
}
// Obsolete nsIDOMStorageManager methods
NS_IMETHODIMP
DOMStorageManager::GetLocalStorageForPrincipal(nsIPrincipal* aPrincipal,
const nsAString& aDocumentURI,
bool aPrivate,
nsIDOMStorage** aRetval)
{
if (mType != LocalStorage) {
return NS_ERROR_UNEXPECTED;
}
return CreateStorage(aPrincipal, aDocumentURI, aPrivate, aRetval);
}
namespace { // anon
class ClearCacheEnumeratorData
{
public:
ClearCacheEnumeratorData(uint32_t aFlags)
: mUnloadFlags(aFlags)
{}
uint32_t mUnloadFlags;
nsCString mKeyPrefix;
};
} // anon
PLDHashOperator
DOMStorageManager::ClearCacheEnumerator(DOMStorageCacheHashKey* aEntry, void* aClosure)
{
DOMStorageCache* cache = aEntry->cache();
nsCString& key = const_cast<nsCString&>(cache->Scope());
ClearCacheEnumeratorData* data = static_cast<ClearCacheEnumeratorData*>(aClosure);
if (data->mKeyPrefix.IsEmpty() || StringBeginsWith(key, data->mKeyPrefix)) {
cache->UnloadItems(data->mUnloadFlags);
}
return PL_DHASH_NEXT;
}
nsresult
DOMStorageManager::Observe(const char* aTopic, const nsACString& aScopePrefix)
{
// Clear everything, caches + database
if (!strcmp(aTopic, "cookie-cleared")) {
ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
return NS_OK;
}
// Clear from caches everything that has been stored
// while in session-only mode
if (!strcmp(aTopic, "session-only-cleared")) {
ClearCacheEnumeratorData data(DOMStorageCache::kUnloadSession);
data.mKeyPrefix = aScopePrefix;
mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
return NS_OK;
}
// Clear everything (including so and pb data) from caches and database
// for the gived domain and subdomains.
if (!strcmp(aTopic, "domain-data-cleared")) {
ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
data.mKeyPrefix = aScopePrefix;
mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
return NS_OK;
}
// Clear all private-browsing caches
if (!strcmp(aTopic, "private-browsing-data-cleared")) {
ClearCacheEnumeratorData data(DOMStorageCache::kUnloadPrivate);
mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
return NS_OK;
}
// Clear localStorage data beloging to an app.
if (!strcmp(aTopic, "app-data-cleared")) {
// sessionStorage is expected to stay
if (mType == SessionStorage) {
return NS_OK;
}
ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
data.mKeyPrefix = aScopePrefix;
mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
return NS_OK;
}
if (!strcmp(aTopic, "profile-change")) {
// For case caches are still referenced - clear them completely
ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
mCaches.Clear();
return NS_OK;
}
#ifdef DOM_STORAGE_TESTS
if (!strcmp(aTopic, "test-reload")) {
if (mType != LocalStorage) {
return NS_OK;
}
// This immediately completely reloads all caches from the database.
ClearCacheEnumeratorData data(DOMStorageCache::kTestReload);
mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
return NS_OK;
}
if (!strcmp(aTopic, "test-flushed")) {
if (XRE_GetProcessType() != GeckoProcessType_Default) {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->NotifyObservers(nullptr, "domstorage-test-flushed", nullptr);
}
}
return NS_OK;
}
#endif
NS_ERROR("Unexpected topic");
return NS_ERROR_UNEXPECTED;
}
// DOMLocalStorageManager
DOMLocalStorageManager::DOMLocalStorageManager()
: DOMStorageManager(LocalStorage)
{
NS_ASSERTION(!sSelf, "Somebody is trying to do_CreateInstance(\"@mozilla/dom/localStorage-manager;1\"");
sSelf = this;
if (XRE_GetProcessType() != GeckoProcessType_Default) {
// Do this only on the child process. The thread IPC bridge
// is also used to communicate chrome observer notifications.
// Note: must be called after we set sSelf
DOMStorageCache::StartDatabase();
}
}
DOMLocalStorageManager::~DOMLocalStorageManager()
{
sSelf = nullptr;
}
// DOMSessionStorageManager
DOMSessionStorageManager::DOMSessionStorageManager()
: DOMStorageManager(SessionStorage)
{
if (XRE_GetProcessType() != GeckoProcessType_Default) {
// Do this only on the child process. The thread IPC bridge
// is also used to communicate chrome observer notifications.
DOMStorageCache::StartDatabase();
}
}
} // ::dom
} // ::mozilla

Просмотреть файл

@ -0,0 +1,127 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsDOMStorageManager_h__
#define nsDOMStorageManager_h__
#include "nsIDOMStorageManager.h"
#include "DOMStorageObserver.h"
#include "nsPIDOMStorage.h"
#include "DOMStorageCache.h"
#include "nsTHashtable.h"
namespace mozilla {
namespace dom {
const nsPIDOMStorage::StorageType SessionStorage = nsPIDOMStorage::SessionStorage;
const nsPIDOMStorage::StorageType LocalStorage = nsPIDOMStorage::LocalStorage;
class DOMStorage;
class DOMStorageManager : public nsIDOMStorageManager
, public DOMStorageObserverSink
{
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMSTORAGEMANAGER
public:
virtual nsPIDOMStorage::StorageType Type() { return mType; }
// Reads the preference for DOM storage quota
static uint32_t GetQuota();
// Gets (but not ensures) cache for the given scope
DOMStorageCache* GetCache(const nsACString& aScope) const;
protected:
DOMStorageManager(nsPIDOMStorage::StorageType aType);
virtual ~DOMStorageManager();
private:
// DOMStorageObserverSink, handler to various chrome clearing notification
virtual nsresult Observe(const char* aTopic, const nsACString& aScopePrefix);
// Since nsTHashtable doesn't like multiple inheritance, we have to aggregate
// DOMStorageCache into the entry.
class DOMStorageCacheHashKey : public nsCStringHashKey
{
public:
DOMStorageCacheHashKey(const nsACString* aKey)
: nsCStringHashKey(aKey)
, mCache(new DOMStorageCache(aKey))
{}
DOMStorageCacheHashKey(const DOMStorageCacheHashKey& aOther)
: nsCStringHashKey(aOther)
{
NS_ERROR("Shouldn't be called");
}
DOMStorageCache* cache() { return mCache; }
// Keep the cache referenced forever, used for sessionStorage.
void HardRef() { mCacheRef = mCache; }
private:
// weak ref only since cache references its manager.
DOMStorageCache* mCache;
// hard ref when this is sessionStorage to keep it alive forever.
nsRefPtr<DOMStorageCache> mCacheRef;
};
// Ensures cache for a scope, when it doesn't exist it is created and initalized,
// this also starts preload of persistent data.
DOMStorageCache* PutCache(const nsACString& aScope,
nsIPrincipal* aPrincipal);
// Helper for creation of DOM storage objects
nsresult GetStorageInternal(bool aCreate,
nsIPrincipal* aPrincipal,
const nsAString& aDocumentURI,
bool aPrivate,
nsIDOMStorage** aRetval);
// Scope->cache map
nsTHashtable<DOMStorageCacheHashKey> mCaches;
const nsPIDOMStorage::StorageType mType;
static PLDHashOperator ClearCacheEnumerator(DOMStorageCacheHashKey* aCache,
void* aClosure);
protected:
friend class DOMStorageCache;
// Releases cache since it is no longer referrered by any DOMStorage object.
virtual void DropCache(DOMStorageCache* aCache);
};
// Derived classes to allow two different contract ids, one for localStorage and
// one for sessionStorage management. localStorage manager is used as service
// scoped to the application while sessionStorage managers are instantiated by each
// top doc shell in the application since sessionStorages are isolated per top level
// browsing context. The code may easily by shared by both.
class DOMLocalStorageManager MOZ_FINAL : public DOMStorageManager
{
public:
DOMLocalStorageManager();
virtual ~DOMLocalStorageManager();
// Global getter of localStorage manager service
static DOMLocalStorageManager* Self() { return sSelf; }
private:
static DOMLocalStorageManager* sSelf;
};
class DOMSessionStorageManager MOZ_FINAL : public DOMStorageManager
{
public:
DOMSessionStorageManager();
};
} // ::dom
} // ::mozilla
#endif /* nsDOMStorageManager_h__ */

Просмотреть файл

@ -0,0 +1,332 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DOMStorageObserver.h"
#include "DOMStorageDBThread.h"
#include "DOMStorageCache.h"
#include "nsIObserverService.h"
#include "nsIURI.h"
#include "nsIURL.h"
#include "nsIScriptSecurityManager.h"
#include "nsIPermission.h"
#include "nsIIDNService.h"
#include "mozIApplicationClearPrivateDataParams.h"
#include "nsICookiePermission.h"
#include "nsPrintfCString.h"
#include "nsXULAppAPI.h"
#include "nsEscape.h"
#include "nsNetCID.h"
#include "mozilla/Services.h"
#include "nsServiceManagerUtils.h"
namespace mozilla {
namespace dom {
static const char kStartupTopic[] = "sessionstore-windows-restored";
static const uint32_t kStartupDelay = 0;
NS_IMPL_ISUPPORTS2(DOMStorageObserver,
nsIObserver,
nsISupportsWeakReference)
DOMStorageObserver* DOMStorageObserver::sSelf = nullptr;
extern nsresult
CreateReversedDomain(const nsACString& aAsciiDomain, nsACString& aKey);
// static
nsresult
DOMStorageObserver::Init()
{
if (sSelf) {
return NS_OK;
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs) {
return NS_ERROR_UNEXPECTED;
}
sSelf = new DOMStorageObserver();
NS_ADDREF(sSelf);
// Chrome clear operations.
obs->AddObserver(sSelf, kStartupTopic, true);
obs->AddObserver(sSelf, "cookie-changed", true);
obs->AddObserver(sSelf, "perm-changed", true);
obs->AddObserver(sSelf, "browser:purge-domain-data", true);
obs->AddObserver(sSelf, "last-pb-context-exited", true);
obs->AddObserver(sSelf, "webapps-clear-data", true);
// Shutdown
obs->AddObserver(sSelf, "profile-after-change", true);
obs->AddObserver(sSelf, "profile-before-change", true);
obs->AddObserver(sSelf, "xpcom-shutdown", true);
#ifdef DOM_STORAGE_TESTS
// Testing
obs->AddObserver(sSelf, "domstorage-test-flush-force", true);
if (XRE_GetProcessType() == GeckoProcessType_Default) {
// Only to forward to child process.
obs->AddObserver(sSelf, "domstorage-test-flushed", true);
}
obs->AddObserver(sSelf, "domstorage-test-reload", true);
#endif
return NS_OK;
}
// static
nsresult
DOMStorageObserver::Shutdown()
{
if (!sSelf) {
return NS_ERROR_NOT_INITIALIZED;
}
NS_RELEASE(sSelf);
return NS_OK;
}
void
DOMStorageObserver::AddSink(DOMStorageObserverSink* aObs)
{
mSinks.AppendElement(aObs);
}
void
DOMStorageObserver::RemoveSink(DOMStorageObserverSink* aObs)
{
mSinks.RemoveElement(aObs);
}
void
DOMStorageObserver::Notify(const char* aTopic, const nsACString& aData)
{
for (uint32_t i = 0; i < mSinks.Length(); ++i) {
DOMStorageObserverSink* sink = mSinks[i];
sink->Observe(aTopic, aData);
}
}
NS_IMETHODIMP
DOMStorageObserver::Observe(nsISupports* aSubject,
const char* aTopic,
const PRUnichar* aData)
{
nsresult rv;
// Start the thread that opens the database.
if (!strcmp(aTopic, kStartupTopic)) {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->RemoveObserver(this, kStartupTopic);
mDBThreadStartDelayTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
if (!mDBThreadStartDelayTimer) {
return NS_ERROR_UNEXPECTED;
}
mDBThreadStartDelayTimer->Init(this, nsITimer::TYPE_ONE_SHOT, kStartupDelay);
return NS_OK;
}
// Timer callback used to start the database a short timer after startup
if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
nsCOMPtr<nsITimer> timer = do_QueryInterface(aSubject);
if (!timer) {
return NS_ERROR_UNEXPECTED;
}
if (timer == mDBThreadStartDelayTimer) {
mDBThreadStartDelayTimer = nullptr;
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
}
return NS_OK;
}
// Clear everything, caches + database
if (!strcmp(aTopic, "cookie-changed")) {
if (!NS_LITERAL_STRING("cleared").Equals(aData)) {
return NS_OK;
}
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
db->AsyncClearAll();
Notify("cookie-cleared");
return NS_OK;
}
// Clear from caches everything that has been stored
// while in session-only mode
if (!strcmp(aTopic, "perm-changed")) {
// Check for cookie permission change
nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject));
if (!perm) {
return NS_OK;
}
nsAutoCString type;
perm->GetType(type);
if (type != NS_LITERAL_CSTRING("cookie")) {
return NS_OK;
}
uint32_t cap = 0;
perm->GetCapability(&cap);
if (!(cap & nsICookiePermission::ACCESS_SESSION) ||
!NS_LITERAL_STRING("deleted").Equals(nsDependentString(aData))) {
return NS_OK;
}
nsAutoCString host;
perm->GetHost(host);
if (host.IsEmpty()) {
return NS_OK;
}
nsAutoCString scope;
rv = CreateReversedDomain(host, scope);
NS_ENSURE_SUCCESS(rv, rv);
Notify("session-only-cleared", scope);
return NS_OK;
}
// Clear everything (including so and pb data) from caches and database
// for the gived domain and subdomains.
if (!strcmp(aTopic, "browser:purge-domain-data")) {
// Convert the domain name to the ACE format
nsAutoCString aceDomain;
nsCOMPtr<nsIIDNService> converter = do_GetService(NS_IDNSERVICE_CONTRACTID);
if (converter) {
rv = converter->ConvertUTF8toACE(NS_ConvertUTF16toUTF8(aData), aceDomain);
NS_ENSURE_SUCCESS(rv, rv);
} else {
// In case the IDN service is not available, this is the best we can come up with!
NS_EscapeURL(NS_ConvertUTF16toUTF8(aData),
esc_OnlyNonASCII | esc_AlwaysCopy,
aceDomain);
}
nsAutoCString scopePrefix;
rv = CreateReversedDomain(aceDomain, scopePrefix);
NS_ENSURE_SUCCESS(rv, rv);
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
db->AsyncClearMatchingScope(scopePrefix);
Notify("domain-data-cleared", scopePrefix);
return NS_OK;
}
// Clear all private-browsing caches
if (!strcmp(aTopic, "last-pb-context-exited")) {
Notify("private-browsing-data-cleared");
return NS_OK;
}
// Clear data beloging to an app.
if (!strcmp(aTopic, "webapps-clear-data")) {
nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
do_QueryInterface(aSubject);
if (!params) {
NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams");
return NS_ERROR_UNEXPECTED;
}
uint32_t appId;
bool browserOnly;
rv = params->GetAppId(&appId);
NS_ENSURE_SUCCESS(rv, rv);
rv = params->GetBrowserOnly(&browserOnly);
NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
nsAutoCString scope;
scope.AppendInt(appId);
scope.Append(NS_LITERAL_CSTRING(":t:"));
db->AsyncClearMatchingScope(scope);
Notify("app-data-cleared", scope);
if (!browserOnly) {
scope.Truncate();
scope.AppendInt(appId);
scope.Append(NS_LITERAL_CSTRING(":f:"));
db->AsyncClearMatchingScope(scope);
Notify("app-data-cleared", scope);
}
return NS_OK;
}
if (!strcmp(aTopic, "profile-after-change")) {
Notify("profile-change");
return NS_OK;
}
if (!strcmp(aTopic, "profile-before-change") ||
!strcmp(aTopic, "xpcom-shutdown")) {
rv = DOMStorageCache::StopDatabase();
if (NS_FAILED(rv)) {
NS_WARNING("Error while stopping DOMStorage DB background thread");
}
return NS_OK;
}
#ifdef DOM_STORAGE_TESTS
if (!strcmp(aTopic, "domstorage-test-flush-force")) {
DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
if (db) {
db->AsyncFlush();
}
return NS_OK;
}
if (!strcmp(aTopic, "domstorage-test-flushed")) {
// Only used to propagate to IPC children
Notify("test-flushed");
return NS_OK;
}
if (!strcmp(aTopic, "domstorage-test-reload")) {
Notify("test-reload");
return NS_OK;
}
#endif
NS_ERROR("Unexpected topic");
return NS_ERROR_UNEXPECTED;
}
} // ::dom
} // ::mozilla

Просмотреть файл

@ -0,0 +1,62 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsIDOMStorageObserver_h__
#define nsIDOMStorageObserver_h__
#include "nsIObserver.h"
#include "nsITimer.h"
#include "nsWeakReference.h"
#include "nsTArray.h"
#include "nsString.h"
namespace mozilla {
namespace dom {
class DOMStorageObserver;
// Implementers are DOMStorageManager and DOMStorageDBParent to forward to
// child processes.
class DOMStorageObserverSink
{
public:
virtual ~DOMStorageObserverSink() {}
private:
friend class DOMStorageObserver;
virtual nsresult Observe(const char* aTopic, const nsACString& aScopePrefix) = 0;
};
// Statically (though layout statics) initialized observer receiving and processing
// chrome clearing notifications, such as cookie deletion etc.
class DOMStorageObserver : public nsIObserver
, public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
virtual ~DOMStorageObserver() {}
static nsresult Init();
static nsresult Shutdown();
static DOMStorageObserver* Self() { return sSelf; }
void AddSink(DOMStorageObserverSink* aObs);
void RemoveSink(DOMStorageObserverSink* aObs);
void Notify(const char* aTopic, const nsACString& aData = EmptyCString());
private:
static DOMStorageObserver* sSelf;
// Weak references
nsTArray<DOMStorageObserverSink*> mSinks;
nsCOMPtr<nsITimer> mDBThreadStartDelayTimer;
};
} // ::dom
} // ::mozilla
#endif

Просмотреть файл

@ -15,18 +15,16 @@ LIBRARY_NAME = jsdomstorage_s
LIBXUL_LIBRARY = 1
CPPSRCS = \
nsDOMStorage.cpp \
nsDOMStorageBaseDB.cpp \
nsDOMStorageDBWrapper.cpp \
nsLocalStorageCache.cpp \
nsDOMStoragePersistentDB.cpp \
nsDOMStorageMemoryDB.cpp \
StorageChild.cpp \
StorageParent.cpp \
DOMStorage.cpp \
DOMStorageCache.cpp \
DOMStorageDBThread.cpp \
DOMStorageObserver.cpp \
DOMStorageManager.cpp \
DOMStorageIPC.cpp \
$(NULL)
EXPORTS_NAMESPACES = mozilla/dom
EXPORTS_mozilla/dom = StorageChild.h StorageParent.h
EXPORTS_mozilla/dom = DOMStorageIPC.h
# we don't want the shared lib, but we want to force the creation of a static lib.
FORCE_STATIC_LIB = 1
@ -37,6 +35,10 @@ LOCAL_INCLUDES = \
DEFINES += -D_IMPL_NS_LAYOUT
ifdef ENABLE_TESTS
DEFINES += -DDOM_STORAGE_TESTS
endif
include $(topsrcdir)/config/config.mk
include $(topsrcdir)/ipc/chromium/chromium-config.mk
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -6,61 +6,37 @@
include protocol PContent;
using mozilla::null_t;
namespace mozilla {
namespace dom {
struct ItemData
{
nsString value;
bool secure;
};
// A cross-process GetValue result -- either null, or containing the parameters
// with which to initialize an nsIDOMStorageItem.
union StorageItem
{
null_t;
ItemData;
};
// This protocol is little more than a thin wrapper around the DOMStorageBase
// class in nsDOMStorage.h. The child implementation simply forwards the
// arguments for any given call to the parent, and returns the result.
/* This protocol bridges async access to the database thread running on the parent process
* and caches running on the child process.
*/
sync protocol PStorage
{
manager PContent;
parent:
__delete__();
async __delete__();
Init(bool useDB, bool sessionOnly, bool isPrivate,
nsCString scopeDBKey, nsCString quotaDBKey, uint32_t storageType);
sync Preload(nsCString scope, uint32_t alreadyLoadedCount)
returns (nsString[] keys, nsString[] values, nsresult rv);
async AsyncPreload(nsCString scope, bool priority);
async AsyncGetUsage(nsCString scope);
async AsyncAddItem(nsCString scope, nsString key, nsString value);
async AsyncUpdateItem(nsCString scope, nsString key, nsString value);
async AsyncRemoveItem(nsCString scope, nsString key);
async AsyncClear(nsCString scope);
async AsyncFlush();
sync GetKeys(bool callerSecure)
returns (nsString[] keys);
sync GetLength(bool callerSecure, bool sessionOnly)
returns (uint32_t length, nsresult rv);
sync GetKey(bool callerSecure, bool sessionOnly, uint32_t index)
returns (nsString key, nsresult rv);
sync GetValue(bool callerSecure, bool sessionOnly, nsString key)
returns (StorageItem item, nsresult rv);
sync SetValue(bool callerSecure, bool sessionOnly, nsString key, nsString data)
returns (nsString oldValue, nsresult rv);
sync RemoveValue(bool callerSecure, bool sessionOnly, nsString key)
returns (nsString oldValue, nsresult rv);
sync Clear(bool callerSecure, bool sessionOnly)
returns (int32_t oldCount, nsresult rv);
sync GetDBValue(nsString key)
returns (nsString value, bool secure, nsresult rv);
sync SetDBValue(nsString key, nsString value, bool secure)
returns (nsresult rv);
sync SetSecure(nsString key, bool secure)
returns (nsresult rv);
UpdatePrivateState(bool enabled);
child:
async Observe(nsCString topic, nsCString scopePrefix);
async ScopesHavingData(nsCString[] scopes);
async LoadItem(nsCString scope, nsString key, nsString value);
async LoadDone(nsCString scope, nsresult rv);
async LoadUsage(nsCString scope, int64_t usage);
async Error(nsresult rv);
};
}

Просмотреть файл

@ -1,250 +0,0 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "StorageChild.h"
#include "mozilla/dom/ContentChild.h"
#include "nsError.h"
#include "GeckoProfiler.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_1(StorageChild, mStorage)
NS_IMPL_CYCLE_COLLECTING_ADDREF(StorageChild)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StorageChild)
NS_INTERFACE_MAP_ENTRY(nsIPrivacyTransitionObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPrivacyTransitionObserver)
NS_INTERFACE_MAP_END
NS_IMETHODIMP_(nsrefcnt) StorageChild::Release(void)
{
NS_PRECONDITION(0 != mRefCnt, "dup release");
NS_ASSERT_OWNINGTHREAD(StorageChild);
nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(StorageChild)::Upcast(this);
nsrefcnt count = mRefCnt.decr(base);
NS_LOG_RELEASE(this, count, "StorageChild");
if (count == 1 && mIPCOpen) {
Send__delete__(this);
return 0;
}
if (count == 0) {
mRefCnt.stabilizeForDeletion();
delete this;
return 0;
}
return count;
}
StorageChild::StorageChild(nsDOMStorage* aOwner)
: mStorage(aOwner)
, mIPCOpen(false)
{
}
StorageChild::StorageChild(nsDOMStorage* aOwner, StorageChild& aOther)
: DOMStorageBase(aOther)
, mStorage(aOwner)
, mIPCOpen(false)
{
}
void
StorageChild::AddIPDLReference()
{
NS_ABORT_IF_FALSE(!mIPCOpen, "Attempting to retain multiple IPDL references");
mIPCOpen = true;
AddRef();
}
void
StorageChild::ReleaseIPDLReference()
{
NS_ABORT_IF_FALSE(mIPCOpen, "Attempting to release non-existent IPDL reference");
mIPCOpen = false;
Release();
}
bool
StorageChild::CacheStoragePermissions()
{
nsDOMStorage* storage = static_cast<nsDOMStorage*>(mStorage.get());
return storage->CacheStoragePermissions();
}
void
StorageChild::InitRemote()
{
ContentChild* child = ContentChild::GetSingleton();
AddIPDLReference();
child->SendPStorageConstructor(this, null_t());
SendInit(mUseDB, mSessionOnly, mInPrivateBrowsing, mScopeDBKey,
mQuotaDBKey, mStorageType);
}
void
StorageChild::InitAsSessionStorage(nsIPrincipal* aPrincipal, bool aPrivate)
{
DOMStorageBase::InitAsSessionStorage(aPrincipal, aPrivate);
InitRemote();
}
void
StorageChild::InitAsLocalStorage(nsIPrincipal* aPrincipal, bool aPrivate)
{
DOMStorageBase::InitAsLocalStorage(aPrincipal, aPrivate);
InitRemote();
}
nsTArray<nsString>*
StorageChild::GetKeys(bool aCallerSecure)
{
InfallibleTArray<nsString> remoteKeys;
SendGetKeys(aCallerSecure, &remoteKeys);
nsTArray<nsString>* keys = new nsTArray<nsString>;
*keys = remoteKeys;
return keys;
}
nsresult
StorageChild::GetLength(bool aCallerSecure, uint32_t* aLength)
{
nsresult rv;
SendGetLength(aCallerSecure, mSessionOnly, aLength, &rv);
return rv;
}
nsresult
StorageChild::GetKey(bool aCallerSecure, uint32_t aIndex, nsAString& aKey)
{
nsresult rv;
nsString key;
SendGetKey(aCallerSecure, mSessionOnly, aIndex, &key, &rv);
if (NS_FAILED(rv))
return rv;
aKey = key;
return NS_OK;
}
// Unlike other cross-process forwarding methods, GetValue needs to replicate
// the following behaviour of DOMStorageImpl::GetValue:
//
// - if a security error occurs, or the item isn't found, return null without
// propogating the error.
//
// If DOMStorageImpl::GetValue ever changes its behaviour, this should be kept
// in sync.
nsIDOMStorageItem*
StorageChild::GetValue(bool aCallerSecure, const nsAString& aKey, nsresult* rv)
{
PROFILER_LABEL("StorageChild", "GetValue");
nsresult rv2 = *rv = NS_OK;
StorageItem storageItem;
SendGetValue(aCallerSecure, mSessionOnly, nsString(aKey), &storageItem, &rv2);
if (rv2 == NS_ERROR_DOM_SECURITY_ERR || rv2 == NS_ERROR_DOM_NOT_FOUND_ERR)
return nullptr;
*rv = rv2;
if (NS_FAILED(*rv) || storageItem.type() == StorageItem::Tnull_t)
return nullptr;
const ItemData& data = storageItem.get_ItemData();
nsIDOMStorageItem* item = new nsDOMStorageItem(this, aKey, data.value(),
data.secure());
return item;
}
nsresult
StorageChild::SetValue(bool aCallerSecure, const nsAString& aKey,
const nsAString& aData, nsAString& aOldData)
{
nsresult rv;
nsString oldData;
SendSetValue(aCallerSecure, mSessionOnly, nsString(aKey), nsString(aData),
&oldData, &rv);
if (NS_FAILED(rv))
return rv;
aOldData = oldData;
return NS_OK;
}
nsresult
StorageChild::RemoveValue(bool aCallerSecure, const nsAString& aKey,
nsAString& aOldData)
{
nsresult rv;
nsString oldData;
SendRemoveValue(aCallerSecure, mSessionOnly, nsString(aKey), &oldData, &rv);
if (NS_FAILED(rv))
return rv;
aOldData = oldData;
return NS_OK;
}
nsresult
StorageChild::Clear(bool aCallerSecure, int32_t* aOldCount)
{
nsresult rv;
int32_t oldCount;
SendClear(aCallerSecure, mSessionOnly, &oldCount, &rv);
if (NS_FAILED(rv))
return rv;
*aOldCount = oldCount;
return NS_OK;
}
nsresult
StorageChild::GetDBValue(const nsAString& aKey, nsAString& aValue,
bool* aSecure)
{
nsresult rv;
nsString value;
SendGetDBValue(nsString(aKey), &value, aSecure, &rv);
aValue = value;
return rv;
}
nsresult
StorageChild::SetDBValue(const nsAString& aKey,
const nsAString& aValue,
bool aSecure)
{
nsresult rv;
SendSetDBValue(nsString(aKey), nsString(aValue), aSecure, &rv);
return rv;
}
nsresult
StorageChild::SetSecure(const nsAString& aKey, bool aSecure)
{
nsresult rv;
SendSetSecure(nsString(aKey), aSecure, &rv);
return rv;
}
nsresult
StorageChild::CloneFrom(bool aCallerSecure, DOMStorageBase* aThat)
{
StorageChild* other = static_cast<StorageChild*>(aThat);
ContentChild* child = ContentChild::GetSingleton();
StorageClone clone(nullptr, other, aCallerSecure);
AddIPDLReference();
child->SendPStorageConstructor(this, clone);
SendInit(mUseDB, mSessionOnly, mInPrivateBrowsing,
mScopeDBKey, mQuotaDBKey, mStorageType);
return NS_OK;
}
NS_IMETHODIMP
StorageChild::PrivateModeChanged(bool enabled)
{
mInPrivateBrowsing = enabled;
SendUpdatePrivateState(enabled);
return NS_OK;
}
}
}

Просмотреть файл

@ -1,71 +0,0 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_StorageChild_h
#define mozilla_dom_StorageChild_h
#include "mozilla/dom/PStorageChild.h"
#include "nsDOMStorage.h"
#include "nsCycleCollectionParticipant.h"
namespace mozilla {
namespace dom {
class StorageChild : public PStorageChild
, public DOMStorageBase
, public nsSupportsWeakReference
{
public:
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(StorageChild, nsIPrivacyTransitionObserver)
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_NSIPRIVACYTRANSITIONOBSERVER
StorageChild(nsDOMStorage* aOwner);
StorageChild(nsDOMStorage* aOwner, StorageChild& aOther);
virtual void InitAsSessionStorage(nsIPrincipal* aPrincipal, bool aPrivate);
virtual void InitAsLocalStorage(nsIPrincipal* aPrincipal, bool aPrivate);
virtual bool CacheStoragePermissions();
virtual nsTArray<nsString>* GetKeys(bool aCallerSecure);
virtual nsresult GetLength(bool aCallerSecure, uint32_t* aLength);
virtual nsresult GetKey(bool aCallerSecure, uint32_t aIndex, nsAString& aKey);
virtual nsIDOMStorageItem* GetValue(bool aCallerSecure, const nsAString& aKey,
nsresult* rv);
virtual nsresult SetValue(bool aCallerSecure, const nsAString& aKey,
const nsAString& aData, nsAString& aOldValue);
virtual nsresult RemoveValue(bool aCallerSecure, const nsAString& aKey,
nsAString& aOldValue);
virtual nsresult Clear(bool aCallerSecure, int32_t* aOldCount);
virtual nsresult GetDBValue(const nsAString& aKey,
nsAString& aValue,
bool* aSecure);
virtual nsresult SetDBValue(const nsAString& aKey,
const nsAString& aValue,
bool aSecure);
virtual nsresult SetSecure(const nsAString& aKey, bool aSecure);
virtual nsresult CloneFrom(bool aCallerSecure, DOMStorageBase* aThat);
void AddIPDLReference();
void ReleaseIPDLReference();
private:
void InitRemote();
// Unimplemented
StorageChild(const StorageChild&);
nsCOMPtr<nsIDOMStorageObsolete> mStorage;
bool mIPCOpen;
};
}
}
#endif

Просмотреть файл

@ -1,157 +0,0 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "StorageParent.h"
#include "mozilla/dom/PContentParent.h"
#include "mozilla/unused.h"
#include "nsDOMString.h"
using mozilla::unused;
namespace mozilla {
namespace dom {
StorageParent::StorageParent(const StorageConstructData& aData)
{
if (aData.type() == StorageConstructData::Tnull_t) {
mStorage = new DOMStorageImpl(nullptr);
} else {
const StorageClone& clone = aData.get_StorageClone();
StorageParent* other = static_cast<StorageParent*>(clone.actorParent());
mStorage = new DOMStorageImpl(nullptr, *other->mStorage.get());
mStorage->CloneFrom(clone.callerSecure(), other->mStorage);
}
}
bool
StorageParent::RecvInit(const bool& aUseDB,
const bool& aSessionOnly,
const bool& aPrivate,
const nsCString& aScopeDBKey,
const nsCString& aQuotaDBKey,
const uint32_t& aStorageType)
{
mStorage->InitFromChild(aUseDB, aSessionOnly, aPrivate,
aScopeDBKey, aQuotaDBKey,
aStorageType);
return true;
}
bool
StorageParent::RecvUpdatePrivateState(const bool& aEnabled)
{
mStorage->PrivateModeChanged(aEnabled);
return true;
}
bool
StorageParent::RecvGetKeys(const bool& aCallerSecure, InfallibleTArray<nsString>* aKeys)
{
// Callers are responsible for deallocating the array returned by mStorage->GetKeys
nsAutoPtr<nsTArray<nsString> > keys(mStorage->GetKeys(aCallerSecure));
aKeys->SwapElements(*keys);
return true;
}
bool
StorageParent::RecvGetLength(const bool& aCallerSecure, const bool& aSessionOnly,
uint32_t* aLength, nsresult* rv)
{
mStorage->SetSessionOnly(aSessionOnly);
*rv = mStorage->GetLength(aCallerSecure, aLength);
return true;
}
bool
StorageParent::RecvGetKey(const bool& aCallerSecure, const bool& aSessionOnly,
const uint32_t& aIndex, nsString* aKey, nsresult* rv)
{
mStorage->SetSessionOnly(aSessionOnly);
*rv = mStorage->GetKey(aCallerSecure, aIndex, *aKey);
return true;
}
bool
StorageParent::RecvGetValue(const bool& aCallerSecure, const bool& aSessionOnly,
const nsString& aKey, StorageItem* aItem,
nsresult* rv)
{
mStorage->SetSessionOnly(aSessionOnly);
// We need to ensure that a proper null representation is sent to the child
// if no item is found or an error occurs.
*rv = NS_OK;
nsCOMPtr<nsIDOMStorageItem> item = mStorage->GetValue(aCallerSecure, aKey, rv);
if (NS_FAILED(*rv) || !item) {
*aItem = null_t();
return true;
}
ItemData data(EmptyString(), false);
nsDOMStorageItem* internalItem = static_cast<nsDOMStorageItem*>(item.get());
data.value() = internalItem->GetValueInternal();
if (aCallerSecure)
data.secure() = internalItem->IsSecure();
*aItem = data;
return true;
}
bool
StorageParent::RecvSetValue(const bool& aCallerSecure, const bool& aSessionOnly,
const nsString& aKey, const nsString& aData,
nsString* aOldValue, nsresult* rv)
{
mStorage->SetSessionOnly(aSessionOnly);
*rv = mStorage->SetValue(aCallerSecure, aKey, aData, *aOldValue);
return true;
}
bool
StorageParent::RecvRemoveValue(const bool& aCallerSecure, const bool& aSessionOnly,
const nsString& aKey, nsString* aOldValue,
nsresult* rv)
{
mStorage->SetSessionOnly(aSessionOnly);
*rv = mStorage->RemoveValue(aCallerSecure, aKey, *aOldValue);
return true;
}
bool
StorageParent::RecvClear(const bool& aCallerSecure, const bool& aSessionOnly,
int32_t* aOldCount, nsresult* rv)
{
mStorage->SetSessionOnly(aSessionOnly);
*rv = mStorage->Clear(aCallerSecure, aOldCount);
return true;
}
bool
StorageParent::RecvGetDBValue(const nsString& aKey, nsString* aValue,
bool* aSecure, nsresult* rv)
{
*rv = mStorage->GetDBValue(aKey, *aValue, aSecure);
return true;
}
bool
StorageParent::RecvSetDBValue(const nsString& aKey, const nsString& aValue,
const bool& aSecure, nsresult* rv)
{
*rv = mStorage->SetDBValue(aKey, aValue, aSecure);
return true;
}
bool
StorageParent::RecvSetSecure(const nsString& aKey, const bool& aSecure,
nsresult* rv)
{
*rv = mStorage->SetSecure(aKey, aSecure);
return true;
}
}
}

Просмотреть файл

@ -1,60 +0,0 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_StorageParent_h
#define mozilla_dom_StorageParent_h
#include "mozilla/dom/PStorageParent.h"
#include "nsDOMStorage.h"
namespace mozilla {
namespace dom {
class StorageConstructData;
class StorageParent : public PStorageParent
{
public:
StorageParent(const StorageConstructData& aData);
private:
bool RecvGetKeys(const bool& aCallerSecure, InfallibleTArray<nsString>* aKeys);
bool RecvGetLength(const bool& aCallerSecure, const bool& aSessionOnly,
uint32_t* aLength, nsresult* rv);
bool RecvGetKey(const bool& aCallerSecure, const bool& aSessionOnly,
const uint32_t& aIndex,nsString* aKey, nsresult* rv);
bool RecvGetValue(const bool& aCallerSecure, const bool& aSessionOnly,
const nsString& aKey, StorageItem* aItem, nsresult* rv);
bool RecvSetValue(const bool& aCallerSecure, const bool& aSessionOnly,
const nsString& aKey, const nsString& aData,
nsString* aOldValue, nsresult* rv);
bool RecvRemoveValue(const bool& aCallerSecure, const bool& aSessionOnly,
const nsString& aKey, nsString* aOldData, nsresult* rv);
bool RecvClear(const bool& aCallerSecure, const bool& aSessionOnly,
int32_t* aOldCount, nsresult* rv);
bool RecvGetDBValue(const nsString& aKey, nsString* aValue, bool* aSecure,
nsresult* rv);
bool RecvSetDBValue(const nsString& aKey, const nsString& aValue,
const bool& aSecure, nsresult* rv);
bool RecvSetSecure(const nsString& aKey, const bool& aSecure, nsresult* rv);
bool RecvInit(const bool& aUseDB,
const bool& aSessionOnly,
const bool& aPrivate,
const nsCString& aScopeDBKey,
const nsCString& aQuotaDBKey,
const uint32_t& aStorageType);
bool RecvUpdatePrivateState(const bool& aEnabled);
nsRefPtr<DOMStorageImpl> mStorage;
};
}
}
#endif

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,486 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsDOMStorage_h___
#define nsDOMStorage_h___
#include "nscore.h"
#include "nsAutoPtr.h"
#include "nsIDOMStorageObsolete.h"
#include "nsIDOMStorage.h"
#include "nsIDOMStorageItem.h"
#include "nsIPermissionManager.h"
#include "nsIPrivacyTransitionObserver.h"
#include "nsInterfaceHashtable.h"
#include "nsVoidArray.h"
#include "nsTArray.h"
#include "nsPIDOMStorage.h"
#include "nsIDOMToString.h"
#include "nsDOMEvent.h"
#include "nsIDOMStorageEvent.h"
#include "nsIDOMStorageManager.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIObserver.h"
#include "nsITimer.h"
#include "nsWeakReference.h"
#include "nsIInterfaceRequestor.h"
#include "mozilla/Attributes.h"
#include "nsDOMStorageDBWrapper.h"
class nsDOMStorage;
class nsIDOMStorage;
class nsDOMStorageItem;
class nsDOMStoragePersistentDB;
namespace mozilla {
namespace dom {
class StorageParent;
}
}
using mozilla::dom::StorageParent;
class DOMStorageImpl;
class nsDOMStorageEntry : public nsPtrHashKey<const void>
{
public:
nsDOMStorageEntry(KeyTypePointer aStr);
nsDOMStorageEntry(const nsDOMStorageEntry& aToCopy);
~nsDOMStorageEntry();
// weak reference so that it can be deleted when no longer used
DOMStorageImpl* mStorage;
};
class nsSessionStorageEntry : public nsStringHashKey
{
public:
nsSessionStorageEntry(KeyTypePointer aStr);
nsSessionStorageEntry(const nsSessionStorageEntry& aToCopy);
~nsSessionStorageEntry();
nsRefPtr<nsDOMStorageItem> mItem;
};
class nsDOMStorageManager MOZ_FINAL : public nsIDOMStorageManager
, public nsIObserver
, public nsSupportsWeakReference
{
public:
// nsISupports
NS_DECL_ISUPPORTS
// nsIDOMStorageManager
NS_DECL_NSIDOMSTORAGEMANAGER
// nsIObserver
NS_DECL_NSIOBSERVER
nsDOMStorageManager();
void AddToStoragesHash(DOMStorageImpl* aStorage);
void RemoveFromStoragesHash(DOMStorageImpl* aStorage);
nsresult ClearAllStorages();
static nsresult Initialize();
static nsDOMStorageManager* GetInstance();
static void Shutdown();
static void ShutdownDB();
/**
* Checks whether there is any data waiting to be flushed from a temp table.
*/
bool UnflushedDataExists();
static nsDOMStorageManager* gStorageManager;
protected:
nsTHashtable<nsDOMStorageEntry> mStorages;
};
class DOMStorageBase : public nsIPrivacyTransitionObserver
{
public:
DOMStorageBase();
DOMStorageBase(DOMStorageBase&);
virtual void InitAsSessionStorage(nsIPrincipal* aPrincipal, bool aPrivate);
virtual void InitAsLocalStorage(nsIPrincipal* aPrincipal, bool aPrivate);
virtual nsTArray<nsString>* GetKeys(bool aCallerSecure) = 0;
virtual nsresult GetLength(bool aCallerSecure, uint32_t* aLength) = 0;
virtual nsresult GetKey(bool aCallerSecure, uint32_t aIndex, nsAString& aKey) = 0;
virtual nsIDOMStorageItem* GetValue(bool aCallerSecure, const nsAString& aKey,
nsresult* rv) = 0;
virtual nsresult SetValue(bool aCallerSecure, const nsAString& aKey,
const nsAString& aData, nsAString& aOldValue) = 0;
virtual nsresult RemoveValue(bool aCallerSecure, const nsAString& aKey,
nsAString& aOldValue) = 0;
virtual nsresult Clear(bool aCallerSecure, int32_t* aOldCount) = 0;
// Call nsDOMStorage::CanUseStorage with |this|
bool CanUseStorage();
// If true, the contents of the storage should be stored in the
// database, otherwise this storage should act like a session
// storage.
// This call relies on mSessionOnly, and should only be used
// after a CacheStoragePermissions() call. See the comments
// for mSessionOnly below.
bool UseDB() {
return mUseDB;
}
bool IsPrivate() {
return mInPrivateBrowsing;
}
// retrieve the value and secure state corresponding to a key out of storage.
virtual nsresult
GetDBValue(const nsAString& aKey,
nsAString& aValue,
bool* aSecure) = 0;
// set the value corresponding to a key in the storage. If
// aSecure is false, then attempts to modify a secure value
// throw NS_ERROR_DOM_INVALID_ACCESS_ERR
virtual nsresult
SetDBValue(const nsAString& aKey,
const nsAString& aValue,
bool aSecure) = 0;
// set the value corresponding to a key as secure.
virtual nsresult
SetSecure(const nsAString& aKey, bool aSecure) = 0;
virtual nsresult
CloneFrom(bool aCallerSecure, DOMStorageBase* aThat) = 0;
// e.g. "moc.rab.oof.:" or "moc.rab.oof.:http:80" depending
// on association with a domain (globalStorage) or
// an origin (localStorage).
nsCString& GetScopeDBKey() {return mScopeDBKey;}
// e.g. "moc.rab.%" - reversed eTLD+1 subpart of the domain.
nsCString& GetQuotaDBKey()
{
return mQuotaDBKey;
}
virtual bool CacheStoragePermissions() = 0;
protected:
friend class nsDOMStorageManager;
friend class nsDOMStorage;
nsPIDOMStorage::nsDOMStorageType mStorageType;
// true if the storage database should be used for values
bool mUseDB;
// true if the preferences indicates that this storage should be
// session only. This member is updated by
// CacheStoragePermissions(), using the current principal.
// CacheStoragePermissions() must be called at each entry point to
// make sure this stays up to date.
bool mSessionOnly;
// keys are used for database queries.
// see comments of the getters bellow.
nsCString mScopeDBKey;
nsCString mQuotaDBKey;
bool mInPrivateBrowsing;
};
class DOMStorageImpl MOZ_FINAL : public DOMStorageBase
, public nsSupportsWeakReference
{
public:
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DOMStorageImpl, nsIPrivacyTransitionObserver)
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_NSIPRIVACYTRANSITIONOBSERVER
DOMStorageImpl(nsDOMStorage*);
DOMStorageImpl(nsDOMStorage*, DOMStorageImpl&);
~DOMStorageImpl();
bool SessionOnly() {
return mSessionOnly;
}
virtual nsTArray<nsString>* GetKeys(bool aCallerSecure);
virtual nsresult GetLength(bool aCallerSecure, uint32_t* aLength);
virtual nsresult GetKey(bool aCallerSecure, uint32_t aIndex, nsAString& aKey);
virtual nsIDOMStorageItem* GetValue(bool aCallerSecure, const nsAString& aKey,
nsresult* rv);
virtual nsresult SetValue(bool aCallerSecure, const nsAString& aKey,
const nsAString& aData, nsAString& aOldValue);
virtual nsresult RemoveValue(bool aCallerSecure, const nsAString& aKey,
nsAString& aOldValue);
virtual nsresult Clear(bool aCallerSecure, int32_t* aOldCount);
// cache the keys from the database for faster lookup
nsresult CacheKeysFromDB();
uint64_t CachedVersion() { return mItemsCachedVersion; }
void SetCachedVersion(uint64_t version) { mItemsCachedVersion = version; }
// retrieve the value and secure state corresponding to a key out of storage
// that has been cached in mItems hash table.
nsresult
GetCachedValue(const nsAString& aKey,
nsAString& aValue,
bool* aSecure);
// retrieve the value and secure state corresponding to a key out of storage.
virtual nsresult
GetDBValue(const nsAString& aKey,
nsAString& aValue,
bool* aSecure);
// set the value corresponding to a key in the storage. If
// aSecure is false, then attempts to modify a secure value
// throw NS_ERROR_DOM_INVALID_ACCESS_ERR
virtual nsresult
SetDBValue(const nsAString& aKey,
const nsAString& aValue,
bool aSecure);
// set the value corresponding to a key as secure.
virtual nsresult
SetSecure(const nsAString& aKey, bool aSecure);
// clear all values from the store
void ClearAll();
virtual nsresult
CloneFrom(bool aCallerSecure, DOMStorageBase* aThat);
virtual bool CacheStoragePermissions();
private:
static nsDOMStorageDBWrapper* gStorageDB;
friend class nsDOMStorageManager;
friend class nsDOMStoragePersistentDB;
friend class StorageParent;
void Init(nsDOMStorage*);
// Cross-process storage implementations never have InitAs(Session|Local|Global)Storage
// called, so the appropriate initialization needs to happen from the child.
void InitFromChild(bool aUseDB, bool aSessionOnly,
bool aPrivate,
const nsACString& aScopeDBKey,
const nsACString& aQuotaDBKey,
uint32_t aStorageType);
void SetSessionOnly(bool aSessionOnly);
static nsresult InitDB();
// 0 initially or a positive data version number assigned by gStorageDB
// after keys have been cached from the database
uint64_t mItemsCachedVersion;
// the key->value item pairs
nsTHashtable<nsSessionStorageEntry> mItems;
// Weak reference to the owning storage instance
nsDOMStorage* mOwner;
};
class nsDOMStorage2;
class nsDOMStorage : public nsIDOMStorageObsolete,
public nsPIDOMStorage,
public nsIInterfaceRequestor
{
public:
nsDOMStorage();
nsDOMStorage(nsDOMStorage& aThat);
virtual ~nsDOMStorage();
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMStorage, nsIDOMStorageObsolete)
NS_DECL_NSIDOMSTORAGEOBSOLETE
NS_DECL_NSIINTERFACEREQUESTOR
// Helpers for implementing nsIDOMStorage
nsresult GetItem(const nsAString& key, nsAString& aData);
nsresult Clear();
// nsPIDOMStorage
virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
bool aPrivate);
virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
bool aPrivate);
virtual already_AddRefed<nsIDOMStorage> Clone();
virtual already_AddRefed<nsIDOMStorage> Fork(const nsSubstring &aDocumentURI);
virtual bool IsForkOf(nsIDOMStorage* aThat);
virtual nsTArray<nsString> *GetKeys();
virtual nsIPrincipal* Principal();
virtual bool CanAccess(nsIPrincipal *aPrincipal);
virtual nsDOMStorageType StorageType();
virtual bool IsPrivate() {
return mStorageImpl && mStorageImpl->IsPrivate();
}
// Check whether storage may be used by the caller, and whether it
// is session only. Returns true if storage may be used.
static bool
CanUseStorage(DOMStorageBase* aStorage = nullptr);
// Check whether storage may be used. Updates mSessionOnly based on
// the result of CanUseStorage.
bool
CacheStoragePermissions();
nsIDOMStorageItem* GetNamedItem(const nsAString& aKey, nsresult* aResult);
nsresult SetSecure(const nsAString& aKey, bool aSecure)
{
return mStorageImpl->SetSecure(aKey, aSecure);
}
nsresult CloneFrom(nsDOMStorage* aThat);
protected:
friend class nsDOMStorage2;
friend class nsDOMStoragePersistentDB;
nsRefPtr<DOMStorageBase> mStorageImpl;
bool CanAccessSystem(nsIPrincipal *aPrincipal);
// document URI of the document this storage is bound to
nsString mDocumentURI;
// true if this storage was initialized as a localStorage object. localStorage
// objects are scoped to scheme/host/port in the database, while globalStorage
// objects are scoped just to host. this flag also tells the manager to map
// this storage also in mLocalStorages hash table.
nsDOMStorageType mStorageType;
friend class nsIDOMStorage2;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsDOMStorage2* mEventBroadcaster;
};
class nsDOMStorage2 MOZ_FINAL : public nsIDOMStorage,
public nsPIDOMStorage,
public nsIInterfaceRequestor
{
public:
// nsISupports
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMStorage2, nsIDOMStorage)
nsDOMStorage2(nsDOMStorage2& aThat);
nsDOMStorage2();
NS_DECL_NSIDOMSTORAGE
NS_DECL_NSIINTERFACEREQUESTOR
// nsPIDOMStorage
virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
bool aPrivate);
virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI,
bool aPrivate);
virtual already_AddRefed<nsIDOMStorage> Clone();
virtual already_AddRefed<nsIDOMStorage> Fork(const nsSubstring &aDocumentURI);
virtual bool IsForkOf(nsIDOMStorage* aThat);
virtual nsTArray<nsString> *GetKeys();
virtual nsIPrincipal* Principal();
virtual bool CanAccess(nsIPrincipal *aPrincipal);
virtual nsDOMStorageType StorageType();
virtual bool IsPrivate();
void BroadcastChangeNotification(const nsSubstring &aKey,
const nsSubstring &aOldValue,
const nsSubstring &aNewValue);
void InitAsSessionStorageFork(nsIPrincipal *aPrincipal,
const nsSubstring &aDocumentURI,
nsDOMStorage* aStorage);
private:
// storages bound to an origin hold the principal to
// make security checks against it
nsCOMPtr<nsIPrincipal> mPrincipal;
// Needed for the storage event, this is address of the document this storage
// is bound to
nsString mDocumentURI;
nsRefPtr<nsDOMStorage> mStorage;
};
class nsDOMStorageItem : public nsIDOMStorageItem,
public nsIDOMToString
{
public:
nsDOMStorageItem(DOMStorageBase* aStorage,
const nsAString& aKey,
const nsAString& aValue,
bool aSecure);
virtual ~nsDOMStorageItem();
// nsISupports
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMStorageItem, nsIDOMStorageItem)
// nsIDOMStorageObsolete
NS_DECL_NSIDOMSTORAGEITEM
// nsIDOMToString
NS_DECL_NSIDOMTOSTRING
bool IsSecure()
{
return mSecure;
}
void SetSecureInternal(bool aSecure)
{
mSecure = aSecure;
}
const nsAString& GetValueInternal()
{
return mValue;
}
const void SetValueInternal(const nsAString& aValue)
{
mValue = aValue;
}
void ClearValue()
{
mValue.Truncate();
}
protected:
// true if this value is for secure sites only
bool mSecure;
// key for the item
nsString mKey;
// value of the item
nsString mValue;
// If this item came from the db, mStorage points to the storage
// object where this item came from.
nsRefPtr<DOMStorageBase> mStorage;
};
nsresult
NS_NewDOMStorage2(nsISupports* aOuter, REFNSIID aIID, void** aResult);
#endif /* nsDOMStorage_h___ */

Просмотреть файл

@ -1,86 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsDOMStorageBaseDB.h"
#include "nsDOMStorage.h"
#include "mozilla/Preferences.h"
// Only allow relatively small amounts of data since performance of
// the synchronous IO is very bad.
#define DEFAULT_QUOTA_LIMIT (5 * 1024)
uint64_t nsDOMStorageBaseDB::sGlobalVersion = 1;
int32_t nsDOMStorageBaseDB::gQuotaLimit = DEFAULT_QUOTA_LIMIT * 1024;
using namespace mozilla;
/* static */
void
nsDOMStorageBaseDB::Init()
{
Preferences::AddIntVarCache(&gQuotaLimit, "dom.storage.default_quota",
DEFAULT_QUOTA_LIMIT);
}
nsDOMStorageBaseDB::nsDOMStorageBaseDB()
{
mScopesVersion.Init(8);
}
// public
void
nsDOMStorageBaseDB::MarkScopeCached(DOMStorageImpl* aStorage)
{
aStorage->SetCachedVersion(CachedScopeVersion(aStorage));
}
bool
nsDOMStorageBaseDB::IsScopeDirty(DOMStorageImpl* aStorage)
{
return !aStorage->CachedVersion() ||
(aStorage->CachedVersion() != CachedScopeVersion(aStorage));
}
// protected
// static
uint64_t
nsDOMStorageBaseDB::NextGlobalVersion()
{
sGlobalVersion++;
if (sGlobalVersion == 0) // Control overlap, never return 0
sGlobalVersion = 1;
return sGlobalVersion;
}
uint64_t
nsDOMStorageBaseDB::CachedScopeVersion(DOMStorageImpl* aStorage)
{
uint64_t currentVersion;
if (mScopesVersion.Get(aStorage->GetScopeDBKey(), &currentVersion))
return currentVersion;
mScopesVersion.Put(aStorage->GetScopeDBKey(), sGlobalVersion);
return sGlobalVersion;
}
void
nsDOMStorageBaseDB::MarkScopeDirty(DOMStorageImpl* aStorage)
{
uint64_t nextVersion = NextGlobalVersion();
mScopesVersion.Put(aStorage->GetScopeDBKey(), nextVersion);
// We may do this because the storage updates its cache along with
// updating the database.
aStorage->SetCachedVersion(nextVersion);
}
void
nsDOMStorageBaseDB::MarkAllScopesDirty()
{
mScopesVersion.Clear();
NextGlobalVersion();
}

Просмотреть файл

@ -1,58 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsDOMStorageBaseDB_h___
#define nsDOMStorageBaseDB_h___
#include "nscore.h"
#include "nsDataHashtable.h"
class DOMStorageImpl;
class nsDOMStorageBaseDB
{
public:
static void Init();
nsDOMStorageBaseDB();
virtual ~nsDOMStorageBaseDB() {}
/**
* Marks the storage as "cached" after the DOMStorageImpl object has loaded
* all items to its memory copy of the entries - IsScopeDirty returns false
* after call of this method for this storage.
*
* When a key is changed or deleted in the storage, the storage scope is
* marked as "dirty" again and makes the DOMStorageImpl object recache its
* keys on next access, because IsScopeDirty returns true again.
*/
void MarkScopeCached(DOMStorageImpl* aStorage);
/**
* Test whether the storage for the scope (i.e. origin or host) has been
* changed since the last MarkScopeCached call.
*/
bool IsScopeDirty(DOMStorageImpl* aStorage);
int32_t GetQuota() {
return gQuotaLimit * 1024;
}
protected:
nsDataHashtable<nsCStringHashKey, uint64_t> mScopesVersion;
static uint64_t NextGlobalVersion();
uint64_t CachedScopeVersion(DOMStorageImpl* aStorage);
void MarkScopeDirty(DOMStorageImpl* aStorage);
void MarkAllScopesDirty();
private:
static uint64_t sGlobalVersion;
static int32_t gQuotaLimit;
};
#endif /* nsDOMStorageDB_h___ */

Просмотреть файл

@ -1,397 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsCOMPtr.h"
#include "nsError.h"
#include "nsDOMStorage.h"
#include "nsDOMStorageDBWrapper.h"
#include "nsIFile.h"
#include "nsIURL.h"
#include "nsIVariant.h"
#include "nsIEffectiveTLDService.h"
#include "nsIScriptSecurityManager.h"
#include "nsAppDirectoryServiceDefs.h"
#include "mozStorageCID.h"
#include "mozStorageHelper.h"
#include "mozIStorageService.h"
#include "mozIStorageValueArray.h"
#include "mozIStorageFunction.h"
#include "nsPrintfCString.h"
#include "nsNetUtil.h"
#include "nsIPrincipal.h"
void ReverseString(const nsCSubstring& source, nsCSubstring& result)
{
nsACString::const_iterator sourceBegin, sourceEnd;
source.BeginReading(sourceBegin);
source.EndReading(sourceEnd);
result.SetLength(source.Length());
nsACString::iterator destEnd;
result.EndWriting(destEnd);
while (sourceBegin != sourceEnd) {
*(--destEnd) = *sourceBegin;
++sourceBegin;
}
}
nsDOMStorageDBWrapper::nsDOMStorageDBWrapper()
{
}
nsDOMStorageDBWrapper::~nsDOMStorageDBWrapper()
{
}
void
nsDOMStorageDBWrapper::Close()
{
mPersistentDB.Close();
}
nsresult
nsDOMStorageDBWrapper::Init()
{
nsresult rv;
rv = mPersistentDB.Init(NS_LITERAL_STRING("webappsstore.sqlite"));
NS_ENSURE_SUCCESS(rv, rv);
rv = mSessionOnlyDB.Init(&mPersistentDB);
NS_ENSURE_SUCCESS(rv, rv);
rv = mPrivateBrowsingDB.Init();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
nsDOMStorageDBWrapper::FlushAndEvictFromCache(bool aIsShuttingDown)
{
nsresult rv = mPersistentDB.FlushAndEvictFromCache(aIsShuttingDown);
// Nothing in the cache? Then no need for a timer.
if (!mPersistentDB.IsFlushTimerNeeded()) {
StopCacheFlushTimer();
}
return rv;
}
#define IMPL_FORWARDER_GUTS(_return, _code) \
PR_BEGIN_MACRO \
if (aStorage->IsPrivate()) \
_return mPrivateBrowsingDB._code; \
if (aStorage->SessionOnly()) \
_return mSessionOnlyDB._code; \
_return mPersistentDB._code; \
PR_END_MACRO
#define IMPL_FORWARDER(_code) \
IMPL_FORWARDER_GUTS(return, _code)
#define IMPL_VOID_FORWARDER(_code) \
IMPL_FORWARDER_GUTS((void), _code)
nsresult
nsDOMStorageDBWrapper::GetAllKeys(DOMStorageImpl* aStorage,
nsTHashtable<nsSessionStorageEntry>* aKeys)
{
IMPL_FORWARDER(GetAllKeys(aStorage, aKeys));
}
nsresult
nsDOMStorageDBWrapper::GetKeyValue(DOMStorageImpl* aStorage,
const nsAString& aKey,
nsAString& aValue,
bool* aSecure)
{
IMPL_FORWARDER(GetKeyValue(aStorage, aKey, aValue, aSecure));
}
nsresult
nsDOMStorageDBWrapper::SetKey(DOMStorageImpl* aStorage,
const nsAString& aKey,
const nsAString& aValue,
bool aSecure)
{
IMPL_FORWARDER(SetKey(aStorage, aKey, aValue, aSecure));
}
nsresult
nsDOMStorageDBWrapper::SetSecure(DOMStorageImpl* aStorage,
const nsAString& aKey,
const bool aSecure)
{
IMPL_FORWARDER(SetSecure(aStorage, aKey, aSecure));
}
nsresult
nsDOMStorageDBWrapper::RemoveKey(DOMStorageImpl* aStorage,
const nsAString& aKey)
{
IMPL_FORWARDER(RemoveKey(aStorage, aKey));
}
nsresult
nsDOMStorageDBWrapper::ClearStorage(DOMStorageImpl* aStorage)
{
IMPL_FORWARDER(ClearStorage(aStorage));
}
void
nsDOMStorageDBWrapper::MarkScopeCached(DOMStorageImpl* aStorage)
{
IMPL_VOID_FORWARDER(MarkScopeCached(aStorage));
}
bool
nsDOMStorageDBWrapper::IsScopeDirty(DOMStorageImpl* aStorage)
{
IMPL_FORWARDER(IsScopeDirty(aStorage));
}
nsresult
nsDOMStorageDBWrapper::DropSessionOnlyStoragesForHost(const nsACString& aHostName)
{
return mSessionOnlyDB.RemoveOwner(aHostName);
}
nsresult
nsDOMStorageDBWrapper::DropPrivateBrowsingStorages()
{
return mPrivateBrowsingDB.RemoveAll();
}
nsresult
nsDOMStorageDBWrapper::RemoveOwner(const nsACString& aOwner)
{
nsresult rv;
rv = mPrivateBrowsingDB.RemoveOwner(aOwner);
NS_ENSURE_SUCCESS(rv, rv);
rv = mSessionOnlyDB.RemoveOwner(aOwner);
NS_ENSURE_SUCCESS(rv, rv);
rv = mPersistentDB.RemoveOwner(aOwner);
NS_ENSURE_SUCCESS(rv, rv);
return rv;
}
nsresult
nsDOMStorageDBWrapper::RemoveAll()
{
nsresult rv;
rv = mPrivateBrowsingDB.RemoveAll();
NS_ENSURE_SUCCESS(rv, rv);
rv = mSessionOnlyDB.RemoveAll();
NS_ENSURE_SUCCESS(rv, rv);
rv = mPersistentDB.RemoveAll();
NS_ENSURE_SUCCESS(rv, rv);
return rv;
}
nsresult
nsDOMStorageDBWrapper::RemoveAllForApp(uint32_t aAppId, bool aOnlyBrowserElement)
{
// We only care about removing the permament storage. Temporary storage such
// as session storage or private browsing storage will not be re-used anyway
// and will be automatically deleted at some point.
return mPersistentDB.RemoveAllForApp(aAppId, aOnlyBrowserElement);
}
nsresult
nsDOMStorageDBWrapper::GetUsage(DOMStorageImpl* aStorage, int32_t *aUsage)
{
IMPL_FORWARDER(GetUsage(aStorage, aUsage));
}
nsresult
nsDOMStorageDBWrapper::GetUsage(const nsACString& aDomain,
int32_t *aUsage, bool aPrivate)
{
if (aPrivate)
return mPrivateBrowsingDB.GetUsage(aDomain, aUsage);
#if 0
// XXX Check where from all this method gets called, not sure this should
// include any potential session-only data
nsresult rv;
rv = mSessionOnlyDB.GetUsage(aDomain, aUsage);
if (NS_SUECEEDED(rv))
return rv;
#endif
return mPersistentDB.GetUsage(aDomain, aUsage);
}
nsresult
nsDOMStorageDBWrapper::CreateScopeDBKey(nsIPrincipal* aPrincipal,
nsACString& aKey)
{
nsCOMPtr<nsIURI> uri;
nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
nsAutoCString domainScope;
rv = uri->GetAsciiHost(domainScope);
NS_ENSURE_SUCCESS(rv, rv);
if (domainScope.IsEmpty()) {
// About pages have an empty host but a valid path. Since they are handled
// internally by our own redirector, we can trust them and use path as key.
// if file:/// protocol, let's make the exact directory the domain
bool isScheme = false;
if ((NS_SUCCEEDED(uri->SchemeIs("about", &isScheme)) && isScheme) ||
(NS_SUCCEEDED(uri->SchemeIs("moz-safe-about", &isScheme)) && isScheme)) {
rv = uri->GetPath(domainScope);
NS_ENSURE_SUCCESS(rv, rv);
// While the host is always canonicalized to lowercase, the path is not,
// thus need to force the casing.
ToLowerCase(domainScope);
}
else if (NS_SUCCEEDED(uri->SchemeIs("file", &isScheme)) && isScheme) {
nsCOMPtr<nsIURL> url = do_QueryInterface(uri, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = url->GetDirectory(domainScope);
NS_ENSURE_SUCCESS(rv, rv);
}
}
nsAutoCString key;
rv = CreateReversedDomain(domainScope, key);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString scheme;
rv = uri->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);
key.Append(NS_LITERAL_CSTRING(":") + scheme);
int32_t port = NS_GetRealPort(uri);
if (port != -1) {
key.Append(nsPrintfCString(":%d", port));
}
uint32_t appId;
rv = aPrincipal->GetAppId(&appId);
NS_ENSURE_SUCCESS(rv, rv);
bool isInBrowserElement;
rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
NS_ENSURE_SUCCESS(rv, rv);
if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
aKey.Assign(key);
return NS_OK;
}
aKey.Truncate();
aKey.AppendInt(appId);
aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
NS_LITERAL_CSTRING(":") + key);
return NS_OK;
}
nsresult
nsDOMStorageDBWrapper::CreateReversedDomain(const nsACString& aAsciiDomain,
nsACString& aKey)
{
if (aAsciiDomain.IsEmpty())
return NS_ERROR_NOT_AVAILABLE;
ReverseString(aAsciiDomain, aKey);
aKey.AppendLiteral(".");
return NS_OK;
}
nsresult
nsDOMStorageDBWrapper::CreateQuotaDBKey(nsIPrincipal* aPrincipal,
nsACString& aKey)
{
nsresult rv;
nsAutoCString subdomainsDBKey;
nsCOMPtr<nsIEffectiveTLDService> eTLDService(do_GetService(
NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> uri;
rv = aPrincipal->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
nsAutoCString eTLDplusOne;
rv = eTLDService->GetBaseDomain(uri, 0, eTLDplusOne);
if (NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS == rv) {
// XXX bug 357323 - what to do for localhost/file exactly?
rv = uri->GetAsciiHost(eTLDplusOne);
}
NS_ENSURE_SUCCESS(rv, rv);
CreateReversedDomain(eTLDplusOne, subdomainsDBKey);
uint32_t appId;
rv = aPrincipal->GetAppId(&appId);
NS_ENSURE_SUCCESS(rv, rv);
bool isInBrowserElement;
rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
NS_ENSURE_SUCCESS(rv, rv);
if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
aKey.Assign(subdomainsDBKey);
return NS_OK;
}
aKey.Truncate();
aKey.AppendInt(appId);
aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
NS_LITERAL_CSTRING(":") + subdomainsDBKey);
return NS_OK;
}
void
nsDOMStorageDBWrapper::EnsureCacheFlushTimer()
{
if (!mCacheFlushTimer) {
nsresult rv;
mCacheFlushTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
if (!NS_SUCCEEDED(rv)) {
mCacheFlushTimer = nullptr;
return;
}
mCacheFlushTimer->Init(nsDOMStorageManager::gStorageManager, 5000,
nsITimer::TYPE_REPEATING_SLACK);
}
}
void
nsDOMStorageDBWrapper::StopCacheFlushTimer()
{
if (mCacheFlushTimer) {
mCacheFlushTimer->Cancel();
mCacheFlushTimer = nullptr;
}
}

Просмотреть файл

@ -1,238 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsDOMStorageDB_h___
#define nsDOMStorageDB_h___
#include "nscore.h"
#include "nsTHashtable.h"
#include "nsDOMStoragePersistentDB.h"
#include "nsDOMStorageMemoryDB.h"
extern void ReverseString(const nsCSubstring& source, nsCSubstring& result);
class nsDOMStorage;
class nsSessionStorageEntry;
/**
* For the purposes of quota checking, we want to be able to efficiently
* reference data items that belong to a host or its subhosts. We do this by
* using a reversed domain name as the key for an item. For example, a
* storage for foo.bar.com would use a key of 'moc.rab.oof.".
*
* Additionally, globalStorage and localStorage items must be distinguished.
* globalStorage items are scoped to the host, and localStorage are items are
* scoped to the scheme/host/port. To scope localStorage data, its port and
* scheme are appended to its key. http://foo.bar.com is stored as
* moc.rab.foo.:http:80.
*
* So the following queries can be used, for http://foo.bar.com:
*
* All data owned by globalStorage["foo.bar.com"] -> SELECT * WHERE Domain =
* "moc.rab.foo.:"
*
* All data owned by localStorage -> SELECT * WHERE Domain =
* "moc.rab.foo.:http:80"
*
* All data owned by foo.bar.com, in any storage ->
* SELECT * WHERE Domain GLOB "moc.rab.foo.:*"
*
* All data owned by foo.bar.com or any subdomain, in any storage ->
* SELECT * WHERE Domain GLOB "moc.rab.foo.*".
*
* This key is called the "scope DB key" throughout the code. So the scope DB
* key for localStorage at http://foo.bar.com is "moc.rab.foo.:http:80".
*
* When calculating quotas, we want to lump together everything in an ETLD+1.
* So we use a "quota key" during lookups to calculate the quota. So the
* quota key for localStorage at http://foo.bar.com is "moc.rab.". */
class nsDOMStorageDBWrapper
{
public:
nsDOMStorageDBWrapper();
~nsDOMStorageDBWrapper();
/**
* Close the connections, finalizing all the cached statements.
*/
void Close();
nsresult
Init();
/**
* Retrieve a list of all the keys associated with a particular domain.
*/
nsresult
GetAllKeys(DOMStorageImpl* aStorage,
nsTHashtable<nsSessionStorageEntry>* aKeys);
/**
* Retrieve a value and secure flag for a key from storage.
*
* @throws NS_ERROR_DOM_NOT_FOUND_ERR if key not found
*/
nsresult
GetKeyValue(DOMStorageImpl* aStorage,
const nsAString& aKey,
nsAString& aValue,
bool* aSecure);
/**
* Set the value and secure flag for a key in storage.
*/
nsresult
SetKey(DOMStorageImpl* aStorage,
const nsAString& aKey,
const nsAString& aValue,
bool aSecure);
/**
* Set the secure flag for a key in storage. Does nothing if the key was
* not found.
*/
nsresult
SetSecure(DOMStorageImpl* aStorage,
const nsAString& aKey,
const bool aSecure);
/**
* Removes a key from storage.
*/
nsresult
RemoveKey(DOMStorageImpl* aStorage,
const nsAString& aKey);
/**
* Remove all keys belonging to this storage.
*/
nsresult
ClearStorage(DOMStorageImpl* aStorage);
/**
* Drop session-only storage for a specific host and all it's subdomains
*/
nsresult
DropSessionOnlyStoragesForHost(const nsACString& aHostName);
/**
* Drop everything we gathered to private browsing in-memory database
*/
nsresult
DropPrivateBrowsingStorages();
/**
* Removes all keys added by a given domain.
*/
nsresult
RemoveOwner(const nsACString& aOwner);
/**
* Removes all keys from storage. Used when clearing storage.
*/
nsresult
RemoveAll();
/**
* Removes all keys from storage for a specific app.
* If aOnlyBrowserElement is true, it will remove only keys with the
* browserElement flag set.
* aAppId has to be a valid app id. It can't be NO_APP_ID or UNKNOWN_APP_ID.
*/
nsresult
RemoveAllForApp(uint32_t aAppId, bool aOnlyBrowserElement);
/**
* Returns usage for a storage using its GetQuotaDBKey() as a key.
*/
nsresult
GetUsage(DOMStorageImpl* aStorage, int32_t *aUsage);
/**
* Returns usage of the domain and optionaly by any subdomain.
*/
nsresult
GetUsage(const nsACString& aDomain, int32_t *aUsage, bool aPrivate);
/**
* Marks the storage as "cached" after the DOMStorageImpl object has loaded
* all items to its memory copy of the entries - IsScopeDirty returns false
* after call of this method for this storage.
*
* When a key is changed or deleted in the storage, the storage scope is
* marked as "dirty" again and makes the DOMStorageImpl object recache its
* keys on next access, because IsScopeDirty returns true again.
*/
void
MarkScopeCached(DOMStorageImpl* aStorage);
/**
* Test whether the storage for the scope (i.e. origin or host) has been
* changed since the last MarkScopeCached call.
*/
bool
IsScopeDirty(DOMStorageImpl* aStorage);
/**
* Turns "http://foo.bar.com:80" to "moc.rab.oof.:http:80",
* i.e. reverses the host, appends a dot, appends the schema
* and a port number.
*/
static nsresult CreateScopeDBKey(nsIPrincipal* aPrincipal, nsACString& aKey);
/**
* Turns "http://foo.bar.com" to "moc.rab.oof.",
* i.e. reverses the host and appends a dot.
*/
static nsresult CreateReversedDomain(nsIURI* aUri, nsACString& aKey);
static nsresult CreateReversedDomain(const nsACString& aAsciiDomain, nsACString& aKey);
/**
* Turns "foo.bar.com" to "moc.rab.",
* i.e. extracts eTLD+1 from the host, reverses the result
* and appends a dot.
*/
static nsresult CreateQuotaDBKey(nsIPrincipal* aPrincipal,
nsACString& aKey);
/**
* Turns "foo.bar.com" to "moc.rab.",
* i.e. extracts eTLD+1 from the host, reverses the result
* and appends a dot.
*/
static nsresult CreateQuotaDBKey(const nsACString& aDomain,
nsACString& aKey)
{
return CreateReversedDomain(aDomain, aKey);
}
/**
* Ensures the cache flush timer is running. This is called when we add
* data that will need to be flushed.
*/
void EnsureCacheFlushTimer();
/**
* Called by the timer or on shutdown/profile change to evict scopes
* that have not been used in a long time and to flush new data to disk.
*/
nsresult FlushAndEvictFromCache(bool aIsShuttingDown);
/**
* Stops the cache flush timer.
*/
void StopCacheFlushTimer();
protected:
nsDOMStoragePersistentDB mPersistentDB;
nsDOMStorageMemoryDB mSessionOnlyDB;
nsDOMStorageMemoryDB mPrivateBrowsingDB;
nsCOMPtr<nsITimer> mCacheFlushTimer;
};
#endif /* nsDOMStorageDB_h___ */

Просмотреть файл

@ -1,383 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsCOMPtr.h"
#include "nsError.h"
#include "nsDOMStorage.h"
#include "nsDOMStorageMemoryDB.h"
#include "nsNetUtil.h"
nsresult
nsDOMStorageMemoryDB::Init(nsDOMStoragePersistentDB* aPreloadDB)
{
mData.Init(20);
mPreloadDB = aPreloadDB;
return NS_OK;
}
static PLDHashOperator
AllKeyEnum(nsSessionStorageEntry* aEntry, void* userArg)
{
nsDOMStorageMemoryDB::nsStorageItemsTable *target =
(nsDOMStorageMemoryDB::nsStorageItemsTable *)userArg;
nsDOMStorageMemoryDB::nsInMemoryItem* item =
new nsDOMStorageMemoryDB::nsInMemoryItem();
if (!item)
return PL_DHASH_STOP;
aEntry->mItem->GetValue(item->mValue);
nsresult rv = aEntry->mItem->GetSecure(&item->mSecure);
if (NS_FAILED(rv))
item->mSecure = false;
target->Put(aEntry->GetKey(), item);
return PL_DHASH_NEXT;
}
nsresult
nsDOMStorageMemoryDB::GetItemsTable(DOMStorageImpl* aStorage,
nsInMemoryStorage** aMemoryStorage)
{
if (mData.Get(aStorage->GetScopeDBKey(), aMemoryStorage))
return NS_OK;
*aMemoryStorage = nullptr;
nsInMemoryStorage* storageData = new nsInMemoryStorage();
if (!storageData)
return NS_ERROR_OUT_OF_MEMORY;
storageData->mTable.Init();
if (mPreloadDB) {
nsresult rv;
nsTHashtable<nsSessionStorageEntry> keys;
keys.Init();
rv = mPreloadDB->GetAllKeys(aStorage, &keys);
NS_ENSURE_SUCCESS(rv, rv);
mPreloading = true;
keys.EnumerateEntries(AllKeyEnum, &storageData->mTable);
mPreloading = false;
}
mData.Put(aStorage->GetScopeDBKey(), storageData);
*aMemoryStorage = storageData;
return NS_OK;
}
struct GetAllKeysEnumStruc
{
nsTHashtable<nsSessionStorageEntry>* mTarget;
DOMStorageImpl* mStorage;
};
static PLDHashOperator
GetAllKeysEnum(const nsAString& keyname,
nsDOMStorageMemoryDB::nsInMemoryItem* item,
void *closure)
{
GetAllKeysEnumStruc* struc = (GetAllKeysEnumStruc*)closure;
nsSessionStorageEntry* entry = struc->mTarget->PutEntry(keyname);
if (!entry)
return PL_DHASH_STOP;
entry->mItem = new nsDOMStorageItem(struc->mStorage,
keyname,
EmptyString(),
item->mSecure);
if (!entry->mItem)
return PL_DHASH_STOP;
return PL_DHASH_NEXT;
}
nsresult
nsDOMStorageMemoryDB::GetAllKeys(DOMStorageImpl* aStorage,
nsTHashtable<nsSessionStorageEntry>* aKeys)
{
nsresult rv;
nsInMemoryStorage* storage;
rv = GetItemsTable(aStorage, &storage);
NS_ENSURE_SUCCESS(rv, rv);
GetAllKeysEnumStruc struc;
struc.mTarget = aKeys;
struc.mStorage = aStorage;
storage->mTable.EnumerateRead(GetAllKeysEnum, &struc);
return NS_OK;
}
nsresult
nsDOMStorageMemoryDB::GetKeyValue(DOMStorageImpl* aStorage,
const nsAString& aKey,
nsAString& aValue,
bool* aSecure)
{
if (mPreloading) {
NS_PRECONDITION(mPreloadDB, "Must have a preload DB set when preloading");
return mPreloadDB->GetKeyValue(aStorage, aKey, aValue, aSecure);
}
nsresult rv;
nsInMemoryStorage* storage;
rv = GetItemsTable(aStorage, &storage);
NS_ENSURE_SUCCESS(rv, rv);
nsInMemoryItem* item;
if (!storage->mTable.Get(aKey, &item))
return NS_ERROR_DOM_NOT_FOUND_ERR;
aValue = item->mValue;
*aSecure = item->mSecure;
return NS_OK;
}
nsresult
nsDOMStorageMemoryDB::SetKey(DOMStorageImpl* aStorage,
const nsAString& aKey,
const nsAString& aValue,
bool aSecure)
{
nsresult rv;
nsInMemoryStorage* storage;
rv = GetItemsTable(aStorage, &storage);
NS_ENSURE_SUCCESS(rv, rv);
int32_t usage = 0;
if (!aStorage->GetQuotaDBKey().IsEmpty()) {
rv = GetUsage(aStorage, &usage);
NS_ENSURE_SUCCESS(rv, rv);
}
usage += aKey.Length() + aValue.Length();
nsInMemoryItem* item;
if (!storage->mTable.Get(aKey, &item)) {
if (usage > GetQuota()) {
return NS_ERROR_DOM_QUOTA_REACHED;
}
item = new nsInMemoryItem();
if (!item)
return NS_ERROR_OUT_OF_MEMORY;
storage->mTable.Put(aKey, item);
storage->mUsageDelta += aKey.Length();
}
else
{
if (!aSecure && item->mSecure)
return NS_ERROR_DOM_SECURITY_ERR;
usage -= aKey.Length() + item->mValue.Length();
if (usage > GetQuota()) {
return NS_ERROR_DOM_QUOTA_REACHED;
}
}
storage->mUsageDelta += aValue.Length() - item->mValue.Length();
item->mValue = aValue;
item->mSecure = aSecure;
MarkScopeDirty(aStorage);
return NS_OK;
}
nsresult
nsDOMStorageMemoryDB::SetSecure(DOMStorageImpl* aStorage,
const nsAString& aKey,
const bool aSecure)
{
nsresult rv;
nsInMemoryStorage* storage;
rv = GetItemsTable(aStorage, &storage);
NS_ENSURE_SUCCESS(rv, rv);
nsInMemoryItem* item;
if (!storage->mTable.Get(aKey, &item))
return NS_ERROR_DOM_NOT_FOUND_ERR;
item->mSecure = aSecure;
MarkScopeDirty(aStorage);
return NS_OK;
}
nsresult
nsDOMStorageMemoryDB::RemoveKey(DOMStorageImpl* aStorage,
const nsAString& aKey)
{
nsresult rv;
nsInMemoryStorage* storage;
rv = GetItemsTable(aStorage, &storage);
NS_ENSURE_SUCCESS(rv, rv);
nsInMemoryItem* item;
if (!storage->mTable.Get(aKey, &item))
return NS_ERROR_DOM_NOT_FOUND_ERR;
storage->mUsageDelta -= aKey.Length() + item->mValue.Length();
storage->mTable.Remove(aKey);
MarkScopeDirty(aStorage);
return NS_OK;
}
static PLDHashOperator
RemoveAllKeysEnum(const nsAString& keyname,
nsAutoPtr<nsDOMStorageMemoryDB::nsInMemoryItem>& item,
void *closure)
{
nsDOMStorageMemoryDB::nsInMemoryStorage* storage =
(nsDOMStorageMemoryDB::nsInMemoryStorage*)closure;
storage->mUsageDelta -= keyname.Length() + item->mValue.Length();
return PL_DHASH_REMOVE;
}
nsresult
nsDOMStorageMemoryDB::ClearStorage(DOMStorageImpl* aStorage)
{
nsresult rv;
nsInMemoryStorage* storage;
rv = GetItemsTable(aStorage, &storage);
NS_ENSURE_SUCCESS(rv, rv);
storage->mTable.Enumerate(RemoveAllKeysEnum, storage);
MarkScopeDirty(aStorage);
return NS_OK;
}
nsresult
nsDOMStorageMemoryDB::DropStorage(DOMStorageImpl* aStorage)
{
mData.Remove(aStorage->GetScopeDBKey());
MarkScopeDirty(aStorage);
return NS_OK;
}
struct RemoveOwnersStruc
{
nsCString* mSubDomain;
bool mMatch;
};
static PLDHashOperator
RemoveOwnersEnum(const nsACString& key,
nsAutoPtr<nsDOMStorageMemoryDB::nsInMemoryStorage>& storage,
void *closure)
{
RemoveOwnersStruc* struc = (RemoveOwnersStruc*)closure;
if (StringBeginsWith(key, *(struc->mSubDomain)) == struc->mMatch)
return PL_DHASH_REMOVE;
return PL_DHASH_NEXT;
}
nsresult
nsDOMStorageMemoryDB::RemoveOwner(const nsACString& aOwner)
{
nsAutoCString subdomainsDBKey;
nsDOMStorageDBWrapper::CreateReversedDomain(aOwner, subdomainsDBKey);
RemoveOwnersStruc struc;
struc.mSubDomain = &subdomainsDBKey;
struc.mMatch = true;
mData.Enumerate(RemoveOwnersEnum, &struc);
MarkAllScopesDirty();
return NS_OK;
}
nsresult
nsDOMStorageMemoryDB::RemoveAll()
{
mData.Clear(); // XXX Check this releases all instances
MarkAllScopesDirty();
return NS_OK;
}
nsresult
nsDOMStorageMemoryDB::GetUsage(DOMStorageImpl* aStorage, int32_t *aUsage)
{
return GetUsageInternal(aStorage->GetQuotaDBKey(), aUsage);
}
nsresult
nsDOMStorageMemoryDB::GetUsage(const nsACString& aDomain,
int32_t *aUsage)
{
nsresult rv;
nsAutoCString quotaDBKey;
rv = nsDOMStorageDBWrapper::CreateQuotaDBKey(aDomain, quotaDBKey);
NS_ENSURE_SUCCESS(rv, rv);
return GetUsageInternal(quotaDBKey, aUsage);
}
struct GetUsageEnumStruc
{
int32_t mUsage;
nsCString mSubdomain;
};
static PLDHashOperator
GetUsageEnum(const nsACString& key,
nsDOMStorageMemoryDB::nsInMemoryStorage* storageData,
void *closure)
{
GetUsageEnumStruc* struc = (GetUsageEnumStruc*)closure;
if (StringBeginsWith(key, struc->mSubdomain)) {
struc->mUsage += storageData->mUsageDelta;
}
return PL_DHASH_NEXT;
}
nsresult
nsDOMStorageMemoryDB::GetUsageInternal(const nsACString& aQuotaDBKey,
int32_t *aUsage)
{
GetUsageEnumStruc struc;
struc.mUsage = 0;
struc.mSubdomain = aQuotaDBKey;
if (mPreloadDB) {
nsresult rv;
rv = mPreloadDB->GetUsageInternal(aQuotaDBKey, &struc.mUsage);
NS_ENSURE_SUCCESS(rv, rv);
}
mData.EnumerateRead(GetUsageEnum, &struc);
*aUsage = struc.mUsage;
return NS_OK;
}

Просмотреть файл

@ -1,145 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsDOMStorageMemoryDB_h___
#define nsDOMStorageMemoryDB_h___
#include "nscore.h"
#include "nsDOMStorageBaseDB.h"
#include "nsClassHashtable.h"
#include "nsDataHashtable.h"
class nsDOMStoragePersistentDB;
class nsDOMStorageMemoryDB : public nsDOMStorageBaseDB
{
public:
nsDOMStorageMemoryDB() : mPreloading(false) {}
~nsDOMStorageMemoryDB() {}
class nsInMemoryItem
{
public:
bool mSecure;
nsString mValue;
};
typedef nsClassHashtable<nsStringHashKey, nsInMemoryItem> nsStorageItemsTable;
class nsInMemoryStorage
{
public:
nsStorageItemsTable mTable;
int32_t mUsageDelta;
nsInMemoryStorage() : mUsageDelta(0) {}
};
/**
* @param aPreloadDB
* If non-null, data for a domain/origin will be preloaded from
* the provided database. Used for session-only cookies mode to
* provide existing data from the persistent database.
*/
nsresult
Init(nsDOMStoragePersistentDB* aPreloadDB = nullptr);
/**
*
*/
nsresult
GetItemsTable(DOMStorageImpl* aStorage,
nsInMemoryStorage** aMemoryStorage);
/**
* Retrieve a list of all the keys associated with a particular domain.
*/
nsresult
GetAllKeys(DOMStorageImpl* aStorage,
nsTHashtable<nsSessionStorageEntry>* aKeys);
/**
* Retrieve a value and secure flag for a key from storage.
*
* @throws NS_ERROR_DOM_NOT_FOUND_ERR if key not found
*/
nsresult
GetKeyValue(DOMStorageImpl* aStorage,
const nsAString& aKey,
nsAString& aValue,
bool* aSecure);
/**
* Set the value and secure flag for a key in storage.
*/
nsresult
SetKey(DOMStorageImpl* aStorage,
const nsAString& aKey,
const nsAString& aValue,
bool aSecure);
/**
* Set the secure flag for a key in storage. Does nothing if the key was
* not found.
*/
nsresult
SetSecure(DOMStorageImpl* aStorage,
const nsAString& aKey,
const bool aSecure);
/**
* Removes a key from storage.
*/
nsresult
RemoveKey(DOMStorageImpl* aStorage,
const nsAString& aKey);
/**
* Remove all keys belonging to this storage.
*/
nsresult
ClearStorage(DOMStorageImpl* aStorage);
/**
* If we have changed the persistent storage, drop any potential session storages
*/
nsresult
DropStorage(DOMStorageImpl* aStorage);
/**
* Removes all keys added by a given domain.
*/
nsresult
RemoveOwner(const nsACString& aOwner);
/**
* Removes all keys from storage. Used when clearing storage.
*/
nsresult
RemoveAll();
/**
* Returns usage for a storage using its GetQuotaDBKey() as a key.
*/
nsresult
GetUsage(DOMStorageImpl* aStorage, int32_t *aUsage);
/**
* Returns usage of the domain and optionaly by any subdomain.
*/
nsresult
GetUsage(const nsACString& aDomain, int32_t *aUsage);
protected:
nsClassHashtable<nsCStringHashKey, nsInMemoryStorage> mData;
nsDOMStoragePersistentDB* mPreloadDB;
bool mPreloading;
nsresult
GetUsageInternal(const nsACString& aQuotaDBKey, int32_t *aUsage);
};
#endif

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,265 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsDOMStoragePersistentDB_h___
#define nsDOMStoragePersistentDB_h___
#include "nscore.h"
#include "nsDOMStorageBaseDB.h"
#include "mozIStorageConnection.h"
#include "mozIStorageStatement.h"
#include "mozilla/storage/StatementCache.h"
#include "mozIStorageBindingParamsArray.h"
#include "nsTHashtable.h"
#include "nsDataHashtable.h"
#include "nsIThread.h"
#include "nsLocalStorageCache.h"
class DOMStorageImpl;
class nsSessionStorageEntry;
class nsDOMStoragePersistentDB : public nsDOMStorageBaseDB
{
typedef mozilla::storage::StatementCache<mozIStorageStatement> StatementCache;
typedef nsLocalStorageCache::FlushData FlushData;
public:
nsDOMStoragePersistentDB();
~nsDOMStoragePersistentDB() {}
nsresult
Init(const nsString& aDatabaseName);
/**
* Close the connection, finalizing all the cached statements.
*/
void
Close();
/**
* Indicates whether any data is cached that might need to be
* flushed or evicted.
*/
bool
IsFlushTimerNeeded() const;
/**
* Retrieve a list of all the keys associated with a particular domain.
*/
nsresult
GetAllKeys(DOMStorageImpl* aStorage,
nsTHashtable<nsSessionStorageEntry>* aKeys);
/**
* Retrieve a value and secure flag for a key from storage.
*
* @throws NS_ERROR_DOM_NOT_FOUND_ERR if key not found
*/
nsresult
GetKeyValue(DOMStorageImpl* aStorage,
const nsAString& aKey,
nsAString& aValue,
bool* aSecure);
/**
* Set the value and secure flag for a key in storage.
*/
nsresult
SetKey(DOMStorageImpl* aStorage,
const nsAString& aKey,
const nsAString& aValue,
bool aSecure);
/**
* Set the secure flag for a key in storage. Does nothing if the key was
* not found.
*/
nsresult
SetSecure(DOMStorageImpl* aStorage,
const nsAString& aKey,
const bool aSecure);
/**
* Removes a key from storage.
*/
nsresult
RemoveKey(DOMStorageImpl* aStorage,
const nsAString& aKey);
/**
* Remove all keys belonging to this storage.
*/
nsresult
ClearStorage(DOMStorageImpl* aStorage);
/**
* Removes all keys added by a given domain.
*/
nsresult
RemoveOwner(const nsACString& aOwner);
/**
* Removes all keys from storage. Used when clearing storage.
*/
nsresult
RemoveAll();
/**
* Removes all keys from storage for a specific app.
* If aOnlyBrowserElement is true, it will remove only keys with the
* browserElement flag set.
* aAppId has to be a valid app id. It can't be NO_APP_ID or UNKNOWN_APP_ID.
*/
nsresult
RemoveAllForApp(uint32_t aAppId, bool aOnlyBrowserElement);
/**
* Returns usage for a storage using its GetQuotaDBKey() as a key.
*/
nsresult
GetUsage(DOMStorageImpl* aStorage, int32_t *aUsage);
/**
* Returns usage of the domain and optionaly by any subdomain.
*/
nsresult
GetUsage(const nsACString& aDomain, int32_t *aUsage);
/**
* Clears all in-memory data from private browsing mode
*/
nsresult
ClearAllPrivateBrowsingData();
/**
* This is the top-level method called by timer & shutdown code.
*
* LocalStorage now flushes dirty items off the main thread to reduce
* main thread jank.
*
* When the flush timer fires, pointers to changed data are retrieved from
* nsLocalStorageCache on the main thread and are used to build an SQLite
* statement array with bound parameters that is then executed on a
* background thread. Only one flush operation is outstanding at once.
* After the flush operation completes, a notification task
* is enqueued on the main thread which updates the cache state.
*
* Cached scopes are evicted if they aren't dirty and haven't been used for
* the maximum idle time.
*/
nsresult
FlushAndEvictFromCache(bool aMainThread);
/**
* Executes SQL statements for flushing dirty data to disk.
*/
nsresult
Flush();
/**
* The notifier task calls this method to update cache state after a
* flush operation completes.
*/
void
HandleFlushComplete(bool aSucceeded);
private:
friend class nsDOMStorageMemoryDB;
/**
* Sets the database's journal mode to WAL or TRUNCATE.
*/
nsresult
SetJournalMode(bool aIsWal);
/**
* Sets the threshold for auto-checkpointing the WAL.
*/
nsresult
ConfigureWalBehavior();
/**
* Ensures that the scope's keys are cached.
*/
nsresult
EnsureScopeLoaded(DOMStorageImpl* aStorage);
/**
* Fetches the scope's data from the database.
*/
nsresult
FetchScope(DOMStorageImpl* aStorage, nsScopeCache* aScopeCache);
/**
* Ensures that the quota usage information for the site is known.
*
* The current quota usage is calculated from the space occupied by
* the cached scopes + the size of the uncached scopes on disk.
* This method ensures that the size of the site's uncached scopes
* on disk is known.
*/
nsresult
EnsureQuotaUsageLoaded(const nsACString& aQuotaKey);
/**
* Fetches the size of scopes matching aQuotaDBKey from the database.
*/
nsresult
FetchQuotaUsage(const nsACString& aQuotaDBKey);
nsresult
GetUsageInternal(const nsACString& aQuotaDBKey, int32_t *aUsage);
/**
* Helper function for RemoveOwner and RemoveAllForApp.
*/
nsresult
FetchMatchingScopeNames(const nsACString& aPattern);
nsresult
PrepareFlushStatements(const FlushData& aFlushData);
/**
* Gathers the dirty data from the cache, prepares SQL statements and
* updates state flags
*/
nsresult
PrepareForFlush();
/**
* Removes non-dirty scopes that haven't been used in X seconds.
*/
void
EvictUnusedScopes();
/**
* DB data structures
*/
nsTArray<nsCOMPtr<mozIStorageStatement> > mFlushStatements;
nsTArray<nsCOMPtr<mozIStorageBindingParamsArray> > mFlushStatementParams;
StatementCache mReadStatements;
StatementCache mWriteStatements;
nsCOMPtr<mozIStorageConnection> mReadConnection;
nsCOMPtr<mozIStorageConnection> mWriteConnection;
/**
* Cache state data
*/
nsLocalStorageCache mCache;
nsDataHashtable<nsCStringHashKey, int32_t> mQuotaUseByUncached;
// Set if the DB needs to be emptied on next flush
bool mWasRemoveAllCalled;
// Set if the DB is currently getting emptied in an async flush
bool mIsRemoveAllPending;
// Set if a flush operation has been enqueued in the async thread
bool mIsFlushPending;
// Helper thread for flushing
nsCOMPtr<nsIThread> mFlushThread;
};
#endif /* nsDOMStorageDB_h___ */

Просмотреть файл

@ -1,416 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsDOMStorage.h"
#include "nsLocalStorageCache.h"
using namespace mozilla;
// Scopes will be evicted from memory if not accessed (read or written)
// for MAX_IDLE_TIME seconds
#define MAX_IDLE_TIME (30) // seconds
/**
* Anonymous namespace holding helper enum functions
*/
namespace {
struct GetUsageEnumData
{
const nsACString& mQuotaKey;
int32_t mUsage;
};
PLDHashOperator
GetUsageEnum(const nsACString& aScopeName,
nsScopeCache* aScopeCache,
void* aParams)
{
GetUsageEnumData* data = static_cast<GetUsageEnumData*>(aParams);
if (StringBeginsWith(aScopeName, data->mQuotaKey)) {
data->mUsage += aScopeCache->GetQuotaUsage();
}
return PL_DHASH_NEXT;
}
struct EvictEnumData
{
nsTArray<nsCString>& mEvicted;
nsTArray<int32_t>& mEvictedSize;
};
struct GetAllKeysEnumData
{
nsTHashtable<nsSessionStorageEntry>& mKeys;
DOMStorageImpl* mStorage;
};
PLDHashOperator
GetAllKeysEnum(const nsAString& aKey,
nsScopeCache::KeyEntry* aEntry,
void* aParams)
{
GetAllKeysEnumData* data = static_cast<GetAllKeysEnumData*>(aParams);
nsSessionStorageEntry* keyEntry = data->mKeys.PutEntry(aKey);
keyEntry->mItem = new nsDOMStorageItem(data->mStorage,
aKey,
aEntry->mValue,
aEntry->mIsSecure);
return PL_DHASH_NEXT;
}
PLDHashOperator
MarkMatchingDeletedEnum(const nsACString& aScopeName,
nsAutoPtr<nsScopeCache>& aScopeCache,
void* aPattern)
{
const nsACString* pattern = static_cast<const nsACString*>(aPattern);
if (!StringBeginsWith(aScopeName, *pattern)) {
return PL_DHASH_NEXT;
}
aScopeCache->DeleteScope();
return PL_DHASH_NEXT;
}
PLDHashOperator
MarkKeysEnum(const nsAString& aKey,
nsAutoPtr<nsScopeCache::KeyEntry>& aEntry,
void* aDirtyState)
{
aEntry->mIsDirty = *(static_cast<bool*>(aDirtyState));
return PL_DHASH_NEXT;
}
PLDHashOperator
GetChangedKeysEnum(const nsAString& aKey,
nsAutoPtr<nsScopeCache::KeyEntry>& aEntry,
void* aParams)
{
if (!aEntry->mIsDirty) {
return PL_DHASH_NEXT;
}
nsLocalStorageCache::FlushData::ChangeSet* changeSet =
static_cast<nsLocalStorageCache::FlushData::ChangeSet*>(aParams);
changeSet->mDirtyKeys.AppendElement(aKey);
changeSet->mDirtyValues.AppendElement(aEntry.get());
return PL_DHASH_NEXT;
}
PLDHashOperator
GetEntrySize(const nsAString& aKey,
nsScopeCache::KeyEntry* aEntry,
void* aParam)
{
int32_t* usage = static_cast<int32_t*>(aParam);
*usage += aKey.Length() + aEntry->mValue.Length();
return PL_DHASH_NEXT;
}
} // anonymous namespace
nsLocalStorageCache::nsLocalStorageCache()
{
mScopeCaches.Init(16);
}
nsScopeCache*
nsLocalStorageCache::GetScope(const nsACString& aScopeName)
{
nsScopeCache* scopeCache = nullptr;
if (mScopeCaches.Get(aScopeName, &scopeCache)) {
scopeCache->mAccessTime = PR_IntervalNow();
}
return scopeCache;
}
void
nsLocalStorageCache::AddScope(const nsACString& aScopeName,
nsScopeCache* aScopeCache)
{
aScopeCache->mAccessTime = PR_IntervalNow();
mScopeCaches.Put(aScopeName, aScopeCache);
}
bool
nsLocalStorageCache::IsScopeCached(const nsACString& aScopeName) const
{
return mScopeCaches.Get(aScopeName, nullptr);
}
uint32_t
nsLocalStorageCache::Count() const
{
return mScopeCaches.Count();
}
int32_t
nsLocalStorageCache::GetQuotaUsage(const nsACString& aQuotaKey) const
{
GetUsageEnumData data = { aQuotaKey , 0 };
mScopeCaches.EnumerateRead(GetUsageEnum, &data);
return data.mUsage;
}
void
nsLocalStorageCache::MarkMatchingScopesDeleted(const nsACString& aPattern)
{
mScopeCaches.Enumerate(MarkMatchingDeletedEnum, (void*)&aPattern);
}
void
nsLocalStorageCache::ForgetAllScopes()
{
mScopeCaches.Clear();
}
/* static */
PLDHashOperator
nsLocalStorageCache::GetDirtyDataEnum(const nsACString& aScopeName,
nsScopeCache* aScopeCache,
void* aParams)
{
if (!aScopeCache->mIsDirty) {
return PL_DHASH_NEXT;
}
FlushData* flushData = static_cast<FlushData*>(aParams);
FlushData::ChangeSet changeSet;
changeSet.mWasDeleted = aScopeCache->mWasScopeDeleted;
changeSet.mDeletedKeys = &aScopeCache->mDeletedKeys;
aScopeCache->mTable.Enumerate(GetChangedKeysEnum, &changeSet);
flushData->mScopeNames.AppendElement(aScopeName);
flushData->mChanged.AppendElement(changeSet);
return PL_DHASH_NEXT;
}
void
nsLocalStorageCache::GetFlushData(FlushData* aData) const
{
mScopeCaches.EnumerateRead(GetDirtyDataEnum, aData);
}
/* static */
PLDHashOperator
nsLocalStorageCache::SetFlushStateEnum(const nsACString& aScopeName,
nsAutoPtr<nsScopeCache>& aScopeCache,
void* aParams)
{
FlushState* newState = static_cast<FlushState*>(aParams);
if (*newState == FLUSH_PENDING && aScopeCache->mIsDirty) {
// Mark all data clean before a flush to allow further changes during flush
bool isDirty = false;
aScopeCache->mTable.Enumerate(MarkKeysEnum, &isDirty);
aScopeCache->mDeletedKeys.Clear();
aScopeCache->mWasScopeDeleted = false;
aScopeCache->mIsDirty = false;
aScopeCache->mIsFlushPending = true;
} else if (*newState == FLUSHED && aScopeCache->mIsFlushPending) {
aScopeCache->mIsFlushPending = false;
} else if (*newState == FLUSH_FAILED && aScopeCache->mIsFlushPending) {
// After a flush fails, disk state is unchanged (thanks to transaction
// rollback) and the cache contains the correct current state.
// So to bring disk back in sync with cache, we simply delete all scope
// data from disk on next flush and write out *ALL* the scope's cached keys.
// To trigger this behavior, we mark mWasScopeDeleted = true and mark all
// keys dirty. Note that scope cache will not be evicted or re-fetched since
// it's marked dirty.
bool isDirty = true;
aScopeCache->mWasScopeDeleted = true;
aScopeCache->mTable.Enumerate(MarkKeysEnum, &isDirty);
aScopeCache->mIsFlushPending = false;
aScopeCache->mIsDirty = true;
}
return PL_DHASH_NEXT;
}
void
nsLocalStorageCache::MarkScopesPending()
{
FlushState newState = FLUSH_PENDING;
mScopeCaches.Enumerate(SetFlushStateEnum, &newState);
}
void
nsLocalStorageCache::MarkScopesFlushed()
{
FlushState newState = FLUSHED;
mScopeCaches.Enumerate(SetFlushStateEnum, &newState);
}
void
nsLocalStorageCache::MarkFlushFailed()
{
FlushState newState = FLUSH_FAILED;
mScopeCaches.Enumerate(SetFlushStateEnum, &newState);
}
/* static */
PLDHashOperator
nsLocalStorageCache::EvictEnum(const nsACString& aScopeName,
nsAutoPtr<nsScopeCache>& aScopeCache,
void* aParams)
{
EvictEnumData* data = static_cast<EvictEnumData*>(aParams);
// We can't evict scopes currently getting flushed in case the flush fails
if (aScopeCache->mIsDirty || aScopeCache->mIsFlushPending) {
return PL_DHASH_NEXT;
}
static const PRIntervalTime kMaxIdleTime = PR_SecondsToInterval(MAX_IDLE_TIME);
bool evict = (PR_IntervalNow() - aScopeCache->mAccessTime) > kMaxIdleTime;
if (evict) {
data->mEvicted.AppendElement(aScopeName);
data->mEvictedSize.AppendElement(aScopeCache->GetQuotaUsage());
return PL_DHASH_REMOVE;
}
return PL_DHASH_NEXT;
}
void
nsLocalStorageCache::EvictScopes(nsTArray<nsCString>& aEvicted,
nsTArray<int32_t>& aEvictedSize)
{
EvictEnumData data = { aEvicted, aEvictedSize };
mScopeCaches.Enumerate(EvictEnum, &data);
}
///////////////////////////////////
nsScopeCache::nsScopeCache()
: mWasScopeDeleted(false), mIsDirty(false), mIsFlushPending(false)
{
mTable.Init();
}
nsresult
nsScopeCache::AddEntry(const nsAString& aKey,
const nsAString& aValue,
bool aSecure)
{
KeyEntry* entry = new KeyEntry();
entry->mValue = aValue;
entry->mIsSecure = aSecure;
entry->mIsDirty = false;
mTable.Put(aKey, entry);
return NS_OK;
}
nsresult
nsScopeCache::GetAllKeys(DOMStorageImpl* aStorage,
nsTHashtable<nsSessionStorageEntry>* aKeys) const
{
GetAllKeysEnumData data = { *aKeys, aStorage };
mTable.EnumerateRead(GetAllKeysEnum, &data);
return NS_OK;
}
bool
nsScopeCache::GetKey(const nsAString& aKey,
nsAString& aValue,
bool* aSecure) const
{
KeyEntry* entry = nullptr;
if (mTable.Get(aKey, &entry)) {
aValue = entry->mValue;
*aSecure = entry->mIsSecure;
return true;
}
return false;
}
nsresult
nsScopeCache::SetKey(const nsAString& aKey,
const nsAString& aValue,
bool aSecure)
{
KeyEntry* entry = nullptr;
if (!mTable.Get(aKey, &entry)) {
entry = new KeyEntry();
mTable.Put(aKey, entry);
} else if (entry->mValue == aValue) {
// Don't mark the cache dirty if nothing changed
return NS_OK;
}
entry->mValue = aValue;
entry->mIsSecure = aSecure;
entry->mIsDirty = true;
mDeletedKeys.RemoveElement(aKey);
mIsDirty = true;
return NS_OK;
}
void
nsScopeCache::SetSecure(const nsAString& aKey,
bool aSecure)
{
KeyEntry* entry = nullptr;
if (!mTable.Get(aKey, &entry)) {
return;
}
entry->mIsSecure = aSecure;
entry->mIsDirty = true;
mIsDirty = true;
}
void
nsScopeCache::RemoveKey(const nsAString& aKey)
{
KeyEntry* entry = nullptr;
if (!mTable.Get(aKey, &entry)) {
return;
}
nsAutoString key(aKey);
if (!mWasScopeDeleted) {
if (!mDeletedKeys.Contains(key)) {
mDeletedKeys.AppendElement(key);
}
}
mTable.Remove(key);
mIsDirty = true;
}
void
nsScopeCache::DeleteScope()
{
mTable.Clear();
mDeletedKeys.Clear();
mWasScopeDeleted = true;
mIsDirty = true;
}
int32_t
nsScopeCache::GetQuotaUsage() const
{
int32_t usage = 0;
mTable.EnumerateRead(GetEntrySize, &usage);
return usage;
}

Просмотреть файл

@ -1,184 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsLocalStorageCache_h___
#define nsLocalStorageCache_h___
#include "nscore.h"
#include "nsClassHashtable.h"
#include "nsTHashtable.h"
#include "nsTArray.h"
class DOMStorageImpl;
class nsSessionStorageEntry;
/**
* nsScopeCache is the representation of a single scope's contents
* and its flush state.
*
* Typical state progression for a scope:
*
* 1. Scope is loaded into memory from disk:
* mIsDirty = false;
* mIsFlushPending = false;
*
* 2. A key is modified/deleted in the scope, or the entire scope is deleted:
* mIsDirty = *true*;
* mIsFlushPending = false;
*
* 3. Flush timer fires and dirty data is collected:
* mIsDirty = *false*;
* mIsFlushPending = *true*;
*
* 4a. If the flush succeds:
* mIsDirty = false;
* mIsFlushPending = *false*;
*
* 4b. If the flush fails for some reason
* e.g. profile dir is on network storage and the network is flaky
*
* mIsDirty = *true*;
* mIsFlushPending = *false*;
*
* Note that in this case, no data would be lost.
* On next flush, the scope would get deleted from the DB to bring
* the DB back to a known state and then ALL the cached keys would
* get written out.
* Failed flushes ought to be very rare.
*/
class nsScopeCache
{
public:
nsScopeCache();
nsresult AddEntry(const nsAString& aKey,
const nsAString& aValue,
bool aSecure);
nsresult GetAllKeys(DOMStorageImpl* aStorage,
nsTHashtable<nsSessionStorageEntry>* aKeys) const;
bool GetKey(const nsAString& aKey, nsAString& aValue, bool* aSecure) const;
nsresult SetKey(const nsAString& aKey, const nsAString& aValue, bool aSecure);
void SetSecure(const nsAString& aKey, bool aSecure);
void RemoveKey(const nsAString& aKey);
void DeleteScope();
int32_t GetQuotaUsage() const;
/**
* A single key/value pair in a scope.
*/
class KeyEntry
{
public:
KeyEntry() : mIsSecure(false), mIsDirty(false) {}
nsString mValue;
bool mIsSecure;
bool mIsDirty;
};
private:
friend class nsLocalStorageCache;
// Scope data
// -- Whether a scope should be deleted from DB first before any DB inserts
bool mWasScopeDeleted;
// -- Keys to be deleted from DB on next flush
nsTArray<nsString> mDeletedKeys;
// -- Mapping of scope keys to scope values
nsClassHashtable<nsStringHashKey, KeyEntry> mTable;
// Scope state:
// -- The last time the scope was used, helps decide whether to evict scope
PRIntervalTime mAccessTime;
// -- Whether the scope needs to be flushed to disk
// Note that dirty scopes can't be evicted
bool mIsDirty;
// -- Prevents eviction + is used to recover from bad flushes
bool mIsFlushPending;
};
/**
* nsLocalStorageCache manages all the cached LocalStorage scopes.
*/
class nsLocalStorageCache
{
public:
nsLocalStorageCache();
nsScopeCache* GetScope(const nsACString& aScopeName);
void AddScope(const nsACString& aScopeName, nsScopeCache* aScopeCache);
bool IsScopeCached(const nsACString& aScopeName) const;
uint32_t Count() const;
int32_t GetQuotaUsage(const nsACString& aQuotaKey) const;
void MarkMatchingScopesDeleted(const nsACString& aPattern);
void ForgetAllScopes();
/**
* Collection of all the dirty data across all scopes.
*/
struct FlushData
{
public:
struct ChangeSet
{
bool mWasDeleted;
nsTArray<nsString>* mDeletedKeys;
nsTArray<nsString> mDirtyKeys;
nsTArray<nsScopeCache::KeyEntry*> mDirtyValues;
};
// A scope's name in mScopeNames has the same index as the
// scope's ChangeSet in mChanged
nsTArray<nsCString> mScopeNames;
nsTArray<ChangeSet> mChanged;
};
/**
* Fills the FlushData structure with the dirty items in the cache.
*/
void GetFlushData(FlushData* aData) const;
/**
* These methods update the state of each cached scope and
* its keys. See description in nsScopeCache.
*/
void MarkScopesPending();
void MarkScopesFlushed();
void MarkFlushFailed();
/**
* Evicts scopes not accessed since aMinAccessTime.
* Returns info about evicted scopes in aEvicted & aEvictedSize.
*/
void EvictScopes(nsTArray<nsCString>& aEvicted,
nsTArray<int32_t>& aEvictedSize);
private:
static PLDHashOperator
GetDirtyDataEnum(const nsACString& aScopeName,
nsScopeCache* aScopeCache,
void* aParams);
enum FlushState {
FLUSH_PENDING,
FLUSHED,
FLUSH_FAILED
};
static PLDHashOperator
SetFlushStateEnum(const nsACString& aScopeName,
nsAutoPtr<nsScopeCache>& aScopeCache,
void* aParams);
static PLDHashOperator
EvictEnum(const nsACString& aScopeName,
nsAutoPtr<nsScopeCache>& aScopeCache,
void* aParams);
private:
// Scopes currently cached in memory
// Maps: scope name -> scope's cache info
nsClassHashtable<nsCStringHashKey, nsScopeCache> mScopeCaches;
};
#endif

Просмотреть файл

@ -13,7 +13,6 @@ include $(DEPTH)/config/autoconf.mk
MOCHITEST_FILES = \
frameAppIsolation.html \
frameBug624047.html \
frameChromeSlave.html \
frameKeySync.html \
frameMasterEqual.html \
@ -27,9 +26,10 @@ MOCHITEST_FILES = \
interOriginFrame.js \
interOriginTest.js \
interOriginTest2.js \
localStorageCommon.js \
test_appIsolation.html \
test_brokenUTF-16.html \
test_bug624047.html \
test_bug600307-DBOps.html \
test_bug746272-1.html \
test_bug746272-2.html \
test_cookieBlock.html \

Просмотреть файл

@ -1,30 +0,0 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>slave for bug 624047 test</title>
<script type="text/javascript" src="interOriginFrame.js"></script>
<script type="text/javascript">
function doStep()
{
localStorage.name = 1;
var timer = setInterval(function() {
is(localStorage.name, 1, "Value is still present");
}, 1000);
setTimeout(function() {
clearTimeout(timer);
localStorage.clear();
postMsg("done");
}, 12000);
return false;
}
</script>
</head>
<body onload="postMsg('frame loaded');">
</body>
</html>

Просмотреть файл

@ -0,0 +1,46 @@
function localStorageFlush(cb)
{
var ob = {
observe : function(sub, top, dat)
{
os().removeObserver(ob, "domstorage-test-flushed");
cb();
}
};
os().addObserver(ob, "domstorage-test-flushed", false);
notify("domstorage-test-flush-force");
}
function localStorageReload()
{
notify("domstorage-test-reload");
}
function localStorageFlushAndReload(cb)
{
localStorageFlush(function() {
localStorageReload();
cb();
});
}
function localStorageClearAll()
{
os().notifyObservers(null, "cookie-changed", "cleared");
}
function localStorageClearDomain(domain)
{
os().notifyObservers(null, "browser:purge-domain-data", domain);
}
function os()
{
return SpecialPowers.Cc["@mozilla.org/observer-service;1"]
.getService(SpecialPowers.Ci.nsIObserverService);
}
function notify(top)
{
os().notifyObservers(null, top, null);
}

Просмотреть файл

@ -0,0 +1,166 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>bug 600307</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="localStorageCommon.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript">
/*
This is strictly implementation specific test for dom storage code from bug 600307.
It exercises code for asynchronous data flushing with regard to cache life time and its preload.
"flush + reload" means to tell the database to store all pending data then wipe the cached content and reload it from the db
"reload" only means to simulate situation when there is a pending flush for an origin but a new cache is about to preload
which is a corner case happening rarely ; this helps check the next preload operation will load correct data from the database anyway
Case 1: set | flush + reload | remove | set | remove | flush + reload | get ?= null
checks coalescing optimization for single key changes does work correctly for repeated removals of a key
Case 2: set | set | clear | flush + reload | count ?= 0
checks whether clear operation superseeds setting keys, the database must be empty for the origin
Case 3: set | set | clear | reload | count ?= 0
check the corner case when a data clear flush is pending but a new cache is about to preload
*/
function startTest()
{
// Have an untouched land
localStorage.clear();
// Initial flush
localStorageFlush(function() {
is(localStorage.length, 0, "localStorage empty at the test start");
// Basic test 1 (set a key, check presence after reload):
localStorage.setItem("item", "value");
localStorageFlushAndReload(function() {
is(localStorage.getItem("item"), "value", "Basic persistance works");
is(localStorage.length, 1, "1 item in localStorage");
// Basic test 2 (set a key twice, check presence of the last value):
localStorage.setItem("item", "value2");
localStorage.setItem("item", "value3");
localStorageFlushAndReload(function() {
is(localStorage.getItem("item"), "value3", "Basic persistance 2 works");
// Basic test 3 (remove a key, check it has been deleted):
localStorage.removeItem("item");
localStorageFlushAndReload(function() {
is(localStorage.getItem("item"), null, "Basic delete works");
// Basic test 4 (set and remove a key, check it is not present):
localStorage.setItem("item", "value4");
localStorage.removeItem("item");
localStorageFlushAndReload(function() {
is(localStorage.getItem("item"), null, "Basic delete 2 works");
// Case 1:
localStorage.setItem("item", "value");
localStorageFlushAndReload(function() {
localStorage.removeItem("item");
localStorage.setItem("item", "value2");
localStorage.removeItem("item");
localStorageFlushAndReload(function() {
is(localStorage.getItem("item"), null, "Item deleted in case 1");
// Case 2:
localStorage.setItem("item", "value");
localStorage.setItem("item2", "value2");
localStorage.clear();
localStorageFlushAndReload(function() {
is(localStorage.length, 0, "localStorage clean in case 2");
// Case 3:
localStorageFlush(function() {
localStorage.setItem("item", "value");
localStorage.setItem("item2", "value2");
localStorage.clear();
localStorageReload();
is(localStorage.length, 0, "localStorage clean in case 4");
if (SpecialPowers.Cc["@mozilla.org/xre/app-info;1"].getService(
SpecialPowers.Ci.nsIXULRuntime).processType != SpecialPowers.Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
// Following tests cannot be run in a child/plugin process type
SimpleTest.finish();
return;
}
// Cookies clean 1
localStorageFlush(function() {
localStorage.setItem("item", "value");
localStorageClearAll();
is(localStorage.length, 0, "localStorage clean after cookies deletion");
localStorage.setItem("item2", "value2");
is(localStorage.getItem("item"), null, "Unexpected key 1, cookies delete");
is(localStorage.getItem("item2"), "value2", "Expected key 2, cookies delete");
localStorageFlushAndReload(function() {
is(localStorage.getItem("item"), null, "Unexpected key 1, cookies delete");
is(localStorage.getItem("item2"), "value2", "Expected key 2, cookies delete");
// Cookies clean 2
localStorage.clear();
localStorageFlush(function() {
localStorage.setItem("item", "value");
localStorageClearAll();
is(localStorage.length, 0, "localStorage clean after cookies deletion 2");
localStorageFlushAndReload(function() {
is(localStorage.length, 0, "localStorage clean after cookies deletion 2");
// Domain clean 1
localStorageFlush(function() {
localStorage.setItem("item", "value");
localStorageClearDomain("test");
is(localStorage.length, 0, "localStorage clean after domain deletion");
localStorage.setItem("item2", "value2");
is(localStorage.getItem("item"), null, "Unexpected key 1, domain delete");
is(localStorage.getItem("item2"), "value2", "Expected key 2, domain delete");
localStorageFlushAndReload(function() {
is(localStorage.getItem("item"), null, "Unexpected key 1, domain delete");
is(localStorage.getItem("item2"), "value2", "Expected key 2, domain delete");
// Domain clean 2
localStorage.clear();
localStorageFlush(function() {
localStorage.setItem("item", "value");
localStorageClearDomain("test");
is(localStorage.length, 0, "localStorage clean after domain deletion 2");
localStorageFlushAndReload(function() {
is(localStorage.length, 0, "localStorage clean after domain deletion 2");
SimpleTest.finish();
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
}
SimpleTest.waitForExplicitFinish();
</script>
</head>
<body onload="startTest();">
</body>
</html>

Просмотреть файл

@ -1,64 +0,0 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>bug 624047</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="interOriginTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript">
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
/*
This test does the folliwing:
- loads a page in an iframe that stores a value to localStorage
- sooner then in 5 seconds reloads the page
- now the page later then 5 seconds after load in the first step checks the
value is still present in localStorage (what is expected)
- if not, the bug is still present
*/
function flushTables()
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var storageManager = Components.classes["@mozilla.org/dom/storagemanager;1"]
.getService(Components.interfaces.nsIObserver);
storageManager.observe(null, "domstorage-flush-force", null);
}
function startTest()
{
slaveOrigin = "http://sub2.test2.example.org";
slave = document.getElementById("__test_frame").contentWindow;
flushTables();
slave.location = slaveOrigin + slavePath + "frameBug624047.html";
setTimeout(function() {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
slaveLoadsPending = 1;
slave.location.reload();
}, 2000);
}
function doNextTest()
{
SimpleTest.finish();
}
function doStep()
{
}
SimpleTest.waitForExplicitFinish();
</script>
</head>
<body onload="startTest();">
This test takes about 15s to complete... Please wait...
<br/>
<iframe src="" id="__test_frame"></iframe>
</body>
</html>

Просмотреть файл

@ -7,7 +7,31 @@
<script type="text/javascript">
var MOZ_STORAGE_CHANGED_COUNT = 21;
var expectedEvents = [
"empty,null,",
"empty,,null",
"key1,null,value1",
"key1,value1,null",
"key1,null,value1",
"key2,null,value2",
"key2,value2,value2-2",
"key1,value1,value1-2",
"key2,value2-2,null",
"testA,null,valueA",
"testA,valueA,valueA2",
"testB,null,valueB",
"testB,valueB,valueB2",
"testC,null,valueC",
"testC,valueC,valueC2",
"testC,valueC2,null",
"testC,null,null",
"testC,null,null",
"null,null,test",
"null,test,null",
"null,null,test",
"null,test,null",
"null,null,null"
];
function startTest()
{
@ -28,12 +52,14 @@ function startTest()
is(typeof localStorage["nonexisting2"], "undefined", "['nonexisting2'] is undefined");
is(typeof localStorage.nonexisting2, "undefined", "nonexisting2 is undefined");
var mozStorageChangedReceived = 0;
var localStorageCopy = localStorage;
function onStorageChanged(e) {
if (e.storageArea == localStorageCopy)
mozStorageChangedReceived++;
if (e.storageArea == localStorageCopy) {
ok(expectedEvents.length > 0, "Not more then expected events encountered");
var receivedEvent = e.key + "," + e.oldValue + "," + e.newValue;
is(receivedEvent, expectedEvents.shift(), "Expected event data: " + receivedEvent);
}
}
// Listen for MozStorageChanged
@ -204,8 +230,7 @@ function startTest()
SimpleTest.executeSoon(function () {
SpecialPowers.removeChromeEventListener("MozStorageChanged", onStorageChanged, false);
is(mozStorageChangedReceived, MOZ_STORAGE_CHANGED_COUNT,
"received the correct number of events");
is(expectedEvents.length, 0, "received the correct number of events");
localStorage.clear();
SimpleTest.finish();

Просмотреть файл

@ -14,12 +14,12 @@ function startTest()
.getService(Components.interfaces.nsIIOService);
var ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
.getService(Components.interfaces.nsIScriptSecurityManager);
var dsm = Components.classes["@mozilla.org/dom/storagemanager;1"]
var dsm = Components.classes["@mozilla.org/dom/localStorage-manager;1"]
.getService(Components.interfaces.nsIDOMStorageManager);
var uri = ios.newURI(url, "", null);
var principal = ssm.getNoAppCodebasePrincipal(uri);
var storage = dsm.getLocalStorageForPrincipal(principal, "");
var storage = dsm.createStorage(principal, "");
storage.setItem("chromekey", "chromevalue");

Просмотреть файл

@ -17,6 +17,8 @@ MOCHITEST_FILES = \
frameNotEqual.html \
file_http.html \
file_https.html \
test_cookieSession.html \
test_sessionStorageBaseSessionOnly.html \
test_sessionStorageBase.html \
test_sessionStorageClone.html \
test_sessionStorageReplace.html \

Просмотреть файл

@ -0,0 +1,98 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>cookie per-session only test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript">
/*
Set cookie access to be just per session and store to the sessionStorage.
Content stored must prevail only for session of the browser, so it must
be accessible in another window we try to access that key in the same
storage.
*/
function startTest()
{
var io = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
.getService(SpecialPowers.Ci.nsIIOService);
var uri = io.newURI(window.location, "", null);
var cp = SpecialPowers.Cc["@mozilla.org/cookie/permission;1"]
.getService(SpecialPowers.Ci.nsICookiePermission);
// ==================== start of the test ============================
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_DEFAULT);
sessionStorage.setItem("persistent1", "persistent value 1");
sessionStorage.setItem("persistent2", "persistent value 2");
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_SESSION);
sessionStorage.setItem("session only", "session value");
is(sessionStorage.getItem("session only"), "session value", "Value present when cookies in session-only mode");
is(sessionStorage.getItem("persistent1"), "persistent value 1", "Persistent value present");
is(sessionStorage.getItem("persistent2"), "persistent value 2", "Persistent value present");
sessionStorage.setItem("persistent1", "changed persistent value 1");
sessionStorage.removeItem("persistent2");
is(sessionStorage.getItem("session only"), "session value", "Value present when cookies in session-only mode");
is(sessionStorage.getItem("persistent1"), "changed persistent value 1", "Persistent value present");
is(sessionStorage.getItem("persistent2"), null, "Persistent value removed");
// This clear has to delete only changes made in session only mode
sessionStorage.clear();
is(sessionStorage.getItem("session only"), null, "Value not present when cookies in session-only mode after delete");
is(sessionStorage.getItem("persistent1"), null, "Persistent value not present in session only after delete");
is(sessionStorage.getItem("persistent2"), null, "Persistent value not present in session only after delete");
sessionStorage.setItem("session only 2", "must be deleted on drop of session-only cookies permissions");
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_DEFAULT);
is(sessionStorage.getItem("session only"), null, "No value when cookies are in default mode");
is(sessionStorage.getItem("session only 2"), null, "No value when cookies are in default mode");
is(sessionStorage.getItem("persistent1"), "persistent value 1", "Persistent value present");
is(sessionStorage.getItem("persistent2"), "persistent value 2", "Persistent value present");
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_SESSION);
is(sessionStorage.getItem("session only"), null, "Value not present when cookies in session-only mode after delete");
is(sessionStorage.getItem("session only 2"), null, "Value not present when cookies in session-only mode after delete");
is(sessionStorage.getItem("persistent1"), "persistent value 1", "Persistent value present again");
is(sessionStorage.getItem("persistent2"), "persistent value 2", "Persistent value present again");
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_DEFAULT);
sessionStorage.clear();
is(sessionStorage.getItem("session only"), null, "No value when cookies are in default mode");
is(sessionStorage.getItem("persistent1"), null, "Persistent value not present after delete");
is(sessionStorage.getItem("persistent2"), null, "Persistent value not present after delete");
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_SESSION);
is(sessionStorage.getItem("session only"), null, "Value not present when cookies in session-only mode after delete");
is(sessionStorage.getItem("session only 2"), null, "No value when cookies are in default mode");
is(sessionStorage.getItem("persistent1"), null, "Persistent value not present in session only after delete");
is(sessionStorage.getItem("persistent2"), null, "Persistent value not present in session only after delete");
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_DEFAULT);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
</script>
</head>
<body onload="startTest();">
</body>
</html>

Просмотреть файл

@ -7,7 +7,18 @@
<script type="text/javascript">
var MOZ_STORAGE_CHANGED_COUNT = 8;
var expectedEvents = [
"empty,null,",
"empty,,null",
"key1,null,value1",
"key1,value1,null",
"key1,null,value1",
"key2,null,value2",
"key2,value2,value2-2",
"key1,value1,value1-2",
"key2,value2-2,null",
"null,null,null"
];
function setup() {
sessionStorage.clear();
@ -37,8 +48,11 @@ function startTest()
var sessionStorageCopy = sessionStorage;
function onStorageChanged(e) {
if (e.storageArea == sessionStorageCopy)
mozStorageChangedReceived++;
if (e.storageArea == sessionStorageCopy) {
ok(expectedEvents.length > 0, "Not more then expected events encountered");
var receivedEvent = e.key + "," + e.oldValue + "," + e.newValue;
is(receivedEvent, expectedEvents.shift(), "Expected event data: " + receivedEvent);
}
}
// Listen for MozStorageChanged
@ -148,8 +162,7 @@ function startTest()
SimpleTest.executeSoon(function () {
SpecialPowers.removeChromeEventListener("MozStorageChanged", onStorageChanged, false);
is(mozStorageChangedReceived, MOZ_STORAGE_CHANGED_COUNT,
"received the correct number of events");
is(expectedEvents.length, 0, "received the correct number of events");
sessionStorage.clear();
SimpleTest.finish();

Просмотреть файл

@ -0,0 +1,214 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>sessionStorage basic test, while in sesison only mode</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript">
function startTest()
{
var io = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
.getService(SpecialPowers.Ci.nsIIOService);
var uri = io.newURI(window.location, "", null);
var cp = SpecialPowers.Cc["@mozilla.org/cookie/permission;1"]
.getService(SpecialPowers.Ci.nsICookiePermission);
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_SESSION);
// Initially check the sessionStorage is empty
is(sessionStorage.length, 0, "The storage is empty [1]");
is(sessionStorage.key(0), null, "key() should return null for out-of-bounds access");
is(sessionStorage.key(-1), null, "key() should return null for out-of-bounds access");
is(sessionStorage.key(1), null, "key() should return null for out-of-bounds access");
is(sessionStorage.getItem("nonexisting"), null, "Nonexisting item is null (getItem())");
is(sessionStorage["nonexisting"], undefined, "Nonexisting item is undefined (array access)");
is(sessionStorage.nonexisting, undefined, "Nonexisting item is undefined (property access)");
sessionStorage.removeItem("nonexisting"); // Just check there is no exception
is(typeof sessionStorage.getItem("nonexisting"), "object", "getItem('nonexisting') is object");
is(typeof sessionStorage["nonexisting"], "undefined", "['nonexisting'] is undefined");
is(typeof sessionStorage.nonexisting, "undefined", "nonexisting is undefined");
is(typeof sessionStorage.getItem("nonexisting2"), "object", "getItem('nonexisting2') is object");
is(typeof sessionStorage["nonexisting2"], "undefined", "['nonexisting2'] is undefined");
is(typeof sessionStorage.nonexisting2, "undefined", "nonexisting2 is undefined");
// add an empty-value key
sessionStorage.setItem("empty", "");
is(sessionStorage.getItem("empty"), "", "Empty value (getItem())");
is(sessionStorage["empty"], "", "Empty value (array access)");
is(sessionStorage.empty, "", "Empty value (property access)");
is(typeof sessionStorage.getItem("empty"), "string", "getItem('empty') is string");
is(typeof sessionStorage["empty"], "string", "['empty'] is string");
is(typeof sessionStorage.empty, "string", "empty is string");
sessionStorage.removeItem("empty");
is(sessionStorage.length, 0, "The storage has no keys");
is(sessionStorage.getItem("empty"), null, "empty item is null (getItem())");
is(sessionStorage["empty"], undefined, "empty item is undefined (array access)");
is(sessionStorage.empty, undefined, "empty item is undefined (property access)");
is(typeof sessionStorage.getItem("empty"), "object", "getItem('empty') is object");
is(typeof sessionStorage["empty"], "undefined", "['empty'] is undefined");
is(typeof sessionStorage.empty, "undefined", "empty is undefined");
// add one key, check it is there
sessionStorage.setItem("key1", "value1");
is(sessionStorage.length, 1, "The storage has one key-value pair");
is(sessionStorage.key(0), "key1");
is(sessionStorage.key(-1), null, "key() should return null for out-of-bounds access");
is(sessionStorage.key(1), null, "key() should return null for out-of-bounds access");
// check all access method give the correct result
// and are of the correct type
is(sessionStorage.getItem("key1"), "value1", "getItem('key1') == value1");
is(sessionStorage["key1"], "value1", "['key1'] == value1");
is(sessionStorage.key1, "value1", "key1 == value1");
is(typeof sessionStorage.getItem("key1"), "string", "getItem('key1') is string");
is(typeof sessionStorage["key1"], "string", "['key1'] is string");
is(typeof sessionStorage.key1, "string", "key1 is string");
// remove the previously added key and check the storage is empty
sessionStorage.removeItem("key1");
is(sessionStorage.length, 0, "The storage is empty [2]");
is(sessionStorage.key(0), null, "key() should return null for out-of-bounds access");
is(sessionStorage.getItem("key1"), null, "\'key1\' removed");
is(typeof sessionStorage.getItem("key1"), "object", "getItem('key1') is object");
is(typeof sessionStorage["key1"], "undefined", "['key1'] is undefined");
is(typeof sessionStorage.key1, "undefined", "key1 is undefined");
// add one key, check it is there
sessionStorage.setItem("key1", "value1");
is(sessionStorage.length, 1, "The storage has one key-value pair");
is(sessionStorage.key(0), "key1");
is(sessionStorage.getItem("key1"), "value1");
// add a second key
sessionStorage.setItem("key2", "value2");
is(sessionStorage.length, 2, "The storage has two key-value pairs");
is(sessionStorage.getItem("key1"), "value1");
is(sessionStorage.getItem("key2"), "value2");
var firstKey = sessionStorage.key(0);
var secondKey = sessionStorage.key(1);
ok((firstKey == 'key1' && secondKey == 'key2') ||
(firstKey == 'key2' && secondKey == 'key1'),
'key() API works.');
// change the second key
sessionStorage.setItem("key2", "value2-2");
is(sessionStorage.length, 2, "The storage has two key-value pairs");
is(sessionStorage.key(0), firstKey); // After key value changes the order must be preserved
is(sessionStorage.key(1), secondKey);
is(sessionStorage.key(-1), null, "key() should return null for out-of-bounds access");
is(sessionStorage.key(2), null, "key() should return null for out-of-bounds access");
is(sessionStorage.getItem("key1"), "value1");
is(sessionStorage.getItem("key2"), "value2-2");
// change the first key
sessionStorage.setItem("key1", "value1-2");
is(sessionStorage.length, 2, "The storage has two key-value pairs");
is(sessionStorage.key(0), firstKey); // After key value changes the order must be preserved
is(sessionStorage.key(1), secondKey);
is(sessionStorage.key(-1), null, "key() should return null for out-of-bounds access");
is(sessionStorage.key(2), null, "key() should return null for out-of-bounds access");
is(sessionStorage.getItem("key1"), "value1-2");
is(sessionStorage.getItem("key2"), "value2-2");
// remove the second key
sessionStorage.removeItem("key2");
is(sessionStorage.length, 1, "The storage has one key-value pair");
is(sessionStorage.key(0), "key1");
is(sessionStorage.key(-1), null, "key() should return null for out-of-bounds access");
is(sessionStorage.key(1), null, "key() should return null for out-of-bounds access");
is(sessionStorage.getItem("key1"), "value1-2");
// JS property test
sessionStorage.testA = "valueA";
is(sessionStorage.testA, "valueA");
is(sessionStorage["testA"], "valueA");
is(sessionStorage.getItem("testA"), "valueA");
sessionStorage.testA = "valueA2";
is(sessionStorage.testA, "valueA2");
is(sessionStorage["testA"], "valueA2");
is(sessionStorage.getItem("testA"), "valueA2");
sessionStorage["testB"] = "valueB";
is(sessionStorage.testB, "valueB");
is(sessionStorage["testB"], "valueB");
is(sessionStorage.getItem("testB"), "valueB");
sessionStorage["testB"] = "valueB2";
is(sessionStorage.testB, "valueB2");
is(sessionStorage["testB"], "valueB2");
is(sessionStorage.getItem("testB"), "valueB2");
sessionStorage.setItem("testC", "valueC");
is(sessionStorage.testC, "valueC");
is(sessionStorage["testC"], "valueC");
is(sessionStorage.getItem("testC"), "valueC");
sessionStorage.setItem("testC", "valueC2");
is(sessionStorage.testC, "valueC2");
is(sessionStorage["testC"], "valueC2");
is(sessionStorage.getItem("testC"), "valueC2");
sessionStorage.setItem("testC", null);
is("testC" in sessionStorage, true);
is(sessionStorage.getItem("testC"), "null");
is(sessionStorage["testC"], "null");
is(sessionStorage.testC, "null");
sessionStorage.removeItem("testC");
sessionStorage["testC"] = null;
is("testC" in sessionStorage, true);
is(sessionStorage.getItem("testC"), "null");
is(sessionStorage["testC"], "null");
is(sessionStorage.testC, "null");
sessionStorage.setItem(null, "test");
is("null" in sessionStorage, true);
is(sessionStorage.getItem("null"), "test");
is(sessionStorage.getItem(null), "test");
is(sessionStorage["null"], "test");
sessionStorage.removeItem(null, "test");
// bug 350023
todo_is("null" in sessionStorage, false);
sessionStorage.setItem(null, "test");
is("null" in sessionStorage, true);
sessionStorage.removeItem("null", "test");
// bug 350023
todo_is("null" in sessionStorage, false);
// Clear the storage
sessionStorage.clear();
is(sessionStorage.length, 0, "The storage is empty [3]");
is(sessionStorage.key(0), null, "key() should return null for out-of-bounds access");
is(sessionStorage.key(-1), null, "key() should return null for out-of-bounds access");
is(sessionStorage.key(1), null, "key() should return null for out-of-bounds access");
is(sessionStorage.getItem("nonexisting"), null, "Nonexisting item is null");
is(sessionStorage.getItem("key1"), null, "key1 removed");
is(sessionStorage.getItem("key2"), null, "key2 removed");
sessionStorage.removeItem("nonexisting"); // Just check there is no exception
sessionStorage.removeItem("key1"); // Just check there is no exception
sessionStorage.removeItem("key2"); // Just check there is no exception
cp.setAccess(uri, SpecialPowers.Ci.nsICookiePermission.ACCESS_DEFAULT);
sessionStorage.clear();
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
</script>
</head>
<body onload="startTest();">
</body>
</html>

Просмотреть файл

@ -48,9 +48,8 @@
#include "nsIWindowProvider.h"
#include "nsIMutableArray.h"
#include "nsISupportsArray.h"
#include "nsIDOMStorageObsolete.h"
#include "nsIDOMStorage.h"
#include "nsPIDOMStorage.h"
#include "nsIDOMStorageManager.h"
#include "nsIWidget.h"
#include "nsFocusManager.h"
#include "nsIPresShell.h"
@ -1000,17 +999,14 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow *aParent,
parentDocShell = piWindow->GetDocShell();
if (subjectPrincipal && parentDocShell) {
nsCOMPtr<nsIDOMStorage> storage;
parentDocShell->GetSessionStorageForPrincipal(subjectPrincipal,
EmptyString(), false,
getter_AddRefs(storage));
nsCOMPtr<nsPIDOMStorage> piStorage =
do_QueryInterface(storage);
if (piStorage){
storage = piStorage->Clone();
newDocShell->AddSessionStorage(
piStorage->Principal(),
storage);
nsCOMPtr<nsIDOMStorageManager> parentStorageManager = do_QueryInterface(parentDocShell);
nsCOMPtr<nsIDOMStorageManager> newStorageManager = do_QueryInterface(newDocShell);
if (parentStorageManager && newStorageManager) {
nsCOMPtr<nsIDOMStorage> storage;
parentStorageManager->GetStorage(subjectPrincipal, isPrivateBrowsingWindow, getter_AddRefs(storage));
if (storage)
newStorageManager->CloneStorage(storage);
}
}

Просмотреть файл

@ -119,7 +119,6 @@ members = [
'nsIDOMStorage.key',
'nsIDOMStorage.removeItem',
'nsIDOMStorage.clear',
'nsIDOMStorageItem.value',
# dom/interfaces/xpath
'nsIDOMXPathExpression.evaluate',

Просмотреть файл

@ -63,17 +63,13 @@
#define NS_CANVASRENDERINGCONTEXTWEBGL_CID \
{ 0x2fe88332, 0x31c6, 0x4829, { 0xb2, 0x47, 0xa0, 0x7d, 0x8a, 0x7e, 0xe8, 0x0fe } }
// {8b449142-1eab-4bfa-9830-fab6ebb09774}
#define NS_DOMSTORAGE_CID \
{ 0x8b449142, 0x1eab, 0x4bfa, { 0x98, 0x30, 0xfa, 0xb6, 0xeb, 0xb0, 0x97, 0x74 } }
// {A746DECD-AE74-4d86-8E75-4FDA81A9BE90}
#define NS_DOMSESSIONSTORAGEMANAGER_CID \
{ 0xa746decd, 0xae74, 0x4d86, { 0x8e, 0x75, 0x4f, 0xda, 0x81, 0xa9, 0xbe, 0x90 } }
// {27AECC62-7777-428e-B34C-5973A47B8298}
#define NS_DOMSTORAGE2_CID \
{ 0x27aecc62, 0x7777, 0x428e, { 0xb3, 0x4c, 0x59, 0x73, 0xa4, 0x7b, 0x82, 0x98 } }
// {b88a4712-eb52-4c10-9b85-bf5894b510f0}
#define NS_DOMSTORAGEMANAGER_CID \
{ 0xb88a4712, 0xeb52, 0x4c10, { 0x9b, 0x85, 0xbf, 0x58, 0x94, 0xb5, 0x10, 0xf0 } }
// {656DB07C-AA80-49e4-BCE8-E431BAAE697D}
#define NS_DOMLOCALSTORAGEMANAGER_CID \
{ 0x656db07c, 0xaa80, 0x49e4, { 0xbc, 0xe8, 0xe4, 0x31, 0xba, 0xae, 0x69, 0x7d } }
// {93ad72a6-02cd-4716-9626-d47d5ec275ec}
#define NS_DOMJSON_CID \

Просмотреть файл

@ -80,7 +80,7 @@
#include "nsScriptNameSpaceManager.h"
#include "nsIControllerContext.h"
#include "nsDOMScriptObjectFactory.h"
#include "nsDOMStorage.h"
#include "DOMStorageManager.h"
#include "nsJSON.h"
#include "mozIApplicationClearPrivateDataParams.h"
#include "mozilla/Attributes.h"
@ -275,8 +275,8 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsBlobProtocolHandler)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsMediaStreamProtocolHandler)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsHostObjectURI)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMParser)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsDOMStorageManager,
nsDOMStorageManager::GetInstance)
NS_GENERIC_FACTORY_CONSTRUCTOR(DOMSessionStorageManager)
NS_GENERIC_FACTORY_CONSTRUCTOR(DOMLocalStorageManager)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsChannelPolicy)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(IndexedDatabaseManager,
IndexedDatabaseManager::FactoryCreate)
@ -768,8 +768,8 @@ NS_DEFINE_NAMED_CID(NS_XMLHTTPREQUEST_CID);
NS_DEFINE_NAMED_CID(NS_EVENTSOURCE_CID);
NS_DEFINE_NAMED_CID(NS_DOMACTIVITY_CID);
NS_DEFINE_NAMED_CID(NS_DOMPARSER_CID);
NS_DEFINE_NAMED_CID(NS_DOMSTORAGE2_CID);
NS_DEFINE_NAMED_CID(NS_DOMSTORAGEMANAGER_CID);
NS_DEFINE_NAMED_CID(NS_DOMSESSIONSTORAGEMANAGER_CID);
NS_DEFINE_NAMED_CID(NS_DOMLOCALSTORAGEMANAGER_CID);
NS_DEFINE_NAMED_CID(NS_DOMJSON_CID);
NS_DEFINE_NAMED_CID(NS_TEXTEDITOR_CID);
NS_DEFINE_NAMED_CID(INDEXEDDB_MANAGER_CID);
@ -1054,8 +1054,8 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
{ &kNS_EVENTSOURCE_CID, false, NULL, EventSourceConstructor },
{ &kNS_DOMACTIVITY_CID, false, NULL, ActivityConstructor },
{ &kNS_DOMPARSER_CID, false, NULL, nsDOMParserConstructor },
{ &kNS_DOMSTORAGE2_CID, false, NULL, NS_NewDOMStorage2 },
{ &kNS_DOMSTORAGEMANAGER_CID, false, NULL, nsDOMStorageManagerConstructor },
{ &kNS_DOMSESSIONSTORAGEMANAGER_CID, false, NULL, DOMSessionStorageManagerConstructor },
{ &kNS_DOMLOCALSTORAGEMANAGER_CID, false, NULL, DOMLocalStorageManagerConstructor },
{ &kNS_DOMJSON_CID, false, NULL, NS_NewJSON },
{ &kNS_TEXTEDITOR_CID, false, NULL, nsPlaintextEditorConstructor },
{ &kINDEXEDDB_MANAGER_CID, false, NULL, IndexedDatabaseManagerConstructor },
@ -1204,8 +1204,8 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
{ NS_EVENTSOURCE_CONTRACTID, &kNS_EVENTSOURCE_CID },
{ NS_DOMACTIVITY_CONTRACTID, &kNS_DOMACTIVITY_CID },
{ NS_DOMPARSER_CONTRACTID, &kNS_DOMPARSER_CID },
{ "@mozilla.org/dom/storage;2", &kNS_DOMSTORAGE2_CID },
{ "@mozilla.org/dom/storagemanager;1", &kNS_DOMSTORAGEMANAGER_CID },
{ "@mozilla.org/dom/localStorage-manager;1", &kNS_DOMLOCALSTORAGEMANAGER_CID },
{ "@mozilla.org/dom/sessionStorage-manager;1", &kNS_DOMSESSIONSTORAGEMANAGER_CID },
{ "@mozilla.org/dom/json;1", &kNS_DOMJSON_CID },
{ "@mozilla.org/editor/texteditor;1", &kNS_TEXTEDITOR_CID },
{ INDEXEDDB_MANAGER_CONTRACTID, &kINDEXEDDB_MANAGER_CID },

Просмотреть файл

@ -41,7 +41,6 @@
#include "nsXBLWindowKeyHandler.h"
#include "nsXBLService.h"
#include "txMozillaXSLTProcessor.h"
#include "nsDOMStorage.h"
#include "nsTreeSanitizer.h"
#include "nsCellMap.h"
#include "nsTextFrameTextRunCache.h"
@ -59,7 +58,7 @@
#include "nsMathMLAtoms.h"
#include "nsMathMLOperators.h"
#include "Navigator.h"
#include "nsDOMStorageBaseDB.h"
#include "DOMStorageObserver.h"
#include "DisplayItemClip.h"
#include "AudioChannelService.h"
@ -217,9 +216,9 @@ nsLayoutStatics::Initialize()
return rv;
}
rv = nsDOMStorageManager::Initialize();
rv = DOMStorageObserver::Init();
if (NS_FAILED(rv)) {
NS_ERROR("Could not initialize nsDOMStorageManager");
NS_ERROR("Could not initialize DOMStorageObserver");
return rv;
}
@ -272,8 +271,6 @@ nsLayoutStatics::Initialize()
nsCookieService::AppClearDataObserverInit();
nsApplicationCacheService::AppClearDataObserverInit();
nsDOMStorageBaseDB::Init();
InitializeDateCacheCleaner();
return NS_OK;
@ -290,7 +287,7 @@ nsLayoutStatics::Shutdown()
#ifdef MOZ_XUL
nsXULPopupManager::Shutdown();
#endif
nsDOMStorageManager::Shutdown();
DOMStorageObserver::Shutdown();
txMozillaXSLTProcessor::Shutdown();
Attr::Shutdown();
nsEventListenerManager::Shutdown();

Просмотреть файл

@ -411,7 +411,9 @@
"dom/tests/mochitest/localstorage/test_localStorageReplace.html":"",
"dom/tests/mochitest/pointerlock/test_pointerlock-api.html":"",
"dom/tests/mochitest/sessionstorage/test_cookieSession.html":"",
"dom/tests/mochitest/sessionstorage/test_sessionStorageBase.html":"",
"dom/tests/mochitest/sessionstorage/test_sessionStorageBaseSessionOnly.html":"",
"dom/tests/mochitest/sessionstorage/test_sessionStorageClone.html":"",
"dom/tests/mochitest/sessionstorage/test_sessionStorageHttpHttps.html":"",
"dom/tests/mochitest/sessionstorage/test_sessionStorageReplace.html":"",

Просмотреть файл

@ -2550,6 +2550,24 @@
"kind": "boolean",
"description": "DOM: Ranges that are detached on destruction (bug 702948)"
},
"LOCALDOMSTORAGE_INIT_DATABASE_MS": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Time to open the localStorage database (ms)"
},
"LOCALDOMSTORAGE_SHUTDOWN_DATABASE_MS": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Time to flush and close the localStorage database (ms)"
},
"LOCALDOMSTORAGE_PRELOAD_PENDING_ON_FIRST_ACCESS": {
"kind": "boolean",
"description": "True when we had to wait for a pending preload on first access to localStorage data, false otherwise"
},
"LOCALDOMSTORAGE_GETALLKEYS_MS": {
"kind": "exponential",
"high": "3000",
@ -2557,6 +2575,41 @@
"extended_statistics_ok": true,
"description": "Time to return a list of all keys in domain's LocalStorage (ms)"
},
"LOCALDOMSTORAGE_GETALLKEYS_BLOCKING_MS": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Time to block before we return a list of all keys in domain's LocalStorage (ms)"
},
"LOCALDOMSTORAGE_GETKEY_MS": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Time to return a key name in domain's LocalStorage (ms)"
},
"LOCALDOMSTORAGE_GETKEY_BLOCKING_MS": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Time to block before we return a key name in domain's LocalStorage (ms)"
},
"LOCALDOMSTORAGE_GETLENGTH_MS": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Time to return number of keys in domain's LocalStorage (ms)"
},
"LOCALDOMSTORAGE_GETLENGTH_BLOCKING_MS": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Time to block before we return number of keys in domain's LocalStorage (ms)"
},
"LOCALDOMSTORAGE_GETVALUE_MS": {
"kind": "exponential",
"high": "3000",
@ -2564,6 +2617,13 @@
"extended_statistics_ok": true,
"description": "Time to return a value for a key in LocalStorage (ms)"
},
"LOCALDOMSTORAGE_GETVALUE_BLOCKING_MS": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Time to block before we return a value for a key in LocalStorage (ms)"
},
"LOCALDOMSTORAGE_SETVALUE_MS": {
"kind": "exponential",
"high": "3000",
@ -2571,6 +2631,13 @@
"extended_statistics_ok": true,
"description": "Time to set a single key's value in LocalStorage (ms)"
},
"LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Time to block before we set a single key's value in LocalStorage (ms)"
},
"LOCALDOMSTORAGE_REMOVEKEY_MS": {
"kind": "exponential",
"high": "3000",
@ -2578,33 +2645,40 @@
"extended_statistics_ok": true,
"description": "Time to remove a single key from LocalStorage (ms)"
},
"LOCALDOMSTORAGE_REMOVEALL_MS": {
"LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Time to block before we remove a single key from LocalStorage (ms)"
},
"LOCALDOMSTORAGE_CLEAR_MS": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Time to clear LocalStorage for all domains (ms)"
},
"LOCALDOMSTORAGE_FETCH_DOMAIN_MS": {
"LOCALDOMSTORAGE_CLEAR_BLOCKING_MS": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Time to fetch LocalStorage data for a domain (ms)"
"description": "Time to block before we clear LocalStorage for all domains (ms)"
},
"LOCALDOMSTORAGE_FETCH_QUOTA_USE_MS": {
"LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Time to fetch quota use stats for a TLD (ms)"
"description": "Time to fetch LocalStorage data before we can clean the cache (ms)"
},
"LOCALDOMSTORAGE_TIMER_FLUSH_MS": {
"LOCALDOMSTORAGE_SESSIONONLY_PRELOAD_BLOCKING_MS": {
"kind": "exponential",
"high": "3000",
"n_buckets": 10,
"extended_statistics_ok": true,
"description": "Time to flush dirty entries from the cache (ms)"
"description": "Time to fetch LocalStorage data before we can expose them as session only data (ms)"
},
"LOCALDOMSTORAGE_KEY_SIZE_BYTES": {
"kind": "exponential",

Просмотреть файл

@ -50,7 +50,7 @@ let initTable = [
["search", "@mozilla.org/browser/search-service;1", "nsIBrowserSearchService"],
#endif
["storage", "@mozilla.org/storage/service;1", "mozIStorageService"],
["domStorageManager", "@mozilla.org/dom/storagemanager;1", "nsIDOMStorageManager"],
["domStorageManager", "@mozilla.org/dom/localStorage-manager;1", "nsIDOMStorageManager"],
["strings", "@mozilla.org/intl/stringbundle;1", "nsIStringBundleService"],
["telemetry", "@mozilla.org/base/telemetry;1", "nsITelemetry"],
["tm", "@mozilla.org/thread-manager;1", "nsIThreadManager"],

Просмотреть файл

@ -659,9 +659,9 @@ function test_storage_cleared()
let principal = Cc["@mozilla.org/scriptsecuritymanager;1"].
getService(Ci.nsIScriptSecurityManager).
getNoAppCodebasePrincipal(aURI);
let dsm = Cc["@mozilla.org/dom/storagemanager;1"].
let dsm = Cc["@mozilla.org/dom/localStorage-manager;1"].
getService(Ci.nsIDOMStorageManager);
return dsm.getLocalStorageForPrincipal(principal, "");
return dsm.createStorage(principal, "");
}
let s = [

Просмотреть файл

@ -500,6 +500,12 @@
ERROR(NS_ERROR_DOM_RETVAL_UNDEFINED, FAILURE(1013)),
ERROR(NS_ERROR_DOM_QUOTA_REACHED, FAILURE(1014)),
ERROR(NS_ERROR_DOM_JS_EXCEPTION, FAILURE(1015)),
/* May be used to indicate when e.g. setting a property value didn't
* actually change the value, like for obj.foo = "bar"; obj.foo = "bar";
* the second assignment throws NS_SUCCESS_DOM_NO_OPERATION.
*/
ERROR(NS_SUCCESS_DOM_NO_OPERATION, SUCCESS(1)),
#undef MODULE