--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -18,256 +18,16 @@
#include "nsGlobalWindow.h"
#include "nsIPresShell.h"
#include "nsIPrincipal.h"
#include "nsPresContext.h"
namespace mozilla {
namespace dom {
-namespace {
-
-struct StructuredCloneInfo
-{
- PostMessageEvent* event;
- 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;
-};
-
-} // 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");
-
- // What we get back from the reader is a FileListClonedData.
- // From that we create a new FileList.
- FileListClonedData* fileListClonedData;
- if (JS_ReadBytes(reader, &fileListClonedData, sizeof(fileListClonedData))) {
- MOZ_ASSERT(fileListClonedData);
-
- // nsRefPtr<FileList> 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<FileList> fileList =
- FileList::Create(scInfo->window, fileListClonedData);
- if (!fileList || !ToJSValue(cx, fileList, &val)) {
- return nullptr;
- }
- }
-
- return &val.toObject();
- }
- }
-
- 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 (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;
- }
- }
- }
-
- // See if this is a FileList object.
- {
- FileList* fileList = nullptr;
- if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, obj, fileList))) {
- nsRefPtr<FileListClonedData> fileListClonedData =
- fileList->CreateClonedData();
- MOZ_ASSERT(fileListClonedData);
- FileListClonedData* ptr = fileListClonedData.get();
- if (JS_WriteUint32Pair(writer, SCTAG_DOM_FILELIST, 0) &&
- JS_WriteBytes(writer, &ptr, sizeof(ptr))) {
- scInfo->event->StoreISupports(fileListClonedData);
- return true;
- }
- }
- }
-
- 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)
-{
- if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
- MOZ_ASSERT(aClosure);
- MOZ_ASSERT(!aContent);
-
- StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
- MessagePort::ForceClose(scInfo->event->GetPortIdentifier(aExtraData));
- }
-}
-
PostMessageEvent::PostMessageEvent(nsGlobalWindow* aSource,
const nsAString& aCallerOrigin,
nsGlobalWindow* aTargetWindow,
nsIPrincipal* aProvidedPrincipal,
bool aTrustedCaller)
: mSource(aSource),
mCallerOrigin(aCallerOrigin),
mTargetWindow(aTargetWindow),
@@ -277,30 +37,16 @@ PostMessageEvent::PostMessageEvent(nsGlo
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!");
@@ -342,38 +88,34 @@ PostMessageEvent::Run()
// 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)) {
+ nsCOMPtr<nsPIDOMWindow> window = targetWindow.get();
+ if (!Read(window, cx, &messageData)) {
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));
+ GetTransferredPorts()));
// 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;
@@ -387,24 +129,10 @@ PostMessageEvent::Run()
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, 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);
-}
-
} // namespace dom
} // namespace mozilla
--- a/dom/base/PostMessageEvent.h
+++ b/dom/base/PostMessageEvent.h
@@ -2,17 +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/. */
#ifndef mozilla_dom_PostMessageEvent_h
#define mozilla_dom_PostMessageEvent_h
-#include "js/StructuredClone.h"
+#include "mozilla/dom/StructuredCloneHelper.h"
#include "nsCOMPtr.h"
#include "nsRefPtr.h"
#include "nsTArray.h"
#include "nsThreadUtils.h"
class nsGlobalWindow;
class nsIPrincipal;
class nsPIDOMWindow;
@@ -23,87 +23,33 @@ 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 StructuredCloneHelper
{
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, 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;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_PostMessageEvent_h
--- a/dom/base/StructuredCloneHelper.cpp
+++ b/dom/base/StructuredCloneHelper.cpp
@@ -1,16 +1,20 @@
/* -*- 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 "StructuredCloneHelper.h"
+#include "mozilla/dom/BlobBinding.h"
+#include "mozilla/dom/FileListBinding.h"
+#include "mozilla/dom/StructuredCloneTags.h"
+
namespace mozilla {
namespace dom {
namespace {
JSObject*
StructuredCloneCallbacksRead(JSContext* aCx,
JSStructuredCloneReader* aReader,
@@ -94,27 +98,40 @@ const JSStructuredCloneCallbacks gCallba
StructuredCloneCallbacksError,
StructuredCloneCallbacksReadTransfer,
StructuredCloneCallbacksWriteTransfer,
StructuredCloneCallbacksFreeTransfer
};
} // anonymous namespace
+// StructuredCloneHelperInternal class
+
bool
StructuredCloneHelperInternal::Write(JSContext* aCx,
JS::Handle<JS::Value> aValue)
{
MOZ_ASSERT(!mBuffer, "Double Write is not allowed");
mBuffer = new JSAutoStructuredCloneBuffer(&gCallbacks, this);
return mBuffer->write(aCx, aValue, &gCallbacks, this);
}
bool
+StructuredCloneHelperInternal::Write(JSContext* aCx,
+ JS::Handle<JS::Value> aValue,
+ JS::Handle<JS::Value> aTransfer)
+{
+ MOZ_ASSERT(!mBuffer, "Double Write is not allowed");
+
+ mBuffer = new JSAutoStructuredCloneBuffer(&gCallbacks, this);
+ return mBuffer->write(aCx, aValue, aTransfer, &gCallbacks, this);
+}
+
+bool
StructuredCloneHelperInternal::Read(JSContext* aCx,
JS::MutableHandle<JS::Value> aValue)
{
MOZ_ASSERT(mBuffer, "Read() without Write() is not allowed.");
bool ok = mBuffer->read(aCx, aValue, &gCallbacks, this);
mBuffer = nullptr;
return ok;
@@ -127,17 +144,16 @@ StructuredCloneHelperInternal::ReadTrans
void* aContent,
uint64_t aExtraData,
JS::MutableHandleObject aReturnObject)
{
MOZ_CRASH("Nothing to read.");
return false;
}
-
bool
StructuredCloneHelperInternal::WriteTransferCallback(JSContext* aCx,
JS::Handle<JSObject*> aObj,
uint32_t* aTag,
JS::TransferableOwnership* aOwnership,
void** aContent,
uint64_t* aExtraData)
{
@@ -149,10 +165,225 @@ void
StructuredCloneHelperInternal::FreeTransferCallback(uint32_t aTag,
JS::TransferableOwnership aOwnership,
void* aContent,
uint64_t aExtraData)
{
MOZ_CRASH("Nothing to free.");
}
+// StructuredCloneHelper class
+
+StructuredCloneHelper::StructuredCloneHelper(uint32_t aFlags)
+ : mFlags(aFlags)
+ , mParent(nullptr)
+{}
+
+StructuredCloneHelper::~StructuredCloneHelper()
+{}
+
+bool
+StructuredCloneHelper::Write(JSContext* aCx,
+ JS::Handle<JS::Value> aValue,
+ JS::Handle<JS::Value> aTransfer)
+{
+ bool ok = StructuredCloneHelperInternal::Write(aCx, aValue, aTransfer);
+ mTransferringPort.Clear();
+ return ok;
+}
+
+bool
+StructuredCloneHelper::Read(nsISupports* aParent,
+ JSContext* aCx,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ mozilla::AutoRestore<nsISupports*> guard(mParent);
+ mParent = aParent;
+
+ return StructuredCloneHelperInternal::Read(aCx, aValue);
+}
+
+JSObject*
+StructuredCloneHelper::ReadCallback(JSContext* aCx,
+ JSStructuredCloneReader* aReader,
+ uint32_t aTag,
+ uint32_t aIndex)
+{
+ if (aTag == SCTAG_DOM_BLOB) {
+ MOZ_ASSERT(!(mFlags & eBlobNotSupported));
+
+ BlobImpl* blobImpl;
+ if (JS_ReadBytes(aReader, &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(aCx);
+ {
+ nsRefPtr<Blob> blob = Blob::Create(mParent, blobImpl);
+ if (!ToJSValue(aCx, blob, &val)) {
+ return nullptr;
+ }
+ }
+
+ return &val.toObject();
+ }
+ }
+
+ if (aTag == SCTAG_DOM_FILELIST) {
+ MOZ_ASSERT(!(mFlags & eFileListNotSupported));
+
+ FileListClonedData* fileListClonedData;
+ if (JS_ReadBytes(aReader, &fileListClonedData,
+ sizeof(fileListClonedData))) {
+ MOZ_ASSERT(fileListClonedData);
+
+ // nsRefPtr<FileList> 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);
+ {
+ nsRefPtr<FileList> fileList =
+ FileList::Create(mParent, fileListClonedData);
+ if (!fileList || !ToJSValue(aCx, fileList, &val)) {
+ return nullptr;
+ }
+ }
+
+ return &val.toObject();
+ }
+ }
+
+ return NS_DOMReadStructuredClone(aCx, aReader, aTag, aIndex, nullptr);
+}
+
+bool
+StructuredCloneHelper::WriteCallback(JSContext* aCx,
+ JSStructuredCloneWriter* aWriter,
+ JS::Handle<JSObject*> aObj)
+{
+ // See if this is a File/Blob object.
+ if (!(mFlags & eBlobNotSupported)) {
+ Blob* blob = nullptr;
+ if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
+ BlobImpl* blobImpl = blob->Impl();
+ return JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB, 0) &&
+ JS_WriteBytes(aWriter, &blobImpl, sizeof(blobImpl)) &&
+ StoreISupports(blobImpl);
+ }
+ }
+
+ if (!(mFlags & eFileListNotSupported)) {
+ FileList* fileList = nullptr;
+ if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList))) {
+ nsRefPtr<FileListClonedData> fileListClonedData =
+ fileList->CreateClonedData();
+ MOZ_ASSERT(fileListClonedData);
+ FileListClonedData* ptr = fileListClonedData.get();
+ return JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST, 0) &&
+ JS_WriteBytes(aWriter, &ptr, sizeof(ptr)) &&
+ StoreISupports(fileListClonedData);
+ }
+ }
+
+ return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
+}
+
+bool
+StructuredCloneHelper::ReadTransferCallback(JSContext* aCx,
+ JSStructuredCloneReader* aReader,
+ uint32_t aTag,
+ void* aContent,
+ uint64_t aExtraData,
+ JS::MutableHandleObject aReturnObject)
+{
+ if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
+ MOZ_ASSERT(!(mFlags & eMessagePortNotSupported));
+
+ // This can be null.
+ nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mParent);
+
+ MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
+ const MessagePortIdentifier& portIdentifier = mPortIdentifiers[aExtraData];
+
+ // aExtraData is the index of this port identifier.
+ ErrorResult rv;
+ nsRefPtr<MessagePort> port =
+ MessagePort::Create(window, portIdentifier, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ return false;
+ }
+
+ mTransferredPorts.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;
+}
+
+
+bool
+StructuredCloneHelper::WriteTransferCallback(JSContext* aCx,
+ JS::Handle<JSObject*> aObj,
+ uint32_t* aTag,
+ JS::TransferableOwnership* aOwnership,
+ void** aContent,
+ uint64_t* aExtraData)
+{
+ if (!(mFlags & eMessagePortNotSupported)) {
+ MessagePortBase* port = nullptr;
+ nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
+ if (NS_SUCCEEDED(rv)) {
+ if (mTransferringPort.Contains(port)) {
+ // No duplicates.
+ return false;
+ }
+
+ // We use aExtraData to store the index of this new port identifier.
+ *aExtraData = mPortIdentifiers.Length();
+ MessagePortIdentifier* identifier = mPortIdentifiers.AppendElement();
+
+ if (!port->CloneAndDisentangle(*identifier)) {
+ return false;
+ }
+
+ mTransferringPort.AppendElement(port);
+
+ *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
+ *aOwnership = JS::SCTAG_TMO_CUSTOM;
+ *aContent = nullptr;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+StructuredCloneHelper::FreeTransferCallback(uint32_t aTag,
+ JS::TransferableOwnership aOwnership,
+ void* aContent,
+ uint64_t aExtraData)
+{
+ if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
+ MOZ_ASSERT(!(mFlags & eMessagePortNotSupported));
+ MOZ_ASSERT(!aContent);
+ MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
+ MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
+ }
+}
+
} // dom namespace
} // mozilla namespace
--- a/dom/base/StructuredCloneHelper.h
+++ b/dom/base/StructuredCloneHelper.h
@@ -3,16 +3,18 @@
* 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_StructuredCloneHelper_h
#define mozilla_dom_StructuredCloneHelper_h
#include "js/StructuredClone.h"
#include "nsAutoPtr.h"
+#include "nsISupports.h"
+#include "nsTArray.h"
namespace mozilla {
namespace dom {
class StructuredCloneHelperInternal
{
public:
// These methods should be implemented in order to clone data.
@@ -64,12 +66,115 @@ public:
bool Read(JSContext* aCx,
JS::MutableHandle<JS::Value> aValue);
protected:
nsAutoPtr<JSAutoStructuredCloneBuffer> mBuffer;
};
+class MessagePortBase;
+class MessagePortIdentifier;
+
+class StructuredCloneHelper : public StructuredCloneHelperInternal
+{
+public:
+ enum StructuredCloneHelperFlags {
+ eAll = 0,
+
+ // Disable the cloning of blobs. If a blob is part of the cloning value,
+ // an exception will be thrown.
+ eBlobNotSupported = 1 << 0,
+
+ // Disable the cloning of FileLists. If a FileList is part of the cloning
+ // value, an exception will be thrown.
+ eFileListNotSupported = 1 << 1,
+
+ // MessagePort can just be transfered. Using this flag we do not support
+ // the transfering.
+ eMessagePortNotSupported = 1 << 2,
+ };
+
+ // aFlags is a bitmap of StructuredCloneHelperFlags.
+ explicit StructuredCloneHelper(uint32_t aFlags = eAll);
+ virtual ~StructuredCloneHelper();
+
+ bool Write(JSContext* aCx,
+ JS::Handle<JS::Value> aValue,
+ JS::Handle<JS::Value> aTransfer);
+
+ bool Read(nsISupports* aParent,
+ JSContext* aCx,
+ JS::MutableHandle<JS::Value> aValue);
+
+ nsTArray<nsRefPtr<MessagePortBase>>& GetTransferredPorts()
+ {
+ MOZ_ASSERT(!(mFlags & eMessagePortNotSupported));
+ return mTransferredPorts;
+ }
+
+ // Custom Callbacks
+
+ virtual JSObject* ReadCallback(JSContext* aCx,
+ JSStructuredCloneReader* aReader,
+ uint32_t aTag,
+ uint32_t aIndex) override;
+
+ virtual bool WriteCallback(JSContext* aCx,
+ JSStructuredCloneWriter* aWriter,
+ JS::Handle<JSObject*> aObj) override;
+
+ virtual bool ReadTransferCallback(JSContext* aCx,
+ JSStructuredCloneReader* aReader,
+ uint32_t aTag,
+ void* aContent,
+ uint64_t aExtraData,
+ JS::MutableHandleObject aReturnObject) override;
+
+ virtual bool WriteTransferCallback(JSContext* aCx,
+ JS::Handle<JSObject*> aObj,
+ uint32_t* aTag,
+ JS::TransferableOwnership* aOwnership,
+ void** aContent,
+ uint64_t* aExtraData) override;
+
+ virtual void FreeTransferCallback(uint32_t aTag,
+ JS::TransferableOwnership aOwnership,
+ void* aContent,
+ uint64_t aExtraData) override;
+private:
+ bool StoreISupports(nsISupports* aSupports)
+ {
+ MOZ_ASSERT(aSupports);
+ mSupportsArray.AppendElement(aSupports);
+ return true;
+ }
+
+ // This is our bitmap.
+ uint32_t mFlags;
+
+ // Useful for the structured clone algorithm:
+
+ nsTArray<nsCOMPtr<nsISupports>> mSupportsArray;
+
+ // This raw pointer is set and unset into the ::Read(). It's always null
+ // outside that method. For this reason it's a raw pointer.
+ nsISupports* MOZ_NON_OWNING_REF mParent;
+
+ // This hashtable contains the ports while doing write (transferring and
+ // mapping transferred objects to the objects in the clone). It's an empty
+ // array outside the 'Write()' method.
+ nsTArray<nsRefPtr<MessagePortBase>> mTransferringPort;
+
+ // This array contains the ports once we've finished the reading. It's
+ // generated from the mPortIdentifiers array.
+ nsTArray<nsRefPtr<MessagePortBase>> mTransferredPorts;
+
+ // This array contains the identifiers of the MessagePorts. Based on these we
+ // are able to reconnect the new transferred ports with the other
+ // MessageChannel ports.
+ nsTArray<MessagePortIdentifier> mPortIdentifiers;
+};
+
} // dom namespace
} // mozilla namespace
#endif // mozilla_dom_StructuredCloneHelper_h
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -8559,17 +8559,17 @@ nsGlobalWindow::PostMessageMozOuter(JSCo
origin,
this,
providedPrincipal,
nsContentUtils::IsCallerChrome());
JS::Rooted<JS::Value> message(aCx, aMessage);
JS::Rooted<JS::Value> transfer(aCx, aTransfer);
- if (!event->Write(aCx, message, transfer, this)) {
+ if (!event->Write(aCx, message, transfer)) {
aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
aError = NS_DispatchToCurrentThread(event);
}
void