mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
Add support for a stacking context pass to layout_2020
This adds an intermediary data structure that allows the display list builder to move through the fragment tree in stacking context painting order. Spatial nodes are built during this phase and all display list items are added to the end of the display list.
This commit is contained in:
parent
6d6d16f7f4
commit
843df5b529
3 changed files with 180 additions and 78 deletions
|
@ -9,12 +9,9 @@ use crate::replaced::IntrinsicSizes;
|
|||
use embedder_traits::Cursor;
|
||||
use euclid::{Point2D, SideOffsets2D, Size2D};
|
||||
use gfx::text::glyph::GlyphStore;
|
||||
use gfx_traits::{combine_id_with_fragment_type, FragmentType};
|
||||
use mitochondria::OnceCell;
|
||||
use net_traits::image_cache::UsePlaceholder;
|
||||
use std::sync::Arc;
|
||||
use style::computed_values::overflow_x::T as ComputedOverflow;
|
||||
use style::computed_values::position::T as ComputedPosition;
|
||||
use style::dom::OpaqueNode;
|
||||
use style::properties::ComputedValues;
|
||||
|
||||
|
@ -24,6 +21,7 @@ use webrender_api::{self as wr, units};
|
|||
|
||||
mod background;
|
||||
mod gradient;
|
||||
mod stacking_context;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct WebRenderImageInfo {
|
||||
|
@ -83,15 +81,7 @@ impl Fragment {
|
|||
) {
|
||||
match self {
|
||||
Fragment::Box(b) => BuilderForBoxFragment::new(b, containing_block).build(builder),
|
||||
Fragment::Anonymous(a) => {
|
||||
let rect = a
|
||||
.rect
|
||||
.to_physical(a.mode, containing_block)
|
||||
.translate(containing_block.origin.to_vector());
|
||||
for child in &a.children {
|
||||
child.build_display_list(builder, &rect)
|
||||
}
|
||||
},
|
||||
Fragment::Anonymous(_) => {},
|
||||
Fragment::Text(t) => {
|
||||
builder.is_contentful = true;
|
||||
let rect = t
|
||||
|
@ -246,71 +236,9 @@ impl<'a> BuilderForBoxFragment<'a> {
|
|||
}
|
||||
|
||||
fn build(&mut self, builder: &mut DisplayListBuilder) {
|
||||
builder.clipping_and_scrolling_scope(|builder| {
|
||||
self.adjust_spatial_id_for_positioning(builder);
|
||||
self.build_hit_test(builder);
|
||||
self.build_background(builder);
|
||||
self.build_border(builder);
|
||||
|
||||
// We want to build the scroll frame after the background and border, because
|
||||
// they shouldn't scroll with the rest of the box content.
|
||||
self.build_scroll_frame_if_necessary(builder);
|
||||
|
||||
let content_rect = self
|
||||
.fragment
|
||||
.content_rect
|
||||
.to_physical(self.fragment.style.writing_mode, self.containing_block)
|
||||
.translate(self.containing_block.origin.to_vector());
|
||||
for child in &self.fragment.children {
|
||||
child.build_display_list(builder, &content_rect)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn adjust_spatial_id_for_positioning(&self, builder: &mut DisplayListBuilder) {
|
||||
if self.fragment.style.get_box().position != ComputedPosition::Fixed {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(mrobinson): Eventually this should use the spatial id of the reference
|
||||
// frame that is the parent of this one once we have full support for stacking
|
||||
// contexts and transforms.
|
||||
builder.current_space_and_clip.spatial_id =
|
||||
wr::SpatialId::root_reference_frame(builder.wr.pipeline_id);
|
||||
}
|
||||
|
||||
fn build_scroll_frame_if_necessary(&self, builder: &mut DisplayListBuilder) {
|
||||
let overflow_x = self.fragment.style.get_box().overflow_x;
|
||||
let overflow_y = self.fragment.style.get_box().overflow_y;
|
||||
let original_scroll_and_clip_info = builder.current_space_and_clip;
|
||||
if overflow_x != ComputedOverflow::Visible || overflow_y != ComputedOverflow::Visible {
|
||||
// TODO(mrobinson): We should use the correct fragment type, once we generate
|
||||
// fragments from ::before and ::after generated content selectors.
|
||||
let id = combine_id_with_fragment_type(
|
||||
self.fragment.tag.id() as usize,
|
||||
FragmentType::FragmentBody,
|
||||
) as u64;
|
||||
let external_id = wr::ExternalScrollId(id, builder.wr.pipeline_id);
|
||||
|
||||
let sensitivity = if ComputedOverflow::Hidden == overflow_x &&
|
||||
ComputedOverflow::Hidden == overflow_y
|
||||
{
|
||||
wr::ScrollSensitivity::Script
|
||||
} else {
|
||||
wr::ScrollSensitivity::ScriptAndInputEvents
|
||||
};
|
||||
|
||||
builder.current_space_and_clip = builder.wr.define_scroll_frame(
|
||||
&original_scroll_and_clip_info,
|
||||
Some(external_id),
|
||||
self.fragment.scrollable_overflow().to_webrender(),
|
||||
*self.padding_rect(),
|
||||
vec![], // complex_clips
|
||||
None, // image_mask
|
||||
sensitivity,
|
||||
wr::units::LayoutVector2D::zero(),
|
||||
);
|
||||
}
|
||||
self.build_hit_test(builder);
|
||||
self.build_background(builder);
|
||||
self.build_border(builder);
|
||||
}
|
||||
|
||||
fn build_hit_test(&self, builder: &mut DisplayListBuilder) {
|
||||
|
|
167
components/layout_2020/display_list/stacking_context.rs
Normal file
167
components/layout_2020/display_list/stacking_context.rs
Normal file
|
@ -0,0 +1,167 @@
|
|||
/* 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 crate::display_list::DisplayListBuilder;
|
||||
use crate::fragments::{AnonymousFragment, BoxFragment, Fragment};
|
||||
use crate::geom::{PhysicalRect, ToWebRender};
|
||||
use gfx_traits::{combine_id_with_fragment_type, FragmentType};
|
||||
use std::default::Default;
|
||||
use style::computed_values::overflow_x::T as ComputedOverflow;
|
||||
use style::computed_values::position::T as ComputedPosition;
|
||||
use style::values::computed::Length;
|
||||
use webrender_api::units::LayoutVector2D;
|
||||
use webrender_api::{ExternalScrollId, ScrollSensitivity, SpaceAndClipInfo, SpatialId};
|
||||
|
||||
pub(crate) struct StackingContextFragment<'a> {
|
||||
space_and_clip: SpaceAndClipInfo,
|
||||
containing_block: PhysicalRect<Length>,
|
||||
fragment: &'a Fragment,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct StackingContext<'a> {
|
||||
fragments: Vec<StackingContextFragment<'a>>,
|
||||
}
|
||||
|
||||
impl Fragment {
|
||||
pub(crate) fn build_stacking_context_tree<'a>(
|
||||
&'a self,
|
||||
builder: &mut DisplayListBuilder,
|
||||
containing_block: &PhysicalRect<Length>,
|
||||
stacking_context: &mut StackingContext<'a>,
|
||||
) {
|
||||
match self {
|
||||
Fragment::Box(fragment) => fragment.build_stacking_context_tree(
|
||||
self,
|
||||
builder,
|
||||
containing_block,
|
||||
stacking_context,
|
||||
),
|
||||
Fragment::Anonymous(fragment) => {
|
||||
fragment.build_stacking_context_tree(builder, containing_block, stacking_context)
|
||||
},
|
||||
Fragment::Text(_) | Fragment::Image(_) => {
|
||||
stacking_context.fragments.push(StackingContextFragment {
|
||||
space_and_clip: builder.current_space_and_clip,
|
||||
containing_block: *containing_block,
|
||||
fragment: self,
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxFragment {
|
||||
fn build_stacking_context_tree<'a>(
|
||||
&'a self,
|
||||
fragment: &'a Fragment,
|
||||
builder: &mut DisplayListBuilder,
|
||||
containing_block: &PhysicalRect<Length>,
|
||||
stacking_context: &mut StackingContext<'a>,
|
||||
) {
|
||||
builder.clipping_and_scrolling_scope(|builder| {
|
||||
self.adjust_spatial_id_for_positioning(builder);
|
||||
|
||||
stacking_context.fragments.push(StackingContextFragment {
|
||||
space_and_clip: builder.current_space_and_clip,
|
||||
containing_block: *containing_block,
|
||||
fragment,
|
||||
});
|
||||
|
||||
// We want to build the scroll frame after the background and border, because
|
||||
// they shouldn't scroll with the rest of the box content.
|
||||
self.build_scroll_frame_if_necessary(builder, containing_block);
|
||||
|
||||
let new_containing_block = self
|
||||
.content_rect
|
||||
.to_physical(self.style.writing_mode, containing_block)
|
||||
.translate(containing_block.origin.to_vector());
|
||||
for child in &self.children {
|
||||
child.build_stacking_context_tree(builder, &new_containing_block, stacking_context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn adjust_spatial_id_for_positioning(&self, builder: &mut DisplayListBuilder) {
|
||||
if self.style.get_box().position != ComputedPosition::Fixed {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(mrobinson): Eventually this should use the spatial id of the reference
|
||||
// frame that is the parent of this one once we have full support for stacking
|
||||
// contexts and transforms.
|
||||
builder.current_space_and_clip.spatial_id =
|
||||
SpatialId::root_reference_frame(builder.wr.pipeline_id);
|
||||
}
|
||||
|
||||
fn build_scroll_frame_if_necessary(
|
||||
&self,
|
||||
builder: &mut DisplayListBuilder,
|
||||
containing_block: &PhysicalRect<Length>,
|
||||
) {
|
||||
let overflow_x = self.style.get_box().overflow_x;
|
||||
let overflow_y = self.style.get_box().overflow_y;
|
||||
let original_scroll_and_clip_info = builder.current_space_and_clip;
|
||||
if overflow_x != ComputedOverflow::Visible || overflow_y != ComputedOverflow::Visible {
|
||||
// TODO(mrobinson): We should use the correct fragment type, once we generate
|
||||
// fragments from ::before and ::after generated content selectors.
|
||||
let id =
|
||||
combine_id_with_fragment_type(self.tag.id() as usize, FragmentType::FragmentBody)
|
||||
as u64;
|
||||
let external_id = ExternalScrollId(id, builder.wr.pipeline_id);
|
||||
|
||||
let sensitivity = if ComputedOverflow::Hidden == overflow_x &&
|
||||
ComputedOverflow::Hidden == overflow_y
|
||||
{
|
||||
ScrollSensitivity::Script
|
||||
} else {
|
||||
ScrollSensitivity::ScriptAndInputEvents
|
||||
};
|
||||
|
||||
let padding_rect = self
|
||||
.padding_rect()
|
||||
.to_physical(self.style.writing_mode, containing_block)
|
||||
.translate(containing_block.origin.to_vector())
|
||||
.to_webrender();
|
||||
builder.current_space_and_clip = builder.wr.define_scroll_frame(
|
||||
&original_scroll_and_clip_info,
|
||||
Some(external_id),
|
||||
self.scrollable_overflow().to_webrender(),
|
||||
padding_rect,
|
||||
vec![], // complex_clips
|
||||
None, // image_mask
|
||||
sensitivity,
|
||||
LayoutVector2D::zero(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AnonymousFragment {
|
||||
fn build_stacking_context_tree<'a>(
|
||||
&'a self,
|
||||
builder: &mut DisplayListBuilder,
|
||||
containing_block: &PhysicalRect<Length>,
|
||||
stacking_context: &mut StackingContext<'a>,
|
||||
) {
|
||||
let new_containing_block = self
|
||||
.rect
|
||||
.to_physical(self.mode, containing_block)
|
||||
.translate(containing_block.origin.to_vector());
|
||||
for child in &self.children {
|
||||
child.build_stacking_context_tree(builder, &new_containing_block, stacking_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> StackingContext<'a> {
|
||||
pub(crate) fn build_display_list(&'a self, builder: &'a mut DisplayListBuilder) {
|
||||
for child in &self.fragments {
|
||||
builder.current_space_and_clip = child.space_and_clip;
|
||||
child
|
||||
.fragment
|
||||
.build_display_list(builder, &child.containing_block);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -181,9 +181,16 @@ impl BoxTreeRoot {
|
|||
|
||||
impl FragmentTreeRoot {
|
||||
pub fn build_display_list(&self, builder: &mut crate::display_list::DisplayListBuilder) {
|
||||
let mut stacking_context = Default::default();
|
||||
for fragment in &self.children {
|
||||
fragment.build_display_list(builder, &self.initial_containing_block)
|
||||
fragment.build_stacking_context_tree(
|
||||
builder,
|
||||
&self.initial_containing_block,
|
||||
&mut stacking_context,
|
||||
);
|
||||
}
|
||||
|
||||
stacking_context.build_display_list(builder);
|
||||
}
|
||||
|
||||
pub fn print(&self) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue