script: Focus on mousedown instead of mouse click according to spec (#38589)

- Focus on mousedown instead of mouse click according to
[spec](https://w3c.github.io/uievents/#handle-native-mouse-down)
- Refactor to follow spec closer and make things more clear.
- Add some spec link.
- Remove some dead spec link.

Still some preparation before implementing #38435.
Testing: No regression in WebDriver & WPT. But update some outdated
test.
Fixes: #38588

---------

Signed-off-by: Euclid Ye <euclid.ye@huawei.com>
This commit is contained in:
Euclid Ye 2025-08-12 10:40:21 +08:00 committed by GitHub
parent d2122c8bd8
commit 5d21234872
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 69 additions and 31 deletions

View file

@ -1546,6 +1546,7 @@ impl Document {
} }
} }
/// <https://w3c.github.io/uievents/#mouseevent-algorithms>
pub(crate) fn handle_mouse_button_event( pub(crate) fn handle_mouse_button_event(
&self, &self,
event: MouseButtonEvent, event: MouseButtonEvent,
@ -1579,15 +1580,6 @@ impl Document {
if el.is_actually_disabled() { if el.is_actually_disabled() {
return; return;
} }
// For a node within a text input UA shadow DOM, delegate the focus target into its shadow host.
// TODO: This focus delegation should be done with shadow DOM delegateFocus attribute.
let target_el = el.find_focusable_shadow_host_if_necessary();
self.begin_focus_transaction();
// Try to focus `el`. If it's not focusable, focus the document instead.
self.request_focus(None, FocusInitiator::Local, can_gc);
self.request_focus(target_el.as_deref(), FocusInitiator::Local, can_gc);
} }
let dom_event = DomRoot::upcast::<Event>(MouseEvent::for_platform_mouse_event( let dom_event = DomRoot::upcast::<Event>(MouseEvent::for_platform_mouse_event(
@ -1599,44 +1591,65 @@ impl Document {
can_gc, can_gc,
)); ));
// https://html.spec.whatwg.org/multipage/#run-authentic-click-activation-steps
let activatable = el.as_maybe_activatable(); let activatable = el.as_maybe_activatable();
match event.action { match event.action {
// https://w3c.github.io/uievents/#handle-native-mouse-click
MouseButtonAction::Click => { MouseButtonAction::Click => {
el.set_click_in_progress(true); el.set_click_in_progress(true);
dom_event.fire(node.upcast(), can_gc); dom_event.dispatch(node.upcast(), false, can_gc);
el.set_click_in_progress(false); el.set_click_in_progress(false);
self.maybe_fire_dblclick(node, &hit_test_result, input_event, can_gc);
}, },
// https://w3c.github.io/uievents/#handle-native-mouse-down
MouseButtonAction::Down => { MouseButtonAction::Down => {
// (TODO) Step 6. Maybe send pointerdown event with `dom_event`.
if let Some(a) = activatable { if let Some(a) = activatable {
a.enter_formal_activation_state(); a.enter_formal_activation_state();
} }
let target = node.upcast(); // For a node within a text input UA shadow DOM,
dom_event.fire(target, can_gc); // delegate the focus target into its shadow host.
// TODO: This focus delegation should be done
// with shadow DOM delegateFocus attribute.
let target_el = el.find_focusable_shadow_host_if_necessary();
self.begin_focus_transaction();
// Try to focus `el`. If it's not focusable, focus the document instead.
self.request_focus(None, FocusInitiator::Local, can_gc);
self.request_focus(target_el.as_deref(), FocusInitiator::Local, can_gc);
// Step 7. Let result = dispatch event at target
let result = dom_event.dispatch(node.upcast(), false, can_gc);
// Step 8. If result is true and target is a focusable area
// that is click focusable, then Run the focusing steps at target.
if result && self.focus_transaction.borrow().is_some() {
self.commit_focus_transaction(FocusInitiator::Local, can_gc);
}
// Step 9. If mbutton is the secondary mouse button, then
// Maybe show context menu with native, target.
if let (MouseButtonAction::Down, MouseButton::Right) = (event.action, event.button)
{
self.maybe_show_context_menu(
node.upcast(),
&hit_test_result,
input_event,
can_gc,
);
}
}, },
// https://w3c.github.io/uievents/#handle-native-mouse-up
MouseButtonAction::Up => { MouseButtonAction::Up => {
if let Some(a) = activatable { if let Some(a) = activatable {
a.exit_formal_activation_state(); a.exit_formal_activation_state();
} }
// (TODO) Step 6. Maybe send pointerup event with `dom_event``.
let target = node.upcast(); // Step 7. dispatch event at target.
dom_event.fire(target, can_gc); dom_event.dispatch(node.upcast(), false, can_gc);
}, },
} }
if let MouseButtonAction::Click = event.action {
if self.focus_transaction.borrow().is_some() {
self.commit_focus_transaction(FocusInitiator::Local, can_gc);
}
self.maybe_fire_dblclick(node, &hit_test_result, input_event, can_gc);
}
// When the contextmenu event is triggered by right mouse button
// the contextmenu event MUST be dispatched after the mousedown event.
if let (MouseButtonAction::Down, MouseButton::Right) = (event.action, event.button) {
self.maybe_show_context_menu(node.upcast(), &hit_test_result, input_event, can_gc);
}
} }
/// <https://www.w3.org/TR/uievents/#maybe-show-context-menu> /// <https://www.w3.org/TR/uievents/#maybe-show-context-menu>

