Bug 455070 - Make sessionStorage object conform the WHATWG spec, r+sr=jst
☠☠ backed out by 4abf1ac9a782 ☠ ☠
authorHonza Bambas <honzab.moz@firemni.cz>
Wed, 20 May 2009 10:23:41 +0200
changeset 28628 ef25b4175b78e80a648afaaf849d172f7553555f
parent 28627 dbf96b1b73bdae7a6579f2c81c2afa0d14bf1255
child 28629 752810cce4610954435403e1566f0a1ca9b45a4f
child 28630 4abf1ac9a78214f78059c1dd4edd87461227d11d
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs455070
milestone1.9.2a1pre
Bug 455070 - Make sessionStorage object conform the WHATWG spec, r+sr=jst
browser/components/sessionstore/src/nsSessionStore.js
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsIDocShell.idl
dom/base/nsGlobalWindow.cpp
dom/interfaces/storage/nsIDOMStorageWindow.idl
dom/interfaces/storage/nsPIDOMStorage.h
dom/src/storage/nsDOMStorage.cpp
dom/src/storage/nsDOMStorage.h
dom/tests/mochitest/Makefile.in
dom/tests/mochitest/sessionstorage/Makefile.in
dom/tests/mochitest/sessionstorage/frameEqual.html
dom/tests/mochitest/sessionstorage/frameNotEqual.html
dom/tests/mochitest/sessionstorage/frameReplace.html
dom/tests/mochitest/sessionstorage/interOriginSlave.js
dom/tests/mochitest/sessionstorage/interOriginTest.js
dom/tests/mochitest/sessionstorage/test_sessionStorageBase.html
dom/tests/mochitest/sessionstorage/test_sessionStorageClone.html
dom/tests/mochitest/sessionstorage/test_sessionStorageReplace.html
embedding/components/windowwatcher/src/nsWindowWatcher.cpp
layout/build/nsLayoutCID.h
layout/build/nsLayoutModule.cpp
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -1266,17 +1266,17 @@ SessionStoreService.prototype = {
    */
   _serializeSessionStorage:
     function sss_serializeSessionStorage(aTabData, aHistory, aDocShell, aFullData) {
     let storageData = {};
     let hasContent = false;
 
     for (let i = 0; i < aHistory.count; i++) {
       let uri = aHistory.getEntryAtIndex(i, false).URI;
-      // sessionStorage is saved per domain (cf. nsDocShell::GetSessionStorageForURI)
+      // sessionStorage is saved per origin (cf. nsDocShell::GetSessionStorageForURI)
       let domain = uri.spec;
       try {
         if (uri.host)
           domain = uri.prePath;
       }
       catch (ex) { /* this throws for host-less URIs (such as about: or jar:) */ }
       if (storageData[domain] || !(aFullData || this._checkPrivacyLevel(uri.schemeIs("https"))))
         continue;
@@ -1290,19 +1290,17 @@ SessionStoreService.prototype = {
       if (storageItemCount == 0)
         continue;
 
       let data = storageData[domain] = {};
       for (let j = 0; j < storageItemCount; j++) {
         try {
           let key = storage.key(j);
           let item = storage.getItem(key);
-          data[key] = { value: item.value };
-          if (uri.schemeIs("https") && item.secure)
-            data[key].secure = true;
+          data[key] = item;
         }
         catch (ex) { /* XXXzeniko this currently throws for secured items (cf. bug 442048) */ }
       }
       hasContent = true;
     }
 
     if (hasContent)
       aTabData.storage = storageData;
@@ -2103,19 +2101,17 @@ SessionStoreService.prototype = {
    */
   _deserializeSessionStorage: function sss_deserializeSessionStorage(aStorageData, aDocShell) {
     let ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
     for (let url in aStorageData) {
       let uri = ioService.newURI(url, null, null);
       let storage = aDocShell.getSessionStorageForURI(uri);
       for (let key in aStorageData[url]) {
         try {
-          storage.setItem(key, aStorageData[url][key].value);
-          if (uri.schemeIs("https"))
-            storage.getItem(key).secure = aStorageData[url][key].secure || false;
+          storage.setItem(key, aStorageData[url][key]);
         }
         catch (ex) { Cu.reportError(ex); } // throws e.g. for URIs that can't have sessionStorage
       }
     }
   },
 
   /**
    * Restore properties to a loaded document
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -49,16 +49,17 @@
 #include "nsIBrowserDOMWindow.h"
 #include "nsIComponentManager.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMNSDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMStorageObsolete.h"
+#include "nsIDOMStorage.h"
 #include "nsPIDOMStorage.h"
 #include "nsIDocumentViewer.h"
 #include "nsIDocumentLoaderFactory.h"
 #include "nsCURILoader.h"
 #include "nsURILoader.h"
 #include "nsDocShellCID.h"
 #include "nsLayoutCID.h"
 #include "nsDOMCID.h"
@@ -848,17 +849,16 @@ NS_INTERFACE_MAP_BEGIN(nsDocShell)
     NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
     NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     NS_INTERFACE_MAP_ENTRY(nsIContentViewerContainer)
     NS_INTERFACE_MAP_ENTRY(nsIEditorDocShell)
     NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
     NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
     NS_INTERFACE_MAP_ENTRY(nsIObserver)
     NS_INTERFACE_MAP_ENTRY(nsILoadContext)
-    NS_INTERFACE_MAP_ENTRY(nsIDocShell_MOZILLA_1_9_1)
     NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
     NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
     NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
 
 ///*****************************************************************************
 // nsDocShell::nsIInterfaceRequestor
 //*****************************************************************************   
@@ -2138,37 +2138,64 @@ nsDocShell::HistoryPurged(PRInt32 aNumEn
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
                                           PRBool aCreate,
-                                          nsIDOMStorageObsolete** aStorage)
+                                          nsIDOMStorage** aStorage)
 {
     NS_ENSURE_ARG_POINTER(aStorage);
     *aStorage = nsnull;
 
     if (!aPrincipal)
         return NS_OK;
 
-    nsCOMPtr<nsIURI> codebaseURI;
-    nsresult rv = aPrincipal->GetDomain(getter_AddRefs(codebaseURI));
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (!codebaseURI) {
-        rv = aPrincipal->GetURI(getter_AddRefs(codebaseURI));
-        NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    if (!codebaseURI)
+    nsresult rv;
+
+    nsCOMPtr<nsIDocShellTreeItem> topItem;
+    rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
+    if (NS_FAILED(rv))
+        return rv;
+
+    if (!topItem)
+        return NS_ERROR_FAILURE;
+
+    nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
+    if (topDocShell != this)
+        return topDocShell->GetSessionStorageForPrincipal(aPrincipal, aCreate,
+                                                          aStorage);
+
+    nsXPIDLCString origin;
+    rv = aPrincipal->GetOrigin(getter_Copies(origin));
+    if (NS_FAILED(rv))
+        return rv;
+
+    if (origin.IsEmpty())
+        return NS_ERROR_FAILURE;
+
+    if (!mStorages.Get(origin, aStorage) && aCreate) {
+        nsCOMPtr<nsIDOMStorage> newstorage =
+            do_CreateInstance("@mozilla.org/dom/storage;2");
+        if (!newstorage)
+            return NS_ERROR_OUT_OF_MEMORY;
+
+        nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(newstorage);
+        if (!pistorage)
+            return NS_ERROR_FAILURE;
+        pistorage->InitAsSessionStorage(aPrincipal);
+
+        if (!mStorages.Put(origin, newstorage))
+            return NS_ERROR_OUT_OF_MEMORY;
+
+        newstorage.swap(*aStorage);
         return NS_OK;
-
-    rv = GetSessionStorageForURI(codebaseURI, aCreate, aStorage);
-    NS_ENSURE_SUCCESS(rv, rv);
+    }
 
     nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(*aStorage);
     if (piStorage) {
         PRBool canAccess = piStorage->CanAccess(aPrincipal);
         NS_ASSERTION(canAccess,
                      "GetSessionStorageForPrincipal got a storage "
                      "that could not be accessed!");
         if (!canAccess) {
@@ -2177,101 +2204,80 @@ nsDocShell::GetSessionStorageForPrincipa
         }
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetSessionStorageForURI(nsIURI* aURI,
-                                    nsIDOMStorageObsolete** aStorage)
+                                    nsIDOMStorage** aStorage)
 {
     return GetSessionStorageForURI(aURI, PR_TRUE, aStorage);
 }
 
 nsresult
 nsDocShell::GetSessionStorageForURI(nsIURI* aURI,
                                     PRBool aCreate,
-                                    nsIDOMStorageObsolete** aStorage)
+                                    nsIDOMStorage** aStorage)
 {
     NS_ENSURE_ARG(aURI);
     NS_ENSURE_ARG_POINTER(aStorage);
 
     *aStorage = nsnull;
 
-    nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
-    NS_ASSERTION(innerURI, "Failed to get innermost URI");
-    if (!innerURI)
-        return NS_ERROR_FAILURE;
-
-    nsCOMPtr<nsIDocShellTreeItem> topItem;
-    nsresult rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
+    nsresult rv;
+
+    nsCOMPtr<nsIScriptSecurityManager> securityManager =
+        do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // This is terrible hack and should go away along with this whole method.
+    nsCOMPtr<nsIPrincipal> principal;
+    rv = securityManager->GetCodebasePrincipal(aURI, getter_AddRefs(principal));
     if (NS_FAILED(rv))
         return rv;
 
-    if (!topItem)
-        return NS_ERROR_FAILURE;
-
-    nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
-    if (topDocShell != this)
-        return topDocShell->GetSessionStorageForURI(aURI, aCreate, aStorage);
-
-    nsCAutoString currentDomain;
-    rv = innerURI->GetAsciiHost(currentDomain);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (currentDomain.IsEmpty())
-        return NS_OK;
-
-    if (!mStorages.Get(currentDomain, aStorage) && aCreate) {
-        nsCOMPtr<nsIDOMStorageObsolete> newstorage =
-            do_CreateInstance("@mozilla.org/dom/storage;1");
-        if (!newstorage)
-            return NS_ERROR_OUT_OF_MEMORY;
-
-        nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(newstorage);
-        if (!pistorage)
-            return NS_ERROR_FAILURE;
-        pistorage->InitAsSessionStorage(aURI);
-
-        if (!mStorages.Put(currentDomain, newstorage))
-            return NS_ERROR_OUT_OF_MEMORY;
-
-        newstorage.swap(*aStorage);
-    }
-
-    return NS_OK;
+    return GetSessionStorageForPrincipal(principal, aCreate, aStorage);
 }
 
 nsresult
-nsDocShell::AddSessionStorage(const nsACString& aDomain,
-                              nsIDOMStorageObsolete* aStorage)
+nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal,
+                              nsIDOMStorage* aStorage)
 {
     NS_ENSURE_ARG_POINTER(aStorage);
 
-    if (aDomain.IsEmpty())
+    if (!aPrincipal)
         return NS_OK;
 
     nsCOMPtr<nsIDocShellTreeItem> topItem;
     nsresult rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
     if (NS_FAILED(rv))
         return rv;
 
     if (topItem) {
         nsCOMPtr<nsIDocShell> topDocShell = do_QueryInterface(topItem);
         if (topDocShell == this) {
+            nsXPIDLCString origin;
+            rv = aPrincipal->GetOrigin(getter_Copies(origin));
+            if (NS_FAILED(rv))
+                return rv;
+
+            if (origin.IsEmpty())
+                return NS_ERROR_FAILURE;
+
             // Do not replace an existing session storage.
-            if (mStorages.GetWeak(aDomain))
+            if (mStorages.GetWeak(origin))
                 return NS_ERROR_NOT_AVAILABLE;
 
-            if (!mStorages.Put(aDomain, aStorage))
+            if (!mStorages.Put(origin, aStorage))
                 return NS_ERROR_OUT_OF_MEMORY;
         }
         else {
-            return topDocShell->AddSessionStorage(aDomain, aStorage);
+            return topDocShell->AddSessionStorage(aPrincipal, aStorage);
         }
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult)
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -208,17 +208,16 @@ class nsDocShell : public nsDocLoader,
                    public nsIScriptGlobalObjectOwner,
                    public nsIRefreshURI,
                    public nsIWebProgressListener,
                    public nsIEditorDocShell,
                    public nsIWebPageDescriptor,
                    public nsIAuthPromptProvider,
                    public nsIObserver,
                    public nsILoadContext,
-                   public nsIDocShell_MOZILLA_1_9_1,
                    public nsIWebShellServices,
                    public nsILinkHandler,
                    public nsIClipboardCommands
 {
     friend class nsDSURIContentListener;
 
 public:
     // Object Management
@@ -243,17 +242,16 @@ public:
     NS_DECL_NSIWEBPROGRESSLISTENER
     NS_DECL_NSIREFRESHURI
     NS_DECL_NSICONTENTVIEWERCONTAINER
     NS_DECL_NSIEDITORDOCSHELL
     NS_DECL_NSIWEBPAGEDESCRIPTOR
     NS_DECL_NSIAUTHPROMPTPROVIDER
     NS_DECL_NSIOBSERVER
     NS_DECL_NSILOADCONTEXT
-    NS_DECL_NSIDOCSHELL_MOZILLA_1_9_1
     NS_DECL_NSICLIPBOARDCOMMANDS
     NS_DECL_NSIWEBSHELLSERVICES
 
     NS_IMETHOD Stop() {
         // Need this here because otherwise nsIWebNavigation::Stop
         // overrides the docloader's Stop()
         return nsDocLoader::Stop();
     }
@@ -601,17 +599,17 @@ protected:
     // InternalLoad).  This makes sure that we're not inside unload, or that if
     // we are it's still OK to load this URI.
     PRBool IsOKToLoadURI(nsIURI* aURI);
     
     void ReattachEditorToWindow(nsISHEntry *aSHEntry);
 
     nsresult GetSessionStorageForURI(nsIURI* aURI,
                                      PRBool create,
-                                     nsIDOMStorageObsolete** aStorage);
+                                     nsIDOMStorage** aStorage);
 
     // helpers for executing commands
     nsresult GetControllerForCommand(const char *inCommand,
                                      nsIController** outController);
     nsresult IsCommandEnabled(const char * inCommand, PRBool* outEnabled);
     nsresult DoCommand(const char * inCommand);
     nsresult EnsureCommandHandler();
     
@@ -625,20 +623,23 @@ protected:
         NS_DECL_NSIRUNNABLE
         RestorePresentationEvent(nsDocShell *ds) : mDocShell(ds) {}
         void Revoke() { mDocShell = nsnull; }
     private:
         nsDocShell *mDocShell;
     };
 
     // hash of session storages, keyed by domain
-    nsInterfaceHashtable<nsCStringHashKey, nsIDOMStorageObsolete> mStorages;
-    nsIntRect                  mBounds; // Dimensions of the docshell
+    nsInterfaceHashtable<nsCStringHashKey, nsIDOMStorage> mStorages;
+
+    // Dimensions of the docshell
+    nsIntRect                  mBounds;
     nsString                   mName;
     nsString                   mTitle;
+
     /**
      * Content-Type Hint of the most-recently initiated load. Used for
      * session history entries.
      */
     nsCString                  mContentTypeHint;
     nsIntPoint                 mDefaultScrollbarPref; // persistent across doc loads
 
     nsCOMPtr<nsISupportsArray> mRefreshURIList;
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -61,20 +61,20 @@ interface nsIDocShellLoadInfo;
 interface nsIDocumentCharsetInfo;
 interface nsIWebNavigation;
 interface nsISimpleEnumerator;
 interface nsIInputStream;
 interface nsIRequest;
 interface nsISHEntry;
 interface nsILayoutHistoryState;
 interface nsISecureBrowserUI;
-interface nsIDOMStorageObsolete;
+interface nsIDOMStorage;
 interface nsIPrincipal;
 
-[scriptable, uuid(f100ede8-fa36-4ed2-99b2-71a3b94f9a70)]
+[scriptable, uuid(3156f677-f444-4c3e-a47f-b47e568eafd6)]
 interface nsIDocShell : nsISupports
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -434,25 +434,35 @@ interface nsIDocShell : nsISupports
   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 uri the uri of the storage object to retrieve
    */
-  nsIDOMStorageObsolete getSessionStorageForURI(in nsIURI uri);
+  nsIDOMStorage getSessionStorageForURI(in nsIURI uri);
+
+  /*
+   * Retrieves the WebApps session storage object for the supplied principal.
+   *
+   * @param principal returns a storage for this principal
+   * @param create If true and a session storage object doesn't
+   *               already exist, a new one will be created.
+   */
+  nsIDOMStorage getSessionStorageForPrincipal(in nsIPrincipal principal,
+                                              in boolean create);
 
   /*
    * Add a WebApps session storage object to the docshell.
    *
-   * @param domain the domain the storage object is associated with
+   * @param principal the principal the storage object is associated with
    * @param storage the storage object to add
    */
-  void addSessionStorage(in ACString aDomain, in nsIDOMStorageObsolete storage);
+  void addSessionStorage(in nsIPrincipal principal, in nsIDOMStorage storage);
 
   /**
    * Gets the channel for the currently loaded document, if any. 
    * For a new document load, this will be the channel of the previous document
    * until after OnLocationChange fires.
    */
   readonly attribute nsIChannel currentDocumentChannel;
 
@@ -484,22 +494,8 @@ interface nsIDocShell : nsISupports
 
   /**
    * If true, this browser is not visible in the traditional sense, but
    * is actively being rendered to the screen (ex. painted on a canvas)
    * and should be treated accordingly.
    **/
   attribute boolean isOffScreenBrowser;    
 };
-
-[scriptable, uuid(460ba822-e664-4c38-9b08-98d2736473d7)]
-interface nsIDocShell_MOZILLA_1_9_1 : nsISupports
-{
-  /*
-   * Retrieves the WebApps session storage object for the supplied principal.
-   *
-   * @param principal returns a storage for this principal
-   * @param create If true and a session storage object doesn't
-   *               already exist, a new one will be created.
-   */
-  nsIDOMStorageObsolete getSessionStorageForPrincipal(in nsIPrincipal principal,
-                                                      in boolean create);
-};
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -6767,25 +6767,24 @@ nsGlobalWindow::GetDocument(nsIDOMDocume
   return rv;
 }
 
 //*****************************************************************************
 // nsGlobalWindow::nsIDOMStorageWindow
 //*****************************************************************************
 
 NS_IMETHODIMP
-nsGlobalWindow::GetSessionStorage(nsIDOMStorageObsolete ** aSessionStorage)
+nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
 {
   FORWARD_TO_INNER(GetSessionStorage, (aSessionStorage), NS_ERROR_UNEXPECTED);
 
   *aSessionStorage = nsnull;
 
   nsIPrincipal *principal = GetPrincipal();
-  nsCOMPtr<nsIDocShell_MOZILLA_1_9_1> docShell =
-    do_QueryInterface(GetDocShell());
+  nsIDocShell* docShell = GetDocShell();
 
   if (!principal || !docShell) {
     return NS_OK;
   }
 
   nsresult rv = docShell->GetSessionStorageForPrincipal(principal,
                                                         PR_TRUE,
                                                         aSessionStorage);
@@ -6981,20 +6980,19 @@ nsGlobalWindow::Observe(nsISupports* aSu
   }
 
   if (IsInnerWindow() && !nsCRT::strcmp(aTopic, "dom-storage-changed")) {
     nsIPrincipal *principal;
     nsresult rv;
 
     principal = GetPrincipal();
     if (!aData) {
-      nsCOMPtr<nsIDocShell_MOZILLA_1_9_1> docShell =
-        do_QueryInterface(GetDocShell());
+      nsIDocShell* docShell = GetDocShell();
       if (principal && docShell) {
-        nsCOMPtr<nsIDOMStorageObsolete> storage;
+        nsCOMPtr<nsIDOMStorage> storage;
         docShell->GetSessionStorageForPrincipal(principal,
                                                 PR_FALSE,
                                                 getter_AddRefs(storage));
 
         if (!SameCOMIdentity(storage, aSubject)) {
           // A sessionStorage object changed, but not our session storage
           // object.
           return NS_OK;
@@ -7078,17 +7076,17 @@ nsGlobalWindow::Observe(nsISupports* aSu
   return NS_ERROR_FAILURE;
 }
 
 static PLDHashOperator
 FirePendingStorageEvents(const nsAString& aKey, PRBool aData, void *userArg)
 {
   nsGlobalWindow *win = static_cast<nsGlobalWindow *>(userArg);
 
-  nsCOMPtr<nsIDOMStorageObsolete> storage;
+  nsCOMPtr<nsIDOMStorage> storage;
   win->GetSessionStorage(getter_AddRefs(storage));
 
   if (storage) {
     win->Observe(storage, "dom-storage-changed",
                  aKey.IsEmpty() ? nsnull : PromiseFlatString(aKey).get());
   }
 
   return PL_DHASH_NEXT;
--- a/dom/interfaces/storage/nsIDOMStorageWindow.idl
+++ b/dom/interfaces/storage/nsIDOMStorageWindow.idl
@@ -44,23 +44,23 @@
  *
  * Allows access to contextual storage areas.
  */
 
 interface nsIDOMStorageObsolete;
 interface nsIDOMStorage;
 interface nsIDOMStorageList;
 
-[scriptable, uuid(390FBF21-36F5-4516-B3F2-9D1232718C69)]
+[scriptable, uuid(A44581FE-DD9B-4fd7-9893-00C4AB43F12E)]
 interface nsIDOMStorageWindow : nsISupports
 {
   /**
    * Session storage for the current browsing context.
    */
-  readonly attribute nsIDOMStorageObsolete sessionStorage;
+  readonly attribute nsIDOMStorage sessionStorage;
 
   /**
    * Global storage, accessible by domain.
    */
   readonly attribute nsIDOMStorageList globalStorage;
 
   /**
    * Local storage for the current browsing context.
--- a/dom/interfaces/storage/nsPIDOMStorage.h
+++ b/dom/interfaces/storage/nsPIDOMStorage.h
@@ -43,31 +43,31 @@
 #include "nsISupports.h"
 #include "nsTArray.h"
 
 class nsIDOMStorageObsolete;
 class nsIURI;
 class nsIPrincipal;
 
 #define NS_PIDOMSTORAGE_IID                                 \
-  { 0x3231d539, 0xdd51, 0x4451,                             \
-      { 0xba, 0xdc, 0xf7, 0x72, 0xf5, 0xda, 0x1c, 0x8a } }
+  { 0x5ffbee8d, 0x9a86, 0x4a57,                           \
+      { 0x8c, 0x63, 0x76, 0x56, 0x18, 0x9c, 0xb2, 0xbc } }
 
 class nsPIDOMStorage : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMSTORAGE_IID)
 
+  virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal) = 0;
   virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal) = 0;
   virtual nsresult InitAsGlobalStorage(const nsACString &aDomainDemanded) = 0;
-  virtual nsresult InitAsSessionStorage(nsIURI* aURI) = 0;
 
-  virtual already_AddRefed<nsIDOMStorageObsolete> Clone() = 0;
+  virtual already_AddRefed<nsIDOMStorage> Clone() = 0;
 
   virtual nsTArray<nsString> *GetKeys() = 0;
 
-  virtual const nsCString &Domain() = 0;
+  virtual nsIPrincipal* Principal() = 0;
   virtual PRBool CanAccess(nsIPrincipal *aPrincipal) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMStorage, NS_PIDOMSTORAGE_IID)
 
 #endif // __nsPIDOMStorage_h_
--- a/dom/src/storage/nsDOMStorage.cpp
+++ b/dom/src/storage/nsDOMStorage.cpp
@@ -511,55 +511,101 @@ NS_INTERFACE_MAP_END
 
 NS_IMETHODIMP
 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 storage->QueryInterface(aIID, aResult);
+}
 
-  return NS_OK;
+NS_IMETHODIMP
+NS_NewDOMStorage2(nsISupports* aOuter, REFNSIID aIID, void** aResult)
+{
+  nsDOMStorage2* storage = new nsDOMStorage2();
+  if (!storage)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  return storage->QueryInterface(aIID, aResult);
 }
 
 nsDOMStorage::nsDOMStorage()
   : mUseDB(PR_FALSE)
   , mSessionOnly(PR_TRUE)
   , mLocalStorage(PR_FALSE)
   , mItemsCached(PR_FALSE)
 {
   mItems.Init(8);
   if (nsDOMStorageManager::gStorageManager)
     nsDOMStorageManager::gStorageManager->AddToStoragesHash(this);
 }
 
+static PLDHashOperator
+CopyStorageItems(nsSessionStorageEntry* aEntry, void* userArg)
+{
+  nsDOMStorage* newstorage = 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;
+}
+
 nsDOMStorage::nsDOMStorage(nsDOMStorage& aThat)
   : mUseDB(PR_FALSE) // Any clone is not using the database
   , mSessionOnly(PR_TRUE)
   , mLocalStorage(PR_FALSE) // Any clone is not a localStorage
   , mItemsCached(PR_FALSE)
   , mDomain(aThat.mDomain)
 #ifdef MOZ_STORAGE
   , mScopeDBKey(aThat.mScopeDBKey)
 #endif
 {
   mItems.Init(8);
+  aThat.mItems.EnumerateEntries(CopyStorageItems, this);
+
   if (nsDOMStorageManager::gStorageManager)
     nsDOMStorageManager::gStorageManager->AddToStoragesHash(this);
 }
 
 nsDOMStorage::~nsDOMStorage()
 {
   if (nsDOMStorageManager::gStorageManager)
     nsDOMStorageManager::gStorageManager->RemoveFromStoragesHash(this);
 }
 
 nsresult
+nsDOMStorage::InitAsSessionStorage(nsIPrincipal *aPrincipal)
+{
+  nsresult rv;
+
+  nsCOMPtr<nsIURI> uri;
+  rv = aPrincipal->GetURI(getter_AddRefs(uri));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIURI> innerUri = NS_GetInnermostURI(uri);
+  if (!innerUri)
+    return NS_ERROR_UNEXPECTED;
+
+  innerUri->GetAsciiHost(mDomain);
+
+#ifdef MOZ_STORAGE
+  mUseDB = PR_FALSE;
+  mScopeDBKey.Truncate();
+  mQuotaDomainDBKey.Truncate();
+#endif
+  return NS_OK;
+}
+
+nsresult
 nsDOMStorage::InitAsLocalStorage(nsIPrincipal *aPrincipal)
 {
   nsresult rv;
 
   nsCOMPtr<nsIURI> uri;
   rv = aPrincipal->GetURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -610,30 +656,16 @@ nsDOMStorage::InitAsGlobalStorage(const 
   if (!(mUseDB = !mScopeDBKey.IsEmpty()))
     mScopeDBKey.AppendLiteral(":");
 
   nsDOMStorageDBWrapper::CreateQuotaDomainDBKey(aDomainDemanded, PR_TRUE, mQuotaDomainDBKey);
 #endif
   return NS_OK;
 }
 
-nsresult
-nsDOMStorage::InitAsSessionStorage(nsIURI* aURI)
-{
-  nsCAutoString domain;
-  aURI->GetAsciiHost(domain);
-  mDomain = domain;
-#ifdef MOZ_STORAGE
-  mUseDB = PR_FALSE;
-  mScopeDBKey.Truncate();
-  mQuotaDomainDBKey.Truncate();
-#endif
-  return NS_OK;
-}
-
 //static
 PRBool
 nsDOMStorage::CanUseStorage(PRPackedBool* aSessionOnly)
 {
   // check if the calling domain can use storage. Downgrade to session
   // only if only session storage may be used.
   NS_ASSERTION(aSessionOnly, "null session flag");
   *aSessionOnly = PR_FALSE;
@@ -1235,51 +1267,21 @@ ClearStorageItem(nsSessionStorageEntry* 
 
 void
 nsDOMStorage::ClearAll()
 {
   mItems.EnumerateEntries(ClearStorageItem, nsnull);
   mItemsCached = PR_FALSE;
 }
 
-static PLDHashOperator
-CopyStorageItems(nsSessionStorageEntry* aEntry, void* userArg)
-{
-  nsDOMStorage* newstorage = 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<nsIDOMStorageObsolete>
+already_AddRefed<nsIDOMStorage>
 nsDOMStorage::Clone()
 {
-  if (UseDB()) {
-    NS_ERROR("Uh, don't clone a global or local storage object.");
-
-    return nsnull;
-  }
-
-  nsDOMStorage* storage = new nsDOMStorage(*this);
-  if (!storage)
-    return nsnull;
-
-  mItems.EnumerateEntries(CopyStorageItems, storage);
-
-  NS_ADDREF(storage);
-
-  if (nsDOMStorageManager::gStorageManager)
-    nsDOMStorageManager::gStorageManager->AddToStoragesHash(storage);
-
-  return storage;
+  NS_ASSERTION(PR_FALSE, "Old DOMStorage doesn't implement cloning");
+  return nsnull;
 }
 
 struct KeysArrayBuilderStruct
 {
   PRBool callerIsSecure;
   nsTArray<nsString> *keys;
 };
 
@@ -1304,20 +1306,20 @@ nsDOMStorage::GetKeys()
   keystruct.callerIsSecure = IsCallerSecure();
   keystruct.keys = new nsTArray<nsString>();
   if (keystruct.keys)
     mItems.EnumerateEntries(KeysArrayBuilder, &keystruct);
  
   return keystruct.keys;
 }
 
-const nsCString &
-nsDOMStorage::Domain()
+nsIPrincipal*
+nsDOMStorage::Principal()
 {
-  return mDomain;
+  return nsnull;
 }
 
 PRBool
 nsDOMStorage::CanAccessSystem(nsIPrincipal *aPrincipal)
 {
   if (!aPrincipal)
     return PR_TRUE;
 
@@ -1382,16 +1384,37 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOU
 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsDOMStorage2, nsIDOMStorage)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorage2)
   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
 
+nsDOMStorage2::nsDOMStorage2()
+{
+}
+
+nsDOMStorage2::nsDOMStorage2(nsDOMStorage2& aThat)
+{
+  mStorage = new nsDOMStorage(*aThat.mStorage.get());
+  mPrincipal = aThat.mPrincipal;
+}
+
+nsresult
+nsDOMStorage2::InitAsSessionStorage(nsIPrincipal *aPrincipal)
+{
+  mStorage = new nsDOMStorage();
+  if (!mStorage)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  mPrincipal = aPrincipal;
+  return mStorage->InitAsSessionStorage(aPrincipal);
+}
+
 nsresult
 nsDOMStorage2::InitAsLocalStorage(nsIPrincipal *aPrincipal)
 {
   mStorage = new nsDOMStorage();
   if (!mStorage)
     return NS_ERROR_OUT_OF_MEMORY;
 
   mPrincipal = aPrincipal;
@@ -1400,45 +1423,38 @@ nsDOMStorage2::InitAsLocalStorage(nsIPri
 
 nsresult
 nsDOMStorage2::InitAsGlobalStorage(const nsACString &aDomainDemanded)
 {
   NS_ASSERTION(PR_FALSE, "Should not initialize nsDOMStorage2 as global storage.");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-nsresult
-nsDOMStorage2::InitAsSessionStorage(nsIURI* aURI)
-{
-  mStorage = new nsDOMStorage();
-  if (!mStorage)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  return mStorage->InitAsSessionStorage(aURI);
-}
-
-already_AddRefed<nsIDOMStorageObsolete>
+already_AddRefed<nsIDOMStorage>
 nsDOMStorage2::Clone()
 {
-  // XXX: this will need to be fixed before sessionStorage is moved
-  // to nsIDOMStorage.
-  NS_ASSERTION(PR_FALSE, "Cannot clone nsDOMStorage2");
-  return nsnull;
+  nsDOMStorage2* storage = new nsDOMStorage2(*this);
+  if (!storage)
+    return nsnull;
+
+  NS_ADDREF(storage);
+
+  return storage;
 }
 
 nsTArray<nsString> *
 nsDOMStorage2::GetKeys()
 {
   return mStorage->GetKeys();
 }
 
-const nsCString &
-nsDOMStorage2::Domain()
+nsIPrincipal*
+nsDOMStorage2::Principal()
 {
-  return mStorage->Domain();
+  return mPrincipal;
 }
 
 PRBool
 nsDOMStorage2::CanAccess(nsIPrincipal *aPrincipal)
 {
   // Allow C++ callers to access the storage
   if (!aPrincipal)
     return PR_TRUE;
--- a/dom/src/storage/nsDOMStorage.h
+++ b/dom/src/storage/nsDOMStorage.h
@@ -135,22 +135,22 @@ public:
   // nsIDOMStorageObsolete
   NS_DECL_NSIDOMSTORAGEOBSOLETE
 
   // Helpers for implementing nsIDOMStorage
   nsresult GetItem(const nsAString& key, nsAString& aData);
   nsresult Clear();
 
   // nsPIDOMStorage
+  virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal);
   virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal);
   virtual nsresult InitAsGlobalStorage(const nsACString &aDomainDemanded);
-  virtual nsresult InitAsSessionStorage(nsIURI* aURI);
-  virtual already_AddRefed<nsIDOMStorageObsolete> Clone();
+  virtual already_AddRefed<nsIDOMStorage> Clone();
   virtual nsTArray<nsString> *GetKeys();
-  virtual const nsCString &Domain();
+  virtual nsIPrincipal* Principal();
   virtual PRBool CanAccess(nsIPrincipal *aPrincipal);
 
   // If true, the contents of the storage should be stored in the
   // database, otherwise this storage should act like a session
   // storage.
   // This call relies on mSessionOnly, and should only be used
   // after a CacheStoragePermissions() call.  See the comments
   // for mSessionOnly below.
@@ -262,25 +262,28 @@ public:
 class nsDOMStorage2 : public nsIDOMStorage,
                       public nsPIDOMStorage
 {
 public:
   // nsISupports
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMStorage2, nsIDOMStorage)
 
+  nsDOMStorage2(nsDOMStorage2& aThat);
+  nsDOMStorage2();
+
   NS_DECL_NSIDOMSTORAGE
 
   // nsPIDOMStorage
+  virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal);
   virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal);
   virtual nsresult InitAsGlobalStorage(const nsACString &aDomainDemanded);
-  virtual nsresult InitAsSessionStorage(nsIURI* aURI);
-  virtual already_AddRefed<nsIDOMStorageObsolete> Clone();
+  virtual already_AddRefed<nsIDOMStorage> Clone();
   virtual nsTArray<nsString> *GetKeys();
-  virtual const nsCString &Domain();
+  virtual nsIPrincipal* Principal();
   virtual PRBool CanAccess(nsIPrincipal *aPrincipal);
 
 private:
   // storages bound to an origin hold the principal to
   // make security checks against it
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   nsRefPtr<nsDOMStorage> mStorage;
@@ -427,12 +430,15 @@ public:
 
 protected:
   nsString mDomain;
 };
 
 NS_IMETHODIMP
 NS_NewDOMStorage(nsISupports* aOuter, REFNSIID aIID, void** aResult);
 
+NS_IMETHODIMP
+NS_NewDOMStorage2(nsISupports* aOuter, REFNSIID aIID, void** aResult);
+
 nsresult
 NS_NewDOMStorageList(nsIDOMStorageList** aResult);
 
 #endif /* nsDOMStorage_h___ */
--- a/dom/tests/mochitest/Makefile.in
+++ b/dom/tests/mochitest/Makefile.in
@@ -49,12 +49,13 @@ DIRS	+= \
 	dom-level2-html \
 	ajax \
 	bugs \
 	chrome \
 	general \
 	whatwg \
 	geolocation \
 	localstorage \
+	sessionstorage \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/sessionstorage/Makefile.in
@@ -0,0 +1,60 @@
+#
+# ***** 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 Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2007
+# 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@
+relativesrcdir	= dom/tests/mochitest/sessionstorage
+
+include $(DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES	= \
+    frameReplace.html \
+    frameEqual.html \
+    frameNotEqual.html \
+    test_sessionStorageBase.html \
+    test_sessionStorageClone.html \
+    test_sessionStorageReplace.html \
+    interOriginSlave.js \
+    interOriginTest.js \
+    $(NULL)
+
+libs::	$(_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/sessionstorage/frameEqual.html
@@ -0,0 +1,47 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>slave for sessionStorage test</title>
+
+<script type="text/javascript" src="interOriginSlave.js"></script>
+<script type="text/javascript">
+
+var currentStep = 2;
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    case 2:
+      is(sessionStorage.getItem("A"), "1", "A is 1 in the slave");
+      is(sessionStorage.getItem("B"), "2", "B is 2 in the slave");
+      is(sessionStorage.length, 2, "Num of items is 2");
+
+      sessionStorage.setItem("C", "3");
+      is(sessionStorage.getItem("C"), "3", "C is 3 in the slave");
+      is(sessionStorage.length, 3, "Num of items is 3");
+      break;
+
+    case 4:
+      is(sessionStorage.getItem("A"), "1", "A is 1 in the slave");
+      is(sessionStorage.getItem("B"), "2", "B is 2 in the slave");
+      is(sessionStorage.getItem("C"), "3", "C is 3 in the slave");
+      is(sessionStorage.length, 3, "Num of items is 3");
+      break;
+
+    case 6:
+      return finishTest();
+  }
+
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="postMsg('frame loaded');">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/sessionstorage/frameNotEqual.html
@@ -0,0 +1,43 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>slave for sessionStorage test</title>
+
+<script type="text/javascript" src="interOriginSlave.js"></script>
+<script type="text/javascript">
+
+var currentStep = 2;
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    case 2:
+      is(sessionStorage.length, 0, "Num of items is 0");
+
+      sessionStorage.setItem("C", "3");
+      is(sessionStorage.getItem("C"), "3", "C is 3 in the slave");
+      is(sessionStorage.length, 1, "Num of items is 1");
+      break;
+
+    case 4:
+      is(sessionStorage.getItem("C"), "3", "C is 3 in the slave");
+      is(sessionStorage.length, 1, "Num of items is 1");
+      break;
+
+    case 6:
+      return finishTest();
+  }
+
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="postMsg('frame loaded');">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/sessionstorage/frameReplace.html
@@ -0,0 +1,75 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>sessionStorage replace frame</title>
+
+<script type="text/javascript">
+
+var shell;
+
+function ok(a, message)
+{
+  if (!a)
+    shell.postMessage("FAILURE: " + message, "http://localhost:8888");
+  else
+    shell.postMessage(message, "http://localhost:8888");
+}
+
+function is(a, b, message)
+{
+  if (a != b)
+    shell.postMessage("FAILURE: " + message + ", expected "+b+" got "+a, "http://localhost:8888");
+  else
+    shell.postMessage(message + ", expected "+b+" got "+a, "http://localhost:8888");
+}
+
+function doTest()
+{
+  var query = location.search.substring(1);
+  var queries = query.split("&");
+
+  var action = queries[0];
+  shell = queries[1];
+  switch (shell)
+  {
+    case "frame":
+      shell = parent;
+      break;
+    case "window":
+      shell = opener;
+      break;
+  }
+
+  switch (action)
+  {
+    case "init":
+      sessionStorage.setItem("A", "1");
+      sessionStorage.setItem("B", "2");
+      sessionStorage.setItem("C", "3");
+      is(sessionStorage.getItem("A"), "1", "'A' is '1'");
+      is(sessionStorage.getItem("B"), "2", "'A' is '2'");
+      is(sessionStorage.getItem("C"), "3", "'A' is '3'");
+      break;
+
+    case "check":
+      is(sessionStorage.getItem("A"), null, "'A' is null");
+      is(sessionStorage.getItem("B"), null, "'A' is null");
+      is(sessionStorage.getItem("C"), null, "'A' is null");
+      break;
+
+    case "clean":
+      is(sessionStorage.getItem("A"), "1", "'A' is '1'");
+      is(sessionStorage.getItem("B"), "2", "'A' is '2'");
+      is(sessionStorage.getItem("C"), "3", "'A' is '3'");
+      sessionStorage.clear();
+      break;
+  }
+
+  shell.postMessage(action + "_done", "http://localhost:8888");
+}
+
+</script>
+
+</head>
+<body onload="doTest();">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/sessionstorage/interOriginSlave.js
@@ -0,0 +1,42 @@
+function postMsg(message)
+{
+  opener.postMessage(message, "http://localhost:8888");
+}
+
+window.addEventListener("message", onMessageReceived, false);
+
+function onMessageReceived(event)
+{
+  //alert("slave got event: "+event.data);
+  if (event.data == "step") {
+    if (doStep())
+      postMsg("perf");
+
+    return;
+  }
+
+  postMsg("Invalid message");
+}
+
+function ok(a, message)
+{
+  if (!a)
+    postMsg("FAILURE: " + message);
+  else
+    postMsg(message);
+}
+
+function is(a, b, message)
+{
+  if (a != b)
+    postMsg("FAILURE: " + message + ", expected "+b+" got "+a);
+  else
+    postMsg(message + ", expected "+b+" got "+a);
+}
+
+function finishTest()
+{
+  sessionStorage.clear();
+  postMsg("done");
+  return false;
+}
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/sessionstorage/interOriginTest.js
@@ -0,0 +1,44 @@
+var slaveLoadsPending = 1;
+
+var slaveOrigin = "";
+var slave = null;
+
+var failureRegExp = new RegExp("^FAILURE");
+const slavePath = "/tests/dom/tests/mochitest/sessionstorage/";
+
+window.addEventListener("message", onMessageReceived, false);
+
+function onMessageReceived(event)
+{
+  //alert("master got event: "+event.data);
+  switch (event.data)
+  {
+    // Indication of the frame onload event
+    case "frame loaded":
+      if (--slaveLoadsPending)
+        break;
+
+      // Just fall through...
+
+    // Indication of successfully finished step of a test
+    case "perf":
+      // We called doStep before the frame was load
+      if (event.data == "perf")
+        doStep();
+
+      slave.postMessage("step", slaveOrigin);
+      break;
+
+    // Indication of all test parts finish (from any of the frames)
+    case "done":
+      sessionStorage.clear();
+      slaveLoadsPending = 1;
+      doNextTest();
+      break;
+
+    // Any other message indicates error or succes message of a test
+    default:
+      SimpleTest.ok(!event.data.match(failureRegExp), event.data);
+      break;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/sessionstorage/test_sessionStorageBase.html
@@ -0,0 +1,156 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>sessionStorage basic test</title>
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<script type="text/javascript">
+
+var INDEX_SIZE_ERR = 1;
+
+function checkException(func, exc)
+{
+  var exceptionThrew = false;
+  try {
+    func();
+  }
+  catch (ex) {
+    exceptionThrew = true;
+    is(ex.code, exc, "Expected "+exc+" exception");
+  }
+  ok(exceptionThrew, "Exception "+exc+" threw");
+}
+
+function startTest()
+{
+  // Initially check the sessionStorage is empty
+  is(sessionStorage.length, 0, "The storage is empty [1]");
+  checkException(function() {sessionStorage.key(0);}, INDEX_SIZE_ERR);
+  checkException(function() {sessionStorage.key(-1);}, INDEX_SIZE_ERR);
+  checkException(function() {sessionStorage.key(1);}, INDEX_SIZE_ERR);
+  is(sessionStorage.getItem("nonexisting"), null, "Nonexisting item is null (getItem())");
+  is(sessionStorage["nonexisting"], null, "Nonexisting item is null (array access)");
+  is(sessionStorage.nonexisting, null, "Nonexisting item is null (property access)");
+  sessionStorage.removeItem("nonexisting"); // Just check there is no exception
+
+  is(typeof sessionStorage.getItem("nonexisting"), "object", "getItem('nonexisting') is object");
+  is(typeof sessionStorage["nonexisting"], "object", "['nonexisting'] is object");
+  is(typeof sessionStorage.nonexisting, "object", "nonexisting is object");
+  is(typeof sessionStorage.getItem("nonexisting2"), "object", "getItem('nonexisting2') is object");
+  is(typeof sessionStorage["nonexisting2"], "object", "['nonexisting2'] is object");
+  is(typeof sessionStorage.nonexisting2, "object", "nonexisting2 is object");
+
+  // add an empty-value key
+  sessionStorage.setItem("empty", "");
+  is(sessionStorage.getItem("empty"), "", "Empty value (getItem())");
+  is(sessionStorage["empty"], "", "Empty value (array access)");
+  is(sessionStorage.empty, "", "Empty value (property access)");
+  is(typeof sessionStorage.getItem("empty"), "string", "getItem('empty') is string");
+  is(typeof sessionStorage["empty"], "string", "['empty'] is string");
+  is(typeof sessionStorage.empty, "string", "empty is string");
+  sessionStorage.removeItem("empty");
+  is(sessionStorage.length, 0, "The storage has no keys");
+  is(sessionStorage.getItem("empty"), null, "empty item is null (getItem())");
+  is(sessionStorage["empty"], null, "empty item is null (array access)");
+  is(sessionStorage.empty, null, "empty item is null (property access)");
+  is(typeof sessionStorage.getItem("empty"), "object", "getItem('empty') is object");
+  is(typeof sessionStorage["empty"], "object", "['empty'] is object");
+  is(typeof sessionStorage.empty, "object", "empty is object");
+
+  // add one key, check it is there
+  sessionStorage.setItem("key1", "value1");
+  is(sessionStorage.length, 1, "The storage has one key-value pair");
+  is(sessionStorage.key(0), "key1");
+  checkException(function() {sessionStorage.key(-1);}, INDEX_SIZE_ERR);
+  checkException(function() {sessionStorage.key(1);}, INDEX_SIZE_ERR);
+
+  // check all access method give the correct result
+  // and are of the correct type
+  is(sessionStorage.getItem("key1"), "value1", "getItem('key1') == value1");
+  is(sessionStorage["key1"], "value1", "['key1'] == value1");
+  is(sessionStorage.key1, "value1", "key1 == value1");
+
+  is(typeof sessionStorage.getItem("key1"), "string", "getItem('key1') is string");
+  is(typeof sessionStorage["key1"], "string", "['key1'] is string");
+  is(typeof sessionStorage.key1, "string", "key1 is string");
+
+  // remove the previously added key and check the storage is empty
+  sessionStorage.removeItem("key1");
+  is(sessionStorage.length, 0, "The storage is empty [2]");
+  checkException(function() {sessionStorage.key(0);}, INDEX_SIZE_ERR);
+  is(sessionStorage.getItem("key1"), null, "\'key1\' removed");
+
+  is(typeof sessionStorage.getItem("key1"), "object", "getItem('key1') is object");
+  is(typeof sessionStorage["key1"], "object", "['key1'] is object");
+  is(typeof sessionStorage.key1, "object", "key1 is object");
+
+  // add one key, check it is there
+  sessionStorage.setItem("key1", "value1");
+  is(sessionStorage.length, 1, "The storage has one key-value pair");
+  is(sessionStorage.key(0), "key1");
+  is(sessionStorage.getItem("key1"), "value1");
+
+  // add a second key
+  sessionStorage.setItem("key2", "value2");
+  is(sessionStorage.length, 2, "The storage has two key-value pairs");
+  is(sessionStorage.key(1), "key1"); // This test might not be accurate because order is not preserved
+  is(sessionStorage.key(0), "key2");
+  is(sessionStorage.getItem("key1"), "value1");
+  is(sessionStorage.getItem("key2"), "value2");
+
+  // change the second key
+  sessionStorage.setItem("key2", "value2-2");
+  is(sessionStorage.length, 2, "The storage has two key-value pairs");
+  is(sessionStorage.key(1), "key1"); // After key value changes the order must be preserved
+  is(sessionStorage.key(0), "key2");
+  checkException(function() {sessionStorage.key(-1);}, INDEX_SIZE_ERR);
+  checkException(function() {sessionStorage.key(2);}, INDEX_SIZE_ERR);
+  is(sessionStorage.getItem("key1"), "value1");
+  is(sessionStorage.getItem("key2"), "value2-2");
+
+  // change the first key
+  sessionStorage.setItem("key1", "value1-2");
+  is(sessionStorage.length, 2, "The storage has two key-value pairs");
+  is(sessionStorage.key(1), "key1"); // After key value changes the order must be preserved
+  is(sessionStorage.key(0), "key2");
+  checkException(function() {sessionStorage.key(-1);}, INDEX_SIZE_ERR);
+  checkException(function() {sessionStorage.key(2);}, INDEX_SIZE_ERR);
+  is(sessionStorage.getItem("key1"), "value1-2");
+  is(sessionStorage.getItem("key2"), "value2-2");
+
+  // remove the second key
+  sessionStorage.removeItem("key2");
+  is(sessionStorage.length, 1, "The storage has one key-value pair");
+  is(sessionStorage.key(0), "key1");
+  checkException(function() {sessionStorage.key(-1);}, INDEX_SIZE_ERR);
+  checkException(function() {sessionStorage.key(1);}, INDEX_SIZE_ERR);
+  is(sessionStorage.getItem("key1"), "value1-2");
+
+  // Clear the storage
+  sessionStorage.clear();
+  is(sessionStorage.length, 0, "The storage is empty [3]");
+  checkException(function() {sessionStorage.key(0);}, INDEX_SIZE_ERR); // this is unspecified!
+  checkException(function() {sessionStorage.key(-1);}, INDEX_SIZE_ERR);
+  checkException(function() {sessionStorage.key(1);}, INDEX_SIZE_ERR);
+  is(sessionStorage.getItem("nonexisting"), null, "Nonexisting item is null");
+  is(sessionStorage.getItem("key1"), null, "key1 removed");
+  is(sessionStorage.getItem("key2"), null, "key2 removed");
+  sessionStorage.removeItem("nonexisting"); // Just check there is no exception
+  sessionStorage.removeItem("key1"); // Just check there is no exception
+  sessionStorage.removeItem("key2"); // Just check there is no exception
+
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body onload="startTest();">
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/sessionstorage/test_sessionStorageClone.html
@@ -0,0 +1,95 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>sessionStorage clone equal origins</title>
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="interOriginTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<script type="text/javascript">
+
+var currentTest = 1;
+var currentStep = 1;
+
+/*
+window.addEventListener("storage", onStorageEvent, false);
+
+function onStorageEvent(event)
+{
+}
+*/
+
+function doNextTest()
+{
+  // We must perform the first step of the test
+  // to prepare the land.
+  currentStep = 1;
+  doStep();
+
+  switch (currentTest)
+  {
+    case 1:
+      // Open a window from the same origin and check data
+      // are copied but not further modified on our side
+      slaveOrigin = "http://localhost:8888";
+      slave = window.open(slaveOrigin + slavePath + "frameEqual.html");
+      break;
+
+    case 2:
+      slave.close();
+      // Open a window from a different origin and check data
+      // are NOT copied and not modified on our side
+      slaveOrigin = "http://example.com";
+      slave = window.open(slaveOrigin + slavePath + "frameNotEqual.html");
+      break;
+
+    case 3:
+      slave.close();
+      sessionStorage.clear();
+      SimpleTest.finish();
+      break;
+  }
+
+  ++currentTest;
+}
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    case 1:
+      sessionStorage.setItem("A", "1");
+      sessionStorage.setItem("B", "2");
+      is(sessionStorage.getItem("A"), "1", "A is 1 in the master");
+      is(sessionStorage.getItem("B"), "2", "B is 2 in the master");
+      is(sessionStorage.length, 2, "Num of items is 2");
+      break;
+
+    case 3:
+      is(sessionStorage.getItem("A"), "1", "A is 1 in the master");
+      is(sessionStorage.getItem("B"), "2", "B is 2 in the master");
+      is(sessionStorage.getItem("C"), null, "C is null in the master");
+      is(sessionStorage.length, 2, "Num of items is 2");
+
+      sessionStorage.setItem("C", "4");
+      is(sessionStorage.getItem("C"), "4", "C is 4 in the master");
+      is(sessionStorage.length, 3, "Num of items is 3");
+      break;
+  }
+
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body onload="doNextTest();">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/sessionstorage/test_sessionStorageReplace.html
@@ -0,0 +1,79 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>sessionStorage replace test</title>
+
+<!--
+  This test checks that sessionStorage object doesn't leak
+  in a window that changes its location. We do this by switching
+  frame location inside of this window and then by changing location
+  of a top level window.
+-->
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<script type="text/javascript">
+
+var shell;
+var shellType;
+var failureRegExp = new RegExp("^FAILURE");
+
+window.addEventListener("message", onMessageReceived, false);
+
+function onMessageReceived(event)
+{
+  //alert("onMessageReceived "+event.data);
+  switch (event.data)
+  {
+    case "init_done":
+      // This is frame with different origin in the same browsing context
+      // as the first frame adding data to sessionStorage of the first origin.
+      shell.location = "http://example.com/tests/dom/tests/mochitest/sessionStorage/frameReplace.html?check&" + shellType;
+      break;
+
+    case "check_done":
+      // Recheck and clean the sessionStorage of the first origin.
+      shell.location = "http://example.org:80/tests/dom/tests/mochitest/sessionStorage/frameReplace.html?clean&" + shellType;
+      break;
+
+    case "clean_done":
+      switch (shellType)
+      {
+        case "frame":
+          // We finished testing in a frame
+          // proceed with test in a separate window
+          shellType = "window";
+          shell = window.open("http://example.org/tests/dom/tests/mochitest/sessionStorage/frameReplace.html?init&" + shellType);
+          break;
+
+        case "window":
+          shell.close();
+          window.setTimeout(function() {SimpleTest.finish();}, 0);
+          break;
+      }
+      break;
+
+    default:
+      SimpleTest.ok(!event.data.match(failureRegExp), event.data);
+      break;
+  }
+}
+
+function startTest()
+{
+  shellType = "frame";
+  shell = frame;
+  shell.location = "http://example.org/tests/dom/tests/mochitest/sessionStorage/frameReplace.html?init&" + shellType;
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body onload="startTest();">
+  <iframe src="" name="frame"></iframe>
+</body>
+</html>
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -81,16 +81,17 @@
 #include "nsIMarkupDocumentViewer.h"
 #include "nsIContentViewer.h"
 #include "nsIDocumentViewer.h"
 #include "nsIWindowProvider.h"
 #include "nsIMutableArray.h"
 #include "nsISupportsArray.h"
 #include "nsIDeviceContext.h"
 #include "nsIDOMStorageObsolete.h"
+#include "nsIDOMStorage.h"
 #include "nsPIDOMStorage.h"
 #include "nsIWidget.h"
 
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 
 #include "jsinterp.h" // for js_AllocStack() and js_FreeStack()
 
@@ -926,30 +927,30 @@ nsWindowWatcher::OpenWindowJSInternal(ns
 
     newDocShell->LoadURI(uriToLoad, loadInfo,
       windowIsNew ? nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD :
                     nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE);
   }
 
   // Copy the current session storage for the current domain.
   nsCOMPtr<nsPIDOMWindow> piWindow = do_QueryInterface(aParent);
-  nsCOMPtr<nsIDocShell_MOZILLA_1_9_1> parentDocShell;
+  nsIDocShell* parentDocShell = nsnull;
   if (piWindow)
-    parentDocShell = do_QueryInterface(piWindow->GetDocShell());
+    parentDocShell = piWindow->GetDocShell();
 
   if (subjectPrincipal && parentDocShell) {
-    nsCOMPtr<nsIDOMStorageObsolete> storage;
+    nsCOMPtr<nsIDOMStorage> storage;
     parentDocShell->GetSessionStorageForPrincipal(subjectPrincipal, PR_FALSE,
                                                   getter_AddRefs(storage));
     nsCOMPtr<nsPIDOMStorage> piStorage =
       do_QueryInterface(storage);
     if (piStorage){
       storage = piStorage->Clone();
       newDocShell->AddSessionStorage(
-        piStorage->Domain(),
+        piStorage->Principal(),
         storage);
     }
   }
 
   if (isNewToplevelWindow)
     SizeOpenedDocShellItem(newDocShellItem, aParent, sizeSpec);
 
   // XXXbz isn't windowIsModal always true when windowIsModalContentDialog?
--- a/layout/build/nsLayoutCID.h
+++ b/layout/build/nsLayoutCID.h
@@ -213,16 +213,20 @@
 // {a35d1cd4-c505-4d2d-a0f9-aef00b7ce5a5}
 #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 } }
 
+// {27AECC62-7777-428e-B34C-5973A47B8298}
+#define NS_DOMSTORAGE2_CID \
+{ 0x27aecc62, 0x7777, 0x428e, { 0xb3, 0x4c, 0x59, 0x73, 0xa4, 0x7b, 0x82, 0x98 } }
+
 // {b88a4712-eb52-4c10-9b85-bf5894b510f0}
 #define NS_DOMSTORAGEMANAGER_CID               \
 { 0xb88a4712, 0xeb52, 0x4c10, { 0x9b, 0x85, 0xbf, 0x58, 0x94, 0xb5, 0x10, 0xf0 } }
 
 // {14632191-AC21-4BDF-83E7-2363AD17E838}
 #define NS_XULPOPUPMANAGER_CID \
 { 0x14632191, 0xac21, 0x4bdf, { 0x83, 0xe7, 0x23, 0x63, 0xad, 0x17, 0xe8, 0x38 } }
 
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -1397,16 +1397,21 @@ static const nsModuleComponentInfo gComp
     NS_DOMPARSER_CONTRACTID,
     nsDOMParserConstructor },
 
   { "DOM Storage",
     NS_DOMSTORAGE_CID,
     "@mozilla.org/dom/storage;1",
     NS_NewDOMStorage },
 
+  { "DOM Storage 2",
+    NS_DOMSTORAGE2_CID,
+    "@mozilla.org/dom/storage;2",
+    NS_NewDOMStorage2 },
+
   { "DOM Storage Manager",
     NS_DOMSTORAGEMANAGER_CID,
     "@mozilla.org/dom/storagemanager;1",
     nsDOMStorageManagerConstructor },
 
   { "DOM JSON",
     NS_DOMJSON_CID,
     "@mozilla.org/dom/json;1",