Bug 966439 - BroadcastChannel API - patch 6 - Support any kind of body messages, r=bent

This commit is contained in:
Andrea Marchesini 2015-01-14 11:50:35 +00:00
parent 1f47b88f42
commit 02bd738939
16 changed files with 246 additions and 67 deletions

View File

@ -7,6 +7,7 @@
#include "BroadcastChannelChild.h"
#include "mozilla/dom/BroadcastChannelBinding.h"
#include "mozilla/dom/Navigator.h"
#include "mozilla/dom/StructuredCloneUtils.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundChild.h"
@ -28,6 +29,22 @@ namespace dom {
using namespace workers;
class BroadcastChannelMessage MOZ_FINAL
{
public:
NS_INLINE_DECL_REFCOUNTING(BroadcastChannelMessage)
JSAutoStructuredCloneBuffer mBuffer;
StructuredCloneClosure mClosure;
BroadcastChannelMessage()
{ }
private:
~BroadcastChannelMessage()
{ }
};
namespace {
void
@ -159,9 +176,9 @@ public:
NS_DECL_ISUPPORTS
PostMessageRunnable(BroadcastChannelChild* aActor,
const nsAString& aMessage)
BroadcastChannelMessage* aData)
: mActor(aActor)
, mMessage(aMessage)
, mData(aData)
{
MOZ_ASSERT(mActor);
}
@ -169,9 +186,24 @@ public:
NS_IMETHODIMP Run()
{
MOZ_ASSERT(mActor);
if (!mActor->IsActorDestroyed()) {
mActor->SendPostMessage(mMessage);
if (mActor->IsActorDestroyed()) {
return NS_OK;
}
BroadcastChannelMessageData message;
SerializedStructuredCloneBuffer& buffer = message.data();
buffer.data = mData->mBuffer.data();
buffer.dataLength = mData->mBuffer.nbytes();
#ifdef DEBUG
{
const nsTArray<nsRefPtr<File>>& blobs = mData->mClosure.mBlobs;
MOZ_ASSERT(blobs.IsEmpty());
}
#endif
mActor->SendPostMessage(message);
return NS_OK;
}
@ -185,7 +217,7 @@ private:
~PostMessageRunnable() {}
nsRefPtr<BroadcastChannelChild> mActor;
nsString mMessage;
nsRefPtr<BroadcastChannelMessage> mData;
};
NS_IMPL_ISUPPORTS(PostMessageRunnable, nsICancelableRunnable, nsIRunnable)
@ -460,22 +492,38 @@ BroadcastChannel::Constructor(const GlobalObject& aGlobal,
}
void
BroadcastChannel::PostMessage(const nsAString& aMessage, ErrorResult& aRv)
BroadcastChannel::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
ErrorResult& aRv)
{
if (mState != StateActive) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
PostMessageInternal(aMessage);
PostMessageInternal(aCx, aMessage, aRv);
}
void
BroadcastChannel::PostMessageInternal(const nsAString& aMessage)
BroadcastChannel::PostMessageInternal(JSContext* aCx,
JS::Handle<JS::Value> aMessage,
ErrorResult& aRv)
{
nsRefPtr<BroadcastChannelMessage> data = new BroadcastChannelMessage();
if (!WriteStructuredClone(aCx, aMessage, data->mBuffer, data->mClosure)) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
PostMessageData(data);
}
void
BroadcastChannel::PostMessageData(BroadcastChannelMessage* aData)
{
if (mActor) {
nsRefPtr<PostMessageRunnable> runnable =
new PostMessageRunnable(mActor, aMessage);
new PostMessageRunnable(mActor, aData);
if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
NS_WARNING("Failed to dispatch to the current thread!");
@ -484,7 +532,7 @@ BroadcastChannel::PostMessageInternal(const nsAString& aMessage)
return;
}
mPendingMessages.AppendElement(aMessage);
mPendingMessages.AppendElement(aData);
}
void
@ -527,7 +575,7 @@ BroadcastChannel::ActorCreated(ipc::PBackgroundChild* aActor)
// Flush pending messages.
for (uint32_t i = 0; i < mPendingMessages.Length(); ++i) {
PostMessageInternal(mPendingMessages[i]);
PostMessageData(mPendingMessages[i]);
}
mPendingMessages.Clear();

View File

