Firebug internals

  • Revision slug: Firebug_internals
  • Revision title: Firebug internals
  • Revision id: 108880
  • Created:
  • Creator: JohnJBarton1
  • Is current revision? No
  • Comment /* Extending Firebug */

Revision Content

Get Firebug

Source Code

  1. Get Firebug; The source is included.
  2. Find the installed firebug in the Mozilla extensions directory, eg
C:\Documents and Settings\John J. Barton\Application Data\Mozilla\Firefox\Profiles\w5tmpjcs.test\extensions\firebug@software.joehewitt.com\
  1. Copy the directory to your workspace
  2. find firebug.jar in the chrome directory.
  3. rename it to firebug.zip.
  4. unzip it such that chrome directory contains the content of the jar,eg
  • chrome/content
  • chrome/icons
  • chrome/locale
  • chrome/skin

Rezip the files into a jar for testing.

Major Components

  • Panels - these are the tabs in the firebug UI
  • Modules - ?
  • lib aka FBL aka Firebug Library - common functions used in many panels
  • firebug-service - javascript - implemented XPCOM component, in firebug/components.
  • sourceCache - stores .htm, .js content read for source views
  • reps.js - representations usually used by multiple panels.
  • error.js - console observer for nsIScriptError and nsIConsoleMessage
  • XUL files - structure for the UI
  • css files - decorations
  • firebug.js
    • Globals like top.Firebug: version state
    • Base objects:
      • Firebug.Module, base for eg debugger
      • Firebug.Panel, interface for panels

Extending Firebug

From Christoph Dorn on Firebug news group Jan 1, 2:53 pm (mostly quoted, but edited for this wiki --JohnJBarton 21:20, 1 January 2007 (PST))

Subject: Re: Adding a new panel to firebug from another extension

To add a new panel to Firebug from my own Firefox extension.

Here is the code:

File: chrome.manifest - Add the following overlay directive to your extension:

overlay chrome://firebug/content/firebugOverlay.xul chrome://developerwiki/content/firebugOverlay.xul


File: firebugOverlay.xul - This inserts some sample buttons for the new panel

<?xml version="1.0"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/x-javascript" src="chrome://developerwiki/content/FirebugPanel.js"/>
   <commandset id="mainCommandSet">
       <command id="cmd_button1DeveloperWiki" oncommand="Firebug.DeveloperWiki.button1()"/>
       <command id="cmd_button2DeveloperWiki" oncommand="Firebug.DeveloperWiki.button2()"/>
   </commandset>
   <toolbar id="fbToolbar" align="center">
       <hbox id="fbToolbarInner" insertbefore="fbDetachButton" flex="1" align="center">
           <hbox id="fbDeveloperWikiButtons" insertafter="fbNetButtons">
               <toolbarbutton label="Button 1" class="toolbar-text-button"                               tooltiptext="Button 1" command="cmd_button1DeveloperWiki"/>
               <toolbarbutton label="Button 2" class="toolbar-text-button"                                tooltiptext="Button 2" command="cmd_button2DeveloperWiki"/>
           </hbox>
       </hbox>
   </toolbar>
</overlay>


File: FirebugPanel.js - Simple Panel Example

FBL.ns(function() { with (FBL) {
Firebug.DeveloperWiki = extend(Firebug.Module,
{
   showPanel: function(browser, panel)
   {
       var isDeveloperWiki = panel && panel.name == "developerwiki";
       var DeveloperWikiButtons = browser.chrome.$("fbDeveloperWikiButtons");
       collapse(DeveloperWikiButtons, !isDeveloperWiki);
   },
   button1: function()
   {
     alert('Clicked Button 1');
   },
   button2: function()
   {
     alert('Clicked Button 2');
   }
});
function DeveloperWikiPanel() {}
DeveloperWikiPanel.prototype = extend(Firebug.Panel,
{
   name: "developerwiki",
   title: "DeveloperWiki",
   searchable: false,
   editable: false
});
 Firebug.registerModule(Firebug.DeveloperWiki);
 Firebug.registerPanel(DeveloperWikiPanel);
}});


The trick was to define the "title" property on the DeveloperWikiPanel.prototype object as it would try and load it from the firebug.properties file otherwise (bindings.xml Line 55) and fail silently.

The above code works because Firefox will initialize Firebug first, then based on your overlay directive call your JS code to register the new panel and then call the initializeUI for the Firebug extension.

