331 lines
10 KiB
JavaScript
331 lines
10 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/. */
|
|
|
|
"use strict";
|
|
|
|
const { setText, viewCertHelper, checkCertHelper } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/psm/pippki.sys.mjs"
|
|
);
|
|
|
|
var gDialog;
|
|
var gSecInfo;
|
|
var gCert;
|
|
var gChecking;
|
|
var gBroken;
|
|
var gNeedReset;
|
|
|
|
const { PrivateBrowsingUtils } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/PrivateBrowsingUtils.sys.mjs"
|
|
);
|
|
|
|
function initExceptionDialog() {
|
|
gNeedReset = false;
|
|
gDialog = document.getElementById("exceptiondialog");
|
|
let warningText = document.getElementById("warningText");
|
|
document.l10n.setAttributes(warningText, "add-exception-branded-warning");
|
|
let confirmButton = gDialog.getButton("extra1");
|
|
let l10nUpdatedElements = [confirmButton, warningText];
|
|
confirmButton.disabled = true;
|
|
|
|
document
|
|
.getElementById("locationTextBox")
|
|
.addEventListener("input", () => handleTextChange());
|
|
document
|
|
.getElementById("viewCertButton")
|
|
.addEventListener("input", () => viewCertButtonClick());
|
|
|
|
var args = window.arguments;
|
|
if (args && args[0]) {
|
|
if (args[0].location) {
|
|
// We were pre-seeded with a location.
|
|
document.getElementById("locationTextBox").value = args[0].location;
|
|
document.getElementById("checkCertButton").disabled = false;
|
|
|
|
if (args[0].securityInfo) {
|
|
gSecInfo = args[0].securityInfo;
|
|
gCert = gSecInfo.serverCert;
|
|
gBroken = true;
|
|
l10nUpdatedElements = l10nUpdatedElements.concat(updateCertStatus());
|
|
} else if (args[0].prefetchCert) {
|
|
// We can optionally pre-fetch the certificate too. Don't do this
|
|
// synchronously, since it would prevent the window from appearing
|
|
// until the fetch is completed, which could be multiple seconds.
|
|
// Instead, let's use a timer to spawn the actual fetch, but update
|
|
// the dialog to "checking..." state right away, so that the UI
|
|
// is appropriately responsive. Bug 453855
|
|
document.getElementById("checkCertButton").disabled = true;
|
|
gChecking = true;
|
|
l10nUpdatedElements = l10nUpdatedElements.concat(updateCertStatus());
|
|
|
|
window.setTimeout(checkCert, 0);
|
|
}
|
|
}
|
|
|
|
// Set out parameter to false by default
|
|
args[0].exceptionAdded = false;
|
|
}
|
|
|
|
for (let id of [
|
|
"warningSupplemental",
|
|
"certLocationLabel",
|
|
"checkCertButton",
|
|
"statusDescription",
|
|
"statusLongDescription",
|
|
"viewCertButton",
|
|
"permanent",
|
|
]) {
|
|
let element = document.getElementById(id);
|
|
l10nUpdatedElements.push(element);
|
|
}
|
|
|
|
document.l10n
|
|
.translateElements(l10nUpdatedElements)
|
|
.then(() => window.sizeToContent());
|
|
|
|
document.addEventListener("dialogextra1", addException);
|
|
document.addEventListener("dialogextra2", checkCert);
|
|
}
|
|
|
|
/**
|
|
* Helper function for checkCert. Set as the onerror/onload callbacks for an
|
|
* XMLHttpRequest. Sets gSecInfo, gCert, gBroken, and gChecking according to
|
|
* the load information from the request. Probably should not be used directly.
|
|
*
|
|
* @param {XMLHttpRequest} req
|
|
* The XMLHttpRequest created and sent by checkCert.
|
|
* @param {Event} evt
|
|
* The load or error event.
|
|
*/
|
|
function grabCert(req, evt) {
|
|
if (req.channel && req.channel.securityInfo) {
|
|
gSecInfo = req.channel.securityInfo;
|
|
gCert = gSecInfo ? gSecInfo.serverCert : null;
|
|
}
|
|
gBroken = evt.type == "error";
|
|
gChecking = false;
|
|
document.l10n
|
|
.translateElements(updateCertStatus())
|
|
.then(() => window.sizeToContent());
|
|
}
|
|
|
|
/**
|
|
* Attempt to download the certificate for the location specified, and populate
|
|
* the Certificate Status section with the result.
|
|
*/
|
|
async function checkCert() {
|
|
gCert = null;
|
|
gSecInfo = null;
|
|
gChecking = true;
|
|
gBroken = false;
|
|
await document.l10n.translateElements(updateCertStatus());
|
|
window.sizeToContent();
|
|
|
|
let uri = getURI();
|
|
|
|
if (uri) {
|
|
checkCertHelper(uri, grabCert);
|
|
} else {
|
|
gChecking = false;
|
|
await document.l10n.translateElements(updateCertStatus());
|
|
window.sizeToContent();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Build and return a URI, based on the information supplied in the
|
|
* Certificate Location fields
|
|
*
|
|
* @returns {nsIURI}
|
|
* URI constructed from the information supplied on success, null
|
|
* otherwise.
|
|
*/
|
|
function getURI() {
|
|
// Use fixup service instead of just ioservice's newURI since it's quite
|
|
// likely that the host will be supplied without a protocol prefix, resulting
|
|
// in malformed uri exceptions being thrown.
|
|
let locationTextBox = document.getElementById("locationTextBox");
|
|
let { preferredURI: uri } = Services.uriFixup.getFixupURIInfo(
|
|
locationTextBox.value
|
|
);
|
|
|
|
if (!uri) {
|
|
return null;
|
|
}
|
|
|
|
let mutator = uri.mutate();
|
|
if (uri.scheme == "http") {
|
|
mutator.setScheme("https");
|
|
}
|
|
|
|
if (uri.port == -1) {
|
|
mutator.setPort(443);
|
|
}
|
|
|
|
return mutator.finalize();
|
|
}
|
|
|
|
function resetDialog() {
|
|
document.getElementById("viewCertButton").disabled = true;
|
|
document.getElementById("permanent").disabled = true;
|
|
gDialog.getButton("extra1").disabled = true;
|
|
setText(document, "headerDescription", "");
|
|
setText(document, "statusDescription", "");
|
|
setText(document, "statusLongDescription", "");
|
|
setText(document, "status2Description", "");
|
|
setText(document, "status2LongDescription", "");
|
|
setText(document, "status3Description", "");
|
|
setText(document, "status3LongDescription", "");
|
|
window.sizeToContent();
|
|
}
|
|
|
|
/**
|
|
* Called by input textboxes to manage UI state
|
|
*/
|
|
function handleTextChange() {
|
|
var checkCertButton = document.getElementById("checkCertButton");
|
|
checkCertButton.disabled = !document.getElementById("locationTextBox").value;
|
|
if (gNeedReset) {
|
|
gNeedReset = false;
|
|
resetDialog();
|
|
}
|
|
}
|
|
|
|
function updateCertStatus() {
|
|
var shortDesc, longDesc;
|
|
let l10nUpdatedElements = [];
|
|
if (gCert) {
|
|
if (gBroken) {
|
|
var mms = "add-exception-domain-mismatch-short";
|
|
var mml = "add-exception-domain-mismatch-long";
|
|
var exs = "add-exception-expired-short";
|
|
var exl = "add-exception-expired-long";
|
|
var uts = "add-exception-unverified-or-bad-signature-short";
|
|
var utl = "add-exception-unverified-or-bad-signature-long";
|
|
if (
|
|
gSecInfo.overridableErrorCategory ==
|
|
Ci.nsITransportSecurityInfo.ERROR_TRUST
|
|
) {
|
|
shortDesc = uts;
|
|
longDesc = utl;
|
|
} else if (
|
|
gSecInfo.overridableErrorCategory ==
|
|
Ci.nsITransportSecurityInfo.ERROR_DOMAIN
|
|
) {
|
|
shortDesc = mms;
|
|
longDesc = mml;
|
|
} else if (
|
|
gSecInfo.overridableErrorCategory ==
|
|
Ci.nsITransportSecurityInfo.ERROR_TIME
|
|
) {
|
|
shortDesc = exs;
|
|
longDesc = exl;
|
|
}
|
|
// In these cases, we do want to enable the "Add Exception" button
|
|
gDialog.getButton("extra1").disabled = false;
|
|
|
|
// If the Private Browsing service is available and the mode is active,
|
|
// don't store permanent exceptions, since they would persist after
|
|
// private browsing mode was disabled.
|
|
var inPrivateBrowsing = inPrivateBrowsingMode();
|
|
var pe = document.getElementById("permanent");
|
|
pe.disabled = inPrivateBrowsing;
|
|
pe.checked = !inPrivateBrowsing;
|
|
|
|
let headerDescription = document.getElementById("headerDescription");
|
|
document.l10n.setAttributes(
|
|
headerDescription,
|
|
"add-exception-invalid-header"
|
|
);
|
|
l10nUpdatedElements.push(headerDescription);
|
|
} else {
|
|
shortDesc = "add-exception-valid-short";
|
|
longDesc = "add-exception-valid-long";
|
|
gDialog.getButton("extra1").disabled = true;
|
|
document.getElementById("permanent").disabled = true;
|
|
}
|
|
|
|
// We're done checking the certificate, so allow the user to check it again.
|
|
document.getElementById("checkCertButton").disabled = false;
|
|
document.getElementById("viewCertButton").disabled = false;
|
|
|
|
// Notify observers about the availability of the certificate
|
|
Services.obs.notifyObservers(null, "cert-exception-ui-ready");
|
|
} else if (gChecking) {
|
|
shortDesc = "add-exception-checking-short";
|
|
longDesc = "add-exception-checking-long";
|
|
// We're checking the certificate, so we disable the Get Certificate
|
|
// button to make sure that the user can't interrupt the process and
|
|
// trigger another certificate fetch.
|
|
document.getElementById("checkCertButton").disabled = true;
|
|
document.getElementById("viewCertButton").disabled = true;
|
|
gDialog.getButton("extra1").disabled = true;
|
|
document.getElementById("permanent").disabled = true;
|
|
} else {
|
|
shortDesc = "add-exception-no-cert-short";
|
|
longDesc = "add-exception-no-cert-long";
|
|
// We're done checking the certificate, so allow the user to check it again.
|
|
document.getElementById("checkCertButton").disabled = false;
|
|
document.getElementById("viewCertButton").disabled = true;
|
|
gDialog.getButton("extra1").disabled = true;
|
|
document.getElementById("permanent").disabled = true;
|
|
}
|
|
let statusDescription = document.getElementById("statusDescription");
|
|
let statusLongDescription = document.getElementById("statusLongDescription");
|
|
document.l10n.setAttributes(statusDescription, shortDesc);
|
|
document.l10n.setAttributes(statusLongDescription, longDesc);
|
|
l10nUpdatedElements.push(statusDescription);
|
|
l10nUpdatedElements.push(statusLongDescription);
|
|
|
|
gNeedReset = true;
|
|
return l10nUpdatedElements;
|
|
}
|
|
|
|
/**
|
|
* Handle user request to display certificate details
|
|
*/
|
|
function viewCertButtonClick() {
|
|
if (gCert) {
|
|
viewCertHelper(this, gCert);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle user request to add an exception for the specified cert
|
|
*/
|
|
function addException() {
|
|
if (!gCert || !gSecInfo) {
|
|
return;
|
|
}
|
|
|
|
var overrideService = Cc["@mozilla.org/security/certoverride;1"].getService(
|
|
Ci.nsICertOverrideService
|
|
);
|
|
var permanentCheckbox = document.getElementById("permanent");
|
|
var shouldStorePermanently =
|
|
permanentCheckbox.checked && !inPrivateBrowsingMode();
|
|
var uri = getURI();
|
|
overrideService.rememberValidityOverride(
|
|
uri.asciiHost,
|
|
uri.port,
|
|
{},
|
|
gCert,
|
|
!shouldStorePermanently
|
|
);
|
|
|
|
let args = window.arguments;
|
|
if (args && args[0]) {
|
|
args[0].exceptionAdded = true;
|
|
}
|
|
|
|
gDialog.acceptDialog();
|
|
}
|
|
|
|
/**
|
|
* @returns {boolean} Whether this dialog is in private browsing mode.
|
|
*/
|
|
function inPrivateBrowsingMode() {
|
|
return PrivateBrowsingUtils.isWindowPrivate(window);
|
|
}
|
|
|
|
window.addEventListener("load", () => initExceptionDialog());
|