@ -24,6 +24,7 @@ class WorkerFeature;
}
class BroadcastChannelChild;
struct BroadcastChannelMessage;
class BroadcastChannel MOZ_FINAL
: public DOMEventTargetHelper
@ -55,7 +56,8 @@ public:
aName = mChannel;
}
void PostMessage(const nsAString& aMessage, ErrorResult& aRv);
void PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
ErrorResult& aRv);
void Close();
@ -85,12 +87,15 @@ private:
~BroadcastChannel();
void PostMessageInternal(const nsAString& aMessage);
void PostMessageData(BroadcastChannelMessage* aData);
void PostMessageInternal(JSContext* aCx, JS::Handle<JS::Value> aMessage,
ErrorResult& aRv);
void UpdateMustKeepAlive();
nsRefPtr<BroadcastChannelChild> mActor;
nsTArray<nsString> mPendingMessages;
nsTArray<nsRefPtr<BroadcastChannelMessage>> mPendingMessages;
workers::WorkerFeature* mWorkerFeature;

View File

@ -8,6 +8,9 @@
#include "jsapi.h"
#include "mozilla/dom/MessageEvent.h"
#include "mozilla/dom/MessageEventBinding.h"
#include "mozilla/dom/StructuredCloneUtils.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerScope.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "WorkerPrivate.h"
@ -34,7 +37,7 @@ BroadcastChannelChild::~BroadcastChannelChild()
}
bool
BroadcastChannelChild::RecvNotify(const nsString& aMessage)
BroadcastChannelChild::RecvNotify(const BroadcastChannelMessageData& aData)
{
nsCOMPtr<DOMEventTargetHelper> helper = mBC;
nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(helper);
@ -50,39 +53,36 @@ BroadcastChannelChild::RecvNotify(const nsString& aMessage)
return true;
}
if (NS_IsMainThread()) {
AutoJSAPI autoJS;
if (!autoJS.Init(mBC->GetParentObject())) {
NS_WARNING("Dropping message");
return true;
}
AutoJSAPI jsapi;
nsCOMPtr<nsIGlobalObject> globalObject;
Notify(autoJS.cx(), aMessage);
if (NS_IsMainThread()) {
globalObject = do_QueryInterface(mBC->GetParentObject());
} else {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
globalObject = workerPrivate->GlobalScope();
}
if (!globalObject || !jsapi.Init(globalObject)) {
NS_WARNING("Failed to initialize AutoJSAPI object.");
return true;
}
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
JSContext* cx = jsapi.cx();
Notify(workerPrivate->GetJSContext(), aMessage);
return true;
}
const SerializedStructuredCloneBuffer& buffer = aData.data();
StructuredCloneData cloneData;
cloneData.mData = buffer.data;
cloneData.mDataLength = buffer.dataLength;
void
BroadcastChannelChild::Notify(JSContext* aCx, const nsString& aMessage)
{
JS::Rooted<JSString*> str(aCx,
JS_NewUCStringCopyN(aCx, aMessage.get(),
aMessage.Length()));
if (!str) {
// OOM, no exception needed.
NS_WARNING("Failed allocating a JS string. Probably OOM.");
return;
JS::Rooted<JS::Value> value(cx, JS::NullValue());
if (cloneData.mDataLength && !ReadStructuredClone(cx, cloneData, &value)) {
JS_ClearPendingException(cx);
return false;
}
JS::Rooted<JS::Value> value(aCx, JS::StringValue(str));
RootedDictionary<MessageEventInit> init(aCx);
RootedDictionary<MessageEventInit> init(cx);
init.mBubbles = false;
init.mCancelable = false;
init.mOrigin.Construct(mOrigin);
@ -93,13 +93,15 @@ BroadcastChannelChild::Notify(JSContext* aCx, const nsString& aMessage)
MessageEvent::Constructor(mBC, NS_LITERAL_STRING("message"), init, rv);
if (rv.Failed()) {
NS_WARNING("Failed to create a MessageEvent object.");
return;
return true;
}
event->SetTrusted(true);
bool status;
mBC->DispatchEvent(static_cast<Event*>(event.get()), &status);
return true;
}
void

View File

@ -29,7 +29,8 @@ public:
mBC = aBC;
}
virtual bool RecvNotify(const nsString& aMessage) MOZ_OVERRIDE;
virtual bool RecvNotify(const BroadcastChannelMessageData& aData)
MOZ_OVERRIDE;
bool IsActorDestroyed() const
{
@ -42,8 +43,6 @@ private:
~BroadcastChannelChild();
void Notify(JSContext* aCx, const nsString& aMessage);
void ActorDestroy(ActorDestroyReason aWhy);
// This raw pointer is actually the parent object.

View File

@ -31,7 +31,8 @@ BroadcastChannelParent::~BroadcastChannelParent()
}
bool
BroadcastChannelParent::RecvPostMessage(const nsString& aMessage)
BroadcastChannelParent::RecvPostMessage(
const BroadcastChannelMessageData& aData)
{
AssertIsOnBackgroundThread();
@ -39,7 +40,7 @@ BroadcastChannelParent::RecvPostMessage(const nsString& aMessage)
return false;
}
mService->PostMessage(this, aMessage, mOrigin, mChannel);
mService->PostMessage(this, aData, mOrigin, mChannel);
return true;
}
@ -73,14 +74,15 @@ BroadcastChannelParent::ActorDestroy(ActorDestroyReason aWhy)
}
void
BroadcastChannelParent::CheckAndDeliver(const nsString& aMessage,
const nsString& aOrigin,
const nsString& aChannel)
BroadcastChannelParent::CheckAndDeliver(
const BroadcastChannelMessageData& aData,
const nsString& aOrigin,
const nsString& aChannel)
{
AssertIsOnBackgroundThread();
if (aOrigin == mOrigin && aChannel == mChannel) {
unused << SendNotify(aMessage);
unused << SendNotify(aData);
}
}

