1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
<!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;
async function third_party_test(t) {
let target_url = HTTPS_ORIGIN + '/web-locks/resources/iframe.html';
target_url = new URL(
`./resources/partitioned-parent.html?target=${encodeURIComponent(target_url)}`,
HTTPS_NOTSAMESITE_ORIGIN + self.location.pathname);
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;
});
const w = window.open(target_url);
const result = await new Promise(resolve => window.onmessage = resolve);
// When 3rd party storage partitioning is enabled, the iframe should be able
// to aquire 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');
t.add_cleanup(() => {
w.close()
for(let i = 1; i < next_lock_id; i++){
held.get(i)();
}
});
}
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.
// 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(
`./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(
`./resources/partitioned-parent.html?target=${encodeURIComponent(middle_url)}`,
HTTPS_ORIGIN + self.location.pathname);
// Request the weblock for the top-level site.
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));
// 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);
// 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');
t.add_cleanup(() => {
w.close()
for(let i = 1; i < next_lock_id_2; i++){
held_2.get(i)();
}
});
}
promise_test(t => {
return nested_iframe_test(t);
}, 'WebLocks of a nested iframe with a cross-site ancestor are partitioned');
</script>
</body>
|