Developing Firefox for Fun and Profit

Josh Matthews, Platform Engineer (Mozilla)

Developing Firefox for Fun and Profit

Why contribute to Mozilla Firefox?

Help keep the web open.

Gain practical experience with 10M LOC.

Work on a wide variety of challenging technical problems.

What sorts of technical problems?

Static analysis Parallel programming
Audio/video decoding Sandboxing and multiprocess
Network protocols Garbage collection
JIT compilation Multi-language integration
Distributed synchronization

That's an intimidating list.

More generally, we get to work on:

Evolving web standards Performance improvements
Memory usage Native platform integration
Privacy improvements Cross-platform constraints
Internationalization Minimizing attack surface
Backwards compatibility

Getting Involved

There are two main ways to do this:

  • Join a team and work on their projects
  • Work on assorted tasks that interest you

Join a team

whatcanidoformozilla.org

Find relevant teams based on required skills.

Follow links to instructions for how to join that team.

Fix assorted tasks

Bugs Ahoy!

Find relevant tasks based on areas of interest.

Each task has an associated mentor to help you succeed.

Where does it happen?

Bugzilla: where tasks are tracked, patches attached, and reviews sought and given (here's an example).

Lots of communication happens on irc.mozilla.org. Newcomers and oldcomers are welcome in #introduction.

Where does it happen?

Other popular IRC channels:
  • #developers - high traffic, general Gecko-related development
  • #jsapi - JavaScript engine development
  • #gfx - graphics/rendering discussion
  • #content - DOM implementation
  • #fx-team - Firefox frontend development
  • #devtools - Firefox web developer tools team
  • #necko - Networking stack development
  • #mobile - Firefox for Android development
  • #b2g - Firefox OS development

How does it happen?

  1. Find a bug in Bugzilla that you want to fix.
  2. Leave a comment with your intentions.
  3. Attach a patch to the bug ("Add an attachment").
  4. Add the review? flag to the attachment, targetted at your mentor (or someone mentioned a lot in hg log).

How does it happen?

  1. If you receive review+ (AKA r+), it's ready to be committed.
  2. Address all code review comments and attach a new version.
  3. Request a new review if you did not receive review+.
  4. Ask your reviewer to commit your patch for you.
When confused, ask someone.

#introduction is a very helpful place for these questions.

Building Firefox from scratch

There's a wiki page that has everything you need.

Bootstrap: $ wget https://hg.mozilla.org/mozilla-central/raw-file/default/python/mozboot/bin/bootstrap.py && python bootstrap.py
Clone: $ hg clone https://hg.mozilla.org/mozilla-central
Build: $ ./mach build
Run: $ ./mach run

Full builds take 15 minutes to 1-2 hours depending on hardware.

Overview of Firefox's parts

  • User interface - initiate a page load via URL bar (browser/, toolkit/)
  • Networking - make HTTP(S) request, handle response (netwerk/)
  • HTML parser - interpret response, create DOM tree (parser/)
  • Layout - style elements of the tree, lay out result (layout/)
  • Graphics - draw the result of layout onscreen as fast as possible (gfx/, image/)
  • JavaScript engine - execute JS found on the page (js/)
  • DOM implementation - make DOM APIs behave as specified, dispatch events for web content (dom/, content/)

Finding what you need

MXR is your friend, as is DXR (with a guide).

  • Search for user-facing strings - menu item labels, dialog text, button labels, etc. (case study)
  • Watch relevant Bugzilla components
  • Ask questions in #introduction
  • Talk to your mentor, if you're working on a mentored task

Tests

We have a lot of tests, and a lot of testing frameworks.

  • ./mach mochitest-plain: web content tests
  • ./mach mochitest-chrome: web content tests with elevated privileges
  • ./mach mochitest-browser: browser UI tests with elevated privileges
  • ./mach mochitest-a11y: accessibility tests
  • ./mach reftest: rendering/layout comparison tests
  • ./mach crashtest: tests that don't crash
  • ./mach xpcshell-test: JS-only tests that run in a limited environment (no DOM)

Tryserver

Ask your mentor to submit your patches to our build farm.

You can also (and should!) request limited commit access to do so yourself.

Worked Examples

  1. Adding an init method to the JS mozContacts API
  2. Creating an internal interface for use by C++ and JS

mozContact.init()

mozContacts API defined by WebIDL (Contacts.webidl):

interface mozContact {
  attribute DOMString? sex;
  attribute DOMString? genderIdentity;
  attribute sequence<ContactAddress>? adr;
  void setMetadata(DOMString id, Date? updated);
};

mozContact.init()

Need to introduce an init method for compatibility reasons:

interface mozContact {
  ...
  void setMetadata(DOMString id, Date? updated);

  void init(optional ContactProperties properties);
};

Meanwhile, in ContactManager.js:

Next we need an implementation:

function Contact() { }
Contact.prototype = {
  setMetadata: function(aId, aUpdated) {
    ...
  }
};

Meanwhile, in ContactManager.js:

Add the new method:

Contact.prototype = {
  setMetadata: function(aId, aUpdated) {
  },
  init: function(aProperties) {
    Components.utils.reportError("deprecated");
  }
};

Homework:

Read the complete patch (it's quite short).

It also demonstrates automated tests for the new method.

Read through bug 951766 to see the full process.

The Network Seer

Want to create a module that can improve network preloading.

Needs to see all requests and learn user habits.

It should not be accessible to arbitrary web content.

nsINetworkSeer.idl

nsINetworkSeer interface defined by IDL (nsINetworkSeer.idl):

interface nsINetworkSeer : nsISupports {
  void reset();
  void learn(in nsIURI targetURI);
};

Digression: XPCOM

XPCOM uses interfaces to interact with "components."

eg. nsIChannel provides common interface for:

  • nsHttpChannel (http://)
  • nsFtpChannel (ftp://)
  • nsFileChannel (file://)

Digression: XPCOM

Similarly, nsIGeolocationProvider provides interface for

  • NetworkGeolocationProvider, JS component that initiates an XMLHttpRequest to Google's service
  • GonkGPSGeolocationProvider, C++ component that uses hardware GPS

Seer.h

Implemented by C++ (Seer.h/Seer.cpp):

#include "nsINetworkSeer.h"
class Seer : public nsINetworkSeer {
public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSINETWORKSEER
  nsCOMPtr<nsIThread> mIOThread;
};

Seer.h

Implemented by C++ (Seer.h/Seer.cpp):

#include "nsINetworkSeer.h"
class Seer : public nsINetworkSeer {
public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSINETWORKSEER
  nsCOMPtr<nsIThread> mIOThread;
};

Seer.h

Implemented by C++ (Seer.h/Seer.cpp):

#include "nsINetworkSeer.h"
class Seer : public nsINetworkSeer {
public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSINETWORKSEER
  nsCOMPtr<nsIThread> mIOThread;
};

Seer.h

Implemented by C++ (Seer.h/Seer.cpp):

#include "nsINetworkSeer.h"
class Seer : public nsINetworkSeer {
public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSINETWORKSEER
  nsCOMPtr<nsIThread> mIOThread;
};

Seer.h

Implemented by C++ (Seer.h/Seer.cpp):

#include "nsINetworkSeer.h"
class Seer : public nsINetworkSeer {
public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSINETWORKSEER
  nsCOMPtr<nsIThread> mIOThread;
};

Seer.cpp

Implemented by C++ (Seer.h/Seer.cpp):

NS_IMETHODIMP Seer::Learn(nsIURI* targetURI) {
    nsRefPtr<SeerLearnEvent> event =
      new SeerLearnEvent(targetURI);
    mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
    return NS_OK;
}

Seer.cpp

Implemented by C++ (Seer.h/Seer.cpp):

NS_IMETHODIMP Seer::Learn(nsIURI* targetURI) {
    nsRefPtr<SeerLearnEvent> event =
      new SeerLearnEvent(targetURI);
    mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
    return NS_OK;
}

Seer.cpp

Implemented by C++ (Seer.h/Seer.cpp):

NS_IMETHODIMP Seer::Learn(nsIURI* targetURI) {
    nsRefPtr<SeerLearnEvent> event =
      new SeerLearnEvent(targetURI);
    mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
    return NS_OK;
}

Seer.cpp

Implemented by C++ (Seer.h/Seer.cpp):

NS_IMETHODIMP Seer::Learn(nsIURI* targetURI) {
    nsRefPtr<SeerLearnEvent> event =
      new SeerLearnEvent(targetURI);
    mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
    return NS_OK;
}

Seer.cpp

Implemented by C++ (Seer.h/Seer.cpp):

NS_IMETHODIMP Seer::Learn(nsIURI* targetURI) {
    nsRefPtr<SeerLearnEvent> event =
      new SeerLearnEvent(targetURI);
    mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
    return NS_OK;
}

Let's use it

From C++, we can use Seer. From JS, we use nsINetworkSeer.

try {
  var seer =
    Components.classes["@mozilla.org/network/seer;1"]
      .getService(Components.interfaces.nsINetworkSeer);
  seer.reset();
} catch (e) { }

Let's use it

From C++, we can use Seer. From JS, we use nsINetworkSeer.

try {
  var seer =
    Components.classes["@mozilla.org/network/seer;1"]
      .getService(Components.interfaces.nsINetworkSeer);
  seer.reset();
} catch (e) { }

Let's use it

From C++, we can use Seer. From JS, we use nsINetworkSeer.

try {
  var seer =
    Components.classes["@mozilla.org/network/seer;1"]
      .getService(Components.interfaces.nsINetworkSeer);
  seer.reset();
} catch (e) { }

Seer.cpp

Report an error instead:

NS_IMETHODIMP Seer::Learn(nsIURI* targetURI) {
  if (DatabaseMissing()) {
    return NS_ERROR_NOT_AVAILABLE;
  }
  ...
}

Let's use it

What happens when an error is returned from C++?

...
try {
  seer.learn(document.documentURIObject);
} catch (e) {
  Components.utils.reportError("Whoops.");
}

Homework:

Skim the main patch (it's quite long).

It's a good, contained example of implementing a feature.

Read through remaining integration patches to see how the new module is used (they're quite short).

Next steps

You now know much more than most people who see Mozilla's code for the first time.

Your turn.

Remember: when in doubt, ask someone! The Mozilla community awaits you.

Developing Firefox for Fun and Profit

Josh Matthews, Mozilla Engineer and Community Builder

Slides: http://joshmatthews.net/cusec14/