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 /**
6 * This is a default implementation of amIWebInstallListener that should work
7 * for most applications but can be overriden. It notifies the observer service
8 * about blocked installs. For normal installs it pops up an install
9 * confirmation when all the add-ons have been downloaded.
10 */
12 "use strict";
14 const Cc = Components.classes;
15 const Ci = Components.interfaces;
16 const Cr = Components.results;
17 const Cu = Components.utils;
19 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
20 Cu.import("resource://gre/modules/AddonManager.jsm");
21 Cu.import("resource://gre/modules/Services.jsm");
23 const URI_XPINSTALL_DIALOG = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
25 // Installation can begin from any of these states
26 const READY_STATES = [
27 AddonManager.STATE_AVAILABLE,
28 AddonManager.STATE_DOWNLOAD_FAILED,
29 AddonManager.STATE_INSTALL_FAILED,
30 AddonManager.STATE_CANCELLED
31 ];
33 Cu.import("resource://gre/modules/Log.jsm");
34 const LOGGER_ID = "addons.weblistener";
36 // Create a new logger for use by the Addons Web Listener
37 // (Requires AddonManager.jsm)
38 let logger = Log.repository.getLogger(LOGGER_ID);
40 function notifyObservers(aTopic, aWindow, aUri, aInstalls) {
41 let info = {
42 originatingWindow: aWindow,
43 originatingURI: aUri,
44 installs: aInstalls,
46 QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
47 };
48 Services.obs.notifyObservers(info, aTopic, null);
49 }
51 /**
52 * Creates a new installer to monitor downloads and prompt to install when
53 * ready
54 *
55 * @param aWindow
56 * The window that started the installations
57 * @param aUrl
58 * The URL that started the installations
59 * @param aInstalls
60 * An array of AddonInstalls
61 */
62 function Installer(aWindow, aUrl, aInstalls) {
63 this.window = aWindow;
64 this.url = aUrl;
65 this.downloads = aInstalls;
66 this.installed = [];
68 notifyObservers("addon-install-started", aWindow, aUrl, aInstalls);
70 aInstalls.forEach(function(aInstall) {
71 aInstall.addListener(this);
73 // Start downloading if it hasn't already begun
74 if (READY_STATES.indexOf(aInstall.state) != -1)
75 aInstall.install();
76 }, this);
78 this.checkAllDownloaded();
79 }
81 Installer.prototype = {
82 window: null,
83 downloads: null,
84 installed: null,
85 isDownloading: true,
87 /**
88 * Checks if all downloads are now complete and if so prompts to install.
89 */
90 checkAllDownloaded: function Installer_checkAllDownloaded() {
91 // Prevent re-entrancy caused by the confirmation dialog cancelling unwanted
92 // installs.
93 if (!this.isDownloading)
94 return;
96 var failed = [];
97 var installs = [];
99 for (let install of this.downloads) {
100 switch (install.state) {
101 case AddonManager.STATE_AVAILABLE:
102 case AddonManager.STATE_DOWNLOADING:
103 // Exit early if any add-ons haven't started downloading yet or are
104 // still downloading
105 return;
106 case AddonManager.STATE_DOWNLOAD_FAILED:
107 failed.push(install);
108 break;
109 case AddonManager.STATE_DOWNLOADED:
110 // App disabled items are not compatible and so fail to install
111 if (install.addon.appDisabled)
112 failed.push(install);
113 else
114 installs.push(install);
116 if (install.linkedInstalls) {
117 install.linkedInstalls.forEach(function(aInstall) {
118 aInstall.addListener(this);
119 // App disabled items are not compatible and so fail to install
120 if (aInstall.addon.appDisabled)
121 failed.push(aInstall);
122 else
123 installs.push(aInstall);
124 }, this);
125 }
126 break;
127 case AddonManager.STATE_CANCELLED:
128 // Just ignore cancelled downloads
129 break;
130 default:
131 logger.warn("Download of " + install.sourceURI.spec + " in unexpected state " +
132 install.state);
133 }
134 }
136 this.isDownloading = false;
137 this.downloads = installs;
139 if (failed.length > 0) {
140 // Stop listening and cancel any installs that are failed because of
141 // compatibility reasons.
142 failed.forEach(function(aInstall) {
143 if (aInstall.state == AddonManager.STATE_DOWNLOADED) {
144 aInstall.removeListener(this);
145 aInstall.cancel();
146 }
147 }, this);
148 notifyObservers("addon-install-failed", this.window, this.url, failed);
149 }
151 // If none of the downloads were successful then exit early
152 if (this.downloads.length == 0)
153 return;
155 // Check for a custom installation prompt that may be provided by the
156 // applicaton
157 if ("@mozilla.org/addons/web-install-prompt;1" in Cc) {
158 try {
159 let prompt = Cc["@mozilla.org/addons/web-install-prompt;1"].
160 getService(Ci.amIWebInstallPrompt);
161 prompt.confirm(this.window, this.url, this.downloads, this.downloads.length);
162 return;
163 }
164 catch (e) {}
165 }
167 let args = {};
168 args.url = this.url;
169 args.installs = this.downloads;
170 args.wrappedJSObject = args;
172 try {
173 Cc["@mozilla.org/base/telemetry;1"].
174 getService(Ci.nsITelemetry).
175 getHistogramById("SECURITY_UI").
176 add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL);
177 Services.ww.openWindow(this.window, URI_XPINSTALL_DIALOG,
178 null, "chrome,modal,centerscreen", args);
179 } catch (e) {
180 this.downloads.forEach(function(aInstall) {
181 aInstall.removeListener(this);
182 // Cancel the installs, as currently there is no way to make them fail
183 // from here.
184 aInstall.cancel();
185 }, this);
186 notifyObservers("addon-install-cancelled", this.window, this.url,
187 this.downloads);
188 }
189 },
191 /**
192 * Checks if all installs are now complete and if so notifies observers.
193 */
194 checkAllInstalled: function Installer_checkAllInstalled() {
195 var failed = [];
197 for (let install of this.downloads) {
198 switch(install.state) {
199 case AddonManager.STATE_DOWNLOADED:
200 case AddonManager.STATE_INSTALLING:
201 // Exit early if any add-ons haven't started installing yet or are
202 // still installing
203 return;
204 case AddonManager.STATE_INSTALL_FAILED:
205 failed.push(install);
206 break;
207 }
208 }
210 this.downloads = null;
212 if (failed.length > 0)
213 notifyObservers("addon-install-failed", this.window, this.url, failed);
215 if (this.installed.length > 0)
216 notifyObservers("addon-install-complete", this.window, this.url, this.installed);
217 this.installed = null;
218 },
220 onDownloadCancelled: function Installer_onDownloadCancelled(aInstall) {
221 aInstall.removeListener(this);
222 this.checkAllDownloaded();
223 },
225 onDownloadFailed: function Installer_onDownloadFailed(aInstall) {
226 aInstall.removeListener(this);
227 this.checkAllDownloaded();
228 },
230 onDownloadEnded: function Installer_onDownloadEnded(aInstall) {
231 this.checkAllDownloaded();
232 return false;
233 },
235 onInstallCancelled: function Installer_onInstallCancelled(aInstall) {
236 aInstall.removeListener(this);
237 this.checkAllInstalled();
238 },
240 onInstallFailed: function Installer_onInstallFailed(aInstall) {
241 aInstall.removeListener(this);
242 this.checkAllInstalled();
243 },
245 onInstallEnded: function Installer_onInstallEnded(aInstall) {
246 aInstall.removeListener(this);
247 this.installed.push(aInstall);
249 // If installing a theme that is disabled and can be enabled then enable it
250 if (aInstall.addon.type == "theme" &&
251 aInstall.addon.userDisabled == true &&
252 aInstall.addon.appDisabled == false) {
253 aInstall.addon.userDisabled = false;
254 }
256 this.checkAllInstalled();
257 }
258 };
260 function extWebInstallListener() {
261 }
263 extWebInstallListener.prototype = {
264 /**
265 * @see amIWebInstallListener.idl
266 */
267 onWebInstallDisabled: function extWebInstallListener_onWebInstallDisabled(aWindow, aUri, aInstalls) {
268 let info = {
269 originatingWindow: aWindow,
270 originatingURI: aUri,
271 installs: aInstalls,
273 QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
274 };
275 Services.obs.notifyObservers(info, "addon-install-disabled", null);
276 },
278 /**
279 * @see amIWebInstallListener.idl
280 */
281 onWebInstallBlocked: function extWebInstallListener_onWebInstallBlocked(aWindow, aUri, aInstalls) {
282 let info = {
283 originatingWindow: aWindow,
284 originatingURI: aUri,
285 installs: aInstalls,
287 install: function onWebInstallBlocked_install() {
288 new Installer(this.originatingWindow, this.originatingURI, this.installs);
289 },
291 QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
292 };
293 Services.obs.notifyObservers(info, "addon-install-blocked", null);
295 return false;
296 },
298 /**
299 * @see amIWebInstallListener.idl
300 */
301 onWebInstallRequested: function extWebInstallListener_onWebInstallRequested(aWindow, aUri, aInstalls) {
302 new Installer(aWindow, aUri, aInstalls);
304 // We start the installs ourself
305 return false;
306 },
308 classDescription: "XPI Install Handler",
309 contractID: "@mozilla.org/addons/web-install-listener;1",
310 classID: Components.ID("{0f38e086-89a3-40a5-8ffc-9b694de1d04a}"),
311 QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallListener])
312 };
314 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([extWebInstallListener]);