home *** CD-ROM | disk | FTP | other *** search
/ PC World 2007 December / PCWorld_2007-12_cd.bin / audio-video / songbird / Songbird_0.3_windows-i686.exe / xulrunner / modules / XPCOMUtils.jsm < prev   
Text File  |  2007-09-21  |  10KB  |  268 lines

  1. /*
  2.  * ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is Mozilla code.
  16.  *
  17.  * The Initial Developer of the Original Code is
  18.  * Netscape Communications Corporation.
  19.  * Portions created by the Initial Developer are Copyright (C) 2004
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *    Alex Fritze <alex@croczilla.com> (original author)
  24.  *    Nickolay Ponomarev <asqueella@gmail.com>
  25.  *
  26.  * Alternatively, the contents of this file may be used under the terms of
  27.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  28.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29.  * in which case the provisions of the GPL or the LGPL are applicable instead
  30.  * of those above. If you wish to allow use of your version of this file only
  31.  * under the terms of either the GPL or the LGPL, and not to allow others to
  32.  * use your version of this file under the terms of the MPL, indicate your
  33.  * decision by deleting the provisions above and replace them with the notice
  34.  * and other provisions required by the GPL or the LGPL. If you do not delete
  35.  * the provisions above, a recipient may use your version of this file under
  36.  * the terms of any one of the MPL, the GPL or the LGPL.
  37.  *
  38.  * ***** END LICENSE BLOCK ***** */
  39.  
  40. /**
  41.  * Utilities for JavaScript components loaded by the JS component
  42.  * loader.
  43.  *
  44.  * Import into a JS component using
  45.  * 'Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");'
  46.  *
  47.  * Exposing a JS 'class' as a component using these utility methods consists
  48.  * of several steps:
  49.  * 0. Import XPCOMUtils, as described above.
  50.  * 1. Declare the 'class' (or multiple classes) implementing the component(s):
  51.  *  function MyComponent() {
  52.  *    // constructor
  53.  *  }
  54.  *  MyComponent.prototype = {
  55.  *    // properties required for XPCOM registration:
  56.  *    classDescription: "unique text description",
  57.  *    classID:          Components.ID("{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"),
  58.  *    contractID:       "@example.com/xxx;1",
  59.  *
  60.  *    // [optional] custom factory (an object implementing nsIFactory). If not
  61.  *    // provided, the default factory is used, which returns
  62.  *    // |(new MyComponent()).QueryInterface(iid)| in its createInstance().
  63.  *    _xpcom_factory: { ... },
  64.  *
  65.  *    // [optional] an array of categories to register this component in.
  66.  *    _xpcom_categories: [{
  67.  *      // Each object in the array specifies the parameters to pass to
  68.  *      // nsICategoryManager.addCategoryEntry(). 'true' is passed for
  69.  *      // both aPersist and aReplace params.
  70.  *      category: "some-category",
  71.  *      // optional, defaults to the object's classDescription
  72.  *      entry: "entry name",
  73.  *      // optional, defaults to the object's contractID (unless
  74.  *      // 'service' is specified)
  75.  *      value: "...",
  76.  *      // optional, defaults to false. When set to true, and only if 'value'
  77.  *      // is not specified, the concatenation of the string "service," and the
  78.  *      // object's contractID is passed as aValue parameter of addCategoryEntry.
  79.  *      service: true
  80.  *    }],
  81.  *
  82.  *    // QueryInterface implementation, e.g. using the generateQI helper
  83.  *    QueryInterface: XPCOMUtils.generateQI(
  84.  *      [Components.interfaces.nsIObserver,
  85.  *       Components.interfaces.nsIMyInterface]),
  86.  *
  87.  *    // ...component implementation...
  88.  *  };
  89.  *
  90.  * 2. Create an array of component constructors (like the one
  91.  * created in step 1):
  92.  *  var components = [MyComponent];
  93.  *
  94.  * 3. Define the NSGetModule entry point:
  95.  *  function NSGetModule(compMgr, fileSpec) {
  96.  *    // components is the array created in step 2.
  97.  *    return XPCOMUtils.generateModule(components);
  98.  *  }
  99.  */
  100.  
  101.  
  102. EXPORTED_SYMBOLS = [ "XPCOMUtils" ];
  103.  
  104. const Ci = Components.interfaces;
  105. const Cr = Components.results;
  106.  
  107. var XPCOMUtils = {
  108.   /**
  109.    * Generate a QueryInterface implementation. The returned function must be
  110.    * assigned to the 'QueryInterface' property of a JS object. When invoked on
  111.    * that object, it checks if the given iid is listed in the |interfaces|
  112.    * param, and if it is, returns |this| (the object it was called on).
  113.    */
  114.   generateQI: function(interfaces) {
  115.     return makeQI([i.name for each(i in interfaces)]);
  116.   },
  117.  
  118.   /**
  119.    * Generate the NSGetModule function (along with the module definition).
  120.    * See the parameters to generateModule.
  121.    */
  122.   generateNSGetModule: function(componentsArray, postRegister, preUnregister) {
  123.     return function NSGetModule(compMgr, fileSpec) {
  124.       return XPCOMUtils.generateModule(componentsArray,
  125.                                        postRegister,
  126.                                        preUnregister);
  127.     }
  128.   },
  129.  
  130.   /**
  131.    * Generate a module implementation.
  132.    *
  133.    * @param componentsArray  Array of component constructors. See the comment
  134.    *                         at the top of this file for details.
  135.    * @param postRegister  optional post-registration function with
  136.    *                      signature 'postRegister(nsIComponentManager,
  137.    *                                              nsIFile, componentsArray)'
  138.    * @param preUnregister optional pre-unregistration function with
  139.    *                      signature 'preUnregister(nsIComponentManager,
  140.    *                                               nsIFile, componentsArray)'
  141.    */
  142.   generateModule: function(componentsArray, postRegister, preUnregister) {
  143.     let classes = [];
  144.     for each (let component in componentsArray) {
  145.       classes.push({
  146.         cid:          component.prototype.classID,
  147.         className:    component.prototype.classDescription,
  148.         contractID:   component.prototype.contractID,
  149.         factory:      this._getFactory(component),
  150.         categories:   component.prototype._xpcom_categories
  151.       });
  152.     }
  153.  
  154.     return { // nsIModule impl.
  155.       getClassObject: function(compMgr, cid, iid) {
  156.         // We only support nsIFactory queries, not nsIClassInfo
  157.         if (!iid.equals(Ci.nsIFactory))
  158.           throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  159.  
  160.         for each (let classDesc in classes) {
  161.           if (classDesc.cid.equals(cid))
  162.             return classDesc.factory;
  163.         }
  164.  
  165.         throw Cr.NS_ERROR_FACTORY_NOT_REGISTERED;
  166.       },
  167.  
  168.       registerSelf: function(compMgr, fileSpec, location, type) {
  169.         var componentCount = 0;
  170.         debug("*** registering " + fileSpec.leafName + ": [ ");
  171.         compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  172.  
  173.         for each (let classDesc in classes) {
  174.           debug((componentCount++ ? ", " : "") + classDesc.className);
  175.           compMgr.registerFactoryLocation(classDesc.cid,
  176.                                           classDesc.className,
  177.                                           classDesc.contractID,
  178.                                           fileSpec,
  179.                                           location,
  180.                                           type);
  181.           if (classDesc.categories) {
  182.             let catMan = XPCOMUtils.categoryManager;
  183.             for each (let cat in classDesc.categories) {
  184.               let defaultValue = (cat.service ? "service," : "") +
  185.                                  classDesc.contractID;
  186.               catMan.addCategoryEntry(cat.category,
  187.                                       cat.entry || classDesc.className,
  188.                                       cat.value || defaultValue,
  189.                                       true, true);
  190.             }
  191.           }
  192.         }
  193.  
  194.         if (postRegister)
  195.           postRegister(compMgr, fileSpec, componentsArray);
  196.         debug(" ]\n");
  197.       },
  198.  
  199.       unregisterSelf: function(compMgr, fileSpec, location) {
  200.         var componentCount = 0;
  201.         debug("*** unregistering " + fileSpec.leafName + ": [ ");
  202.         compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  203.         if (preUnregister)
  204.           preUnregister(compMgr, fileSpec, componentsArray);
  205.  
  206.         for each (let classDesc in classes) {
  207.           debug((componentCount++ ? ", " : "") + classDesc.className);
  208.           if (classDesc.categories) {
  209.             let catMan = XPCOMUtils.categoryManager;
  210.             for each (let cat in classDesc.categories) {
  211.               catMan.deleteCategoryEntry(cat.category,
  212.                                          cat.entry || classDesc.className,
  213.                                          true);
  214.             }
  215.           }
  216.           compMgr.unregisterFactoryLocation(classDesc.cid, fileSpec);
  217.         }
  218.         debug(" ]\n");
  219.       },
  220.  
  221.       canUnload: function(compMgr) {
  222.         return true;
  223.       }
  224.     };
  225.   },
  226.  
  227.   /**
  228.    * Convenience access to category manager
  229.    */
  230.   get categoryManager() {
  231.     return Components.classes["@mozilla.org/categorymanager;1"]
  232.            .getService(Ci.nsICategoryManager);
  233.   },
  234.  
  235.   /**
  236.    * Returns an nsIFactory for |component|.
  237.    */
  238.   _getFactory: function(component) {
  239.     var factory = component.prototype._xpcom_factory;
  240.     if (!factory) {
  241.       factory = {
  242.         createInstance: function(outer, iid) {
  243.           if (outer)
  244.             throw Cr.NS_ERROR_NO_AGGREGATION;
  245.           return (new component()).QueryInterface(iid);
  246.         }
  247.       }
  248.     }
  249.     return factory;
  250.   }
  251. };
  252.  
  253. /**
  254.  * Helper for XPCOMUtils.generateQI to avoid leaks - see bug 381651#c1
  255.  */
  256. function makeQI(interfaceNames) {
  257.   return function XPCOMUtils_QueryInterface(iid) {
  258.     if (iid.equals(Ci.nsISupports))
  259.       return this;
  260.     for each(let interfaceName in interfaceNames) {
  261.       if (Ci[interfaceName].equals(iid))
  262.         return this;
  263.     }
  264.  
  265.     throw Cr.NS_ERROR_NO_INTERFACE;
  266.   };
  267. }
  268.