Skip to content

Commit 72f5e93

Browse files
authored
🐛 修复 ScriptCat 长时间运行时可能出现的内存泄漏问题 (#1401)
1 parent 8fe5035 commit 72f5e93

5 files changed

Lines changed: 46 additions & 7 deletions

File tree

src/app/service/content/gm_api/gm_xhr.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ export function GM_xmlhttpRequest(
290290
onMessageHandler = null;
291291
doAbort = null;
292292
refCleanup = null;
293+
connect?.disconnect(); // 确保 connect 断开
293294
connect = null;
294295
};
295296

@@ -529,14 +530,22 @@ export function GM_xmlhttpRequest(
529530
};
530531
if (msgData.code === -1) {
531532
// 处理错误
533+
const message = msgData.message || "unknown";
534+
const code = msgData.code;
532535
LoggerCore.logger().error("GM_xmlhttpRequest error", {
533-
code: msgData.code,
534-
message: msgData.message,
535-
});
536-
details.onerror?.({
537-
readyState: ReadyStateCode.DONE,
538-
error: msgData.message || "unknown",
536+
code: code,
537+
message,
539538
});
539+
if (!reqDone) {
540+
errorOccur = message;
541+
details.onerror?.({
542+
readyState: ReadyStateCode.DONE,
543+
error: message,
544+
});
545+
reqDone = true;
546+
retPromiseReject?.(message);
547+
refCleanup?.();
548+
}
540549
return;
541550
}
542551
// 处理返回

src/app/service/service_worker/gm_api/gm_api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ const cleanupOnAPIError = (requestId: string) => {
109109
if (!markerID) return;
110110
redirectedUrls.delete(markerID);
111111
nwErrorResults.delete(markerID);
112+
nwErrorResultPromises.delete(markerID);
112113
scXhrRequests.delete(markerID);
113114
headersReceivedMap.delete(markerID);
114115
headersSettled(markerID); // 处理完毕
@@ -891,6 +892,7 @@ export default class GMApi {
891892
const loadendCleanUp = () => {
892893
redirectedUrls.delete(markerID);
893894
nwErrorResults.delete(markerID);
895+
nwErrorResultPromises.delete(markerID);
894896
const reqId = scXhrRequests.get(markerID);
895897
if (reqId) scXhrRequests.delete(reqId);
896898
scXhrRequests.delete(markerID);

src/app/service/service_worker/gm_api/gm_xhr.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ export class GMXhrFetchStrategy implements GMXhrStrategy {
110110
}),
111111
new Promise((r) => setTimeout(r, 800)),
112112
]);
113+
nwErrorResultPromises.delete(this.resultParam.markerID);
113114
nwErr = nwErrorResults.get(this.resultParam.markerID);
114115
}
115116
if (nwErr) {

src/pkg/utils/match.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@ describe.concurrent("UrlMatch-internal1", () => {
4242
});
4343
});
4444

45+
describe.concurrent("UrlMatch-cache", () => {
46+
it.concurrent("limits cached URL entries and refreshes recently used entries", () => {
47+
const url = new UrlMatch<string>(2);
48+
url.addMatch("*://*/*", "ok");
49+
50+
expect(url.urlMatch("https://a.example/")).toEqual(["ok"]);
51+
expect(url.urlMatch("https://b.example/")).toEqual(["ok"]);
52+
expect(url.urlMatch("https://a.example/")).toEqual(["ok"]);
53+
expect(url.urlMatch("https://c.example/")).toEqual(["ok"]);
54+
55+
expect([...url.cacheMap.keys()]).toEqual(["https://a.example/", "https://c.example/"]);
56+
});
57+
});
58+
4559
describe.concurrent("UrlMatch-internal2", () => {
4660
const url = new UrlMatch<string>();
4761
url.addInclude("*gro???***???.com*", "ok1");

src/pkg/utils/match.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ export class UrlMatch<T> {
66
public readonly cacheMap = new Map<string, T[]>();
77
private sorter: Partial<Record<string, number>> | null = null;
88

9+
constructor(private readonly maxCacheEntries: number = 4096) {
10+
if (this.maxCacheEntries <= 0) throw new Error(`maxCacheEntries ${this.maxCacheEntries} <= 0`);
11+
}
12+
913
public addRules(uuid: T, rules: URLRuleEntry[]) {
1014
this.cacheMap.clear();
1115
let map = this.rulesMap.get(uuid);
@@ -15,7 +19,12 @@ export class UrlMatch<T> {
1519

1620
public urlMatch(url: string): T[] {
1721
const cacheMap = this.cacheMap;
18-
if (cacheMap.has(url)) return cacheMap.get(url) as T[];
22+
if (cacheMap.has(url)) {
23+
const cached = cacheMap.get(url) as T[];
24+
cacheMap.delete(url);
25+
cacheMap.set(url, cached);
26+
return cached;
27+
}
1928
const res: T[] = [];
2029
for (const [uuid, rules] of this.rulesMap) {
2130
try {
@@ -38,6 +47,10 @@ export class UrlMatch<T> {
3847
});
3948
}
4049
cacheMap.set(url, res);
50+
if (cacheMap.size > this.maxCacheEntries) {
51+
const oldest = cacheMap.keys().next().value;
52+
if (oldest !== undefined) cacheMap.delete(oldest);
53+
}
4154
return res;
4255
}
4356

0 commit comments

Comments
 (0)