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 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
michael@0 | 2 | * vim: sw=2 ts=2 sts=2 et filetype=javascript |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | /** |
michael@0 | 8 | * Utilities for JavaScript components loaded by the JS component |
michael@0 | 9 | * loader. |
michael@0 | 10 | * |
michael@0 | 11 | * Import into a JS component using |
michael@0 | 12 | * 'Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");' |
michael@0 | 13 | * |
michael@0 | 14 | * Exposing a JS 'class' as a component using these utility methods consists |
michael@0 | 15 | * of several steps: |
michael@0 | 16 | * 0. Import XPCOMUtils, as described above. |
michael@0 | 17 | * 1. Declare the 'class' (or multiple classes) implementing the component(s): |
michael@0 | 18 | * function MyComponent() { |
michael@0 | 19 | * // constructor |
michael@0 | 20 | * } |
michael@0 | 21 | * MyComponent.prototype = { |
michael@0 | 22 | * // properties required for XPCOM registration: |
michael@0 | 23 | * classID: Components.ID("{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"), |
michael@0 | 24 | * |
michael@0 | 25 | * // [optional] custom factory (an object implementing nsIFactory). If not |
michael@0 | 26 | * // provided, the default factory is used, which returns |
michael@0 | 27 | * // |(new MyComponent()).QueryInterface(iid)| in its createInstance(). |
michael@0 | 28 | * _xpcom_factory: { ... }, |
michael@0 | 29 | * |
michael@0 | 30 | * // QueryInterface implementation, e.g. using the generateQI helper |
michael@0 | 31 | * QueryInterface: XPCOMUtils.generateQI( |
michael@0 | 32 | * [Components.interfaces.nsIObserver, |
michael@0 | 33 | * Components.interfaces.nsIMyInterface, |
michael@0 | 34 | * "nsIFoo", |
michael@0 | 35 | * "nsIBar" ]), |
michael@0 | 36 | * |
michael@0 | 37 | * // [optional] classInfo implementation, e.g. using the generateCI helper. |
michael@0 | 38 | * // Will be automatically returned from QueryInterface if that was |
michael@0 | 39 | * // generated with the generateQI helper. |
michael@0 | 40 | * classInfo: XPCOMUtils.generateCI( |
michael@0 | 41 | * {classID: Components.ID("{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"), |
michael@0 | 42 | * contractID: "@example.com/xxx;1", |
michael@0 | 43 | * classDescription: "unique text description", |
michael@0 | 44 | * interfaces: [Components.interfaces.nsIObserver, |
michael@0 | 45 | * Components.interfaces.nsIMyInterface, |
michael@0 | 46 | * "nsIFoo", |
michael@0 | 47 | * "nsIBar"], |
michael@0 | 48 | * flags: Ci.nsIClassInfo.SINGLETON}), |
michael@0 | 49 | * |
michael@0 | 50 | * // The following properties were used prior to Mozilla 2, but are no |
michael@0 | 51 | * // longer supported. They may still be included for compatibility with |
michael@0 | 52 | * // prior versions of XPCOMUtils. In Mozilla 2, this information is |
michael@0 | 53 | * // included in the .manifest file which registers this JS component. |
michael@0 | 54 | * classDescription: "unique text description", |
michael@0 | 55 | * contractID: "@example.com/xxx;1", |
michael@0 | 56 | * |
michael@0 | 57 | * // [optional] an array of categories to register this component in. |
michael@0 | 58 | * _xpcom_categories: [{ |
michael@0 | 59 | * // Each object in the array specifies the parameters to pass to |
michael@0 | 60 | * // nsICategoryManager.addCategoryEntry(). 'true' is passed for |
michael@0 | 61 | * // both aPersist and aReplace params. |
michael@0 | 62 | * category: "some-category", |
michael@0 | 63 | * // optional, defaults to the object's classDescription |
michael@0 | 64 | * entry: "entry name", |
michael@0 | 65 | * // optional, defaults to the object's contractID (unless |
michael@0 | 66 | * // 'service' is specified) |
michael@0 | 67 | * value: "...", |
michael@0 | 68 | * // optional, defaults to false. When set to true, and only if 'value' |
michael@0 | 69 | * // is not specified, the concatenation of the string "service," and the |
michael@0 | 70 | * // object's contractID is passed as aValue parameter of addCategoryEntry. |
michael@0 | 71 | * service: true, |
michael@0 | 72 | * // optional, it can be an array of applications' IDs in the form: |
michael@0 | 73 | * // [ "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}", ... ] |
michael@0 | 74 | * // If defined the component will be registered in this category only for |
michael@0 | 75 | * // the provided applications. |
michael@0 | 76 | * apps: [...] |
michael@0 | 77 | * }], |
michael@0 | 78 | * |
michael@0 | 79 | * // ...component implementation... |
michael@0 | 80 | * }; |
michael@0 | 81 | * |
michael@0 | 82 | * 2. Create an array of component constructors (like the one |
michael@0 | 83 | * created in step 1): |
michael@0 | 84 | * var components = [MyComponent]; |
michael@0 | 85 | * |
michael@0 | 86 | * 3. Define the NSGetFactory entry point: |
michael@0 | 87 | * this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components); |
michael@0 | 88 | */ |
michael@0 | 89 | |
michael@0 | 90 | |
michael@0 | 91 | this.EXPORTED_SYMBOLS = [ "XPCOMUtils" ]; |
michael@0 | 92 | |
michael@0 | 93 | const Cc = Components.classes; |
michael@0 | 94 | const Ci = Components.interfaces; |
michael@0 | 95 | const Cr = Components.results; |
michael@0 | 96 | const Cu = Components.utils; |
michael@0 | 97 | |
michael@0 | 98 | this.XPCOMUtils = { |
michael@0 | 99 | /** |
michael@0 | 100 | * Generate a QueryInterface implementation. The returned function must be |
michael@0 | 101 | * assigned to the 'QueryInterface' property of a JS object. When invoked on |
michael@0 | 102 | * that object, it checks if the given iid is listed in the |interfaces| |
michael@0 | 103 | * param, and if it is, returns |this| (the object it was called on). |
michael@0 | 104 | * If the JS object has a classInfo property it'll be returned for the |
michael@0 | 105 | * nsIClassInfo IID, generateCI can be used to generate the classInfo |
michael@0 | 106 | * property. |
michael@0 | 107 | */ |
michael@0 | 108 | generateQI: function XPCU_generateQI(interfaces) { |
michael@0 | 109 | /* Note that Ci[Ci.x] == Ci.x for all x */ |
michael@0 | 110 | return makeQI([Ci[i].name for each (i in interfaces) if (Ci[i])]); |
michael@0 | 111 | }, |
michael@0 | 112 | |
michael@0 | 113 | /** |
michael@0 | 114 | * Generate a ClassInfo implementation for a component. The returned object |
michael@0 | 115 | * must be assigned to the 'classInfo' property of a JS object. The first and |
michael@0 | 116 | * only argument should be an object that contains a number of optional |
michael@0 | 117 | * properties: "interfaces", "contractID", "classDescription", "classID" and |
michael@0 | 118 | * "flags". The values of the properties will be returned as the values of the |
michael@0 | 119 | * various properties of the nsIClassInfo implementation. |
michael@0 | 120 | */ |
michael@0 | 121 | generateCI: function XPCU_generateCI(classInfo) |
michael@0 | 122 | { |
michael@0 | 123 | if (QueryInterface in classInfo) |
michael@0 | 124 | throw Error("In generateCI, don't use a component for generating classInfo"); |
michael@0 | 125 | /* Note that Ci[Ci.x] == Ci.x for all x */ |
michael@0 | 126 | var _interfaces = [Ci[i] for each (i in classInfo.interfaces) if (Ci[i])]; |
michael@0 | 127 | return { |
michael@0 | 128 | getInterfaces: function XPCU_getInterfaces(countRef) { |
michael@0 | 129 | countRef.value = _interfaces.length; |
michael@0 | 130 | return _interfaces; |
michael@0 | 131 | }, |
michael@0 | 132 | getHelperForLanguage: function XPCU_getHelperForLanguage(language) null, |
michael@0 | 133 | contractID: classInfo.contractID, |
michael@0 | 134 | classDescription: classInfo.classDescription, |
michael@0 | 135 | classID: classInfo.classID, |
michael@0 | 136 | implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT, |
michael@0 | 137 | flags: classInfo.flags, |
michael@0 | 138 | QueryInterface: this.generateQI([Ci.nsIClassInfo]) |
michael@0 | 139 | }; |
michael@0 | 140 | }, |
michael@0 | 141 | |
michael@0 | 142 | /** |
michael@0 | 143 | * Generate a NSGetFactory function given an array of components. |
michael@0 | 144 | */ |
michael@0 | 145 | generateNSGetFactory: function XPCU_generateNSGetFactory(componentsArray) { |
michael@0 | 146 | let classes = {}; |
michael@0 | 147 | for each (let component in componentsArray) { |
michael@0 | 148 | if (!(component.prototype.classID instanceof Components.ID)) |
michael@0 | 149 | throw Error("In generateNSGetFactory, classID missing or incorrect for component " + component); |
michael@0 | 150 | |
michael@0 | 151 | classes[component.prototype.classID] = this._getFactory(component); |
michael@0 | 152 | } |
michael@0 | 153 | return function NSGetFactory(cid) { |
michael@0 | 154 | let cidstring = cid.toString(); |
michael@0 | 155 | if (cidstring in classes) |
michael@0 | 156 | return classes[cidstring]; |
michael@0 | 157 | throw Cr.NS_ERROR_FACTORY_NOT_REGISTERED; |
michael@0 | 158 | } |
michael@0 | 159 | }, |
michael@0 | 160 | |
michael@0 | 161 | /** |
michael@0 | 162 | * Defines a getter on a specified object that will be created upon first use. |
michael@0 | 163 | * |
michael@0 | 164 | * @param aObject |
michael@0 | 165 | * The object to define the lazy getter on. |
michael@0 | 166 | * @param aName |
michael@0 | 167 | * The name of the getter to define on aObject. |
michael@0 | 168 | * @param aLambda |
michael@0 | 169 | * A function that returns what the getter should return. This will |
michael@0 | 170 | * only ever be called once. |
michael@0 | 171 | */ |
michael@0 | 172 | defineLazyGetter: function XPCU_defineLazyGetter(aObject, aName, aLambda) |
michael@0 | 173 | { |
michael@0 | 174 | Object.defineProperty(aObject, aName, { |
michael@0 | 175 | get: function () { |
michael@0 | 176 | delete aObject[aName]; |
michael@0 | 177 | return aObject[aName] = aLambda.apply(aObject); |
michael@0 | 178 | }, |
michael@0 | 179 | configurable: true, |
michael@0 | 180 | enumerable: true |
michael@0 | 181 | }); |
michael@0 | 182 | }, |
michael@0 | 183 | |
michael@0 | 184 | /** |
michael@0 | 185 | * Defines a getter on a specified object for a service. The service will not |
michael@0 | 186 | * be obtained until first use. |
michael@0 | 187 | * |
michael@0 | 188 | * @param aObject |
michael@0 | 189 | * The object to define the lazy getter on. |
michael@0 | 190 | * @param aName |
michael@0 | 191 | * The name of the getter to define on aObject for the service. |
michael@0 | 192 | * @param aContract |
michael@0 | 193 | * The contract used to obtain the service. |
michael@0 | 194 | * @param aInterfaceName |
michael@0 | 195 | * The name of the interface to query the service to. |
michael@0 | 196 | */ |
michael@0 | 197 | defineLazyServiceGetter: function XPCU_defineLazyServiceGetter(aObject, aName, |
michael@0 | 198 | aContract, |
michael@0 | 199 | aInterfaceName) |
michael@0 | 200 | { |
michael@0 | 201 | this.defineLazyGetter(aObject, aName, function XPCU_serviceLambda() { |
michael@0 | 202 | return Cc[aContract].getService(Ci[aInterfaceName]); |
michael@0 | 203 | }); |
michael@0 | 204 | }, |
michael@0 | 205 | |
michael@0 | 206 | /** |
michael@0 | 207 | * Defines a getter on a specified object for a module. The module will not |
michael@0 | 208 | * be imported until first use. |
michael@0 | 209 | * |
michael@0 | 210 | * @param aObject |
michael@0 | 211 | * The object to define the lazy getter on. |
michael@0 | 212 | * @param aName |
michael@0 | 213 | * The name of the getter to define on aObject for the module. |
michael@0 | 214 | * @param aResource |
michael@0 | 215 | * The URL used to obtain the module. |
michael@0 | 216 | * @param aSymbol |
michael@0 | 217 | * The name of the symbol exported by the module. |
michael@0 | 218 | * This parameter is optional and defaults to aName. |
michael@0 | 219 | */ |
michael@0 | 220 | defineLazyModuleGetter: function XPCU_defineLazyModuleGetter(aObject, aName, |
michael@0 | 221 | aResource, |
michael@0 | 222 | aSymbol) |
michael@0 | 223 | { |
michael@0 | 224 | this.defineLazyGetter(aObject, aName, function XPCU_moduleLambda() { |
michael@0 | 225 | var temp = {}; |
michael@0 | 226 | Cu.import(aResource, temp); |
michael@0 | 227 | return temp[aSymbol || aName]; |
michael@0 | 228 | }); |
michael@0 | 229 | }, |
michael@0 | 230 | |
michael@0 | 231 | /** |
michael@0 | 232 | * Convenience access to category manager |
michael@0 | 233 | */ |
michael@0 | 234 | get categoryManager() { |
michael@0 | 235 | return Components.classes["@mozilla.org/categorymanager;1"] |
michael@0 | 236 | .getService(Ci.nsICategoryManager); |
michael@0 | 237 | }, |
michael@0 | 238 | |
michael@0 | 239 | /** |
michael@0 | 240 | * Helper which iterates over a nsISimpleEnumerator. |
michael@0 | 241 | * @param e The nsISimpleEnumerator to iterate over. |
michael@0 | 242 | * @param i The expected interface for each element. |
michael@0 | 243 | */ |
michael@0 | 244 | IterSimpleEnumerator: function XPCU_IterSimpleEnumerator(e, i) |
michael@0 | 245 | { |
michael@0 | 246 | while (e.hasMoreElements()) |
michael@0 | 247 | yield e.getNext().QueryInterface(i); |
michael@0 | 248 | }, |
michael@0 | 249 | |
michael@0 | 250 | /** |
michael@0 | 251 | * Helper which iterates over a string enumerator. |
michael@0 | 252 | * @param e The string enumerator (nsIUTF8StringEnumerator or |
michael@0 | 253 | * nsIStringEnumerator) over which to iterate. |
michael@0 | 254 | */ |
michael@0 | 255 | IterStringEnumerator: function XPCU_IterStringEnumerator(e) |
michael@0 | 256 | { |
michael@0 | 257 | while (e.hasMore()) |
michael@0 | 258 | yield e.getNext(); |
michael@0 | 259 | }, |
michael@0 | 260 | |
michael@0 | 261 | /** |
michael@0 | 262 | * Returns an nsIFactory for |component|. |
michael@0 | 263 | */ |
michael@0 | 264 | _getFactory: function XPCOMUtils__getFactory(component) { |
michael@0 | 265 | var factory = component.prototype._xpcom_factory; |
michael@0 | 266 | if (!factory) { |
michael@0 | 267 | factory = { |
michael@0 | 268 | createInstance: function(outer, iid) { |
michael@0 | 269 | if (outer) |
michael@0 | 270 | throw Cr.NS_ERROR_NO_AGGREGATION; |
michael@0 | 271 | return (new component()).QueryInterface(iid); |
michael@0 | 272 | } |
michael@0 | 273 | } |
michael@0 | 274 | } |
michael@0 | 275 | return factory; |
michael@0 | 276 | }, |
michael@0 | 277 | |
michael@0 | 278 | /** |
michael@0 | 279 | * Allows you to fake a relative import. Expects the global object from the |
michael@0 | 280 | * module that's calling us, and the relative filename that we wish to import. |
michael@0 | 281 | */ |
michael@0 | 282 | importRelative: function XPCOMUtils__importRelative(that, path, scope) { |
michael@0 | 283 | if (!("__URI__" in that)) |
michael@0 | 284 | throw Error("importRelative may only be used from a JSM, and its first argument "+ |
michael@0 | 285 | "must be that JSM's global object (hint: use this)"); |
michael@0 | 286 | let uri = that.__URI__; |
michael@0 | 287 | let i = uri.lastIndexOf("/"); |
michael@0 | 288 | Components.utils.import(uri.substring(0, i+1) + path, scope || that); |
michael@0 | 289 | }, |
michael@0 | 290 | |
michael@0 | 291 | /** |
michael@0 | 292 | * generates a singleton nsIFactory implementation that can be used as |
michael@0 | 293 | * the _xpcom_factory of the component. |
michael@0 | 294 | * @param aServiceConstructor |
michael@0 | 295 | * Constructor function of the component. |
michael@0 | 296 | */ |
michael@0 | 297 | generateSingletonFactory: |
michael@0 | 298 | function XPCOMUtils_generateSingletonFactory(aServiceConstructor) { |
michael@0 | 299 | return { |
michael@0 | 300 | _instance: null, |
michael@0 | 301 | createInstance: function XPCU_SF_createInstance(aOuter, aIID) { |
michael@0 | 302 | if (aOuter !== null) { |
michael@0 | 303 | throw Cr.NS_ERROR_NO_AGGREGATION; |
michael@0 | 304 | } |
michael@0 | 305 | if (this._instance === null) { |
michael@0 | 306 | this._instance = new aServiceConstructor(); |
michael@0 | 307 | } |
michael@0 | 308 | return this._instance.QueryInterface(aIID); |
michael@0 | 309 | }, |
michael@0 | 310 | lockFactory: function XPCU_SF_lockFactory(aDoLock) { |
michael@0 | 311 | throw Cr.NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 312 | }, |
michael@0 | 313 | QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]) |
michael@0 | 314 | }; |
michael@0 | 315 | }, |
michael@0 | 316 | }; |
michael@0 | 317 | |
michael@0 | 318 | /** |
michael@0 | 319 | * Helper for XPCOMUtils.generateQI to avoid leaks - see bug 381651#c1 |
michael@0 | 320 | */ |
michael@0 | 321 | function makeQI(interfaceNames) { |
michael@0 | 322 | return function XPCOMUtils_QueryInterface(iid) { |
michael@0 | 323 | if (iid.equals(Ci.nsISupports)) |
michael@0 | 324 | return this; |
michael@0 | 325 | if (iid.equals(Ci.nsIClassInfo) && "classInfo" in this) |
michael@0 | 326 | return this.classInfo; |
michael@0 | 327 | for each(let interfaceName in interfaceNames) { |
michael@0 | 328 | if (Ci[interfaceName].equals(iid)) |
michael@0 | 329 | return this; |
michael@0 | 330 | } |
michael@0 | 331 | |
michael@0 | 332 | throw Cr.NS_ERROR_NO_INTERFACE; |
michael@0 | 333 | }; |
michael@0 | 334 | } |