December 09, 2004

Determining whether your extension is running in FireFox or Thunderbird

One of the things that is cool about building extensions for FireFox, is that in most cases they can easily made to run within Thunderbird. However, sometimes your extension needs to know which environment it is running within. You can determine this by checking the navigator.vendor property.

var firefox = false;
var thunderbird = false;

var vendor = navigator.vendor;

if(vendor == "FireFox")
{
    firefox = true;
}
else if(vendor == "Thunderbird")
{
    thunderbird = true;
}

dump("Running in FireFox : " + firefox + "\n");
dump("Running in Thunderbird : " + thunderbird + "\n");

This makes it easy to branch your code when necessary.

December 9, 2004 in FireFox Development, Thunderbird Development | Permalink | Comments (2)

December 08, 2004

Create an empty menuseparator in a FireFox extension

Here is a quick tip on how to add an empty separator into a FireFox extension menu. By default, the XUL menuseparator tag draws a line across the menu. Here is how to add a separator that just adds a space (no line):

<menuseparator style="visibility:hidden" />

Pretty simple, but it took me a little searching to figure out, so I thought I would post it here.

December 8, 2004 in FireFox Development | Permalink | Comments (0)

December 01, 2004

Simple Class to Make XUL StatusBarPanel Icon Blink from FireFox extension

I have put together a simple class that will make the icon in a XUL StatusBarPanel tag within a FireFox extension blink.

In order for this to work, the StatusBarPanel tag must have its class set to statusbarpanel-menu-iconic.

For example, here is the XUL tag:

<statusbarpanel class="statusbarpanel-menu-iconic" id="my-status" />

and the associated style sheet:

statusbarpanel#my-status[status="on"]
{
    list-style-image: url("chrome://appname/skin/on_status.png");
}

statusbarpanel#my-status[status="off"]
{
   list-style-image: url("chrome://appname/skin/off_status.png");
}

And finally, here is the code to use the class and make the icon blink:

var menuStatusBar = document.getElementById("my-status");

var blinker = new StatusBarPanelBlinker();
blinker.setStatusBarPanel(menuStatusBar);
blinker.setOnStatus("on");
blinker.setOffStatus("off");

blinker.startBlink();

Note that I am still getting familiar with XUL and the JavaScript API, so there may be a way to make this more generic to work with other tags. If I figure it out, I will update the class.

Here is the class:

/*
StatusBarPanelBliner Class
Created by Mike Chambers
http://mesh.typepad.com

Makes the icon of an XUL statusbarpanel instance blink.

Usage:

var blinker = new StatusBarPanelBlinker();
blinker.setStatusBarPanel(menuStatusBar);
blinker.setOnStatus("on");
blinker.setOffStatus("off");
blinker.startBlink();

The StatusBarPanel tag must have its class set to "statusbarpanel-menu-iconic"	

For example, here is a tag:



and the associated style sheet:

statusbarpanel#my-status[status="on"]
{
list-style-image: url("chrome://appname/skin/on_status.png");
}

statusbarpanel#my-status[status="off"]
{
list-style-image: url("chrome://appname/skin/off_status.png");
}


*/

/***** Constructor *****/
function StatusBarPanelBlinker()
{
}

/***** Properties *****/

StatusBarPanelBlinker.prototype.onStatus = undefined; //String
StatusBarPanelBlinker.prototype.offStatus = ""; //String
StatusBarPanelBlinker.prototype.interval = 750; //Number
StatusBarPanelBlinker.prototype.statusBarPanel = undefined; //StatusBarPanel
StatusBarPanelBlinker.prototype.intervalId = undefined; //Number

/***** Methods *****/

//starts the blinking of the icon
StatusBarPanelBlinker.prototype.startBlink = function()
{
 this.intervalId = setInterval(this.doBlink, this.interval, this);
}


