12 · 跨浏览器兼容与安全边界
“WebExtension API 大体兼容”只解决表面问题。后台生命周期、权限授予、侧边栏、请求拦截、剪贴板和 Safari 原生能力仍需要显式分支。
一、兼容分层
API 语法差异 → webextension-polyfill
能力是否存在 → detectBrowser + feature detection
声明差异 → manifest.chrome/firefox/safari.json
网络/权限差异 → background fetch proxy + optional permissions
Safari 系统能力 → native messaging host
UI 差异 → browser class + SCSS不要把所有差异都塞进 if (browser === ...)。能由 manifest/feature detection 解决的,就不进入业务核心。
二、浏览器检测
browser-detection.ts 综合 user agent 与能力判断,返回 chrome/firefox/safari 等类型,并向 <html> 添加 browser class。
class 用于:
- Safari 特殊样式;
- Firefox UI/permission 提示;
- Chrome side panel 行为;
- 移动端/平台修正。
功能逻辑应优先 feature detection,例如判断 browser.sidePanel 或 sendNativeMessage 是否存在,避免 Chromium fork 被误判。
三、后台模型差异
| 浏览器 | Manifest 声明 | 生命周期含义 |
|---|---|---|
| Chrome | service_worker | 可随时挂起,不能依赖永久内存 |
| Safari | service_worker | 与原生 app/host 协作 |
| Firefox | background.scripts | 兼容实现与 Chrome 不完全同构 |
因此 tab mode 内存只做体验缓存,权威持久数据必须进 storage;service worker 重启后可从 content state/事件重新同步。
四、Side Panel 只属于部分浏览器
Chrome manifest 请求 sidePanel,background 维护 open windows,context menu 才添加 “Open side panel”。Firefox/Safari 不声明该能力。
业务 UI 复用 popup bundle,平台层负责是否提供入口。这样不支持 side panel 的浏览器不需要到处判断。
五、Host permission 与 Firefox
独立 Reader 需要 fetch 任意文章。Chrome 安装时 host permissions 可直接可用;Firefox MV3 可能要求用户显式授予 optional host permission。
流程:
reader-view proxyFetch
→ background fetch
→ CORS/permission failure
→ { error: 'CORS_PERMISSION_NEEDED' }
→ extension page request permission
→ retry错误用协议值返回,而不是只抛模糊 TypeError,UI 才能给出正确修复动作。
六、Safari native fetch
Safari 某些请求会遇到 CORS、bot detection 或 WebExtension 限制。background 的 nativeFetch():
runtime.sendNativeMessage('application.id', {
type: 'fetchRequest', url, method, headers, body
})原生 Swift 侧使用 URLSession 请求,再把 status/text 返回扩展。
background fetch proxy 的降级顺序:
- 标准 fetch;
- 若非 ok 且返回 bot-detection HTML,尝试 native;
- 标准 fetch 抛错时,若支持 native 则尝试;
- 否则返回 permission needed。
七、YouTube 网络规则
Reader/transcript 对 YouTube 需要 embed 与 Innertube 请求。不同浏览器使用不同网络能力:
- Chrome 的
declarativeNetRequest动态规则; - Firefox 的
webRequest/blocking; - Safari 可借助 native fetch。
规则只在需要的 tab/场景启用,并在结束时关闭,避免全局改变其他站点请求。
八、剪贴板降级
现代 navigator.clipboard.writeText 受 secure context、用户手势和页面权限限制。项目保留:
popup request copy
→ background route
→ content script creates textarea
→ select + document.execCommand('copy')旧 API 虽已过时,在扩展/网页交界仍是兼容性逃生舱。
九、受限页面
扩展不能向所有 URL 注入 content script,例如:
chrome:///about:;- 扩展商店页面;
- 某些 PDF/内部页面;
- 空白页;
- 浏览器安全保留域。
active-tab-manager.ts 提供 isValidUrl、isBlankPage、isRestrictedUrl、isNormalPageUrl。popup 应尽早展示明确错误,而不是等 sendMessage 抛 “Receiving end does not exist”。
十、安全边界
CSP
扩展页只允许 self script/object,阻止远程脚本与 inline eval。
HTML
- fullHtml 删除 script/style 与 inline style;
- highlights library 插入远端 HTML 时用 DOMPurify;
- UI 多使用
textContent、createElement; - 少数 setHTML helper 只应接收受控/清洗内容。
网络
- Interpreter 只向用户配置 provider 发请求;
- fetch proxy 可请求任意 URL,因此调用方需限制协议;
- native host 扩大了权限边界,应验证 message type、URL 与方法。
存储
- API key 在 extension storage;
- history/highlights 在 local;
- debug output 不应打印 secret,生产 debug 分支裁剪。
十一、Shadow DOM 与 isolated world
content script 默认在 isolated world,能访问 DOM,但不能直接看到页面 JS realm 的闭包/monkey patched state。flatten shadow DOM 辅助脚本作为 web-accessible resource 注入 main world,再把结果反映到 DOM。
这种 main/isolated 协作必须有超时与幂等标记,页面脚本可能阻塞或重复加载。
十二、国际化
src/_locales 有 36 个 locale。i18n.ts 提供:
getMessage;- translate page;
- language selection;
- document
lang/dir; - RTL 支持。
Chrome/Firefox manifest 可用 __MSG_*__,Safari manifest 某些文案直接写英文,说明打包/平台对 locale substitution 的支持不同。
i18n-automation.ts 与 scripts 用于同步/检查 strings,避免 locale key 漂移。
十三、CSS 平台修正
样式目录包含:
mobile.scss
rtl.scss
safari.scss
side-panel.scss
reader/*把平台差异集中在 class/media query,而不是每个组件 inline style。Reader 仍有部分动态 style,因为它们来自用户设置或运行时几何。
十四、兼容功能的测试策略
跨浏览器功能应分三层验证:
- 纯函数:URL 分类、manifest 数据、request adapter;
- mock WebExtension API:消息路由、storage、permissions;
- 真机/浏览器:service worker suspend、side panel、native host、clipboard、注入受限页。
jsdom 无法证明浏览器权限行为正确,发布前必须至少加载三套构建产物。
十五、本章检查点
- 能说出三类 manifest 的后台/权限差异;
- 能追踪 Firefox permission 与 Safari native fetch 降级;
- 能解释 main world/isolated world;
- 能识别受限 URL、clipboard 和 side panel 的 feature boundary;
- 能列出 CSP、HTML、网络、存储四类安全边界。