diff options
Diffstat (limited to 'browser/base/content/aboutDialog-appUpdater.js')
-rw-r--r-- | browser/base/content/aboutDialog-appUpdater.js | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/browser/base/content/aboutDialog-appUpdater.js b/browser/base/content/aboutDialog-appUpdater.js new file mode 100644 index 0000000000..21bf83bc42 --- /dev/null +++ b/browser/base/content/aboutDialog-appUpdater.js @@ -0,0 +1,300 @@ +/* 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/. */ + +// Note: this file is included in aboutDialog.xhtml and preferences/advanced.xhtml +// if MOZ_UPDATER is defined. + +/* import-globals-from aboutDialog.js */ + +var { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); + +ChromeUtils.defineESModuleGetters(this, { + AppUpdater: "resource://gre/modules/AppUpdater.sys.mjs", + DownloadUtils: "resource://gre/modules/DownloadUtils.sys.mjs", + UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs", +}); + +XPCOMUtils.defineLazyServiceGetter( + this, + "AUS", + "@mozilla.org/updates/update-service;1", + "nsIApplicationUpdateService" +); + +var UPDATING_MIN_DISPLAY_TIME_MS = 1500; + +var gAppUpdater; + +function onUnload(aEvent) { + if (gAppUpdater) { + gAppUpdater.destroy(); + gAppUpdater = null; + } +} + +function appUpdater(options = {}) { + this._appUpdater = new AppUpdater(); + + this._appUpdateListener = (status, ...args) => { + this._onAppUpdateStatus(status, ...args); + }; + this._appUpdater.addListener(this._appUpdateListener); + + this.options = options; + this.updatingMinDisplayTimerId = null; + this.updateDeck = document.getElementById("updateDeck"); + + this.bundle = Services.strings.createBundle( + "chrome://browser/locale/browser.properties" + ); + + try { + let manualURL = new URL( + Services.urlFormatter.formatURLPref("app.update.url.manual") + ); + + for (const manualLink of document.querySelectorAll(".manualLink")) { + // Strip hash and search parameters for display text. + let displayUrl = manualURL.origin + manualURL.pathname; + manualLink.href = manualURL.href; + document.l10n.setArgs(manualLink.closest("[data-l10n-id]"), { + displayUrl, + }); + } + + document.getElementById("failedLink").href = manualURL.href; + } catch (e) { + console.error("Invalid manual update url.", e); + } + + this._appUpdater.check(); +} + +appUpdater.prototype = { + destroy() { + this.stopCurrentCheck(); + if (this.updatingMinDisplayTimerId) { + clearTimeout(this.updatingMinDisplayTimerId); + } + }, + + stopCurrentCheck() { + this._appUpdater.removeListener(this._appUpdateListener); + this._appUpdater.stop(); + }, + + get update() { + return this._appUpdater.update; + }, + + get selectedPanel() { + return this.updateDeck.selectedPanel; + }, + + _onAppUpdateStatus(status, ...args) { + switch (status) { + case AppUpdater.STATUS.UPDATE_DISABLED_BY_POLICY: + this.selectPanel("policyDisabled"); + break; + case AppUpdater.STATUS.READY_FOR_RESTART: + this.selectPanel("apply"); + break; + case AppUpdater.STATUS.OTHER_INSTANCE_HANDLING_UPDATES: + this.selectPanel("otherInstanceHandlingUpdates"); + break; + case AppUpdater.STATUS.DOWNLOADING: { + const downloadStatus = document.getElementById("downloading"); + if (!args.length) { + // Very early in the DOWNLOADING state, `selectedPatch` may not be + // available yet. But this function will be called again when it is + // available. A `maxSize < 0` indicates that the max size is not yet + // available. + let maxSize = -1; + if (this.update.selectedPatch) { + maxSize = this.update.selectedPatch.size; + } + const transfer = DownloadUtils.getTransferTotal(0, maxSize); + document.l10n.setArgs(downloadStatus, { transfer }); + this.selectPanel("downloading"); + } else { + let [progress, max] = args; + const transfer = DownloadUtils.getTransferTotal(progress, max); + document.l10n.setArgs(downloadStatus, { transfer }); + } + break; + } + case AppUpdater.STATUS.STAGING: + this.selectPanel("applying"); + break; + case AppUpdater.STATUS.CHECKING: { + this.checkingForUpdatesDelayPromise = new Promise(resolve => { + this.updatingMinDisplayTimerId = setTimeout( + resolve, + UPDATING_MIN_DISPLAY_TIME_MS + ); + }); + if (Services.policies.isAllowed("appUpdate")) { + this.selectPanel("checkingForUpdates"); + } else { + this.selectPanel("policyDisabled"); + } + break; + } + case AppUpdater.STATUS.CHECKING_FAILED: + this.selectPanel("checkingFailed"); + break; + case AppUpdater.STATUS.NO_UPDATES_FOUND: + this.checkingForUpdatesDelayPromise.then(() => { + if (Services.policies.isAllowed("appUpdate")) { + this.selectPanel("noUpdatesFound"); + } else { + this.selectPanel("policyDisabled"); + } + }); + break; + case AppUpdater.STATUS.UNSUPPORTED_SYSTEM: + if (this.update.detailsURL) { + let unsupportedLink = document.getElementById("unsupportedLink"); + unsupportedLink.href = this.update.detailsURL; + } + this.selectPanel("unsupportedSystem"); + break; + case AppUpdater.STATUS.MANUAL_UPDATE: + this.selectPanel("manualUpdate"); + break; + case AppUpdater.STATUS.DOWNLOAD_AND_INSTALL: + this.selectPanel("downloadAndInstall"); + break; + case AppUpdater.STATUS.DOWNLOAD_FAILED: + this.selectPanel("downloadFailed"); + break; + case AppUpdater.STATUS.INTERNAL_ERROR: + this.selectPanel("internalError"); + break; + case AppUpdater.STATUS.NEVER_CHECKED: + this.selectPanel("checkForUpdates"); + break; + case AppUpdater.STATUS.NO_UPDATER: + default: + this.selectPanel("noUpdater"); + break; + } + }, + + /** + * Sets the panel of the updateDeck and the visibility of icons + * in the #icons element. + * + * @param aChildID + * The id of the deck's child to select, e.g. "apply". + */ + selectPanel(aChildID) { + let panel = document.getElementById(aChildID); + let icons = document.getElementById("icons"); + if (icons) { + icons.className = aChildID; + } + + // Make sure to select the panel before potentially auto-focusing the button. + this.updateDeck.selectedPanel = panel; + + let button = panel.querySelector("button"); + if (button) { + if (aChildID == "downloadAndInstall") { + let updateVersion = gAppUpdater.update.displayVersion; + // Include the build ID if this is an "a#" (nightly or aurora) build + if (/a\d+$/.test(updateVersion)) { + let buildID = gAppUpdater.update.buildID; + let year = buildID.slice(0, 4); + let month = buildID.slice(4, 6); + let day = buildID.slice(6, 8); + updateVersion += ` (${year}-${month}-${day})`; + } + button.label = this.bundle.formatStringFromName( + "update.downloadAndInstallButton.label", + [updateVersion] + ); + button.accessKey = this.bundle.GetStringFromName( + "update.downloadAndInstallButton.accesskey" + ); + } + if (this.options.buttonAutoFocus) { + let promise = Promise.resolve(); + if (document.readyState != "complete") { + promise = new Promise(resolve => + window.addEventListener("load", resolve, { once: true }) + ); + } + promise.then(() => { + if ( + !document.commandDispatcher.focusedElement || // don't steal the focus + // except from the other buttons + document.commandDispatcher.focusedElement.localName == "button" + ) { + button.focus(); + } + }); + } + } + }, + + /** + * Check for updates + */ + checkForUpdates() { + this._appUpdater.check(); + }, + + /** + * Handles oncommand for the "Restart to Update" button + * which is presented after the download has been downloaded. + */ + buttonRestartAfterDownload() { + if (AUS.currentState != Ci.nsIApplicationUpdateService.STATE_PENDING) { + return; + } + + gAppUpdater.selectPanel("restarting"); + + // Notify all windows that an application quit has been requested. + let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance( + Ci.nsISupportsPRBool + ); + Services.obs.notifyObservers( + cancelQuit, + "quit-application-requested", + "restart" + ); + + // Something aborted the quit process. + if (cancelQuit.data) { + gAppUpdater.selectPanel("apply"); + return; + } + + // If already in safe mode restart in safe mode (bug 327119) + if (Services.appinfo.inSafeMode) { + Services.startup.restartInSafeMode(Ci.nsIAppStartup.eAttemptQuit); + return; + } + + if ( + !Services.startup.quit( + Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart + ) + ) { + // Either the user or the hidden window aborted the quit process. + gAppUpdater.selectPanel("apply"); + } + }, + + /** + * Starts the download of an update mar. + */ + startDownload() { + this._appUpdater.allowUpdateDownload(); + }, +}; |