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