fx115: Fix "Check for Updates" window

Also:

- Tweak text to be less alarming (fixes #3074)
- Don't show "No updates found" if update was already downloaded
  (fixes #3148)

Download progress still isn't shown (#3130) but I'll fix that later
This commit is contained in:
Dan Stillman 2024-03-21 07:45:42 -04:00
parent b42e412c4f
commit 827bcd704d
4 changed files with 207 additions and 114 deletions

View file

@ -1,10 +1,3 @@
/* Hide the wizard's header so the size of the billboard can size the window
on creation. A custom header will be used in its place when a header is
needed. */
.wizard-header {
display: none;
}
/* Display the custom header */ /* Display the custom header */
.update-header { .update-header {
display: -moz-box !important; display: -moz-box !important;
@ -18,12 +11,6 @@ needed. */
-moz-box-flex: 1; -moz-box-flex: 1;
} }
/* Update History Window */
richlistitem.update {
display: -moz-box;
-moz-box-orient: vertical;
}
.update-name { .update-name {
-moz-box-flex: 1; -moz-box-flex: 1;
} }

View file

@ -78,6 +78,79 @@ function openUpdateURL(event) {
} }
} }
class AbortError extends Error {
constructor(...params) {
super(...params);
this.name = this.constructor.name;
}
}
/**
* `AbortablePromise`s automatically add themselves to this set on construction
* and remove themselves when they settle.
*/
var gPendingAbortablePromises = new Set();
/**
* Creates a Promise that can be resolved immediately with an abort method.
*
* Note that the underlying Promise will probably still run to completion since
* there isn't any general way to abort Promises. So if it is possible to abort
* the operation instead or in addition to using this class, that is preferable.
*/
class AbortablePromise {
#abortFn;
#promise;
#hasCompleted = false;
constructor(promise) {
let abortPromise = new Promise((resolve, reject) => {
this.#abortFn = () => reject(new AbortError());
});
this.#promise = Promise.race([promise, abortPromise]);
this.#promise = this.#promise.finally(() => {
this.#hasCompleted = true;
gPendingAbortablePromises.delete(this);
});
gPendingAbortablePromises.add(this);
}
abort() {
if (this.#hasCompleted) {
return;
}
this.#abortFn();
}
/**
* This can be `await`ed on to get the result of the `AbortablePromise`. It
* will resolve with the value that the Promise provided to the constructor
* resolves with.
*/
get promise() {
return this.#promise;
}
/**
* Will be `true` if the Promise provided to the constructor has resolved or
* `abort()` has been called. Otherwise `false`.
*/
get hasCompleted() {
return this.#hasCompleted;
}
}
function makeAbortable(promise) {
let abortable = new AbortablePromise(promise);
return abortable.promise;
}
function abortAllPromises() {
for (const promise of gPendingAbortablePromises) {
promise.abort();
}
}
/** /**
* A set of shared data and control functions for the wizard as a whole. * A set of shared data and control functions for the wizard as a whole.
*/ */
@ -522,8 +595,9 @@ var gUpdates = {
var um = Cc["@mozilla.org/updates/update-manager;1"].getService( var um = Cc["@mozilla.org/updates/update-manager;1"].getService(
Ci.nsIUpdateManager Ci.nsIUpdateManager
); );
if (um.activeUpdate) { let activeUpdate = um.downloadingUpdate || um.readyUpdate;
this.setUpdate(um.activeUpdate); if (activeUpdate) {
this.setUpdate(activeUpdate);
aCallback("downloading"); aCallback("downloading");
return; return;
} }
@ -569,61 +643,57 @@ var gCheckingPage = {
/** /**
* Initialize * Initialize
*/ */
onPageShow() { async onPageShow() {
gUpdates.setButtons(null, null, null, false, true); gUpdates.setButtons(null, null, null, false, true);
gUpdates.wiz.getButton("cancel").focus(); gUpdates.wiz.getButton("cancel").focus();
// Clear elevation never prefs to handle the scenario where the user clicked try {
// "never" for an update and then canceled a manual update check. If the
// preference isn't cleared then future notifications will never happen.
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) {
Services.prefs.clearUserPref(PREF_APP_UPDATE_ELEVATE_NEVER);
}
// The user will be notified if there is an error so clear the background
// check error count.
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS)) {
Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDERRORS);
}
// The preference will be set back to true if the system is still
// unsupported.
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED)) {
Services.prefs.clearUserPref(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED);
}
this._checker = Cc["@mozilla.org/updates/update-checker;1"].createInstance( this._checker = Cc["@mozilla.org/updates/update-checker;1"].createInstance(
Ci.nsIUpdateChecker Ci.nsIUpdateChecker
); );
this._checker.checkForUpdates(this.updateListener, true); let check = await this._checker.checkForUpdates(this._checker.FOREGROUND_CHECK);
}, let result;
try {
result = await makeAbortable(check.result);
}
catch (e) {
// If we are aborting, stop the update check on our way out.
if (e instanceof AbortError) {
this._checker.stopCheck(check.id);
}
throw e;
}
/** if (!result.checksAllowed) {
* The user has closed the window, either by pressing cancel or using a Window // This shouldn't happen. The cases where this can happen should be
* Manager control, so stop checking for updates. // handled specifically, above.
*/ LOG("gCheckingPage:onPageShow - !checksAllowed; INTERNAL_ERROR");
onWizardCancel() { gUpdates.wiz.goTo("errors");
this._checker.stopCurrentCheck(); return;
}, }
if (!result.succeeded) {
LOG("gCheckingPage:onPageShow - Update check failed; CHECKING_FAILED");
gUpdates.wiz.goTo("errors");
return;
}
LOG("gCheckingPage:onPageShow - Update check succeeded");
gUpdates.setUpdate(gAUS.selectUpdate(result.updates));
if (!gUpdates.update) {
LOG("gCheckingPage:onPageShow - result: NO_UPDATES_FOUND");
gUpdates.wiz.goTo("noupdatesfound");
return;
}
/**
* An object implementing nsIUpdateCheckListener that is notified as the
* update check commences.
*/
updateListener: {
/**
* See nsIUpdateCheckListener
*/
async onCheckComplete(request, updates) {
gUpdates.setUpdate(gAUS.selectUpdate(updates));
if (gUpdates.update) {
LOG("gCheckingPage", "onCheckComplete - update found");
if (gUpdates.update.unsupported) { if (gUpdates.update.unsupported) {
LOG("gCheckingPage:onPageShow - result: UNSUPPORTED SYSTEM");
gUpdates.wiz.goTo("unsupported"); gUpdates.wiz.goTo("unsupported");
return; return;
} }
if (gUpdates.update.elevationFailure) { if (gUpdates.update.elevationFailure) {
LOG("gCheckingPage:onPageShow - result: elevationFailure")
// Prevent multiple notifications for the same update when the client // Prevent multiple notifications for the same update when the client
// has had an elevation failure. // has had an elevation failure.
gUpdates.never(); gUpdates.never();
@ -632,31 +702,62 @@ var gCheckingPage = {
} }
if (!gAUS.canApplyUpdates) { if (!gAUS.canApplyUpdates) {
LOG("gCheckingPage:onPageShow - result: MANUAL_UPDATE");
gUpdates.wiz.goTo("manualUpdate"); gUpdates.wiz.goTo("manualUpdate");
return; return;
} }
gUpdates.wiz.goTo(gUpdates.updatesFoundPageId); /*let updateAuto = await makeAbortable(
return; lazy.UpdateUtils.getAppUpdateAutoEnabled()
);
if (!updateAuto || this.aus.manualUpdateOnly) {
LOG(
"gCheckingPage:onPageShow - Need to wait for user approval to start the " +
"download."
);
let downloadPermissionPromise = new Promise(resolve => {
this.#permissionToDownloadGivenFn = resolve;
});
// There are other interfaces through which the user can start the
// download, so we want to listen both for permission, and for the
// download to independently start.
let downloadStartPromise = Promise.race([
downloadPermissionPromise,
this.aus.stateTransition,
]);
this.#setStatus(AppUpdater.STATUS.DOWNLOAD_AND_INSTALL);
await makeAbortable(downloadStartPromise);
LOG("gCheckingPage:onPageShow - Got user approval. Proceeding with download");
// If we resolved because of `aus.stateTransition`, we may actually be
// downloading a different update now.
if (this.um.downloadingUpdate) {
this.#update = this.um.downloadingUpdate;
} }
} else {
LOG("gCheckingPage", "onCheckComplete - no update found"); LOG(
gUpdates.wiz.goTo("noupdatesfound"); "gCheckingPage:onPageShow - updateAuto is active and " +
}, "manualUpdateOnlydateOnly is inactive. Start the download."
);
/** }
* See nsIUpdateCheckListener await this.#downloadUpdate();*/
*/ gUpdates.wiz.goTo(gUpdates.updatesFoundPageId);
async onError(request, update) { }
LOG("gCheckingPage", "onError - proceeding to error page"); catch (e) {
gUpdates.setUpdate(update); LOG("gCheckingPage:onPageShow - result: Exception");
LOG(e);
gUpdates.wiz.goTo("errors"); gUpdates.wiz.goTo("errors");
}
}, },
/** /**
* See nsISupports.idl * The user has closed the window, either by pressing cancel or using a Window
* Manager control, so stop checking for updates.
*/ */
QueryInterface: ChromeUtils.generateQI(["nsIUpdateCheckListener"]), onWizardCancel() {
this._checker.stopCurrentCheck();
}, },
}; };
@ -732,7 +833,7 @@ var gUpdatesFoundBasicPage = {
/** /**
* Initialize * Initialize
*/ */
onPageShow() { async onPageShow() {
gUpdates.wiz.canRewind = false; gUpdates.wiz.canRewind = false;
var update = gUpdates.update; var update = gUpdates.update;
gUpdates.setButtons( gUpdates.setButtons(
@ -745,23 +846,21 @@ var gUpdatesFoundBasicPage = {
btn.focus(); btn.focus();
var updateName = update.name; var updateName = update.name;
if (update.channel == "nightly") {
updateName = gUpdates.getAUSString("updateNightlyName", [
gUpdates.brandName,
update.displayVersion,
update.buildID,
]);
}
var updateNameElement = document.getElementById("updateName"); var updateNameElement = document.getElementById("updateName");
updateNameElement.value = updateName; updateNameElement.value = updateName;
var introText = gUpdates.getAUSString("intro_" + update.type, [ var introElem = document.getElementById("updatesFoundIntro");
introElem.setAttribute("severity", update.type);
if (update.type == 'major') {
let introText = gUpdates.getAUSString("intro_" + update.type, [
gUpdates.brandName, gUpdates.brandName,
update.displayVersion, update.displayVersion,
]); ]);
var introElem = document.getElementById("updatesFoundInto");
introElem.setAttribute("severity", update.type);
introElem.textContent = introText; introElem.textContent = introText;
}
else {
document.l10n.setAttributes(introElem, 'update-updates-found-intro-minor');
}
var updateMoreInfoURL = document.getElementById("updateMoreInfoURL"); var updateMoreInfoURL = document.getElementById("updateMoreInfoURL");
if (update.detailsURL) { if (update.detailsURL) {
@ -823,18 +922,16 @@ var gDownloadingPage = {
var um = Cc["@mozilla.org/updates/update-manager;1"].getService( var um = Cc["@mozilla.org/updates/update-manager;1"].getService(
Ci.nsIUpdateManager Ci.nsIUpdateManager
); );
var activeUpdate = um.activeUpdate; var activeUpdate = um.downloadingUpdate || um.readyUpdate;
if (activeUpdate) { if (activeUpdate) {
gUpdates.setUpdate(activeUpdate); gUpdates.setUpdate(activeUpdate);
// It's possible the update has already been downloaded and is being // It's possible the update has already been downloaded and is being
// applied by the time this page is shown, depending on how fast the // applied by the time this page is shown, depending on how fast the
// download goes and how quickly the 'next' button is clicked to get here. // download goes and how quickly the 'next' button is clicked to get here.
if ( if (activeUpdate.state == STATE_PENDING
activeUpdate.state == STATE_PENDING || || activeUpdate.state == STATE_PENDING_ELEVATE
activeUpdate.state == STATE_PENDING_ELEVATE || || activeUpdate.state == STATE_PENDING_SERVICE) {
activeUpdate.state == STATE_PENDING_SERVICE
) {
if (!activeUpdate.getProperty("stagingFailed")) { if (!activeUpdate.getProperty("stagingFailed")) {
gUpdates.setButtons("hideButton", null, null, false); gUpdates.setButtons("hideButton", null, null, false);
gUpdates.wiz.getButton("extra1").focus(); gUpdates.wiz.getButton("extra1").focus();
@ -846,6 +943,11 @@ var gDownloadingPage = {
gUpdates.wiz.goTo("finished"); gUpdates.wiz.goTo("finished");
return; return;
} }
else if (activeUpdate.state == STATE_APPLIED
|| activeUpdate.state == STATE_APPLIED_SERVICE) {
gUpdates.wiz.goTo("finished");
return;
}
} }
if (!gUpdates.update) { if (!gUpdates.update) {
@ -978,7 +1080,7 @@ var gDownloadingPage = {
var um = Cc["@mozilla.org/updates/update-manager;1"].getService( var um = Cc["@mozilla.org/updates/update-manager;1"].getService(
Ci.nsIUpdateManager Ci.nsIUpdateManager
); );
um.activeUpdate = gUpdates.update; um.readyUpdate = gUpdates.update;
// Continue download in the background at full speed. // Continue download in the background at full speed.
LOG( LOG(
@ -1161,7 +1263,7 @@ var gErrorsPage = {
gUpdates.setButtons(null, null, "okButton", true); gUpdates.setButtons(null, null, "okButton", true);
gUpdates.wiz.getButton("finish").focus(); gUpdates.wiz.getButton("finish").focus();
var statusText = gUpdates.update.statusText; var statusText = gUpdates.update?.statusText || "";
LOG("gErrorsPage", "onPageShow - update.statusText: " + statusText); LOG("gErrorsPage", "onPageShow - update.statusText: " + statusText);
var errorReason = document.getElementById("errorReason"); var errorReason = document.getElementById("errorReason");

View file

@ -29,6 +29,7 @@
<wizard width="500" height="400"> <wizard width="500" height="400">
<linkset> <linkset>
<html:link rel="localization" href="zotero.ftl"/>
<html:link rel="localization" href="toolkit/global/wizard.ftl"/> <html:link rel="localization" href="toolkit/global/wizard.ftl"/>
</linkset> </linkset>
@ -91,11 +92,11 @@
object="gUpdatesFoundBasicPage" next="downloading"> object="gUpdatesFoundBasicPage" next="downloading">
<update-header id="updatesFoundBasicHeader" label=""/> <update-header id="updatesFoundBasicHeader" label=""/>
<vbox class="update-content" flex="1"> <vbox class="update-content" flex="1">
<label id="updatesFoundInto"/> <label id="updatesFoundIntro"/>
<separator class="thin"/> <separator class="thin"/>
<label id="updateName" crop="right" value=""/> <label id="updateName" crop="right" value=""/>
<separator id="updateNameSep" class="thin"/> <separator id="updateNameSep" class="thin"/>
<label id="upgradeEvangelism">&evangelism.desc;</label> <label data-l10n-id="update-updates-found-desc"/>
<separator id="upgradeEvangelismSep" flex="1"/> <separator id="upgradeEvangelismSep" flex="1"/>
<vbox flex="1"> <vbox flex="1">
<hbox id="moreInfoURL"> <hbox id="moreInfoURL">

View file

@ -104,6 +104,9 @@ itembox-button-options =
reader-use-dark-mode-for-content = reader-use-dark-mode-for-content =
.label = Use Dark Mode for Content .label = Use Dark Mode for Content
update-updates-found-intro-minor = An update for { -app-name } is available:
update-updates-found-desc = It is recommended that you apply this update as soon as possible.
import-window = import-window =
.title = Import .title = Import