Skip to content

🦊 启用 Firefox MV3 构建并完善 sandbox/offscreen 通信#1561

Merged
CodFrm merged 50 commits into
scriptscat:mainfrom
cyfung1031:pr/ff3-mv3-002
Jul 21, 2026
Merged

🦊 启用 Firefox MV3 构建并完善 sandbox/offscreen 通信#1561
CodFrm merged 50 commits into
scriptscat:mainfrom
cyfung1031:pr/ff3-mv3-002

Conversation

@cyfung1031

@cyfung1031 cyfung1031 commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

Checklist / 检查清单

  • Fixes mentioned issues / 修复已提及的问题(无关联 Issue)
  • Code reviewed by human / 代码通过人工检查
  • Changes tested / 已完成测试

Description / 描述

本 PR 启用 ScriptCat 的 Firefox MV3 构建,并修复 Firefox 下 sandbox、event page 与 Service Worker 之间的启动时序和跨上下文通信问题。

主要改动

  • 启用 Firefox MV3 打包

    • 开启 Firefox 构建产物生成。
    • 保留 Firefox manifest 中的 sandbox 配置,并补充 sandbox CSP。
    • 将 Firefox 最低版本设为 154.0a1,以匹配 manifest sandbox 支持。
    • Chrome 构建继续移除 Firefox 专用的 sandbox CSP,避免影响现有 Chromium 路径。
  • 重构 sandbox/offscreen 就绪流程

    • 由 sandbox 在自身初始化完成后主动发送 preparationSandbox,不再由父层猜测、轮询或伪造就绪状态。
    • offscreen/event page 收到信号后再通知 Service Worker 环境已就绪。
    • 增加 15 秒兜底超时,sandbox 未能上报时仍可继续启动,并输出明确日志,避免永久阻塞。
    • 复用已有的 getExtensionEnv 请求执行非阻塞通道自检,并将检测结果上报到父层日志。
  • 修复 Firefox 下的消息通道问题

    • Firefox MV3 中 event page 与 Service Worker 运行在同一脚本/进程内,新增 InProcessMessage 处理 offscreen → Service Worker 通信,避免 chrome.runtime.sendMessage 向自身发送时出现 Receiving end does not exist
    • WindowMessage 支持惰性解析目标窗口,避免 iframe 尚处于 about:blank 时过早缓存 contentWindow,导致导航到跨源 sandbox 页面后消息来源匹配失败。
    • MessageQueue.publish() 主动捕获无接收方时 Firefox 返回的 rejected Promise,避免产生未处理的 Promise rejection。
  • 补充测试与文档

    • 新增或更新 sandbox 就绪、进程内消息桥接、惰性窗口目标及无接收方广播等单元测试。
    • 更新架构文档,说明 Firefox MV3 下 sandbox/offscreen 的初始化和通信机制。

兼容性说明

  • Firefox 构建要求 154.0a1 或更高版本。
  • Chromium 继续使用真实 offscreen document,现有通信路径保持不变。
  • 本 PR 不包含界面改动。

验证

  • tsc --noEmit
  • eslint
  • prettier --check
  • 完整测试套件通过:292 个测试文件、3099 个测试用例

Screenshots / 截图

不适用:本 PR 仅涉及构建配置、启动协议和内部消息通信。

@cyfung1031 cyfung1031 changed the title ♻️ 修复 Firefox MV3 sandbox 就绪握手与 offscreen↔SW 通信问题 ♻️ ScriptCat MV3 正式支持 Firefox - 修复 Firefox MV3 sandbox 就绪握手与 offscreen↔SW 通信问题 Jul 11, 2026
@cyfung1031 cyfung1031 changed the title ♻️ ScriptCat MV3 正式支持 Firefox - 修复 Firefox MV3 sandbox 就绪握手与 offscreen↔SW 通信问题 🦊 启用 Firefox MV3 构建并完善 sandbox/offscreen 通信 Jul 11, 2026
@cyfung1031

cyfung1031 commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator Author

b6329d4 修正了 定时脚本 没有自动执行的问题


RuntimeService(service worker 侧)和 ScriptService(offscreen 侧)各自持有一个独立的 MessageQueue 实例。两者之间的跨上下文消息传递,完全依赖 MessageQueue.publish() 通过 chrome.runtime.sendMessage() 广播。

在 Chrome 中,这一机制可以正常工作,因为 offscreen document 运行在独立进程中。

但在 Firefox 中,EventPageOffscreenManager 与 service worker 代码运行在同一个脚本和 Frame 内。根据 WebExtensions 的 runtime.onMessage 规则,广播会发送到扩展中的其他 Frame,但不会触发发送者所在 Frame 的监听器。

因此,当 service worker 侧调用:

publish("enableScripts", ...)

消息无法到达 offscreen 侧队列的监听器,因为两个队列都位于同一个发送者 Frame 中。

cron 调度机制本身没有问题。使用 createCronJob* * * * * * 进行独立测试时,3.5 秒内按计划触发了 3 次,说明问题不在 cron 包的集成。

两种执行方式的差异如下:

手动运行通过 runScript() 执行,使用 this.msgSender 进行点对点 RPC,也就是 IOffscreenSend / InProcessMessage 桥接。这条路径可以正常工作。

自动调度依赖 RuntimeServicepreparationOffscreen 订阅者发布:

this.mq.publish("enableScripts", res)

该消息需要触发 ScriptService"enableScripts" 的订阅,然后才能在 sandbox 中执行:

enableScript()crontabScript()

但在 Firefox 中,这条广播无法到达,因此 crontabScript() 根本不会被调用。

实际问题不是“任务执行一次后停止”,而是任务从一开始就没有完成调度。

同一问题还会导致以下消息无法同步到 Firefox 的 sandbox:

  • deleteScripts
  • installScript
  • setSandboxLanguage

这些失败不会产生明显错误,因此容易被忽略。

修复方案

BackgroundEnvManagerBase 增加可注入的 messageQueue 构造参数,默认值仍为 new MessageQueue()

这样不会影响 Chrome 的 OffscreenManager。在 Chrome 中,offscreen 和 service worker 本来就运行在不同进程中,因此仍然需要各自独立的队列。

EventPageOffscreenManager 现在要求通过第二个构造参数传入队列。service_worker.ts 会将现有的 messageQueue 实例传给它,避免基类再创建一个互不连通的新实例。

共享同一个 MessageQueue 实例后,publish() 内部的本地 this.EE.emit() 就可以直接完成两侧通信,无需再通过 chrome.runtime.sendMessage() 中转。

新增测试覆盖以下场景:

  • 注入的 MessageQueue 实例会被正确保留,不会被新的实例覆盖。
  • 在共享队列上调用 publish() 后,offscreen 侧订阅能够正常收到消息。

@cyfung1031 cyfung1031 added P0 🚑 需要紧急处理的内容 Pre-FF-MV3 labels Jul 11, 2026
@cyfung1031

cyfung1031 commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator Author

Firefox retains WebRequest blocking in MV3

https://blog.mozilla.org/addons/2022/10/31/begin-your-mv3-migration-by-implementing-new-features-today/

因此 Firefox MV3版 用了 WebRequest blocking 来避免 Event Page 被 kill 掉 (定时脚本需要这个设计)

mozilla 不接受再处理吧
例如先关掉这功能,之后再换成 Alarms

@cyfung1031
cyfung1031 requested a review from CodFrm July 11, 2026 04:29
@cyfung1031

cyfung1031 commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator Author

在使用 startFirefoxEventPageKeepAliveLoop 情况下,// @crontab * * * * * 顺利按时执行

Screenshot 2026-07-12 at 0 32 36

@CodFrm

CodFrm commented Jul 18, 2026

Copy link
Copy Markdown
Member

更新:已修正 harness 的等待与结果卡解析,并补做无 CSP 页面上的完整 Firefox Nightly headed 验证。下面是当前最终证据口径。

