37
37
* ***** END LICENSE BLOCK ***** */
39
var PluginXPIInstallService = {
47
startPluginInstallation: function (aPluginXPIUrlsArray,
50
this.pluginPidArray = aPluginPidArray;
52
var xpiManager = Components.classes["@mozilla.org/xpinstall/install-manager;1"]
53
.createInstance(Components.interfaces.nsIXPInstallManager);
54
xpiManager.initManagerWithHashes(aPluginXPIUrlsArray, aPluginHashes,
55
aPluginXPIUrlsArray.length, this);
58
// XPI progress listener stuff
59
onStateChange: function (aIndex, aState, aValue)
61
// get the pid to return to the wizard
62
var pid = this.pluginPidArray[aIndex];
65
if (aState == Components.interfaces.nsIXPIProgressDialog.INSTALL_DONE) {
67
var xpinstallStrings = document.getElementById("xpinstallStrings");
69
errorMsg = xpinstallStrings.getString("error" + aValue);
72
errorMsg = xpinstallStrings.getFormattedString("unknown.error", [aValue]);
77
gPluginInstaller.pluginXPIInstallationProgress(pid, aState, errorMsg);
81
onProgress: function (aIndex, aValue, aMaxValue)
83
// get the pid to return to the wizard
84
var pid = this.pluginPidArray[aIndex];
86
gPluginInstaller.pluginXPIInstallationProgressMeter(pid, aValue, aMaxValue);
39
Components.utils.import("resource://gre/modules/AddonManager.jsm");
41
const DOWNLOAD_STARTED = 0;
42
const DOWNLOAD_FINISHED = 1;
43
const INSTALL_STARTED = 2;
44
const INSTALL_FINISHED = 3;
45
const INSTALLS_COMPLETE = 4;
47
function getLocalizedError(key)
49
return document.getElementById("xpinstallStrings").getString(key);
52
function binaryToHex(input)
54
return [('0' + input.charCodeAt(i).toString(16)).slice(-2)
55
for (i in input)].join('');
58
function verifyHash(aFile, aHash)
61
var [, method, hash] = /^([A-Za-z0-9]+):(.*)$/.exec(aHash);
63
var fis = Components.classes['@mozilla.org/network/file-input-stream;1'].
64
createInstance(Components.interfaces.nsIFileInputStream);
65
fis.init(aFile, -1, -1, 0);
67
var hasher = Components.classes['@mozilla.org/security/hash;1'].
68
createInstance(Components.interfaces.nsICryptoHash);
69
hasher.initWithString(method);
70
hasher.updateFromStream(fis, -1);
71
dlhash = binaryToHex(hasher.finish(false));
72
return dlhash == hash;
75
Components.utils.reportError(e);
80
function InstallerObserver(aPlugin)
82
this._plugin = aPlugin;
86
InstallerObserver.prototype = {
90
var ios = Components.classes["@mozilla.org/network/io-service;1"].
91
getService(Components.interfaces.nsIIOService);
92
var uri = ios.newURI(this._plugin.InstallerLocation, null, null);
93
uri.QueryInterface(Components.interfaces.nsIURL);
95
// Use a local filename appropriate for the OS
96
var leafName = uri.fileName;
97
var os = Components.classes["@mozilla.org/xre/app-info;1"]
98
.getService(Components.interfaces.nsIXULRuntime)
100
if (os == "WINNT" && leafName.indexOf(".") < 0)
103
var dirs = Components.classes["@mozilla.org/file/directory_service;1"].
104
getService(Components.interfaces.nsIProperties);
106
var resultFile = dirs.get("TmpD", Components.interfaces.nsIFile);
107
resultFile.append(leafName);
108
resultFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE,
111
var channel = ios.newChannelFromURI(uri);
113
Components.classes["@mozilla.org/network/downloader;1"].
114
createInstance(Components.interfaces.nsIDownloader);
115
this._downloader.init(this, resultFile);
116
channel.notificationCallbacks = this;
118
this._fireNotification(DOWNLOAD_STARTED, null);
120
channel.asyncOpen(this._downloader, null);
123
Components.utils.reportError(e);
124
this._fireNotification(INSTALL_FINISHED, getLocalizedError("error-228"));
125
if (resultFile && resultFile.exists())
126
resultfile.remove(false);
131
* Inform the gPluginInstaller about what's going on.
133
_fireNotification: function(aStatus, aErrorMsg)
135
gPluginInstaller.pluginXPIInstallationProgress(this._plugin.pid,
138
if (aStatus == INSTALL_FINISHED) {
139
--PluginInstallService._installersPending;
140
PluginInstallService._fireFinishedNotification();
144
QueryInterface: function(iid)
146
if (iid.equals(Components.interfaces.nsISupports) ||
147
iid.equals(Components.interfaces.nsIInterfaceRequestor) ||
148
iid.equals(Components.interfaces.nsIDownloadObserver) ||
149
iid.equals(Components.interfaces.nsIProgressEventSink))
152
throw Components.results.NS_ERROR_NO_INTERFACE;
155
getInterface: function(iid)
157
if (iid.equals(Components.interfaces.nsIProgressEventSink))
163
onDownloadComplete: function(downloader, request, ctxt, status, result)
165
if (!Components.isSuccessCode(status)) {
166
// xpinstall error 228 is "Download Error"
167
this._fireNotification(INSTALL_FINISHED, getLocalizedError("error-228"));
168
result.remove(false);
172
this._fireNotification(DOWNLOAD_FINISHED);
174
if (this._plugin.InstallerHash &&
175
!verifyHash(result, this._plugin.InstallerHash)) {
176
// xpinstall error 261 is "Invalid file hash..."
177
this._fireNotification(INSTALL_FINISHED, getLocalizedError("error-261"));
178
result.remove(false);
182
this._fireNotification(INSTALL_STARTED);
184
result.QueryInterface(Components.interfaces.nsILocalFile);
186
// Make sure the file is executable
187
result.permissions = 0770;
188
var process = Components.classes["@mozilla.org/process/util;1"]
189
.createInstance(Components.interfaces.nsIProcess);
190
process.init(result);
192
process.runAsync([], 0, {
193
observe: function(subject, topic, data) {
194
if (topic != "process-finished") {
195
Components.utils.reportError("Failed to launch installer");
196
self._fireNotification(INSTALL_FINISHED,
197
getLocalizedError("error-207"));
199
else if (process.exitValue != 0) {
200
Components.utils.reportError("Installer returned exit code " + process.exitValue);
201
self._fireNotification(INSTALL_FINISHED,
202
getLocalizedError("error-203"));
205
self._fireNotification(INSTALL_FINISHED, null);
207
result.remove(false);
212
Components.utils.reportError(e);
213
this._fireNotification(INSTALL_FINISHED, getLocalizedError("error-207"));
214
result.remove(false);
218
onProgress: function(aRequest, aContext, aProgress, aProgressMax)
220
gPluginInstaller.pluginInstallationProgressMeter(this._plugin.pid,
225
onStatus: function(aRequest, aContext, aStatus, aStatusArg)
231
var PluginInstallService = {
234
* Start installation of installers and XPI plugins.
235
* @param aInstallerPlugins An array of objects which should have the
236
* properties "pid", "InstallerLocation",
237
* and "InstallerHash"
238
* @param aXPIPlugins An array of objects which should have the
239
* properties "pid", "XPILocation",
242
startPluginInstallation: function (aInstallerPlugins,
245
this._xpiPlugins = aXPIPlugins;
246
this._xpisPending = aXPIPlugins.length;
248
aXPIPlugins.forEach(function(plugin) {
249
AddonManager.getInstallForURL(plugin.XPILocation, function(install) {
250
install.addListener(PluginInstallService);
252
}, "application/x-xpinstall", plugin.XPIHash);
255
// InstallerObserver may finish immediately so we must initialise the
256
// installers after setting the number of installers and xpis pending
257
this._installersPending = aInstallerPlugins.length;
258
this._installerPlugins = [new InstallerObserver(plugin)
259
for each (plugin in aInstallerPlugins)];
262
_fireFinishedNotification: function()
264
if (this._installersPending == 0 && this._xpisPending == 0)
265
gPluginInstaller.pluginXPIInstallationProgress(null, INSTALLS_COMPLETE, null);
268
getPidForInstall: function(install) {
269
for (let i = 0; i < this._xpiPlugins.length; i++) {
270
if (install.sourceURI.spec == this._xpiPlugins[i].XPILocation)
271
return this._xpiPlugins[i].pid;
276
// InstallListener interface
277
onDownloadStarted: function(install) {
278
var pid = this.getPidForInstall(install);
279
gPluginInstaller.pluginXPIInstallationProgress(pid, DOWNLOAD_STARTED, null);
282
onDownloadProgress: function(install) {
283
var pid = this.getPidForInstall(install);
284
gPluginInstaller.pluginXPIInstallationProgressMeter(pid, install.progress,
285
install.maxProgress);
288
onDownloadEnded: function(install) {
289
var pid = this.getPidForInstall(install);
290
gPluginInstaller.pluginXPIInstallationProgress(pid, DOWNLOAD_FINISHED, null);
293
onDownloadFailed: function(install) {
294
var pid = this.getPidForInstall(install);
295
switch (install.error) {
296
case AddonManager.ERROR_NETWORK_FAILURE:
297
var errorMsg = getLocalizedError("error-228");
299
case AddonManager.ERROR_INCORRECT_HASH:
300
var errorMsg = getLocalizedError("error-261");
302
case AddonManager.ERROR_CORRUPT_FILE:
303
var errorMsg = getLocalizedError("error-207");
306
gPluginInstaller.pluginXPIInstallationProgress(pid, INSTALL_FINISHED, errorMsg);
309
this._fireFinishedNotification();
312
onInstallStarted: function(install) {
313
var pid = this.getPidForInstall(install);
314
gPluginInstaller.pluginXPIInstallationProgress(pid, INSTALL_STARTED, null);
317
onInstallEnded: function(install, addon) {
318
var pid = this.getPidForInstall(install);
319
gPluginInstaller.pluginXPIInstallationProgress(pid, INSTALL_FINISHED, null);
322
this._fireFinishedNotification();
325
onInstallFailed: function(install) {
326
var pid = this.getPidForInstall(install);
327
gPluginInstaller.pluginXPIInstallationProgress(pid, INSTALL_FINISHED,
328
getLocalizedError("error-203"));
331
this._fireFinishedNotification();