78
edits
m (→Notes) |
|||
| Line 150: | Line 150: | ||
==The Hello World of XPCOM== | ==The Hello World of XPCOM== | ||
There are a few good resources, and this is not to be a replacement for an actual complete guide. Rather, this is to complement the [http://www.mozilla.org/projects/xpcom/book/cxc/ guide], and provide extra information that the guide..lacks. I found [http://starkravingfinkle.org/blog/2006/10/ Mark Finkle's blog post] to almost be what I wanted. But decided to add my own bits. | |||
I assume : | |||
# You know what XPCOM is | |||
# You have experience in Javascript/C++ | |||
# You know how to access XPCOM objects from Javascript | |||
I am new to XPCOM, and I no doubt misunderstand something and communicating the wrong information to others. | |||
In order to write a C++ XPCOM component and use it in javascript, you must have at least three things (yay redundancy) : | |||
# The interface - This is how javascript sees your code. Javascript has no knowledge of the implementation, it just knows what that implementation needs. | |||
# The implementation - the code that does what you want | |||
# A factory that creates the object. | |||
Lets start with the interface. The interface is not written in C++ as one might expect. It written in some obscure language known as [http://www.mozilla.org/scriptable/xpidl/idl-authors-guide/rules.html IDL]. Here is an example of my idl file : | |||
iTest.idl | |||
1 #include "nsISupports.idl" | |||
2 | |||
3 [scriptable, uuid(7696adf6-147f-11dc-8314-0800200c9a66)] | |||
4 interface iTest : nsISupports { | |||
5 void hello(out ACString world); | |||
6 }; | |||
As you can see, it looks like C++ syntactically, but don't be fooled. You can't overload functions using IDL, and there are a few other limitations as well. But let me break it down line-by-line. | |||
Line 1 : we're including nsISupports, which the interface implements. All objects must implement nsISupports as far as I know. It handles reference counting, and a ton of other garbage. | |||
Line 3 : I guess scriptable means we can use it from Javascript. If you look at [http://www.xulplanet.com/ xulplanet]'s XPCOM reference, you sometimes see functions with [noscript]. A uuid is a unique identifier. On most *nix machines, you can get it from uuidgen command. | |||
Line 4 : Here we name the class, and tell it what other interfaces it implements. | |||
Line 5 : My function. It takes in a string and changes it. The out keyword tells the IDL compiler to change it to a reference. You can see that I didn't return a string, but rather passed in one. Its strange to explain, maybe it would be more clear as we progress. | |||
Now we need to compile using xpidl. This is found in your xulrunner/dist/bin directory. You'll also need to include the directory which contains nsISupports, that is in xulrunner/dist/sdk/include. | |||
We need to create two files, a header file (.h) which is used in our code, and a typelib file (.xpt) that is used by XULRunner. | |||
Here is the somewhat the form it takes | |||
xpidl -m header -w -v -I path/to/include iTest.idl | |||
xpidl -m typelib -w -v -I path/to/include iTest.idl | |||
That will create iTest.xpt and iTest.h | |||
Wow this is going to be ''long''. | |||
Ok. The implementation. Now you need to define a class that implements the iTest interface. I will put the factory and the implementation in one file, mainly for simplicity. | |||
1 #include "iTest.h" | |||
2 #include "nsIGenericFactory.h" | |||
3 #include "nsStringAPI.h" | |||
4 #include "nsEmbedString.h" | |||
Line 1 : The IDL header in .h form | |||
Line 2 : We need a factory to handle the creation of the object. This header file gives us access to a macro that we need | |||
Line 3 : This allows us to take in Strings. We should use this rather than char *. | |||
Line 4 : This isn't needed, ignore this. | |||
6 #define BOO_CID \ | |||
7 { 0x4842ae27, 0x4361, 0x4549, \ | |||
8 { 0x97, 0x23, 0xb4, 0xf6, 0xb1, 0x47, 0x6a, 0xc9 } } | |||
Line 6-8 : This is the IID of our class. I don't know why I put it here. I only use it in one place. I also named it wrong. CID is something else. | |||
10 class Boo : public iTest { | |||
11 public : | |||
12 Boo(); | |||
13 NS_DECL_ISUPPORTS | |||
14 | |||
15 NS_IMETHOD Hello(nsACString & world); | |||
16 }; | |||
Our class definition, this should be obvious. We implement our interface. The only thing that isn't obvious is line 13 and 15. | |||
Line 13 : This macro defines (I may be wrong here), functionality of nsISupports. For example, reference counter and addref() method. | |||
Line 14 : All methods in the declaration should have a return value of NS_IMETHOD. See the iTest.h file for a sample implementation. We also use nsACString, which is an abstract class. You want to avoid concrete implementations when passing variables. | |||
18 Boo::Boo() { | |||
19 NS_INIT_ISUPPORTS(); | |||
20 } | |||
Line 19 : Self-explanitory. I'm not 100% sure what it does, but it should be there :) | |||
23 NS_IMETHODIMP Boo::Hello(nsACString & world) { | |||
24 world.Append("Hello"); | |||
25 return NS_OK; | |||
26 } | |||
Line 23 : The declaration uses NS_IMETHOD, and the implementation uses NS_IMETHODIMP. This is just a rule, don't ask. | |||
Line 25 : We need to return whether we were successful or not. | |||
Line 37-46 : Commented out | |||
47 NS_IMPL_ISUPPORTS2(Boo, nsISupports, iTest); | |||
Line 47 : Bascially, it tells XPCOM what classes class "Boo" supports. You'll notice that its NS_IMPL_ISUPPORTSn, where n is the number of interfaces you implement. If you don't include this, and you try to .createInstance() in your javascript, you will likely hit an expection. | |||
48 NS_GENERIC_FACTORY_CONSTRUCTOR(Boo) | |||
Line 48 : Magically creates a constructor object. It will make slightly more sense below. | |||
50 static const nsModuleComponentInfo components[] = | |||
51 { | |||
52 { "Boo module", | |||
53 BOO_CID, | |||
54 "@cesar.org/boo;1", | |||
55 BooConstructor | |||
56 } | |||
57 }; | |||
I'm not quite sure how to explain this because I'm not sure what its for. | |||
Line 52 : "Boo module" is just a name as far as I can tell. I haven't seen it used. | |||
Line 53 : BOO_CID is the IID that I mistakenly named. This is the only placed used as far as I know. | |||
Line 54 : "@cesar.org/boo;1" is the CID. This is what we use in Components.classes["@cesar.org/boo;1"] | |||
Line 55 : BooConstructor is magic. It is created by the NS_GENERIC_FACTORY_CONSTRUCTOR | |||
59 NS_IMPL_NSGETMODULE(haheRobbleRobble, components) | |||
Line 59 : Turns our hard work into a module named... "haheRobbleRobble". Don't ask | |||
Now you need to compile this cpp file (lets call it hello.cpp) into a shared library. For windows/mac, your on your own. In linux, your in luck! | |||
g++ -fPIC -c -g hello.cpp -o hello.o -I path/to/include | |||
creates an object file in a format that can be put into a shared library. | |||
g++ -shared -o libhello.so hello.o | |||
Actually creates the shared library (I heard the library must start with lib). | |||
Ok, we have an xpt and a .so file. Now we just need to have XULRunner find these and it will load them up so we can start using them. I am using the trunk version of XULRunner, your results may vary. | |||
First copy the library to your app/components directory. If you don't have it, create it. Then copy the .xpt file to your xulrunner/components directory. I have heard that it should go in app/components as well, but I found that it wouldn't register. | |||
Now either delete your application profile ~/.myapp, or increment the BuildID in your application.ini file. Either method should start loading components (in our case, haheRobbleRobble). A neat way to find out if your component/interface exist is to do the following in your javascript code | |||
for (i in Components.[classes|interfaces]) { dump(i + "\n"); } | |||
Which prints a long list of items. Pipe to less to find your component/interface. | |||
Now you can access your XPCOM object like this | |||
var x = new Object(); | |||
Boo.Hello(x); | |||
alert(x.value); | |||
=Feedback= | =Feedback= | ||
I appreciate any comments/suggestions/criticisms. But please post anything in the discussion page. | I appreciate any comments/suggestions/criticisms. But please post anything in the discussion page. | ||
edits