Bug 1863277 - Make Web Locks tests deal with promise argument conversion as defined in the WebIDL spec. r=saschanaz

Some Web Locks wpt tests release locks from a cleanup task, but they don't wait
until the lock is actually released. The tests currently pass, because in most
browser engine implementations the promise that's returned from the callback for
these locks is incorrectly converted by just keeping the original promise. The
WebIDL spec actually requires the conversion to create a new promise around the
returned promise, which means the release will require another microtask. If we
fix implementations to follow the WebIDL spec then we'll start the next test
before the locks from the previous test are released, causing havoc.

Differential Revision: https://phabricator.services.mozilla.com/D192819
This commit is contained in:
Peter Van der Beken 2023-11-06 11:08:49 +00:00
parent f34ff74915
commit 312850d94e
2 changed files with 31 additions and 18 deletions

View File

@ -41,18 +41,17 @@ async function third_party_test(t) {
HTTPS_NOTSAMESITE_ORIGIN + self.location.pathname);
// Step 1.
navigator.locks.request('testLock', {mode: 'exclusive', ifAvailable: true},
let lock_id = next_lock_id++;
let [ promise, release ] = makePromiseAndResolveFunc();
let released = navigator.locks.request('testLock', {mode: 'exclusive', ifAvailable: true},
lock => {
if (lock === null) {
assert_true(false)
return;
}
let lock_id = next_lock_id++;
let release;
const promise = new Promise(r => { release = r; });
held.set(lock_id, release);
return promise;
});
held.set(lock_id, { release, released });
// Step 2.
const w = window.open(target_url);
@ -60,10 +59,14 @@ async function third_party_test(t) {
// Step 7.
t.add_cleanup(() => {
w.close()
w.close();
let released = [];
for(let i = 1; i < next_lock_id; i++){
held.get(i)();
let h = held.get(i);
h.release();
released.push(h.released);
}
return Promise.allSettled(released);
});
// Step 8.
@ -124,19 +127,17 @@ async function nested_iframe_test(t) {
// Nested Step 1.
// Request the weblock for the top-level site.
navigator.locks.request('testLock', {mode: 'exclusive', ifAvailable: true},
let lock_id = next_lock_id_2++;
let [ promise, release ] = makePromiseAndResolveFunc();
let released = navigator.locks.request('testLock', {mode: 'exclusive', ifAvailable: true},
lock => {
if (lock === null) {
assert_true(false)
return;
}
// Obtain and store the release functions for clean-up.
let lock_id = next_lock_id_2++;
let release;
const promise = new Promise(r => { release = r; });
held_2.set(lock_id, release);
return promise;
}).catch(error => alert(error.message));
held_2.set(lock_id, { release, released });
// Nested Step 2.
// Open the nested iframes. The script in the innermost child iframe
@ -146,10 +147,14 @@ async function nested_iframe_test(t) {
// Nested Step 10.
t.add_cleanup(() => {
w.close()
for(let i = 1; i < next_lock_id_2; i++){
held_2.get(i)();
w.close();
let released = [];
for(let i = 1; i < next_lock_id; i++){
let h = held_2.get(i);
h.release();
released.push(h.released);
}
return Promise.allSettled(released);
});
// Nested Step 11.

View File

@ -69,9 +69,17 @@
* @returns
*/
self.requestLockAndHold = (t, name, options = {}) => {
return navigator.locks.request(name, options, () => {
return new Promise(resolve => t.add_cleanup(resolve));
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 = () => {