tor-browser

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

popup-test.js (3824B)


      1 // To use the functions below, be sure to include the following files in your
      2 // test:
      3 // - "/common/get-host-info.sub.js" to get the different origin values.
      4 // - "common.js" to have the origins easily available.
      5 // - "/common/dispatcher/dispatcher.js" for cross-origin messaging.
      6 // - "/common/utils.js" for token().
      7 
      8 function getExecutorPath(uuid, origin, headers) {
      9  const executor_path = '/common/dispatcher/executor.html?';
     10  const coop_header = headers.coop ?
     11    `|header(Cross-Origin-Opener-Policy,${encodeURIComponent(headers.coop)})` : '';
     12  const coep_header = headers.coep ?
     13    `|header(Cross-Origin-Embedder-Policy,${encodeURIComponent(headers.coep)})` : '';
     14  return origin +
     15         executor_path +
     16         `uuid=${uuid}` +
     17         '&pipe=' + coop_header + coep_header;
     18 }
     19 
     20 function getPopupHasOpener(popup_token) {
     21  const reply_token = token();
     22  send(popup_token, `send('${reply_token}', window.opener != null);`);
     23  return receive(reply_token);
     24 }
     25 
     26 // Return true if |object|.|property| can be called without throwing an error.
     27 function canAccessProperty(object, property) {
     28  try {
     29    const unused = object[property];
     30    return true;
     31  } catch (errors) {
     32    return false;
     33  }
     34 }
     35 
     36 // Verifies that a popup with origin `origin` and headers `headers` has
     37 // the expected `opener_state` after being opened.
     38 async function popup_test(description, origin, headers, expected_opener_state) {
     39  promise_test(async t => {
     40    const popup_token = token();
     41    const reply_token = token();
     42 
     43    const popup_url = getExecutorPath(
     44      popup_token,
     45      origin.origin,
     46      headers);
     47 
     48    // We open popup and then ping it, it will respond after loading.
     49    const popup = window.open(popup_url);
     50    send(popup_token, `send('${reply_token}', 'Popup loaded');`);
     51    assert_equals(await receive(reply_token), 'Popup loaded');
     52 
     53    // Make sure the popup will be closed once the test has run, keeping a clean
     54    // state.
     55    t.add_cleanup(() => {
     56      send(popup_token, `close()`);
     57    });
     58 
     59    // Give some time for things to settle across processes etc. before
     60    // proceeding with verifications.
     61    await new Promise(resolve => { t.step_timeout(resolve, 500); });
     62 
     63    // Verify that the opener is in the state we expect it to be in.
     64    switch (expected_opener_state) {
     65      case 'preserved': {
     66        assert_false(popup.closed, 'Popup is closed from opener?');
     67        assert_true(await getPopupHasOpener(popup_token) === "true",
     68                    'Popup has nulled opener?');
     69        assert_equals(canAccessProperty(popup, "document"),
     70                      origin === SAME_ORIGIN,
     71                      'Main page has dom access to the popup?');
     72        assert_true(canAccessProperty(popup, "frames"),
     73                    'Main page has cross origin access to the popup?');
     74        break;
     75      }
     76      case 'restricted': {
     77        assert_false(popup.closed, 'Popup is closed from opener?');
     78        assert_true(await getPopupHasOpener(popup_token) === "true",
     79                    'Popup has nulled opener?');
     80        assert_false(canAccessProperty(popup, "document"),
     81                     'Main page has dom access to the popup?');
     82        assert_false(canAccessProperty(popup, "frames"),
     83                    'Main page has cross origin access to the popup?');
     84        assert_true(canAccessProperty(popup, "closed"),
     85                    'Main page has limited cross origin access to the popup?');
     86        break;
     87      }
     88      case 'severed': {
     89        assert_true(popup.closed, 'Popup is closed from opener?');
     90        assert_false(await getPopupHasOpener(popup_token) === "true",
     91                     'Popup has nulled opener?');
     92        break;
     93      }
     94      default:
     95        assert_unreached(true, "Unrecognized opener relationship: " + expected_opener_state);
     96    }
     97  }, description);
     98 }