Firebug 1.10 Extension Architecture

From FirebugWiki

(Difference between revisions)
Jump to: navigation, search
(Module Paths)
Line 217: Line 217:
-
=== Module Paths ===
+
=== Module URL ===
-
A module needs to be identified by and ID (usually a path) to be loaded so, let's see how the path is generated in case of Firebug. First see a directory structure of an example extension (based on standard Firefox extension structure).
+
A module needs to be identified by and ID (usually a path/url) to be loaded so, let's see how the path is generated in case of Firebug. First, see a directory structure of an example extension (based on standard Firefox extension structure).
<pre>
<pre>
Line 233: Line 233:
</pre>
</pre>
-
*
+
The important part (related to module URLs) is done in <code>chrome.manifest</code> file.
 +
 
 +
<pre>
 +
content  helloworld              chrome/content/
 +
skin    helloworld classic/1.0  chrome/skin/classic/
 +
locale  helloworld en-US        chrome/locale/en-US/
 +
</pre>
 +
 
 +
* This is the standard way how to map extension's directories to <code>chrome</code> URLs
 +
* The first line maps our content directory to: <pre>chrome://helloworld/content/</pre>
 +
* So, for example, URL of the '''main.js''' file is: <pre>chrome://helloworld/content/main.js</pre>
 +
 
 +
 
 +
Now what Firebug does.
 +
 
 +
* Firebug module loader creates AMD shortcut mapping: <pre><extid> := "chrome://" + extid + "/content"</pre>
 +
* So, expected ID of the '''main''' module is: <pre>helloworld/main</pre> URLs

Revision as of 13:33, 10 February 2012

Firebug 1.10 introduces support for bootstrapped extensions and also new APIs for extensions. This page is intended to document these new APIs.

Contents

Introduction

Firebug 1.10 supports three concepts that can be used when developing an extension:

  • XUL Based Extensions
  • Bootstrapped Extensions
  • AMD Extensions


XUL Based Extensions

XUL represents an old school technique and Firebug has always supported this ways of extending. The main difference from the bootstrapped extensions is that you need to restart the browser whenever your extension is installed or uninstalled.

Important thing related to XUL based extension is to understand scopes. First see the next diagram.

Firebug-scopes.png

There are two scopes that are relevant to extension development.

  • Browser Window: use this scope when overlaying browser UI. Your extension might want to append new button into Firefox toolbar or create, create a new menu items, etc.
  • Firebug UI: this is the scope mostly used by Firebug extension, use it whenever you want to extend Firebug. Your extension might want to crate a new Firebug panel, etc.

Note that Firebug 1.10 introduces a new feature called Delayed Load. This means that overlays applied to chrome://firebug/content/firebugOverlay.xul are loaded the first time the Firebug UI is actually opened by the user. This way loading doesn't slow down Firefox start up time.

Also, the global Firebug object is available since Firefox start even if Firebug is not fully loaded yet, but contains only a small set of APIs.


Bootstrapped Extensions

Support for bootstrapped extensions has been introduced in Firebug 1.10. There are (at least) three entities you'll be dealing with when developing basic structure of a bootstrapped (restart-less) extension.

  • bootstrap.js - Every Firefox bootstrapped extension needs to provide a bootstrap.js file that is expected to implement basic functions like: install, uninstall, startup, shutdown. In case of a Firebug extension further functions like: firebugStartup, firebugFrameLoad, etc. are expected. These functions/callbacks are automatically executed to allow proper initialization/shutdown of the extension.
  • FirebugLoader - is a component resource://firebug/loader.js that distributes events to all Firebug bootstrapped extensions.
  • Firebug - The Firebug object

See following sequence diagram:

Bootstrapped-init.png

  • When Firebug itself is loaded (bootstrapped), FirebugLoader fires an firebugStartup event. This is the moment when the extension can register itself as a listener for further events by calling registerBootstrappScope function.
  • Later when the user requires Firebug for the first time, the firebugFrameLoad callback is executed. The callback has one parameter the Firebug object. This is also the time when overlays applied to chrome://firebug/content/firebugOverlay.xul are loaded.
  • If the extension is using AMD, it can register itself as by calling Firebug.registerExtension (see further AMD Extensions chapter)


Following Firebug related callbacks can be implemented in bootstrap.js file:

  • firebugStartup() - called when Firebug is bootstrapped
  • firebugShutdown() - called when Firebug is uninstalled
  • topWindowLoad(win) - called when a new browser window is opened
  • topWindowUnload(win) - called when an existing browser window is closed
  • firebugFrameLoad(Firebug) - called when Firebug UI is loaded into an existing browser window
  • firebugFrameUnload(Firebug) - called when Firebug UI is unloaded from an existing browser window


