Extension Points

From FirebugWiki

(Difference between revisions)
Jump to: navigation, search
(Info Tip)
m (Added link to API documentation)
(2 intermediate revisions not shown)
Line 1: Line 1:
Firebug provides number of ways how to append new features by developing a new extension. This document is intended to summarize and describe purpose of all these extension points. This document isn’t intended as a tutorial how to write an extension for Firefox.
Firebug provides number of ways how to append new features by developing a new extension. This document is intended to summarize and describe purpose of all these extension points. This document isn’t intended as a tutorial how to write an extension for Firefox.
 +
 +
== Resources ==
 +
[http://getfirebug.com/developer/api/ API documentation]
==Core Extension Points==
==Core Extension Points==
Line 19: Line 22:
One of the first things the developer needs to do when building a new FB extension is creation of a new module. Each Module is represented by an object that’s usually derived from one of the predefined Module object.
One of the first things the developer needs to do when building a new FB extension is creation of a new module. Each Module is represented by an object that’s usually derived from one of the predefined Module object.
-
<pre>
+
<source lang="javascript">
Firebug.MyFirstModule = extend(Firebug.Module,
Firebug.MyFirstModule = extend(Firebug.Module,
{
{
Line 36: Line 39:
     }
     }
});
});
-
</pre>
+
</source>
Each module must be registered in Firebug as follows:
Each module must be registered in Firebug as follows:
-
<pre>
+
<source lang="javascript">
Firebug.registerModule(Firebug.MyFirstModule);
Firebug.registerModule(Firebug.MyFirstModule);
-
</pre>
+
</source>
Module Events Sent By Firebug
Module Events Sent By Firebug
Line 69: Line 72:
This snippet shows how to derive a new panel from Firebug.Panel.
This snippet shows how to derive a new panel from Firebug.Panel.
-
<pre>
+
<source lang="javascript">
function MyFirstPanel() {}
function MyFirstPanel() {}
MyFirstPanel.prototype = extend(Firebug.Panel,
MyFirstPanel.prototype = extend(Firebug.Panel,
Line 90: Line 93:
     }
     }
});
});
-
</pre>
+
</source>
Each panel must be registered in Firebug as follows:
Each panel must be registered in Firebug as follows:
-
<pre>
+
<source lang="javascript">
Firebug.registerPanel(MyFirstPanel);
Firebug.registerPanel(MyFirstPanel);
-
</pre>
+
</source>
===New Side Panel===
===New Side Panel===
Line 113: Line 116:
Part of Firebug's UI is a toolbar with extensible set of buttons. In order to append a new button we have to define an overlay for Firebug's toolbar. See an example.
Part of Firebug's UI is a toolbar with extensible set of buttons. In order to append a new button we have to define an overlay for Firebug's toolbar. See an example.
-
<pre>
+
<source lang="xml">
<?xml version="1.0"?>
<?xml version="1.0"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
Line 126: Line 129:
     </toolbar>
     </toolbar>
