MDN’s new design is in Beta! A sneak peek: https://blog.mozilla.org/opendesign/mdns-new-design-beta/

Cette traduction est incomplète. Aidez à traduire cet article depuis l'anglais.

Ce document présente les règles de styles et les pattern utilisés dans la base de code de Mozilla. Tout nouveau code doit essayer de se conformer à ces standards, afin de pouvoir être maintenu facilement. Bien sûr, toute règle va avec ses exceptions, mais il est tout de même important de connaître les règles !

Ce guide s'adresse avant tout aux personnes qui commencent à contribuer et s'apprêtent à demander une revue de leur code. Avant de soumettre votre code, lisez-le et assurez-vous que votre code est conforme aux recommendations.

Nommage et formatage du code

Les règles suivantes s'appliquent à du code nouveau, ou à du code qui doit être nettoyé. Pour étendre un code existant, utilisez le style qui prévaut dans un fichier ou un module, et demandez au responsable de la page si le style à utiliser ne vous semble pas clair.

Espaces blancs

Pas de tabulation. Pas d'espace en fin de ligne.

Pour les retour à la ligne, utilisez le style Unix ('\n'), et non pas le style Windows ('\r\n').

Longueur de ligne

80 charactères ou moins (for laptop side-by-side diffing and two-window tiling; also for Bonsai / hgweb and hardcopy printing).

Indentation

Deux espaces par niveau logique, quatre pour le code Python.

Notez que la visibilité d'une classe et les instructions goto ne correspondent pas à un niveau logique, à la différence des instructions switch case (cf les exemples ci-après).

Structures de contrôle

Utilisez le style d'indentation K&R : accolade ouvrante à la fin de la première ligne, accolades des deux côtés du else sur la même ligne, accolade fermante sur une nouvelle ligne.

Note: Les définitions des classes et des fonctions ne sont pas des structures de contrôle; l'accolade ouvrante va dans cas sur une nouvelle ligne sans indentation supplémentaire.

Utilisez toujours des accolades pour les structures contrôlées, même quand il s'agit d'une instruction d'une seule ligne pour un bloc if else else . C'est redondant mais c'est plus sécurisé pour passer à l'échelle, car cela permet d'éviter les bugs de "dangling else" (cas dans lequel il peut y avoir plusieurs arbres d'analyse corrects pour une même instruction).

Coupez les conditions longues après les opérateurs logiques && et ||. Voir ci-dessous pour les règles concernant les autres opérateurs.

Exemples:

if (...) {
} else if (...) {
} else {
}

while (...) {
}

do {
} while (...);

for (...; ...; ...) {
}

switch (...) {
  case 1: {
    // Lorsque vous avez besoin de déclarer une variable 
    // à l'intérieur d'un switch, 
    // mettez le bloc entre accolades
    int var;
    break;
  }
  case 2:
    ...
    break;
  default:
    break;
}

Pour configurer ce type d'indentation pour switch dans emacs, on peut définir le "case-label" offset:

(c-set-offset 'case-label '+)

Les mots-clé de contrôle if, for, while, et switch sont toujours suivi d'un espace.
Les appels de fonction en revanche n'ont pas d'espace blanc avant la parenthèse.

Espaces de noms C++

Les déclarations C++ qui s'inscrivent dans le projet Mozilla doivent se trouver dans l'espace de noms "mozilla".  Les modules doivent éviter d'utiliser des espaces de noms imbriqués sauf s'ils contiennent des noms qui ont une forte probabilité d'entrer en conflit avec d'autres noms de la base de code (comme Point, Path, etc.).  Ceux-ci peuvent se situer dans des espaces de noms spécifiques, avec des noms en bas de casse uniquement. Il n'est pas possible d'utiliser d'autre espace de noms global que "mozilla".

