Affected errors: NS_ERROR_OFFLINE NS_ERROR_DOM_COOP_FAILED (blockedByCOOP) NS_ERROR_DOM_COEP_FAILED (blockedByCOEP) NS_ERROR_NET_EMPTY_RESPONSE (netReset) Differential Revision: https://phabricator.services.mozilla.com/D294024
248 lines
8.2 KiB
JavaScript
248 lines
8.2 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/. */
|
|
|
|
/**
|
|
* Utilities for looking up error configurations and resolving dynamic content.
|
|
*
|
|
* Some error configurations require runtime data (e.g., hostname, certificate
|
|
* validity dates, connectivity status). This module provides resolver functions
|
|
* that inject runtime context into static configurations.
|
|
*/
|
|
|
|
import {
|
|
getErrorConfig,
|
|
isErrorSupported,
|
|
} from "chrome://global/content/errors/error-registry.mjs";
|
|
|
|
/**
|
|
* Resolve browser error signals to a canonical registry ID.
|
|
*
|
|
* Handles resolution scenarios in priority order:
|
|
* 1. System state overrides (no connectivity, VPN active)
|
|
* 2. Direct registry lookup by errorCodeString (C++ error strings)
|
|
* 3. Fallback to a shared config when errorCodeString is unregistered
|
|
* 4. Direct registry lookup by gErrorCode (URL-derived error codes)
|
|
*
|
|
* @param {object} signals
|
|
* @param {string} signals.errorCodeString - Raw error string from the security/network layer
|
|
* @param {string} signals.gErrorCode - URL-derived error code (e= parameter)
|
|
* @param {boolean} signals.noConnectivity - System has no network connectivity
|
|
* @param {boolean} signals.vpnActive - VPN is currently active
|
|
* @returns {string|null} Canonical registry ID, or null if the error has no
|
|
* Felt Privacy config.
|
|
*/
|
|
export function resolveErrorID({
|
|
errorCodeString,
|
|
gErrorCode,
|
|
noConnectivity,
|
|
vpnActive,
|
|
}) {
|
|
// System state overrides.
|
|
// Currently these are all network-related.
|
|
if (noConnectivity) {
|
|
return "NS_ERROR_OFFLINE";
|
|
}
|
|
if (gErrorCode === "proxyConnectFailure" && vpnActive) {
|
|
return "vpnFailure";
|
|
}
|
|
|
|
// Default: use errorCodeString if it is set and registered. This value comes
|
|
// from getFailedCertSecurityInfo() or getNetErrorInfo() and identifies the
|
|
// specific error.
|
|
if (errorCodeString && isErrorSupported(errorCodeString)) {
|
|
return errorCodeString;
|
|
}
|
|
|
|
// In some cases, errorCodeString will be set but not registered. This can
|
|
// be done on purpose to allow many different yet similar errors to share a
|
|
// config in the error registry. For example, if we see an unregistered error
|
|
// where gErrorCode is "nssFailure2", we use the nssFailure2 registry entry.
|
|
if (errorCodeString) {
|
|
if (gErrorCode !== "nssFailure2" || !isErrorSupported("nssFailure2")) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// In some cases, errorCodeString will not be set. Use the code in
|
|
// gErrorCode, which is parsed from the URL e= parameter and set by
|
|
// nsDocShell, as a fallback.
|
|
if (gErrorCode && isErrorSupported(gErrorCode)) {
|
|
return gErrorCode;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Check if an error has no action the user can take to fix it.
|
|
*
|
|
* @param {string} id - The error id to check
|
|
* @returns {boolean} True if the error has no user fix
|
|
*/
|
|
export function errorHasNoUserFix(id) {
|
|
const config = getErrorConfig(id);
|
|
return config ? config.hasNoUserFix === true : false;
|
|
}
|
|
|
|
/**
|
|
* Check if an error is supported by the Felt Privacy v1 experience.
|
|
*
|
|
* @param {string} id - The error id to check
|
|
* @returns {boolean} True if the error has a configuration
|
|
*/
|
|
export function isFeltPrivacySupported(id) {
|
|
const config = getErrorConfig(id);
|
|
return !!config;
|
|
}
|
|
|
|
/**
|
|
* Resolve l10n arguments by injecting runtime context.
|
|
*
|
|
* @param {object | null} l10nConfig - The l10n config with { dataL10nId, dataL10nArgs }
|
|
* @param {object} l10nArgValues - Context from the environment during runtime (hostname, errorInfo, etc.)
|
|
* @returns {object | null} Resolved l10n config with dataL10nArgs filled in
|
|
*/
|
|
export function resolveL10nArgs(l10nConfig, l10nArgValues) {
|
|
if (!l10nConfig?.dataL10nArgs) {
|
|
return l10nConfig;
|
|
}
|
|
|
|
const values = {
|
|
hostname: l10nArgValues.hostname,
|
|
path: l10nArgValues.filePath,
|
|
date: Date.now(),
|
|
now: l10nArgValues.now ?? Date.now(),
|
|
errorMessage: l10nArgValues.errorInfo?.errorMessage ?? "",
|
|
errorCodeString: l10nArgValues.errorInfo?.errorCodeString ?? "",
|
|
validHosts: l10nArgValues.domainMismatchNames ?? "",
|
|
mitm: l10nArgValues.mitmName ?? "",
|
|
responsestatus: l10nArgValues.errorInfo?.responseStatus ?? 0,
|
|
responsestatustext: l10nArgValues.errorInfo?.responseStatusText ?? "",
|
|
};
|
|
|
|
if (typeof l10nConfig.dataL10nId === "function") {
|
|
l10nConfig.dataL10nId = l10nConfig.dataL10nId(l10nArgValues);
|
|
}
|
|
|
|
for (const [key, value] of Object.entries(l10nConfig.dataL10nArgs)) {
|
|
if (value === null || value === "") {
|
|
l10nConfig.dataL10nArgs[key] = values[key];
|
|
} else if (typeof value === "function") {
|
|
l10nConfig.dataL10nArgs[key] = value(l10nArgValues);
|
|
}
|
|
}
|
|
return l10nConfig;
|
|
}
|
|
|
|
/**
|
|
* Resolve an array of l10n arguments by injecting runtime context.
|
|
*
|
|
* @param {Array | null} l10nConfig - The l10n config(s) with { dataL10nId, dataL10nArgs }
|
|
* @param {object} l10nArgValues - Context from the environment during runtime (hostname, errorInfo, etc.)
|
|
* @returns {Array | null} Resolved l10n config with dataL10nArgs filled in
|
|
*/
|
|
export function resolveManyL10nArgs(l10nConfigs, l10nArgValues) {
|
|
if (!l10nConfigs) {
|
|
return null;
|
|
}
|
|
for (let i = 0; i < l10nConfigs.length; i++) {
|
|
l10nConfigs[i] = resolveL10nArgs(l10nConfigs[i], l10nArgValues);
|
|
}
|
|
return l10nConfigs;
|
|
}
|
|
|
|
/**
|
|
* Resolve description parts by calling resolver functions for dynamic content.
|
|
*
|
|
* @param {Array|Function} descriptionParts - Static parts array or resolver function
|
|
* @param {object} l10nArgValues - Context from the environment during runtime { noConnectivity, hostname, errorInfo }
|
|
* @returns {Array} Resolved description parts
|
|
*/
|
|
export function resolveDescriptionParts(descriptionParts, l10nArgValues) {
|
|
if (!descriptionParts) {
|
|
return [];
|
|
}
|
|
|
|
if (typeof descriptionParts === "function") {
|
|
return descriptionParts(l10nArgValues);
|
|
}
|
|
|
|
// Static parts - resolve any l10n args
|
|
return descriptionParts.map(part => resolveL10nArgs(part, l10nArgValues));
|
|
}
|
|
|
|
/**
|
|
* Resolve the advanced section configuration.
|
|
*
|
|
* @param {object | null} advancedConfig - The advanced section config
|
|
* @param {object} l10nArgValues - Context from the environment during runtime
|
|
* @returns {object | null} Resolved advanced config
|
|
*/
|
|
export function resolveAdvancedConfig(advancedConfig, l10nArgValues) {
|
|
if (!advancedConfig) {
|
|
return null;
|
|
}
|
|
|
|
const resolved = { ...advancedConfig };
|
|
["whyDangerous", "whatCanYouDo", "learnMore"].forEach(key => {
|
|
if (resolved[key]) {
|
|
resolved[key] = resolveL10nArgs(advancedConfig[key], l10nArgValues);
|
|
}
|
|
});
|
|
return resolved;
|
|
}
|
|
|
|
function resolveCustomNetError(customNetError, l10nArgValues) {
|
|
if (!customNetError) {
|
|
return customNetError;
|
|
}
|
|
const resolved = { ...customNetError };
|
|
if (typeof resolved.whatCanYouDoItems === "function") {
|
|
resolved.whatCanYouDoItems = resolved.whatCanYouDoItems(l10nArgValues);
|
|
}
|
|
if (customNetError.whatCanYouDoL10nArgs) {
|
|
const argsClone = {
|
|
dataL10nArgs: { ...customNetError.whatCanYouDoL10nArgs },
|
|
};
|
|
resolveL10nArgs(argsClone, l10nArgValues);
|
|
resolved.whatCanYouDoL10nArgs = argsClone.dataL10nArgs;
|
|
}
|
|
return resolved;
|
|
}
|
|
|
|
/**
|
|
* Get a fully resolved error configuration with runtime context applied.
|
|
*
|
|
* @param {string} id - The error id to look up
|
|
* @param {object} l10nArgValues - Context from the environment during runtime { hostname, errorInfo, noConnectivity, showOSXPermissionWarning, offline }
|
|
* @returns {object} Fully resolved error configuration
|
|
*/
|
|
export function getResolvedErrorConfig(id, l10nArgValues) {
|
|
const baseConfig = getErrorConfig(id);
|
|
if (!baseConfig) {
|
|
return {};
|
|
}
|
|
|
|
const introContentHandler = Array.isArray(baseConfig.introContent)
|
|
? resolveManyL10nArgs
|
|
: resolveL10nArgs;
|
|
return {
|
|
...baseConfig,
|
|
introContent: introContentHandler(baseConfig.introContent, l10nArgValues),
|
|
shortDescription: resolveL10nArgs(
|
|
baseConfig.shortDescription,
|
|
l10nArgValues
|
|
),
|
|
descriptionParts: resolveDescriptionParts(
|
|
baseConfig.descriptionParts,
|
|
l10nArgValues
|
|
),
|
|
advanced: resolveAdvancedConfig(baseConfig.advanced, l10nArgValues),
|
|
customNetError: resolveCustomNetError(
|
|
baseConfig.customNetError,
|
|
l10nArgValues
|
|
),
|
|
};
|
|
}
|