diff --git a/test/fixtures/wpt/README.md b/test/fixtures/wpt/README.md
index 406e89b820e9f7..8336539a7d70d4 100644
--- a/test/fixtures/wpt/README.md
+++ b/test/fixtures/wpt/README.md
@@ -27,7 +27,7 @@ Last update:
- performance-timeline: https://github.com/web-platform-tests/wpt/tree/94caab7038/performance-timeline
- resource-timing: https://github.com/web-platform-tests/wpt/tree/22d38586d0/resource-timing
- resources: https://github.com/web-platform-tests/wpt/tree/1e140d63ec/resources
-- streams: https://github.com/web-platform-tests/wpt/tree/2bd26e124c/streams
+- streams: https://github.com/web-platform-tests/wpt/tree/bc9dcbbf1a/streams
- url: https://github.com/web-platform-tests/wpt/tree/67880a4eb8/url
- user-timing: https://github.com/web-platform-tests/wpt/tree/5ae85bf826/user-timing
- wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/cde25e7e3c/wasm/jsapi
diff --git a/test/fixtures/wpt/streams/idlharness-shadowrealm.window.js b/test/fixtures/wpt/streams/idlharness-shadowrealm.window.js
deleted file mode 100644
index 099b2475ca7e87..00000000000000
--- a/test/fixtures/wpt/streams/idlharness-shadowrealm.window.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// META: script=/resources/idlharness-shadowrealm.js
-idl_test_shadowrealm(["streams"], ["dom"]);
diff --git a/test/fixtures/wpt/streams/idlharness.any.js b/test/fixtures/wpt/streams/idlharness.any.js
index 42a17da58c5ae3..0be03b2078f9bc 100644
--- a/test/fixtures/wpt/streams/idlharness.any.js
+++ b/test/fixtures/wpt/streams/idlharness.any.js
@@ -1,4 +1,4 @@
-// META: global=window,worker
+// META: global=window,worker,shadowrealm-in-window
// META: script=/resources/WebIDLParser.js
// META: script=/resources/idlharness.js
// META: timeout=long
diff --git a/test/fixtures/wpt/streams/readable-byte-streams/general.any.js b/test/fixtures/wpt/streams/readable-byte-streams/general.any.js
index cdce2244c3c84b..4b0c73865f7cf9 100644
--- a/test/fixtures/wpt/streams/readable-byte-streams/general.any.js
+++ b/test/fixtures/wpt/streams/readable-byte-streams/general.any.js
@@ -870,11 +870,11 @@ promise_test(() => {
start(c) {
controller = c;
},
- async pull() {
+ pull() {
byobRequestDefined.push(controller.byobRequest !== null);
const initialByobRequest = controller.byobRequest;
- const transferredView = await transferArrayBufferView(controller.byobRequest.view);
+ const transferredView = transferArrayBufferView(controller.byobRequest.view);
transferredView[0] = 0x01;
controller.byobRequest.respondWithNewView(transferredView);
@@ -2288,7 +2288,7 @@ promise_test(async t => {
await pullCalledPromise;
// Transfer the original BYOB request's buffer, and respond with a new view on that buffer
- const transferredView = await transferArrayBufferView(controller.byobRequest.view);
+ const transferredView = transferArrayBufferView(controller.byobRequest.view);
const newView = transferredView.subarray(0, 1);
newView[0] = 42;
@@ -2328,7 +2328,7 @@ promise_test(async t => {
await pullCalledPromise;
// Transfer the original BYOB request's buffer, and respond with an empty view on that buffer
- const transferredView = await transferArrayBufferView(controller.byobRequest.view);
+ const transferredView = transferArrayBufferView(controller.byobRequest.view);
const newView = transferredView.subarray(0, 0);
controller.close();
diff --git a/test/fixtures/wpt/streams/readable-byte-streams/patched-global.any.js b/test/fixtures/wpt/streams/readable-byte-streams/patched-global.any.js
new file mode 100644
index 00000000000000..ce2e9e9993ae57
--- /dev/null
+++ b/test/fixtures/wpt/streams/readable-byte-streams/patched-global.any.js
@@ -0,0 +1,54 @@
+// META: global=window,worker,shadowrealm
+// META: script=../resources/test-utils.js
+'use strict';
+
+// Tests which patch the global environment are kept separate to avoid
+// interfering with other tests.
+
+promise_test(async (t) => {
+ let controller;
+ const rs = new ReadableStream({
+ type: 'bytes',
+ start(c) {
+ controller = c;
+ }
+ });
+ const reader = rs.getReader({mode: 'byob'});
+
+ const length = 0x4000;
+ const buffer = new ArrayBuffer(length);
+ const bigArray = new BigUint64Array(buffer, length - 8, 1);
+
+ const read1 = reader.read(new Uint8Array(new ArrayBuffer(0x100)));
+ const read2 = reader.read(bigArray);
+
+ let flag = false;
+ Object.defineProperty(Object.prototype, 'then', {
+ get: t.step_func(() => {
+ if (!flag) {
+ flag = true;
+ assert_equals(controller.byobRequest, null, 'byobRequest should be null after filling both views');
+ }
+ }),
+ configurable: true
+ });
+ t.add_cleanup(() => {
+ delete Object.prototype.then;
+ });
+
+ controller.enqueue(new Uint8Array(0x110).fill(0x42));
+ assert_true(flag, 'patched then() should be called');
+
+ // The first read() is filled entirely with 0x100 bytes
+ const result1 = await read1;
+ assert_false(result1.done, 'result1.done');
+ assert_typed_array_equals(result1.value, new Uint8Array(0x100).fill(0x42), 'result1.value');
+
+ // The second read() is filled with the remaining 0x10 bytes
+ const result2 = await read2;
+ assert_false(result2.done, 'result2.done');
+ assert_equals(result2.value.constructor, BigUint64Array, 'result2.value constructor');
+ assert_equals(result2.value.byteOffset, length - 8, 'result2.value byteOffset');
+ assert_equals(result2.value.length, 1, 'result2.value length');
+ assert_array_equals([...result2.value], [0x42424242_42424242n], 'result2.value contents');
+}, 'Patched then() sees byobRequest after filling all pending pull-into descriptors');
diff --git a/test/fixtures/wpt/streams/readable-byte-streams/tee.any.js b/test/fixtures/wpt/streams/readable-byte-streams/tee.any.js
index 7dd5ba3f3fb013..60d82b9cf6a1fd 100644
--- a/test/fixtures/wpt/streams/readable-byte-streams/tee.any.js
+++ b/test/fixtures/wpt/streams/readable-byte-streams/tee.any.js
@@ -934,3 +934,36 @@ promise_test(async () => {
assert_typed_array_equals(result4.value, new Uint8Array([0]).subarray(0, 0), 'second chunk from branch2 should be correct');
}, 'ReadableStream teeing with byte source: respond() and close() while both branches are pulling');
+
+promise_test(async t => {
+ let pullCount = 0;
+ const arrayBuffer = new Uint8Array([0x01, 0x02, 0x03]).buffer;
+ const enqueuedChunk = new Uint8Array(arrayBuffer, 2);
+ assert_equals(enqueuedChunk.length, 1);
+ assert_equals(enqueuedChunk.byteOffset, 2);
+ const rs = new ReadableStream({
+ type: 'bytes',
+ pull(c) {
+ ++pullCount;
+ if (pullCount === 1) {
+ c.enqueue(enqueuedChunk);
+ }
+ }
+ });
+
+ const [branch1, branch2] = rs.tee();
+ const reader1 = branch1.getReader();
+ const reader2 = branch2.getReader();
+
+ const [result1, result2] = await Promise.all([reader1.read(), reader2.read()]);
+ assert_equals(result1.done, false, 'reader1 done');
+ assert_equals(result2.done, false, 'reader2 done');
+
+ const view1 = result1.value;
+ const view2 = result2.value;
+ // The first stream has the transferred buffer, but the second stream has the
+ // cloned buffer.
+ const underlying = new Uint8Array([0x01, 0x02, 0x03]).buffer;
+ assert_typed_array_equals(view1, new Uint8Array(underlying, 2), 'reader1 value');
+ assert_typed_array_equals(view2, new Uint8Array([0x03]), 'reader2 value');
+}, 'ReadableStream teeing with byte source: reading an array with a byte offset should clone correctly');
diff --git a/test/fixtures/wpt/streams/readable-streams/crashtests/from-cross-realm.https.html b/test/fixtures/wpt/streams/readable-streams/crashtests/from-cross-realm.https.html
new file mode 100644
index 00000000000000..58a4371186ece7
--- /dev/null
+++ b/test/fixtures/wpt/streams/readable-streams/crashtests/from-cross-realm.https.html
@@ -0,0 +1,18 @@
+
+
+
diff --git a/test/fixtures/wpt/streams/readable-streams/owning-type-video-frame.any.js b/test/fixtures/wpt/streams/readable-streams/owning-type-video-frame.any.js
index b652f9c5fcb4b6..ec01fda0b3c737 100644
--- a/test/fixtures/wpt/streams/readable-streams/owning-type-video-frame.any.js
+++ b/test/fixtures/wpt/streams/readable-streams/owning-type-video-frame.any.js
@@ -1,4 +1,4 @@
-// META: global=window,worker,shadowrealm
+// META: global=window,worker
// META: script=../resources/test-utils.js
// META: script=../resources/rs-utils.js
'use strict';
diff --git a/test/fixtures/wpt/streams/resources/rs-utils.js b/test/fixtures/wpt/streams/resources/rs-utils.js
index f1a014275a2fbc..0f7742a5b3b190 100644
--- a/test/fixtures/wpt/streams/resources/rs-utils.js
+++ b/test/fixtures/wpt/streams/resources/rs-utils.js
@@ -1,5 +1,42 @@
'use strict';
(function () {
+ // Fake setInterval-like functionality in environments that don't have it
+ class IntervalHandle {
+ constructor(callback, delayMs) {
+ this.callback = callback;
+ this.delayMs = delayMs;
+ this.cancelled = false;
+ Promise.resolve().then(() => this.check());
+ }
+
+ async check() {
+ while (true) {
+ await new Promise(resolve => step_timeout(resolve, this.delayMs));
+ if (this.cancelled) {
+ return;
+ }
+ this.callback();
+ }
+ }
+
+ cancel() {
+ this.cancelled = true;
+ }
+ }
+
+ let localSetInterval, localClearInterval;
+ if (typeof globalThis.setInterval !== "undefined" &&
+ typeof globalThis.clearInterval !== "undefined") {
+ localSetInterval = globalThis.setInterval;
+ localClearInterval = globalThis.clearInterval;
+ } else {
+ localSetInterval = function setInterval(callback, delayMs) {
+ return new IntervalHandle(callback, delayMs);
+ }
+ localClearInterval = function clearInterval(handle) {
+ handle.cancel();
+ }
+ }
class RandomPushSource {
constructor(toPush) {
@@ -18,12 +55,12 @@
}
if (!this.started) {
- this._intervalHandle = setInterval(writeChunk, 2);
+ this._intervalHandle = localSetInterval(writeChunk, 2);
this.started = true;
}
if (this.paused) {
- this._intervalHandle = setInterval(writeChunk, 2);
+ this._intervalHandle = localSetInterval(writeChunk, 2);
this.paused = false;
}
@@ -37,7 +74,7 @@
if (source.toPush > 0 && source.pushed > source.toPush) {
if (source._intervalHandle) {
- clearInterval(source._intervalHandle);
+ localClearInterval(source._intervalHandle);
source._intervalHandle = undefined;
}
source.closed = true;
@@ -55,7 +92,7 @@
if (this.started) {
this.paused = true;
- clearInterval(this._intervalHandle);
+ localClearInterval(this._intervalHandle);
this._intervalHandle = undefined;
} else {
throw new Error('Can\'t pause reading an unstarted source.');
@@ -178,15 +215,7 @@
}
function transferArrayBufferView(view) {
- const noopByteStream = new ReadableStream({
- type: 'bytes',
- pull(c) {
- c.byobRequest.respond(c.byobRequest.view.byteLength);
- c.close();
- }
- });
- const reader = noopByteStream.getReader({ mode: 'byob' });
- return reader.read(view).then((result) => result.value);
+ return structuredClone(view, { transfer: [view.buffer] });
}
self.RandomPushSource = RandomPushSource;
diff --git a/test/fixtures/wpt/streams/transferable/transfer-with-messageport.window.js b/test/fixtures/wpt/streams/transferable/transfer-with-messageport.window.js
index 37f8c9df169607..3bfe634a6e153d 100644
--- a/test/fixtures/wpt/streams/transferable/transfer-with-messageport.window.js
+++ b/test/fixtures/wpt/streams/transferable/transfer-with-messageport.window.js
@@ -105,7 +105,7 @@ async function transferMessagePortWith(constructor) {
await transferMessagePortWithOrder3(new constructor());
}
-async function advancedTransferMesagePortWith(constructor) {
+async function advancedTransferMessagePortWith(constructor) {
await transferMessagePortWithOrder4(new constructor());
await transferMessagePortWithOrder5(new constructor());
await transferMessagePortWithOrder6(new constructor());
@@ -166,7 +166,7 @@ async function mixedTransferMessagePortWithOrder3() {
);
}
-async function mixedTransferMesagePortWith() {
+async function mixedTransferMessagePortWith() {
await mixedTransferMessagePortWithOrder1();
await mixedTransferMessagePortWithOrder2();
await mixedTransferMessagePortWithOrder3();
@@ -185,19 +185,19 @@ promise_test(async t => {
}, "Transferring a MessagePort with a TransformStream should set `.ports`");
promise_test(async t => {
- await transferMessagePortWith(ReadableStream);
+ await advancedTransferMessagePortWith(ReadableStream);
}, "Transferring a MessagePort with a ReadableStream should set `.ports`, advanced");
promise_test(async t => {
- await transferMessagePortWith(WritableStream);
+ await advancedTransferMessagePortWith(WritableStream);
}, "Transferring a MessagePort with a WritableStream should set `.ports`, advanced");
promise_test(async t => {
- await transferMessagePortWith(TransformStream);
+ await advancedTransferMessagePortWith(TransformStream);
}, "Transferring a MessagePort with a TransformStream should set `.ports`, advanced");
promise_test(async t => {
- await mixedTransferMesagePortWith();
+ await mixedTransferMessagePortWith();
}, "Transferring a MessagePort with multiple streams should set `.ports`");
test(() => {
diff --git a/test/fixtures/wpt/versions.json b/test/fixtures/wpt/versions.json
index 55227b3be66e1b..a3e0dd60ed66ab 100644
--- a/test/fixtures/wpt/versions.json
+++ b/test/fixtures/wpt/versions.json
@@ -68,7 +68,7 @@
"path": "resources"
},
"streams": {
- "commit": "2bd26e124cf17b2f0a25c150794d640b07b2a870",
+ "commit": "bc9dcbbf1a4c2c741ef47f47d6ede6458f40c4a4",
"path": "streams"
},
"url": {