diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index d73da133c7b0..ddf3e6544935 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -55,6 +55,7 @@ #include "nsHashKeys.h" #include "nsNodeInfoManager.h" #include "nsIStreamListener.h" +#include "nsIVariant.h" #include "nsIObserver.h" #include "nsGkAtoms.h" #include "nsAutoPtr.h" @@ -1422,26 +1423,13 @@ public: 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; } /** @@ -1736,7 +1724,7 @@ protected: */ PRUint32 mExternalScriptsBeingEvaluated; - nsString mPendingStateObject; + nsString mCurrentStateObject; // Weak reference to mScriptGlobalObject QI:d to nsPIDOMWindow, // updated on every set of mSecriptGlobalObject. @@ -1752,6 +1740,8 @@ protected: // Our base target. nsString mBaseTarget; + + nsCOMPtr mCurrentStateObjectCached; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID) diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 831999e44214..fd4a06f106c0 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -104,6 +104,7 @@ #include "nsDOMError.h" #include "nsIPresShell.h" #include "nsPresContext.h" +#include "nsIJSON.h" #include "nsThreadUtils.h" #include "nsNodeInfoManager.h" #include "nsIXBLService.h" @@ -1726,6 +1727,7 @@ NS_INTERFACE_TABLE_HEAD(nsDocument) 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) @@ -1905,6 +1907,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument) 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) @@ -8122,6 +8125,52 @@ nsIDocument::ScheduleBeforePaintEvent(nsIAnimationFrameListener* aListener) } +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 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 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 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) { diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index d007e3ef3079..91b6659dc50f 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -517,7 +517,8 @@ class nsDocument : public nsIDocument, public nsIScriptObjectPrincipal, public nsIRadioGroupContainer, public nsIApplicationCacheContainer, - public nsStubMutationObserver + public nsStubMutationObserver, + public nsIDOMNSDocument_MOZILLA_2_0_BRANCH { public: typedef mozilla::dom::Element Element; @@ -808,6 +809,7 @@ public: // nsIDOMNSDocument NS_DECL_NSIDOMNSDOCUMENT + NS_DECL_NSIDOMNSDOCUMENT_MOZILLA_2_0_BRANCH // nsIDOMDocumentEvent NS_DECL_NSIDOMDOCUMENTEVENT diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 2dcfa1c7be6f..10c1a6bc7a9c 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -6074,7 +6074,7 @@ nsDocShell::EndPageLoad(nsIWebProgress * aProgress, 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); @@ -7766,7 +7766,7 @@ nsDocShell::SetupNewViewer(nsIContentViewer * aNewViewer) } nsresult -nsDocShell::SetDocPendingStateObj(nsISHEntry *shEntry) +nsDocShell::SetDocCurrentStateObj(nsISHEntry *shEntry) { nsresult rv; @@ -7782,7 +7782,7 @@ nsDocShell::SetDocPendingStateObj(nsISHEntry *shEntry) // empty string. } - document->SetPendingStateObject(stateData); + document->SetCurrentStateObject(stateData); return NS_OK; } @@ -8432,7 +8432,7 @@ nsDocShell::InternalLoad(nsIURI * aURI, doc->SetDocumentURI(newURI); } - SetDocPendingStateObj(mOSHE); + SetDocCurrentStateObj(mOSHE); // Dispatch the popstate and hashchange events, as appropriate. nsCOMPtr window = do_QueryInterface(mScriptGlobal); diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 4af55061707d..81cd09fb7a9d 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -564,10 +564,10 @@ protected: 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(); diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 7d7f42536877..a6432e6823b6 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -2193,7 +2193,8 @@ nsDOMClassInfo::WrapNativeParent(JSContext *cx, JSObject *scope, 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 \ diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index bc90c1938756..6414f272bf07 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -183,7 +183,6 @@ #include "nsIXULAppInfo.h" #include "nsNetUtil.h" #include "nsFocusManager.h" -#include "nsIJSON.h" #include "nsIXULWindow.h" #include "nsEventStateManager.h" #ifdef MOZ_XUL @@ -7717,60 +7716,17 @@ nsGlobalWindow::DispatchSyncPopState(PRBool aIsInitial) 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 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 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 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 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 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(); diff --git a/dom/interfaces/core/nsIDOMNSDocument.idl b/dom/interfaces/core/nsIDOMNSDocument.idl index 0ecf89efb738..861440217fc5 100644 --- a/dom/interfaces/core/nsIDOMNSDocument.idl +++ b/dom/interfaces/core/nsIDOMNSDocument.idl @@ -42,6 +42,7 @@ interface nsIBoxObject; interface nsIDOMLocation; +interface nsIVariant; [scriptable, uuid(92f2c6f8-3668-4a47-8251-2a900afe11fa)] interface nsIDOMNSDocument : nsISupports @@ -129,3 +130,9 @@ interface nsIDOMNSDocument : nsISupports 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; +}; diff --git a/dom/tests/mochitest/whatwg/test_bug500328.html b/dom/tests/mochitest/whatwg/test_bug500328.html index 63b524d19b1a..c2c4c82a941f 100644 --- a/dom/tests/mochitest/whatwg/test_bug500328.html +++ b/dom/tests/mochitest/whatwg/test_bug500328.html @@ -222,6 +222,8 @@ function runTest() { 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."); @@ -229,6 +231,8 @@ function runTest() { 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."); @@ -236,6 +240,8 @@ function runTest() { 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."); @@ -246,6 +252,8 @@ function runTest() { 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());