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.
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 | "use strict"; |
michael@0 | 6 | |
michael@0 | 7 | const {classes: Cc, interfaces: Ci, utils: Cu} = Components; |
michael@0 | 8 | |
michael@0 | 9 | Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
michael@0 | 10 | Cu.import("resource://gre/modules/Services.jsm"); |
michael@0 | 11 | Cu.import("resource://gre/modules/Preferences.jsm"); |
michael@0 | 12 | Cu.import("resource://gre/modules/Log.jsm"); |
michael@0 | 13 | |
michael@0 | 14 | const XPINSTALL_MIMETYPE = "application/x-xpinstall"; |
michael@0 | 15 | |
michael@0 | 16 | const MSG_INSTALL_ENABLED = "WebInstallerIsInstallEnabled"; |
michael@0 | 17 | const MSG_INSTALL_ADDONS = "WebInstallerInstallAddonsFromWebpage"; |
michael@0 | 18 | const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback"; |
michael@0 | 19 | |
michael@0 | 20 | |
michael@0 | 21 | let log = Log.repository.getLogger("AddonManager.InstallTrigger"); |
michael@0 | 22 | log.level = Log.Level[Preferences.get("extensions.logging.enabled", false) ? "Warn" : "Trace"]; |
michael@0 | 23 | |
michael@0 | 24 | function CallbackObject(id, callback, urls, mediator) { |
michael@0 | 25 | this.id = id; |
michael@0 | 26 | this.callback = callback; |
michael@0 | 27 | this.urls = new Set(urls); |
michael@0 | 28 | this.callCallback = function(url, status) { |
michael@0 | 29 | try { |
michael@0 | 30 | this.callback(url, status); |
michael@0 | 31 | } |
michael@0 | 32 | catch (e) { |
michael@0 | 33 | log.warn("InstallTrigger callback threw an exception: " + e); |
michael@0 | 34 | } |
michael@0 | 35 | |
michael@0 | 36 | this.urls.delete(url); |
michael@0 | 37 | if (this.urls.size == 0) |
michael@0 | 38 | mediator._callbacks.delete(id); |
michael@0 | 39 | }; |
michael@0 | 40 | } |
michael@0 | 41 | |
michael@0 | 42 | function RemoteMediator(windowID) { |
michael@0 | 43 | this._windowID = windowID; |
michael@0 | 44 | this._lastCallbackID = 0; |
michael@0 | 45 | this._callbacks = new Map(); |
michael@0 | 46 | this.mm = Cc["@mozilla.org/childprocessmessagemanager;1"] |
michael@0 | 47 | .getService(Ci.nsISyncMessageSender); |
michael@0 | 48 | this.mm.addWeakMessageListener(MSG_INSTALL_CALLBACK, this); |
michael@0 | 49 | } |
michael@0 | 50 | |
michael@0 | 51 | RemoteMediator.prototype = { |
michael@0 | 52 | receiveMessage: function(message) { |
michael@0 | 53 | if (message.name == MSG_INSTALL_CALLBACK) { |
michael@0 | 54 | let payload = message.data; |
michael@0 | 55 | let callbackHandler = this._callbacks.get(payload.callbackID); |
michael@0 | 56 | if (callbackHandler) { |
michael@0 | 57 | callbackHandler.callCallback(payload.url, payload.status); |
michael@0 | 58 | } |
michael@0 | 59 | } |
michael@0 | 60 | }, |
michael@0 | 61 | |
michael@0 | 62 | enabled: function(url) { |
michael@0 | 63 | let params = { |
michael@0 | 64 | referer: url, |
michael@0 | 65 | mimetype: XPINSTALL_MIMETYPE |
michael@0 | 66 | }; |
michael@0 | 67 | return this.mm.sendSyncMessage(MSG_INSTALL_ENABLED, params)[0]; |
michael@0 | 68 | }, |
michael@0 | 69 | |
michael@0 | 70 | install: function(installs, referer, callback, window) { |
michael@0 | 71 | let callbackID = this._addCallback(callback, installs.uris); |
michael@0 | 72 | |
michael@0 | 73 | installs.mimetype = XPINSTALL_MIMETYPE; |
michael@0 | 74 | installs.referer = referer; |
michael@0 | 75 | installs.callbackID = callbackID; |
michael@0 | 76 | |
michael@0 | 77 | return this.mm.sendSyncMessage(MSG_INSTALL_ADDONS, installs, {win: window})[0]; |
michael@0 | 78 | }, |
michael@0 | 79 | |
michael@0 | 80 | _addCallback: function(callback, urls) { |
michael@0 | 81 | if (!callback || typeof callback != "function") |
michael@0 | 82 | return -1; |
michael@0 | 83 | |
michael@0 | 84 | let callbackID = this._windowID + "-" + ++this._lastCallbackID; |
michael@0 | 85 | let callbackObject = new CallbackObject(callbackID, callback, urls, this); |
michael@0 | 86 | this._callbacks.set(callbackID, callbackObject); |
michael@0 | 87 | return callbackID; |
michael@0 | 88 | }, |
michael@0 | 89 | |
michael@0 | 90 | QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference]) |
michael@0 | 91 | }; |
michael@0 | 92 | |
michael@0 | 93 | |
michael@0 | 94 | function InstallTrigger() { |
michael@0 | 95 | } |
michael@0 | 96 | |
michael@0 | 97 | InstallTrigger.prototype = { |
michael@0 | 98 | // Here be magic. We've declared ourselves as providing the |
michael@0 | 99 | // nsIDOMGlobalPropertyInitializer interface, and are registered in the |
michael@0 | 100 | // "JavaScript-global-property" category in the XPCOM category manager. This |
michael@0 | 101 | // means that for newly created windows, XPCOM will createinstance this |
michael@0 | 102 | // object, and then call init, passing in the window for which we need to |
michael@0 | 103 | // provide an instance. We then initialize ourselves and return the webidl |
michael@0 | 104 | // version of this object using the webidl-provided _create method, which |
michael@0 | 105 | // XPCOM will then duly expose as a property value on the window. All this |
michael@0 | 106 | // indirection is necessary because webidl does not (yet) support statics |
michael@0 | 107 | // (bug 863952). See bug 926712 for more details about this implementation. |
michael@0 | 108 | init: function(window) { |
michael@0 | 109 | this._window = window; |
michael@0 | 110 | this._principal = window.document.nodePrincipal; |
michael@0 | 111 | this._url = window.document.documentURIObject; |
michael@0 | 112 | |
michael@0 | 113 | window.QueryInterface(Components.interfaces.nsIInterfaceRequestor); |
michael@0 | 114 | let utils = window.getInterface(Components.interfaces.nsIDOMWindowUtils); |
michael@0 | 115 | this._mediator = new RemoteMediator(utils.currentInnerWindowID); |
michael@0 | 116 | |
michael@0 | 117 | return window.InstallTriggerImpl._create(window, this); |
michael@0 | 118 | }, |
michael@0 | 119 | |
michael@0 | 120 | enabled: function() { |
michael@0 | 121 | return this._mediator.enabled(this._url.spec); |
michael@0 | 122 | }, |
michael@0 | 123 | |
michael@0 | 124 | updateEnabled: function() { |
michael@0 | 125 | return this.enabled(); |
michael@0 | 126 | }, |
michael@0 | 127 | |
michael@0 | 128 | install: function(installs, callback) { |
michael@0 | 129 | let installData = { |
michael@0 | 130 | uris: [], |
michael@0 | 131 | hashes: [], |
michael@0 | 132 | names: [], |
michael@0 | 133 | icons: [], |
michael@0 | 134 | }; |
michael@0 | 135 | |
michael@0 | 136 | for (let name of Object.keys(installs)) { |
michael@0 | 137 | let item = installs[name]; |
michael@0 | 138 | if (typeof item === "string") { |
michael@0 | 139 | item = { URL: item }; |
michael@0 | 140 | } |
michael@0 | 141 | if (!item.URL) { |
michael@0 | 142 | throw new this._window.DOMError("Error", "Missing URL property for '" + name + "'"); |
michael@0 | 143 | } |
michael@0 | 144 | |
michael@0 | 145 | let url = this._resolveURL(item.URL); |
michael@0 | 146 | if (!this._checkLoadURIFromScript(url)) { |
michael@0 | 147 | throw new this._window.DOMError("SecurityError", "Insufficient permissions to install: " + url.spec); |
michael@0 | 148 | } |
michael@0 | 149 | |
michael@0 | 150 | let iconUrl = null; |
michael@0 | 151 | if (item.IconURL) { |
michael@0 | 152 | iconUrl = this._resolveURL(item.IconURL); |
michael@0 | 153 | if (!this._checkLoadURIFromScript(iconUrl)) { |
michael@0 | 154 | iconUrl = null; // If page can't load the icon, just ignore it |
michael@0 | 155 | } |
michael@0 | 156 | } |
michael@0 | 157 | |
michael@0 | 158 | installData.uris.push(url.spec); |
michael@0 | 159 | installData.hashes.push(item.Hash || null); |
michael@0 | 160 | installData.names.push(name); |
michael@0 | 161 | installData.icons.push(iconUrl ? iconUrl.spec : null); |
michael@0 | 162 | } |
michael@0 | 163 | |
michael@0 | 164 | return this._mediator.install(installData, this._url.spec, callback, this._window); |
michael@0 | 165 | }, |
michael@0 | 166 | |
michael@0 | 167 | startSoftwareUpdate: function(url, flags) { |
michael@0 | 168 | let filename = Services.io.newURI(url, null, null) |
michael@0 | 169 | .QueryInterface(Ci.nsIURL) |
michael@0 | 170 | .filename; |
michael@0 | 171 | let args = {}; |
michael@0 | 172 | args[filename] = { "URL": url }; |
michael@0 | 173 | return this.install(args); |
michael@0 | 174 | }, |
michael@0 | 175 | |
michael@0 | 176 | installChrome: function(type, url, skin) { |
michael@0 | 177 | return this.startSoftwareUpdate(url); |
michael@0 | 178 | }, |
michael@0 | 179 | |
michael@0 | 180 | _resolveURL: function (url) { |
michael@0 | 181 | return Services.io.newURI(url, null, this._url); |
michael@0 | 182 | }, |
michael@0 | 183 | |
michael@0 | 184 | _checkLoadURIFromScript: function(uri) { |
michael@0 | 185 | let secman = Services.scriptSecurityManager; |
michael@0 | 186 | try { |
michael@0 | 187 | secman.checkLoadURIWithPrincipal(this._principal, |
michael@0 | 188 | uri, |
michael@0 | 189 | secman.DISALLOW_INHERIT_PRINCIPAL); |
michael@0 | 190 | return true; |
michael@0 | 191 | } |
michael@0 | 192 | catch(e) { |
michael@0 | 193 | return false; |
michael@0 | 194 | } |
michael@0 | 195 | }, |
michael@0 | 196 | |
michael@0 | 197 | classID: Components.ID("{9df8ef2b-94da-45c9-ab9f-132eb55fddf1}"), |
michael@0 | 198 | contractID: "@mozilla.org/addons/installtrigger;1", |
michael@0 | 199 | QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIDOMGlobalPropertyInitializer]) |
michael@0 | 200 | }; |
michael@0 | 201 | |
michael@0 | 202 | |
michael@0 | 203 | |
michael@0 | 204 | this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InstallTrigger]); |