Create your own browser extensions, Part 3: Extend your reach into Safari

Write a basic browser extension for Safari

Every browser has its fans, detractors, advantages, and disadvantages. One thing they all have in common is that people increasingly spend more time using them. This series examines how to build the same basic extension for Chrome, Firefox, and Safari. You'll learn what it is like to extend each browser, how hard or easy it is to perform some common tasks, and how to distribute your extension. In this article, you'll build an extension for Safari.

05 Apr 2013 - Added links to Part 4 in About this series and Resources.

Share:

Duane O'Brien, Software developer, Freelance

Photograph of Duane O'BrienDuane O'Brien is a tired computer scientist. He has written a number of articles on developing web applications and various PHP frameworks. To learn more about Duane, check out his blog or read his tweets.



05 April 2013

Also available in Russian Japanese Vietnamese

About this series

In this four-part series, build the Gawkblocker extension for three browsers: Chrome, Firefox, and Safari.

  • In Part 1, take your Google Chrome extension from inception to the app store.
  • In Part 2, build an add-on (or extension) for Mozilla Firefox.
  • In this part, tailor the extension to the Safari browser.
  • In Part 4, tweak your code for a browser-agnostic extension.

This article, the third in a four-part series on building browser extensions, shows you how to build an extension for Safari. You built the Gawkblocker extension for Chrome in Part 1 and for Firefox in Part 2. In this article, you port Gawkblocker to Safari. See Download to get the complete source code.

Gawkblocker redux

Recall that Gawkblocker enables you (and its other users) to block certain domains that you prefer not to visit, such as time-consuming blogs. Gawkblocker has several components:

  • A pop-up window that shows you what you're blocking
  • A visible browser icon that's the point of entry for the extension
  • An options page for configuring the domains that you want to block and where you want to go instead

In Chrome, your Gawkblocker extension attaches a listener to each tab or window and matches URLs to a block list, redirecting blocked URLs to a local page. The Firefox add-on behaves in much the same way, and you can reasonably expect similar behavior in Safari.

Gawkblocker reaches into the browser in specific ways to do things that you'll do with all the extensions you build in this series. As you did in the Chrome and Firefox articles, you want answers to these questions:

  • How hard is it to get a presence somewhere in the browser UI?
  • What is involved in persisting data between browser sessions?
  • How do the different parts of the extension talk to one another?
  • How deep can you go into the user's data?

You can answer these questions after you complete the process of building Gawkblocker for Safari.


Before you start

For this article you need a Mac running the current version of Safari (6.0 at the time this article was written). Apple also requires that extension developers sign up for the free Apple Developer Program (see Resources). You also want a tool that edits HTML, CSS, and JavaScript. Some experience using Safari with some extensions is helpful. If you lack such experience, browse the available extensions in the Safari Extensions Gallery (see Resources). Try a few of the extensions for this article.

You'll do most of your work in the Safari Extension Builder, which is built into Safari 5.0 and higher. With the Safari Extension Builder, you can build, install, reload, and uninstall Safari extensions from within Safari. To use it, you must download and install a Developer Certificate from the Safari Dev Center (see Resources) and follow the simple instructions. Your reference document will be the Safari Extensions Reference (see Resources).

See Download to get the complete source code.


Anatomy of a Safari extension

Gawkblocker for Safari will have:

  • A page that loads once when Safari starts (called a global page or global HTML page in Safari-development jargon)
  • A JavaScript file that contains some core functions (copied right out of the Chrome extension)
  • A combination pop-up/options page (ported from the Firefox add-on) — in Figure 1 — so you have only one page to manage
    Figure 1. The pop-up/options page
    Screen capture of the Safari extension's Pop-up/Options page
  • An icon on the toolbar that you use for your extension
  • A page that displays when the user clicks the toolbar icon (a popover window)

Injecting JavaScript

You can inject CSS or JavaScript into pages in Safari just as you can in Chrome and Firefox. You put the CSS or JavaScript into an external file and specify in the Extension Builder that you want to inject it. However, you won't inject it for Gawkblocker.

The pages are raw HTML, CSS, and JavaScript. The result is something of a hybrid of the Chrome and Firefox extensions.

The extension itself is defined in an Info.plist file. You won't modify this file directly. The Safari Extension Builder outputs it for you. Info.plist contains information similar to what you put in the manifest.json file in your Chrome extension, but Info.plist is an XML file.


Using the Safari Extension Builder

To use the Extension Builder, you must enable the Develop menu in Safari->Preferences->Advanced by selecting the check box for Show Develop menu in menu bar, as in Figure 2:

Figure 2. Enabling the Develop menu
Screen capture of Preferences dialog shows selected 'Show Develop menu in menu bar' checkbox

Now start the Safari Extension Builder from the Safari menu bar. When the Extension Builder first launches, it looks like a large empty box. Click the little plus (+) icon in the lower-left corner and tell it that you want to create an extension. A dialog box asks where you want to save the extension, as in Figure 3:

Figure 3. Creating an extension
Screen capture from Safari Extension Building showing how to create a new extension

Specify a folder location, and then copy into that folder:

  • The background.html and gawkblocker.js files from your Chrome extension.
  • The popup.html file from your Firefox add-on.
  • The jQuery library you're using (if any).
  • An Icon.png file. Your icon must be a square at least 64 x 64 pixels and transparent except for the actual image you want it to show. (For details, see the Safari Extensions Reference in Resources.)

With these files in place, you're ready to enter information into the Extension Builder. Figure 4 shows the initial set of fields to complete.

Figure 4. Configuring the extension, part 1
Screen capture of starting to configure Gawkblocker in the Extension Builder

In Figure 4, complete these fields:

  • Display Name: The name that you want your extension to show in the Extensions list.
  • Author: Your name.
  • Description: A short, descriptive phrase.
  • Website: A URL where a user might learn more.
  • Bundle Identifier: Something in the format orgtype.yourentity.yourextension (for example, com.dontgothere.gawkblocker).
  • Display Version: The version that's shown when the user checks the Extensions list.
  • Bundle Version: A more granular internal version that the operating system uses to check for updates.
  • Extension Website Access: Options here are None, Some (and provide a list), or All. To get the URL that the browser is loading, Gawkblocker must use the All access level.
  • Global Page File: The page that runs in the background.
  • Database Quota: A maximum size limit for the localStorage database for your extension. 1 MB in this case is overly generous.

So far the process is straightforward. But when you scroll down to add the Toolbar Items and Popovers, things are slightly trickier, as in Figure 5:

Figure 5. Configuring the extension, part 2
Screen capture of continuing to configure Gawkblocker in the Extension Builder

Start by defining the popover in the Popovers section. This section defines the window that Safari opens when the user clicks the Gawkblocker icon on the toolbar. Give the popover a unique Identifier, specify the File to load into the window, and specify the Width and Height of the window.

Now go to the Toolbar Items section to add the new Toolbar item. Give it a Label (the text in the tooltip), specify the Image to use for the button, enter the Popover to use (the one you just created), and give the toolbar item a unique Identifier.

At the top of the Extension Builder UI, you might now tell it to build and install the extension locally — but it won't work yet. You must make a couple of changes first.


Updating the Gawkblocker files

Only three files are in play: gawkblocker.js, background.html, and popup.html. The gawkblocker.js file that you copied over from the Chrome extension can be dropped right in without modification. But the other two pages need some updating.

Making the global page speak Safari

Obviously, you cannot call chrome.windows (a Chrome API) in the background.html page, so you must substitute in the corresponding Safari API calls. And you cannot send users to a local page as you did in Chrome with instead.html. Rather, you must send them right to the Madness video hosted on YouTube, as in Listing 1:

Listing 1. Sending users right to the video hosted on YouTube
...
if (!GB.getWatchThisInstead()) {
    GB.setWatchThisInstead("http://www.youtube.com/watch?v=N-uyWAe0NhQ");
}
...

Safari event listeners work a little differently than Chrome event listeners. In Safari, you specify the event that you're listening for and a handler for the event, and Safari passes the event to the handler. So you want a function to handle that event, as in Listing 2:

Listing 2. Safari event listener
function shouldIBlockThis(event) {
    var site;
    for (site in GB.getBlockedSites()) {
        if (event.url && event.url.match(site)) {
            event.preventDefault();
            event.target.url = GB.getWatchThisInstead();
        }
    }
}

With the event-handling function in place, listening to the event is trivial. Safari gives you a beforeNavigate event that's fired before any URL is called:

safari.application.addEventListener("beforeNavigate", shouldIBlockThis, true);

On the whole, the changes in the background.html page are fairly minimal. The changes in popup.html aren't much deeper.

Making the pop-up page speak Safari

In Firefox, you did much message passing from main.js to the pop-up page. In Safari, you can call to the global page, similarly to the Chrome approach. You can get and set values directly in the GB object. So, get the current redirect landing and set the value into the appropriate field:

$("#watchthatinstead").val(
   safari.extension.globalPage.contentWindow.GB.getWatchThisInstead());

After you replace all of the spots where you were passing messages to main.js with the appropriate calls to the global page, you don't have much else to do. The popover maintains state, unlike Chrome's pop-up window. Whenever the pop-up window opens, reset the state. Safari provides an event to make that easy:

safari.application.addEventListener(
   "popover", function() { $("#onestep").show();$("#options").hide();}, true);

You might as well clean up the way you update the block list, as in Listing 3:

Listing 3. Cleaning up the way you update the block list
function showBlockList () {
    var blockList = safari.extension.globalPage.contentWindow.GB.getBlockedSites();
    $("#blockedlist").children().remove();
    $("#blocklist").children().remove();
    var i=1;
    $.each(blockList, function (index, value) {
        $("#blockedlist").append("<div class='siterow' title='"+value+"'><div
 class='sitename'>"+index+"</div><span class='sitedesc'> : "+value+"
 </span></div>");
        $("#blocklist").append("<div id='site-"+i+"'><input type='button' 
 id='unblock-"+i+"' value='OH GO ON THEN' /> " + index + "</div>");
        $("#unblock-"+i).click(function () {
            safari.extension.globalPage.contentWindow.GB.removeBlockedSite(index);
            showBlockList();
        });
        i++;
    });
}

On the whole, not many changes were required. With minimal effort, you used the work from the Chrome and Firefox versions of Gawkblocker into a working Safari extension.


Testing from the Extension Builder

When you use the Extension Builder, you have tools to help in testing. First, the upper left of the Extension Builder has Install/Uninstall and Reload buttons that make it easy to install your changes for testing. Click Inspect Global Page to display a Web Inspector view of the background page. Figure 6 shows the Web Inspector in action:

Figure 6. Web Inspector for the global page
Screen capture of Web Inspector for the global page

Distributing your extension

When you're certain your extension is ready for prime time, you have several options for distributing it: You can download-distribute a packaged extension or submit your extension to the Safari Extensions Gallery.

Distributing a packaged extension

To distribute a packaged extension, download it from the Extension Builder. Click Build Package to get a .safariextz file that anyone can install. You can distribute it however you want (email, hosting, installers, and other methods). But you're responsible for handling updates and hosting.

Submitting to the Extensions Gallery

You can submit your extension for review and addition to the Extensions Gallery. Complete the required steps — more signing requirements, an agreement with Apple, and more details to provide. But the Extensions Gallery is the primary way that a Safari user is likely to discover your extension.


Finding your answers

Now that your Safari extension is complete, it's time to see how the answers to your questions turned out, compared to the answers for Chrome and Firefox:

How hard is it to get a presence somewhere in the browser UI?
Perhaps even easier than it was with Chrome. The Safari Extension Builder takes much guesswork out of the process.
What is involved in persisting data between browser sessions?
Using the same localStorage wrapper that you created for Chrome, you can easily persist data between browser sessions.
How do the different parts of the extension talk to one another?
By directly accessing the global page, you can easily get information to and from the various parts of the extension.
How deep can you go into the user's data?
Safari offers some granularity to the permissions you can request, but — with the ability to inject scripts into user pages and access all pages that a user visits — you can go deep. Also, worth noting: Safari doesn't appear to pop permission warnings the same way Chrome does.

Conclusion

Now that you've built the Gawkblocker extension three times (for Chrome, Firefox, and Safari), you might look for a way to combine the two core JavaScript files into a single file for all three browsers to use. In fact, that's exactly what you'll work on in Part 4. And, you can do a lot more with Safari extensions.


Download

DescriptionNameSize
Gawkblocker source codesafari-sourcecode.zip39KB

Resources

Learn

Get products and technologies

  • Evaluate IBM products in the way that suits you best: Download a product trial, try a product online, use a product in a cloud environment, or spend a few hours in the SOA Sandbox learning how to implement Service Oriented Architecture efficiently.

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source, Web development
ArticleID=863328
ArticleTitle=Create your own browser extensions, Part 3: Extend your reach into Safari
publish-date=04052013