Bug 634435: Add a property to expose the current pushState-state. r=jlebar a=beltzner
authorJonas Sicking <jonas@sicking.cc>
Thu, 17 Feb 2011 12:44:04 -0800
changeset 62765 06e5c81524e9744145a6bed7197625ecb6214b7d
parent 62764 2f08f950bffe469a93098ed52a46539c8609d89a
child 62766 3a1bdbbca259149b682d270a0a363e172756c195
push idunknown
push userunknown
push dateunknown
reviewersjlebar, beltzner
bugs634435
milestone2.0b12pre
Bug 634435: Add a property to expose the current pushState-state. r=jlebar a=beltzner
content/base/public/nsIDocument.h
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
dom/base/nsDOMClassInfo.cpp
dom/base/nsGlobalWindow.cpp
dom/interfaces/core/nsIDOMNSDocument.idl
dom/tests/mochitest/whatwg/test_bug500328.html
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -50,16 +50,17 @@
 #include "mozFlushType.h"
 #include "nsIAtom.h"
 #include "nsCompatibility.h"
 #include "nsTObserverArray.h"
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "nsNodeInfoManager.h"
 #include "nsIStreamListener.h"
+#include "nsIVariant.h"
 #include "nsIObserver.h"
 #include "nsGkAtoms.h"
 #include "nsAutoPtr.h"
 #include "nsPIDOMWindow.h"
 #ifdef MOZ_SMIL
 #include "nsSMILAnimationController.h"
 #endif // MOZ_SMIL
 #include "nsIScriptGlobalObject.h"
@@ -1418,35 +1419,22 @@ public:
     Doc_Theme_Uninitialized, // not determined yet
     Doc_Theme_None,
     Doc_Theme_Neutral,
     Doc_Theme_Dark,
     Doc_Theme_Bright
   };
 
   /**
-   * Returns the document's pending state object (serialized to JSON), or the
-   * empty string if one doesn't exist.
-   *
-   * This field serves as a waiting place for the history entry's state object:
-   * We set the field's value to the history entry's state object early on in
-   * the load, then after we fire onload we deserialize the field's value and
-   * fire a popstate event containing the resulting object.
-   */
-  nsAString& GetPendingStateObject()
-  {
-    return mPendingStateObject;
-  }
-
-  /**
    * Set the document's pending state object (as serialized to JSON).
    */
-  void SetPendingStateObject(nsAString &obj)
+  void SetCurrentStateObject(nsAString &obj)
   {
-    mPendingStateObject.Assign(obj);
+    mCurrentStateObject.Assign(obj);
+    mCurrentStateObjectCached = nsnull;
   }
 
   /**
    * Returns Doc_Theme_None if there is no lightweight theme specified,
    * Doc_Theme_Dark for a dark theme, Doc_Theme_Bright for a light theme, and
    * Doc_Theme_Neutral for any other theme. This is used to determine the state
    * of the pseudoclasses :-moz-lwtheme and :-moz-lwtheme-text.
    */
@@ -1731,32 +1719,34 @@ protected:
   PRUint32 mEventsSuppressed;
 
   /**
    * The number number of external scripts (ones with the src attribute) that
    * have this document as their owner and that are being evaluated right now.
    */
   PRUint32 mExternalScriptsBeingEvaluated;
 
-  nsString mPendingStateObject;
+  nsString mCurrentStateObject;
 
   // Weak reference to mScriptGlobalObject QI:d to nsPIDOMWindow,
   // updated on every set of mSecriptGlobalObject.
   nsPIDOMWindow *mWindow;
 
   nsCOMPtr<nsIDocumentEncoder> mCachedEncoder;
 
   AnimationListenerList mAnimationFrameListeners;
 
   // The session history entry in which we're currently bf-cached. Non-null
   // if and only if we're currently in the bfcache.
   nsISHEntry* mSHEntry;
 
   // Our base target.
   nsString mBaseTarget;