Extending nsIFirebug.idl

The firebug-service is an XPCOM component implemented in javascript. To change its API you need to change the nsIFirebug.idl file, compile it with xpidl.exe, and put the new .xpt file in to your .xpi file. The IDL file includes some other idl files from XPCOM. These files and the xpcom.exe driver are available in the Gecko SDK. Such changes will make your code incompatible with Firebug; it seems wise to rename the resulting .xpt file to avoid confusion.

Glue

  • context - a web page, passed to many functions.
  • Firebug.registerModule(Errors);
    • Errors extends Firebug.Module
  • Firebug.registerPanel(ScriptPanel);
    • ScriptPanel extends Panel

Firebug Service

fbs is FirebugService implemented in firebug-serivces.js, under components dir

 const fbs = CCSV("@joehewitt.com/firebug;1", "nsIFireBug");
 this.fbs = this.CCSV("@joehewitt.com/firebug;1", "nsIFireBug");
 this.jsd = this.CCSV("@mozilla.org/js/jsd/debugger-service;1", "jsdIDebuggerService");

Debugger objects can be registered with the service, then the service sets hooks (callbacks) into jsdIDebuggerService (jsd) and routes the results to the correct debugger for a given web page. The routing mechanism is a bit tricky: FBS calls supportsWindow for each debugger until one says "true"; as a side-effect of this call, the debugger sets breakContext equal to the window. Then FBS calls the debugger function (eg onXXX()) and the debugger (in its onXXX()) sets its context to breakContext. (The JS engine is thankfully single threaded).

interface nsIFireBugDebugger : nsISupports
{
  boolean supportsWindow(in nsIDOMWindow window);
  void onLock(in boolean state); 
  unsigned long onBreak(in jsdIStackFrame frame);
  unsigned long onHalt(in jsdIStackFrame frame);
  void onCall(in jsdIStackFrame frame);
  void onError(in jsdIStackFrame frame);
  void onResume();
  void onToggleBreakpoint(in string url, in unsigned long lineNo, in boolean isSet);
  void onToggleBreakpointCondition(in string url, in unsigned long lineNo, in boolean isSet);
  void onToggleBreakpointDisabled(in string url, in unsigned long lineNo, in boolean disabled);
  void onToggleErrorBreakpoint(in string url, in unsigned long lineNo, in boolean isSet);
  void onToggleMonitor(in string url, in unsigned long lineNo, in boolean isSet);
};

The service is in a different execution space from the UI; data only flows through the interfaces. This keeps jsd objects out of the UI for the most part. The all important stack is the major exception.

To change the service you must delete the file compreg.dat in the FF extension directory to force it to re-read the service definitions.

PluginPanel

PluginPanel defined in plugin.js

  • looks like a base for tabContext.createPanelType
  • getOptionsMenuItems is the options filling function, shows up on all panels
  • script panel is in debugger.js

debugger.js

Interface to firebug-service and implementation of the "script" panel.

debugger.showStackFrame

  • puts up the stack across the top of the script panel.

debuggr.onError(frame) called by FBS.onDebug(frame, type, rv)

  • so the frame should be a jsdIStackFrame
  • only called if reportNextError true, a flag set by onError when this.showStackTrace (console prefs)
  • sets Firebug.errorStackTrace (not per context?)

Revision Source

