Have you ever been interested in what extensions are available for Firebug? If yes, take a look at what I have found. Frankly, I was quite surprised how many Firebug extensions already exists out there.

  • YSlow
  • Firecookie
  • FirePHP
  • RainbowForFirebug
  • Jiffy
  • Firequark
  • LiveCoder
  • PixelPerfect
  • FirebugCodeCoverage
  • Firebug Net Panel History Overlay
  • Fireclipse
  • Chromebug

Read more...

Even if this part is also about web services integration, the main purpose is actually to show another (and a bit more complex) example of Domplate usage. In this part I have used Yahoo! Search Web Services to track inbound links and demonstrate how Domplate can be used together with real data. Final extension in this part will discover what pages link to the current page in the browser.
Read more...

One of the most noticeable thing (apart from bunch of bug-fixes) of the upcoming Firebug 1.2beta4 (check AMO) is new UI for Firebug activation.

We have been hardworking on this (UI is hard), following carefully all the great user feedback on Firebug newsgroups, and this version has finally something, which looks like quite promising solution (I am curious about the next user feedback ;-).

In the previous version (Firebug 1.05 and 1.1), it was only possible to disabled/enable Firebug entirely (and/or for specific site). However, most of the Firebug features don't reduce Firefox performance and thus can be available and handy all the time. For instance, the DOM Inspector is indispensable tool for a lot of users and there is no performance penalty when using Firefox.

The solution we have chosen makes possible to independently enable/disable those features that can reduce Firefox performance (now disabled by default). The other features are just available and there is no reason to disable them.

So, the following panels are currently activable:

  • Console logging - Console panel
  • Javascript Debugger - Script panel
  • Network monitoring - Net panel

We have introduced a new menu, which is accessible directly from the panel's tab. See the following screen-shots. You can also notice that disabled tab label is grayed out (click the image to enlarge it).

Firebug activation menu

and...

Firebug activation menu

The menu is available for all activable panels (i.e. for those which can be enabled/disabled) and is displayed only for the currently selected tab so, the space on the tab-bar isn’t wasted. The menu target (the black-arrow) can’t be overlooked by the user, but it’s also not eye catching.

All the activable logic is behind these four options:

  1. Enabled – The panel is enabled for all sites.
  2. Disabled - The panel is disabled for all sites.
  3. Enable <panel>for <site> - The panel is enabled for specific site.
  4. Disable <panel> for <site> - The panel is enabled for specific site.

So, some users who use Firebug just occasionally can keep all activable panels disabled and perhaps enable them only for some specific sites. The others can enable all panels and perhaps disable them just for sites like e.g. gmail.

There is also additional Sites... option that displays list of sites for which the panel has been enabled or disabled.

List of enabled/disabled sites

You can see that in this case, Network Monitor has been enabled for www.google.cz.

Finally, the following page is displayed for a disabled panel (Net panel in this case). The page allows one click enablement of the panel and also activation of multiple panels at once. The user can just select appropriate panels and press Enable selected... button.

Firebug\'s panel disabled

The internal architecture of the UI is extensible so, possible Firebug extensions can utilize all the APIs and hook up into the UI as well. This will be useful especially for the user as the activation approach will be the same thorough entire Firebug IDE.

I welcome any new ideas about how to improve the UI!

Just a quick update for all who just installed fresh new Firefox 3 and want to use latest compatible Firecookie extension.

Download Firecookie 0.6

I would also strongly recommend to  use Firebug 1.2 with Firefox 3.

