Flock | the social web browser

Code Style

(Redirected from Writing Guidelines)

Contents

[edit] Introduction

This guide is initially intended for code originated by Flock.

When editing existing scripts, the most important rule is to be consistent with the prevailing style, even when it's at odds with this document. If you're doing a significant refactor however, consider making the code match the style documented here. You may also pick a file and make a stylistic-only commit, to make it match the guidelines in this document. Do not mix minor functional code changes with style changes. Do not change the style in files that originate from upstream Firefox, since that makes merges much harder.

This is based on Mozilla's style guide.

[edit] Whitespace

  • The basic indentation is two spaces. Tabs are not to be used at all.
  • Try to keep lines to 80 characters or less. When wrapping lines, try to indent to line up with a related item on the previous line. See Code_Style#Indentation_examples.
  • Lines should not contain trailing spaces, even after binary operators, commas or semicolons. One exception is the license block, that has one trailing space on each empty line.
  • Separate binary operators with spaces.
  • Spaces after commas and semicolons, but not before.
  • Spaces after keywords, e.g. if (x > 0).
  • One (or two) blank lines between block definitions. Also consider breaking up large code blocks with blank lines.
  • End the file with a newline. (This applies mainly to emacs and Eclipse users.)
  • Use Unix-style line endings unless DOS style are explicitly required.
  • Set the SVN property svn:eol-style to native on all plain text files.
svn propset svn:eol-style native foo.txt
  • Do not add extraneous spaces and don't align symbols together. Aligning symbols improves readability however, it messes with svn blame.
const FOO1 = 1;
const FOO_BAR = "foo bar";
const FOO3 = 3;
const FOOBAR1 = "foobar1";
const BAR = 2;

[edit] Symbols

  • Spaces around braces used for in-line functions or objects, except before commas or semicolons, e.g.
function valueObject(aValue) {
  return { value: aValue };
}
  • Do not add unnecessary spaces inside brackets e.g. parameter lists, array subscripts. This includes wrapping an in-line JavaScript object in parentheses. The only exception is when defining Hash elements.
var aHash = { key1: value1, key2: value2, ..., keyN: valueN };
  • Prefer double quotes, except in in-line event handlers or when quoting double quotes.
  • Always use begin and end braces even for single line statements.
  • The opening brace is always on the same line as the if statement except when you have multiple arguements.
if (arg1 ||
    arg2 < arg1 ||
    arg3)
{
  doSomething();
}
  • || and && are listed at the end of a line when breaking up logic.
  • Do not combine code statements on a single line of code; this makes code difficult to read and more work to maintain.
if (dlmgrWindow) {
  return;
}
  • Braces are not indented relative to their parent statement. Stick to the style used in existing files, but when creating new files you may choose your favorite of the following acceptable constructs:
if (dlmgrWindow) {
  dlmgrWindow.focus();
}

if (dlmgrWindow) {
  dlmgrWindow.focus();
} else {
  dlmgr.open(window, null);
}
  • Do not include empty else statements.
  • else statements must be preceded by a closing brace and ended with an opening brace when used.
  • 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.

[edit] Code style

  • Always put else on its own line, as shown above.
  • Don't use else after return, i.e.
if (x < y)
  return -1;
if (x > y)
  return 1;
return 0;
  • Both i++ and ++i are acceptable.
  • Name inline functions, this makes it easier to debug them. Just assigning a function to a property doesn't name the function, you should to do this:
var offlineObserver = {
  observe: function OO_observe(aSubject, aTopic, aState) {
    if (aTopic == "network:offline-status-changed")
      setOfflineUI(aState == "offline");
  }
};

