summaryrefslogtreecommitdiffstats
path: root/dom/security/test/mixedcontentblocker
diff options
context:
space:
mode:
Diffstat (limited to 'dom/security/test/mixedcontentblocker')
-rw-r--r--dom/security/test/mixedcontentblocker/auto_upgrading_identity.html11
-rw-r--r--dom/security/test/mixedcontentblocker/auto_upgrading_identity.pngbin0 -> 70 bytes
-rw-r--r--dom/security/test/mixedcontentblocker/browser.toml35
-rw-r--r--dom/security/test/mixedcontentblocker/browser_auto_upgrading_identity.js58
-rw-r--r--dom/security/test/mixedcontentblocker/browser_csp_block_all_mixedcontent_and_mixed_content_display_upgrade.js171
-rw-r--r--dom/security/test/mixedcontentblocker/browser_mixed_content_auth_download.js169
-rw-r--r--dom/security/test/mixedcontentblocker/browser_mixed_content_auto_upgrade_display_console.js54
-rw-r--r--dom/security/test/mixedcontentblocker/browser_test_mixed_content_download.js341
-rw-r--r--dom/security/test/mixedcontentblocker/download_page.html41
-rw-r--r--dom/security/test/mixedcontentblocker/download_server.sjs20
-rw-r--r--dom/security/test/mixedcontentblocker/file_auth_download_page.html22
-rw-r--r--dom/security/test/mixedcontentblocker/file_auth_download_server.sjs61
-rw-r--r--dom/security/test/mixedcontentblocker/file_bug1550792.html39
-rw-r--r--dom/security/test/mixedcontentblocker/file_bug1551886.html25
-rw-r--r--dom/security/test/mixedcontentblocker/file_bug803225_test_mailto.html13
-rw-r--r--dom/security/test/mixedcontentblocker/file_csp_block_all_mixedcontent_and_mixed_content_display_upgrade.html14
-rw-r--r--dom/security/test/mixedcontentblocker/file_frameNavigation.html72
-rw-r--r--dom/security/test/mixedcontentblocker/file_frameNavigation_blankTarget.html33
-rw-r--r--dom/security/test/mixedcontentblocker/file_frameNavigation_grandchild.html55
-rw-r--r--dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html74
-rw-r--r--dom/security/test/mixedcontentblocker/file_frameNavigation_secure.html72
-rw-r--r--dom/security/test/mixedcontentblocker/file_frameNavigation_secure_grandchild.html56
-rw-r--r--dom/security/test/mixedcontentblocker/file_main.html336
-rw-r--r--dom/security/test/mixedcontentblocker/file_main_bug803225.html175
-rw-r--r--dom/security/test/mixedcontentblocker/file_main_bug803225_websocket_wsh.py6
-rw-r--r--dom/security/test/mixedcontentblocker/file_mixed_content_auto_upgrade_display_console.html10
-rw-r--r--dom/security/test/mixedcontentblocker/file_redirect.html31
-rw-r--r--dom/security/test/mixedcontentblocker/file_redirect_handler.sjs34
-rw-r--r--dom/security/test/mixedcontentblocker/file_server.sjs131
-rw-r--r--dom/security/test/mixedcontentblocker/file_windowOpen.html9
-rw-r--r--dom/security/test/mixedcontentblocker/mochitest.toml66
-rw-r--r--dom/security/test/mixedcontentblocker/pass.pngbin0 -> 1689 bytes
-rw-r--r--dom/security/test/mixedcontentblocker/test.ogvbin0 -> 2344665 bytes
-rw-r--r--dom/security/test/mixedcontentblocker/test.wavbin0 -> 353022 bytes
-rw-r--r--dom/security/test/mixedcontentblocker/test_bug1550792.html35
-rw-r--r--dom/security/test/mixedcontentblocker/test_bug1551886.html33
-rw-r--r--dom/security/test/mixedcontentblocker/test_bug803225.html157
-rw-r--r--dom/security/test/mixedcontentblocker/test_frameNavigation.html127
-rw-r--r--dom/security/test/mixedcontentblocker/test_main.html231
-rw-r--r--dom/security/test/mixedcontentblocker/test_redirect.html45
-rw-r--r--dom/security/test/mixedcontentblocker/test_windowOpen.html82
41 files changed, 2944 insertions, 0 deletions
diff --git a/dom/security/test/mixedcontentblocker/auto_upgrading_identity.html b/dom/security/test/mixedcontentblocker/auto_upgrading_identity.html
new file mode 100644
index 0000000000..d843b7fae1
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/auto_upgrading_identity.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset='utf-8'>
+ <title>Bug 1674341: Test SiteIdentity when auto-upgrading mixed content</title>
+</head>
+<body>
+ <!-- needs to be http: image for mixed content and auto-upgrading -->
+ <img type="image/png" id="testimage" src="http://example.com/browser/dom/security/test/mixedcontentblocker/auto_upgrading_identity.png" />
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/auto_upgrading_identity.png b/dom/security/test/mixedcontentblocker/auto_upgrading_identity.png
new file mode 100644
index 0000000000..52c591798e
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/auto_upgrading_identity.png
Binary files differ
diff --git a/dom/security/test/mixedcontentblocker/browser.toml b/dom/security/test/mixedcontentblocker/browser.toml
new file mode 100644
index 0000000000..5b0b85cb0b
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/browser.toml
@@ -0,0 +1,35 @@
+[DEFAULT]
+prefs = ["dom.security.https_first=false"]
+support-files = [
+ "download_page.html",
+ "download_server.sjs",
+]
+
+["browser_auto_upgrading_identity.js"]
+support-files = [
+ "auto_upgrading_identity.html",
+ "auto_upgrading_identity.png",
+]
+
+["browser_csp_block_all_mixedcontent_and_mixed_content_display_upgrade.js"]
+support-files = [
+ "file_csp_block_all_mixedcontent_and_mixed_content_display_upgrade.html",
+ "pass.png",
+ "test.ogv",
+ "test.wav",
+]
+
+["browser_mixed_content_auth_download.js"]
+support-files = [
+ "file_auth_download_page.html",
+ "file_auth_download_server.sjs",
+]
+
+["browser_mixed_content_auto_upgrade_display_console.js"]
+support-files = ["file_mixed_content_auto_upgrade_display_console.html"]
+
+["browser_test_mixed_content_download.js"]
+skip-if = [
+ "win11_2009", # Bug 1784764
+ "os == 'linux' && !debug", # Bug 1784764
+]
diff --git a/dom/security/test/mixedcontentblocker/browser_auto_upgrading_identity.js b/dom/security/test/mixedcontentblocker/browser_auto_upgrading_identity.js
new file mode 100644
index 0000000000..6e467b8d30
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/browser_auto_upgrading_identity.js
@@ -0,0 +1,58 @@
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+const TEST_TOPLEVEL_URI = TEST_PATH + "auto_upgrading_identity.html";
+
+// auto upgrading mixed content should not indicate passive mixed content loaded
+add_task(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["security.mixed_content.upgrade_display_content", true],
+ ["security.mixed_content.upgrade_display_content.image", true],
+ ],
+ });
+ await BrowserTestUtils.withNewTab(
+ TEST_TOPLEVEL_URI,
+ async function (browser) {
+ await ContentTask.spawn(browser, {}, async function () {
+ let testImg = content.document.getElementById("testimage");
+ ok(
+ testImg.src.includes("auto_upgrading_identity.png"),
+ "sanity: correct image is loaded"
+ );
+ });
+ // Ensure the identiy handler does not show mixed content!
+ ok(
+ !gIdentityHandler._isMixedPassiveContentLoaded,
+ "Auto-Upgrading Mixed Content: Identity should note indicate mixed content"
+ );
+ }
+ );
+});
+
+// regular mixed content test should indicate passive mixed content loaded
+add_task(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.mixed_content.upgrade_display_content", false]],
+ });
+ await BrowserTestUtils.withNewTab(
+ TEST_TOPLEVEL_URI,
+ async function (browser) {
+ await ContentTask.spawn(browser, {}, async function () {
+ let testImg = content.document.getElementById("testimage");
+ ok(
+ testImg.src.includes("auto_upgrading_identity.png"),
+ "sanity: correct image is loaded"
+ );
+ });
+ // Ensure the identiy handler does show mixed content!
+ ok(
+ gIdentityHandler._isMixedPassiveContentLoaded,
+ "Regular Mixed Content: Identity should indicate mixed content"
+ );
+ }
+ );
+});
diff --git a/dom/security/test/mixedcontentblocker/browser_csp_block_all_mixedcontent_and_mixed_content_display_upgrade.js b/dom/security/test/mixedcontentblocker/browser_csp_block_all_mixedcontent_and_mixed_content_display_upgrade.js
new file mode 100644
index 0000000000..6e130d16e0
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/browser_csp_block_all_mixedcontent_and_mixed_content_display_upgrade.js
@@ -0,0 +1,171 @@
+/*
+ * Description of the Test:
+ * We load an https page which uses a CSP including block-all-mixed-content.
+ * The page embedded an audio, img and video. ML2 should upgrade them and
+ * CSP should not be triggered.
+ */
+
+const PRE_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+let gExpectedMessages = 0;
+let gExpectBlockAllMsg = false;
+
+function onConsoleMessage({ message }) {
+ // Check if csp warns about block-all-mixed content being obsolete
+ if (message.includes("Content-Security-Policy")) {
+ if (gExpectBlockAllMsg) {
+ ok(
+ message.includes("block-all-mixed-content obsolete"),
+ "CSP warns about block-all-mixed content being obsolete"
+ );
+ } else {
+ ok(
+ message.includes("Blocking insecure request"),
+ "CSP error about blocking insecure request"
+ );
+ }
+ }
+ if (message.includes("Mixed Content:")) {
+ ok(
+ message.includes("Upgrading insecure display request"),
+ "msg included a mixed content upgrade"
+ );
+ gExpectedMessages--;
+ }
+}
+
+async function checkLoadedElements() {
+ let url =
+ PRE_PATH +
+ "file_csp_block_all_mixedcontent_and_mixed_content_display_upgrade.html";
+ return BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url,
+ waitForLoad: true,
+ },
+ async function (browser) {
+ return ContentTask.spawn(browser, [], async function () {
+ console.log(content.document.innerHTML);
+
+ // Check image loaded
+ let image = content.document.getElementById("some-img");
+ let imageLoaded = image && image.complete && image.naturalHeight !== 0;
+ // Check audio loaded
+ let audio = content.document.getElementById("some-audio");
+ let audioLoaded = audio && audio.readyState >= 2;
+ // Check video loaded
+ let video = content.document.getElementById("some-video");
+ //let videoPlayable = await once(video, "loadeddata").then(_ => true);
+ let videoLoaded = video && video.readyState === 4;
+ return { audio: audioLoaded, img: imageLoaded, video: videoLoaded };
+ });
+ }
+ );
+}
+
+add_task(async function test_upgrade_all() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["security.mixed_content.upgrade_display_content", true],
+ // Not enabled by default outside Nightly.
+ ["security.mixed_content.upgrade_display_content.image", true],
+ ],
+ });
+ Services.console.registerListener(onConsoleMessage);
+
+ // Starting the test
+ gExpectedMessages = 3;
+ gExpectBlockAllMsg = true;
+
+ let loadedElements = await checkLoadedElements();
+ is(loadedElements.img, true, "Image loaded and was upgraded");
+ is(loadedElements.video, true, "Video loaded and was upgraded");
+ is(loadedElements.audio, true, "Audio loaded and was upgraded");
+
+ await BrowserTestUtils.waitForCondition(() => gExpectedMessages === 0);
+
+ // Clean up
+ Services.console.unregisterListener(onConsoleMessage);
+ SpecialPowers.popPrefEnv();
+});
+
+add_task(async function test_dont_upgrade_image() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["security.mixed_content.upgrade_display_content", true],
+ ["security.mixed_content.upgrade_display_content.image", false],
+ ],
+ });
+ Services.console.registerListener(onConsoleMessage);
+
+ // Starting the test
+ gExpectedMessages = 2;
+ gExpectBlockAllMsg = false;
+
+ let loadedElements = await checkLoadedElements();
+ is(loadedElements.img, false, "Image was not loaded");
+ is(loadedElements.video, true, "Video loaded and was upgraded");
+ is(loadedElements.audio, true, "Audio loaded and was upgraded");
+
+ await BrowserTestUtils.waitForCondition(() => gExpectedMessages === 0);
+
+ // Clean up
+ Services.console.unregisterListener(onConsoleMessage);
+ SpecialPowers.popPrefEnv();
+});
+
+add_task(async function test_dont_upgrade_audio() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["security.mixed_content.upgrade_display_content", true],
+ ["security.mixed_content.upgrade_display_content.image", true],
+ ["security.mixed_content.upgrade_display_content.audio", false],
+ ],
+ });
+ Services.console.registerListener(onConsoleMessage);
+
+ // Starting the test
+ gExpectedMessages = 2;
+ gExpectBlockAllMsg = false;
+
+ let loadedElements = await checkLoadedElements();
+ is(loadedElements.img, true, "Image loaded and was upgraded");
+ is(loadedElements.video, true, "Video loaded and was upgraded");
+ is(loadedElements.audio, false, "Audio was not loaded");
+
+ await BrowserTestUtils.waitForCondition(() => gExpectedMessages === 0);
+
+ // Clean up
+ Services.console.unregisterListener(onConsoleMessage);
+ SpecialPowers.popPrefEnv();
+});
+
+add_task(async function test_dont_upgrade_video() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["security.mixed_content.upgrade_display_content", true],
+ ["security.mixed_content.upgrade_display_content.image", true],
+ ["security.mixed_content.upgrade_display_content.video", false],
+ ],
+ });
+ Services.console.registerListener(onConsoleMessage);
+
+ // Starting the test
+ gExpectedMessages = 2;
+ gExpectBlockAllMsg = false;
+
+ let loadedElements = await checkLoadedElements();
+ is(loadedElements.img, true, "Image loaded and was upgraded");
+ is(loadedElements.video, false, "Video was not loaded");
+ is(loadedElements.audio, true, "Audio loaded and was upgraded");
+
+ await BrowserTestUtils.waitForCondition(() => gExpectedMessages === 0);
+
+ // Clean up
+ Services.console.unregisterListener(onConsoleMessage);
+ SpecialPowers.popPrefEnv();
+});
diff --git a/dom/security/test/mixedcontentblocker/browser_mixed_content_auth_download.js b/dom/security/test/mixedcontentblocker/browser_mixed_content_auth_download.js
new file mode 100644
index 0000000000..25fee8de3c
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/browser_mixed_content_auth_download.js
@@ -0,0 +1,169 @@
+/* Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ Downloads: "resource://gre/modules/Downloads.sys.mjs",
+ DownloadsCommon: "resource:///modules/DownloadsCommon.sys.mjs",
+});
+
+const { PromptTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PromptTestUtils.sys.mjs"
+);
+
+let authPromptModalType = Services.prefs.getIntPref(
+ "prompts.modalType.httpAuth"
+);
+
+const downloadMonitoringView = {
+ _listeners: [],
+ onDownloadAdded(download) {
+ for (let listener of this._listeners) {
+ listener(download);
+ }
+ this._listeners = [];
+ },
+ waitForDownload(listener) {
+ this._listeners.push(listener);
+ },
+};
+
+let SECURE_BASE_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content/",
+ "https://example.com/"
+ ) + "file_auth_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(res => {
+ downloadMonitoringView.waitForDownload(res);
+ });
+}
+function shouldNotifyDownloadUI() {
+ return new Promise(res => {
+ downloadMonitoringView.waitForDownload(async aDownload => {
+ let { error } = aDownload;
+ if (
+ error.becauseBlockedByReputationCheck &&
+ error.reputationCheckVerdict == Downloads.Error.BLOCK_VERDICT_INSECURE
+ ) {
+ // It's an insecure Download, now Check that it has been cleaned up properly
+ if ((await IOUtils.stat(aDownload.target.path)).size != 0) {
+ throw new Error(`Download target is not empty!`);
+ }
+ if ((await IOUtils.stat(aDownload.target.path)).size != 0) {
+ throw new Error(`Download partFile was not cleaned up properly`);
+ }
+ // Assert that the Referrer is present
+ if (!aDownload.source.referrerInfo) {
+ throw new Error("The Blocked download is missing the ReferrerInfo");
+ }
+
+ res(aDownload);
+ } else {
+ ok(false, "No error for download that was expected to error!");
+ }
+ });
+ });
+}
+
+async function resetDownloads() {
+ // Removes all downloads from the download List
+ const types = new Set();
+ let publicList = await Downloads.getList(Downloads.ALL);
+ let downloads = await publicList.getAll();
+ for (let download of downloads) {
+ if (download.contentType) {
+ types.add(download.contentType);
+ }
+ publicList.remove(download);
+ await download.finalize(true);
+ }
+}
+
+async function runTest(url, link, checkFunction, description) {
+ await resetDownloads();
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+ 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();
+ });
+ // Wait for the auth prompt, enter the login details and close the prompt
+ await PromptTestUtils.handleNextPrompt(
+ gBrowser.selectedBrowser,
+ { modalType: authPromptModalType, promptType: "promptUserAndPass" },
+ { buttonNumClick: 0, loginInput: "user", passwordInput: "pass" }
+ );
+ await checkPromise;
+ ok(true, description);
+ // Close download panel
+ DownloadsPanel.hidePanel();
+ is(DownloadsPanel.panel.state, "closed", "Panel should be closed");
+ await BrowserTestUtils.removeTab(tab);
+}
+
+add_setup(async function () {
+ let list = await Downloads.getList(Downloads.ALL);
+ list.addView(downloadMonitoringView);
+ registerCleanupFunction(() => list.removeView(downloadMonitoringView));
+ // Ensure to delete all cached credentials before running test
+ await new Promise(resolve => {
+ Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, resolve);
+ });
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.block_download_insecure", true]],
+ });
+});
+//Test description:
+// 1. Open "https://example.com".
+// 2. From "https://example.com" download something, but that download is only available via http
+// and with authentication.
+// 3. Login and start download.
+// 4. Mixed-content blocker blocks download.
+// 5. Unblock download and verify the downloaded file.
+add_task(async function test_auth_download() {
+ await runTest(
+ SECURE_BASE_URL,
+ "insecure",
+ async () => {
+ let [, download] = await Promise.all([
+ shouldTriggerDownload(),
+ shouldNotifyDownloadUI(),
+ ]);
+ await download.unblock();
+ Assert.equal(
+ download.error,
+ null,
+ "There should be no error after unblocking"
+ );
+ info(
+ "Start download to be able to validate the size and the success of the download"
+ );
+ await download.start();
+ is(
+ download.contentType,
+ "text/html",
+ "File contentType should be correct."
+ );
+ ok(download.succeeded, "Download succeeded!");
+ is(download.target.size, 27, "Download has correct size");
+ },
+ "A locked Download from an auth server should succeeded to Download after a Manual unblock"
+ );
+});
diff --git a/dom/security/test/mixedcontentblocker/browser_mixed_content_auto_upgrade_display_console.js b/dom/security/test/mixedcontentblocker/browser_mixed_content_auto_upgrade_display_console.js
new file mode 100644
index 0000000000..033467dd1c
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/browser_mixed_content_auto_upgrade_display_console.js
@@ -0,0 +1,54 @@
+// Bug 1673574 - Improve Console logging for mixed content auto upgrading
+"use strict";
+
+const testPath = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+let seenAutoUpgradeMessage = false;
+
+const kTestURI =
+ testPath + "file_mixed_content_auto_upgrade_display_console.html";
+
+add_task(async function () {
+ // A longer timeout is necessary for this test than the plain mochitests
+ // due to opening a new tab with the web console.
+ requestLongerTimeout(4);
+
+ // Enable HTTPS-Only Mode and register console-listener
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["security.mixed_content.upgrade_display_content", true],
+ ["security.mixed_content.upgrade_display_content.image", true],
+ ],
+ });
+ Services.console.registerListener(on_auto_upgrade_message);
+
+ BrowserTestUtils.startLoadingURIString(gBrowser.selectedBrowser, kTestURI);
+
+ await BrowserTestUtils.waitForCondition(() => seenAutoUpgradeMessage);
+
+ Services.console.unregisterListener(on_auto_upgrade_message);
+});
+
+function on_auto_upgrade_message(msgObj) {
+ const message = msgObj.message;
+
+ // The console message is:
+ // "Mixed Content: Upgrading insecure display request
+ // ‘http://example.com/file_mixed_content_auto_upgrade_display_console.jpg’ to use ‘https’"
+
+ if (!message.includes("Mixed Content:")) {
+ return;
+ }
+ ok(
+ message.includes("Upgrading insecure display request"),
+ "msg includes info"
+ );
+ ok(
+ message.includes("file_mixed_content_auto_upgrade_display_console.jpg"),
+ "msg includes file"
+ );
+ seenAutoUpgradeMessage = true;
+}
diff --git a/dom/security/test/mixedcontentblocker/browser_test_mixed_content_download.js b/dom/security/test/mixedcontentblocker/browser_test_mixed_content_download.js
new file mode 100644
index 0000000000..ee350008aa
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/browser_test_mixed_content_download.js
@@ -0,0 +1,341 @@
+ChromeUtils.defineESModuleGetters(this, {
+ Downloads: "resource://gre/modules/Downloads.sys.mjs",
+ DownloadsCommon: "resource:///modules/DownloadsCommon.sys.mjs",
+});
+
+const HandlerService = Cc[
+ "@mozilla.org/uriloader/handler-service;1"
+].getService(Ci.nsIHandlerService);
+
+const MIMEService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
+
+let INSECURE_BASE_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content/",
+ "http://example.com/"
+ ) + "download_page.html";
+let SECURE_BASE_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content/",
+ "https://example.com/"
+ ) + "download_page.html";
+
+function promiseFocus() {
+ return new Promise(resolve => {
+ waitForFocus(resolve);
+ });
+}
+
+function promisePanelOpened() {
+ if (DownloadsPanel.panel && DownloadsPanel.panel.state == "open") {
+ return Promise.resolve();
+ }
+ return BrowserTestUtils.waitForEvent(DownloadsPanel.panel, "popupshown");
+}
+
+async function task_openPanel() {
+ await promiseFocus();
+
+ let promise = promisePanelOpened();
+ DownloadsPanel.showPanel();
+ await promise;
+}
+
+const downloadMonitoringView = {
+ _listeners: [],
+ onDownloadAdded(download) {
+ for (let listener of this._listeners) {
+ listener(download);
+ }
+ this._listeners = [];
+ },
+ waitForDownload(listener) {
+ this._listeners.push(listener);
+ },
+};
+
+/**
+ * Waits until a download is triggered.
+ * Unless the always_ask_before_handling_new_types pref is true, the download
+ * will simply be saved, so resolve when the view is notified of the new
+ * download. Otherwise, it waits until a prompt is shown, selects the choosen
+ * <action>, then accepts the dialog
+ * @param [action] Which action to select, either:
+ * "handleInternally", "save" or "open".
+ * @returns {Promise} Resolved once done.
+ */
+
+function shouldTriggerDownload(action = "save") {
+ if (
+ Services.prefs.getBoolPref(
+ "browser.download.always_ask_before_handling_new_types"
+ )
+ ) {
+ 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(action);
+ actionRadio.click();
+ button.disabled = false;
+ dialog.acceptDialog();
+ resolve();
+ } else {
+ reject();
+ }
+ }, win);
+ },
+ });
+ });
+ }
+ return new Promise(res => {
+ downloadMonitoringView.waitForDownload(res);
+ });
+}
+
+const CONSOLE_ERROR_MESSAGE = "Blocked downloading insecure content";
+
+function shouldConsoleError() {
+ // Waits until CONSOLE_ERROR_MESSAGE was logged
+ return new Promise((resolve, reject) => {
+ function listener(msgObj) {
+ let text = msgObj.message;
+ if (text.includes(CONSOLE_ERROR_MESSAGE)) {
+ Services.console.unregisterListener(listener);
+ 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);
+ }
+ }
+}
+
+function shouldNotifyDownloadUI() {
+ return new Promise(res => {
+ downloadMonitoringView.waitForDownload(async aDownload => {
+ let { error } = aDownload;
+ if (
+ error.becauseBlockedByReputationCheck &&
+ error.reputationCheckVerdict == Downloads.Error.BLOCK_VERDICT_INSECURE
+ ) {
+ // It's an insecure Download, now Check that it has been cleaned up properly
+ if ((await IOUtils.stat(aDownload.target.path)).size != 0) {
+ throw new Error(`Download target is not empty!`);
+ }
+ if ((await IOUtils.stat(aDownload.target.path)).size != 0) {
+ throw new Error(`Download partFile was not cleaned up properly`);
+ }
+ // Assert that the Referrer is presnt
+ if (!aDownload.source.referrerInfo) {
+ throw new Error("The Blocked download is missing the ReferrerInfo");
+ }
+
+ res(aDownload);
+ } else {
+ ok(false, "No error for download that was expected to error!");
+ }
+ });
+ });
+}
+
+async function runTest(url, link, checkFunction, description) {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.block_download_insecure", true]],
+ });
+ await resetDownloads();
+
+ let tab = BrowserTestUtils.addTab(gBrowser, url);
+ gBrowser.selectedTab = tab;
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ 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();
+}
+
+add_setup(async () => {
+ let list = await Downloads.getList(Downloads.ALL);
+ list.addView(downloadMonitoringView);
+ registerCleanupFunction(() => list.removeView(downloadMonitoringView));
+});
+
+// Test Blocking
+add_task(async function test_blocking() {
+ for (let prefVal of [true, false]) {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.download.always_ask_before_handling_new_types", prefVal]],
+ });
+ await runTest(
+ INSECURE_BASE_URL,
+ "insecure",
+ shouldTriggerDownload,
+ "Insecure -> Insecure should download"
+ );
+ await runTest(
+ INSECURE_BASE_URL,
+ "secure",
+ shouldTriggerDownload,
+ "Insecure -> Secure should download"
+ );
+ await runTest(
+ SECURE_BASE_URL,
+ "insecure",
+ () =>
+ Promise.all([
+ shouldTriggerDownload(),
+ shouldNotifyDownloadUI(),
+ shouldConsoleError(),
+ ]),
+ "Secure -> Insecure should Error"
+ );
+ await runTest(
+ SECURE_BASE_URL,
+ "secure",
+ shouldTriggerDownload,
+ "Secure -> Secure should Download"
+ );
+ await SpecialPowers.popPrefEnv();
+ }
+});
+
+// Test Manual Unblocking
+add_task(async function test_manual_unblocking() {
+ for (let prefVal of [true, false]) {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.download.always_ask_before_handling_new_types", prefVal]],
+ });
+ await runTest(
+ SECURE_BASE_URL,
+ "insecure",
+ async () => {
+ let [, download] = await Promise.all([
+ shouldTriggerDownload(),
+ shouldNotifyDownloadUI(),
+ ]);
+ await download.unblock();
+ Assert.equal(
+ download.error,
+ null,
+ "There should be no error after unblocking"
+ );
+ },
+ "A Blocked Download Should succeeded to Download after a Manual unblock"
+ );
+ await SpecialPowers.popPrefEnv();
+ }
+});
+
+// Test Unblock Download Visible
+add_task(async function test_unblock_download_visible() {
+ for (let prefVal of [true, false]) {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.download.always_ask_before_handling_new_types", prefVal]],
+ });
+ // Focus, open and close the panel once
+ // to make sure the panel is loaded and ready
+ await promiseFocus();
+ await runTest(
+ SECURE_BASE_URL,
+ "insecure",
+ async () => {
+ let panelHasOpened = promisePanelOpened();
+ info("awaiting that the download is triggered and added to the list");
+ await Promise.all([shouldTriggerDownload(), shouldNotifyDownloadUI()]);
+ info("awaiting that the Download list shows itself");
+ await panelHasOpened;
+ DownloadsPanel.hidePanel();
+ ok(true, "The Download Panel should have opened on blocked download");
+ },
+ "A Blocked Download Should open the Download Panel"
+ );
+ await SpecialPowers.popPrefEnv();
+ }
+});
+
+// Test Download an insecure svg and choose "Open with Firefox"
+add_task(async function download_open_insecure_SVG() {
+ const mimeInfo = MIMEService.getFromTypeAndExtension("image/svg+xml", "svg");
+ mimeInfo.alwaysAskBeforeHandling = false;
+ mimeInfo.preferredAction = mimeInfo.handleInternally;
+ HandlerService.store(mimeInfo);
+
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.download.always_ask_before_handling_new_types", false]],
+ });
+ await promiseFocus();
+ await runTest(
+ SECURE_BASE_URL,
+ "insecureSVG",
+ async () => {
+ info("awaiting that the download is triggered and added to the list");
+ let [_, download] = await Promise.all([
+ shouldTriggerDownload("handleInternally"),
+ shouldNotifyDownloadUI(),
+ ]);
+
+ let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser);
+ await download.unblock();
+ Assert.equal(
+ download.error,
+ null,
+ "There should be no error after unblocking"
+ );
+
+ let tab = await newTabPromise;
+
+ ok(
+ tab.linkedBrowser._documentURI.filePath.includes(".svg"),
+ "The download target was opened"
+ );
+ BrowserTestUtils.removeTab(tab);
+ ok(true, "The Content was opened in a new tab");
+ await SpecialPowers.popPrefEnv();
+ },
+ "A Blocked SVG can be opened internally"
+ );
+
+ HandlerService.remove(mimeInfo);
+});
diff --git a/dom/security/test/mixedcontentblocker/download_page.html b/dom/security/test/mixedcontentblocker/download_page.html
new file mode 100644
index 0000000000..86f605c478
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/download_page.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=676619
+-->
+ <head>
+
+ <title>Test for the download attribute</title>
+
+ </head>
+ <body>
+ hi
+
+ <script>
+ const host = window.location.host;
+ const path = location.pathname.replace("download_page.html","download_server.sjs");
+
+ const secureLink = document.createElement("a");
+ secureLink.href=`https://${host}/${path}`;
+ secureLink.download="true";
+ secureLink.textContent="Secure Link";
+ secureLink.id="secure";
+
+ const insecureLink = document.createElement("a");
+ insecureLink.href=`http://${host}/${path}`;
+ insecureLink.download="true";
+ insecureLink.id="insecure";
+ insecureLink.textContent="Not secure Link";
+
+ const insecureSVG = document.createElement("a");
+ insecureSVG.href=`http://${host}/${path}?type=image/svg+xml&name=example.svg`;
+ insecureSVG.download="true";
+ insecureSVG.id="insecureSVG";
+ insecureSVG.textContent="Not secure Link to SVG";
+
+ document.body.append(insecureSVG);
+ document.body.append(secureLink);
+ document.body.append(insecureLink);
+ </script>
+ </body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/download_server.sjs b/dom/security/test/mixedcontentblocker/download_server.sjs
new file mode 100644
index 0000000000..e659df2f40
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/download_server.sjs
@@ -0,0 +1,20 @@
+// force the Browser to Show a Download Prompt
+
+function handleRequest(request, response) {
+ let type = "image/png";
+ let filename = "hello.png";
+ request.queryString.split("&").forEach(val => {
+ var [key, value] = val.split("=");
+ if (key == "type") {
+ type = value;
+ }
+ if (key == "name") {
+ filename = value;
+ }
+ });
+
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Disposition", `attachment; filename=${filename}`);
+ response.setHeader("Content-Type", type);
+ response.write("🙈🙊🐵🙊");
+}
diff --git a/dom/security/test/mixedcontentblocker/file_auth_download_page.html b/dom/security/test/mixedcontentblocker/file_auth_download_page.html
new file mode 100644
index 0000000000..931d9d00ad
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_auth_download_page.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Test mixed content download with auth header by https-first</title>
+ </head>
+ <body>
+ hi
+
+ <script>
+ const host = window.location.host;
+ const path = location.pathname.replace("file_auth_download_page.html","file_auth_download_server.sjs");
+
+ const insecureLink = document.createElement("a");
+ insecureLink.href = `http://${host}${path}?user=user&pass=pass`;
+ insecureLink.download = "true";
+ insecureLink.id = "insecure";
+ insecureLink.textContent = "Not secure Link";
+
+ document.body.append(insecureLink);
+ </script>
+ </body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_auth_download_server.sjs b/dom/security/test/mixedcontentblocker/file_auth_download_server.sjs
new file mode 100644
index 0000000000..1600d4aa12
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_auth_download_server.sjs
@@ -0,0 +1,61 @@
+"use strict";
+
+function handleRequest(request, response) {
+ let match;
+
+ // Allow the caller to drive how authentication is processed via the query.
+ // Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar
+ // The extra ? allows the user/pass/realm checks to succeed if the name is
+ // at the beginning of the query string.
+ let query = new URLSearchParams(request.queryString);
+
+ let expected_user = query.get("user");
+ let expected_pass = query.get("pass");
+ let realm = query.get("realm");
+
+ // Look for an authentication header, if any, in the request.
+ //
+ // EG: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
+ //
+ // This test only supports Basic auth. The value sent by the client is
+ // "username:password", obscured with base64 encoding.
+
+ let actual_user = "",
+ actual_pass = "",
+ authHeader;
+ if (request.hasHeader("Authorization")) {
+ authHeader = request.getHeader("Authorization");
+ match = /Basic (.+)/.exec(authHeader);
+ if (match.length != 2) {
+ throw new Error("Couldn't parse auth header: " + authHeader);
+ }
+ // Decode base64 to string
+ let userpass = atob(match[1]);
+ match = /(.*):(.*)/.exec(userpass);
+ if (match.length != 3) {
+ throw new Error("Couldn't decode auth header: " + userpass);
+ }
+ actual_user = match[1];
+ actual_pass = match[2];
+ }
+
+ // Don't request authentication if the credentials we got were what we
+ // expected.
+ let requestAuth =
+ expected_user != actual_user || expected_pass != actual_pass;
+
+ if (requestAuth) {
+ response.setStatusLine("1.0", 401, "Authentication required");
+ response.setHeader("WWW-Authenticate", 'basic realm="' + realm + '"', true);
+ response.write("Authentication required");
+ } else {
+ response.setStatusLine("1.0", 200, "OK");
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader(
+ "Content-Disposition",
+ "attachment; filename=dummy-file.html"
+ );
+ response.setHeader("Content-Type", "text/html");
+ response.write("<p id='success'>SUCCESS</p>");
+ }
+}
diff --git a/dom/security/test/mixedcontentblocker/file_bug1550792.html b/dom/security/test/mixedcontentblocker/file_bug1550792.html
new file mode 100644
index 0000000000..30bdc5d5c5
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_bug1550792.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+</head>
+<body>
+ <iframe id="nestframe"></iframe>
+<script>
+ let nestframe = document.getElementById("nestframe");
+
+ window.addEventListener("message", (event) => {
+ parent.postMessage(event.data, "*");
+
+ // Only create second iframe once
+ if(event.data.type === "https") {
+ return;
+ }
+
+ nestframe.contentDocument.body.innerHTML = `
+ <iframe id="frametwo"
+ src=\"https://example.com\"
+ onload=\"parent.postMessage({status:'loaded', type: 'https'}, '*')\"
+ onerror=\"parent.postMessage({status:'blocked', type: 'https'}, '*')\"
+ ></iframe>`;
+ });
+
+ nestframe.onload = (event) => {
+ nestframe.contentDocument.body.innerHTML = `
+ <iframe id="frameone"
+ src=\"http://example.com\"
+ onload=\"parent.postMessage({status:'loaded', type: 'http'}, '*')\"
+ onerror=\"parent.postMessage({status:'blocked', type: 'http'}, '*')\"
+ ></iframe>`;
+ }
+
+ nestframe.src = "about:blank";
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_bug1551886.html b/dom/security/test/mixedcontentblocker/file_bug1551886.html
new file mode 100644
index 0000000000..41c46b5273
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_bug1551886.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+</head>
+
+<body>
+<script>
+ let f = document.createElement("iframe");
+ f.src = "data:text/html,<iframe src='http://example.com' onload=\"parent.postMessage({status:'loaded', type: 'http'}, 'https://example.com')\" onerror=\"parent.postMessage({status:'blocked', type: 'http'}, 'https://example.com')\"></iframe>";
+ window.addEventListener("message", (event) => {
+ parent.postMessage(event.data, "http://mochi.test:8888");
+
+ // Only create second iframe once
+ if(event.data.type === "https") {
+ return;
+ }
+
+ let f2 = document.createElement("iframe");
+ f2.src = "data:text/html,<iframe src='https://example.com' onload=\"parent.postMessage({status:'loaded', type: 'https'}, 'https://example.com')\" onerror=\"parent.postMessage({status:'blocked', type: 'https'}, 'https://example.com')\"></iframe>";
+ document.body.appendChild(f2);
+ });
+ document.body.appendChild(f);
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_bug803225_test_mailto.html b/dom/security/test/mixedcontentblocker/file_bug803225_test_mailto.html
new file mode 100644
index 0000000000..f1459d3667
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_bug803225_test_mailto.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker - Mailto Protocol Compose Page
+https://bugzilla.mozilla.org/show_bug.cgi?id=803225
+-->
+<head> <meta charset="utf-8">
+</head>
+<body>
+Hello
+<script>window.close();</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_csp_block_all_mixedcontent_and_mixed_content_display_upgrade.html b/dom/security/test/mixedcontentblocker/file_csp_block_all_mixedcontent_and_mixed_content_display_upgrade.html
new file mode 100644
index 0000000000..80e97443ed
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_csp_block_all_mixedcontent_and_mixed_content_display_upgrade.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1806080 - ML2 with CSP block-all-mixed-content </title>
+ <meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">
+</head>
+<body>
+ <!--upgradeable resources--->
+ <img id="some-img" src="http://test1.example.com/browser/dom/security/test/mixedcontentblocker/pass.png" width="100px">
+ <video id="some-video" src="http://test1.example.com/browser/dom/security/test/mixedcontentblocker/test.ogv" width="100px">
+ <audio id="some-audio" src="http://test1.example.com/browser/dom/security/test/mixedcontentblocker/test.wav" width="100px">
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_frameNavigation.html b/dom/security/test/mixedcontentblocker/file_frameNavigation.html
new file mode 100644
index 0000000000..ae76ec806f
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_frameNavigation.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker related to navigating children, grandchildren, etc
+https://bugzilla.mozilla.org/show_bug.cgi?id=840388
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Mixed Content Frame Navigation</title>
+</head>
+<body>
+<div id="testContent"></div>
+
+<script>
+ var baseUrlHttps = "https://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html";
+
+ // For tests that require setTimeout, set the maximum polling time to 50 x 100ms = 5 seconds.
+ var MAX_COUNT = 50;
+ var TIMEOUT_INTERVAL = 100;
+
+ var testContent = document.getElementById("testContent");
+
+ // Test 1: Navigate secure iframe to insecure iframe on an insecure page
+ var iframe_test1 = document.createElement("iframe");
+ var counter_test1 = 0;
+ iframe_test1.src = baseUrlHttps + "?insecurePage_navigate_child";
+ iframe_test1.setAttribute("id", "test1");
+ iframe_test1.onerror = function() {
+ parent.postMessage({"test": "insecurePage_navigate_child", "msg": "got an onerror alert when loading or navigating testing iframe"}, "http://mochi.test:8888");
+ };
+ testContent.appendChild(iframe_test1);
+
+ function navigationStatus(iframe_test1)
+ {
+ // When the page is navigating, it goes through about:blank and we will get a permission denied for loc.
+ // Catch that specific exception and return
+ try {
+ var loc = document.getElementById("test1").contentDocument.location;
+ } catch(e) {
+ if (e.name === "SecurityError") {
+ // We received an exception we didn't expect.
+ throw e;
+ }
+ counter_test1++;
+ return;
+ }
+ if (loc == "http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?insecurePage_navigate_child_response") {
+ return;
+ }
+ if (counter_test1 < MAX_COUNT) {
+ counter_test1++;
+ setTimeout(navigationStatus, TIMEOUT_INTERVAL, iframe_test1);
+ }
+ else {
+ // After we have called setTimeout the maximum number of times, assume navigating the iframe is blocked
+ parent.postMessage({"test": "insecurePage_navigate_child", "msg": "navigating to insecure iframe blocked on insecure page"}, "http://mochi.test:8888");
+ }
+ }
+
+ setTimeout(navigationStatus, TIMEOUT_INTERVAL, iframe_test1);
+
+ // Test 2: Navigate secure grandchild iframe to insecure grandchild iframe on a page that has no secure parents
+ var iframe_test2 = document.createElement("iframe");
+ iframe_test2.src = "http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_grandchild.html"
+ iframe_test2.onerror = function() {
+ parent.postMessage({"test": "insecurePage_navigate_grandchild", "msg": "got an on error alert when loading or navigating testing iframe"}, "http://mochi.test:8888");
+ };
+ testContent.appendChild(iframe_test2);
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_frameNavigation_blankTarget.html b/dom/security/test/mixedcontentblocker/file_frameNavigation_blankTarget.html
new file mode 100644
index 0000000000..c4502ce8d2
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_frameNavigation_blankTarget.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker - Opening link with _blank target in an https iframe.
+https://bugzilla.mozilla.org/show_bug.cgi?id=841850
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Mixed Content Frame Navigation</title>
+</head>
+<body>
+<a href="http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?blankTarget" id="blankTarget" target="_blank" rel="opener">Go to http site</a>
+
+<script>
+ var blankTarget = document.getElementById("blankTarget");
+ blankTarget.click();
+
+ var observer = {
+ observe(subject, topic, data) {
+ if (topic === "specialpowers-http-notify-request" &&
+ data === "http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?blankTarget") {
+ parent.parent.postMessage({"test": "blankTarget", "msg": "opened an http link with target=_blank from a secure page"}, "http://mochi.test:8888");
+ SpecialPowers.removeObserver(observer, "specialpowers-http-notify-request");
+ }
+ }
+ }
+
+ // This is a special observer topic that is proxied from http-on-modify-request
+ // in the parent process to inform us when a URI is loaded
+ SpecialPowers.addObserver(observer, "specialpowers-http-notify-request");
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_frameNavigation_grandchild.html b/dom/security/test/mixedcontentblocker/file_frameNavigation_grandchild.html
new file mode 100644
index 0000000000..e88033dae5
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_frameNavigation_grandchild.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker - Navigating Grandchild frames when a secure parent doesn't exist
+https://bugzilla.mozilla.org/show_bug.cgi?id=840388
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Mixed Content Frame Navigation</title>
+</head>
+<body>
+<iframe src="https://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?insecurePage_navigate_grandchild" id="child"></iframe>
+
+<script>
+ // For tests that require setTimeout, set the maximum polling time to 100 x 100ms = 10 seconds.
+ var MAX_COUNT = 50;
+ var TIMEOUT_INTERVAL = 100;
+ var counter = 0;
+
+ var child = document.getElementById("child");
+ function navigationStatus(child)
+ {
+ // When the page is navigating, it goes through about:blank and we will get a permission denied for loc.
+ // Catch that specific exception and return
+ try {
+ var loc;
+ if (child.contentDocument) {
+ loc = child.contentDocument.location;
+ }
+ } catch(e) {
+ if (e.message && !e.message.includes("Permission denied to access property")) {
+ // We received an exception we didn't expect.
+ throw e;
+ }
+ counter++;
+ return;
+ }
+ if (loc == "http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?insecurePage_navigate_grandchild_response") {
+ return;
+ }
+ if(counter < MAX_COUNT) {
+ counter++;
+ setTimeout(navigationStatus, TIMEOUT_INTERVAL, child);
+ }
+ else {
+ // After we have called setTimeout the maximum number of times, assume navigating the iframe is blocked
+ parent.parent.postMessage({"test": "insecurePage_navigate_grandchild", "msg": "navigating to insecure grandchild iframe blocked on insecure page"}, "http://mochi.test:8888");
+ }
+ }
+
+ setTimeout(navigationStatus, TIMEOUT_INTERVAL, child);
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html b/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html
new file mode 100644
index 0000000000..251bb73e33
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<div id="content"></div>
+<script>
+ // get the case from the query string
+ var type = location.search.substring(1);
+
+ function clickLink() {
+ // If we don't reflow before clicking the link, the test will fail intermittently. The reason is still unknown. We'll track this issue in bug 1259715.
+ requestAnimationFrame(function() {
+ setTimeout(function() {
+ document.getElementById("link").click();
+ }, 0);
+ });
+ }
+
+ switch (type) {
+ case "insecurePage_navigate_child":
+ document.getElementById("content").innerHTML =
+ '<a href="http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?insecurePage_navigate_child_response" id="link">Testing\<\/a>';
+ clickLink();
+ break;
+
+ case "insecurePage_navigate_child_response":
+ parent.parent.postMessage({"test": "insecurePage_navigate_child", "msg": "navigated to insecure iframe on insecure page"}, "http://mochi.test:8888");
+ document.getElementById("content").innerHTML = "Navigated from secure to insecure frame on an insecure page";
+ break;
+
+ case "insecurePage_navigate_grandchild":
+ document.getElementById("content").innerHTML =
+ '<a href="http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?insecurePage_navigate_grandchild_response" id="link">Testing\<\/a>';
+ clickLink();
+ break;
+
+ case "insecurePage_navigate_grandchild_response":
+ parent.parent.parent.postMessage({"test": "insecurePage_navigate_grandchild", "msg": "navigated to insecure grandchild iframe on insecure page"}, "http://mochi.test:8888");
+ document.getElementById("content").innerHTML = "Navigated from secure to insecure grandchild frame on an insecure page";
+ break;
+
+ case "securePage_navigate_child":
+ document.getElementById("content").innerHTML =
+ '<a href="http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?securePage_navigate_child_response" id="link">Testing\<\/a>';
+ clickLink();
+ break;
+
+ case "securePage_navigate_child_response":
+ document.getElementById("content").innerHTML = "<p>Navigated from secure to insecure frame on a secure page</p>";
+ parent.parent.postMessage({"test": "securePage_navigate_child", "msg": "navigated to insecure iframe on secure page"}, "http://mochi.test:8888");
+ break;
+
+ case "securePage_navigate_grandchild":
+ document.getElementById("content").innerHTML=
+ '<a href="http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?securePage_navigate_grandchild_response" id="link">Testing\<\/a>';
+ clickLink();
+ break;
+
+ case "securePage_navigate_grandchild_response":
+ parent.parent.parent.postMessage({"test": "securePage_navigate_grandchild", "msg": "navigated to insecure grandchild iframe on secure page"}, "http://mochi.test:8888");
+ document.getElementById("content").innerHTML = "<p>Navigated from secure to insecure grandchild frame on a secure page</p>";
+ break;
+
+ case "blankTarget":
+ window.close();
+ break;
+
+ default:
+ document.getElementById("content").innerHTML = "Hello";
+ break;
+ }
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_frameNavigation_secure.html b/dom/security/test/mixedcontentblocker/file_frameNavigation_secure.html
new file mode 100644
index 0000000000..0502c0c9dd
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_frameNavigation_secure.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker related to navigating children, grandchildren, etc
+https://bugzilla.mozilla.org/show_bug.cgi?id=840388
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Mixed Content Frame Navigation</title>
+</head>
+<body>
+<div id="testContent"></div>
+
+<script>
+ var baseUrl = "https://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html";
+
+ // For tests that require setTimeout, set the maximum polling time to 50 x 100ms = 5 seconds.
+ var MAX_COUNT = 50;
+ var TIMEOUT_INTERVAL = 100;
+
+ var testContent = document.getElementById("testContent");
+
+ // Test 1: Navigate secure iframe to insecure iframe on a secure page
+ var iframe_test1 = document.createElement("iframe");
+ var counter_test1 = 0;
+ iframe_test1.setAttribute("id", "test1");
+ iframe_test1.src = baseUrl + "?securePage_navigate_child";
+ iframe_test1.onerror = function() {
+ parent.postMessage({"test": "securePage_navigate_child", "msg": "navigating to insecure iframe blocked on secure page"}, "http://mochi.test:8888");
+ };
+ testContent.appendChild(iframe_test1);
+
+ function navigationStatus(iframe_test1)
+ {
+ // When the page is navigating, it goes through about:blank and we will get a permission denied for loc.
+ // Catch that specific exception and return
+ try {
+ var loc = document.getElementById("test1").contentDocument.location;
+ } catch(e) {
+ if (e.name === "SecurityError") {
+ // We received an exception we didn't expect.
+ throw e;
+ }
+ counter_test1++;
+ return;
+ }
+ if (loc == "http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?insecurePage_navigate_child_response") {
+ return;
+ }
+ if (counter_test1 < MAX_COUNT) {
+ counter_test1++;
+ setTimeout(navigationStatus, TIMEOUT_INTERVAL, iframe_test1);
+ }
+ else {
+ // After we have called setTimeout the maximum number of times, assume navigating the iframe is blocked
+ parent.postMessage({"test": "securePage_navigate_child", "msg": "navigating to insecure iframe blocked on secure page"}, "http://mochi.test:8888");
+ }
+ }
+
+ setTimeout(navigationStatus, TIMEOUT_INTERVAL, iframe_test1);
+
+ // Test 2: Open an http page in a new tab from a link click with target=_blank.
+ var iframe_test3 = document.createElement("iframe");
+ iframe_test3.src = "https://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_blankTarget.html";
+ iframe_test3.onerror = function() {
+ parent.postMessage({"test": "blankTarget", "msg": "got an onerror event when loading or navigating testing iframe"}, "http://mochi.test:8888");
+ };
+ testContent.appendChild(iframe_test3);
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_frameNavigation_secure_grandchild.html b/dom/security/test/mixedcontentblocker/file_frameNavigation_secure_grandchild.html
new file mode 100644
index 0000000000..bbdfe2beab
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_frameNavigation_secure_grandchild.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker - Navigating Grandchild Frames when a secure parent exists
+https://bugzilla.mozilla.org/show_bug.cgi?id=840388
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Mixed Content Frame Navigation</title>
+</head>
+<body>
+
+<iframe src="https://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?securePage_navigate_grandchild" id="child"></iframe>
+<script>
+ // For tests that require setTimeout, set the maximum polling time to 50 x 100ms = 5 seconds.
+ var MAX_COUNT = 50;
+ var TIMEOUT_INTERVAL = 100;
+ var counter = 0;
+
+ var child = document.getElementById("child");
+ function navigationStatus(child)
+ {
+ var loc;
+ // When the page is navigating, it goes through about:blank and we will get a permission denied for loc.
+ // Catch that specific exception and return
+ try {
+ loc = document.getElementById("child").contentDocument.location;
+ } catch(e) {
+ if (e.message && !e.message.includes("Permission denied to access property")) {
+ // We received an exception we didn't expect.
+ throw e;
+ }
+ counter++;
+ return;
+ }
+ if (loc == "http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?securePage_navigate_grandchild_response") {
+ return;
+ }
+ if (counter < MAX_COUNT) {
+ counter++;
+ setTimeout(navigationStatus, TIMEOUT_INTERVAL, child);
+ }
+ else {
+ // After we have called setTimeout the maximum number of times, assume navigating the iframe is blocked
+ dump("\nThe current location of the grandchild iframe is: "+loc+".\n");
+ dump("\nWe have past the maximum timeout. Navigating a grandchild iframe from an https location to an http location on a secure page failed. We are about to post message to the top level page\n");
+ parent.parent.postMessage({"test": "securePage_navigate_grandchild", "msg": "navigating to insecure grandchild iframe blocked on secure page"}, "http://mochi.test:8888");
+ dump("\nAttempted postMessage\n");
+ }
+ }
+
+ setTimeout(navigationStatus, TIMEOUT_INTERVAL, child);
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_main.html b/dom/security/test/mixedcontentblocker/file_main.html
new file mode 100644
index 0000000000..5bbbf0ec3a
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_main.html
@@ -0,0 +1,336 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker
+https://bugzilla.mozilla.org/show_bug.cgi?id=62178
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 62178</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="testContent"></div>
+
+<!-- types the Mixed Content Blocker can block
+ /*
+ switch (aContentType) {
+ case nsIContentPolicy::TYPE_OBJECT:
+ case nsIContentPolicy::TYPE_SCRIPT:
+ case nsIContentPolicy::TYPE_STYLESHEET:
+ case nsIContentPolicy::TYPE_SUBDOCUMENT:
+ case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
+
+ case nsIContentPolicy::TYPE_FONT: - NO TEST:
+ Load events for external fonts are not detectable by javascript.
+ case nsIContentPolicy::TYPE_WEBSOCKET: - NO TEST:
+ websocket connections over https require an encrypted websocket protocol (wss:)
+
+ case nsIContentPolicy::TYPE_IMAGE:
+ case nsIContentPolicy::TYPE_IMAGESET:
+ case nsIContentPolicy::TYPE_MEDIA:
+ case nsIContentPolicy::TYPE_PING:
+ our ping implementation is off by default and does not comply with the current spec (bug 786347)
+ case nsIContentPolicy::TYPE_BEACON:
+
+ }
+ */
+-->
+
+<script>
+ function ok(a, msg) {
+ parent.postMessage({msg, check: true, status: !!a}, "http://mochi.test:8888");
+ }
+
+ function is(a, b, msg) {
+ ok(a == b, msg);
+ }
+
+ function report(type, msg) {
+ parent.postMessage({test: type, msg}, "http://mochi.test:8888");
+ }
+
+ function uniqueID() {
+ return Math.floor(Math.random() * 100000)
+ }
+ function uniqueIDParam(id) {
+ return "&uniqueID=" + id;
+ }
+
+ async function init() {
+ var baseUrl = "http://example.com/tests/dom/security/test/mixedcontentblocker/file_server.sjs";
+ var checkLastRequestUrl = "https://example.com/tests/dom/security/test/mixedcontentblocker/file_server.sjs?lastRequest=true";
+
+ //For tests that require setTimeout, set the maximum polling time to 100 x 100ms = 10 seconds.
+ var MAX_COUNT = 100;
+ var TIMEOUT_INTERVAL = 100;
+
+ var testContent = document.getElementById("testContent");
+
+ async function checkLastRequest() {
+ const response = await fetch(checkLastRequestUrl);
+ return response.json();
+ }
+
+ /* Part 1: Mixed Script tests */
+
+ // Test 1a: insecure object
+ var object = document.createElement("object");
+ var objectId = uniqueID();
+ object.data = baseUrl + "?type=object" + uniqueIDParam(objectId);
+ object.type = "image/png";
+ object.width = "200";
+ object.height = "200";
+
+ testContent.appendChild(object);
+
+ var objectCount = 0;
+
+ function objectStatus(object) {
+ // Expose our privileged bits on the object. We will match the MIME type to the one
+ // provided by file_server.sjs
+ object = SpecialPowers.wrap(object);
+ var typeIsSet = object.actualType && (object.actualType !== '');
+ var isLoaded = typeIsSet && object.actualType === 'application/x-test-match';
+ if (isLoaded) {
+ //object loaded
+ report("object", "insecure object loaded");
+ }
+ else {
+ if(!typeIsSet && objectCount < MAX_COUNT) {
+ objectCount++;
+ setTimeout(objectStatus, TIMEOUT_INTERVAL, object);
+ }
+ else {
+ //After we have called setTimeout the maximum number of times, assume object is blocked
+ report("object", "insecure object blocked");
+ }
+ }
+ }
+
+ // object does not have onload and onerror events. Hence we need a setTimeout to check the object's status
+ setTimeout(objectStatus, TIMEOUT_INTERVAL, object);
+
+ // Test 1b: insecure script
+ var script = document.createElement("script");
+ var scriptId = uniqueID();
+ var scriptLoad = false;
+ var scriptCount = 0;
+ script.src = baseUrl + "?type=script" + uniqueIDParam(scriptId);
+ script.onload = function(e) {
+ report("script", "insecure script loaded");
+ scriptLoad = true;
+ }
+ testContent.appendChild(script);
+
+ function scriptStatus(script)
+ {
+ if(scriptLoad) {
+ return;
+ }
+ if (scriptCount < MAX_COUNT) {
+ scriptCount++;
+ setTimeout(scriptStatus, TIMEOUT_INTERVAL, script);
+ }
+ else {
+ //After we have called setTimeout the maximum number of times, assume script is blocked
+ report("script", "insecure script blocked");
+ }
+ }
+
+ // scripts blocked by Content Policy's do not have onerror events (see bug 789856). Hence we need a setTimeout to check the script's status
+ setTimeout(scriptStatus, TIMEOUT_INTERVAL, script);
+
+
+ // Test 1c: insecure stylesheet
+ var cssStyleSheet = document.createElement("link");
+ var cssStyleSheetId = uniqueID();
+ cssStyleSheet.rel = "stylesheet";
+ cssStyleSheet.href = baseUrl + "?type=stylesheet" + uniqueIDParam(cssStyleSheetId);
+ cssStyleSheet.type = "text/css";
+ testContent.appendChild(cssStyleSheet);
+
+ var styleCount = 0;
+
+ function styleStatus(cssStyleSheet) {
+ if( cssStyleSheet.sheet || cssStyleSheet.styleSheet || cssStyleSheet.innerHTML ) {
+ report("stylesheet", "insecure stylesheet loaded");
+ }
+ else {
+ if(styleCount < MAX_COUNT) {
+ styleCount++;
+ setTimeout(styleStatus, TIMEOUT_INTERVAL, cssStyleSheet);
+ }
+ else {
+ //After we have called setTimeout the maximum number of times, assume stylesheet is blocked
+ report("stylesheet", "insecure stylesheet blocked");
+ }
+ }
+ }
+
+ // link does not have onload and onerror events. Hence we need a setTimeout to check the link's status
+ window.setTimeout(styleStatus, TIMEOUT_INTERVAL, cssStyleSheet);
+
+ // Test 1d: insecure iframe
+ var iframe = document.createElement("iframe");
+ var iframeId = uniqueID();
+ iframe.src = baseUrl + "?type=iframe" + uniqueIDParam(iframeId);
+ iframe.onload = function() {
+ report("iframe", "insecure iframe loaded");
+ }
+ iframe.onerror = function() {
+ report("iframe", "insecure iframe blocked");
+ };
+ testContent.appendChild(iframe);
+
+
+ // Test 1e: insecure xhr
+ await new Promise((resolve) => {
+ var xhr = new XMLHttpRequest;
+ var xhrId = uniqueID();
+ try {
+ xhr.open("GET", baseUrl + "?type=xhr" + uniqueIDParam(xhrId), true);
+ xhr.send();
+ xhr.onloadend = function (oEvent) {
+ if (xhr.status == 200) {
+ report("xhr", "insecure xhr loaded");
+ resolve();
+ }
+ else {
+ report("xhr", "insecure xhr blocked");
+ resolve();
+ }
+ }
+ } catch(ex) {
+ report("xhr", "insecure xhr blocked");
+ resolve();
+ }
+ });
+
+ /* Part 2: Mixed Display tests */
+
+ // Shorthand for all image test variants
+ async function imgHandlers(img, test, uniqueID) {
+ await new Promise((resolve) => {
+ img.onload = async () => {
+ const lastRequest = await checkLastRequest();
+ is(lastRequest.uniqueID, uniqueID, "UniqueID for the last request matches");
+ let message = "insecure image loaded";
+ if (lastRequest.scheme == "https") {
+ message = "secure image loaded after upgrade";
+ }
+ report(test, message);
+ resolve();
+ }
+ img.onerror = async () => {
+ let message = "insecure image blocked";
+ report(test, message);
+ resolve();
+ }
+ });
+ }
+
+ // Test 2a: insecure image
+ var img = document.createElement("img");
+ var imgId = uniqueID();
+ img.src = baseUrl + "?type=img" + uniqueIDParam(imgId);
+ await imgHandlers(img, "image", imgId);
+ // We don't need to append the image to the document. Doing so causes the image test to run twice.
+
+ // Test 2b: insecure media
+ var media = document.createElement("video");
+ var mediaId = uniqueID();
+ media.src = baseUrl + "?type=media" + uniqueIDParam(mediaId);
+ media.width = "320";
+ media.height = "200";
+ media.type = "video/ogg";
+ await new Promise(res => {
+ media.onloadeddata = async () => {
+ const lastRequest = await checkLastRequest();
+ is(lastRequest.uniqueID, mediaId, "UniqueID for the last request matches");
+ let message = "insecure media loaded";
+ if (lastRequest.scheme == "https") {
+ message = "secure media loaded after upgrade";
+ }
+ report("media", message);
+ res();
+ }
+ media.onerror = function() {
+ report("media", "insecure media blocked");
+ res();
+ }
+ });
+ // We don't need to append the video to the document. Doing so causes the image test to run twice.
+
+ /* Part 3: Mixed Active Tests for Image srcset */
+
+ // Test 3a: image with srcset
+ var imgA = document.createElement("img");
+ var imgAId = uniqueID();
+ imgA.srcset = baseUrl + "?type=img&subtype=imageSrcset" + uniqueIDParam(imgAId);
+ await imgHandlers(imgA, "imageSrcset", imgAId);
+
+ // Test 3b: image with srcset, using fallback from src, should still use imageset policy
+ var imgB = document.createElement("img");
+ var imgBId = uniqueID();
+ imgB.srcset = " ";
+ imgB.src = baseUrl + "?type=img&subtype=imageSrcSetFallback" + uniqueIDParam(imgBId);
+ await imgHandlers(imgB, "imageSrcsetFallback", imgBId);
+
+ // Test 3c: image in <picture>
+ var imgC = document.createElement("img");
+ var pictureC = document.createElement("picture");
+ var sourceC = document.createElement("source");
+ var sourceCId = uniqueID();
+ sourceC.srcset = baseUrl + "?type=img&subtype=imagePicture" + uniqueIDParam(sourceCId);
+ pictureC.appendChild(sourceC);
+ pictureC.appendChild(imgC);
+ await imgHandlers(imgC, "imagePicture", sourceCId);
+
+ // Test 3d: Loaded basic image switching to a <picture>, loading
+ // same source, should still redo the request with new
+ // policy.
+ var imgD = document.createElement("img");
+ var imgDId = uniqueID();
+ var sourceDId = uniqueID();
+ imgD.src = baseUrl + "?type=img&subtype=imageJoinPicture" + uniqueIDParam(imgDId);
+ await new Promise(res => {
+ imgD.onload = imgD.onerror = function() {
+ // Whether or not it loads, we want to now append it to a picture and observe
+ var pictureD = document.createElement("picture");
+ var sourceD = document.createElement("source");
+ sourceD.srcset = baseUrl + "?type=img&subtype=imageJoinPicture2" + uniqueIDParam(sourceDId);
+ pictureD.appendChild(sourceD);
+ pictureD.appendChild(imgD);
+ res();
+ }
+ });
+ await imgHandlers(imgD, "imageJoinPicture", sourceDId);
+
+ // Test 3e: img load from <picture> source reverts to img.src as it
+ // is removed -- the new request should revert to mixed
+ // display policy
+ var imgE = document.createElement("img");
+ var pictureE = document.createElement("picture");
+ var pictureEId = uniqueID();
+ var sourceE = document.createElement("source");
+ var sourceEId = uniqueID();
+ sourceE.srcset = baseUrl + "?type=img&subtype=imageLeavePicture" + uniqueIDParam(sourceEId);
+ pictureE.appendChild(sourceE);
+ pictureE.appendChild(imgE);
+ imgE.src = baseUrl + "?type=img&subtype=imageLeavePicture2" + uniqueIDParam(pictureEId);
+ await new Promise(res => {
+ imgE.onload = imgE.onerror = function() {
+ // Whether or not it loads, remove it from the picture and observe
+ pictureE.removeChild(imgE)
+ res();
+ }
+ });
+ await imgHandlers(imgE, "imageLeavePicture", pictureEId);
+ }
+
+ init();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_main_bug803225.html b/dom/security/test/mixedcontentblocker/file_main_bug803225.html
new file mode 100644
index 0000000000..1363a68886
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_main_bug803225.html
@@ -0,0 +1,175 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker - Allowed Protocols
+https://bugzilla.mozilla.org/show_bug.cgi?id=803225
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 62178</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="testContent"></div>
+
+<!-- Test additional schemes the Mixed Content Blocker should not block
+ "about" protocol URIs that are URI_SAFE_FOR_UNTRUSTED_CONTENT (moz-safe-about; see nsAboutProtocolHandler::NewURI
+ "data",
+ "javascript",
+ "mailto",
+ "resource",
+ "wss"
+-->
+
+<script>
+ const { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+ );
+
+ //For tests that require setTimeout, set the timeout interval
+ var TIMEOUT_INTERVAL = 100;
+
+ var testContent = document.getElementById("testContent");
+
+ // Test 1 & 2: about and javascript protcols within an iframe
+ var data = Array(2,2);
+ var protocols = [
+ ["about", ""], //When no source is specified, the frame gets a source of about:blank
+ ["javascript", "javascript:document.open();document.write='<h1>SUCCESS</h1>';document.close();"],
+ ];
+ for(var i=0; i < protocols.length; i++)
+ {
+ var generic_frame = document.createElement("iframe");
+ generic_frame.src = protocols[i][1];
+ generic_frame.name="generic_protocol";
+
+ generic_frame.onload = function(i) {
+ data = {"test": protocols[i][0], "msg": "resource with " + protocols[i][0] + " protocol loaded"};
+ parent.postMessage(data, "http://mochi.test:8888");
+ }.bind(generic_frame, i)
+
+ generic_frame.onerror = function(i) {
+ data = {"test": protocols[i][0], "msg": "resource with " + protocols[i][0] + " protocol did not load"};
+ parent.postMessage(data, "http://mochi.test:8888");
+ }.bind(generic_frame, i);
+
+ testContent.appendChild(generic_frame, i);
+ }
+
+ // Test 3: for resource within a script tag
+ // Note: the script we load throws an exception, but the script element's
+ // onload listener is called after we successfully fetch the script,
+ // independently of whether it throws an exception.
+ var resource_script=document.createElement("script");
+ resource_script.src = "resource://gre/modules/XPCOMUtils.sys.mjs";
+ resource_script.name = "resource_protocol";
+ resource_script.onload = function() {
+ parent.postMessage({"test": "resource", "msg": "resource with resource protocol loaded"}, "http://mochi.test:8888");
+ }
+ resource_script.onerror = function() {
+ parent.postMessage({"test": "resource", "msg": "resource with resource protocol did not load"}, "http://mochi.test:8888");
+ }
+
+ testContent.appendChild(resource_script);
+
+ // Test 4: about unsafe protocol within an iframe
+ var unsafe_about_frame = document.createElement("iframe");
+ unsafe_about_frame.src = "about:config";
+ unsafe_about_frame.name = "unsafe_about_protocol";
+ unsafe_about_frame.onload = function() {
+ parent.postMessage({"test": "unsafe_about", "msg": "resource with unsafe about protocol loaded"}, "http://mochi.test:8888");
+ }
+ unsafe_about_frame.onerror = function() {
+ parent.postMessage({"test": "unsafe_about", "msg": "resource with unsafe about protocol did not load"}, "http://mochi.test:8888");
+ }
+ testContent.appendChild(unsafe_about_frame);
+
+ // Test 5: data protocol within a script tag
+ var x = 2;
+ var newscript = document.createElement("script");
+ newscript.src= "data:text/javascript,var x = 4;";
+ newscript.onload = function() {
+ parent.postMessage({"test": "data_protocol", "msg": "resource with data protocol loaded"}, "http://mochi.test:8888");
+ }
+ newscript.onerror = function() {
+ parent.postMessage({"test": "data_protocol", "msg": "resource with data protocol did not load"}, "http://mochi.test:8888");
+ }
+ testContent.appendChild(newscript);
+
+ // Test 6: mailto protocol
+ let mm = SpecialPowers.loadChromeScript(function launchHandler() {
+ /* eslint-env mozilla/chrome-script */
+ var ioService = Cc["@mozilla.org/network/io-service;1"].
+ getService(Ci.nsIIOService);
+
+ var webHandler = Cc["@mozilla.org/uriloader/web-handler-app;1"].
+ createInstance(Ci.nsIWebHandlerApp);
+ webHandler.name = "Web Handler";
+ webHandler.uriTemplate = "http://example.com/tests/dom/security/test/mixedcontentblocker/file_bug803225_test_mailto.html?s=%";
+
+ Services.ppmm.addMessageListener("Test:content-ready", function contentReadyListener() {
+ Services.ppmm.removeMessageListener("Test:content-ready", contentReadyListener);
+ sendAsyncMessage("Test:content-ready-forward");
+ Services.ppmm.removeDelayedProcessScript(pScript);
+ })
+
+ var pScript = "data:,new " + function () {
+ /* eslint-env mozilla/process-script */
+ var os = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ var observer = {
+ observe(subject, topic, data) {
+ if (topic == "content-document-global-created" && data == "http://example.com") {
+ sendAsyncMessage("Test:content-ready");
+ os.removeObserver(observer, "content-document-global-created");
+ }
+ }
+ };
+ os.addObserver(observer, "content-document-global-created");
+ }
+
+ Services.ppmm.loadProcessScript(pScript, true);
+
+ var uri = ioService.newURI("mailto:foo@bar.com");
+ webHandler.launchWithURI(uri);
+ });
+
+ var mailto = false;
+
+ mm.addMessageListener("Test:content-ready-forward", function contentReadyListener() {
+ mm.removeMessageListener("Test:content-ready-forward", contentReadyListener);
+ mailto = true;
+ parent.postMessage({"test": "mailto", "msg": "resource with mailto protocol loaded"}, "http://mochi.test:8888");
+ });
+
+ function mailtoProtocolStatus() {
+ if(!mailto) {
+ //There is no onerror event associated with the WebHandler, and hence we need a setTimeout to check the status
+ setTimeout(mailtoProtocolStatus, TIMEOUT_INTERVAL);
+ }
+ }
+
+ mailtoProtocolStatus();
+
+ // Test 7: wss protocol
+ // WebSocket tests are not supported on Android Yet. Bug 1566168.
+ if (AppConstants.platform !== "android") {
+ var wss;
+ wss = new WebSocket("wss://example.com/tests/dom/security/test/mixedcontentblocker/file_main_bug803225_websocket");
+
+ var status_wss = "started";
+ wss.onopen = function(e) {
+ status_wss = "opened";
+ wss.close();
+ }
+ wss.onclose = function(e) {
+ if(status_wss == "opened") {
+ parent.postMessage({"test": "wss", "msg": "resource with wss protocol loaded"}, "http://mochi.test:8888");
+ } else {
+ parent.postMessage({"test": "wss", "msg": "resource with wss protocol did not load"}, "http://mochi.test:8888");
+ }
+ }
+ }
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_main_bug803225_websocket_wsh.py b/dom/security/test/mixedcontentblocker/file_main_bug803225_websocket_wsh.py
new file mode 100644
index 0000000000..b7159c742b
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_main_bug803225_websocket_wsh.py
@@ -0,0 +1,6 @@
+def web_socket_do_extra_handshake(request):
+ pass
+
+
+def web_socket_transfer_data(request):
+ pass
diff --git a/dom/security/test/mixedcontentblocker/file_mixed_content_auto_upgrade_display_console.html b/dom/security/test/mixedcontentblocker/file_mixed_content_auto_upgrade_display_console.html
new file mode 100644
index 0000000000..b86fbc9cbc
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_mixed_content_auto_upgrade_display_console.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1673574 - Improve Console logging for mixed content auto upgrading</title>
+</head>
+<body>
+ <!-- The following file does in fact not exist because we only care if it shows up in the console -->
+ <img src="http://example.com/file_mixed_content_auto_upgrade_display_console.jpg">
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_redirect.html b/dom/security/test/mixedcontentblocker/file_redirect.html
new file mode 100644
index 0000000000..99e1873791
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_redirect.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug1402363: Test mixed content redirects</title>
+</head>
+<body>
+
+<script type="text/javascript">
+ const PATH = "https://example.com/tests/dom/security/test/mixedcontentblocker/";
+
+ // check a fetch redirect from https to https (should be allowed)
+ fetch(PATH + "file_redirect_handler.sjs?https-to-https-redirect", {
+ method: 'get'
+ }).then(function(response) {
+ window.parent.postMessage("https-to-https-loaded", "*");
+ }).catch(function(err) {
+ window.parent.postMessage("https-to-https-blocked", "*");
+ });
+
+ // check a fetch redirect from https to http (should be blocked)
+ fetch(PATH + "file_redirect_handler.sjs?https-to-http-redirect", {
+ method: 'get'
+ }).then(function(response) {
+ window.parent.postMessage("https-to-http-loaded", "*");
+ }).catch(function(err) {
+ window.parent.postMessage("https-to-http-blocked", "*");
+ });
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_redirect_handler.sjs b/dom/security/test/mixedcontentblocker/file_redirect_handler.sjs
new file mode 100644
index 0000000000..f4ed278675
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_redirect_handler.sjs
@@ -0,0 +1,34 @@
+// custom *.sjs file for
+// Bug 1402363: Test Mixed Content Redirect Blocking.
+
+const URL_PATH = "example.com/tests/dom/security/test/mixedcontentblocker/";
+
+function handleRequest(request, response) {
+ response.setHeader("Cache-Control", "no-cache", false);
+ let queryStr = request.queryString;
+
+ if (queryStr === "https-to-https-redirect") {
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader(
+ "Location",
+ "https://" + URL_PATH + "file_redirect_handler.sjs?load",
+ false
+ );
+ return;
+ }
+
+ if (queryStr === "https-to-http-redirect") {
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader(
+ "Location",
+ "http://" + URL_PATH + "file_redirect_handler.sjs?load",
+ false
+ );
+ return;
+ }
+
+ if (queryStr === "load") {
+ response.setHeader("Content-Type", "text/html", false);
+ response.write("foo");
+ }
+}
diff --git a/dom/security/test/mixedcontentblocker/file_server.sjs b/dom/security/test/mixedcontentblocker/file_server.sjs
new file mode 100644
index 0000000000..4f86c282ee
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_server.sjs
@@ -0,0 +1,131 @@
+const { NetUtil } = ChromeUtils.importESModule(
+ "resource://gre/modules/NetUtil.sys.mjs"
+);
+
+function ERR(response, msg) {
+ dump("ERROR: " + msg + "\n");
+ response.write("HTTP/1.1 400 Bad Request\r\n");
+ response.write("Content-Type: text/html; charset=UTF-8\r\n");
+ response.write("Content-Length: " + msg.length + "\r\n");
+ response.write("\r\n");
+ response.write(msg);
+}
+
+function loadContentFromFile(path) {
+ // Load the content to return in the response from file.
+ // Since it's relative to the cwd of the test runner, we start there and
+ // append to get to the actual path of the file.
+ var testContentFile = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("CurWorkD", Ci.nsIFile);
+ var dirs = path.split("/");
+ for (var i = 0; i < dirs.length; i++) {
+ testContentFile.append(dirs[i]);
+ }
+ var testContentFileStream = Cc[
+ "@mozilla.org/network/file-input-stream;1"
+ ].createInstance(Ci.nsIFileInputStream);
+ testContentFileStream.init(testContentFile, -1, 0, 0);
+ var testContent = NetUtil.readInputStreamToString(
+ testContentFileStream,
+ testContentFileStream.available()
+ );
+ return testContent;
+}
+
+function handleRequest(request, response) {
+ const { scheme, host, path } = request;
+ // get the Content-Type to serve from the query string
+ var contentType = null;
+ var uniqueID = null;
+ var showLastRequest = false;
+ request.queryString.split("&").forEach(function (val) {
+ var [name, value] = val.split("=");
+ if (name == "type") {
+ contentType = unescape(value);
+ }
+ if (name == "uniqueID") {
+ uniqueID = unescape(value);
+ }
+ if (name == "lastRequest") {
+ showLastRequest = true;
+ }
+ });
+
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ if (showLastRequest) {
+ response.setHeader("Content-Type", "text/html", false);
+
+ // We don't want to expose the same lastRequest multiple times.
+ var state = getState("lastRequest");
+ setState("lastRequest", "");
+
+ if (state == "") {
+ ERR(response, "No last request!");
+ return;
+ }
+
+ response.write(state);
+ return;
+ }
+
+ if (!uniqueID) {
+ ERR(response, "No uniqueID?!?");
+ return;
+ }
+
+ setState(
+ "lastRequest",
+ JSON.stringify({
+ scheme,
+ host,
+ path,
+ uniqueID,
+ contentType: contentType || "other",
+ })
+ );
+
+ switch (contentType) {
+ case "img":
+ response.setHeader("Content-Type", "image/png", false);
+ response.write(
+ loadContentFromFile("tests/image/test/mochitest/blue.png")
+ );
+ break;
+
+ case "media":
+ response.setHeader("Content-Type", "video/ogg", false);
+ response.write(loadContentFromFile("tests/dom/media/test/320x240.ogv"));
+ break;
+
+ case "iframe":
+ response.setHeader("Content-Type", "text/html", false);
+ response.write("frame content");
+ break;
+
+ case "script":
+ response.setHeader("Content-Type", "application/javascript", false);
+ break;
+
+ case "stylesheet":
+ response.setHeader("Content-Type", "text/css", false);
+ break;
+
+ case "object":
+ response.setHeader("Content-Type", "application/x-test-match", false);
+ break;
+
+ case "xhr":
+ response.setHeader("Content-Type", "text/xml", false);
+ response.setHeader("Access-Control-Allow-Origin", "https://example.com");
+ response.write('<?xml version="1.0" encoding="UTF-8" ?><test></test>');
+ break;
+
+ default:
+ response.setHeader("Content-Type", "text/html", false);
+ response.write("<html><body>Hello World</body></html>");
+ break;
+ }
+}
diff --git a/dom/security/test/mixedcontentblocker/file_windowOpen.html b/dom/security/test/mixedcontentblocker/file_windowOpen.html
new file mode 100644
index 0000000000..508c1d17db
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_windowOpen.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Test mixed content load in iframe via window.open</title>
+ </head>
+ <body>
+ I'm in an iframe!
+ </body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/mochitest.toml b/dom/security/test/mixedcontentblocker/mochitest.toml
new file mode 100644
index 0000000000..17d8cb4608
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/mochitest.toml
@@ -0,0 +1,66 @@
+[DEFAULT]
+tags = "mcb"
+prefs = [
+ "security.mixed_content.upgrade_display_content=false",
+ "dom.security.https_first=false",
+]
+support-files = [
+ "file_bug803225_test_mailto.html",
+ "file_frameNavigation.html",
+ "file_frameNavigation_blankTarget.html",
+ "file_frameNavigation_grandchild.html",
+ "file_frameNavigation_innermost.html",
+ "file_frameNavigation_secure.html",
+ "file_frameNavigation_secure_grandchild.html",
+ "file_main.html",
+ "file_main_bug803225.html",
+ "file_main_bug803225_websocket_wsh.py",
+ "file_server.sjs",
+ "!/dom/media/test/320x240.ogv",
+ "!/image/test/mochitest/blue.png",
+ "file_redirect.html",
+ "file_redirect_handler.sjs",
+ "file_bug1550792.html",
+ "file_bug1551886.html",
+ "file_windowOpen.html",
+]
+
+["test_bug803225.html"]
+skip-if = [
+ "os=='linux' && bits==32", # Linux32:bug 1324870
+ "headless", # Headless:bug 1405870
+ "tsan", # tsan:bug 1612707
+ "http3",
+ "http2",
+]
+
+["test_bug1550792.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug1551886.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_frameNavigation.html"]
+skip-if = ["true"] # Bug 1424752
+
+["test_main.html"]
+skip-if = [
+ "true",
+ "os == 'android'",
+ "verify && !debug && os == 'linux'", # Android: TIMED_OUT; bug 1402554
+ "tsan", # Times out / Memory consumption, bug 1612707
+]
+
+["test_redirect.html"]
+
+["test_windowOpen.html"]
+scheme = "https"
+skip-if = [
+ "!debug" # Bug 1855588
+]
diff --git a/dom/security/test/mixedcontentblocker/pass.png b/dom/security/test/mixedcontentblocker/pass.png
new file mode 100644
index 0000000000..2fa1e0ac06
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/pass.png
Binary files differ
diff --git a/dom/security/test/mixedcontentblocker/test.ogv b/dom/security/test/mixedcontentblocker/test.ogv
new file mode 100644
index 0000000000..0f83996e5d
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/test.ogv
Binary files differ
diff --git a/dom/security/test/mixedcontentblocker/test.wav b/dom/security/test/mixedcontentblocker/test.wav
new file mode 100644
index 0000000000..85dc1ea904
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/test.wav
Binary files differ
diff --git a/dom/security/test/mixedcontentblocker/test_bug1550792.html b/dom/security/test/mixedcontentblocker/test_bug1550792.html
new file mode 100644
index 0000000000..4f15f9489a
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/test_bug1550792.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1550792: Block insecure subresource with non-https secure context parent</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+let f = document.createElement("iframe");
+f.src = "https://example.com/tests/dom/security/test/mixedcontentblocker/file_bug1550792.html";
+
+window.addEventListener("message", (event) => {
+ switch(event.data.type) {
+ case 'http':
+ is(event.data.status, "blocked", "nested load of http should be blocked.");
+ break
+ case 'https':
+ is(event.data.status, "loaded", "nested load of https should not be blocked.");
+ SimpleTest.finish();
+ break;
+ }
+});
+
+document.body.appendChild(f);
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/test_bug1551886.html b/dom/security/test/mixedcontentblocker/test_bug1551886.html
new file mode 100644
index 0000000000..bf128256a4
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/test_bug1551886.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Bug 1551886: Opaque documents aren't considered in the mixed content blocker</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+let f = document.createElement("iframe");
+f.src = "https://example.com/tests/dom/security/test/mixedcontentblocker/file_bug1551886.html";
+
+window.addEventListener("message", (event) => {
+ switch(event.data.type) {
+ case 'http':
+ is(event.data.status, "blocked", "nested load of http://example should get blocked by the MCB");
+ break
+ case 'https':
+ is(event.data.status, "loaded", "nested load of https://example should not get blocked by the MCB");
+ SimpleTest.finish();
+ break;
+ }
+});
+
+document.body.appendChild(f);
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/test_bug803225.html b/dom/security/test/mixedcontentblocker/test_bug803225.html
new file mode 100644
index 0000000000..87b372adf7
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/test_bug803225.html
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Testing Allowlist of Resource Scheme for Mixed Content Blocker
+https://bugzilla.mozilla.org/show_bug.cgi?id=803225
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 803225</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <script>
+ const { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+ );
+
+ var counter = 0;
+ var settings = [ [true, true], [true, false], [false, true], [false, false] ];
+
+ var blockActive;
+ var blockDisplay;
+
+ //Cycle through 4 different preference settings.
+ function changePrefs(callback) {
+ let newPrefs = [
+ ["security.all_resource_uri_content_accessible", true], // Temporarily allow content to access all resource:// URIs.
+ ["security.mixed_content.block_display_content", settings[counter][0]],
+ ["security.mixed_content.block_active_content", settings[counter][1]]
+ ];
+
+ SpecialPowers.pushPrefEnv({"set": newPrefs}, function () {
+ blockDisplay = SpecialPowers.getBoolPref("security.mixed_content.block_display_content");
+ blockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
+ counter++;
+ callback();
+ });
+ }
+
+ var testsToRun = {
+ /* https - Tests already run as part of bug 62178. */
+ about: false,
+ resource: false,
+ unsafe_about: false,
+ data_protocol: false,
+ javascript: false,
+ };
+
+ if (AppConstants.platform !== "android") {
+ // WebSocket tests are not supported on Android Yet. Bug 1566168.
+ testsToRun.wss = false;
+ testsToRun.mailto = false;
+ }
+
+ function log(msg) {
+ document.getElementById("log").textContent += "\n" + msg;
+ }
+
+ function reloadFrame() {
+ document.getElementById('framediv').innerHTML = '<iframe id="testHarness" src="https://example.com/tests/dom/security/test/mixedcontentblocker/file_main_bug803225.html"></iframe>';
+ }
+
+ function checkTestsCompleted() {
+ for (var prop in testsToRun) {
+ // some test hasn't run yet so we're not done
+ if (!testsToRun[prop])
+ return;
+ }
+ //if the testsToRun are all completed, change the pref and run the tests again until we have cycled through all the prefs.
+ if(counter < 4) {
+ for (var prop in testsToRun) {
+ testsToRun[prop] = false;
+ }
+ //call to change the preferences
+ changePrefs(function() {
+ log("\nblockDisplay set to "+blockDisplay+", blockActive set to "+blockActive+".");
+ reloadFrame();
+ });
+ }
+ else {
+ SimpleTest.finish();
+ }
+ }
+
+ var firstTest = true;
+
+ function receiveMessage(event) {
+ if(firstTest) {
+ log("blockDisplay set to "+blockDisplay+", blockActive set to "+blockActive+".");
+ firstTest = false;
+ }
+
+ log("test: "+event.data.test+", msg: "+event.data.msg + " logging message.");
+ // test that the load type matches the pref for this type of content
+ // (i.e. active vs. display)
+
+ switch(event.data.test) {
+
+ /* Mixed Script tests */
+ case "about":
+ ok(event.data.msg == "resource with about protocol loaded", "resource with about protocol did not load");
+ testsToRun.about = true;
+ break;
+
+ case "resource":
+ ok(event.data.msg == "resource with resource protocol loaded", "resource with resource protocol did not load");
+ testsToRun.resource = true;
+ break;
+
+ case "unsafe_about":
+ // This one should not load
+ ok(event.data.msg == "resource with unsafe about protocol did not load", "resource with unsafe about protocol loaded");
+ testsToRun.unsafe_about = true;
+ break;
+
+ case "data_protocol":
+ ok(event.data.msg == "resource with data protocol loaded", "resource with data protocol did not load");
+ testsToRun.data_protocol = true;
+ break;
+
+ case "javascript":
+ ok(event.data.msg == "resource with javascript protocol loaded", "resource with javascript protocol did not load");
+ testsToRun.javascript = true;
+ break;
+
+ case "wss":
+ ok(event.data.msg == "resource with wss protocol loaded", "resource with wss protocol did not load");
+ testsToRun.wss = true;
+ break;
+
+ case "mailto":
+ ok(event.data.msg == "resource with mailto protocol loaded", "resource with mailto protocol did not load");
+ testsToRun.mailto = true;
+ break;
+ }
+ checkTestsCompleted();
+ }
+
+ function startTest() {
+ //Set the first set of settings (true, true) and increment the counter.
+ changePrefs(function() {
+ // listen for a messages from the mixed content test harness
+ window.addEventListener("message", receiveMessage);
+
+ reloadFrame();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ </script>
+</head>
+
+<body onload='startTest()'>
+ <div id="framediv"></div>
+ <pre id="log"></pre>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/test_frameNavigation.html b/dom/security/test/mixedcontentblocker/test_frameNavigation.html
new file mode 100644
index 0000000000..82e3e715d2
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/test_frameNavigation.html
@@ -0,0 +1,127 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker
+https://bugzilla.mozilla.org/show_bug.cgi?id=840388
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 840388</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <script>
+ var counter = 0;
+ var origBlockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
+
+ SpecialPowers.setBoolPref("security.mixed_content.block_active_content", true);
+ var blockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
+
+
+ var testsToRunInsecure = {
+ insecurePage_navigate_child: false,
+ insecurePage_navigate_grandchild: false,
+ };
+
+ var testsToRunSecure = {
+ securePage_navigate_child: false,
+ blankTarget: false,
+ };
+
+ function log(msg) {
+ document.getElementById("log").textContent += "\n" + msg;
+ }
+
+ var secureTestsStarted = false;
+ async function checkTestsCompleted() {
+ for (var prop in testsToRunInsecure) {
+ // some test hasn't run yet so we're not done
+ if (!testsToRunInsecure[prop])
+ return;
+ }
+ // If we are here, all the insecure tests have run.
+ // If we haven't changed the iframe to run the secure tests, change it now.
+ if (!secureTestsStarted) {
+ document.getElementById('testing_frame').src = "https://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_secure.html";
+ secureTestsStarted = true;
+ }
+ for (var prop in testsToRunSecure) {
+ // some test hasn't run yet so we're not done
+ if (!testsToRunSecure[prop])
+ return;
+ }
+ //if the secure and insecure testsToRun are all completed, change the block mixed active content pref and run the tests again.
+ if(counter < 1) {
+ for (var prop in testsToRunSecure) {
+ testsToRunSecure[prop] = false;
+ }
+ for (var prop in testsToRunInsecure) {
+ testsToRunInsecure[prop] = false;
+ }
+ //call to change the preferences
+ counter++;
+ await SpecialPowers.setBoolPref("security.mixed_content.block_active_content", false);
+ blockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
+ log("blockActive set to "+blockActive+".");
+ secureTestsStarted = false;
+ document.getElementById('framediv').innerHTML = '<iframe src="http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation.html" id="testing_frame"></iframe>';
+ }
+ else {
+ //set the prefs back to what they were set to originally
+ SpecialPowers.setBoolPref("security.mixed_content.block_active_content", origBlockActive);
+ SimpleTest.finish();
+ }
+ }
+
+ var firstTestDebugMessage = true;
+
+ // listen for a messages from the mixed content test harness
+ window.addEventListener("message", receiveMessage);
+ function receiveMessage(event) {
+ if(firstTestDebugMessage) {
+ log("blockActive set to "+blockActive);
+ firstTestDebugMessage = false;
+ }
+
+ log("test: "+event.data.test+", msg: "+event.data.msg + ".");
+ // test that the load type matches the pref for this type of content
+ // (i.e. active vs. display)
+
+ switch(event.data.test) {
+
+ case "insecurePage_navigate_child":
+ is(event.data.msg, "navigated to insecure iframe on insecure page", "navigating to insecure iframe blocked on insecure page");
+ testsToRunInsecure.insecurePage_navigate_child = true;
+ break;
+
+ case "insecurePage_navigate_grandchild":
+ is(event.data.msg, "navigated to insecure grandchild iframe on insecure page", "navigating to insecure grandchild iframe blocked on insecure page");
+ testsToRunInsecure.insecurePage_navigate_grandchild = true;
+ break;
+
+ case "securePage_navigate_child":
+ ok(blockActive == (event.data.msg == "navigating to insecure iframe blocked on secure page"), "navigated to insecure iframe on secure page");
+ testsToRunSecure.securePage_navigate_child = true;
+ break;
+
+ case "blankTarget":
+ is(event.data.msg, "opened an http link with target=_blank from a secure page", "couldn't open an http link in a new window from a secure page");
+ testsToRunSecure.blankTarget = true;
+ break;
+
+ }
+ checkTestsCompleted();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ </script>
+</head>
+
+<body>
+ <div id="framediv">
+ <iframe src="http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation.html" id="testing_frame"></iframe>
+ </div>
+
+ <pre id="log"></pre>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/test_main.html b/dom/security/test/mixedcontentblocker/test_main.html
new file mode 100644
index 0000000000..9a13a0853f
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/test_main.html
@@ -0,0 +1,231 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker
+https://bugzilla.mozilla.org/show_bug.cgi?id=62178
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 62178</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <script>
+ let counter = 0;
+ // blockDisplay blockActive upgradeDisplay
+ const settings = [
+ [true, true, true],
+ [true, false, true],
+ [false, true, true],
+ [false, false, true],
+ [true, true, false],
+ [true, false, false],
+ [false, true, false],
+ [false, false, false],
+ ];
+
+ let blockActive;
+ let blockDisplay;
+ let upgradeDisplay;
+
+ //Cycle through 8 different preference settings.
+ function changePrefs(otherPrefs, callback) {
+ let basePrefs = [["security.mixed_content.block_display_content", settings[counter][0]],
+ ["security.mixed_content.block_active_content", settings[counter][1]],
+ ["security.mixed_content.upgrade_display_content", settings[counter][2]]];
+ let newPrefs = basePrefs.concat(otherPrefs);
+
+ SpecialPowers.pushPrefEnv({"set": newPrefs}, function () {
+ blockDisplay = SpecialPowers.getBoolPref("security.mixed_content.block_display_content");
+ blockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
+ upgradeDisplay = SpecialPowers.getBoolPref("security.mixed_content.upgrade_display_content");
+ counter++;
+ callback();
+ });
+ }
+
+ let testsToRun = {
+ iframe: false,
+ image: false,
+ imageSrcset: false,
+ imageSrcsetFallback: false,
+ imagePicture: false,
+ imageJoinPicture: false,
+ imageLeavePicture: false,
+ script: false,
+ stylesheet: false,
+ object: false,
+ media: false,
+ xhr: false,
+ };
+
+ function log(msg) {
+ document.getElementById("log").textContent += "\n" + msg;
+ }
+
+ function reloadFrame() {
+ document.getElementById('framediv').innerHTML = '<iframe id="testHarness" src="https://example.com/tests/dom/security/test/mixedcontentblocker/file_main.html"></iframe>';
+ }
+
+ function checkTestsCompleted() {
+ for (var prop in testsToRun) {
+ // some test hasn't run yet so we're not done
+ if (!testsToRun[prop])
+ return;
+ }
+ //if the testsToRun are all completed, chnage the pref and run the tests again until we have cycled through all the prefs.
+ if(counter < 8) {
+ for (var prop in testsToRun) {
+ testsToRun[prop] = false;
+ }
+ //call to change the preferences
+ changePrefs([], function() {
+ log(`\nblockDisplay set to ${blockDisplay}, blockActive set to ${blockActive}, upgradeDisplay set to ${upgradeDisplay}`);
+ reloadFrame();
+ });
+ }
+ else {
+ SimpleTest.finish();
+ }
+ }
+
+ var firstTest = true;
+
+ function receiveMessage(event) {
+ if(firstTest) {
+ log(`blockActive set to ${blockActive}, blockDisplay set to ${blockDisplay}, upgradeDisplay set to ${upgradeDisplay}.`);
+ firstTest = false;
+ }
+
+ // Simple check from the iframe.
+ if (event.data.check) {
+ ok(event.data.status, event.data.msg);
+ return;
+ }
+
+ log("test: "+event.data.test+", msg: "+event.data.msg + " logging message.");
+ // test that the load type matches the pref for this type of content
+ // (i.e. active vs. display)
+
+ switch(event.data.test) {
+
+ /* Mixed Script tests */
+ case "iframe":
+ ok(blockActive == (event.data.msg == "insecure iframe blocked"), "iframe did not follow block_active_content pref");
+ testsToRun.iframe = true;
+ break;
+
+ case "object":
+ ok(blockActive == (event.data.msg == "insecure object blocked"), "object did not follow block_active_content pref");
+ testsToRun.object = true;
+ break;
+
+ case "script":
+ ok(blockActive == (event.data.msg == "insecure script blocked"), "script did not follow block_active_content pref");
+ testsToRun.script = true;
+ break;
+
+ case "stylesheet":
+ ok(blockActive == (event.data.msg == "insecure stylesheet blocked"), "stylesheet did not follow block_active_content pref");
+ testsToRun.stylesheet = true;
+ break;
+
+ case "xhr":
+ ok(blockActive == (event.data.msg == "insecure xhr blocked"), "xhr did not follow block_active_content pref");
+ testsToRun.xhr = true;
+ break;
+
+ /* Mixed Display tests */
+ case "image":
+ //test that the image load matches the pref for display content
+ if (upgradeDisplay) {
+ ok(event.data.msg == "secure image loaded after upgrade", "image did not follow upgrade_display_content pref");
+ } else {
+ ok(blockDisplay == (event.data.msg == "insecure image blocked"), "image did not follow block_display_content pref");
+ }
+ testsToRun.image = true;
+ break;
+
+ case "media":
+ if (upgradeDisplay) {
+ ok(event.data.msg == "secure media loaded after upgrade", "media did not follow upgrade_display_content pref");
+ } else {
+ ok(blockDisplay == (event.data.msg == "insecure media blocked"), "media did not follow block_display_content pref");
+ }
+ testsToRun.media = true;
+ break;
+
+ /* Images using the "imageset" policy, from <img srcset> and <picture>, do not get the mixed display exception */
+ case "imageSrcset":
+ // When blockDisplay && blockActive && upgradeDisplay are all true the request is blocked
+ // This appears to be a side effect of blockDisplay taking precedence here.
+ if (event.data.msg == "secure image loaded after upgrade") {
+ ok(upgradeDisplay, "imageSrcset did not follow upgrade_display_content pref");
+ } else {
+ ok(blockActive == (event.data.msg == "insecure image blocked"), "imageSrcset did not follow block_active_content pref");
+ }
+ testsToRun.imageSrcset = true;
+ break;
+
+ case "imageSrcsetFallback":
+ if (event.data.msg == "secure image loaded after upgrade") {
+ ok(upgradeDisplay, "imageSrcsetFallback did not follow upgrade_display_content pref");
+ } else {
+ ok(blockActive == (event.data.msg == "insecure image blocked"), "imageSrcsetFallback did not follow block_active_content pref");
+ }
+ testsToRun.imageSrcsetFallback = true;
+ break;
+
+ case "imagePicture":
+ if (event.data.msg == "secure image loaded after upgrade") {
+ ok(upgradeDisplay, "imagePicture did not follow upgrade_display_content pref");
+ } else {
+ ok(blockActive == (event.data.msg == "insecure image blocked"), "imagePicture did not follow block_active_content pref");
+ }
+ testsToRun.imagePicture = true;
+ break;
+
+ case "imageJoinPicture":
+ if (event.data.msg == "secure image loaded after upgrade") {
+ ok(upgradeDisplay, "imageJoinPicture did not follow upgrade_display_content pref");
+ } else {
+ ok(blockActive == (event.data.msg == "insecure image blocked"), "imageJoinPicture did not follow block_active_content pref");
+ }
+ testsToRun.imageJoinPicture = true;
+ break;
+
+ // Should return to mixed display mode
+ case "imageLeavePicture":
+ if (event.data.msg == "secure image loaded after upgrade") {
+ ok(upgradeDisplay, "imageLeavePicture did not follow upgrade_display_content pref");
+ } else {
+ ok(blockDisplay == (event.data.msg == "insecure image blocked"), "imageLeavePicture did not follow block_display_content pref");
+ }
+ testsToRun.imageLeavePicture = true;
+ break;
+
+ }
+ checkTestsCompleted();
+ }
+
+ function startTest() {
+ //Set the first set of mixed content settings and increment the counter.
+ changePrefs([], function() {
+ //listen for a messages from the mixed content test harness
+ window.addEventListener("message", receiveMessage);
+
+ //Kick off test
+ reloadFrame();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ </script>
+</head>
+
+<body onload='startTest()'>
+ <div id="framediv"></div>
+ <pre id="log"></pre>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/test_redirect.html b/dom/security/test/mixedcontentblocker/test_redirect.html
new file mode 100644
index 0000000000..3fdd4e2e7b
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/test_redirect.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug1402363: Test mixed content redirects</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body onload='startTest()'>
+<iframe style="width:100%;height:300px;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+const PATH = "https://example.com/tests/dom/security/test/mixedcontentblocker/";
+let testcounter = 0;
+
+window.addEventListener("message", receiveMessage);
+function receiveMessage(event) {
+ if (event.data === "https-to-https-loaded") {
+ ok(true, "https to https fetch redirect should be allowed");
+ }
+ else if (event.data === "https-to-http-blocked") {
+ ok(true, "https to http fetch redirect should be blocked");
+ }
+ else {
+ ok(false, "sanity: we should never enter that branch (" + event.data + ")");
+ }
+ testcounter++;
+ if (testcounter < 2) {
+ return;
+ }
+ window.removeEventListener("message", receiveMessage);
+ SimpleTest.finish();
+}
+
+function startTest() {
+ let testframe = document.getElementById("testframe");
+ testframe.src = PATH + "file_redirect.html";
+}
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/test_windowOpen.html b/dom/security/test/mixedcontentblocker/test_windowOpen.html
new file mode 100644
index 0000000000..ae286c38f8
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/test_windowOpen.html
@@ -0,0 +1,82 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Mixed Content Navigation with window.open</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+let testsCompleted = 0;
+const numberOfTestCases = 2;
+
+function markTestCaseComplete() {
+ testsCompleted++;
+
+ if (testsCompleted == numberOfTestCases) {
+ SimpleTest.finish();
+ }
+}
+
+window.onmessage = function(event) {
+ if (event.data.src.includes("test1")) {
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ is(event.data.target, "http://test1.example.com/tests/dom/security/test/mixedcontentblocker/file_windowOpen.html", "error thrown for failed iframe load should be from test1's iframe.");
+ is(event.data.outcome, "blocked", "http iframe should be blocked from loading in child https window.");
+ is(event.data.method, "http", "messages from test1 iframe should be http.");
+ markTestCaseComplete();
+ }
+ else if (event.data.src.includes("test2")) {
+ if (event.data.outcome != 'csp-error') {
+ is(event.data.target, "https://test2.example.com/tests/dom/security/test/mixedcontentblocker/file_windowOpen.html", "event message received for successful iframe load should be from test2's iframe.");
+ is(event.data.triggeringPrincipal, "https://example.com/tests/dom/security/test/mixedcontentblocker/test_windowOpen.html", "triggeringPrincipal for successfully loaded https iframe should be the original test file.");
+ is(event.data.outcome, "loaded", "https iframe should be allowed to load in child https window.");
+ is(event.data.method, "https", "messages from test2 iframe should be https");
+ }
+ markTestCaseComplete();
+ }
+};
+
+function testURLInOpenedWindow(testURL) {
+ let openedWindow = window.open("javascript:''","_blank");
+ openedWindow.onload = function() {
+ openedWindow.document.body.innerHTML = '<iframe id="testframe">'
+
+ let testframe = openedWindow.document.getElementById("testframe");
+ testframe.onload = function(event) {
+ try {
+ let triggeringPrincipal = SpecialPowers.wrap(this.contentWindow).docShell.currentDocumentChannel.loadInfo.triggeringPrincipal.asciiSpec;
+ openedWindow.opener.postMessage({outcome: 'loaded', method: this.src.split(":")[0], src: this.src, target: event.target.src, triggeringPrincipal}, '*');
+ }
+ catch (error) {
+ // If we can't get the docShell due to CSP blocking access to the iframe's docShell then skip this test case
+ if (error.name === "SecurityError" && error.message === 'Permission denied to access property "docShell" on cross-origin object') {
+ openedWindow.opener.postMessage({outcome: 'csp-error', method: this.src.split(":")[0], src: this.src}, '*');
+ }
+ else throw error;
+ }
+ openedWindow.close();
+ }
+ testframe.onerror = function(error) {
+ openedWindow.opener.postMessage({outcome: 'blocked', method: this.src.split(":")[0], src: this.src, target: error.target.src}, '*');
+ openedWindow.close();
+ }
+
+ testframe.src = testURL;
+ };
+};
+
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+testURLInOpenedWindow("http://test1.example.com/tests/dom/security/test/mixedcontentblocker/file_windowOpen.html");
+testURLInOpenedWindow("https://test2.example.com/tests/dom/security/test/mixedcontentblocker/file_windowOpen.html");
+
+</script>
+</body>
+</html>