mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +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::base::Image;
|
||||||
use net_traits::image_cache::CorsStatus;
|
use net_traits::image_cache::CorsStatus;
|
||||||
use num_traits::FromPrimitive;
|
|
||||||
#[cfg(feature = "gl")]
|
#[cfg(feature = "gl")]
|
||||||
use pixels::PixelFormat;
|
use pixels::PixelFormat;
|
||||||
use profile_traits::time::{self as profile_time, profile, ProfilerCategory};
|
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::CompositorEvent::{MouseButtonEvent, MouseMoveEvent, TouchEvent, WheelEvent};
|
||||||
use script_traits::{AnimationState, AnimationTickType, LayoutControlMsg};
|
|
||||||
use script_traits::{
|
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 servo_geometry::{DeviceIndependentPixel, FramebufferUintLength};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
@ -48,8 +48,10 @@ use std::rc::Rc;
|
||||||
use style_traits::viewport::ViewportConstraints;
|
use style_traits::viewport::ViewportConstraints;
|
||||||
use style_traits::{CSSPixel, DevicePixel, PinchZoomFactor};
|
use style_traits::{CSSPixel, DevicePixel, PinchZoomFactor};
|
||||||
use time::{now, precise_time_ns, precise_time_s};
|
use time::{now, precise_time_ns, precise_time_s};
|
||||||
use webrender_api::units::{DeviceIntPoint, DeviceIntSize, DevicePoint, LayoutVector2D};
|
use webrender_api::units::{
|
||||||
use webrender_api::{self, HitTestFlags, HitTestResult, ScrollLocation};
|
DeviceIntPoint, DeviceIntSize, DevicePoint, LayoutVector2D, WorldPoint,
|
||||||
|
};
|
||||||
|
use webrender_api::{self, HitTestFlags, ScrollLocation};
|
||||||
use webrender_surfman::WebrenderSurfman;
|
use webrender_surfman::WebrenderSurfman;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -263,6 +265,10 @@ struct PipelineDetails {
|
||||||
|
|
||||||
/// Whether this pipeline is visible
|
/// Whether this pipeline is visible
|
||||||
visible: bool,
|
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 {
|
impl PipelineDetails {
|
||||||
|
@ -272,6 +278,7 @@ impl PipelineDetails {
|
||||||
animations_running: false,
|
animations_running: false,
|
||||||
animation_callbacks_running: false,
|
animation_callbacks_running: false,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
hit_test_items: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -381,17 +388,16 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
|
||||||
self.webrender.deinit();
|
self.webrender.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_cursor(&mut self, hit_test_results: HitTestResult) {
|
fn update_cursor(&mut self, result: CompositorHitTestResult) {
|
||||||
if let Some(item) = hit_test_results.items.first() {
|
let cursor = match result.cursor {
|
||||||
if let Some(cursor) = Cursor::from_u8(item.tag.1 as _) {
|
Some(cursor) if cursor != self.cursor => cursor,
|
||||||
if cursor != self.cursor {
|
_ => return,
|
||||||
self.cursor = cursor;
|
};
|
||||||
let msg = ConstellationMsg::SetCursor(cursor);
|
|
||||||
if let Err(e) = self.constellation_chan.send(msg) {
|
self.cursor = cursor;
|
||||||
warn!("Sending event to constellation failed ({:?}).", e);
|
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) => {
|
(Msg::NewScrollFrameReady(recomposite_needed), ShutdownState::NotShuttingDown) => {
|
||||||
self.waiting_for_results_of_scroll = false;
|
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 {
|
if recomposite_needed {
|
||||||
self.composition_request = CompositionRequest::CompositeNow(
|
self.composition_request = CompositionRequest::CompositeNow(
|
||||||
CompositingReason::NewWebRenderScrollFrame,
|
CompositingReason::NewWebRenderScrollFrame,
|
||||||
|
@ -628,9 +636,14 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
|
||||||
size2,
|
size2,
|
||||||
receiver,
|
receiver,
|
||||||
descriptor,
|
descriptor,
|
||||||
|
compositor_display_list_info,
|
||||||
)) => match receiver.recv() {
|
)) => match receiver.recv() {
|
||||||
Ok(data) => {
|
Ok(data) => {
|
||||||
self.waiting_on_pending_frame = true;
|
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();
|
let mut txn = webrender_api::Transaction::new();
|
||||||
txn.set_display_list(
|
txn.set_display_list(
|
||||||
epoch,
|
epoch,
|
||||||
|
@ -656,9 +669,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
|
||||||
flags,
|
flags,
|
||||||
sender,
|
sender,
|
||||||
)) => {
|
)) => {
|
||||||
let result =
|
let result = self.hit_test_at_point_with_flags_and_pipeline(point, flags, pipeline);
|
||||||
self.webrender_api
|
|
||||||
.hit_test(self.webrender_document, pipeline, point, flags);
|
|
||||||
let _ = sender.send(result);
|
let _ = sender.send(result);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -904,8 +915,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
|
||||||
MouseWindowEvent::MouseUp(_, p) => p,
|
MouseWindowEvent::MouseUp(_, p) => p,
|
||||||
};
|
};
|
||||||
|
|
||||||
let results = self.hit_test_at_point(point);
|
let result = match self.hit_test_at_device_point(point) {
|
||||||
let result = match results.items.first() {
|
|
||||||
Some(result) => result,
|
Some(result) => result,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
@ -920,29 +930,68 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
|
||||||
event_type,
|
event_type,
|
||||||
button,
|
button,
|
||||||
result.point_in_viewport.to_untyped(),
|
result.point_in_viewport.to_untyped(),
|
||||||
Some(UntrustedNodeAddress(result.tag.0 as *const c_void)),
|
Some(result.node),
|
||||||
Some(result.point_relative_to_item.to_untyped()),
|
Some(result.point_relative_to_item),
|
||||||
button as u16,
|
button as u16,
|
||||||
);
|
);
|
||||||
|
|
||||||
let pipeline_id = PipelineId::from_webrender(result.pipeline);
|
let msg = ConstellationMsg::ForwardEvent(result.pipeline_id, event_to_send);
|
||||||
let msg = ConstellationMsg::ForwardEvent(pipeline_id, event_to_send);
|
|
||||||
if let Err(e) = self.constellation_chan.send(msg) {
|
if let Err(e) = self.constellation_chan.send(msg) {
|
||||||
warn!("Sending event to constellation failed ({:?}).", e);
|
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 dppx = self.page_zoom * self.hidpi_factor();
|
||||||
let scaled_point = (point / dppx).to_untyped();
|
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);
|
fn hit_test_at_point(&self, point: WorldPoint) -> Option<CompositorHitTestResult> {
|
||||||
self.webrender_api.hit_test(
|
return self
|
||||||
self.webrender_document,
|
.hit_test_at_point_with_flags_and_pipeline(point, HitTestFlags::empty(), None)
|
||||||
None,
|
.first()
|
||||||
world_cursor,
|
.cloned();
|
||||||
HitTestFlags::empty(),
|
}
|
||||||
)
|
|
||||||
|
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) {
|
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) {
|
fn dispatch_mouse_window_move_event_class(&mut self, cursor: DevicePoint) {
|
||||||
let root_pipeline_id = match self.root_pipeline.id {
|
let result = match self.hit_test_at_device_point(cursor) {
|
||||||
Some(root_pipeline_id) => root_pipeline_id,
|
Some(result) => result,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
if self.pipeline(root_pipeline_id).is_none() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let results = self.hit_test_at_point(cursor);
|
let event = MouseMoveEvent(result.point_in_viewport, Some(result.node), 0);
|
||||||
if let Some(item) = results.items.first() {
|
let msg = ConstellationMsg::ForwardEvent(result.pipeline_id, event);
|
||||||
let node_address = Some(UntrustedNodeAddress(item.tag.0 as *const c_void));
|
if let Err(e) = self.constellation_chan.send(msg) {
|
||||||
let event = MouseMoveEvent(item.point_in_viewport.to_untyped(), node_address, 0);
|
warn!("Sending event to constellation failed ({:?}).", e);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
self.update_cursor(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_touch_event(
|
fn send_touch_event(
|
||||||
|
@ -982,16 +1023,14 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
|
||||||
identifier: TouchId,
|
identifier: TouchId,
|
||||||
point: DevicePoint,
|
point: DevicePoint,
|
||||||
) {
|
) {
|
||||||
let results = self.hit_test_at_point(point);
|
if let Some(result) = self.hit_test_at_device_point(point) {
|
||||||
if let Some(item) = results.items.first() {
|
|
||||||
let event = TouchEvent(
|
let event = TouchEvent(
|
||||||
event_type,
|
event_type,
|
||||||
identifier,
|
identifier,
|
||||||
item.point_in_viewport.to_untyped(),
|
result.point_in_viewport,
|
||||||
Some(UntrustedNodeAddress(item.tag.0 as *const c_void)),
|
Some(result.node),
|
||||||
);
|
);
|
||||||
let pipeline_id = PipelineId::from_webrender(item.pipeline);
|
let msg = ConstellationMsg::ForwardEvent(result.pipeline_id, event);
|
||||||
let msg = ConstellationMsg::ForwardEvent(pipeline_id, event);
|
|
||||||
if let Err(e) = self.constellation_chan.send(msg) {
|
if let Err(e) = self.constellation_chan.send(msg) {
|
||||||
warn!("Sending event to constellation failed ({:?}).", e);
|
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) {
|
pub fn send_wheel_event(&mut self, delta: WheelDelta, point: DevicePoint) {
|
||||||
let results = self.hit_test_at_point(point);
|
if let Some(result) = self.hit_test_at_device_point(point) {
|
||||||
if let Some(item) = results.items.first() {
|
let event = WheelEvent(delta, result.point_in_viewport, Some(result.node));
|
||||||
let event = WheelEvent(
|
let msg = ConstellationMsg::ForwardEvent(result.pipeline_id, event);
|
||||||
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 Err(e) = self.constellation_chan.send(msg) {
|
if let Err(e) = self.constellation_chan.send(msg) {
|
||||||
warn!("Sending event to constellation failed ({:?}).", e);
|
warn!("Sending event to constellation failed ({:?}).", e);
|
||||||
}
|
}
|
||||||
|
@ -1169,7 +1202,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
|
||||||
sl @ ScrollLocation::Start | sl @ ScrollLocation::End => sl,
|
sl @ ScrollLocation::Start | sl @ ScrollLocation::End => sl,
|
||||||
};
|
};
|
||||||
let cursor = (combined_event.cursor.to_f32() / self.scale).to_untyped();
|
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();
|
let mut txn = webrender_api::Transaction::new();
|
||||||
txn.scroll(scroll_location, cursor);
|
txn.scroll(scroll_location, cursor);
|
||||||
if combined_event.magnification != 1.0 {
|
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
|
/// A cursor for the window. This is different from a CSS cursor (see
|
||||||
/// `CursorKind`) in that it has no `Auto` value.
|
/// `CursorKind`) in that it has no `Auto` value.
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Clone, Copy, Deserialize, Eq, FromPrimitive, PartialEq, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, FromPrimitive, PartialEq, Serialize)]
|
||||||
pub enum Cursor {
|
pub enum Cursor {
|
||||||
None,
|
None,
|
||||||
Default,
|
Default,
|
||||||
|
|
|
@ -413,11 +413,7 @@ impl<'a> DisplayListBuildState<'a> {
|
||||||
clipping_and_scrolling: ClippingAndScrolling,
|
clipping_and_scrolling: ClippingAndScrolling,
|
||||||
) -> BaseDisplayItem {
|
) -> BaseDisplayItem {
|
||||||
BaseDisplayItem::new(
|
BaseDisplayItem::new(
|
||||||
DisplayItemMetadata {
|
DisplayItemMetadata { node, cursor },
|
||||||
node,
|
|
||||||
// Store cursor id in display list.
|
|
||||||
pointing: cursor.map(|x| x as u16),
|
|
||||||
},
|
|
||||||
clip_rect.to_layout(),
|
clip_rect.to_layout(),
|
||||||
section,
|
section,
|
||||||
self.current_stacking_context_id,
|
self.current_stacking_context_id,
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
//! They are therefore not exactly analogous to constructs like Skia pictures, which consist of
|
//! They are therefore not exactly analogous to constructs like Skia pictures, which consist of
|
||||||
//! low-level drawing primitives.
|
//! low-level drawing primitives.
|
||||||
|
|
||||||
|
use embedder_traits::Cursor;
|
||||||
use euclid::{SideOffsets2D, Vector2D};
|
use euclid::{SideOffsets2D, Vector2D};
|
||||||
use gfx_traits::print_tree::PrintTree;
|
use gfx_traits::print_tree::PrintTree;
|
||||||
use gfx_traits::{self, StackingContextId};
|
use gfx_traits::{self, StackingContextId};
|
||||||
|
@ -465,7 +466,7 @@ impl BaseDisplayItem {
|
||||||
BaseDisplayItem {
|
BaseDisplayItem {
|
||||||
metadata: DisplayItemMetadata {
|
metadata: DisplayItemMetadata {
|
||||||
node: OpaqueNode(0),
|
node: OpaqueNode(0),
|
||||||
pointing: None,
|
cursor: None,
|
||||||
},
|
},
|
||||||
// Create a rectangle of maximal size.
|
// Create a rectangle of maximal size.
|
||||||
clip_rect: LayoutRect::max_rect(),
|
clip_rect: LayoutRect::max_rect(),
|
||||||
|
@ -542,7 +543,7 @@ pub struct DisplayItemMetadata {
|
||||||
pub node: OpaqueNode,
|
pub node: OpaqueNode,
|
||||||
/// The value of the `cursor` property when the mouse hovers over this display item. If `None`,
|
/// 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`).
|
/// this display item is ineligible for pointer events (`pointer-events: none`).
|
||||||
pub pointing: Option<u16>,
|
pub cursor: Option<Cursor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Serialize)]
|
#[derive(Clone, Eq, PartialEq, Serialize)]
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
use crate::display_list::items::{BaseDisplayItem, ClipScrollNode, ClipScrollNodeType, ClipType};
|
use crate::display_list::items::{BaseDisplayItem, ClipScrollNode, ClipScrollNodeType, ClipType};
|
||||||
use crate::display_list::items::{DisplayItem, DisplayList, StackingContextType};
|
use crate::display_list::items::{DisplayItem, DisplayList, StackingContextType};
|
||||||
use msg::constellation_msg::PipelineId;
|
use msg::constellation_msg::PipelineId;
|
||||||
|
use script_traits::compositor::CompositorDisplayListInfo;
|
||||||
use webrender_api::units::{LayoutPoint, LayoutVector2D};
|
use webrender_api::units::{LayoutPoint, LayoutVector2D};
|
||||||
use webrender_api::{
|
use webrender_api::{
|
||||||
self, ClipId, CommonItemProperties, DisplayItem as WrDisplayItem, DisplayListBuilder,
|
self, ClipId, CommonItemProperties, DisplayItem as WrDisplayItem, DisplayListBuilder,
|
||||||
|
@ -22,6 +23,7 @@ struct ClipScrollState {
|
||||||
spatial_ids: Vec<Option<SpatialId>>,
|
spatial_ids: Vec<Option<SpatialId>>,
|
||||||
active_clip_id: ClipId,
|
active_clip_id: ClipId,
|
||||||
active_spatial_id: SpatialId,
|
active_spatial_id: SpatialId,
|
||||||
|
compositor_info: CompositorDisplayListInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClipScrollState {
|
impl ClipScrollState {
|
||||||
|
@ -34,6 +36,7 @@ impl ClipScrollState {
|
||||||
spatial_ids: vec![None; size],
|
spatial_ids: vec![None; size],
|
||||||
active_clip_id: root_clip_id,
|
active_clip_id: root_clip_id,
|
||||||
active_spatial_id: root_scroll_node_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
|
// 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(
|
pub fn convert_to_webrender(
|
||||||
&mut self,
|
&mut self,
|
||||||
pipeline_id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
) -> (DisplayListBuilder, IsContentful) {
|
) -> (DisplayListBuilder, CompositorDisplayListInfo, IsContentful) {
|
||||||
let webrender_pipeline = pipeline_id.to_webrender();
|
let webrender_pipeline = pipeline_id.to_webrender();
|
||||||
let mut state = ClipScrollState::new(self.clip_scroll_nodes.len(), webrender_pipeline);
|
let mut state = ClipScrollState::new(self.clip_scroll_nodes.len(), webrender_pipeline);
|
||||||
|
|
||||||
|
@ -99,7 +102,7 @@ impl DisplayList {
|
||||||
.0;
|
.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
(builder, is_contentful)
|
(builder, state.compositor_info, is_contentful)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,9 +135,14 @@ impl DisplayItem {
|
||||||
state.active_clip_id = cur_clip_id;
|
state.active_clip_id = cur_clip_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
let build_common_item_properties = |base: &BaseDisplayItem| {
|
let mut build_common_item_properties = |base: &BaseDisplayItem| {
|
||||||
let tag = match base.metadata.pointing {
|
let tag = match base.metadata.cursor {
|
||||||
Some(cursor) => Some((base.metadata.node.0 as u64, 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,
|
None => None,
|
||||||
};
|
};
|
||||||
CommonItemProperties {
|
CommonItemProperties {
|
||||||
|
|
|
@ -13,6 +13,7 @@ use euclid::{Point2D, SideOffsets2D, Size2D};
|
||||||
use gfx::text::glyph::GlyphStore;
|
use gfx::text::glyph::GlyphStore;
|
||||||
use mitochondria::OnceCell;
|
use mitochondria::OnceCell;
|
||||||
use net_traits::image_cache::UsePlaceholder;
|
use net_traits::image_cache::UsePlaceholder;
|
||||||
|
use script_traits::compositor::CompositorDisplayListInfo;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use style::computed_values::text_decoration_style::T as ComputedTextDecorationStyle;
|
use style::computed_values::text_decoration_style::T as ComputedTextDecorationStyle;
|
||||||
use style::dom::OpaqueNode;
|
use style::dom::OpaqueNode;
|
||||||
|
@ -46,6 +47,7 @@ pub struct DisplayListBuilder<'a> {
|
||||||
element_for_canvas_background: OpaqueNode,
|
element_for_canvas_background: OpaqueNode,
|
||||||
pub context: &'a LayoutContext<'a>,
|
pub context: &'a LayoutContext<'a>,
|
||||||
pub wr: wr::DisplayListBuilder,
|
pub wr: wr::DisplayListBuilder,
|
||||||
|
pub compositor_info: CompositorDisplayListInfo,
|
||||||
|
|
||||||
/// Contentful paint, for the purpose of
|
/// Contentful paint, for the purpose of
|
||||||
/// https://w3c.github.io/paint-timing/#first-contentful-paint
|
/// https://w3c.github.io/paint-timing/#first-contentful-paint
|
||||||
|
@ -66,6 +68,7 @@ impl<'a> DisplayListBuilder<'a> {
|
||||||
is_contentful: false,
|
is_contentful: false,
|
||||||
context,
|
context,
|
||||||
wr: wr::DisplayListBuilder::new(pipeline_id, fragment_tree.scrollable_overflow()),
|
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(),
|
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 {
|
impl Fragment {
|
||||||
|
@ -157,7 +175,7 @@ impl Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut common = builder.common_properties(rect.to_webrender(), &fragment.parent_style);
|
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 color = fragment.parent_style.clone_color();
|
||||||
let font_metrics = &fragment.font_metrics;
|
let font_metrics = &fragment.font_metrics;
|
||||||
|
@ -351,7 +369,7 @@ impl<'a> BuilderForBoxFragment<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_hit_test(&self, builder: &mut DisplayListBuilder) {
|
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() {
|
if hit_info.is_some() {
|
||||||
let mut common = builder.common_properties(self.border_rect, &self.fragment.style);
|
let mut common = builder.common_properties(self.border_rect, &self.fragment.style);
|
||||||
common.hit_info = hit_info;
|
common.hit_info = hit_info;
|
||||||
|
@ -559,18 +577,6 @@ fn glyphs(
|
||||||
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 {
|
fn cursor(kind: CursorKind, auto_cursor: Cursor) -> Cursor {
|
||||||
match kind {
|
match kind {
|
||||||
CursorKind::Auto => auto_cursor,
|
CursorKind::Auto => auto_cursor,
|
||||||
|
|
|
@ -1146,7 +1146,8 @@ impl LayoutThread {
|
||||||
debug!("Layout done!");
|
debug!("Layout done!");
|
||||||
|
|
||||||
// TODO: Avoid the temporary conversion and build webrender sc/dl directly!
|
// 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(
|
let viewport_size = Size2D::new(
|
||||||
self.viewport_size.width.to_f32_px(),
|
self.viewport_size.width.to_f32_px(),
|
||||||
|
@ -1165,8 +1166,12 @@ impl LayoutThread {
|
||||||
self.paint_time_metrics
|
self.paint_time_metrics
|
||||||
.maybe_observe_paint_time(self, epoch, is_contentful.0);
|
.maybe_observe_paint_time(self, epoch, is_contentful.0);
|
||||||
|
|
||||||
self.webrender_api
|
self.webrender_api.send_display_list(
|
||||||
.send_display_list(epoch, viewport_size, builder.finalize());
|
epoch,
|
||||||
|
viewport_size,
|
||||||
|
compositor_info,
|
||||||
|
builder.finalize(),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1594,11 +1599,8 @@ impl LayoutThread {
|
||||||
flags,
|
flags,
|
||||||
);
|
);
|
||||||
|
|
||||||
rw_data.nodes_from_point_response = results
|
rw_data.nodes_from_point_response =
|
||||||
.items
|
results.iter().map(|result| result.node).collect()
|
||||||
.iter()
|
|
||||||
.map(|item| UntrustedNodeAddress(item.tag.0 as *const c_void))
|
|
||||||
.collect()
|
|
||||||
},
|
},
|
||||||
&QueryMsg::ElementInnerTextQuery(node) => {
|
&QueryMsg::ElementInnerTextQuery(node) => {
|
||||||
let node = unsafe { ServoLayoutNode::new(&node) };
|
let node = unsafe { ServoLayoutNode::new(&node) };
|
||||||
|
|
|
@ -1345,8 +1345,12 @@ impl LayoutThread {
|
||||||
self.viewport_size.width.to_f32_px(),
|
self.viewport_size.width.to_f32_px(),
|
||||||
self.viewport_size.height.to_f32_px(),
|
self.viewport_size.height.to_f32_px(),
|
||||||
));
|
));
|
||||||
self.webrender_api
|
self.webrender_api.send_display_list(
|
||||||
.send_display_list(epoch, viewport_size, display_list.wr.finalize());
|
epoch,
|
||||||
|
viewport_size,
|
||||||
|
display_list.compositor_info,
|
||||||
|
display_list.wr.finalize(),
|
||||||
|
);
|
||||||
|
|
||||||
if self.trace_layout {
|
if self.trace_layout {
|
||||||
layout_debug::end_trace(self.generation.get());
|
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]
|
#[macro_use]
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
|
|
||||||
|
pub mod compositor;
|
||||||
mod script_msg;
|
mod script_msg;
|
||||||
pub mod serializable;
|
pub mod serializable;
|
||||||
pub mod transferable;
|
pub mod transferable;
|
||||||
pub mod webdriver_msg;
|
pub mod webdriver_msg;
|
||||||
|
|
||||||
|
use crate::compositor::CompositorDisplayListInfo;
|
||||||
use crate::serializable::{BlobData, BlobImpl};
|
use crate::serializable::{BlobData, BlobImpl};
|
||||||
use crate::transferable::MessagePortImpl;
|
use crate::transferable::MessagePortImpl;
|
||||||
use crate::webdriver_msg::{LoadStatus, WebDriverScriptCommand};
|
use crate::webdriver_msg::{LoadStatus, WebDriverScriptCommand};
|
||||||
|
@ -30,7 +32,7 @@ use bluetooth_traits::BluetoothRequest;
|
||||||
use canvas_traits::webgl::WebGLPipeline;
|
use canvas_traits::webgl::WebGLPipeline;
|
||||||
use crossbeam_channel::{Receiver, RecvTimeoutError, Sender};
|
use crossbeam_channel::{Receiver, RecvTimeoutError, Sender};
|
||||||
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
|
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 euclid::{default::Point2D, Length, Rect, Scale, Size2D, UnknownUnit, Vector2D};
|
||||||
use gfx_traits::Epoch;
|
use gfx_traits::Epoch;
|
||||||
use http::HeaderMap;
|
use http::HeaderMap;
|
||||||
|
@ -74,7 +76,7 @@ use webrender_api::{
|
||||||
BuiltDisplayList, DocumentId, ExternalImageData, ExternalScrollId, ImageData, ImageDescriptor,
|
BuiltDisplayList, DocumentId, ExternalImageData, ExternalScrollId, ImageData, ImageDescriptor,
|
||||||
ImageKey, ScrollClamping,
|
ImageKey, ScrollClamping,
|
||||||
};
|
};
|
||||||
use webrender_api::{BuiltDisplayListDescriptor, HitTestFlags, HitTestResult};
|
use webrender_api::{BuiltDisplayListDescriptor, HitTestFlags};
|
||||||
|
|
||||||
pub use crate::script_msg::{
|
pub use crate::script_msg::{
|
||||||
DOMMessage, HistoryEntryReplacement, Job, JobError, JobResult, JobResultValue, JobType,
|
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.
|
/// The set of WebRender operations that can be initiated by the content process.
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub enum WebrenderMsg {
|
pub enum WebrenderMsg {
|
||||||
|
@ -1125,6 +1146,7 @@ pub enum WebrenderMsg {
|
||||||
LayoutSize,
|
LayoutSize,
|
||||||
ipc::IpcBytesReceiver,
|
ipc::IpcBytesReceiver,
|
||||||
BuiltDisplayListDescriptor,
|
BuiltDisplayListDescriptor,
|
||||||
|
CompositorDisplayListInfo,
|
||||||
),
|
),
|
||||||
/// Perform a hit test operation. The result will be returned via
|
/// Perform a hit test operation. The result will be returned via
|
||||||
/// the provided channel sender.
|
/// the provided channel sender.
|
||||||
|
@ -1132,7 +1154,7 @@ pub enum WebrenderMsg {
|
||||||
Option<webrender_api::PipelineId>,
|
Option<webrender_api::PipelineId>,
|
||||||
WorldPoint,
|
WorldPoint,
|
||||||
HitTestFlags,
|
HitTestFlags,
|
||||||
IpcSender<HitTestResult>,
|
IpcSender<Vec<CompositorHitTestResult>>,
|
||||||
),
|
),
|
||||||
/// Create a new image key. The result will be returned via the
|
/// Create a new image key. The result will be returned via the
|
||||||
/// provided channel sender.
|
/// provided channel sender.
|
||||||
|
@ -1178,6 +1200,7 @@ impl WebrenderIpcSender {
|
||||||
&self,
|
&self,
|
||||||
epoch: Epoch,
|
epoch: Epoch,
|
||||||
size: LayoutSize,
|
size: LayoutSize,
|
||||||
|
display_list_info: CompositorDisplayListInfo,
|
||||||
(pipeline, size2, list): (webrender_api::PipelineId, LayoutSize, BuiltDisplayList),
|
(pipeline, size2, list): (webrender_api::PipelineId, LayoutSize, BuiltDisplayList),
|
||||||
) {
|
) {
|
||||||
let (data, descriptor) = list.into_data();
|
let (data, descriptor) = list.into_data();
|
||||||
|
@ -1189,6 +1212,7 @@ impl WebrenderIpcSender {
|
||||||
size2,
|
size2,
|
||||||
receiver,
|
receiver,
|
||||||
descriptor,
|
descriptor,
|
||||||
|
display_list_info,
|
||||||
)) {
|
)) {
|
||||||
warn!("Error sending display list: {}", e);
|
warn!("Error sending display list: {}", e);
|
||||||
}
|
}
|
||||||
|
@ -1205,7 +1229,7 @@ impl WebrenderIpcSender {
|
||||||
pipeline: Option<webrender_api::PipelineId>,
|
pipeline: Option<webrender_api::PipelineId>,
|
||||||
point: WorldPoint,
|
point: WorldPoint,
|
||||||
flags: HitTestFlags,
|
flags: HitTestFlags,
|
||||||
) -> HitTestResult {
|
) -> Vec<CompositorHitTestResult> {
|
||||||
let (sender, receiver) = ipc::channel().unwrap();
|
let (sender, receiver) = ipc::channel().unwrap();
|
||||||
self.0
|
self.0
|
||||||
.send(WebrenderMsg::HitTest(pipeline, point, flags, sender))
|
.send(WebrenderMsg::HitTest(pipeline, point, flags, sender))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue