1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 // Note: this file is included in aboutDialog.xhtml and preferences/advanced.xhtml
6 // if MOZ_UPDATER is defined.
8 /* import-globals-from aboutDialog.js */
10 var { XPCOMUtils } = ChromeUtils.importESModule(
11 "resource://gre/modules/XPCOMUtils.sys.mjs"
14 ChromeUtils.defineESModuleGetters(this, {
15 AppUpdater: "resource://gre/modules/AppUpdater.sys.mjs",
16 DownloadUtils: "resource://gre/modules/DownloadUtils.sys.mjs",
17 UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs",
20 XPCOMUtils.defineLazyServiceGetter(
23 "@mozilla.org/updates/update-service;1",
24 "nsIApplicationUpdateService"
27 var UPDATING_MIN_DISPLAY_TIME_MS = 1500;
31 function onUnload(_aEvent) {
33 gAppUpdater.destroy();
38 function appUpdater(options = {}) {
39 this._appUpdater = new AppUpdater();
41 this._appUpdateListener = (status, ...args) => {
42 this._onAppUpdateStatus(status, ...args);
44 this._appUpdater.addListener(this._appUpdateListener);
46 this.options = options;
47 this.updatingMinDisplayTimerId = null;
48 this.updateDeck = document.getElementById("updateDeck");
50 this.bundle = Services.strings.createBundle(
51 "chrome://browser/locale/browser.properties"
55 let manualURL = new URL(
56 Services.urlFormatter.formatURLPref("app.update.url.manual")
59 for (const manualLink of document.querySelectorAll(".manualLink")) {
60 // Strip hash and search parameters for display text.
61 let displayUrl = manualURL.origin + manualURL.pathname;
62 manualLink.href = manualURL.href;
63 document.l10n.setArgs(manualLink.closest("[data-l10n-id]"), {
68 document.getElementById("failedLink").href = manualURL.href;
70 console.error("Invalid manual update url.", e);
73 this._appUpdater.check();
76 appUpdater.prototype = {
78 this.stopCurrentCheck();
79 if (this.updatingMinDisplayTimerId) {
80 clearTimeout(this.updatingMinDisplayTimerId);
85 this._appUpdater.removeListener(this._appUpdateListener);
86 this._appUpdater.stop();
90 return this._appUpdater.update;
94 return this.updateDeck.selectedPanel;
97 _onAppUpdateStatus(status, ...args) {
99 case AppUpdater.STATUS.UPDATE_DISABLED_BY_POLICY:
100 this.selectPanel("policyDisabled");
102 case AppUpdater.STATUS.READY_FOR_RESTART:
103 this.selectPanel("apply");
105 case AppUpdater.STATUS.OTHER_INSTANCE_HANDLING_UPDATES:
106 this.selectPanel("otherInstanceHandlingUpdates");
108 case AppUpdater.STATUS.DOWNLOADING: {
109 const downloadStatus = document.getElementById("downloading");
111 // Very early in the DOWNLOADING state, `selectedPatch` may not be
112 // available yet. But this function will be called again when it is
113 // available. A `maxSize < 0` indicates that the max size is not yet
116 if (this.update.selectedPatch) {
117 maxSize = this.update.selectedPatch.size;
119 const transfer = DownloadUtils.getTransferTotal(0, maxSize);
120 document.l10n.setArgs(downloadStatus, { transfer });
121 this.selectPanel("downloading");
123 let [progress, max] = args;
124 const transfer = DownloadUtils.getTransferTotal(progress, max);
125 document.l10n.setArgs(downloadStatus, { transfer });
129 case AppUpdater.STATUS.STAGING:
130 this.selectPanel("applying");
132 case AppUpdater.STATUS.CHECKING: {
133 this.checkingForUpdatesDelayPromise = new Promise(resolve => {
134 this.updatingMinDisplayTimerId = setTimeout(
136 UPDATING_MIN_DISPLAY_TIME_MS
139 if (Services.policies.isAllowed("appUpdate")) {
140 this.selectPanel("checkingForUpdates");
142 this.selectPanel("policyDisabled");
146 case AppUpdater.STATUS.CHECKING_FAILED:
147 this.selectPanel("checkingFailed");
149 case AppUpdater.STATUS.NO_UPDATES_FOUND:
150 this.checkingForUpdatesDelayPromise.then(() => {
151 if (Services.policies.isAllowed("appUpdate")) {
152 this.selectPanel("noUpdatesFound");
154 this.selectPanel("policyDisabled");
158 case AppUpdater.STATUS.UNSUPPORTED_SYSTEM:
159 if (this.update.detailsURL) {
160 let unsupportedLink = document.getElementById("unsupportedLink");
161 unsupportedLink.href = this.update.detailsURL;
163 this.selectPanel("unsupportedSystem");
165 case AppUpdater.STATUS.MANUAL_UPDATE:
166 this.selectPanel("manualUpdate");
168 case AppUpdater.STATUS.DOWNLOAD_AND_INSTALL:
169 this.selectPanel("downloadAndInstall");
171 case AppUpdater.STATUS.DOWNLOAD_FAILED:
172 this.selectPanel("downloadFailed");
174 case AppUpdater.STATUS.INTERNAL_ERROR:
175 this.selectPanel("internalError");
177 case AppUpdater.STATUS.NEVER_CHECKED:
178 this.selectPanel("checkForUpdates");
180 case AppUpdater.STATUS.NO_UPDATER:
182 this.selectPanel("noUpdater");
188 * Sets the panel of the updateDeck and the visibility of icons
189 * in the #icons element.
192 * The id of the deck's child to select, e.g. "apply".
194 selectPanel(aChildID) {
195 let panel = document.getElementById(aChildID);
196 let icons = document.getElementById("icons");
198 icons.className = aChildID;
201 // Make sure to select the panel before potentially auto-focusing the button.
202 this.updateDeck.selectedPanel = panel;
204 let button = panel.querySelector("button");
206 if (aChildID == "downloadAndInstall") {
207 let updateVersion = gAppUpdater.update.displayVersion;
208 // Include the build ID if this is an "a#" (nightly or aurora) build
209 if (/a\d+$/.test(updateVersion)) {
210 let buildID = gAppUpdater.update.buildID;
211 let year = buildID.slice(0, 4);
212 let month = buildID.slice(4, 6);
213 let day = buildID.slice(6, 8);
214 updateVersion += ` (${year}-${month}-${day})`;
216 button.label = this.bundle.formatStringFromName(
217 "update.downloadAndInstallButton.label",
220 button.accessKey = this.bundle.GetStringFromName(
221 "update.downloadAndInstallButton.accesskey"
224 if (this.options.buttonAutoFocus) {
225 let promise = Promise.resolve();
226 if (document.readyState != "complete") {
227 promise = new Promise(resolve =>
228 window.addEventListener("load", resolve, { once: true })
233 !document.commandDispatcher.focusedElement || // don't steal the focus
234 // except from the other buttons
235 document.commandDispatcher.focusedElement.localName == "button"
248 this._appUpdater.check();
252 * Handles oncommand for the "Restart to Update" button
253 * which is presented after the download has been downloaded.
255 buttonRestartAfterDownload() {
256 if (AUS.currentState != Ci.nsIApplicationUpdateService.STATE_PENDING) {
260 gAppUpdater.selectPanel("restarting");
262 // Notify all windows that an application quit has been requested.
263 let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(
266 Services.obs.notifyObservers(
268 "quit-application-requested",
272 // Something aborted the quit process.
273 if (cancelQuit.data) {
274 gAppUpdater.selectPanel("apply");
278 // If already in safe mode restart in safe mode (bug 327119)
279 if (Services.appinfo.inSafeMode) {
280 Services.startup.restartInSafeMode(Ci.nsIAppStartup.eAttemptQuit);
285 !Services.startup.quit(
286 Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart
289 // Either the user or the hidden window aborted the quit process.
290 gAppUpdater.selectPanel("apply");
295 * Starts the download of an update mar.
298 this._appUpdater.allowUpdateDownload();