--- a/dom/base/NodeInfo.cpp
+++ b/dom/base/NodeInfo.cpp
@@ -106,17 +106,17 @@ NodeInfo::NodeInfo(nsIAtom *aName, nsIAt
// nsISupports
NS_IMPL_CYCLE_COLLECTION_CLASS(NodeInfo)
NS_IMPL_CYCLE_COLLECTION_UNLINK_0(NodeInfo)
-static const char* kNSURIs[] = {
+static const char* kNodeInfoNSURIs[] = {
" ([none])",
" (xmlns)",
" (xml)",
" (xhtml)",
" (XLink)",
" (XSLT)",
" (XBL)",
" (MathML)",
@@ -124,18 +124,18 @@ static const char* kNSURIs[] = {
" (XUL)"
};
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(NodeInfo)
if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
char name[72];
uint32_t nsid = tmp->NamespaceID();
nsAtomCString localName(tmp->NameAtom());
- if (nsid < ArrayLength(kNSURIs)) {
- PR_snprintf(name, sizeof(name), "NodeInfo%s %s", kNSURIs[nsid],
+ if (nsid < ArrayLength(kNodeInfoNSURIs)) {
+ PR_snprintf(name, sizeof(name), "NodeInfo%s %s", kNodeInfoNSURIs[nsid],
localName.get());
}
else {
PR_snprintf(name, sizeof(name), "NodeInfo %s", localName.get());
}
cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
}
new file mode 100644
--- /dev/null
+++ b/dom/base/PostMessageEvent.cpp
@@ -0,0 +1,389 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PostMessageEvent.h"
+
+#include "MessageEvent.h"
+#include "mozilla/dom/BlobBinding.h"
+#include "mozilla/dom/MessagePort.h"
+#include "mozilla/dom/MessagePortBinding.h"
+#include "mozilla/dom/PMessagePort.h"
+#include "mozilla/dom/StructuredCloneTags.h"
+#include "mozilla/EventDispatcher.h"
+#include "nsGlobalWindow.h"
+#include "nsIPresShell.h"
+#include "nsIPrincipal.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+struct StructuredCloneInfo
+{
+ PostMessageEvent* event;
+ bool subsumes;
+ nsPIDOMWindow* window;
+
+ // This hashtable contains the transferred ports - used to avoid duplicates.
+ nsTArray<nsRefPtr<MessagePortBase>> transferredPorts;
+
+ // This array is populated when the ports are cloned.
+ nsTArray<nsRefPtr<MessagePortBase>> clonedPorts;
+};
+
+} // anonymous namespace
+
+const JSStructuredCloneCallbacks PostMessageEvent::sPostMessageCallbacks = {
+ PostMessageEvent::ReadStructuredClone,
+ PostMessageEvent::WriteStructuredClone,
+ nullptr,
+ PostMessageEvent::ReadTransferStructuredClone,
+ PostMessageEvent::TransferStructuredClone,
+ PostMessageEvent::FreeTransferStructuredClone
+};
+
+/* static */ JSObject*
+PostMessageEvent::ReadStructuredClone(JSContext* cx,
+ JSStructuredCloneReader* reader,
+ uint32_t tag,
+ uint32_t data,
+ void* closure)
+{
+ StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
+ NS_ASSERTION(scInfo, "Must have scInfo!");
+
+ if (tag == SCTAG_DOM_BLOB) {
+ NS_ASSERTION(!data, "Data should be empty");
+
+ // What we get back from the reader is a BlobImpl.
+ // From that we create a new File.
+ BlobImpl* blobImpl;
+ if (JS_ReadBytes(reader, &blobImpl, sizeof(blobImpl))) {
+ MOZ_ASSERT(blobImpl);
+
+ // nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
+ // called because the static analysis thinks dereferencing XPCOM objects
+ // can GC (because in some cases it can!), and a return statement with a
+ // JSObject* type means that JSObject* is on the stack as a raw pointer
+ // while destructors are running.
+ JS::Rooted<JS::Value> val(cx);
+ {
+ nsRefPtr<Blob> blob = Blob::Create(scInfo->window, blobImpl);
+ if (!ToJSValue(cx, blob, &val)) {
+ return nullptr;
+ }
+ }
+
+ return &val.toObject();
+ }
+ }
+
+ if (tag == SCTAG_DOM_FILELIST) {
+ NS_ASSERTION(!data, "Data should be empty");
+
+ nsISupports* supports;
+ if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
+ JS::Rooted<JS::Value> val(cx);
+ if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, supports, &val))) {
+ return val.toObjectOrNull();
+ }
+ }
+ }
+
+ const JSStructuredCloneCallbacks* runtimeCallbacks =
+ js::GetContextStructuredCloneCallbacks(cx);
+
+ if (runtimeCallbacks) {
+ return runtimeCallbacks->read(cx, reader, tag, data, nullptr);
+ }
+
+ return nullptr;
+}
+
+/* static */ bool
+PostMessageEvent::WriteStructuredClone(JSContext* cx,
+ JSStructuredCloneWriter* writer,
+ JS::Handle<JSObject*> obj,
+ void *closure)
+{
+ StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
+ NS_ASSERTION(scInfo, "Must have scInfo!");
+
+ // See if this is a File/Blob object.
+ {
+ Blob* blob = nullptr;
+ if (scInfo->subsumes && NS_SUCCEEDED(UNWRAP_OBJECT(Blob, obj, blob))) {
+ BlobImpl* blobImpl = blob->Impl();
+ if (JS_WriteUint32Pair(writer, SCTAG_DOM_BLOB, 0) &&
+ JS_WriteBytes(writer, &blobImpl, sizeof(blobImpl))) {
+ scInfo->event->StoreISupports(blobImpl);
+ return true;
+ }
+ }
+ }
+
+ nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
+ nsContentUtils::XPConnect()->
+ GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
+ if (wrappedNative) {
+ uint32_t scTag = 0;
+ nsISupports* supports = wrappedNative->Native();
+
+ nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports);
+ if (list && scInfo->subsumes)
+ scTag = SCTAG_DOM_FILELIST;
+
+ if (scTag)
+ return JS_WriteUint32Pair(writer, scTag, 0) &&
+ JS_WriteBytes(writer, &supports, sizeof(supports)) &&
+ scInfo->event->StoreISupports(supports);
+ }
+
+ const JSStructuredCloneCallbacks* runtimeCallbacks =
+ js::GetContextStructuredCloneCallbacks(cx);
+
+ if (runtimeCallbacks) {
+ return runtimeCallbacks->write(cx, writer, obj, nullptr);
+ }
+
+ return false;
+}
+
+/* static */ bool
+PostMessageEvent::ReadTransferStructuredClone(JSContext* aCx,
+ JSStructuredCloneReader* reader,
+ uint32_t tag, void* aData,
+ uint64_t aExtraData,
+ void* aClosure,
+ JS::MutableHandle<JSObject*> returnObject)
+{
+ StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
+ NS_ASSERTION(scInfo, "Must have scInfo!");
+
+ if (tag == SCTAG_DOM_MAP_MESSAGEPORT) {
+ MOZ_ASSERT(!aData);
+ // aExtraData is the index of this port identifier.
+ ErrorResult rv;
+ nsRefPtr<MessagePort> port =
+ MessagePort::Create(scInfo->window,
+ scInfo->event->GetPortIdentifier(aExtraData),
+ rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ return false;
+ }
+
+ scInfo->clonedPorts.AppendElement(port);
+
+ JS::Rooted<JS::Value> value(aCx);
+ if (!GetOrCreateDOMReflector(aCx, port, &value)) {
+ JS_ClearPendingException(aCx);
+ return false;
+ }
+
+ returnObject.set(&value.toObject());
+ return true;
+ }
+
+ return false;
+}
+
+/* static */ bool
+PostMessageEvent::TransferStructuredClone(JSContext* aCx,
+ JS::Handle<JSObject*> aObj,
+ void* aClosure,
+ uint32_t* aTag,
+ JS::TransferableOwnership* aOwnership,
+ void** aContent,
+ uint64_t* aExtraData)
+{
+ StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
+ NS_ASSERTION(scInfo, "Must have scInfo!");
+
+ MessagePortBase* port = nullptr;
+ nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
+ if (NS_SUCCEEDED(rv)) {
+ if (scInfo->transferredPorts.Contains(port)) {
+ // No duplicates.
+ return false;
+ }
+
+ // We use aExtraData to store the index of this new port identifier.
+ MessagePortIdentifier* identifier =
+ scInfo->event->NewPortIdentifier(aExtraData);
+
+ if (!port->CloneAndDisentangle(*identifier)) {
+ return false;
+ }
+
+ scInfo->transferredPorts.AppendElement(port);
+
+ *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
+ *aOwnership = JS::SCTAG_TMO_CUSTOM;
+ *aContent = nullptr;
+
+ return true;
+ }
+
+ return false;
+}
+
+/* static */ void
+PostMessageEvent::FreeTransferStructuredClone(uint32_t aTag,
+ JS::TransferableOwnership aOwnership,
+ void *aContent,
+ uint64_t aExtraData,
+ void* aClosure)
+{
+ // Nothing to do.
+}
+
+PostMessageEvent::PostMessageEvent(nsGlobalWindow* aSource,
+ const nsAString& aCallerOrigin,
+ nsGlobalWindow* aTargetWindow,
+ nsIPrincipal* aProvidedPrincipal,
+ bool aTrustedCaller)
+: mSource(aSource),
+ mCallerOrigin(aCallerOrigin),
+ mTargetWindow(aTargetWindow),
+ mProvidedPrincipal(aProvidedPrincipal),
+ mTrustedCaller(aTrustedCaller)
+{
+ MOZ_COUNT_CTOR(PostMessageEvent);
+}
+
+PostMessageEvent::~PostMessageEvent()
+{
+ MOZ_COUNT_DTOR(PostMessageEvent);
+}
+
+const MessagePortIdentifier&
+PostMessageEvent::GetPortIdentifier(uint64_t aId)
+{
+ MOZ_ASSERT(aId < mPortIdentifiers.Length());
+ return mPortIdentifiers[aId];
+}
+
+MessagePortIdentifier*
+PostMessageEvent::NewPortIdentifier(uint64_t* aPosition)
+{
+ *aPosition = mPortIdentifiers.Length();
+ return mPortIdentifiers.AppendElement();
+}
+
+NS_IMETHODIMP
+PostMessageEvent::Run()
+{
+ MOZ_ASSERT(mTargetWindow->IsOuterWindow(),
+ "should have been passed an outer window!");
+ MOZ_ASSERT(!mSource || mSource->IsOuterWindow(),
+ "should have been passed an outer window!");
+
+ AutoJSAPI jsapi;
+ jsapi.Init();
+ JSContext* cx = jsapi.cx();
+
+ // If we bailed before this point we're going to leak mMessage, but
+ // that's probably better than crashing.
+
+ nsRefPtr<nsGlobalWindow> targetWindow;
+ if (mTargetWindow->IsClosedOrClosing() ||
+ !(targetWindow = mTargetWindow->GetCurrentInnerWindowInternal()) ||
+ targetWindow->IsClosedOrClosing())
+ return NS_OK;
+
+ MOZ_ASSERT(targetWindow->IsInnerWindow(),
+ "we ordered an inner window!");
+ JSAutoCompartment ac(cx, targetWindow->GetWrapperPreserveColor());
+
+ // Ensure that any origin which might have been provided is the origin of this
+ // window's document. Note that we do this *now* instead of when postMessage
+ // is called because the target window might have been navigated to a
+ // different location between then and now. If this check happened when
+ // postMessage was called, it would be fairly easy for a malicious webpage to
+ // intercept messages intended for another site by carefully timing navigation
+ // of the target window so it changed location after postMessage but before
+ // now.
+ if (mProvidedPrincipal) {
+ // Get the target's origin either from its principal or, in the case the
+ // principal doesn't carry a URI (e.g. the system principal), the target's
+ // document.
+ nsIPrincipal* targetPrin = targetWindow->GetPrincipal();
+ if (NS_WARN_IF(!targetPrin))
+ return NS_OK;
+
+ // Note: This is contrary to the spec with respect to file: URLs, which
+ // the spec groups into a single origin, but given we intentionally
+ // don't do that in other places it seems better to hold the line for
+ // now. Long-term, we want HTML5 to address this so that we can
+ // be compliant while being safer.
+ if (!targetPrin->Equals(mProvidedPrincipal)) {
+ return NS_OK;
+ }
+ }
+
+ // Deserialize the structured clone data
+ JS::Rooted<JS::Value> messageData(cx);
+ StructuredCloneInfo scInfo;
+ scInfo.event = this;
+ scInfo.window = targetWindow;
+
+ if (!mBuffer.read(cx, &messageData, &sPostMessageCallbacks, &scInfo)) {
+ return NS_ERROR_DOM_DATA_CLONE_ERR;
+ }
+
+ // Create the event
+ nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
+ do_QueryInterface(static_cast<nsPIDOMWindow*>(targetWindow.get()));
+ nsRefPtr<MessageEvent> event =
+ new MessageEvent(eventTarget, nullptr, nullptr);
+
+ event->InitMessageEvent(NS_LITERAL_STRING("message"), false /*non-bubbling */,
+ false /*cancelable */, messageData, mCallerOrigin,
+ EmptyString(), mSource);
+
+ event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()),
+ scInfo.clonedPorts));
+
+ // We can't simply call dispatchEvent on the window because doing so ends
+ // up flipping the trusted bit on the event, and we don't want that to
+ // happen because then untrusted content can call postMessage on a chrome
+ // window if it can get a reference to it.
+
+ nsIPresShell *shell = targetWindow->GetExtantDoc()->GetShell();
+ nsRefPtr<nsPresContext> presContext;
+ if (shell)
+ presContext = shell->GetPresContext();
+
+ event->SetTrusted(mTrustedCaller);
+ WidgetEvent* internalEvent = event->GetInternalNSEvent();
+
+ nsEventStatus status = nsEventStatus_eIgnore;
+ EventDispatcher::Dispatch(static_cast<nsPIDOMWindow*>(mTargetWindow),
+ presContext,
+ internalEvent,
+ static_cast<dom::Event*>(event.get()),
+ &status);
+ return NS_OK;
+}
+
+bool
+PostMessageEvent::Write(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ JS::Handle<JS::Value> aTransfer, bool aSubsumes,
+ nsPIDOMWindow* aWindow)
+{
+ // We *must* clone the data here, or the JS::Value could be modified
+ // by script
+ StructuredCloneInfo scInfo;
+ scInfo.event = this;
+ scInfo.window = aWindow;
+
+ return mBuffer.write(aCx, aMessage, aTransfer, &sPostMessageCallbacks,
+ &scInfo);
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/base/PostMessageEvent.h
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_PostMessageEvent_h
+#define mozilla_dom_PostMessageEvent_h
+
+#include "js/StructuredClone.h"
+#include "nsCOMPtr.h"
+#include "nsRefPtr.h"
+#include "nsTArray.h"
+
+class nsGlobalWindow;
+class nsIPrincipal;
+
+namespace mozilla {
+namespace dom {
+
+class MessagePortBase;
+class MessagePortIdentifier;
+
+/**
+ * Class used to represent events generated by calls to Window.postMessage,
+ * which asynchronously creates and dispatches events.
+ */
+class PostMessageEvent final : public nsRunnable
+{
+public:
+ NS_DECL_NSIRUNNABLE
+
+ PostMessageEvent(nsGlobalWindow* aSource,
+ const nsAString& aCallerOrigin,
+ nsGlobalWindow* aTargetWindow,
+ nsIPrincipal* aProvidedPrincipal,
+ bool aTrustedCaller);
+
+ bool Write(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ JS::Handle<JS::Value> aTransfer, bool aSubsumes,
+ nsPIDOMWindow* aWindow);
+
+private:
+ ~PostMessageEvent();
+
+ const MessagePortIdentifier& GetPortIdentifier(uint64_t aId);
+
+ MessagePortIdentifier* NewPortIdentifier(uint64_t* aPosition);
+
+ bool StoreISupports(nsISupports* aSupports)
+ {
+ mSupportsArray.AppendElement(aSupports);
+ return true;
+ }
+
+ static JSObject*
+ ReadStructuredClone(JSContext* cx,
+ JSStructuredCloneReader* reader,
+ uint32_t tag,
+ uint32_t data,
+ void* closure);
+
+ static bool
+ WriteStructuredClone(JSContext* cx,
+ JSStructuredCloneWriter* writer,
+ JS::Handle<JSObject*> obj,
+ void *closure);
+
+ static bool
+ ReadTransferStructuredClone(JSContext* aCx,
+ JSStructuredCloneReader* reader,
+ uint32_t tag, void* aData,
+ uint64_t aExtraData,
+ void* aClosure,
+ JS::MutableHandle<JSObject*> returnObject);
+
+ static bool
+ TransferStructuredClone(JSContext* aCx,
+ JS::Handle<JSObject*> aObj,
+ void* aClosure,
+ uint32_t* aTag,
+ JS::TransferableOwnership* aOwnership,
+ void** aContent,
+ uint64_t* aExtraData);
+
+ static void
+ FreeTransferStructuredClone(uint32_t aTag,
+ JS::TransferableOwnership aOwnership,
+ void *aContent,
+ uint64_t aExtraData,
+ void* aClosure);
+
+ static const JSStructuredCloneCallbacks sPostMessageCallbacks;
+
+ JSAutoStructuredCloneBuffer mBuffer;
+ nsRefPtr<nsGlobalWindow> mSource;
+ nsString mCallerOrigin;
+ nsRefPtr<nsGlobalWindow> mTargetWindow;
+ nsCOMPtr<nsIPrincipal> mProvidedPrincipal;
+ bool mTrustedCaller;
+ nsTArray<nsCOMPtr<nsISupports>> mSupportsArray;
+ nsTArray<MessagePortIdentifier> mPortIdentifiers;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_PostMessageEvent_h
--- a/dom/base/ProcessGlobal.cpp
+++ b/dom/base/ProcessGlobal.cpp
@@ -2,16 +2,17 @@
/* 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 "ProcessGlobal.h"
#include "nsContentCID.h"
+#include "nsDOMClassInfoID.h"
using namespace mozilla;
using namespace mozilla::dom;
ProcessGlobal::ProcessGlobal(nsFrameMessageManager* aMessageManager)
: mInitialized(false),
mMessageManager(aMessageManager)
{
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -174,19 +174,16 @@ EXPORTS.mozilla.dom += [
'ElementInlines.h',
'EventSource.h',
'File.h',
'FragmentOrElement.h',
'FromParser.h',
'ImageEncoder.h',
'ImportManager.h',
'Link.h',
- 'MessageChannel.h',
- 'MessagePort.h',
- 'MessagePortList.h',
'NameSpaceConstants.h',
'Navigator.h',
'NodeInfo.h',
'NodeInfoInlines.h',
'NodeIterator.h',
'PerformanceEntry.h',
'PerformanceMark.h',
'PerformanceMeasure.h',
@@ -232,18 +229,16 @@ UNIFIED_SOURCES += [
'Element.cpp',
'EventSource.cpp',
'File.cpp',
'FileIOObject.cpp',
'FragmentOrElement.cpp',
'ImageEncoder.cpp',
'ImportManager.cpp',
'Link.cpp',
- 'MessageChannel.cpp',
- 'MessagePortList.cpp',
'MultipartBlobImpl.cpp',
'Navigator.cpp',
'NodeInfo.cpp',
'NodeIterator.cpp',
'nsAtomListUtils.cpp',
'nsAttrAndChildArray.cpp',
'nsAttrValue.cpp',
'nsAttrValueOrString.cpp',
@@ -324,16 +319,17 @@ UNIFIED_SOURCES += [
'nsXHTMLContentSerializer.cpp',
'nsXMLContentSerializer.cpp',
'nsXMLHttpRequest.cpp',
'nsXMLNameSpaceMap.cpp',
'PerformanceEntry.cpp',
'PerformanceMark.cpp',
'PerformanceMeasure.cpp',
'PerformanceResourceTiming.cpp',
+ 'PostMessageEvent.cpp',
'ProcessGlobal.cpp',
'ResponsiveImageSelector.cpp',
'SameProcessMessageQueue.cpp',
'ScriptSettings.cpp',
'ShadowRoot.cpp',
'StyleSheetList.cpp',
'SubtleCrypto.cpp',
'Text.cpp',
@@ -348,18 +344,16 @@ UNIFIED_SOURCES += [
if CONFIG['MOZ_WEBRTC']:
UNIFIED_SOURCES += [
'nsDOMDataChannel.cpp',
]
# these files couldn't be in UNIFIED_SOURCES for now for reasons given below:
SOURCES += [
- # this file doesn't like windows.h
- 'MessagePort.cpp',
# Because of OS X headers.
'nsContentUtils.cpp',
# this file doesn't like windows.h
'nsDOMWindowUtils.cpp',
# Conflicts with windows.h's definition of SendMessage.
'nsFrameMessageManager.cpp',
# This file has a #error "Never include windows.h in this file!"
'nsGlobalWindow.cpp',
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -213,16 +213,17 @@ const char kLoadAsData[] = "loadAsData";
nsIXPConnect *nsContentUtils::sXPConnect;
nsIScriptSecurityManager *nsContentUtils::sSecurityManager;
nsIPrincipal *nsContentUtils::sSystemPrincipal;
nsIPrincipal *nsContentUtils::sNullSubjectPrincipal;
nsIParserService *nsContentUtils::sParserService = nullptr;
nsNameSpaceManager *nsContentUtils::sNameSpaceManager;
nsIIOService *nsContentUtils::sIOService;
+nsIUUIDGenerator *nsContentUtils::sUUIDGenerator;
nsIConsoleService *nsContentUtils::sConsoleService;
nsDataHashtable<nsISupportsHashKey, EventNameMapping>* nsContentUtils::sAtomEventTable = nullptr;
nsDataHashtable<nsStringHashKey, EventNameMapping>* nsContentUtils::sStringEventTable = nullptr;
nsCOMArray<nsIAtom>* nsContentUtils::sUserDefinedEvents = nullptr;
nsIStringBundleService *nsContentUtils::sStringBundleService;
nsIStringBundle *nsContentUtils::sStringBundles[PropertiesFile_COUNT];
nsIContentPolicy *nsContentUtils::sContentPolicyService;
bool nsContentUtils::sTriedToGetContentPolicy = false;
@@ -546,16 +547,23 @@ nsContentUtils::Init()
#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
Preferences::AddBoolVarCache(&sDOMWindowDumpEnabled,
"browser.dom.window.dump.enabled");
#endif
Element::InitCCCallbacks();
+ nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
+ do_GetService("@mozilla.org/uuid-generator;1", &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ uuidGenerator.forget(&sUUIDGenerator);
+
sInitialized = true;
return NS_OK;
}
void
nsContentUtils::GetShiftText(nsAString& text)
{
@@ -1798,16 +1806,17 @@ nsContentUtils::Shutdown()
NS_IF_RELEASE(sStringBundleService);
NS_IF_RELEASE(sConsoleService);
sXPConnect = nullptr;
NS_IF_RELEASE(sSecurityManager);
NS_IF_RELEASE(sSystemPrincipal);
NS_IF_RELEASE(sNullSubjectPrincipal);
NS_IF_RELEASE(sParserService);
NS_IF_RELEASE(sIOService);
+ NS_IF_RELEASE(sUUIDGenerator);
NS_IF_RELEASE(sLineBreaker);
NS_IF_RELEASE(sWordBreaker);
NS_IF_RELEASE(sBidiKeyboard);
delete sAtomEventTable;
sAtomEventTable = nullptr;
delete sStringEventTable;
sStringEventTable = nullptr;
@@ -7146,16 +7155,29 @@ nsContentUtils::IsJavascriptMIMEType(con
if (aMIMEType.LowerCaseEqualsASCII(jsTypes[i])) {
return true;
}
}
return false;
}
+nsresult
+nsContentUtils::GenerateUUIDInPlace(nsID& aUUID)
+{
+ MOZ_ASSERT(sUUIDGenerator);
+
+ nsresult rv = sUUIDGenerator->GenerateUUIDInPlace(&aUUID);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
uint64_t
nsContentUtils::GetInnerWindowID(nsIRequest* aRequest)
{
// can't do anything if there's no nsIRequest!
if (!aRequest) {
return 0;
}
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -80,16 +80,17 @@ class nsIRequest;
class nsIRunnable;
class nsIScriptContext;
class nsIScriptSecurityManager;
class nsIStringBundle;
class nsIStringBundleService;
class nsISupportsArray;
class nsISupportsHashKey;
class nsIURI;
+class nsIUUIDGenerator;
class nsIWidget;
class nsIWordBreaker;
class nsIXPConnect;
class nsNodeInfoManager;
class nsPIDOMWindow;
class nsPresContext;
class nsStringBuffer;
class nsStringHashKey;
@@ -848,16 +849,21 @@ public:
* A helper function that parses a sandbox attribute (of an <iframe> or
* a CSP directive) and converts it to the set of flags used internally.
*
* @param sandboxAttr the sandbox attribute
* @return the set of flags (0 if sandboxAttr is null)
*/
static uint32_t ParseSandboxAttributeToFlags(const nsAttrValue* sandboxAttr);
+ /**
+ * Helper function that generates a UUID.
+ */
+ static nsresult GenerateUUIDInPlace(nsID& aUUID);
+
/**
* Fill (with the parameters given) the localized string named |aKey| in
* properties file |aFile|.
*/
private:
static nsresult FormatLocalizedString(PropertiesFile aFile,
const char* aKey,
@@ -2433,16 +2439,17 @@ private:
static nsIPrincipal *sSystemPrincipal;
static nsIPrincipal *sNullSubjectPrincipal;
static nsIParserService *sParserService;
static nsNameSpaceManager *sNameSpaceManager;
static nsIIOService *sIOService;
+ static nsIUUIDGenerator *sUUIDGenerator;
static bool sImgLoaderInitialized;
static void InitImgLoader();
// The following four members are initialized lazily
static imgLoader* sImgLoader;
static imgLoader* sPrivateImgLoader;
static imgICache* sImgCache;
--- a/dom/base/nsCopySupport.cpp
+++ b/dom/base/nsCopySupport.cpp
@@ -6,16 +6,17 @@
#include "nsCopySupport.h"
#include "nsIDocumentEncoder.h"
#include "nsISupports.h"
#include "nsIContent.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsIClipboard.h"
+#include "nsIFormControl.h"
#include "nsISelection.h"
#include "nsWidgetsCID.h"
#include "nsXPCOM.h"
#include "nsISupportsPrimitives.h"
#include "nsIDOMRange.h"
#include "nsRange.h"
#include "imgIContainer.h"
#include "nsIPresShell.h"
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -272,25 +272,25 @@ template<ActorFlavorEnum Flavor>
static bool
BuildClonedMessageData(typename BlobTraits<Flavor>::ConcreteContentManagerType* aManager,
const StructuredCloneData& aData,
ClonedMessageData& aClonedData)
{
SerializedStructuredCloneBuffer& buffer = aClonedData.data();
buffer.data = aData.mData;
buffer.dataLength = aData.mDataLength;
- const nsTArray<nsRefPtr<Blob>>& blobs = aData.mClosure.mBlobs;
- if (!blobs.IsEmpty()) {
+ const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = aData.mClosure.mBlobImpls;
+ if (!blobImpls.IsEmpty()) {
typedef typename BlobTraits<Flavor>::ProtocolType ProtocolType;
InfallibleTArray<ProtocolType*>& blobList = DataBlobs<Flavor>::Blobs(aClonedData);
- uint32_t length = blobs.Length();
+ uint32_t length = blobImpls.Length();
blobList.SetCapacity(length);
for (uint32_t i = 0; i < length; ++i) {
typename BlobTraits<Flavor>::BlobType* protocolActor =
- aManager->GetOrCreateActorForBlob(blobs[i]);
+ aManager->GetOrCreateActorForBlobImpl(blobImpls[i]);
if (!protocolActor) {
return false;
}
blobList.AppendElement(protocolActor);
}
}
return true;
}
@@ -318,29 +318,26 @@ UnpackClonedMessageData(const ClonedMess
const SerializedStructuredCloneBuffer& buffer = aData.data();
typedef typename BlobTraits<Flavor>::ProtocolType ProtocolType;
const InfallibleTArray<ProtocolType*>& blobs = DataBlobs<Flavor>::Blobs(aData);
StructuredCloneData cloneData;
cloneData.mData = buffer.data;
cloneData.mDataLength = buffer.dataLength;
if (!blobs.IsEmpty()) {
uint32_t length = blobs.Length();
- cloneData.mClosure.mBlobs.SetCapacity(length);
+ cloneData.mClosure.mBlobImpls.SetCapacity(length);
for (uint32_t i = 0; i < length; ++i) {
auto* blob =
static_cast<typename BlobTraits<Flavor>::BlobType*>(blobs[i]);
MOZ_ASSERT(blob);
nsRefPtr<BlobImpl> blobImpl = blob->GetBlobImpl();
MOZ_ASSERT(blobImpl);
- // This object will be duplicated with a correct parent before being
- // exposed to JS.
- nsRefPtr<Blob> domBlob = Blob::Create(nullptr, blobImpl);
- cloneData.mClosure.mBlobs.AppendElement(domBlob);
+ cloneData.mClosure.mBlobImpls.AppendElement(blobImpl);
}
}
return cloneData;
}
StructuredCloneData
mozilla::dom::ipc::UnpackClonedMessageDataForParent(const ClonedMessageData& aData)
{
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -54,29 +54,28 @@
#include "mozilla/unused.h"
// Other Classes
#include "mozilla/dom/BarProps.h"
#include "nsContentCID.h"
#include "nsLayoutStatics.h"
#include "nsCCUncollectableMarker.h"
#include "mozilla/dom/workers/Workers.h"
-#include "mozilla/dom/MessagePortList.h"
#include "mozilla/dom/ToJSValue.h"
#include "nsJSPrincipals.h"
#include "mozilla/Attributes.h"
#include "mozilla/Debug.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/EventStates.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/ProcessHangMonitor.h"
#include "AudioChannelService.h"
-#include "MessageEvent.h"
#include "nsAboutProtocolUtils.h"
#include "nsCharTraits.h" // NS_IS_HIGH/LOW_SURROGATE
+#include "PostMessageEvent.h"
// Interfaces Needed
#include "nsIFrame.h"
#include "nsCanvasFrame.h"
#include "nsIWidget.h"
#include "nsIWidgetListener.h"
#include "nsIBaseWindow.h"
#include "nsIDeviceSensors.h"
@@ -174,23 +173,19 @@
#include "nsFrameLoader.h"
#include "nsISupportsPrimitives.h"
#include "nsXPCOMCID.h"
#include "mozilla/Logging.h"
#include "prenv.h"
#include "prprf.h"
#include "mozilla/dom/MessageChannel.h"
-#include "mozilla/dom/MessagePort.h"
-#include "mozilla/dom/MessagePortBinding.h"
#include "mozilla/dom/indexedDB/IDBFactory.h"
#include "mozilla/dom/Promise.h"
-#include "mozilla/dom/StructuredCloneTags.h"
-
#ifdef MOZ_GAMEPAD
#include "mozilla/dom/Gamepad.h"
#include "mozilla/dom/GamepadService.h"
#endif
#include "mozilla/dom/VRDevice.h"
#include "nsRefreshDriver.h"
@@ -202,17 +197,16 @@
#include "nsHTMLDocument.h"
#include "nsWrapperCacheInlines.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "prrng.h"
#include "nsSandboxFlags.h"
#include "TimeChangeObserver.h"
#include "TouchCaret.h"
#include "mozilla/dom/AudioContext.h"
-#include "mozilla/dom/BlobBinding.h"
#include "mozilla/dom/BrowserElementDictionariesBinding.h"
#include "mozilla/dom/cache/CacheStorage.h"
#include "mozilla/dom/Console.h"
#include "mozilla/dom/Fetch.h"
#include "mozilla/dom/FunctionBinding.h"
#include "mozilla/dom/HashChangeEvent.h"
#include "mozilla/dom/MozSelfSupportBinding.h"
#include "mozilla/dom/PopStateEvent.h"
@@ -8033,377 +8027,16 @@ nsGlobalWindow::CallerInnerWindow()
}
// The calling window must be holding a reference, so we can return a weak
// pointer.
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(global);
return static_cast<nsGlobalWindow*>(win.get());
}
-/**
- * Class used to represent events generated by calls to Window.postMessage,
- * which asynchronously creates and dispatches events.
- */
-class PostMessageEvent : public nsRunnable
-{
- public:
- NS_DECL_NSIRUNNABLE
-
- PostMessageEvent(nsGlobalWindow* aSource,
- const nsAString& aCallerOrigin,
- nsGlobalWindow* aTargetWindow,
- nsIPrincipal* aProvidedPrincipal,
- bool aTrustedCaller)
- : mSource(aSource),
- mCallerOrigin(aCallerOrigin),
- mTargetWindow(aTargetWindow),
- mProvidedPrincipal(aProvidedPrincipal),
- mTrustedCaller(aTrustedCaller)
- {
- MOZ_COUNT_CTOR(PostMessageEvent);
- }
-
-protected:
- ~PostMessageEvent()
- {
- MOZ_COUNT_DTOR(PostMessageEvent);
- }
-
-public:
- JSAutoStructuredCloneBuffer& Buffer()
- {
- return mBuffer;
- }
-
- bool StoreISupports(nsISupports* aSupports)
- {
- mSupportsArray.AppendElement(aSupports);
- return true;
- }
-
- private:
- JSAutoStructuredCloneBuffer mBuffer;
- nsRefPtr<nsGlobalWindow> mSource;
- nsString mCallerOrigin;
- nsRefPtr<nsGlobalWindow> mTargetWindow;
- nsCOMPtr<nsIPrincipal> mProvidedPrincipal;
- bool mTrustedCaller;
- nsTArray<nsCOMPtr<nsISupports> > mSupportsArray;
-};
-
-namespace {
-
-struct StructuredCloneInfo {
- PostMessageEvent* event;
- bool subsumes;
- nsPIDOMWindow* window;
- nsRefPtrHashtable<nsRefPtrHashKey<MessagePortBase>, MessagePortBase> ports;
-};
-
-static JSObject*
-PostMessageReadStructuredClone(JSContext* cx,
- JSStructuredCloneReader* reader,
- uint32_t tag,
- uint32_t data,
- void* closure)
-{
- StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
- NS_ASSERTION(scInfo, "Must have scInfo!");
-
- if (tag == SCTAG_DOM_BLOB) {
- NS_ASSERTION(!data, "Data should be empty");
-
- // What we get back from the reader is a BlobImpl.
- // From that we create a new File.
- BlobImpl* blobImpl;
- if (JS_ReadBytes(reader, &blobImpl, sizeof(blobImpl))) {
- MOZ_ASSERT(blobImpl);
-
- // nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
- // called because the static analysis thinks dereferencing XPCOM objects
- // can GC (because in some cases it can!), and a return statement with a
- // JSObject* type means that JSObject* is on the stack as a raw pointer
- // while destructors are running.
- JS::Rooted<JS::Value> val(cx);
- {
- nsRefPtr<Blob> blob = Blob::Create(scInfo->window, blobImpl);
- if (!ToJSValue(cx, blob, &val)) {
- return nullptr;
- }
- }
-
- return &val.toObject();
- }
- }
-
- if (tag == SCTAG_DOM_FILELIST) {
- NS_ASSERTION(!data, "Data should be empty");
-
- nsISupports* supports;
- if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
- JS::Rooted<JS::Value> val(cx);
- if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, supports, &val))) {
- return val.toObjectOrNull();
- }
- }
- }
-
- const JSStructuredCloneCallbacks* runtimeCallbacks =
- js::GetContextStructuredCloneCallbacks(cx);
-
- if (runtimeCallbacks) {
- return runtimeCallbacks->read(cx, reader, tag, data, nullptr);
- }
-
- return nullptr;
-}
-
-static bool
-PostMessageWriteStructuredClone(JSContext* cx,
- JSStructuredCloneWriter* writer,
- JS::Handle<JSObject*> obj,
- void *closure)
-{
- StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
- NS_ASSERTION(scInfo, "Must have scInfo!");
-
- // See if this is a File/Blob object.
- {
- Blob* blob = nullptr;
- if (scInfo->subsumes && NS_SUCCEEDED(UNWRAP_OBJECT(Blob, obj, blob))) {
- BlobImpl* blobImpl = blob->Impl();
- if (JS_WriteUint32Pair(writer, SCTAG_DOM_BLOB, 0) &&
- JS_WriteBytes(writer, &blobImpl, sizeof(blobImpl))) {
- scInfo->event->StoreISupports(blobImpl);
- return true;
- }
- }
- }
-
- nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
- nsContentUtils::XPConnect()->
- GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
- if (wrappedNative) {
- uint32_t scTag = 0;
- nsISupports* supports = wrappedNative->Native();
-
- nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports);
- if (list && scInfo->subsumes)
- scTag = SCTAG_DOM_FILELIST;
-
- if (scTag)
- return JS_WriteUint32Pair(writer, scTag, 0) &&
- JS_WriteBytes(writer, &supports, sizeof(supports)) &&
- scInfo->event->StoreISupports(supports);
- }
-
- const JSStructuredCloneCallbacks* runtimeCallbacks =
- js::GetContextStructuredCloneCallbacks(cx);
-
- if (runtimeCallbacks) {
- return runtimeCallbacks->write(cx, writer, obj, nullptr);
- }
-
- return false;
-}
-
-static bool
-PostMessageReadTransferStructuredClone(JSContext* aCx,
- JSStructuredCloneReader* reader,
- uint32_t tag, void* aData,
- uint64_t aExtraData,
- void* aClosure,
- JS::MutableHandle<JSObject*> returnObject)
-{
- StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
- NS_ASSERTION(scInfo, "Must have scInfo!");
-
- if (tag == SCTAG_DOM_MAP_MESSAGEPORT) {
- MessagePort* port = static_cast<MessagePort*>(aData);
- port->BindToOwner(scInfo->window);
- scInfo->ports.Put(port, nullptr);
-
- JS::Rooted<JSObject*> obj(aCx, port->WrapObject(aCx, nullptr));
- if (JS_WrapObject(aCx, &obj)) {
- MOZ_ASSERT(port->GetOwner() == scInfo->window);
- returnObject.set(obj);
- }
-
- return true;
- }
-
- return false;
-}
-
-static bool
-PostMessageTransferStructuredClone(JSContext* aCx,
- JS::Handle<JSObject*> aObj,
- void* aClosure,
- uint32_t* aTag,
- JS::TransferableOwnership* aOwnership,
- void** aContent,
- uint64_t* aExtraData)
-{
- StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
- NS_ASSERTION(scInfo, "Must have scInfo!");
-
- MessagePortBase* port = nullptr;
- nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
- if (NS_SUCCEEDED(rv)) {
- nsRefPtr<MessagePortBase> newPort;
- if (scInfo->ports.Get(port, getter_AddRefs(newPort))) {
- // No duplicate.
- return false;
- }
-
- newPort = port->Clone();
- scInfo->ports.Put(port, newPort);
-
- *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
- *aOwnership = JS::SCTAG_TMO_CUSTOM;
- *aContent = newPort;
- *aExtraData = 0;
-
- return true;
- }
-
- return false;
-}
-
-void
-PostMessageFreeTransferStructuredClone(uint32_t aTag, JS::TransferableOwnership aOwnership,
- void *aContent, uint64_t aExtraData, void* aClosure)
-{
- StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
- NS_ASSERTION(scInfo, "Must have scInfo!");
-
- if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
- nsRefPtr<MessagePortBase> port(static_cast<MessagePort*>(aContent));
- scInfo->ports.Remove(port);
- }
-}
-
-const JSStructuredCloneCallbacks kPostMessageCallbacks = {
- PostMessageReadStructuredClone,
- PostMessageWriteStructuredClone,
- nullptr,
- PostMessageReadTransferStructuredClone,
- PostMessageTransferStructuredClone,
- PostMessageFreeTransferStructuredClone
-};
-
-} // anonymous namespace
-
-static PLDHashOperator
-PopulateMessagePortList(MessagePortBase* aKey, MessagePortBase* aValue, void* aClosure)
-{
- nsTArray<nsRefPtr<MessagePortBase> > *array =
- static_cast<nsTArray<nsRefPtr<MessagePortBase> > *>(aClosure);
-
- array->AppendElement(aKey);
- return PL_DHASH_NEXT;
-}
-
-NS_IMETHODIMP
-PostMessageEvent::Run()
-{
- MOZ_ASSERT(mTargetWindow->IsOuterWindow(),
- "should have been passed an outer window!");
- MOZ_ASSERT(!mSource || mSource->IsOuterWindow(),
- "should have been passed an outer window!");
-
- AutoJSAPI jsapi;
- jsapi.Init();
- JSContext* cx = jsapi.cx();
-
- // If we bailed before this point we're going to leak mMessage, but
- // that's probably better than crashing.
-
- nsRefPtr<nsGlobalWindow> targetWindow;
- if (mTargetWindow->IsClosedOrClosing() ||
- !(targetWindow = mTargetWindow->GetCurrentInnerWindowInternal()) ||
- targetWindow->IsClosedOrClosing())
- return NS_OK;
-
- MOZ_ASSERT(targetWindow->IsInnerWindow(),
- "we ordered an inner window!");
- JSAutoCompartment ac(cx, targetWindow->GetWrapperPreserveColor());
-
- // Ensure that any origin which might have been provided is the origin of this
- // window's document. Note that we do this *now* instead of when postMessage
- // is called because the target window might have been navigated to a
- // different location between then and now. If this check happened when
- // postMessage was called, it would be fairly easy for a malicious webpage to
- // intercept messages intended for another site by carefully timing navigation
- // of the target window so it changed location after postMessage but before
- // now.
- if (mProvidedPrincipal) {
- // Get the target's origin either from its principal or, in the case the
- // principal doesn't carry a URI (e.g. the system principal), the target's
- // document.
- nsIPrincipal* targetPrin = targetWindow->GetPrincipal();
- if (NS_WARN_IF(!targetPrin))
- return NS_OK;
-
- // Note: This is contrary to the spec with respect to file: URLs, which
- // the spec groups into a single origin, but given we intentionally
- // don't do that in other places it seems better to hold the line for
- // now. Long-term, we want HTML5 to address this so that we can
- // be compliant while being safer.
- if (!targetPrin->Equals(mProvidedPrincipal)) {
- return NS_OK;
- }
- }
-
- // Deserialize the structured clone data
- JS::Rooted<JS::Value> messageData(cx);
- StructuredCloneInfo scInfo;
- scInfo.event = this;
- scInfo.window = targetWindow;
-
- if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
- return NS_ERROR_DOM_DATA_CLONE_ERR;
- }
-
- // Create the event
- nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
- do_QueryInterface(static_cast<nsPIDOMWindow*>(targetWindow.get()));
- nsRefPtr<MessageEvent> event =
- new MessageEvent(eventTarget, nullptr, nullptr);
-
- event->InitMessageEvent(NS_LITERAL_STRING("message"), false /*non-bubbling */,
- false /*cancelable */, messageData, mCallerOrigin,
- EmptyString(), mSource);
-
- nsTArray<nsRefPtr<MessagePortBase> > ports;
- scInfo.ports.EnumerateRead(PopulateMessagePortList, &ports);
- event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()), ports));
-
- // We can't simply call dispatchEvent on the window because doing so ends
- // up flipping the trusted bit on the event, and we don't want that to
- // happen because then untrusted content can call postMessage on a chrome
- // window if it can get a reference to it.
-
- nsIPresShell *shell = targetWindow->mDoc->GetShell();
- nsRefPtr<nsPresContext> presContext;
- if (shell)
- presContext = shell->GetPresContext();
-
- event->SetTrusted(mTrustedCaller);
- WidgetEvent* internalEvent = event->GetInternalNSEvent();
-
- nsEventStatus status = nsEventStatus_eIgnore;
- EventDispatcher::Dispatch(static_cast<nsPIDOMWindow*>(mTargetWindow),
- presContext,
- internalEvent,
- static_cast<dom::Event*>(event.get()),
- &status);
- return NS_OK;
-}
-
void
nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
const nsAString& aTargetOrigin,
JS::Handle<JS::Value> aTransfer,
ErrorResult& aError)
{
FORWARD_TO_OUTER_OR_THROW(PostMessageMoz,
(aCx, aMessage, aTargetOrigin, aTransfer, aError),
@@ -8517,28 +8150,23 @@ nsGlobalWindow::PostMessageMoz(JSContext
new PostMessageEvent(nsContentUtils::IsCallerChrome() || !callerInnerWin
? nullptr
: callerInnerWin->GetOuterWindowInternal(),
origin,
this,
providedPrincipal,
nsContentUtils::IsCallerChrome());
- // We *must* clone the data here, or the JS::Value could be modified
- // by script
- StructuredCloneInfo scInfo;
- scInfo.event = event;
- scInfo.window = this;
-
nsIPrincipal* principal = GetPrincipal();
JS::Rooted<JS::Value> message(aCx, aMessage);
JS::Rooted<JS::Value> transfer(aCx, aTransfer);
- if (NS_FAILED(callerPrin->Subsumes(principal, &scInfo.subsumes)) ||
- !event->Buffer().write(aCx, message, transfer, &kPostMessageCallbacks,
- &scInfo)) {
+ bool subsumes;
+
+ if (NS_FAILED(callerPrin->Subsumes(principal, &subsumes)) ||
+ !event->Write(aCx, message, transfer, subsumes, this)) {
aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
aError = NS_DispatchToCurrentThread(event);
}
void
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -106,16 +106,17 @@ class External;
class Function;
class Gamepad;
class VRDevice;
class MediaQueryList;
class MozSelfSupport;
class Navigator;
class OwningExternalOrWindowProxy;
class Promise;
+class PostMessageEvent;
struct RequestInit;
class RequestOrUSVString;
class Selection;
class SpeechSynthesis;
class WakeLock;
namespace cache {
class CacheStorage;
} // namespace cache
@@ -1747,17 +1748,17 @@ protected:
bool mVRDevicesInitialized;
// The VRDevies for this window
nsTArray<nsRefPtr<mozilla::dom::VRDevice>> mVRDevices;
// Any attached HMD when fullscreen
nsRefPtr<mozilla::gfx::VRHMDInfo> mVRHMDInfo;
friend class nsDOMScriptableHelper;
friend class nsDOMWindowUtils;
- friend class PostMessageEvent;
+ friend class mozilla::dom::PostMessageEvent;
friend class DesktopNotification;
static WindowByIdTable* sWindowsById;
static bool sWarnedAboutWindowInternal;
};
inline nsISupports*
ToSupports(nsGlobalWindow *p)
--- a/dom/base/test/chrome.ini
+++ b/dom/base/test/chrome.ini
@@ -8,17 +8,16 @@ support-files =
file_bug1008126_worker.js
[test_anonymousContent_xul_window.xul]
[test_bug715041.xul]
[test_bug715041_removal.xul]
[test_domrequesthelper.xul]
[test_url.xul]
[test_console.xul]
-[test_messageChannel.xul]
[test_navigator_resolve_identity_xrays.xul]
[test_sendQueryContentAndSelectionSetEvent.html]
[test_bug1016960.html]
[test_bug357450.js]
[test_copypaste.xul]
[test_messagemanager_principal.html]
[test_messagemanager_send_principal.html]
skip-if = buildapp == 'mulet'
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -1,18 +1,14 @@
[DEFAULT]
support-files =
audio.ogg
iframe_bug976673.html
iframe_main_bug1022229.html
iframe_sandbox_bug1022229.html
- iframe_messageChannel_cloning.html
- iframe_messageChannel_chrome.html
- iframe_messageChannel_pingpong.html
- iframe_messageChannel_post.html
file_empty.html
iframe_postMessage_solidus.html
file_setname.html
345339_iframe.html
Ahem.ttf
accesscontrol.resource
accesscontrol.resource^headers^
badContentType.eventsource
@@ -281,25 +277,17 @@ skip-if = buildapp == 'mulet' || buildap
[test_gsp-standards.html]
[test_getFeature_with_perm.html]
[test_getFeature_without_perm.html]
[test_hasFeature.html]
[test_history_document_open.html]
[test_history_state_null.html]
[test_Image_constructor.html]
[test_innersize_scrollport.html]
-[test_messageChannel.html]
-[test_messageChannel_cloning.html]
-[test_messageChannel_pingpong.html]
-[test_messageChannel_post.html]
-[test_messageChannel_pref.html]
-[test_messageChannel_start.html]
[test_messagemanager_targetchain.html]
-[test_messageChannel_transferable.html]
-[test_messageChannel_unshipped.html]
[test_named_frames.html]
[test_navigator_resolve_identity.html]
[test_navigator_language.html]
[test_openDialogChromeOnly.html]
[test_open_null_features.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Fails on b2g-desktop, tracked in bug 1011874
[test_postMessage_solidus.html]
[test_screen_orientation.html]
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -724,19 +724,16 @@ DOMInterfaces = {
'MediaRecorder': {
'headerFile': 'MediaRecorder.h',
},
'MessagePort': {
'nativeType': 'mozilla::dom::MessagePortBase',
'headerFile': 'mozilla/dom/MessagePort.h',
- 'binaryNames': {
- 'postMessage': 'postMessageMoz',
- },
},
'MimeType': {
'headerFile' : 'nsMimeTypeArray.h',
'nativeType': 'nsMimeType',
},
'MimeTypeArray': {
--- a/dom/broadcastchannel/BroadcastChannel.cpp
+++ b/dom/broadcastchannel/BroadcastChannel.cpp
@@ -207,24 +207,25 @@ public:
SerializedStructuredCloneBuffer& buffer = message.data();
buffer.data = mData->mBuffer.data();
buffer.dataLength = mData->mBuffer.nbytes();
PBackgroundChild* backgroundManager = mActor->Manager();
MOZ_ASSERT(backgroundManager);
- const nsTArray<nsRefPtr<Blob>>& blobs = mData->mClosure.mBlobs;
+ const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = mData->mClosure.mBlobImpls;
+
+ if (!blobImpls.IsEmpty()) {
+ message.blobsChild().SetCapacity(blobImpls.Length());
- if (!blobs.IsEmpty()) {
- message.blobsChild().SetCapacity(blobs.Length());
-
- for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
+ for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
PBlobChild* blobChild =
- BackgroundChild::GetOrCreateActorForBlob(backgroundManager, blobs[i]);
+ BackgroundChild::GetOrCreateActorForBlobImpl(backgroundManager,
+ blobImpls[i]);
MOZ_ASSERT(blobChild);
message.blobsChild().AppendElement(blobChild);
}
}
mActor->SendPostMessage(message);
return NS_OK;
@@ -536,19 +537,19 @@ BroadcastChannel::PostMessageInternal(JS
{
nsRefPtr<BroadcastChannelMessage> data = new BroadcastChannelMessage();
if (!WriteStructuredClone(aCx, aMessage, data->mBuffer, data->mClosure)) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
- const nsTArray<nsRefPtr<Blob>>& blobs = data->mClosure.mBlobs;
- for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
- if (!blobs[i]->Impl()->MayBeClonedToOtherThreads()) {
+ const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = data->mClosure.mBlobImpls;
+ for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
+ if (!blobImpls[i]->MayBeClonedToOtherThreads()) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
}
PostMessageData(data);
}
--- a/dom/broadcastchannel/BroadcastChannelChild.cpp
+++ b/dom/broadcastchannel/BroadcastChannelChild.cpp
@@ -37,26 +37,25 @@ BroadcastChannelChild::~BroadcastChannel
MOZ_ASSERT(!mBC);
}
bool
BroadcastChannelChild::RecvNotify(const ClonedMessageData& aData)
{
// Make sure to retrieve all blobs from the message before returning to avoid
// leaking their actors.
- nsTArray<nsRefPtr<Blob>> blobs;
+ nsTArray<nsRefPtr<BlobImpl>> blobs;
if (!aData.blobsChild().IsEmpty()) {
blobs.SetCapacity(aData.blobsChild().Length());
for (uint32_t i = 0, len = aData.blobsChild().Length(); i < len; ++i) {
nsRefPtr<BlobImpl> impl =
static_cast<BlobChild*>(aData.blobsChild()[i])->GetBlobImpl();
- nsRefPtr<Blob> blob = Blob::Create(mBC ? mBC->GetOwner() : nullptr, impl);
- blobs.AppendElement(blob);
+ blobs.AppendElement(impl);
}
}
nsCOMPtr<DOMEventTargetHelper> helper = mBC;
nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(helper);
// This object has been already closed by content or is going to be deleted
// soon. No notify is required.
@@ -87,17 +86,17 @@ BroadcastChannelChild::RecvNotify(const
}
JSContext* cx = jsapi.cx();
const SerializedStructuredCloneBuffer& buffer = aData.data();
StructuredCloneData cloneData;
cloneData.mData = buffer.data;
cloneData.mDataLength = buffer.dataLength;
- cloneData.mClosure.mBlobs.SwapElements(blobs);
+ cloneData.mClosure.mBlobImpls.SwapElements(blobs);
JS::Rooted<JS::Value> value(cx, JS::NullValue());
if (cloneData.mDataLength && !ReadStructuredClone(cx, cloneData, &value)) {
JS_ClearPendingException(cx);
return false;
}
RootedDictionary<MessageEventInit> init(cx);
--- a/dom/ipc/StructuredCloneUtils.cpp
+++ b/dom/ipc/StructuredCloneUtils.cpp
@@ -45,33 +45,33 @@ Read(JSContext* aCx, JSStructuredCloneRe
if (aTag == SCTAG_DOM_BLOB) {
// nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
// called because the static analysis thinks dereferencing XPCOM objects
// can GC (because in some cases it can!), and a return statement with a
// JSObject* type means that JSObject* is on the stack as a raw pointer
// while destructors are running.
JS::Rooted<JS::Value> val(aCx);
{
- MOZ_ASSERT(aData < closure->mBlobs.Length());
- nsRefPtr<Blob> blob = closure->mBlobs[aData];
+ MOZ_ASSERT(aData < closure->mBlobImpls.Length());
+ nsRefPtr<BlobImpl> blobImpl = closure->mBlobImpls[aData];
#ifdef DEBUG
{
// Blob should not be mutable.
bool isMutable;
- MOZ_ASSERT(NS_SUCCEEDED(blob->GetMutable(&isMutable)));
+ MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
MOZ_ASSERT(!isMutable);
}
#endif
// Let's create a new blob with the correct parent.
nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
MOZ_ASSERT(global);
- nsRefPtr<Blob> newBlob = Blob::Create(global, blob->Impl());
+ nsRefPtr<Blob> newBlob = Blob::Create(global, blobImpl);
if (!ToJSValue(aCx, newBlob, &val)) {
return nullptr;
}
}
return &val.toObject();
}
@@ -88,18 +88,18 @@ Write(JSContext* aCx, JSStructuredCloneW
static_cast<StructuredCloneClosure*>(aClosure);
// See if the wrapped native is a File/Blob.
{
Blob* blob = nullptr;
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob)) &&
NS_SUCCEEDED(blob->SetMutable(false)) &&
JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
- closure->mBlobs.Length())) {
- closure->mBlobs.AppendElement(blob);
+ closure->mBlobImpls.Length())) {
+ closure->mBlobImpls.AppendElement(blob->Impl());
return true;
}
}
return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
}
const JSStructuredCloneCallbacks gCallbacks = {
--- a/dom/ipc/StructuredCloneUtils.h
+++ b/dom/ipc/StructuredCloneUtils.h
@@ -14,17 +14,17 @@
#include "js/StructuredClone.h"
namespace mozilla {
namespace dom {
struct
StructuredCloneClosure
{
- nsTArray<nsRefPtr<Blob>> mBlobs;
+ nsTArray<nsRefPtr<BlobImpl>> mBlobImpls;
};
struct
StructuredCloneData
{
StructuredCloneData() : mData(nullptr), mDataLength(0) {}
uint64_t* mData;
size_t mDataLength;
rename from dom/base/MessageChannel.cpp
rename to dom/messagechannel/MessageChannel.cpp
--- a/dom/base/MessageChannel.cpp
+++ b/dom/messagechannel/MessageChannel.cpp
@@ -4,101 +4,225 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MessageChannel.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/MessageChannelBinding.h"
#include "mozilla/dom/MessagePort.h"
+#include "mozilla/dom/Navigator.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerRunnable.h"
#include "nsContentUtils.h"
+#include "nsIDocument.h"
+#include "nsIPrincipal.h"
#include "nsPIDOMWindow.h"
+#include "nsServiceManagerUtils.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MessageChannel, mWindow, mPort1, mPort2)
NS_IMPL_CYCLE_COLLECTING_ADDREF(MessageChannel)
NS_IMPL_CYCLE_COLLECTING_RELEASE(MessageChannel)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MessageChannel)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
namespace {
- bool gPrefInitialized = false;
- bool gPrefEnabled = false;
+bool gPrefInitialized = false;
+bool gPrefEnabled = false;
-}
+bool
+CheckPermission(nsIPrincipal* aPrincipal, bool aCallerChrome)
+{
+ MOZ_ASSERT(NS_IsMainThread());
-/* static */ bool
-MessageChannel::Enabled(JSContext* aCx, JSObject* aObj)
-{
if (!gPrefInitialized) {
Preferences::AddBoolVarCache(&gPrefEnabled, "dom.messageChannel.enabled");
gPrefInitialized = true;
}
// Enabled by pref
if (gPrefEnabled) {
return true;
}
// Chrome callers are allowed.
- if (nsContentUtils::ThreadsafeIsCallerChrome()) {
+ if (aCallerChrome) {
return true;
}
- nsCOMPtr<nsIPrincipal> principal = nsContentUtils::SubjectPrincipal();
- MOZ_ASSERT(principal);
-
nsCOMPtr<nsIURI> uri;
- if (NS_FAILED(principal->GetURI(getter_AddRefs(uri))) || !uri) {
+ if (NS_FAILED(aPrincipal->GetURI(getter_AddRefs(uri))) || !uri) {
return false;
}
bool isResource = false;
if (NS_FAILED(uri->SchemeIs("resource", &isResource))) {
return false;
}
return isResource;
}
+nsIPrincipal*
+GetPrincipalFromWorkerPrivate(workers::WorkerPrivate* aWorkerPrivate)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsIPrincipal* principal = aWorkerPrivate->GetPrincipal();
+ if (principal) {
+ return principal;
+ }
+
+ // Walk up to our containing page
+ workers::WorkerPrivate* wp = aWorkerPrivate;
+ while (wp->GetParent()) {
+ wp = wp->GetParent();
+ }
+
+ nsPIDOMWindow* window = wp->GetWindow();
+ if (!window) {
+ return nullptr;
+ }
+
+ nsIDocument* doc = window->GetExtantDoc();
+ if (!doc) {
+ return nullptr;
+ }
+
+ return doc->NodePrincipal();
+}
+
+// A WorkerMainThreadRunnable to synchronously dispatch the call of
+// CheckPermission() from the worker thread to the main thread.
+class CheckPermissionRunnable final : public workers::WorkerMainThreadRunnable
+{
+public:
+ bool mResult;
+ bool mCallerChrome;
+
+ explicit CheckPermissionRunnable(workers::WorkerPrivate* aWorkerPrivate)
+ : workers::WorkerMainThreadRunnable(aWorkerPrivate)
+ , mResult(false)
+ , mCallerChrome(false)
+ {
+ MOZ_ASSERT(aWorkerPrivate);
+ aWorkerPrivate->AssertIsOnWorkerThread();
+ mCallerChrome = aWorkerPrivate->UsesSystemPrincipal();
+ }
+
+protected:
+ virtual bool
+ MainThreadRun() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsIPrincipal* principal = GetPrincipalFromWorkerPrivate(mWorkerPrivate);
+ if (!principal) {
+ return true;
+ }
+
+ bool isNullPrincipal;
+ nsresult rv = principal->GetIsNullPrincipal(&isNullPrincipal);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return true;
+ }
+
+ if (NS_WARN_IF(isNullPrincipal)) {
+ return true;
+ }
+
+ mResult = CheckPermission(principal, mCallerChrome);
+ return true;
+ }
+};
+
+} // anonymous namespace
+
+/* static */ bool
+MessageChannel::Enabled(JSContext* aCx, JSObject* aGlobal)
+{
+ if (NS_IsMainThread()) {
+ JS::Rooted<JSObject*> global(aCx, aGlobal);
+
+ nsCOMPtr<nsPIDOMWindow> win = Navigator::GetWindowFromGlobal(global);
+ if (!win) {
+ return false;
+ }
+
+ nsIDocument* doc = win->GetExtantDoc();
+ if (!doc) {
+ return false;
+ }
+
+ return CheckPermission(doc->NodePrincipal(),
+ nsContentUtils::IsCallerChrome());
+ }
+
+ workers::WorkerPrivate* workerPrivate =
+ workers::GetWorkerPrivateFromContext(aCx);
+ workerPrivate->AssertIsOnWorkerThread();
+
+ nsRefPtr<CheckPermissionRunnable> runnable =
+ new CheckPermissionRunnable(workerPrivate);
+ runnable->Dispatch(aCx);
+
+ return runnable->mResult;
+}
+
MessageChannel::MessageChannel(nsPIDOMWindow* aWindow)
: mWindow(aWindow)
{
- MOZ_COUNT_CTOR(MessageChannel);
-
- mPort1 = new MessagePort(mWindow);
- mPort2 = new MessagePort(mWindow);
-
- mPort1->Entangle(mPort2);
- mPort2->Entangle(mPort1);
}
MessageChannel::~MessageChannel()
{
- MOZ_COUNT_DTOR(MessageChannel);
}
JSObject*
MessageChannel::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return MessageChannelBinding::Wrap(aCx, this, aGivenProto);
}
/* static */ already_AddRefed<MessageChannel>
MessageChannel::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
{
+ // window can be null in workers.
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
- if (!window) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
+
+ nsID portUUID1;
+ aRv = nsContentUtils::GenerateUUIDInPlace(portUUID1);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ nsID portUUID2;
+ aRv = nsContentUtils::GenerateUUIDInPlace(portUUID2);
+ if (aRv.Failed()) {
return nullptr;
}
nsRefPtr<MessageChannel> channel = new MessageChannel(window);
+
+ channel->mPort1 = MessagePort::Create(window, portUUID1, portUUID2, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ channel->mPort2 = MessagePort::Create(window, portUUID2, portUUID1, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ channel->mPort1->UnshippedEntangle(channel->mPort2);
+ channel->mPort2->UnshippedEntangle(channel->mPort1);
+
return channel.forget();
}
} // namespace dom
} // namespace mozilla
rename from dom/base/MessageChannel.h
rename to dom/messagechannel/MessageChannel.h
--- a/dom/base/MessageChannel.h
+++ b/dom/messagechannel/MessageChannel.h
@@ -26,18 +26,16 @@ class MessageChannel final : public nsIS
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MessageChannel)
static bool Enabled(JSContext* aCx, JSObject* aGlobal);
public:
- explicit MessageChannel(nsPIDOMWindow* aWindow);
-
nsPIDOMWindow*
GetParentObject() const
{
return mWindow;
}
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -53,16 +51,17 @@ public:
MessagePort*
Port2() const
{
return mPort2;
}
private:
+ explicit MessageChannel(nsPIDOMWindow* aWindow);
~MessageChannel();
nsCOMPtr<nsPIDOMWindow> mWindow;
nsRefPtr<MessagePort> mPort1;
nsRefPtr<MessagePort> mPort2;
};
rename from dom/base/MessagePort.cpp
rename to dom/messagechannel/MessagePort.cpp
--- a/dom/base/MessagePort.cpp
+++ b/dom/messagechannel/MessagePort.cpp
@@ -1,516 +1,531 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MessagePort.h"
+
#include "MessageEvent.h"
+#include "MessagePortChild.h"
#include "mozilla/dom/BlobBinding.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/MessageChannel.h"
#include "mozilla/dom/MessagePortBinding.h"
+#include "mozilla/dom/MessagePortChild.h"
#include "mozilla/dom/MessagePortList.h"
+#include "mozilla/dom/PMessagePort.h"
#include "mozilla/dom/StructuredCloneTags.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerScope.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
#include "nsContentUtils.h"
#include "nsGlobalWindow.h"
#include "nsPresContext.h"
#include "ScriptSettings.h"
+#include "SharedMessagePortMessage.h"
+#include "nsIBFCacheEntry.h"
#include "nsIDocument.h"
#include "nsIDOMFileList.h"
#include "nsIPresShell.h"
+#include "nsISupportsPrimitives.h"
+#include "nsServiceManagerUtils.h"
+
+#ifdef XP_WIN
+#undef PostMessage
+#endif
+
+using namespace mozilla::dom::workers;
namespace mozilla {
namespace dom {
-class DispatchEventRunnable : public nsRunnable
-{
- friend class MessagePort;
-
- public:
- explicit DispatchEventRunnable(MessagePort* aPort)
- : mPort(aPort)
- {
- }
-
- NS_IMETHOD
- Run()
- {
- nsRefPtr<DispatchEventRunnable> mKungFuDeathGrip(this);
-
- mPort->mDispatchRunnable = nullptr;
- mPort->Dispatch();
-
- return NS_OK;
- }
-
- private:
- nsRefPtr<MessagePort> mPort;
-};
-
-class PostMessageRunnable : public nsRunnable
+class DispatchEventRunnable final : public nsICancelableRunnable
{
friend class MessagePort;
- public:
- NS_DECL_NSIRUNNABLE
+public:
+ NS_DECL_ISUPPORTS
- PostMessageRunnable()
- {
- }
+ explicit DispatchEventRunnable(MessagePort* aPort)
+ : mPort(aPort)
+ { }
- ~PostMessageRunnable()
- {
- }
-
- JSAutoStructuredCloneBuffer& Buffer()
- {
- return mBuffer;
- }
+ NS_IMETHOD
+ Run() override
+ {
+ MOZ_ASSERT(mPort);
+ MOZ_ASSERT(mPort->mDispatchRunnable == this);
+ mPort->mDispatchRunnable = nullptr;
+ mPort->Dispatch();
- bool StoreISupports(nsISupports* aSupports)
- {
- mSupportsArray.AppendElement(aSupports);
- return true;
- }
-
- void Dispatch(MessagePort* aPort)
- {
- mPort = aPort;
- NS_DispatchToCurrentThread(this);
- }
+ return NS_OK;
+ }
- private:
- nsRefPtr<MessagePort> mPort;
- JSAutoStructuredCloneBuffer mBuffer;
-
- nsTArray<nsCOMPtr<nsISupports> > mSupportsArray;
-};
+ NS_IMETHOD
+ Cancel() override
+ {
+ mPort = nullptr;
+ return NS_OK;
+ }
-namespace {
+private:
+ ~DispatchEventRunnable()
+ {}
-struct StructuredCloneInfo
-{
- PostMessageRunnable* mEvent;
- MessagePort* mPort;
- nsRefPtrHashtable<nsRefPtrHashKey<MessagePortBase>, MessagePortBase> mPorts;
+ nsRefPtr<MessagePort> mPort;
};
-static JSObject*
-PostMessageReadStructuredClone(JSContext* cx,
- JSStructuredCloneReader* reader,
- uint32_t tag,
- uint32_t data,
- void* closure)
-{
- StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
- NS_ASSERTION(scInfo, "Must have scInfo!");
-
- if (tag == SCTAG_DOM_BLOB) {
- NS_ASSERTION(!data, "Data should be empty");
-
- // What we get back from the reader is a BlobImpl.
- // From that we create a new File.
- BlobImpl* blobImpl;
- if (JS_ReadBytes(reader, &blobImpl, sizeof(blobImpl))) {
- MOZ_ASSERT(blobImpl);
+NS_IMPL_ISUPPORTS(DispatchEventRunnable, nsICancelableRunnable, nsIRunnable)
- // nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
- // called because the static analysis thinks dereferencing XPCOM objects
- // can GC (because in some cases it can!), and a return statement with a
- // JSObject* type means that JSObject* is on the stack as a raw pointer
- // while destructors are running.
- JS::Rooted<JS::Value> val(cx);
- {
- nsRefPtr<Blob> blob = Blob::Create(scInfo->mPort->GetParentObject(),
- blobImpl);
- if (!ToJSValue(cx, blob, &val)) {
- return nullptr;
- }
- }
-
- return &val.toObject();
- }
- }
-
- if (tag == SCTAG_DOM_FILELIST) {
- NS_ASSERTION(!data, "Data should be empty");
-
- nsISupports* supports;
- if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
- JS::Rooted<JS::Value> val(cx);
- if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, supports, &val))) {
- return val.toObjectOrNull();
- }
- }
- }
+class PostMessageRunnable final : public nsICancelableRunnable
+{
+public:
+ NS_DECL_ISUPPORTS
- const JSStructuredCloneCallbacks* runtimeCallbacks =
- js::GetContextStructuredCloneCallbacks(cx);
-
- if (runtimeCallbacks) {
- return runtimeCallbacks->read(cx, reader, tag, data, nullptr);
- }
-
- return nullptr;
-}
-
-static bool
-PostMessageWriteStructuredClone(JSContext* cx,
- JSStructuredCloneWriter* writer,
- JS::Handle<JSObject*> obj,
- void *closure)
-{
- StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
- NS_ASSERTION(scInfo, "Must have scInfo!");
-
- // See if this is a File/Blob object.
+ PostMessageRunnable(MessagePort* aPort, SharedMessagePortMessage* aData)
+ : mPort(aPort)
+ , mData(aData)
{
- Blob* blob = nullptr;
- if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, obj, blob))) {
- BlobImpl* blobImpl = blob->Impl();
- if (JS_WriteUint32Pair(writer, SCTAG_DOM_BLOB, 0) &&
- JS_WriteBytes(writer, &blobImpl, sizeof(blobImpl))) {
- scInfo->mEvent->StoreISupports(blobImpl);
- return true;
- }
- }
- }
-
- nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
- nsContentUtils::XPConnect()->
- GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
- if (wrappedNative) {
- uint32_t scTag = 0;
- nsISupports* supports = wrappedNative->Native();
-
- nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports);
- if (list) {
- scTag = SCTAG_DOM_FILELIST;
- }
-
- if (scTag) {
- return JS_WriteUint32Pair(writer, scTag, 0) &&
- JS_WriteBytes(writer, &supports, sizeof(supports)) &&
- scInfo->mEvent->StoreISupports(supports);
- }
+ MOZ_ASSERT(aPort);
+ MOZ_ASSERT(aData);
}
- const JSStructuredCloneCallbacks* runtimeCallbacks =
- js::GetContextStructuredCloneCallbacks(cx);
-
- if (runtimeCallbacks) {
- return runtimeCallbacks->write(cx, writer, obj, nullptr);
- }
-
- return false;
-}
+ NS_IMETHOD
+ Run() override
+ {
+ nsCOMPtr<nsIGlobalObject> globalObject;
-static bool
-PostMessageReadTransferStructuredClone(JSContext* aCx,
- JSStructuredCloneReader* reader,
- uint32_t tag, void* data,
- uint64_t unused,
- void* aClosure,
- JS::MutableHandle<JSObject*> returnObject)
-{
- StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
- NS_ASSERTION(scInfo, "Must have scInfo!");
+ if (NS_IsMainThread()) {
+ globalObject = do_QueryInterface(mPort->GetParentObject());
+ } else {
+ WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+ MOZ_ASSERT(workerPrivate);
+ globalObject = workerPrivate->GlobalScope();
+ }
- if (tag == SCTAG_DOM_MAP_MESSAGEPORT) {
- MessagePort* port = static_cast<MessagePort*>(data);
- port->BindToOwner(scInfo->mPort->GetOwner());
- scInfo->mPorts.Put(port, nullptr);
-
- JS::Rooted<JSObject*> obj(aCx, port->WrapObject(aCx, nullptr));
- if (!obj || !JS_WrapObject(aCx, &obj)) {
- return false;
+ AutoJSAPI jsapi;
+ if (!globalObject || !jsapi.Init(globalObject)) {
+ NS_WARNING("Failed to initialize AutoJSAPI object.");
+ return NS_ERROR_FAILURE;
}
- MOZ_ASSERT(port->GetOwner() == scInfo->mPort->GetOwner());
- returnObject.set(obj);
- return true;
- }
+ JSContext* cx = jsapi.cx();
- return false;
-}
+ nsTArray<nsRefPtr<MessagePort>> ports;
+ nsCOMPtr<nsPIDOMWindow> window =
+ do_QueryInterface(mPort->GetParentObject());
-static bool
-PostMessageTransferStructuredClone(JSContext* aCx,
- JS::Handle<JSObject*> aObj,
- void* aClosure,
- uint32_t* aTag,
- JS::TransferableOwnership* aOwnership,
- void** aContent,
- uint64_t *aExtraData)
-{
- StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
- NS_ASSERTION(scInfo, "Must have scInfo!");
+ JS::Rooted<JS::Value> value(cx);
+ if (!mData->mData.IsEmpty()) {
+ bool ok = ReadStructuredCloneWithTransfer(cx, mData->mData,
+ mData->mClosure,
+ &value, window, ports);
+ FreeStructuredClone(mData->mData, mData->mClosure);
- MessagePortBase *port = nullptr;
- nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
- if (NS_SUCCEEDED(rv)) {
- nsRefPtr<MessagePortBase> newPort;
- if (scInfo->mPorts.Get(port, getter_AddRefs(newPort))) {
- // No duplicate.
- return false;
+ if (!ok) {
+ return NS_ERROR_FAILURE;
+ }
}
- newPort = port->Clone();
- scInfo->mPorts.Put(port, newPort);
+ // The data should be already be cleaned.
+ MOZ_ASSERT(!mData->mData.Length());
+
+ // Create the event
+ nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
+ do_QueryInterface(mPort->GetOwner());
+ nsRefPtr<MessageEvent> event =
+ new MessageEvent(eventTarget, nullptr, nullptr);
- *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
- *aOwnership = JS::SCTAG_TMO_CUSTOM;
- *aContent = newPort;
- *aExtraData = 0;
+ event->InitMessageEvent(NS_LITERAL_STRING("message"),
+ false /* non-bubbling */,
+ false /* cancelable */, value, EmptyString(),
+ EmptyString(), nullptr);
+ event->SetTrusted(true);
+ event->SetSource(mPort);
- return true;
+ nsTArray<nsRefPtr<MessagePortBase>> array;
+ array.SetCapacity(ports.Length());
+ for (uint32_t i = 0; i < ports.Length(); ++i) {
+ array.AppendElement(ports[i]);
+ }
+
+ nsRefPtr<MessagePortList> portList =
+ new MessagePortList(static_cast<dom::Event*>(event.get()), array);
+ event->SetPorts(portList);
+
+ bool dummy;
+ mPort->DispatchEvent(static_cast<dom::Event*>(event.get()), &dummy);
+ return NS_OK;
}
- return false;
-}
-
-static void
-PostMessageFreeTransferStructuredClone(uint32_t aTag, JS::TransferableOwnership aOwnership,
- void* aData,
- uint64_t aExtraData,
- void* aClosure)
-{
- StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
- NS_ASSERTION(scInfo, "Must have scInfo!");
+ NS_IMETHOD
+ Cancel() override
+ {
+ mPort = nullptr;
+ mData = nullptr;
+ return NS_OK;
+ }
- if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
- MOZ_ASSERT(aOwnership == JS::SCTAG_TMO_CUSTOM);
- nsRefPtr<MessagePort> port(static_cast<MessagePort*>(aData));
- scInfo->mPorts.Remove(port);
- }
-}
+private:
+ ~PostMessageRunnable()
+ {}
-const JSStructuredCloneCallbacks kPostMessageCallbacks = {
- PostMessageReadStructuredClone,
- PostMessageWriteStructuredClone,
- nullptr,
- PostMessageReadTransferStructuredClone,
- PostMessageTransferStructuredClone,
- PostMessageFreeTransferStructuredClone
+ nsRefPtr<MessagePort> mPort;
+ nsRefPtr<SharedMessagePortMessage> mData;
};
-} // anonymous namespace
-
-static PLDHashOperator
-PopulateMessagePortList(MessagePortBase* aKey, MessagePortBase* aValue, void* aClosure)
-{
- nsTArray<nsRefPtr<MessagePortBase> > *array =
- static_cast<nsTArray<nsRefPtr<MessagePortBase> > *>(aClosure);
-
- array->AppendElement(aKey);
- return PL_DHASH_NEXT;
-}
-
-NS_IMETHODIMP
-PostMessageRunnable::Run()
-{
- MOZ_ASSERT(mPort);
-
- AutoJSAPI jsapi;
- if (NS_WARN_IF(!jsapi.Init(mPort->GetParentObject()))) {
- return NS_ERROR_UNEXPECTED;
- }
- JSContext* cx = jsapi.cx();
-
- // Deserialize the structured clone data
- JS::Rooted<JS::Value> messageData(cx);
- StructuredCloneInfo scInfo;
- scInfo.mEvent = this;
- scInfo.mPort = mPort;
-
- if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
- return NS_ERROR_DOM_DATA_CLONE_ERR;
- }
-
- // Create the event
- nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
- do_QueryInterface(mPort->GetOwner());
- nsRefPtr<MessageEvent> event =
- new MessageEvent(eventTarget, nullptr, nullptr);
-
- event->InitMessageEvent(NS_LITERAL_STRING("message"), false /* non-bubbling */,
- false /* cancelable */, messageData, EmptyString(),
- EmptyString(), nullptr);
- event->SetTrusted(true);
- event->SetSource(mPort);
-
- nsTArray<nsRefPtr<MessagePortBase> > ports;
- scInfo.mPorts.EnumerateRead(PopulateMessagePortList, &ports);
- event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()), ports));
-
- bool status;
- mPort->DispatchEvent(static_cast<dom::Event*>(event.get()), &status);
- return status ? NS_OK : NS_ERROR_FAILURE;
-}
+NS_IMPL_ISUPPORTS(PostMessageRunnable, nsICancelableRunnable, nsIRunnable)
MessagePortBase::MessagePortBase(nsPIDOMWindow* aWindow)
: DOMEventTargetHelper(aWindow)
{
}
MessagePortBase::MessagePortBase()
{
}
NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
- DOMEventTargetHelper)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mEntangledPort)
-
- // Custom unlink loop because this array contains nsRunnable objects
- // which are not cycle colleactable.
- while (!tmp->mMessageQueue.IsEmpty()) {
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageQueue[0]->mPort);
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageQueue[0]->mSupportsArray);
- tmp->mMessageQueue.RemoveElementAt(0);
- }
-
+ MessagePortBase)
if (tmp->mDispatchRunnable) {
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDispatchRunnable->mPort);
}
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessages);
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagesForTheOtherPort);
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mUnshippedEntangledPort);
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
- DOMEventTargetHelper)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEntangledPort)
-
- // Custom unlink loop because this array contains nsRunnable objects
- // which are not cycle colleactable.
- for (uint32_t i = 0, len = tmp->mMessageQueue.Length(); i < len; ++i) {
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageQueue[i]->mPort);
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageQueue[i]->mSupportsArray);
- }
-
+ MessagePortBase)
if (tmp->mDispatchRunnable) {
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDispatchRunnable->mPort);
}
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnshippedEntangledPort);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort)
-NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+ NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+NS_INTERFACE_MAP_END_INHERITING(MessagePortBase)
+
+NS_IMPL_ADDREF_INHERITED(MessagePort, MessagePortBase)
+NS_IMPL_RELEASE_INHERITED(MessagePort, MessagePortBase)
+
+namespace {
+
+class MessagePortFeature final : public workers::WorkerFeature
+{
+ MessagePort* mPort;
-NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper)
-NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper)
+public:
+ explicit MessagePortFeature(MessagePort* aPort)
+ : mPort(aPort)
+ {
+ MOZ_ASSERT(aPort);
+ MOZ_COUNT_CTOR(MessagePortFeature);
+ }
+
+ virtual bool Notify(JSContext* aCx, workers::Status aStatus) override
+ {
+ if (mPort && aStatus > Running) {
+ mPort->Close();
+ }
+
+ return true;
+ }
+
+private:
+ ~MessagePortFeature()
+ {
+ MOZ_COUNT_DTOR(MessagePortFeature);
+ }
+};
+
+} // anonymous namespace
MessagePort::MessagePort(nsPIDOMWindow* aWindow)
: MessagePortBase(aWindow)
+ , mInnerID(0)
, mMessageQueueEnabled(false)
+ , mIsKeptAlive(false)
{
+ mIdentifier = new MessagePortIdentifier();
+ mIdentifier->neutered() = true;
+ mIdentifier->sequenceId() = 0;
}
MessagePort::~MessagePort()
{
Close();
+ MOZ_ASSERT(!mWorkerFeature);
+}
+
+/* static */ already_AddRefed<MessagePort>
+MessagePort::Create(nsPIDOMWindow* aWindow, const nsID& aUUID,
+ const nsID& aDestinationUUID, ErrorResult& aRv)
+{
+ nsRefPtr<MessagePort> mp = new MessagePort(aWindow);
+ mp->Initialize(aUUID, aDestinationUUID, 1 /* 0 is an invalid sequence ID */,
+ false /* Neutered */, eStateUnshippedEntangled, aRv);
+ return mp.forget();
+}
+
+/* static */ already_AddRefed<MessagePort>
+MessagePort::Create(nsPIDOMWindow* aWindow,
+ const MessagePortIdentifier& aIdentifier,
+ ErrorResult& aRv)
+{
+ nsRefPtr<MessagePort> mp = new MessagePort(aWindow);
+ mp->Initialize(aIdentifier.uuid(), aIdentifier.destinationUuid(),
+ aIdentifier.sequenceId(), aIdentifier.neutered(),
+ eStateEntangling, aRv);
+ return mp.forget();
+}
+
+void
+MessagePort::UnshippedEntangle(MessagePort* aEntangledPort)
+{
+ MOZ_ASSERT(aEntangledPort);
+ MOZ_ASSERT(!mUnshippedEntangledPort);
+
+ mUnshippedEntangledPort = aEntangledPort;
+}
+
+void
+MessagePort::Initialize(const nsID& aUUID,
+ const nsID& aDestinationUUID,
+ uint32_t aSequenceID, bool mNeutered,
+ State aState, ErrorResult& aRv)
+{
+ MOZ_ASSERT(mIdentifier);
+ mIdentifier->uuid() = aUUID;
+ mIdentifier->destinationUuid() = aDestinationUUID;
+ mIdentifier->sequenceId() = aSequenceID;
+
+ mState = aState;
+ mNextStep = eNextStepNone;
+
+ if (mNeutered) {
+ mState = eStateDisentangled;
+ } else if (mState == eStateEntangling) {
+ ConnectToPBackground();
+ } else {
+ MOZ_ASSERT(mState == eStateUnshippedEntangled);
+ }
+
+ // The port has to keep itself alive until it's entangled.
+ UpdateMustKeepAlive();
+
+ if (NS_IsMainThread()) {
+ MOZ_ASSERT(GetOwner());
+ MOZ_ASSERT(GetOwner()->IsInnerWindow());
+ mInnerID = GetOwner()->WindowID();
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->AddObserver(this, "inner-window-destroyed", false);
+ }
+ } else {
+ WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+ MOZ_ASSERT(workerPrivate);
+ MOZ_ASSERT(!mWorkerFeature);
+
+ nsAutoPtr<WorkerFeature> feature(new MessagePortFeature(this));
+ JSContext* cx = workerPrivate->GetJSContext();
+ if (NS_WARN_IF(!workerPrivate->AddFeature(cx, feature))) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+
+ mWorkerFeature = Move(feature);
+ }
}
JSObject*
MessagePort::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return MessagePortBinding::Wrap(aCx, this, aGivenProto);
}
void
-MessagePort::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
- ErrorResult& aRv)
+MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ const Optional<Sequence<JS::Value>>& aTransferable,
+ ErrorResult& aRv)
{
- nsRefPtr<PostMessageRunnable> event = new PostMessageRunnable();
-
// We *must* clone the data here, or the JS::Value could be modified
// by script
- StructuredCloneInfo scInfo;
- scInfo.mEvent = event;
- scInfo.mPort = this;
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
if (aTransferable.WasPassed()) {
const Sequence<JS::Value>& realTransferable = aTransferable.Value();
+ // Here we want to check if the transerable object list contains
+ // this port. No other checks are done.
+ for (const JS::Value& value : realTransferable) {
+ if (!value.isObject()) {
+ continue;
+ }
+
+ MessagePortBase* port = nullptr;
+ nsresult rv = UNWRAP_OBJECT(MessagePort, &value.toObject(), port);
+ if (NS_FAILED(rv)) {
+ continue;
+ }
+
+ if (port == this) {
+ aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
+ return;
+ }
+ }
+
// The input sequence only comes from the generated bindings code, which
// ensures it is rooted.
JS::HandleValueArray elements =
JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
realTransferable.Elements());
JSObject* array =
JS_NewArrayObject(aCx, elements);
if (!array) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
+
transferable.setObject(*array);
}
- if (!event->Buffer().write(aCx, aMessage, transferable,
- &kPostMessageCallbacks, &scInfo)) {
+ nsRefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
+
+ if (!WriteStructuredCloneWithTransfer(aCx, aMessage, transferable,
+ data->mData, data->mClosure)) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
- if (!mEntangledPort) {
+ // This message has to be ignored.
+ if (mState > eStateEntangled) {
+ return;
+ }
+
+ // If we are unshipped we are connected to the other port on the same thread.
+ if (mState == eStateUnshippedEntangled) {
+ MOZ_ASSERT(mUnshippedEntangledPort);
+ mUnshippedEntangledPort->mMessages.AppendElement(data);
+ mUnshippedEntangledPort->Dispatch();
+ return;
+ }
+
+ // Not entangled yet, but already closed.
+ if (mNextStep != eNextStepNone) {
return;
}
- mEntangledPort->mMessageQueue.AppendElement(event);
- mEntangledPort->Dispatch();
+ RemoveDocFromBFCache();
+
+ // Not entangled yet.
+ if (mState == eStateEntangling) {
+ mMessagesForTheOtherPort.AppendElement(data);
+ return;
+ }
+
+ MOZ_ASSERT(mActor);
+ MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
+
+ nsAutoTArray<nsRefPtr<SharedMessagePortMessage>, 1> array;
+ array.AppendElement(data);
+
+ nsAutoTArray<MessagePortMessage, 1> messages;
+ SharedMessagePortMessage::FromSharedToMessagesChild(mActor, array, messages);
+ mActor->SendPostMessages(messages);
}
void
MessagePort::Start()
{
if (mMessageQueueEnabled) {
return;
}
mMessageQueueEnabled = true;
Dispatch();
}
void
MessagePort::Dispatch()
{
- if (!mMessageQueueEnabled || mMessageQueue.IsEmpty() || mDispatchRunnable) {
+ if (!mMessageQueueEnabled || mMessages.IsEmpty() || mDispatchRunnable ||
+ mState > eStateEntangled || mNextStep != eNextStepNone) {
return;
}
- nsRefPtr<PostMessageRunnable> event = mMessageQueue.ElementAt(0);
- mMessageQueue.RemoveElementAt(0);
+ nsRefPtr<SharedMessagePortMessage> data = mMessages.ElementAt(0);
+ mMessages.RemoveElementAt(0);
- event->Dispatch(this);
+ nsRefPtr<PostMessageRunnable> runnable = new PostMessageRunnable(this, data);
+
+ MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(runnable)));
mDispatchRunnable = new DispatchEventRunnable(this);
- NS_DispatchToCurrentThread(mDispatchRunnable);
+
+ MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(mDispatchRunnable)));
}
void
MessagePort::Close()
{
- if (!mEntangledPort) {
+ // Not entangled yet, but already closed.
+ if (mNextStep != eNextStepNone) {
+ return;
+ }
+
+ if (mState == eStateUnshippedEntangled) {
+ MOZ_ASSERT(mUnshippedEntangledPort);
+
+ // This avoids loops.
+ nsRefPtr<MessagePort> port = Move(mUnshippedEntangledPort);
+ MOZ_ASSERT(mUnshippedEntangledPort == nullptr);
+
+ mState = eStateDisentangled;
+ port->Close();
+
+ UpdateMustKeepAlive();
return;
}
- // This avoids loops.
- nsRefPtr<MessagePort> port = mEntangledPort;
- mEntangledPort = nullptr;
+ // Not entangled yet, we have to wait.
+ if (mState < eStateEntangling) {
+ mNextStep = eNextStepClose;
+ return;
+ }
+
+ if (mState > eStateEntangled) {
+ return;
+ }
- // Let's disentangle the 2 ports symmetrically.
- port->Close();
+ // We don't care about stopping the sending of messages because from now all
+ // the incoming messages will be ignored.
+ mState = eStateDisentangled;
+
+ MOZ_ASSERT(mActor);
+
+ mActor->SendClose();
+ mActor->SetPort(nullptr);
+ mActor = nullptr;
+
+ UpdateMustKeepAlive();
}
EventHandlerNonNull*
MessagePort::GetOnmessage()
{
if (NS_IsMainThread()) {
return GetEventHandler(nsGkAtoms::onmessage, EmptyString());
}
@@ -525,40 +540,328 @@ MessagePort::SetOnmessage(EventHandlerNo
} 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::Entangle(MessagePort* aMessagePort)
+MessagePort::Entangled(nsTArray<MessagePortMessage>& aMessages)
+{
+ MOZ_ASSERT(mState == eStateEntangling);
+
+ mState = eStateEntangled;
+
+ // If we have pending messages, these have to be sent.
+ if (!mMessagesForTheOtherPort.IsEmpty()) {
+ nsTArray<MessagePortMessage> messages;
+ SharedMessagePortMessage::FromSharedToMessagesChild(mActor,
+ mMessagesForTheOtherPort,
+ messages);
+ mMessagesForTheOtherPort.Clear();
+ mActor->SendPostMessages(messages);
+ }
+
+ // We must convert the messages into SharedMessagePortMessages to avoid leaks.
+ FallibleTArray<nsRefPtr<SharedMessagePortMessage>> data;
+ if (NS_WARN_IF(!SharedMessagePortMessage::FromMessagesToSharedChild(aMessages,
+ data))) {
+ // OOM, we cannot continue.
+ return;
+ }
+
+ if (mNextStep == eNextStepClose) {
+ Close();
+ return;
+ }
+
+ mMessages.AppendElements(data);
+
+ // We were waiting for the entangling callback in order to disentangle this
+ // port immediately after.
+ if (mNextStep == eNextStepDisentangle) {
+ StartDisentangling();
+ return;
+ }
+
+ MOZ_ASSERT(mNextStep == eNextStepNone);
+ Dispatch();
+}
+
+void
+MessagePort::StartDisentangling()
{
- MOZ_ASSERT(aMessagePort);
- MOZ_ASSERT(aMessagePort != this);
+ MOZ_ASSERT(mActor);
+ MOZ_ASSERT(mState == eStateEntangled);
+
+ mState = eStateDisentangling;
+ mNextStep = eNextStepNone;
+
+ // Sending this message we communicate to the parent actor that we don't want
+ // to receive any new messages. It is possible that a message has been
+ // already sent but not received yet. So we have to collect all of them and
+ // we send them in the SendDispatch() request.
+ mActor->SendStopSendingData();
+}
+
+void
+MessagePort::MessagesReceived(nsTArray<MessagePortMessage>& aMessages)
+{
+ MOZ_ASSERT(mState == eStateEntangled || mState == eStateDisentangling);
+ MOZ_ASSERT(mNextStep == eNextStepNone);
+ MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
+
+ RemoveDocFromBFCache();
+
+ FallibleTArray<nsRefPtr<SharedMessagePortMessage>> data;
+ if (!NS_WARN_IF(SharedMessagePortMessage::FromMessagesToSharedChild(aMessages,
+ data))) {
+ // OOM, We cannot continue.
+ return;
+ }
+
+ mMessages.AppendElements(data);
- Close();
+ if (mState == eStateEntangled) {
+ Dispatch();
+ }
+}
+
+void
+MessagePort::StopSendingDataConfirmed()
+{
+ MOZ_ASSERT(mState == eStateDisentangling);
+ MOZ_ASSERT(mActor);
+
+ Disentangle();
+}
- mEntangledPort = aMessagePort;
+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();
}
-already_AddRefed<MessagePortBase>
-MessagePort::Clone()
+bool
+MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier)
{
- nsRefPtr<MessagePort> newPort = new MessagePort(nullptr);
+ MOZ_ASSERT(mIdentifier);
+
+ // We can clone a port that has already been transfered. In this case, on the
+ // otherside will have a neutered port. Here we set neutered to true so that
+ // we are safe in case a early return.
+ aIdentifier.neutered() = true;
+
+ if (mState > eStateEntangled) {
+ return true;
+ }
+
+ // We already have a 'next step'. We have to consider this port as already
+ // cloned/closed/disentangled.
+ if (mNextStep != eNextStepNone) {
+ return true;
+ }
+
+ aIdentifier.uuid() = mIdentifier->uuid();
+ aIdentifier.destinationUuid() = mIdentifier->destinationUuid();
+ aIdentifier.sequenceId() = mIdentifier->sequenceId() + 1;
+ aIdentifier.neutered() = false;
+
+ // We have to entangle first.
+ if (mState == eStateUnshippedEntangled) {
+ MOZ_ASSERT(mUnshippedEntangledPort);
+ MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
+
+ // Disconnect the entangled port and connect it to PBackground.
+ mUnshippedEntangledPort->ConnectToPBackground();
+ mUnshippedEntangledPort = nullptr;
- // Move all the events in the port message queue of original port.
- newPort->mMessageQueue.SwapElements(mMessageQueue);
+ // In this case, we don't need to be connected to the PBackground service.
+ if (mMessages.IsEmpty()) {
+ aIdentifier.sequenceId() = mIdentifier->sequenceId();
+
+ mState = eStateDisentangled;
+ UpdateMustKeepAlive();
+ return true;
+ }
+
+ // Register this component to PBackground.
+ ConnectToPBackground();
+
+ mNextStep = eNextStepDisentangle;
+ return true;
+ }
- if (mEntangledPort) {
- nsRefPtr<MessagePort> port = mEntangledPort;
- mEntangledPort = nullptr;
+ // Not entangled yet, we have to wait.
+ if (mState < eStateEntangled) {
+ mNextStep = eNextStepDisentangle;
+ return true;
+ }
+
+ StartDisentangling();
+ return true;
+}
- newPort->Entangle(port);
- port->Entangle(newPort);
+void
+MessagePort::Closed()
+{
+ if (mState == eStateDisentangled) {
+ return;
+ }
+
+ mState = eStateDisentangled;
+
+ if (mActor) {
+ mActor->SetPort(nullptr);
+ mActor = nullptr;
}
- return newPort.forget();
+ UpdateMustKeepAlive();
+}
+
+void
+MessagePort::ConnectToPBackground()
+{
+ mState = eStateEntangling;
+
+ PBackgroundChild* actor =
+ mozilla::ipc::BackgroundChild::GetForCurrentThread();
+ if (actor) {
+ ActorCreated(actor);
+ } else {
+ if (NS_WARN_IF(
+ !mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(this))) {
+ MOZ_CRASH();
+ }
+ }
+}
+
+void
+MessagePort::ActorFailed()
+{
+ MOZ_CRASH("Failed to create a PBackgroundChild actor!");
+}
+
+void
+MessagePort::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(!mActor);
+ MOZ_ASSERT(mIdentifier);
+ MOZ_ASSERT(mState == eStateEntangling);
+
+ PMessagePortChild* actor =
+ aActor->SendPMessagePortConstructor(mIdentifier->uuid(),
+ mIdentifier->destinationUuid(),
+ mIdentifier->sequenceId());
+
+ mActor = static_cast<MessagePortChild*>(actor);
+ MOZ_ASSERT(mActor);
+
+ mActor->SetPort(this);
+}
+
+void
+MessagePort::UpdateMustKeepAlive()
+{
+ if (mState == eStateDisentangled && mIsKeptAlive) {
+ mIsKeptAlive = false;
+
+ if (mWorkerFeature) {
+ WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+ MOZ_ASSERT(workerPrivate);
+
+ workerPrivate->RemoveFeature(workerPrivate->GetJSContext(),
+ mWorkerFeature);
+ mWorkerFeature = nullptr;
+ }
+
+ Release();
+ return;
+ }
+
+ if (mState < eStateDisentangled && !mIsKeptAlive) {
+ mIsKeptAlive = true;
+ AddRef();
+ }
+}
+
+NS_IMETHODIMP
+MessagePort::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (strcmp(aTopic, "inner-window-destroyed")) {
+ return NS_OK;
+ }
+
+ // If the window id destroyed we have to release the reference that we are
+ // keeping.
+ if (!mIsKeptAlive) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
+ NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
+
+ uint64_t innerID;
+ nsresult rv = wrapper->GetData(&innerID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (innerID == mInnerID) {
+ nsCOMPtr<nsIObserverService> obs =
+ do_GetService("@mozilla.org/observer-service;1");
+ if (obs) {
+ obs->RemoveObserver(this, "inner-window-destroyed");
+ }
+
+ Close();
+ }
+
+ return NS_OK;
+}
+
+void
+MessagePort::RemoveDocFromBFCache()
+{
+ if (!NS_IsMainThread()) {
+ return;
+ }
+
+ nsPIDOMWindow* window = GetOwner();
+ MOZ_ASSERT(window);
+
+ nsIDocument* doc = window->GetExtantDoc();
+ if (!doc) {
+ return;
+ }
+
+ nsCOMPtr<nsIBFCacheEntry> bfCacheEntry = doc->GetBFCacheEntry();
+ if (!bfCacheEntry) {
+ return;
+ }
+
+ bfCacheEntry->RemoveFromBFCacheSync();
}
} // namespace dom
} // namespace mozilla
rename from dom/base/MessagePort.h
rename to dom/messagechannel/MessagePort.h
--- a/dom/base/MessagePort.h
+++ b/dom/messagechannel/MessagePort.h
@@ -4,112 +4,207 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_MessagePort_h
#define mozilla_dom_MessagePort_h
#include "mozilla/Attributes.h"
#include "mozilla/DOMEventTargetHelper.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
+#include "nsTArray.h"
+
+#ifdef XP_WIN
+#undef PostMessage
+#endif
class nsPIDOMWindow;
namespace mozilla {
namespace dom {
class DispatchEventRunnable;
-class PostMessageRunnable;
+class MessagePortChild;
+class MessagePortIdentifier;
+class MessagePortMessage;
+class SharedMessagePortMessage;
+
+namespace workers {
+class WorkerFeature;
+}
class MessagePortBase : public DOMEventTargetHelper
{
protected:
explicit MessagePortBase(nsPIDOMWindow* aWindow);
MessagePortBase();
public:
virtual void
- PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
- ErrorResult& aRv) = 0;
+ PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ const Optional<Sequence<JS::Value>>& aTransferable,
+ ErrorResult& aRv) = 0;
virtual void
Start() = 0;
virtual void
Close() = 0;
// The 'message' event handler has to call |Start()| method, so we
// cannot use IMPL_EVENT_HANDLER macro here.
virtual EventHandlerNonNull*
GetOnmessage() = 0;
virtual void
SetOnmessage(EventHandlerNonNull* aCallback) = 0;
// Duplicate this message port. This method is used by the Structured Clone
- // Algorithm and makes the new MessagePort active with the entangled
- // MessagePort of this object.
- virtual already_AddRefed<MessagePortBase>
- Clone() = 0;
+ // Algorithm and populates a MessagePortIdentifier object with the information
+ // useful to create new MessagePort.
+ virtual bool
+ CloneAndDisentangle(MessagePortIdentifier& aIdentifier) = 0;
};
class MessagePort final : public MessagePortBase
+ , public nsIIPCBackgroundChildCreateCallback
+ , public nsIObserver
{
friend class DispatchEventRunnable;
- friend class PostMessageRunnable;
public:
+ NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
+ NS_DECL_NSIOBSERVER
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort,
DOMEventTargetHelper)
- explicit MessagePort(nsPIDOMWindow* aWindow);
+ static already_AddRefed<MessagePort>
+ Create(nsPIDOMWindow* aWindow, const nsID& aUUID,
+ const nsID& aDestinationUUID, ErrorResult& aRv);
+
+ static already_AddRefed<MessagePort>
+ Create(nsPIDOMWindow* aWindow, const MessagePortIdentifier& aIdentifier,
+ ErrorResult& aRv);
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual void
- PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
- ErrorResult& aRv) override;
+ PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ const Optional<Sequence<JS::Value>>& aTransferable,
+ ErrorResult& aRv) override;
- virtual void
- Start() override;
+ virtual void Start() override;
- virtual void
- Close() override;
+ virtual void Close() override;
- virtual EventHandlerNonNull*
- GetOnmessage() override;
+ virtual EventHandlerNonNull* GetOnmessage() override;
- virtual void
- SetOnmessage(EventHandlerNonNull* aCallback) override;
+ virtual void SetOnmessage(EventHandlerNonNull* aCallback) override;
// Non WebIDL methods
- // This method entangles this MessagePort with another one.
- // If it is already entangled, it's disentangled first and enatangle to the
- // new one.
- void
- Entangle(MessagePort* aMessagePort);
+ void UnshippedEntangle(MessagePort* aEntangledPort);
+
+ virtual bool CloneAndDisentangle(MessagePortIdentifier& aIdentifier) override;
- virtual already_AddRefed<MessagePortBase>
- Clone() override;
+ // These methods are useful for MessagePortChild
+
+ void Entangled(nsTArray<MessagePortMessage>& aMessages);
+ void MessagesReceived(nsTArray<MessagePortMessage>& aMessages);
+ void StopSendingDataConfirmed();
+ void Closed();
private:
+ explicit MessagePort(nsPIDOMWindow* aWindow);
~MessagePort();
+ enum State {
+ // When a port is created by a MessageChannel it is entangled with the
+ // other. They both run on the same thread, same event loop and the
+ // messages are added to the queues without using PBackground actors.
+ // When one of the port is shipped, the state is changed to
+ // StateEntangling.
+ eStateUnshippedEntangled,
+
+ // If the port is closed or cloned when we are in this state, we set the
+ // mNextStep. This 'next' operation will be done when entangled() message
+ // is received.
+ eStateEntangling,
+
+ // When entangled() is received we send all the messages in the
+ // mMessagesForTheOtherPort to the actor and we change the state to
+ // StateEntangled. At this point the port is entangled with the other. We
+ // send and receive messages.
+ // If the port queue is not enabled, the received messages are stored in
+ // the mMessages.
+ eStateEntangled,
+
+ // When the port is cloned or disentangled we want to stop receiving
+ // messages. We call 'SendStopSendingData' to the actor and we wait for an
+ // answer. All the messages received between now and the
+ // 'StopSendingDataComfirmed are queued in the mMessages but not
+ // dispatched.
+ eStateDisentangling,
+
+ // When 'StopSendingDataConfirmed' is received, we can disentangle the port
+ // calling SendDisentangle in the actor because we are 100% sure that we
+ // don't receive any other message, so nothing will be lost.
+ // Disentangling the port we send all the messages from the mMessages
+ // though the actor.
+ eStateDisentangled
+ };
+
+ void Initialize(const nsID& aUUID, const nsID& aDestinationUUID,
+ uint32_t aSequenceID, bool mNeutered, State aState,
+ ErrorResult& aRv);
+
+ void ConnectToPBackground();
+
// Dispatch events from the Message Queue using a nsRunnable.
void Dispatch();
+ void StartDisentangling();
+ void Disentangle();
+
+ void RemoveDocFromBFCache();
+
+ // This method is meant to keep alive the MessagePort when this object is
+ // creating the actor and until the actor is entangled.
+ // We release the object when the port is closed or disentangled.
+ void UpdateMustKeepAlive();
+
+ nsAutoPtr<workers::WorkerFeature> mWorkerFeature;
+
nsRefPtr<DispatchEventRunnable> mDispatchRunnable;
- nsRefPtr<MessagePort> mEntangledPort;
+ nsRefPtr<MessagePortChild> mActor;
+
+ nsRefPtr<MessagePort> mUnshippedEntangledPort;
+
+ nsTArray<nsRefPtr<SharedMessagePortMessage>> mMessages;
+ nsTArray<nsRefPtr<SharedMessagePortMessage>> mMessagesForTheOtherPort;
+
+ nsAutoPtr<MessagePortIdentifier> mIdentifier;
+
+ uint64_t mInnerID;
- nsTArray<nsRefPtr<PostMessageRunnable> > mMessageQueue;
+ State mState;
+
+ // This 'nextStep' is used when we are waiting to be entangled but the
+ // content has called Clone() or Close().
+ enum {
+ eNextStepNone,
+ eNextStepDisentangle,
+ eNextStepClose
+ } mNextStep;
+
bool mMessageQueueEnabled;
+
+ bool mIsKeptAlive;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_MessagePort_h
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/MessagePortChild.cpp
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MessagePortChild.h"
+#include "MessagePort.h"
+#include "mozilla/dom/MessageEvent.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+
+namespace mozilla {
+namespace dom {
+
+bool
+MessagePortChild::RecvStopSendingDataConfirmed()
+{
+ MOZ_ASSERT(mPort);
+ mPort->StopSendingDataConfirmed();
+ MOZ_ASSERT(!mPort);
+ return true;
+}
+
+bool
+MessagePortChild::RecvEntangled(nsTArray<MessagePortMessage>&& aMessages)
+{
+ MOZ_ASSERT(mPort);
+ mPort->Entangled(aMessages);
+ return true;
+}
+
+bool
+MessagePortChild::RecvReceiveData(nsTArray<MessagePortMessage>&& aMessages)
+{
+ MOZ_ASSERT(mPort);
+ mPort->MessagesReceived(aMessages);
+ return true;
+}
+
+void
+MessagePortChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ if (mPort) {
+ mPort->Closed();
+ MOZ_ASSERT(!mPort);
+ }
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /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() {}
+
+ void SetPort(MessagePort* aPort)
+ {
+ mPort = aPort;
+ }
+
+private:
+ ~MessagePortChild()
+ {
+ MOZ_ASSERT(!mPort);
+ }
+
+ virtual bool
+ RecvEntangled(nsTArray<MessagePortMessage>&& aMessages) override;
+
+ virtual bool
+ RecvReceiveData(nsTArray<MessagePortMessage>&& aMessages) override;
+
+ virtual bool RecvStopSendingDataConfirmed() override;
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ // This is a raw pointer because this child is owned by this MessagePort.
+ MessagePort* mPort;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_MessagePortChild_h
rename from dom/base/MessagePortList.cpp
rename to dom/messagechannel/MessagePortList.cpp
rename from dom/base/MessagePortList.h
rename to dom/messagechannel/MessagePortList.h
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/MessagePortParent.cpp
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MessagePortParent.h"
+#include "MessagePortService.h"
+#include "SharedMessagePortMessage.h"
+#include "mozilla/unused.h"
+
+namespace mozilla {
+namespace dom {
+
+MessagePortParent::MessagePortParent(const nsID& aUUID)
+ : mService(MessagePortService::GetOrCreate())
+ , mUUID(aUUID)
+ , mEntangled(false)
+ , mCanSendData(true)
+{
+ MOZ_ASSERT(mService);
+}
+
+MessagePortParent::~MessagePortParent()
+{
+ MOZ_ASSERT(!mService);
+ MOZ_ASSERT(!mEntangled);
+}
+
+bool
+MessagePortParent::Entangle(const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID)
+{
+ if (!mService) {
+ NS_WARNING("Entangle is called after a shutdown!");
+ return false;
+ }
+
+ MOZ_ASSERT(!mEntangled);
+
+ return mService->RequestEntangling(this, aDestinationUUID, aSequenceID);
+}
+
+bool
+MessagePortParent::RecvPostMessages(nsTArray<MessagePortMessage>&& aMessages)
+{
+ // This converts the object in a data struct where we have BlobImpls.
+ FallibleTArray<nsRefPtr<SharedMessagePortMessage>> messages;
+ if (NS_WARN_IF(
+ !SharedMessagePortMessage::FromMessagesToSharedParent(aMessages,
+ messages))) {
+ return false;
+ }
+
+ if (!mEntangled) {
+ return false;
+ }
+
+ if (!mService) {
+ NS_WARNING("Entangle is called after a shutdown!");
+ return false;
+ }
+
+ if (messages.IsEmpty()) {
+ return false;
+ }
+
+ return mService->PostMessages(this, messages);
+}
+
+bool
+MessagePortParent::RecvDisentangle(nsTArray<MessagePortMessage>&& aMessages)
+{
+ // This converts the object in a data struct where we have BlobImpls.
+ FallibleTArray<nsRefPtr<SharedMessagePortMessage>> messages;
+ if (NS_WARN_IF(
+ !SharedMessagePortMessage::FromMessagesToSharedParent(aMessages,
+ messages))) {
+ return false;
+ }
+
+ if (!mEntangled) {
+ return false;
+ }
+
+ if (!mService) {
+ NS_WARNING("Entangle is called after a shutdown!");
+ return false;
+ }
+
+ if (!mService->DisentanglePort(this, messages)) {
+ return false;
+ }
+
+ CloseAndDelete();
+ return true;
+}
+
+bool
+MessagePortParent::RecvStopSendingData()
+{
+ if (!mEntangled) {
+ return true;
+ }
+
+ mCanSendData = false;
+ unused << SendStopSendingDataConfirmed();
+ return true;
+}
+
+bool
+MessagePortParent::RecvClose()
+{
+ if (mService) {
+ MOZ_ASSERT(mEntangled);
+
+ if (!mService->ClosePort(this)) {
+ return false;
+ }
+
+ Close();
+ }
+
+ MOZ_ASSERT(!mEntangled);
+
+ unused << Send__delete__(this);
+ return true;
+}
+
+void
+MessagePortParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ if (mService && mEntangled) {
+ // When the last parent is deleted, this service is freed but this cannot
+ // be done when the hashtables are written by CloseAll.
+ nsRefPtr<MessagePortService> kungFuDeathGrip = mService;
+ mService->ParentDestroy(this);
+ }
+}
+
+bool
+MessagePortParent::Entangled(const nsTArray<MessagePortMessage>& aMessages)
+{
+ MOZ_ASSERT(!mEntangled);
+ mEntangled = true;
+ return SendEntangled(aMessages);
+}
+
+void
+MessagePortParent::CloseAndDelete()
+{
+ Close();
+ unused << Send__delete__(this);
+}
+
+void
+MessagePortParent::Close()
+{
+ mService = nullptr;
+ mEntangled = false;
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/MessagePortParent.h
@@ -0,0 +1,61 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_MessagePortParent_h
+#define mozilla_dom_MessagePortParent_h
+
+#include "mozilla/dom/PMessagePortParent.h"
+
+namespace mozilla {
+namespace dom {
+
+class MessagePortService;
+
+class MessagePortParent final : public PMessagePortParent
+{
+public:
+ explicit MessagePortParent(const nsID& aUUID);
+ ~MessagePortParent();
+
+ bool Entangle(const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID);
+
+ bool Entangled(const nsTArray<MessagePortMessage>& aMessages);
+
+ void Close();
+ void CloseAndDelete();
+
+ bool CanSendData() const
+ {
+ return mCanSendData;
+ }
+
+ const nsID& ID() const
+ {
+ return mUUID;
+ }
+
+private:
+ virtual bool RecvPostMessages(nsTArray<MessagePortMessage>&& aMessages)
+ override;
+
+ virtual bool RecvDisentangle(nsTArray<MessagePortMessage>&& aMessages)
+ override;
+
+ virtual bool RecvStopSendingData() override;
+
+ virtual bool RecvClose() override;
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ nsRefPtr<MessagePortService> mService;
+ const nsID mUUID;
+ bool mEntangled;
+ bool mCanSendData;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_MessagePortParent_h
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/MessagePortService.cpp
@@ -0,0 +1,328 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MessagePortService.h"
+#include "MessagePortParent.h"
+#include "SharedMessagePortMessage.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/unused.h"
+#include "nsDataHashtable.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+StaticRefPtr<MessagePortService> gInstance;
+} // anonymous namespace
+
+class MessagePortService::MessagePortServiceData final
+{
+public:
+ explicit MessagePortServiceData(const nsID& aDestinationUUID)
+ : mDestinationUUID(aDestinationUUID)
+ , mSequenceID(1)
+ , mParent(nullptr)
+ {
+ MOZ_COUNT_CTOR(MessagePortServiceData);
+ }
+
+ MessagePortServiceData(const MessagePortServiceData& aOther) = delete;
+ MessagePortServiceData& operator=(const MessagePortServiceData&) = delete;
+
+ ~MessagePortServiceData()
+ {
+ MOZ_COUNT_DTOR(MessagePortServiceData);
+ }
+
+ nsID mDestinationUUID;
+
+ uint32_t mSequenceID;
+ MessagePortParent* mParent;
+
+ struct NextParent
+ {
+ uint32_t mSequenceID;
+ // MessagePortParent keeps the service alive, and we don't want a cycle.
+ MessagePortParent* mParent;
+ };
+
+ FallibleTArray<NextParent> mNextParents;
+ FallibleTArray<nsRefPtr<SharedMessagePortMessage>> mMessages;
+};
+
+/* static */ MessagePortService*
+MessagePortService::GetOrCreate()
+{
+ if (!gInstance) {
+ gInstance = new MessagePortService();
+ }
+
+ return gInstance;
+}
+
+bool
+MessagePortService::RequestEntangling(MessagePortParent* aParent,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID)
+{
+ MOZ_ASSERT(aParent);
+ MessagePortServiceData* data;
+
+ // If we don't have a MessagePortServiceData, we must create 2 of them for
+ // both ports.
+ if (!mPorts.Get(aParent->ID(), &data)) {
+ // Create the MessagePortServiceData for the destination.
+ if (mPorts.Get(aDestinationUUID, nullptr)) {
+ MOZ_ASSERT(false, "The creation of the 2 ports should be in sync.");
+ return false;
+ }
+
+ data = new MessagePortServiceData(aParent->ID());
+ mPorts.Put(aDestinationUUID, data);
+
+ data = new MessagePortServiceData(aDestinationUUID);
+ mPorts.Put(aParent->ID(), data);
+ }
+
+ // This is a security check.
+ if (!data->mDestinationUUID.Equals(aDestinationUUID)) {
+ MOZ_ASSERT(false, "DestinationUUIDs do not match!");
+ return false;
+ }
+
+ if (aSequenceID < data->mSequenceID) {
+ MOZ_ASSERT(false, "Invalid sequence ID!");
+ return false;
+ }
+
+ if (aSequenceID == data->mSequenceID) {
+ if (data->mParent) {
+ MOZ_ASSERT(false, "Two ports cannot have the same sequenceID.");
+ return false;
+ }
+
+ // We activate this port, sending all the messages.
+ data->mParent = aParent;
+ FallibleTArray<MessagePortMessage> array;
+ if (!SharedMessagePortMessage::FromSharedToMessagesParent(aParent,
+ data->mMessages,
+ array)) {
+ return false;
+ }
+
+ data->mMessages.Clear();
+ return aParent->Entangled(array);
+ }
+
+ // This new parent will be the next one when a Disentangle request is
+ // received from the current parent.
+ MessagePortServiceData::NextParent* nextParent =
+ data->mNextParents.AppendElement(mozilla::fallible);
+ if (!nextParent) {
+ return false;
+ }
+
+ nextParent->mSequenceID = aSequenceID;
+ nextParent->mParent = aParent;
+
+ return true;
+}
+
+bool
+MessagePortService::DisentanglePort(
+ MessagePortParent* aParent,
+ FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aMessages)
+{
+ MessagePortServiceData* data;
+ if (!mPorts.Get(aParent->ID(), &data)) {
+ MOZ_ASSERT(false, "Unknown MessagePortParent should not happen.");
+ return false;
+ }
+
+ if (data->mParent != aParent) {
+ MOZ_ASSERT(false, "DisentanglePort() should be called just from the correct parent.");
+ return false;
+ }
+
+ // Let's put the messages in the correct order. |aMessages| contains the
+ // unsent messages so they have to go first.
+ if (!aMessages.AppendElements(data->mMessages, mozilla::fallible)) {
+ return false;
+ }
+
+ data->mMessages.Clear();
+
+ ++data->mSequenceID;
+
+ // If we don't have a parent, we have to store the pending messages and wait.
+ uint32_t index = 0;
+ MessagePortParent* nextParent = nullptr;
+ for (; index < data->mNextParents.Length(); ++index) {
+ if (data->mNextParents[index].mSequenceID == data->mSequenceID) {
+ nextParent = data->mNextParents[index].mParent;
+ break;
+ }
+ }
+
+ // We didn't find the parent.
+ if (!nextParent) {
+ data->mMessages.SwapElements(aMessages);
+ data->mParent = nullptr;
+ return true;
+ }
+
+ data->mParent = nextParent;
+ data->mNextParents.RemoveElementAt(index);
+
+ FallibleTArray<MessagePortMessage> array;
+ if (!SharedMessagePortMessage::FromSharedToMessagesParent(data->mParent,
+ aMessages,
+ array)) {
+ return false;
+ }
+
+ unused << data->mParent->Entangled(array);
+ return true;
+}
+
+bool
+MessagePortService::ClosePort(MessagePortParent* aParent)
+{
+ MessagePortServiceData* data;
+ if (!mPorts.Get(aParent->ID(), &data)) {
+ MOZ_ASSERT(false, "Unknown MessagePortParent should not happend.");
+ return false;
+ }
+
+ if (data->mParent != aParent) {
+ MOZ_ASSERT(false, "ClosePort() should be called just from the correct parent.");
+ return false;
+ }
+
+ if (!data->mNextParents.IsEmpty()) {
+ MOZ_ASSERT(false, "ClosePort() should be called when there are not next parents.");
+ return false;
+ }
+
+ // We don't want to send a message to this parent.
+ data->mParent = nullptr;
+
+ CloseAll(aParent->ID());
+ return true;
+}
+
+#ifdef DEBUG
+PLDHashOperator
+MessagePortService::CloseAllDebugCheck(const nsID& aID,
+ MessagePortServiceData* aData,
+ void* aPtr)
+{
+ nsID* id = static_cast<nsID*>(aPtr);
+ MOZ_ASSERT(!id->Equals(aID));
+ return PL_DHASH_NEXT;
+}
+#endif
+
+void
+MessagePortService::CloseAll(const nsID& aUUID)
+{
+ MessagePortServiceData* data;
+ if (!mPorts.Get(aUUID, &data)) {
+ MaybeShutdown();
+ return;
+ }
+
+ if (data->mParent) {
+ data->mParent->Close();
+ }
+
+ for (const MessagePortServiceData::NextParent& parent : data->mNextParents) {
+ parent.mParent->CloseAndDelete();
+ }
+
+ nsID destinationUUID = data->mDestinationUUID;
+ mPorts.Remove(aUUID);
+
+ CloseAll(destinationUUID);
+
+#ifdef DEBUG
+ mPorts.EnumerateRead(CloseAllDebugCheck, const_cast<nsID*>(&aUUID));
+#endif
+
+ MaybeShutdown();
+}
+
+// This service can be dismissed when there are not active ports.
+void
+MessagePortService::MaybeShutdown()
+{
+ if (mPorts.Count() == 0) {
+ gInstance = nullptr;
+ }
+}
+
+bool
+MessagePortService::PostMessages(
+ MessagePortParent* aParent,
+ FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aMessages)
+{
+ MessagePortServiceData* data;
+ if (!mPorts.Get(aParent->ID(), &data)) {
+ MOZ_ASSERT(false, "Unknown MessagePortParent should not happend.");
+ return false;
+ }
+
+ if (data->mParent != aParent) {
+ MOZ_ASSERT(false, "PostMessages() should be called just from the correct parent.");
+ return false;
+ }
+
+ MOZ_ALWAYS_TRUE(mPorts.Get(data->mDestinationUUID, &data));
+
+ if (!data->mMessages.AppendElements(aMessages, mozilla::fallible)) {
+ return false;
+ }
+
+ // If the parent can send data to the child, let's proceed.
+ if (data->mParent && data->mParent->CanSendData()) {
+ FallibleTArray<MessagePortMessage> messages;
+ if (!SharedMessagePortMessage::FromSharedToMessagesParent(data->mParent,
+ data->mMessages,
+ messages)) {
+ return false;
+ }
+
+ data->mMessages.Clear();
+ unused << data->mParent->SendReceiveData(messages);
+ }
+
+ return true;
+}
+
+void
+MessagePortService::ParentDestroy(MessagePortParent* aParent)
+{
+ // This port has already been destroyed.
+ MessagePortServiceData* data;
+ if (!mPorts.Get(aParent->ID(), &data)) {
+ return;
+ }
+
+ if (data->mParent != aParent) {
+ // We don't want to send a message to this parent.
+ for (uint32_t i = 0; i < data->mNextParents.Length(); ++i) {
+ if (aParent == data->mNextParents[i].mParent) {
+ data->mNextParents.RemoveElementAt(i);
+ break;
+ }
+ }
+ }
+
+ CloseAll(aParent->ID());
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/MessagePortService.h
@@ -0,0 +1,61 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_MessagePortService_h
+#define mozilla_dom_MessagePortService_h
+
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace dom {
+
+class MessagePortParent;
+class SharedMessagePortMessage;
+
+class MessagePortService final
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(MessagePortService)
+
+ static MessagePortService* GetOrCreate();
+
+ bool RequestEntangling(MessagePortParent* aParent,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID);
+
+ bool DisentanglePort(
+ MessagePortParent* aParent,
+ FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aMessages);
+
+ bool ClosePort(MessagePortParent* aParent);
+
+ bool PostMessages(
+ MessagePortParent* aParent,
+ FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aMessages);
+
+ void ParentDestroy(MessagePortParent* aParent);
+
+private:
+ ~MessagePortService() {}
+
+ void CloseAll(const nsID& aUUID);
+ void MaybeShutdown();
+
+ class MessagePortServiceData;
+
+#ifdef DEBUG
+ static PLDHashOperator
+ CloseAllDebugCheck(const nsID& aID, MessagePortServiceData* aData,
+ void* aPtr);
+#endif
+
+ nsClassHashtable<nsIDHashKey, MessagePortServiceData> mPorts;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_MessagePortService_h
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/MessagePortUtils.cpp
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MessagePortUtils.h"
+#include "MessagePort.h"
+#include "mozilla/dom/BlobBinding.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/MessagePortBinding.h"
+#include "mozilla/dom/StructuredCloneTags.h"
+
+namespace mozilla {
+namespace dom {
+namespace messageport {
+
+namespace {
+
+struct MOZ_STACK_CLASS StructuredCloneClosureInternal
+{
+ StructuredCloneClosureInternal(
+ StructuredCloneClosure& aClosure, nsPIDOMWindow* aWindow)
+ : mClosure(aClosure)
+ , mWindow(aWindow)
+ { }
+
+ StructuredCloneClosure& mClosure;
+ nsPIDOMWindow* mWindow;
+ nsTArray<nsRefPtr<MessagePort>> mMessagePorts;
+ nsTArray<nsRefPtr<MessagePortBase>> mTransferredPorts;
+};
+
+struct MOZ_STACK_CLASS StructuredCloneClosureInternalReadOnly
+{
+ StructuredCloneClosureInternalReadOnly(
+ const StructuredCloneClosure& aClosure, nsPIDOMWindow* aWindow)
+ : mClosure(aClosure)
+ , mWindow(aWindow)
+ { }
+
+ const StructuredCloneClosure& mClosure;
+ nsPIDOMWindow* mWindow;
+ nsTArray<nsRefPtr<MessagePort>> mMessagePorts;
+ nsTArray<nsRefPtr<MessagePortBase>> mTransferredPorts;
+};
+
+void
+Error(JSContext* aCx, uint32_t aErrorId)
+{
+ if (NS_IsMainThread()) {
+ NS_DOMStructuredCloneError(aCx, aErrorId);
+ } else {
+ Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+ }
+}
+
+JSObject*
+Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
+ uint32_t aData, void* aClosure)
+{
+ MOZ_ASSERT(aClosure);
+
+ auto* closure = static_cast<StructuredCloneClosureInternalReadOnly*>(aClosure);
+
+ if (aTag == SCTAG_DOM_BLOB) {
+ // nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
+ // called because the static analysis thinks dereferencing XPCOM objects
+ // can GC (because in some cases it can!), and a return statement with a
+ // JSObject* type means that JSObject* is on the stack as a raw pointer
+ // while destructors are running.
+ JS::Rooted<JS::Value> val(aCx);
+ {
+ MOZ_ASSERT(aData < closure->mClosure.mBlobImpls.Length());
+ nsRefPtr<BlobImpl> blobImpl = closure->mClosure.mBlobImpls[aData];
+
+#ifdef DEBUG
+ {
+ // Blob should not be mutable.
+ bool isMutable;
+ MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
+ MOZ_ASSERT(!isMutable);
+ }
+#endif
+
+ // Let's create a new blob with the correct parent.
+ nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
+ MOZ_ASSERT(global);
+
+ nsRefPtr<Blob> newBlob = Blob::Create(global, blobImpl);
+ if (!ToJSValue(aCx, newBlob, &val)) {
+ return nullptr;
+ }
+ }
+
+ return &val.toObject();
+ }
+
+ return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr);
+}
+
+bool
+Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
+ JS::Handle<JSObject*> aObj, void* aClosure)
+{
+ MOZ_ASSERT(aClosure);
+
+ auto* closure = static_cast<StructuredCloneClosureInternal*>(aClosure);
+
+ // See if the wrapped native is a File/Blob.
+ {
+ Blob* blob = nullptr;
+ if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob)) &&
+ NS_SUCCEEDED(blob->SetMutable(false)) &&
+ JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
+ closure->mClosure.mBlobImpls.Length())) {
+ closure->mClosure.mBlobImpls.AppendElement(blob->Impl());
+ return true;
+ }
+ }
+
+ return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
+}
+
+bool
+ReadTransfer(JSContext* aCx, JSStructuredCloneReader* aReader,
+ uint32_t aTag, void* aContent, uint64_t aExtraData,
+ void* aClosure, JS::MutableHandle<JSObject*> aReturnObject)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aClosure);
+
+ auto* closure = static_cast<StructuredCloneClosureInternalReadOnly*>(aClosure);
+
+ if (aTag != SCTAG_DOM_MAP_MESSAGEPORT) {
+ return false;
+ }
+
+ MOZ_ASSERT(aContent == 0);
+ MOZ_ASSERT(aExtraData < closure->mClosure.mMessagePortIdentifiers.Length());
+
+ ErrorResult rv;
+ nsRefPtr<MessagePort> port =
+ MessagePort::Create(closure->mWindow,
+ closure->mClosure.mMessagePortIdentifiers[(uint32_t)aExtraData],
+ rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ return false;
+ }
+
+ closure->mMessagePorts.AppendElement(port);
+
+ JS::Rooted<JS::Value> value(aCx);
+ if (!GetOrCreateDOMReflector(aCx, port, &value)) {
+ JS_ClearPendingException(aCx);
+ return false;
+ }
+
+ aReturnObject.set(&value.toObject());
+ return true;
+}
+
+bool
+WriteTransfer(JSContext* aCx, JS::Handle<JSObject*> aObj, void* aClosure,
+ uint32_t* aTag, JS::TransferableOwnership* aOwnership,
+ void** aContent, uint64_t* aExtraData)
+{
+ MOZ_ASSERT(aClosure);
+
+ auto* closure = static_cast<StructuredCloneClosureInternal*>(aClosure);
+
+ MessagePortBase* port = nullptr;
+ nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ if (closure->mTransferredPorts.Contains(port)) {
+ // No duplicates.
+ return false;
+ }
+
+ MessagePortIdentifier identifier;
+ if (!port->CloneAndDisentangle(identifier)) {
+ return false;
+ }
+
+ closure->mClosure.mMessagePortIdentifiers.AppendElement(identifier);
+ closure->mTransferredPorts.AppendElement(port);
+
+ *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
+ *aOwnership = JS::SCTAG_TMO_CUSTOM;
+ *aContent = nullptr;
+ *aExtraData = closure->mClosure.mMessagePortIdentifiers.Length() - 1;
+
+ return true;
+}
+
+const JSStructuredCloneCallbacks gCallbacks = {
+ Read,
+ Write,
+ Error,
+ ReadTransfer,
+ WriteTransfer,
+ nullptr
+};
+
+} // anonymous namespace
+
+bool
+ReadStructuredCloneWithTransfer(JSContext* aCx, nsTArray<uint8_t>& aData,
+ const StructuredCloneClosure& aClosure,
+ JS::MutableHandle<JS::Value> aClone,
+ nsPIDOMWindow* aParentWindow,
+ nsTArray<nsRefPtr<MessagePort>>& aMessagePorts)
+{
+ auto* data = reinterpret_cast<uint64_t*>(aData.Elements());
+ size_t dataLen = aData.Length();
+ MOZ_ASSERT(!(dataLen % sizeof(*data)));
+
+ StructuredCloneClosureInternalReadOnly internalClosure(aClosure,
+ aParentWindow);
+
+ bool rv = JS_ReadStructuredClone(aCx, data, dataLen,
+ JS_STRUCTURED_CLONE_VERSION, aClone,
+ &gCallbacks, &internalClosure);
+ if (rv) {
+ aMessagePorts.SwapElements(internalClosure.mMessagePorts);
+ }
+
+ return rv;
+}
+
+bool
+WriteStructuredCloneWithTransfer(JSContext* aCx, JS::Handle<JS::Value> aSource,
+ JS::Handle<JS::Value> aTransferable,
+ nsTArray<uint8_t>& aData,
+ StructuredCloneClosure& aClosure)
+{
+ StructuredCloneClosureInternal internalClosure(aClosure, nullptr);
+ JSAutoStructuredCloneBuffer buffer(&gCallbacks, &internalClosure);
+
+ if (!buffer.write(aCx, aSource, aTransferable, &gCallbacks,
+ &internalClosure)) {
+ return false;
+ }
+
+ FallibleTArray<uint8_t> cloneData;
+ if (NS_WARN_IF(!cloneData.SetLength(buffer.nbytes(), mozilla::fallible))) {
+ return false;
+ }
+
+ uint64_t* data;
+ size_t size;
+ buffer.steal(&data, &size);
+
+ memcpy(cloneData.Elements(), data, size);
+ js_free(data);
+
+ MOZ_ASSERT(aData.IsEmpty());
+ aData.SwapElements(cloneData);
+ return true;
+}
+
+void
+FreeStructuredClone(nsTArray<uint8_t>& aData, StructuredCloneClosure& aClosure)
+{
+ auto* data = reinterpret_cast<uint64_t*>(aData.Elements());
+ size_t dataLen = aData.Length();
+ MOZ_ASSERT(!(dataLen % sizeof(*data)));
+
+ JS_ClearStructuredClone(data, dataLen, &gCallbacks, &aClosure, false);
+ aData.Clear();
+}
+
+} // messageport namespace
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/MessagePortUtils.h
@@ -0,0 +1,55 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_MessagePortUtils_h
+#define mozilla_dom_MessagePortUtils_h
+
+#include "MessagePort.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/PMessagePort.h"
+
+class nsPIDOMWindow;
+
+namespace mozilla {
+namespace dom {
+namespace messageport {
+
+struct
+StructuredCloneClosure
+{
+ nsTArray<nsRefPtr<BlobImpl>> mBlobImpls;
+ nsTArray<MessagePortIdentifier> mMessagePortIdentifiers;
+};
+
+struct
+StructuredCloneData
+{
+ StructuredCloneData() : mData(nullptr), mDataLength(0) {}
+ uint64_t* mData;
+ size_t mDataLength;
+ StructuredCloneClosure mClosure;
+};
+
+bool
+ReadStructuredCloneWithTransfer(JSContext* aCx, nsTArray<uint8_t>& aData,
+ const StructuredCloneClosure& aClosure,
+ JS::MutableHandle<JS::Value> aClone,
+ nsPIDOMWindow* aParentWindow,
+ nsTArray<nsRefPtr<MessagePort>>& aMessagePorts);
+
+bool
+WriteStructuredCloneWithTransfer(JSContext* aCx, JS::Handle<JS::Value> aSource,
+ JS::Handle<JS::Value> aTransferable,
+ nsTArray<uint8_t>& aData,
+ StructuredCloneClosure& aClosure);
+
+void
+FreeStructuredClone(nsTArray<uint8_t>& aData,
+ StructuredCloneClosure& aClosure);
+
+} // messageport namespace
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_MessagePortUtils_h
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/PMessagePort.ipdl
@@ -0,0 +1,62 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PBackground;
+include protocol PBlob;
+
+using struct nsID from "nsID.h";
+
+namespace mozilla {
+namespace dom {
+
+struct MessagePortIdentifier
+{
+ nsID uuid;
+ nsID destinationUuid;
+ uint32_t sequenceId;
+ bool neutered;
+};
+
+struct MessagePortMessage
+{
+ MessagePortIdentifier[] transferredPorts;
+ uint8_t[] data;
+ PBlob[] blobs;
+};
+
+// This protocol is used for the MessageChannel/MessagePort API
+protocol PMessagePort
+{
+ manager PBackground;
+
+ /* Many of these methods are used just for the shutdown sequence. The
+ correct sequence for the child actor is:
+ 1. SendStopSendingData();
+ 2. RecvStopSendingDataConfirmed();
+ 3. SendClose();
+ 4. Recv__delete__(); */
+
+ /* When the port is transferred the sequence is:
+ 1. SendStopSendingData();
+ 2. RecvStopSendingDataConfirmed();
+ 3. SendDisentangle();
+ 4. Recv__delete__(); */
+
+parent:
+ PostMessages(MessagePortMessage[] messages);
+ Disentangle(MessagePortMessage[] messages);
+ StopSendingData();
+ Close();
+
+child:
+ Entangled(MessagePortMessage[] messages);
+ ReceiveData(MessagePortMessage[] messages);
+ StopSendingDataConfirmed();
+
+ __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/SharedMessagePortMessage.cpp
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedMessagePortMessage.h"
+#include "MessagePort.h"
+#include "MessagePortChild.h"
+#include "MessagePortParent.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/PMessagePort.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundParent.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+namespace dom {
+
+SharedMessagePortMessage::~SharedMessagePortMessage()
+{
+ if (!mData.IsEmpty()) {
+ FreeStructuredClone(mData, mClosure);
+ }
+}
+
+/* static */ void
+SharedMessagePortMessage::FromSharedToMessagesChild(
+ MessagePortChild* aActor,
+ const nsTArray<nsRefPtr<SharedMessagePortMessage>>& aData,
+ nsTArray<MessagePortMessage>& aArray)
+{
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(aArray.IsEmpty());
+ aArray.SetCapacity(aData.Length());
+
+ PBackgroundChild* backgroundManager = aActor->Manager();
+ MOZ_ASSERT(backgroundManager);
+
+ for (auto& data : aData) {
+ MessagePortMessage* message = aArray.AppendElement();
+ message->data().SwapElements(data->mData);
+
+ const nsTArray<nsRefPtr<BlobImpl>>& blobImpls =
+ data->mClosure.mBlobImpls;
+ if (!blobImpls.IsEmpty()) {
+ message->blobsChild().SetCapacity(blobImpls.Length());
+
+ for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
+ PBlobChild* blobChild =
+ BackgroundChild::GetOrCreateActorForBlobImpl(backgroundManager,
+ blobImpls[i]);
+ message->blobsChild().AppendElement(blobChild);
+ }
+ }
+
+ message->transferredPorts().AppendElements(
+ data->mClosure.mMessagePortIdentifiers);
+ }
+}
+
+/* static */ bool
+SharedMessagePortMessage::FromMessagesToSharedChild(
+ nsTArray<MessagePortMessage>& aArray,
+ FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aData)
+{
+ MOZ_ASSERT(aData.IsEmpty());
+
+ if (NS_WARN_IF(!aData.SetCapacity(aArray.Length(), mozilla::fallible))) {
+ return false;
+ }
+
+ for (auto& message : aArray) {
+ nsRefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
+
+ data->mData.SwapElements(message.data());
+
+ const nsTArray<PBlobChild*>& blobs = message.blobsChild();
+ if (!blobs.IsEmpty()) {
+ data->mClosure.mBlobImpls.SetCapacity(blobs.Length());
+
+ for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
+ nsRefPtr<BlobImpl> impl =
+ static_cast<BlobChild*>(blobs[i])->GetBlobImpl();
+ data->mClosure.mBlobImpls.AppendElement(impl);
+ }
+ }
+
+ data->mClosure.mMessagePortIdentifiers.AppendElements(
+ message.transferredPorts());
+
+ if (!aData.AppendElement(data, mozilla::fallible)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* static */ bool
+SharedMessagePortMessage::FromSharedToMessagesParent(
+ MessagePortParent* aActor,
+ const nsTArray<nsRefPtr<SharedMessagePortMessage>>& aData,
+ FallibleTArray<MessagePortMessage>& aArray)
+{
+ MOZ_ASSERT(aArray.IsEmpty());
+
+ if (NS_WARN_IF(!aArray.SetCapacity(aData.Length(), mozilla::fallible))) {
+ return false;
+ }
+
+ PBackgroundParent* backgroundManager = aActor->Manager();
+ MOZ_ASSERT(backgroundManager);
+
+ for (auto& data : aData) {
+ MessagePortMessage* message = aArray.AppendElement(mozilla::fallible);
+ message->data().SwapElements(data->mData);
+
+ const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = data->mClosure.mBlobImpls;
+ if (!blobImpls.IsEmpty()) {
+ message->blobsParent().SetCapacity(blobImpls.Length());
+
+ for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
+ PBlobParent* blobParent =
+ BackgroundParent::GetOrCreateActorForBlobImpl(backgroundManager,
+ blobImpls[i]);
+ message->blobsParent().AppendElement(blobParent);
+ }
+ }
+
+ message->transferredPorts().AppendElements(
+ data->mClosure.mMessagePortIdentifiers);
+ }
+
+ return true;
+}
+
+/* static */ bool
+SharedMessagePortMessage::FromMessagesToSharedParent(
+ nsTArray<MessagePortMessage>& aArray,
+ FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aData)
+{
+ MOZ_ASSERT(aData.IsEmpty());
+
+ if (NS_WARN_IF(!aData.SetCapacity(aArray.Length(), mozilla::fallible))) {
+ return false;
+ }
+
+ for (auto& message : aArray) {
+ nsRefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
+
+ data->mData.SwapElements(message.data());
+
+ const nsTArray<PBlobParent*>& blobs = message.blobsParent();
+ if (!blobs.IsEmpty()) {
+ data->mClosure.mBlobImpls.SetCapacity(blobs.Length());
+
+ for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
+ nsRefPtr<BlobImpl> impl =
+ static_cast<BlobParent*>(blobs[i])->GetBlobImpl();
+ data->mClosure.mBlobImpls.AppendElement(impl);
+ }
+ }
+
+ data->mClosure.mMessagePortIdentifiers.AppendElements(
+ message.transferredPorts());
+
+ if (!aData.AppendElement(data, mozilla::fallible)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/SharedMessagePortMessage.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_SharedMessagePortMessage_h
+#define mozilla_dom_SharedMessagePortMessage_h
+
+#include "MessagePortUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+class MessagePortChild;
+class MessagePortMessage;
+class MessagePortParent;
+
+class SharedMessagePortMessage final
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(SharedMessagePortMessage)
+
+ nsTArray<uint8_t> mData;
+ messageport::StructuredCloneClosure mClosure;
+
+ SharedMessagePortMessage()
+ {}
+
+ static void
+ FromSharedToMessagesChild(
+ MessagePortChild* aActor,
+ const nsTArray<nsRefPtr<SharedMessagePortMessage>>& aData,
+ nsTArray<MessagePortMessage>& aArray);
+
+ static bool
+ FromMessagesToSharedChild(
+ nsTArray<MessagePortMessage>& aArray,
+ FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aData);
+
+ static bool
+ FromSharedToMessagesParent(
+ MessagePortParent* aActor,
+ const nsTArray<nsRefPtr<SharedMessagePortMessage>>& aData,
+ FallibleTArray<MessagePortMessage>& aArray);
+
+ static bool
+ FromMessagesToSharedParent(
+ nsTArray<MessagePortMessage>& aArray,
+ FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aData);
+
+private:
+ ~SharedMessagePortMessage();
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_SharedMessagePortMessage_h
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/moz.build
@@ -0,0 +1,41 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+TEST_DIRS += ['tests']
+
+EXPORTS.mozilla.dom += [
+ 'MessageChannel.h',
+ 'MessagePort.h',
+ 'MessagePortChild.h',
+ 'MessagePortList.h',
+ 'MessagePortParent.h',
+]
+
+UNIFIED_SOURCES += [
+ 'MessageChannel.cpp',
+ 'MessagePort.cpp',
+ 'MessagePortChild.cpp',
+ 'MessagePortList.cpp',
+ 'MessagePortParent.cpp',
+ 'MessagePortService.cpp',
+ 'MessagePortUtils.cpp',
+ 'SharedMessagePortMessage.cpp',
+]
+
+IPDL_SOURCES += [
+ 'PMessagePort.ipdl',
+]
+
+LOCAL_INCLUDES += [
+ '../base',
+ '../events',
+ '../workers',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+FAIL_ON_WARNINGS = True
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/chrome.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+support-files =
+ iframe_messageChannel_chrome.html
+
+[test_messageChannel.xul]
rename from dom/base/test/iframe_messageChannel_chrome.html
rename to dom/messagechannel/tests/iframe_messageChannel_chrome.html
rename from dom/base/test/iframe_messageChannel_cloning.html
rename to dom/messagechannel/tests/iframe_messageChannel_cloning.html
rename from dom/base/test/iframe_messageChannel_pingpong.html
rename to dom/messagechannel/tests/iframe_messageChannel_pingpong.html
rename from dom/base/test/iframe_messageChannel_post.html
rename to dom/messagechannel/tests/iframe_messageChannel_post.html
new file mode 100644
--- /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>
+
new file mode 100644
--- /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>
+
+
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/mochitest.ini
@@ -0,0 +1,25 @@
+[DEFAULT]
+support-files =
+ iframe_messageChannel_cloning.html
+ iframe_messageChannel_pingpong.html
+ iframe_messageChannel_post.html
+ iframe_messageChannel_transferable.html
+ worker_messageChannel.js
+ worker_messageChannel_any.js
+ sharedWorker_messageChannel.js
+ sharedWorker2_messageChannel.js
+ iframe_messageChannel_sharedWorker2.html
+
+[test_messageChannel.html]
+[test_messageChannel_cloning.html]
+[test_messageChannel_pingpong.html]
+[test_messageChannel_post.html]
+[test_messageChannel_pref.html]
+[test_messageChannel_start.html]
+[test_messageChannel_transferable.html]
+[test_messageChannel_unshipped.html]
+[test_messageChannel_worker.html]
+[test_messageChannel_selfTransferring.html]
+[test_messageChannel_sharedWorker.html]
+[test_messageChannel_sharedWorker2.html]
+[test_messageChannel_any.html]
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/moz.build
@@ -0,0 +1,8 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+MOCHITEST_MANIFESTS += ['mochitest.ini']
+MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
new file mode 100644
--- /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]]);
+}
new file mode 100644
--- /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);
+ }
+}
rename from dom/base/test/test_messageChannel.html
rename to dom/messagechannel/tests/test_messageChannel.html
rename from dom/base/test/test_messageChannel.xul
rename to dom/messagechannel/tests/test_messageChannel.xul
--- a/dom/base/test/test_messageChannel.xul
+++ b/dom/messagechannel/tests/test_messageChannel.xul
@@ -18,17 +18,17 @@
ok(channel, "MessageChannel is created");
channel.port1.onmessage = function(evt) {
ok(true, "message received!");
SimpleTest.finish();
}
var ifr = document.createElement('browser');
- ifr.setAttribute("src", "http://mochi.test:8888/tests/dom/base/test/iframe_messageChannel_chrome.html");
+ ifr.setAttribute("src", "iframe_messageChannel_chrome.html");
ifr.setAttribute("flex", "1");
ifr.addEventListener('load', function() {
ifr.contentWindow.postMessage(channel.port2, '*', [channel.port2]);
});
var body = document.getElementById("body");
body.appendChild(ifr);
new file mode 100644
--- /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();
+SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTest);
+ </script>
+</body>
+</html>
rename from dom/base/test/test_messageChannel_cloning.html
rename to dom/messagechannel/tests/test_messageChannel_cloning.html
rename from dom/base/test/test_messageChannel_pingpong.html
rename to dom/messagechannel/tests/test_messageChannel_pingpong.html
rename from dom/base/test/test_messageChannel_post.html
rename to dom/messagechannel/tests/test_messageChannel_post.html
rename from dom/base/test/test_messageChannel_pref.html
rename to dom/messagechannel/tests/test_messageChannel_pref.html
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_selfTransferring.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677638
+-->
+<head>
+ <meta charset="utf-8">
+ <title>MessagePort/Channel no self tranferring</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=677638">Mozilla Bug 677638</a>
+<div id="content"></div>
+<pre id="test">
+</pre>
+ <script type="application/javascript">
+
+function runTest() {
+ var a = new MessageChannel();
+
+ var status = false;
+ try {
+ a.port1.postMessage('foobar', [a.port1]);
+ } catch(e) {
+ status =true;
+ }
+
+ ok(status, "Transfering the same port should throw");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTest);
+ </script>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_sharedWorker.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677638
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 677638 - sharedWorker</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=677638">Mozilla Bug 677638</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <iframe name="x" id="x"></iframe>
+ <iframe name="y" id="y"></iframe>
+</div>
+<pre id="test">
+</pre>
+ <script type="application/javascript">
+
+ function runTest() {
+ var a = new SharedWorker('sharedWorker_messageChannel.js');
+ a.port.onmessage = function(evt) {
+ is(evt.ports.length, 1, "We received a port.");
+ evt.ports[0].onmessage = function(e) {
+ is(e.data, 42, "Message reiceved back!");
+ SimpleTest.finish();
+ }
+ evt.ports[0].postMessage(42);
+ }
+ }
+
+ SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTest);
+ SimpleTest.waitForExplicitFinish();
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_sharedWorker2.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677638
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 677638 - sharedWorker</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=677638">Mozilla Bug 677638</a>
+ <div id="content"></div>
+
+ <script type="application/javascript">
+
+ function runTest() {
+ var iframe = document.createElement('iframe');
+ iframe.setAttribute('src', "iframe_messageChannel_sharedWorker2.html");
+ document.getElementById('content').appendChild(iframe);
+
+ var a = new SharedWorker('sharedWorker2_messageChannel.js');
+ a.port.onmessage = function(evt) {
+ is(evt.ports.length, 1, "We received a port.");
+ evt.ports[0].onmessage = function(e) {
+ is(e.data, "Hello from the iframe!", "Message reiceved from the iframe!");
+ SimpleTest.finish();
+ }
+ }
+ }
+
+ SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTest);
+ SimpleTest.waitForExplicitFinish();
+ </script>
+</body>
+</html>
rename from dom/base/test/test_messageChannel_start.html
rename to dom/messagechannel/tests/test_messageChannel_start.html
rename from dom/base/test/test_messageChannel_transferable.html
rename to dom/messagechannel/tests/test_messageChannel_transferable.html
--- a/dom/base/test/test_messageChannel_transferable.html
+++ b/dom/messagechannel/tests/test_messageChannel_transferable.html
@@ -11,24 +11,25 @@ https://bugzilla.mozilla.org/show_bug.cg
</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() {
+ function basic_test() {
var a = new MessageChannel();
ok(a, "MessageChannel created");
window.addEventListener('message', receiveMessage, false);
function receiveMessage(evt) {
if (evt.data.status == 'READY') {
- runTest();
+ a.port1.postMessage({ab: ab, cb: ab}, [ab]);
+ ok(ab.byteLength == 0, "PostMessage - The size is: 0 == " + ab.byteLength)
} else {
ok(false, "Unknown message");
}
}
var div = document.getElementById("content");
ok(div, "Parent exists");
@@ -39,29 +40,72 @@ https://bugzilla.mozilla.org/show_bug.cg
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);
- SimpleTest.finish();
+ 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 runTest() {
- a.port1.postMessage({ab: ab, cb: ab}, [ab]);
- ok(ab.byteLength == 0, "PostMessage - The size is: 0 == " + ab.byteLength)
+ function port_test() {
+ window.addEventListener('message', receiveMessage, false);
+ function receiveMessage(evt) {
+ ok(evt.data.type == 'OK', evt.data.msg);
+ }
+
+ var a = new MessageChannel();
+ ok(a, "MessageChannel created");
+
+ var div = document.getElementById("content");
+ ok(div, "Parent exists");
+
+ var ifr = document.createElement("iframe");
+ ifr.addEventListener("load", iframeLoaded, false);
+ ifr.setAttribute('src', "iframe_messageChannel_transferable.html");
+ div.appendChild(ifr);
+
+ function iframeLoaded() {
+ ifr.contentWindow.postMessage('foobar!', '*', [a.port2]);
+ }
+
+ a.port1.onmessage = function(evt) {
+ ok(evt.ports.length == 1, "Iframe sent a new port!");
+ evt.ports[0].onmessage = function(evt) {
+ is(evt.data, "hello world!", "Message sent and received!");
+ runTests();
+ }
+
+ evt.ports[0].postMessage("hello world!");
}
}
+ var tests = [
+ basic_test,
+ port_test
+ ];
+
+ function runTests() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var t = tests.shift();
+ t();
+ }
+
SimpleTest.waitForExplicitFinish();
- SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, start);
+ SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTests);
</script>
</body>
</html>
rename from dom/base/test/test_messageChannel_unshipped.html
rename to dom/messagechannel/tests/test_messageChannel_unshipped.html
new file mode 100644
--- /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();
+ SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTests);
+ </script>
+</body>
+</html>
new file mode 100644
--- /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);
+}
new file mode 100644
--- /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");
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -91,16 +91,17 @@ DIRS += [
'plugins/ipc',
'indexedDB',
'system',
'ipc',
'workers',
'camera',
'audiochannel',
'broadcastchannel',
+ 'messagechannel',
'promise',
'smil',
'telephony',
'tv',
'voicemail',
'inputmethod',
'webidl',
'xbl',
--- a/dom/webidl/MessageChannel.webidl
+++ b/dom/webidl/MessageChannel.webidl
@@ -2,13 +2,14 @@
/* 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/.
*
* For more information on this interface, please see
* http://www.whatwg.org/specs/web-apps/current-work/#channel-messaging
*/
-[Constructor, Func="MessageChannel::Enabled"]
+[Constructor, Func="MessageChannel::Enabled",
+ Exposed=(Window,Worker)]
interface MessageChannel {
readonly attribute MessagePort port1;
readonly attribute MessagePort port2;
};
--- a/dom/workers/MessagePort.cpp
+++ b/dom/workers/MessagePort.cpp
@@ -12,16 +12,17 @@
#include "nsIDOMEvent.h"
#include "SharedWorker.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
using mozilla::dom::EventHandlerNonNull;
using mozilla::dom::MessagePortBase;
+using mozilla::dom::MessagePortIdentifier;
using mozilla::dom::Optional;
using mozilla::dom::Sequence;
using mozilla::dom::AutoNoJSAPI;
using namespace mozilla;
USING_WORKERS_NAMESPACE
namespace {
@@ -91,19 +92,19 @@ MessagePort::MessagePort(WorkerPrivate*
}
MessagePort::~MessagePort()
{
Close();
}
void
-MessagePort::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
- ErrorResult& aRv)
+MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ const Optional<Sequence<JS::Value>>& aTransferable,
+ ErrorResult& aRv)
{
AssertCorrectThread();
if (IsClosed()) {
aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
return;
}
@@ -193,21 +194,21 @@ MessagePort::SetOnmessage(EventHandlerNo
}
else {
SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback);
}
Start();
}
-already_AddRefed<MessagePortBase>
-MessagePort::Clone()
+bool
+MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier)
{
NS_WARNING("Haven't implemented structured clone for these ports yet!");
- return nullptr;
+ return false;
}
void
MessagePort::CloseInternal()
{
AssertCorrectThread();
MOZ_ASSERT(!IsClosed());
MOZ_ASSERT_IF(mStarted, mQueuedEvents.IsEmpty());
--- a/dom/workers/MessagePort.h
+++ b/dom/workers/MessagePort.h
@@ -38,19 +38,19 @@ class MessagePort final : public mozilla
uint64_t mSerial;
bool mStarted;
public:
static bool
PrefEnabled();
virtual void
- PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
- const Optional<Sequence<JS::Value>>& aTransferable,
- ErrorResult& aRv) override;
+ PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ const Optional<Sequence<JS::Value>>& aTransferable,
+ ErrorResult& aRv) override;
virtual void
Start() override;
virtual void
Close() override;
uint64_t
@@ -66,18 +66,18 @@ public:
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort, DOMEventTargetHelper)
virtual EventHandlerNonNull*
GetOnmessage() override;
virtual void
SetOnmessage(EventHandlerNonNull* aCallback) override;
- virtual already_AddRefed<MessagePortBase>
- Clone() override;
+ virtual bool
+ CloneAndDisentangle(MessagePortIdentifier& aIdentifier) override;
bool
IsClosed() const
{
return !mSharedWorker && !mWorkerPrivate;
}
virtual JSObject*
--- a/dom/workers/ServiceWorkerClient.cpp
+++ b/dom/workers/ServiceWorkerClient.cpp
@@ -7,16 +7,17 @@
#include "ServiceWorkerClient.h"
#include "ServiceWorkerContainer.h"
#include "mozilla/dom/MessageEvent.h"
#include "nsGlobalWindow.h"
#include "nsIDocument.h"
#include "WorkerPrivate.h"
+#include "WorkerStructuredClone.h"
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::dom::workers;
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ServiceWorkerClient, mOwner)
NS_IMPL_CYCLE_COLLECTING_ADDREF(ServiceWorkerClient)
@@ -70,26 +71,28 @@ ServiceWorkerClient::WrapObject(JSContex
}
namespace {
class ServiceWorkerClientPostMessageRunnable final : public nsRunnable
{
uint64_t mWindowId;
JSAutoStructuredCloneBuffer mBuffer;
- nsTArray<nsCOMPtr<nsISupports>> mClonedObjects;
+ WorkerStructuredCloneClosure mClosure;
public:
ServiceWorkerClientPostMessageRunnable(uint64_t aWindowId,
JSAutoStructuredCloneBuffer&& aData,
- nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
+ WorkerStructuredCloneClosure& aClosure)
: mWindowId(aWindowId),
mBuffer(Move(aData))
{
- mClonedObjects.SwapElements(aClonedObjects);
+ mClosure.mClonedObjects.SwapElements(aClosure.mClonedObjects);
+ MOZ_ASSERT(aClosure.mMessagePorts.IsEmpty());
+ mClosure.mMessagePortIdentifiers.SwapElements(aClosure.mMessagePortIdentifiers);
}
NS_IMETHOD
Run()
{
AssertIsOnMainThread();
nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
if (!window) {
@@ -113,18 +116,20 @@ public:
private:
NS_IMETHOD
DispatchDOMEvent(JSContext* aCx, ServiceWorkerContainer* aTargetContainer)
{
AssertIsOnMainThread();
// Release reference to objects that were AddRef'd for
// cloning into worker when array goes out of scope.
- nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
- clonedObjects.SwapElements(mClonedObjects);
+ WorkerStructuredCloneClosure closure;
+ closure.mClonedObjects.SwapElements(mClosure.mClonedObjects);
+ MOZ_ASSERT(mClosure.mMessagePorts.IsEmpty());
+ closure.mMessagePortIdentifiers.SwapElements(mClosure.mMessagePortIdentifiers);
JS::Rooted<JS::Value> messageData(aCx);
if (!mBuffer.read(aCx, &messageData,
WorkerStructuredCloneCallbacks(true))) {
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
return NS_ERROR_FAILURE;
}
@@ -180,24 +185,25 @@ ServiceWorkerClient::PostMessage(JSConte
return;
}
transferable.setObject(*array);
}
const JSStructuredCloneCallbacks* callbacks = WorkerStructuredCloneCallbacks(false);
- nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
+ WorkerStructuredCloneClosure closure;
JSAutoStructuredCloneBuffer buffer;
- if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
+ if (!buffer.write(aCx, aMessage, transferable, callbacks, &closure)) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
nsRefPtr<ServiceWorkerClientPostMessageRunnable> runnable =
- new ServiceWorkerClientPostMessageRunnable(mWindowId, Move(buffer), clonedObjects);
+ new ServiceWorkerClientPostMessageRunnable(mWindowId, Move(buffer),
+ closure);
nsresult rv = NS_DispatchToMainThread(runnable);
if (NS_FAILED(rv)) {
aRv.Throw(NS_ERROR_FAILURE);
}
}
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -47,16 +47,18 @@
#include "mozilla/dom/ErrorEvent.h"
#include "mozilla/dom/ErrorEventBinding.h"
#include "mozilla/dom/Exceptions.h"
#include "mozilla/dom/FunctionBinding.h"
#include "mozilla/dom/ImageData.h"
#include "mozilla/dom/ImageDataBinding.h"
#include "mozilla/dom/MessageEvent.h"
#include "mozilla/dom/MessageEventBinding.h"
+#include "mozilla/dom/MessagePort.h"
+#include "mozilla/dom/MessagePortBinding.h"
#include "mozilla/dom/MessagePortList.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseDebugging.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/StructuredClone.h"
#include "mozilla/dom/WebCryptoCommon.h"
#include "mozilla/dom/WorkerBinding.h"
#include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h"
@@ -100,16 +102,17 @@
#include "RuntimeService.h"
#include "ScriptLoader.h"
#include "ServiceWorkerManager.h"
#include "SharedWorker.h"
#include "WorkerDebuggerManager.h"
#include "WorkerFeature.h"
#include "WorkerRunnable.h"
#include "WorkerScope.h"
+#include "WorkerStructuredClone.h"
#include "WorkerThread.h"
#ifdef XP_WIN
#undef PostMessage
#endif
// JS_MaybeGC will run once every second during normal execution.
#define PERIODIC_GC_TIMER_DELAY_SEC 1
@@ -506,17 +509,17 @@ ReadFormData(JSContext* aCx,
aFormData.set(formData->WrapObject(aCx, nullptr));
}
bool
WriteBlobOrFile(JSContext* aCx,
JSStructuredCloneWriter* aWriter,
BlobImpl* aBlobOrBlobImpl,
- nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
+ WorkerStructuredCloneClosure& aClosure)
{
MOZ_ASSERT(aCx);
MOZ_ASSERT(aWriter);
MOZ_ASSERT(aBlobOrBlobImpl);
nsRefPtr<BlobImpl> blobImpl = EnsureBlobForBackgroundManager(aBlobOrBlobImpl);
MOZ_ASSERT(blobImpl);
@@ -524,17 +527,17 @@ WriteBlobOrFile(JSContext* aCx,
if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0)) ||
NS_WARN_IF(!JS_WriteBytes(aWriter,
&aBlobOrBlobImpl,
sizeof(aBlobOrBlobImpl)))) {
return false;
}
- aClonedObjects.AppendElement(aBlobOrBlobImpl);
+ aClosure.mClonedObjects.AppendElement(aBlobOrBlobImpl);
return true;
}
// A FormData is serialized as:
// - A pair of ints (tag identifying it as a FormData, number of elements in
// the FormData)
// - for each (key, value) pair:
// - pair of ints (is value a file?, 0). If not a file, value is a string.
@@ -542,34 +545,34 @@ WriteBlobOrFile(JSContext* aCx,
// - if value is a file:
// - write the file/blob
// - else:
// - string value
bool
WriteFormData(JSContext* aCx,
JSStructuredCloneWriter* aWriter,
nsFormData* aFormData,
- nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
+ WorkerStructuredCloneClosure& aClosure)
{
MOZ_ASSERT(aCx);
MOZ_ASSERT(aWriter);
MOZ_ASSERT(aFormData);
if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FORMDATA, aFormData->Length()))) {
return false;
}
class MOZ_STACK_CLASS Closure {
JSContext* mCx;
JSStructuredCloneWriter* mWriter;
- nsTArray<nsCOMPtr<nsISupports>>& mClones;
+ WorkerStructuredCloneClosure& mClones;
public:
Closure(JSContext* aCx, JSStructuredCloneWriter* aWriter,
- nsTArray<nsCOMPtr<nsISupports>>& aClones)
+ WorkerStructuredCloneClosure& aClones)
: mCx(aCx), mWriter(aWriter), mClones(aClones)
{ }
static bool
Write(const nsString& aName, bool isFile, const nsString& aValue,
File* aFile, void* aClosure)
{
Closure* closure = static_cast<Closure*>(aClosure);
@@ -590,17 +593,17 @@ WriteFormData(JSContext* aCx,
return false;
}
}
return true;
}
};
- Closure closure(aCx, aWriter, aClonedObjects);
+ Closure closure(aCx, aWriter, aClosure);
return aFormData->ForEach(Closure::Write, &closure);
}
struct WorkerStructuredCloneCallbacks
{
static JSObject*
Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
uint32_t aData, void* aClosure)
@@ -634,28 +637,26 @@ struct WorkerStructuredCloneCallbacks
}
static bool
Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
JS::Handle<JSObject*> aObj, void* aClosure)
{
NS_ASSERTION(aClosure, "Null pointer!");
- // We'll stash any nsISupports pointers that need to be AddRef'd here.
- auto* clonedObjects =
- static_cast<nsTArray<nsCOMPtr<nsISupports>>*>(aClosure);
+ auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
// See if this is a Blob/File object.
{
nsRefPtr<Blob> blob;
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
BlobImpl* blobImpl = blob->Impl();
MOZ_ASSERT(blobImpl);
- if (WriteBlobOrFile(aCx, aWriter, blobImpl, *clonedObjects)) {
+ if (WriteBlobOrFile(aCx, aWriter, blobImpl, *closure)) {
return true;
}
}
}
// See if this is an ImageData object.
{
ImageData* imageData = nullptr;
@@ -663,40 +664,121 @@ struct WorkerStructuredCloneCallbacks
return WriteStructuredCloneImageData(aCx, aWriter, imageData);
}
}
// See if this is a FormData object.
{
nsFormData* formData = nullptr;
if (NS_SUCCEEDED(UNWRAP_OBJECT(FormData, aObj, formData))) {
- if (WriteFormData(aCx, aWriter, formData, *clonedObjects)) {
+ if (WriteFormData(aCx, aWriter, formData, *closure)) {
return true;
}
}
}
Error(aCx, 0);
return false;
}
static void
Error(JSContext* aCx, uint32_t /* aErrorId */)
{
Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
}
+
+ static bool
+ ReadTransfer(JSContext* aCx, JSStructuredCloneReader* aReader,
+ uint32_t aTag, void* aContent, uint64_t aExtraData,
+ void* aClosure, JS::MutableHandle<JSObject*> aReturnObject)
+ {
+ MOZ_ASSERT(aClosure);
+
+ auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
+
+ if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
+ MOZ_ASSERT(!aContent);
+ MOZ_ASSERT(aExtraData < closure->mMessagePortIdentifiers.Length());
+
+ ErrorResult rv;
+ nsRefPtr<MessagePortBase> port =
+ dom::MessagePort::Create(closure->mParentWindow,
+ closure->mMessagePortIdentifiers[aExtraData],
+ rv);
+
+ if (NS_WARN_IF(rv.Failed())) {
+ return false;
+ }
+
+ closure->mMessagePorts.AppendElement(port);
+
+ JS::Rooted<JS::Value> value(aCx);
+ if (!GetOrCreateDOMReflector(aCx, port, &value)) {
+ JS_ClearPendingException(aCx);
+ return false;
+ }
+
+ aReturnObject.set(&value.toObject());
+ return true;
+ }
+
+ return false;
+ }
+
+ static bool
+ Transfer(JSContext* aCx, JS::Handle<JSObject*> aObj, void* aClosure,
+ uint32_t* aTag, JS::TransferableOwnership* aOwnership,
+ void** aContent, uint64_t *aExtraData)
+ {
+ MOZ_ASSERT(aClosure);
+
+ auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
+
+ MessagePortBase* port;
+ nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
+ if (NS_SUCCEEDED(rv)) {
+ if (NS_WARN_IF(closure->mTransferredPorts.Contains(port))) {
+ // No duplicates.
+ return false;
+ }
+
+ MessagePortIdentifier identifier;
+ if (!port->CloneAndDisentangle(identifier)) {
+ return false;
+ }
+
+ closure->mMessagePortIdentifiers.AppendElement(identifier);
+ closure->mTransferredPorts.AppendElement(port);
+
+ *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
+ *aOwnership = JS::SCTAG_TMO_CUSTOM;
+ *aContent = nullptr;
+ *aExtraData = closure->mMessagePortIdentifiers.Length() - 1;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ static void
+ FreeTransfer(uint32_t aTag, JS::TransferableOwnership aOwnership,
+ void *aContent, uint64_t aExtraData, void* aClosure)
+ {
+ // Nothing to do.
+ }
};
const JSStructuredCloneCallbacks gWorkerStructuredCloneCallbacks = {
WorkerStructuredCloneCallbacks::Read,
WorkerStructuredCloneCallbacks::Write,
WorkerStructuredCloneCallbacks::Error,
- nullptr,
- nullptr,
- nullptr
+ WorkerStructuredCloneCallbacks::ReadTransfer,
+ WorkerStructuredCloneCallbacks::Transfer,
+ WorkerStructuredCloneCallbacks::FreeTransfer
};
struct MainThreadWorkerStructuredCloneCallbacks
{
static JSObject*
Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
uint32_t aData, void* aClosure)
{
@@ -726,30 +808,28 @@ struct MainThreadWorkerStructuredCloneCa
static bool
Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
JS::Handle<JSObject*> aObj, void* aClosure)
{
AssertIsOnMainThread();
NS_ASSERTION(aClosure, "Null pointer!");
- // We'll stash any nsISupports pointers that need to be AddRef'd here.
- auto* clonedObjects =
- static_cast<nsTArray<nsCOMPtr<nsISupports>>*>(aClosure);
+ auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
// See if this is a Blob/File object.
{
nsRefPtr<Blob> blob;
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
BlobImpl* blobImpl = blob->Impl();
MOZ_ASSERT(blobImpl);
if (!blobImpl->MayBeClonedToOtherThreads()) {
NS_WARNING("Not all the blob implementations can be sent between threads.");
- } else if (WriteBlobOrFile(aCx, aWriter, blobImpl, *clonedObjects)) {
+ } else if (WriteBlobOrFile(aCx, aWriter, blobImpl, *closure)) {
return true;
}
}
}
JS_ClearPendingException(aCx);
return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
}
@@ -762,19 +842,19 @@ struct MainThreadWorkerStructuredCloneCa
NS_DOMStructuredCloneError(aCx, aErrorId);
}
};
const JSStructuredCloneCallbacks gMainThreadWorkerStructuredCloneCallbacks = {
MainThreadWorkerStructuredCloneCallbacks::Read,
MainThreadWorkerStructuredCloneCallbacks::Write,
MainThreadWorkerStructuredCloneCallbacks::Error,
- nullptr,
- nullptr,
- nullptr
+ WorkerStructuredCloneCallbacks::ReadTransfer,
+ WorkerStructuredCloneCallbacks::Transfer,
+ WorkerStructuredCloneCallbacks::FreeTransfer
};
struct ChromeWorkerStructuredCloneCallbacks
{
static JSObject*
Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
uint32_t aData, void* aClosure)
{
@@ -795,19 +875,19 @@ struct ChromeWorkerStructuredCloneCallba
return WorkerStructuredCloneCallbacks::Error(aCx, aErrorId);
}
};
const JSStructuredCloneCallbacks gChromeWorkerStructuredCloneCallbacks = {
ChromeWorkerStructuredCloneCallbacks::Read,
ChromeWorkerStructuredCloneCallbacks::Write,
ChromeWorkerStructuredCloneCallbacks::Error,
- nullptr,
- nullptr,
- nullptr
+ WorkerStructuredCloneCallbacks::ReadTransfer,
+ WorkerStructuredCloneCallbacks::Transfer,
+ WorkerStructuredCloneCallbacks::FreeTransfer
};
struct MainThreadChromeWorkerStructuredCloneCallbacks
{
static JSObject*
Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
uint32_t aData, void* aClosure)
{
@@ -1148,55 +1228,71 @@ private:
aWorkerPrivate->CloseHandlerFinished();
}
};
class MessageEventRunnable final : public WorkerRunnable
{
JSAutoStructuredCloneBuffer mBuffer;
- nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
+ WorkerStructuredCloneClosure mClosure;
uint64_t mMessagePortSerial;
bool mToMessagePort;
// This is only used for messages dispatched to a service worker.
nsAutoPtr<ServiceWorkerClientInfo> mEventSource;
public:
MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
TargetAndBusyBehavior aBehavior,
- JSAutoStructuredCloneBuffer&& aData,
- nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
bool aToMessagePort, uint64_t aMessagePortSerial)
: WorkerRunnable(aWorkerPrivate, aBehavior)
- , mBuffer(Move(aData))
, mMessagePortSerial(aMessagePortSerial)
, mToMessagePort(aToMessagePort)
{
- mClonedObjects.SwapElements(aClonedObjects);
+ }
+
+ bool
+ Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ JS::Handle<JS::Value> aTransferredValue,
+ const JSStructuredCloneCallbacks *aCallbacks)
+ {
+ bool ok = mBuffer.write(aCx, aValue, aTransferredValue, aCallbacks,
+ &mClosure);
+ // This hashtable has to be empty because it could contain MessagePort
+ // objects that cannot be freed on a different thread.
+ mClosure.mTransferredPorts.Clear();
+ return ok;
}
void
SetMessageSource(ServiceWorkerClientInfo* aSource)
{
mEventSource = aSource;
}
bool
DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
DOMEventTargetHelper* aTarget, bool aIsMainThread)
{
// Release reference to objects that were AddRef'd for
// cloning into worker when array goes out of scope.
- nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
- clonedObjects.SwapElements(mClonedObjects);
+ WorkerStructuredCloneClosure closure;
+ closure.mClonedObjects.SwapElements(mClosure.mClonedObjects);
+ MOZ_ASSERT(mClosure.mMessagePorts.IsEmpty());
+ closure.mMessagePortIdentifiers.SwapElements(mClosure.mMessagePortIdentifiers);
+
+ if (aIsMainThread) {
+ closure.mParentWindow = do_QueryInterface(aTarget->GetParentObject());
+ }
JS::Rooted<JS::Value> messageData(aCx);
if (!mBuffer.read(aCx, &messageData,
- workers::WorkerStructuredCloneCallbacks(aIsMainThread))) {
+ workers::WorkerStructuredCloneCallbacks(aIsMainThread),
+ &closure)) {
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
return false;
}
nsRefPtr<MessageEvent> event = new MessageEvent(aTarget, nullptr, nullptr);
nsresult rv =
event->InitMessageEvent(NS_LITERAL_STRING("message"),
false /* non-bubbling */,
@@ -1212,17 +1308,18 @@ public:
}
if (NS_FAILED(rv)) {
xpc::Throw(aCx, rv);
return false;
}
event->SetTrusted(true);
-
+ event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()),
+ closure.mMessagePorts));
nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
nsEventStatus dummy = nsEventStatus_eIgnore;
aTarget->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
return true;
}
private:
@@ -1238,17 +1335,17 @@ private:
return true;
}
if (mToMessagePort) {
return
aWorkerPrivate->DispatchMessageEventToMessagePort(aCx,
mMessagePortSerial,
Move(mBuffer),
- mClonedObjects);
+ mClosure);
}
if (aWorkerPrivate->IsFrozen()) {
aWorkerPrivate->QueueRunnable(this);
return true;
}
aWorkerPrivate->AssertInnerWindowIsCorrect();
@@ -3374,29 +3471,26 @@ WorkerPrivateParent<Derived>::PostMessag
JS_NewArrayObject(aCx, elements);
if (!array) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
transferable.setObject(*array);
}
- nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
-
- JSAutoStructuredCloneBuffer buffer;
- if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
- aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
- return;
- }
-
nsRefPtr<MessageEventRunnable> runnable =
new MessageEventRunnable(ParentAsWorkerPrivate(),
WorkerRunnable::WorkerThreadModifyBusyCount,
- Move(buffer), clonedObjects, aToMessagePort,
- aMessagePortSerial);
+ aToMessagePort, aMessagePortSerial);
+
+ if (!runnable->Write(aCx, aMessage, transferable, callbacks)) {
+ aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
+ return;
+ }
+
runnable->SetMessageSource(aClientInfo);
if (!runnable->Dispatch(aCx)) {
aRv.Throw(NS_ERROR_FAILURE);
}
}
template <class Derived>
@@ -3427,67 +3521,68 @@ WorkerPrivateParent<Derived>::PostMessag
nullptr, aRv);
}
template <class Derived>
bool
WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
JSContext* aCx, uint64_t aMessagePortSerial,
JSAutoStructuredCloneBuffer&& aBuffer,
- nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
+ WorkerStructuredCloneClosure& aClosure)
{
AssertIsOnMainThread();
JSAutoStructuredCloneBuffer buffer(Move(aBuffer));
- nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
- clonedObjects.SwapElements(aClonedObjects);
+ WorkerStructuredCloneClosure closure;
+ closure.mClonedObjects.SwapElements(aClosure.mClonedObjects);
+ MOZ_ASSERT(aClosure.mMessagePorts.IsEmpty());
+ closure.mMessagePortIdentifiers.SwapElements(aClosure.mMessagePortIdentifiers);
SharedWorker* sharedWorker;
if (!mSharedWorkers.Get(aMessagePortSerial, &sharedWorker)) {
// SharedWorker has already been unregistered?
return true;
}
nsRefPtr<MessagePort> port = sharedWorker->Port();
NS_ASSERTION(port, "SharedWorkers always have a port!");
if (port->IsClosed()) {
return true;
}
+ closure.mParentWindow = do_QueryInterface(port->GetParentObject());
+
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(port->GetParentObject()))) {
return false;
}
JSContext* cx = jsapi.cx();
JS::Rooted<JS::Value> data(cx);
- if (!buffer.read(cx, &data, WorkerStructuredCloneCallbacks(true))) {
+ if (!buffer.read(cx, &data, WorkerStructuredCloneCallbacks(true),
+ &closure)) {
return false;
}
buffer.clear();
nsRefPtr<MessageEvent> event = new MessageEvent(port, nullptr, nullptr);
nsresult rv =
event->InitMessageEvent(NS_LITERAL_STRING("message"), false, false, data,
EmptyString(), EmptyString(), nullptr);
if (NS_FAILED(rv)) {
xpc::Throw(cx, rv);
return false;
}
event->SetTrusted(true);
- nsTArray<nsRefPtr<MessagePortBase>> ports;
- ports.AppendElement(port);
-
- nsRefPtr<MessagePortList> portList = new MessagePortList(port, ports);
- event->SetPorts(portList);
+ event->SetPorts(new MessagePortList(port, closure.mMessagePorts));
nsCOMPtr<nsIDOMEvent> domEvent;
CallQueryInterface(event.get(), getter_AddRefs(domEvent));
NS_ASSERTION(domEvent, "This should never fail!");
bool ignored;
rv = port->DispatchEvent(domEvent, &ignored);
if (NS_FAILED(rv)) {
@@ -6177,29 +6272,26 @@ WorkerPrivate::PostMessageToParentIntern
transferable.setObject(*array);
}
const JSStructuredCloneCallbacks* callbacks =
IsChromeWorker() ?
&gChromeWorkerStructuredCloneCallbacks :
&gWorkerStructuredCloneCallbacks;
- nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
-
- JSAutoStructuredCloneBuffer buffer;
- if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
- aRv = NS_ERROR_DOM_DATA_CLONE_ERR;
- return;
- }
-
nsRefPtr<MessageEventRunnable> runnable =
new MessageEventRunnable(this,
WorkerRunnable::ParentThreadUnchangedBusyCount,
- Move(buffer), clonedObjects, aToMessagePort,
- aMessagePortSerial);
+ aToMessagePort, aMessagePortSerial);
+
+ if (!runnable->Write(aCx, aMessage, transferable, callbacks)) {
+ aRv = NS_ERROR_DOM_DATA_CLONE_ERR;
+ return;
+ }
+
if (!runnable->Dispatch(aCx)) {
aRv = NS_ERROR_FAILURE;
}
}
void
WorkerPrivate::PostMessageToParentMessagePort(
JSContext* aCx,
@@ -7343,9 +7435,25 @@ ChromeWorkerStructuredCloneCallbacks(boo
return aMainRuntime ?
&gMainThreadChromeWorkerStructuredCloneCallbacks :
&gChromeWorkerStructuredCloneCallbacks;
}
// Force instantiation.
template class WorkerPrivateParent<WorkerPrivate>;
+WorkerStructuredCloneClosure::WorkerStructuredCloneClosure()
+{}
+
+WorkerStructuredCloneClosure::~WorkerStructuredCloneClosure()
+{}
+
+void
+WorkerStructuredCloneClosure::Clear()
+{
+ mParentWindow = nullptr;
+ mClonedObjects.Clear();
+ mMessagePorts.Clear();
+ mMessagePortIdentifiers.Clear();
+ mTransferredPorts.Clear();
+}
+
END_WORKERS_NAMESPACE
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -68,16 +68,17 @@ class MessagePort;
class SharedWorker;
class ServiceWorkerClientInfo;
class WorkerControlRunnable;
class WorkerDebugger;
class WorkerDebuggerGlobalScope;
class WorkerGlobalScope;
class WorkerPrivate;
class WorkerRunnable;
+class WorkerStructuredCloneClosure;
class WorkerThread;
// SharedMutex is a small wrapper around an (internal) reference-counted Mutex
// object. It exists to avoid changing a lot of code to use Mutex* instead of
// Mutex&.
class SharedMutex
{
typedef mozilla::Mutex Mutex;
@@ -344,17 +345,17 @@ public:
const Optional<Sequence<JS::Value> >& aTransferable,
ErrorResult& aRv);
bool
DispatchMessageEventToMessagePort(
JSContext* aCx,
uint64_t aMessagePortSerial,
JSAutoStructuredCloneBuffer&& aBuffer,
- nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects);
+ WorkerStructuredCloneClosure& aClosure);
void
UpdateRuntimeOptions(JSContext* aCx,
const JS::RuntimeOptions& aRuntimeOptions);
void
UpdateLanguages(JSContext* aCx, const nsTArray<nsString>& aLanguages);
new file mode 100644
--- /dev/null
+++ b/dom/workers/WorkerStructuredClone.h
@@ -0,0 +1,53 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_workers_WorkerStructuredClone_h
+#define mozilla_dom_workers_WorkerStructuredClone_h
+
+#include "Workers.h"
+#include "mozilla/dom/PMessagePort.h"
+
+class nsPIDOMWindow;
+
+namespace mozilla {
+namespace dom {
+
+class MessagePortBase;
+
+namespace workers {
+
+// This class is implemented in WorkerPrivate.cpp
+class WorkerStructuredCloneClosure final
+{
+private:
+ WorkerStructuredCloneClosure(const WorkerStructuredCloneClosure&) = delete;
+ WorkerStructuredCloneClosure & operator=(const WorkerStructuredCloneClosure&) = delete;
+
+public:
+ WorkerStructuredCloneClosure();
+ ~WorkerStructuredCloneClosure();
+
+ void Clear();
+
+ // This can be null if the MessagePort is created in a worker.
+ nsCOMPtr<nsPIDOMWindow> mParentWindow;
+
+ nsTArray<nsCOMPtr<nsISupports>> mClonedObjects;
+
+ // The transferred ports.
+ nsTArray<nsRefPtr<MessagePortBase>> mMessagePorts;
+
+ // Information for the transferring.
+ nsTArray<MessagePortIdentifier> mMessagePortIdentifiers;
+
+ // To avoid duplicates in the transferred ports.
+ nsTArray<nsRefPtr<MessagePortBase>> mTransferredPorts;
+};
+
+} // workers namespace
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_workers_WorkerStructuredClone_h
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -22,16 +22,17 @@
#include "nsContentUtils.h"
#include "nsFormData.h"
#include "nsJSUtils.h"
#include "nsThreadUtils.h"
#include "RuntimeService.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
+#include "WorkerStructuredClone.h"
#include "XMLHttpRequestUpload.h"
using namespace mozilla;
using namespace mozilla::dom;
USING_WORKERS_NAMESPACE
/**
@@ -408,17 +409,17 @@ private:
}
};
class EventRunnable final : public MainThreadProxyRunnable
{
nsString mType;
nsString mResponseType;
JSAutoStructuredCloneBuffer mResponseBuffer;
- nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
+ WorkerStructuredCloneClosure mResponseClosure;
JS::Heap<JS::Value> mResponse;
nsString mResponseText;
nsString mResponseURL;
nsCString mStatusText;
uint64_t mLoaded;
uint64_t mTotal;
uint32_t mEventStreamId;
uint32_t mStatus;
@@ -789,32 +790,34 @@ private:
nsresult
MainThreadRunInternal();
};
class SendRunnable final : public WorkerThreadProxySyncRunnable
{
nsString mStringBody;
JSAutoStructuredCloneBuffer mBody;
- nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
+ WorkerStructuredCloneClosure mClosure;
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
bool mHasUploadListeners;
public:
SendRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
const nsAString& aStringBody, JSAutoStructuredCloneBuffer&& aBody,
- nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects,
+ WorkerStructuredCloneClosure& aClosure,
nsIEventTarget* aSyncLoopTarget, bool aHasUploadListeners)
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
, mStringBody(aStringBody)
, mBody(Move(aBody))
, mSyncLoopTarget(aSyncLoopTarget)
, mHasUploadListeners(aHasUploadListeners)
{
- mClonedObjects.SwapElements(aClonedObjects);
+ mClosure.mClonedObjects.SwapElements(aClosure.mClonedObjects);
+ MOZ_ASSERT(aClosure.mMessagePorts.IsEmpty());
+ MOZ_ASSERT(aClosure.mMessagePortIdentifiers.IsEmpty());
}
private:
~SendRunnable()
{ }
virtual nsresult
MainThreadRun() override;
@@ -1224,21 +1227,23 @@ EventRunnable::PreDispatch(JSContext* aC
if (doClone) {
// Anything subject to GC must be cloned.
const JSStructuredCloneCallbacks* callbacks =
aWorkerPrivate->IsChromeWorker() ?
workers::ChromeWorkerStructuredCloneCallbacks(true) :
workers::WorkerStructuredCloneCallbacks(true);
- nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
+ WorkerStructuredCloneClosure closure;
if (mResponseBuffer.write(aCx, response, transferable, callbacks,
- &clonedObjects)) {
- mClonedObjects.SwapElements(clonedObjects);
+ &closure)) {
+ mResponseClosure.mClonedObjects.SwapElements(closure.mClonedObjects);
+ MOZ_ASSERT(mResponseClosure.mMessagePorts.IsEmpty());
+ MOZ_ASSERT(mResponseClosure.mMessagePortIdentifiers.IsEmpty());
} else {
NS_WARNING("Failed to clone response!");
mResponseResult = NS_ERROR_DOM_DATA_CLONE_ERR;
mProxy->mArrayBufferResponseWasTransferred = false;
}
}
}
}
@@ -1336,21 +1341,23 @@ EventRunnable::WorkerRun(JSContext* aCx,
JSAutoStructuredCloneBuffer responseBuffer(Move(mResponseBuffer));
const JSStructuredCloneCallbacks* callbacks =
aWorkerPrivate->IsChromeWorker() ?
workers::ChromeWorkerStructuredCloneCallbacks(false) :
workers::WorkerStructuredCloneCallbacks(false);
- nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
- clonedObjects.SwapElements(mClonedObjects);
+ WorkerStructuredCloneClosure closure;
+ closure.mClonedObjects.SwapElements(mResponseClosure.mClonedObjects);
+ MOZ_ASSERT(mResponseClosure.mMessagePorts.IsEmpty());
+ MOZ_ASSERT(mResponseClosure.mMessagePortIdentifiers.IsEmpty());
JS::Rooted<JS::Value> response(aCx);
- if (!responseBuffer.read(aCx, &response, callbacks, &clonedObjects)) {
+ if (!responseBuffer.read(aCx, &response, callbacks, &closure)) {
return false;
}
state->mResponse = response;
}
else {
state->mResponse = mResponse;
}
@@ -1521,27 +1528,27 @@ SendRunnable::MainThreadRun()
nsresult rv = NS_OK;
const JSStructuredCloneCallbacks* callbacks =
mWorkerPrivate->IsChromeWorker() ?
workers::ChromeWorkerStructuredCloneCallbacks(true) :
workers::WorkerStructuredCloneCallbacks(true);
JS::Rooted<JS::Value> body(cx);
- if (mBody.read(cx, &body, callbacks, &mClonedObjects)) {
+ if (mBody.read(cx, &body, callbacks, &mClosure)) {
if (NS_FAILED(xpc->JSValToVariant(cx, body, getter_AddRefs(variant)))) {
rv = NS_ERROR_DOM_INVALID_STATE_ERR;
}
}
else {
rv = NS_ERROR_DOM_DATA_CLONE_ERR;
}
mBody.clear();
- mClonedObjects.Clear();
+ mClosure.Clear();
NS_ENSURE_SUCCESS(rv, rv);
}
else {
nsCOMPtr<nsIWritableVariant> wvariant =
do_CreateInstance(NS_VARIANT_CONTRACTID);
NS_ENSURE_TRUE(wvariant, NS_ERROR_UNEXPECTED);
@@ -1841,17 +1848,17 @@ XMLHttpRequest::Unpin()
mRooted = false;
NS_RELEASE_THIS();
}
void
XMLHttpRequest::SendInternal(const nsAString& aStringBody,
JSAutoStructuredCloneBuffer&& aBody,
- nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
+ WorkerStructuredCloneClosure& aClosure,
ErrorResult& aRv)
{
mWorkerPrivate->AssertIsOnWorkerThread();
// No send() calls when open is running.
if (mProxy->mOpening) {
aRv.Throw(NS_ERROR_FAILURE);
return;
@@ -1875,17 +1882,17 @@ XMLHttpRequest::SendInternal(const nsASt
}
mProxy->mOuterChannelId++;
JSContext* cx = mWorkerPrivate->GetJSContext();
nsRefPtr<SendRunnable> runnable =
new SendRunnable(mWorkerPrivate, mProxy, aStringBody, Move(aBody),
- aClonedObjects, syncLoopTarget, hasUploadListeners);
+ aClosure, syncLoopTarget, hasUploadListeners);
if (!runnable->Dispatch(cx)) {
// Dispatch() may have spun the event loop and we may have already unrooted.
// If so we don't want autoUnpin to try again.
if (!mRooted) {
autoUnpin.Clear();
}
aRv.Throw(NS_ERROR_FAILURE);
return;
@@ -2097,19 +2104,19 @@ XMLHttpRequest::Send(ErrorResult& aRv)
if (!mProxy) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
// Nothing to clone.
JSAutoStructuredCloneBuffer buffer;
- nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
-
- SendInternal(NullString(), Move(buffer), clonedObjects, aRv);
+ WorkerStructuredCloneClosure closure;
+
+ SendInternal(NullString(), Move(buffer), closure, aRv);
}
void
XMLHttpRequest::Send(const nsAString& aBody, ErrorResult& aRv)
{
mWorkerPrivate->AssertIsOnWorkerThread();
if (mCanceled) {
@@ -2119,19 +2126,19 @@ XMLHttpRequest::Send(const nsAString& aB
if (!mProxy) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
// Nothing to clone.
JSAutoStructuredCloneBuffer buffer;
- nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
-
- SendInternal(aBody, Move(buffer), clonedObjects, aRv);
+ WorkerStructuredCloneClosure closure;
+
+ SendInternal(aBody, Move(buffer), closure, aRv);
}
void
XMLHttpRequest::Send(JS::Handle<JSObject*> aBody, ErrorResult& aRv)
{
JSContext* cx = mWorkerPrivate->GetJSContext();
MOZ_ASSERT(aBody);
@@ -2162,25 +2169,25 @@ XMLHttpRequest::Send(JS::Handle<JSObject
valToClone.setString(bodyStr);
}
const JSStructuredCloneCallbacks* callbacks =
mWorkerPrivate->IsChromeWorker() ?
ChromeWorkerStructuredCloneCallbacks(false) :
WorkerStructuredCloneCallbacks(false);
- nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
+ WorkerStructuredCloneClosure closure;
JSAutoStructuredCloneBuffer buffer;
- if (!buffer.write(cx, valToClone, callbacks, &clonedObjects)) {
+ if (!buffer.write(cx, valToClone, callbacks, &closure)) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
- SendInternal(EmptyString(), Move(buffer), clonedObjects, aRv);
+ SendInternal(EmptyString(), Move(buffer), closure, aRv);
}
void
XMLHttpRequest::Send(Blob& aBody, ErrorResult& aRv)
{
mWorkerPrivate->AssertIsOnWorkerThread();
JSContext* cx = mWorkerPrivate->GetJSContext();
@@ -2208,25 +2215,25 @@ XMLHttpRequest::Send(Blob& aBody, ErrorR
return;
}
const JSStructuredCloneCallbacks* callbacks =
mWorkerPrivate->IsChromeWorker() ?
ChromeWorkerStructuredCloneCallbacks(false) :
WorkerStructuredCloneCallbacks(false);
- nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
+ WorkerStructuredCloneClosure closure;
JSAutoStructuredCloneBuffer buffer;
- if (!buffer.write(cx, value, callbacks, &clonedObjects)) {
+ if (!buffer.write(cx, value, callbacks, &closure)) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
- SendInternal(EmptyString(), Move(buffer), clonedObjects, aRv);
+ SendInternal(EmptyString(), Move(buffer), closure, aRv);
}
void
XMLHttpRequest::Send(nsFormData& aBody, ErrorResult& aRv)
{
mWorkerPrivate->AssertIsOnWorkerThread();
JSContext* cx = mWorkerPrivate->GetJSContext();
@@ -2246,25 +2253,24 @@ XMLHttpRequest::Send(nsFormData& aBody,
return;
}
const JSStructuredCloneCallbacks* callbacks =
mWorkerPrivate->IsChromeWorker() ?
ChromeWorkerStructuredCloneCallbacks(false) :
WorkerStructuredCloneCallbacks(false);
- nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
-
JSAutoStructuredCloneBuffer buffer;
- if (!buffer.write(cx, value, callbacks, &clonedObjects)) {
+ WorkerStructuredCloneClosure closure;
+ if (!buffer.write(cx, value, callbacks, &closure)) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
- SendInternal(EmptyString(), Move(buffer), clonedObjects, aRv);
+ SendInternal(EmptyString(), Move(buffer), closure, aRv);
}
void
XMLHttpRequest::Send(const ArrayBuffer& aBody, ErrorResult& aRv)
{
JS::Rooted<JSObject*> obj(mWorkerPrivate->GetJSContext(), aBody.Obj());
return Send(obj, aRv);
}
--- a/dom/workers/XMLHttpRequest.h
+++ b/dom/workers/XMLHttpRequest.h
@@ -23,16 +23,17 @@ class Blob;
}
}
BEGIN_WORKERS_NAMESPACE
class Proxy;
class XMLHttpRequestUpload;
class WorkerPrivate;
+class WorkerStructuredCloneClosure;
class XMLHttpRequest final: public nsXHREventTarget,
public WorkerFeature
{
public:
struct StateData
{
nsString mResponseText;
@@ -287,15 +288,15 @@ private:
void
DispatchPrematureAbortEvent(EventTarget* aTarget,
const nsAString& aEventType, bool aUploadTarget,
ErrorResult& aRv);
void
SendInternal(const nsAString& aStringBody,
JSAutoStructuredCloneBuffer&& aBody,
- nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
+ WorkerStructuredCloneClosure& aClosure,
ErrorResult& aRv);
};
END_WORKERS_NAMESPACE
#endif // mozilla_dom_workers_xmlhttprequest_h__
--- a/dom/workers/test/sharedWorker_sharedWorker.js
+++ b/dom/workers/test/sharedWorker_sharedWorker.js
@@ -74,15 +74,15 @@ onconnect = function(event) {
event.ports[0].onmessage = function(event) {
if (!(event instanceof MessageEvent)) {
throw new Error("'message' event is not a MessageEvent!");
}
if (!("ports" in event)) {
throw new Error("'message' event doesn't have a 'ports' property!");
}
- if (!(event.ports === null)) {
- throw new Error("'message' event has a non-null 'ports' property!");
+ if (event.ports === null) {
+ throw new Error("'message' event has a null 'ports' property!");
}
event.target.postMessage(event.data);
throw new Error(event.data);
};
};
--- a/ipc/glue/BackgroundChild.h
+++ b/ipc/glue/BackgroundChild.h
@@ -10,16 +10,17 @@
#include "mozilla/ipc/Transport.h"
class nsIDOMBlob;
class nsIIPCBackgroundChildCreateCallback;
namespace mozilla {
namespace dom {
+class BlobImpl;
class ContentChild;
class ContentParent;
class PBlobChild;
} // namespace dom
namespace ipc {
@@ -62,16 +63,20 @@ public:
// See above.
static bool
GetOrCreateForCurrentThread(nsIIPCBackgroundChildCreateCallback* aCallback);
static mozilla::dom::PBlobChild*
GetOrCreateActorForBlob(PBackgroundChild* aBackgroundActor,
nsIDOMBlob* aBlob);
+ static mozilla::dom::PBlobChild*
+ GetOrCreateActorForBlobImpl(PBackgroundChild* aBackgroundActor,
+ mozilla::dom::BlobImpl* aBlobImpl);
+
// See above.
static void
CloseForCurrentThread();
private:
// Only called by ContentChild or ContentParent.
static void
Startup();
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -9,16 +9,17 @@
#include "ServiceWorkerManagerChild.h"
#include "FileDescriptorSetChild.h"
#include "mozilla/media/MediaChild.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/PBlobChild.h"
#include "mozilla/dom/cache/ActorUtils.h"
#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/MessagePortChild.h"
#include "mozilla/ipc/PBackgroundTestChild.h"
#include "mozilla/layout/VsyncChild.h"
#include "mozilla/net/PUDPSocketChild.h"
#include "mozilla/dom/network/UDPSocketChild.h"
#include "nsID.h"
#include "nsTraceRefcnt.h"
namespace {
@@ -335,16 +336,38 @@ BackgroundChildImpl::AllocPMediaChild()
}
bool
BackgroundChildImpl::DeallocPMediaChild(media::PMediaChild *aActor)
{
return media::DeallocPMediaChild(aActor);
}
+// -----------------------------------------------------------------------------
+// MessageChannel/MessagePort API
+// -----------------------------------------------------------------------------
+
+dom::PMessagePortChild*
+BackgroundChildImpl::AllocPMessagePortChild(const nsID& aUUID,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID)
+{
+ nsRefPtr<dom::MessagePortChild> agent = new dom::MessagePortChild();
+ return agent.forget().take();
+}
+
+bool
+BackgroundChildImpl::DeallocPMessagePortChild(PMessagePortChild* aActor)
+{
+ nsRefPtr<dom::MessagePortChild> child =
+ dont_AddRef(static_cast<dom::MessagePortChild*>(aActor));
+ MOZ_ASSERT(child);
+ return true;
+}
+
} // namespace ipc
} // namespace mozilla
bool
TestChild::Recv__delete__(const nsCString& aTestArg)
{
MOZ_RELEASE_ASSERT(aTestArg == mTestArg,
"BackgroundTest message was corrupted!");
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -116,16 +116,23 @@ protected:
virtual bool
DeallocPCacheChild(dom::cache::PCacheChild* aActor) override;
virtual dom::cache::PCacheStreamControlChild*
AllocPCacheStreamControlChild() override;
virtual bool
DeallocPCacheStreamControlChild(dom::cache::PCacheStreamControlChild* aActor) override;
+
+ virtual PMessagePortChild*
+ AllocPMessagePortChild(const nsID& aUUID, const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID) override;
+
+ virtual bool
+ DeallocPMessagePortChild(PMessagePortChild* aActor) override;
};
class BackgroundChildImpl::ThreadLocal final
{
friend class nsAutoPtr<ThreadLocal>;
public:
nsAutoPtr<mozilla::dom::indexedDB::ThreadLocal> mIndexedDBThreadLocal;
--- a/ipc/glue/BackgroundImpl.cpp
+++ b/ipc/glue/BackgroundImpl.cpp
@@ -909,27 +909,37 @@ BackgroundChild::GetOrCreateForCurrentTh
return ChildImpl::GetOrCreateForCurrentThread(aCallback);
}
// static
PBlobChild*
BackgroundChild::GetOrCreateActorForBlob(PBackgroundChild* aBackgroundActor,
nsIDOMBlob* aBlob)
{
+ MOZ_ASSERT(aBlob);
+
+ nsRefPtr<BlobImpl> blobImpl = static_cast<Blob*>(aBlob)->Impl();
+ MOZ_ASSERT(blobImpl);
+
+ return GetOrCreateActorForBlobImpl(aBackgroundActor, blobImpl);
+}
+
+// static
+PBlobChild*
+BackgroundChild::GetOrCreateActorForBlobImpl(PBackgroundChild* aBackgroundActor,
+ BlobImpl* aBlobImpl)
+{
MOZ_ASSERT(aBackgroundActor);
- MOZ_ASSERT(aBlob);
+ MOZ_ASSERT(aBlobImpl);
MOZ_ASSERT(GetForCurrentThread(),
"BackgroundChild not created on this thread yet!");
MOZ_ASSERT(aBackgroundActor == GetForCurrentThread(),
"BackgroundChild is bound to a different thread!");
- nsRefPtr<BlobImpl> blobImpl = static_cast<Blob*>(aBlob)->Impl();
- MOZ_ASSERT(blobImpl);
-
- BlobChild* actor = BlobChild::GetOrCreate(aBackgroundActor, blobImpl);
+ BlobChild* actor = BlobChild::GetOrCreate(aBackgroundActor, aBlobImpl);
if (NS_WARN_IF(!actor)) {
return nullptr;
}
return actor;
}
// static
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -6,16 +6,17 @@
#include "BroadcastChannelParent.h"
#include "FileDescriptorSetParent.h"
#include "mozilla/media/MediaParent.h"
#include "mozilla/AppProcessChecker.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/PBlobParent.h"
+#include "mozilla/dom/MessagePortParent.h"
#include "mozilla/dom/ServiceWorkerRegistrar.h"
#include "mozilla/dom/cache/ActorUtils.h"
#include "mozilla/dom/indexedDB/ActorsParent.h"
#include "mozilla/dom/ipc/BlobParent.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "mozilla/ipc/PBackgroundTestParent.h"
@@ -34,16 +35,18 @@
#else
#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false)
#endif
using mozilla::ipc::AssertIsOnBackgroundThread;
using mozilla::dom::cache::PCacheParent;
using mozilla::dom::cache::PCacheStorageParent;
using mozilla::dom::cache::PCacheStreamControlParent;
+using mozilla::dom::MessagePortParent;
+using mozilla::dom::PMessagePortParent;
using mozilla::dom::UDPSocketParent;
namespace {
void
AssertIsInMainProcess()
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
@@ -558,16 +561,51 @@ BackgroundParentImpl::AllocPCacheStreamC
bool
BackgroundParentImpl::DeallocPCacheStreamControlParent(PCacheStreamControlParent* aActor)
{
dom::cache::DeallocPCacheStreamControlParent(aActor);
return true;
}
+PMessagePortParent*
+BackgroundParentImpl::AllocPMessagePortParent(const nsID& aUUID,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ return new MessagePortParent(aUUID);
+}
+
+bool
+BackgroundParentImpl::RecvPMessagePortConstructor(PMessagePortParent* aActor,
+ const nsID& aUUID,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ MessagePortParent* mp = static_cast<MessagePortParent*>(aActor);
+ return mp->Entangle(aDestinationUUID, aSequenceID);
+}
+
+bool
+BackgroundParentImpl::DeallocPMessagePortParent(PMessagePortParent* aActor)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<MessagePortParent*>(aActor);
+ return true;
+}
+
} // namespace ipc
} // namespace mozilla
void
TestParent::ActorDestroy(ActorDestroyReason aWhy)
{
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -124,14 +124,28 @@ protected:
AllocPUDPSocketParent(const OptionalPrincipalInfo& pInfo,
const nsCString& aFilter) override;
virtual bool
RecvPUDPSocketConstructor(PUDPSocketParent*,
const OptionalPrincipalInfo& aPrincipalInfo,
const nsCString& aFilter) override;
virtual bool
DeallocPUDPSocketParent(PUDPSocketParent*) override;
+
+ virtual PMessagePortParent*
+ AllocPMessagePortParent(const nsID& aUUID,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID) override;
+
+ virtual bool
+ RecvPMessagePortConstructor(PMessagePortParent* aActor,
+ const nsID& aUUID,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID) override;
+
+ virtual bool
+ DeallocPMessagePortParent(PMessagePortParent* aActor) override;
};
} // namespace ipc
} // namespace mozilla
#endif // mozilla_ipc_backgroundparentimpl_h__
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -5,16 +5,17 @@
include protocol PBackgroundIDBFactory;
include protocol PBackgroundTest;
include protocol PBlob;
include protocol PBroadcastChannel;
include protocol PCache;
include protocol PCacheStorage;
include protocol PCacheStreamControl;
include protocol PFileDescriptorSet;
+include protocol PMessagePort;
include protocol PMedia;
include protocol PServiceWorkerManager;
include protocol PUDPSocket;
include protocol PVsync;
include DOMTypes;
include PBackgroundSharedTypes;
include PBackgroundIDBSharedTypes;
@@ -30,16 +31,17 @@ sync protocol PBackground
manages PBackgroundIDBFactory;
manages PBackgroundTest;
manages PBlob;
manages PBroadcastChannel;
manages PCache;
manages PCacheStorage;
manages PCacheStreamControl;
manages PFileDescriptorSet;
+ manages PMessagePort;
manages PMedia;
manages PServiceWorkerManager;
manages PUDPSocket;
manages PVsync;
parent:
// Only called at startup during mochitests to check the basic infrastructure.
PBackgroundTest(nsCString testArg);
@@ -54,16 +56,18 @@ parent:
bool privateBrowsing);
PServiceWorkerManager();
ShutdownServiceWorkerRegistrar();
PCacheStorage(Namespace aNamespace, PrincipalInfo aPrincipalInfo);
+ PMessagePort(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
+
child:
PCache();
PCacheStreamControl();
both:
PBlob(BlobConstructorParams params);
PFileDescriptorSet(FileDescriptor fd);
--- a/js/public/StructuredClone.h
+++ b/js/public/StructuredClone.h
@@ -143,17 +143,17 @@ JS_ReadStructuredClone(JSContext* cx, ui
JS_PUBLIC_API(bool)
JS_WriteStructuredClone(JSContext* cx, JS::HandleValue v, uint64_t** datap, size_t* nbytesp,
const JSStructuredCloneCallbacks* optionalCallbacks,
void* closure, JS::HandleValue transferable);
JS_PUBLIC_API(bool)
JS_ClearStructuredClone(uint64_t* data, size_t nbytes,
const JSStructuredCloneCallbacks* optionalCallbacks,
- void* closure);
+ void *closure, bool freeData = true);
JS_PUBLIC_API(bool)
JS_StructuredCloneHasTransferables(const uint64_t* data, size_t nbytes, bool* hasTransferable);
JS_PUBLIC_API(bool)
JS_StructuredClone(JSContext* cx, JS::HandleValue v, JS::MutableHandleValue vp,
const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -1903,20 +1903,22 @@ JS_WriteStructuredClone(JSContext* cx, H
optionalCallbacks :
cx->runtime()->structuredCloneCallbacks;
return WriteStructuredClone(cx, value, bufp, nbytesp, callbacks, closure, transferable);
}
JS_PUBLIC_API(bool)
JS_ClearStructuredClone(uint64_t* data, size_t nbytes,
const JSStructuredCloneCallbacks* optionalCallbacks,
- void* closure)
+ void* closure, bool freeData)
{
DiscardTransferables(data, nbytes, optionalCallbacks, closure);
- js_free(data);
+ if (freeData) {
+ js_free(data);
+ }
return true;
}
JS_PUBLIC_API(bool)
JS_StructuredCloneHasTransferables(const uint64_t* data, size_t nbytes,
bool* hasTransferable)
{
*hasTransferable = StructuredCloneHasTransferObjects(data, nbytes);
deleted file mode 100644
--- a/testing/web-platform/meta/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/event-ports-dedicated.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[event-ports-dedicated.html]
- type: testharness
- [e.ports in dedicated worker]
- expected: FAIL
-