Background
I have been working on BHO for a few months now. It's neat to develop BHO components. On the contrary, Firefox seems really frustrating. I guess the first reason is
that there are never enough examples and codes for you to follow. Secondly, Mozilla does not provide enough documents like Microsoft does. Well, I
don't blame them for that. It's a small company and it's free source after all.
In the following sections, I will use an example to show you how to intercept Firefox's DOM elements and operate them like in BHO.
There are two articles I strongly recommend you to read before you scroll down this page.
The first one teaches you how to build a component; the second teaches you how to make an extension.
The example here is based on the example in the first article.
It took me two whole weeks to learn how Firefox as well as XPCOM operate. I will use this simple graph to demonstrate the structure:
UI--> XPConnect--> XPCOM
Most of the add-ons you have seen and you can download are extensions such as a toolbar etc. The majority of them are written with JavaScript. It's alright and
neat if you don't want to touch some advanced components; for instance, a toolbar only navigates the window, etc. But if you want to go further, you have to
call components built under XPConnect or XPCOM.
The following code is a simple cookie manager from Mozilla's official tutorial.
var cookiemanager = Components.classes["@mozilla.org/cookiemanager;1"].getService();
cookiemanager = cookiemanager.QueryInterface(Components.interfaces.nsICookieManager);
function FinalizeCookieDeletions() {
for (var c=0; c<deletedCookies.length; c++) {
cookiemanager.remove(deletedCookies[c].host, deletedCookies[c].name,
deletedCookies[c].path);
}
deletedCookies.length = 0;
}
It is quite easy to understand the procedure.
- Get Service of Components
- QI the components
- Call the methods under the components
There isn't too much technology in this part. But, how the components work is another issue.
Firefox is based on the Gecko SDK. Basically, all those APIs that start with "ns" such as nsIWebBrowser
(similar to IWebBrowser
in BHO)
are from the Gecko SDK. You can find more information on www.xulplanet.com.
Let's get to the point. Now, I want to build a FormFiller for Firefox. Meanwhile, I do not want to use JavaScript to do the job due to the following reasons:
- Javascript will expose your code.
- Javascript has very limited functionality.
So, here are two solutions:
- Build a Firefox extension with C++
- Build a Firefox component with C++
I go for the second. Although registering a component is far more complicated than registering an extension on Firefox. But it's much more secure when calling
core components and operates the data.
How to do it
Like BHO, there is a nsIWebBrowser
that provides the APIs of any XPCOM based browser such as SeaMonkey or NetScape. Through
nsIWebBrowser
, we can get nsIDOMWindow->nsIDOMDocument->nsIDOMElement
then fill in the information we want. However, this does
not work in Firefox. The reason is very simple: Firefox does not use nsIWebBrowser
. It will return some random useless stuff when
you call nsIWebBrowser
via Component Manager.
I have no idea why they do that. Maybe it's because of the multi-tab structure of Firefox. So our problem is here. We must initialize nsIDOMWindow
before we move further.
To save your time on Googling all the information, please let me present this simple function prototype:
var res = obj.Nothing(window);
where Nothing
is the method called for filling in forms. "window
" is the browser window object that JavaScript naturally has.
So inside your code, you have the method declared as:
NS_IMETHOD Nothing(nsIDOMWindow *domWindow);
NS_IMETHODIMP MyComponent::Nothing(nsIDOMWindow *domWindow)
When JavaScript passed the window into the method, it will be automatically converted into nsIDOMWindow
.
OK, half of the job has been done.
The other half is easy. Look at the following code and you will understand.
NS_IMETHODIMP MyComponent::Nothing(nsIDOMWindow *domWindow)
{
nsEmbedString temp(L"test");
nsEmbedString attribute(L"value");
nsEmbedString id(L"id");
nsEmbedString value(L"value");
nsIDOMWindow* window; window=domWindow;
nsCOMPtr<nsIDOMDocument> domDocument;
nsCOMPtr<nsIDOMElement> element;
window->GetDocument(getter_AddRefs(domDocument)); domDocument->GetElementById(temp,getter_AddRefs(element));
element->GetAttribute(id,value);
element->SetAttribute(attribute,value);
return NS_OK;
}
OK, above is the all the code you will need.
How to build the project
I strongly recommend http://www.iosart.com/firefox/xpcom/. I learned most of the things about XPCOM components there.
To start your project, you need to have a .idl file.
#include "nsISupports.idl"
#include "nsIDOMWindow.idl"
[scriptable, uuid(_YOUR_INTERFACE_GUID_)]
interface IMyComponent : nsISupports
{
void Nothing(in nsIDOMWindow domWindow);
};
Then download XPIDL.exe to generate the .h file:
Quote from the article:
- xpidl -m header -I_DIR_ IMyComponent.idl will create the IMyComponent.h header file.
- xpidl -m typelib -I_DIR_ IMyComponent.idl will create the IMyComponent.xpt typelib file.
Then follow http://www.iosart.com/firefox/xpcom/.
Calling the component
There are two ways to call the component:
- Through an extension
- Call from JavaScript on a page
It's the same. An extension is a XUL with JavaScript. Please refer to http://www.borngeek.com/firefox/toolbar-tutorial/
to find out how it works.
The following code on the HTML page calls the component:
<HTML>
<SCRIPT type="text/javascript">
function MyComponentTestGo() {
try {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
const cid = "@mydomain.com/XPCOMSample/MyComponent;1";
obj = Components.classes[cid].createInstance();
obj = obj.QueryInterface(Components.interfaces.IMyComponent);
} catch (err) {
alert(err);
return;
}
var res = obj.Nothing(window);}
</SCRIPT>
<BODY>
<BUTTON ONCLICK="MyComponentTestGo();" id="go">Go</BUTTON>
<input id="test" value="" />
</BODY>
</HTML>
Conclusion
The biggest problem of developing Firefox components is the lack of documents and code. Even XPCOM itself has the same problem. But I think the situation will get better and better.
Usually, there are some skilled people on Mozillzine you can ask for help.
I hope this simple article is helpful to those who are frustrated developing for Firefox. If you have any comments or questions, please leave them here. I will reply to you as much as I can.