Refactor Layout 2020 layout for a compositor-side scroll tree

This change refactors how layout is done in Layout 2020 in preparation
for a compositor-side scroll tree:

1. Now the SpatialId and ClipId of each fragment is stored separately.
   This will allow storing a scroll node id instead of only the handle
   to the WebRender spatial node.
2. Separate out stacking context tree construction and display list
   building. This change will make it possible to eventually build the
   stacking context tree without the full display list if we find that
   necessary. For instance, this might be useful to cache containing
   block boundaries.
3. Add a `DisplayList` struct that stores both the WebRender display
   list builder and the compositor info. This exposes the API to the
   layout thread for display list building.

In addition, this change adds a lot of missing documentation.  This
should not change behavior.
This commit is contained in:
Martin Robinson 2023-05-15 10:35:45 +02:00
parent c5d31c3ab6
commit b60e105526
5 changed files with 326 additions and 178 deletions

View file

@ -144,9 +144,9 @@ pub(super) fn build_linear(
let stops = fixup_stops(style, items, Length::new(gradient_line_length));
let linear_gradient = builder
.wr
.wr()
.create_gradient(start_point, end_point, stops, extend_mode);
builder.wr.push_gradient(
builder.wr().push_gradient(
&layer.common,
layer.bounds,
linear_gradient,
@ -244,9 +244,9 @@ pub(super) fn build_radial(
let stops = fixup_stops(style, items, Length::new(gradient_line_length));
let radial_gradient = builder
.wr
.wr()
.create_radial_gradient(center, radii, stops, extend_mode);
builder.wr.push_radial_gradient(
builder.wr().push_radial_gradient(
&layer.common,
layer.bounds,
radial_gradient,

View file

@ -10,6 +10,7 @@ use crate::fragments::{BoxFragment, Fragment, TextFragment};
use crate::geom::{PhysicalPoint, PhysicalRect};
use crate::replaced::IntrinsicSizes;
use crate::style_ext::ComputedValuesExt;
use crate::FragmentTree;
use embedder_traits::Cursor;
use euclid::{Point2D, SideOffsets2D, Size2D};
use fnv::FnvHashMap;
@ -32,7 +33,9 @@ use webrender_api::{self as wr, units};
mod background;
mod conversions;
mod gradient;
pub mod stacking_context;
mod stacking_context;
pub use stacking_context::*;
#[derive(Clone, Copy)]
pub struct WebRenderImageInfo {
@ -45,38 +48,99 @@ pub struct WebRenderImageInfo {
type ItemTag = (u64, u16);
type HitInfo = Option<ItemTag>;
pub struct DisplayListBuilder<'a> {
/// The current SpatialId and ClipId information for this `DisplayListBuilder`.
current_space_and_clip: wr::SpaceAndClipInfo,
element_for_canvas_background: OpaqueNode,
pub context: &'a LayoutContext<'a>,
/// 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,
pub compositor_info: CompositorDisplayListInfo,
pub iframe_sizes: FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>,
/// Contentful paint, for the purpose of
/// https://w3c.github.io/paint-timing/#first-contentful-paint
/// (i.e. the display list contains items of type text,
/// image, non-white canvas or SVG). Used by metrics.
pub is_contentful: bool,
/// 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,
}
impl<'a> DisplayListBuilder<'a> {
impl DisplayList {
/// Create a new [DisplayList] given the dimensions of the layout and the WebRender
/// pipeline id.
///
/// TODO(mrobinson): `_viewport_size` will eventually be used in the creation
/// of the compositor-side scroll tree.
pub fn new(
_viewport_size: units::LayoutSize,
content_size: units::LayoutSize,
pipeline_id: wr::PipelineId,
context: &'a LayoutContext,
fragment_tree: &crate::FragmentTree,
) -> Self {
Self {
current_space_and_clip: wr::SpaceAndClipInfo::root_scroll(pipeline_id),
wr: wr::DisplayListBuilder::new(pipeline_id, content_size),
compositor_info: CompositorDisplayListInfo::default(),
}
}
}
pub(crate) struct DisplayListBuilder<'a> {
/// The current [wr::SpatialId] for this [DisplayListBuilder]. This allows
/// only passing the builder instead passing the containing
/// [stacking_context::StackingContextFragment] as an argument to display
/// list building functions.
current_spatial_id: wr::SpatialId,
/// The current [wr::ClipId] for this [DisplayListBuilder]. This allows
/// only passing the builder instead passing the containing
/// [stacking_context::StackingContextFragment] as an argument to display
/// list building functions.
current_clip_id: wr::ClipId,
/// The [OpaqueNode] handle to the node used to paint the page background
/// if the background was a canvas.
element_for_canvas_background: OpaqueNode,
/// 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,
/// A recording of the sizes of iframes encountered when building this
/// display list. This information is forwarded to the layout thread for the
/// iframe so that its layout knows how large the initial containing block /
/// viewport is.
iframe_sizes: FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>,
/// Contentful paint i.e. whether the display list contains items of type
/// text, image, non-white canvas or SVG). Used by metrics.
/// See https://w3c.github.io/paint-timing/#first-contentful-paint.
is_contentful: bool,
}
impl DisplayList {
pub fn build<'a>(
&mut self,
context: &'a LayoutContext,
fragment_tree: &FragmentTree,
root_stacking_context: &StackingContext,
) -> (FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>, bool) {
let mut builder = DisplayListBuilder {
current_spatial_id: wr::SpatialId::root_scroll_node(self.wr.pipeline_id),
current_clip_id: wr::ClipId::root(self.wr.pipeline_id),
element_for_canvas_background: fragment_tree.canvas_background.from_element,
is_contentful: false,
context,
wr: wr::DisplayListBuilder::new(pipeline_id, fragment_tree.scrollable_overflow()),
compositor_info: CompositorDisplayListInfo::default(),
display_list: self,
iframe_sizes: FnvHashMap::default(),
};
fragment_tree.build_display_list(&mut builder, root_stacking_context);
(builder.iframe_sizes, builder.is_contentful)
}
}
impl<'a> DisplayListBuilder<'a> {
fn wr(&mut self) -> &mut wr::DisplayListBuilder {
&mut self.display_list.wr
}
fn common_properties(
@ -89,8 +153,8 @@ impl<'a> DisplayListBuilder<'a> {
// for fragments that paint their entire border rectangle.
wr::CommonItemProperties {
clip_rect,
spatial_id: self.current_space_and_clip.spatial_id,
clip_id: self.current_space_and_clip.clip_id,
spatial_id: self.current_spatial_id,
clip_id: self.current_clip_id,
hit_info: None,
flags: style.get_webrender_primitive_flags(),
}
@ -109,7 +173,7 @@ impl<'a> DisplayListBuilder<'a> {
return None;
}
let hit_test_index = self.compositor_info.add_hit_test_info(
let hit_test_index = self.display_list.compositor_info.add_hit_test_info(
tag?.node.0 as u64,
Some(cursor(inherited_ui.cursor.keyword, auto_cursor)),
);
@ -143,7 +207,7 @@ impl Fragment {
.translate(containing_block.origin.to_vector());
let common = builder.common_properties(rect.to_webrender(), &i.style);
builder.wr.push_image(
builder.wr().push_image(
&common,
rect.to_webrender(),
image_rendering(i.style.get_inherited_box().image_rendering),
@ -169,7 +233,7 @@ impl Fragment {
);
let common = builder.common_properties(rect.to_webrender(), &iframe.style);
builder.wr.push_iframe(
builder.wr().push_iframe(
rect.to_webrender(),
common.clip_rect,
&wr::SpaceAndClipInfo {
@ -248,7 +312,7 @@ impl Fragment {
}
// Text.
builder.wr.push_text(
builder.wr().push_text(
&common,
rect.to_webrender(),
&glyphs,
@ -287,7 +351,7 @@ impl Fragment {
if text_decoration_style == ComputedTextDecorationStyle::MozNone {
return;
}
builder.wr.push_line(
builder.display_list.wr.push_line(
&builder.common_properties(rect, &fragment.parent_style),
&rect,
wavy_line_thickness,
@ -448,7 +512,7 @@ impl<'a> BuilderForBoxFragment<'a> {
if let Some(clip_id) = self.border_edge_clip(builder) {
common.clip_id = clip_id
}
builder.wr.push_hit_test(&common)
builder.wr().push_hit_test(&common)
}
}
@ -473,7 +537,7 @@ impl<'a> BuilderForBoxFragment<'a> {
let layer_index = b.background_image.0.len() - 1;
let (bounds, common) = background::painting_area(self, &source, builder, layer_index);
builder
.wr
.wr()
.push_rect(&common, *bounds, rgba(background_color))
}
@ -550,7 +614,7 @@ impl<'a> BuilderForBoxFragment<'a> {
{
let image_rendering = image_rendering(style.clone_image_rendering());
if layer.repeat {
builder.wr.push_repeating_image(
builder.wr().push_repeating_image(
&layer.common,
layer.bounds,
layer.tile_size,
@ -561,7 +625,7 @@ impl<'a> BuilderForBoxFragment<'a> {
wr::ColorF::WHITE,
)
} else {
builder.wr.push_image(
builder.wr().push_image(
&layer.common,
layer.bounds,
image_rendering,
@ -620,7 +684,7 @@ impl<'a> BuilderForBoxFragment<'a> {
do_aa: true,
});
builder
.wr
.wr()
.push_border(&common, self.border_rect, widths, details)
}
@ -655,7 +719,7 @@ impl<'a> BuilderForBoxFragment<'a> {
do_aa: true,
});
builder
.wr
.wr()
.push_border(&common, outline_rect, widths, details)
}
}
@ -801,8 +865,12 @@ fn clip_for_radii(
if radii.is_zero() {
None
} else {
Some(builder.wr.define_clip_rounded_rect(
&builder.current_space_and_clip,
let parent_space_and_clip = wr::SpaceAndClipInfo {
spatial_id: builder.current_spatial_id,
clip_id: builder.current_clip_id,
};
Some(builder.wr().define_clip_rounded_rect(
&parent_space_and_clip,
wr::ComplexClipRegion {
rect,
radii,

View file

@ -2,6 +2,7 @@
* 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 super::DisplayList;
use crate::cell::ArcRefCell;
use crate::display_list::conversions::ToWebRender;
use crate::display_list::DisplayListBuilder;
@ -9,6 +10,7 @@ use crate::fragment_tree::ContainingBlockManager;
use crate::fragments::{AnonymousFragment, BoxFragment, Fragment};
use crate::geom::PhysicalRect;
use crate::style_ext::ComputedValuesExt;
use crate::FragmentTree;
use euclid::default::Rect;
use servo_arc::Arc as ServoArc;
use std::cmp::Ordering;
@ -24,30 +26,39 @@ use style::values::generics::box_::Perspective;
use style::values::generics::transform;
use style::values::specified::box_::DisplayOutside;
use webrender_api as wr;
use webrender_api::units::{LayoutPoint, LayoutTransform, LayoutVector2D};
use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D};
#[derive(Clone)]
pub(crate) struct ContainingBlock {
/// The SpaceAndClipInfo that contains the children of the fragment that
/// established this containing block.
space_and_clip: wr::SpaceAndClipInfo,
/// The SpatialId of the spatial node that contains the children
/// of this containing block.
spatial_id: wr::SpatialId,
/// The WebRender ClipId to use for this children of this containing
/// block.
clip_id: wr::ClipId,
/// The physical rect of this containing block.
rect: PhysicalRect<Length>,
}
impl ContainingBlock {
pub(crate) fn new(rect: &PhysicalRect<Length>, space_and_clip: wr::SpaceAndClipInfo) -> Self {
pub(crate) fn new(
rect: &PhysicalRect<Length>,
spatial_id: wr::SpatialId,
clip_id: wr::ClipId,
) -> Self {
ContainingBlock {
space_and_clip,
spatial_id,
clip_id,
rect: *rect,
}
}
pub(crate) fn new_replacing_rect(&self, rect: &PhysicalRect<Length>) -> Self {
ContainingBlock {
space_and_clip: self.space_and_clip,
rect: *rect,
..*self
}
}
}
@ -62,8 +73,89 @@ pub(crate) enum StackingContextSection {
Outline,
}
impl DisplayList {
pub fn build_stacking_context_tree(&mut self, fragment_tree: &FragmentTree) -> StackingContext {
let cb_for_non_fixed_descendants = ContainingBlock::new(
&fragment_tree.initial_containing_block,
wr::SpatialId::root_scroll_node(self.wr.pipeline_id),
wr::ClipId::root(self.wr.pipeline_id),
);
let cb_for_fixed_descendants = ContainingBlock::new(
&fragment_tree.initial_containing_block,
wr::SpatialId::root_reference_frame(self.wr.pipeline_id),
wr::ClipId::root(self.wr.pipeline_id),
);
// We need to specify all three containing blocks here, because absolute
// descdendants of the root cannot share the containing block we specify
// for fixed descendants. In this case, they need to have the spatial
// id of the root scroll frame, whereas fixed descendants need the
// spatial id of the root reference frame so that they do not scroll with
// page content.
let containing_block_info = ContainingBlockInfo {
for_non_absolute_descendants: &cb_for_non_fixed_descendants,
for_absolute_descendants: Some(&cb_for_non_fixed_descendants),
for_absolute_and_fixed_descendants: &cb_for_fixed_descendants,
};
let mut root_stacking_context = StackingContext::create_root(&self.wr);
for fragment in &fragment_tree.root_fragments {
fragment.borrow().build_stacking_context_tree(
fragment,
self,
&containing_block_info,
&mut root_stacking_context,
StackingContextBuildMode::SkipHoisted,
);
}
root_stacking_context.sort();
root_stacking_context
}
fn push_reference_frame(
&mut self,
origin: LayoutPoint,
parent_spatial_id: &wr::SpatialId,
transform_style: wr::TransformStyle,
transform: wr::PropertyBinding<LayoutTransform>,
kind: wr::ReferenceFrameKind,
) -> wr::SpatialId {
self.wr
.push_reference_frame(origin, *parent_spatial_id, transform_style, transform, kind)
}
fn pop_reference_frame(&mut self) {
self.wr.pop_reference_frame();
}
fn define_scroll_frame(
&mut self,
parent_spatial_id: &wr::SpatialId,
parent_clip_id: &wr::ClipId,
external_id: Option<wr::ExternalScrollId>,
content_rect: LayoutRect,
clip_rect: LayoutRect,
scroll_sensitivity: wr::ScrollSensitivity,
external_scroll_offset: LayoutVector2D,
) -> (wr::SpatialId, wr::ClipId) {
let new_space_and_clip = self.wr.define_scroll_frame(
&wr::SpaceAndClipInfo {
spatial_id: *parent_spatial_id,
clip_id: *parent_clip_id,
},
external_id,
content_rect,
clip_rect,
scroll_sensitivity,
external_scroll_offset,
);
(new_space_and_clip.spatial_id, new_space_and_clip.clip_id)
}
}
pub(crate) struct StackingContextFragment {
space_and_clip: wr::SpaceAndClipInfo,
spatial_id: wr::SpatialId,
clip_id: wr::ClipId,
section: StackingContextSection,
containing_block: PhysicalRect<Length>,
fragment: ArcRefCell<Fragment>,
@ -71,7 +163,8 @@ pub(crate) struct StackingContextFragment {
impl StackingContextFragment {
fn build_display_list(&self, builder: &mut DisplayListBuilder) {
builder.current_space_and_clip = self.space_and_clip;
builder.current_spatial_id = self.spatial_id;
builder.current_clip_id = self.clip_id;
self.fragment
.borrow()
.build_display_list(builder, &self.containing_block, self.section);
@ -86,7 +179,7 @@ pub(crate) enum StackingContextType {
PseudoAtomicInline,
}
pub(crate) struct StackingContext {
pub struct StackingContext {
/// The spatial id of this fragment. This is used to properly handle
/// things like preserve-3d.
spatial_id: wr::SpatialId,
@ -162,9 +255,9 @@ impl StackingContext {
});
}
fn push_webrender_stacking_context_if_necessary<'a>(
fn push_webrender_stacking_context_if_necessary(
&self,
builder: &'a mut DisplayListBuilder,
builder: &mut DisplayListBuilder,
) -> bool {
let style = match self.initializing_fragment_style.as_ref() {
Some(style) => style,
@ -202,7 +295,7 @@ 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.
builder.wr.push_stacking_context(
builder.wr().push_stacking_context(
LayoutPoint::zero(), // origin
self.spatial_id,
style.get_webrender_primitive_flags(),
@ -251,7 +344,10 @@ impl StackingContext {
if background_color.alpha > 0 {
let common = builder.common_properties(painting_area, &style);
let color = super::rgba(background_color);
builder.wr.push_rect(&common, painting_area, color)
builder
.display_list
.wr
.push_rect(&common, painting_area, color)
}
// `background-color` was comparatively easy,
@ -299,11 +395,10 @@ impl StackingContext {
Some(fragment_tree.canvas_background.root_element),
);
// The root element may have a CSS transform,
// and we want the canvas background image to be transformed.
// To do so, take its `SpatialId` (but not its `ClipId`)
builder.current_space_and_clip.spatial_id =
first_stacking_context_fragment.space_and_clip.spatial_id;
// The root element may have a CSS transform, and we want the canvas
// background image to be transformed. To do so, take its `SpatialId`
// (but not its `ClipId`)
builder.current_spatial_id = first_stacking_context_fragment.spatial_id;
// Now we need express the painting area rectangle in the local coordinate system,
// which differs from the top-level coordinate system based on…
@ -393,7 +488,7 @@ impl StackingContext {
}
if pushed_context {
builder.wr.pop_stacking_context();
builder.display_list.wr.pop_stacking_context();
}
}
}
@ -408,7 +503,7 @@ impl Fragment {
pub(crate) fn build_stacking_context_tree(
&self,
fragment_ref: &ArcRefCell<Fragment>,
wr: &mut wr::DisplayListBuilder,
display_list: &mut DisplayList,
containing_block_info: &ContainingBlockInfo,
stacking_context: &mut StackingContext,
mode: StackingContextBuildMode,
@ -432,7 +527,7 @@ impl Fragment {
fragment.build_stacking_context_tree(
fragment_ref,
wr,
display_list,
containing_block,
containing_block_info,
stacking_context,
@ -447,7 +542,7 @@ impl Fragment {
fragment_ref.borrow().build_stacking_context_tree(
fragment_ref,
wr,
display_list,
containing_block_info,
stacking_context,
StackingContextBuildMode::IncludeHoisted,
@ -455,7 +550,7 @@ impl Fragment {
},
Fragment::Anonymous(fragment) => {
fragment.build_stacking_context_tree(
wr,
display_list,
containing_block,
containing_block_info,
stacking_context,
@ -464,7 +559,8 @@ impl Fragment {
Fragment::Text(_) | Fragment::Image(_) | Fragment::IFrame(_) => {
stacking_context.fragments.push(StackingContextFragment {
section: StackingContextSection::Content,
space_and_clip: containing_block.space_and_clip,
spatial_id: containing_block.spatial_id,
clip_id: containing_block.clip_id,
containing_block: containing_block.rect,
fragment: fragment_ref.clone(),
});
@ -516,14 +612,14 @@ impl BoxFragment {
fn build_stacking_context_tree(
&self,
fragment: &ArcRefCell<Fragment>,
wr: &mut wr::DisplayListBuilder,
display_list: &mut DisplayList,
containing_block: &ContainingBlock,
containing_block_info: &ContainingBlockInfo,
parent_stacking_context: &mut StackingContext,
) {
self.build_stacking_context_tree_maybe_creating_reference_frame(
fragment,
wr,
display_list,
containing_block,
containing_block_info,
parent_stacking_context,
@ -533,7 +629,7 @@ impl BoxFragment {
fn build_stacking_context_tree_maybe_creating_reference_frame(
&self,
fragment: &ArcRefCell<Fragment>,
wr: &mut wr::DisplayListBuilder,
display_list: &mut DisplayList,
containing_block: &ContainingBlock,
containing_block_info: &ContainingBlockInfo,
parent_stacking_context: &mut StackingContext,
@ -544,7 +640,7 @@ impl BoxFragment {
None => {
return self.build_stacking_context_tree_maybe_creating_stacking_context(
fragment,
wr,
display_list,
containing_block,
containing_block_info,
parent_stacking_context,
@ -552,9 +648,9 @@ impl BoxFragment {
},
};
let new_spatial_id = wr.push_reference_frame(
let new_spatial_id = display_list.push_reference_frame(
reference_frame_data.origin.to_webrender(),
containing_block.space_and_clip.spatial_id,
&containing_block.spatial_id,
self.style.get_box().transform_style.to_webrender(),
wr::PropertyBinding::Value(reference_frame_data.transform),
reference_frame_data.kind,
@ -576,29 +672,27 @@ impl BoxFragment {
&containing_block
.rect
.translate(-reference_frame_data.origin.to_vector()),
wr::SpaceAndClipInfo {
spatial_id: new_spatial_id,
clip_id: containing_block.space_and_clip.clip_id,
},
new_spatial_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,
wr,
display_list,
&adjusted_containing_block,
&new_containing_block_info,
parent_stacking_context,
);
wr.pop_reference_frame();
display_list.pop_reference_frame();
}
fn build_stacking_context_tree_maybe_creating_stacking_context(
&self,
fragment: &ArcRefCell<Fragment>,
wr: &mut wr::DisplayListBuilder,
display_list: &mut DisplayList,
containing_block: &ContainingBlock,
containing_block_info: &ContainingBlockInfo,
parent_stacking_context: &mut StackingContext,
@ -608,7 +702,7 @@ impl BoxFragment {
None => {
self.build_stacking_context_tree_for_children(
fragment,
wr,
display_list,
containing_block,
containing_block_info,
parent_stacking_context,
@ -618,13 +712,13 @@ impl BoxFragment {
};
let mut child_stacking_context = StackingContext::new(
containing_block.space_and_clip.spatial_id,
containing_block.spatial_id,
self.style.clone(),
context_type,
);
self.build_stacking_context_tree_for_children(
fragment,
wr,
display_list,
containing_block,
containing_block_info,
&mut child_stacking_context,
@ -647,30 +741,36 @@ impl BoxFragment {
.append(&mut stolen_children);
}
fn build_stacking_context_tree_for_children<'a>(
&'a self,
fn build_stacking_context_tree_for_children(
&self,
fragment: &ArcRefCell<Fragment>,
wr: &mut wr::DisplayListBuilder,
display_list: &mut DisplayList,
containing_block: &ContainingBlock,
containing_block_info: &ContainingBlockInfo,
stacking_context: &mut StackingContext,
) {
let mut new_space_and_clip = containing_block.space_and_clip;
if let Some(new_clip_id) =
self.build_clip_frame_if_necessary(wr, new_space_and_clip, &containing_block.rect)
{
new_space_and_clip.clip_id = new_clip_id;
let mut new_spatial_id = containing_block.spatial_id;
let mut new_clip_id = containing_block.clip_id;
if let Some(clip_id) = self.build_clip_frame_if_necessary(
display_list,
&new_spatial_id,
&new_clip_id,
&containing_block.rect,
) {
new_clip_id = clip_id;
}
stacking_context.fragments.push(StackingContextFragment {
space_and_clip: new_space_and_clip,
spatial_id: new_spatial_id,
clip_id: new_clip_id,
section: self.get_stacking_context_section(),
containing_block: containing_block.rect,
fragment: fragment.clone(),
});
if self.style.get_outline().outline_width.px() > 0.0 {
stacking_context.fragments.push(StackingContextFragment {
space_and_clip: new_space_and_clip,
spatial_id: new_spatial_id,
clip_id: new_clip_id,
section: StackingContextSection::Outline,
containing_block: containing_block.rect,
fragment: fragment.clone(),
@ -679,10 +779,14 @@ 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_space_and_clip) =
self.build_scroll_frame_if_necessary(wr, new_space_and_clip, &containing_block.rect)
{
new_space_and_clip = scroll_space_and_clip;
if let Some((spatial_id, clip_id)) = self.build_scroll_frame_if_necessary(
display_list,
&new_spatial_id,
&new_clip_id,
&containing_block.rect,
) {
new_spatial_id = spatial_id;
new_clip_id = clip_id;
}
let padding_rect = self
@ -694,14 +798,10 @@ impl BoxFragment {
.to_physical(self.style.writing_mode, &containing_block.rect)
.translate(containing_block.rect.origin.to_vector());
let for_absolute_descendants = ContainingBlock {
rect: padding_rect,
space_and_clip: new_space_and_clip,
};
let for_non_absolute_descendants = ContainingBlock {
rect: content_rect,
space_and_clip: new_space_and_clip,
};
let for_absolute_descendants =
ContainingBlock::new(&padding_rect, new_spatial_id, new_clip_id);
let for_non_absolute_descendants =
ContainingBlock::new(&content_rect, new_spatial_id, new_clip_id);
// Create a new `ContainingBlockInfo` for descendants depending on
// whether or not this fragment establishes a containing block for
@ -729,7 +829,7 @@ impl BoxFragment {
for child in &self.children {
child.borrow().build_stacking_context_tree(
child,
wr,
display_list,
&new_containing_block_info,
stacking_context,
StackingContextBuildMode::SkipHoisted,
@ -739,8 +839,9 @@ impl BoxFragment {
fn build_clip_frame_if_necessary(
&self,
wr: &mut wr::DisplayListBuilder,
current_space_and_clip: wr::SpaceAndClipInfo,
display_list: &mut DisplayList,
parent_spatial_id: &wr::SpatialId,
parent_clip_id: &wr::ClipId,
containing_block_rect: &PhysicalRect<Length>,
) -> Option<wr::ClipId> {
let position = self.style.get_box().position;
@ -764,15 +865,22 @@ impl BoxFragment {
.translate(containing_block_rect.origin.to_vector())
.to_webrender();
Some(wr.define_clip_rect(&current_space_and_clip, clip_rect))
Some(display_list.wr.define_clip_rect(
&wr::SpaceAndClipInfo {
spatial_id: *parent_spatial_id,
clip_id: *parent_clip_id,
},
clip_rect,
))
}
fn build_scroll_frame_if_necessary<'a>(
fn build_scroll_frame_if_necessary(
&self,
wr: &mut wr::DisplayListBuilder,
current_space_and_clip: wr::SpaceAndClipInfo,
display_list: &mut DisplayList,
parent_spatial_id: &wr::SpatialId,
parent_clip_id: &wr::ClipId,
containing_block_rect: &PhysicalRect<Length>,
) -> Option<wr::SpaceAndClipInfo> {
) -> Option<(wr::SpatialId, wr::ClipId)> {
let overflow_x = self.style.get_box().overflow_x;
let overflow_y = self.style.get_box().overflow_y;
if overflow_x == ComputedOverflow::Visible && overflow_y == ComputedOverflow::Visible {
@ -780,7 +888,10 @@ impl BoxFragment {
}
let tag = self.base.tag?;
let external_id = wr::ExternalScrollId(tag.to_display_list_fragment_id(), wr.pipeline_id);
let external_id = wr::ExternalScrollId(
tag.to_display_list_fragment_id(),
display_list.wr.pipeline_id,
);
let sensitivity =
if ComputedOverflow::Hidden == overflow_x && ComputedOverflow::Hidden == overflow_y {
@ -794,9 +905,11 @@ impl BoxFragment {
.to_physical(self.style.writing_mode, &containing_block_rect)
.translate(containing_block_rect.origin.to_vector())
.to_webrender();
Some(
wr.define_scroll_frame(
&current_space_and_clip,
display_list.define_scroll_frame(
parent_spatial_id,
parent_clip_id,
Some(external_id),
self.scrollable_overflow(&containing_block_rect)
.to_webrender(),
@ -937,7 +1050,7 @@ impl BoxFragment {
impl AnonymousFragment {
fn build_stacking_context_tree(
&self,
wr: &mut wr::DisplayListBuilder,
display_list: &mut DisplayList,
containing_block: &ContainingBlock,
containing_block_info: &ContainingBlockInfo,
stacking_context: &mut StackingContext,
@ -953,7 +1066,7 @@ impl AnonymousFragment {
for child in &self.children {
child.borrow().build_stacking_context_tree(
child,
wr,
display_list,
&new_containing_block_info,
stacking_context,
StackingContextBuildMode::SkipHoisted,

View file

@ -4,9 +4,7 @@
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
use crate::display_list::stacking_context::{
ContainingBlock, ContainingBlockInfo, StackingContext, StackingContextBuildMode,
};
use crate::display_list::StackingContext;
use crate::dom::{LayoutBox, NodeExt};
use crate::dom_traversal::{iter_child_nodes, Contents, NodeAndStyleInfo};
use crate::flexbox::FlexLevelBox;
@ -38,7 +36,6 @@ use style::dom::OpaqueNode;
use style::properties::ComputedValues;
use style::values::computed::Length;
use style_traits::CSSPixel;
use webrender_api::{ClipId, SpaceAndClipInfo, SpatialId};
#[derive(Serialize)]
pub struct BoxTree {
@ -60,7 +57,7 @@ pub struct FragmentTree {
/// * The first fragment is generated by the root element.
/// * There may be additional fragments generated by positioned boxes
/// that have the initial containing block.
root_fragments: Vec<ArcRefCell<Fragment>>,
pub(crate) root_fragments: Vec<ArcRefCell<Fragment>>,
/// The scrollable overflow rectangle for the entire tree
/// https://drafts.csswg.org/css-overflow/#scrollable
@ -384,58 +381,18 @@ impl BoxTree {
}
impl FragmentTree {
pub fn build_display_list(&self, builder: &mut crate::display_list::DisplayListBuilder) {
let stacking_context = self.build_stacking_context_tree(builder);
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
stacking_context.build_canvas_background_display_list(
root_stacking_context.build_canvas_background_display_list(
builder,
self,
&self.initial_containing_block,
);
stacking_context.build_display_list(builder);
}
fn build_stacking_context_tree(
&self,
builder: &mut crate::display_list::DisplayListBuilder,
) -> StackingContext {
let mut stacking_context = StackingContext::create_root(&builder.wr);
let pipeline_id = builder.wr.pipeline_id;
let cb_for_non_fixed_descendants = ContainingBlock::new(
&self.initial_containing_block,
SpaceAndClipInfo::root_scroll(pipeline_id),
);
let cb_for_fixed_descendants = ContainingBlock::new(
&self.initial_containing_block,
SpaceAndClipInfo {
spatial_id: SpatialId::root_reference_frame(pipeline_id),
clip_id: ClipId::root(pipeline_id),
},
);
for fragment in &self.root_fragments {
fragment.borrow().build_stacking_context_tree(
fragment,
&mut builder.wr,
// We need to specify all three containing blocks here, because absolute
// descdendants of the root cannot share the containing block we specify
// for fixed descendants. In this case, they need to have the spatial
// id of the root scroll frame, whereas fixed descendants need the
// spatial id of the root reference frame so that they do not scroll with
// page content.
&ContainingBlockInfo {
for_non_absolute_descendants: &cb_for_non_fixed_descendants,
for_absolute_descendants: Some(&cb_for_non_fixed_descendants),
for_absolute_and_fixed_descendants: &cb_for_fixed_descendants,
},
&mut stacking_context,
StackingContextBuildMode::SkipHoisted,
);
}
stacking_context.sort();
stacking_context
root_stacking_context.build_display_list(builder);
}
pub fn print(&self) {

View file

@ -29,7 +29,7 @@ use gfx_traits::{node_id_from_scroll_id, Epoch};
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
use layout::context::LayoutContext;
use layout::display_list::{DisplayListBuilder, WebRenderImageInfo};
use layout::display_list::{DisplayList, WebRenderImageInfo};
use layout::dom::DOMLayoutData;
use layout::layout_debug;
use layout::query::{
@ -1268,8 +1268,15 @@ impl LayoutThread {
document.will_paint();
}
let mut display_list =
DisplayListBuilder::new(self.id.to_webrender(), context, &fragment_tree);
let viewport_size = webrender_api::units::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(
viewport_size,
fragment_tree.scrollable_overflow(),
self.id.to_webrender(),
);
// `dump_serialized_display_list` doesn't actually print anything. It sets up
// the display list for printing the serialized version when `finalize()` is called.
@ -1279,7 +1286,14 @@ impl LayoutThread {
display_list.wr.dump_serialized_display_list();
}
fragment_tree.build_display_list(&mut display_list);
// 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);
// Build the rest of the display list which inclues all of the WebRender primitives.
let (iframe_sizes, is_contentful) =
display_list.build(context, &fragment_tree, &root_stacking_context);
if self.debug.dump_flow_tree {
fragment_tree.print();
@ -1294,12 +1308,8 @@ impl LayoutThread {
// sending the display list to WebRender in order to set time related
// Progressive Web Metrics.
self.paint_time_metrics
.maybe_observe_paint_time(self, epoch, display_list.is_contentful);
.maybe_observe_paint_time(self, epoch, is_contentful);
let viewport_size = webrender_api::units::LayoutSize::from_untyped(Size2D::new(
self.viewport_size.width.to_f32_px(),
self.viewport_size.height.to_f32_px(),
));
self.webrender_api.send_display_list(
epoch,
viewport_size,
@ -1307,7 +1317,7 @@ impl LayoutThread {
display_list.wr.finalize(),
);
self.update_iframe_sizes(display_list.iframe_sizes);
self.update_iframe_sizes(iframe_sizes);
if self.debug.trace_layout {
layout_debug::end_trace(self.generation.get());