WWDC Quick Look 💓 By SwiftGGTeam
Create Safari Web Inspector Extensions

Create Safari Web Inspector Extensions

观看原视频

Highlight

WebKit 团队的 Devin Rousso 详细讲解了如何为 Safari 的 Web Inspector 创建扩展。Web Inspector 扩展基于跨浏览器 Web Extensions 标准和 DevTools API 构建,允许开发者在 Web Inspector 中添加自定义标签页。


核心内容

Web Inspector 是 Safari 调试网页的主工具。它有 Elements 等内置标签页,也能覆盖常见的网络、DOM、样式和脚本调试任务。

问题在于,真实项目常常有更窄的需求。你可能要调试一个 JavaScript 框架,也可能只想把网页里的 Open Graph 元数据集中展示出来。这类工具太具体,不适合直接塞进通用调试器。

00:47)Safari 16 的 Web Inspector extensions 给了一个入口。开发者可以基于跨浏览器 Web Extensions 和 DevTools API,在 Web Inspector 里添加自己的标签页。

这场 session 用 Open Graph 扩展做例子。它读取网页里用于链接预览的标题、描述和图片,然后在 Web Inspector 的自定义标签页里显示出来。

详细内容

从 Safari Extension App 开始

02:00)Web Inspector extension 和 Safari Web Extension 走同一套分发方式:通过 App Store 里的 app 分发。新项目可以用 Xcode 的 Safari Extension App 模板创建。

如果已有其他浏览器的 web extension,可以使用 Xcode 附带的转换工具。session 里提到的命令是 safari-web-extension-converter,输入包含 manifest.json 的扩展目录后,它会生成一个可构建运行的 app 项目。

概念示例:

safari-web-extension-converter ./MyExtension

关键点:

  • 第 1 行调用 Xcode 附带的转换工具。
  • ./MyExtension 指向已有 web extension 的目录。
  • 这个目录需要包含 manifest.json,因为 manifest 是 extension 的根配置文件。

manifest 里声明 devtools background page

03:06)Web Inspector extension 的结构和普通 Safari Web Extension 很像,都有 manifest、图标、页面、脚本和样式。区别是它还需要一个专门的 devtools background page。

这个页面负责 Web Inspector 扩展的后台逻辑。它能访问 DevTools API,并创建显示在 Web Inspector 里的自定义标签页。

概念示例:

{
  "devtools_page": "devtools_background.html"
}

关键点:

  • 第 2 行在 manifest 中声明 devtools background page。
  • devtools_background.html 会在 Web Inspector 打开时创建。
  • 普通 background page、content scripts 和 popup 如果用不上,可以从项目和 manifest 中删除,减少扩展的运行面。

每个 Web Inspector 都有自己的实例

03:43)devtools background page 只服务一个 Web Inspector 窗口。如果用户同时检查多个页面,每个 Web Inspector 都会有自己的 devtools background page 实例。

这会影响状态管理。扩展不能默认把某个 tab page 的状态当作全局状态。每个被检查页面都可能对应一组独立的 devtools background page 和 devtools tab page。

创建 Web Inspector 标签页

06:54)devtools background page 的核心任务,是创建 Web Inspector 里的自定义标签页。session 中的示例使用三个参数:标签名称、图标路径、标签页 HTML。

概念示例:

browser.devtools.panels.create(
  browser.i18n.getMessage("extensionName"),
  "images/logo.svg",
  "devtools_tab.html"
);

关键点:

  • 第 1 行调用 DevTools panels create API,创建一个新的 Web Inspector 标签页。
  • 第 2 行通过 browser.i18n.getMessage() 读取本地化后的扩展名称,避免把 UI 文案写死。
  • 第 3 行传入标签图标路径。session 建议使用 SVG 矢量图,这样用户缩放界面时图标仍然清晰。
  • 第 4 行指定实际显示给用户的 HTML 页面。

07:02)Devin Rousso 建议几乎总是创建一个 tab page。原因很直接:如果扩展需要权限,Safari 可以把权限提示显示在 Web Inspector 内部,用户能在看到工具位置的同时完成授权。

在被检查页面里执行 JavaScript

11:45)Web Inspector extension 常见任务是从被检查页面提取数据。Safari 16 提供了 devtools.inspectedWindow.eval(),它会自动定位当前扩展所属的 Web Inspector 正在检查的页面。

官方代码片段:

// Evaluating scripts inside the inspected page

let result = await browser.devtools.inspectedWindow.eval("foo.bar()");

关键点:

  • 第 3 行调用 browser.devtools.inspectedWindow.eval()
  • 传入的字符串会在被检查页面中执行。
  • await 等待执行结果返回,扩展随后可以把 result 渲染到自己的 devtools tab page。
  • API 会自动关联当前 Web Inspector 对应的页面,适合用户同时检查多个页面的场景。

指定 frame 执行代码

12:27)默认情况下,eval() 会在被检查页面的 main frame 中执行表达式。如果页面有多个子 frame,扩展可以通过 frameURL 指定目标 frame。

