Wed, 31 Dec 2014 06:09:35 +0100
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 }