<p><a class="external" href="http://getfirebug.com/">Get Firebug</a>
</p>
<h3 name="Source_Code"> Source Code </h3>
<ol><li> <a class="external" href="http://getfirebug.com/">Get Firebug</a>; The source is included.
</li><li> Find the installed firebug in the Mozilla extensions directory, eg
</li></ol>
<pre class="eval">C:\Documents and Settings\John J. Barton\Application Data\Mozilla\Firefox\Profiles\w5tmpjcs.test\extensions\firebug@software.joehewitt.com\
</pre>
<ol><li> Copy the directory to your workspace
</li><li> find firebug.jar in the chrome directory.
</li><li> rename it to firebug.zip.
</li><li> unzip it such that chrome directory contains the content of the jar,eg
</li></ol>
<ul><li>chrome/content
</li><li>chrome/icons
</li><li>chrome/locale
</li><li>chrome/skin
</li></ul>
<p>Rezip the files into a jar for testing.
</p>
<h3 name="Major_Components"> Major Components </h3>
<ul><li> Panels - these are the tabs in the firebug UI
</li><li> Modules - ?
</li><li> lib aka FBL aka Firebug Library - common functions used in many panels
</li><li> <a href="#Firebug_Service"> firebug-service</a> - javascript - implemented XPCOM component, in firebug/components.
</li><li> sourceCache - stores .htm, .js content read for source views
</li><li> reps.js - representations usually used by multiple panels.
</li><li> error.js - console observer for nsIScriptError and nsIConsoleMessage
</li><li> XUL files - structure for the UI
</li><li> css files - decorations
</li><li> firebug.js 
<ul><li>Globals like top.Firebug: version state
</li><li>Base objects:
<ul><li>Firebug.Module, base for eg debugger
</li><li>Firebug.Panel, interface for panels
</li></ul>
</li></ul>
</li></ul>
<h3 name="Extending_Firebug"> Extending Firebug </h3>
<p>From Christoph Dorn on Firebug news group Jan 1, 2:53 pm 
(mostly quoted, but edited for this wiki --<a href="User:JohnJBarton1">JohnJBarton</a> 21:20, 1 January 2007 (PST)) 
</p><p>Subject: Re: Adding a new panel to firebug from another extension
</p><p>To add a new panel to Firebug from my own Firefox extension.
</p><p>Here is the code:
</p><p>File: chrome.manifest - Add the following overlay directive to your extension:
</p>
<pre class="eval">overlay chrome://firebug/content/firebugOverlay.xul chrome://developerwiki/content/firebugOverlay.xul
</pre>
<p><br>
File: firebugOverlay.xul - This inserts some sample buttons for the new panel
</p>
<pre class="eval">&lt;?xml version="1.0"?&gt;
&lt;overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"&gt;
   &lt;script type="application/x-javascript" src="chrome://developerwiki/content/FirebugPanel.js"/&gt;
   &lt;commandset id="mainCommandSet"&gt;
       &lt;command id="cmd_button1DeveloperWiki" oncommand="Firebug.DeveloperWiki.button1()"/&gt;
       &lt;command id="cmd_button2DeveloperWiki" oncommand="Firebug.DeveloperWiki.button2()"/&gt;
   &lt;/commandset&gt;
   &lt;toolbar id="fbToolbar" align="center"&gt;
       &lt;hbox id="fbToolbarInner" insertbefore="fbDetachButton" flex="1" align="center"&gt;
           &lt;hbox id="fbDeveloperWikiButtons" insertafter="fbNetButtons"&gt;
               &lt;toolbarbutton label="Button 1" class="toolbar-text-button"                               tooltiptext="Button 1" command="cmd_button1DeveloperWiki"/&gt;
               &lt;toolbarbutton label="Button 2" class="toolbar-text-button"                                tooltiptext="Button 2" command="cmd_button2DeveloperWiki"/&gt;
           &lt;/hbox&gt;
       &lt;/hbox&gt;
   &lt;/toolbar&gt;