[edit] Function and variable naming

  • All constants should be in UPPER_CASE.
  • Use InterCaps and capitalize the first letter of constructors.
  • Enumeration values should be prefixed with the letter k, e.g. const kDisplayModeNormal = 0;.
  • Global variables should be prefixed with the letter g, e.g. gFormatToolbar.
  • Arguments (parameter names) should be prefixed with the letter a.
  • Arguments passed in should never be modified. Copy the argument to a local variable before modifying it.
  • Private members should start with _, e.g. _internalFunction
  • Event handler functions should be prefixed with the word on, in particular try to use the names onLoad, onDialogAccept, onDialogCancel, etc., where this is unambiguous.
  • Function names, local variables, and object members have no prefix.
  • Try to declare local variables as near to their use as possible; try to initialize every variable.
  • When inserting code with an XUL overlay, wrap functions and variables inside an object with a unique name to avoid conflicting with existing or future function and variable names
var UniqueName = {
  _privateMember: 3,
  publicMember: "A string",

  init: function() {
    this.doSomething(this.anotherMember);
  },

  doSomething: function(aParam) {
    alert(aParam);
  }
};

[edit] JavaScript features

  • Make sure that your code doesn't generate any strict JavaScript warnings, such as:
    • Duplicate variable declaration
    • Mixing return; with return value; (use return null; instead of return;)
    • Have a function that sometimes returns a value, sometimes doesn't (e.g. a return in the "if" but not in the "else")
    • Trailing comma in JavaScript object declarations
    • 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. For example,
function SupportsString(data) {
  this.data = data;
}
SupportsString.prototype = {
  toString: function toString() {
    return data;
  }
};
  • 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.
  • Don't compare booleans to true or false. For example, write if (ioService.offline). Compare objects to null, numbers to 0 or strings to "" if there is chance for confusion.
  • Remember to add a semicolon after the ending XML tag when using E4X. var myTag = <menuitem id="prefMenuitem" />;
  • Do not declare anonymous functions in prototype definitions.
  • Prototype function definitions must be defined on the next line.
  • Contain all prototype functions within the single prototype scope.
  • Prefix the function name with the object's initials.
  • If the function is private, two underscores separate the object initials and function name (one as a separator, one to indicate privateness).
function FeedManager(data) {
  this.data = data;
}

FeedManager.prototype = {
  getData : function FM_getData() {
    return data;
  }
};
  • It's always worth reading the JavaScript reference. For instance, don't forget that you can index a string as if it was an array.

[edit] XPConnect

  • Don't use object methods and properties more than you have to. It is often faster to store the result in a temporary variable.
  • Don't call methods that you don't have to. For instance, for a single window, windowManager.getEnumerator(aType).hasMoreElements() may be replaced with windowManager.getMostRecentWindow(aType) != null.
  • Don't query interfaces unless you need to access methods and properties of that interface. You do not have to query interfaces to compare objects, nor to pass objects as parameters. (Both of these are required in C++).
  • Don't call QueryInterface unless you expect to succeed. Instead, use instanceof, e,g,:
if (target instanceof Components.interfaces.nsIRDFResource)
  return target.Value;
if (target instanceof Components.interfaces.nsIRDFLiteral)
  return target.Value;
return null;
  • Don't test the return value of QueryInterface, it always returns the original variable if it succeeds. XPConnect knows all about tearoffs and modifies the object that you QueryInterface or instanceof to cache all its known interfaces.
  • Often when passing an object to an XPCOM method it is helpful if the object you pass is an XPCOM object, so that the C++ method access a C++ object. However this is not always necessary or desirable. For instance the offline observer declared above is a JavaScript object that is registered with an XPCOM object, so that the call back from XPCOM executes the JavaScript method. Some XPCOM methods expect an object that implements several interfaces thus requiring you to write a QueryInterface method. However in JavaScript this is quite simple even in the case of a weak reference which in C++ requires a helper class:
var weakObserver = {
  QueryInterface: function QueryInterface(aIID) {
    if (aIID.equals(Components.interfaces.nsIObserver) ||
        aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
        aIID.equals(Components.interfaces.nsISupports))
       return this;
    throw Components.results.NS_NOINTERFACE;
  },
  observe: function observe(aSubject, aTopic, aState) {
  }
}
  • When declaring XPCOM methods try to use the same names for method parameters as are used in the interface definition.

