summaryrefslogtreecommitdiffstats
path: root/dom/tests/mochitest/whatwg/test_structuredCloneAndExposed.html
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/tests/mochitest/whatwg/test_structuredCloneAndExposed.html307
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>