tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 126b864d44986f1aaf357daa800d1389e616b70b
parent 2f8ce2d3d1e117ebda8d02d185565b981cfa53ce
Author: mailelucks <maile.lucks@gmail.com>
Date:   Mon,  1 Dec 2025 19:19:13 +0000

Bug 1992253 - Add NS_ERROR_OFFLINE support to net-error-card component - r=jaws,niklas,fluent-reviewers,bolsson

Differential Revision: https://phabricator.services.mozilla.com/D269671

Diffstat:
Mbrowser/base/content/test/about/browser_aboutNetError_internet_connection_offline.js | 44++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/content/aboutNetError.mjs | 24++++++++++++++----------
Mtoolkit/content/aboutNetErrorHelpers.mjs | 8++++++++
Mtoolkit/content/net-error-card.mjs | 110++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Mtoolkit/locales/en-US/toolkit/neterror/netError.ftl | 9+++++++--
5 files changed, 151 insertions(+), 44 deletions(-)

diff --git a/browser/base/content/test/about/browser_aboutNetError_internet_connection_offline.js b/browser/base/content/test/about/browser_aboutNetError_internet_connection_offline.js @@ -33,6 +33,44 @@ async function checkErrorForInvalidUriLoad(l10nId) { BrowserTestUtils.removeTab(gBrowser.selectedTab); } +async function checkErrorForInvalidUriLoad_feltPrivacyToTrue(l10nId) { + let browser; + let pageLoaded; + await setSecurityCertErrorsFeltPrivacyToTrue(true); + await BrowserTestUtils.openNewForegroundTab( + gBrowser, + () => { + gBrowser.selectedTab = BrowserTestUtils.addTab( + gBrowser, + "https://does-not-exist.test" + ); + browser = gBrowser.selectedBrowser; + pageLoaded = BrowserTestUtils.waitForErrorPage(browser); + }, + false + ); + await pageLoaded; + + await SpecialPowers.spawn(browser, [l10nId], async expectedl10nId => { + const doc = content.document; + const netErrorCard = doc.querySelector("net-error-card").wrappedJSObject; + await netErrorCard.getUpdateComplete(); + + Assert.ok( + doc.documentURI.startsWith("about:neterror"), + "Should be showing error page" + ); + + Assert.strictEqual( + netErrorCard.netErrorTitleText.dataset.l10nId, + expectedl10nId, + "Correct error page title is set" + ); + }); + BrowserTestUtils.removeTab(gBrowser.selectedTab); + await SpecialPowers.popPrefEnv(); +} + registerCleanupFunction(function () { Services.io.offline = false; Services.io.setConnectivityForTesting(true); @@ -41,10 +79,16 @@ registerCleanupFunction(function () { add_task(async function test_offline_mode() { Services.io.offline = true; await checkErrorForInvalidUriLoad("netOffline-title"); + await checkErrorForInvalidUriLoad_feltPrivacyToTrue( + "fp-neterror-offline-body-title" + ); }); add_task(async function test_internet_connection_offline() { Services.io.offline = false; Services.io.setConnectivityForTesting(false); await checkErrorForInvalidUriLoad("internet-connection-offline-title"); + await checkErrorForInvalidUriLoad_feltPrivacyToTrue( + "fp-neterror-offline-body-title" + ); }); diff --git a/toolkit/content/aboutNetError.mjs b/toolkit/content/aboutNetError.mjs @@ -16,6 +16,9 @@ import { getFailedCertificatesAsPEMString, recordSecurityUITelemetry, getCSSClass, + gNoConnectivity, + gOffline, + retryThis, } from "chrome://global/content/aboutNetErrorHelpers.mjs"; const formatter = new Intl.DateTimeFormat(); @@ -104,11 +107,6 @@ function getMitmName(failedCertInfo) { return failedCertInfo.issuerCommonName; } -function retryThis(buttonEl) { - RPMSendAsyncMessage("Browser:EnableOnlineMode"); - buttonEl.disabled = true; -} - function showPrefChangeContainer() { const panel = document.getElementById("prefChangeContainer"); panel.hidden = false; @@ -447,7 +445,6 @@ function initPage() { } const isTRROnlyFailure = gErrorCode == "dnsNotFound" && RPMIsTRROnlyFailure(); - let noConnectivity = gErrorCode == "dnsNotFound" && !RPMHasConnectivity(); const docTitle = document.querySelector("title"); const shortDesc = document.getElementById("errorShortDesc"); @@ -498,7 +495,7 @@ function initPage() { ); // We can handle the offline page separately. - if (noConnectivity) { + if (gNoConnectivity) { pageTitleId = "neterror-dns-not-found-title"; bodyTitleId = "internet-connection-offline-title"; } @@ -511,7 +508,7 @@ function initPage() { // The TRR errors may present options that direct users to settings only available on Firefox Desktop if (RPMIsFirefox()) { - if (isTRROnlyFailure && !noConnectivity) { + if (isTRROnlyFailure && !gNoConnectivity) { pageTitleId = "neterror-dns-not-found-title"; document.l10n.setAttributes(docTitle, pageTitleId); if (bodyTitle) { @@ -639,7 +636,7 @@ function initPage() { setFocus("#netErrorButtonContainer > .try-again"); if (longDesc) { - const parts = getNetErrorDescParts(noConnectivity); + const parts = getNetErrorDescParts(gNoConnectivity); setNetErrorMessageFromParts(longDesc, parts); } @@ -1469,8 +1466,15 @@ function shouldUseFeltPrivacyRefresh() { const errorInfo = gIsCertError ? document.getFailedCertSecurityInfo() : document.getNetErrorInfo(); + let errorCode = errorInfo.errorCodeString + ? errorInfo.errorCodeString + : gErrorCode; + + if (gOffline) { + errorCode = "NS_ERROR_OFFLINE"; + } - return NetErrorCard.ERROR_CODES.has(errorInfo.errorCodeString); + return NetErrorCard.ERROR_CODES.has(errorCode); } if (!shouldUseFeltPrivacyRefresh()) { diff --git a/toolkit/content/aboutNetErrorHelpers.mjs b/toolkit/content/aboutNetErrorHelpers.mjs @@ -27,6 +27,9 @@ export let searchParams = new URLSearchParams( export let gErrorCode = searchParams.get("e"); export let gIsCertError = gErrorCode == "nssBadCert"; export let gHasSts = gIsCertError && getCSSClass() === "badStsCert"; +export let gNoConnectivity = + gErrorCode == "dnsNotFound" && !RPMHasConnectivity(); +export let gOffline = gErrorCode === "netOffline" || gNoConnectivity; const HOST_NAME = getHostName(); export function isCaptive() { @@ -46,6 +49,11 @@ export function getHostName() { return ""; } +export function retryThis(buttonEl) { + RPMSendAsyncMessage("Browser:EnableOnlineMode"); + buttonEl.disabled = true; +} + export async function getFailedCertificatesAsPEMString() { let locationUrl = document.location.href; let failedCertInfo = document.getFailedCertSecurityInfo(); diff --git a/toolkit/content/net-error-card.mjs b/toolkit/content/net-error-card.mjs @@ -7,12 +7,15 @@ import { gHasSts, gIsCertError, + gErrorCode, isCaptive, getCSSClass, getHostName, getSubjectAltNames, getFailedCertificatesAsPEMString, recordSecurityUITelemetry, + gOffline, + retryThis, } from "chrome://global/content/aboutNetErrorHelpers.mjs"; import { html } from "chrome://global/content/vendor/lit.all.mjs"; import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; @@ -46,6 +49,7 @@ export class NetErrorCard extends MozLitElement { learnMoreLink: "#learnMoreLink", whatCanYouDo: "#whatCanYouDo", whyDangerous: "#fp-why-site-dangerous", + netErrorTitleText: "#neterror-title-text", }; static ERROR_CODES = new Set([ @@ -56,6 +60,7 @@ export class NetErrorCard extends MozLitElement { "SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE", "SSL_ERROR_NO_CYPHER_OVERLAP", "MOZILLA_PKIX_ERROR_INSUFFICIENT_CERTIFICATE_TRANSPARENCY", + "NS_ERROR_OFFLINE", ]); constructor() { @@ -77,11 +82,8 @@ export class NetErrorCard extends MozLitElement { } await Promise.all([ - this.getDomainMismatchNames(), - this.getCertificateErrorText(), - ]); - - await Promise.all([ + gErrorCode === "domain-mismatch" && this.getDomainMismatchNames(), + document.getFailedCertSecurityInfo && this.getCertificateErrorText(), this.domainMismatchNamesPromise, this.certificateErrorTextPromise, ]); @@ -161,9 +163,15 @@ export class NetErrorCard extends MozLitElement { } getErrorInfo() { - return gIsCertError + const errorInfo = gIsCertError ? document.getFailedCertSecurityInfo() : document.getNetErrorInfo(); + + if (gOffline) { + errorInfo.errorCodeString = "NS_ERROR_OFFLINE"; + } + + return errorInfo; } introContentTemplate() { @@ -192,6 +200,11 @@ export class NetErrorCard extends MozLitElement { data-l10n-id="fp-certerror-transparency-intro" data-l10n-args='{"hostname": "${this.hostname}"}' ></p>`; + case "NS_ERROR_OFFLINE": + return html`<p + data-l10n-id="fp-neterror-offline-intro" + data-l10n-args='{"hostname": "${this.hostname}"}' + ></p>`; } return null; @@ -393,7 +406,7 @@ export class NetErrorCard extends MozLitElement { </p>` : null} ${gIsCertError - ? html` <p> + ? html`<p> <a id="errorCode" data-l10n-id="fp-cert-error-code" @@ -488,6 +501,11 @@ export class NetErrorCard extends MozLitElement { ); } + handleTryAgain(e) { + this.handleTelemetryClick(e); + retryThis(e); + } + toggleAdvancedShowing(e) { if (e) { this.handleTelemetryClick(e); @@ -579,8 +597,11 @@ export class NetErrorCard extends MozLitElement { target = target.getRootNode().host; } let telemetryId = target.dataset.telemetryId; + const category = gIsCertError + ? "securityUiCerterror" + : "securityUiNeterror"; void recordSecurityUITelemetry( - "securityUiCerterror", + category, "click" + telemetryId .split("_") @@ -604,30 +625,55 @@ export class NetErrorCard extends MozLitElement { <img src="chrome://global/skin/illustrations/security-error.svg" /> </div> <div class="container"> - <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 - id="advanced-button" - data-l10n-id=${this.advancedShowing - ? "fp-certerror-hide-advanced-button" - : "fp-certerror-advanced-button"} - data-telemetry-id="advanced_button" - @click=${this.toggleAdvancedShowing} - ></moz-button - ></moz-button-group> - ${this.advancedContainerTemplate()} - ${this.certErrorDebugInfoTemplate()} + ${this.errorInfo.errorCodeString === "NS_ERROR_OFFLINE" + ? html`<h1 + id="neterror-title-text" + data-l10n-id="fp-neterror-offline-body-title" + ></h1> + ${this.introContentTemplate()} + <p> + <strong data-l10n-id="fp-certerror-what-can-you-do"></strong> + </p> + <p> + <span + data-l10n-id="fp-neterror-offline-what-can-you-do-body" + data-l10n-args='{"hostname": "${this.hostname}"}' + ></span> + </p> + <moz-button-group> + <moz-button + id="neterrorTryAgainButton" + class="try-again" + type="primary" + data-l10n-id="neterror-try-again-button" + data-telemetry-id="try_again_button" + @click=${this.handleTryAgain} + ></moz-button> + </moz-button-group>` + : html`<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 + id="advanced-button" + data-l10n-id=${this.advancedShowing + ? "fp-certerror-hide-advanced-button" + : "fp-certerror-advanced-button"} + data-telemetry-id="advanced_button" + @click=${this.toggleAdvancedShowing} + ></moz-button + ></moz-button-group> + ${this.advancedContainerTemplate()} + ${this.certErrorDebugInfoTemplate()}`} </div> </article>`; } diff --git a/toolkit/locales/en-US/toolkit/neterror/netError.ftl b/toolkit/locales/en-US/toolkit/neterror/netError.ftl @@ -189,12 +189,17 @@ certerror-coep-learn-more = Learn more about Cross Origin Embedder Policies (COE neterror-response-status-code = Error code: { $responsestatus } { $responsestatustext } ## Felt Privacy V1 Strings + +fp-neterror-offline-body-title = Looks like there’s a problem with your internet connection + ## Variables: ## $hostname (String) - Hostname of the website to which the user was trying to connect. fp-neterror-connection-intro = { -brand-short-name } can’t create a secure connection to the server at { $hostname }. +fp-neterror-offline-intro = { -brand-short-name } can’t connect to the server at <strong>{ $hostname }</strong> -# This string appears after the following string: "What makes the site look dangerous?" (fp-neterror-why-site-dangerous) +# This string appears after the following string: "What makes the site look dangerous?" (fp-certerror-why-site-dangerous) fp-neterror-cypher-overlap-why-dangerous-body = It looks like this site is using old software with known security issues. -# This string appears after the following string: "What can you do about it?" (fp-neterror-what-can-you-do) +# This string appears after the following string: "What can you do about it?" (fp-certerror-what-can-you-do) fp-neterror-cypher-overlap-what-can-you-do-body = Make sure you’re using the latest version of { -brand-short-name }. Go to Help > About { -brand-short-name } in the menu. If you’re using the latest { -brand-short-name }, the problem is most likely with the site itself. +fp-neterror-offline-what-can-you-do-body = Try connecting on a different device. Check your modem or router. Disconnect and reconnect to Wi-Fi.