tor-browser

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

browser_contentOrigins.js (6505B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 const { PromptTestUtils } = ChromeUtils.importESModule(
      7  "resource://testing-common/PromptTestUtils.sys.mjs"
      8 );
      9 
     10 let { HttpServer } = ChromeUtils.importESModule(
     11  "resource://testing-common/httpd.sys.mjs"
     12 );
     13 
     14 const TEST_ROOT = getRootDirectory(gTestPath).replace(
     15  "chrome://mochitests/content",
     16  "https://example.com"
     17 );
     18 
     19 const DEFAULT_FAVICON = "chrome://global/skin/icons/defaultFavicon.svg";
     20 const BROKEN_FAVICON = "chrome://global/skin/icons/security-broken.svg";
     21 
     22 async function checkAlert(
     23  pageToLoad,
     24  expectedTitle,
     25  expectedIcon = DEFAULT_FAVICON
     26 ) {
     27  function openFn(browser) {
     28    return SpecialPowers.spawn(browser, [], () => {
     29      if (content.document.nodePrincipal.isSystemPrincipal) {
     30        // Can't eval in privileged contexts due to CSP, just call directly:
     31        content.alert("Test");
     32      } else {
     33        // Eval everywhere else so it gets the principal of the loaded page.
     34        content.eval("alert('Test')");
     35      }
     36    });
     37  }
     38  return checkDialog(pageToLoad, openFn, expectedTitle, expectedIcon);
     39 }
     40 
     41 async function checkBeforeunload(
     42  pageToLoad,
     43  expectedTitle,
     44  expectedIcon = DEFAULT_FAVICON
     45 ) {
     46  async function openFn(browser) {
     47    let tab = gBrowser.getTabForBrowser(browser);
     48    await BrowserTestUtils.synthesizeMouseAtCenter(
     49      "body",
     50      {},
     51      browser.browsingContext
     52    );
     53    return gBrowser.removeTab(tab); // trigger beforeunload.
     54  }
     55  return checkDialog(pageToLoad, openFn, expectedTitle, expectedIcon);
     56 }
     57 
     58 async function checkDialog(
     59  pageToLoad,
     60  openFn,
     61  expectedTitle,
     62  expectedIcon,
     63  modalType = Ci.nsIPrompt.MODAL_TYPE_CONTENT
     64 ) {
     65  return BrowserTestUtils.withNewTab(pageToLoad, async browser => {
     66    let promptPromise = PromptTestUtils.waitForPrompt(browser, {
     67      modalType,
     68    });
     69    let spawnPromise = openFn(browser);
     70    let dialog = await promptPromise;
     71 
     72    let doc = dialog.ui.prompt.document;
     73    let titleEl = doc.getElementById("titleText");
     74    if (expectedTitle.value) {
     75      is(titleEl.textContent, expectedTitle.value, "Title should match.");
     76    } else {
     77      is(
     78        titleEl.dataset.l10nId,
     79        expectedTitle.l10nId,
     80        "Title l10n id should match."
     81      );
     82    }
     83    ok(
     84      !titleEl.parentNode.hasAttribute("overflown"),
     85      "Title should fit without overflowing."
     86    );
     87 
     88    ok(BrowserTestUtils.isVisible(titleEl), "New title should be shown.");
     89    ok(
     90      BrowserTestUtils.isHidden(doc.getElementById("infoTitle")),
     91      "Old title should be hidden."
     92    );
     93    let iconCS = doc.ownerGlobal.getComputedStyle(
     94      doc.querySelector(".titleIcon")
     95    );
     96    Assert.stringContains(
     97      iconCS.backgroundImage,
     98      expectedIcon,
     99      "Icon is as expected."
    100    );
    101 
    102    // This is not particularly neat, but we want to also test overflow
    103    // Our test systems don't have hosts that long, so just fake it:
    104    if (browser.currentURI.asciiHost == "example.com") {
    105      let longerDomain = "extravagantly.long.".repeat(10) + "example.com";
    106      doc.documentElement.setAttribute(
    107        "headertitle",
    108        JSON.stringify({ raw: longerDomain, shouldUseMaskFade: true })
    109      );
    110      info("Wait for the prompt title to update.");
    111      await BrowserTestUtils.waitForMutationCondition(
    112        titleEl,
    113        { characterData: true, attributes: true },
    114        () =>
    115          titleEl.textContent == longerDomain &&
    116          titleEl.parentNode.hasAttribute("overflown")
    117      );
    118      is(titleEl.textContent, longerDomain, "The longer domain is reflected.");
    119      ok(
    120        titleEl.parentNode.hasAttribute("overflown"),
    121        "The domain should overflow."
    122      );
    123    }
    124 
    125    // Close the prompt again.
    126    await PromptTestUtils.handlePrompt(dialog);
    127    // The alert in the content process was sync, we need to make sure it gets
    128    // cleaned up, but couldn't await it above because that'd hang the test!
    129    await spawnPromise;
    130  });
    131 }
    132 
    133 add_task(async function test_check_prompt_origin_display() {
    134  await checkAlert("https://example.com/", { value: "example.com" });
    135  // eslint-disable-next-line @microsoft/sdl/no-insecure-url
    136  await checkAlert("http://example.com/", { value: "example.com" });
    137  await checkAlert("data:text/html,<body>", {
    138    l10nId: "common-dialog-title-null",
    139  });
    140 
    141  let homeDir = Services.dirsvc.get("Home", Ci.nsIFile);
    142  let fileURI = Services.io.newFileURI(homeDir).spec;
    143  await checkAlert(fileURI, { value: "file://" });
    144 
    145  await checkAlert(
    146    "about:config",
    147    { l10nId: "common-dialog-title-system" },
    148    "chrome://branding/content/icon32.png"
    149  );
    150 
    151  await checkBeforeunload(TEST_ROOT + "file_beforeunload_stop.html", {
    152    value: "example.com",
    153  });
    154 });
    155 
    156 add_task(async function test_check_auth() {
    157  let server = new HttpServer();
    158  registerCleanupFunction(() => {
    159    return new Promise(resolve => {
    160      server.stop(() => {
    161        server = null;
    162        resolve();
    163      });
    164    });
    165  });
    166 
    167  function forbiddenHandler(meta, res) {
    168    res.setStatusLine(meta.httpVersion, 401, "Unauthorized");
    169    res.setHeader("WWW-Authenticate", 'Basic realm="Realm"');
    170  }
    171  function pageHandler(meta, res) {
    172    res.setStatusLine(meta.httpVersion, 200, "OK");
    173    res.setHeader("Content-Type", "text/html");
    174    let body = "<html><body></body></html>";
    175    res.bodyOutputStream.write(body, body.length);
    176  }
    177  server.registerPathHandler("/forbidden", forbiddenHandler);
    178  server.registerPathHandler("/page", pageHandler);
    179  server.start(-1);
    180 
    181  const HOST = `localhost:${server.identity.primaryPort}`;
    182  // eslint-disable-next-line @microsoft/sdl/no-insecure-url
    183  const AUTH_URI = `http://${HOST}/forbidden`;
    184 
    185  // Try a simple load:
    186  // Should be broken favicon since AUTH_URI's spec is http
    187  await checkDialog(
    188    "https://example.com/",
    189    browser => BrowserTestUtils.startLoadingURIString(browser, AUTH_URI),
    190    HOST,
    191    BROKEN_FAVICON,
    192    Ci.nsIPrompt.MODAL_TYPE_TAB
    193  );
    194 
    195  let subframeLoad = function (browser, uri) {
    196    return SpecialPowers.spawn(browser, [uri], frameUri => {
    197      let f = content.document.createElement("iframe");
    198      f.src = frameUri;
    199      content.document.body.appendChild(f);
    200    });
    201  };
    202 
    203  // Try x-origin subframe:
    204  await checkDialog(
    205    // eslint-disable-next-line @microsoft/sdl/no-insecure-url
    206    "http://example.org/1",
    207    browser => subframeLoad(browser, AUTH_URI),
    208    HOST,
    209    BROKEN_FAVICON,
    210    Ci.nsIPrompt.MODAL_TYPE_TAB
    211  );
    212 });