View File

@ -22,7 +22,7 @@ class BroadcastChannelParent MOZ_FINAL : public PBroadcastChannelParent
friend class mozilla::ipc::BackgroundParentImpl;
public:
void CheckAndDeliver(const nsString& aMessage,
void CheckAndDeliver(const BroadcastChannelMessageData& aData,
const nsString& aOrigin,
const nsString& aChannel);
@ -31,7 +31,8 @@ private:
const nsAString& aChannel);
~BroadcastChannelParent();
virtual bool RecvPostMessage(const nsString& aMessage) MOZ_OVERRIDE;
virtual bool
RecvPostMessage(const BroadcastChannelMessageData& aData) MOZ_OVERRIDE;
virtual bool RecvClose() MOZ_OVERRIDE;

View File

@ -75,11 +75,11 @@ namespace {
struct MOZ_STACK_CLASS PostMessageData MOZ_FINAL
{
PostMessageData(BroadcastChannelParent* aParent,
const nsAString& aMessage,
const BroadcastChannelMessageData& aData,
const nsAString& aOrigin,
const nsAString& aChannel)
: mParent(aParent)
, mMessage(aMessage)
, mData(aData)
, mOrigin(aOrigin)
, mChannel(aChannel)
{
@ -93,7 +93,7 @@ struct MOZ_STACK_CLASS PostMessageData MOZ_FINAL
}
BroadcastChannelParent* mParent;
const nsString mMessage;
const BroadcastChannelMessageData& mData;
const nsString mOrigin;
const nsString mChannel;
};
@ -108,7 +108,7 @@ PostMessageEnumerator(nsPtrHashKey<BroadcastChannelParent>* aKey, void* aPtr)
MOZ_ASSERT(parent);
if (parent != data->mParent) {
parent->CheckAndDeliver(data->mMessage, data->mOrigin, data->mChannel);
parent->CheckAndDeliver(data->mData, data->mOrigin, data->mChannel);
}
return PL_DHASH_NEXT;
@ -118,7 +118,7 @@ PostMessageEnumerator(nsPtrHashKey<BroadcastChannelParent>* aKey, void* aPtr)
void
BroadcastChannelService::PostMessage(BroadcastChannelParent* aParent,
const nsAString& aMessage,
const BroadcastChannelMessageData& aData,
const nsAString& aOrigin,
const nsAString& aChannel)
{
@ -126,7 +126,7 @@ BroadcastChannelService::PostMessage(BroadcastChannelParent* aParent,
MOZ_ASSERT(aParent);
MOZ_ASSERT(mAgents.Contains(aParent));
PostMessageData data(aParent, aMessage, aOrigin, aChannel);
PostMessageData data(aParent, aData, aOrigin, aChannel);
mAgents.EnumerateEntries(PostMessageEnumerator, &data);
}

View File

@ -13,6 +13,7 @@ namespace mozilla {
namespace dom {
class BroadcastChannelParent;
class BroadcastChannelMessageData;
class BroadcastChannelService MOZ_FINAL
{
@ -25,7 +26,7 @@ public:
void UnregisterActor(BroadcastChannelParent* aParent);
void PostMessage(BroadcastChannelParent* aParent,
const nsAString& aMessage,
const BroadcastChannelMessageData& aData,
const nsAString& aOrigin,
const nsAString& aChannel);

View File

@ -4,20 +4,27 @@
include protocol PBackground;
using struct mozilla::SerializedStructuredCloneBuffer from "ipc/IPCMessageUtils.h";
namespace mozilla {
namespace dom {
struct BroadcastChannelMessageData
{
SerializedStructuredCloneBuffer data;
};
// This protocol is used for the BroadcastChannel API
protocol PBroadcastChannel
{
manager PBackground;
parent:
PostMessage(nsString message);
PostMessage(BroadcastChannelMessageData message);
Close();
child:
Notify(nsString message);
Notify(BroadcastChannelMessageData message);
__delete__();
};

View File

@ -10,8 +10,7 @@ onmessage = function(evt) {
var bc = new BroadcastChannel('foobar');
bc.addEventListener('message', function(event) {
postMessage(event.data == "hello world from the window" ? "OK" : "KO");
bc.postMessage("hello world from the worker");
bc.postMessage(event.data == "hello world from the window" ? "hello world from the worker" : "KO");
bc.close();
}, false);

View File

@ -0,0 +1,5 @@
(new BroadcastChannel('foobar')).onmessage = function(event) {
event.target.postMessage(event.data);
}
postMessage("READY");

View File

@ -5,7 +5,9 @@ support-files =
broadcastchannel_sharedWorker.js
broadcastchannel_worker.js
broadcastchannel_worker_alive.js
broadcastchannel_worker_any.js
[test_broadcastchannel_any.html]
[test_broadcastchannel_basic.html]
[test_broadcastchannel_close.html]
[test_broadcastchannel_self.html]

View File

@ -0,0 +1,109 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for BroadcastChannel</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<div id="content"></div>
<script type="application/javascript">
var tests = [
'hello world',
123,
null,
true,
new Date(),
[ 1, 'test', true, new Date() ],
{ a: true, b: null, c: new Date(), d: [ true, false, {} ] },
];
var currentTest = null;
function getType(a) {
if (a === null || a === undefined)
return 'null';
if (Array.isArray(a))
return 'array';
if (typeof a == 'object')
return 'object';
return 'primitive';
}
function compare(a, b) {
is (getType(a), getType(b), 'Type matches');
var type = getType(a);
if (type == 'array') {
is (a.length, b.length, 'Array.length matches');
for (var i = 0; i < a.length; ++i) {
compare(a[i], b[i]);
}
return;
}
if (type == 'object') {
ok (a !== b, 'They should not match');
var aProps = [];
for (var p in a) aProps.push(p);
var bProps = [];
for (var p in b) bProps.push(p);
is (aProps.length, bProps.length, 'Props match');
is (aProps.sort().toSource(), bProps.sort().toSource(), 'Props match - using toSource()');
for (var p in a) {
compare(a[p], b[p]);
}
return;
}
if (type != 'null') {
is (a.toSource(), b.toSource(), 'Matching using toSource()');
}
}
function runTest() {
var bc = new BroadcastChannel("foobar");
ok(bc, "BroadcastChannel can be created");
bc.onmessage = function(event) {
compare(event.data, currentTest);
next();
}
function next() {
if (!tests.length) {
SimpleTest.finish();
return;
}
currentTest = tests.shift();
bc.postMessage(currentTest);
}
var worker = new Worker("broadcastchannel_worker_any.js");
worker.onmessage = function(event) {
if (event.data == "READY") {
next();
}
};
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.broadcastChannel.enabled", true]]}, runTest);
</script>
</body>
</html>

View File

@ -29,8 +29,6 @@ function testWorker(x) {
if (event.data == "READY") {
ok(true, "Worker is ready!");
bc.postMessage('hello world from the window');
} else if(event.data == "OK") {
ok(true, "Worker has received the message");
} else {
ok(false, "Something wrong happened");
}

View File

@ -28,7 +28,7 @@ function runTests() {
if (event.data == "READY") {
ok(true, "Worker is ready!");
} else {
is(id, event.data, "The message is correct");
is(id, event.data, "The message is correct: " + id);
}
for (var i = 0; i < 3; ++i) {
@ -38,6 +38,7 @@ function runTests() {
if (id == 5) {
SimpleTest.finish();
return;
}
event.target.postMessage(++id);

View File

@ -14,7 +14,7 @@ interface BroadcastChannel : EventTarget {
readonly attribute DOMString name;
[Throws]
void postMessage(DOMString message);
void postMessage(any message);
void close();