Open main menu

CDOT Wiki β

Changes

Real World Mozilla First XPCOM Component

2,829 bytes added, 12:25, 18 November 2008
nsISupports
[[Dive into Real World Mozilla]] > [[Dive into Real World Mozilla Day 3]] > First XPCOM Component
= Introduction =
The functionality of this component will become part of a Gecko-enabled application (in this case, a Firefox binary extension). However, it is important to remember that this is only one of many possible uses for it.
 
'''NOTE: the following assumes you have used an objdir. Replace all occurrences of $(objdir) with your objdir name.'''
= What is a Component? =
= Writing FirstXpcom =
For this walkthrough we will use the Mozilla build system to create our component. It is also possible to us use the '''Gecko SDK''' ([http://developer.mozilla.org/en/docs/How_to_build_a_binary_XPCOM_component_using_Visual_Studio instructions are here]). NOTE: this assumes that you have already done a successful objdir-Firefox build.
== Creating Directories ==
=== nsISupports ===
[http://developer.mozilla.org/en/docs/nsISupports nsISupports] is the base interface for all XPCOM components (i.e., it is possible to pass any XPCOM component around as an nsISupports object). The [http://developer.mozilla.org/en/docs/nsISupports methods] in nsISupports define basic bookkeeping for an interface's lifetime (they also defines define a way to check at runtime if a component implements a given interface, but more on that later).
Components need to keep track of how many clients hold references to them via an interface. This is known as '''reference counting ''' on an interface, and it is used to determine when a component can be safely unloaded so that it doesn't leak (i.e., no one holds a reference any more, but the interface is still in memory).
The members of nsISupports (i.e., QueryInterface, AddRef, and Release) provide the basic means for getting the right interface from an object, incrementing the reference count, and releasing objects once they are not being used.
One point worth mentioning is that '''pointers in XPCOM are to interfaces'''. Interface pointers are known to implement nsISupports, so you can access all of the object lifetime and discovery functionality described above.
So the first line above says, "include the interface for nsISupports (defined in nsISupports.idl) because I'll need it", and the fourth line says, "I'm a new interface called IFirstXpcom, but I'm also nsISupports because I inherit from it."
=== UUID ===
[scriptable, uuid(...see below...)] What does line three 3 mean? This says that our component is '''scriptable''', and can be used or implemented in scripting languages, JavaScript for example (see http://developer.mozilla.org/en/docs/Interfaces:About_Scriptable_Interfaces). Each interface needs to be uniquely identifiable, and Mozilla uses a 128-bit number called a '''UUID''' (Universally Unique Identifier) for this purpose. You can generate one in a number of ways:
Each interface needs to be uniquely identifiable, and Mozilla uses a 128-bit number called a * at the command prompt using the command '''UUIDuuidgen''' (Universally Unique Identifier) for this purpose. :  You can generate one using a number of methods:$ uuidgen 78af1749-014a-47aa-baec-2669670b7601
* in MSYS, use the command-line program '''uuidgen.exe'''
* in IRC ask firebot:
[scriptable, uuid(78af1749-014a-47aa-baec-2669670b7601)]
 
More information about generating UUIDs in different forms is available [http://developer.mozilla.org/En/Generating_GUIDs here].
=== Attributes and Methods ===
attribute AString name; long add(in long a, in long b); Next comes the body of your interface. Our interface defines one '''attribute ''' and one '''method'''. An attribute is a value you can '''Get''' and '''Set''' (NOTE: you can specify attributes that are Get only, that is read-only). We have an attribute called '''name''' of type '''AString''' (a unicode, or two-byte string class. For more details about strings in Mozilla, see the [http://developer.mozilla.org/en/docs/XPCOM_string_guideXPCOM String Guide]).
Our interface also defines a single method called '''add''', which takes two long integers as input, adds them, and returns the result as a long integer; we'll write that code below.
Because we are using the Mozilla build system to help us create our component, we can get it to translate our IDL into .h and .cpp stub files automatically. But To do this we first we have to generate some makefiles.
== Build system changes ==
include $(topsrcdir)/config/rules.mk
Note the '''DIRS''' variable. It says that the two directories, public and src, will be entered during the build. That means Because Mozilla's build system [http://developer.mozilla.org/en/docs/How_Mozilla%27s_build_system_works uses recursive make], we also need Makefile.in files in each of these.
=== mozilla/extensions/firstxpcom/public/Makefile.in ===
include $(topsrcdir)/config/rules.mk
Here we tell the build system about our component's name and where it's its XPIDL file can be found.
=== mozilla/extensions/firstxpcom/src/Makefile.in ===
CPPSRCS = FirstXpcom.cpp \
$(NULL)
include $(topsrcdir)/config/rules.mk
EXTRA_DSO_LDOPTS += \
$(NSPR_LIBS) \
$(NULL)
include $(topsrcdir)/config/rules.mk
=== mozilla/extensions/firstxpcom/install.rdf ===
The last build-related file we need to write is a file telling Firefox's addon manager about our extension and how to install it--'''install.rdf''' (see http://developer.mozilla.org/en/docs/Install_Manifests for details).:
<pre>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!-- firefox -->
<em:minVersion>2.0</em:minVersion>
<em:maxVersion>3.0a3pre0b2pre</em:maxVersion> <!-- trunk build Feb 27Nov 11, 2007 -->
</Description>
</em:targetApplication>
ac_add_options --enable-extensions=default,'''firstxpcom'''
Now you can (BUT DON'T, see below for quick way!!)re-build your tree (also known as "rebuilding world") by doing:
$ cd mozilla
$ 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(NOTE: this will cause errors, see below):
$ cd $(objdir)/extensions/firstxpcom/public
Here we can see the build processing our IDL file. As a result of this first (partially) successful run of make, exported and generated header files (i.e.,from our IDL) will be placed in '''$(objdir)/dist/include/firstxpcom'''.
Within '''$(obdirobjdir)/dist/include/firstxpcom/IFirstXpcom.h''' you'll see the following block of code:
#if 0
#endif
The code in the middle of this block is what you want to use as the basis for implementing your .cpp file (and .h if you choose to split it out, which we won't). Copy and paste this implementation stub into the following file: '''mozilla/extensions/firstxpcom/src/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'''.
Re-run make.
''As an aside, and while you are thinking about Makefiles, you might take a moment to read [https://bugzilla.mozilla.org/show_bug.cgi?id=371201 bug 371201]. This is a bug that was identified while the author was trying to make debug his Makefile.in files. Ted Mielczarek (luser ted on IRC) was finally able to spot the problem--a trailing space on XPI_NAME. I share this anecdote as a way to introduce https://bugzilla.mozilla.org, to emphasize the necessity of the community, and to show the kind of problems that one can have writing files for the build system.--UPDATE: this bug has now been fixed by Ted (March 26, 2007).''
== Examining IFirstXpcom.h ==
* 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(nsIFoo --> NS_DECL_NSIFOO). 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
The declaration of nsISomeClass doesn't include any methods other than the constructor 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 ('''[http://bonsai.mozilla.org/cvsblame.cgi?file=/mozilla/xpcom/base/nscore.h&rev=1.103&raw=1&mark=324-327&#324 nsresult'''], a integer), 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_ISUPPORTS2(classname, interface1, nsISupports)
As an example, consider [http://lxrbonsai.mozilla.org/seamonkey/sourcecvsblame.cgi?file=mozilla/xpcom/io/nsBinaryStream.cpp&rev=1.32&raw=1&mark=65&#62 65 seamonkeymozilla/xpcom/io/nsBinaryStream.cpp]:
NS_IMPL_ISUPPORTS3(nsBinaryOutputStream, nsIObjectOutputStream, nsIBinaryOutputStream, nsIOutputStream)
19f3ef5e-759f-49a4-88e3-ed27f9c83011
Now [http://developer.mozilla.org/En/Generating_GUIDs write a define ] to make it easier to pass this Class ID aroundin C++:
#define FIRSTXPCOM_CID \
{
/* member initializers and constructor code */
'''mName.Assign(LNS_LITERAL_STRING("FirstXpcom Component"));'''
}
* Everything else in dist/bin/extensions/firstxpcom@senecac.on.ca
= Testinig Testing FirstXpcom = == Checking the Add-on Manager ==
We'll write formal tests and code to use our component later. For now, make sure it gets loaded into Firefox and is visible in the Addon Manager. Run Firefox and make sure you can see your extension in the addon manager:
$ cd $(objdir)/dist/bin
$ export MOZ_NO_REMOTE=1
$ export MOZ_DEBUG_BREAK=warn
$ firefox.exe -Profilemanager-no-remote == Accessing FirstXpcom from the JavaScript Shell == Now let's try and access this from JavaScript in the browser. If you haven't done so already, download and install the [http://ted.mielczarek.org/code/mozilla/extensiondev/index.html Extension Developer's extension]. This will allow you to use the [http://developer.mozilla.org/en/docs/Introduction_to_the_JavaScript_shell JavaScript Shell] inside the browser, making it easy to try out the firstxpcom component. Launch the JS Shell ('''Tools > Extension Developer > Javascript Shell''') and write some code to access your XPCOM component. You can work interactively without having to define functions, write a complete extension, etc.: * Define our component's ID so we can create an instance of it below.  const cid = "@senecac.on.ca/firstxpcom;1" print(cid) * Now create an instance of our component. The value of obj will be nsISupports at this point (i.e., we can't call IFirstXpcom's methods yet).  var obj = Components.classes[cid].createInstance() * Next, take the the nsISupports object returned above and query it (i.e., see if it supports your interface type and if so, change to that interface) to IFirstXpcom, often referred to as QI (e.g., ''"...you need to QI it to IFirstXpcom..."'').  obj = obj.QueryInterface(Components.interfaces.IFirstXpcom) * At this point we have the ability to use the IFirstXpcom methods and attributes:  var sum sum = obj.add(4,5) var name name = "FirstXpcom!" obj.name = name print(obj.name) alert(obj.name) When you run this code, also notice how your C++ '''printf''' statements are causing messages to stdout in your console window (you may need to scrollback through all the other messages to find them). = Resources =  * [[Makefile.in Template for In-Tree Extensions]]
1
edit