Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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]); |