Version 9 (modified by Meeh, 20 months ago) (diff)

I2P Browser Development And Hacking

Howto get started with the build system

Howto get started with firefox

Privileged Javascript, wtf?

The fact is that most of Firefox's source is probably Javascript, without me fact checking now. Yes, Firefox contains quite a lot of C++ and some Rust etc.

However the glue to make all those different components together to a usable browser is privileged Javascript. It's about the same as regular, but totally different environment and APIs.

You can launch firefox/i2pbrowser with the —jsconsole argument to spawn the browser console at launch. If not, ctrl+shift+J and cmd+shift+J for OSX users.

let console = (Cu.import("resource://gre/modules/Console.jsm", {})).console;
console.log("Hello from Firefox code");

Often all you need would be imported/required in this statement:

const {classes: Cc, interfaces: Ci, manager: Cm, results: Cr, utils: Cu, Constructor: CC} = Components;

Here are some other nice snippets:

function getStackDump() {
  var lines = [];
  for (var frame = Components.stack; frame; frame = frame.caller) {
    lines.push(frame.filename + " (" + frame.lineNumber + ")");
  return lines.join("\n");

Common methods to solve problems

In firefox they use a lot of the concept "observer". We use it ourself to enable the I2P Health check, and in the future to launch I2P itself.

Here is a example on a object registering for "firefox global" events. "quit-application" is self-explained, but for "profile-after-change" it is the first event we as a plugin and not base firefox can hook ourself into.

If you want more information on why "profile-after-change" is the correct one to use, and how, take a look at Receiving startup notifications at the Mozilla wiki.

function myExt() {}
myExt.prototype = {
  observe: function(aSubject, aTopic, aData) {
    switch (aTopic) {
      case "quit-application":
        obs.removeObserver(this, "quit-application");
      case "profile-after-change":
        obs.addObserver(this, "quit-application", false);

But to fully register you also need to edit the chrome.manifest or a equivalent file if it's in the firefox codebase. Bellow are the line we would need in chrome.manifest when it's about plugins;

category profile-after-change MyComponent @foobar/mycomponent;1

Remember consepts like the observer is kind of like interfaces, if you don't implement all functions the object is said to have, your code would throw a exception and not work as expected.

To read more about all the different notifications a observer can receive please check out Observer Notifications at Mozilla wiki.

Another common problem I want to document and bring up is preferences. Since we use them heavily, most likely you would need to deal with them as you're hacking on the browser. Here is a quick and clean read example:

Cu.import("resource://gre/modules/Services.jsm", this);
let accepted = Services.prefs.getBoolPref("toolkit.asyncshutdown.testing", false);

Recommended development preferences

Note: Not all preferences are defined by default, and are therefore not listed by default. You will have to create new (boolean) entries for them. = true
  - This is quite awesome, enable this and you get a REPL for the high privileged Javascript code that makes firefox. 

browser.dom.window.dump.enabled = true
  - Enables the use of the dump() statement to print to the standard console. See window.dump for more info.
     You can use nsIConsoleService instead of dump() from a privileged script. = true
  - This enables to run JavaScript code snippets in the chrome context of the Scratchpad from the Tools menu.
     Don't forget to switch from content to browser as context.

javascript.options.showInConsole = true

dom.report_all_js_exceptions = true

devtools.errorconsole.enabled = true
  - Logs errors in chrome files to the Error Console.

nglayout.debug.disable_xul_fastload = true

nglayout.debug.disable_xul_cache = true
  - Disables the XUL cache so that changes to windows and dialogs do not require a restart. This assumes you're using directories rather than JARs.
     Changes to XUL overlays will still require reloading of the document overlaid.

extensions.logging.enabled = true
  - This will send more detailed information about installation and update problems to the Error Console. (Note that the extension manager 
     automatically restarts the application at startup sometimes, which may mean you won't have time to see the messages logged before 
     the automatic restart happens. To see them, prevent the automatic restart by setting the environment NO_EM_RESTART to 1 before 
     starting the application.)

dom.report_all_js_exceptions = true

Howto get started with i2pbutton