script: Move point_in_initial_containing_block calculation to script (#38520)

Instead of calculating this value in the compositor, calculate it in
`ScriptThread` now that it is straightforward to get this value from the
layout spatial tree. This allows removing some tricky callback code in
the Compositor.

Testing: This shouldn't change any observable behavior so is covered by
existing tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-08-11 11:32:38 +02:00 committed by GitHub
parent 9d4004135b
commit 005164df4a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 18 additions and 53 deletions

View file

@ -259,20 +259,15 @@ impl ServoRenderer {
self.shutdown_state.get()
}
pub(crate) fn hit_test_at_point<'a>(
&self,
point: DevicePoint,
details_for_pipeline: impl Fn(PipelineId) -> Option<&'a PipelineDetails>,
) -> Vec<CompositorHitTestResult> {
self.hit_test_at_point_with_flags(point, HitTestFlags::empty(), details_for_pipeline)
pub(crate) fn hit_test_at_point(&self, point: DevicePoint) -> Vec<CompositorHitTestResult> {
self.hit_test_at_point_with_flags(point, HitTestFlags::empty())
}
// TODO: split this into first half (global) and second half (one for whole compositor, one for webview)
pub(crate) fn hit_test_at_point_with_flags<'a>(
pub(crate) fn hit_test_at_point_with_flags(
&self,
point: DevicePoint,
flags: HitTestFlags,
details_for_pipeline: impl Fn(PipelineId) -> Option<&'a PipelineDetails>,
) -> Vec<CompositorHitTestResult> {
// DevicePoint and WorldPoint are the same for us.
let world_point = WorldPoint::from_untyped(point.to_untyped());
@ -286,26 +281,14 @@ impl ServoRenderer {
results
.items
.iter()
.filter_map(|item| {
.map(|item| {
let pipeline_id = item.pipeline.into();
let details = details_for_pipeline(pipeline_id)?;
let offset = details
.scroll_tree
.scroll_offset(pipeline_id.root_scroll_id())
.unwrap_or_default();
let point_in_initial_containing_block =
(item.point_in_viewport + offset).to_untyped();
let external_scroll_id = ExternalScrollId(item.tag.0, item.pipeline);
Some(CompositorHitTestResult {
CompositorHitTestResult {
pipeline_id,
point_in_viewport: Point2D::from_untyped(item.point_in_viewport.to_untyped()),
point_relative_to_initial_containing_block: Point2D::from_untyped(
point_in_initial_containing_block,
),
external_scroll_id,
})
}
})
.collect()
}
@ -1112,19 +1095,6 @@ impl IOCompositor {
}
}
fn details_for_pipeline(&self, pipeline_id: PipelineId) -> Option<&PipelineDetails> {
let webview_id = self
.global
.borrow()
.pipeline_to_webview_map
.get(&pipeline_id)
.cloned()?;
self.webview_renderers
.get(webview_id)?
.pipelines
.get(&pipeline_id)
}
/// Returns true if any animation callbacks (ie `requestAnimationFrame`) are waiting for a response.
fn animation_callbacks_running(&self) -> bool {
self.webview_renderers
@ -1604,9 +1574,8 @@ impl IOCompositor {
return;
};
let details_for_pipeline = |pipeline_id| self.details_for_pipeline(pipeline_id);
let Some(hit_test_result) = global
.hit_test_at_point(last_mouse_move_position, details_for_pipeline)
.hit_test_at_point(last_mouse_move_position)
.first()
.cloned()
else {

View file

@ -337,11 +337,10 @@ impl WebViewRenderer {
};
// If we can't find a pipeline to send this event to, we cannot continue.
let get_pipeline_details = |pipeline_id| self.pipelines.get(&pipeline_id);
let Some(result) = self
.global
.borrow()
.hit_test_at_point(point, get_pipeline_details)
.hit_test_at_point(point)
.into_iter()
.nth(0)
else {
@ -852,12 +851,10 @@ impl WebViewRenderer {
ScrollLocation::Start | ScrollLocation::End => scroll_location,
};
let get_pipeline_details = |pipeline_id| self.pipelines.get(&pipeline_id);
let hit_test_results = self.global.borrow().hit_test_at_point_with_flags(
cursor,
HitTestFlags::FIND_ALL,
get_pipeline_details,
);
let hit_test_results = self
.global
.borrow()
.hit_test_at_point_with_flags(cursor, HitTestFlags::FIND_ALL);
// Iterate through all hit test results, processing only the first node of each pipeline.
// This is needed to propagate the scroll events from a pipeline representing an iframe to

View file

@ -2611,6 +2611,10 @@ impl Window {
.into_iter()
.nth(0)?;
let point_in_frame = compositor_hit_test_result.point_in_viewport;
let point_relative_to_initial_containing_block =
point_in_frame + self.scroll_offset().cast_unit();
// SAFETY: This is safe because `Window::query_elements_from_point` has ensured that
// layout has run and any OpaqueNodes that no longer refer to real nodes are gone.
let address = UntrustedNodeAddress(result.node.0 as *const c_void);
@ -2618,9 +2622,8 @@ impl Window {
node: unsafe { from_untrusted_node_address(address) },
cursor: result.cursor,
point_in_node: result.point_in_target,
point_in_frame: compositor_hit_test_result.point_in_viewport,
point_relative_to_initial_containing_block: compositor_hit_test_result
.point_relative_to_initial_containing_block,
point_in_frame,
point_relative_to_initial_containing_block,
})
}

View file

@ -884,10 +884,6 @@ pub struct CompositorHitTestResult {
/// The hit test point in the item's viewport.
pub point_in_viewport: Point2D<f32, CSSPixel>,
/// The hit test point relative to the root scroll node content origin / initial
/// containing block.
pub point_relative_to_initial_containing_block: Point2D<f32, CSSPixel>,
/// The [`ExternalScrollId`] of the scroll tree node associated with this hit test item.
pub external_scroll_id: ExternalScrollId,
}