环境

  • HEAD:cc4dd7fda2c6cee53cc844a31fbfaf8d53b6e3de
  • Firefox Nightly:154.0a1
  • geckodriver:0.37.0
  • Firefox MV3 包:dist/scriptcat-v1.5.0-beta-firefox.zip
  • 扩展包 SHA-256:86a06318cae4ee528dde8578fb6a5d5599a6766c6c70b92524bfccbfcac75d97
  • 平台:Linux x64,headed Xvfb,1440×900

原目标页上的确定问题

同步 Example 的 GM_addElement 用例会插入以下 inline script:

window.foo = "bar";

@match 页面 https://content-security-policy.com/ 的 CSP 会拒绝该 inline script,导致 unsafeWindow.foo === undefined。Firefox 控制台给出的允许哈希与上述脚本内容的 SHA-256 完全一致。因此原运行中该项失败可以由目标页 CSP 直接解释,不应要求 ScriptCat 绕过页面 CSP。

无 CSP 隔离验证

为了只隔离目标页面因素,本轮采用 match-only variant,不是 exact-example:

  • 仓库 Example 文件未修改。
  • 在内存中仅替换 userscript metadata 中的一个 @match
  • 其余测试逻辑、@require@resource、XHR、cookie、notification、clipboard 和全部断言均保持不变。
  • 安装完成后从扩展 storage 回读代码,并要求其字节与 variant SHA-256 完全一致。
  • 测试页为本地临时 HTTP fixture,不发送 Content-Security-Policy 响应头,并提供 Example 原代码依赖的 .container > .masthead DOM。

结果:

Example 原始源码 SHA-256 match-only variant SHA-256 结果
gm_api_sync_test.js dee214fd91ef25850eba84545ce36667137d911099c9283d03beb98d1984b41a 400036d91ac4c8ab46a5e05017c97d14d0e011d5cf655b6f9289afe7752442b0 29/29 PASS,0 FAIL,100%
gm_api_async_test.js 7b3597c56e699b12199668a87255c94c60142d090f51af4068e17bd5fe5723d2 afc652ab0e8bce082b1ce61e1193c09faddcf50973de079331807cc1c8c7f78f 29/29 PASS,0 FAIL,100%

两份结果均满足:

  • 最终结果卡可见;
  • 29 条 individual PASS 日志全部解析;
  • 无 failed check、无 missing check;
  • GM_addElement - 创建元素 / GM.addElement - 创建元素 均通过;
  • XHR、通知、剪贴板、菜单、cookie、resource 和 unsafeWindow 后续测试全部执行并通过;
  • 两段 MP4 均为 H.264/yuv420p、1440×900,并通过视频探测与解码门禁;
  • 测试前后 tracked tree 均为空,HEAD 未变化,git diff --check 通过。

因此可以确认:原同步版 GM_addElement 失败来自原目标页 CSP,不是 Firefox MV3 下 ScriptCat GM_addElement 的通用实现故障。

Example 本身的页面耦合

另用无 CSP 的 https://example.com/ 做过一次验证:inline script 已不再受 CSP 阻止,但同步用例随后在这里失败:

document.querySelector(".container").insertBefore(
  script,
  document.querySelector(".masthead"),
);

原因是 example.com 没有这两个原网站专属节点。这说明当前 Example 同时耦合了目标页 CSP 和特定 DOM 结构。

建议更新 Example 时:

  1. @match 换到项目可控、无 CSP 的 fixture 页面;
  2. 删除上述与 GM API 断言无关的 DOM 移动代码;如确实需要移动节点,至少改用 document.body.appendChild(script) 这类通用锚点;
  3. 不要通过产品逻辑绕过宿主页面 CSP。

对上一版评论的纠错说明

上一版指出 harness 曾在约 2~3 秒后早退、不能据此判断 XHR 或 notification 产品行为,这个纠错仍然成立。现在的新 harness 已使用持续轮询等待完整结果卡,并实际运行到 29/29,因此本次无 CSP 隔离运行补齐了此前缺失的完整验证证据。

