Open main menu

CDOT Wiki β

Changes

Real World Mozilla First XPCOM Component

3,950 bytes added, 13:25, 18 November 2008
nsISupports
[[Real World Mozilla]] > [[Real World Mozilla Day 3]] > First XPCOM Component
 
= Introduction =
* Reuse, as separate components become usable in different applications
This raises a question: what are 'XPCOM components' components of exactly? The answer is Gecko, Mozilla's standards compliant, embeddable web browser and toolkit for creating web browsers and other applications. XPCOM is the means of accessing Gecko library functionality and embedding or extending Gecko.  Much has been [http://developer.mozilla.org/en/docs/XPCOM written about XPCOM] elsewhere, and you are encouraged to read more on the theory behind this technology in conjunction with this walkthrough.
= Writing FirstXpcom =
In this walkthrough we will be creating a simple binary XPCOM component using the build system, called '''FirstXpcom'''. While this component won't do very much yet, it will provide a starting point for many other types of components you may wish to build down the road. 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.
The code this component will become part '''NOTE: the following assumes you have used an objdir. Replace all occurrences of a Gecko-enabled application $(in this case, a Firefox binary extensionobjdir). However, it is important to remember that this is only one of many possible uses for itwith your objdir name.'''
= What is a Component? =
It should also be noted that components can implement multiple interfaces. This is something we'll return to later when we discuss '''interface querying'''.
= Starting 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 ===
What does line three mean? This says that our component is '''[scriptable''', and can be used or implemented in scripting languages, JavaScript for example uuid(...see http://developerbelow..mozilla.org/en/docs/Interfaces:About_Scriptable_Interfaces).]
What does line 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 using in a number of methodsways: * at the command prompt using the command '''uuidgen''' $ 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.
== Makefile.in Build system changes ==
The first step in making the build system aware of our component is to generate an input file for autoconf to use during the configure step, which will build the necessary Makefile automatically.
$ cd === mozilla/extensions/firstxpcom $ touch /Makefile.in===
The Makefile.in should contain the following (NOTE: you can read more about what these files actually mean [http://developer.mozilla.org/en/docs/How_Mozilla%27s_build_system_works here]):
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = firstxpcom
DIRS = public \
src \ $(NULL)
XPI_NAME = firstxpcom
# A Unique ID for your extension
INSTALL_EXTENSION_ID = firstxpcom@senecac.on.ca
# Will create a .xpi in /mozilla/$(MOZ_OBJDIR)/dist/xpi-stage/
XPI_PKGNAME = firstxpcom
# install.rdf will tell Firefox how to install our extension and
# this says, "copy install.rdf into our extension dir and xpi"
DIST_FILES = install.rdf
include $(topsrcdir)/config/rules.mk
Note the '''DIRS''' variable. include $(topsrcdir)It says that the two directories, public and src, will be entered during the build. Because Mozilla's build system [http:/config/rulesdeveloper.mozilla.org/en/docs/How_Mozilla%27s_build_system_works uses recursive make], we also need Makefile.in files in each of these.mk
Note the '''DIRS''' variable. It says that the two directories, === mozilla/extensions/firstxpcom/public and src, will be entered during the build. That means we also need /Makefile.in files in each of these.===
Next we need a Makefile.in the '''public ''' directory
DEPTH = ../../..
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 === Now a Makefile.in in the '''src ''' directory:
DEPTH = ../../..
XPI_NAME = firstxpcom
# The REQUIRES section tells make which modules your
# components uses. This causes the relevant subdirectories
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):
The last build-related file we need to write is a file telling Firefox how to install our extension--'''install.rdf''' (see http://developer.mozilla.org/en/docs/Install_Manifests for details).
<pre>
<?xml version="1.0" encoding="UTF-8"?>
<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>
Now we're ready to build our component. Add the following line to your '''.mozconfig''' file in order to include firstxpcom during the build process:
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.
********** TODO **************** Discuss ''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 at this point, and problems bug 371201]. This is a bug that was identified while the author was trying to debug his Makefile.in files. Ted Mielczarek (ted on IRC) was finally able to spot the problem--a trailing space on XPI_NAME. I had making share this workanecdote as a way to introduce https://bugzilla.mozilla.org, how luser helped meto emphasize the necessity of the community, and in to show the end a bug was filed on 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 ==
=== Use of header guards ===
Header guards (see [http://en.wikipedia.org/wiki/Include_guard Include Guards]) are a portable technique for ensuring that includes or defines are only done once, for example, that nsISupports. h only be included once:  #ifndef __gen_nsISupports_h__ #include "nsISupports.h" #endif Another way of doing this, especially with the Microsoft compiler, is to use
#pragma once
* 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 seamonkey[http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/xpcom/io/nsBinaryStream.cpp (http://lxr&rev=1.32&raw=1&mark=65&#65 mozilla.org/seamonkey/source/xpcom/io/nsBinaryStream.cpp#62)]:
62 NS_IMPL_ISUPPORTS3(nsBinaryOutputStream, nsIObjectOutputStream, nsIBinaryOutputStream, nsIOutputStream)
=== 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.
== Module Code ==
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 In addition to implementing the module and compile it into a librarycode (i.e., it must export a single method named NSGetModule[http://developer. This NSGetModule function is the entry point for accessing the librarymozilla. It gets called during registration and unregistration of the org/en/docs/nsIModule nsIModule]), we also have to write code to allow our componentto be created at runtime based on an interface rather than concrete types--essentially, and when XPCOM wants abstracting the process of creation so that clients don't have to discover what know about real classes underneath the interfaces or classes . This means implementing the module[http://developer.mozilla.org/en/docs/library implementsnsIFactory nsIFactory] interface.
In addition Together, these two interfaces will require us to implementing the module code (iwrite [http://developer.mozilla.eorg/en/docs/Creating_XPCOM_Components:Creating_the_Component_Code#webLock1., nsIModule), we also have to write cpp hundreds lines of code to allow our component to be created at runtime based on interface rather than concrete types--esentially], abstracting the process majority of creation so that clients don't have to know about real classes underneath the interfaceswhich is generic boilerplate code. This means implementing In order to simplify the nsIFactory interface.work component developers must do, a number of macros help us with this task:
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
#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)
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''':
-----------start-------------- NS_GENERIC_FACTORY_CONSTRUCTOR(FirstXpcom)
#include "nsIGenericFactory.h".Note: we also could have provided an initialization function to be called after our object gets allocated (i.e., FirstXpcom->Init()):
// This will result in a function named FirstXpcomConstructor.NS_GENERIC_FACTORY_CONSTRUCTOR NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(FirstXpcomnsYourConcreteClassName, Init)
// 19f3ef5e-759f-49a4-88e3-ed27f9c83011 #define FIRSTXPCOM_CID \ {0x19f3ef5eNext, 0x759fwe need to create proper identification for our component's module so that it can be passed to the module implementation macro, 0x49a4, \ { 0x88, 0xe3, 0xed, 0x27, 0xf9, 0xc8, 0x30'''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, 0x11} }and every component belongs to a module so it can get loaded by the system).
static const nsModuleComponentInfo components[] ={ { "FirstXpcom"Start by generating another '''uuid''', FIRSTXPCOM_CID, "@senecacwhich will be used for identifying our component/class (i.one.ca/firstxpcom;1", FirstXpcomConstructor }}; NS_IMPL_NSGETMODULE(FirstXpcomModulewe can't re-use our interface's uuid), components)for example:
19f3ef5e-759f-49a4-88e3-------end-------------------ed27f9c83011
First, we have to include nsIGenericFactoryNow [http://developer.h in order to get NS_GENERIC_FACTORY_CONSTRUCTORmozilla. Now we can add the following line, which will generate org/En/Generating_GUIDs write a function called FirstXpcomConstructordefine] to make it easier to pass this Class ID around in C++:
NS_GENERIC_FACTORY_CONSTRUCTOR(FirstXpcom) #define FIRSTXPCOM_CID \ {0x19f3ef5e, 0x759f, 0x49a4, \ { 0x88, 0xe3, 0xed, 0x27, 0xf9, 0xc8, 0x30, 0x11} }
Note: Then we also could have provided an initialization function to be called after can populate our object gets allocated (i.e., array with a single entry for the FirstXpcom->Init())component:
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsYourConcreteClassName static const nsModuleComponentInfo components[] = { { "FirstXpcom", Init) // descriptive name FIRSTXPCOM_CID, // CID from above "@senecac.on.ca/firstxpcom;1", // Contract ID FirstXpcomConstructor // Factory Constructor } };
Next, we The last two entries need some explanation. The '''Component ID''' is a human-readable string that clients can use to get/create proper identification for our componentan instance of your class. We's module so that it can be passed ll see how to the module implementation macro NS_IMPL_NSGETMODULEdo this later on. This macro takes Here is an array of nsModuleComponentInfo so that you can define more than one component per module (remember that a module is a collection of components, example Component ID and every component belongs to a module so what it can get loaded by the system).means:
Start by generating another uuid that will be used for identifying our component "@mozilla.org/class, for example:network/ldap-operation;1"
19f3ef5e* domain = @mozilla.org* module = network* component = ldap-759f-49a4-88e3-ed27f9c83011operation* version = 1
Now write a define to make it easier to pass this Class ID around:The final line, the constructor, is the name of the constructor automatically generated by '''NS_GENERIC_FACTORY_CONSTRUCTOR'''. It will be the name of your concrete class followed by "Constructor," in our case '''FirstXpcomConstructor'''.
#define FIRSTXPCOM_CID \ {0x19f3ef5e, 0x759f, 0x49a4, \ { 0x88, 0xe3, 0xed, 0x27, 0xf9, 0xc8, 0x30, 0x11} }And that's it for the module/factory code.
Then we can populate our componets array with a single entry for the == FirstXpcom component:.cpp ==
static const nsModuleComponentInfo components[] ={ { "All that remains is our implementation. Here is the final version of '''FirstXpcom", // descriptive name FIRSTXPCOM_CID, // CID from above "@senecac.on.ca/firstxpcom;1", // Contract ID FirstXpcomConstructor // Factory Constructor }};cpp''' (emphasis added to highlight changes):
The last two need some explanation '''#include <stdio.h> // for printf()''' #include "IFirstXpcom.h" // the CPP .h generated from our .idl #include "nsIGenericFactory. h" // for NS_GENERIC_FACTORY_CONSTRUCTOR() The #include "nsStringAPI.h" // for nsString class FirstXpcom : public IFirstXpcom { public: NS_DECL_ISUPPORTS NS_DECL_IFIRSTXPCOM FirstXpcom(); private: ~FirstXpcom(); protected: '''nsString mName;''' }; NS_IMPL_ISUPPORTS1(FirstXpcom, IFirstXpcom) FirstXpcom::FirstXpcom() { /* member initializers and constructor code */ '''mName.Assign(NS_LITERAL_STRING("FirstXpcom Component ID is "));''' } FirstXpcom::~FirstXpcom() { /* destructor code */ } /* attribute AString name; */ NS_IMETHODIMP FirstXpcom::GetName(nsAString & aName) { '''aName.Assign(mName);''' '''printf("FirstXpcom::GetName\n");''' '''return NS_OK;''' } NS_IMETHODIMP FirstXpcom::SetName(const nsAString & aName) { '''mName.Assign(aName);''' '''printf("FirstXpcom::SetName\n");''' '''return NS_OK;''' } /* long add (in long a, in long b); */ NS_IMETHODIMP FirstXpcom::Add(PRInt32 a human-readable string that clients can use to get, PRInt32 b, PRInt32 *_retval) { '''printf("FirstXpcom::Add(%d, %d)", a, b);''' '''*_retval = a + b;''' '''return NS_OK;''' } //create an instance of your classThis will result in a function named FirstXpcomConstructor. We'll see how to do this later ''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''' '''}''' Here is an example Component ID and what it means:'''};''' '''NS_IMPL_NSGETMODULE(FirstXpcomModule, components)'''
"@mozilla.org/network/ldap-operation;1"Time to call make again:
domain = @mozilla.orgmodule = networkcomponent = ldap-operationversion = 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/.  = 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
Now let's try and access this == Accessing FirstXpcom from JS in the browser. If you haven't done so already, download the Extension Developer's Extension: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.htmlExtension 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.
This will allow you to use Launch the JavaScript JS Shell ('''Tools > Extension Developer > Javascript Shell inside the browser, making it easy ''') and write some code to try out the firstxpcom access your XPCOM component (see http://developer.mozilla.org/en/docs/Introduction_to_the_JavaScript_shell) You can work interactively without having to define functions, write a complete extension, etc.:
Lauch the JSShell (Tools > Extension Developer > Javascript Shell) and write some code to access your XPCOM * Define our component:'s ID so we can create an instance of it below.
// 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 * Now create an instance of our firstxpcom class and return it as component. The value of obj will be nsISupportsvar obj = Componentsat this point (i.classes[cid]e.createInstance(, we can't call IFirstXpcom's methods yet).
// This will take the nsISupports object returned above and QI it to IFirstXpcom var obj = obj.QueryInterface(Components.interfacesclasses[cid].IFirstXpcomcreateInstance()
* 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..."'').
// Now we can use the IFirstXpcom methods and attributesvar sumsum obj = obj.addQueryInterface(4,5Components.interfaces.IFirstXpcom)
var namename = "Dave Humphrey"* 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 sending causing messages to stdout in your shell console 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= Resources =
* Close minefield* Open VS[[Makefile.NET and choose File > Open Project/Solution...* Open $(objdir)/dist/bin/firefox.exe* Right-click the firefox.exe item in the Solution Explorer and choose Properties* Add the following to "Command Arguments": Template for In-p <development_profile_name> or -Profilemanager if you don'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 continue.Tree Extensions]]
1
edit