Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
/* Any copyright is dedicated to the Public Domain.
"use strict";
const lazy = {};
XPCOMUtils.defineLazyServiceGetter(
lazy,
"gDNSOverride",
"@mozilla.org/network/native-dns-override;1",
Ci.nsINativeDNSResolverOverride
);
const DES_PREF = "security.ssl3.deprecated.rsa_des_ede3_sha";
// This includes all the cipher suite prefs we have.
function resetPrefs() {
Services.prefs.clearUserPref("security.tls.version.min");
Services.prefs.clearUserPref("security.tls.version.max");
Services.prefs.clearUserPref("security.tls.version.enable-deprecated");
Services.prefs.clearUserPref("browser.fixup.alternate.enabled");
Services.prefs.clearUserPref(DES_PREF);
}
add_task(async function resetToDefaultConfig() {
info(
"Change TLS config to cause page load to fail, check that reset button is shown and that it works"
);
// Set ourselves up for a TLS error.
Services.prefs.setIntPref("security.tls.version.min", 1); // TLS 1.0
Services.prefs.setIntPref("security.tls.version.max", 1);
let browser;
let pageLoaded;
await BrowserTestUtils.openNewForegroundTab(
gBrowser,
() => {
gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, TLS12_PAGE);
browser = gBrowser.selectedBrowser;
pageLoaded = BrowserTestUtils.waitForErrorPage(browser);
},
false
);
info("Loading and waiting for the net error");
await pageLoaded;
// Setup an observer for the target page.
const finalLoadComplete = BrowserTestUtils.browserLoaded(
browser,
false,
TLS12_PAGE
);
await SpecialPowers.spawn(browser, [], async function () {
const doc = content.document;
ok(
doc.documentURI.startsWith("about:neterror"),
"Should be showing error page"
);
const netErrorCard = await ContentTaskUtils.waitForCondition(
() => content.document.querySelector("net-error-card")?.wrappedJSObject
);
netErrorCard.advancedButton.scrollIntoView(true);
EventUtils.synthesizeMouseAtCenter(
netErrorCard.advancedButton,
{},
content
);
await ContentTaskUtils.waitForCondition(
() => ContentTaskUtils.isVisible(netErrorCard.prefResetButton),
"prefResetButton is visible"
);
if (!Services.focus.focusedElement == netErrorCard.prefResetButton) {
await ContentTaskUtils.waitForEvent(
netErrorCard.prefResetButton,
"focus"
);
}
Assert.ok(true, "prefResetButton has focus");
EventUtils.synthesizeMouseAtCenter(
netErrorCard.prefResetButton,
{},
content
);
});
info("Waiting for the page to load after the click");
await finalLoadComplete;
resetPrefs();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
add_task(async function checkLearnMoreLink() {
info("Load an unsupported TLS page and check for a learn more link");
// Set ourselves up for TLS error
Services.prefs.setIntPref("security.tls.version.min", 3);
Services.prefs.setIntPref("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;
const baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
await SpecialPowers.spawn(browser, [baseURL], async function (_baseURL) {
const doc = content.document;
ok(
doc.documentURI.startsWith("about:neterror"),
"Should be showing error page"
);
const netErrorCard = await ContentTaskUtils.waitForCondition(
() => content.document.querySelector("net-error-card")?.wrappedJSObject
);
netErrorCard.advancedButton.scrollIntoView(true);
EventUtils.synthesizeMouseAtCenter(
netErrorCard.advancedButton,
{},
content
);
const tlsVersionNotice = await ContentTaskUtils.waitForCondition(
() => netErrorCard.tlsNotice
);
ok(
ContentTaskUtils.isVisible(tlsVersionNotice),
"TLS version notice is visible"
);
const learnMoreLink = netErrorCard.learnMoreLink;
ok(ContentTaskUtils.isVisible(learnMoreLink), "Learn More link is visible");
is(learnMoreLink.getAttribute("href"), _baseURL + "connection-not-secure");
const titleEl = netErrorCard.errorTitle;
const actualDataL10nID = titleEl.getAttribute("data-l10n-id");
is(
actualDataL10nID,
"nssFailure2-title",
"Correct error page title is set"
);
const errorCodeEl = netErrorCard.errorIntro.children[0];
is(
errorCodeEl.getAttribute("data-l10n-id"),
"cert-error-ssl-connection-error",
"Correct error code is set"
);
});
resetPrefs();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
// When a user tries going to a host without a suffix
// and the term doesn't match a host and we are able to suggest a
// valid correction, the page should show the correction.
add_task(async function checkDomainCorrectionReplacesLearnMoreLink() {
await SpecialPowers.pushPrefEnv({
set: [["browser.fixup.alternate.enabled", false]],
});
lazy.gDNSOverride.addIPOverride("www.example.com", "::1");
info("Try loading a URI that should result in an error page");
BrowserTestUtils.openNewForegroundTab(
gBrowser,
// eslint-disable-next-line sdl/no-insecure-url
false
);
info("Loading and waiting for the net error");
let browser = gBrowser.selectedBrowser;
let pageLoaded = BrowserTestUtils.waitForErrorPage(browser);
await pageLoaded;
const baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
await SpecialPowers.spawn(browser, [baseURL], async function (_baseURL) {
const doc = content.document;
ok(
doc.documentURI.startsWith("about:neterror"),
"Should be showing error page"
);
const netErrorCard = await ContentTaskUtils.waitForCondition(
() => doc.querySelector("net-error-card")?.wrappedJSObject
);
await netErrorCard.getUpdateComplete();
const errorIntro = netErrorCard.errorIntro;
ok(ContentTaskUtils.isVisible(errorIntro), "Error intro text is visible");
let suggestionLink;
await ContentTaskUtils.waitForCondition(() => {
suggestionLink = netErrorCard.dnsSuggestion?.querySelector("a");
return (
suggestionLink?.textContent != "" &&
suggestionLink?.getAttribute("href") ===
);
}, "www suggestion appears in intro text with correct link");
is(
suggestionLink.getAttribute("href"),
"Suggestion link points to www variant"
);
const learnMoreLink = netErrorCard.learnMoreLink;
ok(learnMoreLink, "Learn more link is present");
is(
learnMoreLink.getAttribute("href"),
_baseURL + "server-not-found-connection-problem",
"Learn more link still points to SUMO page"
);
});
lazy.gDNSOverride.clearHostOverride("www.example.com");
resetPrefs();
// This page records an alternate_host_suggested impression in its content
// process. Flush while the tab is still open: a closing tab's process drops
// still buffered reaches the parent only when the process finally exits,
// which on a slow machine is after the next task has cleared FOG.
await Services.fog.testFlushAllChildren();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
// Impression and click telemetry for the alternate-host suggestion
// hand-built span, so a suggestion that renders without its click listener
// attached fails here.
add_task(async function checkDomainCorrectionTelemetry() {
// Drain before clearing, so data still buffered in a live content process
// does not arrive with our own flush and double-count. The task above also
// has to flush before it closes its tab, which this cannot do for it.
await Services.fog.testFlushAllChildren();
Services.fog.testResetFOG();
await SpecialPowers.pushPrefEnv({
set: [["browser.fixup.alternate.enabled", false]],
});
lazy.gDNSOverride.addIPOverride("www.example.com", "::1");
BrowserTestUtils.openNewForegroundTab(
gBrowser,
// eslint-disable-next-line sdl/no-insecure-url
false
);
let browser = gBrowser.selectedBrowser;
await BrowserTestUtils.waitForErrorPage(browser);
await waitForSettledNetErrorCard(browser);
const suggestionCount = await SpecialPowers.spawn(browser, [], async () => {
const netErrorCard =
content.document.querySelector("net-error-card").wrappedJSObject;
// The suggestion is injected by NetErrorChild off the back of its own async
// DNS lookup, which is independent of the card's render, so it still needs
// a wait of its own after the card has settled.
await ContentTaskUtils.waitForCondition(
() => netErrorCard.dnsSuggestion?.querySelector("a"),
"The suggestion link is injected"
);
return netErrorCard.shadowRoot.querySelectorAll("#dns-suggestion").length;
});
await Services.fog.testFlushAllChildren();
// Asserted separately from the counter so that a page which really did
// render two suggestions is not mistaken for a telemetry bug.
is(suggestionCount, 1, "The page holds a single suggestion");
is(
Glean.securityUiNeterror.alternateHostSuggested.testGetValue(),
1,
"The suggestion impression is recorded once"
);
is(
Glean.securityUiNeterror.clickDnsSuggestionLink.testGetValue(),
null,
"No click is recorded before the user clicks"
);
// Cancel the navigation so the click is measured without leaving the page.
await SpecialPowers.spawn(browser, [], async () => {
const doc = content.document;
doc.addEventListener("click", e => e.preventDefault(), { capture: true });
doc
.querySelector("net-error-card")
.wrappedJSObject.dnsSuggestion.querySelector("a")
.click();
});
await Services.fog.testFlushAllChildren();
const events = Glean.securityUiNeterror.clickDnsSuggestionLink.testGetValue();
is(events?.length, 1, "The suggestion click is recorded once");
is(String(events[0].extra.is_frame), "false", "is_frame recorded");
ok(
!JSON.stringify(events[0].extra).includes("example"),
`No host reaches the event extras (got ${JSON.stringify(events[0].extra)})`
);
is(
Glean.securityUiNeterror.alternateHostSuggested.testGetValue(),
1,
"Clicking does not add a second impression"
);
lazy.gDNSOverride.clearHostOverride("www.example.com");
resetPrefs();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
// When a user tries to access a non-existent domain and no domain
// suggestion is available, the learn more link should point to the
// SUMO support page for DNS troubleshooting.
add_task(async function checkDnsNotFoundLearnMoreLink() {
info("Load a non-existent domain and check the learn more link");
BrowserTestUtils.openNewForegroundTab(
gBrowser,
// eslint-disable-next-line sdl/no-insecure-url
false
);
let browser = gBrowser.selectedBrowser;
let pageLoaded = BrowserTestUtils.waitForErrorPage(browser);
await pageLoaded;
const baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
await SpecialPowers.spawn(browser, [baseURL], async function (_baseURL) {
const doc = content.document;
const netErrorCard = await ContentTaskUtils.waitForCondition(
() => doc.querySelector("net-error-card")?.wrappedJSObject
);
let learnMoreLink;
await ContentTaskUtils.waitForCondition(() => {
learnMoreLink = netErrorCard.learnMoreLink;
return learnMoreLink && learnMoreLink.textContent != "";
}, "Learn more link has been set");
ok(ContentTaskUtils.isVisible(learnMoreLink), "Learn More link is visible");
is(
learnMoreLink.getAttribute("href"),
_baseURL + "server-not-found-connection-problem",
"Link points to SUMO DNS troubleshooting page"
);
});
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
// Test that ciphersuites that use 3DES (namely, TLS_RSA_WITH_3DES_EDE_CBC_SHA)
// can only be enabled when deprecated TLS is enabled.
add_task(async function onlyAllow3DESWithDeprecatedTLS() {
// By default, connecting to a server that only uses 3DES should fail.
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:blank" },
async browser => {
BrowserTestUtils.startLoadingURIString(browser, TRIPLEDES_PAGE);
await BrowserTestUtils.waitForErrorPage(browser);
}
);
// Enabling deprecated TLS should also enable 3DES.
Services.prefs.setBoolPref("security.tls.version.enable-deprecated", true);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:blank" },
async browser => {
BrowserTestUtils.startLoadingURIString(browser, TRIPLEDES_PAGE);
await BrowserTestUtils.browserLoaded(browser, false, TRIPLEDES_PAGE);
}
);
for (let feltPrivacy of [true, false]) {
// 3DES can be disabled separately.
Services.prefs.setBoolPref(DES_PREF, false);
await SpecialPowers.pushPrefEnv({
set: [["security.certerrors.felt-privacy-v1", feltPrivacy]],
});
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:blank" },
async browser => {
BrowserTestUtils.startLoadingURIString(browser, TRIPLEDES_PAGE);
await BrowserTestUtils.waitForErrorPage(browser);
let prefWasReset = TestUtils.waitForPrefChange(DES_PREF);
if (feltPrivacy) {
await SpecialPowers.spawn(browser, [], async function () {
const doc = content.document;
const netErrorCard =
doc.querySelector("net-error-card")?.wrappedJSObject;
Assert.ok(netErrorCard, "netErrorCard is rendered.");
netErrorCard.advancedButton.scrollIntoView();
EventUtils.synthesizeMouseAtCenter(
netErrorCard.advancedButton,
{},
content
);
await ContentTaskUtils.waitForCondition(
() => ContentTaskUtils.isVisible(netErrorCard.advancedContainer),
"Advanced container is visible"
);
const prefResetButton = netErrorCard.prefResetButton;
Assert.ok(prefResetButton, "prefResetButton exists in the DOM.");
netErrorCard.prefResetButton.scrollIntoView();
await ContentTaskUtils.waitForCondition(
() => ContentTaskUtils.isVisible(netErrorCard.prefResetButton),
"Pref reset button is visible"
);
ok(
ContentTaskUtils.isVisible(prefResetButton),
"prefResetButton is visible"
);
prefResetButton.click();
});
} else {
await SpecialPowers.spawn(browser, [], async function () {
const doc = content.document;
const prefResetButton = doc.getElementById("prefResetButton");
Assert.ok(prefResetButton, "prefResetButton exists in the DOM.");
await ContentTaskUtils.waitForCondition(
() => ContentTaskUtils.isVisible(prefResetButton),
"Pref reset button is visible"
);
ok(
ContentTaskUtils.isVisible(prefResetButton),
"prefResetButton is visible"
);
prefResetButton.click();
});
}
await prefWasReset;
}
);
}
resetPrefs();
});
add_task(async function test_tryAgainButtonAutofocus() {
Services.io.offline = true;
registerCleanupFunction(() => {
Services.io.offline = false;
});
let proxyPrefValue = SpecialPowers.getIntPref("network.proxy.type");
await SpecialPowers.pushPrefEnv({
set: [
["network.proxy.type", 0],
["browser.cache.disk.enable", false],
["browser.cache.memory.enable", false],
["security.certerrors.felt-privacy-v1", true],
],
});
await BrowserTestUtils.withNewTab("about:blank", async function (browser) {
let netErrorLoaded = BrowserTestUtils.waitForErrorPage(browser);
// eslint-disable-next-line sdl/no-insecure-url
await netErrorLoaded;
await SpecialPowers.pushPrefEnv({
set: [["network.proxy.type", proxyPrefValue]],
});
await SpecialPowers.spawn(browser, [], async function () {
const netErrorCard =
content.document.querySelector("net-error-card").wrappedJSObject;
await netErrorCard.getUpdateComplete();
const tryAgainButton = netErrorCard.tryAgainButton;
Assert.ok(tryAgainButton, "tryAgainButton exists");
await tryAgainButton.updateComplete;
Assert.equal(
netErrorCard.renderRoot.activeElement,
tryAgainButton,
"tryAgainButton has focus"
);
});
});
await SpecialPowers.popPrefEnv();
});