| // META: title=Web Locks API: AbortSignal integration |
| // META: script=resources/helpers.js |
| // META: global=window,dedicatedworker,sharedworker,serviceworker |
| |
| 'use strict'; |
| |
| promise_test(async t => { |
| const res = uniqueName(t); |
| |
| // These cases should not work: |
| for (const signal of ['string', 12.34, false, {}, Symbol(), () => {}, self]) { |
| await promise_rejects_js( |
| t, TypeError, |
| navigator.locks.request( |
| res, {signal}, t.unreached_func('callback should not run')), |
| 'Bindings should throw if the signal option is a not an AbortSignal'); |
| } |
| }, 'The signal option must be an AbortSignal'); |
| |
| promise_test(async t => { |
| const res = uniqueName(t); |
| const controller = new AbortController(); |
| controller.abort(); |
| |
| await promise_rejects_dom( |
| t, 'AbortError', |
| navigator.locks.request(res, {signal: controller.signal}, |
| t.unreached_func('callback should not run')), |
| 'Request should reject with AbortError'); |
| }, 'Passing an already aborted signal aborts'); |
| |
| promise_test(async t => { |
| const res = uniqueName(t); |
| |
| // Grab a lock and hold it until this subtest completes. |
| requestLockAndHold(t, res); |
| |
| const controller = new AbortController(); |
| |
| const promise = |
| navigator.locks.request(res, {signal: controller.signal}, |
| t.unreached_func('callback should not run')); |
| |
| // Verify the request is enqueued: |
| const state = await navigator.locks.query(); |
| assert_equals(state.held.filter(lock => lock.name === res).length, 1, |
| 'Number of held locks'); |
| assert_equals(state.pending.filter(lock => lock.name === res).length, 1, |
| 'Number of pending locks'); |
| |
| const rejected = promise_rejects_dom( |
| t, 'AbortError', promise, 'Request should reject with AbortError'); |
| |
| controller.abort(); |
| |
| await rejected; |
| |
| }, 'An aborted request results in AbortError'); |
| |
| promise_test(async t => { |
| const res = uniqueName(t); |
| |
| // Grab a lock and hold it until this subtest completes. |
| requestLockAndHold(t, res); |
| |
| const controller = new AbortController(); |
| |
| const promise = |
| navigator.locks.request(res, {signal: controller.signal}, lock => {}); |
| |
| // Verify the request is enqueued: |
| const state = await navigator.locks.query(); |
| assert_equals(state.held.filter(lock => lock.name === res).length, 1, |
| 'Number of held locks'); |
| assert_equals(state.pending.filter(lock => lock.name === res).length, 1, |
| 'Number of pending locks'); |
| |
| const rejected = promise_rejects_dom( |
| t, 'AbortError', promise, 'Request should reject with AbortError'); |
| |
| let callback_called = false; |
| t.step_timeout(() => { |
| callback_called = true; |
| controller.abort(); |
| }, 10); |
| |
| await rejected; |
| assert_true(callback_called, 'timeout should have caused the abort'); |
| |
| }, 'Abort after a timeout'); |
| |
| promise_test(async t => { |
| const res = uniqueName(t); |
| |
| const controller = new AbortController(); |
| |
| let got_lock = false; |
| await navigator.locks.request( |
| res, {signal: controller.signal}, async lock => { got_lock = true; }); |
| |
| assert_true(got_lock, 'Lock should be acquired if abort is not signaled.'); |
| |
| }, 'Signal that is not aborted'); |
| |
| promise_test(async t => { |
| const res = uniqueName(t); |
| |
| const controller = new AbortController(); |
| |
| let got_lock = false; |
| const p = navigator.locks.request( |
| res, {signal: controller.signal}, lock => { got_lock = true; }); |
| |
| // Even though lock is grantable, this abort should be processed synchronously. |
| controller.abort(); |
| |
| await promise_rejects_dom(t, 'AbortError', p, 'Request should abort'); |
| |
| assert_false(got_lock, 'Request should be aborted if signal is synchronous'); |
| |
| await navigator.locks.request(res, lock => { got_lock = true; }); |
| assert_true(got_lock, 'Subsequent request should not be blocked'); |
| |
| }, 'Synchronously signaled abort'); |
| |
| promise_test(async t => { |
| const res = uniqueName(t); |
| |
| const controller = new AbortController(); |
| |
| // Make a promise that resolves when the lock is acquired. |
| const [acquired_promise, acquired_func] = makePromiseAndResolveFunc(); |
| |
| // Request the lock. |
| let release_func; |
| const released_promise = navigator.locks.request( |
| res, {signal: controller.signal}, lock => { |
| acquired_func(); |
| |
| // Hold lock until release_func is called. |
| const [waiting_promise, waiting_func] = makePromiseAndResolveFunc(); |
| release_func = waiting_func; |
| return waiting_promise; |
| }); |
| |
| // Wait for the lock to be acquired. |
| await acquired_promise; |
| |
| // Signal an abort. |
| controller.abort(); |
| |
| // Release the lock. |
| release_func('resolved ok'); |
| |
| assert_equals(await released_promise, 'resolved ok', |
| 'Lock released promise should not reject'); |
| |
| }, 'Abort signaled after lock granted'); |
| |
| promise_test(async t => { |
| const res = uniqueName(t); |
| |
| const controller = new AbortController(); |
| |
| // Make a promise that resolves when the lock is acquired. |
| const [acquired_promise, acquired_func] = makePromiseAndResolveFunc(); |
| |
| // Request the lock. |
| let release_func; |
| const released_promise = navigator.locks.request( |
| res, {signal: controller.signal}, lock => { |
| acquired_func(); |
| |
| // Hold lock until release_func is called. |
| const [waiting_promise, waiting_func] = makePromiseAndResolveFunc(); |
| release_func = waiting_func; |
| return waiting_promise; |
| }); |
| |
| // Wait for the lock to be acquired. |
| await acquired_promise; |
| |
| // Release the lock. |
| release_func('resolved ok'); |
| |
| // Signal an abort. |
| controller.abort(); |
| |
| assert_equals(await released_promise, 'resolved ok', |
| 'Lock released promise should not reject'); |
| |
| }, 'Abort signaled after lock released'); |
| |
| promise_test(async t => { |
| const res = uniqueName(t); |
| |
| const controller = new AbortController(); |
| const first = requestLockAndHold(t, res, { signal: controller.signal }); |
| const next = navigator.locks.request(res, () => "resolved"); |
| controller.abort(); |
| |
| await promise_rejects_dom(t, "AbortError", first, "Request should abort"); |
| assert_equals( |
| await next, |
| "resolved", |
| "The next request is processed after abort" |
| ); |
| }, "Abort should process the next pending lock request"); |