diff --git a/browser/components/sessionstore/src/nsSessionStore.js b/browser/components/sessionstore/src/nsSessionStore.js
index d13431a355b2..9b8650150f96 100644
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -1397,7 +1397,7 @@ SessionStoreService.prototype = {
         // sessionStorage object for the page earlier than the page really
         // requires it. It was causing problems while accessing a storage when
         // a page later changed its domain.
-        storage = aDocShell.getSessionStorageForPrincipal(principal, false);
+        storage = aDocShell.getSessionStorageForPrincipal(principal, "", false);
         if (storage)
           storageItemCount = storage.length;
       }
@@ -2254,7 +2254,7 @@ SessionStoreService.prototype = {
   _deserializeSessionStorage: function sss_deserializeSessionStorage(aStorageData, aDocShell) {
     for (let url in aStorageData) {
       let uri = IOSvc.newURI(url, null, null);
-      let storage = aDocShell.getSessionStorageForURI(uri);
+      let storage = aDocShell.getSessionStorageForURI(uri, "");
       for (let key in aStorageData[url]) {
         try {
           storage.setItem(key, aStorageData[url][key]);
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index 3251571089f8..455e94815456 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -51,6 +51,7 @@
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
+#include "nsIDOM3Document.h"
 #include "nsIDOMNSDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMStorageObsolete.h"
@@ -2144,6 +2145,7 @@ GetPrincipalDomain(nsIPrincipal* aPrincipal, nsACString& aDomain)
 
 NS_IMETHODIMP
 nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
+                                          const nsAString& aDocumentURI,
                                           PRBool aCreate,
                                           nsIDOMStorage** aStorage)
 {
@@ -2165,7 +2167,9 @@ nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
 
     nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
     if (topDocShell != this)
-        return topDocShell->GetSessionStorageForPrincipal(aPrincipal, aCreate,
+        return topDocShell->GetSessionStorageForPrincipal(aPrincipal,
+                                                          aDocumentURI,
+                                                          aCreate,
                                                           aStorage);
 
     nsCAutoString currentDomain;
@@ -2185,7 +2189,7 @@ nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
         nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(newstorage);
         if (!pistorage)
             return NS_ERROR_FAILURE;
-        rv = pistorage->InitAsSessionStorage(aPrincipal);
+        rv = pistorage->InitAsSessionStorage(aPrincipal, aDocumentURI);
         if (NS_FAILED(rv))
             return rv;
 
@@ -2193,19 +2197,57 @@ nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
             return NS_ERROR_OUT_OF_MEMORY;
 
         newstorage.swap(*aStorage);
-        return NS_OK;
+#if defined(PR_LOGGING) && defined(DEBUG)
+        PR_LOG(gDocShellLog, PR_LOG_DEBUG,
+               ("nsDocShell[%p]: created a new sessionStorage %p",
+                this, *aStorage));
+#endif
+    }
+    else if (*aStorage) {
+      nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(*aStorage);
+      if (piStorage) {
+          PRBool canAccess = piStorage->CanAccess(aPrincipal);
+          NS_ASSERTION(canAccess,
+                       "GetSessionStorageForPrincipal got a storage "
+                       "that could not be accessed!");
+          if (!canAccess) {
+              NS_RELEASE(*aStorage);
+              return NS_ERROR_DOM_SECURITY_ERR;
+          }
+      }
+
+#if defined(PR_LOGGING) && defined(DEBUG)
+      PR_LOG(gDocShellLog, PR_LOG_DEBUG,
+             ("nsDocShell[%p]: returns existing sessionStorage %p",
+              this, *aStorage));
+#endif
     }
 
-    nsCOMPtr<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) {
-            NS_RELEASE(*aStorage);
-            return NS_ERROR_DOM_SECURITY_ERR;
-        }
+    if (aCreate) {
+        // We are asked to create a new storage object. This indicates
+        // that a new windows wants it. At this moment we "fork" the existing
+        // storage object (what it means is described in the paragraph bellow).
+        // We must create a single object per a single window to distinguish
+        // a window originating oparations on the storage object to succesfully
+        // prevent dispatch of a storage event to this same window that ivoked
+        // a change in its storage. We also do this to correctly fill
+        // documentURI property in the storage event.
+        //
+        // The difference between clone and fork is that clone creates
+        // a completelly new and independent storage, but fork only creates
+        // a new object wrapping the storage implementation and data and
+        // the forked storage then behaves completelly the same way as
+        // the storage it has been forked of, all such forked storage objects
+        // shares their state and data and change on one such object affects
+        // all others the same way.
+        nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(*aStorage);
+        nsCOMPtr<nsIDOMStorage> fork = piStorage->Fork(aDocumentURI);
+#if defined(PR_LOGGING) && defined(DEBUG)
+        PR_LOG(gDocShellLog, PR_LOG_DEBUG,
+               ("nsDocShell[%p]: forked sessionStorage %p to %p",
+                this, *aStorage, fork.get()));
+#endif
+        fork.swap(*aStorage);
     }
 
     return NS_OK;
@@ -2213,13 +2255,15 @@ nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
 
 NS_IMETHODIMP
 nsDocShell::GetSessionStorageForURI(nsIURI* aURI,
+                                    const nsAString& aDocumentURI,
                                     nsIDOMStorage** aStorage)
 {
-    return GetSessionStorageForURI(aURI, PR_TRUE, aStorage);
+    return GetSessionStorageForURI(aURI, aDocumentURI, PR_TRUE, aStorage);
 }
 
 nsresult
 nsDocShell::GetSessionStorageForURI(nsIURI* aURI,
+                                    const nsSubstring& aDocumentURI,
                                     PRBool aCreate,
                                     nsIDOMStorage** aStorage)
 {
@@ -2240,7 +2284,7 @@ nsDocShell::GetSessionStorageForURI(nsIURI* aURI,
     if (NS_FAILED(rv))
         return rv;
 
-    return GetSessionStorageForPrincipal(principal, aCreate, aStorage);
+    return GetSessionStorageForPrincipal(principal, aDocumentURI, aCreate, aStorage);
 }
 
 nsresult
@@ -2272,6 +2316,11 @@ nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal,
             if (mStorages.GetWeak(currentDomain))
                 return NS_ERROR_NOT_AVAILABLE;
 
+#if defined(PR_LOGGING) && defined(DEBUG)
+            PR_LOG(gDocShellLog, PR_LOG_DEBUG,
+                   ("nsDocShell[%p]: was added a sessionStorage %p",
+                    this, aStorage));
+#endif
             if (!mStorages.Put(currentDomain, aStorage))
                 return NS_ERROR_OUT_OF_MEMORY;
         }
diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h
index dcce9dd0be23..68b95ffe9449 100644
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -605,6 +605,7 @@ protected:
     void ReattachEditorToWindow(nsISHEntry *aSHEntry);
 
     nsresult GetSessionStorageForURI(nsIURI* aURI,
+                                     const nsSubstring& aDocumentURI,
                                      PRBool create,
                                      nsIDOMStorage** aStorage);
 
diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl
index 26634a9d603b..0e1b8aef4362 100644
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -70,7 +70,7 @@ interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 
-[scriptable, uuid(1067ee1a-08e8-455d-9b12-19eeeee56b8e)]
+[scriptable, uuid(c95eaff1-14e6-4db1-a806-46be97d5a9b6)]
 interface nsIDocShell : nsISupports
 {
   /**
@@ -430,17 +430,23 @@ interface nsIDocShell : nsISupports
    * If it doesn't already exist, a new one will be created.
    *
    * @param uri the uri of the storage object to retrieve
+   * @param documentURI new storage will be created with reference to this
+   *                    document.documentURI that will appear in storage event
    */
-  nsIDOMStorage getSessionStorageForURI(in nsIURI uri);
+  nsIDOMStorage getSessionStorageForURI(in nsIURI uri,
+                                        in DOMString documentURI);
 
   /*
    * Retrieves the WebApps session storage object for the supplied principal.
    *
    * @param principal returns a storage for this principal
+   * @param documentURI new storage will be created with reference to this
+   *                    document.documentURI that will appear in storage event
    * @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 DOMString documentURI,
                                               in boolean create);
 
   /*
diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp
index 42fcbf65098f..160ed477f6f2 100644
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -1202,6 +1202,8 @@ static nsDOMClassInfoData sClassInfoData[] = {
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(StorageEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(StorageEventObsolete, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DOMParser, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -3487,6 +3489,10 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageEvent)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(StorageEventObsolete, nsIDOMStorageEventObsolete)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageEventObsolete)
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(DOMParser, nsIDOMParser)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMParser)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMParserJS)
diff --git a/dom/base/nsDOMClassInfoID.h b/dom/base/nsDOMClassInfoID.h
index 124abebcabb0..f5b23f5baa2f 100644
--- a/dom/base/nsDOMClassInfoID.h
+++ b/dom/base/nsDOMClassInfoID.h
@@ -383,6 +383,7 @@ enum nsDOMClassInfoID {
   eDOMClassInfo_StorageList_id,
   eDOMClassInfo_StorageItem_id,
   eDOMClassInfo_StorageEvent_id,
+  eDOMClassInfo_StorageEventObsolete_id,
 
   // DOMParser, XMLSerializer
   eDOMClassInfo_DOMParser_id,
diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
index 8bfc3cbca9ae..492217806d92 100644
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -101,6 +101,7 @@
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMCrypto.h"
 #include "nsIDOMDocument.h"
+#include "nsIDOM3Document.h"
 #include "nsIDOMNSDocument.h"
 #include "nsIDOMDocumentView.h"
 #include "nsIDOMElement.h"
@@ -113,6 +114,7 @@
 #include "nsIDOMPopupBlockedEvent.h"
 #include "nsIDOMOfflineResourceList.h"
 #include "nsIDOMGeoGeolocation.h"
+#include "nsPIDOMStorage.h"
 #include "nsDOMString.h"
 #include "nsIEmbeddingSiteWindow2.h"
 #include "nsThreadUtils.h"
@@ -653,9 +655,9 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
     mTimeoutPublicIdCounter(1),
     mTimeoutFiringDepth(0),
     mJSObject(nsnull),
-    mPendingStorageEvents(nsnull),
     mTimeoutsSuspendDepth(0),
-    mFocusMethod(0)
+    mFocusMethod(0),
+    mPendingStorageEventsObsolete(nsnull)
 #ifdef DEBUG
     , mSetOpenerWindowCalled(PR_FALSE)
 #endif
@@ -687,6 +689,7 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
 
         // Watch for dom-storage-changed so we can fire storage
         // events. Use a strong reference.
+        os->AddObserver(mObserver, "dom-storage2-changed", PR_FALSE);
         os->AddObserver(mObserver, "dom-storage-changed", PR_FALSE);
       }
     }
@@ -773,6 +776,7 @@ nsGlobalWindow::~nsGlobalWindow()
       do_GetService("@mozilla.org/observer-service;1");
     if (os) {
       os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
+      os->RemoveObserver(mObserver, "dom-storage2-changed");
       os->RemoveObserver(mObserver, "dom-storage-changed");
     }
 
@@ -826,7 +830,7 @@ nsGlobalWindow::~nsGlobalWindow()
   nsCycleCollector_DEBUG_wasFreed(static_cast<nsIScriptGlobalObject*>(this));
 #endif
 
-  delete mPendingStorageEvents;
+  delete mPendingStorageEventsObsolete;
 
   nsLayoutStatics::Release();
 }
@@ -1730,6 +1734,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
   mDocument = do_QueryInterface(aDocument);
   mDoc = aDocument;
   mLocalStorage = nsnull;
+  mSessionStorage = nsnull;
 
 #ifdef DEBUG
   mLastOpenedURI = aDocument->GetDocumentURI();
@@ -7082,8 +7087,6 @@ nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
 {
   FORWARD_TO_INNER(GetSessionStorage, (aSessionStorage), NS_ERROR_UNEXPECTED);
 
-  *aSessionStorage = nsnull;
-
   nsIPrincipal *principal = GetPrincipal();
   nsIDocShell* docShell = GetDocShell();
 
@@ -7091,15 +7094,56 @@ nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
     return NS_OK;
   }
 
-  nsresult rv = docShell->GetSessionStorageForPrincipal(principal,
-                                                        PR_TRUE,
-                                                        aSessionStorage);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!*aSessionStorage) {
-    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+  if (mSessionStorage) {
+#ifdef PR_LOGGING
+    if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
+      PR_LogPrint("nsGlobalWindow %p has %p sessionStorage", this, mSessionStorage.get());
+    }
+#endif
+    nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(mSessionStorage);
+    if (piStorage) {
+      PRBool canAccess = piStorage->CanAccess(principal);
+      NS_ASSERTION(canAccess,
+                   "window %x owned sessionStorage "
+                   "that could not be accessed!");
+      if (!canAccess) {
+          mSessionStorage = nsnull;
+      }
+    }
   }
 
+  if (!mSessionStorage) {
+    *aSessionStorage = nsnull;
+
+    nsString documentURI;
+    nsCOMPtr<nsIDOM3Document> document3 = do_QueryInterface(mDoc);
+    if (document3)
+        document3->GetDocumentURI(documentURI);
+
+    nsresult rv = docShell->GetSessionStorageForPrincipal(principal,
+                                                          documentURI,
+                                                          PR_TRUE,
+                                                          getter_AddRefs(mSessionStorage));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+#ifdef PR_LOGGING
+    if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
+      PR_LogPrint("nsGlobalWindow %p tried to get a new sessionStorage %p", this, mSessionStorage.get());
+    }
+#endif
+
+    if (!mSessionStorage) {
+      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+    }
+  }
+
+#ifdef PR_LOGGING
+    if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
+      PR_LogPrint("nsGlobalWindow %p returns %p sessionStorage", this, mSessionStorage.get());
+    }
+#endif
+
+  NS_ADDREF(*aSessionStorage = mSessionStorage);
   return NS_OK;
 }
 
@@ -7147,7 +7191,14 @@ nsGlobalWindow::GetLocalStorage(nsIDOMStorage ** aLocalStorage)
       do_GetService("@mozilla.org/dom/storagemanager;1", &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = storageManager->GetLocalStorageForPrincipal(principal, getter_AddRefs(mLocalStorage));
+    nsString documentURI;
+    nsCOMPtr<nsIDOM3Document> document3 = do_QueryInterface(mDoc);
+    if (document3)
+        document3->GetDocumentURI(documentURI);
+
+    rv = storageManager->GetLocalStorageForPrincipal(principal,
+                                                     documentURI,
+                                                     getter_AddRefs(mLocalStorage));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
@@ -7290,21 +7341,7 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
     nsresult rv;
 
     principal = GetPrincipal();
-    if (!aData) {
-      nsIDocShell* docShell = GetDocShell();
-      if (principal && docShell) {
-        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;
-        }
-      }
-    } else if (principal) {
+    if (principal) {
       // A global storage object changed, check to see if it's one
       // this window can access.
 
@@ -7338,23 +7375,23 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
       // store the domain in which the change happened and fire the
       // events if we're ever thawed.
 
-      if (!mPendingStorageEvents) {
-        mPendingStorageEvents = new nsDataHashtable<nsStringHashKey, PRBool>;
-        NS_ENSURE_TRUE(mPendingStorageEvents, NS_ERROR_OUT_OF_MEMORY);
+      if (!mPendingStorageEventsObsolete) {
+        mPendingStorageEventsObsolete = new nsDataHashtable<nsStringHashKey, PRBool>;
+        NS_ENSURE_TRUE(mPendingStorageEventsObsolete, NS_ERROR_OUT_OF_MEMORY);
 
-        rv = mPendingStorageEvents->Init();
+        rv = mPendingStorageEventsObsolete->Init();
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
-      mPendingStorageEvents->Put(domain, PR_TRUE);
+      mPendingStorageEventsObsolete->Put(domain, PR_TRUE);
 
       return NS_OK;
     }
 
-    nsRefPtr<nsDOMStorageEvent> event = new nsDOMStorageEvent(domain);
+    nsRefPtr<nsDOMStorageEventObsolete> event = new nsDOMStorageEventObsolete();
     NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
 
-    rv = event->Init();
+    rv = event->InitStorageEvent(NS_LITERAL_STRING("storage"), PR_FALSE, PR_FALSE, domain);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIDOMHTMLDocument> htmlDoc(do_QueryInterface(mDocument));
@@ -7373,7 +7410,99 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
     }
 
     PRBool defaultActionEnabled;
-    target->DispatchEvent((nsIDOMStorageEvent *)event, &defaultActionEnabled);
+    target->DispatchEvent((nsIDOMStorageEventObsolete *)event, &defaultActionEnabled);
+
+    return NS_OK;
+  }
+
+  if (IsInnerWindow() && !nsCRT::strcmp(aTopic, "dom-storage2-changed")) {
+    nsIPrincipal *principal;
+    nsresult rv;
+
+    nsCOMPtr<nsIDOMStorageEvent> event = do_QueryInterface(aSubject, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIDOMStorage> changingStorage;
+    rv = event->GetStorageArea(getter_AddRefs(changingStorage));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(changingStorage);
+    nsPIDOMStorage::nsDOMStorageType storageType = pistorage->StorageType();
+
+    principal = GetPrincipal();
+    switch (storageType)
+    {
+    case nsPIDOMStorage::SessionStorage:
+    {
+      if (SameCOMIdentity(mSessionStorage, changingStorage)) {
+        // Do not fire any events for the same storage object, it's not shared
+        // among windows, see nsGlobalWindow::GetSessionStoarge()
+        return NS_OK;
+      }
+
+      nsCOMPtr<nsIDOMStorage> storage = mSessionStorage;
+      if (!storage) {
+        nsIDocShell* docShell = GetDocShell();
+        if (principal && docShell) {
+          // No need to pass documentURI here, it's only needed when we want
+          // to create a new storage, the third paramater would be PR_TRUE
+          docShell->GetSessionStorageForPrincipal(principal,
+                                                  EmptyString(),
+                                                  PR_FALSE,
+                                                  getter_AddRefs(storage));
+        }
+      }
+
+      if (!pistorage->IsForkOf(storage)) {
+        // This storage event is coming from a different doc shell,
+        // i.e. it is a clone, ignore this event.
+        return NS_OK;
+      }
+
+#ifdef PR_LOGGING
+      if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
+        PR_LogPrint("nsGlobalWindow %p with sessionStorage %p passing event from %p", this, mSessionStorage.get(), pistorage.get());
+      }
+#endif
+
+      break;
+    }
+    case nsPIDOMStorage::LocalStorage:
+    {
+      if (SameCOMIdentity(mLocalStorage, changingStorage)) {
+        // Do not fire any events for the same storage object, it's not shared
+        // among windows, see nsGlobalWindow::GetLocalStoarge()
+        return NS_OK;
+      }
+
+      // Allow event fire only for the same principal storages
+      // XXX We have to use EqualsIgnoreDomain after bug 495337 lands
+      nsIPrincipal *storagePrincipal = pistorage->Principal();
+      PRBool equals;
+
+      rv = storagePrincipal->Equals(principal, &equals);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      if (!equals)
+        return NS_OK;
+
+      break;
+    }
+    default:
+      return NS_OK;
+    }
+
+    if (IsFrozen()) {
+      // 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.
+
+      mPendingStorageEvents.AppendElement(event);
+      return NS_OK;
+    }
+
+    PRBool defaultActionEnabled;
+    DispatchEvent((nsIDOMStorageEvent *)event, &defaultActionEnabled);
 
     return NS_OK;
   }
@@ -7403,12 +7532,16 @@ nsGlobalWindow::FireDelayedDOMEvents()
 {
   FORWARD_TO_INNER(FireDelayedDOMEvents, (), NS_ERROR_UNEXPECTED);
 
-  if (mPendingStorageEvents) {
-    // Fire pending storage events.
-    mPendingStorageEvents->EnumerateRead(FirePendingStorageEvents, this);
+  for (PRUint32 i = 0; i < mPendingStorageEvents.Length(); ++i) {
+    Observe(mPendingStorageEvents[i], "dom-storage2-changed", nsnull);
+  }
 
-    delete mPendingStorageEvents;
-    mPendingStorageEvents = nsnull;
+  if (mPendingStorageEventsObsolete) {
+    // Fire pending storage events.
+    mPendingStorageEventsObsolete->EnumerateRead(FirePendingStorageEvents, this);
+
+    delete mPendingStorageEventsObsolete;
+    mPendingStorageEventsObsolete = nsnull;
   }
 
   if (mApplicationCache) {
diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h
index b26241e63113..19aa0c9d8451 100644
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -96,6 +96,7 @@
 #include "nsIDOMStorageObsolete.h"
 #include "nsIDOMStorageList.h"
 #include "nsIDOMStorageWindow.h"
+#include "nsIDOMStorageEvent.h"
 #include "nsIDOMOfflineResourceList.h"
 #include "nsPIDOMEventTarget.h"
 #include "nsIArray.h"
@@ -738,6 +739,7 @@ protected:
   nsCOMPtr<nsIDOMCrypto>        mCrypto;
 
   nsCOMPtr<nsIDOMStorage>      mLocalStorage;
+  nsCOMPtr<nsIDOMStorage>      mSessionStorage;
 
   nsCOMPtr<nsISupports>         mInnerWindowHolders[NS_STID_ARRAY_UBOUND];
   nsCOMPtr<nsIPrincipal> mOpenerScriptPrincipal; // strong; used to determine
@@ -750,7 +752,6 @@ protected:
   nsTimeout*                    mTimeoutInsertionPoint;
   PRUint32                      mTimeoutPublicIdCounter;
   PRUint32                      mTimeoutFiringDepth;
-  nsCOMPtr<nsIDOMStorageObsolete>       mSessionStorage;
 
   // Holder of the dummy java plugin, used to expose window.java and
   // window.packages.
@@ -761,7 +762,9 @@ protected:
   nsCOMPtr<nsIDocument> mDoc;  // For fast access to principals
   JSObject* mJSObject;
 
-  nsDataHashtable<nsStringHashKey, PRBool> *mPendingStorageEvents;
+  typedef nsTArray<nsCOMPtr<nsIDOMStorageEvent> > nsDOMStorageEventArray;
+  nsDOMStorageEventArray mPendingStorageEvents;
+  nsDataHashtable<nsStringHashKey, PRBool> *mPendingStorageEventsObsolete;
 
   PRUint32 mTimeoutsSuspendDepth;
 
diff --git a/dom/interfaces/storage/Makefile.in b/dom/interfaces/storage/Makefile.in
index ca9b3796afc0..4e8f55712f89 100644
--- a/dom/interfaces/storage/Makefile.in
+++ b/dom/interfaces/storage/Makefile.in
@@ -59,6 +59,7 @@ SDK_XPIDLSRCS =                      \
         nsIDOMStorage.idl        \
         nsIDOMStorageObsolete.idl\
         nsIDOMStorageEvent.idl   \
+        nsIDOMStorageEventObsolete.idl \
         nsIDOMStorageItem.idl    \
         nsIDOMStorageList.idl    \
         nsIDOMStorageWindow.idl  \
diff --git a/dom/interfaces/storage/nsIDOMStorageEvent.idl b/dom/interfaces/storage/nsIDOMStorageEvent.idl
index e5a2fce0b9a6..25941ad5f532 100644
--- a/dom/interfaces/storage/nsIDOMStorageEvent.idl
+++ b/dom/interfaces/storage/nsIDOMStorageEvent.idl
@@ -40,26 +40,57 @@
 
 /**
  * Interface for a client side storage. See
- * http://www.whatwg.org/specs/web-apps/current-work/#scs-client-side
+ * http://dev.w3.org/html5/webstorage/#the-storage-event
  * for more information.
  *
  * Event sent to a window when a storage area changes.
  */
 
-[scriptable, uuid(8c021c75-f6b6-493c-914e-0b6c57ccc88d)]
+interface nsIDOMStorage;
+
+[scriptable, uuid(AE0CB688-68B3-4fb3-9A11-2DA8E620E808)]
 interface nsIDOMStorageEvent : nsIDOMEvent
 {
   /**
-   * Domain of the storage area which changed, or #session for
-   * session storage.
+   * Attribute represents the key being changed. The key attribute is null
+   * when change has been invoked by the storage clear() method.
    */
-  readonly attribute DOMString domain;
+  readonly attribute DOMString key;
 
   /**
-   * Initialize a storage event.
+   * The original value of the key. The oldValue is null when the change
+   * has been invoked by storage clear() method or the key has been newly
+   * added and therefor doesn't have any previous value.
+   */
+  readonly attribute DOMString oldValue;
+
+  /**
+   * The new value of the key. The newValue is null when the change
+   * has been invoked by storage clear() method or the key has been removed
+   * from the storage.
+   */
+  readonly attribute DOMString newValue;
+
+  /**
+   * Represents the address of the document whose key changed.
+   */
+  readonly attribute DOMString url;
+
+  /**
+   * Represents the Storage object that was affected.
+   */
+  readonly attribute nsIDOMStorage storageArea;
+
+  /**
+   * Initialize the event in a manner analogous to the similarly-named method
+   * in the DOM Events interfaces.
    */
   void initStorageEvent(in DOMString typeArg, 
                         in boolean canBubbleArg, 
                         in boolean cancelableArg, 
-                        in DOMString domainArg);
+                        in DOMString keyArg,
+                        in DOMString oldValueArg,
+                        in DOMString newValueArg,
+                        in DOMString urlArg,
+                        in nsIDOMStorage storageAreaArg);
 };
diff --git a/dom/interfaces/storage/nsIDOMStorageEventObsolete.idl b/dom/interfaces/storage/nsIDOMStorageEventObsolete.idl
new file mode 100644
index 000000000000..4bf94e7c1a0f
--- /dev/null
+++ b/dom/interfaces/storage/nsIDOMStorageEventObsolete.idl
@@ -0,0 +1,65 @@
+/* -*- 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 <enndeakin@sympatico.ca>
+ * 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(c0178907-847d-41c0-8a62-31301bb946fa)]
+interface nsIDOMStorageEventObsolete : 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);
+};
diff --git a/dom/interfaces/storage/nsIDOMStorageManager.idl b/dom/interfaces/storage/nsIDOMStorageManager.idl
index 2c6367d6f4c6..7df2efa34706 100644
--- a/dom/interfaces/storage/nsIDOMStorageManager.idl
+++ b/dom/interfaces/storage/nsIDOMStorageManager.idl
@@ -40,7 +40,7 @@
 interface nsIDOMStorage;
 interface nsIPrincipal;
 
-[scriptable, uuid(c8e54beb-48f3-4538-a0ce-d6229f4d8f45)]
+[scriptable, uuid(fd91ec36-7da8-43bb-b8f2-4b57a862a467)]
 interface nsIDOMStorageManager : nsISupports
 {
   /**
@@ -64,5 +64,6 @@ interface nsIDOMStorageManager : nsISupports
    * This method ensures there is always only a single instance
    * for a single origin.
    */
-  nsIDOMStorage getLocalStorageForPrincipal(in nsIPrincipal aPrincipal);
+  nsIDOMStorage getLocalStorageForPrincipal(in nsIPrincipal aPrincipal,
+                                            in DOMString aDocumentURI);
 };
diff --git a/dom/interfaces/storage/nsPIDOMStorage.h b/dom/interfaces/storage/nsPIDOMStorage.h
index 2ee17f0c7065..0ade91d719cd 100644
--- a/dom/interfaces/storage/nsPIDOMStorage.h
+++ b/dom/interfaces/storage/nsPIDOMStorage.h
@@ -47,25 +47,41 @@ class nsIDOMStorageObsolete;
 class nsIURI;
 class nsIPrincipal;
 
+// {BAFFCEB1-FD40-4ea9-8378-3509DD79204A}
 #define NS_PIDOMSTORAGE_IID                                 \
-  { 0x5ffbee8d, 0x9a86, 0x4a57,                           \
-      { 0x8c, 0x63, 0x76, 0x56, 0x18, 0x9c, 0xb2, 0xbc } }
+  { 0xbaffceb1, 0xfd40, 0x4ea9,  \
+    { 0x83, 0x78, 0x35, 0x9, 0xdd, 0x79, 0x20, 0x4a } }
 
 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;
+  typedef enum {
+    Unknown = 0,
+    GlobalStorage = 1,
+    LocalStorage = 2,
+    SessionStorage = 3
+  } nsDOMStorageType;
+
+  virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI) = 0;
+  virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI) = 0;
   virtual nsresult InitAsGlobalStorage(const nsACString &aDomainDemanded) = 0;
 
   virtual already_AddRefed<nsIDOMStorage> Clone() = 0;
+  virtual already_AddRefed<nsIDOMStorage> Fork(const nsSubstring &aDocumentURI) = 0;
+  virtual PRBool IsForkOf(nsIDOMStorage* aThat) = 0;
 
   virtual nsTArray<nsString> *GetKeys() = 0;
 
   virtual nsIPrincipal* Principal() = 0;
   virtual PRBool CanAccess(nsIPrincipal *aPrincipal) = 0;
+
+  virtual nsDOMStorageType StorageType() = 0;
+
+  virtual void BroadcastChangeNotification(const nsSubstring &aKey,
+                                           const nsSubstring &aOldValue,
+                                           const nsSubstring &aNewValue) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMStorage, NS_PIDOMSTORAGE_IID)
diff --git a/dom/src/storage/nsDOMStorage.cpp b/dom/src/storage/nsDOMStorage.cpp
index b1be207efe9f..df35c0f41e1b 100644
--- a/dom/src/storage/nsDOMStorage.cpp
+++ b/dom/src/storage/nsDOMStorage.cpp
@@ -64,6 +64,7 @@
 #include "nsIPrivateBrowsingService.h"
 #include "nsDOMString.h"
 #include "nsNetCID.h"
