From 6d4b7e7a226319b3b9e94bdc8f38e0b643f90ddb Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Mon, 13 Mar 2023 11:56:38 +0100 Subject: [PATCH] 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. --- components/compositing/compositor.rs | 165 +++++++++++------- components/embedder_traits/lib.rs | 2 +- components/layout/display_list/builder.rs | 6 +- components/layout/display_list/items.rs | 5 +- .../layout/display_list/webrender_helpers.rs | 18 +- components/layout_2020/display_list/mod.rs | 34 ++-- components/layout_thread/lib.rs | 18 +- components/layout_thread_2020/lib.rs | 8 +- components/script_traits/compositor.rs | 44 +++++ components/script_traits/lib.rs | 32 +++- 10 files changed, 225 insertions(+), 107 deletions(-) create mode 100644 components/script_traits/compositor.rs diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 023c3cac959..b9c4d3930d6 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -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, } 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 IOCompositor { 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 IOCompositor { (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 IOCompositor { 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 IOCompositor { 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 IOCompositor { 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 IOCompositor { 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 { 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 { + 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, + ) -> Vec { + 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 IOCompositor { } 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 IOCompositor { 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 IOCompositor { } 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 IOCompositor { 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 { diff --git a/components/embedder_traits/lib.rs b/components/embedder_traits/lib.rs index c6f372dec19..8654732735d 100644 --- a/components/embedder_traits/lib.rs +++ b/components/embedder_traits/lib.rs @@ -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, diff --git a/components/layout/display_list/builder.rs b/components/layout/display_list/builder.rs index 7dc85fec830..cad8b712142 100644 --- a/components/layout/display_list/builder.rs +++ b/components/layout/display_list/builder.rs @@ -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, diff --git a/components/layout/display_list/items.rs b/components/layout/display_list/items.rs index 4800abe7e53..4b8a5cf1a5e 100644 --- a/components/layout/display_list/items.rs +++ b/components/layout/display_list/items.rs @@ -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, + pub cursor: Option, } #[derive(Clone, Eq, PartialEq, Serialize)] diff --git a/components/layout/display_list/webrender_helpers.rs b/components/layout/display_list/webrender_helpers.rs index 30d581e937a..c32f4ba87eb 100644 --- a/components/layout/display_list/webrender_helpers.rs +++ b/components/layout/display_list/webrender_helpers.rs @@ -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>, 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 { diff --git a/components/layout_2020/display_list/mod.rs b/components/layout_2020/display_list/mod.rs index 3f77e4ae362..73b7b6bb97f 100644 --- a/components/layout_2020/display_list/mod.rs +++ b/components/layout_2020/display_list/mod.rs @@ -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, diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index ac1a7fe21ef..77f95323490 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -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) }; diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index ef99a27668f..8de5cd81cef 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -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()); diff --git a/components/script_traits/compositor.rs b/components/script_traits/compositor.rs new file mode 100644 index 00000000000..00f56cbcfa5 --- /dev/null +++ b/components/script_traits/compositor.rs @@ -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, +} + +/// 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, +} + +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) -> 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 + } +} diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index af336a41374..d306cc696db 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -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 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, + + /// The hit test point relative to the item itself. + pub point_relative_to_item: euclid::default::Point2D, + + /// 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, +} + /// 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, WorldPoint, HitTestFlags, - IpcSender, + IpcSender>, ), /// 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, point: WorldPoint, flags: HitTestFlags, - ) -> HitTestResult { + ) -> Vec { let (sender, receiver) = ipc::channel().unwrap(); self.0 .send(WebrenderMsg::HitTest(pipeline, point, flags, sender))