Bug 551225 - Make pushState use structured clone. r=zpao, sicking

This commit is contained in:
Justin Lebar 2011-04-24 22:30:54 -04:00
parent 4e3e422fee
commit a8ba1048dc
23 changed files with 513 additions and 157 deletions

View File

@ -1781,8 +1781,9 @@ SessionStoreService.prototype = {
entry.docIdentifier = aEntry.docIdentifier;
}
if (aEntry.stateData) {
entry.stateData = aEntry.stateData;
if (aEntry.stateData != null) {
entry.structuredCloneState = aEntry.stateData.getDataAsBase64();
entry.structuredCloneVersion = aEntry.stateData.formatVersion;
}
if (!(aEntry instanceof Ci.nsISHContainer)) {
@ -2998,8 +2999,13 @@ SessionStoreService.prototype = {
if (aEntry.docshellID)
shEntry.docshellID = aEntry.docshellID;
if (aEntry.stateData) {
shEntry.stateData = aEntry.stateData;
if (aEntry.structuredCloneState && aEntry.structuredCloneVersion) {
shEntry.stateData =
Cc["@mozilla.org/docshell/structured-clone-container;1"].
createInstance(Ci.nsIStructuredCloneContainer);
shEntry.stateData.initFromBase64(aEntry.structuredCloneState,
aEntry.structuredCloneVersion);
}
if (aEntry.scroll) {

View File

@ -68,8 +68,7 @@ function checkState(tab) {
}
else if (popStateCount == 1) {
popStateCount++;
is(JSON.stringify(aEvent.state), JSON.stringify({obj3:3}),
"second popstate object.");
is(aEvent.state.obj3.toString(), '/^a$/', "second popstate object.");
// Make sure that the new-elem node is present in the document. If it's
// not, then this history entry has a different doc identifier than the
@ -120,12 +119,12 @@ function test() {
// history entries:
// testURL (state object: null) <-- oldest
// testURL (state object: {obj1:1})
// page2 (state object: {obj3:3}) <-- newest
// page2 (state object: {obj3:/^a$/}) <-- newest
let contentWindow = tab.linkedBrowser.contentWindow;
let history = contentWindow.history;
history.pushState({obj1:1}, "title-obj1");
history.pushState({obj2:2}, "title-obj2", "page2");
history.replaceState({obj3:3}, "title-obj3");
history.replaceState({obj3:/^a$/}, "title-obj3");
let state = ss.getTabState(tab);
gBrowser.removeTab(tab);

View File

@ -189,6 +189,11 @@ class nsContentUtils
public:
static nsresult Init();
/**
* Get a JSContext from the document's scope object.
*/
static JSContext* GetContextFromDocument(nsIDocument *aDocument);
/**
* Get a scope from aNewDocument. Also get a context through the scope of one
* of the documents, from the stack or the safe context.

View File

@ -67,6 +67,7 @@
#include "nsIDocumentEncoder.h"
#include "nsIAnimationFrameListener.h"
#include "nsEventStates.h"
#include "nsIStructuredCloneContainer.h"
class nsIContent;
class nsPresContext;
@ -123,8 +124,8 @@ class Element;
#define NS_IDOCUMENT_IID \
{ 0x2c6ad63f, 0xb7b9, 0x42f8, \
{ 0xbd, 0xde, 0x76, 0x0a, 0x83, 0xe3, 0xb0, 0x49 } }
{ 0x26ef6218, 0xcd5e, 0x4953, \
{ 0xbb, 0x57, 0xb8, 0x50, 0x29, 0xa1, 0xae, 0x40 } }
// Flag for AddStyleSheet().
#define NS_STYLESHEET_FROM_CATALOG (1 << 0)
@ -1422,12 +1423,13 @@ public:
};
/**
* Set the document's pending state object (as serialized to JSON).
* Set the document's pending state object (as serialized using structured
* clone).
*/
void SetCurrentStateObject(nsAString &obj)
void SetStateObject(nsIStructuredCloneContainer *scContainer)
{
mCurrentStateObject.Assign(obj);
mCurrentStateObjectCached = nsnull;
mStateObjectContainer = scContainer;
mStateObjectCached = nsnull;
}
/**
@ -1515,7 +1517,7 @@ public:
// state is unlocked/false.
virtual nsresult SetImageLockingState(PRBool aLocked) = 0;
virtual nsresult GetMozCurrentStateObject(nsIVariant** aResult) = 0;
virtual nsresult GetStateObject(nsIVariant** aResult) = 0;
protected:
~nsIDocument()
@ -1724,8 +1726,6 @@ protected:
*/
PRUint32 mExternalScriptsBeingEvaluated;
nsString mCurrentStateObject;
// Weak reference to mScriptGlobalObject QI:d to nsPIDOMWindow,
// updated on every set of mSecriptGlobalObject.
nsPIDOMWindow *mWindow;
@ -1741,7 +1741,8 @@ protected:
// Our base target.
nsString mBaseTarget;
nsCOMPtr<nsIVariant> mCurrentStateObjectCached;
nsCOMPtr<nsIStructuredCloneContainer> mStateObjectContainer;
nsCOMPtr<nsIVariant> mStateObjectCached;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)

View File

@ -1391,8 +1391,8 @@ nsContentUtils::InProlog(nsINode *aNode)
return !root || doc->IndexOf(aNode) < doc->IndexOf(root);
}
static JSContext *
GetContextFromDocument(nsIDocument *aDocument)
JSContext *
nsContentUtils::GetContextFromDocument(nsIDocument *aDocument)
{
nsIScriptGlobalObject *sgo = aDocument->GetScopeObject();
if (!sgo) {

View File

@ -1881,7 +1881,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)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStateObjectCached)
// Traverse all our nsCOMArrays.
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mStyleSheets)
@ -8153,47 +8153,22 @@ nsIDocument::ScheduleBeforePaintEvent(nsIAnimationFrameListener* aListener)
}
nsresult
nsDocument::GetMozCurrentStateObject(nsIVariant** aState)
nsDocument::GetStateObject(nsIVariant** aState)
{
// Get the document's current state object. This is the object returned form
// both document.mozCurrentStateObject as well as popStateEvent.state
// Get the document's current state object. This is the object backing both
// history.state and popStateEvent.state.
//
// mStateObjectContainer may be null; this just means that there's no
// current state object.
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);
if (!mStateObjectCached && mStateObjectContainer) {
JSContext *cx = nsContentUtils::GetContextFromDocument(this);
mStateObjectContainer->
DeserializeToVariant(cx, getter_AddRefs(mStateObjectCached));
}
NS_IF_ADDREF(*aState = mCurrentStateObjectCached);
NS_IF_ADDREF(*aState = mStateObjectCached);
return NS_OK;
}

View File

@ -979,7 +979,7 @@ public:
virtual NS_HIDDEN_(nsresult) RemoveImage(imgIRequest* aImage);
virtual NS_HIDDEN_(nsresult) SetImageLockingState(PRBool aLocked);
virtual nsresult GetMozCurrentStateObject(nsIVariant** aResult);
virtual nsresult GetStateObject(nsIVariant** aResult);
protected:
friend class nsNodeUtils;

View File

@ -119,4 +119,5 @@ include $(topsrcdir)/config/rules.mk
LOCAL_INCLUDES += \
-I$(srcdir)/../shistory/src \
-I$(topsrcdir)/layout/base \
-I$(topsrcdir)/js/src/xpconnect/src \
$(NULL)

View File

@ -160,6 +160,8 @@
#include "nsIWebBrowserChrome3.h"
#include "nsITabChild.h"
#include "nsIStrictTransportSecurityService.h"
#include "nsStructuredCloneContainer.h"
#include "nsIStructuredCloneContainer.h"
// Editor-related
#include "nsIEditingSession.h"
@ -7741,21 +7743,17 @@ nsDocShell::SetupNewViewer(nsIContentViewer * aNewViewer)
nsresult
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);
nsCOMPtr<nsIStructuredCloneContainer> scContainer;
nsresult rv = shEntry->GetStateData(getter_AddRefs(scContainer));
NS_ENSURE_SUCCESS(rv, rv);
// if shEntry is null, we just set the pending state object to the
// empty string.
}
// It's OK for scContainer too be null here; that just means there's no
// state data associated with this history entry.
document->SetStateObject(scContainer);
document->SetCurrentStateObject(stateData);
return NS_OK;
}
@ -9413,66 +9411,6 @@ nsDocShell::SetReferrerURI(nsIURI * aURI)
// nsDocShell: Session History
//*****************************************************************************
nsresult
nsDocShell::StringifyJSValVariant(JSContext *aCx, nsIVariant *aData,
nsAString &aResult)
{
nsresult rv;
aResult.Truncate();
// First, try to extract a jsval from the variant |aData|. This works only
// if the variant implements GetAsJSVal.
jsval jsData;
rv = aData->GetAsJSVal(&jsData);
NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIJSContextStack> contextStack;
JSContext *cx = aCx;
if (!cx) {
// Now get the JSContext associated with the current document.
// First get the current document.
nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
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);
cx = (JSContext *)scx->GetNativeContext();
// If our json call triggers a JS-to-C++ call, we want that call to use
// aCx as the context. So we push aCx onto the context stack.
contextStack =
do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
contextStack->Push(cx);
}
nsCOMPtr<nsIJSON> json = do_GetService("@mozilla.org/dom/json;1");
if(json) {
// Do the encoding
rv = json->EncodeFromJSVal(&jsData, cx, aResult);
}
else {
rv = NS_ERROR_FAILURE;
}
if (contextStack) {
if (NS_FAILED(rv)) {
JS_ClearPendingException(cx);
}
contextStack->Pop(&cx);
}
return rv;
}
NS_IMETHODIMP
nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
const nsAString& aURL, PRBool aReplace, JSContext* aCx)
@ -9480,7 +9418,7 @@ nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
// Implements History.pushState and History.replaceState
// Here's what we do, roughly in the order specified by HTML5:
// 1. Serialize aData to JSON.
// 1. Serialize aData using structured clone.
// 2. If the third argument is present,
// a. Resolve the url, relative to the first script's base URL
// b. If (a) fails, raise a SECURITY_ERR
@ -9514,13 +9452,16 @@ nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
nsresult rv;
// Step 1: Clone aData by getting its JSON representation.
//
// StringifyJSValVariant might cause arbitrary JS to run, and this code
// might navigate the page we're on, potentially to a different origin! (bug
nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
// Step 1: Serialize aData using structured clone.
nsCOMPtr<nsIStructuredCloneContainer> scContainer;
// scContainer->Init might cause arbitrary JS to run, and this code might
// navigate the page we're on, potentially to a different origin! (bug
// 634834) To protect against this, we abort if our principal changes due
// to the stringify call.
nsString dataStr;
// to the InitFromVariant() call.
{
nsCOMPtr<nsIDocument> origDocument =
do_GetInterface(GetAsSupports(this));
@ -9528,7 +9469,18 @@ nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
return NS_ERROR_DOM_SECURITY_ERR;
nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
rv = StringifyJSValVariant(aCx, aData, dataStr);
scContainer = new nsStructuredCloneContainer();
JSContext *cx = aCx;
if (!cx) {
cx = nsContentUtils::GetContextFromDocument(document);
}
rv = scContainer->InitFromVariant(aData, cx);
// If we're running in the document's context and the structured clone
// failed, clear the context's pending exception. See bug 637116.
if (NS_FAILED(rv) && !aCx) {
JS_ClearPendingException(aCx);
}
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDocument> newDocument =
@ -9543,7 +9495,7 @@ nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
}
// Check that the state object isn't too long.
// Default max length: 640k chars.
// Default max length: 640k bytes.
PRInt32 maxStateObjSize = 0xA0000;
if (mPrefs) {
mPrefs->GetIntPref("browser.history.maxStateObjectSize",
@ -9552,11 +9504,13 @@ nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
if (maxStateObjSize < 0) {
maxStateObjSize = 0;
}
NS_ENSURE_TRUE(dataStr.Length() <= (PRUint32)maxStateObjSize,
NS_ERROR_ILLEGAL_VALUE);
nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
PRUint64 scSize;
rv = scContainer->GetSerializedNBytes(&scSize);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(scSize <= (PRUint32)maxStateObjSize,
NS_ERROR_ILLEGAL_VALUE);
// Step 2: Resolve aURL
PRBool equalURIs = PR_TRUE;
@ -9693,7 +9647,7 @@ nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
// Step 4: Modify new/original session history entry and clear its POST
// data, if there is any.
newSHEntry->SetStateData(dataStr);
newSHEntry->SetStateData(scContainer);
newSHEntry->SetPostData(nsnull);
// Step 5: If aReplace is false, indicating that we're doing a pushState
@ -9731,7 +9685,7 @@ nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
else {
FireDummyOnLocationChange();
}
document->SetCurrentStateObject(dataStr);
document->SetStateObject(scContainer);
return NS_OK;
}

View File

@ -335,9 +335,9 @@ protected:
nsresult ScrollToAnchor(nsACString & curHash, nsACString & newHash,
PRUint32 aLoadType);
// Tries to stringify a given variant by converting it to JSON. This only
// Tries to serialize a given variant using structured clone. This only
// works if the variant is backed by a JSVal.
nsresult StringifyJSValVariant(JSContext *aCx, nsIVariant *aData,
nsresult SerializeJSValVariant(JSContext *aCx, nsIVariant *aData,
nsAString &aResult);
// Returns PR_TRUE if would have called FireOnLocationChange,

View File

@ -72,6 +72,8 @@
// download history
#include "nsDownloadHistory.h"
#include "nsStructuredCloneContainer.h"
static PRBool gInitialized = PR_FALSE;
// The one time initialization for this module
@ -131,6 +133,8 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsSHistory)
// download history
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDownloadHistory)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsStructuredCloneContainer)
NS_DEFINE_NAMED_CID(NS_DOCSHELL_CID);
NS_DEFINE_NAMED_CID(NS_DEFAULTURIFIXUP_CID);
NS_DEFINE_NAMED_CID(NS_WEBNAVIGATION_INFO_CID);
@ -158,6 +162,7 @@ NS_DEFINE_NAMED_CID(NS_SHTRANSACTION_CID);
NS_DEFINE_NAMED_CID(NS_SHISTORY_CID);
NS_DEFINE_NAMED_CID(NS_SHISTORY_INTERNAL_CID);
NS_DEFINE_NAMED_CID(NS_DOWNLOADHISTORY_CID);
NS_DEFINE_NAMED_CID(NS_STRUCTUREDCLONECONTAINER_CID);
const mozilla::Module::CIDEntry kDocShellCIDs[] = {
@ -188,6 +193,7 @@ const mozilla::Module::CIDEntry kDocShellCIDs[] = {
{ &kNS_SHISTORY_CID, false, NULL, nsSHistoryConstructor },
{ &kNS_SHISTORY_INTERNAL_CID, false, NULL, nsSHistoryConstructor },
{ &kNS_DOWNLOADHISTORY_CID, false, NULL, nsDownloadHistoryConstructor },
{ &kNS_STRUCTUREDCLONECONTAINER_CID, false, NULL, nsStructuredCloneContainerConstructor },
{ NULL }
};
@ -236,6 +242,7 @@ const mozilla::Module::ContractIDEntry kDocShellContracts[] = {
{ NS_SHISTORY_CONTRACTID, &kNS_SHISTORY_CID },
{ NS_SHISTORY_INTERNAL_CONTRACTID, &kNS_SHISTORY_INTERNAL_CID },
{ NS_DOWNLOADHISTORY_CONTRACTID, &kNS_DOWNLOADHISTORY_CID },
{ NS_STRUCTUREDCLONECONTAINER_CONTRACTID, &kNS_STRUCTUREDCLONECONTAINER_CID },
{ NULL }
};

View File

@ -50,6 +50,7 @@ interface nsIURI;
interface nsIInputStream;
interface nsIDocShellTreeItem;
interface nsISupportsArray;
interface nsIStructuredCloneContainer;
%{C++
struct nsIntRect;
class nsDocShellEditorData;
@ -201,9 +202,9 @@ interface nsISHEntry : nsIHistoryEntry
/**
* Get/set data associated with this history state via a pushState() call,
* encoded as JSON.
* serialized using structured clone.
**/
attribute AString stateData;
attribute nsIStructuredCloneContainer stateData;
/**
* Gets the owning pointer to the editor data assosicated with

View File

@ -142,8 +142,8 @@ nsSHEntry::nsSHEntry(const nsSHEntry &other)
, mParent(other.mParent)
, mViewerBounds(0, 0, 0, 0)
, mOwner(other.mOwner)
, mStateData(other.mStateData)
, mDocShellID(other.mDocShellID)
, mStateData(other.mStateData)
{
}
@ -992,16 +992,17 @@ nsSHEntry::HasDetachedEditor()
}
NS_IMETHODIMP
nsSHEntry::GetStateData(nsAString &aStateData)
nsSHEntry::GetStateData(nsIStructuredCloneContainer **aContainer)
{
aStateData.Assign(mStateData);
NS_ENSURE_ARG_POINTER(aContainer);
NS_IF_ADDREF(*aContainer = mStateData);
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::SetStateData(const nsAString &aDataStr)
nsSHEntry::SetStateData(nsIStructuredCloneContainer *aContainer)
{
mStateData.Assign(aDataStr);
mStateData = aContainer;
return NS_OK;
}

View File

@ -117,9 +117,9 @@ private:
nsCOMPtr<nsISupports> mOwner;
nsExpirationState mExpirationState;
nsAutoPtr<nsDocShellEditorData> mEditorData;
nsString mStateData;
PRUint64 mDocShellID;
PRUint32 mLastTouched;
nsCOMPtr<nsIStructuredCloneContainer> mStateData;
};
#endif /* nsSHEntry_h */

View File

@ -87,6 +87,7 @@ _TEST_FILES = \
bug529119-window.html \
test_bug540462.html \
file_bug540462.html \
test_bug551225.html \
test_bug580069.html \
file_bug580069_1.html \
file_bug580069_2.sjs \

View File

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=551225
-->
<head>
<title>Test for Bug 551225</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=551225">Mozilla Bug 551225</a>
<script type="application/javascript;version=1.7">
/** Test for Bug 551225 **/
obj = {
a: new Date('1/1/2000'),
b: /^foo$/,
c: 'bar'
};
history.replaceState(obj, '', '');
is(history.state.a.toString(), new Date('1/1/2000').toString(), 'Date object.');
is(history.state.b.toString(), '/^foo$/', 'Regex');
is(history.state.c, 'bar', 'Other state');
</script>
</body>
</html>

View File

@ -84,6 +84,7 @@ EXPORTS = \
nsFocusManager.h \
nsWrapperCache.h \
nsContentPermissionHelper.h \
nsStructuredCloneContainer.h \
$(NULL)
CPPSRCS = \
@ -108,6 +109,7 @@ CPPSRCS = \
nsDOMScriptObjectFactory.cpp \
nsQueryContentEventResult.cpp \
nsContentPermissionHelper.cpp \
nsStructuredCloneContainer.cpp \
$(NULL)
include $(topsrcdir)/dom/dom-config.mk

View File

@ -7772,10 +7772,10 @@ nsGlobalWindow::DispatchSyncPopState()
}
// 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.
// going to send along with the popstate event. The object is serialized
// using structured clone.
nsCOMPtr<nsIVariant> stateObj;
rv = mDoc->GetMozCurrentStateObject(getter_AddRefs(stateObj));
rv = mDoc->GetStateObject(getter_AddRefs(stateObj));
NS_ENSURE_SUCCESS(rv, rv);
// Obtain a presentation shell for use in creating a popstate event.

View File

@ -354,7 +354,7 @@ nsHistory::GetState(nsIVariant **aState)
if (!doc)
return NS_ERROR_NOT_AVAILABLE;
return doc->GetMozCurrentStateObject(aState);
return doc->GetStateObject(aState);
}
NS_IMETHODIMP