+
+  nsCOMPtr<nsIVariant> mCurrentStateObjectCached;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
 
 /**
  * mozAutoSubtreeModified batches DOM mutations so that a DOMSubtreeModified
  * event is dispatched, if necessary, when the outermost mozAutoSubtreeModified
  * object is deleted.
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -99,16 +99,17 @@
 #include "nsTreeWalker.h"
 
 #include "nsIServiceManager.h"
 
 #include "nsContentCID.h"
 #include "nsDOMError.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
+#include "nsIJSON.h"
 #include "nsThreadUtils.h"
 #include "nsNodeInfoManager.h"
 #include "nsIXBLService.h"
 #include "nsIXPointer.h"
 #include "nsIFileChannel.h"
 #include "nsIMultiPartChannel.h"
 #include "nsIRefreshURI.h"
 #include "nsIWebNavigation.h"
@@ -1721,16 +1722,17 @@ NS_INTERFACE_TABLE_HEAD(nsDocument)
     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIScriptObjectPrincipal)
     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOM3EventTarget)
     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNSEventTarget)
     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsPIDOMEventTarget)
     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsISupportsWeakReference)
     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer)
     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver)
     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer)
+    NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNSDocument_MOZILLA_2_0_BRANCH)
   NS_OFFSET_AND_INTERFACE_TABLE_END
   NS_OFFSET_AND_INTERFACE_TABLE_TO_MAP_SEGUE
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsDocument)
   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOM3Node, new nsNode3Tearoff(this))
   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMXPathNSResolver,
                                  new nsNode3Tearoff(this))
   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMNodeSelector,
                                  new nsNodeSelectorTearoff(this))
@@ -1900,16 +1902,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptEventManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mXPathEvaluatorTearoff)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLayoutHistoryState)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnloadBlocker)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFirstBaseNodeWithHref)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDOMImplementation)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOriginalDocument)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCachedEncoder)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCurrentStateObjectCached)
 
   // Traverse all our nsCOMArrays.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mStyleSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mCatalogSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mPreloadingImages)
 
   for (PRUint32 i = 0; i < tmp->mAnimationFrameListeners.Length(); ++i) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAnimationFrameListeners[i]");
@@ -8117,16 +8120,62 @@ nsIDocument::ScheduleBeforePaintEvent(ns
     mHavePendingPaint =
       !mPresShell ||
       mPresShell->GetPresContext()->RefreshDriver()->
         ScheduleBeforePaintEvent(this);
   }
 
 }
 
+NS_IMETHODIMP
+nsDocument::GetMozCurrentStateObject(nsIVariant** aState)
+{
+  // Get the document's current state object. This is the object returned form
+  // both document.mozCurrentStateObject as well as popStateEvent.state
+
+  nsCOMPtr<nsIVariant> stateObj;
+  // Parse the JSON, if there's any to parse.
+  if (!mCurrentStateObjectCached && !mCurrentStateObject.IsEmpty()) {
+    // Get the JSContext associated with this document. We need this for
+    // deserialization.
+    nsIScriptGlobalObject *sgo = GetScopeObject();
+    NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE);
+
+    nsIScriptContext *scx = sgo->GetContext();
+    NS_ENSURE_TRUE(scx, NS_ERROR_FAILURE);
+
+    JSContext *cx = (JSContext*) scx->GetNativeContext();
+
+    // Make sure we in the request while we have jsval on the native stack.
+    JSAutoRequest ar(cx);
+
+    // If our json call triggers a JS-to-C++ call, we want that call to use cx
+    // as the context.  So we push cx onto the context stack.
+    nsCxPusher cxPusher;
+
+    jsval jsStateObj = JSVAL_NULL;
+
+    // Deserialize the state object into an nsIVariant.
+    nsCOMPtr<nsIJSON> json = do_GetService("@mozilla.org/dom/json;1");
+    NS_ENSURE_TRUE(cxPusher.Push(cx), NS_ERROR_FAILURE);
+    nsresult rv = json->DecodeToJSVal(mCurrentStateObject, cx, &jsStateObj);
+    NS_ENSURE_SUCCESS(rv, rv);
+    cxPusher.Pop();
+
+    nsCOMPtr<nsIXPConnect> xpconnect = do_GetService(nsIXPConnect::GetCID());
+    NS_ENSURE_TRUE(xpconnect, NS_ERROR_FAILURE);
+    rv = xpconnect->JSValToVariant(cx, &jsStateObj, getter_AddRefs(mCurrentStateObjectCached));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  NS_IF_ADDREF(*aState = mCurrentStateObjectCached);
+  
+  return NS_OK;
+}
+
 nsresult
 nsDocument::AddImage(imgIRequest* aImage)
 {
   NS_ENSURE_ARG_POINTER(aImage);
 
   // See if the image is already in the hashtable. If it is, get the old count.
   PRUint32 oldCount = 0;
   mImageTracker.Get(aImage, &oldCount);
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -512,17 +512,18 @@ class nsDocument : public nsIDocument,
                    public nsIDOM3Document,
                    public nsSupportsWeakReference,
                    public nsIDOMEventTarget,
                    public nsIDOM3EventTarget,
                    public nsIDOMNSEventTarget,
                    public nsIScriptObjectPrincipal,
                    public nsIRadioGroupContainer,
                    public nsIApplicationCacheContainer,
-                   public nsStubMutationObserver
+                   public nsStubMutationObserver,
+                   public nsIDOMNSDocument_MOZILLA_2_0_BRANCH
 {
 public:
   typedef mozilla::dom::Element Element;
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
   using nsINode::GetScriptTypeID;
 
@@ -803,16 +804,17 @@ public:
   // nsIDOM3Document
   NS_DECL_NSIDOM3DOCUMENT
 
   // nsIDOMXMLDocument
   NS_DECL_NSIDOMXMLDOCUMENT
 
   // nsIDOMNSDocument
   NS_DECL_NSIDOMNSDOCUMENT
+  NS_DECL_NSIDOMNSDOCUMENT_MOZILLA_2_0_BRANCH
 
   // nsIDOMDocumentEvent
   NS_DECL_NSIDOMDOCUMENTEVENT
 
   // nsIDOM3DocumentEvent
   NS_DECL_NSIDOM3DOCUMENTEVENT
 
   // nsIDOMDocumentStyle
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -6069,17 +6069,17 @@ nsDocShell::EndPageLoad(nsIWebProgress *
     //
     nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
 
     // Notify the ContentViewer that the Document has finished loading.  This
     // will cause any OnLoad(...) and PopState(...) handlers to fire.
     if (!mEODForCurrentDocument && mContentViewer) {
         // Set the pending state object which will be returned to the page in
         // the popstate event.
-        SetDocPendingStateObj(mLSHE);
+        SetDocCurrentStateObj(mLSHE);
 
         mIsExecutingOnLoadHandler = PR_TRUE;
         rv = mContentViewer->LoadComplete(aStatus);
 
         // If the load wasn't stopped during LoadComplete, fire the popstate
         // event, if we're not suppressing it.
         if (NS_SUCCEEDED(rv) && rv != NS_SUCCESS_LOAD_STOPPED &&
             !mSuppressPopstate) {
@@ -7761,33 +7761,33 @@ nsDocShell::SetupNewViewer(nsIContentVie
     // We don't show the mContentViewer yet, since we want to draw the old page
     // until we have enough of the new page to show.  Just return with the new
     // viewer still set to hidden.
 
     return NS_OK;
 }
 
 nsresult
-nsDocShell::SetDocPendingStateObj(nsISHEntry *shEntry)
+nsDocShell::SetDocCurrentStateObj(nsISHEntry *shEntry)
 {
     nsresult rv;
 
     nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
     NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
 
     nsAutoString stateData;
     if (shEntry) {
         rv = shEntry->GetStateData(stateData);
         NS_ENSURE_SUCCESS(rv, rv);
 
         // if shEntry is null, we just set the pending state object to the
         // empty string.
     }
 
-    document->SetPendingStateObject(stateData);
+    document->SetCurrentStateObject(stateData);
     return NS_OK;
 }
 
 nsresult
 nsDocShell::CheckLoadingPermissions()
 {
     // This method checks whether the caller may load content into
     // this docshell. Even though we've done our best to hide windows
@@ -8427,17 +8427,17 @@ nsDocShell::InternalLoad(nsIURI * aURI,
                 NS_ENSURE_TRUE(newURI, NS_ERROR_FAILURE);
                 nsCOMPtr<nsIDocument> doc =
                   do_GetInterface(GetAsSupports(this));
                 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
 
                 doc->SetDocumentURI(newURI);
             }
 
-            SetDocPendingStateObj(mOSHE);
+            SetDocCurrentStateObj(mOSHE);
 
             // Dispatch the popstate and hashchange events, as appropriate.
             nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mScriptGlobal);
             if (window) {
                 NS_ASSERTION(!mSuppressPopstate,
                              "Popstate shouldn't be suppressed here.");
 
                 // Pass PR_FALSE to indicate that this is not an "initial" (i.e.
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -559,20 +559,20 @@ protected:
     //
     // Helper method that is called when a new document (including any
     // sub-documents - ie. frames) has been completely loaded.
     //
     virtual nsresult EndPageLoad(nsIWebProgress * aProgress,
                                  nsIChannel * aChannel,
                                  nsresult aResult);
 
-    // Sets the current document's pending state object to the given SHEntry's
-    // state object.  The pending state object is eventually given to the page
+    // Sets the current document's current state object to the given SHEntry's
+    // state object.  The current state object is eventually given to the page
     // in the PopState event.
-    nsresult SetDocPendingStateObj(nsISHEntry *shEntry);
+    nsresult SetDocCurrentStateObj(nsISHEntry *shEntry);
 
     nsresult CheckLoadingPermissions();
 
     // Security checks to prevent frameset spoofing.  See comments at
     // implementation sites.
     static PRBool CanAccessItem(nsIDocShellTreeItem* aTargetItem,
                                 nsIDocShellTreeItem* aAccessingItem,
                                 PRBool aConsiderOpener = PR_TRUE);
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -2188,17 +2188,18 @@ nsDOMClassInfo::WrapNativeParent(JSConte
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDocumentRange)                              \
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDocumentTraversal)                          \
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDocumentXBL)                                \
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSEventTarget)                              \
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)                                \
     DOM_CLASSINFO_MAP_ENTRY(nsIDOM3Document)                                  \
     DOM_CLASSINFO_MAP_ENTRY(nsIDOM3Node)                                      \
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMXPathEvaluator)                             \
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMNodeSelector)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMNodeSelector)                               \
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSDocument_MOZILLA_2_0_BRANCH)
 
 
 #define DOM_CLASSINFO_GENERIC_HTML_MAP_ENTRIES                                \
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSHTMLElement)                              \
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMElementCSSInlineStyle)                      \
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSEventTarget)                              \
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)                                \
     DOM_CLASSINFO_MAP_ENTRY(nsIDOM3Node)                                      \
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -178,17 +178,16 @@
 #include "nsIURIFixup.h"
 #include "mozilla/FunctionTimer.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsEventDispatcher.h"
 #include "nsIObserverService.h"
 #include "nsIXULAppInfo.h"
 #include "nsNetUtil.h"
 #include "nsFocusManager.h"
-#include "nsIJSON.h"
 #include "nsIXULWindow.h"
 #include "nsEventStateManager.h"
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #include "nsIDOMXULControlElement.h"
 #include "nsIFrame.h"
 #endif
 
@@ -7712,70 +7711,27 @@ nsGlobalWindow::DispatchSyncPopState(PRB
 
   nsresult rv = NS_OK;
 
   // Bail if the window is frozen.
   if (IsFrozen()) {
     return NS_OK;
   }
 
-  // Bail if there's no document or the document's readystate isn't "complete".
-  if (!mDoc) {
-    return NS_OK;
-  }
-
-  nsIDocument::ReadyState readyState = mDoc->GetReadyStateEnum();
-  if (readyState != nsIDocument::READYSTATE_COMPLETE) {
-    return NS_OK;
-  }
-
   // Get the document's pending state object -- it contains the data we're
   // going to send along with the popstate event.  The object is serialized as
   // JSON.
-  nsAString& stateObjJSON = mDoc->GetPendingStateObject();
-
   nsCOMPtr<nsIVariant> stateObj;
-  // Parse the JSON, if there's any to parse.
-  if (!stateObjJSON.IsEmpty()) {
-    // Get the JSContext associated with our document. We need this for
-    // deserialization.
-    nsCOMPtr<nsIDocument> document = do_QueryInterface(mDocument);
-    NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
-
-    // Get the JSContext from the document, like we do in
-    // nsContentUtils::GetContextFromDocument().
-    nsIScriptGlobalObject *sgo = document->GetScopeObject();
-    NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE);
-
-    nsIScriptContext *scx = sgo->GetContext();
-    NS_ENSURE_TRUE(scx, NS_ERROR_FAILURE);
-
-    JSContext *cx = (JSContext*) scx->GetNativeContext();
-
-    // Make sure we in the request while we have jsval on the native stack.
-    JSAutoRequest ar(cx);
-
-    // If our json call triggers a JS-to-C++ call, we want that call to use cx
-    // as the context.  So we push cx onto the context stack.
-    nsCxPusher cxPusher;
-
-    jsval jsStateObj = JSVAL_NULL;
-
-    // Deserialize the state object into an nsIVariant.
-    nsCOMPtr<nsIJSON> json = do_GetService("@mozilla.org/dom/json;1");
-    NS_ENSURE_TRUE(cxPusher.Push(cx), NS_ERROR_FAILURE);
-    rv = json->DecodeToJSVal(stateObjJSON, cx, &jsStateObj);
-    NS_ENSURE_SUCCESS(rv, rv);
-    cxPusher.Pop();
-
-    nsCOMPtr<nsIXPConnect> xpconnect = do_GetService(nsIXPConnect::GetCID());
-    NS_ENSURE_TRUE(xpconnect, NS_ERROR_FAILURE);
-    rv = xpconnect->JSValToVariant(cx, &jsStateObj, getter_AddRefs(stateObj));
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
+  nsCOMPtr<nsIDOMNSDocument_MOZILLA_2_0_BRANCH> doc2 = do_QueryInterface(mDoc);
+  if (!doc2) {
+    return NS_OK;
+  }
+  
+  rv = doc2->GetMozCurrentStateObject(getter_AddRefs(stateObj));
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // Obtain a presentation shell for use in creating a popstate event.
   nsIPresShell *shell = mDoc->GetShell();
   nsRefPtr<nsPresContext> presContext;
   if (shell) {
     presContext = shell->GetPresContext();
   }
 
--- a/dom/interfaces/core/nsIDOMNSDocument.idl
+++ b/dom/interfaces/core/nsIDOMNSDocument.idl
@@ -37,16 +37,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "domstubs.idl"
 
 interface nsIBoxObject;
 interface nsIDOMLocation;
+interface nsIVariant;
 
 [scriptable, uuid(92f2c6f8-3668-4a47-8251-2a900afe11fa)]
 interface nsIDOMNSDocument : nsISupports
 {
   readonly attribute DOMString      characterSet;
            attribute DOMString      dir;
 
   readonly attribute nsIDOMLocation location;
@@ -124,8 +125,14 @@ interface nsIDOMNSDocument : nsISupports
    * |aImageElement|
    * @param aImageElement a DOM element to be used as the source image of
    * |-moz-element(#aImageElementId)|. If this is null, the function will
    * unregister the image element ID |aImageElementId|.
    */
   void mozSetImageElement(in DOMString aImageElementId,
                           in nsIDOMElement aImageElement);
 };
