mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			1570 lines
		
	
	
	
		
			50 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1570 lines
		
	
	
	
		
			50 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* 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/. */
 | 
						|
 | 
						|
/* eslint-env mozilla/remote-page */
 | 
						|
/* eslint-disable import/no-unassigned-import */
 | 
						|
 | 
						|
import {
 | 
						|
  parse,
 | 
						|
  pemToDER,
 | 
						|
} from "chrome://global/content/certviewer/certDecoder.mjs";
 | 
						|
 | 
						|
const formatter = new Intl.DateTimeFormat();
 | 
						|
 | 
						|
const HOST_NAME = getHostName();
 | 
						|
 | 
						|
function getHostName() {
 | 
						|
  try {
 | 
						|
    return new URL(RPMGetInnerMostURI(document.location.href)).hostname;
 | 
						|
  } catch (error) {
 | 
						|
    console.error("Could not parse URL", error);
 | 
						|
  }
 | 
						|
  return "";
 | 
						|
}
 | 
						|
 | 
						|
// Used to check if we have a specific localized message for an error.
 | 
						|
const KNOWN_ERROR_TITLE_IDS = new Set([
 | 
						|
  // Error titles:
 | 
						|
  "connectionFailure-title",
 | 
						|
  "deniedPortAccess-title",
 | 
						|
  "dnsNotFound-title",
 | 
						|
  "dns-not-found-trr-only-title2",
 | 
						|
  "fileNotFound-title",
 | 
						|
  "fileAccessDenied-title",
 | 
						|
  "generic-title",
 | 
						|
  "captivePortal-title",
 | 
						|
  "malformedURI-title",
 | 
						|
  "netInterrupt-title",
 | 
						|
  "notCached-title",
 | 
						|
  "netOffline-title",
 | 
						|
  "contentEncodingError-title",
 | 
						|
  "unsafeContentType-title",
 | 
						|
  "netReset-title",
 | 
						|
  "netTimeout-title",
 | 
						|
  "unknownProtocolFound-title",
 | 
						|
  "proxyConnectFailure-title",
 | 
						|
  "proxyResolveFailure-title",
 | 
						|
  "redirectLoop-title",
 | 
						|
  "unknownSocketType-title",
 | 
						|
  "nssFailure2-title",
 | 
						|
  "csp-xfo-error-title",
 | 
						|
  "corruptedContentError-title",
 | 
						|
  "sslv3Used-title",
 | 
						|
  "inadequateSecurityError-title",
 | 
						|
  "blockedByPolicy-title",
 | 
						|
  "clockSkewError-title",
 | 
						|
  "networkProtocolError-title",
 | 
						|
  "nssBadCert-title",
 | 
						|
  "nssBadCert-sts-title",
 | 
						|
  "certerror-mitm-title",
 | 
						|
]);
 | 
						|
 | 
						|
/* The error message IDs from nsserror.ftl get processed into
 | 
						|
 * aboutNetErrorCodes.js which is loaded before we are: */
 | 
						|
/* global KNOWN_ERROR_MESSAGE_IDS */
 | 
						|
const ERROR_MESSAGES_FTL = "toolkit/neterror/nsserrors.ftl";
 | 
						|
 | 
						|
// The following parameters are parsed from the error URL:
 | 
						|
//   e - the error code
 | 
						|
//   s - custom CSS class to allow alternate styling/favicons
 | 
						|
//   d - error description
 | 
						|
//   captive - "true" to indicate we're behind a captive portal.
 | 
						|
//             Any other value is ignored.
 | 
						|
 | 
						|
// Note that this file uses document.documentURI to get
 | 
						|
// the URL (with the format from above). This is because
 | 
						|
// document.location.href gets the current URI off the docshell,
 | 
						|
// which is the URL displayed in the location bar, i.e.
 | 
						|
// the URI that the user attempted to load.
 | 
						|
 | 
						|
let searchParams = new URLSearchParams(document.documentURI.split("?")[1]);
 | 
						|
 | 
						|
let gErrorCode = searchParams.get("e");
 | 
						|
let gIsCertError = gErrorCode == "nssBadCert";
 | 
						|
let gHasSts = gIsCertError && getCSSClass() === "badStsCert";
 | 
						|
 | 
						|
// If the location of the favicon changes, FAVICON_CERTERRORPAGE_URL and/or
 | 
						|
// FAVICON_ERRORPAGE_URL in toolkit/components/places/nsFaviconService.idl
 | 
						|
// should also be updated.
 | 
						|
document.getElementById("favicon").href =
 | 
						|
  gIsCertError || gErrorCode == "nssFailure2"
 | 
						|
    ? "chrome://global/skin/icons/warning.svg"
 | 
						|
    : "chrome://global/skin/icons/info.svg";
 | 
						|
 | 
						|
function getCSSClass() {
 | 
						|
  return searchParams.get("s");
 | 
						|
}
 | 
						|
 | 
						|
function getDescription() {
 | 
						|
  return searchParams.get("d");
 | 
						|
}
 | 
						|
 | 
						|
function isCaptive() {
 | 
						|
  return searchParams.get("captive") == "true";
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * We don't actually know what the MitM is called (since we don't
 | 
						|
 * maintain a list), so we'll try and display the common name of the
 | 
						|
 * root issuer to the user. In the worst case they are as clueless as
 | 
						|
 * before, in the best case this gives them an actionable hint.
 | 
						|
 * This may be revised in the future.
 | 
						|
 */
 | 
						|
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;
 | 
						|
  document.getElementById("netErrorButtonContainer").hidden = true;
 | 
						|
  document
 | 
						|
    .getElementById("prefResetButton")
 | 
						|
    .addEventListener("click", function resetPreferences() {
 | 
						|
      RPMSendAsyncMessage("Browser:ResetSSLPreferences");
 | 
						|
    });
 | 
						|
  setFocus("#prefResetButton", "beforeend");
 | 
						|
}
 | 
						|
 | 
						|
