Wednesday, November 3, 2010

Creating C++ Component / Interface using Mozilla Build System

This is a step-by-step guide to create C++ Component / Interface, build using Mozilla build system, and test our component using extension and web page. I tested this on Ubuntu 10.04.

1. Create a directory with the name of your extension under "/mozilla/extensions/" directory. Use only lowercase letter. Let say we created a directory "krpextension"

2. Create "Makefile.in" in krpextension directory.
#/* Makefile.in under /mozilla/extensions/krpextension directory */
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = krpextension
DIRS = public src
XPI_NAME = krpextension
INSTALL_EXTENSION_ID = krpextension@mycompany.com
XPI_PKGNAME = krpextension
USE_EXTENSION_MANIFEST = 1
DIST_FILES = install.rdf
include $(topsrcdir)/config/rules.mk
#/* End of Makefile */

3. Create two subdirectories in "krpextension" directory namely "public" and "src". Public subdirectory to hold our ".idl" file and src subdirectory to hold component header and implementation files.  Each subdirectory in-turn contains its own makefile. Step 4 contains Makefile for "public" directory and Step 5 contains Makefile for "src" directory.

4. change directory to "public" and create "Makefile.in"
#/* Makefile.in under "/mozilla/extensions/krpextension/public" directory */

DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = krpextension
XPIDL_MODULE = krpextension
XPI_NAME = krpextension
XPIDLSRCS = \
krpIComponent.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk

#/* end of Makefile */

5. change directory to "src" and create "Makefile.in"
#/* Makefile.in under "/mozilla/extensions/krpextension/src" directory */
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
IS_COMPONENT = 1
MODULE = krpextension
LIBRARY_NAME =  krpExtension
USE_STATIC_LIBS = 1
XPI_NAME = krpextension
REQUIRES = xpcom \
 string \
 $(NULL)
CPPSRCS = \
 krpComponent.cpp \
 krpExtension.cpp \
 $(NULL)
include $(topsrcdir)/config/rules.mk
EXTRA_DSO_LDOPTS += \
  $(XPCOM_GLUE_LDOPTS) \
  $(NSPR_LIBS) \
  $(NULL)
#/* end of Makefile */

6. Change to "public" sub-directory under "/mozilla/extension/krpextension" and now we will create our own Interface definition file (.idl) in public directory, Let say "krpIComponent.idl", if you used other name than "krpIComponent" then replace each occurring of  krpIComponent with your custom name of Interface. 
/* krpIComponent.idl file in public subdirectory */
#include <stdio.h>
#include "nsISupports.idl"
[scriptable, uuid(1c0856f7-ed61-471e-9dac-442565a5d1d2)]
interface krpIComponent : nsISupports
{
  unsigned long long Add(in unsigned long long num1, in unsigned long long num2); 
  unsigned long long Sub(in unsigned long long num1, in unsigned long long num2);
};
/* End of krpIComponent.idl file */

7. Change to src sub-directory under /mozilla/extension/krpextension, now we will create header and implementation file for our interface.
/* krpComponent.h   header file for our component */
#ifndef __KRPCOMPONENT_H__
#define __KRPCOMPONENT_H__
#include  <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "krpIComponent.h"
#include "nsIObserverService.h"
#include "nsServiceManagerUtils.h"
#define KRPCOMPONENT_CLASSNAME "krpcomponent"
#define KRPCOMPONENT_CID                                \
{ /* 1c0856f7-ed61-471e-9dac-442565a5d1d2 */            \
  0x1C0856F7, 0xED61, 0x471E,                           \
  {0x9D, 0xAC, 0x44, 0x25, 0x65, 0xA5, 0xD1, 0xD2}      \
}
#define KRPCOMPONENT_CONTRACTID "@mycompany.com/krpcomponent;1" 
/* Header file */
class krpComponent: public krpIComponent
{
public:
  NS_DECL_ISUPPORTS
  NS_DECL_KRPICOMPONENT
  krpComponent();
private:
  ~krpComponent();
protected:
};
#endif // __KRPCOMPONENT_H__
/* End of krpComponent.h header file */


/*krpComponent.cpp C++ Implementation file of our component */
#include "krpComponent.h"
/* Implementation file */
NS_IMPL_ISUPPORTS1(krpComponent, krpIComponent)
krpComponent::krpComponent()
{
  /* member initializers and constructor code */
}
krpComponent::~krpComponent()
{
  /* destructor code */
}
/* unsigned long long Add (in unsigned long long num1, in unsigned long long num2); */
NS_IMETHODIMP krpComponent::Add(PRUint64 num1, PRUint64 num2, PRUint64 *_retval NS_OUTPARAM)
{
  *_retval = num1 + num2;
    return NS_OK;
}
/* unsigned long long Sub (in unsigned long long num1, in unsigned long long num2); */
NS_IMETHODIMP krpComponent::Sub(PRUint64 num1, PRUint64 num2, PRUint64 *_retval NS_OUTPARAM)
{
  if(num1 > num2)
    *_retval = num1 - num2;
  else
    *_retval = num2 - num1;
    return NS_OK;
}
/* End of implementation class of our component. */

