Creating Applications with Mozilla-Chapter 8. XPCOM- P2

Chia sẻ: Thanh Cong | Ngày: | Loại File: PDF | Số trang:18

lượt xem

Creating Applications with Mozilla-Chapter 8. XPCOM- P2

Mô tả tài liệu
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

Tham khảo tài liệu 'creating applications with mozilla-chapter 8. xpcom- p2', công nghệ thông tin, kỹ thuật lập trình phục vụ nhu cầu học tập, nghiên cứu và làm việc hiệu quả

Chủ đề:

Nội dung Text: Creating Applications with Mozilla-Chapter 8. XPCOM- P2

  1. Chapter 8. XPCOM- P2 Root interfaces QueryInterface, Addref, and Release are required methods that are implemented by every component. QueryInterface matches a specific interface with its implementation class module. Addref and Release are methods used for reference counting. When an instance of a component is created, one or more pointers may reference that object. For each reference, a count is incremented by one. When a reference is no longer used, Release is called to decrement the count. You must hold a reference count to ensure that no pointers reference an object after it is deleted. When pointers try to access objects that are deleted, the application core dumps. Reference counting can get tricky, which is why smart pointers manage Addref and Release for you, as described in the later section Section 8.2.4. Defining QueryInterface, Addref, and Release every time an interface is created is clearly not very efficient. Instead, these methods are defined in the base interface called nsISupports. All interfaces inherit from this mother of all interfaces and don't need to redefine these three basic functions. XPIDL supports the C style syntax preparser directive #include to include other IDL files -- not unlike MSCOM, which uses the import statement. At the top of any IDL file that you create, you need to include nsISupports: #include "nsISupports.idl"
  2. interface nsISimple : nsISupports { readonly attribute string value; }; Core IDL Types The core types used in IDL interface files are listed in the file xpcom/base/nsrootidl.idl. This file is included in nsISupports. (This means it is included in every interface file, since all interfaces inherit from nsISupports.) All interfaces used in a system are valid IDL types. For example, nsISimple is a valid type to use as a method parameter or a method return type. Some of the main types listed in this interface are: typedef boolean PRBool; typedef octet PRUint8; typedef unsigned short PRUint16; typedef unsigned short PRUnichar; typedef unsigned long PRUint32; typedef unsigned long long PRUint64; typedef unsigned long long PRTime; typedef short PRInt16; typedef long PRInt32; typedef long long PRInt64; typedef unsigned long nsrefcnt;
  3. typedef unsigned long nsresult; typedef unsigned long size_t; The XPIDL compiler An IDL compiler is a tool that creates a binary distribution file called a type library from an interface description source file. Since support for many different platforms is a requirement for Mozilla, a modified version of the libIDL compiler from the Gnome project is used. This variant is called the XPIDL compiler and is primarily used to compile Mozilla's own dialect of IDL, conveniently called XPIDL. The XPIDL compiler generates XPCOM interface information, headers for XPCOM objects, and XPT type libraries from which objects may be accessed dynamically through XPConnect. It can also generate HTML files for documentation and Java class stubs. Another feature of the XPIDL compiler is the option to generate C++ code stubs. This feature creates nearly all the declaratory C++ code you need when you start a new project, which makes XPIDL useful as a coding wizard that helps you get started. Code generation is covered later in this chapter in the section Section 8.2.5. The XPIDL compiler is located in xpcom/typelib/xpidl/ in the Mozilla sources. If you built Mozilla, you can add this directory to your PATH: $ PATH=$PATH:/usr/src/mozilla/xpcom/typelib/xpidl Using the compiler is fairly easy. If you use the help command, you can see the usage syntax and other basic information about the compiler: $ ./xpidl --help
  4. Usage: xpidl [-m mode] [-w] [-v] [-I path] [-o basename] filename.idl -a emit annotations to typelib -w turn on warnings (recommended) -v verbose mode (NYI) -I add entry to start of include path for ``#include "nsIThing.idl"'' -o use basename (e.g. ``/tmp/nsIThing'') for output -m specify output mode: header Generate C++ header (.h) typelib Generate XPConnect typelib (.xpt) doc Generate HTML documentation (.html) java Generate Java interface (.java) 8.1.4. XPCOM Type Libraries The key to the component architecture of XPCOM is the presence of binary- independent interface files that are used uniformly across platforms, languages, and programming environments. These interface files are compiled into .xpt files by the XPIDL compiler. The Mozilla components subdirectory is where type libraries and modules are
  5. typically stored. If you create a cross-platform type library for your component, you must place it in this directory for it to be accessible to XPCOM. Creating a type library file from an IDL interface To create a (.xpt) typelib file, use the flag -m typelib with warning (- w) and verbose (-v) modes turned on. -o is used for the name of the output file and -I is used to specify paths to other IDL files you want to include. To successfully compile your interface, you must always point to the directory where nsISupports is located. # include path to nsISupports.idl $ $XPIDL_INC = /usr/src/mozilla/xpcom/base #compile nsISimple.idl $ xpidl -m typelib -w -v -I $XPIDL_INC \ > -o nsISimple nsISimple.idl The file created after compilation is nsISimple.xpt. It provides the necessary type information about your interface at runtime. Typelib files enumerate the methods of interfaces and provide detailed type information for each method parameter. 8.1.5. XPCOM Identifiers To simplify the process of dynamically finding, loading, and binding interfaces, all classes and interfaces are assigned IDs. An ID is a unique 128- bit number that is based on universally unique identifiers (UUIDs) generated by various tools such as uuidgen (which we will cover later in this chapter). They are stored in the structure format defined below:
  6. struct nsID { PRUint32 m0; PRUint16 m1, m2; PRUint8 m3[8]; }; To initialize an ID struct, declare it like this: ID = {0x221ffe10, 0xae3c, 0x11d1, {0xb6, 0x6c, 0x00, 0x80, 0x5f, 0x8a, 0x26, 0x76}}; One thing that gives XPCOM its modularity is the dynamic allocation of objects through the use of unique identifiers at runtime. This system of canonical identifiers is used for interface querying and component instantiation. Having an interface is important because it ensures that an immutable binary holds a semantic contract defined for a specific interface class. The two types of identifiers used in XPCOM are the contract ID and the class identifier. These identifiers are shuttled to the Component Manager's createInstance( ) or the Service Manager's getService( ) methods in order to instantiate a component. The Contract ID The program ID (progID), also known as the Contract ID, is a unique human-readable string. Example 8-2 shows various progIDs for different components. This example can be used to instantiate an XPCOM component through the use of a Contract ID.
  7. Example 8-2. progIDs // progID:;1 var f = Components.classes[`;1']; // progID:;1 var bmks = Components.classes[" service;1"]. getService(Components.interfaces.nsIBookmarksServic e); The class identifier The other type of identifier is the classID, or CLSID. The interface and implementation are identified by this 128-bit numerical identifier string: // clsid: {2e23e220-60be-11d3-8c4a-000064657374} var f = Components.classesByID["{2e23e220-60be- 11d3-8c4a-000064657374}"]; Using XPConnect, XPCOM interfaces, classIDs, and progIDs are stored as global JavaScript objects and properties and can be manipulated directly through the top-level Components object discussed earlier. Generating identifiers
  8. To obtain a UUID on Unix, you can use a command-line program called uuidgen that generates a unique number for you: $ uuidgen ce32e3ff-36f8-425f-94be-d85b26e634ee On Windows, a program called guidgen.exe does the same thing and also provides a graphical user interface if you'd rather point and click. Or you can use one of the special "bots" on IRC at the server. irc /join #mozilla /msg mozbot uuid This command makes the bot generate and return a uuid, which you can then copy into your component source code. The information can then be used to uniquely identify your component. 8.1.6. Component Manager One major goal of XPCOM modularization is the removal of link-time dependencies, or dependencies that arise when you link libraries during compilation. The achievement of this goal allows you to access and use modules at runtime. The trouble then becomes finding those modules and figuring out which of their interfaces you want to use. This problem is solved through the use of the Component Manager. The Component Manager is a special set of component management classes and implementation classes that reside in object libraries (.dll, .so, .js, .py, etc.). These classes also include factories, which let you create objects without having access to their class declarations. When you bind to objects
  9. at runtime, as you do in XPCOM, you need functionality like this to help you discover and use objects without looking at their code. The Component Manager also includes the Component Manager class itself, known as nsComponentManager, which is a mapping of class IDs to factories for the libraries they contain. The Component Manager is responsible for the autoregistration of all new or add-on modules located in the components directory. This autoregistration happens behind the scenes and allows you to use new components as they become available without having to register them yourself. A component author first creates an interface file that defines all APIs that will be publicly available for a component. The component author then creates an implementation for the methods and attributes in a separate implementation class. For example, an nsILocalFile interface may have an nsLocalFile implementation class. Then a factory or module is needed to abstract the implementation class, and reduce compile and link-time dependencies. It then creates instances of the implementation class through its own implementation of QueryInterface. For example: // create an instance of the implementation class var f = Components.classes[`;1'].cre ateInstance( ); The variable f is assigned an instance of a nsLocalFile implementation class using the nsIFactory method createInstance( ). To match the correct interface (nsILocalFile in this case) to the implementation, you need
  10. a class instance to be created before you can call on its member method QueryInterface( ): // QI for nsILocalFile interface var f = f.QueryInterface(Components.interfaces.nsILocalFile ); Once you do this, the variable f is ready to use the nsILocalFile interface to access the newly created instance of the nsLocalFile class from script. Simply put, a factory or module is a set of classes used by the Component Manager to register and create an instance of the component's implementation class. A factory can make its way into the Mozilla component repository in several ways. The most direct is through using the Component Manager method RegisterFactory( ), which supports two different registration mechanisms. The first mechanism, which takes a class ID and a pointer to a factory, can be used on factories that are actually linked into the executable. The second, which takes a class ID and the path to a dynamically loadable library, can be used both inside an executable at runtime and externally by using the aPersist flag to tell the repository to store the class ID/library relationship in its permanent store. The Component Manager discovers new factories or modules placed in the components directory and queries those modules for the XPCOM components they provide. The name, contract IDs, and class IDs are placed into a small component registry database for quick retrieval. The factory provides this information through a simple set of APIs required by every XPCOM module. Module creation, covered later in this chapter, describes the process
  11. through which all components contain an implementation of a module or factory. 8.1.7. Getting and Using XPCOM Mozilla is a client application that implements XPCOM, so everything you need to use or build new XPCOM components is already included in the source code and/or the binaries. Whenever you use the JavaScript Components object, as described earlier, you use XPCOM. If you'd rather not build the entire Mozilla browser and you have no interest in existing Mozilla components or the large footprint that comes with an entire distribution, then standalone XPCOM is for you. To pull the XPCOM source on Unix using Mac OS X or cygwin on Windows, invoke the following commands: cvs -z 3 co mozilla/ cd mozilla gmake -f pull_all BUILD_MODULES=xpcom To build the XPCOM Stand Alone version, type: configure --enable-modules=xpcom gmake When you build standalone XPCOM, the directory xpcom/sample contains the source code for a sample application and a nsTestSample component. The sample application built from these sources, also called nsTestSample, is installed in the Mozilla bin directory. (Unix), which is the component that the sample application tries to instantiate, should have been installed in
  12. bin/components. To run the test that indicates whether standalone XPCOM is installed successfully, change to the mozilla/dist/bin directory and run the following commands: ./ ./nsTestSample You should see the following output. If you do not, there is a problem with the installation of standalone XPCOM: Type Manifest File: /D/STAND_ALONE_XPCOM/mozilla/dist/bin/components/xp ti.dat nsNativeComponentLoader: autoregistering begins. nsNativeComponentLoader: autoregistering succeeded nNCL: registering deferred (0) Inital print: initial value Set value to: XPCOM defies gravity Final print : XPCOM defies gravity Test passed. Using standalone XPCOM is a powerful way to use the Mozilla framework of cross-platform COM. Even if you're just hacking on Mozilla, standalone XPCOM is a great way to learn about and use XPCOM for application development. 8.2. Creating XPCOM Components As we mentioned, one advantage of using XPCOM is that it separates the implementation from the interface so you can write a component in a language-agnostic manner. The services your component provides are
  13. available to all other components despite the language used to implement it. This means, for example, that you can use JavaScript not only to access the services of an XPCOM component, but also to create those services. As described in Chapter 5, using JavaScript as a modularized application programming language provides the deepest level of scripting in Mozilla. In your Mozilla build or distribution, you will find a subdirectory named components. Inside this directory, you will see many compiled components. You will also see a number of JavaScript components. If you look at the source of these components, you can get an idea of how a JavaScript component is created. For example, look at the files nsFilePicker.js and nsSidebar.js. These JavaScript components are used in the Mozilla distribution. JavaScript XPCOM components have the advantage over regular scripts of being fast, reusable, and globally accessible to any caller. They also have the advantage over C++-based XPCOM components of being easier to write and maintain. The next few sections describe the creation of a JavaScript-based XPCOM component. If you would rather do your work in C++, then skip to the C++ implementation section in this chapter. 8.2.1. Creating a JavaScript XPCOM Component To create a JavaScript component, you need to create an IDL interface source file and a JavaScript implementation source file. In the Mozilla sources, naming source files with an ns prefix is common practice, so the implementation file should be called something like nsSimple.js. The interface source file, or IDL file, uses a similar convention: it is typical for
  14. interfaces to begin with nsI, using an I to distinguish them as interfaces rather than implementations. Call the IDL source file nsISimple.idl. In addition to these two source files (nsSimple.js and nsISimple.idl), you will compile a cross platform binary interface file, or type library, with the XPIDL compiler, calling it nsISimple.xpt. This .xpt file tells Mozilla that the interface is available and scriptable. You can use it on any platform that Mozilla supports. In other words, you can pick up nsISimple.xpt, which may have been compiled on Unix, drop it into Windows or Mac OS, and use it. All .xpt interface files for Mozilla live in the components directory located in mozilla/dist/bin if you are developing with the Mozilla source code. Otherwise, for binary distributions of Mozilla, they are located in mozilla/components. Mozilla checks this directory upon start up, looking for any new components to register automatically. The XPIDL interface source file Usually, the first step in creating a new component is writing the interface. To begin, open up your favorite text editor and create a new file called nsISimple.idl. The complete source code for the nsISimple.idl interface file is: #include "nsISupports.idl" [scriptable, uuid(ce32e3ff-36f8-425f-94be- d85b26e634ee)] interface nsISimple : nsISupports {
  15. attribute string yourName; void write( ); void change(in string aValue); }; The #include line above includes the file nsISupports.idl, which defines this interface's base class. The [scriptable, uuid..] line declares the interface scriptable and assigns a UUID to the interface. You can use the UUID provided, but creating your own using one of the UUID generation tools described earlier is usually better. The third line, next to the interface keyword, declares the interface's name, nsISimple, and says that it derives from nsISupports. Various attributes and methods are defined within the definition of the nsISimple interface. Attributes are properties of interface objects. They may be read-only or read/write variables. In nsISimple, an attribute called yourName is of the type string. In this implementation, you may get and set this attribute's value. Of the methods defined in this interface, the write( ) method takes no arguments and the change( ) method takes an argument of type string called aValue. The parameter aValue will be a new value that replaces the current value held by yourName. The complete interface IDL is: #include "nsISupports.idl" [scriptable, uuid(ce32e3ff-36f8-425f-94be- d85b26e634ee)] interface nsISimple : nsISupports
  16. { attribute string yourName; void write( ); void change(in string aValue); }; JavaScript implementation file Once you have created an interface file that publicly defines the component's methods and attributes, the next step is to implement those methods and attributes in a separate source file. The listings below walk through the implementation of nsISimple step by step. First, you must declare an empty function called SimpleComponent, which is a standard constructor for a JavaScript object prototype. It's a good idea to name the component in a way that clearly describes both the component and the interface, as SimpleComponent does (i.e., SimpleComponent is an implementation of the nsISimple interface): function SimpleComponent( ) {} With the function declared, we start defining the JavaScript class prototype. SimpleComponent.prototype = { mName : "a default value", In the prototype, we first create a member variable called mName that will be the string placeholder for the IDL attribute yourName. The variable assigns the string a default value. Remember to place commas after all definitions in a prototype. IDL attributes are always implemented as getter
  17. functions. Methods marked with [noscript] will not be available for use with scripting languages. Next we implement the functions below for our definition of attribute string yourName in our file nsISimple.idl. get yourName( ) { return this.mName; }, set yourName(aName) { return this.mName = aName; }, When someone calls an IDL attribute in an interface, getters and setters are used to get or set values for the attribute: simple.yourName='foo'; Or similarly read values from the attribute: var foo = simple.yourName; We first call on the setter function to set a value to the attribute yourName and then use the getter function to obtain the currently set value of yourName. The first function defined in nsISimple is called void write( ). For this method, the implementation can be as simple as the following code: write : function ( ) { dump("Hello " + this.mName + "\n"); }, This example implements the declaration void write( ) by dumping the current value of the variable mName to stdout. The code uses the this keyword to indicate that you are calling to the component's own member variable mName.
  18. The void change( ) method is then implemented as follows: change : function (aValue) { this.mName = aValue; }, change( ) is a method used to change the value variable.
Đồng bộ tài khoản