An example of bootstrapped Firebug extension is available.


AMD Extensions

This technique (Asynchronous Module Definition) is related to how code of your extension is organized. It can be used together with XUL based or bootstrapped extensions. Using AMD in your extension is recommended since it helps to organize your code into modules, properly maintain dependencies and use external modules such as those provided by Firebug framework.

Typical example of a Firebug module can as follows:

define([
    "firebug/lib/trace"
],
function (FBTrace) {
 
var myModuleObject =
{
    myFunction: function()
    {
        FBTrace.sysout("My function executed!");
    }
}
 
return myModuleObject;
});
  • The module specifies one dependency: firebug/lib/trace modules (Firebug tracing)
  • The module implement one object with one function that is also returned from the module
  • See more about AMD syntax.

Extensions based on AMD can use Firebug's built-in module loader and also APIs that automatically load entire extension.


AMD & XUL Overlay

First see how to register our AMD based extension from a XUL overlay.

An overlay applied to chrome://firebug/content/firebugOverlay.xul

<?xml version="1.0"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
    <script type="application/x-javascript" src="firebugOverlay.js"/>
</overlay>

The firebugOverlay.js represents extension's entry point.

var config = {id: "helloworld@janodvarko.cz"};
Firebug.registerExtension("helloworld", config);
  • The extension registers itself within Firebug framework
  • The extension uses an ID coming from install.rdf
  • The registration will automatically look for main module and load it


Bootstrap & AMD

In case of bootstrapped extension the entry point isn't a XUL overlay, but bootstrap.js file. Again we need to make sure that Firebug.registerExtension is called. See the following shortened example:

function firebugFrameLoad(Firebug)
{
    var config = {id: "helloworld@janodvarko.cz"};
    Firebug.registerExtension("helloworld", config);
}
 
function firebugFrameUnload(Firebug)
{
    if (!Firebug.isInitialized)
        return;
 
    Firebug.unregisterExtension("hellobootamd");
}
  • firebugFrameLoaded is called when an instance of Firebug is loaded (happens when the user opens Firebug for the first time). There is one instance of Firebug per browser window.
  • Again, we are using ID coming from install.rdf
  • Since, it's bootstrapped extension we need to also handle uninstall and disable so, unregister the extension in these cases.
  • The registration will automatically look for main module and load it


Main Module

As soon as the extension is registered, Firebug is looking for main module and loads it. Here is an example of such module.

define([
    "firebug/lib/trace",
],
function(FBTrace) {
 
var theApp =
{
    initialize: function()
    {
        // TODO: Extension initialization
    },
 
    shutdown: function()
    {
        // TODO: Unregister all registered Firebug components
    }
}
 
return theApp;
});
  • Any initialization logic should be done in initialize function.
  • Unregistering all registered components (Firebug panels, Firebug modules, listeners, etc.) should be done in shutdown.

Let's advance our example a little bit with a new Firebug panel. Of course, its implementation is done as another AMD module.

define([
    "firebug/lib/lib",
],
function(FBL) {
 
var MyPanel = function MyPanel() {};
MyPanel.prototype = FBL.extend(Firebug.Panel,
{
    name: "helloworldpanel",
    title: "Hello World!",
});
 
return MyPanel;
});


This module can be consequently imported into our main module as follows.

define([
    "firebug/lib/trace",
    "helloworld/myPanel",
],
function(FBTrace, MyPanel) {
 
var theApp =
{
    initialize: function()
    {
        Firebug.registerPanel(MyPanel);
    },
 
    shutdown: function()
    {
        Firebug.unregisterPanel(MyPanel);
    }
}
 
return theApp;
});


Module URL

A module needs to be identified by and ID (usually a path/url) to be loaded so, let's see how the path is generated in case of Firebug. First, see a directory structure of an example extension (based on standard Firefox extension structure).

- Hello  
  - chrome
    - content 
      main.js
      myPanel.js
    + locale
    + skin
    bootstrap.js
    chrome.manifest
    install.rdf    

The important part (related to module URLs) is done in chrome.manifest file.

content  helloworld               chrome/content/
skin     helloworld classic/1.0   chrome/skin/classic/
locale   helloworld en-US         chrome/locale/en-US/
  • This is the standard way how to map extension's directories to chrome URLs
  • The first line maps our content directory to:
    chrome://helloworld/content/
  • So, for example, URL of the main.js file is:
    chrome://helloworld/content/main.js


Now what Firebug does.

  • Firebug module loader creates AMD shortcut mapping:
    <extid> := "chrome://" + extid + "/content"
  • So, expected ID of the main module is:
    helloworld/main
    URLs
Personal tools