function toggleCertErrorDebugInfoVisibility(shouldShow) {
 | 
						|
  let debugInfo = document.getElementById("certificateErrorDebugInformation");
 | 
						|
  let copyButton = document.getElementById("copyToClipboardTop");
 | 
						|
 | 
						|
  if (shouldShow === undefined) {
 | 
						|
    shouldShow = debugInfo.hidden;
 | 
						|
  }
 | 
						|
  debugInfo.hidden = !shouldShow;
 | 
						|
  if (shouldShow) {
 | 
						|
    copyButton.scrollIntoView({ block: "start", behavior: "smooth" });
 | 
						|
    copyButton.focus();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
function setupAdvancedButton() {
 | 
						|
  // Get the hostname and add it to the panel
 | 
						|
  var panel = document.getElementById("badCertAdvancedPanel");
 | 
						|
 | 
						|
  // Register click handler for the weakCryptoAdvancedPanel
 | 
						|
  document
 | 
						|
    .getElementById("advancedButton")
 | 
						|
    .addEventListener("click", togglePanelVisibility);
 | 
						|
 | 
						|
  function togglePanelVisibility() {
 | 
						|
    if (panel.hidden) {
 | 
						|
      // Reveal
 | 
						|
      revealAdvancedPanelSlowlyAsync();
 | 
						|
 | 
						|
      // send event to trigger telemetry ping
 | 
						|
      document.dispatchEvent(
 | 
						|
        new CustomEvent("AboutNetErrorUIExpanded", { bubbles: true })
 | 
						|
      );
 | 
						|
    } else {
 | 
						|
      // Hide
 | 
						|
      panel.hidden = true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (getCSSClass() == "expertBadCert") {
 | 
						|
    revealAdvancedPanelSlowlyAsync();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
async function revealAdvancedPanelSlowlyAsync() {
 | 
						|
  const badCertAdvancedPanel = document.getElementById("badCertAdvancedPanel");
 | 
						|
  const exceptionDialogButton = document.getElementById(
 | 
						|
    "exceptionDialogButton"
 | 
						|
  );
 | 
						|
 | 
						|
  // Toggling the advanced panel must ensure that the debugging
 | 
						|
  // information panel is hidden as well, since it's opened by the
 | 
						|
  // error code link in the advanced panel.
 | 
						|
  toggleCertErrorDebugInfoVisibility(false);
 | 
						|
 | 
						|
  // Reveal, but disabled (and grayed-out) for 3.0s.
 | 
						|
  badCertAdvancedPanel.hidden = false;
 | 
						|
  exceptionDialogButton.disabled = true;
 | 
						|
 | 
						|
  // -
 | 
						|
 | 
						|
  if (exceptionDialogButton.resetReveal) {
 | 
						|
    exceptionDialogButton.resetReveal(); // Reset if previous is pending.
 | 
						|
  }
 | 
						|
  let wasReset = false;
 | 
						|
  exceptionDialogButton.resetReveal = () => {
 | 
						|
    wasReset = true;
 | 
						|
  };
 | 
						|
 | 
						|
  // Wait for 10 frames to ensure that the warning text is rendered
 | 
						|
  // and gets all the way to the screen for the user to read it.
 | 
						|
  // This is only ~0.160s at 60Hz, so it's not too much extra time that we're
 | 
						|
  // taking to ensure that we're caught up with rendering, on top of the
 | 
						|
  // (by default) whole second(s) we're going to wait based on the
 | 
						|
  // security.dialog_enable_delay pref.
 | 
						|
  // The catching-up to rendering is the important part, not the
 | 
						|
  // N-frame-delay here.
 | 
						|
  for (let i = 0; i < 10; i++) {
 | 
						|
    await new Promise(requestAnimationFrame);
 | 
						|
  }
 | 
						|
 | 
						|
  // Wait another Nms (default: 1000) for the user to be very sure. (Sorry speed readers!)
 | 
						|
  const securityDelayMs = RPMGetIntPref("security.dialog_enable_delay", 1000);
 | 
						|
  await new Promise(go => setTimeout(go, securityDelayMs));
 | 
						|
 | 
						|
  if (wasReset) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // Enable and un-gray-out.
 | 
						|
  exceptionDialogButton.disabled = false;
 | 
						|
}
 | 
						|
 | 
						|
function disallowCertOverridesIfNeeded() {
 | 
						|
  // Disallow overrides if this is a Strict-Transport-Security
 | 
						|
  // host and the cert is bad (STS Spec section 7.3) or if the
 | 
						|
  // certerror is in a frame (bug 633691).
 | 
						|
  if (gHasSts || window != top) {
 | 
						|
    document.getElementById("exceptionDialogButton").hidden = true;
 | 
						|
  }
 | 
						|
  if (gHasSts) {
 | 
						|
    const stsExplanation = document.getElementById("badStsCertExplanation");
 | 
						|
    document.l10n.setAttributes(
 | 
						|
      stsExplanation,
 | 
						|
      "certerror-what-should-i-do-bad-sts-cert-explanation",
 | 
						|
      { hostname: HOST_NAME }
 | 
						|
    );
 | 
						|
    stsExplanation.hidden = false;
 | 
						|
 | 
						|
    document.l10n.setAttributes(
 | 
						|
      document.getElementById("returnButton"),
 | 
						|
      "neterror-return-to-previous-page-button"
 | 
						|
    );
 | 
						|
    document.l10n.setAttributes(
 | 
						|
      document.getElementById("advancedPanelReturnButton"),
 | 
						|
      "neterror-return-to-previous-page-button"
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
function recordTRREventTelemetry(
 | 
						|
  warningPageType,
 | 
						|
  trrMode,
 | 
						|
  trrDomain,
 | 
						|
  skipReason
 | 
						|
) {
 | 
						|
  RPMRecordTelemetryEvent(
 | 
						|
    "security.doh.neterror",
 | 
						|
    "load",
 | 
						|
    "dohwarning",
 | 
						|
    warningPageType,
 | 
						|
    {
 | 
						|
      mode: trrMode,
 | 
						|
      provider_key: trrDomain,
 | 
						|
      skip_reason: skipReason,
 | 
						|
    }
 | 
						|
  );
 | 
						|
 | 
						|
  const netErrorButtonDiv = document.getElementById("netErrorButtonContainer");
 | 
						|
  const buttons = netErrorButtonDiv.querySelectorAll("button");
 | 
						|
  for (let b of buttons) {
 | 
						|
    b.addEventListener("click", function (e) {
 | 
						|
      let target = e.originalTarget;
 | 
						|
      let telemetryId = target.dataset.telemetryId;
 | 
						|
      RPMRecordTelemetryEvent(
 | 
						|
        "security.doh.neterror",
 | 
						|
        "click",
 | 
						|
        telemetryId,
 | 
						|
        warningPageType,
 | 
						|
        {
 | 
						|
          mode: trrMode,
 | 
						|
          provider_key: trrDomain,
 | 
						|
          skip_reason: skipReason,
 | 
						|
        }
 | 
						|
      );
 | 
						|
    });
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
function initPage() {
 | 
						|
  // We show an offline support page in case of a system-wide error,
 | 
						|
  // when a user cannot connect to the internet and access the SUMO website.
 | 
						|
  // For example, clock error, which causes certerrors across the web or
 | 
						|
  // a security software conflict where the user is unable to connect
 | 
						|
  // to the internet.
 | 
						|
  // The URL that prompts us to show an offline support page should have the following
 | 
						|
  // format: "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/supportPageSlug",
 | 
						|
  // so we can extract the support page slug.
 | 
						|
  let baseURL = RPMGetFormatURLPref("app.support.baseURL");
 | 
						|
  if (document.location.href.startsWith(baseURL)) {
 | 
						|
    let supportPageSlug = document.location.pathname.split("/").pop();
 | 
						|
    RPMSendAsyncMessage("DisplayOfflineSupportPage", {
 | 
						|
      supportPageSlug,
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  const className = getCSSClass();
 | 
						|
  if (className) {
 | 
						|
    document.body.classList.add(className);
 | 
						|
  }
 | 
						|
 | 
						|
  const isTRROnlyFailure = gErrorCode == "dnsNotFound" && RPMIsTRROnlyFailure();
 | 
						|
 | 
						|
  let isNativeFallbackWarning = false;
 | 
						|
  if (RPMGetBoolPref("network.trr.display_fallback_warning")) {
 | 
						|
    isNativeFallbackWarning =
 | 
						|
      gErrorCode == "dnsNotFound" && RPMIsNativeFallbackFailure();
 | 
						|
  }
 | 
						|
 | 
						|
  const docTitle = document.querySelector("title");
 | 
						|
  const bodyTitle = document.querySelector(".title-text");
 | 
						|
  const shortDesc = document.getElementById("errorShortDesc");
 | 
						|
 | 
						|
  if (gIsCertError) {
 | 
						|
    const isStsError = window !== window.top || gHasSts;
 | 
						|
    const errArgs = { hostname: HOST_NAME };
 | 
						|
    if (isCaptive()) {
 | 
						|
      document.l10n.setAttributes(
 | 
						|
        docTitle,
 | 
						|
        "neterror-captive-portal-page-title"
 | 
						|
      );
 | 
						|
      document.l10n.setAttributes(bodyTitle, "captivePortal-title");
 | 
						|
      document.l10n.setAttributes(
 | 
						|
        shortDesc,
 | 
						|
        "neterror-captive-portal",
 | 
						|
        errArgs
 | 
						|
      );
 | 
						|
      initPageCaptivePortal();
 | 
						|
    } else {
 | 
						|
      if (isStsError) {
 | 
						|
        document.l10n.setAttributes(docTitle, "certerror-sts-page-title");
 | 
						|
        document.l10n.setAttributes(bodyTitle, "nssBadCert-sts-title");
 | 
						|
        document.l10n.setAttributes(shortDesc, "certerror-sts-intro", errArgs);
 | 
						|
      } else {
 | 
						|
        document.l10n.setAttributes(docTitle, "certerror-page-title");
 | 
						|
        document.l10n.setAttributes(bodyTitle, "nssBadCert-title");
 | 
						|
        document.l10n.setAttributes(shortDesc, "certerror-intro", errArgs);
 | 
						|
      }
 | 
						|
      initPageCertError();
 | 
						|
    }
 | 
						|
 | 
						|
    initCertErrorPageActions();
 | 
						|
    setTechnicalDetailsOnCertError();
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  document.body.classList.add("neterror");
 | 
						|
 | 
						|
  let longDesc = document.getElementById("errorLongDesc");
 | 
						|
  const tryAgain = document.getElementById("netErrorButtonContainer");
 | 
						|
  tryAgain.hidden = false;
 | 
						|
  const learnMore = document.getElementById("learnMoreContainer");
 | 
						|
  const learnMoreLink = document.getElementById("learnMoreLink");
 | 
						|
  learnMoreLink.setAttribute("href", baseURL + "connection-not-secure");
 | 
						|
 | 
						|
  let pageTitleId = "neterror-page-title";
 | 
						|
  let bodyTitleId = gErrorCode + "-title";
 | 
						|
 | 
						|
  switch (gErrorCode) {
 | 
						|
    case "blockedByPolicy":
 | 
						|
      pageTitleId = "neterror-blocked-by-policy-page-title";
 | 
						|
      document.body.classList.add("blocked");
 | 
						|
 | 
						|
      // Remove the "Try again" button from pages that don't need it.
 | 
						|
      // For pages blocked by policy, trying again won't help.
 | 
						|
      tryAgain.hidden = true;
 | 
						|
      break;
 | 
						|
 | 
						|
    case "cspBlocked":
 | 
						|
    case "xfoBlocked": {
 | 
						|
      bodyTitleId = "csp-xfo-error-title";
 | 
						|
 | 
						|
      // Remove the "Try again" button for XFO and CSP violations,
 | 
						|
      // since it's almost certainly useless. (Bug 553180)
 | 
						|
      tryAgain.hidden = true;
 | 
						|
 | 
						|
      // Adding a button for opening websites blocked for CSP and XFO violations
 | 
						|
      // in a new window. (Bug 1461195)
 | 
						|
      document.getElementById("errorShortDesc").hidden = true;
 | 
						|
 | 
						|
      document.l10n.setAttributes(longDesc, "csp-xfo-blocked-long-desc", {
 | 
						|
        hostname: HOST_NAME,
 | 
						|
      });
 | 
						|
      longDesc = null;
 | 
						|
 | 
						|
      document.getElementById("openInNewWindowContainer").hidden =
 | 
						|
        RPMGetBoolPref("security.xfocsp.hideOpenInNewWindow");
 | 
						|
 | 
						|
      const openInNewWindowButton = document.getElementById(
 | 
						|
        "openInNewWindowButton"
 | 
						|
      );
 | 
						|
      openInNewWindowButton.href = document.location.href;
 | 
						|
 | 
						|
      // Add a learn more link
 | 
						|
      learnMore.hidden = false;
 | 
						|
      learnMoreLink.setAttribute("href", baseURL + "xframe-neterror-page");
 | 
						|
 | 
						|
      setupBlockingReportingUI();
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    case "dnsNotFound":
 | 
						|
      pageTitleId = "neterror-dns-not-found-title";
 | 
						|
      if (!isTRROnlyFailure) {
 | 
						|
        RPMCheckAlternateHostAvailable();
 | 
						|
      }
 | 
						|
 | 
						|
      break;
 | 
						|
    case "inadequateSecurityError":
 | 
						|
      // Remove the "Try again" button from pages that don't need it.
 | 
						|
      // For HTTP/2 inadequate security, trying again won't help.
 | 
						|
      tryAgain.hidden = true;
 | 
						|
      break;
 | 
						|
 | 
						|
    case "malformedURI":
 | 
						|
      pageTitleId = "neterror-malformed-uri-page-title";
 | 
						|
      // Remove the "Try again" button from pages that don't need it.
 | 
						|
      tryAgain.hidden = true;
 | 
						|
      break;
 | 
						|
 | 
						|
    // TLS errors and non-overridable certificate errors (e.g. pinning
 | 
						|
    // failures) are of type nssFailure2.
 | 
						|
    case "nssFailure2": {
 | 
						|
      learnMore.hidden = false;
 | 
						|
 | 
						|
      const errorCode = document.getNetErrorInfo().errorCodeString;
 | 
						|
      RPMRecordTelemetryEvent(
 | 
						|
        "security.ui.tlserror",
 | 
						|
        "load",
 | 
						|
        "abouttlserror",
 | 
						|
        errorCode,
 | 
						|
        {
 | 
						|
          is_frame: (window.parent != window).toString(),
 | 
						|
        }
 | 
						|
      );
 | 
						|
      switch (errorCode) {
 | 
						|
        case "SSL_ERROR_UNSUPPORTED_VERSION":
 | 
						|
        case "SSL_ERROR_PROTOCOL_VERSION_ALERT": {
 | 
						|
          const tlsNotice = document.getElementById("tlsVersionNotice");
 | 
						|
          tlsNotice.hidden = false;
 | 
						|
          document.l10n.setAttributes(tlsNotice, "cert-error-old-tls-version");
 | 
						|
        }
 | 
						|
        // fallthrough
 | 
						|
 | 
						|
        case "interrupted": // This happens with subresources that are above the max tls
 | 
						|
        case "SSL_ERROR_NO_CIPHERS_SUPPORTED":
 | 
						|
        case "SSL_ERROR_NO_CYPHER_OVERLAP":
 | 
						|
        case "SSL_ERROR_SSL_DISABLED":
 | 
						|
          RPMAddMessageListener("HasChangedCertPrefs", msg => {
 | 
						|
            if (msg.data.hasChangedCertPrefs) {
 | 
						|
              // Configuration overrides might have caused this; offer to reset.
 | 
						|
              showPrefChangeContainer();
 | 
						|
            }
 | 
						|
          });
 | 
						|
          RPMSendAsyncMessage("GetChangedCertPrefs");
 | 
						|
      }
 | 
						|
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    case "sslv3Used":
 | 
						|
      learnMore.hidden = false;
 | 
						|
      document.body.className = "certerror";
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!KNOWN_ERROR_TITLE_IDS.has(bodyTitleId)) {
 | 
						|
    console.error("No strings exist for error:", gErrorCode);
 | 
						|
    bodyTitleId = "generic-title";
 | 
						|
  }
 | 
						|
 | 
						|
  // The TRR errors may present options that direct users to settings only available on Firefox Desktop
 | 
						|
  if (RPMIsFirefox()) {
 | 
						|
    if (isTRROnlyFailure) {
 | 
						|
      document.body.className = "certerror"; // Shows warning icon
 | 
						|
      pageTitleId = "dns-not-found-trr-only-title2";
 | 
						|
      document.l10n.setAttributes(docTitle, pageTitleId);
 | 
						|
      bodyTitleId = "dns-not-found-trr-only-title2";
 | 
						|
      document.l10n.setAttributes(bodyTitle, bodyTitleId);
 | 
						|
 | 
						|
      shortDesc.textContent = "";
 | 
						|
      let skipReason = RPMGetTRRSkipReason();
 | 
						|
 | 
						|
      // enable buttons
 | 
						|
      let trrExceptionButton = document.getElementById("trrExceptionButton");
 | 
						|
      trrExceptionButton.addEventListener("click", () => {
 | 
						|
        RPMSendQuery("Browser:AddTRRExcludedDomain", {
 | 
						|
          hostname: HOST_NAME,
 | 
						|
        }).then(() => {
 | 
						|
          retryThis(trrExceptionButton);
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      let isTrrServerError = true;
 | 
						|
      if (RPMIsSiteSpecificTRRError()) {
 | 
						|
        // Only show the exclude button if the failure is specific to this
 | 
						|
        // domain. If the TRR server is inaccessible we don't want to allow
 | 
						|
        // the user to add an exception just for this domain.
 | 
						|
        trrExceptionButton.hidden = false;
 | 
						|
        isTrrServerError = false;
 | 
						|
      }
 | 
						|
      let trrSettingsButton = document.getElementById("trrSettingsButton");
 | 
						|
      trrSettingsButton.addEventListener("click", () => {
 | 
						|
        RPMSendAsyncMessage("OpenTRRPreferences");
 | 
						|
      });
 | 
						|
      trrSettingsButton.hidden = false;
 | 
						|
      let message = document.getElementById("trrOnlyMessage");
 | 
						|
      document.l10n.setAttributes(
 | 
						|
        message,
 | 
						|
        "neterror-dns-not-found-trr-only-reason2",
 | 
						|
        {
 | 
						|
          hostname: HOST_NAME,
 | 
						|
        }
 | 
						|
      );
 | 
						|
 | 
						|
      let descriptionTag = "neterror-dns-not-found-trr-unknown-problem";
 | 
						|
      let args = { trrDomain: RPMGetTRRDomain() };
 | 
						|
      if (
 | 
						|
        skipReason == "TRR_FAILED" ||
 | 
						|
        skipReason == "TRR_CHANNEL_DNS_FAIL" ||
 | 
						|
        skipReason == "TRR_UNKNOWN_CHANNEL_FAILURE" ||
 | 
						|
        skipReason == "TRR_NET_REFUSED" ||
 | 
						|
        skipReason == "TRR_NET_INTERRUPT" ||
 | 
						|
        skipReason == "TRR_NET_INADEQ_SEQURITY"
 | 
						|
      ) {
 | 
						|
        descriptionTag = "neterror-dns-not-found-trr-only-could-not-connect";
 | 
						|
      } else if (skipReason == "TRR_TIMEOUT") {
 | 
						|
        descriptionTag = "neterror-dns-not-found-trr-only-timeout";
 | 
						|
      } else if (
 | 
						|
        skipReason == "TRR_BROWSER_IS_OFFLINE" ||
 | 
						|
        skipReason == "TRR_NO_CONNECTIVITY"
 | 
						|
      ) {
 | 
						|
        descriptionTag = "neterror-dns-not-found-trr-offline";
 | 
						|
      } else if (
 | 
						|
        skipReason == "TRR_NO_ANSWERS" ||
 | 
						|
        skipReason == "TRR_NXDOMAIN" ||
 | 
						|
        skipReason == "TRR_RCODE_FAIL"
 | 
						|
      ) {
 | 
						|
        descriptionTag = "neterror-dns-not-found-trr-unknown-host2";
 | 
						|
      } else if (
 | 
						|
        skipReason == "TRR_DECODE_FAILED" ||
 | 
						|
        skipReason == "TRR_SERVER_RESPONSE_ERR"
 | 
						|
      ) {
 | 
						|
        descriptionTag = "neterror-dns-not-found-trr-server-problem";
 | 
						|
      } else if (skipReason == "TRR_BAD_URL") {
 | 
						|
        descriptionTag = "neterror-dns-not-found-bad-trr-url";
 | 
						|
      }
 | 
						|
 | 
						|
      let trrMode = RPMGetIntPref("network.trr.mode").toString();
 | 
						|
      recordTRREventTelemetry(
 | 
						|
        "TRROnlyFailure",
 | 
						|
        trrMode,
 | 
						|
        args.trrDomain,
 | 
						|
        skipReason
 | 
						|
      );
 | 
						|
 | 
						|
      let description = document.getElementById("trrOnlyDescription");
 | 
						|
      document.l10n.setAttributes(description, descriptionTag, args);
 | 
						|
 | 
						|
      const trrLearnMoreContainer = document.getElementById(
 | 
						|
        "trrLearnMoreContainer"
 | 
						|
      );
 | 
						|
      trrLearnMoreContainer.hidden = false;
 | 
						|
      let trrOnlyLearnMoreLink = document.getElementById(
 | 
						|
        "trrOnlylearnMoreLink"
 | 
						|
      );
 | 
						|
      if (isTrrServerError) {
 | 
						|
        // Go to DoH settings page
 | 
						|
        trrOnlyLearnMoreLink.href = "about:preferences#privacy-doh";
 | 
						|
        trrOnlyLearnMoreLink.addEventListener("click", event => {
 | 
						|
          event.preventDefault();
 | 
						|
          RPMSendAsyncMessage("OpenTRRPreferences");
 | 
						|
          RPMRecordTelemetryEvent(
 | 
						|
            "security.doh.neterror",
 | 
						|
            "click",
 | 
						|
            "settings_button",
 | 
						|
            "TRROnlyFailure",
 | 
						|
            {
 | 
						|
              mode: trrMode,
 | 
						|
              provider_key: args.trrDomain,
 | 
						|
              skip_reason: skipReason,
 | 
						|
            }
 | 
						|
          );
 | 
						|
        });
 | 
						|
      } else {
 | 
						|
        // This will be replaced at a later point with a link to an offline support page
 | 
						|
        // https://bugzilla.mozilla.org/show_bug.cgi?id=1806257
 | 
						|
        trrOnlyLearnMoreLink.href =
 | 
						|
          RPMGetFormatURLPref("network.trr_ui.skip_reason_learn_more_url") +
 | 
						|
          skipReason.toLowerCase().replaceAll("_", "-");
 | 
						|
      }
 | 
						|
 | 
						|
      let div = document.getElementById("trrOnlyContainer");
 | 
						|
      div.hidden = false;
 | 
						|
 | 
						|
      return;
 | 
						|
    } else if (isNativeFallbackWarning) {
 | 
						|
      showNativeFallbackWarning();
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  document.l10n.setAttributes(docTitle, pageTitleId);
 | 
						|
  document.l10n.setAttributes(bodyTitle, bodyTitleId);
 | 
						|
 | 
						|
  shortDesc.textContent = getDescription();
 | 
						|
  setFocus("#netErrorButtonContainer > .try-again");
 | 
						|
 | 
						|
  if (longDesc) {
 | 
						|
    const parts = getNetErrorDescParts();
 | 
						|
    setNetErrorMessageFromParts(longDesc, parts);
 | 
						|
  }
 | 
						|
 | 
						|
  setNetErrorMessageFromCode();
 | 
						|
}
 | 
						|
 | 
						|
function showNativeFallbackWarning() {
 | 
						|
  const docTitle = document.querySelector("title");
 | 
						|
  const bodyTitle = document.querySelector(".title-text");
 | 
						|
  const shortDesc = document.getElementById("errorShortDesc");
 | 
						|
 | 
						|
  let pageTitleId = "neterror-page-title";
 | 
						|
  let bodyTitleId = gErrorCode + "-title";
 | 
						|
 | 
						|
  document.body.className = "certerror"; // Shows warning icon
 | 
						|
  pageTitleId = "dns-not-found-native-fallback-title2";
 | 
						|
  document.l10n.setAttributes(docTitle, pageTitleId);
 | 
						|
 | 
						|
  bodyTitleId = "dns-not-found-native-fallback-title2";
 | 
						|
  document.l10n.setAttributes(bodyTitle, bodyTitleId);
 | 
						|
 | 
						|
  shortDesc.textContent = "";
 | 
						|
  let nativeFallbackIgnoreButton = document.getElementById(
 | 
						|
    "nativeFallbackIgnoreButton"
 | 
						|
  );
 | 
						|
  nativeFallbackIgnoreButton.addEventListener("click", () => {
 | 
						|
    RPMSetPref("network.trr.display_fallback_warning", false);
 | 
						|
    retryThis(nativeFallbackIgnoreButton);
 | 
						|
  });
 | 
						|
 | 
						|
  let continueThisTimeButton = document.getElementById(
 | 
						|
    "nativeFallbackContinueThisTimeButton"
 | 
						|
  );
 | 
						|
  continueThisTimeButton.addEventListener("click", () => {
 | 
						|
    RPMSetTRRDisabledLoadFlags();
 | 
						|
    document.location.reload();
 | 
						|
  });
 | 
						|
  continueThisTimeButton.hidden = false;
 | 
						|
 | 
						|
  nativeFallbackIgnoreButton.hidden = false;
 | 
						|
  let message = document.getElementById("nativeFallbackMessage");
 | 
						|
  document.l10n.setAttributes(
 | 
						|
    message,
 | 
						|
    "neterror-dns-not-found-native-fallback-reason2",
 | 
						|
    {
 | 
						|
      hostname: HOST_NAME,
 | 
						|
    }
 | 
						|
  );
 | 
						|
  let skipReason = RPMGetTRRSkipReason();
 | 
						|
  let descriptionTag = "neterror-dns-not-found-trr-unknown-problem";
 | 
						|
  let args = { trrDomain: RPMGetTRRDomain() };
 | 
						|
 | 
						|
  if (skipReason.includes("HEURISTIC_TRIPPED")) {
 | 
						|
    descriptionTag = "neterror-dns-not-found-native-fallback-heuristic";
 | 
						|
  } else if (skipReason == "TRR_NOT_CONFIRMED") {
 | 
						|
    descriptionTag = "neterror-dns-not-found-native-fallback-not-confirmed2";
 | 
						|
  }
 | 
						|
 | 
						|
  let description = document.getElementById("nativeFallbackDescription");
 | 
						|
  document.l10n.setAttributes(description, descriptionTag, args);
 | 
						|
 | 
						|
  let learnMoreContainer = document.getElementById(
 | 
						|
    "nativeFallbackLearnMoreContainer"
 | 
						|
  );
 | 
						|
  learnMoreContainer.hidden = false;
 | 
						|
 | 
						|
  let learnMoreLink = document.getElementById("nativeFallbackLearnMoreLink");
 | 
						|
  learnMoreLink.href =
 | 
						|
    RPMGetFormatURLPref("network.trr_ui.skip_reason_learn_more_url") +
 | 
						|
    skipReason.toLowerCase().replaceAll("_", "-");
 | 
						|
 | 
						|
  let div = document.getElementById("nativeFallbackContainer");
 | 
						|
  div.hidden = false;
 | 
						|
 | 
						|
  recordTRREventTelemetry(
 | 
						|
    "NativeFallbackWarning",
 | 
						|
    RPMGetIntPref("network.trr.mode").toString(),
 | 
						|
    args.trrDomain,
 | 
						|
    skipReason
 | 
						|
  );
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Builds HTML elements from `parts` and appends them to `parentElement`.
 | 
						|
 *
 | 
						|
 * @param {HTMLElement} parentElement
 | 
						|
 * @param {Array<["li" | "p" | "span", string, Record<string, string> | undefined]>} parts
 | 
						|
 */
 | 
						|
function setNetErrorMessageFromParts(parentElement, parts) {
 | 
						|
  let list = null;
 | 
						|
 | 
						|
  for (let [tag, l10nId, l10nArgs] of parts) {
 | 
						|
    const elem = document.createElement(tag);
 | 
						|
    elem.dataset.l10nId = l10nId;
 | 
						|
    if (l10nArgs) {
 | 
						|
      elem.dataset.l10nArgs = JSON.stringify(l10nArgs);
 | 
						|
    }
 | 
						|
 | 
						|
    if (tag === "li") {
 | 
						|
      if (!list) {
 | 
						|
        list = document.createElement("ul");
 | 
						|
        parentElement.appendChild(list);
 | 
						|
      }
 | 
						|
      list.appendChild(elem);
 | 
						|
    } else {
 | 
						|
      if (list) {
 | 
						|
        list = null;
 | 
						|
      }
 | 
						|
      parentElement.appendChild(elem);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Returns an array of tuples determining the parts of an error message:
 | 
						|
 * - HTML tag name
 | 
						|
 * - l10n id
 | 
						|
 * - l10n args (optional)
 | 
						|
 *
 | 
						|
 * @returns { Array<["li" | "p" | "span", string, Record<string, string> | undefined]> }
 | 
						|
 */
 | 
						|
function getNetErrorDescParts() {
 | 
						|
  switch (gErrorCode) {
 | 
						|
    case "connectionFailure":
 | 
						|
    case "netInterrupt":
 | 
						|
    case "netReset":
 | 
						|
    case "netTimeout":
 | 
						|
      return [
 | 
						|
        ["li", "neterror-load-error-try-again"],
 | 
						|
        ["li", "neterror-load-error-connection"],
 | 
						|
        ["li", "neterror-load-error-firewall"],
 | 
						|
      ];
 | 
						|
 | 
						|
    case "blockedByPolicy":
 | 
						|
    case "deniedPortAccess":
 | 
						|
    case "malformedURI":
 | 
						|
      return [];
 | 
						|
 | 
						|
    case "captivePortal":
 | 
						|
      return [["p", ""]];
 | 
						|
    case "contentEncodingError":
 | 
						|
      return [["li", "neterror-content-encoding-error"]];
 | 
						|
    case "corruptedContentErrorv2":
 | 
						|
      return [
 | 
						|
        ["p", "neterror-corrupted-content-intro"],
 | 
						|
        ["li", "neterror-corrupted-content-contact-website"],
 | 
						|
      ];
 | 
						|
    case "dnsNotFound":
 | 
						|
      return [
 | 
						|
        ["span", "neterror-dns-not-found-hint-header"],
 | 
						|
        ["li", "neterror-dns-not-found-hint-try-again"],
 | 
						|
        ["li", "neterror-dns-not-found-hint-check-network"],
 | 
						|
        ["li", "neterror-dns-not-found-hint-firewall"],
 | 
						|
      ];
 | 
						|
    case "fileAccessDenied":
 | 
						|
      return [["li", "neterror-access-denied"]];
 | 
						|
    case "fileNotFound":
 | 
						|
      return [
 | 
						|
        ["li", "neterror-file-not-found-filename"],
 | 
						|
        ["li", "neterror-file-not-found-moved"],
 | 
						|
      ];
 | 
						|
    case "inadequateSecurityError":
 | 
						|
      return [
 | 
						|
        ["p", "neterror-inadequate-security-intro", { hostname: HOST_NAME }],
 | 
						|
        ["p", "neterror-inadequate-security-code"],
 | 
						|
      ];
 | 
						|
    case "mitm": {
 | 
						|
      const failedCertInfo = document.getFailedCertSecurityInfo();
 | 
						|
      const errArgs = {
 | 
						|
        hostname: HOST_NAME,
 | 
						|
        mitm: getMitmName(failedCertInfo),
 | 
						|
      };
 | 
						|
      return [["span", "certerror-mitm", errArgs]];
 | 
						|
    }
 | 
						|
    case "netOffline":
 | 
						|
      return [["li", "neterror-net-offline"]];
 | 
						|
    case "networkProtocolError":
 | 
						|
      return [
 | 
						|
        ["p", "neterror-network-protocol-error-intro"],
 | 
						|
        ["li", "neterror-network-protocol-error-contact-website"],
 | 
						|
      ];
 | 
						|
    case "notCached":
 | 
						|
      return [
 | 
						|
        ["p", "neterror-not-cached-intro"],
 | 
						|
        ["li", "neterror-not-cached-sensitive"],
 | 
						|
        ["li", "neterror-not-cached-try-again"],
 | 
						|
      ];
 | 
						|
    case "nssFailure2":
 | 
						|
      return [
 | 
						|
        ["li", "neterror-nss-failure-not-verified"],
 | 
						|
        ["li", "neterror-nss-failure-contact-website"],
 | 
						|
      ];
 | 
						|
    case "proxyConnectFailure":
 | 
						|
      return [
 | 
						|
        ["li", "neterror-proxy-connect-failure-settings"],
 | 
						|
        ["li", "neterror-proxy-connect-failure-contact-admin"],
 | 
						|
      ];
 | 
						|
    case "proxyResolveFailure":
 | 
						|
      return [
 | 
						|
        ["li", "neterror-proxy-resolve-failure-settings"],
 | 
						|
        ["li", "neterror-proxy-resolve-failure-connection"],
 | 
						|
        ["li", "neterror-proxy-resolve-failure-firewall"],
 | 
						|
      ];
 | 
						|
    case "redirectLoop":
 | 
						|
      return [["li", "neterror-redirect-loop"]];
 | 
						|
    case "sslv3Used":
 | 
						|
      return [["span", "neterror-sslv3-used"]];
 | 
						|
    case "unknownProtocolFound":
 | 
						|
      return [["li", "neterror-unknown-protocol"]];
 | 
						|
    case "unknownSocketType":
 | 
						|
      return [
 | 
						|
        ["li", "neterror-unknown-socket-type-psm-installed"],
 | 
						|
        ["li", "neterror-unknown-socket-type-server-config"],
 | 
						|
      ];
 | 
						|
    case "unsafeContentType":
 | 
						|
      return [["li", "neterror-unsafe-content-type"]];
 | 
						|
 | 
						|
    default:
 | 
						|
      return [["p", "neterror-generic-error"]];
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
function setNetErrorMessageFromCode() {
 | 
						|
  let errorCode;
 | 
						|
  try {
 | 
						|
    errorCode = document.getNetErrorInfo().errorCodeString;
 | 
						|
  } catch (ex) {
 | 
						|
    // We don't have a securityInfo when this is for example a DNS error.
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  let errorMessage;
 | 
						|
  if (errorCode) {
 | 
						|
    const l10nId = errorCode.replace(/_/g, "-").toLowerCase();
 | 
						|
    if (KNOWN_ERROR_MESSAGE_IDS.has(l10nId)) {
 | 
						|
      const l10n = new Localization([ERROR_MESSAGES_FTL], true);
 | 
						|
      errorMessage = l10n.formatValueSync(l10nId);
 | 
						|
    }
 | 
						|
 | 
						|
    const shortDesc2 = document.getElementById("errorShortDesc2");
 | 
						|
    document.l10n.setAttributes(shortDesc2, "cert-error-code-prefix", {
 | 
						|
      error: errorCode,
 | 
						|
    });
 | 
						|
  } else {
 | 
						|
    console.warn("This error page has no error code in its security info");
 | 
						|
  }
 | 
						|
 | 
						|
  let hostname = HOST_NAME;
 | 
						|
  const { port } = document.location;
 | 
						|
  if (port && port != 443) {
 | 
						|
    hostname += ":" + port;
 | 
						|
  }
 | 
						|
 | 
						|
  const shortDesc = document.getElementById("errorShortDesc");
 | 
						|
  document.l10n.setAttributes(shortDesc, "cert-error-ssl-connection-error", {
 | 
						|
    errorMessage: errorMessage ?? errorCode ?? "",
 | 
						|
    hostname,
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
function setupBlockingReportingUI() {
 | 
						|
  let checkbox = document.getElementById("automaticallyReportBlockingInFuture");
 | 
						|
 | 
						|
  let reportingAutomatic = RPMGetBoolPref(
 | 
						|
    "security.xfocsp.errorReporting.automatic"
 | 
						|
  );
 | 
						|
  checkbox.checked = !!reportingAutomatic;
 | 
						|
 | 
						|
  checkbox.addEventListener("change", function ({ target: { checked } }) {
 | 
						|
    RPMSetPref("security.xfocsp.errorReporting.automatic", checked);
 | 
						|
 | 
						|
    // If we're enabling reports, send a report for this failure.
 | 
						|
    if (checked) {
 | 
						|
      reportBlockingError();
 | 
						|
    }
 | 
						|
  });
 | 
						|
 | 
						|
  let reportingEnabled = RPMGetBoolPref(
 | 
						|
    "security.xfocsp.errorReporting.enabled"
 | 
						|
  );
 | 
						|
 | 
						|
  if (reportingEnabled) {
 | 
						|
    // Display blocking error reporting UI for XFO error and CSP error.
 | 
						|
    document.getElementById("blockingErrorReporting").hidden = false;
 | 
						|
 | 
						|
    if (reportingAutomatic) {
 | 
						|
      reportBlockingError();
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
function reportBlockingError() {
 | 
						|
  // We only report if we are in a frame.
 | 
						|
  if (window === window.top) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  let err = gErrorCode;
 | 
						|
  // Ensure we only deal with XFO and CSP here.
 | 
						|
  if (!["xfoBlocked", "cspBlocked"].includes(err)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  let xfo_header = RPMGetHttpResponseHeader("X-Frame-Options");
 | 
						|
  let csp_header = RPMGetHttpResponseHeader("Content-Security-Policy");
 | 
						|
 | 
						|
  // Extract the 'CSP: frame-ancestors' from the CSP header.
 | 
						|
  let reg = /(?:^|\s)frame-ancestors\s([^;]*)[$]*/i;
 | 
						|
  let match = reg.exec(csp_header);
 | 
						|
  csp_header = match ? match[1] : "";
 | 
						|
 | 
						|
  // If it's the csp error page without the CSP: frame-ancestors, this means
 | 
						|
  // this error page is not triggered by CSP: frame-ancestors. So, we bail out
 | 
						|
  // early.
 | 
						|
  if (err === "cspBlocked" && !csp_header) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  let xfoAndCspInfo = {
 | 
						|
    error_type: err === "xfoBlocked" ? "xfo" : "csp",
 | 
						|
    xfo_header,
 | 
						|
    csp_header,
 | 
						|
  };
 | 
						|
 | 
						|
  // Trimming the tail colon symbol.
 | 
						|
  let scheme = document.location.protocol.slice(0, -1);
 | 
						|
 | 
						|
  RPMSendAsyncMessage("ReportBlockingError", {
 | 
						|
    scheme,
 | 
						|
    host: document.location.host,
 | 
						|
    port: parseInt(document.location.port) || -1,
 | 
						|
    path: document.location.pathname,
 | 
						|
    xfoAndCspInfo,
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
function initPageCaptivePortal() {
 | 
						|
  document.body.className = "captiveportal";
 | 
						|
  document.getElementById("returnButton").hidden = true;
 | 
						|
  const openButton = document.getElementById("openPortalLoginPageButton");
 | 
						|
  openButton.hidden = false;
 | 
						|
  openButton.addEventListener("click", () => {
 | 
						|
    RPMSendAsyncMessage("Browser:OpenCaptivePortalPage");
 | 
						|
  });
 | 
						|
 | 
						|
  setFocus("#openPortalLoginPageButton");
 | 
						|
  setupAdvancedButton();
 | 
						|
  disallowCertOverridesIfNeeded();
 | 
						|
 | 
						|
  // When the portal is freed, an event is sent by the parent process
 | 
						|
  // that we can pick up and attempt to reload the original page.
 | 
						|
  RPMAddMessageListener("AboutNetErrorCaptivePortalFreed", () => {
 | 
						|
    document.location.reload();
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
function initPageCertError() {
 | 
						|
  document.body.classList.add("certerror");
 | 
						|
 | 
						|
  setFocus("#returnButton");
 | 
						|
  setupAdvancedButton();
 | 
						|
  disallowCertOverridesIfNeeded();
 | 
						|
 | 
						|
  const hideAddExceptionButton = RPMGetBoolPref(
 | 
						|
    "security.certerror.hideAddException",
 | 
						|
    false
 | 
						|
  );
 | 
						|
  if (hideAddExceptionButton) {
 | 
						|
    document.getElementById("exceptionDialogButton").hidden = true;
 | 
						|
  }
 | 
						|
 | 
						|
  const els = document.querySelectorAll("[data-telemetry-id]");
 | 
						|
  for (let el of els) {
 | 
						|
    el.addEventListener("click", recordClickTelemetry);
 | 
						|
  }
 | 
						|
 | 
						|
  const failedCertInfo = document.getFailedCertSecurityInfo();
 | 
						|
  // Truncate the error code to avoid going over the allowed
 | 
						|
  // string size limit for telemetry events.
 | 
						|
  const errorCode = failedCertInfo.errorCodeString.substring(0, 40);
 | 
						|
  RPMRecordTelemetryEvent(
 | 
						|
    "security.ui.certerror",
 | 
						|
    "load",
 | 
						|
    "aboutcerterror",
 | 
						|
    errorCode,
 | 
						|
    {
 | 
						|
      has_sts: gHasSts.toString(),
 | 
						|
      is_frame: (window.parent != window).toString(),
 | 
						|
    }
 | 
						|
  );
 | 
						|
 | 
						|
  setCertErrorDetails();
 | 
						|
}
 | 
						|
 | 
						|
function recordClickTelemetry(e) {
 | 
						|
  let target = e.originalTarget;
 | 
						|
  let telemetryId = target.dataset.telemetryId;
 | 
						|
  let failedCertInfo = document.getFailedCertSecurityInfo();
 | 
						|
  // Truncate the error code to avoid going over the allowed
 | 
						|
  // string size limit for telemetry events.
 | 
						|
  let errorCode = failedCertInfo.errorCodeString.substring(0, 40);
 | 
						|
  RPMRecordTelemetryEvent(
 | 
						|
    "security.ui.certerror",
 | 
						|
    "click",
 | 
						|
    telemetryId,
 | 
						|
    errorCode,
 | 
						|
    {
 | 
						|
      has_sts: gHasSts.toString(),
 | 
						|
      is_frame: (window.parent != window).toString(),
 | 
						|
    }
 | 
						|
  );
 | 
						|
}
 | 
						|
 | 
						|
function initCertErrorPageActions() {
 | 
						|
  document.getElementById(
 | 
						|
    "certErrorAndCaptivePortalButtonContainer"
 | 
						|
  ).hidden = false;
 | 
						|
  document
 | 
						|
    .getElementById("returnButton")
 | 
						|
    .addEventListener("click", onReturnButtonClick);
 | 
						|
  document
 | 
						|
    .getElementById("advancedPanelReturnButton")
 | 
						|
    .addEventListener("click", onReturnButtonClick);
 | 
						|
  document
 | 
						|
    .getElementById("copyToClipboardTop")
 | 
						|
    .addEventListener("click", copyPEMToClipboard);
 | 
						|
  document
 | 
						|
    .getElementById("copyToClipboardBottom")
 | 
						|
    .addEventListener("click", copyPEMToClipboard);
 | 
						|
  document
 | 
						|
    .getElementById("exceptionDialogButton")
 | 
						|
    .addEventListener("click", addCertException);
 | 
						|
}
 | 
						|
 | 
						|
function addCertException() {
 | 
						|
  const isPermanent =
 | 
						|
    !RPMIsWindowPrivate() &&
 | 
						|
    RPMGetBoolPref("security.certerrors.permanentOverride");
 | 
						|
  document.addCertException(!isPermanent).then(
 | 
						|
    () => {
 | 
						|
      location.reload();
 | 
						|
    },
 | 
						|
    () => {}
 | 
						|
  );
 | 
						|
}
 | 
						|
 | 
						|
function onReturnButtonClick() {
 | 
						|
  RPMSendAsyncMessage("Browser:SSLErrorGoBack");
 | 
						|
}
 | 
						|
 | 
						|
function copyPEMToClipboard() {
 | 
						|
  const errorText = document.getElementById("certificateErrorText");
 | 
						|
  navigator.clipboard.writeText(errorText.textContent);
 | 
						|
}
 | 
						|
 | 
						|
async function getFailedCertificatesAsPEMString() {
 | 
						|
  let locationUrl = document.location.href;
 | 
						|
  let failedCertInfo = document.getFailedCertSecurityInfo();
 | 
						|
  let errorMessage = failedCertInfo.errorMessage;
 | 
						|
  let hasHSTS = failedCertInfo.hasHSTS.toString();
 | 
						|
  let hasHPKP = failedCertInfo.hasHPKP.toString();
 | 
						|
  let [hstsLabel, hpkpLabel, failedChainLabel] =
 | 
						|
    await document.l10n.formatValues([
 | 
						|
      { id: "cert-error-details-hsts-label", args: { hasHSTS } },
 | 
						|
      { id: "cert-error-details-key-pinning-label", args: { hasHPKP } },
 | 
						|
      { id: "cert-error-details-cert-chain-label" },
 | 
						|
    ]);
 | 
						|
 | 
						|
  let certStrings = failedCertInfo.certChainStrings;
 | 
						|
  let failedChainCertificates = "";
 | 
						|
  for (let der64 of certStrings) {
 | 
						|
    let wrapped = der64.replace(/(\S{64}(?!$))/g, "$1\r\n");
 | 
						|
    failedChainCertificates +=
 | 
						|
      "-----BEGIN CERTIFICATE-----\r\n" +
 | 
						|
      wrapped +
 | 
						|
      "\r\n-----END CERTIFICATE-----\r\n";
 | 
						|
  }
 | 
						|
 | 
						|
  let details =
 | 
						|
    locationUrl +
 | 
						|
    "\r\n\r\n" +
 | 
						|
    errorMessage +
 | 
						|
    "\r\n\r\n" +
 | 
						|
    hstsLabel +
 | 
						|
    "\r\n" +
 | 
						|
    hpkpLabel +
 | 
						|
    "\r\n\r\n" +
 | 
						|
    failedChainLabel +
 | 
						|
    "\r\n\r\n" +
 | 
						|
    failedChainCertificates;
 | 
						|
  return details;
 | 
						|
}
 | 
						|
 | 
						|
function setCertErrorDetails() {
 | 
						|
  // Check if the connection is being man-in-the-middled. When the parent
 | 
						|
  // detects an intercepted connection, the page may be reloaded with a new
 | 
						|
  // error code (MOZILLA_PKIX_ERROR_MITM_DETECTED).
 | 
						|
  const failedCertInfo = document.getFailedCertSecurityInfo();
 | 
						|
  const mitmPrimingEnabled = RPMGetBoolPref(
 | 
						|
    "security.certerrors.mitm.priming.enabled"
 | 
						|
  );
 | 
						|
  if (
 | 
						|
    mitmPrimingEnabled &&
 | 
						|
    failedCertInfo.errorCodeString == "SEC_ERROR_UNKNOWN_ISSUER" &&
 | 
						|
    // Only do this check for top-level failures.
 | 
						|
    window.parent == window
 | 
						|
  ) {
 | 
						|
    RPMSendAsyncMessage("Browser:PrimeMitm");
 | 
						|
  }
 | 
						|
 | 
						|
  document.body.setAttribute("code", failedCertInfo.errorCodeString);
 | 
						|
 | 
						|
  const learnMore = document.getElementById("learnMoreContainer");
 | 
						|
  learnMore.hidden = false;
 | 
						|
  const learnMoreLink = document.getElementById("learnMoreLink");
 | 
						|
  const baseURL = RPMGetFormatURLPref("app.support.baseURL");
 | 
						|
  learnMoreLink.href = baseURL + "connection-not-secure";
 | 
						|
 | 
						|
  const bodyTitle = document.querySelector(".title-text");
 | 
						|
  const shortDesc = document.getElementById("errorShortDesc");
 | 
						|
  const shortDesc2 = document.getElementById("errorShortDesc2");
 | 
						|
 | 
						|
  let whatToDoParts = null;
 | 
						|
 | 
						|
  switch (failedCertInfo.errorCodeString) {
 | 
						|
    case "SSL_ERROR_BAD_CERT_DOMAIN":
 | 
						|
      whatToDoParts = [
 | 
						|
        ["p", "certerror-bad-cert-domain-what-can-you-do-about-it"],
 | 
						|
      ];
 | 
						|
      break;
 | 
						|
 | 
						|
    case "SEC_ERROR_OCSP_INVALID_SIGNING_CERT": // FIXME - this would have thrown?
 | 
						|
      break;
 | 
						|
 | 
						|
    case "SEC_ERROR_UNKNOWN_ISSUER":
 | 
						|
      whatToDoParts = [
 | 
						|
        ["p", "certerror-unknown-issuer-what-can-you-do-about-it-website"],
 | 
						|
        [
 | 
						|
          "p",
 | 
						|
          "certerror-unknown-issuer-what-can-you-do-about-it-contact-admin",
 | 
						|
        ],
 | 
						|
      ];
 | 
						|
      break;
 | 
						|
 | 
						|
    // This error code currently only exists for the Symantec distrust
 | 
						|
    // in Firefox 63, so we add copy explaining that to the user.
 | 
						|
    // In case of future distrusts of that scale we might need to add
 | 
						|
    // additional parameters that allow us to identify the affected party
 | 
						|
    // without replicating the complex logic from certverifier code.
 | 
						|
    case "MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED": {
 | 
						|
      document.l10n.setAttributes(
 | 
						|
        shortDesc2,
 | 
						|
        "cert-error-symantec-distrust-description",
 | 
						|
        { hostname: HOST_NAME }
 | 
						|
      );
 | 
						|
 | 
						|
      // FIXME - this does nothing
 | 
						|
      const adminDesc = document.createElement("p");
 | 
						|
      document.l10n.setAttributes(
 | 
						|
        adminDesc,
 | 
						|
        "cert-error-symantec-distrust-admin"
 | 
						|
      );
 | 
						|
 | 
						|
      learnMoreLink.href = baseURL + "symantec-warning";
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    case "MOZILLA_PKIX_ERROR_MITM_DETECTED": {
 | 
						|
      const autoEnabledEnterpriseRoots = RPMGetBoolPref(
 | 
						|
        "security.enterprise_roots.auto-enabled",
 | 
						|
        false
 | 
						|
      );
 | 
						|
      if (mitmPrimingEnabled && autoEnabledEnterpriseRoots) {
 | 
						|
        RPMSendAsyncMessage("Browser:ResetEnterpriseRootsPref");
 | 
						|
      }
 | 
						|
 | 
						|
      learnMoreLink.href = baseURL + "security-error";
 | 
						|
 | 
						|
      document.l10n.setAttributes(bodyTitle, "certerror-mitm-title");
 | 
						|
 | 
						|
      document.l10n.setAttributes(shortDesc, "certerror-mitm", {
 | 
						|
        hostname: HOST_NAME,
 | 
						|
        mitm: getMitmName(failedCertInfo),
 | 
						|
      });
 | 
						|
 | 
						|
      const id3 = gHasSts
 | 
						|
        ? "certerror-mitm-what-can-you-do-about-it-attack-sts"
 | 
						|
        : "certerror-mitm-what-can-you-do-about-it-attack";
 | 
						|
      whatToDoParts = [
 | 
						|
        ["li", "certerror-mitm-what-can-you-do-about-it-antivirus"],
 | 
						|
        ["li", "certerror-mitm-what-can-you-do-about-it-corporate"],
 | 
						|
        ["li", id3, { mitm: getMitmName(failedCertInfo) }],
 | 
						|
      ];
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    case "MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT":
 | 
						|
      learnMoreLink.href = baseURL + "security-error";
 | 
						|
      break;
 | 
						|
 | 
						|
    // In case the certificate expired we make sure the system clock
 | 
						|
    // matches the remote-settings service (blocklist via Kinto) ping time
 | 
						|
    // and is not before the build date.
 | 
						|
    case "SEC_ERROR_EXPIRED_CERTIFICATE":
 | 
						|
    case "SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE":
 | 
						|
    case "MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE":
 | 
						|
    case "MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE": {
 | 
						|
      learnMoreLink.href = baseURL + "time-errors";
 | 
						|
 | 
						|
      // We check against the remote-settings server time first if available, because that allows us
 | 
						|
      // to give the user an approximation of what the correct time is.
 | 
						|
      const difference = RPMGetIntPref(
 | 
						|
        "services.settings.clock_skew_seconds",
 | 
						|
        0
 | 
						|
      );
 | 
						|
      const lastFetched =
 | 
						|
        RPMGetIntPref("services.settings.last_update_seconds", 0) * 1000;
 | 
						|
 | 
						|
      // This is set to true later if the user's system clock is at fault for this error.
 | 
						|
      let clockSkew = false;
 | 
						|
 | 
						|
      const now = Date.now();
 | 
						|
      const certRange = {
 | 
						|
        notBefore: failedCertInfo.certValidityRangeNotBefore,
 | 
						|
        notAfter: failedCertInfo.certValidityRangeNotAfter,
 | 
						|
      };
 | 
						|
      const approximateDate = now - difference * 1000;
 | 
						|
      // If the difference is more than a day, we last fetched the date in the last 5 days,
 | 
						|
      // and adjusting the date per the interval would make the cert valid, warn the user:
 | 
						|
      if (
 | 
						|
        Math.abs(difference) > 60 * 60 * 24 &&
 | 
						|
        now - lastFetched <= 60 * 60 * 24 * 5 * 1000 &&
 | 
						|
        certRange.notBefore < approximateDate &&
 | 
						|
        certRange.notAfter > approximateDate
 | 
						|
      ) {
 | 
						|
        clockSkew = true;
 | 
						|
        // If there is no clock skew with Kinto servers, check against the build date.
 | 
						|
        // (The Kinto ping could have happened when the time was still right, or not at all)
 | 
						|
      } else {
 | 
						|
        const appBuildID = RPMGetAppBuildID();
 | 
						|
        const year = parseInt(appBuildID.substr(0, 4), 10);
 | 
						|
        const month = parseInt(appBuildID.substr(4, 2), 10) - 1;
 | 
						|
        const day = parseInt(appBuildID.substr(6, 2), 10);
 | 
						|
 | 
						|
        const buildDate = new Date(year, month, day);
 | 
						|
 | 
						|
        // We don't check the notBefore of the cert with the build date,
 | 
						|
        // as it is of course almost certain that it is now later than the build date,
 | 
						|
        // so we shouldn't exclude the possibility that the cert has become valid
 | 
						|
        // since the build date.
 | 
						|
        if (buildDate > now && new Date(certRange.notAfter) > buildDate) {
 | 
						|
          clockSkew = true;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if (clockSkew) {
 | 
						|
        document.body.classList.add("clockSkewError");
 | 
						|
        document.l10n.setAttributes(bodyTitle, "clockSkewError-title");
 | 
						|
        document.l10n.setAttributes(shortDesc, "neterror-clock-skew-error", {
 | 
						|
          hostname: HOST_NAME,
 | 
						|
          now,
 | 
						|
        });
 | 
						|
        document.getElementById("returnButton").hidden = true;
 | 
						|
        document.getElementById("certErrorTryAgainButton").hidden = false;
 | 
						|
        document.getElementById("advancedButton").hidden = true;
 | 
						|
 | 
						|
        document.getElementById("advancedPanelReturnButton").hidden = true;
 | 
						|
        document.getElementById("advancedPanelTryAgainButton").hidden = false;
 | 
						|
        document.getElementById("exceptionDialogButton").hidden = true;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      document.l10n.setAttributes(shortDesc, "certerror-expired-cert-intro", {
 | 
						|
        hostname: HOST_NAME,
 | 
						|
      });
 | 
						|
 | 
						|
      // The secondary description mentions expired certificates explicitly
 | 
						|
      // and should only be shown if the certificate has actually expired
 | 
						|
      // instead of being not yet valid.
 | 
						|
      if (failedCertInfo.errorCodeString == "SEC_ERROR_EXPIRED_CERTIFICATE") {
 | 
						|
        const sd2Id = gHasSts
 | 
						|
          ? "certerror-expired-cert-sts-second-para"
 | 
						|
          : "certerror-expired-cert-second-para";
 | 
						|
        document.l10n.setAttributes(shortDesc2, sd2Id);
 | 
						|
        if (
 | 
						|
          Math.abs(difference) <= 60 * 60 * 24 &&
 | 
						|
          now - lastFetched <= 60 * 60 * 24 * 5 * 1000
 | 
						|
        ) {
 | 
						|
          whatToDoParts = [
 | 
						|
            ["p", "certerror-bad-cert-domain-what-can-you-do-about-it"],
 | 
						|
          ];
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      whatToDoParts ??= [
 | 
						|
        [
 | 
						|
          "p",
 | 
						|
          "certerror-expired-cert-what-can-you-do-about-it-clock",
 | 
						|
          { hostname: HOST_NAME, now },
 | 
						|
        ],
 | 
						|
        [
 | 
						|
          "p",
 | 
						|
          "certerror-expired-cert-what-can-you-do-about-it-contact-website",
 | 
						|
        ],
 | 
						|
      ];
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (whatToDoParts) {
 | 
						|
    setNetErrorMessageFromParts(
 | 
						|
      document.getElementById("errorWhatToDoText"),
 | 
						|
      whatToDoParts
 | 
						|
    );
 | 
						|
    document.getElementById("errorWhatToDo").hidden = false;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
async function getSubjectAltNames(failedCertInfo) {
 | 
						|
  const serverCertBase64 = failedCertInfo.certChainStrings[0];
 | 
						|
  const parsed = await parse(pemToDER(serverCertBase64));
 | 
						|
  const subjectAltNamesExtension = parsed.ext.san;
 | 
						|
  const subjectAltNames = [];
 | 
						|
  if (subjectAltNamesExtension) {
 | 
						|
    for (let [key, value] of subjectAltNamesExtension.altNames) {
 | 
						|
      if (key === "DNS Name" && value.length) {
 | 
						|
        subjectAltNames.push(value);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return subjectAltNames;
 | 
						|
}
 | 
						|
 | 
						|
// The optional argument is only here for testing purposes.
 | 
						|
function setTechnicalDetailsOnCertError(
 | 
						|
  failedCertInfo = document.getFailedCertSecurityInfo()
 | 
						|
) {
 | 
						|
  let technicalInfo = document.getElementById("badCertTechnicalInfo");
 | 
						|
  technicalInfo.textContent = "";
 | 
						|
 | 
						|
  function addLabel(l10nId, args = null, attrs = null) {
 | 
						|
    let elem = document.createElement("label");
 | 
						|
    technicalInfo.appendChild(elem);
 | 
						|
 | 
						|
    let newLines = document.createTextNode("\n \n");
 | 
						|
    technicalInfo.appendChild(newLines);
 | 
						|
 | 
						|
    if (attrs) {
 | 
						|
      let link = document.createElement("a");
 | 
						|
      for (let [attr, value] of Object.entries(attrs)) {
 | 
						|
        link.setAttribute(attr, value);
 | 
						|
      }
 | 
						|
      elem.appendChild(link);
 | 
						|
    }
 | 
						|
 | 
						|
    document.l10n.setAttributes(elem, l10nId, args);
 | 
						|
  }
 | 
						|
 | 
						|
  function addErrorCodeLink() {
 | 
						|
    addLabel(
 | 
						|
      "cert-error-code-prefix-link",
 | 
						|
      { error: failedCertInfo.errorCodeString },
 | 
						|
      {
 | 
						|
        title: failedCertInfo.errorCodeString,
 | 
						|
        id: "errorCode",
 | 
						|
        "data-l10n-name": "error-code-link",
 | 
						|
        "data-telemetry-id": "error_code_link",
 | 
						|
        href: "#certificateErrorDebugInformation",
 | 
						|
      }
 | 
						|
    );
 | 
						|
 | 
						|
    // We're attaching the event listener to the parent element and not on
 | 
						|
    // the errorCodeLink itself because event listeners cannot be attached
 | 
						|
    // to fluent DOM overlays.
 | 
						|
    technicalInfo.addEventListener("click", event => {
 | 
						|
      if (event.target.id === "errorCode") {
 | 
						|
        event.preventDefault();
 | 
						|
        toggleCertErrorDebugInfoVisibility();
 | 
						|
        recordClickTelemetry(event);
 | 
						|
      }
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  let hostname = HOST_NAME;
 | 
						|
  const { port } = document.location;
 | 
						|
  if (port && port != 443) {
 | 
						|
    hostname += ":" + port;
 | 
						|
  }
 | 
						|
 | 
						|
  switch (failedCertInfo.overridableErrorCategory) {
 | 
						|
    case "trust-error":
 | 
						|
      switch (failedCertInfo.errorCodeString) {
 | 
						|
        case "MOZILLA_PKIX_ERROR_MITM_DETECTED":
 | 
						|
          addLabel("cert-error-mitm-intro");
 | 
						|
          addLabel("cert-error-mitm-mozilla");
 | 
						|
          addLabel("cert-error-mitm-connection");
 | 
						|
          break;
 | 
						|
        case "SEC_ERROR_UNKNOWN_ISSUER":
 | 
						|
          addLabel("cert-error-trust-unknown-issuer-intro");
 | 
						|
          addLabel("cert-error-trust-unknown-issuer", { hostname });
 | 
						|
          break;
 | 
						|
        case "SEC_ERROR_CA_CERT_INVALID":
 | 
						|
          addLabel("cert-error-intro", { hostname });
 | 
						|
          addLabel("cert-error-trust-cert-invalid");
 | 
						|
          break;
 | 
						|
        case "SEC_ERROR_UNTRUSTED_ISSUER":
 | 
						|
          addLabel("cert-error-intro", { hostname });
 | 
						|
          addLabel("cert-error-trust-untrusted-issuer");
 | 
						|
          break;
 | 
						|
        case "SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED":
 | 
						|
          addLabel("cert-error-intro", { hostname });
 | 
						|
          addLabel("cert-error-trust-signature-algorithm-disabled");
 | 
						|
          break;
 | 
						|
        case "SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE":
 | 
						|
          addLabel("cert-error-intro", { hostname });
 | 
						|
          addLabel("cert-error-trust-expired-issuer");
 | 
						|
          break;
 | 
						|
        case "MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT":
 | 
						|
          addLabel("cert-error-intro", { hostname });
 | 
						|
          addLabel("cert-error-trust-self-signed");
 | 
						|
          break;
 | 
						|
        case "MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED":
 | 
						|
          addLabel("cert-error-intro", { hostname });
 | 
						|
          addLabel("cert-error-trust-symantec");
 | 
						|
          break;
 | 
						|
        default:
 | 
						|
          addLabel("cert-error-intro", { hostname });
 | 
						|
          addLabel("cert-error-untrusted-default");
 | 
						|
      }
 | 
						|
      addErrorCodeLink();
 | 
						|
      break;
 | 
						|
 | 
						|
    case "expired-or-not-yet-valid": {
 | 
						|
      const notBefore = failedCertInfo.validNotBefore;
 | 
						|
      const notAfter = failedCertInfo.validNotAfter;
 | 
						|
      if (notBefore && Date.now() < notAfter) {
 | 
						|
        addLabel("cert-error-not-yet-valid-now", {
 | 
						|
          hostname,
 | 
						|
          "not-before-local-time": formatter.format(new Date(notBefore)),
 | 
						|
        });
 | 
						|
      } else {
 | 
						|
        addLabel("cert-error-expired-now", {
 | 
						|
          hostname,
 | 
						|
          "not-after-local-time": formatter.format(new Date(notAfter)),
 | 
						|
        });
 | 
						|
      }
 | 
						|
      addErrorCodeLink();
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    case "domain-mismatch":
 | 
						|
      getSubjectAltNames(failedCertInfo).then(subjectAltNames => {
 | 
						|
        if (!subjectAltNames.length) {
 | 
						|
          addLabel("cert-error-domain-mismatch", { hostname });
 | 
						|
        } else if (subjectAltNames.length > 1) {
 | 
						|
          const names = subjectAltNames.join(", ");
 | 
						|
          addLabel("cert-error-domain-mismatch-multiple", {
 | 
						|
            hostname,
 | 
						|
            "subject-alt-names": names,
 | 
						|
          });
 | 
						|
        } else {
 | 
						|
          const altName = subjectAltNames[0];
 | 
						|
 | 
						|
          // If the alt name is a wildcard domain ("*.example.com")
 | 
						|
          // let's use "www" instead.  "*.example.com" isn't going to
 | 
						|
          // get anyone anywhere useful. bug 432491
 | 
						|
          const okHost = altName.replace(/^\*\./, "www.");
 | 
						|
 | 
						|
          // Let's check if we want to make this a link.
 | 
						|
          const showLink =
 | 
						|
            /* case #1:
 | 
						|
             * example.com uses an invalid security certificate.
 | 
						|
             *
 | 
						|
             * The certificate is only valid for www.example.com
 | 
						|
             *
 | 
						|
             * Make sure to include the "." ahead of thisHost so that a
 | 
						|
             * MitM attack on paypal.com doesn't hyperlink to "notpaypal.com"
 | 
						|
             *
 | 
						|
             * We'd normally just use a RegExp here except that we lack a
 | 
						|
             * library function to escape them properly (bug 248062), and
 | 
						|
             * domain names are famous for having '.' characters in them,
 | 
						|
             * which would allow spurious and possibly hostile matches.
 | 
						|
             */
 | 
						|
            okHost.endsWith("." + HOST_NAME) ||
 | 
						|
            /* case #2:
 | 
						|
             * browser.garage.maemo.org uses an invalid security certificate.
 | 
						|
             *
 | 
						|
             * The certificate is only valid for garage.maemo.org
 | 
						|
             */
 | 
						|
            HOST_NAME.endsWith("." + okHost);
 | 
						|
 | 
						|
          const l10nArgs = { hostname, "alt-name": altName };
 | 
						|
          if (showLink) {
 | 
						|
            // Set the link if we want it.
 | 
						|
            const proto = document.location.protocol + "//";
 | 
						|
            addLabel("cert-error-domain-mismatch-single", l10nArgs, {
 | 
						|
              href: proto + okHost,
 | 
						|
              "data-l10n-name": "domain-mismatch-link",
 | 
						|
              id: "cert_domain_link",
 | 
						|
            });
 | 
						|
 | 
						|
            // If we set a link, meaning there's something helpful for
 | 
						|
            // the user here, expand the section by default
 | 
						|
            if (getCSSClass() != "expertBadCert") {
 | 
						|
              revealAdvancedPanelSlowlyAsync();
 | 
						|
            }
 | 
						|
          } else {
 | 
						|
            addLabel("cert-error-domain-mismatch-single-nolink", l10nArgs);
 | 
						|
          }
 | 
						|
        }
 | 
						|
        addErrorCodeLink();
 | 
						|
      });
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  getFailedCertificatesAsPEMString().then(pemString => {
 | 
						|
    const errorText = document.getElementById("certificateErrorText");
 | 
						|
    errorText.textContent = pemString;
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
/* Only focus if we're the toplevel frame; otherwise we
 | 
						|
   don't want to call attention to ourselves!
 | 
						|
*/
 | 
						|
function setFocus(selector, position = "afterbegin") {
 | 
						|
  if (window.top == window) {
 | 
						|
    var button = document.querySelector(selector);
 | 
						|
    button.parentNode.insertAdjacentElement(position, button);
 | 
						|
    // It's possible setFocus was called via the DOMContentLoaded event
 | 
						|
    // handler and that the button has no frame. Things without a frame cannot
 | 
						|
    // be focused. We use a requestAnimationFrame to queue up the focus to occur
 | 
						|
    // once the button has its frame.
 | 
						|
    requestAnimationFrame(() => {
 | 
						|
      button.focus({ focusVisible: false });
 | 
						|
    });
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
for (let button of document.querySelectorAll(".try-again")) {
 | 
						|
  button.addEventListener("click", function () {
 | 
						|
    retryThis(this);
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
initPage();
 | 
						|
 | 
						|
// Dispatch this event so tests can detect that we finished loading the error page.
 | 
						|
document.dispatchEvent(new CustomEvent("AboutNetErrorLoad", { bubbles: true }));
 |