Notary: Difference between revisions

Jump to navigation Jump to search
7,767 bytes added ,  9 June 2007
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.
78

edits

Navigation menu