+#include "nsIProxyObjectManager.h"
 
 static const PRUint32 ASK_BEFORE_ACCEPT = 1;
 static const PRUint32 ACCEPT_SESSION = 2;
@@ -436,6 +437,7 @@ nsDOMStorageManager::ClearOfflineApps()
 
 NS_IMETHODIMP
 nsDOMStorageManager::GetLocalStorageForPrincipal(nsIPrincipal *aPrincipal,
+                                                 const nsSubstring &aDocumentURI,
                                                  nsIDOMStorage **aResult)
 {
   NS_ENSURE_ARG_POINTER(aPrincipal);
@@ -447,7 +449,7 @@ nsDOMStorageManager::GetLocalStorageForPrincipal(nsIPrincipal *aPrincipal,
   if (!storage)
     return NS_ERROR_OUT_OF_MEMORY;
 
-  rv = storage->InitAsLocalStorage(aPrincipal);
+  rv = storage->InitAsLocalStorage(aPrincipal, aDocumentURI);
   if (NS_FAILED(rv))
     return rv;
 
@@ -548,8 +550,9 @@ NS_NewDOMStorage2(nsISupports* aOuter, REFNSIID aIID, void** aResult)
 nsDOMStorage::nsDOMStorage()
   : mUseDB(PR_FALSE)
   , mSessionOnly(PR_TRUE)
-  , mLocalStorage(PR_FALSE)
   , mItemsCached(PR_FALSE)
+  , mStorageType(nsPIDOMStorage::Unknown)
+  , mEventBroadcaster(nsnull)
 {
   mSecurityChecker = this;
   mItems.Init(8);
@@ -560,12 +563,13 @@ nsDOMStorage::nsDOMStorage()
 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)
+  , mStorageType(aThat.mStorageType)
 #ifdef MOZ_STORAGE
   , mScopeDBKey(aThat.mScopeDBKey)
 #endif
+  , mEventBroadcaster(nsnull)
 {
   mSecurityChecker = this;
   mItems.Init(8);
@@ -610,7 +614,7 @@ GetDomainURI(nsIPrincipal *aPrincipal, PRBool aIncludeDomain, nsIURI **_domain)
 }
 
 nsresult
-nsDOMStorage::InitAsSessionStorage(nsIPrincipal *aPrincipal)
+nsDOMStorage::InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI)
 {
   nsCOMPtr<nsIURI> domainURI;
   nsresult rv = GetDomainURI(aPrincipal, PR_TRUE, getter_AddRefs(domainURI));
@@ -623,16 +627,20 @@ nsDOMStorage::InitAsSessionStorage(nsIPrincipal *aPrincipal)
   // won't get to InitAsSessionStorage.
   domainURI->GetAsciiHost(mDomain);
 
+  mDocumentURI = aDocumentURI;
+
 #ifdef MOZ_STORAGE
   mUseDB = PR_FALSE;
   mScopeDBKey.Truncate();
   mQuotaDomainDBKey.Truncate();
 #endif
+
+  mStorageType = SessionStorage;
   return NS_OK;
 }
 
 nsresult
-nsDOMStorage::InitAsLocalStorage(nsIPrincipal *aPrincipal)
+nsDOMStorage::InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI)
 {
   nsCOMPtr<nsIURI> domainURI;
   nsresult rv = GetDomainURI(aPrincipal, PR_FALSE, getter_AddRefs(domainURI));
@@ -646,6 +654,8 @@ nsDOMStorage::InitAsLocalStorage(nsIPrincipal *aPrincipal)
   // mPrincipal in bug 455070. It is not even used for localStorage.
   domainURI->GetAsciiHost(mDomain);
 
+  mDocumentURI = aDocumentURI;
+
 #ifdef MOZ_STORAGE
   nsDOMStorageDBWrapper::CreateOriginScopeDBKey(domainURI, mScopeDBKey);
 
@@ -661,7 +671,7 @@ nsDOMStorage::InitAsLocalStorage(nsIPrincipal *aPrincipal)
       PR_TRUE, PR_TRUE, mQuotaETLDplus1DomainDBKey);
 #endif
 