View File

@ -0,0 +1,195 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sw=2 et tw=80:
*
* ***** 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 Initial Developer of the Original Code is the Mozilla Foundation.
*
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Justin Lebar <justin.lebar@gmail.com>
*
* 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 "nsStructuredCloneContainer.h"
#include "nsCOMPtr.h"
#include "nsIDocument.h"
#include "nsIJSContextStack.h"
#include "nsIScriptContext.h"
#include "nsIVariant.h"
#include "nsServiceManagerUtils.h"
#include "nsContentUtils.h"
#include "xpcprivate.h"
NS_IMPL_ADDREF(nsStructuredCloneContainer)
NS_IMPL_RELEASE(nsStructuredCloneContainer)
NS_INTERFACE_MAP_BEGIN(nsStructuredCloneContainer)
NS_INTERFACE_MAP_ENTRY(nsIStructuredCloneContainer)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
nsStructuredCloneContainer::nsStructuredCloneContainer()
: mData(nsnull), mSize(0), mVersion(0)
{
}
nsStructuredCloneContainer::~nsStructuredCloneContainer()
{
free(mData);
}
nsresult
nsStructuredCloneContainer::InitFromVariant(nsIVariant *aData, JSContext *aCx)
{
NS_ENSURE_STATE(!mData);
NS_ENSURE_ARG_POINTER(aData);
NS_ENSURE_ARG_POINTER(aCx);
// First, try to extract a jsval from the variant |aData|. This works only
// if the variant implements GetAsJSVal.
jsval jsData;
nsresult rv = aData->GetAsJSVal(&jsData);
NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
// Make sure that we serialize in the right context.
JSAutoRequest ar(aCx);
JSAutoEnterCompartment ac;
NS_ENSURE_STATE(ac.enter(aCx, JS_GetGlobalObject(aCx)));
nsCxPusher cxPusher;
cxPusher.Push(aCx);
PRUint64* jsBytes = nsnull;
PRBool success = JS_WriteStructuredClone(aCx, jsData, &jsBytes, &mSize,
nsnull, nsnull);
NS_ENSURE_STATE(success);
NS_ENSURE_STATE(jsBytes);
// Copy jsBytes into our own buffer.
mData = (PRUint64*) malloc(mSize);
if (!mData) {
mSize = 0;
mVersion = 0;
return NS_ERROR_FAILURE;
}
else {
mVersion = JS_STRUCTURED_CLONE_VERSION;
}
memcpy(mData, jsBytes, mSize);
return NS_OK;
}
nsresult
nsStructuredCloneContainer::InitFromBase64(const nsAString &aData,
PRUint32 aFormatVersion,
JSContext *aCx)
{
NS_ENSURE_STATE(!mData);
NS_ConvertUTF16toUTF8 data(aData);
nsCAutoString binaryData;
nsresult rv = nsXPConnect::Base64Decode(data, binaryData);
NS_ENSURE_SUCCESS(rv, rv);
// Copy the string's data into our own buffer.
mData = (PRUint64*) malloc(binaryData.Length());
NS_ENSURE_STATE(mData);
memcpy(mData, binaryData.get(), binaryData.Length());
mSize = binaryData.Length();
mVersion = aFormatVersion;
return NS_OK;
}
nsresult
nsStructuredCloneContainer::DeserializeToVariant(JSContext *aCx,
nsIVariant **aData)
{
NS_ENSURE_STATE(mData);
NS_ENSURE_ARG_POINTER(aData);
*aData = nsnull;
// Deserialize to a jsval.
jsval jsStateObj;
PRBool success = JS_ReadStructuredClone(aCx, mData, mSize, mVersion,
&jsStateObj, nsnull, nsnull);
NS_ENSURE_STATE(success);
// Now wrap the jsval as an nsIVariant.
nsCOMPtr<nsIVariant> varStateObj;
nsCOMPtr<nsIXPConnect> xpconnect = do_GetService(nsIXPConnect::GetCID());
NS_ENSURE_STATE(xpconnect);
xpconnect->JSValToVariant(aCx, &jsStateObj, getter_AddRefs(varStateObj));
NS_ENSURE_STATE(varStateObj);
NS_IF_ADDREF(*aData = varStateObj);
return NS_OK;
}
nsresult
nsStructuredCloneContainer::GetDataAsBase64(nsAString &aOut)
{
NS_ENSURE_STATE(mData);
aOut.Truncate();
nsCAutoString binaryData(reinterpret_cast<char*>(mData), mSize);
nsCAutoString base64Data;
nsresult rv = nsXPConnect::Base64Encode(binaryData, base64Data);
NS_ENSURE_SUCCESS(rv, rv);
aOut.Assign(NS_ConvertASCIItoUTF16(base64Data));
return NS_OK;
}
nsresult
nsStructuredCloneContainer::GetSerializedNBytes(PRUint64 *aSize)
{
NS_ENSURE_STATE(mData);
NS_ENSURE_ARG_POINTER(aSize);
// mSize is a size_t, while aSize is a PRUint64. We rely on an implicit cast
// here so that we'll get a compile error if a size_t-to-uint64 cast is
// narrowing.
*aSize = mSize;
return NS_OK;
}
nsresult
nsStructuredCloneContainer::GetFormatVersion(PRUint32 *aFormatVersion)
{
NS_ENSURE_STATE(mData);
NS_ENSURE_ARG_POINTER(aFormatVersion);
*aFormatVersion = mVersion;
return NS_OK;
}

