Skip to content

[WebDriver] Add synchronization for key action#37403

Merged
xiaochengh merged 4 commits intoservo:mainfrom
PotatoCP:add-sync-key
Jun 18, 2025
Merged

[WebDriver] Add synchronization for key action#37403
xiaochengh merged 4 commits intoservo:mainfrom
PotatoCP:add-sync-key

Conversation

@PotatoCP
Copy link
Copy Markdown
Contributor

Implement action synchronization for key event. Previously only done for pointer #36932 and wheel #37260.

@PotatoCP
Copy link
Copy Markdown
Contributor Author

@PotatoCP
Copy link
Copy Markdown
Contributor Author

PotatoCP commented Jun 12, 2025

For try, test for key, key_events, key_shortcuts, and key_special_keys seems stable here

https://github.com/PotatoCP/servo/actions/runs/15600770809
https://github.com/PotatoCP/servo/actions/runs/15600776568
https://github.com/PotatoCP/servo/actions/runs/15600920645

Note I run that try before the last commit for wpt expectation.

Also I update the expectation based on servodriver test here

https://github.com/PotatoCP/servo/actions/runs/15579793468
https://github.com/PotatoCP/servo/actions/runs/15581529630

Which is why I remove both failed expectation for key_shortcuts. And I am also reluctant to change the expectation of key_events since it is still timeout with servodriver.

The key_events is getting timeout since it sometimes failed the hit testing. In try, we run with headless browser, so maybe that's why it passes there.

Copy link
Copy Markdown
Contributor

@longvatrong111 longvatrong111 left a comment

Choose a reason for hiding this comment

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

I have some suggestion about implementing count.
My main idea is we should follow the spec and avoid making differences in function signature.


// Step 1.3. Try to dispatch tick actions
self.dispatch_tick_actions(tick_actions, tick_duration)?;
let cnt = self.dispatch_tick_actions(tick_actions, tick_duration)?;
Copy link
Copy Markdown
Contributor

@longvatrong111 longvatrong111 Jun 12, 2025

Choose a reason for hiding this comment

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

Is there any reason to return a count here?
If it's only a refactor so we should avoid changing function signature.
If it improves the couting logic, I recommend we can make a class variable and update anywhere in actions code.

Copy link
Copy Markdown
Contributor Author

@PotatoCP PotatoCP Jun 12, 2025

Choose a reason for hiding this comment

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

Hmm, my concern is that sometimes dispatch_keyup sometimes don't dispatch any event

https://github.com/servo/servo/pull/37403/files#diff-5401b925eefc1f03be09174aea84fbfcbc5d8b85b559db101aa8d7dbef7b6745R328-R342

Hence, I try to return if it's counted or not. I also have concern with differing with spec, but I don't really know how to handle this one.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

As discussed offline, I add sent_action_count item in Handler to avoid returning i32 when dispatching actions. @longvatrong111

