mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 911972 - MessagePort and MessageChannel in workers, r=smaug, r=bent
--HG-- rename : dom/base/MessageChannel.cpp => dom/messagechannel/MessageChannel.cpp rename : dom/base/MessageChannel.h => dom/messagechannel/MessageChannel.h rename : dom/base/MessagePort.cpp => dom/messagechannel/MessagePort.cpp rename : dom/base/MessagePort.h => dom/messagechannel/MessagePort.h rename : dom/base/MessagePortList.cpp => dom/messagechannel/MessagePortList.cpp rename : dom/base/MessagePortList.h => dom/messagechannel/MessagePortList.h rename : dom/base/test/iframe_messageChannel_chrome.html => dom/messagechannel/tests/iframe_messageChannel_chrome.html rename : dom/base/test/iframe_messageChannel_cloning.html => dom/messagechannel/tests/iframe_messageChannel_cloning.html rename : dom/base/test/iframe_messageChannel_pingpong.html => dom/messagechannel/tests/iframe_messageChannel_pingpong.html rename : dom/base/test/iframe_messageChannel_post.html => dom/messagechannel/tests/iframe_messageChannel_post.html rename : dom/base/test/test_messageChannel.html => dom/messagechannel/tests/test_messageChannel.html rename : dom/base/test/test_messageChannel.xul => dom/messagechannel/tests/test_messageChannel.xul rename : dom/base/test/test_messageChannel_cloning.html => dom/messagechannel/tests/test_messageChannel_cloning.html rename : dom/base/test/test_messageChannel_pingpong.html => dom/messagechannel/tests/test_messageChannel_pingpong.html rename : dom/base/test/test_messageChannel_post.html => dom/messagechannel/tests/test_messageChannel_post.html rename : dom/base/test/test_messageChannel_pref.html => dom/messagechannel/tests/test_messageChannel_pref.html rename : dom/base/test/test_messageChannel_start.html => dom/messagechannel/tests/test_messageChannel_start.html rename : dom/base/test/test_messageChannel_transferable.html => dom/messagechannel/tests/test_messageChannel_transferable.html rename : dom/base/test/test_messageChannel_unshipped.html => dom/messagechannel/tests/test_messageChannel_unshipped.html
This commit is contained in:
parent
3a8036a7a7
commit
9b1ac68ebb
@ -1,104 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MessageChannel.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/MessageChannelBinding.h"
|
||||
#include "mozilla/dom/MessagePort.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MessageChannel, mWindow, mPort1, mPort2)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(MessageChannel)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(MessageChannel)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MessageChannel)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
namespace {
|
||||
bool gPrefInitialized = false;
|
||||
bool gPrefEnabled = false;
|
||||
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
MessageChannel::Enabled(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
if (!gPrefInitialized) {
|
||||
Preferences::AddBoolVarCache(&gPrefEnabled, "dom.messageChannel.enabled");
|
||||
gPrefInitialized = true;
|
||||
}
|
||||
|
||||
// Enabled by pref
|
||||
if (gPrefEnabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Chrome callers are allowed.
|
||||
if (nsContentUtils::ThreadsafeIsCallerChrome()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = nsContentUtils::SubjectPrincipal();
|
||||
MOZ_ASSERT(principal);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
if (NS_FAILED(principal->GetURI(getter_AddRefs(uri))) || !uri) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isResource = false;
|
||||
if (NS_FAILED(uri->SchemeIs("resource", &isResource))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isResource;
|
||||
}
|
||||
|
||||
MessageChannel::MessageChannel(nsPIDOMWindow* aWindow)
|
||||
: mWindow(aWindow)
|
||||
{
|
||||
MOZ_COUNT_CTOR(MessageChannel);
|
||||
|
||||
mPort1 = new MessagePort(mWindow);
|
||||
mPort2 = new MessagePort(mWindow);
|
||||
|
||||
mPort1->Entangle(mPort2);
|
||||
mPort2->Entangle(mPort1);
|
||||
}
|
||||
|
||||
MessageChannel::~MessageChannel()
|
||||
{
|
||||
MOZ_COUNT_DTOR(MessageChannel);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
MessageChannel::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return MessageChannelBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<MessageChannel>
|
||||
MessageChannel::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<MessageChannel> channel = new MessageChannel(window);
|
||||
return channel.forget();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,564 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MessagePort.h"
|
||||
#include "MessageEvent.h"
|
||||
#include "mozilla/dom/BlobBinding.h"
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/MessageChannel.h"
|
||||
#include "mozilla/dom/MessagePortBinding.h"
|
||||
#include "mozilla/dom/MessagePortList.h"
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "ScriptSettings.h"
|
||||
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMFileList.h"
|
||||
#include "nsIPresShell.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class DispatchEventRunnable : public nsRunnable
|
||||
{
|
||||
friend class MessagePort;
|
||||
|
||||
public:
|
||||
explicit DispatchEventRunnable(MessagePort* aPort)
|
||||
: mPort(aPort)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
nsRefPtr<DispatchEventRunnable> mKungFuDeathGrip(this);
|
||||
|
||||
mPort->mDispatchRunnable = nullptr;
|
||||
mPort->Dispatch();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<MessagePort> mPort;
|
||||
};
|
||||
|
||||
class PostMessageRunnable : public nsRunnable
|
||||
{
|
||||
friend class MessagePort;
|
||||
|
||||
public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
PostMessageRunnable()
|
||||
{
|
||||
}
|
||||
|
||||
~PostMessageRunnable()
|
||||
{
|
||||
}
|
||||
|
||||
JSAutoStructuredCloneBuffer& Buffer()
|
||||
{
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
bool StoreISupports(nsISupports* aSupports)
|
||||
{
|
||||
mSupportsArray.AppendElement(aSupports);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Dispatch(MessagePort* aPort)
|
||||
{
|
||||
mPort = aPort;
|
||||
NS_DispatchToCurrentThread(this);
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<MessagePort> mPort;
|
||||
JSAutoStructuredCloneBuffer mBuffer;
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports> > mSupportsArray;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
struct StructuredCloneInfo
|
||||
{
|
||||
PostMessageRunnable* mEvent;
|
||||
MessagePort* mPort;
|
||||
nsRefPtrHashtable<nsRefPtrHashKey<MessagePortBase>, MessagePortBase> mPorts;
|
||||
};
|
||||
|
||||
static JSObject*
|
||||
PostMessageReadStructuredClone(JSContext* cx,
|
||||
JSStructuredCloneReader* reader,
|
||||
uint32_t tag,
|
||||
uint32_t data,
|
||||
void* closure)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
if (tag == SCTAG_DOM_BLOB) {
|
||||
NS_ASSERTION(!data, "Data should be empty");
|
||||
|
||||
// What we get back from the reader is a BlobImpl.
|
||||
// From that we create a new File.
|
||||
BlobImpl* blobImpl;
|
||||
if (JS_ReadBytes(reader, &blobImpl, sizeof(blobImpl))) {
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
// nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
|
||||
// called because the static analysis thinks dereferencing XPCOM objects
|
||||
// can GC (because in some cases it can!), and a return statement with a
|
||||
// JSObject* type means that JSObject* is on the stack as a raw pointer
|
||||
// while destructors are running.
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
{
|
||||
nsRefPtr<Blob> blob = Blob::Create(scInfo->mPort->GetParentObject(),
|
||||
blobImpl);
|
||||
if (!ToJSValue(cx, blob, &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return &val.toObject();
|
||||
}
|
||||
}
|
||||
|
||||
if (tag == SCTAG_DOM_FILELIST) {
|
||||
NS_ASSERTION(!data, "Data should be empty");
|
||||
|
||||
nsISupports* supports;
|
||||
if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, supports, &val))) {
|
||||
return val.toObjectOrNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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!");
|
||||
|
||||
// See if this is a File/Blob object.
|
||||
{
|
||||
Blob* blob = nullptr;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, obj, blob))) {
|
||||
BlobImpl* blobImpl = blob->Impl();
|
||||
if (JS_WriteUint32Pair(writer, SCTAG_DOM_BLOB, 0) &&
|
||||
JS_WriteBytes(writer, &blobImpl, sizeof(blobImpl))) {
|
||||
scInfo->mEvent->StoreISupports(blobImpl);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
|
||||
nsContentUtils::XPConnect()->
|
||||
GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
|
||||
if (wrappedNative) {
|
||||
uint32_t scTag = 0;
|
||||
nsISupports* supports = wrappedNative->Native();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks* runtimeCallbacks =
|
||||
js::GetContextStructuredCloneCallbacks(cx);
|
||||
|
||||
if (runtimeCallbacks) {
|
||||
return runtimeCallbacks->write(cx, writer, obj, nullptr);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
PostMessageReadTransferStructuredClone(JSContext* aCx,
|
||||
JSStructuredCloneReader* reader,
|
||||
uint32_t tag, void* data,
|
||||
uint64_t unused,
|
||||
void* aClosure,
|
||||
JS::MutableHandle<JSObject*> returnObject)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
if (tag == SCTAG_DOM_MAP_MESSAGEPORT) {
|
||||
MessagePort* port = static_cast<MessagePort*>(data);
|
||||
port->BindToOwner(scInfo->mPort->GetOwner());
|
||||
scInfo->mPorts.Put(port, nullptr);
|
||||
|
||||
JS::Rooted<JSObject*> obj(aCx, port->WrapObject(aCx, nullptr));
|
||||
if (!obj || !JS_WrapObject(aCx, &obj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(port->GetOwner() == scInfo->mPort->GetOwner());
|
||||
returnObject.set(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
PostMessageTransferStructuredClone(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
void* aClosure,
|
||||
uint32_t* aTag,
|
||||
JS::TransferableOwnership* aOwnership,
|
||||
void** aContent,
|
||||
uint64_t *aExtraData)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
MessagePortBase *port = nullptr;
|
||||
nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsRefPtr<MessagePortBase> newPort;
|
||||
if (scInfo->mPorts.Get(port, getter_AddRefs(newPort))) {
|
||||
// No duplicate.
|
||||
return false;
|
||||
}
|
||||
|
||||
newPort = port->Clone();
|
||||
scInfo->mPorts.Put(port, newPort);
|
||||
|
||||
*aTag = SCTAG_DOM_MAP_MESSAGEPORT;
|
||||
*aOwnership = JS::SCTAG_TMO_CUSTOM;
|
||||
*aContent = newPort;
|
||||
*aExtraData = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
PostMessageFreeTransferStructuredClone(uint32_t aTag, JS::TransferableOwnership aOwnership,
|
||||
void* aData,
|
||||
uint64_t aExtraData,
|
||||
void* aClosure)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
|
||||
MOZ_ASSERT(aOwnership == JS::SCTAG_TMO_CUSTOM);
|
||||
nsRefPtr<MessagePort> port(static_cast<MessagePort*>(aData));
|
||||
scInfo->mPorts.Remove(port);
|
||||
}
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks kPostMessageCallbacks = {
|
||||
PostMessageReadStructuredClone,
|
||||
PostMessageWriteStructuredClone,
|
||||
nullptr,
|
||||
PostMessageReadTransferStructuredClone,
|
||||
PostMessageTransferStructuredClone,
|
||||
PostMessageFreeTransferStructuredClone
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
static PLDHashOperator
|
||||
PopulateMessagePortList(MessagePortBase* aKey, MessagePortBase* aValue, void* aClosure)
|
||||
{
|
||||
nsTArray<nsRefPtr<MessagePortBase> > *array =
|
||||
static_cast<nsTArray<nsRefPtr<MessagePortBase> > *>(aClosure);
|
||||
|
||||
array->AppendElement(aKey);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PostMessageRunnable::Run()
|
||||
{
|
||||
MOZ_ASSERT(mPort);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(mPort->GetParentObject()))) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
// Deserialize the structured clone data
|
||||
JS::Rooted<JS::Value> messageData(cx);
|
||||
StructuredCloneInfo scInfo;
|
||||
scInfo.mEvent = this;
|
||||
scInfo.mPort = mPort;
|
||||
|
||||
if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
|
||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
}
|
||||
|
||||
// Create the event
|
||||
nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
|
||||
do_QueryInterface(mPort->GetOwner());
|
||||
nsRefPtr<MessageEvent> event =
|
||||
new MessageEvent(eventTarget, nullptr, nullptr);
|
||||
|
||||
event->InitMessageEvent(NS_LITERAL_STRING("message"), false /* non-bubbling */,
|
||||
false /* cancelable */, messageData, EmptyString(),
|
||||
EmptyString(), nullptr);
|
||||
event->SetTrusted(true);
|
||||
event->SetSource(mPort);
|
||||
|
||||
nsTArray<nsRefPtr<MessagePortBase> > ports;
|
||||
scInfo.mPorts.EnumerateRead(PopulateMessagePortList, &ports);
|
||||
event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()), ports));
|
||||
|
||||
bool status;
|
||||
mPort->DispatchEvent(static_cast<dom::Event*>(event.get()), &status);
|
||||
return status ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
MessagePortBase::MessagePortBase(nsPIDOMWindow* aWindow)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
{
|
||||
}
|
||||
|
||||
MessagePortBase::MessagePortBase()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mEntangledPort)
|
||||
|
||||
// Custom unlink loop because this array contains nsRunnable objects
|
||||
// which are not cycle colleactable.
|
||||
while (!tmp->mMessageQueue.IsEmpty()) {
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageQueue[0]->mPort);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageQueue[0]->mSupportsArray);
|
||||
tmp->mMessageQueue.RemoveElementAt(0);
|
||||
}
|
||||
|
||||
if (tmp->mDispatchRunnable) {
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDispatchRunnable->mPort);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEntangledPort)
|
||||
|
||||
// Custom unlink loop because this array contains nsRunnable objects
|
||||
// which are not cycle colleactable.
|
||||
for (uint32_t i = 0, len = tmp->mMessageQueue.Length(); i < len; ++i) {
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageQueue[i]->mPort);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageQueue[i]->mSupportsArray);
|
||||
}
|
||||
|
||||
if (tmp->mDispatchRunnable) {
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDispatchRunnable->mPort);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper)
|
||||
|
||||
MessagePort::MessagePort(nsPIDOMWindow* aWindow)
|
||||
: MessagePortBase(aWindow)
|
||||
, mMessageQueueEnabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
MessagePort::~MessagePort()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
MessagePort::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return MessagePortBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsRefPtr<PostMessageRunnable> event = new PostMessageRunnable();
|
||||
|
||||
// We *must* clone the data here, or the JS::Value could be modified
|
||||
// by script
|
||||
StructuredCloneInfo scInfo;
|
||||
scInfo.mEvent = event;
|
||||
scInfo.mPort = this;
|
||||
|
||||
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
|
||||
if (aTransferable.WasPassed()) {
|
||||
const Sequence<JS::Value>& realTransferable = aTransferable.Value();
|
||||
|
||||
// The input sequence only comes from the generated bindings code, which
|
||||
// ensures it is rooted.
|
||||
JS::HandleValueArray elements =
|
||||
JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
|
||||
realTransferable.Elements());
|
||||
|
||||
JSObject* array =
|
||||
JS_NewArrayObject(aCx, elements);
|
||||
if (!array) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
transferable.setObject(*array);
|
||||
}
|
||||
|
||||
if (!event->Buffer().write(aCx, aMessage, transferable,
|
||||
&kPostMessageCallbacks, &scInfo)) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mEntangledPort) {
|
||||
return;
|
||||
}
|
||||
|
||||
mEntangledPort->mMessageQueue.AppendElement(event);
|
||||
mEntangledPort->Dispatch();
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::Start()
|
||||
{
|
||||
if (mMessageQueueEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
mMessageQueueEnabled = true;
|
||||
Dispatch();
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::Dispatch()
|
||||
{
|
||||
if (!mMessageQueueEnabled || mMessageQueue.IsEmpty() || mDispatchRunnable) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<PostMessageRunnable> event = mMessageQueue.ElementAt(0);
|
||||
mMessageQueue.RemoveElementAt(0);
|
||||
|
||||
event->Dispatch(this);
|
||||
|
||||
mDispatchRunnable = new DispatchEventRunnable(this);
|
||||
NS_DispatchToCurrentThread(mDispatchRunnable);
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::Close()
|
||||
{
|
||||
if (!mEntangledPort) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This avoids loops.
|
||||
nsRefPtr<MessagePort> port = mEntangledPort;
|
||||
mEntangledPort = nullptr;
|
||||
|
||||
// Let's disentangle the 2 ports symmetrically.
|
||||
port->Close();
|
||||
}
|
||||
|
||||
EventHandlerNonNull*
|
||||
MessagePort::GetOnmessage()
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return GetEventHandler(nsGkAtoms::onmessage, EmptyString());
|
||||
}
|
||||
return GetEventHandler(nullptr, NS_LITERAL_STRING("message"));
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::SetOnmessage(EventHandlerNonNull* aCallback)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback);
|
||||
} else {
|
||||
SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback);
|
||||
}
|
||||
|
||||
// When using onmessage, the call to start() is implied.
|
||||
Start();
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::Entangle(MessagePort* aMessagePort)
|
||||
{
|
||||
MOZ_ASSERT(aMessagePort);
|
||||
MOZ_ASSERT(aMessagePort != this);
|
||||
|
||||
Close();
|
||||
|
||||
mEntangledPort = aMessagePort;
|
||||
}
|
||||
|
||||
already_AddRefed<MessagePortBase>
|
||||
MessagePort::Clone()
|
||||
{
|
||||
nsRefPtr<MessagePort> newPort = new MessagePort(nullptr);
|
||||
|
||||
// Move all the events in the port message queue of original port.
|
||||
newPort->mMessageQueue.SwapElements(mMessageQueue);
|
||||
|
||||
if (mEntangledPort) {
|
||||
nsRefPtr<MessagePort> port = mEntangledPort;
|
||||
mEntangledPort = nullptr;
|
||||
|
||||
newPort->Entangle(port);
|
||||
port->Entangle(newPort);
|
||||
}
|
||||
|
||||
return newPort.forget();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,115 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_MessagePort_h
|
||||
#define mozilla_dom_MessagePort_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class DispatchEventRunnable;
|
||||
class PostMessageRunnable;
|
||||
|
||||
class MessagePortBase : public DOMEventTargetHelper
|
||||
{
|
||||
protected:
|
||||
explicit MessagePortBase(nsPIDOMWindow* aWindow);
|
||||
MessagePortBase();
|
||||
|
||||
public:
|
||||
|
||||
virtual void
|
||||
PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv) = 0;
|
||||
|
||||
virtual void
|
||||
Start() = 0;
|
||||
|
||||
virtual void
|
||||
Close() = 0;
|
||||
|
||||
// The 'message' event handler has to call |Start()| method, so we
|
||||
// cannot use IMPL_EVENT_HANDLER macro here.
|
||||
virtual EventHandlerNonNull*
|
||||
GetOnmessage() = 0;
|
||||
|
||||
virtual void
|
||||
SetOnmessage(EventHandlerNonNull* aCallback) = 0;
|
||||
|
||||
// Duplicate this message port. This method is used by the Structured Clone
|
||||
// Algorithm and makes the new MessagePort active with the entangled
|
||||
// MessagePort of this object.
|
||||
virtual already_AddRefed<MessagePortBase>
|
||||
Clone() = 0;
|
||||
};
|
||||
|
||||
class MessagePort final : public MessagePortBase
|
||||
{
|
||||
friend class DispatchEventRunnable;
|
||||
friend class PostMessageRunnable;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort,
|
||||
DOMEventTargetHelper)
|
||||
|
||||
explicit MessagePort(nsPIDOMWindow* aWindow);
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
virtual void
|
||||
PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
virtual void
|
||||
Start() override;
|
||||
|
||||
virtual void
|
||||
Close() override;
|
||||
|
||||
virtual EventHandlerNonNull*
|
||||
GetOnmessage() override;
|
||||
|
||||
virtual void
|
||||
SetOnmessage(EventHandlerNonNull* aCallback) override;
|
||||
|
||||
// Non WebIDL methods
|
||||
|
||||
// This method entangles this MessagePort with another one.
|
||||
// If it is already entangled, it's disentangled first and enatangle to the
|
||||
// new one.
|
||||
void
|
||||
Entangle(MessagePort* aMessagePort);
|
||||
|
||||
virtual already_AddRefed<MessagePortBase>
|
||||
Clone() override;
|
||||
|
||||
private:
|
||||
~MessagePort();
|
||||
|
||||
// Dispatch events from the Message Queue using a nsRunnable.
|
||||
void Dispatch();
|
||||
|
||||
nsRefPtr<DispatchEventRunnable> mDispatchRunnable;
|
||||
|
||||
nsRefPtr<MessagePort> mEntangledPort;
|
||||
|
||||
nsTArray<nsRefPtr<PostMessageRunnable> > mMessageQueue;
|
||||
bool mMessageQueueEnabled;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_MessagePort_h
|
@ -111,7 +111,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(NodeInfo)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_0(NodeInfo)
|
||||
|
||||
static const char* kNSURIs[] = {
|
||||
static const char* kNodeInfoNSURIs[] = {
|
||||
" ([none])",
|
||||
" (xmlns)",
|
||||
" (xml)",
|
||||
@ -129,8 +129,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(NodeInfo)
|
||||
char name[72];
|
||||
uint32_t nsid = tmp->NamespaceID();
|
||||
nsAtomCString localName(tmp->NameAtom());
|
||||
if (nsid < ArrayLength(kNSURIs)) {
|
||||
PR_snprintf(name, sizeof(name), "NodeInfo%s %s", kNSURIs[nsid],
|
||||
if (nsid < ArrayLength(kNodeInfoNSURIs)) {
|
||||
PR_snprintf(name, sizeof(name), "NodeInfo%s %s", kNodeInfoNSURIs[nsid],
|
||||
localName.get());
|
||||
}
|
||||
else {
|
||||
|
389
dom/base/PostMessageEvent.cpp
Normal file
389
dom/base/PostMessageEvent.cpp
Normal file
@ -0,0 +1,389 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "PostMessageEvent.h"
|
||||
|
||||
#include "MessageEvent.h"
|
||||
#include "mozilla/dom/BlobBinding.h"
|
||||
#include "mozilla/dom/MessagePort.h"
|
||||
#include "mozilla/dom/MessagePortBinding.h"
|
||||
#include "mozilla/dom/PMessagePort.h"
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsIPrincipal.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace {
|
||||
|
||||
struct StructuredCloneInfo
|
||||
{
|
||||
PostMessageEvent* event;
|
||||
bool subsumes;
|
||||
nsPIDOMWindow* window;
|
||||
|
||||
// This hashtable contains the transferred ports - used to avoid duplicates.
|
||||
nsTArray<nsRefPtr<MessagePortBase>> transferredPorts;
|
||||
|
||||
// This array is populated when the ports are cloned.
|
||||
nsTArray<nsRefPtr<MessagePortBase>> clonedPorts;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
const JSStructuredCloneCallbacks PostMessageEvent::sPostMessageCallbacks = {
|
||||
PostMessageEvent::ReadStructuredClone,
|
||||
PostMessageEvent::WriteStructuredClone,
|
||||
nullptr,
|
||||
PostMessageEvent::ReadTransferStructuredClone,
|
||||
PostMessageEvent::TransferStructuredClone,
|
||||
PostMessageEvent::FreeTransferStructuredClone
|
||||
};
|
||||
|
||||
/* static */ JSObject*
|
||||
PostMessageEvent::ReadStructuredClone(JSContext* cx,
|
||||
JSStructuredCloneReader* reader,
|
||||
uint32_t tag,
|
||||
uint32_t data,
|
||||
void* closure)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
if (tag == SCTAG_DOM_BLOB) {
|
||||
NS_ASSERTION(!data, "Data should be empty");
|
||||
|
||||
// What we get back from the reader is a BlobImpl.
|
||||
// From that we create a new File.
|
||||
BlobImpl* blobImpl;
|
||||
if (JS_ReadBytes(reader, &blobImpl, sizeof(blobImpl))) {
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
// nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
|
||||
// called because the static analysis thinks dereferencing XPCOM objects
|
||||
// can GC (because in some cases it can!), and a return statement with a
|
||||
// JSObject* type means that JSObject* is on the stack as a raw pointer
|
||||
// while destructors are running.
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
{
|
||||
nsRefPtr<Blob> blob = Blob::Create(scInfo->window, blobImpl);
|
||||
if (!ToJSValue(cx, blob, &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return &val.toObject();
|
||||
}
|
||||
}
|
||||
|
||||
if (tag == SCTAG_DOM_FILELIST) {
|
||||
NS_ASSERTION(!data, "Data should be empty");
|
||||
|
||||
nsISupports* supports;
|
||||
if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, supports, &val))) {
|
||||
return val.toObjectOrNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks* runtimeCallbacks =
|
||||
js::GetContextStructuredCloneCallbacks(cx);
|
||||
|
||||
if (runtimeCallbacks) {
|
||||
return runtimeCallbacks->read(cx, reader, tag, data, nullptr);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
PostMessageEvent::WriteStructuredClone(JSContext* cx,
|
||||
JSStructuredCloneWriter* writer,
|
||||
JS::Handle<JSObject*> obj,
|
||||
void *closure)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
// See if this is a File/Blob object.
|
||||
{
|
||||
Blob* blob = nullptr;
|
||||
if (scInfo->subsumes && NS_SUCCEEDED(UNWRAP_OBJECT(Blob, obj, blob))) {
|
||||
BlobImpl* blobImpl = blob->Impl();
|
||||
if (JS_WriteUint32Pair(writer, SCTAG_DOM_BLOB, 0) &&
|
||||
JS_WriteBytes(writer, &blobImpl, sizeof(blobImpl))) {
|
||||
scInfo->event->StoreISupports(blobImpl);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
|
||||
nsContentUtils::XPConnect()->
|
||||
GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
|
||||
if (wrappedNative) {
|
||||
uint32_t scTag = 0;
|
||||
nsISupports* supports = wrappedNative->Native();
|
||||
|
||||
nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports);
|
||||
if (list && scInfo->subsumes)
|
||||
scTag = SCTAG_DOM_FILELIST;
|
||||
|
||||
if (scTag)
|
||||
return JS_WriteUint32Pair(writer, scTag, 0) &&
|
||||
JS_WriteBytes(writer, &supports, sizeof(supports)) &&
|
||||
scInfo->event->StoreISupports(supports);
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks* runtimeCallbacks =
|
||||
js::GetContextStructuredCloneCallbacks(cx);
|
||||
|
||||
if (runtimeCallbacks) {
|
||||
return runtimeCallbacks->write(cx, writer, obj, nullptr);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
PostMessageEvent::ReadTransferStructuredClone(JSContext* aCx,
|
||||
JSStructuredCloneReader* reader,
|
||||
uint32_t tag, void* aData,
|
||||
uint64_t aExtraData,
|
||||
void* aClosure,
|
||||
JS::MutableHandle<JSObject*> returnObject)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
if (tag == SCTAG_DOM_MAP_MESSAGEPORT) {
|
||||
MOZ_ASSERT(!aData);
|
||||
// aExtraData is the index of this port identifier.
|
||||
ErrorResult rv;
|
||||
nsRefPtr<MessagePort> port =
|
||||
MessagePort::Create(scInfo->window,
|
||||
scInfo->event->GetPortIdentifier(aExtraData),
|
||||
rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
scInfo->clonedPorts.AppendElement(port);
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
if (!GetOrCreateDOMReflector(aCx, port, &value)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
returnObject.set(&value.toObject());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
PostMessageEvent::TransferStructuredClone(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
void* aClosure,
|
||||
uint32_t* aTag,
|
||||
JS::TransferableOwnership* aOwnership,
|
||||
void** aContent,
|
||||
uint64_t* aExtraData)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
MessagePortBase* port = nullptr;
|
||||
nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (scInfo->transferredPorts.Contains(port)) {
|
||||
// No duplicates.
|
||||
return false;
|
||||
}
|
||||
|
||||
// We use aExtraData to store the index of this new port identifier.
|
||||
MessagePortIdentifier* identifier =
|
||||
scInfo->event->NewPortIdentifier(aExtraData);
|
||||
|
||||
if (!port->CloneAndDisentangle(*identifier)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
scInfo->transferredPorts.AppendElement(port);
|
||||
|
||||
*aTag = SCTAG_DOM_MAP_MESSAGEPORT;
|
||||
*aOwnership = JS::SCTAG_TMO_CUSTOM;
|
||||
*aContent = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
PostMessageEvent::FreeTransferStructuredClone(uint32_t aTag,
|
||||
JS::TransferableOwnership aOwnership,
|
||||
void *aContent,
|
||||
uint64_t aExtraData,
|
||||
void* aClosure)
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
PostMessageEvent::PostMessageEvent(nsGlobalWindow* aSource,
|
||||
const nsAString& aCallerOrigin,
|
||||
nsGlobalWindow* aTargetWindow,
|
||||
nsIPrincipal* aProvidedPrincipal,
|
||||
bool aTrustedCaller)
|
||||
: mSource(aSource),
|
||||
mCallerOrigin(aCallerOrigin),
|
||||
mTargetWindow(aTargetWindow),
|
||||
mProvidedPrincipal(aProvidedPrincipal),
|
||||
mTrustedCaller(aTrustedCaller)
|
||||
{
|
||||
MOZ_COUNT_CTOR(PostMessageEvent);
|
||||
}
|
||||
|
||||
PostMessageEvent::~PostMessageEvent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(PostMessageEvent);
|
||||
}
|
||||
|
||||
const MessagePortIdentifier&
|
||||
PostMessageEvent::GetPortIdentifier(uint64_t aId)
|
||||
{
|
||||
MOZ_ASSERT(aId < mPortIdentifiers.Length());
|
||||
return mPortIdentifiers[aId];
|
||||
}
|
||||
|
||||
MessagePortIdentifier*
|
||||
PostMessageEvent::NewPortIdentifier(uint64_t* aPosition)
|
||||
{
|
||||
*aPosition = mPortIdentifiers.Length();
|
||||
return mPortIdentifiers.AppendElement();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PostMessageEvent::Run()
|
||||
{
|
||||
MOZ_ASSERT(mTargetWindow->IsOuterWindow(),
|
||||
"should have been passed an outer window!");
|
||||
MOZ_ASSERT(!mSource || mSource->IsOuterWindow(),
|
||||
"should have been passed an outer window!");
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
// If we bailed before this point we're going to leak mMessage, but
|
||||
// that's probably better than crashing.
|
||||
|
||||
nsRefPtr<nsGlobalWindow> targetWindow;
|
||||
if (mTargetWindow->IsClosedOrClosing() ||
|
||||
!(targetWindow = mTargetWindow->GetCurrentInnerWindowInternal()) ||
|
||||
targetWindow->IsClosedOrClosing())
|
||||
return NS_OK;
|
||||
|
||||
MOZ_ASSERT(targetWindow->IsInnerWindow(),
|
||||
"we ordered an inner window!");
|
||||
JSAutoCompartment ac(cx, targetWindow->GetWrapperPreserveColor());
|
||||
|
||||
// Ensure that any origin which might have been provided is the origin of this
|
||||
// window's document. Note that we do this *now* instead of when postMessage
|
||||
// is called because the target window might have been navigated to a
|
||||
// different location between then and now. If this check happened when
|
||||
// postMessage was called, it would be fairly easy for a malicious webpage to
|
||||
// intercept messages intended for another site by carefully timing navigation
|
||||
// of the target window so it changed location after postMessage but before
|
||||
// now.
|
||||
if (mProvidedPrincipal) {
|
||||
// Get the target's origin either from its principal or, in the case the
|
||||
// principal doesn't carry a URI (e.g. the system principal), the target's
|
||||
// document.
|
||||
nsIPrincipal* targetPrin = targetWindow->GetPrincipal();
|
||||
if (NS_WARN_IF(!targetPrin))
|
||||
return NS_OK;
|
||||
|
||||
// Note: This is contrary to the spec with respect to file: URLs, which
|
||||
// the spec groups into a single origin, but given we intentionally
|
||||
// don't do that in other places it seems better to hold the line for
|
||||
// now. Long-term, we want HTML5 to address this so that we can
|
||||
// be compliant while being safer.
|
||||
if (!targetPrin->Equals(mProvidedPrincipal)) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Deserialize the structured clone data
|
||||
JS::Rooted<JS::Value> messageData(cx);
|
||||
StructuredCloneInfo scInfo;
|
||||
scInfo.event = this;
|
||||
scInfo.window = targetWindow;
|
||||
|
||||
if (!mBuffer.read(cx, &messageData, &sPostMessageCallbacks, &scInfo)) {
|
||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
}
|
||||
|
||||
// Create the event
|
||||
nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
|
||||
do_QueryInterface(static_cast<nsPIDOMWindow*>(targetWindow.get()));
|
||||
nsRefPtr<MessageEvent> event =
|
||||
new MessageEvent(eventTarget, nullptr, nullptr);
|
||||
|
||||
event->InitMessageEvent(NS_LITERAL_STRING("message"), false /*non-bubbling */,
|
||||
false /*cancelable */, messageData, mCallerOrigin,
|
||||
EmptyString(), mSource);
|
||||
|
||||
event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()),
|
||||
scInfo.clonedPorts));
|
||||
|
||||
// We can't simply call dispatchEvent on the window because doing so ends
|
||||
// up flipping the trusted bit on the event, and we don't want that to
|
||||
// happen because then untrusted content can call postMessage on a chrome
|
||||
// window if it can get a reference to it.
|
||||
|
||||
nsIPresShell *shell = targetWindow->GetExtantDoc()->GetShell();
|
||||
nsRefPtr<nsPresContext> presContext;
|
||||
if (shell)
|
||||
presContext = shell->GetPresContext();
|
||||
|
||||
event->SetTrusted(mTrustedCaller);
|
||||
WidgetEvent* internalEvent = event->GetInternalNSEvent();
|
||||
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
EventDispatcher::Dispatch(static_cast<nsPIDOMWindow*>(mTargetWindow),
|
||||
presContext,
|
||||
internalEvent,
|
||||
static_cast<dom::Event*>(event.get()),
|
||||
&status);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
PostMessageEvent::Write(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
JS::Handle<JS::Value> aTransfer, bool aSubsumes,
|
||||
nsPIDOMWindow* aWindow)
|
||||
{
|
||||
// We *must* clone the data here, or the JS::Value could be modified
|
||||
// by script
|
||||
StructuredCloneInfo scInfo;
|
||||
scInfo.event = this;
|
||||
scInfo.window = aWindow;
|
||||
|
||||
return mBuffer.write(aCx, aMessage, aTransfer, &sPostMessageCallbacks,
|
||||
&scInfo);
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
108
dom/base/PostMessageEvent.h
Normal file
108
dom/base/PostMessageEvent.h
Normal file
@ -0,0 +1,108 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_PostMessageEvent_h
|
||||
#define mozilla_dom_PostMessageEvent_h
|
||||
|
||||
#include "js/StructuredClone.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsRefPtr.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsGlobalWindow;
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class MessagePortBase;
|
||||
class MessagePortIdentifier;
|
||||
|
||||
/**
|
||||
* Class used to represent events generated by calls to Window.postMessage,
|
||||
* which asynchronously creates and dispatches events.
|
||||
*/
|
||||
class PostMessageEvent final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
PostMessageEvent(nsGlobalWindow* aSource,
|
||||
const nsAString& aCallerOrigin,
|
||||
nsGlobalWindow* aTargetWindow,
|
||||
nsIPrincipal* aProvidedPrincipal,
|
||||
bool aTrustedCaller);
|
||||
|
||||
bool Write(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
JS::Handle<JS::Value> aTransfer, bool aSubsumes,
|
||||
nsPIDOMWindow* aWindow);
|
||||
|
||||
private:
|
||||
~PostMessageEvent();
|
||||
|
||||
const MessagePortIdentifier& GetPortIdentifier(uint64_t aId);
|
||||
|
||||
MessagePortIdentifier* NewPortIdentifier(uint64_t* aPosition);
|
||||
|
||||
bool StoreISupports(nsISupports* aSupports)
|
||||
{
|
||||
mSupportsArray.AppendElement(aSupports);
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
ReadStructuredClone(JSContext* cx,
|
||||
JSStructuredCloneReader* reader,
|
||||
uint32_t tag,
|
||||
uint32_t data,
|
||||
void* closure);
|
||||
|
||||
static bool
|
||||
WriteStructuredClone(JSContext* cx,
|
||||
JSStructuredCloneWriter* writer,
|
||||
JS::Handle<JSObject*> obj,
|
||||
void *closure);
|
||||
|
||||
static bool
|
||||
ReadTransferStructuredClone(JSContext* aCx,
|
||||
JSStructuredCloneReader* reader,
|
||||
uint32_t tag, void* aData,
|
||||
uint64_t aExtraData,
|
||||
void* aClosure,
|
||||
JS::MutableHandle<JSObject*> returnObject);
|
||||
|
||||
static bool
|
||||
TransferStructuredClone(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
void* aClosure,
|
||||
uint32_t* aTag,
|
||||
JS::TransferableOwnership* aOwnership,
|
||||
void** aContent,
|
||||
uint64_t* aExtraData);
|
||||
|
||||
static void
|
||||
FreeTransferStructuredClone(uint32_t aTag,
|
||||
JS::TransferableOwnership aOwnership,
|
||||
void *aContent,
|
||||
uint64_t aExtraData,
|
||||
void* aClosure);
|
||||
|
||||
static const JSStructuredCloneCallbacks sPostMessageCallbacks;
|
||||
|
||||
JSAutoStructuredCloneBuffer mBuffer;
|
||||
nsRefPtr<nsGlobalWindow> mSource;
|
||||
nsString mCallerOrigin;
|
||||
nsRefPtr<nsGlobalWindow> mTargetWindow;
|
||||
nsCOMPtr<nsIPrincipal> mProvidedPrincipal;
|
||||
bool mTrustedCaller;
|
||||
nsTArray<nsCOMPtr<nsISupports>> mSupportsArray;
|
||||
nsTArray<MessagePortIdentifier> mPortIdentifiers;
|
||||
};
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
||||
#endif // mozilla_dom_PostMessageEvent_h
|
@ -7,6 +7,7 @@
|
||||
#include "ProcessGlobal.h"
|
||||
|
||||
#include "nsContentCID.h"
|
||||
#include "nsDOMClassInfoID.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -179,9 +179,6 @@ EXPORTS.mozilla.dom += [
|
||||
'ImageEncoder.h',
|
||||
'ImportManager.h',
|
||||
'Link.h',
|
||||
'MessageChannel.h',
|
||||
'MessagePort.h',
|
||||
'MessagePortList.h',
|
||||
'NameSpaceConstants.h',
|
||||
'Navigator.h',
|
||||
'NodeInfo.h',
|
||||
@ -237,8 +234,6 @@ UNIFIED_SOURCES += [
|
||||
'ImageEncoder.cpp',
|
||||
'ImportManager.cpp',
|
||||
'Link.cpp',
|
||||
'MessageChannel.cpp',
|
||||
'MessagePortList.cpp',
|
||||
'MultipartBlobImpl.cpp',
|
||||
'Navigator.cpp',
|
||||
'NodeInfo.cpp',
|
||||
@ -329,6 +324,7 @@ UNIFIED_SOURCES += [
|
||||
'PerformanceMark.cpp',
|
||||
'PerformanceMeasure.cpp',
|
||||
'PerformanceResourceTiming.cpp',
|
||||
'PostMessageEvent.cpp',
|
||||
'ProcessGlobal.cpp',
|
||||
'ResponsiveImageSelector.cpp',
|
||||
'SameProcessMessageQueue.cpp',
|
||||
@ -353,8 +349,6 @@ if CONFIG['MOZ_WEBRTC']:
|
||||
|
||||
# these files couldn't be in UNIFIED_SOURCES for now for reasons given below:
|
||||
SOURCES += [
|
||||
# this file doesn't like windows.h
|
||||
'MessagePort.cpp',
|
||||
# Because of OS X headers.
|
||||
'nsContentUtils.cpp',
|
||||
# this file doesn't like windows.h
|
||||
|
@ -218,6 +218,7 @@ nsIPrincipal *nsContentUtils::sNullSubjectPrincipal;
|
||||
nsIParserService *nsContentUtils::sParserService = nullptr;
|
||||
nsNameSpaceManager *nsContentUtils::sNameSpaceManager;
|
||||
nsIIOService *nsContentUtils::sIOService;
|
||||
nsIUUIDGenerator *nsContentUtils::sUUIDGenerator;
|
||||
nsIConsoleService *nsContentUtils::sConsoleService;
|
||||
nsDataHashtable<nsISupportsHashKey, EventNameMapping>* nsContentUtils::sAtomEventTable = nullptr;
|
||||
nsDataHashtable<nsStringHashKey, EventNameMapping>* nsContentUtils::sStringEventTable = nullptr;
|
||||
@ -551,6 +552,13 @@ nsContentUtils::Init()
|
||||
|
||||
Element::InitCCCallbacks();
|
||||
|
||||
nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
|
||||
do_GetService("@mozilla.org/uuid-generator;1", &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
uuidGenerator.forget(&sUUIDGenerator);
|
||||
|
||||
sInitialized = true;
|
||||
|
||||
return NS_OK;
|
||||
@ -1803,6 +1811,7 @@ nsContentUtils::Shutdown()
|
||||
NS_IF_RELEASE(sNullSubjectPrincipal);
|
||||
NS_IF_RELEASE(sParserService);
|
||||
NS_IF_RELEASE(sIOService);
|
||||
NS_IF_RELEASE(sUUIDGenerator);
|
||||
NS_IF_RELEASE(sLineBreaker);
|
||||
NS_IF_RELEASE(sWordBreaker);
|
||||
NS_IF_RELEASE(sBidiKeyboard);
|
||||
@ -7151,6 +7160,19 @@ nsContentUtils::IsJavascriptMIMEType(const nsAString& aMIMEType)
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentUtils::GenerateUUIDInPlace(nsID& aUUID)
|
||||
{
|
||||
MOZ_ASSERT(sUUIDGenerator);
|
||||
|
||||
nsresult rv = sUUIDGenerator->GenerateUUIDInPlace(&aUUID);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
nsContentUtils::GetInnerWindowID(nsIRequest* aRequest)
|
||||
{
|
||||
|
@ -85,6 +85,7 @@ class nsIStringBundleService;
|
||||
class nsISupportsArray;
|
||||
class nsISupportsHashKey;
|
||||
class nsIURI;
|
||||
class nsIUUIDGenerator;
|
||||
class nsIWidget;
|
||||
class nsIWordBreaker;
|
||||
class nsIXPConnect;
|
||||
@ -853,6 +854,11 @@ public:
|
||||
*/
|
||||
static uint32_t ParseSandboxAttributeToFlags(const nsAttrValue* sandboxAttr);
|
||||
|
||||
/**
|
||||
* Helper function that generates a UUID.
|
||||
*/
|
||||
static nsresult GenerateUUIDInPlace(nsID& aUUID);
|
||||
|
||||
|
||||
/**
|
||||
* Fill (with the parameters given) the localized string named |aKey| in
|
||||
@ -2438,6 +2444,7 @@ private:
|
||||
static nsNameSpaceManager *sNameSpaceManager;
|
||||
|
||||
static nsIIOService *sIOService;
|
||||
static nsIUUIDGenerator *sUUIDGenerator;
|
||||
|
||||
static bool sImgLoaderInitialized;
|
||||
static void InitImgLoader();
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "nsIComponentManager.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIClipboard.h"
|
||||
#include "nsIFormControl.h"
|
||||
#include "nsISelection.h"
|
||||
#include "nsWidgetsCID.h"
|
||||
#include "nsXPCOM.h"
|
||||
|
@ -277,15 +277,15 @@ BuildClonedMessageData(typename BlobTraits<Flavor>::ConcreteContentManagerType*
|
||||
SerializedStructuredCloneBuffer& buffer = aClonedData.data();
|
||||
buffer.data = aData.mData;
|
||||
buffer.dataLength = aData.mDataLength;
|
||||
const nsTArray<nsRefPtr<Blob>>& blobs = aData.mClosure.mBlobs;
|
||||
if (!blobs.IsEmpty()) {
|
||||
const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = aData.mClosure.mBlobImpls;
|
||||
if (!blobImpls.IsEmpty()) {
|
||||
typedef typename BlobTraits<Flavor>::ProtocolType ProtocolType;
|
||||
InfallibleTArray<ProtocolType*>& blobList = DataBlobs<Flavor>::Blobs(aClonedData);
|
||||
uint32_t length = blobs.Length();
|
||||
uint32_t length = blobImpls.Length();
|
||||
blobList.SetCapacity(length);
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
typename BlobTraits<Flavor>::BlobType* protocolActor =
|
||||
aManager->GetOrCreateActorForBlob(blobs[i]);
|
||||
aManager->GetOrCreateActorForBlobImpl(blobImpls[i]);
|
||||
if (!protocolActor) {
|
||||
return false;
|
||||
}
|
||||
@ -323,7 +323,7 @@ UnpackClonedMessageData(const ClonedMessageData& aData)
|
||||
cloneData.mDataLength = buffer.dataLength;
|
||||
if (!blobs.IsEmpty()) {
|
||||
uint32_t length = blobs.Length();
|
||||
cloneData.mClosure.mBlobs.SetCapacity(length);
|
||||
cloneData.mClosure.mBlobImpls.SetCapacity(length);
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
auto* blob =
|
||||
static_cast<typename BlobTraits<Flavor>::BlobType*>(blobs[i]);
|
||||
@ -332,10 +332,7 @@ UnpackClonedMessageData(const ClonedMessageData& aData)
|
||||
nsRefPtr<BlobImpl> blobImpl = blob->GetBlobImpl();
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
// This object will be duplicated with a correct parent before being
|
||||
// exposed to JS.
|
||||
nsRefPtr<Blob> domBlob = Blob::Create(nullptr, blobImpl);
|
||||
cloneData.mClosure.mBlobs.AppendElement(domBlob);
|
||||
cloneData.mClosure.mBlobImpls.AppendElement(blobImpl);
|
||||
}
|
||||
}
|
||||
return cloneData;
|
||||
|
@ -59,7 +59,6 @@
|
||||
#include "nsLayoutStatics.h"
|
||||
#include "nsCCUncollectableMarker.h"
|
||||
#include "mozilla/dom/workers/Workers.h"
|
||||
#include "mozilla/dom/MessagePortList.h"
|
||||
#include "mozilla/dom/ToJSValue.h"
|
||||
#include "nsJSPrincipals.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
@ -69,9 +68,9 @@
|
||||
#include "mozilla/MouseEvents.h"
|
||||
#include "mozilla/ProcessHangMonitor.h"
|
||||
#include "AudioChannelService.h"
|
||||
#include "MessageEvent.h"
|
||||
#include "nsAboutProtocolUtils.h"
|
||||
#include "nsCharTraits.h" // NS_IS_HIGH/LOW_SURROGATE
|
||||
#include "PostMessageEvent.h"
|
||||
|
||||
// Interfaces Needed
|
||||
#include "nsIFrame.h"
|
||||
@ -179,13 +178,9 @@
|
||||
#include "prprf.h"
|
||||
|
||||
#include "mozilla/dom/MessageChannel.h"
|
||||
#include "mozilla/dom/MessagePort.h"
|
||||
#include "mozilla/dom/MessagePortBinding.h"
|
||||
#include "mozilla/dom/indexedDB/IDBFactory.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
|
||||
#ifdef MOZ_GAMEPAD
|
||||
#include "mozilla/dom/Gamepad.h"
|
||||
#include "mozilla/dom/GamepadService.h"
|
||||
@ -207,7 +202,6 @@
|
||||
#include "TimeChangeObserver.h"
|
||||
#include "TouchCaret.h"
|
||||
#include "mozilla/dom/AudioContext.h"
|
||||
#include "mozilla/dom/BlobBinding.h"
|
||||
#include "mozilla/dom/BrowserElementDictionariesBinding.h"
|
||||
#include "mozilla/dom/cache/CacheStorage.h"
|
||||
#include "mozilla/dom/Console.h"
|
||||
@ -8038,367 +8032,6 @@ nsGlobalWindow::CallerInnerWindow()
|
||||
return static_cast<nsGlobalWindow*>(win.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Class used to represent events generated by calls to Window.postMessage,
|
||||
* which asynchronously creates and dispatches events.
|
||||
*/
|
||||
class PostMessageEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
PostMessageEvent(nsGlobalWindow* aSource,
|
||||
const nsAString& aCallerOrigin,
|
||||
nsGlobalWindow* aTargetWindow,
|
||||
nsIPrincipal* aProvidedPrincipal,
|
||||
bool aTrustedCaller)
|
||||
: mSource(aSource),
|
||||
mCallerOrigin(aCallerOrigin),
|
||||
mTargetWindow(aTargetWindow),
|
||||
mProvidedPrincipal(aProvidedPrincipal),
|
||||
mTrustedCaller(aTrustedCaller)
|
||||
{
|
||||
MOZ_COUNT_CTOR(PostMessageEvent);
|
||||
}
|
||||
|
||||
protected:
|
||||
~PostMessageEvent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(PostMessageEvent);
|
||||
}
|
||||
|
||||
public:
|
||||
JSAutoStructuredCloneBuffer& Buffer()
|
||||
{
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
bool StoreISupports(nsISupports* aSupports)
|
||||
{
|
||||
mSupportsArray.AppendElement(aSupports);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
JSAutoStructuredCloneBuffer mBuffer;
|
||||
nsRefPtr<nsGlobalWindow> mSource;
|
||||
nsString mCallerOrigin;
|
||||
nsRefPtr<nsGlobalWindow> mTargetWindow;
|
||||
nsCOMPtr<nsIPrincipal> mProvidedPrincipal;
|
||||
bool mTrustedCaller;
|
||||
nsTArray<nsCOMPtr<nsISupports> > mSupportsArray;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
struct StructuredCloneInfo {
|
||||
PostMessageEvent* event;
|
||||
bool subsumes;
|
||||
nsPIDOMWindow* window;
|
||||
nsRefPtrHashtable<nsRefPtrHashKey<MessagePortBase>, MessagePortBase> ports;
|
||||
};
|
||||
|
||||
static JSObject*
|
||||
PostMessageReadStructuredClone(JSContext* cx,
|
||||
JSStructuredCloneReader* reader,
|
||||
uint32_t tag,
|
||||
uint32_t data,
|
||||
void* closure)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
if (tag == SCTAG_DOM_BLOB) {
|
||||
NS_ASSERTION(!data, "Data should be empty");
|
||||
|
||||
// What we get back from the reader is a BlobImpl.
|
||||
// From that we create a new File.
|
||||
BlobImpl* blobImpl;
|
||||
if (JS_ReadBytes(reader, &blobImpl, sizeof(blobImpl))) {
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
// nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
|
||||
// called because the static analysis thinks dereferencing XPCOM objects
|
||||
// can GC (because in some cases it can!), and a return statement with a
|
||||
// JSObject* type means that JSObject* is on the stack as a raw pointer
|
||||
// while destructors are running.
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
{
|
||||
nsRefPtr<Blob> blob = Blob::Create(scInfo->window, blobImpl);
|
||||
if (!ToJSValue(cx, blob, &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return &val.toObject();
|
||||
}
|
||||
}
|
||||
|
||||
if (tag == SCTAG_DOM_FILELIST) {
|
||||
NS_ASSERTION(!data, "Data should be empty");
|
||||
|
||||
nsISupports* supports;
|
||||
if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, supports, &val))) {
|
||||
return val.toObjectOrNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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!");
|
||||
|
||||
// See if this is a File/Blob object.
|
||||
{
|
||||
Blob* blob = nullptr;
|
||||
if (scInfo->subsumes && NS_SUCCEEDED(UNWRAP_OBJECT(Blob, obj, blob))) {
|
||||
BlobImpl* blobImpl = blob->Impl();
|
||||
if (JS_WriteUint32Pair(writer, SCTAG_DOM_BLOB, 0) &&
|
||||
JS_WriteBytes(writer, &blobImpl, sizeof(blobImpl))) {
|
||||
scInfo->event->StoreISupports(blobImpl);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
|
||||
nsContentUtils::XPConnect()->
|
||||
GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
|
||||
if (wrappedNative) {
|
||||
uint32_t scTag = 0;
|
||||
nsISupports* supports = wrappedNative->Native();
|
||||
|
||||
nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports);
|
||||
if (list && scInfo->subsumes)
|
||||
scTag = SCTAG_DOM_FILELIST;
|
||||
|
||||
if (scTag)
|
||||
return JS_WriteUint32Pair(writer, scTag, 0) &&
|
||||
JS_WriteBytes(writer, &supports, sizeof(supports)) &&
|
||||
scInfo->event->StoreISupports(supports);
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks* runtimeCallbacks =
|
||||
js::GetContextStructuredCloneCallbacks(cx);
|
||||
|
||||
if (runtimeCallbacks) {
|
||||
return runtimeCallbacks->write(cx, writer, obj, nullptr);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
PostMessageReadTransferStructuredClone(JSContext* aCx,
|
||||
JSStructuredCloneReader* reader,
|
||||
uint32_t tag, void* aData,
|
||||
uint64_t aExtraData,
|
||||
void* aClosure,
|
||||
JS::MutableHandle<JSObject*> returnObject)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
if (tag == SCTAG_DOM_MAP_MESSAGEPORT) {
|
||||
MessagePort* port = static_cast<MessagePort*>(aData);
|
||||
port->BindToOwner(scInfo->window);
|
||||
scInfo->ports.Put(port, nullptr);
|
||||
|
||||
JS::Rooted<JSObject*> obj(aCx, port->WrapObject(aCx, nullptr));
|
||||
if (JS_WrapObject(aCx, &obj)) {
|
||||
MOZ_ASSERT(port->GetOwner() == scInfo->window);
|
||||
returnObject.set(obj);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
PostMessageTransferStructuredClone(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
void* aClosure,
|
||||
uint32_t* aTag,
|
||||
JS::TransferableOwnership* aOwnership,
|
||||
void** aContent,
|
||||
uint64_t* aExtraData)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
MessagePortBase* port = nullptr;
|
||||
nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsRefPtr<MessagePortBase> newPort;
|
||||
if (scInfo->ports.Get(port, getter_AddRefs(newPort))) {
|
||||
// No duplicate.
|
||||
return false;
|
||||
}
|
||||
|
||||
newPort = port->Clone();
|
||||
scInfo->ports.Put(port, newPort);
|
||||
|
||||
*aTag = SCTAG_DOM_MAP_MESSAGEPORT;
|
||||
*aOwnership = JS::SCTAG_TMO_CUSTOM;
|
||||
*aContent = newPort;
|
||||
*aExtraData = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
PostMessageFreeTransferStructuredClone(uint32_t aTag, JS::TransferableOwnership aOwnership,
|
||||
void *aContent, uint64_t aExtraData, void* aClosure)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
|
||||
nsRefPtr<MessagePortBase> port(static_cast<MessagePort*>(aContent));
|
||||
scInfo->ports.Remove(port);
|
||||
}
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks kPostMessageCallbacks = {
|
||||
PostMessageReadStructuredClone,
|
||||
PostMessageWriteStructuredClone,
|
||||
nullptr,
|
||||
PostMessageReadTransferStructuredClone,
|
||||
PostMessageTransferStructuredClone,
|
||||
PostMessageFreeTransferStructuredClone
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
static PLDHashOperator
|
||||
PopulateMessagePortList(MessagePortBase* aKey, MessagePortBase* aValue, void* aClosure)
|
||||
{
|
||||
nsTArray<nsRefPtr<MessagePortBase> > *array =
|
||||
static_cast<nsTArray<nsRefPtr<MessagePortBase> > *>(aClosure);
|
||||
|
||||
array->AppendElement(aKey);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PostMessageEvent::Run()
|
||||
{
|
||||
MOZ_ASSERT(mTargetWindow->IsOuterWindow(),
|
||||
"should have been passed an outer window!");
|
||||
MOZ_ASSERT(!mSource || mSource->IsOuterWindow(),
|
||||
"should have been passed an outer window!");
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
// If we bailed before this point we're going to leak mMessage, but
|
||||
// that's probably better than crashing.
|
||||
|
||||
nsRefPtr<nsGlobalWindow> targetWindow;
|
||||
if (mTargetWindow->IsClosedOrClosing() ||
|
||||
!(targetWindow = mTargetWindow->GetCurrentInnerWindowInternal()) ||
|
||||
targetWindow->IsClosedOrClosing())
|
||||
return NS_OK;
|
||||
|
||||
MOZ_ASSERT(targetWindow->IsInnerWindow(),
|
||||
"we ordered an inner window!");
|
||||
JSAutoCompartment ac(cx, targetWindow->GetWrapperPreserveColor());
|
||||
|
||||
// Ensure that any origin which might have been provided is the origin of this
|
||||
// window's document. Note that we do this *now* instead of when postMessage
|
||||
// is called because the target window might have been navigated to a
|
||||
// different location between then and now. If this check happened when
|
||||
// postMessage was called, it would be fairly easy for a malicious webpage to
|
||||
// intercept messages intended for another site by carefully timing navigation
|
||||
// of the target window so it changed location after postMessage but before
|
||||
// now.
|
||||
if (mProvidedPrincipal) {
|
||||
// Get the target's origin either from its principal or, in the case the
|
||||
// principal doesn't carry a URI (e.g. the system principal), the target's
|
||||
// document.
|
||||
nsIPrincipal* targetPrin = targetWindow->GetPrincipal();
|
||||
if (NS_WARN_IF(!targetPrin))
|
||||
return NS_OK;
|
||||
|
||||
// Note: This is contrary to the spec with respect to file: URLs, which
|
||||
// the spec groups into a single origin, but given we intentionally
|
||||
// don't do that in other places it seems better to hold the line for
|
||||
// now. Long-term, we want HTML5 to address this so that we can
|
||||
// be compliant while being safer.
|
||||
if (!targetPrin->Equals(mProvidedPrincipal)) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Deserialize the structured clone data
|
||||
JS::Rooted<JS::Value> messageData(cx);
|
||||
StructuredCloneInfo scInfo;
|
||||
scInfo.event = this;
|
||||
scInfo.window = targetWindow;
|
||||
|
||||
if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
|
||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
}
|
||||
|
||||
// Create the event
|
||||
nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
|
||||
do_QueryInterface(static_cast<nsPIDOMWindow*>(targetWindow.get()));
|
||||
nsRefPtr<MessageEvent> event =
|
||||
new MessageEvent(eventTarget, nullptr, nullptr);
|
||||
|
||||
event->InitMessageEvent(NS_LITERAL_STRING("message"), false /*non-bubbling */,
|
||||
false /*cancelable */, messageData, mCallerOrigin,
|
||||
EmptyString(), mSource);
|
||||
|
||||
nsTArray<nsRefPtr<MessagePortBase> > ports;
|
||||
scInfo.ports.EnumerateRead(PopulateMessagePortList, &ports);
|
||||
event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()), ports));
|
||||
|
||||
// We can't simply call dispatchEvent on the window because doing so ends
|
||||
// up flipping the trusted bit on the event, and we don't want that to
|
||||
// happen because then untrusted content can call postMessage on a chrome
|
||||
// window if it can get a reference to it.
|
||||
|
||||
nsIPresShell *shell = targetWindow->mDoc->GetShell();
|
||||
nsRefPtr<nsPresContext> presContext;
|
||||
if (shell)
|
||||
presContext = shell->GetPresContext();
|
||||
|
||||
event->SetTrusted(mTrustedCaller);
|
||||
WidgetEvent* internalEvent = event->GetInternalNSEvent();
|
||||
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
EventDispatcher::Dispatch(static_cast<nsPIDOMWindow*>(mTargetWindow),
|
||||
presContext,
|
||||
internalEvent,
|
||||
static_cast<dom::Event*>(event.get()),
|
||||
&status);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const nsAString& aTargetOrigin,
|
||||
@ -8522,18 +8155,13 @@ nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
providedPrincipal,
|
||||
nsContentUtils::IsCallerChrome());
|
||||
|
||||
// We *must* clone the data here, or the JS::Value could be modified
|
||||
// by script
|
||||
StructuredCloneInfo scInfo;
|
||||
scInfo.event = event;
|
||||
scInfo.window = this;
|
||||
|
||||
nsIPrincipal* principal = GetPrincipal();
|
||||
JS::Rooted<JS::Value> message(aCx, aMessage);
|
||||
JS::Rooted<JS::Value> transfer(aCx, aTransfer);
|
||||
if (NS_FAILED(callerPrin->Subsumes(principal, &scInfo.subsumes)) ||
|
||||
!event->Buffer().write(aCx, message, transfer, &kPostMessageCallbacks,
|
||||
&scInfo)) {
|
||||
bool subsumes;
|
||||
|
||||
if (NS_FAILED(callerPrin->Subsumes(principal, &subsumes)) ||
|
||||
!event->Write(aCx, message, transfer, subsumes, this)) {
|
||||
aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return;
|
||||
}
|
||||
|
@ -111,6 +111,7 @@ class MozSelfSupport;
|
||||
class Navigator;
|
||||
class OwningExternalOrWindowProxy;
|
||||
class Promise;
|
||||
class PostMessageEvent;
|
||||
struct RequestInit;
|
||||
class RequestOrUSVString;
|
||||
class Selection;
|
||||
@ -1752,7 +1753,7 @@ protected:
|
||||
|
||||
friend class nsDOMScriptableHelper;
|
||||
friend class nsDOMWindowUtils;
|
||||
friend class PostMessageEvent;
|
||||
friend class mozilla::dom::PostMessageEvent;
|
||||
friend class DesktopNotification;
|
||||
|
||||
static WindowByIdTable* sWindowsById;
|
||||
|
@ -13,7 +13,6 @@ support-files =
|
||||
[test_domrequesthelper.xul]
|
||||
[test_url.xul]
|
||||
[test_console.xul]
|
||||
[test_messageChannel.xul]
|
||||
[test_navigator_resolve_identity_xrays.xul]
|
||||
[test_sendQueryContentAndSelectionSetEvent.html]
|
||||
[test_bug1016960.html]
|
||||
|
@ -4,10 +4,6 @@ support-files =
|
||||
iframe_bug976673.html
|
||||
iframe_main_bug1022229.html
|
||||
iframe_sandbox_bug1022229.html
|
||||
iframe_messageChannel_cloning.html
|
||||
iframe_messageChannel_chrome.html
|
||||
iframe_messageChannel_pingpong.html
|
||||
iframe_messageChannel_post.html
|
||||
file_empty.html
|
||||
iframe_postMessage_solidus.html
|
||||
file_setname.html
|
||||
@ -286,15 +282,7 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e1
|
||||
[test_history_state_null.html]
|
||||
[test_Image_constructor.html]
|
||||
[test_innersize_scrollport.html]
|
||||
[test_messageChannel.html]
|
||||
[test_messageChannel_cloning.html]
|
||||
[test_messageChannel_pingpong.html]
|
||||
[test_messageChannel_post.html]
|
||||
[test_messageChannel_pref.html]
|
||||
[test_messageChannel_start.html]
|
||||
[test_messagemanager_targetchain.html]
|
||||
[test_messageChannel_transferable.html]
|
||||
[test_messageChannel_unshipped.html]
|
||||
[test_named_frames.html]
|
||||
[test_navigator_resolve_identity.html]
|
||||
[test_navigator_language.html]
|
||||
|
@ -729,9 +729,6 @@ DOMInterfaces = {
|
||||
'MessagePort': {
|
||||
'nativeType': 'mozilla::dom::MessagePortBase',
|
||||
'headerFile': 'mozilla/dom/MessagePort.h',
|
||||
'binaryNames': {
|
||||
'postMessage': 'postMessageMoz',
|
||||
},
|
||||
},
|
||||
|
||||
'MimeType': {
|
||||
|
@ -212,14 +212,15 @@ public:
|
||||
PBackgroundChild* backgroundManager = mActor->Manager();
|
||||
MOZ_ASSERT(backgroundManager);
|
||||
|
||||
const nsTArray<nsRefPtr<Blob>>& blobs = mData->mClosure.mBlobs;
|
||||
const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = mData->mClosure.mBlobImpls;
|
||||
|
||||
if (!blobs.IsEmpty()) {
|
||||
message.blobsChild().SetCapacity(blobs.Length());
|
||||
if (!blobImpls.IsEmpty()) {
|
||||
message.blobsChild().SetCapacity(blobImpls.Length());
|
||||
|
||||
for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
|
||||
for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
|
||||
PBlobChild* blobChild =
|
||||
BackgroundChild::GetOrCreateActorForBlob(backgroundManager, blobs[i]);
|
||||
BackgroundChild::GetOrCreateActorForBlobImpl(backgroundManager,
|
||||
blobImpls[i]);
|
||||
MOZ_ASSERT(blobChild);
|
||||
|
||||
message.blobsChild().AppendElement(blobChild);
|
||||
@ -541,9 +542,9 @@ BroadcastChannel::PostMessageInternal(JSContext* aCx,
|
||||
return;
|
||||
}
|
||||
|
||||
const nsTArray<nsRefPtr<Blob>>& blobs = data->mClosure.mBlobs;
|
||||
for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
|
||||
if (!blobs[i]->Impl()->MayBeClonedToOtherThreads()) {
|
||||
const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = data->mClosure.mBlobImpls;
|
||||
for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
|
||||
if (!blobImpls[i]->MayBeClonedToOtherThreads()) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return;
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ BroadcastChannelChild::RecvNotify(const ClonedMessageData& aData)
|
||||
{
|
||||
// Make sure to retrieve all blobs from the message before returning to avoid
|
||||
// leaking their actors.
|
||||
nsTArray<nsRefPtr<Blob>> blobs;
|
||||
nsTArray<nsRefPtr<BlobImpl>> blobs;
|
||||
if (!aData.blobsChild().IsEmpty()) {
|
||||
blobs.SetCapacity(aData.blobsChild().Length());
|
||||
|
||||
@ -50,8 +50,7 @@ BroadcastChannelChild::RecvNotify(const ClonedMessageData& aData)
|
||||
nsRefPtr<BlobImpl> impl =
|
||||
static_cast<BlobChild*>(aData.blobsChild()[i])->GetBlobImpl();
|
||||
|
||||
nsRefPtr<Blob> blob = Blob::Create(mBC ? mBC->GetOwner() : nullptr, impl);
|
||||
blobs.AppendElement(blob);
|
||||
blobs.AppendElement(impl);
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +91,7 @@ BroadcastChannelChild::RecvNotify(const ClonedMessageData& aData)
|
||||
StructuredCloneData cloneData;
|
||||
cloneData.mData = buffer.data;
|
||||
cloneData.mDataLength = buffer.dataLength;
|
||||
cloneData.mClosure.mBlobs.SwapElements(blobs);
|
||||
cloneData.mClosure.mBlobImpls.SwapElements(blobs);
|
||||
|
||||
JS::Rooted<JS::Value> value(cx, JS::NullValue());
|
||||
if (cloneData.mDataLength && !ReadStructuredClone(cx, cloneData, &value)) {
|
||||
|
@ -50,14 +50,14 @@ Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
|
||||
// while destructors are running.
|
||||
JS::Rooted<JS::Value> val(aCx);
|
||||
{
|
||||
MOZ_ASSERT(aData < closure->mBlobs.Length());
|
||||
nsRefPtr<Blob> blob = closure->mBlobs[aData];
|
||||
MOZ_ASSERT(aData < closure->mBlobImpls.Length());
|
||||
nsRefPtr<BlobImpl> blobImpl = closure->mBlobImpls[aData];
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
// Blob should not be mutable.
|
||||
bool isMutable;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(blob->GetMutable(&isMutable)));
|
||||
MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
|
||||
MOZ_ASSERT(!isMutable);
|
||||
}
|
||||
#endif
|
||||
@ -66,7 +66,7 @@ Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
|
||||
nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
|
||||
MOZ_ASSERT(global);
|
||||
|
||||
nsRefPtr<Blob> newBlob = Blob::Create(global, blob->Impl());
|
||||
nsRefPtr<Blob> newBlob = Blob::Create(global, blobImpl);
|
||||
if (!ToJSValue(aCx, newBlob, &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -93,8 +93,8 @@ Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob)) &&
|
||||
NS_SUCCEEDED(blob->SetMutable(false)) &&
|
||||
JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
|
||||
closure->mBlobs.Length())) {
|
||||
closure->mBlobs.AppendElement(blob);
|
||||
closure->mBlobImpls.Length())) {
|
||||
closure->mBlobImpls.AppendElement(blob->Impl());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ namespace dom {
|
||||
struct
|
||||
StructuredCloneClosure
|
||||
{
|
||||
nsTArray<nsRefPtr<Blob>> mBlobs;
|
||||
nsTArray<nsRefPtr<BlobImpl>> mBlobImpls;
|
||||
};
|
||||
|
||||
struct
|
||||
|
228
dom/messagechannel/MessageChannel.cpp
Normal file
228
dom/messagechannel/MessageChannel.cpp
Normal file
@ -0,0 +1,228 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MessageChannel.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/MessageChannelBinding.h"
|
||||
#include "mozilla/dom/MessagePort.h"
|
||||
#include "mozilla/dom/Navigator.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/dom/WorkerRunnable.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MessageChannel, mWindow, mPort1, mPort2)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(MessageChannel)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(MessageChannel)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MessageChannel)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
namespace {
|
||||
bool gPrefInitialized = false;
|
||||
bool gPrefEnabled = false;
|
||||
|
||||
bool
|
||||
CheckPermission(nsIPrincipal* aPrincipal, bool aCallerChrome)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!gPrefInitialized) {
|
||||
Preferences::AddBoolVarCache(&gPrefEnabled, "dom.messageChannel.enabled");
|
||||
gPrefInitialized = true;
|
||||
}
|
||||
|
||||
// Enabled by pref
|
||||
if (gPrefEnabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Chrome callers are allowed.
|
||||
if (aCallerChrome) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
if (NS_FAILED(aPrincipal->GetURI(getter_AddRefs(uri))) || !uri) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isResource = false;
|
||||
if (NS_FAILED(uri->SchemeIs("resource", &isResource))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isResource;
|
||||
}
|
||||
|
||||
nsIPrincipal*
|
||||
GetPrincipalFromWorkerPrivate(workers::WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsIPrincipal* principal = aWorkerPrivate->GetPrincipal();
|
||||
if (principal) {
|
||||
return principal;
|
||||
}
|
||||
|
||||
// Walk up to our containing page
|
||||
workers::WorkerPrivate* wp = aWorkerPrivate;
|
||||
while (wp->GetParent()) {
|
||||
wp = wp->GetParent();
|
||||
}
|
||||
|
||||
nsPIDOMWindow* window = wp->GetWindow();
|
||||
if (!window) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIDocument* doc = window->GetExtantDoc();
|
||||
if (!doc) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return doc->NodePrincipal();
|
||||
}
|
||||
|
||||
// A WorkerMainThreadRunnable to synchronously dispatch the call of
|
||||
// CheckPermission() from the worker thread to the main thread.
|
||||
class CheckPermissionRunnable final : public workers::WorkerMainThreadRunnable
|
||||
{
|
||||
public:
|
||||
bool mResult;
|
||||
bool mCallerChrome;
|
||||
|
||||
explicit CheckPermissionRunnable(workers::WorkerPrivate* aWorkerPrivate)
|
||||
: workers::WorkerMainThreadRunnable(aWorkerPrivate)
|
||||
, mResult(false)
|
||||
, mCallerChrome(false)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
mCallerChrome = aWorkerPrivate->UsesSystemPrincipal();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool
|
||||
MainThreadRun() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsIPrincipal* principal = GetPrincipalFromWorkerPrivate(mWorkerPrivate);
|
||||
if (!principal) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isNullPrincipal;
|
||||
nsresult rv = principal->GetIsNullPrincipal(&isNullPrincipal);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(isNullPrincipal)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
mResult = CheckPermission(principal, mCallerChrome);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
/* static */ bool
|
||||
MessageChannel::Enabled(JSContext* aCx, JSObject* aGlobal)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
JS::Rooted<JSObject*> global(aCx, aGlobal);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> win = Navigator::GetWindowFromGlobal(global);
|
||||
if (!win) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIDocument* doc = win->GetExtantDoc();
|
||||
if (!doc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return CheckPermission(doc->NodePrincipal(),
|
||||
nsContentUtils::IsCallerChrome());
|
||||
}
|
||||
|
||||
workers::WorkerPrivate* workerPrivate =
|
||||
workers::GetWorkerPrivateFromContext(aCx);
|
||||
workerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
nsRefPtr<CheckPermissionRunnable> runnable =
|
||||
new CheckPermissionRunnable(workerPrivate);
|
||||
runnable->Dispatch(aCx);
|
||||
|
||||
return runnable->mResult;
|
||||
}
|
||||
|
||||
MessageChannel::MessageChannel(nsPIDOMWindow* aWindow)
|
||||
: mWindow(aWindow)
|
||||
{
|
||||
}
|
||||
|
||||
MessageChannel::~MessageChannel()
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
MessageChannel::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return MessageChannelBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<MessageChannel>
|
||||
MessageChannel::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
|
||||
{
|
||||
// window can be null in workers.
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
|
||||
nsID portUUID1;
|
||||
aRv = nsContentUtils::GenerateUUIDInPlace(portUUID1);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsID portUUID2;
|
||||
aRv = nsContentUtils::GenerateUUIDInPlace(portUUID2);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<MessageChannel> channel = new MessageChannel(window);
|
||||
|
||||
channel->mPort1 = MessagePort::Create(window, portUUID1, portUUID2, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
channel->mPort2 = MessagePort::Create(window, portUUID2, portUUID1, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
channel->mPort1->UnshippedEntangle(channel->mPort2);
|
||||
channel->mPort2->UnshippedEntangle(channel->mPort1);
|
||||
|
||||
return channel.forget();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -31,8 +31,6 @@ public:
|
||||
static bool Enabled(JSContext* aCx, JSObject* aGlobal);
|
||||
|
||||
public:
|
||||
explicit MessageChannel(nsPIDOMWindow* aWindow);
|
||||
|
||||
nsPIDOMWindow*
|
||||
GetParentObject() const
|
||||
{
|
||||
@ -58,6 +56,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
explicit MessageChannel(nsPIDOMWindow* aWindow);
|
||||
~MessageChannel();
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
867
dom/messagechannel/MessagePort.cpp
Normal file
867
dom/messagechannel/MessagePort.cpp
Normal file
@ -0,0 +1,867 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MessagePort.h"
|
||||
|
||||
#include "MessageEvent.h"
|
||||
#include "MessagePortChild.h"
|
||||
#include "mozilla/dom/BlobBinding.h"
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/MessageChannel.h"
|
||||
#include "mozilla/dom/MessagePortBinding.h"
|
||||
#include "mozilla/dom/MessagePortChild.h"
|
||||
#include "mozilla/dom/MessagePortList.h"
|
||||
#include "mozilla/dom/PMessagePort.h"
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/dom/WorkerScope.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "ScriptSettings.h"
|
||||
#include "SharedMessagePortMessage.h"
|
||||
|
||||
#include "nsIBFCacheEntry.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMFileList.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#undef PostMessage
|
||||
#endif
|
||||
|
||||
using namespace mozilla::dom::workers;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class DispatchEventRunnable final : public nsICancelableRunnable
|
||||
{
|
||||
friend class MessagePort;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
explicit DispatchEventRunnable(MessagePort* aPort)
|
||||
: mPort(aPort)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
MOZ_ASSERT(mPort);
|
||||
MOZ_ASSERT(mPort->mDispatchRunnable == this);
|
||||
mPort->mDispatchRunnable = nullptr;
|
||||
mPort->Dispatch();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Cancel() override
|
||||
{
|
||||
mPort = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~DispatchEventRunnable()
|
||||
{}
|
||||
|
||||
nsRefPtr<MessagePort> mPort;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(DispatchEventRunnable, nsICancelableRunnable, nsIRunnable)
|
||||
|
||||
class PostMessageRunnable final : public nsICancelableRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
PostMessageRunnable(MessagePort* aPort, SharedMessagePortMessage* aData)
|
||||
: mPort(aPort)
|
||||
, mData(aData)
|
||||
{
|
||||
MOZ_ASSERT(aPort);
|
||||
MOZ_ASSERT(aData);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> globalObject;
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
globalObject = do_QueryInterface(mPort->GetParentObject());
|
||||
} else {
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
globalObject = workerPrivate->GlobalScope();
|
||||
}
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (!globalObject || !jsapi.Init(globalObject)) {
|
||||
NS_WARNING("Failed to initialize AutoJSAPI object.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
nsTArray<nsRefPtr<MessagePort>> ports;
|
||||
nsCOMPtr<nsPIDOMWindow> window =
|
||||
do_QueryInterface(mPort->GetParentObject());
|
||||
|
||||
JS::Rooted<JS::Value> value(cx);
|
||||
if (!mData->mData.IsEmpty()) {
|
||||
bool ok = ReadStructuredCloneWithTransfer(cx, mData->mData,
|
||||
mData->mClosure,
|
||||
&value, window, ports);
|
||||
FreeStructuredClone(mData->mData, mData->mClosure);
|
||||
|
||||
if (!ok) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// The data should be already be cleaned.
|
||||
MOZ_ASSERT(!mData->mData.Length());
|
||||
|
||||
// Create the event
|
||||
nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
|
||||
do_QueryInterface(mPort->GetOwner());
|
||||
nsRefPtr<MessageEvent> event =
|
||||
new MessageEvent(eventTarget, nullptr, nullptr);
|
||||
|
||||
event->InitMessageEvent(NS_LITERAL_STRING("message"),
|
||||
false /* non-bubbling */,
|
||||
false /* cancelable */, value, EmptyString(),
|
||||
EmptyString(), nullptr);
|
||||
event->SetTrusted(true);
|
||||
event->SetSource(mPort);
|
||||
|
||||
nsTArray<nsRefPtr<MessagePortBase>> array;
|
||||
array.SetCapacity(ports.Length());
|
||||
for (uint32_t i = 0; i < ports.Length(); ++i) {
|
||||
array.AppendElement(ports[i]);
|
||||
}
|
||||
|
||||
nsRefPtr<MessagePortList> portList =
|
||||
new MessagePortList(static_cast<dom::Event*>(event.get()), array);
|
||||
event->SetPorts(portList);
|
||||
|
||||
bool dummy;
|
||||
mPort->DispatchEvent(static_cast<dom::Event*>(event.get()), &dummy);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Cancel() override
|
||||
{
|
||||
mPort = nullptr;
|
||||
mData = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~PostMessageRunnable()
|
||||
{}
|
||||
|
||||
nsRefPtr<MessagePort> mPort;
|
||||
nsRefPtr<SharedMessagePortMessage> mData;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(PostMessageRunnable, nsICancelableRunnable, nsIRunnable)
|
||||
|
||||
MessagePortBase::MessagePortBase(nsPIDOMWindow* aWindow)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
{
|
||||
}
|
||||
|
||||
MessagePortBase::MessagePortBase()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
|
||||
MessagePortBase)
|
||||
if (tmp->mDispatchRunnable) {
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDispatchRunnable->mPort);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessages);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagesForTheOtherPort);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mUnshippedEntangledPort);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
|
||||
MessagePortBase)
|
||||
if (tmp->mDispatchRunnable) {
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDispatchRunnable->mPort);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnshippedEntangledPort);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_END_INHERITING(MessagePortBase)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(MessagePort, MessagePortBase)
|
||||
NS_IMPL_RELEASE_INHERITED(MessagePort, MessagePortBase)
|
||||
|
||||
namespace {
|
||||
|
||||
class MessagePortFeature final : public workers::WorkerFeature
|
||||
{
|
||||
MessagePort* mPort;
|
||||
|
||||
public:
|
||||
explicit MessagePortFeature(MessagePort* aPort)
|
||||
: mPort(aPort)
|
||||
{
|
||||
MOZ_ASSERT(aPort);
|
||||
MOZ_COUNT_CTOR(MessagePortFeature);
|
||||
}
|
||||
|
||||
virtual bool Notify(JSContext* aCx, workers::Status aStatus) override
|
||||
{
|
||||
if (mPort && aStatus > Running) {
|
||||
mPort->Close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
~MessagePortFeature()
|
||||
{
|
||||
MOZ_COUNT_DTOR(MessagePortFeature);
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
MessagePort::MessagePort(nsPIDOMWindow* aWindow)
|
||||
: MessagePortBase(aWindow)
|
||||
, mInnerID(0)
|
||||
, mMessageQueueEnabled(false)
|
||||
, mIsKeptAlive(false)
|
||||
{
|
||||
mIdentifier = new MessagePortIdentifier();
|
||||
mIdentifier->neutered() = true;
|
||||
mIdentifier->sequenceId() = 0;
|
||||
}
|
||||
|
||||
MessagePort::~MessagePort()
|
||||
{
|
||||
Close();
|
||||
MOZ_ASSERT(!mWorkerFeature);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<MessagePort>
|
||||
MessagePort::Create(nsPIDOMWindow* aWindow, const nsID& aUUID,
|
||||
const nsID& aDestinationUUID, ErrorResult& aRv)
|
||||
{
|
||||
nsRefPtr<MessagePort> mp = new MessagePort(aWindow);
|
||||
mp->Initialize(aUUID, aDestinationUUID, 1 /* 0 is an invalid sequence ID */,
|
||||
false /* Neutered */, eStateUnshippedEntangled, aRv);
|
||||
return mp.forget();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<MessagePort>
|
||||
MessagePort::Create(nsPIDOMWindow* aWindow,
|
||||
const MessagePortIdentifier& aIdentifier,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsRefPtr<MessagePort> mp = new MessagePort(aWindow);
|
||||
mp->Initialize(aIdentifier.uuid(), aIdentifier.destinationUuid(),
|
||||
aIdentifier.sequenceId(), aIdentifier.neutered(),
|
||||
eStateEntangling, aRv);
|
||||
return mp.forget();
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::UnshippedEntangle(MessagePort* aEntangledPort)
|
||||
{
|
||||
MOZ_ASSERT(aEntangledPort);
|
||||
MOZ_ASSERT(!mUnshippedEntangledPort);
|
||||
|
||||
mUnshippedEntangledPort = aEntangledPort;
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::Initialize(const nsID& aUUID,
|
||||
const nsID& aDestinationUUID,
|
||||
uint32_t aSequenceID, bool mNeutered,
|
||||
State aState, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(mIdentifier);
|
||||
mIdentifier->uuid() = aUUID;
|
||||
mIdentifier->destinationUuid() = aDestinationUUID;
|
||||
mIdentifier->sequenceId() = aSequenceID;
|
||||
|
||||
mState = aState;
|
||||
mNextStep = eNextStepNone;
|
||||
|
||||
if (mNeutered) {
|
||||
mState = eStateDisentangled;
|
||||
} else if (mState == eStateEntangling) {
|
||||
ConnectToPBackground();
|
||||
} else {
|
||||
MOZ_ASSERT(mState == eStateUnshippedEntangled);
|
||||
}
|
||||
|
||||
// The port has to keep itself alive until it's entangled.
|
||||
UpdateMustKeepAlive();
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
MOZ_ASSERT(GetOwner());
|
||||
MOZ_ASSERT(GetOwner()->IsInnerWindow());
|
||||
mInnerID = GetOwner()->WindowID();
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->AddObserver(this, "inner-window-destroyed", false);
|
||||
}
|
||||
} else {
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
MOZ_ASSERT(!mWorkerFeature);
|
||||
|
||||
nsAutoPtr<WorkerFeature> feature(new MessagePortFeature(this));
|
||||
JSContext* cx = workerPrivate->GetJSContext();
|
||||
if (NS_WARN_IF(!workerPrivate->AddFeature(cx, feature))) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
mWorkerFeature = Move(feature);
|
||||
}
|
||||
}
|
||||
|
||||
JSObject*
|
||||
MessagePort::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return MessagePortBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// We *must* clone the data here, or the JS::Value could be modified
|
||||
// by script
|
||||
|
||||
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
|
||||
if (aTransferable.WasPassed()) {
|
||||
const Sequence<JS::Value>& realTransferable = aTransferable.Value();
|
||||
|
||||
// Here we want to check if the transerable object list contains
|
||||
// this port. No other checks are done.
|
||||
for (const JS::Value& value : realTransferable) {
|
||||
if (!value.isObject()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MessagePortBase* port = nullptr;
|
||||
nsresult rv = UNWRAP_OBJECT(MessagePort, &value.toObject(), port);
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (port == this) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The input sequence only comes from the generated bindings code, which
|
||||
// ensures it is rooted.
|
||||
JS::HandleValueArray elements =
|
||||
JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
|
||||
realTransferable.Elements());
|
||||
|
||||
JSObject* array =
|
||||
JS_NewArrayObject(aCx, elements);
|
||||
if (!array) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
transferable.setObject(*array);
|
||||
}
|
||||
|
||||
nsRefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
|
||||
|
||||
if (!WriteStructuredCloneWithTransfer(aCx, aMessage, transferable,
|
||||
data->mData, data->mClosure)) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// This message has to be ignored.
|
||||
if (mState > eStateEntangled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we are unshipped we are connected to the other port on the same thread.
|
||||
if (mState == eStateUnshippedEntangled) {
|
||||
MOZ_ASSERT(mUnshippedEntangledPort);
|
||||
mUnshippedEntangledPort->mMessages.AppendElement(data);
|
||||
mUnshippedEntangledPort->Dispatch();
|
||||
return;
|
||||
}
|
||||
|
||||
// Not entangled yet, but already closed.
|
||||
if (mNextStep != eNextStepNone) {
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveDocFromBFCache();
|
||||
|
||||
// Not entangled yet.
|
||||
if (mState == eStateEntangling) {
|
||||
mMessagesForTheOtherPort.AppendElement(data);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mActor);
|
||||
MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
|
||||
|
||||
nsAutoTArray<nsRefPtr<SharedMessagePortMessage>, 1> array;
|
||||
array.AppendElement(data);
|
||||
|
||||
nsAutoTArray<MessagePortMessage, 1> messages;
|
||||
SharedMessagePortMessage::FromSharedToMessagesChild(mActor, array, messages);
|
||||
mActor->SendPostMessages(messages);
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::Start()
|
||||
{
|
||||
if (mMessageQueueEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
mMessageQueueEnabled = true;
|
||||
Dispatch();
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::Dispatch()
|
||||
{
|
||||
if (!mMessageQueueEnabled || mMessages.IsEmpty() || mDispatchRunnable ||
|
||||
mState > eStateEntangled || mNextStep != eNextStepNone) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<SharedMessagePortMessage> data = mMessages.ElementAt(0);
|
||||
mMessages.RemoveElementAt(0);
|
||||
|
||||
nsRefPtr<PostMessageRunnable> runnable = new PostMessageRunnable(this, data);
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(runnable)));
|
||||
|
||||
mDispatchRunnable = new DispatchEventRunnable(this);
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(mDispatchRunnable)));
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::Close()
|
||||
{
|
||||
// Not entangled yet, but already closed.
|
||||
if (mNextStep != eNextStepNone) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mState == eStateUnshippedEntangled) {
|
||||
MOZ_ASSERT(mUnshippedEntangledPort);
|
||||
|
||||
// This avoids loops.
|
||||
nsRefPtr<MessagePort> port = Move(mUnshippedEntangledPort);
|
||||
MOZ_ASSERT(mUnshippedEntangledPort == nullptr);
|
||||
|
||||
mState = eStateDisentangled;
|
||||
port->Close();
|
||||
|
||||
UpdateMustKeepAlive();
|
||||
return;
|
||||
}
|
||||
|
||||
// Not entangled yet, we have to wait.
|
||||
if (mState < eStateEntangling) {
|
||||
mNextStep = eNextStepClose;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mState > eStateEntangled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't care about stopping the sending of messages because from now all
|
||||
// the incoming messages will be ignored.
|
||||
mState = eStateDisentangled;
|
||||
|
||||
MOZ_ASSERT(mActor);
|
||||
|
||||
mActor->SendClose();
|
||||
mActor->SetPort(nullptr);
|
||||
mActor = nullptr;
|
||||
|
||||
UpdateMustKeepAlive();
|
||||
}
|
||||
|
||||
EventHandlerNonNull*
|
||||
MessagePort::GetOnmessage()
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return GetEventHandler(nsGkAtoms::onmessage, EmptyString());
|
||||
}
|
||||
return GetEventHandler(nullptr, NS_LITERAL_STRING("message"));
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::SetOnmessage(EventHandlerNonNull* aCallback)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback);
|
||||
} else {
|
||||
SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback);
|
||||
}
|
||||
|
||||
// When using onmessage, the call to start() is implied.
|
||||
Start();
|
||||
}
|
||||
|
||||
// This method is called when the PMessagePortChild actor is entangled to
|
||||
// another actor. It receives a list of messages to be dispatch. It can be that
|
||||
// we were waiting for this entangling step in order to disentangle the port or
|
||||
// to close it.
|
||||
void
|
||||
MessagePort::Entangled(nsTArray<MessagePortMessage>& aMessages)
|
||||
{
|
||||
MOZ_ASSERT(mState == eStateEntangling);
|
||||
|
||||
mState = eStateEntangled;
|
||||
|
||||
// If we have pending messages, these have to be sent.
|
||||
if (!mMessagesForTheOtherPort.IsEmpty()) {
|
||||
nsTArray<MessagePortMessage> messages;
|
||||
SharedMessagePortMessage::FromSharedToMessagesChild(mActor,
|
||||
mMessagesForTheOtherPort,
|
||||
messages);
|
||||
mMessagesForTheOtherPort.Clear();
|
||||
mActor->SendPostMessages(messages);
|
||||
}
|
||||
|
||||
// We must convert the messages into SharedMessagePortMessages to avoid leaks.
|
||||
FallibleTArray<nsRefPtr<SharedMessagePortMessage>> data;
|
||||
if (NS_WARN_IF(!SharedMessagePortMessage::FromMessagesToSharedChild(aMessages,
|
||||
data))) {
|
||||
// OOM, we cannot continue.
|
||||
return;
|
||||
}
|
||||
|
||||
if (mNextStep == eNextStepClose) {
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
mMessages.AppendElements(data);
|
||||
|
||||
// We were waiting for the entangling callback in order to disentangle this
|
||||
// port immediately after.
|
||||
if (mNextStep == eNextStepDisentangle) {
|
||||
StartDisentangling();
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mNextStep == eNextStepNone);
|
||||
Dispatch();
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::StartDisentangling()
|
||||
{
|
||||
MOZ_ASSERT(mActor);
|
||||
MOZ_ASSERT(mState == eStateEntangled);
|
||||
|
||||
mState = eStateDisentangling;
|
||||
mNextStep = eNextStepNone;
|
||||
|
||||
// Sending this message we communicate to the parent actor that we don't want
|
||||
// to receive any new messages. It is possible that a message has been
|
||||
// already sent but not received yet. So we have to collect all of them and
|
||||
// we send them in the SendDispatch() request.
|
||||
mActor->SendStopSendingData();
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::MessagesReceived(nsTArray<MessagePortMessage>& aMessages)
|
||||
{
|
||||
MOZ_ASSERT(mState == eStateEntangled || mState == eStateDisentangling);
|
||||
MOZ_ASSERT(mNextStep == eNextStepNone);
|
||||
MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
|
||||
|
||||
RemoveDocFromBFCache();
|
||||
|
||||
FallibleTArray<nsRefPtr<SharedMessagePortMessage>> data;
|
||||
if (!NS_WARN_IF(SharedMessagePortMessage::FromMessagesToSharedChild(aMessages,
|
||||
data))) {
|
||||
// OOM, We cannot continue.
|
||||
return;
|
||||
}
|
||||
|
||||
mMessages.AppendElements(data);
|
||||
|
||||
if (mState == eStateEntangled) {
|
||||
Dispatch();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::StopSendingDataConfirmed()
|
||||
{
|
||||
MOZ_ASSERT(mState == eStateDisentangling);
|
||||
MOZ_ASSERT(mActor);
|
||||
|
||||
Disentangle();
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::Disentangle()
|
||||
{
|
||||
MOZ_ASSERT(mState == eStateDisentangling);
|
||||
MOZ_ASSERT(mActor);
|
||||
|
||||
mState = eStateDisentangled;
|
||||
|
||||
nsTArray<MessagePortMessage> messages;
|
||||
SharedMessagePortMessage::FromSharedToMessagesChild(mActor, mMessages,
|
||||
messages);
|
||||
mMessages.Clear();
|
||||
mActor->SendDisentangle(messages);
|
||||
|
||||
mActor->SetPort(nullptr);
|
||||
mActor = nullptr;
|
||||
|
||||
UpdateMustKeepAlive();
|
||||
}
|
||||
|
||||
bool
|
||||
MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier)
|
||||
{
|
||||
MOZ_ASSERT(mIdentifier);
|
||||
|
||||
// We can clone a port that has already been transfered. In this case, on the
|
||||
// otherside will have a neutered port. Here we set neutered to true so that
|
||||
// we are safe in case a early return.
|
||||
aIdentifier.neutered() = true;
|
||||
|
||||
if (mState > eStateEntangled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We already have a 'next step'. We have to consider this port as already
|
||||
// cloned/closed/disentangled.
|
||||
if (mNextStep != eNextStepNone) {
|
||||
return true;
|
||||
}
|
||||
|
||||
aIdentifier.uuid() = mIdentifier->uuid();
|
||||
aIdentifier.destinationUuid() = mIdentifier->destinationUuid();
|
||||
aIdentifier.sequenceId() = mIdentifier->sequenceId() + 1;
|
||||
aIdentifier.neutered() = false;
|
||||
|
||||
// We have to entangle first.
|
||||
if (mState == eStateUnshippedEntangled) {
|
||||
MOZ_ASSERT(mUnshippedEntangledPort);
|
||||
MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
|
||||
|
||||
// Disconnect the entangled port and connect it to PBackground.
|
||||
mUnshippedEntangledPort->ConnectToPBackground();
|
||||
mUnshippedEntangledPort = nullptr;
|
||||
|
||||
// In this case, we don't need to be connected to the PBackground service.
|
||||
if (mMessages.IsEmpty()) {
|
||||
aIdentifier.sequenceId() = mIdentifier->sequenceId();
|
||||
|
||||
mState = eStateDisentangled;
|
||||
UpdateMustKeepAlive();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Register this component to PBackground.
|
||||
ConnectToPBackground();
|
||||
|
||||
mNextStep = eNextStepDisentangle;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not entangled yet, we have to wait.
|
||||
if (mState < eStateEntangled) {
|
||||
mNextStep = eNextStepDisentangle;
|
||||
return true;
|
||||
}
|
||||
|
||||
StartDisentangling();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::Closed()
|
||||
{
|
||||
if (mState == eStateDisentangled) {
|
||||
return;
|
||||
}
|
||||
|
||||
mState = eStateDisentangled;
|
||||
|
||||
if (mActor) {
|
||||
mActor->SetPort(nullptr);
|
||||
mActor = nullptr;
|
||||
}
|
||||
|
||||
UpdateMustKeepAlive();
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::ConnectToPBackground()
|
||||
{
|
||||
mState = eStateEntangling;
|
||||
|
||||
PBackgroundChild* actor =
|
||||
mozilla::ipc::BackgroundChild::GetForCurrentThread();
|
||||
if (actor) {
|
||||
ActorCreated(actor);
|
||||
} else {
|
||||
if (NS_WARN_IF(
|
||||
!mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(this))) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::ActorFailed()
|
||||
{
|
||||
MOZ_CRASH("Failed to create a PBackgroundChild actor!");
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
|
||||
{
|
||||
MOZ_ASSERT(aActor);
|
||||
MOZ_ASSERT(!mActor);
|
||||
MOZ_ASSERT(mIdentifier);
|
||||
MOZ_ASSERT(mState == eStateEntangling);
|
||||
|
||||
PMessagePortChild* actor =
|
||||
aActor->SendPMessagePortConstructor(mIdentifier->uuid(),
|
||||
mIdentifier->destinationUuid(),
|
||||
mIdentifier->sequenceId());
|
||||
|
||||
mActor = static_cast<MessagePortChild*>(actor);
|
||||
MOZ_ASSERT(mActor);
|
||||
|
||||
mActor->SetPort(this);
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::UpdateMustKeepAlive()
|
||||
{
|
||||
if (mState == eStateDisentangled && mIsKeptAlive) {
|
||||
mIsKeptAlive = false;
|
||||
|
||||
if (mWorkerFeature) {
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
workerPrivate->RemoveFeature(workerPrivate->GetJSContext(),
|
||||
mWorkerFeature);
|
||||
mWorkerFeature = nullptr;
|
||||
}
|
||||
|
||||
Release();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mState < eStateDisentangled && !mIsKeptAlive) {
|
||||
mIsKeptAlive = true;
|
||||
AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MessagePort::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (strcmp(aTopic, "inner-window-destroyed")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If the window id destroyed we have to release the reference that we are
|
||||
// keeping.
|
||||
if (!mIsKeptAlive) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
|
||||
NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
|
||||
|
||||
uint64_t innerID;
|
||||
nsresult rv = wrapper->GetData(&innerID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (innerID == mInnerID) {
|
||||
nsCOMPtr<nsIObserverService> obs =
|
||||
do_GetService("@mozilla.org/observer-service;1");
|
||||
if (obs) {
|
||||
obs->RemoveObserver(this, "inner-window-destroyed");
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::RemoveDocFromBFCache()
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsPIDOMWindow* window = GetOwner();
|
||||
MOZ_ASSERT(window);
|
||||
|
||||
nsIDocument* doc = window->GetExtantDoc();
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIBFCacheEntry> bfCacheEntry = doc->GetBFCacheEntry();
|
||||
if (!bfCacheEntry) {
|
||||
return;
|
||||
}
|
||||
|
||||
bfCacheEntry->RemoveFromBFCacheSync();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
210
dom/messagechannel/MessagePort.h
Normal file
210
dom/messagechannel/MessagePort.h
Normal file
@ -0,0 +1,210 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_MessagePort_h
|
||||
#define mozilla_dom_MessagePort_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "nsIIPCBackgroundChildCreateCallback.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#undef PostMessage
|
||||
#endif
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class DispatchEventRunnable;
|
||||
class MessagePortChild;
|
||||
class MessagePortIdentifier;
|
||||
class MessagePortMessage;
|
||||
class SharedMessagePortMessage;
|
||||
|
||||
namespace workers {
|
||||
class WorkerFeature;
|
||||
}
|
||||
|
||||
class MessagePortBase : public DOMEventTargetHelper
|
||||
{
|
||||
protected:
|
||||
explicit MessagePortBase(nsPIDOMWindow* aWindow);
|
||||
MessagePortBase();
|
||||
|
||||
public:
|
||||
|
||||
virtual void
|
||||
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv) = 0;
|
||||
|
||||
virtual void
|
||||
Start() = 0;
|
||||
|
||||
virtual void
|
||||
Close() = 0;
|
||||
|
||||
// The 'message' event handler has to call |Start()| method, so we
|
||||
// cannot use IMPL_EVENT_HANDLER macro here.
|
||||
virtual EventHandlerNonNull*
|
||||
GetOnmessage() = 0;
|
||||
|
||||
virtual void
|
||||
SetOnmessage(EventHandlerNonNull* aCallback) = 0;
|
||||
|
||||
// Duplicate this message port. This method is used by the Structured Clone
|
||||
// Algorithm and populates a MessagePortIdentifier object with the information
|
||||
// useful to create new MessagePort.
|
||||
virtual bool
|
||||
CloneAndDisentangle(MessagePortIdentifier& aIdentifier) = 0;
|
||||
};
|
||||
|
||||
class MessagePort final : public MessagePortBase
|
||||
, public nsIIPCBackgroundChildCreateCallback
|
||||
, public nsIObserver
|
||||
{
|
||||
friend class DispatchEventRunnable;
|
||||
|
||||
public:
|
||||
NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort,
|
||||
DOMEventTargetHelper)
|
||||
|
||||
static already_AddRefed<MessagePort>
|
||||
Create(nsPIDOMWindow* aWindow, const nsID& aUUID,
|
||||
const nsID& aDestinationUUID, ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<MessagePort>
|
||||
Create(nsPIDOMWindow* aWindow, const MessagePortIdentifier& aIdentifier,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
virtual void
|
||||
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
virtual void Start() override;
|
||||
|
||||
virtual void Close() override;
|
||||
|
||||
virtual EventHandlerNonNull* GetOnmessage() override;
|
||||
|
||||
virtual void SetOnmessage(EventHandlerNonNull* aCallback) override;
|
||||
|
||||
// Non WebIDL methods
|
||||
|
||||
void UnshippedEntangle(MessagePort* aEntangledPort);
|
||||
|
||||
virtual bool CloneAndDisentangle(MessagePortIdentifier& aIdentifier) override;
|
||||
|
||||
// These methods are useful for MessagePortChild
|
||||
|
||||
void Entangled(nsTArray<MessagePortMessage>& aMessages);
|
||||
void MessagesReceived(nsTArray<MessagePortMessage>& aMessages);
|
||||
void StopSendingDataConfirmed();
|
||||
void Closed();
|
||||
|
||||
private:
|
||||
explicit MessagePort(nsPIDOMWindow* aWindow);
|
||||
~MessagePort();
|
||||
|
||||
enum State {
|
||||
// When a port is created by a MessageChannel it is entangled with the
|
||||
// other. They both run on the same thread, same event loop and the
|
||||
// messages are added to the queues without using PBackground actors.
|
||||
// When one of the port is shipped, the state is changed to
|
||||
// StateEntangling.
|
||||
eStateUnshippedEntangled,
|
||||
|
||||
// If the port is closed or cloned when we are in this state, we set the
|
||||
// mNextStep. This 'next' operation will be done when entangled() message
|
||||
// is received.
|
||||
eStateEntangling,
|
||||
|
||||
// When entangled() is received we send all the messages in the
|
||||
// mMessagesForTheOtherPort to the actor and we change the state to
|
||||
// StateEntangled. At this point the port is entangled with the other. We
|
||||
// send and receive messages.
|
||||
// If the port queue is not enabled, the received messages are stored in
|
||||
// the mMessages.
|
||||
eStateEntangled,
|
||||
|
||||
// When the port is cloned or disentangled we want to stop receiving
|
||||
// messages. We call 'SendStopSendingData' to the actor and we wait for an
|
||||
// answer. All the messages received between now and the
|
||||
// 'StopSendingDataComfirmed are queued in the mMessages but not
|
||||
// dispatched.
|
||||
eStateDisentangling,
|
||||
|
||||
// When 'StopSendingDataConfirmed' is received, we can disentangle the port
|
||||
// calling SendDisentangle in the actor because we are 100% sure that we
|
||||
// don't receive any other message, so nothing will be lost.
|
||||
// Disentangling the port we send all the messages from the mMessages
|
||||
// though the actor.
|
||||
eStateDisentangled
|
||||
};
|
||||
|
||||
void Initialize(const nsID& aUUID, const nsID& aDestinationUUID,
|
||||
uint32_t aSequenceID, bool mNeutered, State aState,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void ConnectToPBackground();
|
||||
|
||||
// Dispatch events from the Message Queue using a nsRunnable.
|
||||
void Dispatch();
|
||||
|
||||
void StartDisentangling();
|
||||
void Disentangle();
|
||||
|
||||
void RemoveDocFromBFCache();
|
||||
|
||||
// This method is meant to keep alive the MessagePort when this object is
|
||||
// creating the actor and until the actor is entangled.
|
||||
// We release the object when the port is closed or disentangled.
|
||||
void UpdateMustKeepAlive();
|
||||
|
||||
nsAutoPtr<workers::WorkerFeature> mWorkerFeature;
|
||||
|
||||
nsRefPtr<DispatchEventRunnable> mDispatchRunnable;
|
||||
|
||||
nsRefPtr<MessagePortChild> mActor;
|
||||
|
||||
nsRefPtr<MessagePort> mUnshippedEntangledPort;
|
||||
|
||||
nsTArray<nsRefPtr<SharedMessagePortMessage>> mMessages;
|
||||
nsTArray<nsRefPtr<SharedMessagePortMessage>> mMessagesForTheOtherPort;
|
||||
|
||||
nsAutoPtr<MessagePortIdentifier> mIdentifier;
|
||||
|
||||
uint64_t mInnerID;
|
||||
|
||||
State mState;
|
||||
|
||||
// This 'nextStep' is used when we are waiting to be entangled but the
|
||||
// content has called Clone() or Close().
|
||||
enum {
|
||||
eNextStepNone,
|
||||
eNextStepDisentangle,
|
||||
eNextStepClose
|
||||
} mNextStep;
|
||||
|
||||
bool mMessageQueueEnabled;
|
||||
|
||||
bool mIsKeptAlive;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_MessagePort_h
|
49
dom/messagechannel/MessagePortChild.cpp
Normal file
49
dom/messagechannel/MessagePortChild.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MessagePortChild.h"
|
||||
#include "MessagePort.h"
|
||||
#include "mozilla/dom/MessageEvent.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
bool
|
||||
MessagePortChild::RecvStopSendingDataConfirmed()
|
||||
{
|
||||
MOZ_ASSERT(mPort);
|
||||
mPort->StopSendingDataConfirmed();
|
||||
MOZ_ASSERT(!mPort);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MessagePortChild::RecvEntangled(nsTArray<MessagePortMessage>&& aMessages)
|
||||
{
|
||||
MOZ_ASSERT(mPort);
|
||||
mPort->Entangled(aMessages);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MessagePortChild::RecvReceiveData(nsTArray<MessagePortMessage>&& aMessages)
|
||||
{
|
||||
MOZ_ASSERT(mPort);
|
||||
mPort->MessagesReceived(aMessages);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MessagePortChild::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
if (mPort) {
|
||||
mPort->Closed();
|
||||
MOZ_ASSERT(!mPort);
|
||||
}
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
52
dom/messagechannel/MessagePortChild.h
Normal file
52
dom/messagechannel/MessagePortChild.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_MessagePortChild_h
|
||||
#define mozilla_dom_MessagePortChild_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/dom/PMessagePortChild.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class MessagePort;
|
||||
|
||||
class MessagePortChild final : public PMessagePortChild
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(MessagePortChild)
|
||||
|
||||
MessagePortChild() {}
|
||||
|
||||
void SetPort(MessagePort* aPort)
|
||||
{
|
||||
mPort = aPort;
|
||||
}
|
||||
|
||||
private:
|
||||
~MessagePortChild()
|
||||
{
|
||||
MOZ_ASSERT(!mPort);
|
||||
}
|
||||
|
||||
virtual bool
|
||||
RecvEntangled(nsTArray<MessagePortMessage>&& aMessages) override;
|
||||
|
||||
virtual bool
|
||||
RecvReceiveData(nsTArray<MessagePortMessage>&& aMessages) override;
|
||||
|
||||
virtual bool RecvStopSendingDataConfirmed() override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
// This is a raw pointer because this child is owned by this MessagePort.
|
||||
MessagePort* mPort;
|
||||
};
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
||||
#endif // mozilla_dom_MessagePortChild_h
|
163
dom/messagechannel/MessagePortParent.cpp
Normal file
163
dom/messagechannel/MessagePortParent.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MessagePortParent.h"
|
||||
#include "MessagePortService.h"
|
||||
#include "SharedMessagePortMessage.h"
|
||||
#include "mozilla/unused.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
MessagePortParent::MessagePortParent(const nsID& aUUID)
|
||||
: mService(MessagePortService::GetOrCreate())
|
||||
, mUUID(aUUID)
|
||||
, mEntangled(false)
|
||||
, mCanSendData(true)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
}
|
||||
|
||||
MessagePortParent::~MessagePortParent()
|
||||
{
|
||||
MOZ_ASSERT(!mService);
|
||||
MOZ_ASSERT(!mEntangled);
|
||||
}
|
||||
|
||||
bool
|
||||
MessagePortParent::Entangle(const nsID& aDestinationUUID,
|
||||
const uint32_t& aSequenceID)
|
||||
{
|
||||
if (!mService) {
|
||||
NS_WARNING("Entangle is called after a shutdown!");
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mEntangled);
|
||||
|
||||
return mService->RequestEntangling(this, aDestinationUUID, aSequenceID);
|
||||
}
|
||||
|
||||
bool
|
||||
MessagePortParent::RecvPostMessages(nsTArray<MessagePortMessage>&& aMessages)
|
||||
{
|
||||
// This converts the object in a data struct where we have BlobImpls.
|
||||
FallibleTArray<nsRefPtr<SharedMessagePortMessage>> messages;
|
||||
if (NS_WARN_IF(
|
||||
!SharedMessagePortMessage::FromMessagesToSharedParent(aMessages,
|
||||
messages))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mEntangled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mService) {
|
||||
NS_WARNING("Entangle is called after a shutdown!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (messages.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return mService->PostMessages(this, messages);
|
||||
}
|
||||
|
||||
bool
|
||||
MessagePortParent::RecvDisentangle(nsTArray<MessagePortMessage>&& aMessages)
|
||||
{
|
||||
// This converts the object in a data struct where we have BlobImpls.
|
||||
FallibleTArray<nsRefPtr<SharedMessagePortMessage>> messages;
|
||||
if (NS_WARN_IF(
|
||||
!SharedMessagePortMessage::FromMessagesToSharedParent(aMessages,
|
||||
messages))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mEntangled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mService) {
|
||||
NS_WARNING("Entangle is called after a shutdown!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mService->DisentanglePort(this, messages)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CloseAndDelete();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MessagePortParent::RecvStopSendingData()
|
||||
{
|
||||
if (!mEntangled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
mCanSendData = false;
|
||||
unused << SendStopSendingDataConfirmed();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MessagePortParent::RecvClose()
|
||||
{
|
||||
if (mService) {
|
||||
MOZ_ASSERT(mEntangled);
|
||||
|
||||
if (!mService->ClosePort(this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mEntangled);
|
||||
|
||||
unused << Send__delete__(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MessagePortParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
if (mService && mEntangled) {
|
||||
// When the last parent is deleted, this service is freed but this cannot
|
||||
// be done when the hashtables are written by CloseAll.
|
||||
nsRefPtr<MessagePortService> kungFuDeathGrip = mService;
|
||||
mService->ParentDestroy(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MessagePortParent::Entangled(const nsTArray<MessagePortMessage>& aMessages)
|
||||
{
|
||||
MOZ_ASSERT(!mEntangled);
|
||||
mEntangled = true;
|
||||
return SendEntangled(aMessages);
|
||||
}
|
||||
|
||||
void
|
||||
MessagePortParent::CloseAndDelete()
|
||||
{
|
||||
Close();
|
||||
unused << Send__delete__(this);
|
||||
}
|
||||
|
||||
void
|
||||
MessagePortParent::Close()
|
||||
{
|
||||
mService = nullptr;
|
||||
mEntangled = false;
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
61
dom/messagechannel/MessagePortParent.h
Normal file
61
dom/messagechannel/MessagePortParent.h
Normal file
@ -0,0 +1,61 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_MessagePortParent_h
|
||||
#define mozilla_dom_MessagePortParent_h
|
||||
|
||||
#include "mozilla/dom/PMessagePortParent.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class MessagePortService;
|
||||
|
||||
class MessagePortParent final : public PMessagePortParent
|
||||
{
|
||||
public:
|
||||
explicit MessagePortParent(const nsID& aUUID);
|
||||
~MessagePortParent();
|
||||
|
||||
bool Entangle(const nsID& aDestinationUUID,
|
||||
const uint32_t& aSequenceID);
|
||||
|
||||
bool Entangled(const nsTArray<MessagePortMessage>& aMessages);
|
||||
|
||||
void Close();
|
||||
void CloseAndDelete();
|
||||
|
||||
bool CanSendData() const
|
||||
{
|
||||
return mCanSendData;
|
||||
}
|
||||
|
||||
const nsID& ID() const
|
||||
{
|
||||
return mUUID;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual bool RecvPostMessages(nsTArray<MessagePortMessage>&& aMessages)
|
||||
override;
|
||||
|
||||
virtual bool RecvDisentangle(nsTArray<MessagePortMessage>&& aMessages)
|
||||
override;
|
||||
|
||||
virtual bool RecvStopSendingData() override;
|
||||
|
||||
virtual bool RecvClose() override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
nsRefPtr<MessagePortService> mService;
|
||||
const nsID mUUID;
|
||||
bool mEntangled;
|
||||
bool mCanSendData;
|
||||
};
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
||||
#endif // mozilla_dom_MessagePortParent_h
|
328
dom/messagechannel/MessagePortService.cpp
Normal file
328
dom/messagechannel/MessagePortService.cpp
Normal file
@ -0,0 +1,328 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MessagePortService.h"
|
||||
#include "MessagePortParent.h"
|
||||
#include "SharedMessagePortMessage.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace {
|
||||
StaticRefPtr<MessagePortService> gInstance;
|
||||
} // anonymous namespace
|
||||
|
||||
class MessagePortService::MessagePortServiceData final
|
||||
{
|
||||
public:
|
||||
explicit MessagePortServiceData(const nsID& aDestinationUUID)
|
||||
: mDestinationUUID(aDestinationUUID)
|
||||
, mSequenceID(1)
|
||||
, mParent(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(MessagePortServiceData);
|
||||
}
|
||||
|
||||
MessagePortServiceData(const MessagePortServiceData& aOther) = delete;
|
||||
MessagePortServiceData& operator=(const MessagePortServiceData&) = delete;
|
||||
|
||||
~MessagePortServiceData()
|
||||
{
|
||||
MOZ_COUNT_DTOR(MessagePortServiceData);
|
||||
}
|
||||
|
||||
nsID mDestinationUUID;
|
||||
|
||||
uint32_t mSequenceID;
|
||||
MessagePortParent* mParent;
|
||||
|
||||
struct NextParent
|
||||
{
|
||||
uint32_t mSequenceID;
|
||||
// MessagePortParent keeps the service alive, and we don't want a cycle.
|
||||
MessagePortParent* mParent;
|
||||
};
|
||||
|
||||
FallibleTArray<NextParent> mNextParents;
|
||||
FallibleTArray<nsRefPtr<SharedMessagePortMessage>> mMessages;
|
||||
};
|
||||
|
||||
/* static */ MessagePortService*
|
||||
MessagePortService::GetOrCreate()
|
||||
{
|
||||
if (!gInstance) {
|
||||
gInstance = new MessagePortService();
|
||||
}
|
||||
|
||||
return gInstance;
|
||||
}
|
||||
|
||||
bool
|
||||
MessagePortService::RequestEntangling(MessagePortParent* aParent,
|
||||
const nsID& aDestinationUUID,
|
||||
const uint32_t& aSequenceID)
|
||||
{
|
||||
MOZ_ASSERT(aParent);
|
||||
MessagePortServiceData* data;
|
||||
|
||||
// If we don't have a MessagePortServiceData, we must create 2 of them for
|
||||
// both ports.
|
||||
if (!mPorts.Get(aParent->ID(), &data)) {
|
||||
// Create the MessagePortServiceData for the destination.
|
||||
if (mPorts.Get(aDestinationUUID, nullptr)) {
|
||||
MOZ_ASSERT(false, "The creation of the 2 ports should be in sync.");
|
||||
return false;
|
||||
}
|
||||
|
||||
data = new MessagePortServiceData(aParent->ID());
|
||||
mPorts.Put(aDestinationUUID, data);
|
||||
|
||||
data = new MessagePortServiceData(aDestinationUUID);
|
||||
mPorts.Put(aParent->ID(), data);
|
||||
}
|
||||
|
||||
// This is a security check.
|
||||
if (!data->mDestinationUUID.Equals(aDestinationUUID)) {
|
||||
MOZ_ASSERT(false, "DestinationUUIDs do not match!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aSequenceID < data->mSequenceID) {
|
||||
MOZ_ASSERT(false, "Invalid sequence ID!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aSequenceID == data->mSequenceID) {
|
||||
if (data->mParent) {
|
||||
MOZ_ASSERT(false, "Two ports cannot have the same sequenceID.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// We activate this port, sending all the messages.
|
||||
data->mParent = aParent;
|
||||
FallibleTArray<MessagePortMessage> array;
|
||||
if (!SharedMessagePortMessage::FromSharedToMessagesParent(aParent,
|
||||
data->mMessages,
|
||||
array)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data->mMessages.Clear();
|
||||
return aParent->Entangled(array);
|
||||
}
|
||||
|
||||
// This new parent will be the next one when a Disentangle request is
|
||||
// received from the current parent.
|
||||
MessagePortServiceData::NextParent* nextParent =
|
||||
data->mNextParents.AppendElement(mozilla::fallible);
|
||||
if (!nextParent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nextParent->mSequenceID = aSequenceID;
|
||||
nextParent->mParent = aParent;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MessagePortService::DisentanglePort(
|
||||
MessagePortParent* aParent,
|
||||
FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aMessages)
|
||||
{
|
||||
MessagePortServiceData* data;
|
||||
if (!mPorts.Get(aParent->ID(), &data)) {
|
||||
MOZ_ASSERT(false, "Unknown MessagePortParent should not happen.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data->mParent != aParent) {
|
||||
MOZ_ASSERT(false, "DisentanglePort() should be called just from the correct parent.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Let's put the messages in the correct order. |aMessages| contains the
|
||||
// unsent messages so they have to go first.
|
||||
if (!aMessages.AppendElements(data->mMessages, mozilla::fallible)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data->mMessages.Clear();
|
||||
|
||||
++data->mSequenceID;
|
||||
|
||||
// If we don't have a parent, we have to store the pending messages and wait.
|
||||
uint32_t index = 0;
|
||||
MessagePortParent* nextParent = nullptr;
|
||||
for (; index < data->mNextParents.Length(); ++index) {
|
||||
if (data->mNextParents[index].mSequenceID == data->mSequenceID) {
|
||||
nextParent = data->mNextParents[index].mParent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't find the parent.
|
||||
if (!nextParent) {
|
||||
data->mMessages.SwapElements(aMessages);
|
||||
data->mParent = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
data->mParent = nextParent;
|
||||
data->mNextParents.RemoveElementAt(index);
|
||||
|
||||
FallibleTArray<MessagePortMessage> array;
|
||||
if (!SharedMessagePortMessage::FromSharedToMessagesParent(data->mParent,
|
||||
aMessages,
|
||||
array)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unused << data->mParent->Entangled(array);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MessagePortService::ClosePort(MessagePortParent* aParent)
|
||||
{
|
||||
MessagePortServiceData* data;
|
||||
if (!mPorts.Get(aParent->ID(), &data)) {
|
||||
MOZ_ASSERT(false, "Unknown MessagePortParent should not happend.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data->mParent != aParent) {
|
||||
MOZ_ASSERT(false, "ClosePort() should be called just from the correct parent.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!data->mNextParents.IsEmpty()) {
|
||||
MOZ_ASSERT(false, "ClosePort() should be called when there are not next parents.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// We don't want to send a message to this parent.
|
||||
data->mParent = nullptr;
|
||||
|
||||
CloseAll(aParent->ID());
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
PLDHashOperator
|
||||
MessagePortService::CloseAllDebugCheck(const nsID& aID,
|
||||
MessagePortServiceData* aData,
|
||||
void* aPtr)
|
||||
{
|
||||
nsID* id = static_cast<nsID*>(aPtr);
|
||||
MOZ_ASSERT(!id->Equals(aID));
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
MessagePortService::CloseAll(const nsID& aUUID)
|
||||
{
|
||||
MessagePortServiceData* data;
|
||||
if (!mPorts.Get(aUUID, &data)) {
|
||||
MaybeShutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
if (data->mParent) {
|
||||
data->mParent->Close();
|
||||
}
|
||||
|
||||
for (const MessagePortServiceData::NextParent& parent : data->mNextParents) {
|
||||
parent.mParent->CloseAndDelete();
|
||||
}
|
||||
|
||||
nsID destinationUUID = data->mDestinationUUID;
|
||||
mPorts.Remove(aUUID);
|
||||
|
||||
CloseAll(destinationUUID);
|
||||
|
||||
#ifdef DEBUG
|
||||
mPorts.EnumerateRead(CloseAllDebugCheck, const_cast<nsID*>(&aUUID));
|
||||
#endif
|
||||
|
||||
MaybeShutdown();
|
||||
}
|
||||
|
||||
// This service can be dismissed when there are not active ports.
|
||||
void
|
||||
MessagePortService::MaybeShutdown()
|
||||
{
|
||||
if (mPorts.Count() == 0) {
|
||||
gInstance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MessagePortService::PostMessages(
|
||||
MessagePortParent* aParent,
|
||||
FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aMessages)
|
||||
{
|
||||
MessagePortServiceData* data;
|
||||
if (!mPorts.Get(aParent->ID(), &data)) {
|
||||
MOZ_ASSERT(false, "Unknown MessagePortParent should not happend.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data->mParent != aParent) {
|
||||
MOZ_ASSERT(false, "PostMessages() should be called just from the correct parent.");
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(mPorts.Get(data->mDestinationUUID, &data));
|
||||
|
||||
if (!data->mMessages.AppendElements(aMessages, mozilla::fallible)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the parent can send data to the child, let's proceed.
|
||||
if (data->mParent && data->mParent->CanSendData()) {
|
||||
FallibleTArray<MessagePortMessage> messages;
|
||||
if (!SharedMessagePortMessage::FromSharedToMessagesParent(data->mParent,
|
||||
data->mMessages,
|
||||
messages)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data->mMessages.Clear();
|
||||
unused << data->mParent->SendReceiveData(messages);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MessagePortService::ParentDestroy(MessagePortParent* aParent)
|
||||
{
|
||||
// This port has already been destroyed.
|
||||
MessagePortServiceData* data;
|
||||
if (!mPorts.Get(aParent->ID(), &data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data->mParent != aParent) {
|
||||
// We don't want to send a message to this parent.
|
||||
for (uint32_t i = 0; i < data->mNextParents.Length(); ++i) {
|
||||
if (aParent == data->mNextParents[i].mParent) {
|
||||
data->mNextParents.RemoveElementAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseAll(aParent->ID());
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
61
dom/messagechannel/MessagePortService.h
Normal file
61
dom/messagechannel/MessagePortService.h
Normal file
@ -0,0 +1,61 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_MessagePortService_h
|
||||
#define mozilla_dom_MessagePortService_h
|
||||
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class MessagePortParent;
|
||||
class SharedMessagePortMessage;
|
||||
|
||||
class MessagePortService final
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(MessagePortService)
|
||||
|
||||
static MessagePortService* GetOrCreate();
|
||||
|
||||
bool RequestEntangling(MessagePortParent* aParent,
|
||||
const nsID& aDestinationUUID,
|
||||
const uint32_t& aSequenceID);
|
||||
|
||||
bool DisentanglePort(
|
||||
MessagePortParent* aParent,
|
||||
FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aMessages);
|
||||
|
||||
bool ClosePort(MessagePortParent* aParent);
|
||||
|
||||
bool PostMessages(
|
||||
MessagePortParent* aParent,
|
||||
FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aMessages);
|
||||
|
||||
void ParentDestroy(MessagePortParent* aParent);
|
||||
|
||||
private:
|
||||
~MessagePortService() {}
|
||||
|
||||
void CloseAll(const nsID& aUUID);
|
||||
void MaybeShutdown();
|
||||
|
||||
class MessagePortServiceData;
|
||||
|
||||
#ifdef DEBUG
|
||||
static PLDHashOperator
|
||||
CloseAllDebugCheck(const nsID& aID, MessagePortServiceData* aData,
|
||||
void* aPtr);
|
||||
#endif
|
||||
|
||||
nsClassHashtable<nsIDHashKey, MessagePortServiceData> mPorts;
|
||||
};
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
||||
#endif // mozilla_dom_MessagePortService_h
|
277
dom/messagechannel/MessagePortUtils.cpp
Normal file
277
dom/messagechannel/MessagePortUtils.cpp
Normal file
@ -0,0 +1,277 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MessagePortUtils.h"
|
||||
#include "MessagePort.h"
|
||||
#include "mozilla/dom/BlobBinding.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/MessagePortBinding.h"
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace messageport {
|
||||
|
||||
namespace {
|
||||
|
||||
struct MOZ_STACK_CLASS StructuredCloneClosureInternal
|
||||
{
|
||||
StructuredCloneClosureInternal(
|
||||
StructuredCloneClosure& aClosure, nsPIDOMWindow* aWindow)
|
||||
: mClosure(aClosure)
|
||||
, mWindow(aWindow)
|
||||
{ }
|
||||
|
||||
StructuredCloneClosure& mClosure;
|
||||
nsPIDOMWindow* mWindow;
|
||||
nsTArray<nsRefPtr<MessagePort>> mMessagePorts;
|
||||
nsTArray<nsRefPtr<MessagePortBase>> mTransferredPorts;
|
||||
};
|
||||
|
||||
struct MOZ_STACK_CLASS StructuredCloneClosureInternalReadOnly
|
||||
{
|
||||
StructuredCloneClosureInternalReadOnly(
|
||||
const StructuredCloneClosure& aClosure, nsPIDOMWindow* aWindow)
|
||||
: mClosure(aClosure)
|
||||
, mWindow(aWindow)
|
||||
{ }
|
||||
|
||||
const StructuredCloneClosure& mClosure;
|
||||
nsPIDOMWindow* mWindow;
|
||||
nsTArray<nsRefPtr<MessagePort>> mMessagePorts;
|
||||
nsTArray<nsRefPtr<MessagePortBase>> mTransferredPorts;
|
||||
};
|
||||
|
||||
void
|
||||
Error(JSContext* aCx, uint32_t aErrorId)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
NS_DOMStructuredCloneError(aCx, aErrorId);
|
||||
} else {
|
||||
Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
JSObject*
|
||||
Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
|
||||
uint32_t aData, void* aClosure)
|
||||
{
|
||||
MOZ_ASSERT(aClosure);
|
||||
|
||||
auto* closure = static_cast<StructuredCloneClosureInternalReadOnly*>(aClosure);
|
||||
|
||||
if (aTag == SCTAG_DOM_BLOB) {
|
||||
// nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
|
||||
// called because the static analysis thinks dereferencing XPCOM objects
|
||||
// can GC (because in some cases it can!), and a return statement with a
|
||||
// JSObject* type means that JSObject* is on the stack as a raw pointer
|
||||
// while destructors are running.
|
||||
JS::Rooted<JS::Value> val(aCx);
|
||||
{
|
||||
MOZ_ASSERT(aData < closure->mClosure.mBlobImpls.Length());
|
||||
nsRefPtr<BlobImpl> blobImpl = closure->mClosure.mBlobImpls[aData];
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
// Blob should not be mutable.
|
||||
bool isMutable;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
|
||||
MOZ_ASSERT(!isMutable);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Let's create a new blob with the correct parent.
|
||||
nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
|
||||
MOZ_ASSERT(global);
|
||||
|
||||
nsRefPtr<Blob> newBlob = Blob::Create(global, blobImpl);
|
||||
if (!ToJSValue(aCx, newBlob, &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return &val.toObject();
|
||||
}
|
||||
|
||||
return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr);
|
||||
}
|
||||
|
||||
bool
|
||||
Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
|
||||
JS::Handle<JSObject*> aObj, void* aClosure)
|
||||
{
|
||||
MOZ_ASSERT(aClosure);
|
||||
|
||||
auto* closure = static_cast<StructuredCloneClosureInternal*>(aClosure);
|
||||
|
||||
// See if the wrapped native is a File/Blob.
|
||||
{
|
||||
Blob* blob = nullptr;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob)) &&
|
||||
NS_SUCCEEDED(blob->SetMutable(false)) &&
|
||||
JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
|
||||
closure->mClosure.mBlobImpls.Length())) {
|
||||
closure->mClosure.mBlobImpls.AppendElement(blob->Impl());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
|
||||
}
|
||||
|
||||
bool
|
||||
ReadTransfer(JSContext* aCx, JSStructuredCloneReader* aReader,
|
||||
uint32_t aTag, void* aContent, uint64_t aExtraData,
|
||||
void* aClosure, JS::MutableHandle<JSObject*> aReturnObject)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aClosure);
|
||||
|
||||
auto* closure = static_cast<StructuredCloneClosureInternalReadOnly*>(aClosure);
|
||||
|
||||
if (aTag != SCTAG_DOM_MAP_MESSAGEPORT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aContent == 0);
|
||||
MOZ_ASSERT(aExtraData < closure->mClosure.mMessagePortIdentifiers.Length());
|
||||
|
||||
ErrorResult rv;
|
||||
nsRefPtr<MessagePort> port =
|
||||
MessagePort::Create(closure->mWindow,
|
||||
closure->mClosure.mMessagePortIdentifiers[(uint32_t)aExtraData],
|
||||
rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
closure->mMessagePorts.AppendElement(port);
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
if (!GetOrCreateDOMReflector(aCx, port, &value)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
aReturnObject.set(&value.toObject());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WriteTransfer(JSContext* aCx, JS::Handle<JSObject*> aObj, void* aClosure,
|
||||
uint32_t* aTag, JS::TransferableOwnership* aOwnership,
|
||||
void** aContent, uint64_t* aExtraData)
|
||||
{
|
||||
MOZ_ASSERT(aClosure);
|
||||
|
||||
auto* closure = static_cast<StructuredCloneClosureInternal*>(aClosure);
|
||||
|
||||
MessagePortBase* port = nullptr;
|
||||
nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (closure->mTransferredPorts.Contains(port)) {
|
||||
// No duplicates.
|
||||
return false;
|
||||
}
|
||||
|
||||
MessagePortIdentifier identifier;
|
||||
if (!port->CloneAndDisentangle(identifier)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
closure->mClosure.mMessagePortIdentifiers.AppendElement(identifier);
|
||||
closure->mTransferredPorts.AppendElement(port);
|
||||
|
||||
*aTag = SCTAG_DOM_MAP_MESSAGEPORT;
|
||||
*aOwnership = JS::SCTAG_TMO_CUSTOM;
|
||||
*aContent = nullptr;
|
||||
*aExtraData = closure->mClosure.mMessagePortIdentifiers.Length() - 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks gCallbacks = {
|
||||
Read,
|
||||
Write,
|
||||
Error,
|
||||
ReadTransfer,
|
||||
WriteTransfer,
|
||||
nullptr
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
bool
|
||||
ReadStructuredCloneWithTransfer(JSContext* aCx, nsTArray<uint8_t>& aData,
|
||||
const StructuredCloneClosure& aClosure,
|
||||
JS::MutableHandle<JS::Value> aClone,
|
||||
nsPIDOMWindow* aParentWindow,
|
||||
nsTArray<nsRefPtr<MessagePort>>& aMessagePorts)
|
||||
{
|
||||
auto* data = reinterpret_cast<uint64_t*>(aData.Elements());
|
||||
size_t dataLen = aData.Length();
|
||||
MOZ_ASSERT(!(dataLen % sizeof(*data)));
|
||||
|
||||
StructuredCloneClosureInternalReadOnly internalClosure(aClosure,
|
||||
aParentWindow);
|
||||
|
||||
bool rv = JS_ReadStructuredClone(aCx, data, dataLen,
|
||||
JS_STRUCTURED_CLONE_VERSION, aClone,
|
||||
&gCallbacks, &internalClosure);
|
||||
if (rv) {
|
||||
aMessagePorts.SwapElements(internalClosure.mMessagePorts);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
WriteStructuredCloneWithTransfer(JSContext* aCx, JS::Handle<JS::Value> aSource,
|
||||
JS::Handle<JS::Value> aTransferable,
|
||||
nsTArray<uint8_t>& aData,
|
||||
StructuredCloneClosure& aClosure)
|
||||
{
|
||||
StructuredCloneClosureInternal internalClosure(aClosure, nullptr);
|
||||
JSAutoStructuredCloneBuffer buffer(&gCallbacks, &internalClosure);
|
||||
|
||||
if (!buffer.write(aCx, aSource, aTransferable, &gCallbacks,
|
||||
&internalClosure)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FallibleTArray<uint8_t> cloneData;
|
||||
if (NS_WARN_IF(!cloneData.SetLength(buffer.nbytes(), mozilla::fallible))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t* data;
|
||||
size_t size;
|
||||
buffer.steal(&data, &size);
|
||||
|
||||
memcpy(cloneData.Elements(), data, size);
|
||||
js_free(data);
|
||||
|
||||
MOZ_ASSERT(aData.IsEmpty());
|
||||
aData.SwapElements(cloneData);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
FreeStructuredClone(nsTArray<uint8_t>& aData, StructuredCloneClosure& aClosure)
|
||||
{
|
||||
auto* data = reinterpret_cast<uint64_t*>(aData.Elements());
|
||||
size_t dataLen = aData.Length();
|
||||
MOZ_ASSERT(!(dataLen % sizeof(*data)));
|
||||
|
||||
JS_ClearStructuredClone(data, dataLen, &gCallbacks, &aClosure, false);
|
||||
aData.Clear();
|
||||
}
|
||||
|
||||
} // messageport namespace
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
55
dom/messagechannel/MessagePortUtils.h
Normal file
55
dom/messagechannel/MessagePortUtils.h
Normal file
@ -0,0 +1,55 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_MessagePortUtils_h
|
||||
#define mozilla_dom_MessagePortUtils_h
|
||||
|
||||
#include "MessagePort.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/PMessagePort.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace messageport {
|
||||
|
||||
struct
|
||||
StructuredCloneClosure
|
||||
{
|
||||
nsTArray<nsRefPtr<BlobImpl>> mBlobImpls;
|
||||
nsTArray<MessagePortIdentifier> mMessagePortIdentifiers;
|
||||
};
|
||||
|
||||
struct
|
||||
StructuredCloneData
|
||||
{
|
||||
StructuredCloneData() : mData(nullptr), mDataLength(0) {}
|
||||
uint64_t* mData;
|
||||
size_t mDataLength;
|
||||
StructuredCloneClosure mClosure;
|
||||
};
|
||||
|
||||
bool
|
||||
ReadStructuredCloneWithTransfer(JSContext* aCx, nsTArray<uint8_t>& aData,
|
||||
const StructuredCloneClosure& aClosure,
|
||||
JS::MutableHandle<JS::Value> aClone,
|
||||
nsPIDOMWindow* aParentWindow,
|
||||
nsTArray<nsRefPtr<MessagePort>>& aMessagePorts);
|
||||
|
||||
bool
|
||||
WriteStructuredCloneWithTransfer(JSContext* aCx, JS::Handle<JS::Value> aSource,
|
||||
JS::Handle<JS::Value> aTransferable,
|
||||
nsTArray<uint8_t>& aData,
|
||||
StructuredCloneClosure& aClosure);
|
||||
|
||||
void
|
||||
FreeStructuredClone(nsTArray<uint8_t>& aData,
|
||||
StructuredCloneClosure& aClosure);
|
||||
|
||||
} // messageport namespace
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
||||
#endif // mozilla_dom_MessagePortUtils_h
|
62
dom/messagechannel/PMessagePort.ipdl
Normal file
62
dom/messagechannel/PMessagePort.ipdl
Normal file
@ -0,0 +1,62 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
include protocol PBackground;
|
||||
include protocol PBlob;
|
||||
|
||||
using struct nsID from "nsID.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct MessagePortIdentifier
|
||||
{
|
||||
nsID uuid;
|
||||
nsID destinationUuid;
|
||||
uint32_t sequenceId;
|
||||
bool neutered;
|
||||
};
|
||||
|
||||
struct MessagePortMessage
|
||||
{
|
||||
MessagePortIdentifier[] transferredPorts;
|
||||
uint8_t[] data;
|
||||
PBlob[] blobs;
|
||||
};
|
||||
|
||||
// This protocol is used for the MessageChannel/MessagePort API
|
||||
protocol PMessagePort
|
||||
{
|
||||
manager PBackground;
|
||||
|
||||
/* Many of these methods are used just for the shutdown sequence. The
|
||||
correct sequence for the child actor is:
|
||||
1. SendStopSendingData();
|
||||
2. RecvStopSendingDataConfirmed();
|
||||
3. SendClose();
|
||||
4. Recv__delete__(); */
|
||||
|
||||
/* When the port is transferred the sequence is:
|
||||
1. SendStopSendingData();
|
||||
2. RecvStopSendingDataConfirmed();
|
||||
3. SendDisentangle();
|
||||
4. Recv__delete__(); */
|
||||
|
||||
parent:
|
||||
PostMessages(MessagePortMessage[] messages);
|
||||
Disentangle(MessagePortMessage[] messages);
|
||||
StopSendingData();
|
||||
Close();
|
||||
|
||||
child:
|
||||
Entangled(MessagePortMessage[] messages);
|
||||
ReceiveData(MessagePortMessage[] messages);
|
||||
StopSendingDataConfirmed();
|
||||
|
||||
__delete__();
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
180
dom/messagechannel/SharedMessagePortMessage.cpp
Normal file
180
dom/messagechannel/SharedMessagePortMessage.cpp
Normal file
@ -0,0 +1,180 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "SharedMessagePortMessage.h"
|
||||
#include "MessagePort.h"
|
||||
#include "MessagePortChild.h"
|
||||
#include "MessagePortParent.h"
|
||||
#include "mozilla/dom/ipc/BlobChild.h"
|
||||
#include "mozilla/dom/ipc/BlobParent.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/PMessagePort.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/ipc/BackgroundParent.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace ipc;
|
||||
|
||||
namespace dom {
|
||||
|
||||
SharedMessagePortMessage::~SharedMessagePortMessage()
|
||||
{
|
||||
if (!mData.IsEmpty()) {
|
||||
FreeStructuredClone(mData, mClosure);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
SharedMessagePortMessage::FromSharedToMessagesChild(
|
||||
MessagePortChild* aActor,
|
||||
const nsTArray<nsRefPtr<SharedMessagePortMessage>>& aData,
|
||||
nsTArray<MessagePortMessage>& aArray)
|
||||
{
|
||||
MOZ_ASSERT(aActor);
|
||||
MOZ_ASSERT(aArray.IsEmpty());
|
||||
aArray.SetCapacity(aData.Length());
|
||||
|
||||
PBackgroundChild* backgroundManager = aActor->Manager();
|
||||
MOZ_ASSERT(backgroundManager);
|
||||
|
||||
for (auto& data : aData) {
|
||||
MessagePortMessage* message = aArray.AppendElement();
|
||||
message->data().SwapElements(data->mData);
|
||||
|
||||
const nsTArray<nsRefPtr<BlobImpl>>& blobImpls =
|
||||
data->mClosure.mBlobImpls;
|
||||
if (!blobImpls.IsEmpty()) {
|
||||
message->blobsChild().SetCapacity(blobImpls.Length());
|
||||
|
||||
for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
|
||||
PBlobChild* blobChild =
|
||||
BackgroundChild::GetOrCreateActorForBlobImpl(backgroundManager,
|
||||
blobImpls[i]);
|
||||
message->blobsChild().AppendElement(blobChild);
|
||||
}
|
||||
}
|
||||
|
||||
message->transferredPorts().AppendElements(
|
||||
data->mClosure.mMessagePortIdentifiers);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SharedMessagePortMessage::FromMessagesToSharedChild(
|
||||
nsTArray<MessagePortMessage>& aArray,
|
||||
FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aData)
|
||||
{
|
||||
MOZ_ASSERT(aData.IsEmpty());
|
||||
|
||||
if (NS_WARN_IF(!aData.SetCapacity(aArray.Length(), mozilla::fallible))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& message : aArray) {
|
||||
nsRefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
|
||||
|
||||
data->mData.SwapElements(message.data());
|
||||
|
||||
const nsTArray<PBlobChild*>& blobs = message.blobsChild();
|
||||
if (!blobs.IsEmpty()) {
|
||||
data->mClosure.mBlobImpls.SetCapacity(blobs.Length());
|
||||
|
||||
for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
|
||||
nsRefPtr<BlobImpl> impl =
|
||||
static_cast<BlobChild*>(blobs[i])->GetBlobImpl();
|
||||
data->mClosure.mBlobImpls.AppendElement(impl);
|
||||
}
|
||||
}
|
||||
|
||||
data->mClosure.mMessagePortIdentifiers.AppendElements(
|
||||
message.transferredPorts());
|
||||
|
||||
if (!aData.AppendElement(data, mozilla::fallible)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SharedMessagePortMessage::FromSharedToMessagesParent(
|
||||
MessagePortParent* aActor,
|
||||
const nsTArray<nsRefPtr<SharedMessagePortMessage>>& aData,
|
||||
FallibleTArray<MessagePortMessage>& aArray)
|
||||
{
|
||||
MOZ_ASSERT(aArray.IsEmpty());
|
||||
|
||||
if (NS_WARN_IF(!aArray.SetCapacity(aData.Length(), mozilla::fallible))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PBackgroundParent* backgroundManager = aActor->Manager();
|
||||
MOZ_ASSERT(backgroundManager);
|
||||
|
||||
for (auto& data : aData) {
|
||||
MessagePortMessage* message = aArray.AppendElement(mozilla::fallible);
|
||||
message->data().SwapElements(data->mData);
|
||||
|
||||
const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = data->mClosure.mBlobImpls;
|
||||
if (!blobImpls.IsEmpty()) {
|
||||
message->blobsParent().SetCapacity(blobImpls.Length());
|
||||
|
||||
for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
|
||||
PBlobParent* blobParent =
|
||||
BackgroundParent::GetOrCreateActorForBlobImpl(backgroundManager,
|
||||
blobImpls[i]);
|
||||
message->blobsParent().AppendElement(blobParent);
|
||||
}
|
||||
}
|
||||
|
||||
message->transferredPorts().AppendElements(
|
||||
data->mClosure.mMessagePortIdentifiers);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SharedMessagePortMessage::FromMessagesToSharedParent(
|
||||
nsTArray<MessagePortMessage>& aArray,
|
||||
FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aData)
|
||||
{
|
||||
MOZ_ASSERT(aData.IsEmpty());
|
||||
|
||||
if (NS_WARN_IF(!aData.SetCapacity(aArray.Length(), mozilla::fallible))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& message : aArray) {
|
||||
nsRefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
|
||||
|
||||
data->mData.SwapElements(message.data());
|
||||
|
||||
const nsTArray<PBlobParent*>& blobs = message.blobsParent();
|
||||
if (!blobs.IsEmpty()) {
|
||||
data->mClosure.mBlobImpls.SetCapacity(blobs.Length());
|
||||
|
||||
for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
|
||||
nsRefPtr<BlobImpl> impl =
|
||||
static_cast<BlobParent*>(blobs[i])->GetBlobImpl();
|
||||
data->mClosure.mBlobImpls.AppendElement(impl);
|
||||
}
|
||||
}
|
||||
|
||||
data->mClosure.mMessagePortIdentifiers.AppendElements(
|
||||
message.transferredPorts());
|
||||
|
||||
if (!aData.AppendElement(data, mozilla::fallible)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
58
dom/messagechannel/SharedMessagePortMessage.h
Normal file
58
dom/messagechannel/SharedMessagePortMessage.h
Normal file
@ -0,0 +1,58 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_SharedMessagePortMessage_h
|
||||
#define mozilla_dom_SharedMessagePortMessage_h
|
||||
|
||||
#include "MessagePortUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class MessagePortChild;
|
||||
class MessagePortMessage;
|
||||
class MessagePortParent;
|
||||
|
||||
class SharedMessagePortMessage final
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(SharedMessagePortMessage)
|
||||
|
||||
nsTArray<uint8_t> mData;
|
||||
messageport::StructuredCloneClosure mClosure;
|
||||
|
||||
SharedMessagePortMessage()
|
||||
{}
|
||||
|
||||
static void
|
||||
FromSharedToMessagesChild(
|
||||
MessagePortChild* aActor,
|
||||
const nsTArray<nsRefPtr<SharedMessagePortMessage>>& aData,
|
||||
nsTArray<MessagePortMessage>& aArray);
|
||||
|
||||
static bool
|
||||
FromMessagesToSharedChild(
|
||||
nsTArray<MessagePortMessage>& aArray,
|
||||
FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aData);
|
||||
|
||||
static bool
|
||||
FromSharedToMessagesParent(
|
||||
MessagePortParent* aActor,
|
||||
const nsTArray<nsRefPtr<SharedMessagePortMessage>>& aData,
|
||||
FallibleTArray<MessagePortMessage>& aArray);
|
||||
|
||||
static bool
|
||||
FromMessagesToSharedParent(
|
||||
nsTArray<MessagePortMessage>& aArray,
|
||||
FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aData);
|
||||
|
||||
private:
|
||||
~SharedMessagePortMessage();
|
||||
};
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
||||
#endif // mozilla_dom_SharedMessagePortMessage_h
|
41
dom/messagechannel/moz.build
Normal file
41
dom/messagechannel/moz.build
Normal file
@ -0,0 +1,41 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
TEST_DIRS += ['tests']
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'MessageChannel.h',
|
||||
'MessagePort.h',
|
||||
'MessagePortChild.h',
|
||||
'MessagePortList.h',
|
||||
'MessagePortParent.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'MessageChannel.cpp',
|
||||
'MessagePort.cpp',
|
||||
'MessagePortChild.cpp',
|
||||
'MessagePortList.cpp',
|
||||
'MessagePortParent.cpp',
|
||||
'MessagePortService.cpp',
|
||||
'MessagePortUtils.cpp',
|
||||
'SharedMessagePortMessage.cpp',
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'PMessagePort.ipdl',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../base',
|
||||
'../events',
|
||||
'../workers',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
FAIL_ON_WARNINGS = True
|
5
dom/messagechannel/tests/chrome.ini
Normal file
5
dom/messagechannel/tests/chrome.ini
Normal file
@ -0,0 +1,5 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
iframe_messageChannel_chrome.html
|
||||
|
||||
[test_messageChannel.xul]
|
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<body>
|
||||
<script type="application/javascript">
|
||||
|
||||
var a = new SharedWorker('sharedWorker2_messageChannel.js');
|
||||
a.port.onmessage = function(evt) {
|
||||
evt.ports[0].postMessage("Hello from the iframe!");
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<body>
|
||||
<script type="application/javascript">
|
||||
|
||||
function ok(what, msg) {
|
||||
window.parent.postMessage({type: what ? 'OK' : 'KO', msg: msg }, '*');
|
||||
}
|
||||
|
||||
window.addEventListener('message', receiveMessage, false);
|
||||
function receiveMessage(evt) {
|
||||
ok(evt.ports.length == 1, "Port transferred!");
|
||||
|
||||
var a = new MessageChannel();
|
||||
ok(a, "MessageChannel created");
|
||||
evt.ports[0].postMessage('hello world!', [a.port2]);
|
||||
a.port1.onmessage = function(evt) {
|
||||
evt.target.postMessage(evt.data);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
25
dom/messagechannel/tests/mochitest.ini
Normal file
25
dom/messagechannel/tests/mochitest.ini
Normal file
@ -0,0 +1,25 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
iframe_messageChannel_cloning.html
|
||||
iframe_messageChannel_pingpong.html
|
||||
iframe_messageChannel_post.html
|
||||
iframe_messageChannel_transferable.html
|
||||
worker_messageChannel.js
|
||||
worker_messageChannel_any.js
|
||||
sharedWorker_messageChannel.js
|
||||
sharedWorker2_messageChannel.js
|
||||
iframe_messageChannel_sharedWorker2.html
|
||||
|
||||
[test_messageChannel.html]
|
||||
[test_messageChannel_cloning.html]
|
||||
[test_messageChannel_pingpong.html]
|
||||
[test_messageChannel_post.html]
|
||||
[test_messageChannel_pref.html]
|
||||
[test_messageChannel_start.html]
|
||||
[test_messageChannel_transferable.html]
|
||||
[test_messageChannel_unshipped.html]
|
||||
[test_messageChannel_worker.html]
|
||||
[test_messageChannel_selfTransferring.html]
|
||||
[test_messageChannel_sharedWorker.html]
|
||||
[test_messageChannel_sharedWorker2.html]
|
||||
[test_messageChannel_any.html]
|
8
dom/messagechannel/tests/moz.build
Normal file
8
dom/messagechannel/tests/moz.build
Normal file
@ -0,0 +1,8 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
MOCHITEST_MANIFESTS += ['mochitest.ini']
|
||||
MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
|
7
dom/messagechannel/tests/sharedWorker2_messageChannel.js
Normal file
7
dom/messagechannel/tests/sharedWorker2_messageChannel.js
Normal file
@ -0,0 +1,7 @@
|
||||
var mc = new MessageChannel();
|
||||
var i = 0;
|
||||
|
||||
onconnect = function(evt) {
|
||||
dump("CONNECTING: "+ i +"\n");
|
||||
evt.ports[0].postMessage(42, [mc['port' + ++i]]);
|
||||
}
|
8
dom/messagechannel/tests/sharedWorker_messageChannel.js
Normal file
8
dom/messagechannel/tests/sharedWorker_messageChannel.js
Normal file
@ -0,0 +1,8 @@
|
||||
onconnect = function(evt) {
|
||||
var mc = new MessageChannel();
|
||||
|
||||
evt.ports[0].postMessage(42, [mc.port2]);
|
||||
mc.port1.onmessage = function(e) {
|
||||
mc.port1.postMessage(e.data);
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
}
|
||||
|
||||
var ifr = document.createElement('browser');
|
||||
ifr.setAttribute("src", "http://mochi.test:8888/tests/dom/base/test/iframe_messageChannel_chrome.html");
|
||||
ifr.setAttribute("src", "iframe_messageChannel_chrome.html");
|
||||
ifr.setAttribute("flex", "1");
|
||||
ifr.addEventListener('load', function() {
|
||||
ifr.contentWindow.postMessage(channel.port2, '*', [channel.port2]);
|
115
dom/messagechannel/tests/test_messageChannel_any.html
Normal file
115
dom/messagechannel/tests/test_messageChannel_any.html
Normal file
@ -0,0 +1,115 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=677638
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MessagePort/Channel any content</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 tests = [
|
||||
'hello world',
|
||||
123,
|
||||
null,
|
||||
true,
|
||||
new Date(),
|
||||
[ 1, 'test', true, new Date() ],
|
||||
{ a: true, b: null, c: new Date(), d: [ true, false, {} ] },
|
||||
new Blob([123], { type: 'plain/text' })
|
||||
];
|
||||
|
||||
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 mc = new MessageChannel('foobar');
|
||||
ok(mc, "MessageChannel can be created");
|
||||
|
||||
mc.port1.onmessage = function(event) {
|
||||
compare(event.data, currentTest);
|
||||
next();
|
||||
}
|
||||
|
||||
function next() {
|
||||
if (!tests.length) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
currentTest = tests.shift();
|
||||
mc.port1.postMessage(currentTest);
|
||||
}
|
||||
|
||||
var worker = new Worker("worker_messageChannel_any.js");
|
||||
worker.onmessage = function(event) {
|
||||
if (event.data == "READY") {
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
worker.postMessage(mc.port2, [mc.port2]);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,38 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=677638
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MessagePort/Channel no self tranferring</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">
|
||||
|
||||
function runTest() {
|
||||
var a = new MessageChannel();
|
||||
|
||||
var status = false;
|
||||
try {
|
||||
a.port1.postMessage('foobar', [a.port1]);
|
||||
} catch(e) {
|
||||
status =true;
|
||||
}
|
||||
|
||||
ok(status, "Transfering the same port should throw");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -0,0 +1,39 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=677638
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 677638 - sharedWorker</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>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<iframe name="x" id="x"></iframe>
|
||||
<iframe name="y" id="y"></iframe>
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<script type="application/javascript">
|
||||
|
||||
function runTest() {
|
||||
var a = new SharedWorker('sharedWorker_messageChannel.js');
|
||||
a.port.onmessage = function(evt) {
|
||||
is(evt.ports.length, 1, "We received a port.");
|
||||
evt.ports[0].onmessage = function(e) {
|
||||
is(e.data, 42, "Message reiceved back!");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
evt.ports[0].postMessage(42);
|
||||
}
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTest);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,37 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=677638
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 677638 - sharedWorker</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>
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
function runTest() {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('src', "iframe_messageChannel_sharedWorker2.html");
|
||||
document.getElementById('content').appendChild(iframe);
|
||||
|
||||
var a = new SharedWorker('sharedWorker2_messageChannel.js');
|
||||
a.port.onmessage = function(evt) {
|
||||
is(evt.ports.length, 1, "We received a port.");
|
||||
evt.ports[0].onmessage = function(e) {
|
||||
is(e.data, "Hello from the iframe!", "Message reiceved from the iframe!");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTest);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -16,14 +16,15 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=677638
|
||||
</pre>
|
||||
<script type="application/javascript">
|
||||
|
||||
function start() {
|
||||
function basic_test() {
|
||||
var a = new MessageChannel();
|
||||
ok(a, "MessageChannel created");
|
||||
|
||||
window.addEventListener('message', receiveMessage, false);
|
||||
function receiveMessage(evt) {
|
||||
if (evt.data.status == 'READY') {
|
||||
runTest();
|
||||
a.port1.postMessage({ab: ab, cb: ab}, [ab]);
|
||||
ok(ab.byteLength == 0, "PostMessage - The size is: 0 == " + ab.byteLength)
|
||||
} else {
|
||||
ok(false, "Unknown message");
|
||||
}
|
||||
@ -44,7 +45,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=677638
|
||||
a.port1.addEventListener('message', receivePortMessage, false);
|
||||
function receivePortMessage(evt) {
|
||||
is(evt.data.ab.byteLength, size, "The size is: " + size + " == " + ab.byteLength);
|
||||
SimpleTest.finish();
|
||||
window.removeEventListener('message', receiveMessage);
|
||||
runTests();
|
||||
}
|
||||
|
||||
// Start() is not implicity invoked when addEventListener is used.
|
||||
@ -53,15 +55,57 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=677638
|
||||
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)
|
||||
function port_test() {
|
||||
window.addEventListener('message', receiveMessage, false);
|
||||
function receiveMessage(evt) {
|
||||
ok(evt.data.type == 'OK', evt.data.msg);
|
||||
}
|
||||
|
||||
var a = new MessageChannel();
|
||||
ok(a, "MessageChannel created");
|
||||
|
||||
var div = document.getElementById("content");
|
||||
ok(div, "Parent exists");
|
||||
|
||||
var ifr = document.createElement("iframe");
|
||||
ifr.addEventListener("load", iframeLoaded, false);
|
||||
ifr.setAttribute('src', "iframe_messageChannel_transferable.html");
|
||||
div.appendChild(ifr);
|
||||
|
||||
function iframeLoaded() {
|
||||
ifr.contentWindow.postMessage('foobar!', '*', [a.port2]);
|
||||
}
|
||||
|
||||
a.port1.onmessage = function(evt) {
|
||||
ok(evt.ports.length == 1, "Iframe sent a new port!");
|
||||
evt.ports[0].onmessage = function(evt) {
|
||||
is(evt.data, "hello world!", "Message sent and received!");
|
||||
runTests();
|
||||
}
|
||||
|
||||
evt.ports[0].postMessage("hello world!");
|
||||
}
|
||||
}
|
||||
|
||||
var tests = [
|
||||
basic_test,
|
||||
port_test
|
||||
];
|
||||
|
||||
function runTests() {
|
||||
if (!tests.length) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var t = tests.shift();
|
||||
t();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, start);
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTests);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
60
dom/messagechannel/tests/test_messageChannel_worker.html
Normal file
60
dom/messagechannel/tests/test_messageChannel_worker.html
Normal file
@ -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 - basic support</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>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<iframe name="x" id="x"></iframe>
|
||||
<iframe name="y" id="y"></iframe>
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<script type="application/javascript">
|
||||
|
||||
var tests = [ 0, 3 ];
|
||||
|
||||
function runTests() {
|
||||
if (!tests.length) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var a = new Worker('worker_messageChannel.js');
|
||||
a.onmessage = function(evt) {
|
||||
if (evt.data.type == 'finish') {
|
||||
runTests();
|
||||
} else if (evt.data.type == 'info') {
|
||||
info(evt.data.message);
|
||||
} else if (evt.data.type == 'check') {
|
||||
ok(evt.data.check, evt.data.message);
|
||||
} else if (evt.data.type == 'port') {
|
||||
is(evt.ports.length, 1, "A port has been received!");
|
||||
evt.ports[0].onmessage = function(e) {
|
||||
e.target.postMessage(e.data);
|
||||
}
|
||||
} else if (evt.data.type == 'newport') {
|
||||
var ch = new MessageChannel();
|
||||
ok(ch, "MessageChannel created");
|
||||
ch.port1.postMessage(42);
|
||||
a.postMessage('a gift!', [ch.port2]);
|
||||
}
|
||||
}
|
||||
|
||||
a.postMessage(tests.shift());
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTests);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
119
dom/messagechannel/tests/worker_messageChannel.js
Normal file
119
dom/messagechannel/tests/worker_messageChannel.js
Normal file
@ -0,0 +1,119 @@
|
||||
function ok(a, msg) {
|
||||
postMessage({ type: 'check', check: !!a, message: msg });
|
||||
}
|
||||
|
||||
function is(a, b, msg) {
|
||||
ok (a === b, msg);
|
||||
}
|
||||
|
||||
function info(msg) {
|
||||
postMessage({ type: 'info', message: msg });
|
||||
}
|
||||
|
||||
function finish() {
|
||||
postMessage({ type: 'finish' });
|
||||
}
|
||||
|
||||
function basic()
|
||||
{
|
||||
var a = new MessageChannel();
|
||||
ok(a, "MessageChannel created");
|
||||
|
||||
var port1 = a.port1;
|
||||
ok(port1, "MessageChannel.port1 exists");
|
||||
is(port1, a.port1, "MessageChannel.port1 is port1");
|
||||
|
||||
var port2 = a.port2;
|
||||
ok(port2, "MessageChannel.port1 exists");
|
||||
is(port2, a.port2, "MessageChannel.port2 is port2");
|
||||
|
||||
[ 'postMessage', 'start', 'close' ].forEach(function(e) {
|
||||
ok(e in port1, "MessagePort1." + e + " exists");
|
||||
ok(e in port2, "MessagePort2." + e + " exists");
|
||||
});
|
||||
|
||||
runTests();
|
||||
}
|
||||
|
||||
function sendMessages()
|
||||
{
|
||||
var a = new MessageChannel();
|
||||
ok(a, "MessageChannel created");
|
||||
|
||||
a.port1.postMessage("Hello world!");
|
||||
a.port1.onmessage = function(e) {
|
||||
is(e.data, "Hello world!", "The message is back!");
|
||||
runTests();
|
||||
}
|
||||
|
||||
a.port2.onmessage = function(e) {
|
||||
a.port2.postMessage(e.data);
|
||||
}
|
||||
}
|
||||
|
||||
function transferPort()
|
||||
{
|
||||
var a = new MessageChannel();
|
||||
ok(a, "MessageChannel created");
|
||||
|
||||
a.port1.postMessage("Hello world!");
|
||||
a.port1.onmessage = function(e) {
|
||||
is(e.data, "Hello world!", "The message is back!");
|
||||
runTests();
|
||||
}
|
||||
|
||||
postMessage({ type: 'port' }, [a.port2]);
|
||||
}
|
||||
|
||||
function transferPort2()
|
||||
{
|
||||
onmessage = function(evt) {
|
||||
is(evt.ports.length, 1, "A port has been received by the worker");
|
||||
evt.ports[0].onmessage = function(e) {
|
||||
is(e.data, 42, "Data is 42!");
|
||||
runTests();
|
||||
}
|
||||
}
|
||||
|
||||
postMessage({ type: 'newport' });
|
||||
}
|
||||
|
||||
var tests = [
|
||||
basic,
|
||||
sendMessages,
|
||||
transferPort,
|
||||
transferPort2,
|
||||
];
|
||||
|
||||
function runTests() {
|
||||
if (!tests.length) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var t = tests.shift();
|
||||
t();
|
||||
}
|
||||
|
||||
var subworker;
|
||||
onmessage = function(evt) {
|
||||
if (evt.data == 0) {
|
||||
runTests();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!subworker) {
|
||||
info("Create a subworkers. ID: " + evt.data);
|
||||
subworker = new Worker('worker_messageChannel.js');
|
||||
subworker.onmessage = function(e) {
|
||||
info("Proxy a message to the parent.");
|
||||
postMessage(e.data, e.ports);
|
||||
}
|
||||
|
||||
subworker.postMessage(evt.data - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
info("Dispatch a message to the subworker.");
|
||||
subworker.postMessage(evt.data, evt.ports);
|
||||
}
|
7
dom/messagechannel/tests/worker_messageChannel_any.js
Normal file
7
dom/messagechannel/tests/worker_messageChannel_any.js
Normal file
@ -0,0 +1,7 @@
|
||||
onmessage = function(evt) {
|
||||
evt.data.onmessage = function(event) {
|
||||
evt.data.postMessage(event.data);
|
||||
}
|
||||
}
|
||||
|
||||
postMessage("READY");
|
@ -96,6 +96,7 @@ DIRS += [
|
||||
'camera',
|
||||
'audiochannel',
|
||||
'broadcastchannel',
|
||||
'messagechannel',
|
||||
'promise',
|
||||
'smil',
|
||||
'telephony',
|
||||
|
@ -7,7 +7,8 @@
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/#channel-messaging
|
||||
*/
|
||||
|
||||
[Constructor, Func="MessageChannel::Enabled"]
|
||||
[Constructor, Func="MessageChannel::Enabled",
|
||||
Exposed=(Window,Worker)]
|
||||
interface MessageChannel {
|
||||
readonly attribute MessagePort port1;
|
||||
readonly attribute MessagePort port2;
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
using mozilla::dom::EventHandlerNonNull;
|
||||
using mozilla::dom::MessagePortBase;
|
||||
using mozilla::dom::MessagePortIdentifier;
|
||||
using mozilla::dom::Optional;
|
||||
using mozilla::dom::Sequence;
|
||||
using mozilla::dom::AutoNoJSAPI;
|
||||
@ -96,9 +97,9 @@ MessagePort::~MessagePort()
|
||||
}
|
||||
|
||||
void
|
||||
MessagePort::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv)
|
||||
MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
AssertCorrectThread();
|
||||
|
||||
@ -198,11 +199,11 @@ MessagePort::SetOnmessage(EventHandlerNonNull* aCallback)
|
||||
Start();
|
||||
}
|
||||
|
||||
already_AddRefed<MessagePortBase>
|
||||
MessagePort::Clone()
|
||||
bool
|
||||
MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier)
|
||||
{
|
||||
NS_WARNING("Haven't implemented structured clone for these ports yet!");
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -43,9 +43,9 @@ public:
|
||||
PrefEnabled();
|
||||
|
||||
virtual void
|
||||
PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv) override;
|
||||
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
virtual void
|
||||
Start() override;
|
||||
@ -71,8 +71,8 @@ public:
|
||||
virtual void
|
||||
SetOnmessage(EventHandlerNonNull* aCallback) override;
|
||||
|
||||
virtual already_AddRefed<MessagePortBase>
|
||||
Clone() override;
|
||||
virtual bool
|
||||
CloneAndDisentangle(MessagePortIdentifier& aIdentifier) override;
|
||||
|
||||
bool
|
||||
IsClosed() const
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerStructuredClone.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
@ -75,16 +76,18 @@ class ServiceWorkerClientPostMessageRunnable final : public nsRunnable
|
||||
{
|
||||
uint64_t mWindowId;
|
||||
JSAutoStructuredCloneBuffer mBuffer;
|
||||
nsTArray<nsCOMPtr<nsISupports>> mClonedObjects;
|
||||
WorkerStructuredCloneClosure mClosure;
|
||||
|
||||
public:
|
||||
ServiceWorkerClientPostMessageRunnable(uint64_t aWindowId,
|
||||
JSAutoStructuredCloneBuffer&& aData,
|
||||
nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
|
||||
WorkerStructuredCloneClosure& aClosure)
|
||||
: mWindowId(aWindowId),
|
||||
mBuffer(Move(aData))
|
||||
{
|
||||
mClonedObjects.SwapElements(aClonedObjects);
|
||||
mClosure.mClonedObjects.SwapElements(aClosure.mClonedObjects);
|
||||
MOZ_ASSERT(aClosure.mMessagePorts.IsEmpty());
|
||||
mClosure.mMessagePortIdentifiers.SwapElements(aClosure.mMessagePortIdentifiers);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
@ -118,8 +121,10 @@ private:
|
||||
|
||||
// Release reference to objects that were AddRef'd for
|
||||
// cloning into worker when array goes out of scope.
|
||||
nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
|
||||
clonedObjects.SwapElements(mClonedObjects);
|
||||
WorkerStructuredCloneClosure closure;
|
||||
closure.mClonedObjects.SwapElements(mClosure.mClonedObjects);
|
||||
MOZ_ASSERT(mClosure.mMessagePorts.IsEmpty());
|
||||
closure.mMessagePortIdentifiers.SwapElements(mClosure.mMessagePortIdentifiers);
|
||||
|
||||
JS::Rooted<JS::Value> messageData(aCx);
|
||||
if (!mBuffer.read(aCx, &messageData,
|
||||
@ -185,16 +190,17 @@ ServiceWorkerClient::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
|
||||
const JSStructuredCloneCallbacks* callbacks = WorkerStructuredCloneCallbacks(false);
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
|
||||
WorkerStructuredCloneClosure closure;
|
||||
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
|
||||
if (!buffer.write(aCx, aMessage, transferable, callbacks, &closure)) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<ServiceWorkerClientPostMessageRunnable> runnable =
|
||||
new ServiceWorkerClientPostMessageRunnable(mWindowId, Move(buffer), clonedObjects);
|
||||
new ServiceWorkerClientPostMessageRunnable(mWindowId, Move(buffer),
|
||||
closure);
|
||||
nsresult rv = NS_DispatchToMainThread(runnable);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
|
@ -52,6 +52,8 @@
|
||||
#include "mozilla/dom/ImageDataBinding.h"
|
||||
#include "mozilla/dom/MessageEvent.h"
|
||||
#include "mozilla/dom/MessageEventBinding.h"
|
||||
#include "mozilla/dom/MessagePort.h"
|
||||
#include "mozilla/dom/MessagePortBinding.h"
|
||||
#include "mozilla/dom/MessagePortList.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/PromiseDebugging.h"
|
||||
@ -105,6 +107,7 @@
|
||||
#include "WorkerFeature.h"
|
||||
#include "WorkerRunnable.h"
|
||||
#include "WorkerScope.h"
|
||||
#include "WorkerStructuredClone.h"
|
||||
#include "WorkerThread.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
@ -511,7 +514,7 @@ bool
|
||||
WriteBlobOrFile(JSContext* aCx,
|
||||
JSStructuredCloneWriter* aWriter,
|
||||
BlobImpl* aBlobOrBlobImpl,
|
||||
nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
|
||||
WorkerStructuredCloneClosure& aClosure)
|
||||
{
|
||||
MOZ_ASSERT(aCx);
|
||||
MOZ_ASSERT(aWriter);
|
||||
@ -529,7 +532,7 @@ WriteBlobOrFile(JSContext* aCx,
|
||||
return false;
|
||||
}
|
||||
|
||||
aClonedObjects.AppendElement(aBlobOrBlobImpl);
|
||||
aClosure.mClonedObjects.AppendElement(aBlobOrBlobImpl);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -547,7 +550,7 @@ bool
|
||||
WriteFormData(JSContext* aCx,
|
||||
JSStructuredCloneWriter* aWriter,
|
||||
nsFormData* aFormData,
|
||||
nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
|
||||
WorkerStructuredCloneClosure& aClosure)
|
||||
{
|
||||
MOZ_ASSERT(aCx);
|
||||
MOZ_ASSERT(aWriter);
|
||||
@ -560,11 +563,11 @@ WriteFormData(JSContext* aCx,
|
||||
class MOZ_STACK_CLASS Closure {
|
||||
JSContext* mCx;
|
||||
JSStructuredCloneWriter* mWriter;
|
||||
nsTArray<nsCOMPtr<nsISupports>>& mClones;
|
||||
WorkerStructuredCloneClosure& mClones;
|
||||
|
||||
public:
|
||||
Closure(JSContext* aCx, JSStructuredCloneWriter* aWriter,
|
||||
nsTArray<nsCOMPtr<nsISupports>>& aClones)
|
||||
WorkerStructuredCloneClosure& aClones)
|
||||
: mCx(aCx), mWriter(aWriter), mClones(aClones)
|
||||
{ }
|
||||
|
||||
@ -595,7 +598,7 @@ WriteFormData(JSContext* aCx,
|
||||
}
|
||||
};
|
||||
|
||||
Closure closure(aCx, aWriter, aClonedObjects);
|
||||
Closure closure(aCx, aWriter, aClosure);
|
||||
return aFormData->ForEach(Closure::Write, &closure);
|
||||
}
|
||||
|
||||
@ -639,9 +642,7 @@ struct WorkerStructuredCloneCallbacks
|
||||
{
|
||||
NS_ASSERTION(aClosure, "Null pointer!");
|
||||
|
||||
// We'll stash any nsISupports pointers that need to be AddRef'd here.
|
||||
auto* clonedObjects =
|
||||
static_cast<nsTArray<nsCOMPtr<nsISupports>>*>(aClosure);
|
||||
auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
|
||||
|
||||
// See if this is a Blob/File object.
|
||||
{
|
||||
@ -650,7 +651,7 @@ struct WorkerStructuredCloneCallbacks
|
||||
BlobImpl* blobImpl = blob->Impl();
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
if (WriteBlobOrFile(aCx, aWriter, blobImpl, *clonedObjects)) {
|
||||
if (WriteBlobOrFile(aCx, aWriter, blobImpl, *closure)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -668,7 +669,7 @@ struct WorkerStructuredCloneCallbacks
|
||||
{
|
||||
nsFormData* formData = nullptr;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(FormData, aObj, formData))) {
|
||||
if (WriteFormData(aCx, aWriter, formData, *clonedObjects)) {
|
||||
if (WriteFormData(aCx, aWriter, formData, *closure)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -683,15 +684,96 @@ struct WorkerStructuredCloneCallbacks
|
||||
{
|
||||
Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
}
|
||||
|
||||
static bool
|
||||
ReadTransfer(JSContext* aCx, JSStructuredCloneReader* aReader,
|
||||
uint32_t aTag, void* aContent, uint64_t aExtraData,
|
||||
void* aClosure, JS::MutableHandle<JSObject*> aReturnObject)
|
||||
{
|
||||
MOZ_ASSERT(aClosure);
|
||||
|
||||
auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
|
||||
|
||||
if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
|
||||
MOZ_ASSERT(!aContent);
|
||||
MOZ_ASSERT(aExtraData < closure->mMessagePortIdentifiers.Length());
|
||||
|
||||
ErrorResult rv;
|
||||
nsRefPtr<MessagePortBase> port =
|
||||
dom::MessagePort::Create(closure->mParentWindow,
|
||||
closure->mMessagePortIdentifiers[aExtraData],
|
||||
rv);
|
||||
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
closure->mMessagePorts.AppendElement(port);
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
if (!GetOrCreateDOMReflector(aCx, port, &value)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
aReturnObject.set(&value.toObject());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
Transfer(JSContext* aCx, JS::Handle<JSObject*> aObj, void* aClosure,
|
||||
uint32_t* aTag, JS::TransferableOwnership* aOwnership,
|
||||
void** aContent, uint64_t *aExtraData)
|
||||
{
|
||||
MOZ_ASSERT(aClosure);
|
||||
|
||||
auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
|
||||
|
||||
MessagePortBase* port;
|
||||
nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (NS_WARN_IF(closure->mTransferredPorts.Contains(port))) {
|
||||
// No duplicates.
|
||||
return false;
|
||||
}
|
||||
|
||||
MessagePortIdentifier identifier;
|
||||
if (!port->CloneAndDisentangle(identifier)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
closure->mMessagePortIdentifiers.AppendElement(identifier);
|
||||
closure->mTransferredPorts.AppendElement(port);
|
||||
|
||||
*aTag = SCTAG_DOM_MAP_MESSAGEPORT;
|
||||
*aOwnership = JS::SCTAG_TMO_CUSTOM;
|
||||
*aContent = nullptr;
|
||||
*aExtraData = closure->mMessagePortIdentifiers.Length() - 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
FreeTransfer(uint32_t aTag, JS::TransferableOwnership aOwnership,
|
||||
void *aContent, uint64_t aExtraData, void* aClosure)
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
};
|
||||
|
||||
const JSStructuredCloneCallbacks gWorkerStructuredCloneCallbacks = {
|
||||
WorkerStructuredCloneCallbacks::Read,
|
||||
WorkerStructuredCloneCallbacks::Write,
|
||||
WorkerStructuredCloneCallbacks::Error,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr
|
||||
WorkerStructuredCloneCallbacks::ReadTransfer,
|
||||
WorkerStructuredCloneCallbacks::Transfer,
|
||||
WorkerStructuredCloneCallbacks::FreeTransfer
|
||||
};
|
||||
|
||||
struct MainThreadWorkerStructuredCloneCallbacks
|
||||
@ -731,9 +813,7 @@ struct MainThreadWorkerStructuredCloneCallbacks
|
||||
|
||||
NS_ASSERTION(aClosure, "Null pointer!");
|
||||
|
||||
// We'll stash any nsISupports pointers that need to be AddRef'd here.
|
||||
auto* clonedObjects =
|
||||
static_cast<nsTArray<nsCOMPtr<nsISupports>>*>(aClosure);
|
||||
auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
|
||||
|
||||
// See if this is a Blob/File object.
|
||||
{
|
||||
@ -744,7 +824,7 @@ struct MainThreadWorkerStructuredCloneCallbacks
|
||||
|
||||
if (!blobImpl->MayBeClonedToOtherThreads()) {
|
||||
NS_WARNING("Not all the blob implementations can be sent between threads.");
|
||||
} else if (WriteBlobOrFile(aCx, aWriter, blobImpl, *clonedObjects)) {
|
||||
} else if (WriteBlobOrFile(aCx, aWriter, blobImpl, *closure)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -767,9 +847,9 @@ const JSStructuredCloneCallbacks gMainThreadWorkerStructuredCloneCallbacks = {
|
||||
MainThreadWorkerStructuredCloneCallbacks::Read,
|
||||
MainThreadWorkerStructuredCloneCallbacks::Write,
|
||||
MainThreadWorkerStructuredCloneCallbacks::Error,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr
|
||||
WorkerStructuredCloneCallbacks::ReadTransfer,
|
||||
WorkerStructuredCloneCallbacks::Transfer,
|
||||
WorkerStructuredCloneCallbacks::FreeTransfer
|
||||
};
|
||||
|
||||
struct ChromeWorkerStructuredCloneCallbacks
|
||||
@ -800,9 +880,9 @@ const JSStructuredCloneCallbacks gChromeWorkerStructuredCloneCallbacks = {
|
||||
ChromeWorkerStructuredCloneCallbacks::Read,
|
||||
ChromeWorkerStructuredCloneCallbacks::Write,
|
||||
ChromeWorkerStructuredCloneCallbacks::Error,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr
|
||||
WorkerStructuredCloneCallbacks::ReadTransfer,
|
||||
WorkerStructuredCloneCallbacks::Transfer,
|
||||
WorkerStructuredCloneCallbacks::FreeTransfer
|
||||
};
|
||||
|
||||
struct MainThreadChromeWorkerStructuredCloneCallbacks
|
||||
@ -1153,7 +1233,7 @@ private:
|
||||
class MessageEventRunnable final : public WorkerRunnable
|
||||
{
|
||||
JSAutoStructuredCloneBuffer mBuffer;
|
||||
nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
|
||||
WorkerStructuredCloneClosure mClosure;
|
||||
uint64_t mMessagePortSerial;
|
||||
bool mToMessagePort;
|
||||
|
||||
@ -1163,15 +1243,24 @@ class MessageEventRunnable final : public WorkerRunnable
|
||||
public:
|
||||
MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
TargetAndBusyBehavior aBehavior,
|
||||
JSAutoStructuredCloneBuffer&& aData,
|
||||
nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
|
||||
bool aToMessagePort, uint64_t aMessagePortSerial)
|
||||
: WorkerRunnable(aWorkerPrivate, aBehavior)
|
||||
, mBuffer(Move(aData))
|
||||
, mMessagePortSerial(aMessagePortSerial)
|
||||
, mToMessagePort(aToMessagePort)
|
||||
{
|
||||
mClonedObjects.SwapElements(aClonedObjects);
|
||||
}
|
||||
|
||||
bool
|
||||
Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
||||
JS::Handle<JS::Value> aTransferredValue,
|
||||
const JSStructuredCloneCallbacks *aCallbacks)
|
||||
{
|
||||
bool ok = mBuffer.write(aCx, aValue, aTransferredValue, aCallbacks,
|
||||
&mClosure);
|
||||
// This hashtable has to be empty because it could contain MessagePort
|
||||
// objects that cannot be freed on a different thread.
|
||||
mClosure.mTransferredPorts.Clear();
|
||||
return ok;
|
||||
}
|
||||
|
||||
void
|
||||
@ -1186,12 +1275,19 @@ public:
|
||||
{
|
||||
// Release reference to objects that were AddRef'd for
|
||||
// cloning into worker when array goes out of scope.
|
||||
nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
|
||||
clonedObjects.SwapElements(mClonedObjects);
|
||||
WorkerStructuredCloneClosure closure;
|
||||
closure.mClonedObjects.SwapElements(mClosure.mClonedObjects);
|
||||
MOZ_ASSERT(mClosure.mMessagePorts.IsEmpty());
|
||||
closure.mMessagePortIdentifiers.SwapElements(mClosure.mMessagePortIdentifiers);
|
||||
|
||||
if (aIsMainThread) {
|
||||
closure.mParentWindow = do_QueryInterface(aTarget->GetParentObject());
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> messageData(aCx);
|
||||
if (!mBuffer.read(aCx, &messageData,
|
||||
workers::WorkerStructuredCloneCallbacks(aIsMainThread))) {
|
||||
workers::WorkerStructuredCloneCallbacks(aIsMainThread),
|
||||
&closure)) {
|
||||
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return false;
|
||||
}
|
||||
@ -1217,7 +1313,8 @@ public:
|
||||
}
|
||||
|
||||
event->SetTrusted(true);
|
||||
|
||||
event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()),
|
||||
closure.mMessagePorts));
|
||||
nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
|
||||
|
||||
nsEventStatus dummy = nsEventStatus_eIgnore;
|
||||
@ -1243,7 +1340,7 @@ private:
|
||||
aWorkerPrivate->DispatchMessageEventToMessagePort(aCx,
|
||||
mMessagePortSerial,
|
||||
Move(mBuffer),
|
||||
mClonedObjects);
|
||||
mClosure);
|
||||
}
|
||||
|
||||
if (aWorkerPrivate->IsFrozen()) {
|
||||
@ -3379,19 +3476,16 @@ WorkerPrivateParent<Derived>::PostMessageInternal(
|
||||
transferable.setObject(*array);
|
||||
}
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
|
||||
nsRefPtr<MessageEventRunnable> runnable =
|
||||
new MessageEventRunnable(ParentAsWorkerPrivate(),
|
||||
WorkerRunnable::WorkerThreadModifyBusyCount,
|
||||
aToMessagePort, aMessagePortSerial);
|
||||
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
|
||||
if (!runnable->Write(aCx, aMessage, transferable, callbacks)) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<MessageEventRunnable> runnable =
|
||||
new MessageEventRunnable(ParentAsWorkerPrivate(),
|
||||
WorkerRunnable::WorkerThreadModifyBusyCount,
|
||||
Move(buffer), clonedObjects, aToMessagePort,
|
||||
aMessagePortSerial);
|
||||
runnable->SetMessageSource(aClientInfo);
|
||||
|
||||
if (!runnable->Dispatch(aCx)) {
|
||||
@ -3432,14 +3526,16 @@ bool
|
||||
WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
|
||||
JSContext* aCx, uint64_t aMessagePortSerial,
|
||||
JSAutoStructuredCloneBuffer&& aBuffer,
|
||||
nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
|
||||
WorkerStructuredCloneClosure& aClosure)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
JSAutoStructuredCloneBuffer buffer(Move(aBuffer));
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
|
||||
clonedObjects.SwapElements(aClonedObjects);
|
||||
WorkerStructuredCloneClosure closure;
|
||||
closure.mClonedObjects.SwapElements(aClosure.mClonedObjects);
|
||||
MOZ_ASSERT(aClosure.mMessagePorts.IsEmpty());
|
||||
closure.mMessagePortIdentifiers.SwapElements(aClosure.mMessagePortIdentifiers);
|
||||
|
||||
SharedWorker* sharedWorker;
|
||||
if (!mSharedWorkers.Get(aMessagePortSerial, &sharedWorker)) {
|
||||
@ -3454,6 +3550,8 @@ WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
|
||||
return true;
|
||||
}
|
||||
|
||||
closure.mParentWindow = do_QueryInterface(port->GetParentObject());
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(port->GetParentObject()))) {
|
||||
return false;
|
||||
@ -3461,7 +3559,8 @@ WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
JS::Rooted<JS::Value> data(cx);
|
||||
if (!buffer.read(cx, &data, WorkerStructuredCloneCallbacks(true))) {
|
||||
if (!buffer.read(cx, &data, WorkerStructuredCloneCallbacks(true),
|
||||
&closure)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3478,11 +3577,7 @@ WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
|
||||
|
||||
event->SetTrusted(true);
|
||||
|
||||
nsTArray<nsRefPtr<MessagePortBase>> ports;
|
||||
ports.AppendElement(port);
|
||||
|
||||
nsRefPtr<MessagePortList> portList = new MessagePortList(port, ports);
|
||||
event->SetPorts(portList);
|
||||
event->SetPorts(new MessagePortList(port, closure.mMessagePorts));
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> domEvent;
|
||||
CallQueryInterface(event.get(), getter_AddRefs(domEvent));
|
||||
@ -6182,19 +6277,16 @@ WorkerPrivate::PostMessageToParentInternal(
|
||||
&gChromeWorkerStructuredCloneCallbacks :
|
||||
&gWorkerStructuredCloneCallbacks;
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
|
||||
nsRefPtr<MessageEventRunnable> runnable =
|
||||
new MessageEventRunnable(this,
|
||||
WorkerRunnable::ParentThreadUnchangedBusyCount,
|
||||
aToMessagePort, aMessagePortSerial);
|
||||
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
|
||||
if (!runnable->Write(aCx, aMessage, transferable, callbacks)) {
|
||||
aRv = NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<MessageEventRunnable> runnable =
|
||||
new MessageEventRunnable(this,
|
||||
WorkerRunnable::ParentThreadUnchangedBusyCount,
|
||||
Move(buffer), clonedObjects, aToMessagePort,
|
||||
aMessagePortSerial);
|
||||
if (!runnable->Dispatch(aCx)) {
|
||||
aRv = NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -7348,4 +7440,20 @@ ChromeWorkerStructuredCloneCallbacks(bool aMainRuntime)
|
||||
// Force instantiation.
|
||||
template class WorkerPrivateParent<WorkerPrivate>;
|
||||
|
||||
WorkerStructuredCloneClosure::WorkerStructuredCloneClosure()
|
||||
{}
|
||||
|
||||
WorkerStructuredCloneClosure::~WorkerStructuredCloneClosure()
|
||||
{}
|
||||
|
||||
void
|
||||
WorkerStructuredCloneClosure::Clear()
|
||||
{
|
||||
mParentWindow = nullptr;
|
||||
mClonedObjects.Clear();
|
||||
mMessagePorts.Clear();
|
||||
mMessagePortIdentifiers.Clear();
|
||||
mTransferredPorts.Clear();
|
||||
}
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
@ -73,6 +73,7 @@ class WorkerDebuggerGlobalScope;
|
||||
class WorkerGlobalScope;
|
||||
class WorkerPrivate;
|
||||
class WorkerRunnable;
|
||||
class WorkerStructuredCloneClosure;
|
||||
class WorkerThread;
|
||||
|
||||
// SharedMutex is a small wrapper around an (internal) reference-counted Mutex
|
||||
@ -349,7 +350,7 @@ public:
|
||||
JSContext* aCx,
|
||||
uint64_t aMessagePortSerial,
|
||||
JSAutoStructuredCloneBuffer&& aBuffer,
|
||||
nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects);
|
||||
WorkerStructuredCloneClosure& aClosure);
|
||||
|
||||
void
|
||||
UpdateRuntimeOptions(JSContext* aCx,
|
||||
|
53
dom/workers/WorkerStructuredClone.h
Normal file
53
dom/workers/WorkerStructuredClone.h
Normal file
@ -0,0 +1,53 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_workers_WorkerStructuredClone_h
|
||||
#define mozilla_dom_workers_WorkerStructuredClone_h
|
||||
|
||||
#include "Workers.h"
|
||||
#include "mozilla/dom/PMessagePort.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class MessagePortBase;
|
||||
|
||||
namespace workers {
|
||||
|
||||
// This class is implemented in WorkerPrivate.cpp
|
||||
class WorkerStructuredCloneClosure final
|
||||
{
|
||||
private:
|
||||
WorkerStructuredCloneClosure(const WorkerStructuredCloneClosure&) = delete;
|
||||
WorkerStructuredCloneClosure & operator=(const WorkerStructuredCloneClosure&) = delete;
|
||||
|
||||
public:
|
||||
WorkerStructuredCloneClosure();
|
||||
~WorkerStructuredCloneClosure();
|
||||
|
||||
void Clear();
|
||||
|
||||
// This can be null if the MessagePort is created in a worker.
|
||||
nsCOMPtr<nsPIDOMWindow> mParentWindow;
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports>> mClonedObjects;
|
||||
|
||||
// The transferred ports.
|
||||
nsTArray<nsRefPtr<MessagePortBase>> mMessagePorts;
|
||||
|
||||
// Information for the transferring.
|
||||
nsTArray<MessagePortIdentifier> mMessagePortIdentifiers;
|
||||
|
||||
// To avoid duplicates in the transferred ports.
|
||||
nsTArray<nsRefPtr<MessagePortBase>> mTransferredPorts;
|
||||
};
|
||||
|
||||
} // workers namespace
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
||||
#endif // mozilla_dom_workers_WorkerStructuredClone_h
|
@ -27,6 +27,7 @@
|
||||
#include "RuntimeService.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
#include "WorkerStructuredClone.h"
|
||||
#include "XMLHttpRequestUpload.h"
|
||||
|
||||
using namespace mozilla;
|
||||
@ -413,7 +414,7 @@ class EventRunnable final : public MainThreadProxyRunnable
|
||||
nsString mType;
|
||||
nsString mResponseType;
|
||||
JSAutoStructuredCloneBuffer mResponseBuffer;
|
||||
nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
|
||||
WorkerStructuredCloneClosure mResponseClosure;
|
||||
JS::Heap<JS::Value> mResponse;
|
||||
nsString mResponseText;
|
||||
nsString mResponseURL;
|
||||
@ -794,14 +795,14 @@ class SendRunnable final : public WorkerThreadProxySyncRunnable
|
||||
{
|
||||
nsString mStringBody;
|
||||
JSAutoStructuredCloneBuffer mBody;
|
||||
nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
|
||||
WorkerStructuredCloneClosure mClosure;
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
bool mHasUploadListeners;
|
||||
|
||||
public:
|
||||
SendRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
||||
const nsAString& aStringBody, JSAutoStructuredCloneBuffer&& aBody,
|
||||
nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects,
|
||||
WorkerStructuredCloneClosure& aClosure,
|
||||
nsIEventTarget* aSyncLoopTarget, bool aHasUploadListeners)
|
||||
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
|
||||
, mStringBody(aStringBody)
|
||||
@ -809,7 +810,9 @@ public:
|
||||
, mSyncLoopTarget(aSyncLoopTarget)
|
||||
, mHasUploadListeners(aHasUploadListeners)
|
||||
{
|
||||
mClonedObjects.SwapElements(aClonedObjects);
|
||||
mClosure.mClonedObjects.SwapElements(aClosure.mClonedObjects);
|
||||
MOZ_ASSERT(aClosure.mMessagePorts.IsEmpty());
|
||||
MOZ_ASSERT(aClosure.mMessagePortIdentifiers.IsEmpty());
|
||||
}
|
||||
|
||||
private:
|
||||
@ -1229,11 +1232,13 @@ EventRunnable::PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
workers::ChromeWorkerStructuredCloneCallbacks(true) :
|
||||
workers::WorkerStructuredCloneCallbacks(true);
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
|
||||
WorkerStructuredCloneClosure closure;
|
||||
|
||||
if (mResponseBuffer.write(aCx, response, transferable, callbacks,
|
||||
&clonedObjects)) {
|
||||
mClonedObjects.SwapElements(clonedObjects);
|
||||
&closure)) {
|
||||
mResponseClosure.mClonedObjects.SwapElements(closure.mClonedObjects);
|
||||
MOZ_ASSERT(mResponseClosure.mMessagePorts.IsEmpty());
|
||||
MOZ_ASSERT(mResponseClosure.mMessagePortIdentifiers.IsEmpty());
|
||||
} else {
|
||||
NS_WARNING("Failed to clone response!");
|
||||
mResponseResult = NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
@ -1341,11 +1346,13 @@ EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
workers::ChromeWorkerStructuredCloneCallbacks(false) :
|
||||
workers::WorkerStructuredCloneCallbacks(false);
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
|
||||
clonedObjects.SwapElements(mClonedObjects);
|
||||
WorkerStructuredCloneClosure closure;
|
||||
closure.mClonedObjects.SwapElements(mResponseClosure.mClonedObjects);
|
||||
MOZ_ASSERT(mResponseClosure.mMessagePorts.IsEmpty());
|
||||
MOZ_ASSERT(mResponseClosure.mMessagePortIdentifiers.IsEmpty());
|
||||
|
||||
JS::Rooted<JS::Value> response(aCx);
|
||||
if (!responseBuffer.read(aCx, &response, callbacks, &clonedObjects)) {
|
||||
if (!responseBuffer.read(aCx, &response, callbacks, &closure)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1526,7 +1533,7 @@ SendRunnable::MainThreadRun()
|
||||
workers::WorkerStructuredCloneCallbacks(true);
|
||||
|
||||
JS::Rooted<JS::Value> body(cx);
|
||||
if (mBody.read(cx, &body, callbacks, &mClonedObjects)) {
|
||||
if (mBody.read(cx, &body, callbacks, &mClosure)) {
|
||||
if (NS_FAILED(xpc->JSValToVariant(cx, body, getter_AddRefs(variant)))) {
|
||||
rv = NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
@ -1536,7 +1543,7 @@ SendRunnable::MainThreadRun()
|
||||
}
|
||||
|
||||
mBody.clear();
|
||||
mClonedObjects.Clear();
|
||||
mClosure.Clear();
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
@ -1846,7 +1853,7 @@ XMLHttpRequest::Unpin()
|
||||
void
|
||||
XMLHttpRequest::SendInternal(const nsAString& aStringBody,
|
||||
JSAutoStructuredCloneBuffer&& aBody,
|
||||
nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
|
||||
WorkerStructuredCloneClosure& aClosure,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
@ -1880,7 +1887,7 @@ XMLHttpRequest::SendInternal(const nsAString& aStringBody,
|
||||
|
||||
nsRefPtr<SendRunnable> runnable =
|
||||
new SendRunnable(mWorkerPrivate, mProxy, aStringBody, Move(aBody),
|
||||
aClonedObjects, syncLoopTarget, hasUploadListeners);
|
||||
aClosure, syncLoopTarget, hasUploadListeners);
|
||||
if (!runnable->Dispatch(cx)) {
|
||||
// Dispatch() may have spun the event loop and we may have already unrooted.
|
||||
// If so we don't want autoUnpin to try again.
|
||||
@ -2102,9 +2109,9 @@ XMLHttpRequest::Send(ErrorResult& aRv)
|
||||
|
||||
// Nothing to clone.
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
|
||||
WorkerStructuredCloneClosure closure;
|
||||
|
||||
SendInternal(NullString(), Move(buffer), clonedObjects, aRv);
|
||||
SendInternal(NullString(), Move(buffer), closure, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2124,9 +2131,9 @@ XMLHttpRequest::Send(const nsAString& aBody, ErrorResult& aRv)
|
||||
|
||||
// Nothing to clone.
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
|
||||
WorkerStructuredCloneClosure closure;
|
||||
|
||||
SendInternal(aBody, Move(buffer), clonedObjects, aRv);
|
||||
SendInternal(aBody, Move(buffer), closure, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2167,15 +2174,15 @@ XMLHttpRequest::Send(JS::Handle<JSObject*> aBody, ErrorResult& aRv)
|
||||
ChromeWorkerStructuredCloneCallbacks(false) :
|
||||
WorkerStructuredCloneCallbacks(false);
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
|
||||
WorkerStructuredCloneClosure closure;
|
||||
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
if (!buffer.write(cx, valToClone, callbacks, &clonedObjects)) {
|
||||
if (!buffer.write(cx, valToClone, callbacks, &closure)) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
SendInternal(EmptyString(), Move(buffer), clonedObjects, aRv);
|
||||
SendInternal(EmptyString(), Move(buffer), closure, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2213,15 +2220,15 @@ XMLHttpRequest::Send(Blob& aBody, ErrorResult& aRv)
|
||||
ChromeWorkerStructuredCloneCallbacks(false) :
|
||||
WorkerStructuredCloneCallbacks(false);
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
|
||||
WorkerStructuredCloneClosure closure;
|
||||
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
if (!buffer.write(cx, value, callbacks, &clonedObjects)) {
|
||||
if (!buffer.write(cx, value, callbacks, &closure)) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
SendInternal(EmptyString(), Move(buffer), clonedObjects, aRv);
|
||||
SendInternal(EmptyString(), Move(buffer), closure, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2251,15 +2258,14 @@ XMLHttpRequest::Send(nsFormData& aBody, ErrorResult& aRv)
|
||||
ChromeWorkerStructuredCloneCallbacks(false) :
|
||||
WorkerStructuredCloneCallbacks(false);
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
|
||||
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
if (!buffer.write(cx, value, callbacks, &clonedObjects)) {
|
||||
WorkerStructuredCloneClosure closure;
|
||||
if (!buffer.write(cx, value, callbacks, &closure)) {
|
||||
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
SendInternal(EmptyString(), Move(buffer), clonedObjects, aRv);
|
||||
SendInternal(EmptyString(), Move(buffer), closure, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -28,6 +28,7 @@ BEGIN_WORKERS_NAMESPACE
|
||||
class Proxy;
|
||||
class XMLHttpRequestUpload;
|
||||
class WorkerPrivate;
|
||||
class WorkerStructuredCloneClosure;
|
||||
|
||||
class XMLHttpRequest final: public nsXHREventTarget,
|
||||
public WorkerFeature
|
||||
@ -292,7 +293,7 @@ private:
|
||||
void
|
||||
SendInternal(const nsAString& aStringBody,
|
||||
JSAutoStructuredCloneBuffer&& aBody,
|
||||
nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
|
||||
WorkerStructuredCloneClosure& aClosure,
|
||||
ErrorResult& aRv);
|
||||
};
|
||||
|
||||
|
@ -79,8 +79,8 @@ onconnect = function(event) {
|
||||
if (!("ports" in event)) {
|
||||
throw new Error("'message' event doesn't have a 'ports' property!");
|
||||
}
|
||||
if (!(event.ports === null)) {
|
||||
throw new Error("'message' event has a non-null 'ports' property!");
|
||||
if (event.ports === null) {
|
||||
throw new Error("'message' event has a null 'ports' property!");
|
||||
}
|
||||
event.target.postMessage(event.data);
|
||||
throw new Error(event.data);
|
||||
|
@ -15,6 +15,7 @@ class nsIIPCBackgroundChildCreateCallback;
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class BlobImpl;
|
||||
class ContentChild;
|
||||
class ContentParent;
|
||||
class PBlobChild;
|
||||
@ -67,6 +68,10 @@ public:
|
||||
GetOrCreateActorForBlob(PBackgroundChild* aBackgroundActor,
|
||||
nsIDOMBlob* aBlob);
|
||||
|
||||
static mozilla::dom::PBlobChild*
|
||||
GetOrCreateActorForBlobImpl(PBackgroundChild* aBackgroundActor,
|
||||
mozilla::dom::BlobImpl* aBlobImpl);
|
||||
|
||||
// See above.
|
||||
static void
|
||||
CloseForCurrentThread();
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "mozilla/dom/cache/ActorUtils.h"
|
||||
#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
|
||||
#include "mozilla/dom/ipc/BlobChild.h"
|
||||
#include "mozilla/dom/MessagePortChild.h"
|
||||
#include "mozilla/ipc/PBackgroundTestChild.h"
|
||||
#include "mozilla/layout/VsyncChild.h"
|
||||
#include "mozilla/net/PUDPSocketChild.h"
|
||||
@ -340,6 +341,28 @@ BackgroundChildImpl::DeallocPMediaChild(media::PMediaChild *aActor)
|
||||
return media::DeallocPMediaChild(aActor);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// MessageChannel/MessagePort API
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
dom::PMessagePortChild*
|
||||
BackgroundChildImpl::AllocPMessagePortChild(const nsID& aUUID,
|
||||
const nsID& aDestinationUUID,
|
||||
const uint32_t& aSequenceID)
|
||||
{
|
||||
nsRefPtr<dom::MessagePortChild> agent = new dom::MessagePortChild();
|
||||
return agent.forget().take();
|
||||
}
|
||||
|
||||
bool
|
||||
BackgroundChildImpl::DeallocPMessagePortChild(PMessagePortChild* aActor)
|
||||
{
|
||||
nsRefPtr<dom::MessagePortChild> child =
|
||||
dont_AddRef(static_cast<dom::MessagePortChild*>(aActor));
|
||||
MOZ_ASSERT(child);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -121,6 +121,13 @@ protected:
|
||||
|
||||
virtual bool
|
||||
DeallocPCacheStreamControlChild(dom::cache::PCacheStreamControlChild* aActor) override;
|
||||
|
||||
virtual PMessagePortChild*
|
||||
AllocPMessagePortChild(const nsID& aUUID, const nsID& aDestinationUUID,
|
||||
const uint32_t& aSequenceID) override;
|
||||
|
||||
virtual bool
|
||||
DeallocPMessagePortChild(PMessagePortChild* aActor) override;
|
||||
};
|
||||
|
||||
class BackgroundChildImpl::ThreadLocal final
|
||||
|
@ -914,17 +914,27 @@ PBlobChild*
|
||||
BackgroundChild::GetOrCreateActorForBlob(PBackgroundChild* aBackgroundActor,
|
||||
nsIDOMBlob* aBlob)
|
||||
{
|
||||
MOZ_ASSERT(aBackgroundActor);
|
||||
MOZ_ASSERT(aBlob);
|
||||
|
||||
nsRefPtr<BlobImpl> blobImpl = static_cast<Blob*>(aBlob)->Impl();
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
return GetOrCreateActorForBlobImpl(aBackgroundActor, blobImpl);
|
||||
}
|
||||
|
||||
// static
|
||||
PBlobChild*
|
||||
BackgroundChild::GetOrCreateActorForBlobImpl(PBackgroundChild* aBackgroundActor,
|
||||
BlobImpl* aBlobImpl)
|
||||
{
|
||||
MOZ_ASSERT(aBackgroundActor);
|
||||
MOZ_ASSERT(aBlobImpl);
|
||||
MOZ_ASSERT(GetForCurrentThread(),
|
||||
"BackgroundChild not created on this thread yet!");
|
||||
MOZ_ASSERT(aBackgroundActor == GetForCurrentThread(),
|
||||
"BackgroundChild is bound to a different thread!");
|
||||
|
||||
nsRefPtr<BlobImpl> blobImpl = static_cast<Blob*>(aBlob)->Impl();
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
BlobChild* actor = BlobChild::GetOrCreate(aBackgroundActor, blobImpl);
|
||||
BlobChild* actor = BlobChild::GetOrCreate(aBackgroundActor, aBlobImpl);
|
||||
if (NS_WARN_IF(!actor)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/PBlobParent.h"
|
||||
#include "mozilla/dom/MessagePortParent.h"
|
||||
#include "mozilla/dom/ServiceWorkerRegistrar.h"
|
||||
#include "mozilla/dom/cache/ActorUtils.h"
|
||||
#include "mozilla/dom/indexedDB/ActorsParent.h"
|
||||
@ -39,6 +40,8 @@ using mozilla::ipc::AssertIsOnBackgroundThread;
|
||||
using mozilla::dom::cache::PCacheParent;
|
||||
using mozilla::dom::cache::PCacheStorageParent;
|
||||
using mozilla::dom::cache::PCacheStreamControlParent;
|
||||
using mozilla::dom::MessagePortParent;
|
||||
using mozilla::dom::PMessagePortParent;
|
||||
using mozilla::dom::UDPSocketParent;
|
||||
|
||||
namespace {
|
||||
@ -563,6 +566,41 @@ BackgroundParentImpl::DeallocPCacheStreamControlParent(PCacheStreamControlParent
|
||||
return true;
|
||||
}
|
||||
|
||||
PMessagePortParent*
|
||||
BackgroundParentImpl::AllocPMessagePortParent(const nsID& aUUID,
|
||||
const nsID& aDestinationUUID,
|
||||
const uint32_t& aSequenceID)
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
return new MessagePortParent(aUUID);
|
||||
}
|
||||
|
||||
bool
|
||||
BackgroundParentImpl::RecvPMessagePortConstructor(PMessagePortParent* aActor,
|
||||
const nsID& aUUID,
|
||||
const nsID& aDestinationUUID,
|
||||
const uint32_t& aSequenceID)
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
MessagePortParent* mp = static_cast<MessagePortParent*>(aActor);
|
||||
return mp->Entangle(aDestinationUUID, aSequenceID);
|
||||
}
|
||||
|
||||
bool
|
||||
BackgroundParentImpl::DeallocPMessagePortParent(PMessagePortParent* aActor)
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(aActor);
|
||||
|
||||
delete static_cast<MessagePortParent*>(aActor);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -129,6 +129,20 @@ protected:
|
||||
const nsCString& aFilter) override;
|
||||
virtual bool
|
||||
DeallocPUDPSocketParent(PUDPSocketParent*) override;
|
||||
|
||||
virtual PMessagePortParent*
|
||||
AllocPMessagePortParent(const nsID& aUUID,
|
||||
const nsID& aDestinationUUID,
|
||||
const uint32_t& aSequenceID) override;
|
||||
|
||||
virtual bool
|
||||
RecvPMessagePortConstructor(PMessagePortParent* aActor,
|
||||
const nsID& aUUID,
|
||||
const nsID& aDestinationUUID,
|
||||
const uint32_t& aSequenceID) override;
|
||||
|
||||
virtual bool
|
||||
DeallocPMessagePortParent(PMessagePortParent* aActor) override;
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
|
@ -10,6 +10,7 @@ include protocol PCache;
|
||||
include protocol PCacheStorage;
|
||||
include protocol PCacheStreamControl;
|
||||
include protocol PFileDescriptorSet;
|
||||
include protocol PMessagePort;
|
||||
include protocol PMedia;
|
||||
include protocol PServiceWorkerManager;
|
||||
include protocol PUDPSocket;
|
||||
@ -35,6 +36,7 @@ sync protocol PBackground
|
||||
manages PCacheStorage;
|
||||
manages PCacheStreamControl;
|
||||
manages PFileDescriptorSet;
|
||||
manages PMessagePort;
|
||||
manages PMedia;
|
||||
manages PServiceWorkerManager;
|
||||
manages PUDPSocket;
|
||||
@ -59,6 +61,8 @@ parent:
|
||||
|
||||
PCacheStorage(Namespace aNamespace, PrincipalInfo aPrincipalInfo);
|
||||
|
||||
PMessagePort(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
|
||||
|
||||
child:
|
||||
PCache();
|
||||
PCacheStreamControl();
|
||||
|
@ -148,7 +148,7 @@ JS_WriteStructuredClone(JSContext* cx, JS::HandleValue v, uint64_t** datap, size
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_ClearStructuredClone(uint64_t* data, size_t nbytes,
|
||||
const JSStructuredCloneCallbacks* optionalCallbacks,
|
||||
void* closure);
|
||||
void *closure, bool freeData = true);
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_StructuredCloneHasTransferables(const uint64_t* data, size_t nbytes, bool* hasTransferable);
|
||||
|
@ -1908,10 +1908,12 @@ JS_WriteStructuredClone(JSContext* cx, HandleValue value, uint64_t** bufp, size_
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_ClearStructuredClone(uint64_t* data, size_t nbytes,
|
||||
const JSStructuredCloneCallbacks* optionalCallbacks,
|
||||
void* closure)
|
||||
void* closure, bool freeData)
|
||||
{
|
||||
DiscardTransferables(data, nbytes, optionalCallbacks, closure);
|
||||
js_free(data);
|
||||
if (freeData) {
|
||||
js_free(data);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1,5 +0,0 @@
|
||||
[event-ports-dedicated.html]
|
||||
type: testharness
|
||||
[e.ports in dedicated worker]
|
||||
expected: FAIL
|
||||
|
Loading…
Reference in New Issue
Block a user