cyfung1031 and others added 3 commits July 20, 2026 17:52
pnpm run check:i18n 此前因缺失 ko-KR/pt-BR 的 enable_background.enable_success/disable_success
及 keep_scripts_alive.* 而失败;architecture-build.md 仍描述 PACK_FIREFOX 默认关闭、
Firefox manifest 会删除 sandbox 等已被本 PR 改写的旧行为。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@cyfung1031

Copy link
Copy Markdown
Collaborator Author

Reviewed docs and translation completeness against main and pushed two fixes (287c6cf):

  1. docs/references/architecture-build.md was stale — it still described the pre-PR packaging behavior (PACK_FIREFOX off by default, Firefox manifest dropping sandbox, min Firefox 136). Updated to match the current scripts/pack.js/src/manifest.json: Firefox packaging is now on by default, sandbox is kept (with the new content_security_policy.sandbox), debugger/offscreen/userScripts are stripped from Firefox permissions, webRequestBlocking is added to optional_permissions, incognito is "spanning" on Firefox, and min version is 154.0a1.
  2. pnpm run check:i18n was failingko-KR and pt-BR were missing the new enable_background.enable_success/disable_success and the entire keep_scripts_alive.* block that the other 8 locales got. Added translations for both, following existing tone and docs/references/terminology-ko-KR.md / terminology-pt-BR.md. check:i18n now passes.

docs/architecture.md was already correctly updated for the keep-alive/sandbox-handshake work, so no change needed there.

@cyfung1031

Copy link
Copy Markdown
Collaborator Author

@CodFrm 现在这个 PR 还有什么是卡住的吗??

@cyfung1031

This comment was marked as low quality.

@CodFrm

CodFrm commented Jul 20, 2026

Copy link
Copy Markdown
Member

@CodFrm 现在这个 PR 还有什么是卡住的吗??

之前的CSP问题,popup菜单点击更多的UI/UX问题

其它的我还没手动测试,明天着重来处理这个

我将截图、视频放上面了: https://my.feishu.cn/docx/VqgYdrfNaoaHnsx6nGkcjJskn8e

@cyfung1031

This comment was marked as low quality.

@CodFrm

CodFrm commented Jul 20, 2026

Copy link
Copy Markdown
Member

“所以不应修改 ScriptCat 产品逻辑来绕过宿主页面的 CSP。”

在chrome下是可以的,要改产品逻辑呀

@cyfung1031

cyfung1031 commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator Author

“所以不应修改 ScriptCat 产品逻辑来绕过宿主页面的 CSP。”

在chrome下是可以的,要改产品逻辑呀

我觉得这个浏览器 spec 差异的问题不是一时三刻能解决。
应该先合并再以 issue 形式记录

Chrome 在 content 里可跳过CSP执行脚本
Firefox 在 content 里不跳过CSP执行脚本 - 目前 main, isolated, scripting 三个环境都没有这bypass

说不定还要去 Bugzilla 那边回报问题

Comment thread src/pages/popup/App.tsx Outdated
Comment thread scripts/build-config.js
"sandbox allow-downloads allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-scripts allow-storage-access-by-user-activation allow-top-navigation allow-top-navigation-by-user-activation allow-top-navigation-to-custom-protocols; script-src 'unsafe-inline' 'unsafe-eval' https: http: data: blob: 'self';";

