aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout_2020/display_list/stacking_context.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/layout_2020/display_list/stacking_context.rs')
-rw-r--r--components/layout_2020/display_list/stacking_context.rs224
1 files changed, 187 insertions, 37 deletions
diff --git a/components/layout_2020/display_list/stacking_context.rs b/components/layout_2020/display_list/stacking_context.rs
index 22d7a5679e6..0432fa288d7 100644
--- a/components/layout_2020/display_list/stacking_context.rs
+++ b/components/layout_2020/display_list/stacking_context.rs
@@ -6,6 +6,7 @@ use std::cell::RefCell;
use std::mem;
use euclid::default::Rect;
+use euclid::SideOffsets2D;
use gfx_traits::print_tree::PrintTree;
use log::warn;
use script_traits::compositor::{ScrollTreeNodeId, ScrollableNodeInfo};
@@ -23,6 +24,8 @@ use style::values::specified::box_::DisplayOutside;
use webrender_api as wr;
use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D};
use webrender_api::ScrollSensitivity;
+use wr::units::{LayoutPixel, LayoutSize};
+use wr::StickyOffsetBounds;
use super::DisplayList;
use crate::cell::ArcRefCell;
@@ -31,7 +34,7 @@ use crate::display_list::DisplayListBuilder;
use crate::fragment_tree::{
AnonymousFragment, BoxFragment, ContainingBlockManager, Fragment, FragmentTree,
};
-use crate::geom::PhysicalRect;
+use crate::geom::{PhysicalRect, PhysicalSides};
use crate::style_ext::ComputedValuesExt;
#[derive(Clone)]
@@ -40,6 +43,11 @@ pub(crate) struct ContainingBlock {
/// of this containing block.
scroll_node_id: ScrollTreeNodeId,
+ /// The size of the parent scroll frame of this containing block, used for resolving
+ /// sticky margins. If this is None, then this is a direct descendant of a reference
+ /// frame and sticky positioning isn't taken into account.
+ scroll_frame_size: Option<LayoutSize>,
+
/// The WebRender ClipId to use for this children of this containing
/// block.
clip_chain_id: wr::ClipChainId,
@@ -50,14 +58,16 @@ pub(crate) struct ContainingBlock {
impl ContainingBlock {
pub(crate) fn new(
- rect: &PhysicalRect<Length>,
+ rect: PhysicalRect<Length>,
scroll_node_id: ScrollTreeNodeId,
+ scroll_frame_size: Option<LayoutSize>,
clip_chain_id: wr::ClipChainId,
) -> Self {
ContainingBlock {
scroll_node_id,
+ scroll_frame_size,
clip_chain_id,
- rect: *rect,
+ rect,
}
}
@@ -90,13 +100,15 @@ impl DisplayList {
.define_clip_chain(None, [wr::ClipId::root(self.wr.pipeline_id)]);
let cb_for_non_fixed_descendants = ContainingBlock::new(
- &fragment_tree.initial_containing_block,
+ fragment_tree.initial_containing_block,
self.compositor_info.root_scroll_node_id,
+ Some(self.compositor_info.viewport_size),
root_clip_chain_id,
);
let cb_for_fixed_descendants = ContainingBlock::new(
- &fragment_tree.initial_containing_block,
+ fragment_tree.initial_containing_block,
self.compositor_info.root_reference_frame_id,
+ None,
root_clip_chain_id,
);
@@ -114,7 +126,7 @@ impl DisplayList {
let mut root_stacking_context = StackingContext::create_root(&self.wr, debug);
for fragment in &fragment_tree.root_fragments {
- fragment.borrow().build_stacking_context_tree(
+ fragment.borrow_mut().build_stacking_context_tree(
fragment,
self,
&containing_block_info,
@@ -197,6 +209,29 @@ impl DisplayList {
);
(new_scroll_node_id, new_clip_chain_id)
}
+
+ fn define_sticky_frame(
+ &mut self,
+ parent_scroll_node_id: &ScrollTreeNodeId,
+ frame_rect: LayoutRect,
+ margins: SideOffsets2D<Option<f32>, LayoutPixel>,
+ vertical_offset_bounds: StickyOffsetBounds,
+ horizontal_offset_bounds: StickyOffsetBounds,
+ ) -> ScrollTreeNodeId {
+ let new_spatial_id = self.wr.define_sticky_frame(
+ parent_scroll_node_id.spatial_id,
+ frame_rect,
+ margins,
+ vertical_offset_bounds,
+ horizontal_offset_bounds,
+ LayoutVector2D::zero(),
+ );
+ self.compositor_info.scroll_tree.add_scroll_tree_node(
+ Some(parent_scroll_node_id),
+ new_spatial_id,
+ None,
+ )
+ }
}
/// A piece of content that directly belongs to a section of a stacking context.
@@ -769,7 +804,7 @@ pub(crate) enum StackingContextBuildMode {
impl Fragment {
pub(crate) fn build_stacking_context_tree(
- &self,
+ &mut self,
fragment_ref: &ArcRefCell<Fragment>,
display_list: &mut DisplayList,
containing_block_info: &ContainingBlockInfo,
@@ -810,7 +845,7 @@ impl Fragment {
None => unreachable!("Found hoisted box with missing fragment."),
};
- fragment_ref.borrow().build_stacking_context_tree(
+ fragment_ref.borrow_mut().build_stacking_context_tree(
fragment_ref,
display_list,
containing_block_info,
@@ -882,7 +917,7 @@ impl BoxFragment {
}
fn build_stacking_context_tree(
- &self,
+ &mut self,
fragment: &ArcRefCell<Fragment>,
display_list: &mut DisplayList,
containing_block: &ContainingBlock,
@@ -899,7 +934,7 @@ impl BoxFragment {
}
fn build_stacking_context_tree_maybe_creating_reference_frame(
- &self,
+ &mut self,
fragment: &ArcRefCell<Fragment>,
display_list: &mut DisplayList,
containing_block: &ContainingBlock,
@@ -941,10 +976,11 @@ impl BoxFragment {
.style
.establishes_containing_block_for_all_descendants());
let adjusted_containing_block = ContainingBlock::new(
- &containing_block
+ containing_block
.rect
.translate(-reference_frame_data.origin.to_vector()),
new_spatial_id,
+ None,
containing_block.clip_chain_id,
);
let new_containing_block_info =
@@ -962,7 +998,7 @@ impl BoxFragment {
}
fn build_stacking_context_tree_maybe_creating_stacking_context(
- &self,
+ &mut self,
fragment: &ArcRefCell<Fragment>,
display_list: &mut DisplayList,
containing_block: &ContainingBlock,
@@ -1025,7 +1061,7 @@ impl BoxFragment {
}
fn build_stacking_context_tree_for_children(
- &self,
+ &mut self,
fragment: &ArcRefCell<Fragment>,
display_list: &mut DisplayList,
containing_block: &ContainingBlock,
@@ -1034,6 +1070,19 @@ impl BoxFragment {
) {
let mut new_scroll_node_id = containing_block.scroll_node_id;
let mut new_clip_chain_id = containing_block.clip_chain_id;
+ let mut new_scroll_frame_size = containing_block_info
+ .for_non_absolute_descendants
+ .scroll_frame_size;
+
+ if let Some(scroll_node_id) = self.build_sticky_frame_if_necessary(
+ display_list,
+ &new_scroll_node_id,
+ &containing_block.rect,
+ &new_scroll_frame_size,
+ ) {
+ new_scroll_node_id = scroll_node_id;
+ }
+
if let Some(clip_chain_id) = self.build_clip_frame_if_necessary(
display_list,
&new_scroll_node_id,
@@ -1067,14 +1116,17 @@ 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((scroll_node_id, clip_chain_id)) = self.build_scroll_frame_if_necessary(
- display_list,
- &new_scroll_node_id,
- &new_clip_chain_id,
- &containing_block.rect,
- ) {
+ if let Some((scroll_node_id, clip_chain_id, scroll_frame_size)) = self
+ .build_scroll_frame_if_necessary(
+ display_list,
+ &new_scroll_node_id,
+ &new_clip_chain_id,
+ &containing_block.rect,
+ )
+ {
new_scroll_node_id = scroll_node_id;
new_clip_chain_id = clip_chain_id;
+ new_scroll_frame_size = Some(scroll_frame_size);
}
let padding_rect = self
@@ -1086,10 +1138,18 @@ impl BoxFragment {
.to_physical(self.style.writing_mode, &containing_block.rect)
.translate(containing_block.rect.origin.to_vector());
- let for_absolute_descendants =
- ContainingBlock::new(&padding_rect, new_scroll_node_id, new_clip_chain_id);
- let for_non_absolute_descendants =
- ContainingBlock::new(&content_rect, new_scroll_node_id, new_clip_chain_id);
+ let for_absolute_descendants = ContainingBlock::new(
+ padding_rect,
+ new_scroll_node_id,
+ new_scroll_frame_size,
+ new_clip_chain_id,
+ );
+ let for_non_absolute_descendants = ContainingBlock::new(
+ content_rect,
+ new_scroll_node_id,
+ new_scroll_frame_size,
+ new_clip_chain_id,
+ );
// Create a new `ContainingBlockInfo` for descendants depending on
// whether or not this fragment establishes a containing block for
@@ -1115,7 +1175,7 @@ impl BoxFragment {
};
for child in &self.children {
- child.borrow().build_stacking_context_tree(
+ child.borrow_mut().build_stacking_context_tree(
child,
display_list,
&new_containing_block_info,
@@ -1174,7 +1234,7 @@ impl BoxFragment {
parent_scroll_node_id: &ScrollTreeNodeId,
parent_clip_id: &wr::ClipChainId,
containing_block_rect: &PhysicalRect<Length>,
- ) -> Option<(ScrollTreeNodeId, wr::ClipChainId)> {
+ ) -> Option<(ScrollTreeNodeId, wr::ClipChainId, LayoutSize)> {
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 {
@@ -1200,17 +1260,107 @@ impl BoxFragment {
.translate(containing_block_rect.origin.to_vector())
.to_webrender();
- Some(
- display_list.define_scroll_frame(
- parent_scroll_node_id,
- parent_clip_id,
- external_id,
- self.scrollable_overflow(&containing_block_rect)
- .to_webrender(),
- padding_rect,
- sensitivity,
- ),
- )
+ let (scroll_tree_node_id, clip_chain_id) = display_list.define_scroll_frame(
+ parent_scroll_node_id,
+ parent_clip_id,
+ external_id,
+ self.scrollable_overflow(&containing_block_rect)
+ .to_webrender(),
+ padding_rect,
+ sensitivity,
+ );
+
+ Some((scroll_tree_node_id, clip_chain_id, padding_rect.size))
+ }
+
+ fn build_sticky_frame_if_necessary(
+ &mut self,
+ display_list: &mut DisplayList,
+ parent_scroll_node_id: &ScrollTreeNodeId,
+ containing_block_rect: &PhysicalRect<Length>,
+ scroll_frame_size: &Option<LayoutSize>,
+ ) -> Option<ScrollTreeNodeId> {
+ if self.style.get_box().position != ComputedPosition::Sticky {
+ return None;
+ }
+
+ let scroll_frame_size_for_resolve = match scroll_frame_size {
+ Some(size) => size,
+ None => {
+ // This is a direct descendant of a reference frame.
+ &display_list.compositor_info.viewport_size
+ },
+ };
+
+ // Percentages sticky positions offsets are resovled against the size of the
+ // nearest scroll frame instead of the containing block like for other types
+ // of positioning.
+ let position = self.style.get_position();
+ let offsets = PhysicalSides::new(
+ position
+ .top
+ .map(|v| v.resolve(Length::new(scroll_frame_size_for_resolve.height))),
+ position
+ .right
+ .map(|v| v.resolve(Length::new(scroll_frame_size_for_resolve.width))),
+ position
+ .bottom
+ .map(|v| v.resolve(Length::new(scroll_frame_size_for_resolve.height))),
+ position
+ .left
+ .map(|v| v.resolve(Length::new(scroll_frame_size_for_resolve.width))),
+ );
+ self.resolved_sticky_insets = Some(offsets);
+
+ if scroll_frame_size.is_none() {
+ return None;
+ }
+
+ if offsets.top.is_auto() &&
+ offsets.right.is_auto() &&
+ offsets.bottom.is_auto() &&
+ offsets.left.is_auto()
+ {
+ return None;
+ }
+
+ let frame_rect = self
+ .border_rect()
+ .to_physical(self.style.writing_mode, &containing_block_rect)
+ .translate(containing_block_rect.origin.to_vector())
+ .to_webrender();
+
+ // Position:sticky elements are always restricted based on the size and position of their
+ // containing block.
+ let containing_block_rect = containing_block_rect.to_webrender();
+
+ // This is the minimum negative offset and then the maximum positive offset. We just
+ // specify every edge, but if the corresponding margin is None, that offset has no effect.
+ let vertical_offset_bounds = wr::StickyOffsetBounds::new(
+ containing_block_rect.min_y() - frame_rect.min_y(),
+ containing_block_rect.max_y() - frame_rect.max_y(),
+ );
+ let horizontal_offset_bounds = wr::StickyOffsetBounds::new(
+ containing_block_rect.min_x() - frame_rect.min_x(),
+ containing_block_rect.max_x() - frame_rect.max_x(),
+ );
+
+ let margins = SideOffsets2D::new(
+ offsets.top.non_auto().map(|v| v.px()),
+ offsets.right.non_auto().map(|v| v.px()),
+ offsets.bottom.non_auto().map(|v| v.px()),
+ offsets.left.non_auto().map(|v| v.px()),
+ );
+
+ let sticky_node_id = display_list.define_sticky_frame(
+ parent_scroll_node_id,
+ frame_rect,
+ margins,
+ vertical_offset_bounds,
+ horizontal_offset_bounds,
+ );
+
+ Some(sticky_node_id)
}
/// Optionally returns the data for building a reference frame, without yet building it.
@@ -1363,7 +1513,7 @@ impl AnonymousFragment {
containing_block_info.new_for_non_absolute_descendants(&new_containing_block);
for child in &self.children {
- child.borrow().build_stacking_context_tree(
+ child.borrow_mut().build_stacking_context_tree(
child,
display_list,
&new_containing_block_info,