mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
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:
commit
a8da28e55d
10 changed files with 225 additions and 107 deletions
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) };
|
||||
|
|
|
@ -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());
|
||||
|
|
44
components/script_traits/compositor.rs
Normal file
44
components/script_traits/compositor.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue