mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
layout: Split stacking context and display list construction (#37047)
Previously, after a layout was finished (or skipped in the case of repaint-only layout), both the stacking context tree and display list were built. In the case of repaint-only layout, we should be able to skip the reconstruction of the stacking context tree and only do display list building. This change does that, also generally cleaning and up and clarifying the data structure used during this phase of layout. This opens up the possibility of a new kind of incremental layout that does both repaint and a rebuild of the stacking context tree. On the blaster.html test case[^1], this reduces tightly-measured layout time from ~45-50 milliseconds to ~25-30 milliseconds on my M3. [^1]: https://gist.github.com/mrobinson/44ec87d028c0198917a7715a06dd98a0 Testing: There are currently no performance tests for layout. :( This should not modify the results of WPT tests. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
27c8a899ea
commit
d8294fa423
13 changed files with 967 additions and 789 deletions
30
Cargo.lock
generated
30
Cargo.lock
generated
|
@ -1065,7 +1065,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -4261,7 +4261,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c"
|
checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-targets 0.48.5",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -6517,7 +6517,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "selectors"
|
name = "selectors"
|
||||||
version = "0.28.0"
|
version = "0.28.0"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#fb74b958cc7bb93a9335766130d6711ad3e071ce"
|
source = "git+https://github.com/servo/stylo?branch=2025-05-01#88b34ae4293ca7286584cee37bca1f58ab79a8cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
"cssparser",
|
"cssparser",
|
||||||
|
@ -6812,7 +6812,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "servo_arc"
|
name = "servo_arc"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#fb74b958cc7bb93a9335766130d6711ad3e071ce"
|
source = "git+https://github.com/servo/stylo?branch=2025-05-01#88b34ae4293ca7286584cee37bca1f58ab79a8cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
|
@ -7273,7 +7273,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stylo"
|
name = "stylo"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#fb74b958cc7bb93a9335766130d6711ad3e071ce"
|
source = "git+https://github.com/servo/stylo?branch=2025-05-01#88b34ae4293ca7286584cee37bca1f58ab79a8cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units",
|
"app_units",
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
|
@ -7331,7 +7331,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stylo_atoms"
|
name = "stylo_atoms"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#fb74b958cc7bb93a9335766130d6711ad3e071ce"
|
source = "git+https://github.com/servo/stylo?branch=2025-05-01#88b34ae4293ca7286584cee37bca1f58ab79a8cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"string_cache",
|
"string_cache",
|
||||||
"string_cache_codegen",
|
"string_cache_codegen",
|
||||||
|
@ -7340,12 +7340,12 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stylo_config"
|
name = "stylo_config"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#fb74b958cc7bb93a9335766130d6711ad3e071ce"
|
source = "git+https://github.com/servo/stylo?branch=2025-05-01#88b34ae4293ca7286584cee37bca1f58ab79a8cb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stylo_derive"
|
name = "stylo_derive"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#fb74b958cc7bb93a9335766130d6711ad3e071ce"
|
source = "git+https://github.com/servo/stylo?branch=2025-05-01#88b34ae4293ca7286584cee37bca1f58ab79a8cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -7357,7 +7357,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stylo_dom"
|
name = "stylo_dom"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#fb74b958cc7bb93a9335766130d6711ad3e071ce"
|
source = "git+https://github.com/servo/stylo?branch=2025-05-01#88b34ae4293ca7286584cee37bca1f58ab79a8cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
"stylo_malloc_size_of",
|
"stylo_malloc_size_of",
|
||||||
|
@ -7366,7 +7366,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stylo_malloc_size_of"
|
name = "stylo_malloc_size_of"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#fb74b958cc7bb93a9335766130d6711ad3e071ce"
|
source = "git+https://github.com/servo/stylo?branch=2025-05-01#88b34ae4293ca7286584cee37bca1f58ab79a8cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units",
|
"app_units",
|
||||||
"cssparser",
|
"cssparser",
|
||||||
|
@ -7383,12 +7383,12 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stylo_static_prefs"
|
name = "stylo_static_prefs"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#fb74b958cc7bb93a9335766130d6711ad3e071ce"
|
source = "git+https://github.com/servo/stylo?branch=2025-05-01#88b34ae4293ca7286584cee37bca1f58ab79a8cb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stylo_traits"
|
name = "stylo_traits"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#fb74b958cc7bb93a9335766130d6711ad3e071ce"
|
source = "git+https://github.com/servo/stylo?branch=2025-05-01#88b34ae4293ca7286584cee37bca1f58ab79a8cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units",
|
"app_units",
|
||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
|
@ -7771,7 +7771,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "to_shmem"
|
name = "to_shmem"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#fb74b958cc7bb93a9335766130d6711ad3e071ce"
|
source = "git+https://github.com/servo/stylo?branch=2025-05-01#88b34ae4293ca7286584cee37bca1f58ab79a8cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cssparser",
|
"cssparser",
|
||||||
"servo_arc",
|
"servo_arc",
|
||||||
|
@ -7784,7 +7784,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "to_shmem_derive"
|
name = "to_shmem_derive"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2025-05-01#fb74b958cc7bb93a9335766130d6711ad3e071ce"
|
source = "git+https://github.com/servo/stylo?branch=2025-05-01#88b34ae4293ca7286584cee37bca1f58ab79a8cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -8909,7 +8909,7 @@ version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -66,7 +66,7 @@ impl<'a> BackgroundPainter<'a> {
|
||||||
if &BackgroundAttachment::Fixed ==
|
if &BackgroundAttachment::Fixed ==
|
||||||
get_cyclic(&background.background_attachment.0, layer_index)
|
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);
|
return units::LayoutRect::from_origin_and_size(Point2D::origin(), viewport_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ impl<'a> BackgroundPainter<'a> {
|
||||||
if &BackgroundAttachment::Fixed ==
|
if &BackgroundAttachment::Fixed ==
|
||||||
get_cyclic(&style.get_background().background_attachment.0, layer_index)
|
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
|
common
|
||||||
}
|
}
|
||||||
|
|
276
components/layout/display_list/clip.rs
Normal file
276
components/layout/display_list/clip.rs
Normal file
|
@ -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(),
|
||||||
|
}
|
||||||
|
}
|
|
@ -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])
|
|
||||||
}
|
|
|
@ -8,13 +8,15 @@ use std::sync::Arc;
|
||||||
use app_units::{AU_PER_PX, Au};
|
use app_units::{AU_PER_PX, Au};
|
||||||
use base::WebRenderEpochToU16;
|
use base::WebRenderEpochToU16;
|
||||||
use base::id::ScrollTreeNodeId;
|
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 embedder_traits::Cursor;
|
||||||
use euclid::{Point2D, SideOffsets2D, Size2D, UnknownUnit};
|
use euclid::{Point2D, SideOffsets2D, Size2D, UnknownUnit};
|
||||||
use fonts::GlyphStore;
|
use fonts::GlyphStore;
|
||||||
use gradient::WebRenderGradient;
|
use gradient::WebRenderGradient;
|
||||||
use range::Range as ServoRange;
|
use range::Range as ServoRange;
|
||||||
use servo_arc::Arc as ServoArc;
|
use servo_arc::Arc as ServoArc;
|
||||||
|
use servo_config::opts::DebugOptions;
|
||||||
use servo_geometry::MaxRect;
|
use servo_geometry::MaxRect;
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
use style::color::{AbsoluteColor, ColorSpace};
|
use style::color::{AbsoluteColor, ColorSpace};
|
||||||
|
@ -35,8 +37,9 @@ use style::values::specified::ui::CursorKind;
|
||||||
use style_traits::CSSPixel;
|
use style_traits::CSSPixel;
|
||||||
use webrender_api::units::{DevicePixel, LayoutPixel, LayoutRect, LayoutSize};
|
use webrender_api::units::{DevicePixel, LayoutPixel, LayoutRect, LayoutSize};
|
||||||
use webrender_api::{
|
use webrender_api::{
|
||||||
self as wr, BorderDetails, BoxShadowClipMode, ClipChainId, CommonItemProperties,
|
self as wr, BorderDetails, BoxShadowClipMode, BuiltDisplayList, ClipChainId, ClipMode,
|
||||||
ImageRendering, NinePatchBorder, NinePatchBorderSource, SpatialId, units,
|
CommonItemProperties, ComplexClipRegion, ImageRendering, NinePatchBorder,
|
||||||
|
NinePatchBorderSource, PropertyBinding, SpatialId, SpatialTreeItemKey, units,
|
||||||
};
|
};
|
||||||
use wr::units::LayoutVector2D;
|
use wr::units::LayoutVector2D;
|
||||||
|
|
||||||
|
@ -55,7 +58,7 @@ use crate::replaced::NaturalSizes;
|
||||||
use crate::style_ext::{BorderStyleColor, ComputedValuesExt};
|
use crate::style_ext::{BorderStyleColor, ComputedValuesExt};
|
||||||
|
|
||||||
mod background;
|
mod background;
|
||||||
mod clip_path;
|
mod clip;
|
||||||
mod conversions;
|
mod conversions;
|
||||||
mod gradient;
|
mod gradient;
|
||||||
mod stacking_context;
|
mod stacking_context;
|
||||||
|
@ -74,68 +77,6 @@ type ItemTag = (u64, u16);
|
||||||
type HitInfo = Option<ItemTag>;
|
type HitInfo = Option<ItemTag>;
|
||||||
const INSERTION_POINT_LOGICAL_WIDTH: Au = Au(AU_PER_PX);
|
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> {
|
pub(crate) struct DisplayListBuilder<'a> {
|
||||||
/// The current [ScrollTreeNodeId] for this [DisplayListBuilder]. This
|
/// The current [ScrollTreeNodeId] for this [DisplayListBuilder]. This
|
||||||
/// allows only passing the builder instead passing the containing
|
/// 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.
|
/// `background-attachment: fixed` need to not scroll while the rest of the fragment does.
|
||||||
current_reference_frame_scroll_node_id: ScrollTreeNodeId,
|
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
|
/// only passing the builder instead passing the containing
|
||||||
/// [stacking_context::StackingContextContent::Fragment] as an argument to display
|
/// [stacking_context::StackingContextContent::Fragment] as an argument to display
|
||||||
/// list building functions.
|
/// list building functions.
|
||||||
current_clip_chain_id: ClipChainId,
|
current_clip_id: ClipId,
|
||||||
|
|
||||||
/// A [LayoutContext] used to get information about the device pixel ratio
|
/// A [LayoutContext] used to get information about the device pixel ratio
|
||||||
/// and get handles to WebRender images.
|
/// and get handles to WebRender images.
|
||||||
pub context: &'a LayoutContext<'a>,
|
pub context: &'a LayoutContext<'a>,
|
||||||
|
|
||||||
/// The [DisplayList] used to collect display list items and metadata.
|
/// The [`wr::DisplayListBuilder`] for this Servo [`DisplayListBuilder`].
|
||||||
pub display_list: &'a mut DisplayList,
|
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.
|
/// 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.
|
/// element inherits the `<body>`'s background to paint the page canvas background.
|
||||||
/// See <https://drafts.csswg.org/css-backgrounds/#body-background>.
|
/// See <https://drafts.csswg.org/css-backgrounds/#body-background>.
|
||||||
paint_body_background: bool,
|
paint_body_background: bool,
|
||||||
|
|
||||||
|
/// A mapping from [`ClipId`] To WebRender [`ClipChainId`] used when building this WebRender
|
||||||
|
/// display list.
|
||||||
|
clip_map: Vec<ClipChainId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InspectorHighlight {
|
struct InspectorHighlight {
|
||||||
|
@ -207,45 +155,224 @@ impl InspectorHighlight {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DisplayList {
|
impl DisplayListBuilder<'_> {
|
||||||
pub fn build(
|
pub(crate) fn build(
|
||||||
&mut self,
|
|
||||||
context: &LayoutContext,
|
context: &LayoutContext,
|
||||||
|
stacking_context_tree: &mut StackingContextTree,
|
||||||
fragment_tree: &FragmentTree,
|
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")]
|
#[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 {
|
let mut builder = DisplayListBuilder {
|
||||||
current_scroll_node_id: self.compositor_info.root_reference_frame_id,
|
current_scroll_node_id: compositor_info.root_reference_frame_id,
|
||||||
current_reference_frame_scroll_node_id: self.compositor_info.root_reference_frame_id,
|
current_reference_frame_scroll_node_id: compositor_info.root_reference_frame_id,
|
||||||
current_clip_chain_id: ClipChainId::INVALID,
|
current_clip_id: ClipId::INVALID,
|
||||||
context,
|
context,
|
||||||
display_list: self,
|
webrender_display_list_builder: &mut webrender_display_list_builder,
|
||||||
|
compositor_info,
|
||||||
inspector_highlight: context
|
inspector_highlight: context
|
||||||
.highlighted_dom_node
|
.highlighted_dom_node
|
||||||
.map(InspectorHighlight::for_node),
|
.map(InspectorHighlight::for_node),
|
||||||
paint_body_background: true,
|
paint_body_background: true,
|
||||||
|
clip_map: Default::default(),
|
||||||
};
|
};
|
||||||
fragment_tree.build_display_list(&mut builder, root_stacking_context);
|
|
||||||
|
|
||||||
if let Some(highlight) = builder
|
builder.add_all_spatial_nodes();
|
||||||
.inspector_highlight
|
|
||||||
.take()
|
for clip in stacking_context_tree.clip_store.0.iter() {
|
||||||
.and_then(|highlight| highlight.state)
|
builder.add_clip_to_display_list(clip);
|
||||||
{
|
|
||||||
builder.paint_dom_inspector_highlight(highlight);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DisplayListBuilder<'_> {
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
fn wr(&mut self) -> &mut wr::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) {
|
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(
|
fn common_properties(
|
||||||
|
@ -258,8 +385,8 @@ impl DisplayListBuilder<'_> {
|
||||||
// for fragments that paint their entire border rectangle.
|
// for fragments that paint their entire border rectangle.
|
||||||
wr::CommonItemProperties {
|
wr::CommonItemProperties {
|
||||||
clip_rect,
|
clip_rect,
|
||||||
spatial_id: self.current_scroll_node_id.spatial_id,
|
spatial_id: self.spatial_id(self.current_scroll_node_id),
|
||||||
clip_chain_id: self.current_clip_chain_id,
|
clip_chain_id: self.clip_chain_id(self.current_clip_id),
|
||||||
flags: style.get_webrender_primitive_flags(),
|
flags: style.get_webrender_primitive_flags(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -277,19 +404,24 @@ impl DisplayListBuilder<'_> {
|
||||||
return None;
|
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,
|
tag?.node.0 as u64,
|
||||||
Some(cursor(inherited_ui.cursor.keyword, auto_cursor)),
|
Some(cursor(inherited_ui.cursor.keyword, auto_cursor)),
|
||||||
self.current_scroll_node_id,
|
self.current_scroll_node_id,
|
||||||
);
|
);
|
||||||
Some((
|
Some((hit_test_index as u64, self.compositor_info.epoch.as_u16()))
|
||||||
hit_test_index as u64,
|
|
||||||
self.display_list.compositor_info.epoch.as_u16(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw highlights around the node that is currently hovered in the devtools.
|
/// 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 {
|
const CONTENT_BOX_HIGHLIGHT_COLOR: webrender_api::ColorF = webrender_api::ColorF {
|
||||||
r: 0.23,
|
r: 0.23,
|
||||||
g: 0.7,
|
g: 0.7,
|
||||||
|
@ -327,8 +459,7 @@ impl DisplayListBuilder<'_> {
|
||||||
flags: wr::PrimitiveFlags::default(),
|
flags: wr::PrimitiveFlags::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.display_list
|
self.wr()
|
||||||
.wr
|
|
||||||
.push_rect(&properties, content_box, CONTENT_BOX_HIGHLIGHT_COLOR);
|
.push_rect(&properties, content_box, CONTENT_BOX_HIGHLIGHT_COLOR);
|
||||||
|
|
||||||
// Highlight margin, border and padding
|
// Highlight margin, border and padding
|
||||||
|
@ -442,12 +573,14 @@ impl Fragment {
|
||||||
is_hit_test_for_scrollable_overflow: bool,
|
is_hit_test_for_scrollable_overflow: bool,
|
||||||
is_collapsed_table_borders: 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 let Some(inspector_highlight) = &mut builder.inspector_highlight {
|
||||||
if self.tag() == Some(inspector_highlight.tag) {
|
if self.tag() == Some(inspector_highlight.tag) {
|
||||||
inspector_highlight.register_fragment_of_highlighted_dom_node(
|
inspector_highlight.register_fragment_of_highlighted_dom_node(
|
||||||
self,
|
self,
|
||||||
builder.current_scroll_node_id.spatial_id,
|
spatial_id,
|
||||||
builder.current_clip_chain_id,
|
clip_chain_id,
|
||||||
containing_block,
|
containing_block,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -574,8 +707,8 @@ impl Fragment {
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let clip_chain_id = builder.current_clip_chain_id;
|
let clip_chain_id = builder.clip_chain_id(builder.current_clip_id);
|
||||||
let spatial_id = builder.current_scroll_node_id.spatial_id;
|
let spatial_id = builder.spatial_id(builder.current_scroll_node_id);
|
||||||
builder.wr().push_hit_test(
|
builder.wr().push_hit_test(
|
||||||
rect.to_webrender(),
|
rect.to_webrender(),
|
||||||
clip_chain_id,
|
clip_chain_id,
|
||||||
|
@ -765,8 +898,9 @@ impl Fragment {
|
||||||
if text_decoration_style == ComputedTextDecorationStyle::MozNone {
|
if text_decoration_style == ComputedTextDecorationStyle::MozNone {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
builder.display_list.wr.push_line(
|
let common_properties = builder.common_properties(rect, parent_style);
|
||||||
&builder.common_properties(rect, parent_style),
|
builder.wr().push_line(
|
||||||
|
&common_properties,
|
||||||
&rect,
|
&rect,
|
||||||
wavy_line_thickness,
|
wavy_line_thickness,
|
||||||
wr::LineOrientation::Horizontal,
|
wr::LineOrientation::Horizontal,
|
||||||
|
@ -878,12 +1012,8 @@ impl<'a> BuilderForBoxFragment<'a> {
|
||||||
return Some(clip);
|
return Some(clip);
|
||||||
}
|
}
|
||||||
|
|
||||||
let maybe_clip = create_clip_chain(
|
let maybe_clip =
|
||||||
self.border_radius,
|
builder.maybe_create_clip(self.border_radius, self.border_rect, force_clip_creation);
|
||||||
self.border_rect,
|
|
||||||
builder,
|
|
||||||
force_clip_creation,
|
|
||||||
);
|
|
||||||
*self.border_edge_clip_chain_id.borrow_mut() = maybe_clip;
|
*self.border_edge_clip_chain_id.borrow_mut() = maybe_clip;
|
||||||
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 radii = inner_radii(self.border_radius, self.fragment.border.to_webrender());
|
||||||
let maybe_clip =
|
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;
|
*self.padding_edge_clip_chain_id.borrow_mut() = maybe_clip;
|
||||||
maybe_clip
|
maybe_clip
|
||||||
}
|
}
|
||||||
|
@ -918,7 +1048,7 @@ impl<'a> BuilderForBoxFragment<'a> {
|
||||||
(self.fragment.border + self.fragment.padding).to_webrender(),
|
(self.fragment.border + self.fragment.padding).to_webrender(),
|
||||||
);
|
);
|
||||||
let maybe_clip =
|
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;
|
*self.content_edge_clip_chain_id.borrow_mut() = maybe_clip;
|
||||||
maybe_clip
|
maybe_clip
|
||||||
}
|
}
|
||||||
|
@ -1553,38 +1683,6 @@ fn offset_radii(mut radii: wr::BorderRadius, offset: f32) -> wr::BorderRadius {
|
||||||
radii
|
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.
|
/// Resolve the WebRender border-image outset area from the style values.
|
||||||
fn resolve_border_image_outset(
|
fn resolve_border_image_outset(
|
||||||
outset: BorderImageOutset,
|
outset: BorderImageOutset,
|
||||||
|
|
|
@ -9,18 +9,19 @@ use std::mem;
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use base::id::ScrollTreeNodeId;
|
use base::id::ScrollTreeNodeId;
|
||||||
use base::print_tree::PrintTree;
|
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::SideOffsets2D;
|
||||||
use euclid::default::{Point2D, Rect, Size2D};
|
use euclid::default::{Point2D, Rect, Size2D};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use servo_arc::Arc as ServoArc;
|
|
||||||
use servo_config::opts::DebugOptions;
|
use servo_config::opts::DebugOptions;
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
use style::computed_values::float::T as ComputedFloat;
|
use style::computed_values::float::T as ComputedFloat;
|
||||||
use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode;
|
use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode;
|
||||||
use style::computed_values::overflow_x::T as ComputedOverflow;
|
use style::computed_values::overflow_x::T as ComputedOverflow;
|
||||||
use style::computed_values::position::T as ComputedPosition;
|
use style::computed_values::position::T as ComputedPosition;
|
||||||
use style::properties::ComputedValues;
|
|
||||||
use style::values::computed::angle::Angle;
|
use style::values::computed::angle::Angle;
|
||||||
use style::values::computed::basic_shape::ClipPath;
|
use style::values::computed::basic_shape::ClipPath;
|
||||||
use style::values::computed::{ClipRectOrAuto, Length};
|
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 style::values::specified::box_::DisplayOutside;
|
||||||
use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D};
|
use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D};
|
||||||
use webrender_api::{self as wr, BorderRadius};
|
use webrender_api::{self as wr, BorderRadius};
|
||||||
|
use wr::StickyOffsetBounds;
|
||||||
use wr::units::{LayoutPixel, LayoutSize};
|
use wr::units::{LayoutPixel, LayoutSize};
|
||||||
use wr::{ClipChainId, SpatialTreeItemKey, StickyOffsetBounds};
|
|
||||||
|
|
||||||
use super::DisplayList;
|
use super::ClipId;
|
||||||
use super::clip_path::build_clip_path_clip_chain_if_necessary;
|
use super::clip::StackingContextTreeClipStore;
|
||||||
|
use crate::ArcRefCell;
|
||||||
use crate::display_list::conversions::{FilterToWebRender, ToWebRender};
|
use crate::display_list::conversions::{FilterToWebRender, ToWebRender};
|
||||||
use crate::display_list::{BuilderForBoxFragment, DisplayListBuilder, offset_radii};
|
use crate::display_list::{BuilderForBoxFragment, DisplayListBuilder, offset_radii};
|
||||||
use crate::fragment_tree::{
|
use crate::fragment_tree::{
|
||||||
|
@ -54,9 +56,8 @@ pub(crate) struct ContainingBlock {
|
||||||
/// frame and sticky positioning isn't taken into account.
|
/// frame and sticky positioning isn't taken into account.
|
||||||
scroll_frame_size: Option<LayoutSize>,
|
scroll_frame_size: Option<LayoutSize>,
|
||||||
|
|
||||||
/// The WebRender ClipId to use for this children of this containing
|
/// The [`ClipId`] to use for the children of this containing block.
|
||||||
/// block.
|
clip_id: ClipId,
|
||||||
clip_chain_id: wr::ClipChainId,
|
|
||||||
|
|
||||||
/// The physical rect of this containing block.
|
/// The physical rect of this containing block.
|
||||||
rect: PhysicalRect<Au>,
|
rect: PhysicalRect<Au>,
|
||||||
|
@ -67,12 +68,12 @@ impl ContainingBlock {
|
||||||
rect: PhysicalRect<Au>,
|
rect: PhysicalRect<Au>,
|
||||||
scroll_node_id: ScrollTreeNodeId,
|
scroll_node_id: ScrollTreeNodeId,
|
||||||
scroll_frame_size: Option<LayoutSize>,
|
scroll_frame_size: Option<LayoutSize>,
|
||||||
clip_chain_id: wr::ClipChainId,
|
clip_id: ClipId,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
ContainingBlock {
|
ContainingBlock {
|
||||||
scroll_node_id,
|
scroll_node_id,
|
||||||
scroll_frame_size,
|
scroll_frame_size,
|
||||||
clip_chain_id,
|
clip_id,
|
||||||
rect,
|
rect,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,40 +96,56 @@ pub(crate) enum StackingContextSection {
|
||||||
Outline,
|
Outline,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DisplayList {
|
pub(crate) struct StackingContextTree {
|
||||||
/// Produce a new SpatialTreeItemKey. This is currently unused by WebRender,
|
/// The root stacking context of this [`StackingContextTree`].
|
||||||
/// but has to be unique to the entire scene.
|
pub root_stacking_context: StackingContext,
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(
|
/// The information about the WebRender display list that the compositor
|
||||||
feature = "tracing",
|
/// consumes. This curerntly contains the out-of-band hit testing information
|
||||||
tracing::instrument(
|
/// data structure that the compositor uses to map hit tests to information
|
||||||
name = "display_list::build_stacking_context_tree",
|
/// about the item hit.
|
||||||
skip_all,
|
pub compositor_info: CompositorDisplayListInfo,
|
||||||
fields(servo_profiling = true),
|
|
||||||
level = "trace",
|
/// 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 fn build_stacking_context_tree(
|
pub clip_store: StackingContextTreeClipStore,
|
||||||
&mut self,
|
}
|
||||||
|
|
||||||
|
impl StackingContextTree {
|
||||||
|
/// Create a new [DisplayList] given the dimensions of the layout and the WebRender
|
||||||
|
/// pipeline id.
|
||||||
|
pub fn new(
|
||||||
fragment_tree: &FragmentTree,
|
fragment_tree: &FragmentTree,
|
||||||
|
viewport_size: LayoutSize,
|
||||||
|
content_size: LayoutSize,
|
||||||
|
pipeline_id: wr::PipelineId,
|
||||||
|
viewport_scroll_sensitivity: AxesScrollSensitivity,
|
||||||
|
first_reflow: bool,
|
||||||
debug: &DebugOptions,
|
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(
|
let cb_for_non_fixed_descendants = ContainingBlock::new(
|
||||||
fragment_tree.initial_containing_block,
|
fragment_tree.initial_containing_block,
|
||||||
self.compositor_info.root_scroll_node_id,
|
root_scroll_node_id,
|
||||||
Some(self.compositor_info.viewport_size),
|
Some(compositor_info.viewport_size),
|
||||||
ClipChainId::INVALID,
|
ClipId::INVALID,
|
||||||
);
|
);
|
||||||
let cb_for_fixed_descendants = ContainingBlock::new(
|
let cb_for_fixed_descendants = ContainingBlock::new(
|
||||||
fragment_tree.initial_containing_block,
|
fragment_tree.initial_containing_block,
|
||||||
self.compositor_info.root_reference_frame_id,
|
compositor_info.root_reference_frame_id,
|
||||||
None,
|
None,
|
||||||
ClipChainId::INVALID,
|
ClipId::INVALID,
|
||||||
);
|
);
|
||||||
|
|
||||||
// We need to specify all three containing blocks here, because absolute
|
// 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,
|
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 {
|
for fragment in &fragment_tree.root_fragments {
|
||||||
fragment.build_stacking_context_tree(
|
fragment.build_stacking_context_tree(
|
||||||
self,
|
&mut stacking_context_tree,
|
||||||
&containing_block_info,
|
&containing_block_info,
|
||||||
&mut root_stacking_context,
|
&mut root_stacking_context,
|
||||||
StackingContextBuildMode::SkipHoisted,
|
StackingContextBuildMode::SkipHoisted,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
root_stacking_context.sort();
|
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(
|
fn push_reference_frame(
|
||||||
|
@ -161,53 +192,20 @@ impl DisplayList {
|
||||||
origin: LayoutPoint,
|
origin: LayoutPoint,
|
||||||
parent_scroll_node_id: &ScrollTreeNodeId,
|
parent_scroll_node_id: &ScrollTreeNodeId,
|
||||||
transform_style: wr::TransformStyle,
|
transform_style: wr::TransformStyle,
|
||||||
transform: wr::PropertyBinding<LayoutTransform>,
|
transform: LayoutTransform,
|
||||||
kind: wr::ReferenceFrameKind,
|
kind: wr::ReferenceFrameKind,
|
||||||
) -> ScrollTreeNodeId {
|
) -> 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(
|
self.compositor_info.scroll_tree.add_scroll_tree_node(
|
||||||
Some(parent_scroll_node_id),
|
Some(parent_scroll_node_id),
|
||||||
new_spatial_id,
|
SpatialTreeNodeInfo::ReferenceFrame(ReferenceFrameNodeInfo {
|
||||||
None,
|
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(
|
fn define_scroll_frame(
|
||||||
&mut self,
|
&mut self,
|
||||||
parent_scroll_node_id: &ScrollTreeNodeId,
|
parent_scroll_node_id: &ScrollTreeNodeId,
|
||||||
|
@ -216,25 +214,12 @@ impl DisplayList {
|
||||||
clip_rect: LayoutRect,
|
clip_rect: LayoutRect,
|
||||||
scroll_sensitivity: AxesScrollSensitivity,
|
scroll_sensitivity: AxesScrollSensitivity,
|
||||||
) -> ScrollTreeNodeId {
|
) -> 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(
|
self.compositor_info.scroll_tree.add_scroll_tree_node(
|
||||||
Some(parent_scroll_node_id),
|
Some(parent_scroll_node_id),
|
||||||
new_spatial_id,
|
SpatialTreeNodeInfo::Scroll(ScrollableNodeInfo {
|
||||||
Some(ScrollableNodeInfo {
|
|
||||||
external_id,
|
external_id,
|
||||||
scrollable_size: content_rect.size() - clip_rect.size(),
|
content_rect,
|
||||||
|
clip_rect,
|
||||||
scroll_sensitivity,
|
scroll_sensitivity,
|
||||||
offset: LayoutVector2D::zero(),
|
offset: LayoutVector2D::zero(),
|
||||||
}),
|
}),
|
||||||
|
@ -249,21 +234,14 @@ impl DisplayList {
|
||||||
vertical_offset_bounds: StickyOffsetBounds,
|
vertical_offset_bounds: StickyOffsetBounds,
|
||||||
horizontal_offset_bounds: StickyOffsetBounds,
|
horizontal_offset_bounds: StickyOffsetBounds,
|
||||||
) -> ScrollTreeNodeId {
|
) -> 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(
|
self.compositor_info.scroll_tree.add_scroll_tree_node(
|
||||||
Some(parent_scroll_node_id),
|
Some(parent_scroll_node_id),
|
||||||
new_spatial_id,
|
SpatialTreeNodeInfo::Sticky(StickyNodeInfo {
|
||||||
None,
|
frame_rect,
|
||||||
|
margins,
|
||||||
|
vertical_offset_bounds,
|
||||||
|
horizontal_offset_bounds,
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -277,7 +255,7 @@ pub(crate) enum StackingContextContent {
|
||||||
Fragment {
|
Fragment {
|
||||||
scroll_node_id: ScrollTreeNodeId,
|
scroll_node_id: ScrollTreeNodeId,
|
||||||
reference_frame_scroll_node_id: ScrollTreeNodeId,
|
reference_frame_scroll_node_id: ScrollTreeNodeId,
|
||||||
clip_chain_id: wr::ClipChainId,
|
clip_id: ClipId,
|
||||||
section: StackingContextSection,
|
section: StackingContextSection,
|
||||||
containing_block: PhysicalRect<Au>,
|
containing_block: PhysicalRect<Au>,
|
||||||
fragment: Fragment,
|
fragment: Fragment,
|
||||||
|
@ -308,7 +286,7 @@ impl StackingContextContent {
|
||||||
Self::Fragment {
|
Self::Fragment {
|
||||||
scroll_node_id,
|
scroll_node_id,
|
||||||
reference_frame_scroll_node_id,
|
reference_frame_scroll_node_id,
|
||||||
clip_chain_id,
|
clip_id,
|
||||||
section,
|
section,
|
||||||
containing_block,
|
containing_block,
|
||||||
fragment,
|
fragment,
|
||||||
|
@ -317,7 +295,7 @@ impl StackingContextContent {
|
||||||
} => {
|
} => {
|
||||||
builder.current_scroll_node_id = *scroll_node_id;
|
builder.current_scroll_node_id = *scroll_node_id;
|
||||||
builder.current_reference_frame_scroll_node_id = *reference_frame_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(
|
fragment.build_display_list(
|
||||||
builder,
|
builder,
|
||||||
containing_block,
|
containing_block,
|
||||||
|
@ -349,16 +327,14 @@ pub(crate) enum StackingContextType {
|
||||||
pub struct StackingContext {
|
pub struct StackingContext {
|
||||||
/// The spatial id of this fragment. This is used to properly handle
|
/// The spatial id of this fragment. This is used to properly handle
|
||||||
/// things like preserve-3d.
|
/// 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.
|
/// The clip chain id of this stacking context if it has one. Used for filter clipping.
|
||||||
clip_chain_id: Option<wr::ClipChainId>,
|
clip_id: Option<ClipId>,
|
||||||
|
|
||||||
/// The style of the fragment that established this stacking context.
|
/// The [`BoxFragment`] that established this stacking context. We store the fragment here
|
||||||
initializing_fragment_style: Option<ServoArc<ComputedValues>>,
|
/// rather than just the style, so that incremental layout can automatically update the style.
|
||||||
|
initializing_fragment: Option<ArcRefCell<BoxFragment>>,
|
||||||
/// The [`FragmentFlags`] of the [`Fragment`] that established this stacking context.
|
|
||||||
initializing_fragment_flags: FragmentFlags,
|
|
||||||
|
|
||||||
/// The type of this stacking context. Used for collecting and sorting.
|
/// The type of this stacking context. Used for collecting and sorting.
|
||||||
context_type: StackingContextType,
|
context_type: StackingContextType,
|
||||||
|
@ -415,25 +391,23 @@ pub enum DebugPrintField {
|
||||||
impl StackingContext {
|
impl StackingContext {
|
||||||
fn create_descendant(
|
fn create_descendant(
|
||||||
&self,
|
&self,
|
||||||
spatial_id: wr::SpatialId,
|
spatial_id: ScrollTreeNodeId,
|
||||||
clip_chain_id: wr::ClipChainId,
|
clip_id: ClipId,
|
||||||
initializing_fragment_style: ServoArc<ComputedValues>,
|
initializing_fragment: ArcRefCell<BoxFragment>,
|
||||||
initializing_fragment_flags: FragmentFlags,
|
|
||||||
context_type: StackingContextType,
|
context_type: StackingContextType,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// WebRender has two different ways of expressing "no clip." ClipChainId::INVALID should be
|
// 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
|
// 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.
|
// leads to a crash.
|
||||||
let clip_chain_id: Option<ClipChainId> = match clip_chain_id {
|
let clip_id = match clip_id {
|
||||||
ClipChainId::INVALID => None,
|
ClipId::INVALID => None,
|
||||||
clip_chain_id => Some(clip_chain_id),
|
clip_id => Some(clip_id),
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
spatial_id,
|
scroll_tree_node_id: spatial_id,
|
||||||
clip_chain_id,
|
clip_id,
|
||||||
initializing_fragment_style: Some(initializing_fragment_style),
|
initializing_fragment: Some(initializing_fragment),
|
||||||
initializing_fragment_flags,
|
|
||||||
context_type,
|
context_type,
|
||||||
contents: vec![],
|
contents: vec![],
|
||||||
real_stacking_contexts_and_positioned_stacking_containers: 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 {
|
Self {
|
||||||
spatial_id: wr::SpaceAndClipInfo::root_scroll(wr.pipeline_id).spatial_id,
|
scroll_tree_node_id: root_scroll_node_id,
|
||||||
clip_chain_id: None,
|
clip_id: None,
|
||||||
initializing_fragment_style: None,
|
initializing_fragment: None,
|
||||||
initializing_fragment_flags: FragmentFlags::empty(),
|
|
||||||
context_type: StackingContextType::RealStackingContext,
|
context_type: StackingContextType::RealStackingContext,
|
||||||
contents: vec![],
|
contents: vec![],
|
||||||
real_stacking_contexts_and_positioned_stacking_containers: vec![],
|
real_stacking_contexts_and_positioned_stacking_containers: vec![],
|
||||||
|
@ -476,11 +449,10 @@ impl StackingContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn z_index(&self) -> i32 {
|
fn z_index(&self) -> i32 {
|
||||||
self.initializing_fragment_style
|
self.initializing_fragment.as_ref().map_or(0, |fragment| {
|
||||||
.as_ref()
|
let fragment = fragment.borrow();
|
||||||
.map_or(0, |style| {
|
fragment.style.effective_z_index(fragment.base.flags)
|
||||||
style.effective_z_index(self.initializing_fragment_flags)
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn sort(&mut self) {
|
pub(crate) fn sort(&mut self) {
|
||||||
|
@ -519,13 +491,14 @@ impl StackingContext {
|
||||||
&self,
|
&self,
|
||||||
builder: &mut DisplayListBuilder,
|
builder: &mut DisplayListBuilder,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let style = match self.initializing_fragment_style.as_ref() {
|
let fragment = match self.initializing_fragment.as_ref() {
|
||||||
Some(style) => style,
|
Some(fragment) => fragment.borrow(),
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// WebRender only uses the stacking context to apply certain effects. If we don't
|
// 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.
|
// actually need to create a stacking context, just avoid creating one.
|
||||||
|
let style = &fragment.style;
|
||||||
let effects = style.get_effects();
|
let effects = style.get_effects();
|
||||||
if effects.filter.0.is_empty() &&
|
if effects.filter.0.is_empty() &&
|
||||||
effects.opacity == 1.0 &&
|
effects.opacity == 1.0 &&
|
||||||
|
@ -557,11 +530,13 @@ impl StackingContext {
|
||||||
// This will require additional tracking during layout
|
// This will require additional tracking during layout
|
||||||
// before we start collecting stacking contexts so that
|
// before we start collecting stacking contexts so that
|
||||||
// information will be available when we reach this point.
|
// 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(
|
builder.wr().push_stacking_context(
|
||||||
LayoutPoint::zero(), // origin
|
LayoutPoint::zero(), // origin
|
||||||
self.spatial_id,
|
spatial_id,
|
||||||
style.get_webrender_primitive_flags(),
|
style.get_webrender_primitive_flags(),
|
||||||
self.clip_chain_id,
|
clip_chain_id,
|
||||||
style.get_used_transform_style().to_webrender(),
|
style.get_used_transform_style().to_webrender(),
|
||||||
effects.mix_blend_mode.to_webrender(),
|
effects.mix_blend_mode.to_webrender(),
|
||||||
&filters,
|
&filters,
|
||||||
|
@ -635,10 +610,7 @@ impl StackingContext {
|
||||||
if background_color.alpha > 0.0 {
|
if background_color.alpha > 0.0 {
|
||||||
let common = builder.common_properties(painting_area, &source_style);
|
let common = builder.common_properties(painting_area, &source_style);
|
||||||
let color = super::rgba(background_color);
|
let color = super::rgba(background_color);
|
||||||
builder
|
builder.wr().push_rect(&common, painting_area, color)
|
||||||
.display_list
|
|
||||||
.wr
|
|
||||||
.push_rect(&common, painting_area, color)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut fragment_builder = BuilderForBoxFragment::new(
|
let mut fragment_builder = BuilderForBoxFragment::new(
|
||||||
|
@ -741,7 +713,7 @@ impl StackingContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
if pushed_context {
|
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 {
|
impl Fragment {
|
||||||
pub(crate) fn build_stacking_context_tree(
|
pub(crate) fn build_stacking_context_tree(
|
||||||
&self,
|
&self,
|
||||||
display_list: &mut DisplayList,
|
stacking_context_tree: &mut StackingContextTree,
|
||||||
containing_block_info: &ContainingBlockInfo,
|
containing_block_info: &ContainingBlockInfo,
|
||||||
stacking_context: &mut StackingContext,
|
stacking_context: &mut StackingContext,
|
||||||
mode: StackingContextBuildMode,
|
mode: StackingContextBuildMode,
|
||||||
|
@ -852,7 +824,7 @@ impl Fragment {
|
||||||
|
|
||||||
fragment.build_stacking_context_tree(
|
fragment.build_stacking_context_tree(
|
||||||
fragment_clone,
|
fragment_clone,
|
||||||
display_list,
|
stacking_context_tree,
|
||||||
containing_block,
|
containing_block,
|
||||||
containing_block_info,
|
containing_block_info,
|
||||||
stacking_context,
|
stacking_context,
|
||||||
|
@ -866,7 +838,7 @@ impl Fragment {
|
||||||
};
|
};
|
||||||
|
|
||||||
fragment_ref.build_stacking_context_tree(
|
fragment_ref.build_stacking_context_tree(
|
||||||
display_list,
|
stacking_context_tree,
|
||||||
containing_block_info,
|
containing_block_info,
|
||||||
stacking_context,
|
stacking_context,
|
||||||
StackingContextBuildMode::IncludeHoisted,
|
StackingContextBuildMode::IncludeHoisted,
|
||||||
|
@ -875,7 +847,7 @@ impl Fragment {
|
||||||
Fragment::Positioning(fragment) => {
|
Fragment::Positioning(fragment) => {
|
||||||
let fragment = fragment.borrow();
|
let fragment = fragment.borrow();
|
||||||
fragment.build_stacking_context_tree(
|
fragment.build_stacking_context_tree(
|
||||||
display_list,
|
stacking_context_tree,
|
||||||
containing_block,
|
containing_block,
|
||||||
containing_block_info,
|
containing_block_info,
|
||||||
stacking_context,
|
stacking_context,
|
||||||
|
@ -890,7 +862,7 @@ impl Fragment {
|
||||||
reference_frame_scroll_node_id: containing_block_info
|
reference_frame_scroll_node_id: containing_block_info
|
||||||
.for_absolute_and_fixed_descendants
|
.for_absolute_and_fixed_descendants
|
||||||
.scroll_node_id,
|
.scroll_node_id,
|
||||||
clip_chain_id: containing_block.clip_chain_id,
|
clip_id: containing_block.clip_id,
|
||||||
containing_block: containing_block.rect,
|
containing_block: containing_block.rect,
|
||||||
fragment: fragment_clone,
|
fragment: fragment_clone,
|
||||||
is_hit_test_for_scrollable_overflow: false,
|
is_hit_test_for_scrollable_overflow: false,
|
||||||
|
@ -912,7 +884,7 @@ struct ScrollFrameData {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OverflowFrameData {
|
struct OverflowFrameData {
|
||||||
clip_chain_id: wr::ClipChainId,
|
clip_id: ClipId,
|
||||||
scroll_frame_data: Option<ScrollFrameData>,
|
scroll_frame_data: Option<ScrollFrameData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -953,14 +925,14 @@ impl BoxFragment {
|
||||||
fn build_stacking_context_tree(
|
fn build_stacking_context_tree(
|
||||||
&self,
|
&self,
|
||||||
fragment: Fragment,
|
fragment: Fragment,
|
||||||
display_list: &mut DisplayList,
|
stacking_context_tree: &mut StackingContextTree,
|
||||||
containing_block: &ContainingBlock,
|
containing_block: &ContainingBlock,
|
||||||
containing_block_info: &ContainingBlockInfo,
|
containing_block_info: &ContainingBlockInfo,
|
||||||
parent_stacking_context: &mut StackingContext,
|
parent_stacking_context: &mut StackingContext,
|
||||||
) {
|
) {
|
||||||
self.build_stacking_context_tree_maybe_creating_reference_frame(
|
self.build_stacking_context_tree_maybe_creating_reference_frame(
|
||||||
fragment,
|
fragment,
|
||||||
display_list,
|
stacking_context_tree,
|
||||||
containing_block,
|
containing_block,
|
||||||
containing_block_info,
|
containing_block_info,
|
||||||
parent_stacking_context,
|
parent_stacking_context,
|
||||||
|
@ -970,7 +942,7 @@ impl BoxFragment {
|
||||||
fn build_stacking_context_tree_maybe_creating_reference_frame(
|
fn build_stacking_context_tree_maybe_creating_reference_frame(
|
||||||
&self,
|
&self,
|
||||||
fragment: Fragment,
|
fragment: Fragment,
|
||||||
display_list: &mut DisplayList,
|
stacking_context_tree: &mut StackingContextTree,
|
||||||
containing_block: &ContainingBlock,
|
containing_block: &ContainingBlock,
|
||||||
containing_block_info: &ContainingBlockInfo,
|
containing_block_info: &ContainingBlockInfo,
|
||||||
parent_stacking_context: &mut StackingContext,
|
parent_stacking_context: &mut StackingContext,
|
||||||
|
@ -981,7 +953,7 @@ impl BoxFragment {
|
||||||
None => {
|
None => {
|
||||||
return self.build_stacking_context_tree_maybe_creating_stacking_context(
|
return self.build_stacking_context_tree_maybe_creating_stacking_context(
|
||||||
fragment,
|
fragment,
|
||||||
display_list,
|
stacking_context_tree,
|
||||||
containing_block,
|
containing_block,
|
||||||
containing_block_info,
|
containing_block_info,
|
||||||
parent_stacking_context,
|
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(),
|
reference_frame_data.origin.to_webrender(),
|
||||||
&containing_block.scroll_node_id,
|
&containing_block.scroll_node_id,
|
||||||
self.style.get_box().transform_style.to_webrender(),
|
self.style.get_box().transform_style.to_webrender(),
|
||||||
wr::PropertyBinding::Value(reference_frame_data.transform),
|
reference_frame_data.transform,
|
||||||
reference_frame_data.kind,
|
reference_frame_data.kind,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1016,26 +988,24 @@ impl BoxFragment {
|
||||||
.translate(-reference_frame_data.origin.to_vector()),
|
.translate(-reference_frame_data.origin.to_vector()),
|
||||||
new_spatial_id,
|
new_spatial_id,
|
||||||
None,
|
None,
|
||||||
containing_block.clip_chain_id,
|
containing_block.clip_id,
|
||||||
);
|
);
|
||||||
let new_containing_block_info =
|
let new_containing_block_info =
|
||||||
containing_block_info.new_for_non_absolute_descendants(&adjusted_containing_block);
|
containing_block_info.new_for_non_absolute_descendants(&adjusted_containing_block);
|
||||||
|
|
||||||
self.build_stacking_context_tree_maybe_creating_stacking_context(
|
self.build_stacking_context_tree_maybe_creating_stacking_context(
|
||||||
fragment,
|
fragment,
|
||||||
display_list,
|
stacking_context_tree,
|
||||||
&adjusted_containing_block,
|
&adjusted_containing_block,
|
||||||
&new_containing_block_info,
|
&new_containing_block_info,
|
||||||
parent_stacking_context,
|
parent_stacking_context,
|
||||||
);
|
);
|
||||||
|
|
||||||
display_list.pop_reference_frame();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_stacking_context_tree_maybe_creating_stacking_context(
|
fn build_stacking_context_tree_maybe_creating_stacking_context(
|
||||||
&self,
|
&self,
|
||||||
fragment: Fragment,
|
fragment: Fragment,
|
||||||
display_list: &mut DisplayList,
|
stacking_context_tree: &mut StackingContextTree,
|
||||||
containing_block: &ContainingBlock,
|
containing_block: &ContainingBlock,
|
||||||
containing_block_info: &ContainingBlockInfo,
|
containing_block_info: &ContainingBlockInfo,
|
||||||
parent_stacking_context: &mut StackingContext,
|
parent_stacking_context: &mut StackingContext,
|
||||||
|
@ -1045,7 +1015,7 @@ impl BoxFragment {
|
||||||
None => {
|
None => {
|
||||||
self.build_stacking_context_tree_for_children(
|
self.build_stacking_context_tree_for_children(
|
||||||
fragment,
|
fragment,
|
||||||
display_list,
|
stacking_context_tree,
|
||||||
containing_block,
|
containing_block,
|
||||||
containing_block_info,
|
containing_block_info,
|
||||||
parent_stacking_context,
|
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
|
// `clip-path` needs to be applied before filters and creates a stacking context, so it can be
|
||||||
// applied directly to the stacking context itself.
|
// applied directly to the stacking context itself.
|
||||||
// before
|
// before
|
||||||
let stacking_context_clip_chain_id = build_clip_path_clip_chain_if_necessary(
|
let stacking_context_clip_id = stacking_context_tree
|
||||||
self.style.clone_clip_path(),
|
.clip_store
|
||||||
display_list,
|
.add_for_clip_path(
|
||||||
&containing_block.scroll_node_id,
|
self.style.clone_clip_path(),
|
||||||
&containing_block.clip_chain_id,
|
&containing_block.scroll_node_id,
|
||||||
BuilderForBoxFragment::new(
|
&containing_block.clip_id,
|
||||||
self,
|
BuilderForBoxFragment::new(
|
||||||
&containing_block.rect,
|
self,
|
||||||
false, /* is_hit_test_for_scrollable_overflow */
|
&containing_block.rect,
|
||||||
false, /* is_collapsed_table_borders */
|
false, /* is_hit_test_for_scrollable_overflow */
|
||||||
),
|
false, /* is_collapsed_table_borders */
|
||||||
)
|
),
|
||||||
.unwrap_or(containing_block.clip_chain_id);
|
)
|
||||||
|
.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(
|
let mut child_stacking_context = parent_stacking_context.create_descendant(
|
||||||
containing_block.scroll_node_id.spatial_id,
|
containing_block.scroll_node_id,
|
||||||
stacking_context_clip_chain_id,
|
stacking_context_clip_id,
|
||||||
self.style.clone(),
|
box_fragment,
|
||||||
self.base.flags,
|
|
||||||
context_type,
|
context_type,
|
||||||
);
|
);
|
||||||
self.build_stacking_context_tree_for_children(
|
self.build_stacking_context_tree_for_children(
|
||||||
fragment,
|
fragment,
|
||||||
display_list,
|
stacking_context_tree,
|
||||||
containing_block,
|
containing_block,
|
||||||
containing_block_info,
|
containing_block_info,
|
||||||
&mut child_stacking_context,
|
&mut child_stacking_context,
|
||||||
|
@ -1116,19 +1093,19 @@ impl BoxFragment {
|
||||||
fn build_stacking_context_tree_for_children(
|
fn build_stacking_context_tree_for_children(
|
||||||
&self,
|
&self,
|
||||||
fragment: Fragment,
|
fragment: Fragment,
|
||||||
display_list: &mut DisplayList,
|
stacking_context_tree: &mut StackingContextTree,
|
||||||
containing_block: &ContainingBlock,
|
containing_block: &ContainingBlock,
|
||||||
containing_block_info: &ContainingBlockInfo,
|
containing_block_info: &ContainingBlockInfo,
|
||||||
stacking_context: &mut StackingContext,
|
stacking_context: &mut StackingContext,
|
||||||
) {
|
) {
|
||||||
let mut new_scroll_node_id = containing_block.scroll_node_id;
|
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
|
let mut new_scroll_frame_size = containing_block_info
|
||||||
.for_non_absolute_descendants
|
.for_non_absolute_descendants
|
||||||
.scroll_frame_size;
|
.scroll_frame_size;
|
||||||
|
|
||||||
if let Some(scroll_node_id) = self.build_sticky_frame_if_necessary(
|
if let Some(scroll_node_id) = self.build_sticky_frame_if_necessary(
|
||||||
display_list,
|
stacking_context_tree,
|
||||||
&new_scroll_node_id,
|
&new_scroll_node_id,
|
||||||
&containing_block.rect,
|
&containing_block.rect,
|
||||||
&new_scroll_frame_size,
|
&new_scroll_frame_size,
|
||||||
|
@ -1136,20 +1113,19 @@ impl BoxFragment {
|
||||||
new_scroll_node_id = scroll_node_id;
|
new_scroll_node_id = scroll_node_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(clip_chain_id) = self.build_clip_frame_if_necessary(
|
if let Some(clip_id) = self.build_clip_frame_if_necessary(
|
||||||
display_list,
|
stacking_context_tree,
|
||||||
&new_scroll_node_id,
|
&new_scroll_node_id,
|
||||||
&new_clip_chain_id,
|
new_clip_id,
|
||||||
&containing_block.rect,
|
&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(),
|
self.style.clone_clip_path(),
|
||||||
display_list,
|
|
||||||
&new_scroll_node_id,
|
&new_scroll_node_id,
|
||||||
&new_clip_chain_id,
|
&new_clip_id,
|
||||||
BuilderForBoxFragment::new(
|
BuilderForBoxFragment::new(
|
||||||
self,
|
self,
|
||||||
&containing_block.rect,
|
&containing_block.rect,
|
||||||
|
@ -1157,7 +1133,7 @@ impl BoxFragment {
|
||||||
false, /* is_collapsed_table_borders */
|
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
|
let establishes_containing_block_for_all_descendants = self
|
||||||
|
@ -1182,7 +1158,7 @@ impl BoxFragment {
|
||||||
.push(StackingContextContent::Fragment {
|
.push(StackingContextContent::Fragment {
|
||||||
scroll_node_id: new_scroll_node_id,
|
scroll_node_id: new_scroll_node_id,
|
||||||
reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
|
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,
|
section,
|
||||||
containing_block: containing_block.rect,
|
containing_block: containing_block.rect,
|
||||||
fragment: fragment.clone(),
|
fragment: fragment.clone(),
|
||||||
|
@ -1200,12 +1176,12 @@ impl BoxFragment {
|
||||||
// We want to build the scroll frame after the background and border, because
|
// We want to build the scroll frame after the background and border, because
|
||||||
// they shouldn't scroll with the rest of the box content.
|
// they shouldn't scroll with the rest of the box content.
|
||||||
if let Some(overflow_frame_data) = self.build_overflow_frame_if_necessary(
|
if let Some(overflow_frame_data) = self.build_overflow_frame_if_necessary(
|
||||||
display_list,
|
stacking_context_tree,
|
||||||
&new_scroll_node_id,
|
&new_scroll_node_id,
|
||||||
&new_clip_chain_id,
|
new_clip_id,
|
||||||
&containing_block.rect,
|
&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 {
|
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_node_id = scroll_frame_data.scroll_tree_node_id;
|
||||||
new_scroll_frame_size = Some(scroll_frame_data.scroll_frame_rect.size());
|
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,
|
scroll_node_id: new_scroll_node_id,
|
||||||
reference_frame_scroll_node_id:
|
reference_frame_scroll_node_id:
|
||||||
reference_frame_scroll_node_id_for_fragments,
|
reference_frame_scroll_node_id_for_fragments,
|
||||||
clip_chain_id: new_clip_chain_id,
|
clip_id: new_clip_id,
|
||||||
section,
|
section,
|
||||||
containing_block: containing_block.rect,
|
containing_block: containing_block.rect,
|
||||||
fragment: fragment.clone(),
|
fragment: fragment.clone(),
|
||||||
|
@ -1237,13 +1213,13 @@ impl BoxFragment {
|
||||||
padding_rect,
|
padding_rect,
|
||||||
new_scroll_node_id,
|
new_scroll_node_id,
|
||||||
new_scroll_frame_size,
|
new_scroll_frame_size,
|
||||||
new_clip_chain_id,
|
new_clip_id,
|
||||||
);
|
);
|
||||||
let for_non_absolute_descendants = ContainingBlock::new(
|
let for_non_absolute_descendants = ContainingBlock::new(
|
||||||
content_rect,
|
content_rect,
|
||||||
new_scroll_node_id,
|
new_scroll_node_id,
|
||||||
new_scroll_frame_size,
|
new_scroll_frame_size,
|
||||||
new_clip_chain_id,
|
new_clip_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create a new `ContainingBlockInfo` for descendants depending on
|
// Create a new `ContainingBlockInfo` for descendants depending on
|
||||||
|
@ -1265,7 +1241,7 @@ impl BoxFragment {
|
||||||
|
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
child.build_stacking_context_tree(
|
child.build_stacking_context_tree(
|
||||||
display_list,
|
stacking_context_tree,
|
||||||
&new_containing_block_info,
|
&new_containing_block_info,
|
||||||
stacking_context,
|
stacking_context,
|
||||||
StackingContextBuildMode::SkipHoisted,
|
StackingContextBuildMode::SkipHoisted,
|
||||||
|
@ -1281,7 +1257,7 @@ impl BoxFragment {
|
||||||
.push(StackingContextContent::Fragment {
|
.push(StackingContextContent::Fragment {
|
||||||
scroll_node_id: new_scroll_node_id,
|
scroll_node_id: new_scroll_node_id,
|
||||||
reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
|
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,
|
section,
|
||||||
containing_block: containing_block.rect,
|
containing_block: containing_block.rect,
|
||||||
fragment: fragment.clone(),
|
fragment: fragment.clone(),
|
||||||
|
@ -1293,11 +1269,11 @@ impl BoxFragment {
|
||||||
|
|
||||||
fn build_clip_frame_if_necessary(
|
fn build_clip_frame_if_necessary(
|
||||||
&self,
|
&self,
|
||||||
display_list: &mut DisplayList,
|
stacking_context_tree: &mut StackingContextTree,
|
||||||
parent_scroll_node_id: &ScrollTreeNodeId,
|
parent_scroll_node_id: &ScrollTreeNodeId,
|
||||||
parent_clip_chain_id: &wr::ClipChainId,
|
parent_clip_id: ClipId,
|
||||||
containing_block_rect: &PhysicalRect<Au>,
|
containing_block_rect: &PhysicalRect<Au>,
|
||||||
) -> Option<wr::ClipChainId> {
|
) -> Option<ClipId> {
|
||||||
let position = self.style.get_box().position;
|
let position = self.style.get_box().position;
|
||||||
// https://drafts.csswg.org/css2/#clipping
|
// https://drafts.csswg.org/css2/#clipping
|
||||||
// The clip property applies only to absolutely positioned elements
|
// The clip property applies only to absolutely positioned elements
|
||||||
|
@ -1316,18 +1292,19 @@ impl BoxFragment {
|
||||||
.for_border_rect(border_rect)
|
.for_border_rect(border_rect)
|
||||||
.translate(containing_block_rect.origin.to_vector())
|
.translate(containing_block_rect.origin.to_vector())
|
||||||
.to_webrender();
|
.to_webrender();
|
||||||
|
Some(stacking_context_tree.clip_store.add(
|
||||||
let clip_id = display_list
|
BorderRadius::zero(),
|
||||||
.wr
|
clip_rect,
|
||||||
.define_clip_rect(parent_scroll_node_id.spatial_id, clip_rect);
|
*parent_scroll_node_id,
|
||||||
Some(display_list.define_clip_chain(*parent_clip_chain_id, [clip_id]))
|
parent_clip_id,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_overflow_frame_if_necessary(
|
fn build_overflow_frame_if_necessary(
|
||||||
&self,
|
&self,
|
||||||
display_list: &mut DisplayList,
|
stacking_context_tree: &mut StackingContextTree,
|
||||||
parent_scroll_node_id: &ScrollTreeNodeId,
|
parent_scroll_node_id: &ScrollTreeNodeId,
|
||||||
parent_clip_chain_id: &wr::ClipChainId,
|
parent_clip_id: ClipId,
|
||||||
containing_block_rect: &PhysicalRect<Au>,
|
containing_block_rect: &PhysicalRect<Au>,
|
||||||
) -> Option<OverflowFrameData> {
|
) -> Option<OverflowFrameData> {
|
||||||
let overflow = self.style.effective_overflow(self.base.flags);
|
let overflow = self.style.effective_overflow(self.base.flags);
|
||||||
|
@ -1367,15 +1344,15 @@ impl BoxFragment {
|
||||||
radii = BorderRadius::zero();
|
radii = BorderRadius::zero();
|
||||||
}
|
}
|
||||||
|
|
||||||
let clip_chain_id = display_list.clip_overflow_frame(
|
let clip_id = stacking_context_tree.clip_store.add(
|
||||||
parent_scroll_node_id,
|
|
||||||
parent_clip_chain_id,
|
|
||||||
overflow_clip_rect,
|
|
||||||
radii,
|
radii,
|
||||||
|
overflow_clip_rect,
|
||||||
|
*parent_scroll_node_id,
|
||||||
|
parent_clip_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
return Some(OverflowFrameData {
|
return Some(OverflowFrameData {
|
||||||
clip_chain_id,
|
clip_id,
|
||||||
scroll_frame_data: None,
|
scroll_frame_data: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1404,17 +1381,17 @@ impl BoxFragment {
|
||||||
.translate(containing_block_rect.origin.to_vector())
|
.translate(containing_block_rect.origin.to_vector())
|
||||||
.to_webrender();
|
.to_webrender();
|
||||||
|
|
||||||
let clip_chain_id = display_list.clip_overflow_frame(
|
let clip_id = stacking_context_tree.clip_store.add(
|
||||||
parent_scroll_node_id,
|
|
||||||
parent_clip_chain_id,
|
|
||||||
scroll_frame_rect,
|
|
||||||
BuilderForBoxFragment::new(self, containing_block_rect, false, false).border_radius,
|
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 tag = self.base.tag?;
|
||||||
let external_id = wr::ExternalScrollId(
|
let external_id = wr::ExternalScrollId(
|
||||||
tag.to_display_list_fragment_id(),
|
tag.to_display_list_fragment_id(),
|
||||||
display_list.wr.pipeline_id,
|
stacking_context_tree.compositor_info.pipeline_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
let sensitivity = AxesScrollSensitivity {
|
let sensitivity = AxesScrollSensitivity {
|
||||||
|
@ -1424,7 +1401,7 @@ impl BoxFragment {
|
||||||
|
|
||||||
let content_rect = self.reachable_scrollable_overflow_region().to_webrender();
|
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,
|
parent_scroll_node_id,
|
||||||
external_id,
|
external_id,
|
||||||
content_rect,
|
content_rect,
|
||||||
|
@ -1433,7 +1410,7 @@ impl BoxFragment {
|
||||||
);
|
);
|
||||||
|
|
||||||
Some(OverflowFrameData {
|
Some(OverflowFrameData {
|
||||||
clip_chain_id,
|
clip_id,
|
||||||
scroll_frame_data: Some(ScrollFrameData {
|
scroll_frame_data: Some(ScrollFrameData {
|
||||||
scroll_tree_node_id,
|
scroll_tree_node_id,
|
||||||
scroll_frame_rect,
|
scroll_frame_rect,
|
||||||
|
@ -1443,7 +1420,7 @@ impl BoxFragment {
|
||||||
|
|
||||||
fn build_sticky_frame_if_necessary(
|
fn build_sticky_frame_if_necessary(
|
||||||
&self,
|
&self,
|
||||||
display_list: &mut DisplayList,
|
stacking_context_tree: &mut StackingContextTree,
|
||||||
parent_scroll_node_id: &ScrollTreeNodeId,
|
parent_scroll_node_id: &ScrollTreeNodeId,
|
||||||
containing_block_rect: &PhysicalRect<Au>,
|
containing_block_rect: &PhysicalRect<Au>,
|
||||||
scroll_frame_size: &Option<LayoutSize>,
|
scroll_frame_size: &Option<LayoutSize>,
|
||||||
|
@ -1456,7 +1433,7 @@ impl BoxFragment {
|
||||||
Some(size) => size,
|
Some(size) => size,
|
||||||
None => {
|
None => {
|
||||||
// This is a direct descendant of a reference frame.
|
// 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()),
|
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,
|
parent_scroll_node_id,
|
||||||
frame_rect,
|
frame_rect,
|
||||||
margins,
|
margins,
|
||||||
|
@ -1665,7 +1642,7 @@ impl BoxFragment {
|
||||||
impl PositioningFragment {
|
impl PositioningFragment {
|
||||||
fn build_stacking_context_tree(
|
fn build_stacking_context_tree(
|
||||||
&self,
|
&self,
|
||||||
display_list: &mut DisplayList,
|
stacking_context_tree: &mut StackingContextTree,
|
||||||
containing_block: &ContainingBlock,
|
containing_block: &ContainingBlock,
|
||||||
containing_block_info: &ContainingBlockInfo,
|
containing_block_info: &ContainingBlockInfo,
|
||||||
stacking_context: &mut StackingContext,
|
stacking_context: &mut StackingContext,
|
||||||
|
@ -1679,7 +1656,7 @@ impl PositioningFragment {
|
||||||
|
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
child.build_stacking_context_tree(
|
child.build_stacking_context_tree(
|
||||||
display_list,
|
stacking_context_tree,
|
||||||
&new_containing_block_info,
|
&new_containing_block_info,
|
||||||
stacking_context,
|
stacking_context,
|
||||||
StackingContextBuildMode::SkipHoisted,
|
StackingContextBuildMode::SkipHoisted,
|
||||||
|
|
|
@ -92,7 +92,7 @@ pub(crate) struct BoxFragment {
|
||||||
pub scrollable_overflow_from_children: PhysicalRect<Au>,
|
pub scrollable_overflow_from_children: PhysicalRect<Au>,
|
||||||
|
|
||||||
/// The resolved box insets if this box is `position: sticky`. These are calculated
|
/// 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.
|
/// scroll container.
|
||||||
pub(crate) resolved_sticky_insets: AtomicRefCell<Option<PhysicalSides<AuOrAuto>>>,
|
pub(crate) resolved_sticky_insets: AtomicRefCell<Option<PhysicalSides<AuOrAuto>>>,
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ use webrender_api::units;
|
||||||
use super::{BoxFragment, ContainingBlockManager, Fragment};
|
use super::{BoxFragment, ContainingBlockManager, Fragment};
|
||||||
use crate::ArcRefCell;
|
use crate::ArcRefCell;
|
||||||
use crate::context::LayoutContext;
|
use crate::context::LayoutContext;
|
||||||
use crate::display_list::StackingContext;
|
|
||||||
use crate::geom::PhysicalRect;
|
use crate::geom::PhysicalRect;
|
||||||
|
|
||||||
#[derive(MallocSizeOf)]
|
#[derive(MallocSizeOf)]
|
||||||
|
@ -91,16 +90,6 @@ impl FragmentTree {
|
||||||
fragment_tree
|
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) {
|
pub fn print(&self) {
|
||||||
let mut print_tree = PrintTree::new("Fragment Tree".to_string());
|
let mut print_tree = PrintTree::new("Fragment Tree".to_string());
|
||||||
for fragment in &self.root_fragments {
|
for fragment in &self.root_fragments {
|
||||||
|
|
|
@ -77,7 +77,7 @@ use webrender_api::units::{DevicePixel, DevicePoint, LayoutPixel, LayoutPoint, L
|
||||||
use webrender_api::{ExternalScrollId, HitTestFlags};
|
use webrender_api::{ExternalScrollId, HitTestFlags};
|
||||||
|
|
||||||
use crate::context::LayoutContext;
|
use crate::context::LayoutContext;
|
||||||
use crate::display_list::{DisplayList, WebRenderImageInfo};
|
use crate::display_list::{DisplayListBuilder, StackingContextTree, WebRenderImageInfo};
|
||||||
use crate::query::{
|
use crate::query::{
|
||||||
get_the_text_steps, process_client_rect_request, process_content_box_request,
|
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,
|
process_content_boxes_request, process_node_scroll_area_request, process_offset_parent_query,
|
||||||
|
@ -144,6 +144,9 @@ pub struct LayoutThread {
|
||||||
/// The fragment tree.
|
/// The fragment tree.
|
||||||
fragment_tree: RefCell<Option<Arc<FragmentTree>>>,
|
fragment_tree: RefCell<Option<Arc<FragmentTree>>>,
|
||||||
|
|
||||||
|
/// The [`StackingContextTree`] cached from previous layouts.
|
||||||
|
stacking_context_tree: RefCell<Option<StackingContextTree>>,
|
||||||
|
|
||||||
/// A counter for epoch messages
|
/// A counter for epoch messages
|
||||||
epoch: Cell<Epoch>,
|
epoch: Cell<Epoch>,
|
||||||
|
|
||||||
|
@ -521,6 +524,7 @@ impl LayoutThread {
|
||||||
first_reflow: Cell::new(true),
|
first_reflow: Cell::new(true),
|
||||||
box_tree: Default::default(),
|
box_tree: Default::default(),
|
||||||
fragment_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 starts at 1 because of the initial display list for epoch 0 that we send to WR
|
||||||
epoch: Cell::new(Epoch(1)),
|
epoch: Cell::new(Epoch(1)),
|
||||||
viewport_size: Size2D::new(
|
viewport_size: Size2D::new(
|
||||||
|
@ -649,15 +653,17 @@ impl LayoutThread {
|
||||||
highlighted_dom_node: reflow_request.highlighted_dom_node,
|
highlighted_dom_node: reflow_request.highlighted_dom_node,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.restyle_and_build_trees(
|
let did_reflow = self.restyle_and_build_trees(
|
||||||
&reflow_request,
|
&reflow_request,
|
||||||
root_element,
|
root_element,
|
||||||
rayon_pool,
|
rayon_pool,
|
||||||
&mut layout_context,
|
&mut layout_context,
|
||||||
);
|
);
|
||||||
self.build_display_list(&reflow_request, &mut layout_context);
|
|
||||||
self.first_reflow.set(false);
|
|
||||||
|
|
||||||
|
self.build_stacking_context_tree(&reflow_request, did_reflow);
|
||||||
|
self.build_display_list(&reflow_request, &mut layout_context);
|
||||||
|
|
||||||
|
self.first_reflow.set(false);
|
||||||
if let ReflowGoal::UpdateScrollNode(scroll_state) = reflow_request.reflow_goal {
|
if let ReflowGoal::UpdateScrollNode(scroll_state) = reflow_request.reflow_goal {
|
||||||
self.update_scroll_node_state(&scroll_state);
|
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 iframe_sizes = std::mem::take(&mut *layout_context.iframe_sizes.lock());
|
||||||
let node_to_image_animation_map =
|
let node_to_image_animation_map =
|
||||||
std::mem::take(&mut *layout_context.node_image_animation_map.write());
|
std::mem::take(&mut *layout_context.node_image_animation_map.write());
|
||||||
|
|
||||||
Some(ReflowResult {
|
Some(ReflowResult {
|
||||||
pending_images,
|
pending_images,
|
||||||
iframe_sizes,
|
iframe_sizes,
|
||||||
|
@ -742,7 +749,7 @@ impl LayoutThread {
|
||||||
root_element: ServoLayoutElement<'_>,
|
root_element: ServoLayoutElement<'_>,
|
||||||
rayon_pool: Option<&ThreadPool>,
|
rayon_pool: Option<&ThreadPool>,
|
||||||
layout_context: &mut LayoutContext<'_>,
|
layout_context: &mut LayoutContext<'_>,
|
||||||
) {
|
) -> bool {
|
||||||
let dirty_root = unsafe {
|
let dirty_root = unsafe {
|
||||||
ServoLayoutNode::new(&reflow_request.dirty_root.unwrap())
|
ServoLayoutNode::new(&reflow_request.dirty_root.unwrap())
|
||||||
.as_element()
|
.as_element()
|
||||||
|
@ -758,7 +765,7 @@ impl LayoutThread {
|
||||||
|
|
||||||
if !token.should_traverse() {
|
if !token.should_traverse() {
|
||||||
layout_context.style_context.stylist.rule_tree().maybe_gc();
|
layout_context.style_context.stylist.rule_tree().maybe_gc();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dirty_root: ServoLayoutNode =
|
let dirty_root: ServoLayoutNode =
|
||||||
|
@ -768,7 +775,7 @@ impl LayoutThread {
|
||||||
let damage = compute_damage_and_repair_style(layout_context.shared_context(), root_node);
|
let damage = compute_damage_and_repair_style(layout_context.shared_context(), root_node);
|
||||||
if damage == RestyleDamage::REPAINT {
|
if damage == RestyleDamage::REPAINT {
|
||||||
layout_context.style_context.stylist.rule_tree().maybe_gc();
|
layout_context.style_context.stylist.rule_tree().maybe_gc();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut box_tree = self.box_tree.borrow_mut();
|
let mut box_tree = self.box_tree.borrow_mut();
|
||||||
|
@ -803,8 +810,15 @@ impl LayoutThread {
|
||||||
run_layout()
|
run_layout()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if self.debug.dump_flow_tree {
|
||||||
|
fragment_tree.print();
|
||||||
|
}
|
||||||
*self.fragment_tree.borrow_mut() = Some(fragment_tree);
|
*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 {
|
if self.debug.dump_style_tree {
|
||||||
println!(
|
println!(
|
||||||
"{:?}",
|
"{:?}",
|
||||||
|
@ -822,6 +836,39 @@ impl LayoutThread {
|
||||||
|
|
||||||
// GC the rule tree if some heuristics are met.
|
// GC the rule tree if some heuristics are met.
|
||||||
layout_context.style_context.stylist.rule_tree().maybe_gc();
|
layout_context.style_context.stylist.rule_tree().maybe_gc();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
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 !did_reflow && self.stacking_context_tree.borrow().is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let viewport_size = LayoutSize::from_untyped(Size2D::new(
|
||||||
|
self.viewport_size.width.to_f32_px(),
|
||||||
|
self.viewport_size.height.to_f32_px(),
|
||||||
|
));
|
||||||
|
|
||||||
|
// 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(),
|
||||||
|
fragment_tree.viewport_scroll_sensitivity,
|
||||||
|
self.first_reflow.get(),
|
||||||
|
&self.debug,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_display_list(
|
fn build_display_list(
|
||||||
|
@ -829,68 +876,40 @@ impl LayoutThread {
|
||||||
reflow_request: &ReflowRequest,
|
reflow_request: &ReflowRequest,
|
||||||
layout_context: &mut LayoutContext<'_>,
|
layout_context: &mut LayoutContext<'_>,
|
||||||
) {
|
) {
|
||||||
|
if !reflow_request.reflow_goal.needs_display() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let Some(fragment_tree) = &*self.fragment_tree.borrow() else {
|
let Some(fragment_tree) = &*self.fragment_tree.borrow() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if !reflow_request.reflow_goal.needs_display_list() {
|
|
||||||
|
let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
|
||||||
|
let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
|
||||||
return;
|
return;
|
||||||
}
|
};
|
||||||
|
|
||||||
let mut epoch = self.epoch.get();
|
let mut epoch = self.epoch.get();
|
||||||
epoch.next();
|
epoch.next();
|
||||||
self.epoch.set(epoch);
|
self.epoch.set(epoch);
|
||||||
|
stacking_context_tree.compositor_info.epoch = epoch.into();
|
||||||
|
|
||||||
let viewport_size = LayoutSize::from_untyped(Size2D::new(
|
let built_display_list = DisplayListBuilder::build(
|
||||||
self.viewport_size.width.to_f32_px(),
|
layout_context,
|
||||||
self.viewport_size.height.to_f32_px(),
|
stacking_context_tree,
|
||||||
));
|
fragment_tree,
|
||||||
let mut display_list = DisplayList::new(
|
&self.debug,
|
||||||
viewport_size,
|
);
|
||||||
fragment_tree.scrollable_overflow(),
|
self.compositor_api.send_display_list(
|
||||||
self.id.into(),
|
self.webview_id,
|
||||||
epoch.into(),
|
&stacking_context_tree.compositor_info,
|
||||||
fragment_tree.viewport_scroll_sensitivity,
|
built_display_list,
|
||||||
self.first_reflow.get(),
|
|
||||||
);
|
);
|
||||||
display_list.wr.begin();
|
|
||||||
|
|
||||||
// `dump_serialized_display_list` doesn't actually print anything. It sets up
|
let (keys, instance_keys) = self
|
||||||
// the display list for printing the serialized version when `finalize()` is called.
|
.font_context
|
||||||
// We need to call this before adding any display items so that they are printed
|
.collect_unused_webrender_resources(false /* all */);
|
||||||
// during `finalize()`.
|
self.compositor_api
|
||||||
if self.debug.dump_display_list {
|
.remove_unused_font_resources(keys, instance_keys)
|
||||||
display_list.wr.dump_serialized_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, &self.debug);
|
|
||||||
|
|
||||||
// Build the rest of the display list which inclues all of the WebRender primitives.
|
|
||||||
display_list.build(layout_context, fragment_tree, &root_stacking_context);
|
|
||||||
|
|
||||||
if self.debug.dump_flow_tree {
|
|
||||||
fragment_tree.print();
|
|
||||||
}
|
|
||||||
if self.debug.dump_stacking_context_tree {
|
|
||||||
root_stacking_context.debug_print();
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (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) {
|
fn update_scroll_node_state(&self, state: &ScrollState) {
|
||||||
|
|
|
@ -17,7 +17,7 @@ use malloc_size_of::MallocSizeOfOps;
|
||||||
use malloc_size_of_derive::MallocSizeOf;
|
use malloc_size_of_derive::MallocSizeOf;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use webrender_api::{ExternalScrollId, PipelineId as WebRenderPipelineId, SpatialId};
|
use webrender_api::{ExternalScrollId, PipelineId as WebRenderPipelineId};
|
||||||
|
|
||||||
/// Asserts the size of a type at compile time.
|
/// Asserts the size of a type at compile time.
|
||||||
macro_rules! size_of_test {
|
macro_rules! size_of_test {
|
||||||
|
@ -397,7 +397,4 @@ pub const TEST_WEBVIEW_ID: WebViewId = WebViewId(TEST_BROWSING_CONTEXT_ID);
|
||||||
pub struct ScrollTreeNodeId {
|
pub struct ScrollTreeNodeId {
|
||||||
/// The index of this scroll tree node in the tree's array of nodes.
|
/// The index of this scroll tree node in the tree's array of nodes.
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
|
|
||||||
/// The WebRender spatial id of this scroll tree node.
|
|
||||||
pub spatial_id: SpatialId,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,17 @@
|
||||||
|
|
||||||
use base::id::ScrollTreeNodeId;
|
use base::id::ScrollTreeNodeId;
|
||||||
use embedder_traits::Cursor;
|
use embedder_traits::Cursor;
|
||||||
|
use euclid::SideOffsets2D;
|
||||||
use malloc_size_of_derive::MallocSizeOf;
|
use malloc_size_of_derive::MallocSizeOf;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use style::values::specified::Overflow;
|
use style::values::specified::Overflow;
|
||||||
use webrender_api::units::{LayoutSize, LayoutVector2D};
|
use webrender_api::units::{
|
||||||
use webrender_api::{Epoch, ExternalScrollId, PipelineId, ScrollLocation, SpatialId};
|
LayoutPixel, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D,
|
||||||
|
};
|
||||||
|
use webrender_api::{
|
||||||
|
Epoch, ExternalScrollId, PipelineId, ReferenceFrameKind, ScrollLocation, SpatialId,
|
||||||
|
StickyOffsetBounds, TransformStyle,
|
||||||
|
};
|
||||||
|
|
||||||
/// The scroll sensitivity of a scroll node in a particular axis ie whether it can be scrolled due to
|
/// The scroll sensitivity of a scroll node in a particular axis ie whether it can be scrolled due to
|
||||||
/// input events and script events or only script events.
|
/// input events and script events or only script events.
|
||||||
|
@ -56,6 +62,29 @@ pub struct HitTestInfo {
|
||||||
pub scroll_tree_node: ScrollTreeNodeId,
|
pub scroll_tree_node: ScrollTreeNodeId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub enum SpatialTreeNodeInfo {
|
||||||
|
ReferenceFrame(ReferenceFrameNodeInfo),
|
||||||
|
Scroll(ScrollableNodeInfo),
|
||||||
|
Sticky(StickyNodeInfo),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct StickyNodeInfo {
|
||||||
|
pub frame_rect: LayoutRect,
|
||||||
|
pub margins: SideOffsets2D<Option<f32>, LayoutPixel>,
|
||||||
|
pub vertical_offset_bounds: StickyOffsetBounds,
|
||||||
|
pub horizontal_offset_bounds: StickyOffsetBounds,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct ReferenceFrameNodeInfo {
|
||||||
|
pub origin: LayoutPoint,
|
||||||
|
pub transform_style: TransformStyle,
|
||||||
|
pub transform: LayoutTransform,
|
||||||
|
pub kind: ReferenceFrameKind,
|
||||||
|
}
|
||||||
|
|
||||||
/// Data stored for nodes in the [ScrollTree] that actually scroll,
|
/// Data stored for nodes in the [ScrollTree] that actually scroll,
|
||||||
/// as opposed to reference frames and sticky nodes which do not.
|
/// as opposed to reference frames and sticky nodes which do not.
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
@ -64,8 +93,11 @@ pub struct ScrollableNodeInfo {
|
||||||
/// it between successive re-layouts.
|
/// it between successive re-layouts.
|
||||||
pub external_id: ExternalScrollId,
|
pub external_id: ExternalScrollId,
|
||||||
|
|
||||||
/// Amount that this `ScrollableNode` can scroll in both directions.
|
/// The content rectangle for this scroll node;
|
||||||
pub scrollable_size: LayoutSize,
|
pub content_rect: LayoutRect,
|
||||||
|
|
||||||
|
/// The clip rectange for this scroll node.
|
||||||
|
pub clip_rect: LayoutRect,
|
||||||
|
|
||||||
/// Whether this `ScrollableNode` is sensitive to input events.
|
/// Whether this `ScrollableNode` is sensitive to input events.
|
||||||
pub scroll_sensitivity: AxesScrollSensitivity,
|
pub scroll_sensitivity: AxesScrollSensitivity,
|
||||||
|
@ -74,6 +106,12 @@ pub struct ScrollableNodeInfo {
|
||||||
pub offset: LayoutVector2D,
|
pub offset: LayoutVector2D,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ScrollableNodeInfo {
|
||||||
|
fn scrollable_size(&self) -> LayoutSize {
|
||||||
|
self.content_rect.size() - self.clip_rect.size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
/// A node in a tree of scroll nodes. This may either be a scrollable
|
/// A node in a tree of scroll nodes. This may either be a scrollable
|
||||||
/// node which responds to scroll events or a non-scrollable one.
|
/// node which responds to scroll events or a non-scrollable one.
|
||||||
|
@ -82,35 +120,51 @@ pub struct ScrollTreeNode {
|
||||||
/// None then this is the root node.
|
/// None then this is the root node.
|
||||||
pub parent: Option<ScrollTreeNodeId>,
|
pub parent: Option<ScrollTreeNodeId>,
|
||||||
|
|
||||||
/// Scrolling data which will not be None if this is a scrolling node.
|
/// The WebRender id, which is filled in when this tree is serialiezd
|
||||||
pub scroll_info: Option<ScrollableNodeInfo>,
|
/// into a WebRender display list.
|
||||||
|
pub webrender_id: Option<SpatialId>,
|
||||||
|
|
||||||
|
/// Specific information about this node, depending on whether it is a scroll node
|
||||||
|
/// or a reference frame.
|
||||||
|
pub info: SpatialTreeNodeInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScrollTreeNode {
|
impl ScrollTreeNode {
|
||||||
|
/// Get the WebRender [`SpatialId`] for the given [`ScrollNodeId`]. This will
|
||||||
|
/// panic if [`ScrollTree::build_display_list`] has not been called yet.
|
||||||
|
pub fn webrender_id(&self) -> SpatialId {
|
||||||
|
self.webrender_id
|
||||||
|
.expect("Should have called ScrollTree::build_display_list before querying SpatialId")
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the external id of this node.
|
/// Get the external id of this node.
|
||||||
pub fn external_id(&self) -> Option<ExternalScrollId> {
|
pub fn external_id(&self) -> Option<ExternalScrollId> {
|
||||||
self.scroll_info.as_ref().map(|info| info.external_id)
|
match self.info {
|
||||||
|
SpatialTreeNodeInfo::Scroll(ref info) => Some(info.external_id),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the offset id of this node if it applies.
|
/// Get the offset id of this node if it applies.
|
||||||
pub fn offset(&self) -> Option<LayoutVector2D> {
|
pub fn offset(&self) -> Option<LayoutVector2D> {
|
||||||
self.scroll_info.as_ref().map(|info| info.offset)
|
match self.info {
|
||||||
|
SpatialTreeNodeInfo::Scroll(ref info) => Some(info.offset),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the offset for this node, returns false if this was a
|
/// Set the offset for this node, returns false if this was a
|
||||||
/// non-scrolling node for which you cannot set the offset.
|
/// non-scrolling node for which you cannot set the offset.
|
||||||
pub fn set_offset(&mut self, new_offset: LayoutVector2D) -> bool {
|
pub fn set_offset(&mut self, new_offset: LayoutVector2D) -> bool {
|
||||||
match self.scroll_info {
|
match self.info {
|
||||||
Some(ref mut info) => {
|
SpatialTreeNodeInfo::Scroll(ref mut info) => {
|
||||||
let scrollable_width = info.scrollable_size.width;
|
let scrollable_size = info.scrollable_size();
|
||||||
let scrollable_height = info.scrollable_size.height;
|
if scrollable_size.width > 0. {
|
||||||
|
info.offset.x = (new_offset.x).min(0.0).max(-scrollable_size.width);
|
||||||
if scrollable_width > 0. {
|
|
||||||
info.offset.x = (new_offset.x).min(0.0).max(-scrollable_width);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if scrollable_height > 0. {
|
if scrollable_size.height > 0. {
|
||||||
info.offset.y = (new_offset.y).min(0.0).max(-scrollable_height);
|
info.offset.y = (new_offset.y).min(0.0).max(-scrollable_size.height);
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
|
@ -125,9 +179,9 @@ impl ScrollTreeNode {
|
||||||
&mut self,
|
&mut self,
|
||||||
scroll_location: ScrollLocation,
|
scroll_location: ScrollLocation,
|
||||||
) -> Option<(ExternalScrollId, LayoutVector2D)> {
|
) -> Option<(ExternalScrollId, LayoutVector2D)> {
|
||||||
let info = match self.scroll_info {
|
let info = match self.info {
|
||||||
Some(ref mut data) => data,
|
SpatialTreeNodeInfo::Scroll(ref mut info) => info,
|
||||||
None => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if info.scroll_sensitivity.x != ScrollSensitivity::ScriptAndInputEvents &&
|
if info.scroll_sensitivity.x != ScrollSensitivity::ScriptAndInputEvents &&
|
||||||
|
@ -148,7 +202,7 @@ impl ScrollTreeNode {
|
||||||
return Some((info.external_id, info.offset));
|
return Some((info.external_id, info.offset));
|
||||||
},
|
},
|
||||||
ScrollLocation::End => {
|
ScrollLocation::End => {
|
||||||
let end_pos = -info.scrollable_size.height;
|
let end_pos = -info.scrollable_size().height;
|
||||||
if info.offset.y.round() <= end_pos {
|
if info.offset.y.round() <= end_pos {
|
||||||
// Nothing to do on this layer.
|
// Nothing to do on this layer.
|
||||||
return None;
|
return None;
|
||||||
|
@ -159,20 +213,23 @@ impl ScrollTreeNode {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let scrollable_width = info.scrollable_size.width;
|
let scrollable_size = info.scrollable_size();
|
||||||
let scrollable_height = info.scrollable_size.height;
|
|
||||||
let original_layer_scroll_offset = info.offset;
|
let original_layer_scroll_offset = info.offset;
|
||||||
|
|
||||||
if scrollable_width > 0. &&
|
if scrollable_size.width > 0. &&
|
||||||
info.scroll_sensitivity.x == ScrollSensitivity::ScriptAndInputEvents
|
info.scroll_sensitivity.x == ScrollSensitivity::ScriptAndInputEvents
|
||||||
{
|
{
|
||||||
info.offset.x = (info.offset.x + delta.x).min(0.0).max(-scrollable_width);
|
info.offset.x = (info.offset.x + delta.x)
|
||||||
|
.min(0.0)
|
||||||
|
.max(-scrollable_size.width);
|
||||||
}
|
}
|
||||||
|
|
||||||
if scrollable_height > 0. &&
|
if scrollable_size.height > 0. &&
|
||||||
info.scroll_sensitivity.y == ScrollSensitivity::ScriptAndInputEvents
|
info.scroll_sensitivity.y == ScrollSensitivity::ScriptAndInputEvents
|
||||||
{
|
{
|
||||||
info.offset.y = (info.offset.y + delta.y).min(0.0).max(-scrollable_height);
|
info.offset.y = (info.offset.y + delta.y)
|
||||||
|
.min(0.0)
|
||||||
|
.max(-scrollable_size.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
if info.offset != original_layer_scroll_offset {
|
if info.offset != original_layer_scroll_offset {
|
||||||
|
@ -199,16 +256,23 @@ impl ScrollTree {
|
||||||
pub fn add_scroll_tree_node(
|
pub fn add_scroll_tree_node(
|
||||||
&mut self,
|
&mut self,
|
||||||
parent: Option<&ScrollTreeNodeId>,
|
parent: Option<&ScrollTreeNodeId>,
|
||||||
spatial_id: SpatialId,
|
info: SpatialTreeNodeInfo,
|
||||||
scroll_info: Option<ScrollableNodeInfo>,
|
|
||||||
) -> ScrollTreeNodeId {
|
) -> ScrollTreeNodeId {
|
||||||
self.nodes.push(ScrollTreeNode {
|
self.nodes.push(ScrollTreeNode {
|
||||||
parent: parent.cloned(),
|
parent: parent.cloned(),
|
||||||
scroll_info,
|
webrender_id: None,
|
||||||
|
info,
|
||||||
});
|
});
|
||||||
ScrollTreeNodeId {
|
ScrollTreeNodeId {
|
||||||
index: self.nodes.len() - 1,
|
index: self.nodes.len() - 1,
|
||||||
spatial_id,
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Once WebRender display list construction is complete for this [`ScrollTree`], update
|
||||||
|
/// the mapping of nodes to WebRender [`SpatialId`]s.
|
||||||
|
pub fn update_mapping(&mut self, mapping: Vec<SpatialId>) {
|
||||||
|
for (spatial_id, node) in mapping.into_iter().zip(self.nodes.iter_mut()) {
|
||||||
|
node.webrender_id = Some(spatial_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,10 +282,16 @@ impl ScrollTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an immutable reference to the node with the given index.
|
/// Get an immutable reference to the node with the given index.
|
||||||
pub fn get_node(&mut self, id: &ScrollTreeNodeId) -> &ScrollTreeNode {
|
pub fn get_node(&self, id: &ScrollTreeNodeId) -> &ScrollTreeNode {
|
||||||
&self.nodes[id.index]
|
&self.nodes[id.index]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the WebRender [`SpatialId`] for the given [`ScrollNodeId`]. This will
|
||||||
|
/// panic if [`ScrollTree::build_display_list`] has not been called yet.
|
||||||
|
pub fn webrender_id(&self, id: &ScrollTreeNodeId) -> SpatialId {
|
||||||
|
self.get_node(id).webrender_id()
|
||||||
|
}
|
||||||
|
|
||||||
/// Scroll the given scroll node on this scroll tree. If the node cannot be scrolled,
|
/// Scroll the given scroll node on this scroll tree. If the node cannot be scrolled,
|
||||||
/// because it isn't a scrollable node or it's already scrolled to the maximum scroll
|
/// because it isn't a scrollable node or it's already scrolled to the maximum scroll
|
||||||
/// extent, try to scroll an ancestor of this node. Returns the node scrolled and the
|
/// extent, try to scroll an ancestor of this node. Returns the node scrolled and the
|
||||||
|
@ -251,8 +321,10 @@ impl ScrollTree {
|
||||||
offset: LayoutVector2D,
|
offset: LayoutVector2D,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
for node in self.nodes.iter_mut() {
|
for node in self.nodes.iter_mut() {
|
||||||
match node.scroll_info {
|
match node.info {
|
||||||
Some(ref mut scroll_info) if scroll_info.external_id == external_scroll_id => {
|
SpatialTreeNodeInfo::Scroll(ref mut scroll_info)
|
||||||
|
if scroll_info.external_id == external_scroll_id =>
|
||||||
|
{
|
||||||
scroll_info.offset = offset;
|
scroll_info.offset = offset;
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
@ -320,15 +392,19 @@ impl CompositorDisplayListInfo {
|
||||||
let mut scroll_tree = ScrollTree::default();
|
let mut scroll_tree = ScrollTree::default();
|
||||||
let root_reference_frame_id = scroll_tree.add_scroll_tree_node(
|
let root_reference_frame_id = scroll_tree.add_scroll_tree_node(
|
||||||
None,
|
None,
|
||||||
SpatialId::root_reference_frame(pipeline_id),
|
SpatialTreeNodeInfo::ReferenceFrame(ReferenceFrameNodeInfo {
|
||||||
None,
|
origin: Default::default(),
|
||||||
|
transform_style: TransformStyle::Flat,
|
||||||
|
transform: LayoutTransform::identity(),
|
||||||
|
kind: ReferenceFrameKind::default(),
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
let root_scroll_node_id = scroll_tree.add_scroll_tree_node(
|
let root_scroll_node_id = scroll_tree.add_scroll_tree_node(
|
||||||
Some(&root_reference_frame_id),
|
Some(&root_reference_frame_id),
|
||||||
SpatialId::root_scroll_node(pipeline_id),
|
SpatialTreeNodeInfo::Scroll(ScrollableNodeInfo {
|
||||||
Some(ScrollableNodeInfo {
|
|
||||||
external_id: ExternalScrollId(0, pipeline_id),
|
external_id: ExternalScrollId(0, pipeline_id),
|
||||||
scrollable_size: content_size - viewport_size,
|
content_rect: LayoutRect::from_origin_and_size(LayoutPoint::zero(), content_size),
|
||||||
|
clip_rect: LayoutRect::from_origin_and_size(LayoutPoint::zero(), viewport_size),
|
||||||
scroll_sensitivity: viewport_scroll_sensitivity,
|
scroll_sensitivity: viewport_scroll_sensitivity,
|
||||||
offset: LayoutVector2D::zero(),
|
offset: LayoutVector2D::zero(),
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -236,7 +236,7 @@ impl CrossProcessCompositorApi {
|
||||||
pub fn send_display_list(
|
pub fn send_display_list(
|
||||||
&self,
|
&self,
|
||||||
webview_id: WebViewId,
|
webview_id: WebViewId,
|
||||||
display_list_info: CompositorDisplayListInfo,
|
display_list_info: &CompositorDisplayListInfo,
|
||||||
list: BuiltDisplayList,
|
list: BuiltDisplayList,
|
||||||
) {
|
) {
|
||||||
let (display_list_data, display_list_descriptor) = list.into_data();
|
let (display_list_data, display_list_descriptor) = list.into_data();
|
||||||
|
|
|
@ -4,11 +4,12 @@
|
||||||
|
|
||||||
use base::id::ScrollTreeNodeId;
|
use base::id::ScrollTreeNodeId;
|
||||||
use compositing_traits::display_list::{
|
use compositing_traits::display_list::{
|
||||||
AxesScrollSensitivity, ScrollSensitivity, ScrollTree, ScrollableNodeInfo,
|
AxesScrollSensitivity, ScrollSensitivity, ScrollTree, ScrollableNodeInfo, SpatialTreeNodeInfo,
|
||||||
|
StickyNodeInfo,
|
||||||
};
|
};
|
||||||
use euclid::Size2D;
|
use euclid::{SideOffsets2D, Size2D};
|
||||||
use webrender_api::units::LayoutVector2D;
|
use webrender_api::units::LayoutVector2D;
|
||||||
use webrender_api::{ExternalScrollId, PipelineId, ScrollLocation, SpatialId};
|
use webrender_api::{ExternalScrollId, PipelineId, ScrollLocation, StickyOffsetBounds};
|
||||||
|
|
||||||
fn add_mock_scroll_node(tree: &mut ScrollTree) -> ScrollTreeNodeId {
|
fn add_mock_scroll_node(tree: &mut ScrollTree) -> ScrollTreeNodeId {
|
||||||
let pipeline_id = PipelineId(0, 0);
|
let pipeline_id = PipelineId(0, 0);
|
||||||
|
@ -16,7 +17,6 @@ fn add_mock_scroll_node(tree: &mut ScrollTree) -> ScrollTreeNodeId {
|
||||||
let parent = if num_nodes > 0 {
|
let parent = if num_nodes > 0 {
|
||||||
Some(ScrollTreeNodeId {
|
Some(ScrollTreeNodeId {
|
||||||
index: num_nodes - 1,
|
index: num_nodes - 1,
|
||||||
spatial_id: SpatialId::new(num_nodes - 1, pipeline_id),
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -24,10 +24,10 @@ fn add_mock_scroll_node(tree: &mut ScrollTree) -> ScrollTreeNodeId {
|
||||||
|
|
||||||
tree.add_scroll_tree_node(
|
tree.add_scroll_tree_node(
|
||||||
parent.as_ref(),
|
parent.as_ref(),
|
||||||
SpatialId::new(num_nodes, pipeline_id),
|
SpatialTreeNodeInfo::Scroll(ScrollableNodeInfo {
|
||||||
Some(ScrollableNodeInfo {
|
|
||||||
external_id: ExternalScrollId(num_nodes as u64, pipeline_id),
|
external_id: ExternalScrollId(num_nodes as u64, pipeline_id),
|
||||||
scrollable_size: Size2D::new(100.0, 100.0),
|
content_rect: Size2D::new(200.0, 200.0).into(),
|
||||||
|
clip_rect: Size2D::new(100.0, 100.0).into(),
|
||||||
scroll_sensitivity: AxesScrollSensitivity {
|
scroll_sensitivity: AxesScrollSensitivity {
|
||||||
x: ScrollSensitivity::ScriptAndInputEvents,
|
x: ScrollSensitivity::ScriptAndInputEvents,
|
||||||
y: ScrollSensitivity::ScriptAndInputEvents,
|
y: ScrollSensitivity::ScriptAndInputEvents,
|
||||||
|
@ -78,8 +78,15 @@ fn test_scroll_tree_simple_scroll_chaining() {
|
||||||
|
|
||||||
let pipeline_id = PipelineId(0, 0);
|
let pipeline_id = PipelineId(0, 0);
|
||||||
let parent_id = add_mock_scroll_node(&mut scroll_tree);
|
let parent_id = add_mock_scroll_node(&mut scroll_tree);
|
||||||
let unscrollable_child_id =
|
let unscrollable_child_id = scroll_tree.add_scroll_tree_node(
|
||||||
scroll_tree.add_scroll_tree_node(Some(&parent_id), SpatialId::new(1, pipeline_id), None);
|
Some(&parent_id),
|
||||||
|
SpatialTreeNodeInfo::Sticky(StickyNodeInfo {
|
||||||
|
frame_rect: Size2D::new(100.0, 100.0).into(),
|
||||||
|
margins: SideOffsets2D::default(),
|
||||||
|
vertical_offset_bounds: StickyOffsetBounds::new(0.0, 0.0),
|
||||||
|
horizontal_offset_bounds: StickyOffsetBounds::new(0.0, 0.0),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
let (scrolled_id, offset) = scroll_tree
|
let (scrolled_id, offset) = scroll_tree
|
||||||
.scroll_node_or_ancestor(
|
.scroll_node_or_ancestor(
|
||||||
|
@ -157,16 +164,14 @@ fn test_scroll_tree_chain_through_overflow_hidden() {
|
||||||
let pipeline_id = PipelineId(0, 0);
|
let pipeline_id = PipelineId(0, 0);
|
||||||
let parent_id = add_mock_scroll_node(&mut scroll_tree);
|
let parent_id = add_mock_scroll_node(&mut scroll_tree);
|
||||||
let overflow_hidden_id = add_mock_scroll_node(&mut scroll_tree);
|
let overflow_hidden_id = add_mock_scroll_node(&mut scroll_tree);
|
||||||
scroll_tree
|
let node = scroll_tree.get_node_mut(&overflow_hidden_id);
|
||||||
.get_node_mut(&overflow_hidden_id)
|
|
||||||
.scroll_info
|
if let SpatialTreeNodeInfo::Scroll(ref mut scroll_node_info) = node.info {
|
||||||
.as_mut()
|
scroll_node_info.scroll_sensitivity = AxesScrollSensitivity {
|
||||||
.map(|info| {
|
x: ScrollSensitivity::Script,
|
||||||
info.scroll_sensitivity = AxesScrollSensitivity {
|
y: ScrollSensitivity::Script,
|
||||||
x: ScrollSensitivity::Script,
|
};
|
||||||
y: ScrollSensitivity::Script,
|
}
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
let (scrolled_id, offset) = scroll_tree
|
let (scrolled_id, offset) = scroll_tree
|
||||||
.scroll_node_or_ancestor(
|
.scroll_node_or_ancestor(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue