Skip to main content

layout/fragment_tree/
positioning_fragment.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::sync::Arc;
6use std::sync::atomic::{AtomicBool, Ordering};
7
8use app_units::Au;
9use malloc_size_of_derive::MallocSizeOf;
10use servo_arc::Arc as ServoArc;
11use servo_base::print_tree::PrintTree;
12use style::properties::ComputedValues;
13
14use super::{BaseFragment, BaseFragmentInfo, Fragment};
15use crate::fragment_tree::ContainingBlockCalculation;
16use crate::geom::{PhysicalRect, SyncPhysicalRectAu};
17
18/// Can contain child fragments with relative coordinates, but does not contribute to painting
19/// itself. [`PositioningFragment`]s may be completely anonymous, or just non-painting Fragments
20/// generated by boxes.
21#[derive(MallocSizeOf)]
22pub(crate) struct PositioningFragment {
23    pub base: BaseFragment,
24    pub children: Vec<Fragment>,
25
26    /// The scrollable overflow of this anonymous fragment's children.
27    scrollable_overflow: SyncPhysicalRectAu,
28    scrollable_overflow_is_up_to_date: AtomicBool,
29
30    /// This [`PositioningFragment`]'s containing block rectangle in coordinates relative to
31    /// the initial containing block, but not taking into account any transforms.
32    pub cumulative_containing_block_rect: SyncPhysicalRectAu,
33
34    /// Whether or not this [`PositioningFragment`] is a line box.
35    is_line_box: bool,
36}
37
38impl PositioningFragment {
39    pub fn new_anonymous(
40        style: ServoArc<ComputedValues>,
41        rect: PhysicalRect<Au>,
42        children: Vec<Fragment>,
43        is_line_box: bool,
44    ) -> Arc<Self> {
45        Self::new_with_base_fragment_info(
46            BaseFragmentInfo::anonymous(),
47            style,
48            rect,
49            children,
50            is_line_box,
51        )
52    }
53
54    pub fn new_empty(
55        base_fragment_info: BaseFragmentInfo,
56        rect: PhysicalRect<Au>,
57        style: ServoArc<ComputedValues>,
58    ) -> Arc<Self> {
59        Self::new_with_base_fragment_info(base_fragment_info, style, rect, Vec::new(), false)
60    }
61
62    fn new_with_base_fragment_info(
63        base_fragment_info: BaseFragmentInfo,
64        style: ServoArc<ComputedValues>,
65        rect: PhysicalRect<Au>,
66        children: Vec<Fragment>,
67        is_line_box: bool,
68    ) -> Arc<Self> {
69        Arc::new(Self {
70            base: BaseFragment::new(base_fragment_info, style.into(), rect),
71            children,
72            scrollable_overflow: Default::default(),
73            scrollable_overflow_is_up_to_date: AtomicBool::new(false),
74            cumulative_containing_block_rect: Default::default(),
75            is_line_box,
76        })
77    }
78
79    #[inline]
80    pub(crate) fn set_containing_block(&self, containing_block: &PhysicalRect<Au>) {
81        self.cumulative_containing_block_rect.set(*containing_block);
82    }
83
84    pub fn offset_by_containing_block(
85        &self,
86        rect: &PhysicalRect<Au>,
87        containing_block_computation: ContainingBlockCalculation<'_>,
88    ) -> PhysicalRect<Au> {
89        containing_block_computation.ensure();
90        rect.translate(self.cumulative_containing_block_rect.origin().to_vector())
91    }
92
93    /// Get the scrollable overflow for this [`PositioningFragment`] relative to its
94    /// containing block, recalculating scrollable overflow when necessary, for instance
95    /// after a style change.
96    pub(crate) fn scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
97        if self
98            .scrollable_overflow_is_up_to_date
99            .load(Ordering::Acquire)
100        {
101            self.scrollable_overflow.get()
102        } else {
103            let rect = self.calculate_scrollable_overflow();
104            self.scrollable_overflow.set(rect);
105            self.scrollable_overflow_is_up_to_date
106                .store(true, Ordering::Release);
107            rect
108        }
109    }
110
111    /// Clear the scrollable overflow on this [`PositioningFragment`]. This is called
112    /// during damage propagation when a fragment is preserved, itself or one of its
113    /// descendants has scrollable overflow damage.
114    pub(crate) fn clear_scrollable_overflow(&self) {
115        self.scrollable_overflow_is_up_to_date
116            .store(false, Ordering::Release);
117    }
118
119    fn calculate_scrollable_overflow(&self) -> PhysicalRect<Au> {
120        self.children
121            .iter()
122            .fold(PhysicalRect::zero(), |acc, child| {
123                acc.union(
124                    &child
125                        .scrollable_overflow_for_parent()
126                        .translate(self.base.rect().origin.to_vector()),
127                )
128            })
129    }
130
131    pub(crate) fn is_line_box(&self) -> bool {
132        self.is_line_box
133    }
134
135    pub fn print(&self, tree: &mut PrintTree) {
136        tree.new_level(format!(
137            "PositioningFragment\
138                \nbase={:?}\
139                \nrect={:?}\
140                \nscrollable_overflow={:?}",
141            self.base,
142            self.base.rect(),
143            self.scrollable_overflow
144        ));
145
146        for child in &self.children {
147            child.print(tree);
148        }
149        tree.end_level();
150    }
151}