diff options
Diffstat (limited to 'components/layout')
-rw-r--r-- | components/layout/display_list/background.rs | 4 | ||||
-rw-r--r-- | components/layout/display_list/clip.rs | 276 | ||||
-rw-r--r-- | components/layout/display_list/clip_path.rs | 259 | ||||
-rw-r--r-- | components/layout/display_list/mod.rs | 392 | ||||
-rw-r--r-- | components/layout/display_list/stacking_context.rs | 441 | ||||
-rw-r--r-- | components/layout/dom.rs | 14 | ||||
-rw-r--r-- | components/layout/flexbox/mod.rs | 6 | ||||
-rw-r--r-- | components/layout/flow/inline/construct.rs | 2 | ||||
-rw-r--r-- | components/layout/flow/inline/mod.rs | 23 | ||||
-rw-r--r-- | components/layout/flow/mod.rs | 22 | ||||
-rw-r--r-- | components/layout/formatting_contexts.rs | 16 | ||||
-rw-r--r-- | components/layout/fragment_tree/box_fragment.rs | 2 | ||||
-rw-r--r-- | components/layout/fragment_tree/fragment_tree.rs | 11 | ||||
-rw-r--r-- | components/layout/layout_impl.rs | 125 | ||||
-rw-r--r-- | components/layout/table/mod.rs | 5 | ||||
-rw-r--r-- | components/layout/taffy/mod.rs | 6 | ||||
-rw-r--r-- | components/layout/traversal.rs | 5 |
17 files changed, 876 insertions, 733 deletions
diff --git a/components/layout/display_list/background.rs b/components/layout/display_list/background.rs index f49ddfbe6ce..563bce28450 100644 --- a/components/layout/display_list/background.rs +++ b/components/layout/display_list/background.rs @@ -66,7 +66,7 @@ impl<'a> BackgroundPainter<'a> { if &BackgroundAttachment::Fixed == get_cyclic(&background.background_attachment.0, layer_index) { - let viewport_size = builder.display_list.compositor_info.viewport_size; + let viewport_size = builder.compositor_info.viewport_size; return units::LayoutRect::from_origin_and_size(Point2D::origin(), viewport_size); } @@ -121,7 +121,7 @@ impl<'a> BackgroundPainter<'a> { if &BackgroundAttachment::Fixed == get_cyclic(&style.get_background().background_attachment.0, layer_index) { - common.spatial_id = builder.current_reference_frame_scroll_node_id.spatial_id; + common.spatial_id = builder.spatial_id(builder.current_reference_frame_scroll_node_id); } common } diff --git a/components/layout/display_list/clip.rs b/components/layout/display_list/clip.rs new file mode 100644 index 00000000000..d5bd0f52b69 --- /dev/null +++ b/components/layout/display_list/clip.rs @@ -0,0 +1,276 @@ +/* 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/. */ + +use app_units::Au; +use base::id::ScrollTreeNodeId; +use style::values::computed::basic_shape::{BasicShape, ClipPath}; +use style::values::computed::length_percentage::NonNegativeLengthPercentage; +use style::values::computed::position::Position; +use style::values::generics::basic_shape::{GenericShapeRadius, ShapeBox, ShapeGeometryBox}; +use style::values::generics::position::GenericPositionOrAuto; +use webrender_api::BorderRadius; +use webrender_api::units::{LayoutRect, LayoutSideOffsets, LayoutSize}; + +use super::{BuilderForBoxFragment, compute_margin_box_radius, normalize_radii}; + +/// An identifier for a clip used during StackingContextTree construction. This is a simple index in +/// a [`ClipStore`]s vector of clips. +#[derive(Clone, Copy, Debug, PartialEq)] +pub(crate) struct ClipId(pub usize); + +impl ClipId { + /// Equivalent to [`ClipChainId::INVALID`]. This means "no clip." + pub(crate) const INVALID: ClipId = ClipId(usize::MAX); +} + +/// All the information needed to create a clip on a WebRender display list. These are created at +/// two times: during `StackingContextTree` creation and during WebRender display list construction. +/// Only the former are stored in a [`ClipStore`]. +#[derive(Clone)] +pub(crate) struct Clip { + pub id: ClipId, + pub radii: BorderRadius, + pub rect: LayoutRect, + pub parent_scroll_node_id: ScrollTreeNodeId, + pub parent_clip_id: ClipId, +} + +/// A simple vector of [`Clip`] that is built during `StackingContextTree` construction. +/// These are later turned into WebRender clips and clip chains during WebRender display +/// list construction. +#[derive(Clone, Default)] +pub(crate) struct StackingContextTreeClipStore(pub Vec<Clip>); + +impl StackingContextTreeClipStore { + pub(crate) fn add( + &mut self, + radii: webrender_api::BorderRadius, + rect: LayoutRect, + parent_scroll_node_id: ScrollTreeNodeId, + parent_clip_id: ClipId, + ) -> ClipId { + let id = ClipId(self.0.len()); + self.0.push(Clip { + id, + radii, + rect, + parent_scroll_node_id, + parent_clip_id, + }); + id + } + + pub(super) fn add_for_clip_path( + &mut self, + clip_path: ClipPath, + parent_scroll_node_id: &ScrollTreeNodeId, + parent_clip_chain_id: &ClipId, + fragment_builder: BuilderForBoxFragment, + ) -> Option<ClipId> { + let geometry_box = match clip_path { + ClipPath::Shape(_, ShapeGeometryBox::ShapeBox(shape_box)) => shape_box, + ClipPath::Shape(_, ShapeGeometryBox::ElementDependent) => ShapeBox::BorderBox, + ClipPath::Box(ShapeGeometryBox::ShapeBox(shape_box)) => shape_box, + ClipPath::Box(ShapeGeometryBox::ElementDependent) => ShapeBox::BorderBox, + _ => return None, + }; + let layout_rect = match geometry_box { + ShapeBox::BorderBox => fragment_builder.border_rect, + ShapeBox::ContentBox => *fragment_builder.content_rect(), + ShapeBox::PaddingBox => *fragment_builder.padding_rect(), + ShapeBox::MarginBox => *fragment_builder.margin_rect(), + }; + if let ClipPath::Shape(shape, _) = clip_path { + match *shape { + BasicShape::Circle(_) | BasicShape::Ellipse(_) | BasicShape::Rect(_) => self + .add_for_basic_shape( + *shape, + layout_rect, + parent_scroll_node_id, + parent_clip_chain_id, + ), + BasicShape::Polygon(_) | BasicShape::PathOrShape(_) => None, + } + } else { + Some(self.add( + match geometry_box { + ShapeBox::MarginBox => compute_margin_box_radius( + fragment_builder.border_radius, + layout_rect.size(), + fragment_builder.fragment, + ), + _ => fragment_builder.border_radius, + }, + layout_rect, + *parent_scroll_node_id, + *parent_clip_chain_id, + )) + } + } + + #[cfg_attr( + feature = "tracing", + tracing::instrument( + name = "StackingContextClipStore::add_for_basic_shape", + skip_all, + fields(servo_profiling = true), + level = "trace", + ) + )] + fn add_for_basic_shape( + &mut self, + shape: BasicShape, + layout_box: LayoutRect, + parent_scroll_node_id: &ScrollTreeNodeId, + parent_clip_chain_id: &ClipId, + ) -> Option<ClipId> { + match shape { + BasicShape::Rect(rect) => { + let box_height = Au::from_f32_px(layout_box.height()); + let box_width = Au::from_f32_px(layout_box.width()); + let insets = LayoutSideOffsets::new( + rect.rect.0.to_used_value(box_height).to_f32_px(), + rect.rect.1.to_used_value(box_width).to_f32_px(), + rect.rect.2.to_used_value(box_height).to_f32_px(), + rect.rect.3.to_used_value(box_width).to_f32_px(), + ); + + // `inner_rect()` will cause an assertion failure if the insets are larger than the + // rectangle dimension. + let shape_rect = if insets.left + insets.right >= layout_box.width() || + insets.top + insets.bottom > layout_box.height() + { + LayoutRect::from_origin_and_size(layout_box.min, LayoutSize::zero()) + } else { + layout_box.to_rect().inner_rect(insets).to_box2d() + }; + + let corner = |corner: &style::values::computed::BorderCornerRadius| { + LayoutSize::new( + corner.0.width.0.to_used_value(box_width).to_f32_px(), + corner.0.height.0.to_used_value(box_height).to_f32_px(), + ) + }; + let mut radii = webrender_api::BorderRadius { + top_left: corner(&rect.round.top_left), + top_right: corner(&rect.round.top_right), + bottom_left: corner(&rect.round.bottom_left), + bottom_right: corner(&rect.round.bottom_right), + }; + normalize_radii(&layout_box, &mut radii); + Some(self.add( + radii, + shape_rect, + *parent_scroll_node_id, + *parent_clip_chain_id, + )) + }, + BasicShape::Circle(circle) => { + let center = match circle.position { + GenericPositionOrAuto::Position(position) => position, + GenericPositionOrAuto::Auto => Position::center(), + }; + let anchor_x = center + .horizontal + .to_used_value(Au::from_f32_px(layout_box.width())); + let anchor_y = center + .vertical + .to_used_value(Au::from_f32_px(layout_box.height())); + let center = layout_box + .min + .add_size(&LayoutSize::new(anchor_x.to_f32_px(), anchor_y.to_f32_px())); + + let horizontal = compute_shape_radius( + center.x, + &circle.radius, + layout_box.min.x, + layout_box.max.x, + ); + let vertical = compute_shape_radius( + center.y, + &circle.radius, + layout_box.min.y, + layout_box.max.y, + ); + + // If the value is `Length` then both values should be the same at this point. + let radius = match circle.radius { + GenericShapeRadius::FarthestSide => horizontal.max(vertical), + GenericShapeRadius::ClosestSide => horizontal.min(vertical), + GenericShapeRadius::Length(_) => horizontal, + }; + let radius = LayoutSize::new(radius, radius); + let mut radii = webrender_api::BorderRadius { + top_left: radius, + top_right: radius, + bottom_left: radius, + bottom_right: radius, + }; + let start = center.add_size(&-radius); + let rect = LayoutRect::from_origin_and_size(start, radius * 2.); + normalize_radii(&layout_box, &mut radii); + Some(self.add(radii, rect, *parent_scroll_node_id, *parent_clip_chain_id)) + }, + BasicShape::Ellipse(ellipse) => { + let center = match ellipse.position { + GenericPositionOrAuto::Position(position) => position, + GenericPositionOrAuto::Auto => Position::center(), + }; + let anchor_x = center + .horizontal + .to_used_value(Au::from_f32_px(layout_box.width())); + let anchor_y = center + .vertical + .to_used_value(Au::from_f32_px(layout_box.height())); + let center = layout_box + .min + .add_size(&LayoutSize::new(anchor_x.to_f32_px(), anchor_y.to_f32_px())); + + let width = compute_shape_radius( + center.x, + &ellipse.semiaxis_x, + layout_box.min.x, + layout_box.max.x, + ); + let height = compute_shape_radius( + center.y, + &ellipse.semiaxis_y, + layout_box.min.y, + layout_box.max.y, + ); + + let mut radii = webrender_api::BorderRadius { + top_left: LayoutSize::new(width, height), + top_right: LayoutSize::new(width, height), + bottom_left: LayoutSize::new(width, height), + bottom_right: LayoutSize::new(width, height), + }; + let size = LayoutSize::new(width, height); + let start = center.add_size(&-size); + let rect = LayoutRect::from_origin_and_size(start, size * 2.); + normalize_radii(&rect, &mut radii); + Some(self.add(radii, rect, *parent_scroll_node_id, *parent_clip_chain_id)) + }, + _ => None, + } + } +} + +fn compute_shape_radius( + center: f32, + radius: &GenericShapeRadius<NonNegativeLengthPercentage>, + min_edge: f32, + max_edge: f32, +) -> f32 { + let distance_from_min_edge = (min_edge - center).abs(); + let distance_from_max_edge = (max_edge - center).abs(); + match radius { + GenericShapeRadius::FarthestSide => distance_from_min_edge.max(distance_from_max_edge), + GenericShapeRadius::ClosestSide => distance_from_min_edge.min(distance_from_max_edge), + GenericShapeRadius::Length(length) => length + .0 + .to_used_value(Au::from_f32_px(max_edge - min_edge)) + .to_f32_px(), + } +} diff --git a/components/layout/display_list/clip_path.rs b/components/layout/display_list/clip_path.rs deleted file mode 100644 index 419d15c3572..00000000000 --- a/components/layout/display_list/clip_path.rs +++ /dev/null @@ -1,259 +0,0 @@ -/* 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/. */ - -use app_units::Au; -use base::id::ScrollTreeNodeId; -use style::values::computed::basic_shape::{BasicShape, ClipPath}; -use style::values::computed::length_percentage::NonNegativeLengthPercentage; -use style::values::computed::position::Position; -use style::values::generics::basic_shape::{GenericShapeRadius, ShapeBox, ShapeGeometryBox}; -use style::values::generics::position::GenericPositionOrAuto; -use webrender_api::ClipChainId; -use webrender_api::units::{LayoutRect, LayoutSideOffsets, LayoutSize}; - -use super::{BuilderForBoxFragment, DisplayList, compute_margin_box_radius, normalize_radii}; - -pub(super) fn build_clip_path_clip_chain_if_necessary( - clip_path: ClipPath, - display_list: &mut DisplayList, - parent_scroll_node_id: &ScrollTreeNodeId, - parent_clip_chain_id: &ClipChainId, - fragment_builder: BuilderForBoxFragment, -) -> Option<ClipChainId> { - let geometry_box = match clip_path { - ClipPath::Shape(_, ShapeGeometryBox::ShapeBox(shape_box)) => shape_box, - ClipPath::Shape(_, ShapeGeometryBox::ElementDependent) => ShapeBox::BorderBox, - ClipPath::Box(ShapeGeometryBox::ShapeBox(shape_box)) => shape_box, - ClipPath::Box(ShapeGeometryBox::ElementDependent) => ShapeBox::BorderBox, - _ => return None, - }; - let layout_rect = match geometry_box { - ShapeBox::BorderBox => fragment_builder.border_rect, - ShapeBox::ContentBox => *fragment_builder.content_rect(), - ShapeBox::PaddingBox => *fragment_builder.padding_rect(), - ShapeBox::MarginBox => *fragment_builder.margin_rect(), - }; - if let ClipPath::Shape(shape, _) = clip_path { - match *shape { - BasicShape::Circle(_) | BasicShape::Ellipse(_) | BasicShape::Rect(_) => { - build_simple_shape( - *shape, - layout_rect, - parent_scroll_node_id, - parent_clip_chain_id, - display_list, - ) - }, - BasicShape::Polygon(_) | BasicShape::PathOrShape(_) => None, - } - } else { - Some(create_rect_clip_chain( - match geometry_box { - ShapeBox::MarginBox => compute_margin_box_radius( - fragment_builder.border_radius, - layout_rect.size(), - fragment_builder.fragment, - ), - _ => fragment_builder.border_radius, - }, - layout_rect, - parent_scroll_node_id, - parent_clip_chain_id, - display_list, - )) - } -} - -#[cfg_attr( - feature = "tracing", - tracing::instrument( - name = "display_list::build_simple_shape", - skip_all, - fields(servo_profiling = true), - level = "trace", - ) -)] -fn build_simple_shape( - shape: BasicShape, - layout_box: LayoutRect, - parent_scroll_node_id: &ScrollTreeNodeId, - parent_clip_chain_id: &ClipChainId, - display_list: &mut DisplayList, -) -> Option<ClipChainId> { - match shape { - BasicShape::Rect(rect) => { - let box_height = Au::from_f32_px(layout_box.height()); - let box_width = Au::from_f32_px(layout_box.width()); - let insets = LayoutSideOffsets::new( - rect.rect.0.to_used_value(box_height).to_f32_px(), - rect.rect.1.to_used_value(box_width).to_f32_px(), - rect.rect.2.to_used_value(box_height).to_f32_px(), - rect.rect.3.to_used_value(box_width).to_f32_px(), - ); - - // `inner_rect()` will cause an assertion failure if the insets are larger than the - // rectangle dimension. - let shape_rect = if insets.left + insets.right >= layout_box.width() || - insets.top + insets.bottom > layout_box.height() - { - LayoutRect::from_origin_and_size(layout_box.min, LayoutSize::zero()) - } else { - layout_box.to_rect().inner_rect(insets).to_box2d() - }; - - let corner = |corner: &style::values::computed::BorderCornerRadius| { - LayoutSize::new( - corner.0.width.0.to_used_value(box_width).to_f32_px(), - corner.0.height.0.to_used_value(box_height).to_f32_px(), - ) - }; - let mut radii = webrender_api::BorderRadius { - top_left: corner(&rect.round.top_left), - top_right: corner(&rect.round.top_right), - bottom_left: corner(&rect.round.bottom_left), - bottom_right: corner(&rect.round.bottom_right), - }; - normalize_radii(&layout_box, &mut radii); - Some(create_rect_clip_chain( - radii, - shape_rect, - parent_scroll_node_id, - parent_clip_chain_id, - display_list, - )) - }, - BasicShape::Circle(circle) => { - let center = match circle.position { - GenericPositionOrAuto::Position(position) => position, - GenericPositionOrAuto::Auto => Position::center(), - }; - let anchor_x = center - .horizontal - .to_used_value(Au::from_f32_px(layout_box.width())); - let anchor_y = center - .vertical - .to_used_value(Au::from_f32_px(layout_box.height())); - let center = layout_box - .min - .add_size(&LayoutSize::new(anchor_x.to_f32_px(), anchor_y.to_f32_px())); - - let horizontal = - compute_shape_radius(center.x, &circle.radius, layout_box.min.x, layout_box.max.x); - let vertical = - compute_shape_radius(center.y, &circle.radius, layout_box.min.y, layout_box.max.y); - - // If the value is `Length` then both values should be the same at this point. - let radius = match circle.radius { - GenericShapeRadius::FarthestSide => horizontal.max(vertical), - GenericShapeRadius::ClosestSide => horizontal.min(vertical), - GenericShapeRadius::Length(_) => horizontal, - }; - let radius = LayoutSize::new(radius, radius); - let mut radii = webrender_api::BorderRadius { - top_left: radius, - top_right: radius, - bottom_left: radius, - bottom_right: radius, - }; - let start = center.add_size(&-radius); - let rect = LayoutRect::from_origin_and_size(start, radius * 2.); - normalize_radii(&layout_box, &mut radii); - Some(create_rect_clip_chain( - radii, - rect, - parent_scroll_node_id, - parent_clip_chain_id, - display_list, - )) - }, - BasicShape::Ellipse(ellipse) => { - let center = match ellipse.position { - GenericPositionOrAuto::Position(position) => position, - GenericPositionOrAuto::Auto => Position::center(), - }; - let anchor_x = center - .horizontal - .to_used_value(Au::from_f32_px(layout_box.width())); - let anchor_y = center - .vertical - .to_used_value(Au::from_f32_px(layout_box.height())); - let center = layout_box - .min - .add_size(&LayoutSize::new(anchor_x.to_f32_px(), anchor_y.to_f32_px())); - - let width = compute_shape_radius( - center.x, - &ellipse.semiaxis_x, - layout_box.min.x, - layout_box.max.x, - ); - let height = compute_shape_radius( - center.y, - &ellipse.semiaxis_y, - layout_box.min.y, - layout_box.max.y, - ); - - let mut radii = webrender_api::BorderRadius { - top_left: LayoutSize::new(width, height), - top_right: LayoutSize::new(width, height), - bottom_left: LayoutSize::new(width, height), - bottom_right: LayoutSize::new(width, height), - }; - let size = LayoutSize::new(width, height); - let start = center.add_size(&-size); - let rect = LayoutRect::from_origin_and_size(start, size * 2.); - normalize_radii(&rect, &mut radii); - Some(create_rect_clip_chain( - radii, - rect, - parent_scroll_node_id, - parent_clip_chain_id, - display_list, - )) - }, - _ => None, - } -} - -fn compute_shape_radius( - center: f32, - radius: &GenericShapeRadius<NonNegativeLengthPercentage>, - min_edge: f32, - max_edge: f32, -) -> f32 { - let distance_from_min_edge = (min_edge - center).abs(); - let distance_from_max_edge = (max_edge - center).abs(); - match radius { - GenericShapeRadius::FarthestSide => distance_from_min_edge.max(distance_from_max_edge), - GenericShapeRadius::ClosestSide => distance_from_min_edge.min(distance_from_max_edge), - GenericShapeRadius::Length(length) => length - .0 - .to_used_value(Au::from_f32_px(max_edge - min_edge)) - .to_f32_px(), - } -} -fn create_rect_clip_chain( - radii: webrender_api::BorderRadius, - rect: LayoutRect, - parent_scroll_node_id: &ScrollTreeNodeId, - parent_clip_chain_id: &ClipChainId, - display_list: &mut DisplayList, -) -> ClipChainId { - let new_clip_id = if radii.is_zero() { - display_list - .wr - .define_clip_rect(parent_scroll_node_id.spatial_id, rect) - } else { - display_list.wr.define_clip_rounded_rect( - parent_scroll_node_id.spatial_id, - webrender_api::ComplexClipRegion { - rect, - radii, - mode: webrender_api::ClipMode::Clip, - }, - ) - }; - display_list.define_clip_chain(*parent_clip_chain_id, [new_clip_id]) -} diff --git a/components/layout/display_list/mod.rs b/components/layout/display_list/mod.rs index f017642908d..95689cf1186 100644 --- a/components/layout/display_list/mod.rs +++ b/components/layout/display_list/mod.rs @@ -8,13 +8,15 @@ use std::sync::Arc; use app_units::{AU_PER_PX, Au}; use base::WebRenderEpochToU16; use base::id::ScrollTreeNodeId; -use compositing_traits::display_list::{AxesScrollSensitivity, CompositorDisplayListInfo}; +use clip::{Clip, ClipId}; +use compositing_traits::display_list::{CompositorDisplayListInfo, SpatialTreeNodeInfo}; use embedder_traits::Cursor; use euclid::{Point2D, SideOffsets2D, Size2D, UnknownUnit}; use fonts::GlyphStore; use gradient::WebRenderGradient; use range::Range as ServoRange; use servo_arc::Arc as ServoArc; +use servo_config::opts::DebugOptions; use servo_geometry::MaxRect; use style::Zero; use style::color::{AbsoluteColor, ColorSpace}; @@ -35,8 +37,9 @@ use style::values::specified::ui::CursorKind; use style_traits::CSSPixel; use webrender_api::units::{DevicePixel, LayoutPixel, LayoutRect, LayoutSize}; use webrender_api::{ - self as wr, BorderDetails, BoxShadowClipMode, ClipChainId, CommonItemProperties, - ImageRendering, NinePatchBorder, NinePatchBorderSource, SpatialId, units, + self as wr, BorderDetails, BoxShadowClipMode, BuiltDisplayList, ClipChainId, ClipMode, + CommonItemProperties, ComplexClipRegion, ImageRendering, NinePatchBorder, + NinePatchBorderSource, PropertyBinding, SpatialId, SpatialTreeItemKey, units, }; use wr::units::LayoutVector2D; @@ -55,7 +58,7 @@ use crate::replaced::NaturalSizes; use crate::style_ext::{BorderStyleColor, ComputedValuesExt}; mod background; -mod clip_path; +mod clip; mod conversions; mod gradient; mod stacking_context; @@ -74,68 +77,6 @@ type ItemTag = (u64, u16); type HitInfo = Option<ItemTag>; const INSERTION_POINT_LOGICAL_WIDTH: Au = Au(AU_PER_PX); -/// Where the information that's used to build display lists is stored. This -/// includes both a [wr::DisplayListBuilder] for building up WebRender-specific -/// display list information and a [CompositorDisplayListInfo] used to store -/// information used by the compositor, such as a compositor-side scroll tree. -pub struct DisplayList { - /// The [wr::DisplayListBuilder] used to collect display list items. - pub wr: wr::DisplayListBuilder, - - /// The information about the WebRender display list that the compositor - /// consumes. This curerntly contains the out-of-band hit testing information - /// data structure that the compositor uses to map hit tests to information - /// about the item hit. - pub compositor_info: CompositorDisplayListInfo, - - /// A count of the number of SpatialTree nodes pushed to the WebRender display - /// list. This is merely to ensure that the currently-unused SpatialTreeItemKey - /// produced for every SpatialTree node is unique. - pub spatial_tree_count: u64, -} - -impl DisplayList { - /// Create a new [DisplayList] given the dimensions of the layout and the WebRender - /// pipeline id. - pub fn new( - viewport_size: units::LayoutSize, - content_size: units::LayoutSize, - pipeline_id: wr::PipelineId, - epoch: wr::Epoch, - viewport_scroll_sensitivity: AxesScrollSensitivity, - first_reflow: bool, - ) -> Self { - Self { - wr: wr::DisplayListBuilder::new(pipeline_id), - compositor_info: CompositorDisplayListInfo::new( - viewport_size, - content_size, - pipeline_id, - epoch, - viewport_scroll_sensitivity, - first_reflow, - ), - spatial_tree_count: 0, - } - } - - pub fn define_clip_chain<I>(&mut self, parent: ClipChainId, clips: I) -> ClipChainId - where - I: IntoIterator<Item = wr::ClipId>, - I::IntoIter: ExactSizeIterator + Clone, - { - // WebRender has two different ways of expressing "no clip." ClipChainId::INVALID should be - // used for primitives, but `None` is used for stacking contexts and clip chains. We convert - // to the `Option<ClipChainId>` representation here. Just passing Some(ClipChainId::INVALID) - // leads to a crash. - let parent = match parent { - ClipChainId::INVALID => None, - parent => Some(parent), - }; - self.wr.define_clip_chain(parent, clips) - } -} - pub(crate) struct DisplayListBuilder<'a> { /// The current [ScrollTreeNodeId] for this [DisplayListBuilder]. This /// allows only passing the builder instead passing the containing @@ -148,18 +89,21 @@ pub(crate) struct DisplayListBuilder<'a> { /// `background-attachment: fixed` need to not scroll while the rest of the fragment does. current_reference_frame_scroll_node_id: ScrollTreeNodeId, - /// The current [wr::ClipId] for this [DisplayListBuilder]. This allows + /// The current [`ClipId`] for this [DisplayListBuilder]. This allows /// only passing the builder instead passing the containing /// [stacking_context::StackingContextContent::Fragment] as an argument to display /// list building functions. - current_clip_chain_id: ClipChainId, + current_clip_id: ClipId, /// A [LayoutContext] used to get information about the device pixel ratio /// and get handles to WebRender images. pub context: &'a LayoutContext<'a>, - /// The [DisplayList] used to collect display list items and metadata. - pub display_list: &'a mut DisplayList, + /// The [`wr::DisplayListBuilder`] for this Servo [`DisplayListBuilder`]. + pub webrender_display_list_builder: &'a mut wr::DisplayListBuilder, + + /// The [`CompositorDisplayListInfo`] used to collect display list items and metadata. + pub compositor_info: &'a mut CompositorDisplayListInfo, /// Data about the fragments that are highlighted by the inspector, if any. /// @@ -171,6 +115,10 @@ pub(crate) struct DisplayListBuilder<'a> { /// element inherits the `<body>`'s background to paint the page canvas background. /// See <https://drafts.csswg.org/css-backgrounds/#body-background>. paint_body_background: bool, + + /// A mapping from [`ClipId`] To WebRender [`ClipChainId`] used when building this WebRender + /// display list. + clip_map: Vec<ClipChainId>, } struct InspectorHighlight { @@ -207,45 +155,224 @@ impl InspectorHighlight { } } -impl DisplayList { - pub fn build( - &mut self, +impl DisplayListBuilder<'_> { + pub(crate) fn build( context: &LayoutContext, + stacking_context_tree: &mut StackingContextTree, fragment_tree: &FragmentTree, - root_stacking_context: &StackingContext, - ) { + debug: &DebugOptions, + ) -> BuiltDisplayList { + // Build the rest of the display list which inclues all of the WebRender primitives. + let compositor_info = &mut stacking_context_tree.compositor_info; + compositor_info.hit_test_info.clear(); + + let mut webrender_display_list_builder = + webrender_api::DisplayListBuilder::new(compositor_info.pipeline_id); + webrender_display_list_builder.begin(); + + // `dump_serialized_display_list` doesn't actually print anything. It sets up + // the display list for printing the serialized version when `finalize()` is called. + // We need to call this before adding any display items so that they are printed + // during `finalize()`. + if debug.dump_display_list { + webrender_display_list_builder.dump_serialized_display_list(); + } + #[cfg(feature = "tracing")] - let _span = tracing::trace_span!("display_list::build", servo_profiling = true).entered(); + let _span = + tracing::trace_span!("DisplayListBuilder::build", servo_profiling = true).entered(); let mut builder = DisplayListBuilder { - current_scroll_node_id: self.compositor_info.root_reference_frame_id, - current_reference_frame_scroll_node_id: self.compositor_info.root_reference_frame_id, - current_clip_chain_id: ClipChainId::INVALID, + current_scroll_node_id: compositor_info.root_reference_frame_id, + current_reference_frame_scroll_node_id: compositor_info.root_reference_frame_id, + current_clip_id: ClipId::INVALID, context, - display_list: self, + webrender_display_list_builder: &mut webrender_display_list_builder, + compositor_info, inspector_highlight: context .highlighted_dom_node .map(InspectorHighlight::for_node), paint_body_background: true, + clip_map: Default::default(), }; - fragment_tree.build_display_list(&mut builder, root_stacking_context); - if let Some(highlight) = builder - .inspector_highlight - .take() - .and_then(|highlight| highlight.state) - { - builder.paint_dom_inspector_highlight(highlight); + builder.add_all_spatial_nodes(); + + for clip in stacking_context_tree.clip_store.0.iter() { + builder.add_clip_to_display_list(clip); } + + // Paint the canvas’ background (if any) before/under everything else + stacking_context_tree + .root_stacking_context + .build_canvas_background_display_list(&mut builder, fragment_tree); + stacking_context_tree + .root_stacking_context + .build_display_list(&mut builder); + builder.paint_dom_inspector_highlight(); + + webrender_display_list_builder.end().1 } -} -impl DisplayListBuilder<'_> { fn wr(&mut self) -> &mut wr::DisplayListBuilder { - &mut self.display_list.wr + self.webrender_display_list_builder + } + + fn pipeline_id(&mut self) -> wr::PipelineId { + self.compositor_info.pipeline_id } fn mark_is_contentful(&mut self) { - self.display_list.compositor_info.is_contentful = true; + self.compositor_info.is_contentful = true; + } + + fn spatial_id(&self, id: ScrollTreeNodeId) -> SpatialId { + self.compositor_info.scroll_tree.webrender_id(&id) + } + + fn clip_chain_id(&self, id: ClipId) -> ClipChainId { + match id { + ClipId::INVALID => ClipChainId::INVALID, + _ => *self + .clip_map + .get(id.0) + .expect("Should never try to get clip before adding it to WebRender display list"), + } + } + + pub(crate) fn add_all_spatial_nodes(&mut self) { + // A count of the number of SpatialTree nodes pushed to the WebRender display + // list. This is merely to ensure that the currently-unused SpatialTreeItemKey + // produced for every SpatialTree node is unique. + let mut spatial_tree_count = 0; + let mut scroll_tree = std::mem::take(&mut self.compositor_info.scroll_tree); + let mut mapping = Vec::with_capacity(scroll_tree.nodes.len()); + + mapping.push(SpatialId::root_reference_frame(self.pipeline_id())); + mapping.push(SpatialId::root_scroll_node(self.pipeline_id())); + + let pipeline_id = self.pipeline_id(); + let pipeline_tag = ((pipeline_id.0 as u64) << 32) | pipeline_id.1 as u64; + + for node in scroll_tree.nodes.iter().skip(2) { + let parent_scroll_node_id = node + .parent + .expect("Should have already added root reference frame"); + let parent_spatial_node_id = mapping + .get(parent_scroll_node_id.index) + .expect("Should add spatial nodes to display list in order"); + + // Produce a new SpatialTreeItemKey. This is currently unused by WebRender, + // but has to be unique to the entire scene. + spatial_tree_count += 1; + let spatial_tree_item_key = SpatialTreeItemKey::new(pipeline_tag, spatial_tree_count); + + mapping.push(match &node.info { + SpatialTreeNodeInfo::ReferenceFrame(info) => { + let spatial_id = self.wr().push_reference_frame( + info.origin, + *parent_spatial_node_id, + info.transform_style, + PropertyBinding::Value(info.transform), + info.kind, + spatial_tree_item_key, + ); + self.wr().pop_reference_frame(); + spatial_id + }, + SpatialTreeNodeInfo::Scroll(info) => { + self.wr().define_scroll_frame( + *parent_spatial_node_id, + info.external_id, + info.content_rect, + info.clip_rect, + LayoutVector2D::zero(), /* external_scroll_offset */ + 0, /* scroll_offset_generation */ + wr::HasScrollLinkedEffect::No, + spatial_tree_item_key, + ) + }, + SpatialTreeNodeInfo::Sticky(info) => { + self.wr().define_sticky_frame( + *parent_spatial_node_id, + info.frame_rect, + info.margins, + info.vertical_offset_bounds, + info.horizontal_offset_bounds, + LayoutVector2D::zero(), /* previously_applied_offset */ + spatial_tree_item_key, + None, /* transform */ + ) + }, + }); + } + + scroll_tree.update_mapping(mapping); + self.compositor_info.scroll_tree = scroll_tree; + } + + /// Add the given [`Clip`] to the WebRender display list and create a mapping from + /// its [`ClipId`] to a WebRender [`ClipChainId`]. This happens: + /// - When WebRender display list construction starts: All clips created during the + /// `StackingContextTree` construction are added in one batch. These clips are used + /// for things such as `overflow: scroll` elements. + /// - When a clip is added during WebRender display list construction for individual + /// items. In that case, this is called by [`Self::maybe_create_clip`]. + pub(crate) fn add_clip_to_display_list(&mut self, clip: &Clip) -> ClipChainId { + assert_eq!( + clip.id.0, + self.clip_map.len(), + "Clips should be added in order" + ); + + let spatial_id = self.spatial_id(clip.parent_scroll_node_id); + let new_clip_id = if clip.radii.is_zero() { + self.wr().define_clip_rect(spatial_id, clip.rect) + } else { + self.wr().define_clip_rounded_rect( + spatial_id, + ComplexClipRegion { + rect: clip.rect, + radii: clip.radii, + mode: ClipMode::Clip, + }, + ) + }; + + // WebRender has two different ways of expressing "no clip." ClipChainId::INVALID should be + // used for primitives, but `None` is used for stacking contexts and clip chains. We convert + // to the `Option<ClipChainId>` representation here. Just passing Some(ClipChainId::INVALID) + // leads to a crash. + let parent_clip_chain_id = match self.clip_chain_id(clip.parent_clip_id) { + ClipChainId::INVALID => None, + parent => Some(parent), + }; + let clip_chain_id = self + .wr() + .define_clip_chain(parent_clip_chain_id, [new_clip_id]); + self.clip_map.push(clip_chain_id); + clip_chain_id + } + + /// Add a new clip to the WebRender display list being built. This only happens during + /// WebRender display list building and these clips should be added after all clips + /// from the `StackingContextTree` have already been processed. + fn maybe_create_clip( + &mut self, + radii: wr::BorderRadius, + rect: units::LayoutRect, + force_clip_creation: bool, + ) -> Option<ClipChainId> { + if radii.is_zero() && !force_clip_creation { + return None; + } + + Some(self.add_clip_to_display_list(&Clip { + id: ClipId(self.clip_map.len()), + radii, + rect, + parent_scroll_node_id: self.current_scroll_node_id, + parent_clip_id: self.current_clip_id, + })) } fn common_properties( @@ -258,8 +385,8 @@ impl DisplayListBuilder<'_> { // for fragments that paint their entire border rectangle. wr::CommonItemProperties { clip_rect, - spatial_id: self.current_scroll_node_id.spatial_id, - clip_chain_id: self.current_clip_chain_id, + spatial_id: self.spatial_id(self.current_scroll_node_id), + clip_chain_id: self.clip_chain_id(self.current_clip_id), flags: style.get_webrender_primitive_flags(), } } @@ -277,19 +404,24 @@ impl DisplayListBuilder<'_> { return None; } - let hit_test_index = self.display_list.compositor_info.add_hit_test_info( + let hit_test_index = self.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, - self.display_list.compositor_info.epoch.as_u16(), - )) + Some((hit_test_index as u64, self.compositor_info.epoch.as_u16())) } /// Draw highlights around the node that is currently hovered in the devtools. - fn paint_dom_inspector_highlight(&mut self, highlight: HighlightTraversalState) { + fn paint_dom_inspector_highlight(&mut self) { + let Some(highlight) = self + .inspector_highlight + .take() + .and_then(|highlight| highlight.state) + else { + return; + }; + const CONTENT_BOX_HIGHLIGHT_COLOR: webrender_api::ColorF = webrender_api::ColorF { r: 0.23, g: 0.7, @@ -327,8 +459,7 @@ impl DisplayListBuilder<'_> { flags: wr::PrimitiveFlags::default(), }; - self.display_list - .wr + self.wr() .push_rect(&properties, content_box, CONTENT_BOX_HIGHLIGHT_COLOR); // Highlight margin, border and padding @@ -442,12 +573,14 @@ impl Fragment { is_hit_test_for_scrollable_overflow: bool, is_collapsed_table_borders: bool, ) { + let spatial_id = builder.spatial_id(builder.current_scroll_node_id); + let clip_chain_id = builder.clip_chain_id(builder.current_clip_id); if let Some(inspector_highlight) = &mut builder.inspector_highlight { if self.tag() == Some(inspector_highlight.tag) { inspector_highlight.register_fragment_of_highlighted_dom_node( self, - builder.current_scroll_node_id.spatial_id, - builder.current_clip_chain_id, + spatial_id, + clip_chain_id, containing_block, ); } @@ -574,8 +707,8 @@ impl Fragment { None => return, }; - let clip_chain_id = builder.current_clip_chain_id; - let spatial_id = builder.current_scroll_node_id.spatial_id; + let clip_chain_id = builder.clip_chain_id(builder.current_clip_id); + let spatial_id = builder.spatial_id(builder.current_scroll_node_id); builder.wr().push_hit_test( rect.to_webrender(), clip_chain_id, @@ -765,8 +898,9 @@ impl Fragment { if text_decoration_style == ComputedTextDecorationStyle::MozNone { return; } - builder.display_list.wr.push_line( - &builder.common_properties(rect, parent_style), + let common_properties = builder.common_properties(rect, parent_style); + builder.wr().push_line( + &common_properties, &rect, wavy_line_thickness, wr::LineOrientation::Horizontal, @@ -878,12 +1012,8 @@ impl<'a> BuilderForBoxFragment<'a> { return Some(clip); } - let maybe_clip = create_clip_chain( - self.border_radius, - self.border_rect, - builder, - force_clip_creation, - ); + let maybe_clip = + builder.maybe_create_clip(self.border_radius, self.border_rect, force_clip_creation); *self.border_edge_clip_chain_id.borrow_mut() = maybe_clip; maybe_clip } @@ -899,7 +1029,7 @@ impl<'a> BuilderForBoxFragment<'a> { let radii = inner_radii(self.border_radius, self.fragment.border.to_webrender()); let maybe_clip = - create_clip_chain(radii, *self.padding_rect(), builder, force_clip_creation); + builder.maybe_create_clip(radii, *self.padding_rect(), force_clip_creation); *self.padding_edge_clip_chain_id.borrow_mut() = maybe_clip; maybe_clip } @@ -918,7 +1048,7 @@ impl<'a> BuilderForBoxFragment<'a> { (self.fragment.border + self.fragment.padding).to_webrender(), ); let maybe_clip = - create_clip_chain(radii, *self.content_rect(), builder, force_clip_creation); + builder.maybe_create_clip(radii, *self.content_rect(), force_clip_creation); *self.content_edge_clip_chain_id.borrow_mut() = maybe_clip; maybe_clip } @@ -1553,38 +1683,6 @@ fn offset_radii(mut radii: wr::BorderRadius, offset: f32) -> wr::BorderRadius { radii } -fn create_clip_chain( - radii: wr::BorderRadius, - rect: units::LayoutRect, - builder: &mut DisplayListBuilder, - force_clip_creation: bool, -) -> Option<ClipChainId> { - if radii.is_zero() && !force_clip_creation { - return None; - } - - let spatial_id = builder.current_scroll_node_id.spatial_id; - let parent_clip_chain_id = builder.current_clip_chain_id; - let new_clip_id = if radii.is_zero() { - builder.wr().define_clip_rect(spatial_id, rect) - } else { - builder.wr().define_clip_rounded_rect( - spatial_id, - wr::ComplexClipRegion { - rect, - radii, - mode: wr::ClipMode::Clip, - }, - ) - }; - - Some( - builder - .display_list - .define_clip_chain(parent_clip_chain_id, [new_clip_id]), - ) -} - /// Resolve the WebRender border-image outset area from the style values. fn resolve_border_image_outset( outset: BorderImageOutset, diff --git a/components/layout/display_list/stacking_context.rs b/components/layout/display_list/stacking_context.rs index 27fa73a680c..bcd882e3fcc 100644 --- a/components/layout/display_list/stacking_context.rs +++ b/components/layout/display_list/stacking_context.rs @@ -9,18 +9,19 @@ use std::mem; use app_units::Au; use base::id::ScrollTreeNodeId; use base::print_tree::PrintTree; -use compositing_traits::display_list::{AxesScrollSensitivity, ScrollableNodeInfo}; +use compositing_traits::display_list::{ + AxesScrollSensitivity, CompositorDisplayListInfo, ReferenceFrameNodeInfo, ScrollableNodeInfo, + SpatialTreeNodeInfo, StickyNodeInfo, +}; use euclid::SideOffsets2D; use euclid::default::{Point2D, Rect, Size2D}; use log::warn; -use servo_arc::Arc as ServoArc; use servo_config::opts::DebugOptions; use style::Zero; use style::computed_values::float::T as ComputedFloat; use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode; use style::computed_values::overflow_x::T as ComputedOverflow; use style::computed_values::position::T as ComputedPosition; -use style::properties::ComputedValues; use style::values::computed::angle::Angle; use style::values::computed::basic_shape::ClipPath; use style::values::computed::{ClipRectOrAuto, Length}; @@ -29,11 +30,12 @@ use style::values::generics::transform::{self, GenericRotate, GenericScale, Gene use style::values::specified::box_::DisplayOutside; use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D}; use webrender_api::{self as wr, BorderRadius}; +use wr::StickyOffsetBounds; use wr::units::{LayoutPixel, LayoutSize}; -use wr::{ClipChainId, SpatialTreeItemKey, StickyOffsetBounds}; -use super::DisplayList; -use super::clip_path::build_clip_path_clip_chain_if_necessary; +use super::ClipId; +use super::clip::StackingContextTreeClipStore; +use crate::ArcRefCell; use crate::display_list::conversions::{FilterToWebRender, ToWebRender}; use crate::display_list::{BuilderForBoxFragment, DisplayListBuilder, offset_radii}; use crate::fragment_tree::{ @@ -54,9 +56,8 @@ pub(crate) struct ContainingBlock { /// 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, + /// The [`ClipId`] to use for the children of this containing block. + clip_id: ClipId, /// The physical rect of this containing block. rect: PhysicalRect<Au>, @@ -67,12 +68,12 @@ impl ContainingBlock { rect: PhysicalRect<Au>, scroll_node_id: ScrollTreeNodeId, scroll_frame_size: Option<LayoutSize>, - clip_chain_id: wr::ClipChainId, + clip_id: ClipId, ) -> Self { ContainingBlock { scroll_node_id, scroll_frame_size, - clip_chain_id, + clip_id, rect, } } @@ -95,40 +96,56 @@ pub(crate) enum StackingContextSection { Outline, } -impl DisplayList { - /// Produce a new SpatialTreeItemKey. This is currently unused by WebRender, - /// but has to be unique to the entire scene. - fn get_next_spatial_tree_item_key(&mut self) -> SpatialTreeItemKey { - self.spatial_tree_count += 1; - let pipeline_tag = ((self.wr.pipeline_id.0 as u64) << 32) | self.wr.pipeline_id.1 as u64; - SpatialTreeItemKey::new(pipeline_tag, self.spatial_tree_count) - } +pub(crate) struct StackingContextTree { + /// The root stacking context of this [`StackingContextTree`]. + pub root_stacking_context: StackingContext, - #[cfg_attr( - feature = "tracing", - tracing::instrument( - name = "display_list::build_stacking_context_tree", - skip_all, - fields(servo_profiling = true), - level = "trace", - ) - )] - pub fn build_stacking_context_tree( - &mut self, + /// The information about the WebRender display list that the compositor + /// consumes. This curerntly contains the out-of-band hit testing information + /// data structure that the compositor uses to map hit tests to information + /// about the item hit. + pub compositor_info: CompositorDisplayListInfo, + + /// All of the clips collected for this [`StackingContextTree`]. These are added + /// for things like `overflow`. More clips may be created later during WebRender + /// display list construction, but they are never added here. + pub clip_store: StackingContextTreeClipStore, +} + +impl StackingContextTree { + /// Create a new [DisplayList] given the dimensions of the layout and the WebRender + /// pipeline id. + pub fn new( fragment_tree: &FragmentTree, + viewport_size: LayoutSize, + content_size: LayoutSize, + pipeline_id: wr::PipelineId, + viewport_scroll_sensitivity: AxesScrollSensitivity, + first_reflow: bool, debug: &DebugOptions, - ) -> StackingContext { + ) -> Self { + let compositor_info = CompositorDisplayListInfo::new( + viewport_size, + content_size, + pipeline_id, + // This epoch is set when the WebRender display list is built. For now use a dummy value. + wr::Epoch(0), + viewport_scroll_sensitivity, + first_reflow, + ); + + let root_scroll_node_id = compositor_info.root_scroll_node_id; let cb_for_non_fixed_descendants = ContainingBlock::new( fragment_tree.initial_containing_block, - self.compositor_info.root_scroll_node_id, - Some(self.compositor_info.viewport_size), - ClipChainId::INVALID, + root_scroll_node_id, + Some(compositor_info.viewport_size), + ClipId::INVALID, ); let cb_for_fixed_descendants = ContainingBlock::new( fragment_tree.initial_containing_block, - self.compositor_info.root_reference_frame_id, + compositor_info.root_reference_frame_id, None, - ClipChainId::INVALID, + ClipId::INVALID, ); // We need to specify all three containing blocks here, because absolute @@ -143,17 +160,31 @@ impl DisplayList { for_absolute_and_fixed_descendants: &cb_for_fixed_descendants, }; - let mut root_stacking_context = StackingContext::create_root(&self.wr, debug); + let mut stacking_context_tree = Self { + // This is just a temporary value that will be replaced once we have finished building the tree. + root_stacking_context: StackingContext::create_root(root_scroll_node_id, debug), + compositor_info, + clip_store: Default::default(), + }; + + let mut root_stacking_context = StackingContext::create_root(root_scroll_node_id, debug); for fragment in &fragment_tree.root_fragments { fragment.build_stacking_context_tree( - self, + &mut stacking_context_tree, &containing_block_info, &mut root_stacking_context, StackingContextBuildMode::SkipHoisted, ); } root_stacking_context.sort(); - root_stacking_context + + if debug.dump_stacking_context_tree { + root_stacking_context.debug_print(); + } + + stacking_context_tree.root_stacking_context = root_stacking_context; + + stacking_context_tree } fn push_reference_frame( @@ -161,53 +192,20 @@ impl DisplayList { origin: LayoutPoint, parent_scroll_node_id: &ScrollTreeNodeId, transform_style: wr::TransformStyle, - transform: wr::PropertyBinding<LayoutTransform>, + transform: LayoutTransform, kind: wr::ReferenceFrameKind, ) -> ScrollTreeNodeId { - let spatial_tree_item_key = self.get_next_spatial_tree_item_key(); - let new_spatial_id = self.wr.push_reference_frame( - origin, - parent_scroll_node_id.spatial_id, - transform_style, - transform, - kind, - spatial_tree_item_key, - ); self.compositor_info.scroll_tree.add_scroll_tree_node( Some(parent_scroll_node_id), - new_spatial_id, - None, + SpatialTreeNodeInfo::ReferenceFrame(ReferenceFrameNodeInfo { + origin, + transform_style, + transform, + kind, + }), ) } - fn pop_reference_frame(&mut self) { - self.wr.pop_reference_frame(); - } - - fn clip_overflow_frame( - &mut self, - parent_scroll_node_id: &ScrollTreeNodeId, - parent_clip_id: &ClipChainId, - clip_rect: LayoutRect, - radii: wr::BorderRadius, - ) -> ClipChainId { - let new_clip_id = if radii.is_zero() { - self.wr - .define_clip_rect(parent_scroll_node_id.spatial_id, clip_rect) - } else { - self.wr.define_clip_rounded_rect( - parent_scroll_node_id.spatial_id, - webrender_api::ComplexClipRegion { - rect: clip_rect, - radii, - mode: webrender_api::ClipMode::Clip, - }, - ) - }; - - self.define_clip_chain(*parent_clip_id, [new_clip_id]) - } - fn define_scroll_frame( &mut self, parent_scroll_node_id: &ScrollTreeNodeId, @@ -216,25 +214,12 @@ impl DisplayList { clip_rect: LayoutRect, scroll_sensitivity: AxesScrollSensitivity, ) -> ScrollTreeNodeId { - let spatial_tree_item_key = self.get_next_spatial_tree_item_key(); - - let new_spatial_id = self.wr.define_scroll_frame( - parent_scroll_node_id.spatial_id, - external_id, - content_rect, - clip_rect, - LayoutVector2D::zero(), /* external_scroll_offset */ - 0, /* scroll_offset_generation */ - wr::HasScrollLinkedEffect::No, - spatial_tree_item_key, - ); - self.compositor_info.scroll_tree.add_scroll_tree_node( Some(parent_scroll_node_id), - new_spatial_id, - Some(ScrollableNodeInfo { + SpatialTreeNodeInfo::Scroll(ScrollableNodeInfo { external_id, - scrollable_size: content_rect.size() - clip_rect.size(), + content_rect, + clip_rect, scroll_sensitivity, offset: LayoutVector2D::zero(), }), @@ -249,21 +234,14 @@ impl DisplayList { vertical_offset_bounds: StickyOffsetBounds, horizontal_offset_bounds: StickyOffsetBounds, ) -> ScrollTreeNodeId { - let spatial_tree_item_key = self.get_next_spatial_tree_item_key(); - 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(), /* previously_applied_offset */ - spatial_tree_item_key, - None, /* transform */ - ); self.compositor_info.scroll_tree.add_scroll_tree_node( Some(parent_scroll_node_id), - new_spatial_id, - None, + SpatialTreeNodeInfo::Sticky(StickyNodeInfo { + frame_rect, + margins, + vertical_offset_bounds, + horizontal_offset_bounds, + }), ) } } @@ -277,7 +255,7 @@ pub(crate) enum StackingContextContent { Fragment { scroll_node_id: ScrollTreeNodeId, reference_frame_scroll_node_id: ScrollTreeNodeId, - clip_chain_id: wr::ClipChainId, + clip_id: ClipId, section: StackingContextSection, containing_block: PhysicalRect<Au>, fragment: Fragment, @@ -308,7 +286,7 @@ impl StackingContextContent { Self::Fragment { scroll_node_id, reference_frame_scroll_node_id, - clip_chain_id, + clip_id, section, containing_block, fragment, @@ -317,7 +295,7 @@ impl StackingContextContent { } => { builder.current_scroll_node_id = *scroll_node_id; builder.current_reference_frame_scroll_node_id = *reference_frame_scroll_node_id; - builder.current_clip_chain_id = *clip_chain_id; + builder.current_clip_id = *clip_id; fragment.build_display_list( builder, containing_block, @@ -349,16 +327,14 @@ pub(crate) enum StackingContextType { pub struct StackingContext { /// The spatial id of this fragment. This is used to properly handle /// things like preserve-3d. - spatial_id: wr::SpatialId, + scroll_tree_node_id: ScrollTreeNodeId, /// The clip chain id of this stacking context if it has one. Used for filter clipping. - clip_chain_id: Option<wr::ClipChainId>, - - /// The style of the fragment that established this stacking context. - initializing_fragment_style: Option<ServoArc<ComputedValues>>, + clip_id: Option<ClipId>, - /// The [`FragmentFlags`] of the [`Fragment`] that established this stacking context. - initializing_fragment_flags: FragmentFlags, + /// The [`BoxFragment`] that established this stacking context. We store the fragment here + /// rather than just the style, so that incremental layout can automatically update the style. + initializing_fragment: Option<ArcRefCell<BoxFragment>>, /// The type of this stacking context. Used for collecting and sorting. context_type: StackingContextType, @@ -415,25 +391,23 @@ pub enum DebugPrintField { impl StackingContext { fn create_descendant( &self, - spatial_id: wr::SpatialId, - clip_chain_id: wr::ClipChainId, - initializing_fragment_style: ServoArc<ComputedValues>, - initializing_fragment_flags: FragmentFlags, + spatial_id: ScrollTreeNodeId, + clip_id: ClipId, + initializing_fragment: ArcRefCell<BoxFragment>, context_type: StackingContextType, ) -> Self { // WebRender has two different ways of expressing "no clip." ClipChainId::INVALID should be // used for primitives, but `None` is used for stacking contexts and clip chains. We convert - // to the `Option<ClipChainId>` representation here. Just passing Some(ClipChainId::INVALID) + // to the `Option<ClipId>` representation here. Just passing Some(ClipChainId::INVALID) // leads to a crash. - let clip_chain_id: Option<ClipChainId> = match clip_chain_id { - ClipChainId::INVALID => None, - clip_chain_id => Some(clip_chain_id), + let clip_id = match clip_id { + ClipId::INVALID => None, + clip_id => Some(clip_id), }; Self { - spatial_id, - clip_chain_id, - initializing_fragment_style: Some(initializing_fragment_style), - initializing_fragment_flags, + scroll_tree_node_id: spatial_id, + clip_id, + initializing_fragment: Some(initializing_fragment), context_type, contents: vec![], real_stacking_contexts_and_positioned_stacking_containers: vec![], @@ -443,12 +417,11 @@ impl StackingContext { } } - pub(crate) fn create_root(wr: &wr::DisplayListBuilder, debug: &DebugOptions) -> Self { + fn create_root(root_scroll_node_id: ScrollTreeNodeId, debug: &DebugOptions) -> Self { Self { - spatial_id: wr::SpaceAndClipInfo::root_scroll(wr.pipeline_id).spatial_id, - clip_chain_id: None, - initializing_fragment_style: None, - initializing_fragment_flags: FragmentFlags::empty(), + scroll_tree_node_id: root_scroll_node_id, + clip_id: None, + initializing_fragment: None, context_type: StackingContextType::RealStackingContext, contents: vec![], real_stacking_contexts_and_positioned_stacking_containers: vec![], @@ -476,11 +449,10 @@ impl StackingContext { } fn z_index(&self) -> i32 { - self.initializing_fragment_style - .as_ref() - .map_or(0, |style| { - style.effective_z_index(self.initializing_fragment_flags) - }) + self.initializing_fragment.as_ref().map_or(0, |fragment| { + let fragment = fragment.borrow(); + fragment.style.effective_z_index(fragment.base.flags) + }) } pub(crate) fn sort(&mut self) { @@ -519,13 +491,14 @@ impl StackingContext { &self, builder: &mut DisplayListBuilder, ) -> bool { - let style = match self.initializing_fragment_style.as_ref() { - Some(style) => style, + let fragment = match self.initializing_fragment.as_ref() { + Some(fragment) => fragment.borrow(), None => return false, }; // WebRender only uses the stacking context to apply certain effects. If we don't // actually need to create a stacking context, just avoid creating one. + let style = &fragment.style; let effects = style.get_effects(); if effects.filter.0.is_empty() && effects.opacity == 1.0 && @@ -557,11 +530,13 @@ impl StackingContext { // This will require additional tracking during layout // before we start collecting stacking contexts so that // information will be available when we reach this point. + let spatial_id = builder.spatial_id(self.scroll_tree_node_id); + let clip_chain_id = self.clip_id.map(|clip_id| builder.clip_chain_id(clip_id)); builder.wr().push_stacking_context( LayoutPoint::zero(), // origin - self.spatial_id, + spatial_id, style.get_webrender_primitive_flags(), - self.clip_chain_id, + clip_chain_id, style.get_used_transform_style().to_webrender(), effects.mix_blend_mode.to_webrender(), &filters, @@ -635,10 +610,7 @@ impl StackingContext { if background_color.alpha > 0.0 { let common = builder.common_properties(painting_area, &source_style); let color = super::rgba(background_color); - builder - .display_list - .wr - .push_rect(&common, painting_area, color) + builder.wr().push_rect(&common, painting_area, color) } let mut fragment_builder = BuilderForBoxFragment::new( @@ -741,7 +713,7 @@ impl StackingContext { } if pushed_context { - builder.display_list.wr.pop_stacking_context(); + builder.wr().pop_stacking_context(); } } @@ -824,7 +796,7 @@ pub(crate) enum StackingContextBuildMode { impl Fragment { pub(crate) fn build_stacking_context_tree( &self, - display_list: &mut DisplayList, + stacking_context_tree: &mut StackingContextTree, containing_block_info: &ContainingBlockInfo, stacking_context: &mut StackingContext, mode: StackingContextBuildMode, @@ -852,7 +824,7 @@ impl Fragment { fragment.build_stacking_context_tree( fragment_clone, - display_list, + stacking_context_tree, containing_block, containing_block_info, stacking_context, @@ -866,7 +838,7 @@ impl Fragment { }; fragment_ref.build_stacking_context_tree( - display_list, + stacking_context_tree, containing_block_info, stacking_context, StackingContextBuildMode::IncludeHoisted, @@ -875,7 +847,7 @@ impl Fragment { Fragment::Positioning(fragment) => { let fragment = fragment.borrow(); fragment.build_stacking_context_tree( - display_list, + stacking_context_tree, containing_block, containing_block_info, stacking_context, @@ -890,7 +862,7 @@ impl Fragment { reference_frame_scroll_node_id: containing_block_info .for_absolute_and_fixed_descendants .scroll_node_id, - clip_chain_id: containing_block.clip_chain_id, + clip_id: containing_block.clip_id, containing_block: containing_block.rect, fragment: fragment_clone, is_hit_test_for_scrollable_overflow: false, @@ -912,7 +884,7 @@ struct ScrollFrameData { } struct OverflowFrameData { - clip_chain_id: wr::ClipChainId, + clip_id: ClipId, scroll_frame_data: Option<ScrollFrameData>, } @@ -953,14 +925,14 @@ impl BoxFragment { fn build_stacking_context_tree( &self, fragment: Fragment, - display_list: &mut DisplayList, + stacking_context_tree: &mut StackingContextTree, containing_block: &ContainingBlock, containing_block_info: &ContainingBlockInfo, parent_stacking_context: &mut StackingContext, ) { self.build_stacking_context_tree_maybe_creating_reference_frame( fragment, - display_list, + stacking_context_tree, containing_block, containing_block_info, parent_stacking_context, @@ -970,7 +942,7 @@ impl BoxFragment { fn build_stacking_context_tree_maybe_creating_reference_frame( &self, fragment: Fragment, - display_list: &mut DisplayList, + stacking_context_tree: &mut StackingContextTree, containing_block: &ContainingBlock, containing_block_info: &ContainingBlockInfo, parent_stacking_context: &mut StackingContext, @@ -981,7 +953,7 @@ impl BoxFragment { None => { return self.build_stacking_context_tree_maybe_creating_stacking_context( fragment, - display_list, + stacking_context_tree, containing_block, containing_block_info, parent_stacking_context, @@ -989,11 +961,11 @@ impl BoxFragment { }, }; - let new_spatial_id = display_list.push_reference_frame( + let new_spatial_id = stacking_context_tree.push_reference_frame( reference_frame_data.origin.to_webrender(), &containing_block.scroll_node_id, self.style.get_box().transform_style.to_webrender(), - wr::PropertyBinding::Value(reference_frame_data.transform), + reference_frame_data.transform, reference_frame_data.kind, ); @@ -1016,26 +988,24 @@ impl BoxFragment { .translate(-reference_frame_data.origin.to_vector()), new_spatial_id, None, - containing_block.clip_chain_id, + containing_block.clip_id, ); let new_containing_block_info = containing_block_info.new_for_non_absolute_descendants(&adjusted_containing_block); self.build_stacking_context_tree_maybe_creating_stacking_context( fragment, - display_list, + stacking_context_tree, &adjusted_containing_block, &new_containing_block_info, parent_stacking_context, ); - - display_list.pop_reference_frame(); } fn build_stacking_context_tree_maybe_creating_stacking_context( &self, fragment: Fragment, - display_list: &mut DisplayList, + stacking_context_tree: &mut StackingContextTree, containing_block: &ContainingBlock, containing_block_info: &ContainingBlockInfo, parent_stacking_context: &mut StackingContext, @@ -1045,7 +1015,7 @@ impl BoxFragment { None => { self.build_stacking_context_tree_for_children( fragment, - display_list, + stacking_context_tree, containing_block, containing_block_info, parent_stacking_context, @@ -1068,30 +1038,37 @@ impl BoxFragment { // `clip-path` needs to be applied before filters and creates a stacking context, so it can be // applied directly to the stacking context itself. // before - let stacking_context_clip_chain_id = build_clip_path_clip_chain_if_necessary( - self.style.clone_clip_path(), - display_list, - &containing_block.scroll_node_id, - &containing_block.clip_chain_id, - BuilderForBoxFragment::new( - self, - &containing_block.rect, - false, /* is_hit_test_for_scrollable_overflow */ - false, /* is_collapsed_table_borders */ - ), - ) - .unwrap_or(containing_block.clip_chain_id); + let stacking_context_clip_id = stacking_context_tree + .clip_store + .add_for_clip_path( + self.style.clone_clip_path(), + &containing_block.scroll_node_id, + &containing_block.clip_id, + BuilderForBoxFragment::new( + self, + &containing_block.rect, + false, /* is_hit_test_for_scrollable_overflow */ + false, /* is_collapsed_table_borders */ + ), + ) + .unwrap_or(containing_block.clip_id); + + let box_fragment = match fragment { + Fragment::Box(ref box_fragment) | Fragment::Float(ref box_fragment) => { + box_fragment.clone() + }, + _ => unreachable!("Should never try to make stacking context for non-BoxFragment"), + }; let mut child_stacking_context = parent_stacking_context.create_descendant( - containing_block.scroll_node_id.spatial_id, - stacking_context_clip_chain_id, - self.style.clone(), - self.base.flags, + containing_block.scroll_node_id, + stacking_context_clip_id, + box_fragment, context_type, ); self.build_stacking_context_tree_for_children( fragment, - display_list, + stacking_context_tree, containing_block, containing_block_info, &mut child_stacking_context, @@ -1116,19 +1093,19 @@ impl BoxFragment { fn build_stacking_context_tree_for_children( &self, fragment: Fragment, - display_list: &mut DisplayList, + stacking_context_tree: &mut StackingContextTree, containing_block: &ContainingBlock, containing_block_info: &ContainingBlockInfo, stacking_context: &mut StackingContext, ) { 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_clip_id = containing_block.clip_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, + stacking_context_tree, &new_scroll_node_id, &containing_block.rect, &new_scroll_frame_size, @@ -1136,20 +1113,19 @@ impl BoxFragment { new_scroll_node_id = scroll_node_id; } - if let Some(clip_chain_id) = self.build_clip_frame_if_necessary( - display_list, + if let Some(clip_id) = self.build_clip_frame_if_necessary( + stacking_context_tree, &new_scroll_node_id, - &new_clip_chain_id, + new_clip_id, &containing_block.rect, ) { - new_clip_chain_id = clip_chain_id; + new_clip_id = clip_id; } - if let Some(clip_chain_id) = build_clip_path_clip_chain_if_necessary( + if let Some(clip_id) = stacking_context_tree.clip_store.add_for_clip_path( self.style.clone_clip_path(), - display_list, &new_scroll_node_id, - &new_clip_chain_id, + &new_clip_id, BuilderForBoxFragment::new( self, &containing_block.rect, @@ -1157,7 +1133,7 @@ impl BoxFragment { false, /* is_collapsed_table_borders */ ), ) { - new_clip_chain_id = clip_chain_id; + new_clip_id = clip_id; } let establishes_containing_block_for_all_descendants = self @@ -1182,7 +1158,7 @@ impl BoxFragment { .push(StackingContextContent::Fragment { scroll_node_id: new_scroll_node_id, reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments, - clip_chain_id: new_clip_chain_id, + clip_id: new_clip_id, section, containing_block: containing_block.rect, fragment: fragment.clone(), @@ -1200,12 +1176,12 @@ 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(overflow_frame_data) = self.build_overflow_frame_if_necessary( - display_list, + stacking_context_tree, &new_scroll_node_id, - &new_clip_chain_id, + new_clip_id, &containing_block.rect, ) { - new_clip_chain_id = overflow_frame_data.clip_chain_id; + new_clip_id = overflow_frame_data.clip_id; if let Some(scroll_frame_data) = overflow_frame_data.scroll_frame_data { new_scroll_node_id = scroll_frame_data.scroll_tree_node_id; new_scroll_frame_size = Some(scroll_frame_data.scroll_frame_rect.size()); @@ -1216,7 +1192,7 @@ impl BoxFragment { scroll_node_id: new_scroll_node_id, reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments, - clip_chain_id: new_clip_chain_id, + clip_id: new_clip_id, section, containing_block: containing_block.rect, fragment: fragment.clone(), @@ -1237,13 +1213,13 @@ impl BoxFragment { padding_rect, new_scroll_node_id, new_scroll_frame_size, - new_clip_chain_id, + new_clip_id, ); let for_non_absolute_descendants = ContainingBlock::new( content_rect, new_scroll_node_id, new_scroll_frame_size, - new_clip_chain_id, + new_clip_id, ); // Create a new `ContainingBlockInfo` for descendants depending on @@ -1265,7 +1241,7 @@ impl BoxFragment { for child in &self.children { child.build_stacking_context_tree( - display_list, + stacking_context_tree, &new_containing_block_info, stacking_context, StackingContextBuildMode::SkipHoisted, @@ -1281,7 +1257,7 @@ impl BoxFragment { .push(StackingContextContent::Fragment { scroll_node_id: new_scroll_node_id, reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments, - clip_chain_id: new_clip_chain_id, + clip_id: new_clip_id, section, containing_block: containing_block.rect, fragment: fragment.clone(), @@ -1293,11 +1269,11 @@ impl BoxFragment { fn build_clip_frame_if_necessary( &self, - display_list: &mut DisplayList, + stacking_context_tree: &mut StackingContextTree, parent_scroll_node_id: &ScrollTreeNodeId, - parent_clip_chain_id: &wr::ClipChainId, + parent_clip_id: ClipId, containing_block_rect: &PhysicalRect<Au>, - ) -> Option<wr::ClipChainId> { + ) -> Option<ClipId> { let position = self.style.get_box().position; // https://drafts.csswg.org/css2/#clipping // The clip property applies only to absolutely positioned elements @@ -1316,18 +1292,19 @@ impl BoxFragment { .for_border_rect(border_rect) .translate(containing_block_rect.origin.to_vector()) .to_webrender(); - - let clip_id = display_list - .wr - .define_clip_rect(parent_scroll_node_id.spatial_id, clip_rect); - Some(display_list.define_clip_chain(*parent_clip_chain_id, [clip_id])) + Some(stacking_context_tree.clip_store.add( + BorderRadius::zero(), + clip_rect, + *parent_scroll_node_id, + parent_clip_id, + )) } fn build_overflow_frame_if_necessary( &self, - display_list: &mut DisplayList, + stacking_context_tree: &mut StackingContextTree, parent_scroll_node_id: &ScrollTreeNodeId, - parent_clip_chain_id: &wr::ClipChainId, + parent_clip_id: ClipId, containing_block_rect: &PhysicalRect<Au>, ) -> Option<OverflowFrameData> { let overflow = self.style.effective_overflow(self.base.flags); @@ -1367,15 +1344,15 @@ impl BoxFragment { radii = BorderRadius::zero(); } - let clip_chain_id = display_list.clip_overflow_frame( - parent_scroll_node_id, - parent_clip_chain_id, - overflow_clip_rect, + let clip_id = stacking_context_tree.clip_store.add( radii, + overflow_clip_rect, + *parent_scroll_node_id, + parent_clip_id, ); return Some(OverflowFrameData { - clip_chain_id, + clip_id, scroll_frame_data: None, }); } @@ -1404,17 +1381,17 @@ impl BoxFragment { .translate(containing_block_rect.origin.to_vector()) .to_webrender(); - let clip_chain_id = display_list.clip_overflow_frame( - parent_scroll_node_id, - parent_clip_chain_id, - scroll_frame_rect, + let clip_id = stacking_context_tree.clip_store.add( BuilderForBoxFragment::new(self, containing_block_rect, false, false).border_radius, + scroll_frame_rect, + *parent_scroll_node_id, + parent_clip_id, ); let tag = self.base.tag?; let external_id = wr::ExternalScrollId( tag.to_display_list_fragment_id(), - display_list.wr.pipeline_id, + stacking_context_tree.compositor_info.pipeline_id, ); let sensitivity = AxesScrollSensitivity { @@ -1424,7 +1401,7 @@ impl BoxFragment { let content_rect = self.reachable_scrollable_overflow_region().to_webrender(); - let scroll_tree_node_id = display_list.define_scroll_frame( + let scroll_tree_node_id = stacking_context_tree.define_scroll_frame( parent_scroll_node_id, external_id, content_rect, @@ -1433,7 +1410,7 @@ impl BoxFragment { ); Some(OverflowFrameData { - clip_chain_id, + clip_id, scroll_frame_data: Some(ScrollFrameData { scroll_tree_node_id, scroll_frame_rect, @@ -1443,7 +1420,7 @@ impl BoxFragment { fn build_sticky_frame_if_necessary( &self, - display_list: &mut DisplayList, + stacking_context_tree: &mut StackingContextTree, parent_scroll_node_id: &ScrollTreeNodeId, containing_block_rect: &PhysicalRect<Au>, scroll_frame_size: &Option<LayoutSize>, @@ -1456,7 +1433,7 @@ impl BoxFragment { Some(size) => size, None => { // This is a direct descendant of a reference frame. - &display_list.compositor_info.viewport_size + &stacking_context_tree.compositor_info.viewport_size }, }; @@ -1513,7 +1490,7 @@ impl BoxFragment { offsets.left.non_auto().map(|v| v.to_f32_px()), ); - let sticky_node_id = display_list.define_sticky_frame( + let sticky_node_id = stacking_context_tree.define_sticky_frame( parent_scroll_node_id, frame_rect, margins, @@ -1665,7 +1642,7 @@ impl BoxFragment { impl PositioningFragment { fn build_stacking_context_tree( &self, - display_list: &mut DisplayList, + stacking_context_tree: &mut StackingContextTree, containing_block: &ContainingBlock, containing_block_info: &ContainingBlockInfo, stacking_context: &mut StackingContext, @@ -1679,7 +1656,7 @@ impl PositioningFragment { for child in &self.children { child.build_stacking_context_tree( - display_list, + stacking_context_tree, &new_containing_block_info, stacking_context, StackingContextBuildMode::SkipHoisted, diff --git a/components/layout/dom.rs b/components/layout/dom.rs index e3a22eb5197..88176ffbbb0 100644 --- a/components/layout/dom.rs +++ b/components/layout/dom.rs @@ -126,15 +126,15 @@ impl LayoutBox { .repair_style(context, node, new_style); } }, - LayoutBox::FlexLevel(flex_level_box) => { - flex_level_box.borrow_mut().repair_style(context, new_style) - }, + LayoutBox::FlexLevel(flex_level_box) => flex_level_box + .borrow_mut() + .repair_style(context, node, new_style), LayoutBox::TableLevelBox(table_level_box) => { - table_level_box.repair_style(context, new_style) - }, - LayoutBox::TaffyItemBox(taffy_item_box) => { - taffy_item_box.borrow_mut().repair_style(context, new_style) + table_level_box.repair_style(context, node, new_style) }, + LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box + .borrow_mut() + .repair_style(context, node, new_style), } } } diff --git a/components/layout/flexbox/mod.rs b/components/layout/flexbox/mod.rs index 91a12b31812..96a311ee2b5 100644 --- a/components/layout/flexbox/mod.rs +++ b/components/layout/flexbox/mod.rs @@ -4,6 +4,7 @@ use geom::{FlexAxis, MainStartCrossStart}; use malloc_size_of_derive::MallocSizeOf; +use script::layout_dom::ServoLayoutNode; use servo_arc::Arc as ServoArc; use style::context::SharedStyleContext; use style::logical_geometry::WritingMode; @@ -154,16 +155,17 @@ impl FlexLevelBox { pub(crate) fn repair_style( &mut self, context: &SharedStyleContext, + node: &ServoLayoutNode, new_style: &ServoArc<ComputedValues>, ) { match self { FlexLevelBox::FlexItem(flex_item_box) => flex_item_box .independent_formatting_context - .repair_style(context, new_style), + .repair_style(context, node, new_style), FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box .borrow_mut() .context - .repair_style(context, new_style), + .repair_style(context, node, new_style), } } diff --git a/components/layout/flow/inline/construct.rs b/components/layout/flow/inline/construct.rs index a99de1679a4..600da9b721a 100644 --- a/components/layout/flow/inline/construct.rs +++ b/components/layout/flow/inline/construct.rs @@ -31,7 +31,7 @@ pub(crate) struct InlineFormattingContextBuilder { /// inline box stack, and importantly, one for every `display: contents` element that we are /// currently processing. Normally `display: contents` elements don't affect the structure of /// the [`InlineFormattingContext`], but the styles they provide do style their children. - shared_inline_styles_stack: Vec<SharedInlineStyles>, + pub shared_inline_styles_stack: Vec<SharedInlineStyles>, /// The collection of text strings that make up this [`InlineFormattingContext`] under /// construction. diff --git a/components/layout/flow/inline/mod.rs b/components/layout/flow/inline/mod.rs index 7e69aa1aaae..74d42ca6fb4 100644 --- a/components/layout/flow/inline/mod.rs +++ b/components/layout/flow/inline/mod.rs @@ -91,6 +91,7 @@ use line_breaker::LineBreaker; use malloc_size_of_derive::MallocSizeOf; use range::Range; use script::layout_dom::ServoLayoutNode; +use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode}; use servo_arc::Arc; use style::Zero; use style::computed_values::text_wrap_mode::T as TextWrapMode; @@ -158,6 +159,10 @@ pub(crate) struct InlineFormattingContext { /// context in order to avoid duplicating this information. pub font_metrics: Vec<FontKeyAndMetrics>, + /// The [`SharedInlineStyles`] for the root of this [`InlineFormattingContext`] that are used to + /// share styles with all [`TextRun`] children. + pub(super) shared_inline_styles: SharedInlineStyles, + pub(super) text_decoration_line: TextDecorationLine, /// Whether this IFC contains the 1st formatted line of an element: @@ -237,12 +242,14 @@ impl InlineItem { InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box .borrow_mut() .context - .repair_style(context, new_style), + .repair_style(context, node, new_style), InlineItem::OutOfFlowFloatBox(float_box) => float_box .borrow_mut() .contents - .repair_style(context, new_style), - InlineItem::Atomic(atomic, ..) => atomic.borrow_mut().repair_style(context, new_style), + .repair_style(context, node, new_style), + InlineItem::Atomic(atomic, ..) => { + atomic.borrow_mut().repair_style(context, node, new_style) + }, } } @@ -1699,6 +1706,11 @@ impl InlineFormattingContext { inline_items: builder.inline_items, inline_boxes: builder.inline_boxes, font_metrics, + shared_inline_styles: builder + .shared_inline_styles_stack + .last() + .expect("Should have at least one SharedInlineStyle for the root of an IFC") + .clone(), text_decoration_line: propagated_data.text_decoration, has_first_formatted_line, contains_floats: builder.contains_floats, @@ -1707,6 +1719,11 @@ impl InlineFormattingContext { } } + pub(crate) fn repair_style(&self, node: &ServoLayoutNode, new_style: &Arc<ComputedValues>) { + *self.shared_inline_styles.style.borrow_mut() = new_style.clone(); + *self.shared_inline_styles.selected.borrow_mut() = node.to_threadsafe().selected_style(); + } + pub(super) fn layout( &self, layout_context: &LayoutContext, diff --git a/components/layout/flow/mod.rs b/components/layout/flow/mod.rs index 99b84d088e5..4776b65771c 100644 --- a/components/layout/flow/mod.rs +++ b/components/layout/flow/mod.rs @@ -78,6 +78,15 @@ impl BlockContainer { BlockContainer::InlineFormattingContext(context) => context.contains_floats, } } + + pub(crate) fn repair_style(&mut self, node: &ServoLayoutNode, new_style: &Arc<ComputedValues>) { + match self { + BlockContainer::BlockLevelBoxes(..) => {}, + BlockContainer::InlineFormattingContext(inline_formatting_context) => { + inline_formatting_context.repair_style(node, new_style) + }, + } + } } #[derive(Debug, MallocSizeOf)] @@ -106,20 +115,21 @@ impl BlockLevelBox { match self { BlockLevelBox::Independent(independent_formatting_context) => { - independent_formatting_context.repair_style(context, new_style) + independent_formatting_context.repair_style(context, node, new_style) }, BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box .borrow_mut() .context - .repair_style(context, new_style), + .repair_style(context, node, new_style), BlockLevelBox::OutOfFlowFloatBox(float_box) => { - float_box.contents.repair_style(context, new_style) + float_box.contents.repair_style(context, node, new_style) }, BlockLevelBox::OutsideMarker(outside_marker) => { outside_marker.repair_style(context, node, new_style) }, - BlockLevelBox::SameFormattingContextBlock { base, .. } => { + BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => { base.repair_style(new_style); + contents.repair_style(node, new_style); }, } } @@ -477,6 +487,10 @@ impl BlockFormattingContext { pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> { LayoutStyle::Default(&base.style) } + + pub(crate) fn repair_style(&mut self, node: &ServoLayoutNode, new_style: &Arc<ComputedValues>) { + self.contents.repair_style(node, new_style); + } } /// Finds the min/max-content inline size of the block-level children of a block container. diff --git a/components/layout/formatting_contexts.rs b/components/layout/formatting_contexts.rs index a489df2b663..2b242c00361 100644 --- a/components/layout/formatting_contexts.rs +++ b/components/layout/formatting_contexts.rs @@ -4,7 +4,7 @@ use app_units::Au; use malloc_size_of_derive::MallocSizeOf; -use script::layout_dom::ServoLayoutElement; +use script::layout_dom::{ServoLayoutElement, ServoLayoutNode}; use servo_arc::Arc; use style::context::SharedStyleContext; use style::properties::ComputedValues; @@ -223,12 +223,13 @@ impl IndependentFormattingContext { pub(crate) fn repair_style( &mut self, context: &SharedStyleContext, + node: &ServoLayoutNode, new_style: &Arc<ComputedValues>, ) { self.base.repair_style(new_style); match &mut self.contents { IndependentFormattingContextContents::NonReplaced(content) => { - content.repair_style(context, new_style); + content.repair_style(context, node, new_style); }, IndependentFormattingContextContents::Replaced(..) => {}, } @@ -356,9 +357,16 @@ impl IndependentNonReplacedContents { matches!(self, Self::Table(_)) } - fn repair_style(&mut self, context: &SharedStyleContext, new_style: &Arc<ComputedValues>) { + fn repair_style( + &mut self, + context: &SharedStyleContext, + node: &ServoLayoutNode, + new_style: &Arc<ComputedValues>, + ) { match self { - IndependentNonReplacedContents::Flow(..) => {}, + IndependentNonReplacedContents::Flow(block_formatting_context) => { + block_formatting_context.repair_style(node, new_style); + }, IndependentNonReplacedContents::Flex(flex_container) => { flex_container.repair_style(new_style) }, diff --git a/components/layout/fragment_tree/box_fragment.rs b/components/layout/fragment_tree/box_fragment.rs index 9b96b1c4fb4..b7c3a2a3524 100644 --- a/components/layout/fragment_tree/box_fragment.rs +++ b/components/layout/fragment_tree/box_fragment.rs @@ -92,7 +92,7 @@ pub(crate) struct BoxFragment { pub scrollable_overflow_from_children: PhysicalRect<Au>, /// The resolved box insets if this box is `position: sticky`. These are calculated - /// during stacking context tree construction because they rely on the size of the + /// during `StackingContextTree` construction because they rely on the size of the /// scroll container. pub(crate) resolved_sticky_insets: AtomicRefCell<Option<PhysicalSides<AuOrAuto>>>, diff --git a/components/layout/fragment_tree/fragment_tree.rs b/components/layout/fragment_tree/fragment_tree.rs index 979bd0090fc..ba03a72ac21 100644 --- a/components/layout/fragment_tree/fragment_tree.rs +++ b/components/layout/fragment_tree/fragment_tree.rs @@ -14,7 +14,6 @@ use webrender_api::units; use super::{BoxFragment, ContainingBlockManager, Fragment}; use crate::ArcRefCell; use crate::context::LayoutContext; -use crate::display_list::StackingContext; use crate::geom::PhysicalRect; #[derive(MallocSizeOf)] @@ -91,16 +90,6 @@ impl FragmentTree { fragment_tree } - pub(crate) fn build_display_list( - &self, - builder: &mut crate::display_list::DisplayListBuilder, - root_stacking_context: &StackingContext, - ) { - // Paint the canvas’ background (if any) before/under everything else - root_stacking_context.build_canvas_background_display_list(builder, self); - root_stacking_context.build_display_list(builder); - } - pub fn print(&self) { let mut print_tree = PrintTree::new("Fragment Tree".to_string()); for fragment in &self.root_fragments { diff --git a/components/layout/layout_impl.rs b/components/layout/layout_impl.rs index fcf658036b2..b490b4a0506 100644 --- a/components/layout/layout_impl.rs +++ b/components/layout/layout_impl.rs @@ -77,7 +77,7 @@ use webrender_api::units::{DevicePixel, DevicePoint, LayoutPixel, LayoutPoint, L use webrender_api::{ExternalScrollId, HitTestFlags}; use crate::context::LayoutContext; -use crate::display_list::{DisplayList, WebRenderImageInfo}; +use crate::display_list::{DisplayListBuilder, StackingContextTree, WebRenderImageInfo}; use crate::query::{ get_the_text_steps, process_client_rect_request, process_content_box_request, process_content_boxes_request, process_node_scroll_area_request, process_offset_parent_query, @@ -144,6 +144,9 @@ pub struct LayoutThread { /// The fragment tree. fragment_tree: RefCell<Option<Arc<FragmentTree>>>, + /// The [`StackingContextTree`] cached from previous layouts. + stacking_context_tree: RefCell<Option<StackingContextTree>>, + /// A counter for epoch messages epoch: Cell<Epoch>, @@ -521,6 +524,7 @@ impl LayoutThread { first_reflow: Cell::new(true), box_tree: Default::default(), fragment_tree: Default::default(), + stacking_context_tree: Default::default(), // Epoch starts at 1 because of the initial display list for epoch 0 that we send to WR epoch: Cell::new(Epoch(1)), viewport_size: Size2D::new( @@ -649,15 +653,17 @@ impl LayoutThread { highlighted_dom_node: reflow_request.highlighted_dom_node, }; - self.restyle_and_build_trees( + let did_reflow = self.restyle_and_build_trees( &reflow_request, root_element, rayon_pool, &mut layout_context, ); + + self.build_stacking_context_tree(&reflow_request, did_reflow); self.build_display_list(&reflow_request, &mut layout_context); - self.first_reflow.set(false); + self.first_reflow.set(false); if let ReflowGoal::UpdateScrollNode(scroll_state) = reflow_request.reflow_goal { self.update_scroll_node_state(&scroll_state); } @@ -666,6 +672,7 @@ impl LayoutThread { let iframe_sizes = std::mem::take(&mut *layout_context.iframe_sizes.lock()); let node_to_image_animation_map = std::mem::take(&mut *layout_context.node_image_animation_map.write()); + Some(ReflowResult { pending_images, iframe_sizes, @@ -742,7 +749,7 @@ impl LayoutThread { root_element: ServoLayoutElement<'_>, rayon_pool: Option<&ThreadPool>, layout_context: &mut LayoutContext<'_>, - ) { + ) -> bool { let dirty_root = unsafe { ServoLayoutNode::new(&reflow_request.dirty_root.unwrap()) .as_element() @@ -758,7 +765,7 @@ impl LayoutThread { if !token.should_traverse() { layout_context.style_context.stylist.rule_tree().maybe_gc(); - return; + return false; } let dirty_root: ServoLayoutNode = @@ -768,7 +775,7 @@ impl LayoutThread { let damage = compute_damage_and_repair_style(layout_context.shared_context(), root_node); if damage == RestyleDamage::REPAINT { layout_context.style_context.stylist.rule_tree().maybe_gc(); - return; + return false; } let mut box_tree = self.box_tree.borrow_mut(); @@ -803,8 +810,15 @@ impl LayoutThread { run_layout() }); + if self.debug.dump_flow_tree { + fragment_tree.print(); + } *self.fragment_tree.borrow_mut() = Some(fragment_tree); + // The FragmentTree has been updated, so any existing StackingContext tree that layout + // had is now out of date and should be rebuilt. + *self.stacking_context_tree.borrow_mut() = None; + if self.debug.dump_style_tree { println!( "{:?}", @@ -822,75 +836,80 @@ impl LayoutThread { // GC the rule tree if some heuristics are met. layout_context.style_context.stylist.rule_tree().maybe_gc(); + true } - fn build_display_list( - &self, - reflow_request: &ReflowRequest, - layout_context: &mut LayoutContext<'_>, - ) { + fn build_stacking_context_tree(&self, reflow_request: &ReflowRequest, did_reflow: bool) { + if !reflow_request.reflow_goal.needs_display_list() && + !reflow_request.reflow_goal.needs_display() + { + return; + } let Some(fragment_tree) = &*self.fragment_tree.borrow() else { return; }; - if !reflow_request.reflow_goal.needs_display_list() { + if !did_reflow && self.stacking_context_tree.borrow().is_some() { return; } - let mut epoch = self.epoch.get(); - epoch.next(); - self.epoch.set(epoch); - let viewport_size = LayoutSize::from_untyped(Size2D::new( self.viewport_size.width.to_f32_px(), self.viewport_size.height.to_f32_px(), )); - let mut display_list = DisplayList::new( + + // Build the StackingContextTree. This turns the `FragmentTree` into a + // tree of fragments in CSS painting order and also creates all + // applicable spatial and clip nodes. + *self.stacking_context_tree.borrow_mut() = Some(StackingContextTree::new( + fragment_tree, viewport_size, fragment_tree.scrollable_overflow(), self.id.into(), - epoch.into(), fragment_tree.viewport_scroll_sensitivity, self.first_reflow.get(), - ); - display_list.wr.begin(); - - // `dump_serialized_display_list` doesn't actually print anything. It sets up - // the display list for printing the serialized version when `finalize()` is called. - // We need to call this before adding any display items so that they are printed - // during `finalize()`. - if self.debug.dump_display_list { - display_list.wr.dump_serialized_display_list(); - } + &self.debug, + )); + } - // Build the root stacking context. This turns the `FragmentTree` into a - // tree of fragments in CSS painting order and also creates all - // applicable spatial and clip nodes. - let root_stacking_context = - display_list.build_stacking_context_tree(fragment_tree, &self.debug); + fn build_display_list( + &self, + reflow_request: &ReflowRequest, + layout_context: &mut LayoutContext<'_>, + ) { + if !reflow_request.reflow_goal.needs_display() { + return; + } + let Some(fragment_tree) = &*self.fragment_tree.borrow() else { + return; + }; - // Build the rest of the display list which inclues all of the WebRender primitives. - display_list.build(layout_context, fragment_tree, &root_stacking_context); + let mut stacking_context_tree = self.stacking_context_tree.borrow_mut(); + let Some(stacking_context_tree) = stacking_context_tree.as_mut() else { + return; + }; - if self.debug.dump_flow_tree { - fragment_tree.print(); - } - if self.debug.dump_stacking_context_tree { - root_stacking_context.debug_print(); - } + let mut epoch = self.epoch.get(); + epoch.next(); + self.epoch.set(epoch); + stacking_context_tree.compositor_info.epoch = epoch.into(); - if reflow_request.reflow_goal.needs_display() { - self.compositor_api.send_display_list( - self.webview_id, - display_list.compositor_info, - display_list.wr.end().1, - ); + let built_display_list = DisplayListBuilder::build( + layout_context, + stacking_context_tree, + fragment_tree, + &self.debug, + ); + self.compositor_api.send_display_list( + self.webview_id, + &stacking_context_tree.compositor_info, + built_display_list, + ); - let (keys, instance_keys) = self - .font_context - .collect_unused_webrender_resources(false /* all */); - self.compositor_api - .remove_unused_font_resources(keys, instance_keys) - } + let (keys, instance_keys) = self + .font_context + .collect_unused_webrender_resources(false /* all */); + self.compositor_api + .remove_unused_font_resources(keys, instance_keys) } fn update_scroll_node_state(&self, state: &ScrollState) { diff --git a/components/layout/table/mod.rs b/components/layout/table/mod.rs index 72b67863e7d..78884c377e9 100644 --- a/components/layout/table/mod.rs +++ b/components/layout/table/mod.rs @@ -76,7 +76,7 @@ pub(crate) use construct::AnonymousTableContent; pub use construct::TableBuilder; use euclid::{Point2D, Size2D, UnknownUnit, Vector2D}; use malloc_size_of_derive::MallocSizeOf; -use script::layout_dom::ServoLayoutElement; +use script::layout_dom::{ServoLayoutElement, ServoLayoutNode}; use servo_arc::Arc; use style::context::SharedStyleContext; use style::properties::ComputedValues; @@ -425,13 +425,14 @@ impl TableLevelBox { pub(crate) fn repair_style( &self, context: &SharedStyleContext<'_>, + node: &ServoLayoutNode, new_style: &Arc<ComputedValues>, ) { match self { TableLevelBox::Caption(caption) => caption .borrow_mut() .context - .repair_style(context, new_style), + .repair_style(context, node, new_style), TableLevelBox::Cell(cell) => cell.borrow_mut().repair_style(new_style), TableLevelBox::TrackGroup(track_group) => { track_group.borrow_mut().repair_style(new_style); diff --git a/components/layout/taffy/mod.rs b/components/layout/taffy/mod.rs index ba80824fa99..2bc7a598d08 100644 --- a/components/layout/taffy/mod.rs +++ b/components/layout/taffy/mod.rs @@ -7,6 +7,7 @@ use std::fmt; use app_units::Au; use malloc_size_of_derive::MallocSizeOf; +use script::layout_dom::ServoLayoutNode; use servo_arc::Arc; use style::context::SharedStyleContext; use style::properties::ComputedValues; @@ -152,17 +153,18 @@ impl TaffyItemBox { pub(crate) fn repair_style( &mut self, context: &SharedStyleContext, + node: &ServoLayoutNode, new_style: &Arc<ComputedValues>, ) { self.style = new_style.clone(); match &mut self.taffy_level_box { TaffyItemBoxInner::InFlowBox(independent_formatting_context) => { - independent_formatting_context.repair_style(context, new_style) + independent_formatting_context.repair_style(context, node, new_style) }, TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box .borrow_mut() .context - .repair_style(context, new_style), + .repair_style(context, node, new_style), } } } diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index 17c3d0b1c20..d05deb24bfa 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -112,15 +112,14 @@ pub(crate) fn compute_damage_and_repair_style_inner( .element_data .borrow_mut(); + original_damage = std::mem::take(&mut element_data.damage); if let Some(ref style) = element_data.styles.primary { if style.get_box().display == Display::None { return parent_restyle_damage; } } - original_damage = std::mem::take(&mut element_data.damage); - element_data.damage |= parent_restyle_damage; - element_data.damage + original_damage | parent_restyle_damage }; let mut propagated_damage = damage; |