Skip to content

🐛 修复 popup「当前页运行脚本」误报:过滤不可运行页面并恢复 iframe 脚本显示 (#1687) - #1689

Open
CodFrm wants to merge 8 commits into
mainfrom
fix/popup-actual-injection-1687
Open

🐛 修复 popup「当前页运行脚本」误报:过滤不可运行页面并恢复 iframe 脚本显示 (#1687)#1689
CodFrm wants to merge 8 commits into
mainfrom
fix/popup-actual-injection-1687

Conversation

@CodFrm

@CodFrm CodFrm commented Aug 24, 2026

Copy link
Copy Markdown
Member

Checklist / 检查清单

Description / 描述

popup「当前页运行脚本」原本只按顶层网址的 pattern 匹配产生,导致列出的并不是实际在跑的脚本。#1687 报的两个症状同源:

1. iframe 内运行的脚本整条消失(beta.2 回归)

#1666 为了让「排除本站」后该行立即消失,删掉了 #1511 加的「运行过但未匹配」合并。代价是 @match 只命中 iframe 的脚本、连同它在 iframe 里注册的 GM 菜单,都不再出现在 popup(右键菜单和角标却仍有,三个界面口径不一致)。

改为 「本 tab 跑过 ∧ 现在仍匹配某个子 frame」,而不是简单恢复无条件合并:排除本站后脚本对所有 frame 都不再匹配,该行照样立即消失,#1666 的契约不变。只在存在「跑过但顶层没匹配」的记录时才去查 chrome.webNavigation.getAllFrames,常见页面零额外开销。

仅匹配子 frame 的行标记 matchesTopFrame=false,UI 据此隐藏站点范围操作——那些规则按顶层 host 生成,对它不成立,点下去会写出 *://settings/* 这类垃圾规则。

2. 脚本猫触及不到的页面照样列出脚本

新增页面状态判定,非 ok 时不列脚本、改为说明原因(后台脚本区不受影响):

状态 判据 文案
restricted 协议名单(chrome:// / 扩展页 / about: / devtools: …),或已确认没注入的扩展商店页 浏览器不允许扩展在此页面运行脚本
blacklist 命中网址黑名单 当前页面在黑名单中,无法使用脚本
file-access-denied file:// 且未开启文件访问 要在本地文件上运行脚本,请在扩展详情页开启「允许访问文件网址」
not-injected 可注入但本 tab 无 content script 报到 脚本尚未在此页面运行,刷新页面后生效

判据是实测选出来的:chrome://settings/getAllFrames 照常返回 frames,当不了可达性判据;而「本 tab 有没有 content script 报到」是精确的运行时证据(scriptcat-scripting 注册在 <all_urls>+allFrames,src/scripting.ts 无条件调 pageLoad())。因此用两条正交信号:名单给准确原因,报到兜住名单漏掉的(企业策略等)。

顺序有意为之:协议层面的受限(chrome://、扩展页等,跨浏览器确定)与黑名单先判;其余一律以报到为准;两项与浏览器相关的判据——扩展商店域和 file:// 权限查询——放在注入证据之后,只用来给已确认没注入的页面一个更准确的原因,绝不反过来否定已注入的事实。

这一点对商店域尤其要紧:各浏览器只保护自家商店,microsoftedge.microsoft.com/addons 在 Chrome 里就是普通网页,chromewebstore.google.com 在 Firefox 里也一样。硬名单会误伤那些页面上正常运行的脚本;放在注入证据之后则两种浏览器都对。四家商店都已覆盖(Chrome / Chrome 旧域 / Edge / Firefox)。

报到标记(新 session key tabLoaded:<tabId>)存 origin 而非完整网址:SPA 换页不失效,跳到另一个 origin 自然失效。

黑名单页并入同一状态——它本来就不会注入(runtime.ts 命中黑名单直接 return null),此前却照常列脚本。GetPopupDataRes.isBlacklist 相应替换为 pageStatus

附带:修正一处测试假前提

e2e/popup-matching-regressions.spec.ts 原先用 e2e/fixtures.ts 的简单 fixture,而它不授予 userScripts 权限(注释即写明「不需要 userScripts 的测试使用」)。探针证实:它 goto("https://example.com/") 后页面 console 为空、tabScript:/tabLoaded: 皆无——脚本从未真正注入,这三条 e2e 一直只验证 pattern 命中那一层,正是本 issue 的假象在测试套件里的镜像。已改用带 userScripts 权限与 .test host 解析的 fixture + 本地 mock 页面,三条重新全绿,且现在建立在真实注入之上。

测试

  • 修复前后各观测一次(真实 Chrome):iframe 场景修复前 popup 清单 [],修复后拿到完整行 runNumByIframe:1matchesTopFrame:false,以及 iframe 内注册的菜单(frameId:8)。

  • 可达性:同一次运行里 chrome://settings/pageStatus:"restricted"scripts:[];普通页 → "ok"、脚本正常列出(用的就是 issue 正文的 @match http*://*/*)。

  • 扩展商店(真实外网,跑在 Chrome 上)chromewebstore.google.com 主 frame 脚本没跑 → restrictedscripts:[]microsoftedge.microsoft.com/addons 主 frame 脚本跑了 → okscripts:["allsite-probe#1"]。两侧都与「商店判定排在注入证据之后」一致。

    判断「脚本是否在某页跑过」不能用 page.on("console")——它会把子 frame 的日志算进来,@match http*://*/* 的探针在页面里任何第三方 iframe 跑一次就是假阳性(我一度因此误判 CWS 能注入)。改用只作用于主 frame 的 page.evaluate(() => window.__PROBE_RAN__) 后,与 getAllFrames 的 frame 列表、tabScript:/tabLoaded: 三方一致。

  • 全量 e2e 62 passed;全量单测 343 文件 / 4252 条pnpm run lint 全 0。

  • 新增单测:page_access.test.ts 32 条纯函数、popup 服务层 10 条状态判定(含 SPA 换页、跨 origin、商店页与 file:// 的跨浏览器误报防护)、popup 子 frame 合并 6 条、popup UI 4 态文案 + iframe 行隐藏站点操作。

Screenshots / 截图

popup 在 chrome://settings/ 与普通页(含跨 host iframe)下的真实渲染,明暗双主题各一组,
e2e/scratch/iframe-popup/capture-shots.spec.ts 生成(scratch 目录 gitignored):

  • 受限页(chrome://settings/:橙色说明条「浏览器不允许扩展在此页面运行脚本」,当前页清单 (0/0)、无任何脚本行。
  • 普通页(含跨 host iframe)(2/2)allsite-probe(命中顶层)与 iframe-only-probe(只匹配 iframe)都在,
    两者分别在顶层 / iframe 注册的 GM 菜单 demo-menu / iframe-menu 也都列出可点 —— 同时是缺陷 B 的渲染证据。
  • Chrome 应用商店:说明条 + (0/0)(脚本确实注入不了)。
  • Edge 商店(在 Chrome 里打开)(1/1) 正常列出脚本 —— 这一组就是「硬名单会误伤」的反证。

截 popup 有个坑:它必须开在 focused:false 的独立窗口里,否则 popup 自己就是 lastFocusedWindow
active tab,getCurrentTab() 拿不到目标页;视口要固定 360px,否则 popup.html 会按 #686 的移动端媒体查询铺满。

restricted-chrome-settings-light

已知限制

  • 未在真实 Edge 浏览器上观察 Edge 访问自家商店的行为(本机无 Edge + driver)。该方向由结构保证而非名单:商店判定排在注入证据之后,拦了就没报到 → restricted,没拦就有报到 → ok,两种都不误伤;两条单测钉住了这个顺序。
  • 企业策略 runtime_blocked_hosts 拦下的页面无法探测具体原因,落到 not-injected(文案为「刷新页面后生效」)。
  • file:// 那条只给文案、不给跳转按钮:Chrome 是 chrome://extensions/?id=、Firefox 是 about:addons,跨浏览器引导不统一。
  • url_matcher.ts*:// / http*:// 匹配任意协议(与 Chrome/TM 的 * = http|https 不同)未在本 PR 处理。做完可达性判定后它在 popup 上不可见,但仍影响 file:// 的真实注入行为,建议单独跟进。

CodFrm and others added 3 commits August 24, 2026 16:45
popup「当前页运行脚本」原本只按顶层网址做 pattern 匹配,带来两类失真:

1. iframe 内运行的脚本整条消失。#1666 为了让「排除本站」后该行立即消失,
   删掉了 #1511 加的「运行过但未匹配」合并,代价是 @match 只命中 iframe 的
   脚本连同它在 iframe 注册的 GM 菜单都不再出现。现改为「本 tab 跑过 ∧ 仍匹配
   某个子 frame」——排除本站后脚本对所有 frame 都不再匹配,仍会立即消失。
   仅匹配子 frame 的行标记 matchesTopFrame=false,UI 据此隐藏按顶层 host
   生成规则的站点范围操作(否则会写出 *://settings/* 这类垃圾规则)。

2. chrome:// 等脚本猫触及不到的页面上照样列出脚本。新增页面状态判定:
   协议/商店名单给出准确原因,「本 tab 有没有 content script 报到」作为
   运行时证据兜住白名单漏掉的情况(企业策略等)。file:// 的权限查询只用于
   给未注入的情况一个更准确的原因,不反过来否定已注入的事实。
   黑名单页并入同一状态——它同样不会注入,此前却照常列脚本。
   GetPopupDataRes.isBlacklist 相应替换为 pageStatus。

e2e/popup-matching-regressions.spec.ts 原先用不授予 userScripts 权限的
fixture,脚本从未真正注入,验证的只是 pattern 命中这一层(即本 issue 的假象
在测试套件里的镜像)。改用带权限与 .test host 解析的 fixture + 本地 mock 页。
商店域是浏览器相关的:Edge 商店在 Chrome 里就是普通网页,Chrome 商店在
Firefox 里也一样。原实现把商店域并进 getPageAccessKind 的硬名单、优先于
注入证据判定,会在「别家浏览器」上误报脚本不能运行——而它其实在跑。

改为与 file:// 权限查询同一处理:只用来给「已确认没注入」的页面一个更准确
的原因,不反过来否定已注入的事实。顺带补上 microsoftedge.microsoft.com/addons。

This comment was marked as outdated.

@cyfung1031 cyfung1031 changed the title 🐛 popup 当前页脚本按实际注入展示,而非 pattern 命中 (#1687) 🐛 修复 popup 将未运行脚本误显示为当前页脚本,并恢复 iframe 脚本显示 (#1687) Aug 24, 2026

This comment was marked as outdated.

@cyfung1031 cyfung1031 changed the title 🐛 修复 popup 将未运行脚本误显示为当前页脚本,并恢复 iframe 脚本显示 (#1687) 🐛 修复 popup「当前页运行脚本」误报:过滤不可运行页面并恢复 iframe 脚本显示 (#1687) Aug 24, 2026
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.

[BUG] popup当前页运行脚本展示的并不是实际运行的脚本

2 participants