summaryrefslogtreecommitdiffstats
path: root/toolkit/components/httpsonlyerror/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /toolkit/components/httpsonlyerror/tests
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/httpsonlyerror/tests')
-rw-r--r--toolkit/components/httpsonlyerror/tests/browser/browser.toml17
-rw-r--r--toolkit/components/httpsonlyerror/tests/browser/browser_errorpage.js191
-rw-r--r--toolkit/components/httpsonlyerror/tests/browser/browser_errorpage_timeout.js53
-rw-r--r--toolkit/components/httpsonlyerror/tests/browser/browser_errorpage_www_suggestion.js80
-rw-r--r--toolkit/components/httpsonlyerror/tests/browser/browser_exception.js152
-rw-r--r--toolkit/components/httpsonlyerror/tests/browser/browser_fpi_nested_uri.js46
-rw-r--r--toolkit/components/httpsonlyerror/tests/browser/file_errorpage_timeout_server.sjs15
-rw-r--r--toolkit/components/httpsonlyerror/tests/browser/file_errorpage_www_suggestion.html12
-rw-r--r--toolkit/components/httpsonlyerror/tests/browser/file_upgrade_insecure_server.sjs108
-rw-r--r--toolkit/components/httpsonlyerror/tests/browser/head.js99
10 files changed, 773 insertions, 0 deletions
diff --git a/toolkit/components/httpsonlyerror/tests/browser/browser.toml b/toolkit/components/httpsonlyerror/tests/browser/browser.toml
new file mode 100644
index 0000000000..5bbad48226
--- /dev/null
+++ b/toolkit/components/httpsonlyerror/tests/browser/browser.toml
@@ -0,0 +1,17 @@
+[DEFAULT]
+support-files = ["head.js"]
+
+["browser_errorpage.js"]
+skip-if = ["os == 'linux' && asan && !debug"] # Bug 1658616
+
+["browser_errorpage_timeout.js"]
+support-files = ["file_errorpage_timeout_server.sjs"]
+
+["browser_errorpage_www_suggestion.js"]
+support-files = ["file_errorpage_www_suggestion.html"]
+skip-if = ["os == 'android'"] # no https-only errorpage support in android
+
+["browser_exception.js"]
+support-files = ["file_upgrade_insecure_server.sjs"]
+
+["browser_fpi_nested_uri.js"]
diff --git a/toolkit/components/httpsonlyerror/tests/browser/browser_errorpage.js b/toolkit/components/httpsonlyerror/tests/browser/browser_errorpage.js
new file mode 100644
index 0000000000..43f46699c2
--- /dev/null
+++ b/toolkit/components/httpsonlyerror/tests/browser/browser_errorpage.js
@@ -0,0 +1,191 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const SECURE_PAGE = "https://example.com/";
+const GOOD_PAGE = "http://example.com/";
+const BAD_CERT = "http://expired.example.com/";
+const UNKNOWN_ISSUER = "http://self-signed.example.com/";
+
+const { TabStateFlusher } = ChromeUtils.importESModule(
+ "resource:///modules/sessionstore/TabStateFlusher.sys.mjs"
+);
+
+add_task(async function () {
+ info("Check that the error pages shows up");
+
+ await Promise.all([
+ testPageWithURI(
+ GOOD_PAGE,
+ "Should not show error page on upgradeable website.",
+ false
+ ),
+ testPageWithURI(
+ BAD_CERT,
+ "Should show error page on bad-certificate error.",
+ true
+ ),
+ testPageWithURI(
+ UNKNOWN_ISSUER,
+ "Should show error page on unkown-issuer error.",
+ true
+ ),
+ ]);
+});
+
+add_task(async function () {
+ info("Check that the go-back button returns to previous page");
+
+ // Test with and without being in an iFrame
+ for (let useFrame of [false, true]) {
+ let tab = await openErrorPage(BAD_CERT, useFrame);
+ let browser = tab.linkedBrowser;
+
+ is(
+ browser.webNavigation.canGoBack,
+ false,
+ "!webNavigation.canGoBack should be false."
+ );
+ is(
+ browser.webNavigation.canGoForward,
+ false,
+ "webNavigation.canGoForward should be false."
+ );
+
+ // Populate the shistory entries manually, since it happens asynchronously
+ // and the following tests will be too soon otherwise.
+ await TabStateFlusher.flush(browser);
+ let { entries } = JSON.parse(SessionStore.getTabState(tab));
+ is(entries.length, 1, "There should be 1 shistory entry.");
+
+ let bc = browser.browsingContext;
+ if (useFrame) {
+ bc = bc.children[0];
+ }
+
+ if (useFrame) {
+ await SpecialPowers.spawn(bc, [], async function () {
+ let returnButton = content.document.getElementById("goBack");
+ is(
+ returnButton,
+ null,
+ "Return-button should not be present in iFrame."
+ );
+ });
+ } else {
+ let locationChangePromise = BrowserTestUtils.waitForLocationChange(
+ gBrowser,
+ "about:home"
+ );
+ await SpecialPowers.spawn(bc, [], async function () {
+ let returnButton = content.document.getElementById("goBack");
+ is(
+ returnButton.getAttribute("autofocus"),
+ "true",
+ "Return-button should have focus."
+ );
+ returnButton.click();
+ });
+
+ await locationChangePromise;
+
+ is(browser.webNavigation.canGoBack, true, "webNavigation.canGoBack");
+ is(
+ browser.webNavigation.canGoForward,
+ false,
+ "!webNavigation.canGoForward"
+ );
+ is(gBrowser.currentURI.spec, "about:home", "Went back");
+ }
+
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ }
+});
+
+add_task(async function () {
+ info("Check that the go-back button returns to about:home");
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, SECURE_PAGE);
+ let browser = gBrowser.selectedBrowser;
+
+ let errorPageLoaded = BrowserTestUtils.waitForErrorPage(browser);
+ BrowserTestUtils.startLoadingURIString(browser, BAD_CERT);
+ await errorPageLoaded;
+
+ is(
+ browser.webNavigation.canGoBack,
+ true,
+ "webNavigation.canGoBack should be true before navigation."
+ );
+ is(
+ browser.webNavigation.canGoForward,
+ false,
+ "webNavigation.canGoForward should be false before navigation."
+ );
+
+ // Populate the shistory entries manually, since it happens asynchronously
+ // and the following tests will be too soon otherwise.
+ await TabStateFlusher.flush(browser);
+ let { entries } = JSON.parse(SessionStore.getTabState(tab));
+ is(entries.length, 2, "There should be 1 shistory entries.");
+
+ let pageShownPromise = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "pageshow",
+ true
+ );
+
+ // Click on "go back" Button
+ await SpecialPowers.spawn(browser, [], async function () {
+ let returnButton = content.document.getElementById("goBack");
+ returnButton.click();
+ });
+ await pageShownPromise;
+
+ is(
+ browser.webNavigation.canGoBack,
+ false,
+ "webNavigation.canGoBack should be false after navigation."
+ );
+ is(
+ browser.webNavigation.canGoForward,
+ true,
+ "webNavigation.canGoForward should be true after navigation."
+ );
+ is(
+ gBrowser.currentURI.spec,
+ SECURE_PAGE,
+ "Should go back to previous page after button click."
+ );
+
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
+// Utils
+
+async function testPageWithURI(uri, message, expect) {
+ // Open new Tab with URI
+ let tab;
+ if (expect) {
+ tab = await openErrorPage(uri, false);
+ } else {
+ tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, uri, true);
+ }
+
+ // Check if HTTPS-Only Error-Page loaded instead
+ let browser = tab.linkedBrowser;
+ await SpecialPowers.spawn(
+ browser,
+ [message, expect],
+ function (message, expect) {
+ const doc = content.document;
+ let result = doc.documentURI.startsWith("about:httpsonlyerror");
+ is(result, expect, message);
+ }
+ );
+
+ // Close tab again
+ BrowserTestUtils.removeTab(tab);
+}
diff --git a/toolkit/components/httpsonlyerror/tests/browser/browser_errorpage_timeout.js b/toolkit/components/httpsonlyerror/tests/browser/browser_errorpage_timeout.js
new file mode 100644
index 0000000000..853d93694a
--- /dev/null
+++ b/toolkit/components/httpsonlyerror/tests/browser/browser_errorpage_timeout.js
@@ -0,0 +1,53 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// We need to request longer timeout because HTTPS-Only Mode sends the
+// backround http request with a delay of N milliseconds before the
+// actual load gets cancelled.
+requestLongerTimeout(5);
+
+const TEST_PATH_HTTP = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "http://example.com"
+);
+const TEST_PATH_HTTPS = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+const TIMEOUT_PAGE_URI_HTTP =
+ TEST_PATH_HTTP + "file_errorpage_timeout_server.sjs";
+const TIMEOUT_PAGE_URI_HTTPS =
+ TEST_PATH_HTTPS + "file_errorpage_timeout_server.sjs";
+
+add_task(async function avoid_timeout_and_show_https_only_error_page() {
+ await BrowserTestUtils.withNewTab("about:blank", async function (browser) {
+ let loaded = BrowserTestUtils.browserLoaded(
+ browser,
+ false, // includeSubFrames = false, no need to includeSubFrames
+ TIMEOUT_PAGE_URI_HTTPS, // Wait for upgraded page to timeout
+ true // maybeErrorPage = true, because we need the error page to appear
+ );
+ BrowserTestUtils.startLoadingURIString(browser, TIMEOUT_PAGE_URI_HTTP);
+ await loaded;
+
+ await SpecialPowers.spawn(browser, [], async function () {
+ const doc = content.document;
+ let errorPage = doc.body.innerHTML;
+ // It's possible that fluent has not been translated when running in
+ // chaos mode, hence let's rather use an element id for verification
+ // that the https-only mode error page has loaded.
+ ok(
+ errorPage.includes("about-httpsonly-button-continue-to-site"),
+ "Potential time-out in https-only mode should cause error page to appear!"
+ );
+ // Verify that the right title is set.
+ ok(
+ errorPage.includes("about-httpsonly-title-site-not-available"),
+ "Potential time-out in https-only mode should cause error page to appear with right title!"
+ );
+ });
+ });
+});
diff --git a/toolkit/components/httpsonlyerror/tests/browser/browser_errorpage_www_suggestion.js b/toolkit/components/httpsonlyerror/tests/browser/browser_errorpage_www_suggestion.js
new file mode 100644
index 0000000000..7e3eac9817
--- /dev/null
+++ b/toolkit/components/httpsonlyerror/tests/browser/browser_errorpage_www_suggestion.js
@@ -0,0 +1,80 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+requestLongerTimeout(2);
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ ""
+);
+const HTML_PATH = "/file_errorpage_www_suggestion.html";
+const KICK_OF_REQUEST_WITH_SUGGESTION =
+ "http://suggestion-example.com" + TEST_PATH + HTML_PATH;
+
+add_task(async function () {
+ info("Check that the www button shows up and leads to a secure www page");
+
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.security.https_only_mode", true],
+ ["dom.security.https_only_mode_send_http_background_request", false],
+ ["dom.security.https_only_mode_error_page_user_suggestions", true],
+ ],
+ });
+
+ let browser = gBrowser.selectedBrowser;
+ let errorPageLoaded = BrowserTestUtils.waitForErrorPage(browser);
+ BrowserTestUtils.startLoadingURIString(
+ browser,
+ KICK_OF_REQUEST_WITH_SUGGESTION
+ );
+ await errorPageLoaded;
+
+ let pageShownPromise = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "pageshow",
+ true
+ );
+
+ // There's an arbitrary interval of 2 seconds in which the background
+ // request for the www page is made. we wait this out to ensure the
+ // www button has shown up.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(c => setTimeout(c, 2000));
+
+ await SpecialPowers.spawn(browser, [], async function () {
+ let doc = content.document;
+ let innerHTML = doc.body.innerHTML;
+ let errorPageL10nId = "about-httpsonly-title-alert";
+ let suggestionBoxL10nId = "about-httpsonly-suggestion-box-www-text";
+
+ ok(innerHTML.includes(errorPageL10nId), "the error page should show up");
+ ok(doc.documentURI.startsWith("about:httpsonlyerror"));
+ ok(
+ innerHTML.includes(suggestionBoxL10nId),
+ "the suggestion box should show up"
+ );
+
+ // click on www button
+ let wwwButton = content.document.getElementById("openWWW");
+ Assert.notStrictEqual(wwwButton, null, "The www Button should be shown");
+
+ if (!wwwButton) {
+ ok(false, "We should not be here");
+ } else {
+ wwwButton.click();
+ }
+ });
+ await pageShownPromise;
+ await SpecialPowers.spawn(browser, [], async function () {
+ let doc = content.document;
+ let innerHTML = doc.body.innerHTML;
+ ok(
+ innerHTML.includes("You are now on the secure www. page"),
+ "The secure page should be reached after clicking the button"
+ );
+ ok(doc.documentURI.startsWith("https://www."), "Page should be secure www");
+ });
+});
diff --git a/toolkit/components/httpsonlyerror/tests/browser/browser_exception.js b/toolkit/components/httpsonlyerror/tests/browser/browser_exception.js
new file mode 100644
index 0000000000..7cf98b467f
--- /dev/null
+++ b/toolkit/components/httpsonlyerror/tests/browser/browser_exception.js
@@ -0,0 +1,152 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const ROOT_PATH = getRootDirectory(gTestPath);
+const EXPIRED_ROOT_PATH = ROOT_PATH.replace(
+ "chrome://mochitests/content",
+ "http://supports-insecure.expired.example.com"
+);
+const SECURE_ROOT_PATH = ROOT_PATH.replace(
+ "chrome://mochitests/content",
+ "http://example.com"
+);
+const INSECURE_ROOT_PATH = ROOT_PATH.replace(
+ "chrome://mochitests/content",
+ "http://example.com"
+);
+
+// This is how this test works:
+//
+// +----[REQUEST] https://file_upgrade_insecure_server.sjs?queryresult
+// |
+// | +-[REQUEST] http://file_upgrade_insecure_server.sjs?content
+// | | -> Internal HTTPS redirect
+// | |
+// | +>[RESPONSE] Expired Certificate Response
+// | -> HTTPS-Only Mode Error Page shows up
+// | -> Click exception button
+// |
+// | +-[REQUEST] http://file_upgrade_insecure_server.sjs?content
+// | |
+// | +>[RESPONSE] Webpage with a bunch of sub-resources
+// | -> http://file_upgrade_insecure_ser^er.sjs?img
+// | -> http://file_upgrade_insecure_server.sjs?xhr
+// | -> http://file_upgrade_insecure_server.sjs?iframe
+// | -> etc.
+// |
+// +--->[RESPONSE] List of all recorded requests and whether they were loaded
+// with HTTP or not (eg.: img-ok, xhr-ok, iframe-error, ...)
+
+add_task(async function () {
+ const testCases = ["default", "private", "firstpartyisolation"];
+ for (let i = 0; i < testCases.length; i++) {
+ // Call sjs-file with setup query-string and store promise
+ let expectedQueries = new Set([
+ "content",
+ "img",
+ "iframe",
+ "xhr",
+ "nestedimg",
+ ]);
+
+ const filesLoaded = setupFileServer();
+ // Since we don't know when the server has saved all it's variables,
+ // let's wait a bit before reloading the page.
+ await new Promise(resolve => executeSoon(resolve));
+
+ // Create a new private window but reuse the normal one.
+ let privateWindow = false;
+ if (testCases[i] === "private") {
+ privateWindow = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ } else if (testCases[i] === "firstpartyisolation") {
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.firstparty.isolate", true]],
+ });
+ }
+
+ // Create new tab with sjs-file requesting content.
+ // "supports-insecure.expired.example.com" responds to http and https but
+ // with an expired certificate
+ let tab = await openErrorPage(
+ `${EXPIRED_ROOT_PATH}file_upgrade_insecure_server.sjs?content`,
+ false,
+ privateWindow
+ );
+ let browser = tab.linkedBrowser;
+
+ let pageShownPromise = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "pageshow",
+ true
+ );
+
+ await waitForAndClickOpenInsecureButton(browser);
+
+ await pageShownPromise;
+
+ // Check if the original page got loaded with http this time
+ await SpecialPowers.spawn(browser, [], async function () {
+ let doc = content.document;
+ ok(
+ !doc.documentURI.startsWith("http://expired.example.com"),
+ "Page should load normally after exception button was clicked."
+ );
+ });
+
+ // Wait for initial sjs request to resolve
+ let results = await filesLoaded;
+
+ for (let resultIndex in results) {
+ const response = results[resultIndex];
+ // A response looks either like this "iframe-ok" or "[key]-[result]"
+ const [key, result] = response.split("-", 2);
+ // try to find the expected result within the results array
+ if (expectedQueries.has(key)) {
+ expectedQueries.delete(key);
+ is(result, "ok", `Request '${key}' should be loaded with HTTP.'`);
+ } else {
+ ok(false, `Unexpected response from server (${response})`);
+ }
+ }
+
+ // Clean up permissions, tab and potentially preferences
+ Services.perms.removeAll();
+
+ if (testCases[i] === "firstpartyisolation") {
+ await SpecialPowers.popPrefEnv();
+ }
+
+ if (privateWindow) {
+ await BrowserTestUtils.closeWindow(privateWindow);
+ } else {
+ gBrowser.removeCurrentTab();
+ }
+ }
+});
+
+function setupFileServer() {
+ // We initialize the upgrade-server with the queryresult query-string.
+ // We'll get a response once all files have been requested and then
+ // can see if they have been requested with http.
+ return new Promise((resolve, reject) => {
+ var xhrRequest = new XMLHttpRequest();
+ xhrRequest.open(
+ "GET",
+ `${SECURE_ROOT_PATH}file_upgrade_insecure_server.sjs?queryresult=${INSECURE_ROOT_PATH}`
+ );
+ xhrRequest.onload = function (e) {
+ var results = xhrRequest.responseText.split(",");
+ resolve(results);
+ };
+ xhrRequest.onerror = e => {
+ ok(false, "Could not query results from server (" + e.message + ")");
+ reject();
+ };
+ xhrRequest.send();
+ });
+}
diff --git a/toolkit/components/httpsonlyerror/tests/browser/browser_fpi_nested_uri.js b/toolkit/components/httpsonlyerror/tests/browser/browser_fpi_nested_uri.js
new file mode 100644
index 0000000000..45bf718f3f
--- /dev/null
+++ b/toolkit/components/httpsonlyerror/tests/browser/browser_fpi_nested_uri.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that a nested URI (in this case `view-source:`) does not result
+// in a redirect loop when HTTPS-Only and First Party Isolation are
+// enabled (Bug 1855734).
+
+const INSECURE_VIEW_SOURCE_URL = "view-source:http://123.123.123.123/";
+
+function promiseIsErrorPage() {
+ return new Promise(resolve => {
+ BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser).then(() =>
+ resolve(true)
+ );
+ BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() =>
+ resolve(false)
+ );
+ });
+}
+
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.security.https_only_mode", true],
+ ["dom.security.https_only_mode.upgrade_local", true],
+ ["privacy.firstparty.isolate", true],
+ ],
+ });
+
+ let loaded = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
+ info(`Starting to load ${INSECURE_VIEW_SOURCE_URL}`);
+ BrowserTestUtils.startLoadingURIString(gBrowser, INSECURE_VIEW_SOURCE_URL);
+ await loaded;
+ info(`${INSECURE_VIEW_SOURCE_URL} finished loading`);
+
+ loaded = promiseIsErrorPage();
+ await waitForAndClickOpenInsecureButton(gBrowser.selectedBrowser);
+ info(`Waiting for normal or error page to load`);
+ const isErrorPage = await loaded;
+
+ ok(!isErrorPage, "We should not land on an error page");
+
+ await Services.perms.removeAll();
+});
diff --git a/toolkit/components/httpsonlyerror/tests/browser/file_errorpage_timeout_server.sjs b/toolkit/components/httpsonlyerror/tests/browser/file_errorpage_timeout_server.sjs
new file mode 100644
index 0000000000..f77d5351b5
--- /dev/null
+++ b/toolkit/components/httpsonlyerror/tests/browser/file_errorpage_timeout_server.sjs
@@ -0,0 +1,15 @@
+// Custom *.sjs file specifically for the needs of Bug 1657348
+
+function handleRequest(request, response) {
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ if (request.scheme === "https") {
+ // Simulating a timeout by processing the https request
+ // async and *never* return anything!
+ response.processAsync();
+ return;
+ }
+ // we should never get here; just in case, return something unexpected
+ response.write("do'h");
+}
diff --git a/toolkit/components/httpsonlyerror/tests/browser/file_errorpage_www_suggestion.html b/toolkit/components/httpsonlyerror/tests/browser/file_errorpage_www_suggestion.html
new file mode 100644
index 0000000000..9d5c42ca73
--- /dev/null
+++ b/toolkit/components/httpsonlyerror/tests/browser/file_errorpage_www_suggestion.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1665057 - Add www button on https-only error page</title>
+</head>
+<body>
+ <div>
+ You are now on the secure www. page
+ </div>
+</body>
+</html>
diff --git a/toolkit/components/httpsonlyerror/tests/browser/file_upgrade_insecure_server.sjs b/toolkit/components/httpsonlyerror/tests/browser/file_upgrade_insecure_server.sjs
new file mode 100644
index 0000000000..e05ea7b3a3
--- /dev/null
+++ b/toolkit/components/httpsonlyerror/tests/browser/file_upgrade_insecure_server.sjs
@@ -0,0 +1,108 @@
+// Serverside Javascript for browser_exception.js
+// Bug 1625156 - Error page for HTTPS Only Mode
+
+const expectedQueries = ["content", "img", "iframe", "xhr", "nestedimg"];
+const TOTAL_EXPECTED_REQUESTS = expectedQueries.length;
+
+const CONTENT = path => `
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ </head>
+ <body>
+ <p>Insecure website</p>
+ <script type="application/javascript">
+ var myXHR = new XMLHttpRequest();
+ myXHR.open("GET", "${path}file_upgrade_insecure_server.sjs?xhr");
+ myXHR.send(null);
+ </script>
+ <img src='${path}file_upgrade_insecure_server.sjs?img'></img>
+ <iframe src="${path}file_upgrade_insecure_server.sjs?iframe"></iframe>
+ </body>
+</html>`;
+
+const IFRAME_CONTENT = path => `
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ </head>
+ <body>
+ <p>Nested insecure website</p>
+ <img src='${path}file_upgrade_insecure_server.sjs?nestedimg'></img>
+ </body>
+</html>`;
+
+function handleRequest(request, response) {
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+ var queryString = request.queryString;
+
+ // initialize server variables and save the object state
+ // of the initial request, which returns async once the
+ // server has processed all requests.
+ if (queryString.startsWith("queryresult")) {
+ response.processAsync();
+ setState("totaltests", TOTAL_EXPECTED_REQUESTS.toString());
+ setState("receivedQueries", "");
+ setState("rootPath", /=(.+)/.exec(queryString)[1]);
+ setObjectState("queryResult", response);
+ return;
+ }
+
+ // just in case error handling for unexpected queries
+ if (!expectedQueries.includes(queryString)) {
+ response.write("unexpected-response");
+ return;
+ }
+
+ // make sure all the requested queries are indeed http
+ const testResult =
+ queryString + (request.scheme == "http" ? "-ok" : "-error");
+
+ var receivedQueries = getState("receivedQueries");
+
+ // images, scripts, etc. get queried twice, do not
+ // confuse the server by storing the preload as
+ // well as the actual load. If either the preload
+ // or the actual load is not https, then we would
+ // append "-error" in the array and the test would
+ // fail at the end.
+ if (receivedQueries.includes(testResult)) {
+ return;
+ }
+
+ // append the result to the total query string array
+ if (receivedQueries != "") {
+ receivedQueries += ",";
+ }
+ receivedQueries += testResult;
+ setState("receivedQueries", receivedQueries);
+
+ // keep track of how many more requests the server
+ // is expecting
+ var totaltests = parseInt(getState("totaltests"));
+ totaltests -= 1;
+ setState("totaltests", totaltests.toString());
+
+ // Respond with html content
+ if (queryString == "content") {
+ response.write(CONTENT(getState("rootPath")));
+ } else if (queryString == "iframe") {
+ response.write(IFRAME_CONTENT(getState("rootPath")));
+ }
+
+ // if we have received all the requests, we return
+ // the result back.
+ if (totaltests == 0) {
+ getObjectState("queryResult", function (queryResponse) {
+ if (!queryResponse) {
+ return;
+ }
+ var receivedQueries = getState("receivedQueries");
+ queryResponse.write(receivedQueries);
+ queryResponse.finish();
+ });
+ }
+}
diff --git a/toolkit/components/httpsonlyerror/tests/browser/head.js b/toolkit/components/httpsonlyerror/tests/browser/head.js
new file mode 100644
index 0000000000..7efbfbe8b5
--- /dev/null
+++ b/toolkit/components/httpsonlyerror/tests/browser/head.js
@@ -0,0 +1,99 @@
+// Enable HTTPS-Only Mode
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.security.https_only_mode", true]],
+ });
+});
+
+// Copied from: https://searchfox.org/mozilla-central/rev/9f074fab9bf905fad62e7cc32faf121195f4ba46/browser/base/content/test/about/head.js
+
+async function injectErrorPageFrame(tab, src, sandboxed) {
+ let loadedPromise = BrowserTestUtils.browserLoaded(
+ tab.linkedBrowser,
+ true,
+ null,
+ true
+ );
+
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [src, sandboxed],
+ async function (frameSrc, frameSandboxed) {
+ let iframe = content.document.createElement("iframe");
+ iframe.src = frameSrc;
+ if (frameSandboxed) {
+ iframe.setAttribute("sandbox", "allow-scripts");
+ }
+ content.document.body.appendChild(iframe);
+ }
+ );
+
+ await loadedPromise;
+}
+
+async function openErrorPage(src, useFrame, privateWindow, sandboxed) {
+ let gb = gBrowser;
+ if (privateWindow) {
+ gb = privateWindow.gBrowser;
+ }
+ let dummyPage =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "dummy_page.html";
+
+ let tab;
+ if (useFrame) {
+ info("Loading error page in an iframe");
+ tab = await BrowserTestUtils.openNewForegroundTab(gb, dummyPage);
+ await injectErrorPageFrame(tab, src, sandboxed);
+ } else {
+ let ErrorPageLoaded;
+ tab = await BrowserTestUtils.openNewForegroundTab(
+ gb,
+ () => {
+ gb.selectedTab = BrowserTestUtils.addTab(gb, src);
+ let browser = gb.selectedBrowser;
+ ErrorPageLoaded = BrowserTestUtils.waitForErrorPage(browser);
+ },
+ false
+ );
+ info("Loading and waiting for the error page");
+ await ErrorPageLoaded;
+ }
+
+ return tab;
+}
+
+/**
+ * On a loaded HTTPS-Only error page, waits until the "Open Insecure"
+ * button gets enabled and then presses it.
+ *
+ * @returns {Promise<void>}
+ */
+function waitForAndClickOpenInsecureButton(browser) {
+ return SpecialPowers.spawn(browser, [], async function () {
+ let openInsecureButton = content.document.getElementById("openInsecure");
+ Assert.notEqual(
+ openInsecureButton,
+ null,
+ "openInsecureButton should exist."
+ );
+ info("Waiting for openInsecureButton to be enabled.");
+ function callback() {
+ if (!openInsecureButton.inert) {
+ info("openInsecureButton was enabled, waiting two frames.");
+ observer.disconnect();
+ content.requestAnimationFrame(() => {
+ content.requestAnimationFrame(() => {
+ info("clicking openInsecureButton.");
+ openInsecureButton.click();
+ });
+ });
+ }
+ }
+ const observer = new content.MutationObserver(callback);
+ observer.observe(openInsecureButton, { attributeFilter: ["inert"] });
+ callback();
+ });
+}