@PotatoCP PotatoCP changed the title [Webdriver] Add synchronization for key action [WebDriver] Add synchronization for key action Jun 12, 2025
@PotatoCP PotatoCP marked this pull request as ready for review June 12, 2025 09:27

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ServoKeyEvent {
pub event: KeyboardEvent,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This pattern is weird and also inconsistent with the other types of events.

Is it possible to add message id to the existing KeyboardEvent struct?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately KeyboardEvent is taken from keyboard_types crate. I don't know how to add the id unless we wrap the event into another struct.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I see, thanks. Then wrapping it seems to be the only way to go.

  1. I'm still concerned about the inconsistent approaches between keyboard events and other events. But maybe I'm overthinking...
  2. ServoKeyEvent is still a weird name. Any better idea?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

For number one, I also think that the approach is weird, but I think we have to do it like this until in the future, if the design for waiting the action is changed for the better.

For number two, how about WrappedKeyEvent?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't have a good idea. Maybe just call it KeyboardEventWithWebDriverMsgId, plus some comment what this is for and why, and a TODO for thinking about a better name.

A name should explain its role and usage. WrappedKeyEvent is therefore not informative enough, as readers still can't tell why it's wrapped.

Copy link
Copy Markdown
Contributor

@longvatrong111 longvatrong111 Jun 13, 2025

Choose a reason for hiding this comment

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

How about keep it KeyboardEvent? Because it still appears everywhere in Servo. Other parts don't need to care about webdriver_id we add inside.

Copy link
Copy Markdown
Member

@jdm jdm Jun 13, 2025

Choose a reason for hiding this comment

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

I think calling it KeyboardEvent makes sense.

Copy link
Copy Markdown
Contributor Author

@PotatoCP PotatoCP Jun 14, 2025

Choose a reason for hiding this comment

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

The problem with calling it KeyboardEvent is that in headed_window.rs, we already have KeyboardEvent from Servo. So I think there will be conflict of name.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll go with Xiaocheng's suggestion for now.

Copy link
Copy Markdown
Contributor

@longvatrong111 longvatrong111 left a comment

Choose a reason for hiding this comment

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

The purpose of wrapping a type is we pretend the wrapper type is the internal type.
We can expose everything of keyboard_types::KeyboardEvent in our embedder::KeyboardEvent, it seems a bit verbose here but help keeping the API input_events provides.
Or can we fix it in keyboard_types crate?

/// Wrap the KeyboardEvent from crate to pair it with webdriver_id,
/// which is used for webdriver action synchronization.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct KeyboardEventWithWebDriverId {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we do something like this? I believe it can minimize the change outside of this file.

Suggested change
pub struct KeyboardEventWithWebDriverId {
use keyboard_types::{CompositionEvent, KeyboardEvent as ExternalKeyboardEvent, Modifier};
pub struct KeyboardEvent {
pub event: ExternalKeyboardEvent,
pub modifiers: Modifier,
...
}
impl KeyboardEvent {
pub fn new(event: ExternalKeyboardEvent) -> Self {
Self {
event: keyboard_event,
modifiers: event.modifiers,
webdriver_id: None,
}
}
fn foo(&self) {
self.event.foo()
}
}

Copy link
Copy Markdown
Contributor Author

@PotatoCP PotatoCP Jun 16, 2025

Choose a reason for hiding this comment

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

This looks good. I think the event item is unneeded, since keyboard_types:KeyboardEvent has no method

The event field is still needed I guess, see below comment.

@PotatoCP
Copy link
Copy Markdown
Contributor Author

I have tried to disguise the wrapper struct. Maybe you can check later when you have time @longvatrong111.

Comment on lines +120 to +144
pub struct ShortcutMatcher;
impl ShortcutMatcher {
pub fn from_event<T>(event: KeyboardEvent) -> ExternalShortcutMatcher<T> {
ExternalShortcutMatcher::from_event(event.event)
}
}

Copy link
Copy Markdown
Contributor Author

@PotatoCP PotatoCP Jun 16, 2025

Choose a reason for hiding this comment

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

This is for

fn handle_overridable_key_bindings(&self, webview: ::servo::WebView, event: KeyboardEvent) {
let origin = webview.rect().min.ceil().to_i32();
ShortcutMatcher::from_event(event)
.shortcut(CMD_OR_CONTROL, '=', || {
webview.set_zoom(1.1);
})
.shortcut(CMD_OR_CONTROL, '+', || {
webview.set_zoom(1.1);
})

Let me know if anyone has any better suggestion about this, since I has some doubts.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If we can't totally remove keyboard_types::KeyboardEvent, I think it's good to convert between 2 events sometimes.

Copy link
Copy Markdown
Contributor

@longvatrong111 longvatrong111 left a comment

Choose a reason for hiding this comment

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

Look mostly good to me. I have some more suggestions.

@PotatoCP PotatoCP force-pushed the add-sync-key branch 3 times, most recently from 7d1c871 to 12080fd Compare June 17, 2025 01:54
/// which is used for webdriver action synchronization.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct KeyboardEvent {
pub state: KeyState,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We can either wrap or unpack the external KeyboardEvent, but doing both at the same time looks really bad to me.

Let's go back to simply wrapping it so that we can reuse the abilities of the external KeyboardEvent.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks. I have made so that we only wrap the event but still making sure that code other than input_event only use input_event::KeyboardEvent instead of keyboard_types::KeyboardEvent

@xiaochengh xiaochengh added this pull request to the merge queue Jun 17, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jun 17, 2025
@xiaochengh
Copy link
Copy Markdown
Contributor

Build on OH failed. Could you fix the usage of KeyboardEvent in ports/servoshell/egl/app_state.rs?

@PotatoCP PotatoCP requested a review from jschwe as a code owner June 18, 2025 06:06
@PotatoCP
Copy link
Copy Markdown
Contributor Author

Thanks for the notice. It should be good now @xiaochengh

@jschwe jschwe added the T-ohos Do a try run on OpenHarmony label Jun 18, 2025
@github-actions github-actions bot removed the T-ohos Do a try run on OpenHarmony label Jun 18, 2025
@github-actions
Copy link
Copy Markdown

🔨 Triggering try run (#15726388444) for OpenHarmony

@github-actions
Copy link
Copy Markdown

🐰 Bencher Report

Branchmain
TestbedHUAWEI Mate 60 Pro

⚠️ WARNING: No Threshold found!

Without a Threshold, no Alerts will ever be generated.

Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the --ci-only-thresholds flag.

Click to view all benchmark results
BenchmarkLatencymilliseconds (ms)MemoryBytes
E2E/https://www.google.com/JS/gc-heap/admin📈 view plot
⚠️ NO THRESHOLD
23,840.00
E2E/https://www.google.com/JS/gc-heap/decommitted📈 view plot
⚠️ NO THRESHOLD
573,440.00
E2E/https://www.google.com/JS/gc-heap/unused📈 view plot
⚠️ NO THRESHOLD
49,272.00
E2E/https://www.google.com/JS/gc-heap/used📈 view plot
⚠️ NO THRESHOLD
402,024.00
E2E/https://www.google.com/JS/malloc-heap📈 view plot
⚠️ NO THRESHOLD
2,862,067.00
E2E/https://www.google.com/JS/non-heap📈 view plot
⚠️ NO THRESHOLD
262,144.00
E2E/https://www.google.com/LayoutThread/box-tree📈 view plot
⚠️ NO THRESHOLD
101,488.00
E2E/https://www.google.com/LayoutThread/display-list📈 view plot
⚠️ NO THRESHOLD
0.00
E2E/https://www.google.com/LayoutThread/font-context📈 view plot
⚠️ NO THRESHOLD
4,040.00
E2E/https://www.google.com/LayoutThread/fragment-tree📈 view plot
⚠️ NO THRESHOLD
112.00
E2E/https://www.google.com/LayoutThread/stylist📈 view plot
⚠️ NO THRESHOLD
5,264.00
E2E/https://www.google.com/Load📈 view plot
⚠️ NO THRESHOLD
444.59 ms
E2E/https://www.google.com/Resident📈 view plot
⚠️ NO THRESHOLD
184,971,264.00
E2E/https://www.google.com/image-cache📈 view plot
⚠️ NO THRESHOLD
35,328.00
E2E/https://www.servo.org/Load📈 view plot
⚠️ NO THRESHOLD
1,463.39 ms
E2E/https://www.servo.org/Resident📈 view plot
⚠️ NO THRESHOLD
265,460,121.00
🐰 View full continuous benchmarking report in Bencher

@xiaochengh xiaochengh added this pull request to the merge queue Jun 18, 2025
@github-actions
Copy link
Copy Markdown

✨ Try run (#15726388444) succeeded.

Merged via the queue into servo:main with commit cdc8b45 Jun 18, 2025
33 checks passed
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.

5 participants