Bug 634435: Add a property to expose the current pushState-state. r=jlebar a=beltzner

This commit is contained in:
Jonas Sicking 2011-02-17 12:44:04 -08:00
Родитель 39318298b6
Коммит 8147551d23
9 изменённых файлов: 89 добавлений и 76 удалений

Просмотреть файл

@ -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<nsIVariant> mCurrentStateObjectCached;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)

Просмотреть файл

@ -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<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)
{

Просмотреть файл

@ -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

Просмотреть файл

@ -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<nsPIDOMWindow> window = do_QueryInterface(mScriptGlobal);

Просмотреть файл

@ -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();

Просмотреть файл

@ -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 \

Просмотреть файл

@ -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,61 +7716,18 @@ 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<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;

Просмотреть файл

@ -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;
};

Просмотреть файл

@ -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());