toolkit/mozapps/plugins/content/pluginInstallerService.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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 Components.utils.import("resource://gre/modules/AddonManager.jsm");
     7 const DOWNLOAD_STARTED = 0;
     8 const DOWNLOAD_FINISHED = 1;
     9 const INSTALL_STARTED = 2;
    10 const INSTALL_FINISHED = 3;
    11 const INSTALLS_COMPLETE = 4;
    13 function getLocalizedError(key)
    14 {
    15   return document.getElementById("xpinstallStrings").getString(key);
    16 }
    18 function binaryToHex(input)
    19 {
    20   return [('0' + input.charCodeAt(i).toString(16)).slice(-2)
    21           for (i in input)].join('');
    22 }
    24 function verifyHash(aFile, aHash)
    25 {
    26   try {
    27     var [, method, hash] = /^([A-Za-z0-9]+):(.*)$/.exec(aHash);
    29     var fis = Components.classes['@mozilla.org/network/file-input-stream;1'].
    30       createInstance(Components.interfaces.nsIFileInputStream);
    31     fis.init(aFile, -1, -1, 0);
    33     var hasher = Components.classes['@mozilla.org/security/hash;1'].
    34       createInstance(Components.interfaces.nsICryptoHash);
    35     hasher.initWithString(method);
    36     hasher.updateFromStream(fis, -1);
    37     dlhash = binaryToHex(hasher.finish(false));
    38     return dlhash == hash;
    39   }
    40   catch (e) {
    41     Components.utils.reportError(e);
    42     return false;
    43   }
    44 }
    46 function InstallerObserver(aPlugin)
    47 {
    48   this._plugin = aPlugin;
    49   this._init();
    50 }
    52 InstallerObserver.prototype = {
    53   _init: function()
    54   {
    55     try {
    56       var ios = Components.classes["@mozilla.org/network/io-service;1"].
    57         getService(Components.interfaces.nsIIOService);
    58       var uri = ios.newURI(this._plugin.InstallerLocation, null, null);
    59       uri.QueryInterface(Components.interfaces.nsIURL);
    61       // Use a local filename appropriate for the OS
    62       var leafName = uri.fileName;
    63       var os = Components.classes["@mozilla.org/xre/app-info;1"]
    64                          .getService(Components.interfaces.nsIXULRuntime)
    65                          .OS;
    66       if (os == "WINNT" && leafName.indexOf(".") < 0)
    67         leafName += ".exe";
    69       var dirs = Components.classes["@mozilla.org/file/directory_service;1"].
    70         getService(Components.interfaces.nsIProperties);
    72       var resultFile = dirs.get("TmpD", Components.interfaces.nsIFile);
    73       resultFile.append(leafName);
    74       resultFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE,
    75                               0770);
    77       var channel = ios.newChannelFromURI(uri);
    78       this._downloader =
    79         Components.classes["@mozilla.org/network/downloader;1"].
    80           createInstance(Components.interfaces.nsIDownloader);
    81       this._downloader.init(this, resultFile);
    82       channel.notificationCallbacks = this;
    84       this._fireNotification(DOWNLOAD_STARTED, null);
    86       channel.asyncOpen(this._downloader, null);
    87     }
    88     catch (e) {
    89       Components.utils.reportError(e);
    90       this._fireNotification(INSTALL_FINISHED, getLocalizedError("error-228"));
    91       if (resultFile && resultFile.exists())
    92         resultfile.remove(false);
    93     }
    94   },
    96   /**
    97    * Inform the gPluginInstaller about what's going on.
    98    */
    99   _fireNotification: function(aStatus, aErrorMsg)
   100   {
   101     gPluginInstaller.pluginInstallationProgress(this._plugin.pid,
   102                                                 aStatus, aErrorMsg);
   104     if (aStatus == INSTALL_FINISHED) {
   105       --PluginInstallService._installersPending;
   106       PluginInstallService._fireFinishedNotification();
   107     }
   108   },
   110   QueryInterface: function(iid)
   111   {
   112     if (iid.equals(Components.interfaces.nsISupports) ||
   113         iid.equals(Components.interfaces.nsIInterfaceRequestor) ||
   114         iid.equals(Components.interfaces.nsIDownloadObserver) ||
   115         iid.equals(Components.interfaces.nsIProgressEventSink))
   116       return this;
   118     throw Components.results.NS_ERROR_NO_INTERFACE;
   119   },
   121   getInterface: function(iid)
   122   {
   123     if (iid.equals(Components.interfaces.nsIProgressEventSink))
   124       return this;
   126     return null;
   127   },
   129   onDownloadComplete: function(downloader, request, ctxt, status, result)
   130   {
   131     if (!Components.isSuccessCode(status)) {
   132       // xpinstall error 228 is "Download Error"
   133       this._fireNotification(INSTALL_FINISHED, getLocalizedError("error-228"));
   134       result.remove(false);
   135       return;
   136     }
   138     this._fireNotification(DOWNLOAD_FINISHED);
   140     if (this._plugin.InstallerHash &&
   141         !verifyHash(result, this._plugin.InstallerHash)) {
   142       // xpinstall error 261 is "Invalid file hash..."
   143       this._fireNotification(INSTALL_FINISHED, getLocalizedError("error-261"));
   144       result.remove(false);
   145       return;
   146     }
   148     this._fireNotification(INSTALL_STARTED);
   150     result.QueryInterface(Components.interfaces.nsILocalFile);
   151     try {
   152       // Make sure the file is executable
   153       result.permissions = 0770;
   154       var process = Components.classes["@mozilla.org/process/util;1"]
   155                               .createInstance(Components.interfaces.nsIProcess);
   156       process.init(result);
   157       var self = this;
   158       process.runAsync([], 0, {
   159         observe: function(subject, topic, data) {
   160           if (topic != "process-finished") {
   161             Components.utils.reportError("Failed to launch installer");
   162             self._fireNotification(INSTALL_FINISHED,
   163                                    getLocalizedError("error-207"));
   164           }
   165           else if (process.exitValue != 0) {
   166             Components.utils.reportError("Installer returned exit code " + process.exitValue);
   167             self._fireNotification(INSTALL_FINISHED,
   168                                    getLocalizedError("error-203"));
   169           }
   170           else {
   171             self._fireNotification(INSTALL_FINISHED, null);
   172           }
   173           result.remove(false);
   174         }
   175       });
   176     }
   177     catch (e) {
   178       Components.utils.reportError(e);
   179       this._fireNotification(INSTALL_FINISHED, getLocalizedError("error-207"));
   180       result.remove(false);
   181     }
   182   },
   184   onProgress: function(aRequest, aContext, aProgress, aProgressMax)
   185   {
   186     gPluginInstaller.pluginInstallationProgressMeter(this._plugin.pid,
   187                                                      aProgress,
   188                                                      aProgressMax);
   189   },
   191   onStatus: function(aRequest, aContext, aStatus, aStatusArg)
   192   {
   193     /* pass */
   194   }
   195 };
   197 var PluginInstallService = {
   199   /**
   200    * Start installation of installers and XPI plugins.
   201    * @param aInstallerPlugins An array of objects which should have the
   202    *                          properties "pid", "InstallerLocation",
   203    *                          and "InstallerHash"
   204    * @param aXPIPlugins       An array of objects which should have the
   205    *                          properties "pid", "XPILocation",
   206    *                          and "XPIHash"
   207    */
   208   startPluginInstallation: function (aInstallerPlugins,
   209                                      aXPIPlugins)
   210   {
   211     this._xpiPlugins = aXPIPlugins;
   212     this._xpisPending = aXPIPlugins.length;
   214     aXPIPlugins.forEach(function(plugin) {
   215       AddonManager.getInstallForURL(plugin.XPILocation, function(install) {
   216         install.addListener(PluginInstallService);
   217         install.install();
   218       }, "application/x-xpinstall", plugin.XPIHash);
   219     });
   221     // InstallerObserver may finish immediately so we must initialise the
   222     // installers after setting the number of installers and xpis pending
   223     this._installersPending = aInstallerPlugins.length;
   224     this._installerPlugins = [new InstallerObserver(plugin)
   225                               for each (plugin in aInstallerPlugins)];
   226   },
   228   _fireFinishedNotification: function()
   229   {
   230     if (this._installersPending == 0 && this._xpisPending == 0)
   231       gPluginInstaller.pluginInstallationProgress(null, INSTALLS_COMPLETE, null);
   232   },
   234   getPidForInstall: function(install) {
   235     for (let i = 0; i < this._xpiPlugins.length; i++) {
   236       if (install.sourceURI.spec == this._xpiPlugins[i].XPILocation)
   237         return this._xpiPlugins[i].pid;
   238     }
   239     return -1;
   240   },
   242   // InstallListener interface
   243   onDownloadStarted: function(install) {
   244     var pid = this.getPidForInstall(install);
   245     gPluginInstaller.pluginInstallationProgress(pid, DOWNLOAD_STARTED, null);
   246   },
   248   onDownloadProgress: function(install) {
   249     var pid = this.getPidForInstall(install);
   250     gPluginInstaller.pluginInstallationProgressMeter(pid, install.progress,
   251                                                      install.maxProgress);
   252   },
   254   onDownloadEnded: function(install) {
   255     var pid = this.getPidForInstall(install);
   256     gPluginInstaller.pluginInstallationProgress(pid, DOWNLOAD_FINISHED, null);
   257   },
   259   onDownloadFailed: function(install) {
   260     var pid = this.getPidForInstall(install);
   261     switch (install.error) {
   262     case AddonManager.ERROR_NETWORK_FAILURE:
   263       var errorMsg = getLocalizedError("error-228");
   264       break;
   265     case AddonManager.ERROR_INCORRECT_HASH:
   266       var errorMsg = getLocalizedError("error-261");
   267       break;
   268     case AddonManager.ERROR_CORRUPT_FILE:
   269       var errorMsg = getLocalizedError("error-207");
   270       break;
   271     }
   272     gPluginInstaller.pluginInstallationProgress(pid, INSTALL_FINISHED, errorMsg);
   274     this._xpisPending--;
   275     this._fireFinishedNotification();
   276   },
   278   onInstallStarted: function(install) {
   279     var pid = this.getPidForInstall(install);
   280     gPluginInstaller.pluginInstallationProgress(pid, INSTALL_STARTED, null);
   281   },
   283   onInstallEnded: function(install, addon) {
   284     var pid = this.getPidForInstall(install);
   285     gPluginInstaller.pluginInstallationProgress(pid, INSTALL_FINISHED, null);
   287     this._xpisPending--;
   288     this._fireFinishedNotification();
   289   },
   291   onInstallFailed: function(install) {
   292     var pid = this.getPidForInstall(install);
   293     gPluginInstaller.pluginInstallationProgress(pid, INSTALL_FINISHED,
   294                                                 getLocalizedError("error-203"));
   296     this._xpisPending--;
   297     this._fireFinishedNotification();
   298   }
   299 }

mercurial