Timothy Hatcher: Hello, everyone.
Welcome to "Creating Safari Extensions, Part One."
Well, hopefully, after today's session, you'll be able to get started making Extensions for Safari, and I'm proud to announce that Safari 5 has everything you need, as a developer, to start creating Extensions.
And later this summer, there's going to be a user release that will enable Extensions, and we want to get your Extensions right away.
Once we get a great number of Extensions in the Extension Gallery, we're going to show these off to all your users and let them discover these Extensions.
So, you'll have an immediate audience of users to find and start using your Extensions.
So, start creating and definitely become a member of the Safari Developer Program, and submit your Extension as soon as possible.
So, what are Safari Extensions?
Well, you might have heard, in the State of the Union, they're all based on standards, open standards, and these standards are the core of what make up WebKit, and WebKit powers all of these Extensions, just like they do your browsing experience.
And there's no native code in Extensions, and we do this because of a couple of reasons.
There's the security aspect.
Users expect Extensions to be lightweight and not able to muck around with their system.
So, with all these security features, there's also cross platform aspects.
So, native code really doesn't port to Windows well.
So, if you write a Mac Extension, all the Windows users that you could have wouldn't have that, if you use native code.
And Extensions are secure.
They're signed with a code-signing certificate that's free on the Safari Developer Program.
Those things normally aren't free, so we're giving you a free code-signing certificate for your Extension, so you can provide secure updates for your users, with no dialogs they have to click through.
They can just automatically install your Extensions and trust it's coming from you.
And because it's built on WebKit and open standards, there's just no crashing.
WebKit never crashes, and [laughter] you're going to have great code, and you're not going to have any problems with crashing Safari because of your Extension.
So, let's talk about a little of what you'll learn today.
So, we'll talk about when you want to make a Safari Extension, what Safari Extensions can do, and I'll give you a demo on how to build one.
So, let's go ahead and get started.
So, when do you want to make a Safari Extension?
Well, the first one is very simple.
When you want to break out of this box you've built a webpage, and you just can't do everything you want, because you're stuck in this yellow box.
But with Extensions, you can extend out.
You can break out of that box, go into the Toolbar, have Context menus, have Toolbar items for your users to interact with.
Another aspect of Extensions, when you want to make one, is if you want to provide a persistent experience.
So, today, if you want to check your email, you have to open a web browser window, go to your email website, log in, get set up, and it'll notify you that you have new messages, but you either have to keep that window open, or you have to keep closing it, coming back in, wondering if you have new messages yet.
And, sure enough, you do.
It's from your boss, and you have a fire drill that you have to deal with, and you don't want to have to wait.
You want to be notified right away.
But web email clients are so popular, you want that built in right into your browser.
So, with the Safari Extension, you don't have this reload situation.
You can have a persistent experience for your users.
The Extension can automatically keep your credentials, without any browser windows open, and it can also present notifications, like badging a Toolbar item.
So, when a new message does come in, the user is notified right away, and they can click on your Toolbar item, go into the web email client, and they know right away when that new message from their boss comes in.
And the final great scenario for Extensions, and when you would want to make one, is mashups.
Usually, mashups require taking information from one website and another.
So, like, a realty website, and you want to show a map on that website.
You have to pull down information from both servers.
You usually have to run your own server to combine that information, and get everything mixed in and mashed together, and then you have to present that user, your users, with another version of the site with those things mashed together.
But with Extensions, you can cut out that middleman.
You can cut out that middle server and reduce your infrastructure.
So, you take website A, a realty website.
Your Extension can talk to the server for a map site, pull down information, and process it, and inject it back into website A.
So, your no secondary server that you have to process, and preload this information for your users.
So, it's a great way to do mashups.
So, that's the final example of when you might want to create Extensions.
There's tons more, but those are the three I wanted to show you today.
So, let's talk about what Safari Extensions can do.
So, to do that, I'm just going to jump in and do a demo.
So, I have Safari 5 here.
I've already enabled Extensions, but I'll show you how to go about that, if you haven't done it already.
In Safari Preferences, under the Advanced Preferences, you enable the Develop menu.
This will give you all of Safari's developer tools, like the Web Inspector, User Agent Switching, things like that.
But we've also built in Extensions under the Develop menu, because they're a developer feature in Safari 5, so you need to enable Extensions from the Develop menu, and that gives you access to the Extension Builder, which I'll show in a little bit.
So, I have a couple of Extensions installed here, and I wanted to show you them.
So, I have a wwdc Extension and a Twitter Extension, and they're both enabled well, the Twitter Extension isn't, so we'll go ahead and enable it.
And you'll see I didn't have to restart.
This Extension was disabled, and I enabled it immediately, and user interface changed in Safari no relaunching the browser, immediate installation.
So, this Twitter Extension talks to Twitter, does a search for whatever query I typed in, in the Preferences, and it rotates every ten seconds, and those options were configurable, presented in Safari's Preferences, just like you would expect any other Preference in Safari.
And I have a setting to change the background color of my bar when the mood is happy or sad.
So, you can see how I'm overlaying the metal with a transparent green, or no color, when there's no emotion being shown.
And these are actually some quotes that we've pulled from Twitter from developers like you that are very excited about building Extensions.
The other Extension I wanted to show you was the wwdc bar that I built.
This shows you all the sessions that are currently going on in this timeslot, and the one that we're in now is Favorited, so it's expanded by default.
But I can expand, and I'm using CSS3 here to do these animations for me.
I'm not writing any code, except the couple of lines of CSS it takes.
And if we want more information about a session, we just click the More Info button, and you can Favorite it, and take over the whole bar with a custom experience.
So, that's a couple of examples of Safari Extensions and specifically Extension bars.
So, that's what the Extensions look like, but what's the architecture underneath that?
Well, we'll start at the high level.
Every web browser has a window or one or more windows.
Web browsers have tabs.
Safari's had tabs for a while.
And every tab has a webpage.
We also provide areas for bars, so we hold a spot for your Extension bars, and we also have spots for your Toolbar items, alongside Safari's native bars and Toolbar items.
When you install an Extension, your Extension brings along a bunch of assets that get injected into Safari's experience.
So, when the user installs or enables your Extension, these things move over and get injected into Safari's UI.
And same for the Content Scripts.
They get injected into page loads, depending on the page that the user loads.
And there's a split.
There's a Web Content Layer and an Application Layer, and these layers provide a sense of security, so content in the web area can't break out and talk to the Application Layer.
It requires some message passing, but the Global Page and the bars can talk to each other.
They can call functions in each other's code directly, and the Global Page can send messages to the web content area, to scripts that you've injected in pages.
And same for the bars.
The Content Scripts can message up, and the bar can message back with some content, some payload.
So, that's the basic overall architecture of Safari Extensions.
So, let's dive in and talk about the Application Layer.
This is the top layer.
This is the UI, the user experience layer, where you see most of the bars and Toolbar items.
Let's first talk about the API a little bit, before we go into specifics.
And it's window.safari, but since it's on the window object, you don't need to type out window.
at all. You can just refer to the object as safari in your code, so it's less typing.
And on that object, we expose three sub objects.
Since the Safari object is just a namespace, there's an application object for application APIs.
There's an Extension object, which is like your current Extension, so you access APIs about your Extension on that object.
And then there's a special property, called the Self property, and this will be whatever instance you're currently running in.
So, if you're in a bar, it's going to be the bar object.
If you're in the Global Page, it's going to be the Global Page object.
So, you know who you are, and you can break out from there.
These are the three APIs that you're going to use, because these are the only objects that are on Safari, and everything else is branched out from one of these objects.
So, let's talk about windows and tabs.
This is the high level user interface element in a web browser, and you have access to all of the windows and tabs, as an Extension developer.
Unlike a webpage, where you only have access to your window and any windows you open, you can access all of the windows and tabs.
You can create them.
You can move them around.
You can request them to be closed.
And they serve as a great access point for everything that you need to do, so event listening, and they're part of the event listening hierarchy, so you add event listeners on tabs, adding event listeners on windows.
Even if your Extension doesn't create, or destroy, or move around windows, you're still going to be interfacing with these window and tab objects, because they're so fundamental to the web browser.
And I talked about the Safari Application object.
Here's one of the APIs that you have on the Application object.
You can ask for all of the browser windows, and you get an array back, ordered in front to back.
So, 0 is the front-most window.
And this array can be empty, if there's no windows open.
You can open a new browser window, which will give you one tab in that browser window, and you can create more tabs on windows whenever you want.
So, that's windows and tabs.
Now let's talk about bars.
So, if you have a bar like the stock ticker that was demoed in the State of the Union, this is where you show a strip of information.
It can be Tools.
It can be moving text like the Twitter example that I showed.
They're loaded as HTML files once per window.
So, when you create when the user creates a new window, your bar is loaded once for that window, and they make a new window, you get another instance of that Toolbar.
They display above Safari's Tab bar and below the Bookmarks bar.
They're a fixed height of 30px.
So, you have a limited amount of space, but you can do a lot in that space, as you've seen in the examples in the State of the Union and the ones I just showed.
And you have access to the Global Page, so you can share information with the Global Page, functions and data, and I'll go into that in a little bit.
And if you're in the Global Page, you can access all the bars.
So, the Extension object has an array of all the bars.
These are the instances of those bars.
So, if you have multiple windows open, and your Extension only has one bar, you still might have multiple bars in this array, and that's each instance of the bar in multiple windows.
And if you're in the bar itself, you can call safari.self, and that will be that current instance of that bar.
So, Toolbar items, these are displayed right alongside Safari's normal Toolbar items: Back, Forward, the Location field, etc. And they're created once per browser window, just like bars, and they're optional.
Users can choose to remove them at any time, just like the native Toolbar items.
They're inserted in the Toolbar by default.
You can turn this off, but we recommend that you let the user see the Toolbar items show up right away.
Only if you have multiple Toolbar items, should you turn off this feature of inserting by default.
Then, they would have to go into the customized sheet and customize the Toolbar, but your users usually don't know how to customize the Toolbar.
You'll be surprised at the number of requests you would get, if you didn't have this turned on, that you're going to be answering questions.
"Where's the Toolbar item?"
So, it's inserted by default.
They're styled to look just like Safari's Toolbar items.
So, on Mac, they're going to have a nice grey gradient.
On Windows, they're going to look a little different, because the platforms look different.
So, you don't have to worry about that platform difference.
We take care of that for you.
They support numerical badges.
So, if you have an email client, you can have a badge of unread messages show up or whatever you want to show to your user as a notification on your Toolbar item.
They send something called a "validate" and "command" event, and I'll go into these in a little more detail in a couple of slides, but they basically send events, when they're clicked, and they send validate events when they need to be updated.
So, the UI needs to change.
And just like bars, there's an array on the Extension object, where you can access all of the instances of those Toolbar items.
So, even if your Extension only has one Toolbar item, you still will have multiple instances for each window, so you can update them independently, or you can iterate that array and update them all at once.
And when you get one of these events, like the validate event, it's a great time to update the badge, so you get a "validate event" right when those Toolbar items are created.
And in your validate event, you should disable the item, update the image, or change the badge to reflect the current state that you need to show to your user.
So, that's Toolbar items.
Context menu items, Safari provides a lot of Context menu items, but we wanted to give you a chance to provide your own.
So, in the Content area of Safari, you can insert Context menu items just like WebKit and Safari does.
They're appended to the web content's Context menu.
Content Scripts can provide context information.
So, if you right click on an image, you're going to get an event in the Global Web Application Layer, but in the Web Layer, you need to provide information on what was clicked.
So, we provide some APIs that let you pass information up, so your Application Layer code in the Global Page can answer these questions, whether to show the item, what the text should say, whatever you want to use that information for.
And they send the "contextmenu" event.
Up in the Global Page, or bars, you can listen to a context menu event, and that's when you can add items and build up your menu.
And they also send "validate" and "command" events, just like Toolbar items, and they actually share logic.
So, if you have a Toolbar item and a Context menu item, and they do the same thing, they can both send the same command, and you only have to write one piece of code to handle that command.
And when you get the Context menu event, this is the function you would use to append an item.
You can also have your Extension come with prepackaged items, so you don't have to if you're adding an item over and over again, you should just prepackage it, so you don't have to run any code.
So, now let's talk about those command and validate events.
So, if you're familiar with any Cocoa programming on Mac, you might be familiar with the validate protocol, and it's very similar to this.
But if you're not, it's very simple to understand.
Validate events are sent whenever there's an "interesting" user action.
So, the user types in a new URL and navigates.
A new tab is created or closed.
New windows show up.
The item is created and put in the Toolbar.
But you do not need to know what happens.
You just need to validate your interface.
Check whatever state you need in the browser, whether there's a tab or URL open.
Whatever your Toolbar item cares about, you should update your user interface in the validate event.
And validate events should disable and update items, like I said, and that's a good place to also update badge counts.
So, it's your opportunity to update the user interface right before it's shown to the user and whenever something happens in the browser.
Analogous to validate events, they're sent when the user clicks, so these items contain a command string, and the validate event gets sent with that command string, and the command event gets sent whenever the user interacts with that item, so pictured Context menu, or clicks on the Toolbar, and they send to the same event hierarchy, so your validate event listeners and your command event listeners usually are right next to each other.
And they're sent up the hierarchy, and they're if you're familiar with DOM events, the events are just like that.
You use "addEventListener" to listen for these events.
So, you can listen for the validate event on the application, on the windows, on the tabs, wherever you want to inject your event listeners.
And when you get an event, like I said, it's a great place to disable or update the user interface, and you can just set the Disabled property on the target of the event, and the target is the Context menu item or the Toolbar item.
So, that's validate and command events.
Now let's talk about the Global Page.
I've been talking about this in a few of these slides.
This is where you put your main code, and it's loaded once, when you install the Extension, and it's not visible to the user.
It's just like a webpage, but it's just hidden.
You have the whole DOM available to you.
You have scripts that you can run in this page, so it's like your invisible page that you can run code.
You can have DOM elements that you interact with, that you prep before you send them off somewhere.
And it's really where you put your main logic, so you can share a whole bunch of code with all your Toolbars.
Since the Global Page is only loaded once, it's a great place to put large amounts of data.
And if your Toolbar, like the Twitter Extension and the session Extension that I showed you the Global Page is the one that's pulling down that data from Twitter and the Apple developer site, and processing it, and telling all the bars to update.
So, the bars are just little the user interface parts, and they get told by the Global Page to update and show new information.
So, that's a great way to structure your Extension.
So, bars load as quick as possible, and making new windows in Safari is fast and efficient, and the Global Page, just like the bars, they can talk to each other, so they have access to each other.
And if you're in a bar, you can get to the Global Page with the Extension object.
But the Global Page doesn't really have anything interesting.
It's not visible.
You can't show and hide it.
There's just no APIs on it, except one, and that's the content window, and this is the DOM window of the Global Page.
So, any global variables or functions that you declare in your Global Page can be accessed from the content window, and it's just like typing window., except you're accessing it from the Global Page window.
So, that's the Global Page.
Another feature in the Application Layer are Settings and Secure Settings.
And if you're familiar with HTML's localStorage, this is going to look very similar to you, but there's a few differences.
They're exposed in Safari's Preferences.
As I showed in the Twitter example, I had a bunch of Preferences, so that your uses can type in new queries.
They can check and uncheck check boxes.
There's dropdown menus.
So, when the user picks one of those settings, it's just going into a key-value store that your Extension and only your Extension has access to, just like localStorage.
And anything that you put in this is automatically JSON serialized and deserialized for you, so you can store complex objects, as long as they're not cyclical, and you get those back on the other end as a usable object.
You don't have to worry about serializing and deserializing.
We take care of that for you.
Just like Settings, we have some Secure Settings.
So, if you require a level of security for usernames, and passwords, and credit cards, like the one password guys that did a great demo in the State of the Union.
You could use Secure Storage, and those automatically get put into the user's keychain on Mac OS X.
And on Windows, we have a secure store very similar.
And you can have a default value very similar to Mac OS X's user defaults and iPhone.
You can get a default value.
When the value hasn't been set yet, you can get a value returned back to you, so you don't have to worry about instantiating or initializing these defaults.
You just specify them in your Extension, and they get returned to you automatically.
There's also APIs, so you can set keys, just like you would HTML5's localStorage, and you can set rich objects that you get back on the other end.
And Secure Settings are very similar, but this example, I'm deleting a setting from Secure Storage.
So, those are a couple of examples how you use Settings and Secure Settings.
That's it for the Application Layer.
So, let's talk about the Web Content Layer.
This is where everything is in the webpage, in that box that we were talking about.
And any time you want to talk to the Web Layer, you have to message pass, and that's for secure reasons, so your code doesn't inadvertently get exposed to the webpage and vice versa.
The webpage can't call into your secure application settings and open the Application Layer.
The APIs are very similar in the Web layer.
There is a Safari namespace object, just like you had in the Application Layer, that's exposed to your injected scripts.
And there's very similar APIs on that namespace object.
There's an Extension object, and there's a Self property, but the Extension object is not the same object that you have in the Application Layer.
You don't have access to Secure Settings or Settings in this API.
You really only have access to a couple of properties on this Extension API in the Contents Script.
So, just keep that in mind.
You access them very similarly, but they're actually distinct objects and like a completely different branch of the API.
And in Content Scripts, the Self property will return the webpage.
It's a webpage object, and that's where you add event listeners and things.
So, Content Scripts.
They're evaluated once per page load, and you can specify what pages they get injected into.
If you're familiar with Greasemonkey, this is a very similar concept.
Content Scripts get injected into the page.
You specify what URL patterns you want to match.
And this is going to be talked about in the second part of this session, so I'm just going to skim over these here.
They load either before or during the load event.
So, you don't have to add event listeners to run code when the load event happens.
You're getting executed the main body of your script is being executed either during the DOM content loaded phase or the load phase.
And what does this mean?
So, no global variables can be accessed from the page.
They can't see anything that you define in your Content Scripts; and, likewise, you can't see anything the page defines.
So, if you have a version of jQuery that you want to use in your Content Scripts, you don't have to worry about the page also using jQuery, or worse yet, a different version of jQuery that you might not be able to use.
So, you have complete isolation from the page.
And scripting permissions are the same.
So, think of your Content Scripts as executing exactly as you would if you put that script on that web server and executed the code.
You can't do cross domain, XHRs, or anything like that.
And if you need the power of the Application Layer, all you do is send a message, and you can get messages back from the Application Layer.
So, if you do need cross domain, XML, HTTP requests, you can tell your Application Layer to do it for you and message you back with the result.
So, you really do have the power to do some amazing things in the Content Scripts.
You just can't do them directly, for security reasons.
So, to send a message to the Global Page, you tell your webpage, which is the Self object.
You get its tab, and this tab is a proxy object.
It doesn't have any of the APIs available that you have in the Application Layer.
It only has "dispatchMessage" and "addEventListener."
So, you tell that tab proxy, "Here's a message, and here's some content."
That gets delivered on the application side.
And if you want to listen for messages in your Content Script, you add an event listener on the Self object, and that listens for the message event, and you register your function, like you would any using the DOM addEventListener, but this is the Extensions addEventListener.
So, that's Content Scripts.
We also allow you to have Content Stylesheets.
They're very similar.
They're loaded once per page.
They match the same whitelist and blacklist of URL patterns.
They're treated as user stylesheets.
So, if you've ever used Safari's User Stylesheet Advance Preference to make a stylesheet that you affect all pages, they're treated in the same way, and all you really need to know is that your stylesheet doesn't take precedent over the author's stylesheet.
So, if you have two properties that conflict say, the page wants it to be black, and you want it to be red, the author is going to win, but you have override power.
You can use the !important keyword on the properties that you want to say, "I win over the author," and that's when you ultimately get the say over anything the author and the page does.
And you can load relative URLs from your Extension bundle.
So if you have images that you want injected in that page, you can just refer to them as relative URLs to your bundle, and they'll automatically just work in the Content Scripts and stylesheets.
So, that's Content Stylesheets and Scripts.
But what about Full Page Content?
If you want to show a full experience, very similar to Safari's Top Site feature, where you have this immersive experience that your users can interact with.
So, say you have a Toolbar item, and you want to, when that user clicks on that, you want to take them to a local immersive experience that's on their computer, built in to your Extension, and that's where you use Full Page Content.
And very similarly to Top Sites, there's no URL shown, because they're just cryptic URLs.
We want the URL to be invisible to the user, so they're able to type and navigate to content at their whim.
And you load an HTML file from your bundle, using a special URL that we provide for your Extension, and they have access to the Content Script APIs.
So, they don't have access to the Application Layer APIs, because there's still that separation, but they're available to you in the normal world, so you don't have to worry about you can put your scripts right in the HTML file and not have to worry about secondary injected scripts.
And to tell your Extension in Safari to load one of these, you use an API that's available on the Extension object, the base URI, and that's your URI for your Extension bundle, and you append a relative path to that to load an HTML file out of that bundle.
So, in this example, I'm telling the active browser window's Active Tab, which is the front-most window and the front-most selected tab, to load this Extension URL, and that will take them right to your full page experience.
So, that's the Content Layer stuff.
So, now let's show you exactly how to build one of these Extensions.
I'm going to go ahead and uninstall my Twitter Extension, because that's the one we're going to be building.
So, I talked about the Extension Builder and how it's available under the Develop menu.
So, let's go ahead and show it.
Here it is.
It's basically empty until you start creating an Extension.
But the first thing you need to do is install a developer certificate, and this is the free code-signing certificate that you can get on the Safari Developer Center.
So, we'll go ahead and install the certificate that I got, and go ahead and put it into the keychain.
So, here it is.
I'm John Appleseed today.
So, we'll go ahead.
The certificate's installed.
Now, let's go ahead and make a new Extension in the Extension Builder.
So, in this + button in the corner, you have a menu that shows up, letting you make a new, brand new, fresh Extension, or add one that you might have made earlier, that you just removed, because you weren't working on it recently.
So, we'll go ahead and make a new Extension, and we'll go ahead and put it on the Desktop, and we're going to call it Twitter, and we'll Save.
So, I only typed in one string, but my Extension is ready to install.
I can install this Extension, except that it's going to use my keychain item for my certificate, and now I have an Extension installed.
You'll see it in Safari's Extension Preferences.
It just doesn't do anything yet.
We haven't given it any logic, but it's a fully functional Extension fully functional, doing nothing but let's go ahead and build it up.
So, the Extension Builder is, basically, a form that you fill out, that asks information of you.
Who are you, the author, a description of the Extension, your website.
So, let's say this is a Twitter Search bar, and we can provide a website that's optional, and a bundle identifier, if you're familiar with iPhone and Mac OS X applications.
This is your unique identifier for the Extension, so it's usually a reverse domain name syntax.
We'll go ahead and leave it as the default.
And an update manifest, this is where you can provide a URL that we ping your server, because you're self-hosting these Extensions as developers, and we can request when new updates are pulled down from the server, so you can automatically notify your users of updates with this.
Diversion Information, we're going to go ahead and leave as the default.
And we'll go ahead and specify an access level.
This blocks XHRs by default.
So, I know we need to access Twitter's Search API.
So, we'll make a new domain pattern of *.Twitter.com.
That will let me access any cross domain things that you normally wouldn't be able to do from webpages, from your Global Page.
So, you can access Twitter.com or whatever you put in here.
And I don't need access to secure pages, so this prevents access and inadvertent access to secure pages.
So, if you really do need access to HTTPS sites, you can check this checkbox.
So, now, I'm down to the part where I need to specify files.
So, I'm going to drag this over to my Extension and drop them into my bundle.
And the Extension Builder has already put an Info.plist, and that's like the manifest file for your Extension in this bundle.
So, let's go ahead and close that, and close this.
So, now you'll see it picked up my icon.
I now have the Twitter icon showing up right here.
So, let's go ahead and associate our Global Page.
It's automatically detected that these are HTML files, and I need to pick an HTML file.
So, we only show you HTML files, so it's easy to isolate what your Global Page is.
And now I know my Twitter Extension has a Toolbar, a bar, so I'm going to make it called Twitter, and this is what's shown to the user in the View menu.
And I'll go ahead and pick Bar, and that's my HTML file for my bar, and I'll pick an identifier.
This is your internal identifier, so if you're localizing your Extension, you don't have to redo your code.
You can always refer to it as the Twitter Bar, even if it says something else in another language.
So, that's all I need to build my Extension.
I wrote the code.
There's not a whole lot of code, and I've automatically built it up with the Extension Builder from scratch here.
So, let's go ahead and install it, and we'll go to the View menu and show my Twitter Bar.
[applause] So, let's see that again.
I had it disabled by default, and Safari remembered that for me.
So, we'll uninstall and reinstall, and it shows right back up.
So, you can iterate, and these buttons let you control the installation.
And if you're iterating in your text editor, and you're coming back to the Builder, just click the reload button, and that pulls in the new resources, re-signs everything, and installs it.
And if you're wanting to distribute your Extension to your users, you would build it up as a package, and you would save it on your Desktop or wherever you want.
And here's the built Extension that you can upload to your web server, and link to, and your users can download that, and that's also what you would use to submit to our Safari Extension Gallery.
I want to add a Toolbar item to my Extension to go into a full page experience that I have.
That's very easy, as well, so let's go ahead and drag in some more files.
So, I'm going to drag these over to my Extension.
I'm also going to open up my Extension and modify the Global Page to add some code.
So, I have just a few lines of code that I need to add to make this Toolbar item work.
Oops. So we'll put this right at the bottom here.
And this is the command event listener stuff.
And I'll go over these in slides right after this, so you don't have to worry about looking at it now.
So, that's all I needed to add to get this Toolbar item to show up.
And let's go ahead and uninstall and reinstall.
Oh, I forgot to associate the Toolbar item, so let's go ahead and make a new Toolbar item.
We'll call this Twitter Flow, and this is the text that shows up, like, if it's in the Overflow menu, and the Palette Label is what you see in the customized sheet.
And if you don't provide it, it just uses the label.
Same for the Toolbar item.
It'll default to the label, if you leave it empty, and we will pick our Toolbar item pane.
And we'll give it an identifier that's similar to the bars, so we'll just call it Twitter, and we'll tell it the command string to send, when that command event fires.
When the user clicks on the item, what command is this going to perform?
So, we'll call this Show Twitter Flow, and that's what my code is expecting, and it's included by default.
We can turn that off, if we want, but it's recommended that you keep it on, so your users see the item right away.
So, we'll go ahead and uninstall and reinstall.
And you'll see the Toolbar item has shown up, and it's injected right next to the Back/Forward buttons.
[applause] And when I click on this, it's going to take me to my full HTML5 experience that I've made, using CSS3 animations and transforms, using 3D transforms, in this case.
And you'll notice the Bar and the Twitter Flow in the page are synchronized.
The Global Page is the one telling it to change, so I have a timer in my Global Page updating all of these user interface elements, so they stay in sync.
So, if I have another window open, my two Toolbars are in sync.
Wait for it.
There it goes.
So, that's what you should use your Global Page to do, synchronize all your user interface elements, make all your bars work together.
So, that's how to build the Twitter Extension.
So, let's dive in, in what I showed you.
So, I showed you the Extension Builder, and this is the one-stop shop, where you go and define and build your Extension.
It's everything you need to specify what your Extension does, hook up all your code, and build it, and package it, so you can upload it to your servers.
It requires a free code-signing certificate from the Safari Developer Center, and it lets you manage multiple Extensions.
I only had one that I showed you, but you can have a whole sidebar full of Extensions that you're working on.
And the certificate's available at the Safari Developer Center for free.
So, let's talk about some of the code.
So, I have these bars that I have, and they need to know what the current Tweet is, since the Global Page is the one that's downloading and requesting all the Tweets.
I need to show that Tweet when my Bar loads.
So, when a user creates a new window, my Bar executes.
It needs to show some information right away.
So, this gets called during onload, and it immediately talks to the Global Page, gets the Content window to the Global Page, so I can access its global variables.
And on that Global window, I have a Tweet Array, and this array has all the Tweets that I have available.
And if that array exists, and it has a length because your Bar could still be loaded before the Global Page, right at startup, so you need to be careful of using these objects during launch.
So, just be careful when you do that.
And I just tell my Bar to show this Tweet, and I also access the current Tweet Index.
So, it automatically shows whatever the current Tweet is.
So, if I'm opening a new window, and the Tweet Index is incremented to the 10th Tweet, it will automatically show that 10th Tweet to be in sync with all the other windows.
So, that's working from a bar with the Global Page.
Inside the Global Page, when that timer fires, that I was talking about, it calls Show Tweet, and it passes in the current Tweet that needs to be shown, and it gets all of the bars that need to be updated, iterates over all of them, gets their Content window, very similar to the API you get from the Global Page to get the Content window, and it calls the function Show Tweet in that Bar, so the Bar will show that Tweet.
And it's calling the same function that I had in my previous slide, that gets called right away when the Bar loads, but the Global Page can call it, too.
Same thing for the Tweet Flow Tabs.
But since these are not directly callable, like the bars are, because they're isolated in that content layer, I have to dispatch a message, telling it to show the Tweet, and it listens for this message, takes the Tweet that I passed in my message, and calls its internal Show Tweet function.
So, it's indirect, but you can pass the same information that you would, if you were talking to a Bar directly.
And that's the same way you would talk to a Content Script or any other scripts that you have in the Content area.
So, responding to commands, this is the code that I dragged in when I wanted to add that Toolbar item, and I listened for the command event on the application, and it calls my Perform Command Function when that event fires.
And you should listen, you should check the command identifier on the event.
If you add more Toolbar items in the future, you're going to have different commands for those, but you don't want your code running when one of the items was clicked, and it was meant for the other.
So, you should always check that command text.
And in this case, I'm using a Switch Statement.
So, even if you have one Toolbar item, you should always check the command.
And I'm getting the current browser window.
Or if the browser window isn't created, there's no browser windows, I'm going to make a new one.
But, in reality, this would never happen, because my Toolbar item had to be in a window before it was clicked.
But you could have the commands fire some other time, when you don't have a window open, but this is just for demonstration purposes on how you can either access the Active window if there isn't one already, you can create a new one.
And I tell that window, I tell its Active tab to load a new URL, and I get the base URI from my Extension, and load my Tweet Flow HTML file, and it's that simple to show your full page experience.
So, that's responding to commands.
So, that's basics of how to build an Extension, and the second session, after this one, is going to go into more detail on specifics of Content Scripts and message passing.
So, if you want to stay around for that, you should.
So, in summary, sign up for the Developer program.
You have no excuse not to sign up for it and start building Extensions.
Turn on the Develop menu and enable Extensions.
That's your way to get access to this Developer feature and start building them today.
And get started with the Extension Builder.
It's the simplest way to build an Extension for any browser.
And developers are just loving it, and I hope you do, too.
So, if you have any questions, Vicki Murley is the Safari Internet and Technologies evangelist.
You can access the Safari Dev Center.
It's been revamped with a lot of information, documentation on Extensions.
We have extensive documentation.
They worked their butt off to get you great documentation on how to build these.
And some great sample code up there.
We have about seven samples of Extensions that you can play with, and download, and get started building on.
So, we have some related sessions.
We have Part Two coming up right after this, and immediately after that, in the same room, you can just keep in your seats and enjoy the ride.
We have some great sessions about the developer tools coming up, too.
So, you can use those to debug and experiment with your Extensions.