172 lines
6.4 KiB
HTML
172 lines
6.4 KiB
HTML
|
|
<!DOCTYPE html>
|
|
<meta charset="utf-8"/>
|
|
<title>Web Locks API: Partitioned WebLocks</title>
|
|
|
|
<!-- Pull in get_host_info() -->
|
|
<script src="/common/get-host-info.sub.js"></script>
|
|
<script src="/common/utils.js"></script>
|
|
<script src="/common/dispatcher/dispatcher.js"></script>
|
|
<script src="resources/helpers.js"></script>
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<body>
|
|
<script>
|
|
|
|
const { HTTPS_ORIGIN, HTTPS_NOTSAMESITE_ORIGIN } = get_host_info();
|
|
// Map of lock_id => function that releases a lock.
|
|
const held = new Map();
|
|
let next_lock_id = 1;
|
|
|
|
// How this test works:
|
|
// Step 1 (top-frame): request an exclusive web-lock and store its id
|
|
// and release for clean-up.
|
|
// Step 2 (top-frame): open a pop-up window and load a not-same-site
|
|
// ./web-locks/resources/partitioned-parent.html
|
|
// Step 3 (pop-up): load a same-site iframe inside the pop-up.
|
|
// Step 4 (pop-up): send a web-lock request to the same-site iframe.
|
|
// Step 5 (iframe): process the web-lock request and message the result
|
|
// back to the pop-up.
|
|
// Step 6 (pop-up): intercept the result message from the iframe and
|
|
// send it to the top-frame.
|
|
// Step 7 (top-frame): add cleanup hook.
|
|
// Step 8 (top-frame): ensure that the same-site iframe's web-lock
|
|
// request succeeds since it and the top-level site are successfully
|
|
// partitioned and each can hold an exclusive lock.
|
|
|
|
async function third_party_test(t) {
|
|
let target_url = HTTPS_ORIGIN + '/web-locks/resources/iframe.html';
|
|
target_url = new URL(
|
|
`/web-locks/resources/partitioned-parent.html?target=${encodeURIComponent(target_url)}`,
|
|
HTTPS_NOTSAMESITE_ORIGIN + self.location.pathname);
|
|
|
|
// Step 1.
|
|
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;
|
|
}
|
|
return promise;
|
|
});
|
|
held.set(lock_id, { release, released });
|
|
|
|
// Step 2.
|
|
const w = window.open(target_url);
|
|
const result = await new Promise(resolve => window.onmessage = resolve);
|
|
|
|
// Step 7.
|
|
t.add_cleanup(() => {
|
|
w.close();
|
|
let released = [];
|
|
for(let i = 1; i < next_lock_id; i++){
|
|
let h = held.get(i);
|
|
h.release();
|
|
released.push(h.released);
|
|
}
|
|
return Promise.allSettled(released);
|
|
});
|
|
|
|
// Step 8.
|
|
// When 3rd party storage partitioning is enabled, the iframe should be able
|
|
// to acquire a lock with the same name as one exclusively held by the opener
|
|
// of its top window, even when that opener has the same origin.
|
|
assert_equals(result.data.failed, undefined,
|
|
'The 1p iframe failed to acquire the lock');
|
|
}
|
|
|
|
promise_test(t => {
|
|
return third_party_test(t);
|
|
}, 'WebLocks of an iframe under a 3rd-party site are partitioned');
|
|
|
|
|
|
// Optional Test: Checking for partitioned web locks in an A->B->A
|
|
// (nested-iframe with cross-site ancestor chain) scenario.
|
|
//
|
|
// How this test works:
|
|
// Nested Step 1 (top frame): request an exclusive web-lock and
|
|
// store its id and release for clean-up.
|
|
// Nested Step 2 (top frame): open a pop-up window and load a
|
|
// same-site /web-locks/resources/partitioned-parent.html.
|
|
// Nested Step 3 (pop-up): load a not-same-site "parent" iframe (A->B)
|
|
// (/web-locks/resources/iframe-parent.html) inside the pop-up.
|
|
// Nested Step 4 (pop-up): send a web-lock request to the parent iframe.
|
|
// Nested Step 5 (parent iframe): load a "child" iframe (A->B->A)
|
|
// (/web-locks/resources/iframe.html) that is same-site with the
|
|
// pop-up inside the "parent" iframe.
|
|
// Nested Step 6 (parent iframe): pass on the web-lock request message to
|
|
// the "child" iframe.
|
|
// Nested Step 7 (child iframe): process the web-lock request and message
|
|
// the result to the parent iframe.
|
|
// Nested Step 8 (parent iframe): intercept the result message from the
|
|
// child iframe and send it to the pop-up.
|
|
// Nested Step 9 (pop-up): intercept the result message from the parent
|
|
// iframe and send it to the top frame.
|
|
// Nested Step 10 (top frame): add cleanup hook
|
|
// Nested Step 11 (top frame): ensure that the same-site iframe's web-lock
|
|
// request succeeds since it and the top-level are successfully
|
|
// partitioned and each can hold an exclusive lock.
|
|
|
|
// Map of lock_id => function that releases a lock.
|
|
const held_2 = new Map();
|
|
let next_lock_id_2 = 1;
|
|
|
|
async function nested_iframe_test(t) {
|
|
// Create innermost child iframe (leaf).
|
|
let leaf_url = HTTPS_ORIGIN + '/web-locks/resources/iframe.html';
|
|
// Wrap the child iframe in its cross-origin parent (middle).
|
|
let middle_url = new URL(
|
|
`/web-locks/resources/iframe-parent.html?target=${encodeURIComponent(leaf_url)}`,
|
|
HTTPS_NOTSAMESITE_ORIGIN + self.location.pathname);
|
|
// Embed the parent iframe in the top-level site (top).
|
|
let top_url = new URL(
|
|
`/web-locks/resources/partitioned-parent.html?target=${encodeURIComponent(middle_url)}`,
|
|
HTTPS_ORIGIN + self.location.pathname);
|
|
|
|
// Nested Step 1.
|
|
// Request the weblock for the top-level site.
|
|
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;
|
|
}
|
|
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
|
|
// will attempt to obtain the same weblock as above.
|
|
const w = window.open(top_url);
|
|
const result = await new Promise(resolve => window.onmessage = resolve);
|
|
|
|
// Nested Step 10.
|
|
t.add_cleanup(() => {
|
|
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.
|
|
// With third-party storage partitioning enabled, the same-site iframe
|
|
// should be able to acquire the lock as it has a cross-site ancestor
|
|
// and is partitioned separately from the top-level site.
|
|
assert_equals(result.data.failed, undefined,
|
|
'The 1p iframe failed to acquire the lock');
|
|
}
|
|
|
|
promise_test(t => {
|
|
return nested_iframe_test(t);
|
|
}, 'WebLocks of a nested iframe with a cross-site ancestor are partitioned');
|
|
</script>
|
|
</body>
|