Skip to content

Commit 1d1c111

Browse files
committed
fix: use history length for install page exit
1 parent 056134a commit 1d1c111

3 files changed

Lines changed: 18 additions & 21 deletions

File tree

src/app/service/service_worker/script.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ export class ScriptService {
323323
action: {
324324
type: "redirect" as chrome.declarativeNetRequest.RuleActionType,
325325
redirect: {
326-
regexSubstitution: `${installPageURL}?byWebRequest=1&url=\\1`,
326+
regexSubstitution: `${installPageURL}?url=\\1`,
327327
},
328328
},
329329
condition: condition,

src/pages/install/useInstallData.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ describe("useInstallData 数据流编排", () => {
251251
});
252252

253253
describe("安装成功后离开安装页:独立新标签应关闭,网页链接接管的原标签应返回上一页", () => {
254-
const setupReady = async (paramOptions: Record<string, unknown> = {}) => {
254+
const setupReady = async () => {
255255
window.history.replaceState({}, "", "/install.html?uuid=u1");
256256
const metadata = { name: ["示例脚本"], version: ["1.0.0"], match: ["https://e.com/*"] };
257257
const info: ScriptInfo = {
@@ -262,7 +262,7 @@ describe("useInstallData 数据流编排", () => {
262262
metadata,
263263
source: "user",
264264
};
265-
(scriptClient.getInstallInfo as Mock).mockResolvedValue([false, info, paramOptions]);
265+
(scriptClient.getInstallInfo as Mock).mockResolvedValue([false, info, {}]);
266266
(getTempCode as Mock).mockResolvedValue("// code");
267267
(prepareScriptByCode as Mock).mockResolvedValue({ script: makeAction(metadata) });
268268
(scriptClient.install as Mock).mockResolvedValue(undefined);
@@ -271,12 +271,12 @@ describe("useInstallData 数据流编排", () => {
271271
return result;
272272
};
273273

274-
it("独立新标签即使 history.length > 1 也应使用 window.close() 关闭", async () => {
274+
it("独立新标签 history.length 1 时应使用 window.close() 关闭", async () => {
275275
const result = await setupReady();
276276
const closeSpy = vi.spyOn(window, "close").mockImplementation(() => {});
277277
const backSpy = vi.spyOn(window.history, "back").mockImplementation(() => {});
278278
const removeSpy = vi.spyOn(chrome.tabs, "remove").mockResolvedValue();
279-
vi.spyOn(window.history, "length", "get").mockReturnValue(2);
279+
vi.spyOn(window.history, "length", "get").mockReturnValue(1);
280280

281281
await act(async () => {
282282
await result.current.install();
@@ -289,8 +289,8 @@ describe("useInstallData 数据流编排", () => {
289289
expect(backSpy).not.toHaveBeenCalled();
290290
});
291291

292-
it("byWebRequest 且 history.length > 1 时应返回上一页而非关闭用户标签", async () => {
293-
const result = await setupReady({ byWebRequest: true });
292+
it("history.length > 1 时应返回上一页而非关闭用户标签", async () => {
293+
const result = await setupReady();
294294
const closeSpy = vi.spyOn(window, "close").mockImplementation(() => {});
295295
const backSpy = vi.spyOn(window.history, "back").mockImplementation(() => {});
296296
vi.spyOn(window.history, "length", "get").mockReturnValue(2);
@@ -305,8 +305,8 @@ describe("useInstallData 数据流编排", () => {
305305
expect(closeSpy).not.toHaveBeenCalled();
306306
});
307307

308-
it("byWebRequest 但 history.length 为 1 时应关闭无处可退的标签", async () => {
309-
const result = await setupReady({ byWebRequest: true });
308+
it("history.length 为 1 时应关闭无处可退的标签", async () => {
309+
const result = await setupReady();
310310
const closeSpy = vi.spyOn(window, "close").mockImplementation(() => {});
311311
const backSpy = vi.spyOn(window.history, "back").mockImplementation(() => {});
312312
vi.spyOn(window.history, "length", "get").mockReturnValue(1);

src/pages/install/useInstallData.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -124,19 +124,19 @@ const buildScriptInfo = (uuid: string, code: string, url: string, metadata: SCMe
124124
source: "user",
125125
});
126126

127-
// 安装页可能是专为安装打开的新标签,也可能由 declarativeNetRequest 接管用户原标签。
128-
// 独立新标签可能继承多条历史,DNR 入口也可能没有上一页,因此必须同时检查入口与历史栈:
129-
// 仅在 DNR 接管且确实有历史可退时返回,否则关闭当前独立安装标签
127+
// 安装页可能是 ScriptCat 新建的独立标签,也可能由 declarativeNetRequest 接管用户原标签。
128+
// 前者由 chrome.tabs.create 创建且没有可返回的安装历史,后者才可能有上一页;
129+
// 因此只需用 history.length 区分返回与关闭,不要让入口标记承担第二种语义
130130
// install()/close() 等可能在短时间内被重复触发(如用户连续点击、close 与 install 的
131131
// setTimeout 前后脚打到),leaveInstallPageRunning 防止 back()/close() 被并发调用多次;
132132
// 推到 requestAnimationFrame 里执行,让触发它的那次交互(如按钮点击态)先完成一帧渲染。
133133
let leaveInstallPageRunning = false;
134-
const leaveInstallPage = (byWebRequest: boolean) => {
134+
const leaveInstallPage = () => {
135135
if (leaveInstallPageRunning) return;
136136
leaveInstallPageRunning = true;
137137
requestAnimationFrame(() => {
138138
leaveInstallPageRunning = false;
139-
if (byWebRequest && window.history.length > 1) {
139+
if (window.history.length > 1) {
140140
window.history.back();
141141
} else {
142142
window.close();
@@ -190,7 +190,6 @@ export function useInstallData(): UseInstallData {
190190
const infoRef = useRef<ScriptInfo | null>(null);
191191
const handleRef = useRef<FileSystemFileHandle | null>(null);
192192
const skillUuidRef = useRef<string | null>(null);
193-
const byWebRequestRef = useRef(false);
194193

195194
useEffect(() => {
196195
const params = new URLSearchParams(location.search);
@@ -200,7 +199,6 @@ export function useInstallData(): UseInstallData {
200199
const fid = params.get("file");
201200
const urlIdx = location.search.indexOf("url=");
202201
const rawUrl = !uuid && urlIdx !== -1 ? location.search.slice(urlIdx + 4) : null;
203-
byWebRequestRef.current = params.get("byWebRequest") === "1";
204202
let cancelled = false;
205203

206204
const failed = (e: unknown) => {
@@ -266,7 +264,6 @@ export function useInstallData(): UseInstallData {
266264
const code = await getTempCode(uuid);
267265
if (code === undefined) throw new Error(t("install:script_info_load_failed"));
268266
info.code = code;
269-
byWebRequestRef.current = cached?.[2]?.byWebRequest === true;
270267
await loadFromInfo(info, !!cached?.[0], cached?.[2] || {});
271268
} else if (rawUrl) {
272269
// .cat.md URL → Skill 安装流程(DNR 把 *.cat.md 重定向到安装页),不走脚本解析;仅 agent 启用时
@@ -369,7 +366,7 @@ export function useInstallData(): UseInstallData {
369366
await scriptClient.install({ script, code: info.code });
370367
notify.success(t("install:success"));
371368
}
372-
if (closeAfterInstall) setTimeout(() => leaveInstallPage(byWebRequestRef.current), 300);
369+
if (closeAfterInstall) setTimeout(() => leaveInstallPage(), 300);
373370
} catch (e) {
374371
notify.error(`${t("install:failed")}: ${(e as Error)?.message || String(e)}`);
375372
}
@@ -393,7 +390,7 @@ export function useInstallData(): UseInstallData {
393390
if (opts?.noMoreUpdates && info && !info.userSubscribe) {
394391
void scriptClient.setCheckUpdateUrl(info.uuid, false);
395392
}
396-
leaveInstallPage(byWebRequestRef.current);
393+
leaveInstallPage();
397394
}, []);
398395

399396
// 监听文件变更后自动重装,并刷新视图代码
@@ -450,7 +447,7 @@ export function useInstallData(): UseInstallData {
450447
try {
451448
await agentClient.completeSkillInstall(uuid);
452449
notify.success(t("install:success"));
453-
setTimeout(() => leaveInstallPage(byWebRequestRef.current), 300);
450+
setTimeout(() => leaveInstallPage(), 300);
454451
} catch (e) {
455452
notify.error(`${t("install:failed")}: ${(e as Error)?.message || String(e)}`);
456453
}
@@ -459,7 +456,7 @@ export function useInstallData(): UseInstallData {
459456
const cancelSkill = useCallback(() => {
460457
const uuid = skillUuidRef.current;
461458
if (uuid) void agentClient.cancelSkillInstall(uuid);
462-
leaveInstallPage(byWebRequestRef.current);
459+
leaveInstallPage();
463460
}, []);
464461

465462
// 重新触发加载(供加载失败后的重试按钮)

0 commit comments

Comments
 (0)