mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 15:23:51 +00:00
Bug 422526 - implement localStorage, p=Honza Bambas+Dave Camp, r=jst+dcamp+bz
This commit is contained in:
parent
8a5e821be1
commit
57c667fe6b
@ -1792,7 +1792,7 @@ nsDocShell::GetSessionStorageForURI(nsIURI* aURI,
|
||||
nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(newstorage);
|
||||
if (!pistorage)
|
||||
return NS_ERROR_FAILURE;
|
||||
pistorage->Init(NS_ConvertUTF8toUTF16(currentDomain), PR_FALSE);
|
||||
pistorage->InitAsSessionStorage(aURI);
|
||||
|
||||
if (!mStorages.Put(currentDomain, newstorage))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
@ -1210,6 +1210,15 @@ static nsDOMClassInfoData sClassInfoData[] = {
|
||||
nsIXPCScriptable::WANT_DELPROPERTY |
|
||||
nsIXPCScriptable::DONT_ENUM_STATIC_PROPS |
|
||||
nsIXPCScriptable::WANT_NEWENUMERATE)
|
||||
|
||||
NS_DEFINE_CLASSINFO_DATA(Storage2, nsStorage2SH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS |
|
||||
nsIXPCScriptable::WANT_NEWRESOLVE |
|
||||
nsIXPCScriptable::WANT_GETPROPERTY |
|
||||
nsIXPCScriptable::WANT_SETPROPERTY |
|
||||
nsIXPCScriptable::WANT_DELPROPERTY |
|
||||
nsIXPCScriptable::DONT_ENUM_STATIC_PROPS |
|
||||
nsIXPCScriptable::WANT_NEWENUMERATE)
|
||||
NS_DEFINE_CLASSINFO_DATA(StorageList, nsStorageListSH,
|
||||
ARRAY_SCRIPTABLE_FLAGS)
|
||||
NS_DEFINE_CLASSINFO_DATA(StorageItem, nsDOMGenericSH,
|
||||
@ -3432,6 +3441,10 @@ nsDOMClassInfo::Init()
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorage)
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(Storage2, nsIDOMStorage2)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorage2)
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(StorageList, nsIDOMStorageList)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageList)
|
||||
DOM_CLASSINFO_MAP_END
|
||||
@ -10495,6 +10508,208 @@ nsStorageSH::NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
}
|
||||
|
||||
|
||||
// Storage2SH
|
||||
|
||||
// One reason we need a newResolve hook is that in order for
|
||||
// enumeration of storage object keys to work the keys we're
|
||||
// enumerating need to exist on the storage object for the JS engine
|
||||
// to find them.
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsStorage2SH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj, jsval id, PRUint32 flags,
|
||||
JSObject **objp, PRBool *_retval)
|
||||
{
|
||||
JSObject *realObj;
|
||||
wrapper->GetJSObject(&realObj);
|
||||
|
||||
// First check to see if the property is defined on our prototype,
|
||||
// after converting id to a string if it's an integer.
|
||||
|
||||
JSString *jsstr = JS_ValueToString(cx, id);
|
||||
if (!jsstr) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSObject *proto = ::JS_GetPrototype(cx, realObj);
|
||||
JSBool hasProp;
|
||||
|
||||
if (proto &&
|
||||
(::JS_HasUCProperty(cx, proto, ::JS_GetStringChars(jsstr),
|
||||
::JS_GetStringLength(jsstr), &hasProp) &&
|
||||
hasProp)) {
|
||||
// We found the property we're resolving on the prototype,
|
||||
// nothing left to do here then.
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We're resolving property that doesn't exist on the prototype,
|
||||
// check if the key exists in the storage object.
|
||||
|
||||
nsCOMPtr<nsIDOMStorage2> storage(do_QueryWrappedNative(wrapper));
|
||||
|
||||
// GetItem() will return null if the caller can't access the session
|
||||
// storage item.
|
||||
nsAutoString data;
|
||||
nsresult rv = storage->GetItem(nsDependentJSString(jsstr), data);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!DOMStringIsNull(data)) {
|
||||
if (!::JS_DefineUCProperty(cx, realObj, ::JS_GetStringChars(jsstr),
|
||||
::JS_GetStringLength(jsstr), JSVAL_VOID, nsnull,
|
||||
nsnull, 0)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
*objp = realObj;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsStorage2SH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj, jsval id, jsval *vp, PRBool *_retval)
|
||||
{
|
||||
nsCOMPtr<nsIDOMStorage2> storage(do_QueryWrappedNative(wrapper));
|
||||
NS_ENSURE_TRUE(storage, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsAutoString val;
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (JSVAL_IS_STRING(id)) {
|
||||
// For native wrappers, do not get random names on storage objects.
|
||||
if (ObjectIsNativeWrapper(cx, obj)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
rv = storage->GetItem(nsDependentJSString(id), val);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
PRInt32 n = GetArrayIndexFromId(cx, id);
|
||||
NS_ENSURE_TRUE(n >= 0, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
rv = storage->Key(n, val);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
if (DOMStringIsNull(val)) {
|
||||
*vp = JSVAL_NULL;
|
||||
}
|
||||
else {
|
||||
JSString *str =
|
||||
::JS_NewUCStringCopyN(cx, reinterpret_cast<const jschar *>(val.get()),
|
||||
val.Length());
|
||||
NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
*vp = STRING_TO_JSVAL(str);
|
||||
}
|
||||
|
||||
return NS_SUCCESS_I_DID_SOMETHING;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsStorage2SH::SetProperty(nsIXPConnectWrappedNative *wrapper,
|
||||
JSContext *cx, JSObject *obj, jsval id,
|
||||
jsval *vp, PRBool *_retval)
|
||||
{
|
||||
nsCOMPtr<nsIDOMStorage2> storage(do_QueryWrappedNative(wrapper));
|
||||
NS_ENSURE_TRUE(storage, NS_ERROR_UNEXPECTED);
|
||||
|
||||
JSString *key = ::JS_ValueToString(cx, id);
|
||||
NS_ENSURE_TRUE(key, NS_ERROR_UNEXPECTED);
|
||||
|
||||
JSString *value = ::JS_ValueToString(cx, *vp);
|
||||
NS_ENSURE_TRUE(value, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsresult rv = storage->SetItem(nsDependentJSString(key),
|
||||
nsDependentJSString(value));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = NS_SUCCESS_I_DID_SOMETHING;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsStorage2SH::DelProperty(nsIXPConnectWrappedNative *wrapper,
|
||||
JSContext *cx, JSObject *obj, jsval id,
|
||||
jsval *vp, PRBool *_retval)
|
||||
{
|
||||
nsCOMPtr<nsIDOMStorage2> storage(do_QueryWrappedNative(wrapper));
|
||||
NS_ENSURE_TRUE(storage, NS_ERROR_UNEXPECTED);
|
||||
|
||||
JSString *key = ::JS_ValueToString(cx, id);
|
||||
NS_ENSURE_TRUE(key, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsresult rv = storage->RemoveItem(nsDependentJSString(key));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = NS_SUCCESS_I_DID_SOMETHING;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsStorage2SH::NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj, PRUint32 enum_op, jsval *statep,
|
||||
jsid *idp, PRBool *_retval)
|
||||
{
|
||||
nsTArray<nsString> *keys =
|
||||
(nsTArray<nsString> *)JSVAL_TO_PRIVATE(*statep);
|
||||
|
||||
switch (enum_op) {
|
||||
case JSENUMERATE_INIT:
|
||||
{
|
||||
nsCOMPtr<nsPIDOMStorage> storage(do_QueryWrappedNative(wrapper));
|
||||
|
||||
// XXXndeakin need to free the keys afterwards
|
||||
keys = storage->GetKeys();
|
||||
NS_ENSURE_TRUE(keys, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
*statep = PRIVATE_TO_JSVAL(keys);
|
||||
|
||||
if (idp) {
|
||||
*idp = INT_TO_JSVAL(keys->Length());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JSENUMERATE_NEXT:
|
||||
if (keys->Length() != 0) {
|
||||
nsString& key = keys->ElementAt(0);
|
||||
JSString *str =
|
||||
JS_NewUCStringCopyN(cx, reinterpret_cast<const jschar *>
|
||||
(key.get()),
|
||||
key.Length());
|
||||
NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
JS_ValueToId(cx, STRING_TO_JSVAL(str), idp);
|
||||
|
||||
keys->RemoveElementAt(0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Fall through
|
||||
case JSENUMERATE_DESTROY:
|
||||
delete keys;
|
||||
|
||||
*statep = JSVAL_NULL;
|
||||
|
||||
break;
|
||||
default:
|
||||
NS_NOTREACHED("Bad call from the JS engine");
|
||||
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// StorageList scriptable helper
|
||||
|
||||
nsISupports*
|
||||
|
@ -1478,6 +1478,38 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class nsStorage2SH : public nsDOMGenericSH
|
||||
{
|
||||
protected:
|
||||
nsStorage2SH(nsDOMClassInfoData* aData) : nsDOMGenericSH(aData)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~nsStorage2SH()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj, jsval id, PRUint32 flags,
|
||||
JSObject **objp, PRBool *_retval);
|
||||
NS_IMETHOD SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj, jsval id, jsval *vp, PRBool *_retval);
|
||||
NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj, jsval id, jsval *vp, PRBool *_retval);
|
||||
NS_IMETHOD DelProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj, jsval id, jsval *vp, PRBool *_retval);
|
||||
NS_IMETHOD NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj, PRUint32 enum_op, jsval *statep,
|
||||
jsid *idp, PRBool *_retval);
|
||||
|
||||
public:
|
||||
static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
|
||||
{
|
||||
return new nsStorage2SH(aData);
|
||||
}
|
||||
};
|
||||
|
||||
class nsStorageListSH : public nsNamedArraySH
|
||||
{
|
||||
protected:
|
||||
|
@ -384,6 +384,7 @@ enum nsDOMClassInfoID {
|
||||
|
||||
// WhatWG WebApps Objects
|
||||
eDOMClassInfo_Storage_id,
|
||||
eDOMClassInfo_Storage2_id,
|
||||
eDOMClassInfo_StorageList_id,
|
||||
eDOMClassInfo_StorageItem_id,
|
||||
eDOMClassInfo_StorageEvent_id,
|
||||
|
@ -1664,6 +1664,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
||||
|
||||
mDocument = do_QueryInterface(aDocument);
|
||||
mDoc = aDocument;
|
||||
mLocalStorage = nsnull;
|
||||
|
||||
#ifdef DEBUG
|
||||
mLastOpenedURI = aDocument->GetDocumentURI();
|
||||
@ -6841,6 +6842,41 @@ nsGlobalWindow::GetGlobalStorage(nsIDOMStorageList ** aGlobalStorage)
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGlobalWindow::GetLocalStorage(nsIDOMStorage2 ** aLocalStorage)
|
||||
{
|
||||
FORWARD_TO_INNER(GetLocalStorage, (aLocalStorage), NS_ERROR_UNEXPECTED);
|
||||
|
||||
NS_ENSURE_ARG(aLocalStorage);
|
||||
|
||||
if (!mLocalStorage) {
|
||||
*aLocalStorage = nsnull;
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsIPrincipal *principal = GetPrincipal();
|
||||
if (!principal)
|
||||
return NS_OK;
|
||||
|
||||
PRPackedBool sessionOnly;
|
||||
if (!nsDOMStorage::CanUseStorage(&sessionOnly))
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
|
||||
if (sessionOnly)
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
|
||||
nsCOMPtr<nsIDOMStorageManager> storageManager =
|
||||
do_GetService("@mozilla.org/dom/storagemanager;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = storageManager->GetLocalStorageForPrincipal(principal, getter_AddRefs(mLocalStorage));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
NS_ADDREF(*aLocalStorage = mLocalStorage);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
// nsGlobalWindow::nsIInterfaceRequestor
|
||||
//*****************************************************************************
|
||||
@ -6985,7 +7021,7 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
PR_FALSE,
|
||||
getter_AddRefs(storage));
|
||||
|
||||
if (storage != aSubject) {
|
||||
if (!SameCOMIdentity(storage, aSubject)) {
|
||||
// A sessionStorage object changed, but not our session storage
|
||||
// object.
|
||||
return NS_OK;
|
||||
@ -7008,8 +7044,8 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!nsDOMStorageList::CanAccessDomain(nsDependentString(aData),
|
||||
NS_ConvertASCIItoUTF16(currentDomain))) {
|
||||
if (!nsDOMStorageList::CanAccessDomain(NS_ConvertUTF16toUTF8(aData),
|
||||
currentDomain)) {
|
||||
// This window can't reach the global storage object for the
|
||||
// domain for which the change happened, so don't fire any
|
||||
// events in this window.
|
||||
|
@ -710,6 +710,8 @@ protected:
|
||||
nsCOMPtr<nsIDOMCrypto> mCrypto;
|
||||
nsCOMPtr<nsIDOMPkcs11> mPkcs11;
|
||||
|
||||
nsCOMPtr<nsIDOMStorage2> mLocalStorage;
|
||||
|
||||
nsCOMPtr<nsISupports> mInnerWindowHolders[NS_STID_ARRAY_UBOUND];
|
||||
nsCOMPtr<nsIPrincipal> mOpenerScriptPrincipal; // strong; used to determine
|
||||
// whether to clear scope
|
||||
|
@ -52,6 +52,7 @@ EXPORTS = \
|
||||
|
||||
XPIDLSRCS = \
|
||||
nsIDOMToString.idl \
|
||||
nsIDOMStorage2.idl \
|
||||
nsIDOMStorageManager.idl \
|
||||
$(NULL)
|
||||
|
||||
|
@ -85,7 +85,6 @@ interface nsIDOMStorage : nsISupports
|
||||
*
|
||||
* @param key key to set
|
||||
* @param data data to associate with the key
|
||||
* @returns found item or null if the key was not found
|
||||
*/
|
||||
void setItem(in DOMString key, in DOMString data);
|
||||
|
||||
|
102
dom/interfaces/storage/nsIDOMStorage2.idl
Normal file
102
dom/interfaces/storage/nsIDOMStorage2.idl
Normal file
@ -0,0 +1,102 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Jan Bambas <honzab@firemni.cz>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "domstubs.idl"
|
||||
|
||||
/**
|
||||
* Interface for client side storage. See
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/multipage/structured.html#storage0
|
||||
* 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.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(A67BA00F-CCB8-4ffe-BADA-F8913CAAFB20)]
|
||||
interface nsIDOMStorage2 : 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 data or empty string if the key was not found
|
||||
*/
|
||||
DOMString 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);
|
||||
|
||||
/**
|
||||
* Clear the content of this storage bound to a domain
|
||||
* or an origin.
|
||||
*/
|
||||
void clear();
|
||||
};
|
@ -37,7 +37,10 @@
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(6e4bc25e-f056-4c6c-b27e-89152ca91834)]
|
||||
interface nsIDOMStorage2;
|
||||
interface nsIPrincipal;
|
||||
|
||||
[scriptable, uuid(9efc2081-218f-4622-837b-40bdb870a1c8)]
|
||||
interface nsIDOMStorageManager : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -55,4 +58,11 @@ interface nsIDOMStorageManager : nsISupports
|
||||
* with the "offline-app" permission will be removed from the database.
|
||||
*/
|
||||
void clearOfflineApps();
|
||||
|
||||
/**
|
||||
* Returns instance of localStorage object for aURI's origin.
|
||||
* This method ensures there is always only a single instance
|
||||
* for a single origin.
|
||||
*/
|
||||
nsIDOMStorage2 getLocalStorageForPrincipal(in nsIPrincipal aPrincipal);
|
||||
};
|
||||
|
@ -46,9 +46,10 @@
|
||||
*/
|
||||
|
||||
interface nsIDOMStorage;
|
||||
interface nsIDOMStorage2;
|
||||
interface nsIDOMStorageList;
|
||||
|
||||
[scriptable, uuid(55E9C181-2476-47CF-97F8-EFDAAF7B6F7A)]
|
||||
[scriptable, uuid(B4F572FB-9BA5-480b-9361-C230863323E4)]
|
||||
interface nsIDOMStorageWindow : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -60,4 +61,9 @@ interface nsIDOMStorageWindow : nsISupports
|
||||
* Global storage, accessible by domain.
|
||||
*/
|
||||
readonly attribute nsIDOMStorageList globalStorage;
|
||||
|
||||
/**
|
||||
* Local storage for the current browsing context.
|
||||
*/
|
||||
readonly attribute nsIDOMStorage2 localStorage;
|
||||
};
|
||||
|
@ -56,13 +56,15 @@ class nsPIDOMStorage : public nsISupports
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMSTORAGE_IID)
|
||||
|
||||
virtual void Init(const nsAString &aDomain, PRBool aUseDB) = 0;
|
||||
virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal) = 0;
|
||||
virtual nsresult InitAsGlobalStorage(const nsACString &aDomainDemanded) = 0;
|
||||
virtual nsresult InitAsSessionStorage(nsIURI* aURI) = 0;
|
||||
|
||||
virtual already_AddRefed<nsIDOMStorage> Clone() = 0;
|
||||
|
||||
virtual nsTArray<nsString> *GetKeys() = 0;
|
||||
|
||||
virtual const nsString &Domain() = 0;
|
||||
virtual const nsCString &Domain() = 0;
|
||||
virtual PRBool CanAccess(nsIPrincipal *aPrincipal) = 0;
|
||||
};
|
||||
|
||||
|
@ -61,6 +61,7 @@
|
||||
#include "nsIOfflineCacheUpdate.h"
|
||||
#include "nsIJSContextStack.h"
|
||||
#include "nsIPrivateBrowsingService.h"
|
||||
#include "nsDOMString.h"
|
||||
#include "nsNetCID.h"
|
||||
|
||||
static const PRUint32 ASK_BEFORE_ACCEPT = 1;
|
||||
@ -84,7 +85,7 @@ static const char kOfflineAppQuota[] = "offline-apps.quota.max";
|
||||
// The URI returned is the innermost URI that should be used for
|
||||
// security-check-like stuff. aHost is its hostname, correctly canonicalized.
|
||||
static nsresult
|
||||
GetPrincipalURIAndHost(nsIPrincipal* aPrincipal, nsIURI** aURI, nsString& aHost)
|
||||
GetPrincipalURIAndHost(nsIPrincipal* aPrincipal, nsIURI** aURI, nsCString& aHost)
|
||||
{
|
||||
nsresult rv = aPrincipal->GetDomain(aURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -103,13 +104,11 @@ GetPrincipalURIAndHost(nsIPrincipal* aPrincipal, nsIURI** aURI, nsString& aHost)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsCAutoString asciiHost;
|
||||
rv = innerURI->GetAsciiHost(asciiHost);
|
||||
rv = innerURI->GetAsciiHost(aHost);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
CopyUTF8toUTF16(asciiHost, aHost);
|
||||
innerURI.swap(*aURI);
|
||||
|
||||
return NS_OK;
|
||||
@ -157,11 +156,11 @@ IsCallerSecure()
|
||||
// and a limit after which a warning event will be sent to the observer
|
||||
// service. The warn limit may be -1, in which case there will be no warning.
|
||||
static void
|
||||
GetQuota(const nsAString &aDomain, PRInt32 *aQuota, PRInt32 *aWarnQuota)
|
||||
GetQuota(const nsACString &aDomain, PRInt32 *aQuota, PRInt32 *aWarnQuota)
|
||||
{
|
||||
// Fake a URI for the permission manager
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
NS_NewURI(getter_AddRefs(uri), NS_LITERAL_STRING("http://") + aDomain);
|
||||
NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + aDomain);
|
||||
|
||||
if (uri) {
|
||||
nsCOMPtr<nsIPermissionManager> permissionManager =
|
||||
@ -295,8 +294,11 @@ GetOfflineDomains(nsTArray<nsString>& aDomains)
|
||||
|
||||
PRBool hasMore;
|
||||
while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
|
||||
nsCOMPtr<nsIPermission> perm;
|
||||
rv = enumerator->GetNext(getter_AddRefs(perm));
|
||||
nsCOMPtr<nsISupports> supp;
|
||||
rv = enumerator->GetNext(getter_AddRefs(supp));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIPermission> perm(do_QueryInterface(supp, &rv));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRUint32 capability;
|
||||
@ -330,7 +332,8 @@ nsDOMStorageManager::Observe(nsISupports *aSubject,
|
||||
#ifdef MOZ_STORAGE
|
||||
nsresult rv = nsDOMStorage::InitDB();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return nsDOMStorage::gStorageDB->RemoveOwner(nsDependentString(aData));
|
||||
return nsDOMStorage::gStorageDB->RemoveOwner(NS_ConvertUTF16toUTF8(aData),
|
||||
PR_FALSE);
|
||||
#endif
|
||||
} else if (!strcmp(aTopic, "cookie-changed") &&
|
||||
!nsCRT::strcmp(aData, NS_LITERAL_STRING("cleared").get())) {
|
||||
@ -344,7 +347,7 @@ nsDOMStorageManager::Observe(nsISupports *aSubject,
|
||||
nsTArray<nsString> domains;
|
||||
rv = GetOfflineDomains(domains);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return nsDOMStorage::gStorageDB->RemoveOwners(domains, PR_FALSE);
|
||||
return nsDOMStorage::gStorageDB->RemoveOwners(domains, PR_FALSE, PR_FALSE);
|
||||
#endif
|
||||
} else if (!strcmp(aTopic, NS_PRIVATE_BROWSING_SWITCH_TOPIC)) {
|
||||
mStorages.EnumerateEntries(ClearStorage, nsnull);
|
||||
@ -364,7 +367,8 @@ nsDOMStorageManager::GetUsage(const nsAString& aDomain,
|
||||
nsresult rv = nsDOMStorage::InitDB();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return nsDOMStorage::gStorageDB->GetUsage(aDomain, aUsage);
|
||||
return nsDOMStorage::gStorageDB->GetUsage(NS_ConvertUTF16toUTF8(aDomain),
|
||||
PR_FALSE, aUsage);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -376,7 +380,30 @@ nsDOMStorageManager::ClearOfflineApps()
|
||||
nsTArray<nsString> domains;
|
||||
rv = GetOfflineDomains(domains);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return nsDOMStorage::gStorageDB->RemoveOwners(domains, PR_TRUE);
|
||||
return nsDOMStorage::gStorageDB->RemoveOwners(domains, PR_FALSE, PR_TRUE);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMStorageManager::GetLocalStorageForPrincipal(nsIPrincipal *aPrincipal,
|
||||
nsIDOMStorage2 **aResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aPrincipal);
|
||||
*aResult = nsnull;
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsRefPtr<nsDOMStorage2> storage = new nsDOMStorage2();
|
||||
if (!storage)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
rv = storage->InitAsLocalStorage(aPrincipal);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
*aResult = storage.get();
|
||||
storage.forget();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
@ -390,7 +417,7 @@ nsDOMStorageManager::AddToStoragesHash(nsDOMStorage* aStorage)
|
||||
void
|
||||
nsDOMStorageManager::RemoveFromStoragesHash(nsDOMStorage* aStorage)
|
||||
{
|
||||
nsDOMStorageEntry* entry = mStorages.GetEntry(aStorage);
|
||||
nsDOMStorageEntry* entry = mStorages.GetEntry(aStorage);
|
||||
if (entry)
|
||||
mStorages.RemoveEntry(aStorage);
|
||||
}
|
||||
@ -461,23 +488,26 @@ NS_NewDOMStorage(nsISupports* aOuter, REFNSIID aIID, void** aResult)
|
||||
}
|
||||
|
||||
nsDOMStorage::nsDOMStorage()
|
||||
: mUseDB(PR_FALSE), mSessionOnly(PR_TRUE), mItemsCached(PR_FALSE)
|
||||
: mUseDB(PR_FALSE)
|
||||
, mSessionOnly(PR_TRUE)
|
||||
, mLocalStorage(PR_FALSE)
|
||||
, mItemsCached(PR_FALSE)
|
||||
{
|
||||
mItems.Init(8);
|
||||
if (nsDOMStorageManager::gStorageManager)
|
||||
nsDOMStorageManager::gStorageManager->AddToStoragesHash(this);
|
||||
}
|
||||
|
||||
nsDOMStorage::nsDOMStorage(const nsAString& aDomain, PRBool aUseDB)
|
||||
: mUseDB(aUseDB),
|
||||
mSessionOnly(PR_TRUE),
|
||||
mItemsCached(PR_FALSE),
|
||||
mDomain(aDomain)
|
||||
{
|
||||
#ifndef MOZ_STORAGE
|
||||
mUseDB = PR_FALSE;
|
||||
nsDOMStorage::nsDOMStorage(nsDOMStorage& aThat)
|
||||
: mUseDB(PR_FALSE) // Any clone is not using the database
|
||||
, mSessionOnly(PR_TRUE)
|
||||
, mLocalStorage(PR_FALSE) // Any clone is not a localStorage
|
||||
, mItemsCached(PR_FALSE)
|
||||
, mDomain(aThat.mDomain)
|
||||
#ifdef MOZ_STORAGE
|
||||
, mScopeDBKey(aThat.mScopeDBKey)
|
||||
#endif
|
||||
|
||||
{
|
||||
mItems.Init(8);
|
||||
if (nsDOMStorageManager::gStorageManager)
|
||||
nsDOMStorageManager::gStorageManager->AddToStoragesHash(this);
|
||||
@ -489,15 +519,74 @@ nsDOMStorage::~nsDOMStorage()
|
||||
nsDOMStorageManager::gStorageManager->RemoveFromStoragesHash(this);
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMStorage::Init(const nsAString& aDomain, PRBool aUseDB)
|
||||
nsresult
|
||||
nsDOMStorage::InitAsLocalStorage(nsIPrincipal *aPrincipal)
|
||||
{
|
||||
mDomain.Assign(aDomain);
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = aPrincipal->GetURI(getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIURI> innerUri = NS_GetInnermostURI(uri);
|
||||
if (!innerUri)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
// No need to check for a return value. If this would fail we would not get
|
||||
// here as we call GetPrincipalURIAndHost (nsDOMStorage.cpp:88) from
|
||||
// nsDOMStorage::CanUseStorage before we query the storage manager for a new
|
||||
// localStorage. It calls GetAsciiHost on innermost URI. If it fails, we won't
|
||||
// get to InitAsLocalStorage. Actually, mDomain will get replaced with
|
||||
// mPrincipal in bug 455070. It is not even used for localStorage.
|
||||
innerUri->GetAsciiHost(mDomain);
|
||||
|
||||
#ifdef MOZ_STORAGE
|
||||
mUseDB = aUseDB;
|
||||
#else
|
||||
mUseDB = PR_FALSE;
|
||||
nsDOMStorageDB::CreateOriginScopeDBKey(innerUri, mScopeDBKey);
|
||||
|
||||
// XXX Bug 357323, we have to solve the issue how to define
|
||||
// origin for file URLs. In that case CreateOriginScopeDBKey
|
||||
// fails (the result is empty) and we must avoid database use
|
||||
// in that case because it produces broken entries w/o owner.
|
||||
mUseDB = !mScopeDBKey.IsEmpty();
|
||||
|
||||
nsDOMStorageDB::CreateQuotaDomainDBKey(mDomain, PR_TRUE, mQuotaDomainDBKey);
|
||||
#endif
|
||||
|
||||
mLocalStorage = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorage::InitAsGlobalStorage(const nsACString &aDomainDemanded)
|
||||
{
|
||||
mDomain = aDomainDemanded;
|
||||
#ifdef MOZ_STORAGE
|
||||
nsDOMStorageDB::CreateDomainScopeDBKey(aDomainDemanded, mScopeDBKey);
|
||||
|
||||
// XXX Bug 357323, we have to solve the issue how to define
|
||||
// origin for file URLs. In that case CreateOriginScopeDBKey
|
||||
// fails (the result is empty) and we must avoid database use
|
||||
// in that case because it produces broken entries w/o owner.
|
||||
if (!(mUseDB = !mScopeDBKey.IsEmpty()))
|
||||
mScopeDBKey.AppendLiteral(":");
|
||||
|
||||
nsDOMStorageDB::CreateQuotaDomainDBKey(aDomainDemanded, PR_TRUE, mQuotaDomainDBKey);
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorage::InitAsSessionStorage(nsIURI* aURI)
|
||||
{
|
||||
nsCAutoString domain;
|
||||
aURI->GetAsciiHost(domain);
|
||||
mDomain = domain;
|
||||
#ifdef MOZ_STORAGE
|
||||
mUseDB = PR_FALSE;
|
||||
mScopeDBKey.Truncate();
|
||||
mQuotaDomainDBKey.Truncate();
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//static
|
||||
@ -524,7 +613,7 @@ nsDOMStorage::CanUseStorage(PRPackedBool* aSessionOnly)
|
||||
// IsCallerChrome().
|
||||
|
||||
nsCOMPtr<nsIURI> subjectURI;
|
||||
nsAutoString unused;
|
||||
nsCAutoString unused;
|
||||
if (NS_FAILED(GetPrincipalURIAndHost(subjectPrincipal,
|
||||
getter_AddRefs(subjectURI),
|
||||
unused))) {
|
||||
@ -609,6 +698,9 @@ nsDOMStorage::GetLength(PRUint32 *aLength)
|
||||
if (!CacheStoragePermissions())
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
|
||||
// Force reload of items from database. This ensures sync localStorages for
|
||||
// same origins among different windows.
|
||||
mItemsCached = PR_FALSE;
|
||||
if (UseDB())
|
||||
CacheKeysFromDB();
|
||||
|
||||
@ -709,8 +801,7 @@ nsDOMStorage::GetNamedItem(const nsAString& aKey, nsresult* aResult)
|
||||
else if (UseDB()) {
|
||||
PRBool secure;
|
||||
nsAutoString value;
|
||||
nsAutoString unused;
|
||||
nsresult rv = GetDBValue(aKey, value, &secure, unused);
|
||||
nsresult rv = GetDBValue(aKey, value, &secure);
|
||||
// return null if access isn't allowed or the key wasn't found
|
||||
if (rv == NS_ERROR_DOM_SECURITY_ERR || rv == NS_ERROR_DOM_NOT_FOUND_ERR)
|
||||
return nsnull;
|
||||
@ -731,6 +822,32 @@ nsDOMStorage::GetNamedItem(const nsAString& aKey, nsresult* aResult)
|
||||
return item;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorage::GetItem(const nsAString& aKey, nsAString &aData)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// IMPORTANT:
|
||||
// CacheStoragePermissions() is called inside of
|
||||
// GetItem(nsAString, nsIDOMStorageItem)
|
||||
// To call it particularly in this method would just duplicate
|
||||
// the call. If the code changes, make sure that call to
|
||||
// CacheStoragePermissions() is put here!
|
||||
|
||||
nsCOMPtr<nsIDOMStorageItem> item;
|
||||
rv = GetItem(aKey, getter_AddRefs(item));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (item) {
|
||||
rv = item->GetValue(aData);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else
|
||||
SetDOMStringToNull(aData);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMStorage::GetItem(const nsAString& aKey, nsIDOMStorageItem **aItem)
|
||||
@ -763,10 +880,7 @@ nsDOMStorage::SetItem(const nsAString& aKey, const nsAString& aData)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (UseDB())
|
||||
newitem = new nsDOMStorageItem(this, aKey, aData, PR_FALSE);
|
||||
else
|
||||
newitem = new nsDOMStorageItem(this, aKey, aData, PR_FALSE);
|
||||
newitem = new nsDOMStorageItem(this, aKey, aData, IsCallerSecure());
|
||||
if (!newitem)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
@ -810,13 +924,12 @@ NS_IMETHODIMP nsDOMStorage::RemoveItem(const nsAString& aKey)
|
||||
|
||||
nsAutoString value;
|
||||
PRBool secureItem;
|
||||
nsAutoString owner;
|
||||
rv = GetDBValue(aKey, value, &secureItem, owner);
|
||||
rv = GetDBValue(aKey, value, &secureItem);
|
||||
if (rv == NS_ERROR_DOM_NOT_FOUND_ERR)
|
||||
return NS_OK;
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = gStorageDB->RemoveKey(mDomain, aKey, owner,
|
||||
rv = gStorageDB->RemoveKey(this, aKey,
|
||||
aKey.Length() + value.Length());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
@ -839,6 +952,47 @@ NS_IMETHODIMP nsDOMStorage::RemoveItem(const nsAString& aKey)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PR_STATIC_CALLBACK(PLDHashOperator)
|
||||
CheckSecure(nsSessionStorageEntry* aEntry, void* userArg)
|
||||
{
|
||||
PRBool* secure = (PRBool*)userArg;
|
||||
*secure |= aEntry->mItem->IsSecure();
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorage::Clear()
|
||||
{
|
||||
if (!CacheStoragePermissions())
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
|
||||
if (UseDB())
|
||||
CacheKeysFromDB();
|
||||
|
||||
PRBool foundSecureItem = PR_FALSE;
|
||||
mItems.EnumerateEntries(CheckSecure, &foundSecureItem);
|
||||
|
||||
if (foundSecureItem && !IsCallerSecure()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
#ifdef MOZ_STORAGE
|
||||
if (UseDB()) {
|
||||
nsresult rv = InitDB();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = gStorageDB->ClearStorage(this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
#endif
|
||||
|
||||
mItems.Clear();
|
||||
BroadcastChangeNotification();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorage::InitDB()
|
||||
{
|
||||
@ -876,7 +1030,9 @@ nsDOMStorage::CacheKeysFromDB()
|
||||
nsresult rv = InitDB();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = gStorageDB->GetAllKeys(mDomain, this, &mItems);
|
||||
mItems.Clear();
|
||||
|
||||
rv = gStorageDB->GetAllKeys(this, &mItems);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mItemsCached = PR_TRUE;
|
||||
@ -888,7 +1044,7 @@ nsDOMStorage::CacheKeysFromDB()
|
||||
|
||||
nsresult
|
||||
nsDOMStorage::GetDBValue(const nsAString& aKey, nsAString& aValue,
|
||||
PRBool* aSecure, nsAString& aOwner)
|
||||
PRBool* aSecure)
|
||||
{
|
||||
aValue.Truncate();
|
||||
|
||||
@ -900,7 +1056,12 @@ nsDOMStorage::GetDBValue(const nsAString& aKey, nsAString& aValue,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoString value;
|
||||
rv = gStorageDB->GetKeyValue(mDomain, aKey, value, aSecure, aOwner);
|
||||
rv = gStorageDB->GetKeyValue(this, aKey, value, aSecure);
|
||||
|
||||
if (rv == NS_ERROR_DOM_NOT_FOUND_ERR && mLocalStorage) {
|
||||
SetDOMStringToNull(aValue);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
@ -934,7 +1095,7 @@ nsDOMStorage::SetDBValue(const nsAString& aKey,
|
||||
nsCOMPtr<nsIPrincipal> subjectPrincipal;
|
||||
ssm->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
|
||||
|
||||
nsAutoString currentDomain;
|
||||
nsCAutoString currentDomain;
|
||||
|
||||
if (subjectPrincipal) {
|
||||
nsCOMPtr<nsIURI> unused;
|
||||
@ -963,8 +1124,7 @@ nsDOMStorage::SetDBValue(const nsAString& aKey,
|
||||
GetQuota(currentDomain, "a, &warnQuota);
|
||||
|
||||
PRInt32 usage;
|
||||
rv = gStorageDB->SetKey(mDomain, aKey, aValue, aSecure,
|
||||
currentDomain, quota, &usage);
|
||||
rv = gStorageDB->SetKey(this, aKey, aValue, aSecure, quota, &usage);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mItemsCached = PR_FALSE;
|
||||
@ -986,7 +1146,7 @@ nsDOMStorage::SetDBValue(const nsAString& aKey,
|
||||
nsCOMPtr<nsIObserverService> os =
|
||||
do_GetService("@mozilla.org/observer-service;1");
|
||||
os->NotifyObservers(window, "dom-storage-warn-quota-exceeded",
|
||||
currentDomain.get());
|
||||
NS_ConvertUTF8toUTF16(currentDomain).get());
|
||||
}
|
||||
|
||||
BroadcastChangeNotification();
|
||||
@ -1003,7 +1163,7 @@ nsDOMStorage::SetSecure(const nsAString& aKey, PRBool aSecure)
|
||||
nsresult rv = InitDB();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return gStorageDB->SetSecure(mDomain, aKey, aSecure);
|
||||
return gStorageDB->SetSecure(this, aKey, aSecure);
|
||||
}
|
||||
#else
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
@ -1051,12 +1211,12 @@ already_AddRefed<nsIDOMStorage>
|
||||
nsDOMStorage::Clone()
|
||||
{
|
||||
if (UseDB()) {
|
||||
NS_ERROR("Uh, don't clone a global storage object.");
|
||||
NS_ERROR("Uh, don't clone a global or local storage object.");
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsDOMStorage* storage = new nsDOMStorage(mDomain, PR_FALSE);
|
||||
nsDOMStorage* storage = new nsDOMStorage(*this);
|
||||
if (!storage)
|
||||
return nsnull;
|
||||
|
||||
@ -1064,6 +1224,9 @@ nsDOMStorage::Clone()
|
||||
|
||||
NS_ADDREF(storage);
|
||||
|
||||
if (nsDOMStorageManager::gStorageManager)
|
||||
nsDOMStorageManager::gStorageManager->AddToStoragesHash(storage);
|
||||
|
||||
return storage;
|
||||
}
|
||||
|
||||
@ -1099,16 +1262,15 @@ nsDOMStorage::GetKeys()
|
||||
return keystruct.keys;
|
||||
}
|
||||
|
||||
const nsString &
|
||||
const nsCString &
|
||||
nsDOMStorage::Domain()
|
||||
{
|
||||
return mDomain;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsDOMStorage::CanAccess(nsIPrincipal *aPrincipal)
|
||||
nsDOMStorage::CanAccessSystem(nsIPrincipal *aPrincipal)
|
||||
{
|
||||
// Allow C++/system callers to access the storage
|
||||
if (!aPrincipal)
|
||||
return PR_TRUE;
|
||||
|
||||
@ -1120,7 +1282,17 @@ nsDOMStorage::CanAccess(nsIPrincipal *aPrincipal)
|
||||
if (NS_SUCCEEDED(ssm->IsSystemPrincipal(aPrincipal, &isSystem) && isSystem))
|
||||
return PR_TRUE;
|
||||
|
||||
nsAutoString domain;
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsDOMStorage::CanAccess(nsIPrincipal *aPrincipal)
|
||||
{
|
||||
// Allow C++/system callers to access the storage
|
||||
if (CanAccessSystem(aPrincipal))
|
||||
return PR_TRUE;
|
||||
|
||||
nsCAutoString domain;
|
||||
nsCOMPtr<nsIURI> unused;
|
||||
nsresult rv = GetPrincipalURIAndHost(aPrincipal,
|
||||
getter_AddRefs(unused), domain);
|
||||
@ -1144,7 +1316,129 @@ nsDOMStorage::BroadcastChangeNotification()
|
||||
// domain, but if it's a global storage object we do.
|
||||
observerService->NotifyObservers((nsIDOMStorage *)this,
|
||||
"dom-storage-changed",
|
||||
UseDB() ? mDomain.get() : nsnull);
|
||||
UseDB() ? NS_ConvertUTF8toUTF16(mDomain).get() : nsnull);
|
||||
}
|
||||
|
||||
//
|
||||
// nsDOMStorage2
|
||||
//
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMStorage2)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMStorage2)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mStorage)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMStorage2)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mStorage, nsIDOMStorage)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsDOMStorage2, nsIDOMStorage2)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsDOMStorage2, nsIDOMStorage2)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorage2)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorage2)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMStorage2)
|
||||
NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage2)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
nsresult
|
||||
nsDOMStorage2::InitAsLocalStorage(nsIPrincipal *aPrincipal)
|
||||
{
|
||||
mStorage = new nsDOMStorage();
|
||||
if (!mStorage)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
mPrincipal = aPrincipal;
|
||||
return mStorage->InitAsLocalStorage(aPrincipal);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorage2::InitAsGlobalStorage(const nsACString &aDomainDemanded)
|
||||
{
|
||||
NS_ASSERTION(PR_FALSE, "Should not initialize nsDOMStorage2 as global storage.");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorage2::InitAsSessionStorage(nsIURI* aURI)
|
||||
{
|
||||
mStorage = new nsDOMStorage();
|
||||
if (!mStorage)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
return mStorage->InitAsSessionStorage(aURI);
|
||||
}
|
||||
|
||||
already_AddRefed<nsIDOMStorage>
|
||||
nsDOMStorage2::Clone()
|
||||
{
|
||||
// XXX: this will need to be fixed before sessionStorage is moved
|
||||
// to nsIDOMStorage2.
|
||||
NS_ASSERTION(PR_FALSE, "Cannot clone nsDOMStorage2");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsTArray<nsString> *
|
||||
nsDOMStorage2::GetKeys()
|
||||
{
|
||||
return mStorage->GetKeys();
|
||||
}
|
||||
|
||||
const nsCString &
|
||||
nsDOMStorage2::Domain()
|
||||
{
|
||||
return mStorage->Domain();
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsDOMStorage2::CanAccess(nsIPrincipal *aPrincipal)
|
||||
{
|
||||
// Allow C++ callers to access the storage
|
||||
if (!aPrincipal)
|
||||
return PR_TRUE;
|
||||
|
||||
// Allow system and all weaker principals access the storage
|
||||
PRBool subsumes;
|
||||
nsresult rv = aPrincipal->Subsumes(mPrincipal, &subsumes);
|
||||
if (NS_FAILED(rv))
|
||||
return PR_FALSE;
|
||||
|
||||
return subsumes;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMStorage2::GetLength(PRUint32 *aLength)
|
||||
{
|
||||
return mStorage->GetLength(aLength);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMStorage2::Key(PRUint32 aIndex, nsAString& aKey)
|
||||
{
|
||||
return mStorage->Key(aIndex, aKey);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMStorage2::GetItem(const nsAString& aKey, nsAString &aData)
|
||||
{
|
||||
return mStorage->GetItem(aKey, aData);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMStorage2::SetItem(const nsAString& aKey, const nsAString& aData)
|
||||
{
|
||||
return mStorage->SetItem(aKey, aData);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMStorage2::RemoveItem(const nsAString& aKey)
|
||||
{
|
||||
return mStorage->RemoveItem(aKey);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMStorage2::Clear()
|
||||
{
|
||||
return mStorage->Clear();
|
||||
}
|
||||
|
||||
//
|
||||
@ -1189,7 +1483,7 @@ nsDOMStorageList::GetNamedItem(const nsAString& aDomain, nsresult* aResult)
|
||||
*aResult = ssm->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
|
||||
NS_ENSURE_SUCCESS(*aResult, nsnull);
|
||||
|
||||
nsAutoString currentDomain;
|
||||
nsCAutoString currentDomain;
|
||||
if (subjectPrincipal) {
|
||||
nsCOMPtr<nsIURI> unused;
|
||||
*aResult = GetPrincipalURIAndHost(subjectPrincipal, getter_AddRefs(unused),
|
||||
@ -1209,7 +1503,7 @@ nsDOMStorageList::GetNamedItem(const nsAString& aDomain, nsresult* aResult)
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
return GetStorageForDomain(NS_ConvertUTF8toUTF16(requestedDomain),
|
||||
return GetStorageForDomain(requestedDomain,
|
||||
currentDomain, isSystem, aResult);
|
||||
}
|
||||
|
||||
@ -1224,19 +1518,19 @@ nsDOMStorageList::NamedItem(const nsAString& aDomain,
|
||||
|
||||
// static
|
||||
PRBool
|
||||
nsDOMStorageList::CanAccessDomain(const nsAString& aRequestedDomain,
|
||||
const nsAString& aCurrentDomain)
|
||||
nsDOMStorageList::CanAccessDomain(const nsACString& aRequestedDomain,
|
||||
const nsACString& aCurrentDomain)
|
||||
{
|
||||
return aRequestedDomain.Equals(aCurrentDomain);
|
||||
}
|
||||
|
||||
nsIDOMStorage*
|
||||
nsDOMStorageList::GetStorageForDomain(const nsAString& aRequestedDomain,
|
||||
const nsAString& aCurrentDomain,
|
||||
nsDOMStorageList::GetStorageForDomain(const nsACString& aRequestedDomain,
|
||||
const nsACString& aCurrentDomain,
|
||||
PRBool aNoCurrentDomainCheck,
|
||||
nsresult* aResult)
|
||||
{
|
||||
nsTArray<nsString> requestedDomainArray;
|
||||
nsTArray<nsCString> requestedDomainArray;
|
||||
if ((!aNoCurrentDomainCheck &&
|
||||
!CanAccessDomain(aRequestedDomain, aCurrentDomain)) ||
|
||||
!ConvertDomainToArray(aRequestedDomain, &requestedDomainArray)) {
|
||||
@ -1246,12 +1540,12 @@ nsDOMStorageList::GetStorageForDomain(const nsAString& aRequestedDomain,
|
||||
}
|
||||
|
||||
// now rebuild a string for the domain.
|
||||
nsAutoString usedDomain;
|
||||
nsCAutoString usedDomain;
|
||||
PRUint32 requestedPos = 0;
|
||||
for (requestedPos = 0; requestedPos < requestedDomainArray.Length();
|
||||
requestedPos++) {
|
||||
if (!usedDomain.IsEmpty())
|
||||
usedDomain.AppendLiteral(".");
|
||||
usedDomain.Append('.');
|
||||
usedDomain.Append(requestedDomainArray[requestedPos]);
|
||||
}
|
||||
|
||||
@ -1260,11 +1554,19 @@ nsDOMStorageList::GetStorageForDomain(const nsAString& aRequestedDomain,
|
||||
// now have a valid domain, so look it up in the storage table
|
||||
nsIDOMStorage* storage = mStorages.GetWeak(usedDomain);
|
||||
if (!storage) {
|
||||
nsCOMPtr<nsIDOMStorage> newstorage = new nsDOMStorage(usedDomain, PR_TRUE);
|
||||
if (newstorage && mStorages.Put(usedDomain, newstorage))
|
||||
nsRefPtr<nsDOMStorage> newstorage;
|
||||
newstorage = new nsDOMStorage();
|
||||
if (newstorage && mStorages.Put(usedDomain, newstorage)) {
|
||||
*aResult = newstorage->InitAsGlobalStorage(usedDomain);
|
||||
if (NS_FAILED(*aResult)) {
|
||||
mStorages.Remove(usedDomain);
|
||||
return nsnull;
|
||||
}
|
||||
storage = newstorage;
|
||||
else
|
||||
}
|
||||
else {
|
||||
*aResult = NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
return storage;
|
||||
@ -1272,14 +1574,14 @@ nsDOMStorageList::GetStorageForDomain(const nsAString& aRequestedDomain,
|
||||
|
||||
// static
|
||||
PRBool
|
||||
nsDOMStorageList::ConvertDomainToArray(const nsAString& aDomain,
|
||||
nsTArray<nsString> *aArray)
|
||||
nsDOMStorageList::ConvertDomainToArray(const nsACString& aDomain,
|
||||
nsTArray<nsCString> *aArray)
|
||||
{
|
||||
PRInt32 length = aDomain.Length();
|
||||
PRInt32 n = 0;
|
||||
while (n < length) {
|
||||
PRInt32 dotpos = aDomain.FindChar('.', n);
|
||||
nsAutoString domain;
|
||||
nsCAutoString domain;
|
||||
|
||||
if (dotpos == -1) // no more dots
|
||||
domain.Assign(Substring(aDomain, n));
|
||||
@ -1361,8 +1663,7 @@ nsDOMStorageItem::GetSecure(PRBool* aSecure)
|
||||
|
||||
if (mStorage->UseDB()) {
|
||||
nsAutoString value;
|
||||
nsAutoString owner;
|
||||
return mStorage->GetDBValue(mKey, value, aSecure, owner);
|
||||
return mStorage->GetDBValue(mKey, value, aSecure);
|
||||
}
|
||||
|
||||
*aSecure = IsSecure();
|
||||
@ -1394,9 +1695,10 @@ nsDOMStorageItem::GetValue(nsAString& aValue)
|
||||
if (mStorage->UseDB()) {
|
||||
// GetDBValue checks the secure state so no need to do it here
|
||||
PRBool secure;
|
||||
nsAutoString unused;
|
||||
nsresult rv = mStorage->GetDBValue(mKey, aValue, &secure, unused);
|
||||
return (rv == NS_ERROR_DOM_NOT_FOUND_ERR) ? NS_OK : rv;
|
||||
nsresult rv = mStorage->GetDBValue(mKey, aValue, &secure);
|
||||
if (rv == NS_ERROR_DOM_NOT_FOUND_ERR)
|
||||
return NS_OK;
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (IsSecure() && !IsCallerSecure()) {
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "nscore.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIDOMStorage.h"
|
||||
#include "nsIDOMStorage2.h"
|
||||
#include "nsIDOMStorageList.h"
|
||||
#include "nsIDOMStorageItem.h"
|
||||
#include "nsInterfaceHashtable.h"
|
||||
@ -61,6 +62,7 @@
|
||||
#endif
|
||||
|
||||
class nsDOMStorage;
|
||||
class nsIDOMStorage2;
|
||||
class nsDOMStorageItem;
|
||||
|
||||
class nsDOMStorageEntry : public nsVoidPtrHashKey
|
||||
@ -123,7 +125,7 @@ class nsDOMStorage : public nsIDOMStorage,
|
||||
{
|
||||
public:
|
||||
nsDOMStorage();
|
||||
nsDOMStorage(const nsAString& aDomain, PRBool aUseDB);
|
||||
nsDOMStorage(nsDOMStorage& aThat);
|
||||
virtual ~nsDOMStorage();
|
||||
|
||||
// nsISupports
|
||||
@ -133,11 +135,17 @@ public:
|
||||
// nsIDOMStorage
|
||||
NS_DECL_NSIDOMSTORAGE
|
||||
|
||||
// Helpers for implementing nsIDOMStorage2
|
||||
nsresult GetItem(const nsAString& key, nsAString& aData);
|
||||
nsresult Clear();
|
||||
|
||||
// nsPIDOMStorage
|
||||
virtual void Init(const nsAString& aDomain, PRBool aUseDB);
|
||||
virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal);
|
||||
virtual nsresult InitAsGlobalStorage(const nsACString &aDomainDemanded);
|
||||
virtual nsresult InitAsSessionStorage(nsIURI* aURI);
|
||||
virtual already_AddRefed<nsIDOMStorage> Clone();
|
||||
virtual nsTArray<nsString> *GetKeys();
|
||||
virtual const nsString &Domain();
|
||||
virtual const nsCString &Domain();
|
||||
virtual PRBool CanAccess(nsIPrincipal *aPrincipal);
|
||||
|
||||
// If true, the contents of the storage should be stored in the
|
||||
@ -165,8 +173,7 @@ public:
|
||||
nsresult
|
||||
GetDBValue(const nsAString& aKey,
|
||||
nsAString& aValue,
|
||||
PRBool* aSecure,
|
||||
nsAString& aOwner);
|
||||
PRBool* aSecure);
|
||||
|
||||
// set the value corresponding to a key in the storage. If
|
||||
// aSecure is false, then attempts to modify a secure value
|
||||
@ -193,6 +200,7 @@ public:
|
||||
protected:
|
||||
|
||||
friend class nsDOMStorageManager;
|
||||
friend class nsDOMStorage2;
|
||||
|
||||
static nsresult InitDB();
|
||||
|
||||
@ -201,6 +209,8 @@ protected:
|
||||
|
||||
void BroadcastChangeNotification();
|
||||
|
||||
PRBool CanAccessSystem(nsIPrincipal *aPrincipal);
|
||||
|
||||
// true if the storage database should be used for values
|
||||
PRPackedBool mUseDB;
|
||||
|
||||
@ -211,18 +221,66 @@ protected:
|
||||
// make sure this stays up to date.
|
||||
PRPackedBool mSessionOnly;
|
||||
|
||||
// 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.
|
||||
PRPackedBool mLocalStorage;
|
||||
|
||||
// true if items from the database are cached
|
||||
PRPackedBool mItemsCached;
|
||||
|
||||
// domain this store is associated with
|
||||
nsString mDomain;
|
||||
nsCString mDomain;
|
||||
|
||||
// the key->value item pairs
|
||||
nsTHashtable<nsSessionStorageEntry> mItems;
|
||||
|
||||
#ifdef MOZ_STORAGE
|
||||
static nsDOMStorageDB* gStorageDB;
|
||||
#endif
|
||||
// keys are used for database queries.
|
||||
// see comments of the getters bellow.
|
||||
nsCString mScopeDBKey;
|
||||
nsCString mQuotaDomainDBKey;
|
||||
|
||||
public:
|
||||
// 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 or
|
||||
// (in future) reversed offline application allowed domain.
|
||||
nsCString& GetQuotaDomainDBKey() {return mQuotaDomainDBKey;}
|
||||
|
||||
#ifdef MOZ_STORAGE
|
||||
static nsDOMStorageDB* gStorageDB;
|
||||
#endif
|
||||
};
|
||||
|
||||
class nsDOMStorage2 : public nsIDOMStorage2,
|
||||
public nsPIDOMStorage
|
||||
{
|
||||
public:
|
||||
// nsISupports
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMStorage2, nsIDOMStorage2)
|
||||
|
||||
NS_DECL_NSIDOMSTORAGE2
|
||||
|
||||
// nsPIDOMStorage
|
||||
virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal);
|
||||
virtual nsresult InitAsGlobalStorage(const nsACString &aDomainDemanded);
|
||||
virtual nsresult InitAsSessionStorage(nsIURI* aURI);
|
||||
virtual already_AddRefed<nsIDOMStorage> Clone();
|
||||
virtual nsTArray<nsString> *GetKeys();
|
||||
virtual const nsCString &Domain();
|
||||
virtual PRBool CanAccess(nsIPrincipal *aPrincipal);
|
||||
|
||||
private:
|
||||
// storages bound to an origin hold the principal to
|
||||
// make security checks against it
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
||||
nsRefPtr<nsDOMStorage> mStorage;
|
||||
};
|
||||
|
||||
class nsDOMStorageList : public nsIDOMStorageList
|
||||
@ -247,8 +305,8 @@ public:
|
||||
* Check whether aCurrentDomain has access to aRequestedDomain
|
||||
*/
|
||||
static PRBool
|
||||
CanAccessDomain(const nsAString& aRequestedDomain,
|
||||
const nsAString& aCurrentDomain);
|
||||
CanAccessDomain(const nsACString& aRequestedDomain,
|
||||
const nsACString& aCurrentDomain);
|
||||
|
||||
protected:
|
||||
|
||||
@ -263,8 +321,8 @@ protected:
|
||||
* @param aNoCurrentDomainCheck true to skip domain comparison
|
||||
*/
|
||||
nsIDOMStorage*
|
||||
GetStorageForDomain(const nsAString& aRequestedDomain,
|
||||
const nsAString& aCurrentDomain,
|
||||
GetStorageForDomain(const nsACString& aRequestedDomain,
|
||||
const nsACString& aCurrentDomain,
|
||||
PRBool aNoCurrentDomainCheck,
|
||||
nsresult* aResult);
|
||||
|
||||
@ -272,10 +330,10 @@ protected:
|
||||
* Convert the domain into an array of its component parts.
|
||||
*/
|
||||
static PRBool
|
||||
ConvertDomainToArray(const nsAString& aDomain,
|
||||
nsTArray<nsString>* aArray);
|
||||
ConvertDomainToArray(const nsACString& aDomain,
|
||||
nsTArray<nsCString>* aArray);
|
||||
|
||||
nsInterfaceHashtable<nsStringHashKey, nsIDOMStorage> mStorages;
|
||||
nsInterfaceHashtable<nsCStringHashKey, nsIDOMStorage> mStorages;
|
||||
};
|
||||
|
||||
class nsDOMStorageItem : public nsIDOMStorageItem,
|
||||
|
@ -41,11 +41,66 @@
|
||||
#include "nsDOMStorage.h"
|
||||
#include "nsDOMStorageDB.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIVariant.h"
|
||||
#include "nsIEffectiveTLDService.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"
|
||||
|
||||
static
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
class nsReverseStringSQLFunction : public mozIStorageFunction
|
||||
{
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_MOZISTORAGEFUNCTION
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsReverseStringSQLFunction, mozIStorageFunction)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsReverseStringSQLFunction::OnFunctionCall(
|
||||
mozIStorageValueArray *aFunctionArguments, nsIVariant **aResult)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCAutoString stringToReverse;
|
||||
rv = aFunctionArguments->GetUTF8String(0, stringToReverse);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCAutoString result;
|
||||
ReverseString(stringToReverse, result);
|
||||
|
||||
nsCOMPtr<nsIWritableVariant> outVar(do_CreateInstance(
|
||||
NS_VARIANT_CONTRACTID, &rv));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = outVar->SetAsAUTF8String(result);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*aResult = outVar.get();
|
||||
outVar.forget();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDB::Init()
|
||||
@ -72,134 +127,156 @@ nsDOMStorageDB::Init()
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool exists;
|
||||
rv = mConnection->TableExists(NS_LITERAL_CSTRING("webappsstore"), &exists);
|
||||
// Ensure Gecko 1.9.1 storage table
|
||||
rv = mConnection->ExecuteSimpleSQL(
|
||||
NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS webappsstore2 ("
|
||||
"scope TEXT, "
|
||||
"key TEXT, "
|
||||
"value TEXT, "
|
||||
"secure INTEGER, "
|
||||
"owner TEXT)"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (! exists) {
|
||||
rv = mConnection->ExecuteSimpleSQL(
|
||||
NS_LITERAL_CSTRING("CREATE TABLE webappsstore ("
|
||||
"domain TEXT, "
|
||||
"key TEXT, "
|
||||
"value TEXT, "
|
||||
"secure INTEGER, "
|
||||
"owner TEXT)"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS scope_key_index"
|
||||
" ON webappsstore2(scope, key)"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<mozIStorageFunction> function(new nsReverseStringSQLFunction());
|
||||
NS_ENSURE_TRUE(function, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
rv = mConnection->CreateFunction(NS_LITERAL_CSTRING("REVERSESTRING"), 1, function);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool exists;
|
||||
|
||||
// Check if there is storage of Gecko 1.9.0 and if so, upgrade that storage
|
||||
// to actual webappsstore2 table and drop the obsolete table. First process
|
||||
// this newer table upgrade to priority potential duplicates from older
|
||||
// storage table.
|
||||
rv = mConnection->TableExists(NS_LITERAL_CSTRING("webappsstore"),
|
||||
&exists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (exists) {
|
||||
rv = mConnection->ExecuteSimpleSQL(
|
||||
NS_LITERAL_CSTRING("INSERT OR IGNORE INTO "
|
||||
"webappsstore2(scope, key, value, secure, owner) "
|
||||
"SELECT REVERSESTRING(domain) || '.:', key, value, secure, owner "
|
||||
"FROM webappsstore"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mConnection->ExecuteSimpleSQL(
|
||||
NS_LITERAL_CSTRING("DROP TABLE webappsstore"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Check if there is storage of Gecko 1.8 and if so, upgrade that storage
|
||||
// to actual webappsstore2 table and drop the obsolete table. Potential
|
||||
// duplicates will be ignored.
|
||||
rv = mConnection->TableExists(NS_LITERAL_CSTRING("moz_webappsstore"),
|
||||
&exists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (exists) {
|
||||
// upgrade an old store
|
||||
|
||||
// create a temporary index to handle dup checking
|
||||
rv = mConnection->ExecuteSimpleSQL(
|
||||
NS_LITERAL_CSTRING("CREATE UNIQUE INDEX webappsstore_tmp "
|
||||
" ON webappsstore(domain, key)"));
|
||||
rv = mConnection->ExecuteSimpleSQL(
|
||||
NS_LITERAL_CSTRING("INSERT OR IGNORE INTO "
|
||||
"webappsstore2(scope, key, value, secure, owner) "
|
||||
"SELECT REVERSESTRING(domain) || '.:', key, value, secure, domain "
|
||||
"FROM moz_webappsstore"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// if the index can't be created, there are dup domain/key combos
|
||||
// in moz_webappstore2, which indicates a bug elsewhere. Fail to upgrade
|
||||
// in this case
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = mConnection->ExecuteSimpleSQL(
|
||||
NS_LITERAL_CSTRING("INSERT OR IGNORE INTO "
|
||||
"webappsstore(domain, key, value, secure, owner) "
|
||||
"SELECT domain, key, value, secure, domain "
|
||||
"FROM moz_webappsstore"));
|
||||
|
||||
// try to drop the index even in case of an error
|
||||
mConnection->ExecuteSimpleSQL(
|
||||
NS_LITERAL_CSTRING("DROP INDEX webappsstore_tmp"));
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mConnection->ExecuteSimpleSQL(
|
||||
NS_LITERAL_CSTRING("DROP TABLE moz_webappsstore"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
rv = mConnection->ExecuteSimpleSQL(
|
||||
NS_LITERAL_CSTRING("DROP TABLE moz_webappsstore"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// retrieve all keys associated with a domain
|
||||
rv = mConnection->CreateStatement(
|
||||
NS_LITERAL_CSTRING("SELECT key, secure FROM webappsstore "
|
||||
"WHERE domain = ?1"),
|
||||
NS_LITERAL_CSTRING("SELECT key, secure FROM webappsstore2 "
|
||||
"WHERE scope = ?1"),
|
||||
getter_AddRefs(mGetAllKeysStatement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// retrieve a value given a domain and a key
|
||||
rv = mConnection->CreateStatement(
|
||||
NS_LITERAL_CSTRING("SELECT value, secure, owner FROM webappsstore "
|
||||
"WHERE domain = ?1 "
|
||||
NS_LITERAL_CSTRING("SELECT value, secure FROM webappsstore2 "
|
||||
"WHERE scope = ?1 "
|
||||
"AND key = ?2"),
|
||||
getter_AddRefs(mGetKeyValueStatement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// insert a new key
|
||||
rv = mConnection->CreateStatement(
|
||||
NS_LITERAL_CSTRING("INSERT INTO "
|
||||
"webappsstore(domain, key, value, secure, owner) "
|
||||
"VALUES (?1, ?2, ?3, ?4, ?5)"),
|
||||
NS_LITERAL_CSTRING("INSERT OR REPLACE INTO "
|
||||
"webappsstore2(scope, key, value, secure) "
|
||||
"VALUES (?1, ?2, ?3, ?4)"),
|
||||
getter_AddRefs(mInsertKeyStatement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// update an existing key
|
||||
rv = mConnection->CreateStatement(
|
||||
NS_LITERAL_CSTRING("UPDATE webappsstore "
|
||||
"SET value = ?1, secure = ?2, owner = ?3"
|
||||
"WHERE domain = ?4 "
|
||||
"AND key = ?5 "),
|
||||
NS_LITERAL_CSTRING("UPDATE webappsstore2 "
|
||||
"SET value = ?1, secure = ?2"
|
||||
"WHERE scope = ?3 "
|
||||
"AND key = ?4"),
|
||||
getter_AddRefs(mUpdateKeyStatement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// update the secure status of an existing key
|
||||
rv = mConnection->CreateStatement(
|
||||
NS_LITERAL_CSTRING("UPDATE webappsstore "
|
||||
NS_LITERAL_CSTRING("UPDATE webappsstore2 "
|
||||
"SET secure = ?1 "
|
||||
"WHERE domain = ?2 "
|
||||
"WHERE scope = ?2 "
|
||||
"AND key = ?3 "),
|
||||
getter_AddRefs(mSetSecureStatement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// remove a key
|
||||
rv = mConnection->CreateStatement(
|
||||
NS_LITERAL_CSTRING("DELETE FROM webappsstore "
|
||||
"WHERE domain = ?1 "
|
||||
NS_LITERAL_CSTRING("DELETE FROM webappsstore2 "
|
||||
"WHERE scope = ?1 "
|
||||
"AND key = ?2"),
|
||||
getter_AddRefs(mRemoveKeyStatement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// remove keys owned by a specific domain
|
||||
rv = mConnection->CreateStatement(
|
||||
NS_LITERAL_CSTRING("DELETE FROM webappsstore "
|
||||
"WHERE owner = ?1"),
|
||||
NS_LITERAL_CSTRING("DELETE FROM webappsstore2 "
|
||||
"WHERE scope GLOB ?1"),
|
||||
getter_AddRefs(mRemoveOwnerStatement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// remove keys belonging exactly only to a specific domain
|
||||
rv = mConnection->CreateStatement(
|
||||
NS_LITERAL_CSTRING("DELETE FROM webappsstore2 "
|
||||
"WHERE scope = ?1"),
|
||||
getter_AddRefs(mRemoveStorageStatement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// remove all keys
|
||||
rv = mConnection->CreateStatement(
|
||||
NS_LITERAL_CSTRING("DELETE FROM webappsstore"),
|
||||
NS_LITERAL_CSTRING("DELETE FROM webappsstore2"),
|
||||
getter_AddRefs(mRemoveAllStatement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// check the usage for a given owner
|
||||
rv = mConnection->CreateStatement(
|
||||
NS_LITERAL_CSTRING("SELECT SUM(LENGTH(key) + LENGTH(value)) "
|
||||
"FROM webappsstore "
|
||||
"WHERE owner = ?1"),
|
||||
"FROM webappsstore2 "
|
||||
"WHERE scope GLOB ?1"),
|
||||
getter_AddRefs(mGetUsageStatement));
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDB::GetAllKeys(const nsAString& aDomain,
|
||||
nsDOMStorage* aStorage,
|
||||
nsDOMStorageDB::GetAllKeys(nsDOMStorage* aStorage,
|
||||
nsTHashtable<nsSessionStorageEntry>* aKeys)
|
||||
{
|
||||
mozStorageStatementScoper scope(mGetAllKeysStatement);
|
||||
|
||||
nsresult rv = mGetAllKeysStatement->BindStringParameter(0, aDomain);
|
||||
nsresult rv = mGetAllKeysStatement->BindUTF8StringParameter(0, aStorage->GetScopeDBKey());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool exists;
|
||||
@ -228,15 +305,15 @@ nsDOMStorageDB::GetAllKeys(const nsAString& aDomain,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDB::GetKeyValue(const nsAString& aDomain,
|
||||
nsDOMStorageDB::GetKeyValue(nsDOMStorage* aStorage,
|
||||
const nsAString& aKey,
|
||||
nsAString& aValue,
|
||||
PRBool* aSecure,
|
||||
nsAString& aOwner)
|
||||
PRBool* aSecure)
|
||||
{
|
||||
mozStorageStatementScoper scope(mGetKeyValueStatement);
|
||||
|
||||
nsresult rv = mGetKeyValueStatement->BindStringParameter(0, aDomain);
|
||||
nsresult rv = mGetKeyValueStatement->BindUTF8StringParameter(
|
||||
0, aStorage->GetScopeDBKey());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mGetKeyValueStatement->BindStringParameter(1, aKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -252,9 +329,6 @@ nsDOMStorageDB::GetKeyValue(const nsAString& aDomain,
|
||||
|
||||
rv = mGetKeyValueStatement->GetInt32(1, &secureInt);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mGetKeyValueStatement->GetString(2, aOwner);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else {
|
||||
rv = NS_ERROR_DOM_NOT_FOUND_ERR;
|
||||
@ -266,11 +340,10 @@ nsDOMStorageDB::GetKeyValue(const nsAString& aDomain,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDB::SetKey(const nsAString& aDomain,
|
||||
nsDOMStorageDB::SetKey(nsDOMStorage* aStorage,
|
||||
const nsAString& aKey,
|
||||
const nsAString& aValue,
|
||||
PRBool aSecure,
|
||||
const nsAString& aOwner,
|
||||
PRInt32 aQuota,
|
||||
PRInt32 *aNewUsage)
|
||||
{
|
||||
@ -278,14 +351,15 @@ nsDOMStorageDB::SetKey(const nsAString& aDomain,
|
||||
|
||||
PRInt32 usage = 0;
|
||||
nsresult rv;
|
||||
if (!aOwner.IsEmpty()) {
|
||||
rv = GetUsage(aOwner, &usage);
|
||||
if (!aStorage->GetQuotaDomainDBKey().IsEmpty()) {
|
||||
rv = GetUsage(aStorage, &usage);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
usage += aKey.Length() + aValue.Length();
|
||||
|
||||
rv = mGetKeyValueStatement->BindStringParameter(0, aDomain);
|
||||
rv = mGetKeyValueStatement->BindUTF8StringParameter(0,
|
||||
aStorage->GetScopeDBKey());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mGetKeyValueStatement->BindStringParameter(1, aKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -303,16 +377,11 @@ nsDOMStorageDB::SetKey(const nsAString& aDomain,
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsAutoString previousOwner;
|
||||
rv = mGetKeyValueStatement->GetString(2, previousOwner);
|
||||
nsAutoString previousValue;
|
||||
rv = mGetKeyValueStatement->GetString(0, previousValue);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (previousOwner == aOwner) {
|
||||
nsAutoString previousValue;
|
||||
rv = mGetKeyValueStatement->GetString(0, previousValue);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
usage -= aKey.Length() + previousValue.Length();
|
||||
}
|
||||
usage -= aKey.Length() + previousValue.Length();
|
||||
|
||||
mGetKeyValueStatement->Reset();
|
||||
|
||||
@ -326,11 +395,10 @@ nsDOMStorageDB::SetKey(const nsAString& aDomain,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mUpdateKeyStatement->BindInt32Parameter(1, aSecure);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mUpdateKeyStatement->BindStringParameter(2, aOwner);
|
||||
rv = mUpdateKeyStatement->BindUTF8StringParameter(2,
|
||||
aStorage->GetScopeDBKey());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mUpdateKeyStatement->BindStringParameter(3, aDomain);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mUpdateKeyStatement->BindStringParameter(4, aKey);
|
||||
rv = mUpdateKeyStatement->BindStringParameter(3, aKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mUpdateKeyStatement->Execute();
|
||||
@ -343,7 +411,8 @@ nsDOMStorageDB::SetKey(const nsAString& aDomain,
|
||||
|
||||
mozStorageStatementScoper scopeinsert(mInsertKeyStatement);
|
||||
|
||||
rv = mInsertKeyStatement->BindStringParameter(0, aDomain);
|
||||
rv = mInsertKeyStatement->BindUTF8StringParameter(0,
|
||||
aStorage->GetScopeDBKey());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mInsertKeyStatement->BindStringParameter(1, aKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -351,15 +420,13 @@ nsDOMStorageDB::SetKey(const nsAString& aDomain,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mInsertKeyStatement->BindInt32Parameter(3, aSecure);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mInsertKeyStatement->BindStringParameter(4, aOwner);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
||||
rv = mInsertKeyStatement->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (!aOwner.IsEmpty()) {
|
||||
mCachedOwner = aOwner;
|
||||
if (!aStorage->GetQuotaDomainDBKey().IsEmpty()) {
|
||||
mCachedOwner = aStorage->GetQuotaDomainDBKey();
|
||||
mCachedUsage = usage;
|
||||
}
|
||||
|
||||
@ -369,52 +436,37 @@ nsDOMStorageDB::SetKey(const nsAString& aDomain,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDB::SetSecure(const nsAString& aDomain,
|
||||
nsDOMStorageDB::SetSecure(nsDOMStorage* aStorage,
|
||||
const nsAString& aKey,
|
||||
const PRBool aSecure)
|
||||
{
|
||||
mozStorageStatementScoper scope(mGetKeyValueStatement);
|
||||
nsresult rv;
|
||||
|
||||
nsresult rv = mGetKeyValueStatement->BindStringParameter(0, aDomain);
|
||||
mozStorageStatementScoper scope(mSetSecureStatement);
|
||||
|
||||
rv = mSetSecureStatement->BindInt32Parameter(0, aSecure ? 1 : 0);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mGetKeyValueStatement->BindStringParameter(1, aKey);
|
||||
rv = mSetSecureStatement->BindUTF8StringParameter(1, aStorage->GetScopeDBKey());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mSetSecureStatement->BindStringParameter(2, aKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool exists;
|
||||
rv = mGetKeyValueStatement->ExecuteStep(&exists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (exists) {
|
||||
mGetKeyValueStatement->Reset();
|
||||
|
||||
mozStorageStatementScoper scopeupdate(mUpdateKeyStatement);
|
||||
|
||||
rv = mSetSecureStatement->BindInt32Parameter(0, aSecure ? 1 : 0);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mSetSecureStatement->BindStringParameter(1, aDomain);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mSetSecureStatement->BindStringParameter(2, aKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return mSetSecureStatement->Execute();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return mSetSecureStatement->Execute();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDB::RemoveKey(const nsAString& aDomain,
|
||||
nsDOMStorageDB::RemoveKey(nsDOMStorage* aStorage,
|
||||
const nsAString& aKey,
|
||||
const nsAString& aOwner,
|
||||
PRInt32 aKeyUsage)
|
||||
{
|
||||
mozStorageStatementScoper scope(mRemoveKeyStatement);
|
||||
|
||||
if (aOwner == mCachedOwner) {
|
||||
if (aStorage->GetQuotaDomainDBKey() == mCachedOwner) {
|
||||
mCachedUsage -= aKeyUsage;
|
||||
}
|
||||
|
||||
nsresult rv = mRemoveKeyStatement->BindStringParameter(0, aDomain);
|
||||
nsresult rv = mRemoveKeyStatement->BindUTF8StringParameter(
|
||||
0, aStorage->GetScopeDBKey());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mRemoveKeyStatement->BindStringParameter(1, aKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -423,16 +475,42 @@ nsDOMStorageDB::RemoveKey(const nsAString& aDomain,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDB::RemoveOwner(const nsAString& aOwner)
|
||||
nsDOMStorageDB::ClearStorage(nsDOMStorage* aStorage)
|
||||
{
|
||||
mozStorageStatementScoper scope(mRemoveStorageStatement);
|
||||
|
||||
mCachedUsage = 0;
|
||||
mCachedOwner.Truncate();
|
||||
|
||||
nsresult rv;
|
||||
|
||||
rv = mRemoveStorageStatement->BindUTF8StringParameter(
|
||||
0, aStorage->GetScopeDBKey());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return mRemoveStorageStatement->Execute();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDB::RemoveOwner(const nsACString& aOwner, PRBool aIncludeSubDomains)
|
||||
{
|
||||
mozStorageStatementScoper scope(mRemoveOwnerStatement);
|
||||
|
||||
if (aOwner == mCachedOwner) {
|
||||
nsCAutoString subdomainsDBKey;
|
||||
nsDOMStorageDB::CreateDomainScopeDBKey(aOwner, subdomainsDBKey);
|
||||
|
||||
if (!aIncludeSubDomains)
|
||||
subdomainsDBKey.AppendLiteral(":");
|
||||
subdomainsDBKey.AppendLiteral("*");
|
||||
|
||||
if (subdomainsDBKey == mCachedOwner) {
|
||||
mCachedUsage = 0;
|
||||
mCachedOwner.Truncate();
|
||||
}
|
||||
|
||||
nsresult rv = mRemoveOwnerStatement->BindStringParameter(0, aOwner);
|
||||
nsresult rv;
|
||||
|
||||
rv = mRemoveOwnerStatement->BindUTF8StringParameter(0, subdomainsDBKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return mRemoveOwnerStatement->Execute();
|
||||
@ -440,7 +518,8 @@ nsDOMStorageDB::RemoveOwner(const nsAString& aOwner)
|
||||
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDB::RemoveOwners(const nsTArray<nsString> &aOwners, PRBool aMatch)
|
||||
nsDOMStorageDB::RemoveOwners(const nsTArray<nsString> &aOwners,
|
||||
PRBool aIncludeSubDomains, PRBool aMatch)
|
||||
{
|
||||
if (aOwners.Length() == 0) {
|
||||
if (aMatch) {
|
||||
@ -450,20 +529,23 @@ nsDOMStorageDB::RemoveOwners(const nsTArray<nsString> &aOwners, PRBool aMatch)
|
||||
return RemoveAll();
|
||||
}
|
||||
|
||||
nsCAutoString expression;
|
||||
// Using nsString here because it is going to be very long
|
||||
nsCString expression;
|
||||
|
||||
if (aMatch) {
|
||||
expression.Assign(NS_LITERAL_CSTRING("DELETE FROM webappsstore "
|
||||
"WHERE owner IN (?"));
|
||||
expression.AppendLiteral("DELETE FROM webappsstore2 WHERE scope IN (");
|
||||
} else {
|
||||
expression.Assign(NS_LITERAL_CSTRING("DELETE FROM webappsstore "
|
||||
"WHERE owner NOT IN (?"));
|
||||
expression.AppendLiteral("DELETE FROM webappsstore2 WHERE scope NOT IN (");
|
||||
}
|
||||
|
||||
for (PRUint32 i = 1; i < aOwners.Length(); i++) {
|
||||
expression.Append(", ?");
|
||||
for (PRUint32 i = 0; i < aOwners.Length(); i++) {
|
||||
if (i)
|
||||
expression.AppendLiteral(" UNION ");
|
||||
|
||||
expression.AppendLiteral(
|
||||
"SELECT DISTINCT scope FROM webappsstore2 WHERE scope GLOB ?");
|
||||
}
|
||||
expression.Append(")");
|
||||
expression.AppendLiteral(");");
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> statement;
|
||||
|
||||
@ -472,11 +554,21 @@ nsDOMStorageDB::RemoveOwners(const nsTArray<nsString> &aOwners, PRBool aMatch)
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
for (PRUint32 i = 0; i < aOwners.Length(); i++) {
|
||||
rv = statement->BindStringParameter(i, aOwners[i]);
|
||||
nsCAutoString quotaKey;
|
||||
rv = nsDOMStorageDB::CreateDomainScopeDBKey(NS_ConvertUTF16toUTF8(aOwners[i]), quotaKey);
|
||||
|
||||
if (!aIncludeSubDomains)
|
||||
quotaKey.AppendLiteral(":");
|
||||
quotaKey.AppendLiteral("*");
|
||||
|
||||
rv = statement->BindUTF8StringParameter(i, quotaKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return statement->Execute();
|
||||
rv = statement->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -487,16 +579,40 @@ nsDOMStorageDB::RemoveAll()
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDB::GetUsage(const nsAString &aOwner, PRInt32 *aUsage)
|
||||
nsDOMStorageDB::GetUsage(nsDOMStorage* aStorage, PRInt32 *aUsage)
|
||||
{
|
||||
if (aOwner == mCachedOwner) {
|
||||
return GetUsageInternal(aStorage->GetQuotaDomainDBKey(), aUsage);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDB::GetUsage(const nsACString& aDomain,
|
||||
PRBool aIncludeSubDomains, PRInt32 *aUsage)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCAutoString quotadomainDBKey;
|
||||
rv = nsDOMStorageDB::CreateQuotaDomainDBKey(aDomain,
|
||||
aIncludeSubDomains,
|
||||
quotadomainDBKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return GetUsageInternal(quotadomainDBKey, aUsage);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDB::GetUsageInternal(const nsACString& aQuotaDomainDBKey,
|
||||
PRInt32 *aUsage)
|
||||
{
|
||||
if (aQuotaDomainDBKey == mCachedOwner) {
|
||||
*aUsage = mCachedUsage;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mozStorageStatementScoper scope(mGetUsageStatement);
|
||||
|
||||
nsresult rv = mGetUsageStatement->BindStringParameter(0, aOwner);
|
||||
nsresult rv;
|
||||
|
||||
rv = mGetUsageStatement->BindUTF8StringParameter(0, aQuotaDomainDBKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool exists;
|
||||
@ -511,10 +627,96 @@ nsDOMStorageDB::GetUsage(const nsAString &aOwner, PRInt32 *aUsage)
|
||||
rv = mGetUsageStatement->GetInt32(0, aUsage);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!aOwner.IsEmpty()) {
|
||||
mCachedOwner = aOwner;
|
||||
if (!aQuotaDomainDBKey.IsEmpty()) {
|
||||
mCachedOwner = aQuotaDomainDBKey;
|
||||
mCachedUsage = *aUsage;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDB::CreateOriginScopeDBKey(nsIURI* aUri, nsACString& aKey)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
rv = CreateDomainScopeDBKey(aUri, aKey);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsCAutoString scheme;
|
||||
rv = aUri->GetScheme(scheme);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aKey.AppendLiteral(":");
|
||||
aKey.Append(scheme);
|
||||
|
||||
PRInt32 port = NS_GetRealPort(aUri);
|
||||
if (port != -1) {
|
||||
aKey.AppendLiteral(":");
|
||||
aKey.Append(nsPrintfCString(32, "%d", port));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDB::CreateDomainScopeDBKey(nsIURI* aUri, nsACString& aKey)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCAutoString host;
|
||||
rv = aUri->GetAsciiHost(host);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = CreateDomainScopeDBKey(host, aKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDB::CreateDomainScopeDBKey(const nsACString& aAsciiDomain, nsACString& aKey)
|
||||
{
|
||||
if (aAsciiDomain.IsEmpty())
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
ReverseString(aAsciiDomain, aKey);
|
||||
|
||||
aKey.AppendLiteral(".");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMStorageDB::CreateQuotaDomainDBKey(const nsACString& aAsciiDomain,
|
||||
PRBool aIncludeSubDomains, nsACString& aKey)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIEffectiveTLDService> eTLDService(do_GetService(
|
||||
NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + aAsciiDomain);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCAutoString eTLDplusOne;
|
||||
rv = eTLDService->GetBaseDomain(uri, 0, eTLDplusOne);
|
||||
if (NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS == rv) {
|
||||
// XXX bug 357323 - what to do for localhost/file exactly?
|
||||
eTLDplusOne = aAsciiDomain;
|
||||
rv = NS_OK;
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCAutoString subdomainsDBKey;
|
||||
nsDOMStorageDB::CreateDomainScopeDBKey(eTLDplusOne, subdomainsDBKey);
|
||||
|
||||
if (!aIncludeSubDomains)
|
||||
subdomainsDBKey.AppendLiteral(":");
|
||||
subdomainsDBKey.AppendLiteral("*");
|
||||
|
||||
aKey.Assign(subdomainsDBKey);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -47,6 +47,39 @@
|
||||
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 nsDOMStorageDB
|
||||
{
|
||||
public:
|
||||
@ -60,8 +93,7 @@ public:
|
||||
* Retrieve a list of all the keys associated with a particular domain.
|
||||
*/
|
||||
nsresult
|
||||
GetAllKeys(const nsAString& aDomain,
|
||||
nsDOMStorage* aStorage,
|
||||
GetAllKeys(nsDOMStorage* aStorage,
|
||||
nsTHashtable<nsSessionStorageEntry>* aKeys);
|
||||
|
||||
/**
|
||||
@ -70,21 +102,19 @@ public:
|
||||
* @throws NS_ERROR_DOM_NOT_FOUND_ERR if key not found
|
||||
*/
|
||||
nsresult
|
||||
GetKeyValue(const nsAString& aDomain,
|
||||
GetKeyValue(nsDOMStorage* aStorage,
|
||||
const nsAString& aKey,
|
||||
nsAString& aValue,
|
||||
PRBool* aSecure,
|
||||
nsAString& aOwner);
|
||||
PRBool* aSecure);
|
||||
|
||||
/**
|
||||
* Set the value and secure flag for a key in storage.
|
||||
*/
|
||||
nsresult
|
||||
SetKey(const nsAString& aDomain,
|
||||
SetKey(nsDOMStorage* aStorage,
|
||||
const nsAString& aKey,
|
||||
const nsAString& aValue,
|
||||
PRBool aSecure,
|
||||
const nsAString& aOwner,
|
||||
PRInt32 aQuota,
|
||||
PRInt32* aNewUsage);
|
||||
|
||||
@ -93,7 +123,7 @@ public:
|
||||
* not found.
|
||||
*/
|
||||
nsresult
|
||||
SetSecure(const nsAString& aDomain,
|
||||
SetSecure(nsDOMStorage* aStorage,
|
||||
const nsAString& aKey,
|
||||
const PRBool aSecure);
|
||||
|
||||
@ -101,23 +131,28 @@ public:
|
||||
* Removes a key from storage.
|
||||
*/
|
||||
nsresult
|
||||
RemoveKey(const nsAString& aDomain,
|
||||
RemoveKey(nsDOMStorage* aStorage,
|
||||
const nsAString& aKey,
|
||||
const nsAString& aOwner,
|
||||
PRInt32 aKeyUsage);
|
||||
|
||||
/**
|
||||
* Remove all keys belonging to this storage.
|
||||
*/
|
||||
nsresult ClearStorage(nsDOMStorage* aStorage);
|
||||
|
||||
/**
|
||||
* Removes all keys added by a given domain.
|
||||
*/
|
||||
nsresult
|
||||
RemoveOwner(const nsAString& aOwner);
|
||||
RemoveOwner(const nsACString& aOwner, PRBool aIncludeSubDomains);
|
||||
|
||||
/**
|
||||
* Removes keys owned by domains that either match or don't match the
|
||||
* list.
|
||||
*/
|
||||
nsresult
|
||||
RemoveOwners(const nsTArray<nsString>& aOwners, PRBool aMatch);
|
||||
RemoveOwners(const nsTArray<nsString>& aOwners,
|
||||
PRBool aIncludeSubDomains, PRBool aMatch);
|
||||
|
||||
/**
|
||||
* Removes all keys from storage. Used when clearing storage.
|
||||
@ -125,7 +160,39 @@ public:
|
||||
nsresult
|
||||
RemoveAll();
|
||||
|
||||
nsresult GetUsage(const nsAString &aOwner, PRInt32 *aUsage);
|
||||
/**
|
||||
* Returns usage for a storage using its GetQuotaDomainDBKey() as a key.
|
||||
*/
|
||||
nsresult
|
||||
GetUsage(nsDOMStorage* aStorage, PRInt32 *aUsage);
|
||||
|
||||
/**
|
||||
* Returns usage of the domain and optionaly by any subdomain.
|
||||
*/
|
||||
nsresult
|
||||
GetUsage(const nsACString& aDomain, PRBool aIncludeSubDomains, PRInt32 *aUsage);
|
||||
|
||||
/**
|
||||
* 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 CreateOriginScopeDBKey(nsIURI* aUri, nsACString& aKey);
|
||||
|
||||
/**
|
||||
* Turns "http://foo.bar.com" to "moc.rab.oof.",
|
||||
* i.e. reverses the host and appends a dot.
|
||||
*/
|
||||
static nsresult CreateDomainScopeDBKey(nsIURI* aUri, nsACString& aKey);
|
||||
static nsresult CreateDomainScopeDBKey(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 CreateQuotaDomainDBKey(const nsACString& aAsciiDomain,
|
||||
PRBool aIncludeSubDomains, nsACString& aKey);
|
||||
|
||||
protected:
|
||||
|
||||
@ -138,11 +205,15 @@ protected:
|
||||
nsCOMPtr<mozIStorageStatement> mSetSecureStatement;
|
||||
nsCOMPtr<mozIStorageStatement> mRemoveKeyStatement;
|
||||
nsCOMPtr<mozIStorageStatement> mRemoveOwnerStatement;
|
||||
nsCOMPtr<mozIStorageStatement> mRemoveStorageStatement;
|
||||
nsCOMPtr<mozIStorageStatement> mRemoveAllStatement;
|
||||
nsCOMPtr<mozIStorageStatement> mGetUsageStatement;
|
||||
|
||||
nsAutoString mCachedOwner;
|
||||
nsCString mCachedOwner;
|
||||
PRInt32 mCachedUsage;
|
||||
|
||||
nsresult
|
||||
GetUsageInternal(const nsACString& aQuotaDomainDBKey, PRInt32 *aUsage);
|
||||
};
|
||||
|
||||
#endif /* nsDOMStorageDB_h___ */
|
||||
|
@ -53,6 +53,7 @@ DIRS += \
|
||||
general \
|
||||
whatwg \
|
||||
geolocation \
|
||||
localstorage \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
80
dom/tests/mochitest/localstorage/Makefile.in
Normal file
80
dom/tests/mochitest/localstorage/Makefile.in
Normal file
@ -0,0 +1,80 @@
|
||||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2008
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Jan Bambas <honzab@firemni.cz>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = dom/tests/mochitest/localstorage
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_TEST_FILES = \
|
||||
frameChromeSlave.html \
|
||||
frameMasterEqual.html \
|
||||
frameMasterNotEqual.html \
|
||||
frameSlaveEqual.html \
|
||||
frameSlaveNotEqual.html \
|
||||
frameReplace.html \
|
||||
frameQuota.html \
|
||||
frameOrder.html \
|
||||
interOriginFrame.js \
|
||||
interOriginTest.js \
|
||||
interOriginTest2.js \
|
||||
test_localStorageBase.html \
|
||||
test_localStorageOriginsEquals.html \
|
||||
test_localStorageOriginsDiff.html \
|
||||
test_localStorageOriginsPortDiffs.html \
|
||||
test_localStorageOriginsDomainDiffs.html \
|
||||
test_localStorageOriginsSchemaDiffs.html \
|
||||
test_localStorageReplace.html \
|
||||
test_localStorageQuota.html \
|
||||
test_localStorageKeyOrder.html \
|
||||
test_removeOwnersAPI.html \
|
||||
$(NULL)
|
||||
|
||||
_CHROME_FILES = \
|
||||
test_localStorageFromChrome.xhtml \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
|
||||
libs:: $(_CHROME_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
|
9
dom/tests/mochitest/localstorage/frameChromeSlave.html
Normal file
9
dom/tests/mochitest/localstorage/frameChromeSlave.html
Normal file
@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<body>
|
||||
<span id="data"></span>
|
||||
<script>
|
||||
var span = document.getElementById("data");
|
||||
span.innerHTML = localStorage.chromekey
|
||||
</script>
|
||||
</body>
|
56
dom/tests/mochitest/localstorage/frameMasterEqual.html
Normal file
56
dom/tests/mochitest/localstorage/frameMasterEqual.html
Normal file
@ -0,0 +1,56 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>frame for localStorage test</title>
|
||||
|
||||
<script type="text/javascript" src="interOriginFrame.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var currentStep = 1;
|
||||
|
||||
function doStep()
|
||||
{
|
||||
switch (currentStep)
|
||||
{
|
||||
case 1:
|
||||
localStorage.setItem("X", "1");
|
||||
is(localStorage.getItem("X"), "1", "X is 1 in the master");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
is(localStorage.getItem("X"), "2", "X set to 2 in the master");
|
||||
localStorage.removeItem("X");
|
||||
is(localStorage.getItem("X"), null, "X was removed from the master");
|
||||
break;
|
||||
|
||||
case 5:
|
||||
is(localStorage.getItem("Y"), "3", "Y is 3 in the master");
|
||||
localStorage.setItem("Z", "4");
|
||||
is(localStorage.getItem("Z"), "4", "Z is 4 in the master");
|
||||
|
||||
localStorage.clear();
|
||||
is(localStorage.length, 0, "Master is empty");
|
||||
break;
|
||||
|
||||
case 7:
|
||||
is(localStorage.length, 0, "Master is empty");
|
||||
break;
|
||||
|
||||
case 9:
|
||||
return finishTest();
|
||||
}
|
||||
|
||||
// Increase by two to distinguish each test step order
|
||||
// in both master doStep and slave doStep functions.
|
||||
++currentStep;
|
||||
++currentStep;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="postMsg('frame loaded');">
|
||||
</body>
|
||||
</html>
|
47
dom/tests/mochitest/localstorage/frameMasterNotEqual.html
Normal file
47
dom/tests/mochitest/localstorage/frameMasterNotEqual.html
Normal file
@ -0,0 +1,47 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>frame for localStorage test</title>
|
||||
|
||||
<script type="text/javascript" src="interOriginFrame.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var currentStep = 1;
|
||||
|
||||
function doStep()
|
||||
{
|
||||
switch (currentStep)
|
||||
{
|
||||
case 1:
|
||||
localStorage.setItem("X", "1");
|
||||
is(localStorage.getItem("X"), "1", "X is 1 in the master");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
is(localStorage.getItem("X"), "1", "X remains 1 in the master");
|
||||
localStorage.removeItem("X");
|
||||
is(localStorage.getItem("X"), null, "X was removed from the master");
|
||||
break;
|
||||
|
||||
case 5:
|
||||
is(localStorage.getItem("Y"), null, "Y null in the master");
|
||||
break;
|
||||
|
||||
case 7:
|
||||
return finishTest();
|
||||
}
|
||||
|
||||
// Increase by two to distinguish each test step order
|
||||
// in both master doStep and slave doStep functions.
|
||||
++currentStep;
|
||||
++currentStep;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="postMsg('frame loaded');">
|
||||
</body>
|
||||
</html>
|
27
dom/tests/mochitest/localstorage/frameOrder.html
Normal file
27
dom/tests/mochitest/localstorage/frameOrder.html
Normal file
@ -0,0 +1,27 @@
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<script type="text/javascript">
|
||||
function doTest()
|
||||
{
|
||||
var query = location.search.substring(1);
|
||||
query = unescape(query);
|
||||
var keyNames = eval(query);
|
||||
|
||||
parent.is(localStorage.a, "10", "a = 10");
|
||||
parent.is(localStorage.b, "20", "b = 20");
|
||||
parent.is(localStorage.c, "30", "c = 30");
|
||||
parent.is(localStorage.d, "40", "d = 40");
|
||||
parent.is(localStorage.e, "50", "e = 50");
|
||||
parent.is(localStorage.length, 5, "length = 5");
|
||||
|
||||
for (var i = 0; i < localStorage.length; ++i)
|
||||
parent.is(keyNames[i], localStorage.key(i), "key "+keyNames[i]+" on same index");
|
||||
|
||||
parent.SimpleTest.finish();
|
||||
localStorage.clear();
|
||||
}
|
||||
</script>
|
||||
<body onload="doTest();">
|
||||
</body>
|
||||
</html>
|
105
dom/tests/mochitest/localstorage/frameQuota.html
Normal file
105
dom/tests/mochitest/localstorage/frameQuota.html
Normal file
@ -0,0 +1,105 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>slave for sessionStorage test</title>
|
||||
|
||||
<script type="text/javascript" src="interOriginFrame.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
const DOM_QUOTA_REACHED = 2152924150;
|
||||
|
||||
function checkException(func, exc)
|
||||
{
|
||||
var exceptionThrew = false;
|
||||
try {
|
||||
func();
|
||||
}
|
||||
catch (ex) {
|
||||
exceptionThrew = true;
|
||||
is(ex.result, exc, "Expected "+exc+" exception");
|
||||
}
|
||||
ok(exceptionThrew, "Exception "+exc+" threw at "+location);
|
||||
}
|
||||
|
||||
function doStep()
|
||||
{
|
||||
var query = location.search.substring(1);
|
||||
var queries = query.split("&");
|
||||
|
||||
var operation = queries[0];
|
||||
var keyName = queries[1];
|
||||
var result = queries[2];
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case "success":
|
||||
switch (operation)
|
||||
{
|
||||
case "add":
|
||||
// Store 500 bytes long string must succeed
|
||||
localStorage.setItem(keyName, "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
|
||||
is(localStorage.getItem(keyName), "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", "500 bytes key "+keyName+" stored");
|
||||
break;
|
||||
|
||||
case "remove":
|
||||
localStorage.removeItem(keyName);
|
||||
is(localStorage.getItem(keyName), null, "Key "+keyName+" removed");
|
||||
break;
|
||||
|
||||
case "checkclean":
|
||||
is(localStorage.getItem(keyName), null, "Key "+keyName+" not present");
|
||||
break;
|
||||
|
||||
case "checknotclean":
|
||||
is(localStorage.getItem(keyName), "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", "Key "+keyName+" is present");
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "failure":
|
||||
switch (operation)
|
||||
{
|
||||
case "add":
|
||||
// Attempt to store 500 bytes long string that doens't
|
||||
// fit the quota, have to throw DOM_QUOTA_REACHED exception
|
||||
checkException(function() {
|
||||
localStorage.setItem(keyName, "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
|
||||
}, DOM_QUOTA_REACHED);
|
||||
is(localStorage.getItem(keyName), null, "500 bytes key "+keyName+" is NOT stored");
|
||||
break;
|
||||
|
||||
case "add2":
|
||||
// Attempt to change a key value to reach the DOM quota and
|
||||
// check it fails and the old key value is still present.
|
||||
checkException(function() {
|
||||
localStorage.setItem(keyName, "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
|
||||
}, DOM_QUOTA_REACHED);
|
||||
is(localStorage.getItem(keyName), "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", "Key "+keyName+" left unchanged");
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "":
|
||||
switch (operation)
|
||||
{
|
||||
case "clear":
|
||||
localStorage.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Just inform the master we are finished now
|
||||
postMsg("done");
|
||||
return false;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="postMsg('frame loaded');">
|
||||
</body>
|
||||
</html>
|
72
dom/tests/mochitest/localstorage/frameReplace.html
Normal file
72
dom/tests/mochitest/localstorage/frameReplace.html
Normal file
@ -0,0 +1,72 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>localStorage replace frame</title>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var shell;
|
||||
|
||||
function ok(a, message)
|
||||
{
|
||||
if (!a)
|
||||
shell.postMessage("FAILURE: " + message, "http://localhost:8888");
|
||||
else
|
||||
shell.postMessage(message, "http://localhost:8888");
|
||||
}
|
||||
|
||||
function is(a, b, message)
|
||||
{
|
||||
if (a != b)
|
||||
shell.postMessage("FAILURE: " + message + ", expected "+b+" got "+a, "http://localhost:8888");
|
||||
else
|
||||
shell.postMessage(message + ", expected "+b+" got "+a, "http://localhost:8888");
|
||||
}
|
||||
|
||||
function doTest()
|
||||
{
|
||||
var query = location.search.substring(1);
|
||||
var queries = query.split("&");
|
||||
|
||||
var action = queries[0];
|
||||
shell = queries[1];
|
||||
switch (shell)
|
||||
{
|
||||
case "frame":
|
||||
shell = parent;
|
||||
break;
|
||||
case "window":
|
||||
shell = opener;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case "init":
|
||||
localStorage.setItem("A", "1");
|
||||
localStorage.setItem("B", "2");
|
||||
localStorage.setItem("C", "3");
|
||||
is(localStorage.getItem("A"), "1", "'A' is '1'");
|
||||
is(localStorage.getItem("B"), "2", "'A' is '2'");
|
||||
is(localStorage.getItem("C"), "3", "'A' is '3'");
|
||||
break;
|
||||
|
||||
case "check":
|
||||
is(localStorage.getItem("A"), null, "'A' is null");
|
||||
is(localStorage.getItem("B"), null, "'A' is null");
|
||||
is(localStorage.getItem("C"), null, "'A' is null");
|
||||
break;
|
||||
|
||||
case "clean":
|
||||
localStorage.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
shell.postMessage(action + "_done", "http://localhost:8888");
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body onload="doTest();">
|
||||
</body>
|
||||
</html>
|
51
dom/tests/mochitest/localstorage/frameSlaveEqual.html
Normal file
51
dom/tests/mochitest/localstorage/frameSlaveEqual.html
Normal file
@ -0,0 +1,51 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>frame for localStorage test</title>
|
||||
|
||||
<script type="text/javascript" src="interOriginFrame.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var currentStep = 2;
|
||||
|
||||
function doStep()
|
||||
{
|
||||
switch (currentStep)
|
||||
{
|
||||
case 2:
|
||||
is(localStorage.getItem("X"), "1", "X is 1 in the slave");
|
||||
localStorage.setItem("X", "2");
|
||||
is(localStorage.getItem("X"), "2", "X set to 2 in the slave");
|
||||
break;
|
||||
|
||||
case 4:
|
||||
is(localStorage.getItem("X"), null, "X was removed from the slave");
|
||||
localStorage.setItem("Y", "3");
|
||||
is(localStorage.getItem("Y"), "3", "Y set to 3 in the slave");
|
||||
break;
|
||||
|
||||
case 6:
|
||||
is(localStorage.length, 0, "Slave is empty");
|
||||
is(localStorage.getItem("X"), null, "X is null in the slave");
|
||||
is(localStorage.getItem("Y"), null, "Y is null in the slave");
|
||||
is(localStorage.getItem("Z"), null, "Z is null in the slave");
|
||||
break;
|
||||
|
||||
case 8:
|
||||
return finishTest();
|
||||
}
|
||||
|
||||
// Increase by two to distinguish each test step order
|
||||
// in both master doStep and slave doStep functions.
|
||||
++currentStep;
|
||||
++currentStep;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="postMsg('frame loaded');">
|
||||
</body>
|
||||
</html>
|
44
dom/tests/mochitest/localstorage/frameSlaveNotEqual.html
Normal file
44
dom/tests/mochitest/localstorage/frameSlaveNotEqual.html
Normal file
@ -0,0 +1,44 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>frame for localStorage test</title>
|
||||
|
||||
<script type="text/javascript" src="interOriginFrame.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var currentStep = 2;
|
||||
|
||||
function doStep()
|
||||
{
|
||||
switch (currentStep)
|
||||
{
|
||||
case 2:
|
||||
is(localStorage.getItem("X"), null, "X not set in the slave");
|
||||
localStorage.setItem("X", "2");
|
||||
is(localStorage.getItem("X"), "2", "X set to 2 in the slave");
|
||||
break;
|
||||
|
||||
case 4:
|
||||
is(localStorage.getItem("X"), "2", "X still set to 2 in the slave");
|
||||
localStorage.setItem("Y", "3");
|
||||
is(localStorage.getItem("Y"), "3", "Y set to 4 (MUST FAIL!) in the slave");
|
||||
break;
|
||||
|
||||
case 6:
|
||||
return finishTest();
|
||||
}
|
||||
|
||||
// Increase by two to distinguish each test step order
|
||||
// in both master doStep and slave doStep functions.
|
||||
++currentStep;
|
||||
++currentStep;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="postMsg('frame loaded');">
|
||||
</body>
|
||||
</html>
|
55
dom/tests/mochitest/localstorage/interOriginFrame.js
Normal file
55
dom/tests/mochitest/localstorage/interOriginFrame.js
Normal file
@ -0,0 +1,55 @@
|
||||
function postMsg(message)
|
||||
{
|
||||
parent.postMessage(message, "http://localhost:8888");
|
||||
}
|
||||
|
||||
window.addEventListener("message", onMessageReceived, false);
|
||||
|
||||
function onMessageReceived(event)
|
||||
{
|
||||
if (event.data == "step") {
|
||||
var performed = false;
|
||||
try {
|
||||
performed = doStep();
|
||||
}
|
||||
catch (ex) {
|
||||
postMsg("FAILURE: exception threw at "+ location +":\n" + ex);
|
||||
finishTest();
|
||||
}
|
||||
|
||||
if (performed)
|
||||
postMsg("perf");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
postMsg("Invalid message");
|
||||
}
|
||||
|
||||
function ok(a, message)
|
||||
{
|
||||
if (!a)
|
||||
postMsg("FAILURE: " + message);
|
||||
else
|
||||
postMsg(message);
|
||||
}
|
||||
|
||||
function is(a, b, message)
|
||||
{
|
||||
if (a != b)
|
||||
postMsg("FAILURE: " + message + ", expected "+b+" got "+a);
|
||||
else
|
||||
postMsg(message + ", expected "+b+" got "+a);
|
||||
}
|
||||
|
||||
function todo(a, b, message)
|
||||
{
|
||||
postMsg("TODO: " + message + ", expected "+b+" got "+a);
|
||||
}
|
||||
|
||||
function finishTest()
|
||||
{
|
||||
localStorage.clear();
|
||||
postMsg("done");
|
||||
return false;
|
||||
}
|
42
dom/tests/mochitest/localstorage/interOriginTest.js
Normal file
42
dom/tests/mochitest/localstorage/interOriginTest.js
Normal file
@ -0,0 +1,42 @@
|
||||
var slaveLoadsPending = 1;
|
||||
|
||||
var slaveOrigin = "";
|
||||
var slave = null;
|
||||
|
||||
var failureRegExp = new RegExp("^FAILURE");
|
||||
const slavePath = "/tests/dom/tests/mochitest/localstorage/";
|
||||
|
||||
window.addEventListener("message", onMessageReceived, false);
|
||||
|
||||
function onMessageReceived(event)
|
||||
{
|
||||
switch (event.data)
|
||||
{
|
||||
// Indication of the frame onload event
|
||||
case "frame loaded":
|
||||
if (--slaveLoadsPending)
|
||||
break;
|
||||
|
||||
// Just fall through...
|
||||
|
||||
// Indication of successfully finished step of a test
|
||||
case "perf":
|
||||
if (event.data == "perf")
|
||||
doStep();
|
||||
|
||||
slave.postMessage("step", slaveOrigin);
|
||||
break;
|
||||
|
||||
// Indication of all test parts finish (from any of the frames)
|
||||
case "done":
|
||||
localStorage.clear();
|
||||
slaveLoadsPending = 1;
|
||||
doNextTest();
|
||||
break;
|
||||
|
||||
// Any other message indicates error or succes message of a test
|
||||
default:
|
||||
SimpleTest.ok(!event.data.match(failureRegExp), event.data);
|
||||
break;
|
||||
}
|
||||
}
|
53
dom/tests/mochitest/localstorage/interOriginTest2.js
Normal file
53
dom/tests/mochitest/localstorage/interOriginTest2.js
Normal file
@ -0,0 +1,53 @@
|
||||
var frameLoadsPending = 2;
|
||||
|
||||
var callMasterFrame = true;
|
||||
var testDone = false;
|
||||
|
||||
var masterFrameOrigin = "";
|
||||
var slaveFrameOrigin = "";
|
||||
|
||||
var failureRegExp = new RegExp("^FAILURE");
|
||||
var todoRegExp = new RegExp("^TODO");
|
||||
|
||||
const framePath = "/tests/dom/tests/mochitest/localstorage/";
|
||||
|
||||
window.addEventListener("message", onMessageReceived, false);
|
||||
|
||||
function onMessageReceived(event)
|
||||
{
|
||||
switch (event.data)
|
||||
{
|
||||
// Indication of the frame onload event
|
||||
case "frame loaded":
|
||||
if (--frameLoadsPending)
|
||||
break;
|
||||
|
||||
// Just fall through...
|
||||
|
||||
// Indication of successfully finished step of a test
|
||||
case "perf":
|
||||
if (callMasterFrame)
|
||||
masterFrame.postMessage("step", masterFrameOrigin);
|
||||
else
|
||||
slaveFrame.postMessage("step", slaveFrameOrigin);
|
||||
callMasterFrame = !callMasterFrame;
|
||||
break;
|
||||
|
||||
// Indication of all test parts finish (from any of the frames)
|
||||
case "done":
|
||||
if (testDone)
|
||||
break;
|
||||
|
||||
testDone = true;
|
||||
SimpleTest.finish();
|
||||
break;
|
||||
|
||||
// Any other message indicates error, succes or todo message of a test
|
||||
default:
|
||||
if (event.data.match(todoRegExp))
|
||||
SimpleTest.todo(false, event.data);
|
||||
else
|
||||
SimpleTest.ok(!event.data.match(failureRegExp), event.data);
|
||||
break;
|
||||
}
|
||||
}
|
187
dom/tests/mochitest/localstorage/test_localStorageBase.html
Normal file
187
dom/tests/mochitest/localstorage/test_localStorageBase.html
Normal file
@ -0,0 +1,187 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>localStorage basic test</title>
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<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">
|
||||
|
||||
var INDEX_SIZE_ERR = 1;
|
||||
|
||||
function checkException(func, exc)
|
||||
{
|
||||
var exceptionThrew = false;
|
||||
try {
|
||||
func();
|
||||
}
|
||||
catch (ex) {
|
||||
exceptionThrew = true;
|
||||
is(ex.code, exc, "Expected "+exc+" exception");
|
||||
}
|
||||
ok(exceptionThrew, "Exception "+exc+" threw");
|
||||
}
|
||||
|
||||
function startTest()
|
||||
{
|
||||
// Initially check the localStorage is empty
|
||||
is(localStorage.length, 0, "The storage is empty [1]");
|
||||
checkException(function() {localStorage.key(0);}, INDEX_SIZE_ERR);
|
||||
checkException(function() {localStorage.key(-1);}, INDEX_SIZE_ERR);
|
||||
checkException(function() {localStorage.key(1);}, INDEX_SIZE_ERR);
|
||||
is(localStorage.getItem("nonexisting"), null, "Nonexisting item is null (getItem())");
|
||||
is(localStorage["nonexisting"], null, "Nonexisting item is null (array access)");
|
||||
is(localStorage.nonexisting, null, "Nonexisting item is null (property access)");
|
||||
localStorage.removeItem("nonexisting"); // Just check there is no exception
|
||||
|
||||
is(typeof localStorage.getItem("nonexisting"), "object", "getItem('nonexisting') is object");
|
||||
is(typeof localStorage["nonexisting"], "object", "['nonexisting'] is object");
|
||||
is(typeof localStorage.nonexisting, "object", "nonexisting is object");
|
||||
is(typeof localStorage.getItem("nonexisting2"), "object", "getItem('nonexisting2') is object");
|
||||
is(typeof localStorage["nonexisting2"], "object", "['nonexisting2'] is object");
|
||||
is(typeof localStorage.nonexisting2, "object", "nonexisting2 is object");
|
||||
|
||||
// add an empty-value key
|
||||
localStorage.setItem("empty", "");
|
||||
is(localStorage.getItem("empty"), "", "Empty value (getItem())");
|
||||
is(localStorage["empty"], "", "Empty value (array access)");
|
||||
is(localStorage.empty, "", "Empty value (property access)");
|
||||
is(typeof localStorage.getItem("empty"), "string", "getItem('empty') is string");
|
||||
is(typeof localStorage["empty"], "string", "['empty'] is string");
|
||||
is(typeof localStorage.empty, "string", "empty is string");
|
||||
localStorage.removeItem("empty");
|
||||
is(localStorage.length, 0, "The storage has no keys");
|
||||
is(localStorage.getItem("empty"), null, "empty item is null (getItem())");
|
||||
is(localStorage["empty"], null, "empty item is null (array access)");
|
||||
is(localStorage.empty, null, "empty item is null (property access)");
|
||||
is(typeof localStorage.getItem("empty"), "object", "getItem('empty') is object");
|
||||
is(typeof localStorage["empty"], "object", "['empty'] is object");
|
||||
is(typeof localStorage.empty, "object", "empty is object");
|
||||
|
||||
// add one key, check it is there
|
||||
localStorage.setItem("key1", "value1");
|
||||
is(localStorage.length, 1, "The storage has one key-value pair");
|
||||
is(localStorage.key(0), "key1");
|
||||
checkException(function() {localStorage.key(-1);}, INDEX_SIZE_ERR);
|
||||
checkException(function() {localStorage.key(1);}, INDEX_SIZE_ERR);
|
||||
|
||||
// check all access method give the correct result
|
||||
// and are of the correct type
|
||||
is(localStorage.getItem("key1"), "value1", "getItem('key1') == value1");
|
||||
is(localStorage["key1"], "value1", "['key1'] == value1");
|
||||
is(localStorage.key1, "value1", "key1 == value1");
|
||||
|
||||
is(typeof localStorage.getItem("key1"), "string", "getItem('key1') is string");
|
||||
is(typeof localStorage["key1"], "string", "['key1'] is string");
|
||||
is(typeof localStorage.key1, "string", "key1 is string");
|
||||
|
||||
// remove the previously added key and check the storage is empty
|
||||
localStorage.removeItem("key1");
|
||||
is(localStorage.length, 0, "The storage is empty [2]");
|
||||
checkException(function() {localStorage.key(0);}, INDEX_SIZE_ERR);
|
||||
is(localStorage.getItem("key1"), null, "\'key1\' removed");
|
||||
|
||||
is(typeof localStorage.getItem("key1"), "object", "getItem('key1') is object");
|
||||
is(typeof localStorage["key1"], "object", "['key1'] is object");
|
||||
is(typeof localStorage.key1, "object", "key1 is object");
|
||||
|
||||
// add one key, check it is there
|
||||
localStorage.setItem("key1", "value1");
|
||||
is(localStorage.length, 1, "The storage has one key-value pair");
|
||||
is(localStorage.key(0), "key1");
|
||||
is(localStorage.getItem("key1"), "value1");
|
||||
|
||||
// add a second key
|
||||
localStorage.setItem("key2", "value2");
|
||||
is(localStorage.length, 2, "The storage has two key-value pairs");
|
||||
is(localStorage.key(1), "key1"); // This test might not be accurate because order is not preserved
|
||||
is(localStorage.key(0), "key2");
|
||||
is(localStorage.getItem("key1"), "value1");
|
||||
is(localStorage.getItem("key2"), "value2");
|
||||
|
||||
// change the second key
|
||||
localStorage.setItem("key2", "value2-2");
|
||||
is(localStorage.length, 2, "The storage has two key-value pairs");
|
||||
is(localStorage.key(1), "key1"); // After key value changes the order must be preserved
|
||||
is(localStorage.key(0), "key2");
|
||||
checkException(function() {localStorage.key(-1);}, INDEX_SIZE_ERR);
|
||||
checkException(function() {localStorage.key(2);}, INDEX_SIZE_ERR);
|
||||
is(localStorage.getItem("key1"), "value1");
|
||||
is(localStorage.getItem("key2"), "value2-2");
|
||||
|
||||
// change the first key
|
||||
localStorage.setItem("key1", "value1-2");
|
||||
is(localStorage.length, 2, "The storage has two key-value pairs");
|
||||
is(localStorage.key(1), "key1"); // After key value changes the order must be preserved
|
||||
is(localStorage.key(0), "key2");
|
||||
checkException(function() {localStorage.key(-1);}, INDEX_SIZE_ERR);
|
||||
checkException(function() {localStorage.key(2);}, INDEX_SIZE_ERR);
|
||||
is(localStorage.getItem("key1"), "value1-2");
|
||||
is(localStorage.getItem("key2"), "value2-2");
|
||||
|
||||
// remove the second key
|
||||
localStorage.removeItem("key2");
|
||||
is(localStorage.length, 1, "The storage has one key-value pair");
|
||||
is(localStorage.key(0), "key1");
|
||||
checkException(function() {localStorage.key(-1);}, INDEX_SIZE_ERR);
|
||||
checkException(function() {localStorage.key(1);}, INDEX_SIZE_ERR);
|
||||
is(localStorage.getItem("key1"), "value1-2");
|
||||
|
||||
// JS property test
|
||||
localStorage.testA = "valueA";
|
||||
is(localStorage.testA, "valueA");
|
||||
is(localStorage["testA"], "valueA");
|
||||
is(localStorage.getItem("testA"), "valueA");
|
||||
|
||||
localStorage.testA = "valueA2";
|
||||
is(localStorage.testA, "valueA2");
|
||||
is(localStorage["testA"], "valueA2");
|
||||
is(localStorage.getItem("testA"), "valueA2");
|
||||
|
||||
localStorage["testB"] = "valueB";
|
||||
is(localStorage.testB, "valueB");
|
||||
is(localStorage["testB"], "valueB");
|
||||
is(localStorage.getItem("testB"), "valueB");
|
||||
|
||||
localStorage["testB"] = "valueB2";
|
||||
is(localStorage.testB, "valueB2");
|
||||
is(localStorage["testB"], "valueB2");
|
||||
is(localStorage.getItem("testB"), "valueB2");
|
||||
|
||||
localStorage.setItem("testC", "valueC");
|
||||
is(localStorage.testC, "valueC");
|
||||
is(localStorage["testC"], "valueC");
|
||||
is(localStorage.getItem("testC"), "valueC");
|
||||
|
||||
localStorage.setItem("testC", "valueC2");
|
||||
is(localStorage.testC, "valueC2");
|
||||
is(localStorage["testC"], "valueC2");
|
||||
is(localStorage.getItem("testC"), "valueC2");
|
||||
|
||||
// Clear the storage
|
||||
localStorage.clear();
|
||||
is(localStorage.length, 0, "The storage is empty [3]");
|
||||
checkException(function() {localStorage.key(0);}, INDEX_SIZE_ERR); // this is unspecified!
|
||||
checkException(function() {localStorage.key(-1);}, INDEX_SIZE_ERR);
|
||||
checkException(function() {localStorage.key(1);}, INDEX_SIZE_ERR);
|
||||
is(localStorage.getItem("nonexisting"), null, "Nonexisting item is null");
|
||||
is(localStorage.getItem("key1"), null, "key1 removed");
|
||||
is(localStorage.getItem("key2"), null, "key2 removed");
|
||||
localStorage.removeItem("nonexisting"); // Just check there is no exception
|
||||
localStorage.removeItem("key1"); // Just check there is no exception
|
||||
localStorage.removeItem("key2"); // Just check there is no exception
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="startTest();">
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,46 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>localStorage basic test</title>
|
||||
|
||||
<script type="text/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function startTest()
|
||||
{
|
||||
var url = "http://example.com/tests/dom/tests/mochitest/localstorage/frameChromeSlave.html";
|
||||
var ios = Components.classes["@mozilla.org/network/io-service;1"]
|
||||
.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"]
|
||||
.getService(Components.interfaces.nsIDOMStorageManager);
|
||||
|
||||
var uri = ios.newURI(url, "", null);
|
||||
var principal = ssm.getCodebasePrincipal(uri);
|
||||
var storage = dsm.getLocalStorageForPrincipal(principal);
|
||||
|
||||
storage.wrappedJSObject.setItem("chromekey", "chromevalue");
|
||||
|
||||
var aframe = document.getElementById("aframe");
|
||||
aframe.onload = function()
|
||||
{
|
||||
is(storage.wrappedJSObject.getItem("chromekey"), "chromevalue");
|
||||
is(aframe.contentDocument.getElementById("data").innerHTML, "chromevalue");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
aframe.src = "http://example.com/tests/dom/tests/mochitest/localstorage/frameChromeSlave.html";
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="startTest();">
|
||||
<iframe src="" id="aframe"></iframe>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,76 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>localStorage key order test</title>
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
|
||||
<!--
|
||||
Check we preserve order of keys in localStorage when
|
||||
keys are just modified. When a new key is added or
|
||||
a key is removed order is again unspecified.
|
||||
-->
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var INDEX_SIZE_ERR = 1;
|
||||
|
||||
function startTest()
|
||||
{
|
||||
try
|
||||
{
|
||||
var keyNames = new Array;
|
||||
localStorage.a = "1";
|
||||
localStorage.b = "2";
|
||||
localStorage.c = "3";
|
||||
localStorage.d = "4";
|
||||
localStorage.e = "5";
|
||||
|
||||
is(localStorage.a, "1", "a = 1");
|
||||
is(localStorage.b, "2", "b = 2");
|
||||
is(localStorage.c, "3", "c = 3");
|
||||
is(localStorage.d, "4", "d = 4");
|
||||
is(localStorage.e, "5", "e = 5");
|
||||
is(localStorage.length, 5, "length = 5");
|
||||
|
||||
for (var i = 0; i < localStorage.length; ++i)
|
||||
keyNames[i] = localStorage.key(i);
|
||||
|
||||
localStorage.a = "10";
|
||||
localStorage.b = "20";
|
||||
localStorage.c = "30";
|
||||
localStorage.d = "40";
|
||||
localStorage.e = "50";
|
||||
|
||||
is(localStorage.a, "10", "a = 10");
|
||||
is(localStorage.b, "20", "b = 20");
|
||||
is(localStorage.c, "30", "c = 30");
|
||||
is(localStorage.d, "40", "d = 40");
|
||||
is(localStorage.e, "50", "e = 50");
|
||||
is(localStorage.length, 5, "length = 5");
|
||||
|
||||
for (var i = 0; i < localStorage.length; ++i)
|
||||
is(keyNames[i], localStorage.key(i), "key "+keyNames[i]+" on same index");
|
||||
|
||||
keyNamesStringify = "[\"" + keyNames.join("\",\"") + "\"]";
|
||||
frame.location = "http://localhost:8888/tests/dom/tests/mochitest/localstorage/frameOrder.html?" +
|
||||
keyNamesStringify;
|
||||
}
|
||||
catch (ex)
|
||||
{
|
||||
localStorage.clear();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="startTest();">
|
||||
<iframe src="" name="frame"></frame>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,44 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>localStorage different origins</title>
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="interOriginTest2.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
|
||||
<!--
|
||||
This test loads two frames from different
|
||||
origins and checks that entries of localStorage
|
||||
objects don't leak each between other.
|
||||
|
||||
The subsystem is based on postMessage and addEventListener
|
||||
to send messages among different origins. The subsystem waits
|
||||
for both frames be loaded and then alternately calls each frames'
|
||||
doStep() function that on each call proceeds with a single step
|
||||
of the test on its side. This way the subsystem alternate between
|
||||
both frames until both sequences completely finish.
|
||||
-->
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function startTest()
|
||||
{
|
||||
masterFrameOrigin = "http://example.org:80";
|
||||
slaveFrameOrigin = "http://example.com:80";
|
||||
|
||||
masterFrame.location = masterFrameOrigin + framePath + "frameMasterNotEqual.html";
|
||||
slaveFrame.location = slaveFrameOrigin + framePath + "frameSlaveNotEqual.html";
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="startTest();">
|
||||
<iframe src="" name="masterFrame"></iframe>
|
||||
<iframe src="" name="slaveFrame"></iframe>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,44 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>localStorage different domains</title>
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="interOriginTest2.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
|
||||
<!--
|
||||
This test loads two frames from different
|
||||
origins and checks that entries of localStorage
|
||||
objects don't leak each between other.
|
||||
|
||||
The subsystem is based on postMessage and addEventListener
|
||||
to send messages among different origins. The subsystem waits
|
||||
for both frames be loaded and then alternately calls each frames'
|
||||
doStep() function that on each call proceeds with a single step
|
||||
of the test on its side. This way the subsystem alternate between
|
||||
both frames until both sequences completely finish.
|
||||
-->
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function startTest()
|
||||
{
|
||||
masterFrameOrigin = "http://example.org:80";
|
||||
slaveFrameOrigin = "http://test1.example.org:80";
|
||||
|
||||
masterFrame.location = masterFrameOrigin + framePath + "frameMasterNotEqual.html";
|
||||
slaveFrame.location = slaveFrameOrigin + framePath + "frameSlaveNotEqual.html";
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="startTest();">
|
||||
<iframe src="" name="masterFrame"></iframe>
|
||||
<iframe src="" name="slaveFrame"></iframe>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,45 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>localStorage equal origins</title>
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="interOriginTest2.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
|
||||
<!--
|
||||
This test loads two frames from equal
|
||||
origins and checks that entries of localStorage
|
||||
objects are kept up-to-date and synchronized
|
||||
between both frames.
|
||||
|
||||
The subsystem is based on postMessage and addEventListener
|
||||
to send messages among different origins. The subsystem waits
|
||||
for both frames be loaded and then alternately calls each frames'
|
||||
doStep() function that on each call proceeds with a single step
|
||||
of the test on its side. This way the subsystem alternate between
|
||||
both frames until both sequences completely finish.
|
||||
-->
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function startTest()
|
||||
{
|
||||
masterFrameOrigin = "http://example.org:80";
|
||||
slaveFrameOrigin = "http://example.org:80";
|
||||
|
||||
masterFrame.location = masterFrameOrigin + framePath + "frameMasterEqual.html";
|
||||
slaveFrame.location = slaveFrameOrigin + framePath + "frameSlaveEqual.html";
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="startTest();">
|
||||
<iframe src="" name="masterFrame"></iframe>
|
||||
<iframe src="" name="slaveFrame"></iframe>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,44 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>localStorage different port numbers</title>
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="interOriginTest2.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
|
||||
<!--
|
||||
This test loads two frames from different
|
||||
origins and checks that entries of localStorage
|
||||
objects don't leak each between other.
|
||||
|
||||
The subsystem is based on postMessage and addEventListener
|
||||
to send messages among different origins. The subsystem waits
|
||||
for both frames be loaded and then alternately calls each frames'
|
||||
doStep() function that on each call proceeds with a single step
|
||||
of the test on its side. This way the subsystem alternate between
|
||||
both frames until both sequences completely finish.
|
||||
-->
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function startTest()
|
||||
{
|
||||
masterFrameOrigin = "http://example.org:80";
|
||||
slaveFrameOrigin = "http://example.org:8000";
|
||||
|
||||
masterFrame.location = masterFrameOrigin + framePath + "frameMasterNotEqual.html";
|
||||
slaveFrame.location = slaveFrameOrigin + framePath + "frameSlaveNotEqual.html";
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="startTest();">
|
||||
<iframe src="" name="masterFrame"></iframe>
|
||||
<iframe src="" name="slaveFrame"></iframe>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,44 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>localStorage different domains</title>
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="interOriginTest2.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
|
||||
<!--
|
||||
This test loads two frames from different
|
||||
origins and checks that entries of localStorage
|
||||
objects don't leak each between other.
|
||||
|
||||
The subsystem is based on postMessage and addEventListener
|
||||
to send messages among different origins. The subsystem waits
|
||||
for both frames be loaded and then alternately calls each frames'
|
||||
doStep() function that on each call proceeds with a single step
|
||||
of the test on its side. This way the subsystem alternate between
|
||||
both frames until both sequences completely finish.
|
||||
-->
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function startTest()
|
||||
{
|
||||
masterFrameOrigin = "http://example.com";
|
||||
slaveFrameOrigin = "https://example.com";
|
||||
|
||||
masterFrame.location = masterFrameOrigin + framePath + "frameMasterNotEqual.html";
|
||||
slaveFrame.location = slaveFrameOrigin + framePath + "frameSlaveNotEqual.html";
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="startTest();">
|
||||
<iframe src="" name="masterFrame"></iframe>
|
||||
<iframe src="" name="slaveFrame"></iframe>
|
||||
</body>
|
||||
</html>
|
127
dom/tests/mochitest/localstorage/test_localStorageQuota.html
Normal file
127
dom/tests/mochitest/localstorage/test_localStorageQuota.html
Normal file
@ -0,0 +1,127 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>localStorage and DOM quota test</title>
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<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");
|
||||
|
||||
var currentTest = 1;
|
||||
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
|
||||
.getService(Components.interfaces.nsIPrefBranch);
|
||||
var quota;
|
||||
|
||||
function doNextTest()
|
||||
{
|
||||
slave = frame;
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
switch (currentTest)
|
||||
{
|
||||
// Initialy setup the quota to testing value of 1024B and
|
||||
// set a 500 bytes key with name length 1 (allocate 501 bytes)
|
||||
case 1:
|
||||
try {
|
||||
quota = prefs.getIntPref("dom.storage.default_quota");
|
||||
} catch (ex) {
|
||||
quota = 5*1024;
|
||||
}
|
||||
prefs.setIntPref("dom.storage.default_quota", 1);
|
||||
|
||||
slaveOrigin = "http://example.com";
|
||||
slave.location = slaveOrigin + slavePath + "frameQuota.html?add&A&success";
|
||||
break;
|
||||
|
||||
// In subdomain now set another key with length 500 bytes, i.e.
|
||||
// allocate 501 bytes
|
||||
case 2:
|
||||
slaveOrigin = "http://test1.example.com";
|
||||
slave.location = slaveOrigin + slavePath + "frameQuota.html?add&B&success";
|
||||
break;
|
||||
|
||||
// Try to set the same key value again to check we don't fail
|
||||
// even 1002 bytes has already been exhausted from the quota
|
||||
// We just change the value of an existing key.
|
||||
case 3:
|
||||
slaveOrigin = "http://test1.example.com";
|
||||
slave.location = slaveOrigin + slavePath + "frameQuota.html?add&B&success";
|
||||
break;
|
||||
|
||||
// Try to set the same key to a larger value that would lead to
|
||||
// quota reach and check that the value is still the old one
|
||||
case 4:
|
||||
slaveOrigin = "http://test1.example.com";
|
||||
slave.location = slaveOrigin + slavePath + "frameQuota.html?add2&B&failure";
|
||||
break;
|
||||
|
||||
// In a different subdomain try to set a new 500 bytes key
|
||||
// and check we fail because we are over the quota
|
||||
case 5:
|
||||
slaveOrigin = "https://test2.example.com";
|
||||
slave.location = slaveOrigin + slavePath + "frameQuota.html?add&C&failure";
|
||||
break;
|
||||
|
||||
// Remove from the second subdomain the second key, it must not fail
|
||||
// This should release the allocated space of the quota assigned to
|
||||
// example.com.
|
||||
case 6:
|
||||
slaveOrigin = "http://test1.example.com";
|
||||
slave.location = slaveOrigin + slavePath + "frameQuota.html?remove&B&success";
|
||||
break;
|
||||
|
||||
// Now try again to set 500 bytes key, it must succeed.
|
||||
case 7:
|
||||
slaveOrigin = "https://test2.example.com";
|
||||
slave.location = slaveOrigin + slavePath + "frameQuota.html?add&C&success";
|
||||
break;
|
||||
|
||||
case 8:
|
||||
// Do a clean up...
|
||||
// TODO Bug 455070, use just ?clear what invokes call
|
||||
// of clear() in the target frame. W/o clear method we must
|
||||
// call clear implemented as removeItem for each item in
|
||||
// the localStorage.
|
||||
slaveOrigin = "http://example.com";
|
||||
slave.location = slaveOrigin + slavePath + "frameQuota.html?clear&A&";
|
||||
break;
|
||||
|
||||
case 9:
|
||||
// Do a clean up...
|
||||
slaveOrigin = "http://test1.example.com";
|
||||
slave.location = slaveOrigin + slavePath + "frameQuota.html?clear&B&";
|
||||
break;
|
||||
|
||||
case 10:
|
||||
// Do a clean up...
|
||||
slaveOrigin = "https://test2.example.com";
|
||||
slave.location = slaveOrigin + slavePath + "frameQuota.html?clear&C&";
|
||||
break;
|
||||
|
||||
case 11:
|
||||
prefs.setIntPref("dom.storage.default_quota", quota);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
++currentTest;
|
||||
}
|
||||
|
||||
function doStep()
|
||||
{
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="doNextTest();">
|
||||
<iframe src="" name="frame"></iframe>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,78 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>localStorage replace test</title>
|
||||
|
||||
<!--
|
||||
This test checks that localStorage object doesn't leak
|
||||
in a window that changes its location. We do this by switching
|
||||
frame location inside of this window and then by changing location
|
||||
of a top level window.
|
||||
-->
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<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">
|
||||
|
||||
var shell;
|
||||
var shellType;
|
||||
var failureRegExp = new RegExp("^FAILURE");
|
||||
|
||||
window.addEventListener("message", onMessageReceived, false);
|
||||
|
||||
function onMessageReceived(event)
|
||||
{
|
||||
switch (event.data)
|
||||
{
|
||||
case "init_done":
|
||||
// This is frame with different origin in the same browsing context
|
||||
// as the first frame adding data to localStorage of the first origin.
|
||||
shell.location = "http://example.com:80/tests/dom/tests/mochitest/localstorage/frameReplace.html?check&" + shellType;
|
||||
break;
|
||||
|
||||
case "check_done":
|
||||
// Clean the localStorage of the first origin.
|
||||
shell.location = "http://example.org:80/tests/dom/tests/mochitest/localstorage/frameReplace.html?clean&" + shellType;
|
||||
break;
|
||||
|
||||
case "clean_done":
|
||||
switch (shellType)
|
||||
{
|
||||
case "frame":
|
||||
// We finished testing in a frame
|
||||
// proceed with test in a separate window
|
||||
shellType = "window";
|
||||
shell = window.open("http://example.org:80/tests/dom/tests/mochitest/localstorage/frameReplace.html?init&" + shellType);
|
||||
break;
|
||||
|
||||
case "window":
|
||||
shell.close();
|
||||
window.setTimeout(function() {SimpleTest.finish();}, 0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
SimpleTest.ok(!event.data.match(failureRegExp), event.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function startTest()
|
||||
{
|
||||
shellType = "frame";
|
||||
shell = frame;
|
||||
shell.location = "http://example.org:80/tests/dom/tests/mochitest/localstorage/frameReplace.html?init&" + shellType;
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="startTest();">
|
||||
<iframe src="" name="frame"></iframe>
|
||||
</body>
|
||||
</html>
|
135
dom/tests/mochitest/localstorage/test_removeOwnersAPI.html
Normal file
135
dom/tests/mochitest/localstorage/test_removeOwnersAPI.html
Normal file
@ -0,0 +1,135 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>localStorage and DOM quota test</title>
|
||||
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<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");
|
||||
|
||||
var currentTest = 1;
|
||||
var currentStep = 1;
|
||||
|
||||
function addOfflineApp(url)
|
||||
{
|
||||
var permissionManager = Components.classes["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Components.interfaces.nsIPermissionManager);
|
||||
var uri = Components.classes["@mozilla.org/network/io-service;1"]
|
||||
.getService(Components.interfaces.nsIIOService)
|
||||
.newURI(url, null, null);
|
||||
permissionManager.add(uri, "offline-app",
|
||||
Components.interfaces.nsIPermissionManager.ALLOW_ACTION);
|
||||
}
|
||||
|
||||
function removeOfflineApp(url)
|
||||
{
|
||||
var permissionManager = Components.classes["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Components.interfaces.nsIPermissionManager);
|
||||
var uri = Components.classes["@mozilla.org/network/io-service;1"]
|
||||
.getService(Components.interfaces.nsIIOService)
|
||||
.newURI(url, null, null);
|
||||
permissionManager.remove(uri.host, "offline-app");
|
||||
}
|
||||
|
||||
function doNextTest()
|
||||
{
|
||||
slave = frame;
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
currentStep = 1;
|
||||
|
||||
switch (currentTest)
|
||||
{
|
||||
// Add something to storage of example.com
|
||||
case 1:
|
||||
slaveOrigin = "http://example.com";
|
||||
slave.location = slaveOrigin + slavePath + "frameQuota.html?add&A&success";
|
||||
break;
|
||||
|
||||
// Add something to storage of test1.example.com, secure schema
|
||||
case 2:
|
||||
slaveOrigin = "https://test1.example.com";
|
||||
slave.location = slaveOrigin + slavePath + "frameQuota.html?add&B&success";
|
||||
break;
|
||||
|
||||
// Add something to storage of http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp, secure schema
|
||||
case 3:
|
||||
slaveOrigin = "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp";
|
||||
slave.location = slaveOrigin + slavePath + "frameQuota.html?add&C&success";
|
||||
break;
|
||||
|
||||
// Call RemoveOwners API through storage manager.
|
||||
// Classify the sites above as offline-app using
|
||||
// the permission manager to let the storage manager
|
||||
// know about them.
|
||||
case 4:
|
||||
addOfflineApp("http://example.com");
|
||||
addOfflineApp("http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp");
|
||||
var manager = Components.classes["@mozilla.org/dom/storagemanager;1"]
|
||||
.getService(Components.interfaces.nsIDOMStorageManager);
|
||||
try {
|
||||
manager.clearOfflineApps();
|
||||
}
|
||||
catch (ex) {
|
||||
ok(false, "Exception not thrown during clearOfflineApps()");
|
||||
}
|
||||
removeOfflineApp("http://example.com");
|
||||
removeOfflineApp("http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp");
|
||||
|
||||
// Now check that those two sites' data disappeared
|
||||
slaveOrigin = "http://example.com";
|
||||
slave.location = slaveOrigin + slavePath + "frameQuota.html?checkclean&A&success";
|
||||
break;
|
||||
|
||||
case 5:
|
||||
slaveOrigin = "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp";
|
||||
slave.location = slaveOrigin + slavePath + "frameQuota.html?checkclean&C&success";
|
||||
break;
|
||||
|
||||
case 6:
|
||||
slaveOrigin = "https://test1.example.com";
|
||||
slave.location = slaveOrigin + slavePath + "frameQuota.html?checknotclean&B&success";
|
||||
break;
|
||||
|
||||
case 7:
|
||||
addOfflineApp("https://test1.example.com");
|
||||
var manager = Components.classes["@mozilla.org/dom/storagemanager;1"]
|
||||
.getService(Components.interfaces.nsIDOMStorageManager);
|
||||
try {
|
||||
manager.clearOfflineApps();
|
||||
}
|
||||
catch (ex) {
|
||||
ok(false, "Exception not thrown during clearOfflineApps()");
|
||||
}
|
||||
removeOfflineApp("https://test1.example.com");
|
||||
|
||||
// Now check that those site's data disappeared
|
||||
slaveOrigin = "https://test1.example.com";
|
||||
slave.location = slaveOrigin + slavePath + "frameQuota.html?checkclean&B&success";
|
||||
break;
|
||||
|
||||
case 8:
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
++currentTest;
|
||||
}
|
||||
|
||||
function doStep()
|
||||
{
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="doNextTest();">
|
||||
<iframe src="" name="frame"></iframe>
|
||||
</body>
|
||||
</html>
|
@ -943,7 +943,7 @@ nsWindowWatcher::OpenWindowJSInternal(nsIDOMWindow *aParent,
|
||||
if (piStorage){
|
||||
storage = piStorage->Clone();
|
||||
newDocShell->AddSessionStorage(
|
||||
NS_ConvertUTF16toUTF8(piStorage->Domain()),
|
||||
piStorage->Domain(),
|
||||
storage);
|
||||
}
|
||||
}
|
||||
|
@ -460,11 +460,12 @@ members = [
|
||||
|
||||
# dom/public/idl/storage
|
||||
'nsIDOMToString.toString',
|
||||
'nsIDOMStorage.setItem',
|
||||
'nsIDOMStorage.length',
|
||||
'nsIDOMStorage.getItem',
|
||||
'nsIDOMStorage.key',
|
||||
'nsIDOMStorage.removeItem',
|
||||
'nsIDOMStorage2.setItem',
|
||||
'nsIDOMStorage2.length',
|
||||
'nsIDOMStorage2.getItem',
|
||||
'nsIDOMStorage2.key',
|
||||
'nsIDOMStorage2.removeItem',
|
||||
'nsIDOMStorage2.clear',
|
||||
'nsIDOMStorageItem.value',
|
||||
'nsIDOMStorageWindow.sessionStorage',
|
||||
'nsIDOMStorageWindow.globalStorage',
|
||||
|
Loading…
Reference in New Issue
Block a user