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:
Andrea Marchesini 2015-06-17 11:44:27 +01:00
parent 84d9d4e6f1
commit 8372e7d29d
87 changed files with 4218 additions and 1349 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -0,0 +1,390 @@
/* -*- 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;
scInfo.subsumes = aSubsumes;
return mBuffer.write(aCx, aMessage, aTransfer, &sPostMessageCallbacks,
&scInfo);
}
} // dom namespace
} // mozilla namespace

108
dom/base/PostMessageEvent.h Normal file
View 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

View File

@ -7,6 +7,7 @@
#include "ProcessGlobal.h"
#include "nsContentCID.h"
#include "nsDOMClassInfoID.h"
using namespace mozilla;
using namespace mozilla::dom;

View File

@ -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

View File

@ -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);
@ -7150,6 +7159,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)
{

View File

@ -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
@ -2443,6 +2449,7 @@ private:
static nsNameSpaceManager *sNameSpaceManager;
static nsIIOService *sIOService;
static nsIUUIDGenerator *sUUIDGenerator;
static bool sImgLoaderInitialized;
static void InitImgLoader();

View File

@ -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"

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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]

View File

@ -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
@ -285,15 +281,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]

View File

@ -729,9 +729,6 @@ DOMInterfaces = {
'MessagePort': {
'nativeType': 'mozilla::dom::MessagePortBase',
'headerFile': 'mozilla/dom/MessagePort.h',
'binaryNames': {
'postMessage': 'postMessageMoz',
},
},
'MimeType': {

View File

@ -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;
}

View File

@ -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)) {

View File

@ -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;
}
}

View File

@ -19,7 +19,7 @@ namespace dom {
struct
StructuredCloneClosure
{
nsTArray<nsRefPtr<Blob>> mBlobs;
nsTArray<nsRefPtr<BlobImpl>> mBlobImpls;
};
struct

View 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

View File

@ -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;

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@ -0,0 +1,5 @@
[DEFAULT]
support-files =
iframe_messageChannel_chrome.html
[test_messageChannel.xul]

View File

@ -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>

View File

@ -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>

View 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]

View 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']

View 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]]);
}

View 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);
}
}

View File

@ -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]);

View 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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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>

View 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);
}

View File

@ -0,0 +1,7 @@
onmessage = function(evt) {
evt.data.onmessage = function(event) {
evt.data.postMessage(event.data);
}
}
postMessage("READY");

View File

@ -96,6 +96,7 @@ DIRS += [
'camera',
'audiochannel',
'broadcastchannel',
'messagechannel',
'promise',
'smil',
'telephony',

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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,

View 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

View File

@ -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

View File

@ -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);
};

View File

@ -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);

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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);

View File

@ -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;
}

View File

@ -1,5 +0,0 @@
[event-ports-dedicated.html]
type: testharness
[e.ports in dedicated worker]
expected: FAIL