You write Safari extensions using HTML, CSS, and JavaScript, with support for HTML5 and CSS3. Safari exposes a set of methods and properties to JavaScript for extensions to use, letting your extension do things that scripts normally can’t.
To develop extensions for Safari, you first need to sign up for the Safari developer program online, at http://developer.apple.com. You need to join the program and obtain a certificate before your extension can be installed.
What Your Extension Can Do
Safari extensions let you add persistent items to Safari, such as controls, local or web-based content, and scripts that modify web-based content.
You can create controls by adding buttons to the Safari toolbar, adding contextual menu items, creating extension bars, or injecting controls into webpages.
You can display HTML content in an extension bar, in its own window or tab, or inject it into webpages.
Your extension can run invisibly in the background.
You can modify and reformat web content by applying scripts and style sheets.
Your scripts and style sheets can be applied either universally or selectively, using whitelists and blacklists of URL patterns to determine which web pages they should be applied to.
To see examples of Safari Extensions, visit the Safari Extensions Gallery (http://extensions.apple.com/).
The Extension Parts List
An extension starts as a folder. Depending on what you want your extension to do, you put some or all of the following items into the folder:
Global HTML page—An HTML page containing support code for your extension, typically JavaScript. This page is never displayed, but it can contain HTML elements, such as <audio>, for example, that are used by other parts of your extension. The global HTML page is loaded once—when the application launches or your extension is installed or enabled—and has access to the Safari application API. This is the right place to code buttons for the Safari toolbar or contextual menu items.
Extension bars—HTML, CSS, JavaScript, and media files. Extension bars can display controls and a short strip of HTML content, such as scrolling headlines or a stock ticker; extension bars have access to the Safari application-level API and can also contain code for Safari toolbar items or contextual menu items.
Injected scripts—JavaScript files to be injected into browser content. These scripts can read, modify, add to, or delete content, and can be applied selectively to webpages using URL patterns.
Injected style sheets—User style sheets that can modify the display of web content by overriding or adding to the styles normally applied. These style sheets can also be applied selectively using URL patterns.
Icon.png—The icon for your extension. It should be at least 64x64 pixels and can be larger. For best results, include an Icon-48.png and Icon-32.png for optimized display at smaller sizes. If your icon is exactly 64x64, you should name the image file Icon-64.png.
Content files—You can display HTML content in a full window or tab or inject it into pages by creating an iframe. The content source files can be hosted on remote web servers but it is recommended that they reside in your extension package.
Any other images or media your extension needs.
Extension Architecture
You can think of extensions as being divided into two parts: a part that interacts with the Safari application, and a part that interacts with web content.
The part of an extension that interacts with the Safari application resides in either your extension’s global HTML page or in extension bars, or in both. The part that interacts with web content resides in JavaScript files or CSS style sheets that are injected into content pages.
The division between these parts is strict, but you can send messages between them using proxies. If the global HTML page or an extension bar page needs to act on web content, it sends a message via the webpage proxy, where an injected script can act on it.
Similarly, if an injected script needs to make use of code in the global HTML page or an extension bar, it can send a message via the tab proxy.
The extension architecture is illustrated:
The extension architecture
An extension does not necessarily need to have both of these parts—an extension can operate only on the Safari application or only on web content. For example, a toolbar button to close a window or insert a tab would interact only with the application, while a style sheet that reformats websites into black text on a white background would operate only on web content.
The Safari Extensions JavaScript API
In addition to the usual JavaScript methods, extensions have access to a special JavaScript API that lets them access the Safari application and web content. The full API is documented in Safari Extensions Reference, but this section covers the main things you need to know.
Classes and Properties
The Safari extensions API includes several classes—such as SafariBrowserWindow, SafariBrowserTab, and SafariWebPageProxy—representing, respectively, a window, a tab, and the webpage loaded in a tab. You rarely, if ever, use the actual class names in your code, however. Instead, your extension JavaScript uses the SafariNamespace object, safari, followed by a chain of properties. For example:
safari.application.activeBrowserWindow returns the active instance of SafariBrowserWindow.
safari.application.activeBrowserWindow.activeTab returns an instance of SafariBrowserTab.
safari.application.activeBrowserWindow.activeTab.page returns an instance of SafariWebPageProxy.
As usual in JavaScript, there is more than one way to address a particular object and the chain of properties goes both ways—a browser window has a tabs property representing its tabs, for example, and each tab has a browserWindow property representing its parent window.
The Application and Extension Objects
The SafariApplication object allows you to work with windows and tabs, and to respond to commands from toolbar items and contextual menu items (also known as shortcut menu items). For example, you open a new browser window like this:
safari.application.openBrowserWindow();
The SafariExtension object allows you to add and delete buttons, menu items, scripts, and style sheets from your extension. For example, the following code snippet adds a simple black-and-white style sheet to the injected contents of your extension:
var bw = "body { color:black !important; background:white !important}" ;
safari.extension.addContentStyleSheet(bw);
You can access the SafariApplication and SafariExtension classes from your extension’s global HTML page or from an extension bar. The classes are accessed as safari.application and safari.extension.
Web Content Interaction
Scripts that are injected into web content can access the DOM of webpages they are injected into, allowing them to read and modify the content. Injected scripts use the normal JavaScript API—getElementsByTagName(), innerHTML, and so on—but because they are injected into a webpage, they have the privileges of a script loaded from the same domain the content comes from. In other words, a script injected by an extension can do anything the website author’s own scripts can do.
You can also designate style sheets as injected content. Injected style sheets are treated as user style sheets, as defined by the W3C. This means that they can override styles applied by the webpage’s author if they are declared important. For example, to override the body element’s background color, you could declare:
body { background: #ffffff !important }
The style cascades in the following order:
- Your injected style sheet’s normal declarations are applied.
- The website author’s style sheet’s normal declarations are applied.
- Styles declared as important in the website author’s style sheets are applied.
- Styles declared as important in your injected style sheets are applied, overriding any previous\ definition (you have the last say).
There are several types of events in the Safari Extensions API, but there are four event types you commonly use: “command”, “validate”, “contextmenu”, and “message”.
Command events are generated when the user clicks an extension’s toolbar item or chooses an extension’s contextual menu item. You respond to commands by installing listener functions for “command” events, then testing the command name. Listener functions are added using addEventListener("command", functionName, false).
You can add an event listener function to the window or application:
safari.application.addEventListener("command", myCmdHandler, false);
Validate events are sent at various times prior to any command events, to ensure the command is valid, and before showing context menu items. You can respond to a “validate” event by disabling your toolbar item or menu item, modifying what it does, or by doing nothing if the command should be executed normally.
You can respond to command and validate events in either your global HTML page (recommended) or in an extension bar.
Contextmenu events are sent when the user right-clicks or control-clicks in a webpage, but before any contextual menu is displayed, and before any validate or command event associated with the menu are sent. Your injected scripts can respond to these events by passing contextual information, such as what element is being clicked on, to your global script or extension bar, which can modify the contextual menu before it is presented.
Message events are your way to pass information between parts of the extension. Messages are sent using dispatchMessage(messageName, data). You listen for messages by installing a listener function for “message” events: addEventListener("message", functionName, false).
The message API is accessible from all parts of an extension—the global HTML page, extension bars, and injected scripts.
Proxies are used to support message passing across the application/content boundary. A proxy object represents a pair of objects on different sides of the boundary. There is a page proxy object (class SafariContentWebPage / SafariWebPageProxy) for sending messages to injected scripts and a tab proxy object (class SafariBrowserTab / SafariContentBrowserTabProxy) for sending messages to an extension bar or to the global page.
How To Create Extensions
Extensions are created using Extension Builder, which is built into Safari 5.0 and later. Enable the Develop menu in the Advanced pane of Safari Preferences. Then choose Show Extension Builder in the Develop menu.
Note: Extensions were introduced in Safari 5.0, and were disabled by default, so in Safari 5.0 you must enable extensions in the Develop menu before you can show Extension Builder.
Extensions are enabled by default in Safari 5.0.1 and later.
An extension consists of an extension package—a signed, compressed folder with the .safariextz extension, containing all your extension's files and a generated plist file that tells Safari how your extension is organized and what it does.
Note: The Safari extension package is a Mac OS X bundle. You don’t need to understand bundles to create extensions, but you may find it helpful. To learn more about bundles and the plist, see Bundle Programming Guide.
To create an extension, first make an extension folder by clicking the + button in Extension Builder and choosing New Extension, then create the HTML, CSS, JavaScript, and media files you need and put them in the folder. The folder has the .safariextension extension when it is created.
Use Extension Builder to specify details about the structure and behavior of your extension and build an extension package. Clicking Build creates a compressed, installable version of your extension with the .safariextz extension. For details, see “Using Extension Builder.”
Here’s a more detailed description of the things you put into the extension folder:
Global HTML Page
Your extension can have one global HTML page. It is not mandatory. This page is loaded only once, when Safari loads your extension. It is never displayed. It exists as a container for JavaScript. You can add JavaScript to your global page in-line, or include it in a separate file or files in your extension.
If you are adding items to the main Safari toolbar, it’s generally best to write a global HTML page to specify what the toolbar items do, but you can also specify what the items do in a extension bar file. For details, see “Adding Buttons to the Main Safari Toolbar.”
If you are adding contextual menu items, it’s generally best to write a global HTML page and specify what the menu items are and what they do, but again, you can also specify contextual menu items and actions in a toolbar file. For details, see “Adding Contextual Menu Items.”
Putting the code for toolbar items and contextual menus in your global page is more efficient than putting it in an extension bar file because extension bar files are reloaded every time a window is opened, whereas the global file is loaded only once during the application’s lifetime.
If your injected scripts use a large amount of code or data, it should be moved to the global HTML page, so time isn’t spent reloading large blocks of code or data each time the user opens a webpage. Injected scripts can’t call functions defined in your global page directly, but injected scripts can pass messages to the global page, and the message handler in the global page can call other functions. For details, see “Messages and Proxies.”
Extension Bar Files
Extension bars are toolbar-sized strips added to the Safari frame—below the bookmarks bar and above the tab bar—and dedicated to a particular extension. There can be multiple extensions with bars installed, and multiple bars per extension. If more than one extension bar exists, they are stacked. An example of an extension bar is shown in Figure 1-2.
Each extension bar has a label which is listed in the View menu (the View menu is hidden by default in Windows, but can be access through the gear button) and the menu item can be toggled to show or conceal each bar in the stack.
Extension bar example
You can use extension bars to add controls to Safari or to display other content, such as a stock ticker, weather forecast, flight information, or headlines. Extension bars are only 30 pixels tall, so content that needs a taller display space should be shown in its own tab or injected into the browser content instead.
Extension bar files can access the Safari application to do things like opening and closing windows and tabs, loading URLs, responding to Safari toolbar items, and responding to menu choices in contextual menus. Extension bar files cannot manipulate content loaded in a browser tab, however; for that you need to use injected scripts or styles (see “About Safari Extensions”). You can send and receive messages from scripts in extension bars.
You create extension bars using HTML (also CSS, JavaScript, and any media files). You don’t need to do anything special in the HTML to have your content displayed in an extension bar—just tell Extension Builder which HTML files are sources for extension bars.
If your extension bar uses images or other media, they can be included in the extension package or loaded from the web at runtime. It is strongly recommended that you use local media whenever possible.
Extension bar files are loaded each time Safari opens a window, so if your extension bar has code or data that needs to load only once, you should put that material in a global HTML page instead.
If you want to create an extension bar, see “Adding Extension Bars.”
Injected Scripts and Style sheets
You can have Safari inject scripts or style sheets that you provide into the webpages Safari loads. These injected scripts and styles can read and modify browser content.
Scripts can be specified as End scripts (interpreted when the page’s onload event occurs), or Start scripts (interpreted before the page is parsed). Most scripts are End scripts. Scripts that block unwanted content before it displays are the most common use for Start scripts. You can have both Start scripts and End scripts.
Style sheets are applied as user style sheets, so normal declarations in them precede the webpage author’s declarations in the cascade, but !important declarations are applied after the author’s declarations, allowing user style sheets to override the webpage author’s styles.
You can use URL patterns to decide which webpages your scripts and style sheets are applied to. Use URL patterns when creating a blacklist or a whitelist for your extension. The blacklist contains URL patterns for webpages you don’t want to inject scripts or styles into. The whitelist contains URL patterns for webpages you do want your scripts and styles injected into. For details, see “The Extension Builder Interface.”
If you want to inject scripts into webpages, see “Injecting Scripts.”
If you want to apply user style sheets to webpages, see “Injecting Styles.”
The plist Files
The Info.plist file contains your extension’s metadata. This includes the extension name, author, and version, as well as information about how your extension is organized—whether it has a global HTML page, extension bars, or injected scripts, and which files are used for what. If your extension has settings, they are also defined in a plist file—Settings.plist. Settings.plist is optional, but Info.plist is required. When someone talks about your extension’s plist file, they generally mean Info.plist.
The plist files are created for you using Extension Builder, so you shouldn’t need to do anything with them yourself. But to really understand how extensions work, you need to know the plist files exist. All the fields you fill out in the Extension Builder interface are stored in a plist file.