videoFrame-serialization.https.html (9326B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <script src='/resources/testharness.js'></script> 5 <script src='/resources/testharnessreport.js'></script> 6 <script src='/common/get-host-info.sub.js'></script> 7 <script src='/webcodecs/utils.js'></script> 8 <script id='workerCode' type='javascript/worker'> 9 self.onmessage = (e) => { 10 let frame = e.data.frame; 11 if (e.data.transfer) { 12 postMessage(frame, [frame]); 13 } else { 14 postMessage(frame); 15 } 16 }; 17 </script> 18 <script id='sharedWorkerCode' type='javascript/worker'> 19 const data = new Uint8Array([ 20 1, 2, 3, 4, 5, 6, 7, 8, 21 9, 10, 11, 12, 13, 14, 15, 16, 22 ]); 23 let received = new Map(); 24 self.onconnect = function (event) { 25 const port = event.ports[0]; 26 port.onmessage = function (e) { 27 if (e.data == 'create-frame') { 28 let frameOrError = null; 29 try { 30 frameOrError = new VideoFrame(data, { 31 timestamp: 0, 32 codedWidth: 2, 33 codedHeight: 2, 34 format: 'RGBA', 35 }); 36 } catch (error) { 37 frameOrError = error 38 } 39 port.postMessage(frameOrError); 40 return; 41 } 42 if (e.data.hasOwnProperty('id')) { 43 port.postMessage( 44 received.get(e.data.id) ? 'RECEIVED' : 'NOT_RECEIVED'); 45 return; 46 } 47 if (e.data.toString() == '[object VideoFrame]') { 48 received.set(e.data.timestamp, e.data); 49 } 50 }; 51 }; 52 </script> 53 </head> 54 <body> 55 <script> 56 const HELPER = '/webcodecs/videoFrame-serialization.crossAgentCluster.helper.html'; 57 const SAMEORIGIN_BASE = get_host_info().HTTPS_ORIGIN; 58 const CROSSORIGIN_BASE = get_host_info().HTTPS_NOTSAMESITE_ORIGIN; 59 const SAMEORIGIN_HELPER = SAMEORIGIN_BASE + HELPER; 60 const CROSSORIGIN_HELPER = CROSSORIGIN_BASE + HELPER; 61 const SERVICE_WORKER = 'serialization.crossAgentCluster.serviceworker.js'; 62 63 promise_test(async (t) => { 64 const target = (await appendIframe(SAMEORIGIN_HELPER)).contentWindow; 65 let frame = createVideoFrame(10); 66 t.add_cleanup(() => frame.close()); 67 assert_true(await canSerializeVideoFrame(target, frame)); 68 }, 'Verify frames can be passed within the same agent clusters'); 69 70 promise_test(async (t) => { 71 const blob = new Blob([document.querySelector('#workerCode').textContent], { 72 type: 'text/javascript', 73 }); 74 const worker = new Worker(window.URL.createObjectURL(blob)); 75 let frame = createVideoFrame(30); 76 t.add_cleanup(() => frame.close()); 77 worker.postMessage({frame: frame, transfer: false}); 78 const received = await new Promise(resolve => worker.onmessage = e => { 79 resolve(e.data); 80 }); 81 assert_equals(received.toString(), '[object VideoFrame]'); 82 assert_equals(received.timestamp, 30); 83 }, 'Verify frames can be passed back and forth between main and worker'); 84 85 promise_test(async (t) => { 86 const encodedScriptText = btoa("self.onmessage = (e) => { postMessage(e.data);};"); 87 const scriptURL = 'data:text/javascript;base64,' + encodedScriptText; 88 const worker = new Worker(scriptURL); 89 let frame = createVideoFrame(40); 90 t.add_cleanup(() => frame.close()); 91 worker.postMessage(frame); 92 const received = await new Promise(resolve => worker.onmessage = e => { 93 resolve(e.data); 94 }); 95 assert_equals(received.toString(), '[object VideoFrame]'); 96 assert_equals(received.timestamp, 40); 97 }, 'Verify frames can be passed back and forth between main and data-url worker'); 98 99 promise_test(async (t) => { 100 const blob = new Blob([document.querySelector('#sharedWorkerCode').textContent], { 101 type: 'text/javascript', 102 }); 103 const worker = new SharedWorker(window.URL.createObjectURL(blob)); 104 let frame = createVideoFrame(50); 105 t.add_cleanup(() => frame.close()); 106 worker.port.postMessage(frame); 107 worker.port.postMessage({'id': 50}); 108 const received = await new Promise(resolve => worker.port.onmessage = e => { 109 resolve(e.data); 110 }); 111 assert_equals(received, 'NOT_RECEIVED'); 112 }, 'Verify frames cannot be passed to sharedworker'); 113 114 promise_test(async (t) => { 115 navigator.serviceWorker.register(SERVICE_WORKER); 116 navigator.serviceWorker.ready.then((registration) => { 117 let frame = createVideoFrame(60); 118 t.add_cleanup(() => frame.close()); 119 registration.active.postMessage(frame); 120 registration.active.postMessage({'videoFrameId': 60}); 121 }); 122 const received = await new Promise(resolve => navigator.serviceWorker.onmessage = (e) => { 123 resolve(e.data); 124 }); 125 assert_equals(received, 'NOT_RECEIVED'); 126 }, 'Verify frames cannot be passed to serviceworker'); 127 128 promise_test(async (t) => { 129 const target = (await appendIframe(SAMEORIGIN_HELPER)).contentWindow; 130 let frame = createVideoFrame(70); 131 t.add_cleanup(() => frame.close()); 132 assert_true(await canTransferVideoFrame(target, frame)); 133 assert_true(isFrameClosed(frame)); 134 }, 'Verify frames can be transferred within the same agent clusters'); 135 136 promise_test(async (t) => { 137 const blob = new Blob([document.querySelector('#workerCode').textContent], { 138 type: 'text/javascript', 139 }); 140 const worker = new Worker(window.URL.createObjectURL(blob)); 141 let frame = createVideoFrame(90); 142 t.add_cleanup(() => frame.close()); 143 worker.postMessage({frame: frame, transfer: true}, [frame]); 144 const received = await new Promise(resolve => worker.onmessage = e => { 145 resolve(e.data); 146 }); 147 assert_equals(received.toString(), '[object VideoFrame]'); 148 assert_equals(received.timestamp, 90); 149 }, 'Verify frames can be transferred back and forth between main and worker'); 150 151 promise_test(async (t) => { 152 const encodedScriptText = btoa("self.onmessage = (e) => { let f = e.data; postMessage(f, [f]); };"); 153 const scriptURL = 'data:text/javascript;base64,' + encodedScriptText; 154 const worker = new Worker(scriptURL); 155 let frame = createVideoFrame(100); 156 t.add_cleanup(() => frame.close()); 157 worker.postMessage(frame, [frame]); 158 const received = await new Promise(resolve => worker.onmessage = e => { 159 resolve(e.data); 160 }); 161 assert_equals(received.toString(), '[object VideoFrame]'); 162 assert_equals(received.timestamp, 100); 163 }, 'Verify frames can be transferred back and forth between main and data-url worker'); 164 165 promise_test(async (t) => { 166 const blob = new Blob([document.querySelector('#sharedWorkerCode').textContent], { 167 type: 'text/javascript', 168 }); 169 const worker = new SharedWorker(window.URL.createObjectURL(blob)); 170 let frame = createVideoFrame(110); 171 t.add_cleanup(() => frame.close()); 172 worker.port.postMessage(frame, [frame]); 173 worker.port.postMessage({'id': 110}); 174 const received = await new Promise(resolve => worker.port.onmessage = e => { 175 resolve(e.data); 176 }); 177 assert_equals(received, 'NOT_RECEIVED'); 178 }, 'Verify frames cannot be transferred to a sharedworker'); 179 180 promise_test(async (t) => { 181 navigator.serviceWorker.register(SERVICE_WORKER); 182 navigator.serviceWorker.ready.then((registration) => { 183 let frame = createVideoFrame(120); 184 t.add_cleanup(() => frame.close()); 185 registration.active.postMessage(frame, [frame]); 186 registration.active.postMessage({'videoFrameId': 120}); 187 }); 188 const received = await new Promise(resolve => navigator.serviceWorker.onmessage = (e) => { 189 resolve(e.data); 190 }); 191 assert_equals(received, 'NOT_RECEIVED'); 192 }, 'Verify frames cannot be transferred to serviceworker'); 193 194 promise_test(async () => { 195 const blob = new Blob([document.querySelector('#sharedWorkerCode').textContent], { 196 type: 'text/javascript', 197 }); 198 const worker = new SharedWorker(window.URL.createObjectURL(blob)); 199 worker.port.postMessage('create-frame'); 200 const received = await new Promise(resolve => worker.port.onmessage = e => { 201 resolve(e.data); 202 }); 203 assert_true(received instanceof ReferenceError); 204 }, 'Verify frames is unavailable in sharedworker'); 205 206 promise_test(async () => { 207 navigator.serviceWorker.register(SERVICE_WORKER); 208 let registration = await navigator.serviceWorker.ready; 209 registration.active.postMessage('create-VideoFrame'); 210 const received = await new Promise(resolve => navigator.serviceWorker.onmessage = (e) => { 211 resolve(e.data); 212 }); 213 assert_true(received instanceof ReferenceError); 214 }, 'Verify frames is unavailable in serviceworker'); 215 216 function appendIframe(src) { 217 const frame = document.createElement('iframe'); 218 document.body.appendChild(frame); 219 frame.src = src; 220 return new Promise(resolve => frame.onload = () => resolve(frame)); 221 }; 222 223 function createVideoFrame(ts) { 224 let data = new Uint8Array([ 225 1, 2, 3, 4, 5, 6, 7, 8, 226 9, 10, 11, 12, 13, 14, 15, 16, 227 ]); 228 return new VideoFrame(data, { 229 timestamp: ts, 230 codedWidth: 2, 231 codedHeight: 2, 232 format: 'RGBA', 233 }); 234 } 235 236 function canSerializeVideoFrame(target, vf) { 237 return canPostVideoFrame(target, vf, false); 238 }; 239 240 function canTransferVideoFrame(target, vf) { 241 return canPostVideoFrame(target, vf, true); 242 }; 243 244 function canPostVideoFrame(target, vf, transfer) { 245 if (transfer) { 246 target.postMessage(vf, '*', [vf]); 247 assert_true(isFrameClosed(vf)); 248 } else { 249 target.postMessage(vf, '*'); 250 } 251 // vf.timestamp doesn't change after vf is closed, so it's fine to use it. 252 target.postMessage({'id': vf.timestamp}, '*'); 253 return new Promise(resolve => window.onmessage = e => { 254 resolve(e.data == 'RECEIVED'); 255 }); 256 }; 257 </script> 258 </body> 259 </html>