Servo's hit-test silently absorbs notify_input_event on a hidden or
unfocused WebView. Today show()+focus() are called at creation and on
the first-navigate rebuild, but every later sibling-WebView creation
also calls focus() — silently stealing focus from the foreground tab.
load() (later navigates), resize, set_page_zoom, and paint never
re-focus, so a click on the visible tab can land on an unreachable
WebView and disappear.
Move the invariant from a state spread across creation/navigation/
tab-switching into a property of the dispatch path itself: a private
webview_for_input(id) helper re-asserts show()+focus() and returns
the WebView; click/hover/drag/touch_tap/scroll/type_text all go
through it. The cosmetic show/focus calls in create_webview_in_context
and the navigate-rebuild branch stay (first-frame paint), now annotated
to point to webview_for_input as the input-path owner.
Not a complete fix on its own. focus() goes through constellation_proxy
asynchronously (servo crate webview.rs:352), so debug_assert!(focused())
right after focus() would race the constellation — doc comment is the
only guard. Sidecar integration tests pass 9/9 but only exercise
single-WebView sessions; multi-tab focus stealing on Google / YouTube /
Twitter still needs human verification in the live shell. Linus's
critique of the GPUI-side data structure (PerTabSurface Option-as-queue
+ Ensure bundling config+input+frame) is a separate layer untouched
by this change.
Pre-existing tests/software_host.rs::manages_real_servo_webview_lifecycle
already times out on https://servo.org/ with state=Complete but
has_pending_frame=false on b8795bf without this change, so unrelated.
Root cause: Servo's WebView is hidden+unfocused by default. notify_input_event
on a hidden WebView runs paint() hit-test, which returns no hit, and Servo
silently absorbs the event as "already handled". Eleven prior commits all
patched the GPUI side of input forwarding while every event landed in
exactly that black hole.
Fix: webview.show() + webview.focus() immediately after WebViewBuilder::build
in create_webview_in_context, and again in navigate() only on the
should_create_initial_document branch (the load() branch keeps existing
visibility+focus, otherwise a background tab finishing navigation would
steal focus from the foreground tab — Linus correctness ask).
Also unblocks the sidecar binary build, which had been frozen at the
May 9 13:53 stale binary because servo-engine feature was broken on two
fronts:
- ServoHost trait was missing the hover() method that the SoftwareServoHost
impl declared (regression from "Plug three holes" commit eb58ce7).
- Servo SDK renamed Key::Enter / Backspace / Tab / Escape / Delete /
Arrow{Up,Down,Left,Right} / Home / End / Page{Up,Down} to
Key::Named(NamedKey::*). keyboard.rs updated to match.
Sidecar binary rebuilt: 335 MB at May 10 01:44. The earlier 11 commits
were never reaching users because they couldn't recompile the sidecar
without these two upstream-API repairs.
1. Scroll no longer wipes keyboard focus. Servo holds DOM focus across
wheel events; the shell was clearing keyboard_focus and typed_texts
on every scroll, so a focused input went deaf the moment the user
scrolled. Scroll still drops the buffered click point because that
coordinate is captured against the pre-scroll viewport.
2. Mouse-down hands focus to the shell's root focus handle (in
addition to mouse-up's existing click forwarding). The user can now
start typing the moment they press the page, instead of having to
first complete a click round-trip to escape the omnibar's focus.
3. Sidecar hover() honors the requesting webview_id instead of
defaulting to the first webview in the map, so multi-tab sidecars
no longer pipe every hover into tab #1.
The live-mode `poll_frame` budgeted 60 ms for Servo to paint after
each `Ensure` request. That value was set when the only thing being
applied was navigation, where the GPUI tick timer (16 ms cadence) would
backfill missed frames quickly. Once we started forwarding clicks and
typing through the same `Ensure`, 60 ms was tighter than the
software renderer needs to handle MouseDown + MouseUp + layout +
paint on a real page like google.com — so the response carried the
pre-click frame and the user saw no visible reaction.
Bump the budget to 250 ms. That covers the click → focus-ring paint
cycle on the software backend without making nav slower (`apply_layout`
exits early on stable size, so unchanged ensures still return on the
first poll).
Without continuous MouseMove events, Servo never updates hover state:
no cursor changes over links, no :hover CSS effects, no mouseenter
JavaScript events. The web page appears completely non-interactive.
Track mouse position from GPUI on_mouse_move through the full IPC
pipeline to Servo. Hover position is included with each ensure
request so Servo updates hover state at frame rate (~60fps).
Enter, Backspace, Tab, Escape, Delete, arrow keys, Home, End,
PageUp, PageDown now forward to Servo. Previously only printable
characters reached the web engine, making forms unusable.