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
31 changes: 29 additions & 2 deletions apps/desktop/src/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
type BbDesktopBrowserSnapshotHandler,
type BbDesktopBrowserStateHandler,
type BbDesktopBrowserUnsubscribe,
type BbDesktopBrowserViewBounds,
type BbDesktopCloseWindowRequestHandler,
type BbDesktopInfo,
type BbDesktopInfoChangeHandler,
Expand Down Expand Up @@ -188,9 +189,32 @@ const bbSpellcheckApi: BbDesktopSpellcheckApi = {
},
};

function browserViewBoundsAtWindowScale(
bounds: BbDesktopBrowserViewBounds,
): BbDesktopBrowserViewBounds {
const zoomFactor = webFrame.getZoomFactor();
if (zoomFactor === 1) {
return bounds;
}
const x = Math.round(bounds.x * zoomFactor);
const y = Math.round(bounds.y * zoomFactor);
return {
x,
y,
width: Math.max(0, Math.round((bounds.x + bounds.width) * zoomFactor) - x),
height: Math.max(
0,
Math.round((bounds.y + bounds.height) * zoomFactor) - y,
),
};
}

const bbBrowserApi: BbDesktopBrowserApi = {
attach(request): void {
ipcRenderer.send(BB_DESKTOP_BROWSER_ATTACH_CHANNEL, request);
ipcRenderer.send(BB_DESKTOP_BROWSER_ATTACH_CHANNEL, {
...request,
bounds: browserViewBoundsAtWindowScale(request.bounds),
});
},
detach(tabId): void {
ipcRenderer.send(BB_DESKTOP_BROWSER_DETACH_CHANNEL, { tabId });
Expand All @@ -211,7 +235,10 @@ const bbBrowserApi: BbDesktopBrowserApi = {
ipcRenderer.send(BB_DESKTOP_BROWSER_STOP_CHANNEL, { tabId });
},
setBounds(request): void {
ipcRenderer.send(BB_DESKTOP_BROWSER_SET_BOUNDS_CHANNEL, request);
ipcRenderer.send(BB_DESKTOP_BROWSER_SET_BOUNDS_CHANNEL, {
...request,
bounds: browserViewBoundsAtWindowScale(request.bounds),
});
},
setVisible(request): void {
ipcRenderer.send(BB_DESKTOP_BROWSER_SET_VISIBLE_CHANNEL, request);
Expand Down
43 changes: 43 additions & 0 deletions apps/desktop/test/preload-browser-api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ const electronMock = vi.hoisted(() => {
let exposedSpellcheckApi: {
getCorrectionContext(word: string): unknown;
} | null = null;
let zoomFactor = 1;

return {
get exposedApi() {
Expand All @@ -97,6 +98,10 @@ const electronMock = vi.hoisted(() => {
invokeCalls.length = 0;
listeners.clear();
sendCalls.length = 0;
zoomFactor = 1;
},
setZoomFactor(nextZoomFactor: number): void {
zoomFactor = nextZoomFactor;
},
contextBridge: {
exposeInMainWorld(name: string, api: unknown): void {
Expand Down Expand Up @@ -129,6 +134,9 @@ const electronMock = vi.hoisted(() => {
},
},
webFrame: {
getZoomFactor(): number {
return zoomFactor;
},
getWordSuggestions(word: string): string[] {
return word === "recieve" ? ["receive", "relieve"] : [];
},
Expand Down Expand Up @@ -298,6 +306,41 @@ describe("desktop preload browser API", () => {
);
}, 10_000);

it("converts zoomed renderer bounds to native window coordinates", async () => {
const api = await loadPreload();
electronMock.setZoomFactor(1.25);

api.browser.attach({
tabId: "browser:zoomed",
url: "https://example.com/",
bounds: { x: 800, y: 40, width: 400, height: 600 },
visible: false,
});
api.browser.setBounds({
tabId: "browser:zoomed",
bounds: { x: 801, y: 41, width: 399, height: 599 },
});

expect(electronMock.sendCalls).toEqual([
{
channel: BB_DESKTOP_BROWSER_ATTACH_CHANNEL,
payload: {
tabId: "browser:zoomed",
url: "https://example.com/",
bounds: { x: 1000, y: 50, width: 500, height: 750 },
visible: false,
},
},
{
channel: BB_DESKTOP_BROWSER_SET_BOUNDS_CHANNEL,
payload: {
tabId: "browser:zoomed",
bounds: { x: 1001, y: 51, width: 499, height: 749 },
},
},
]);
});

it("validates browser event payloads before notifying renderer listeners", async () => {
const api = await loadPreload();
const states: BbDesktopBrowserState[] = [];
Expand Down
17 changes: 9 additions & 8 deletions packages/desktop-contract/src/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ export const BB_DESKTOP_BROWSER_MAX_URL_LENGTH = 4096;
export const BB_DESKTOP_BROWSER_MAX_TITLE_LENGTH = 1024;

/**
* Pixel rect (CSS px, which equal device-independent points on macOS) of the
* panel region the native browser view must overlay, measured by the renderer
* against its own layout viewport. This rect is the single placement
* authority: the renderer re-measures and pushes it whenever its layout moves
* the panel, and the desktop main process only intersects it with the live
* window content bounds — it never extrapolates placement from native window
* resizes, whose size the renderer's (possibly lagging) chrome paint does not
* yet reflect.
* Pixel rect of the panel region the native browser view must overlay,
* measured by the renderer against its own layout viewport. The preload
* converts these CSS pixels to native window points at the current page zoom
* before it sends the rect to the desktop main process. This rect is the
* single placement authority: the renderer re-measures and pushes it whenever
* its layout moves the panel, and the desktop main process only intersects it
* with the live window content bounds — it never extrapolates placement from
* native window resizes, whose size the renderer's (possibly lagging) chrome
* paint does not yet reflect.
*/
export const bbDesktopBrowserViewBoundsSchema = z
.object({
Expand Down
Loading