1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/xpconnect/loader/XPCOMUtils.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,334 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * vim: sw=2 ts=2 sts=2 et filetype=javascript 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/** 1.11 + * Utilities for JavaScript components loaded by the JS component 1.12 + * loader. 1.13 + * 1.14 + * Import into a JS component using 1.15 + * 'Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");' 1.16 + * 1.17 + * Exposing a JS 'class' as a component using these utility methods consists 1.18 + * of several steps: 1.19 + * 0. Import XPCOMUtils, as described above. 1.20 + * 1. Declare the 'class' (or multiple classes) implementing the component(s): 1.21 + * function MyComponent() { 1.22 + * // constructor 1.23 + * } 1.24 + * MyComponent.prototype = { 1.25 + * // properties required for XPCOM registration: 1.26 + * classID: Components.ID("{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"), 1.27 + * 1.28 + * // [optional] custom factory (an object implementing nsIFactory). If not 1.29 + * // provided, the default factory is used, which returns 1.30 + * // |(new MyComponent()).QueryInterface(iid)| in its createInstance(). 1.31 + * _xpcom_factory: { ... }, 1.32 + * 1.33 + * // QueryInterface implementation, e.g. using the generateQI helper 1.34 + * QueryInterface: XPCOMUtils.generateQI( 1.35 + * [Components.interfaces.nsIObserver, 1.36 + * Components.interfaces.nsIMyInterface, 1.37 + * "nsIFoo", 1.38 + * "nsIBar" ]), 1.39 + * 1.40 + * // [optional] classInfo implementation, e.g. using the generateCI helper. 1.41 + * // Will be automatically returned from QueryInterface if that was 1.42 + * // generated with the generateQI helper. 1.43 + * classInfo: XPCOMUtils.generateCI( 1.44 + * {classID: Components.ID("{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"), 1.45 + * contractID: "@example.com/xxx;1", 1.46 + * classDescription: "unique text description", 1.47 + * interfaces: [Components.interfaces.nsIObserver, 1.48 + * Components.interfaces.nsIMyInterface, 1.49 + * "nsIFoo", 1.50 + * "nsIBar"], 1.51 + * flags: Ci.nsIClassInfo.SINGLETON}), 1.52 + * 1.53 + * // The following properties were used prior to Mozilla 2, but are no 1.54 + * // longer supported. They may still be included for compatibility with 1.55 + * // prior versions of XPCOMUtils. In Mozilla 2, this information is 1.56 + * // included in the .manifest file which registers this JS component. 1.57 + * classDescription: "unique text description", 1.58 + * contractID: "@example.com/xxx;1", 1.59 + * 1.60 + * // [optional] an array of categories to register this component in. 1.61 + * _xpcom_categories: [{ 1.62 + * // Each object in the array specifies the parameters to pass to 1.63 + * // nsICategoryManager.addCategoryEntry(). 'true' is passed for 1.64 + * // both aPersist and aReplace params. 1.65 + * category: "some-category", 1.66 + * // optional, defaults to the object's classDescription 1.67 + * entry: "entry name", 1.68 + * // optional, defaults to the object's contractID (unless 1.69 + * // 'service' is specified) 1.70 + * value: "...", 1.71 + * // optional, defaults to false. When set to true, and only if 'value' 1.72 + * // is not specified, the concatenation of the string "service," and the 1.73 + * // object's contractID is passed as aValue parameter of addCategoryEntry. 1.74 + * service: true, 1.75 + * // optional, it can be an array of applications' IDs in the form: 1.76 + * // [ "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}", ... ] 1.77 + * // If defined the component will be registered in this category only for 1.78 + * // the provided applications. 1.79 + * apps: [...] 1.80 + * }], 1.81 + * 1.82 + * // ...component implementation... 1.83 + * }; 1.84 + * 1.85 + * 2. Create an array of component constructors (like the one 1.86 + * created in step 1): 1.87 + * var components = [MyComponent]; 1.88 + * 1.89 + * 3. Define the NSGetFactory entry point: 1.90 + * this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components); 1.91 + */ 1.92 + 1.93 + 1.94 +this.EXPORTED_SYMBOLS = [ "XPCOMUtils" ]; 1.95 + 1.96 +const Cc = Components.classes; 1.97 +const Ci = Components.interfaces; 1.98 +const Cr = Components.results; 1.99 +const Cu = Components.utils; 1.100 + 1.101 +this.XPCOMUtils = { 1.102 + /** 1.103 + * Generate a QueryInterface implementation. The returned function must be 1.104 + * assigned to the 'QueryInterface' property of a JS object. When invoked on 1.105 + * that object, it checks if the given iid is listed in the |interfaces| 1.106 + * param, and if it is, returns |this| (the object it was called on). 1.107 + * If the JS object has a classInfo property it'll be returned for the 1.108 + * nsIClassInfo IID, generateCI can be used to generate the classInfo 1.109 + * property. 1.110 + */ 1.111 + generateQI: function XPCU_generateQI(interfaces) { 1.112 + /* Note that Ci[Ci.x] == Ci.x for all x */ 1.113 + return makeQI([Ci[i].name for each (i in interfaces) if (Ci[i])]); 1.114 + }, 1.115 + 1.116 + /** 1.117 + * Generate a ClassInfo implementation for a component. The returned object 1.118 + * must be assigned to the 'classInfo' property of a JS object. The first and 1.119 + * only argument should be an object that contains a number of optional 1.120 + * properties: "interfaces", "contractID", "classDescription", "classID" and 1.121 + * "flags". The values of the properties will be returned as the values of the 1.122 + * various properties of the nsIClassInfo implementation. 1.123 + */ 1.124 + generateCI: function XPCU_generateCI(classInfo) 1.125 + { 1.126 + if (QueryInterface in classInfo) 1.127 + throw Error("In generateCI, don't use a component for generating classInfo"); 1.128 + /* Note that Ci[Ci.x] == Ci.x for all x */ 1.129 + var _interfaces = [Ci[i] for each (i in classInfo.interfaces) if (Ci[i])]; 1.130 + return { 1.131 + getInterfaces: function XPCU_getInterfaces(countRef) { 1.132 + countRef.value = _interfaces.length; 1.133 + return _interfaces; 1.134 + }, 1.135 + getHelperForLanguage: function XPCU_getHelperForLanguage(language) null, 1.136 + contractID: classInfo.contractID, 1.137 + classDescription: classInfo.classDescription, 1.138 + classID: classInfo.classID, 1.139 + implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT, 1.140 + flags: classInfo.flags, 1.141 + QueryInterface: this.generateQI([Ci.nsIClassInfo]) 1.142 + }; 1.143 + }, 1.144 + 1.145 + /** 1.146 + * Generate a NSGetFactory function given an array of components. 1.147 + */ 1.148 + generateNSGetFactory: function XPCU_generateNSGetFactory(componentsArray) { 1.149 + let classes = {}; 1.150 + for each (let component in componentsArray) { 1.151 + if (!(component.prototype.classID instanceof Components.ID)) 1.152 + throw Error("In generateNSGetFactory, classID missing or incorrect for component " + component); 1.153 + 1.154 + classes[component.prototype.classID] = this._getFactory(component); 1.155 + } 1.156 + return function NSGetFactory(cid) { 1.157 + let cidstring = cid.toString(); 1.158 + if (cidstring in classes) 1.159 + return classes[cidstring]; 1.160 + throw Cr.NS_ERROR_FACTORY_NOT_REGISTERED; 1.161 + } 1.162 + }, 1.163 + 1.164 + /** 1.165 + * Defines a getter on a specified object that will be created upon first use. 1.166 + * 1.167 + * @param aObject 1.168 + * The object to define the lazy getter on. 1.169 + * @param aName 1.170 + * The name of the getter to define on aObject. 1.171 + * @param aLambda 1.172 + * A function that returns what the getter should return. This will 1.173 + * only ever be called once. 1.174 + */ 1.175 + defineLazyGetter: function XPCU_defineLazyGetter(aObject, aName, aLambda) 1.176 + { 1.177 + Object.defineProperty(aObject, aName, { 1.178 + get: function () { 1.179 + delete aObject[aName]; 1.180 + return aObject[aName] = aLambda.apply(aObject); 1.181 + }, 1.182 + configurable: true, 1.183 + enumerable: true 1.184 + }); 1.185 + }, 1.186 + 1.187 + /** 1.188 + * Defines a getter on a specified object for a service. The service will not 1.189 + * be obtained until first use. 1.190 + * 1.191 + * @param aObject 1.192 + * The object to define the lazy getter on. 1.193 + * @param aName 1.194 + * The name of the getter to define on aObject for the service. 1.195 + * @param aContract 1.196 + * The contract used to obtain the service. 1.197 + * @param aInterfaceName 1.198 + * The name of the interface to query the service to. 1.199 + */ 1.200 + defineLazyServiceGetter: function XPCU_defineLazyServiceGetter(aObject, aName, 1.201 + aContract, 1.202 + aInterfaceName) 1.203 + { 1.204 + this.defineLazyGetter(aObject, aName, function XPCU_serviceLambda() { 1.205 + return Cc[aContract].getService(Ci[aInterfaceName]); 1.206 + }); 1.207 + }, 1.208 + 1.209 + /** 1.210 + * Defines a getter on a specified object for a module. The module will not 1.211 + * be imported until first use. 1.212 + * 1.213 + * @param aObject 1.214 + * The object to define the lazy getter on. 1.215 + * @param aName 1.216 + * The name of the getter to define on aObject for the module. 1.217 + * @param aResource 1.218 + * The URL used to obtain the module. 1.219 + * @param aSymbol 1.220 + * The name of the symbol exported by the module. 1.221 + * This parameter is optional and defaults to aName. 1.222 + */ 1.223 + defineLazyModuleGetter: function XPCU_defineLazyModuleGetter(aObject, aName, 1.224 + aResource, 1.225 + aSymbol) 1.226 + { 1.227 + this.defineLazyGetter(aObject, aName, function XPCU_moduleLambda() { 1.228 + var temp = {}; 1.229 + Cu.import(aResource, temp); 1.230 + return temp[aSymbol || aName]; 1.231 + }); 1.232 + }, 1.233 + 1.234 + /** 1.235 + * Convenience access to category manager 1.236 + */ 1.237 + get categoryManager() { 1.238 + return Components.classes["@mozilla.org/categorymanager;1"] 1.239 + .getService(Ci.nsICategoryManager); 1.240 + }, 1.241 + 1.242 + /** 1.243 + * Helper which iterates over a nsISimpleEnumerator. 1.244 + * @param e The nsISimpleEnumerator to iterate over. 1.245 + * @param i The expected interface for each element. 1.246 + */ 1.247 + IterSimpleEnumerator: function XPCU_IterSimpleEnumerator(e, i) 1.248 + { 1.249 + while (e.hasMoreElements()) 1.250 + yield e.getNext().QueryInterface(i); 1.251 + }, 1.252 + 1.253 + /** 1.254 + * Helper which iterates over a string enumerator. 1.255 + * @param e The string enumerator (nsIUTF8StringEnumerator or 1.256 + * nsIStringEnumerator) over which to iterate. 1.257 + */ 1.258 + IterStringEnumerator: function XPCU_IterStringEnumerator(e) 1.259 + { 1.260 + while (e.hasMore()) 1.261 + yield e.getNext(); 1.262 + }, 1.263 + 1.264 + /** 1.265 + * Returns an nsIFactory for |component|. 1.266 + */ 1.267 + _getFactory: function XPCOMUtils__getFactory(component) { 1.268 + var factory = component.prototype._xpcom_factory; 1.269 + if (!factory) { 1.270 + factory = { 1.271 + createInstance: function(outer, iid) { 1.272 + if (outer) 1.273 + throw Cr.NS_ERROR_NO_AGGREGATION; 1.274 + return (new component()).QueryInterface(iid); 1.275 + } 1.276 + } 1.277 + } 1.278 + return factory; 1.279 + }, 1.280 + 1.281 + /** 1.282 + * Allows you to fake a relative import. Expects the global object from the 1.283 + * module that's calling us, and the relative filename that we wish to import. 1.284 + */ 1.285 + importRelative: function XPCOMUtils__importRelative(that, path, scope) { 1.286 + if (!("__URI__" in that)) 1.287 + throw Error("importRelative may only be used from a JSM, and its first argument "+ 1.288 + "must be that JSM's global object (hint: use this)"); 1.289 + let uri = that.__URI__; 1.290 + let i = uri.lastIndexOf("/"); 1.291 + Components.utils.import(uri.substring(0, i+1) + path, scope || that); 1.292 + }, 1.293 + 1.294 + /** 1.295 + * generates a singleton nsIFactory implementation that can be used as 1.296 + * the _xpcom_factory of the component. 1.297 + * @param aServiceConstructor 1.298 + * Constructor function of the component. 1.299 + */ 1.300 + generateSingletonFactory: 1.301 + function XPCOMUtils_generateSingletonFactory(aServiceConstructor) { 1.302 + return { 1.303 + _instance: null, 1.304 + createInstance: function XPCU_SF_createInstance(aOuter, aIID) { 1.305 + if (aOuter !== null) { 1.306 + throw Cr.NS_ERROR_NO_AGGREGATION; 1.307 + } 1.308 + if (this._instance === null) { 1.309 + this._instance = new aServiceConstructor(); 1.310 + } 1.311 + return this._instance.QueryInterface(aIID); 1.312 + }, 1.313 + lockFactory: function XPCU_SF_lockFactory(aDoLock) { 1.314 + throw Cr.NS_ERROR_NOT_IMPLEMENTED; 1.315 + }, 1.316 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]) 1.317 + }; 1.318 + }, 1.319 +}; 1.320 + 1.321 +/** 1.322 + * Helper for XPCOMUtils.generateQI to avoid leaks - see bug 381651#c1 1.323 + */ 1.324 +function makeQI(interfaceNames) { 1.325 + return function XPCOMUtils_QueryInterface(iid) { 1.326 + if (iid.equals(Ci.nsISupports)) 1.327 + return this; 1.328 + if (iid.equals(Ci.nsIClassInfo) && "classInfo" in this) 1.329 + return this.classInfo; 1.330 + for each(let interfaceName in interfaceNames) { 1.331 + if (Ci[interfaceName].equals(iid)) 1.332 + return this; 1.333 + } 1.334 + 1.335 + throw Cr.NS_ERROR_NO_INTERFACE; 1.336 + }; 1.337 +}