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 && root_overflow.y == Overflow::Visible {
107 if let Some(body_overflow) = propagate_from_body() {
108 return body_overflow;
109 }
110 }
111 base.base_fragment_info
112 .flags
113 .insert(FragmentFlags::PROPAGATED_OVERFLOW_TO_VIEWPORT);
114 root_overflow
115 })
116 }
117}
118
119fn construct_for_root_element(
120 context: &LayoutContext,
121 root_element: ServoLayoutNode<'_>,
122) -> Vec<ArcRefCell<BlockLevelBox>> {
123 let info = NodeAndStyleInfo::new(root_element, root_element.style(&context.style_context));
124 let box_style = info.style.get_box();
125
126 let display_inside = match Display::from(box_style.display) {
127 Display::None => return Vec::new(),
128 Display::Contents => {
129 unreachable!()
133 },
134 Display::GeneratingBox(display_generating_box) => display_generating_box.display_inside(),
136 };
137
138 let contents = Contents::for_element(root_element, context);
139
140 let propagated_data = PropagatedBoxTreeData::default();
141 let root_box = if box_style.position.is_absolutely_positioned() {
142 BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(ArcRefCell::new(
143 AbsolutelyPositionedBox::construct(context, &info, display_inside, contents),
144 ))
145 } else if box_style.float.is_floating() {
146 BlockLevelBox::OutOfFlowFloatBox(FloatBox::construct(
147 context,
148 &info,
149 display_inside,
150 contents,
151 propagated_data,
152 ))
153 } else {
154 BlockLevelBox::Independent(IndependentFormattingContext::construct(
155 context,
156 &info,
157 display_inside,
158 contents,
159 propagated_data,
160 ))
161 };
162
163 let root_box = ArcRefCell::new(root_box);
164 root_element
165 .box_slot()
166 .set(LayoutBox::BlockLevel(root_box.clone()));
167 vec![root_box]
168}
169
170impl BoxTree {
171 #[servo_tracing::instrument(name = "Fragment Tree Construction", skip_all)]
172 pub(crate) fn layout(
173 &self,
174 layout_context: &LayoutContext,
175 viewport: UntypedSize2D<Au>,
176 ) -> FragmentTree {
177 let style = layout_context
178 .style_context
179 .stylist
180 .device()
181 .default_computed_values();
182
183 let physical_containing_block: Rect<Au, CSSPixel> =
186 PhysicalSize::from_untyped(viewport).into();
187 let initial_containing_block = DefiniteContainingBlock {
188 size: LogicalVec2 {
189 inline: physical_containing_block.size.width,
190 block: physical_containing_block.size.height,
191 },
192 style,
193 };
194
195 let mut positioning_context = PositioningContext::default();
196 let independent_layout = self.root.layout(
197 layout_context,
198 &mut positioning_context,
199 &(&initial_containing_block).into(),
200 );
201
202 let mut root_fragments = independent_layout.fragments;
203
204 assert!(root_fragments.len() <= 1);
206
207 positioning_context.layout_initial_containing_block_children(
211 layout_context,
212 &initial_containing_block,
213 &mut root_fragments,
214 );
215
216 let viewport_scroll_sensitivity = AxesScrollSensitivity {
217 x: self.viewport_overflow.x.into(),
218 y: self.viewport_overflow.y.into(),
219 };
220
221 FragmentTree::new(
222 root_fragments,
223 physical_containing_block,
224 viewport_scroll_sensitivity,
225 )
226 }
227}