diff options
Diffstat (limited to '')
-rw-r--r-- | dom/tests/mochitest/whatwg/test_structuredCloneAndExposed.html | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/dom/tests/mochitest/whatwg/test_structuredCloneAndExposed.html b/dom/tests/mochitest/whatwg/test_structuredCloneAndExposed.html new file mode 100644 index 0000000000..2bd15247af --- /dev/null +++ b/dom/tests/mochitest/whatwg/test_structuredCloneAndExposed.html @@ -0,0 +1,307 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1828264 +--> +<head> + <title>Basic structured clone tests</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1828264">Mozilla Bug 1828264</a> +<p id="display"> +<iframe id="frame"></iframe> +<image href="four-colors.png" height="320" width="240"/> +</p> +<div id="content" style="display: none"></div> + + +<pre id="test"> +<script class="testbody" type="application/javascript"> + +SimpleTest.waitForExplicitFinish(); + +class WebIDLGlobal { + globalNames; + resolve; + #otherSide; + + constructor(globalNames, initSides) { + this.globalNames = globalNames; + let global = this; + this.#otherSide = initSides().then(([ourSide, otherSide, cleanup]) => { + if (cleanup) { + SimpleTest.registerCleanupFunction(cleanup); + } + ourSide.addEventListener("message", ({ data }) => { + global.resultReceived(data); + }); + return otherSide; + }); + } + + get otherSide() { + return this.#otherSide; + } + + waitForResult() { + ok(!this.resolve, "Still waiting on previous message?"); + return new Promise(resolve => { + this.resolve = resolve; + }); + } + resultReceived(value) { + this.resolve(value); + this.resolve = undefined; + } +} + +// Init functions return an array containing in order the object to use for +// sending a messages to the other global, the object to use to listen for +// messages from the other global and optioanlly a cleanup function. + +function waitForInitMessage(target) { + return new Promise(resolve => { + target.addEventListener("message", resolve, { once: true }); + }); +} + +function getModuleURL(additionalScript = "") { + return new URL( + `file_structuredCloneAndExposed.sjs?additionalScript=${encodeURIComponent( + additionalScript + )}`, + location.href + ); +} + +function initFrame() { + let callInstallListeners = ` + installListeners(globalThis, parent); + `; + frame.srcdoc = + `<script src="${getModuleURL(callInstallListeners)}" type="module"></` + + `script>`; + return waitForInitMessage(self).then(() => [self, frame.contentWindow]); +} + +function initDedicatedWorker() { + let callInstallListeners = ` + installListeners(globalThis, globalThis); + `; + const worker = new Worker(getModuleURL(callInstallListeners), { + type: "module", + }); + return waitForInitMessage(worker).then(() => [worker, worker]); +} + +function initSharedWorker() { + let callInstallListeners = ` + self.addEventListener("connect", (e) => { + const port = e.ports[0]; + port.start(); + installListeners(port, port); + }); + `; + const worker = new SharedWorker(getModuleURL(callInstallListeners), { + type: "module", + }); + worker.port.start(); + return waitForInitMessage(worker.port).then(() => [worker.port, worker.port]); +} + +function initServiceWorker() { + let callInstallListeners = ` + addEventListener("message", ({ source: client }) => { + installListeners(globalThis, client); + }, { once: true }); + `; + + return navigator.serviceWorker + .register(getModuleURL(callInstallListeners), { type: "module" }) + .then(registration => { + let worker = + registration.installing || registration.waiting || registration.active; + worker.postMessage("installListeners"); + return waitForInitMessage(navigator.serviceWorker).then(() => [ + navigator.serviceWorker, + worker, + () => registration.unregister(), + ]); + }); +} + +function initAudioWorklet() { + const exporter = ` + export { installListeners }; + `; + + const workletSource = ` + import { installListeners } from "${getModuleURL(exporter)}"; + + class CustomAudioWorkletProcessor extends AudioWorkletProcessor { + constructor() { + super(); + this.port.start(); + installListeners(this.port, this.port, "AudioWorklet"); + } + + process(inputs, outputs, params) { + // Do nothing, output silence + } + } + + registerProcessor("custom-audio-worklet-processor", CustomAudioWorkletProcessor); + `; + let workletURL = URL.createObjectURL( + new Blob([workletSource], { type: "text/javascript" }) + ); + + // We need to keep the context alive while we're using the message ports. + globalThis.context = new OfflineAudioContext(1, 64, 8000); + return context.audioWorklet.addModule(workletURL).then(() => { + let node = new AudioWorkletNode(context, "custom-audio-worklet-processor"); + node.port.start(); + return waitForInitMessage(node.port).then(() => [ + node.port, + node.port, + () => { + node.port.close(); + delete globalThis.context; + }, + ]); + }); +} + +const globals = [ + ["Window", ["Window"], initFrame], + [ + "DedicatedWorkerGlobalScope", + ["Worker", "DedicatedWorker"], + initDedicatedWorker, + ], + ["SharedWorkerGlobalScope", ["Worker", "SharedWorker"], initSharedWorker], + ["ServiceWorkerGlobalScope", ["Worker", "ServiceWorker"], initServiceWorker], + // WorkerDebuggerGlobalScope can only receive string messages. + ["AudioWorkletGlobalScope", ["Worklet", "AudioWorklet"], initAudioWorklet], + // PaintWorkletGlobalScope doesn't have a messaging mechanism +].map(([global, globalNames, init]) => [ + global, + new WebIDLGlobal(globalNames, init), +]); + + +function generateCertificate() { + return RTCPeerConnection.generateCertificate({ + name: "ECDSA", + namedCurve: "P-256", + }); +} + +function makeCanvas() { + const width = 20; + const height = 20; + let canvas = new OffscreenCanvas(width, height); + let ctx = canvas.getContext("2d"); + ctx.fillStyle = "rgba(50, 100, 150, 255)"; + ctx.fillRect(0, 0, width, height); + return canvas; +} + +function makeVideoFrame() { + return new VideoFrame(makeCanvas(), { timestamp: 1, alpha: "discard" }); +} + +function makeImageBitmap() { + return makeCanvas().transferToImageBitmap(); +} + +const serializable = [ + ["DOMException", ["Window", "Worker"], () => new DOMException()], + ["DOMMatrixReadOnly", ["Window", "Worker"], () => new DOMMatrixReadOnly()], + ["ImageBitmap", ["Window", "Worker"], makeImageBitmap], + ["RTCCertificate", ["Window"], generateCertificate], + ["VideoFrame", ["Window", "DedicatedWorker"], makeVideoFrame], +]; + +const transferable = [ + [ + "MessagePort", + ["Window", "Worker", "AudioWorklet"], + () => new MessageChannel().port1, + ], + ["ImageBitmap", ["Window", "Worker"], makeImageBitmap], + ["ReadableStream", ["*"], () => new ReadableStream()], + ["VideoFrame", ["Window", "DedicatedWorker"], makeVideoFrame], +]; + + +function isExposed(exposure, global) { + if (exposure.length === 1 && exposure[0] === "*") { + return true; + } + return !!global.globalNames.filter(v => exposure.includes(v)).length; +} + + +async function runTest() { + await SpecialPowers.pushPrefEnv({ set: [["dom.media.webcodecs.enabled", true]] }); + + async function testDOMClass(domClass, exposure, createObject, transferable) { + for ([globalName, webidlGlobal] of globals) { + info( + `Testing ${ + transferable ? "transfer" : "serialization" + } of ${domClass} with ${globalName}` + ); + + let object = await createObject(); + let otherSide = await webidlGlobal.otherSide; + let options = { targetOrigin: "*" }; + let message; + if (transferable) { + options.transfer = [object]; + } else { + message = object; + } + otherSide.postMessage(message, options); + + let result = await webidlGlobal.waitForResult(); + let expected = isExposed(exposure, webidlGlobal); + + if ( + domClass === "ImageBitmap" && + globalName === "ServiceWorkerGlobalScope" + ) { + // Service workers don't support transferring shared memory objects + // (see ServiceWorker::PostMessage). + expected = false; + } + + is( + result, + expected, + `Deserialization for ${domClass} in ${globalName} ${ + expected ? "should" : "shouldn't" + } succeed` + ); + } + } + + for ([domClass, exposure, createObject] of serializable) { + await testDOMClass(domClass, exposure, createObject, false); + } + for ([domClass, exposure, createObject] of transferable) { + await testDOMClass(domClass, exposure, createObject, true); + } + + SimpleTest.finish(); +} + +runTest(); + +</script> +</pre> +</body> +</html> |