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.

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

  • Please let me know if you’re looking for a writer for your weblog.
    You have some really great articles and I believe I would be a good asset.
    If you ever want to take some of the load off, I’d absolutely love to write some articles for your blog in exchange for a link back to mine.
    Please blast me an e-mail if interested.


  • Hello, Neat post. There is a problem with your web site in web explorer, might check
    this? IE nonetheless is the marketplace chief and a big section of folks will leave out your excellent writing due to this problem.

  • I see a lot of interesting posts on your website.

    You have to spend a lot of time writing, i know how to save you
    a lot of time, there is a tool that creates unique,
    google friendly articles in couple of seconds, just type in google – k2 unlimited content

  • I read a lot of interesting articles here. Probably you spend a lot of time writing, i know how to save you a lot of
    work, there is an online tool that creates unique, google friendly posts in minutes, just search in google
    – laranitas free content source

  • Very good article!
    Is it possible to use builtin safari functions like print_2_pdf?
    I want an extension to store the site as a pdf

  • Hello Sir, i want to port my extension written in chrome to safari browser on macos how can i do it because i am not able to find any relatable tutorials for it even on apple developers site can you help in any way?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre user="" computer="" escaped="">