tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

aboutDialog-appUpdater.js (9086B)


      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/. */
      4 
      5 // Note: this file is included in aboutDialog.xhtml and preferences/advanced.xhtml
      6 // if MOZ_UPDATER is defined.
      7 
      8 /* import-globals-from aboutDialog.js */
      9 
     10 var { XPCOMUtils } = ChromeUtils.importESModule(
     11  "resource://gre/modules/XPCOMUtils.sys.mjs"
     12 );
     13 
     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",
     18 });
     19 
     20 XPCOMUtils.defineLazyServiceGetter(
     21  this,
     22  "AUS",
     23  "@mozilla.org/updates/update-service;1",
     24  Ci.nsIApplicationUpdateService
     25 );
     26 
     27 var UPDATING_MIN_DISPLAY_TIME_MS = 1500;
     28 
     29 var gAppUpdater;
     30 
     31 function onUnload(_aEvent) {
     32  if (gAppUpdater) {
     33    gAppUpdater.destroy();
     34    gAppUpdater = null;
     35  }
     36 }
     37 
     38 function appUpdater(options = {}) {
     39  this._appUpdater = new AppUpdater();
     40 
     41  this._appUpdateListener = (status, ...args) => {
     42    this._onAppUpdateStatus(status, ...args);
     43  };
     44  this._appUpdater.addListener(this._appUpdateListener);
     45 
     46  this.options = options;
     47  this.updatingMinDisplayTimerId = null;
     48  this.updateDeck = document.getElementById("updateDeck");
     49 
     50  this.bundle = Services.strings.createBundle(
     51    "chrome://browser/locale/browser.properties"
     52  );
     53 
     54  try {
     55    let manualURL = new URL(
     56      Services.urlFormatter.formatURLPref("app.update.url.manual")
     57    );
     58 
     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]"), {
     64        displayUrl,
     65      });
     66    }
     67 
     68    document.getElementById("failedLink").href = manualURL.href;
     69  } catch (e) {
     70    console.error("Invalid manual update url.", e);
     71  }
     72 
     73  this._appUpdater.check();
     74 }
     75 
     76 appUpdater.prototype = {
     77  destroy() {
     78    this.stopCurrentCheck();
     79    if (this.updatingMinDisplayTimerId) {
     80      clearTimeout(this.updatingMinDisplayTimerId);
     81    }
     82  },
     83 
     84  stopCurrentCheck() {
     85    this._appUpdater.removeListener(this._appUpdateListener);
     86    this._appUpdater.stop();
     87  },
     88 
     89  get update() {
     90    return this._appUpdater.update;
     91  },
     92 
     93  get selectedPanel() {
     94    return this.updateDeck.selectedPanel;
     95  },
     96 
     97  _onAppUpdateStatus(status, ...args) {
     98    switch (status) {
     99      case AppUpdater.STATUS.UPDATE_DISABLED_BY_POLICY:
    100        this.selectPanel("policyDisabled");
    101        break;
    102      case AppUpdater.STATUS.READY_FOR_RESTART:
    103        this.selectPanel("apply");
    104        break;
    105      case AppUpdater.STATUS.OTHER_INSTANCE_HANDLING_UPDATES:
    106        this.selectPanel("otherInstanceHandlingUpdates");
    107        break;
    108      case AppUpdater.STATUS.DOWNLOADING: {
    109        const downloadStatus = document.getElementById("downloading");
    110        if (!args.length) {
    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
    114          // available.
    115          let maxSize = -1;
    116          if (this.update.selectedPatch) {
    117            maxSize = this.update.selectedPatch.size;
    118          }
    119          const transfer = DownloadUtils.getTransferTotal(0, maxSize);
    120          document.l10n.setArgs(downloadStatus, { transfer });
    121          this.selectPanel("downloading");
    122        } else {
    123          let [progress, max] = args;
    124          const transfer = DownloadUtils.getTransferTotal(progress, max);
    125          document.l10n.setArgs(downloadStatus, { transfer });
    126        }
    127        break;
    128      }
    129      case AppUpdater.STATUS.STAGING:
    130        this.selectPanel("applying");
    131        break;
    132      case AppUpdater.STATUS.CHECKING: {
    133        this.checkingForUpdatesDelayPromise = new Promise(resolve => {
    134          this.updatingMinDisplayTimerId = setTimeout(
    135            resolve,
    136            UPDATING_MIN_DISPLAY_TIME_MS
    137          );
    138        });
    139        if (Services.policies.isAllowed("appUpdate")) {
    140          this.selectPanel("checkingForUpdates");
    141        } else {
    142          this.selectPanel("policyDisabled");
    143        }
    144        break;
    145      }
    146      case AppUpdater.STATUS.CHECKING_FAILED:
    147        this.selectPanel("checkingFailed");
    148        break;
    149      case AppUpdater.STATUS.NO_UPDATES_FOUND:
    150        this.checkingForUpdatesDelayPromise.then(() => {
    151          if (Services.policies.isAllowed("appUpdate")) {
    152            this.selectPanel("noUpdatesFound");
    153          } else {
    154            this.selectPanel("policyDisabled");
    155          }
    156        });
    157        break;
    158      case AppUpdater.STATUS.UNSUPPORTED_SYSTEM:
    159        if (this.update.detailsURL) {
    160          let unsupportedLink = document.getElementById("unsupportedLink");
    161          unsupportedLink.href = this.update.detailsURL;
    162        }
    163        this.selectPanel("unsupportedSystem");
    164        break;
    165      case AppUpdater.STATUS.MANUAL_UPDATE:
    166        this.selectPanel("manualUpdate");
    167        break;
    168      case AppUpdater.STATUS.DOWNLOAD_AND_INSTALL:
    169        this.selectPanel("downloadAndInstall");
    170        break;
    171      case AppUpdater.STATUS.DOWNLOAD_FAILED:
    172        this.selectPanel("downloadFailed");
    173        break;
    174      case AppUpdater.STATUS.INTERNAL_ERROR:
    175        this.selectPanel("internalError");
    176        break;
    177      case AppUpdater.STATUS.NEVER_CHECKED:
    178        this.selectPanel("checkForUpdates");
    179        break;
    180      case AppUpdater.STATUS.NO_UPDATER:
    181      default:
    182        this.selectPanel("noUpdater");
    183        break;
    184    }
    185  },
    186 
    187  /**
    188   * Sets the panel of the updateDeck and the icon class.
    189   *
    190   * @param  aChildID
    191   *         The id of the deck's child to select, e.g. "apply".
    192   */
    193  selectPanel(aChildID) {
    194    let panel = document.getElementById(aChildID);
    195    let icon = document.getElementById("updateIcon");
    196    if (icon) {
    197      icon.className = aChildID;
    198    }
    199 
    200    // Make sure to select the panel before potentially auto-focusing the button.
    201    this.updateDeck.selectedPanel = panel;
    202 
    203    let button = panel.querySelector("button");
    204    if (button) {
    205      if (aChildID == "downloadAndInstall") {
    206        let updateVersion = gAppUpdater.update.displayVersion;
    207        // Include the build ID if this is an "a#" (nightly or aurora) build
    208        if (!AppConstants.BASE_BROWSER_UPDATE && /a\d+$/.test(updateVersion)) {
    209          let buildID = gAppUpdater.update.buildID;
    210          let year = buildID.slice(0, 4);
    211          let month = buildID.slice(4, 6);
    212          let day = buildID.slice(6, 8);
    213          updateVersion += ` (${year}-${month}-${day})`;
    214        }
    215        button.label = this.bundle.formatStringFromName(
    216          "update.downloadAndInstallButton.label",
    217          [updateVersion]
    218        );
    219        button.accessKey = this.bundle.GetStringFromName(
    220          "update.downloadAndInstallButton.accesskey"
    221        );
    222      }
    223      if (this.options.buttonAutoFocus) {
    224        let promise = Promise.resolve();
    225        if (document.readyState != "complete") {
    226          promise = new Promise(resolve =>
    227            window.addEventListener("load", resolve, { once: true })
    228          );
    229        }
    230        promise.then(() => {
    231          if (
    232            !document.commandDispatcher.focusedElement || // don't steal the focus
    233            // except from the other buttons
    234            document.commandDispatcher.focusedElement.localName == "button"
    235          ) {
    236            button.focus();
    237          }
    238        });
    239      }
    240    }
    241  },
    242 
    243  /**
    244   * Check for updates
    245   */
    246  checkForUpdates() {
    247    this._appUpdater.check();
    248  },
    249 
    250  /**
    251   * Handles oncommand for the "Restart to Update" button
    252   * which is presented after the download has been downloaded.
    253   */
    254  buttonRestartAfterDownload() {
    255    if (AUS.currentState != Ci.nsIApplicationUpdateService.STATE_PENDING) {
    256      return;
    257    }
    258 
    259    gAppUpdater.selectPanel("restarting");
    260 
    261    // Notify all windows that an application quit has been requested.
    262    let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(
    263      Ci.nsISupportsPRBool
    264    );
    265    Services.obs.notifyObservers(
    266      cancelQuit,
    267      "quit-application-requested",
    268      "restart"
    269    );
    270 
    271    // Something aborted the quit process.
    272    if (cancelQuit.data) {
    273      gAppUpdater.selectPanel("apply");
    274      return;
    275    }
    276 
    277    // If already in safe mode restart in safe mode (bug 327119)
    278    if (Services.appinfo.inSafeMode) {
    279      Services.startup.restartInSafeMode(Ci.nsIAppStartup.eAttemptQuit);
    280      return;
    281    }
    282 
    283    if (
    284      !Services.startup.quit(
    285        Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart
    286      )
    287    ) {
    288      // Either the user or the hidden window aborted the quit process.
    289      gAppUpdater.selectPanel("apply");
    290    }
    291  },
    292 
    293  /**
    294   * Starts the download of an update mar.
    295   */
    296  startDownload() {
    297    this._appUpdater.allowUpdateDownload();
    298  },
    299 };