Open main menu

CDOT Wiki β

Changes

Real World Mozilla First XPCOM Component

193 bytes added, 19:14, 27 February 2007
Makefile.in
== Makefile.in ==
The first step in making the build system aware of our component is to generate an input file for autoconf to use during the configure step, which will build our the necessary Makefile automatically.
$ cd mozilla/extensions/firstxpcom
The Makefile.in should contain the following (NOTE: you can read more about what these files actually mean [http://developer.mozilla.org/en/docs/How_Mozilla%27s_build_system_works here]):
DEPTH = ../.. topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = firstxpcom
DIRS = public \
src \
$(NULL)
XPI_NAME = firstxpcom
# A Unique ID for your extension INSTALL_EXTENSION_ID = firstxpcom@senecac.on.ca
# Will create a .xpi in /mozilla/$(MOZ_OBJDIR)/dist/xpi-stage/ XPI_PKGNAME = firstxpcom
# install.rdf will tell Firefox how to install our extension and # this says, "copy install.rdf into our extension dir and xpi" DIST_FILES = install.rdf
include $(topsrcdir)/config/rules.mk
 Note the '''DIRS ''' variable. It says that the two directories, public and src, will be enteredduring the build. We That means we also need Makefile.in files in each of thosethese.
Next we need a Makefile.in public directory
-----------start--------------  DEPTH = ../../.. topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk MODULE = firstxpcom XPIDL_MODULE = firstxpcom XPI_NAME = firstxpcom # The files under EXPORTS are copied directly to # /mozilla/$(MOZ_OBJDIR)/dist/include/myextension/firstxpcom # and are thus accessible from other modules #EXPORTS = \ # myHeader.h \ # $(NULL) XPIDLSRCS = IFirstXpcom.idl include $(topsrcdir)/config/rules.mk ----------end-------------------
Here we tell the build system about our component's name and where it's XPIDL file can be found.
Now a Makefile.in for in the srcdirectory:
-----------start-------------- DEPTH = ../../.. topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk IS_COMPONENT = 1 MODULE = firstxpcom LIBRARY_NAME = firstxpcom XPI_NAME = firstxpcom
DEPTH = . # The REQUIRES section tells make which modules your # components uses.This causes the relevant subdirectories # of /mozilla/$(MOZ_OBJDIR)/dist/..include/to be added to the # C++ compiler's include path.If you're including Mozilla # headers and the compiler isn't finding them, it could well # mean that you haven't listed all of the necessary modules here.topsrcdir REQUIRES = @top_srcdir@xpcom \ string \ $(NULL) # The .cpp source files to be compiledsrcdir CPPSRCS = @srcdir@FirstXpcom.cpp \ $(NULL) VPATH EXTRA_DSO_LDOPTS += @srcdir@\ $(XPCOM_GLUE_LDOPTS) \ $(NSPR_LIBS) \ $(NULL) include $(topsrcdir)/config/rules.mk
include $(DEPTH)/config/autoconf=== install.mkrdf ===
IS_COMPONENT = 1MODULE = firstxpcomLIBRARY_NAME = firstxpcomThe last build-related file we need to write is a file telling Firefox how to install our extension--'''install.rdf''' (see http://developer.mozilla.org/en/docs/Install_Manifests for details).
XPI_NAME <?xml version= "1.0" encoding="UTF-8"?> <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#"> <Description about="urn:mozilla:install-manifest"> <em:id>firstxpcom@senecac.on.ca</em:id> <em:name>firstxpcom</em:name> <em:version>1.0</em:version> <em:creator>David Humphrey</em:creator> <em:description>A Simple XPCOM Extension.</em:description> <em:homepageURL>http://abc.com</em:homepageURL> <em:aboutURL>chrome://firstxpcom/content/about.xul</em:aboutURL> <em:targetApplication> <Description> <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!-- firefox --> <em:minVersion>2.0</em:minVersion> <em:maxVersion>3.0a3pre</em:maxVersion> </Description> </em:targetApplication> </Description> </RDF>
# The REQUIRES section tells make which modules your # components uses. This causes the relevant subdirectories# of /mozilla/$(MOZ_OBJDIR)/dist/include/ to be added to the# C++ compiler's include path. If you're including Mozilla# headers and the compiler isn't finding them, it could well# mean that you haven't listed all of the necessary modules here.REQUIRES = xpcom \ string \ $(NULL)= Building FirstXpcom ==
# The Now we're ready to build our component.cpp source files Add the following line to be compiledCPPSRCS = FirstXpcomyour '''.cpp \ $(NULL)mozconfig''' file in order to include firstxpcom during the build process:
EXTRA_DSO_LDOPTS += \ $(XPCOM_GLUE_LDOPTS) \ $(NSPR_LIBS) \ $(NULL) include $(topsrcdir)/config/rules.mk ----------end------------------- Last, we need to create a file telling Firefox how to install our extension, install.rdf (see http://developer.mozilla.org/en/docs/Install_Manifests for details). -----------start-------------- <?xml version="1.0"?> <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#"> <Description about="urn:mozilla:install-manifest"> <em:id>firstxpcom@senecac.on.ca</em:id> <em:version>0.1</em:version> <em:type>2</em:type>  <em:targetApplication> <!-- Firefox --> <Description> <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <em:minVersion>2.0+</em:minVersion> <em:maxVersion>3.0a3</em:maxVersion> </Description> </em:targetApplication> <!-- front-end metadata --> <em:name>My First XPCOM Extension</em:name> <em:description>Just a simple XPCOM component.</em:description> <em:creator>David Humphrey</em:creator> <em:homepageURL>http://zenit.senecac.on.ca/wiki</em:homepageURL> </Description></RDF> ----------end-------------------  * Now let's build our component Add the following line to your .mozconfig file in order to enable your extension in the build system:  ac_add_options --enable-extensions=default,firstxpcom
Now you can re-build your tree (also known as "rebuilding world") by doing:
$ cd mozilla $ make -f client.mk build
BUT...this takes forever for it to actually reach your code, since it has to traverse the entire tree checking for changes. So, you can bypass the rest of the tree and go directly to your component's build.
$ cd $(objdir) $ ../build/autoconf/make-makefile extensions/firstxpcom
Now call make in $(objdir)/extensions/firstxpcom
$ cd $(objdir)/extensions/firstxpcom $ make
This will create the '''public ''' and '''src ''' directories, as well as generate the Makefiles necessary in each. Go into the public directory and call make again, in order to have your .h and .cpp stubs generated:
$ cd $(objdir)/extensions/firstxpcom/public $ make
If all goes well, you'll see a hundred lines or so of output from make, ending in an error (NOTE: make will error out, since we have no source files yet). We can safely ignore most of it, but a few lines are significant at this point:
<code> IFirstXpcom.idl ../../../dist/bin/xpidl.exe -m header -w -I../../../../extensions/firstxpcom/public -I../../../dist/idl -o _xpidlgen/IFirstXpcom /c/temp/proj/ff-trunk/mozilla/obj-fftrunk/extensions/firstxpcom/public/../../../../extensions/firstxpcom/public/IFirstXpcom.idl Here it is processing our IDL file. As a result of this first partially successful run make, exported and generated header files (i.e.,from our IDL) will be placed in dist/include/firstxpcom. Within $(obdir)/dist/include</firstxpcom/IFirstXpcom.h you'll see a block that begins:code>
#if 0/* Use Here we can see the code below as build processing our IDL file. As a template for the implementation class for result of this interfacefirst (partially) successful run of make, exported and generated header files (i.e. *,from our IDL) will be placed in '''$(objdir)/dist/...include/* End of implementation class templatefirstxpcom'''. */#endif
The code in the middle of this block is what you want to start working with in order to implement your .cpp file Within '''$(and obdir)/dist/include/firstxpcom/IFirstXpcom.h if ''' you choose to split it out, which we won't). Copy this implementation stub into ll see the following fileblock of code:
mozilla #if 0 /extensions* Use the code below as a template for the implementation class for this interface. */firstxpcom ... /FirstXpcom* End of implementation class template.cpp*/ #endif
You'll need The code in the middle of this block is what you want to do a search/replace on _MYCLASS_ use as the basis for implementing your .cpp file (and change .h if you choose to split it to out, which we won't). Copy and paste this implementation stub into the name of your classfollowing file: '''mozilla/extensions/firstxpcom/FirstXpcom.cpp'''
You'll need to do a search/replace on '''_MYCLASS_ > ''' and change it to the name of your class, in our case, '''FirstXpcom'''.
Now you can try and re-run make for your extension:
$ cd $(objdir)/extensions/firstxpcom $ make
This will produce a host of errors, mostly related to the fact that we don't have proper includes setup set-up and it can't find declarations it needs. You need to add an include for your interface's generated .hfile:
#include "IFirstXpcom.h"
Now reRe-run make.
********** TODO ****************
Discuss https://bugzilla.mozilla.org/show_bug.cgi?id=371201 at this point, and problems I had making this work, how luser helped me, and in the end a bug was filed on the build system.
Let's examine the code more closely and try to make sense of things ($(objdir)/dist/include/firstxpcom/== Examining IFirstXpcom.h): -----------start-------------- /* * DO NOT EDIT. THIS FILE IS GENERATED FROM c:/temp/proj/ff-trunk/mozilla/obj-fftrunk/extensions/firstxpcom/public/../../../../extensions/firstxpcom/public/IFirstXpcom.idl */ #ifndef __gen_IFirstXpcom_h__#define __gen_IFirstXpcom_h__  #ifndef __gen_nsISupports_h__#include "nsISupports.h"#endif /* For IDL files that don't want to include root IDL files. */#ifndef NS_NO_VTABLE#define NS_NO_VTABLE#endif /* starting interface: IFirstXpcom */#define IFIRSTXPCOM_IID_STR "78af1749-014a-47aa-baec-2669670b7601" #define IFIRSTXPCOM_IID \ {0x78af1749, 0x014a, 0x47aa, \ { 0xba, 0xec, 0x26, 0x69, 0x67, 0x0b, 0x76, 0x01 }} class NS_NO_VTABLE IFirstXpcom : public nsISupports { public:   NS_DECLARE_STATIC_IID_ACCESSOR(IFIRSTXPCOM_IID)  /* attribute AString name; */ NS_IMETHOD GetName(nsAString & aName) = 0; NS_IMETHOD SetName(const nsAString & aName) = 0;  /* long add (in long a, in long b); */ NS_IMETHOD Add(PRInt32 a, PRInt32 b, PRInt32 *_retval) = 0; };
NS_DEFINE_STATIC_IID_ACCESSORLet's examine the code more closely and try to make sense of things. Here is the code in '''$(objdir)/dist/include/firstxpcom/IFirstXpcom, IFIRSTXPCOM_IID).h''':
/* Use this macro when declaring classes * DO NOT EDIT. THIS FILE IS GENERATED FROM c:/temp/proj/ff-trunk/mozilla/obj-fftrunk/extensions/firstxpcom/public/../../../../extensions/firstxpcom/public/IFirstXpcom.idl */ #ifndef __gen_IFirstXpcom_h__ #define __gen_IFirstXpcom_h__ #ifndef __gen_nsISupports_h__ #include "nsISupports.h" #endif /* For IDL files that implement this interfacedon't want to include root IDL files. */ #ifndef NS_NO_VTABLE #define NS_DECL_IFIRSTXPCOM \NS_NO_VTABLE NS_IMETHOD GetName(nsAString & aName); \ #endif NS_IMETHOD SetName(const nsAString & aName); \ NS_IMETHOD Add(PRInt32 a, PRInt32 b, PRInt32 /* starting interface: IFirstXpcom *_retval); / #define IFIRSTXPCOM_IID_STR "78af1749-014a-47aa-baec-2669670b7601"
/* Use this macro to declare functions that forward the behavior of this interface to another object. */ #define NS_FORWARD_IFIRSTXPCOM(_to) IFIRSTXPCOM_IID \ NS_IMETHOD GetName(nsAString & aName) { return _to GetName(aName); } 0x78af1749, 0x014a, 0x47aa, \ NS_IMETHOD SetName(const nsAString & aName) { return _to SetName(aName); } \ NS_IMETHOD Add(PRInt32 a0xba, 0xec, 0x26, 0x69, PRInt32 b0x67, PRInt32 *_retval) { return _to Add(a0x0b, b0x76, _retval); 0x01 }}
class NS_NO_VTABLE IFirstXpcom : public nsISupports { public: NS_DECLARE_STATIC_IID_ACCESSOR(IFIRSTXPCOM_IID) /* attribute AString name; */ NS_IMETHOD GetName(nsAString & aName) = 0; NS_IMETHOD SetName(const nsAString & aName) = 0; /* long add (in long a, in long b); */ NS_IMETHOD Add(PRInt32 a, PRInt32 b, PRInt32 *_retval) = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(IFirstXpcom, IFIRSTXPCOM_IID) /* Use this macro when declaring classes that implement this interface. */ #define NS_DECL_IFIRSTXPCOM \ NS_IMETHOD GetName(nsAString & aName); \ NS_IMETHOD SetName(const nsAString & aName); \ NS_IMETHOD Add(PRInt32 a, PRInt32 b, PRInt32 *_retval); /* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */ #define NS_FORWARD_SAFE_IFIRSTXPCOMNS_FORWARD_IFIRSTXPCOM(_to) \ NS_IMETHOD GetName(nsAString & aName) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetName(aName); } \ NS_IMETHOD SetName(const nsAString & aName) { return !_to ? NS_ERROR_NULL_POINTER : _to->SetName(aName); } \ NS_IMETHOD Add(PRInt32 a, PRInt32 b, PRInt32 *_retval) { return !_to ? NS_ERROR_NULL_POINTER : _to->Add(a, b, _retval); } #if 0 /* Use this macro to declare functions that forward the code below as a template for the implementation class for behavior of this interfaceto another object in a safe way. */ /* Header file */class _MYCLASS_ : public IFirstXpcom{public: NS_DECL_ISUPPORTS NS_DECL_IFIRSTXPCOM  _MYCLASS_ #define NS_FORWARD_SAFE_IFIRSTXPCOM(_to);\ private: ~_MYCLASS_ NS_IMETHOD GetName(nsAString & aName); protected: /* additional members */}; /* Implementation file */NS_IMPL_ISUPPORTS1(_MYCLASS_, IFirstXpcom) _MYCLASS_::_MYCLASS_(){ /* member initializers and constructor code */} _MYCLASS_::~_MYCLASS_(){ /* destructor code */} /* attribute AString name; */NS_IMETHODIMP _MYCLASS_:return !_to ? NS_ERROR_NULL_POINTER : _to->GetName(nsAString & aName){ return NS_ERROR_NOT_IMPLEMENTED;}\NS_IMETHODIMP _MYCLASS_:: NS_IMETHOD SetName(const nsAString & aName){ return NS_ERROR_NOT_IMPLEMENTED;} /* long add !_to ? NS_ERROR_NULL_POINTER : _to->SetName(in long a, in long baName); */} \NS_IMETHODIMP _MYCLASS_:: NS_IMETHOD Add(PRInt32 a, PRInt32 b, PRInt32 *_retval){ return NS_ERROR_NOT_IMPLEMENTED!_to ? NS_ERROR_NULL_POINTER : _to->Add(a, b, _retval);} /* End of implementation class template. */#endif  #endif /* __gen_IFirstXpcom_h__ */ ----------end-------------------
#if 0
/* Use the code below as a template for the implementation class for this interface. */
/* Header file */
class _MYCLASS_ : public IFirstXpcom
{
public:
NS_DECL_ISUPPORTS
NS_DECL_IFIRSTXPCOM
_MYCLASS_();
private:
~_MYCLASS_();
protected:
/* additional members */
};
/* Implementation file */
NS_IMPL_ISUPPORTS1(_MYCLASS_, IFirstXpcom)
_MYCLASS_::_MYCLASS_()
{
/* member initializers and constructor code */
}
_MYCLASS_::~_MYCLASS_()
{
/* destructor code */
}
/* attribute AString name; */
NS_IMETHODIMP _MYCLASS_::GetName(nsAString & aName)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP _MYCLASS_::SetName(const nsAString & aName)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* long add (in long a, in long b); */
NS_IMETHODIMP _MYCLASS_::Add(PRInt32 a, PRInt32 b, PRInt32 *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* End of implementation class template. */
#endif
#endif /* __gen_IFirstXpcom_h__ */
What things do you notice? What strikes you?
* === Use of header guards===
Header guards (or Include Guards, see [http://en.wikipedia.org/wiki/Include_guardInclude Guards]) are a portable technique for ensuring that includes or defines are only done once. Another way of doing this, especially with the Microsoft compiler, is to use
#pragma once
But this is not portable, and therefore Mozilla doesn't use it.
=== Use of macros ===
* Use of macros Mozilla provides many convenience macros to help C++ developers do common tasks more efficiently. Much of the code involved in making a module/component is generic, and has to be repeated ever every time. You can spot macros in the code by the fact that they are all capitals, and often begin with '''NS_*'''. Examples include:
* NS_DECL_ISUPPORTS* NS_ENSURE_TRUE* NS_IMPL_NSGETMODULE(* see http://developer.mozilla.org/en/docs/Category:XPCOM_Macros for more examples)
NS_DECL_ appended with any interface name in all caps will declare all of the methods of that interface for you. Looking at the code above shows you how this is possible. For example, NS_DECL_NSIFOO will declare all of the methods of nsIFoo , provided that it exists and that nsIFoo.h was generated by the XPIDL compiler. Consider the following real class:
class myClass : public nsISomeClass { public: NS_DECL_ISUPPORTS // declares AddRef, Release, and QueryInterface NS_DECL_NSISOMECLASS // declares all methods of nsISomeClass myClass(); virtual ~myClass() {} };
myClass(); virtual ~myClass() {}};The declaration of nsISomeClass doesn't include any methods other than the constructor and destructor. Instead, the class uses the NS_DECL_ macro
The declaration of nsISomeClass doesn't include any methods other than the contructor and destructor. Instead, the class uses the NS_DECL_ macro Also note the use of NS_METHOD and NS_METHODIMP for return type signatures. All XPCOM functions are required to return a result code ('''nsresult'''), which indicates whether or not the function worked (e.g., NS_OK).
Next there is NS_IMPL_ISUPPORTS1. This macro implements the nsISupports interface for you, specifically the implementation of AddRef, Release, and QueryInterface for any object.
NS_IMPL_ISUPPORTS1(classname, interface1)
Also, if If your class implements more than one interface, you can simply change the number 1 in the macro to the number of interfaces you support and list the interfaces, separated by commas. For example:
NS_IMPL_ISUPPORTS2(classname, interface1, interface2) NS_IMPL_ISUPPORTSn(classname, interface1, ..., interfacen)
These macros automatically add the nsISupports entry for you, so you ''don't '' need to do something like thisthe following:
NS_IMPL_ISUPPORTS2(classname, interface1, nsISupports)
As an example, consider seamonkey/xpcom/io/nsBinaryStream.cpp (http://lxr.mozilla.org/seamonkey/source/xpcom/io/nsBinaryStream.cpp#62):
62 NS_IMPL_ISUPPORTS3(nsBinaryOutputStream, nsIObjectOutputStream, nsIBinaryOutputStream, nsIOutputStream) 
* === Use of never before-seen types===
nsAString, nsresult, PRInt32 -- what are these new types?