summaryrefslogtreecommitdiffstats
path: root/dom/messagechannel
diff options
context:
space:
mode:
Diffstat (limited to 'dom/messagechannel')
-rw-r--r--dom/messagechannel/MessageChannel.cpp91
-rw-r--r--dom/messagechannel/MessageChannel.h71
-rw-r--r--dom/messagechannel/MessagePort.cpp1011
-rw-r--r--dom/messagechannel/MessagePort.h192
-rw-r--r--dom/messagechannel/MessagePortChild.cpp51
-rw-r--r--dom/messagechannel/MessagePortChild.h52
-rw-r--r--dom/messagechannel/MessagePortParent.cpp177
-rw-r--r--dom/messagechannel/MessagePortParent.h65
-rw-r--r--dom/messagechannel/MessagePortService.cpp409
-rw-r--r--dom/messagechannel/MessagePortService.h60
-rw-r--r--dom/messagechannel/PMessagePort.ipdl58
-rw-r--r--dom/messagechannel/SharedMessagePortMessage.cpp176
-rw-r--r--dom/messagechannel/SharedMessagePortMessage.h57
-rw-r--r--dom/messagechannel/moz.build37
-rw-r--r--dom/messagechannel/tests/chrome.ini12
-rw-r--r--dom/messagechannel/tests/iframe_messageChannel_chrome.html11
-rw-r--r--dom/messagechannel/tests/iframe_messageChannel_cloning.html22
-rw-r--r--dom/messagechannel/tests/iframe_messageChannel_pingpong.html33
-rw-r--r--dom/messagechannel/tests/iframe_messageChannel_post.html24
-rw-r--r--dom/messagechannel/tests/iframe_messageChannel_sharedWorker2.html14
-rw-r--r--dom/messagechannel/tests/iframe_messageChannel_transferable.html26
-rw-r--r--dom/messagechannel/tests/mm_messageChannel.js76
-rw-r--r--dom/messagechannel/tests/mm_messageChannelParent.js143
-rw-r--r--dom/messagechannel/tests/mm_messageChannelParent.xul12
-rw-r--r--dom/messagechannel/tests/mm_messageChannelParentNotRemote.xul12
-rw-r--r--dom/messagechannel/tests/mochitest.ini28
-rw-r--r--dom/messagechannel/tests/moz.build8
-rw-r--r--dom/messagechannel/tests/sharedWorker2_messageChannel.js7
-rw-r--r--dom/messagechannel/tests/sharedWorker_messageChannel.js8
-rw-r--r--dom/messagechannel/tests/test_messageChannel.html43
-rw-r--r--dom/messagechannel/tests/test_messageChannel.xul38
-rw-r--r--dom/messagechannel/tests/test_messageChannelWithMessageManager.xul28
-rw-r--r--dom/messagechannel/tests/test_messageChannelWithMessageManagerNotRemote.xul28
-rw-r--r--dom/messagechannel/tests/test_messageChannel_any.html115
-rw-r--r--dom/messagechannel/tests/test_messageChannel_bug1178076.html38
-rw-r--r--dom/messagechannel/tests/test_messageChannel_bug1224825.html94
-rw-r--r--dom/messagechannel/tests/test_messageChannel_cloning.html70
-rw-r--r--dom/messagechannel/tests/test_messageChannel_forceClose.html30
-rw-r--r--dom/messagechannel/tests/test_messageChannel_pingpong.html77
-rw-r--r--dom/messagechannel/tests/test_messageChannel_post.html76
-rw-r--r--dom/messagechannel/tests/test_messageChannel_selfTransferring.html33
-rw-r--r--dom/messagechannel/tests/test_messageChannel_sharedWorker.html36
-rw-r--r--dom/messagechannel/tests/test_messageChannel_sharedWorker2.html34
-rw-r--r--dom/messagechannel/tests/test_messageChannel_start.html235
-rw-r--r--dom/messagechannel/tests/test_messageChannel_transferable.html111
-rw-r--r--dom/messagechannel/tests/test_messageChannel_unshipped.html123
-rw-r--r--dom/messagechannel/tests/test_messageChannel_worker.html60
-rw-r--r--dom/messagechannel/tests/test_messageChannel_worker_forceClose.html27
-rw-r--r--dom/messagechannel/tests/worker_messageChannel.js119
-rw-r--r--dom/messagechannel/tests/worker_messageChannel_any.js7
50 files changed, 4365 insertions, 0 deletions
diff --git a/dom/messagechannel/MessageChannel.cpp b/dom/messagechannel/MessageChannel.cpp
new file mode 100644
index 000000000..a0604ae4a
--- /dev/null
+++ b/dom/messagechannel/MessageChannel.cpp
@@ -0,0 +1,91 @@
+/* -*- 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/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 "nsIGlobalObject.h"
+#include "nsIPrincipal.h"
+#include "nsServiceManagerUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MessageChannel, mGlobal, 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
+
+MessageChannel::MessageChannel(nsIGlobalObject* aGlobal)
+ : mGlobal(aGlobal)
+{
+ MOZ_ASSERT(aGlobal);
+}
+
+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)
+{
+ nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+ return Constructor(global, aRv);
+}
+
+/* static */ already_AddRefed<MessageChannel>
+MessageChannel::Constructor(nsIGlobalObject* aGlobal, ErrorResult& aRv)
+{
+ MOZ_ASSERT(aGlobal);
+
+ nsID portUUID1;
+ aRv = nsContentUtils::GenerateUUIDInPlace(portUUID1);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ nsID portUUID2;
+ aRv = nsContentUtils::GenerateUUIDInPlace(portUUID2);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ RefPtr<MessageChannel> channel = new MessageChannel(aGlobal);
+
+ channel->mPort1 = MessagePort::Create(aGlobal, portUUID1, portUUID2, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ channel->mPort2 = MessagePort::Create(aGlobal, 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
diff --git a/dom/messagechannel/MessageChannel.h b/dom/messagechannel/MessageChannel.h
new file mode 100644
index 000000000..a9c952181
--- /dev/null
+++ b/dom/messagechannel/MessageChannel.h
@@ -0,0 +1,71 @@
+/* -*- 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_MessageChannel_h
+#define mozilla_dom_MessageChannel_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+#include "nsCOMPtr.h"
+
+class nsIGlobalObject;
+
+namespace mozilla {
+namespace dom {
+
+class MessagePort;
+
+class MessageChannel final : public nsISupports
+ , public nsWrapperCache
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MessageChannel)
+
+ nsIGlobalObject*
+ GetParentObject() const
+ {
+ return mGlobal;
+ }
+
+ virtual JSObject*
+ WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+ static already_AddRefed<MessageChannel>
+ Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
+
+ static already_AddRefed<MessageChannel>
+ Constructor(nsIGlobalObject* aGlobal, ErrorResult& aRv);
+
+ MessagePort*
+ Port1() const
+ {
+ return mPort1;
+ }
+
+ MessagePort*
+ Port2() const
+ {
+ return mPort2;
+ }
+
+private:
+ explicit MessageChannel(nsIGlobalObject* aGlobal);
+ ~MessageChannel();
+
+ nsCOMPtr<nsIGlobalObject> mGlobal;
+
+ RefPtr<MessagePort> mPort1;
+ RefPtr<MessagePort> mPort2;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_MessageChannel_h
diff --git a/dom/messagechannel/MessagePort.cpp b/dom/messagechannel/MessagePort.cpp
new file mode 100644
index 000000000..56204da99
--- /dev/null
+++ b/dom/messagechannel/MessagePort.cpp
@@ -0,0 +1,1011 @@
+/* -*- 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/MessageEventBinding.h"
+#include "mozilla/dom/MessagePortBinding.h"
+#include "mozilla/dom/MessagePortChild.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 "mozilla/MessagePortTimelineMarker.h"
+#include "mozilla/TimelineConsumers.h"
+#include "mozilla/TimelineMarker.h"
+#include "mozilla/Unused.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 PostMessageRunnable final : public CancelableRunnable
+{
+ friend class MessagePort;
+
+public:
+ PostMessageRunnable(MessagePort* aPort, SharedMessagePortMessage* aData)
+ : mPort(aPort)
+ , mData(aData)
+ {
+ MOZ_ASSERT(aPort);
+ MOZ_ASSERT(aData);
+ }
+
+ NS_IMETHOD
+ Run() override
+ {
+ NS_ASSERT_OWNINGTHREAD(Runnable);
+
+ // The port can be cycle collected while this runnable is pending in
+ // the event queue.
+ if (!mPort) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(mPort->mPostMessageRunnable == this);
+
+ nsresult rv = DispatchMessage();
+
+ // We must check if we were waiting for this message in order to shutdown
+ // the port.
+ mPort->UpdateMustKeepAlive();
+
+ mPort->mPostMessageRunnable = nullptr;
+ mPort->Dispatch();
+
+ return rv;
+ }
+
+ nsresult
+ Cancel() override
+ {
+ NS_ASSERT_OWNINGTHREAD(Runnable);
+
+ mPort = nullptr;
+ mData = nullptr;
+ return NS_OK;
+ }
+
+private:
+ nsresult
+ DispatchMessage() const
+ {
+ NS_ASSERT_OWNINGTHREAD(Runnable);
+
+ nsCOMPtr<nsIGlobalObject> globalObject = mPort->GetParentObject();
+
+ AutoJSAPI jsapi;
+ if (!globalObject || !jsapi.Init(globalObject)) {
+ NS_WARNING("Failed to initialize AutoJSAPI object.");
+ return NS_ERROR_FAILURE;
+ }
+
+ JSContext* cx = jsapi.cx();
+
+ ErrorResult rv;
+ JS::Rooted<JS::Value> value(cx);
+
+ UniquePtr<AbstractTimelineMarker> start;
+ UniquePtr<AbstractTimelineMarker> end;
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+ bool isTimelineRecording = timelines && !timelines->IsEmpty();
+
+ if (isTimelineRecording) {
+ start = MakeUnique<MessagePortTimelineMarker>(
+ ProfileTimelineMessagePortOperationType::DeserializeData,
+ MarkerTracingType::START);
+ }
+
+ mData->Read(mPort->GetParentObject(), cx, &value, rv);
+
+ if (isTimelineRecording) {
+ end = MakeUnique<MessagePortTimelineMarker>(
+ ProfileTimelineMessagePortOperationType::DeserializeData,
+ MarkerTracingType::END);
+ timelines->AddMarkerForAllObservedDocShells(start);
+ timelines->AddMarkerForAllObservedDocShells(end);
+ }
+
+ if (NS_WARN_IF(rv.Failed())) {
+ return rv.StealNSResult();
+ }
+
+ // Create the event
+ nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
+ do_QueryInterface(mPort->GetOwner());
+ RefPtr<MessageEvent> event =
+ new MessageEvent(eventTarget, nullptr, nullptr);
+
+ Sequence<OwningNonNull<MessagePort>> ports;
+ if (!mData->TakeTransferredPortsAsSequence(ports)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ Nullable<WindowProxyOrMessagePort> source;
+ source.SetValue().SetAsMessagePort() = mPort;
+
+ event->InitMessageEvent(nullptr, NS_LITERAL_STRING("message"),
+ false /* non-bubbling */,
+ false /* cancelable */, value, EmptyString(),
+ EmptyString(), source, ports);
+ event->SetTrusted(true);
+
+ bool dummy;
+ mPort->DispatchEvent(static_cast<dom::Event*>(event.get()), &dummy);
+
+ return NS_OK;
+ }
+
+private:
+ ~PostMessageRunnable()
+ {}
+
+ RefPtr<MessagePort> mPort;
+ RefPtr<SharedMessagePortMessage> mData;
+};
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
+ DOMEventTargetHelper)
+ if (tmp->mPostMessageRunnable) {
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mPostMessageRunnable->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,
+ DOMEventTargetHelper)
+ if (tmp->mPostMessageRunnable) {
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPostMessageRunnable->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(DOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper)
+
+namespace {
+
+class MessagePortWorkerHolder final : public workers::WorkerHolder
+{
+ MessagePort* mPort;
+
+public:
+ explicit MessagePortWorkerHolder(MessagePort* aPort)
+ : mPort(aPort)
+ {
+ MOZ_ASSERT(aPort);
+ MOZ_COUNT_CTOR(MessagePortWorkerHolder);
+ }
+
+ virtual bool Notify(workers::Status aStatus) override
+ {
+ if (aStatus > Running) {
+ // We cannot process messages anymore because we cannot dispatch new
+ // runnables. Let's force a Close().
+ mPort->CloseForced();
+ }
+
+ return true;
+ }
+
+private:
+ ~MessagePortWorkerHolder()
+ {
+ MOZ_COUNT_DTOR(MessagePortWorkerHolder);
+ }
+};
+
+class ForceCloseHelper final : public nsIIPCBackgroundChildCreateCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ static void ForceClose(const MessagePortIdentifier& aIdentifier)
+ {
+ PBackgroundChild* actor =
+ mozilla::ipc::BackgroundChild::GetForCurrentThread();
+ if (actor) {
+ Unused << actor->SendMessagePortForceClose(aIdentifier.uuid(),
+ aIdentifier.destinationUuid(),
+ aIdentifier.sequenceId());
+ return;
+ }
+
+ RefPtr<ForceCloseHelper> helper = new ForceCloseHelper(aIdentifier);
+ if (NS_WARN_IF(!mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(helper))) {
+ MOZ_CRASH();
+ }
+ }
+
+private:
+ explicit ForceCloseHelper(const MessagePortIdentifier& aIdentifier)
+ : mIdentifier(aIdentifier)
+ {}
+
+ ~ForceCloseHelper() {}
+
+ void ActorFailed() override
+ {
+ MOZ_CRASH("Failed to create a PBackgroundChild actor!");
+ }
+
+ void ActorCreated(mozilla::ipc::PBackgroundChild* aActor) override
+ {
+ ForceClose(mIdentifier);
+ }
+
+ const MessagePortIdentifier mIdentifier;
+};
+
+NS_IMPL_ISUPPORTS(ForceCloseHelper, nsIIPCBackgroundChildCreateCallback)
+
+} // namespace
+
+MessagePort::MessagePort(nsIGlobalObject* aGlobal)
+ : DOMEventTargetHelper(aGlobal)
+ , mInnerID(0)
+ , mMessageQueueEnabled(false)
+ , mIsKeptAlive(false)
+{
+ MOZ_ASSERT(aGlobal);
+
+ mIdentifier = new MessagePortIdentifier();
+ mIdentifier->neutered() = true;
+ mIdentifier->sequenceId() = 0;
+}
+
+MessagePort::~MessagePort()
+{
+ CloseForced();
+ MOZ_ASSERT(!mWorkerHolder);
+}
+
+/* static */ already_AddRefed<MessagePort>
+MessagePort::Create(nsIGlobalObject* aGlobal, const nsID& aUUID,
+ const nsID& aDestinationUUID, ErrorResult& aRv)
+{
+ MOZ_ASSERT(aGlobal);
+
+ RefPtr<MessagePort> mp = new MessagePort(aGlobal);
+ mp->Initialize(aUUID, aDestinationUUID, 1 /* 0 is an invalid sequence ID */,
+ false /* Neutered */, eStateUnshippedEntangled, aRv);
+ return mp.forget();
+}
+
+/* static */ already_AddRefed<MessagePort>
+MessagePort::Create(nsIGlobalObject* aGlobal,
+ const MessagePortIdentifier& aIdentifier,
+ ErrorResult& aRv)
+{
+ MOZ_ASSERT(aGlobal);
+
+ RefPtr<MessagePort> mp = new MessagePort(aGlobal);
+ 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;
+
+ if (mNeutered) {
+ // If this port is neutered we don't want to keep it alive artificially nor
+ // we want to add listeners or workerWorkerHolders.
+ mState = eStateDisentangled;
+ return;
+ }
+
+ if (mState == eStateEntangling) {
+ ConnectToPBackground();
+ } else {
+ MOZ_ASSERT(mState == eStateUnshippedEntangled);
+ }
+
+ // The port has to keep itself alive until it's entangled.
+ UpdateMustKeepAlive();
+
+ if (!NS_IsMainThread()) {
+ WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+ MOZ_ASSERT(workerPrivate);
+ MOZ_ASSERT(!mWorkerHolder);
+
+ nsAutoPtr<WorkerHolder> workerHolder(new MessagePortWorkerHolder(this));
+ if (NS_WARN_IF(!workerHolder->HoldWorker(workerPrivate, Closing))) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+
+ mWorkerHolder = Move(workerHolder);
+ } else if (GetOwner()) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(GetOwner()->IsInnerWindow());
+ mInnerID = GetOwner()->WindowID();
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->AddObserver(this, "inner-window-destroyed", false);
+ }
+ }
+}
+
+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;
+ }
+
+ JS::Rooted<JSObject*> object(aCx, &value.toObject());
+
+ MessagePort* port = nullptr;
+ nsresult rv = UNWRAP_OBJECT(MessagePort, &object, 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);
+ }
+
+ RefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
+
+ UniquePtr<AbstractTimelineMarker> start;
+ UniquePtr<AbstractTimelineMarker> end;
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+ bool isTimelineRecording = timelines && !timelines->IsEmpty();
+
+ if (isTimelineRecording) {
+ start = MakeUnique<MessagePortTimelineMarker>(
+ ProfileTimelineMessagePortOperationType::SerializeData,
+ MarkerTracingType::START);
+ }
+
+ data->Write(aCx, aMessage, transferable,
+ JS::CloneDataPolicy().denySharedArrayBuffer(), aRv);
+
+ if (isTimelineRecording) {
+ end = MakeUnique<MessagePortTimelineMarker>(
+ ProfileTimelineMessagePortOperationType::SerializeData,
+ MarkerTracingType::END);
+ timelines->AddMarkerForAllObservedDocShells(start);
+ timelines->AddMarkerForAllObservedDocShells(end);
+ }
+
+ if (NS_WARN_IF(aRv.Failed())) {
+ 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/disentangled.
+ if (mState == eStateEntanglingForDisentangle ||
+ mState == eStateEntanglingForClose) {
+ return;
+ }
+
+ RemoveDocFromBFCache();
+
+ // Not entangled yet.
+ if (mState == eStateEntangling) {
+ mMessagesForTheOtherPort.AppendElement(data);
+ return;
+ }
+
+ MOZ_ASSERT(mActor);
+ MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
+
+ AutoTArray<RefPtr<SharedMessagePortMessage>, 1> array;
+ array.AppendElement(data);
+
+ AutoTArray<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() || mPostMessageRunnable) {
+ return;
+ }
+
+ switch (mState) {
+ case eStateUnshippedEntangled:
+ // Everything is fine here. We have messages because the other
+ // port populates our queue directly.
+ break;
+
+ case eStateEntangling:
+ // Everything is fine here as well. We have messages because the other
+ // port populated our queue directly when we were in the
+ // eStateUnshippedEntangled state.
+ break;
+
+ case eStateEntanglingForDisentangle:
+ // Here we don't want to ship messages because these messages must be
+ // delivered by the cloned version of this one. They will be sent in the
+ // SendDisentangle().
+ return;
+
+ case eStateEntanglingForClose:
+ // We still want to deliver messages if we are closing. These messages
+ // are here from the previous eStateUnshippedEntangled state.
+ break;
+
+ case eStateEntangled:
+ // This port is up and running.
+ break;
+
+ case eStateDisentangling:
+ // If we are in the process to disentangle the port, we cannot dispatch
+ // messages. They will be sent to the cloned version of this port via
+ // SendDisentangle();
+ return;
+
+ case eStateDisentangled:
+ MOZ_CRASH("This cannot happen.");
+ // It cannot happen because Disentangle should take off all the pending
+ // messages.
+ break;
+
+ case eStateDisentangledForClose:
+ // If we are here is because the port has been closed. We can still
+ // process the pending messages.
+ break;
+ }
+
+ RefPtr<SharedMessagePortMessage> data = mMessages.ElementAt(0);
+ mMessages.RemoveElementAt(0);
+
+ mPostMessageRunnable = new PostMessageRunnable(this, data);
+
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mPostMessageRunnable));
+}
+
+void
+MessagePort::Close()
+{
+ CloseInternal(true /* aSoftly */);
+}
+
+void
+MessagePort::CloseForced()
+{
+ CloseInternal(false /* aSoftly */);
+}
+
+void
+MessagePort::CloseInternal(bool aSoftly)
+{
+ // If we have some messages to send but we don't want a 'soft' close, we have
+ // to flush them now.
+ if (!aSoftly) {
+ mMessages.Clear();
+ }
+
+ if (mState == eStateUnshippedEntangled) {
+ MOZ_ASSERT(mUnshippedEntangledPort);
+
+ // This avoids loops.
+ RefPtr<MessagePort> port = Move(mUnshippedEntangledPort);
+ MOZ_ASSERT(mUnshippedEntangledPort == nullptr);
+
+ mState = eStateDisentangledForClose;
+ port->CloseInternal(aSoftly);
+
+ UpdateMustKeepAlive();
+ return;
+ }
+
+ // Not entangled yet, we have to wait.
+ if (mState == eStateEntangling) {
+ mState = eStateEntanglingForClose;
+ return;
+ }
+
+ // Not entangled but already cloned or closed
+ if (mState == eStateEntanglingForDisentangle ||
+ mState == eStateEntanglingForClose) {
+ return;
+ }
+
+ // Maybe we were already closing the port but softly. In this case we call
+ // UpdateMustKeepAlive() to consider the empty pending message queue.
+ if (mState == eStateDisentangledForClose && !aSoftly) {
+ UpdateMustKeepAlive();
+ 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 = eStateDisentangledForClose;
+
+ 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 == eStateEntanglingForDisentangle ||
+ mState == eStateEntanglingForClose);
+
+ State oldState = mState;
+ 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<RefPtr<SharedMessagePortMessage>> data;
+ if (NS_WARN_IF(!SharedMessagePortMessage::FromMessagesToSharedChild(aMessages,
+ data))) {
+ // OOM, we cannot continue.
+ return;
+ }
+
+ // If the next step is to close the port, we do it ignoring the received
+ // messages.
+ if (oldState == eStateEntanglingForClose) {
+ CloseForced();
+ return;
+ }
+
+ mMessages.AppendElements(data);
+
+ // We were waiting for the entangling callback in order to disentangle this
+ // port immediately after.
+ if (oldState == eStateEntanglingForDisentangle) {
+ StartDisentangling();
+ return;
+ }
+
+ Dispatch();
+}
+
+void
+MessagePort::StartDisentangling()
+{
+ MOZ_ASSERT(mActor);
+ MOZ_ASSERT(mState == eStateEntangled);
+
+ mState = eStateDisentangling;
+
+ // 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 ||
+ // This last step can happen only if Close() has been called
+ // manually. At this point SendClose() is sent but we can still
+ // receive something until the Closing request is processed.
+ mState == eStateDisentangledForClose);
+ MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
+
+ RemoveDocFromBFCache();
+
+ FallibleTArray<RefPtr<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();
+}
+
+void
+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;
+ }
+
+ // We already have a 'next step'. We have to consider this port as already
+ // cloned/closed/disentangled.
+ if (mState == eStateEntanglingForDisentangle ||
+ mState == eStateEntanglingForClose) {
+ return;
+ }
+
+ 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;
+ }
+
+ // Register this component to PBackground.
+ ConnectToPBackground();
+
+ mState = eStateEntanglingForDisentangle;
+ return;
+ }
+
+ // Not entangled yet, we have to wait.
+ if (mState == eStateEntangling) {
+ mState = eStateEntanglingForDisentangle;
+ return;
+ }
+
+ MOZ_ASSERT(mState == eStateEntangled);
+ StartDisentangling();
+}
+
+void
+MessagePort::Closed()
+{
+ if (mState >= eStateDisentangled) {
+ return;
+ }
+
+ mState = eStateDisentangledForClose;
+
+ 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 ||
+ mState == eStateEntanglingForDisentangle ||
+ mState == eStateEntanglingForClose);
+
+ 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 &&
+ mMessages.IsEmpty() &&
+ mIsKeptAlive) {
+ mIsKeptAlive = false;
+
+ // The DTOR of this WorkerHolder will release the worker for us.
+ mWorkerHolder = nullptr;
+
+ if (NS_IsMainThread()) {
+ nsCOMPtr<nsIObserverService> obs =
+ do_GetService("@mozilla.org/observer-service;1");
+ if (obs) {
+ obs->RemoveObserver(this, "inner-window-destroyed");
+ }
+ }
+
+ 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) {
+ CloseForced();
+ }
+
+ return NS_OK;
+}
+
+void
+MessagePort::RemoveDocFromBFCache()
+{
+ if (!NS_IsMainThread()) {
+ return;
+ }
+
+ nsPIDOMWindowInner* window = GetOwner();
+ if (!window) {
+ return;
+ }
+
+ nsIDocument* doc = window->GetExtantDoc();
+ if (!doc) {
+ return;
+ }
+
+ nsCOMPtr<nsIBFCacheEntry> bfCacheEntry = doc->GetBFCacheEntry();
+ if (!bfCacheEntry) {
+ return;
+ }
+
+ bfCacheEntry->RemoveFromBFCacheSync();
+}
+
+/* static */ void
+MessagePort::ForceClose(const MessagePortIdentifier& aIdentifier)
+{
+ ForceCloseHelper::ForceClose(aIdentifier);
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/messagechannel/MessagePort.h b/dom/messagechannel/MessagePort.h
new file mode 100644
index 000000000..afa909195
--- /dev/null
+++ b/dom/messagechannel/MessagePort.h
@@ -0,0 +1,192 @@
+/* -*- 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 "nsAutoPtr.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
+#include "nsTArray.h"
+
+#ifdef XP_WIN
+#undef PostMessage
+#endif
+
+class nsIGlobalObject;
+
+namespace mozilla {
+namespace dom {
+
+class MessagePortChild;
+class MessagePortIdentifier;
+class MessagePortMessage;
+class PostMessageRunnable;
+class SharedMessagePortMessage;
+
+namespace workers {
+class WorkerHolder;
+} // namespace workers
+
+class MessagePort final : public DOMEventTargetHelper
+ , public nsIIPCBackgroundChildCreateCallback
+ , public nsIObserver
+{
+ friend class PostMessageRunnable;
+
+public:
+ NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
+ NS_DECL_NSIOBSERVER
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort,
+ DOMEventTargetHelper)
+
+ static already_AddRefed<MessagePort>
+ Create(nsIGlobalObject* aGlobal, const nsID& aUUID,
+ const nsID& aDestinationUUID, ErrorResult& aRv);
+
+ static already_AddRefed<MessagePort>
+ Create(nsIGlobalObject* aGlobal,
+ const MessagePortIdentifier& aIdentifier,
+ ErrorResult& aRv);
+
+ // For IPC.
+ static void
+ ForceClose(const MessagePortIdentifier& aIdentifier);
+
+ virtual JSObject*
+ WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+ void
+ PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ const Optional<Sequence<JS::Value>>& aTransferable,
+ ErrorResult& aRv);
+
+ void Start();
+
+ void Close();
+
+ EventHandlerNonNull* GetOnmessage();
+
+ void SetOnmessage(EventHandlerNonNull* aCallback);
+
+ // Non WebIDL methods
+
+ void UnshippedEntangle(MessagePort* aEntangledPort);
+
+ void CloneAndDisentangle(MessagePortIdentifier& aIdentifier);
+
+ void CloseForced();
+
+ // These methods are useful for MessagePortChild
+
+ void Entangled(nsTArray<MessagePortMessage>& aMessages);
+ void MessagesReceived(nsTArray<MessagePortMessage>& aMessages);
+ void StopSendingDataConfirmed();
+ void Closed();
+
+private:
+ explicit MessagePort(nsIGlobalObject* aGlobal);
+ ~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 go in one
+ // of the following 2 steps. EntanglingForClose or ForDisentangle.
+ eStateEntangling,
+
+ // We are not fully entangled yet but are already disentangled.
+ eStateEntanglingForDisentangle,
+
+ // We are not fully entangled yet but are already closed.
+ eStateEntanglingForClose,
+
+ // 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,
+
+ // We are here if Close() has been called. We are disentangled but we can
+ // still send pending messages.
+ eStateDisentangledForClose
+ };
+
+ 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();
+
+ void CloseInternal(bool aSoftly);
+
+ // 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();
+
+ bool IsCertainlyAliveForCC() const override
+ {
+ return mIsKeptAlive;
+ }
+
+ nsAutoPtr<workers::WorkerHolder> mWorkerHolder;
+
+ RefPtr<PostMessageRunnable> mPostMessageRunnable;
+
+ RefPtr<MessagePortChild> mActor;
+
+ RefPtr<MessagePort> mUnshippedEntangledPort;
+
+ nsTArray<RefPtr<SharedMessagePortMessage>> mMessages;
+ nsTArray<RefPtr<SharedMessagePortMessage>> mMessagesForTheOtherPort;
+
+ nsAutoPtr<MessagePortIdentifier> mIdentifier;
+
+ uint64_t mInnerID;
+
+ State mState;
+
+ bool mMessageQueueEnabled;
+
+ bool mIsKeptAlive;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_MessagePort_h
diff --git a/dom/messagechannel/MessagePortChild.cpp b/dom/messagechannel/MessagePortChild.cpp
new file mode 100644
index 000000000..4560b3c4d
--- /dev/null
+++ b/dom/messagechannel/MessagePortChild.cpp
@@ -0,0 +1,51 @@
+/* -*- 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)
+{
+ if (mPort) {
+ mPort->Entangled(aMessages);
+ }
+ return true;
+}
+
+bool
+MessagePortChild::RecvReceiveData(nsTArray<MessagePortMessage>&& aMessages)
+{
+ if (mPort) {
+ mPort->MessagesReceived(aMessages);
+ }
+ return true;
+}
+
+void
+MessagePortChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ if (mPort) {
+ mPort->Closed();
+ MOZ_ASSERT(!mPort);
+ }
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/messagechannel/MessagePortChild.h b/dom/messagechannel/MessagePortChild.h
new file mode 100644
index 000000000..4777bc64e
--- /dev/null
+++ b/dom/messagechannel/MessagePortChild.h
@@ -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() : mPort(nullptr) {}
+
+ 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;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_MessagePortChild_h
diff --git a/dom/messagechannel/MessagePortParent.cpp b/dom/messagechannel/MessagePortParent.cpp
new file mode 100644
index 000000000..bf68c2630
--- /dev/null
+++ b/dom/messagechannel/MessagePortParent.cpp
@@ -0,0 +1,177 @@
+/* -*- 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<RefPtr<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<RefPtr<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.
+ RefPtr<MessagePortService> kungFuDeathGrip = mService;
+ kungFuDeathGrip->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;
+}
+
+/* static */ bool
+MessagePortParent::ForceClose(const nsID& aUUID,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID)
+{
+ MessagePortService* service = MessagePortService::Get();
+ if (!service) {
+ NS_WARNING("The service must exist if we want to close an existing MessagePort.");
+ return false;
+ }
+
+ return service->ForceClose(aUUID, aDestinationUUID, aSequenceID);
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/messagechannel/MessagePortParent.h b/dom/messagechannel/MessagePortParent.h
new file mode 100644
index 000000000..4cbebe29b
--- /dev/null
+++ b/dom/messagechannel/MessagePortParent.h
@@ -0,0 +1,65 @@
+/* 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;
+ }
+
+ static bool ForceClose(const nsID& aUUID,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID);
+
+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;
+
+ RefPtr<MessagePortService> mService;
+ const nsID mUUID;
+ bool mEntangled;
+ bool mCanSendData;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_MessagePortParent_h
diff --git a/dom/messagechannel/MessagePortService.cpp b/dom/messagechannel/MessagePortService.cpp
new file mode 100644
index 000000000..a2d495d77
--- /dev/null
+++ b/dom/messagechannel/MessagePortService.cpp
@@ -0,0 +1,409 @@
+/* -*- 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 "MessagePortService.h"
+#include "MessagePortParent.h"
+#include "SharedMessagePortMessage.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Unused.h"
+#include "nsTArray.h"
+
+using mozilla::ipc::AssertIsOnBackgroundThread;
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+StaticRefPtr<MessagePortService> gInstance;
+
+void
+AssertIsInMainProcess()
+{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+}
+
+} // namespace
+
+class MessagePortService::MessagePortServiceData final
+{
+public:
+ explicit MessagePortServiceData(const nsID& aDestinationUUID)
+ : mDestinationUUID(aDestinationUUID)
+ , mSequenceID(1)
+ , mParent(nullptr)
+ // By default we don't know the next parent.
+ , mWaitingForNewParent(true)
+ , mNextStepCloseAll(false)
+ {
+ 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<RefPtr<SharedMessagePortMessage>> mMessages;
+
+ bool mWaitingForNewParent;
+ bool mNextStepCloseAll;
+};
+
+/* static */ MessagePortService*
+MessagePortService::Get()
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ return gInstance;
+}
+
+/* static */ MessagePortService*
+MessagePortService::GetOrCreate()
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ 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!");
+ CloseAll(aParent->ID());
+ return false;
+ }
+
+ if (aSequenceID < data->mSequenceID) {
+ MOZ_ASSERT(false, "Invalid sequence ID!");
+ CloseAll(aParent->ID());
+ return false;
+ }
+
+ if (aSequenceID == data->mSequenceID) {
+ if (data->mParent) {
+ MOZ_ASSERT(false, "Two ports cannot have the same sequenceID.");
+ CloseAll(aParent->ID());
+ return false;
+ }
+
+ // We activate this port, sending all the messages.
+ data->mParent = aParent;
+ data->mWaitingForNewParent = false;
+ FallibleTArray<MessagePortMessage> array;
+ if (!SharedMessagePortMessage::FromSharedToMessagesParent(aParent,
+ data->mMessages,
+ array)) {
+ CloseAll(aParent->ID());
+ return false;
+ }
+
+ data->mMessages.Clear();
+
+ // We can entangle the port.
+ if (!aParent->Entangled(array)) {
+ CloseAll(aParent->ID());
+ return false;
+ }
+
+ // If we were waiting for this parent in order to close this channel, this
+ // is the time to do it.
+ if (data->mNextStepCloseAll) {
+ CloseAll(aParent->ID());
+ }
+
+ return true;
+ }
+
+ // 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) {
+ CloseAll(aParent->ID());
+ return false;
+ }
+
+ nextParent->mSequenceID = aSequenceID;
+ nextParent->mParent = aParent;
+
+ return true;
+}
+
+bool
+MessagePortService::DisentanglePort(
+ MessagePortParent* aParent,
+ FallibleTArray<RefPtr<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->mWaitingForNewParent = true;
+ 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;
+}
+
+void
+MessagePortService::CloseAll(const nsID& aUUID, bool aForced)
+{
+ 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;
+
+ // If we have informations about the other port and that port has some
+ // pending messages to deliver but the parent has not processed them yet,
+ // because its entangling request didn't arrive yet), we cannot close this
+ // channel.
+ MessagePortServiceData* destinationData;
+ if (!aForced &&
+ mPorts.Get(destinationUUID, &destinationData) &&
+ !destinationData->mMessages.IsEmpty() &&
+ destinationData->mWaitingForNewParent) {
+ MOZ_ASSERT(!destinationData->mNextStepCloseAll);
+ destinationData->mNextStepCloseAll = true;
+ return;
+ }
+
+ mPorts.Remove(aUUID);
+
+ CloseAll(destinationUUID, aForced);
+
+ // CloseAll calls itself recursively and it can happen that it deletes
+ // itself. Before continuing we must check if we are still alive.
+ if (!gInstance) {
+ return;
+ }
+
+#ifdef DEBUG
+ for (auto iter = mPorts.Iter(); !iter.Done(); iter.Next()) {
+ MOZ_ASSERT(!aUUID.Equals(iter.Key()));
+ }
+#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<RefPtr<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());
+}
+
+bool
+MessagePortService::ForceClose(const nsID& aUUID,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID)
+{
+ MessagePortServiceData* data;
+ if (!mPorts.Get(aUUID, &data)) {
+ NS_WARNING("Unknown MessagePort in ForceClose()");
+ return true;
+ }
+
+ if (!data->mDestinationUUID.Equals(aDestinationUUID) ||
+ data->mSequenceID != aSequenceID) {
+ NS_WARNING("DestinationUUID and/or sequenceID do not match.");
+ return false;
+ }
+
+ CloseAll(aUUID, true);
+ return true;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/messagechannel/MessagePortService.h b/dom/messagechannel/MessagePortService.h
new file mode 100644
index 000000000..eedaa40dd
--- /dev/null
+++ b/dom/messagechannel/MessagePortService.h
@@ -0,0 +1,60 @@
+/* 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* Get();
+ static MessagePortService* GetOrCreate();
+
+ bool RequestEntangling(MessagePortParent* aParent,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID);
+
+ bool DisentanglePort(
+ MessagePortParent* aParent,
+ FallibleTArray<RefPtr<SharedMessagePortMessage>>& aMessages);
+
+ bool ClosePort(MessagePortParent* aParent);
+
+ bool PostMessages(
+ MessagePortParent* aParent,
+ FallibleTArray<RefPtr<SharedMessagePortMessage>>& aMessages);
+
+ void ParentDestroy(MessagePortParent* aParent);
+
+ bool ForceClose(const nsID& aUUID,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID);
+
+private:
+ ~MessagePortService() {}
+
+ void CloseAll(const nsID& aUUID, bool aForced = false);
+ void MaybeShutdown();
+
+ class MessagePortServiceData;
+
+ nsClassHashtable<nsIDHashKey, MessagePortServiceData> mPorts;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_MessagePortService_h
diff --git a/dom/messagechannel/PMessagePort.ipdl b/dom/messagechannel/PMessagePort.ipdl
new file mode 100644
index 000000000..db4cb5a6f
--- /dev/null
+++ b/dom/messagechannel/PMessagePort.ipdl
@@ -0,0 +1,58 @@
+/* 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;
+
+include DOMTypes;
+
+using struct mozilla::SerializedStructuredCloneBuffer
+ from "ipc/IPCMessageUtils.h";
+
+namespace mozilla {
+namespace dom {
+
+
+struct MessagePortMessage
+{
+ SerializedStructuredCloneBuffer data;
+ PBlob[] blobs;
+ MessagePortIdentifier[] transferredPorts;
+};
+
+// 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:
+ async PostMessages(MessagePortMessage[] messages);
+ async Disentangle(MessagePortMessage[] messages);
+ async StopSendingData();
+ async Close();
+
+child:
+ async Entangled(MessagePortMessage[] messages);
+ async ReceiveData(MessagePortMessage[] messages);
+ async StopSendingDataConfirmed();
+
+ async __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
+
diff --git a/dom/messagechannel/SharedMessagePortMessage.cpp b/dom/messagechannel/SharedMessagePortMessage.cpp
new file mode 100644
index 000000000..f7ace9dd5
--- /dev/null
+++ b/dom/messagechannel/SharedMessagePortMessage.cpp
@@ -0,0 +1,176 @@
+/* -*- 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 {
+
+/* static */ void
+SharedMessagePortMessage::FromSharedToMessagesChild(
+ MessagePortChild* aActor,
+ const nsTArray<RefPtr<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();
+ data->mBuffer->abandon();
+ data->mBuffer->steal(&message->data().data);
+
+ const nsTArray<RefPtr<BlobImpl>>& blobImpls = data->BlobImpls();
+ 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->PortIdentifiers());
+ }
+}
+
+/* static */ bool
+SharedMessagePortMessage::FromMessagesToSharedChild(
+ nsTArray<MessagePortMessage>& aArray,
+ FallibleTArray<RefPtr<SharedMessagePortMessage>>& aData)
+{
+ MOZ_ASSERT(aData.IsEmpty());
+
+ if (NS_WARN_IF(!aData.SetCapacity(aArray.Length(), mozilla::fallible))) {
+ return false;
+ }
+
+ for (auto& message : aArray) {
+ RefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
+
+ data->mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>(
+ JS::StructuredCloneScope::DifferentProcess, nullptr, nullptr);
+ data->mBuffer->adopt(Move(message.data().data), JS_STRUCTURED_CLONE_VERSION,
+ &StructuredCloneHolder::sCallbacks, data.get());
+
+ const nsTArray<PBlobChild*>& blobs = message.blobsChild();
+ if (!blobs.IsEmpty()) {
+ data->BlobImpls().SetCapacity(blobs.Length());
+
+ for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
+ RefPtr<BlobImpl> impl =
+ static_cast<BlobChild*>(blobs[i])->GetBlobImpl();
+ data->BlobImpls().AppendElement(impl);
+ }
+ }
+
+ data->PortIdentifiers().AppendElements(message.transferredPorts());
+
+ if (!aData.AppendElement(data, mozilla::fallible)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* static */ bool
+SharedMessagePortMessage::FromSharedToMessagesParent(
+ MessagePortParent* aActor,
+ const nsTArray<RefPtr<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);
+ data->mBuffer->abandon();
+ data->mBuffer->steal(&message->data().data);
+
+ const nsTArray<RefPtr<BlobImpl>>& blobImpls = data->BlobImpls();
+ 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->PortIdentifiers());
+ }
+
+ return true;
+}
+
+/* static */ bool
+SharedMessagePortMessage::FromMessagesToSharedParent(
+ nsTArray<MessagePortMessage>& aArray,
+ FallibleTArray<RefPtr<SharedMessagePortMessage>>& aData)
+{
+ MOZ_ASSERT(aData.IsEmpty());
+
+ if (NS_WARN_IF(!aData.SetCapacity(aArray.Length(), mozilla::fallible))) {
+ return false;
+ }
+
+ for (auto& message : aArray) {
+ RefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
+
+ data->mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>(
+ JS::StructuredCloneScope::DifferentProcess, nullptr, nullptr);
+ data->mBuffer->adopt(Move(message.data().data), JS_STRUCTURED_CLONE_VERSION,
+ &StructuredCloneHolder::sCallbacks, data.get());
+
+ const nsTArray<PBlobParent*>& blobs = message.blobsParent();
+ if (!blobs.IsEmpty()) {
+ data->BlobImpls().SetCapacity(blobs.Length());
+
+ for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
+ RefPtr<BlobImpl> impl =
+ static_cast<BlobParent*>(blobs[i])->GetBlobImpl();
+ data->BlobImpls().AppendElement(impl);
+ }
+ }
+
+ data->PortIdentifiers().AppendElements(message.transferredPorts());
+
+ if (!aData.AppendElement(data, mozilla::fallible)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/messagechannel/SharedMessagePortMessage.h b/dom/messagechannel/SharedMessagePortMessage.h
new file mode 100644
index 000000000..7c3214c81
--- /dev/null
+++ b/dom/messagechannel/SharedMessagePortMessage.h
@@ -0,0 +1,57 @@
+/* -*- 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 "mozilla/dom/StructuredCloneHolder.h"
+
+namespace mozilla {
+namespace dom {
+
+class MessagePortChild;
+class MessagePortMessage;
+class MessagePortParent;
+
+class SharedMessagePortMessage final : public StructuredCloneHolder
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(SharedMessagePortMessage)
+
+ SharedMessagePortMessage()
+ : StructuredCloneHolder(CloningSupported, TransferringSupported,
+ StructuredCloneScope::DifferentProcess)
+ {}
+
+ static void
+ FromSharedToMessagesChild(
+ MessagePortChild* aActor,
+ const nsTArray<RefPtr<SharedMessagePortMessage>>& aData,
+ nsTArray<MessagePortMessage>& aArray);
+
+ static bool
+ FromMessagesToSharedChild(
+ nsTArray<MessagePortMessage>& aArray,
+ FallibleTArray<RefPtr<SharedMessagePortMessage>>& aData);
+
+ static bool
+ FromSharedToMessagesParent(
+ MessagePortParent* aActor,
+ const nsTArray<RefPtr<SharedMessagePortMessage>>& aData,
+ FallibleTArray<MessagePortMessage>& aArray);
+
+ static bool
+ FromMessagesToSharedParent(
+ nsTArray<MessagePortMessage>& aArray,
+ FallibleTArray<RefPtr<SharedMessagePortMessage>>& aData);
+
+private:
+ ~SharedMessagePortMessage() {}
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_SharedMessagePortMessage_h
diff --git a/dom/messagechannel/moz.build b/dom/messagechannel/moz.build
new file mode 100644
index 000000000..679d7eeb8
--- /dev/null
+++ b/dom/messagechannel/moz.build
@@ -0,0 +1,37 @@
+# -*- Mode: python; 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',
+ 'MessagePortParent.h',
+]
+
+UNIFIED_SOURCES += [
+ 'MessageChannel.cpp',
+ 'MessagePort.cpp',
+ 'MessagePortChild.cpp',
+ 'MessagePortParent.cpp',
+ 'MessagePortService.cpp',
+ 'SharedMessagePortMessage.cpp',
+]
+
+IPDL_SOURCES += [
+ 'PMessagePort.ipdl',
+]
+
+LOCAL_INCLUDES += [
+ '../base',
+ '../events',
+ '../workers',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
diff --git a/dom/messagechannel/tests/chrome.ini b/dom/messagechannel/tests/chrome.ini
new file mode 100644
index 000000000..8613309c5
--- /dev/null
+++ b/dom/messagechannel/tests/chrome.ini
@@ -0,0 +1,12 @@
+[DEFAULT]
+support-files =
+ iframe_messageChannel_chrome.html
+ mm_messageChannelParent.xul
+ mm_messageChannelParentNotRemote.xul
+ mm_messageChannelParent.js
+ mm_messageChannel.js
+
+[test_messageChannel.xul]
+[test_messageChannelWithMessageManager.xul]
+skip-if = os == 'android'
+[test_messageChannelWithMessageManagerNotRemote.xul]
diff --git a/dom/messagechannel/tests/iframe_messageChannel_chrome.html b/dom/messagechannel/tests/iframe_messageChannel_chrome.html
new file mode 100644
index 000000000..1c9e4d3e1
--- /dev/null
+++ b/dom/messagechannel/tests/iframe_messageChannel_chrome.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+ <script type="application/javascript">
+
+window.addEventListener('message', receiveMessage, false);
+function receiveMessage(evt) {
+ evt.data.postMessage("Hello world");
+}
+ </script>
+</body>
diff --git a/dom/messagechannel/tests/iframe_messageChannel_cloning.html b/dom/messagechannel/tests/iframe_messageChannel_cloning.html
new file mode 100644
index 000000000..de03cead9
--- /dev/null
+++ b/dom/messagechannel/tests/iframe_messageChannel_cloning.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+ <script type="application/javascript">
+
+ function ok(a, msg) {
+ window.parent.postMessage({ status: a ? "OK" : "KO", message: msg }, "*");
+ }
+
+ window.addEventListener('message', receiveMessage, false);
+ function receiveMessage(evt) {
+ ok (evt.data, "Data received");
+ ok (evt.data.port instanceof MessagePort, "Data contains a MessagePort");
+
+ var a = new MessageChannel();
+ window.parent.postMessage({ status: "FINISH", port: a.port2 }, '*', [a.port2]);
+ }
+
+ </script>
+</body>
+</html>
+
diff --git a/dom/messagechannel/tests/iframe_messageChannel_pingpong.html b/dom/messagechannel/tests/iframe_messageChannel_pingpong.html
new file mode 100644
index 000000000..d83d75c48
--- /dev/null
+++ b/dom/messagechannel/tests/iframe_messageChannel_pingpong.html
@@ -0,0 +1,33 @@
+<!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) {
+ if (evt.data.type == 'PORT') {
+ var port = evt.data.port;
+ var counter = 0;
+ port.onmessage = function(evt) {
+ if (counter++ == 0) {
+ ok(!(evt.data % 2), "The number " + evt.data + " has been received correctly by the iframe");
+
+ window.parent.postMessage({ type: 'PORT', port: port }, '*', [port]);
+ }
+ else {
+ ok(false, "Wrong message!");
+ }
+ }
+ } else {
+ ok(false, "Unknown message");
+ }
+ }
+
+ </script>
+</body>
+</html>
+
diff --git a/dom/messagechannel/tests/iframe_messageChannel_post.html b/dom/messagechannel/tests/iframe_messageChannel_post.html
new file mode 100644
index 000000000..e551dd61c
--- /dev/null
+++ b/dom/messagechannel/tests/iframe_messageChannel_post.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+ <script type="application/javascript">
+
+ var port;
+
+ window.addEventListener('message', receiveMessage, false);
+ function receiveMessage(evt) {
+ port = evt.data.port;
+
+ port.addEventListener('message', receivePostMessage, false);
+ function receivePostMessage(evt) {
+ port.postMessage(evt.data);
+ }
+ port.start();
+
+ window.parent.postMessage({ status: "READY" }, '*');
+ }
+
+ </script>
+</body>
+</html>
+
diff --git a/dom/messagechannel/tests/iframe_messageChannel_sharedWorker2.html b/dom/messagechannel/tests/iframe_messageChannel_sharedWorker2.html
new file mode 100644
index 000000000..a693cba22
--- /dev/null
+++ b/dom/messagechannel/tests/iframe_messageChannel_sharedWorker2.html
@@ -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>
+
diff --git a/dom/messagechannel/tests/iframe_messageChannel_transferable.html b/dom/messagechannel/tests/iframe_messageChannel_transferable.html
new file mode 100644
index 000000000..108edeb7e
--- /dev/null
+++ b/dom/messagechannel/tests/iframe_messageChannel_transferable.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+ <script type="application/javascript">
+
+ function ok(what, msg) {
+ window.parent.postMessage({type: what ? 'OK' : 'KO', msg: msg }, '*');
+ }
+
+ window.addEventListener('message', receiveMessage, false);
+ function receiveMessage(evt) {
+ ok(evt.ports.length == 1, "Port transferred!");
+
+ var a = new MessageChannel();
+ ok(a, "MessageChannel created");
+ evt.ports[0].postMessage('hello world!', [a.port2]);
+ a.port1.onmessage = function(evt) {
+ evt.target.postMessage(evt.data);
+ }
+ }
+
+ </script>
+</body>
+</html>
+
+
diff --git a/dom/messagechannel/tests/mm_messageChannel.js b/dom/messagechannel/tests/mm_messageChannel.js
new file mode 100644
index 000000000..914d800fd
--- /dev/null
+++ b/dom/messagechannel/tests/mm_messageChannel.js
@@ -0,0 +1,76 @@
+function debug(msg) {
+ dump("[mmMessageChannelChild]" + msg + "\n");
+}
+
+/**
+ * Preparation Test
+ */
+let port;
+let toString = Object.prototype.toString;
+
+(function prepare() {
+ debug("Script loaded.");
+ addTestReceiver();
+ sendAsyncMessage("mmMessagePort:finishScriptLoad", {}, {});
+})();
+
+function ok(condition, message) {
+ debug('condition: ' + condition + ', ' + message + '\n');
+ if (!condition) {
+ sendAsyncMessage("mmMessagePort:fail", { message: message });
+ throw 'failed check: ' + message;
+ }
+}
+
+function is(a, b, message) {
+ ok(a===b, message);
+}
+
+
+/**
+ * Testing codes.
+ */
+function addTestReceiver() {
+ addMessageListener("BasicTest:PortCreated", basicTest);
+ addMessageListener("CloseTest:PortCreated", closeTest);
+ addMessageListener("EmptyTest:PortCreated", emptyTest);
+ addMessageListener("NotTransferableTest:PortCreated", notTransferableTest);
+}
+
+function basicTest(msg) {
+ port = msg.ports[0];
+ is(toString.call(port), "[object MessagePort]", "created MessagePort.");
+
+ port.onmessage = (msg) => {
+ is(msg.data, "BasicTest:StartTest", "Replied message is correct.");
+ port.postMessage("BasicTest:TestOK");
+ };
+
+ sendAsyncMessage("BasicTest:FinishPrepare", { message: "OK" });
+}
+
+function closeTest(msg) {
+ port = msg.ports[0];
+ is(toString.call(port), "[object MessagePort]", "created MessagePort.");
+
+ port.onmessage = (msg) => {
+ ok(msg.data,"CloseTest:StartTest", "Replied message is correct.");
+ port.postMessage("CloseTest:TestOK");
+ };
+
+ port.close();
+
+ sendAsyncMessage("CloseTest:FinishPrepare", { message: "OK"});
+}
+
+function emptyTest(msg) {
+ let portSize = msg.ports.length;
+ is(portSize, 0, "transfered port size is zero.");
+
+ sendAsyncMessage("EmptyTest:FinishPrepare", { message: "OK"});
+}
+
+function notTransferableTest(msg) {
+ sendAsyncMessage("NotTransferableTest:FinishPrepare", {message: "OK"});
+}
+
diff --git a/dom/messagechannel/tests/mm_messageChannelParent.js b/dom/messagechannel/tests/mm_messageChannelParent.js
new file mode 100644
index 000000000..a5ffa2caf
--- /dev/null
+++ b/dom/messagechannel/tests/mm_messageChannelParent.js
@@ -0,0 +1,143 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+let port;
+let mm;
+
+function info(message) {
+ return opener.wrappedJSObject.info(message);
+}
+
+function ok(condition, message) {
+ return opener.wrappedJSObject.ok(condition, message);
+}
+
+function is(v1, v2, message) {
+ return opener.wrappedJSObject.is(v1, v2, message);
+}
+
+function todo_is(v1, v2, message) {
+ return opener.wrappedJSObject.todo_is(v1, v2, message);
+}
+
+function finish() {
+ opener.setTimeout("done()", 0);
+ window.close();
+}
+
+function debug(msg) {
+ dump("[mmMessageChannelParent]" + msg + "\n");
+}
+
+let tests = [ basic_test,
+ close_test,
+ empty_transferable,
+ not_transferable];
+
+// Test Routine
+function run_tests() {
+ let test = tests.shift();
+ if (test === undefined) {
+ finish();
+ return;
+ }
+
+ test(function() {
+ setTimeout(run_tests,0);
+ });
+}
+
+// Basic communication test.
+function basic_test(finish) {
+ ok(mm, "basic_test");
+
+ let finishPrepare = (msg) => {
+ is(msg.data.message, "OK", "");
+ ok(port, "");
+ port.onmessage = (msg) => {
+ is(msg.data, "BasicTest:TestOK", "");
+ finish();
+ }
+ port.postMessage("BasicTest:StartTest");
+ mm.removeMessageListener("BasicTest:FinishPrepare", finishPrepare);
+ };
+
+ let channel = new MessageChannel();
+ port = channel.port2;
+ mm.addMessageListener("BasicTest:FinishPrepare", finishPrepare);
+ mm.sendAsyncMessage("BasicTest:PortCreated", {}, {}, undefined, [channel.port1]);
+}
+
+// Communicate with closed port.
+function close_test(finish) {
+ ok(mm, "close_test");
+
+ let finishPrepare = (msg) => {
+ is(msg.data.message, "OK", "");
+ ok(port, "");
+
+ port.onmessage = (msg) => {
+ ok(false, "Port is alive.");
+ finish();
+ }
+
+ port.postMessage("CloseTest:StartTest");
+ mm.removeMessageListener("CloseTest:FinishPrepare", finishPrepare);
+ finish();
+ }
+
+ let channel = new MessageChannel();
+ port = channel.port2;
+ mm.addMessageListener("CloseTest:FinishPrepare", finishPrepare);
+ mm.sendAsyncMessage("CloseTest:PortCreated", {}, {}, undefined, [channel.port1]);
+}
+
+// Empty transferable object
+function empty_transferable(finish) {
+ ok(mm, "empty_transferable");
+
+ let finishPrepare = (msg) => {
+ ok(true, "Same basic test.");
+ mm.removeMessageListener("EmptyTest:FinishPrepare", finishPrepare);
+ finish();
+ };
+
+ mm.addMessageListener("EmptyTest:FinishPrepare", finishPrepare);
+ mm.sendAsyncMessage("EmptyTest:PortCreated", {}, {}, undefined, []);
+}
+
+// Not transferable object.
+function not_transferable(finish) {
+ ok(mm, "not_transferable");
+
+ let finishPrepare = (msg) => {
+ ok(true, "Same basic test.");
+ finish();
+ }
+
+ mm.addMessageListener("NotTransferableTest:FinishPrepare", finishPrepare);
+ mm.sendAsyncMessage("NotTransferableTest:PortCreated", {}, {}, undefined, [""]);
+}
+
+/*
+ * Test preparation
+ */
+function finishLoad(msg) {
+ run_tests();
+}
+
+function prepare_test() {
+ debug("start run_tests()");
+ var node = document.getElementById('messagechannel_remote');
+ mm = node.messageManager; //Services.ppmm.getChildAt(1);
+ ok(mm, "created MessageManager.")
+
+ mm.addMessageListener("mmMessagePort:finishScriptLoad", finishLoad);
+ mm.addMessageListener("mmMessagePort:fail", failed_test);
+ //mm.loadProcessScript("chrome://mochitests/content/chrome/dom/messagechannel/tests/mm_messageChannel.js", true);
+ mm.loadFrameScript("chrome://mochitests/content/chrome/dom/messagechannel/tests/mm_messageChannel.js", true);
+ ok(true, "Loaded");
+}
+
+function failed_test() {
+ debug("failed test in child process");
+ ok(false, "");
+}
diff --git a/dom/messagechannel/tests/mm_messageChannelParent.xul b/dom/messagechannel/tests/mm_messageChannelParent.xul
new file mode 100644
index 000000000..5f5c5ae5b
--- /dev/null
+++ b/dom/messagechannel/tests/mm_messageChannelParent.xul
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window title="Test MessageChannel API with nsFrameMessageManager(bug 1174624)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="prepare_test()">
+
+ <!-- test code goes here -->
+ <script type="application/javascript"
+ src="chrome://mochitests/content/chrome/dom/messagechannel/tests/mm_messageChannelParent.js"></script>
+ <browser type="content" src="about:blank" id="messagechannel_remote" remote="true"/>
+</window>
diff --git a/dom/messagechannel/tests/mm_messageChannelParentNotRemote.xul b/dom/messagechannel/tests/mm_messageChannelParentNotRemote.xul
new file mode 100644
index 000000000..cb3b55dea
--- /dev/null
+++ b/dom/messagechannel/tests/mm_messageChannelParentNotRemote.xul
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window title="Test MessageChannel API with nsFrameMessageManager(bug 1174624)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="prepare_test()">
+
+ <!-- test code goes here -->
+ <script type="application/javascript"
+ src="chrome://mochitests/content/chrome/dom/messagechannel/tests/mm_messageChannelParent.js"></script>
+ <browser type="content" src="about:blank" id="messagechannel_remote"/>
+</window>
diff --git a/dom/messagechannel/tests/mochitest.ini b/dom/messagechannel/tests/mochitest.ini
new file mode 100644
index 000000000..67d18d06f
--- /dev/null
+++ b/dom/messagechannel/tests/mochitest.ini
@@ -0,0 +1,28 @@
+[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_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]
+[test_messageChannel_forceClose.html]
+[test_messageChannel_bug1178076.html]
+[test_messageChannel_bug1224825.html]
+[test_messageChannel_worker_forceClose.html]
diff --git a/dom/messagechannel/tests/moz.build b/dom/messagechannel/tests/moz.build
new file mode 100644
index 000000000..42fcbb69e
--- /dev/null
+++ b/dom/messagechannel/tests/moz.build
@@ -0,0 +1,8 @@
+# -*- Mode: python; 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']
diff --git a/dom/messagechannel/tests/sharedWorker2_messageChannel.js b/dom/messagechannel/tests/sharedWorker2_messageChannel.js
new file mode 100644
index 000000000..8cc98aa20
--- /dev/null
+++ b/dom/messagechannel/tests/sharedWorker2_messageChannel.js
@@ -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]]);
+}
diff --git a/dom/messagechannel/tests/sharedWorker_messageChannel.js b/dom/messagechannel/tests/sharedWorker_messageChannel.js
new file mode 100644
index 000000000..4b24642f9
--- /dev/null
+++ b/dom/messagechannel/tests/sharedWorker_messageChannel.js
@@ -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);
+ }
+}
diff --git a/dom/messagechannel/tests/test_messageChannel.html b/dom/messagechannel/tests/test_messageChannel.html
new file mode 100644
index 000000000..3e761e299
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel.html
@@ -0,0 +1,43 @@
+
+<!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">
+
+ /** Test for Bug 677638 **/
+ 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");
+ });
+
+ </script>
+</body>
+</html>
diff --git a/dom/messagechannel/tests/test_messageChannel.xul b/dom/messagechannel/tests/test_messageChannel.xul
new file mode 100644
index 000000000..3d8e3485c
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel.xul
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window title="Test for MessageChannel API"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml" id="body">
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ ok("MessageChannel" in window, "Should MessageChannel exist?");
+
+ var channel = new MessageChannel();
+ ok(channel, "MessageChannel is created");
+
+ channel.port1.onmessage = function(evt) {
+ ok(true, "message received!");
+ SimpleTest.finish();
+ }
+
+ var ifr = document.createElement('browser');
+ ifr.setAttribute("src", "iframe_messageChannel_chrome.html");
+ ifr.setAttribute("flex", "1");
+ ifr.addEventListener('load', function() {
+ ifr.contentWindow.postMessage(channel.port2, '*', [channel.port2]);
+ });
+
+ var body = document.getElementById("body");
+ body.appendChild(ifr);
+
+ SimpleTest.waitForExplicitFinish();
+
+ ]]></script>
+</window>
diff --git a/dom/messagechannel/tests/test_messageChannelWithMessageManager.xul b/dom/messagechannel/tests/test_messageChannelWithMessageManager.xul
new file mode 100644
index 000000000..11a1fc585
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannelWithMessageManager.xul
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Test MessageChannel API with nsFrameMessageManager(bug 1174624)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ SimpleTest.waitForExplicitFinish();
+
+ function done() {
+ info("done called");
+ SimpleTest.finish();
+ }
+
+ addLoadEvent(function() {
+ window.open("mm_messageChannelParent.xul", "", "chrome");
+ });
+ ]]></script>
+</window>
diff --git a/dom/messagechannel/tests/test_messageChannelWithMessageManagerNotRemote.xul b/dom/messagechannel/tests/test_messageChannelWithMessageManagerNotRemote.xul
new file mode 100644
index 000000000..24ea7ed0d
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannelWithMessageManagerNotRemote.xul
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Test MessageChannel API with nsFrameMessageManager(bug 1174624)"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ SimpleTest.waitForExplicitFinish();
+
+ function done() {
+ info("done called");
+ SimpleTest.finish();
+ }
+
+ addLoadEvent(function() {
+ window.open("mm_messageChannelParentNotRemote.xul", "", "chrome");
+ });
+ ]]></script>
+</window>
diff --git a/dom/messagechannel/tests/test_messageChannel_any.html b/dom/messagechannel/tests/test_messageChannel_any.html
new file mode 100644
index 000000000..845f5c734
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_any.html
@@ -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();
+runTest();
+ </script>
+</body>
+</html>
diff --git a/dom/messagechannel/tests/test_messageChannel_bug1178076.html b/dom/messagechannel/tests/test_messageChannel_bug1178076.html
new file mode 100644
index 000000000..bf7077250
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_bug1178076.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1178076
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1178076</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=1178076">Mozilla Bug 1178076</a>
+<div id="content"></div>
+<pre id="test">
+</pre>
+ <script type="application/javascript">
+
+ function runTest() {
+ onmessage = function(e) {
+ ok(e.ports.length, 1, "A port has been received!");
+ var port = e.ports[0];
+ ok(port instanceof MessagePort, "This is a port.");
+ SimpleTest.finish();
+ }
+
+ // In this test we want to see if we leak a neutered port closing port1
+ // and sending port2 to the same window. This operation doesn't involve IPC.
+ var mc = new MessageChannel();
+ mc.port1.close();
+ postMessage(42, '*', [mc.port2]);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ runTest();
+ </script>
+</body>
+</html>
diff --git a/dom/messagechannel/tests/test_messageChannel_bug1224825.html b/dom/messagechannel/tests/test_messageChannel_bug1224825.html
new file mode 100644
index 000000000..f50115547
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_bug1224825.html
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1224825
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1224825</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=1224825">Mozilla Bug 1224825</a>
+<div id="content"></div>
+<pre id="test">
+</pre>
+ <script type="application/javascript">
+
+var MAX = 100;
+
+function test_fullDeliveredMessages() {
+ var worker = new Worker('data:javascript,onmessage = function(e) { e.ports[0].onmessage = function(evt) { postMessage(evt.data);}}');
+
+ var count = 0;
+ worker.onmessage = function(e) {
+ is(e.data, count, "Correct value expected!");
+ ok(count < MAX,"No count > MAX messages!");
+ if (++count == MAX) {
+
+ SimpleTest.requestFlakyTimeout("Testing an event not happening");
+ setTimeout(function() {
+ runTests();
+ }, 200);
+
+ info("All the messages correctly received");
+ }
+ }
+
+ var mc = new MessageChannel();
+ worker.postMessage(42, [mc.port2]);
+
+ for (var i = 0; i < MAX; ++i) {
+ mc.port1.postMessage(i);
+ }
+
+ mc.port1.close();
+
+ for (var i = 0; i < MAX * 2; ++i) {
+ mc.port1.postMessage(i);
+ }
+}
+
+function test_closeInBetween() {
+ var mc = new MessageChannel();
+
+ for (var i = 0; i < MAX; ++i) {
+ mc.port1.postMessage(i);
+ }
+
+ mc.port1.onmessage = function(e) {
+ ok (e.data < MAX/2, "Correct message received from port1:" + e.data);
+ }
+
+ mc.port2.onmessage = function(e) {
+ ok (e.data < MAX, "Correct message received from port2:" + e.data);
+ if (e.data == MAX/2) {
+ mc.port2.close();
+ }
+
+ mc.port2.postMessage(e.data);
+
+ if (e.data == MAX - 1) {
+ runTests();
+ }
+ }
+}
+
+var tests = [ test_fullDeliveredMessages, test_closeInBetween ];
+
+function runTests() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+}
+
+SimpleTest.waitForExplicitFinish();
+runTests();
+ </script>
+</body>
+</html>
diff --git a/dom/messagechannel/tests/test_messageChannel_cloning.html b/dom/messagechannel/tests/test_messageChannel_cloning.html
new file mode 100644
index 000000000..36aa3f0b7
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_cloning.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677638
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 677638 - port cloning</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=677638">Mozilla Bug 677638</a>
+<div id="content"></div>
+<pre id="test">
+</pre>
+ <script type="application/javascript">
+
+ // This test checks if MessagePorts can be shared with iframes
+ function test_iframe() {
+ window.addEventListener('message', receiveMessage, false);
+ function receiveMessage(evt) {
+ if (evt.data.status == 'OK') {
+ ok(true, evt.data.message);
+ } else if (evt.data.status == 'KO') {
+ ok(false, evt.data.message);
+ } else if (evt.data.status == 'FINISH') {
+ ok (evt.data.port instanceof MessagePort, "Data contains a MessagePort");
+ window.removeEventListener('message', receiveMessage);
+ runTest();
+ } else {
+ ok(false, "Unknown message");
+ }
+ }
+
+ 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_cloning.html");
+ div.appendChild(ifr);
+
+ function iframeLoaded() {
+ ifr.contentWindow.postMessage({ port: a.port2 }, '*', [a.port2]);
+ }
+ }
+
+ var tests = [
+ test_iframe
+ ];
+
+ function runTest() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ runTest();
+ </script>
+</body>
+</html>
diff --git a/dom/messagechannel/tests/test_messageChannel_forceClose.html b/dom/messagechannel/tests/test_messageChannel_forceClose.html
new file mode 100644
index 000000000..fd3efdc70
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_forceClose.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1176034
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1176034 - start/close</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=1176034">Mozilla Bug 1176034</a>
+<div id="content"></div>
+<pre id="test">
+</pre>
+ <script type="application/javascript">
+
+ var mc = new MessageChannel();
+
+ try {
+ postMessage(42, "*", [ mc.port1, window ]);
+ ok(false, "Something went wrong.");
+ } catch(e) {
+ ok(true, "PostMessage should fail and we should not leak.");
+ }
+
+ </script>
+</body>
+</html>
diff --git a/dom/messagechannel/tests/test_messageChannel_pingpong.html b/dom/messagechannel/tests/test_messageChannel_pingpong.html
new file mode 100644
index 000000000..f13eb54e6
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_pingpong.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677638
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 677638 - port cloning</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=677638">Mozilla Bug 677638</a>
+<div id="content"></div>
+<pre id="test">
+</pre>
+ <script type="application/javascript">
+
+ function runTest() {
+ var MAX = 100;
+
+ var a = new MessageChannel();
+ ok(a, "MessageChannel created");
+
+ // Populate the message queue of this port.
+ for (var i = 0; i < MAX; ++i) {
+ a.port1.postMessage(i);
+ }
+
+ window.addEventListener('message', receiveMessage, false);
+ function receiveMessage(evt) {
+
+ // This test sends the port from this window to the iframe and viceversa.
+ if (evt.data.type == 'PORT') {
+ var port = evt.data.port;
+ var counter = 0;
+ port.onmessage = function(evt) {
+ // only 1 message should be received by this port.
+ if (counter++ == 0) {
+ ok(evt.data % 2, "The number " + evt.data + " has been received correctly by the main window");
+
+ if (evt.data < MAX - 1) {
+ ifr.contentWindow.postMessage({ type: 'PORT', port: port }, '*', [port]);
+ } else {
+ SimpleTest.finish();
+ }
+ } else {
+ ok(false, "Wrong message!");
+ }
+ }
+ } else if (evt.data.type == 'OK') {
+ ok(true, evt.data.msg);
+ } else if (evt.data.type == 'KO') {
+ ok(false, evt.data.msg);
+ } else {
+ ok(false, "Unknown message");
+ }
+ }
+
+ var div = document.getElementById("content");
+ ok(div, "Parent exists");
+
+ var ifr = document.createElement("iframe");
+ ifr.addEventListener("load", iframeLoaded, false);
+ ifr.setAttribute('src', "iframe_messageChannel_pingpong.html");
+ div.appendChild(ifr);
+
+ function iframeLoaded() {
+ ifr.contentWindow.postMessage({ type: 'PORT', port: a.port2 }, '*', [a.port2]);
+ }
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ runTest();
+ </script>
+</body>
+</html>
diff --git a/dom/messagechannel/tests/test_messageChannel_post.html b/dom/messagechannel/tests/test_messageChannel_post.html
new file mode 100644
index 000000000..ddbf59dc9
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_post.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677638
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 677638 - port cloning</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=677638">Mozilla Bug 677638</a>
+<div id="content"></div>
+<pre id="test">
+</pre>
+ <script type="application/javascript">
+
+ function start() {
+ var a = new MessageChannel();
+ ok(a, "MessageChannel created");
+
+ window.addEventListener('message', receiveMessage, false);
+ function receiveMessage(evt) {
+ if (evt.data.status == 'READY') {
+ runTest();
+ } else {
+ ok(false, "Unknown message");
+ }
+ }
+
+ var div = document.getElementById("content");
+ ok(div, "Parent exists");
+
+ var ifr = document.createElement("iframe");
+ ifr.addEventListener("load", iframeLoaded, false);
+ ifr.setAttribute('src', "iframe_messageChannel_post.html");
+ div.appendChild(ifr);
+
+ function iframeLoaded() {
+ ifr.contentWindow.postMessage({ port: a.port2 }, '*', [a.port2]);
+ }
+
+ var tests = [ 42,
+ null,
+ undefined,
+ "hello world",
+ new Blob([]),
+ true ];
+
+ a.port1.onmessage = function(evt) {
+ ok(tests.length, "We are waiting for a message");
+ if (typeof(tests[0]) == 'object') {
+ is(typeof(tests[0]), typeof(evt.data), "Value ok: " + tests[0]);
+ } else {
+ is(tests[0], evt.data, "Value ok: " + tests[0]);
+ }
+ tests.shift();
+ runTest();
+ }
+
+ function runTest() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ a.port1.postMessage(tests[0]);
+ }
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ start();
+ </script>
+</body>
+</html>
diff --git a/dom/messagechannel/tests/test_messageChannel_selfTransferring.html b/dom/messagechannel/tests/test_messageChannel_selfTransferring.html
new file mode 100644
index 000000000..d01ca5fc3
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_selfTransferring.html
@@ -0,0 +1,33 @@
+<!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">
+
+ 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");
+
+ </script>
+</body>
+</html>
+
diff --git a/dom/messagechannel/tests/test_messageChannel_sharedWorker.html b/dom/messagechannel/tests/test_messageChannel_sharedWorker.html
new file mode 100644
index 000000000..3b7b9ea69
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_sharedWorker.html
@@ -0,0 +1,36 @@
+<!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">
+
+ 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);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ </script>
+</body>
+</html>
diff --git a/dom/messagechannel/tests/test_messageChannel_sharedWorker2.html b/dom/messagechannel/tests/test_messageChannel_sharedWorker2.html
new file mode 100644
index 000000000..57a1e606a
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_sharedWorker2.html
@@ -0,0 +1,34 @@
+<!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">
+
+ 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();
+ }
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ </script>
+</body>
+</html>
diff --git a/dom/messagechannel/tests/test_messageChannel_start.html b/dom/messagechannel/tests/test_messageChannel_start.html
new file mode 100644
index 000000000..192230adb
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_start.html
@@ -0,0 +1,235 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677638
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 677638 - start/close</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 runTests() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+ }
+
+ function testOnMessage() {
+ var a = new MessageChannel();
+ ok(a, "MessageChannel created");
+
+ a.port1.postMessage(42);
+ a.port2.postMessage(43);
+ ok(true, "MessagePort{1,2}.postmessage() invoked");
+
+ var events = 2;
+
+ a.port1.onmessage = function(evt) {
+ ok(true, "This method should be called");
+ if (!--events) runTests();
+ }
+
+ a.port2.onmessage = function(evt) {
+ ok(true, "This method should be called");
+ if (!--events) runTests();
+ }
+ }
+
+ function testAddEventListener() {
+ var a = new MessageChannel();
+ ok(a, "MessageChannel created");
+
+ a.port1.postMessage(42);
+ a.port2.postMessage(43);
+ ok(true, "MessagePort{1,2}.postmessage() invoked");
+
+ a.port1.addEventListener('message', function(evt) {
+ ok(false, "This method should not be called");
+ }, false);
+
+ a.port2.addEventListener('message', function(evt) {
+ ok(false, "This method should not be called");
+ }, false);
+
+ setTimeout(runTests, 0);
+ }
+
+ function testAddEventListenerAndStart() {
+ var a = new MessageChannel();
+ ok(a, "MessageChannel created");
+
+ a.port1.postMessage(42);
+ a.port2.postMessage(43);
+ ok(true, "MessagePort{1,2}.postmessage() invoked");
+
+ var events = 2;
+
+ a.port1.addEventListener('message', function(evt) {
+ ok(true, "This method should be called");
+ if (!--events) runTests();
+ }, false);
+
+ a.port2.addEventListener('message', function(evt) {
+ ok(true, "This method should be called");
+ if (!--events) runTests();
+ }, false);
+
+ a.port1.start();
+ a.port2.start();
+ }
+
+ function testAddEventListener1AndStart() {
+ var a = new MessageChannel();
+ ok(a, "MessageChannel created");
+
+ a.port1.postMessage(42);
+ a.port2.postMessage(43);
+ ok(true, "MessagePort{1,2}.postmessage() invoked");
+
+ var events = 1;
+
+ a.port1.addEventListener('message', function(evt) {
+ ok(true, "This method should be called");
+ if (!--events) runTests();
+ }, false);
+
+ a.port2.addEventListener('message', function(evt) {
+ ok(false, "This method should not be called");
+ }, false);
+
+ a.port1.start();
+ }
+
+ function testAddEventListener2AndStart() {
+ var a = new MessageChannel();
+ ok(a, "MessageChannel created");
+
+ a.port1.postMessage(42);
+ a.port2.postMessage(43);
+ ok(true, "MessagePort{1,2}.postmessage() invoked");
+
+ var events = 1;
+
+ a.port1.addEventListener('message', function(evt) {
+ ok(false, "This method should not be called");
+ }, false);
+
+ a.port2.addEventListener('message', function(evt) {
+ ok(true, "This method should be called");
+ if (!--events) runTests();
+ }, false);
+
+ a.port2.start();
+ }
+
+ function testTimer() {
+ var a = new MessageChannel();
+ ok(a, "MessageChannel created");
+
+ a.port1.postMessage(42);
+ a.port2.postMessage(43);
+ ok(true, "MessagePort{1,2}.postmessage() invoked");
+
+ setTimeout(function() {
+ var events = 2;
+ a.port1.onmessage = function(evt) {
+ ok(true, "This method should be called");
+ if (!--events) runTests();
+ }
+
+ a.port2.onmessage = function(evt) {
+ ok(true, "This method should be called");
+ if (!--events) runTests();
+ }
+ }, 200);
+ }
+
+ function testAddEventListenerAndStartWrongOrder() {
+ var a = new MessageChannel();
+ ok(a, "MessageChannel created");
+
+ a.port1.postMessage(42);
+ a.port2.postMessage(43);
+ ok(true, "MessagePort{1,2}.postmessage() invoked");
+
+ var events = 2;
+
+ a.port1.start();
+ a.port1.addEventListener('message', function(evt) {
+ ok(true, "This method should be called");
+ if (!--events) runTests();
+ }, false);
+
+ a.port2.start();
+ a.port2.addEventListener('message', function(evt) {
+ ok(true, "This method should be called");
+ if (!--events) runTests();
+ }, false);
+ }
+
+ function testOnMessageClone() {
+ var a = new MessageChannel();
+ ok(a, "MessageChannel created");
+
+ a.port1.postMessage(42);
+ a.port2.postMessage(43);
+ ok(true, "MessagePort{1,2}.postmessage() invoked");
+
+ var events = 2;
+
+ addEventListener('message', testOnMessageCloneCb, false);
+ function testOnMessageCloneCb(evt) {
+ a.port1.onmessage = function(evt) {
+ ok(true, "This method should be called");
+ testOnMessageCloneFinish();
+ }
+
+ evt.data.onmessage = function(evt) {
+ ok(true, "This method should be called");
+ testOnMessageCloneFinish();
+ }
+
+ a.port2.onmessage = function(evt) {
+ ok(false, "This method should not be called");
+ }
+ }
+
+ function testOnMessageCloneFinish() {
+ if (!--events) {
+ removeEventListener('message', testOnMessageCloneCb);
+ runTests();
+ }
+ }
+
+ postMessage(a.port2, '*', [a.port2]);
+ }
+
+ var tests = [
+ testOnMessage,
+ testAddEventListener,
+ testAddEventListenerAndStart,
+ testAddEventListener1AndStart,
+ testAddEventListener2AndStart,
+ testTimer,
+ testAddEventListenerAndStartWrongOrder,
+ testOnMessageClone,
+ ];
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+ runTests();
+ </script>
+</body>
+</html>
diff --git a/dom/messagechannel/tests/test_messageChannel_transferable.html b/dom/messagechannel/tests/test_messageChannel_transferable.html
new file mode 100644
index 000000000..095bf25dc
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_transferable.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677638
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 677638 - port cloning</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=677638">Mozilla Bug 677638</a>
+<div id="content"></div>
+<pre id="test">
+</pre>
+ <script type="application/javascript">
+
+ function basic_test() {
+ var a = new MessageChannel();
+ ok(a, "MessageChannel created");
+
+ window.addEventListener('message', receiveMessage, false);
+ function receiveMessage(evt) {
+ if (evt.data.status == 'READY') {
+ a.port1.postMessage({ab: ab, cb: ab}, [ab]);
+ ok(ab.byteLength == 0, "PostMessage - The size is: 0 == " + ab.byteLength)
+ } else {
+ ok(false, "Unknown message");
+ }
+ }
+
+ var div = document.getElementById("content");
+ ok(div, "Parent exists");
+
+ var ifr = document.createElement("iframe");
+ ifr.addEventListener("load", iframeLoaded, false);
+ ifr.setAttribute('src', "iframe_messageChannel_post.html");
+ div.appendChild(ifr);
+
+ function iframeLoaded() {
+ ifr.contentWindow.postMessage({ port: a.port2 }, '*', [a.port2]);
+ }
+
+ a.port1.addEventListener('message', receivePortMessage, false);
+ function receivePortMessage(evt) {
+ is(evt.data.ab.byteLength, size, "The size is: " + size + " == " + ab.byteLength);
+ window.removeEventListener('message', receiveMessage);
+ runTests();
+ }
+
+ // Start() is not implicity invoked when addEventListener is used.
+ a.port1.start();
+
+ var size = 1024 * 1024 * 32;
+ var ab = new ArrayBuffer(size);
+ is(ab.byteLength, size, "The size is: " + size + " == " + 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();
+ runTests();
+ </script>
+</body>
+</html>
diff --git a/dom/messagechannel/tests/test_messageChannel_unshipped.html b/dom/messagechannel/tests/test_messageChannel_unshipped.html
new file mode 100644
index 000000000..6661b7d0a
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_unshipped.html
@@ -0,0 +1,123 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677638
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 677638 - unshipped message port queue</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 test_orderedMessages() {
+ var a = new MessageChannel();
+ ok(a, "MessageChannel created");
+
+ var b = new MessageChannel();
+ ok(b, "MessageChannel created");
+
+ var expectedNumber = 1;
+ function testEvent(number, id) {
+ is(expectedNumber, number, "This is the right number!");
+ ok(!((expectedNumber - id) % 4), "From the right port: " + expectedNumber + " " + id);
+ expectedNumber++;
+
+ if (expectedNumber >100) {
+ runTests();
+ }
+ }
+
+ a.port1.onmessage = function(evt) {
+ testEvent(evt.data, 2);
+ };
+
+ a.port2.onmessage = function(evt) {
+ testEvent(evt.data, 1);
+ };
+
+ b.port1.onmessage = function(evt) {
+ testEvent(evt.data, 4);
+ };
+
+ b.port2.onmessage = function(evt) {
+ testEvent(evt.data, 3);
+ };
+
+ for (var i = 0; i < 100;) {
+ a.port1.postMessage(++i);
+ a.port2.postMessage(++i);
+ b.port1.postMessage(++i);
+ b.port2.postMessage(++i);
+ }
+ }
+
+ function test_unstarted() {
+ var a = new MessageChannel();
+ ok(a, "MessageChannel created");
+
+ var b = new MessageChannel();
+ ok(b, "MessageChannel created");
+
+ var expectedNumber = 1;
+ function testEvent(number, id) {
+ is(expectedNumber, number, "This is the right number!");
+ ok(!((expectedNumber - id) % 3), "From the right port: " + expectedNumber + " " + id);
+ expectedNumber++;
+
+ // 102 because it's the first multiple of 3.
+ if (expectedNumber > 102) {
+ runTests();
+ }
+ }
+
+ a.port1.onmessage = function(evt) {
+ testEvent(evt.data, 2);
+ };
+
+ a.port2.onmessage = function(evt) {
+ testEvent(evt.data, 1);
+ };
+
+ b.port1.addEventListener("message", function() {
+ ok(false, "shouldn't be called");
+ });
+
+ b.port2.onmessage = function(evt) {
+ testEvent(evt.data, 3);
+ };
+
+ for (var i = 0; i < 100;) {
+ a.port1.postMessage(++i);
+ a.port2.postMessage(++i);
+ b.port1.postMessage(++i);
+ b.port2.postMessage(1000);
+ }
+ }
+
+ var tests = [
+ test_orderedMessages,
+ test_unstarted
+ ];
+
+ function runTests() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ runTests();
+ </script>
+</body>
+</html>
diff --git a/dom/messagechannel/tests/test_messageChannel_worker.html b/dom/messagechannel/tests/test_messageChannel_worker.html
new file mode 100644
index 000000000..2c1c02626
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_worker.html
@@ -0,0 +1,60 @@
+
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677638
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 677638 - 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();
+ runTests();
+ </script>
+</body>
+</html>
diff --git a/dom/messagechannel/tests/test_messageChannel_worker_forceClose.html b/dom/messagechannel/tests/test_messageChannel_worker_forceClose.html
new file mode 100644
index 000000000..1610fc265
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_worker_forceClose.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for forcing the closing of the port in workers</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<div id="content"></div>
+<pre id="test">
+</pre>
+ <script type="application/javascript">
+
+ var worker = new Worker('data:javascript,onmessage = function(e) { "doing nothing with this port"; }');
+
+ var mc = new MessageChannel();
+ worker.postMessage(42, [mc.port2]);
+
+ for (var i = 0; i < 10; ++i) {
+ mc.port1.postMessage(i);
+ }
+
+ ok(true, "All the messages are sent! We should shutdown correctly.");
+ </script>
+</body>
+</html>
diff --git a/dom/messagechannel/tests/worker_messageChannel.js b/dom/messagechannel/tests/worker_messageChannel.js
new file mode 100644
index 000000000..87b0b8eb0
--- /dev/null
+++ b/dom/messagechannel/tests/worker_messageChannel.js
@@ -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);
+}
diff --git a/dom/messagechannel/tests/worker_messageChannel_any.js b/dom/messagechannel/tests/worker_messageChannel_any.js
new file mode 100644
index 000000000..bbb1d50f9
--- /dev/null
+++ b/dom/messagechannel/tests/worker_messageChannel_any.js
@@ -0,0 +1,7 @@
+onmessage = function(evt) {
+ evt.data.onmessage = function(event) {
+ evt.data.postMessage(event.data);
+ }
+}
+
+postMessage("READY");