js/xpconnect/loader/XPCOMUtils.jsm

changeset 0
6474c204b198
     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 +}

mercurial