667
edits
Changes
→Use of never before-seen types
=== Use of never before-seen types ===
A quick scan of the code also reveals types that will be unfamiliar, including: nsAString, nsresult, and PRInt32 -- what . What are these new types?
Because Mozilla is cross-platform almost all of the standard types you are used to have Mozilla-specific versions. For example, PRInt32, which is defined as part of the Netscape Portable Runtime (hence the ''PR '' prefix), is a signed 32-bit integer on all platforms , no matter the OS you are using (see http://developer.mozilla.org/en/docs/PRInt32). Depending on the platform you use this could mean a regular int or a long. The same is true of strings (Mozilla has it's own string classes -- see http://developer.mozilla.org/en/docs/XPCOM_string_guide) because of the need for multi-language support and other things necessary to make Mozilla's products workaround the world. At first there are so many of these to learn. But you quickly get accustomed to them, and looking at how other people code (via lxr) can help you in this process.
At first there are so many of these to learn. But you quickly get accustomed to them, and looking at how other people code (via [http://lxr.mozilla.org lxr]) can help you in this process.
You'll also notice differences between the original IDL and the autogenerated C++ signatures. In our IDL file, the IFirstXpcom::Add method took two longs and returned a long. However in the C++ code stub it says something different:
/* XPIDL -- long add (in long a, in long b); */
NS_IMETHOD Add(PRInt32 a, PRInt32 b, PRInt32 *_retval) = 0;
The return value of XPCOM methods generated from XPIDL is always of the type '''nsresult''', and the small macro used in these expansions, '''NS_IMETHOD''', actually represents nsresult. nsresult is returned even when in XPIDL you specify that the method return a void. If your IDL requires a return type, as ours does, that value will be added as a final parameter to the call list--in this case '''PRInt32 *_retval'''.
There are other things you should know about making your code compatible on all of the supported Mozilla platforms Mozilla supports. You can read about them here: http://www.mozilla.org/hacking/portable-cpp.html.
== Registering FirstXpcom ==
Together, these two interfaces will require us to write hundreds lines of code (see http://developer.mozilla.org/en/docs/Creating_XPCOM_Components:Creating_the_Component_Code#webLock1.cpp as an example), the majority of which is generic boilerplate code. In order to simplify the work component developers must do, a number of macros help us with this task:
* NS_GENERIC_FACTORY_CONSTRUCTOR* NS_IMPL_NSGETMODULE
NS_IMPL_NSGETMODULE(FirstXpcomModule, components) ----------end-------------------
First, we have to include '''nsIGenericFactory.h ''' in order to get '''NS_GENERIC_FACTORY_CONSTRUCTOR'''. Now we can add the following line, which will generate a function called '''FirstXpcomConstructor''':
NS_GENERIC_FACTORY_CONSTRUCTOR(FirstXpcom)
Note: we also could have provided an initialization function to be called after our object gets allocated (i.e., FirstXpcom->Init()):
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsYourConcreteClassName, Init)
Next, we need to create proper identification for our component's module so that it can be passed to the module implementation macro , '''NS_IMPL_NSGETMODULE'''. This macro takes an array of '''nsModuleComponentInfo '' so that you can define more than one component per module (remember that a module is a collection of components, and every component belongs to a module so it can get loaded by the system).
Start by generating another '''uuid that ''', which will be used for identifying our component/class(i.e., we can't re-use our interface's uuid), for example:
19f3ef5e-759f-49a4-88e3-ed27f9c83011
Now write a define to make it easier to pass this Class ID around:
#define FIRSTXPCOM_CID \ {0x19f3ef5e, 0x759f, 0x49a4, \ { 0x88, 0xe3, 0xed, 0x27, 0xf9, 0xc8, 0x30, 0x11} }
Then we can populate our componets array with a single entry for the FirstXpcom component:
static const nsModuleComponentInfo components[] = { { "FirstXpcom", // descriptive name FIRSTXPCOM_CID, // CID from above "@senecac.on.ca/firstxpcom;1", // Contract ID FirstXpcomConstructor // Factory Constructor } };
The last two entries need some explanation. The '''Component ID ''' is a human-readable string that clients can use to get/create an instance of your class. We'll see how to do this later on. Here is an example Component ID and what it means:
"@mozilla.org/network/ldap-operation;1"
* domain = @mozilla.org* module = network* component = ldap-operation* version = 1
The final line, the constructor, is the name of the constructor automatically generated by the '''NS_GENERIC_FACTORY_CONSTRUCTOR'''. It will be the name of your concrete class followed by "Constructor," in our case '''FirstXpcomConstructor'''.
And that's it! We've done it. Time to call make again:
$ cd $(objdir)/extensions/firstxpcom $ make
Assuming this works without errors, here's what has happened:
* Generated makefiles for your projects project were created in extensions/firstxpcom/ (remember, we’re we're under /mozilla/$(MOZ_OBJDIR)/.
* Exported header files and generated header files (from IDL) in dist/include/firstxpcom/
* Static libraries for your modules in dist/lib/ (in case other modules want to link statically to your stuff instead of using XPCOM).
* XPI file in dist/xpi-stage/firstxpcom.xpi.
* Everything else in dist/bin/extensions/firstxpcom@senecac.on.ca/. == Testinig FirstXpcom ==
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 Now let's try and access this from JS in the browser. If you haven't done so already, download the Extension Developer's Extension: http://ted.mielczarek.org/code/mozilla/extensiondev/index.html This will allow you to use the JavaScript Shell inside the browser, making it easy to try out the firstxpcom component (see http://developer.mozilla.org/en/docs/Introduction_to_the_JavaScript_shell). Lauch the JSShell (Tools > Extension Developer > Javascript Shell) and write some code to access your XPCOM component: // Define our component's ID so we can create an instance of it below.const cid = "@senecac.on.ca/firstxpcom;1"print(cid) // This will create an instance of our firstxpcom class and return it as nsISupportsvar obj = Components.classes[cid].createInstance() // This will take the nsISupports object returned above and QI it to IFirstXpcomobj = obj.QueryInterface(Components.interfaces.IFirstXpcom) // Now we can use the IFirstXpcom methods and attributesvar sumsum = obj.add(4,5) var namename = "Dave Humphrey" obj.name = nameprint(obj.name)alert(obj.name) When you run this code, also notice how your C++ printf statements are sending messages to stdout in your shell window (you may need to scrollback through all the other messages to find them). Let's try debugging FirstXpcom in C++ with Visual Studio.NET