commit cf480d4cec629836555a8ef75a1bc1feb6558180
parent 0f4e2b6d02a87b1389f1753ad340b0eb7c530157
Author: Sandra Qu <squiles@mozilla.com>
Date: Mon, 27 Oct 2025 15:23:18 +0000
Bug 1990920 Updated test for updated Firefox Desktop network error page appearance/content r=niklas
Differential Revision: https://phabricator.services.mozilla.com/D266228
Diffstat:
3 files changed, 607 insertions(+), 76 deletions(-)
diff --git a/browser/base/content/test/about/browser_aboutCertError.js b/browser/base/content/test/about/browser_aboutCertError.js
@@ -8,75 +8,112 @@
const GOOD_PAGE = "https://example.com/";
const GOOD_PAGE_2 = "https://example.org/";
const BAD_CERT = "https://expired.example.com/";
-const UNKNOWN_ISSUER = "https://self-signed.example.com ";
+const UNKNOWN_ISSUER = "https://self-signed.example.com";
const BAD_STS_CERT =
"https://badchain.include-subdomains.pinning.example.com:443";
const { TabStateFlusher } = ChromeUtils.importESModule(
"resource:///modules/sessionstore/TabStateFlusher.sys.mjs"
);
+// Security CertError Felt Privacy set to false & true
+// checked in one checkReturnToAboutHome to avoid duplicating code
add_task(async function checkReturnToAboutHome() {
info(
"Loading a bad cert page directly and making sure 'return to previous page' goes to about:home"
);
- for (let useFrame of [false, true]) {
- let tab = await openErrorPage(BAD_CERT, useFrame);
- let browser = tab.linkedBrowser;
- await SpecialPowers.spawn(browser, [], () => {
- content.document.notifyUserGestureActivation();
- });
+ for (let toggleFeltPrivacy of [
+ setSecurityCertErrorsFeltPrivacyToFalse,
+ setSecurityCertErrorsFeltPrivacyToTrue,
+ ]) {
+ await toggleFeltPrivacy();
+
+ for (let useFrame of [false, true]) {
+ let tab = await openErrorPage(BAD_CERT, useFrame);
+ let browser = tab.linkedBrowser;
+ await SpecialPowers.spawn(browser, [], () => {
+ content.document.notifyUserGestureActivation();
+ });
- is(browser.webNavigation.canGoBack, false, "!webNavigation.canGoBack");
- is(
- browser.webNavigation.canGoForward,
- false,
- "!webNavigation.canGoForward"
- );
+ is(browser.webNavigation.canGoBack, false, "!webNavigation.canGoBack");
+ is(
+ browser.webNavigation.canGoForward,
+ false,
+ "!webNavigation.canGoForward"
+ );
- // Populate the shistory entries manually, since it happens asynchronously
- // and the following tests will be too soon otherwise.
- await TabStateFlusher.flush(browser);
- let { entries } = JSON.parse(SessionStore.getTabState(tab));
- is(entries.length, 1, "there is one shistory entry");
+ // Populate the shistory entries manually, since it happens asynchronously
+ // and the following tests will be too soon otherwise.
+ await TabStateFlusher.flush(browser);
+ let { entries } = JSON.parse(SessionStore.getTabState(tab));
+ is(entries.length, 1, "there is one shistory entry");
- info("Clicking the go back button on about:certerror");
- let bc = browser.browsingContext;
- if (useFrame) {
- bc = bc.children[0];
- }
- let locationChangePromise = BrowserTestUtils.waitForLocationChange(
- gBrowser,
- "about:home"
- );
- await SpecialPowers.spawn(bc, [useFrame], async function (subFrame) {
- let returnButton = content.document.getElementById("returnButton");
- if (!subFrame) {
- if (!Services.focus.focusedElement == returnButton) {
- await ContentTaskUtils.waitForEvent(returnButton, "focus");
- }
- Assert.ok(true, "returnButton has focus");
+ info("Clicking the go back button on about:certerror");
+ let bc = browser.browsingContext;
+ if (useFrame) {
+ bc = bc.children[0];
}
- // Note that going back to about:newtab might cause a process flip, if
- // the browser is configured to run about:newtab in its own special
- // content process.
- returnButton.click();
- });
+ let locationChangePromise = BrowserTestUtils.waitForLocationChange(
+ gBrowser,
+ "about:home"
+ );
- await locationChangePromise;
+ if (Services.prefs.getBoolPref("security.certerrors.felt-privacy-v1")) {
+ info("Felt Privacy enabled - using net-error-card");
+
+ await SpecialPowers.spawn(bc, [useFrame], async function (subFrame) {
+ const netErrorCard =
+ content.document.querySelector("net-error-card").wrappedJSObject;
+ await netErrorCard.getUpdateComplete();
+ const returnButton = netErrorCard.returnButton;
+
+ if (!subFrame) {
+ if (!Services.focus.focusedElement == returnButton) {
+ await ContentTaskUtils.waitForEvent(returnButton, "focus");
+ }
+ Assert.ok(true, "returnButton has focus");
+ }
+ // Note that going back to about:newtab might cause a process flip, if
+ // the browser is configured to run about:newtab in its own special
+ // content process.
+ returnButton.scrollIntoView(true);
+ EventUtils.synthesizeMouseAtCenter(returnButton, {}, content);
+ });
+ } else {
+ info("Felt Privacy disabled - using aboutNetError");
+ await SpecialPowers.spawn(bc, [useFrame], async function (subFrame) {
+ let returnButton = content.document.getElementById("returnButton");
+ if (!subFrame) {
+ if (!Services.focus.focusedElement == returnButton) {
+ await ContentTaskUtils.waitForEvent(returnButton, "focus");
+ }
+ Assert.ok(true, "returnButton has focus");
+ }
+ // Note that going back to about:newtab might cause a process flip, if
+ // the browser is configured to run about:newtab in its own special
+ // content process.
+ returnButton.click();
+ });
+ }
- is(browser.webNavigation.canGoBack, true, "webNavigation.canGoBack");
- is(
- browser.webNavigation.canGoForward,
- false,
- "!webNavigation.canGoForward"
- );
- is(gBrowser.currentURI.spec, "about:home", "Went back");
+ await locationChangePromise;
- BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ is(browser.webNavigation.canGoBack, true, "webNavigation.canGoBack");
+ is(
+ browser.webNavigation.canGoForward,
+ false,
+ "!webNavigation.canGoForward"
+ );
+ is(gBrowser.currentURI.spec, "about:home", "Went back");
+
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ }
+ SpecialPowers.popPrefEnv();
}
});
-add_task(async function checkReturnToPreviousPage() {
+// Security CertError Felt Privacy set to false only
+add_task(async function checkReturnToPreviousPage_feltPrivacyToFalse() {
+ await setSecurityCertErrorsFeltPrivacyToFalse();
info(
"Loading a bad cert page and making sure 'return to previous page' goes back"
);
@@ -144,6 +181,7 @@ add_task(async function checkReturnToPreviousPage() {
}
});
+// Works for both Security CertError Felt Privacy set to true and false
// This checks that the appinfo.appBuildID starts with a date string,
// which is required for the misconfigured system time check.
add_task(async function checkAppBuildIDIsDate() {
@@ -152,12 +190,13 @@ add_task(async function checkAppBuildIDIsDate() {
let month = parseInt(appBuildID.substr(4, 2), 10);
let day = parseInt(appBuildID.substr(6, 2), 10);
- ok(year >= 2016 && year <= 2100, "appBuildID contains a valid year");
- ok(month >= 1 && month <= 12, "appBuildID contains a valid month");
- ok(day >= 1 && day <= 31, "appBuildID contains a valid day");
+ Assert.ok(year >= 2016 && year <= 2100, "appBuildID contains a valid year");
+ Assert.ok(month >= 1 && month <= 12, "appBuildID contains a valid month");
+ Assert.ok(day >= 1 && day <= 31, "appBuildID contains a valid day");
});
-add_task(async function checkAdvancedDetails() {
+add_task(async function checkAdvancedDetails_feltPrivacyToFalse() {
+ await setSecurityCertErrorsFeltPrivacyToFalse();
info(
"Loading a bad cert page and verifying the main error and advanced details section"
);
@@ -256,7 +295,8 @@ add_task(async function checkAdvancedDetails() {
}
});
-add_task(async function checkAdvancedDetailsForHSTS() {
+add_task(async function checkAdvancedDetailsForHSTS_feltPrivacyToFalse() {
+ await setSecurityCertErrorsFeltPrivacyToFalse();
info(
"Loading a bad STS cert page and verifying the advanced details section"
);
@@ -340,7 +380,8 @@ add_task(async function checkAdvancedDetailsForHSTS() {
}
});
-add_task(async function checkUnknownIssuerLearnMoreLink() {
+add_task(async function checkUnknownIssuerLearnMoreLink_feltPrivacyToFalse() {
+ await setSecurityCertErrorsFeltPrivacyToFalse();
info(
"Loading a cert error for self-signed pages and checking the correct link is shown"
);
@@ -363,7 +404,8 @@ add_task(async function checkUnknownIssuerLearnMoreLink() {
}
});
-add_task(async function checkViewCertificate() {
+add_task(async function checkViewCertificate_feltPrivacyToFalse() {
+ await setSecurityCertErrorsFeltPrivacyToFalse();
info("Loading a cert error and checking that the certificate can be shown.");
for (let useFrame of [true, false]) {
if (useFrame) {
@@ -385,7 +427,7 @@ add_task(async function checkViewCertificate() {
});
await loaded;
- let spec = gBrowser.selectedTab.linkedBrowser.documentURI.spec;
+ let spec = gBrowser.currentURI.spec;
Assert.ok(
spec.startsWith("about:certificate"),
"about:certificate is the new opened tab"
@@ -428,7 +470,8 @@ add_task(async function checkViewCertificate() {
}
});
-add_task(async function checkBadStsCertHeadline() {
+add_task(async function checkBadStsCertHeadline_feltPrivacyToFalse() {
+ await setSecurityCertErrorsFeltPrivacyToFalse();
info(
"Loading a bad sts cert error page and checking that the correct headline is shown"
);
@@ -453,7 +496,8 @@ add_task(async function checkBadStsCertHeadline() {
}
});
-add_task(async function checkSandboxedIframe() {
+add_task(async function checkSandboxedIframe_feltPrivacyToFalse() {
+ await setSecurityCertErrorsFeltPrivacyToFalse();
info(
"Loading a bad sts cert error in a sandboxed iframe and check that the correct headline is shown"
);
@@ -491,7 +535,8 @@ add_task(async function checkSandboxedIframe() {
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
-add_task(async function checkViewSource() {
+add_task(async function checkViewSource_feltPrivacyToFalse() {
+ await setSecurityCertErrorsFeltPrivacyToFalse();
info(
"Loading a bad sts cert error in a sandboxed iframe and check that the correct headline is shown"
);
@@ -578,8 +623,21 @@ add_task(async function testCertificateTransparency() {
});
for (let useFrame of [false, true]) {
- let tab = await openErrorPage(GOOD_PAGE, useFrame);
- let browser = tab.linkedBrowser;
+ let tab;
+ let browser;
+ if (useFrame) {
+ tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:blank"
+ );
+ browser = tab.linkedBrowser;
+
+ // injectErrorPageFrame is a helper from head.js for this purpose
+ await injectErrorPageFrame(tab, GOOD_PAGE, useFrame);
+ } else {
+ tab = await openErrorPage(GOOD_PAGE, useFrame);
+ browser = tab.linkedBrowser;
+ }
let bc = browser.browsingContext;
if (useFrame) {
@@ -587,18 +645,17 @@ add_task(async function testCertificateTransparency() {
}
let message = await SpecialPowers.spawn(bc, [], async function () {
- let doc = content.document;
-
+ const doc = content.document;
const shortDesc = doc.getElementById("errorShortDesc");
const sdArgs = JSON.parse(shortDesc.dataset.l10nArgs);
- is(
+ Assert.equal(
sdArgs.hostname,
"example.com",
"Should list hostname in error message."
);
-
- let advancedButton = doc.getElementById("advancedButton");
- advancedButton.click();
+ const advancedButton = doc.getElementById("advancedButton");
+ advancedButton.scrollIntoView(true);
+ EventUtils.synthesizeMouseAtCenter(advancedButton, {}, content);
// Wait until fluent sets the errorCode inner text.
let errorCode;
@@ -612,25 +669,28 @@ add_task(async function testCertificateTransparency() {
tagName: errorCode.tagName.toLowerCase(),
};
});
- is(
+
+ Assert.equal(
message.textContent,
"MOZILLA_PKIX_ERROR_INSUFFICIENT_CERTIFICATE_TRANSPARENCY",
"Correct error message found"
);
- is(message.tagName, "a", "Error message is a link");
+ Assert.equal(message.tagName, "a", "Error message is a link");
message = await SpecialPowers.spawn(bc, [], async function () {
- let doc = content.document;
- let errorCode = doc.getElementById("errorCode");
- errorCode.click();
- let text = doc.getElementById("certificateErrorText");
+ const doc = content.document;
+ const errorCode = doc.getElementById("errorCode");
+ errorCode.scrollIntoView(true);
+ EventUtils.synthesizeMouseAtCenter(errorCode, {}, content);
+ const text = doc.getElementById("certificateErrorText");
return {
text: text.textContent,
};
});
- ok(message.text.includes(GOOD_PAGE), "Correct URL found");
+ Assert.ok(message.text.includes(GOOD_PAGE), "Correct URL found");
BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ BrowserTestUtils.removeTab(tab);
}
await SpecialPowers.popPrefEnv();
@@ -638,3 +698,454 @@ add_task(async function testCertificateTransparency() {
// Certificate transparency can only be enforced for our test certificates in
// debug builds.
}).skip(!AppConstants.DEBUG);
+
+/**
+ * A reusable helper that runs assertions on a network error page.
+ * It encapsulates the SpecialPowers.spawn call to be CSP-compliant.
+ *
+ * @param {object} params - Parameters for the assertion.
+ * @param {string} params.expectedUrl - The URL to load and check.
+ * @param {string} params.expectedHostname - The expected hostname to assert in the error page.
+ * @param {string} params.expectedErrorCode - The expected error code to assert.
+ * @param {string} params.expectedInfo - Info string for logging.
+ * @param {string} params.expectedErrorMessage - Error message to assert in the error text.
+ * @param {boolean} params.expectedHpkp - HPKP value to assert in the error text.
+ */
+async function assertNetErrorPage({
+ expectedUrl,
+ expectedHostname,
+ expectedErrorCode,
+ expectedInfo,
+ expectedErrorMessage,
+ expectedHpkp,
+}) {
+ await setSecurityCertErrorsFeltPrivacyToTrue();
+ info(`${expectedInfo}`);
+
+ for (let useFrame of [false, true]) {
+ let tab = await openErrorPage(expectedUrl, useFrame);
+ let browser = tab.linkedBrowser;
+ let bc = browser.browsingContext;
+ if (useFrame) {
+ bc = bc.children[0];
+ }
+
+ const newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
+ const contentData = await SpecialPowers.spawn(
+ bc,
+ [expectedHostname, expectedErrorCode],
+ async function (hostname, errorCode) {
+ const netErrorCard =
+ content.document.querySelector("net-error-card").wrappedJSObject;
+ await netErrorCard.getUpdateComplete();
+
+ // Assert Error Card Basics
+ Assert.ok(
+ netErrorCard.certErrorBodyTitle,
+ "The error page title should exist."
+ );
+
+ const shortDesc = netErrorCard.certErrorIntro;
+ const shortDescArgs = JSON.parse(shortDesc.dataset.l10nArgs);
+ Assert.equal(
+ shortDescArgs.hostname,
+ hostname,
+ "Should list hostname in error message."
+ );
+
+ // Assert Advanced button
+ Assert.ok(
+ !netErrorCard.advancedContainer,
+ "The Advanced container should NOT be found in shadow DOM before click."
+ );
+ const advancedButton = netErrorCard.advancedButton;
+ Assert.ok(advancedButton, "The advanced button should exist.");
+ Assert.equal(
+ advancedButton.dataset.l10nId,
+ "fp-certerror-advanced-button",
+ "Button should have the 'advanced' l10n ID."
+ );
+
+ advancedButton.scrollIntoView(true);
+ EventUtils.synthesizeMouseAtCenter(advancedButton, {}, content);
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ netErrorCard.exceptionButton &&
+ !netErrorCard.exceptionButton.disabled,
+ "Wait for the exception button to be created."
+ );
+
+ Assert.ok(
+ !netErrorCard.exceptionButton.disabled,
+ "The exception button is now enabled."
+ );
+ Assert.equal(
+ advancedButton.dataset.l10nId,
+ "fp-certerror-hide-advanced-button",
+ "Button should have the 'hide-advanced' l10n ID."
+ );
+ Assert.ok(
+ netErrorCard.advancedShowing,
+ "Advanced showing attribute should be true"
+ );
+
+ // Assert Error Code
+ const certErrorCodeLink = netErrorCard.errorCode;
+ Assert.equal(
+ certErrorCodeLink.textContent,
+ `Error Code: ${errorCode}`,
+ "Error code text is as expected"
+ );
+ Assert.equal(
+ certErrorCodeLink.tagName.toLowerCase(),
+ "a",
+ "Error code is a link"
+ );
+
+ certErrorCodeLink.scrollIntoView(true);
+ EventUtils.synthesizeMouseAtCenter(certErrorCodeLink, {}, content);
+ await ContentTaskUtils.waitForMutationCondition(
+ netErrorCard,
+ { attributeFilter: ["certErrorDebugInfoShowing"] },
+ () => netErrorCard.certErrorDebugInfoShowing
+ );
+ Assert.ok(
+ netErrorCard.certErrorDebugInfoShowing,
+ "The 'certErrorDebugInfoShowing' boolean should be toggled (to true) after Advance button click on assertAdvancedButton."
+ );
+ Assert.ok(netErrorCard.certErrorText, "Error Code Detail should exist");
+
+ // Assert Site Certificate
+ info("Clicking the View Certificate button in advanced panel");
+ netErrorCard.viewCertificate.scrollIntoView(true);
+ EventUtils.synthesizeMouseAtCenter(
+ netErrorCard.viewCertificate,
+ {},
+ content
+ );
+
+ // Extract data needed by the parent process
+ const failedCertChain =
+ content.docShell.failedChannel.securityInfo.handshakeCertificates.map(
+ cert => cert.getBase64DERString()
+ );
+
+ return {
+ errorText: netErrorCard.certErrorText.textContent,
+ rawCertChain: failedCertChain,
+ };
+ }
+ );
+
+ Assert.ok(
+ contentData.errorText.includes(expectedHostname),
+ "Correct URL found"
+ );
+ Assert.ok(
+ contentData.errorText.includes(expectedErrorMessage),
+ "Correct error message exists"
+ );
+ Assert.ok(
+ contentData.errorText.includes("HTTP Strict Transport Security: false"),
+ "Correct HSTS value exists"
+ );
+ Assert.ok(
+ contentData.errorText.includes(
+ `HTTP Public Key Pinning: ${expectedHpkp}`
+ ),
+ "Correct HPKP value exists"
+ );
+
+ info("Loading the about:certificate page");
+ let newTab = await newTabPromise;
+
+ // Parent process checking if certificate viewer opened
+ const spec = gBrowser.currentURI.spec;
+ Assert.ok(
+ spec.startsWith("about:certificate"),
+ "about:certificate is the new opened tab"
+ );
+
+ await SpecialPowers.spawn(
+ gBrowser.selectedTab.linkedBrowser,
+ [expectedHostname],
+ async function (hostname) {
+ const doc = content.document;
+ const certificateSection = await ContentTaskUtils.waitForCondition(
+ () => {
+ // Content process checking if we're no longer on the error page
+ Assert.ok(
+ !doc.documentURI.startsWith("about:certerror"),
+ "We are now in a new tab with content including: about:certificate"
+ );
+ return doc.querySelector("certificate-section");
+ },
+ "Certificate section found"
+ );
+
+ const infoGroup =
+ certificateSection.shadowRoot.querySelector("info-group");
+ Assert.ok(infoGroup, "infoGroup found");
+
+ const items = infoGroup.shadowRoot.querySelectorAll("info-item");
+ const commonNameID = items[items.length - 1].shadowRoot
+ .querySelector("label")
+ .getAttribute("data-l10n-id");
+ Assert.equal(
+ commonNameID,
+ "certificate-viewer-common-name",
+ "The correct item was selected"
+ );
+
+ const commonNameValue =
+ items[items.length - 1].shadowRoot.querySelector(".info").textContent;
+
+ // Bug 1992278
+ // Structuring the logic this way avoids issue to be addressed
+ // in most cases the values match, "self-signed.example.com", "self-signed.example.com"
+ // in the case of pinning, the commonNameValue is shorter, "pinning-test.example.com",
+ // than the hostname "badchain.include-subdomains.pinning.example.com"
+ // as a temporary measure I have reversed the logic such that hostname includes commonNameValue
+ Assert.ok(
+ hostname.includes(commonNameValue),
+ "Error text should include the expected hostname"
+ );
+ }
+ );
+
+ BrowserTestUtils.removeTab(newTab);
+ BrowserTestUtils.removeTab(tab);
+ }
+}
+
+/**
+ * A reusable helper that runs assertions on a view-source network error page.
+ * It encapsulates the SpecialPowers.spawn call to be CSP-compliant.
+ *
+ * @param {object} params - Parameters for the assertion.
+ * @param {string} params.expectedHostname - The expected hostname to assert in the error page.
+ * @param {string} params.expectedUrl - The URL to load and check.
+ */
+async function assertViewSourceNetErrorPage({
+ expectedHostname,
+ expectedUrl,
+ expectedInfo,
+}) {
+ await setSecurityCertErrorsFeltPrivacyToTrue();
+ info(`${expectedInfo}`);
+
+ let tab = await openErrorPage(expectedUrl);
+ const browser = tab.linkedBrowser;
+ const loaded = BrowserTestUtils.browserLoaded(browser, false, expectedUrl);
+
+ await SpecialPowers.spawn(browser, [], async function () {
+ const netErrorCard =
+ content.document.querySelector("net-error-card").wrappedJSObject;
+ await netErrorCard.getUpdateComplete();
+ // Advanced button
+ const advancedButton = netErrorCard.advancedButton;
+ advancedButton.scrollIntoView(true);
+ EventUtils.synthesizeMouseAtCenter(advancedButton, {}, content);
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ netErrorCard.exceptionButton && !netErrorCard.exceptionButton.disabled,
+ "Wait for the exception button to be created."
+ );
+
+ info("Clicking the Proceed Risky button in advanced panel");
+ netErrorCard.exceptionButton.scrollIntoView(true);
+ EventUtils.synthesizeMouseAtCenter(
+ netErrorCard.exceptionButton,
+ {},
+ content
+ );
+ });
+
+ info("Loading the url after proceeding with risk");
+ await loaded;
+
+ // Clean up the cert override
+ const certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ certOverrideService.clearValidityOverride(expectedHostname, -1, {});
+
+ // To ensure the state is reset, reload and wait for the error page to return.
+ info("Reloading to ensure the certificate override was cleared.");
+ const errorPageLoaded = BrowserTestUtils.waitForErrorPage(browser);
+
+ BrowserCommands.reloadSkipCache();
+ await errorPageLoaded;
+
+ info("Override cleared and error page is shown again.");
+ BrowserTestUtils.removeTab(tab);
+}
+
+// Security CertError Felt Privacy set to true only
+add_task(async function checkReturnToPreviousPage_feltPrivacyToTrue() {
+ await setSecurityCertErrorsFeltPrivacyToTrue();
+ info(
+ "Loading a bad cert page and making sure 'return to previous page' goes back"
+ );
+ for (let useFrame of [false, true]) {
+ let tab;
+ let browser;
+ if (useFrame) {
+ tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, GOOD_PAGE);
+ browser = tab.linkedBrowser;
+ await SpecialPowers.spawn(browser, [], () => {
+ content.document.notifyUserGestureActivation();
+ });
+
+ BrowserTestUtils.startLoadingURIString(browser, GOOD_PAGE_2);
+ await BrowserTestUtils.browserLoaded(browser, false, GOOD_PAGE_2);
+ await injectErrorPageFrame(tab, BAD_CERT);
+ } else {
+ tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, GOOD_PAGE);
+ browser = gBrowser.selectedBrowser;
+ await SpecialPowers.spawn(browser, [], () => {
+ content.document.notifyUserGestureActivation();
+ });
+
+ info("Loading and waiting for the cert error");
+ const certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser);
+ BrowserTestUtils.startLoadingURIString(browser, BAD_CERT);
+ await certErrorLoaded;
+ }
+
+ Assert.ok(browser.webNavigation.canGoBack, "webNavigation.canGoBack");
+ Assert.ok(
+ !browser.webNavigation.canGoForward,
+ "!webNavigation.canGoForward"
+ );
+
+ // Populate the shistory entries manually, since it happens asynchronously
+ // and the following tests will be too soon otherwise.
+ await TabStateFlusher.flush(browser);
+ const { entries } = JSON.parse(SessionStore.getTabState(tab));
+ Assert.equal(entries.length, 2, "there are two history entries");
+
+ info("Clicking the go back button on about:certerror");
+ let bc = browser.browsingContext;
+ if (useFrame) {
+ bc = bc.children[0];
+ }
+
+ const pageShownPromise = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "pageshow",
+ true
+ );
+ await SpecialPowers.spawn(bc, [useFrame], async function () {
+ const netErrorCard =
+ content.document.querySelector("net-error-card").wrappedJSObject;
+ await netErrorCard.getUpdateComplete();
+ const returnButton = netErrorCard.returnButton;
+ returnButton.scrollIntoView(true);
+ EventUtils.synthesizeMouseAtCenter(returnButton, {}, content);
+ });
+ await pageShownPromise;
+
+ Assert.ok(!browser.webNavigation.canGoBack, "!webNavigation.canGoBack");
+ Assert.ok(browser.webNavigation.canGoForward, "webNavigation.canGoForward");
+ Assert.equal(gBrowser.currentURI.spec, GOOD_PAGE, "Went back");
+
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ }
+});
+
+add_task(async function checkAdvancedDetails_feltPrivacyToTrue() {
+ // Helper function does all the content-side checks.
+ await assertNetErrorPage({
+ expectedUrl: BAD_CERT,
+ expectedHostname: new URL(BAD_CERT).hostname,
+ expectedErrorCode: "SEC_ERROR_EXPIRED_CERTIFICATE",
+ expectedInfo:
+ "Loading a bad cert page and verifying the main error and advanced details section",
+ expectedErrorMessage: "Certificate has expired",
+ expectedHpkp: false,
+ });
+});
+
+add_task(async function checkAdvancedDetailsForHSTS_feltPrivacyToTrue() {
+ // Helper function does all the content-side checks.
+ await assertNetErrorPage({
+ expectedUrl: BAD_STS_CERT,
+ expectedHostname: new URL(BAD_STS_CERT).hostname,
+ expectedErrorCode: "SSL_ERROR_BAD_CERT_DOMAIN",
+ expectedInfo:
+ "Loading a bad STS cert page and verifying the advanced details section",
+ expectedErrorMessage:
+ "requested domain name does not match the server\u2019s certificate", // Expected error message
+ expectedHpkp: true,
+ });
+});
+
+add_task(async function checkUnknownIssuerDetails_feltPrivacyToTrue() {
+ // Helper function does all the content-side checks.
+ await assertNetErrorPage({
+ expectedUrl: UNKNOWN_ISSUER,
+ expectedHostname: new URL(UNKNOWN_ISSUER).hostname,
+ expectedErrorCode: "MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT",
+ expectedInfo:
+ "Loading a cert error for self-signed pages and checking the correct link is shown",
+ expectedErrorMessage:
+ "The certificate is not trusted because it is self-signed.",
+ expectedHpkp: false,
+ });
+});
+
+add_task(async function checkViewSource_feltPrivacyToTrue() {
+ await assertViewSourceNetErrorPage({
+ expectedHostname: new URL(BAD_CERT).hostname,
+ expectedUrl: "view-source:" + BAD_CERT,
+ expectedInfo:
+ "Loading a bad sts cert error in a sandboxed iframe and check that the correct headline is shown",
+ });
+});
+
+add_task(async function checkSandboxedIframe_feltPrivacyToTrue() {
+ await setSecurityCertErrorsFeltPrivacyToTrue();
+ info(
+ "Loading a bad sts cert error in a sandboxed iframe and check that the correct headline is shown"
+ );
+ let useFrame = true;
+ let sandboxed = true;
+ let tab = await openErrorPage(BAD_CERT, useFrame, sandboxed);
+ let browser = tab.linkedBrowser;
+
+ let bc = browser.browsingContext.children[0];
+ await SpecialPowers.spawn(bc, [], async function () {
+ const netErrorCard =
+ content.document.querySelector("net-error-card").wrappedJSObject;
+ await netErrorCard.getUpdateComplete();
+
+ // Assert Error Card Basics
+ Assert.ok(
+ netErrorCard.certErrorBodyTitle,
+ "The error page title should exist."
+ );
+ const advancedButton = netErrorCard.advancedButton;
+ advancedButton.scrollIntoView(true);
+ EventUtils.synthesizeMouseAtCenter(advancedButton, {}, content);
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ netErrorCard.exceptionButton && !netErrorCard.exceptionButton.disabled,
+ "Wait for the exception button to be created."
+ );
+
+ // Assert Error Code
+ const certErrorCodeLink = netErrorCard.errorCode;
+ Assert.equal(
+ certErrorCodeLink.textContent,
+ `Error Code: SEC_ERROR_EXPIRED_CERTIFICATE`,
+ "Error Code is as expected"
+ );
+ Assert.equal(
+ certErrorCodeLink.tagName.toLowerCase(),
+ "a",
+ "Error Code is a link"
+ );
+ });
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
diff --git a/browser/base/content/test/about/head.js b/browser/base/content/test/about/head.js
@@ -169,3 +169,12 @@ function isBookmarksToolbarVisible(win = window) {
let toolbar = win.document.getElementById("PersonalToolbar");
return !toolbar.collapsed;
}
+
+const setSecurityCertErrorsFeltPrivacyToTrue = async () =>
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.certerrors.felt-privacy-v1", true]],
+ });
+const setSecurityCertErrorsFeltPrivacyToFalse = async () =>
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.certerrors.felt-privacy-v1", false]],
+ });
diff --git a/toolkit/content/net-error-card.mjs b/toolkit/content/net-error-card.mjs
@@ -36,6 +36,12 @@ export class NetErrorCard extends MozLitElement {
errorCode: "#errorCode",
advancedContainer: ".advanced-container",
advancedButton: "#advanced-button",
+ certErrorIntro: "#certErrorIntro",
+ certErrorDebugInfo: "#certificateErrorDebugInformation",
+ certErrorText: "#certificateErrorText",
+ viewCertificate: "#viewCertificate",
+ certErrorBodyTitle: "#certErrorBodyTitle",
+ returnButton: "#returnButton",
};
static ERROR_CODES = new Set([
@@ -133,6 +139,7 @@ export class NetErrorCard extends MozLitElement {
case "SEC_ERROR_EXPIRED_CERTIFICATE":
case "MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT":
return html`<p
+ id="certErrorIntro"
data-l10n-id="fp-certerror-intro"
data-l10n-args='{"hostname": "${this.hostname}"}'
></p>`;
@@ -546,13 +553,17 @@ export class NetErrorCard extends MozLitElement {
<img src="chrome://global/skin/illustrations/security-error.svg" />
</div>
<div class="container">
- <h1 data-l10n-id="fp-certerror-body-title"></h1>
+ <h1
+ id="certErrorBodyTitle"
+ data-l10n-id="fp-certerror-body-title"
+ ></h1>
${this.introContentTemplate()}
<moz-button-group
><moz-button
type="primary"
data-l10n-id="fp-certerror-return-to-previous-page-recommended-button"
data-telemetry-id="return_button_adv"
+ id="returnButton"
@click=${this.handleGoBackClick}
></moz-button
><moz-button