Open browser links in workspace tabs (#1375)

* Open browser window requests as workspace tabs

* Tighten browser new-tab request boundaries
This commit is contained in:
Mohamed Boudra
2026-06-06 19:30:21 +08:00
committed by GitHub
parent fd022bc44b
commit 9a8912b3ef
9 changed files with 299 additions and 54 deletions
@@ -0,0 +1,54 @@
import { describe, expect, it } from "vitest";
import type { WorkspaceLayout } from "@/stores/workspace-layout-store";
import { createDefaultLayout } from "@/stores/workspace-layout-store";
import { openTabInLayoutFocused } from "@/stores/workspace-layout-actions";
import { resolveBrowserNewTabRequest, type BrowserNewTabRequest } from ".";
function createLayoutWithBrowser(browserId: string): WorkspaceLayout {
return openTabInLayoutFocused({
layout: createDefaultLayout(),
target: { kind: "browser", browserId },
now: 1,
}).layout;
}
describe("browser new-tab requests", () => {
it("accepts desktop requests from browser tabs in the current workspace", () => {
const request = resolveBrowserNewTabRequest({
payload: {
sourceBrowserId: "browser-1",
url: "https://example.com/target",
},
workspaceLayout: createLayoutWithBrowser("browser-1"),
});
expect(request).toEqual<BrowserNewTabRequest>({
sourceBrowserId: "browser-1",
url: "https://example.com/target",
});
});
it("ignores desktop requests from another workspace", () => {
const request = resolveBrowserNewTabRequest({
payload: {
sourceBrowserId: "browser-from-other-workspace",
url: "https://example.com/target",
},
workspaceLayout: createLayoutWithBrowser("browser-1"),
});
expect(request).toBeNull();
});
it("rejects unsupported desktop request URLs", () => {
const request = resolveBrowserNewTabRequest({
payload: {
sourceBrowserId: "browser-1",
url: "file:///etc/passwd",
},
workspaceLayout: createLayoutWithBrowser("browser-1"),
});
expect(request).toBeNull();
});
});
@@ -0,0 +1,99 @@
import { useEffect } from "react";
import { getDesktopHost, type DesktopBrowserNewTabRequestEvent } from "@/desktop/host";
import { collectAllTabs, type WorkspaceLayout } from "@/stores/workspace-layout-store";
import { getIsElectron } from "@/constants/platform";
import { useStableEvent } from "@/hooks/use-stable-event";
export type BrowserNewTabRequest = DesktopBrowserNewTabRequestEvent;
function isAllowedBrowserNewTabUrl(value: string): boolean {
try {
const parsed = new URL(value);
return (
parsed.protocol === "http:" || parsed.protocol === "https:" || parsed.href === "about:blank"
);
} catch {
return false;
}
}
function readDesktopBrowserNewTabRequest(payload: unknown): BrowserNewTabRequest | null {
if (!payload || typeof payload !== "object") {
return null;
}
const candidate = payload as Partial<BrowserNewTabRequest>;
if (typeof candidate.sourceBrowserId !== "string" || !candidate.sourceBrowserId.trim()) {
return null;
}
if (typeof candidate.url !== "string" || !isAllowedBrowserNewTabUrl(candidate.url)) {
return null;
}
return {
sourceBrowserId: candidate.sourceBrowserId,
url: candidate.url,
};
}
function workspaceContainsBrowser(input: {
workspaceLayout: WorkspaceLayout | null | undefined;
browserId: string;
}): boolean {
if (!input.workspaceLayout) {
return false;
}
return collectAllTabs(input.workspaceLayout.root).some((tab) => {
return tab.target.kind === "browser" && tab.target.browserId === input.browserId;
});
}
export function resolveBrowserNewTabRequest(input: {
payload: unknown;
workspaceLayout: WorkspaceLayout | null | undefined;
}): BrowserNewTabRequest | null {
const request = readDesktopBrowserNewTabRequest(input.payload);
if (!request) {
return null;
}
if (
!workspaceContainsBrowser({
workspaceLayout: input.workspaceLayout,
browserId: request.sourceBrowserId,
})
) {
return null;
}
return request;
}
export function useDesktopBrowserNewTabRequests(input: {
enabled: boolean;
workspaceLayout: WorkspaceLayout | null | undefined;
openUrl: (url: string) => void;
}): void {
const handleNewTabRequest = useStableEvent((payload: unknown) => {
const request = resolveBrowserNewTabRequest({
payload,
workspaceLayout: input.workspaceLayout,
});
if (!request) {
return;
}
input.openUrl(request.url);
});
useEffect(() => {
if (!input.enabled || !getIsElectron()) {
return;
}
const unsubscribe = getDesktopHost()?.events?.on?.(
"browser-new-tab-request",
handleNewTabRequest,
);
if (typeof unsubscribe === "function") {
return unsubscribe;
}
return () => {
void unsubscribe?.then((dispose) => dispose());
};
}, [handleNewTabRequest, input.enabled]);
}