tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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>