Skip to content

Commit 2b7f799

Browse files
committed
Bug 1659025 - Implement [Transferable] for ReadableStream r=smaug,sfink
Differential Revision: https://phabricator.services.mozilla.com/D139525
1 parent ff14b28 commit 2b7f799

32 files changed

+1550
-214
lines changed

dom/base/StructuredCloneHolder.cpp

Lines changed: 196 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,18 @@
4646
#include "mozilla/dom/MessagePortBinding.h"
4747
#include "mozilla/dom/OffscreenCanvas.h"
4848
#include "mozilla/dom/OffscreenCanvasBinding.h"
49+
#include "mozilla/dom/ReadableStream.h"
50+
#include "mozilla/dom/ReadableStreamBinding.h"
4951
#include "mozilla/dom/ScriptSettings.h"
5052
#include "mozilla/dom/StructuredCloneBlob.h"
5153
#include "mozilla/dom/StructuredCloneHolderBinding.h"
5254
#include "mozilla/dom/StructuredCloneTags.h"
5355
#include "mozilla/dom/ToJSValue.h"
56+
#include "mozilla/dom/TransformStream.h"
57+
#include "mozilla/dom/TransformStreamBinding.h"
5458
#include "mozilla/dom/WebIDLSerializable.h"
59+
#include "mozilla/dom/WritableStream.h"
60+
#include "mozilla/dom/WritableStreamBinding.h"
5561
#include "mozilla/dom/WorkerCommon.h"
5662
#include "mozilla/dom/WorkerPrivate.h"
5763
#include "mozilla/fallible.h"
@@ -1115,7 +1121,26 @@ bool StructuredCloneHolder::CustomWriteHandler(
11151121
return WriteFullySerializableObjects(aCx, aWriter, aObj);
11161122
}
11171123

