Bug 634435: Add a property to expose the current pushState-state. r=jlebar a=beltzner
--- 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);