servo/components/shared/script/compositor.rs
Martin Robinson 7e82c5c957
Compile Servo with the latest version of rust stable (#30831)
This completes the transition to compiling Servo with rust stable. Some
nightly-only features are still used when compiling the `script` and
`crown` crates, as well as for some style unit tests. These will likely
break with newer compiler versions, but `crown` can be disabled for them
conditionally. This is just the first step.

This has some caveats:

1. We need to disable setting up the special linker on Linux. The -Z
   option isn't supported with stable rust so using this is out --
   meanwhile we can't be sure that lld is installed on most systems.
2. `cargo fmt` still uses some unstable options, so we need to rely on
   the unstable toolchain just for running `fmt`. The idea is to fix this
   gradually.
2023-12-06 17:36:07 +00:00

306 lines
11 KiB
Rust

/* 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;
use serde::{Deserialize, Serialize};
use webrender_api::units::{LayoutSize, LayoutVector2D};
use webrender_api::{
Epoch, ExternalScrollId, PipelineId, ScrollLocation, ScrollSensitivity, SpatialId,
};
/// 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>,
/// The id of the [ScrollTree] associated with this hit test item.
pub scroll_tree_node: ScrollTreeNodeId,
}
/// An id for a ScrollTreeNode in the ScrollTree. This contains both the index
/// to the node in the tree's array of nodes as well as the corresponding SpatialId
/// for the SpatialNode in the WebRender display list.
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct ScrollTreeNodeId {
/// The index of this scroll tree node in the tree's array of nodes.
pub index: usize,
/// The WebRender spatial id of this scroll tree node.
pub spatial_id: SpatialId,
}
/// Data stored for nodes in the [ScrollTree] that actually scroll,
/// as opposed to reference frames and sticky nodes which do not.
#[derive(Debug, Deserialize, Serialize)]
pub struct ScrollableNodeInfo {
/// The external scroll id of this node, used to track
/// it between successive re-layouts.
pub external_id: ExternalScrollId,
/// Amount that this `ScrollableNode` can scroll in both directions.
pub scrollable_size: LayoutSize,
/// Whether this `ScrollableNode` is sensitive to input events.
pub scroll_sensitivity: ScrollSensitivity,
/// The current offset of this scroll node.
pub offset: LayoutVector2D,
}
#[derive(Debug, Deserialize, Serialize)]
/// A node in a tree of scroll nodes. This may either be a scrollable
/// node which responds to scroll events or a non-scrollable one.
pub struct ScrollTreeNode {
/// The index of the parent of this node in the tree. If this is
/// None then this is the root node.
pub parent: Option<ScrollTreeNodeId>,
/// Scrolling data which will not be None if this is a scrolling node.
pub scroll_info: Option<ScrollableNodeInfo>,
}
impl ScrollTreeNode {
/// Get the external id of this node.
pub fn external_id(&self) -> Option<ExternalScrollId> {
self.scroll_info.as_ref().map(|info| info.external_id)
}
/// Get the offset id of this node if it applies.
pub fn offset(&self) -> Option<LayoutVector2D> {
self.scroll_info.as_ref().map(|info| info.offset)
}
/// Set the offset for this node, returns false if this was a
/// non-scrolling node for which you cannot set the offset.
pub fn set_offset(&mut self, new_offset: LayoutVector2D) -> bool {
match self.scroll_info {
Some(ref mut info) => {
info.offset = new_offset;
true
},
_ => false,
}
}
/// Scroll this node given a WebRender ScrollLocation. Returns a tuple that can
/// be used to scroll an individual WebRender scroll frame if the operation
/// actually changed an offset.
pub fn scroll(
&mut self,
scroll_location: ScrollLocation,
) -> Option<(ExternalScrollId, LayoutVector2D)> {
let info = match self.scroll_info {
Some(ref mut data) => data,
None => return None,
};
if info.scroll_sensitivity != ScrollSensitivity::ScriptAndInputEvents {
return None;
}
let delta = match scroll_location {
ScrollLocation::Delta(delta) => delta,
ScrollLocation::Start => {
if info.offset.y.round() >= 0.0 {
// Nothing to do on this layer.
return None;
}
info.offset.y = 0.0;
return Some((info.external_id, info.offset));
},
ScrollLocation::End => {
let end_pos = -info.scrollable_size.height;
if info.offset.y.round() <= end_pos {
// Nothing to do on this layer.
return None;
}
info.offset.y = end_pos;
return Some((info.external_id, info.offset));
},
};
let scrollable_width = info.scrollable_size.width;
let scrollable_height = info.scrollable_size.height;
let original_layer_scroll_offset = info.offset.clone();
if scrollable_width > 0. {
info.offset.x = (info.offset.x + delta.x).min(0.0).max(-scrollable_width);
}
if scrollable_height > 0. {
info.offset.y = (info.offset.y + delta.y).min(0.0).max(-scrollable_height);
}
if info.offset != original_layer_scroll_offset {
Some((info.external_id, info.offset))
} else {
None
}
}
}
/// A tree of spatial nodes, which mirrors the spatial nodes in the WebRender
/// display list, except these are used to scrolling in the compositor so that
/// new offsets can be sent to WebRender.
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct ScrollTree {
/// A list of compositor-side scroll nodes that describe the tree
/// of WebRender spatial nodes, used by the compositor to scroll the
/// contents of the display list.
pub nodes: Vec<ScrollTreeNode>,
}
impl ScrollTree {
/// Add a scroll node to this ScrollTree returning the id of the new node.
pub fn add_scroll_tree_node(
&mut self,
parent: Option<&ScrollTreeNodeId>,
spatial_id: SpatialId,
scroll_info: Option<ScrollableNodeInfo>,
) -> ScrollTreeNodeId {
self.nodes.push(ScrollTreeNode {
parent: parent.cloned(),
scroll_info,
});
return ScrollTreeNodeId {
index: self.nodes.len() - 1,
spatial_id,
};
}
/// Get a mutable reference to the node with the given index.
pub fn get_node_mut(&mut self, id: &ScrollTreeNodeId) -> &mut ScrollTreeNode {
&mut self.nodes[id.index]
}
/// Get an immutable reference to the node with the given index.
pub fn get_node(&mut self, id: &ScrollTreeNodeId) -> &ScrollTreeNode {
&self.nodes[id.index]
}
/// Scroll the given scroll node on this scroll tree. If the node cannot be scrolled,
/// because it isn't a scrollable node or it's already scrolled to the maximum scroll
/// extent, try to scroll an ancestor of this node. Returns the node scrolled and the
/// new offset if a scroll was performed, otherwise returns None.
pub fn scroll_node_or_ancestor(
&mut self,
scroll_node_id: &ScrollTreeNodeId,
scroll_location: ScrollLocation,
) -> Option<(ExternalScrollId, LayoutVector2D)> {
let parent = {
let ref mut node = self.get_node_mut(scroll_node_id);
let result = node.scroll(scroll_location);
if result.is_some() {
return result;
}
node.parent
};
parent.and_then(|parent| self.scroll_node_or_ancestor(&parent, scroll_location))
}
}
/// A data structure which stores compositor-side information about
/// display lists sent to the compositor.
#[derive(Debug, Deserialize, Serialize)]
pub struct CompositorDisplayListInfo {
/// The WebRender [PipelineId] of this display list.
pub pipeline_id: PipelineId,
/// The size of the viewport that this display list renders into.
pub viewport_size: LayoutSize,
/// The size of this display list's content.
pub content_size: LayoutSize,
/// The epoch of the display list.
pub epoch: Epoch,
/// 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>,
/// A ScrollTree used by the compositor to scroll the contents of the
/// display list.
pub scroll_tree: ScrollTree,
/// The `ScrollTreeNodeId` of the root reference frame of this info's scroll
/// tree.
pub root_reference_frame_id: ScrollTreeNodeId,
/// The `ScrollTreeNodeId` of the topmost scrolling frame of this info's scroll
/// tree.
pub root_scroll_node_id: ScrollTreeNodeId,
}
impl CompositorDisplayListInfo {
/// Create a new CompositorDisplayListInfo with the root reference frame
/// and scroll frame already added to the scroll tree.
pub fn new(
viewport_size: LayoutSize,
content_size: LayoutSize,
pipeline_id: PipelineId,
epoch: Epoch,
) -> Self {
let mut scroll_tree = ScrollTree::default();
let root_reference_frame_id = scroll_tree.add_scroll_tree_node(
None,
SpatialId::root_reference_frame(pipeline_id),
None,
);
let root_scroll_node_id = scroll_tree.add_scroll_tree_node(
Some(&root_reference_frame_id),
SpatialId::root_scroll_node(pipeline_id),
Some(ScrollableNodeInfo {
external_id: ExternalScrollId(0, pipeline_id),
scrollable_size: content_size - viewport_size,
scroll_sensitivity: ScrollSensitivity::ScriptAndInputEvents,
offset: LayoutVector2D::zero(),
}),
);
CompositorDisplayListInfo {
pipeline_id,
viewport_size,
content_size,
epoch,
hit_test_info: Default::default(),
scroll_tree,
root_reference_frame_id,
root_scroll_node_id,
}
}
/// 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>,
scroll_tree_node: ScrollTreeNodeId,
) -> 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,
scroll_tree_node,
});
self.hit_test_info.len() - 1
}
}