One of the most interesting parts of the Firebug framework is a template system called Domplate. This engine is used to generate HTML UI of Firebug (content of all FB's panels).

It's quite powerful template system and I would definitely recommend to use it when creating UI for Firebug extensions.

Domplate generates the result HTML markup according to templates written in JavaScript. These templates are internally evaluated into a text, which is consequently inserted into specified DOM element on the page through innerHTML property.

It's also possible to define DOM event listeners so, the template object can handle even the user interaction with the final UI.

Hello World! template

So, let's take a look at how to make up some HTML for our Hello World! extension. First of all, see the following piece of code that defines a simple Domplate template.

var helloWorldRep = domplate(
{
    myTag:
        DIV({class: "MyDiv"},
            "Hello World!"
        )
});

HTML generated from this template should look like as follows:

<DIV class="MyDiv">Hello World!</DIV>

The template object is created by domplate function and stored in helloWorldRep variable. This new object usually contains three type of properties:

  1. Tag - set of functions (constructors) that represent a markup template.
  2. Data provider - a function that is intended to provide dynamic data for the template during generation process.
  3. Event handler - a function that is intended to handle DOM events fired when the generated UI is used by the user.

In our case, we have only one tag that describes how the result markup should look like. Only static text will be generated so, no additional providers or handlers are necessary.

The next piece of code shows how to evaluate the template and append generated HTML into our panel. It's executed within onMyButton function, which is a handler for a toolbar button created in part II.

onMyButton: function(context)
{
    var panel = context.getPanel(panelName);
    var parentNode = panel.panelNode;
    var rootTemplateElement = helloWorldRep.myTag.append(
        {}, parentNode, helloWorldRep);
}

We are using panel's member variable panelNode, which represents content area of the panel - final HTML will be inserted into it. Notice that we are using getPanel function to get our panel for the current context (page) where panelName represents ID of the panel, see part II.

HTML is inserted into the panel by calling append method of the template tag. This method has three parameters. The first one is used to provide data for the template, it's just empty object in our case as there are no dynamic properties in our template. The second is the parent DOM node and the last represents context object (this) that contains callback methods (data providers and event handlers). It can be null in our case as we don't have any callbacks yet, but it's good practice to use the template itself. It's usually the template which defines all these callbacks. Return value is root element of the created DOM (we don't need it for now).

In order to test the example just press Say Hello toolbar button (multiple times). Following screenshot shows how the output should look like.

Dynamic template

Let's make our template a bit more complicated and see how to (a) use dynamic properties, (b) localize the message and (c) use an event listener.

var helloWorldRep = domplate(
{
    myTag:
        DIV({class: "MyDiv", onclick: "$onClick"},
            SPAN($HW_STR("helloworld.message")),
            SPAN(" "),
            SPAN("$date")
        ),

    onClick: function(event)
    {
        alert("Hello World!");
    }
});

The templates uses a dynamic property date so, don't forget to provide the actual value when inserting the template output into the page.

onMyButton: function(context)
{
    var panel = context.getPanel(panelName);
    var args = {
        date: (new Date()).toGMTString()
    };
    var root = helloWorldRep.myTag.append(args, panel.panelNode, null);
},

Generated markup should look as follows:

<DIV class="MyDiv">
    <SPAN>Hello World!</SPAN>
    <SPAN> </SPAN>
    <SPAN>Wed, 04 Jun 2008 11:06:28 GMT</SPAN>
</DIV>

$date represents an expression that looks for a property named date within provided args object. The onclick: "$onClick" statement looks for a method named onClick within the provided args object and if it's not there it looks within context object (the third parameter of the append method). Finally, the Hello World! message is localized using our $HW_STR method that comes from part IV.

Example extension (helloworld-0.0.5.xpi) can be downloaded here.

Domplate & JQuery

Entire engine is completely independent of the rest of Firebug API and so, it can be also easily used individually (e.g. within a web page). There is already some activity in this area. Christoph Dorn (creator of FirePHP) wrapped it into a jquery plugin. You can download it here.

I have already mentioned a few times that we'll take a look at how to properly localize our extension. Even if the process is nothing new for experienced extension programmers, I think it should be part of this tutorial.

First of all, let's extend our directory structure. We'll need a new place where to put localized strings.

helloworld@janodvarko.cz/
    chrome/
        content/
            helloworld/
                helloWorld.xul
                helloWorld.js
        locale/
            en-US/
                helloWorld.dtd
                helloWorld.properties
    defaults/
        preferences/
            prefs.js
    chrome.manifest
    install.rdf

There are two new files: helloWorld.dtd contains entities that are used to localize strings contained in XUL files and helloWorld.properies is used to localize strings in JavaScript code.

To let Firefox know about these files, we need to modify our chrome.manifest file and append following line to it.

locale  helloworld  en-US       chrome/locale/en-US/

Localizing strings in XUL files

All strings in our helloWorld.xul are localized using helloWorld.dtd locale file. This DTD file contains list of entities with corresponding localized strings. In our extension we have to localize just a toolbar button. So, we need two following entities defined in it.

<!ENTITY  helloworld.mybutton.label         "Say Hello">
<!ENTITY  helloworld.mybutton.tooltip       "Push to say hello">

In order to use these entities within the helloWorld.xul file we have to create a reference to the locale file.

<!DOCTYPE helloworldDTD SYSTEM "chrome://helloworld/locale/helloWorld.dtd">

Sometimes you need to reference multiple DTDs from single XUL file. Here is an article that explains how to do it.

Now, we have to replace the strings by the entities that comes from the locale file. Following source code shows how the toolbar button definition looks after the replacement (see label and tooltiptext attributes).

<toolbarbutton id="hwMyButton"
    class="toolbar-text-button"
    label="&amp;helloworld.mybutton.label;"
    tooltiptext="&amp;helloworld.mybutton.tooltip;"
    command="cmd_hwMyButton"/>

Localizing strings in JavaScript code

There is also a few strings in our JavaScript code that must be localized as well. We'll do it by using a standard string bundle that gets the strings from helloWorld.properties file (see more about property files). This file contains following set of strings (name=value pairs).

helloworld.option1=Option1
helloworld.option2=Option2
helloworld.paneltitle=Hello World!
helloworld.message=Hello World!

Now, when we have the property file ready, we need to create a string bundle. There are two ways how this can be done. We can use nsIStringBundleService and read the property file in JavaScript.

var src="chrome://helloworld/locale/helloWorld.properties";
var localeService =
    Components.classes["@mozilla.org/intl/nslocaleservice;1"]
    .getService(Components.interfaces.nsILocaleService);
var appLocale = localeService.GetApplicationLocale();
var stringBundleService =
    Components.classes["@mozilla.org/intl/stringbundle;1"]
    .getService(Components.interfaces.nsIStringBundleService);
var bundle = stringBundleService.CreateBundle(src, appLocale);

See chapter 11. Localization from Creating Application with Mozilla book for more information about string bundles.

The other way is to define the bundle in helloWorld.xul, using stringbundleset and stringbundle elements. This method is simpler and used in our extension.

<stringbundleset id="stringbundleset">
    <stringbundle id="strings_helloWorld"
        src="chrome://helloworld/locale/helloWorld.properties"/>

</stringbundleset>

As soon as the string bundle is ready we have to make sure the JavaScript code loads every string from it instead of using just literal strings. In order to make things a bit easier we'll define two helper functions that load a given string from the string bundle and returns its localized value.

function $HW_STR(name)
{
    return document.getElementById("strings_helloWorld").getString(name);
}

function $HW_STRF(name, args)
{
    return document.getElementById("strings_helloWorld")
        .getFormattedString(name, args);
}

Following source code shows how the $HW_STR method is used to localize Options menu items defined in the previous part.

getOptionsMenuItems: function(context)
{
    return [
        this.optionMenu($HW_STR("helloworld.option1"),
            "helloworld.option1"),
        "-",
        this.optionMenu($HW_STR("helloworld.option2"),
            "helloworld.option2")
    ];
}

If you need to put some dynamic values into the localized string use the $HW_STRF method. This method takes an extra parameter - array of arguments, which are substituted into the string in the bundle. See the following example how to use it.

Here is a localized string with placeholders where dynamic data should be inserted:

helloworld.formattedstring=Here is a value: %S and here is another: %S.

And here is the piece of JavaScript that loads the string:

var localizedString = $HW_STRF("helloworld.formattedstring",
    ["value 1", "value 2"]);

If you are interested in more details how to properly localize Firefox extensions, read this article.

Example extension (helloworld-0.0.4.xpi) can be downloaded here.

FBTrace Console

by Honza

I have already mentioned a few times how useful is FBTrace (made by John J. Barton) for debugging Firebug. So, for those who are interested how Firebug works internally, it's something I would definitely recommend to use.

As the name indicates, FBTrace represents support for tracing within Firebug, notice that it isn't part of the release version of Firebug. So, if you would like to try it out, you have to download a version that includes tracing support from public SVN repository. See this readme.txt for more details about individual branches.

At the present, I would recommend to use branch Firebug 1.2 (or download beta with tracing here) as it's currently the version under development.

If you have managed to successfully install Firebug with tracing support, you should see a new panel called FBTrace. This panel shows many options (you can see them in the Options menu too) that represent various areas in Firebug internal framework. If you want to see some log messages related to a specific area, just click appropriate option (it's bold if it's on) and refresh the current page (or restart Firefox) - depends on specific option. Now, Firebug should print bunch of log messages into the system console window.

In order to see the system console, don't forget to launch Firefox with -console option on the command line.

FBTrace panel

I am using the console quite often (sometimes it's only the way how to track down what is actually happening) and I have soon realized how much I am missing the following:

  1. Quickly find out where is the piece of code that prints the message.
  2. Search within all messages whether specific log is there.
  3. Put a separator at the end of the list.
  4. Clear the log.

A few weeks ago, I couldn't resist and I have written down a simple replacement of the tedious and black system console. The new console is based on domplate - quite powerful UI template system in Firebug (I'll write something about this too) - it intercepts FBTrace's logs and displays it in more readable way.

Every message is expandable so, you can see additional information. E.g. there is a stack trace for each log (you can click it and see the source code).

There are also some actions available in the toolbar.

  1. Clear - removes all messages from the log.
  2. Find - displays standard find bar at the bottom of the window.
  3. Bookmark - puts a separator at the end of the list.
  4. Restart Firefox
  5. Exit Firefox

All these simple features helped me a lot. I haven't spent much time on testing (used with: WinXP, FF3, FB 1.2) as it's done mainly for my personal usage. But, if you want to explore more how Firebug's code works and you think this could help, download the new trace-console from here.

If it turns out to be really useful, it could be perhaps core part of the FBTrace in the future.

Enjoy!

New version of Firecookie is now available. It includes a few bug fixes and bunch of new features. Thanks for all the ideas and comments you have posted!

This version is intended as beta and I would like to keep it here for some time. Even if it's quite stable, I would appreciate some testing and make sure everything works fine. As soon as I get positive feedback, I'll move it to Mozilla add on site. The review process will be easy then :-)

Download Firecookie 0.5beta5 (Latest Beta Version, compatible with Firefox 3 RC1)

Firecookie home page
Firecookie on Mozilla.org (Latest Version: 0.6 - Pending Review)

New features in v0.5 since v0.0.5 - big step ahead ;-) :

  1. List of cookies can be sorted by clicking on a column header (0.0.6).
  2. Click on a cookie event (cookie name) in the Console tab navigates the user to Cookie tab. (0.0.6)
  3. Cookies from all sub-domains of the current domain are displayed. (0.0.6)
  4. There is a new column with cookie size.
  5. The list can be filtered by cookie path (a filter option). So, only cookies matching the current path are displayed.
  6. Rejected cookies are displayed too (a filter option).
  7. Cookies from redirected domains are displayed too. This is useful e.g. in login systems, which can redirect the user over different domains while setting cookies.
  8. Cookies set from embedded iframes are displayed too.
  9. Columns in the cookie list are resizable. It's persistent and can be reset from context menu of the header.

And of course, if you would have any further ideas or comments please let me know! Especially the UI design is always tough (and your observation could have impact even on other features in Firebug). For instance, I am not sure about the way how the filtering is done...