Skip to content

🐛 修复标签页被丢弃后 GM_getTab 读不到 GM_saveTab 数据 - #1796

Open
CodFrm wants to merge 1 commit into
mainfrom
fix/1791-tab-replaced-gm-tab
Open

CodFrm wants to merge 1 commit into
mainfrom
fix/1791-tab-replaced-gm-tab

Conversation

@CodFrm

@CodFrm CodFrm commented Oct 20, 2026

Copy link
Copy Markdown
Member

Checklist / 检查清单

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

背景

fix #1791

浏览器没有启用 WebContentsDiscard 时(Chromium 桌面端源码默认关闭,正式版 Chrome 由 Finch 灰度决定,发行版自己编译的 Chromium 通常关闭),丢弃标签页会用新 tabId 替换原标签页:只触发 chrome.tabs.onReplaced(addedTabId, removedTabId),旧 tabId 不会再触发 onRemoved。GM_saveTab 的数据按 tabId 存在 GM_getTab:<uuid> 里,此前只处理了 onRemoved,所以切回标签页后,页面用新 tabId 查不到数据,GM_getTab 返回 {};GM_getTabs 里旧 tabId 的数据一直残留。

本次改动

  • 新增 chrome.tabs.onReplaced 监听:每个脚本的 GM_saveTab 数据从旧 tabId 迁移到新 tabId。旧 tabId 的其余标签页缓存(脚本列表、运行计数、菜单、加载标记)按关闭释放,以前这些在替换后会一直留在 session storage 里。
  • 标签页释放逻辑从 init() 内的 clearData 闭包提为 PopupService.clearTabData(tabId, replacedByTabId?),三个调用点(onRemoved、onBeforeNavigate、新增的 onReplaced)都改为调用它,便于在服务层测试。onRemoved 和 onBeforeNavigate 的行为不变。

实现考虑

  • 迁移而不是删除:对用户来说被丢弃又恢复的仍是同一个标签页,GM_getTab 的语义是数据在标签页打开期间一直保留。Tampermonkey 5.5.0 在同样条件下会把旧数据删掉(实测切回后同样返回 {}),这里没有照搬。
  • 放在 popup.ts 而不是 gm_api.ts:GM_saveTab 数据只能借助「该标签页运行过的脚本列表」(CACHE_KEY_TAB_SCRIPT)找到,原有的关闭清理就放在这里,并且在同一个事务回调里先读脚本列表再处理数据。如果分开写,两个监听器会争同一份数据。

已知限制

  • onReplaced 监听的接线没有单元测试:现有 chrome mock 没有 onActivated/webNavigation,测试里调不了 init()。接线是靠下面的真实浏览器运行验证的。
  • 按代码推断,GM_openInTab 的关闭通知(gm_api.ts,缓存键 GM_openInTab:<tabId>)有同样的根因:被打开的标签页如果被丢弃后再关闭,脚本收不到 onclose。没有实际复现,属于另一个功能,不在本 PR 范围内。

验证

基线 58309d5e7406f7f71a26b2e20a63fa3a4a8e1abc,本 PR head f9b66c12772994748f1510bf2a5117c728587996;git diff --stat origin/main...HEAD 只有 popup.ts、popup.test.ts 两个文件。

  • 先写测试:popup.test.ts 新增两条用例(替换时迁移到新 tabId;关闭时只删除该 tabId 的数据),改代码前两条都失败。改完后临时去掉迁移那一行,替换用例会失败,确认它能抓到回归。
  • pnpm exec vitest run --no-coverage src/app/service/service_worker/popup.test.ts:59/59 通过。
  • pnpm run typecheck、改动文件的 prettier --check 和 eslint 都通过(commit hook 也跑过)。
  • pnpm exec vitest run --no-coverage src/app/service/service_worker:多次运行偶尔有 1~3 条失败,都在 script.test.ts(openUpdatePageByUUID)和 trash_freeze.test.ts。把本 PR 改动 stash 掉、在基线上跑也会出现同样的失败,单独跑 script.test.ts 3 次全过,判断为已有的不稳定用例,和本改动无关。
  • 真实浏览器:pnpm run build 后加载 dist/ext,在 Chrome for Testing 153 上,由一个辅助扩展执行「打开 → GM_saveTab → 刷新 → 切到其他标签页 → chrome.tabs.discard → 切回」:
    • 修复前,--disable-features=WebContentsDiscard:tabId 变化,触发 onReplaced,切回后 GM_getTab 返回 {}。
    • 修复后,--disable-features=WebContentsDiscard:tabId 597587208 → 597587210,切回后读到原数据。
    • 修复后,--enable-features=WebContentsDiscard:tabId 不变,数据照常保留。

浏览器未启用 WebContentsDiscard 时,丢弃标签页会用新 tabId 替换原标签页,
只触发 tabs.onReplaced、旧 tabId 不再触发 onRemoved。GM_saveTab 数据按 tabId
存放,此前只处理了 onRemoved,切回标签页后用新 tabId 查不到数据。

新增 onReplaced 监听:GM_saveTab 数据随标签页迁移到新 tabId,旧 tabId 的其余
标签页缓存按关闭释放。tab 释放逻辑从 init 闭包提为 PopupService.clearTabData,
便于在服务层测试。
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] GM_getTab does not return GM_saveTab data after discard

1 participant