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 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 Direction::Block,
351 Size::FitContent,
352 Au::zero,
353 available_block_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}