Skip to content

✨ Popup 站点范围快捷操作常驻,关闭本站执行优先移出匹配 - #1696

Open
CodFrm wants to merge 1 commit into
mainfrom
feat/popup-site-scope-always-on
Open

✨ Popup 站点范围快捷操作常驻,关闭本站执行优先移出匹配#1696
CodFrm wants to merge 1 commit into
mainfrom
feat/popup-site-scope-always-on

Conversation

@CodFrm

@CodFrm CodFrm commented Aug 28, 2026

Copy link
Copy Markdown
Member

Checklist / 检查清单

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

N/A — 本 PR 无关联 issue,改动由维护者直接提出。

背景

站点范围快捷操作(「仅在 X 执行 / 排除在 X 上执行 / 允许在 X 执行」)此前藏在设置项
popup_site_scope_actions 后面,默认关闭;关闭态走的是另一条 excludeUrl 分支,只能加排除、不能撤销。

「排除在 X 上执行」无论如何都会往 @exclude 里写一条。用户覆盖是整体替换作者规则的
getCombinedMeta),所以写排除会把作者当时的 @exclude 冻结进用户覆盖,之后脚本更新新增的排除规则
不再生效;对于 @match 本来就只覆盖当前站点的脚本,还会留下「匹配说要跑、排除说不跑」的自相矛盾状态。

本次改动

1. 快捷操作常驻。 删除 popup_site_scope_actions 配置项(config getter/setter、备份分区、设置页开关、
preload / usePopupData / App 全链路、10 个语言包的两个 key)。随之只在关闭态使用的 excludeUrl
(SW handler + client + popup handler)已无调用点,一并删除。

2. 「排除在 X 上执行」改为条件回退ScriptService.excludeFromMatch):

  • 先移出网域部分与当前 host 完全相等@match 条目;
  • isUrlIncluded 对剩余规则重算,只有仍命中当前网址(通配 @match@include、正则等移不动的规则)
    才追加 *://host/* 到排除;
  • 两者都没发生(当前站点本就不在匹配范围内)则不写库。

因此普通单站脚本只会得到 selfMetadata.match 的删减,排除列表保持干净;通配脚本仍按老路子写排除。
按钮文案维持「排除在 X 上执行」不变。

3. 删除死键 exclude_on(「恢复在 X 上执行」,10 个语言包)。它在 #1646 把三态压成 exclude_off
单键时就失去了调用点,本次移除 excludeUrl 后更不可能再被用到;terminology-zh-TW.md 里对该 key 的
引用一并更新。

4. 修掉匹配被移空后从 Popup 消失的死路。 scriptURLPatternResults 此前在生效 match+include 为空时返回
null,applyScriptMatchInfo 会把 uuiduuid{ORIGINAL} 的规则一起清掉,脚本直接从 Popup 消失,
再也点不到「允许在 X 执行」(先「仅在此站执行」再「不在此站执行」在改动前就能触发)。现在生效规则为空但原始
规则还在时继续解析并注册原始规则,脚本以「未生效」留在列表里可恢复。

实现考虑

  • 为什么不是无条件「只删匹配」@match *://*/* 的匹配列表里根本没有 *://host/* 可删,只删匹配等于
    按钮点了没反应;@include、正则同理。只有排除能表达「除了这个站点」。
  • 为什么不删通配子域:移除 *://*.example.com/* 会连带关掉 a.example.com 等兄弟子域,超出「不在
    www.example.com 执行」的语义,因此这类条目一律走排除回退。
  • 空匹配不能注册getApiMatchesAndGlobs 对一条 inclusion 都不剩的规则集会退回 matches: ["*://*/*"]
    第 4 点放行空匹配脚本后如果照常注册,脚本会变成全站运行buildAndSaveCompiledResourceFromScript
    因此在注册前挡下没有 inclusion 规则的脚本(该分支由 runtime.test.ts 先复现再修)。
  • 消息签名变更excludeFromMatch(uuid, matchPattern)excludeFromMatch(uuid, host, url),SW 需要完整
    网址才能做「移出后是否还命中」的重算。这是扩展内部消息,无外部契约。

已知限制

带端口的站点(localhost:3000 这类)host 含冒号,构不成合法 match pattern,永远走排除回退分支 —— 与改动前
行为一致,没有变差,但享受不到「只删匹配」的好处。

