1#![expect(rustdoc::private_intra_doc_links)]
5
6use std::sync::Arc;
10
11use app_units::Au;
12use malloc_size_of_derive::MallocSizeOf;
13use script::layout_dom::ServoLayoutNode;
14use servo_arc::Arc as ServoArc;
15use style::Zero;
16use style::context::SharedStyleContext;
17use style::logical_geometry::LogicalAxis;
18use style::properties::ComputedValues;
19use style::servo::selector_parser::PseudoElement;
20
21use crate::context::LayoutContext;
22use crate::flow::float::{Clear, ContainingBlockPositionInfo, SequentialLayoutState};
23use crate::flow::{
24 BlockContainer, CollapsibleWithParentStartMargin, ContainingBlockPaddingAndBorder,
25 ResolvedMargins, solve_containing_block_padding_and_border_for_in_flow_box, solve_margins,
26};
27use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, CollapsedMargin, FragmentFlags};
28use crate::geom::{LogicalRect, LogicalSides1D, LogicalVec2};
29use crate::layout_box_base::LayoutBoxBase;
30use crate::positioned::PositioningContext;
31use crate::sizing::InlineContentSizesResult;
32use crate::style_ext::LayoutStyle;
33use crate::{ConstraintSpace, ContainingBlock};
34
35#[derive(Debug, MallocSizeOf)]
37pub(crate) struct SameFormattingContextBlock {
38 pub base: LayoutBoxBase,
39 pub contents: BlockContainer,
40 pub contains_floats: bool,
41}
42
43impl SameFormattingContextBlock {
44 pub(crate) fn new(
45 base: LayoutBoxBase,
46 contents: BlockContainer,
47 contains_floats: bool,
48 ) -> Self {
49 Self {
50 base,
51 contents,
52 contains_floats,
53 }
54 }
55
56 pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
57 self.contents.layout_style(&self.base)
58 }
59
60 pub(crate) fn repair_style(
61 &mut self,
62 context: &SharedStyleContext,
63 node: &ServoLayoutNode,
64 new_style: &ServoArc<ComputedValues>,
65 ) {
66 self.base.repair_style(new_style);
67 self.contents.repair_style(context, node, new_style);
68 }
69
70 pub(crate) fn inline_content_sizes(
71 &self,
72 layout_context: &LayoutContext,
73 constraint_space: &ConstraintSpace,
74 ) -> InlineContentSizesResult {
75 self.base
76 .inline_content_sizes(layout_context, constraint_space, &self.contents)
77 }
78
79 #[expect(clippy::too_many_arguments)]
86 pub(crate) fn layout_in_flow_non_replaced_block_level_cached(
87 &self,
88 layout_context: &LayoutContext<'_>,
89 positioning_context: &mut PositioningContext,
90 containing_block: &ContainingBlock<'_>,
91 sequential_layout_state: Option<&mut SequentialLayoutState>,
92 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
93 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
94 has_inline_parent: bool,
95 ) -> Arc<BoxFragment> {
96 let mut allows_caching = sequential_layout_state.is_none();
97
98 if allows_caching &&
99 let Some(cached_result) = self
100 .base
101 .cached_same_formatting_context_block_if_applicable(
102 containing_block,
103 collapsible_with_parent_start_margin,
104 ignore_block_margins_for_stretch,
105 has_inline_parent,
106 )
107 {
108 return cached_result;
109 };
110
111 let positioning_context_length = positioning_context.len();
112 let fragment = Arc::new(positioning_context.layout_maybe_position_relative_fragment(
113 layout_context,
114 containing_block,
115 &self.base,
116 |positioning_context| {
117 self.layout_in_flow_non_replaced_block_level(
118 layout_context,
119 positioning_context,
120 containing_block,
121 sequential_layout_state,
122 collapsible_with_parent_start_margin,
123 ignore_block_margins_for_stretch,
124 has_inline_parent,
125 )
126 },
127 ));
128
129 allows_caching = allows_caching && positioning_context_length == positioning_context.len();
135
136 if !allows_caching {
137 self.base.clear_fragments_and_dirty_fragment_cache();
138 } else {
139 self.base.cache_same_formatting_context_block_layout(
140 containing_block,
141 collapsible_with_parent_start_margin,
142 ignore_block_margins_for_stretch,
143 has_inline_parent,
144 fragment.clone(),
145 );
146 }
147
148 fragment
149 }
150
151 #[expect(clippy::too_many_arguments)]
156 fn layout_in_flow_non_replaced_block_level(
157 &self,
158 layout_context: &LayoutContext,
159 positioning_context: &mut PositioningContext,
160 containing_block: &ContainingBlock,
161 mut sequential_layout_state: Option<&mut SequentialLayoutState>,
162 collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
163 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
164 has_inline_parent: bool,
165 ) -> BoxFragment {
166 let style = &self.base.style;
167 let layout_style = self.contents.layout_style(&self.base);
168 let containing_block_writing_mode = containing_block.style.writing_mode;
169 let get_inline_content_sizes = |constraint_space: &ConstraintSpace| {
170 self.base
171 .inline_content_sizes(layout_context, constraint_space, &self.contents)
172 .sizes
173 };
174 let ContainingBlockPaddingAndBorder {
175 containing_block: containing_block_for_children,
176 pbm,
177 block_sizes,
178 depends_on_block_constraints,
179 block_stretch_size,
180 justify_self,
181 automatic_block_size,
182 ..
183 } = solve_containing_block_padding_and_border_for_in_flow_box(
184 layout_context,
185 containing_block,
186 &layout_style,
187 get_inline_content_sizes,
188 ignore_block_margins_for_stretch,
189 &self.base,
190 None,
191 has_inline_parent,
192 );
193 let ResolvedMargins {
194 margin,
195 effective_margin_inline_start,
196 } = solve_margins(
197 containing_block,
198 &pbm,
199 containing_block_for_children.size.inline,
200 justify_self,
201 );
202
203 let start_margin_can_collapse_with_children =
204 pbm.padding.block_start.is_zero() && pbm.border.block_start.is_zero();
205
206 let mut clearance = None;
207 let parent_containing_block_position_info = match sequential_layout_state {
208 None => None,
209 Some(ref mut sequential_layout_state) => {
210 let clear = Clear::from_style_and_container_writing_mode(
211 style,
212 containing_block_writing_mode,
213 );
214 let mut block_start_margin = CollapsedMargin::new(margin.block_start);
215
216 let collapsible_with_parent_start_margin = collapsible_with_parent_start_margin.expect(
228 "We should know whether we are collapsing the block start margin with the parent \
229 when laying out sequentially",
230 ).0 && clear == Clear::None;
231 if !collapsible_with_parent_start_margin && start_margin_can_collapse_with_children
232 {
233 self.contents.find_block_margin_collapsing_with_parent(
234 layout_context,
235 &mut block_start_margin,
236 &containing_block_for_children,
237 );
238 }
239
240 clearance = sequential_layout_state.calculate_clearance(clear, &block_start_margin);
242 if clearance.is_some() {
243 sequential_layout_state.commit_margin();
244 }
245 sequential_layout_state.adjoin_assign(&block_start_margin);
246 if !start_margin_can_collapse_with_children {
247 sequential_layout_state.commit_margin();
248 }
249
250 sequential_layout_state.advance_block_position(
253 pbm.padding.block_start +
254 pbm.border.block_start +
255 clearance.unwrap_or_else(Au::zero),
256 );
257
258 let inline_start = sequential_layout_state
264 .floats
265 .containing_block_info
266 .inline_start +
267 pbm.padding.inline_start +
268 pbm.border.inline_start +
269 effective_margin_inline_start;
270 let new_cb_offsets = ContainingBlockPositionInfo {
271 block_start: sequential_layout_state.bfc_relative_block_position,
272 block_start_margins_not_collapsed: sequential_layout_state.current_margin,
273 inline_start,
274 inline_end: inline_start + containing_block_for_children.size.inline,
275 };
276 Some(sequential_layout_state.replace_containing_block_position_info(new_cb_offsets))
277 },
278 };
279
280 let is_anonymous = matches!(
281 self.base.style.pseudo(),
282 Some(PseudoElement::ServoAnonymousBox)
283 );
284
285 let ignore_block_margins_for_stretch = if is_anonymous {
295 ignore_block_margins_for_stretch
296 } else {
297 LogicalSides1D::new(
298 pbm.border.block_start.is_zero() && pbm.padding.block_start.is_zero(),
299 pbm.border.block_end.is_zero() && pbm.padding.block_end.is_zero(),
300 )
301 };
302
303 let flow_layout = self.contents.layout(
304 layout_context,
305 positioning_context,
306 &containing_block_for_children,
307 sequential_layout_state.as_deref_mut(),
308 CollapsibleWithParentStartMargin(start_margin_can_collapse_with_children),
309 ignore_block_margins_for_stretch,
310 );
311 let mut content_block_size = flow_layout.content_block_size;
312
313 let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
315 let mut collapsible_margins_in_children = flow_layout.collapsible_margins_in_children;
316 if start_margin_can_collapse_with_children {
317 block_margins_collapsed_with_children
318 .start
319 .adjoin_assign(&collapsible_margins_in_children.start);
320 if collapsible_margins_in_children.collapsed_through {
321 block_margins_collapsed_with_children
322 .start
323 .adjoin_assign(&std::mem::replace(
324 &mut collapsible_margins_in_children.end,
325 CollapsedMargin::zero(),
326 ));
327 }
328 }
329
330 let tentative_block_size = if is_anonymous {
331 &Default::default()
335 } else {
336 &containing_block_for_children.size.block
337 };
338 let collapsed_through = collapsible_margins_in_children.collapsed_through &&
339 pbm.padding_border_sums.block.is_zero() &&
340 tentative_block_size.definite_or_min().is_zero();
341 block_margins_collapsed_with_children.collapsed_through = collapsed_through;
342
343 let end_margin_can_collapse_with_children =
344 pbm.padding.block_end.is_zero() && pbm.border.block_end.is_zero();
345 if !end_margin_can_collapse_with_children {
346 content_block_size += collapsible_margins_in_children.end.solve();
347 }
348
349 let block_size = block_sizes.resolve(
350 LogicalAxis::Block,
351 automatic_block_size,
352 Au::zero,
353 block_stretch_size,
354 || content_block_size.into(),
355 false, );
357
358 let end_margin_can_collapse_with_children = end_margin_can_collapse_with_children &&
373 block_size == content_block_size &&
374 (collapsed_through || !tentative_block_size.is_definite());
375 if end_margin_can_collapse_with_children {
376 block_margins_collapsed_with_children
377 .end
378 .adjoin_assign(&collapsible_margins_in_children.end);
379 }
380
381 if let Some(ref mut sequential_layout_state) = sequential_layout_state {
382 sequential_layout_state.replace_containing_block_position_info(
385 parent_containing_block_position_info.unwrap(),
386 );
387
388 sequential_layout_state.advance_block_position(
398 block_size - content_block_size + pbm.padding.block_end + pbm.border.block_end,
399 );
400
401 if !end_margin_can_collapse_with_children {
402 sequential_layout_state.commit_margin();
403 }
404 sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_end));
405 }
406
407 let content_rect = LogicalRect {
408 start_corner: LogicalVec2 {
409 block: (pbm.padding.block_start +
410 pbm.border.block_start +
411 clearance.unwrap_or_else(Au::zero)),
412 inline: pbm.padding.inline_start +
413 pbm.border.inline_start +
414 effective_margin_inline_start,
415 },
416 size: LogicalVec2 {
417 block: block_size,
418 inline: containing_block_for_children.size.inline,
419 },
420 };
421
422 let mut base_fragment_info = self.base.base_fragment_info;
423
424 if depends_on_block_constraints ||
428 (is_anonymous && flow_layout.depends_on_block_constraints)
429 {
430 base_fragment_info.flags.insert(
431 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
432 );
433 }
434
435 BoxFragment::new(
436 base_fragment_info,
437 style.clone(),
438 flow_layout.fragments,
439 content_rect.as_physical(Some(containing_block)),
440 pbm.padding.to_physical(containing_block_writing_mode),
441 pbm.border.to_physical(containing_block_writing_mode),
442 margin.to_physical(containing_block_writing_mode),
443 flow_layout.specific_layout_info,
444 )
445 .with_baselines(flow_layout.baselines)
446 .with_block_level_layout_info(block_margins_collapsed_with_children, clearance)
447 }
448}