Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/app/service/service_worker/popup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1172,3 +1172,35 @@ describe("PopupService removeDeletedScriptsFromPopupCaches 缓存清理边界情
await expect(service.getScriptMenu(-1)).resolves.toHaveLength(0);
});
});

describe("PopupService clearTabData 标签页关闭/替换时释放 tab 数据", () => {
beforeEach(async () => {
await cacheInstance.clear();
});

it("标签页被替换(如被丢弃后换了 tabId)时,GM_saveTab 数据迁移到新 tabId,旧 tabId 的缓存被清理", async () => {
await cacheInstance.set(`${CACHE_KEY_TAB_SCRIPT}${10}`, [createMenu("saved"), createMenu("unsaved")]);
await cacheInstance.set(`${CACHE_KEY_TAB_LOADED}${10}`, "https://example.com");
await cacheInstance.set("GM_getTab:saved", { 10: { v: "tab10" }, 20: { v: "tab20" } });
await cacheInstance.set("GM_getTab:unsaved", { 20: { v: "only-tab20" } });

const { service } = createService();
await service.clearTabData(10, 11);

await expect(cacheInstance.get("GM_getTab:saved")).resolves.toEqual({ 11: { v: "tab10" }, 20: { v: "tab20" } });
await expect(cacheInstance.get("GM_getTab:unsaved")).resolves.toEqual({ 20: { v: "only-tab20" } });
await expect(service.getScriptMenu(10)).resolves.toEqual([]);
await expect(cacheInstance.get(`${CACHE_KEY_TAB_LOADED}${10}`)).resolves.toBeUndefined();
});

it("标签页关闭时删除该 tabId 的 GM_saveTab 数据,不影响其他标签页", async () => {
await cacheInstance.set(`${CACHE_KEY_TAB_SCRIPT}${10}`, [createMenu("saved")]);
await cacheInstance.set("GM_getTab:saved", { 10: { v: "tab10" }, 20: { v: "tab20" } });

const { service } = createService();
await service.clearTabData(10);

await expect(cacheInstance.get("GM_getTab:saved")).resolves.toEqual({ 20: { v: "tab20" } });
await expect(service.getScriptMenu(10)).resolves.toEqual([]);
});
});
78 changes: 47 additions & 31 deletions src/app/service/service_worker/popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,42 @@ export class PopupService {
);
}

/**
* 释放标签页关闭时需要清理的数据。
* @param replacedByTabId 标签页被替换时的新 tabId:对用户而言仍是同一个标签页,GM_saveTab 数据随之迁移而不是删除
*/
clearTabData(tabId: number, replacedByTabId?: number) {
runCountMap.delete(tabId);
scriptCountMap.delete(tabId);
cacheInstance.del(`${CACHE_KEY_TAB_LOADED}${tabId}`);
const list = this.updateMenuCommands.get(tabId);
if (list) {
// 避免 menuCommand 更新在 Tab 移除后触发
list.length = 0;
this.updateMenuCommands.delete(tabId);
}
return cacheInstance.tx(`${CACHE_KEY_TAB_SCRIPT}${tabId}`, (scripts: ScriptMenu[] | undefined, tx) => {
if (scripts) {
tx.del();
return Promise.all(
scripts.map(({ uuid }) => {
// GM_saveTab 的数据按 tabId 存放,只能借助该 tab 运行过的脚本列表找到,
// 所以必须在这里、在删除脚本列表的同一事务里处理
return cacheInstance.tx(`GM_getTab:${uuid}`, (tabData: { [key: number]: any } | undefined, tx) => {
if (tabData) {
if (replacedByTabId !== undefined && tabId in tabData) {
tabData[replacedByTabId] = tabData[tabId];
}
delete tabData[tabId];
tx.set(tabData);
}
});
})
);
}
});
}

init() {
// 处理脚本菜单数据
this.mq.subscribe<TScriptMenuRegister>("registerMenuCommand", this.registerMenuCommand.bind(this));
Expand All @@ -817,43 +853,23 @@ export class PopupService {
this.dealBackgroundScriptInstall();

// 监听tab开关
const clearData = async (tabId: number) => {
runCountMap.delete(tabId);
scriptCountMap.delete(tabId);
cacheInstance.del(`${CACHE_KEY_TAB_LOADED}${tabId}`);
const list = this.updateMenuCommands.get(tabId);
if (list) {
// 避免 menuCommand 更新在 Tab 移除后触发
list.length = 0;
this.updateMenuCommands.delete(tabId);
}
// 清理数据tab关闭需要释放的数据
cacheInstance.tx(`${CACHE_KEY_TAB_SCRIPT}${tabId}`, (scripts: ScriptMenu[] | undefined, tx) => {
if (scripts) {
tx.del();
return Promise.all(
scripts.map(({ uuid }) => {
// 处理GM_saveTab关闭事件, 由于需要用到tab相关的脚本数据,所以需要在这里处理
// 避免先删除了数据获取不到
return cacheInstance.tx(`GM_getTab:${uuid}`, (tabData: { [key: number]: any } | undefined, tx) => {
if (tabData) {
delete tabData[tabId];
tx.set(tabData);
}
});
})
);
}
});
};
chrome.tabs.onRemoved.addListener((tabId) => {
const lastError = chrome.runtime.lastError;
if (lastError) {
console.error("chrome.runtime.lastError in chrome.tabs.onRemoved:", lastError);
// 没有 tabId 资讯,无法释放数据
return;
}
clearData(tabId);
this.clearTabData(tabId);
});
// 标签页被丢弃(discard)等情况下,浏览器会用新 tabId 替换原标签页,旧 tabId 不会再触发 onRemoved
chrome.tabs.onReplaced.addListener((addedTabId, removedTabId) => {
const lastError = chrome.runtime.lastError;
if (lastError) {
console.error("chrome.runtime.lastError in chrome.tabs.onReplaced:", lastError);
return;
}
this.clearTabData(removedTabId, addedTabId);
});
/**
* @param tabId 当前页面的 tabId。如 tabId 为 null, 则呼叫 getCurrentTab() 以API取当前页面的 tabId。
Expand Down Expand Up @@ -898,7 +914,7 @@ export class PopupService {
// 没有开启脚本时不会触发pageLoad更新数据
// 这里做一次清理
if (runCountMap.has(details.tabId) && !this.runtime.isLoadScripts) {
clearData(details.tabId);
this.clearTabData(details.tabId);
}
});

Expand Down
Loading