At the second level of scripting, XPConnect binds JavaScript and the user interface to the application core. Here, JavaScript can access all XPCOM components that implement scriptable libraries and services through a special global object whose methods and properties can be used in JavaScript. Consider these JavaScript snippets from the Mozilla source code:
// add filters to the file picker fp.appendFilters( nsIFilePicker.HTML ); // display a directory in the file picker fp.displayDirectory ( dir ); // read a line from an open file file.readLine(tmpBuf, 1024, didTruncate); // create a new directory this.fileInst.create( DIRECTORY, parseInt(permissions) ); retval=OK;
The filepicker, file, and localfile components that these JavaScript objects represent are a tiny fraction of the components available via XPConnect to programmers in Mozilla. This section describes how to find these components, create the corresponding JavaScript objects, and use them in your application programming.
Until now, scripting has referred to scripting the DOM, manipulating various elements in the interface, and using methods available in Mozilla JavaScript files. However, for real applications like the Mozilla browser itself, this may be only the beginning. The UI must be hooked up to the application code and services (i.e., the application's actual functionality) to be more than just a visual interface. This is where XPConnect and XPCOM come in.
Browsing the Web, reading email, and parsing XML files are examples of application-level services in Mozilla. They are part of Mozilla's lower-level functionality. This functionality is usually written and compiled in platform-native code and typically written in C++. This functionality is also most often organized into modules, which take advantage of Mozilla's cross-platform component object model (XPCOM), and are known as XPCOM components. The relationship of these components and the application services they provide to the interface is shown in Figure 5-4.
In Mozilla, XPConnect is the bridge between JavaScript and XPCOM components. The XPConnect technology wraps natively compiled components with JavaScript objects. XPCOM, Mozilla's own cross-platform component technology, is the framework on top of which these scriptable components are built. Using JavaScript and XPConnect, you can create instances of these components and use their methods and properties as you do any regular JavaScript object, as described here. You can access any or all of the functionality in Mozilla in this way.
Chapter 8 describes more about the XPConnect technology and how it connects components to the interface. It also describes the components themselves and their interfaces, the XPCOM technology, and how you can create your own XPCOM components.
Example 5-10 demonstrates the creation and use of an XPCOM component in JavaScript. In this example, the script instantiates the filepicker object and then uses it to display a file picker dialog with all of the file filters selected. To run this example, add the function to your xfly.js file and call it from an event handler on the "New" menu item you added in Example 3-5.
Note the first two lines in the function and the way they work together to create the fp filepicker object. The first line in the function assigns the name of the nsFilepicker interface to the nsIFilePicker variable in JavaScript. This variable is used in the second line, where the instance is created from the component to specify which interface on that component should be used. Discovering and using library interfaces is an important aspect of XPCOM, where components always implement at least two interfaces.
In Example 5-11, an HTML file (stored locally, since it wouldn't have the required XPConnect access as a remote file because of security boundaries) loaded in Mozilla instantiates a Mozilla sound component and plays a sound with it. Go ahead and try it.
As in Example 5-10, the classes[ ] array on the special Mozilla Components object refers to a particular component -- in this case, the sound component -- by contract ID. All XPCOM objects must have a contract ID that uniquely identifies them with the domain, the component name, and a version number ["@mozilla.org/sound;1"], respectively. See the Section 8.1.5 section in Chapter 8 for more information about this.
Most components are scripted in Mozilla. In fact, the challenge is not to find cases when this scripting occurs (which you can learn by searching LXR for the Components), but to find Mozilla components that don't use scriptable components. Finding components and interfaces in Mozilla and seeing how they are used can be useful when writing your own application.
The Mozilla Component Viewer is a great tool for discovering components and provides a convenient UI for seeing components and looking at their interfaces from within Mozilla. The Component Viewer can be built as an extension to Mozilla (see "cview" in the extensions directory of the Mozilla source), or it can be downloaded and installed as a separate XPI from http://www.hacksrus.com/~ginda/cview/. Appendix B describes the Component Viewer in more detail.
Commonly used XPCOM objects in the browser and other Mozilla applications include file objects, RDF services, URL objects, and category managers.
In all cases, the way to get the object into script is to instantiate it with the special classes object and use the createInstance( ) method on the class to select the interface you want to use. These two steps are often done together, as in the following example, which gets the component with the contract ID ldap-connection;1, instantiates an object from the nsILDAPConnection interface, and then calls a method on that object:
var connection = Components.classes ["@mozilla.org/network/ldap-connection;1"]. createInstance(Components.interfaces.nsILDAPConnection); connection.init(queryURL.host, queryURL.port, null, generateGetTargetsBoundCallback( ));
These two common processes -- getting a component and selecting one of its interfaces to assign to an object -- can also be separated into two different statements:
// get the ldap connection component var connection = Components.classes ["@mozilla.org/network/ldap-connection;1"]; // create an object from the nsILDAPConnection interface; connection.createInstance(Components.interfaces.nsILDAPConnection); // call the init( ) method on that object connection.init(queryURL.host, queryURL.port, null, generateGetTargetsBoundCallback( ));
Mozilla constantly uses these processes. Wherever functionality is organized into XPCOM objects (and most of it is), these two statements bring that functionality into JavaScript as high-level and user-friendly JavaScript objects.