mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 15:52:07 +00:00
Patch 1 (main patch) - Bug 583408 - Notify user when the certificate attribute check fails. r=dtownsend, a=blocking2.0-beta6
This commit is contained in:
parent
a3783283b2
commit
9c62c92e57
@ -90,6 +90,23 @@ pref("app.update.timer", 600000);
|
||||
// Enables some extra Application Update Logging (can reduce performance)
|
||||
pref("app.update.log", false);
|
||||
|
||||
// When |app.update.cert.requireBuiltIn| is true or not specified the
|
||||
// final certificate and all certificates the connection is redirected to before
|
||||
// the final certificate for the url specified in the |app.update.url|
|
||||
// preference must be built-in.
|
||||
pref("app.update.cert.requireBuiltIn", true);
|
||||
|
||||
// When |app.update.cert.checkAttributes| is true or not specified the
|
||||
// certificate attributes specified in the |app.update.certs.| preference branch
|
||||
// are checked against the certificate for the url specified by the
|
||||
// |app.update.url| preference.
|
||||
pref("app.update.cert.checkAttributes", true);
|
||||
|
||||
// The number of certificate attribute check failures to allow for background
|
||||
// update checks before notifying the user of the failure. User initiated update
|
||||
// checks always notify the user of the certificate attribute check failure.
|
||||
pref("app.update.cert.maxErrors", 5);
|
||||
|
||||
// The |app.update.certs.| preference branch contains branches that are
|
||||
// sequentially numbered starting at 1 that contain attribute name / value
|
||||
// pairs for the certificate used by the server that hosts the update xml file
|
||||
|
@ -54,11 +54,14 @@ const Cu = Components.utils;
|
||||
*
|
||||
* @param aChannel
|
||||
* The nsIChannel that will have its certificate checked.
|
||||
* @param aCerts
|
||||
* @param aAllowNonBuiltInCerts (optional)
|
||||
* When true certificates that aren't builtin are allowed. When false
|
||||
* or not specified the certificate must be a builtin certificate.
|
||||
* @param aCerts (optional)
|
||||
* An array of JS objects with names / values corresponding to the
|
||||
* channel's expected certificate's attribute names / values. This can
|
||||
* be null or an empty array. If it isn't null the the scheme for the
|
||||
* channel's originalURI must be https.
|
||||
* channel's expected certificate's attribute names / values. If it
|
||||
* isn't null or not specified the the scheme for the channel's
|
||||
* originalURI must be https.
|
||||
* @throws NS_ERROR_UNEXPECTED if a certificate is expected and the URI scheme
|
||||
* is not https.
|
||||
* NS_ERROR_ILLEGAL_VALUE if a certificate attribute name from the
|
||||
@ -66,7 +69,7 @@ const Cu = Components.utils;
|
||||
* from the aCerts param is different than the expected value.
|
||||
* NS_ERROR_ABORT if the certificate issuer is not built-in.
|
||||
*/
|
||||
function checkCert(aChannel, aCerts) {
|
||||
function checkCert(aChannel, aAllowNonBuiltInCerts, aCerts) {
|
||||
if (!aChannel.originalURI.schemeIs("https")) {
|
||||
// Require https if there are certificate values to verify
|
||||
if (aCerts) {
|
||||
@ -112,6 +115,8 @@ function checkCert(aChannel, aCerts) {
|
||||
}
|
||||
}
|
||||
|
||||
if (aAllowNonBuiltInCerts === true)
|
||||
return;
|
||||
|
||||
var issuerCert = cert;
|
||||
while (issuerCert.issuer && !issuerCert.issuer.equals(issuerCert))
|
||||
@ -136,6 +141,10 @@ function isBuiltinToken(tokenName) {
|
||||
* This class implements nsIBadCertListener. Its job is to prevent "bad cert"
|
||||
* security dialogs from being shown to the user. It is better to simply fail
|
||||
* if the certificate is bad. See bug 304286.
|
||||
*
|
||||
* @param aAllowNonBuiltInCerts (optional)
|
||||
* When true certificates that aren't builtin are allowed. When false
|
||||
* or not specified the certificate must be a builtin certificate.
|
||||
*/
|
||||
function BadCertHandler(aAllowNonBuiltInCerts) {
|
||||
this.allowNonBuiltInCerts = aAllowNonBuiltInCerts;
|
||||
|
@ -58,9 +58,9 @@ function testXHRError(aEvent) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function getCheckCertResult(aChannel, aCerts) {
|
||||
function getCheckCertResult(aChannel, aAllowNonBuiltIn, aCerts) {
|
||||
try {
|
||||
checkCert(aChannel, aCerts);
|
||||
checkCert(aChannel, aAllowNonBuiltIn, aCerts);
|
||||
}
|
||||
catch (e) {
|
||||
return e.result;
|
||||
@ -74,18 +74,22 @@ function testXHRLoad(aEvent) {
|
||||
var channel = aEvent.target.channel;
|
||||
|
||||
var certs = null;
|
||||
is(getCheckCertResult(channel, certs), Cr.NS_ERROR_ABORT,
|
||||
is(getCheckCertResult(channel, false, certs), Cr.NS_ERROR_ABORT,
|
||||
"checkCert should throw NS_ERROR_ABORT when the certificate attributes " +
|
||||
"array passed to checkCert is null and the certificate is not builtin");
|
||||
|
||||
is(getCheckCertResult(channel, true, certs), Cr.NS_OK,
|
||||
"checkCert should not throw when the certificate attributes array " +
|
||||
"passed to checkCert is null and builtin certificates aren't enforced");
|
||||
|
||||
certs = [ { invalidAttribute: "Invalid attribute" } ];
|
||||
is(getCheckCertResult(channel, certs), Cr.NS_ERROR_ILLEGAL_VALUE,
|
||||
is(getCheckCertResult(channel, false, certs), Cr.NS_ERROR_ILLEGAL_VALUE,
|
||||
"checkCert should throw NS_ERROR_ILLEGAL_VALUE when the certificate " +
|
||||
"attributes array passed to checkCert has an element that has an " +
|
||||
"attribute that does not exist on the certificate");
|
||||
|
||||
certs = [ { issuerName: "Incorrect issuerName" } ];
|
||||
is(getCheckCertResult(channel, certs), Cr.NS_ERROR_ILLEGAL_VALUE,
|
||||
is(getCheckCertResult(channel, false, certs), Cr.NS_ERROR_ILLEGAL_VALUE,
|
||||
"checkCert should throw NS_ERROR_ILLEGAL_VALUE when the certificate " +
|
||||
"attributes array passed to checkCert has an element that has an " +
|
||||
"issuerName that is not the same as the certificate's");
|
||||
@ -95,35 +99,46 @@ function testXHRLoad(aEvent) {
|
||||
|
||||
certs = [ { issuerName: cert.issuerName,
|
||||
commonName: cert.commonName } ];
|
||||
is(getCheckCertResult(channel, certs), Cr.NS_ERROR_ABORT,
|
||||
is(getCheckCertResult(channel, false, certs), Cr.NS_ERROR_ABORT,
|
||||
"checkCert should throw NS_ERROR_ABORT when the certificate attributes " +
|
||||
"array passed to checkCert has a single element that has the same " +
|
||||
"issuerName and commonName as the certificate's and the certificate is " +
|
||||
"not builtin");
|
||||
|
||||
is(getCheckCertResult(channel, true, certs), Cr.NS_OK,
|
||||
"checkCert should not throw when the certificate attributes array " +
|
||||
"passed to checkCert has a single element that has the same issuerName " +
|
||||
"and commonName as the certificate's and and builtin certificates " +
|
||||
"aren't enforced");
|
||||
|
||||
certs = [ { issuerName: "Incorrect issuerName",
|
||||
invalidAttribute: "Invalid attribute" },
|
||||
{ issuerName: cert.issuerName,
|
||||
commonName: "Invalid Common Name" },
|
||||
{ issuerName: cert.issuerName,
|
||||
commonName: cert.commonName } ];
|
||||
is(getCheckCertResult(channel, certs), Cr.NS_ERROR_ABORT,
|
||||
is(getCheckCertResult(channel, false, certs), Cr.NS_ERROR_ABORT,
|
||||
"checkCert should throw NS_ERROR_ABORT when the certificate attributes " +
|
||||
"array passed to checkCert has an element that has the same issuerName " +
|
||||
"and commonName as the certificate's and the certificate is not builtin");
|
||||
|
||||
is(getCheckCertResult(channel, true, certs), Cr.NS_OK,
|
||||
"checkCert should not throw when the certificate attributes array " +
|
||||
"passed to checkCert has an element that has the same issuerName and " +
|
||||
"commonName as the certificate's and builtin certificates aren't enforced");
|
||||
|
||||
var mockChannel = { originalURI: Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService).
|
||||
newURI("http://example.com/", null, null) };
|
||||
|
||||
certs = [ ];
|
||||
is(getCheckCertResult(mockChannel, certs), Cr.NS_ERROR_UNEXPECTED,
|
||||
is(getCheckCertResult(mockChannel, false, certs), Cr.NS_ERROR_UNEXPECTED,
|
||||
"checkCert should throw NS_ERROR_UNEXPECTED when the certificate " +
|
||||
"attributes array passed to checkCert is not null and the channel's " +
|
||||
"originalURI is not https");
|
||||
|
||||
certs = null;
|
||||
is(getCheckCertResult(mockChannel, certs), Cr.NS_OK,
|
||||
is(getCheckCertResult(mockChannel, false, certs), Cr.NS_OK,
|
||||
"checkCert should not throw when the certificate attributes object " +
|
||||
"passed to checkCert is null and the the channel's originalURI is not " +
|
||||
"https");
|
||||
|
@ -51,6 +51,7 @@ const CoR = Components.results;
|
||||
const XMLNS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
const PREF_APP_UPDATE_BILLBOARD_TEST_URL = "app.update.billboard.test_url";
|
||||
const PREF_APP_UPDATE_CERT_ERRORS = "app.update.cert.errors";
|
||||
const PREF_APP_UPDATE_ENABLED = "app.update.enabled";
|
||||
const PREF_APP_UPDATE_LOG = "app.update.log";
|
||||
const PREF_APP_UPDATE_MANUAL_URL = "app.update.url.manual";
|
||||
@ -72,6 +73,9 @@ const STATE_FAILED = "failed";
|
||||
const SRCEVT_FOREGROUND = 1;
|
||||
const SRCEVT_BACKGROUND = 2;
|
||||
|
||||
const CERT_ATTR_CHECK_FAILED_NO_UPDATE = 100;
|
||||
const CERT_ATTR_CHECK_FAILED_HAS_UPDATE = 101;
|
||||
|
||||
var gLogEnabled = false;
|
||||
var gUpdatesFoundPageId;
|
||||
|
||||
@ -399,6 +403,12 @@ var gUpdates = {
|
||||
// user that the background checking found an update that requires
|
||||
// their permission to install, and it's ready for download.
|
||||
this.setUpdate(arg0);
|
||||
if (this.update.errorCode == CERT_ATTR_CHECK_FAILED_NO_UPDATE ||
|
||||
this.update.errorCode == CERT_ATTR_CHECK_FAILED_HAS_UPDATE) {
|
||||
aCallback("errorcertcheck");
|
||||
return;
|
||||
}
|
||||
|
||||
var p = this.update.selectedPatch;
|
||||
if (p) {
|
||||
var state = p.state;
|
||||
@ -654,7 +664,14 @@ var gCheckingPage = {
|
||||
onError: function(request, update) {
|
||||
LOG("gCheckingPage", "onError - proceeding to error page");
|
||||
gUpdates.setUpdate(update);
|
||||
gUpdates.wiz.goTo("errors");
|
||||
if (update.errorCode &&
|
||||
(update.errorCode == CERT_ATTR_CHECK_FAILED_NO_UPDATE ||
|
||||
update.errorCode == CERT_ATTR_CHECK_FAILED_HAS_UPDATE )) {
|
||||
gUpdates.wiz.goTo("errorcertcheck");
|
||||
}
|
||||
else {
|
||||
gUpdates.wiz.goTo("errors");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1592,6 +1609,34 @@ var gErrorsPage = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The page shown when there is a certificate attribute check error.
|
||||
*/
|
||||
var gErrorCertCheckPage = {
|
||||
/**
|
||||
* Initialize
|
||||
*/
|
||||
onPageShow: function() {
|
||||
gUpdates.setButtons(null, null, "okButton", true);
|
||||
gUpdates.wiz.getButton("finish").focus();
|
||||
|
||||
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CERT_ERRORS))
|
||||
Services.prefs.clearUserPref(PREF_APP_UPDATE_CERT_ERRORS);
|
||||
|
||||
if (gUpdates.update.errorCode == CERT_ATTR_CHECK_FAILED_HAS_UPDATE) {
|
||||
document.getElementById("errorCertAttrHasUpdateLabel").hidden = false;
|
||||
}
|
||||
else {
|
||||
document.getElementById("errorCertCheckNoUpdateLabel").hidden = false;
|
||||
var manualURL = Services.urlFormatter.formatURLPref(PREF_APP_UPDATE_MANUAL_URL);
|
||||
var errorLinkLabel = document.getElementById("errorCertAttrLinkLabel");
|
||||
errorLinkLabel.value = manualURL;
|
||||
errorLinkLabel.setAttribute("url", manualURL);
|
||||
errorLinkLabel.hidden = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The "There was an error applying a partial patch" page.
|
||||
*/
|
||||
|
@ -229,6 +229,22 @@
|
||||
</hbox>
|
||||
</vbox>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="errorcertcheck" pageid="errorcertcheck"
|
||||
object="gErrorCertCheckPage"
|
||||
onpageshow="gErrorCertCheckPage.onPageShow();">
|
||||
<updateheader label="&error.title;"/>
|
||||
<vbox class="update-content" flex="1">
|
||||
<label id="errorCertAttrHasUpdateLabel"
|
||||
hidden="true">&errorCertAttrHasUpdate.label;</label>
|
||||
<label id="errorCertCheckNoUpdateLabel"
|
||||
hidden="true">&errorCertAttrNoUpdate.label;</label>
|
||||
<hbox>
|
||||
<label id="errorCertAttrLinkLabel" class="text-link" hidden="true"
|
||||
value="" onclick="openUpdateURL(event);"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="errorpatching" pageid="errorpatching" next="downloading"
|
||||
object="gErrorPatchingPage"
|
||||
|
@ -251,10 +251,12 @@ interface nsIUpdate : nsISupports
|
||||
|
||||
/**
|
||||
* A numeric error code that conveys additional information about the state
|
||||
* of a failed update. If the update is not in the "failed" state, then this
|
||||
* value is zero.
|
||||
* of a failed update or failed certificate attribute check during an update
|
||||
* check. If the update is not in the "failed" state or the certificate
|
||||
* attribute check has not failed the value is zero.
|
||||
*
|
||||
* TODO: Define typical error codes (for now, see updater/errors.h)
|
||||
* TODO: Define typical error codes (for now, see updater/errors.h and the
|
||||
* CERT_ATTR_CHECK_FAILED_* values in nsUpdateService.js)
|
||||
*/
|
||||
attribute long errorCode;
|
||||
|
||||
|
@ -54,6 +54,10 @@ const Cr = Components.results;
|
||||
const PREF_APP_UPDATE_AUTO = "app.update.auto";
|
||||
const PREF_APP_UPDATE_BACKGROUND_INTERVAL = "app.update.download.backgroundInterval";
|
||||
const PREF_APP_UPDATE_CERTS_BRANCH = "app.update.certs.";
|
||||
const PREF_APP_UPDATE_CERT_CHECKATTRS = "app.update.cert.checkAttributes";
|
||||
const PREF_APP_UPDATE_CERT_ERRORS = "app.update.cert.errors";
|
||||
const PREF_APP_UPDATE_CERT_MAXERRORS = "app.update.cert.maxErrors";
|
||||
const PREF_APP_UPDATE_CERT_REQUIREBUILTIN = "app.update.cert.requireBuiltIn";
|
||||
const PREF_APP_UPDATE_CHANNEL = "app.update.channel";
|
||||
const PREF_APP_UPDATE_ENABLED = "app.update.enabled";
|
||||
const PREF_APP_UPDATE_IDLETIME = "app.update.idletime";
|
||||
@ -114,6 +118,9 @@ const STATE_FAILED = "failed";
|
||||
const WRITE_ERROR = 7;
|
||||
const ELEVATION_CANCELED = 9;
|
||||
|
||||
const CERT_ATTR_CHECK_FAILED_NO_UPDATE = 100;
|
||||
const CERT_ATTR_CHECK_FAILED_HAS_UPDATE = 101;
|
||||
|
||||
const DOWNLOAD_CHUNK_SIZE = 300000; // bytes
|
||||
const DOWNLOAD_BACKGROUND_INTERVAL = 600; // seconds
|
||||
const DOWNLOAD_FOREGROUND_INTERVAL = 0;
|
||||
@ -1192,14 +1199,18 @@ UpdateService.prototype = {
|
||||
cleanupActiveUpdate();
|
||||
}
|
||||
else {
|
||||
// If we hit an error, then the error code will be included in the
|
||||
// status string following a colon. If we had an I/O error, then we
|
||||
// assume that the patch is not invalid, and we restage the patch so
|
||||
// that it can be attempted again the next time we restart.
|
||||
var ary = status.split(": ");
|
||||
// If we hit an error, then the error code will be included in the status
|
||||
// string following a colon and a space. If we had an I/O error, then we
|
||||
// assume that the patch is not invalid, and we re-stage the patch so that
|
||||
// it can be attempted again the next time we restart. This will leave a
|
||||
// space at the beginning of the error code when there is a failure which
|
||||
// will be removed by using parseInt below. This prevents panic which has
|
||||
// occurred numerous times previously (see bug 569642 comment #9 for one
|
||||
// example) when testing releases due to forgetting to include the space.
|
||||
var ary = status.split(":");
|
||||
update.state = ary[0];
|
||||
if (update.state == STATE_FAILED && ary[1]) {
|
||||
update.errorCode = ary[1];
|
||||
update.errorCode = parseInt(ary[1]);
|
||||
if (update.errorCode == WRITE_ERROR) {
|
||||
prompter.showUpdateError(update);
|
||||
writeStatusFile(getUpdatesDir(), update.state = STATE_PENDING);
|
||||
@ -1270,8 +1281,23 @@ UpdateService.prototype = {
|
||||
onError: function AUS_notify_onError(request, update) {
|
||||
LOG("UpdateService:notify:listener - error during background update: " +
|
||||
update.statusText);
|
||||
},
|
||||
}
|
||||
|
||||
if (!update.errorCode ||
|
||||
update.errorCode != CERT_ATTR_CHECK_FAILED_NO_UPDATE &&
|
||||
update.errorCode != CERT_ATTR_CHECK_FAILED_HAS_UPDATE)
|
||||
return;
|
||||
|
||||
var errCount = getPref("getIntPref", PREF_APP_UPDATE_CERT_ERRORS, 0);
|
||||
errCount++;
|
||||
Services.prefs.setIntPref(PREF_APP_UPDATE_CERT_ERRORS, errCount);
|
||||
|
||||
if (errCount >= getPref("getIntPref", PREF_APP_UPDATE_CERT_MAXERRORS, 5)) {
|
||||
var prompter = Cc["@mozilla.org/updates/update-prompt;1"].
|
||||
createInstance(Ci.nsIUpdatePrompt);
|
||||
prompter.showUpdateError(update);
|
||||
}
|
||||
}
|
||||
};
|
||||
this.backgroundChecker.checkForUpdates(listener, false);
|
||||
},
|
||||
|
||||
@ -2002,7 +2028,9 @@ Checker.prototype = {
|
||||
this._request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsIXMLHttpRequest);
|
||||
this._request.open("GET", url, true);
|
||||
this._request.channel.notificationCallbacks = new gCertUtils.BadCertHandler();
|
||||
var allowNonBuiltIn = !getPref("getBoolPref",
|
||||
PREF_APP_UPDATE_CERT_REQUIREBUILTIN, true);
|
||||
this._request.channel.notificationCallbacks = new gCertUtils.BadCertHandler(allowNonBuiltIn);
|
||||
this._request.overrideMimeType("text/xml");
|
||||
this._request.setRequestHeader("Cache-Control", "no-cache");
|
||||
|
||||
@ -2094,6 +2122,7 @@ Checker.prototype = {
|
||||
var prefs = Services.prefs;
|
||||
var certs = null;
|
||||
if (!prefs.prefHasUserValue(PREF_APP_UPDATE_URL_OVERRIDE) &&
|
||||
getPref("getBoolPref", PREF_APP_UPDATE_CERT_CHECKATTRS, true) &&
|
||||
prefs.getBranch(PREF_APP_UPDATE_CERTS_BRANCH).getChildList("").length) {
|
||||
certs = [];
|
||||
let counter = 1;
|
||||
@ -2113,39 +2142,32 @@ Checker.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
var certAttrCheckFailed = false;
|
||||
var status;
|
||||
try {
|
||||
try {
|
||||
gCertUtils.checkCert(this._request.channel, certs);
|
||||
}
|
||||
catch (e) {
|
||||
Components.utils.reportError(e);
|
||||
if (e.result != Cr.NS_ERROR_ILLEGAL_VALUE)
|
||||
throw e;
|
||||
|
||||
certAttrCheckFailed = true;
|
||||
}
|
||||
|
||||
// Analyze the resulting DOM and determine the set of updates. If the
|
||||
// certificate attribute check failed treat it as no updates found until
|
||||
// Bug 583408 is fixed.
|
||||
var updates = certAttrCheckFailed ? [] : this._updates;
|
||||
|
||||
// Analyze the resulting DOM and determine the set of updates.
|
||||
var updates = this._updates;
|
||||
LOG("Checker:onLoad - number of updates available: " + updates.length);
|
||||
var allowNonBuiltIn = !getPref("getBoolPref",
|
||||
PREF_APP_UPDATE_CERT_REQUIREBUILTIN, true);
|
||||
gCertUtils.checkCert(this._request.channel, allowNonBuiltIn, certs);
|
||||
|
||||
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CERT_ERRORS))
|
||||
Services.prefs.clearUserPref(PREF_APP_UPDATE_CERT_ERRORS);
|
||||
|
||||
// Tell the Update Service about the updates
|
||||
this._callback.onCheckComplete(event.target, updates, updates.length);
|
||||
}
|
||||
catch (e) {
|
||||
LOG("Checker:onLoad - there was a problem with the update service URL " +
|
||||
"specified, either the XML file was malformed or it does not exist " +
|
||||
"at the location specified. Exception: " + e);
|
||||
LOG("Checker:onLoad - there was a problem checking for updates. " +
|
||||
"Exception: " + e);
|
||||
var request = event.target;
|
||||
var status = this._getChannelStatus(request);
|
||||
LOG("Checker:onLoad - request.status: " + status);
|
||||
var update = new Update(null);
|
||||
update.statusText = getStatusTextFromCode(status, 404);
|
||||
if (e.result == Cr.NS_ERROR_ILLEGAL_VALUE) {
|
||||
update.errorCode = updates[0] ? CERT_ATTR_CHECK_FAILED_HAS_UPDATE
|
||||
: CERT_ATTR_CHECK_FAILED_NO_UPDATE;
|
||||
}
|
||||
this._callback.onError(request, update);
|
||||
}
|
||||
|
||||
@ -2803,6 +2825,14 @@ UpdatePrompt.prototype = {
|
||||
if (!this._enabled)
|
||||
return;
|
||||
|
||||
if (update.errorCode &&
|
||||
(update.errorCode == CERT_ATTR_CHECK_FAILED_NO_UPDATE ||
|
||||
update.errorCode != CERT_ATTR_CHECK_FAILED_HAS_UPDATE)) {
|
||||
this._showUIWhenIdle(null, URI_UPDATE_PROMPT_DIALOG, null,
|
||||
UPDATE_WINDOW_NAME, null, update);
|
||||
return;
|
||||
}
|
||||
|
||||
// In some cases, we want to just show a simple alert dialog:
|
||||
if (update.state == STATE_FAILED && update.errorCode == WRITE_ERROR) {
|
||||
var title = gUpdateBundle.GetStringFromName("updaterIOErrorTitle");
|
||||
|
Loading…
Reference in New Issue
Block a user