tor-browser

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

browser_ManifestObtainer_obtain.js (8232B)


      1 "use strict";
      2 
      3 add_setup(async function () {
      4  await SpecialPowers.pushPrefEnv({
      5    set: [
      6      ["dom.manifest.enabled", true],
      7      ["dom.security.https_first", false],
      8    ],
      9  });
     10 });
     11 
     12 const { ManifestObtainer } = ChromeUtils.importESModule(
     13  "resource://gre/modules/ManifestObtainer.sys.mjs"
     14 );
     15 const remoteURL =
     16  "http://mochi.test:8888/browser/dom/manifest/test/resource.sjs";
     17 const defaultURL = new URL(
     18  "http://example.org/browser/dom/manifest/test/resource.sjs"
     19 );
     20 defaultURL.searchParams.set("Content-Type", "text/html; charset=utf-8");
     21 requestLongerTimeout(4);
     22 
     23 const tests = [
     24  // Fetch tests.
     25  {
     26    body: `<!-- no manifest in document -->`,
     27    run(manifest) {
     28      is(manifest, null, "Manifest without a href yields a null manifest.");
     29    },
     30  },
     31  {
     32    body: `<link rel="manifest">`,
     33    run(manifest) {
     34      is(manifest, null, "Manifest without a href yields a null manifest.");
     35    },
     36  },
     37  {
     38    body: `
     39      <link rel="manifesto" href='resource.sjs?body={"name":"fail"}'>
     40      <link rel="foo bar manifest bar test" href='resource.sjs?body={"name":"pass-1"}'>
     41      <link rel="manifest" href='resource.sjs?body={"name":"fail"}'>`,
     42    run(manifest) {
     43      is(
     44        manifest.name,
     45        "pass-1",
     46        "Manifest is first `link` where @rel contains token manifest."
     47      );
     48    },
     49  },
     50  {
     51    body: `
     52      <link rel="foo bar manifest bar test" href='resource.sjs?body={"name":"pass-2"}'>
     53      <link rel="manifest" href='resource.sjs?body={"name":"fail"}'>
     54      <link rel="manifest foo bar test" href='resource.sjs?body={"name":"fail"}'>`,
     55    run(manifest) {
     56      is(
     57        manifest.name,
     58        "pass-2",
     59        "Manifest is first `link` where @rel contains token manifest."
     60      );
     61    },
     62  },
     63  {
     64    body: `<link rel="manifest" href='${remoteURL}?body={"name":"pass-3"}'>`,
     65    run(err) {
     66      is(
     67        err.name,
     68        "TypeError",
     69        "By default, manifest cannot load cross-origin."
     70      );
     71    },
     72  },
     73  // CORS Tests.
     74  {
     75    get body() {
     76      const body = 'body={"name": "pass-4"}';
     77      const CORS = `Access-Control-Allow-Origin=${defaultURL.origin}`;
     78      const link = `<link
     79        crossorigin=anonymous
     80        rel="manifest"
     81        href='${remoteURL}?${body}&${CORS}'>`;
     82      return link;
     83    },
     84    run(manifest) {
     85      is(manifest.name, "pass-4", "CORS enabled, manifest must be fetched.");
     86    },
     87  },
     88  {
     89    get body() {
     90      const body = 'body={"name": "fail"}';
     91      const CORS = "Access-Control-Allow-Origin=http://not-here";
     92      const link = `<link
     93        crossorigin
     94        rel="manifest"
     95        href='${remoteURL}?${body}&${CORS}'>`;
     96      return link;
     97    },
     98    run(err) {
     99      is(
    100        err.name,
    101        "TypeError",
    102        "Fetch blocked by CORS - origin does not match."
    103      );
    104    },
    105  },
    106  {
    107    body: `<link rel="manifest" href='about:whatever'>`,
    108    run(err) {
    109      is(
    110        err.name,
    111        "TypeError",
    112        "Trying to load from about:whatever is TypeError."
    113      );
    114    },
    115  },
    116  {
    117    body: `<link rel="manifest" href='file://manifest'>`,
    118    run(err) {
    119      is(
    120        err.name,
    121        "TypeError",
    122        "Trying to load from file://whatever is a TypeError."
    123      );
    124    },
    125  },
    126  // URL parsing tests
    127  {
    128    body: `<link rel="manifest" href='http://[12.1212.21.21.12.21.12]'>`,
    129    run(err) {
    130      is(err.name, "TypeError", "Trying to load invalid URL is a TypeError.");
    131    },
    132  },
    133 ];
    134 
    135 function makeTestURL({ body }) {
    136  const url = new URL(defaultURL);
    137  url.searchParams.set("body", encodeURIComponent(body));
    138  return url.href;
    139 }
    140 
    141 add_task(async function () {
    142  const promises = tests
    143    .map(test => ({
    144      gBrowser,
    145      testRunner: testObtainingManifest(test),
    146      url: makeTestURL(test),
    147    }))
    148    .reduce((collector, tabOpts) => {
    149      const promise = BrowserTestUtils.withNewTab(tabOpts, tabOpts.testRunner);
    150      collector.push(promise);
    151      return collector;
    152    }, []);
    153 
    154  await Promise.all(promises);
    155 
    156  function testObtainingManifest(aTest) {
    157    return async function (aBrowser) {
    158      try {
    159        const manifest = await ManifestObtainer.browserObtainManifest(aBrowser);
    160        aTest.run(manifest);
    161      } catch (e) {
    162        aTest.run(e);
    163      }
    164    };
    165  }
    166 });
    167 
    168 add_task(async () => {
    169  // This loads a generic html page.
    170  const url = new URL(defaultURL);
    171  // The body get injected into the page on the server.
    172  const body = `<link rel="manifest" href='resource.sjs?body={"name": "conformance check"}'>`;
    173  url.searchParams.set("body", encodeURIComponent(body));
    174 
    175  // Let's open a tab!
    176  const tabOpts = {
    177    gBrowser,
    178    url: url.href,
    179  };
    180  // Let's do the test
    181  await BrowserTestUtils.withNewTab(tabOpts, async aBrowser => {
    182    const obtainerOpts = {
    183      checkConformance: true, // gives us back "moz_manifest_url" member
    184    };
    185    const manifest = await ManifestObtainer.browserObtainManifest(
    186      aBrowser,
    187      obtainerOpts
    188    );
    189    is(manifest.name, "conformance check");
    190    ok("moz_manifest_url" in manifest, "Has a moz_manifest_url member");
    191    const testString = defaultURL.origin + defaultURL.pathname;
    192    ok(
    193      manifest.moz_manifest_url.startsWith(testString),
    194      `Expect to start with with the testString, but got ${manifest.moz_manifest_url} instead,`
    195    );
    196    // Clean up!
    197    gBrowser.removeTab(gBrowser.getTabForBrowser(aBrowser));
    198  });
    199 });
    200 
    201 /*
    202 * e10s race condition tests
    203 * Open a bunch of tabs and load manifests
    204 * in each tab. They should all return pass.
    205 */
    206 add_task(async function () {
    207  const defaultPath = "/browser/dom/manifest/test/manifestLoader.html";
    208  const tabURLs = [
    209    `http://example.com:80${defaultPath}`,
    210    `http://example.org:80${defaultPath}`,
    211    `http://example.org:8000${defaultPath}`,
    212    `http://mochi.test:8888${defaultPath}`,
    213    `http://sub1.test1.example.com:80${defaultPath}`,
    214    `http://sub1.test1.example.org:80${defaultPath}`,
    215    `http://sub1.test1.example.org:8000${defaultPath}`,
    216    `http://sub1.test1.mochi.test:8888${defaultPath}`,
    217    `http://sub1.test2.example.com:80${defaultPath}`,
    218    `http://sub1.test2.example.org:80${defaultPath}`,
    219    `http://sub1.test2.example.org:8000${defaultPath}`,
    220    `http://sub2.test1.example.com:80${defaultPath}`,
    221    `http://sub2.test1.example.org:80${defaultPath}`,
    222    `http://sub2.test1.example.org:8000${defaultPath}`,
    223    `http://sub2.test2.example.com:80${defaultPath}`,
    224    `http://sub2.test2.example.org:80${defaultPath}`,
    225    `http://sub2.test2.example.org:8000${defaultPath}`,
    226    `http://sub2.xn--lt-uia.mochi.test:8888${defaultPath}`,
    227    `http://test1.example.com:80${defaultPath}`,
    228    `http://test1.example.org:80${defaultPath}`,
    229    `http://test1.example.org:8000${defaultPath}`,
    230    `http://test1.mochi.test:8888${defaultPath}`,
    231    `http://test2.example.com:80${defaultPath}`,
    232    `http://test2.example.org:80${defaultPath}`,
    233    `http://test2.example.org:8000${defaultPath}`,
    234    `http://test2.mochi.test:8888${defaultPath}`,
    235    `http://test:80${defaultPath}`,
    236    `http://www.example.com:80${defaultPath}`,
    237  ];
    238  // Open tabs an collect corresponding browsers
    239  let browsers = tabURLs.map(
    240    url => BrowserTestUtils.addTab(gBrowser, url).linkedBrowser
    241  );
    242 
    243  // Once all the pages have loaded, run a bunch of tests in "parallel".
    244  await Promise.all(
    245    (function* () {
    246      for (let browser of browsers) {
    247        yield BrowserTestUtils.browserLoaded(browser);
    248      }
    249    })()
    250  );
    251  // Flood random browsers with requests. Once promises settle, check that
    252  // responses all pass.
    253  const results = await Promise.all(
    254    (function* () {
    255      for (let browser of randBrowsers(browsers, 50)) {
    256        yield ManifestObtainer.browserObtainManifest(browser);
    257      }
    258    })()
    259  );
    260  const pass = results.every(manifest => manifest.name === "pass");
    261  ok(pass, "Expect every manifest to have name equal to `pass`.");
    262  // cleanup
    263  browsers
    264    .map(browser => gBrowser.getTabForBrowser(browser))
    265    .forEach(tab => gBrowser.removeTab(tab));
    266 
    267  // Helper generator, spits out random browsers
    268  function* randBrowsers(aBrowsers, aMax) {
    269    for (let i = 0; i < aMax; i++) {
    270      const randNum = Math.round(Math.random() * (aBrowsers.length - 1));
    271      yield aBrowsers[randNum];
    272    }
    273  }
    274 });