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
- What’s new in Safari Web Extensions — 继续了解 Manifest V3、Safari Web Extensions API 更新和跨设备同步。
- Meet Web Push for Safari — 了解 Safari 里另一组基于 Web 标准的能力:Push API、Notifications API 和 Service Worker。
- Meet Safari Web Extensions — 从 Safari Web Extension 的创建、转换和分发流程理解这场 session 的基础。
- Meet CKTool JS — 参考另一个以 JavaScript 做开发者自动化工具的 WWDC 2022 session。
评论
GitHub Issues · utterances