8. Now we will create module definition file lets say "krpExtension.cpp". If you use other name then replace all occurrences of krpExtension or krpExtension.cpp with your custom name. 
/*krpExtension.cpp Module definition file */
#include "nsXPCOM.h"
#include "nsIGenericFactory.h"
/**
 * Components to be registered
 */
#include "krpComponent.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(krpComponent)
//----------------------------------------------------------
static const nsModuleComponentInfo components[] =
{
{
KRPCOMPONENT_CLASSNAME,
KRPCOMPONENT_CID,
KRPCOMPONENT_CONTRACTID,
krpComponentConstructor
},
};
NS_IMPL_NSGETMODULE(krpExtension, components)
/* end of krpExtension.cpp File */

9. Now to integrate our component into Extension, we will create a custom extension. In "/mozilla/extensions/krpextension" create "install.rdf" file.
/*install.rdf file contents */
  <?xml version="1.0" encoding="UTF-8"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
  <Description about="urn:mozilla:install-manifest">
    <em:id>krpextension@mycompany.com</em:id>
    <em:version>0.1</em:version>
    <em:targetApplication>
      <!-- Firefox -->
      <Description>
        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
        <em:minVersion>3.0</em:minVersion>
        <em:maxVersion>3.6.*</em:maxVersion>
      </Description>
    </em:targetApplication>
    <!-- front-end metadata -->
    <em:name>My First Interface Extension</em:name>
    <em:description>Just an FF Interface Example.</em:description>
    <em:creator>kailas</em:creator>
    <em:homepageURL>http://kailaspatil.blogspot.com/</em:homepageURL>
  </Description>
</RDF>
/*install.rdf file ends here */

10. Now create another file "jar.mn" in "/mozilla/extensions/krpextension" directory.
/* jar.mn file contents */
krpextension.jar:
% content krpextension %content/
% locale krpextension en-US %locale/en-US/
% skin krpextension classic/1.0 %skin/
% overlay chrome://browser/content/browser.xul  chrome://krpextension/content/ff-overlay.xul
  content/overlay.js          (chrome/content/overlay.js)
  content/ff-overlay.xul         (chrome/content/ff-overlay.xul)
  content/ff-overlay.js (chrome/content/ff-overlay.js)
  content/about.xul         (chrome/content/about.xul)
  locale/en-US/overlay.dtd         (chrome/locale/en-US/overlay.dtd)
  locale/en-US/about.dtd         (chrome/locale/en-US/overlay.dtd)
  locale/en-US/overlay.properties (chrome/locale/en-US/overlay.properties)
  skin/overlay.css         (chrome/skin/overlay.css)
/* end of jar.mn file */
The files in jar.mn reflects the XUL/JavaScript files in your custom Extension.
Use Mozilla addon to build an empty extension for Firefox, with name krpextension. Copy its "chrome" directory including subdirectories "content, locale, and skin" in chrome directory into our extension directory, i.e "/mozilla/extensions/krpextension". Note that the paths in parentheses point to actual files.

11. Now run "make" command in "mozilla/$(OBJ_DIR)$" or "make -f client.mk build" command in "/mozilla" directory. If you are using make -f client.mk build command then make sure that you setup ".mozconfig" file.  Pls read an article https://developer.mozilla.org/en/build_documentation to build firefox and prerequisites required to build Firefox. 

12. Our component should be build successfully, and in "Tools->Addons->Extension" Tab you should see our custom Extension.

13. To test our component with Extension and Webpage.  First we will test it with Extension. Open overlay.js file in our extension folder "/mozilla/extensions/krpextension/chrome/content/overlay.js".  In OnLoad: function, add following lines of code to test our component. 
/*********************/
   var instance = Components.classes["@mycompany.com/krpcomponent;1"].getService();
    var obj =instance.QueryInterface(Components.interfaces.krpIComponent);
    var result = obj.Sub(10, 5);
    alert("Sub(10-5) = " + result);
/******************/
Each time firefox will start, our custom extension will start, and during initialization of our extension it will call our component method (i.e Sub(10,5) which will return result of Subtraction operation and its get displayed to user using alert box. 

Now we test our component using a Web page. Create a HTML file on local system, and use following script to test our component. It should display the correct result of Addition operation.
/************************/
<script>
try {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
const cid = "@mydomain.com/XPCOMSample/krpComponent;1";
var obj = Components.classes[cid].createInstance();
var obj1 = obj.QueryInterface(Components.interfaces.KFFComponent);


               var res = obj1.Add(10, 5);
alert('Performing 10 + 5  =  ' +  res );

} catch (err) {
alert(err);
}
</script>
/************************************/

No comments:

Post a Comment