partitioned-web-locks.tentative.https.html (6537B)
1 <!DOCTYPE html> 2 <meta charset="utf-8"/> 3 <title>Web Locks API: Partitioned WebLocks</title> 4 5 <!-- Pull in get_host_info() --> 6 <script src="/common/get-host-info.sub.js"></script> 7 <script src="/common/utils.js"></script> 8 <script src="/common/dispatcher/dispatcher.js"></script> 9 <script src="resources/helpers.js"></script> 10 <script src="/resources/testharness.js"></script> 11 <script src="/resources/testharnessreport.js"></script> 12 <body> 13 <script> 14 15 const { HTTPS_ORIGIN, HTTPS_NOTSAMESITE_ORIGIN } = get_host_info(); 16 // Map of lock_id => function that releases a lock. 17 const held = new Map(); 18 let next_lock_id = 1; 19 20 // How this test works: 21 // Step 1 (top-frame): request an exclusive web-lock and store its id 22 // and release for clean-up. 23 // Step 2 (top-frame): open a pop-up window and load a not-same-site 24 // ./web-locks/resources/partitioned-parent.html 25 // Step 3 (pop-up): load a same-site iframe inside the pop-up. 26 // Step 4 (pop-up): send a web-lock request to the same-site iframe. 27 // Step 5 (iframe): process the web-lock request and message the result 28 // back to the pop-up. 29 // Step 6 (pop-up): intercept the result message from the iframe and 30 // send it to the top-frame. 31 // Step 7 (top-frame): add cleanup hook. 32 // Step 8 (top-frame): ensure that the same-site iframe's web-lock 33 // request succeeds since it and the top-level site are successfully 34 // partitioned and each can hold an exclusive lock. 35 36 async function third_party_test(t) { 37 let target_url = HTTPS_ORIGIN + '/web-locks/resources/iframe.html'; 38 target_url = new URL( 39 `/web-locks/resources/partitioned-parent.html?target=${encodeURIComponent(target_url)}`, 40 HTTPS_NOTSAMESITE_ORIGIN + self.location.pathname); 41 42 // Step 1. 43 let lock_id = next_lock_id++; 44 let [ promise, release ] = makePromiseAndResolveFunc(); 45 let released = navigator.locks.request('testLock', {mode: 'exclusive', ifAvailable: true}, 46 lock => { 47 if (lock === null) { 48 assert_true(false) 49 return; 50 } 51 return promise; 52 }); 53 held.set(lock_id, { release, released }); 54 55 // Step 2. 56 const w = window.open(target_url); 57 const result = await new Promise(resolve => window.onmessage = resolve); 58 59 // Step 7. 60 t.add_cleanup(() => { 61 w.close(); 62 let released = []; 63 for(let i = 1; i < next_lock_id; i++){ 64 let h = held.get(i); 65 h.release(); 66 released.push(h.released); 67 } 68 return Promise.allSettled(released); 69 }); 70 71 // Step 8. 72 // When 3rd party storage partitioning is enabled, the iframe should be able 73 // to acquire a lock with the same name as one exclusively held by the opener 74 // of its top window, even when that opener has the same origin. 75 assert_equals(result.data.failed, undefined, 76 'The 1p iframe failed to acquire the lock'); 77 } 78 79 promise_test(t => { 80 return third_party_test(t); 81 }, 'WebLocks of an iframe under a 3rd-party site are partitioned'); 82 83 84 // Optional Test: Checking for partitioned web locks in an A->B->A 85 // (nested-iframe with cross-site ancestor chain) scenario. 86 // 87 // How this test works: 88 // Nested Step 1 (top frame): request an exclusive web-lock and 89 // store its id and release for clean-up. 90 // Nested Step 2 (top frame): open a pop-up window and load a 91 // same-site /web-locks/resources/partitioned-parent.html. 92 // Nested Step 3 (pop-up): load a not-same-site "parent" iframe (A->B) 93 // (/web-locks/resources/iframe-parent.html) inside the pop-up. 94 // Nested Step 4 (pop-up): send a web-lock request to the parent iframe. 95 // Nested Step 5 (parent iframe): load a "child" iframe (A->B->A) 96 // (/web-locks/resources/iframe.html) that is same-site with the 97 // pop-up inside the "parent" iframe. 98 // Nested Step 6 (parent iframe): pass on the web-lock request message to 99 // the "child" iframe. 100 // Nested Step 7 (child iframe): process the web-lock request and message 101 // the result to the parent iframe. 102 // Nested Step 8 (parent iframe): intercept the result message from the 103 // child iframe and send it to the pop-up. 104 // Nested Step 9 (pop-up): intercept the result message from the parent 105 // iframe and send it to the top frame. 106 // Nested Step 10 (top frame): add cleanup hook 107 // Nested Step 11 (top frame): ensure that the same-site iframe's web-lock 108 // request succeeds since it and the top-level are successfully 109 // partitioned and each can hold an exclusive lock. 110 111 // Map of lock_id => function that releases a lock. 112 const held_2 = new Map(); 113 let next_lock_id_2 = 1; 114 115 async function nested_iframe_test(t) { 116 // Create innermost child iframe (leaf). 117 let leaf_url = HTTPS_ORIGIN + '/web-locks/resources/iframe.html'; 118 // Wrap the child iframe in its cross-origin parent (middle). 119 let middle_url = new URL( 120 `/web-locks/resources/iframe-parent.html?target=${encodeURIComponent(leaf_url)}`, 121 HTTPS_NOTSAMESITE_ORIGIN + self.location.pathname); 122 // Embed the parent iframe in the top-level site (top). 123 let top_url = new URL( 124 `/web-locks/resources/partitioned-parent.html?target=${encodeURIComponent(middle_url)}`, 125 HTTPS_ORIGIN + self.location.pathname); 126 127 // Nested Step 1. 128 // Request the weblock for the top-level site. 129 let lock_id = next_lock_id_2++; 130 let [ promise, release ] = makePromiseAndResolveFunc(); 131 let released = navigator.locks.request('testLock', {mode: 'exclusive', ifAvailable: true}, 132 lock => { 133 if (lock === null) { 134 assert_true(false) 135 return; 136 } 137 return promise; 138 }).catch(error => alert(error.message)); 139 held_2.set(lock_id, { release, released }); 140 141 // Nested Step 2. 142 // Open the nested iframes. The script in the innermost child iframe 143 // will attempt to obtain the same weblock as above. 144 const w = window.open(top_url); 145 const result = await new Promise(resolve => window.onmessage = resolve); 146 147 // Nested Step 10. 148 t.add_cleanup(() => { 149 w.close(); 150 let released = []; 151 for(let i = 1; i < next_lock_id; i++){ 152 let h = held_2.get(i); 153 h.release(); 154 released.push(h.released); 155 } 156 return Promise.allSettled(released); 157 }); 158 159 // Nested Step 11. 160 // With third-party storage partitioning enabled, the same-site iframe 161 // should be able to acquire the lock as it has a cross-site ancestor 162 // and is partitioned separately from the top-level site. 163 assert_equals(result.data.failed, undefined, 164 'The 1p iframe failed to acquire the lock'); 165 } 166 167 promise_test(t => { 168 return nested_iframe_test(t); 169 }, 'WebLocks of a nested iframe with a cross-site ancestor are partitioned'); 170 </script> 171 </body>