forked from mirrors/gecko-dev
Bug 1659025 - Implement [Transferable] for ReadableStream r=smaug,sfink
Differential Revision: https://phabricator.services.mozilla.com/D139525
This commit is contained in:
parent
ff14b28782
commit
2b7f799d55
32 changed files with 1550 additions and 214 deletions
|
|
@ -46,12 +46,18 @@
|
|||
#include "mozilla/dom/MessagePortBinding.h"
|
||||
#include "mozilla/dom/OffscreenCanvas.h"
|
||||
#include "mozilla/dom/OffscreenCanvasBinding.h"
|
||||
#include "mozilla/dom/ReadableStream.h"
|
||||
#include "mozilla/dom/ReadableStreamBinding.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/StructuredCloneBlob.h"
|
||||
#include "mozilla/dom/StructuredCloneHolderBinding.h"
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
#include "mozilla/dom/ToJSValue.h"
|
||||
#include "mozilla/dom/TransformStream.h"
|
||||
#include "mozilla/dom/TransformStreamBinding.h"
|
||||
#include "mozilla/dom/WebIDLSerializable.h"
|
||||
#include "mozilla/dom/WritableStream.h"
|
||||
#include "mozilla/dom/WritableStreamBinding.h"
|
||||
#include "mozilla/dom/WorkerCommon.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/fallible.h"
|
||||
|
|
@ -1115,7 +1121,26 @@ bool StructuredCloneHolder::CustomWriteHandler(
|
|||
return WriteFullySerializableObjects(aCx, aWriter, aObj);
|
||||
}
|
||||
|
||||
bool StructuredCloneHolder::CustomReadTransferHandler(
|
||||
already_AddRefed<MessagePort> StructuredCloneHolder::ReceiveMessagePort(
|
||||
uint64_t aIndex) {
|
||||
if (NS_WARN_IF(aIndex >= mPortIdentifiers.Length())) {
|
||||
return nullptr;
|
||||
}
|
||||
UniqueMessagePortId portId(mPortIdentifiers[aIndex]);
|
||||
|
||||
ErrorResult rv;
|
||||
RefPtr<MessagePort> port = MessagePort::Create(mGlobal, portId, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
rv.SuppressException();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return port.forget();
|
||||
}
|
||||
|
||||
// TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
|
||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY bool
|
||||
StructuredCloneHolder::CustomReadTransferHandler(
|
||||
JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
|
||||
void* aContent, uint64_t aExtraData,
|
||||
JS::MutableHandleObject aReturnObject) {
|
||||
|
|
@ -1127,16 +1152,10 @@ bool StructuredCloneHolder::CustomReadTransferHandler(
|
|||
return false;
|
||||
}
|
||||
#endif
|
||||
MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
|
||||
UniqueMessagePortId portIdentifier(mPortIdentifiers[aExtraData]);
|
||||
|
||||
ErrorResult rv;
|
||||
RefPtr<MessagePort> port = MessagePort::Create(mGlobal, portIdentifier, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
rv.SuppressException();
|
||||
RefPtr<MessagePort> port = ReceiveMessagePort(aExtraData);
|
||||
if (!port) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mTransferredPorts.AppendElement(port);
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
|
|
@ -1186,10 +1205,56 @@ bool StructuredCloneHolder::CustomReadTransferHandler(
|
|||
return true;
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_READABLESTREAM) {
|
||||
#ifdef FUZZING
|
||||
if (aExtraData >= mPortIdentifiers.Length()) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
RefPtr<MessagePort> port = ReceiveMessagePort(aExtraData);
|
||||
if (!port) {
|
||||
return false;
|
||||
}
|
||||
nsCOMPtr<nsIGlobalObject> global = mGlobal;
|
||||
return ReadableStream::ReceiveTransfer(aCx, global, *port, aReturnObject);
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_WRITABLESTREAM) {
|
||||
#ifdef FUZZING
|
||||
if (aExtraData >= mPortIdentifiers.Length()) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
RefPtr<MessagePort> port = ReceiveMessagePort(aExtraData);
|
||||
if (!port) {
|
||||
return false;
|
||||
}
|
||||
nsCOMPtr<nsIGlobalObject> global = mGlobal;
|
||||
return WritableStream::ReceiveTransfer(aCx, global, *port, aReturnObject);
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_TRANSFORMSTREAM) {
|
||||
#ifdef FUZZING
|
||||
if (aExtraData + 1 >= mPortIdentifiers.Length()) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
RefPtr<MessagePort> port1 = ReceiveMessagePort(aExtraData);
|
||||
RefPtr<MessagePort> port2 = ReceiveMessagePort(aExtraData + 1);
|
||||
if (!port1 || !port2) {
|
||||
return false;
|
||||
}
|
||||
nsCOMPtr<nsIGlobalObject> global = mGlobal;
|
||||
return TransformStream::ReceiveTransfer(aCx, global, *port1, *port2,
|
||||
aReturnObject);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StructuredCloneHolder::CustomWriteTransferHandler(
|
||||
// TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
|
||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY bool
|
||||
StructuredCloneHolder::CustomWriteTransferHandler(
|
||||
JSContext* aCx, JS::Handle<JSObject*> aObj, uint32_t* aTag,
|
||||
JS::TransferableOwnership* aOwnership, void** aContent,
|
||||
uint64_t* aExtraData) {
|
||||
|
|
@ -1263,6 +1328,68 @@ bool StructuredCloneHolder::CustomWriteTransferHandler(
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
RefPtr<ReadableStream> stream;
|
||||
rv = UNWRAP_OBJECT(ReadableStream, &obj, stream);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
MOZ_ASSERT(stream);
|
||||
|
||||
*aTag = SCTAG_DOM_READABLESTREAM;
|
||||
*aOwnership = JS::SCTAG_TMO_CUSTOM;
|
||||
*aContent = nullptr;
|
||||
|
||||
UniqueMessagePortId id;
|
||||
if (!stream->Transfer(aCx, id)) {
|
||||
return false;
|
||||
}
|
||||
*aExtraData = mPortIdentifiers.Length();
|
||||
mPortIdentifiers.AppendElement(id.release());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
RefPtr<WritableStream> stream;
|
||||
rv = UNWRAP_OBJECT(WritableStream, &obj, stream);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
MOZ_ASSERT(stream);
|
||||
|
||||
*aTag = SCTAG_DOM_WRITABLESTREAM;
|
||||
*aOwnership = JS::SCTAG_TMO_CUSTOM;
|
||||
*aContent = nullptr;
|
||||
|
||||
UniqueMessagePortId id;
|
||||
if (!stream->Transfer(aCx, id)) {
|
||||
return false;
|
||||
}
|
||||
*aExtraData = mPortIdentifiers.Length();
|
||||
mPortIdentifiers.AppendElement(id.release());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
RefPtr<TransformStream> stream;
|
||||
rv = UNWRAP_OBJECT(TransformStream, &obj, stream);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
MOZ_ASSERT(stream);
|
||||
|
||||
*aTag = SCTAG_DOM_TRANSFORMSTREAM;
|
||||
*aOwnership = JS::SCTAG_TMO_CUSTOM;
|
||||
*aContent = nullptr;
|
||||
|
||||
UniqueMessagePortId id1;
|
||||
UniqueMessagePortId id2;
|
||||
if (!stream->Transfer(aCx, id1, id2)) {
|
||||
return false;
|
||||
}
|
||||
*aExtraData = mPortIdentifiers.Length();
|
||||
mPortIdentifiers.AppendElement(id1.release());
|
||||
mPortIdentifiers.AppendElement(id2.release());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -1301,6 +1428,31 @@ void StructuredCloneHolder::CustomFreeTransferHandler(
|
|||
delete data;
|
||||
return;
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_READABLESTREAM || aTag == SCTAG_DOM_WRITABLESTREAM) {
|
||||
MOZ_ASSERT(!aContent);
|
||||
#ifdef FUZZING
|
||||
if (aExtraData >= mPortIdentifiers.Length()) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
|
||||
MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_TRANSFORMSTREAM) {
|
||||
MOZ_ASSERT(!aContent);
|
||||
#ifdef FUZZING
|
||||
if (aExtraData + 1 >= mPortIdentifiers.Length()) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
MOZ_ASSERT(aExtraData + 1 < mPortIdentifiers.Length());
|
||||
MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
|
||||
MessagePort::ForceClose(mPortIdentifiers[aExtraData + 1]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool StructuredCloneHolder::CustomCanTransferHandler(
|
||||
|
|
@ -1342,6 +1494,40 @@ bool StructuredCloneHolder::CustomCanTransferHandler(
|
|||
}
|
||||
}
|
||||
|
||||
{
|
||||
ReadableStream* stream = nullptr;
|
||||
nsresult rv = UNWRAP_OBJECT(ReadableStream, &obj, stream);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// https://streams.spec.whatwg.org/#ref-for-transfer-steps
|
||||
// Step 1: If ! IsReadableStreamLocked(value) is true, throw a
|
||||
// "DataCloneError" DOMException.
|
||||
return !IsReadableStreamLocked(stream);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
WritableStream* stream = nullptr;
|
||||
nsresult rv = UNWRAP_OBJECT(WritableStream, &obj, stream);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// https://streams.spec.whatwg.org/#ref-for-transfer-steps①
|
||||
// Step 1: If ! IsWritableStreamLocked(value) is true, throw a
|
||||
// "DataCloneError" DOMException.
|
||||
return !IsWritableStreamLocked(stream);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
TransformStream* stream = nullptr;
|
||||
nsresult rv = UNWRAP_OBJECT(TransformStream, &obj, stream);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// https://streams.spec.whatwg.org/#ref-for-transfer-steps②
|
||||
// Step 3 + 4: If ! Is{Readable,Writable}StreamLocked(value) is true,
|
||||
// throw a "DataCloneError" DOMException.
|
||||
return !IsReadableStreamLocked(stream->Readable()) &&
|
||||
!IsWritableStreamLocked(stream->Writable());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -335,6 +335,8 @@ class StructuredCloneHolder : public StructuredCloneHolderBase {
|
|||
|
||||
void SameProcessScopeRequired(bool* aSameProcessScopeRequired);
|
||||
|
||||
already_AddRefed<MessagePort> ReceiveMessagePort(uint64_t aIndex);
|
||||
|
||||
bool mSupportsCloning;
|
||||
bool mSupportsTransferring;
|
||||
|
||||
|
|
|
|||
|
|
@ -140,6 +140,12 @@ enum StructuredCloneTags : uint32_t {
|
|||
|
||||
SCTAG_DOM_CLONED_ERROR_OBJECT,
|
||||
|
||||
SCTAG_DOM_READABLESTREAM,
|
||||
|
||||
SCTAG_DOM_WRITABLESTREAM,
|
||||
|
||||
SCTAG_DOM_TRANSFORMSTREAM,
|
||||
|
||||
// IMPORTANT: If you plan to add an new IDB tag, it _must_ be add before the
|
||||
// "less stable" tags!
|
||||
};
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ class EventMessageAutoOverride;
|
|||
class ExtendableEvent;
|
||||
class KeyboardEvent;
|
||||
class MouseEvent;
|
||||
class MessageEvent;
|
||||
class TimeEvent;
|
||||
class UIEvent;
|
||||
class WantsPopupControlCheck;
|
||||
|
|
@ -127,6 +128,9 @@ class Event : public nsISupports, public nsWrapperCache {
|
|||
// CustomEvent has a non-autogeneratable initCustomEvent.
|
||||
virtual CustomEvent* AsCustomEvent() { return nullptr; }
|
||||
|
||||
// MessageEvent has a non-autogeneratable initMessageEvent and more.
|
||||
virtual MessageEvent* AsMessageEvent() { return nullptr; }
|
||||
|
||||
void InitEvent(const nsAString& aEventTypeArg, bool aCanBubble,
|
||||
bool aCancelable) {
|
||||
InitEvent(aEventTypeArg, aCanBubble ? CanBubble::eYes : CanBubble::eNo,
|
||||
|
|
|
|||
|
|
@ -43,8 +43,10 @@ class MessageEvent final : public Event {
|
|||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(MessageEvent, Event)
|
||||
|
||||
virtual JSObject* WrapObjectInternal(
|
||||
JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
JSObject* WrapObjectInternal(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
MessageEvent* AsMessageEvent() override { return this; }
|
||||
|
||||
void GetData(JSContext* aCx, JS::MutableHandle<JS::Value> aData,
|
||||
ErrorResult& aRv);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
#include "mozilla/dom/ByteStreamHelpers.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/Promise-inl.h"
|
||||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
#include "mozilla/dom/ReadableByteStreamController.h"
|
||||
#include "mozilla/dom/ReadableByteStreamControllerBinding.h"
|
||||
#include "mozilla/dom/ReadIntoRequest.h"
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ using OwningReadableStreamReader =
|
|||
OwningReadableStreamDefaultReaderOrReadableStreamBYOBReader;
|
||||
class NativeUnderlyingSource;
|
||||
class BodyStreamHolder;
|
||||
class UniqueMessagePortId;
|
||||
class MessagePort;
|
||||
|
||||
class ReadableStream final : public nsISupports, public nsWrapperCache {
|
||||
public:
|
||||
|
|
@ -95,6 +97,15 @@ class ReadableStream final : public nsISupports, public nsWrapperCache {
|
|||
|
||||
void ReleaseObjects();
|
||||
|
||||
// [Transferable]
|
||||
// https://html.spec.whatwg.org/multipage/structured-data.html#transfer-steps
|
||||
MOZ_CAN_RUN_SCRIPT bool Transfer(JSContext* aCx,
|
||||
UniqueMessagePortId& aPortId);
|
||||
// https://html.spec.whatwg.org/multipage/structured-data.html#transfer-receiving-steps
|
||||
static MOZ_CAN_RUN_SCRIPT bool ReceiveTransfer(
|
||||
JSContext* aCx, nsIGlobalObject* aGlobal, MessagePort& aPort,
|
||||
JS::MutableHandle<JSObject*> aReturnObject);
|
||||
|
||||
public:
|
||||
nsIGlobalObject* GetParentObject() const { return mGlobal; }
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
#include "mozilla/HoldDropJSObjects.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/Promise-inl.h"
|
||||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
#include "mozilla/dom/ReadableStream.h"
|
||||
#include "mozilla/dom/ReadableStreamController.h"
|
||||
#include "mozilla/dom/ReadableStreamDefaultController.h"
|
||||
|
|
@ -503,7 +502,8 @@ void SetUpReadableStreamDefaultController(
|
|||
}
|
||||
|
||||
// Step 10.
|
||||
RefPtr<Promise> startPromise = Promise::Create(GetIncumbentGlobal(), aRv);
|
||||
RefPtr<Promise> startPromise =
|
||||
Promise::Create(aStream->GetParentObject(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
#include "js/TypeDecls.h"
|
||||
#include "js/experimental/TypedData.h"
|
||||
#include "mozilla/dom/ByteStreamHelpers.h"
|
||||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
#include "mozilla/dom/Promise-inl.h"
|
||||
#include "mozilla/dom/ReadIntoRequest.h"
|
||||
#include "mozilla/dom/ReadableStream.h"
|
||||
|
|
|
|||
1065
dom/streams/Transferable.cpp
Normal file
1065
dom/streams/Transferable.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -12,7 +12,6 @@
|
|||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/Promise-inl.h"
|
||||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
#include "mozilla/dom/WritableStream.h"
|
||||
#include "mozilla/dom/ReadableStream.h"
|
||||
#include "mozilla/dom/RootedDictionary.h"
|
||||
|
|
@ -45,6 +44,13 @@ TransformStream::TransformStream(nsIGlobalObject* aGlobal) : mGlobal(aGlobal) {
|
|||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
TransformStream::TransformStream(nsIGlobalObject* aGlobal,
|
||||
ReadableStream* aReadable,
|
||||
WritableStream* aWritable)
|
||||
: mGlobal(aGlobal), mReadable(aReadable), mWritable(aWritable) {
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
TransformStream::~TransformStream() { mozilla::DropJSObjects(this); }
|
||||
|
||||
JSObject* TransformStream::WrapObject(JSContext* aCx,
|
||||
|
|
|
|||
|
|
@ -19,12 +19,17 @@ namespace mozilla::dom {
|
|||
|
||||
class WritableStream;
|
||||
class ReadableStream;
|
||||
class UniqueMessagePortId;
|
||||
class MessagePort;
|
||||
|
||||
class TransformStream final : public nsISupports, public nsWrapperCache {
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TransformStream)
|
||||
|
||||
TransformStream(nsIGlobalObject* aGlobal, ReadableStream* aReadable,
|
||||
WritableStream* aWritable);
|
||||
|
||||
// Internal slot accessors
|
||||
bool Backpressure() const { return mBackpressure; }
|
||||
void SetBackpressure(bool aBackpressure) { mBackpressure = aBackpressure; }
|
||||
|
|
@ -42,6 +47,16 @@ class TransformStream final : public nsISupports, public nsWrapperCache {
|
|||
MOZ_KNOWN_LIVE ReadableStream* Readable() { return mReadable; }
|
||||
MOZ_KNOWN_LIVE WritableStream* Writable() { return mWritable; }
|
||||
|
||||
// [Transferable]
|
||||
// https://html.spec.whatwg.org/multipage/structured-data.html#transfer-steps
|
||||
MOZ_CAN_RUN_SCRIPT bool Transfer(JSContext* aCx,
|
||||
UniqueMessagePortId& aPortId1,
|
||||
UniqueMessagePortId& aPortId2);
|
||||
// https://html.spec.whatwg.org/multipage/structured-data.html#transfer-receiving-steps
|
||||
static MOZ_CAN_RUN_SCRIPT bool ReceiveTransfer(
|
||||
JSContext* aCx, nsIGlobalObject* aGlobal, MessagePort& aPort1,
|
||||
MessagePort& aPort2, JS::MutableHandle<JSObject*> aReturnObject);
|
||||
|
||||
protected:
|
||||
~TransformStream();
|
||||
explicit TransformStream(nsIGlobalObject* aGlobal);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ class Promise;
|
|||
class WritableStreamDefaultController;
|
||||
class WritableStreamDefaultWriter;
|
||||
class UnderlyingSinkAlgorithmsBase;
|
||||
class UniqueMessagePortId;
|
||||
class MessagePort;
|
||||
|
||||
class WritableStream : public nsISupports, public nsWrapperCache {
|
||||
public:
|
||||
|
|
@ -41,7 +43,6 @@ class WritableStream : public nsISupports, public nsWrapperCache {
|
|||
enum class WriterState { Writable, Closed, Erroring, Errored };
|
||||
|
||||
// Slot Getter/Setters:
|
||||
public:
|
||||
bool Backpressure() const { return mBackpressure; }
|
||||
void SetBackpressure(bool aBackpressure) { mBackpressure = aBackpressure; }
|
||||
|
||||
|
|
@ -133,7 +134,15 @@ class WritableStream : public nsISupports, public nsWrapperCache {
|
|||
// WritableStreamUpdateBackpressure
|
||||
void UpdateBackpressure(bool aBackpressure, ErrorResult& aRv);
|
||||
|
||||
public:
|
||||
// [Transferable]
|
||||
// https://html.spec.whatwg.org/multipage/structured-data.html#transfer-steps
|
||||
MOZ_CAN_RUN_SCRIPT bool Transfer(JSContext* aCx,
|
||||
UniqueMessagePortId& aPortId);
|
||||
// https://html.spec.whatwg.org/multipage/structured-data.html#transfer-receiving-steps
|
||||
static MOZ_CAN_RUN_SCRIPT bool ReceiveTransfer(
|
||||
JSContext* aCx, nsIGlobalObject* aGlobal, MessagePort& aPort,
|
||||
JS::MutableHandle<JSObject*> aReturnObject);
|
||||
|
||||
nsIGlobalObject* GetParentObject() const { return mGlobal; }
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
|
|
|
|||
|
|
@ -12,11 +12,9 @@
|
|||
#include "mozilla/dom/AbortSignal.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/Promise-inl.h"
|
||||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
#include "mozilla/dom/WritableStream.h"
|
||||
#include "mozilla/dom/WritableStreamDefaultController.h"
|
||||
#include "mozilla/dom/WritableStreamDefaultControllerBinding.h"
|
||||
// #include "mozilla/dom/ReadableStreamDefaultReaderBinding.h"
|
||||
#include "mozilla/dom/UnderlyingSinkBinding.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsDebug.h"
|
||||
|
|
@ -185,7 +183,8 @@ void SetUpWritableStreamDefaultController(
|
|||
}
|
||||
|
||||
// Step 16. Let startPromise be a promise resolved with startResult.
|
||||
RefPtr<Promise> startPromise = Promise::Create(GetIncumbentGlobal(), aRv);
|
||||
RefPtr<Promise> startPromise =
|
||||
Promise::Create(aStream->GetParentObject(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ UNIFIED_SOURCES += [
|
|||
"ReadableStreamTee.cpp",
|
||||
"StreamUtils.cpp",
|
||||
"TeeState.cpp",
|
||||
"Transferable.cpp",
|
||||
"TransformerCallbackHelpers.cpp",
|
||||
"TransformStream.cpp",
|
||||
"TransformStreamDefaultController.cpp",
|
||||
|
|
@ -60,10 +61,13 @@ UNIFIED_SOURCES += [
|
|||
"WritableStreamDefaultWriter.cpp",
|
||||
]
|
||||
|
||||
include("/ipc/chromium/chromium-config.mozbuild") # to import MessagePort.h
|
||||
|
||||
FINAL_LIBRARY = "xul"
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
"/dom/base",
|
||||
"/dom/ipc",
|
||||
]
|
||||
|
||||
# MOCHITEST_MANIFESTS += ["tests/mochitest.ini"]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
[Exposed=*,
|
||||
//Transferable See Bug 1734240
|
||||
//Transferable See Bug 1562065
|
||||
]
|
||||
interface ReadableStream {
|
||||
[Throws]
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ namespace net {
|
|||
|
||||
class ChannelEvent;
|
||||
class ChannelEventQueue;
|
||||
class MessageEvent;
|
||||
|
||||
class WebSocketChannelChild final : public BaseWebSocketChannel,
|
||||
public PWebSocketChild,
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
[messagechannel.any.sharedworker.html]
|
||||
[A subclass instance will be received as its closest transferable superclass]
|
||||
expected: PRECONDITION_FAILED
|
||||
|
||||
|
||||
[messagechannel.any.html]
|
||||
[A subclass instance will be received as its closest transferable superclass]
|
||||
expected: PRECONDITION_FAILED
|
||||
|
||||
|
||||
[messagechannel.any.worker.html]
|
||||
[A subclass instance will be received as its closest transferable superclass]
|
||||
expected: PRECONDITION_FAILED
|
||||
|
||||
|
||||
[messagechannel.any.serviceworker.html]
|
||||
[A subclass instance will be received as its closest transferable superclass]
|
||||
expected: PRECONDITION_FAILED
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
[window-postmessage.window.html]
|
||||
[A subclass instance will be received as its closest transferable superclass]
|
||||
expected: PRECONDITION_FAILED
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
[structured-clone.any.html]
|
||||
[A subclass instance will be received as its closest transferable superclass]
|
||||
expected: PRECONDITION_FAILED
|
||||
|
||||
|
||||
[structured-clone.any.worker.html]
|
||||
[A subclass instance will be received as its closest transferable superclass]
|
||||
expected: PRECONDITION_FAILED
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
[deserialize-error.window.html]
|
||||
expected: ERROR
|
||||
[a ReadableStream deserialization failure should result in a DataCloneError]
|
||||
expected: TIMEOUT
|
||||
|
||||
[a WritableStream deserialization failure should result in a DataCloneError]
|
||||
expected: TIMEOUT
|
||||
|
||||
|
|
@ -1,49 +1,3 @@
|
|||
[readable-stream.html]
|
||||
[sending ten chunks on demand should work]
|
||||
expected: FAIL
|
||||
|
||||
[race between cancel() and enqueue() should be benign]
|
||||
expected: FAIL
|
||||
|
||||
[transferring a non-serializable chunk should error both sides]
|
||||
expected: FAIL
|
||||
|
||||
[stream cancel should not wait for underlying source cancel]
|
||||
expected: FAIL
|
||||
|
||||
[cancel should abort a pending read()]
|
||||
expected: FAIL
|
||||
|
||||
[race between cancel() and error() should leave sides in different states]
|
||||
expected: FAIL
|
||||
|
||||
[cancel should be propagated to the original]
|
||||
expected: FAIL
|
||||
|
||||
[race between cancel() and close() should be benign]
|
||||
expected: FAIL
|
||||
|
||||
[errors should be passed through]
|
||||
expected: FAIL
|
||||
|
||||
[the extra queue from transferring is counted in chunks]
|
||||
expected: FAIL
|
||||
|
||||
[sending one chunk through a transferred stream should work]
|
||||
expected: FAIL
|
||||
|
||||
[transferring a stream should relieve backpressure]
|
||||
expected: FAIL
|
||||
|
||||
[sending ten chunks through a transferred stream should work]
|
||||
expected: FAIL
|
||||
|
||||
[serialization should not happen until the value is read]
|
||||
expected: FAIL
|
||||
|
||||
[sending ten chunks one at a time should work]
|
||||
expected: FAIL
|
||||
|
||||
[transferring a stream should add one chunk to the queue size]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
|||
|
|
@ -2,45 +2,18 @@
|
|||
[a TypeError message should not be preserved if it is inherited]
|
||||
expected: FAIL
|
||||
|
||||
[DOMException errors should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[reason with a simple value of '3' should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[URIError should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[reason with a simple value of 'null' should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[reason with a simple value of '7' should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[a TypeError message should be converted to a string]
|
||||
expected: FAIL
|
||||
|
||||
[objects that can be completely expressed in JSON should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[reason with a simple value of 'true' should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[TypeError should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[reason with a simple value of 'undefined' should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[RangeError should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[reason with a simple value of 'NaN' should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[objects that cannot be expressed in JSON should also be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[other attributes of a TypeError should not be preserved]
|
||||
expected: FAIL
|
||||
|
||||
|
|
@ -50,24 +23,11 @@
|
|||
[SyntaxError should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[reason with a simple value of 'Infinity' should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[EvalError should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[reason with a simple value of '\t\r\n' should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[the type and message of a TypeError should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[reason with a simple value of 'hi' should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[reason with a simple value of 'false' should be preserved]
|
||||
expected: FAIL
|
||||
|
||||
[a TypeError message should not be preserved if it is a getter]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
[service-worker.https.html]
|
||||
expected:
|
||||
if (os == "android") and debug and not swgl: [OK, ERROR]
|
||||
[serviceWorker.controller.postMessage should be able to transfer a ReadableStream]
|
||||
expected: FAIL
|
||||
|
||||
[postMessage in a service worker should be able to transfer ReadableStream]
|
||||
expected: FAIL
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
[shared-worker.html]
|
||||
[postMessage in a worker should be able to transfer a ReadableStream]
|
||||
expected: FAIL
|
||||
|
||||
[worker.postMessage should be able to transfer a ReadableStream]
|
||||
expected: FAIL
|
||||
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
[transform-stream.html]
|
||||
[piping through transferred transforms should work]
|
||||
expected: FAIL
|
||||
|
||||
[window.postMessage should be able to transfer a TransformStream]
|
||||
expected: FAIL
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
[window.html]
|
||||
[transfer to and from an iframe should work]
|
||||
expected: FAIL
|
||||
|
||||
[the same ReadableStream posted multiple times should arrive together]
|
||||
expected: FAIL
|
||||
|
||||
[port.postMessage should be able to transfer a ReadableStream]
|
||||
expected: FAIL
|
||||
|
||||
[window.postMessage should be able to transfer a ReadableStream]
|
||||
expected: FAIL
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
[worker.html]
|
||||
expected:
|
||||
if (os == "win") and not debug and (processor == "x86_64"): [OK, ERROR]
|
||||
[terminating a worker should not error the stream]
|
||||
expected: FAIL
|
||||
|
||||
[postMessage in a worker should be able to transfer a ReadableStream]
|
||||
expected: FAIL
|
||||
|
||||
[worker.postMessage should be able to transfer a ReadableStream]
|
||||
expected: FAIL
|
||||
|
|
@ -1,22 +1,6 @@
|
|||
[writable-stream.html]
|
||||
[window.postMessage should be able to transfer a WritableStream]
|
||||
expected: FAIL
|
||||
|
||||
[second write should wait for first underlying write to complete]
|
||||
expected: FAIL
|
||||
|
||||
[abort() should work]
|
||||
expected: FAIL
|
||||
|
||||
[window.postMessage should be able to transfer a {readable, writable} pair]
|
||||
expected: FAIL
|
||||
|
||||
[writing a unclonable object should error the stream]
|
||||
expected: FAIL
|
||||
|
||||
[effective queue size of a transferred writable should be 2]
|
||||
expected: FAIL
|
||||
|
||||
[desiredSize for a newly-transferred stream should be 1]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
[dedicated.html]
|
||||
[A subclass instance will be received as its closest transferable superclass]
|
||||
expected: PRECONDITION_FAILED
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
[shared.html]
|
||||
disabled:
|
||||
if os == "win": Bug 1661351
|
||||
[A subclass instance will be received as its closest transferable superclass]
|
||||
expected: PRECONDITION_FAILED
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
"use strict";
|
||||
|
||||
function receiveEventOnce(target, name) {
|
||||
return new Promise(resolve => {
|
||||
target.addEventListener(
|
||||
name,
|
||||
ev => {
|
||||
resolve(ev);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async function postAndTestMessageEvent(data, transfer, title) {
|
||||
postMessage(data, "*", transfer);
|
||||
const messagePortCount = transfer.filter(i => i instanceof MessagePort)
|
||||
.length;
|
||||
const ev = await receiveEventOnce(window, "message");
|
||||
assert_equals(
|
||||
ev.ports.length,
|
||||
messagePortCount,
|
||||
`Correct number of ports ${title}`
|
||||
);
|
||||
for (const [i, port] of ev.ports.entries()) {
|
||||
assert_true(
|
||||
port instanceof MessagePort,
|
||||
`ports[${i}] include MessagePort ${title}`
|
||||
);
|
||||
}
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
assert_true(
|
||||
ev.data[key] instanceof value.constructor,
|
||||
`data.${key} has correct interface ${value.constructor.name} ${title}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function transferMessagePortWithOrder1(stream) {
|
||||
const channel = new MessageChannel();
|
||||
await postAndTestMessageEvent(
|
||||
{ stream, port2: channel.port2 },
|
||||
[stream, channel.port2],
|
||||
`when transferring [${stream.constructor.name}, MessagePort]`
|
||||
);
|
||||
}
|
||||
|
||||
async function transferMessagePortWithOrder2(stream) {
|
||||
const channel = new MessageChannel();
|
||||
await postAndTestMessageEvent(
|
||||
{ stream, port2: channel.port2 },
|
||||
[channel.port2, stream],
|
||||
`when transferring [MessagePort, ${stream.constructor.name}]`
|
||||
);
|
||||
}
|
||||
|
||||
async function transferMessagePortWithOrder3(stream) {
|
||||
const channel = new MessageChannel();
|
||||
await postAndTestMessageEvent(
|
||||
{ port1: channel.port1, stream, port2: channel.port2 },
|
||||
[channel.port1, stream, channel.port2],
|
||||
`when transferring [MessagePort, ${stream.constructor.name}, MessagePort]`
|
||||
);
|
||||
}
|
||||
|
||||
async function transferMessagePortWithOrder4(stream) {
|
||||
const channel = new MessageChannel();
|
||||
await postAndTestMessageEvent(
|
||||
{},
|
||||
[channel.port1, stream, channel.port2],
|
||||
`when transferring [MessagePort, ${stream.constructor.name}, MessagePort] but with empty data`
|
||||
);
|
||||
}
|
||||
|
||||
async function transferMessagePortWithOrder5(stream) {
|
||||
const channel = new MessageChannel();
|
||||
await postAndTestMessageEvent(
|
||||
{ port2: channel.port2, port1: channel.port1, stream },
|
||||
[channel.port1, stream, channel.port2],
|
||||
`when transferring [MessagePort, ${stream.constructor.name}, MessagePort] but with data having different order`
|
||||
);
|
||||
}
|
||||
|
||||
async function transferMessagePortWithOrder6(stream) {
|
||||
const channel = new MessageChannel();
|
||||
await postAndTestMessageEvent(
|
||||
{ port2: channel.port2, port1: channel.port1 },
|
||||
[channel.port1, stream, channel.port2],
|
||||
`when transferring [MessagePort, ${stream.constructor.name}, MessagePort] but with stream not being in the data`
|
||||
);
|
||||
}
|
||||
|
||||
async function transferMessagePortWithOrder7(stream) {
|
||||
const channel = new MessageChannel();
|
||||
await postAndTestMessageEvent(
|
||||
{ stream },
|
||||
[channel.port1, stream, channel.port2],
|
||||
`when transferring [MessagePort, ${stream.constructor.name}, MessagePort] but with ports not being in the data`
|
||||
);
|
||||
}
|
||||
|
||||
async function transferMessagePortWith(constructor) {
|
||||
await transferMessagePortWithOrder1(new constructor());
|
||||
await transferMessagePortWithOrder2(new constructor());
|
||||
await transferMessagePortWithOrder3(new constructor());
|
||||
}
|
||||
|
||||
async function advancedTransferMesagePortWith(constructor) {
|
||||
await transferMessagePortWithOrder4(new constructor());
|
||||
await transferMessagePortWithOrder5(new constructor());
|
||||
await transferMessagePortWithOrder6(new constructor());
|
||||
await transferMessagePortWithOrder7(new constructor());
|
||||
}
|
||||
|
||||
async function mixedTransferMessagePortWithOrder1() {
|
||||
const channel = new MessageChannel();
|
||||
const readable = new ReadableStream();
|
||||
const writable = new WritableStream();
|
||||
const transform = new TransformStream();
|
||||
await postAndTestMessageEvent(
|
||||
{
|
||||
readable,
|
||||
writable,
|
||||
transform,
|
||||
port1: channel.port1,
|
||||
port2: channel.port2,
|
||||
},
|
||||
[readable, writable, transform, channel.port1, channel.port2],
|
||||
`when transferring [ReadableStream, WritableStream, TransformStream, MessagePort, MessagePort]`
|
||||
);
|
||||
}
|
||||
|
||||
async function mixedTransferMessagePortWithOrder2() {
|
||||
const channel = new MessageChannel();
|
||||
const readable = new ReadableStream();
|
||||
const writable = new WritableStream();
|
||||
const transform = new TransformStream();
|
||||
await postAndTestMessageEvent(
|
||||
{ readable, writable, transform },
|
||||
[transform, channel.port1, readable, channel.port2, writable],
|
||||
`when transferring [TransformStream, MessagePort, ReadableStream, MessagePort, WritableStream]`
|
||||
);
|
||||
}
|
||||
|
||||
async function mixedTransferMessagePortWithOrder3() {
|
||||
const channel = new MessageChannel();
|
||||
const readable1 = new ReadableStream();
|
||||
const readable2 = new ReadableStream();
|
||||
const writable1 = new WritableStream();
|
||||
const writable2 = new WritableStream();
|
||||
const transform1 = new TransformStream();
|
||||
const transform2 = new TransformStream();
|
||||
await postAndTestMessageEvent(
|
||||
{ readable1, writable1, transform1, readable2, writable2, transform2 },
|
||||
[
|
||||
transform2,
|
||||
channel.port1,
|
||||
readable1,
|
||||
channel.port2,
|
||||
writable2,
|
||||
readable2,
|
||||
writable1,
|
||||
transform1,
|
||||
],
|
||||
`when transferring [TransformStream, MessagePort, ReadableStream, MessagePort, WritableStream, ReadableStream, WritableStream, TransformStream] but with the data having different order`
|
||||
);
|
||||
}
|
||||
|
||||
async function mixedTransferMesagePortWith() {
|
||||
await mixedTransferMessagePortWithOrder1();
|
||||
await mixedTransferMessagePortWithOrder2();
|
||||
await mixedTransferMessagePortWithOrder3();
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
await transferMessagePortWith(ReadableStream);
|
||||
}, "Transferring a MessagePort with a ReadableStream should set `.ports`");
|
||||
|
||||
promise_test(async t => {
|
||||
await transferMessagePortWith(WritableStream);
|
||||
}, "Transferring a MessagePort with a WritableStream should set `.ports`");
|
||||
|
||||
promise_test(async t => {
|
||||
await transferMessagePortWith(TransformStream);
|
||||
}, "Transferring a MessagePort with a TransformStream should set `.ports`");
|
||||
|
||||
promise_test(async t => {
|
||||
await transferMessagePortWith(ReadableStream);
|
||||
}, "Transferring a MessagePort with a ReadableStream should set `.ports`, advanced");
|
||||
|
||||
promise_test(async t => {
|
||||
await transferMessagePortWith(WritableStream);
|
||||
}, "Transferring a MessagePort with a WritableStream should set `.ports`, advanced");
|
||||
|
||||
promise_test(async t => {
|
||||
await transferMessagePortWith(TransformStream);
|
||||
}, "Transferring a MessagePort with a TransformStream should set `.ports`, advanced");
|
||||
|
||||
promise_test(async t => {
|
||||
await mixedTransferMesagePortWith();
|
||||
}, "Transferring a MessagePort with multiple streams should set `.ports`");
|
||||
|
||||
test(() => {
|
||||
assert_throws_dom("DataCloneError", () =>
|
||||
postMessage({ stream: new ReadableStream() }, "*")
|
||||
);
|
||||
}, "ReadableStream must not be serializable");
|
||||
|
||||
test(() => {
|
||||
assert_throws_dom("DataCloneError", () =>
|
||||
postMessage({ stream: new WritableStream() }, "*")
|
||||
);
|
||||
}, "WritableStream must not be serializable");
|
||||
|
||||
test(() => {
|
||||
assert_throws_dom("DataCloneError", () =>
|
||||
postMessage({ stream: new TransformStream() }, "*")
|
||||
);
|
||||
}, "TransformStream must not be serializable");
|
||||
Loading…
Reference in a new issue