toolkit/mozapps/extensions/amWebInstallListener.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.

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

mercurial