Commit 4068383d authored by lyavor's avatar lyavor
Browse files

Bug 1745146 - Add a test for https-first download from mixed content site. r=ckerschb

parent 2cb41f23
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -12,3 +12,8 @@ support-files = file_downgrade_view_source.sjs
support-files = file_navigation.html
[browser_httpsfirst_speculative_connect.js]
support-files = file_httpsfirst_speculative_connect.html
[browser_mixed_content_download.js]
skip-if = win10_2004 && debug # Bug 1723573
support-files =
  download_page.html
  download_server.sjs
+159 −0
Original line number Diff line number Diff line
ChromeUtils.defineModuleGetter(
  this,
  "Downloads",
  "resource://gre/modules/Downloads.jsm"
);
ChromeUtils.defineModuleGetter(
  this,
  "DownloadsCommon",
  "resource:///modules/DownloadsCommon.jsm"
);

const HandlerService = Cc[
  "@mozilla.org/uriloader/handler-service;1"
].getService(Ci.nsIHandlerService);

const MIMEService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);

let SECURE_BASE_URL =
  getRootDirectory(gTestPath).replace(
    "chrome://mochitests/content/",
    "https://example.com/"
  ) + "download_page.html";

/**
 * Waits until a download is triggered.
 * It waits until a prompt is shown,
 * saves and then accepts the dialog.
 * @returns {Promise} Resolved once done.
 */

function shouldTriggerDownload() {
  return new Promise((resolve, reject) => {
    Services.wm.addListener({
      onOpenWindow(xulWin) {
        Services.wm.removeListener(this);
        let win = xulWin.docShell.domWindow;
        waitForFocus(() => {
          if (
            win.location ==
            "chrome://mozapps/content/downloads/unknownContentType.xhtml"
          ) {
            let dialog = win.document.getElementById("unknownContentType");
            let button = dialog.getButton("accept");
            let actionRadio = win.document.getElementById("save");
            actionRadio.click();
            button.disabled = false;
            dialog.acceptDialog();
            resolve();
          } else {
            reject();
          }
        }, win);
      },
    });
  });
}

const CONSOLE_UPGRADE_MESSAGE = "Upgrading insecure request";
const CONSOLE_DOWNGRADE_MESSAGE = "Downgrading to “http” again.";
const DOWNLOAD_URL =
  "example.com/browser/dom/security/test/https-first/download_server.sjs";
// Verifies that https-first tries to upgrade download,
// falls back since download is not available via https
let msgCounter = 0;
function shouldConsoleError() {
  return new Promise((resolve, reject) => {
    function listener(msgObj) {
      let text = msgObj.message;
      if (text.includes(CONSOLE_UPGRADE_MESSAGE) && msgCounter == 0) {
        ok(
          text.includes("http://" + DOWNLOAD_URL),
          "Https-first tries to upgrade download to https"
        );
        msgCounter++;
      }
      if (text.includes(CONSOLE_DOWNGRADE_MESSAGE) && msgCounter == 1) {
        ok(
          text.includes("https://" + DOWNLOAD_URL),
          "Https-first downgrades download to http."
        );
        resolve();
      }
    }
    Services.console.registerListener(listener);
  });
}

async function resetDownloads() {
  // Removes all downloads from the download List
  const types = new Set();
  let publicList = await Downloads.getList(Downloads.PUBLIC);
  let downloads = await publicList.getAll();
  for (let download of downloads) {
    if (download.contentType) {
      types.add(download.contentType);
    }
    publicList.remove(download);
    await download.finalize(true);
  }

  if (types.size) {
    // reset handlers for the contentTypes of any files previously downloaded
    for (let type of types) {
      const mimeInfo = MIMEService.getFromTypeAndExtension(type, "");
      info("resetting handler for type: " + type);
      HandlerService.remove(mimeInfo);
    }
  }
}

async function runTest(url, link, checkFunction, description) {
  await SpecialPowers.pushPrefEnv({
    set: [
      ["dom.security.https_first", true],
      ["browser.download.improvements_to_download_panel", false],
    ],
  });
  requestLongerTimeout(2);
  await resetDownloads();

  let tab = BrowserTestUtils.addTab(gBrowser, url);
  gBrowser.selectedTab = tab;

  let browser = gBrowser.getBrowserForTab(tab);
  await BrowserTestUtils.browserLoaded(browser);
  is(
    gBrowser.currentURI.schemeIs("https"),
    true,
    "Scheme of opened tab should be https"
  );
  info("Checking: " + description);

  let checkPromise = checkFunction();
  // Click the Link to trigger the download
  SpecialPowers.spawn(gBrowser.selectedBrowser, [link], contentLink => {
    content.document.getElementById(contentLink).click();
  });
  await checkPromise;
  ok(true, description);
  BrowserTestUtils.removeTab(tab);

  await SpecialPowers.popPrefEnv();
}

//Test description:
// 1. Open "https://example.com"
// 2. From "https://example.com" download something, but that download is only available via http.
// 3. Https-first tries to upgrade the download.
// 4. Upgrading fails - so http-first downgrade download to http.

add_task(async function test_mixed_download() {
  await runTest(
    SECURE_BASE_URL,
    "insecure",
    () => Promise.all([shouldTriggerDownload(), shouldConsoleError()]),
    "Secure -> Insecure should Error"
  );
  await SpecialPowers.popPrefEnv();
});
+22 −0
Original line number Diff line number Diff line
<!DOCTYPE HTML>
<html>
  <head>
    <title>Test mixed content download by https-first</title>
  </head>
  <body>
    hi

    <script>
      const host = window.location.host;
      const path = location.pathname.replace("download_page.html","download_server.sjs");

      const insecureLink = document.createElement("a");
      insecureLink.href=`http://${host}${path}`;
      insecureLink.download="true";
      insecureLink.id="insecure";
      insecureLink.textContent="Not secure Link";

      document.body.append(insecureLink);
  </script>
  </body>
</html>
+16 −0
Original line number Diff line number Diff line
function handleRequest(request, response) {
  // Only answer to http, in case request is in https let the reply time out.
  if (request.scheme === "https") {
    response.processAsync();
    return;
  }

  response.setHeader("Cache-Control", "no-cache", false);
  // Send some file, e.g. an image
  response.setHeader(
    "Content-Disposition",
    "attachment; filename=some-file.png"
  );
  response.setHeader("Content-Type", "image/png");
  response.write("Success!");
}