mirror of
https://github.com/servo/servo.git
synced 2025-07-25 16:20:36 +01:00
This is a followup to #36629, continuing to implement script-based layout queries using the `Fragment`s attached to the `BoxTree`. In this change, geometry queris (apart from parent offset) are calculated using `Fragment`s hanging of the `BoxTree`. In order to make this work, all `Fragment`s for inlines split by blocks, need to be accessible in the `BoxTree`. This required some changes to the way that box tree items were stored in DOM `BoxSlot`s. Now every inline level item can have more than a single `BoxTree` item. These are carefully collected by the `InlineFormattingContextBuilder` -- currently a bit fragile, but with more documentation. Testing: There are tests for these changes. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Oriol Brufau <obrufau@igalia.com>
148 lines
5.5 KiB
Rust
148 lines
5.5 KiB
Rust
/* 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::print_tree::PrintTree;
|
||
use compositing_traits::display_list::AxesScrollSensitivity;
|
||
use euclid::default::Size2D;
|
||
use fxhash::FxHashSet;
|
||
use malloc_size_of_derive::MallocSizeOf;
|
||
use style::animation::AnimationSetKey;
|
||
use webrender_api::units;
|
||
|
||
use super::{ContainingBlockManager, Fragment};
|
||
use crate::context::LayoutContext;
|
||
use crate::display_list::StackingContext;
|
||
use crate::flow::CanvasBackground;
|
||
use crate::geom::PhysicalRect;
|
||
|
||
#[derive(MallocSizeOf)]
|
||
pub struct FragmentTree {
|
||
/// Fragments at the top-level of the tree.
|
||
///
|
||
/// If the root element has `display: none`, there are zero fragments.
|
||
/// Otherwise, there is at least one:
|
||
///
|
||
/// * The first fragment is generated by the root element.
|
||
/// * There may be additional fragments generated by positioned boxes
|
||
/// that have the initial containing block.
|
||
pub(crate) root_fragments: Vec<Fragment>,
|
||
|
||
/// The scrollable overflow rectangle for the entire tree
|
||
/// <https://drafts.csswg.org/css-overflow/#scrollable>
|
||
pub(crate) scrollable_overflow: PhysicalRect<Au>,
|
||
|
||
/// The containing block used in the layout of this fragment tree.
|
||
pub(crate) initial_containing_block: PhysicalRect<Au>,
|
||
|
||
/// <https://drafts.csswg.org/css-backgrounds/#special-backgrounds>
|
||
pub(crate) canvas_background: CanvasBackground,
|
||
|
||
/// Whether or not the viewport is sensitive to scroll input events.
|
||
pub viewport_scroll_sensitivity: AxesScrollSensitivity,
|
||
}
|
||
|
||
impl FragmentTree {
|
||
pub(crate) fn new(
|
||
layout_context: &LayoutContext,
|
||
root_fragments: Vec<Fragment>,
|
||
scrollable_overflow: PhysicalRect<Au>,
|
||
initial_containing_block: PhysicalRect<Au>,
|
||
canvas_background: CanvasBackground,
|
||
viewport_scroll_sensitivity: AxesScrollSensitivity,
|
||
) -> Self {
|
||
let fragment_tree = Self {
|
||
root_fragments,
|
||
scrollable_overflow,
|
||
initial_containing_block,
|
||
canvas_background,
|
||
viewport_scroll_sensitivity,
|
||
};
|
||
|
||
// As part of building the fragment tree, we want to stop animating elements and
|
||
// pseudo-elements that used to be animating or had animating images attached to
|
||
// them. Create a set of all elements that used to be animating.
|
||
let mut animations = layout_context.style_context.animations.sets.write();
|
||
let mut invalid_animating_nodes: FxHashSet<_> = animations.keys().cloned().collect();
|
||
let mut image_animations = layout_context.node_image_animation_map.write().to_owned();
|
||
let mut invalid_image_animating_nodes: FxHashSet<_> = image_animations
|
||
.keys()
|
||
.cloned()
|
||
.map(|node| AnimationSetKey::new(node, None))
|
||
.collect();
|
||
|
||
fragment_tree.find(|fragment, _level, containing_block| {
|
||
if let Some(tag) = fragment.tag() {
|
||
invalid_animating_nodes.remove(&AnimationSetKey::new(tag.node, tag.pseudo));
|
||
invalid_image_animating_nodes.remove(&AnimationSetKey::new(tag.node, tag.pseudo));
|
||
}
|
||
|
||
fragment.set_containing_block(containing_block);
|
||
None::<()>
|
||
});
|
||
|
||
// Cancel animations for any elements and pseudo-elements that are no longer found
|
||
// in the fragment tree.
|
||
for node in &invalid_animating_nodes {
|
||
if let Some(state) = animations.get_mut(node) {
|
||
state.cancel_all_animations();
|
||
}
|
||
}
|
||
for node in &invalid_image_animating_nodes {
|
||
image_animations.remove(&node.node);
|
||
}
|
||
|
||
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,
|
||
&self.initial_containing_block,
|
||
);
|
||
root_stacking_context.build_display_list(builder);
|
||
}
|
||
|
||
pub fn print(&self) {
|
||
let mut print_tree = PrintTree::new("Fragment Tree".to_string());
|
||
for fragment in &self.root_fragments {
|
||
fragment.print(&mut print_tree);
|
||
}
|
||
}
|
||
|
||
pub fn scrollable_overflow(&self) -> units::LayoutSize {
|
||
units::LayoutSize::from_untyped(Size2D::new(
|
||
self.scrollable_overflow.size.width.to_f32_px(),
|
||
self.scrollable_overflow.size.height.to_f32_px(),
|
||
))
|
||
}
|
||
|
||
pub(crate) fn find<T>(
|
||
&self,
|
||
mut process_func: impl FnMut(&Fragment, usize, &PhysicalRect<Au>) -> Option<T>,
|
||
) -> Option<T> {
|
||
let info = ContainingBlockManager {
|
||
for_non_absolute_descendants: &self.initial_containing_block,
|
||
for_absolute_descendants: None,
|
||
for_absolute_and_fixed_descendants: &self.initial_containing_block,
|
||
};
|
||
self.root_fragments
|
||
.iter()
|
||
.find_map(|child| child.find(&info, 0, &mut process_func))
|
||
}
|
||
|
||
pub fn get_scrolling_area_for_viewport(&self) -> PhysicalRect<Au> {
|
||
let mut scroll_area = self.initial_containing_block;
|
||
for fragment in self.root_fragments.iter() {
|
||
scroll_area = fragment.scrolling_area().union(&scroll_area);
|
||
}
|
||
scroll_area
|
||
}
|
||
}
|