The goal

I couldn’t really find a centralized place for documentation on how to make a new tab extension in safari, so here it is. There’s a few different APIs and some safari specific things that come together to make this thing, it’s kind of a pain.

Extension Builder

Enable the Develop menu by clicking Safari then Preferences, then Advanced, then at the bottom, Show Develop menu in menu bar.

Preferences

Show Develop menu in menu bar

After that click Develop then Show extension builder.

This is almost the last GUI thing we need to do.

In the next window that appears, click New Extension.

Finally, we’re on to the coding. Your extension builder should look like this. Go ahead and click install even though nothing is going to happen yet. Any time you make a change, be sure to hit the reload button that will appear after you click install.

I set some settings up for this particular extension:

Storage > Database Quota: 1 MB

1 MB Website Access > Access Level: All, checked Include Secure Pages

All, checked Include Secure Pages Global Page > Global Page File : global.html once you create this file it’ll be available

: global.html once you create this file it’ll be available Injected Content > Start Scripts: new-tab.js same thing here as above

Time to create global.html, global.js, new-tab.js and new-tab.html

cd my-extension.safariextension

touch global.html

touch global.js

touch new-tab.js

touch new-tab.html

Safari Extension APIs

These actually aren’t half bad, unfortunately the one we really care about *is* half bad, so that stinks. Detecting a new tab is kind of a dirty hack, just like javascript itself, so I guess in the scheme of things, it’s par for the course. Here’s how to set it up so that you can see something when you open a new tab with your extension installed.

Pretty basic 💁 so far

There’s a caveat here, if you have your new tab set to “Blank page” this won’t work unfortunately. However if you have it set to “top sites” or “favorites” it will.

You can style new-tab.html any way you want, but I used tachyons and one custom font size style.

Pretty straightforward. The only problem with having a separate page for the new tab is that it can’t directly make http requests which is what I needed to get the quote of the day, picture of the day and the current weather. Luckily you can send messages back and forth between injected scripts like so:

when new-tab.html is loaded and ready to roll, it will send an event with new-tab.html in the name which will then get picked up by global.js and it will call setTabContent() which brings us to the final piece of this puzzle.

APIs

I’m not really an API connoisseur, so I just googled for a few seconds and came up with these two to put stuff on the page.

https://reddit.com/r/earthporn.json

Yahoo Weather API

For the quotes I just scraped some random ones from around the web, don’t expect accuracy.

I wanted to cut down on http requests for speed and for API limits. I chose to save responses to local storage and only update them either every hour for the weather or every day for the image and quote. Local storage is kind of a piece of crap compared to cookies, but it can store so much more data, the first thing is to wrap it so each item can expire arbitrarily.

This hopefully works like so

var tomorrow = new Date();

tomorrow.setHours(24, 1, 0, 0); cache.set("hello", "world", { expires: tomorrow }); var who = cache.get("hello"); // tomorrow it will return null

Next I needed to get data from the two different APIs, so time to wrap xhr.

I also coupled the cache to it, because why not? For the quotes I just hacked it together.

Hack, it’s what for dinner

Here’s the final, relevant code that gets the data on the page

That’s it! I didn’t cover signing and trying to put it in the extension gallery, I’ll leave that up to you! Here’s the full code if you’re interested. Give it a star if you like it and file an issue if you want me to fix the horrible hardcoded numbers and numerous bugs I’m sure are lurking in there somewhere.