// Test helpers used by multiple Web Locks API tests. (() => { // Generate a unique resource identifier, using the script path and // test case name. This is useful to avoid lock interference between // test cases. let res_num = 0; self.uniqueName = (testCase, prefix) => { return `${self.location.pathname}-${prefix}-${testCase.name}-${++res_num}`; }; self.uniqueNameByQuery = () => { const prefix = new URL(location.href).searchParams.get('prefix'); return `${prefix}-${++res_num}`; } // Inject an iframe showing the given url into the page, and resolve // the returned promise when the frame is loaded. self.iframe = url => new Promise(resolve => { const element = document.createElement('iframe'); element.addEventListener( 'load', () => { resolve(element); }, { once: true }); element.src = url; document.documentElement.appendChild(element); }); // Post a message to the target frame, and resolve the returned // promise when a response comes back. The posted data is annotated // with unique id to track the response. This assumes the use of // 'iframe.html' as the frame, which implements this protocol. let next_request_id = 0; self.postToFrameAndWait = (frame, data) => { const iframe_window = frame.contentWindow; data.rqid = next_request_id++; iframe_window.postMessage(data, '*'); return new Promise(resolve => { const listener = event => { if (event.source !== iframe_window || event.data.rqid !== data.rqid) return; self.removeEventListener('message', listener); resolve(event.data); }; self.addEventListener('message', listener); }); }; // Post a message to the target worker, and resolve the returned // promise when a response comes back. The posted data is annotated // with unique id to track the response. This assumes the use of // 'worker.js' as the worker, which implements this protocol. self.postToWorkerAndWait = (worker, data) => { return new Promise(resolve => { data.rqid = next_request_id++; worker.postMessage(data); const listener = event => { if (event.data.rqid !== data.rqid) return; worker.removeEventListener('message', listener); resolve(event.data); }; worker.addEventListener('message', listener); }); }; /** * Request a lock and hold it until the subtest ends. * @param {*} t test runner object * @param {string} name lock name * @param {LockOptions=} options lock options * @returns */ self.requestLockAndHold = (t, name, options = {}) => { let [promise, resolve] = self.makePromiseAndResolveFunc(); const released = navigator.locks.request(name, options, () => promise); // Add a cleanup function that releases the lock by resolving the promise, // and then waits until the lock is really released, to avoid contaminating // following tests with temporarily held locks. t.add_cleanup(() => { resolve(); // Cleanup shouldn't fail if the request is aborted. return released.catch(() => undefined); }); return released; }; self.makePromiseAndResolveFunc = () => { let resolve; const promise = new Promise(r => { resolve = r; }); return [promise, resolve]; }; })();