Bug 553125: Use the structured clone algorithm for window.postMessage. r=bent

This commit is contained in:
Kyle Huey 2011-05-23 11:53:31 -07:00
parent a67c9aebe9
commit 3d12cc00e3
15 changed files with 316 additions and 32 deletions

View File

@ -1339,10 +1339,33 @@ nsEventSource::DispatchAllMessageEvents()
return;
}
// Let's play get the JSContext
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(mOwner);
NS_ENSURE_TRUE(sgo,);
nsIScriptContext* scriptContext = sgo->GetContext();
NS_ENSURE_TRUE(scriptContext,);
JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
NS_ENSURE_TRUE(cx,);
while (mMessagesToDispatch.GetSize() > 0) {
nsAutoPtr<Message>
message(static_cast<Message*>(mMessagesToDispatch.PopFront()));
// Now we can turn our string into a jsval
jsval jsData;
{
JSString* jsString;
JSAutoRequest ar(cx);
jsString = JS_NewUCStringCopyN(cx,
message->mData.get(),
message->mData.Length());
NS_ENSURE_TRUE(jsString,);
jsData = STRING_TO_JSVAL(jsString);
}
// create an event that uses the MessageEvent interface,
// which does not bubble, is not cancelable, and has no default action
@ -1356,7 +1379,7 @@ nsEventSource::DispatchAllMessageEvents()
nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event);
rv = messageEvent->InitMessageEvent(message->mEventName,
PR_FALSE, PR_FALSE,
message->mData,
jsData,
NS_ConvertUTF8toUTF16(mOrigin),
message->mLastEventID, nsnull);
if (NS_FAILED(rv)) {

View File

@ -819,6 +819,31 @@ nsWebSocket::CreateAndDispatchMessageEvent(const nsACString& aData)
return NS_OK;
}
// Let's play get the JSContext
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(mOwner);
NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE);
nsIScriptContext* scriptContext = sgo->GetContext();
NS_ENSURE_TRUE(scriptContext, NS_ERROR_FAILURE);
JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
// Now we can turn our string into a jsval
jsval jsData;
{
NS_ConvertUTF8toUTF16 utf16Data(aData);
JSString* jsString;
JSAutoRequest ar(cx);
jsString = JS_NewUCStringCopyN(cx,
utf16Data.get(),
utf16Data.Length());
NS_ENSURE_TRUE(jsString, NS_ERROR_FAILURE);
jsData = STRING_TO_JSVAL(jsString);
}
// create an event that uses the MessageEvent interface,
// which does not bubble, is not cancelable, and has no default action
@ -829,7 +854,7 @@ nsWebSocket::CreateAndDispatchMessageEvent(const nsACString& aData)
nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event);
rv = messageEvent->InitMessageEvent(NS_LITERAL_STRING("message"),
PR_FALSE, PR_FALSE,
NS_ConvertUTF8toUTF16(aData),
jsData,
mUTF16Origin,
EmptyString(), nsnull);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -23,10 +23,10 @@ var gen = runTest();
function runTest() {
var iframe = $('iframe');
iframe.src = "http://noxul.example.com/tests/content/base/test/file_bug590870.html";
is((yield), "true", "shouldn't be able to create XUL elements");
is((yield), true, "shouldn't be able to create XUL elements");
iframe.src = "file_bug590870.html";
is((yield), "false", "should be able to create XUL elements");
is((yield), false, "should be able to create XUL elements");
SimpleTest.finish();
yield;

View File

@ -22,7 +22,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=601803
SimpleTest.waitForExplicitFinish();
window.onmessage = function (event) {
is(event.data, "false", "Shouldn't throw when adopting a node cross-compartment");
is(event.data, false, "Shouldn't throw when adopting a node cross-compartment");
SimpleTest.finish();
}

View File

@ -38,17 +38,29 @@
#include "nsDOMMessageEvent.h"
#include "nsContentUtils.h"
#include "jsapi.h"
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMMessageEvent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMMessageEvent, nsDOMEvent)
if (tmp->mDataRooted) {
tmp->UnrootData();
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSource)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMMessageEvent, nsDOMEvent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSource)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMMessageEvent)
if (JSVAL_IS_GCTHING(tmp->mData)) {
void *gcThing = JSVAL_TO_GCTHING(tmp->mData);
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mData")
}
NS_IMPL_CYCLE_COLLECTION_TRACE_END
DOMCI_DATA(MessageEvent, nsDOMMessageEvent)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMMessageEvent)
@ -59,10 +71,41 @@ NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
NS_IMPL_ADDREF_INHERITED(nsDOMMessageEvent, nsDOMEvent)
NS_IMPL_RELEASE_INHERITED(nsDOMMessageEvent, nsDOMEvent)
NS_IMETHODIMP
nsDOMMessageEvent::GetData(nsAString& aData)
nsDOMMessageEvent::nsDOMMessageEvent(nsPresContext* aPresContext,
nsEvent* aEvent)
: nsDOMEvent(aPresContext, aEvent),
mData(JSVAL_VOID),
mDataRooted(false)
{
aData = mData;
}
nsDOMMessageEvent::~nsDOMMessageEvent()
{
if (mDataRooted)
UnrootData();
}
void
nsDOMMessageEvent::RootData()
{
NS_ASSERTION(!mDataRooted, "...");
NS_HOLD_JS_OBJECTS(this, nsDOMMessageEvent);
mDataRooted = true;
}
void
nsDOMMessageEvent::UnrootData()
{
NS_ASSERTION(mDataRooted, "...");
NS_DROP_JS_OBJECTS(this, nsDOMMessageEvent);
mDataRooted = false;
mData = JSVAL_VOID;
}
NS_IMETHODIMP
nsDOMMessageEvent::GetData(jsval* aData)
{
*aData = mData;
return NS_OK;
}
@ -91,7 +134,7 @@ NS_IMETHODIMP
nsDOMMessageEvent::InitMessageEvent(const nsAString& aType,
PRBool aCanBubble,
PRBool aCancelable,
const nsAString& aData,
const jsval& aData,
const nsAString& aOrigin,
const nsAString& aLastEventId,
nsIDOMWindow* aSource)
@ -99,7 +142,12 @@ nsDOMMessageEvent::InitMessageEvent(const nsAString& aType,
nsresult rv = nsDOMEvent::InitEvent(aType, aCanBubble, aCancelable);
NS_ENSURE_SUCCESS(rv, rv);
// Allowing double-initialization seems a little silly, but we have a test
// for it so it might be important ...
if (mDataRooted)
UnrootData();
mData = aData;
RootData();
mOrigin = aOrigin;
mLastEventId = aLastEventId;
mSource = aSource;
@ -113,8 +161,6 @@ NS_NewDOMMessageEvent(nsIDOMEvent** aInstancePtrResult,
nsEvent* aEvent)
{
nsDOMMessageEvent* it = new nsDOMMessageEvent(aPresContext, aEvent);
if (nsnull == it)
return NS_ERROR_OUT_OF_MEMORY;
return CallQueryInterface(it, aInstancePtrResult);
}

View File

@ -54,21 +54,23 @@ class nsDOMMessageEvent : public nsDOMEvent,
public nsIDOMMessageEvent
{
public:
nsDOMMessageEvent(nsPresContext* aPresContext, nsEvent* aEvent)
: nsDOMEvent(aPresContext, aEvent)
{
}
nsDOMMessageEvent(nsPresContext* aPresContext, nsEvent* aEvent);
~nsDOMMessageEvent();
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDOMMessageEvent, nsDOMEvent)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsDOMMessageEvent,
nsDOMEvent)
NS_DECL_NSIDOMMESSAGEEVENT
// Forward to base class
NS_FORWARD_TO_NSDOMEVENT
void RootData();
void UnrootData();
private:
nsString mData;
jsval mData;
bool mDataRooted;
nsString mOrigin;
nsString mLastEventId;
nsCOMPtr<nsIDOMWindow> mSource;

