From 9b8a6e9232dca95a9ffddbe755b07c6028880626 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 21 Nov 2024 12:10:43 -0800 Subject: [PATCH 1/3] Convert some streams .html tests to .any.js format --- ...readable-stream.html => readable-stream.any.js} | 14 ++++---------- ...ansform-stream.html => transform-stream.any.js} | 10 ++-------- ...writable-stream.html => writable-stream.any.js} | 13 ++++--------- 3 files changed, 10 insertions(+), 27 deletions(-) rename streams/transferable/{readable-stream.html => readable-stream.any.js} (96%) rename streams/transferable/{transform-stream.html => transform-stream.any.js} (94%) rename streams/transferable/{writable-stream.html => writable-stream.any.js} (94%) diff --git a/streams/transferable/readable-stream.html b/streams/transferable/readable-stream.any.js similarity index 96% rename from streams/transferable/readable-stream.html rename to streams/transferable/readable-stream.any.js index b1ede4695bf4cd..491dfc0f210b92 100644 --- a/streams/transferable/readable-stream.html +++ b/streams/transferable/readable-stream.any.js @@ -1,11 +1,7 @@ - - - - - - - - diff --git a/streams/transferable/transform-stream.html b/streams/transferable/transform-stream.any.js similarity index 94% rename from streams/transferable/transform-stream.html rename to streams/transferable/transform-stream.any.js index 355d5d807433d7..15b02c25684468 100644 --- a/streams/transferable/transform-stream.html +++ b/streams/transferable/transform-stream.any.js @@ -1,9 +1,5 @@ - - - - - - diff --git a/streams/transferable/writable-stream.html b/streams/transferable/writable-stream.any.js similarity index 94% rename from streams/transferable/writable-stream.html rename to streams/transferable/writable-stream.any.js index 7e25dad94d4cfd..6cad7a0055e972 100644 --- a/streams/transferable/writable-stream.html +++ b/streams/transferable/writable-stream.any.js @@ -1,11 +1,7 @@ - - - - - - - - From bb04aa16c65fe35eea370eea8b94144341d3f65b Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 21 Nov 2024 12:18:17 -0800 Subject: [PATCH 2/3] Enable additional streams tests in ShadowRealm Requires adapting the tests (and a helper file) to use structuredClone instead of postMessage to transfer the streams when running in a ShadowRealm scope. --- streams/transferable/readable-stream.any.js | 15 ++- streams/transferable/resources/helpers.js | 12 ++- streams/transferable/transform-stream.any.js | 91 ++++++++++-------- streams/transferable/writable-stream.any.js | 99 ++++++++++++-------- 4 files changed, 131 insertions(+), 86 deletions(-) diff --git a/streams/transferable/readable-stream.any.js b/streams/transferable/readable-stream.any.js index 491dfc0f210b92..f885a48e4a5454 100644 --- a/streams/transferable/readable-stream.any.js +++ b/streams/transferable/readable-stream.any.js @@ -1,3 +1,4 @@ +// META: global=window,dedicatedworker,shadowrealm // META: script=resources/helpers.js // META: script=../resources/recording-streams.js // META: script=../resources/test-utils.js @@ -90,11 +91,15 @@ promise_test(async () => { const rs = recordingReadableStream({}, { highWaterMark: 0 }); await delay(0); assert_array_equals(rs.events, [], 'pull() should not have been called'); - // Eat the message so it can't interfere with other tests. - addEventListener('message', () => {}, {once: true}); - // The transfer is done manually to verify that it is posting the stream that - // relieves backpressure, not receiving it. - postMessage(rs, '*', [rs]); + if (GLOBAL.isShadowRealm()) { + structuredClone(rs, { transfer: [rs] }); + } else { + // Eat the message so it can't interfere with other tests. + addEventListener('message', () => {}, {once: true}); + // The transfer is done manually to verify that it is posting the stream that + // relieves backpressure, not receiving it. + postMessage(rs, '*', [rs]); + } await delay(0); assert_array_equals(rs.events, ['pull'], 'pull() should have been called'); }, 'transferring a stream should relieve backpressure'); diff --git a/streams/transferable/resources/helpers.js b/streams/transferable/resources/helpers.js index 12504537f91eab..30c6abb4b83815 100644 --- a/streams/transferable/resources/helpers.js +++ b/streams/transferable/resources/helpers.js @@ -98,7 +98,11 @@ } }, {once: true}); }); - postMessage(original, '*', [original]); + if (GLOBAL.isShadowRealm()) { + structuredClone(original, {transfer: [original]}); + } else { + postMessage(original, '*', [original]); + } return promise; } @@ -117,7 +121,11 @@ } }, {once: true}); }); - postMessage(original, '*', [original]); + if (GLOBAL.isShadowRealm()) { + structuredClone(original, {transfer: [original]}); + } else { + postMessage(original, '*', [original]); + } return promise; } diff --git a/streams/transferable/transform-stream.any.js b/streams/transferable/transform-stream.any.js index 15b02c25684468..86a68e93dfe21e 100644 --- a/streams/transferable/transform-stream.any.js +++ b/streams/transferable/transform-stream.any.js @@ -1,48 +1,66 @@ +// META: global=window,dedicatedworker,shadowrealm // META: script=../resources/test-utils.js 'use strict'; -promise_test(t => { - const orig = new TransformStream(); - const promise = new Promise(resolve => { +function transfer(t, obj, transfers) { + if (GLOBAL.isShadowRealm()) { + const transferred = structuredClone(obj, {transfer: transfers}); + return Promise.resolve(transferred); + } + return new Promise(resolve => { addEventListener('message', t.step_func(evt => { const transferred = evt.data; - assert_equals(transferred.constructor, TransformStream, - 'transferred should be a TransformStream in this realm'); - assert_true(transferred instanceof TransformStream, - 'instanceof check should pass'); - - // Perform a brand-check on |transferred|. - const readableGetter = Object.getOwnPropertyDescriptor( - TransformStream.prototype, 'readable').get; - assert_true(readableGetter.call(transferred) instanceof ReadableStream, - 'brand check should pass and readable stream should result'); - const writableGetter = Object.getOwnPropertyDescriptor( - TransformStream.prototype, 'writable').get; - assert_true(writableGetter.call(transferred) instanceof WritableStream, - 'brand check should pass and writable stream should result'); - resolve(); + resolve(transferred); }), {once: true}); + postMessage(obj, '*', transfers); }); - postMessage(orig, '*', [orig]); +} + +function failToTransfer(obj) { + if (GLOBAL.isShadowRealm()) { + structuredClone(obj, {transfer: [obj]}); + } else { + postMessage(obj, '*', [obj]); + } +} + +promise_test(t => { + const orig = new TransformStream(); + const promise = transfer(t, orig, [orig]); assert_true(orig.readable.locked, 'the readable side should be locked'); assert_true(orig.writable.locked, 'the writable side should be locked'); - return promise; -}, 'window.postMessage should be able to transfer a TransformStream'); + return promise.then(transferred => { + assert_equals(transferred.constructor, TransformStream, + 'transferred should be a TransformStream in this realm'); + assert_true(transferred instanceof TransformStream, + 'instanceof check should pass'); + + // Perform a brand-check on |transferred|. + const readableGetter = Object.getOwnPropertyDescriptor( + TransformStream.prototype, 'readable').get; + assert_true(readableGetter.call(transferred) instanceof ReadableStream, + 'brand check should pass and readable stream should result'); + const writableGetter = Object.getOwnPropertyDescriptor( + TransformStream.prototype, 'writable').get; + assert_true(writableGetter.call(transferred) instanceof WritableStream, + 'brand check should pass and writable stream should result'); + }); +}, `should be able to transfer a TransformStream`); test(() => { const ts = new TransformStream(); const writer = ts.writable.getWriter(); - assert_throws_dom('DataCloneError', () => postMessage(ts, '*', [ts]), - 'postMessage should throw'); + assert_throws_dom('DataCloneError', () => failToTransfer(ts), + 'transferring should throw'); assert_false(ts.readable.locked, 'readable side should not get locked'); }, 'a TransformStream with a locked writable should not be transferable'); test(() => { const ts = new TransformStream(); const reader = ts.readable.getReader(); - assert_throws_dom('DataCloneError', () => postMessage(ts, '*', [ts]), - 'postMessage should throw'); + assert_throws_dom('DataCloneError', () => failToTransfer(ts), + 'transferring should throw'); assert_false(ts.writable.locked, 'writable side should not get locked'); }, 'a TransformStream with a locked readable should not be transferable'); @@ -50,8 +68,8 @@ test(() => { const ts = new TransformStream(); const reader = ts.readable.getReader(); const writer = ts.writable.getWriter(); - assert_throws_dom('DataCloneError', () => postMessage(ts, '*', [ts]), - 'postMessage should throw'); + assert_throws_dom('DataCloneError', () => failToTransfer(ts), + 'transferring should throw'); }, 'a TransformStream with both sides locked should not be transferable'); promise_test(t => { @@ -83,18 +101,15 @@ promise_test(t => { controller.enqueue(chunk + chunk); } }); - const promise = new Promise(resolve => { - addEventListener('message', t.step_func(evt => { - const data = evt.data; + return transfer(t, {source, sink, transform1, transform2}, + [source, transform1, sink, transform2]) + .then(data => { resolve(data.source - .pipeThrough(data.transform1) - .pipeThrough(data.transform2) - .pipeTo(data.sink)); - })); - }); - postMessage({source, sink, transform1, transform2}, '*', - [source, transform1, sink, transform2]); - return ready + .pipeThrough(data.transform1) + .pipeThrough(data.transform2) + .pipeTo(data.sink)); + }) + .then(() => ready) .then(() => { assert_equals(result, 'HELLO HELLO THERE THERE ', 'transforms should have been applied'); diff --git a/streams/transferable/writable-stream.any.js b/streams/transferable/writable-stream.any.js index 6cad7a0055e972..4d57ecba973d08 100644 --- a/streams/transferable/writable-stream.any.js +++ b/streams/transferable/writable-stream.any.js @@ -1,61 +1,78 @@ +// META: global=window,dedicatedworker,shadowrealm // META: script=resources/helpers.js // META: script=../resources/test-utils.js // META: script=../resources/recording-streams.js 'use strict'; -promise_test(t => { - const orig = new WritableStream(); - const promise = new Promise(resolve => { +function transfer(t, obj, transfers) { + if (GLOBAL.isShadowRealm()) { + const transferred = structuredClone(obj, {transfer: transfers}); + return Promise.resolve(transferred); + } + return new Promise(resolve => { addEventListener('message', t.step_func(evt => { const transferred = evt.data; - assert_equals(transferred.constructor, WritableStream, - 'transferred should be a WritableStream in this realm'); - assert_true(transferred instanceof WritableStream, - 'instanceof check should pass'); - - // Perform a brand-check on |transferred|. - const writer = WritableStream.prototype.getWriter.call(transferred); - resolve(); + resolve(transferred); }), {once: true}); + postMessage(obj, '*', transfers); }); - postMessage(orig, '*', [orig]); +} + +function failToTransfer(obj) { + if (GLOBAL.isShadowRealm()) { + structuredClone(obj, {transfer: [obj]}); + } else { + postMessage(obj, '*', [obj]); + } +} + +promise_test(t => { + const orig = new WritableStream(); + const promise = transfer(t, orig, [orig]); assert_true(orig.locked, 'the original stream should be locked'); - return promise; -}, 'window.postMessage should be able to transfer a WritableStream'); + return promise.then(transferred => { + assert_equals(transferred.constructor, WritableStream, + 'transferred should be a WritableStream in this realm'); + assert_true(transferred instanceof WritableStream, + 'instanceof check should pass'); + + // Perform a brand-check on |transferred|. + const writer = WritableStream.prototype.getWriter.call(transferred); + }); +}, 'should be able to transfer a WritableStream'); test(() => { const ws = new WritableStream(); const writer = ws.getWriter(); - assert_throws_dom('DataCloneError', () => postMessage(ws, '*', [ws]), - 'postMessage should throw'); + assert_throws_dom('DataCloneError', () => failToTransfer(ws), + 'transferring should throw'); }, 'a locked WritableStream should not be transferable'); promise_test(t => { const {writable, readable} = new TransformStream(); - const promise = new Promise(resolve => { - addEventListener('message', t.step_func(async evt => { - const {writable, readable} = evt.data; - const reader = readable.getReader(); - const writer = writable.getWriter(); - const writerPromises = Promise.all([ - writer.write('hi'), - writer.close(), - ]); - const {value, done} = await reader.read(); - assert_false(done, 'we should not be done'); - assert_equals(value, 'hi', 'chunk should have been delivered'); - const readResult = await reader.read(); - assert_true(readResult.done, 'readable should be closed'); - await writerPromises; - resolve(); - }), {once: true}); + const promise = transfer(t, {writable, readable}, [writable, readable]); + return promise.then(async ({writable, readable}) => { + const reader = readable.getReader(); + const writer = writable.getWriter(); + const writerPromises = Promise.all([ + writer.write('hi'), + writer.close(), + ]); + const {value, done} = await reader.read(); + assert_false(done, 'we should not be done'); + assert_equals(value, 'hi', 'chunk should have been delivered'); + const readResult = await reader.read(); + assert_true(readResult.done, 'readable should be closed'); + await writerPromises; }); - postMessage({writable, readable}, '*', [writable, readable]); - return promise; -}, 'window.postMessage should be able to transfer a {readable, writable} pair'); +}, 'should be able to transfer a {readable, writable} pair'); -function transfer(stream) { +function transferSimple(stream) { + if (GLOBAL.isShadowRealm()) { + const transferred = structuredClone(stream, {transfer: [stream]}); + return Promise.resolve(transferred); + } return new Promise(resolve => { addEventListener('message', evt => resolve(evt.data), { once: true }); postMessage(stream, '*', [stream]); @@ -65,7 +82,7 @@ function transfer(stream) { promise_test(async () => { const orig = new WritableStream( {}, new ByteLengthQueuingStrategy({ highWaterMark: 65536 })); - const transferred = await transfer(orig); + const transferred = await transferSimple(orig); const writer = transferred.getWriter(); assert_equals(writer.desiredSize, 1, 'desiredSize should be 1'); }, 'desiredSize for a newly-transferred stream should be 1'); @@ -76,7 +93,7 @@ promise_test(async () => { return new Promise(() => {}); } }); - const transferred = await transfer(orig); + const transferred = await transferSimple(orig); const writer = transferred.getWriter(); await writer.write('a'); assert_equals(writer.desiredSize, 1, 'desiredSize should be 1'); @@ -93,7 +110,7 @@ promise_test(async () => { }); } }); - const transferred = await transfer(orig); + const transferred = await transferSimple(orig); const writer = transferred.getWriter(); await writer.write('a'); let writeDone = false; @@ -113,7 +130,7 @@ async function transferredWritableStreamWithAbortPromise() { resolveAbortCalled(); } }); - const transferred = await transfer(orig); + const transferred = await transferSimple(orig); return { orig, transferred, abortCalled }; } From b8426449ce5a4fd23895fff6ccf53d01ae7e314d Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 21 Nov 2024 12:49:45 -0800 Subject: [PATCH 3/3] Don't enable readable-streams/owning-type-message-port in ShadowRealm This test depends on MessageChannel, which is not [Exposed=*]. --- streams/readable-streams/owning-type-message-port.any.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/streams/readable-streams/owning-type-message-port.any.js b/streams/readable-streams/owning-type-message-port.any.js index 282c1f411485c0..e9961ce042256a 100644 --- a/streams/readable-streams/owning-type-message-port.any.js +++ b/streams/readable-streams/owning-type-message-port.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';