View File

@ -0,0 +1,73 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sw=2 et tw=80:
*
* ***** 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 Initial Developer of the Original Code is the Mozilla Foundation.
*
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Justin Lebar <justin.lebar@gmail.com>
*
* 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 ***** */
#ifndef nsStructuredCloneContainer_h__
#define nsStructuredCloneContainer_h__
#include "nsIStructuredCloneContainer.h"
#include "jsapi.h"
#define NS_STRUCTUREDCLONECONTAINER_CLASSNAME "nsStructuredCloneContainer"
#define NS_STRUCTUREDCLONECONTAINER_CONTRACTID \
"@mozilla.org/docshell/structured-clone-container;1"
#define NS_STRUCTUREDCLONECONTAINER_CID \
{ /* 38bd0634-0fd4-46f0-b85f-13ced889eeec */ \
0x38bd0634, \
0x0fd4, \
0x46f0, \
{0xb8, 0x5f, 0x13, 0xce, 0xd8, 0x89, 0xee, 0xec} \
}
class nsStructuredCloneContainer : public nsIStructuredCloneContainer
{
public:
nsStructuredCloneContainer();
~nsStructuredCloneContainer();
NS_DECL_ISUPPORTS
NS_DECL_NSISTRUCTUREDCLONECONTAINER
private:
PRUint64* mData;
// This needs to be size_t rather than a PR-type so it matches the JS API.
size_t mSize;
PRUint32 mVersion;
};
#endif

