mirror of
https://github.com/servo/servo.git
synced 2025-07-23 07:13:52 +01:00
Move click event trigger from embedding layer to ScriptThread
(#36413)
1. Move click event trigger from embedding layer to `ScriptThread`
2. Previously, the logic is to trigger click event at same position as
`MouseButtonAction::Up` if `MouseButtonAction::Up` is within 10px of
`MouseButtonAction::Down`, in embedding layer. This PR ~~removes the
condition~~ moves the check to `ScriptThread`.
Testing: tested for webdriver with self written test case. Perform
actions of pointermove, pointerdown, pointerup in sequence. Click event
can now be triggered.
Fixes: #35395
cc @xiaochengh @jdm
For `MAYBE? TODO:` part I added, should we do it? I read the
[spec](https://w3c.github.io/uievents/#event-type-click), it doesn't
specify we have to implement MDN's way.
If we should work in the MDN's way, it also should be fixed in another
PR, as this PR doesn't regress anything. Also I am not sure what is the
best way to do it.
Should I handle it in
4d4f94936f/components/script/dom/document.rs (L1296-L1297)
?
---------
Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
This commit is contained in:
parent
2ee8427665
commit
46247a7621
4 changed files with 60 additions and 35 deletions
|
@ -50,9 +50,10 @@ use devtools_traits::{
|
||||||
};
|
};
|
||||||
use embedder_traits::user_content_manager::UserContentManager;
|
use embedder_traits::user_content_manager::UserContentManager;
|
||||||
use embedder_traits::{
|
use embedder_traits::{
|
||||||
CompositorHitTestResult, EmbedderMsg, InputEvent, MediaSessionActionType, Theme,
|
CompositorHitTestResult, EmbedderMsg, InputEvent, MediaSessionActionType, MouseButton,
|
||||||
ViewportDetails, WebDriverScriptCommand,
|
MouseButtonAction, MouseButtonEvent, Theme, ViewportDetails, WebDriverScriptCommand,
|
||||||
};
|
};
|
||||||
|
use euclid::Point2D;
|
||||||
use euclid::default::Rect;
|
use euclid::default::Rect;
|
||||||
use fonts::{FontContext, SystemFontServiceProxy};
|
use fonts::{FontContext, SystemFontServiceProxy};
|
||||||
use headers::{HeaderMapExt, LastModified, ReferrerPolicy as ReferrerPolicyHeader};
|
use headers::{HeaderMapExt, LastModified, ReferrerPolicy as ReferrerPolicyHeader};
|
||||||
|
@ -98,6 +99,7 @@ use url::Position;
|
||||||
#[cfg(feature = "webgpu")]
|
#[cfg(feature = "webgpu")]
|
||||||
use webgpu_traits::{WebGPUDevice, WebGPUMsg};
|
use webgpu_traits::{WebGPUDevice, WebGPUMsg};
|
||||||
use webrender_api::DocumentId;
|
use webrender_api::DocumentId;
|
||||||
|
use webrender_api::units::DevicePixel;
|
||||||
|
|
||||||
use crate::document_collection::DocumentCollection;
|
use crate::document_collection::DocumentCollection;
|
||||||
use crate::document_loader::DocumentLoader;
|
use crate::document_loader::DocumentLoader;
|
||||||
|
@ -333,6 +335,16 @@ pub struct ScriptThread {
|
||||||
/// A factory for making new layouts. This allows layout to depend on script.
|
/// A factory for making new layouts. This allows layout to depend on script.
|
||||||
#[no_trace]
|
#[no_trace]
|
||||||
layout_factory: Arc<dyn LayoutFactory>,
|
layout_factory: Arc<dyn LayoutFactory>,
|
||||||
|
|
||||||
|
// Mouse down button: TO BE REMOVED. Not needed as click event should only
|
||||||
|
// triggered for primary button
|
||||||
|
#[no_trace]
|
||||||
|
mouse_down_button: Cell<Option<MouseButton>>,
|
||||||
|
|
||||||
|
// Mouse down point.
|
||||||
|
// In future, this shall be mouse_down_point for primary button
|
||||||
|
#[no_trace]
|
||||||
|
relative_mouse_down_point: Cell<Point2D<f32, DevicePixel>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BHMExitSignal {
|
struct BHMExitSignal {
|
||||||
|
@ -947,6 +959,8 @@ impl ScriptThread {
|
||||||
gpu_id_hub: Arc::new(IdentityHub::default()),
|
gpu_id_hub: Arc::new(IdentityHub::default()),
|
||||||
inherited_secure_context: state.inherited_secure_context,
|
inherited_secure_context: state.inherited_secure_context,
|
||||||
layout_factory,
|
layout_factory,
|
||||||
|
mouse_down_button: Cell::new(None),
|
||||||
|
relative_mouse_down_point: Cell::new(Point2D::zero()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3339,7 +3353,47 @@ impl ScriptThread {
|
||||||
warn!("Compositor event sent to closed pipeline {pipeline_id}.");
|
warn!("Compositor event sent to closed pipeline {pipeline_id}.");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
document.note_pending_input_event(event);
|
|
||||||
|
if let InputEvent::MouseButton(mouse_button_event) = event.event {
|
||||||
|
if mouse_button_event.action == MouseButtonAction::Down {
|
||||||
|
self.mouse_down_button.set(Some(mouse_button_event.button));
|
||||||
|
self.relative_mouse_down_point.set(mouse_button_event.point)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.note_pending_input_event(event.clone());
|
||||||
|
|
||||||
|
// Also send a 'click' event with same hit-test result if this is release
|
||||||
|
|
||||||
|
// MAYBE? TODO: https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event
|
||||||
|
// If the button is pressed on one element and the pointer is moved outside the element
|
||||||
|
// before the button is released, the event is fired on the most specific ancestor element
|
||||||
|
// that contained both elements.
|
||||||
|
|
||||||
|
// But spec doesn't specify this https://w3c.github.io/uievents/#event-type-click
|
||||||
|
|
||||||
|
// Servo-specific: Trigger if within 10px of the down point
|
||||||
|
if let InputEvent::MouseButton(mouse_button_event) = event.event {
|
||||||
|
if let (Some(mouse_down_button), MouseButtonAction::Up) =
|
||||||
|
(self.mouse_down_button.get(), mouse_button_event.action)
|
||||||
|
{
|
||||||
|
let pixel_dist = self.relative_mouse_down_point.get() - mouse_button_event.point;
|
||||||
|
let pixel_dist = (pixel_dist.x * pixel_dist.x + pixel_dist.y * pixel_dist.y).sqrt();
|
||||||
|
if mouse_down_button == mouse_button_event.button &&
|
||||||
|
pixel_dist < 10.0 * document.window().device_pixel_ratio().get()
|
||||||
|
{
|
||||||
|
document.note_pending_input_event(ConstellationInputEvent {
|
||||||
|
hit_test_result: event.hit_test_result,
|
||||||
|
pressed_mouse_buttons: event.pressed_mouse_buttons,
|
||||||
|
active_keyboard_modifiers: event.active_keyboard_modifiers,
|
||||||
|
event: InputEvent::MouseButton(MouseButtonEvent {
|
||||||
|
action: MouseButtonAction::Click,
|
||||||
|
button: mouse_button_event.button,
|
||||||
|
point: mouse_button_event.point,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle a "navigate an iframe" message from the constellation.
|
/// Handle a "navigate an iframe" message from the constellation.
|
||||||
|
|
|
@ -51,7 +51,7 @@ pub struct MouseButtonEvent {
|
||||||
pub point: DevicePoint,
|
pub point: DevicePoint,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
|
||||||
pub enum MouseButton {
|
pub enum MouseButton {
|
||||||
Left,
|
Left,
|
||||||
Middle,
|
Middle,
|
||||||
|
|
|
@ -256,7 +256,7 @@ pub enum DocumentState {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Input events from the embedder that are sent via the `Constellation`` to the `ScriptThread`.
|
/// Input events from the embedder that are sent via the `Constellation`` to the `ScriptThread`.
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct ConstellationInputEvent {
|
pub struct ConstellationInputEvent {
|
||||||
/// The hit test result of this input event, if any.
|
/// The hit test result of this input event, if any.
|
||||||
pub hit_test_result: Option<CompositorHitTestResult>,
|
pub hit_test_result: Option<CompositorHitTestResult>,
|
||||||
|
|
|
@ -53,8 +53,6 @@ pub struct Window {
|
||||||
screen_size: Size2D<u32, DeviceIndependentPixel>,
|
screen_size: Size2D<u32, DeviceIndependentPixel>,
|
||||||
inner_size: Cell<PhysicalSize<u32>>,
|
inner_size: Cell<PhysicalSize<u32>>,
|
||||||
toolbar_height: Cell<Length<f32, DeviceIndependentPixel>>,
|
toolbar_height: Cell<Length<f32, DeviceIndependentPixel>>,
|
||||||
mouse_down_button: Cell<Option<MouseButton>>,
|
|
||||||
webview_relative_mouse_down_point: Cell<Point2D<f32, DevicePixel>>,
|
|
||||||
monitor: winit::monitor::MonitorHandle,
|
monitor: winit::monitor::MonitorHandle,
|
||||||
webview_relative_mouse_point: Cell<Point2D<f32, DevicePixel>>,
|
webview_relative_mouse_point: Cell<Point2D<f32, DevicePixel>>,
|
||||||
last_pressed: Cell<Option<(KeyboardEvent, Option<LogicalKey>)>>,
|
last_pressed: Cell<Option<(KeyboardEvent, Option<LogicalKey>)>>,
|
||||||
|
@ -144,8 +142,6 @@ impl Window {
|
||||||
debug!("Created window {:?}", winit_window.id());
|
debug!("Created window {:?}", winit_window.id());
|
||||||
Window {
|
Window {
|
||||||
winit_window,
|
winit_window,
|
||||||
mouse_down_button: Cell::new(None),
|
|
||||||
webview_relative_mouse_down_point: Cell::new(Point2D::zero()),
|
|
||||||
webview_relative_mouse_point: Cell::new(Point2D::zero()),
|
webview_relative_mouse_point: Cell::new(Point2D::zero()),
|
||||||
last_pressed: Cell::new(None),
|
last_pressed: Cell::new(None),
|
||||||
keys_down: RefCell::new(HashMap::new()),
|
keys_down: RefCell::new(HashMap::new()),
|
||||||
|
@ -251,7 +247,6 @@ impl Window {
|
||||||
|
|
||||||
/// Helper function to handle a click
|
/// Helper function to handle a click
|
||||||
fn handle_mouse(&self, webview: &WebView, button: MouseButton, action: ElementState) {
|
fn handle_mouse(&self, webview: &WebView, button: MouseButton, action: ElementState) {
|
||||||
let max_pixel_dist = 10.0 * self.hidpi_scale_factor().get();
|
|
||||||
let mouse_button = match &button {
|
let mouse_button = match &button {
|
||||||
MouseButton::Left => ServoMouseButton::Left,
|
MouseButton::Left => ServoMouseButton::Left,
|
||||||
MouseButton::Right => ServoMouseButton::Right,
|
MouseButton::Right => ServoMouseButton::Right,
|
||||||
|
@ -263,11 +258,7 @@ impl Window {
|
||||||
|
|
||||||
let point = self.webview_relative_mouse_point.get();
|
let point = self.webview_relative_mouse_point.get();
|
||||||
let action = match action {
|
let action = match action {
|
||||||
ElementState::Pressed => {
|
ElementState::Pressed => MouseButtonAction::Down,
|
||||||
self.webview_relative_mouse_down_point.set(point);
|
|
||||||
self.mouse_down_button.set(Some(button));
|
|
||||||
MouseButtonAction::Down
|
|
||||||
},
|
|
||||||
ElementState::Released => MouseButtonAction::Up,
|
ElementState::Released => MouseButtonAction::Up,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -276,26 +267,6 @@ impl Window {
|
||||||
button: mouse_button,
|
button: mouse_button,
|
||||||
point,
|
point,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Also send a 'click' event if this is release and the press was recorded
|
|
||||||
// to be within a 10 pixels.
|
|
||||||
//
|
|
||||||
// TODO: This should be happening within the ScriptThread.
|
|
||||||
if action != MouseButtonAction::Up {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(mouse_down_button) = self.mouse_down_button.get() {
|
|
||||||
let pixel_dist = self.webview_relative_mouse_down_point.get() - point;
|
|
||||||
let pixel_dist = (pixel_dist.x * pixel_dist.x + pixel_dist.y * pixel_dist.y).sqrt();
|
|
||||||
if mouse_down_button == button && pixel_dist < max_pixel_dist {
|
|
||||||
webview.notify_input_event(InputEvent::MouseButton(MouseButtonEvent {
|
|
||||||
action: MouseButtonAction::Click,
|
|
||||||
button: mouse_button,
|
|
||||||
point,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle key events before sending them to Servo.
|
/// Handle key events before sending them to Servo.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue