1use app_units::Au;
6use atomic_refcell::AtomicRefCell;
7use layout_api::LayoutNode;
8use malloc_size_of_derive::MallocSizeOf;
9use script::layout_dom::{ServoDangerousStyleElement, ServoLayoutNode};
10use servo_arc::Arc;
11use style::context::SharedStyleContext;
12use style::logical_geometry::LogicalAxis;
13use style::properties::ComputedValues;
14use style::selector_parser::PseudoElement;
15
16use crate::context::LayoutContext;
17use crate::dom::{LayoutBox, NodeExt, WeakLayoutBox};
18use crate::dom_traversal::{Contents, NodeAndStyleInfo, NonReplacedContents};
19use crate::flexbox::FlexContainer;
20use crate::flow::BlockFormattingContext;
21use crate::flow::inline::InlineFormattingContext;
22use crate::fragment_tree::{BaseFragmentInfo, FragmentFlags};
23use crate::layout_box_base::{IndependentFormattingContextLayoutResult, LayoutBoxBase};
24use crate::positioned::{LayoutRootLayoutInputs, PositioningContext};
25use crate::replaced::ReplacedContents;
26use crate::sizing::{
27 self, ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, LazySize,
28 SizeConstraint,
29};
30use crate::style_ext::{AspectRatio, Display, DisplayInside, LayoutStyle};
31use crate::table::Table;
32use crate::taffy::TaffyContainer;
33use crate::{
34 ArcRefCell, ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock,
35 LogicalVec2, PropagatedBoxTreeData,
36};
37
38#[derive(Debug, MallocSizeOf)]
40pub(crate) struct IndependentFormattingContext {
41 pub base: LayoutBoxBase,
42 contents: IndependentFormattingContextContents,
45 pub propagated_data: PropagatedBoxTreeData,
48 pub layout_root_layout_inputs: AtomicRefCell<Option<Box<LayoutRootLayoutInputs>>>,
51}
52
53#[derive(Debug, MallocSizeOf)]
54pub(crate) enum IndependentFormattingContextContents {
55 Replaced(
57 ReplacedContents,
58 Option<ArcRefCell<IndependentFormattingContext>>,
59 ),
60 Flow(BlockFormattingContext),
61 Flex(FlexContainer),
62 Grid(TaffyContainer),
63 Table(Table),
64 }
66
67impl IndependentFormattingContextContents {
68 fn subtree_size(&self) -> usize {
69 match self {
70 IndependentFormattingContextContents::Replaced(_, widget) => widget
71 .as_ref()
72 .map_or(0, |widget| widget.borrow().subtree_size()),
73 IndependentFormattingContextContents::Flow(block_formatting_context) => {
74 block_formatting_context.contents.subtree_size()
75 },
76 IndependentFormattingContextContents::Flex(flex_container) => {
77 flex_container.subtree_size()
78 },
79 IndependentFormattingContextContents::Grid(taffy_container) => {
80 taffy_container.subtree_size()
81 },
82 IndependentFormattingContextContents::Table(table) => table.subtree_size(),
83 }
84 }
85}
86
87#[derive(Clone, Copy, Debug, Default, MallocSizeOf)]
90pub(crate) struct Baselines {
91 pub first: Option<Au>,
92 pub last: Option<Au>,
93}
94
95impl Baselines {
96 pub(crate) fn offset(&self, block_offset: Au) -> Baselines {
97 Self {
98 first: self.first.map(|first| first + block_offset),
99 last: self.last.map(|last| last + block_offset),
100 }
101 }
102}
103
104impl IndependentFormattingContext {
105 pub(crate) fn new(
106 base: LayoutBoxBase,
107 contents: IndependentFormattingContextContents,
108 propagated_data: PropagatedBoxTreeData,
109 ) -> Self {
110 base.set_subtree_size(contents.subtree_size() + 1);
111 Self {
112 base,
113 contents,
114 propagated_data,
115 layout_root_layout_inputs: None.into(),
116 }
117 }
118
119 pub(crate) fn rebuild(
120 &mut self,
121 layout_context: &LayoutContext,
122 node_and_style_info: &NodeAndStyleInfo,
123 ) {
124 let contents = Contents::for_element(node_and_style_info.node, layout_context);
125 let display = match Display::from(node_and_style_info.style.get_box().display) {
126 Display::None | Display::Contents => {
127 unreachable!("Should never try to rebuild IndependentFormattingContext with no box")
128 },
129 Display::GeneratingBox(display) => {
130 display.used_value_for_contents(&contents, node_and_style_info)
131 },
132 };
133
134 self.base.base_fragment_info = node_and_style_info.into();
137
138 self.contents = Self::construct_contents(
139 layout_context,
140 node_and_style_info,
141 &mut self.base.base_fragment_info,
142 display.display_inside(),
143 contents,
144 self.propagated_data,
145 );
146
147 self.base.clear_fragments_and_dirty_fragment_cache();
148 *self.base.cached_inline_content_size.borrow_mut() = None;
149 self.base.repair_style(&node_and_style_info.style);
150 }
151
152 pub(crate) fn construct(
153 context: &LayoutContext,
154 node_and_style_info: &NodeAndStyleInfo,
155 display_inside: DisplayInside,
156 contents: Contents,
157 propagated_data: PropagatedBoxTreeData,
158 ) -> Self {
159 let mut base_fragment_info: BaseFragmentInfo = node_and_style_info.into();
160 let contents = Self::construct_contents(
161 context,
162 node_and_style_info,
163 &mut base_fragment_info,
164 display_inside,
165 contents,
166 propagated_data,
167 );
168
169 let base = LayoutBoxBase::new(base_fragment_info, node_and_style_info.style.clone());
170 base.set_subtree_size(contents.subtree_size() + 1);
171
172 Self {
173 base,
174 contents,
175 propagated_data,
176 layout_root_layout_inputs: None.into(),
177 }
178 }
179
180 fn construct_contents(
181 context: &LayoutContext,
182 node_and_style_info: &NodeAndStyleInfo,
183 base_fragment_info: &mut BaseFragmentInfo,
184 display_inside: DisplayInside,
185 contents: Contents,
186 propagated_data: PropagatedBoxTreeData,
187 ) -> IndependentFormattingContextContents {
188 let non_replaced_contents = match contents {
189 Contents::Replaced(contents) => {
190 base_fragment_info.flags.insert(FragmentFlags::IS_REPLACED);
191
192 let node = node_and_style_info.node;
194 let should_make_widget = node.pseudo_element_chain().is_empty() &&
195 node.is_root_of_user_agent_widget() &&
196 !contents.is_content_replacement;
197 let widget = should_make_widget.then(|| {
198 Self::construct_for_widget(context, node_and_style_info, propagated_data)
199 });
200
201 return IndependentFormattingContextContents::Replaced(contents, widget);
202 },
203 Contents::Widget(non_replaced_contents) => {
204 base_fragment_info.flags.insert(FragmentFlags::IS_WIDGET);
205 non_replaced_contents
206 },
207 Contents::NonReplaced(non_replaced_contents) => non_replaced_contents,
208 };
209
210 match display_inside {
211 DisplayInside::Flow { is_list_item } | DisplayInside::FlowRoot { is_list_item } => {
212 IndependentFormattingContextContents::Flow(BlockFormattingContext::construct(
213 context,
214 node_and_style_info,
215 non_replaced_contents,
216 propagated_data,
217 is_list_item,
218 ))
219 },
220 DisplayInside::Grid => {
221 IndependentFormattingContextContents::Grid(TaffyContainer::construct(
222 context,
223 node_and_style_info,
224 non_replaced_contents,
225 propagated_data,
226 base_fragment_info.flags,
227 ))
228 },
229 DisplayInside::Flex => {
230 IndependentFormattingContextContents::Flex(FlexContainer::construct(
231 context,
232 node_and_style_info,
233 non_replaced_contents,
234 propagated_data,
235 ))
236 },
237 DisplayInside::Table => {
238 let table_grid_style = context
239 .style_context
240 .stylist
241 .style_for_anonymous::<ServoDangerousStyleElement>(
242 &context.style_context.guards,
243 &PseudoElement::ServoTableGrid,
244 &node_and_style_info.style,
245 );
246 base_fragment_info.flags.insert(FragmentFlags::DO_NOT_PAINT);
247 IndependentFormattingContextContents::Table(Table::construct(
248 context,
249 node_and_style_info,
250 table_grid_style,
251 non_replaced_contents,
252 propagated_data,
253 ))
254 },
255 }
256 }
257
258 fn construct_for_widget(
259 context: &LayoutContext,
260 node_and_style_info: &NodeAndStyleInfo,
261 propagated_data: PropagatedBoxTreeData,
262 ) -> ArcRefCell<IndependentFormattingContext> {
263 let widget_info = node_and_style_info
264 .with_pseudo_element(context, PseudoElement::ServoAnonymousBox)
265 .expect("Should always be able to construct info for anonymous boxes.");
266 let widget_contents =
268 IndependentFormattingContextContents::Flow(BlockFormattingContext::construct(
269 context,
270 &widget_info,
271 NonReplacedContents::OfElement,
272 propagated_data,
273 false, ));
275
276 let box_slot = widget_info.node.box_slot();
277 let widget_base = LayoutBoxBase::new((&widget_info).into(), widget_info.style);
278 let widget = ArcRefCell::new(Self::new(widget_base, widget_contents, propagated_data));
279 box_slot.set(LayoutBox::Widget(widget.clone()));
280 widget
281 }
282
283 #[inline]
284 pub fn style(&self) -> &Arc<ComputedValues> {
285 &self.base.style
286 }
287
288 #[inline]
289 pub fn base_fragment_info(&self) -> BaseFragmentInfo {
290 self.base.base_fragment_info
291 }
292
293 pub(crate) fn inline_content_sizes(
294 &self,
295 layout_context: &LayoutContext,
296 constraint_space: &ConstraintSpace,
297 ) -> InlineContentSizesResult {
298 self.base
299 .inline_content_sizes(layout_context, constraint_space, &self.contents)
300 }
301
302 pub(crate) fn tentative_block_content_size(
313 &self,
314 preferred_aspect_ratio: Option<AspectRatio>,
315 inline_stretch_size: Option<Au>,
316 ) -> Option<ContentSizes> {
317 let result = self.tentative_block_content_size_with_dependency(
318 preferred_aspect_ratio,
319 inline_stretch_size,
320 );
321 Some(result?.0)
322 }
323
324 pub(crate) fn tentative_block_content_size_with_dependency(
328 &self,
329 preferred_aspect_ratio: Option<AspectRatio>,
330 inline_stretch_size: Option<Au>,
331 ) -> Option<(ContentSizes, bool)> {
332 match &self.contents {
335 IndependentFormattingContextContents::Replaced(contents, _) => {
336 let ratio = preferred_aspect_ratio?;
338 let writing_mode = self.style().writing_mode;
339 let natural_sizes = contents.logical_natural_sizes(writing_mode);
340 let (block_size, depends_on_inline_stretch_size) =
341 match (natural_sizes.block, natural_sizes.inline) {
342 (Some(block_size), None) => (block_size, false),
343 (_, Some(inline_size)) => (
344 ratio.compute_dependent_size(LogicalAxis::Block, inline_size),
345 false,
346 ),
347 (None, None) => (
348 ratio.compute_dependent_size(
349 LogicalAxis::Block,
350 inline_stretch_size.unwrap_or_default(),
351 ),
352 true,
353 ),
354 };
355 Some((block_size.into(), depends_on_inline_stretch_size))
356 },
357 _ => None,
358 }
359 }
360
361 pub(crate) fn outer_inline_content_sizes(
362 &self,
363 layout_context: &LayoutContext,
364 containing_block: &IndefiniteContainingBlock,
365 auto_minimum: &LogicalVec2<Au>,
366 auto_block_size_stretches_to_containing_block: bool,
367 skippable_for_block_percentage_quirk: bool,
368 ) -> InlineContentSizesResult {
369 sizing::outer_inline(
370 &self.base,
371 &self.layout_style(),
372 containing_block,
373 auto_minimum,
374 auto_block_size_stretches_to_containing_block,
375 self.is_replaced(),
376 true, skippable_for_block_percentage_quirk,
378 |padding_border_sums| self.preferred_aspect_ratio(padding_border_sums),
379 |constraint_space| self.inline_content_sizes(layout_context, constraint_space),
380 |preferred_aspect_ratio| {
381 self.tentative_block_content_size(preferred_aspect_ratio, None)
382 },
383 )
384 }
385
386 pub(crate) fn repair_style(
387 &mut self,
388 context: &SharedStyleContext,
389 node: &ServoLayoutNode,
390 new_style: &Arc<ComputedValues>,
391 ) {
392 self.base.repair_style(new_style);
393 match &mut self.contents {
394 IndependentFormattingContextContents::Replaced(replaced, ..) => {
395 *replaced.selected_style.borrow_mut() = node.selected_style(context);
396 },
397 IndependentFormattingContextContents::Flow(block_formatting_context) => {
398 block_formatting_context.repair_style(context, node, new_style);
399 },
400 IndependentFormattingContextContents::Flex(flex_container) => {
401 flex_container.repair_style(new_style)
402 },
403 IndependentFormattingContextContents::Grid(taffy_container) => {
404 taffy_container.repair_style(new_style)
405 },
406 IndependentFormattingContextContents::Table(table) => {
407 table.repair_style(context, new_style, &self.base)
408 },
409 }
410 }
411
412 #[inline]
413 pub(crate) fn is_block_container(&self) -> bool {
414 matches!(self.contents, IndependentFormattingContextContents::Flow(_))
415 }
416
417 #[inline]
418 pub(crate) fn is_replaced(&self) -> bool {
419 matches!(
420 self.contents,
421 IndependentFormattingContextContents::Replaced(_, _)
422 )
423 }
424
425 #[inline]
426 pub(crate) fn is_table(&self) -> bool {
427 matches!(
428 &self.contents,
429 IndependentFormattingContextContents::Table(_)
430 )
431 }
432
433 #[inline]
434 pub(crate) fn is_grid(&self) -> bool {
435 matches!(
436 &self.contents,
437 IndependentFormattingContextContents::Grid(_)
438 )
439 }
440
441 #[servo_tracing::instrument(
442 name = "IndependentFormattingContext::layout_without_caching",
443 skip_all
444 )]
445 fn layout_without_caching(
446 &self,
447 layout_context: &LayoutContext,
448 positioning_context: &mut PositioningContext,
449 containing_block_for_children: &ContainingBlock,
450 containing_block: &ContainingBlock,
451 preferred_aspect_ratio: Option<AspectRatio>,
452 lazy_block_size: &LazySize,
453 ) -> IndependentFormattingContextLayoutResult {
454 match &self.contents {
455 IndependentFormattingContextContents::Replaced(replaced, widget) => {
456 let mut replaced_layout = replaced.layout(
457 layout_context,
458 containing_block_for_children,
459 preferred_aspect_ratio,
460 &self.base,
461 lazy_block_size,
462 );
463 let resolved_block_size =
465 lazy_block_size.resolve(|| replaced_layout.content_block_size);
466 let widget_cb = ContainingBlock {
467 size: ContainingBlockSize {
468 inline: containing_block_for_children.size.inline,
469 block: SizeConstraint::Definite(resolved_block_size),
470 },
471 style: &self.base.style,
472 };
473 if let Some(widget) = widget {
474 let mut widget_layout = widget.borrow().layout(
475 layout_context,
476 positioning_context,
477 &widget_cb,
478 &widget_cb,
479 None,
480 &LazySize::intrinsic(),
481 );
482 replaced_layout
483 .fragments
484 .append(&mut widget_layout.fragments);
485 }
486 replaced_layout
487 },
488 IndependentFormattingContextContents::Flow(bfc) => bfc.layout(
489 layout_context,
490 positioning_context,
491 containing_block_for_children,
492 lazy_block_size,
493 Some(&self.base),
494 ),
495 IndependentFormattingContextContents::Flex(fc) => fc.layout(
496 layout_context,
497 positioning_context,
498 containing_block_for_children,
499 lazy_block_size,
500 ),
501 IndependentFormattingContextContents::Grid(fc) => fc.layout(
502 layout_context,
503 positioning_context,
504 containing_block_for_children,
505 containing_block,
506 ),
507 IndependentFormattingContextContents::Table(table) => table.layout(
508 layout_context,
509 positioning_context,
510 containing_block_for_children,
511 containing_block,
512 ),
513 }
514 }
515
516 pub(crate) fn layout_and_is_cached(
517 &self,
518 layout_context: &LayoutContext,
519 positioning_context: &mut PositioningContext,
520 containing_block_for_children: &ContainingBlock,
521 containing_block: &ContainingBlock,
522 preferred_aspect_ratio: Option<AspectRatio>,
523 lazy_block_size: &LazySize,
524 ) -> (IndependentFormattingContextLayoutResult, bool) {
525 let content_block_size_under_default_constraint = match self
526 .base
527 .cached_independent_formatting_context_layout_if_applicable(
528 positioning_context,
529 containing_block_for_children,
530 ) {
531 Ok(cached_layout_result) => return (cached_layout_result, true),
532 Err(v) => v,
533 };
534
535 #[cfg(feature = "tracing")]
536 tracing::debug!(
537 name: "IndependentFormattingContext::layout cache miss",
538 required = ?containing_block_for_children.size,
539 );
540 let mut child_positioning_context = PositioningContext::default();
541 let result = self.layout_without_caching(
542 layout_context,
543 &mut child_positioning_context,
544 containing_block_for_children,
545 containing_block,
546 preferred_aspect_ratio,
547 lazy_block_size,
548 );
549 self.base.cache_independent_formatting_context_layout(
550 containing_block_for_children,
551 &child_positioning_context,
552 content_block_size_under_default_constraint,
553 &result,
554 );
555 positioning_context.append(child_positioning_context);
556 (result, false)
557 }
558
559 pub(crate) fn layout(
560 &self,
561 layout_context: &LayoutContext,
562 positioning_context: &mut PositioningContext,
563 containing_block_for_children: &ContainingBlock,
564 containing_block: &ContainingBlock,
565 preferred_aspect_ratio: Option<AspectRatio>,
566 lazy_block_size: &LazySize,
567 ) -> IndependentFormattingContextLayoutResult {
568 self.layout_and_is_cached(
569 layout_context,
570 positioning_context,
571 containing_block_for_children,
572 containing_block,
573 preferred_aspect_ratio,
574 lazy_block_size,
575 )
576 .0
577 }
578
579 pub(crate) fn content_block_size_under_default_constraint(
580 &self,
581 layout_context: &LayoutContext,
582 positioning_context: &mut PositioningContext,
583 inline_size: Au,
584 containing_block: &ContainingBlock,
585 preferred_aspect_ratio: Option<AspectRatio>,
586 lazy_block_size: &LazySize,
587 ) -> Au {
588 if let Some(content_block_size) = self
589 .base
590 .cached_content_block_size_under_default_constraint_if_applicable(inline_size)
591 {
592 return content_block_size;
593 }
594 self.layout(
595 layout_context,
596 positioning_context,
597 &ContainingBlock {
598 size: ContainingBlockSize {
599 inline: inline_size,
600 block: SizeConstraint::default(),
601 },
602 style: self.style(),
603 },
604 containing_block,
605 preferred_aspect_ratio,
606 lazy_block_size,
607 )
608 .content_block_size
609 }
610
611 #[inline]
612 pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
613 match &self.contents {
614 IndependentFormattingContextContents::Replaced(replaced, _) => {
615 replaced.layout_style(&self.base)
616 },
617 IndependentFormattingContextContents::Flow(fc) => fc.layout_style(&self.base),
618 IndependentFormattingContextContents::Flex(fc) => fc.layout_style(),
619 IndependentFormattingContextContents::Grid(fc) => fc.layout_style(),
620 IndependentFormattingContextContents::Table(fc) => fc.layout_style(None),
621 }
622 }
623
624 #[inline]
625 pub(crate) fn preferred_aspect_ratio(
626 &self,
627 padding_border_sums: &LogicalVec2<Au>,
628 ) -> Option<AspectRatio> {
629 match &self.contents {
630 IndependentFormattingContextContents::Replaced(replaced, _) => {
631 replaced.preferred_aspect_ratio(self.style(), padding_border_sums)
632 },
633 _ => None,
635 }
636 }
637
638 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
639 match &self.contents {
640 IndependentFormattingContextContents::Replaced(_, widget) => {
641 if let Some(widget) = widget {
642 widget.borrow_mut().base.parent_box.replace(layout_box);
643 }
644 },
645 IndependentFormattingContextContents::Flow(contents) => {
646 contents.attached_to_tree(layout_box)
647 },
648 IndependentFormattingContextContents::Flex(contents) => {
649 contents.attached_to_tree(layout_box)
650 },
651 IndependentFormattingContextContents::Grid(contents) => {
652 contents.attached_to_tree(layout_box)
653 },
654 IndependentFormattingContextContents::Table(contents) => {
655 contents.attached_to_tree(layout_box)
656 },
657 }
658 }
659
660 pub(crate) fn subtree_size(&self) -> usize {
661 self.base.subtree_size()
662 }
663
664 pub(crate) fn set_selection(&self, selected: bool) -> bool {
667 match &self.contents {
668 IndependentFormattingContextContents::Replaced(replaced_contents, ..) => {
669 replaced_contents.set_selection(selected)
670 },
671 _ => false,
672 }
673 }
674
675 pub(crate) fn inline_formatting_context(&self) -> Option<&InlineFormattingContext> {
676 match &self.contents {
677 IndependentFormattingContextContents::Flow(block_formatting_context) => {
678 block_formatting_context.inline_formatting_context()
679 },
680 _ => None,
681 }
682 }
683}
684
685impl ComputeInlineContentSizes for IndependentFormattingContextContents {
686 fn compute_inline_content_sizes(
687 &self,
688 layout_context: &LayoutContext,
689 constraint_space: &ConstraintSpace,
690 ) -> InlineContentSizesResult {
691 match self {
692 Self::Replaced(inner, _) => {
693 inner.compute_inline_content_sizes(layout_context, constraint_space)
694 },
695 Self::Flow(inner) => inner
696 .contents
697 .compute_inline_content_sizes(layout_context, constraint_space),
698 Self::Flex(inner) => {
699 inner.compute_inline_content_sizes(layout_context, constraint_space)
700 },
701 Self::Grid(inner) => {
702 inner.compute_inline_content_sizes(layout_context, constraint_space)
703 },
704 Self::Table(inner) => {
705 inner.compute_inline_content_sizes(layout_context, constraint_space)
706 },
707 }
708 }
709}