blob: c4509de637b81f55a3bc29a75f4b381b93f11b7b [file] [log] [blame]
// 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");