robot.png

Mozilla Add-Ons Workshop

Paris, samedi 20 Septembre 2008

Développement de composants XPCOM en C/C++

Florian Quèze -- florian@instantbird.org

En collaboration avec :
Logo XulFr
http://xulfr.org/

Pourquoi un composant binaire ?

Création d'un composant

3 étapes :

L'interface - Généralités

L'interface - exemple

Exemple simple (maowIExemple.idl) :

#include "nsISupports.idl"

[scriptable, uuid(95e5dd33-6ce1-4e03-8d55-62dc2839c9b3)]
interface maowIExemple: nsISupports {
  readonly attribute AUTF8String message;

  float divise(in long dividende,
               in long diviseur);
};

L'interface - Compilation

On utilise l'outil xpidl, faisant partie du SDK de Mozilla.

2 utilisations du fichier .idl :

L'interface - En-tête C++ générée

Résultat :

...

class NS_SCRIPTABLE maowIExemple : public nsISupports {
 public: 
  ...

  /* readonly attribute AUTF8String message; */
  NS_SCRIPTABLE NS_IMETHOD GetMessage(nsACString & aMessage) = 0;

  /* float divise (in long dividende, in long diviseur); */
  NS_SCRIPTABLE NS_IMETHOD Divise(PRInt32 dividende,
                                  PRInt32 diviseur,
                                  float *_retval) = 0;
};

...

L'interface - Types (1)

Correspondance des types :

IDLC++
void void
boolean PRBool
octet PRUint8
short PRInt16
long PRInt32
long long PRInt64
unsigned short PRUint16
unsigned long PRUint32
unsigned long long PRUint64
float float
double double

L'interface - Types (2)

Correspondance des types :

IDLC++
char char
wchar PRUnichar
string char*
wstring PRUnichar*
ACString nsACString
AUTF8String nsACString
AString nsAString

Et aussi... autres interfaces, constantes, types natifs, ... (voir documentation).

Implémentation - Code généré (1)

Utilisation du code généré à partir de l'interface :

/* Use the code below as a template for the
   implementation class for this interface. */

/* Header file */
class _MYCLASS_ : public maowIExemple
{
public:
  NS_DECL_ISUPPORTS
  NS_DECL_MAOWIEXEMPLE

  _MYCLASS_();

private:
  ~_MYCLASS_();

protected:
  /* additional members */
};

Implémentation - Code généré (2)

/* Implementation file */
NS_IMPL_ISUPPORTS1(_MYCLASS_, maowIExemple)

_MYCLASS_::_MYCLASS_()
{
  /* member initializers and constructor code */
}

_MYCLASS_::~_MYCLASS_() { /* destructor code */ }

...

/* float divise (in long dividende, in long diviseur); */
NS_IMETHODIMP _MYCLASS_::Divise(PRInt32 dividende,
                                PRInt32 diviseur,
                                float *_retval)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}

/* End of implementation class template. */

Implémentation en C++

Implémentation en C++

/* float divise (in long dividende, in long diviseur); */
NS_IMETHODIMP maowExemple::Divise(PRInt32 dividende,
                                  PRInt32 diviseur,
                                  float *_retval)
{
  if (!diviseur)
    return NS_ERROR_INVALID_ARG;

  *_retval = (float)dividende / diviseur;
  return NS_OK;
}

Observations :

Comparaison JavaScript / C++

/* float divise (in long dividende, in long diviseur); */
NS_IMETHODIMP maowExemple::Divise(PRInt32 dividende,
                                  PRInt32 diviseur,
                                  float *_retval)
{
  if (!diviseur)
    return NS_ERROR_INVALID_ARG;

  *_retval = (float)dividende / diviseur;
  return NS_OK;
}

function divise(dividende, diviseur) {
  if (!diviseur)
    throw "Division par 0.";

  return dividende / diviseur;
}

Enregistrement XPCOM

Il faut :

Les deux se font à l'aide de macros, il suffit de définir quelques paramètres.

Dans maowExemple.h, on ajoute :

// f4b9733f-e851-431d-a1f7-fdcc305d0f4e
#define MAOW_EXEMPLE_CID                             \
 { 0xf4b9733f, 0xe851, 0x431d,                       \
  { 0xa1, 0xf7, 0xfd, 0xcc, 0x30, 0x5d, 0x0f, 0x4e } \
 }
#define MAOW_EXEMPLE_CONTRACTID \
  "@instantbird.org/maow;1"

Enregistrement XPCOM - Module

On crée un fichier maowModule.cpp qui implémente le module :

#include "nsIGenericFactory.h"
#include "maowExemple.h"
	
NS_GENERIC_FACTORY_CONSTRUCTOR(maowExemple)

static const nsModuleComponentInfo composants[] =
{
  {
    "MAOW Exemple",          // Description
    MAOW_EXEMPLE_CID,        // Component id
    MAOW_EXEMPLE_CONTRACTID, // Contract id
    maowExempleConstructor   // Constructeur
  }
};
	
NS_IMPL_NSGETMODULE(maowModule, composants)

Liste des fichiers

Nous avons donc maintenant les fichiers suivants :

On compile nos 2 fichiers .cpp pour former une bibliothèque dynamique que l'on place avec l'interface binaire dans le dossier components de l'application (ou extension).

Appel en JavaScript

Utilisation d'autres bibliothèques

Plusieurs possibilités pour utiliser une autre bibliothèque :

Chargement des bibliothèques

Si le composant XPCOM binaire dépend d'autres bibliothèques (non liées statiquement), il faut qu'elles soient chargées en même temps que le composant XPCOM lui-même.

Chargement manuel

Exemple complet sur developer.mozilla.org

La méthode :

Rôle du 'stub'

Le stub implémente uniquement la fonction NSGetModule. Son rôle est de :

Rôle du 'stub' (suite)

Aide au débug

L'enregistrement XPCOM du composant a échoué, comment trouver le problème ?

Plus de détails...

Liens

Questions ?

Crédits

Une partie du contenu de ces slides est directement inspiré des articles Creating Custom Firefox Extensions with the Mozilla Build System de Matthew Gertner et Using Dependent Libraries In Extension Components de Benjamin Smedberg.

/
© Florian Quèze 2008