script: Ensure that leaving the WebView sets the cursor back to the default cursor (#38759)

This changes makes a variety of changes to ensure that the cursor is set
back to the default cursor when it leaves the `WebView`:

1. Display list updates can come after a mouse leaves the `WebView`, so
   when refreshing the cursor after the update, base the updated cursor
   on the last hovered location in the `DocumentEventHandler`, rather
   than the compositor. This allows us to catch when the last hovered
   position is `None` (ie the cursor has left the `WebView`).
2. When handling `MouseLeftViewport` events for the cursor leaving the
   entire WebView, properly set the
   MouseLeftViewport::focus_moving_to_another_iframe` on the input event
   passed to the script thread.
3. When moving out of the `WebView` entirely, explicitly ask the
   embedder to set the cursor back to the default.

Testing: This change adds a unit test verifying this behavior.
Fixes: #38710.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-08-22 00:49:56 -07:00 committed by GitHub
parent 66adf2bf9f
commit 4784ff0375
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 164 additions and 90 deletions

View file

@ -91,7 +91,6 @@ use script_traits::{
use servo_config::{opts, prefs};
use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
use style::thread_state::{self, ThreadState};
use style_traits::CSSPixel;
use stylo_atoms::Atom;
use timers::{TimerEventRequest, TimerId, TimerScheduler};
use url::Position;
@ -1884,8 +1883,8 @@ impl ScriptThread {
);
}
},
ScriptThreadMessage::RefreshCursor(pipeline_id, cursor_position) => {
self.handle_refresh_cursor(pipeline_id, cursor_position);
ScriptThreadMessage::RefreshCursor(pipeline_id) => {
self.handle_refresh_cursor(pipeline_id);
},
ScriptThreadMessage::PreferencesUpdated(updates) => {
let mut current_preferences = prefs::get().clone();
@ -3996,15 +3995,11 @@ impl ScriptThread {
));
}
fn handle_refresh_cursor(
&self,
pipeline_id: PipelineId,
cursor_position: Point2D<f32, CSSPixel>,
) {
let Some(window) = self.documents.borrow().find_window(pipeline_id) else {
fn handle_refresh_cursor(&self, pipeline_id: PipelineId) {
let Some(document) = self.documents.borrow().find_document(pipeline_id) else {
return;
};
window.handle_refresh_cursor(cursor_position);
document.event_handler().handle_refresh_cursor();
}
}