1use app_units::Au;
6use euclid::Rect;
7use euclid::default::Size2D as UntypedSize2D;
8use layout_api::{AxesOverflow, LayoutElement, LayoutNode};
9use malloc_size_of_derive::MallocSizeOf;
10use paint_api::display_list::AxesScrollSensitivity;
11use script::layout_dom::ServoLayoutNode;
12use style::values::computed::Overflow;
13use style_traits::CSSPixel;
14
15use crate::cell::ArcRefCell;
16use crate::context::LayoutContext;
17use crate::dom::{LayoutBox, NodeExt};
18use crate::dom_traversal::{Contents, NodeAndStyleInfo};
19use crate::flow::float::FloatBox;
20use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox};
21use crate::formatting_contexts::IndependentFormattingContext;
22use crate::fragment_tree::{FragmentFlags, FragmentTree};
23use crate::geom::{LogicalVec2, PhysicalSize};
24use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
25use crate::style_ext::Display;
26use crate::{DefiniteContainingBlock, PropagatedBoxTreeData};
27
28#[derive(MallocSizeOf)]
29pub struct BoxTree {
30 root: BlockFormattingContext,
33
34 pub(crate) viewport_overflow: AxesOverflow,
36}
37
38impl BoxTree {
39 #[servo_tracing::instrument(name = "Box Tree Construction", skip_all)]
40 pub(crate) fn construct(context: &LayoutContext, root_element: ServoLayoutNode<'_>) -> Self {
41 let boxes = construct_for_root_element(context, root_element);
42
43 assert!(boxes.len() <= 1);
45
46 let viewport_overflow = Self::viewport_overflow(root_element, boxes.first());
47 let contents = BlockContainer::BlockLevelBoxes(boxes);
48 let contains_floats = contents.contains_floats();
49 Self {
50 root: BlockFormattingContext {
51 contents,
52 contains_floats,
53 },
54 viewport_overflow: viewport_overflow.to_scrollable(),
58 }
59 }
60
61 fn viewport_overflow(
62 root_element: ServoLayoutNode<'_>,
63 root_box: Option<&ArcRefCell<BlockLevelBox>>,
64 ) -> AxesOverflow {
65 let Some(root_box) = root_box else {
78 return AxesOverflow::default();
79 };
80
81 let propagate_from_body = || {
82 let body = root_element.flat_tree_children().find(|child| {
86 child
87 .as_element()
88 .is_some_and(|element| element.is_body_element_of_html_element_root())
89 })?;
90
91 let body_layout_data = body.inner_layout_data()?;
95 let mut body_box = body_layout_data.self_box.borrow_mut();
96 body_box.as_mut()?.with_base_mut(|base| {
97 base.base_fragment_info
98 .flags
99 .insert(FragmentFlags::PROPAGATED_OVERFLOW_TO_VIEWPORT);
100 AxesOverflow::from(&*base.style)
101 })
102 };
103
104 root_box.borrow_mut().with_base_mut(|base| {
105 let root_overflow = AxesOverflow::from(&*base.style);
106 if root_overflow.x == Overflow::Visible &&
107 root_overflow.y == Overflow::Visible &&
108 let Some(body_overflow) = propagate_from_body()
109 {
110 return body_overflow;
111 }
112 base.base_fragment_info
113 .flags
114 .insert(FragmentFlags::PROPAGATED_OVERFLOW_TO_VIEWPORT);
115 root_overflow
116 })
117 }
118}
119
120fn construct_for_root_element(
121 context: &LayoutContext,
122 root_element: ServoLayoutNode<'_>,
123) -> Vec<ArcRefCell<BlockLevelBox>> {
124 let info = NodeAndStyleInfo::new(root_element, root_element.style(&context.style_context));
125 let box_style = info.style.get_box();
126
127 let display_inside = match Display::from(box_style.display) {
128 Display::None => return Vec::new(),
129 Display::Contents => {
130 unreachable!()
134 },
135 Display::GeneratingBox(display_generating_box) => display_generating_box.display_inside(),
137 };
138
139 let contents = Contents::for_element(root_element, context);
140
141 let propagated_data = PropagatedBoxTreeData::default();
142 let root_box = if box_style.position.is_absolutely_positioned() {
143 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(ArcRefCell::new(
144 AbsolutelyPositionedBox::construct(context, &info, display_inside, contents),
145 ))
146 } else if box_style.float.is_floating() {
147 BlockLevelBox::OutOfFlowFloatBox(FloatBox::construct(
148 context,
149 &info,
150 display_inside,
151 contents,
152 propagated_data,
153 ))
154 } else {
155 BlockLevelBox::Independent(IndependentFormattingContext::construct(
156 context,
157 &info,
158 display_inside,
159 contents,
160 propagated_data,
161 ))
162 };
163
164 let root_box = ArcRefCell::new(root_box);
165 root_element
166 .box_slot()
167 .set(LayoutBox::BlockLevel(root_box.clone()));
168 vec![root_box]
169}
170
171impl BoxTree {
172 #[servo_tracing::instrument(name = "Fragment Tree Construction", skip_all)]
173 pub(crate) fn layout(
174 &self,
175 layout_context: &LayoutContext,
176 viewport: UntypedSize2D<Au>,
177 ) -> FragmentTree {
178 let style = layout_context
179 .style_context
180 .stylist
181 .device()
182 .default_computed_values();
183
184 let physical_containing_block: Rect<Au, CSSPixel> =
187 PhysicalSize::from_untyped(viewport).into();
188 let initial_containing_block = DefiniteContainingBlock {
189 size: LogicalVec2 {
190 inline: physical_containing_block.size.width,
191 block: physical_containing_block.size.height,
192 },
193 style,
194 };
195
196 let mut positioning_context = PositioningContext::default();
197 let independent_layout = self.root.layout(
198 layout_context,
199 &mut positioning_context,
200 &(&initial_containing_block).into(),
201 );
202
203 let mut root_fragments = independent_layout.fragments;
204
205 assert!(root_fragments.len() <= 1);
207
208 positioning_context.layout_initial_containing_block_children(
212 layout_context,
213 &initial_containing_block,
214 &mut root_fragments,
215 );
216
217 let viewport_scroll_sensitivity = AxesScrollSensitivity {
218 x: self.viewport_overflow.x.into(),
219 y: self.viewport_overflow.y.into(),
220 };
221
222 FragmentTree::new(
223 root_fragments,
224 physical_containing_block,
225 viewport_scroll_sensitivity,
226 )
227 }
228}