View file

@ -222,6 +222,7 @@ impl MouseEvent {
} }
/// Create a [MouseEvent] triggered by the embedder /// Create a [MouseEvent] triggered by the embedder
/// <https://w3c.github.io/uievents/#create-a-cancelable-mouseevent-id>
pub(crate) fn for_platform_mouse_event( pub(crate) fn for_platform_mouse_event(
event: embedder_traits::MouseButtonEvent, event: embedder_traits::MouseButtonEvent,
pressed_mouse_buttons: u16, pressed_mouse_buttons: u16,

View file

@ -1,9 +1,17 @@
[navigate.py] [navigate.py]
expected: TIMEOUT [test_link_from_toplevel_context_with_target[_blank\]]
[test_link_hash]
expected: FAIL expected: FAIL
[test_link_from_toplevel_context_with_target[_blank\]] [test_link_from_nested_context_with_target[]]
expected: FAIL
[test_link_from_nested_context_with_target[_parent]]
expected: FAIL
[test_link_from_nested_context_with_target[_self]]
expected: FAIL
[test_link_from_nested_context_with_target[_top]]
expected: FAIL expected: FAIL
[test_link_from_toplevel_context_with_target[_parent\]] [test_link_from_toplevel_context_with_target[_parent\]]

View file

@ -7,3 +7,9 @@
[test_dismiss[prompt-None\]] [test_dismiss[prompt-None\]]
expected: FAIL expected: FAIL
[test_accept[alert-None\]]
expected: FAIL
[test_dismiss[confirm-False\]]
expected: FAIL

View file

@ -10,3 +10,6 @@
[test_seen_nodes[https coop\]] [test_seen_nodes[https coop\]]
expected: FAIL expected: FAIL
[test_removed_iframe]
expected: FAIL

View file

@ -1,3 +1,4 @@
[navigation.py] [navigation.py]
expected: TIMEOUT
[test_pointer] [test_pointer]
expected: FAIL expected: FAIL

View file

@ -0,0 +1,3 @@
[pointer_modifier_click.py]
[test_many_modifiers_click]
expected: FAIL

View file

@ -4,3 +4,6 @@
[test_set_to_available_size] [test_set_to_available_size]
expected: FAIL expected: FAIL
[test_negative_x_y]
expected: FAIL