By utilizing cross-browser web extensions and the DevTools APIs, you can now add your own tabs in Web Inspector in Safari 16. Let's take a quick look at a Web Inspector extension in action, and then I'll show you how you can build your own. First I'll open Safari's extension preferences.
And enable a Web Inspector extension. Then I'll close Safari's extension preferences and inspect the page.
I'll do that from the Develop menu, Show Web Inspector. In Web Inspector, not only can I see the many built-in tabs like the Elements Tab, but also the tab for the Web Inspector extension I just enabled. Because we've just enabled this extension, however, we first have to give it permission to work with the currently inspected page. I have the same permission duration options as other extensions outside of Web Inspector, so for now let's give it access for one day. This Open Graph extension is what I'll be building in this session. It displays the common social media metadata that most websites put on their pages for use by link previews in Messages and other social media sites. Now that we've gotten a quick look at what a Web Inspector extension can do, let's talk about how they're built. Like other Safari Web Extensions, Web Inspector extensions are distributed via an app in the App Store.
To build your own Web Inspector extension, you will need to have Xcode, Apple's app creation tool for building Mac and iOS apps. Xcode also comes with project templates to help jumpstart making a new Safari extension app. If you already have an existing web extension you've created for another browser, you can also use Xcode's bundled conversion tool. Simply run safari-web-extension-converter from the Terminal, passing a path to your extension's directory that contains the manifest.json file. A full app project will then be created for your extension that is ready to build and run.
For more information on this tool, check out Meet Safari Web Extensions from WWDC 2020, as well as the online documentation. So today I'm going to go over the basic structure of a Web Inspector extension, cover how to best evaluate code in that extension, and discuss some good best practices for your users. Let's get started.
Web Inspector extensions are structured just like other Safari Web Extensions, with a toolbar icon, background page, content scripts, and more, but they also have a dedicated devtools background page. Let's take a look at how that works in practice. The structure of a typical Safari Web Extension begins with a manifest file that declares the basic details about the extension, like the name, icons, descriptions, and more. It can declare a background page to handle all the behind-the-scenes logic of your extension, and can also declare any content scripts used to inject functionality into web pages visited by the user. For Web Inspector extensions, a couple other pages enter the mix. First there is a required devtools background page for the behind-the-scenes logic of your Web Inspector extension. This page has access to the unique devtools APIs and only a limited set of content script APIs. From this devtools background page you can create devtools tab pages that will be shown in Web Inspector. But all of that is just for a single Web Inspector. If there are multiple Web Inspectors, each has its own instance of this same devtools background page, staying alive for the duration that the related Web Inspector is open. As such, you might also have multiple instances of each devtools tab page as well. Let's take a look at how this structure looks in practice, and start building my Open Graph Web Inspector extension.
I'll start by creating a new project in Xcode.
The project type I'm going to create is a Safari Extension App. I only really need macOS, but I'm going to leave it as is, in multiplatform, in case I want to add iOS functionality in the future. I'm going to name this "Open Graph", and keep the rest of the defaults. Note that you need to pick a team and bundle identifier based on the Apple developer account you're using. And finally, I'm going to save this on the Desktop.
Now I have a generic Safari Web Extension project, ready for me to modify. I am first taken to the manifest.json file, which is the root configuration file for every web extension. The manifest file references the other resources that make up your extension– localizations, images, pages, scripts, styles, and more. For my Web Inspector extension, I won't need some of these files, like the background page, content scripts, or popup, so I'm going to delete them from the manifest and from the entire project.
And scroll down to find the Empty file template.
I'm going to call this file "devtools_background.html" to match the name I used in the manifest.
The location is already set to be right next to my other resources and part of the correct targets, so I don't need to change anything here.
…which I'll call "devtools_background.js".
Remember, this page gets created when Web Inspector opens, and is responsible for creating the custom tabs that appear in Web Inspector. I almost always want to create a tab so that, if needed, the permissions I saw earlier will be displayed to my users right there in-line, instead of in some other place. This devtools panels create API takes three simple arguments. The first is the name of the tab. For that, I am using the localization method to look up the localized name of my extension. The next argument is the icon path to use. Note that this should be a vector image to smoothly scale to any size if the user chooses to zoom their user interface. But in order to use this icon, I need to make sure it's part of my project, along with all of the other icons I need for my extension. The icons in the "images" folder are still the defaults from the project template. So let's delete these from the project.
And replace them with some icons I created earlier...
…including the "logo.svg" I am trying to use when creating the devtools tab.
Now that this is here, I'll go back to the devtools background script.
The third argument is the HTML used by the tab in Web Inspector. And just like with the images a moment ago, I need to create this page before I can use it.
I'll name this "devtools_tab.html" to match the name I gave the API.
…and the CSS file "devtools_tab.css".
It's great that I've already set up this structure, but for now I'll just add the usual "Hello World" to make sure that things are working correctly. But don't worry, we'll dive deeper into this a little bit later, because first, we still have a few more icons to replace to make sure this extension has a consistent look. First we need to replace the default large icon...
…by deleting it...
…and dragging my large icon in its place.
Since this large icon is part of the app, however, I need to add it to the right target.
The remaining icons are all part of the Assets catalog, specifically the AppIcon set.
I've already prepared these icons, so I'll just paste them in. And with that, I think we've replaced all of the default icons, so I'm now going to run my extension. Note that this may take a few seconds the first time the project is built.
And there it is! Most of this UI is from the Xcode template, but I do correctly see my icon instead of the default one. Once we've launched the Safari extension app at least once, we can close it, as the app doesn't need to be running anymore for Safari to pick it up.
But before I can see it in Safari, however, I have to Allow Unsigned Extensions in the Develop menu as this is a locally built unsigned app. Now, in Safari's Extensions preferences, I can see Open Graph. I'll turn it on. I'm going to open a Safari tab and browse to apple.com so that I can try my extension. And already we can see that my icon is in the toolbar.
And my extension tab is now in the tab bar. Switching to it, we can see the same permission prompt we saw earlier. This permission prompt is automatically shown if permissions are needed by the extension. Just like earlier, I'll allow it for one day.
Then I'll add some basic CSS to give my devtools tab a nice style.
I want to make sure that my devtools tab fits in with the rest of Web Inspector, so I've declared a root `color-scheme` property that will make my devtools tab match the appearance of the rest of Web Inspector. I am using a system font family and inheriting the font size, and matching the colors of more important text. As far as functionality, I'll start by adding some text in case the page doesn't have any opengraph metadata. Note that I can use web extension localized strings in Web Inspector just like I can anywhere else in a web extension. But in order to do so, I need to add that same localized string identifier to the localized strings file.
…and image, as well as the current ready state of the inspected page's document, bundling it all together to send back to the devtools page via the return value. Once that's done, I can grab the HTML element that corresponds to each of these properties and configure them so that they show the data that was gathered.
And if the document isn't quite ready yet, I can try again after a short delay.
I also want to redo all of this every time the inspected page navigates, so I'll add a listener for devtools network onNavigated.
This all looks great, so I'll build again to test it.
Now, whenever I open Web Inspector or navigate with Web Inspector already open, I can see the opengraph title, description, and image of every page. And that's a simple example of how to use some of the many new and powerful Web Inspector extension APIs. My OpenGraph extension is coming along very nicely. My Web Inspector extension's devtools tab page is now able to evaluate in the inspected page. I'm able to take the result data and process it to display it in a user friendly format for quick access. So what are some great ways to make great user experiences when creating a Web Inspector extension? Always create devtools tab pages from the devtools background page. This way, the user can see where these tabs will appear in Web Inspector, and any appropriate permission prompting will be shown in-line. Instead of asking for specific host permissions, try to use the activeTab permission to keep Web Inspector extensions as targeted as possible. And make sure to use the CSS color-scheme property or the web extension devtools theme APIs to match the overall theme of Web Inspector. So today I showed you at how to create entirely new debugging tools with Web Inspector extensions and covered some great best practices to keep in mind while doing so. You're welcome to download the OpenGraph Web Inspector extension in this session's related resources if you want to take a closer look for yourself.
We're super interested in hearing your thoughts, so please file bugs and feature requests using the Feedback Assistant, or come chat with us on the Safari developer forums. And even consider joining the WebExtensions community group to help shape the future of web extensions.
And also, make sure to check out What's new in Safari Web Extensions, and the documentation online, to learn more about what's new this year. I really hope you've enjoyed learning about the amazing new ability to create custom tooling within Web Inspector. We can't wait to see what awesome Web Inspector extensions you'll create, and look forward to all the ways you'll push the bounds of what's possible. Thank you so much for listening, and I hope you have a fantastic rest of your WWDC. ♪ ♪