</overlay>
</overlay>
-
</pre>
+
</source>
A few notes:
A few notes:
Line 137: Line 140:
If you need to display new set of buttons only for a specific panel (which is recommended tactic) use show and hide methods of your panel object.
If you need to display new set of buttons only for a specific panel (which is recommended tactic) use show and hide methods of your panel object.
-
<pre>
+
<source lang="javascript">
function MyFirstPanel() {}
function MyFirstPanel() {}
MyFirstPanel.prototype = extend(Firebug.Panel,
MyFirstPanel.prototype = extend(Firebug.Panel,
Line 154: Line 157:
     },
     },
});
});
-
</pre>
+
</source>
The showToolbarButtons method updates internally collapsed attribute of the myFirstSetOfButtons element so, it's visible as appropriate.
The showToolbarButtons method updates internally collapsed attribute of the myFirstSetOfButtons element so, it's visible as appropriate.
Line 161: Line 164:
Every panel in Firebug can utilize an Options menu that is displayed at the most right side of Firebug's tab bar. This menu is intended for panel's options (if any) and it's content is provided by current panel object. In order to populate the menu with some items, the panel object must override a getOptionsMenuItems method.
Every panel in Firebug can utilize an Options menu that is displayed at the most right side of Firebug's tab bar. This menu is intended for panel's options (if any) and it's content is provided by current panel object. In order to populate the menu with some items, the panel object must override a getOptionsMenuItems method.
-
<pre>
+
<source lang="javascript">
function MyFirstPanel() {}
function MyFirstPanel() {}
MyFirstPanel.prototype = extend(Firebug.Panel,
MyFirstPanel.prototype = extend(Firebug.Panel,
Line 174: Line 177:
     }
     }
});
});
-
</pre>
+
</source>
Every object returned from the method (within an array) represents one menu item in the Options menu. There are following properties you can set on such an object.
Every object returned from the method (within an array) represents one menu item in the Options menu. There are following properties you can set on such an object.
Line 194: Line 197:
The info tip is usually displayed by panels. See the following example that shows how to override/implement <code>showInfoTip</code> method within a custom panel.
The info tip is usually displayed by panels. See the following example that shows how to override/implement <code>showInfoTip</code> method within a custom panel.
-
<pre>
+
<source lang="javascript">
var MyInfoTip = domplate(Firebug.Rep,
var MyInfoTip = domplate(Firebug.Rep,
{
{
Line 216: Line 219:
     }
     }
});  
});  
-
</pre>
+
</source>
<code>Firebug.showInfoTip</code> method is called automatically by the framework for the currently selected panel. In case we want to handle the infotip by non-panel object we need to register a listener.
<code>Firebug.showInfoTip</code> method is called automatically by the framework for the currently selected panel. In case we want to handle the infotip by non-panel object we need to register a listener.
-
<pre>
+
<source lang="javascript">
var MyInfoTip = domplate(Firebug.Rep,
var MyInfoTip = domplate(Firebug.Rep,
{
{
Line 253: Line 256:
     },
     },
});  
});  
-
</pre>
+
</source>
An example extension is available here:
An example extension is available here:
http://code.google.com/p/fbug/source/browse/#svn%2Fexamples%2Ffirebug1.7%2Finfotip
http://code.google.com/p/fbug/source/browse/#svn%2Fexamples%2Ffirebug1.7%2Finfotip
 +
 +
