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