//stops the icon from blinking
StatusBarPanelBlinker.prototype.stopBlink = function()
{
 clearInterval(this.intervalId);
 this.intervalId = undefined;
 
 this.statusBarPanel.setAttribute("status", this.onStatus);
}


//private internal function that toggles the state of the icon
StatusBarPanelBlinker.prototype.doBlink = function(scope)
{
 if(scope == undefined)
 {
  scope = this;
 }
 
 var newStatus = (scope.statusBarPanel.getAttribute("status") == scope.onStatus)?
 scope.offStatus: scope.onStatus;
 
 scope.statusBarPanel.setAttribute("status", newStatus);
}


//returns whether the icon is currently blinking, i.e. startBlink() has been called
StatusBarPanelBlinker.prototype.isBlinking = function()
{
 return !(this.intervalId == undefined);
}

/***** Getter / Setters *****/

//set the statusbarpanel instance whose icon we will blink
StatusBarPanelBlinker.prototype.setStatusBarPanel = function(statusBarPanel)
{
 this.statusBarPanel = statusBarPanel;
}

//return the statusbarpanel instance that the class is making blink
StatusBarPanelBlinker.prototype.getStatusBarPanel = function()
{
 return this.statusBarPanel;
}


//interval between blink states in milliseconds. This is basically how fast
//the icon blinks
StatusBarPanelBlinker.prototype.setBlinkInterval = function(interval)
{
 this.interval = interval;
}

//returns the blink interval
StatusBarPanelBlinker.prototype.getBlinkInterval = function()
{
 return this.interval;
}


//the value of the statusbarpanel's status attribute that displays the icon
StatusBarPanelBlinker.prototype.setOnStatus = function(onStatus)
{
 this.onStatus = onStatus;
}

//return's on icon status for statusbarpanel

StatusBarPanelBlinker.prototype.getOnStatus = function()
{
 return this.onStatus;
}

//the value of the statusbarpanel's status attribute that displays the off
//status of the blink / icon
StatusBarPanelBlinker.prototype.setOffStatus = function(offStatus)
{
 this.offStatus = offStatus;
}


//return's off icon status for statusbarpanel
StatusBarPanelBlinker.prototype.getOffStatus = function()
{
 return this.offStatus;
}

Post any comments, questions or suggestions in the comments section.

December 1, 2004 in FireFox Development | Permalink | Comments (0)

November 29, 2004

Opening an Extension's About box from a FireFox Extension

Here is another quick FireFox extension tip (which took me forever to figure out). This code snippet shows how to open the About box for your extension from within the extension.

var aboutXULPath = "chrome://extensionname/content/about.xul";
openDialog(aboutXULPath, "", "chrome,modal");

Where aboutXULPath is the path to the about box XUL file specified in the extension's install.rdf file.

Post any suggestions / tips in the comments

November 29, 2004 in FireFox Development | Permalink | Comments (0)

Creating a new Tab from a FireFox Extension

Here is a quick tip on how you can create a new tab that opens to a specified URL via a FireFox extension:

	var myUrl = "http://mesh.typepad.com";
var tBrowser = document.getElementById("content");
var tab = tBrowser.addTab(myUrl);

Post any improvements and / or tips in the comments.

November 29, 2004 in FireFox Development | Permalink | Comments (5)

Debugging FireFox Extensions on OS X

If you are developing extensions on FireFox on OS X, and are having trouble getting your extension to install or work, you can get some additional debug information from FireFox by starting it from the terminal.

On OS X, you can do that with the following command:

/Applications/firefox.app/Contents/MacOS/firefox

If you need to start the ProfileManager you can do it like so:

/Applications/firefox.app/Contents/MacOS/firefox -ProfileManager

Note, you may need to change the path to point to where you installed the FireFox bundle.

Any debug or error information will then be sent to the terminal window, which can make it much easier to debug any problems that might arise.

You can find some additional tips on setting up your debug environment for extension development here.

