File:  [mozdev] / books / www / docbook / ch08.xml
Revision 1.5: download - view: text, annotated - select for diffs - revision graph
Sun Apr 27 10:06:18 2003 UTC (16 years, 6 months ago) by brian
Branches: MAIN
CVS tags: HEAD
chapter 8 corrections - bug 3563

    1: <chapter label="8" id="mozilla-CHP-8">
    2: 
    3: <title>XPCOM</title>
    4: 
    5: 
    6: <para>This chapter provides a
    7: <indexterm id="IXT-8-1584"><primary>XPCOM</primary></indexterm>high-level
    8: introduction to XPCOM component technology. XPCOM can be difficult to
    9: master, but after reading this chapter, you should have a good sense
   10: of what it is and the important part it plays as
   11: Mozilla's core technology. You should be able to
   12: find and use existing scriptable components in your own applications
   13: and create a simple XPCOM component by using JavaScript or C++.
   14: </para>
   15: 
   16: <para>XPCOM permits a reusable code module to be globally accessible to a
   17: Mozilla-based application. You do not need to worry about including
   18: external source files in your application distribution and you can
   19: distribute components by using XPInstall. This type of architecture
   20: makes the development of core application services flexible and
   21: entirely modular.
   22: </para>
   23: 
   24: <para>The section <link linkend="mozilla-CHP-8-SECT-2.1">Section 8.2.1</link> lets you
   25: create an interface from start to finish -- writing the
   26: implementation for that interface, compiling it into a type library,
   27: registering it with Mozilla, and then testing the new component. One
   28: advantage of using XPCOM is that you can create multiple
   29: implementations for a single interface; following the JavaScript
   30: component section, we will take the same <literal>nsISimple</literal>
   31: interface and implement it in C++ as well.
   32: </para>
   33: 
   34: <para>The section <link linkend="mozilla-CHP-8-SECT-2.5">Section 8.2.5</link> includes
   35: some techniques and programming tasks that are particular to C++
   36: components, such as handling return values and generating header
   37: files and useful macros. The section <link linkend="mozilla-CHP-8-SECT-2.7">Section 8.2.7</link> introduces the XPCOM
   38: bindings
   39: <indexterm id="IXT-8-1585"><primary>bindings</primary><secondary>pyXPCOM</secondary></indexterm>
   40: <indexterm id="IXT-8-1586"><primary>pyXPCOM</primary></indexterm>
   41: <indexterm id="IXT-8-1587"><primary>XPCOM</primary><secondary>bindings</secondary></indexterm>for
   42: the Python language (pyXPCOM). First, it provides an overview of
   43: XPCOM and how it relates to other technologies used in Mozilla.
   44: </para>
   45: 
   46: 
   47: <sect1 role="" id="mozilla-CHP-8-SECT-1" label="8.1">
   48: <title>What Is XPCOM?</title>
   49: 
   50: <para>XPCOM is Mozilla's cross-platform component object
   51: model. Although it is similar to Microsoft's COM
   52: technology, this chapter points out some important differences.
   53: </para>
   54: 
   55: <para>Essentially, when you program in a component-based environment, you
   56: do one of three things: you create a new component using existing
   57: components, write a component that implements other components, and
   58: establish interdependencies and a service network.
   59: </para>
   60: 
   61: <sect2 role="" id="mozilla-CHP-8-SECT-1.1" label="8.1.1">
   62: <title>What Is a Component?</title>
   63: 
   64: <para>You've already seen components used in this
   65: <indexterm id="IXT-8-1588"><primary>XPCOM</primary><secondary>components</secondary></indexterm>
   66: <indexterm id="IXT-8-1589"><primary>components</primary><secondary>XPCOM</secondary></indexterm>book.
   67: In some cases, you may have used the services of Mozilla components
   68: without knowing it -- for example, when you created a XUL tree
   69: widget in the section <link linkend="mozilla-CHP-3-SECT-4.2">Section 3.4.2</link>
   70: in <link linkend="mozilla-CHP-3">Chapter 3</link>, and used its built-in layout and
   71: view capabilities. Some of this functionality is defined in an
   72: interface called <emphasis>nsITreeView</emphasis>, which provides
   73: specific methods and properties for a XUL tree, persisting its state,
   74: row, cell, and column properties, navigation, and other object
   75: metadata used in a tree object. Behind the scenes,
   76: you'll find an XPCOM-instantiated tree view object
   77: where methods and properties associated with the XUL element are
   78: accessed via DOM &gt; JavaScript &gt;
   79: XPConnect &gt; XPCOM layers.
   80: </para>
   81: 
   82: <para>A component is a reusable or modular piece of code that implements a
   83: clearly defined interface. In Mozilla, this code can exist as a
   84: singleton service or an object instance. A singleton service is an
   85: object instance that is created only once and then used by other code
   86: (usually called "callers,"
   87: "clients," or
   88: "consumers"). An object instance is
   89: an object that is instantiated once or many times. Components are
   90: written as classes that typically have member variables and methods.
   91: The basic purpose of a component is to implement a clearly defined
   92: set of APIs that exist in a public interface. The interface exists
   93: separately so that the implementation is abstracted away, and it can
   94: be changed without affecting the interface or breaking binary
   95: compatibility. When interfaces are deployed in a production
   96: environment, they are frozen, which means they are held in an
   97: immutable state -- theoretically for as long as the application
   98: exists. While MSCOM provides a component-based programming model on
   99: Microsoft platforms, XPCOM provides it on all platforms where Mozilla
  100: is available.
  101: </para>
  102: 
  103: <para><link linkend="mozilla-CHP-8-EX-1">Example 8-1</link> shows how simple
  104: <indexterm id="IXT-8-1590"><primary>scripts</primary><secondary>objects,
  105: XPCOM</secondary></indexterm>
  106: <indexterm id="IXT-8-1591"><primary>objects</primary><secondary>XPCOM, in
  107: scripts</secondary></indexterm>
  108: <indexterm id="IXT-8-1592"><primary>XPCOM</primary><secondary>methods</secondary><tertiary>JavaScript
  109: implementation</tertiary></indexterm>using XPCOM components can be.
  110: In two lines, an XPConnect<filename>-</filename>wrapped
  111: <literal>nsIBookmarksService</literal> object is instantiated, and
  112: one of its methods is called, providing easy access to this XPCOM
  113: component from JavaScript.
  114: </para>
  115: 
  116: <example id="mozilla-CHP-8-EX-1" label="8-1">
  117: <title>Using an XPCOM object in script </title>
  118: <programlisting>// create a bookmark service object in JS
  119: var bmks = 
  120:   Components.classes["@mozilla.org/browser/bookmarks-service;1"].
  121:       getService(Components.interfaces.nsIBookmarksService);
  122: // call one of the object's methods:
  123: // flush the bookmarks to disk if they've been touched.
  124: bmks.Flush( );</programlisting>
  125: </example>
  126: 
  127: <para>As you can see, the assignment of an XPCOM object to the variable
  128: <emphasis>bmks</emphasis> takes only a single line. Once you are
  129: comfortable using XPCOM from JavaScript, you can use any of
  130: Mozilla's scriptable interfaces in your application.
  131: Once an object like <literal>bmks</literal> is created, as in <link linkend="mozilla-CHP-8-EX-1">Example 8-1</link>, it can be used to call any method in the
  132: <emphasis>nsIBookmarksService</emphasis> interface, of which
  133: <literal>Flush( )</literal> is an example.
  134: </para>
  135: 
  136: </sect2>
  137: <sect2 role="" id="mozilla-CHP-8-SECT-1.2" label="8.1.2">
  138: <title>XPConnect and the Component Object</title>
  139: 
  140: <para>As shown the previous example,
  141: <indexterm id="IXT-8-1593"><primary>XPConnect</primary><secondary>Component object
  142: and</secondary></indexterm> <indexterm id="IXT-8-1594"><primary>Component
  143: object</primary><secondary>XPConnect</secondary></indexterm>
  144: <indexterm id="IXT-8-1595"><primary>objects</primary><secondary>Component,
  145: XPConnect</secondary></indexterm>the XPCOM object is called and
  146: instantiated from script. For an interpreted language like JavaScript
  147: to call and instantiate it, a bridge must bind JavaScript types to
  148: XPCOM types. These type bindings are part of a technology called
  149: XPConnect.
  150: </para>
  151: 
  152: <para>In XPConnect, XPCOM interfaces, <literal>classIDs</literal>, and
  153: <literal>progIDs</literal> are stored as global JavaScript objects
  154: and properties that can be manipulated directly through a top-level
  155: object called <literal>Components</literal>. This object accesses any
  156: <indexterm id="IXT-8-1596"><primary>scriptable objects, Component
  157: object</primary></indexterm> <indexterm id="IXT-8-1597"><primary>Component
  158: object</primary><secondary>scriptable
  159: objects</secondary></indexterm>component that is declared
  160: "scriptable" in an XPCOM IDL
  161: interface. Through the <literal>Components</literal> object, you can
  162: access and use the services that these interfaces provide. The
  163: <literal>Component</literal> object's top-level
  164: properties and methods include:
  165: </para>
  166: 
  167: <variablelist>
  168: <varlistentry><term>QueryInterface</term>
  169: <listitem>
  170: <para>A method used to match an <indexterm id="IXT-8-1598"><primary>QueryInterface
  171: method</primary><secondary>Component
  172: object</secondary></indexterm><indexterm id="IXT-8-1599"><primary>Component
  173: object</primary><secondary>QueryInterface
  174: method</secondary></indexterm><indexterm id="IXT-8-1600"><primary>methods</primary><secondary>QueryInterface</secondary><tertiary>Component
  175: object</tertiary></indexterm>interface with a desired implementation.
  176: The implementation can be in C, C++, JavaScript, Python, and other
  177: languages for which appropriate bindings are created. You can have
  178: multiple implementations for an interface. Through
  179: <literal>QueryInterface</literal>, you can ask for and assign the
  180: desired interface to its implementation. Each XPCOM object needs to
  181: implement <literal>QueryInterface</literal> in order to return an
  182: instance of that object's class:
  183: </para>
  184: </listitem>
  185: </varlistentry>
  186: </variablelist>
  187: 
  188: <programlisting>js&gt; var clz  = Components.classes['@mozilla.org/file/local;1'];
  189: js&gt; var inst = clz.getService( );
  190: js&gt; inst.QueryInterface(C.interfaces.nsILocalFile);
  191: [xpconnect wrapped nsILocalFile @ 0x81b7040]</programlisting>
  192: 
  193: <variablelist>
  194: <varlistentry><term>interfaces</term>
  195: <listitem>
  196: <para>A read-only object array
  197: <indexterm id="IXT-8-1601"><primary>interfaces</primary><secondary>Component
  198: object</secondary></indexterm><indexterm id="IXT-8-1602"><primary>Component
  199: object</primary><secondary>interfaces</secondary></indexterm>containing
  200: all the interfaces declared scriptable in the IDL file. The object
  201: name has the same name as the interface it represents.
  202: </para>
  203: </listitem>
  204: </varlistentry>
  205: </variablelist>
  206: 
  207: <programlisting>Components.interfaces.nsILocalFile</programlisting>
  208: 
  209: <para>The source file for this particular interface, for example, is
  210: <emphasis>nsILocalFile.idl</emphasis>. This XPIDL compiler compiles
  211: this file to produce a cross-platform binary type library,
  212: <emphasis>nsILocalFile.xpt</emphasis>, which contains tokenized IDL
  213: in an efficiently parsed form.
  214: </para>
  215: 
  216: <variablelist>
  217: <varlistentry><term>classes</term>
  218: <listitem>
  219: <para>A read-only array of all
  220: <indexterm id="IXT-8-1603"><primary>classes</primary><secondary>Component
  221: object</secondary></indexterm> <indexterm id="IXT-8-1604"><primary>Component
  222: object</primary><secondary>classes</secondary></indexterm>the XPCOM
  223: component classes indexed by the <literal>ProgID</literal> (or
  224: human-readable name) of the component class. The
  225: <literal>classes</literal> object has these properties associated
  226: with it:
  227: </para>
  228: </listitem>
  229: </varlistentry>
  230: </variablelist>
  231: 
  232: <programlisting>toString       Returns the string progID.           
  233: QueryInterface Used to QI this interface.           
  234: name           Returns the string progid name.      
  235: number         Returns the string components uuid   
  236:                number.                              
  237: valid          Boolean verifies if the instance is  
  238:                valid.                               
  239: equals         The Boolean used to match identical  
  240:                instances.                           
  241: initialize     I don't know what this does.         
  242: createInstance Will create an instance of the       
  243:                component; you can have many         
  244:                instances.                           
  245: getService     Will instantiate the component as a  
  246:                service; you can have only one       
  247:                instance of a service.               </programlisting>
  248: 
  249: <variablelist>
  250: <varlistentry><term>classesByID</term>
  251: <listitem>
  252: <para>The same as classes, except <indexterm id="IXT-8-1605"><primary>classesByID object,
  253: Component object</primary></indexterm><indexterm id="IXT-8-1606"><primary>Component
  254: object</primary><secondary>classesByID
  255: object</secondary></indexterm>this time the array is indexed by the
  256: "canonical" or
  257: "well-established" form of their
  258: CLSID:
  259: </para>
  260: </listitem>
  261: </varlistentry>
  262: </variablelist>
  263: 
  264: <programlisting>Components.classesByID['{dea98e50-1dd1-11b2-9344-8902b4805a2e}'];</programlisting>
  265: 
  266: <para>The <literal>classesByID</literal> object has the same properties
  267: object associated with it as the <literal>class</literal> object. The
  268: properties are also used in the same way:
  269: </para>
  270: 
  271: <programlisting>toString
  272: QueryInterface
  273: name
  274: number
  275: valid
  276: equals
  277: initialize
  278: createInstance
  279: getService</programlisting>
  280: 
  281: <variablelist>
  282: <varlistentry><term>stack</term>
  283: <listitem>
  284: <para>A read-only property that represents <indexterm id="IXT-8-1607"><primary>stack
  285: property, Component
  286: object</primary></indexterm><indexterm id="IXT-8-1608"><primary>Component
  287: object</primary><secondary>stack
  288: property</secondary></indexterm><indexterm id="IXT-8-1609"><primary>properties</primary><secondary>stack,
  289: Component object</secondary></indexterm>a snapshot of the current
  290: JavaScript call stack. JavaScript handles each code interpretation
  291: one call at a time and then places that code onto a call stack. This
  292: property can be used for recondite diagnostic purposes:
  293: </para>
  294: </listitem>
  295: </varlistentry>
  296: </variablelist>
  297: 
  298: <programlisting>js&gt; var C=Components;
  299: js&gt; C.stack;
  300: JS frame :: typein :: &lt;TOP_LEVEL&gt; :: line 2
  301: js&gt;  C.stack;
  302: JS frame :: typein :: &lt;TOP_LEVEL&gt; :: line 3</programlisting>
  303: 
  304: <variablelist>
  305: <varlistentry><term>results</term>
  306: <listitem>
  307: <para>An object array of
  308: <literal>nserror</literal><indexterm id="IXT-8-1610"><primary>results object array,
  309: Component object</primary></indexterm><indexterm id="IXT-8-1611"><primary>Component
  310: object</primary><secondary>results object
  311: array</secondary></indexterm> results:
  312: </para>
  313: </listitem>
  314: </varlistentry>
  315: </variablelist>
  316: 
  317: <programlisting> Components.results.NS_ERROR_FILE_ACCESS_DENIED;
  318:   2152857621</programlisting>
  319: 
  320: <variablelist>
  321: <varlistentry><term>manager</term>
  322: <listitem>
  323: <para>A reflection of the XPCOM global native component manager service.
  324: Using the component manager <indexterm id="IXT-8-1612"><primary>Component
  325: object</primary><secondary>manager</secondary></indexterm>
  326: <indexterm id="IXT-8-1613"><primary>managers, Component object
  327: and</primary></indexterm>is the only way for a component to actually
  328: be created. It uses the components factory to create an instance of
  329: the <literal>class</literal> object.
  330: </para>
  331: </listitem>
  332: </varlistentry>
  333: 
  334: <varlistentry><term>ID</term>
  335: <listitem>
  336: <para>A constructor used <indexterm id="IXT-8-1614"><primary>ID constructor, Component
  337: object</primary></indexterm><indexterm id="IXT-8-1615"><primary>Component
  338: object</primary><secondary>ID
  339: constructor</secondary></indexterm><indexterm id="IXT-8-1616"><primary>constructors</primary><secondary>ID
  340: constructor, Component object</secondary></indexterm>for a component
  341: written in JavaScript This component needs to register itself with
  342: the component manager by using its own <literal>nsID</literal> (an ID
  343: that is not already registered and thus does not appear in
  344: <filename>Components.classes</filename>).
  345: </para>
  346: </listitem>
  347: </varlistentry>
  348: 
  349: <varlistentry><term>Exception</term>
  350: <listitem>
  351: <para>A JavaScript constructor <indexterm id="IXT-8-1617"><primary>exceptions, Component
  352: object</primary></indexterm><indexterm id="IXT-8-1618"><primary>Component
  353: object</primary><secondary>exceptions</secondary></indexterm>used to
  354: create exception objects. When implementing XPCOM interfaces in
  355: JavaScript, these exception objects are the preferred types of
  356: exceptions. When an XPCOM exception is thrown in your JS code, it
  357: takes the form of an <literal>Exception</literal> object that has
  358: properties associated with this object. Exceptions are usually caught
  359: in a "catch" block.
  360: </para>
  361: </listitem>
  362: </varlistentry>
  363: 
  364: <varlistentry><term>Constructor</term>
  365: <listitem>
  366: <para>A JavaScript constructor <indexterm id="IXT-8-1619"><primary>constructor objects,
  367: Component object</primary></indexterm><indexterm id="IXT-8-1620"><primary>Component
  368: object</primary><secondary>constructor
  369: object</secondary></indexterm>object that constructs new instances of
  370: XPCOM components:
  371: </para>
  372: </listitem>
  373: </varlistentry>
  374: </variablelist>
  375: 
  376: <programlisting>js&gt; var File=new Components.Constructor(
  377:   "@mozilla.org/file/local;1", "nsILocalFile", "initWithPath");</programlisting>
  378: 
  379: <para>The interface <emphasis>nsILocalFile</emphasis> and the method
  380: <literal>initWithPath</literal> are optional. This example creates
  381: and initializes the <emphasis>nsILocalFile</emphasis> component.
  382: </para>
  383: 
  384: <variablelist>
  385: <varlistentry><term>isSucessCode</term>
  386: <listitem>
  387: <para>A function that determines if the results code argument is
  388: successful. It takes an argument of <literal>nsresult</literal> and
  389: returns the Boolean values true or false:
  390: </para>
  391: </listitem>
  392: </varlistentry>
  393: </variablelist>
  394: 
  395: <programlisting>js&gt; Components.isSuccessCode(Components.results.NS_OK);
  396:   true
  397: js&gt; Components.isSuccessCode(Components.results.NS_ERROR_FAILURE);
  398:   false</programlisting>
  399: 
  400: <para>The methods and properties of the <literal>Components</literal>
  401: object listed above provide the only means to instantiate and access
  402: XPCOM objects from JavaScript. They are found often in the Mozilla
  403: codebase. In the sections that follow, they will be used frequently.
  404: </para>
  405: 
  406: </sect2>
  407: <sect2 role="" id="mozilla-CHP-8-SECT-1.3" label="8.1.3">
  408: <title>XPCOM Interfaces and the IDL</title>
  409: 
  410: <para>All XPCOM interfaces are defined
  411: <indexterm id="IXT-8-1621"><primary>XPCOM</primary><secondary>interfaces</secondary><tertiary>IDL
  412: and</tertiary></indexterm><indexterm id="IXT-8-1622"><primary>IDL (Interface
  413: Definition Language),</primary><secondary>XPCOM interfaces
  414: and</secondary></indexterm><indexterm id="IXT-8-1623"><primary>interfaces</primary><secondary>XPCOM</secondary><tertiary>IDL
  415: and</tertiary></indexterm>with the Interface Definition Language
  416: (IDL). IDL provides a language-neutral way to describe the public
  417: methods and properties of a component. Mozilla actually uses a
  418: modified, cross-platform version of IDL called XPIDL to compile
  419: interface source files.
  420: </para>
  421: 
  422: <para>The separation of interface and implementation is a key distinction
  423: of COM programming. If the application programming interface (API) is
  424: abstracted from the implementation language and then frozen,
  425: consumers of that API will receive a guaranteed, established contract
  426: with the interface that ensures it will not be changed. This is
  427: perhaps the main reason why COM was invented: to maintain
  428: compatibility on a binary level so the client code can find and use
  429: the library it needs without worrying about linking to it. To make
  430: this sort of modularity possible at runtime, IDL interfaces are
  431: compiled into binary files called type libraries, which are described
  432: later in the section <link linkend="mozilla-CHP-8-SECT-1.4">Section 8.1.4</link>.
  433: </para>
  434: 
  435: <sect3 role="" id="mozilla-CHP-8-SECT-1.3.1" label="8.1.3.1">
  436: <title>Interfaces versus components</title>
  437: 
  438: <para>It is important to understand that
  439: <indexterm id="IXT-8-1624"><primary>interfaces</primary><secondary>XPCOM</secondary><tertiary>components
  440: and</tertiary></indexterm><indexterm id="IXT-8-1625"><primary>XPCOM</primary><secondary>interfaces</secondary><tertiary>components
  441: and</tertiary></indexterm><indexterm id="IXT-8-1626"><primary>components</primary><secondary>XPCOM</secondary><tertiary>interfaces
  442: and</tertiary></indexterm>most XPCOM components implement at least
  443: two interfaces. Like COM, each component needs the
  444: <literal>QueryInterface</literal>, <literal>AddRef</literal>, and
  445: Release functions to be available as an XPCOM object. These methods
  446: are derived from a basic interface called
  447: <emphasis>nsISupports</emphasis>, which is
  448: <indexterm id="IXT-8-1627"><primary>IUnknown interface, XPCOM</primary></indexterm>
  449: <indexterm id="IXT-8-1628"><primary>XPCOM</primary><secondary>IUnknown
  450: interface</secondary></indexterm>the XPCOM equivalent to Microsoft
  451: COM's <literal>IUnknown</literal>, shown in <link linkend="mozilla-CHP-8-TABLE-1">Table 8-1</link>. 
  452: </para>
  453: 
  454: <table id="mozilla-CHP-8-TABLE-1" label="8-1">
  455: 
  456: <title>The IUnknown interface </title>
  457: <tgroup cols="4">
  458: <colspec colnum="1" colname="col1"/>
  459: <colspec colnum="2" colname="col2"/>
  460: <colspec colnum="3" colname="col3"/>
  461: <colspec colnum="4" colname="col4"/>
  462: <thead>
  463: <row>
  464: <entry>
  465: <para>Name</para>
  466: </entry>
  467: <entry>
  468: <para>Type</para>
  469: </entry>
  470: <entry>
  471: <para>Description</para>
  472: </entry>
  473: <entry>
  474: <para>Parameters / return value</para>
  475: </entry>
  476: </row>
  477: </thead>
  478: <tbody>
  479: <row>
  480: <entry>
  481: <para>AddRef</para>
  482: </entry>
  483: <entry>
  484: <programlisting>ULONG AddRef(void)</programlisting>
  485: </entry>
  486: <entry>
  487: <para>Increments the reference count on the COM object.</para>
  488: </entry>
  489: <entry>
  490: <para>Returns:</para>
  491: 
  492: <para>int, which is the new incremented reference count on the object. This
  493: value may be useful for diagnostics or testing.
  494: </para>
  495: </entry>
  496: </row>
  497: <row>
  498: <entry>
  499: <para>QueryInterface</para>
  500: </entry>
  501: <entry>
  502: <programlisting>HRESULT QueryInterface(/* [in] */ REFIID riid, /* [iid_is][out] */ void **ppvObject)</programlisting>
  503: </entry>
  504: <entry>
  505: <para>Retrieves a pointer to the requested interface.</para>
  506: </entry>
  507: <entry>
  508: <para>Parameters:</para>
  509: 
  510: <para>iid, which is an [in] identifier of the requested interface.</para>
  511: 
  512: <para>ppvObject, which is an [out] pointer to the interface pointer
  513: identified by iid. If the object does not support this interface,
  514: ppvObject is set to NULL.
  515: </para>
  516: 
  517: <para>Returns:</para>
  518: 
  519: <para>HRESULT, which is the standard HRESULT value.</para>
  520: </entry>
  521: </row>
  522: <row>
  523: <entry>
  524: <para>Release</para>
  525: </entry>
  526: <entry>
  527: <programlisting>ULONG Release(void)</programlisting>
  528: </entry>
  529: <entry>
  530: <para>Decrements the reference count on the COM object.</para>
  531: </entry>
  532: <entry>
  533: <para>Returns:</para>
  534: 
  535: <para>int, which is the new decremented reference count on the object. This
  536: value may be useful for diagnostics or testing.
  537: </para>
  538: </entry>
  539: </row>
  540: </tbody>
  541: </tgroup>
  542: </table>
  543: 
  544: <para>Tables 8-1 and 8-2 illustrate the minor differences between
  545: Microsoft's <emphasis>nsIUnknown</emphasis> and
  546: Mozilla's <emphasis>nsISupports</emphasis> root
  547: interfaces. The usage is covered in detail throughout this chapter.
  548: </para>
  549: 
  550: <table id="mozilla-CHP-8-TABLE-2" label="8-2">
  551: 
  552: <title>The nsISupports interface </title>
  553: <tgroup cols="4">
  554: <colspec colnum="1" colname="col1"/>
  555: <colspec colnum="2" colname="col2"/>
  556: <colspec colnum="3" colname="col3"/>
  557: <colspec colnum="4" colname="col4"/>
  558: <thead>
  559: <row>
  560: <entry>
  561: <para>Name</para>
  562: </entry>
  563: <entry>
  564: <para>Type</para>
  565: </entry>
  566: <entry>
  567: <para>Description</para>
  568: </entry>
  569: <entry>
  570: <para>Parameters / return value</para>
  571: </entry>
  572: </row>
  573: </thead>
  574: <tbody>
  575: <row>
  576: <entry>
  577: <para>AddRef</para>
  578: </entry>
  579: <entry>
  580: <para><emphasis>NS_IMETHOD_(nsrefcnt)</emphasis></para>
  581: 
  582: <para>AddRef(void)</para>
  583: </entry>
  584: <entry>
  585: <para>Increases the reference count for this interface.</para>
  586: 
  587: <para>The associated instance will not be deleted unless the reference
  588: count is returned to zero.
  589: </para>
  590: </entry>
  591: <entry>
  592: <para>Returns:</para>
  593: 
  594: <para>The resulting reference count.</para>
  595: </entry>
  596: </row>
  597: <row>
  598: <entry>
  599: <para>QueryInterface</para>
  600: </entry>
  601: <entry>
  602: <para><emphasis>NS_IMETHOD QueryInterface(REFNSIID aIID,</emphasis> void**
  603: aInstancePtr)
  604: </para>
  605: </entry>
  606: <entry>
  607: <para>A runtime mechanism for interface discovery.</para>
  608: </entry>
  609: <entry>
  610: <para>Parameters:</para>
  611: 
  612: <para>param aIID [in], which is a requested interface IID.</para>
  613: 
  614: <para>param aInstancePtr [out], which is a pointer to an interface pointer
  615: that receives the result.
  616: </para>
  617: 
  618: <para>Returns:</para>
  619: 
  620: <para>NS_OK if the interface is supported by the associated instance;
  621: NS_NOINTERFACE if it is not; and NS_ERROR_INVALID_POINTER if
  622: aInstancePtr is NULL.
  623: </para>
  624: </entry>
  625: </row>
  626: <row>
  627: <entry>
  628: <para>Release</para>
  629: </entry>
  630: <entry>
  631: <para><emphasis>NS_IMETHOD_(nsrefcnt) Release</emphasis>(void) = 0;</para>
  632: </entry>
  633: <entry>
  634: <para>Decreases the reference count for this interface. Generally, if the
  635: reference count returns to zero, the associated instance is deleted.
  636: </para>
  637: </entry>
  638: <entry>
  639: <para>Returns:</para>
  640: 
  641: <para>The resulting reference count.</para>
  642: </entry>
  643: </row>
  644: </tbody>
  645: </tgroup>
  646: </table>
  647: 
  648: </sect3>
  649: 
  650: <sect3 role="" id="mozilla-CHP-8-SECT-1.3.2" label="8.1.3.2">
  651: <title>Root interfaces</title>
  652: 
  653: <para><literal>QueryInterface</literal>, <literal>Addref</literal>, and
  654: <literal>Release</literal> are <indexterm id="IXT-8-1629"><primary>nsISupports
  655: interface, XPCOM</primary></indexterm>
  656: <indexterm id="IXT-8-1630"><primary>XPCOM</primary><secondary>nsISupports
  657: interface</secondary></indexterm>required <indexterm id="IXT-8-1631"><primary>root
  658: interfaces, XPCOM</primary></indexterm>
  659: <indexterm id="IXT-8-1632"><primary>XPCOM</primary><secondary>interfaces</secondary><tertiary>root
  660: interfaces</tertiary></indexterm>
  661: <indexterm id="IXT-8-1633"><primary>interfaces</primary><secondary>XPCOM</secondary><tertiary>root
  662: interfaces</tertiary></indexterm>methods that are implemented by
  663: every component. <literal>QueryInterface</literal> matches a specific
  664: interface with its implementation class module.
  665: <literal>Addref</literal> and <literal>Release</literal> are methods
  666: used for reference counting. When an instance of a component is
  667: created, one or more pointers may reference that object. For each
  668: reference, a count is incremented by one. When a reference is no
  669: longer used, <literal>Release</literal> is called to decrement the
  670: count. You must hold a reference count to ensure that no pointers
  671: reference an object after it is deleted. When pointers try to access
  672: objects that are deleted, the application core dumps. Reference
  673: counting can get tricky, which is why smart pointers manage
  674: <literal>Addref</literal> and <literal>Release</literal> for you, as
  675: described in the later section <link linkend="mozilla-CHP-8-SECT-2.4">Section 8.2.4</link>. 
  676: </para>
  677: 
  678: <para>Defining <literal>QueryInterface</literal>,
  679: <literal>Addref</literal>, and <literal>Release</literal> every time
  680: an interface is created is clearly not very efficient. Instead, these
  681: methods are defined in the base interface called
  682: <emphasis>nsISupports</emphasis>. All interfaces inherit from this
  683: mother of all interfaces and don't need to redefine
  684: these three basic functions.
  685: </para>
  686: 
  687: <para>XPIDL supports the C style syntax preparser directive
  688: <filename>#include</filename> to include other IDL files -- not
  689: unlike MSCOM, which uses the import statement. At the top of any IDL
  690: file that you create, you need to include
  691: <emphasis>nsISupports</emphasis>:
  692: </para>
  693: 
  694: <programlisting>#include "nsISupports.idl"
  695: interface nsISimple : nsISupports {
  696:   readonly attribute string value; 
  697: };</programlisting>
  698: 
  699: <sidebar id="mozilla-CHP-8-SIDEBAR-1">
  700: <title>Core IDL Types</title>
  701: 
  702: <para>The core types used in IDL interface files <indexterm id="IXT-8-1634"><primary>IDL
  703: (Interface Definition Language),</primary><secondary>interface
  704: files</secondary></indexterm> <indexterm id="IXT-8-1635"><primary>core types, IDL
  705: interface files</primary></indexterm>are listed in the file
  706: <filename>xpcom/base/nsrootidl.idl</filename>. This file is included
  707: in <emphasis>nsISupports</emphasis><filename>.</filename> (This means
  708: it is included in every interface file, since all interfaces inherit
  709: from <emphasis>nsISupports</emphasis>.) All interfaces used in a
  710: system are valid IDL types. For example,
  711: <emphasis>nsISimple</emphasis> is a valid type to use as a method
  712: parameter or a method return type. Some of the main types listed in
  713: this interface are:
  714: </para>
  715: 
  716: <programlisting>typedef boolean             PRBool;</programlisting>
  717: 
  718: <programlisting>typedef octet               PRUint8;</programlisting>
  719: 
  720: <programlisting>typedef unsigned short      PRUint16;</programlisting>
  721: 
  722: <programlisting>typedef unsigned short      PRUnichar;</programlisting>
  723: 
  724: <programlisting>typedef unsigned long       PRUint32;</programlisting>
  725: 
  726: <programlisting>typedef unsigned long long  PRUint64;</programlisting>
  727: 
  728: <programlisting>typedef unsigned long long  PRTime;</programlisting>
  729: 
  730: <programlisting>typedef short               PRInt16;</programlisting>
  731: 
  732: <programlisting>typedef long                PRInt32;</programlisting>
  733: 
  734: <programlisting>typedef long long           PRInt64;</programlisting>
  735: 
  736: <programlisting>typedef unsigned long       nsrefcnt;</programlisting>
  737: 
  738: <programlisting>typedef unsigned long       nsresult;</programlisting>
  739: 
  740: <programlisting>typedef unsigned long       size_t;</programlisting>
  741: </sidebar>
  742: 
  743: </sect3>
  744: 
  745: <sect3 role="" id="mozilla-CHP-8-SECT-1.3.3" label="8.1.3.3">
  746: <title>The XPIDL compiler</title>
  747: 
  748: <para>An IDL compiler is a tool that
  749: <indexterm id="IXT-8-1636"><primary>XPIDL</primary><secondary>compiler</secondary></indexterm><indexterm id="IXT-8-1637"><primary>type
  750: libraries</primary><secondary>IDL
  751: compilers</secondary></indexterm>creates a binary distribution file
  752: called a <emphasis>type library</emphasis> from an interface
  753: description source file. Since support for many different platforms
  754: is a requirement for Mozilla, a modified version of the libIDL
  755: compiler from the Gnome project is used. This variant is called the
  756: XPIDL compiler and is primarily used to compile
  757: Mozilla's own dialect of IDL, conveniently called
  758: XPIDL. The XPIDL compiler generates XPCOM interface information,
  759: headers for XPCOM objects, and XPT type libraries from which objects
  760: may be accessed dynamically through XPConnect. It can also generate
  761: HTML files for documentation and Java class stubs. Another feature of
  762: the XPIDL compiler is the option to generate C++ code stubs. This
  763: feature creates nearly all the declaratory C++ code you need when you
  764: start a new project, which makes XPIDL useful as a coding wizard that
  765: helps you get started. Code generation is covered later in this
  766: chapter in the section <link linkend="mozilla-CHP-8-SECT-2.5">Section 8.2.5</link>. 
  767: </para>
  768: 
  769: <para>The XPIDL compiler is located in
  770: <filename>xpcom/typelib/xpidl/</filename> in the Mozilla sources. If
  771: you built Mozilla, you can add this directory to your
  772: <literal>PATH</literal>:
  773: </para>
  774: 
  775: <programlisting>$ PATH=$PATH:/usr/src/mozilla/xpcom/typelib/xpidl</programlisting>
  776: 
  777: <para>Using the compiler is fairly easy. If you use the help command, you
  778: can see the usage syntax and other basic information about the
  779: compiler:
  780: </para>
  781: 
  782: <programlisting>$ ./xpidl --help
  783: Usage: xpidl [-m mode] [-w] [-v] [-I path] [-o basename] filename.idl
  784:        -a emit annotations to typelib
  785:        -w turn on warnings (recommended)
  786:        -v verbose mode (NYI)
  787:        -I add entry to start of include path for ``#include "nsIThing.idl"''
  788:        -o use basename (e.g. ``/tmp/nsIThing'') for output
  789:        -m specify output mode:
  790:           header        Generate C++ header            (.h)
  791:           typelib       Generate XPConnect typelib     (.xpt)
  792:           doc           Generate HTML documentation    (.html)
  793:           java          Generate Java interface        (.java)</programlisting>
  794: 
  795: </sect3>
  796: </sect2>
  797: <sect2 role="" id="mozilla-CHP-8-SECT-1.4" label="8.1.4">
  798: <title>XPCOM Type Libraries</title>
  799: 
  800: <para>The key to the component architecture of XPCOM is the presence of
  801: binary-independent interface files that are used uniformly across
  802: platforms, languages, and programming environments. These interface
  803: files are compiled into <filename>.xpt</filename> files by the XPIDL
  804: compiler. The Mozilla <filename>components</filename> subdirectory is
  805: where type libraries and modules are typically stored. If you create
  806: a cross-platform type library for your component, you must place it
  807: in this directory for it to be accessible to XPCOM.
  808: </para>
  809: 
  810: <sect3 role="" id="mozilla-CHP-8-SECT-1.4.1" label="8.1.4.1">
  811: <title>Creating a type library file from an IDL interface</title>
  812: 
  813: <para>To create a (<filename>.xpt</filename>) typelib file, use
  814: <indexterm id="IXT-8-1638"><primary>XPCOM</primary><secondary>type libraries,
  815: creating from IDL
  816: interface</secondary></indexterm><indexterm id="IXT-8-1639"><primary>type
  817: libraries</primary><secondary>XPCOM, creating from IDL
  818: interface</secondary></indexterm><indexterm id="IXT-8-1640"><primary>IDL (Interface
  819: Definition Language),</primary><secondary>type library
  820: creation</secondary></indexterm><indexterm id="IXT-8-1641"><primary>interfaces</primary><secondary>IDL
  821: type library creation</secondary></indexterm>the flag <literal>-m
  822: typelib</literal> with warning (<literal>-w</literal>) and verbose
  823: (<literal>-v</literal>) modes turned on. <literal>-o</literal> is
  824: used for the name of the output file and <literal>-I</literal> is
  825: used to specify paths to other IDL files you want to include. To
  826: successfully compile your interface, you must always point to the
  827: directory where <emphasis>nsISupports</emphasis> is located.
  828: </para>
  829: 
  830: <programlisting># include path to nsISupports.idl
  831: $ $XPIDL_INC = /usr/src/mozilla/xpcom/base
  832: #compile nsISimple.idl 
  833: $ xpidl -m typelib -w -v -I $XPIDL_INC \
  834: &gt; -o nsISimple nsISimple.idl</programlisting>
  835: 
  836: <para>The file created after compilation is
  837: <filename>nsISimple.xpt</filename>. It provides the necessary type
  838: information about your interface at runtime. Typelib files enumerate
  839: the methods of interfaces and provide detailed type information for
  840: each method parameter.
  841: </para>
  842: 
  843: </sect3>
  844: </sect2>
  845: <sect2 role="" id="mozilla-CHP-8-SECT-1.5" label="8.1.5">
  846: <title>XPCOM Identifiers</title>
  847: 
  848: <para>To simplify the process of
  849: <indexterm id="IXT-8-1642"><primary>XPCOM</primary><secondary>identifiers</secondary></indexterm><indexterm id="IXT-8-1643"><primary>identifiers</primary><secondary>XPCOM</secondary></indexterm>dynamically
  850: finding, loading, and binding interfaces, all classes and interfaces
  851: are assigned IDs. An ID is a unique 128-bit number that is based on
  852: universally unique <indexterm id="IXT-8-1644"><primary>UUIDs (universally unique
  853: identifiers)</primary></indexterm>identifiers (UUIDs) generated by
  854: various <indexterm id="IXT-8-1645"><primary>uuidgen tool</primary></indexterm>tools
  855: such as <filename>uuidgen</filename> (which we will cover later in
  856: this chapter). They are stored in the structure format defined below:
  857: </para>
  858: 
  859: <programlisting>struct nsID {
  860:   PRUint32 m0;
  861:   PRUint16 m1, m2;
  862:   PRUint8 m3[8];
  863: };</programlisting>
  864: 
  865: <para>To initialize an ID struct, declare it like this:</para>
  866: 
  867: <programlisting>ID = {0x221ffe10, 0xae3c, 0x11d1,
  868:        {0xb6, 0x6c, 0x00, 0x80, 0x5f, 0x8a, 0x26, 0x76}};</programlisting>
  869: 
  870: <para>One thing that gives XPCOM its modularity is the dynamic allocation
  871: of objects through the use of unique identifiers at runtime. This
  872: system of canonical identifiers is used for interface querying and
  873: component instantiation. Having an interface is important because it
  874: ensures that an immutable binary holds a semantic contract defined
  875: for a specific interface class.
  876: </para>
  877: 
  878: <para>The two types of identifiers used in XPCOM are the contract ID and
  879: the class identifier. These identifiers are shuttled to the Component
  880: Manager's <literal>createInstance( )</literal> or
  881: the Service Manager's <literal>getService(
  882: )</literal> methods in order to instantiate a component.
  883: </para>
  884: 
  885: <sect3 role="" id="mozilla-CHP-8-SECT-1.5.1" label="8.1.5.1">
  886: <title>The Contract ID</title>
  887: 
  888: <para>The program ID (<literal>progID</literal>), also known
  889: <indexterm id="IXT-8-1646"><primary>Contract
  890: ID</primary></indexterm><indexterm id="IXT-8-1647"><primary>identifiers</primary><secondary>XPCOM</secondary><tertiary>Contract
  891: ID</tertiary></indexterm><indexterm id="IXT-8-1648"><primary>identifiers</primary><secondary>XPCOM</secondary><tertiary>progIDz</tertiary></indexterm><indexterm id="IXT-8-1649"><primary>XPCOM</primary><secondary>identifiers</secondary><tertiary>Contract
  892: ID</tertiary></indexterm><indexterm id="IXT-8-1650"><primary>XPCOM</primary><secondary>identifiers</secondary><tertiary>progID</tertiary></indexterm><indexterm id="IXT-8-1651"><primary>progID
  893: (program ID)</primary></indexterm>as the Contract ID, is a unique
  894: human-readable string. <link linkend="mozilla-CHP-8-EX-2">Example 8-2</link> shows various
  895: <literal>progID</literal>s for different components. This example can
  896: be used to instantiate an XPCOM component through the use of a
  897: Contract ID.
  898: </para>
  899: 
  900: <example id="mozilla-CHP-8-EX-2" label="8-2">
  901: <title>progIDs </title>
  902: <programlisting>// progID: @mozilla.org/file/local;1
  903: var f = Components.classes[`@mozilla.org/file/local;1'];
  904: // progID: @mozilla.org/browser/bookmarks-service;1
  905: var bmks = 
  906:   Components.classes["@mozilla.org/browser/bookmarks-service;1"].
  907:       getService(Components.interfaces.nsIBookmarksService);</programlisting>
  908: </example>
  909: 
  910: </sect3>
  911: 
  912: <sect3 role="" id="mozilla-CHP-8-SECT-1.5.2" label="8.1.5.2">
  913: <title>The class identifier</title>
  914: 
  915: <para>The other type of identifier is the
  916: <literal>classID</literal><indexterm id="IXT-8-1652"><primary>classID</primary></indexterm><indexterm id="IXT-8-1653"><primary>CLSID
  917: (classID)</primary></indexterm><indexterm id="IXT-8-1654"><primary>identifiers</primary><secondary>XPCOM</secondary><tertiary>CLSID</tertiary></indexterm><indexterm id="IXT-8-1655"><primary>XPCOM</primary><secondary>identifiers</secondary><tertiary>CLSID</tertiary></indexterm>,
  918: or CLSID. The interface and implementation are identified by this
  919: 128-bit numerical identifier string:
  920: </para>
  921: 
  922: <programlisting>// clsid: {2e23e220-60be-11d3-8c4a-000064657374}
  923: var f = Components.classesByID["{2e23e220-60be-11d3-8c4a-000064657374}"];</programlisting>
  924: 
  925: <para>Using XPConnect, XPCOM interfaces,
  926: <indexterm id="IXT-8-1656"><primary>XPConnect</primary><secondary>identifiers
  927: and</secondary></indexterm>
  928: <indexterm id="IXT-8-1657"><primary>identifiers</primary><secondary>XPCOM</secondary><tertiary>XPConnect
  929: and</tertiary></indexterm><literal>classID</literal>s, and
  930: <literal>progID</literal>s are stored as global JavaScript objects
  931: and properties and can be manipulated directly through the top-level
  932: <literal>Components</literal> object discussed earlier.
  933: </para>
  934: 
  935: </sect3>
  936: 
  937: <sect3 role="" id="mozilla-CHP-8-SECT-1.5.3" label="8.1.5.3">
  938: <title>Generating identifiers</title>
  939: 
  940: <para>To obtain a UUID on Unix, you can
  941: <indexterm id="IXT-8-1658"><primary>identifiers</primary><secondary>XPCOM</secondary><tertiary>generating</tertiary></indexterm><indexterm id="IXT-8-1659"><primary>XPCOM</primary><secondary>identifiers</secondary><tertiary>generating</tertiary></indexterm>use
  942: a <indexterm id="IXT-8-1660"><primary>uuidgen
  943: tool</primary></indexterm><indexterm id="IXT-8-1661"><primary>XPCOM</primary><secondary>identifiers</secondary><tertiary>uuidgen
  944: and</tertiary></indexterm><indexterm id="IXT-8-1662"><primary>identifiers</primary><secondary>XPCOM</secondary><tertiary>uuidgen</tertiary></indexterm>command-line
  945: program called <filename>uuidgen</filename> that generates a unique
  946: number for you:
  947: </para>
  948: 
  949: <programlisting>$ uuidgen 
  950: ce32e3ff-36f8-425f-94be-d85b26e634ee</programlisting>
  951: 
  952: <para>On Windows, a program called <filename>guidgen.exe</filename> does
  953: <indexterm id="IXT-8-1663"><primary>guidgen.exe</primary></indexterm><indexterm id="IXT-8-1664"><primary>XPCOM</primary><secondary>identifiers</secondary><tertiary>guidgen.exe
  954: and</tertiary></indexterm><indexterm id="IXT-8-1665"><primary>identifiers</primary><secondary>XPCOM</secondary><tertiary>guidgen.exe
  955: and</tertiary></indexterm>the same thing and also provides a
  956: graphical user interface if you'd rather point and
  957: click.
  958: </para>
  959: 
  960: <para>Or you can use one of the special
  961: "bots" on IRC at the
  962: <emphasis>irc.mozilla.org</emphasis> server.
  963: </para>
  964: 
  965: <programlisting>irc irc.mozilla.org
  966: /join #mozilla
  967: /msg mozbot uuid</programlisting>
  968: 
  969: <para>This command makes the bot generate and return a uuid, which you can
  970: then copy into your component source code. The information can then
  971: be used to uniquely identify your component.
  972: </para>
  973: 
  974: </sect3>
  975: </sect2>
  976: <sect2 role="" id="mozilla-CHP-8-SECT-1.6" label="8.1.6">
  977: <title>Component Manager</title>
  978: 
  979: <para>One major goal of XPCOM modularization
  980: <indexterm id="IXT-8-1666"><primary>XPCOM</primary><secondary>Component
  981: Manager</secondary></indexterm><indexterm id="IXT-8-1667"><primary>Component Manager,
  982: XPCOM</primary></indexterm>is the removal of link-time dependencies,
  983: or dependencies that arise when you link libraries during
  984: compilation. The achievement of this goal allows you to access and
  985: use modules at runtime. The trouble then becomes finding those
  986: modules and figuring out which of their interfaces you want to use.
  987: This problem is solved through the use of the Component Manager.
  988: </para>
  989: 
  990: <para>The Component Manager is a special set of component management
  991: classes and implementation classes that reside in object libraries
  992: (<filename>.dll</filename>, <filename>.so</filename>,
  993: <filename>.js</filename>, <filename>.py</filename>, etc.). These
  994: classes also include factories, which <indexterm id="IXT-8-1668"><primary>factories,
  995: Component Manager
  996: and</primary></indexterm><indexterm id="IXT-8-1669"><primary>Component Manager,
  997: XPCOM</primary><secondary>factories</secondary></indexterm>let you
  998: create objects without having access to their class declarations.
  999: When you bind to objects at runtime, as you do in XPCOM, you need
 1000: functionality like this to help you discover and use objects without
 1001: looking at their code. The Component Manager
 1002: <indexterm id="IXT-8-1670"><primary>classes</primary><secondary>Component
 1003: Manager</secondary></indexterm><indexterm id="IXT-8-1671"><primary>Component Manager
 1004: class</primary></indexterm>also includes the Component Manager class
 1005: itself, known as <literal>nsComponentManager</literal>, which is a
 1006: mapping of class IDs to factories for the libraries they contain. The
 1007: Component Manager is responsible for the autoregistration of all new
 1008: or add-on modules <indexterm id="IXT-8-1672"><primary>components
 1009: directory</primary></indexterm><indexterm id="IXT-8-1673"><primary>directories</primary><secondary>components</secondary></indexterm>located
 1010: in the <emphasis>components</emphasis> directory. This
 1011: autoregistration happens behind the scenes and allows you to use new
 1012: components as they become available without having to register them
 1013: yourself.
 1014: </para>
 1015: 
 1016: <para>A component author first creates <indexterm id="IXT-8-1674"><primary>Component
 1017: Manager, XPCOM</primary><secondary>APIs and</secondary></indexterm>
 1018: <indexterm id="IXT-8-1675"><primary>APIs (Application Prgramming Interfaces),
 1019: Component Manager and</primary></indexterm>an interface file that
 1020: defines all APIs that will be publicly available for a component. The
 1021: component author then creates an implementation for the methods and
 1022: attributes in a separate implementation class. For example, an
 1023: <emphasis>nsILocalFile</emphasis> interface may have an
 1024: <literal>nsLocalFile</literal> implementation class. Then a factory
 1025: or module is needed to abstract the implementation class, and reduce
 1026: compile and link-time dependencies. It then creates instances of the
 1027: implementation class through its own implementation of
 1028: <literal>QueryInterface</literal>. For example:
 1029: </para>
 1030: 
 1031: <programlisting>// create an instance of the implementation class 
 1032: var f = Components.classes[`@mozilla.org/file/local;1'].createInstance( );</programlisting>
 1033: 
 1034: <para>The variable <emphasis>f</emphasis> is assigned an instance of a
 1035: <filename>nsLocalFile</filename> implementation class using the
 1036: <emphasis>nsIFactory</emphasis> method <literal>createInstance(
 1037: )</literal>. To match the correct interface
 1038: (<emphasis>nsILocalFile</emphasis> in this case) to the
 1039: implementation, you need a class instance to be created before you
 1040: can call on its member method <literal>QueryInterface( )</literal>:
 1041: </para>
 1042: 
 1043: <programlisting>// QI for nsILocalFile interface
 1044: var f = f.QueryInterface(Components.interfaces.nsILocalFile);</programlisting>
 1045: 
 1046: <para>Once you do this, the variable <emphasis>f</emphasis> is ready to use
 1047: the <emphasis>nsILocalFile</emphasis> interface to access the newly
 1048: created instance of the <literal>nsLocalFile</literal> class from
 1049: script.
 1050: </para>
 1051: 
 1052: <para>Simply put, a factory or module is a set of classes used by the
 1053: Component Manager to register and create an instance of the
 1054: component's implementation class. A factory can make
 1055: its way into the Mozilla component repository in several ways. The
 1056: most direct is through using the Component Manager method
 1057: <literal>RegisterFactory( )</literal>, which supports two different
 1058: registration mechanisms. The first mechanism, which takes a class ID
 1059: and a pointer to a factory, can be used on factories that are
 1060: actually linked into the executable. The second, which takes a class
 1061: ID and the path to a dynamically loadable library, can be used both
 1062: inside an executable at runtime and externally by using the
 1063: <literal>aPersist</literal> flag to tell the repository to store the
 1064: class ID/library relationship in its permanent store. The Component
 1065: Manager discovers new factories or modules placed in the
 1066: <emphasis>components</emphasis> directory and queries those modules
 1067: for the XPCOM components they provide. The name, contract IDs, and
 1068: class IDs are placed into a small component registry database for
 1069: quick retrieval. The factory provides this information through a
 1070: simple set of APIs required by every XPCOM module. Module creation,
 1071: covered later in this chapter, describes the process through which
 1072: all components contain an implementation of a module or factory.
 1073: </para>
 1074: 
 1075: </sect2>
 1076: <sect2 role="" id="mozilla-CHP-8-SECT-1.7" label="8.1.7">
 1077: <title>Getting and Using XPCOM</title>
 1078: 
 1079: <para>Mozilla is a client application that implements XPCOM, so everything
 1080: you need to use or build new XPCOM components is already included in
 1081: the source code and/or the binaries. Whenever you use the JavaScript
 1082: <literal>Components</literal> object, as described earlier, you use
 1083: XPCOM.
 1084: </para>
 1085: 
 1086: <para>If you'd rather not build the entire Mozilla browser
 1087: and you have no interest in existing Mozilla components or the large
 1088: footprint that comes with an entire distribution, then standalone
 1089: XPCOM is for you. To
 1090: <indexterm id="IXT-8-1676"><primary>XPCOM</primary><secondary>Mac OS
 1091: X</secondary></indexterm><indexterm id="IXT-8-1677"><primary>XPCOM</primary><secondary>cygwin</secondary></indexterm><indexterm id="IXT-8-1678"><primary>cygwin,
 1092: XPCOM</primary></indexterm><indexterm id="IXT-8-1679"><primary>Unix</primary><secondary>XPCOM</secondary></indexterm><indexterm id="IXT-8-1680"><primary>Macintosh</primary><secondary>XPCOM</secondary></indexterm>pull
 1093: the XPCOM source on Unix using Mac OS X or
 1094: <filename>cygwin</filename> on Windows, invoke the following
 1095: commands:
 1096: </para>
 1097: 
 1098: <programlisting><userinput>cvs -z 3 co mozilla/client.mk</userinput>
 1099: <userinput>cd mozilla</userinput>
 1100: <userinput>gmake -f client.mk pull_all BUILD_MODULES=xpcom</userinput></programlisting>
 1101: 
 1102: <para>To build the XPCOM Stand Alone version, type:</para>
 1103: 
 1104: <programlisting><userinput>configure --enable-modules=xpcom</userinput>
 1105: <userinput>gmake</userinput></programlisting>
 1106: 
 1107: <para>When you build
 1108: <indexterm id="IXT-8-1681"><primary>XPCOM</primary><secondary>standalone</secondary></indexterm>standalone
 1109: XPCOM, the directory <filename>xpcom/sample</filename> contains the
 1110: source code for a sample <indexterm id="IXT-8-1682"><primary>nsTestSample
 1111: component</primary></indexterm>application and a
 1112: <literal>nsTestSample</literal> component. The sample application
 1113: built from these sources, also called
 1114: <literal>nsTestSample</literal>, is installed in the Mozilla
 1115: <filename>bin</filename> directory. <filename>libsample.so</filename>
 1116: (Unix), which is the component that the sample application tries to
 1117: instantiate, should have been installed in
 1118: <filename>bin/components</filename>. To run the test that indicates
 1119: whether standalone XPCOM is installed successfully, change to the
 1120: <filename>mozilla/dist/bin</filename> directory and run the following
 1121: commands:
 1122: </para>
 1123: 
 1124: <programlisting><userinput>./run-mozilla.sh ./nsTestSample</userinput></programlisting>
 1125: 
 1126: <para>You should see the following output. If you do not, there is a
 1127: problem with the installation of standalone XPCOM:
 1128: </para>
 1129: 
 1130: <programlisting>Type Manifest File: /D/STAND_ALONE_XPCOM/mozilla/dist/bin/components/xpti.dat
 1131: nsNativeComponentLoader: autoregistering begins.
 1132: nsNativeComponentLoader: autoregistering succeeded
 1133: nNCL: registering deferred (0)
 1134: Inital print: initial value
 1135: Set value to: XPCOM defies gravity
 1136: Final print : XPCOM defies gravity
 1137: Test passed.</programlisting>
 1138: 
 1139: <para>Using standalone XPCOM is a powerful way to use the Mozilla framework
 1140: of cross-platform COM. Even if you're just hacking
 1141: on Mozilla, standalone XPCOM is a great way to learn about and use
 1142: XPCOM for application development.
 1143: </para>
 1144: 
 1145: </sect2>
 1146: </sect1>
 1147: 
 1148: <sect1 role="" id="mozilla-CHP-8-SECT-2" label="8.2">
 1149: <title>Creating XPCOM Components</title>
 1150: 
 1151: <para>As we mentioned, one
 1152: <indexterm id="IXT-8-1683"><primary>XPCOM</primary><secondary>components</secondary><tertiary>creating</tertiary></indexterm>
 1153: <indexterm id="IXT-8-1684"><primary>components</primary><secondary>XPCOM</secondary><tertiary>creating</tertiary></indexterm>advantage
 1154: of using XPCOM is that it separates the implementation from the
 1155: interface so you can write a component in a language-agnostic manner.
 1156: The services your component provides are available to all other
 1157: components despite the language used to implement it. This means, for
 1158: example, that you can use JavaScript not only to access the services
 1159: of an XPCOM component, but also to create those services. As
 1160: described in <link linkend="mozilla-CHP-5">Chapter 5</link>, using JavaScript as a
 1161: modularized application programming language provides the deepest
 1162: level of scripting in Mozilla.
 1163: </para>
 1164: 
 1165: <para>In your Mozilla build
 1166: <indexterm id="IXT-8-1685"><primary>directories</primary><secondary>components</secondary></indexterm>
 1167: <indexterm id="IXT-8-1686"><primary>components directory</primary></indexterm>or
 1168: distribution, you will find a subdirectory named
 1169: <filename>components</filename>. Inside this directory, you will see
 1170: many compiled components. You will also see a number of JavaScript
 1171: components. If you look at the source of these components, you can
 1172: get an idea of how
 1173: <indexterm id="IXT-8-1687"><primary>components</primary><secondary>JavaScript</secondary></indexterm>
 1174: <indexterm id="IXT-8-1688"><primary>JavaScript</primary><secondary>components</secondary></indexterm>
 1175: <indexterm id="IXT-8-1689"><primary>nsFilePicker.js file</primary></indexterm>
 1176: <indexterm id="IXT-8-1690"><primary>nsSidebar.js file</primary></indexterm>a
 1177: JavaScript component is created. For example, look at the files
 1178: <emphasis>nsFilePicker.js</emphasis> and
 1179: <emphasis>nsSidebar.js</emphasis>. These JavaScript components are
 1180: used in the Mozilla distribution.
 1181: </para>
 1182: 
 1183: <para>JavaScript XPCOM components have the advantage over regular scripts
 1184: of being fast, reusable, and globally accessible to any caller. They
 1185: also have the advantage over C++-based XPCOM components of being
 1186: easier to write and maintain. The next few sections describe the
 1187: creation of a JavaScript-based XPCOM component. If you would rather
 1188: do your work in C++, then skip to the C++ implementation section in
 1189: this chapter.
 1190: </para>
 1191: 
 1192: <sect2 role="" id="mozilla-CHP-8-SECT-2.1" label="8.2.1">
 1193: <title>Creating a JavaScript XPCOM Component</title>
 1194: 
 1195: <para>To create a JavaScript component, <indexterm class="startofrange" id="mozilla-IDXTERM-1169"><primary>JavaScript</primary><secondary>XPCOM
 1196: components</secondary></indexterm> <indexterm class="startofrange" id="mozilla-IDXTERM-1170"><primary>XPCOM</primary><secondary>components</secondary><tertiary>JavaScript</tertiary></indexterm>
 1197: <indexterm class="startofrange" id="mozilla-IDXTERM-1171"><primary>components</primary><secondary>XPCOM</secondary><tertiary>JavaScript</tertiary></indexterm>you
 1198: need to create an IDL interface source file and a JavaScript
 1199: implementation source file. In the Mozilla sources, naming source
 1200: files with an <filename>ns</filename> prefix is common practice, so
 1201: the implementation file should be called something like
 1202: <filename>nsSimple.js</filename>. The interface source file, or IDL
 1203: file, uses a similar convention: it is typical for interfaces to
 1204: begin with <filename>nsI</filename>, using an <filename>I</filename>
 1205: to distinguish them as interfaces rather than implementations. Call
 1206: the IDL source file <filename>nsISimple.idl</filename>.
 1207: </para>
 1208: 
 1209: <para>In addition to these two source files
 1210: (<filename>nsSimple.js</filename> and
 1211: <filename>nsISimple.idl</filename>), you will compile a cross
 1212: platform binary interface file, or type library, with the XPIDL
 1213: compiler, calling it <emphasis>nsISimple.xpt</emphasis>. This
 1214: <filename>.xpt</filename> file tells Mozilla that the interface is
 1215: available and scriptable. You can use it on any platform that Mozilla
 1216: supports. In other words, you can pick up
 1217: <emphasis>nsISimple.xpt</emphasis>, which may have been compiled on
 1218: Unix, drop it into Windows or Mac OS, and use it.
 1219: </para>
 1220: 
 1221: <para>All <filename>.xpt</filename> interface files for Mozilla live in the
 1222: <filename>components</filename> directory located in
 1223: <emphasis>mozilla/dist/bin</emphasis> if you are developing with the
 1224: Mozilla source code. Otherwise, for binary distributions of Mozilla,
 1225: they are located in <emphasis>mozilla/components</emphasis>. Mozilla
 1226: checks this directory upon start up, looking for any new components
 1227: to register automatically.
 1228: </para>
 1229: 
 1230: <sect3 role="" id="mozilla-CHP-8-SECT-2.1.1" label="8.2.1.1">
 1231: <title>The XPIDL interface source file</title>
 1232: 
 1233: <para>Usually, the first step in creating
 1234: <indexterm id="IXT-8-1691"><primary>XPIDL</primary><secondary>interface source
 1235: file</secondary></indexterm><indexterm id="IXT-8-1692"><primary>interfaces</primary><secondary>XPIDL
 1236: source file</secondary></indexterm>a new component is writing the
 1237: interface. To begin, open up your favorite text editor and create a
 1238: new file called <filename>nsISimple.idl</filename>.
 1239: </para>
 1240: 
 1241: <para>The complete source code for the <filename>nsISimple.idl</filename>
 1242: interface file is:
 1243: </para>
 1244: 
 1245: <programlisting>#include "nsISupports.idl"
 1246: [scriptable, uuid(ce32e3ff-36f8-425f-94be-d85b26e634ee)]
 1247: interface nsISimple : nsISupports
 1248: {
 1249:     attribute string yourName;
 1250:     void write( );
 1251:     void change(in string aValue);
 1252: };</programlisting>
 1253: 
 1254: <para>The <literal>#include</literal> line above includes the file
 1255: <filename>nsISupports.idl</filename>, which defines this
 1256: interface's base class. The <literal>[scriptable,
 1257: uuid..]</literal> line declares the interface scriptable and assigns
 1258: a UUID<filename> </filename>to the interface. You can use the UUID
 1259: provided, but creating your own using one of the UUID generation
 1260: tools described earlier is usually better. The third line, next to
 1261: the <literal>interface</literal> keyword, declares the
 1262: interface's name, <emphasis>nsISimple</emphasis>,
 1263: and says that it derives from <emphasis>nsISupports</emphasis>.
 1264: </para>
 1265: 
 1266: <para>Various attributes and methods are defined within the definition of
 1267: the <literal>nsISimple</literal> interface. Attributes are properties
 1268: of interface objects. They may be read-only or read/write variables.
 1269: In <emphasis>nsISimple</emphasis>, an attribute called
 1270: <literal>yourName</literal> is of the type <literal>string</literal>.
 1271: In this implementation, you may get and set this
 1272: attribute's value. Of the methods defined in this
 1273: interface, the <literal>write( )</literal> method takes no arguments
 1274: and the <literal>change( )</literal> method takes an argument of type
 1275: string called <literal>aValue</literal>. The parameter
 1276: <literal>aValue</literal> will be a new value that replaces the
 1277: current value held by <literal>yourName</literal>. The complete
 1278: interface IDL is:
 1279: </para>
 1280: 
 1281: <programlisting>#include "nsISupports.idl"
 1282: [scriptable, uuid(ce32e3ff-36f8-425f-94be-d85b26e634ee)]
 1283: interface nsISimple : nsISupports
 1284: {
 1285:     attribute string yourName;
 1286:     void write( );
 1287:     void change(in string aValue);
 1288: };</programlisting>
 1289: 
 1290: </sect3>
 1291: 
 1292: <sect3 role="" id="mozilla-CHP-8-SECT-2.1.2" label="8.2.1.2">
 1293: <title>JavaScript implementation file</title>
 1294: 
 1295: <para>Once you have created an interface <indexterm id="IXT-8-1693"><primary>implementation
 1296: files, JavaScript XPCOM components</primary></indexterm>file that
 1297: publicly defines the component's methods and
 1298: attributes, the next step is to implement those methods and
 1299: attributes in a separate source file. The listings below walk through
 1300: the implementation of <literal>nsISimple</literal> step by step.
 1301: </para>
 1302: 
 1303: <para>First, you must declare an empty function
 1304: <indexterm id="IXT-8-1694"><primary>SimpleComponent
 1305: function</primary></indexterm><indexterm id="IXT-8-1695"><primary>functions</primary><secondary>SimpleComponent</secondary></indexterm>called
 1306: <literal>SimpleComponent</literal>, which is a standard constructor
 1307: for a JavaScript object prototype. It's a good idea
 1308: to name the component in a way that clearly describes both the
 1309: component and the interface, as <literal>SimpleComponent</literal>
 1310: does (i.e., <literal>SimpleComponent</literal> is an implementation
 1311: of the <literal>nsISimple</literal> interface):
 1312: </para>
 1313: 
 1314: <programlisting>function SimpleComponent( ) {}</programlisting>
 1315: 
 1316: <para>With the function declared, we start defining the JavaScript class
 1317: prototype.
 1318: </para>
 1319: 
 1320: <programlisting>SimpleComponent.prototype = {
 1321:     mName : "a default value",</programlisting>
 1322: 
 1323: <para>In the prototype, we first create a member variable called
 1324: <emphasis>mName</emphasis> that will be the string placeholder for
 1325: the IDL attribute <literal>yourName</literal>. The variable assigns
 1326: the string a default value. Remember to place commas after all
 1327: definitions in a prototype. IDL attributes are always implemented as
 1328: getter functions. Methods marked with <literal>[noscript]</literal>
 1329: will not be available for use with scripting languages.
 1330: </para>
 1331: 
 1332: <para>Next we implement the functions below for our definition of
 1333: <literal>attribute</literal> <literal>string</literal>
 1334: <literal>yourName</literal> in our file
 1335: <filename>nsISimple.idl</filename>.
 1336: </para>
 1337: 
 1338: <programlisting>    get yourName( )      { return this.mName; },
 1339:     set yourName(aName) { return this.mName = aName; },</programlisting>
 1340: 
 1341: <para>When someone calls an IDL attribute in an interface, getters and
 1342: setters are used to get or set values for the attribute:
 1343: </para>
 1344: 
 1345: <programlisting>simple.yourName='foo';</programlisting>
 1346: 
 1347: <para>Or similarly read values from the attribute:</para>
 1348: 
 1349: <programlisting>var foo = simple.yourName;</programlisting>
 1350: 
 1351: <para>We first call on the setter function to set a value to the attribute
 1352: <literal>yourName</literal> and then use the getter function to
 1353: obtain the currently set value of <literal>yourName</literal>.
 1354: </para>
 1355: 
 1356: <para>The first function defined in <literal>nsISimple</literal> is called
 1357: <literal>void write( )</literal>. For this method, the implementation
 1358: can be as simple as the following code:
 1359: </para>
 1360: 
 1361: <programlisting>    write : function ( ) { dump("Hello " + this.mName + "\n"); },</programlisting>
 1362: 
 1363: <para>This example implements the declaration <literal>void write(
 1364: )</literal> by dumping the current value of the variable
 1365: <emphasis>mName</emphasis> to <literal>stdout</literal>. The code
 1366: uses the <literal>this</literal> keyword to indicate that you are
 1367: calling to the component's own member variable
 1368: <emphasis>mName</emphasis>.
 1369: </para>
 1370: 
 1371: <para>The <literal>void change( )</literal> method is then implemented as
 1372: follows:
 1373: </para>
 1374: 
 1375: <programlisting>    change : function (aValue) { this.mName = aValue; },</programlisting>
 1376: 
 1377: <para><literal>change( )</literal> is a method used to change the value
 1378: variable.
 1379: </para>
 1380: 
 1381: </sect3>
 1382: 
 1383: <sect3 role="" id="mozilla-CHP-8-SECT-2.1.3" label="8.2.1.3">
 1384: <title>Implementing the required XPCOM methods in JavaScript</title>
 1385: 
 1386: <para>Once the definitions in the <emphasis>nsISimple</emphasis> interface
 1387: <indexterm id="IXT-8-1696"><primary>XPCOM</primary><secondary>methods</secondary><tertiary>JavaScript
 1388: implementation</tertiary></indexterm><indexterm id="IXT-8-1697"><primary>methods</primary><secondary>XPCOM</secondary><tertiary>JavaScript
 1389: implementation</tertiary></indexterm><indexterm id="IXT-8-1698"><primary>JavaScript</primary><secondary>XPCOM
 1390: method implemenation</secondary></indexterm>are implemented, you need
 1391: to implement required methods and factories that make this JavaScript
 1392: implementation class an XPCOM component. Recall that all XPCOM
 1393: components must implement the <literal>nsISupports</literal>
 1394: interface.
 1395: </para>
 1396: 
 1397: <para><link linkend="mozilla-CHP-8-EX-3">Example 8-3</link> shows an implementation of
 1398: <literal>QueryInterface</literal> specific to our new component.
 1399: <literal>QueryInterface</literal> ensures that the correct interface
 1400: (<literal>nsISimple</literal>) is used by matching the
 1401: <literal>iid</literal> with the <literal>nsISimple</literal>
 1402: interface that this component implements. If the interface
 1403: doesn't match, then the argument is invalid. In this
 1404: case, the exception
 1405: <filename>Components.results.NS_ERROR_NO_INTERFACE </filename>is
 1406: thrown, which maps to the error code number 2147500034, and code
 1407: execution is stopped. If the interface identifier parameter matches
 1408: the interface, then an instance of the implementation class object
 1409: <literal>SimpleComponent</literal> with its interface is returned as
 1410: a ready-to-use XPCOM component. In XPCOM, every component you
 1411: implement must <indexterm id="IXT-8-1699"><primary>QueryInterface
 1412: method</primary><secondary>XPCOM</secondary></indexterm><indexterm id="IXT-8-1700"><primary>methods</primary><secondary>XPCOM</secondary><tertiary>QueryInterface</tertiary></indexterm><indexterm id="IXT-8-1701"><primary>QueryInterface
 1413: method</primary><secondary>XPCOM</secondary></indexterm>have a
 1414: <literal>QueryInterface</literal> method.
 1415: </para>
 1416: 
 1417: <example id="mozilla-CHP-8-EX-3" label="8-3">
 1418: <title>QueryInterface method for nsISimple interface </title>
 1419: <programlisting>  QueryInterface: function (iid) 
 1420:   {
 1421:    if(!iid.equals(Components.interfaces.nsISimple) 
 1422:         &amp;&amp; !iid.equals(Components.interfaces.nsISupports))
 1423:        throw Components.results.NS_ERROR_NO_INTERFACE;
 1424:    return this;
 1425:   }</programlisting>
 1426: </example>
 1427: 
 1428: <para>The next requirement is to create a JavaScript object called
 1429: <literal>Module</literal>. This module implements the methods needed
 1430: for autoregistration and component return type objects.
 1431: </para>
 1432: 
 1433: <programlisting>var Module = {
 1434:     firstTime  : true,</programlisting>
 1435: 
 1436: <para>The Boolean <literal>firstTime</literal> is a flag used only when the
 1437: component is initially registered:
 1438: </para>
 1439: 
 1440: <programlisting>registerSelf: function (compMgr, fileSpec, location, type) {
 1441:   if (this.firstTime) {
 1442:     dump("*** first time registration of Simple JS component\n");
 1443:     this.firstTime = false;
 1444:     throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
 1445:   }</programlisting>
 1446: 
 1447: <para>The Component Manager can do a lot in the registration process, but
 1448: you have to add some logic for first time registration so the
 1449: Component Manager has the information it needs.
 1450: <literal>RegisterSelf</literal> is called at registration time
 1451: (component installation) and is responsible for notifying the
 1452: component manager of all components implemented in this module. The
 1453: <literal>fileSpec</literal>, <literal>location</literal>, and
 1454: <literal>type</literal> parameters can be passed on to the
 1455: <literal>registerComponent</literal> method unmolested. Next,
 1456: register the component with the Component Manager using code like the
 1457: following example. The parameters include the CID, a description, a
 1458: <literal>progID</literal>, and the other parameters you can pass
 1459: without changing:
 1460: </para>
 1461: 
 1462: <programlisting>  dump(" ***** Registering: Simple JS component! ****\n");
 1463:   compMgr.registerComponentWithType(this.myCID,
 1464:             "My JS Component",
 1465:             this.myProgID, fileSpec,
 1466:             location, true, true,
 1467:             type);
 1468:     },</programlisting>
 1469: 
 1470: <para>The <literal>GetClassObject</literal>
 1471: <indexterm id="IXT-8-1702"><primary>GetClassObject method,
 1472: XPCOM</primary></indexterm><indexterm id="IXT-8-1703"><primary>XPCOM</primary><secondary>methods</secondary><tertiary>GetClassObject</tertiary></indexterm><indexterm id="IXT-8-1704"><primary>methods</primary><secondary>XPCOM</secondary><tertiary>GetClassObject</tertiary></indexterm>method
 1473: produces <literal>Factory</literal> and
 1474: <literal>SingletonFactory</literal> objects. Singleton objects are
 1475: specialized for services that allow only one instance of the object.
 1476: Upon success, the method returns an instance of the components
 1477: factory, which is the implementation class less its interface:
 1478: </para>
 1479: 
 1480: <programlisting>getClassObject : function (compMgr, cid, iid) {
 1481:   if (!cid.equals(this.myCID))
 1482:       throw Components.results.NS_ERROR_NO_INTERFACE;
 1483:   if (!iid.equals(Components.interfaces.nsIFactory))
 1484:       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
 1485:   return this.myFactory;
 1486:   },</programlisting>
 1487: 
 1488: <para>In the previous list, the member variables <emphasis>myCID</emphasis>
 1489: and <emphasis>myProgID</emphasis> are the class ID and the
 1490: human-readable canonical program ID, respectively:
 1491: </para>
 1492: 
 1493: <programlisting>    myCID: Components.ID("{98aa9afd-8b08-415b-91ed-01916a130d16}"),
 1494:     myProgID: "@mozilla.org/js_simple_component;1",</programlisting>
 1495: 
 1496: <para>The member object <literal>myFactory</literal> is the components
 1497: factory, which through its own member function,
 1498: <literal>createInstance( )</literal>, constructs and returns an
 1499: instance of the complete component (if the <literal>iid</literal>
 1500: parameter is specified and is the correct interface). Otherwise, if
 1501: no <literal>iid</literal> parameter is used, the
 1502: <literal>iid</literal> of <emphasis>nsISupports</emphasis> is used
 1503: and an instance of the module is created that will then need a
 1504: subsequent call to <literal>QueryInterface</literal> to instantiate
 1505: the object as a component.
 1506: </para>
 1507: 
 1508: <programlisting>    myFactory: {
 1509:       createInstance: function (outer, iid) {
 1510:         dump("CI: " + iid + "\n");
 1511:         if (outer != null)
 1512:         throw Components.results.NS_ERROR_NO_AGGREGATION;
 1513:         return (new SimpleComponent( )).QueryInterface(iid);
 1514:   }
 1515: },</programlisting>
 1516: 
 1517: <para>The method <literal>canUnload</literal> unloads the module when
 1518: shutdown occurs and is the last function in the module. The
 1519: <literal>componentManager</literal> calls the method
 1520: <literal>NSGetModule</literal> to initialize these required XPCOM
 1521: methods and objects:
 1522: </para>
 1523: 
 1524: <programlisting>canUnload: function(compMgr) {
 1525:   dump("****** Unloading: Simple JS component! ****** \n");
 1526:   return true;
 1527: }
 1528: function NSGetModule(compMgr, fileSpec) { return Module; }</programlisting>
 1529: 
 1530: <para>The code in <link linkend="mozilla-CHP-8-EX-4">Example 8-4</link> shows <indexterm id="IXTR3-1745" class="endofrange" startref="mozilla-IDXTERM-1169"/> <indexterm id="IXTR3-1746" class="endofrange" startref="mozilla-IDXTERM-1170"/> <indexterm id="IXTR3-1747" class="endofrange" startref="mozilla-IDXTERM-1171"/>the implementation
 1531: for the <emphasis>nsISimple</emphasis> interface in its entirety.
 1532: </para>
 1533: 
 1534: <example id="mozilla-CHP-8-EX-4" label="8-4">
 1535: <title>JavaScript implementation of nsISimple </title>
 1536: <programlisting>
 1537: 
 1538: function SimpleComponent(){}
 1539: 
 1540: SimpleComponent.prototype = {
 1541: 
 1542:     get yourName()        { return this.mName; },
 1543:     set yourName(aName)   { return this.mName = aName; },
 1544: 
 1545:     write: function () { dump("Hello " + this.mName + "\n"); },
 1546:     change: function (aValue) { this.mName = aValue; },
 1547:     mName: "a default value",
 1548: 
 1549:     QueryInterface: function (iid) {
 1550:         if (!iid.equals(Components.interfaces.nsISimple)
 1551:             &amp;&amp; !iid.equals(Components.interfaces.nsISupports))
 1552:         {
 1553:             throw Components.results.NS_ERROR_NO_INTERFACE;
 1554:         }
 1555:         return this;
 1556:     }
 1557: }
 1558: 
 1559: var Module = {
 1560:     firstTime: true,
 1561: 
 1562:     registerSelf: function (compMgr, fileSpec, location, type) {
 1563:         if (this.firstTime) {
 1564:             dump("*** Deferring registration of simple JS components\n");
 1565:             this.firstTime = false;
 1566:             throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
 1567:         }
 1568:         debug("*** Registering sample JS components\n");
 1569:         compMgr =
 1570: compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
 1571:         compMgr.registerFactoryLocation(this.myCID,
 1572:                                         "Simple JS Component",
 1573:                                         this.myProgID,
 1574:                                         fileSpec,
 1575:                                         location,
 1576:                                         type);
 1577:     },
 1578: 
 1579:     getClassObject : function (compMgr, cid, iid) {
 1580:         if (!cid.equals(this.myCID))
 1581:         throw Components.results.NS_ERROR_NO_INTERFACE
 1582:         if (!iid.equals(Components.interfaces.nsIFactory))
 1583:         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
 1584:         return this.myFactory;
 1585:     },
 1586: 
 1587:     myCID: Components.ID("{98aa9afd-8b08-415b-91ed-01916a130d16}"),
 1588:     myProgID: "@mozilla.org/js_simple_component;1",
 1589: 
 1590:     myFactory: {
 1591:         createInstance: function (outer, iid) {
 1592:             dump("CI: " + iid + "\n");
 1593:             if (outer != null)
 1594:             throw Components.results.NS_ERROR_NO_AGGREGATION;
 1595:             return (new SimpleComponent()).QueryInterface(iid);
 1596:         }
 1597:     },
 1598: 
 1599:     canUnload: function(compMgr) {
 1600:         dump("****** Unloading: Simple JS component! ****** \n");
 1601:         return true;
 1602:     }
 1603: }; // END Module
 1604: 
 1605: function NSGetModule(compMgr, fileSpec) { return Module; }
 1606: </programlisting>
 1607: </example>
 1608: 
 1609: </sect3>
 1610: </sect2>
 1611: <sect2 role="" id="mozilla-CHP-8-SECT-2.2" label="8.2.2">
 1612: <title>Compiling the Component</title>
 1613: 
 1614: <para>Once you create an IDL source <indexterm class="startofrange" id="mozilla-IDXTERM-1189"><primary>JavaScript</primary><secondary>XPCOM
 1615: components</secondary><tertiary>compiling</tertiary></indexterm>
 1616: <indexterm class="startofrange" id="mozilla-IDXTERM-1190"><primary>XPCOM</primary><secondary>components</secondary><tertiary>JavaScript</tertiary></indexterm>
 1617: <indexterm class="startofrange" id="mozilla-IDXTERM-1191"><primary>components</primary><secondary>XPCOM</secondary><tertiary>JavaScript</tertiary></indexterm>
 1618: <indexterm class="startofrange" id="mozilla-IDXTERM-1192"><primary>compiling components, XPCOM with
 1619: JavaScript</primary></indexterm>file and a JavaScript implementation
 1620: file, you need to compile <literal>nsISimple.idl</literal> into a
 1621: <filename>.xpt</filename> type library.
 1622: </para>
 1623: 
 1624: <sect3 role="" id="mozilla-CHP-8-SECT-2.2.1" label="8.2.2.1">
 1625: <title>Compiling the type library</title>
 1626: 
 1627: <para>To compile the XPIDL interface <indexterm id="IXT-8-1705"><primary>type
 1628: libraries</primary><secondary>XPCOM and
 1629: JavaScript</secondary></indexterm>file
 1630: <emphasis>nsISimple.idl</emphasis>, you need to add the path of the
 1631: XPIDL compiler to your environment. As mentioned earlier, the XPIDL
 1632: compiler is located at
 1633: <filename>mozilla/xpcom/typelib/xpidl</filename>. Here is the output
 1634: of a <filename>Unix/cygwin/OSX</filename> session showing the
 1635: compilation starting with the source file
 1636: (<filename>nsISimple.idl</filename>) created earlier in the chapter.
 1637: Afterwards, <filename>nsISimple.xpt</filename> and
 1638: <filename>nsSimple.js</filename> are copied to the
 1639: <filename>components</filename> directory:
 1640: </para>
 1641: 
 1642: <programlisting>$ ls
 1643: nsISimple.idl  nsSimple.js
 1644: $ PATH=$PATH:/usr/src/mozilla/xpcom/typelib/xpidl
 1645: $ echo $PATH
 1646: /sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/bin:/usr/X11R6/bin:/root/bin:/usr/src/mozilla/xpcom/typelib/xpidl
 1647: $ export XPIDL_INC=/usr/src/mozilla/xpcom/base
 1648: $ echo $XPIDL_INC
 1649: /usr/src/mozilla/xpcom/base
 1650: $ xpidl -m typelib -w -v -I $XPIDL_INC \
 1651: &gt; -o nsISimple nsISimple.idl
 1652: $ ls
 1653: nsISimple.idl  nsISimple.xpt  nsSimple.js
 1654: $ cp nsISimple.xpt nsSimple.js \
 1655: &gt; /usr/src/mozilla/dist/bin/components/</programlisting>
 1656: 
 1657: <para>This output illustrates the compilation of the
 1658: <filename>nsISimple.idl </filename>source file into the
 1659: <filename>nsISimple.xpt</filename> typelib file. The newly compiled
 1660: <filename>typelib</filename> file and the JavaScript implementation
 1661: file are then copied to the Mozilla distribution components directory
 1662: where component registration will occur automatically when Mozilla is
 1663: launched.
 1664: </para>
 1665: 
 1666: </sect3>
 1667: 
 1668: <sect3 role="" id="mozilla-CHP-8-SECT-2.2.2" label="8.2.2.2">
 1669: <title>Creating a Makefile for your component project</title>
 1670: 
 1671: <para>All previous steps were done <indexterm id="IXT-8-1706"><primary>Makefiles, compiling
 1672: XPCOM components</primary></indexterm>manually. You can also create a
 1673: Makefile to automate this process by using GNU make, in which case
 1674: you would create a Makefile with the following variables and targets
 1675: defined:
 1676: </para>
 1677: 
 1678: <programlisting>TOP_SRC=/usr/src/mozilla
 1679: INST_DIR=$(TOP_SRC)/dist/bin/components
 1680: XPIDL=$(TOP_SRC)/xpcom/typelib/xpidl
 1681: XPIDL_INC=$(TOP_SRC)/xpcom/base
 1682: FLAGS=-m typelib -w -v -I $(XPIDL_INC) -o
 1683: all:
 1684:   $(XPIDL)/xpidl $(FLAGS) \
 1685:   nsISimple nsISimple.idl
 1686: install:
 1687:   cp nsISimple.xpt nsSimple.js $(INST_DIR)
 1688: clean:
 1689:   rm -rf *.xpt
 1690: uninstall:
 1691:   rm -f $(INST_DIR)/nsISimple.xpt
 1692:   rm -f $(INST_DIR)/nsSimple.js</programlisting>
 1693: 
 1694: <para>Remember that you must indent after your targets with a
 1695: <literal>&lt;tab&gt;</literal>.
 1696: </para>
 1697: 
 1698: <para>In this file, which can be used on Unix, Windows using
 1699: <filename>cygwin</filename>, or Mac OS X, the
 1700: <literal>TOP_SRC</literal> environment variable points to the Mozilla
 1701: source tree's top-level directory, the
 1702: <literal>INST_DIR</literal> points to the directory where the
 1703: component should be installed, and the XPIDL variables drive the
 1704: XPIDL executable and its environment and compiler flags. The
 1705: "all" Makefile target compiles and
 1706: creates the type library <filename>nsISimple.xpt</filename>.
 1707: </para>
 1708: 
 1709: <para>Note that in addition to the type <indexterm id="IXTR3-1748" class="endofrange" startref="mozilla-IDXTERM-1189"/> <indexterm id="IXTR3-1749" class="endofrange" startref="mozilla-IDXTERM-1190"/> <indexterm id="IXTR3-1750" class="endofrange" startref="mozilla-IDXTERM-1191"/> <indexterm id="IXTR3-1751" class="endofrange" startref="mozilla-IDXTERM-1192"/>libraries, the XPIDL compiler compiles
 1710: header files, Java class files, and special HTML documentation, if
 1711: necessary.
 1712: </para>
 1713: 
 1714: </sect3>
 1715: </sect2>
 1716: <sect2 role="" id="mozilla-CHP-8-SECT-2.3" label="8.2.3">
 1717: <title>Testing the Component</title>
 1718: 
 1719: <para>When you start up <filename>xpcshell</filename>, the Component
 1720: Manager finds the new <literal>nsISimple</literal> component and
 1721: registers it. The result of your test should look similar to <link linkend="mozilla-CHP-8-EX-5">Example 8-5</link>. 
 1722: </para>
 1723: 
 1724: <example id="mozilla-CHP-8-EX-5" label="8-5">
 1725: <title>Scripting the "simple" component in xpcshell </title>
 1726: <programlisting>$ cd /usr/src/mozilla/dist/bin/
 1727: $ ./run-mozilla.sh ./xpcshell  
 1728: Type Manifest File: /home/petejc/MOZILLA/mozilla/dist/bin/components/xpti.dat
 1729: nsNativeComponentLoader: autoregistering begins.
 1730: nsNativeComponentLoader: autoregistering succeeded
 1731: *** first time registration of Simple JS component
 1732: nNCL: registering deferred (0)
 1733:  ***** Registering: Simple JS component! ****
 1734: nNCL: registering deferred (0)
 1735: js&gt;const Simple=new Components.Constructor("@mozilla.org/js_simple_component;1", "nsISimple");
 1736: js&gt; var simple=new Simple( );
 1737: CI: {ce32e3ff-36f8-425f-94be-d85b26e634ee}
 1738: js&gt; for(var list in simple)
 1739: print(list);
 1740: QueryInterface
 1741: yourName
 1742: write
 1743: change
 1744: js&gt; simple.yourName;
 1745: a default value
 1746: js&gt; simple.yourName="Pete";
 1747: Pete
 1748: js&gt; simple.write( );
 1749: Hello Pete
 1750: null
 1751: js&gt; simple.change("Brian");
 1752: null
 1753: js&gt; simple.write( );
 1754: Hello Brian
 1755: null
 1756: js&gt; simple.yourName;
 1757: Brian
 1758: js&gt; quit( );
 1759: CanUnload_enumerate: skipping native
 1760: ****** Unloading: Simple JS component! ******</programlisting>
 1761: </example>
 1762: 
 1763: <para>Once the component is tested and registered as an XPCOM object, you
 1764: can use JavaScript from a local web page or from the chrome to create
 1765: an <literal>nsISimple</literal> object and use it as you would any
 1766: ordinary JavaScript object:
 1767: </para>
 1768: 
 1769: <programlisting>&lt;script type="application/x-JavaScript"&gt;
 1770: netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 1771: var Simple=new Components.Constructor("@mozilla.org/js_simple_component;1", "nsISimple");
 1772: var s = new Simple( );
 1773: for(var list in s)
 1774: document.write(list+"&lt;br&gt;\n");
 1775: &lt;/script&gt;</programlisting>
 1776: 
 1777: <para>In addition to creating a component in JavaScript, you can implement
 1778: XPCOM components in C++ and Python. The next sections cover the C++
 1779: implementation of the <literal>nsISimple</literal> interface.
 1780: </para>
 1781: 
 1782: </sect2>
 1783: <sect2 role="" id="mozilla-CHP-8-SECT-2.4" label="8.2.4">
 1784: <title>Useful C++ Macros and Types</title>
 1785: 
 1786: <para>Before you begin working on <indexterm id="IXT-8-1707"><primary>macros,
 1787: C++</primary></indexterm><indexterm id="IXT-8-1708"><primary>C++</primary><secondary>macros</secondary></indexterm><indexterm id="IXT-8-1709"><primary>types,
 1788: C++</primary></indexterm><indexterm id="IXT-8-1710"><primary>C++</primary><secondary>types</secondary></indexterm>an
 1789: actual implementation of a C++ component, familiarize yourself with
 1790: some of the tools that make C++ programming for XPCOM a little
 1791: easier. Templates, special types, and macros can ease some of the
 1792: extra housekeeping that programming XPCOM requires.
 1793: </para>
 1794: 
 1795: <para>More tools than we can cover in this introduction are available, but
 1796: this section reviews some of the most common, including a macro that
 1797: implements the <literal>nsISupports</literal> methods
 1798: <literal>QueryInterface</literal>, <literal>AddRef</literal>, and
 1799: <literal>Release</literal>, macros for testing
 1800: <literal>nsresults</literal>, <literal>smart</literal><filename>
 1801: </filename><literal>pointers</literal>, and special types.
 1802: </para>
 1803: 
 1804: <sect3 role="" id="mozilla-CHP-8-SECT-2.4.1" label="8.2.4.1">
 1805: <title>The NS_IMPL_ISUPPORTS1_CI macro</title>
 1806: 
 1807: <para>Rather than having to <indexterm id="IXT-8-1711"><primary>NS_IMPL_ISUPPORTS1_CI
 1808: macro</primary></indexterm>implement
 1809: <literal>QueryInterface</literal>, <literal>AddRef</literal>, and the
 1810: <literal>Release</literal> methods like we did in our JavaScript
 1811: component, the <literal>NS_IMPL_ISUPPORTS</literal> macro inserts the
 1812: implementation code for you.
 1813: </para>
 1814: 
 1815: <para>To use this macro for the <literal>nsISimple</literal> interface,
 1816: type:
 1817: </para>
 1818: 
 1819: <programlisting>NS_IMPL_ISUPPORTS1_CI(nsSimpleImpl, nsISimple)</programlisting>
 1820: 
 1821: <para>The following lines define this macro:</para>
 1822: 
 1823: <programlisting>#define NS_IMPL_ISUPPORTS1(_class, _interface) \
 1824: NS_IMPL_ADDREF(_class)                         \
 1825: NS_IMPL_RELEASE(_class)                        \
 1826: NS_IMPL_QUERY_INTERFACE1(_class, _interface)</programlisting>
 1827: 
 1828: <para>As you can see, the macro is made up of other macros that implement
 1829: basic methods of the <literal>nsISupports</literal> interface. Unless
 1830: you need to modify these macros, they should be left as is. This
 1831: macro is used later on when we create our C++ component.
 1832: </para>
 1833: 
 1834: <para><link linkend="mozilla-CHP-8-EX-6">Example 8-6</link> shows a reference
 1835: <indexterm id="IXT-8-1712"><primary>QueryInterface
 1836: method</primary><secondary>C++</secondary></indexterm>
 1837: <indexterm id="IXT-8-1713"><primary>methods</primary><secondary>QueryInterface</secondary><tertiary>C++</tertiary></indexterm>implementation
 1838: of the <literal>QueryInterface</literal> method in C++.
 1839: </para>
 1840: 
 1841: <example id="mozilla-CHP-8-EX-6" label="8-6">
 1842: <title>Reference implementation of QueryInterface </title>
 1843: <programlisting>NS_IMETHODIMP
 1844: nsMyImplementation::QueryInterface( REFNSIID aIID, void** aInstancePtr )
 1845: {
 1846:   NS_ASSERTION(aInstancePtr, "QueryInterface requires a non-NULL destination!");
 1847:   if ( !aInstancePtr )
 1848:     return NS_ERROR_NULL_POINTER;
 1849:   nsISupports* foundInterface;
 1850:   if ( aIID.Equals(nsCOMTypeInfo&lt;nsIX&gt;::GetIID( )) )
 1851:     foundInterface = NS_STATIC_CAST(nsIX*, this);
 1852:   else if ( aIID.Equals(nsCOMTypeInfo&lt;nsIY&gt;::GetIID( )) )
 1853:     foundInterface = NS_STATIC_CAST(nsIY*, this);
 1854:   else if ( aIID.Equals(nsCOMTypeInfo&lt;nsISupports&gt;::GetIID( )) )
 1855:     foundInterface = NS_STATIC_CAST(nsISupports*, NS_STATIC_CAST(nsIX*, this));
 1856:   else
 1857:     foundInterface = 0;
 1858:   nsresult status;
 1859:   if ( !foundInterface ) {
 1860:     status = NS_NOINTERFACE;
 1861:   } else {
 1862:     NS_ADDREF(foundInterface);
 1863:     status = NS_OK;
 1864:   }
 1865:   *aInstancePtr = foundInterface;
 1866:   return status;
 1867: }</programlisting>
 1868: </example>
 1869: 
 1870: </sect3>
 1871: 
 1872: <sect3 role="" id="mozilla-CHP-8-SECT-2.4.2" label="8.2.4.2">
 1873: <title>The results macros</title>
 1874: 
 1875: <para>Since all XPCOM methods <indexterm id="IXT-8-1714"><primary>results
 1876: macro</primary></indexterm>return result codes called
 1877: <filename>nsresults</filename>, another useful macro is the
 1878: <literal>NS_SUCCEEDED</literal> macro. This
 1879: <indexterm id="IXT-8-1715"><primary>NS_SUCCEEDED macro</primary></indexterm>indicates
 1880: whether an XPCOM accessor has returned a successful result. It is
 1881: defined in <filename>nsError.h</filename>:
 1882: </para>
 1883: 
 1884: <programlisting>  #define NS_SUCCEEDED(_nsresult) (!((_nsresult) &amp; 0x80000000))</programlisting>
 1885: 
 1886: <para>A related macro, <literal>NS_FAILED</literal>, is indicates whether
 1887: an XPCOM accessor returned a failure code result. It too is defined
 1888: in <filename>nsError.h</filename>. The following code demonstrates
 1889: the typical use of these two macros:
 1890: </para>
 1891: 
 1892: <programlisting>nsresult rv; 
 1893: nsCOMPtr&lt;nsILocalFile&gt; file(do_CreateInstance("@mozilla.org/file/local;1", &amp;rv));
 1894:   if (NS_FAILED(rv)) {
 1895:     printf("FAILED\n");
 1896:     return rv;
 1897:   }
 1898:   if (NS_SUCCEEDED(rv)) {
 1899:     printf(" SUCCEEDED \n");
 1900:     return rv;
 1901:   }</programlisting>
 1902: 
 1903: <para>You may have noticed that the declaration of the identifier
 1904: <literal>rv</literal> as the type <literal>nsresult</literal>.
 1905: <literal>nsresult</literal> is a 32-bit unsigned integer declared in
 1906: <filename>nscore.h</filename>:
 1907: </para>
 1908: 
 1909: <programlisting>  typedef PRUint32 nsresult;</programlisting>
 1910: 
 1911: <para>We assign an <filename>nsCOMPtr</filename> or smart pointer named
 1912: <literal>file</literal> to a newly created instance of the
 1913: <literal>nsILocalFile</literal> component. Using the
 1914: <literal>NS_FAILED</literal> and <literal>NS_SUCCEEDED</literal>
 1915: macros, we test for the <literal>nsresult</literal> to see if our
 1916: attempt to create an instance of the component failed. If it did,
 1917: <literal>rv</literal> would be assigned an integer with a specific
 1918: error return code. Return codes are defined in
 1919: <filename>nsError.h</filename>. Alternatively, you can test your
 1920: results for the success code:
 1921: </para>
 1922: 
 1923: <programlisting>nsresult rv = nsComponentManager::CreateInstance("@mozilla.org/file/local;1", 
 1924:                                      nsnull,
 1925:                                      NS_GET_IID(nsILocalFile), 
 1926:                                      (void **)&amp;refp);</programlisting>
 1927: 
 1928: <para>If a result is successful, the value of <literal>rv</literal> returns
 1929: <literal>NS_OK</literal>, which is 0.
 1930: </para>
 1931: 
 1932: <para>Return codes are used in XPCOM instead of exceptions. Exceptions are
 1933: not allowed because of their inconsistent implementation across
 1934: different compilers. All error code numbers equate to a specific type
 1935: of error. For example <literal>NS_ERROR_FAILURE</literal> and
 1936: <literal>NS_ERROR_NULL_POINTER</literal> are common types of error
 1937: code return values used throughout the Mozilla code base. If a value
 1938: returned to <literal>rv</literal> was
 1939: <literal>NS_ERROR_NULL_POINTER</literal>, the test for failure would
 1940: be true and the code would return the numerical result code for
 1941: <literal>NS_ERROR_NULL_POINTER</literal>.
 1942: </para>
 1943: 
 1944: </sect3>
 1945: 
 1946: <sect3 role="" id="mozilla-CHP-8-SECT-2.4.3" label="8.2.4.3">
 1947: <title>The nsnull type</title>
 1948: 
 1949: <para>Another widely use type is <literal>nsnull</literal>,
 1950: <indexterm id="IXT-8-1716"><primary>nsnull type, C++</primary></indexterm>
 1951: <indexterm id="IXT-8-1717"><primary>C++</primary><secondary>nsnull
 1952: type</secondary></indexterm>defined in <filename>nscore.h</filename>.
 1953: Here is the definition:
 1954: </para>
 1955: 
 1956: <programlisting>#define nsnull 0</programlisting>
 1957: 
 1958: <para>This definition, <literal>nsnull</literal>, is the most common way to
 1959: use <literal>null</literal>. The following code shows how to use
 1960: <literal>nsnull</literal>:
 1961: </para>
 1962: 
 1963: <programlisting>  nsresult rv;
 1964:   nsCOMPtr&lt;nsILocalFile&gt; file =
 1965:   do_CreateInstance("@mozilla.org/file/local;1", &amp;rv);
 1966:   if (NS_SUCCEEDED(rv)) {
 1967:     char* msg = "we successfully created an instance of file\n";
 1968:     *_retval = (char*) nsMemory::Alloc(PL_strlen(msg) + 1);
 1969:     if (!*_retval)
 1970:       return NS_ERROR_OUT_OF_MEMORY;
 1971:     PL_strcpy(*_retval, msg);
 1972:   } else {
 1973:     *_retval = nsnull;
 1974:   }</programlisting>
 1975: 
 1976: </sect3>
 1977: 
 1978: <sect3 role="" id="mozilla-CHP-8-SECT-2.4.4" label="8.2.4.4">
 1979: <title>The NS_IMETHODIMP macro</title>
 1980: 
 1981: <para>If you look in the Mozilla C++ <indexterm id="IXT-8-1718"><primary>NS_IMETHODIMP
 1982: macro, C++</primary></indexterm>
 1983: <indexterm id="IXT-8-1719"><primary>C++</primary><secondary>NS_IMETHODIMP</secondary></indexterm>source
 1984: code, you will see the macro <literal>NS_IMETHODIMP</literal> used
 1985: frequently. This macro identifies the type of your interface
 1986: implementation method. It is also defined in
 1987: <filename>nscore.h,</filename> as shown in <link linkend="mozilla-CHP-8-EX-7">Example 8-7</link>. 
 1988: </para>
 1989: 
 1990: <example id="mozilla-CHP-8-EX-7" label="8-7">
 1991: <title>Platform macros in xpcom/base/nscore.h </title>
 1992: <programlisting>#define NS_IMETHODIMP NS_IMETHODIMP_(nsresult)
 1993: #ifdef NS_WIN32
 1994:   #define NS_IMETHODIMP_(type) type _ _stdcall
 1995: #elif defined(XP_MAC)
 1996:   #define NS_IMETHODIMP_(type) type
 1997: #elif defined(XP_OS2)
 1998:   #define NS_IMETHODIMP_(type) type
 1999: #else
 2000:   #define NS_IMETHODIMP_(type) type
 2001: #endif</programlisting>
 2002: </example>
 2003: 
 2004: <para><link linkend="mozilla-CHP-8-EX-8">Example 8-8</link> shows a typical use of the
 2005: <literal>NS_IMETHODIMP</literal> macro. All methods that implement an
 2006: interface are of the type <literal>NS_IMETHODIMP</literal>.
 2007: </para>
 2008: 
 2009: <example id="mozilla-CHP-8-EX-8" label="8-8">
 2010: <title>NS_IMETHOD macro </title>
 2011: <programlisting>NS_IMETHODIMP
 2012: nsMyImpl::GetSomeString(char** _retval)
 2013: {
 2014:   nsresult rv;
 2015:   nsCOMPtr&lt;nsILocalFile&gt; file =
 2016:   do_CreateInstance("@mozilla.org/file/local;1", &amp;rv);
 2017:   if (NS_SUCCEEDED(rv)) {
 2018:     char* msg = "we successfully created an instance of file\n";
 2019:     *_retval = (char*) nsMemory::Alloc(PL_strlen(msg) + 1);
 2020:     if (!*_retval)
 2021:       return NS_ERROR_OUT_OF_MEMORY;
 2022:     PL_strcpy(*_retval, msg);
 2023:   } else {
 2024:     *_retval = nsnull;
 2025:   } 
 2026:   return NS_OK;
 2027: }</programlisting>
 2028: </example>
 2029: 
 2030: <para>The macro in <link linkend="mozilla-CHP-8-EX-8">Example 8-8</link> declares the method
 2031: <literal>GetSomeString</literal> as an XPCOM implementation.
 2032: </para>
 2033: 
 2034: </sect3>
 2035: 
 2036: <sect3 role="" id="mozilla-CHP-8-SECT-2.4.5" label="8.2.4.5">
 2037: <title>nsCOMPtr smart pointer</title>
 2038: 
 2039: <para>As described earlier, XPCOM provides <indexterm id="IXT-8-1720"><primary>nsCOMPtr
 2040: smart pointer, C++</primary></indexterm>
 2041: <indexterm id="IXT-8-1721"><primary>C++</primary><secondary>nsCOMPtr smart
 2042: pointer</secondary></indexterm>a C++ tool called a smart pointer to
 2043: manage reference counting. A smart pointer is a template class that
 2044: acts syntactically, just like an ordinary pointer in C or C++. You
 2045: can apply <literal>*</literal> to dereference the pointer,
 2046: <literal>-&gt;</literal>, or access what the pointer refers to.
 2047: Unlike a raw COM interface pointer, however,
 2048: <literal>nsCOMPtr</literal> manages <literal>AddRef</literal>,
 2049: <literal>Release</literal>, and <literal>QueryInterface</literal> for
 2050: you, thereby preventing memory leaks.
 2051: </para>
 2052: 
 2053: <para>Here is how to create a raw pointer:</para>
 2054: 
 2055: <programlisting>nsILocalFile *refp(nsnull);
 2056: nsresult rv = nsComponentManager::CreateInstance("@mozilla.org/file/local;1", 
 2057:    nsnull,
 2058:   NS_GET_IID(nsILocalFile), 
 2059:   (void **)&amp;refp);
 2060: if (refp)
 2061:   printf("%p\n", (void*)refp);</programlisting>
 2062: 
 2063: <para>After you create a new object that <literal>refp</literal> points to,
 2064: <literal>refp</literal> is considered an owning reference, and any
 2065: other pointers that point to it must be
 2066: "refcounted." <link linkend="mozilla-CHP-8-EX-9">Example 8-9</link> uses <literal>anotherPtr</literal>
 2067: and<filename> </filename><literal>oneMorePtr</literal> to point to
 2068: <literal>refp</literal>, and manually manages
 2069: <literal>AddRef</literal> and <literal>Release</literal>.
 2070: </para>
 2071: 
 2072: <example id="mozilla-CHP-8-EX-9" label="8-9">
 2073: <title>Manual reference counting using raw pointers </title>
 2074: <programlisting>nsILocalFile *refp(nsnull);
 2075: nsresult rv = nsComponentManager::CreateInstance("@mozilla.org/file/local;1", 
 2076:    nsnull,
 2077:    NS_GET_IID(nsILocalFile), 
 2078:    (void **)&amp;refp);
 2079: nsILocalFile *anotherPtr = refp;
 2080: NS_IF_ADDREF(anotherPtr); // increment refcount
 2081: nsILocalFile *oneMorePtr = refp;
 2082: NS_IF_ADDREF(oneMorePtr); // increment refcount
 2083: if (!someCondition) {
 2084:   NS_RELEASE(anotherPtr); // decrement refcount
 2085:   return NS_OK;
 2086: }
 2087: . . .
 2088: NS_RELEASE(anotherPtr); // decrement refcount
 2089: NS_RELEASE(oneMorePtr); // decrement refcount
 2090: return NS_OK;
 2091: }</programlisting>
 2092: </example>
 2093: 
 2094: <para>In <link linkend="mozilla-CHP-8-EX-9">Example 8-9</link>, if <literal>someCondition</literal>
 2095: is false, <literal>anotherPtr</literal> is released and the function
 2096: then returns (<literal>NS_OK</literal>). But what about
 2097: <literal>oneMorePtr</literal>? In this instance, it is never
 2098: released; if you remember, an object cannot be released from memory
 2099: until our <literal>refcount</literal> is at zero. The
 2100: <literal>refcount</literal> is out of sync,
 2101: <literal>oneMorePtr</literal> is never decremented before the return,
 2102: and the object is thus left dangling in memory. With the
 2103: <literal>refcount</literal> off, the object leaks. Remember that
 2104: <literal>Release( )</literal> calls the C++ <literal>delete</literal>
 2105: operator to free up the allocated XPCOM object only when the count is
 2106: decremented to 0. If <literal>Release</literal> thinks there are
 2107: still references to the object because the
 2108: <literal>refcount</literal> hasn't been properly
 2109: decremented, <literal>delete</literal> is never called. The correct
 2110: code is shown below:
 2111: </para>
 2112: 
 2113: <programlisting>if (!someCondition) {
 2114:   NS_RELEASE(anotherPtr); // decrement refcount
 2115:   NS_RELEASE(oneMorePtr); // decrement refcount
 2116:   return NS_OK;
 2117: }</programlisting>
 2118: 
 2119: <para>As you can see, manual management of reference counting is prone to
 2120: error. To alleviate this burden and extra code bloat,
 2121: <literal>nsCOMPtr</literal> implements <literal>AddRef</literal> and
 2122: <literal>Release</literal> for you and makes life much easier. Before
 2123: the <literal>nsCOMPtr</literal> class is removed from the stack, it
 2124: calls <literal>Release</literal> in its destructor. After all
 2125: references are properly released, <literal>delete</literal> is called
 2126: and the object is freed from memory. <link linkend="mozilla-CHP-8-EX-10">Example 8-10</link>
 2127: shows a typical use of <literal>nsCOMPtr</literal>.
 2128: </para>
 2129: 
 2130: <example id="mozilla-CHP-8-EX-10" label="8-10">
 2131: <title>Using nsCOMPtr in your code </title>
 2132: <programlisting>nsCOMPtr&lt;nsILocalFile&gt; refp = do_CreateInstance("@mozilla.org/file/local;1");
 2133: nsCOMPtr&lt;nsILocalFile&gt; anotherPtr = refp;
 2134: nsCOMPtr&lt;nsILocalFile&gt; oneMorePtr = refp;
 2135: nsresult rv;
 2136: if (!someCondition) 
 2137:   return NS_OK;
 2138: . . .
 2139: //no need to release here because nsCOMPtr smart pointer's destructor 
 2140: // will call release automatically and the above references will be    
 2141: // properly decremented.
 2142: return NS_OK;</programlisting>
 2143: </example>
 2144: 
 2145: <para>Wherever the code returns, all pointers holding references to the
 2146: <literal>nsLocalFile</literal> XPCOM object are released
 2147: automatically in the <literal>nsCOMPtr</literal> class destructor
 2148: before the instructions are removed from the stack. By letting
 2149: <literal>nsCOMPtr</literal> manage <literal>AddRef</literal> and
 2150: <literal>Release</literal> for you, you remove a margin for error,
 2151: code complexity, and bloat.
 2152: </para>
 2153: 
 2154: </sect3>
 2155: </sect2>
 2156: <sect2 role="" id="mozilla-CHP-8-SECT-2.5" label="8.2.5">
 2157: <title>C++ Implementation of nsISimple</title>
 2158: 
 2159: <para>Now that you have seen some of <indexterm id="IXT-8-1722"><primary>nsISimple
 2160: implementation, C++</primary></indexterm>
 2161: <indexterm id="IXT-8-1723"><primary>C++</primary><secondary>nsISimple
 2162: implementation</secondary></indexterm>the C++ tools you need for
 2163: XPCOM, you can turn to an actual implementation.
 2164: </para>
 2165: 
 2166: <para>Earlier in this chapter, the section <link linkend="mozilla-CHP-8-SECT-2.1">Section 8.2.1</link> showed you how to create an
 2167: interface and implement it in JavaScript. However, you may need a C++
 2168: implementation to benefit from the better performance offered by a
 2169: compiled language.
 2170: </para>
 2171: 
 2172: <para>Most components used in Mozilla are written in C++. This section
 2173: discusses how to create a C++ implementation for the
 2174: <literal>nsISimple</literal> interface. A few more steps are
 2175: involved, but as you will see, they are generally similar to the
 2176: processes described in the JavaScript component section, facilitated
 2177: to some extent by the available tools and templates discussed
 2178: previously.
 2179: </para>
 2180: 
 2181: <sect3 role="" id="mozilla-CHP-8-SECT-2.5.1" label="8.2.5.1">
 2182: <title>Creating a C++ component</title>
 2183: 
 2184: <para>First, you must find a good place
 2185: <indexterm id="IXT-8-1724"><primary>components</primary><secondary>C++,
 2186: creating</secondary></indexterm>
 2187: <indexterm id="IXT-8-1725"><primary>C++</primary><secondary>components,
 2188: creation</secondary></indexterm>to put the source file you create for
 2189: the component. In your local Mozilla source tree,
 2190: <filename>mozilla/xpcom/sample/</filename> is a great place to start
 2191: because it's the directory in which the sample XPCOM
 2192: interface and implementations already reside.
 2193: </para>
 2194: 
 2195: <para>First, create a new directory and call it <emphasis>simple</emphasis>:</para>
 2196: 
 2197: <programlisting>$ mkdir simple 
 2198: $ cd simple</programlisting>
 2199: 
 2200: <para>You can place the <literal>nsISimple</literal> interface you created
 2201: earlier in this new directory as a file called
 2202: <emphasis>nsISimple.idl</emphasis>:
 2203: </para>
 2204: 
 2205: <programlisting>#include "nsISupports.idl"
 2206: [scriptable, uuid(ce32e3ff-36f8-425f-94be-d85b26e634ee)]
 2207: interface nsISimple : nsISupports
 2208: {
 2209:     attribute string yourName;
 2210:     void write( );
 2211:     void change(in string aName);
 2212: };</programlisting>
 2213: 
 2214: <para>Once you have the interface source file in which the attribute
 2215: <literal>yourName</literal> and the methods <literal>write(
 2216: )</literal> and <literal>change( )</literal> are defined, you can
 2217: create a header file for the implementation source file.
 2218: </para>
 2219: 
 2220: </sect3>
 2221: 
 2222: <sect3 role="" id="mozilla-CHP-8-SECT-2.5.2" label="8.2.5.2">
 2223: <title>nsISimple C++ header file</title>
 2224: 
 2225: <para>Earlier, you created the type <indexterm id="IXT-8-1726"><primary>nsISimple header
 2226: file, C++</primary></indexterm>
 2227: <indexterm id="IXT-8-1727"><primary>C++</primary><secondary>nsISimple header
 2228: file</secondary></indexterm>library
 2229: <filename>nsISimple.xpt</filename> for the JavaScript component and
 2230: installed it in the components subdirectory. Since
 2231: we've already covered those steps, we can move
 2232: forward to generating a C++ header file. To create a C++ header file
 2233: from your original IDL, run your IDL file through the
 2234: <filename>xpidl</filename> compiler:
 2235: </para>
 2236: 
 2237: <programlisting>$ xpidl -m header -w -v -I $XPIDL_INC \
 2238: &gt; -o nsISimple nsISimple.idl</programlisting>
 2239: 
 2240: <para>The generated file is <filename>nsISimple.h</filename> and is shown
 2241: in <link linkend="mozilla-CHP-8-EX-11">Example 8-11</link>.
 2242: </para>
 2243: 
 2244: <example id="mozilla-CHP-8-EX-11" label="8-11">
 2245: <title>nsISimple header file generated by xpidl compiler </title>
 2246: <programlisting>/*
 2247:  * DO NOT EDIT.  THIS FILE IS GENERATED FROM nsISimple.idl
 2248:  */
 2249: #ifndef _ _gen_nsISimple_h_ _
 2250: #define _ _gen_nsISimple_h_ _
 2251: #ifndef _ _gen_nsISupports_h_ _
 2252: #include "nsISupports.h"
 2253: #endif
 2254: /* For IDL files that don't want to include root IDL files. */
 2255: #ifndef NS_NO_VTABLE
 2256: #define NS_NO_VTABLE
 2257: #endif
 2258: /* starting interface:    nsISimple */
 2259: #define NS_ISIMPLE_IID_STR "ce32e3ff-36f8-425f-94be-d85b26e634ee"
 2260: #define NS_ISIMPLE_IID \
 2261:   {0xce32e3ff, 0x36f8, 0x425f, \
 2262:     { 0x94, 0xbe, 0xd8, 0x5b, 0x26, 0xe6, 0x34, 0xee }}
 2263: class NS_NO_VTABLE nsISimple : public nsISupports {
 2264:  public: 
 2265:   NS_DEFINE_STATIC_IID_ACCESSOR(NS_ISIMPLE_IID)
 2266:   /* attribute string yourName; */
 2267:   NS_IMETHOD GetYourName(char * *aYourName) = 0;
 2268:   NS_IMETHOD SetYourName(const char * aYourName) = 0;
 2269:   /* void write ( ); */
 2270:   NS_IMETHOD Write(void) = 0;
 2271:   /* void change (in string aName); */
 2272:   NS_IMETHOD Change(const char *aName) = 0;
 2273: };
 2274: /* Use this macro when declaring classes that implement this interface. */
 2275: #define NS_DECL_NSISIMPLE \
 2276:   NS_IMETHOD GetYourName(char * *aYourName); \
 2277:   NS_IMETHOD SetYourName(const char * aYourName); \
 2278:   NS_IMETHOD Write(void); \
 2279:   NS_IMETHOD Change(const char *aName); 
 2280: /* Use this macro to declare functions that forward the behavior of this interface to another object. */
 2281: #define NS_FORWARD_NSISIMPLE(_to) \
 2282:   NS_IMETHOD GetYourName(char * *aYourName) { return _to ## GetYourName(aYourName); } \
 2283:   NS_IMETHOD SetYourName(const char * aYourName) { return _to ## SetYourName(aYourName); } \
 2284:   NS_IMETHOD Write(void) { return _to ## Write( ); } \
 2285:   NS_IMETHOD Change(const char *aName) { return _to ## Change(aName); } 
 2286: /* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */
 2287: #define NS_FORWARD_SAFE_NSISIMPLE(_to) \
 2288:   NS_IMETHOD GetYourName(char * *aYourName) { return !_to ## ? NS_ERROR_NULL_POINTER : _to ##-&gt;GetYourName(aYourName); } \
 2289:   NS_IMETHOD SetYourName(const char * aYourName) { return !_to ## ? NS_ERROR_NULL_POINTER : _to ##-&gt;SetYourName(aYourName); } \
 2290:   NS_IMETHOD Write(void) { return !_to ## ? NS_ERROR_NULL_POINTER : _to ##-&gt; Write( ); } \
 2291:   NS_IMETHOD Change(const char *aName) { return !_to ## ? NS_ERROR_NULL_POINTER : _to ##-&gt; Change(aName); } 
 2292: #if 0
 2293: /* Use the code below as a template for the implementation class for this interface. */
 2294: /* Header file */
 2295: class nsSimple : public nsISimple
 2296: {
 2297: public:
 2298:   NS_DECL_ISUPPORTS
 2299:   NS_DECL_NSISIMPLE
 2300:   nsSimple( );
 2301:   virtual ~nsSimple( );
 2302:   /* additional members */
 2303: };
 2304: /* Implementation file */
 2305: NS_IMPL_ISUPPORTS1(nsSimple, nsISimple)
 2306: nsSimple::nsSimple( )
 2307: {
 2308:   NS_INIT_ISUPPORTS( );
 2309:   /* member initializers and constructor code */
 2310: }
 2311: nsSimple::~nsSimple( )
 2312: {
 2313:   /* destructor code */
 2314: }
 2315: /* attribute string yourName; */
 2316: NS_IMETHODIMP nsSimple::GetYourName(char * *aYourName)
 2317: {
 2318:     return NS_ERROR_NOT_IMPLEMENTED;
 2319: }
 2320: NS_IMETHODIMP nsSimple::SetYourName(const char * aYourName)
 2321: {
 2322:     return NS_ERROR_NOT_IMPLEMENTED;
 2323: }
 2324: /* void write ( ); */
 2325: NS_IMETHODIMP nsSimple::Write( )
 2326: {
 2327:     return NS_ERROR_NOT_IMPLEMENTED;
 2328: }
 2329: /* void change (in string aName); */
 2330: NS_IMETHODIMP nsSimple::Change(const char *aName)
 2331: {
 2332:     return NS_ERROR_NOT_IMPLEMENTED;
 2333: }
 2334: /* End of implementation class template. */
 2335: #endif
 2336: #endif /* _ _gen_nsISimple_h_ _ */</programlisting>
 2337: </example>
 2338: 
 2339: <para>As you can see, the <filename>xpidl</filename> compiler can do a lot
 2340: of work for you. The code generated in <link linkend="mozilla-CHP-8-EX-11">Example 8-11</link>
 2341: is a C++ header file that declares the methods of
 2342: <literal>nsISimple</literal>. It provides the class definition,
 2343: macros for using the interface, and a template for the class
 2344: implementation, which contains stubbed-out declaratory code that you
 2345: can paste into your implementation file to quickly get started.
 2346: </para>
 2347: 
 2348: </sect3>
 2349: 
 2350: <sect3 role="" id="mozilla-CHP-8-SECT-2.5.3" label="8.2.5.3">
 2351: <title>Creating the implementation file</title>
 2352: 
 2353: <para>The implementation file actually <indexterm id="IXT-8-1728"><primary>implementation
 2354: file creation, C++</primary></indexterm>
 2355: <indexterm id="IXT-8-1729"><primary>C++</primary><secondary>implementation file
 2356: creation</secondary></indexterm>contains the C++ code that implements
 2357: the member functions and properties declared in your interface. For
 2358: <literal>nsISimple</literal>, these members are the
 2359: <literal>yourName</literal> attribute and the <literal>write(
 2360: )</literal> and <literal>change( )</literal> methods.
 2361: </para>
 2362: 
 2363: <para>First you need to generate a new UUID for the new implementation
 2364: class you'll write. Every XPCOM implementation class
 2365: must have its own UUID:
 2366: </para>
 2367: 
 2368: <programlisting>$ uuidgen
 2369: 79e9424f-2c4d-4cae-a762-31b334079252</programlisting>
 2370: 
 2371: <para>As part of the generated file <filename>nsISimple.h</filename>, all
 2372: the code stubs you need to get started are ready to be copied and
 2373: pasted into the C++ source files. You can use those stubs as a guide
 2374: to implement the component. In a text editor, create a new file
 2375: called <filename>nsSimple.h</filename> and enter the code shown in
 2376: <link linkend="mozilla-CHP-8-EX-12">Example 8-12</link>.
 2377: </para>
 2378: 
 2379: <para>To maintain clarity, the C++ implementation class is named
 2380: <literal>nsSimpleImpl</literal>, where the default class name
 2381: generated by the <filename>xpidl</filename> compiler is
 2382: <literal>nsSimple</literal> and the header file,
 2383: <filename>nsSimple.h</filename>, is shown in <link linkend="mozilla-CHP-8-EX-12">Example 8-12</link>. 
 2384: </para>
 2385: 
 2386: <example id="mozilla-CHP-8-EX-12" label="8-12">
 2387: <title>The component header file nsSimple.h </title>
 2388: <programlisting>#include "nsISimple.h"
 2389: // 79e9424f-2c4d-4cae-a762-31b334079252
 2390: #define NS_SIMPLE_CID \
 2391: { 0x79e9424f, 0x2c4d, 0x4cae, { 0xa7, 0x62, 0x31, 0xb3, 0x34, 0x07, 0x92, 0x52 } }
 2392: #define NS_SIMPLE_CONTRACTID "@mozilla.org/cpp_simple;1"
 2393: class nsSimpleImpl : public nsISimple
 2394: {
 2395: public:
 2396:     nsSimpleImpl( );
 2397:     virtual ~nsSimpleImpl( );
 2398:     // nsISupports interface
 2399:     NS_DECL_ISUPPORTS
 2400:     NS_DECL_NSISIMPLE
 2401: private:
 2402:     char* mName;
 2403: };</programlisting>
 2404: </example>
 2405: 
 2406: <para><link linkend="mozilla-CHP-8-EX-12">Example 8-12</link> includes the ID-generated header file
 2407: <filename>nsISimple.h</filename>, which holds the C++ declarations
 2408: for the interface class <literal>nsISimple</literal>. It then takes
 2409: the new UUID and breaks it into a class ID struct defined as
 2410: <literal>NS_SIMPLE_CID</literal>. Next, it defines the contract ID
 2411: for this implementation class.
 2412: </para>
 2413: 
 2414: <para>The example uses a completely different class ID and contract ID than
 2415: the one used for the JavaScript component because
 2416: it's a different implementation class and needs to
 2417: have it's own unique identification (even though it
 2418: implements the same interface).
 2419: </para>
 2420: 
 2421: <para>Now the example makes the class declaration of the implementation,
 2422: called <literal>nsSimpleImpl</literal>, which inherits from
 2423: <literal>nsISimple</literal>, defining the class constructor and
 2424: virtual destructor. <literal>NS_DECL_ISUPPORTS</literal> is a
 2425: <indexterm id="IXT-8-1730"><primary>NS_DECL_ISUPPORTS macro,
 2426: C++</primary></indexterm>
 2427: <indexterm id="IXT-8-1731"><primary>C++</primary><secondary>NS_DECL_ISUPPORTS
 2428: macro</secondary></indexterm>macro that holds the declaration of our
 2429: required <literal>QueryInterface</literal>,
 2430: <literal>AddRef</literal>, and <literal>Release</literal> methods.
 2431: <literal>NS_DECL_NSISIMPLE</literal> is created in the generated
 2432: header file <filename>nsISimple.h</filename>. It expands to the used
 2433: interface method declarations. Finally <link linkend="mozilla-CHP-8-EX-12">Example 8-12</link>
 2434: shows the addition of the <literal>char*</literal> member variable
 2435: identified as <emphasis>mName</emphasis>. This variable is used to
 2436: hold the value of the interface attribute
 2437: <literal>yourName</literal>, just as it did earlier in the JavaScript
 2438: class implementation.
 2439: </para>
 2440: 
 2441: <para>Once you have the header file, you are ready to start the
 2442: implementation source file. With a text editor, create a new file
 2443: called <filename>nsSimple.cpp</filename>. As in any C++ source file,
 2444: you should add the header files required by the implementation:
 2445: </para>
 2446: 
 2447: <programlisting>#include "plstr.h"
 2448: #include "stdio.h"
 2449: #include "nsCOMPtr.h"
 2450: #include "nsMemory.h"
 2451: #include "nsSimple.h"</programlisting>
 2452: 
 2453: <para>Start by adding the implementation of our class constructor and
 2454: destructor:
 2455: </para>
 2456: 
 2457: <programlisting>// c++ constructor
 2458: nsSimpleImpl::nsSimpleImpl( ) : mName(nsnull)
 2459: {
 2460:     NS_INIT_REFCNT( );
 2461:     mName = PL_strdup("default value");
 2462: }
 2463: // c++ destructor
 2464: nsSimpleImpl::~nsSimpleImpl( )
 2465: {
 2466:     if (mName)
 2467:         PL_strfree(mName);
 2468: }</programlisting>
 2469: 
 2470: <para>Then add the macro <literal>NS_IMPL_ISUPPORTS1_CI</literal>. As
 2471: discussed earlier, this macro conveniently implements
 2472: <literal>QueryInterface</literal>, <literal>AddRef</literal>, and
 2473: <literal>Release</literal>:
 2474: </para>
 2475: 
 2476: <programlisting>NS_IMPL_ISUPPORTS1_CI(nsSimpleImpl, nsISimple);</programlisting>
 2477: 
 2478: <para>Next you are ready to implement the actual
 2479: <literal>nsISimple</literal> interface methods:
 2480: </para>
 2481: 
 2482: <programlisting>NS_IMETHODIMP
 2483: nsSimpleImpl::GetYourName(char** aName)
 2484: {
 2485:     NS_PRECONDITION(aName != nsnull, "null ptr");
 2486:     if (!aName)
 2487:         return NS_ERROR_NULL_POINTER;
 2488:     if (mName) {
 2489:         *aName = (char*) nsMemory::Alloc(PL_strlen(mName) + 1);
 2490:         if (! *aName)
 2491:             return NS_ERROR_NULL_POINTER;
 2492:         PL_strcpy(*aName, mName);
 2493:     }
 2494:     else {
 2495:         *aName = nsnull;
 2496:     }
 2497:     return NS_OK;
 2498: <userinput>}</userinput></programlisting>
 2499: 
 2500: <para>A C++ implementation of an IDL method is <indexterm id="IXT-8-1732"><primary>IDL
 2501: (Interface Definition Language),</primary><secondary>method
 2502: implementation, C++</secondary></indexterm>declared as the type
 2503: <literal>NS_IMETHODIMP</literal>. The implementation starts with the
 2504: getter method <literal>GetYourName</literal>, which takes a
 2505: <literal>char**</literal> parameter for the method's
 2506: return value. Return values in C++ XPCOM components are marshaled via
 2507: method arguments because interface implementations must always return
 2508: a numerical <literal>nsresult</literal>, as described earlier. To
 2509: ensure that the <literal>aName</literal> parameter is a pointer, use
 2510: the macro <literal>NS_PRECONDITION</literal> to
 2511: <indexterm id="IXT-8-1733"><primary>NS_PRECONDITION macro, C++</primary></indexterm>
 2512: <indexterm id="IXT-8-1734"><primary>C++</primary><secondary>NS_PRECONDITION
 2513: macro</secondary></indexterm>warn if null, follow with a null test in
 2514: the line below, and return the error result code
 2515: <literal>NS_ERROR_NULL_POINTER</literal>. Then test whether the
 2516: member variable <emphasis>mName</emphasis> holds a value. If it does,
 2517: allocate the necessary memory to accommodate the size of the copy.
 2518: Then by using <literal>PL_strcpy</literal>, you can assign the value
 2519: to the parameter <literal>aName</literal>. Otherwise,
 2520: <emphasis>mName</emphasis> is null and you can assign null into
 2521: <literal>aName</literal> and return:
 2522: </para>
 2523: 
 2524: <programlisting>NS_IMETHODIMP
 2525: nsSimpleImpl::SetYourName(const char* aName)
 2526: {
 2527:    NS_PRECONDITION(aName != nsnull, "null ptr");
 2528:     if (!aName)
 2529:         return NS_ERROR_NULL_POINTER;
 2530:     if (mName) {
 2531:         PL_strfree(mName);
 2532:     }
 2533:     mName = PL_strdup(aName);
 2534:     return NS_OK;
 2535: }</programlisting>
 2536: 
 2537: <para>After implementing the getter, implement the setter. Again, use
 2538: <literal>NS_PRECONDITION</literal> and then a null test on the
 2539: <literal>aName</literal>. If that parameter holds data, you can free
 2540: it by using <literal>PL_strfree</literal> and calling
 2541: <literal>PL_strdup</literal>. Then assign the new value to class
 2542: member <literal>mName</literal>:
 2543: </para>
 2544: 
 2545: <programlisting>NS_IMETHODIMP
 2546: nsSimpleImpl::Write( )
 2547: {
 2548:   printf("%s\n", mName);
 2549:   return NS_OK;
 2550: }
 2551: NS_IMETHODIMP
 2552: nsSimpleImpl::Change(const char* aName)
 2553: {
 2554:   return SetYourName(aName);
 2555: <userinput>}</userinput></programlisting>
 2556: 
 2557: <para>Finally, implement the <literal>Write</literal> and
 2558: <literal>Change</literal> methods by using <literal>printf</literal>
 2559: to write the value of <literal>mName</literal> to
 2560: <literal>stdout</literal> and set a new value to
 2561: <literal>mName</literal>. <link linkend="mozilla-CHP-8-EX-13">Example 8-13</link> shows the C++
 2562: source code in its entirety.
 2563: </para>
 2564: 
 2565: <example id="mozilla-CHP-8-EX-13" label="8-13">
 2566: <title>nsSimple.cpp </title>
 2567: <programlisting>#include "plstr.h"
 2568: #include "stdio.h"
 2569: #include "nsSimple.h"
 2570: #include "nsCOMPtr.h"
 2571: #include "nsMemory.h"
 2572: // c++ constructor
 2573: nsSimpleImpl::nsSimpleImpl( ) : mName(nsnull)
 2574: {
 2575:     // NS_INIT_REFCNT( ); // has been depricated use NS_INIT_ISUPPORTS() 
 2576:     NS_INIT_ISUPPORTS(); 
 2577:     mValue = PL_strdup("default value");
 2578: }
 2579: // c++ destructor
 2580: nsSimpleImpl::~nsSimpleImpl( )
 2581: {
 2582:     if ( )
 2583:         PL_strfree( );
 2584: }
 2585: // This macro implements the nsISupports interface methods
 2586: // QueryInterface, AddRef and Release
 2587: NS_IMPL_ISUPPORTS1_CI(nsSimpleImpl, nsISimple);
 2588: NS_IMETHODIMP
 2589: nsSimpleImpl::GetYourName(char** aName)
 2590: {
 2591:     NS_PRECONDITION(aName != nsnull, "null ptr");
 2592:     if (!aName)
 2593:         return NS_ERROR_NULL_POINTER;
 2594:     if ( ) {
 2595:         *aName = (char*) nsMemory::Alloc(PL_strlen( ) + 1);
 2596:         if (! *aName)
 2597:             return NS_ERROR_NULL_POINTER;
 2598:         PL_strcpy(*aName, );
 2599:     }
 2600:     else {
 2601:         *aName = nsnull;
 2602:     }
 2603:     return NS_OK;
 2604: }
 2605: NS_IMETHODIMP
 2606: nsSimpleImpl::SetYourName(const char* aName)
 2607: {
 2608:    NS_PRECONDITION(aName != nsnull, "null ptr");
 2609:     if (!aName)
 2610:         return NS_ERROR_NULL_POINTER;
 2611:     if ( ) {
 2612:         PL_strfree( );
 2613:     }
 2614:      = PL_strdup(aName);
 2615:     return NS_OK;
 2616: }
 2617: NS_IMETHODIMP
 2618: nsSimpleImpl::Write( )
 2619: {
 2620:   printf("%s\n", );
 2621:   return NS_OK;
 2622: }
 2623: NS_IMETHODIMP
 2624: nsSimpleImpl::Change(const char* aName)
 2625: {
 2626:   return SetYourName(aName);
 2627: }</programlisting>
 2628: </example>
 2629: 
 2630: </sect3>
 2631: </sect2>
 2632: <sect2 role="" id="mozilla-CHP-8-SECT-2.6" label="8.2.6">
 2633: <title>The nsSimple module code</title>
 2634: 
 2635: <para>As you needed to do with the JavaScript implementation,
 2636: <indexterm id="IXT-8-1735"><primary>nsSimple module code</primary></indexterm>you
 2637: must create the code for the module. The module code abstracts the
 2638: implementation class and makes the implementation a component
 2639: library. In your text editor, create
 2640: <indexterm id="IXT-8-1736"><primary>nsSimpleModule.cpp file</primary></indexterm>a
 2641: file called <emphasis>nsSimpleModule.cpp</emphasis> and enter the
 2642: code shown in <link linkend="mozilla-CHP-8-EX-14">Example 8-14</link>.
 2643: </para>
 2644: 
 2645: <example id="mozilla-CHP-8-EX-14" label="8-14">
 2646: <title>nsSimpleModule.cpp </title>
 2647: <programlisting>#include "nsIGenericFactory.h"
 2648: #include "nsSimple.h"
 2649: NS_GENERIC_FACTORY_CONSTRUCTOR(nsSimpleImpl)
 2650: static NS_METHOD nsSimpleRegistrationProc(nsIComponentManager *aCompMgr,
 2651:                                           nsIFile *aPath,
 2652:                                           const char *registryLocation,
 2653:                                           const char *componentType,
 2654:                                           const nsModuleComponentInfo *info)
 2655: {
 2656:     return NS_OK;
 2657: }
 2658: static NS_METHOD nsSimpleUnregistrationProc(nsIComponentManager *aCompMgr,
 2659:                                             nsIFile *aPath,
 2660:                                             const char *registryLocation,
 2661:                                             const nsModuleComponentInfo *info)
 2662: {
 2663:     return NS_OK;
 2664: }
 2665: // For each class that wishes to support nsIClassInfo, add a line like this
 2666: NS_DECL_CLASSINFO(nsSimpleImpl)
 2667: static nsModuleComponentInfo components[ ] =
 2668: {
 2669:   { "A Simple Component",    // a message to display when component is loaded
 2670:     NS_SIMPLE_CID,           // our UUID 
 2671:     NS_SIMPLE_CONTRACTID,    // our human readable PROGID or CLSID
 2672:     nsSimpleImplConstructor,
 2673:     nsSimpleRegistrationProc      /* NULL if you dont need one */,
 2674:     nsSimpleUnregistrationProc    /* NULL if you dont need one */,
 2675:     NULL /* no factory destructor */,
 2676:     NS_CI_INTERFACE_GETTER_NAME(nsSimpleImpl),
 2677:     NULL /* no language helper */,
 2678:     &amp;NS_CLASSINFO_NAME(nsSimpleImpl)
 2679:   }
 2680: };
 2681: NS_IMPL_NSGETMODULE(nsSimpleModule, components)</programlisting>
 2682: </example>
 2683: 
 2684: <sect3 role="" id="mozilla-CHP-8-SECT-2.6.1" label="8.2.6.1">
 2685: <title>The final steps for a C++ component</title>
 2686: 
 2687: <para>Once you have an interface file <filename>nsISimple.idl</filename>, a
 2688: C++ source file <filename>nsSimple.cpp</filename> with its header
 2689: file <filename>nsSimple.h</filename>, and a module file
 2690: <filename>nsSimpleModule.cpp</filename>, you can create a Makefile
 2691: like the one shown in <link linkend="mozilla-CHP-8-EX-15">Example 8-15</link>. This Makefile can
 2692: compile the sources into an XPCOM component.
 2693: </para>
 2694: 
 2695: <para>A Makefile directs the Mozilla build system to build the sources and
 2696: install them into the Mozilla
 2697: <filename>dist/bin/components</filename> directory. To use the
 2698: Makefile, run gmake to compile and install the component library
 2699: file.
 2700: </para>
 2701: 
 2702: <example id="mozilla-CHP-8-EX-15" label="8-15">
 2703: <title>Sample Makefile </title>
 2704: <programlisting>DEPTH         = ../../..
 2705: topsrcdir     = ../../..
 2706: srcdir        = .
 2707: VPATH         = .
 2708: 
 2709: include $(DEPTH)/config/autoconf.mk
 2710: 
 2711: MODULE        = xpcom
 2712: XPIDL_MODULE  = simple
 2713: LIBRARY_NAME  = simple
 2714: IS_COMPONENT  = 1
 2715: MODULE_NAME   = nsSimpleModule
 2716: REQUIRES      = string \
 2717:                 xpcom \
 2718:                 $(NULL)
 2719: 
 2720: CPPSRCS   =              \
 2721:     nsSimple.cpp         \
 2722:     nsSimpleModule.cpp   \
 2723:     $(NULL)
 2724: 
 2725: XPIDLSRCS = nsISimple.idl
 2726: 
 2727: include $(topsrcdir)/config/config.mk
 2728: 
 2729: LIBS    +=        \
 2730:     $(XPCOM_LIBS) \
 2731:     $(NSPR_LIBS)  \
 2732:     $(NULL)
 2733: 
 2734: include $(topsrcdir)/config/rules.mk
 2735: 
 2736: EXTRA_DSO_LDOPTS += $(MOZ_COMPONENT_LIBS)
 2737: 
 2738: install:: $(TARGETS)</programlisting>
 2739: </example>
 2740: 
 2741: <para>To test the newly compiled component, you can use
 2742: <filename>xpcshell</filename> like you did for the JavaScript
 2743: component. <link linkend="mozilla-CHP-8-EX-16">Example 8-16</link> shows a session with
 2744: <filename>xpcshell</filename> that tests the new component.
 2745: </para>
 2746: 
 2747: <example id="mozilla-CHP-8-EX-16" label="8-16">
 2748: <title>Sample use of component in xpcshell </title>
 2749: <programlisting>$ ./run-mozilla.sh ./xpcshell 
 2750: Type Manifest File: /usr/src/commit_mozilla/mozilla/dist/bin/components/xpti.dat
 2751: nsNativeComponentLoader: autoregistering begins.
 2752: *** Registering nsSimpleModule components (all right -- a generic module!)
 2753: nsNativeComponentLoader: autoregistering succeeded
 2754: nNCL: registering deferred (0)
 2755: js&gt; var Simple = new Components.Constructor("@mozilla.org/cpp_simple;1", "nsISimple");
 2756: js&gt; var s = new Simple( );
 2757: js&gt; s.yourName;
 2758: default value
 2759: js&gt; s.write( );
 2760: default value
 2761: js&gt; s.change('pete');
 2762: js&gt; s.yourName;
 2763: pete
 2764: js&gt; s.yourName = 'brian';
 2765: brian
 2766: js&gt;</programlisting>
 2767: </example>
 2768: 
 2769: </sect3>
 2770: 
 2771: <sect3 role="" id="mozilla-CHP-8-SECT-2.6.2" label="8.2.6.2">
 2772: <title>Creating an instance of an existing Mozilla component</title>
 2773: 
 2774: <para>Creating an instance
 2775: <indexterm id="IXT-8-1737"><primary>components</primary><secondary>instances</secondary></indexterm>
 2776: <indexterm id="IXT-8-1738"><primary>instances, components</primary></indexterm>of a
 2777: component and accessing methods and attributes is different in C++
 2778: than it is in JavaScript. Using the <emphasis>nsILocalFile</emphasis>
 2779: interface lets you walk through the code to create an instance of
 2780: this component from C++:
 2781: </para>
 2782: 
 2783: <programlisting>nsCOMPtr&lt;nsILocalFile&gt;
 2784: file(do_CreateInstance("@mozilla.org/file/local;1"));</programlisting>
 2785: 
 2786: <para>You can also instantiate the object as follows:</para>
 2787: 
 2788: <programlisting>nsresult rv; 
 2789: nsCOMPtr&lt;nsILocalFile&gt; file =
 2790:     do_CreateInstance("@mozilla.org/file/local;1", &amp;rv); 
 2791: if (NS_FAILED(rv))
 2792:   return rv;</programlisting>
 2793: 
 2794: <para>Both techniques assign an <literal>nsCOMPtr</literal> to a newly
 2795: allocated instance of an <literal>nsLocalFile</literal> object.
 2796: </para>
 2797: 
 2798: <para><link linkend="mozilla-CHP-8-EX-17">Example 8-17</link> accesses the public methods available
 2799: from this component by using the pointer identifier
 2800: <literal>file</literal>.
 2801: </para>
 2802: 
 2803: <example id="mozilla-CHP-8-EX-17" label="8-17">
 2804: <title>Example 8-17: Testing for nsresults from component methods </title>
 2805: <programlisting>if (file) {
 2806:   nsresult rv;
 2807:   rv = file-&gt;InitWithPath(NS_LITERAL_STRING("/tmp"));
 2808:   if (NS_FAILED(rv))
 2809:     return rv; 
 2810:   PRBool exists;
 2811:   rv = file-&gt;Exists(&amp;exists);
 2812:   if (NS_FAILED(rv))
 2813:     return rv; 
 2814:   if (exists)
 2815:     print("yep it exists!\n");
 2816:   nsAutoString leafName; 
 2817:   rv = file-&gt;GetLeafName(leafName);
 2818:   if (NS_FAILED(rv))
 2819:     return rv; 
 2820:   if (!leafName.IsEmpty( ))
 2821:     printf("leaf name is %s\n", NS_ConvertUCS2toUTF8(leafName).get( ));
 2822: }</programlisting>
 2823: </example>
 2824: 
 2825: <para>Always test accessors of all XPCOM public methods, getters, and
 2826: setters. Failures can appear at any time, so be sure to use result
 2827: checking in your implementations.
 2828: </para>
 2829: 
 2830: </sect3>
 2831: </sect2>
 2832: <sect2 role="" id="mozilla-CHP-8-SECT-2.7" label="8.2.7">
 2833: <title>Other Languages for XPCOM</title>
 2834: 
 2835: <para>Although most components available from XPCOM are written in C++, the
 2836: XPConnect/XPCOM pairing can also accommodate other languages.
 2837: Language independence is a goal of the XPCOM architecture. Currently,
 2838: implementations for Python (PyXPCOM) and Ruby (rbXPCOM) exist, with
 2839: other language bindings being developed. In this respect, the Mozilla
 2840: framework dovetails with one of the main trends in application
 2841: development, which is to mix different languages in the development
 2842: environment.
 2843: </para>
 2844: 
 2845: <sect3 role="" id="mozilla-CHP-8-SECT-2.7.1" label="8.2.7.1">
 2846: <title>PyXPCOM: the Python binding for XPCOM</title>
 2847: 
 2848: <para>Python has emerged as a very
 2849: <indexterm id="IXT-8-1739"><primary>XPCOM</primary><secondary>pyXPCOM</secondary></indexterm>
 2850: <indexterm id="IXT-8-1740"><primary>pyXPCOM</primary></indexterm>popular programming
 2851: language in the last couple of years. It even does some of the
 2852: application work and other heavy lifting that were the province of
 2853: C++. Mozilla now offers a Python
 2854: "binding" similar to the XPConnect
 2855: binding for JavaScript that allows you to write application code in
 2856: Python, compile it in XPCOM, and make it available like you would any
 2857: C++ component in the Mozilla application framework. As with other
 2858: XPCOM programming languages, you must create an implementation file
 2859: (in Python) and an interface file (in IDL), as shown in Examples 8-18
 2860: and 8-19, respectively.
 2861: </para>
 2862: 
 2863: <para>The terms and constructs for Python components are similar to those
 2864: of C++. In the implementation, you need to import components from the
 2865: XPCOM module to access the standard public members. The syntax is the
 2866: same as that for importing any regular Python library:
 2867: </para>
 2868: 
 2869: <programlisting>from xpcom import components</programlisting>
 2870: 
 2871: <para>The IDL for a Python implementation of an XPCOM component can be
 2872: identical to one for a JavaScript- or C++-based component (which is
 2873: the point of XPCOM, after all). As in any component, your IDL needs
 2874: to include <emphasis>nsISupports.idl</emphasis> and declare itself as
 2875: scriptable with a unique UUID:
 2876: </para>
 2877: 
 2878: <programlisting>[scriptable, uuid(6D9F47DE-ADC1-4a8e-8E7D-2F7B037239BF)]</programlisting>
 2879: 
 2880: <para>JavaScript accesses the component in the same way, using classes and
 2881: interface members of the component's interfaces to
 2882: set up an instance of the component:
 2883: </para>
 2884: 
 2885: <programlisting>Components.classes["@foo.com/appSysUtils;1"].
 2886:     getService(Components.interfaces.appISysUtils);</programlisting>
 2887: 
 2888: <para>With these foundations, and assuming that you have to have a Python
 2889: distribution on your system that Mozilla can access, you are ready to
 2890: go! <link linkend="mozilla-CHP-8-EX-18">Example 8-18</link> shows a complete implementation of a
 2891: PyXPCOM component. This file needs to be saved with a
 2892: <filename>.py</filename> extension and put in the
 2893: <emphasis>components</emphasis> directory and registered like any
 2894: other component.
 2895: </para>
 2896: 
 2897: <example id="mozilla-CHP-8-EX-18" label="8-18">
 2898: <title>Sample Python component implementation </title>
 2899: <programlisting>import sys, os
 2900: from xpcom import components, nsError, ServerException
 2901: class appSysUtils:
 2902:     _com_interfaces_ = [components.interfaces.appISysUtils]
 2903:     _reg_clsid_ = "{56F686E0-A989-4714-A5D6-D77BC850C5C0}"
 2904:     _reg_contractid_ = "@foo.com/appSysUtils;1"
 2905:     _reg_desc_ = "System Utilities Service"
 2906:     def _ _init_ _(self):
 2907:         self.F_OK = os.F_OK
 2908:         self.R_OK = os.R_OK
 2909:         self.W_OK = os.W_OK
 2910:         self.X_OK = os.X_OK
 2911:     # ...
 2912:     def Access(self, filename, mode):
 2913:         return os.access(filename, mode)</programlisting>
 2914: </example>
 2915: 
 2916: <para>The special attributes defined in
 2917: <indexterm id="IXT-8-1741"><primary>XPCOM</primary><secondary>attributes,
 2918: Python</secondary></indexterm><indexterm id="IXT-8-1742"><primary>attributes</primary><secondary>XPCOM,
 2919: Python</secondary></indexterm><indexterm id="IXT-8-1743"><primary>Python</primary><secondary>XPCOM
 2920: attributes</secondary></indexterm>the <literal>appSysUtils</literal>
 2921: class correspond to the special identifiers you must use in XPCOM to
 2922: make your code a reusable component (see <link linkend="mozilla-CHP-8-SECT-1.5">Section 8.1.5</link>, earlier in this chapter).
 2923: <link linkend="mozilla-CHP-8-TABLE-3">Table 8-3</link> describes these attributes.
 2924: </para>
 2925: 
 2926: <table id="mozilla-CHP-8-TABLE-3" label="8-3">
 2927: 
 2928: <title>Special XPCOM attributes in Python </title>
 2929: <tgroup cols="2">
 2930: <colspec colnum="1" colname="col1"/>
 2931: <colspec colnum="2" colname="col2"/>
 2932: <thead>
 2933: <row>
 2934: <entry>
 2935: <para>Attribute</para>
 2936: </entry>
 2937: <entry>
 2938: <para>Description</para>
 2939: </entry>
 2940: </row>
 2941: </thead>
 2942: <tbody>
 2943: <row>
 2944: <entry>
 2945: <programlisting>_com_interfaces_</programlisting>
 2946: </entry>
 2947: <entry>
 2948: <para>The interface IDs supported by this component. This attribute is
 2949: required. It can be a single IID or a list, but you do not have to
 2950: list base interfaces such as <emphasis>nsISupports</emphasis>.
 2951: </para>
 2952: </entry>
 2953: </row>
 2954: <row>
 2955: <entry>
 2956: <programlisting>_reg_contractid_</programlisting>
 2957: </entry>
 2958: <entry>
 2959: <para>The component's contract ID. Required.</para>
 2960: </entry>
 2961: </row>
 2962: <row>
 2963: <entry>
 2964: <programlisting>_reg_clsid_</programlisting>
 2965: </entry>
 2966: <entry>
 2967: <para>The Class ID (CLSID) or progID of the component in the form:
 2968: @domain/component;version.Required.
 2969: </para>
 2970: </entry>
 2971: </row>
 2972: <row>
 2973: <entry>
 2974: <programlisting>_reg_desc_</programlisting>
 2975: </entry>
 2976: <entry>
 2977: <para>A description of the component. Optional.</para>
 2978: </entry>
 2979: </row>
 2980: </tbody>
 2981: </tgroup>
 2982: </table>
 2983: 
 2984: <para><link linkend="mozilla-CHP-8-EX-19">Example 8-19</link> is the IDL file you also need to create
 2985: a Python component.
 2986: </para>
 2987: 
 2988: <example id="mozilla-CHP-8-EX-19" label="8-19">
 2989: <title>IDL for the Python component </title>
 2990: <programlisting>#include "nsISupports.idl"
 2991: // some useful system utilities
 2992: [scriptable, uuid(6D9F47DE-ADC1-4a8e-8E7D-2F7B037239BF)]
 2993: interface appSysUtils : nsISupports {
 2994:     boolean IsFile(in string filename);
 2995:     boolean IsDir(in string dirname);
 2996:     void Stat(in string filename,
 2997:               out PRUint32 st_mode,
 2998:               out PRUint32 st_ino,
 2999:               out PRUint32 st_dev,
 3000:               out PRUint32 st_nlink,
 3001:               out PRUint32 st_uid,
 3002:               out PRUint32 st_gid,
 3003:               out PRUint32 st_size,
 3004:               out PRUint32 st_atime,
 3005:               out PRUint32 st_mtime,
 3006:               out PRUint32 st_ctime);
 3007:     boolean Access(in string filename, in PRUint32 mode);
 3008:     readonly attribute PRUint32 F_OK;
 3009:     readonly attribute PRUint32 R_OK;
 3010:     readonly attribute PRUint32 W_OK;
 3011:     readonly attribute PRUint32 X_OK;
 3012: };</programlisting>
 3013: </example>
 3014: 
 3015: <para>Finally, <link linkend="mozilla-CHP-8-EX-20">Example 8-20</link> shows how this component might
 3016: be used in script -- for example, in a function you define for an
 3017: event handler in the XUL interface.
 3018: </para>
 3019: 
 3020: <example id="mozilla-CHP-8-EX-20" label="8-20">
 3021: <title>Using the Python component in script </title>
 3022: <programlisting>var appSysUtils = Components.classes["@foo.com/appSysUtils;1"].getService(Components
 3023:         interfaces.appISysUtils);
 3024: // Read-write status
 3025: var write = appSysUtils.Access(url, appSysUtils.W_OK);
 3026: var read = appSysUtils.Access(url, appSysUtils.R_OK);
 3027: var rwcheck = document.getElementById('rwCheckbox');
 3028: if (read)  {
 3029:     if (write &amp;&amp; read)
 3030:         ro = false;
 3031:     else 
 3032:         ro = true;
 3033:     rwcheck.setAttribute('checked', ro);
 3034: }</programlisting>
 3035: </example>
 3036: 
 3037: <para>The component is a small system utility that checks the read/write
 3038: permissions status of a file on the local filesystem. The JavaScript
 3039: uses it to display a visual notifier of the status in the UI. In this
 3040: case, the DOM's <literal>rwcheck</literal> node
 3041: refers to a checkbox. It's easy to imagine this
 3042: component being extended to do other things, such as getting
 3043: information about a file (the <literal>Stat</literal> stub is in the
 3044: IDL). The source code, samples, and documentation for PyXPCOM are
 3045: located in the Mozilla tree at
 3046: <filename>mozilla/extensions/python</filename>.
 3047: </para>
 3048: 
 3049: </sect3>
 3050: </sect2>
 3051: <sect2 role="" id="mozilla-CHP-8-SECT-2.8" label="8.2.8">
 3052: <title>XPCOM as an Open Cross-Platform Solution</title>
 3053: 
 3054: <para>XPCOM can be an entire book
 3055: <indexterm id="IXT-8-1744"><primary>XPCOM</primary><secondary>as open cross-platform
 3056: solution</secondary></indexterm><indexterm id="IXT-8-1745"><primary>MSCOM</primary></indexterm>in
 3057: itself. This chapter has merely touched upon the role it plays in
 3058: Mozilla application development. Understanding the basics of this
 3059: framework is vital to understanding the very foundation of
 3060: Mozilla's componentized architecture.
 3061: </para>
 3062: 
 3063: <para>Although other component-based systems exist on various
 3064: platforms -- MSCOM for Microsoft or a CORBA system for GNOME, for
 3065: example -- if you want to write truly cross-platform
 3066: component-based applications, then XPCOM is the best tool for the
 3067: job. It can be deployed on any platform Mozilla is ported to, and can
 3068: be scripted by using JavaScript or Python.
 3069: </para>
 3070: 
 3071: <para>Above all, XPCOM is entirely open source, so there are no costs
 3072: associated with it, no proprietary secrets in how
 3073: it's put together, and you have various software
 3074: licenses to choose from. Although XPCOM has become a solid framework,
 3075: its developers are still making improvements and uncovering and
 3076: fixing bugs. However, XPCOM offers tremendous flexibility as a
 3077: software development framework and the Mozilla community is an
 3078: excellent technical support resource for all technologies covered in
 3079: this book.
 3080: </para>
 3081: 
 3082: 
 3083: 
 3084: </sect2>
 3085: </sect1>
 3086: </chapter>

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>