+
+[scriptable, uuid(fee67aed-3b94-4136-ad7d-fb88ef23f45f)]
+interface nsIDOMNSDocument_MOZILLA_2_0_BRANCH : nsISupports
+{
+  readonly attribute nsIVariant mozCurrentStateObject;
+};
--- a/dom/tests/mochitest/whatwg/test_bug500328.html
+++ b/dom/tests/mochitest/whatwg/test_bug500328.html
@@ -217,40 +217,48 @@ function runTest() {
   // are completely synchronous.  In fact, if we did yield, JS would throw an
   // error because we'd be calling gGen.next from within gGen.next.
   iframeCw.history.back();
 
   statusMsg("Awake after going back to page 1.");
   popstateExpected("Going back to page 1 should trigger a popstate.");
   is(JSON.stringify(gLastPopStateEvent.state), JSON.stringify(testObj1),
      "Wrong state object popped after going back to page 1.");
+  ok(gLastPopStateEvent.state === iframeCw.document.mozCurrentStateObject,
+     "Wrong state object in document after going back to page 1.");
   ok(iframeCw.location.toString().match(/file_bug500328_1.html$/),
       "Going back to page 1 hould take us to original page.");
 
   iframeCw.history.back();
   popstateExpected("Going back to page 0 should trigger a popstate.");
   is(gLastPopStateEvent.state, null,
       "Going back to page 0 should pop a null state.");
+  is(iframeCw.document.mozCurrentStateObject, null,
+      "Going back to page 0 should pop a null state.");
   is(iframeCw.location.search, "",
       "Going back to page 0 should clear the querystring.");
 
   iframeCw.history.forward();
   popstateExpected("Going forward to page 1 should trigger a popstate.");
   is(JSON.stringify(gLastPopStateEvent.state), JSON.stringify(testObj1),
       "Wrong state object popped after going forward to page 1.");
+  ok(gLastPopStateEvent.state === iframeCw.document.mozCurrentStateObject,
+      "Wrong state object in document after going forward to page 1.");
   ok(iframeCw.location.toString().match(/file_bug500328_1.html$/),
       "Going forward to page 1 should leave us at original page.");
 
 
   statusMsg("About to go forward to page 2.");
   iframeCw.history.forward();
   statusMsg("Awake after going forward to page 2.");
   popstateExpected("Going forward to page 2 should trigger a popstate.");
   is(JSON.stringify(gLastPopStateEvent.state), JSON.stringify(testObj2),
      "Wrong state object popped after going forward to page 2.");
+  ok(iframeCw.document.mozCurrentStateObject === gLastPopStateEvent.state,
+     "Wrong state object in document after going forward to page 2.");
   ok(iframeCw.location.toString().match(/file_bug500328_1.html\?test1#foo$/),
      "Going forward to page 2 took us to " + iframeCw.location.toString());
 
   // The iframe's current location is file_bug500328_1.html?test1#foo.
   // Clicking link1 should take us to file_bug500328_1.html?test1#1.
 
   enableChildPopStateCallback();
   sendMouseEvent({type:'click'}, 'link-anchor1', iframeCw);