Native page bounds in the built-in browser
The Electron shell DOM toolbar and native WebContentsView must maintain the same page bounds at any shell zoom factor.
Verified gaps
At a shell zoom factor of approximately 1.095 , the renderer sends x=812.63, width=518.64 CSS px, which the main process uses directly as DIP. Correct native bounds should be approximately x=890, width=568. The webpage consequently covers the left chat pane and leaves blank space on the right.
The old occlusion check hides every native Web pane whenever any role=dialog exists in the document. Local working-directory, project and permission menus at the bottom blank the neighboring webpage even when entirely inside the chat pane. Meanwhile, role=menu, role=listbox and several custom fullscreen confirmation overlays are not covered by the same rule.
Unified overlay rules for multiple Web panes
| Overlay type | Detection | Native Web behavior |
|---|---|---|
| Local Popover, Dropdown, listbox and tab/split menus | role=dialog/menu/listbox | Calculate rectangles per Web body; hide only panes that actually intersect. |
| Fullscreen modals, confirmation backdrops and attachment previews | data-native-view-occluder=true or the existing merge backdrop | The fullscreen rectangle intersects each Web body, hiding every covered pane. |
| Tooltips, toasts, drag cues and ordinary portaled content | Excluded from the occlusion selector | Transient hints do not blank the whole webpage. |
| Desktop main/context/bookmark native menus | Separate overlay owner in the main process | Retain native-menu lifecycle management; do not manage it again through renderer DOM. |
Multiple split Web panes do not share a global visible flag. Each WebTabPane uses its own body bounds against the same set of current overlays. If a menu covers only the left Web pane, the right pane stays visible. Agent picture-in-picture previews reuse the same registerVisibleWebTabBounds / syncVisible path and publish only the small-window body rectangle, keeping toolbar buttons and the lower-right resize handle outside the native view. Existing MutationObserver, ResizeObserver and scroll/resize listeners republish bounds after overlays close, move or resize. During small-window dragging/resizing the renderer throttles calls to syncVisible to 100ms and publishes immediately on pointer release, avoiding per-frame IPC. Global flush still batches microtasks within the same tick.
Design
- The renderer continues sending CSS px measured by
getBoundingClientRect(). - Only at the renderer IPC boundary does the main process read
event.sender.getZoomFactor(), convert CSS px into native DIP, then apply existing integer rounding and nonnegative dimension constraints. sync-visibleand the compatibility entryset-boundsuse the same conversion. Internal show/hide/transfer paths already using DIP are not scaled again.- Shell zoom changes trigger the existing renderer
resizelistener to remeasure and send current bounds. The main process reads the current zoom factor on every IPC call. - Invalid or nonpositive zoom factors are treated as
1. - A DOM overlay occludes the native
WebContentsViewonly when its visible rectangle intersects the current Web body. Working-directory, project and permission menus inside the chat pane must not hide pages in neighboring splits. Dialogs, attachment previews and merge backdrops covering a webpage must still hide the native view; existing bounds observers restore it on close.
Boundaries and exclusions
Do not forcibly reset user zoom or change split ratios, DOM layout, preload APIs, site zoom, Browser Agent, dependencies or configuration. Electron WebContentsView.setBounds takes DIP while renderer DOM measurements use CSS px. The renderer retains measurement precision; the main process converts units and performs final rounding.
Task files and acceptance
Production code: apps/desktop/main.js, apps/web/components/center-tabs/web-tab-pane.tsx and apps/web/lib/browser/web-tab-bounds.ts. Public gates: apps/desktop/scripts/check-webtab-zoom-bounds.js and existing web split gates.
- Automation covers 100% zoom, approximately 109.5% zoom, invalid zoom factors, both IPC entry points and existing desktop regressions.
- The installed App uses the default worker on 18100. At non-100% shell zoom, verify matching width and height between the DOM body and native viewport; check splits, split-ratio changes and window resizing.
- Visual acceptance uses only one current viewport screenshot, without OCR, candidate boxes or repeated crops.
- Public regressions must verify: a working-directory/project menu left of a nonintersecting right Web body leaves it visible; a menu/listbox intersecting one Web body hides only that pane; a fullscreen modal backdrop hides every intersecting pane; edge contact without overlapping area leaves it visible.
Implementation evidence
- Complete desktop and web checks passed. Dynamic renderer tests retain fractional DOMRect values and verify
syncVisiblepayload. check:web-splitruns rectangle-intersection regressions: the left working-directory/project menu does not hide the right WebTab;dialog/menu/listbox, intersecting overlays and fullscreen dialog backdrops are evaluated separately for each Web body. Zero area and edge-only contact do not hide panes. GeneralDialogOverlay, permission confirmations, context click-catchers and scoped drop overlays enter the same check throughdata-native-view-occluder.- Final specification and quality reviews both returned
PASS; no product CSS or existing menu dimensions, positions, radii or interaction structure changed. - At approximately 109.5% shell zoom in the installed App, converted DOM-body dimensions and native-viewport dimensions are both
568×763 DIP. - After resizing the window to
1350×800, converted DOM-body dimensions and native-viewport dimensions are both526×712 DIP; they still match after restoring the window. - The default worker health check on 18100 passed; CDP 9223 observed both the shell and the current native Bilibili page.
Implementation status
Unit conversion, automated gates and installed-App acceptance are complete.