зеркало из https://github.com/mozilla/gecko-dev.git
Bug 677638 - MessagePort.PostMessage
This commit is contained in:
Родитель
b1842412ca
Коммит
1b6e3e25d2
|
@ -6,11 +6,243 @@
|
|||
#include "MessagePort.h"
|
||||
#include "mozilla/dom/MessageChannel.h"
|
||||
#include "mozilla/dom/MessagePortBinding.h"
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsEventDispatcher.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsDOMEvent.h"
|
||||
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMFile.h"
|
||||
#include "nsIDOMFileList.h"
|
||||
#include "nsIDOMMessageEvent.h"
|
||||
#include "nsIPresShell.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class PostMessageRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
PostMessageRunnable(MessagePort* aPort)
|
||||
: mPort(aPort)
|
||||
, mMessage(nullptr)
|
||||
, mMessageLen(0)
|
||||
{
|
||||
}
|
||||
|
||||
~PostMessageRunnable()
|
||||
{
|
||||
NS_ASSERTION(!mMessage, "Message should have been deserialized!");
|
||||
}
|
||||
|
||||
void SetJSData(JSAutoStructuredCloneBuffer& aBuffer)
|
||||
{
|
||||
NS_ASSERTION(!mMessage && mMessageLen == 0, "Don't call twice!");
|
||||
aBuffer.steal(&mMessage, &mMessageLen);
|
||||
}
|
||||
|
||||
bool StoreISupports(nsISupports* aSupports)
|
||||
{
|
||||
mSupportsArray.AppendElement(aSupports);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<MessagePort> mPort;
|
||||
uint64_t* mMessage;
|
||||
size_t mMessageLen;
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports> > mSupportsArray;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
struct StructuredCloneInfo
|
||||
{
|
||||
PostMessageRunnable* mEvent;
|
||||
MessagePort* mPort;
|
||||
};
|
||||
|
||||
static JSObject*
|
||||
PostMessageReadStructuredClone(JSContext* cx,
|
||||
JSStructuredCloneReader* reader,
|
||||
uint32_t tag,
|
||||
uint32_t data,
|
||||
void* closure)
|
||||
{
|
||||
NS_ASSERTION(closure, "Must have closure!");
|
||||
|
||||
if (tag == SCTAG_DOM_BLOB || tag == SCTAG_DOM_FILELIST) {
|
||||
NS_ASSERTION(!data, "Data should be empty");
|
||||
|
||||
nsISupports* supports;
|
||||
if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
|
||||
JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
if (global) {
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
|
||||
if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, global, supports,
|
||||
val.address(),
|
||||
getter_AddRefs(wrapper)))) {
|
||||
return JSVAL_TO_OBJECT(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tag == SCTAG_DOM_MESSAGEPORT) {
|
||||
NS_ASSERTION(!data, "Data should be empty");
|
||||
|
||||
MessagePort* port;
|
||||
if (JS_ReadBytes(reader, &port, sizeof(port))) {
|
||||
JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
if (global) {
|
||||
JS::Rooted<JSObject*> obj(cx, port->WrapObject(cx, global));
|
||||
if (JS_WrapObject(cx, obj.address())) {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks* runtimeCallbacks =
|
||||
js::GetContextStructuredCloneCallbacks(cx);
|
||||
|
||||
if (runtimeCallbacks) {
|
||||
return runtimeCallbacks->read(cx, reader, tag, data, nullptr);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool
|
||||
PostMessageWriteStructuredClone(JSContext* cx,
|
||||
JSStructuredCloneWriter* writer,
|
||||
JS::Handle<JSObject*> obj,
|
||||
void *closure)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
|
||||
nsContentUtils::XPConnect()->
|
||||
GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
|
||||
if (wrappedNative) {
|
||||
uint32_t scTag = 0;
|
||||
nsISupports* supports = wrappedNative->Native();
|
||||
|
||||
nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
|
||||
if (blob) {
|
||||
scTag = SCTAG_DOM_BLOB;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports);
|
||||
if (list) {
|
||||
scTag = SCTAG_DOM_FILELIST;
|
||||
}
|
||||
|
||||
if (scTag) {
|
||||
return JS_WriteUint32Pair(writer, scTag, 0) &&
|
||||
JS_WriteBytes(writer, &supports, sizeof(supports)) &&
|
||||
scInfo->mEvent->StoreISupports(supports);
|
||||
}
|
||||
}
|
||||
|
||||
MessagePort* port = nullptr;
|
||||
nsresult rv = UNWRAP_OBJECT(MessagePort, cx, obj, port);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsRefPtr<MessagePort> newPort = port->Clone(scInfo->mPort->GetOwner());
|
||||
|
||||
return JS_WriteUint32Pair(writer, SCTAG_DOM_MESSAGEPORT, 0) &&
|
||||
JS_WriteBytes(writer, &newPort, sizeof(newPort)) &&
|
||||
scInfo->mEvent->StoreISupports(newPort);
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks* runtimeCallbacks =
|
||||
js::GetContextStructuredCloneCallbacks(cx);
|
||||
|
||||
if (runtimeCallbacks) {
|
||||
return runtimeCallbacks->write(cx, writer, obj, nullptr);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
JSStructuredCloneCallbacks kPostMessageCallbacks = {
|
||||
PostMessageReadStructuredClone,
|
||||
PostMessageWriteStructuredClone,
|
||||
nullptr
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
NS_IMETHODIMP
|
||||
PostMessageRunnable::Run()
|
||||
{
|
||||
// Ensure that the buffer is freed even if we fail to post the message
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
buffer.adopt(mMessage, mMessageLen);
|
||||
mMessage = nullptr;
|
||||
mMessageLen = 0;
|
||||
|
||||
// Get the JSContext for the target window
|
||||
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(mPort->GetOwner());
|
||||
NS_ENSURE_STATE(sgo);
|
||||
nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
|
||||
AutoPushJSContext cx(scriptContext ? scriptContext->GetNativeContext()
|
||||
: nsContentUtils::GetSafeJSContext());
|
||||
|
||||
MOZ_ASSERT(cx);
|
||||
|
||||
// Deserialize the structured clone data
|
||||
JS::Rooted<JS::Value> messageData(cx);
|
||||
{
|
||||
StructuredCloneInfo scInfo;
|
||||
scInfo.mEvent = this;
|
||||
scInfo.mPort = mPort;
|
||||
|
||||
if (!buffer.read(cx, messageData.address(), &kPostMessageCallbacks,
|
||||
&scInfo)) {
|
||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the event
|
||||
nsIDocument* doc = mPort->GetOwner()->GetExtantDoc();
|
||||
if (!doc) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ErrorResult error;
|
||||
nsRefPtr<nsDOMEvent> event =
|
||||
doc->CreateEvent(NS_LITERAL_STRING("MessageEvent"), error);
|
||||
if (error.Failed()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMMessageEvent> message = do_QueryInterface(event);
|
||||
nsresult rv = message->InitMessageEvent(NS_LITERAL_STRING("message"),
|
||||
false /* non-bubbling */,
|
||||
true /* cancelable */,
|
||||
messageData,
|
||||
EmptyString(),
|
||||
EmptyString(),
|
||||
mPort->GetOwner());
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
message->SetTrusted(true);
|
||||
|
||||
bool status;
|
||||
mPort->DispatchEvent(event, &status);
|
||||
return status ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED_1(MessagePort, nsDOMEventTargetHelper,
|
||||
mEntangledPort)
|
||||
|
||||
|
@ -41,13 +273,34 @@ MessagePort::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
|
|||
|
||||
void
|
||||
MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<JS::Handle<JS::Value> >& aTransfer)
|
||||
const Optional<JS::Handle<JS::Value> >& aTransfer,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (!mEntangledPort) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO
|
||||
nsRefPtr<PostMessageRunnable> event = new PostMessageRunnable(mEntangledPort);
|
||||
|
||||
// We *must* clone the data here, or the JS::Value could be modified
|
||||
// by script
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
StructuredCloneInfo scInfo;
|
||||
scInfo.mEvent = event;
|
||||
scInfo.mPort = this;
|
||||
|
||||
JS::Handle<JS::Value> transferable = aTransfer.WasPassed()
|
||||
? aTransfer.Value()
|
||||
: JS::UndefinedHandleValue;
|
||||
|
||||
if (!buffer.write(aCx, aMessage, transferable, &kPostMessageCallbacks,
|
||||
&scInfo)) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
event->SetJSData(buffer);
|
||||
NS_DispatchToCurrentThread(event);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -85,7 +338,7 @@ MessagePort::Entangle(MessagePort* aMessagePort)
|
|||
already_AddRefed<MessagePort>
|
||||
MessagePort::Clone(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
nsRefPtr<MessagePort> newPort = new MessagePort(aWindow);
|
||||
nsRefPtr<MessagePort> newPort = new MessagePort(aWindow->GetCurrentInnerWindow());
|
||||
|
||||
// TODO Move all the events in the port message queue of original port to the
|
||||
// port message queue of new port, if any, leaving the new port's port
|
||||
|
|
|
@ -31,7 +31,8 @@ public:
|
|||
|
||||
void
|
||||
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<JS::Handle<JS::Value> >& aTransfer);
|
||||
const Optional<JS::Handle<JS::Value> >& aTransfer,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
Start();
|
||||
|
|
|
@ -34,6 +34,9 @@ MOCHITEST_FILES = \
|
|||
test_messageChannel.html \
|
||||
test_messageChannel_cloning.html \
|
||||
iframe_messageChannel_cloning.html \
|
||||
test_messageChannel_post.html \
|
||||
iframe_messageChannel_post.html \
|
||||
test_messageChannel_transferable.html \
|
||||
$(NULL)
|
||||
|
||||
MOCHITEST_CHROME_FILES = \
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<body>
|
||||
<script type="application/javascript">
|
||||
|
||||
var port;
|
||||
|
||||
window.addEventListener('message', receiveMessage, false);
|
||||
function receiveMessage(evt) {
|
||||
port = evt.data.port;
|
||||
|
||||
port.addEventListener('message', receivePostMessage, false);
|
||||
function receivePostMessage(evt) {
|
||||
port.postMessage(evt.data);
|
||||
}
|
||||
|
||||
window.parent.postMessage({ status: "READY" }, '*');
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=677638
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 677638 - port cloning</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.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=677638">Mozilla Bug 677638</a>
|
||||
<div id="content"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<script type="application/javascript">
|
||||
|
||||
var a = new MessageChannel();
|
||||
ok(a, "MessageChannel created");
|
||||
|
||||
window.addEventListener('message', receiveMessage, false);
|
||||
function receiveMessage(evt) {
|
||||
if (evt.data.status == 'READY') {
|
||||
runTest();
|
||||
} else {
|
||||
ok(false, "Unknown message");
|
||||
}
|
||||
}
|
||||
|
||||
var div = document.getElementById("content");
|
||||
ok(div, "Parent exists");
|
||||
|
||||
var ifr = document.createElement("iframe");
|
||||
ifr.addEventListener("load", iframeLoaded, false);
|
||||
ifr.setAttribute('src', "iframe_messageChannel_post.html");
|
||||
div.appendChild(ifr);
|
||||
|
||||
function iframeLoaded() {
|
||||
ifr.contentWindow.postMessage({ port: a.port2 }, '*');
|
||||
}
|
||||
|
||||
var tests = [ 42,
|
||||
null,
|
||||
undefined,
|
||||
"hello world",
|
||||
new Blob([]),
|
||||
true ];
|
||||
|
||||
a.port1.onmessage = function(evt) {
|
||||
ok(tests.length, "We are waiting for a message");
|
||||
is(tests[0], evt.data, "Value ok: " + tests[0]);
|
||||
tests.shift();
|
||||
runTest();
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
if (!tests.length) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
a.port1.postMessage(tests[0]);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,60 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=677638
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 677638 - port cloning</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.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=677638">Mozilla Bug 677638</a>
|
||||
<div id="content"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<script type="application/javascript">
|
||||
|
||||
var a = new MessageChannel();
|
||||
ok(a, "MessageChannel created");
|
||||
|
||||
window.addEventListener('message', receiveMessage, false);
|
||||
function receiveMessage(evt) {
|
||||
if (evt.data.status == 'READY') {
|
||||
runTest();
|
||||
} else {
|
||||
ok(false, "Unknown message");
|
||||
}
|
||||
}
|
||||
|
||||
var div = document.getElementById("content");
|
||||
ok(div, "Parent exists");
|
||||
|
||||
var ifr = document.createElement("iframe");
|
||||
ifr.addEventListener("load", iframeLoaded, false);
|
||||
ifr.setAttribute('src', "iframe_messageChannel_post.html");
|
||||
div.appendChild(ifr);
|
||||
|
||||
function iframeLoaded() {
|
||||
ifr.contentWindow.postMessage({ port: a.port2 }, '*');
|
||||
}
|
||||
|
||||
a.port1.onmessage = function(evt) {
|
||||
is(evt.data.ab.byteLength, size, "The size is: " + size + " == " + ab.byteLength);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var size = 1024 * 1024 * 32;
|
||||
var ab = new ArrayBuffer(size);
|
||||
is(ab.byteLength, size, "The size is: " + size + " == " + ab.byteLength);
|
||||
|
||||
function runTest() {
|
||||
a.port1.postMessage({ab: ab, cb: ab}, [ab]);
|
||||
ok(ab.byteLength == 0, "PostMessage - The size is: 0 == " + ab.byteLength)
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -9,7 +9,9 @@
|
|||
|
||||
interface MessagePort : EventTarget {
|
||||
// TODO void postMessage(any message, optional sequence<Transferable> transfer);
|
||||
[Throws]
|
||||
void postMessage(any message, optional any transfer);
|
||||
|
||||
void start();
|
||||
void close();
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче