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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
<!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>
|