[edit] DOM elements

  • DOM elements are just XPCOM objects with some of the interfaces precached.
  • Don't call getAttribute to see if an attribute exists, call hasAttribute instead.
  • Prefer to loop through childNodes rather than using first/lastChild with next/previousSibling. But prefer hasChildNodes() to childNodes.length > 0. Similarly prefer document.getElementsByTagName(aTag).item(0) != null to document.getElementsByTagName(aTag).length > 0.
  • Prefer to use localName rather than tagName.
  • XUL elements have many of the attributes mapped to properties. This was done for a reason, so use them! The properties are:
    • id
    • align
    • dir
    • flex
    • orient
    • pack
    • observes
    • contextMenu
    • tooltip
    • width
    • height
    • minWidth
    • minHeight
    • maxWidth
    • maxHeight
    • persist
    • left
    • top
    • datasources
    • ref
    • tooltipText
    • statusText
    • allowEvents
  • Properties must be listed in alphabetical order. The only exception to this rule is that the id property must be first in this list if present.
  • Properties must be listed one per line. This will facilitate svn blame.
  • XUL also maps the ordinal attribute but this defaults to "1" if it is not present.
  • XUL also maps the class attribute, but unfortunately class is a reserved identifier, so the property is named className. (The property could have been implemented as ['class'] but that just looks silly.)
  • XUL also maps the hidden and collapsed attributes to properties, but note that these are boolean properties whereas the above list are all string properties.
  • XUL also maps other useful properties and methods using XBL bindings; these vary from element to element.
  • For best performance give ids to all important elements. However in addition to locating elements by tag name XUL also allows you to locate an element by attribute, starting at any element in the document.
  • Don't forget to use DOM constants such as event.keyCode == KeyEvent.DOM_VK_RETURN rather than event.keyCode == 13.

[edit] Indentation examples

  • When indenting a line and cutting at a dot, put the dot on the second line.

Examples of valid indentation:

var result = prompt(aMessage,
                    aInitialValue,
                    aCaption);

var ioSvc = Components.classes["@mozilla.org/network/io-service;1"]
                      .getService(Components.interfaces.nsIIOService);

var myObject = { a: foo, b: bar };

var ioSvc = CC["@mozilla.org/network/io-service;1"]
            .getService(CI.nsIIOService);

switch (aSomething) {
  // Do something for foo
  case "foo":
    fooFunction();
    break;
  // Do something else for bar
  case "bar":
    barFunction();
    break;
  // baz falls through
  case "baz":
  default:
    defaultFunction();
    break;
  }

buildTooltip: function facebookPhoto_buildTooltip()
{
  default xml namespace =
      "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  var tip =
    <vbox>
      <hbox>
        <image src={this.midSizePhoto} style="margin-bottom: 2px;" />
        <spacer flex="1" />
      </hbox>
      <hbox>
        <image src={this.icon} />
        <vbox style="max-width: 250px;">
          <label crop="end">{this.title}</label>
          <label class="user">{this.username}</label>
        </vbox>
      </hbox>
    </vbox>;

  var parser = CC["@mozilla.org/xmlextras/domparser;1"].
               createInstance(CI.nsIDOMParser);
  var doc = parser.parseFromString(tip, "text/xml");
  return doc.documentElement;
},


Valid styles of XPCOM component initialization:

var ioSvc = Components.classes["@mozilla.org/network/io-service;1"]
                      .getService(Components.interfaces.nsIIOService);

const CI = Components.interfaces;
const CC = Components.classes;

var ioSvc = CC["@mozilla.org/network/io-service;1"]
            .getService(CI.nsIIOService);

CC["@mozilla.org/consoleservice;1"].getService(CI.nsIConsoleService)
                                   .logStringMessage(aMsg);

[edit] Tools

  • JST Review - While intended for C++, this provides coverage for some basic JS rules (tabs, line-length, etc.)

[edit] References