/* 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, /// The scrollable overflow rectangle for the entire tree /// pub(crate) scrollable_overflow: PhysicalRect, /// The containing block used in the layout of this fragment tree. pub(crate) initial_containing_block: PhysicalRect, /// 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, scrollable_overflow: PhysicalRect, initial_containing_block: PhysicalRect, 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( &self, mut process_func: impl FnMut(&Fragment, usize, &PhysicalRect) -> Option, ) -> Option { 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 { 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 } }