建议审查重点

  • excludeFromMatch 的回退判定:getMatchPatternHost(pattern) !== host 的取舍(只删完全等于当前 host 的
    条目),以及重算时用的生效 include/exclude 取值方式。
  • scriptURLPatternResults 放行空匹配后,所有依赖它的调用点:applyScriptMatchInfo
    getOrBuildPatternCache(Popup 禁用脚本匹配器)、buildAndSaveCompiledResourceFromScript(新守卫)。
  • 删除 excludeUrl 是否还有遗漏调用点(e2e/popup-matching-regressions.spec.ts 原本驱动的就是它,已改用
    excludeFromMatch + allowUrl)。

关联

无关联 issue(维护者直接提出)。#1591 的回归守卫仍保留,只是改用新消息驱动。

Screenshots / 截图

改动的是 Popup 折叠区里的动作项,无新增视觉样式。默认设置下展开一个当前页脚本的动作列表
node e2e/drive.mjs snapshot,全新 profile):

text="编辑"                      button
text="脚本设置"                   button
text="仅在 example.com 执行"      button      ← 改动前默认隐藏
text="排除在 example.com 上执行"    button
text="删除"                      button

排除本站执行后重开 Popup,同一脚本变为:

text="允许在 example.com 执行"     button      ← 匹配已被移空,仍可从这里恢复

对应截图留在本地验证目录 e2e/scratch/site-scope/shots/(gitignore,未入库)。

验证

npx vitest run                → 341/343 files, 4290 passed
npx tsc --noEmit              → clean
npx eslint <staged>           → clean
npx prettier --check <staged> → clean
node scripts/check-i18n.mjs   → passed
npx playwright test e2e/popup-matching-regressions.spec.ts → 3 passed
pnpm run build                → 成功(4 条既有 bundle size / monaco 警告)

全量单测的 5 个失败与本改动无关:4 个是 check-i18n.test.mjs 在 git worktree 里找不到 node_modules
(直接 node scripts/check-i18n.mjs 通过),1 个是并发下超时的 flaky(单独跑通过,main 上同样表现)。
pre-commit hook 因同一 worktree 原因会退回到全局 TypeScript 5.0.2,已用仓库自带的 6.0.3 手工跑完 hook 的四项
检查后 SKIP_PRE_COMMIT=1 提交。

实机验证(Chrome,e2e/session.mjs,全新 profile):

场景 结果
未开任何设置项打开 Popup 三个站点范围动作直接可见
单站脚本(@match *://example.com/*)点「排除在 example.com 上执行」 selfMetadata = { match: [] }未写排除
同一脚本重开 Popup 仍在列表(未生效),点「允许在 example.com 执行」后 match 恢复,页面重载脚本重新执行
通配脚本(@match *://*/*)点「排除在 example.com 上执行」 selfMetadata = { exclude: ["*://example.com/*"] },match 未动;example.com 不再执行,iana.org 仍执行
匹配被移空的脚本 example.com 与 iana.org 两次真实加载都无执行日志 —— 没有退化成全站运行

存储读取走 Service Worker,与被点击的 Popup UI 不是同一条路径。

站点范围快捷操作不再需要在设置里开启:删除 popup_site_scope_actions
配置项与随之只在关闭态使用的 excludeUrl 全链路。

「排除在 $0 上执行」改为优先把网域与当前 host 完全相等的 @match 移出匹配
列表,只有移完仍命中当前网址(通配 @match@include、正则等移不动的
规则)才追加 @exclude。排除会冻结作者 @exclude 并让匹配与排除自相矛盾,
只在删不掉时才用。通配子域匹配不移除,否则会连带关掉兄弟子域。

匹配被移空的脚本此前会连原始规则一起从匹配器清掉,从 Popup 消失、无法
再用「允许在 X 执行」恢复;现在生效规则为空但原始规则还在时继续解析并
注册原始规则。相应地,注册前须挡下一条 inclusion 都不剩的脚本——
getApiMatchesAndGlobs 对这种规则集会退回 *://*/*,注册出去等于全站运行。

顺带删掉 exclude_on:#1646 把三态压成 exclude_off 单键后它就没有调用点,
本次移除 excludeUrl 后更不可能再被用到。
@CodFrm
CodFrm force-pushed the feat/popup-site-scope-always-on branch from 70e94da to 42f9516 Compare August 28, 2026 09:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant