A Step-By-Step tutorial on building your own Safari extension (part 2)

PART 2: Building the extension

Getting started

This is the second part of the tutorial, If you missed the first part of this tutorial, you can find it here.

Open listed files using your preferred IDE or text editor:

  • global.hml
  • popover.html
  •  listner.js

Sublime Text 2

The End Script

The main goal of using an End Script,because it has access to the DOM of the web page. This is the only place where we can put ourJS code with the ability to addthe event listener on the visited page elements.

listener.js souce code

function clickHandler(o,recursive) {
	var e=(recursive)?o:o.target;
	if ($(e).is('a')) { 
		clicked element is a link so 
		send a message with link information
	    to global file (calls handleMessage function) 
	    safari.self.tab.dispatchMessage("link", $(e).get(0).href);
	} else {
		if the clicked element is not a link so 
		we need to go up to the parent element 
		recursively util a link tag is found.
		var parent = $(e).parents("a").get(0);
		if(parent) {
bind clickHandler function for each 
existing element of the document.
clickHandler will trigger when user
click on a element.

 As you can see in the source code above in line 28, the advantage of using JQuery.on() that the attached click Event handler clickHandler() will work for both current and FUTURE elements (like a new element created by a script). The purpose is that when the user clicks on an element clickHandler() will be executed and the clicked element (the object) itself will be passed as parameter to this function. Inside this function we check the element type. If it is a link or an element wrapped by a link tag <a> we dispatch a message containing link information to the Global Page.

The Global Page

global.html source code:

<!DOCTYPE html>
    <script type="text/javascript" charset="utf-8">
        const gui = safari.extension.popovers[0].contentWindow //Create the popover instance
		function handleMessage(msg){
				if (msg.name == "link"){
					//exectue addLink() inside popover
		//creating the event listener
		safari.application.addEventListener("message",handleMessage, false);

 As show above in the Global Page source code, the event listener in line 13 is executed once Safari launches or when enabling the extension, it tells handleMessage() to catch incoming messages from external scripts. When dispatchMessage() is executed inside “listener.js”, the message will be immediately cached by handleMessage() from the global page. Inside this function the message type is checked then addLink() from “popover.html” is called with passing the link URL as parameter. addLink() will update the container from the Popover by adding a new row. You should know that you can’t update Popover interface from the global page because it hasn’t got access to Popover DOM.

The Popover

The popover.html is the user interface. It’s based on usual Web scripting technologies HTML, CSS and JavaScript. As mentioned earlier we placed addLink() inside this script file because this is the only place we can access the Popover DOM.

popover.html source code:

<!DOCTYPE html>
  <script type="text/javascript" charset="utf-8" src="jquery-1.7.min.js"></script>
  <style type="text/css">
		body {
			color: #333;
			font-family: tahoma;
			font-size: 10px;
			background: #F7F7F7;

		h1 {
			text-align: center;;
			font-size: 12px;
			color: #990000;

		#container {
			outline: 1px solid #AAA;
			padding: 4px;
			background: #FFF;
			height: 350px;
			overflow-y: auto;

	    .entry {
	    	padding-top: 6px;

	    .link {
			cursor: hand; 
			cursor: pointer;
			color: #0099CC;
	    .link:hover {
			color: blue;

	    .time {
	    	padding-right: 4px;
	<script type="text/javascript" charset="utf-8">
			function addLink(link) {
				$("#start").after('<div class="entry"><span class="time">'+getDateTime()+'</span><span class="link" onclick=\'openLink("'+link+'")\'>'+getShortString(link,34)+'</span></div>');

			function getShortString( str, limit ) {
				if (str.length > limit) {
				 return str.substring(0,limit)+"...";
				} else {
					return str;

			function openLink(link) {

			function getDateTime() {
				var currentdate = new Date(); 
				var datetime =  ('0' +currentdate.getDate()).slice(-2) + "/"
				            + ('0' +(currentdate.getMonth()+1)).slice(-2)  + "/" 
				            + currentdate.getFullYear() + " "  
				            + ('0' +currentdate.getHours()).slice(-2) + ":"  
				            + ('0' +currentdate.getMinutes()).slice(-2);
				return datetime;

   <h1>Visited Links Logger</h1>
   	<div id="container">
   		<span id="start" style="display:none"/>

 The picture below summarizes the above text. safarituto-arch

Testing Extension Package

The development of Visited Links Logger Safari extension has come to the end and it’s now time to test it. Ready?  So, go to the Extension Builder then click on Reload button which will update our extension by new the modifications we made.

Safari extension builder reload button

Now everything should be fine, try to visit a few web pages and click on the links inside, you will get a result like in the picture below.

Final Safari Extension

Building Extension Package

At this point we are still in the development stage and we haven’t made a finished extension yet, the extension is stored as a folder that contains some source files and an icon, for the moment it can’t be delivered to any end-users. All we need to do next is to generate a compact package. Once again the Extension Builder makes this task very easy to do by a single click on the Build Package button (see the picture below) then choose where you want to save your package.

Extension builder build package As a result, you should have a new package named: “VistedLinksLogger.safariextz”, it can be installed by opening the file or dragging it to Safari. You can also publish this extension in the Apple extension gallery.

Here is the project (zipped) : VisitedLinksLogger.safariextension.zip

I hope this tutorial was very helpful. If you need more help, please don’t hesitate to contact me or leave a comment and I will try to reply ASAP. To end off, I would like to thank Paola Pereira for the English review.

A Step-By-Step tutorial on building your own Safari extension (part 1)

Hello and welcome to this tutorial which is primarily focused on new Safari extension developers. The main goal of this tutorial is to demonstrate you how to build your own Safari extension from A to Z. We have chosen a good example of extension which involves various features that you have to learn. Within this tutorial, you will discover how simple and easy it is to create your own Safari extension.
This tutorial is divided into two parts:

Part 1 : Configuring the extension

In this part, you prepare the environment, enable Extension builder, create a toolbar icon, create and configure the popover.

Part 2 : Building the extension

In this part, you create (start and end) injected script, work with the Global Page, work with jQuery event listener, build the extension user interface (Popover), pass messages between scripts, build extension package.

Introducing the extension

We are asked to build a Safari extension that could log all visited links while you are surfing with a Safari browser.  All we need to log is the date on which the link was clicked and the URL. This is an additional specification:

  • The interface containing the log should be a sidebar or a popup window that could be shown by clicking on a button located in the toolbar.
  • Information about logged click (date and URL) should be placed in a single row (line).
  • The URL text should be clickable and underlined, when clicked it opens the URL in a new tab or window.
  • The top row should correspond with the most recent clicked link.
  • The log should be viewed in a medium-sized frame (maximum size: 400×500 pixels).
  • When the log passes the bottom border of the frame a vertical scroll bar must be shown.
  • When a row passes the right border of the frame, an excerpt of the URL must be shown followed by (…).

The mock-up below explains pretty much everything.


PART 1: Configuring the extension

Getting started

This is everything we will need to build our extension:

  1. Operating System: Mac OS and Windows.Linux? I’m a Linux user and unfortunately there is no official version of Safari for Linux, so I have used Ubuntu Linux in the development stage and Windows on Oracle Virtual Box to test my extension on Safari.
  2. A recent version of Safari browser version 5 or higher (version 5.x.x recommended)
  3. Your favorite IDE or simply a text editor (In this tutorial I have used Sublime Text 2, you can download it from this link)
  4. A Safari Developer certificate is needed in order to use Extension Builder. Register by clicking this link then go to the Safari Extension Certificate Utility and follow the instructions.

After installing Safari, the first thing you need to do is to activate the develop menu in the menu Bar in order to use the Extension Builder.

Extension Builder is a powerful extension development utility included out-of-the-box with Safari.

To activate the develop menu go to the general setting menu and choose preferences then click Advances tab (as show below). By checking the box “show develop menu in menu bar” you will activate the develop menu.


Safari preferences - advanced

It’s time now to open Extension Builder, so go to the develop menu (see the picture below) then click on “show extension builder”.

Safari develop menu


As shown in the picture below, when you start the Extension builder the first time you will discover that there is no extension.

Safari Extension builder

In the next section we will start to configure our extension.

Configure our extension

Make sure that you have launched the Extension Builder then create a new extension by clicking the (add) icon button located in the bottom-left corner of the Extension Builder window.

When the file save dialog opens, type “VisitedLinksLogger” in the file name input box, then click on the Save button. (See the picture below).

Save as dialog

When you have clicked on the Save button, behind the scenes, a new folder has been created in the desired location. This folder is the extension project root directory (you can see the folder below) that currently contains only one file named “Info.plist” in which project settings and metadata are stored.


Safari Extension builder

Extension configuration

First step we need to do is to provide information about our extension. Fill in Extension Info section as shown in the next picture. Feel free to provide your own information.


In Extension Website Access section set Access Level to ALL, then check “Include secure pages” in order to include all web sites.


Global Page

During this current step we will define the Global Page file. A Global Page contains a code that’s loaded once, when Safari launches or when your extension is enabled. This page is never displayed.

To define your Global Page go to the extension root folder  “VisitedLinksLogger.safariextension” then create a new empty file named “global.html” thereafter go back to the Extension Builder and in the Extension Global Page section select “global.html” from the drop-down list.


Toolbar button

As advised in the beginning of this tutorial our extension must include a button with icon in the toolbar.To create this toolbar button, start by downloading the icon from this link or you can choose your own. Note that it is highly recommended that the size of the icon should be 18×18 (pixels).

Once you have the icon you must place it in the root of the extension folder.In the next step we will configure the toolbar button using the Extension Builder. In order to create a new toolbar button click on “add new toolbar item” and fill in the information (label, Image and identifier) as seen below, then select your toolbar icon from the Image drop-down list.



Right now, nothing will happen if you try to click on the tool bar icon because we haven’t specified an event for it yet.

The Popover

At the beginning of this tutorial we were asked to build a popup window that would contain visited links log and this popup should be shown or hidden when we click on the toolbar icon we added previously.

The appropriate choice that meets the specifications is to use a Popover; it’s the extension user interface in which visited links log will be shown. So don’t stress about how to build Popover interface, it requires a basic HTML and JS skills.

Like a regular webpage you will need to create a single HTML file in the extension root folder then name it “popover.html”.

Now let’s move once again to the Extension Builder in order to configure our Popover. Go to the Popovers section, click on add new Popover button then enter the information as shown in the picture below.

Here is a description of each property:

  • Identifier : The name of the Popover
  • File: popover html file (interface).
  • Width and Height: popover dimension in pixel.


Up until now we have created a popover and a toolbar button but we have not yet configured this button to show the Popover when clicked. To configure our button we to go back to Toolbar Item 1 section and select myPopover from Popover drop-down list.


Injected Scripts

Our extension needs two kinds of scripts, a Start Script and End Script. Unlike the global page and Popover file we created in the last part, injected scripts are loaded each time a page is loaded. Here is the difference between a Start Script and an End Script:

  • Start script: executes when the document has been created but before the webpage has been parsed.
  • End Script: executes when the DOM is fully loaded

Start Script

To ease JavaScript development we will use JQuery library. In most cases,  JQuery functions are called when DOM is fully loaded. Our extension will need to call a JQuery function inside the end script. Since JQuery should be loaded first, it must be configured as a Start Script.

Download JQuery library 1.7 and higher or directly from this link, once the download is completed put the library (with .js extension) in the our extension root directory, then go back to Safari Extension Builder, in Start Scripts section click on “New Script” button then select added JQuery library from the drop-down list.


End Script

Create a new empty file “listener.js” and put it under the extension root directory then repeat the same steps in Start Script.Check that you have selected “listener.js” in the End Scripts drop-down list.


We have finished extension configuration,  last thing to do is to install Your Extension by clicking “Install” button on the right-top corner of Extension builder.


Once you have clicked on Install, go to Safari main window and near the address bar you should see the extension button, when you click on this a blank popover will be displayed.

Safari extension toolbar element button icon

Safari Extension popover

We have come to the end of the first part of the tutorial. During this part we have learned how to configure a Safari extension using Extension Builder which is a powerful extension development utility included out-of-the-box with Safari Browser.

The second part of the tutorial mainly focuses on the business logic side of our extension. We will dive into Injected Scripts, Global Page, passing messages between scripts and building the GUI. Please continue with second part here.