mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Add a compositor-side scroll tree
This will allow the compositor to properly chain scrolling requests up when a node has reached the extent of the scroll area. This fixes scrolling on servo.org.
This commit is contained in:
parent
918557ad6d
commit
c56a814806
12 changed files with 677 additions and 102 deletions
|
@ -18,7 +18,7 @@ use gfx::text::glyph::GlyphStore;
|
|||
use mitochondria::OnceCell;
|
||||
use msg::constellation_msg::BrowsingContextId;
|
||||
use net_traits::image_cache::UsePlaceholder;
|
||||
use script_traits::compositor::CompositorDisplayListInfo;
|
||||
use script_traits::compositor::{CompositorDisplayListInfo, ScrollTreeNodeId};
|
||||
use std::sync::Arc;
|
||||
use style::computed_values::text_decoration_style::T as ComputedTextDecorationStyle;
|
||||
use style::dom::OpaqueNode;
|
||||
|
@ -66,27 +66,28 @@ pub struct DisplayList {
|
|||
impl DisplayList {
|
||||
/// Create a new [DisplayList] given the dimensions of the layout and the WebRender
|
||||
/// pipeline id.
|
||||
///
|
||||
/// TODO(mrobinson): `_viewport_size` will eventually be used in the creation
|
||||
/// of the compositor-side scroll tree.
|
||||
pub fn new(
|
||||
_viewport_size: units::LayoutSize,
|
||||
viewport_size: units::LayoutSize,
|
||||
content_size: units::LayoutSize,
|
||||
pipeline_id: wr::PipelineId,
|
||||
) -> Self {
|
||||
Self {
|
||||
wr: wr::DisplayListBuilder::new(pipeline_id, content_size),
|
||||
compositor_info: CompositorDisplayListInfo::default(),
|
||||
compositor_info: CompositorDisplayListInfo::new(
|
||||
viewport_size,
|
||||
content_size,
|
||||
pipeline_id,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DisplayListBuilder<'a> {
|
||||
/// The current [wr::SpatialId] for this [DisplayListBuilder]. This allows
|
||||
/// only passing the builder instead passing the containing
|
||||
/// The current [ScrollTreeNodeId] for this [DisplayListBuilder]. This
|
||||
/// allows only passing the builder instead passing the containing
|
||||
/// [stacking_context::StackingContextFragment] as an argument to display
|
||||
/// list building functions.
|
||||
current_spatial_id: wr::SpatialId,
|
||||
current_scroll_node_id: ScrollTreeNodeId,
|
||||
|
||||
/// The current [wr::ClipId] for this [DisplayListBuilder]. This allows
|
||||
/// only passing the builder instead passing the containing
|
||||
|
@ -125,7 +126,7 @@ impl DisplayList {
|
|||
root_stacking_context: &StackingContext,
|
||||
) -> (FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>, bool) {
|
||||
let mut builder = DisplayListBuilder {
|
||||
current_spatial_id: wr::SpatialId::root_scroll_node(self.wr.pipeline_id),
|
||||
current_scroll_node_id: self.compositor_info.root_scroll_node_id,
|
||||
current_clip_id: wr::ClipId::root(self.wr.pipeline_id),
|
||||
element_for_canvas_background: fragment_tree.canvas_background.from_element,
|
||||
is_contentful: false,
|
||||
|
@ -153,7 +154,7 @@ impl<'a> DisplayListBuilder<'a> {
|
|||
// for fragments that paint their entire border rectangle.
|
||||
wr::CommonItemProperties {
|
||||
clip_rect,
|
||||
spatial_id: self.current_spatial_id,
|
||||
spatial_id: self.current_scroll_node_id.spatial_id,
|
||||
clip_id: self.current_clip_id,
|
||||
hit_info: None,
|
||||
flags: style.get_webrender_primitive_flags(),
|
||||
|
@ -176,6 +177,7 @@ impl<'a> DisplayListBuilder<'a> {
|
|||
let hit_test_index = self.display_list.compositor_info.add_hit_test_info(
|
||||
tag?.node.0 as u64,
|
||||
Some(cursor(inherited_ui.cursor.keyword, auto_cursor)),
|
||||
self.current_scroll_node_id,
|
||||
);
|
||||
Some((hit_test_index as u64, 0u16))
|
||||
}
|
||||
|
@ -866,7 +868,7 @@ fn clip_for_radii(
|
|||
None
|
||||
} else {
|
||||
let parent_space_and_clip = wr::SpaceAndClipInfo {
|
||||
spatial_id: builder.current_spatial_id,
|
||||
spatial_id: builder.current_scroll_node_id.spatial_id,
|
||||
clip_id: builder.current_clip_id,
|
||||
};
|
||||
Some(builder.wr().define_clip_rounded_rect(
|
||||
|
|
|
@ -12,6 +12,7 @@ use crate::geom::PhysicalRect;
|
|||
use crate::style_ext::ComputedValuesExt;
|
||||
use crate::FragmentTree;
|
||||
use euclid::default::Rect;
|
||||
use script_traits::compositor::{ScrollTreeNodeId, ScrollableNodeInfo};
|
||||
use servo_arc::Arc as ServoArc;
|
||||
use std::cmp::Ordering;
|
||||
use std::mem;
|
||||
|
@ -32,7 +33,7 @@ use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVecto
|
|||
pub(crate) struct ContainingBlock {
|
||||
/// The SpatialId of the spatial node that contains the children
|
||||
/// of this containing block.
|
||||
spatial_id: wr::SpatialId,
|
||||
scroll_node_id: ScrollTreeNodeId,
|
||||
|
||||
/// The WebRender ClipId to use for this children of this containing
|
||||
/// block.
|
||||
|
@ -45,11 +46,11 @@ pub(crate) struct ContainingBlock {
|
|||
impl ContainingBlock {
|
||||
pub(crate) fn new(
|
||||
rect: &PhysicalRect<Length>,
|
||||
spatial_id: wr::SpatialId,
|
||||
scroll_node_id: ScrollTreeNodeId,
|
||||
clip_id: wr::ClipId,
|
||||
) -> Self {
|
||||
ContainingBlock {
|
||||
spatial_id,
|
||||
scroll_node_id,
|
||||
clip_id,
|
||||
rect: *rect,
|
||||
}
|
||||
|
@ -77,12 +78,12 @@ impl DisplayList {
|
|||
pub fn build_stacking_context_tree(&mut self, fragment_tree: &FragmentTree) -> StackingContext {
|
||||
let cb_for_non_fixed_descendants = ContainingBlock::new(
|
||||
&fragment_tree.initial_containing_block,
|
||||
wr::SpatialId::root_scroll_node(self.wr.pipeline_id),
|
||||
self.compositor_info.root_scroll_node_id,
|
||||
wr::ClipId::root(self.wr.pipeline_id),
|
||||
);
|
||||
let cb_for_fixed_descendants = ContainingBlock::new(
|
||||
&fragment_tree.initial_containing_block,
|
||||
wr::SpatialId::root_reference_frame(self.wr.pipeline_id),
|
||||
self.compositor_info.root_reference_frame_id,
|
||||
wr::ClipId::root(self.wr.pipeline_id),
|
||||
);
|
||||
|
||||
|
@ -115,13 +116,23 @@ impl DisplayList {
|
|||
fn push_reference_frame(
|
||||
&mut self,
|
||||
origin: LayoutPoint,
|
||||
parent_spatial_id: &wr::SpatialId,
|
||||
parent_scroll_node_id: &ScrollTreeNodeId,
|
||||
transform_style: wr::TransformStyle,
|
||||
transform: wr::PropertyBinding<LayoutTransform>,
|
||||
kind: wr::ReferenceFrameKind,
|
||||
) -> wr::SpatialId {
|
||||
self.wr
|
||||
.push_reference_frame(origin, *parent_spatial_id, transform_style, transform, kind)
|
||||
) -> ScrollTreeNodeId {
|
||||
let new_spatial_id = self.wr.push_reference_frame(
|
||||
origin,
|
||||
parent_scroll_node_id.spatial_id,
|
||||
transform_style,
|
||||
transform,
|
||||
kind,
|
||||
);
|
||||
self.compositor_info.scroll_tree.add_scroll_tree_node(
|
||||
Some(parent_scroll_node_id),
|
||||
new_spatial_id,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn pop_reference_frame(&mut self) {
|
||||
|
@ -130,31 +141,41 @@ impl DisplayList {
|
|||
|
||||
fn define_scroll_frame(
|
||||
&mut self,
|
||||
parent_spatial_id: &wr::SpatialId,
|
||||
parent_scroll_node_id: &ScrollTreeNodeId,
|
||||
parent_clip_id: &wr::ClipId,
|
||||
external_id: Option<wr::ExternalScrollId>,
|
||||
external_id: wr::ExternalScrollId,
|
||||
content_rect: LayoutRect,
|
||||
clip_rect: LayoutRect,
|
||||
scroll_sensitivity: wr::ScrollSensitivity,
|
||||
external_scroll_offset: LayoutVector2D,
|
||||
) -> (wr::SpatialId, wr::ClipId) {
|
||||
) -> (ScrollTreeNodeId, wr::ClipId) {
|
||||
let new_space_and_clip = self.wr.define_scroll_frame(
|
||||
&wr::SpaceAndClipInfo {
|
||||
spatial_id: *parent_spatial_id,
|
||||
spatial_id: parent_scroll_node_id.spatial_id,
|
||||
clip_id: *parent_clip_id,
|
||||
},
|
||||
external_id,
|
||||
Some(external_id),
|
||||
content_rect,
|
||||
clip_rect,
|
||||
scroll_sensitivity,
|
||||
external_scroll_offset,
|
||||
);
|
||||
(new_space_and_clip.spatial_id, new_space_and_clip.clip_id)
|
||||
let new_scroll_node_id = self.compositor_info.scroll_tree.add_scroll_tree_node(
|
||||
Some(&parent_scroll_node_id),
|
||||
new_space_and_clip.spatial_id,
|
||||
Some(ScrollableNodeInfo {
|
||||
external_id,
|
||||
scrollable_size: content_rect.size - clip_rect.size,
|
||||
scroll_sensitivity,
|
||||
offset: LayoutVector2D::zero(),
|
||||
}),
|
||||
);
|
||||
(new_scroll_node_id, new_space_and_clip.clip_id)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct StackingContextFragment {
|
||||
spatial_id: wr::SpatialId,
|
||||
scroll_node_id: ScrollTreeNodeId,
|
||||
clip_id: wr::ClipId,
|
||||
section: StackingContextSection,
|
||||
containing_block: PhysicalRect<Length>,
|
||||
|
@ -163,7 +184,7 @@ pub(crate) struct StackingContextFragment {
|
|||
|
||||
impl StackingContextFragment {
|
||||
fn build_display_list(&self, builder: &mut DisplayListBuilder) {
|
||||
builder.current_spatial_id = self.spatial_id;
|
||||
builder.current_scroll_node_id = self.scroll_node_id;
|
||||
builder.current_clip_id = self.clip_id;
|
||||
self.fragment
|
||||
.borrow()
|
||||
|
@ -398,7 +419,7 @@ impl StackingContext {
|
|||
// The root element may have a CSS transform, and we want the canvas’
|
||||
// background image to be transformed. To do so, take its `SpatialId`
|
||||
// (but not its `ClipId`)
|
||||
builder.current_spatial_id = first_stacking_context_fragment.spatial_id;
|
||||
builder.current_scroll_node_id = first_stacking_context_fragment.scroll_node_id;
|
||||
|
||||
// Now we need express the painting area rectangle in the local coordinate system,
|
||||
// which differs from the top-level coordinate system based on…
|
||||
|
@ -559,7 +580,7 @@ impl Fragment {
|
|||
Fragment::Text(_) | Fragment::Image(_) | Fragment::IFrame(_) => {
|
||||
stacking_context.fragments.push(StackingContextFragment {
|
||||
section: StackingContextSection::Content,
|
||||
spatial_id: containing_block.spatial_id,
|
||||
scroll_node_id: containing_block.scroll_node_id,
|
||||
clip_id: containing_block.clip_id,
|
||||
containing_block: containing_block.rect,
|
||||
fragment: fragment_ref.clone(),
|
||||
|
@ -650,7 +671,7 @@ impl BoxFragment {
|
|||
|
||||
let new_spatial_id = display_list.push_reference_frame(
|
||||
reference_frame_data.origin.to_webrender(),
|
||||
&containing_block.spatial_id,
|
||||
&containing_block.scroll_node_id,
|
||||
self.style.get_box().transform_style.to_webrender(),
|
||||
wr::PropertyBinding::Value(reference_frame_data.transform),
|
||||
reference_frame_data.kind,
|
||||
|
@ -712,7 +733,7 @@ impl BoxFragment {
|
|||
};
|
||||
|
||||
let mut child_stacking_context = StackingContext::new(
|
||||
containing_block.spatial_id,
|
||||
containing_block.scroll_node_id.spatial_id,
|
||||
self.style.clone(),
|
||||
context_type,
|
||||
);
|
||||
|
@ -749,11 +770,11 @@ impl BoxFragment {
|
|||
containing_block_info: &ContainingBlockInfo,
|
||||
stacking_context: &mut StackingContext,
|
||||
) {
|
||||
let mut new_spatial_id = containing_block.spatial_id;
|
||||
let mut new_scroll_node_id = containing_block.scroll_node_id;
|
||||
let mut new_clip_id = containing_block.clip_id;
|
||||
if let Some(clip_id) = self.build_clip_frame_if_necessary(
|
||||
display_list,
|
||||
&new_spatial_id,
|
||||
&new_scroll_node_id,
|
||||
&new_clip_id,
|
||||
&containing_block.rect,
|
||||
) {
|
||||
|
@ -761,7 +782,7 @@ impl BoxFragment {
|
|||
}
|
||||
|
||||
stacking_context.fragments.push(StackingContextFragment {
|
||||
spatial_id: new_spatial_id,
|
||||
scroll_node_id: new_scroll_node_id,
|
||||
clip_id: new_clip_id,
|
||||
section: self.get_stacking_context_section(),
|
||||
containing_block: containing_block.rect,
|
||||
|
@ -769,7 +790,7 @@ impl BoxFragment {
|
|||
});
|
||||
if self.style.get_outline().outline_width.px() > 0.0 {
|
||||
stacking_context.fragments.push(StackingContextFragment {
|
||||
spatial_id: new_spatial_id,
|
||||
scroll_node_id: new_scroll_node_id,
|
||||
clip_id: new_clip_id,
|
||||
section: StackingContextSection::Outline,
|
||||
containing_block: containing_block.rect,
|
||||
|
@ -779,13 +800,13 @@ impl BoxFragment {
|
|||
|
||||
// We want to build the scroll frame after the background and border, because
|
||||
// they shouldn't scroll with the rest of the box content.
|
||||
if let Some((spatial_id, clip_id)) = self.build_scroll_frame_if_necessary(
|
||||
if let Some((scroll_node_id, clip_id)) = self.build_scroll_frame_if_necessary(
|
||||
display_list,
|
||||
&new_spatial_id,
|
||||
&new_scroll_node_id,
|
||||
&new_clip_id,
|
||||
&containing_block.rect,
|
||||
) {
|
||||
new_spatial_id = spatial_id;
|
||||
new_scroll_node_id = scroll_node_id;
|
||||
new_clip_id = clip_id;
|
||||
}
|
||||
|
||||
|
@ -799,9 +820,9 @@ impl BoxFragment {
|
|||
.translate(containing_block.rect.origin.to_vector());
|
||||
|
||||
let for_absolute_descendants =
|
||||
ContainingBlock::new(&padding_rect, new_spatial_id, new_clip_id);
|
||||
ContainingBlock::new(&padding_rect, new_scroll_node_id, new_clip_id);
|
||||
let for_non_absolute_descendants =
|
||||
ContainingBlock::new(&content_rect, new_spatial_id, new_clip_id);
|
||||
ContainingBlock::new(&content_rect, new_scroll_node_id, new_clip_id);
|
||||
|
||||
// Create a new `ContainingBlockInfo` for descendants depending on
|
||||
// whether or not this fragment establishes a containing block for
|
||||
|
@ -840,7 +861,7 @@ impl BoxFragment {
|
|||
fn build_clip_frame_if_necessary(
|
||||
&self,
|
||||
display_list: &mut DisplayList,
|
||||
parent_spatial_id: &wr::SpatialId,
|
||||
parent_scroll_node_id: &ScrollTreeNodeId,
|
||||
parent_clip_id: &wr::ClipId,
|
||||
containing_block_rect: &PhysicalRect<Length>,
|
||||
) -> Option<wr::ClipId> {
|
||||
|
@ -867,7 +888,7 @@ impl BoxFragment {
|
|||
|
||||
Some(display_list.wr.define_clip_rect(
|
||||
&wr::SpaceAndClipInfo {
|
||||
spatial_id: *parent_spatial_id,
|
||||
spatial_id: parent_scroll_node_id.spatial_id,
|
||||
clip_id: *parent_clip_id,
|
||||
},
|
||||
clip_rect,
|
||||
|
@ -877,10 +898,10 @@ impl BoxFragment {
|
|||
fn build_scroll_frame_if_necessary(
|
||||
&self,
|
||||
display_list: &mut DisplayList,
|
||||
parent_spatial_id: &wr::SpatialId,
|
||||
parent_scroll_node_id: &ScrollTreeNodeId,
|
||||
parent_clip_id: &wr::ClipId,
|
||||
containing_block_rect: &PhysicalRect<Length>,
|
||||
) -> Option<(wr::SpatialId, wr::ClipId)> {
|
||||
) -> Option<(ScrollTreeNodeId, wr::ClipId)> {
|
||||
let overflow_x = self.style.get_box().overflow_x;
|
||||
let overflow_y = self.style.get_box().overflow_y;
|
||||
if overflow_x == ComputedOverflow::Visible && overflow_y == ComputedOverflow::Visible {
|
||||
|
@ -908,9 +929,9 @@ impl BoxFragment {
|
|||
|
||||
Some(
|
||||
display_list.define_scroll_frame(
|
||||
parent_spatial_id,
|
||||
parent_scroll_node_id,
|
||||
parent_clip_id,
|
||||
Some(external_id),
|
||||
external_id,
|
||||
self.scrollable_overflow(&containing_block_rect)
|
||||
.to_webrender(),
|
||||
padding_rect,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue