截图对象来自不受扩展控制的页面

Tweet Craft 在 X 页面中识别帖子节点,然后把目标 DOM 交给截图库。目标节点不仅包含正文、头像和媒体,也可能嵌入 scriptnoscripttemplate,以及扩展注入的操作按钮。页面还可能设置严格 Content Security Policy,禁止内联脚本或未知资源。

如果截图库为了还原节点而克隆所有后代,浏览器可能把克隆脚本视为新的执行或资源加载尝试。即使最终图片不需要脚本,控制台仍会产生 CSP 拒绝,极端情况下整个截图 Promise 失败。

排除列表和运行时过滤要同时存在

当前公共选项把 scriptnoscripttemplate.tsc-action-button 放进静态 exclude 列表。同时提供 filter 回调,再次检查节点 tagName 和扩展按钮 class。两层看起来重复,但它们服务于不同阶段:库原生 exclude 能尽早减少遍历,filter 则在库版本或节点匹配行为变化时提供最后一道边界。

危险标签保存在 Set 中,让每次判断是明确的成员查询。过滤器只返回布尔值,不修改页面节点,也不读取脚本内容。截图过程因此保持只读,不会为了“修好页面”去删除用户正在浏览的 DOM。

为什么扩展自己的按钮也必须消失

内容脚本会在帖子操作区加入复制、截图或归档按钮。这些按钮帮助用户触发功能,却不是原帖内容。如果截图包含它们,结果会混入产品 UI,既影响排版,也可能让读者误以为按钮来自 X 页面。

使用稳定的 tsc-action-button class 比按图标、文本或位置判断可靠。按钮语言会变化,图标可能更新,页面布局也会调整;由扩展自己维护的语义 class 才是最稳定的隔离契约。

过滤不能解决所有截图问题

外部图片仍可能受 CORS、临时 URL 或加载时机影响;Web 字体可能尚未完成;视频当前帧和渐变背景也需要单独处理。过滤脚本节点只解决“哪些内容不该进入克隆”,不能保证所有资源都可被画布读取。

因此调用方还需要等待必要资源、设置超时,并在富截图失败时给出可理解的错误。不能把空白图片当成功,也不应无限重试第三方资源。对于无法读取的媒体,保留文本和链接通常比生成一个看似完整但缺图的结果更诚实。

验证要覆盖敌对页面条件

普通开发页面往往没有严格 CSP,测试很容易全部通过。更有效的样本需要包含内联脚本、模板节点、跨域图片、加载失败字体和扩展注入按钮。断言不仅看“是否生成图片”,还要确认输出不含操作按钮、控制台没有新增 CSP 错误、原页面 DOM 没有被修改。

移动端和深色模式也应单独检查,因为目标节点的计算样式会随视口和主题变化。截图工具的可靠性不在于一次生成多漂亮,而在于面对不能控制的页面时,知道哪些内容可以复制、哪些必须舍弃,以及失败后如何回到用户可继续操作的状态。

共享安全选项避免实现漂移

Tweet Craft 同时有单帖、线程和增强截图路径。如果每条路径各写一套过滤条件,新增一个危险节点时很容易漏改。现在的 createSnapdomSafeOptions 是共享入口,让所有截图服务使用同一套 CSP 防护和 UI 排除规则。

这类小函数的价值不是减少几行代码,而是把安全边界变成可审阅的单一事实来源。未来截图库升级时,只需针对这一入口验证行为,不需要在多个服务里猜测差异。