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