Skip to content

fix(webview): dispatch key events in their own tasks#41056

Merged
yury-s merged 3 commits into
microsoft:mainfrom
yury-s:webview-input-tasks
Jun 1, 2026
Merged

fix(webview): dispatch key events in their own tasks#41056
yury-s merged 3 commits into
microsoft:mainfrom
yury-s:webview-input-tasks

Conversation

@yury-s
Copy link
Copy Markdown
Member

@yury-s yury-s commented May 30, 2026

Summary

Dispatch the synthesized keyboard events in their own event-loop tasks, matching real WebKit.

Real WebKit delivers keydown, keypress and textInput each 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 the keydown → keypress → textInput sequence across tasks and awaits the result via Runtime.awaitPromise (stock WebKit's Runtime.evaluate has no awaitPromise option). Timers are taken from the existing globals snapshot (saveGlobalsSnapshotSource, now extended with setTimeout/setImmediate) so a page overriding them can't break scheduling.

Also: a cancelled keydown now suppresses keypress/insertion (checked at the call site), matching the platform.

Adds a cross-browser test asserting a microtask scheduled in the keydown handler runs before keypress.

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.

yury-s added 3 commits May 29, 2026 16:27
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.
@github-actions
Copy link
Copy Markdown
Contributor

Test results for "MCP"

7233 passed, 1113 skipped


Merge workflow run.

@github-actions
Copy link
Copy Markdown
Contributor

Test results for "tests 1"

3 flaky ⚠️ [chromium-library] › library/video.spec.ts:719 › screencast › should work with video+trace `@chromium-ubuntu-22.04-arm-node20`
⚠️ [chromium-library] › library/inspector/recorder-api.spec.ts:120 › should type `@chromium-ubuntu-22.04-node22`
⚠️ [playwright-test] › ui-mode-test-update.spec.ts:202 › should update test locations `@macos-latest-node20`

43996 passed, 865 skipped


Merge workflow run.

// 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> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this will work, you should instead do

postTask();
postTask();
postTask();

synchronously and serialized.

@yury-s yury-s merged commit dc51ddb into microsoft:main Jun 1, 2026
51 checks passed
@yury-s yury-s deleted the webview-input-tasks branch June 1, 2026 17:43
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.

2 participants