js/xpconnect/loader/XPCOMUtils.jsm

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

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 }

mercurial