Перевод не завершен. Пожалуйста, помогите перевести эту статью с английского.
Этот документ объяcняет основные стили и шаблоны, которые используются в коде Mozilla. Новый код должен соответствовать этим нормам, чтобы было проще поддерживать существующий код. Конечно, для каждого правила возможно исключение, но тем не менее важно знать эти правила!
Данный документ в основном направлен на новых людей в Mozilla, которые занимаются code review. Перед тем как сделать review, пожалуйста прочитайте этот документ и убедитесь, что ваш код соответствует рекомендациям.
Общий стиль
- Не ставьте
else
сразу послеreturn
. Удаляйтеelse
, в этом случае оно не нужно , зато увеличиваются количество отступов. - Не оставляйте за собой отладочные
printf
и прочий мусор. - Используйте Unix-формат символа возврата каретки ("\n"), а не формат Windows/DOS ("\r\n"). Конвертацию DOS-форматирования в Unix можно выполнить при помощи утилиты 'dos2unix', или же используйте Ваш любимый текстовый редактор.
- Для отступов используйте два пробела.
- Используйте JavaDoc-стиль комментариев.
- Если Вы исправили ошибку, проверьте, встречается ли еще такая проблема в этом файле и, по-возможности, исправьте ее во всех случаях.
- Завершайте файл символом новой строки (убедитесь, что Ваши патчи не содержат файлы с текстом "no newline at end of file").
- Объявляйте локальные переменные как можно ближе к месту их использования.
- Для новых файлов, убедитесь в использовании верного шаблона лицензии исходя из нашей политики лицензий.
C/C++ стиль
- Вы проверили предупреждения компилятора? Предупреждения часто указывают на реальные баги.
- В C++ используйте
nullptr
для указателей. В Cи допускается использованиеNULL
или0
. - Не используйте
PRBool
иPRPackedBool
в C++, используйтеbool
. - При проверке указателя, пишите
(
!myPtr
)
или(myPtr)
; не пишитеmyPtr !=
nullptr
илиmyPtr ==
nullptr
. - Не пишите
x == true
илиx == false
. Пишите(x)
или(!x)
.x == true
, на самом деле, отличается от if(x)
! - В рядовых случаях, инициализируйте переменные так:
nsFoo aFoo = bFoo
, а не так: nsFoo aFoo(bFoo). - Чтобы избежать предупреждений компилятора из-за переменных, используемых только в debug-билдах, при их декларировании используйте хелпер
DebugOnly<T>
. - 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 withclass nsIContent;
instead of#include "nsIContent.h"
- Includes are split into three blocks and are sorted alphabetically in each block:
- The main header: Foo.h in Foo.cpp
- Standard library includes:
#include <map>
- 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
orx == false
. Use(x)
or(!x)
instead.x == true
, in fact, is different from if(x)
! Compare objects tonull
, numbers to0
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;
withreturn 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 usetypeof(aObject.name) == "function"
(or whichever type you are expecting).
- Use
[value1, value2]
to create a JavaScript array in preference to usingnew Array(value1, value2)
which can be confusing, asnew 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 overnew 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 useaString.search
if you need to know the position of the result, oraString.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 inaString
, or the empty string ifaString
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 thanif (x > 5):
- Use string formatters rather than var + str(val).
var = 'Type %s value is %d' % ('int', 5)
- Utilize tools like
pylint
orpychecker
to screen sources for common problems. - Testing/Unit tests, write them.
- See also Code Like a Pythonista.
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 typingNS_RELEASE
should be a signal to you to question your code: "Should I be usingnsCOMPtr
here?". Generally the only valid use ofNS_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()
andEmptyCString()
instead ofNS_LITERAL_STRING("")
ornsAutoString empty
;. - Use
str.IsEmpty()
instead ofstr.Length() == 0
. - Use
str.Truncate()
instead ofstr.SetLength(0)
orstr.Assign(EmptyString())
. - Don't use
QueryInterface
directly. UseCallQueryInterface
ordo_QueryInterface
instead. nsresult
should be declared asrv
. Not res, not result, not foo.- For constant strings, use
NS_LITERAL_STRING("...")
instead ofNS_ConvertASCIItoUCS2("...")
,AssignWithConversion("...")
,EqualsWithConversion("...")
, ornsAutoString()
- 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.
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(somethingthatshouldbetrue)) { 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.
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 nsCAutoString
- 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() { nsCAutoString 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"); } .. nsCAutoString 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.
Naming and formatting code
The following norms should be followed for new code, and for Tower of Babel code that needs cleanup. For existing code, use the prevailing style in a file or module, or ask the owner if you are on someone else's turf and it's not clear what style to use.
Whitespace
No tabs. No whitespace at the end of a line.
Line length
80 characters or less (for laptop side-by-side diffing and two-window tiling; also for Bonsai / hgweb and hardcopy printing).
Indentation
Two spaces per logic level, or four spaces in Python code.
Note that class visibility and goto labels do not consume a logic level, but switch case labels do. See examples below.
Control structures
Use K&R bracing style: left brace at end of first line, cuddle else on both sides.
Always brace controlled statements, even a single-line consequent of an if else else
. This is redundant typically, but it avoids dangling else bugs, so it's safer at scale than fine-tuning.
Break long conditions after &&
and ||
logical connectives. See below for the rule for other operators.
Examples:
if (...) { } else if (...) { } else { } while (...) { } do { } while (...); for (...; ...; ...) { } switch (...) { case 1: { // When you need to declare a variable in a switch, put the block in braces int var; break; } case 2: ... break; default: break; }
Control keywords if
, for
, while
, and switch
are always followed by a space to distinguish them from function calls which have no trailing space.
C++ namespaces
Mozilla project C++ declarations should be in the "mozilla" namespace. Modules should avoid adding nested namespaces under "mozilla" unless they are meant to contain names which have a high probability of colliding with other names in the code base (e.g., Point, Path, etc.). Such symbols can be put under module-specific namespaces under "mozilla", with short all-lowercase names. Other global namespaces besides "mozilla" are not allowed.
No "using" statements are allowed in header files, except inside class definitions or functions. (We don't want to pollute the global scope of compilation units that use the header file.)
using namespace ...;
is only allowed in .cpp files after all #include
s. Prefer to wrap code in namespace ... { ... };
instead if possible. using namespace ...;
should always specify the fully qualified namespace. That is, to use Foo::Bar
do not write using namespace Foo;
using namespace Bar;
, write using namespace Foo::Bar;
Don't indent code inside namespace ... { ... }
. You can prevent this inside emacs by setting the "innamespace" offset:
(c-set-offset 'innamespace 0)
Anonymous namespaces
We prefer using 'static' instead of anonymous C++ namespaces. This may change once there is better debugger support (on Windows especially) for placing breakpoints, etc on code in anonymous namespaces. You may still use anonymous namespaces for things that can't be hidden with 'static' (such as types, or certain objects that need to be passed to template functions).
C++ classes
namespace mozilla { class MyClass : public A { }; class MyClass : public X // when deriving from more than one class, put each on its own line , public Y { public: MyClass() : mVar(0) , mVar2(0) { ... } int TinyFunction() { return mVar; } // Tiny functions can be written in a single line int LargerFunction() { ... ... } private: int mVar; }; } // namespace mozilla
Existing classes in the global namespace are named with a short prefix (e.g. "ns") as a pseudo-namespace.
For small functions in a class declaration, it's OK to do the above. For larger ones use something similar to method declarations below.
Methods and functions
C/C++
In C/C++, method names should be capitalized and use CamelCase.
int MyFunction(...) { ... } int MyClass::Method(...) { ... }
Getters that never fail and never return null are named Foo()
, while all other getters use GetFoo()
. Getters can return an object value via a Foo** aResult
outparam (typical for an XPCOM getter) or as an already_AddRefed<Foo>
(typical for a WebIDL getter, possibly with an ErrorResult& rv
parameter) or occasionally as a Foo*
(typical for an internal getter for an object with a known lifetime). (See баг 223255 for more information.) XPCOM getters always return primitive values via an outparam while other getters normally use a return value.
Method declarations that override virtual methods from a base class should be annotated with both "virtual" and "MOZ_OVERRIDE".
JavaScript
In JavaScript, functions should use camelCase but should not capitalize the first letter. Methods should not use the named function expression syntax, because our tools understand method names:
doSomething: function (aFoo, aBar) { ... }
In-line functions should have spaces around braces, except before commas or semicolons:
function valueObject(aValue) { return { value: aValue }; }
JavaScript objects
var foo = { prop1: "value1" }; var bar = { prop1: "value1", prop2: "value2" };
Constructors for objects should be capitalized and use CamelCase:
function ObjectConstructor() { this.foo = "bar"; }
Mode line
Files should have Emacs and vim mode line comments as the first two lines of the file, which should set indent-tabs-mode to nil. For new files, use this, specifying two-space indentation:
/* -*- 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 https://mozilla.org/MPL/2.0/. */
Make sure to use the correct "Mode" in the first line—don't use "C++" in JavaScript files. The exception is Python code, in which we use four spaces for indentations.
Declarations
In general, snuggle pointer stars with the type, not the variable name:
T* p
; // GOOD
T *p; // BAD
T* p, q; // OOPS put these on separate lines
Some existing modules still use the T *p
style, such as the SpiderMonkey JS engine.
Operators
Overlong expressions not joined by &&
and ||
should break so the operator starts on the second line and starts in the same column as the start of the expression in the first line. This applies to ?:
, binary arithmetic operators including +
, and member-of operators (in particular the .
operator in JS, see the Rationale).
Rationale: operator at the front of the continuation line makes for faster visual scanning, because there is no need to read to end of line. Also there exists a context-sensitive keyword hazard in JS, see https://bugzilla.mozilla.org/show_bug.cgi?id=442099#c19, which can be avoided by putting . at the start of a continuation line in long member expression.
In JavaScript, ==
is preferred to ===
.
Unary keyword operators, such as typeof
and sizeof
, should have their operand parenthesized; e.g. typeof("foo") == "string"
.
Literals
Double-quoted strings (e.g. "foo"
) are preferred to single-quoted strings (e.g. 'foo'
) in JavaScript, except to avoid escaping of embedded double quotes or when assigning inline event handlers.
Use \uXXXX
unicode escapes for non-ASCII characters. The character set for XUL, DTD, script, and properties files is UTF-8, which is not easily readable.
Prefixes
Follow these naming prefix conventions:
Variable prefixes
- k=constant (e.g.
kNC_child
). Not all code uses this style; some usesALL_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
)
- s=static member (e.g.
- 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()
)
- _=member (variable or function) (e.g.
nsI
:const nsISupports = Components.interfaces.nsISupports;
const nsIWBN = Components.interfaces.nsIWebBrowserNavigation;
Global functions/macros/etc
- Macros begin with
NS_
, and are all caps (e.g.NS_IMPL_ISUPPORTS
)
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`
Usage of PR_(MAX|MIN|ABS|ROUNDUP) macro calls
Use the stanard-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
.