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