Les instructions "using" ne sont pas autorisées dans les fichiers d'en-tête, sauf à l'intérieur d'une définition de classe ou d'une fonction (afin de ne pas polluer la portée globale des unités de compilation qui utilisent le fichier d'en-tête).

using namespace ...; est autorisé uniquement dans les fichiers .cpp , après toutes les instructions  #include. Autant que possible, il est préférable d'encapsuler du code dans un bloc (namespace ... { ... };). Une instruction  using namespace ...; devrait toujours spécifier l'intégralité du namespace. C'est-à-dire que pour utiliser Foo::Bar il ne faut écrire using namespace Foo::Bar; (et non pas using namespace Foo; using namespace Bar;

Le code à l'intérieur du bloc namespace ... { ... } ne doit pas être indenté. On peut le désactiver dans emacs en configurant l'offset "innamespace" :

(c-set-offset 'innamespace 0)

Espaces de noms anonymes

L'usage de "static" est préférable à l'utilisation d'un espace de noms anonyme. Cela peut être amené à changer lorsque les débuggeurs (en particulier celui de Windows) supporteront le placement de points d'arrêt dans le code des espaces de noms anonymes. Ces derniers peuvent cependant être utilisés pour les éléments qui ne peuvent pas être statiques (comme les types, ou certains objets qui demandent d'être passés à des fonctions de templating).

Classes C++  

namespace mozilla {

class MyClass : public A
{
  ...
};

class MyClass
  : public X  // Lorsqu'on hérite de plus d'une classe,
  , public Y  // on écrit chaque classe sur une ligne.
{
public:
  MyClass(int aVar, int aVar2)
    : mVar(aVar)
    , mVar2(aVar2)
  {
     ...
  }

  // Les constructeurs et destructeurs courts peuvent être écrits sur une seule ligne.
  MyClass() { }

  // A moins qu'il s'agisse d'un constructeur de copie ou de déplacement (copy or move constructor)
  // ou que vous ayiez une bonne raison d'autoriser la conversion implicite
  // définissez tous les constructeurs prenant un argument unique comme explicites.
  explicit MyClass(OtherClass aArg)
  {
    ...
  }

  // Ce constructeur accepte également un argument unique,
  // donc il est également défini comme explicite.
  explicit MyClass(OtherClass aArg, AnotherClass aArg2 = AnotherClass())
  {
    ...
  }
  
  int TinyFunction() { return mVar; }  // Les fonctions courtes peuvent être écrites sur une seule ligne.

  int LargerFunction()
  {
    ...
    ...
  }

private:
  int mVar;
};

} // namespace mozilla

Définissez les classes selon le style utilisé ci-dessus.

Les classes situées dans l'espace de noms global sont nommées avec un préfixe court en guise de pseudo espace de noms (par exemple "ns").

Methods and functions

Pour les fonctions, constructeurs, ou autres constructions entre accolades, il est possible de les écrire sur une ligne comme pour la fonction TinyFunction. Lorsqu'ils sont plus longs, on utilise on utilise les règles suivantes.

C/C++

En C/C++, les noms de méthodes commencent par une lettre majuscule et utilisent la convention CamelCase.

template<typename T>  // Template sur une ligne.
static int            // Valeur de retour sur une ligne pour la fonction de premier niveau.
MyFunction(...)
{
  ...
}

int
MyClass::Method(...)
{
  ...
}

Les accesseurs qui n'échouent jamais et ne retournent jamais null sont nommés Foo(), while tandis que les autres accesseurs s'intitulent GetFoo(). Les accesseurs peuvent renvoyer la valeur d'un objet via un paramètre de sortie Foo** aResult (typiquement pour un accesseur XPCOM) ou already_AddRefed<Foo> (typiquement pour un accesseur WebIDL, potentiellement avec un paramètre ErrorResult& rv) ou occasionnellement Foo* (typiquement pour un accesseur interne pour un objet dont la durée de vie est connue). Voir bug 223255 pour davantage d'information.

Les accesseurs XPCOM renvoient toujours des valeurs primitives via un paramètre de sortie tandis que les autres accesseurs utilisent une valeur de retour.

Les déclarations de méthodes doivent utiliser au moins l'un des mots-clé suivants: virtual, override, ou final. Utilisez virtual pour déclarer des méthodes virtuelles, qui ne surchargent pas une méthode de la classe de base avec la même signature. Utilisez override pour déclarer des méthodes virtuelles qui ne surchargent pas une méthode de la classe de base avec la même signature, et peuvent être surchargées dans des classes dérivées.  Utilisez final pour déclarer des méthodes virtuelles qui ne surchargent pas une méthode de la classe de base avec la même signature, mais ne peuvent pas être surchargées dans des classes dérivées.  Cela devrait aider une personne qui lit votre code à comprendre ce que la déclaration fait exactement sans avoir besoin d'inspecter les classes de base.

JavaScript

En JavaScript, les fonctions suivant la convention camelCase avec la première lettre en minuscule. Les méthodes ne doivent pas utiliser la syntaxe des expressions nommées, car nos outils comprennent les noms de méthodes:

doSomething: function (aFoo, aBar) {
  ...
}

Les fonctions sur une ligne doivent avoir des espaces avant et après les accolades, excepté après une virgule ou un point virgule :

function valueObject(aValue) { return { value: aValue }; }

JavaScript objects

var foo = { prop1: "value1" };

var bar = {
  prop1: "value1",
  prop2: "value2"
}; 

Les constructeurs d'objets doivent commencer par une une majuscule et suivre la convention CamelCase:

function ObjectConstructor() {
  this.foo = "bar";
}

Mode line

Les commentaires de type mode line pour Emacs ou vim doivent être indiqués sur les deux premières lignes du fichier, et définir indent-tabs-mode à nil. Pour les nouveaux fichiers, utilisez le code suivant:

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

Assurez-vous d'indiquer le bon "Mode" sur la première ligne — n'utilisez pas "C++" dans les fichiers JavaScript. Pour le code Python, définissez quatre espaces pour l'indentation.

Déclarations

En général, l'étoile indiquant le pointeur est collée au type, pas au nom de la variable:

T* p; // BIEN
T *p; // PAS BIEN
T* p, q; // OOPS mettre chaque variable sur une ligne

Certains modules déjà existants utilisent le style T *p .

Opérateurs

Les expressions particulièrement longues ne comprenant pas de && ou || doivent être coupées de façon à ce que l'opérateur commence sur la seconde ligne, aligné avec le début de l'expression sur la ligne précédente. Cela s'applique à ? et :, aux opérateurs binaires comme +, et aux opérateurs d'appartenance (en particulier le . en JavaScript, voir la Rationale).

Rationale: mettre l'opérateur au début de la ligne de continuation permet une lecture plus rapide, car il indique qu'il n'est pas nécessaire de lire la ligne précédente juqu'à la fin. De plus il existe un risque de sensibilité au contexte en Javascript, qui peut être évité ainsi ; voir bug 442099, comment 19.

En JavaScript, == est préférable à ===.

L'opérande des opérateurs unaires tels que typeof et sizeof, doit être entre parenthèses; e.g. typeof("foo") == "string".

Littéraux

En JavaScript, utilisez plutôt des guillemets doubles (e.g. "foo") que des guillemets simples (e.g. 'foo'), sauf quand c'est nécessaire pour éviter d'échapper des guillemets à l'intérieur d'une chaîne, ou pour assigner des gestionnaires d'évènement.

Utilisez l'échappement unicode de type \uXXXX pour les caractères non-ASCII. L'encodage pour XUL, les DTD, les scripts, et les fichiers de propriétés est l'UTF-8.

Préfixes

Préfixes variables
  • k=constant (e.g. kNC_child). Not all code uses this style; some uses ALL_CAPS for constants.
  • g=global (e.g. gPrefService)
  • a=argument (e.g. aCount)
  • C++ Specific Prefixes
    • s=static member (e.g. sPrefChecked)
    • m=member (e.g. mLength)
    • e=enum values (e.g. enum class Foo { eBar, eBaz })
  • JavaScript Specific Prefixes
    • _=member (variable or function) (e.g. _length or _setType(aType))
    • k=enumeration value (e.g. const kDisplayModeNormal = 0)
    • on=event handler (e.g. function onLoad())
    • Convenience constants for interface names should be prefixed with nsI:
      const nsISupports = Components.interfaces.nsISupports;
      const nsIWBN = Components.interfaces.nsIWebBrowserNavigation;
Global functions/macros/etc
  • Macros begin with MOZ_, and are all caps (e.g. MOZ_WOW_GOODNESS). Note that older code uses the NS_ prefix; while these aren't being changed, you should only use MOZ_ for new macros. The only exception is if you're creating a new macro which is part of a set of related macros that still use the old NS_ prefix. Then you should be consistent with the existing macros.
Error Variables
  • local nsresult result codes should be `rv`. `rv` should not be used for bool or other result types.
  • local bool result codes should be `ok`

General practices

  • Don't put an else right after a return, break or continue. Delete the else, it's unnecessary and increases indentation level.
  • Don't leave debug printfs or dumps lying around.
  • Use Unix-style carriage returns ("\n") rather than Windows/DOS ones ("\r\n").  You can convert patches with DOS newlines to Unix via the 'dos2unix' utility, or your favorite text editor.
  • Use two spaces for indentation.
  • Use JavaDoc-style comments.
  • When fixing a problem, check to see if the problem occurs elsewhere in the same file, and fix it everywhere if possible.
  • End the file with a newline (make sure your patches don't contain files with the text "no newline at end of file" in them).
  • Declare local variables as near to their use as possible.
  • For new files, be sure to use the right license boilerplate per our license policy.

C/C++ practices

  • Have you checked for compiler warnings? Warnings often point to real bugs.
  • In C++ code, use nullptr for pointers. In C code, using NULL or 0 is allowed.
  • Don't use PRBool and PRPackedBool in C++, use bool instead.
  • When testing a pointer, use (!myPtr) or (myPtr); don't use myPtr != nullptr or myPtr == nullptr.
  • Do not compare x == true or x == false. Use (x) or (!x) instead. x == true, in fact, is different from if (x)!
  • In general, initialize variables with nsFoo aFoo = bFoo and not nsFoo aFoo(bFoo).
    • For constructors initialize member variables with : nsFoo aFoo(bFoo) syntax
  • To avoid warnings created by variables used only in debug builds, use the DebugOnly<T> helper when declaring them.
  • You should use the static preference API for working with preferences.
  • One-argument constructors that are not copy or move constructors should generally be marked explicit.  Exceptions should be annotated with MOZ_IMPLICIT.
  • Forward-declare classes in your header files instead of including them whenever possible. For example, if you have an interface with a void DoSomething(nsIContent* aContent) function, forward-declare with class nsIContent; instead of #include "nsIContent.h"
  • Includes are split into three blocks and are sorted alphabetically in each block:
    1. The main header: #include "Foo.h" in Foo.cpp
    2. Standard library includes: #include <map>
    3. Mozilla includes: #include "mozilla/dom/Element.h"
  • Include guards are named by determining the fully-qualified include path, then substituting _ for / and . and - in it. For example, nsINode.h's guard is nsINode_h, and Element.h's guard is mozilla_dom_Element_h (because its include path is mozilla/dom/Element.h).

    Use the following exact form for include guards. GCC and clang recognize this idiom and avoid re-reading headers that use it. To avoid confusing GCC's and clang's header optimization, do not include any code before or after the include guards (but comments and whitespace are OK). Do not combine any other preprocessor checks in the #ifndef <guard> expression.

    #ifndef <guard>
    #define <guard>
    ... All code ...
    #endif // <guard>
    

JavaScript practices

  • Make sure you are aware of the JavaScript Tips.
  • Do not compare x == true or x == false. Use (x) or (!x) instead. x == true, in fact, is different from if (x)! Compare objects to null, numbers to 0 or strings to "" if there is chance for confusion.
  • Make sure that your code doesn't generate any strict JavaScript warnings, such as:
    • Duplicate variable declaration
    • Mixing return; with return value;
    • Undeclared variables or members. If you are unsure if an array value exists, compare the index to the array's length. If you are unsure if an object member exists, use "name" in aObject, or if you are expecting a particular type you may use typeof(aObject.name) == "function" (or whichever type you are expecting).
  • Use [value1, value2] to create a JavaScript array in preference to using new Array(value1, value2) which can be confusing, as new Array(length) will actually create a physically empty array with the given logical length, while [value] will always create a 1-element array. You cannot actually guarantee to be able to preallocate memory for an array.
  • Use { member: value, ... } to create a JavaScript object; a useful advantage over new Object() is the ability to create initial properties and use extended JavaScript syntax to define getters and setters.
  • If having defined a constructor you need to assign default properties it is preferred to assign an object literal to the prototype property.
  • Use regular expressions, but use them wisely. For instance, to check that aString is not completely whitespace use /\S/.test(aString); only use aString.search if you need to know the position of the result, or aString.match if you need to collect matching substrings (delimited by parentheses in the regular expression). Regular expressions are less useful if the match is unknown in advance, or to extract substrings in known positions in the string. For instance, aString.slice(-1) returns the last letter in aString, or the empty string if aString is empty.

Java practices

  • We use the Java Coding Style. Quick summary:
    • FirstLetterUpperCase for class names
    • camelCase for method and variable names
    • One declaration per line:
      int x, y; // this is BAD!
      int a;    // split it over
      int b;    // two lines
      
  • Braces should be placed like so (generally opening braces on same line, closing braces on a new line):
    public void func(int arg) {
        if (arg != 0) {
            while (arg > 0) {
                arg--;
            }
        } else {
            arg++;
        }
    }
    
  • Places we differ from the Java coding style:
    • Start class variable names with 'm' prefix (e.g. mSomeClassVariable) and static variables with 's' prefix (e.g. sSomeStaticVariable)
    • import statements:
      • Do not use wildcard imports like `import java.util.*;`
      • Organize imports by blocks separated by empty line: org.mozilla.*, com.*, net.*, org.*, android.*, then java.*
      • Within each import block, alphabetize import names with uppercase before lowercase (so `com.example.Foo` is before `com.example.bar`).
    • 4-space indents
    • spaces, not tabs
    • Don't restrict yourself to 80-character lines. Google's Android style guide suggests 100-character lines. Java code tends to be long horizontally, so use appropriate judgement when wrapping. Avoid deep indents on wrapping. Note that aligning the wrapped part of a line with some previous part of the line (rather than just using a fixed indent) may require shifting the code every time the line changes, resulting in spurious whitespace changes.
  • For additional specifics on Firefox for Android, see the Coding Style guide for Firefox on Android.
  • The Android Coding Style has some useful guidelines too.

Makefile/moz.build practices

  • Changes to makefile and moz.build variables do not require build-config peer review. Any other build system changes such as adding new scripts or rules require review from the build-config team.
  • Suffix long if/endif conditionals with #{ & #} so editors can display matching tokens that enclose a block of statements.
    ifdef CHECK_TYPE #{
      ifneq ($(flavor var_type),recursive) #{
        $(warning var should be expandable but detected var_type=$(flavor var_type))
      endif #}
    endif #}
  • moz.build are python and follow normal Python style
  • List assignments should be written with one element per line.  Align closing square brace with start of variable assignment. If ordering is not important, variables should be in alphabetical order.
    var += [
        'foo',
        'bar'
    ]
  • Use CONFIG['CPU_ARCH'] {=arm} to test for generic classes of architecure rather than CONFIG['OS_TEST'] {=armv7} (re: bug 886689)

Python practices

  • Install the mozext Mercurial extension and address every issue reported on commit, qrefresh, or the output of hg critic.
  • Follow PEP 8.
  • Do not place statements on the same line as if/elif/else conditionals to form a one-liner.
  • Global vars, avoid them at all cost.
  • Exclude outer parenthesis from conditionals.  Use if x > 5: rather than if (x > 5):
  • Use string formatters rather than var + str(val).  var = 'Type %s value is %d' % ('int', 5)
  • Utilize tools like pylint or pychecker to screen sources for common problems.
  • Testing/Unit tests, write them.
  • See also Code Like a Pythonista.

SVG practices

Check SVG Guidelines for more details.

COM, pointers and strings

  • Use nsCOMPtr<>
    If you don't know how to use it, start looking in the code for examples. The general rule is that the very act of typing NS_RELEASE should be a signal to you to question your code: "Should I be using nsCOMPtr here?". Generally the only valid use of NS_RELEASE are when you are storing refcounted pointers in a long-lived datastructure.
  • Declare new XPCOM interfaces using XPIDL so they will be scriptable.
  • Use nsCOMPtr for strong references, and nsWeakPtr for weak references.
  • String arguments to functions should be declared as nsAString.
  • Use EmptyString() and EmptyCString() instead of NS_LITERAL_STRING("") or nsAutoString empty;.
  • Use str.IsEmpty() instead of str.Length() == 0.
  • Use str.Truncate() instead of str.SetLength(0) or str.Assign(EmptyString()).
  • Don't use QueryInterface directly. Use CallQueryInterface or do_QueryInterface instead.
  • nsresult should be declared as rv. Not res, not result, not foo.
  • For constant strings, use NS_LITERAL_STRING("...") instead of NS_ConvertASCIItoUCS2("..."), AssignWithConversion("..."), EqualsWithConversion("..."), or nsAutoString()
  • To compare a string with a literal, use .EqualsLiteral("...").
  • Use Contract IDs instead of CIDs with do_CreateInstance/do_GetService.
  • Use pointers instead of references for function out parameters, even for primitive types.

IDL

Use leading-lowercase, or "interCaps"

When defining a method or attribute in IDL, the first letter should be lowercase, and each following word should be capitalized. For example:

long updateStatusBar();

Use attributes wherever possible

Whenever you are retrieving or setting a single value without any context, you should use attributes. Don't use two methods when you could use one attribute. Using attributes logically connects the getting and setting of a value, and makes scripted code look cleaner.

This example has too many methods:

interface nsIFoo : nsISupports
{
    long getLength();
    void setLength(in long length);
    long getColor();
};

The code below will generate the exact same C++ signature, but is more script-friendly.

interface nsIFoo : nsISupports
{
    attribute long length;
    readonly attribute long color;
};

Use Java-style constants

When defining scriptable constants in IDL, the name should be all uppercase, with underscores between words:

const long ERROR_UNDEFINED_VARIABLE = 1;

See also

For details on interface development, as well as more detailed style guides, see the Interface development guide.

Error handling

Check for errors early and often

Every time you make a call into an XPCOM function, you should check for an error condition. You need to do this even if you know that call will never fail. Why?

  • Someone may change the callee in the future to return a failure condition.
  • The object in question may live on another thread, another process, or possibly even another machine. The proxy could have failed to actually make your call in the first place.

Also, when you make a new function which is failable (i.e. it will return a nsresult or a bool that may indicate an error), you should explicitly mark the return value should always be checked, e.g.:

// for IDL
[must_use] nsISupports
create();

// for C++, add this in *declaration*, do not add it again in implementation
MOZ_MUST_USE nsresult
DoSomething();

There are some exceptions:

  • Predicates or getters which return bool or nsresult
  • IPC method implementation (e.g. bool RecvSomeMessage())
  • Most callers will check the output parameter, see below
nsresult
SomeMap::GetValue(const nsString& key, nsString& value);

If most callers need to check the output value first, then adding MOZ_MUST_USE might be too verbose. In this case, change the return value to void might be a reasonable choice.

There is also a static analysis attribute MOZ_MUST_USE_TYPE, which can be added to class declarations to ensure that those declarations are always used when they are returned.

Use the NS_WARN_IF macro when errors are unexpected.

The NS_WARN_IF macro can be used to issue a console warning in debug builds if the condition fails. This should only be used when the failure is unexpected and cannot be caused by normal web content.

If you are writing code that wants to issue warnings when methods fail, please either use NS_WARNING directly or use the new NS_WARN_IF macro.

if (NS_WARN_IF(somethingthatshouldbefalse)) {
  return NS_ERROR_INVALID_ARG;
}

if (NS_WARN_IF(NS_FAILED(rv))) {
  return rv;
}

Previously the NS_ENSURE_* macros were used for this purpose, but those macros hide return statements and should not be used in new code. (This coding style rule isn't generally agreed, so use of NS_ENSURE_* can be valid.)

Return from errors immediately

In most cases, your knee-jerk reaction should be to return from the current function when an error condition occurs. Don't do this:

rv = foo->Call1();
if (NS_SUCCEEDED(rv)) {
  rv = foo->Call2();
  if (NS_SUCCEEDED(rv)) {
    rv = foo->Call3();
  }
}
return rv;

Instead, do this:

rv = foo->Call1();
if (NS_FAILED(rv)) {
  return rv;
}

rv = foo->Call2();
if (NS_FAILED(rv)) {
  return rv;
}

rv = foo->Call3();
if (NS_FAILED(rv)) {
  return rv;
}

Why? Because error handling should not obfuscate the logic of the code. The author's intent in the first example was to make 3 calls in succession, but wrapping the calls in nested if() statements obscured the most likely behavior of the code.

Consider a more complicated example that actually hides a bug:

bool val;
rv = foo->GetBooleanValue(&val);
if (NS_SUCCEEDED(rv) && val) {
  foo->Call1();
} else {
  foo->Call2();
}

The intent of the author may have been that foo->Call2() would only happen when val had a false value. In fact, foo->Call2() will also be called when foo->GetBooleanValue(&val) fails. This may or may not have been the author's intent, and it is not clear from this code. Here is an updated version:

bool val;
rv = foo->GetBooleanValue(&val);
if (NS_FAILED(rv)) {
  return rv;
}
if (val) {
  foo->Call1();
} else {
  foo->Call2();
}

In this example, the author's intent is clear, and an error condition avoids both calls to foo->Call1() and foo->Call2();

Possible exceptions: Sometimes it is not fatal if a call fails. For instance, if you are notifying a series of observers that an event has fired, it might be inconsequential that one of these notifications failed:

for (size_t i = 0; i < length; ++i) {
  // we don't care if any individual observer fails
  observers[i]->Observe(foo, bar, baz);
}

Another possibility is that you are not sure if a component exists or is installed, and you wish to continue normally if the component is not found.

nsCOMPtr<nsIMyService> service = do_CreateInstance(NS_MYSERVICE_CID, &rv);
// if the service is installed, then we'll use it
if (NS_SUCCEEDED(rv)) {
  // non-fatal if this fails too, ignore this error
  service->DoSomething();

  // this is important, handle this error!
  rv = service->DoSomethingImportant();
  if (NS_FAILED(rv)) {
    return rv;
  }
}
    
// continue normally whether or not the service exists

C++ strings

Use the Auto form of strings for local values

When declaring a local, short-lived nsString class, always use nsAutoString or nsAutoCString - these versions pre-allocate a 64-byte buffer on the stack, and avoid fragmenting the heap. Don't do this:

nsresult
foo()
{
  nsCString bar;
  ..
}

instead:

nsresult
foo()
{
  nsAutoCString bar;
  ..
}

Be wary of leaking values from non-XPCOM functions that return char* or PRUnichar*

It is an easy trap to return an allocated string from an internal helper function, and then use that function inline in your code without freeing the value. Consider this code:

static char*
GetStringValue()
{
  ..
  return resultString.ToNewCString();
}

  ..
  WarnUser(GetStringValue());

In the above example, WarnUser will get the string allocated from resultString.ToNewCString() and throw away the pointer. The resulting value is never freed. Instead, either use the string classes to make sure your string is automatically freed when it goes out of scope, or make sure that your string is freed.

Automatic cleanup:

static void
GetStringValue(nsAWritableCString& aResult)
{
  ..
  aResult.Assign("resulting string");
}

  ..
  nsAutoCString warning;
  GetStringValue(warning);
  WarnUser(warning.get());

Free the string manually:

static char*
GetStringValue()
{
  ..
  return resultString.ToNewCString();
}

  ..
  char* warning = GetStringValue();
  WarnUser(warning);
  nsMemory::Free(warning);

Use MOZ_UTF16() or NS_LITERAL_STRING() to avoid runtime string conversion

It is very common to need to assign the value of a literal string such as "Some String" into a unicode buffer. Instead of using nsString's AssignLiteral and AppendLiteral, use NS_LITERAL_STRING() instead. On most platforms, this will force the compiler to compile in a raw unicode string, and assign it directly.

Incorrect:

nsAutoString warning;
warning.AssignLiteral("danger will robinson!");
...
foo->SetStringValue(warning);
...
bar->SetUnicodeValue(warning.get());

Correct:

NS_NAMED_LITERAL_STRING(warning, "danger will robinson!");
...
// if you'll be using the 'warning' string, you can still use it as before:
foo->SetStringValue(warning);
...
bar->SetUnicodeValue(warning.get());

// alternatively, use the wide string directly:
foo->SetStringValue(NS_LITERAL_STRING("danger will robinson!"));
...
bar->SetUnicodeValue(MOZ_UTF16("danger will robinson!"));

Note: named literal strings cannot yet be static.

Usage of PR_(MAX|MIN|ABS|ROUNDUP) macro calls

Use the standard-library functions (std::max) instead of PR_(MAX|MIN|ABS|ROUNDUP).

Use mozilla::Abs instead of PR_ABS. All PR_ABS calls in C++ code have been replaced with mozilla::Abs calls in bug 847480. All new code in Firefox/core/toolkit needs to #include "nsAlgorithm.h" and use the NS_foo variants instead of PR_foo, or #include "mozilla/MathAlgorithms.h" for mozilla::Abs.

Étiquettes et contributeurs liés au document

 Contributeurs à cette page : marie-ototoi, sylvestre
 Dernière mise à jour par : marie-ototoi,