Auto merge of #29490 - mrobinson:move-hit-testing-out, r=mukilan

Move hit testing information out of WebRender

Store hit testing information in a data structure that sits alongside the display list in the compositor. This will allow the compositor to store more information per-node. The data structure also takes care of de-duplicating information between successive display list entries. In the future, the data structure can be even more aggressive in producing smaller side hit testing lists, if necessary.

<!-- Please describe your changes on the following line: -->

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes do not require tests because they should not change behavior.

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
bors-servo 2023-03-16 12:41:37 +01:00 committed by GitHub
commit a8da28e55d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 225 additions and 107 deletions

View file

@ -28,16 +28,16 @@ use msg::constellation_msg::{
};
use net_traits::image::base::Image;
use net_traits::image_cache::CorsStatus;
use num_traits::FromPrimitive;
#[cfg(feature = "gl")]
use pixels::PixelFormat;
use profile_traits::time::{self as profile_time, profile, ProfilerCategory};
use script_traits::compositor::HitTestInfo;
use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent, TouchEvent, WheelEvent};
use script_traits::{AnimationState, AnimationTickType, LayoutControlMsg};
use script_traits::{
MouseButton, MouseEventType, ScrollState, TouchEventType, TouchId, WheelDelta,
AnimationState, AnimationTickType, CompositorHitTestResult, LayoutControlMsg, MouseButton,
MouseEventType, ScrollState, TouchEventType, TouchId, UntrustedNodeAddress, WheelDelta,
WindowSizeData, WindowSizeType,
};
use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType};
use servo_geometry::{DeviceIndependentPixel, FramebufferUintLength};
use std::collections::HashMap;
use std::env;
@ -48,8 +48,10 @@ use std::rc::Rc;
use style_traits::viewport::ViewportConstraints;
use style_traits::{CSSPixel, DevicePixel, PinchZoomFactor};
use time::{now, precise_time_ns, precise_time_s};
use webrender_api::units::{DeviceIntPoint, DeviceIntSize, DevicePoint, LayoutVector2D};
use webrender_api::{self, HitTestFlags, HitTestResult, ScrollLocation};
use webrender_api::units::{
DeviceIntPoint, DeviceIntSize, DevicePoint, LayoutVector2D, WorldPoint,
};
use webrender_api::{self, HitTestFlags, ScrollLocation};
use webrender_surfman::WebrenderSurfman;
#[derive(Debug, PartialEq)]
@ -263,6 +265,10 @@ struct PipelineDetails {
/// Whether this pipeline is visible
visible: bool,
/// Hit test items for this pipeline. This is used to map WebRender hit test
/// information to the full information necessary for Servo.
hit_test_items: Vec<HitTestInfo>,
}
impl PipelineDetails {
@ -272,6 +278,7 @@ impl PipelineDetails {
animations_running: false,
animation_callbacks_running: false,
visible: true,
hit_test_items: Vec::new(),
}
}
}
@ -381,17 +388,16 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
self.webrender.deinit();
}
pub fn update_cursor(&mut self, hit_test_results: HitTestResult) {
if let Some(item) = hit_test_results.items.first() {
if let Some(cursor) = Cursor::from_u8(item.tag.1 as _) {
if cursor != self.cursor {
self.cursor = cursor;
let msg = ConstellationMsg::SetCursor(cursor);
if let Err(e) = self.constellation_chan.send(msg) {
warn!("Sending event to constellation failed ({:?}).", e);
}
}
}
fn update_cursor(&mut self, result: CompositorHitTestResult) {
let cursor = match result.cursor {
Some(cursor) if cursor != self.cursor => cursor,
_ => return,
};
self.cursor = cursor;
let msg = ConstellationMsg::SetCursor(cursor);
if let Err(e) = self.constellation_chan.send(msg) {
warn!("Sending event to constellation failed ({:?}).", e);
}
}
@ -514,7 +520,9 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
(Msg::NewScrollFrameReady(recomposite_needed), ShutdownState::NotShuttingDown) => {
self.waiting_for_results_of_scroll = false;
self.update_cursor(self.hit_test_at_point(self.cursor_pos));
if let Some(result) = self.hit_test_at_device_point(self.cursor_pos) {
self.update_cursor(result);
}
if recomposite_needed {
self.composition_request = CompositionRequest::CompositeNow(
CompositingReason::NewWebRenderScrollFrame,
@ -628,9 +636,14 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
size2,
receiver,
descriptor,
compositor_display_list_info,
)) => match receiver.recv() {
Ok(data) => {
self.waiting_on_pending_frame = true;
let details = self.pipeline_details(PipelineId::from_webrender(pipeline));
details.hit_test_items = compositor_display_list_info.hit_test_info;
let mut txn = webrender_api::Transaction::new();
txn.set_display_list(
epoch,
@ -656,9 +669,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
flags,
sender,
)) => {
let result =
self.webrender_api
.hit_test(self.webrender_document, pipeline, point, flags);
let result = self.hit_test_at_point_with_flags_and_pipeline(point, flags, pipeline);
let _ = sender.send(result);
},
@ -904,8 +915,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
MouseWindowEvent::MouseUp(_, p) => p,
};
let results = self.hit_test_at_point(point);
let result = match results.items.first() {
let result = match self.hit_test_at_device_point(point) {
Some(result) => result,
None => return,
};
@ -920,29 +930,68 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
event_type,
button,
result.point_in_viewport.to_untyped(),
Some(UntrustedNodeAddress(result.tag.0 as *const c_void)),
Some(result.point_relative_to_item.to_untyped()),
Some(result.node),
Some(result.point_relative_to_item),
button as u16,
);
let pipeline_id = PipelineId::from_webrender(result.pipeline);
let msg = ConstellationMsg::ForwardEvent(pipeline_id, event_to_send);
let msg = ConstellationMsg::ForwardEvent(result.pipeline_id, event_to_send);
if let Err(e) = self.constellation_chan.send(msg) {
warn!("Sending event to constellation failed ({:?}).", e);
}
}
fn hit_test_at_point(&self, point: DevicePoint) -> HitTestResult {
fn hit_test_at_device_point(&self, point: DevicePoint) -> Option<CompositorHitTestResult> {
let dppx = self.page_zoom * self.hidpi_factor();
let scaled_point = (point / dppx).to_untyped();
let world_point = WorldPoint::from_untyped(scaled_point);
return self.hit_test_at_point(world_point);
}
let world_cursor = webrender_api::units::WorldPoint::from_untyped(scaled_point);
self.webrender_api.hit_test(
self.webrender_document,
None,
world_cursor,
HitTestFlags::empty(),
)
fn hit_test_at_point(&self, point: WorldPoint) -> Option<CompositorHitTestResult> {
return self
.hit_test_at_point_with_flags_and_pipeline(point, HitTestFlags::empty(), None)
.first()
.cloned();
}
fn hit_test_at_point_with_flags_and_pipeline(
&self,
point: WorldPoint,
flags: HitTestFlags,
pipeline_id: Option<webrender_api::PipelineId>,
) -> Vec<CompositorHitTestResult> {
let root_pipeline_id = match self.root_pipeline.id {
Some(root_pipeline_id) => root_pipeline_id,
None => return vec![],
};
if self.pipeline(root_pipeline_id).is_none() {
return vec![];
}
let results =
self.webrender_api
.hit_test(self.webrender_document, pipeline_id, point, flags);
results
.items
.iter()
.filter_map(|item| {
let pipeline_id = PipelineId::from_webrender(item.pipeline);
let details = match self.pipeline_details.get(&pipeline_id) {
Some(details) => details,
None => return None,
};
let info = &details.hit_test_items[item.tag.0 as usize];
Some(CompositorHitTestResult {
pipeline_id,
point_in_viewport: item.point_in_viewport.to_untyped(),
point_relative_to_item: item.point_relative_to_item.to_untyped(),
node: UntrustedNodeAddress(info.node as *const c_void),
cursor: info.cursor,
})
})
.collect()
}
pub fn on_mouse_window_move_event_class(&mut self, cursor: DevicePoint) {
@ -955,25 +1004,17 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
}
fn dispatch_mouse_window_move_event_class(&mut self, cursor: DevicePoint) {
let root_pipeline_id = match self.root_pipeline.id {
Some(root_pipeline_id) => root_pipeline_id,
let result = match self.hit_test_at_device_point(cursor) {
Some(result) => result,
None => return,
};
if self.pipeline(root_pipeline_id).is_none() {
return;
}
let results = self.hit_test_at_point(cursor);
if let Some(item) = results.items.first() {
let node_address = Some(UntrustedNodeAddress(item.tag.0 as *const c_void));
let event = MouseMoveEvent(item.point_in_viewport.to_untyped(), node_address, 0);
let pipeline_id = PipelineId::from_webrender(item.pipeline);
let msg = ConstellationMsg::ForwardEvent(pipeline_id, event);
if let Err(e) = self.constellation_chan.send(msg) {
warn!("Sending event to constellation failed ({:?}).", e);
}
self.update_cursor(results);
let event = MouseMoveEvent(result.point_in_viewport, Some(result.node), 0);
let msg = ConstellationMsg::ForwardEvent(result.pipeline_id, event);
if let Err(e) = self.constellation_chan.send(msg) {
warn!("Sending event to constellation failed ({:?}).", e);
}
self.update_cursor(result);
}
fn send_touch_event(
@ -982,16 +1023,14 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
identifier: TouchId,
point: DevicePoint,
) {
let results = self.hit_test_at_point(point);
if let Some(item) = results.items.first() {
if let Some(result) = self.hit_test_at_device_point(point) {
let event = TouchEvent(
event_type,
identifier,
item.point_in_viewport.to_untyped(),
Some(UntrustedNodeAddress(item.tag.0 as *const c_void)),
result.point_in_viewport,
Some(result.node),
);
let pipeline_id = PipelineId::from_webrender(item.pipeline);
let msg = ConstellationMsg::ForwardEvent(pipeline_id, event);
let msg = ConstellationMsg::ForwardEvent(result.pipeline_id, event);
if let Err(e) = self.constellation_chan.send(msg) {
warn!("Sending event to constellation failed ({:?}).", e);
}
@ -999,15 +1038,9 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
}
pub fn send_wheel_event(&mut self, delta: WheelDelta, point: DevicePoint) {
let results = self.hit_test_at_point(point);
if let Some(item) = results.items.first() {
let event = WheelEvent(
delta,
item.point_in_viewport.to_untyped(),
Some(UntrustedNodeAddress(item.tag.0 as *const c_void)),
);
let pipeline_id = PipelineId::from_webrender(item.pipeline);
let msg = ConstellationMsg::ForwardEvent(pipeline_id, event);
if let Some(result) = self.hit_test_at_device_point(point) {
let event = WheelEvent(delta, result.point_in_viewport, Some(result.node));
let msg = ConstellationMsg::ForwardEvent(result.pipeline_id, event);
if let Err(e) = self.constellation_chan.send(msg) {
warn!("Sending event to constellation failed ({:?}).", e);
}
@ -1169,7 +1202,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
sl @ ScrollLocation::Start | sl @ ScrollLocation::End => sl,
};
let cursor = (combined_event.cursor.to_f32() / self.scale).to_untyped();
let cursor = webrender_api::units::WorldPoint::from_untyped(cursor);
let cursor = WorldPoint::from_untyped(cursor);
let mut txn = webrender_api::Transaction::new();
txn.scroll(scroll_location, cursor);
if combined_event.magnification != 1.0 {

View file

@ -26,7 +26,7 @@ pub use webxr_api::MainThreadWaker as EventLoopWaker;
/// A cursor for the window. This is different from a CSS cursor (see
/// `CursorKind`) in that it has no `Auto` value.
#[repr(u8)]
#[derive(Clone, Copy, Deserialize, Eq, FromPrimitive, PartialEq, Serialize)]
#[derive(Clone, Copy, Debug, Deserialize, Eq, FromPrimitive, PartialEq, Serialize)]
pub enum Cursor {
None,
Default,

View file

@ -413,11 +413,7 @@ impl<'a> DisplayListBuildState<'a> {
clipping_and_scrolling: ClippingAndScrolling,
) -> BaseDisplayItem {
BaseDisplayItem::new(
DisplayItemMetadata {
node,
// Store cursor id in display list.
pointing: cursor.map(|x| x as u16),
},
DisplayItemMetadata { node, cursor },
clip_rect.to_layout(),
section,
self.current_stacking_context_id,

View file

@ -12,6 +12,7 @@
//! They are therefore not exactly analogous to constructs like Skia pictures, which consist of
//! low-level drawing primitives.
use embedder_traits::Cursor;
use euclid::{SideOffsets2D, Vector2D};
use gfx_traits::print_tree::PrintTree;
use gfx_traits::{self, StackingContextId};
@ -465,7 +466,7 @@ impl BaseDisplayItem {
BaseDisplayItem {
metadata: DisplayItemMetadata {
node: OpaqueNode(0),
pointing: None,
cursor: None,
},
// Create a rectangle of maximal size.
clip_rect: LayoutRect::max_rect(),
@ -542,7 +543,7 @@ pub struct DisplayItemMetadata {
pub node: OpaqueNode,
/// The value of the `cursor` property when the mouse hovers over this display item. If `None`,
/// this display item is ineligible for pointer events (`pointer-events: none`).
pub pointing: Option<u16>,
pub cursor: Option<Cursor>,
}
#[derive(Clone, Eq, PartialEq, Serialize)]

View file

@ -10,6 +10,7 @@
use crate::display_list::items::{BaseDisplayItem, ClipScrollNode, ClipScrollNodeType, ClipType};
use crate::display_list::items::{DisplayItem, DisplayList, StackingContextType};
use msg::constellation_msg::PipelineId;
use script_traits::compositor::CompositorDisplayListInfo;
use webrender_api::units::{LayoutPoint, LayoutVector2D};
use webrender_api::{
self, ClipId, CommonItemProperties, DisplayItem as WrDisplayItem, DisplayListBuilder,
@ -22,6 +23,7 @@ struct ClipScrollState {
spatial_ids: Vec<Option<SpatialId>>,
active_clip_id: ClipId,
active_spatial_id: SpatialId,
compositor_info: CompositorDisplayListInfo,
}
impl ClipScrollState {
@ -34,6 +36,7 @@ impl ClipScrollState {
spatial_ids: vec![None; size],
active_clip_id: root_clip_id,
active_spatial_id: root_scroll_node_id,
compositor_info: CompositorDisplayListInfo::default(),
};
// We need to register the WebRender root reference frame and root scroll node ids
@ -82,7 +85,7 @@ impl DisplayList {
pub fn convert_to_webrender(
&mut self,
pipeline_id: PipelineId,
) -> (DisplayListBuilder, IsContentful) {
) -> (DisplayListBuilder, CompositorDisplayListInfo, IsContentful) {
let webrender_pipeline = pipeline_id.to_webrender();
let mut state = ClipScrollState::new(self.clip_scroll_nodes.len(), webrender_pipeline);
@ -99,7 +102,7 @@ impl DisplayList {
.0;
}
(builder, is_contentful)
(builder, state.compositor_info, is_contentful)
}
}
@ -132,9 +135,14 @@ impl DisplayItem {
state.active_clip_id = cur_clip_id;
}
let build_common_item_properties = |base: &BaseDisplayItem| {
let tag = match base.metadata.pointing {
Some(cursor) => Some((base.metadata.node.0 as u64, cursor)),
let mut build_common_item_properties = |base: &BaseDisplayItem| {
let tag = match base.metadata.cursor {
Some(cursor) => {
let hit_test_index = state
.compositor_info
.add_hit_test_info(base.metadata.node.0 as u64, Some(cursor));
Some((hit_test_index as u64, 0u16))
},
None => None,
};
CommonItemProperties {

View file

@ -13,6 +13,7 @@ use euclid::{Point2D, SideOffsets2D, Size2D};
use gfx::text::glyph::GlyphStore;
use mitochondria::OnceCell;
use net_traits::image_cache::UsePlaceholder;
use script_traits::compositor::CompositorDisplayListInfo;
use std::sync::Arc;
use style::computed_values::text_decoration_style::T as ComputedTextDecorationStyle;
use style::dom::OpaqueNode;
@ -46,6 +47,7 @@ pub struct DisplayListBuilder<'a> {
element_for_canvas_background: OpaqueNode,
pub context: &'a LayoutContext<'a>,
pub wr: wr::DisplayListBuilder,
pub compositor_info: CompositorDisplayListInfo,
/// Contentful paint, for the purpose of
/// https://w3c.github.io/paint-timing/#first-contentful-paint
@ -66,6 +68,7 @@ impl<'a> DisplayListBuilder<'a> {
is_contentful: false,
context,
wr: wr::DisplayListBuilder::new(pipeline_id, fragment_tree.scrollable_overflow()),
compositor_info: CompositorDisplayListInfo::default(),
}
}
@ -85,6 +88,21 @@ impl<'a> DisplayListBuilder<'a> {
flags: style.get_webrender_primitive_flags(),
}
}
fn hit_info(&mut self, style: &ComputedValues, tag: Tag, auto_cursor: Cursor) -> HitInfo {
use style::computed_values::pointer_events::T as PointerEvents;
let inherited_ui = style.get_inherited_ui();
if inherited_ui.pointer_events == PointerEvents::None {
None
} else {
let hit_test_index = self.compositor_info.add_hit_test_info(
tag.node().0 as u64,
Some(cursor(inherited_ui.cursor.keyword, auto_cursor)),
);
Some((hit_test_index as u64, 0u16))
}
}
}
impl Fragment {
@ -157,7 +175,7 @@ impl Fragment {
}
let mut common = builder.common_properties(rect.to_webrender(), &fragment.parent_style);
common.hit_info = hit_info(&fragment.parent_style, fragment.tag, Cursor::Text);
common.hit_info = builder.hit_info(&fragment.parent_style, fragment.tag, Cursor::Text);
let color = fragment.parent_style.clone_color();
let font_metrics = &fragment.font_metrics;
@ -351,7 +369,7 @@ impl<'a> BuilderForBoxFragment<'a> {
}
fn build_hit_test(&self, builder: &mut DisplayListBuilder) {
let hit_info = hit_info(&self.fragment.style, self.fragment.tag, Cursor::Default);
let hit_info = builder.hit_info(&self.fragment.style, self.fragment.tag, Cursor::Default);
if hit_info.is_some() {
let mut common = builder.common_properties(self.border_rect, &self.fragment.style);
common.hit_info = hit_info;
@ -559,18 +577,6 @@ fn glyphs(
glyphs
}
fn hit_info(style: &ComputedValues, tag: Tag, auto_cursor: Cursor) -> HitInfo {
use style::computed_values::pointer_events::T as PointerEvents;
let inherited_ui = style.get_inherited_ui();
if inherited_ui.pointer_events == PointerEvents::None {
None
} else {
let cursor = cursor(inherited_ui.cursor.keyword, auto_cursor);
Some((tag.node().0 as u64, cursor as u16))
}
}
fn cursor(kind: CursorKind, auto_cursor: Cursor) -> Cursor {
match kind {
CursorKind::Auto => auto_cursor,

View file

@ -1146,7 +1146,8 @@ impl LayoutThread {
debug!("Layout done!");
// TODO: Avoid the temporary conversion and build webrender sc/dl directly!
let (builder, is_contentful) = display_list.convert_to_webrender(self.id);
let (builder, compositor_info, is_contentful) =
display_list.convert_to_webrender(self.id);
let viewport_size = Size2D::new(
self.viewport_size.width.to_f32_px(),
@ -1165,8 +1166,12 @@ impl LayoutThread {
self.paint_time_metrics
.maybe_observe_paint_time(self, epoch, is_contentful.0);
self.webrender_api
.send_display_list(epoch, viewport_size, builder.finalize());
self.webrender_api.send_display_list(
epoch,
viewport_size,
compositor_info,
builder.finalize(),
);
},
);
}
@ -1594,11 +1599,8 @@ impl LayoutThread {
flags,
);
rw_data.nodes_from_point_response = results
.items
.iter()
.map(|item| UntrustedNodeAddress(item.tag.0 as *const c_void))
.collect()
rw_data.nodes_from_point_response =
results.iter().map(|result| result.node).collect()
},
&QueryMsg::ElementInnerTextQuery(node) => {
let node = unsafe { ServoLayoutNode::new(&node) };

View file

@ -1345,8 +1345,12 @@ impl LayoutThread {
self.viewport_size.width.to_f32_px(),
self.viewport_size.height.to_f32_px(),
));
self.webrender_api
.send_display_list(epoch, viewport_size, display_list.wr.finalize());
self.webrender_api.send_display_list(
epoch,
viewport_size,
display_list.compositor_info,
display_list.wr.finalize(),
);
if self.trace_layout {
layout_debug::end_trace(self.generation.get());

View file

@ -0,0 +1,44 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! Defines data structures which are consumed by the Compositor.
use embedder_traits::Cursor;
/// Information that Servo keeps alongside WebRender display items
/// in order to add more context to hit test results.
#[derive(Debug, Deserialize, Serialize)]
pub struct HitTestInfo {
/// The id of the node of this hit test item.
pub node: u64,
/// The cursor of this node's hit test item.
pub cursor: Option<Cursor>,
}
/// A data structure which stores compositor-side information about
/// display lists sent to the compositor.
/// by a WebRender display list.
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct CompositorDisplayListInfo {
/// An array of `HitTestInfo` which is used to store information
/// to assist the compositor to take various actions (set the cursor,
/// scroll without layout) using a WebRender hit test result.
pub hit_test_info: Vec<HitTestInfo>,
}
impl CompositorDisplayListInfo {
/// Add or re-use a duplicate HitTestInfo entry in this `CompositorHitTestInfo`
/// and return the index.
pub fn add_hit_test_info(&mut self, node: u64, cursor: Option<Cursor>) -> usize {
if let Some(last) = self.hit_test_info.last() {
if node == last.node && cursor == last.cursor {
return self.hit_test_info.len() - 1;
}
}
self.hit_test_info.push(HitTestInfo { node, cursor });
self.hit_test_info.len() - 1
}
}

View file

@ -18,11 +18,13 @@ extern crate malloc_size_of_derive;
#[macro_use]
extern crate serde;
pub mod compositor;
mod script_msg;
pub mod serializable;
pub mod transferable;
pub mod webdriver_msg;
use crate::compositor::CompositorDisplayListInfo;
use crate::serializable::{BlobData, BlobImpl};
use crate::transferable::MessagePortImpl;
use crate::webdriver_msg::{LoadStatus, WebDriverScriptCommand};
@ -30,7 +32,7 @@ use bluetooth_traits::BluetoothRequest;
use canvas_traits::webgl::WebGLPipeline;
use crossbeam_channel::{Receiver, RecvTimeoutError, Sender};
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
use embedder_traits::EventLoopWaker;
use embedder_traits::{Cursor, EventLoopWaker};
use euclid::{default::Point2D, Length, Rect, Scale, Size2D, UnknownUnit, Vector2D};
use gfx_traits::Epoch;
use http::HeaderMap;
@ -74,7 +76,7 @@ use webrender_api::{
BuiltDisplayList, DocumentId, ExternalImageData, ExternalScrollId, ImageData, ImageDescriptor,
ImageKey, ScrollClamping,
};
use webrender_api::{BuiltDisplayListDescriptor, HitTestFlags, HitTestResult};
use webrender_api::{BuiltDisplayListDescriptor, HitTestFlags};
pub use crate::script_msg::{
DOMMessage, HistoryEntryReplacement, Job, JobError, JobResult, JobResultValue, JobType,
@ -1110,6 +1112,25 @@ impl From<i32> for MediaSessionActionType {
}
}
/// The result of a hit test in the compositor.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct CompositorHitTestResult {
/// The pipeline id of the resulting item.
pub pipeline_id: PipelineId,
/// The hit test point in the item's viewport.
pub point_in_viewport: euclid::default::Point2D<f32>,
/// The hit test point relative to the item itself.
pub point_relative_to_item: euclid::default::Point2D<f32>,
/// The node address of the hit test result.
pub node: UntrustedNodeAddress,
/// The cursor that should be used when hovering the item hit by the hit test.
pub cursor: Option<Cursor>,
}
/// The set of WebRender operations that can be initiated by the content process.
#[derive(Deserialize, Serialize)]
pub enum WebrenderMsg {
@ -1125,6 +1146,7 @@ pub enum WebrenderMsg {
LayoutSize,
ipc::IpcBytesReceiver,
BuiltDisplayListDescriptor,
CompositorDisplayListInfo,
),
/// Perform a hit test operation. The result will be returned via
/// the provided channel sender.
@ -1132,7 +1154,7 @@ pub enum WebrenderMsg {
Option<webrender_api::PipelineId>,
WorldPoint,
HitTestFlags,
IpcSender<HitTestResult>,
IpcSender<Vec<CompositorHitTestResult>>,
),
/// Create a new image key. The result will be returned via the
/// provided channel sender.
@ -1178,6 +1200,7 @@ impl WebrenderIpcSender {
&self,
epoch: Epoch,
size: LayoutSize,
display_list_info: CompositorDisplayListInfo,
(pipeline, size2, list): (webrender_api::PipelineId, LayoutSize, BuiltDisplayList),
) {
let (data, descriptor) = list.into_data();
@ -1189,6 +1212,7 @@ impl WebrenderIpcSender {
size2,
receiver,
descriptor,
display_list_info,
)) {
warn!("Error sending display list: {}", e);
}
@ -1205,7 +1229,7 @@ impl WebrenderIpcSender {
pipeline: Option<webrender_api::PipelineId>,
point: WorldPoint,
flags: HitTestFlags,
) -> HitTestResult {
) -> Vec<CompositorHitTestResult> {
let (sender, receiver) = ipc::channel().unwrap();
self.0
.send(WebrenderMsg::HitTest(pipeline, point, flags, sender))