Open main menu

CDOT Wiki β

Changes

Real World Mozilla First XPCOM Component

1,772 bytes removed, 20:44, 27 February 2007
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.
* === Strange return types - differences between original IDL and the C++ signatures===
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 ==
Continuing on, we We now have need to write a bit of code to get our component registered. Components reside in modules, and those modules are defined in shared library files (i.e., DLLs or DSOs) that typically sit in the components directory of an XPCOM application.
A set of default libraries stored in this components directory makes up When you build a component or module and compile it into a typical Gecko installationlibrary, providing functionality that consists it must export a single method named '''NSGetModule'''. This NSGetModule function is the entry point for accessing the library. It gets called during registration and unregistration of networking, layout, composition, a cross-platform user interfacethe component, and otherswhen XPCOM wants to discover what interfaces or classes the module/library implements.
When you build a component or module and compile it into a library, it must export a single method named NSGetModule. This NSGetModule function is the entry point for accessing the library. It gets called during registration and unregistration of the component, and when XPCOM wants to discover what interfaces or classes the module/library implements. In addition to implementing the module code (i.e., [http://developer.mozilla.org/en/docs/nsIModulensIModule]), we also have to write code to allow our component to be created at runtime based on an interface rather than concrete types--esentiallyessentially, abstracting the process of creation so that clients don't have to know about real classes underneath the interfaces. This means implementing the [http://developer.mozilla.org/en/docs/nsIFactory nsIFactory ] interface.
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
 -----------start--------------  #include "nsIGenericFactory.h" ...  // This will result in a function named FirstXpcomConstructor. NS_GENERIC_FACTORY_CONSTRUCTOR(FirstXpcom) // 19f3ef5e-759f-49a4-88e3-ed27f9c83011 #define FIRSTXPCOM_CID \ {0x19f3ef5e, 0x759f, 0x49a4, \ { 0x88, 0xe3, 0xed, 0x27, 0xf9, 0xc8, 0x30, 0x11} } static const nsModuleComponentInfo components[] = { { "FirstXpcom", FIRSTXPCOM_CID, "@senecac.on.ca/firstxpcom;1", FirstXpcomConstructor } };
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
* Close minefield* Open VS.NET In subsequent walkthroughs and choose File > Open Project/Solution...* Open $(objdir)/dist/bin/firefox.exe* Right-click the firefox.exe item labs, we'll use this component in the Solution Explorer JavaScript and choose Properties* Add the following write a XUL interface to "Command Arguments": -p <development_profile_name> or -Profilemanager if you donaccess it't have one yet.* Add the following to "Environment": XPCOM_DEBUG_BREAK=warn. Click OK* File > Open... mozilla/extensions/firstxpcom/src/FirstXpcom.cpp* Set a breakpoint on ::Add --> *_retval = a + b;* Click "Start Debugging" (the "play" button)* Follow the same steps above in the Javascript Shell in order to create and call add() on your component.* Use F11 to move line by line when you get dropped into the debugger, and F5 to continues members.