官方代码片段:

// Evaluating scripts inside a frame in the inspected page

let result = await browser.devtools.inspectedWindow.eval("foo.bar()", {
    frameURL: "http://example.com/",
});

关键点:

  • 第 3 行仍然使用 browser.devtools.inspectedWindow.eval()
  • 第二个参数是选项对象。
  • 第 4 行的 frameURL 指定要在哪个 frame 中执行表达式。
  • 只有需要从子 frame 提取数据时才需要这个选项;Open Graph 示例只读取 main frame。

Open Graph 扩展的数据流

13:05)示例扩展把占位的 “Hello World” 替换成真正的 tab HTML、CSS 和 JavaScript。CSS 使用根级 color-scheme,让标签页匹配 Web Inspector 的外观。

14:09)核心逻辑是把一段 JavaScript 传给 inspectedWindow.eval()。这段脚本在被检查页面中查询 DOM,读取常见 Open Graph 元数据:title、description、image,以及当前 document 的 ready state。

概念示例:

let result = await browser.devtools.inspectedWindow.eval(`
  ({
    title: document.querySelector('meta[property="og:title"]')?.content,
    description: document.querySelector('meta[property="og:description"]')?.content,
    image: document.querySelector('meta[property="og:image"]')?.content,
    readyState: document.readyState,
  })
`);

关键点:

  • 第 1 行通过 inspectedWindow.eval() 把脚本送到被检查页面。
  • 第 3 行读取 og:title 元数据。
  • 第 4 行读取 og:description 元数据。
  • 第 5 行读取 og:image 元数据。
  • 第 6 行把 document.readyState 一起返回,扩展可据此判断页面是否已准备好。
  • 第 8 行拿到的 result 可用于更新 devtools tab page 中对应的 HTML 元素。

15:00)示例还监听 devtools.network.onNavigated。当被检查页面发生导航时,扩展重新读取 Open Graph 数据,确保 Web Inspector 中显示的是当前页面的信息。

概念示例:

browser.devtools.network.onNavigated.addListener(() => {
  refreshOpenGraphMetadata();
});

关键点:

  • 第 1 行注册页面导航监听器。
  • onNavigated 来自 DevTools network API。
  • 第 2 行在导航后重新读取被检查页面的数据。
  • 这种写法适合用户在 Web Inspector 保持打开时连续切换页面。

用户体验细节

15:57)session 最后给出三条实践建议。

第一,从 devtools background page 创建 devtools tab page。这样权限提示会出现在 Web Inspector 内部,用户不必跳到别处授权。

第二,优先使用 activeTab permission,而不是请求具体 host 权限。这样 Web Inspector extension 的访问范围更集中,只作用于当前被检查页面。

第三,用 CSS color-scheme 或 web extension devtools theme APIs 匹配 Web Inspector 主题。调试工具常常长时间打开,视觉一致性会直接影响可读性。

核心启发

1. 做一个框架状态检查标签页

  • 做什么:为内部前端框架做 Web Inspector 标签页,显示当前路由、页面状态和关键 store 数据。
  • 为什么值得做:session 展示了如何用 inspectedWindow.eval() 读取被检查页面的数据,并把结果显示在 devtools tab page 中。
  • 怎么开始:先创建 devtools background page,再用 browser.devtools.panels.create() 添加标签页,在 tab page 中通过 browser.devtools.inspectedWindow.eval() 调用页面内的调试入口函数。

2. 做一个 SEO 与社交分享检查器

  • 做什么:读取 Open Graph、Twitter Card、canonical URL 和页面标题,直接在 Web Inspector 中给出缺失项。
  • 为什么值得做:Open Graph 示例已经覆盖了读取 meta 标签、处理 ready state、渲染结果这条路径。
  • 怎么开始:从 document.querySelector() 读取目标 meta 标签;页面还没 ready 时短暂延迟后重试;导航时用 browser.devtools.network.onNavigated 刷新结果。

3. 做一个 iframe 内容定位工具

  • 做什么:列出页面中的子 frame,并允许开发者选择某个 frame 执行检查脚本。
  • 为什么值得做:session 明确展示了 frameURL 选项,适合从多 frame 页面中提取目标数据。
  • 怎么开始:先在 main frame 收集 frame URL,再把用户选择的 URL 作为 browser.devtools.inspectedWindow.eval()frameURL 参数。

4. 做一个项目规范审计工具

  • 做什么:检查页面是否符合团队规范,例如是否带有指定 data attribute、是否加载调试标记、是否输出必要埋点字段。
  • 为什么值得做:Web Inspector extension 适合个人或团队工作流,session 开头专门提到它能覆盖通用开发者工具不适合内置的场景。
  • 怎么开始:把规范检查写成在页面中执行的 JavaScript 表达式,再把结果返回到 devtools tab page,用列表展示通过项和失败项。

关联 Session

评论

GitHub Issues · utterances