&lt;/overlay&gt;
</pre>
<p><br>
File: FirebugPanel.js - Simple Panel Example
</p>
<pre class="eval">FBL.ns(function() { with (FBL) {
Firebug.DeveloperWiki = extend(Firebug.Module,
{
   showPanel: function(browser, panel)
   {
       var isDeveloperWiki = panel &amp;&amp; panel.name == "developerwiki";
       var DeveloperWikiButtons = browser.chrome.$("fbDeveloperWikiButtons");
       collapse(DeveloperWikiButtons, !isDeveloperWiki);
   },
   button1: function()
   {
     alert('Clicked Button 1');
   },
   button2: function()
   {
     alert('Clicked Button 2');
   }
});
</pre>
<pre class="eval">function DeveloperWikiPanel() {}
DeveloperWikiPanel.prototype = extend(Firebug.Panel,
{
   name: "developerwiki",
   title: "DeveloperWiki",
   searchable: false,
   editable: false
});
</pre>
<pre class="eval"> Firebug.registerModule(Firebug.DeveloperWiki);
 Firebug.registerPanel(DeveloperWikiPanel);
}});
</pre>
<p><br>
The trick was to define the "title" property on the <code>DeveloperWikiPanel.prototype</code> object as it would try and load it from the firebug.properties file otherwise (bindings.xml Line 55) and fail silently.
</p><p>The above code works because Firefox will initialize Firebug first, then based on your overlay directive call your JS code to register the new panel and then call the <code>initializeUI</code> for the Firebug extension.
</p>
<h3 name="Extending_nsIFirebug.idl"> Extending nsIFirebug.idl </h3>
<p>The firebug-service is an XPCOM component implemented in javascript. To change its API you need to change the nsIFirebug.idl file, compile it with xpidl.exe, and put the new .xpt file in to your .xpi file. The IDL file includes some other idl files from XPCOM.  These files and the xpcom.exe driver are available in the <a class="external" href="http://developer.mozilla.org/en/docs/Gecko_SDK">Gecko SDK</a>. Such changes will make your code incompatible with Firebug; it seems wise to rename the resulting .xpt file to avoid confusion.
</p>
<h3 name="Glue"> Glue </h3>
<ul><li> context - a web page, passed to many functions.
</li><li> Firebug.registerModule(Errors);
<ul><li> Errors extends Firebug.Module
</li></ul>
</li><li> Firebug.registerPanel(ScriptPanel);
<ul><li> ScriptPanel extends Panel
</li></ul>
</li></ul>
<h3 name="Firebug_Service"> Firebug Service </h3>
<p>fbs  is FirebugService implemented in firebug-serivces.js, under components dir
</p>
<pre class="eval"> const fbs = CCSV("@joehewitt.com/firebug;1", "nsIFireBug");
 this.fbs = this.CCSV("@joehewitt.com/firebug;1", "nsIFireBug");
 this.jsd = this.CCSV("@mozilla.org/js/jsd/debugger-service;1", "jsdIDebuggerService");
</pre>
<p>Debugger objects can be registered with the service, then the service sets hooks (callbacks) into jsdIDebuggerService (jsd) and routes the results to the correct debugger for a given web page. The routing mechanism is a bit tricky: FBS calls <code>supportsWindow</code> for each debugger until one says "true"; as a side-effect of this call, the debugger sets breakContext equal to the window. Then FBS calls the debugger function (eg onXXX()) and the debugger (in its onXXX()) sets its context to breakContext. (The JS engine is thankfully single threaded).
</p>
<pre class="eval">interface nsIFireBugDebugger : nsISupports
{
  boolean supportsWindow(in nsIDOMWindow window);
  void onLock(in boolean state); 
  unsigned long onBreak(in jsdIStackFrame frame);
  unsigned long onHalt(in jsdIStackFrame frame);
  void onCall(in jsdIStackFrame frame);
  void onError(in jsdIStackFrame frame);
  void onResume();
  void onToggleBreakpoint(in string url, in unsigned long lineNo, in boolean isSet);
  void onToggleBreakpointCondition(in string url, in unsigned long lineNo, in boolean isSet);
  void onToggleBreakpointDisabled(in string url, in unsigned long lineNo, in boolean disabled);
  void onToggleErrorBreakpoint(in string url, in unsigned long lineNo, in boolean isSet);
  void onToggleMonitor(in string url, in unsigned long lineNo, in boolean isSet);
};
</pre>
<p>The service is in a different execution space from the UI; data only flows through the interfaces. This keeps jsd objects out of the UI for the most part.  The all important stack is the major exception.
</p><p>To change the service you must delete the file compreg.dat in the FF extension directory to force it to re-read the service definitions.
</p>
<h3 name="PluginPanel"> PluginPanel </h3>
<p>PluginPanel defined in plugin.js
</p>
<ul><li> looks like a base for tabContext.createPanelType
</li><li> getOptionsMenuItems is the options filling function, shows up on all panels
</li><li> script panel is in debugger.js
</li></ul>
<h3 name="debugger.js"> debugger.js </h3>
<p>Interface to <a href="#Firebug_Service"> firebug-service</a> and implementation of the "script" panel.
</p><p>debugger.showStackFrame
</p>
<ul><li>puts up the stack across the top of the script panel.
</li></ul>
<p>debuggr.onError(frame) called by FBS.onDebug(frame, type, rv)
</p>
<ul><li> so the frame should be a jsdIStackFrame 
</li><li> only called if reportNextError true, a flag set by onError when this.showStackTrace (console prefs)
</li><li> sets Firebug.errorStackTrace (not per context?)
</li></ul>
Revert to this revision