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
20 changes: 20 additions & 0 deletions src/pages/install/context_fallback.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { describe, expect, it } from "vitest";
import { DocumentationSite } from "@App/app/const";
import { getIncompleteContextFallbackUrl } from "./context_fallback";

describe("getIncompleteContextFallbackUrl", () => {
it("扩展 API 不完整的 ?url= 安装页转到文档站安装中间页,脚本地址原样带上(含其自身的查询串)", () => {
const scriptUrl = "https://example.com/a.user.js?v=1&lang=zh";
expect(getIncompleteContextFallbackUrl(`?url=${scriptUrl}`, false)).toBe(
`${DocumentationSite}/docs/script_installation/#url=${scriptUrl}`
);
});

it("扩展 API 完整时不转跳,正常浏览器照常在本页安装", () => {
expect(getIncompleteContextFallbackUrl("?url=https://example.com/a.user.js", true)).toBeUndefined();
});

it("没有 ?url= 的入口(如 SW 新建的 ?uuid=)不转跳", () => {
expect(getIncompleteContextFallbackUrl("?uuid=8f3c2a10-0000-4000-8000-000000000000", false)).toBeUndefined();
});
});
18 changes: 18 additions & 0 deletions src/pages/install/context_fallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { DocumentationSite } from "@App/app/const";

/**
* 浏览器兼容处理(#1816):360 X 中,网页以 noopener 新开的标签被 DNR 重定向到安装页后,页面只拿到
* 不完整的扩展 API(chrome.tabs / chrome.permissions 缺失,读 chrome.storage 报
* "Invalid context type provided.")。浏览器为何这样分类尚未确认,在本页刷新或原标签重新导航都恢复不了,
* 由 SW 用 tabs.create 新开的安装页才正常。
*
* 因此这种上下文改为转到文档站的安装中间页:SW 的 webNavigation 监听会接住这次导航,备好脚本后新开
* `?uuid=` 安装页。只有 DNR 产生的 `?url=` 入口会转跳,SW 新建的入口不会再落回这里,不会循环。
* `url=` 之后的整段原样转交,与安装页读取 `?url=` 的方式一致,脚本地址自带的查询串不会被截断。
*/
export function getIncompleteContextFallbackUrl(search: string, hasTabsApi: boolean): string | undefined {
if (hasTabsApi) return undefined;
const urlIdx = search.indexOf("url=");
if (urlIdx === -1) return undefined;
return `${DocumentationSite}/docs/script_installation/#url=${search.slice(urlIdx + 4)}`;
}
12 changes: 9 additions & 3 deletions src/pages/install/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { message } from "../store/global.ts";
import MessageWriter from "@App/app/logger/message_writer.ts";
import { ThemeProvider } from "../components/theme-provider.tsx";
import { Toaster } from "../components/ui/sonner.tsx";
import { getIncompleteContextFallbackUrl } from "./context_fallback.ts";
import "@App/index.css";

// 初始化日志组件
Expand All @@ -23,6 +24,11 @@ const Root = (
</ThemeProvider>
);

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
process.env.NODE_ENV === "development" ? <React.StrictMode>{Root}</React.StrictMode> : Root
);
const fallbackUrl = getIncompleteContextFallbackUrl(location.search, typeof chrome.tabs !== "undefined");
if (fallbackUrl) {
location.replace(fallbackUrl);
} else {
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
process.env.NODE_ENV === "development" ? <React.StrictMode>{Root}</React.StrictMode> : Root
);
}
Loading