1118-
bool StructuredCloneHolder::CustomReadTransferHandler(
1124+
already_AddRefed<MessagePort> StructuredCloneHolder::ReceiveMessagePort(
1125+
uint64_t aIndex) {
1126+
if (NS_WARN_IF(aIndex >= mPortIdentifiers.Length())) {
1127+
return nullptr;
1128+
}
1129+
UniqueMessagePortId portId(mPortIdentifiers[aIndex]);
1130+
1131+
ErrorResult rv;
1132+
RefPtr<MessagePort> port = MessagePort::Create(mGlobal, portId, rv);
1133+
if (NS_WARN_IF(rv.Failed())) {
1134+
rv.SuppressException();
1135+
return nullptr;
1136+
}
1137+
1138+
return port.forget();
1139+
}
1140+
1141+
// TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
1142+
MOZ_CAN_RUN_SCRIPT_BOUNDARY bool
1143+
StructuredCloneHolder::CustomReadTransferHandler(
11191144
JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
11201145
void* aContent, uint64_t aExtraData,
11211146
JS::MutableHandleObject aReturnObject) {
@@ -1127,16 +1152,10 @@ bool StructuredCloneHolder::CustomReadTransferHandler(
11271152
return false;
11281153
}
11291154
#endif
1130-
MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
1131-
UniqueMessagePortId portIdentifier(mPortIdentifiers[aExtraData]);
1132-
1133-
ErrorResult rv;
1134-
RefPtr<MessagePort> port = MessagePort::Create(mGlobal, portIdentifier, rv);
1135-
if (NS_WARN_IF(rv.Failed())) {
1136-
rv.SuppressException();
1155+
RefPtr<MessagePort> port = ReceiveMessagePort(aExtraData);
1156+
if (!port) {
11371157
return false;
11381158
}
1139-
11401159
mTransferredPorts.AppendElement(port);
11411160

11421161
JS::Rooted<JS::Value> value(aCx);
@@ -1186,10 +1205,56 @@ bool StructuredCloneHolder::CustomReadTransferHandler(
11861205
return true;
11871206
}
11881207

1208+
if (aTag == SCTAG_DOM_READABLESTREAM) {
1209+
#ifdef FUZZING
1210+
if (aExtraData >= mPortIdentifiers.Length()) {
1211+
return false;
1212+
}
1213+
#endif
1214+
RefPtr<MessagePort> port = ReceiveMessagePort(aExtraData);
1215+
if (!port) {
1216+
return false;
1217+
}
1218+
nsCOMPtr<nsIGlobalObject> global = mGlobal;
1219+
return ReadableStream::ReceiveTransfer(aCx, global, *port, aReturnObject);
1220+
}
1221+
1222+
if (aTag == SCTAG_DOM_WRITABLESTREAM) {
1223+
#ifdef FUZZING
1224+
if (aExtraData >= mPortIdentifiers.Length()) {
1225+
return false;
1226+
}
1227+
#endif
1228+
RefPtr<MessagePort> port = ReceiveMessagePort(aExtraData);
1229+
if (!port) {
1230+
return false;
1231+
}
1232+
nsCOMPtr<nsIGlobalObject> global = mGlobal;
1233+
return WritableStream::ReceiveTransfer(aCx, global, *port, aReturnObject);
1234+
}
1235+
1236+
if (aTag == SCTAG_DOM_TRANSFORMSTREAM) {
1237+
#ifdef FUZZING
1238+
if (aExtraData + 1 >= mPortIdentifiers.Length()) {
1239+
return false;
1240+
}
1241+
#endif
1242+
RefPtr<MessagePort> port1 = ReceiveMessagePort(aExtraData);
1243+
RefPtr<MessagePort> port2 = ReceiveMessagePort(aExtraData + 1);
1244+
if (!port1 || !port2) {
1245+
return false;
1246+
}
1247+
nsCOMPtr<nsIGlobalObject> global = mGlobal;
1248+
return TransformStream::ReceiveTransfer(aCx, global, *port1, *port2,
1249+
aReturnObject);
1250+
}
1251+
11891252
return false;
11901253
}
11911254

1192-
bool StructuredCloneHolder::CustomWriteTransferHandler(
1255+
// TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
1256+
MOZ_CAN_RUN_SCRIPT_BOUNDARY bool
1257+
StructuredCloneHolder::CustomWriteTransferHandler(
11931258
JSContext* aCx, JS::Handle<JSObject*> aObj, uint32_t* aTag,
11941259
JS::TransferableOwnership* aOwnership, void** aContent,
11951260
uint64_t* aExtraData) {
@@ -1263,6 +1328,68 @@ bool StructuredCloneHolder::CustomWriteTransferHandler(
12631328
return true;
12641329
}
12651330
}
1331+
1332+
{
1333+
RefPtr<ReadableStream> stream;
1334+
rv = UNWRAP_OBJECT(ReadableStream, &obj, stream);
1335+
if (NS_SUCCEEDED(rv)) {
1336+
MOZ_ASSERT(stream);
1337+
1338+
*aTag = SCTAG_DOM_READABLESTREAM;
1339+
*aOwnership = JS::SCTAG_TMO_CUSTOM;
1340+
*aContent = nullptr;
1341+
1342+
UniqueMessagePortId id;
1343+
if (!stream->Transfer(aCx, id)) {
1344+
return false;
1345+
}
1346+
*aExtraData = mPortIdentifiers.Length();
1347+
mPortIdentifiers.AppendElement(id.release());
1348+
return true;
1349+
}
1350+
}
1351+
1352+
{
1353+
RefPtr<WritableStream> stream;
1354+
rv = UNWRAP_OBJECT(WritableStream, &obj, stream);
1355+
if (NS_SUCCEEDED(rv)) {
1356+
MOZ_ASSERT(stream);
1357+
1358+
*aTag = SCTAG_DOM_WRITABLESTREAM;
1359+
*aOwnership = JS::SCTAG_TMO_CUSTOM;
1360+
*aContent = nullptr;
1361+
1362+
UniqueMessagePortId id;
1363+
if (!stream->Transfer(aCx, id)) {
1364+
return false;
1365+
}
1366+
*aExtraData = mPortIdentifiers.Length();
1367+
mPortIdentifiers.AppendElement(id.release());
1368+
return true;
1369+
}
1370+
}
1371+
1372+
{
1373+
RefPtr<TransformStream> stream;
1374+
rv = UNWRAP_OBJECT(TransformStream, &obj, stream);
1375+
if (NS_SUCCEEDED(rv)) {
1376+
MOZ_ASSERT(stream);
1377+
1378+
*aTag = SCTAG_DOM_TRANSFORMSTREAM;
1379+
*aOwnership = JS::SCTAG_TMO_CUSTOM;
1380+
*aContent = nullptr;
1381+
1382+
UniqueMessagePortId id1;
1383+
UniqueMessagePortId id2;
1384+
if (!stream->Transfer(aCx, id1, id2)) {
1385+
return false;
1386+
}
1387+
*aExtraData = mPortIdentifiers.Length();
1388+
mPortIdentifiers.AppendElement(id1.release());
1389+
mPortIdentifiers.AppendElement(id2.release());
1390+
return true;
1391+
}
1392+
}
12661393
}
12671394

12681395
return false;
@@ -1301,6 +1428,31 @@ void StructuredCloneHolder::CustomFreeTransferHandler(
13011428
delete data;
13021429
return;
13031430
}
1431+
1432+
if (aTag == SCTAG_DOM_READABLESTREAM || aTag == SCTAG_DOM_WRITABLESTREAM) {
1433+
MOZ_ASSERT(!aContent);
1434+
#ifdef FUZZING
1435+
if (aExtraData >= mPortIdentifiers.Length()) {
1436+
return;
1437+
}
1438+
#endif
1439+
MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
1440+
MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
1441+
return;
1442+
}
1443+
1444+
if (aTag == SCTAG_DOM_TRANSFORMSTREAM) {
1445+
MOZ_ASSERT(!aContent);
1446+
#ifdef FUZZING
1447+
if (aExtraData + 1 >= mPortIdentifiers.Length()) {
1448+
return;
1449+
}
1450+
#endif
1451+
MOZ_ASSERT(aExtraData + 1 < mPortIdentifiers.Length());
1452+
MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
1453+
MessagePort::ForceClose(mPortIdentifiers[aExtraData + 1]);
1454+
return;
1455+
}
13041456
}
13051457

13061458
bool StructuredCloneHolder::CustomCanTransferHandler(
@@ -1342,6 +1494,40 @@ bool StructuredCloneHolder::CustomCanTransferHandler(
13421494
}
13431495
}
13441496

1497+
{
1498+
ReadableStream* stream = nullptr;
1499+
nsresult rv = UNWRAP_OBJECT(ReadableStream, &obj, stream);
1500+
if (NS_SUCCEEDED(rv)) {
1501+
// https://streams.spec.whatwg.org/#ref-for-transfer-steps
1502+
// Step 1: If ! IsReadableStreamLocked(value) is true, throw a
1503+
// "DataCloneError" DOMException.
1504+
return !IsReadableStreamLocked(stream);
1505+
}
1506+
}
1507+
1508+
{
1509+
WritableStream* stream = nullptr;
1510+
nsresult rv = UNWRAP_OBJECT(WritableStream, &obj, stream);
1511+
if (NS_SUCCEEDED(rv)) {
1512+
// https://streams.spec.whatwg.org/#ref-for-transfer-steps①
1513+
// Step 1: If ! IsWritableStreamLocked(value) is true, throw a
1514+
// "DataCloneError" DOMException.
1515+
return !IsWritableStreamLocked(stream);
1516+
}
1517+
}
1518+
1519+
{
1520+
TransformStream* stream = nullptr;
1521+
nsresult rv = UNWRAP_OBJECT(TransformStream, &obj, stream);
1522+
if (NS_SUCCEEDED(rv)) {
1523+
// https://streams.spec.whatwg.org/#ref-for-transfer-steps②
1524+
// Step 3 + 4: If ! Is{Readable,Writable}StreamLocked(value) is true,
1525+
// throw a "DataCloneError" DOMException.
1526+
return !IsReadableStreamLocked(stream->Readable()) &&
1527+
!IsWritableStreamLocked(stream->Writable());
1528+
}
1529+
}
1530+
13451531
return false;
13461532
}
13471533

