Skip to content

12 · 跨浏览器兼容与安全边界

“WebExtension API 大体兼容”只解决表面问题。后台生命周期、权限授予、侧边栏、请求拦截、剪贴板和 Safari 原生能力仍需要显式分支。

一、兼容分层

text
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.sidePanelsendNativeMessage 是否存在,避免 Chromium fork 被误判。

三、后台模型差异

浏览器Manifest 声明生命周期含义
Chromeservice_worker可随时挂起,不能依赖永久内存
Safariservice_worker与原生 app/host 协作
Firefoxbackground.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。

流程:

text
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()

text
runtime.sendNativeMessage('application.id', {
  type: 'fetchRequest', url, method, headers, body
})

原生 Swift 侧使用 URLSession 请求,再把 status/text 返回扩展。

background fetch proxy 的降级顺序:

  1. 标准 fetch;
  2. 若非 ok 且返回 bot-detection HTML,尝试 native;
  3. 标准 fetch 抛错时,若支持 native 则尝试;
  4. 否则返回 permission needed。

七、YouTube 网络规则

Reader/transcript 对 YouTube 需要 embed 与 Innertube 请求。不同浏览器使用不同网络能力:

  • Chrome 的 declarativeNetRequest 动态规则;
  • Firefox 的 webRequest/blocking;
  • Safari 可借助 native fetch。

规则只在需要的 tab/场景启用,并在结束时关闭,避免全局改变其他站点请求。

八、剪贴板降级

现代 navigator.clipboard.writeText 受 secure context、用户手势和页面权限限制。项目保留:

text
popup request copy
  → background route
  → content script creates textarea
  → select + document.execCommand('copy')

旧 API 虽已过时,在扩展/网页交界仍是兼容性逃生舱。

九、受限页面

扩展不能向所有 URL 注入 content script,例如:

  • chrome:// / about:
  • 扩展商店页面;
  • 某些 PDF/内部页面;
  • 空白页;
  • 浏览器安全保留域。

active-tab-manager.ts 提供 isValidUrlisBlankPageisRestrictedUrlisNormalPageUrl。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 平台修正

样式目录包含:

text
mobile.scss
rtl.scss
safari.scss
side-panel.scss
reader/*

把平台差异集中在 class/media query,而不是每个组件 inline style。Reader 仍有部分动态 style,因为它们来自用户设置或运行时几何。

十四、兼容功能的测试策略

跨浏览器功能应分三层验证:

  1. 纯函数:URL 分类、manifest 数据、request adapter;
  2. mock WebExtension API:消息路由、storage、permissions;
  3. 真机/浏览器: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、网络、存储四类安全边界。

基于 Obsidian Web Clipper 1.7.1 源码快照的独立学习笔记