1use app_units::Au;
6use layout_api::wrapper_traits::ThreadSafeLayoutNode;
7use malloc_size_of_derive::MallocSizeOf;
8use script::layout_dom::{ServoLayoutElement, ServoThreadSafeLayoutNode};
9use servo_arc::Arc;
10use style::context::SharedStyleContext;
11use style::logical_geometry::Direction;
12use style::properties::ComputedValues;
13use style::selector_parser::PseudoElement;
14
15use crate::context::LayoutContext;
16use crate::dom::WeakLayoutBox;
17use crate::dom_traversal::{Contents, NodeAndStyleInfo, NonReplacedContents};
18use crate::flexbox::FlexContainer;
19use crate::flow::BlockFormattingContext;
20use crate::fragment_tree::{BaseFragmentInfo, FragmentFlags};
21use crate::layout_box_base::{
22 CacheableLayoutResult, CacheableLayoutResultAndInputs, LayoutBoxBase,
23};
24use crate::positioned::PositioningContext;
25use crate::replaced::ReplacedContents;
26use crate::sizing::{
27 self, ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, LazySize,
28};
29use crate::style_ext::{AspectRatio, Display, DisplayInside, LayoutStyle};
30use crate::table::Table;
31use crate::taffy::TaffyContainer;
32use crate::{
33 ArcRefCell, ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, LogicalVec2,
34 PropagatedBoxTreeData,
35};
36
37#[derive(Debug, MallocSizeOf)]
39pub(crate) struct IndependentFormattingContext {
40 pub base: LayoutBoxBase,
41 contents: IndependentFormattingContextContents,
44 pub propagated_data: PropagatedBoxTreeData,
47}
48
49#[derive(Debug, MallocSizeOf)]
50pub(crate) enum IndependentFormattingContextContents {
51 Replaced(
53 ReplacedContents,
54 Option<ArcRefCell<IndependentFormattingContext>>,
55 ),
56 Flow(BlockFormattingContext),
57 Flex(FlexContainer),
58 Grid(TaffyContainer),
59 Table(Table),
60 }
62
63#[derive(Clone, Copy, Debug, Default, MallocSizeOf)]
66pub(crate) struct Baselines {
67 pub first: Option<Au>,
68 pub last: Option<Au>,
69}
70
71impl Baselines {
72 pub(crate) fn offset(&self, block_offset: Au) -> Baselines {
73 Self {
74 first: self.first.map(|first| first + block_offset),
75 last: self.last.map(|last| last + block_offset),
76 }
77 }
78}
79
80impl IndependentFormattingContext {
81 pub(crate) fn new(
82 base: LayoutBoxBase,
83 contents: IndependentFormattingContextContents,
84 propagated_data: PropagatedBoxTreeData,
85 ) -> Self {
86 Self {
87 base,
88 contents,
89 propagated_data,
90 }
91 }
92
93 pub(crate) fn rebuild(
94 &mut self,
95 layout_context: &LayoutContext,
96 node_and_style_info: &NodeAndStyleInfo,
97 ) {
98 let contents = Contents::for_element(node_and_style_info.node, layout_context);
99 let display = match Display::from(node_and_style_info.style.get_box().display) {
100 Display::None | Display::Contents => {
101 unreachable!("Should never try to rebuild IndependentFormattingContext with no box")
102 },
103 Display::GeneratingBox(display) => display.used_value_for_contents(&contents),
104 };
105 self.contents = Self::construct_contents(
106 layout_context,
107 node_and_style_info,
108 &mut self.base.base_fragment_info,
109 display.display_inside(),
110 contents,
111 self.propagated_data,
112 );
113
114 self.base.clear_fragments_and_fragment_cache();
115 *self.base.cached_inline_content_size.borrow_mut() = None;
116 self.base.repair_style(&node_and_style_info.style);
117 }
118
119 pub(crate) fn construct(
120 context: &LayoutContext,
121 node_and_style_info: &NodeAndStyleInfo,
122 display_inside: DisplayInside,
123 contents: Contents,
124 propagated_data: PropagatedBoxTreeData,
125 ) -> Self {
126 let mut base_fragment_info: BaseFragmentInfo = node_and_style_info.into();
127 let contents = Self::construct_contents(
128 context,
129 node_and_style_info,
130 &mut base_fragment_info,
131 display_inside,
132 contents,
133 propagated_data,
134 );
135 Self {
136 base: LayoutBoxBase::new(base_fragment_info, node_and_style_info.style.clone()),
137 contents,
138 propagated_data,
139 }
140 }
141
142 fn construct_contents(
143 context: &LayoutContext,
144 node_and_style_info: &NodeAndStyleInfo,
145 base_fragment_info: &mut BaseFragmentInfo,
146 display_inside: DisplayInside,
147 contents: Contents,
148 propagated_data: PropagatedBoxTreeData,
149 ) -> IndependentFormattingContextContents {
150 let non_replaced_contents = match contents {
151 Contents::Replaced(contents) => {
152 base_fragment_info.flags.insert(FragmentFlags::IS_REPLACED);
153 let widget = Some(node_and_style_info.node)
155 .filter(|node| node.pseudo_element_chain().is_empty())
156 .and_then(|node| node.as_element())
157 .and_then(|element| element.shadow_root())
158 .is_some_and(|shadow_root| shadow_root.is_ua_widget())
159 .then(|| {
160 let widget_info = node_and_style_info
161 .with_pseudo_element(context, PseudoElement::ServoAnonymousBox)
162 .expect("Should always be able to construct info for anonymous boxes.");
163 let widget_contents = IndependentFormattingContextContents::Flow(
165 BlockFormattingContext::construct(
166 context,
167 &widget_info,
168 NonReplacedContents::OfElement,
169 propagated_data,
170 false, ),
172 );
173 let widget_base =
174 LayoutBoxBase::new((&widget_info).into(), widget_info.style);
175 ArcRefCell::new(IndependentFormattingContext::new(
176 widget_base,
177 widget_contents,
178 propagated_data,
179 ))
180 });
181 return IndependentFormattingContextContents::Replaced(contents, widget);
182 },
183 Contents::Widget(non_replaced_contents) => {
184 base_fragment_info.flags.insert(FragmentFlags::IS_WIDGET);
185 non_replaced_contents
186 },
187 Contents::NonReplaced(non_replaced_contents) => non_replaced_contents,
188 };
189
190 match display_inside {
191 DisplayInside::Flow { is_list_item } | DisplayInside::FlowRoot { is_list_item } => {
192 IndependentFormattingContextContents::Flow(BlockFormattingContext::construct(
193 context,
194 node_and_style_info,
195 non_replaced_contents,
196 propagated_data,
197 is_list_item,
198 ))
199 },
200 DisplayInside::Grid => {
201 IndependentFormattingContextContents::Grid(TaffyContainer::construct(
202 context,
203 node_and_style_info,
204 non_replaced_contents,
205 propagated_data,
206 ))
207 },
208 DisplayInside::Flex => {
209 IndependentFormattingContextContents::Flex(FlexContainer::construct(
210 context,
211 node_and_style_info,
212 non_replaced_contents,
213 propagated_data,
214 ))
215 },
216 DisplayInside::Table => {
217 let table_grid_style = context
218 .style_context
219 .stylist
220 .style_for_anonymous::<ServoLayoutElement>(
221 &context.style_context.guards,
222 &PseudoElement::ServoTableGrid,
223 &node_and_style_info.style,
224 );
225 base_fragment_info.flags.insert(FragmentFlags::DO_NOT_PAINT);
226 IndependentFormattingContextContents::Table(Table::construct(
227 context,
228 node_and_style_info,
229 table_grid_style,
230 non_replaced_contents,
231 propagated_data,
232 ))
233 },
234 }
235 }
236
237 #[inline]
238 pub fn style(&self) -> &Arc<ComputedValues> {
239 &self.base.style
240 }
241
242 #[inline]
243 pub fn base_fragment_info(&self) -> BaseFragmentInfo {
244 self.base.base_fragment_info
245 }
246
247 pub(crate) fn inline_content_sizes(
248 &self,
249 layout_context: &LayoutContext,
250 constraint_space: &ConstraintSpace,
251 ) -> InlineContentSizesResult {
252 self.base
253 .inline_content_sizes(layout_context, constraint_space, &self.contents)
254 }
255
256 pub(crate) fn tentative_block_content_size(
267 &self,
268 preferred_aspect_ratio: Option<AspectRatio>,
269 ) -> Option<ContentSizes> {
270 match &self.contents {
273 IndependentFormattingContextContents::Replaced(contents, _) => {
274 let ratio = preferred_aspect_ratio?;
276 let writing_mode = self.style().writing_mode;
277 let natural_sizes = contents.logical_natural_sizes(writing_mode);
278 let block_size = match (natural_sizes.block, natural_sizes.inline) {
279 (Some(block_size), None) => block_size,
280 _ => {
281 let inline_size = contents.fallback_inline_size(writing_mode);
282 ratio.compute_dependent_size(Direction::Block, inline_size)
283 },
284 };
285 Some(block_size.into())
286 },
287 _ => None,
288 }
289 }
290
291 pub(crate) fn outer_inline_content_sizes(
292 &self,
293 layout_context: &LayoutContext,
294 containing_block: &IndefiniteContainingBlock,
295 auto_minimum: &LogicalVec2<Au>,
296 auto_block_size_stretches_to_containing_block: bool,
297 ) -> InlineContentSizesResult {
298 sizing::outer_inline(
299 &self.base,
300 &self.layout_style(),
301 containing_block,
302 auto_minimum,
303 auto_block_size_stretches_to_containing_block,
304 self.is_replaced(),
305 true, |padding_border_sums| self.preferred_aspect_ratio(padding_border_sums),
307 |constraint_space| self.inline_content_sizes(layout_context, constraint_space),
308 |preferred_aspect_ratio| self.tentative_block_content_size(preferred_aspect_ratio),
309 )
310 }
311
312 pub(crate) fn repair_style(
313 &mut self,
314 context: &SharedStyleContext,
315 node: &ServoThreadSafeLayoutNode,
316 new_style: &Arc<ComputedValues>,
317 ) {
318 self.base.repair_style(new_style);
319 match &mut self.contents {
320 IndependentFormattingContextContents::Replaced(_, widget) => {
321 if let Some(widget) = widget {
322 let node = node
323 .with_pseudo(PseudoElement::ServoAnonymousBox)
324 .expect("Should always be able to construct info for anonymous boxes.");
325 widget.borrow_mut().repair_style(context, &node, new_style);
326 }
327 },
328 IndependentFormattingContextContents::Flow(block_formatting_context) => {
329 block_formatting_context.repair_style(context, node, new_style);
330 },
331 IndependentFormattingContextContents::Flex(flex_container) => {
332 flex_container.repair_style(new_style)
333 },
334 IndependentFormattingContextContents::Grid(taffy_container) => {
335 taffy_container.repair_style(new_style)
336 },
337 IndependentFormattingContextContents::Table(table) => {
338 table.repair_style(context, new_style)
339 },
340 }
341 }
342
343 #[inline]
344 pub(crate) fn is_block_container(&self) -> bool {
345 matches!(self.contents, IndependentFormattingContextContents::Flow(_))
346 }
347
348 #[inline]
349 pub(crate) fn is_replaced(&self) -> bool {
350 matches!(
351 self.contents,
352 IndependentFormattingContextContents::Replaced(_, _)
353 )
354 }
355
356 #[inline]
357 pub(crate) fn is_table(&self) -> bool {
358 matches!(
359 &self.contents,
360 IndependentFormattingContextContents::Table(_)
361 )
362 }
363
364 fn layout_without_caching(
365 &self,
366 layout_context: &LayoutContext,
367 positioning_context: &mut PositioningContext,
368 containing_block_for_children: &ContainingBlock,
369 containing_block: &ContainingBlock,
370 preferred_aspect_ratio: Option<AspectRatio>,
371 lazy_block_size: &LazySize,
372 ) -> CacheableLayoutResult {
373 match &self.contents {
374 IndependentFormattingContextContents::Replaced(replaced, widget) => {
375 let mut replaced_layout = replaced.layout(
376 layout_context,
377 containing_block_for_children,
378 preferred_aspect_ratio,
379 &self.base,
380 lazy_block_size,
381 );
382 if let Some(widget) = widget {
383 let mut widget_layout = widget.borrow().layout(
384 layout_context,
385 positioning_context,
386 containing_block_for_children,
387 containing_block_for_children,
388 None,
389 &LazySize::intrinsic(),
390 );
391 replaced_layout
392 .fragments
393 .append(&mut widget_layout.fragments);
394 }
395 replaced_layout
396 },
397 IndependentFormattingContextContents::Flow(bfc) => bfc.layout(
398 layout_context,
399 positioning_context,
400 containing_block_for_children,
401 ),
402 IndependentFormattingContextContents::Flex(fc) => fc.layout(
403 layout_context,
404 positioning_context,
405 containing_block_for_children,
406 lazy_block_size,
407 ),
408 IndependentFormattingContextContents::Grid(fc) => fc.layout(
409 layout_context,
410 positioning_context,
411 containing_block_for_children,
412 containing_block,
413 ),
414 IndependentFormattingContextContents::Table(table) => table.layout(
415 layout_context,
416 positioning_context,
417 containing_block_for_children,
418 containing_block,
419 ),
420 }
421 }
422
423 #[servo_tracing::instrument(name = "IndependentFormattingContext::layout", skip_all)]
424 pub(crate) fn layout(
425 &self,
426 layout_context: &LayoutContext,
427 positioning_context: &mut PositioningContext,
428 containing_block_for_children: &ContainingBlock,
429 containing_block: &ContainingBlock,
430 preferred_aspect_ratio: Option<AspectRatio>,
431 lazy_block_size: &LazySize,
432 ) -> CacheableLayoutResult {
433 if let Some(cache) = self.base.cached_layout_result.borrow().as_ref() {
434 let cache = &**cache;
435 if cache.containing_block_for_children_size.inline ==
436 containing_block_for_children.size.inline &&
437 (cache.containing_block_for_children_size.block ==
438 containing_block_for_children.size.block ||
439 !cache.result.depends_on_block_constraints)
440 {
441 positioning_context.append(cache.positioning_context.clone());
442 return cache.result.clone();
443 }
444 #[cfg(feature = "tracing")]
445 tracing::debug!(
446 name: "IndependentFormattingContext::layout cache miss",
447 cached = ?cache.containing_block_for_children_size,
448 required = ?containing_block_for_children.size,
449 );
450 }
451
452 let mut child_positioning_context = PositioningContext::default();
453 let result = self.layout_without_caching(
454 layout_context,
455 &mut child_positioning_context,
456 containing_block_for_children,
457 containing_block,
458 preferred_aspect_ratio,
459 lazy_block_size,
460 );
461
462 *self.base.cached_layout_result.borrow_mut() =
463 Some(Box::new(CacheableLayoutResultAndInputs {
464 result: result.clone(),
465 positioning_context: child_positioning_context.clone(),
466 containing_block_for_children_size: containing_block_for_children.size.clone(),
467 }));
468 positioning_context.append(child_positioning_context);
469
470 result
471 }
472
473 #[inline]
474 pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
475 match &self.contents {
476 IndependentFormattingContextContents::Replaced(replaced, _) => {
477 replaced.layout_style(&self.base)
478 },
479 IndependentFormattingContextContents::Flow(fc) => fc.layout_style(&self.base),
480 IndependentFormattingContextContents::Flex(fc) => fc.layout_style(),
481 IndependentFormattingContextContents::Grid(fc) => fc.layout_style(),
482 IndependentFormattingContextContents::Table(fc) => fc.layout_style(None),
483 }
484 }
485
486 #[inline]
487 pub(crate) fn preferred_aspect_ratio(
488 &self,
489 padding_border_sums: &LogicalVec2<Au>,
490 ) -> Option<AspectRatio> {
491 match &self.contents {
492 IndependentFormattingContextContents::Replaced(replaced, _) => {
493 replaced.preferred_aspect_ratio(self.style(), padding_border_sums)
494 },
495 _ => None,
497 }
498 }
499
500 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
501 match &self.contents {
502 IndependentFormattingContextContents::Replaced(_, widget) => {
503 if let Some(widget) = widget {
504 widget.borrow_mut().base.parent_box.replace(layout_box);
505 }
506 },
507 IndependentFormattingContextContents::Flow(contents) => {
508 contents.attached_to_tree(layout_box)
509 },
510 IndependentFormattingContextContents::Flex(contents) => {
511 contents.attached_to_tree(layout_box)
512 },
513 IndependentFormattingContextContents::Grid(contents) => {
514 contents.attached_to_tree(layout_box)
515 },
516 IndependentFormattingContextContents::Table(contents) => {
517 contents.attached_to_tree(layout_box)
518 },
519 }
520 }
521}
522
523impl ComputeInlineContentSizes for IndependentFormattingContextContents {
524 fn compute_inline_content_sizes(
525 &self,
526 layout_context: &LayoutContext,
527 constraint_space: &ConstraintSpace,
528 ) -> InlineContentSizesResult {
529 match self {
530 Self::Replaced(inner, _) => {
531 inner.compute_inline_content_sizes(layout_context, constraint_space)
532 },
533 Self::Flow(inner) => inner
534 .contents
535 .compute_inline_content_sizes(layout_context, constraint_space),
536 Self::Flex(inner) => {
537 inner.compute_inline_content_sizes(layout_context, constraint_space)
538 },
539 Self::Grid(inner) => {
540 inner.compute_inline_content_sizes(layout_context, constraint_space)
541 },
542 Self::Table(inner) => {
543 inner.compute_inline_content_sizes(layout_context, constraint_space)
544 },
545 }
546 }
547}