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.
This commit is contained in:
Martin Robinson 2023-03-13 11:56:38 +01:00
parent d95c371d79
commit 6d4b7e7a22
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))