1#![allow(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::Direction;
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, Size};
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 available_block_size,
180 justify_self,
181 ..
182 } = solve_containing_block_padding_and_border_for_in_flow_box(
183 containing_block,
184 &layout_style,
185 get_inline_content_sizes,
186 ignore_block_margins_for_stretch,
187 None,
188 has_inline_parent,
189 );
190 let ResolvedMargins {
191 margin,
192 effective_margin_inline_start,
193 } = solve_margins(
194 containing_block,
195 &pbm,
196 containing_block_for_children.size.inline,
197 justify_self,
198 );
199
200 let start_margin_can_collapse_with_children =
201 pbm.padding.block_start.is_zero() && pbm.border.block_start.is_zero();
202
203 let mut clearance = None;
204 let parent_containing_block_position_info;
205 match sequential_layout_state {
206 None => parent_containing_block_position_info = None,
207 Some(ref mut sequential_layout_state) => {
208 let clear = Clear::from_style_and_container_writing_mode(
209 style,
210 containing_block_writing_mode,
211 );
212 let mut block_start_margin = CollapsedMargin::new(margin.block_start);
213
214 let collapsible_with_parent_start_margin = collapsible_with_parent_start_margin.expect(
226 "We should know whether we are collapsing the block start margin with the parent \
227 when laying out sequentially",
228 ).0 && clear == Clear::None;
229 if !collapsible_with_parent_start_margin && start_margin_can_collapse_with_children
230 {
231 self.contents.find_block_margin_collapsing_with_parent(
232 layout_context,
233 &mut block_start_margin,
234 &containing_block_for_children,
235 );
236 }
237
238 clearance = sequential_layout_state.calculate_clearance(clear, &block_start_margin);
240 if clearance.is_some() {
241 sequential_layout_state.commit_margin();
242 }
243 sequential_layout_state.adjoin_assign(&block_start_margin);
244 if !start_margin_can_collapse_with_children {
245 sequential_layout_state.commit_margin();
246 }
247
248 sequential_layout_state.advance_block_position(
251 pbm.padding.block_start +
252 pbm.border.block_start +
253 clearance.unwrap_or_else(Au::zero),
254 );
255
256 let inline_start = sequential_layout_state
262 .floats
263 .containing_block_info
264 .inline_start +
265 pbm.padding.inline_start +
266 pbm.border.inline_start +
267 effective_margin_inline_start;
268 let new_cb_offsets = ContainingBlockPositionInfo {
269 block_start: sequential_layout_state.bfc_relative_block_position,
270 block_start_margins_not_collapsed: sequential_layout_state.current_margin,
271 inline_start,
272 inline_end: inline_start + containing_block_for_children.size.inline,
273 };
274 parent_containing_block_position_info = Some(
275 sequential_layout_state.replace_containing_block_position_info(new_cb_offsets),
276 );
277 },
278 };
279
280 let ignore_block_margins_for_stretch = LogicalSides1D::new(
286 pbm.border.block_start.is_zero() && pbm.padding.block_start.is_zero(),
287 pbm.border.block_end.is_zero() && pbm.padding.block_end.is_zero(),
288 );
289
290 let flow_layout = self.contents.layout(
291 layout_context,
292 positioning_context,
293 &containing_block_for_children,
294 sequential_layout_state.as_deref_mut(),
295 CollapsibleWithParentStartMargin(start_margin_can_collapse_with_children),
296 ignore_block_margins_for_stretch,
297 );
298 let mut content_block_size = flow_layout.content_block_size;
299
300 let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
302 let mut collapsible_margins_in_children = flow_layout.collapsible_margins_in_children;
303 if start_margin_can_collapse_with_children {
304 block_margins_collapsed_with_children
305 .start
306 .adjoin_assign(&collapsible_margins_in_children.start);
307 if collapsible_margins_in_children.collapsed_through {
308 block_margins_collapsed_with_children
309 .start
310 .adjoin_assign(&std::mem::replace(
311 &mut collapsible_margins_in_children.end,
312 CollapsedMargin::zero(),
313 ));
314 }
315 }
316
317 let is_anonymous = matches!(
318 self.base.style.pseudo(),
319 Some(PseudoElement::ServoAnonymousBox)
320 );
321 let tentative_block_size = if is_anonymous {
322 &Default::default()
326 } else {
327 &containing_block_for_children.size.block
328 };
329 let collapsed_through = collapsible_margins_in_children.collapsed_through &&
330 pbm.padding_border_sums.block.is_zero() &&
331 tentative_block_size.definite_or_min().is_zero();
332 block_margins_collapsed_with_children.collapsed_through = collapsed_through;
333
334 let end_margin_can_collapse_with_children =
335 pbm.padding.block_end.is_zero() && pbm.border.block_end.is_zero();
336 if !end_margin_can_collapse_with_children {
337 content_block_size += collapsible_margins_in_children.end.solve();
338 }
339
340 let block_size = block_sizes.resolve(
341 Direction::Block,
342 Size::FitContent,
343 Au::zero,
344 available_block_size,
345 || content_block_size.into(),
346 false, );
348
349 let end_margin_can_collapse_with_children = end_margin_can_collapse_with_children &&
364 block_size == content_block_size &&
365 (collapsed_through || !tentative_block_size.is_definite());
366 if end_margin_can_collapse_with_children {
367 block_margins_collapsed_with_children
368 .end
369 .adjoin_assign(&collapsible_margins_in_children.end);
370 }
371
372 if let Some(ref mut sequential_layout_state) = sequential_layout_state {
373 sequential_layout_state.replace_containing_block_position_info(
376 parent_containing_block_position_info.unwrap(),
377 );
378
379 sequential_layout_state.advance_block_position(
389 block_size - content_block_size + pbm.padding.block_end + pbm.border.block_end,
390 );
391
392 if !end_margin_can_collapse_with_children {
393 sequential_layout_state.commit_margin();
394 }
395 sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_end));
396 }
397
398 let content_rect = LogicalRect {
399 start_corner: LogicalVec2 {
400 block: (pbm.padding.block_start +
401 pbm.border.block_start +
402 clearance.unwrap_or_else(Au::zero)),
403 inline: pbm.padding.inline_start +
404 pbm.border.inline_start +
405 effective_margin_inline_start,
406 },
407 size: LogicalVec2 {
408 block: block_size,
409 inline: containing_block_for_children.size.inline,
410 },
411 };
412
413 let mut base_fragment_info = self.base.base_fragment_info;
414
415 if depends_on_block_constraints ||
419 (is_anonymous && flow_layout.depends_on_block_constraints)
420 {
421 base_fragment_info.flags.insert(
422 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
423 );
424 }
425
426 BoxFragment::new(
427 base_fragment_info,
428 style.clone(),
429 flow_layout.fragments,
430 content_rect.as_physical(Some(containing_block)),
431 pbm.padding.to_physical(containing_block_writing_mode),
432 pbm.border.to_physical(containing_block_writing_mode),
433 margin.to_physical(containing_block_writing_mode),
434 flow_layout.specific_layout_info,
435 )
436 .with_baselines(flow_layout.baselines)
437 .with_block_level_layout_info(block_margins_collapsed_with_children, clearance)
438 }
439}