스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
Meet Safari Web Extensions
When you create a Safari Web Extension, you can help people get common online tasks done more quickly and efficiently. We'll show you how to build a new Safari Web Extension and host it on the App Store, as well as how to use the safari-web-extension-converter tool to migrate existing extensions from other web browsers like Chrome, Firefox, or Edge with very little effort.
리소스
- Developing a Safari Web Extension
- Explore the Human Interface Guidelines
- MDN Web Docs - Web Extensions API
- Messaging a Web Extension’s Native App
관련 비디오
WWDC21
WWDC20
-
다운로드
♪ Hello, and welcome to WWDC. Ellie Epskamp-Hunt: Hi! My name is Ellie Epskamp-Hunt. I work on Safari. Today I'm going to introduce a new type of Safari extension, Safari web extensions. Before I go into more detail, let's cover Safari's existing extension ecosystem. First, there are content blockers. These are available for iOS and macOS and are designed for speed and user privacy. Safari 13.1 can even run your content blockers delivered with apps built with Mac Catalyst. Next up, share extensions. These are also available on both iOS and macOS. Once invoked by the user, they're able to run JavaScript on the web page and return data to your app extension. And third, Safari app extensions available on Mac OS. These are great if you are a native app developer already familiar with Swift or Objective-C who wants to extend your app's functionality into Safari. These extensions are all built using native technologies like Swift or Objective-C. But what if you aren't familiar with this style of development, but are instead a web developer familiar with JavaScript, HTML, and CSS. Maybe you even have an extension written for another major web browser and you don't want to rewrite it in native code. This year we are thrilled to announce that Safari is adding support for web extensions on macOS. These are built primarily using JavaScript, HTML, and CSS, similar to legacy Safari extensions, but this type of extension brings along significant improvements. You can use the same extension model and APIs that you're already familiar with in other browsers. And Web extensions also provide improved user privacy controls along with the ability to sell through the App Store. Today we're going to show you just how easy it is to bring your extensions from other browsers to Safari. We will walk you through using our conversion tool. We will review our privacy centric permissions model to show how users will grant capabilities to your extension. Finally we'll cover tips and tricks for debugging and show how to message your extension's native app. Before we create our first Safari web extension, let's talk about how these extensions are packaged. Just like other Safari extensions, Safari web extensions are packaged with native apps. When that native app is installed, the extension is also installed in Safari. It's up to you, the developer, if the containing app should play any role beyond this. The containing apps for Safari web extensions would be distributed through the App Store. And because these extensions are packaged with apps, you'll need to download Xcode 12 or higher to build and run that app. We've built a command line tool that ships with Xcode to help you convert existing extensions to use in Safari. This tool will package your extension into a native app. This tool only needs to be run once to create a default Xcode project using information from your extension's manifest file. That project will build and run the native app containing your extension. If there are keys that appear in your manifest that are not supported by the current version of Safari, the tool will let you know. The largest icon you declare in the manifest will be used as your app's icon. So you'll want to declare a larger icon size in your manifest. We recommend that you add a 512 by 512 icon and a 1024 by 1024 icon. For more information on designing great Mac app icons, check out the macOS Human Interface Guidelines. Finally, if you add more files while developing your extension, these new files must be added to your Xcode project -- and note that when you modify your source files through Xcode your changes will appear in the original file. Now let's see how this tool works in practice. I have my unconverted extension here. Sea Creator is an extension that replaces aquatic animal names and web pages with the corresponding emoji. This folder contains all of my extension's resources. In Terminal, I'll run the Safari web extension converter command and provide the path to the extension folder. Here it's asking if the information it gleaned from the manifest is correct. If I saw an issue with anything here, I could type "no" and the tool would allow me to make changes. But everything looks good, so I will press Enter to use the default option in the brackets, which is "yes." We've got a warning from the tool. Let's minimize Xcode for a second.
I declared that I wanted to use the notifications API in my manifest, but that isn't supported in this version of Safari. Since notifications aren't a critical part of this extension's functionality, I can continue converting. Now let's run the app and see the extension appear in Safari. Click the run button to build and run. Here's the app that was created by the converter tool. It has the extensions icon, some text indicating if the extension is turned on or off, and a button to open up Safari's Preferences pane. It's up to you if you want to customize this app further. OK, let's open up Safari's Preferences. Note that our extension isn't here. Because I am just trying this tool out, I don't have a developer certificate yet, so the parent app is ad-hoc signed. By default, Safari won't show extensions from ad-hoc signed apps. To show these extensions, we need to first turn on the Develop menu. Click in Advanced, and then Show Develop menu in menu bar. Then in the Develop menu, Allow Unsigned Extensions and authenticate. Coming back to the Extension pane, there's our extension installed in Safari. Below the extension description is an explanation of the privileges this extension has. This extension's permissions mean it can only access the current tab's web page after the user interacts with the toolbar icon, a context menu item, or a keyboard shortcut. All right, let's turn this extension on and see it working.
The extension has a toolbar button now but its icon is greyed out. This indicates that the extension is not active on this current page. Now let's find an article about fish. When I click on the toolbar button to use the popover, the extension icon lights up. The first thing you might notice here is this broken image. We will come back and fix that during the debugging portion of this session. Now let's replace all the fish words on this page with a cool emoji. Great. We converted an existing extension and used it in Safari. For more information on this converter tool, consult the documentation linked in the Resources section. Now what if you don't have an extension made for another browser. You can create one from scratch using the Safari extension app template in Xcode with Safari web extension as the type. But it's possible that you have a Mac app that you'd like to add a Safari web extension to. Let's cover how to do that now. Here's my existing Mac app. It's an app that lets me browse and save recipes. Maybe we want to create an extension that lets me save recipes right in Safari. Let's explore how to do that. We're going to add a new target. Select File > New and Target. Make sure you have the macOS tab selected and filter by Safari. Select the Safari Extension option. You'd use the Safari Extension App option if you wanted to create both the extension and the containing app completely from scratch. Let's give this extension a name and then make sure you have web extension as the selected type. So the new target was created, which we can see here. And there's a new folder here in the sidebar with some default extension files. For those who are unfamiliar with this type of development, let's go over what's here. This information I'm about to cover is applicable to all browsers that support web extensions. First let's look at the manifest file. The manifest file defines the overall structure of your extension. For example, you name your extension by giving a value for the name key. Here the value of the name key is a special string that allows the extension's name to be localized. It's further defined in the "locales" folder in the messages.json file.
There are three main parts of an extension. The background scripts, content scripts, and a popover. There's a special API that lets an extension communicate between these three parts as well as create keyboard shortcuts, access cookies, and more.
The background key defines which scripts make up the background page. These scripts have no visible UI and can contain the logic that drives your extension. Let's take a look at the background script. Here we have a snippet of code that receives a message from another part of the extension and sends out a response. Next up, content scripts.
Content scripts are injected into web pages and can modify their appearance and function. These scripts execute in an isolated world, meaning they won't conflict with the web page's JavaScript. The matches keyword here defines which domains you want your content scripts to be injected into. If we take a look at this content script, we can see it using the sendMessage API to send out a message which we saw being processed by the background page. And finally a popover. Here the browser action key defines a popover that appears when the user clicks the toolbar button. This is an easy way for users to interact with your extension. Another important part of the manifest is the permissions key. The permissions key defines privileges your extension has in Safari. You can put API names here, like cookies, which lets an extension read and set browser cookies. You can also have URL match strings to define which websites your extension has access to generally. There are lots of online resources to learn more about developing web extensions, and you can look at the information on developer.apple.com for content specific to Safari web extensions. And that's how you create Safari web extensions either from scratch or by converting an existing one. Now let's talk about extension permissions in greater detail. Apple takes user privacy very seriously. Our browsing experience, including which web sites we visit and what we do on those web sites can be highly personal. So when we install an extension, we want to make sure that only the data we are comfortable sharing with that extension gets shared. To show how users will control their privacy in Safari when it comes to extensions, let's make some modifications to the sea creator extension. Let's say I've really enjoyed using my extension on Wikipedia and I just want it to always work on that site without any action on my end. Previously my extension made use of the active tab permission, which is what allowed it access to the current tab I was on. The replace words button injected the content script dynamically. Instead, we want to declare our content script in the manifest. First we'll go into the manifest and remove the active tab keyword from the permissions array. Then we will add a content scripts key to inject content.js into wikipedia.org. Let's build and see what this experience looks like in Safari. I'll use the Product menu and select Build.
Let's go to that article about fish. The first thing that we notice is that our toolbar button is badged with the warning icon. This lets the user know that the extension wants access to one or more sites. If we click on the button, we get this dialogue in the popover explaining what web site the extension wants access to. We can go ahead and allow for one day. And then our content script is injected. Notice that if we visit another Wikipedia page, our choice is remembered and the content script is injected immediately without me having to interact with the toolbar button again. The toolbar button still remains highlighted, letting me know that the extension is able to run code on this page. Now let's say I want to add a share button for Shiny, the social network, to my extension. This is a good chance to use optional permissions. Optional permissions are used for permissions that aren't critical to your extension's functionality. To use optional permissions, we first name the URL or API name in the manifest and then request it in our JavaScript code. First let's add a URL match pattern for shiny.com in the manifest. In our popover code, I've already added a share button. Let's add the code that requests the optional permission I added in the shareOnShiny function. I'm calling browser.permissions.request with the particular origin I want access to. Then I could add more code here that would share information about the extension. Let's see what this looks like in Safari. Use the Product menu to build again. Back in Safari, when I click the Share button, I get this dialogue that asks me if I want to allow my extension access to shiny.com. I can allow for one day. And then I would be able to tell the world about the Sea Creator extension. All right, so maybe I'm just loving my extension so much that I just want it to work everywhere. To do this, I'll modify the manifest again and use the all_urls keyword in the matches section to indicate that I want this content script to be injected onto every web page I visit.
Let's build again.
Now if I come back into the Preferences pane I can see that my previous choices were remembered. If I go to another web page about fish, this time there's no warning badge. The warning badge won't appear every time a user visits a new site. We put the warning badge there the first time your extension wants to inject into a web page as a way to educate users about activating an extension from the toolbar item. When we click the popover, we get a similar dialogue about giving access to a web site. This time I'll allow on all web sites, but this prompt appears to make sure that the user is completely aware of the access they are giving. Back in the Preferences pane, the list of websites where your extension has access has disappeared and has been replaced by this message letting the user know the capability that this extension has. And that's how extension permissions work in Safari. Now let's talk about some best practices when it comes to these permissions. The best way for an extension to respect user intention when it comes to privacy is by using the activeTab permission. With this permission, your extension is only granted access to know things about a tab like its URL, and inject script into the web page when your user expresses intent to use your extension with that tab very clearly by using the toolbar item, keyboard shortcut, or context menu item. Optional permissions are another great way to respect user privacy. These permissions are also declared in the manifest and represent permissions that your extension would like to have but that aren't critical to the core functionality of your extension. For some extensions, you will want to not require a user gesture to take action. For example, you might have an extension that inserts a useful toolbar on certain domains. You might have a manifest that looks something like this where a script is injected onto all domains that match to apple.com. But this doesn't mean that your extension will automatically be given access to inject on those domains. Instead, the user will see your extension's toolbar icon badge the first time they visit a web site that matches what's declared in the manifest. Some extensions ask for access to everything by using the all_urls permissions key in the manifest. If your extension doesn't actually need access to all web pages, scope your web page access requests more appropriately. In summary, active tab is a great way to respect user privacy. If active tab isn't compatible with your extension, request the minimum access you need for your extension to work and use optional permissions to request more access for non-critical features of your extension. And that was an overview of privacy and permissions for Safari web extensions. Now let's talk about some tools we added to make debugging your web extensions easier in Safari. I can right-click in the popover to inspect. Well, there's an error. It looks like I've hard coded my extension resource URLs to use the moz extension scheme. That's not going to work in Safari. Let's go back to Xcode and fix this. One approach might be to change the scheme of this URL to be Safari web extension. This isn't going to work though because the host of your extension URL changes across every launch of Safari to prevent users of your extension from being fingerprinted. Instead, we need to use an extension API that forms extension URLs. It's called browser.runtime.getURL. Let's see if that fixes our bug.
Great. Now our beautiful wave image appears. Our issue is in the popover this time, but you might also want to inspect your background page. I'll use the Develop menu, Web Extension Background Pages, and click on the name of my extension. You might also run into bugs in your content scripts. Let's take a look at how to debug those. First, let's go to a web page where my content script is injected. Then let's right-click on the page. Here in the sources tab, you can see all the extension scripts that are injected into a page. Any injected style sheets would also appear here. Maybe I want to print out the contents of a variable in my content script. If I do that now, it won't work. That's because Web Inspector is currently targeting the page's JavaScript world. Again, content scripts execute in an isolated world to avoid conflicts with the web page JavaScript. We need to switch to the extension's isolated world. I can do that using this menu in the bottom right. Now if I print out the variable, I can see its contents. And those are some developer tools we added to help you debug your web extensions. If you want to learn more about web inspector, check out the session dedicated to this topic. Let's quickly discuss some common bugs you may encounter while converting your web extension to use in Safari. First, if you perform user agent checking, think again about this approach. Using feature detection is the more reliable way to determine what's available in the browser your extension is running in. Second, bugs can come from making assumptions about your extension's resource URLs. For example, if you check for a particular scheme like Chrome extension, or assume the host of your URL will be same across the launches of Safari, this might cause bugs. Third, your extension may have problems in Safari if it has content scripts that depend on being injected immediately. Remember, the user may navigate to a page and then allow your extensions to inject scripts. So in this case, if you have a script that listens for when the DOM content has loaded and then performs an action, that code won't be called and your extension will appear broken to the user. Finally, it's possible that there's slight implementation differences between browsers for an API your extension usees. Consult online documentation like on MDN, the Mozilla Developer Network, for more detailed information. And those were some tips and tricks for debugging your web extension in Safari. Let's discuss how to communicate with your native app using the native messaging API. Native messaging allows apps and extensions to communicate. Unlike in other browsers, your extension is only allowed to communicate with its container app. Let's discuss how to send messages between the different components of your extension: the app, the app extension, and the background page running in Safari. Communicating from the background page to your app extension's native code uses the same API found in other browsers. Use browser.runtime .sendNativeMessage or browser.runtime.connectNative, and make sure you request the native messaging permission in your manifest. You don't need to provide the application IDs to these APIs, as Safari securely guarantees that the message will be relayed to your app extension. If we want to go the other way and send a message from the app extension's native code to the background page, use the completion handler on the NSextension context object passed into SafariWebExtension Handler.beginRequest. To send a message from your app to the extension's background page, you can use SFSafariApplication .dispatchMessage. Before using this method, It's a good idea to check that your extension is actually turned on in Safari. You can do this by calling SFSafariExtensionManager. getstateOfSafariExtension. If you've written a Safari app extension before, these APIs are the same ones you've used. To receive the message in the background page, you must have opened a port using browser.runtime.connectnative. And to communicate between the different components of your app group use NSUserDefaults or an XPC connection. That's how you send messages between the different components of your app and extension. Now that we've got an idea of how to communicate between our native app and extension, let's see how it works in practice. Let's add a feature that reports how many times our word replacement script ran in the native app UI. To achieve this, we will send a message from our extension to our app extension, write the value to NSUserdefaults, and then read that value and display it in the app's UI. First we need to add the native messaging permission to our manifest. Next let's add a message from the content script to the backround page to indicate that some replacement of text has happened. Then in our background page, we can call sendNativeMessage to send a message from our app extension's native code.
Now let's handle that message in Safari web extension handler. The code that's here was provided by Xcode when we converted our extension. Using the NSextension context object, we can grab the contents of that message and then write to NSuser defaults. Make sure you specify a particular suite. The bundle identifier here matches the ID I used to group my app and app extension in the capability section of the targets. I added the app group capability here for each target and gave them the same identifier. Now that we've written to our defaults, we can read that value in the parent app. I've already created a button that will update the text in our app's UI, so we need to read and then set the text. Let's build and see it in action.
I navigate to the page and the script works. I click this button, and great! This update based on information from the extension. And that was an overview of how to communicate between your extension and app. We've gone over a lot today. First we showed how to convert an existing extension for use in Safari or how to create ones from scratch. Then we covered Safari's permission model developed with privacy in mind. We explored tools added in Safari to help you debug your extension and common pitfalls. And finally, we learned how to communicate with your extension's parent app. Now that you've seen Safari web extensions work in practice, try it out yourself. Start by downloading the sample code project that builds and runs the Sea Creator extension that you saw in this session and play around with it. We're very excited to see you bring your web extensions to Safari. So try using our converter tool to bring over an extension that you've made for another browser. We are just getting started with web extensions in Safari, and you might find that some APIs you need are missing. We are depending on your feedback to make web extensions in Safari even better, so tell us which APIs are the most important to help you provide the best experience for your users. Reach out through feedback assistant or on the Safari developer forums to let us know what you think or of any bugs you find.
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.