-  mLocalStorage = PR_TRUE;
+  mStorageType = LocalStorage;
   return NS_OK;
 }
 
@@ -684,6 +694,9 @@ nsDOMStorage::InitAsGlobalStorage(const nsACString &aDomainDemanded)
   nsDOMStorageDBWrapper::CreateQuotaDomainDBKey(aDomainDemanded,
       PR_TRUE, PR_TRUE, mQuotaETLDplus1DomainDBKey);
 #endif
+
+  mStorageType = GlobalStorage;
+  mEventBroadcaster = this;
   return NS_OK;
 }
 
@@ -995,6 +1008,9 @@ nsDOMStorage::SetItem(const nsAString& aKey, const nsAString& aData)
   if (aKey.IsEmpty())
     return NS_OK;
 
+  nsString oldValue;
+  SetDOMStringToNull(oldValue);
+
   nsresult rv;
   nsRefPtr<nsDOMStorageItem> newitem = nsnull;
   nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
@@ -1002,9 +1018,8 @@ nsDOMStorage::SetItem(const nsAString& aKey, const nsAString& aData)
     if (entry->mItem->IsSecure() && !IsCallerSecure()) {
       return NS_ERROR_DOM_SECURITY_ERR;
     }
-    if (!UseDB()) {
-      entry->mItem->SetValueInternal(aData);
-    }
+    oldValue = entry->mItem->GetValueInternal();
+    entry->mItem->SetValueInternal(aData);
   }
   else {
     newitem = new nsDOMStorageItem(this, aKey, aData, IsCallerSecure());
@@ -1023,9 +1038,8 @@ nsDOMStorage::SetItem(const nsAString& aKey, const nsAString& aData)
     entry->mItem = newitem;
   }
 