November 29, 2004 in FireFox Development | Permalink | Comments (1)

November 28, 2004

Building and Packaging FireFox Extensions on OS X

I have been learning how to build FireFox extensions on OS X. Actually doing a build and packaging it to install and test the extension is a little involved (you have to create multiple jar files and and an xpi file). Luckily there are a couple of scripts available that basically automate this process:

I am running on OS X so I was using the Bash / Linux script. However, this needed a few tweaks to get it to work correctly. Basically, I had to fix some path errors, and also get it to not package the mac .DS_Store folders.

Here is the updated script, which is based on the script found here.

#!/bin/bash
# build.sh: build JAR and XPI files from source
# based on Nathan Yergler's build script
# Modified to work on OS X by Mike Chambers (http://mesh.typepad.com)

#### editable items (none of these can be blank)
APP_NAME=helloworld    # short-name, jar and xpi files name.
HAS_DEFAULTS=0       # whether the ext. provides default values for user prefs etc.
HAS_COMPONENTS=0     # whether the ext. includes any components
HAS_LOCALE=0         # package APP_NAME.jar/locale/ ?
HAS_SKIN=1           # package APP_NAME.jar/skin/ ?
KEEP_JAR=1           # leave the jar when done?
ROOT_FILES="license.txt install.rdf install.js" # put these files in root of xpi
#### editable items end

TMP_DIR=build.tmp

#uncomment to debug
#set -x

# remove any left-over files
rm $APP_NAME.jar
rm $APP_NAME.xpi
rm -rf $TMP_DIR

# create xpi directory layout and populate it
mkdir $TMP_DIR
mkdir $TMP_DIR/chrome

if [ $HAS_COMPONENTS = 1 ]; then
  mkdir $TMP_DIR/components
  cp components/* $TMP_DIR/components
fi

if [ $HAS_DEFAULTS = 1 ]; then
  DEFAULT_FILES="`find ./defaults -path '*DS_Store*' -prune -o -type f -print | grep -v \~`"
  cp --parents $DEFAULT_FILES $TMP_DIR
fi

# Copy other files to the root of future XPI.
cp $ROOT_FILES $TMP_DIR

# generate the JAR file, excluding .DS_Store and temporary files
zip -0 -r $TMP_DIR/chrome/$APP_NAME.jar `find content -path '*DS_Store*' -prune -o -type f -print | grep -v \~`
if [ $HAS_LOCALE = 1 ]; then
  zip -0 -r $TMP_DIR/chrome/$APP_NAME.jar `find locale -path '*DS_Store*' -prune -o -type f -print | grep -v \~`
fi
if [ $HAS_SKIN = 1 ]; then
  zip -0 -r $TMP_DIR/chrome/$APP_NAME.jar `find skin -path '*DS_Store*' -prune -o -type f -print | grep -v \~`
fi

# generate the XPI file
cd $TMP_DIR
zip -r ../$APP_NAME.xpi *
cd ..

if [ $KEEP_JAR = 1 ]; then
  # save the jar file
  mv $TMP_DIR/chrome/$APP_NAME.jar .
fi

# remove the working files
rm -rf $TMP_DIR

In order to use this, you need to set up the directory structure in a specific way. Using the HelloWorld extension from this tutorial (highly recommended), the directory structure would look like this:

You can download the files and directory structure from here.

Notice that the build.sh file is in the same directory as the install.rdf. In order to build the extension, edit the parameters at the top of the build.sh file (at a minimum set the APP_NAME variable to match your extension name), and then just run it from the terminal. If it doesn't run, make sure that it is set to be executable by running the following command:

chmod 755 ./build.sh

from the terminal.

Thanks to everyone in the Mozilla Extension forum for helping me out, roachfiend.com for a great extension tutorial, and to whoever wrote the Linux / Bash package script that the OS X script was based on.

November 28, 2004 in FireFox Development | Permalink | Comments (2)