View File

@ -5843,13 +5843,13 @@ class PostMessageEvent : public nsRunnable
PostMessageEvent(nsGlobalWindow* aSource,
const nsAString& aCallerOrigin,
const nsAString& aMessage,
nsGlobalWindow* aTargetWindow,
nsIURI* aProvidedOrigin,
PRBool aTrustedCaller)
: mSource(aSource),
mCallerOrigin(aCallerOrigin),
mMessage(aMessage),
mMessage(nsnull),
mMessageLen(0),
mTargetWindow(aTargetWindow),
mProvidedOrigin(aProvidedOrigin),
mTrustedCaller(aTrustedCaller)
@ -5859,13 +5859,21 @@ class PostMessageEvent : public nsRunnable
~PostMessageEvent()
{
NS_ASSERTION(!mMessage, "Message should have been deserialized!");
MOZ_COUNT_DTOR(PostMessageEvent);
}
void SetJSData(JSAutoStructuredCloneBuffer& aBuffer)
{
NS_ASSERTION(!mMessage && mMessageLen == 0, "Don't call twice!");
aBuffer.steal(&mMessage, &mMessageLen);
}
private:
nsRefPtr<nsGlobalWindow> mSource;
nsString mCallerOrigin;
nsString mMessage;
uint64* mMessage;
size_t mMessageLen;
nsRefPtr<nsGlobalWindow> mTargetWindow;
nsCOMPtr<nsIURI> mProvidedOrigin;
PRBool mTrustedCaller;
@ -5879,6 +5887,22 @@ PostMessageEvent::Run()
NS_ABORT_IF_FALSE(!mSource || mSource->IsOuterWindow(),
"should have been passed an outer window!");
// Get the JSContext for the target window
nsIScriptContext* scriptContext = mTargetWindow->GetContext();
NS_ENSURE_TRUE(scriptContext, NS_ERROR_FAILURE);
JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
// If we bailed before this point we're going to leak mMessage, but
// that's probably better than crashing.
// Ensure that the buffer is freed even if we fail to post the message
JSAutoStructuredCloneBuffer buffer;
buffer.adopt(cx, mMessage, mMessageLen);
mMessage = nsnull;
mMessageLen = 0;
nsRefPtr<nsGlobalWindow> targetWindow;
if (mTargetWindow->IsClosedOrClosing() ||
!(targetWindow = mTargetWindow->GetCurrentInnerWindowInternal()) ||
@ -5924,6 +5948,14 @@ PostMessageEvent::Run()
return NS_OK;
}
// Deserialize the structured clone data
jsval messageData;
{
JSAutoRequest ar(cx);
if (!buffer.read(&messageData, cx, nsnull))
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
// Create the event
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(targetWindow->mDocument);
@ -5939,7 +5971,7 @@ PostMessageEvent::Run()
nsresult rv = message->InitMessageEvent(NS_LITERAL_STRING("message"),
PR_FALSE /* non-bubbling */,
PR_TRUE /* cancelable */,
mMessage,
messageData,
mCallerOrigin,
EmptyString(),
mSource);
@ -5971,9 +6003,12 @@ PostMessageEvent::Run()
}
NS_IMETHODIMP
nsGlobalWindow::PostMessageMoz(const nsAString& aMessage, const nsAString& aOrigin)
nsGlobalWindow::PostMessageMoz(const jsval& aMessage,
const nsAString& aOrigin,
JSContext* aCx)
{
FORWARD_TO_OUTER(PostMessageMoz, (aMessage, aOrigin), NS_ERROR_NOT_INITIALIZED);
FORWARD_TO_OUTER(PostMessageMoz, (aMessage, aOrigin, aCx),
NS_ERROR_NOT_INITIALIZED);
//
// Window.postMessage is an intentional subversion of the same-origin policy.
@ -6037,10 +6072,19 @@ nsGlobalWindow::PostMessageMoz(const nsAString& aMessage, const nsAString& aOrig
? nsnull
: callerInnerWin->GetOuterWindowInternal(),
origin,
aMessage,
this,
providedOrigin,
nsContentUtils::IsCallerTrustedForWrite());
// We *must* clone the data here, or the jsval could be modified
// by script
JSAutoStructuredCloneBuffer buffer;
if (!buffer.write(aCx, aMessage, nsnull, nsnull))
return NS_ERROR_DOM_DATA_CLONE_ERR;
event->SetJSData(buffer);
return NS_DispatchToCurrentThread(event);
}