-  // SetDBValue already calls BroadcastChangeNotification so don't do it again
-  if (!UseDB())
-    BroadcastChangeNotification();
+  if ((oldValue != aData || mStorageType == GlobalStorage) && mEventBroadcaster)
+    mEventBroadcaster->BroadcastChangeNotification(aKey, oldValue, aData);
 
   return NS_OK;
 }
@@ -1038,6 +1052,7 @@ NS_IMETHODIMP nsDOMStorage::RemoveItem(const nsAString& aKey)
   if (aKey.IsEmpty())
     return NS_OK;
 
+  nsString oldValue;
   nsSessionStorageEntry *entry = mItems.GetEntry(aKey);
 
   if (entry && entry->mItem->IsSecure() && !IsCallerSecure()) {
@@ -1056,26 +1071,31 @@ NS_IMETHODIMP nsDOMStorage::RemoveItem(const nsAString& aKey)
       return NS_OK;
     NS_ENSURE_SUCCESS(rv, rv);
 
+    oldValue = value;
+
     rv = gStorageDB->RemoveKey(this, aKey, !IsOfflineAllowed(mDomain),
                                aKey.Length() + value.Length());
     NS_ENSURE_SUCCESS(rv, rv);
 
     mItemsCached = PR_FALSE;
-
-    BroadcastChangeNotification();
 #endif
   }
   else if (entry) {
     // clear string as StorageItems may be referencing this item
+    oldValue = entry->mItem->GetValueInternal();
     entry->mItem->ClearValue();
-
-    BroadcastChangeNotification();
   }
 
   if (entry) {
     mItems.RawRemoveEntry(entry);
   }
 
+  if ((!oldValue.IsEmpty() && mStorageType != GlobalStorage) && mEventBroadcaster) {
+    nsAutoString nullString;
+    SetDOMStringToNull(nullString);
+    mEventBroadcaster->BroadcastChangeNotification(aKey, oldValue, nullString);
+  }
+
   return NS_OK;
 }
 
@@ -1100,6 +1120,8 @@ nsDOMStorage::Clear()
   if (UseDB())
     CacheKeysFromDB();
 
+  PRInt32 oldCount = mItems.Count();
+
   PRBool foundSecureItem = PR_FALSE;
   mItems.EnumerateEntries(CheckSecure, &foundSecureItem);
 
@@ -1118,7 +1140,12 @@ nsDOMStorage::Clear()
 #endif
 
   mItems.Clear();
-  BroadcastChangeNotification();
+
+  if (oldCount && mEventBroadcaster) {
+    nsAutoString nullString;
+    SetDOMStringToNull(nullString);
+    mEventBroadcaster->BroadcastChangeNotification(nullString, nullString, nullString);
+  }
 
   return NS_OK;
 }
@@ -1188,7 +1215,7 @@ nsDOMStorage::GetDBValue(const nsAString& aKey, nsAString& aValue,
   nsAutoString value;
   rv = gStorageDB->GetKeyValue(this, aKey, value, aSecure);
 
-  if (rv == NS_ERROR_DOM_NOT_FOUND_ERR && mLocalStorage) {
+  if (rv == NS_ERROR_DOM_NOT_FOUND_ERR && mStorageType != GlobalStorage) {
     SetDOMStringToNull(aValue);
   }
 
@@ -1250,7 +1277,6 @@ nsDOMStorage::SetDBValue(const nsAString& aKey,
                         NS_ConvertUTF8toUTF16(mDomain).get());
   }
 
-  BroadcastChangeNotification();
 #endif
 
   return NS_OK;
@@ -1301,6 +1327,19 @@ nsDOMStorage::Clone()
   return nsnull;
 }
 
+already_AddRefed<nsIDOMStorage>
+nsDOMStorage::Fork(const nsSubstring &aDocumentURI)
+{
+  NS_ASSERTION(PR_FALSE, "Old DOMStorage doesn't implement forking");
+  return nsnull;
+}
+
+PRBool nsDOMStorage::IsForkOf(nsIDOMStorage* aThat)
+{
+  NS_ASSERTION(PR_FALSE, "Old DOMStorage doesn't implement forking");
+  return PR_FALSE;
+}
+
 struct KeysArrayBuilderStruct
 {
   PRBool callerIsSecure;
@@ -1371,8 +1410,16 @@ nsDOMStorage::CanAccess(nsIPrincipal *aPrincipal)
   return domain.Equals(mDomain);
 }
 
+nsPIDOMStorage::nsDOMStorageType
+nsDOMStorage::StorageType()
+{
+  return mStorageType;
+}
+
 void
-nsDOMStorage::BroadcastChangeNotification()
+nsDOMStorage::BroadcastChangeNotification(const nsSubstring &aKey,
+                                          const nsSubstring &aOldValue,
+                                          const nsSubstring &aNewValue)
 {
   nsresult rv;
   nsCOMPtr<nsIObserverService> observerService =
@@ -1386,7 +1433,7 @@ nsDOMStorage::BroadcastChangeNotification()
   // domain, but if it's a global storage object we do.
   observerService->NotifyObservers((nsIDOMStorageObsolete *)this,
                                    "dom-storage-changed",
-                                   UseDB() ? NS_ConvertUTF8toUTF16(mDomain).get() : nsnull);
+                                   NS_ConvertUTF8toUTF16(mDomain).get());
 }
 
 //
@@ -1422,7 +1469,7 @@ nsDOMStorage2::nsDOMStorage2(nsDOMStorage2& aThat)
 }
 
 nsresult
-nsDOMStorage2::InitAsSessionStorage(nsIPrincipal *aPrincipal)
+nsDOMStorage2::InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI)
 {
   mStorage = new nsDOMStorage();
   if (!mStorage)
@@ -1431,11 +1478,13 @@ nsDOMStorage2::InitAsSessionStorage(nsIPrincipal *aPrincipal)
   // Leave security checks only for domain (nsDOMStorage implementation)
   mStorage->mSecurityChecker = mStorage;
   mPrincipal = aPrincipal;
-  return mStorage->InitAsSessionStorage(aPrincipal);
+  mDocumentURI = aDocumentURI;
+
+  return mStorage->InitAsSessionStorage(aPrincipal, aDocumentURI);
 }
 
 nsresult
-nsDOMStorage2::InitAsLocalStorage(nsIPrincipal *aPrincipal)
+nsDOMStorage2::InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI)
 {
   mStorage = new nsDOMStorage();
   if (!mStorage)
@@ -1443,7 +1492,9 @@ nsDOMStorage2::InitAsLocalStorage(nsIPrincipal *aPrincipal)
 
   mStorage->mSecurityChecker = this;
   mPrincipal = aPrincipal;
-  return mStorage->InitAsLocalStorage(aPrincipal);
+  mDocumentURI = aDocumentURI;
+
+  return mStorage->InitAsLocalStorage(aPrincipal, aDocumentURI);
 }
 
 nsresult
@@ -1466,6 +1517,41 @@ nsDOMStorage2::Clone()
   return storage;
 }
 
+already_AddRefed<nsIDOMStorage>
+nsDOMStorage2::Fork(const nsSubstring &aDocumentURI)
+{
+  nsRefPtr<nsDOMStorage2> storage = new nsDOMStorage2();
+  if (!storage)
+    return nsnull;
+
+  nsresult rv = storage->InitAsSessionStorageFork(mPrincipal, aDocumentURI, mStorage);
+  if (NS_FAILED(rv))
+    return nsnull;
+
+  nsIDOMStorage* result = static_cast<nsIDOMStorage*>(storage.get());
+  storage.forget();
+  return result;
+}
+
+PRBool nsDOMStorage2::IsForkOf(nsIDOMStorage* aThat)
+{
+  if (!aThat)
+    return PR_FALSE;
+
+  nsDOMStorage2* storage = static_cast<nsDOMStorage2*>(aThat);
+  return mStorage == storage->mStorage;
+}
+
+nsresult
+nsDOMStorage2::InitAsSessionStorageFork(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI, nsIDOMStorageObsolete* aStorage)
+{
+  mPrincipal = aPrincipal;
+  mDocumentURI = aDocumentURI;
+  mStorage = static_cast<nsDOMStorage*>(aStorage);
+
+  return NS_OK;
+}
+
 nsTArray<nsString> *
 nsDOMStorage2::GetKeys()
 {
@@ -1497,6 +1583,56 @@ nsDOMStorage2::CanAccess(nsIPrincipal *aPrincipal)
   return subsumes;
 }
 
+nsPIDOMStorage::nsDOMStorageType
+nsDOMStorage2::StorageType()
+{
+  if (mStorage)
+    return mStorage->StorageType();
+
+  return nsPIDOMStorage::Unknown;
+}
+
+void
+nsDOMStorage2::BroadcastChangeNotification(const nsSubstring &aKey,
+                                          const nsSubstring &aOldValue,
+                                          const nsSubstring &aNewValue)
+{
+  nsresult rv;
+  nsCOMPtr<nsIDOMStorageEvent> event = new nsDOMStorageEvent();
+  rv = event->InitStorageEvent(NS_LITERAL_STRING("storage"),
+                               PR_FALSE,
+                               PR_FALSE,
+                               aKey,
+                               aOldValue,
+                               aNewValue,
+                               mDocumentURI,
+                               static_cast<nsIDOMStorage*>(this));
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  nsCOMPtr<nsIObserverService> observerService =
+    do_GetService("@mozilla.org/observer-service;1", &rv);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  nsCOMPtr<nsIObserverService> observerServiceProxy;
+  rv = NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
+                            NS_GET_IID(nsIObserverService),
+                            observerService,
+                            NS_PROXY_ASYNC | NS_PROXY_ALWAYS,
+                            getter_AddRefs(observerServiceProxy));
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  // Fire off a notification that a storage object changed.
+  observerServiceProxy->NotifyObservers(event,
+                                        "dom-storage2-changed",
+                                        nsnull);
+}
+
 NS_IMETHODIMP
 nsDOMStorage2::GetLength(PRUint32 *aLength)
 {
@@ -1518,18 +1654,21 @@ nsDOMStorage2::GetItem(const nsAString& aKey, nsAString &aData)
 NS_IMETHODIMP
 nsDOMStorage2::SetItem(const nsAString& aKey, const nsAString& aData)
 {
+  mStorage->mEventBroadcaster = this;
   return mStorage->SetItem(aKey, aData);
 }
 
 NS_IMETHODIMP
 nsDOMStorage2::RemoveItem(const nsAString& aKey)
 {
+  mStorage->mEventBroadcaster = this;
   return mStorage->RemoveItem(aKey);
 }
 
 NS_IMETHODIMP
 nsDOMStorage2::Clear()
 {
+  mStorage->mEventBroadcaster = this;
   return mStorage->Clear();
 }
 
@@ -1842,8 +1981,81 @@ NS_IMPL_ADDREF_INHERITED(nsDOMStorageEvent, nsDOMEvent)
 NS_IMPL_RELEASE_INHERITED(nsDOMStorageEvent, nsDOMEvent)
 
 
+/* readonly attribute DOMString key; */
+NS_IMETHODIMP nsDOMStorageEvent::GetKey(nsAString & aKey)
+{
+  aKey = mKey;
+  return NS_OK;
+}
+
+/* readonly attribute DOMString oldValue; */
+NS_IMETHODIMP nsDOMStorageEvent::GetOldValue(nsAString & aOldValue)
+{
+  aOldValue = mOldValue;
+  return NS_OK;
+}
+
+/* readonly attribute DOMString newValue; */
+NS_IMETHODIMP nsDOMStorageEvent::GetNewValue(nsAString & aNewValue)
+{
+  aNewValue = mNewValue;
+  return NS_OK;
+}
+
+/* readonly attribute DOMString url; */
+NS_IMETHODIMP nsDOMStorageEvent::GetUrl(nsAString & aUrl)
+{
+  aUrl = mUrl;
+  return NS_OK;
+}
+
+/* readonly attribute nsIDOMStorage storageArea; */
+NS_IMETHODIMP nsDOMStorageEvent::GetStorageArea(nsIDOMStorage * *aStorageArea)
+{
+  NS_ENSURE_ARG_POINTER(aStorageArea);
+
+  NS_ADDREF(*aStorageArea = mStorageArea);
+  return NS_OK;
+}
+
+/* void initStorageEvent (in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString keyArg, in DOMString oldValueArg, in DOMString newValueArg, in DOMString urlArg, in nsIDOMStorage storageAreaArg); */
+NS_IMETHODIMP nsDOMStorageEvent::InitStorageEvent(const nsAString & typeArg,
+                                                  PRBool canBubbleArg,
+                                                  PRBool cancelableArg,
+                                                  const nsAString & keyArg,
+                                                  const nsAString & oldValueArg,
+                                                  const nsAString & newValueArg,
+                                                  const nsAString & urlArg,
+                                                  nsIDOMStorage *storageAreaArg)
+{
+  nsresult rv;
+
+  rv = InitEvent(typeArg, canBubbleArg, cancelableArg);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mKey = keyArg;
+  mOldValue = oldValueArg;
+  mNewValue = newValueArg;
+  mUrl = urlArg;
+  mStorageArea = storageAreaArg;
+
+  return NS_OK;
+}
+
+// Obsolete globalStorage event
+
+// QueryInterface implementation for nsDOMStorageEventObsolete
+NS_INTERFACE_MAP_BEGIN(nsDOMStorageEventObsolete)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMStorageEventObsolete)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageEventObsolete)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
+
+NS_IMPL_ADDREF_INHERITED(nsDOMStorageEventObsolete, nsDOMEvent)
+NS_IMPL_RELEASE_INHERITED(nsDOMStorageEventObsolete, nsDOMEvent)
+
+
 NS_IMETHODIMP
-nsDOMStorageEvent::GetDomain(nsAString& aDomain)
+nsDOMStorageEventObsolete::GetDomain(nsAString& aDomain)
 {
   // mDomain will be #session for session storage for events that fire
   // due to a change in a session storage object.
@@ -1852,21 +2064,8 @@ nsDOMStorageEvent::GetDomain(nsAString& aDomain)
   return NS_OK;
 }
 
-nsresult
-nsDOMStorageEvent::Init()
-{
-  nsresult rv = InitEvent(NS_LITERAL_STRING("storage"), PR_TRUE, PR_FALSE);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // 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,
+nsDOMStorageEventObsolete::InitStorageEvent(const nsAString& aTypeArg,
                                     PRBool aCanBubbleArg,
                                     PRBool aCancelableArg,
                                     const nsAString& aDomainArg)
diff --git a/dom/src/storage/nsDOMStorage.h b/dom/src/storage/nsDOMStorage.h
index 7d3675228ca9..67817120070a 100644
--- a/dom/src/storage/nsDOMStorage.h
+++ b/dom/src/storage/nsDOMStorage.h
@@ -55,6 +55,7 @@
 #include "nsIDOMToString.h"
 #include "nsDOMEvent.h"
 #include "nsIDOMStorageEvent.h"
+#include "nsIDOMStorageEventObsolete.h"
 #include "nsIDOMStorageManager.h"
 #include "nsCycleCollectionParticipant.h"
 
@@ -145,13 +146,19 @@ public:
   nsresult Clear();
 
   // nsPIDOMStorage
-  virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal);
-  virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal);
+  virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI);
+  virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI);
   virtual nsresult InitAsGlobalStorage(const nsACString &aDomainDemanded);
   virtual already_AddRefed<nsIDOMStorage> Clone();
+  virtual already_AddRefed<nsIDOMStorage> Fork(const nsSubstring &aDocumentURI);
+  virtual PRBool IsForkOf(nsIDOMStorage* aThat);
   virtual nsTArray<nsString> *GetKeys();
   virtual nsIPrincipal* Principal();
   virtual PRBool CanAccess(nsIPrincipal *aPrincipal);
+  virtual nsDOMStorageType StorageType();
+  virtual void BroadcastChangeNotification(const nsSubstring &aKey,
+                                           const nsSubstring &aOldValue,
+                                           const nsSubstring &aNewValue);
 
   // If true, the contents of the storage should be stored in the
   // database, otherwise this storage should act like a session
@@ -218,13 +225,17 @@ protected:
   // cache the keys from the database for faster lookup
   nsresult CacheKeysFromDB();
 
