// META: title=Web Locks API: navigator.locks.request method // META: script=resources/helpers.js // META: global=window,dedicatedworker,sharedworker,serviceworker 'use strict'; promise_test(async t => { const res = uniqueName(t); await promise_rejects_js(t, TypeError, navigator.locks.request()); await promise_rejects_js(t, TypeError, navigator.locks.request(res)); }, 'navigator.locks.request requires a name and a callback'); promise_test(async t => { const res = uniqueName(t); await promise_rejects_js( t, TypeError, navigator.locks.request(res, {mode: 'foo'}, lock => {})); await promise_rejects_js( t, TypeError, navigator.locks.request(res, {mode: null }, lock => {})); assert_equals(await navigator.locks.request( res, {mode: 'exclusive'}, lock => lock.mode), 'exclusive', 'mode is exclusive'); assert_equals(await navigator.locks.request( res, {mode: 'shared'}, lock => lock.mode), 'shared', 'mode is shared'); }, 'mode must be "shared" or "exclusive"'); promise_test(async t => { const res = uniqueName(t); await promise_rejects_dom( t, 'NotSupportedError', navigator.locks.request( res, {steal: true, ifAvailable: true}, lock => {}), "A NotSupportedError should be thrown if both " + "'steal' and 'ifAvailable' are specified."); }, "The 'steal' and 'ifAvailable' options are mutually exclusive"); promise_test(async t => { const res = uniqueName(t); await promise_rejects_dom( t, 'NotSupportedError', navigator.locks.request(res, {mode: 'shared', steal: true}, lock => {}), 'Request with mode=shared and steal=true should fail'); }, "The 'steal' option must be used with exclusive locks"); promise_test(async t => { const res = uniqueName(t); const controller = new AbortController(); await promise_rejects_dom( t, 'NotSupportedError', navigator.locks.request( res, {signal: controller.signal, steal: true}, lock => {}), 'Request with signal and steal=true should fail'); }, "The 'signal' and 'steal' options are mutually exclusive"); promise_test(async t => { const res = uniqueName(t); const controller = new AbortController(); await promise_rejects_dom( t, 'NotSupportedError', navigator.locks.request( res, {signal: controller.signal, ifAvailable: true}, lock => {}), 'Request with signal and ifAvailable=true should fail'); }, "The 'signal' and 'ifAvailable' options are mutually exclusive"); promise_test(async t => { const res = uniqueName(t); await promise_rejects_js( t, TypeError, navigator.locks.request(res, undefined)); await promise_rejects_js( t, TypeError, navigator.locks.request(res, null)); await promise_rejects_js( t, TypeError, navigator.locks.request(res, 123)); await promise_rejects_js( t, TypeError, navigator.locks.request(res, 'abc')); await promise_rejects_js( t, TypeError, navigator.locks.request(res, [])); await promise_rejects_js( t, TypeError, navigator.locks.request(res, {})); await promise_rejects_js( t, TypeError, navigator.locks.request(res, new Promise(r => {}))); }, 'callback must be a function'); promise_test(async t => { const res = uniqueName(t); let release; const promise = new Promise(r => { release = r; }); let returned = navigator.locks.request(res, lock => { return promise; }); const order = []; returned.then(() => { order.push('returned'); }); promise.then(() => { order.push('holding'); }); release(); await Promise.all([returned, promise]); assert_array_equals(order, ['holding', 'returned']); }, 'navigator.locks.request\'s returned promise resolves after' + ' lock is released'); promise_test(async t => { const res = uniqueName(t); const test_error = {name: 'test'}; const p = navigator.locks.request(res, lock => { throw test_error; }); assert_equals(Promise.resolve(p), p, 'request() result is a Promise'); await promise_rejects_exactly(t, test_error, p, 'result should reject'); }, 'Returned Promise rejects if callback throws synchronously'); promise_test(async t => { const res = uniqueName(t); const test_error = {name: 'test'}; const p = navigator.locks.request(res, async lock => { throw test_error; }); assert_equals(Promise.resolve(p), p, 'request() result is a Promise'); await promise_rejects_exactly(t, test_error, p, 'result should reject'); }, 'Returned Promise rejects if callback throws asynchronously'); promise_test(async t => { const res = uniqueName(t); let then_invoked = false; const test_error = { then: _ => { then_invoked = true; } }; const p = navigator.locks.request(res, async lock => { throw test_error; }); assert_equals(Promise.resolve(p), p, 'request() result is a Promise'); await promise_rejects_exactly(t, test_error, p, 'result should reject'); assert_false(then_invoked, 'then() should not be invoked'); }, 'If callback throws a thenable, its then() should not be invoked');