Skip to content

Commit 1511a5b

Browse files
authored
ver: update to v1.8.83 (#39)
# v1.8.83(含迭代内容) ## 主要改动 ### 1. 功能增强 - ✅ 菜单功能完善 - 添加浏览器拓展右键菜单 - ✅ 重构 Service Worker 初始化逻辑: - **统一初始化入口**:创建了单一的 `initialize` 函数,用于处理扩展安装、浏览器启动和 Service Worker 意外重启时的状态恢复,确保了逻辑的一致性 - **实现懒加载守卫**:引入 `ensureInitialized` 机制,在处理 `onMessage`、`onUpdated` 等事件前检查并确保初始化已完成,防止因 Service Worker 休眠后重启导致的状态丢失问题 - ✅ 修复并发与竞态问题: - **引入互斥机制**:为 `updateHeaderRules` 添加 Promise 队列,确保规则更新操作串行执行,避免并发调用导致的状态不一致 - **防抖合并请求**:实现短时间内相同语言切换请求的自动合并,减少不必要的API调用和日志 - **优化缓存策略**:改进 `rulesCache` 机制,自动切换也能安全使用短路返回,因为规则仅由本扩展管理 - **状态同步增强**:确保 `lastAppliedLanguage` 和 `rulesCache` 在并发场景下的一致性 - ✅ 性能优化 - 修复自动切换时的无效更新与性能浪费问题,通过改进缓存检查逻辑避免不必要的 API 调用 ### 2. 代码优化 - ✅ 清理无效代码 - 去除无效变量 err - ✅ 重构 createContextMenusOnce 使用 async/await(Promise API),符合代码规范 - ✅ 右键菜单统一使用英文,去除 i18n 化(简化实现) - ✅ 简化URL检查条件 - ✅ 优化动态规则ID数组创建逻辑,采用 `.some()` 方法以兼顾性能与代码表现力 - ✅ 将 `updateHeaderRules` 拆分为外部包装器和内部实现,提供更好的并发控制 - ✅ 修复重试逻辑中的递归调用问题,避免绕过并发控制机制,优化错误处理 - ✅ 优化按钮代码 - ✅ 优化 Chrome API 调用方式,移除不必要的 Promise 包装,直接使用 async/await - ✅ 重构调试 UI 的代码结构,将回调函数改为 async/await 方式,调整了部分变量和函数的命名,移除冗余的代码 - ✅ 优化多处异步操作并移除错误处理冗余 ### 3. Bug 修复 - ✅ 修复 `tabs.onUpdated` 过滤条件 Bug,该 Bug 会导致 http 页面不触发自动切换 - ✅ 修复动态规则清理范围过宽问题,现在只移除与 `RULE_ID` 相关的规则,避免误删其他动态规则 - ✅ 收窄 DNR 资源类型与匹配范围 - ✅ 修复debug-ui.js资源管理问题 - ✅ detect.js资源管理修复 - ✅ background.js资源管理修复 ## 文件变更清单 ### 新增文件 - Problems.md - LLM整理的现存问题(v1.8.67) - Wiki_EN.md - 英文版Wiki(v1.8.69) - Resource_Management_Guide.md - 资源管理最佳实践指南(v1.8.79) ### 重命名文件 - ### 修改文件 - manifest.json - 版本号更新(v1.8.67、v1.8.68、v1.8.69、v1.8.70、v1.8.71、v1.8.72、v1.8.73、v1.8.74、v1.8.79、v1.8.83);添加 contextMenus 权限(v1.8.67) - TODO.md - 修改待办事项(v1.8.67、v1.8.79) - Update.md - 版本更新记录(v1.8.67、v1.8.68、v1.8.69、v1.8.70、v1.8.71、v1.8.72、v1.8.73、v1.8.74、v1.8.79、v1.8.83) - background.js - 添加右键菜单初始化标志(v1.8.67);重构 contextMenus 创建逻辑(async/await),右键菜单统一使用英文,去除 i18n 化(v1.8.68);修复 `tabs.onUpdated` 的 URL 过滤条件 Bug(v1.8.68);简化URL检查条件(v1.8.68);重构了初始化和事件处理逻辑(v1.8.69);修复动态规则清理范围过宽问题(v1.8.70);优化动态规则ID数组创建逻辑(v1.8.70);引入并发控制机制,添加 Promise 队列和防抖机制,修复并发与竞态问题(v1.8.71);优化异步操作并移除错误处理冗余(v1.8.73); 修复自动切换时的无效更新与性能浪费问题(v1.8.73);收窄 DNR 资源类型与匹配范围(v1.8.74);添加完整的resourceTracker系统管理事件监听器和定时器(v1.8.80);替换setTimeout调用为resourceTracker.setTimeout,跟踪所有Chrome API事件监听器(v1.8.81);添加扩展暂停时的自动清理机制(v1.8.82) - shared-i18n-base.js - 优化翻译加载机制,避免重复加载脚本文件,完善翻译回退(v1.8.68) - I18n_Usage_Guide.md - 内容更新(v1.8.67、v1.8.68) - .gitignore - git忽略文件更新(v1.8.67) - Problems.md - 问题列表更新(v1.8.68、v1.8.69、v1.8.70、v1.8.71、v1.8.73、v1.8.74) - Wiki.md - Wiki更新(v1.8.69、v1.8.70) - Wiki_EN.md - 英文Wiki更新(v1.8.70) - i18n/background-*.js - 完善相关翻译(v1.8.71、v1.8.73) - toggle.js - 优化按钮代码(v1.8.71);添加完整的resourceTracker系统管理事件监听器(v1.8.80);替换addEventListener调用为resourceTracker.addEventListener(v1.8.81);添加页面卸载时的自动清理机制(v1.8.82);跟踪语言切换按钮、主题切换按钮、系统主题监听器和页面加载事件(v1.8.83) - popup.js - 优化 Chrome API 调用方式,直接使用 async/await(v1.8.72);优化按钮处理逻辑为event事件处理(v1.8.73) - debug-ui.js - 优化代码结构,将回调函数改为 async/await 方式,调整了部分变量和函数的命名,移除冗余的代码(v1.8.72);添加resourceTracker系统统一管理资源生命周期(v1.8.75);修复15+个未管理的事件监听器内存泄漏(v1.8.76);修复1个setInterval和2个setTimeout定时器泄漏(v1.8.77);修复2个消息监听器资源泄漏(v1.8.78);添加页面卸载时的自动清理机制(v1.8.79) - shared-utils.js - 完善fallback翻译键(v1.8.72) - Code_Style_Guide.md - 代码风格指南更新(v1.8.72) - shared-update-checker.js - 优化异步操作并移除错误处理冗余(v1.8.73) - domain-rules-manager.js - 优化异步操作并移除错误处理冗余(v1.8.73) - detect.js - 添加完整的resourceTracker系统管理所有资源类型(v1.8.80);替换多个函数创建为resourceTracker.create函数,替换定时器调用为resourceTracker.setTimeout(v1.8.81);- 添加页面卸载时的自动清理机制(v1.8.82) ### 移除内容 - 移除未使用的变量 chrome.runtime.lastError 读取语句 - 已完成的TODO / 已修复的问题 - 将 `performInitialization` 和 `handleAutoSwitchToggleRequest` 中的重复规则应用逻辑提取到新的共享函数 `applyLanguageRulesBasedOnState` 中 - 移除大量prromise包装
2 parents 5c97d02 + d166ae3 commit 1511a5b

8 files changed

Lines changed: 863 additions & 118 deletions

File tree

background.js

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,45 @@ importScripts('domain-rules-manager.js');
1313
// 5. 导入更新检查器
1414
importScripts('shared/shared-update-checker.js');
1515

16+
// --- 资源管理器 ---
17+
// 仅管理定时器,避免误导性的事件监听器管理
18+
const resourceTracker = {
19+
timers: [],
20+
intervals: [],
21+
22+
// 定时器管理
23+
setTimeout: function(callback, delay) {
24+
const id = setTimeout(callback, delay);
25+
this.timers.push(id);
26+
return id;
27+
},
28+
29+
setInterval: function(callback, delay) {
30+
const id = setInterval(callback, delay);
31+
this.intervals.push(id);
32+
return id;
33+
},
34+
35+
clearTimeout: function(id) {
36+
clearTimeout(id);
37+
this.timers = this.timers.filter(timerId => timerId !== id);
38+
},
39+
40+
clearInterval: function(id) {
41+
clearInterval(id);
42+
this.intervals = this.intervals.filter(intervalId => intervalId !== id);
43+
},
44+
45+
// 统一清理方法
46+
cleanup: function() {
47+
// 清理定时器
48+
this.timers.forEach(id => clearTimeout(id));
49+
this.timers = [];
50+
this.intervals.forEach(id => clearInterval(id));
51+
this.intervals = [];
52+
}
53+
};
54+
1655
// 常量定义
1756
const RULE_ID = 1;
1857
// 统一并简化语言常量
@@ -336,7 +375,7 @@ const handleRuleUpdateError = async (error, language, retryCount) => {
336375
sendBackgroundLog(`${backgroundI18n.t('retry_after', { delay, count: nextRetryCount })}`, 'warning');
337376

338377
// 等待后重试
339-
await new Promise(resolve => setTimeout(resolve, delay));
378+
await new Promise(resolve => resourceTracker.setTimeout(resolve, delay));
340379
return await updateHeaderRulesInternal(language, nextRetryCount, false);
341380
} else {
342381
// 超过重试次数或不可重试的错误
@@ -450,8 +489,10 @@ const notifyPopupUIUpdate = (autoSwitchEnabled, currentLanguage) => {
450489
latestAutoSwitchEnabled = autoSwitchEnabled;
451490
latestCurrentLanguage = currentLanguage;
452491

453-
clearTimeout(pendingUIUpdate);
454-
pendingUIUpdate = setTimeout(() => {
492+
if (pendingUIUpdate) {
493+
clearTimeout(pendingUIUpdate);
494+
}
495+
pendingUIUpdate = resourceTracker.setTimeout(() => {
455496
const message = {
456497
type: 'AUTO_SWITCH_UI_UPDATE',
457498
autoSwitchEnabled: latestAutoSwitchEnabled,
@@ -1215,7 +1256,7 @@ const handleGetManifestInfoRequest = (sendResponse) => {
12151256
try {
12161257
const manifest = chrome.runtime.getManifest();
12171258
const extensionId = chrome.runtime.id;
1218-
1259+
12191260
if (typeof sendResponse === 'function') {
12201261
sendResponse({
12211262
success: true,
@@ -1232,4 +1273,10 @@ const handleGetManifestInfoRequest = (sendResponse) => {
12321273
});
12331274
}
12341275
}
1235-
};
1276+
};
1277+
1278+
// 扩展卸载时的清理
1279+
chrome.runtime.onSuspend.addListener(() => {
1280+
sendBackgroundLog('Extension suspending, cleaning up resources...', 'info');
1281+
resourceTracker.cleanup();
1282+
});

debug-ui.js

Lines changed: 109 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,87 @@
11
// debug-ui.js - 调试页面UI交互脚本
22

3+
/**
4+
* 资源跟踪器 - 管理事件监听器、定时器等资源
5+
*/
6+
const resourceTracker = {
7+
eventListeners: [],
8+
timers: [],
9+
intervals: [],
10+
messageListeners: [],
11+
12+
/**
13+
* 添加事件监听器并跟踪
14+
* @param {Element} element - 目标元素
15+
* @param {string} event - 事件类型
16+
* @param {Function} handler - 事件处理函数
17+
* @param {Object} options - 事件选项
18+
*/
19+
addEventListener: function(element, event, handler, options = null) {
20+
element.addEventListener(event, handler, options);
21+
this.eventListeners.push({ element, event, handler, options });
22+
},
23+
24+
/**
25+
* 添加定时器并跟踪
26+
* @param {Function} callback - 回调函数
27+
* @param {number} delay - 延迟时间
28+
* @param {...any} args - 传递给回调的参数
29+
* @returns {number} 定时器ID
30+
*/
31+
setTimeout: function(callback, delay, ...args) {
32+
const id = setTimeout(callback, delay, ...args);
33+
this.timers.push(id);
34+
return id;
35+
},
36+
37+
/**
38+
* 添加间隔定时器并跟踪
39+
* @param {Function} callback - 回调函数
40+
* @param {number} interval - 间隔时间
41+
* @param {...any} args - 传递给回调的参数
42+
* @returns {number} 间隔定时器ID
43+
*/
44+
setInterval: function(callback, interval, ...args) {
45+
const id = setInterval(callback, interval, ...args);
46+
this.intervals.push(id);
47+
return id;
48+
},
49+
50+
/**
51+
* 添加消息监听器并跟踪
52+
* @param {Function} listener - 消息监听器函数
53+
*/
54+
addMessageListener: function(listener) {
55+
chrome.runtime.onMessage.addListener(listener);
56+
this.messageListeners.push(listener);
57+
},
58+
59+
/**
60+
* 清理所有跟踪的资源
61+
*/
62+
cleanup: function() {
63+
// 移除所有事件监听器
64+
this.eventListeners.forEach(({ element, event, handler, options }) => {
65+
element.removeEventListener(event, handler, options);
66+
});
67+
this.eventListeners = [];
68+
69+
// 清除所有定时器
70+
this.timers.forEach(id => clearTimeout(id));
71+
this.timers = [];
72+
73+
// 清除所有间隔定时器
74+
this.intervals.forEach(id => clearInterval(id));
75+
this.intervals = [];
76+
77+
// 移除所有消息监听器
78+
this.messageListeners.forEach(listener => {
79+
chrome.runtime.onMessage.removeListener(listener);
80+
});
81+
this.messageListeners = [];
82+
}
83+
};
84+
385
/**
486
* 安全地创建带有样式类的消息元素
587
* @param {string} message - 消息文本
@@ -65,15 +147,15 @@ const getExternalCheckLinks = (prefix = debugI18n.t('please_visit')) => {
65147
return `<p>${prefix} <a href="https://webcha.cn/" target="_blank">https://webcha.cn/</a> ${debugI18n.t('or')} <a href="https://www.browserscan.net/zh" target="_blank">https://www.browserscan.net/zh</a> ${debugI18n.t('to_view')}</p>`;
66148
};
67149

68-
document.addEventListener('DOMContentLoaded', () => {
150+
resourceTracker.addEventListener(document, 'DOMContentLoaded', () => {
69151
// 初始化语言选项
70152
const testLanguageSelect = document.getElementById('testLanguage');
71153
if (testLanguageSelect) {
72154
populateLanguageSelect(testLanguageSelect);
73155
}
74156

75157
// 显示当前规则和匹配的规则详情
76-
document.getElementById('showRulesBtn').addEventListener('click', () => {
158+
resourceTracker.addEventListener(document.getElementById('showRulesBtn'), 'click', () => {
77159
const resultElement = document.getElementById('rulesResult');
78160
resultElement.innerHTML = debugI18n.t('getting_rule_info');
79161

@@ -213,7 +295,7 @@ document.addEventListener('DOMContentLoaded', () => {
213295
};
214296

215297
// 监听来自扩展其他部分的日志消息
216-
chrome.runtime.onMessage.addListener((request) => {
298+
resourceTracker.addMessageListener((request) => {
217299
if (request.type !== 'DEBUG_LOG') return;
218300

219301
// 过滤掉后台脚本的日志消息
@@ -223,14 +305,14 @@ document.addEventListener('DOMContentLoaded', () => {
223305
});
224306

225307
// 清除日志按钮功能
226-
clearLogsBtn.addEventListener('click', () => {
308+
resourceTracker.addEventListener(clearLogsBtn, 'click', () => {
227309
allLogMessages = []; // 清空存储的日志
228310
renderLogs(); // 渲染空日志列表
229311
});
230312

231313
// 监听过滤器变化
232314
document.querySelectorAll('.form-check-input[id^="filter"]').forEach(checkbox => {
233-
checkbox.addEventListener('change', renderLogs);
315+
resourceTracker.addEventListener(checkbox, 'change', renderLogs);
234316
});
235317

236318
// 使用通用 fallback 翻译系统,避免依赖异步加载的 debugI18n
@@ -253,7 +335,7 @@ document.addEventListener('DOMContentLoaded', () => {
253335
if (debugI18n.translations && Object.keys(debugI18n.translations).length > 0) {
254336
addLogMessage(`${result.data?.autoSwitchEnabled ? debugI18n.t('auto_switch_enabled') : debugI18n.t('auto_switch_disabled')}`, 'info');
255337
} else {
256-
setTimeout(checkI18nAndLog, 100);
338+
resourceTracker.setTimeout(checkI18nAndLog, 100);
257339
}
258340
};
259341

@@ -265,7 +347,7 @@ document.addEventListener('DOMContentLoaded', () => {
265347

266348

267349
// 测试请求头
268-
document.getElementById('testHeaderBtn').addEventListener('click', async () => {
350+
resourceTracker.addEventListener(document.getElementById('testHeaderBtn'), 'click', async () => {
269351
const language = document.getElementById('testLanguage').value;
270352
const resultElement = document.getElementById('headerTestResult');
271353
setSafeContent(resultElement, `${debugI18n.t('testing_language_header')} "${language}" ${debugI18n.t('header_test_multiple')}`);
@@ -353,7 +435,7 @@ document.addEventListener('DOMContentLoaded', () => {
353435
});
354436

355437
// 修复规则优先级
356-
document.getElementById('fixPriorityBtn').addEventListener('click', () => {
438+
resourceTracker.addEventListener(document.getElementById('fixPriorityBtn'), 'click', () => {
357439
const resultElement = document.getElementById('fixResult');
358440
resultElement.innerHTML = debugI18n.t('fixing_rule_priority');
359441
addLogMessage(debugI18n.t('try_fix_priority'), 'info');
@@ -397,7 +479,7 @@ document.addEventListener('DOMContentLoaded', () => {
397479
});
398480

399481
// 清除并重新应用规则
400-
document.getElementById('clearAllRulesBtn').addEventListener('click', () => {
482+
resourceTracker.addEventListener(document.getElementById('clearAllRulesBtn'), 'click', () => {
401483
const resultElement = document.getElementById('fixResult');
402484
resultElement.innerHTML = debugI18n.t('clearing_rules_reapply');
403485
addLogMessage(debugI18n.t('try_clear_reapply'), 'info');
@@ -511,7 +593,7 @@ document.addEventListener('DOMContentLoaded', () => {
511593
}
512594

513595
// 应用自定义语言设置
514-
document.getElementById('applyCustomLangBtn').addEventListener('click', () => {
596+
resourceTracker.addEventListener(document.getElementById('applyCustomLangBtn'), 'click', () => {
515597
const customLangInput = document.getElementById('customLanguageInput');
516598
const customLangResult = document.getElementById('customLangResult');
517599
const languageString = customLangInput.value.trim();
@@ -589,7 +671,7 @@ document.addEventListener('DOMContentLoaded', () => {
589671
});
590672

591673
// 显示诊断信息
592-
document.getElementById('showDiagnosticsBtn').addEventListener('click', () => {
674+
resourceTracker.addEventListener(document.getElementById('showDiagnosticsBtn'), 'click', () => {
593675
const resultElement = document.getElementById('diagnosticsResult');
594676
resultElement.innerHTML = debugI18n.t('collecting_diagnostics');
595677
addLogMessage(debugI18n.t('try_show_diagnostics'), 'info');
@@ -671,7 +753,7 @@ document.addEventListener('DOMContentLoaded', () => {
671753
});
672754

673755
// 自动切换功能控制
674-
document.getElementById('autoSwitchToggle').addEventListener('change', function () {
756+
resourceTracker.addEventListener(document.getElementById('autoSwitchToggle'), 'change', function () {
675757
const isEnabled = this.checked;
676758
addLogMessage(`${debugI18n.t('try_enable_disable_auto')}${isEnabled ? debugI18n.t('enable') : debugI18n.t('disable')}${debugI18n.t('auto_switch_function_ellipsis')}`, 'info');
677759

@@ -697,7 +779,7 @@ document.addEventListener('DOMContentLoaded', () => {
697779
});
698780

699781
// 显示域名映射规则
700-
document.getElementById('showDomainRulesBtn').addEventListener('click', () => {
782+
resourceTracker.addEventListener(document.getElementById('showDomainRulesBtn'), 'click', () => {
701783
const resultElement = document.getElementById('domainRulesResult');
702784
resultElement.innerHTML = debugI18n.t('getting_domain_rules');
703785
addLogMessage(debugI18n.t('try_get_domain_rules'), 'info');
@@ -815,7 +897,7 @@ document.addEventListener('DOMContentLoaded', () => {
815897
});
816898

817899
// 监听来自 background.js 的消息
818-
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
900+
resourceTracker.addMessageListener(function (request, sender, sendResponse) {
819901
if (request.type === 'AUTO_SWITCH_UI_UPDATE') {
820902
const autoSwitchToggle = document.getElementById('autoSwitchToggle');
821903
if (autoSwitchToggle) {
@@ -847,6 +929,11 @@ document.addEventListener('DOMContentLoaded', () => {
847929
// 缓存管理功能
848930
initializeCacheManagement();
849931

932+
// 页面卸载时清理所有资源
933+
resourceTracker.addEventListener(window, 'beforeunload', () => {
934+
resourceTracker.cleanup();
935+
});
936+
850937
}); // DOMContentLoaded 结束
851938

852939
/**
@@ -859,41 +946,41 @@ const initializeCacheManagement = () => {
859946
// 刷新缓存统计按钮
860947
const refreshCacheStatsBtn = document.getElementById('refreshCacheStatsBtn');
861948
if (refreshCacheStatsBtn) {
862-
refreshCacheStatsBtn.addEventListener('click', refreshCacheStats);
949+
resourceTracker.addEventListener(refreshCacheStatsBtn, 'click', refreshCacheStats);
863950
}
864951

865952
// 预加载规则按钮
866953
const preloadRulesBtn = document.getElementById('preloadRulesBtn');
867954
if (preloadRulesBtn) {
868-
preloadRulesBtn.addEventListener('click', preloadDomainRules);
955+
resourceTracker.addEventListener(preloadRulesBtn, 'click', preloadDomainRules);
869956
}
870957

871958
// 清理域名缓存按钮
872959
const clearDomainCacheBtn = document.getElementById('clearDomainCacheBtn');
873960
if (clearDomainCacheBtn) {
874-
clearDomainCacheBtn.addEventListener('click', clearDomainCache);
961+
resourceTracker.addEventListener(clearDomainCacheBtn, 'click', clearDomainCache);
875962
}
876963

877964
// 清理所有缓存按钮
878965
const clearAllCacheBtn = document.getElementById('clearAllCacheBtn');
879966
if (clearAllCacheBtn) {
880-
clearAllCacheBtn.addEventListener('click', clearAllCache);
967+
resourceTracker.addEventListener(clearAllCacheBtn, 'click', clearAllCache);
881968
}
882969

883970
// 重置缓存统计按钮
884971
const resetCacheStatsBtn = document.getElementById('resetCacheStatsBtn');
885972
if (resetCacheStatsBtn) {
886-
resetCacheStatsBtn.addEventListener('click', resetCacheStats);
973+
resourceTracker.addEventListener(resetCacheStatsBtn, 'click', resetCacheStats);
887974
}
888975

889976
// 域名测试按钮
890977
const testDomainBtn = document.getElementById('testDomainBtn');
891978
if (testDomainBtn) {
892-
testDomainBtn.addEventListener('click', testDomainCache);
979+
resourceTracker.addEventListener(testDomainBtn, 'click', testDomainCache);
893980
}
894981

895982
// 初始加载缓存统计,并定期检查直到规则加载完成
896-
setTimeout(() => {
983+
resourceTracker.setTimeout(() => {
897984
refreshCacheStats();
898985
// 如果规则还未加载,定期检查状态
899986
checkPreloadStatusPeriodically();
@@ -908,7 +995,7 @@ const checkPreloadStatusPeriodically = () => {
908995
const maxChecks = 30; // 最多检查30次(15秒)
909996
const checkInterval = 500; // 每500ms检查一次
910997

911-
const intervalId = setInterval(() => {
998+
const intervalId = resourceTracker.setInterval(() => {
912999
checkCount++;
9131000

9141001
// 如果达到最大检查次数,停止检查

0 commit comments

Comments
 (0)