fix(webview): dispatch key events in their own tasks#41056
Merged
Conversation
Real WebKit delivers keydown, keypress and textInput each in a separate event-loop task (a microtask checkpoint runs between them), so handlers that schedule promises/microtasks between events behave correctly. The synthetic dispatcher fired them synchronously in one task. Spread the keydown/keypress/ textInput sequence across tasks (setTimeout, captured before page scripts can override it) and await the resulting promise via Runtime.awaitPromise, since stock WebKit's Runtime.evaluate has no awaitPromise option. Adds a cross-browser test asserting a microtask scheduled in the keydown handler runs before keypress. Mouse dispatch stays synchronous: task-separating it shifts when hover-triggered interstitials appear relative to action retries and destabilizes addLocatorHandler.
…spatch - Snapshot setTimeout/setImmediate in saveGlobalsSnapshotSource so the page-side input dispatcher reads them from there instead of capturing setTimeout itself; a page overriding timers still cannot break per-event task scheduling. - callWebViewInput: use the typed protocol params (drop the `as any` casts), gate the awaitPromise/releaseObject path on className === 'Promise' (so a void result never leaks a handle), guard against a missing result on navigation, and release the promise handle fire-and-forget. - Drop a duplicated comment.
Move the keydown-not-prevented check out of _typeCharacter to keydown, so a cancelled keydown returns before keypress/textInput. This drops a parameter and matches the platform: preventing keydown suppresses the keypress too.
Contributor
Test results for "MCP"7233 passed, 1113 skipped Merge workflow run. |
Contributor
Test results for "tests 1"3 flaky43996 passed, 865 skipped Merge workflow run. |
pavelfeldman
approved these changes
May 30, 2026
| // a sequence the same way so handlers that schedule work between events behave | ||
| // as they would on a real device. Timers come from the globals snapshot (taken | ||
| // at bootstrap), so a page overriding setTimeout cannot break scheduling. | ||
| private _nextTask(): Promise<void> { |
Member
There was a problem hiding this comment.
I don't think this will work, you should instead do
postTask();
postTask();
postTask();
synchronously and serialized.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Dispatch the synthesized keyboard events in their own event-loop tasks, matching real WebKit.
Real WebKit delivers
keydown,keypressandtextInputeach in a separate task (a microtask checkpoint runs between them), so page handlers that schedule promises/microtasks between events behave correctly. The WebView dispatcher fired them synchronously in one task. This spreads thekeydown → keypress → textInputsequence across tasks and awaits the result viaRuntime.awaitPromise(stock WebKit'sRuntime.evaluatehas noawaitPromiseoption). Timers are taken from the existing globals snapshot (saveGlobalsSnapshotSource, now extended withsetTimeout/setImmediate) so a page overriding them can't break scheduling.Also: a cancelled
keydownnow suppresseskeypress/insertion (checked at the call site), matching the platform.Adds a cross-browser test asserting a microtask scheduled in the
keydownhandler runs beforekeypress.Mouse dispatch is intentionally left synchronous: task-separating it shifts when hover-triggered interstitials appear relative to Playwright's hit-target interception, which destabilizes
addLocatorHandler.