See also [http://www.softwareishard.com/blog/firebug-tutorial/extending-firebug-infotip-part-xi/ Extending Firebug] tutorial.
===Custom CSS===
===Custom CSS===
Line 272: Line 277:
If any FB extension needs to handle these events a new observer object (nsIObserver) should be registered within the component as follows.
If any FB extension needs to handle these events a new observer object (nsIObserver) should be registered within the component as follows.
-
<pre>
+
<source lang="javascript">
var httpObserver = Cc["@joehewitt.com/firebug-http-observer;1"]
var httpObserver = Cc["@joehewitt.com/firebug-http-observer;1"]
     .getService(Ci.nsIObserverService);
     .getService(Ci.nsIObserverService);
Line 311: Line 316:
     }
     }
});
});
-
</pre>
+
</source>
===Firebug Cache===
===Firebug Cache===
Line 323: Line 328:
Following snippet shows how to register a listener in order to hook all XHR.
Following snippet shows how to register a listener in order to hook all XHR.
-
<pre>
+
<source lang="javascript">
Firebug.SpyMonitorModel = extend(Firebug.Module,
Firebug.SpyMonitorModel = extend(Firebug.Module,
{
{
Line 346: Line 351:
     }
     }
});
});
-
</pre>
+
</source>
===Console Listener===
===Console Listener===
Firebug also allows to register a listener into the Console panel. The best place for registration of such a listener are (as usual) initialize and shutdown methods of our model object.
Firebug also allows to register a listener into the Console panel. The best place for registration of such a listener are (as usual) initialize and shutdown methods of our model object.
-
<pre>
+
<source lang="javascript">
Firebug.MyFirstModel = extend(Firebug.Module,
Firebug.MyFirstModel = extend(Firebug.Module,
{
{
Line 366: Line 371:
     }
     }
});
});
-
</pre>
+
</source>
ConsoleListener object can be defined as follows:
ConsoleListener object can be defined as follows:
-
<pre>
+
<source lang="javascript">
var ConsoleListener = {
var ConsoleListener = {
     onConsoleInjected:function(context, win) {},
     onConsoleInjected:function(context, win) {},
Line 376: Line 381:
     logFormatted: function(context, objects, className, sourceLink) {}
     logFormatted: function(context, objects, className, sourceLink) {}
};
};
-
</pre>
+
</source>
* onConsoleInjected [context, win] called after the console injection script for context has been evaluated in window.
* onConsoleInjected [context, win] called after the console injection script for context has been evaluated in window.
Line 421: Line 426:
Each entry within Net panel represents a network request made by a given page. If this entry is expanded, several tabs are displayed to the user (like, Headers, Response, etc.) showing detailed info. Following code shows how a new tab can be created.
Each entry within Net panel represents a network request made by a given page. If this entry is expanded, several tabs are displayed to the user (like, Headers, Response, etc.) showing detailed info. Following code shows how a new tab can be created.
-
<pre>
+
<source lang="javascript">
Firebug.MyFirstModule = extend(Firebug.Module,
Firebug.MyFirstModule = extend(Firebug.Module,
{
{
Line 466: Line 471:
     }
     }
});
});
-
</pre>
+
</source>
===Net File Link===
===Net File Link===
If you need to create a link within Firebug‘s UI that points to a specific network request within Net panel, use NetFileLink object.
If you need to create a link within Firebug‘s UI that points to a specific network request within Net panel, use NetFileLink object.
-
<pre>
+
<source lang="javascript">
// Navigation by URL
// Navigation by URL
context.chrome.select(new FBL.NetFileLink("http://..."));
context.chrome.select(new FBL.NetFileLink("http://..."));
Line 482: Line 487:
// a request in the Net panel).
// a request in the Net panel).
context.chrome.select(file.getFileLink());
context.chrome.select(file.getFileLink());
-
</pre>
+
</source>

Revision as of 11:03, 24 January 2012

Firebug provides number of ways how to append new features by developing a new extension. This document is intended to summarize and describe purpose of all these extension points. This document isn’t intended as a tutorial how to write an extension for Firefox.

Contents

Resources

API documentation

Core Extension Points

This section describes ways how to extend core Firebug features that are *not* specific to any existing panel.

TabWatcher

The TabWatcher monitors Firefox tabs/windows. TabWatcher events primarily go to Firebug for redispatch to modules. This object could also be called ContextFactory, it creates instances of tabContext and maintains the mapping from window to contexts.

  • shouldCreateContext(win,uri): return true to force a URI to be debugged (new in 1.4, was acceptContext)
  • shouldNotCreateContext(win, uri): return true to prevent a URI from being debugged (new in 1.4, was declineContext)
  • initContext
  • showContext(browser, context): This is re-dispatched to modules, so TabWatchers get it first.
  • loadContext
  • destroyContext

Firebug Module

ModulesHier.png

One of the first things the developer needs to do when building a new FB extension is creation of a new module. Each Module is represented by an object that’s usually derived from one of the predefined Module object.

Firebug.MyFirstModule = extend(Firebug.Module,
{
    initialize: function()
    {
        Firebug.Module.initialize.apply(this, arguments);
 
        // TODO: Module initialization
    },
 
    shutdown: function()
    {
        Firebug.Module.shutdown.apply(this, arguments);
 
        // TODO: Module cleanup
    }
});

Each module must be registered in Firebug as follows:

Firebug.registerModule(Firebug.MyFirstModule);

