Meet Safari Web Extensions
Developer Tools 入门 16m

认识 Safari Web Extensions

Meet Safari Web Extensions

2020年6月25日

在 Apple 官方观看视频

一句话判断

Safari 14 终于支持了 Chrome/Firefox 的 WebExtensions API——用 xcrun safari-web-extension-converter 一键转换现有插件,再通过 Mac App Store 分发,这是苹果弥补浏览器生态短板的重要一步。

这场 Session 讲了什么

Safari 14 引入了对 WebExtensions API 的支持,这是 Chrome、Firefox、Edge 浏览器扩展所使用的标准 API。这意味着现有的浏览器扩展可以以极低的成本移植到 Safari。

Session 详细讲解了 WebExtensions 的工作原理:manifest.json 配置、background scripts、content scripts、popup 页面、以及各种 API(chrome.tabschrome.storagechrome.webRequest 等)在 Safari 中的支持情况。

重点介绍了苹果提供的 xcrun safari-web-extension-converter 命令行工具,它可以自动将 Chrome Web Store 的扩展转换为 Xcode 项目。转换后的扩展作为 Safari App Extension 打包在 Mac app 内,通过 Mac App Store 分发。

值得深挖的点

Safari Web Extensions 的分发模式

Safari 扩展不是独立安装的,而是打包在一个 Mac app 里。用户从 Mac App Store 下载这个 app 后,Safari 会自动发现并提示启用扩展。这个设计有安全优势(经过 App Review 审核),但也意味着扩展必须面向 macOS,不支持 iOS 上的 Safari。

API 兼容性的细节

Safari 支持 WebExtensions API 的大部分核心功能:tabs、bookmarks、storage、webRequest(含 blocking)、cookies、notifications 等。但不支持 chrome.gcm(推送)、chrome.identity(Google 账号登录)、以及部分 DevTools API。Session 建议在 manifest.json 中用 minimum_chrome_version 声明最低版本,Safari 会忽略它不支持的 API 调用而不会崩溃。

代码片段

# 将 Chrome 扩展转换为 Safari 扩展

# 方法一:转换本地扩展目录
xcrun safari-web-extension-converter /path/to/chrome-extension

# 方法二:直接从 Chrome Web Store URL 转换
xcrun safari-web-extension-converter --chrome-web-store-item-id "abcdefghijklmnopqrstuvwxyz"

# 转换后会生成一个 Xcode 项目
# 包含:Mac app 容器 + Safari App Extension target
# 打开 .xcodeproj 后直接编译运行即可测试
// manifest.json —— Safari Web Extension 的标准配置
{
    "manifest_version": 2,
    "name": "我的 Safari 扩展",
    "version": "1.0",
    "description": "一个示例 Safari 扩展",
    
    // 弹出窗口
    "browser_action": {
        "default_popup": "popup.html",
        "default_icon": {
            "16": "icons/icon-16.png",
            "48": "icons/icon-48.png"
        }
    },
    
    // 后台脚本
    "background": {
        "scripts": ["background.js"]
    },
    
    // 内容脚本(注入到网页中)
    "content_scripts": [{
        "matches": ["*://*.example.com/*"],
        "js": ["content.js"],
        "css": ["content.css"]
    }],
    
    // 权限声明
    "permissions": [
        "tabs",
        "storage",
        "webRequest",
        "*://*.example.com/*"
    ]
}
// background.js —— 标签页管理示例
// 这段代码在 Chrome 和 Safari 中都可以运行

// 监听标签页更新
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
    if (changeInfo.status === 'complete' && tab.url) {
        console.log(`标签页加载完成: ${tab.url}`);
    }
});

// 使用 storage API 保存设置
chrome.storage.local.set({ theme: 'dark', enabled: true }, () => {
    console.log('设置已保存');
});

// 读取设置
chrome.storage.local.get(['theme', 'enabled'], (result) => {
    console.log(`主题: ${result.theme}, 启用: ${result.enabled}`);
});

最佳实践

  1. 转换后立即测试 API 兼容性。虽然 converter 工具会自动处理大部分问题,但有些 API 的行为在 Safari 和 Chrome 之间有细微差异(特别是 webRequest 的 blocking 行为)。转换后跑一遍完整的测试用例。
  2. 利用 browser polyfill。Safari 使用 browser 命名空间而不是 chrome。虽然 Safari 也支持 chrome 别名,但建议使用 Mozilla 的 browser-polyfill.js 确保跨浏览器兼容。
  3. 在 Mac App Store 的描述中说明这是 Safari 扩展。很多用户不知道扩展需要通过容器 app 安装,在描述和截图中说明「安装后在 Safari 偏好设置中启用扩展」。

还有什么值得关注

  • Safari Web Extensions 在 macOS 11 Big Sur 及以上版本可用,不支持 Catalina 及更早版本。
  • Content Script 的 CSP(Content Security Policy)在 Safari 中比 Chrome 更严格,内联脚本和 eval() 被禁止。
  • 苹果建议扩展的 Mac app 容器也提供一些有用的功能(而不仅仅是个「空壳」),这样更容易通过 App Review。
WWDC 2020