View File

@ -87,6 +87,7 @@ XPIDLSRCS = \
nsITabChild.idl \
nsITabParent.idl \
nsIDOMGlobalPropertyInitializer.idl \
nsIStructuredCloneContainer.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,101 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sw=2 et tw=80:
*
* ***** 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 Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Justin Lebar <justin.lebar@gmail.com>
*
* 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 "nsISupports.idl"
interface nsIVariant;
interface nsIDocument;
%{C++
struct JSContext;
%}
/**
* This interface acts as a container for an object serialized using the
* structured clone algorithm.
*
* You can copy an object into an nsIStructuredCloneContainer using
* initFromVariant or initFromBase64. It's an error to initialize an
* nsIStructuredCloneContainer more than once.
*
* Once you've initialized the container, you can get a copy of the object it
* stores by calling deserializeToVariant. You can also get a base-64-encoded
* string containing a copy of the container's serialized data, using
* getDataAsBase64.
*/
[scriptable, uuid(400a282d-7157-4ed0-85b4-8bdc2fa634cd)]
interface nsIStructuredCloneContainer : nsISupports
{
/**
* Initialize this structured clone container so it contains a clone of the
* given variant. aData must be backed by a jsval.
*/
[implicit_jscontext]
void initFromVariant(in nsIVariant aData);
/**
* Initialize this structured clone container from a base-64-encoded byte
* stream, stored in aData. aFormatVersion should be the version of the
* structured clone algorithm which was used to generate aData.
*/
[implicit_jscontext]
void initFromBase64(in AString aData,in unsigned long aFormatVersion);
/**
* Deserialize the object this conatiner holds, returning it wrapped as
* an nsIVariant.
*/
[implicit_jscontext]
nsIVariant deserializeToVariant();
/**
* Get this structured clone container's data as a base-64-encoded string.
*/
AString getDataAsBase64();
/**
* Get the size in bytes of this container's serialized data.
*/
readonly attribute unsigned long long serializedNBytes;
/**
* Get the version of the structured clone algorithm which was used to
* generate this container's serialized buffer.
*/
readonly attribute unsigned long formatVersion;
};