-  void BroadcastChangeNotification();
-
   PRBool CanAccessSystem(nsIPrincipal *aPrincipal);
 
   // true if the storage database should be used for values
   PRPackedBool mUseDB;
 
+  // domain this store is associated with
+  nsCString mDomain;
+
+  // document URI of the document this storage is bound to
+  nsString mDocumentURI;
+
   // true if the preferences indicates that this storage should be
   // session only. This member is updated by
   // CacheStoragePermissions(), using the current principal.
@@ -236,14 +247,11 @@ protected:
   // objects are scoped to scheme/host/port in the database, while globalStorage
   // objects are scoped just to host.  this flag also tells the manager to map
   // this storage also in mLocalStorages hash table.
-  PRPackedBool mLocalStorage;
+  nsDOMStorageType mStorageType;
 
   // true if items from the database are cached
   PRPackedBool mItemsCached;
 
-  // domain this store is associated with
-  nsCString mDomain;
-
   // the key->value item pairs
   nsTHashtable<nsSessionStorageEntry> mItems;
 
@@ -255,6 +263,7 @@ protected:
 
   friend class nsIDOMStorage2;
   nsPIDOMStorage* mSecurityChecker;
+  nsPIDOMStorage* mEventBroadcaster;
 
 public:
   // e.g. "moc.rab.oof.:" or "moc.rab.oof.:http:80" depending
@@ -288,19 +297,32 @@ public:
   NS_DECL_NSIDOMSTORAGE
 
   // nsPIDOMStorage
-  virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal);
-  virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal);
+  virtual nsresult InitAsSessionStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI);
+  virtual nsresult InitAsLocalStorage(nsIPrincipal *aPrincipal, const nsSubstring &aDocumentURI);
   virtual nsresult InitAsGlobalStorage(const nsACString &aDomainDemanded);
   virtual already_AddRefed<nsIDOMStorage> Clone();
+  virtual already_AddRefed<nsIDOMStorage> Fork(const nsSubstring &aDocumentURI);
+  virtual PRBool IsForkOf(nsIDOMStorage* aThat);
   virtual nsTArray<nsString> *GetKeys();
   virtual nsIPrincipal* Principal();
   virtual PRBool CanAccess(nsIPrincipal *aPrincipal);
+  virtual nsDOMStorageType StorageType();
+  virtual void BroadcastChangeNotification(const nsSubstring &aKey,
+                                           const nsSubstring &aOldValue,
+                                           const nsSubstring &aNewValue);
+
+  nsresult InitAsSessionStorageFork(nsIPrincipal *aPrincipal,
+                                    const nsSubstring &aDocumentURI,
+                                    nsIDOMStorageObsolete* aStorage);
 
 private:
   // storages bound to an origin hold the principal to
   // make security checks against it
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
+  // Needed for the storage event, this is address of the document this storage
+  // is bound to
+  nsString mDocumentURI;
   nsRefPtr<nsDOMStorage> mStorage;
 };
 
