diff --git a/mozilla/Makefile.in b/mozilla/Makefile.in index 588cd864941..675077fe849 100644 --- a/mozilla/Makefile.in +++ b/mozilla/Makefile.in @@ -152,8 +152,13 @@ tier_9_dirs += \ modules/libutil \ netwerk \ modules/libjar \ + db \ $(NULL) +ifdef MOZ_STORAGE +tier_9_dirs += storage +endif + ifdef MOZ_XUL tier_9_dirs += rdf endif @@ -235,7 +240,6 @@ tier_50_dirs += xpfe/bootstrap/appleevents endif tier_50_dirs += \ - db \ xpfe \ toolkit/components \ $(NULL) @@ -282,10 +286,6 @@ endif endif endif -ifdef MOZ_STORAGE -tier_50_dirs += storage -endif - ifdef MOZ_XUL_APP ifndef BUILD_STATIC_LIBS tier_50_dirs += toolkit/library diff --git a/mozilla/docshell/base/nsDocShell.cpp b/mozilla/docshell/base/nsDocShell.cpp index e4602e5f606..2690725e507 100644 --- a/mozilla/docshell/base/nsDocShell.cpp +++ b/mozilla/docshell/base/nsDocShell.cpp @@ -52,6 +52,8 @@ #include "nsIDOMDocument.h" #include "nsIDOMNSDocument.h" #include "nsIDOMElement.h" +#include "nsIDOMStorage.h" +#include "nsPIDOMStorage.h" #include "nsIDocumentViewer.h" #include "nsIDocumentLoaderFactory.h" #include "nsCURILoader.h" @@ -340,6 +342,9 @@ nsDocShell::Init() rv = mContentListener->Init(); NS_ENSURE_SUCCESS(rv, rv); + if (!mStorages.Init()) + return NS_ERROR_OUT_OF_MEMORY; + // We want to hold a strong ref to the loadgroup, so it better hold a weak // ref to us... use an InterfaceRequestorProxy to do this. nsCOMPtr proxy = @@ -1659,6 +1664,78 @@ nsDocShell::HistoryPurged(PRInt32 aNumEntries) return NS_OK; } +NS_IMETHODIMP +nsDocShell::GetSessionStorageForDomain(const nsACString& aDomain, + nsIDOMStorage** aStorage) +{ + NS_ENSURE_ARG_POINTER(aStorage); + + *aStorage = nsnull; + + if (aDomain.IsEmpty()) + return NS_OK; + + nsCOMPtr topItem; + nsresult rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem)); + if (NS_FAILED(rv)) + return rv; + + if (!topItem) + return NS_ERROR_FAILURE; + + nsCOMPtr topDocShell = do_QueryInterface(topItem); + if (topDocShell != this) + return topDocShell->GetSessionStorageForDomain(aDomain, aStorage); + + if (!mStorages.Get(aDomain, aStorage)) { + nsCOMPtr newstorage = + do_CreateInstance("@mozilla.org/dom/storage;1"); + if (!newstorage) + return NS_ERROR_OUT_OF_MEMORY; + + nsCOMPtr pistorage = do_QueryInterface(newstorage); + if (!pistorage) + return NS_ERROR_FAILURE; + pistorage->Init(NS_ConvertUTF8toUTF16(aDomain), PR_FALSE); + + if (!mStorages.Put(aDomain, newstorage)) + return NS_ERROR_OUT_OF_MEMORY; + + *aStorage = newstorage; + NS_ADDREF(*aStorage); + } + + return NS_OK; +} + +nsresult +nsDocShell::AddSessionStorage(const nsACString& aDomain, + nsIDOMStorage* aStorage) +{ + NS_ENSURE_ARG_POINTER(aStorage); + + if (aDomain.IsEmpty()) + return NS_OK; + + nsCOMPtr topItem; + nsresult rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem)); + if (NS_FAILED(rv)) + return rv; + + if (topItem) { + nsCOMPtr topDocShell = do_QueryInterface(topItem); + if (topDocShell == this) { + if (!mStorages.Put(aDomain, aStorage)) + return NS_ERROR_OUT_OF_MEMORY; + } + else { + return topDocShell->AddSessionStorage(aDomain, aStorage); + } + } + + return NS_OK; +} + //***************************************************************************** // nsDocShell::nsIDocShellTreeItem //***************************************************************************** @@ -6278,6 +6355,40 @@ nsDocShell::InternalLoad(nsIURI * aURI, nsCOMPtr webNav = do_GetInterface(newWin); targetDocShell = do_QueryInterface(webNav); + + nsCOMPtr sop = + do_QueryInterface(mScriptGlobal); + nsCOMPtr currentCodebase; + + if (sop) { + nsIPrincipal *principal = sop->GetPrincipal(); + + if (principal) { + principal->GetURI(getter_AddRefs(currentCodebase)); + } + } + + // We opened a new window for the target, clone the + // session storage if the current URI's domain matches + // that of the loading URI. + if (targetDocShell && currentCodebase && aURI) { + nsCAutoString thisDomain, newDomain; + nsresult gethostrv = currentCodebase->GetAsciiHost(thisDomain); + gethostrv |= aURI->GetAsciiHost(newDomain); + if (NS_SUCCEEDED(gethostrv) && thisDomain.Equals(newDomain)) { + nsCOMPtr storage; + GetSessionStorageForDomain(thisDomain, + getter_AddRefs(storage)); + nsCOMPtr piStorage = + do_QueryInterface(storage); + if (piStorage) { + nsCOMPtr newstorage = + piStorage->Clone(); + targetDocShell->AddSessionStorage(thisDomain, + newstorage); + } + } + } } // diff --git a/mozilla/docshell/base/nsDocShell.h b/mozilla/docshell/base/nsDocShell.h index d6bc1e6a5a8..6c99623c10e 100644 --- a/mozilla/docshell/base/nsDocShell.h +++ b/mozilla/docshell/base/nsDocShell.h @@ -49,6 +49,7 @@ #include "nsIContentViewer.h" #include "nsIPrefBranch.h" #include "nsVoidArray.h" +#include "nsInterfaceHashtable.h" #include "nsIScriptContext.h" #include "nsITimer.h" @@ -562,6 +563,9 @@ protected: // the event whenever necessary. nsRevocableEventPtr mRestorePresentationEvent; + // hash of session storages, keyed by domain + nsInterfaceHashtable mStorages; + // Index into the SHTransaction list, indicating the previous and current // transaction at the time that this DocShell begins to load PRInt32 mPreviousTransIndex; diff --git a/mozilla/docshell/base/nsIDocShell.idl b/mozilla/docshell/base/nsIDocShell.idl index d6a6e08cc0f..f417e1eec1a 100644 --- a/mozilla/docshell/base/nsIDocShell.idl +++ b/mozilla/docshell/base/nsIDocShell.idl @@ -65,8 +65,9 @@ interface nsIRequest; interface nsISHEntry; interface nsILayoutHistoryState; interface nsISecureBrowserUI; +interface nsIDOMStorage; -[scriptable, uuid(9f0c7461-b9a4-47f6-b88c-421dce1bce66)] +[scriptable, uuid(616b8670-d170-11da-a94d-0800200c9a66)] interface nsIDocShell : nsISupports { /** @@ -404,5 +405,21 @@ interface nsIDocShell : nsISupports * @param numEntries - The number of entries removed */ void historyPurged(in long numEntries); + + /* + * Retrieves the WebApps session storage object for the supplied domain. + * If it doesn't already exist, a new one will be created. + * + * @param domain the domain of the storage object to retrieve + */ + nsIDOMStorage getSessionStorageForDomain(in ACString aDomain); + + /* + * Add a WebApps session storage object to the docshell. + * + * @param domain the domain the storage object is associated with + * @param storage the storage object to add + */ + void addSessionStorage(in ACString aDomain, in nsIDOMStorage storage); }; diff --git a/mozilla/dom/public/idl/Makefile.in b/mozilla/dom/public/idl/Makefile.in index dc0a4ee3abf..6e43850ee6e 100644 --- a/mozilla/dom/public/idl/Makefile.in +++ b/mozilla/dom/public/idl/Makefile.in @@ -1,4 +1,3 @@ -# # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # @@ -57,7 +56,8 @@ DIRS = \ xbl \ xpath \ ls \ - xul + xul \ + storage ifdef MOZ_SVG DIRS += svg diff --git a/mozilla/dom/public/idl/storage/Makefile.in b/mozilla/dom/public/idl/storage/Makefile.in new file mode 100644 index 00000000000..c46f21d6edb --- /dev/null +++ b/mozilla/dom/public/idl/storage/Makefile.in @@ -0,0 +1,65 @@ +# +# ***** 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 +# Neil Deakin +# Portions created by the Initial Developer are Copyright (C) 2006 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# 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@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = dom +XPIDL_MODULE = dom_storage +GRE_MODULE = 1 + +EXPORTS = \ + nsPIDOMStorage.h \ + $(NULL) + +XPIDLSRCS = \ + nsIDOMToString.idl \ + $(NULL) + +SDK_XPIDLSRCS = \ + nsIDOMStorage.idl \ + nsIDOMStorageEvent.idl \ + nsIDOMStorageItem.idl \ + nsIDOMStorageList.idl \ + nsIDOMStorageWindow.idl \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/mozilla/dom/public/idl/storage/nsIDOMStorage.idl b/mozilla/dom/public/idl/storage/nsIDOMStorage.idl new file mode 100644 index 00000000000..6f26113a5da --- /dev/null +++ b/mozilla/dom/public/idl/storage/nsIDOMStorage.idl @@ -0,0 +1,98 @@ +/* -*- 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 + * Neil Deakin + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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/#scs-client-side + * for more information. + * + * A storage object stores an arbitrary set of key-value pairs, which + * may be retrieved, modified and removed as needed. A key may only + * exist once within a storage object, and only one value may be + * associated with a particular key. Keys are stored in a particular + * order with the condition that this order not change by merely changing + * the value associated with a key, but the order may change when a + * key is added or removed. + */ + +interface nsIDOMStorageItem; + +[scriptable, uuid(95CC1383-3B62-4B89-AAEF-1004A513EF47)] +interface nsIDOMStorage : nsISupports +{ + /** + * The number of keys stored. + */ + readonly attribute unsigned long length; + + /** + * Retrieve the name of the key at a particular index. + * + * @param index index of the item to retrieve + * @returns the key at index + * @throws INDEX_SIZE_ERR if there is no key at that index + */ + DOMString key(in unsigned long index); + + /** + * Retrieve an item with a given key + * + * @param key key to retrieve + * @returns found item or null if the key was not found + */ + nsIDOMStorageItem getItem(in DOMString key); + + /** + * Assign a value with a key. If the key does not exist already, a new + * key is added associated with that value. If the key already exists, + * then the existing value is replaced with a new value. + * + * @param key key to set + * @param data data to associate with the key + * @returns found item or null if the key was not found + */ + 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); +}; diff --git a/mozilla/dom/public/idl/storage/nsIDOMStorageEvent.idl b/mozilla/dom/public/idl/storage/nsIDOMStorageEvent.idl new file mode 100644 index 00000000000..fd528716c26 --- /dev/null +++ b/mozilla/dom/public/idl/storage/nsIDOMStorageEvent.idl @@ -0,0 +1,74 @@ +/* -*- 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 + * Neil Deakin + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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" +#include "nsIDOMEvent.idl" + +/** + * Interface for a client side storage. See + * http://www.whatwg.org/specs/web-apps/current-work/#scs-client-side + * for more information. + * + * Event sent to a window when a storage area changes. + */ + +[scriptable, uuid(FC540C28-8EDD-4B7A-9C30-8638289B7A7D)] +interface nsIDOMStorageEvent : nsIDOMEvent +{ + /** + * Domain of the storage area which changed, or #session for + * session storage. + */ + readonly attribute DOMString domain; + + /** + * Initialize a storage event. + */ + void initStorageEvent(in DOMString typeArg, + in boolean canBubbleArg, + in boolean cancelableArg, + in DOMString domainArg); + + /** + * Initialize a storage event. + */ + void initStorageEventNS(in DOMString namespaceURIArg, + in DOMString typeArg, + in boolean canBubbleArg, + in boolean cancelableArg, + in DOMString domainArg); +}; diff --git a/mozilla/dom/public/idl/storage/nsIDOMStorageItem.idl b/mozilla/dom/public/idl/storage/nsIDOMStorageItem.idl new file mode 100644 index 00000000000..1d09698b607 --- /dev/null +++ b/mozilla/dom/public/idl/storage/nsIDOMStorageItem.idl @@ -0,0 +1,60 @@ +/* -*- 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 + * Neil Deakin + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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 a client side storage item. See + * http://www.whatwg.org/specs/web-apps/current-work/#scs-client-side + * for more information. + * + * A respresentation of a storage object item. + */ + +[scriptable, uuid(0CC37C78-4C5F-48E1-ADFC-7480B8FE9DC4)] +interface nsIDOMStorageItem : nsISupports +{ + /** + * Indicates whether a key is available only in a secure context. + */ + attribute boolean secure; + + /** + * The value associated with the item. + */ + attribute DOMString value; +}; diff --git a/mozilla/dom/public/idl/storage/nsIDOMStorageList.idl b/mozilla/dom/public/idl/storage/nsIDOMStorageList.idl new file mode 100644 index 00000000000..e7e97418db1 --- /dev/null +++ b/mozilla/dom/public/idl/storage/nsIDOMStorageList.idl @@ -0,0 +1,60 @@ +/* -*- 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 + * Neil Deakin + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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 a client side storage. See + * http://www.whatwg.org/specs/web-apps/current-work/#scs-client-side + * for more information. + * + * Allows access to contextual storage areas by domain. + */ + +interface nsIDOMStorage; + +[scriptable, uuid(f2166929-91B6-4372-8D5F-C366F47A5F54)] +interface nsIDOMStorageList : nsISupports +{ + /** + * Returns a storage object for a particular domain. + * + * @param domain domain to retrieve + * @returns a storage area for the given domain + */ + nsIDOMStorage namedItem(in DOMString domain); +}; diff --git a/mozilla/dom/public/idl/storage/nsIDOMStorageWindow.idl b/mozilla/dom/public/idl/storage/nsIDOMStorageWindow.idl new file mode 100644 index 00000000000..130835efe24 --- /dev/null +++ b/mozilla/dom/public/idl/storage/nsIDOMStorageWindow.idl @@ -0,0 +1,63 @@ +/* -*- 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 + * Neil Deakin + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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 a client side storage. See + * http://www.whatwg.org/specs/web-apps/current-work/#scs-client-side + * for more information. + * + * Allows access to contextual storage areas. + */ + +interface nsIDOMStorage; +interface nsIDOMStorageList; + +[scriptable, uuid(55E9C181-2476-47CF-97F8-EFDAAF7B6F7A)] +interface nsIDOMStorageWindow : nsISupports +{ + /** + * Session storage for the current browsing context. + */ + readonly attribute nsIDOMStorage sessionStorage; + + /** + * Global storage, accessible by domain. + */ + readonly attribute nsIDOMStorageList globalStorage; +}; diff --git a/mozilla/dom/public/idl/storage/nsIDOMToString.idl b/mozilla/dom/public/idl/storage/nsIDOMToString.idl new file mode 100644 index 00000000000..c3e16a6c0f2 --- /dev/null +++ b/mozilla/dom/public/idl/storage/nsIDOMToString.idl @@ -0,0 +1,56 @@ +/* -*- 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.com code. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Johnny Stenback (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either 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" + +/** + * The sole purpose of this interface is to make it easy for XPCOM + * object's to hook into JS' toString() when interacting with + * XPConnect. If you implement this interface and advertize it in the + * object's classinfo, you'll get automatic mapping from JS' + * toString() to the toString() method in this interface. + * + * XXXjst: This doesn't really belong in dom/public/idl/storage, but + * it gets to live here until I find a more suitable place. + */ + +[scriptable, uuid(2a72e20f-e337-4822-8994-2e35b5550d03)] +interface nsIDOMToString : nsISupports +{ + DOMString toString(); +}; diff --git a/mozilla/dom/public/idl/storage/nsPIDOMStorage.h b/mozilla/dom/public/idl/storage/nsPIDOMStorage.h new file mode 100644 index 00000000000..87945bfa2a7 --- /dev/null +++ b/mozilla/dom/public/idl/storage/nsPIDOMStorage.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=2 et tw=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.com code. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Johnny Stenback (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either 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 ***** */ + +#ifndef __nsPIDOMStorage_h_ +#define __nsPIDOMStorage_h_ + +#include "nsISupports.h" +#include "nsTArray.h" + +class nsIDOMStorage; + +#define NS_PIDOMSTORAGE_IID \ + { 0x2fdbb82e, 0x4b47, 0x406a, \ + { 0xb1, 0x17, 0x6d, 0x67, 0x58, 0xc1, 0xee, 0x6b } } + +class nsPIDOMStorage : public nsISupports +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMSTORAGE_IID) + + virtual void Init(const nsAString &aDomain, PRBool aUseDB) = 0; + + virtual already_AddRefed Clone() = 0; + + virtual nsTArray *GetKeys() = 0; +}; + +#endif // __nsPIDOMStorage_h_ diff --git a/mozilla/dom/public/nsDOMClassInfoID.h b/mozilla/dom/public/nsDOMClassInfoID.h index 74d9ea37d07..749f470e62e 100644 --- a/mozilla/dom/public/nsDOMClassInfoID.h +++ b/mozilla/dom/public/nsDOMClassInfoID.h @@ -365,6 +365,12 @@ enum nsDOMClassInfoID { eDOMClassInfo_XPathNSResolver_id, eDOMClassInfo_XPathResult_id, + // WhatWG WebApps Objects + eDOMClassInfo_Storage_id, + eDOMClassInfo_StorageList_id, + eDOMClassInfo_StorageItem_id, + eDOMClassInfo_StorageEvent_id, + eDOMClassInfo_WindowRoot_id, // DOMParser, XMLSerializer diff --git a/mozilla/dom/src/Makefile.in b/mozilla/dom/src/Makefile.in index e3d1cefa6c9..a0ac623e1f3 100644 --- a/mozilla/dom/src/Makefile.in +++ b/mozilla/dom/src/Makefile.in @@ -42,7 +42,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -DIRS = base jsurl events +DIRS = base jsurl events storage include $(topsrcdir)/config/rules.mk diff --git a/mozilla/dom/src/base/Makefile.in b/mozilla/dom/src/base/Makefile.in index 2ab48a3f593..ca721ea7e3a 100644 --- a/mozilla/dom/src/base/Makefile.in +++ b/mozilla/dom/src/base/Makefile.in @@ -80,6 +80,7 @@ REQUIRES = xpcom \ rdf \ xultmpl \ jar \ + storage \ $(NULL) ifdef NS_TRACE_MALLOC @@ -107,12 +108,15 @@ CPPSRCS = \ nsDOMScriptObjectFactory.cpp \ $(NULL) -# we don't want the shared lib, but we want to force the creation of a static lib. +# we don't want the shared lib, but we want to force the creation of a +# static lib. FORCE_STATIC_LIB = 1 LOCAL_INCLUDES = \ -I$(srcdir)/../events \ + -I$(srcdir)/../storage \ -I$(topsrcdir)/content/xbl/src \ + -I$(topsrcdir)/content/events/src \ $(NULL) DEFINES += -D_IMPL_NS_LAYOUT diff --git a/mozilla/dom/src/base/nsDOMClassInfo.cpp b/mozilla/dom/src/base/nsDOMClassInfo.cpp index 17887d9c3ff..9994e9d3871 100644 --- a/mozilla/dom/src/base/nsDOMClassInfo.cpp +++ b/mozilla/dom/src/base/nsDOMClassInfo.cpp @@ -56,6 +56,7 @@ #include "nsUnicharUtils.h" #include "xptcall.h" #include "prprf.h" +#include "nsTArray.h" // JavaScript includes #include "jsapi.h" @@ -412,6 +413,14 @@ #include "nsIImageDocument.h" +// Storage includes +#include "nsIDOMStorage.h" +#include "nsPIDOMStorage.h" +#include "nsIDOMStorageList.h" +#include "nsIDOMStorageItem.h" +#include "nsIDOMStorageEvent.h" +#include "nsIDOMToString.h" + static NS_DEFINE_CID(kCPluginManagerCID, NS_PLUGINMANAGER_CID); static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID); @@ -1104,6 +1113,27 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(XPathResult, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) + // WhatWG Storage + + // mrbkap says we don't need WANT_ADDPROPERTY on Storage objects + // since a call to addProperty() is always followed by a call to + // setProperty(), except in the case when a getter or setter is set + // for a property. But we don't care about getters or setters here. + NS_DEFINE_CLASSINFO_DATA(Storage, nsStorageSH, + 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, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(StorageEvent, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) + // We just want this to have classinfo so it gets mark callbacks for marking // event listeners. // We really don't want any of the default flags! @@ -1760,6 +1790,7 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget) DOM_CLASSINFO_MAP_ENTRY(nsIDOMViewCSS) DOM_CLASSINFO_MAP_ENTRY(nsIDOMAbstractView) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageWindow) DOM_CLASSINFO_MAP_END DOM_CLASSINFO_MAP_BEGIN(WindowUtils, nsIDOMWindowUtils) @@ -2445,6 +2476,8 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindowInternal) DOM_CLASSINFO_MAP_ENTRY(nsIDOMChromeWindow) DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget) + // XXXjst: Do we want this on chrome windows? + // DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageWindow) DOM_CLASSINFO_MAP_ENTRY(nsIDOMViewCSS) DOM_CLASSINFO_MAP_ENTRY(nsIDOMAbstractView) DOM_CLASSINFO_MAP_END @@ -3024,6 +3057,23 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_ENTRY(nsIDOMXPathResult) DOM_CLASSINFO_MAP_END + DOM_CLASSINFO_MAP_BEGIN(Storage, nsIDOMStorage) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorage) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN(StorageList, nsIDOMStorageList) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageList) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN(StorageItem, nsIDOMStorageItem) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageItem) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMToString) + DOM_CLASSINFO_MAP_END + + DOM_CLASSINFO_MAP_BEGIN(StorageEvent, nsIDOMStorageEvent) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageEvent) + DOM_CLASSINFO_MAP_END + // We just want this to have classinfo so it gets mark callbacks for marking // event listeners. DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(WindowRoot, nsISupports) @@ -3350,7 +3400,6 @@ nsDOMClassInfo::PostCreate(nsIXPConnectWrappedNative *wrapper, if (if_info) { nsXPIDLCString name; if_info->GetName(getter_Copies(name)); - NS_ASSERTION(nsCRT::strcmp(CutPrefix(name), mData->mName) == 0, "Class name and proto chain interface name mismatch!"); } @@ -6151,8 +6200,8 @@ nsWindowSH::NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, switch (enum_op) { case JSENUMERATE_INIT: { - // First, do the security check that nsDOMClassInfo does to see if we need to - // do any work at all. + // First, do the security check that nsDOMClassInfo does to see + // if we need to do any work at all. nsDOMClassInfo::Enumerate(wrapper, cx, obj, _retval); if (!*_retval) { return NS_OK; @@ -9590,6 +9639,202 @@ nsTreeColumnsSH::GetNamedItem(nsISupports *aNative, #endif +// Storage scriptable helper + +// 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 +nsStorageSH::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 storage(do_QueryWrappedNative(wrapper)); + + // GetItem() will return null if the caller can't access the session + // storage item. + nsCOMPtr item; + nsresult rv = storage->GetItem(nsDependentJSString(jsstr), + getter_AddRefs(item)); + NS_ENSURE_SUCCESS(rv, rv); + + if (item) { + 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; +} + +nsresult +nsStorageSH::GetNamedItem(nsISupports *aNative, const nsAString& aName, + nsISupports **aResult) +{ + nsCOMPtr storage(do_QueryInterface(aNative)); + NS_ENSURE_TRUE(storage, NS_ERROR_UNEXPECTED); + + // Weak, transfer the ownership over to aResult + nsIDOMStorageItem* item = nsnull; + nsresult rv = storage->GetItem(aName, &item); + + *aResult = item; + + return rv; +} + +NS_IMETHODIMP +nsStorageSH::SetProperty(nsIXPConnectWrappedNative *wrapper, + JSContext *cx, JSObject *obj, jsval id, + jsval *vp, PRBool *_retval) +{ + nsCOMPtr 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 +nsStorageSH::DelProperty(nsIXPConnectWrappedNative *wrapper, + JSContext *cx, JSObject *obj, jsval id, + jsval *vp, PRBool *_retval) +{ + nsCOMPtr 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 +nsStorageSH::NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj, PRUint32 enum_op, jsval *statep, + jsid *idp, PRBool *_retval) +{ + nsTArray *keys = + (nsTArray *)JSVAL_TO_PRIVATE(*statep); + + switch (enum_op) { + case JSENUMERATE_INIT: + { + nsCOMPtr 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, NS_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 + +nsresult +nsStorageListSH::GetNamedItem(nsISupports *aNative, const nsAString& aName, + nsISupports **aResult) +{ + nsCOMPtr storagelist(do_QueryInterface(aNative)); + NS_ENSURE_TRUE(storagelist, NS_ERROR_UNEXPECTED); + + // Weak, transfer the ownership over to aResult + nsIDOMStorage* storage = nsnull; + nsresult rv = storagelist->NamedItem(aName, &storage); + + *aResult = storage; + + return rv; +} + + // nsIDOMEventListener::HandleEvent() 'this' converter helper NS_INTERFACE_MAP_BEGIN(nsEventListenerThisTranslator) diff --git a/mozilla/dom/src/base/nsDOMClassInfo.h b/mozilla/dom/src/base/nsDOMClassInfo.h index dd55bb9a8f2..7faf0b29e3f 100644 --- a/mozilla/dom/src/base/nsDOMClassInfo.h +++ b/mozilla/dom/src/base/nsDOMClassInfo.h @@ -480,7 +480,7 @@ public: JSObject **objp, PRBool *_retval); NS_IMETHOD NewEnumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, PRUint32 enum_op, jsval *statep, - jsid *id, PRBool *_retval); + jsid *idp, PRBool *_retval); NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj); NS_IMETHOD Equality(nsIXPConnectWrappedNative *wrapper, JSContext * cx, @@ -1475,6 +1475,63 @@ public: }; #endif +// WebApps Storage helpers + +class nsStorageSH : public nsNamedArraySH +{ +protected: + nsStorageSH(nsDOMClassInfoData* aData) : nsNamedArraySH(aData) + { + } + + virtual ~nsStorageSH() + { + } + + 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 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); + + // Override nsNamedArraySH::GetNamedItem() + virtual nsresult GetNamedItem(nsISupports *aNative, const nsAString& aName, + nsISupports **aResult); + +public: + static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) + { + return new nsStorageSH(aData); + } +}; + +class nsStorageListSH : public nsNamedArraySH +{ +protected: + nsStorageListSH(nsDOMClassInfoData* aData) : nsNamedArraySH(aData) + { + } + + virtual ~nsStorageListSH() + { + } + + // Override nsNamedArraySH::GetNamedItem() + virtual nsresult GetNamedItem(nsISupports *aNative, const nsAString& aName, + nsISupports **aResult); + +public: + static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) + { + return new nsStorageListSH(aData); + } +}; + // Event handler 'this' translator class, this is called by XPConnect // when a "function interface" (nsIDOMEventListener) is called, this diff --git a/mozilla/dom/src/base/nsGlobalWindow.cpp b/mozilla/dom/src/base/nsGlobalWindow.cpp index db234da0006..851ca4b7009 100644 --- a/mozilla/dom/src/base/nsGlobalWindow.cpp +++ b/mozilla/dom/src/base/nsGlobalWindow.cpp @@ -47,6 +47,7 @@ #include "nsScreen.h" #include "nsHistory.h" #include "nsBarProps.h" +#include "nsDOMStorage.h" // Helper Classes #include "nsXPIDLString.h" @@ -82,6 +83,8 @@ #include "nsIDocCharset.h" #include "nsIDocument.h" #include "nsIHTMLDocument.h" +#include "nsIDOMHTMLDocument.h" +#include "nsIDOMHTMLElement.h" #include "nsIDOMCrypto.h" #include "nsIDOMDocument.h" #include "nsIDOMNSDocument.h" @@ -336,7 +339,8 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) mTimeoutInsertionPoint(&mTimeouts), mTimeoutPublicIdCounter(1), mTimeoutFiringDepth(0), - mJSObject(nsnull) + mJSObject(nsnull), + mPendingStorageEvents(nsnull) #ifdef DEBUG , mSetOpenerWindowCalled(PR_FALSE) #endif @@ -357,7 +361,12 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) if (os) { // Watch for online/offline status changes so we can fire events. Use // a strong reference. - os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, PR_FALSE); + os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, + PR_FALSE); + + // Watch for dom-storage-chaged so we can fire storage + // events. Use a strong reference. + os->AddObserver(mObserver, "dom-storage-changed", PR_FALSE); } } } else { @@ -404,7 +413,9 @@ nsGlobalWindow::~nsGlobalWindow() do_GetService("@mozilla.org/observer-service;1"); if (os) { os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC); + os->RemoveObserver(mObserver, "dom-storage-changed"); } + // Drop its reference to this dying window, in case for some bogus reason // the object stays around. mObserver->Forget(); @@ -450,6 +461,8 @@ nsGlobalWindow::~nsGlobalWindow() NS_ASSERTION(!mArguments, "mArguments wasn't cleaned up properly!"); CleanUp(); + + delete mPendingStorageEvents; } // static @@ -573,6 +586,7 @@ NS_INTERFACE_MAP_BEGIN(nsGlobalWindow) NS_INTERFACE_MAP_ENTRY(nsPIDOMWindow) NS_INTERFACE_MAP_ENTRY(nsIDOMViewCSS) NS_INTERFACE_MAP_ENTRY(nsIDOMAbstractView) + NS_INTERFACE_MAP_ENTRY(nsIDOMStorageWindow) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Window) @@ -5345,7 +5359,56 @@ nsGlobalWindow::GetDocument(nsIDOMDocumentView ** aDocumentView) return rv; } -///***************************************************************************** +//***************************************************************************** +// nsGlobalWindow::nsIDOMStorageWindow +//***************************************************************************** + +NS_IMETHODIMP +nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage) +{ + *aSessionStorage = nsnull; + + FORWARD_TO_OUTER(GetSessionStorage, (aSessionStorage), NS_OK); + + nsIPrincipal *principal = GetPrincipal(); + + if (!principal || !mDocShell) { + return NS_OK; + } + + nsCOMPtr codebase; + nsresult rv = principal->GetURI(getter_AddRefs(codebase)); + NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && codebase, rv); + + nsCAutoString currentDomain; + rv = codebase->GetAsciiHost(currentDomain); + NS_ENSURE_SUCCESS(rv, rv); + + return GetDocShell()->GetSessionStorageForDomain(currentDomain, + aSessionStorage); +} + +NS_IMETHODIMP +nsGlobalWindow::GetGlobalStorage(nsIDOMStorageList ** aGlobalStorage) +{ + NS_ENSURE_ARG_POINTER(aGlobalStorage); + +#ifdef MOZ_STORAGE + if (!gGlobalStorageList) { + nsresult rv = NS_NewDOMStorageList(getter_AddRefs(gGlobalStorageList)); + NS_ENSURE_SUCCESS(rv, rv); + } + + *aGlobalStorage = gGlobalStorageList; + NS_IF_ADDREF(*aGlobalStorage); + + return NS_OK; +#else + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; +#endif +} + +//***************************************************************************** // nsGlobalWindow::nsIInterfaceRequestor //***************************************************************************** @@ -5455,8 +5518,7 @@ nsGlobalWindow::FireOfflineStatusEvent() } nsresult -nsGlobalWindow::Observe(nsISupports* aSubject, - const char* aTopic, +nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData) { if (!nsCRT::strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) { @@ -5469,6 +5531,96 @@ nsGlobalWindow::Observe(nsISupports* aSubject, } return NS_OK; } + + if (IsInnerWindow() && !nsCRT::strcmp(aTopic, "dom-storage-changed")) { + nsIPrincipal *principal; + nsresult rv; + + if (!aData) { + nsCOMPtr storage; + GetSessionStorage(getter_AddRefs(storage)); + + if (storage != aSubject && !aData) { + // A sessionStorage object changed, but not our session storage + // object. + return NS_OK; + } + } else if ((principal = GetPrincipal())) { + // A global storage object changed, check to see if it's one + // this window can access. + + nsCOMPtr codebase; + principal->GetURI(getter_AddRefs(codebase)); + + if (!codebase) { + return NS_OK; + } + + nsCAutoString currentDomain; + rv = codebase->GetAsciiHost(currentDomain); + if (NS_FAILED(rv)) { + return NS_OK; + } + + if (!nsDOMStorageList::CanAccessDomain(nsDependentString(aData), + NS_ConvertASCIItoUTF16(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. + + return NS_OK; + } + } + + if (mIsFrozen) { + // This window is frozen, rather than firing the events here, + // store the domain in which the change happened and fire the + // events if we're ever thawed. + + if (!mPendingStorageEvents) { + mPendingStorageEvents = new nsDataHashtable; + NS_ENSURE_TRUE(mPendingStorageEvents, NS_ERROR_OUT_OF_MEMORY); + + rv = mPendingStorageEvents->Init(); + NS_ENSURE_SUCCESS(rv, rv); + } + + mPendingStorageEvents->Put(Substring(aData, + aData + nsCRT::strlen(aData)), + PR_TRUE); + + return NS_OK; + } + + nsAutoString domain(aData); + + nsRefPtr event = new nsDOMStorageEvent(domain); + NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY); + + rv = event->Init(); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr htmlDoc(do_QueryInterface(mDocument)); + + nsCOMPtr target; + + if (htmlDoc) { + nsCOMPtr body; + htmlDoc->GetBody(getter_AddRefs(body)); + + target = do_QueryInterface(body); + } + + if (!target) { + target = this; + } + + PRBool defaultActionEnabled; + target->DispatchEvent((nsIDOMStorageEvent *)event, &defaultActionEnabled); + + return NS_OK; + } + NS_WARNING("unrecognized topic in nsGlobalWindow::Observe"); return NS_ERROR_FAILURE; } @@ -5544,7 +5696,16 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName, const PRBool checkForPopup = !aDialog && !WindowExists(aName, !aCalledNoScript); - + + // Grab the current codebase before we do any opening as that could + // change the current codebase. + nsIPrincipal *currentPrincipal = GetPrincipal(); + nsCOMPtr currentCodebase; + + if (currentPrincipal) { + currentPrincipal->GetURI(getter_AddRefs(currentCodebase)); + } + // These next two variables are only accessed when checkForPopup is true PopupControlState abuseLevel; OpenAllowValue allowReason; @@ -5654,8 +5815,6 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName, domReturn.swap(*aReturn); } - - if (NS_SUCCEEDED(rv)) { if (aDoJSFixups) { nsCOMPtr chrome_win(do_QueryInterface(*aReturn)); @@ -5693,6 +5852,38 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName, FireAbuseEvents(PR_FALSE, PR_TRUE, aUrl, aName, aOptions); } } + + // copy the session storage data over to the new window if + // necessary. If the new window has the same domain as this window + // did at the beginning of this function, the session storage data + // for that domain, and only that domain, is copied over. + nsGlobalWindow *opened = NS_STATIC_CAST(nsGlobalWindow *, *aReturn); + nsIDocShell* newDocShell = opened->GetDocShell(); + + if (currentCodebase && newDocShell && mDocShell && url.get()) { + nsCOMPtr newURI; + + JSContext *cx; + PRBool freePass; + BuildURIfromBase(url, getter_AddRefs(newURI), &freePass, &cx); + + if (newURI) { + nsCAutoString thisDomain, newDomain; + nsresult gethostrv = currentCodebase->GetAsciiHost(thisDomain); + gethostrv |= newURI->GetAsciiHost(newDomain); + + if (NS_SUCCEEDED(gethostrv) && thisDomain.Equals(newDomain)) { + nsCOMPtr storage; + mDocShell->GetSessionStorageForDomain(thisDomain, + getter_AddRefs(storage)); + nsCOMPtr piStorage = do_QueryInterface(storage); + if (piStorage) { + nsCOMPtr newstorage = piStorage->Clone(); + newDocShell->AddSessionStorage(thisDomain, newstorage); + } + } + } + } return rv; } @@ -6757,6 +6948,22 @@ nsGlobalWindow::SaveWindowState(nsISupports **aState) return NS_OK; } +PR_STATIC_CALLBACK(PLDHashOperator) +FirePendingStorageEvents(const nsAString& aKey, PRBool aData, void *userArg) +{ + nsGlobalWindow *win = NS_STATIC_CAST(nsGlobalWindow *, userArg); + + nsCOMPtr storage; + win->GetSessionStorage(getter_AddRefs(storage)); + + if (storage) { + win->Observe(storage, "dom-storage-changed", + aKey.IsEmpty() ? nsnull : PromiseFlatString(aKey).get()); + } + + return PL_DHASH_NEXT; +} + nsresult nsGlobalWindow::RestoreWindowState(nsISupports *aState) { @@ -6820,6 +7027,15 @@ nsGlobalWindow::RestoreWindowState(nsISupports *aState) holder->DidRestoreWindow(); + if (inner->mPendingStorageEvents) { + // Fire pending storage events. + inner->mPendingStorageEvents->EnumerateRead(FirePendingStorageEvents, + inner); + + delete inner->mPendingStorageEvents; + inner->mPendingStorageEvents = nsnull; + } + return NS_OK; } diff --git a/mozilla/dom/src/base/nsGlobalWindow.h b/mozilla/dom/src/base/nsGlobalWindow.h index 3782d66d288..5a137fe7063 100644 --- a/mozilla/dom/src/base/nsGlobalWindow.h +++ b/mozilla/dom/src/base/nsGlobalWindow.h @@ -49,6 +49,7 @@ #include "nsAutoPtr.h" #include "nsWeakReference.h" #include "nsHashtable.h" +#include "nsDataHashtable.h" // Interfaces Needed #include "nsDOMWindowList.h" @@ -56,7 +57,6 @@ #include "nsIBrowserDOMWindow.h" #include "nsIChromeEventHandler.h" #include "nsIControllers.h" -#include "nsIObserver.h" #include "nsIDocShellTreeOwner.h" #include "nsIDocShellTreeItem.h" #include "nsIDOMViewCSS.h" @@ -90,6 +90,9 @@ #include "mozFlushType.h" #include "prclist.h" #include "nsIDOMGCParticipant.h" +#include "nsIDOMStorage.h" +#include "nsIDOMStorageList.h" +#include "nsIDOMStorageWindow.h" #define DEFAULT_HOME_PAGE "www.mozilla.org" #define PREF_BROWSER_STARTUP_HOMEPAGE "browser.startup.homepage" @@ -147,6 +150,7 @@ class nsGlobalWindow : public nsPIDOMWindow, public nsIDOM3EventTarget, public nsIDOMNSEventTarget, public nsIDOMViewCSS, + public nsIDOMStorageWindow, public nsSupportsWeakReference, public nsIInterfaceRequestor, public PRCListStr @@ -250,6 +254,9 @@ public: // nsIDOMAbstractView NS_DECL_NSIDOMABSTRACTVIEW + // nsIDOMStorageWindow + NS_DECL_NSIDOMSTORAGEWINDOW + // nsIInterfaceRequestor NS_DECL_NSIINTERFACEREQUESTOR @@ -290,8 +297,9 @@ public: { return mIsFrozen; } - - nsresult Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData); + + nsresult Observe(nsISupports* aSubject, const char* aTopic, + const PRUnichar* aData); static void ShutDown(); static PRBool IsCallerChrome(); @@ -513,6 +521,8 @@ protected: nsCOMPtr mCrypto; nsCOMPtr mPkcs11; + nsCOMPtr gGlobalStorageList; + nsCOMPtr mInnerWindowHolder; nsCOMPtr mOpenerScriptPrincipal; // strong; used to determine // whether to clear scope @@ -523,12 +533,15 @@ protected: nsTimeout** mTimeoutInsertionPoint; PRUint32 mTimeoutPublicIdCounter; PRUint32 mTimeoutFiringDepth; + nsCOMPtr mSessionStorage; // These member variables are used on both inner and the outer windows. nsCOMPtr mDocumentPrincipal; nsCOMPtr mDoc; // For fast access to principals JSObject* mJSObject; + nsDataHashtable *mPendingStorageEvents; + #ifdef DEBUG PRBool mSetOpenerWindowCalled; #endif diff --git a/mozilla/dom/src/storage/Makefile.in b/mozilla/dom/src/storage/Makefile.in new file mode 100644 index 00000000000..eca7de11bca --- /dev/null +++ b/mozilla/dom/src/storage/Makefile.in @@ -0,0 +1,85 @@ +# +# ***** 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 +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# 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@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = dom +LIBRARY_NAME = jsdomstorage_s +MOZILLA_INTERNAL_API = 1 + +REQUIRES = xpcom \ + string \ + content \ + caps \ + gfx \ + js \ + layout \ + locale \ + necko \ + pref \ + unicharutil \ + widget \ + xpconnect \ + $(NULL) + +ifdef MOZ_STORAGE +REQUIRES += storage +endif + +CPPSRCS = \ + nsDOMStorage.cpp \ + $(NULL) + +ifdef MOZ_STORAGE +CPPSRCS += nsDOMStorageDB.cpp +endif + +# we don't want the shared lib, but we want to force the creation of a static lib. +FORCE_STATIC_LIB = 1 + +LOCAL_INCLUDES = \ + -I$(srcdir)/../base \ + -I$(topsrcdir)/content/events/src + +DEFINES += -D_IMPL_NS_LAYOUT + +include $(topsrcdir)/config/rules.mk diff --git a/mozilla/dom/src/storage/nsDOMStorage.cpp b/mozilla/dom/src/storage/nsDOMStorage.cpp new file mode 100644 index 00000000000..bd7a738b0e1 --- /dev/null +++ b/mozilla/dom/src/storage/nsDOMStorage.cpp @@ -0,0 +1,910 @@ +/* -*- 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) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Neil Deakin + * Johnny Stenback + * + * 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 "nsCOMPtr.h" +#include "nsDOMError.h" +#include "nsDOMClassInfo.h" +#include "nsUnicharUtils.h" +#include "nsIDocument.h" +#include "nsDOMStorage.h" +#include "nsContentUtils.h" +#include "nsIScriptSecurityManager.h" +#include "nsIPrincipal.h" +#include "nsIURI.h" +#include "nsReadableUtils.h" +#include "nsIObserverService.h" + +// +// Helper that tells us whether the caller is secure or not. +// + +static PRBool +IsCallerSecure() +{ + nsCOMPtr callerDoc = + do_QueryInterface(nsContentUtils::GetDocumentFromCaller()); + + if (!callerDoc) { + return PR_FALSE; + } + + nsIURI *uri = callerDoc->GetDocumentURI(); + + if (!uri) { + return PR_FALSE; + } + + PRBool isHttps = PR_FALSE; + nsresult rv = uri->SchemeIs("https", &isHttps); + + return NS_SUCCEEDED(rv) && isHttps; +} + +// +// nsDOMStorage +// + +#ifdef MOZ_STORAGE +nsDOMStorageDB* nsDOMStorage::gStorageDB = nsnull; +#endif + +NS_INTERFACE_MAP_BEGIN(nsDOMStorage) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorage) + NS_INTERFACE_MAP_ENTRY(nsIDOMStorage) + NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(nsDOMStorage) +NS_IMPL_RELEASE(nsDOMStorage) + +nsresult +NS_NewDOMStorage(nsISupports* aOuter, REFNSIID aIID, void** aResult) +{ + nsDOMStorage* storage = new nsDOMStorage(); + if (!storage) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(storage); + *aResult = storage; + + return NS_OK; +} + +nsDOMStorage::nsDOMStorage() + : mUseDB(PR_FALSE), mItemsCached(PR_FALSE) +{ + mItems.Init(8); +} + +nsDOMStorage::nsDOMStorage(const nsAString& aDomain, PRBool aUseDB) + : mUseDB(aUseDB), mItemsCached(PR_FALSE), mDomain(aDomain) +{ +#ifndef MOZ_STORAGE + mUseDB = PR_FALSE; +#endif + + mItems.Init(8); +} + +nsDOMStorage::~nsDOMStorage() +{ +} + +void +nsDOMStorage::Init(const nsAString& aDomain, PRBool aUseDB) +{ + mDomain.Assign(aDomain); +#ifdef MOZ_STORAGE + mUseDB = aUseDB; +#else + mUseDB = PR_FALSE; +#endif +} + +class ItemCounterState +{ +public: + ItemCounterState(PRBool aIsCallerSecure) + : mIsCallerSecure(aIsCallerSecure), mCount(0) + { + } + + PRBool mIsCallerSecure; + PRBool mCount; +private: + ItemCounterState(); // Not to be implemented +}; + +PR_STATIC_CALLBACK(PLDHashOperator) +ItemCounter(nsSessionStorageEntry* aEntry, void* userArg) +{ + ItemCounterState *state = (ItemCounterState *)userArg; + + if (state->mIsCallerSecure || !aEntry->mItem->IsSecure()) { + ++state->mCount; + } + + return PL_DHASH_NEXT; +} + +NS_IMETHODIMP +nsDOMStorage::GetLength(PRUint32 *aLength) +{ + if (mUseDB) + CacheKeysFromDB(); + + ItemCounterState state(IsCallerSecure()); + + mItems.EnumerateEntries(ItemCounter, &state); + + *aLength = state.mCount; + + return NS_OK; +} + +class IndexFinderData +{ +public: + IndexFinderData(PRBool aIsCallerSecure, PRUint32 aWantedIndex) + : mIsCallerSecure(aIsCallerSecure), mIndex(0), mWantedIndex(aWantedIndex), + mItem(nsnull) + { + } + + PRBool mIsCallerSecure; + PRUint32 mIndex; + PRUint32 mWantedIndex; + nsSessionStorageEntry *mItem; + +private: + IndexFinderData(); // Not to be implemented +}; + +PR_STATIC_CALLBACK(PLDHashOperator) +IndexFinder(nsSessionStorageEntry* aEntry, void* userArg) +{ + IndexFinderData *data = (IndexFinderData *)userArg; + + if (data->mIndex == data->mWantedIndex && + (data->mIsCallerSecure || !aEntry->mItem->IsSecure())) { + data->mItem = aEntry; + + return PL_DHASH_STOP; + } + + ++data->mIndex; + + return PL_DHASH_NEXT; +} + +NS_IMETHODIMP +nsDOMStorage::Key(PRUint32 aIndex, nsAString& aKey) +{ + // XXXjst: This is as retarded as the DOM spec is, takes an unsigned + // int, but the spec talks about what to do if a negative value is + // passed in. + + // XXX: This does a linear search for the key at index, which would + // suck if there's a large numer of indexes. Do we care? If so, + // maybe we need to have a lazily populated key array here or + // something? + + if (mUseDB) + CacheKeysFromDB(); + + IndexFinderData data(IsCallerSecure(), aIndex); + mItems.EnumerateEntries(IndexFinder, &data); + + if (!data.mItem) { + // aIndex was larger than the number of accessible keys. Throw. + return NS_ERROR_DOM_INDEX_SIZE_ERR; + } + + aKey = data.mItem->GetKey(); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMStorage::GetItem(const nsAString& aKey, nsIDOMStorageItem **aItem) +{ + *aItem = nsnull; + + if (aKey.IsEmpty()) + return NS_OK; + + nsSessionStorageEntry *entry = mItems.GetEntry(aKey); + + if (entry) { + if (!IsCallerSecure() && entry->mItem->IsSecure()) { + return NS_OK; + } + NS_ADDREF(*aItem = entry->mItem); + } + else if (mUseDB) { + PRBool secure; + nsAutoString value; + 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 NS_OK; + NS_ENSURE_SUCCESS(rv, rv); + + nsRefPtr newitem = + new nsDOMStorageItem(this, aKey, secure); + if (!newitem) + return NS_ERROR_OUT_OF_MEMORY; + + entry = mItems.PutEntry(aKey); + NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY); + + entry->mItem = newitem; + NS_ADDREF(*aItem = newitem); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMStorage::SetItem(const nsAString& aKey, const nsAString& aData) +{ + if (aKey.IsEmpty()) + return NS_OK; + + nsresult rv; + nsRefPtr newitem = nsnull; + nsSessionStorageEntry *entry = mItems.GetEntry(aKey); + if (entry) { + if (entry->mItem->IsSecure() && !IsCallerSecure()) { + return NS_ERROR_DOM_SECURITY_ERR; + } + if (!mUseDB) { + rv = entry->mItem->SetValue(aData); + NS_ENSURE_SUCCESS(rv, rv); + } + } + else { + if (mUseDB) + newitem = new nsDOMStorageItem(this, aKey, PR_FALSE); + else + newitem = new nsDOMStorageItem(nsnull, aData, PR_FALSE); + if (!newitem) + return NS_ERROR_OUT_OF_MEMORY; + } + + if (mUseDB) { + rv = SetDBValue(aKey, aData, IsCallerSecure()); + NS_ENSURE_SUCCESS(rv, rv); + } + + if (newitem) { + entry = mItems.PutEntry(aKey); + NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY); + entry->mItem = newitem; + } + + // SetDBValue already calls BroadcastChangeNotification so don't do it again + if (!mUseDB) + BroadcastChangeNotification(); + + return NS_OK; +} + +NS_IMETHODIMP nsDOMStorage::RemoveItem(const nsAString& aKey) +{ + if (aKey.IsEmpty()) + return NS_OK; + + nsSessionStorageEntry *entry = mItems.GetEntry(aKey); + + if (entry && entry->mItem->IsSecure() && !IsCallerSecure()) { + return NS_ERROR_DOM_SECURITY_ERR; + } + + if (mUseDB) { +#ifdef MOZ_STORAGE + nsresult rv = InitDB(); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString unused; + PRBool secureItem; + rv = GetDBValue(aKey, unused, &secureItem); + if (rv == NS_ERROR_DOM_NOT_FOUND_ERR) + return NS_OK; + NS_ENSURE_SUCCESS(rv, rv); + + rv = gStorageDB->RemoveKey(mDomain, aKey); + NS_ENSURE_SUCCESS(rv, rv); + + mItemsCached = PR_FALSE; +#endif + } + else if (entry) { + // clear string as StorageItems may be referencing this item + entry->mItem->ClearValue(); + } + + mItems.RawRemoveEntry(entry); + + BroadcastChangeNotification(); + + return NS_OK; +} + +nsresult +nsDOMStorage::InitDB() +{ +#ifdef MOZ_STORAGE + if (!gStorageDB) { + gStorageDB = new nsDOMStorageDB(); + if (!gStorageDB) + return NS_ERROR_OUT_OF_MEMORY; + + nsresult rv = gStorageDB->Init(); + if (NS_FAILED(rv)) { + // Failed to initialize the DB, delete it and null out the + // pointer so we don't end up attempting to use an + // un-initialized DB later on. + + delete gStorageDB; + gStorageDB = nsnull; + + return rv; + } + } +#endif + + return NS_OK; +} + +nsresult +nsDOMStorage::CacheKeysFromDB() +{ +#ifdef MOZ_STORAGE + // cache all the keys in the hash. This is used by the Length and Key methods + // use this cache for better performance. The disadvantage is that the + // order may break if someone changes the keys in the database directly. + if (!mItemsCached) { + nsresult rv = InitDB(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = gStorageDB->GetAllKeys(mDomain, this, &mItems); + NS_ENSURE_SUCCESS(rv, rv); + + mItemsCached = PR_TRUE; + } +#endif + + return NS_OK; +} + +nsresult +nsDOMStorage::GetDBValue(const nsAString& aKey, nsAString& aValue, + PRBool* aSecure) +{ + aValue.Truncate(); + +#ifdef MOZ_STORAGE + NS_ASSERTION(mUseDB, + "Uh, we should only get here if we're using the database!"); + + nsresult rv = InitDB(); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString value; + rv = gStorageDB->GetKeyValue(mDomain, aKey, value, aSecure); + if (NS_FAILED(rv)) + return rv; + + if (!IsCallerSecure() && *aSecure) { + return NS_ERROR_DOM_SECURITY_ERR; + } + + aValue.Assign(value); +#endif + + return NS_OK; +} + +nsresult +nsDOMStorage::SetDBValue(const nsAString& aKey, + const nsAString& aValue, + PRBool aSecure) +{ +#ifdef MOZ_STORAGE + NS_ASSERTION(mUseDB, + "Uh, we should only get here if we're using the database!"); + + nsresult rv = InitDB(); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString value; + rv = gStorageDB->SetKey(mDomain, aKey, aValue, aSecure); + NS_ENSURE_SUCCESS(rv, rv); + + mItemsCached = PR_FALSE; + + BroadcastChangeNotification(); +#endif + + return NS_OK; +} + +nsresult +nsDOMStorage::SetSecure(const nsAString& aKey, PRBool aSecure) +{ +#ifdef MOZ_STORAGE + if (mUseDB) { + nsresult rv = InitDB(); + NS_ENSURE_SUCCESS(rv, rv); + + return gStorageDB->SetSecure(mDomain, aKey, aSecure); + } +#else + return NS_ERROR_NOT_IMPLEMENTED; +#endif + + nsSessionStorageEntry *entry = mItems.GetEntry(aKey); + NS_ASSERTION(entry, "Don't use SetSecure() with non-existing keys!"); + + if (entry) { + entry->mItem->SetSecureInternal(aSecure); + } + + return NS_OK; +} + +PR_STATIC_CALLBACK(PLDHashOperator) +CopyStorageItems(nsSessionStorageEntry* aEntry, void* userArg) +{ + nsDOMStorage* newstorage = NS_STATIC_CAST(nsDOMStorage*, userArg); + + newstorage->SetItem(aEntry->GetKey(), aEntry->mItem->GetValueInternal()); + + if (aEntry->mItem->IsSecure()) { + newstorage->SetSecure(aEntry->GetKey(), PR_TRUE); + } + + return PL_DHASH_NEXT; +} + +already_AddRefed +nsDOMStorage::Clone() +{ + if (mUseDB) { + NS_ERROR("Uh, don't clone a global storage object."); + + return nsnull; + } + + nsDOMStorage* storage = new nsDOMStorage(mDomain, PR_FALSE); + if (!storage) + return nsnull; + + mItems.EnumerateEntries(CopyStorageItems, storage); + + NS_ADDREF(storage); + + return storage; +} + +struct KeysArrayBuilderStruct +{ + PRBool callerIsSecure; + nsTArray *keys; +}; + +PR_STATIC_CALLBACK(PLDHashOperator) +KeysArrayBuilder(nsSessionStorageEntry* aEntry, void* userArg) +{ + KeysArrayBuilderStruct *keystruct = (KeysArrayBuilderStruct *)userArg; + + if (keystruct->callerIsSecure || !aEntry->mItem->IsSecure()) + keystruct->keys->AppendElement(aEntry->GetKey()); + + return PL_DHASH_NEXT; +} + +nsTArray * +nsDOMStorage::GetKeys() +{ + if (mUseDB) + CacheKeysFromDB(); + + KeysArrayBuilderStruct keystruct; + keystruct.callerIsSecure = IsCallerSecure(); + keystruct.keys = new nsTArray(); + if (keystruct.keys) + mItems.EnumerateEntries(KeysArrayBuilder, &keystruct); + + return keystruct.keys; +} + +void +nsDOMStorage::BroadcastChangeNotification() +{ + nsresult rv; + nsCOMPtr observerService = + do_GetService("@mozilla.org/observer-service;1", &rv); + if (NS_FAILED(rv)) { + return; + } + + // Fire off a notification that a storage object changed. If the + // storage object is a session storage object, we don't pass a + // domain, but if it's a global storage object we do. + observerService->NotifyObservers((nsIDOMStorage *)this, + "dom-storage-changed", + mUseDB ? mDomain.get() : nsnull); +} + +// +// nsDOMStorageList +// + +NS_INTERFACE_MAP_BEGIN(nsDOMStorageList) + NS_INTERFACE_MAP_ENTRY(nsISupports) + NS_INTERFACE_MAP_ENTRY(nsIDOMStorageList) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageList) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(nsDOMStorageList) +NS_IMPL_RELEASE(nsDOMStorageList) + +nsresult +nsDOMStorageList::NamedItem(const nsAString& aDomain, + nsIDOMStorage** aStorage) +{ + nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); + if (!ssm) + return NS_ERROR_FAILURE; + + nsCOMPtr subjectPrincipal; + nsresult rv = ssm->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr systemPrincipal; + rv = ssm->GetSystemPrincipal(getter_AddRefs(systemPrincipal)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCAutoString currentDomain; + if (subjectPrincipal) { + nsCOMPtr uri; + rv = subjectPrincipal->GetURI(getter_AddRefs(uri)); + if (NS_SUCCEEDED(rv) && uri) { + rv = uri->GetAsciiHost(currentDomain); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR); + } + } + + if (subjectPrincipal == systemPrincipal || !currentDomain.IsEmpty()) { + return GetStorageForDomain(aDomain, NS_ConvertUTF8toUTF16(currentDomain), + subjectPrincipal == systemPrincipal, aStorage); + } + + return NS_ERROR_DOM_SECURITY_ERR; +} + +// static +PRBool +nsDOMStorageList::CanAccessDomain(const nsAString& aRequestedDomain, + const nsAString& aCurrentDomain) +{ + nsStringArray requestedDomainArray, currentDomainArray; + PRBool ok = ConvertDomainToArray(aRequestedDomain, &requestedDomainArray); + if (!ok) + return PR_FALSE; + + ok = ConvertDomainToArray(aCurrentDomain, ¤tDomainArray); + if (!ok) + return PR_FALSE; + + if (currentDomainArray.Count() == 1) + currentDomainArray.AppendString(NS_LITERAL_STRING("localdomain")); + + // need to use the shorter of the two arrays + PRInt32 currentPos = 0; + PRInt32 requestedPos = 0; + PRInt32 length = requestedDomainArray.Count(); + if (currentDomainArray.Count() > length) + currentPos = currentDomainArray.Count() - length; + else if (currentDomainArray.Count() < length) + requestedPos = length - currentDomainArray.Count(); + + // If the current domain is different in any of the parts from the + // requested domain, a security exception is raised + for (; requestedPos < length; requestedPos++, currentPos++) { + if (*requestedDomainArray[requestedPos] != *currentDomainArray[currentPos]) + return PR_FALSE; + } + + return PR_TRUE; +} + +nsresult +nsDOMStorageList::GetStorageForDomain(const nsAString& aRequestedDomain, + const nsAString& aCurrentDomain, + PRBool aNoCurrentDomainCheck, + nsIDOMStorage** aStorage) +{ + if (!aNoCurrentDomainCheck && !CanAccessDomain(aRequestedDomain, + aCurrentDomain)) { + return NS_ERROR_DOM_SECURITY_ERR; + } + + nsStringArray requestedDomainArray; + PRBool ok = ConvertDomainToArray(aRequestedDomain, &requestedDomainArray); + if (!ok) + return NS_ERROR_DOM_SECURITY_ERR; + + // now rebuild a string for the domain. + nsAutoString usedDomain; + PRInt32 requestedPos = 0; + for (requestedPos = 0; requestedPos < requestedDomainArray.Count(); + requestedPos++) { + if (!usedDomain.IsEmpty()) + usedDomain.AppendLiteral("."); + usedDomain.Append(*requestedDomainArray[requestedPos]); + } + + // now have a valid domain, so look it up in the storage table + if (!mStorages.Get(usedDomain, aStorage)) { + nsCOMPtr newstorage = new nsDOMStorage(usedDomain, PR_TRUE); + if (!newstorage) + return NS_ERROR_OUT_OF_MEMORY; + + if (!mStorages.Put(usedDomain, newstorage)) + return NS_ERROR_OUT_OF_MEMORY; + + newstorage.swap(*aStorage); + } + + return NS_OK; +} + +// static +PRBool +nsDOMStorageList::ConvertDomainToArray(const nsAString& aDomain, + nsStringArray* aArray) +{ + PRInt32 length = aDomain.Length(); + PRInt32 n = 0; + while (n < length) { + PRInt32 dotpos = aDomain.FindChar('.', n); + nsAutoString domain; + + if (dotpos == -1) // no more dots + domain.Assign(Substring(aDomain, n)); + else if (dotpos - n == 0) // no point continuing in this case + return false; + else if (dotpos >= 0) + domain.Assign(Substring(aDomain, n, dotpos - n)); + + ToLowerCase(domain); + aArray->AppendString(domain); + + if (dotpos == -1) + break; + + n = dotpos + 1; + } + + // if n equals the length, there is a dot at the end, so treat it as invalid + return (n != length); +} + +nsresult +NS_NewDOMStorageList(nsIDOMStorageList** aResult) +{ + *aResult = new nsDOMStorageList(); + if (!*aResult) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*aResult); + return NS_OK; +} + +// +// nsDOMStorageItem +// + +NS_INTERFACE_MAP_BEGIN(nsDOMStorageItem) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorageItem) + NS_INTERFACE_MAP_ENTRY(nsIDOMStorageItem) + NS_INTERFACE_MAP_ENTRY(nsIDOMToString) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageItem) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(nsDOMStorageItem) +NS_IMPL_RELEASE(nsDOMStorageItem) + +nsDOMStorageItem::nsDOMStorageItem(nsDOMStorage* aStorage, + const nsAString& aKey, + PRBool aSecure) + : mSecure(aSecure), + mKeyOrValue(aKey), + mStorage(aStorage) +{ +} + +nsDOMStorageItem::~nsDOMStorageItem() +{ +} + +NS_IMETHODIMP +nsDOMStorageItem::GetSecure(PRBool* aSecure) +{ + if (!IsCallerSecure()) { + return NS_ERROR_DOM_INVALID_ACCESS_ERR; + } + + if (mStorage) { + nsAutoString value; + return mStorage->GetDBValue(mKeyOrValue, value, aSecure); + } + + *aSecure = IsSecure(); + return NS_OK; +} + +NS_IMETHODIMP +nsDOMStorageItem::SetSecure(PRBool aSecure) +{ + if (!IsCallerSecure()) { + return NS_ERROR_DOM_INVALID_ACCESS_ERR; + } + + if (mStorage) { + nsresult rv = mStorage->SetSecure(mKeyOrValue, aSecure); + NS_ENSURE_SUCCESS(rv, rv); + } + + mSecure = aSecure; + return NS_OK; +} + +NS_IMETHODIMP +nsDOMStorageItem::GetValue(nsAString& aValue) +{ + if (mStorage) { + // GetDBValue checks the secure state so no need to do it here + PRBool secure; + nsresult rv = mStorage->GetDBValue(mKeyOrValue, aValue, &secure); + return (rv == NS_ERROR_DOM_NOT_FOUND_ERR) ? NS_OK : rv; + } + + if (IsSecure() && !IsCallerSecure()) { + return NS_ERROR_DOM_SECURITY_ERR; + } + + aValue = mKeyOrValue; + return NS_OK; +} + +NS_IMETHODIMP +nsDOMStorageItem::SetValue(const nsAString& aValue) +{ + PRBool secureCaller = IsCallerSecure(); + + if (mStorage) { + // SetDBValue() does the security checks for us. + return mStorage->SetDBValue(mKeyOrValue, aValue, secureCaller); + } + + PRBool secureItem = IsSecure(); + + if (!secureCaller && secureItem) { + // The item is secure, but the caller isn't. Throw. + + return NS_ERROR_DOM_SECURITY_ERR; + } + + mKeyOrValue = aValue; + mSecure = secureCaller; + return NS_OK; +} + +NS_IMETHODIMP +nsDOMStorageItem::ToString(nsAString& aStr) +{ + return GetValue(aStr); +} + +// QueryInterface implementation for nsDOMStorageEvent +NS_INTERFACE_MAP_BEGIN(nsDOMStorageEvent) + NS_INTERFACE_MAP_ENTRY(nsIDOMStorageEvent) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageEvent) +NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent) + +NS_IMPL_ADDREF_INHERITED(nsDOMStorageEvent, nsDOMEvent) +NS_IMPL_RELEASE_INHERITED(nsDOMStorageEvent, nsDOMEvent) + + +NS_IMETHODIMP +nsDOMStorageEvent::GetDomain(nsAString& aDomain) +{ + // mDomain will be #session for session storage for events that fire + // due to a change in a session storage object. + aDomain = mDomain; + + return NS_OK; +} + +nsresult +nsDOMStorageEvent::Init() +{ + nsresult rv = InitEvent(NS_LITERAL_STRING("storage"), PR_TRUE, PR_FALSE); + + // This init method is only called by native code, so set the + // trusted flag here. + SetTrusted(PR_TRUE); + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMStorageEvent::InitStorageEvent(const nsAString& aTypeArg, + PRBool aCanBubbleArg, + PRBool aCancelableArg, + const nsAString& aDomainArg) +{ + nsresult rv = InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg); + NS_ENSURE_SUCCESS(rv, rv); + + mDomain = aDomainArg; + + return NS_OK; +} + +NS_IMETHODIMP +nsDOMStorageEvent::InitStorageEventNS(const nsAString& aNamespaceURIArg, + const nsAString& aTypeArg, + PRBool aCanBubbleArg, + PRBool aCancelableArg, + const nsAString& aDomainArg) +{ + // XXXjst: Figure out what to do with aNamespaceURIArg here! + nsresult rv = InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg); + NS_ENSURE_SUCCESS(rv, rv); + + mDomain = aDomainArg; + + return NS_OK; +} diff --git a/mozilla/dom/src/storage/nsDOMStorage.h b/mozilla/dom/src/storage/nsDOMStorage.h new file mode 100644 index 00000000000..6b729c1e97e --- /dev/null +++ b/mozilla/dom/src/storage/nsDOMStorage.h @@ -0,0 +1,279 @@ +/* -*- 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) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Neil Deakin + * Johnny Stenback + * + * 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 ***** */ + +#ifndef nsDOMStorage_h___ +#define nsDOMStorage_h___ + +#include "nscore.h" +#include "nsAutoPtr.h" +#include "nsIDOMStorage.h" +#include "nsIDOMStorageList.h" +#include "nsIDOMStorageItem.h" +#include "nsInterfaceHashtable.h" +#include "nsVoidArray.h" +#include "nsPIDOMStorage.h" +#include "nsIDOMToString.h" +#include "nsDOMEvent.h" +#include "nsIDOMStorageEvent.h" + +#ifdef MOZ_STORAGE +#include "nsDOMStorageDB.h" +#endif + +class nsDOMStorageItem; + +class nsSessionStorageEntry : public nsStringHashKey +{ +public: + nsSessionStorageEntry(KeyTypePointer aStr) : nsStringHashKey(aStr) + { + } + nsSessionStorageEntry(const nsSessionStorageEntry& aToCopy) + : nsStringHashKey(aToCopy) + { + NS_ERROR("We're horked."); + } + + nsRefPtr mItem; +}; + +class nsDOMStorage : public nsIDOMStorage, + public nsPIDOMStorage +{ +public: + nsDOMStorage(); + nsDOMStorage(const nsAString& aDomain, PRBool aUseDB); + virtual ~nsDOMStorage(); + + // nsISupports + NS_DECL_ISUPPORTS + + // nsIDOMStorage + NS_DECL_NSIDOMSTORAGE + + // nsPIDOMStorage + virtual void Init(const nsAString& aDomain, PRBool aUseDB); + virtual already_AddRefed Clone(); + virtual nsTArray *GetKeys(); + + // cache the keys from the database for faster lookup + nsresult + CacheKeysFromDB(); + + // retrieve the value and secure state corresponding to a key out of storage. + nsresult + GetDBValue(const nsAString& aKey, nsAString& aValue, PRBool* aSecure); + + // set the value corresponding to a key in the storage. If + // aSecure is false, then attempts to modify a secure value + // throw NS_ERROR_DOM_INVALID_ACCESS_ERR + nsresult + SetDBValue(const nsAString& aKey, + const nsAString& aValue, + PRBool aSecure); + + // set the value corresponding to a key as secure. + nsresult + SetSecure(const nsAString& aKey, PRBool aSecure); + +protected: + + nsresult InitDB(); + + void BroadcastChangeNotification(); + + // true if the storage database should be used for values + PRBool mUseDB; + + // true if items from the database are cached + PRBool mItemsCached; + + // domain this store is associated with + nsAutoString mDomain; + + // the key->value item pairs + nsTHashtable mItems; + +#ifdef MOZ_STORAGE + static nsDOMStorageDB* gStorageDB; +#endif +}; + +class nsDOMStorageList : public nsIDOMStorageList +{ +public: + nsDOMStorageList() + { + mStorages.Init(); + }; + + virtual ~nsDOMStorageList() {}; + + // nsISupports + NS_DECL_ISUPPORTS + + // nsIDOMStorageList + NS_DECL_NSIDOMSTORAGELIST + + /** + * Return the global nsIDOMStorage for a particular domain. + * aNoCurrentDomainCheck may be true to skip the domain comparison; + * this is used for chrome code so that it may retrieve data from + * any domain. + * + * @param aRequestedDomain domain to return + * @param aCurrentDomain domain of current caller + * @param aNoCurrentDomainCheck true to skip domain comparison + */ + nsresult + GetStorageForDomain(const nsAString& aRequestedDomain, + const nsAString& aCurrentDomain, + PRBool aNoCurrentDomainCheck, + nsIDOMStorage** aStorage); + + /** + * Convert the domain into an array of its component parts. + */ + static PRBool + ConvertDomainToArray(const nsAString& aDomain, + nsStringArray* aArray); + + /** + * Check whether aCurrentDomain has access to aRequestedDomain + */ + static PRBool + CanAccessDomain(const nsAString& aRequestedDomain, + const nsAString& aCurrentDomain); + +protected: + + nsInterfaceHashtable mStorages; +}; + +class nsDOMStorageItem : public nsIDOMStorageItem, + public nsIDOMToString +{ +public: + nsDOMStorageItem(nsDOMStorage* aStorage, + const nsAString& aKey, + PRBool aSecure); + virtual ~nsDOMStorageItem(); + + // nsISupports + NS_DECL_ISUPPORTS + + // nsIDOMStorage + NS_DECL_NSIDOMSTORAGEITEM + + // nsIDOMToString + NS_DECL_NSIDOMTOSTRING + + PRBool IsSecure() + { + return mSecure; + } + + void SetSecureInternal(PRBool aSecure) + { + mSecure = aSecure; + } + + const nsAString& GetValueInternal() + { + NS_ASSERTION(!mStorage, "Don't call this on global storage items!"); + + return mKeyOrValue; + } + + void ClearValue() + { + NS_ASSERTION(!mStorage, "Don't call this on global storage items!"); + + mKeyOrValue.Truncate(); + } + +protected: + + // true if this value is for secure sites only + PRBool mSecure; + + // value of the item, or key for the item if it came from the db. + nsString mKeyOrValue; + + // If this item came from the db, mStorage points to the storage + // object where this item came from. + nsRefPtr mStorage; +}; + +class nsDOMStorageEvent : public nsDOMEvent, + public nsIDOMStorageEvent +{ +public: + nsDOMStorageEvent(const nsAString& aDomain) + : nsDOMEvent(nsnull, nsnull), mDomain(aDomain) + { + if (aDomain.IsEmpty()) { + // An empty domain means this event is for a session sotrage + // object change. Store #session as the domain. + + mDomain = NS_LITERAL_STRING("#session"); + } + } + + virtual ~nsDOMStorageEvent() + { + } + + nsresult Init(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIDOMSTORAGEEVENT + NS_FORWARD_NSIDOMEVENT(nsDOMEvent::) + +protected: + nsString mDomain; +}; + +nsresult +NS_NewDOMStorage(nsISupports* aOuter, REFNSIID aIID, void** aResult); + +nsresult +NS_NewDOMStorageList(nsIDOMStorageList** aResult); + +#endif /* nsDOMStorage_h___ */ diff --git a/mozilla/dom/src/storage/nsDOMStorageDB.cpp b/mozilla/dom/src/storage/nsDOMStorageDB.cpp new file mode 100644 index 00000000000..77bc556c38e --- /dev/null +++ b/mozilla/dom/src/storage/nsDOMStorageDB.cpp @@ -0,0 +1,302 @@ +/* -*- 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) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Neil Deakin + * + * 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 "nsCOMPtr.h" +#include "nsDOMError.h" +#include "nsDOMStorage.h" +#include "nsDOMStorageDB.h" +#include "nsIFile.h" +#include "nsAppDirectoryServiceDefs.h" +#include "mozStorageCID.h" +#include "mozStorageHelper.h" +#include "mozIStorageService.h" +#include "mozIStorageValueArray.h" + +nsresult +nsDOMStorageDB::Init() +{ + nsresult rv; + + nsCOMPtr storageFile; + rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, + getter_AddRefs(storageFile)); + NS_ENSURE_SUCCESS(rv, rv); + rv = storageFile->Append(NS_LITERAL_STRING("webappsstore.sqlite")); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr service; + + service = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + rv = service->OpenDatabase(storageFile, getter_AddRefs(mConnection)); + NS_ENSURE_SUCCESS(rv, rv); + + PRBool exists; + rv = mConnection->TableExists(NS_LITERAL_CSTRING("moz_webappsstore"), &exists); + NS_ENSURE_SUCCESS(rv, rv); + if (! exists) { + rv = mConnection->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE TABLE moz_webappsstore (" + "domain TEXT, " + "key TEXT, " + "value TEXT, " + "secure INTEGER) ")); + NS_ENSURE_SUCCESS(rv, rv); + } + + // retrieve all keys associated with a domain + rv = mConnection->CreateStatement( + NS_LITERAL_CSTRING("SELECT key, secure FROM moz_webappsstore " + "WHERE domain = ?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 FROM moz_webappsstore " + "WHERE domain = ?1 " + "AND key = ?2"), + getter_AddRefs(mGetKeyValueStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + // insert a new key + rv = mConnection->CreateStatement( + NS_LITERAL_CSTRING("INSERT INTO moz_webappsstore values (?1, ?2, ?3, ?4)"), + getter_AddRefs(mInsertKeyStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + // update an existing key + rv = mConnection->CreateStatement( + NS_LITERAL_CSTRING("UPDATE moz_webappsstore " + "SET value = ?1, secure = ?2 " + "WHERE domain = ?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 moz_webappsstore " + "SET secure = ?1 " + "WHERE domain = ?2 " + "AND key = ?3 "), + getter_AddRefs(mSetSecureStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + // remove a key + rv = mConnection->CreateStatement( + NS_LITERAL_CSTRING("DELETE FROM moz_webappsstore " + "WHERE domain = ?1 " + "AND key = ?2"), + getter_AddRefs(mRemoveKeyStatement)); + + return rv; +} + +nsresult +nsDOMStorageDB::GetAllKeys(const nsAString& aDomain, + nsDOMStorage* aStorage, + nsTHashtable* aKeys) +{ + mozStorageStatementScoper scope(mGetAllKeysStatement); + + nsresult rv = mGetAllKeysStatement->BindStringParameter(0, aDomain); + NS_ENSURE_SUCCESS(rv, rv); + + PRBool exists; + while (NS_SUCCEEDED(rv = mGetAllKeysStatement->ExecuteStep(&exists)) && + exists) { + + nsAutoString key; + rv = mGetAllKeysStatement->GetString(0, key); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt32 secureInt = 0; + rv = mGetAllKeysStatement->GetInt32(1, &secureInt); + NS_ENSURE_SUCCESS(rv, rv); + + nsSessionStorageEntry* entry = aKeys->PutEntry(key); + NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY); + + entry->mItem = new nsDOMStorageItem(aStorage, key, secureInt); + if (!entry->mItem) { + aKeys->RawRemoveEntry(entry); + return NS_ERROR_OUT_OF_MEMORY; + } + } + + return NS_OK; +} + +nsresult +nsDOMStorageDB::GetKeyValue(const nsAString& aDomain, + const nsAString& aKey, + nsAString& aValue, + PRBool* aSecure) +{ + mozStorageStatementScoper scope(mGetKeyValueStatement); + + nsresult rv = mGetKeyValueStatement->BindStringParameter(0, aDomain); + NS_ENSURE_SUCCESS(rv, rv); + rv = mGetKeyValueStatement->BindStringParameter(1, aKey); + NS_ENSURE_SUCCESS(rv, rv); + + PRBool exists; + rv = mGetKeyValueStatement->ExecuteStep(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt32 secureInt = 0; + if (exists) { + rv = mGetKeyValueStatement->GetString(0, aValue); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mGetKeyValueStatement->GetInt32(1, &secureInt); + NS_ENSURE_SUCCESS(rv, rv); + } + else { + rv = NS_ERROR_DOM_NOT_FOUND_ERR; + } + + *aSecure = (PRBool)secureInt; + + return rv; +} + +nsresult +nsDOMStorageDB::SetKey(const nsAString& aDomain, + const nsAString& aKey, + const nsAString& aValue, + PRBool aSecure) +{ + mozStorageStatementScoper scope(mGetKeyValueStatement); + + nsresult rv = mGetKeyValueStatement->BindStringParameter(0, aDomain); + NS_ENSURE_SUCCESS(rv, rv); + rv = mGetKeyValueStatement->BindStringParameter(1, aKey); + NS_ENSURE_SUCCESS(rv, rv); + + PRBool exists; + rv = mGetKeyValueStatement->ExecuteStep(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (exists) { + if (!aSecure) { + PRInt32 secureInt = 0; + rv = mGetKeyValueStatement->GetInt32(1, &secureInt); + NS_ENSURE_SUCCESS(rv, rv); + if (secureInt) + return NS_ERROR_DOM_SECURITY_ERR; + } + + mGetKeyValueStatement->Reset(); + + mozStorageStatementScoper scopeupdate(mUpdateKeyStatement); + + rv = mUpdateKeyStatement->BindStringParameter(0, aValue); + NS_ENSURE_SUCCESS(rv, rv); + rv = mUpdateKeyStatement->BindInt32Parameter(1, aSecure); + NS_ENSURE_SUCCESS(rv, rv); + rv = mUpdateKeyStatement->BindStringParameter(2, aDomain); + NS_ENSURE_SUCCESS(rv, rv); + rv = mUpdateKeyStatement->BindStringParameter(3, aKey); + NS_ENSURE_SUCCESS(rv, rv); + + return mUpdateKeyStatement->Execute(); + } + + mozStorageStatementScoper scopeinsert(mInsertKeyStatement); + + rv = mInsertKeyStatement->BindStringParameter(0, aDomain); + NS_ENSURE_SUCCESS(rv, rv); + rv = mInsertKeyStatement->BindStringParameter(1, aKey); + NS_ENSURE_SUCCESS(rv, rv); + rv = mInsertKeyStatement->BindStringParameter(2, aValue); + NS_ENSURE_SUCCESS(rv, rv); + rv = mInsertKeyStatement->BindInt32Parameter(3, aSecure); + NS_ENSURE_SUCCESS(rv, rv); + + return mInsertKeyStatement->Execute(); +} + +nsresult +nsDOMStorageDB::SetSecure(const nsAString& aDomain, + const nsAString& aKey, + const PRBool aSecure) +{ + mozStorageStatementScoper scope(mGetKeyValueStatement); + + nsresult rv = mGetKeyValueStatement->BindStringParameter(0, aDomain); + NS_ENSURE_SUCCESS(rv, rv); + rv = mGetKeyValueStatement->BindStringParameter(1, 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; +} + +nsresult +nsDOMStorageDB::RemoveKey(const nsAString& aDomain, + const nsAString& aKey) +{ + mozStorageStatementScoper scope(mRemoveKeyStatement); + + nsresult rv = mRemoveKeyStatement->BindStringParameter(0, aDomain); + NS_ENSURE_SUCCESS(rv, rv); + rv = mRemoveKeyStatement->BindStringParameter(1, aKey); + NS_ENSURE_SUCCESS(rv, rv); + + return mRemoveKeyStatement->Execute(); +} diff --git a/mozilla/dom/src/storage/nsDOMStorageDB.h b/mozilla/dom/src/storage/nsDOMStorageDB.h new file mode 100644 index 00000000000..1cebadb9dfd --- /dev/null +++ b/mozilla/dom/src/storage/nsDOMStorageDB.h @@ -0,0 +1,113 @@ +/* -*- 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 Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Neil Deakin + * + * 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 ***** */ + +#ifndef nsDOMStorageDB_h___ +#define nsDOMStorageDB_h___ + +#include "nscore.h" +#include "mozIStorageConnection.h" +#include "mozIStorageStatement.h" +#include "nsTHashtable.h" + +class nsDOMStorage; +class nsSessionStorageEntry; + +class nsDOMStorageDB +{ +public: + nsDOMStorageDB() {}; + ~nsDOMStorageDB() {}; + + nsresult + Init(); + + /** + * Retrieve a list of all the keys associated with a particular domain. + */ + nsresult + GetAllKeys(const nsAString& aDomain, + nsDOMStorage* aStorage, + nsTHashtable* aKeys); + + /** + * Retrieve a value and secure flag for a key from storage. + * + * @throws NS_ERROR_DOM_NOT_FOUND_ERR if key not found + */ + nsresult + GetKeyValue(const nsAString& aDomain, + const nsAString& aKey, + nsAString& aValue, + PRBool* aSecure); + + /** + * Set the value and secure flag for a key in storage. + */ + nsresult + SetKey(const nsAString& aDomain, + const nsAString& aKey, + const nsAString& aValue, + PRBool aSecure); + + /** + * Set the secure flag for a key in storage. Does nothing if the key was + * not found. + */ + nsresult + SetSecure(const nsAString& aDomain, + const nsAString& aKey, + const PRBool aSecure); + + /** + * Removes a key from storage. + */ + nsresult + RemoveKey(const nsAString& aDomain, + const nsAString& aKey); + +protected: + + nsCOMPtr mConnection; + + nsCOMPtr mGetAllKeysStatement; + nsCOMPtr mGetKeyValueStatement; + nsCOMPtr mInsertKeyStatement; + nsCOMPtr mUpdateKeyStatement; + nsCOMPtr mSetSecureStatement; + nsCOMPtr mRemoveKeyStatement; +}; + +#endif /* nsDOMStorageDB_h___ */ diff --git a/mozilla/layout/build/Makefile.in b/mozilla/layout/build/Makefile.in index 5f0d7972093..7874b22674e 100644 --- a/mozilla/layout/build/Makefile.in +++ b/mozilla/layout/build/Makefile.in @@ -83,6 +83,7 @@ REQUIRES = xpcom \ locale \ necko \ dom \ + storage \ editor \ webshell \ docshell \ @@ -141,6 +142,7 @@ SHARED_LIBRARY_LIBS = \ $(DEPTH)/dom/src/base/$(LIB_PREFIX)jsdombase_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/src/events/$(LIB_PREFIX)jsdomevents_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/src/jsurl/$(LIB_PREFIX)jsurl_s.$(LIB_SUFFIX) \ + $(DEPTH)/dom/src/storage/$(LIB_PREFIX)jsdomstorage_s.$(LIB_SUFFIX) \ $(NULL) ifdef NS_PRINTING @@ -257,6 +259,7 @@ LOCAL_INCLUDES += -I$(srcdir)/../base \ -I$(topsrcdir)/view/src \ -I$(topsrcdir)/dom/src/base \ -I$(topsrcdir)/dom/src/jsurl \ + -I$(topsrcdir)/dom/src/storage \ -I. \ $(NULL) diff --git a/mozilla/layout/build/nsLayoutCID.h b/mozilla/layout/build/nsLayoutCID.h index 9c12d85f071..54c2add6383 100644 --- a/mozilla/layout/build/nsLayoutCID.h +++ b/mozilla/layout/build/nsLayoutCID.h @@ -229,4 +229,8 @@ #define NS_CANVASRENDERINGCONTEXT2D_CID \ { 0xa35d1cd4, 0xc505, 0x4d2d, { 0xa0, 0xf9, 0xae, 0xf0, 0x0b, 0x7c, 0xe5, 0xa5 } } +// {8b449142-1eab-4bfa-9830-fab6ebb09774} +#define NS_DOMSTORAGE_CID \ +{ 0x8b449142, 0x1eab, 0x4bfa, { 0x98, 0x30, 0xfa, 0xb6, 0xeb, 0xb0, 0x97, 0x74 } } + #endif /* nsLayoutCID_h__ */ diff --git a/mozilla/layout/build/nsLayoutModule.cpp b/mozilla/layout/build/nsLayoutModule.cpp index add4e8b5c3f..522a6ece0a4 100644 --- a/mozilla/layout/build/nsLayoutModule.cpp +++ b/mozilla/layout/build/nsLayoutModule.cpp @@ -162,6 +162,7 @@ #include "nsDOMScriptObjectFactory.h" #include "nsAutoCopyListener.h" #include "nsDOMAttribute.h" +#include "nsDOMStorage.h" #include "nsHTMLCanvasFrame.h" @@ -1353,7 +1354,12 @@ static const nsModuleComponentInfo gComponents[] = { { "DOM Parser", NS_DOMPARSER_CID, NS_DOMPARSER_CONTRACTID, - nsDOMParserConstructor } + nsDOMParserConstructor }, + + { "DOM Storage", + NS_DOMSTORAGE_CID, + "@mozilla.org/dom/storage;1", + NS_NewDOMStorage } }; NS_IMPL_NSGETMODULE_WITH_CTOR(nsLayoutModule, gComponents, Initialize)