View File

@ -80,8 +80,8 @@ class nsIArray;
class nsPIWindowRoot;
#define NS_PIDOMWINDOW_IID \
{ 0xafc4849b, 0x21d3, 0x45ea, \
{ 0x8b, 0xfd, 0x61, 0xec, 0x12, 0x5d, 0x38, 0x64 } }
{ 0x176e69ce, 0x25d3, 0x4f2a, \
{ 0x9d, 0x99, 0x81, 0xa3, 0x9a, 0xfd, 0xe2, 0xf0 } }
class nsPIDOMWindow : public nsIDOMWindowInternal
{

View File

@ -39,6 +39,10 @@
#include "nsIDOMWindow2.idl"
%{ C++
#include "jspubtd.h"
%}
interface nsIPrompt;
interface nsIControllers;
interface nsIDOMLocation;
@ -46,7 +50,7 @@ interface nsIVariant;
interface nsIAnimationFrameListener;
interface nsIDOMMediaQueryList;
[scriptable, uuid(4d95736f-8130-43cb-a276-5bc554eca80a)]
[scriptable, uuid(3a7b0839-b9d6-42ff-8ba6-910aba60a966)]
interface nsIDOMWindowInternal : nsIDOMWindow2
{
readonly attribute nsIDOMWindowInternal window;
@ -214,8 +218,8 @@ interface nsIDOMWindowInternal : nsIDOMWindow2
*
* See the WHATWG HTML5 specification, section 6.4, for more details.
*/
[binaryname(PostMessageMoz)] void postMessage(in DOMString message,
in DOMString targetOrigin);
[implicit_jscontext, binaryname(PostMessageMoz)]
void postMessage(in jsval message, in DOMString targetOrigin);
/**
* Returns the number of times this document for this window has

View File

@ -38,6 +38,10 @@
#include "nsIDOMEvent.idl"
%{ C++
#include "jspubtd.h"
%}
/**
* The nsIDOMMessageEvent interface is used for server-sent events and for
* cross-domain messaging.
@ -45,13 +49,13 @@
* For more information on this interface, please see
* http://www.whatwg.org/specs/web-apps/current-work/#messageevent
*/
[scriptable, uuid(d02e9a24-e5b2-47e4-bd80-7e980bf56b45)]
[scriptable, uuid(dc8ec5c6-ebf2-4f95-be99-cd13e3c0d0c6)]
interface nsIDOMMessageEvent : nsIDOMEvent
{
/**
* Custom string data associated with this event.
*/
readonly attribute DOMString data;
readonly attribute jsval data;
/**
* The origin of the site from which this event originated, which is the
@ -80,7 +84,7 @@ interface nsIDOMMessageEvent : nsIDOMEvent
void initMessageEvent(in DOMString aType,
in boolean aCanBubble,
in boolean aCancelable,
in DOMString aData,
in jsval aData,
in DOMString aOrigin,
in DOMString aLastEventId,
in nsIDOMWindow aSource);

View File

@ -49,6 +49,9 @@ function onMessageReceived(event)
// Any other message indicates error, succes or todo message of a test
default:
if (typeof event.data == "undefined")
break; // XXXkhuey this receives undefined values
// (which used to become empty strings) on occasion ...
if (event.data.match(todoRegExp))
SimpleTest.todo(false, event.data);
else

View File

@ -80,6 +80,9 @@ _TEST_FILES = \
test_bug500328.html \
file_bug500328_1.html \
file_bug500328_2.html \
test_postMessage_structured_clone.html \
postMessage_structured_clone_helper.js \
postMessage_structured_clone_helper.html \
$(NULL)
_CHROME_FILES = \

View File

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<title>postMessage structured clone page</title>
<script type="application/javascript;version=1.7"
src="postMessage_structured_clone_helper.js"></script>
<script type="application/javascript">
var generator = new getTestContent()
function eq(lhs, rhs)
{
for(p in lhs)
{
switch(typeof(lhs[p]))
{
case 'object':
if (!eq(lhs[p], rhs[p])) { return false }; break;
case 'function':
if (typeof(rhs[p])=='undefined' || (p != 'equals' && lhs[p].toString() != rhs[p].toString())) { return false; }; break;
default:
if (lhs[p] != rhs[p]) { return false; }
}
}
for(p in rhs)
{
if(typeof(lhs[p])=='undefined') {return false;}
}
return true;
}
function receiveMessage(evt)
{
if (eq(evt.data,generator.next()))
window.parent.postMessage("TEST-PASS", "*");
else
window.parent.postMessage("TEST-FAIL", "*");
}
window.addEventListener("message", receiveMessage, false);
</script>
</head>
<body>
</body>
</html>

View File

@ -0,0 +1,16 @@
function getTestContent()
{
yield "hello";
yield 2+3;
yield 12;
yield null;
yield "complex" + "string";
yield new Object();
yield new Date(1306113544);
yield [1, 2, 3, 4, 5];
let obj = new Object();
obj.foo = 3;
obj.bar = "hi";
obj.baz = new Date(1306113544);
yield obj;
}

View File

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=553125
-->
<head>
<title>postMessage uses structured clone</title>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript;version=1.7"
src="postMessage_structured_clone_helper.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=553125">Mozilla Bug 553125</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<iframe src="http://mochi.test:8888/tests/dom/tests/mochitest/whatwg/postMessage_structured_clone_helper.html"
name="sameDomain"></iframe>
<iframe src="http://example.org:8000/tests/dom/tests/mochitest/whatwg/postMessage_structured_clone_helper.html"
name="crossDomain"></iframe>
<pre id="test">
<script class="testbody" type="application/javascript">
/** Test for Bug 553125 **/
SimpleTest.waitForExplicitFinish();
var lastMessage = null;
var crossOrigin = false;
var generator = new getTestContent();
function runNextSameOrigin() {
try {
lastMessage = generator.next();
} catch (e) {
generator = new getTestContent();
crossOrigin = true;
runNextCrossOrigin();
}
window.frames.sameDomain.postMessage(lastMessage, "http://mochi.test:8888");
}
function runNextCrossOrigin() {
try {
lastMessage = generator.next();
} catch (e) {
SimpleTest.finish();
}
window.frames.crossDomain.postMessage(lastMessage, "http://example.org:8000");
}
function receiveMessage(evt) {
if (evt.data == "TEST-PASS")
SimpleTest.ok(true, "structured clone of | " + lastMessage + " | succeeded");
else
SimpleTest.ok(false, "structured clone of | " + lastMessage + " | failed");
setTimeout(crossOrigin ? runNextCrossOrigin : runNextSameOrigin, 0);
}
window.addEventListener("message", receiveMessage, false);
window.addEventListener("load", runNextSameOrigin, false);
</script>
</pre>
</body>
</html>