Skip to content
Merged
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
62 changes: 32 additions & 30 deletions apps/desktop/src/desktop-browser-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ interface NativeTabRef extends NativeTabScope {
}

interface BrowserViewEntry {
webContents: WebContents;
view: WebContentsView;
hostWindow: DesktopBrowserHostWindow;
threadId: string;
Expand Down Expand Up @@ -354,7 +355,7 @@ function buildBrowserState(
tabId: string,
entry: BrowserViewEntry,
): BbDesktopBrowserState {
const webContents = entry.view.webContents;
const webContents = entry.webContents;
const url = webContents.getURL();
const rawTitle = webContents.getTitle();
const title = rawTitle.length > 0 && rawTitle !== url ? rawTitle : null;
Expand Down Expand Up @@ -403,7 +404,7 @@ export function createDesktopBrowserViewManager(
entry: BrowserViewEntry,
hostWindow: DesktopBrowserHostWindow,
): void {
if (entry.view.webContents.isDestroyed()) {
if (entry.webContents.isDestroyed()) {
return;
}
entry.view.setVisible(
Expand Down Expand Up @@ -446,7 +447,7 @@ export function createDesktopBrowserViewManager(
}
entry.rendererRecoveryTimer = setTimeout(() => {
entry.rendererRecoveryTimer = null;
const webContents = entry.view.webContents;
const webContents = entry.webContents;
if (
webContents.isDestroyed() ||
entry.rendererRecoveryState !== "pending" ||
Expand All @@ -470,7 +471,7 @@ export function createDesktopBrowserViewManager(
const hideCap = setTimeout(() => {
applyEntryVisibility(entry, hostWindow);
}, RESIZE_SNAPSHOT_HIDE_CAP_MS);
entry.view.webContents
entry.webContents
.capturePage()
.then((image) => {
if (!isHostResizing(hostWindow) || image.isEmpty()) {
Expand Down Expand Up @@ -521,7 +522,7 @@ export function createDesktopBrowserViewManager(
tabId: string,
): void {
const entry = entries.get(browserViewKey(hostWindow, tabId));
if (!entry || entry.view.webContents.isDestroyed()) {
if (!entry || entry.webContents.isDestroyed()) {
return;
}
send(
Expand Down Expand Up @@ -601,7 +602,7 @@ export function createDesktopBrowserViewManager(
tabId: string,
entry: BrowserViewEntry,
): void {
const webContents = entry.view.webContents;
const webContents = entry.webContents;

webContents.on("destroyed", () => {
const key = browserViewKey(hostWindow, tabId);
Expand Down Expand Up @@ -792,6 +793,7 @@ export function createDesktopBrowserViewManager(
});
const entry: BrowserViewEntry = {
view,
webContents: view.webContents,
hostWindow: args.hostWindow,
threadId: args.threadId,
generation: randomUUID(),
Expand All @@ -818,14 +820,14 @@ export function createDesktopBrowserViewManager(
if (url.length === 0) {
return;
}
if (entry.view.webContents.getURL() === url) {
if (entry.webContents.getURL() === url) {
return;
}
if (!isAllowedBrowserUrl(url)) {
return;
}
entry.lastErrorText = null;
entry.view.webContents.loadURL(url).catch(() => {});
entry.webContents.loadURL(url).catch(() => {});
}

function disposeEntry(key: string, entry: BrowserViewEntry): void {
Expand All @@ -835,8 +837,8 @@ export function createDesktopBrowserViewManager(
if (!popupWindow.isDestroyed()) popupWindow.destroy();
}
entry.popupWindows.clear();
if (!entry.view.webContents.isDestroyed()) {
entry.view.webContents.close();
if (!entry.webContents.isDestroyed()) {
entry.webContents.close();
}
notifyAutomationTabs();
}
Expand All @@ -860,7 +862,7 @@ export function createDesktopBrowserViewManager(
fn: (entry: BrowserViewEntry) => void,
): void {
const entry = entries.get(browserViewKey(args.hostWindow, args.tabId));
if (!entry || entry.view.webContents.isDestroyed()) {
if (!entry || entry.webContents.isDestroyed()) {
return;
}
fn(entry);
Expand All @@ -872,7 +874,7 @@ export function createDesktopBrowserViewManager(
entry === undefined ||
entry.threadId !== ref.threadId ||
entry.generation !== ref.generation ||
entry.view.webContents.isDestroyed()
entry.webContents.isDestroyed()
) {
throw new Error("Native browser tab is unavailable or has been replaced");
}
Expand Down Expand Up @@ -908,7 +910,7 @@ export function createDesktopBrowserViewManager(

function focusEntryWithoutNotifying(entry: BrowserViewEntry): void {
entry.suppressNextFocusNotification = true;
entry.view.webContents.focus();
entry.webContents.focus();
setTimeout(() => {
entry.suppressNextFocusNotification = false;
}, 0);
Expand All @@ -931,7 +933,7 @@ export function createDesktopBrowserViewManager(
request.visible &&
!wasVisible &&
!hasOtherVisibleEntry(hostWindow, request.tabId) &&
!entry.view.webContents.isDestroyed()
!entry.webContents.isDestroyed()
) {
focusEntryWithoutNotifying(entry);
}
Expand Down Expand Up @@ -969,7 +971,7 @@ export function createDesktopBrowserViewManager(
if (
key.startsWith(prefix) &&
(threadId === null || entry.threadId === threadId) &&
!entry.view.webContents.isDestroyed()
!entry.webContents.isDestroyed()
) {
tabs.push(nativeTab(key.slice(prefix.length), entry));
}
Expand Down Expand Up @@ -998,7 +1000,7 @@ export function createDesktopBrowserViewManager(
) {
throw new Error("Invalid browser capture dimensions or quality");
}
const image = await captureDesktopBrowserPage(entry.view.webContents);
const image = await captureDesktopBrowserPage(entry.webContents);
requireNativeEntry(request);
if (image.isEmpty()) throw new Error("Native browser capture is empty");
const size = image.getSize();
Expand Down Expand Up @@ -1026,11 +1028,11 @@ export function createDesktopBrowserViewManager(
if (
key.startsWith(prefix) &&
entry.threadId === threadId &&
!entry.view.webContents.isDestroyed()
!entry.webContents.isDestroyed()
) {
tabs.push({
tabId: key.slice(prefix.length),
webContents: entry.view.webContents,
webContents: entry.webContents,
});
}
}
Expand Down Expand Up @@ -1066,7 +1068,7 @@ export function createDesktopBrowserViewManager(
request.visible &&
!wasVisible &&
!hasOtherVisibleEntry(hostWindow, request.tabId) &&
!entry.view.webContents.isDestroyed()
!entry.webContents.isDestroyed()
) {
focusEntryWithoutNotifying(entry);
}
Expand All @@ -1091,32 +1093,32 @@ export function createDesktopBrowserViewManager(
},
goBack({ hostWindow, tabId }) {
withEntry({ hostWindow, tabId }, (entry) => {
if (entry.view.webContents.navigationHistory.canGoBack()) {
if (entry.webContents.navigationHistory.canGoBack()) {
resetEntryRendererRecovery(entry);
applyEntryVisibility(entry, hostWindow);
entry.view.webContents.navigationHistory.goBack();
entry.webContents.navigationHistory.goBack();
}
});
},
goForward({ hostWindow, tabId }) {
withEntry({ hostWindow, tabId }, (entry) => {
if (entry.view.webContents.navigationHistory.canGoForward()) {
if (entry.webContents.navigationHistory.canGoForward()) {
resetEntryRendererRecovery(entry);
applyEntryVisibility(entry, hostWindow);
entry.view.webContents.navigationHistory.goForward();
entry.webContents.navigationHistory.goForward();
}
});
},
reload({ hostWindow, tabId }) {
withEntry({ hostWindow, tabId }, (entry) => {
resetEntryRendererRecovery(entry);
entry.view.webContents.reload();
entry.webContents.reload();
applyEntryVisibility(entry, hostWindow);
});
},
stop({ hostWindow, tabId }) {
withEntry({ hostWindow, tabId }, (entry) => {
entry.view.webContents.stop();
entry.webContents.stop();
});
},
setBounds({ hostWindow, request }) {
Expand All @@ -1126,7 +1128,7 @@ export function createDesktopBrowserViewManager(
},
findInPage({ hostWindow, request }) {
withEntry({ hostWindow, tabId: request.tabId }, (entry) => {
entry.activeFindRequestId = entry.view.webContents.findInPage(
entry.activeFindRequestId = entry.webContents.findInPage(
request.text,
{
forward: request.forward,
Expand All @@ -1138,7 +1140,7 @@ export function createDesktopBrowserViewManager(
stopFindInPage({ hostWindow, request }) {
withEntry({ hostWindow, tabId: request.tabId }, (entry) => {
entry.activeFindRequestId = null;
entry.view.webContents.stopFindInPage(request.action);
entry.webContents.stopFindInPage(request.action);
});
},
setVisible({ hostWindow, request }) {
Expand All @@ -1154,7 +1156,7 @@ export function createDesktopBrowserViewManager(
resizingHostIds.add(hostWindow.webContents.id);
const prefix = `${hostWindow.webContents.id}:`;
for (const [key, entry] of entries.entries()) {
if (!key.startsWith(prefix) || entry.view.webContents.isDestroyed()) {
if (!key.startsWith(prefix) || entry.webContents.isDestroyed()) {
continue;
}
if (entry.visible) {
Expand All @@ -1169,7 +1171,7 @@ export function createDesktopBrowserViewManager(
resizingHostIds.delete(hostWindow.webContents.id);
const prefix = `${hostWindow.webContents.id}:`;
for (const [key, entry] of entries.entries()) {
if (!key.startsWith(prefix) || entry.view.webContents.isDestroyed()) {
if (!key.startsWith(prefix) || entry.webContents.isDestroyed()) {
continue;
}
if (entry.visible) {
Expand All @@ -1186,7 +1188,7 @@ export function createDesktopBrowserViewManager(
resizingHostIds.delete(hostWindow.webContents.id);
const prefix = `${hostWindow.webContents.id}:`;
for (const [key, entry] of entries.entries()) {
if (!key.startsWith(prefix) || entry.view.webContents.isDestroyed()) {
if (!key.startsWith(prefix) || entry.webContents.isDestroyed()) {
continue;
}
entry.visible = false;
Expand Down
23 changes: 23 additions & 0 deletions apps/desktop/test/desktop-browser-view-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2131,6 +2131,29 @@ describe("DesktopBrowserViewManager", () => {
expect(snapshots).toHaveLength(4);
});

it.each(["visibility", "destroyed", "detach"] as const)(
"tolerates a missing guest during %s",
(operation) => {
const { manager, hostWindow, view } = createRendererRecoveryFixture(94);
const guest = view.webContents;
if (operation !== "destroyed") guest.destroyed = true;
Object.defineProperty(view, "webContents", { get: () => undefined });
expect(() => {
if (operation === "visibility") {
manager.setVisible({
hostWindow,
request: { tabId: "browser:a", visible: false },
});
} else if (operation === "destroyed") {
guest.close();
} else {
manager.detach({ hostWindow, tabId: "browser:a" });
}
}).not.toThrow();
manager.destroyAll();
},
);

it.each(["detach", "releaseWindow", "destroyAll", "destroyed"] as const)(
"notifies once after removing a native target through %s",
(operation) => {
Expand Down
Loading