summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/siteIdentity
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /browser/base/content/test/siteIdentity
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/base/content/test/siteIdentity')
-rw-r--r--browser/base/content/test/siteIdentity/browser.ini152
-rw-r--r--browser/base/content/test/siteIdentity/browser_about_blank_same_document_tabswitch.js79
-rw-r--r--browser/base/content/test/siteIdentity/browser_bug1045809.js105
-rw-r--r--browser/base/content/test/siteIdentity/browser_bug822367.js254
-rw-r--r--browser/base/content/test/siteIdentity/browser_bug902156.js171
-rw-r--r--browser/base/content/test/siteIdentity/browser_bug906190.js340
-rw-r--r--browser/base/content/test/siteIdentity/browser_check_identity_state.js882
-rw-r--r--browser/base/content/test/siteIdentity/browser_check_identity_state_pdf.js77
-rw-r--r--browser/base/content/test/siteIdentity/browser_csp_block_all_mixedcontent.js60
-rw-r--r--browser/base/content/test/siteIdentity/browser_deprecatedTLSVersions.js94
-rw-r--r--browser/base/content/test/siteIdentity/browser_geolocation_indicator.js381
-rw-r--r--browser/base/content/test/siteIdentity/browser_getSecurityInfo.js35
-rw-r--r--browser/base/content/test/siteIdentity/browser_identityBlock_flicker.js52
-rw-r--r--browser/base/content/test/siteIdentity/browser_identityBlock_focus.js126
-rw-r--r--browser/base/content/test/siteIdentity/browser_identityIcon_img_url.js148
-rw-r--r--browser/base/content/test/siteIdentity/browser_identityPopup_HttpsOnlyMode.js191
-rw-r--r--browser/base/content/test/siteIdentity/browser_identityPopup_clearSiteData.js245
-rw-r--r--browser/base/content/test/siteIdentity/browser_identityPopup_clearSiteData_extensions.js80
-rw-r--r--browser/base/content/test/siteIdentity/browser_identityPopup_custom_roots.js82
-rw-r--r--browser/base/content/test/siteIdentity/browser_identityPopup_focus.js120
-rw-r--r--browser/base/content/test/siteIdentity/browser_identity_UI.js192
-rw-r--r--browser/base/content/test/siteIdentity/browser_iframe_navigation.js108
-rw-r--r--browser/base/content/test/siteIdentity/browser_ignore_same_page_navigation.js50
-rw-r--r--browser/base/content/test/siteIdentity/browser_mcb_redirect.js360
-rw-r--r--browser/base/content/test/siteIdentity/browser_mixedContentFramesOnHttp.js37
-rw-r--r--browser/base/content/test/siteIdentity/browser_mixedContentFromOnunload.js68
-rw-r--r--browser/base/content/test/siteIdentity/browser_mixed_content_cert_override.js69
-rw-r--r--browser/base/content/test/siteIdentity/browser_mixed_content_with_navigation.js131
-rw-r--r--browser/base/content/test/siteIdentity/browser_mixed_passive_content_indicator.js18
-rw-r--r--browser/base/content/test/siteIdentity/browser_mixedcontent_securityflags.js71
-rw-r--r--browser/base/content/test/siteIdentity/browser_navigation_failures.js166
-rw-r--r--browser/base/content/test/siteIdentity/browser_no_mcb_for_loopback.js88
-rw-r--r--browser/base/content/test/siteIdentity/browser_no_mcb_for_onions.js41
-rw-r--r--browser/base/content/test/siteIdentity/browser_no_mcb_on_http_site.js133
-rw-r--r--browser/base/content/test/siteIdentity/browser_secure_transport_insecure_scheme.js185
-rw-r--r--browser/base/content/test/siteIdentity/browser_session_store_pageproxystate.js92
-rw-r--r--browser/base/content/test/siteIdentity/browser_tab_sharing_state.js96
-rw-r--r--browser/base/content/test/siteIdentity/dummy_iframe_page.html10
-rw-r--r--browser/base/content/test/siteIdentity/dummy_page.html10
-rw-r--r--browser/base/content/test/siteIdentity/file_bug1045809_1.html7
-rw-r--r--browser/base/content/test/siteIdentity/file_bug1045809_2.html7
-rw-r--r--browser/base/content/test/siteIdentity/file_bug822367_1.html18
-rw-r--r--browser/base/content/test/siteIdentity/file_bug822367_1.js1
-rw-r--r--browser/base/content/test/siteIdentity/file_bug822367_2.html16
-rw-r--r--browser/base/content/test/siteIdentity/file_bug822367_3.html27
-rw-r--r--browser/base/content/test/siteIdentity/file_bug822367_4.html18
-rw-r--r--browser/base/content/test/siteIdentity/file_bug822367_4.js2
-rw-r--r--browser/base/content/test/siteIdentity/file_bug822367_4B.html18
-rw-r--r--browser/base/content/test/siteIdentity/file_bug822367_5.html23
-rw-r--r--browser/base/content/test/siteIdentity/file_bug822367_6.html16
-rw-r--r--browser/base/content/test/siteIdentity/file_bug902156.js6
-rw-r--r--browser/base/content/test/siteIdentity/file_bug902156_1.html15
-rw-r--r--browser/base/content/test/siteIdentity/file_bug902156_2.html17
-rw-r--r--browser/base/content/test/siteIdentity/file_bug902156_3.html15
-rw-r--r--browser/base/content/test/siteIdentity/file_bug906190.js6
-rw-r--r--browser/base/content/test/siteIdentity/file_bug906190.sjs18
-rw-r--r--browser/base/content/test/siteIdentity/file_bug906190_1.html15
-rw-r--r--browser/base/content/test/siteIdentity/file_bug906190_2.html15
-rw-r--r--browser/base/content/test/siteIdentity/file_bug906190_3_4.html14
-rw-r--r--browser/base/content/test/siteIdentity/file_bug906190_redirected.html15
-rw-r--r--browser/base/content/test/siteIdentity/file_csp_block_all_mixedcontent.html11
-rw-r--r--browser/base/content/test/siteIdentity/file_csp_block_all_mixedcontent.js3
-rw-r--r--browser/base/content/test/siteIdentity/file_mixedContentFramesOnHttp.html14
-rw-r--r--browser/base/content/test/siteIdentity/file_mixedContentFromOnunload.html18
-rw-r--r--browser/base/content/test/siteIdentity/file_mixedContentFromOnunload_test1.html14
-rw-r--r--browser/base/content/test/siteIdentity/file_mixedContentFromOnunload_test2.html15
-rw-r--r--browser/base/content/test/siteIdentity/file_mixedPassiveContent.html13
-rw-r--r--browser/base/content/test/siteIdentity/file_pdf.pdf12
-rw-r--r--browser/base/content/test/siteIdentity/file_pdf_blob.html18
-rw-r--r--browser/base/content/test/siteIdentity/head.js435
-rw-r--r--browser/base/content/test/siteIdentity/iframe_navigation.html44
-rw-r--r--browser/base/content/test/siteIdentity/insecure_opener.html9
-rw-r--r--browser/base/content/test/siteIdentity/open-self-from-frame.html6
-rw-r--r--browser/base/content/test/siteIdentity/simple_mixed_passive.html1
-rw-r--r--browser/base/content/test/siteIdentity/test-mixedcontent-securityerrors.html21
-rw-r--r--browser/base/content/test/siteIdentity/test_mcb_double_redirect_image.html23
-rw-r--r--browser/base/content/test/siteIdentity/test_mcb_redirect.html15
-rw-r--r--browser/base/content/test/siteIdentity/test_mcb_redirect.js5
-rw-r--r--browser/base/content/test/siteIdentity/test_mcb_redirect.sjs29
-rw-r--r--browser/base/content/test/siteIdentity/test_mcb_redirect_image.html23
-rw-r--r--browser/base/content/test/siteIdentity/test_no_mcb_for_loopback.html56
-rw-r--r--browser/base/content/test/siteIdentity/test_no_mcb_for_onions.html29
-rw-r--r--browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.css11
-rw-r--r--browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.html44
-rw-r--r--browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.css1
-rw-r--r--browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.html45
-rw-r--r--browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.css3
-rw-r--r--browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.html44
88 files changed, 6850 insertions, 0 deletions
diff --git a/browser/base/content/test/siteIdentity/browser.ini b/browser/base/content/test/siteIdentity/browser.ini
new file mode 100644
index 0000000000..724669f18a
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser.ini
@@ -0,0 +1,152 @@
+[DEFAULT]
+support-files =
+ head.js
+ dummy_page.html
+ !/image/test/mochitest/blue.png
+
+[browser_about_blank_same_document_tabswitch.js]
+https_first_disabled = true
+support-files =
+ open-self-from-frame.html
+[browser_bug1045809.js]
+tags = mcb
+support-files =
+ file_bug1045809_1.html
+ file_bug1045809_2.html
+[browser_bug822367.js]
+tags = mcb
+support-files =
+ file_bug822367_1.html
+ file_bug822367_1.js
+ file_bug822367_2.html
+ file_bug822367_3.html
+ file_bug822367_4.html
+ file_bug822367_4.js
+ file_bug822367_4B.html
+ file_bug822367_5.html
+ file_bug822367_6.html
+[browser_bug902156.js]
+tags = mcb
+support-files =
+ file_bug902156.js
+ file_bug902156_1.html
+ file_bug902156_2.html
+ file_bug902156_3.html
+[browser_bug906190.js]
+tags = mcb
+support-files =
+ file_bug906190_1.html
+ file_bug906190_2.html
+ file_bug906190_3_4.html
+ file_bug906190_redirected.html
+ file_bug906190.js
+ file_bug906190.sjs
+[browser_check_identity_state.js]
+https_first_disabled = true
+skip-if =
+ os == "win" && os_version == "6.1" # Skip on Azure - frequent failure
+[browser_check_identity_state_pdf.js]
+https_first_disabled = true
+support-files =
+ file_pdf.pdf
+ file_pdf_blob.html
+[browser_csp_block_all_mixedcontent.js]
+tags = mcb
+support-files =
+ file_csp_block_all_mixedcontent.html
+ file_csp_block_all_mixedcontent.js
+[browser_deprecatedTLSVersions.js]
+[browser_geolocation_indicator.js]
+[browser_getSecurityInfo.js]
+https_first_disabled = true
+support-files =
+ dummy_iframe_page.html
+[browser_identityBlock_flicker.js]
+[browser_identityBlock_focus.js]
+support-files = ../permissions/permissions.html
+[browser_identityIcon_img_url.js]
+https_first_disabled = true
+support-files =
+ file_mixedPassiveContent.html
+ file_csp_block_all_mixedcontent.html
+[browser_identityPopup_HttpsOnlyMode.js]
+[browser_identityPopup_clearSiteData.js]
+skip-if = (os == "linux" && bits == 64) # Bug 1577395
+[browser_identityPopup_clearSiteData_extensions.js]
+[browser_identityPopup_custom_roots.js]
+https_first_disabled = true
+[browser_identityPopup_focus.js]
+skip-if =
+ verify
+ os == "linux" && (asan || tsan) # Bug 1723899
+[browser_identity_UI.js]
+https_first_disabled = true
+[browser_iframe_navigation.js]
+https_first_disabled = true
+support-files =
+ iframe_navigation.html
+[browser_ignore_same_page_navigation.js]
+[browser_mcb_redirect.js]
+https_first_disabled = true
+tags = mcb
+support-files =
+ test_mcb_redirect.html
+ test_mcb_redirect_image.html
+ test_mcb_double_redirect_image.html
+ test_mcb_redirect.js
+ test_mcb_redirect.sjs
+[browser_mixedContentFramesOnHttp.js]
+https_first_disabled = true
+tags = mcb
+support-files =
+ file_mixedContentFramesOnHttp.html
+ file_mixedPassiveContent.html
+[browser_mixedContentFromOnunload.js]
+https_first_disabled = true
+tags = mcb
+support-files =
+ file_mixedContentFromOnunload.html
+ file_mixedContentFromOnunload_test1.html
+ file_mixedContentFromOnunload_test2.html
+[browser_mixed_content_cert_override.js]
+skip-if = verify
+tags = mcb
+support-files =
+ test-mixedcontent-securityerrors.html
+[browser_mixed_content_with_navigation.js]
+tags = mcb
+support-files =
+ file_mixedPassiveContent.html
+ file_bug1045809_1.html
+[browser_mixed_passive_content_indicator.js]
+tags = mcb
+support-files =
+ simple_mixed_passive.html
+[browser_mixedcontent_securityflags.js]
+tags = mcb
+support-files =
+ test-mixedcontent-securityerrors.html
+[browser_navigation_failures.js]
+[browser_no_mcb_for_loopback.js]
+tags = mcb
+support-files =
+ ../general/moz.png
+ test_no_mcb_for_loopback.html
+[browser_no_mcb_for_onions.js]
+tags = mcb
+support-files =
+ test_no_mcb_for_onions.html
+[browser_no_mcb_on_http_site.js]
+https_first_disabled = true
+tags = mcb
+support-files =
+ test_no_mcb_on_http_site_img.html
+ test_no_mcb_on_http_site_img.css
+ test_no_mcb_on_http_site_font.html
+ test_no_mcb_on_http_site_font.css
+ test_no_mcb_on_http_site_font2.html
+ test_no_mcb_on_http_site_font2.css
+[browser_secure_transport_insecure_scheme.js]
+https_first_disabled = true
+[browser_session_store_pageproxystate.js]
+[browser_tab_sharing_state.js]
diff --git a/browser/base/content/test/siteIdentity/browser_about_blank_same_document_tabswitch.js b/browser/base/content/test/siteIdentity/browser_about_blank_same_document_tabswitch.js
new file mode 100644
index 0000000000..5e58b4bedb
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_about_blank_same_document_tabswitch.js
@@ -0,0 +1,79 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.org"
+);
+
+const TEST_PAGE = TEST_PATH + "open-self-from-frame.html";
+
+add_task(async function test_identityBlock_inherited_blank() {
+ await BrowserTestUtils.withNewTab("https://example.com", async browser => {
+ let identityBox = document.getElementById("identity-box");
+ // Ensure we remove the 3rd party storage permission for example.org, or
+ // it'll mess up other tests:
+ let principal = browser.contentPrincipal;
+ registerCleanupFunction(() => {
+ Services.perms.removeFromPrincipal(
+ principal,
+ "3rdPartyStorage^http://example.org"
+ );
+ });
+ is(
+ identityBox.className,
+ "verifiedDomain",
+ "Should indicate a secure site."
+ );
+ // Open a popup from the web content.
+ let popupPromise = BrowserTestUtils.waitForNewWindow();
+ await SpecialPowers.spawn(browser, [TEST_PAGE], testPage => {
+ content.open(testPage, "_blank", "height=300,width=300");
+ });
+ // Open a tab back in the main window:
+ let popup = await popupPromise;
+ info("Opened popup");
+ let popupBC = popup.gBrowser.selectedBrowser.browsingContext;
+ await TestUtils.waitForCondition(
+ () => popupBC.children[0]?.currentWindowGlobal
+ );
+
+ info("Waiting for button to appear");
+ await SpecialPowers.spawn(popupBC.children[0], [], async () => {
+ await ContentTaskUtils.waitForCondition(() =>
+ content.document.querySelector("button")
+ );
+ });
+
+ info("Got frame contents.");
+
+ let otherTabPromise = BrowserTestUtils.waitForLocationChange(
+ gBrowser,
+ TEST_PAGE
+ );
+ info("Clicking button");
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "button",
+ {},
+ popupBC.children[0]
+ );
+ info("Waiting for tab");
+ await otherTabPromise;
+
+ ok(
+ gURLBar.value.startsWith("example.org/"),
+ "URL bar value should be correct, was " + gURLBar.value
+ );
+ is(
+ identityBox.className,
+ "notSecure",
+ "Identity box should have been updated."
+ );
+
+ await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ await BrowserTestUtils.closeWindow(popup);
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/browser_bug1045809.js b/browser/base/content/test/siteIdentity/browser_bug1045809.js
new file mode 100644
index 0000000000..b39d669d0b
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_bug1045809.js
@@ -0,0 +1,105 @@
+// Test that the Mixed Content Doorhanger Action to re-enable protection works
+
+const PREF_ACTIVE = "security.mixed_content.block_active_content";
+const PREF_INSECURE = "security.insecure_connection_icon.enabled";
+const TEST_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "file_bug1045809_1.html";
+
+var origBlockActive;
+
+add_task(async function () {
+ registerCleanupFunction(function () {
+ Services.prefs.setBoolPref(PREF_ACTIVE, origBlockActive);
+ gBrowser.removeCurrentTab();
+ });
+
+ // Store original preferences so we can restore settings after testing
+ origBlockActive = Services.prefs.getBoolPref(PREF_ACTIVE);
+
+ // Make sure mixed content blocking is on
+ Services.prefs.setBoolPref(PREF_ACTIVE, true);
+
+ let tab = (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser));
+
+ // Check with insecure lock disabled
+ await SpecialPowers.pushPrefEnv({ set: [[PREF_INSECURE, false]] });
+ await runTests(tab);
+
+ // Check with insecure lock disabled
+ await SpecialPowers.pushPrefEnv({ set: [[PREF_INSECURE, true]] });
+ await runTests(tab);
+});
+
+async function runTests(tab) {
+ // Test 1: mixed content must be blocked
+ await promiseTabLoadEvent(tab, TEST_URL);
+ await test1(gBrowser.getBrowserForTab(tab));
+
+ await promiseTabLoadEvent(tab);
+ // Test 2: mixed content must NOT be blocked
+ await test2(gBrowser.getBrowserForTab(tab));
+
+ // Test 3: mixed content must be blocked again
+ await promiseTabLoadEvent(tab);
+ await test3(gBrowser.getBrowserForTab(tab));
+}
+
+async function test1(gTestBrowser) {
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: false,
+ });
+
+ await SpecialPowers.spawn(gTestBrowser, [], function () {
+ let iframe = content.document.getElementsByTagName("iframe")[0];
+
+ SpecialPowers.spawn(iframe, [], () => {
+ let container = content.document.getElementById("mixedContentContainer");
+ is(container, null, "Mixed Content is NOT to be found in Test1");
+ });
+ });
+
+ // Disable Mixed Content Protection for the page (and reload)
+ gIdentityHandler.disableMixedContentProtection();
+}
+
+async function test2(gTestBrowser) {
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: true,
+ activeBlocked: false,
+ passiveLoaded: false,
+ });
+
+ await SpecialPowers.spawn(gTestBrowser, [], function () {
+ let iframe = content.document.getElementsByTagName("iframe")[0];
+
+ SpecialPowers.spawn(iframe, [], () => {
+ let container = content.document.getElementById("mixedContentContainer");
+ isnot(container, null, "Mixed Content is to be found in Test2");
+ });
+ });
+
+ // Re-enable Mixed Content Protection for the page (and reload)
+ gIdentityHandler.enableMixedContentProtection();
+}
+
+async function test3(gTestBrowser) {
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: false,
+ });
+
+ await SpecialPowers.spawn(gTestBrowser, [], function () {
+ let iframe = content.document.getElementsByTagName("iframe")[0];
+
+ SpecialPowers.spawn(iframe, [], () => {
+ let container = content.document.getElementById("mixedContentContainer");
+ is(container, null, "Mixed Content is NOT to be found in Test3");
+ });
+ });
+}
diff --git a/browser/base/content/test/siteIdentity/browser_bug822367.js b/browser/base/content/test/siteIdentity/browser_bug822367.js
new file mode 100644
index 0000000000..881c920899
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_bug822367.js
@@ -0,0 +1,254 @@
+/*
+ * User Override Mixed Content Block - Tests for Bug 822367
+ */
+
+const PREF_DISPLAY = "security.mixed_content.block_display_content";
+const PREF_DISPLAY_UPGRADE = "security.mixed_content.upgrade_display_content";
+const PREF_ACTIVE = "security.mixed_content.block_active_content";
+
+// We alternate for even and odd test cases to simulate different hosts
+const HTTPS_TEST_ROOT = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+const HTTPS_TEST_ROOT_2 = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://test1.example.com"
+);
+
+var gTestBrowser = null;
+
+add_task(async function test() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [PREF_DISPLAY, true],
+ [PREF_DISPLAY_UPGRADE, false],
+ [PREF_ACTIVE, true],
+ ],
+ });
+
+ var newTab = BrowserTestUtils.addTab(gBrowser);
+ gBrowser.selectedTab = newTab;
+ gTestBrowser = gBrowser.selectedBrowser;
+ newTab.linkedBrowser.stop();
+
+ // Mixed Script Test
+ var url = HTTPS_TEST_ROOT + "file_bug822367_1.html";
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+ await BrowserTestUtils.browserLoaded(gTestBrowser, false, url);
+});
+
+// Mixed Script Test
+add_task(async function MixedTest1A() {
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: false,
+ });
+
+ gTestBrowser.ownerGlobal.gIdentityHandler.disableMixedContentProtection();
+ await BrowserTestUtils.browserLoaded(gTestBrowser);
+});
+
+add_task(async function MixedTest1B() {
+ await SpecialPowers.spawn(gTestBrowser, [], async function () {
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.getElementById("p1").innerHTML == "hello",
+ "Waited too long for mixed script to run in Test 1"
+ );
+ });
+ gTestBrowser.ownerGlobal.gIdentityHandler.enableMixedContentProtectionNoReload();
+});
+
+// Mixed Display Test - Doorhanger should not appear
+add_task(async function MixedTest2() {
+ var url = HTTPS_TEST_ROOT_2 + "file_bug822367_2.html";
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+ await BrowserTestUtils.browserLoaded(gTestBrowser, false, url);
+
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: false,
+ activeBlocked: false,
+ passiveLoaded: false,
+ });
+});
+
+// Mixed Script and Display Test - User Override should cause both the script and the image to load.
+add_task(async function MixedTest3() {
+ var url = HTTPS_TEST_ROOT + "file_bug822367_3.html";
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+ await BrowserTestUtils.browserLoaded(gTestBrowser, false, url);
+});
+
+add_task(async function MixedTest3A() {
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: false,
+ });
+
+ gTestBrowser.ownerGlobal.gIdentityHandler.disableMixedContentProtection();
+ await BrowserTestUtils.browserLoaded(gTestBrowser);
+});
+
+add_task(async function MixedTest3B() {
+ await SpecialPowers.spawn(gTestBrowser, [], async function () {
+ let p1 = ContentTaskUtils.waitForCondition(
+ () => content.document.getElementById("p1").innerHTML == "hello",
+ "Waited too long for mixed script to run in Test 3"
+ );
+ let p2 = ContentTaskUtils.waitForCondition(
+ () => content.document.getElementById("p2").innerHTML == "bye",
+ "Waited too long for mixed image to load in Test 3"
+ );
+ await Promise.all([p1, p2]);
+ });
+
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: true,
+ activeBlocked: false,
+ passiveLoaded: true,
+ });
+ gTestBrowser.ownerGlobal.gIdentityHandler.enableMixedContentProtectionNoReload();
+});
+
+// Location change - User override on one page doesn't propagate to another page after location change.
+add_task(async function MixedTest4() {
+ var url = HTTPS_TEST_ROOT_2 + "file_bug822367_4.html";
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+ await BrowserTestUtils.browserLoaded(gTestBrowser, false, url);
+});
+
+let preLocationChangePrincipal = null;
+add_task(async function MixedTest4A() {
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: false,
+ });
+
+ preLocationChangePrincipal = gTestBrowser.contentPrincipal;
+ gTestBrowser.ownerGlobal.gIdentityHandler.disableMixedContentProtection();
+ await BrowserTestUtils.browserLoaded(gTestBrowser);
+});
+
+add_task(async function MixedTest4B() {
+ let url = HTTPS_TEST_ROOT + "file_bug822367_4B.html";
+ await SpecialPowers.spawn(gTestBrowser, [url], async function (wantedUrl) {
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.location == wantedUrl,
+ "Waited too long for mixed script to run in Test 4"
+ );
+ });
+});
+
+add_task(async function MixedTest4C() {
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: false,
+ });
+
+ await SpecialPowers.spawn(gTestBrowser, [], async function () {
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.getElementById("p1").innerHTML == "",
+ "Mixed script loaded in test 4 after location change!"
+ );
+ });
+ SitePermissions.removeFromPrincipal(
+ preLocationChangePrincipal,
+ "mixed-content"
+ );
+});
+
+// Mixed script attempts to load in a document.open()
+add_task(async function MixedTest5() {
+ var url = HTTPS_TEST_ROOT + "file_bug822367_5.html";
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+ await BrowserTestUtils.browserLoaded(gTestBrowser, false, url);
+});
+
+add_task(async function MixedTest5A() {
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: false,
+ });
+
+ gTestBrowser.ownerGlobal.gIdentityHandler.disableMixedContentProtection();
+ await BrowserTestUtils.browserLoaded(gTestBrowser);
+});
+
+add_task(async function MixedTest5B() {
+ await SpecialPowers.spawn(gTestBrowser, [], async function () {
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.getElementById("p1").innerHTML == "hello",
+ "Waited too long for mixed script to run in Test 5"
+ );
+ });
+ gTestBrowser.ownerGlobal.gIdentityHandler.enableMixedContentProtectionNoReload();
+});
+
+// Mixed script attempts to load in a document.open() that is within an iframe.
+add_task(async function MixedTest6() {
+ var url = HTTPS_TEST_ROOT_2 + "file_bug822367_6.html";
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+ await BrowserTestUtils.browserLoaded(gTestBrowser, false, url);
+});
+
+add_task(async function MixedTest6A() {
+ gTestBrowser.removeEventListener("load", MixedTest6A, true);
+ let { gIdentityHandler } = gTestBrowser.ownerGlobal;
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"),
+ "Waited too long for control center to get mixed active blocked state"
+ );
+});
+
+add_task(async function MixedTest6B() {
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: false,
+ });
+
+ gTestBrowser.ownerGlobal.gIdentityHandler.disableMixedContentProtection();
+
+ await BrowserTestUtils.browserLoaded(gTestBrowser);
+});
+
+add_task(async function MixedTest6C() {
+ await SpecialPowers.spawn(gTestBrowser, [], async function () {
+ function test() {
+ try {
+ return (
+ content.document
+ .getElementById("f1")
+ .contentDocument.getElementById("p1").innerHTML == "hello"
+ );
+ } catch (e) {
+ return false;
+ }
+ }
+
+ await ContentTaskUtils.waitForCondition(
+ test,
+ "Waited too long for mixed script to run in Test 6"
+ );
+ });
+});
+
+add_task(async function MixedTest6D() {
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: true,
+ activeBlocked: false,
+ passiveLoaded: false,
+ });
+ gTestBrowser.ownerGlobal.gIdentityHandler.enableMixedContentProtectionNoReload();
+});
+
+add_task(async function cleanup() {
+ gBrowser.removeCurrentTab();
+});
diff --git a/browser/base/content/test/siteIdentity/browser_bug902156.js b/browser/base/content/test/siteIdentity/browser_bug902156.js
new file mode 100644
index 0000000000..3485771427
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_bug902156.js
@@ -0,0 +1,171 @@
+/*
+ * Description of the Tests for
+ * - Bug 902156: Persist "disable protection" option for Mixed Content Blocker
+ *
+ * 1. Navigate to the same domain via document.location
+ * - Load a html page which has mixed content
+ * - Control Center button to disable protection appears - we disable it
+ * - Load a new page from the same origin using document.location
+ * - Control Center button should not appear anymore!
+ *
+ * 2. Navigate to the same domain via simulateclick for a link on the page
+ * - Load a html page which has mixed content
+ * - Control Center button to disable protection appears - we disable it
+ * - Load a new page from the same origin simulating a click
+ * - Control Center button should not appear anymore!
+ *
+ * 3. Navigate to a differnet domain and show the content is still blocked
+ * - Load a different html page which has mixed content
+ * - Control Center button to disable protection should appear again because
+ * we navigated away from html page where we disabled the protection.
+ *
+ * Note, for all tests we set gHttpTestRoot to use 'https'.
+ */
+
+const PREF_ACTIVE = "security.mixed_content.block_active_content";
+
+// We alternate for even and odd test cases to simulate different hosts.
+const HTTPS_TEST_ROOT_1 = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://test1.example.com"
+);
+const HTTPS_TEST_ROOT_2 = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://test2.example.com"
+);
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({ set: [[PREF_ACTIVE, true]] });
+});
+
+add_task(async function test1() {
+ let url = HTTPS_TEST_ROOT_1 + "file_bug902156_1.html";
+ await BrowserTestUtils.withNewTab(url, async function (browser) {
+ await assertMixedContentBlockingState(browser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: false,
+ });
+
+ // Disable Mixed Content Protection for the page (and reload)
+ let browserLoaded = BrowserTestUtils.browserLoaded(browser, false, url);
+ let { gIdentityHandler } = browser.ownerGlobal;
+ gIdentityHandler.disableMixedContentProtection();
+ await browserLoaded;
+
+ await SpecialPowers.spawn(browser, [], async function () {
+ let expected = "Mixed Content Blocker disabled";
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.getElementById("mctestdiv").innerHTML == expected,
+ "Error: Waited too long for mixed script to run in Test 1"
+ );
+
+ let actual = content.document.getElementById("mctestdiv").innerHTML;
+ is(
+ actual,
+ "Mixed Content Blocker disabled",
+ "OK: Executed mixed script in Test 1"
+ );
+ });
+
+ // The Script loaded after we disabled the page, now we are going to reload the
+ // page and see if our decision is persistent
+ url = HTTPS_TEST_ROOT_1 + "file_bug902156_2.html";
+ browserLoaded = BrowserTestUtils.browserLoaded(browser, false, url);
+ BrowserTestUtils.loadURIString(browser, url);
+ await browserLoaded;
+
+ // The Control Center button should appear but isMixedContentBlocked should be NOT true,
+ // because our decision of disabling the mixed content blocker is persistent.
+ await assertMixedContentBlockingState(browser, {
+ activeLoaded: true,
+ activeBlocked: false,
+ passiveLoaded: false,
+ });
+ await SpecialPowers.spawn(browser, [], function () {
+ let actual = content.document.getElementById("mctestdiv").innerHTML;
+ is(
+ actual,
+ "Mixed Content Blocker disabled",
+ "OK: Executed mixed script in Test 1"
+ );
+ });
+ gIdentityHandler.enableMixedContentProtection();
+ });
+});
+
+// ------------------------ Test 2 ------------------------------
+
+add_task(async function test2() {
+ let url = HTTPS_TEST_ROOT_2 + "file_bug902156_2.html";
+ await BrowserTestUtils.withNewTab(url, async function (browser) {
+ await assertMixedContentBlockingState(browser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: false,
+ });
+
+ // Disable Mixed Content Protection for the page (and reload)
+ let browserLoaded = BrowserTestUtils.browserLoaded(browser, false, url);
+ let { gIdentityHandler } = browser.ownerGlobal;
+ gIdentityHandler.disableMixedContentProtection();
+ await browserLoaded;
+
+ await SpecialPowers.spawn(browser, [], async function () {
+ let expected = "Mixed Content Blocker disabled";
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.getElementById("mctestdiv").innerHTML == expected,
+ "Error: Waited too long for mixed script to run in Test 2"
+ );
+
+ let actual = content.document.getElementById("mctestdiv").innerHTML;
+ is(
+ actual,
+ "Mixed Content Blocker disabled",
+ "OK: Executed mixed script in Test 2"
+ );
+ });
+
+ // The Script loaded after we disabled the page, now we are going to reload the
+ // page and see if our decision is persistent
+ url = HTTPS_TEST_ROOT_2 + "file_bug902156_1.html";
+ browserLoaded = BrowserTestUtils.browserLoaded(browser, false, url);
+ // reload the page using the provided link in the html file
+ await SpecialPowers.spawn(browser, [], function () {
+ let mctestlink = content.document.getElementById("mctestlink");
+ mctestlink.click();
+ });
+ await browserLoaded;
+
+ // The Control Center button should appear but isMixedContentBlocked should be NOT true,
+ // because our decision of disabling the mixed content blocker is persistent.
+ await assertMixedContentBlockingState(browser, {
+ activeLoaded: true,
+ activeBlocked: false,
+ passiveLoaded: false,
+ });
+
+ await SpecialPowers.spawn(browser, [], function () {
+ let actual = content.document.getElementById("mctestdiv").innerHTML;
+ is(
+ actual,
+ "Mixed Content Blocker disabled",
+ "OK: Executed mixed script in Test 2"
+ );
+ });
+ gIdentityHandler.enableMixedContentProtection();
+ });
+});
+
+add_task(async function test3() {
+ let url = HTTPS_TEST_ROOT_1 + "file_bug902156_3.html";
+ await BrowserTestUtils.withNewTab(url, async function (browser) {
+ await assertMixedContentBlockingState(browser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: false,
+ });
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/browser_bug906190.js b/browser/base/content/test/siteIdentity/browser_bug906190.js
new file mode 100644
index 0000000000..a0410e76cb
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_bug906190.js
@@ -0,0 +1,340 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests the persistence of the "disable protection" option for Mixed Content
+ * Blocker in child tabs (bug 906190).
+ */
+
+requestLongerTimeout(2);
+
+// We use the different urls for testing same origin checks before allowing
+// mixed content on child tabs.
+const HTTPS_TEST_ROOT_1 = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://test1.example.com"
+);
+const HTTPS_TEST_ROOT_2 = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://test2.example.com"
+);
+
+/**
+ * For all tests, we load the pages over HTTPS and test both:
+ * - |CTRL+CLICK|
+ * - |RIGHT CLICK -> OPEN LINK IN TAB|
+ */
+async function doTest(
+ parentTabSpec,
+ childTabSpec,
+ testTaskFn,
+ waitForMetaRefresh
+) {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: parentTabSpec,
+ },
+ async function (browser) {
+ // As a sanity check, test that active content has been blocked as expected.
+ await assertMixedContentBlockingState(gBrowser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: false,
+ });
+
+ // Disable the Mixed Content Blocker for the page, which reloads it.
+ let promiseReloaded = BrowserTestUtils.browserLoaded(browser);
+ let principal = gBrowser.contentPrincipal;
+ gIdentityHandler.disableMixedContentProtection();
+ await promiseReloaded;
+
+ // Wait for the script in the page to update the contents of the test div.
+ await SpecialPowers.spawn(
+ browser,
+ [childTabSpec],
+ async childTabSpecContent => {
+ let testDiv = content.document.getElementById("mctestdiv");
+ await ContentTaskUtils.waitForCondition(
+ () => testDiv.innerHTML == "Mixed Content Blocker disabled"
+ );
+
+ // Add the link for the child tab to the page.
+ let mainDiv = content.document.createElement("div");
+
+ // eslint-disable-next-line no-unsanitized/property
+ mainDiv.innerHTML =
+ '<p><a id="linkToOpenInNewTab" href="' +
+ childTabSpecContent +
+ '">Link</a></p>';
+ content.document.body.appendChild(mainDiv);
+ }
+ );
+
+ // Execute the test in the child tabs with the two methods to open it.
+ for (let openFn of [simulateCtrlClick, simulateContextMenuOpenInTab]) {
+ let promiseTabLoaded = waitForSomeTabToLoad();
+ openFn(browser);
+ await promiseTabLoaded;
+ gBrowser.selectTabAtIndex(2);
+
+ if (waitForMetaRefresh) {
+ await waitForSomeTabToLoad();
+ }
+
+ await testTaskFn();
+
+ gBrowser.removeCurrentTab();
+ }
+
+ SitePermissions.removeFromPrincipal(principal, "mixed-content");
+ }
+ );
+}
+
+function simulateCtrlClick(browser) {
+ BrowserTestUtils.synthesizeMouseAtCenter(
+ "#linkToOpenInNewTab",
+ { ctrlKey: true, metaKey: true },
+ browser
+ );
+}
+
+function simulateContextMenuOpenInTab(browser) {
+ BrowserTestUtils.waitForEvent(document, "popupshown", false, event => {
+ // These are operations that must be executed synchronously with the event.
+ document.getElementById("context-openlinkintab").doCommand();
+ event.target.hidePopup();
+ return true;
+ });
+ BrowserTestUtils.synthesizeMouseAtCenter(
+ "#linkToOpenInNewTab",
+ { type: "contextmenu", button: 2 },
+ browser
+ );
+}
+
+// Waits for a load event somewhere in the browser but ignore events coming
+// from <xul:browser>s without a tab assigned. That are most likely browsers
+// that preload the new tab page.
+function waitForSomeTabToLoad() {
+ return BrowserTestUtils.firstBrowserLoaded(window, true, browser => {
+ let tab = gBrowser.getTabForBrowser(browser);
+ return !!tab;
+ });
+}
+
+/**
+ * Ensure the Mixed Content Blocker is enabled.
+ */
+add_task(async function test_initialize() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["security.mixed_content.block_active_content", true],
+ // We need to disable the dFPI heuristic. So, we won't have unnecessary
+ // 3rd party cookie permission that could affect following tests because
+ // it will create a permission icon on the URL bar.
+ ["privacy.restrict3rdpartystorage.heuristic.recently_visited", false],
+ ],
+ });
+});
+
+/**
+ * 1. - Load a html page which has mixed content
+ * - Doorhanger to disable protection appears - we disable it
+ * - Load a subpage from the same origin in a new tab simulating a click
+ * - Doorhanger should >> NOT << appear anymore!
+ */
+add_task(async function test_same_origin() {
+ await doTest(
+ HTTPS_TEST_ROOT_1 + "file_bug906190_1.html",
+ HTTPS_TEST_ROOT_1 + "file_bug906190_2.html",
+ async function () {
+ // The doorhanger should appear but activeBlocked should be >> NOT << true,
+ // because our decision of disabling the mixed content blocker is persistent
+ // across tabs.
+ await assertMixedContentBlockingState(gBrowser, {
+ activeLoaded: true,
+ activeBlocked: false,
+ passiveLoaded: false,
+ });
+
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
+ Assert.equal(
+ content.document.getElementById("mctestdiv").innerHTML,
+ "Mixed Content Blocker disabled",
+ "OK: Executed mixed script"
+ );
+ });
+ }
+ );
+});
+
+/**
+ * 2. - Load a html page which has mixed content
+ * - Doorhanger to disable protection appears - we disable it
+ * - Load a new page from a different origin in a new tab simulating a click
+ * - Doorhanger >> SHOULD << appear again!
+ */
+add_task(async function test_different_origin() {
+ await doTest(
+ HTTPS_TEST_ROOT_1 + "file_bug906190_2.html",
+ HTTPS_TEST_ROOT_2 + "file_bug906190_2.html",
+ async function () {
+ // The doorhanger should appear and activeBlocked should be >> TRUE <<,
+ // because our decision of disabling the mixed content blocker should only
+ // persist if pages are from the same domain.
+ await assertMixedContentBlockingState(gBrowser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: false,
+ });
+
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
+ Assert.equal(
+ content.document.getElementById("mctestdiv").innerHTML,
+ "Mixed Content Blocker enabled",
+ "OK: Blocked mixed script"
+ );
+ });
+ }
+ );
+});
+
+/**
+ * 3. - Load a html page which has mixed content
+ * - Doorhanger to disable protection appears - we disable it
+ * - Load a new page from the same origin in a new tab simulating a click
+ * - Redirect to another page from the same origin using meta-refresh
+ * - Doorhanger should >> NOT << appear again!
+ */
+add_task(async function test_same_origin_metarefresh_same_origin() {
+ // file_bug906190_3_4.html redirects to page test1.example.com/* using meta-refresh
+ await doTest(
+ HTTPS_TEST_ROOT_1 + "file_bug906190_1.html",
+ HTTPS_TEST_ROOT_1 + "file_bug906190_3_4.html",
+ async function () {
+ // The doorhanger should appear but activeBlocked should be >> NOT << true!
+ await assertMixedContentBlockingState(gBrowser, {
+ activeLoaded: true,
+ activeBlocked: false,
+ passiveLoaded: false,
+ });
+
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
+ Assert.equal(
+ content.document.getElementById("mctestdiv").innerHTML,
+ "Mixed Content Blocker disabled",
+ "OK: Executed mixed script"
+ );
+ });
+ },
+ true
+ );
+});
+
+/**
+ * 4. - Load a html page which has mixed content
+ * - Doorhanger to disable protection appears - we disable it
+ * - Load a new page from the same origin in a new tab simulating a click
+ * - Redirect to another page from a different origin using meta-refresh
+ * - Doorhanger >> SHOULD << appear again!
+ */
+add_task(async function test_same_origin_metarefresh_different_origin() {
+ await doTest(
+ HTTPS_TEST_ROOT_2 + "file_bug906190_1.html",
+ HTTPS_TEST_ROOT_2 + "file_bug906190_3_4.html",
+ async function () {
+ // The doorhanger should appear and activeBlocked should be >> TRUE <<.
+ await assertMixedContentBlockingState(gBrowser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: false,
+ });
+
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
+ Assert.equal(
+ content.document.getElementById("mctestdiv").innerHTML,
+ "Mixed Content Blocker enabled",
+ "OK: Blocked mixed script"
+ );
+ });
+ },
+ true
+ );
+});
+
+/**
+ * 5. - Load a html page which has mixed content
+ * - Doorhanger to disable protection appears - we disable it
+ * - Load a new page from the same origin in a new tab simulating a click
+ * - Redirect to another page from the same origin using 302 redirect
+ */
+add_task(async function test_same_origin_302redirect_same_origin() {
+ // the sjs files returns a 302 redirect- note, same origins
+ await doTest(
+ HTTPS_TEST_ROOT_1 + "file_bug906190_1.html",
+ HTTPS_TEST_ROOT_1 + "file_bug906190.sjs",
+ async function () {
+ // The doorhanger should appear but activeBlocked should be >> NOT << true.
+ // Currently it is >> TRUE << - see follow up bug 914860
+ ok(
+ !gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"),
+ "OK: Mixed Content is NOT being blocked"
+ );
+
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
+ Assert.equal(
+ content.document.getElementById("mctestdiv").innerHTML,
+ "Mixed Content Blocker disabled",
+ "OK: Executed mixed script"
+ );
+ });
+ }
+ );
+});
+
+/**
+ * 6. - Load a html page which has mixed content
+ * - Doorhanger to disable protection appears - we disable it
+ * - Load a new page from the same origin in a new tab simulating a click
+ * - Redirect to another page from a different origin using 302 redirect
+ */
+add_task(async function test_same_origin_302redirect_different_origin() {
+ // the sjs files returns a 302 redirect - note, different origins
+ await doTest(
+ HTTPS_TEST_ROOT_2 + "file_bug906190_1.html",
+ HTTPS_TEST_ROOT_2 + "file_bug906190.sjs",
+ async function () {
+ // The doorhanger should appear and activeBlocked should be >> TRUE <<.
+ await assertMixedContentBlockingState(gBrowser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: false,
+ });
+
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => {
+ Assert.equal(
+ content.document.getElementById("mctestdiv").innerHTML,
+ "Mixed Content Blocker enabled",
+ "OK: Blocked mixed script"
+ );
+ });
+ }
+ );
+});
+
+/**
+ * 7. - Test memory leak issue on redirection error. See Bug 1269426.
+ */
+add_task(async function test_bad_redirection() {
+ // the sjs files returns a 302 redirect - note, different origins
+ await doTest(
+ HTTPS_TEST_ROOT_2 + "file_bug906190_1.html",
+ HTTPS_TEST_ROOT_2 + "file_bug906190.sjs?bad-redirection=1",
+ function () {
+ // Nothing to do. Just see if memory leak is reported in the end.
+ ok(true, "Nothing to do");
+ }
+ );
+});
diff --git a/browser/base/content/test/siteIdentity/browser_check_identity_state.js b/browser/base/content/test/siteIdentity/browser_check_identity_state.js
new file mode 100644
index 0000000000..e5ecbc66f2
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_check_identity_state.js
@@ -0,0 +1,882 @@
+/*
+ * Test the identity mode UI for a variety of page types
+ */
+
+"use strict";
+
+const DUMMY = "browser/browser/base/content/test/siteIdentity/dummy_page.html";
+const INSECURE_ICON_PREF = "security.insecure_connection_icon.enabled";
+const INSECURE_TEXT_PREF = "security.insecure_connection_text.enabled";
+const INSECURE_PBMODE_ICON_PREF =
+ "security.insecure_connection_icon.pbmode.enabled";
+const HTTPS_FIRST_PBM_PREF = "dom.security.https_first_pbm";
+
+function loadNewTab(url) {
+ return BrowserTestUtils.openNewForegroundTab(gBrowser, url, true);
+}
+
+function getConnectionState() {
+ // Prevents items that are being lazy loaded causing issues
+ document.getElementById("identity-icon-box").click();
+ gIdentityHandler.refreshIdentityPopup();
+ return document.getElementById("identity-popup").getAttribute("connection");
+}
+
+function getSecurityConnectionBG() {
+ // Get the background image of the security connection.
+ document.getElementById("identity-icon-box").click();
+ gIdentityHandler.refreshIdentityPopup();
+ return gBrowser.ownerGlobal
+ .getComputedStyle(
+ document
+ .getElementById("identity-popup-mainView")
+ .getElementsByClassName("identity-popup-security-connection")[0]
+ )
+ .getPropertyValue("list-style-image");
+}
+
+async function getReaderModeURL() {
+ // Gets the reader mode URL from "identity-popup mainView panel header span"
+ document.getElementById("identity-icon-box").click();
+ gIdentityHandler.refreshIdentityPopup();
+
+ let headerSpan = document.getElementById(
+ "identity-popup-mainView-panel-header-span"
+ );
+ await BrowserTestUtils.waitForCondition(() =>
+ headerSpan.innerHTML.includes("example.com")
+ );
+ return headerSpan.innerHTML;
+}
+
+// This test is slow on Linux debug e10s
+requestLongerTimeout(2);
+
+add_task(async function chromeUITest() {
+ // needs to be set due to bug in ion.js that occurs when testing
+ SpecialPowers.pushPrefEnv({
+ set: [
+ ["toolkit.pioneer.testCachedContent", "[]"],
+ ["toolkit.pioneer.testCachedAddons", "[]"],
+ ],
+ });
+ // Might needs to be extended with new secure chrome pages
+ // about:debugging is a secure chrome UI but is not tested for causing problems.
+ let secureChromePages = [
+ "addons",
+ "cache",
+ "certificate",
+ "compat",
+ "config",
+ "downloads",
+ "ion",
+ "license",
+ "logins",
+ "loginsimportreport",
+ "performance",
+ "plugins",
+ "policies",
+ "preferences",
+ "processes",
+ "profiles",
+ "profiling",
+ "protections",
+ "rights",
+ "sessionrestore",
+ "studies",
+ "support",
+ "telemetry",
+ "welcomeback",
+ ];
+
+ // else skip about:crashes, it is only available with plugin
+ if (AppConstants.MOZ_CRASHREPORTER) {
+ secureChromePages.push("crashes");
+ }
+
+ let nonSecureExamplePages = [
+ "about:about",
+ "about:credits",
+ "about:home",
+ "about:logo",
+ "about:memory",
+ "about:mozilla",
+ "about:networking",
+ "about:privatebrowsing",
+ "about:robots",
+ "about:serviceWorkers",
+ "about:sync-log",
+ "about:unloads",
+ "about:url-classifier",
+ "about:webrtc",
+ "about:welcome",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/" + DUMMY,
+ ];
+
+ for (let i = 0; i < secureChromePages.length; i++) {
+ await BrowserTestUtils.withNewTab("about:" + secureChromePages[i], () => {
+ is(getIdentityMode(), "chromeUI", "Identity should be chromeUI");
+ });
+ }
+
+ for (let i = 0; i < nonSecureExamplePages.length; i++) {
+ console.log(nonSecureExamplePages[i]);
+ await BrowserTestUtils.withNewTab(nonSecureExamplePages[i], () => {
+ ok(getIdentityMode() != "chromeUI", "Identity should not be chromeUI");
+ });
+ }
+});
+
+async function webpageTest(secureCheck) {
+ await SpecialPowers.pushPrefEnv({ set: [[INSECURE_ICON_PREF, secureCheck]] });
+ let oldTab = await loadNewTab("about:robots");
+
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ let newTab = await loadNewTab("http://example.com/" + DUMMY);
+ if (secureCheck) {
+ is(getIdentityMode(), "notSecure", "Identity should be not secure");
+ } else {
+ is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+ }
+
+ gBrowser.selectedTab = oldTab;
+ is(getIdentityMode(), "localResource", "Identity should be localResource");
+
+ gBrowser.selectedTab = newTab;
+ if (secureCheck) {
+ is(getIdentityMode(), "notSecure", "Identity should be not secure");
+ } else {
+ is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+ }
+
+ gBrowser.removeTab(newTab);
+ gBrowser.removeTab(oldTab);
+ await SpecialPowers.popPrefEnv();
+}
+
+add_task(async function test_webpage() {
+ await webpageTest(false);
+ await webpageTest(true);
+});
+
+async function webpageTestTextWarning(secureCheck) {
+ await SpecialPowers.pushPrefEnv({ set: [[INSECURE_TEXT_PREF, secureCheck]] });
+ let oldTab = await loadNewTab("about:robots");
+
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ let newTab = await loadNewTab("http://example.com/" + DUMMY);
+ if (secureCheck) {
+ is(
+ getIdentityMode(),
+ "notSecure notSecureText",
+ "Identity should have not secure text"
+ );
+ } else {
+ is(getIdentityMode(), "notSecure", "Identity should be not secure");
+ }
+
+ gBrowser.selectedTab = oldTab;
+ is(getIdentityMode(), "localResource", "Identity should be localResource");
+
+ gBrowser.selectedTab = newTab;
+ if (secureCheck) {
+ is(
+ getIdentityMode(),
+ "notSecure notSecureText",
+ "Identity should have not secure text"
+ );
+ } else {
+ is(getIdentityMode(), "notSecure", "Identity should be not secure");
+ }
+
+ gBrowser.removeTab(newTab);
+ gBrowser.removeTab(oldTab);
+ await SpecialPowers.popPrefEnv();
+}
+
+add_task(async function test_webpage_text_warning() {
+ await webpageTestTextWarning(false);
+ await webpageTestTextWarning(true);
+});
+
+async function webpageTestTextWarningCombined(secureCheck) {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [INSECURE_TEXT_PREF, secureCheck],
+ [INSECURE_ICON_PREF, secureCheck],
+ ],
+ });
+ let oldTab = await loadNewTab("about:robots");
+
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ let newTab = await loadNewTab("http://example.com/" + DUMMY);
+ if (secureCheck) {
+ is(
+ getIdentityMode(),
+ "notSecure notSecureText",
+ "Identity should be not secure"
+ );
+ } else {
+ is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+ }
+
+ gBrowser.selectedTab = oldTab;
+ is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+
+ gBrowser.selectedTab = newTab;
+ if (secureCheck) {
+ is(
+ getIdentityMode(),
+ "notSecure notSecureText",
+ "Identity should be not secure"
+ );
+ } else {
+ is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+ }
+
+ gBrowser.removeTab(newTab);
+ gBrowser.removeTab(oldTab);
+ await SpecialPowers.popPrefEnv();
+}
+
+add_task(async function test_webpage_text_warning_combined() {
+ await webpageTestTextWarning(false);
+ await webpageTestTextWarning(true);
+});
+
+async function blankPageTest(secureCheck) {
+ let oldTab = await loadNewTab("about:robots");
+ await SpecialPowers.pushPrefEnv({ set: [[INSECURE_ICON_PREF, secureCheck]] });
+
+ let newTab = await loadNewTab("about:blank");
+ is(
+ gURLBar.getAttribute("pageproxystate"),
+ "invalid",
+ "pageproxystate should be invalid"
+ );
+
+ gBrowser.selectedTab = oldTab;
+ is(
+ gURLBar.getAttribute("pageproxystate"),
+ "valid",
+ "pageproxystate should be valid"
+ );
+ is(getIdentityMode(), "localResource", "Identity should be localResource");
+
+ gBrowser.selectedTab = newTab;
+ is(
+ gURLBar.getAttribute("pageproxystate"),
+ "invalid",
+ "pageproxystate should be invalid"
+ );
+
+ gBrowser.removeTab(newTab);
+ gBrowser.removeTab(oldTab);
+ await SpecialPowers.popPrefEnv();
+}
+
+add_task(async function test_blank() {
+ await blankPageTest(true);
+ await blankPageTest(false);
+});
+
+async function secureTest(secureCheck) {
+ let oldTab = await loadNewTab("about:robots");
+ await SpecialPowers.pushPrefEnv({ set: [[INSECURE_ICON_PREF, secureCheck]] });
+
+ let newTab = await loadNewTab("https://example.com/" + DUMMY);
+ is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
+
+ gBrowser.selectedTab = oldTab;
+ is(getIdentityMode(), "localResource", "Identity should be localResource");
+
+ gBrowser.selectedTab = newTab;
+ is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
+
+ gBrowser.removeTab(newTab);
+ gBrowser.removeTab(oldTab);
+
+ await SpecialPowers.popPrefEnv();
+}
+
+add_task(async function test_secure_enabled() {
+ await secureTest(true);
+ await secureTest(false);
+});
+
+async function viewSourceTest() {
+ let sourceTab = await loadNewTab("view-source:https://example.com/" + DUMMY);
+
+ gBrowser.selectedTab = sourceTab;
+ is(
+ getIdentityMode(),
+ "verifiedDomain",
+ "Identity should be verified while viewing source"
+ );
+
+ gBrowser.removeTab(sourceTab);
+}
+
+add_task(async function test_viewSource() {
+ await viewSourceTest();
+});
+
+async function insecureTest(secureCheck) {
+ let oldTab = await loadNewTab("about:robots");
+ await SpecialPowers.pushPrefEnv({ set: [[INSECURE_ICON_PREF, secureCheck]] });
+
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ let newTab = await loadNewTab("http://example.com/" + DUMMY);
+ if (secureCheck) {
+ is(getIdentityMode(), "notSecure", "Identity should be not secure");
+ is(
+ document.getElementById("identity-icon").getAttribute("tooltiptext"),
+ gNavigatorBundle.getString("identity.notSecure.tooltip"),
+ "The insecure lock icon has a correct tooltip text."
+ );
+ } else {
+ is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+ }
+
+ gBrowser.selectedTab = oldTab;
+ is(getIdentityMode(), "localResource", "Identity should be localResource");
+
+ gBrowser.selectedTab = newTab;
+ if (secureCheck) {
+ is(getIdentityMode(), "notSecure", "Identity should be not secure");
+ is(
+ document.getElementById("identity-icon").getAttribute("tooltiptext"),
+ gNavigatorBundle.getString("identity.notSecure.tooltip"),
+ "The insecure lock icon has a correct tooltip text."
+ );
+ } else {
+ is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+ }
+
+ gBrowser.removeTab(newTab);
+ gBrowser.removeTab(oldTab);
+
+ await SpecialPowers.popPrefEnv();
+}
+
+add_task(async function test_insecure() {
+ await insecureTest(true);
+ await insecureTest(false);
+});
+
+async function addonsTest(secureCheck) {
+ let oldTab = await loadNewTab("about:robots");
+ await SpecialPowers.pushPrefEnv({ set: [[INSECURE_ICON_PREF, secureCheck]] });
+
+ let newTab = await loadNewTab("about:addons");
+ is(getIdentityMode(), "chromeUI", "Identity should be chrome");
+
+ gBrowser.selectedTab = oldTab;
+ is(getIdentityMode(), "localResource", "Identity should be localResource");
+
+ gBrowser.selectedTab = newTab;
+ is(getIdentityMode(), "chromeUI", "Identity should be chrome");
+
+ gBrowser.removeTab(newTab);
+ gBrowser.removeTab(oldTab);
+
+ await SpecialPowers.popPrefEnv();
+}
+
+add_task(async function test_addons() {
+ await addonsTest(true);
+ await addonsTest(false);
+});
+
+async function fileTest(secureCheck) {
+ let oldTab = await loadNewTab("about:robots");
+ await SpecialPowers.pushPrefEnv({ set: [[INSECURE_ICON_PREF, secureCheck]] });
+ let fileURI = getTestFilePath("");
+
+ let newTab = await loadNewTab(fileURI);
+ is(getConnectionState(), "file", "Connection should be file");
+
+ gBrowser.selectedTab = oldTab;
+ is(getIdentityMode(), "localResource", "Identity should be localResource");
+
+ gBrowser.selectedTab = newTab;
+ is(getConnectionState(), "file", "Connection should be file");
+
+ gBrowser.removeTab(newTab);
+ gBrowser.removeTab(oldTab);
+
+ await SpecialPowers.popPrefEnv();
+}
+
+add_task(async function test_file() {
+ await fileTest(true);
+ await fileTest(false);
+});
+
+async function resourceUriTest(secureCheck) {
+ let oldTab = await loadNewTab("about:robots");
+ await SpecialPowers.pushPrefEnv({ set: [[INSECURE_ICON_PREF, secureCheck]] });
+ let dataURI = "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+ let newTab = await loadNewTab(dataURI);
+
+ is(getConnectionState(), "file", "Connection should be file");
+
+ gBrowser.selectedTab = oldTab;
+ is(
+ getIdentityMode(),
+ "localResource",
+ "Identity should be a local a resource"
+ );
+
+ gBrowser.selectedTab = newTab;
+ is(getConnectionState(), "file", "Connection should be file");
+
+ gBrowser.removeTab(newTab);
+ gBrowser.removeTab(oldTab);
+
+ await SpecialPowers.popPrefEnv();
+}
+
+add_task(async function test_resource_uri() {
+ await resourceUriTest(true);
+ await resourceUriTest(false);
+});
+
+async function noCertErrorTest(secureCheck) {
+ let oldTab = await loadNewTab("about:robots");
+ await SpecialPowers.pushPrefEnv({ set: [[INSECURE_ICON_PREF, secureCheck]] });
+ let newTab = BrowserTestUtils.addTab(gBrowser);
+ gBrowser.selectedTab = newTab;
+
+ let promise = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
+ BrowserTestUtils.loadURIString(gBrowser, "https://nocert.example.com/");
+ await promise;
+ is(
+ getIdentityMode(),
+ "certErrorPage notSecureText",
+ "Identity should be the cert error page."
+ );
+ is(
+ getConnectionState(),
+ "cert-error-page",
+ "Connection should be the cert error page."
+ );
+
+ gBrowser.selectedTab = oldTab;
+ is(getIdentityMode(), "localResource", "Identity should be localResource");
+
+ gBrowser.selectedTab = newTab;
+ is(
+ getIdentityMode(),
+ "certErrorPage notSecureText",
+ "Identity should be the cert error page."
+ );
+ is(
+ getConnectionState(),
+ "cert-error-page",
+ "Connection should be the cert error page."
+ );
+
+ gBrowser.removeTab(newTab);
+ gBrowser.removeTab(oldTab);
+
+ await SpecialPowers.popPrefEnv();
+}
+
+add_task(async function test_about_net_error_uri() {
+ await noCertErrorTest(true);
+ await noCertErrorTest(false);
+});
+
+add_task(async function httpsOnlyErrorTest() {
+ let oldTab = await loadNewTab("about:robots");
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.security.https_only_mode", true]],
+ });
+ let newTab = BrowserTestUtils.addTab(gBrowser);
+ gBrowser.selectedTab = newTab;
+
+ let promise = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ BrowserTestUtils.loadURIString(gBrowser, "http://nocert.example.com/");
+ await promise;
+ is(
+ getIdentityMode(),
+ "httpsOnlyErrorPage",
+ "Identity should be the https-only mode error page."
+ );
+ is(
+ getConnectionState(),
+ "https-only-error-page",
+ "Connection should be the https-only mode error page."
+ );
+
+ gBrowser.selectedTab = oldTab;
+ is(getIdentityMode(), "localResource", "Identity should be localResource");
+
+ gBrowser.selectedTab = newTab;
+ is(
+ getIdentityMode(),
+ "httpsOnlyErrorPage",
+ "Identity should be the https-only mode error page."
+ );
+ is(
+ getConnectionState(),
+ "https-only-error-page",
+ "Connection should be the https-only mode page."
+ );
+
+ gBrowser.removeTab(newTab);
+ gBrowser.removeTab(oldTab);
+
+ await SpecialPowers.popPrefEnv();
+});
+
+async function noCertErrorFromNavigationTest(secureCheck) {
+ await SpecialPowers.pushPrefEnv({ set: [[INSECURE_ICON_PREF, secureCheck]] });
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ let newTab = await loadNewTab("http://example.com/" + DUMMY);
+
+ let promise = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () {
+ content.document.getElementById("no-cert").click();
+ });
+ await promise;
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () {
+ is(
+ content.window.location.href,
+ "https://nocert.example.com/",
+ "Should be the cert error URL"
+ );
+ });
+
+ is(
+ newTab.linkedBrowser.documentURI.spec.startsWith("about:certerror?"),
+ true,
+ "Should be an about:certerror"
+ );
+ is(
+ getIdentityMode(),
+ "certErrorPage notSecureText",
+ "Identity should be the cert error page."
+ );
+ is(
+ getConnectionState(),
+ "cert-error-page",
+ "Connection should be the cert error page."
+ );
+
+ gBrowser.removeTab(newTab);
+
+ await SpecialPowers.popPrefEnv();
+}
+
+add_task(async function test_about_net_error_uri_from_navigation_tab() {
+ await noCertErrorFromNavigationTest(true);
+ await noCertErrorFromNavigationTest(false);
+});
+
+add_task(async function tlsErrorPageTest() {
+ const TLS10_PAGE = "https://tls1.example.com/";
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["security.tls.version.min", 3],
+ ["security.tls.version.max", 4],
+ ],
+ });
+
+ let browser;
+ let pageLoaded;
+ await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ () => {
+ gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, TLS10_PAGE);
+ browser = gBrowser.selectedBrowser;
+ pageLoaded = BrowserTestUtils.waitForErrorPage(browser);
+ },
+ false
+ );
+
+ info("Loading and waiting for the net error");
+ await pageLoaded;
+
+ await SpecialPowers.spawn(browser, [], function () {
+ const doc = content.document;
+ ok(
+ doc.documentURI.startsWith("about:neterror"),
+ "Should be showing error page"
+ );
+ });
+
+ is(
+ getConnectionState(),
+ "cert-error-page",
+ "Connection state should be the cert error page."
+ );
+
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async function netErrorPageTest() {
+ // Connect to a server that rejects all requests, to test network error pages:
+ let { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
+ let server = new HttpServer();
+ server.registerPrefixHandler("/", (req, res) =>
+ res.abort(new Error("Noooope."))
+ );
+ server.start(-1);
+ let port = server.identity.primaryPort;
+ const ERROR_PAGE = `http://localhost:${port}/`;
+
+ let browser;
+ let pageLoaded;
+ await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ () => {
+ gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, ERROR_PAGE);
+ browser = gBrowser.selectedBrowser;
+ pageLoaded = BrowserTestUtils.waitForErrorPage(browser);
+ },
+ false
+ );
+
+ info("Loading and waiting for the net error");
+ await pageLoaded;
+
+ await SpecialPowers.spawn(browser, [], function () {
+ const doc = content.document;
+ ok(
+ doc.documentURI.startsWith("about:neterror"),
+ "Should be showing error page"
+ );
+ });
+
+ is(
+ getConnectionState(),
+ "net-error-page",
+ "Connection should be the net error page."
+ );
+
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
+async function aboutBlockedTest(secureCheck) {
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ let url = "http://www.itisatrap.org/firefox/its-an-attack.html";
+ let oldTab = await loadNewTab("about:robots");
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [INSECURE_ICON_PREF, secureCheck],
+ ["urlclassifier.blockedTable", "moztest-block-simple"],
+ ],
+ });
+ let newTab = BrowserTestUtils.addTab(gBrowser);
+ gBrowser.selectedTab = newTab;
+
+ BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, url);
+
+ await BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ url,
+ true
+ );
+
+ is(getIdentityMode(), "unknownIdentity", "Identity should be unknown.");
+ is(getConnectionState(), "not-secure", "Connection should be not secure.");
+
+ gBrowser.selectedTab = oldTab;
+ is(getIdentityMode(), "localResource", "Identity should be localResource");
+
+ gBrowser.selectedTab = newTab;
+ is(getIdentityMode(), "unknownIdentity", "Identity should be unknown.");
+ is(getConnectionState(), "not-secure", "Connection should be not secure.");
+
+ gBrowser.removeTab(newTab);
+ gBrowser.removeTab(oldTab);
+
+ await SpecialPowers.popPrefEnv();
+}
+
+add_task(async function test_about_blocked() {
+ await aboutBlockedTest(true);
+ await aboutBlockedTest(false);
+});
+
+add_task(async function noCertErrorSecurityConnectionBGTest() {
+ let tab = BrowserTestUtils.addTab(gBrowser);
+ gBrowser.selectedTab = tab;
+ let promise = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
+ BrowserTestUtils.loadURIString(gBrowser, "https://nocert.example.com/");
+ await promise;
+
+ is(
+ getSecurityConnectionBG(),
+ `url("chrome://global/skin/icons/security-warning.svg")`,
+ "Security connection should show a warning lock icon."
+ );
+
+ BrowserTestUtils.removeTab(tab);
+});
+
+async function aboutUriTest(secureCheck) {
+ let oldTab = await loadNewTab("about:robots");
+ await SpecialPowers.pushPrefEnv({ set: [[INSECURE_ICON_PREF, secureCheck]] });
+ let aboutURI = "about:robots";
+
+ let newTab = await loadNewTab(aboutURI);
+ is(getConnectionState(), "file", "Connection should be file");
+
+ gBrowser.selectedTab = oldTab;
+ is(getIdentityMode(), "localResource", "Identity should be localResource");
+
+ gBrowser.selectedTab = newTab;
+ is(getConnectionState(), "file", "Connection should be file");
+
+ gBrowser.removeTab(newTab);
+ gBrowser.removeTab(oldTab);
+
+ await SpecialPowers.popPrefEnv();
+}
+
+add_task(async function test_about_uri() {
+ await aboutUriTest(true);
+ await aboutUriTest(false);
+});
+
+async function readerUriTest(secureCheck) {
+ await SpecialPowers.pushPrefEnv({ set: [[INSECURE_ICON_PREF, secureCheck]] });
+
+ let newTab = await loadNewTab("about:reader?url=http://example.com");
+ gBrowser.selectedTab = newTab;
+ let readerURL = await getReaderModeURL();
+ is(
+ readerURL,
+ "Site information for example.com",
+ "should be the correct URI in reader mode"
+ );
+
+ gBrowser.removeTab(newTab);
+
+ await SpecialPowers.popPrefEnv();
+}
+
+add_task(async function test_reader_uri() {
+ await readerUriTest(true);
+ await readerUriTest(false);
+});
+
+async function dataUriTest(secureCheck) {
+ let oldTab = await loadNewTab("about:robots");
+ await SpecialPowers.pushPrefEnv({ set: [[INSECURE_ICON_PREF, secureCheck]] });
+ let dataURI = "data:text/html,hi";
+
+ let newTab = await loadNewTab(dataURI);
+ if (secureCheck) {
+ is(getIdentityMode(), "notSecure", "Identity should be not secure");
+ } else {
+ is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+ }
+
+ gBrowser.selectedTab = oldTab;
+ is(getIdentityMode(), "localResource", "Identity should be localResource");
+
+ gBrowser.selectedTab = newTab;
+ if (secureCheck) {
+ is(getIdentityMode(), "notSecure", "Identity should be not secure");
+ } else {
+ is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
+ }
+
+ gBrowser.removeTab(newTab);
+ gBrowser.removeTab(oldTab);
+
+ await SpecialPowers.popPrefEnv();
+}
+
+add_task(async function test_data_uri() {
+ await dataUriTest(true);
+ await dataUriTest(false);
+});
+
+async function pbModeTest(prefs, secureCheck) {
+ await SpecialPowers.pushPrefEnv({ set: prefs });
+
+ let privateWin = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ let oldTab = await BrowserTestUtils.openNewForegroundTab(
+ privateWin.gBrowser,
+ "about:robots"
+ );
+ let newTab = await BrowserTestUtils.openNewForegroundTab(
+ privateWin.gBrowser,
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/" + DUMMY
+ );
+
+ if (secureCheck) {
+ is(
+ getIdentityMode(privateWin),
+ "notSecure",
+ "Identity should be not secure"
+ );
+ } else {
+ is(
+ getIdentityMode(privateWin),
+ "unknownIdentity",
+ "Identity should be unknown"
+ );
+ }
+
+ privateWin.gBrowser.selectedTab = oldTab;
+ is(
+ getIdentityMode(privateWin),
+ "localResource",
+ "Identity should be localResource"
+ );
+
+ privateWin.gBrowser.selectedTab = newTab;
+ if (secureCheck) {
+ is(
+ getIdentityMode(privateWin),
+ "notSecure",
+ "Identity should be not secure"
+ );
+ } else {
+ is(
+ getIdentityMode(privateWin),
+ "unknownIdentity",
+ "Identity should be unknown"
+ );
+ }
+
+ await BrowserTestUtils.closeWindow(privateWin);
+
+ await SpecialPowers.popPrefEnv();
+}
+
+add_task(async function test_pb_mode() {
+ let prefs = [
+ [INSECURE_ICON_PREF, true],
+ [INSECURE_PBMODE_ICON_PREF, true],
+ [HTTPS_FIRST_PBM_PREF, false],
+ ];
+ await pbModeTest(prefs, true);
+ prefs = [
+ [INSECURE_ICON_PREF, false],
+ [INSECURE_PBMODE_ICON_PREF, true],
+ [HTTPS_FIRST_PBM_PREF, false],
+ ];
+ await pbModeTest(prefs, true);
+ prefs = [
+ [INSECURE_ICON_PREF, false],
+ [INSECURE_PBMODE_ICON_PREF, false],
+ [HTTPS_FIRST_PBM_PREF, false],
+ ];
+ await pbModeTest(prefs, false);
+});
diff --git a/browser/base/content/test/siteIdentity/browser_check_identity_state_pdf.js b/browser/base/content/test/siteIdentity/browser_check_identity_state_pdf.js
new file mode 100644
index 0000000000..8180238e84
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_check_identity_state_pdf.js
@@ -0,0 +1,77 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests that sites opened via the PDF viewer have the correct identity state.
+ */
+
+"use strict";
+
+function testIdentityMode(uri, expectedState, message) {
+ return BrowserTestUtils.withNewTab(uri, () => {
+ is(getIdentityMode(), expectedState, message);
+ });
+}
+
+/**
+ * Test site identity state for PDFs served via file URI.
+ */
+add_task(async function test_pdf_fileURI() {
+ let path = getTestFilePath("./file_pdf.pdf");
+ info("path:" + path);
+
+ await testIdentityMode(
+ path,
+ "localResource",
+ "Identity should be localResource for a PDF served via file URI"
+ );
+});
+
+/**
+ * Test site identity state for PDFs served via blob URI.
+ */
+add_task(async function test_pdf_blobURI() {
+ let uri =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "file_pdf_blob.html";
+
+ await BrowserTestUtils.withNewTab(uri, async browser => {
+ let newTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
+
+ await BrowserTestUtils.synthesizeMouseAtCenter("a", {}, browser);
+ await newTabOpened;
+
+ is(
+ getIdentityMode(),
+ "localResource",
+ "Identity should be localResource for a PDF served via blob URI"
+ );
+
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+});
+
+/**
+ * Test site identity state for PDFs served via HTTP.
+ */
+add_task(async function test_pdf_http() {
+ const PDF_URI_NOSCHEME =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "example.com"
+ ) + "file_pdf.pdf";
+
+ await testIdentityMode(
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://" + PDF_URI_NOSCHEME,
+ "notSecure",
+ "Identity should be notSecure for a PDF served via HTTP."
+ );
+ await testIdentityMode(
+ "https://" + PDF_URI_NOSCHEME,
+ "verifiedDomain",
+ "Identity should be verifiedDomain for a PDF served via HTTPS."
+ );
+});
diff --git a/browser/base/content/test/siteIdentity/browser_csp_block_all_mixedcontent.js b/browser/base/content/test/siteIdentity/browser_csp_block_all_mixedcontent.js
new file mode 100644
index 0000000000..693c9418de
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_csp_block_all_mixedcontent.js
@@ -0,0 +1,60 @@
+/*
+ * Description of the Test:
+ * We load an https page which uses a CSP including block-all-mixed-content.
+ * The page tries to load a script over http. We make sure the UI is not
+ * influenced when blocking the mixed content. In particular the page
+ * should still appear fully encrypted with a green lock.
+ */
+
+const PRE_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+var gTestBrowser = null;
+
+// ------------------------------------------------------
+function cleanUpAfterTests() {
+ gBrowser.removeCurrentTab();
+ window.focus();
+ finish();
+}
+
+// ------------------------------------------------------
+async function verifyUInotDegraded() {
+ // make sure that not mixed content is loaded and also not blocked
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: false,
+ activeBlocked: false,
+ passiveLoaded: false,
+ });
+ // clean up and finish test
+ cleanUpAfterTests();
+}
+
+// ------------------------------------------------------
+function runTests() {
+ var newTab = BrowserTestUtils.addTab(gBrowser);
+ gBrowser.selectedTab = newTab;
+ gTestBrowser = gBrowser.selectedBrowser;
+ newTab.linkedBrowser.stop();
+
+ // Starting the test
+ var url = PRE_PATH + "file_csp_block_all_mixedcontent.html";
+ BrowserTestUtils.browserLoaded(gTestBrowser, false, url).then(
+ verifyUInotDegraded
+ );
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+}
+
+// ------------------------------------------------------
+function test() {
+ // Performing async calls, e.g. 'onload', we have to wait till all of them finished
+ waitForExplicitFinish();
+
+ SpecialPowers.pushPrefEnv(
+ { set: [["security.mixed_content.block_active_content", true]] },
+ function () {
+ runTests();
+ }
+ );
+}
diff --git a/browser/base/content/test/siteIdentity/browser_deprecatedTLSVersions.js b/browser/base/content/test/siteIdentity/browser_deprecatedTLSVersions.js
new file mode 100644
index 0000000000..22fa33f3c2
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_deprecatedTLSVersions.js
@@ -0,0 +1,94 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * Tests for Bug 1535210 - Set SSL STATE_IS_BROKEN flag for TLS1.0 and TLS 1.1 connections
+ */
+
+const HTTPS_TLS1_0 = "https://tls1.example.com";
+const HTTPS_TLS1_1 = "https://tls11.example.com";
+const HTTPS_TLS1_2 = "https://tls12.example.com";
+const HTTPS_TLS1_3 = "https://tls13.example.com";
+
+function getIdentityMode(aWindow = window) {
+ return aWindow.document.getElementById("identity-box").className;
+}
+
+function closeIdentityPopup() {
+ let promise = BrowserTestUtils.waitForEvent(
+ gIdentityHandler._identityPopup,
+ "popuphidden"
+ );
+ gIdentityHandler._identityPopup.hidePopup();
+ return promise;
+}
+
+async function checkConnectionState(state) {
+ await openIdentityPopup();
+ is(getConnectionState(), state, "connectionState should be " + state);
+ await closeIdentityPopup();
+}
+
+function getConnectionState() {
+ return document.getElementById("identity-popup").getAttribute("connection");
+}
+
+registerCleanupFunction(function () {
+ // Set preferences back to their original values
+ Services.prefs.clearUserPref("security.tls.version.min");
+ Services.prefs.clearUserPref("security.tls.version.max");
+});
+
+add_task(async function () {
+ // Run with all versions enabled for this test.
+ Services.prefs.setIntPref("security.tls.version.min", 1);
+ Services.prefs.setIntPref("security.tls.version.max", 4);
+
+ await BrowserTestUtils.withNewTab("about:blank", async function (browser) {
+ // Try deprecated versions
+ BrowserTestUtils.loadURIString(browser, HTTPS_TLS1_0);
+ await BrowserTestUtils.browserLoaded(browser);
+ isSecurityState(browser, "broken");
+ is(
+ getIdentityMode(),
+ "unknownIdentity weakCipher",
+ "Identity should be unknownIdentity"
+ );
+ await checkConnectionState("not-secure");
+
+ BrowserTestUtils.loadURIString(browser, HTTPS_TLS1_1);
+ await BrowserTestUtils.browserLoaded(browser);
+ isSecurityState(browser, "broken");
+ is(
+ getIdentityMode(),
+ "unknownIdentity weakCipher",
+ "Identity should be unknownIdentity"
+ );
+ await checkConnectionState("not-secure");
+
+ // Transition to secure
+ BrowserTestUtils.loadURIString(browser, HTTPS_TLS1_2);
+ await BrowserTestUtils.browserLoaded(browser);
+ isSecurityState(browser, "secure");
+ is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
+ await checkConnectionState("secure");
+
+ // Transition back to broken
+ BrowserTestUtils.loadURIString(browser, HTTPS_TLS1_1);
+ await BrowserTestUtils.browserLoaded(browser);
+ isSecurityState(browser, "broken");
+ is(
+ getIdentityMode(),
+ "unknownIdentity weakCipher",
+ "Identity should be unknownIdentity"
+ );
+ await checkConnectionState("not-secure");
+
+ // TLS1.3 for completeness
+ BrowserTestUtils.loadURIString(browser, HTTPS_TLS1_3);
+ await BrowserTestUtils.browserLoaded(browser);
+ isSecurityState(browser, "secure");
+ is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
+ await checkConnectionState("secure");
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/browser_geolocation_indicator.js b/browser/base/content/test/siteIdentity/browser_geolocation_indicator.js
new file mode 100644
index 0000000000..078b7ab975
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_geolocation_indicator.js
@@ -0,0 +1,381 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+requestLongerTimeout(2);
+
+const { PermissionUI } = ChromeUtils.importESModule(
+ "resource:///modules/PermissionUI.sys.mjs"
+);
+
+const { PermissionTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PermissionTestUtils.sys.mjs"
+);
+
+const CP = Cc["@mozilla.org/content-pref/service;1"].getService(
+ Ci.nsIContentPrefService2
+);
+
+const EXAMPLE_PAGE_URL = "https://example.com";
+const EXAMPLE_PAGE_URI = Services.io.newURI(EXAMPLE_PAGE_URL);
+const EXAMPLE_PAGE_PRINCIPAL =
+ Services.scriptSecurityManager.createContentPrincipal(EXAMPLE_PAGE_URI, {});
+const GEO_CONTENT_PREF_KEY = "permissions.geoLocation.lastAccess";
+const POLL_INTERVAL_FALSE_STATE = 50;
+
+async function testGeoSharingIconVisible(state = true) {
+ let sharingIcon = document.getElementById("geo-sharing-icon");
+ ok(sharingIcon, "Geo sharing icon exists");
+
+ try {
+ await TestUtils.waitForCondition(
+ () => sharingIcon.hasAttribute("sharing") === true,
+ "Waiting for geo sharing icon visibility state",
+ // If we wait for sharing icon to *not* show, waitForCondition will always timeout on correct state.
+ // In these cases we want to reduce the wait time from 5 seconds to 2.5 seconds to prevent test duration timeouts
+ !state ? POLL_INTERVAL_FALSE_STATE : undefined
+ );
+ } catch (e) {
+ ok(!state, "Geo sharing icon not showing");
+ return;
+ }
+ ok(state, "Geo sharing icon showing");
+}
+
+async function checkForDOMElement(state, id) {
+ info(`Testing state ${state} of element ${id}`);
+ let el;
+ try {
+ await TestUtils.waitForCondition(
+ () => {
+ el = document.getElementById(id);
+ return el != null;
+ },
+ `Waiting for ${id}`,
+ !state ? POLL_INTERVAL_FALSE_STATE : undefined
+ );
+ } catch (e) {
+ ok(!state, `${id} has correct state`);
+ return el;
+ }
+ ok(state, `${id} has correct state`);
+
+ return el;
+}
+
+async function testPermissionPopupGeoContainer(
+ containerVisible,
+ timestampVisible
+) {
+ // The container holds the timestamp element, therefore we can't have a
+ // visible timestamp without the container.
+ if (timestampVisible && !containerVisible) {
+ ok(false, "Can't have timestamp without container");
+ }
+
+ // Only call openPermissionPopup if popup is closed, otherwise it does not resolve
+ if (!gPermissionPanel._identityPermissionBox.hasAttribute("open")) {
+ await openPermissionPopup();
+ }
+
+ let checkContainer = checkForDOMElement(
+ containerVisible,
+ "permission-popup-geo-container"
+ );
+
+ if (containerVisible && timestampVisible) {
+ // Wait for the geo container to be fully populated.
+ // The time label is computed async.
+ let container = await checkContainer;
+ await TestUtils.waitForCondition(
+ () => container.childElementCount == 2,
+ "permission-popup-geo-container should have two elements."
+ );
+ is(
+ container.childNodes[0].classList[0],
+ "permission-popup-permission-item",
+ "Geo container should have permission item."
+ );
+ is(
+ container.childNodes[1].id,
+ "geo-access-indicator-item",
+ "Geo container should have indicator item."
+ );
+ }
+ let checkAccessIndicator = checkForDOMElement(
+ timestampVisible,
+ "geo-access-indicator-item"
+ );
+
+ return Promise.all([checkContainer, checkAccessIndicator]);
+}
+
+function openExamplePage(tabbrowser = gBrowser) {
+ return BrowserTestUtils.openNewForegroundTab(tabbrowser, EXAMPLE_PAGE_URL);
+}
+
+function requestGeoLocation(browser) {
+ return SpecialPowers.spawn(browser, [], () => {
+ return new Promise(resolve => {
+ content.navigator.geolocation.getCurrentPosition(
+ () => resolve(true),
+ error => resolve(error.code !== 1) // PERMISSION_DENIED = 1
+ );
+ });
+ });
+}
+
+function answerGeoLocationPopup(allow, remember = false) {
+ let notification = PopupNotifications.getNotification("geolocation");
+ ok(
+ PopupNotifications.isPanelOpen && notification,
+ "Geolocation notification is open"
+ );
+
+ let rememberCheck = PopupNotifications.panel.querySelector(
+ ".popup-notification-checkbox"
+ );
+ rememberCheck.checked = remember;
+
+ let popupHidden = BrowserTestUtils.waitForEvent(
+ PopupNotifications.panel,
+ "popuphidden"
+ );
+ if (allow) {
+ let allowBtn = PopupNotifications.panel.querySelector(
+ ".popup-notification-primary-button"
+ );
+ allowBtn.click();
+ } else {
+ let denyBtn = PopupNotifications.panel.querySelector(
+ ".popup-notification-secondary-button"
+ );
+ denyBtn.click();
+ }
+ return popupHidden;
+}
+
+function setGeoLastAccess(browser, state) {
+ return new Promise(resolve => {
+ let host = browser.currentURI.host;
+ let handler = {
+ handleCompletion: () => resolve(),
+ };
+
+ if (!state) {
+ CP.removeByDomainAndName(
+ host,
+ GEO_CONTENT_PREF_KEY,
+ browser.loadContext,
+ handler
+ );
+ return;
+ }
+ CP.set(
+ host,
+ GEO_CONTENT_PREF_KEY,
+ new Date().toString(),
+ browser.loadContext,
+ handler
+ );
+ });
+}
+
+async function testGeoLocationLastAccessSet(browser) {
+ let timestamp = await new Promise(resolve => {
+ let lastAccess = null;
+ CP.getByDomainAndName(
+ gBrowser.currentURI.spec,
+ GEO_CONTENT_PREF_KEY,
+ browser.loadContext,
+ {
+ handleResult(pref) {
+ lastAccess = pref.value;
+ },
+ handleCompletion() {
+ resolve(lastAccess);
+ },
+ }
+ );
+ });
+
+ ok(timestamp != null, "Geo last access timestamp set");
+
+ let parseSuccess = true;
+ try {
+ timestamp = new Date(timestamp);
+ } catch (e) {
+ parseSuccess = false;
+ }
+ ok(
+ parseSuccess && !isNaN(timestamp),
+ "Geo last access timestamp is valid Date"
+ );
+}
+
+async function cleanup(tab) {
+ await setGeoLastAccess(tab.linkedBrowser, false);
+ SitePermissions.removeFromPrincipal(
+ tab.linkedBrowser.contentPrincipal,
+ "geo",
+ tab.linkedBrowser
+ );
+ gBrowser.resetBrowserSharing(tab.linkedBrowser);
+ BrowserTestUtils.removeTab(tab);
+}
+
+async function testIndicatorGeoSharingState(active) {
+ let tab = await openExamplePage();
+ gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: active });
+ await testGeoSharingIconVisible(active);
+
+ await cleanup(tab);
+}
+
+async function testIndicatorExplicitAllow(persistent) {
+ let tab = await openExamplePage();
+
+ let popupShown = BrowserTestUtils.waitForEvent(
+ PopupNotifications.panel,
+ "popupshown"
+ );
+ info("Requesting geolocation");
+ let request = requestGeoLocation(tab.linkedBrowser);
+ await popupShown;
+ info("Allowing geolocation via popup");
+ answerGeoLocationPopup(true, persistent);
+ await request;
+
+ await Promise.all([
+ testGeoSharingIconVisible(true),
+ testPermissionPopupGeoContainer(true, true),
+ testGeoLocationLastAccessSet(tab.linkedBrowser),
+ ]);
+
+ await cleanup(tab);
+}
+
+// Indicator and permission popup entry shown after explicit PermissionUI geolocation allow
+add_task(function test_indicator_and_timestamp_after_explicit_allow() {
+ return testIndicatorExplicitAllow(false);
+});
+add_task(function test_indicator_and_timestamp_after_explicit_allow_remember() {
+ return testIndicatorExplicitAllow(true);
+});
+
+// Indicator and permission popup entry shown after auto PermissionUI geolocation allow
+add_task(async function test_indicator_and_timestamp_after_implicit_allow() {
+ PermissionTestUtils.add(
+ EXAMPLE_PAGE_URI,
+ "geo",
+ Services.perms.ALLOW_ACTION,
+ Services.perms.EXPIRE_NEVER
+ );
+ let tab = await openExamplePage();
+ let result = await requestGeoLocation(tab.linkedBrowser);
+ ok(result, "Request should be allowed");
+
+ await Promise.all([
+ testGeoSharingIconVisible(true),
+ testPermissionPopupGeoContainer(true, true),
+ testGeoLocationLastAccessSet(tab.linkedBrowser),
+ ]);
+
+ await cleanup(tab);
+});
+
+// Indicator shown when manually setting sharing state to true
+add_task(function test_indicator_sharing_state_active() {
+ return testIndicatorGeoSharingState(true);
+});
+
+// Indicator not shown when manually setting sharing state to false
+add_task(function test_indicator_sharing_state_inactive() {
+ return testIndicatorGeoSharingState(false);
+});
+
+// Permission popup shows permission if geo permission is set to persistent allow
+add_task(async function test_permission_popup_permission_scope_permanent() {
+ PermissionTestUtils.add(
+ EXAMPLE_PAGE_URI,
+ "geo",
+ Services.perms.ALLOW_ACTION,
+ Services.perms.EXPIRE_NEVER
+ );
+ let tab = await openExamplePage();
+
+ await testPermissionPopupGeoContainer(true, false); // Expect permission to be visible, but not lastAccess indicator
+
+ await cleanup(tab);
+});
+
+// Sharing state set, but no permission
+add_task(async function test_permission_popup_permission_sharing_state() {
+ let tab = await openExamplePage();
+ gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: true });
+ await testPermissionPopupGeoContainer(true, false);
+
+ await cleanup(tab);
+});
+
+// Permission popup has correct state if sharing state and last geo access timestamp are set
+add_task(
+ async function test_permission_popup_permission_sharing_state_timestamp() {
+ let tab = await openExamplePage();
+ gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: true });
+ await setGeoLastAccess(tab.linkedBrowser, true);
+
+ await testPermissionPopupGeoContainer(true, true);
+
+ await cleanup(tab);
+ }
+);
+
+// Clicking permission clear button clears permission and resets geo sharing state
+add_task(async function test_permission_popup_permission_clear() {
+ PermissionTestUtils.add(
+ EXAMPLE_PAGE_URI,
+ "geo",
+ Services.perms.ALLOW_ACTION,
+ Services.perms.EXPIRE_NEVER
+ );
+ let tab = await openExamplePage();
+ gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: true });
+
+ await openPermissionPopup();
+
+ let clearButton = document.querySelector(
+ "#permission-popup-geo-container button"
+ );
+ ok(clearButton, "Clear button is visible");
+ clearButton.click();
+
+ await Promise.all([
+ testGeoSharingIconVisible(false),
+ testPermissionPopupGeoContainer(false, false),
+ TestUtils.waitForCondition(() => {
+ let sharingState = tab._sharingState;
+ return (
+ sharingState == null ||
+ sharingState.geo == null ||
+ sharingState.geo === false
+ );
+ }, "Waiting for geo sharing state to reset"),
+ ]);
+ await cleanup(tab);
+});
+
+/**
+ * Tests that we only show the last access label once when the sharing
+ * state is updated multiple times while the popup is open.
+ */
+add_task(async function test_permission_no_duplicate_last_access_label() {
+ let tab = await openExamplePage();
+ await setGeoLastAccess(tab.linkedBrowser, true);
+ await openPermissionPopup();
+ gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: true });
+ gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: true });
+ await testPermissionPopupGeoContainer(true, true);
+ await cleanup(tab);
+});
diff --git a/browser/base/content/test/siteIdentity/browser_getSecurityInfo.js b/browser/base/content/test/siteIdentity/browser_getSecurityInfo.js
new file mode 100644
index 0000000000..b3b086f39d
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_getSecurityInfo.js
@@ -0,0 +1,35 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE;
+const MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT = MOZILLA_PKIX_ERROR_BASE + 14;
+
+add_task(async function test() {
+ await BrowserTestUtils.withNewTab("about:blank", async function (browser) {
+ let loaded = BrowserTestUtils.waitForErrorPage(browser);
+ BrowserTestUtils.loadURIString(browser, "https://self-signed.example.com");
+ await loaded;
+ let securityInfo = gBrowser.securityUI.secInfo;
+ ok(!securityInfo, "Found no security info");
+
+ loaded = BrowserTestUtils.browserLoaded(browser);
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ BrowserTestUtils.loadURIString(browser, "http://example.com");
+ await loaded;
+ securityInfo = gBrowser.securityUI.secInfo;
+ ok(!securityInfo, "Found no security info");
+
+ loaded = BrowserTestUtils.browserLoaded(browser);
+ BrowserTestUtils.loadURIString(browser, "https://example.com");
+ await loaded;
+ securityInfo = gBrowser.securityUI.secInfo;
+ ok(securityInfo, "Found some security info");
+ ok(securityInfo.succeededCertChain, "Has a succeeded cert chain");
+ is(securityInfo.errorCode, 0, "Has no error code");
+ is(
+ securityInfo.serverCert.commonName,
+ "example.com",
+ "Has the correct certificate"
+ );
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/browser_identityBlock_flicker.js b/browser/base/content/test/siteIdentity/browser_identityBlock_flicker.js
new file mode 100644
index 0000000000..de2a137100
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_identityBlock_flicker.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* Tests that the identity icons don't flicker when navigating,
+ * i.e. the box should show no intermediate identity state. */
+
+add_task(async function test() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:robots",
+ true
+ );
+ let identityBox = document.getElementById("identity-box");
+
+ is(
+ identityBox.className,
+ "localResource",
+ "identity box has the correct class"
+ );
+
+ let observerOptions = {
+ attributes: true,
+ attributeFilter: ["class"],
+ };
+ let classChanges = 0;
+
+ let observer = new MutationObserver(function (mutations) {
+ for (let mutation of mutations) {
+ is(mutation.type, "attributes");
+ is(mutation.attributeName, "class");
+ classChanges++;
+ is(
+ identityBox.className,
+ "verifiedDomain",
+ "identity box class changed correctly"
+ );
+ }
+ });
+ observer.observe(identityBox, observerOptions);
+
+ let loaded = BrowserTestUtils.browserLoaded(
+ tab.linkedBrowser,
+ false,
+ "https://example.com/"
+ );
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, "https://example.com");
+ await loaded;
+
+ is(classChanges, 1, "Changed the className once");
+ observer.disconnect();
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/base/content/test/siteIdentity/browser_identityBlock_focus.js b/browser/base/content/test/siteIdentity/browser_identityBlock_focus.js
new file mode 100644
index 0000000000..858cd3d632
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_identityBlock_focus.js
@@ -0,0 +1,126 @@
+/* Tests that the identity block can be reached via keyboard
+ * shortcuts and that it has the correct tab order.
+ */
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+const PERMISSIONS_PAGE = TEST_PATH + "permissions.html";
+
+// The DevEdition has the DevTools button in the toolbar by default. Remove it
+// to prevent branch-specific rules what button should be focused.
+CustomizableUI.removeWidgetFromArea("developer-button");
+
+registerCleanupFunction(async function resetToolbar() {
+ await CustomizableUI.reset();
+});
+
+add_task(async function setupHomeButton() {
+ // Put the home button in the pre-proton placement to test focus states.
+ CustomizableUI.addWidgetToArea(
+ "home-button",
+ "nav-bar",
+ CustomizableUI.getPlacementOfWidget("stop-reload-button").position + 1
+ );
+});
+
+function synthesizeKeyAndWaitForFocus(element, keyCode, options) {
+ let focused = BrowserTestUtils.waitForEvent(element, "focus");
+ EventUtils.synthesizeKey(keyCode, options);
+ return focused;
+}
+
+// Checks that the tracking protection icon container is the next element after
+// the urlbar to be focused if there are no active notification anchors.
+add_task(async function testWithoutNotifications() {
+ await SpecialPowers.pushPrefEnv({ set: [["accessibility.tabfocus", 7]] });
+ await BrowserTestUtils.withNewTab("https://example.com", async function () {
+ await synthesizeKeyAndWaitForFocus(gURLBar, "l", { accelKey: true });
+ is(document.activeElement, gURLBar.inputField, "urlbar should be focused");
+ await synthesizeKeyAndWaitForFocus(
+ gProtectionsHandler._trackingProtectionIconContainer,
+ "VK_TAB",
+ { shiftKey: true }
+ );
+ is(
+ document.activeElement,
+ gProtectionsHandler._trackingProtectionIconContainer,
+ "tracking protection icon container should be focused"
+ );
+ });
+});
+
+// Checks that when there is a notification anchor, it will receive
+// focus before the identity block.
+add_task(async function testWithNotifications() {
+ await SpecialPowers.pushPrefEnv({ set: [["accessibility.tabfocus", 7]] });
+ await BrowserTestUtils.withNewTab(PERMISSIONS_PAGE, async function (browser) {
+ let popupshown = BrowserTestUtils.waitForEvent(
+ PopupNotifications.panel,
+ "popupshown"
+ );
+ // Request a permission;
+ BrowserTestUtils.synthesizeMouseAtCenter("#geo", {}, browser);
+ await popupshown;
+
+ await synthesizeKeyAndWaitForFocus(gURLBar, "l", { accelKey: true });
+ is(document.activeElement, gURLBar.inputField, "urlbar should be focused");
+ await synthesizeKeyAndWaitForFocus(
+ gProtectionsHandler._trackingProtectionIconContainer,
+ "VK_TAB",
+ { shiftKey: true }
+ );
+ is(
+ document.activeElement,
+ gProtectionsHandler._trackingProtectionIconContainer,
+ "tracking protection icon container should be focused"
+ );
+ await synthesizeKeyAndWaitForFocus(
+ gIdentityHandler._identityIconBox,
+ "ArrowRight"
+ );
+ is(
+ document.activeElement,
+ gIdentityHandler._identityIconBox,
+ "identity block should be focused"
+ );
+ let geoIcon = document.getElementById("geo-notification-icon");
+ await synthesizeKeyAndWaitForFocus(geoIcon, "ArrowRight");
+ is(
+ document.activeElement,
+ geoIcon,
+ "notification anchor should be focused"
+ );
+ });
+});
+
+// Checks that with invalid pageproxystate the identity block is ignored.
+add_task(async function testInvalidPageProxyState() {
+ await SpecialPowers.pushPrefEnv({ set: [["accessibility.tabfocus", 7]] });
+ await BrowserTestUtils.withNewTab("about:blank", async function (browser) {
+ // Loading about:blank will automatically focus the urlbar, which, however, can
+ // race with the test code. So we only send the shortcut if the urlbar isn't focused yet.
+ if (document.activeElement != gURLBar.inputField) {
+ await synthesizeKeyAndWaitForFocus(gURLBar, "l", { accelKey: true });
+ }
+ is(document.activeElement, gURLBar.inputField, "urlbar should be focused");
+ await synthesizeKeyAndWaitForFocus(
+ document.getElementById("home-button"),
+ "VK_TAB",
+ { shiftKey: true }
+ );
+ await synthesizeKeyAndWaitForFocus(
+ document.getElementById("tabs-newtab-button"),
+ "VK_TAB",
+ { shiftKey: true }
+ );
+ isnot(
+ document.activeElement,
+ gProtectionsHandler._trackingProtectionIconContainer,
+ "tracking protection icon container should not be focused"
+ );
+ // Restore focus to the url bar.
+ gURLBar.focus();
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/browser_identityIcon_img_url.js b/browser/base/content/test/siteIdentity/browser_identityIcon_img_url.js
new file mode 100644
index 0000000000..9ef8b6dfed
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_identityIcon_img_url.js
@@ -0,0 +1,148 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+/**
+ * Test Bug 1562881 - Ensuring the identity icon loads correct img in different
+ * circumstances.
+ */
+
+const kBaseURI = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+const kBaseURILocalhost = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "http://127.0.0.1"
+);
+
+const TEST_CASES = [
+ {
+ type: "http",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ testURL: "http://example.com",
+ img_url: `url("chrome://global/skin/icons/security-broken.svg")`,
+ },
+ {
+ type: "https",
+ testURL: "https://example.com",
+ img_url: `url("chrome://global/skin/icons/security.svg")`,
+ },
+ {
+ type: "non-chrome about page",
+ testURL: "about:about",
+ img_url: `url("chrome://global/skin/icons/page-portrait.svg")`,
+ },
+ {
+ type: "chrome about page",
+ testURL: "about:preferences",
+ img_url: `url("chrome://branding/content/icon${
+ window.devicePixelRatio > 1 ? 32 : 16
+ }.png")`,
+ },
+ {
+ type: "file",
+ testURL: "dummy_page.html",
+ img_url: `url("chrome://global/skin/icons/page-portrait.svg")`,
+ },
+ {
+ type: "resource",
+ testURL: "resource://gre/modules/Log.sys.mjs",
+ img_url: `url("chrome://global/skin/icons/page-portrait.svg")`,
+ },
+ {
+ type: "mixedPassiveContent",
+ testURL: kBaseURI + "file_mixedPassiveContent.html",
+ img_url: `url("chrome://global/skin/icons/security-warning.svg")`,
+ },
+ {
+ type: "mixedActiveContent",
+ testURL: kBaseURI + "file_csp_block_all_mixedcontent.html",
+ img_url: `url("chrome://global/skin/icons/security.svg")`,
+ },
+ {
+ type: "certificateError",
+ testURL: "https://self-signed.example.com",
+ img_url: `url("chrome://global/skin/icons/security-warning.svg")`,
+ },
+ {
+ type: "localhost",
+ testURL: "http://127.0.0.1",
+ img_url: `url("chrome://global/skin/icons/page-portrait.svg")`,
+ },
+ {
+ type: "localhost + http frame",
+ testURL: kBaseURILocalhost + "file_csp_block_all_mixedcontent.html",
+ img_url: `url("chrome://global/skin/icons/page-portrait.svg")`,
+ },
+ {
+ type: "data URI",
+ testURL: "data:text/html,<div>",
+ img_url: `url("chrome://global/skin/icons/security-broken.svg")`,
+ },
+ {
+ type: "view-source HTTP",
+ testURL: "view-source:http://example.com/",
+ img_url: `url("chrome://global/skin/icons/security-broken.svg")`,
+ },
+ {
+ type: "view-source HTTPS",
+ testURL: "view-source:https://example.com/",
+ img_url: `url("chrome://global/skin/icons/security.svg")`,
+ },
+];
+
+add_task(async function test() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ // By default, proxies don't apply to 127.0.0.1. We need them to for this test, though:
+ ["network.proxy.allow_hijacking_localhost", true],
+ ["security.mixed_content.upgrade_display_content", false],
+ ],
+ });
+
+ for (let testData of TEST_CASES) {
+ info(`Testing for ${testData.type}`);
+ // Open the page for testing.
+ let testURL = testData.testURL;
+
+ // Overwrite the url if it is testing the file url.
+ if (testData.type === "file") {
+ let dir = getChromeDir(getResolvedURI(gTestPath));
+ dir.append(testURL);
+ dir.normalize();
+ testURL = Services.io.newFileURI(dir).spec;
+ }
+
+ let pageLoaded;
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ () => {
+ gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, testURL);
+ let browser = gBrowser.selectedBrowser;
+ if (testData.type === "certificateError") {
+ pageLoaded = BrowserTestUtils.waitForErrorPage(browser);
+ } else {
+ pageLoaded = BrowserTestUtils.browserLoaded(browser);
+ }
+ },
+ false
+ );
+ await pageLoaded;
+
+ let identityIcon = document.getElementById("identity-icon");
+
+ // Get the image url from the identity icon.
+ let identityIconImageURL = gBrowser.ownerGlobal
+ .getComputedStyle(identityIcon)
+ .getPropertyValue("list-style-image");
+
+ is(
+ identityIconImageURL,
+ testData.img_url,
+ "The identity icon has a correct image url."
+ );
+
+ BrowserTestUtils.removeTab(tab);
+ }
+});
diff --git a/browser/base/content/test/siteIdentity/browser_identityPopup_HttpsOnlyMode.js b/browser/base/content/test/siteIdentity/browser_identityPopup_HttpsOnlyMode.js
new file mode 100644
index 0000000000..a83f38e1f6
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_identityPopup_HttpsOnlyMode.js
@@ -0,0 +1,191 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const HTTPS_ONLY_PERMISSION = "https-only-load-insecure";
+const WEBSITE = scheme => `${scheme}://example.com`;
+
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.security.https_only_mode", true]],
+ });
+
+ // Site is already HTTPS, so the UI should not be visible.
+ await runTest({
+ name: "No HTTPS-Only UI",
+ initialScheme: "https",
+ initialPermission: 0,
+ permissionScheme: "https",
+ isUiVisible: false,
+ });
+
+ // Site gets upgraded to HTTPS, so the UI should be visible.
+ // Disabling HTTPS-Only Mode through the menulist should reload the page and
+ // set the permission accordingly.
+ await runTest({
+ name: "Disable HTTPS-Only",
+ initialScheme: "http",
+ initialPermission: 0,
+ permissionScheme: "https",
+ isUiVisible: true,
+ selectPermission: 1,
+ expectReload: true,
+ finalScheme: "https",
+ });
+
+ // HTTPS-Only Mode is disabled for this site, so the UI should be visible.
+ // Disabling HTTPS-Only Mode through the menulist should not reload the page
+ // but set the permission accordingly.
+ await runTest({
+ name: "Switch between off states",
+ initialScheme: "http",
+ initialPermission: 1,
+ permissionScheme: "http",
+ isUiVisible: true,
+ selectPermission: 2,
+ expectReload: false,
+ finalScheme: "http",
+ });
+
+ // HTTPS-Only Mode is disabled for this site, so the UI should be visible.
+ // Enabling HTTPS-Only Mode through the menulist should reload and upgrade the
+ // page and set the permission accordingly.
+ await runTest({
+ name: "Enable HTTPS-Only again",
+ initialScheme: "http",
+ initialPermission: 2,
+ permissionScheme: "http",
+ isUiVisible: true,
+ selectPermission: 0,
+ expectReload: true,
+ finalScheme: "https",
+ });
+});
+
+async function runTest(options) {
+ // Set the initial permission
+ setPermission(WEBSITE(options.permissionScheme), options.initialPermission);
+
+ await BrowserTestUtils.withNewTab(
+ WEBSITE(options.initialScheme),
+ async function (browser) {
+ const name = options.name + " | ";
+
+ // Check if the site has the expected scheme
+ is(
+ browser.currentURI.scheme,
+ options.permissionScheme,
+ name + "Expected scheme should match actual scheme"
+ );
+
+ // Open the identity popup.
+ let { gIdentityHandler } = gBrowser.ownerGlobal;
+ let promisePanelOpen = BrowserTestUtils.waitForEvent(
+ gBrowser.ownerGlobal,
+ "popupshown",
+ true,
+ event => event.target == gIdentityHandler._identityPopup
+ );
+ gIdentityHandler._identityIconBox.click();
+ await promisePanelOpen;
+
+ // Check if the HTTPS-Only UI is visible
+ const httpsOnlyUI = document.getElementById(
+ "identity-popup-security-httpsonlymode"
+ );
+ is(
+ gBrowser.ownerGlobal.getComputedStyle(httpsOnlyUI).display != "none",
+ options.isUiVisible,
+ options.isUiVisible
+ ? name + "HTTPS-Only UI should be visible."
+ : name + "HTTPS-Only UI shouldn't be visible."
+ );
+
+ // If it's not visible we can't do much else :)
+ if (!options.isUiVisible) {
+ return;
+ }
+
+ // Check if the value of the menulist matches the initial permission
+ const httpsOnlyMenulist = document.getElementById(
+ "identity-popup-security-httpsonlymode-menulist"
+ );
+ is(
+ parseInt(httpsOnlyMenulist.value, 10),
+ options.initialPermission,
+ name + "Menulist value should match expected permission value."
+ );
+
+ // Select another HTTPS-Only state and potentially wait for the page to reload
+ if (options.expectReload) {
+ const loaded = BrowserTestUtils.browserLoaded(browser);
+ httpsOnlyMenulist.getItemAtIndex(options.selectPermission).doCommand();
+ await loaded;
+ } else {
+ httpsOnlyMenulist.getItemAtIndex(options.selectPermission).doCommand();
+ }
+
+ // Check if the site has the expected scheme
+ is(
+ browser.currentURI.scheme,
+ options.finalScheme,
+ name + "Unexpected scheme after page reloaded."
+ );
+
+ // Check if the permission was sucessfully changed
+ is(
+ getPermission(WEBSITE(options.permissionScheme)),
+ options.selectPermission,
+ name + "Set permission should match the one selected from the menulist."
+ );
+ }
+ );
+
+ // Reset permission
+ Services.perms.removeAll();
+}
+
+function setPermission(url, newValue) {
+ let uri = Services.io.newURI(url);
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ uri,
+ {}
+ );
+ if (newValue === 0) {
+ Services.perms.removeFromPrincipal(principal, HTTPS_ONLY_PERMISSION);
+ } else if (newValue === 1) {
+ Services.perms.addFromPrincipal(
+ principal,
+ HTTPS_ONLY_PERMISSION,
+ Ci.nsIHttpsOnlyModePermission.LOAD_INSECURE_ALLOW,
+ Ci.nsIPermissionManager.EXPIRE_NEVER
+ );
+ } else {
+ Services.perms.addFromPrincipal(
+ principal,
+ HTTPS_ONLY_PERMISSION,
+ Ci.nsIHttpsOnlyModePermission.LOAD_INSECURE_ALLOW_SESSION,
+ Ci.nsIPermissionManager.EXPIRE_SESSION
+ );
+ }
+}
+
+function getPermission(url) {
+ let uri = Services.io.newURI(url);
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ uri,
+ {}
+ );
+ const state = Services.perms.testPermissionFromPrincipal(
+ principal,
+ HTTPS_ONLY_PERMISSION
+ );
+ switch (state) {
+ case Ci.nsIHttpsOnlyModePermission.LOAD_INSECURE_ALLOW_SESSION:
+ return 2; // Off temporarily
+ case Ci.nsIHttpsOnlyModePermission.LOAD_INSECURE_ALLOW:
+ return 1; // Off
+ default:
+ return 0; // On
+ }
+}
diff --git a/browser/base/content/test/siteIdentity/browser_identityPopup_clearSiteData.js b/browser/base/content/test/siteIdentity/browser_identityPopup_clearSiteData.js
new file mode 100644
index 0000000000..efc4f34310
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_identityPopup_clearSiteData.js
@@ -0,0 +1,245 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TEST_ORIGIN = "https://example.com";
+const TEST_SUB_ORIGIN = "https://test1.example.com";
+const TEST_ORIGIN_2 = "https://example.net";
+const REMOVE_DIALOG_URL =
+ "chrome://browser/content/preferences/dialogs/siteDataRemoveSelected.xhtml";
+
+// Greek IDN for 'example.test'.
+const TEST_IDN_ORIGIN =
+ "https://\u03C0\u03B1\u03C1\u03AC\u03B4\u03B5\u03B9\u03B3\u03BC\u03B1.\u03B4\u03BF\u03BA\u03B9\u03BC\u03AE";
+const TEST_PUNY_ORIGIN = "https://xn--hxajbheg2az3al.xn--jxalpdlp/";
+const TEST_PUNY_SUB_ORIGIN = "https://sub1.xn--hxajbheg2az3al.xn--jxalpdlp/";
+
+ChromeUtils.defineESModuleGetters(this, {
+ SiteDataTestUtils: "resource://testing-common/SiteDataTestUtils.sys.mjs",
+});
+
+async function testClearing(
+ testQuota,
+ testCookies,
+ testURI,
+ originA,
+ subOriginA,
+ originB
+) {
+ // Create a variant of originB which is partitioned under top level originA.
+ let { scheme, host } = Services.io.newURI(originA);
+ let partitionKey = `(${scheme},${host})`;
+
+ let { origin: originBPartitioned } =
+ Services.scriptSecurityManager.createContentPrincipal(
+ Services.io.newURI(originB),
+ { partitionKey }
+ );
+
+ // Add some test quota storage.
+ if (testQuota) {
+ await SiteDataTestUtils.addToIndexedDB(originA);
+ await SiteDataTestUtils.addToIndexedDB(subOriginA);
+ await SiteDataTestUtils.addToIndexedDB(originBPartitioned);
+ }
+
+ // Add some test cookies.
+ if (testCookies) {
+ SiteDataTestUtils.addToCookies({
+ origin: originA,
+ name: "test1",
+ value: "1",
+ });
+ SiteDataTestUtils.addToCookies({
+ origin: originA,
+ name: "test2",
+ value: "2",
+ });
+ SiteDataTestUtils.addToCookies({
+ origin: subOriginA,
+ name: "test3",
+ value: "1",
+ });
+
+ SiteDataTestUtils.addToCookies({
+ origin: originBPartitioned,
+ name: "test4",
+ value: "1",
+ });
+ }
+
+ await BrowserTestUtils.withNewTab(testURI, async function (browser) {
+ // Verify we have added quota storage.
+ if (testQuota) {
+ let usage = await SiteDataTestUtils.getQuotaUsage(originA);
+ Assert.greater(usage, 0, "Should have data for the base origin.");
+
+ usage = await SiteDataTestUtils.getQuotaUsage(subOriginA);
+ Assert.greater(usage, 0, "Should have data for the sub origin.");
+
+ usage = await SiteDataTestUtils.getQuotaUsage(originBPartitioned);
+ Assert.greater(usage, 0, "Should have data for the partitioned origin.");
+ }
+
+ // Open the identity popup.
+ let { gIdentityHandler } = gBrowser.ownerGlobal;
+ let promisePanelOpen = BrowserTestUtils.waitForEvent(
+ gBrowser.ownerGlobal,
+ "popupshown",
+ true,
+ event => event.target == gIdentityHandler._identityPopup
+ );
+ gIdentityHandler._identityIconBox.click();
+ await promisePanelOpen;
+
+ let clearFooter = document.getElementById(
+ "identity-popup-clear-sitedata-footer"
+ );
+ let clearButton = document.getElementById(
+ "identity-popup-clear-sitedata-button"
+ );
+ TestUtils.waitForCondition(
+ () => !clearFooter.hidden,
+ "The clear data footer is not hidden."
+ );
+
+ let cookiesCleared;
+ if (testCookies) {
+ cookiesCleared = Promise.all([
+ TestUtils.topicObserved(
+ "cookie-changed",
+ (subj, data) => data == "deleted" && subj.name == "test1"
+ ),
+ TestUtils.topicObserved(
+ "cookie-changed",
+ (subj, data) => data == "deleted" && subj.name == "test2"
+ ),
+ TestUtils.topicObserved(
+ "cookie-changed",
+ (subj, data) => data == "deleted" && subj.name == "test3"
+ ),
+ TestUtils.topicObserved(
+ "cookie-changed",
+ (subj, data) => data == "deleted" && subj.name == "test4"
+ ),
+ ]);
+ }
+
+ // Click the "Clear data" button.
+ let siteDataUpdated = TestUtils.topicObserved(
+ "sitedatamanager:sites-updated"
+ );
+ let hideEvent = BrowserTestUtils.waitForEvent(
+ gIdentityHandler._identityPopup,
+ "popuphidden"
+ );
+ let removeDialogPromise = BrowserTestUtils.promiseAlertDialogOpen(
+ "accept",
+ REMOVE_DIALOG_URL
+ );
+ clearButton.click();
+ await hideEvent;
+ await removeDialogPromise;
+
+ await siteDataUpdated;
+
+ // Check that cookies were deleted.
+ if (testCookies) {
+ await cookiesCleared;
+ let uri = Services.io.newURI(originA);
+ is(
+ Services.cookies.countCookiesFromHost(uri.host),
+ 0,
+ "Cookies from the base domain should be cleared"
+ );
+ uri = Services.io.newURI(subOriginA);
+ is(
+ Services.cookies.countCookiesFromHost(uri.host),
+ 0,
+ "Cookies from the sub domain should be cleared"
+ );
+ ok(
+ !SiteDataTestUtils.hasCookies(originBPartitioned),
+ "Partitioned cookies should be cleared"
+ );
+ }
+
+ // Check that quota storage was deleted.
+ if (testQuota) {
+ await TestUtils.waitForCondition(async () => {
+ let usage = await SiteDataTestUtils.getQuotaUsage(originA);
+ return usage == 0;
+ }, "Should have no data for the base origin.");
+
+ let usage = await SiteDataTestUtils.getQuotaUsage(subOriginA);
+ is(usage, 0, "Should have no data for the sub origin.");
+
+ usage = await SiteDataTestUtils.getQuotaUsage(originBPartitioned);
+ is(usage, 0, "Should have no data for the partitioned origin.");
+ }
+
+ // Open the site identity panel again to check that the button isn't shown anymore.
+ promisePanelOpen = BrowserTestUtils.waitForEvent(
+ gIdentityHandler._identityPopup,
+ "popupshown"
+ );
+ gIdentityHandler._identityIconBox.click();
+ await promisePanelOpen;
+
+ // Wait for a second to see if the button is shown.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(c => setTimeout(c, 1000));
+
+ ok(
+ clearFooter.hidden,
+ "The clear data footer is hidden after clearing data."
+ );
+ });
+}
+
+// Test removing quota managed storage.
+add_task(async function test_ClearSiteData() {
+ await testClearing(
+ true,
+ false,
+ TEST_ORIGIN,
+ TEST_ORIGIN,
+ TEST_SUB_ORIGIN,
+ TEST_ORIGIN_2
+ );
+});
+
+// Test removing cookies.
+add_task(async function test_ClearCookies() {
+ await testClearing(
+ false,
+ true,
+ TEST_ORIGIN,
+ TEST_ORIGIN,
+ TEST_SUB_ORIGIN,
+ TEST_ORIGIN_2
+ );
+});
+
+// Test removing both.
+add_task(async function test_ClearCookiesAndSiteData() {
+ await testClearing(
+ true,
+ true,
+ TEST_ORIGIN,
+ TEST_ORIGIN,
+ TEST_SUB_ORIGIN,
+ TEST_ORIGIN_2
+ );
+});
+
+// Test IDN Domains
+add_task(async function test_IDN_ClearCookiesAndSiteData() {
+ await testClearing(
+ true,
+ true,
+ TEST_IDN_ORIGIN,
+ TEST_PUNY_ORIGIN,
+ TEST_PUNY_SUB_ORIGIN,
+ TEST_ORIGIN_2
+ );
+});
diff --git a/browser/base/content/test/siteIdentity/browser_identityPopup_clearSiteData_extensions.js b/browser/base/content/test/siteIdentity/browser_identityPopup_clearSiteData_extensions.js
new file mode 100644
index 0000000000..2d0d9f7068
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_identityPopup_clearSiteData_extensions.js
@@ -0,0 +1,80 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+/*
+ * Test for Bug 1661534 - Extension page: "Clear Cookies and Site Data"
+ * does nothing.
+ *
+ * Expected behavior: when viewing a page controlled by a WebExtension,
+ * the "Clear Cookies and Site Data..." button should not be visible.
+ */
+
+add_task(async function testClearSiteDataFooterHiddenForExtensions() {
+ // Create an extension that opens an options page
+ let extension = ExtensionTestUtils.loadExtension({
+ useAddonManager: "temporary",
+
+ manifest: {
+ permissions: ["tabs"],
+ options_ui: {
+ page: "options.html",
+ open_in_tab: true,
+ },
+ },
+ files: {
+ "options.html": `<!DOCTYPE html>
+ <html>
+ <head>
+ <meta charset="utf-8">
+ </head>
+ <body>
+ <h1>This is a test options page for a WebExtension</h1>
+ </body>
+ </html>`,
+ },
+ async background() {
+ await browser.runtime.openOptionsPage();
+ browser.test.sendMessage("optionsopened");
+ },
+ });
+
+ // Run the extension and wait until its options page has finished loading
+ let browser = gBrowser.selectedBrowser;
+ let browserLoadedPromise = BrowserTestUtils.browserLoaded(browser);
+ await extension.startup();
+ await extension.awaitMessage("optionsopened");
+ await browserLoadedPromise;
+
+ await SpecialPowers.spawn(browser, [], () => {
+ ok(
+ content.document.documentURI.startsWith("moz-extension://"),
+ "Extension page has now finished loading in the browser window"
+ );
+ });
+
+ // Open the site identity popup
+ let { gIdentityHandler } = gBrowser.ownerGlobal;
+ let promisePanelOpen = BrowserTestUtils.waitForEvent(
+ gBrowser.ownerGlobal,
+ "popupshown",
+ true,
+ event => event.target == gIdentityHandler._identityPopup
+ );
+ gIdentityHandler._identityIconBox.click();
+ await promisePanelOpen;
+
+ let clearSiteDataFooter = document.getElementById(
+ "identity-popup-clear-sitedata-footer"
+ );
+
+ ok(
+ clearSiteDataFooter.hidden,
+ "The clear site data footer is hidden on a WebExtension page."
+ );
+
+ // Unload the extension
+ await extension.unload();
+});
diff --git a/browser/base/content/test/siteIdentity/browser_identityPopup_custom_roots.js b/browser/base/content/test/siteIdentity/browser_identityPopup_custom_roots.js
new file mode 100644
index 0000000000..2b9ef53bb0
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_identityPopup_custom_roots.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* Test that the UI for imported root certificates shows up correctly in the identity popup.
+ */
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+// This test is incredibly simple, because our test framework already
+// imports root certificates by default, so we just visit example.com
+// and verify that the custom root certificates UI is visible.
+add_task(async function test_https() {
+ await BrowserTestUtils.withNewTab("https://example.com", async function () {
+ let promisePanelOpen = BrowserTestUtils.waitForEvent(
+ window,
+ "popupshown",
+ true,
+ event => event.target == gIdentityHandler._identityPopup
+ );
+
+ gIdentityHandler._identityIconBox.click();
+ await promisePanelOpen;
+ let customRootWarning = document.getElementById(
+ "identity-popup-security-decription-custom-root"
+ );
+ ok(
+ BrowserTestUtils.is_visible(customRootWarning),
+ "custom root warning is visible"
+ );
+
+ let securityView = document.getElementById("identity-popup-securityView");
+ let shown = BrowserTestUtils.waitForEvent(securityView, "ViewShown");
+ document.getElementById("identity-popup-security-button").click();
+ await shown;
+
+ let subPanelInfo = document.getElementById(
+ "identity-popup-content-verifier-unknown"
+ );
+ ok(
+ BrowserTestUtils.is_visible(subPanelInfo),
+ "custom root warning in sub panel is visible"
+ );
+ });
+});
+
+// Also check that there are conditions where this isn't shown.
+add_task(async function test_http() {
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ await BrowserTestUtils.withNewTab("http://example.com", async function () {
+ let promisePanelOpen = BrowserTestUtils.waitForEvent(
+ window,
+ "popupshown",
+ true,
+ event => event.target == gIdentityHandler._identityPopup
+ );
+ gIdentityHandler._identityIconBox.click();
+ await promisePanelOpen;
+ let customRootWarning = document.getElementById(
+ "identity-popup-security-decription-custom-root"
+ );
+ ok(
+ BrowserTestUtils.is_hidden(customRootWarning),
+ "custom root warning is hidden"
+ );
+
+ let securityView = document.getElementById("identity-popup-securityView");
+ let shown = BrowserTestUtils.waitForEvent(securityView, "ViewShown");
+ document.getElementById("identity-popup-security-button").click();
+ await shown;
+
+ let subPanelInfo = document.getElementById(
+ "identity-popup-content-verifier-unknown"
+ );
+ ok(
+ BrowserTestUtils.is_hidden(subPanelInfo),
+ "custom root warning in sub panel is hidden"
+ );
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/browser_identityPopup_focus.js b/browser/base/content/test/siteIdentity/browser_identityPopup_focus.js
new file mode 100644
index 0000000000..80e70619ff
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_identityPopup_focus.js
@@ -0,0 +1,120 @@
+/* Tests the focus behavior of the identity popup. */
+
+// Focusing on the identity box is handled by the ToolbarKeyboardNavigator
+// component (see browser/base/content/browser-toolbarKeyNav.js).
+async function focusIdentityBox() {
+ gURLBar.inputField.focus();
+ is(document.activeElement, gURLBar.inputField, "urlbar should be focused");
+ const focused = BrowserTestUtils.waitForEvent(
+ gIdentityHandler._identityIconBox,
+ "focus"
+ );
+ EventUtils.synthesizeKey("VK_TAB", { shiftKey: true });
+ EventUtils.synthesizeKey("ArrowRight");
+ await focused;
+}
+
+// Access the identity popup via mouseclick. Focus should not be moved inside.
+add_task(async function testIdentityPopupFocusClick() {
+ await SpecialPowers.pushPrefEnv({ set: [["accessibility.tabfocus", 7]] });
+ await BrowserTestUtils.withNewTab("https://example.com", async function () {
+ let shown = BrowserTestUtils.waitForEvent(
+ window,
+ "popupshown",
+ true,
+ event => event.target == gIdentityHandler._identityPopup
+ );
+ EventUtils.synthesizeMouseAtCenter(gIdentityHandler._identityIconBox, {});
+ await shown;
+ isnot(
+ Services.focus.focusedElement,
+ document.getElementById("identity-popup-security-button")
+ );
+ });
+});
+
+// Access the identity popup via keyboard. Focus should be moved inside.
+add_task(async function testIdentityPopupFocusKeyboard() {
+ await SpecialPowers.pushPrefEnv({ set: [["accessibility.tabfocus", 7]] });
+ await BrowserTestUtils.withNewTab("https://example.com", async function () {
+ await focusIdentityBox();
+ let shown = BrowserTestUtils.waitForEvent(
+ window,
+ "popupshown",
+ true,
+ event => event.target == gIdentityHandler._identityPopup
+ );
+ EventUtils.sendString(" ");
+ await shown;
+ is(
+ Services.focus.focusedElement,
+ document.getElementById("identity-popup-security-button")
+ );
+ });
+});
+
+// Access the Site Security panel, then move focus with the tab key.
+// Tabbing should be able to reach the More Information button.
+add_task(async function testSiteSecurityTabOrder() {
+ await SpecialPowers.pushPrefEnv({ set: [["accessibility.tabfocus", 7]] });
+ await BrowserTestUtils.withNewTab("https://example.com", async function () {
+ // 1. Access the identity popup.
+ await focusIdentityBox();
+ let shown = BrowserTestUtils.waitForEvent(
+ window,
+ "popupshown",
+ true,
+ event => event.target == gIdentityHandler._identityPopup
+ );
+ EventUtils.sendString(" ");
+ await shown;
+ is(
+ Services.focus.focusedElement,
+ document.getElementById("identity-popup-security-button")
+ );
+
+ // 2. Access the Site Security section.
+ let securityView = document.getElementById("identity-popup-securityView");
+ shown = BrowserTestUtils.waitForEvent(securityView, "ViewShown");
+ EventUtils.sendString(" ");
+ await shown;
+
+ // 3. Custom root learn more info should be focused by default
+ // This is probably not present in real-world scenarios, but needs to be present in our test infrastructure.
+ let customRootLearnMore = document.getElementById(
+ "identity-popup-custom-root-learn-more"
+ );
+ is(
+ Services.focus.focusedElement,
+ customRootLearnMore,
+ "learn more option for custom roots is focused"
+ );
+
+ // 4. First press of tab should move to the More Information button.
+ let moreInfoButton = document.getElementById("identity-popup-more-info");
+ let focused = BrowserTestUtils.waitForEvent(
+ gIdentityHandler._identityPopup,
+ "focusin"
+ );
+ EventUtils.sendKey("tab");
+ await focused;
+ is(
+ Services.focus.focusedElement,
+ moreInfoButton,
+ "more info button is focused"
+ );
+
+ // 5. Second press of tab should focus the Back button.
+ let backButton = gIdentityHandler._identityPopup.querySelector(
+ ".subviewbutton-back"
+ );
+ // Wait for focus to move somewhere. We use focusin because focus doesn't bubble.
+ focused = BrowserTestUtils.waitForEvent(
+ gIdentityHandler._identityPopup,
+ "focusin"
+ );
+ EventUtils.sendKey("tab");
+ await focused;
+ is(Services.focus.focusedElement, backButton, "back button is focused");
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/browser_identity_UI.js b/browser/base/content/test/siteIdentity/browser_identity_UI.js
new file mode 100644
index 0000000000..bda5e225b0
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_identity_UI.js
@@ -0,0 +1,192 @@
+/* Tests for correct behaviour of getHostForDisplay on identity handler */
+
+requestLongerTimeout(2);
+
+// Greek IDN for 'example.test'.
+var idnDomain =
+ "\u03C0\u03B1\u03C1\u03AC\u03B4\u03B5\u03B9\u03B3\u03BC\u03B1.\u03B4\u03BF\u03BA\u03B9\u03BC\u03AE";
+var tests = [
+ {
+ name: "normal domain",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ location: "http://test1.example.org/",
+ hostForDisplay: "test1.example.org",
+ hasSubview: true,
+ },
+ {
+ name: "view-source",
+ location: "view-source:http://example.com/",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ newURI: "http://example.com/",
+ hostForDisplay: "example.com",
+ hasSubview: true,
+ },
+ {
+ name: "normal HTTPS",
+ location: "https://example.com/",
+ hostForDisplay: "example.com",
+ hasSubview: true,
+ },
+ {
+ name: "IDN subdomain",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ location: "http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp/",
+ hostForDisplay: "sub1." + idnDomain,
+ hasSubview: true,
+ },
+ {
+ name: "subdomain with port",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ location: "http://sub1.test1.example.org:8000/",
+ hostForDisplay: "sub1.test1.example.org",
+ hasSubview: true,
+ },
+ {
+ name: "subdomain HTTPS",
+ location: "https://test1.example.com/",
+ hostForDisplay: "test1.example.com",
+ hasSubview: true,
+ },
+ {
+ name: "view-source HTTPS",
+ location: "view-source:https://example.com/",
+ newURI: "https://example.com/",
+ hostForDisplay: "example.com",
+ hasSubview: true,
+ },
+ {
+ name: "IP address",
+ location: "http://127.0.0.1:8888/",
+ hostForDisplay: "127.0.0.1",
+ hasSubview: false,
+ },
+ {
+ name: "about:certificate",
+ location:
+ "about:certificate?cert=MIIHQjCCBiqgAwIBAgIQCgYwQn9bvO&cert=1pVzllk7ZFHzANBgkqhkiG9w0BAQ",
+ hostForDisplay: "about:certificate",
+ hasSubview: false,
+ },
+ {
+ name: "about:reader",
+ location: "about:reader?url=http://example.com",
+ hostForDisplay: "example.com",
+ hasSubview: false,
+ },
+ {
+ name: "chrome:",
+ location: "chrome://global/skin/in-content/info-pages.css",
+ hostForDisplay: "chrome://global/skin/in-content/info-pages.css",
+ hasSubview: false,
+ },
+];
+
+add_task(async function test() {
+ ok(gIdentityHandler, "gIdentityHandler should exist");
+
+ await BrowserTestUtils.openNewForegroundTab(gBrowser);
+
+ for (let i = 0; i < tests.length; i++) {
+ await runTest(i, true);
+ }
+
+ gBrowser.removeCurrentTab();
+ await BrowserTestUtils.openNewForegroundTab(gBrowser);
+
+ for (let i = tests.length - 1; i >= 0; i--) {
+ await runTest(i, false);
+ }
+
+ gBrowser.removeCurrentTab();
+});
+
+async function runTest(i, forward) {
+ let currentTest = tests[i];
+ let testDesc = "#" + i + " (" + currentTest.name + ")";
+ if (!forward) {
+ testDesc += " (second time)";
+ }
+
+ info("Running test " + testDesc);
+
+ let popupHidden = null;
+ if ((forward && i > 0) || (!forward && i < tests.length - 1)) {
+ popupHidden = BrowserTestUtils.waitForEvent(
+ gIdentityHandler._identityPopup,
+ "popuphidden"
+ );
+ }
+
+ let loaded = BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ currentTest.location
+ );
+ BrowserTestUtils.loadURIString(
+ gBrowser.selectedBrowser,
+ currentTest.location
+ );
+ await loaded;
+ await popupHidden;
+ ok(
+ !gIdentityHandler._identityPopup ||
+ BrowserTestUtils.is_hidden(gIdentityHandler._identityPopup),
+ "Control Center is hidden"
+ );
+
+ // Sanity check other values, and the value of gIdentityHandler.getHostForDisplay()
+ is(
+ gIdentityHandler._uri.spec,
+ currentTest.newURI || currentTest.location,
+ "location matches for test " + testDesc
+ );
+ // getHostForDisplay can't be called for all modes
+ if (currentTest.hostForDisplay !== null) {
+ is(
+ gIdentityHandler.getHostForDisplay(),
+ currentTest.hostForDisplay,
+ "hostForDisplay matches for test " + testDesc
+ );
+ }
+
+ // Open the Control Center and make sure it closes after nav (Bug 1207542).
+ let popupShown = BrowserTestUtils.waitForEvent(
+ window,
+ "popupshown",
+ true,
+ event => event.target == gIdentityHandler._identityPopup
+ );
+ gIdentityHandler._identityIconBox.click();
+ info("Waiting for the Control Center to be shown");
+ await popupShown;
+ ok(
+ !BrowserTestUtils.is_hidden(gIdentityHandler._identityPopup),
+ "Control Center is visible"
+ );
+ let displayedHost = currentTest.hostForDisplay || currentTest.location;
+ ok(
+ gIdentityHandler._identityPopupMainViewHeaderLabel.textContent.includes(
+ displayedHost
+ ),
+ "identity UI header shows the host for test " + testDesc
+ );
+
+ let securityButton = gBrowser.ownerDocument.querySelector(
+ "#identity-popup-security-button"
+ );
+ is(
+ securityButton.disabled,
+ !currentTest.hasSubview,
+ "Security button has correct disabled state"
+ );
+ if (currentTest.hasSubview) {
+ // Show the subview, which is an easy way in automation to reproduce
+ // Bug 1207542, where the CC wouldn't close on navigation.
+ let promiseViewShown = BrowserTestUtils.waitForEvent(
+ gIdentityHandler._identityPopup,
+ "ViewShown"
+ );
+ securityButton.click();
+ await promiseViewShown;
+ }
+}
diff --git a/browser/base/content/test/siteIdentity/browser_iframe_navigation.js b/browser/base/content/test/siteIdentity/browser_iframe_navigation.js
new file mode 100644
index 0000000000..ac2884d31a
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_iframe_navigation.js
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the site identity icon and related machinery reflects the correct
+// security state after navigating an iframe in various contexts.
+// See bug 1490982.
+
+const ROOT_URI = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+const SECURE_TEST_URI = ROOT_URI + "iframe_navigation.html";
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+const INSECURE_TEST_URI = SECURE_TEST_URI.replace("https://", "http://");
+
+// From a secure URI, navigate the iframe to about:blank (should still be
+// secure).
+add_task(async function () {
+ let uri = SECURE_TEST_URI + "#blank";
+ await BrowserTestUtils.withNewTab(uri, async browser => {
+ let identityMode = window.document.getElementById("identity-box").className;
+ is(identityMode, "verifiedDomain", "identity should be secure before");
+
+ await SpecialPowers.spawn(browser, [], async () => {
+ content.postMessage("", "*"); // This kicks off the navigation.
+ await ContentTaskUtils.waitForCondition(() => {
+ return !content.document.body.classList.contains("running");
+ });
+ });
+
+ let newIdentityMode =
+ window.document.getElementById("identity-box").className;
+ is(newIdentityMode, "verifiedDomain", "identity should be secure after");
+ });
+});
+
+// From a secure URI, navigate the iframe to an insecure URI (http://...)
+// (mixed active content should be blocked, should still be secure).
+add_task(async function () {
+ let uri = SECURE_TEST_URI + "#insecure";
+ await BrowserTestUtils.withNewTab(uri, async browser => {
+ let identityMode = window.document.getElementById("identity-box").className;
+ is(identityMode, "verifiedDomain", "identity should be secure before");
+
+ await SpecialPowers.spawn(browser, [], async () => {
+ content.postMessage("", "*"); // This kicks off the navigation.
+ await ContentTaskUtils.waitForCondition(() => {
+ return !content.document.body.classList.contains("running");
+ });
+ });
+
+ let newIdentityMode =
+ window.document.getElementById("identity-box").classList;
+ ok(
+ newIdentityMode.contains("mixedActiveBlocked"),
+ "identity should be blocked mixed active content after"
+ );
+ ok(
+ newIdentityMode.contains("verifiedDomain"),
+ "identity should still contain 'verifiedDomain'"
+ );
+ is(newIdentityMode.length, 2, "shouldn't have any other identity states");
+ });
+});
+
+// From an insecure URI (http://..), navigate the iframe to about:blank (should
+// still be insecure).
+add_task(async function () {
+ let uri = INSECURE_TEST_URI + "#blank";
+ await BrowserTestUtils.withNewTab(uri, async browser => {
+ let identityMode = window.document.getElementById("identity-box").className;
+ is(identityMode, "notSecure", "identity should be 'not secure' before");
+
+ await SpecialPowers.spawn(browser, [], async () => {
+ content.postMessage("", "*"); // This kicks off the navigation.
+ await ContentTaskUtils.waitForCondition(() => {
+ return !content.document.body.classList.contains("running");
+ });
+ });
+
+ let newIdentityMode =
+ window.document.getElementById("identity-box").className;
+ is(newIdentityMode, "notSecure", "identity should be 'not secure' after");
+ });
+});
+
+// From an insecure URI (http://..), navigate the iframe to a secure URI
+// (https://...) (should still be insecure).
+add_task(async function () {
+ let uri = INSECURE_TEST_URI + "#secure";
+ await BrowserTestUtils.withNewTab(uri, async browser => {
+ let identityMode = window.document.getElementById("identity-box").className;
+ is(identityMode, "notSecure", "identity should be 'not secure' before");
+
+ await SpecialPowers.spawn(browser, [], async () => {
+ content.postMessage("", "*"); // This kicks off the navigation.
+ await ContentTaskUtils.waitForCondition(() => {
+ return !content.document.body.classList.contains("running");
+ });
+ });
+
+ let newIdentityMode =
+ window.document.getElementById("identity-box").className;
+ is(newIdentityMode, "notSecure", "identity should be 'not secure' after");
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/browser_ignore_same_page_navigation.js b/browser/base/content/test/siteIdentity/browser_ignore_same_page_navigation.js
new file mode 100644
index 0000000000..86ec70a0cf
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_ignore_same_page_navigation.js
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that the nsISecureBrowserUI implementation doesn't send extraneous OnSecurityChange events
+// when it receives OnLocationChange events with the LOCATION_CHANGE_SAME_DOCUMENT flag set.
+
+add_task(async function () {
+ await BrowserTestUtils.withNewTab("about:blank", async browser => {
+ let onLocationChangeCount = 0;
+ let onSecurityChangeCount = 0;
+ let progressListener = {
+ onStateChange() {},
+ onLocationChange() {
+ onLocationChangeCount++;
+ },
+ onSecurityChange() {
+ onSecurityChangeCount++;
+ },
+ onProgressChange() {},
+ onStatusChange() {},
+
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIWebProgressListener",
+ "nsISupportsWeakReference",
+ ]),
+ };
+ browser.addProgressListener(progressListener, Ci.nsIWebProgress.NOTIFY_ALL);
+
+ let uri =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "dummy_page.html";
+ BrowserTestUtils.loadURIString(browser, uri);
+ await BrowserTestUtils.browserLoaded(browser, false, uri);
+ is(onLocationChangeCount, 1, "should have 1 onLocationChange event");
+ is(onSecurityChangeCount, 1, "should have 1 onSecurityChange event");
+ await SpecialPowers.spawn(browser, [], async () => {
+ content.history.pushState({}, "", "https://example.com");
+ });
+ is(onLocationChangeCount, 2, "should have 2 onLocationChange events");
+ is(
+ onSecurityChangeCount,
+ 1,
+ "should still have only 1 onSecurityChange event"
+ );
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/browser_mcb_redirect.js b/browser/base/content/test/siteIdentity/browser_mcb_redirect.js
new file mode 100644
index 0000000000..df7f6be15c
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_mcb_redirect.js
@@ -0,0 +1,360 @@
+/*
+ * Description of the Tests for
+ * - Bug 418354 - Call Mixed content blocking on redirects
+ *
+ * Single redirect script tests
+ * 1. Load a script over https inside an https page
+ * - the server responds with a 302 redirect to a >> HTTP << script
+ * - the doorhanger should appear!
+ *
+ * 2. Load a script over https inside an http page
+ * - the server responds with a 302 redirect to a >> HTTP << script
+ * - the doorhanger should not appear!
+ *
+ * Single redirect image tests
+ * 3. Load an image over https inside an https page
+ * - the server responds with a 302 redirect to a >> HTTP << image
+ * - the image should not load
+ *
+ * 4. Load an image over https inside an http page
+ * - the server responds with a 302 redirect to a >> HTTP << image
+ * - the image should load and get cached
+ *
+ * Single redirect cached image tests
+ * 5. Using offline mode to ensure we hit the cache, load a cached image over
+ * https inside an http page
+ * - the server would have responded with a 302 redirect to a >> HTTP <<
+ * image, but instead we try to use the cached image.
+ * - the image should load
+ *
+ * 6. Using offline mode to ensure we hit the cache, load a cached image over
+ * https inside an https page
+ * - the server would have responded with a 302 redirect to a >> HTTP <<
+ * image, but instead we try to use the cached image.
+ * - the image should not load
+ *
+ * Double redirect image test
+ * 7. Load an image over https inside an http page
+ * - the server responds with a 302 redirect to a >> HTTP << server
+ * - the HTTP server responds with a 302 redirect to a >> HTTPS << image
+ * - the image should load and get cached
+ *
+ * Double redirect cached image tests
+ * 8. Using offline mode to ensure we hit the cache, load a cached image over
+ * https inside an http page
+ * - the image would have gone through two redirects: HTTPS->HTTP->HTTPS,
+ * but instead we try to use the cached image.
+ * - the image should load
+ *
+ * 9. Using offline mode to ensure we hit the cache, load a cached image over
+ * https inside an https page
+ * - the image would have gone through two redirects: HTTPS->HTTP->HTTPS,
+ * but instead we try to use the cached image.
+ * - the image should not load
+ */
+
+const PREF_ACTIVE = "security.mixed_content.block_active_content";
+const PREF_DISPLAY = "security.mixed_content.block_display_content";
+const PREF_DISPLAY_UPGRADE = "security.mixed_content.upgrade_display_content";
+const HTTPS_TEST_ROOT = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+const HTTP_TEST_ROOT = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+);
+const PREF_INSECURE_ICON = "security.insecure_connection_icon.enabled";
+
+var origBlockActive;
+var origBlockDisplay;
+var origUpgradeDisplay;
+var origInsecurePref;
+var gTestBrowser = null;
+
+// ------------------------ Helper Functions ---------------------
+
+registerCleanupFunction(function () {
+ // Set preferences back to their original values
+ Services.prefs.setBoolPref(PREF_ACTIVE, origBlockActive);
+ Services.prefs.setBoolPref(PREF_DISPLAY, origBlockDisplay);
+ Services.prefs.setBoolPref(PREF_DISPLAY_UPGRADE, origUpgradeDisplay);
+ Services.prefs.setBoolPref(PREF_INSECURE_ICON, origInsecurePref);
+
+ // Make sure we are online again
+ Services.io.offline = false;
+});
+
+function cleanUpAfterTests() {
+ gBrowser.removeCurrentTab();
+ window.focus();
+ finish();
+}
+
+// ------------------------ Test 1 ------------------------------
+
+function test1() {
+ Services.prefs.setBoolPref(PREF_INSECURE_ICON, false);
+
+ var url = HTTPS_TEST_ROOT + "test_mcb_redirect.html";
+ BrowserTestUtils.browserLoaded(gTestBrowser, false, url).then(
+ checkUIForTest1
+ );
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+}
+
+function testInsecure1() {
+ Services.prefs.setBoolPref(PREF_INSECURE_ICON, true);
+
+ var url = HTTPS_TEST_ROOT + "test_mcb_redirect.html";
+ BrowserTestUtils.browserLoaded(gTestBrowser, false, url).then(
+ checkUIForTest1
+ );
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+}
+
+async function checkUIForTest1() {
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: false,
+ });
+
+ SpecialPowers.spawn(gTestBrowser, [], async function () {
+ var expected = "script blocked";
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.getElementById("mctestdiv").innerHTML == expected,
+ "OK: Expected result in innerHTML for Test1!"
+ );
+ }).then(test2);
+}
+
+// ------------------------ Test 2 ------------------------------
+
+function test2() {
+ var url = HTTP_TEST_ROOT + "test_mcb_redirect.html";
+ BrowserTestUtils.browserLoaded(gTestBrowser, false, url).then(
+ checkUIForTest2
+ );
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+}
+
+async function checkUIForTest2() {
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: false,
+ activeBlocked: false,
+ passiveLoaded: false,
+ });
+
+ SpecialPowers.spawn(gTestBrowser, [], async function () {
+ var expected = "script executed";
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.getElementById("mctestdiv").innerHTML == expected,
+ "OK: Expected result in innerHTML for Test2!"
+ );
+ }).then(test3);
+}
+
+// ------------------------ Test 3 ------------------------------
+// HTTPS page loading insecure image
+function test3() {
+ info("test3");
+ var url = HTTPS_TEST_ROOT + "test_mcb_redirect_image.html";
+ BrowserTestUtils.browserLoaded(gTestBrowser, false, url).then(
+ checkLoadEventForTest3
+ );
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+}
+
+function checkLoadEventForTest3() {
+ SpecialPowers.spawn(gTestBrowser, [], async function () {
+ var expected = "image blocked";
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.getElementById("mctestdiv").innerHTML == expected,
+ "OK: Expected result in innerHTML for Test3!"
+ );
+ }).then(test4);
+}
+
+// ------------------------ Test 4 ------------------------------
+// HTTP page loading insecure image
+function test4() {
+ info("test4");
+ var url = HTTP_TEST_ROOT + "test_mcb_redirect_image.html";
+ BrowserTestUtils.browserLoaded(gTestBrowser, false, url).then(
+ checkLoadEventForTest4
+ );
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+}
+
+function checkLoadEventForTest4() {
+ SpecialPowers.spawn(gTestBrowser, [], async function () {
+ var expected = "image loaded";
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.getElementById("mctestdiv").innerHTML == expected,
+ "OK: Expected result in innerHTML for Test4!"
+ );
+ }).then(test5);
+}
+
+// ------------------------ Test 5 ------------------------------
+// HTTP page laoding insecure cached image
+// Assuming test 4 succeeded, the image has already been loaded once
+// and hence should be cached per the sjs cache-control header
+// Going into offline mode to ensure we are loading from the cache.
+function test5() {
+ // Go into offline mode
+ info("test5");
+ Services.io.offline = true;
+ var url = HTTP_TEST_ROOT + "test_mcb_redirect_image.html";
+ BrowserTestUtils.browserLoaded(gTestBrowser, false, url).then(
+ checkLoadEventForTest5
+ );
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+}
+
+function checkLoadEventForTest5() {
+ SpecialPowers.spawn(gTestBrowser, [], async function () {
+ var expected = "image loaded";
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.getElementById("mctestdiv").innerHTML == expected,
+ "OK: Expected result in innerHTML for Test5!"
+ );
+ }).then(() => {
+ // Go back online
+ Services.io.offline = false;
+ test6();
+ });
+}
+
+// ------------------------ Test 6 ------------------------------
+// HTTPS page loading insecure cached image
+// Assuming test 4 succeeded, the image has already been loaded once
+// and hence should be cached per the sjs cache-control header
+// Going into offline mode to ensure we are loading from the cache.
+function test6() {
+ // Go into offline mode
+ info("test6");
+ Services.io.offline = true;
+ var url = HTTPS_TEST_ROOT + "test_mcb_redirect_image.html";
+ BrowserTestUtils.browserLoaded(gTestBrowser, false, url).then(
+ checkLoadEventForTest6
+ );
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+}
+
+function checkLoadEventForTest6() {
+ SpecialPowers.spawn(gTestBrowser, [], async function () {
+ var expected = "image blocked";
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.getElementById("mctestdiv").innerHTML == expected,
+ "OK: Expected result in innerHTML for Test6!"
+ );
+ }).then(() => {
+ // Go back online
+ Services.io.offline = false;
+ test7();
+ });
+}
+
+// ------------------------ Test 7 ------------------------------
+// HTTP page loading insecure image that went through a double redirect
+function test7() {
+ var url = HTTP_TEST_ROOT + "test_mcb_double_redirect_image.html";
+ BrowserTestUtils.browserLoaded(gTestBrowser, false, url).then(
+ checkLoadEventForTest7
+ );
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+}
+
+function checkLoadEventForTest7() {
+ SpecialPowers.spawn(gTestBrowser, [], async function () {
+ var expected = "image loaded";
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.getElementById("mctestdiv").innerHTML == expected,
+ "OK: Expected result in innerHTML for Test7!"
+ );
+ }).then(test8);
+}
+
+// ------------------------ Test 8 ------------------------------
+// HTTP page loading insecure cached image that went through a double redirect
+// Assuming test 7 succeeded, the image has already been loaded once
+// and hence should be cached per the sjs cache-control header
+// Going into offline mode to ensure we are loading from the cache.
+function test8() {
+ // Go into offline mode
+ Services.io.offline = true;
+ var url = HTTP_TEST_ROOT + "test_mcb_double_redirect_image.html";
+ BrowserTestUtils.browserLoaded(gTestBrowser, false, url).then(
+ checkLoadEventForTest8
+ );
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+}
+
+function checkLoadEventForTest8() {
+ SpecialPowers.spawn(gTestBrowser, [], async function () {
+ var expected = "image loaded";
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.getElementById("mctestdiv").innerHTML == expected,
+ "OK: Expected result in innerHTML for Test8!"
+ );
+ }).then(() => {
+ // Go back online
+ Services.io.offline = false;
+ test9();
+ });
+}
+
+// ------------------------ Test 9 ------------------------------
+// HTTPS page loading insecure cached image that went through a double redirect
+// Assuming test 7 succeeded, the image has already been loaded once
+// and hence should be cached per the sjs cache-control header
+// Going into offline mode to ensure we are loading from the cache.
+function test9() {
+ // Go into offline mode
+ Services.io.offline = true;
+ var url = HTTPS_TEST_ROOT + "test_mcb_double_redirect_image.html";
+ BrowserTestUtils.browserLoaded(gTestBrowser, false, url).then(
+ checkLoadEventForTest9
+ );
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+}
+
+function checkLoadEventForTest9() {
+ SpecialPowers.spawn(gTestBrowser, [], async function () {
+ var expected = "image blocked";
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.getElementById("mctestdiv").innerHTML == expected,
+ "OK: Expected result in innerHTML for Test9!"
+ );
+ }).then(() => {
+ // Go back online
+ Services.io.offline = false;
+ cleanUpAfterTests();
+ });
+}
+
+// ------------------------ SETUP ------------------------------
+
+function test() {
+ // Performing async calls, e.g. 'onload', we have to wait till all of them finished
+ waitForExplicitFinish();
+
+ // Store original preferences so we can restore settings after testing
+ origBlockActive = Services.prefs.getBoolPref(PREF_ACTIVE);
+ origBlockDisplay = Services.prefs.getBoolPref(PREF_DISPLAY);
+ origUpgradeDisplay = Services.prefs.getBoolPref(PREF_DISPLAY_UPGRADE);
+ origInsecurePref = Services.prefs.getBoolPref(PREF_INSECURE_ICON);
+ Services.prefs.setBoolPref(PREF_ACTIVE, true);
+ Services.prefs.setBoolPref(PREF_DISPLAY, true);
+ Services.prefs.setBoolPref(PREF_DISPLAY_UPGRADE, false);
+
+ var newTab = BrowserTestUtils.addTab(gBrowser);
+ gBrowser.selectedTab = newTab;
+ gTestBrowser = gBrowser.selectedBrowser;
+ newTab.linkedBrowser.stop();
+
+ executeSoon(testInsecure1);
+}
diff --git a/browser/base/content/test/siteIdentity/browser_mixedContentFramesOnHttp.js b/browser/base/content/test/siteIdentity/browser_mixedContentFramesOnHttp.js
new file mode 100644
index 0000000000..c6096342cc
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_mixedContentFramesOnHttp.js
@@ -0,0 +1,37 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * Test for Bug 1182551 -
+ *
+ * This test has a top level HTTP page with an HTTPS iframe. The HTTPS iframe
+ * includes an HTTP image. We check that the top level security state is
+ * STATE_IS_INSECURE. The mixed content from the iframe shouldn't "upgrade"
+ * the HTTP top level page to broken HTTPS.
+ */
+
+const TEST_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+ ) + "file_mixedContentFramesOnHttp.html";
+
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["security.mixed_content.block_active_content", true],
+ ["security.mixed_content.block_display_content", false],
+ ["security.mixed_content.upgrade_display_content", false],
+ ],
+ });
+
+ await BrowserTestUtils.withNewTab(TEST_URL, async function (browser) {
+ isSecurityState(browser, "insecure");
+ await assertMixedContentBlockingState(browser, {
+ activeLoaded: false,
+ activeBlocked: false,
+ passiveLoaded: true,
+ });
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/browser_mixedContentFromOnunload.js b/browser/base/content/test/siteIdentity/browser_mixedContentFromOnunload.js
new file mode 100644
index 0000000000..c9e11e54a7
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_mixedContentFromOnunload.js
@@ -0,0 +1,68 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * Tests for Bug 947079 - Fix bug in nsSecureBrowserUIImpl that sets the wrong
+ * security state on a page because of a subresource load that is not on the
+ * same page.
+ */
+
+// We use different domains for each test and for navigation within each test
+const HTTP_TEST_ROOT_1 = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+);
+const HTTPS_TEST_ROOT_1 = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://test1.example.com"
+);
+const HTTP_TEST_ROOT_2 = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.net"
+);
+const HTTPS_TEST_ROOT_2 = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://test2.example.com"
+);
+
+add_task(async function () {
+ let url = HTTP_TEST_ROOT_1 + "file_mixedContentFromOnunload.html";
+ await BrowserTestUtils.withNewTab(url, async function (browser) {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["security.mixed_content.block_active_content", true],
+ ["security.mixed_content.block_display_content", false],
+ ["security.mixed_content.upgrade_display_content", false],
+ ],
+ });
+ // Navigation from an http page to a https page with no mixed content
+ // The http page loads an http image on unload
+ url = HTTPS_TEST_ROOT_1 + "file_mixedContentFromOnunload_test1.html";
+ BrowserTestUtils.loadURIString(browser, url);
+ await BrowserTestUtils.browserLoaded(browser);
+ // check security state. Since current url is https and doesn't have any
+ // mixed content resources, we expect it to be secure.
+ isSecurityState(browser, "secure");
+ await assertMixedContentBlockingState(browser, {
+ activeLoaded: false,
+ activeBlocked: false,
+ passiveLoaded: false,
+ });
+ // Navigation from an http page to a https page that has mixed display content
+ // The https page loads an http image on unload
+ url = HTTP_TEST_ROOT_2 + "file_mixedContentFromOnunload.html";
+ BrowserTestUtils.loadURIString(browser, url);
+ await BrowserTestUtils.browserLoaded(browser);
+ url = HTTPS_TEST_ROOT_2 + "file_mixedContentFromOnunload_test2.html";
+ BrowserTestUtils.loadURIString(browser, url);
+ await BrowserTestUtils.browserLoaded(browser);
+ isSecurityState(browser, "broken");
+ await assertMixedContentBlockingState(browser, {
+ activeLoaded: false,
+ activeBlocked: false,
+ passiveLoaded: true,
+ });
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/browser_mixed_content_cert_override.js b/browser/base/content/test/siteIdentity/browser_mixed_content_cert_override.js
new file mode 100644
index 0000000000..6ca9655406
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_mixed_content_cert_override.js
@@ -0,0 +1,69 @@
+/*
+ * Bug 1253771 - check mixed content blocking in combination with overriden certificates
+ */
+
+"use strict";
+
+const MIXED_CONTENT_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://self-signed.example.com"
+ ) + "test-mixedcontent-securityerrors.html";
+
+function getConnectionState() {
+ return document.getElementById("identity-popup").getAttribute("connection");
+}
+
+function getPopupContentVerifier() {
+ return document.getElementById("identity-popup-content-verifier");
+}
+
+function getIdentityIcon() {
+ return window.getComputedStyle(document.getElementById("identity-icon"))
+ .listStyleImage;
+}
+
+function checkIdentityPopup(icon) {
+ gIdentityHandler.refreshIdentityPopup();
+ is(getIdentityIcon(), `url("chrome://global/skin/icons/${icon}")`);
+ is(getConnectionState(), "secure-cert-user-overridden");
+ isnot(
+ getPopupContentVerifier().style.display,
+ "none",
+ "Overridden certificate warning is shown"
+ );
+ ok(
+ getPopupContentVerifier().textContent.includes("security exception"),
+ "Text shows overridden certificate warning."
+ );
+}
+
+add_task(async function () {
+ await BrowserTestUtils.openNewForegroundTab(gBrowser);
+
+ // check that a warning is shown when loading a page with mixed content and an overridden certificate
+ await loadBadCertPage(MIXED_CONTENT_URL);
+ checkIdentityPopup("security-warning.svg");
+
+ // check that the crossed out icon is shown when disabling mixed content protection
+ gIdentityHandler.disableMixedContentProtection();
+ await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+
+ checkIdentityPopup("security-broken.svg");
+
+ // check that a warning is shown even without mixed content
+ BrowserTestUtils.loadURIString(
+ gBrowser.selectedBrowser,
+ "https://self-signed.example.com"
+ );
+ await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ checkIdentityPopup("security-warning.svg");
+
+ // remove cert exception
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ certOverrideService.clearValidityOverride("self-signed.example.com", -1, {});
+
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
diff --git a/browser/base/content/test/siteIdentity/browser_mixed_content_with_navigation.js b/browser/base/content/test/siteIdentity/browser_mixed_content_with_navigation.js
new file mode 100644
index 0000000000..48171ee876
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_mixed_content_with_navigation.js
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the site identity indicator is properly updated when loading from
+// the BF cache. This is achieved by loading a page, navigating to another page,
+// and then going "back" to the first page, as well as the reverse (loading to
+// the other page, navigating to the page we're interested in, going back, and
+// then going forward again).
+
+const kBaseURI = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+const kSecureURI = kBaseURI + "dummy_page.html";
+
+const kTestcases = [
+ {
+ uri: kBaseURI + "file_mixedPassiveContent.html",
+ expectErrorPage: false,
+ expectedIdentityMode: "mixedDisplayContent",
+ },
+ {
+ uri: kBaseURI + "file_bug1045809_1.html",
+ expectErrorPage: false,
+ expectedIdentityMode: "mixedActiveBlocked",
+ },
+ {
+ uri: "https://expired.example.com",
+ expectErrorPage: true,
+ expectedIdentityMode: "certErrorPage",
+ },
+];
+
+add_task(async function () {
+ for (let testcase of kTestcases) {
+ await run_testcase(testcase);
+ }
+});
+
+async function run_testcase(testcase) {
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.mixed_content.upgrade_display_content", false]],
+ });
+ // Test the forward and back case.
+ // Start by loading an unrelated URI so that this generalizes well when the
+ // testcase would otherwise first navigate to an error page, which doesn't
+ // seem to work with withNewTab.
+ await BrowserTestUtils.withNewTab("about:blank", async browser => {
+ // Navigate to the test URI.
+ BrowserTestUtils.loadURIString(browser, testcase.uri);
+ if (!testcase.expectErrorPage) {
+ await BrowserTestUtils.browserLoaded(browser, false, testcase.uri);
+ } else {
+ await BrowserTestUtils.waitForErrorPage(browser);
+ }
+ let identityMode = window.document.getElementById("identity-box").classList;
+ ok(
+ identityMode.contains(testcase.expectedIdentityMode),
+ `identity should be ${testcase.expectedIdentityMode}`
+ );
+
+ // Navigate to a URI that should be secure.
+ BrowserTestUtils.loadURIString(browser, kSecureURI);
+ await BrowserTestUtils.browserLoaded(browser, false, kSecureURI);
+ let secureIdentityMode =
+ window.document.getElementById("identity-box").className;
+ is(secureIdentityMode, "verifiedDomain", "identity should be secure now");
+
+ // Go back to the test page.
+ browser.webNavigation.goBack();
+ if (!testcase.expectErrorPage) {
+ await BrowserTestUtils.browserStopped(browser, testcase.uri);
+ } else {
+ await BrowserTestUtils.waitForErrorPage(browser);
+ }
+ let identityModeAgain =
+ window.document.getElementById("identity-box").classList;
+ ok(
+ identityModeAgain.contains(testcase.expectedIdentityMode),
+ `identity should again be ${testcase.expectedIdentityMode}`
+ );
+ });
+
+ // Test the back and forward case.
+ // Start on a secure page.
+ await BrowserTestUtils.withNewTab(kSecureURI, async browser => {
+ let secureIdentityMode =
+ window.document.getElementById("identity-box").className;
+ is(secureIdentityMode, "verifiedDomain", "identity should start as secure");
+
+ // Navigate to the test URI.
+ BrowserTestUtils.loadURIString(browser, testcase.uri);
+ if (!testcase.expectErrorPage) {
+ await BrowserTestUtils.browserLoaded(browser, false, testcase.uri);
+ } else {
+ await BrowserTestUtils.waitForErrorPage(browser);
+ }
+ let identityMode = window.document.getElementById("identity-box").classList;
+ ok(
+ identityMode.contains(testcase.expectedIdentityMode),
+ `identity should be ${testcase.expectedIdentityMode}`
+ );
+
+ // Go back to the secure page.
+ browser.webNavigation.goBack();
+ await BrowserTestUtils.browserStopped(browser, kSecureURI);
+ let secureIdentityModeAgain =
+ window.document.getElementById("identity-box").className;
+ is(
+ secureIdentityModeAgain,
+ "verifiedDomain",
+ "identity should be secure again"
+ );
+
+ // Go forward again to the test URI.
+ browser.webNavigation.goForward();
+ if (!testcase.expectErrorPage) {
+ await BrowserTestUtils.browserStopped(browser, testcase.uri);
+ } else {
+ await BrowserTestUtils.waitForErrorPage(browser);
+ }
+ let identityModeAgain =
+ window.document.getElementById("identity-box").classList;
+ ok(
+ identityModeAgain.contains(testcase.expectedIdentityMode),
+ `identity should again be ${testcase.expectedIdentityMode}`
+ );
+ });
+}
diff --git a/browser/base/content/test/siteIdentity/browser_mixed_passive_content_indicator.js b/browser/base/content/test/siteIdentity/browser_mixed_passive_content_indicator.js
new file mode 100644
index 0000000000..909764c597
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_mixed_passive_content_indicator.js
@@ -0,0 +1,18 @@
+const TEST_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "simple_mixed_passive.html";
+
+add_task(async function test_mixed_passive_content_indicator() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.mixed_content.upgrade_display_content", false]],
+ });
+ await BrowserTestUtils.withNewTab(TEST_URL, function () {
+ is(
+ document.getElementById("identity-box").className,
+ "unknownIdentity mixedDisplayContent",
+ "identity box has class name for mixed content"
+ );
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/browser_mixedcontent_securityflags.js b/browser/base/content/test/siteIdentity/browser_mixedcontent_securityflags.js
new file mode 100644
index 0000000000..3e39426c51
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_mixedcontent_securityflags.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// The test loads a web page with mixed active and mixed display content and
+// makes sure that the mixed content flags on the docshell are set correctly.
+// * Using default about:config prefs (mixed active blocked, mixed display
+// loaded) we load the page and check the flags.
+// * We change the about:config prefs (mixed active blocked, mixed display
+// blocked), reload the page, and check the flags again.
+// * We override protection so all mixed content can load and check the
+// flags again.
+
+const TEST_URI =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "test-mixedcontent-securityerrors.html";
+const PREF_DISPLAY = "security.mixed_content.block_display_content";
+const PREF_DISPLAY_UPGRADE = "security.mixed_content.upgrade_display_content";
+const PREF_ACTIVE = "security.mixed_content.block_active_content";
+var gTestBrowser = null;
+
+registerCleanupFunction(function () {
+ // Set preferences back to their original values
+ Services.prefs.clearUserPref(PREF_DISPLAY);
+ Services.prefs.clearUserPref(PREF_DISPLAY_UPGRADE);
+ Services.prefs.clearUserPref(PREF_ACTIVE);
+ gBrowser.removeCurrentTab();
+});
+
+add_task(async function blockMixedActiveContentTest() {
+ // Turn on mixed active blocking and mixed display loading and load the page.
+ Services.prefs.setBoolPref(PREF_DISPLAY, false);
+ Services.prefs.setBoolPref(PREF_DISPLAY_UPGRADE, false);
+ Services.prefs.setBoolPref(PREF_ACTIVE, true);
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URI);
+ gTestBrowser = gBrowser.getBrowserForTab(tab);
+
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: true,
+ });
+
+ // Turn on mixed active and mixed display blocking and reload the page.
+ Services.prefs.setBoolPref(PREF_DISPLAY, true);
+ Services.prefs.setBoolPref(PREF_ACTIVE, true);
+
+ gBrowser.reload();
+ await BrowserTestUtils.browserLoaded(gTestBrowser);
+
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: false,
+ activeBlocked: true,
+ passiveLoaded: false,
+ });
+});
+
+add_task(async function overrideMCB() {
+ // Disable mixed content blocking (reloads page) and retest
+ let { gIdentityHandler } = gTestBrowser.ownerGlobal;
+ gIdentityHandler.disableMixedContentProtection();
+ await BrowserTestUtils.browserLoaded(gTestBrowser);
+
+ await assertMixedContentBlockingState(gTestBrowser, {
+ activeLoaded: true,
+ activeBlocked: false,
+ passiveLoaded: true,
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/browser_navigation_failures.js b/browser/base/content/test/siteIdentity/browser_navigation_failures.js
new file mode 100644
index 0000000000..ddb0d93fab
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_navigation_failures.js
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the site identity indicator is properly updated for navigations
+// that fail for various reasons. In particular, we currently test TLS handshake
+// failures, about: pages that don't actually exist, and situations where the
+// TLS handshake completes but the server then closes the connection.
+// See bug 1492424, bug 1493427, and bug 1391207.
+
+const kSecureURI =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "dummy_page.html";
+add_task(async function () {
+ await BrowserTestUtils.withNewTab(kSecureURI, async browser => {
+ let identityMode = window.document.getElementById("identity-box").className;
+ is(identityMode, "verifiedDomain", "identity should be secure before");
+
+ const TLS_HANDSHAKE_FAILURE_URI = "https://ssl3.example.com/";
+ // Try to connect to a server where the TLS handshake will fail.
+ BrowserTestUtils.loadURIString(browser, TLS_HANDSHAKE_FAILURE_URI);
+ await BrowserTestUtils.browserLoaded(
+ browser,
+ false,
+ TLS_HANDSHAKE_FAILURE_URI,
+ true
+ );
+
+ let newIdentityMode =
+ window.document.getElementById("identity-box").className;
+ is(
+ newIdentityMode,
+ "certErrorPage notSecureText",
+ "identity should be unknown (not secure) after"
+ );
+ });
+});
+
+add_task(async function () {
+ await BrowserTestUtils.withNewTab(kSecureURI, async browser => {
+ let identityMode = window.document.getElementById("identity-box").className;
+ is(identityMode, "verifiedDomain", "identity should be secure before");
+
+ const BAD_ABOUT_PAGE_URI = "about:somethingthatdoesnotexist";
+ // Try to load an about: page that doesn't exist
+ BrowserTestUtils.loadURIString(browser, BAD_ABOUT_PAGE_URI);
+ await BrowserTestUtils.browserLoaded(
+ browser,
+ false,
+ BAD_ABOUT_PAGE_URI,
+ true
+ );
+
+ let newIdentityMode =
+ window.document.getElementById("identity-box").className;
+ is(
+ newIdentityMode,
+ "unknownIdentity",
+ "identity should be unknown (not secure) after"
+ );
+ });
+});
+
+// Helper function to start a TLS server that will accept a connection, complete
+// the TLS handshake, but then close the connection.
+function startServer(cert) {
+ let tlsServer = Cc["@mozilla.org/network/tls-server-socket;1"].createInstance(
+ Ci.nsITLSServerSocket
+ );
+ tlsServer.init(-1, true, -1);
+ tlsServer.serverCert = cert;
+
+ let input, output;
+
+ let listener = {
+ onSocketAccepted(socket, transport) {
+ let connectionInfo = transport.securityCallbacks.getInterface(
+ Ci.nsITLSServerConnectionInfo
+ );
+ connectionInfo.setSecurityObserver(listener);
+ input = transport.openInputStream(0, 0, 0);
+ output = transport.openOutputStream(0, 0, 0);
+ },
+
+ onHandshakeDone(socket, status) {
+ input.asyncWait(
+ {
+ onInputStreamReady(readyInput) {
+ try {
+ input.close();
+ output.close();
+ } catch (e) {
+ info(e);
+ }
+ },
+ },
+ 0,
+ 0,
+ Services.tm.currentThread
+ );
+ },
+
+ onStopListening() {},
+ };
+
+ tlsServer.setSessionTickets(false);
+ tlsServer.asyncListen(listener);
+
+ return tlsServer;
+}
+
+// Test that if we complete a TLS handshake but the server closes the connection
+// just after doing so (resulting in a "connection reset" error page), the site
+// identity information gets updated appropriately (it should indicate "not
+// secure").
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ // This test fails on some platforms if we leave IPv6 enabled.
+ set: [["network.dns.disableIPv6", true]],
+ });
+
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+
+ let cert = getTestServerCertificate();
+ // Start a server and trust its certificate.
+ let server = startServer(cert);
+ certOverrideService.rememberValidityOverride(
+ "localhost",
+ server.port,
+ {},
+ cert,
+ true
+ );
+
+ // Un-do configuration changes we've made when the test is done.
+ registerCleanupFunction(() => {
+ certOverrideService.clearValidityOverride("localhost", server.port, {});
+ server.close();
+ });
+
+ // Open up a new tab...
+ await BrowserTestUtils.withNewTab("about:blank", async browser => {
+ const TLS_HANDSHAKE_FAILURE_URI = `https://localhost:${server.port}/`;
+ // Try to connect to a server where the TLS handshake will succeed, but then
+ // the server closes the connection right after.
+ BrowserTestUtils.loadURIString(browser, TLS_HANDSHAKE_FAILURE_URI);
+ await BrowserTestUtils.browserLoaded(
+ browser,
+ false,
+ TLS_HANDSHAKE_FAILURE_URI,
+ true
+ );
+
+ let identityMode = window.document.getElementById("identity-box").className;
+ is(
+ identityMode,
+ "certErrorPage notSecureText",
+ "identity should be 'unknown'"
+ );
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/browser_no_mcb_for_loopback.js b/browser/base/content/test/siteIdentity/browser_no_mcb_for_loopback.js
new file mode 100644
index 0000000000..1c854e2849
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_no_mcb_for_loopback.js
@@ -0,0 +1,88 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// The test loads a HTTPS web page with active content from HTTP loopback URLs
+// and makes sure that the mixed content flags on the docshell are not set.
+//
+// Note that the URLs referenced within the test page intentionally use the
+// unassigned port 8 because we don't want to actually load anything, we just
+// want to check that the URLs are not blocked.
+
+// The following rejections should not be left uncaught. This test has been
+// whitelisted until the issue is fixed.
+if (!gMultiProcessBrowser) {
+ const { PromiseTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PromiseTestUtils.sys.mjs"
+ );
+ PromiseTestUtils.expectUncaughtRejection(/NetworkError/);
+ PromiseTestUtils.expectUncaughtRejection(/NetworkError/);
+}
+
+const TEST_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "test_no_mcb_for_loopback.html";
+
+const LOOPBACK_PNG_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "http://127.0.0.1:8888"
+ ) + "moz.png";
+
+const PREF_BLOCK_DISPLAY = "security.mixed_content.block_display_content";
+const PREF_UPGRADE_DISPLAY = "security.mixed_content.upgrade_display_content";
+const PREF_BLOCK_ACTIVE = "security.mixed_content.block_active_content";
+
+function clearAllImageCaches() {
+ let tools = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools);
+ let imageCache = tools.getImgCacheForDocument(window.document);
+ imageCache.clearCache(true); // true=chrome
+ imageCache.clearCache(false); // false=content
+}
+
+registerCleanupFunction(function () {
+ clearAllImageCaches();
+ Services.prefs.clearUserPref(PREF_BLOCK_DISPLAY);
+ Services.prefs.clearUserPref(PREF_UPGRADE_DISPLAY);
+ Services.prefs.clearUserPref(PREF_BLOCK_ACTIVE);
+ gBrowser.removeCurrentTab();
+});
+
+add_task(async function allowLoopbackMixedContent() {
+ Services.prefs.setBoolPref(PREF_BLOCK_DISPLAY, true);
+ Services.prefs.setBoolPref(PREF_UPGRADE_DISPLAY, false);
+ Services.prefs.setBoolPref(PREF_BLOCK_ACTIVE, true);
+
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+ const browser = gBrowser.getBrowserForTab(tab);
+
+ // Check that loopback content served from the cache is not blocked.
+ await SpecialPowers.spawn(
+ browser,
+ [LOOPBACK_PNG_URL],
+ async function (loopbackPNGUrl) {
+ const doc = content.document;
+ const img = doc.createElement("img");
+ const promiseImgLoaded = ContentTaskUtils.waitForEvent(
+ img,
+ "load",
+ false
+ );
+ img.src = loopbackPNGUrl;
+ Assert.ok(!img.complete, "loopback image not yet loaded");
+ doc.body.appendChild(img);
+ await promiseImgLoaded;
+
+ const cachedImg = doc.createElement("img");
+ cachedImg.src = img.src;
+ Assert.ok(cachedImg.complete, "loopback image loaded from cache");
+ }
+ );
+
+ await assertMixedContentBlockingState(browser, {
+ activeBlocked: false,
+ activeLoaded: false,
+ passiveLoaded: false,
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/browser_no_mcb_for_onions.js b/browser/base/content/test/siteIdentity/browser_no_mcb_for_onions.js
new file mode 100644
index 0000000000..4a5c65b125
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_no_mcb_for_onions.js
@@ -0,0 +1,41 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// The test loads a HTTPS web page with active content from HTTP .onion URLs
+// and makes sure that the mixed content flags on the docshell are not set.
+//
+// Note that the URLs referenced within the test page intentionally use the
+// unassigned port 8 because we don't want to actually load anything, we just
+// want to check that the URLs are not blocked.
+
+const TEST_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "test_no_mcb_for_onions.html";
+
+const PREF_BLOCK_DISPLAY = "security.mixed_content.block_display_content";
+const PREF_BLOCK_ACTIVE = "security.mixed_content.block_active_content";
+const PREF_ONION_ALLOWLIST = "dom.securecontext.allowlist_onions";
+
+add_task(async function allowOnionMixedContent() {
+ registerCleanupFunction(function () {
+ gBrowser.removeCurrentTab();
+ });
+
+ await SpecialPowers.pushPrefEnv({ set: [[PREF_BLOCK_DISPLAY, true]] });
+ await SpecialPowers.pushPrefEnv({ set: [[PREF_BLOCK_ACTIVE, true]] });
+ await SpecialPowers.pushPrefEnv({ set: [[PREF_ONION_ALLOWLIST, true]] });
+
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_URL
+ ).catch(console.error);
+ const browser = gBrowser.getBrowserForTab(tab);
+
+ await assertMixedContentBlockingState(browser, {
+ activeBlocked: false,
+ activeLoaded: false,
+ passiveLoaded: false,
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/browser_no_mcb_on_http_site.js b/browser/base/content/test/siteIdentity/browser_no_mcb_on_http_site.js
new file mode 100644
index 0000000000..30caae4ea5
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_no_mcb_on_http_site.js
@@ -0,0 +1,133 @@
+/*
+ * Description of the Tests for
+ * - Bug 909920 - Mixed content warning should not show on a HTTP site
+ *
+ * Description of the tests:
+ * Test 1:
+ * 1) Load an http page
+ * 2) The page includes a css file using https
+ * 3) The css file loads an |IMAGE| << over http
+ *
+ * Test 2:
+ * 1) Load an http page
+ * 2) The page includes a css file using https
+ * 3) The css file loads a |FONT| over http
+ *
+ * Test 3:
+ * 1) Load an http page
+ * 2) The page includes a css file using https
+ * 3) The css file imports (@import) another css file using http
+ * 3) The imported css file loads a |FONT| over http
+ *
+ * Since the top-domain is >> NOT << served using https, the MCB
+ * should >> NOT << trigger a warning.
+ */
+
+const PREF_ACTIVE = "security.mixed_content.block_active_content";
+const PREF_DISPLAY = "security.mixed_content.block_display_content";
+
+const HTTP_TEST_ROOT = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+);
+
+var gTestBrowser = null;
+
+function cleanUpAfterTests() {
+ gBrowser.removeCurrentTab();
+ window.focus();
+}
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [PREF_ACTIVE, true],
+ [PREF_DISPLAY, true],
+ ],
+ });
+ let url = HTTP_TEST_ROOT + "test_no_mcb_on_http_site_img.html";
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+ gTestBrowser = tab.linkedBrowser;
+});
+
+// ------------- TEST 1 -----------------------------------------
+
+add_task(async function test1() {
+ let expected =
+ "Verifying MCB does not trigger warning/error for an http page ";
+ expected += "with https css that includes http image";
+
+ await SpecialPowers.spawn(
+ gTestBrowser,
+ [expected],
+ async function (condition) {
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.getElementById("testDiv").innerHTML == condition,
+ "Waited too long for status in Test 1!"
+ );
+ }
+ );
+
+ // Explicit OKs needed because the harness requires at least one call to ok.
+ ok(true, "test 1 passed");
+
+ // set up test 2
+ let url = HTTP_TEST_ROOT + "test_no_mcb_on_http_site_font.html";
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+ await BrowserTestUtils.browserLoaded(gTestBrowser);
+});
+
+// ------------- TEST 2 -----------------------------------------
+
+add_task(async function test2() {
+ let expected =
+ "Verifying MCB does not trigger warning/error for an http page ";
+ expected += "with https css that includes http font";
+
+ await SpecialPowers.spawn(
+ gTestBrowser,
+ [expected],
+ async function (condition) {
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.getElementById("testDiv").innerHTML == condition,
+ "Waited too long for status in Test 2!"
+ );
+ }
+ );
+
+ ok(true, "test 2 passed");
+
+ // set up test 3
+ let url = HTTP_TEST_ROOT + "test_no_mcb_on_http_site_font2.html";
+ BrowserTestUtils.loadURIString(gTestBrowser, url);
+ await BrowserTestUtils.browserLoaded(gTestBrowser);
+});
+
+// ------------- TEST 3 -----------------------------------------
+
+add_task(async function test3() {
+ let expected =
+ "Verifying MCB does not trigger warning/error for an http page ";
+ expected +=
+ "with https css that imports another http css which includes http font";
+
+ await SpecialPowers.spawn(
+ gTestBrowser,
+ [expected],
+ async function (condition) {
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.getElementById("testDiv").innerHTML == condition,
+ "Waited too long for status in Test 3!"
+ );
+ }
+ );
+
+ ok(true, "test3 passed");
+});
+
+// ------------------------------------------------------
+
+add_task(async function cleanup() {
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
diff --git a/browser/base/content/test/siteIdentity/browser_secure_transport_insecure_scheme.js b/browser/base/content/test/siteIdentity/browser_secure_transport_insecure_scheme.js
new file mode 100644
index 0000000000..1d282ef6de
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_secure_transport_insecure_scheme.js
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that an insecure resource routed over a secure transport is considered
+// insecure in terms of the site identity panel. We achieve this by running an
+// HTTP-over-TLS "proxy" and having Firefox request an http:// URI over it.
+
+/**
+ * Tests that the page info dialog "security" section labels a
+ * connection as unencrypted and does not show certificate.
+ * @param {string} uri - URI of the page to test with.
+ */
+async function testPageInfoNotEncrypted(uri) {
+ let pageInfo = BrowserPageInfo(uri, "securityTab");
+ await BrowserTestUtils.waitForEvent(pageInfo, "load");
+ let pageInfoDoc = pageInfo.document;
+ let securityTab = pageInfoDoc.getElementById("securityTab");
+ await TestUtils.waitForCondition(
+ () => BrowserTestUtils.is_visible(securityTab),
+ "Security tab should be visible."
+ );
+
+ let secLabel = pageInfoDoc.getElementById("security-technical-shortform");
+ await TestUtils.waitForCondition(
+ () => secLabel.value == "Connection Not Encrypted",
+ "pageInfo 'Security Details' should show not encrypted"
+ );
+
+ let viewCertBtn = pageInfoDoc.getElementById("security-view-cert");
+ ok(
+ viewCertBtn.collapsed,
+ "pageInfo 'View Cert' button should not be visible"
+ );
+ pageInfo.close();
+}
+
+// But first, a quick test that we don't incorrectly treat a
+// blob:https://example.com URI as secure.
+add_task(async function () {
+ let uri =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "dummy_page.html";
+ await BrowserTestUtils.withNewTab(uri, async browser => {
+ await SpecialPowers.spawn(browser, [], async () => {
+ let debug = { hello: "world" };
+ let blob = new Blob([JSON.stringify(debug, null, 2)], {
+ type: "application/json",
+ });
+ let blobUri = URL.createObjectURL(blob);
+ content.document.location = blobUri;
+ });
+ await BrowserTestUtils.browserLoaded(browser);
+ let identityMode = window.document.getElementById("identity-box").className;
+ is(identityMode, "localResource", "identity should be 'localResource'");
+ await testPageInfoNotEncrypted(uri);
+ });
+});
+
+// This server pretends to be a HTTP over TLS proxy. It isn't really, but this
+// is sufficient for the purposes of this test.
+function startServer(cert) {
+ let tlsServer = Cc["@mozilla.org/network/tls-server-socket;1"].createInstance(
+ Ci.nsITLSServerSocket
+ );
+ tlsServer.init(-1, true, -1);
+ tlsServer.serverCert = cert;
+
+ let input, output;
+
+ let listener = {
+ onSocketAccepted(socket, transport) {
+ let connectionInfo = transport.securityCallbacks.getInterface(
+ Ci.nsITLSServerConnectionInfo
+ );
+ connectionInfo.setSecurityObserver(listener);
+ input = transport.openInputStream(0, 0, 0);
+ output = transport.openOutputStream(0, 0, 0);
+ },
+
+ onHandshakeDone(socket, status) {
+ input.asyncWait(
+ {
+ onInputStreamReady(readyInput) {
+ try {
+ let request = NetUtil.readInputStreamToString(
+ readyInput,
+ readyInput.available()
+ );
+ ok(
+ request.startsWith("GET ") && request.includes("HTTP/1.1"),
+ "expecting an HTTP/1.1 GET request"
+ );
+ let response =
+ "HTTP/1.1 200 OK\r\nContent-Type:text/plain\r\n" +
+ "Connection:Close\r\nContent-Length:2\r\n\r\nOK";
+ output.write(response, response.length);
+ } catch (e) {
+ info(e);
+ }
+ },
+ },
+ 0,
+ 0,
+ Services.tm.currentThread
+ );
+ },
+
+ onStopListening() {
+ input.close();
+ output.close();
+ },
+ };
+
+ tlsServer.setSessionTickets(false);
+ tlsServer.asyncListen(listener);
+
+ return tlsServer;
+}
+
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ // This test fails on some platforms if we leave IPv6 enabled.
+ set: [["network.dns.disableIPv6", true]],
+ });
+
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+
+ let cert = getTestServerCertificate();
+ // Start the proxy and configure Firefox to trust its certificate.
+ let server = startServer(cert);
+ certOverrideService.rememberValidityOverride(
+ "localhost",
+ server.port,
+ {},
+ cert,
+ true
+ );
+ // Configure Firefox to use the proxy.
+ let systemProxySettings = {
+ QueryInterface: ChromeUtils.generateQI(["nsISystemProxySettings"]),
+ mainThreadOnly: true,
+ PACURI: null,
+ getProxyForURI: (aSpec, aScheme, aHost, aPort) => {
+ return `HTTPS localhost:${server.port}`;
+ },
+ };
+ let oldProxyType = Services.prefs.getIntPref("network.proxy.type");
+ Services.prefs.setIntPref(
+ "network.proxy.type",
+ Ci.nsIProtocolProxyService.PROXYCONFIG_SYSTEM
+ );
+ let { MockRegistrar } = ChromeUtils.importESModule(
+ "resource://testing-common/MockRegistrar.sys.mjs"
+ );
+ let mockProxy = MockRegistrar.register(
+ "@mozilla.org/system-proxy-settings;1",
+ systemProxySettings
+ );
+ // Register cleanup to undo the configuration changes we've made.
+ registerCleanupFunction(() => {
+ certOverrideService.clearValidityOverride("localhost", server.port, {});
+ Services.prefs.setIntPref("network.proxy.type", oldProxyType);
+ MockRegistrar.unregister(mockProxy);
+ server.close();
+ });
+
+ // Navigate to 'http://example.com'. Our proxy settings will route this via
+ // the "proxy" we just started. Even though our connection to the proxy is
+ // secure, in a real situation the connection from the proxy to
+ // http://example.com won't be secure, so we treat it as not secure.
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ await BrowserTestUtils.withNewTab("http://example.com/", async browser => {
+ let identityMode = window.document.getElementById("identity-box").className;
+ is(identityMode, "notSecure", "identity should be 'not secure'");
+
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ await testPageInfoNotEncrypted("http://example.com");
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/browser_session_store_pageproxystate.js b/browser/base/content/test/siteIdentity/browser_session_store_pageproxystate.js
new file mode 100644
index 0000000000..5d8c011727
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_session_store_pageproxystate.js
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const triggeringPrincipal_base64 = E10SUtils.SERIALIZED_SYSTEMPRINCIPAL;
+
+let origBrowserState = SessionStore.getBrowserState();
+
+add_setup(async function () {
+ registerCleanupFunction(() => {
+ SessionStore.setBrowserState(origBrowserState);
+ });
+});
+
+// Test that when restoring tabs via SessionStore, we directly show the correct
+// security state.
+add_task(async function test_session_store_security_state() {
+ const state = {
+ windows: [
+ {
+ tabs: [
+ {
+ entries: [
+ { url: "https://example.net", triggeringPrincipal_base64 },
+ ],
+ },
+ {
+ entries: [
+ { url: "https://example.org", triggeringPrincipal_base64 },
+ ],
+ },
+ ],
+ selected: 1,
+ },
+ ],
+ };
+
+ // Create a promise that resolves when the tabs have finished restoring.
+ let promiseTabsRestored = Promise.all([
+ TestUtils.topicObserved("sessionstore-browser-state-restored"),
+ BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "SSTabRestored"),
+ ]);
+
+ SessionStore.setBrowserState(JSON.stringify(state));
+
+ await promiseTabsRestored;
+
+ is(gBrowser.selectedTab, gBrowser.tabs[0], "First tab is selected initially");
+
+ info("Switch to second tab which has not been loaded yet.");
+ BrowserTestUtils.switchTab(gBrowser, gBrowser.tabs[1]);
+ is(
+ gURLBar.textbox.getAttribute("pageproxystate"),
+ "invalid",
+ "Page proxy state is invalid after tab switch"
+ );
+
+ // Wait for valid pageproxystate. As soon as we have a valid pageproxystate,
+ // showing the identity box, it should indicate a secure connection.
+ await BrowserTestUtils.waitForMutationCondition(
+ gURLBar.textbox,
+ {
+ attributeFilter: ["pageproxystate"],
+ },
+ () => gURLBar.textbox.getAttribute("pageproxystate") == "valid"
+ );
+
+ // Wait for a tick for security state to apply.
+ await new Promise(resolve => setTimeout(resolve, 0));
+
+ is(
+ gBrowser.currentURI.spec,
+ "https://example.org/",
+ "Should have loaded example.org"
+ );
+ is(
+ gIdentityHandler._identityBox.getAttribute("pageproxystate"),
+ "valid",
+ "identityBox pageproxystate is valid"
+ );
+
+ ok(
+ gIdentityHandler._isSecureConnection,
+ "gIdentityHandler._isSecureConnection is true"
+ );
+ is(
+ gIdentityHandler._identityBox.className,
+ "verifiedDomain",
+ "identityBox class signals secure connection."
+ );
+});
diff --git a/browser/base/content/test/siteIdentity/browser_tab_sharing_state.js b/browser/base/content/test/siteIdentity/browser_tab_sharing_state.js
new file mode 100644
index 0000000000..6c6ba57c55
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/browser_tab_sharing_state.js
@@ -0,0 +1,96 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests gBrowser#updateBrowserSharing
+ */
+add_task(async function testBrowserSharingStateSetter() {
+ const WEBRTC_TEST_STATE = {
+ camera: 0,
+ microphone: 1,
+ paused: false,
+ sharing: "microphone",
+ showMicrophoneIndicator: true,
+ showScreenSharingIndicator: "",
+ windowId: 0,
+ };
+
+ const WEBRTC_TEST_STATE2 = {
+ camera: 1,
+ microphone: 1,
+ paused: false,
+ sharing: "camera",
+ showCameraIndicator: true,
+ showMicrophoneIndicator: true,
+ showScreenSharingIndicator: "",
+ windowId: 1,
+ };
+
+ await BrowserTestUtils.withNewTab("https://example.com", async browser => {
+ let tab = gBrowser.selectedTab;
+ is(tab._sharingState, undefined, "No sharing state initially.");
+ ok(!tab.hasAttribute("sharing"), "No tab sharing attribute initially.");
+
+ // Set an active sharing state for webrtc
+ gBrowser.updateBrowserSharing(browser, { webRTC: WEBRTC_TEST_STATE });
+ Assert.deepEqual(
+ tab._sharingState,
+ { webRTC: WEBRTC_TEST_STATE },
+ "Should have correct webRTC sharing state."
+ );
+ is(
+ tab.getAttribute("sharing"),
+ WEBRTC_TEST_STATE.sharing,
+ "Tab sharing attribute reflects webRTC sharing state."
+ );
+
+ // Set sharing state for geolocation
+ gBrowser.updateBrowserSharing(browser, { geo: true });
+ Assert.deepEqual(
+ tab._sharingState,
+ {
+ webRTC: WEBRTC_TEST_STATE,
+ geo: true,
+ },
+ "Should have sharing state for both webRTC and geolocation."
+ );
+ is(
+ tab.getAttribute("sharing"),
+ WEBRTC_TEST_STATE.sharing,
+ "Geolocation sharing doesn't update the tab sharing attribute."
+ );
+
+ // Update webRTC sharing state
+ gBrowser.updateBrowserSharing(browser, { webRTC: WEBRTC_TEST_STATE2 });
+ Assert.deepEqual(
+ tab._sharingState,
+ { geo: true, webRTC: WEBRTC_TEST_STATE2 },
+ "Should have updated webRTC sharing state while maintaining geolocation state."
+ );
+ is(
+ tab.getAttribute("sharing"),
+ WEBRTC_TEST_STATE2.sharing,
+ "Tab sharing attribute reflects webRTC sharing state."
+ );
+
+ // Clear webRTC sharing state
+ gBrowser.updateBrowserSharing(browser, { webRTC: null });
+ Assert.deepEqual(
+ tab._sharingState,
+ { geo: true, webRTC: null },
+ "Should only have sharing state for geolocation."
+ );
+ ok(
+ !tab.hasAttribute("sharing"),
+ "Ending webRTC sharing should remove tab sharing attribute."
+ );
+
+ // Clear geolocation sharing state
+ gBrowser.updateBrowserSharing(browser, { geo: null });
+ Assert.deepEqual(tab._sharingState, { geo: null, webRTC: null });
+ ok(
+ !tab.hasAttribute("sharing"),
+ "Tab sharing attribute should not be set."
+ );
+ });
+});
diff --git a/browser/base/content/test/siteIdentity/dummy_iframe_page.html b/browser/base/content/test/siteIdentity/dummy_iframe_page.html
new file mode 100644
index 0000000000..ea80367aa5
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/dummy_iframe_page.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+<title>Dummy iframe test page</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta>
+</head>
+<body>
+ <iframe src="https://example.org"></iframe>
+<p>Dummy test page</p>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/dummy_page.html b/browser/base/content/test/siteIdentity/dummy_page.html
new file mode 100644
index 0000000000..a7747a0bca
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/dummy_page.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+<title>Dummy test page</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta>
+</head>
+<body>
+ <a href="https://nocert.example.com" id="no-cert">No Cert page</a>
+<p>Dummy test page</p>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_bug1045809_1.html b/browser/base/content/test/siteIdentity/file_bug1045809_1.html
new file mode 100644
index 0000000000..c4f281d670
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug1045809_1.html
@@ -0,0 +1,7 @@
+<html>
+ <head>
+ </head>
+ <body>
+ <iframe src="http://test1.example.com/browser/browser/base/content/test/siteIdentity/file_bug1045809_2.html"></iframe>
+ </body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_bug1045809_2.html b/browser/base/content/test/siteIdentity/file_bug1045809_2.html
new file mode 100644
index 0000000000..67a297dbc5
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug1045809_2.html
@@ -0,0 +1,7 @@
+<html>
+ <head>
+ </head>
+ <body>
+ <div id="mixedContentContainer">Mixed Content is here</div>
+ </body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_bug822367_1.html b/browser/base/content/test/siteIdentity/file_bug822367_1.html
new file mode 100644
index 0000000000..a6e3fafc23
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug822367_1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test 1 for Mixed Content Blocker User Override - Mixed Script
+https://bugzilla.mozilla.org/show_bug.cgi?id=822367
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 1 for Bug 822367</title>
+</head>
+<body>
+ <div id="testContent">
+ <p id="p1"></p>
+ </div>
+ <script src="http://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_1.js">
+ </script>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_bug822367_1.js b/browser/base/content/test/siteIdentity/file_bug822367_1.js
new file mode 100644
index 0000000000..e4b5fb86c6
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug822367_1.js
@@ -0,0 +1 @@
+document.getElementById("p1").innerHTML = "hello";
diff --git a/browser/base/content/test/siteIdentity/file_bug822367_2.html b/browser/base/content/test/siteIdentity/file_bug822367_2.html
new file mode 100644
index 0000000000..fe56ee2130
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug822367_2.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test 2 for Mixed Content Blocker User Override - Mixed Display
+https://bugzilla.mozilla.org/show_bug.cgi?id=822367
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 2 for Bug 822367 - Mixed Display</title>
+</head>
+<body>
+ <div id="testContent">
+ <img src="http://example.com/tests/image/test/mochitest/blue.png">
+ </div>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_bug822367_3.html b/browser/base/content/test/siteIdentity/file_bug822367_3.html
new file mode 100644
index 0000000000..0cf5db7b20
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug822367_3.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test 3 for Mixed Content Blocker User Override - Mixed Script and Display
+https://bugzilla.mozilla.org/show_bug.cgi?id=822367
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 3 for Bug 822367</title>
+ <script>
+ function foo() {
+ var x = document.createElement("p");
+ x.setAttribute("id", "p2");
+ x.innerHTML = "bye";
+ document.getElementById("testContent").appendChild(x);
+ }
+ </script>
+</head>
+<body>
+ <div id="testContent">
+ <p id="p1"></p>
+ <img src="http://example.com/tests/image/test/mochitest/blue.png" onload="foo()">
+ </div>
+ <script src="http://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_1.js">
+ </script>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_bug822367_4.html b/browser/base/content/test/siteIdentity/file_bug822367_4.html
new file mode 100644
index 0000000000..8e5aeb67f2
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug822367_4.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test 4 for Mixed Content Blocker User Override - Mixed Script and Display
+https://bugzilla.mozilla.org/show_bug.cgi?id=822367
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 4 for Bug 822367</title>
+</head>
+<body>
+ <div id="testContent">
+ <p id="p1"></p>
+ </div>
+ <script src="http://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_4.js">
+ </script>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_bug822367_4.js b/browser/base/content/test/siteIdentity/file_bug822367_4.js
new file mode 100644
index 0000000000..8bdc791180
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug822367_4.js
@@ -0,0 +1,2 @@
+document.location =
+ "https://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_4B.html";
diff --git a/browser/base/content/test/siteIdentity/file_bug822367_4B.html b/browser/base/content/test/siteIdentity/file_bug822367_4B.html
new file mode 100644
index 0000000000..9af942525f
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug822367_4B.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test 4B for Mixed Content Blocker User Override - Location Changed
+https://bugzilla.mozilla.org/show_bug.cgi?id=822367
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 4B Location Change for Bug 822367</title>
+</head>
+<body>
+ <div id="testContent">
+ <p id="p1"></p>
+ </div>
+ <script src="http://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_1.js">
+ </script>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_bug822367_5.html b/browser/base/content/test/siteIdentity/file_bug822367_5.html
new file mode 100644
index 0000000000..6341539e83
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug822367_5.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test 5 for Mixed Content Blocker User Override - Mixed Script in document.open()
+https://bugzilla.mozilla.org/show_bug.cgi?id=822367
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 5 for Bug 822367</title>
+ <script>
+ function createDoc() {
+ var doc = document.open("text/html", "replace");
+ doc.write('<!DOCTYPE html><html><body><p id="p1">This is some content</p><script src="http://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_1.js">\<\/script\>\<\/body>\<\/html>');
+ doc.close();
+ }
+ </script>
+</head>
+<body>
+ <div id="testContent">
+ <img src="https://example.com/tests/image/test/mochitest/blue.png" onload="createDoc()">
+ </div>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_bug822367_6.html b/browser/base/content/test/siteIdentity/file_bug822367_6.html
new file mode 100644
index 0000000000..2c071a785d
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug822367_6.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test 6 for Mixed Content Blocker User Override - Mixed Script in document.open() within an iframe
+https://bugzilla.mozilla.org/show_bug.cgi?id=822367
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 6 for Bug 822367</title>
+</head>
+<body>
+ <div id="testContent">
+ <iframe name="f1" id="f1" src="https://example.com/browser/browser/base/content/test/siteIdentity/file_bug822367_5.html"></iframe>
+ </div>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_bug902156.js b/browser/base/content/test/siteIdentity/file_bug902156.js
new file mode 100644
index 0000000000..01ef4073fb
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug902156.js
@@ -0,0 +1,6 @@
+/*
+ * Once the mixed content blocker is disabled for the page, this scripts loads
+ * and updates the text inside the div container.
+ */
+document.getElementById("mctestdiv").innerHTML =
+ "Mixed Content Blocker disabled";
diff --git a/browser/base/content/test/siteIdentity/file_bug902156_1.html b/browser/base/content/test/siteIdentity/file_bug902156_1.html
new file mode 100644
index 0000000000..4cac7cfb93
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug902156_1.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ Test 1 for Bug 902156 - See file browser_bug902156.js for description.
+ https://bugzilla.mozilla.org/show_bug.cgi?id=902156
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 1 for Bug 902156</title>
+</head>
+<body>
+ <div id="mctestdiv">Mixed Content Blocker enabled</div>
+ <script src="http://test1.example.com/browser/browser/base/content/test/siteIdentity/file_bug902156.js" ></script>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_bug902156_2.html b/browser/base/content/test/siteIdentity/file_bug902156_2.html
new file mode 100644
index 0000000000..c815a09a93
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug902156_2.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ Test 2 for Bug 902156 - See file browser_bug902156.js for description.
+ https://bugzilla.mozilla.org/show_bug.cgi?id=902156
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 2 for Bug 902156</title>
+</head>
+<body>
+ <div id="mctestdiv">Mixed Content Blocker enabled</div>
+ <a href="https://test2.example.com/browser/browser/base/content/test/siteIdentity/file_bug902156_1.html"
+ id="mctestlink" target="_top">Go to http site</a>
+ <script src="http://test2.example.com/browser/browser/base/content/test/siteIdentity/file_bug902156.js" ></script>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_bug902156_3.html b/browser/base/content/test/siteIdentity/file_bug902156_3.html
new file mode 100644
index 0000000000..7a26f4b0f0
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug902156_3.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ Test 3 for Bug 902156 - See file browser_bug902156.js for description.
+ https://bugzilla.mozilla.org/show_bug.cgi?id=902156
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 3 for Bug 902156</title>
+</head>
+<body>
+ <div id="mctestdiv">Mixed Content Blocker enabled</div>
+ <script src="http://test1.example.com/browser/browser/base/content/test/siteIdentity/file_bug902156.js" ></script>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_bug906190.js b/browser/base/content/test/siteIdentity/file_bug906190.js
new file mode 100644
index 0000000000..01ef4073fb
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug906190.js
@@ -0,0 +1,6 @@
+/*
+ * Once the mixed content blocker is disabled for the page, this scripts loads
+ * and updates the text inside the div container.
+ */
+document.getElementById("mctestdiv").innerHTML =
+ "Mixed Content Blocker disabled";
diff --git a/browser/base/content/test/siteIdentity/file_bug906190.sjs b/browser/base/content/test/siteIdentity/file_bug906190.sjs
new file mode 100644
index 0000000000..088153d671
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug906190.sjs
@@ -0,0 +1,18 @@
+function handleRequest(request, response) {
+ var page = "<!DOCTYPE html><html><body>bug 906190</body></html>";
+ var path =
+ "https://test1.example.com/browser/browser/base/content/test/siteIdentity/";
+ var url;
+
+ if (request.queryString.includes("bad-redirection=1")) {
+ url = path + "this_page_does_not_exist.html";
+ } else {
+ url = path + "file_bug906190_redirected.html";
+ }
+
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html", false);
+ response.setStatusLine(request.httpVersion, "302", "Found");
+ response.setHeader("Location", url, false);
+ response.write(page);
+}
diff --git a/browser/base/content/test/siteIdentity/file_bug906190_1.html b/browser/base/content/test/siteIdentity/file_bug906190_1.html
new file mode 100644
index 0000000000..031c229f0d
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug906190_1.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ Test 1 for Bug 906190 - See file browser_bug902156.js for description.
+ https://bugzilla.mozilla.org/show_bug.cgi?id=906190
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 1 for Bug 906190</title>
+</head>
+<body>
+ <div id="mctestdiv">Mixed Content Blocker enabled</div>
+ <script src="http://test1.example.com/browser/browser/base/content/test/siteIdentity/file_bug906190.js" ></script>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_bug906190_2.html b/browser/base/content/test/siteIdentity/file_bug906190_2.html
new file mode 100644
index 0000000000..2a7546dca4
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug906190_2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ Test 2 for Bug 906190 - See file browser_bug902156.js for description.
+ https://bugzilla.mozilla.org/show_bug.cgi?id=906190
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 2 for Bug 906190</title>
+</head>
+<body>
+ <div id="mctestdiv">Mixed Content Blocker enabled</div>
+ <script src="http://test2.example.com/browser/browser/base/content/test/siteIdentity/file_bug906190.js" ></script>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_bug906190_3_4.html b/browser/base/content/test/siteIdentity/file_bug906190_3_4.html
new file mode 100644
index 0000000000..e78e271f85
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug906190_3_4.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ Test 3 and 4 for Bug 906190 - See file browser_bug902156.js for description.
+ https://bugzilla.mozilla.org/show_bug.cgi?id=906190
+-->
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="refresh" content="0; url=https://test1.example.com/browser/browser/base/content/test/siteIdentity/file_bug906190_redirected.html">
+ <title>Test 3 and 4 for Bug 906190</title>
+</head>
+<body>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_bug906190_redirected.html b/browser/base/content/test/siteIdentity/file_bug906190_redirected.html
new file mode 100644
index 0000000000..d0bc4a39f5
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_bug906190_redirected.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ Redirected Page of Test 3 to 6 for Bug 906190 - See file browser_bug902156.js for description.
+ https://bugzilla.mozilla.org/show_bug.cgi?id=906190
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Redirected Page for Bug 906190</title>
+</head>
+<body>
+ <div id="mctestdiv">Mixed Content Blocker enabled</div>
+ <script src="http://test1.example.com/browser/browser/base/content/test/siteIdentity/file_bug906190.js" ></script>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_csp_block_all_mixedcontent.html b/browser/base/content/test/siteIdentity/file_csp_block_all_mixedcontent.html
new file mode 100644
index 0000000000..b5463d8d5b
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_csp_block_all_mixedcontent.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1122236 - CSP: Implement block-all-mixed-content</title>
+ <meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">
+</head>
+<body>
+ <script src="http://example.com/browser/browser/base/content/test/siteIdentity/file_csp_block_all_mixedcontent.js"></script>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_csp_block_all_mixedcontent.js b/browser/base/content/test/siteIdentity/file_csp_block_all_mixedcontent.js
new file mode 100644
index 0000000000..dc6d6a64e4
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_csp_block_all_mixedcontent.js
@@ -0,0 +1,3 @@
+// empty script file just used for testing Bug 1122236.
+// Making sure the UI is not degraded when blocking
+// mixed content using the CSP directive: block-all-mixed-content.
diff --git a/browser/base/content/test/siteIdentity/file_mixedContentFramesOnHttp.html b/browser/base/content/test/siteIdentity/file_mixedContentFramesOnHttp.html
new file mode 100644
index 0000000000..3ed5b82641
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_mixedContentFramesOnHttp.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test for https://bugzilla.mozilla.org/show_bug.cgi?id=1182551
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1182551</title>
+</head>
+<body>
+ <p>Test for Bug 1182551. This is an HTTP top level page. We include an HTTPS iframe that loads mixed passive content.</p>
+ <iframe src="https://example.org/browser/browser/base/content/test/siteIdentity/file_mixedPassiveContent.html"></iframe>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_mixedContentFromOnunload.html b/browser/base/content/test/siteIdentity/file_mixedContentFromOnunload.html
new file mode 100644
index 0000000000..ae134f8cb0
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_mixedContentFromOnunload.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test for https://bugzilla.mozilla.org/show_bug.cgi?id=947079
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 947079</title>
+</head>
+<body>
+ <p>Test for Bug 947079</p>
+ <script>
+ window.addEventListener("unload", function() {
+ new Image().src = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
+ });
+ </script>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_mixedContentFromOnunload_test1.html b/browser/base/content/test/siteIdentity/file_mixedContentFromOnunload_test1.html
new file mode 100644
index 0000000000..1d027b0362
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_mixedContentFromOnunload_test1.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test 1 for https://bugzilla.mozilla.org/show_bug.cgi?id=947079
+Page with no insecure subresources
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 1 for Bug 947079</title>
+</head>
+<body>
+ <p>There are no insecure resource loads on this page</p>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_mixedContentFromOnunload_test2.html b/browser/base/content/test/siteIdentity/file_mixedContentFromOnunload_test2.html
new file mode 100644
index 0000000000..4813337cc8
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_mixedContentFromOnunload_test2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test 2 for https://bugzilla.mozilla.org/show_bug.cgi?id=947079
+Page with an insecure image load
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 2 for Bug 947079</title>
+</head>
+<body>
+ <p>Page with http image load</p>
+ <img src="http://test2.example.com/tests/image/test/mochitest/blue.png">
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_mixedPassiveContent.html b/browser/base/content/test/siteIdentity/file_mixedPassiveContent.html
new file mode 100644
index 0000000000..a60ac94e8b
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_mixedPassiveContent.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test for https://bugzilla.mozilla.org/show_bug.cgi?id=1182551
+-->
+<head>
+ <meta charset="utf-8">
+ <title>HTTPS page with HTTP image</title>
+</head>
+<body>
+ <img src="http://mochi.test:8888/tests/image/test/mochitest/blue.png">
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/file_pdf.pdf b/browser/base/content/test/siteIdentity/file_pdf.pdf
new file mode 100644
index 0000000000..593558f9a4
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_pdf.pdf
@@ -0,0 +1,12 @@
+%PDF-1.0
+1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj 2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj 3 0 obj<</Type/Page/MediaBox[0 0 3 3]>>endobj
+xref
+0 4
+0000000000 65535 f
+0000000010 00000 n
+0000000053 00000 n
+0000000102 00000 n
+trailer<</Size 4/Root 1 0 R>>
+startxref
+149
+%EOF \ No newline at end of file
diff --git a/browser/base/content/test/siteIdentity/file_pdf_blob.html b/browser/base/content/test/siteIdentity/file_pdf_blob.html
new file mode 100644
index 0000000000..ff6ed659a2
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/file_pdf_blob.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset='utf-8'>
+</head>
+<body>
+ <script>
+ let blob = new Blob(["x"], { type: "application/pdf" });
+ let blobURL = URL.createObjectURL(blob);
+
+ let link = document.createElement("a");
+ link.innerText = "PDF blob";
+ link.target = "_blank";
+ link.href = blobURL;
+ document.body.appendChild(link);
+ </script>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/head.js b/browser/base/content/test/siteIdentity/head.js
new file mode 100644
index 0000000000..d2a588a815
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/head.js
@@ -0,0 +1,435 @@
+function openIdentityPopup() {
+ gIdentityHandler._initializePopup();
+ let mainView = document.getElementById("identity-popup-mainView");
+ let viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown");
+ gIdentityHandler._identityIconBox.click();
+ return viewShown;
+}
+
+function openPermissionPopup() {
+ gPermissionPanel._initializePopup();
+ let mainView = document.getElementById("permission-popup-mainView");
+ let viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown");
+ gPermissionPanel.openPopup();
+ return viewShown;
+}
+
+function getIdentityMode(aWindow = window) {
+ return aWindow.document.getElementById("identity-box").className;
+}
+
+/**
+ * Waits for a load (or custom) event to finish in a given tab. If provided
+ * load an uri into the tab.
+ *
+ * @param tab
+ * The tab to load into.
+ * @param [optional] url
+ * The url to load, or the current url.
+ * @return {Promise} resolved when the event is handled.
+ * @resolves to the received event
+ * @rejects if a valid load event is not received within a meaningful interval
+ */
+function promiseTabLoadEvent(tab, url) {
+ info("Wait tab event: load");
+
+ function handle(loadedUrl) {
+ if (loadedUrl === "about:blank" || (url && loadedUrl !== url)) {
+ info(`Skipping spurious load event for ${loadedUrl}`);
+ return false;
+ }
+
+ info("Tab event received: load");
+ return true;
+ }
+
+ let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, handle);
+
+ if (url) {
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, url);
+ }
+
+ return loaded;
+}
+
+// Compares the security state of the page with what is expected
+function isSecurityState(browser, expectedState) {
+ let ui = browser.securityUI;
+ if (!ui) {
+ ok(false, "No security UI to get the security state");
+ return;
+ }
+
+ const wpl = Ci.nsIWebProgressListener;
+
+ // determine the security state
+ let isSecure = ui.state & wpl.STATE_IS_SECURE;
+ let isBroken = ui.state & wpl.STATE_IS_BROKEN;
+ let isInsecure = ui.state & wpl.STATE_IS_INSECURE;
+
+ let actualState;
+ if (isSecure && !(isBroken || isInsecure)) {
+ actualState = "secure";
+ } else if (isBroken && !(isSecure || isInsecure)) {
+ actualState = "broken";
+ } else if (isInsecure && !(isSecure || isBroken)) {
+ actualState = "insecure";
+ } else {
+ actualState = "unknown";
+ }
+
+ is(
+ expectedState,
+ actualState,
+ "Expected state " +
+ expectedState +
+ " and the actual state is " +
+ actualState +
+ "."
+ );
+}
+
+/**
+ * Test the state of the identity box and control center to make
+ * sure they are correctly showing the expected mixed content states.
+ *
+ * @note The checks are done synchronously, but new code should wait on the
+ * returned Promise object to ensure the identity panel has closed.
+ * Bug 1221114 is filed to fix the existing code.
+ *
+ * @param tabbrowser
+ * @param Object states
+ * MUST include the following properties:
+ * {
+ * activeLoaded: true|false,
+ * activeBlocked: true|false,
+ * passiveLoaded: true|false,
+ * }
+ *
+ * @return {Promise}
+ * @resolves When the operation has finished and the identity panel has closed.
+ */
+async function assertMixedContentBlockingState(tabbrowser, states = {}) {
+ if (
+ !tabbrowser ||
+ !("activeLoaded" in states) ||
+ !("activeBlocked" in states) ||
+ !("passiveLoaded" in states)
+ ) {
+ throw new Error(
+ "assertMixedContentBlockingState requires a browser and a states object"
+ );
+ }
+
+ let { passiveLoaded, activeLoaded, activeBlocked } = states;
+ let { gIdentityHandler } = tabbrowser.ownerGlobal;
+ let doc = tabbrowser.ownerDocument;
+ let identityBox = gIdentityHandler._identityBox;
+ let classList = identityBox.classList;
+ let identityIcon = doc.getElementById("identity-icon");
+ let identityIconImage = tabbrowser.ownerGlobal
+ .getComputedStyle(identityIcon)
+ .getPropertyValue("list-style-image");
+
+ let stateSecure =
+ gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_SECURE;
+ let stateBroken =
+ gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_BROKEN;
+ let stateInsecure =
+ gIdentityHandler._state & Ci.nsIWebProgressListener.STATE_IS_INSECURE;
+ let stateActiveBlocked =
+ gIdentityHandler._state &
+ Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT;
+ let stateActiveLoaded =
+ gIdentityHandler._state &
+ Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT;
+ let statePassiveLoaded =
+ gIdentityHandler._state &
+ Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT;
+
+ is(
+ activeBlocked,
+ !!stateActiveBlocked,
+ "Expected state for activeBlocked matches UI state"
+ );
+ is(
+ activeLoaded,
+ !!stateActiveLoaded,
+ "Expected state for activeLoaded matches UI state"
+ );
+ is(
+ passiveLoaded,
+ !!statePassiveLoaded,
+ "Expected state for passiveLoaded matches UI state"
+ );
+
+ if (stateInsecure) {
+ const insecureConnectionIcon = Services.prefs.getBoolPref(
+ "security.insecure_connection_icon.enabled"
+ );
+ if (!insecureConnectionIcon) {
+ // HTTP request, there should be no MCB classes for the identity box and the non secure icon
+ // should always be visible regardless of MCB state.
+ ok(classList.contains("unknownIdentity"), "unknownIdentity on HTTP page");
+ ok(
+ BrowserTestUtils.is_visible(identityIcon),
+ "information icon should be still visible"
+ );
+ } else {
+ // HTTP request, there should be a broken padlock shown always.
+ ok(classList.contains("notSecure"), "notSecure on HTTP page");
+ ok(
+ !BrowserTestUtils.is_hidden(identityIcon),
+ "information icon should be visible"
+ );
+ }
+
+ ok(!classList.contains("mixedActiveContent"), "No MCB icon on HTTP page");
+ ok(!classList.contains("mixedActiveBlocked"), "No MCB icon on HTTP page");
+ ok(!classList.contains("mixedDisplayContent"), "No MCB icon on HTTP page");
+ ok(
+ !classList.contains("mixedDisplayContentLoadedActiveBlocked"),
+ "No MCB icon on HTTP page"
+ );
+ } else {
+ // Make sure the identity box UI has the correct mixedcontent states and icons
+ is(
+ classList.contains("mixedActiveContent"),
+ activeLoaded,
+ "identityBox has expected class for activeLoaded"
+ );
+ is(
+ classList.contains("mixedActiveBlocked"),
+ activeBlocked && !passiveLoaded,
+ "identityBox has expected class for activeBlocked && !passiveLoaded"
+ );
+ is(
+ classList.contains("mixedDisplayContent"),
+ passiveLoaded && !(activeLoaded || activeBlocked),
+ "identityBox has expected class for passiveLoaded && !(activeLoaded || activeBlocked)"
+ );
+ is(
+ classList.contains("mixedDisplayContentLoadedActiveBlocked"),
+ passiveLoaded && activeBlocked,
+ "identityBox has expected class for passiveLoaded && activeBlocked"
+ );
+
+ ok(
+ !BrowserTestUtils.is_hidden(identityIcon),
+ "information icon should be visible"
+ );
+ if (activeLoaded) {
+ is(
+ identityIconImage,
+ 'url("chrome://global/skin/icons/security-broken.svg")',
+ "Using active loaded icon"
+ );
+ }
+ if (activeBlocked && !passiveLoaded) {
+ is(
+ identityIconImage,
+ 'url("chrome://global/skin/icons/security.svg")',
+ "Using active blocked icon"
+ );
+ }
+ if (passiveLoaded && !(activeLoaded || activeBlocked)) {
+ is(
+ identityIconImage,
+ 'url("chrome://global/skin/icons/security-warning.svg")',
+ "Using passive loaded icon"
+ );
+ }
+ if (passiveLoaded && activeBlocked) {
+ is(
+ identityIconImage,
+ 'url("chrome://global/skin/icons/security-warning.svg")',
+ "Using active blocked and passive loaded icon"
+ );
+ }
+ }
+
+ // Make sure the identity popup has the correct mixedcontent states
+ let promisePanelOpen = BrowserTestUtils.waitForEvent(
+ tabbrowser.ownerGlobal,
+ "popupshown",
+ true,
+ event => event.target == gIdentityHandler._identityPopup
+ );
+ gIdentityHandler._identityIconBox.click();
+ await promisePanelOpen;
+ let popupAttr = doc
+ .getElementById("identity-popup")
+ .getAttribute("mixedcontent");
+ let bodyAttr = doc
+ .getElementById("identity-popup-securityView-extended-info")
+ .getAttribute("mixedcontent");
+
+ is(
+ popupAttr.includes("active-loaded"),
+ activeLoaded,
+ "identity-popup has expected attr for activeLoaded"
+ );
+ is(
+ bodyAttr.includes("active-loaded"),
+ activeLoaded,
+ "securityView-body has expected attr for activeLoaded"
+ );
+
+ is(
+ popupAttr.includes("active-blocked"),
+ activeBlocked,
+ "identity-popup has expected attr for activeBlocked"
+ );
+ is(
+ bodyAttr.includes("active-blocked"),
+ activeBlocked,
+ "securityView-body has expected attr for activeBlocked"
+ );
+
+ is(
+ popupAttr.includes("passive-loaded"),
+ passiveLoaded,
+ "identity-popup has expected attr for passiveLoaded"
+ );
+ is(
+ bodyAttr.includes("passive-loaded"),
+ passiveLoaded,
+ "securityView-body has expected attr for passiveLoaded"
+ );
+
+ // Make sure the correct icon is visible in the Control Center.
+ // This logic is controlled with CSS, so this helps prevent regressions there.
+ let securityViewBG = tabbrowser.ownerGlobal
+ .getComputedStyle(
+ document
+ .getElementById("identity-popup-securityView")
+ .getElementsByClassName("identity-popup-security-connection")[0]
+ )
+ .getPropertyValue("list-style-image");
+ let securityContentBG = tabbrowser.ownerGlobal
+ .getComputedStyle(
+ document
+ .getElementById("identity-popup-mainView")
+ .getElementsByClassName("identity-popup-security-connection")[0]
+ )
+ .getPropertyValue("list-style-image");
+
+ if (stateInsecure) {
+ is(
+ securityViewBG,
+ 'url("chrome://global/skin/icons/security-broken.svg")',
+ "CC using 'not secure' icon"
+ );
+ is(
+ securityContentBG,
+ 'url("chrome://global/skin/icons/security-broken.svg")',
+ "CC using 'not secure' icon"
+ );
+ }
+
+ if (stateSecure) {
+ is(
+ securityViewBG,
+ 'url("chrome://global/skin/icons/security.svg")',
+ "CC using secure icon"
+ );
+ is(
+ securityContentBG,
+ 'url("chrome://global/skin/icons/security.svg")',
+ "CC using secure icon"
+ );
+ }
+
+ if (stateBroken) {
+ if (activeLoaded) {
+ is(
+ securityViewBG,
+ 'url("chrome://browser/skin/controlcenter/mcb-disabled.svg")',
+ "CC using active loaded icon"
+ );
+ is(
+ securityContentBG,
+ 'url("chrome://browser/skin/controlcenter/mcb-disabled.svg")',
+ "CC using active loaded icon"
+ );
+ } else if (activeBlocked || passiveLoaded) {
+ is(
+ securityViewBG,
+ 'url("chrome://global/skin/icons/security-warning.svg")',
+ "CC using degraded icon"
+ );
+ is(
+ securityContentBG,
+ 'url("chrome://global/skin/icons/security-warning.svg")',
+ "CC using degraded icon"
+ );
+ } else {
+ // There is a case here with weak ciphers, but no bc tests are handling this yet.
+ is(
+ securityViewBG,
+ 'url("chrome://global/skin/icons/security.svg")',
+ "CC using degraded icon"
+ );
+ is(
+ securityContentBG,
+ 'url("chrome://global/skin/icons/security.svg")',
+ "CC using degraded icon"
+ );
+ }
+ }
+
+ if (activeLoaded || activeBlocked || passiveLoaded) {
+ let promiseViewShown = BrowserTestUtils.waitForEvent(
+ gIdentityHandler._identityPopup,
+ "ViewShown"
+ );
+ doc.getElementById("identity-popup-security-button").click();
+ await promiseViewShown;
+ is(
+ Array.prototype.filter.call(
+ doc
+ .getElementById("identity-popup-securityView")
+ .querySelectorAll(".identity-popup-mcb-learn-more"),
+ element => !BrowserTestUtils.is_hidden(element)
+ ).length,
+ 1,
+ "The 'Learn more' link should be visible once."
+ );
+ }
+
+ if (gIdentityHandler._identityPopup.state != "closed") {
+ let hideEvent = BrowserTestUtils.waitForEvent(
+ gIdentityHandler._identityPopup,
+ "popuphidden"
+ );
+ info("Hiding identity popup");
+ gIdentityHandler._identityPopup.hidePopup();
+ await hideEvent;
+ }
+}
+
+async function loadBadCertPage(url) {
+ let loaded = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
+ BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, url);
+ await loaded;
+
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
+ content.document.getElementById("exceptionDialogButton").click();
+ });
+ await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+}
+
+// nsITLSServerSocket needs a certificate with a corresponding private key
+// available. In mochitests, the certificate with the common name "Mochitest
+// client" has such a key.
+function getTestServerCertificate() {
+ const certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ for (const cert of certDB.getCerts()) {
+ if (cert.commonName == "Mochitest client") {
+ return cert;
+ }
+ }
+ return null;
+}
diff --git a/browser/base/content/test/siteIdentity/iframe_navigation.html b/browser/base/content/test/siteIdentity/iframe_navigation.html
new file mode 100644
index 0000000000..d4564569e7
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/iframe_navigation.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+<meta charset="UTF-8">
+</head>
+<body class="running">
+ <script>
+ window.addEventListener("message", doNavigation);
+
+ function doNavigation() {
+ let destination;
+ let destinationIdentifier = window.location.hash.substring(1);
+ switch (destinationIdentifier) {
+ case "blank":
+ destination = "about:blank";
+ break;
+ case "secure":
+ destination =
+ "https://example.com/browser/browser/base/content/test/siteIdentity/dummy_page.html";
+ break;
+ case "insecure":
+ destination =
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/browser/browser/base/content/test/siteIdentity/dummy_page.html";
+ break;
+ }
+ setTimeout(() => {
+ let frame = document.getElementById("navigateMe");
+ frame.onload = done;
+ frame.onerror = done;
+ frame.src = destination;
+ }, 0);
+ }
+
+ function done() {
+ document.body.classList.toggle("running");
+ }
+ </script>
+ <iframe id="navigateMe" src="dummy_page.html">
+ </iframe>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/insecure_opener.html b/browser/base/content/test/siteIdentity/insecure_opener.html
new file mode 100644
index 0000000000..26ed014f63
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/insecure_opener.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <meta charset="utf8">
+ </head>
+ <body>
+ <a id="link" target="_blank" href="https://example.com/browser/toolkit/components/passwordmgr/test/browser/form_basic.html">Click me, I'm "secure".</a>
+ </body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/open-self-from-frame.html b/browser/base/content/test/siteIdentity/open-self-from-frame.html
new file mode 100644
index 0000000000..17d0cf56ef
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/open-self-from-frame.html
@@ -0,0 +1,6 @@
+<iframe src="about:blank"></iframe>
+<script>
+ document.querySelector("iframe").contentDocument.write(
+ `<button onclick="window.open().document.write('Hi')">click me!</button>`
+ );
+</script>
diff --git a/browser/base/content/test/siteIdentity/simple_mixed_passive.html b/browser/base/content/test/siteIdentity/simple_mixed_passive.html
new file mode 100644
index 0000000000..2e4cda790a
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/simple_mixed_passive.html
@@ -0,0 +1 @@
+<img src="http://example.com/browser/browser/base/content/test/siteIdentity/moz.png">
diff --git a/browser/base/content/test/siteIdentity/test-mixedcontent-securityerrors.html b/browser/base/content/test/siteIdentity/test-mixedcontent-securityerrors.html
new file mode 100644
index 0000000000..cb8cfdaaf5
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/test-mixedcontent-securityerrors.html
@@ -0,0 +1,21 @@
+<!--
+ Bug 875456 - Log mixed content messages from the Mixed Content Blocker to the
+ Security Pane in the Web Console
+-->
+
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <meta charset="utf8">
+ <title>Mixed Content test - http on https</title>
+ <script src="testscript.js"></script>
+ <!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+ </head>
+ <body>
+ <iframe src="http://example.com"></iframe>
+ <img src="http://example.com/tests/image/test/mochitest/blue.png"></img>
+ </body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/test_mcb_double_redirect_image.html b/browser/base/content/test/siteIdentity/test_mcb_double_redirect_image.html
new file mode 100644
index 0000000000..adadf01944
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/test_mcb_double_redirect_image.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ Test 7-9 for Bug 1082837 - See file browser_mcb_redirect.js for description.
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1082837
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1082837</title>
+ <script>
+ function image_loaded() {
+ document.getElementById("mctestdiv").innerHTML = "image loaded";
+ }
+ function image_blocked() {
+ document.getElementById("mctestdiv").innerHTML = "image blocked";
+ }
+ </script>
+</head>
+<body>
+ <div id="mctestdiv"></div>
+ <img src="https://example.com/browser/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs?image_redirect_http_sjs" onload="image_loaded()" onerror="image_blocked()" ></image>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/test_mcb_redirect.html b/browser/base/content/test/siteIdentity/test_mcb_redirect.html
new file mode 100644
index 0000000000..fc7ccc2764
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/test_mcb_redirect.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ Test 1 for Bug 418354 - See file browser_mcb_redirect.js for description.
+ https://bugzilla.mozilla.org/show_bug.cgi?id=418354
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Bug 418354</title>
+</head>
+<body>
+ <div id="mctestdiv">script blocked</div>
+ <script src="https://example.com/browser/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs?script" ></script>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/test_mcb_redirect.js b/browser/base/content/test/siteIdentity/test_mcb_redirect.js
new file mode 100644
index 0000000000..48538c9409
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/test_mcb_redirect.js
@@ -0,0 +1,5 @@
+/*
+ * Once the mixed content blocker is disabled for the page, this scripts loads
+ * and updates the text inside the div container.
+ */
+document.getElementById("mctestdiv").innerHTML = "script executed";
diff --git a/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs b/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs
new file mode 100644
index 0000000000..53b8cf2b08
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs
@@ -0,0 +1,29 @@
+function handleRequest(request, response) {
+ var page =
+ "<!DOCTYPE html><html><body>bug 418354 and bug 1082837</body></html>";
+
+ let redirect;
+ if (request.queryString === "script") {
+ redirect =
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/browser/browser/base/content/test/siteIdentity/test_mcb_redirect.js";
+ response.setHeader("Cache-Control", "no-cache", false);
+ } else if (request.queryString === "image_http") {
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ redirect = "http://example.com/tests/image/test/mochitest/blue.png";
+ response.setHeader("Cache-Control", "max-age=3600", false);
+ } else if (request.queryString === "image_redirect_http_sjs") {
+ redirect =
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/browser/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs?image_redirect_https";
+ response.setHeader("Cache-Control", "max-age=3600", false);
+ } else if (request.queryString === "image_redirect_https") {
+ redirect = "https://example.com/tests/image/test/mochitest/blue.png";
+ response.setHeader("Cache-Control", "max-age=3600", false);
+ }
+
+ response.setHeader("Content-Type", "text/html", false);
+ response.setStatusLine(request.httpVersion, "302", "Found");
+ response.setHeader("Location", redirect, false);
+ response.write(page);
+}
diff --git a/browser/base/content/test/siteIdentity/test_mcb_redirect_image.html b/browser/base/content/test/siteIdentity/test_mcb_redirect_image.html
new file mode 100644
index 0000000000..42da0d7c13
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/test_mcb_redirect_image.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ Test 3-6 for Bug 1082837 - See file browser_mcb_redirect.js for description.
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1082837
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1082837</title>
+ <script>
+ function image_loaded() {
+ document.getElementById("mctestdiv").innerHTML = "image loaded";
+ }
+ function image_blocked() {
+ document.getElementById("mctestdiv").innerHTML = "image blocked";
+ }
+ </script>
+</head>
+<body>
+ <div id="mctestdiv"></div>
+ <img src="https://example.com/browser/browser/base/content/test/siteIdentity/test_mcb_redirect.sjs?image_http" onload="image_loaded()" onerror="image_blocked()" ></image>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/test_no_mcb_for_loopback.html b/browser/base/content/test/siteIdentity/test_no_mcb_for_loopback.html
new file mode 100644
index 0000000000..34193d370b
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_for_loopback.html
@@ -0,0 +1,56 @@
+<!-- See browser_no_mcb_for_localhost.js -->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>Bug 903966, Bug 1402530</title>
+ </head>
+
+ <style>
+ @font-face {
+ font-family: "Font-IPv4";
+ src: url("http://127.0.0.1:8/test.ttf");
+ }
+
+ @font-face {
+ font-family: "Font-IPv6";
+ src: url("http://[::1]:8/test.ttf");
+ }
+
+ #ip-v4 {
+ font-family: "Font-IPv4"
+ }
+
+ #ip-v6 {
+ font-family: "Font-IPv6"
+ }
+ </style>
+
+ <body>
+ <div id="ip-v4">test</div>
+ <div id="ip-v6">test</div>
+
+ <img src="http://127.0.0.1:8/test.png">
+ <img src="http://[::1]:8/test.png">
+ <img src="http://localhost:8/test.png">
+
+ <iframe src="http://127.0.0.1:8/test.html"></iframe>
+ <iframe src="http://[::1]:8/test.html"></iframe>
+ <iframe src="http://localhost:8/test.html"></iframe>
+ </body>
+
+ <script src="http://127.0.0.1:8/test.js"></script>
+ <script src="http://[::1]:8/test.js"></script>
+ <script src="http://localhost:8/test.js"></script>
+
+ <link href="http://127.0.0.1:8/test.css" rel="stylesheet"></link>
+ <link href="http://[::1]:8/test.css" rel="stylesheet"></link>
+ <link href="http://localhost:8/test.css" rel="stylesheet"></link>
+
+ <script>
+ fetch("http://127.0.0.1:8");
+ fetch("http://localhost:8");
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ fetch("http://[::1]:8");
+ </script>
+</html>
diff --git a/browser/base/content/test/siteIdentity/test_no_mcb_for_onions.html b/browser/base/content/test/siteIdentity/test_no_mcb_for_onions.html
new file mode 100644
index 0000000000..c73c3681a3
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_for_onions.html
@@ -0,0 +1,29 @@
+<!-- See browser_no_mcb_for_onions.js -->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>Bug 1382359</title>
+ </head>
+
+ <style>
+ @font-face {
+ src: url("http://123456789abcdef.onion:8/test.ttf");
+ }
+ </style>
+
+ <body>
+ <img src="http://123456789abcdef.onion:8/test.png">
+
+ <iframe src="http://123456789abcdef.onion:8/test.html"></iframe>
+ </body>
+
+ <script src="http://123456789abcdef.onion:8/test.js"></script>
+
+ <link href="http://123456789abcdef.onion:8/test.css" rel="stylesheet"></link>
+
+ <script>
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ fetch("http://123456789abcdef.onion:8");
+ </script>
+</html>
diff --git a/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.css b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.css
new file mode 100644
index 0000000000..0587ef7939
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.css
@@ -0,0 +1,11 @@
+@font-face {
+ font-family: testFont;
+ src: url(http://example.com/browser/devtools/client/fontinspector/test/browser_font.woff);
+}
+/* stylelint-disable font-family-no-missing-generic-family-keyword */
+body {
+ font-family: Arial;
+}
+div {
+ font-family: testFont;
+}
diff --git a/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.html b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.html
new file mode 100644
index 0000000000..7b39be064c
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ Test 2 for Bug 909920 - See file browser_no_mcb_on_http_site.js for description.
+ https://bugzilla.mozilla.org/show_bug.cgi?id=909920
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 2 for Bug 909920</title>
+ <link rel="stylesheet" type="text/css" href="https://example.com/browser/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.css" />
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+<script type="text/javascript">
+ async function checkLoadStates() {
+ let state = await SpecialPowers.getSecurityState(window);
+
+ var loadedMixedActive =
+ !!(state & SpecialPowers.Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT);
+ is(loadedMixedActive, false, "OK: Should not load mixed active content!");
+
+ var blockedMixedActive =
+ !!(state & SpecialPowers.Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT);
+ is(blockedMixedActive, false, "OK: Should not block mixed active content!");
+
+ var loadedMixedDisplay =
+ !!(state & SpecialPowers.Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT);
+ is(loadedMixedDisplay, false, "OK: Should not load mixed display content!");
+
+ var blockedMixedDisplay =
+ !!(state & SpecialPowers.Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_DISPLAY_CONTENT);
+ is(blockedMixedDisplay, false, "OK: Should not block mixed display content!");
+
+ var newValue = "Verifying MCB does not trigger warning/error for an http page with https css that includes http font";
+ // eslint-disable-next-line no-unsanitized/property
+ document.getElementById("testDiv").innerHTML = newValue;
+ }
+</script>
+</head>
+<body onload="checkLoadStates()">
+ <div class="testDiv" id="testDiv">
+ Testing MCB does not trigger warning/error for an http page with https css that includes http font
+ </div>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.css b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.css
new file mode 100644
index 0000000000..3ac6c87a6b
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.css
@@ -0,0 +1 @@
+@import url(http://example.com/browser/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font.css);
diff --git a/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.html b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.html
new file mode 100644
index 0000000000..3da31592dd
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ Test 3 for Bug 909920 - See file browser_no_mcb_on_http_site.js for description.
+ https://bugzilla.mozilla.org/show_bug.cgi?id=909920
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 3 for Bug 909920</title>
+ <link rel="stylesheet" type="text/css" href="https://example.com/browser/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_font2.css" />
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+<script type="text/javascript">
+ async function checkLoadStates() {
+ let state = await SpecialPowers.getSecurityState(window);
+
+ var loadedMixedActive =
+ !!(state & SpecialPowers.Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT);
+ is(loadedMixedActive, false, "OK: Should not load mixed active content!");
+
+ var blockedMixedActive =
+ !!(state & SpecialPowers.Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT);
+ is(blockedMixedActive, false, "OK: Should not block mixed active content!");
+
+ var loadedMixedDisplay =
+ !!(state & SpecialPowers.Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT);
+ is(loadedMixedDisplay, false, "OK: Should not load mixed display content!");
+
+ var blockedMixedDisplay =
+ !!(state & SpecialPowers.Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_DISPLAY_CONTENT);
+ is(blockedMixedDisplay, false, "OK: Should not block mixed display content!");
+
+ var newValue = "Verifying MCB does not trigger warning/error for an http page ";
+ newValue += "with https css that imports another http css which includes http font";
+ // eslint-disable-next-line no-unsanitized/property
+ document.getElementById("testDiv").innerHTML = newValue;
+ }
+</script>
+</head>
+<body onload="checkLoadStates()">
+ <div class="testDiv" id="testDiv">
+ Testing MCB does not trigger warning/error for an http page with https css that imports another http css which includes http font
+ </div>
+</body>
+</html>
diff --git a/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.css b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.css
new file mode 100644
index 0000000000..d045e21ba0
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.css
@@ -0,0 +1,3 @@
+#testDiv {
+ background: url(http://example.com/tests/image/test/mochitest/blue.png)
+}
diff --git a/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.html b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.html
new file mode 100644
index 0000000000..10aa281959
--- /dev/null
+++ b/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ Test 1 for Bug 909920 - See file browser_no_mcb_on_http_site.js for description.
+ https://bugzilla.mozilla.org/show_bug.cgi?id=909920
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 1 for Bug 909920</title>
+ <link rel="stylesheet" type="text/css" href="https://example.com/browser/browser/base/content/test/siteIdentity/test_no_mcb_on_http_site_img.css" />
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+<script type="text/javascript">
+ async function checkLoadStates() {
+ let state = await SpecialPowers.getSecurityState(window);
+
+ var loadedMixedActive =
+ !!(state & SpecialPowers.Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT);
+ is(loadedMixedActive, false, "OK: Should not load mixed active content!");
+
+ var blockedMixedActive =
+ !!(state & SpecialPowers.Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT);
+ is(blockedMixedActive, false, "OK: Should not block mixed active content!");
+
+ var loadedMixedDisplay =
+ !!(state & SpecialPowers.Ci.nsIWebProgressListener.STATE_LOADED_MIXED_DISPLAY_CONTENT);
+ is(loadedMixedDisplay, false, "OK: Should not load mixed display content!");
+
+ var blockedMixedDisplay =
+ !!(state & SpecialPowers.Ci.nsIWebProgressListener.STATE_BLOCKED_MIXED_DISPLAY_CONTENT);
+ is(blockedMixedDisplay, false, "OK: Should not block mixed display content!");
+
+ var newValue = "Verifying MCB does not trigger warning/error for an http page with https css that includes http image";
+ // eslint-disable-next-line no-unsanitized/property
+ document.getElementById("testDiv").innerHTML = newValue;
+ }
+</script>
+</head>
+<body onload="checkLoadStates()">
+ <div class="testDiv" id="testDiv">
+ Testing MCB does not trigger warning/error for an http page with https css that includes http image
+ </div>
+</body>
+</html>