@@ -422,15 +444,9 @@ class nsDOMStorageEvent : public nsDOMEvent,
                           public nsIDOMStorageEvent
 {
 public:
-  nsDOMStorageEvent(const nsAString& aDomain)
-    : nsDOMEvent(nsnull, nsnull), mDomain(aDomain)
+  nsDOMStorageEvent()
+    : nsDOMEvent(nsnull, nsnull)
   {
-    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()
@@ -443,6 +459,31 @@ public:
   NS_DECL_NSIDOMSTORAGEEVENT
   NS_FORWARD_NSIDOMEVENT(nsDOMEvent::)
 
+protected:
+  nsString mKey;
+  nsString mOldValue;
+  nsString mNewValue;
+  nsString mUrl;
+  nsCOMPtr<nsIDOMStorage> mStorageArea;
+};
+
+class nsDOMStorageEventObsolete : public nsDOMEvent,
+                          public nsIDOMStorageEventObsolete
+{
+public:
+  nsDOMStorageEventObsolete()
+    : nsDOMEvent(nsnull, nsnull)
+  {
+  }
+
+  virtual ~nsDOMStorageEventObsolete()
+  {
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSTORAGEEVENTOBSOLETE
+  NS_FORWARD_NSIDOMEVENT(nsDOMEvent::)
+
 protected:
   nsString mDomain;
 };
diff --git a/dom/tests/mochitest/Makefile.in b/dom/tests/mochitest/Makefile.in
index 9e3d220f6642..881c0fec4174 100644
--- a/dom/tests/mochitest/Makefile.in
+++ b/dom/tests/mochitest/Makefile.in
@@ -55,6 +55,7 @@ DIRS	+= \
 	geolocation \
 	localstorage \
 	sessionstorage \
+	storageevent \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
diff --git a/dom/tests/mochitest/localstorage/test_localStorageFromChrome.xhtml b/dom/tests/mochitest/localstorage/test_localStorageFromChrome.xhtml
index 4b52449d1824..ec7b6ab3d965 100644
--- a/dom/tests/mochitest/localstorage/test_localStorageFromChrome.xhtml
+++ b/dom/tests/mochitest/localstorage/test_localStorageFromChrome.xhtml
@@ -20,7 +20,7 @@ function startTest()
 
   var uri = ios.newURI(url, "", null);
   var principal = ssm.getCodebasePrincipal(uri);
-  var storage = dsm.getLocalStorageForPrincipal(principal);
+  var storage = dsm.getLocalStorageForPrincipal(principal, "");
   
   storage.setItem("chromekey", "chromevalue");
   
diff --git a/dom/tests/mochitest/storageevent/Makefile.in b/dom/tests/mochitest/storageevent/Makefile.in
new file mode 100644
index 000000000000..2cd91eff3ab5
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/Makefile.in
@@ -0,0 +1,71 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Corporation.
+# Portions created by the Initial Developer are Copyright (C) 2008
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Jan Bambas <honzab@firemni.cz>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+relativesrcdir	= dom/tests/mochitest/storageevent
+
+include $(DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES	= \
+    frameGlobalStorageMaster.html \
+    frameGlobalStorageSlaveEqual.html \
+    frameGlobalStorageSlaveNotEqual.html \
+    frameLocalStorageMaster.html \
+    frameLocalStorageSlaveEqual.html \
+    frameLocalStorageSlaveNotEqual.html \
+    frameSessionStorageMasterEqual.html \
+    frameSessionStorageMasterNotEqual.html \
+    frameSessionStorageSlaveEqual.html \
+    frameSessionStorageSlaveNotEqual.html \
+    test_storageGlobalStorageEventCheckNoPropagation.html \
+    test_storageGlobalStorageEventCheckPropagation.html \
+    test_storageLocalStorageEventCheckNoPropagation.html \
+    test_storageLocalStorageEventCheckPropagation.html \
+    test_storageSessionStorageEventCheckPropagation.html \
+    test_storageSessionStorageEventCheckNoPropagation.html \
+    interOriginFrame.js \
+    interOriginTest2.js \
+    $(NULL)
+
+libs::	$(_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
diff --git a/dom/tests/mochitest/storageevent/frameGlobalStorageMaster.html b/dom/tests/mochitest/storageevent/frameGlobalStorageMaster.html
new file mode 100644
index 000000000000..deac8594c75b
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameGlobalStorageMaster.html
@@ -0,0 +1,53 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>master frame for event storage propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 1;
+var gotEvent = false;
+
+function bind()
+{
+  document.body.addEventListener("storage", function(event)
+  {
+    gotEvent = true;
+  }, false);
+}
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    case 1:
+      var storage = globalStorage[location.host];
+      storage.setItem("X", "1");
+      storage.setItem("X", "2");
+      storage.setItem("X", "2");
+      storage.removeItem("X");
+      storage.removeItem("X");
+      storage.removeItem("Y");
+      storage.setItem("X", "2");
+      break;
+
+    case 3:
+      todo(gotEvent, false, "Expected no events");
+      return finishTest();
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="bind(); postMsg('frame loaded');">
+</body>
+</html>
diff --git a/dom/tests/mochitest/storageevent/frameGlobalStorageSlaveEqual.html b/dom/tests/mochitest/storageevent/frameGlobalStorageSlaveEqual.html
new file mode 100644
index 000000000000..9309148f9a99
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameGlobalStorageSlaveEqual.html
@@ -0,0 +1,56 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>slave for storage event propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 2;
+
+var events = [];
+
+function bind()
+{
+  document.body.addEventListener("storage", function(event)
+  {
+    events.push(event);
+  }, false);
+}
+
+function doStep()
+{
+  function checkEvent()
+  {
+    var event = events.shift();
+    ok(event);
+    if (!event)
+      return;
+    is(event.domain, "example.com");
+  }
+
+  switch (currentStep)
+  {
+    case 2:
+      is(events.length, 4, "Expected 5 events");
+      checkEvent();
+      checkEvent();
+      checkEvent();
+      checkEvent();
+      break;
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="bind(); postMsg('frame loaded');">
+</body>
+</html>
diff --git a/dom/tests/mochitest/storageevent/frameGlobalStorageSlaveNotEqual.html b/dom/tests/mochitest/storageevent/frameGlobalStorageSlaveNotEqual.html
new file mode 100644
index 000000000000..219b94ef3b28
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameGlobalStorageSlaveNotEqual.html
@@ -0,0 +1,42 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>slave for storage event propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 2;
+var gotEvent = false;
+
+function bind()
+{
+  document.body.addEventListener("storage", function(event)
+  {
+    gotEvent = true;
+  }, false);
+}
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    case 2:
+      is(gotEvent, false, "Expected no events");
+      break;
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="bind(); postMsg('frame loaded');">
+</body>
+</html>
diff --git a/dom/tests/mochitest/storageevent/frameLocalStorageMaster.html b/dom/tests/mochitest/storageevent/frameLocalStorageMaster.html
new file mode 100644
index 000000000000..b4f372d6c7f5
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameLocalStorageMaster.html
@@ -0,0 +1,63 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>master frame for event storage propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 1;
+var gotEvent = false;
+
+window.addEventListener("storage", function(event)
+{
+  gotEvent = true;
+}, false);
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    case 1:
+      // Must not fire (storage must be clear!)
+      localStorage.clear();
+      // Must fire X:null->'1'
+      localStorage.setItem("X", "1");
+      // Must fire X:'1'->'2'
+      localStorage.setItem("X", "2");
+      // Must not fire
+      localStorage.setItem("X", "2");
+      // Must fire X:'2'->null
+      localStorage.removeItem("X");
+      // Must not fire
+      localStorage.removeItem("X");
+      // Must not fire
+      localStorage.removeItem("Y");
+      // Must fire X:null->'2' (we need something in the storage)
+      localStorage.setItem("X", "2");
+      // Must fire null:null->null (one item has been erased)
+      localStorage.clear();
+      // Must not fire
+      localStorage.clear();
+      break;
+
+    // Wait some time to let the async event be propagated
+    case 11:
+      is(gotEvent, false, "Expected no events");
+      return finishTest();
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="postMsg('frame loaded');">
+</body>
+</html>
diff --git a/dom/tests/mochitest/storageevent/frameLocalStorageSlaveEqual.html b/dom/tests/mochitest/storageevent/frameLocalStorageSlaveEqual.html
new file mode 100644
index 000000000000..3bde58c495dc
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameLocalStorageSlaveEqual.html
@@ -0,0 +1,55 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>slave for storage event propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 2;
+
+var events = [];
+
+window.addEventListener("storage", function(event)
+{
+  events.push(event);
+}, false);
+
+function doStep()
+{
+  function checkEvent(expectedKey, expectedOldValue, expectedNewValue)
+  {
+    var event = events.shift();
+    is(event.key, expectedKey, "key name check");
+    is(event.oldValue, expectedOldValue, "old value check");
+    is(event.newValue, expectedNewValue, "new value check");
+    is(event.url, "http://example.com/tests/dom/tests/mochitest/storageevent/frameLocalStorageMaster.html");
+    ok(event.storageArea);
+  }
+
+  switch (currentStep)
+  {
+    case 10:
+      is(events.length, 5, "Expected 5 events");
+      checkEvent("X", null, "1");
+      checkEvent("X", "1", "2");
+      checkEvent("X", "2", null);
+      checkEvent("X", null, "2");
+      checkEvent(null, null, null);
+      break;
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="postMsg('frame loaded');">
+</body>
+</html>
diff --git a/dom/tests/mochitest/storageevent/frameLocalStorageSlaveNotEqual.html b/dom/tests/mochitest/storageevent/frameLocalStorageSlaveNotEqual.html
new file mode 100644
index 000000000000..ba0e4ff98c76
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameLocalStorageSlaveNotEqual.html
@@ -0,0 +1,39 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>slave for storage event propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 2;
+var gotEvent = false;
+
+window.addEventListener("storage", function(event)
+{
+  gotEvent = true;
+}, false);
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    case 10:
+      is(gotEvent, false, "Expected no events");
+      break;
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="postMsg('frame loaded');">
+</body>
+</html>
diff --git a/dom/tests/mochitest/storageevent/frameSessionStorageMasterEqual.html b/dom/tests/mochitest/storageevent/frameSessionStorageMasterEqual.html
new file mode 100644
index 000000000000..70e89f7e49a6
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameSessionStorageMasterEqual.html
@@ -0,0 +1,67 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>master frame for event storage propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 1;
+var gotEvent = false;
+
+window.addEventListener("storage", function(event)
+{
+  gotEvent = true;
+}, false);
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    // In step 2 we instantiate sessionStorage in the other frame
+    case 1:
+      // Must not fire (storage must be clear!)
+      sessionStorage.clear();
+      // Must fire X:null->'1'
+      sessionStorage.setItem("X", "1");
+      // Must fire X:'1'->'2'
+      sessionStorage.setItem("X", "2");
+      // Must not fire
+      sessionStorage.setItem("X", "2");
+      // Must fire X:'2'->null
+      sessionStorage.removeItem("X");
+      // Must not fire
+      sessionStorage.removeItem("X");
+      // Must not fire
+      sessionStorage.removeItem("Y");
+      // Must fire X:null->'2' (we need something in the storage)
+      sessionStorage.setItem("X", "2");
+      // Must fire null:null->null (one item has been erased)
+      sessionStorage.clear();
+      // Must not fire
+      sessionStorage.clear();
+      break;
+
+    // Wait some time to let the async event be propagated
+    case 11:
+      is(gotEvent, false, "Expected no events");
+      return finishTest();
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="postMsg('frame loaded');">
+  <iframe src="http://example.com:80/tests/dom/tests/mochitest/storageevent/frameSessionStorageSlaveEqual.html"
+          name="slaveFrame">
+  </iframe>
+</body>
+</html>
diff --git a/dom/tests/mochitest/storageevent/frameSessionStorageMasterNotEqual.html b/dom/tests/mochitest/storageevent/frameSessionStorageMasterNotEqual.html
new file mode 100644
index 000000000000..a101b88583ca
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameSessionStorageMasterNotEqual.html
@@ -0,0 +1,66 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>master frame for event storage propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 1;
+var gotEvent = false;
+
+window.addEventListener("storage", function(event)
+{
+  gotEvent = true;
+}, false);
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    case 1:
+      // Must not fire (storage must be clear!)
+      sessionStorage.clear();
+      // Must fire X:null->'1'
+      sessionStorage.setItem("X", "1");
+      // Must fire X:'1'->'2'
+      sessionStorage.setItem("X", "2");
+      // Must not fire
+      sessionStorage.setItem("X", "2");
+      // Must fire X:'2'->null
+      sessionStorage.removeItem("X");
+      // Must not fire
+      sessionStorage.removeItem("X");
+      // Must not fire
+      sessionStorage.removeItem("Y");
+      // Must fire X:null->'2' (we need something in the storage)
+      sessionStorage.setItem("X", "2");
+      // Must fire null:null->null (one item has been erased)
+      sessionStorage.clear();
+      // Must not fire
+      sessionStorage.clear();
+      break;
+
+    // Wait some time to let the async event be propagated
+    case 11:
+      is(gotEvent, false, "Expected no events");
+      return finishTest();
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="postMsg('frame loaded');">
+  <iframe src="http://example.com:80/tests/dom/tests/mochitest/storageevent/frameSessionStorageSlaveNotEqual.html"
+          name="slaveFrame">
+  </iframe>
+</body>
+</html>
diff --git a/dom/tests/mochitest/storageevent/frameSessionStorageSlaveEqual.html b/dom/tests/mochitest/storageevent/frameSessionStorageSlaveEqual.html
new file mode 100644
index 000000000000..287cb22f66ef
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameSessionStorageSlaveEqual.html
@@ -0,0 +1,59 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>slave for storage event propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 2;
+
+var events = [];
+
+window.addEventListener("storage", function(event)
+{
+  events.push(event);
+}, false);
+
+function doStep()
+{
+  function checkEvent(expectedKey, expectedOldValue, expectedNewValue)
+  {
+    var event = events.shift();
+    ok(event, "Event is present");
+    if (!event)
+      return;
+
+    is(event.key, expectedKey, "key name check");
+    is(event.oldValue, expectedOldValue, "old value check");
+    is(event.newValue, expectedNewValue, "new value check");
+    is(event.url, "http://example.com/tests/dom/tests/mochitest/storageevent/frameSessionStorageMasterEqual.html");
+    ok(event.storageArea);
+  }
+
+  switch (currentStep)
+  {
+    case 10:
+      is(events.length, 5, "Expected 5 events");
+      checkEvent("X", null, "1");
+      checkEvent("X", "1", "2");
+      checkEvent("X", "2", null);
+      checkEvent("X", null, "2");
+      checkEvent(null, null, null);
+      break;
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="postMsg('frame loaded');">
+</body>
+</html>
diff --git a/dom/tests/mochitest/storageevent/frameSessionStorageSlaveNotEqual.html b/dom/tests/mochitest/storageevent/frameSessionStorageSlaveNotEqual.html
new file mode 100644
index 000000000000..ba0e4ff98c76
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/frameSessionStorageSlaveNotEqual.html
@@ -0,0 +1,39 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>slave for storage event propagation</title>
+
+<script type="text/javascript" src="interOriginFrame.js"></script>
+<script type="text/javascript">
+
+var currentStep = 2;
+var gotEvent = false;
+
+window.addEventListener("storage", function(event)
+{
+  gotEvent = true;
+}, false);
+
+function doStep()
+{
+  switch (currentStep)
+  {
+    case 10:
+      is(gotEvent, false, "Expected no events");
+      break;
+  }
+
+  // Increase by two to distinguish each test step order
+  // in both master doStep and slave doStep functions.
+  ++currentStep;
+  ++currentStep;
+
+  return true;
+}
+
+</script>
+
+</head>
+
+<body onload="postMsg('frame loaded');">
+</body>
+</html>
diff --git a/dom/tests/mochitest/storageevent/interOriginFrame.js b/dom/tests/mochitest/storageevent/interOriginFrame.js
new file mode 100644
index 000000000000..d3ff1bd00b3f
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/interOriginFrame.js
@@ -0,0 +1,57 @@
+function postMsg(message)
+{
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
+  var l = parent.window.location;
+  parent.postMessage(message, l.protocol + "//" + l.host);
+}
+
+window.addEventListener("message", onMessageReceived, false);
+
+function onMessageReceived(event)
+{
+  if (event.data == "step") {
+    var performed = false;
+    try {
+      performed = doStep();
+    }
+    catch (ex) {
+      postMsg("FAILURE: exception threw at "+ location +":\n" + ex);
+      finishTest();
+    }
+
+    if (performed)
+      postMsg("perf");
+
+    return;
+  }
+
+  if (parent)
+    postMsg(event.data);
+}
+
+function ok(a, message)
+{
+  if (!a)
+    postMsg("FAILURE: " + message);
+  else
+    postMsg(message);
+}
+
+function is(a, b, message)
+{
+  if (a != b)
+    postMsg("FAILURE: " + message + ", expected "+b+" got "+a);
+  else
+    postMsg(message + ", expected "+b+" got "+a);
+}
+
+function todo(a, b, message)
+{
+  postMsg("TODO: " + message + ", expected "+b+" got "+a);
+}
+
+function finishTest()
+{
+  postMsg("done");
+  return false;
+}
diff --git a/dom/tests/mochitest/storageevent/interOriginTest2.js b/dom/tests/mochitest/storageevent/interOriginTest2.js
new file mode 100644
index 000000000000..014d9c4d814f
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/interOriginTest2.js
@@ -0,0 +1,58 @@
+var frameLoadsPending = 2;
+
+var callMasterFrame = true;
+var testDone = false;
+
+var masterFrameOrigin = "";
+var slaveFrameOrigin = "";
+
+var failureRegExp = new RegExp("^FAILURE");
+var todoRegExp = new RegExp("^TODO");
+
+const framePath = "/tests/dom/tests/mochitest/storageevent/";
+
+window.addEventListener("message", onMessageReceived, false);
+
+function onMessageReceived(event)
+{
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
+
+  switch (event.data)
+  {
+    // Indication of the frame onload event
+    case "frame loaded":
+      if (--frameLoadsPending)
+        break;
+
+      // Just fall through...
+
+    // Indication of successfully finished step of a test
+    case "perf":
+      if (callMasterFrame)
+        masterFrame.postMessage("step", masterFrameOrigin);
+      else if (slaveFrame)
+        slaveFrame.postMessage("step", slaveFrameOrigin);
+      else if (masterFrame.slaveFrame)
+        masterFrame.slaveFrame.postMessage("step", slaveFrameOrigin);
+      callMasterFrame = !callMasterFrame;
+      break;
+
+    // Indication of all test parts finish (from any of the frames)
+    case "done":
+      if (testDone)
+        break;
+
+      testDone = true;
+      SimpleTest.finish();
+      break;
+
+    // Any other message indicates error, succes or todo message of a test
+    default:
+      if (event.data.match(todoRegExp))
+        SimpleTest.todo(false, event.data);
+      else
+        SimpleTest.ok(!event.data.match(failureRegExp), event.data);
+      break;
+  }
+}
diff --git a/dom/tests/mochitest/storageevent/test_storageGlobalStorageEventCheckNoPropagation.html b/dom/tests/mochitest/storageevent/test_storageGlobalStorageEventCheckNoPropagation.html
new file mode 100644
index 000000000000..f87277017d3e
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/test_storageGlobalStorageEventCheckNoPropagation.html
@@ -0,0 +1,44 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>storage event propagation test</title>
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="interOriginTest2.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<!--
+  This test loads two frames from different
+  origins and checks that entries of localStorage
+  objects don't leak each between other.
+
+  The subsystem is based on postMessage and addEventListener
+  to send messages among different origins. The subsystem waits
+  for both frames be loaded and then alternately calls each frames'
+  doStep() function that on each call proceeds with a single step
+  of the test on its side. This way the subsystem alternate between
+  both frames until both sequences completely finish.
+-->
+
+<script type="text/javascript">
+
+function startTest()
+{
+  masterFrameOrigin = "http://example.com:80";
+  slaveFrameOrigin = "http://example.org:80";
+
+  masterFrame.location = masterFrameOrigin + framePath + "frameGlobalStorageMaster.html";
+  slaveFrame.location = slaveFrameOrigin + framePath + "frameGlobalStorageSlaveNotEqual.html";
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body onload="startTest();">
+  <iframe src="" name="masterFrame"></iframe>
+  <iframe src="" name="slaveFrame"></iframe>
+</body>
+</html>
diff --git a/dom/tests/mochitest/storageevent/test_storageGlobalStorageEventCheckPropagation.html b/dom/tests/mochitest/storageevent/test_storageGlobalStorageEventCheckPropagation.html
new file mode 100644
index 000000000000..542fd5b6249d
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/test_storageGlobalStorageEventCheckPropagation.html
@@ -0,0 +1,44 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>storage event propagation test</title>
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="interOriginTest2.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<!--
+  This test loads two frames from different
+  origins and checks that entries of localStorage
+  objects don't leak each between other.
+
+  The subsystem is based on postMessage and addEventListener
+  to send messages among different origins. The subsystem waits
+  for both frames be loaded and then alternately calls each frames'
+  doStep() function that on each call proceeds with a single step
+  of the test on its side. This way the subsystem alternate between
+  both frames until both sequences completely finish.
+-->
+
+<script type="text/javascript">
+
+function startTest()
+{
+  masterFrameOrigin = "http://example.com:80";
+  slaveFrameOrigin = "http://example.com:80";
+
+  masterFrame.location = masterFrameOrigin + framePath + "frameGlobalStorageMaster.html";
+  slaveFrame.location = slaveFrameOrigin + framePath + "frameGlobalStorageSlaveEqual.html";
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body onload="startTest();">
+  <iframe src="" name="masterFrame"></iframe>
+  <iframe src="" name="slaveFrame"></iframe>
+</body>
+</html>
diff --git a/dom/tests/mochitest/storageevent/test_storageLocalStorageEventCheckNoPropagation.html b/dom/tests/mochitest/storageevent/test_storageLocalStorageEventCheckNoPropagation.html
new file mode 100644
index 000000000000..57b7bdfd7eb3
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/test_storageLocalStorageEventCheckNoPropagation.html
@@ -0,0 +1,44 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>storage event propagation test</title>
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="interOriginTest2.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<!--
+  This test loads two frames from different
+  origins and checks that entries of localStorage
+  objects don't leak each between other.
+
+  The subsystem is based on postMessage and addEventListener
+  to send messages among different origins. The subsystem waits
+  for both frames be loaded and then alternately calls each frames'
+  doStep() function that on each call proceeds with a single step
+  of the test on its side. This way the subsystem alternate between
+  both frames until both sequences completely finish.
+-->
+
+<script type="text/javascript">
+
+function startTest()
+{
+  masterFrameOrigin = "http://example.com:80";
+  slaveFrameOrigin = "http://example.org:80";
+
+  masterFrame.location = masterFrameOrigin + framePath + "frameLocalStorageMaster.html";
+  slaveFrame.location = slaveFrameOrigin + framePath + "frameLocalStorageSlaveNotEqual.html";
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body onload="startTest();">
+  <iframe src="" name="masterFrame"></iframe>
+  <iframe src="" name="slaveFrame"></iframe>
+</body>
+</html>
diff --git a/dom/tests/mochitest/storageevent/test_storageLocalStorageEventCheckPropagation.html b/dom/tests/mochitest/storageevent/test_storageLocalStorageEventCheckPropagation.html
new file mode 100644
index 000000000000..e1bb26d5720b
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/test_storageLocalStorageEventCheckPropagation.html
@@ -0,0 +1,44 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>storage event propagation test</title>
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="interOriginTest2.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<!--
+  This test loads two frames from different
+  origins and checks that entries of localStorage
+  objects don't leak each between other.
+
+  The subsystem is based on postMessage and addEventListener
+  to send messages among different origins. The subsystem waits
+  for both frames be loaded and then alternately calls each frames'
+  doStep() function that on each call proceeds with a single step
+  of the test on its side. This way the subsystem alternate between
+  both frames until both sequences completely finish.
+-->
+
+<script type="text/javascript">
+
+function startTest()
+{
+  masterFrameOrigin = "http://example.com:80";
+  slaveFrameOrigin = "http://example.com:80";
+
+  masterFrame.location = masterFrameOrigin + framePath + "frameLocalStorageMaster.html";
+  slaveFrame.location = slaveFrameOrigin + framePath + "frameLocalStorageSlaveEqual.html";
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body onload="startTest();">
+  <iframe src="" name="masterFrame"></iframe>
+  <iframe src="" name="slaveFrame"></iframe>
+</body>
+</html>
diff --git a/dom/tests/mochitest/storageevent/test_storageSessionStorageEventCheckNoPropagation.html b/dom/tests/mochitest/storageevent/test_storageSessionStorageEventCheckNoPropagation.html
new file mode 100644
index 000000000000..ecc496bc908d
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/test_storageSessionStorageEventCheckNoPropagation.html
@@ -0,0 +1,44 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>storage event propagation test</title>
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="interOriginTest2.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<!--
+  This test loads two frames from different
+  origins and checks that entries of localStorage
+  objects don't leak each between other.
+
+  The subsystem is based on postMessage and addEventListener
+  to send messages among different origins. The subsystem waits
+  for both frames be loaded and then alternately calls each frames'
+  doStep() function that on each call proceeds with a single step
+  of the test on its side. This way the subsystem alternate between
+  both frames until both sequences completely finish.
+-->
+
+<script type="text/javascript">
+
+var slaveFrame = null;
+
+function startTest()
+{
+  masterFrameOrigin = "http://example.org:80";
+  slaveFrameOrigin = "http://example.com:80";
+
+  masterFrame.location = masterFrameOrigin + framePath + "frameSessionStorageMasterNotEqual.html";
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body onload="startTest();">
+  <iframe src="" name="masterFrame"></iframe>
+</body>
+</html>
diff --git a/dom/tests/mochitest/storageevent/test_storageSessionStorageEventCheckPropagation.html b/dom/tests/mochitest/storageevent/test_storageSessionStorageEventCheckPropagation.html
new file mode 100644
index 000000000000..48343b9b573d
--- /dev/null
+++ b/dom/tests/mochitest/storageevent/test_storageSessionStorageEventCheckPropagation.html
@@ -0,0 +1,44 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>storage event propagation test</title>
+
+<script type="text/javascript" src="/MochiKit/packed.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="interOriginTest2.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<!--
+  This test loads two frames from different
+  origins and checks that entries of localStorage
+  objects don't leak each between other.
+
+  The subsystem is based on postMessage and addEventListener
+  to send messages among different origins. The subsystem waits
+  for both frames be loaded and then alternately calls each frames'
+  doStep() function that on each call proceeds with a single step
+  of the test on its side. This way the subsystem alternate between
+  both frames until both sequences completely finish.
+-->
+
+<script type="text/javascript">
+
+var slaveFrame = null;
+
+function startTest()
+{
+  masterFrameOrigin = "http://example.com:80";
+  slaveFrameOrigin = "http://example.com:80";
+
+  masterFrame.location = masterFrameOrigin + framePath + "frameSessionStorageMasterEqual.html";
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</head>
+
+<body onload="startTest();">
+  <iframe src="" name="masterFrame"></iframe>
+</body>
+</html>
diff --git a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
index 6ea021be037f..f7d889f07e97 100644
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -950,7 +950,8 @@ nsWindowWatcher::OpenWindowJSInternal(nsIDOMWindow *aParent,
 
   if (subjectPrincipal && parentDocShell) {
     nsCOMPtr<nsIDOMStorage> storage;
-    parentDocShell->GetSessionStorageForPrincipal(subjectPrincipal, PR_FALSE,
+    parentDocShell->GetSessionStorageForPrincipal(subjectPrincipal,
+                                                  EmptyString(), PR_FALSE,
                                                   getter_AddRefs(storage));
     nsCOMPtr<nsPIDOMStorage> piStorage =
       do_QueryInterface(storage);