Module Events Sent By Firebug

  • initialize // window is opened. Non-UI setup
  • initializeUI: function(detachArgs) UI setup like addEventListener
  • shutdown: function() UI teardown like removeEventListener
  • initContext: function(context, persistedState), Called when a new context is created but before the page is loaded
  • reattachContext: function(browser, context)
  • destroyContext: function(context, persistedState) { },
  • watchWindow: function(context, win) { }, // Called when a FF tab is create or activated (user changes FF tab)
  • unwatchWindow: function(context, win) { }, // Called after context is created or with context == null (to abort?)
  • showContext: function(browser, context) { },
  • loadedContext: function(context) Called after a context's page gets DOMContentLoaded (not 'load')
  • showPanel: function(browser, panel) { },
  • showSidePanel: function(browser, panel) { },
  • updateOption: function(name, value) { },
  • getObjectByURL: function(context, url) { },
  • isReadyElsePreparing: function(context, win) { }, For intermodule dependency

Firebug Panel

PanelsHier.png

Each panel in Firebug (like Console, Script, Net, etc.) is represented by an object that is derived from one of the predefined Panel objects. In order to create a new Panel a new object must be created.

This snippet shows how to derive a new panel from Firebug.Panel.

function MyFirstPanel() {}
MyFirstPanel.prototype = extend(Firebug.Panel,
{
    name: "MyFirstPanelID",
    title: "My First Panel Title"),
 
    initialize: function(context, doc)
    {
        Firebug.Panel.initialize.apply(this, arguments);
 
        // TODO: Panel initialization
    },
 
    destroy: function(state)
    {
        Firebug.Panel.destroy.apply(this, arguments);
 
        // TODO: Panel cleanup.
    }
});

Each panel must be registered in Firebug as follows:

Firebug.registerPanel(MyFirstPanel);

New Side Panel

TBD

Searchable Panel

TBD

Activable Panel

TBD

Editable Panel

TBD

Toolbar Buttons

Part of Firebug's UI is a toolbar with extensible set of buttons. In order to append a new button we have to define an overlay for Firebug's toolbar. See an example.

<?xml version="1.0"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
    <toolbar id="fbToolbar" align="center">
        <hbox id="fbToolbarInner" insertbefore="fbDetachButton" flex="1" align="center">
            <hbox id="myFirstSetOfButtons" insertafter="fbNetButtons">
                <toolbarseparator/>
                <toolbarbutton label="My Button" class="toolbar-text-button"
                    oncommand="Firebug.MyFirstModule.onMyButton(FirebugContext)"/>
            </hbox>
        </hbox>
    </toolbar>
</overlay>

A few notes:

  • Notice that "My Button" label should be properly localized.
  • The <toolbarbutton> can be associated with a <command> element.
  • New set of buttons should start with <toolbarseparator/> so, it's visually separated from an existing buttons from the left side.
  • ID of our set (myFirstSetOfButtons in this case) should be unique enough so, there are no collisions with other IDs within browser.xul scope.

If you need to display new set of buttons only for a specific panel (which is recommended tactic) use show and hide methods of your panel object.

function MyFirstPanel() {}
MyFirstPanel.prototype = extend(Firebug.Panel,
{
    name: "MyFirstPanelID",
    title: "My First Panel Title"),
 
    show: function(state)
    {
        this.showToolbarButtons("myFirstSetOfButtons", true);
    },
 
    hide: function()
    {
        this.showToolbarButtons("myFirstSetOfButtons", false);
    },
});

The showToolbarButtons method updates internally collapsed attribute of the myFirstSetOfButtons element so, it's visible as appropriate.

Panel Options

Every panel in Firebug can utilize an Options menu that is displayed at the most right side of Firebug's tab bar. This menu is intended for panel's options (if any) and it's content is provided by current panel object. In order to populate the menu with some items, the panel object must override a getOptionsMenuItems method.

function MyFirstPanel() {}
MyFirstPanel.prototype = extend(Firebug.Panel,
{
    getOptionsMenuItems: function(context)    {
        return [{
            label: "My First Menu Item",
            nol10n: true,
            type: "checkbox",
            command: function() { alert("Hello from the Options menu!"); }
        }];
    }
});