/**
* Firefox MV3 的 sandbox manifest 支持要求显式声明 CSP;Chrome 继续使用原有 manifest。

@cyfung1031 cyfung1031 Jul 20, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这会造成两种 sandbox. 影响代码维护。建议统一成 firefox 那个。日后再调整

目标是尽量减少分支代码。降低维护负担

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

补充: 可以调整这些 sandbox feature. 如果你有自动化测试,应该可以找出最少 sandbox feature

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这会影响chrome商店审核,所以删掉,mv2的时候打包就会特意删掉

@CodFrm

CodFrm commented Jul 20, 2026

Copy link
Copy Markdown
Member

Firefox MV3 GM_addElement / strict CSP 调查更新

TL;DR:结论与推荐方案

结论:这是 Firefox MV3 与 Chrome 的真实产品兼容差异,需要产品逻辑处理。 Firefox 会让插入页面的 inline <script> 继续受宿主页面 CSP 管辖;configureWorld() 的宽松 CSP 不会随 DOM 节点转移。单纯使用 content realm、createElementNS 或原生 textContent setter 不能解决无 nonce 的 hash-only CSP。

对照结果:

  • Violentmonkey 2.41.0 / 2.45.0:失败。 元素成功插入,但 inline code 被 CSP 拦截。
  • Tampermonkey 5.5.0:成功。 依赖 webRequest 改写响应 CSP,并用 nonce 授权脚本;语义更接近原生,但权限和安全策略影响更大。

推荐 ScriptCat 采用窄范围兼容方案,而不是 TM 式全局 CSP rewrite:

  1. 只处理 Firefox MAIN-world 发起的 inline classic script;排除 src、module、nomodule、nonce。
  2. DOMParser 创建 parser-inert 的真实 HTMLScriptElement,adopt 到页面后同步插入并返回。
  3. parser-created 节点保持 “already started”,插入和后续移动都不会触发页面原生执行或二次执行。
  4. 在 Firefox 允许动态编译的 USER_SCRIPT world 中受控执行源码一次,显式传入页面 window / document 等 globals,并在执行期间让 document.currentScript 指向返回元素。
  5. Chrome、external script、module、nonce 等继续走原生路径。

本地未提交原型已通过两次独立的原始同步 Example 验证,均为 29/29;聚焦测试 40/40,并确认返回真实 connected element、同步可见、指定 parent、移动后 exactly-once。代价是不能百分百模拟顶层 var / function declaration、dynamic import、嵌套 eval、page-global error identity 和 inline element load/error 等完整 native classic-script 语义。

因此建议维护者在以下两者中选择:

  • 推荐:窄范围 parser-inert + USER_SCRIPT execution。 权限更小,不改整页 CSP,覆盖常见 GM_addElement("script", {textContent}) 用例。
  • 若必须追求更完整的原生脚本语义:TM 式 CSP rewrite + nonce。 但需明确接受 webRequestBlocking、响应 CSP 改写和更大的安全影响。

当前 PR 分支尚未包含该修复;以上 29/29 是本地原型证据,本轮没有 commit 或 push。

补充并纠正此前“只调整 Example、不改产品逻辑”的结论:Chrome 在同一 API 场景可以执行,而 Firefox Nightly MV3 会失败,这是需要产品兼容处理的浏览器差异。

本轮只发布调查、对照实测和本地原型验证结果;没有提交或推送任何代码。PR 当前 HEAD 仍是 cc4dd7fda2c6cee53cc844a31fbfaf8d53b6e3de

1. 根因与稳定复现

原同步 Example:

GM_addElement("script", { textContent: 'window.foo = "bar";' });

https://content-security-policy.com/ 上,真实 <script> 能连接到页面,但 Firefox 按宿主 Document 的 script-src / script-src-elem 检查该 inline script,代码未执行,unsafeWindow.foo === undefined

  • Firefox Nightly 154.0a1,geckodriver 0.37.0
  • 原始同步 Example 连续 5 轮:每轮 28/29,唯一失败稳定为 GM_addElement - 创建元素
  • 无 CSP 页面:29/29
  • Firefox 控制台的 CSP hash 与 window.foo = "bar"; 精确对应

调用链是 MAIN-world userscript → 同步 bridge → scriptcat-content USER_SCRIPT world 创建并追加元素。Firefox configureWorld() 的宽松 CSP 只影响 USER_SCRIPT world 内编译的代码,不会随 DOM <script> 节点转移;节点最终仍受页面 CSP 管辖。Chrome 当前允许现有跨 world 插入路径。

已验证以下方式不能单独解决 hash-only CSP:createElementcreateElementNS、捕获的原生 Node.prototype.textContent setter、TextNode。目标页没有 nonce;MAIN world 的间接 eval / Function 也被 Firefox 明确拒绝:

EvalError: call to eval() blocked by CSP

2. Violentmonkey / Tampermonkey 真浏览器对照

同一个 Firefox Nightly、同一个严格 CSP 页面、同一个 inline body:

Violentmonkey 2.41.0 / webext 2.45.0

  • 返回并插入真实、connected、page-owned HTMLScriptElement
  • source/attributes 正确保留
  • window.__gmAddElementCsp 仍为 undefined
  • 控制台出现对应 script-src-elem 拦截

VM 使用 content-script realm、createElementNS、捕获的原生 textContent setter,并在可用时复制 nonce;不临时修改 type,也不另行执行源码。实测证明该路线在无 nonce 的 hash-only CSP 页面上不能解决问题。

Tampermonkey Firefox 5.5.0

  • 返回真实 connected element
  • immediate read 与最终 page global 均为 "ok"
  • 没有对应 inline-script CSP 拦截

签名 XPI 的包内实现与官方资料相互印证:TM 为匹配站点生成 nonce,通过 webRequest 改写响应 CSP 以授权 nonce,插入 <script> 时携带 nonce,随后移除元素上的 nonce 属性。这不是单纯的 DOM 插入技巧;也不能让任意 external src、module import、iframe 或 media 绕过各自 CSP。

3. 本地未提交兼容原型

为了确认 ScriptCat 是否存在不改 Example 的产品修复方向,在 detached worktree 做了一个未提交、未推送的窄范围原型:

  • 只路由 Firefox MAIN-world 发起的 inline classic script
  • 排除 src、module、nomodule、nonce
  • DOMParser 创建 parser-inert 的真实 HTMLScriptElement,adopt 后同步插入并返回
  • parser-created script 在后续移动时仍不进入原生 script preparation
  • 在 Firefox 允许动态编译的 USER_SCRIPT world 受控执行一次
  • Chrome 和其他脚本类型保持原生路径

验证结果:

  • focused Vitest:3 files / 40 tests PASS
  • typecheck、变更文件 ESLint、Prettier、git diff --check:PASS
  • production build、Firefox pack、ZIP integrity:PASS
  • 严格 CSP 原始同步 Example:29/29,0 FAIL,100%
  • 额外独立原始源码轮次:29/29,0 FAIL,100%
  • GM_addStyle computed style:PASS
  • parser-inert / move probe:真实元素插入和移动均不原生执行,受控执行总计 exactly once
  • Example 未修改;SHA-256 仍为 dee214fd91ef25850eba84545ce36667137d911099c9283d03beb98d1984b41a

4. 兼容边界

这个原型不是完整的 native classic-script 语义模拟。已知边界包括:

  • 顶层 var / function declaration、裸未声明赋值不会完整获得页面全局声明语义
  • dynamic import、嵌套 eval 保留 USER_SCRIPT realm
  • 页面全局 error-listener 身份不能完全模拟
  • 不为 inline classic 人工生成 element load / error 事件

因此正式实现如果采用这条路线,应继续严格限制在 Firefox MAIN-world、无 src / module / nomodule / nonce 的 inline classic case,并明确保留 external/module/nonce 的原生行为。另一条 TM 式方案需要修改响应 CSP 和扩大权限/安全策略影响,是否接受应单独讨论。

5. 当前状态

  • PR 分支尚未修复该问题:本地原型未提交、未推送。
  • 现有证据已证明 VM 的纯 DOM/content-realm 路线在该页面失败,TM 依赖 CSP rewrite + nonce 才成功。
  • ScriptCat 的窄范围 parser-inert + USER_SCRIPT execution 原型已用原始 Example 完成两次独立 29/29 验证,但正式提交前仍需维护者确认上述语义边界。

完整重编版报告(按结论、验收、根因、VM/TM 对照、推荐方案、边界和证据重新组织):

https://my.feishu.cn/docx/FxJOdONClofP5ixrUfZcvwpsnnc

旧版完整证据库(119 张截图、11 段视频及历史过程):

https://my.feishu.cn/docx/VqgYdrfNaoaHnsx6nGkcjJskn8e

@cyfung1031

cyfung1031 commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator Author
  • GM API 从 MAIN world 发起,经同步桥接到 USER_SCRIPT/content world 创建节点。Firefox configureWorld 的宽松 CSP 只影响 USER_SCRIPT 内编译的代码,不会随 DOM script 节点转移;节点仍受页面 CSP 管辖。Chrome 当前允许该跨 world 插入路径。

目前我在推测 ScriptCat 在 Chrome 的 inline script execution 是由于 Chrome 本身的CSP模型有漏洞
我们刚好把 bug 当成 feature 来用

@CodFrm

CodFrm commented Jul 20, 2026

Copy link
Copy Markdown
Member

目前我在推测 ScriptCat 在 Chrome 的 inline script execution 是由于 Chrome 本身的CSP模型有漏洞

我们刚好把 bug 当成 feature 来用

不清楚,暂时没发现其它问题了,csp这个问题后续再处理吧

明天再手动看一下 就合并发布


应该本来就是feature,这个csp受扩展csp/chrome.user.configureWorld控制的,反而是ff的bug

@cyfung1031

This comment was marked as low quality.

@cyfung1031

cyfung1031 commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator Author

https://bugzilla.mozilla.org/show_bug.cgi?id=1875475#c1

看来是 FF 未做好 userScripts API 的问题

https://github.com/cyfung1031/mv3-userscript-world-csp-unsafe-inline 重现了问题

@CodFrm

CodFrm commented Jul 21, 2026

Copy link
Copy Markdown
Member

Firefox Nightly E2E 最终补充:PR HEAD 定向复跑 + #1629 高价值合同

更新此前结论:原评论中的 background/crontab 两项 INCONCLUSIVE 已在 PR #1561 当前 HEAD 上完成真 Firefox 定向复跑,并进一步参考 PR #1629 的高价值 E2E 契约做了更广覆盖。

基线与总结果

1. @storageName:此前两个 INCONCLUSIVE 已闭环为 PASS

在两个完全独立的全新 Firefox profile 中重复执行:

场景 Run 1 Run 2
normal page ↔ crontab sharing PASS PASS
background reader observation PASS PASS

取得的直接证据包括:

  • crontab 注册、启用并实际进入调度;每轮两次 instrumented 写入间隔均为 5001ms,距真实 5 秒边界约 4–5ms
  • GM_setValue 前后 marker 完整,底层 value:example 真实变化;
  • normal、background、crontab 均解析到相同 storage identity example
  • background sandbox 启动、listener 注册,normal/background 两端均观察到 remote:true
  • 换回仓库 byte-exact 原始脚本后,background reader 仍收到两次更新;
  • 两轮 fresh profile 结果一致,PR HEAD 和 tracked tree 前后不变。

所以旧的“9 PASS / 0 FAIL / 2 INCONCLUSIVE”应更新为:normal-page 主链路,以及 normal/background/crontab 共享组合,均已在 #1561 HEAD 真 Firefox 上验证通过。此前 INCONCLUSIVE 是 harness 缺少分层证据,不是产品失败。

2. 参考 #1629 的高价值 Firefox 合同覆盖

本轮把 #1629 的行为断言作为契约,适配到能真实加载 WebExtension 的 Firefox/Selenium harness;没有把 Chromium Playwright 结果冒充 Firefox 结果。

已通过:

  • 相同 @storageName 的 sync/async 跨 UUID 读写及双向 remote listener;不同 storageName 隔离;
  • owner 进入回收站、purge/restore、最后 owner purge 清除旧共享值;
  • install.html v1 安装/执行 → 同源 v2 更新 → 新页面仅执行 v2;
  • 真实备份回环:导出有效 ZIP → 删除原脚本和值 → 实际导入 → 脚本执行且 GM value 恢复;
  • 订阅生命周期 A/B → A/B/C → A/C → 删除后不再注入;
  • 本地 script-src 'none' CSP 下 page/content @early-start 均为 14/14,另有 probe 证明普通 inline script 确实被 CSP 阻止;
  • 本地 GM XHR:允许的 @connect 请求为 200、自定义 header 到达服务端、内部 DNR marker 未泄漏、非 @connect 请求在到达服务端前被拒绝;
  • 🦊 启用 Firefox MV3 构建并完善 sandbox/offscreen 通信 #1561 event-page/sandbox/background/crontab 启动、跨上下文通信及真实 Firefox 进程重启恢复;
  • keep-alive optional permission 与配置开关的可观察状态;
  • Firefox 重启后的 options/install/import/confirm 页面稳定状态和已安装脚本恢复。

最强的 #1561 生命周期证据:真实关闭 Firefox 并用同一 fresh profile 启动新进程后,background start 从 1 增至 2,cron tick 从 2 增至 3 且时间戳继续推进,normal/background/crontab 三类脚本仍在。

3. 唯一 SKIP:GM_addElement inline script 与 Firefox CSP

可移植 GM 断言全部通过:sync 28/28、async 29/29

剩余一项是在 script-src 'none' 页面通过 GM_addElement 插入 inline <script>,并期待 unsafeWindow.foo === "bar"。Firefox 按页面 CSP 阻止其执行,实际为 undefined;两个 focused fresh profile 稳定复现。

该项分类为 Firefox 平台语义 + #1629/示例的跨浏览器可移植性边界,不是 #1561 产品回归,也不建议通过绕过页面 CSP 让断言变绿。

4. gm_xhr_test.js redirect 的原调查结论仍成立

仓库原始 example/tests/gm_xhr_test.js 两轮均为 138 total / 134 PASS / 4 FAIL / 0 pending;四个显示红项实际是 [xhr] / [fetch] 标签下重复的两类 redirect 预期:

  • redirect:"error":Firefox native Fetch reject,onloadstart → onerror → onloadend,status 为 0,而非示例预期的合成 408;
  • redirect:"manual":Firefox 返回 opaqueredirect,response headers 按平台语义不可见。

这仍更支持 Example 的跨浏览器预期与第三方 fixture 问题,不建议为了变绿伪造 status 或 opaque response headers。若项目要把 Chromium/Tampermonkey 风格的合成值定义为跨浏览器产品契约,应先补 Firefox failing regression,再单独决定兼容策略。

5. 未扩大宣称的非阻塞边界

  • keep-alive 的 packaged optional permission、权限和配置切换已验证;浏览器授权弹窗 UX 与 invalid-domain keep-alive 实际网络效果缺少稳定自动化成功信号,不包含在 PASS 内;
  • manifest 的 incognito:"spanning" 与提交测试已检查,但这 15 项合同没有单独执行真实 Firefox 私密窗口 @run-in;若它是合并门禁,应增加专门场景;
  • ✅ 补全并重构高价值 E2E 覆盖 #1629 的 WindowMessage/sandbox 示例没有原样运行,其公网依赖和不同语义由 🦊 启用 Firefox MV3 构建并完善 sandbox/offscreen 通信 #1561 集成 background/page remote、scheduled sandbox 和真实重启恢复覆盖替代。

结论

基于 #1561 HEAD 的真实 Firefox Nightly 证据:

  • 此前 background/crontab @storageName 的两项 INCONCLUSIVE 已更新为 PASS;
  • 本轮 15 项高价值合同为 14 PASS / 1 SKIP / 0 FAIL / 0 INCONCLUSIVE
  • 没有新增已确认的 Firefox 产品问题,也没有观察到 Firefox MV3 合并阻塞。

所有 broad 非通过项都在至少两个额外完成的 fresh profile 中复跑;最终做了 51 项一致性审计,全部通过,HEAD 精确且 tracked tree 干净。

@CodFrm

CodFrm commented Jul 21, 2026

Copy link
Copy Markdown
Member

看起来没什么严重的问题了合并了

我直接发 Firefox beta

@CodFrm
CodFrm merged commit e7d448b into scriptscat:main Jul 21, 2026
16 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P0 🚑 需要紧急处理的内容 Pre-FF-MV3

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants