browser/base/content/aboutDialog.js

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

     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 // Services = object with smart getters for common XPCOM services
     6 Components.utils.import("resource://gre/modules/Services.jsm");
     8 const PREF_EM_HOTFIX_ID = "extensions.hotfix.id";
    10 #ifdef TOR_BROWSER_VERSION
    11 # Add double-quotes back on (stripped by JarMaker.py).
    12 #expand const TOR_BROWSER_VERSION = "__TOR_BROWSER_VERSION__";
    13 #endif
    15 function init(aEvent)
    16 {
    17   if (aEvent.target != document)
    18     return;
    20   try {
    21     var distroId = Services.prefs.getCharPref("distribution.id");
    22     if (distroId) {
    23       var distroVersion = Services.prefs.getCharPref("distribution.version");
    25       var distroIdField = document.getElementById("distributionId");
    26       distroIdField.value = distroId + " - " + distroVersion;
    27       distroIdField.style.display = "block";
    29       try {
    30         // This is in its own try catch due to bug 895473 and bug 900925.
    31         var distroAbout = Services.prefs.getComplexValue("distribution.about",
    32           Components.interfaces.nsISupportsString);
    33         var distroField = document.getElementById("distribution");
    34         distroField.value = distroAbout;
    35         distroField.style.display = "block";
    36       }
    37       catch (ex) {
    38         // Pref is unset
    39         Components.utils.reportError(ex);
    40       }
    41     }
    42   }
    43   catch (e) {
    44     // Pref is unset
    45   }
    47   // Include the build ID and display warning if this is an "a#" (nightly or aurora) build
    48   let version = Services.appinfo.version;
    49   if (/a\d+$/.test(version)) {
    50     document.getElementById("experimental").hidden = false;
    51     document.getElementById("communityDesc").hidden = true;
    52   }
    54 #ifdef TOR_BROWSER_VERSION
    55   let versionElem = document.getElementById("version");
    56   if (versionElem)
    57     versionElem.textContent += " (Tor Browser " + TOR_BROWSER_VERSION + ")";
    58 #endif
    60 #ifdef MOZ_UPDATER
    61   gAppUpdater = new appUpdater();
    63 #if MOZ_UPDATE_CHANNEL != release
    64   let defaults = Services.prefs.getDefaultBranch("");
    65   let channelLabel = document.getElementById("currentChannel");
    66   channelLabel.value = defaults.getCharPref("app.update.channel");
    67 #endif
    68 #endif
    70 #ifdef XP_MACOSX
    71   // it may not be sized at this point, and we need its width to calculate its position
    72   window.sizeToContent();
    73   window.moveTo((screen.availWidth / 2) - (window.outerWidth / 2), screen.availHeight / 5);
    74 #endif
    75 }
    77 #ifdef MOZ_UPDATER
    78 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
    79 Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
    80 Components.utils.import("resource://gre/modules/AddonManager.jsm");
    82 var gAppUpdater;
    84 function onUnload(aEvent) {
    85   if (gAppUpdater.isChecking)
    86     gAppUpdater.checker.stopChecking(Components.interfaces.nsIUpdateChecker.CURRENT_CHECK);
    87   // Safe to call even when there isn't a download in progress.
    88   gAppUpdater.removeDownloadListener();
    89   gAppUpdater = null;
    90 }
    93 function appUpdater()
    94 {
    95   this.updateDeck = document.getElementById("updateDeck");
    97   // Hide the update deck when there is already an update window open to avoid
    98   // syncing issues between them.
    99   if (Services.wm.getMostRecentWindow("Update:Wizard")) {
   100     this.updateDeck.hidden = true;
   101     return;
   102   }
   104   XPCOMUtils.defineLazyServiceGetter(this, "aus",
   105                                      "@mozilla.org/updates/update-service;1",
   106                                      "nsIApplicationUpdateService");
   107   XPCOMUtils.defineLazyServiceGetter(this, "checker",
   108                                      "@mozilla.org/updates/update-checker;1",
   109                                      "nsIUpdateChecker");
   110   XPCOMUtils.defineLazyServiceGetter(this, "um",
   111                                      "@mozilla.org/updates/update-manager;1",
   112                                      "nsIUpdateManager");
   114   this.bundle = Services.strings.
   115                 createBundle("chrome://browser/locale/browser.properties");
   117   let manualURL = Services.urlFormatter.formatURLPref("app.update.url.manual");
   118   let manualLink = document.getElementById("manualLink");
   119   manualLink.value = manualURL;
   120   manualLink.href = manualURL;
   121   document.getElementById("failedLink").href = manualURL;
   123   if (this.updateDisabledAndLocked) {
   124     this.selectPanel("adminDisabled");
   125     return;
   126   }
   128   if (this.isPending || this.isApplied) {
   129     this.selectPanel("apply");
   130     return;
   131   }
   133   if (this.aus.isOtherInstanceHandlingUpdates) {
   134     this.selectPanel("otherInstanceHandlingUpdates");
   135     return;
   136   }
   138   if (this.isDownloading) {
   139     this.startDownload();
   140     // selectPanel("downloading") is called from setupDownloadingUI().
   141     return;
   142   }
   144   // Honor the "Never check for updates" option by not only disabling background
   145   // update checks, but also in the About dialog, by presenting a
   146   // "Check for updates" button.
   147   // If updates are found, the user is then asked if he wants to "Update to <version>".
   148   if (!this.updateEnabled) {
   149     this.selectPanel("checkForUpdates");
   150     return;
   151   }
   153   // That leaves the options
   154   // "Check for updates, but let me choose whether to install them", and
   155   // "Automatically install updates".
   156   // In both cases, we check for updates without asking.
   157   // In the "let me choose" case, we ask before downloading though, in onCheckComplete.
   158   this.checkForUpdates();
   159 }
   161 appUpdater.prototype =
   162 {
   163   // true when there is an update check in progress.
   164   isChecking: false,
   166   // true when there is an update already staged / ready to be applied.
   167   get isPending() {
   168     if (this.update) {
   169       return this.update.state == "pending" ||
   170              this.update.state == "pending-service";
   171     }
   172     return this.um.activeUpdate &&
   173            (this.um.activeUpdate.state == "pending" ||
   174             this.um.activeUpdate.state == "pending-service");
   175   },
   177   // true when there is an update already installed in the background.
   178   get isApplied() {
   179     if (this.update)
   180       return this.update.state == "applied" ||
   181              this.update.state == "applied-service";
   182     return this.um.activeUpdate &&
   183            (this.um.activeUpdate.state == "applied" ||
   184             this.um.activeUpdate.state == "applied-service");
   185   },
   187   // true when there is an update download in progress.
   188   get isDownloading() {
   189     if (this.update)
   190       return this.update.state == "downloading";
   191     return this.um.activeUpdate &&
   192            this.um.activeUpdate.state == "downloading";
   193   },
   195   // true when updating is disabled by an administrator.
   196   get updateDisabledAndLocked() {
   197     return !this.updateEnabled &&
   198            Services.prefs.prefIsLocked("app.update.enabled");
   199   },
   201   // true when updating is enabled.
   202   get updateEnabled() {
   203     try {
   204       return Services.prefs.getBoolPref("app.update.enabled");
   205     }
   206     catch (e) { }
   207     return true; // Firefox default is true
   208   },
   210   // true when updating in background is enabled.
   211   get backgroundUpdateEnabled() {
   212     return this.updateEnabled &&
   213            gAppUpdater.aus.canStageUpdates;
   214   },
   216   // true when updating is automatic.
   217   get updateAuto() {
   218     try {
   219       return Services.prefs.getBoolPref("app.update.auto");
   220     }
   221     catch (e) { }
   222     return true; // Firefox default is true
   223   },
   225   /**
   226    * Sets the panel of the updateDeck.
   227    *
   228    * @param  aChildID
   229    *         The id of the deck's child to select, e.g. "apply".
   230    */
   231   selectPanel: function(aChildID) {
   232     let panel = document.getElementById(aChildID);
   234     let button = panel.querySelector("button");
   235     if (button) {
   236       if (aChildID == "downloadAndInstall") {
   237         let updateVersion = gAppUpdater.update.displayVersion;
   238         button.label = this.bundle.formatStringFromName("update.downloadAndInstallButton.label", [updateVersion], 1);
   239         button.accessKey = this.bundle.GetStringFromName("update.downloadAndInstallButton.accesskey");
   240       }
   241       this.updateDeck.selectedPanel = panel;
   242       if (!document.commandDispatcher.focusedElement || // don't steal the focus
   243           document.commandDispatcher.focusedElement.localName == "button") // except from the other buttons
   244         button.focus();
   246     } else {
   247       this.updateDeck.selectedPanel = panel;
   248     }
   249   },
   251   /**
   252    * Check for updates
   253    */
   254   checkForUpdates: function() {
   255     this.selectPanel("checkingForUpdates");
   256     this.isChecking = true;
   257     this.checker.checkForUpdates(this.updateCheckListener, true);
   258     // after checking, onCheckComplete() is called
   259   },
   261   /**
   262    * Check for addon compat, or start the download right away
   263    */
   264   doUpdate: function() {
   265     // skip the compatibility check if the update doesn't provide appVersion,
   266     // or the appVersion is unchanged, e.g. nightly update
   267 #ifdef TOR_BROWSER_UPDATE
   268     let pkgVersion = TOR_BROWSER_VERSION;
   269 #else
   270     let pkgVersion = Services.appinfo.version;
   271 #endif
   272     if (!this.update.appVersion ||
   273         Services.vc.compare(gAppUpdater.update.appVersion, pkgVersion) == 0) {
   274       this.startDownload();
   275     } else {
   276       this.checkAddonCompatibility();
   277     }
   278   },
   280   /**
   281    * Handles oncommand for the "Restart to Update" button
   282    * which is presented after the download has been downloaded.
   283    */
   284   buttonRestartAfterDownload: function() {
   285     if (!this.isPending && !this.isApplied)
   286       return;
   288       // Notify all windows that an application quit has been requested.
   289       let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"].
   290                        createInstance(Components.interfaces.nsISupportsPRBool);
   291       Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
   293       // Something aborted the quit process.
   294       if (cancelQuit.data)
   295         return;
   297       let appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"].
   298                        getService(Components.interfaces.nsIAppStartup);
   300       // If already in safe mode restart in safe mode (bug 327119)
   301       if (Services.appinfo.inSafeMode) {
   302         appStartup.restartInSafeMode(Components.interfaces.nsIAppStartup.eAttemptQuit);
   303         return;
   304       }
   306       appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit |
   307                       Components.interfaces.nsIAppStartup.eRestart);
   308     },
   310   /**
   311    * Handles oncommand for the "Apply Update…" button
   312    * which is presented if we need to show the billboard or license.
   313    */
   314   buttonApplyBillboard: function() {
   315     const URI_UPDATE_PROMPT_DIALOG = "chrome://mozapps/content/update/updates.xul";
   316     var ary = null;
   317     ary = Components.classes["@mozilla.org/supports-array;1"].
   318           createInstance(Components.interfaces.nsISupportsArray);
   319     ary.AppendElement(this.update);
   320     var openFeatures = "chrome,centerscreen,dialog=no,resizable=no,titlebar,toolbar=no";
   321     Services.ww.openWindow(null, URI_UPDATE_PROMPT_DIALOG, "", openFeatures, ary);
   322     window.close(); // close the "About" window; updates.xul takes over.
   323   },
   325   /**
   326    * Implements nsIUpdateCheckListener. The methods implemented by
   327    * nsIUpdateCheckListener are in a different scope from nsIIncrementalDownload
   328    * to make it clear which are used by each interface.
   329    */
   330   updateCheckListener: {
   331     /**
   332      * See nsIUpdateService.idl
   333      */
   334     onCheckComplete: function(aRequest, aUpdates, aUpdateCount) {
   335       gAppUpdater.isChecking = false;
   336       gAppUpdater.update = gAppUpdater.aus.
   337                            selectUpdate(aUpdates, aUpdates.length);
   338       if (!gAppUpdater.update) {
   339         gAppUpdater.selectPanel("noUpdatesFound");
   340         return;
   341       }
   343       if (gAppUpdater.update.unsupported) {
   344         if (gAppUpdater.update.detailsURL) {
   345           let unsupportedLink = document.getElementById("unsupportedLink");
   346           unsupportedLink.href = gAppUpdater.update.detailsURL;
   347         }
   348         gAppUpdater.selectPanel("unsupportedSystem");
   349         return;
   350       }
   352       if (!gAppUpdater.aus.canApplyUpdates) {
   353         gAppUpdater.selectPanel("manualUpdate");
   354         return;
   355       }
   357       // Firefox no longer displays a license for updates and the licenseURL
   358       // check is just in case a distibution does.
   359       if (gAppUpdater.update.billboardURL || gAppUpdater.update.licenseURL) {
   360         gAppUpdater.selectPanel("applyBillboard");
   361         return;
   362       }
   364       if (gAppUpdater.updateAuto) // automatically download and install
   365         gAppUpdater.doUpdate();
   366       else // ask
   367         gAppUpdater.selectPanel("downloadAndInstall");
   368     },
   370     /**
   371      * See nsIUpdateService.idl
   372      */
   373     onError: function(aRequest, aUpdate) {
   374       // Errors in the update check are treated as no updates found. If the
   375       // update check fails repeatedly without a success the user will be
   376       // notified with the normal app update user interface so this is safe.
   377       gAppUpdater.isChecking = false;
   378       gAppUpdater.selectPanel("noUpdatesFound");
   379     },
   381     /**
   382      * See nsISupports.idl
   383      */
   384     QueryInterface: function(aIID) {
   385       if (!aIID.equals(Components.interfaces.nsIUpdateCheckListener) &&
   386           !aIID.equals(Components.interfaces.nsISupports))
   387         throw Components.results.NS_ERROR_NO_INTERFACE;
   388       return this;
   389     }
   390   },
   392   /**
   393    * Checks the compatibility of add-ons for the application update.
   394    */
   395   checkAddonCompatibility: function() {
   396     try {
   397       var hotfixID = Services.prefs.getCharPref(PREF_EM_HOTFIX_ID);
   398     }
   399     catch (e) { }
   401     var self = this;
   402     AddonManager.getAllAddons(function(aAddons) {
   403       self.addons = [];
   404       self.addonsCheckedCount = 0;
   405       aAddons.forEach(function(aAddon) {
   406         // Protect against code that overrides the add-ons manager and doesn't
   407         // implement the isCompatibleWith or the findUpdates method.
   408         if (!("isCompatibleWith" in aAddon) || !("findUpdates" in aAddon)) {
   409           let errMsg = "Add-on doesn't implement either the isCompatibleWith " +
   410                        "or the findUpdates method!";
   411           if (aAddon.id)
   412             errMsg += " Add-on ID: " + aAddon.id;
   413           Components.utils.reportError(errMsg);
   414           return;
   415         }
   417         // If an add-on isn't appDisabled and isn't userDisabled then it is
   418         // either active now or the user expects it to be active after the
   419         // restart. If that is the case and the add-on is not installed by the
   420         // application and is not compatible with the new application version
   421         // then the user should be warned that the add-on will become
   422         // incompatible. If an addon's type equals plugin it is skipped since
   423         // checking plugins compatibility information isn't supported and
   424         // getting the scope property of a plugin breaks in some environments
   425         // (see bug 566787). The hotfix add-on is also ignored as it shouldn't
   426         // block the user from upgrading.
   427         try {
   428 #ifdef TOR_BROWSER_UPDATE
   429           let compatVersion = self.update.platformVersion;
   430 #else
   431           let compatVersion = self.update.appVersion;
   432 #endif
   433           if (aAddon.type != "plugin" && aAddon.id != hotfixID &&
   434               !aAddon.appDisabled && !aAddon.userDisabled &&
   435               aAddon.scope != AddonManager.SCOPE_APPLICATION &&
   436               aAddon.isCompatible &&
   437               !aAddon.isCompatibleWith(compatVersion,
   438                                        self.update.platformVersion))
   439             self.addons.push(aAddon);
   440         }
   441         catch (e) {
   442           Components.utils.reportError(e);
   443         }
   444       });
   445       self.addonsTotalCount = self.addons.length;
   446       if (self.addonsTotalCount == 0) {
   447         self.startDownload();
   448         return;
   449       }
   451       self.checkAddonsForUpdates();
   452     });
   453   },
   455   /**
   456    * Checks if there are updates for add-ons that are incompatible with the
   457    * application update.
   458    */
   459   checkAddonsForUpdates: function() {
   460     this.addons.forEach(function(aAddon) {
   461 #ifdef TOR_BROWSER_UPDATE
   462       let compatVersion = this.update.platformVersion;
   463 #else
   464       let compatVersion = this.update.appVersion;
   465 #endif
   466       aAddon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED,
   467                          compatVersion,
   468                          this.update.platformVersion);
   469     }, this);
   470   },
   472   /**
   473    * See XPIProvider.jsm
   474    */
   475   onCompatibilityUpdateAvailable: function(aAddon) {
   476     for (var i = 0; i < this.addons.length; ++i) {
   477       if (this.addons[i].id == aAddon.id) {
   478         this.addons.splice(i, 1);
   479         break;
   480       }
   481     }
   482   },
   484   /**
   485    * See XPIProvider.jsm
   486    */
   487   onUpdateAvailable: function(aAddon, aInstall) {
   488 #ifdef TOR_BROWSER_UPDATE
   489     let compatVersion = this.update.platformVersion;
   490 #else
   491     let compatVersion = this.update.appVersion;
   492 #endif
   493     if (!Services.blocklist.isAddonBlocklisted(aAddon,
   494                                                compatVersion,
   495                                                this.update.platformVersion)) {
   496       // Compatibility or new version updates mean the same thing here.
   497       this.onCompatibilityUpdateAvailable(aAddon);
   498     }
   499   },
   501   /**
   502    * See XPIProvider.jsm
   503    */
   504   onUpdateFinished: function(aAddon) {
   505     ++this.addonsCheckedCount;
   507     if (this.addonsCheckedCount < this.addonsTotalCount)
   508       return;
   510     if (this.addons.length == 0) {
   511       // Compatibility updates or new version updates were found for all add-ons
   512       this.startDownload();
   513       return;
   514     }
   516     this.selectPanel("apply");
   517   },
   519   /**
   520    * Starts the download of an update mar.
   521    */
   522   startDownload: function() {
   523     if (!this.update)
   524       this.update = this.um.activeUpdate;
   525     this.update.QueryInterface(Components.interfaces.nsIWritablePropertyBag);
   526     this.update.setProperty("foregroundDownload", "true");
   528     this.aus.pauseDownload();
   529     let state = this.aus.downloadUpdate(this.update, false);
   530     if (state == "failed") {
   531       this.selectPanel("downloadFailed");
   532       return;
   533     }
   535     this.setupDownloadingUI();
   536   },
   538   /**
   539    * Switches to the UI responsible for tracking the download.
   540    */
   541   setupDownloadingUI: function() {
   542     this.downloadStatus = document.getElementById("downloadStatus");
   543     this.downloadStatus.value =
   544       DownloadUtils.getTransferTotal(0, this.update.selectedPatch.size);
   545     this.selectPanel("downloading");
   546     this.aus.addDownloadListener(this);
   547   },
   549   removeDownloadListener: function() {
   550     if (this.aus) {
   551       this.aus.removeDownloadListener(this);
   552     }
   553   },
   555   /**
   556    * See nsIRequestObserver.idl
   557    */
   558   onStartRequest: function(aRequest, aContext) {
   559   },
   561   /**
   562    * See nsIRequestObserver.idl
   563    */
   564   onStopRequest: function(aRequest, aContext, aStatusCode) {
   565     switch (aStatusCode) {
   566     case Components.results.NS_ERROR_UNEXPECTED:
   567       if (this.update.selectedPatch.state == "download-failed" &&
   568           (this.update.isCompleteUpdate || this.update.patchCount != 2)) {
   569         // Verification error of complete patch, informational text is held in
   570         // the update object.
   571         this.removeDownloadListener();
   572         this.selectPanel("downloadFailed");
   573         break;
   574       }
   575       // Verification failed for a partial patch, complete patch is now
   576       // downloading so return early and do NOT remove the download listener!
   577       break;
   578     case Components.results.NS_BINDING_ABORTED:
   579       // Do not remove UI listener since the user may resume downloading again.
   580       break;
   581     case Components.results.NS_OK:
   582       this.removeDownloadListener();
   583       if (this.backgroundUpdateEnabled) {
   584         this.selectPanel("applying");
   585         let update = this.um.activeUpdate;
   586         let self = this;
   587         Services.obs.addObserver(function (aSubject, aTopic, aData) {
   588           // Update the UI when the background updater is finished
   589           let status = aData;
   590           if (status == "applied" || status == "applied-service" ||
   591               status == "pending" || status == "pending-service") {
   592             // If the update is successfully applied, or if the updater has
   593             // fallen back to non-staged updates, show the "Restart to Update"
   594             // button.
   595             self.selectPanel("apply");
   596           } else if (status == "failed") {
   597             // Background update has failed, let's show the UI responsible for
   598             // prompting the user to update manually.
   599             self.selectPanel("downloadFailed");
   600           } else if (status == "downloading") {
   601             // We've fallen back to downloading the full update because the
   602             // partial update failed to get staged in the background.
   603             // Therefore we need to keep our observer.
   604             self.setupDownloadingUI();
   605             return;
   606           }
   607           Services.obs.removeObserver(arguments.callee, "update-staged");
   608         }, "update-staged", false);
   609       } else {
   610         this.selectPanel("apply");
   611       }
   612       break;
   613     default:
   614       this.removeDownloadListener();
   615       this.selectPanel("downloadFailed");
   616       break;
   617     }
   618   },
   620   /**
   621    * See nsIProgressEventSink.idl
   622    */
   623   onStatus: function(aRequest, aContext, aStatus, aStatusArg) {
   624   },
   626   /**
   627    * See nsIProgressEventSink.idl
   628    */
   629   onProgress: function(aRequest, aContext, aProgress, aProgressMax) {
   630     this.downloadStatus.value =
   631       DownloadUtils.getTransferTotal(aProgress, aProgressMax);
   632   },
   634   /**
   635    * See nsISupports.idl
   636    */
   637   QueryInterface: function(aIID) {
   638     if (!aIID.equals(Components.interfaces.nsIProgressEventSink) &&
   639         !aIID.equals(Components.interfaces.nsIRequestObserver) &&
   640         !aIID.equals(Components.interfaces.nsISupports))
   641       throw Components.results.NS_ERROR_NO_INTERFACE;
   642     return this;
   643   }
   644 };
   645 #endif

mercurial