Safari has a user-customizable toolbar that can contain a selection of buttons, such as a Home button, Zoom button, and New Tab button. Your extension can define new toolbar items that can be installed in the toolbar. These items also appear in the Customize Toolbar panel.
You control the actions of a toolbar item from either the global HTML page or from an extension bar by installing a listener function for the “command” event.
Your extension can add more than one button to the toolbar, but if you are adding more than a few you should not have them installed by default; you might also consider creating an extension bar for them instead.
Adding a button requires three steps: creating an image; filling out the appropriate fields in Extension Builder; and adding logic to make the button do something.
Buttons on the Safari toolbar are largely transparent, allowing them to be filled with the appropriate gradient for the current Mac OS X or Windows user interface. You do not need to draw the button itself, only the opaque part of its contents.
If you are used to working with alpha channels, create a 16 x 16 image consisting solely of an 8-bit alpha channel, with alpha set to 255 for the transparent part of the button (including the outline) alpha set to 0 for the opaque parts of the button, which will appear in black, and intermediate alpha values for anti-aliasing.
If you are not used to working with alpha channels, create a 16 x 16 pixel image with a transparent background. Fill in the button contents in black, or simply set the opacity for those pixels to 100%. If you draw or put text on the button in black, notice that some pixels are grey. This is anti-aliasing. These pixels are not completely black, so they are shaded. This is illustrated in Figure 6-1
If you save your image as-is, these shaded pixels appear as black because they are completely opaque. To preserve the anti-aliasing, make these grey pixels partly transparent. Depending on your image editor, this may be accomplished by partly erasing the pixels or by setting the pixel opacity directly.
Save your image as a .png file (portable network graphics).
If you have not already done so, click the + button in Extension Builder, choose New Extension, and give your extension a name. Create an image for each button you are adding and drag it into your extension folder.
Click New Toolbar Item in Extension Builder. This expands the Toolbar Items pane, as shown in Figure 6-2.
Enter a label for your toolbar item. This is a text label that is displayed if the bar has more buttons than can be shown and the user clicks the chevron to see the overflow.
Enter a palette label for your toolbar item. This is the text label that is displayed when the user is customizing the toolbar. It can be the same as the label, or it can be a bit longer if that makes the function of your toolbar item clearer. If this field is left blank, the label is used.
Enter a tool tip for your toolbar item. This is the text displayed when the mouse pointer hovers over the item. if this field is left blank, the label is used.
Drag a .png
image file into your extension folder. The image should ideally be 14x14 or 16x16 pixels. Larger images are cropped at 18x18 pixels. The image is an alpha mask. It must contain an 8-bit alpha channel defining the part of the button face that is drawn, and nothing else. Using an alpha mask allows your button to blend in with Safari’s native toolbar buttons on different platforms, even if the Safari UI should change. Once you have an image file in your extension folder, you can choose it from the Image pop-up menu. An image is required.
Enter an identifier. This field is required. The identifier is your name for the toolbar item. The name must be unique in your extension. You can identify the toolbar item later by iterating through the array of items and checking the value of the identifier
property:
var itemArray = safari.extension.toolbarItems; |
for (var i = 0; i < itemArray.length; ++i) { |
var item = itemArray[i]; |
if (item.identifier === "my lovely button") |
{ |
/* Do something. */ |
} |
Enter a command name. This is the event.command
property of the event that is generated when the user clicks your item in the toolbar. It does not need to be unique. For example, you might have both a toolbar button and a contextual menu item that issue the same command. If this field is left blank, he identifier is used.
If you check Include By Default, the item is installed in the toolbar when the extension is installed. Otherwise, the user must choose to add the item in the Customize Toolbar window.
When the user clicks the button, Safari emits a “command” event. The command
property of the event is the string you entered in the Command field in Extension Builder. If you left the Command field blank, the identifier is used instead.
You can respond to the “command” event by installing a listener function in either a global HTML page or an extension bar.
You can’t receive the “command” event in an injected script. If you need the command to initiate an action in an injected script, respond to the command in the global HTML page or an extension bar and send a message to the script. For details, see “Messages and Proxies.”
At various times, such as when a tab is added, Safari will ask you to validate the command. If there is any possibility that the command could be invalid, you should add an listener function for the “validate” event.
Your validate function should verify that the command is ready and should be enabled. For example, if your command reloads the active tab, you should verify that the active tab has a URL to reload. If the tab is empty, your validate function should disable the button. You can also modify the item’s image to reflect a modified behavior.
The validate event is fired when an item is added to the toolbar, so this is also a good moment to update badges.
You can have multiple UI items that issue the same command, such as a toolbar item and a contextual menu item. You can use the same event handlers, regardless of the source.
If your functions are part of the global HTML page, you should register your listener functions with the application:
safari.application.addEventListener("command", myCommandHandler, false);
safari.application.addEventListener("validate", myValidateHandler, false);
If your functions are part of an extension bar, you should register your listener functions with the extension bar’s parent window:
safari.self.browserWindow.addEventListener("command", myCommandHandler, false);
safari.self.browserWindow.addEventListener("validate", myValidateHandler, false);
While you can implement the event handlers in either a global HTML page or in an extension bar, it is more efficient to use a global HTML page, because the code is loaded only once, when Safari loads the extension, instead of once per window.
When your button is clicked, a “command” event is generated. You can listen for the event in either a global HTML page or in an extension bar. If you put the event handler in the global file, you should register for the event at the application level. If your event handler is in an extension bar, you should register with the extension bar’s parent window.
The difference is that there is only one instance of the global HTML page’s functions, but there is an instance of an extension bar in every open window.
If every instance of the extension bar registers for events at the application level, every instance responds to the command. If each instance registers with its parent window, only the instance in the window where the button was clicked responds to the command.
Using the global page is more efficient, as it loads only once. Furthermore, you shouldn’t create an empty extension bar just to hold an event handler. If your extension has a bar, however, it might make sense to put the event handler there, particularly if the action it takes is window-specific, like rearranging the tabs.
The only time you might want to put an event handler in the extension bar and register it with the application is if your command acts on all open windows. Then your choice would be to iterate through the windows in a global function or have a function local to each window that acts independently.
Register with the application: safari.application.addEventListener()
The window the event comes from is: event.target.browserWindow
.
Register with the parent page: safari.self.browserWindow.addEventListener()
The window the event comes from is: self.browserWindow
or event.target.browserWindow
. The two are equivalent.
The following example responds to the “reload-page” command event and the “reload-page” validate event. The validate function disables the control if there’s nothing for it to do, but the command handler checks anyway, in case things have changed since validation.
Listing 6-1 Reload command and validate handlers
function performCommand(event) |
{ |
if (event.command === "reload-page") { |
var currentURL = event.target.browserWindow.activeTab.url; |
if (currentURL) |
event.target.browserWindow.activeTab.url = currentURL; |
} |
} |
function validateCommand(event) |
{ |
if (event.command === "reload-page") { |
// Disable the button if there is no URL loaded in the tab. |
event.target.disabled = !event.target.browserWindow.activeTab.url; |
} |
} |
// if event handlers are in the global HTML page, |
// register with application: |
safari.application.addEventListener("command", performCommand, false); |
safari.application.addEventListener("validate", validateCommand, false); |
// if event handlers are in an extension bar, |
// register with parent window: |
// safari.self.browserWindow.addEventListener("command", performCommand, false); |
// safari.self.browserWindow.addEventListener("validate", validateCommand, false); |
Last updated: 2010-08-03