Skip to content

Fix window opening larger than the screen under display scaling - #694

Open
pratyush618 wants to merge 4 commits into
daijro:mainfrom
pratyush618:fix/425-display-scaling
Open

Fix window opening larger than the screen under display scaling#694
pratyush618 wants to merge 4 commits into
daijro:mainfrom
pratyush618:fix/425-display-scaling

Conversation

@pratyush618

Copy link
Copy Markdown
Contributor

Fixes #425.

Stacked on #674. Only the last two commits belong to this PR:
Probe the host monitor in CSS pixels and Apply screen constraints on Windows and macOS.
The first two are #674's branch, which this builds on to avoid duplicating its clamp.
Rebase onto main once #674 lands.

Problem

With Windows display scaling enabled (150% / 200%), the browser window opens larger than the monitor.

Cause

Two independent defects, both in get_screen_cons()'s contract.

1. The bound is computed in the wrong unit.

screeninfo's Windows enumerator calls SetProcessDpiAwareness(2) before enumerating, explicitly so that it reports physical pixels:

# Make the process DPI aware so it will detect the actual
# resolution and not a virtualized resolution reported by
# Windows when DPI virtualization is in use.
ctypes.windll.shcore.SetProcessDpiAwareness(2)

Firefox lays windows out in CSS pixels. Nothing in this repo pins layout.css.devPixelsPerPx, so it stays at -1 and tracks the system scale factor. browser-init.patch then calls window.resizeTo(outerWidth, outerHeight) with a value the Python layer derived from physical pixels.

At 150% on a 1920x1080 panel the CSS screen is 1280x720, so a fingerprint bounded at "1920x1080" produces a window of 2880x1620 device pixels — 1.5x the screen on both axes.

2. The bound is never applied on Windows or macOS.

screen_cons = screen or get_screen_cons(headless or 'DISPLAY' in env)

DISPLAY is X11-only. On Windows and macOS it is never set, so headful runs took the headless is False early return and generated with no monitor bound at all — the scaling mismatch above then applied to whatever BrowserForge picked, up to 2560x1440.

Change

camoufox/display.py (new) — probes the host monitor in CSS pixels.

  • largest_display() returns a DisplaySize in CSS px. On Windows it divides the physical size by the monitor's effective DPI (shcore!GetDpiForMonitor via MonitorFromPoint); macOS (NSScreen.frame) and X11 already report CSS px, so scaling is Windows-only.
  • Degrades to 1.0 whenever the DPI cannot be read (pre-Windows 8.1, shcore missing, non-zero HRESULT), so behaviour is unchanged where it does not apply.
  • Uses private ctypes.WinDLL handles rather than the process-wide ctypes.windll cache, so annotating the prototypes cannot leak into other libraries in the same process.
  • has_display(env) replaces the 'DISPLAY' in env probe: always true off Linux, DISPLAY or WAYLAND_DISPLAY on Linux.

get_screen_cons() keeps its signature and now returns CSS-pixel bounds.

Scope

Untouched: headless (no window to overflow), headless='virtual' (excluded by #674's gate), any caller passing screen= or window=, and any host whose display cannot be probed.

Windows without scaling is also unaffected — GetDpiForMonitor returns 96 DPI, the scale factor is 1.0, and the bound is the physical size as before.

Tests

pythonlib/tests/test_display.py (new) — the DPI conversion table (96/120/144/192 -> 1.0/1.25/1.5/2.0), the Windows-only guard, both fallback paths, monitor selection, and has_display per platform.

pythonlib/tests/test_launch_geometry.py — adds TestHeadfulFitsOnDisplay: 15 randomised headful launches against a 1280x720 CSS display, asserting no screen.* or window.outer* value exceeds it, plus the geometry invariants and the unprobeable-host case. Asserts the outcome rather than the constraint, since BrowserForge silently drops constraints it cannot satisfy.

Full pythonlib suite: 89 passed. The 2 test_virtdisplay.py failures are pre-existing (no Xvfb on this host) and reproduce unchanged on a clean tree.

Not addressed

Launching the binary directly, without the Python package, still uses the hardcoded window.resizeTo(1280, 1040) in browser-init.patch, which can overflow a small scaled display. That needs a patch regeneration through make edits.

get_screen_cons() bounds the generated fingerprint to the monitor, but
BrowserForge honours a Screen constraint only when its pool has a match:
FingerprintGenerator.partial_csp catches the filtering failure and deletes the
constraint unless strict=True. So a 1366x768 laptop routinely gets a 2560x1440
fingerprint with window.outerWidth 1920, and browser-init resizes the real
chrome window to it -- rendering past the edge of the monitor.

Re-apply the bound after generation instead of trusting BrowserForge with it,
and pull screenX/screenY back inside the shrunken screen.

Headful only. headless has no window to overflow, and headless='virtual' runs a
1x1 Xvfb whose "monitor" would otherwise shrink the fingerprint to 1x1.

Fixes daijro#499
headless='virtual' reaches launch_options as headless=False with
virtual_display set (async_api rewrites it), so the headful gate fired and
clamped the fingerprint to Xvfb's 1x1 stub. fix_screen_no_taskbar then drove
availHeight to -39 and validate_config rejected the launch outright.
screeninfo makes the process per-monitor DPI aware, so it reports physical
pixels, while Firefox lays windows out in CSS pixels. At 150% Windows scaling a
1920x1080 panel is 1280x720 CSS px, so bounding the fingerprint by the physical
size lets the window open 1.5x larger than the screen.

Refs daijro#425
get_screen_cons() was gated on DISPLAY being set, which only ever happens on
Linux, so headful runs on Windows and macOS generated fingerprints with no
monitor bound at all.

Fixes daijro#425
@DemonMartin

Copy link
Copy Markdown

When will this be shipped? I was forced to do a very hacky fix myself for now but it was causing flags with a lot of captchas

@JWriter20 JWriter20 mentioned this pull request Jul 30, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Window opens larger than screen with display scaling enabled

2 participants