Every object returned from the method (within an array) represents one menu item in the Options menu. There are following properties you can set on such an object.

  • label {string} The label that will appear on the menu item.
  • nol10n {boolean} Indicates whether the label should be localized.
  • type {string} Specifies type of the menu.
  • checked {boolean} Specifies type of the menu.
  • disabled {boolean} Indicates whether the element is disabled or not.
  • image {URL} The URL of the image to appear on the menu item.
  • command {js-function} java script menu item handler.

Localization

TBD

InfoTip

Infotips are used in Firebug UI for displaying additional information. Infotips are similar to tooltips but providing richer content. The content doesn't have to be only a text but any HTML. The content is usually generated using Domplate.

The info tip is usually displayed by panels. See the following example that shows how to override/implement showInfoTip method within a custom panel.

var MyInfoTip = domplate(Firebug.Rep,
{
    tag:
        DIV("Hello from an info tip!"),
});
 
function MyFirstPanel() {} 
MyFirstPanel.prototype = extend(Firebug.Panel, 
{ 
    name: "MyFirstPanelID", 
    title: "My First Panel Title", 
 
    showInfoTip: function(infoTip, target, x, y)
    {
        if (!hasClass(target, "helloWorld"))
            return false;
 
        MyInfoTip.tag.replace({}, infoTip);
        return true;
    }
});

Firebug.showInfoTip method is called automatically by the framework for the currently selected panel. In case we want to handle the infotip by non-panel object we need to register a listener.

var MyInfoTip = domplate(Firebug.Rep,
{
    tag:
        DIV("Hello from an info tip!"),
 
    // InfoTip listener
    showInfoTip: function(infoTip, target, x, y)
    {
        if (!hasClass(target, "helloWorld"))
            return false;
 
        FBTrace.sysout("showInfoTip", target);
 
        this.tag.replace({}, infoTip);
        return true;
    }
});
 
Firebug.MyModule = extend(Firebug.Module,
{ 
    initialize: function()
    {
        Firebug.NetMonitor.NetInfoBody.addListener(this);
        Firebug.InfoTip.addListener(MyInfoTip);
    },
 
    shutdown: function()
    {
        Firebug.NetMonitor.NetInfoBody.removeListener(this);
        Firebug.InfoTip.removeListener(MyInfoTip);
    },
});

An example extension is available here: http://code.google.com/p/fbug/source/browse/#svn%2Fexamples%2Ffirebug1.7%2Finfotip

See also Extending Firebug tutorial.

Custom CSS

TBD

Panel Context Menu

TBD

FBTrace

TBD

Monitor HTTP Request

In order to monitor network activity, Firebug implements a component that register itself as an observer for HTTP-ON-MODIFY-REQUEST, HTTP-ON-EXAMINE-RESPONSE, HTTP-ON-CACHED-RESPONSE.

If any FB extension needs to handle these events a new observer object (nsIObserver) should be registered within the component as follows.

var httpObserver = Cc["@joehewitt.com/firebug-http-observer;1"]
    .getService(Ci.nsIObserverService);
 
Firebug.MyFirstModule = extend(Firebug.Module,
{
    initialize: function()
    {
        Firebug.Module.initialize.apply(this, arguments);
 
        httpObserver.addObserver(this, "firebug-http-event", false);   
    },
 
    shutdown: function()
    {
        Firebug.Module.shutdown.apply(observer, arguments);
 
        httpObserver.removeObserver(this, "firebug-http-event");
    },
 
    /* nsIObserver */
    observe: function(subject, topic, data)
    {
        try
        {
            if (topic == "http-on-modify-request")
                // TODO: onModifyRequest
            else if (topic == "http-on-examine-response")
                // TODO: onExamineResponse
            else if (topic == "http-on-cached-response")
                // TODO: onCachedResponse
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS)
                FBTrace.sysout("MyFirstModule.observe EXCEPTION", err);
        }
    }
});

Firebug Cache

TBD

Extending Console Panel

Spy

A Spy is intended to display XHR within FB console. _Show XMLHttpRequests_ option must be enabled (checked) within Console panel’s Options menu.

Following snippet shows how to register a listener in order to hook all XHR.

