mirror of
https://github.com/servo/servo.git
synced 2025-06-29 19:43:39 +01:00
200 lines
5.9 KiB
JavaScript
200 lines
5.9 KiB
JavaScript
// META: title=Web Locks API: AbortSignal integration
|
|
// META: script=resources/helpers.js
|
|
// META: global=window,dedicatedworker,sharedworker,serviceworker
|
|
|
|
'use strict';
|
|
|
|
function makePromiseAndResolveFunc() {
|
|
let resolve;
|
|
const promise = new Promise(r => { resolve = r; });
|
|
return [promise, resolve];
|
|
}
|
|
|
|
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(
|
|
t, new 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(
|
|
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.
|
|
const never_settled = new Promise(resolve => t.add_cleanup(resolve));
|
|
navigator.locks.request(res, lock => never_settled);
|
|
|
|
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(
|
|
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.
|
|
const never_settled = new Promise(resolve => t.add_cleanup(resolve));
|
|
navigator.locks.request(res, lock => never_settled);
|
|
|
|
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(
|
|
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(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');
|