dom/base/StructuredCloneHolder.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,8 @@ class StructuredCloneHolder : public StructuredCloneHolderBase {
335335

336336
void SameProcessScopeRequired(bool* aSameProcessScopeRequired);
337337

338+
already_AddRefed<MessagePort> ReceiveMessagePort(uint64_t aIndex);
339+
338340
bool mSupportsCloning;
339341
bool mSupportsTransferring;
340342

dom/base/StructuredCloneTags.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,12 @@ enum StructuredCloneTags : uint32_t {
140140

141141
SCTAG_DOM_CLONED_ERROR_OBJECT,
142142

143+
SCTAG_DOM_READABLESTREAM,
144+
145+
SCTAG_DOM_WRITABLESTREAM,
146+
147+
SCTAG_DOM_TRANSFORMSTREAM,
148+
143149
// IMPORTANT: If you plan to add an new IDB tag, it _must_ be add before the
144150
// "less stable" tags!
145151
};

dom/events/Event.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class EventMessageAutoOverride;
5151
class ExtendableEvent;
5252
class KeyboardEvent;
5353
class MouseEvent;
54+
class MessageEvent;
5455
class TimeEvent;
5556
class UIEvent;
5657
class WantsPopupControlCheck;
@@ -127,6 +128,9 @@ class Event : public nsISupports, public nsWrapperCache {
127128
// CustomEvent has a non-autogeneratable initCustomEvent.
128129
virtual CustomEvent* AsCustomEvent() { return nullptr; }
129130

131+
// MessageEvent has a non-autogeneratable initMessageEvent and more.
132+
virtual MessageEvent* AsMessageEvent() { return nullptr; }
133+
130134
void InitEvent(const nsAString& aEventTypeArg, bool aCanBubble,
131135
bool aCancelable) {
132136
InitEvent(aEventTypeArg, aCanBubble ? CanBubble::eYes : CanBubble::eNo,

dom/events/MessageEvent.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@ class MessageEvent final : public Event {
4343
NS_DECL_ISUPPORTS_INHERITED
4444
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(MessageEvent, Event)
4545

46-
virtual JSObject* WrapObjectInternal(
47-
JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
46+
JSObject* WrapObjectInternal(JSContext* aCx,
47+
JS::Handle<JSObject*> aGivenProto) override;
48+
49+
MessageEvent* AsMessageEvent() override { return this; }
4850

4951
void GetData(JSContext* aCx, JS::MutableHandle<JS::Value> aData,
5052
ErrorResult& aRv);

dom/streams/ReadableByteStreamController.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
#include "mozilla/dom/ByteStreamHelpers.h"
2020
#include "mozilla/dom/Promise.h"
2121
#include "mozilla/dom/Promise-inl.h"
22-
#include "mozilla/dom/PromiseNativeHandler.h"
2322
#include "mozilla/dom/ReadableByteStreamController.h"
2423
#include "mozilla/dom/ReadableByteStreamControllerBinding.h"
2524
#include "mozilla/dom/ReadIntoRequest.h"

dom/streams/ReadableStream.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ using OwningReadableStreamReader =
3737
OwningReadableStreamDefaultReaderOrReadableStreamBYOBReader;
3838
class NativeUnderlyingSource;
3939
class BodyStreamHolder;
40+
class UniqueMessagePortId;
41+
class MessagePort;
4042

4143
class ReadableStream final : public nsISupports, public nsWrapperCache {
4244
public:
@@ -95,6 +97,15 @@ class ReadableStream final : public nsISupports, public nsWrapperCache {
9597

9698
void ReleaseObjects();
9799

100+
// [Transferable]
101+
// https://html.spec.whatwg.org/multipage/structured-data.html#transfer-steps
102+
MOZ_CAN_RUN_SCRIPT bool Transfer(JSContext* aCx,
103+
UniqueMessagePortId& aPortId);
104+
// https://html.spec.whatwg.org/multipage/structured-data.html#transfer-receiving-steps
105+
static MOZ_CAN_RUN_SCRIPT bool ReceiveTransfer(
106+
JSContext* aCx, nsIGlobalObject* aGlobal, MessagePort& aPort,
107+
JS::MutableHandle<JSObject*> aReturnObject);
108+
98109
public:
99110
nsIGlobalObject* GetParentObject() const { return mGlobal; }
100111

dom/streams/ReadableStreamDefaultController.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
#include "mozilla/HoldDropJSObjects.h"
1313
#include "mozilla/dom/Promise.h"
1414
#include "mozilla/dom/Promise-inl.h"
15-
#include "mozilla/dom/PromiseNativeHandler.h"
1615
#include "mozilla/dom/ReadableStream.h"
1716
#include "mozilla/dom/ReadableStreamController.h"
1817
#include "mozilla/dom/ReadableStreamDefaultController.h"
@@ -503,7 +502,8 @@ void SetUpReadableStreamDefaultController(
503502
}
504503

505504
// Step 10.
506-
RefPtr<Promise> startPromise = Promise::Create(GetIncumbentGlobal(), aRv);
505+
RefPtr<Promise> startPromise =
506+
Promise::Create(aStream->GetParentObject(), aRv);
507507
if (aRv.Failed()) {
508508
return;
509509
}

dom/streams/ReadableStreamTee.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
#include "js/TypeDecls.h"
99
#include "js/experimental/TypedData.h"
1010
#include "mozilla/dom/ByteStreamHelpers.h"
11-
#include "mozilla/dom/PromiseNativeHandler.h"
1211
#include "mozilla/dom/Promise-inl.h"
1312
#include "mozilla/dom/ReadIntoRequest.h"
1413
#include "mozilla/dom/ReadableStream.h"

0 commit comments

Comments
 (0)