Firebug.SpyMonitorModel = extend(Firebug.Module,
{
    initialize: function(detachArgs)
    {
        Firebug.Spy.addListener(this);
    },
 
    shutdown: function()
    {
        Firebug.Spy.removeListener(this);
    },
 
    onStart: function(context, spy)
    {
        // XHR started.
    },
 
    onLoad: function(context, spy)
    {
        // XHR finished.
    }
});

Console Listener

Firebug also allows to register a listener into the Console panel. The best place for registration of such a listener are (as usual) initialize and shutdown methods of our model object.

Firebug.MyFirstModel = extend(Firebug.Module,
{
    initialize: function()
    {
        Firebug.Module.initialize.apply(this, arguments);
        Firebug.Console.addListener(ConsoleListener);
    },
 
    shutdown: function()
    {
        Firebug.Module.shutdown.apply(observer, arguments);
        Firebug.Console.addListener(ConsoleListener);
    }
});

ConsoleListener object can be defined as follows:

var ConsoleListener = {
    onConsoleInjected:function(context, win) {},
    log: function(context, object, className, sourceLink) {},
    logFormatted: function(context, objects, className, sourceLink) {}
};
  • onConsoleInjected [context, win] called after the console injection script for context has been evaluated in window.
  • log [context, object, className, sourceLink] called when a simple text is printed into the Console panel
  • logFormatted [context, object, className, sourceLink] called when a domplate object is printed in to the Console panel (can be a custom domplate object).


Custom Log Format

TBD

New Command Line APIs

TBD

Extending Script Panel

Source Link

TBD

Debugger Listener

There is several events fired by Firebug debugger

  • onStop
  • onResume
  • onJSDActivate
  • onJSDDeactivate
  • onThrow
  • onMonitorScript
  • onFunctionCall
  • onError
  • onEvalScriptCreated(context, frame, url)
  • onEventScriptCreated(context, frame, url)
  • onTopLevelScriptCreated(context, frame, url)
  • onFunctionConstructor(context, frame, ctor_script, url) // not called in Firebug <1.4
  • onSourceFileCreated(context, sourceFile) // when the sourceFile is added to the context.

TBD

Extending Net Panel

Net Panel Listener

TBD

Custom info tab for a network request

Each entry within Net panel represents a network request made by a given page. If this entry is expanded, several tabs are displayed to the user (like, Headers, Response, etc.) showing detailed info. Following code shows how a new tab can be created.

Firebug.MyFirstModule = extend(Firebug.Module,
{
    initialize: function()
    {
        Firebug.Module.initialize.apply(this, arguments);
 
        Firebug.NetMonitor.NetInfoBody.addListener(this);
    },
 
    shutdown: function()
    {
        Firebug.Module.shutdown.apply(observer, arguments);
 
        Firebug.NetMonitor.NetInfoBody.removeListener(this);
    },
 
    // Listener for NetInfoBody.
    initTabBody: function(infoBox, file)
    {
        Firebug.NetMonitor.NetInfoBody.appendTab(infoBox,
            "Test", "Tab Title"));
    },
 
    destroyTabBody: function(infoBox, file)
    {
        // TODO: clean up code for tab content.
    },
 
    updateTabBody: function(infoBox, file, context)
    {
        // Generate content only for the first time and only if the
        // new tab has been just activated.
        var tab = infoBox.selectedTab;
        if (tab.dataPresented || !hasClass(tab, "netInfoTestTab"))
            return;
 
        tab.dataPresented = true;
 
        // Get body element associated with the tab.
        var tabBody = getElementByClass(infoBox, "netInfoTestText");
 
        // TODO: initialize tab content.
    }
});

Net File Link

If you need to create a link within Firebug‘s UI that points to a specific network request within Net panel, use NetFileLink object.

// Navigation by URL
context.chrome.select(new FBL.NetFileLink("http://..."));
 
// Navigation by request object (in case there can be more requests
// with the same URI in the Net panel).
context.chrome.select(new FBL.NetFileLink(null, request));
 
// Selection by file object (the object that is used to represent
// a request in the Net panel).
context.chrome.select(file.getFileLink());
Personal tools