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(_, widget) => {
380 if let Some(widget) = widget {
381 let node = node
382 .with_pseudo(PseudoElement::ServoAnonymousBox)
383 .expect("Should always be able to construct info for anonymous boxes.");
384 widget.borrow_mut().repair_style(context, &node, new_style);
385 }
386 },
387 IndependentFormattingContextContents::Flow(block_formatting_context) => {
388 block_formatting_context.repair_style(context, node, new_style);
389 },
390 IndependentFormattingContextContents::Flex(flex_container) => {
391 flex_container.repair_style(new_style)
392 },
393 IndependentFormattingContextContents::Grid(taffy_container) => {
394 taffy_container.repair_style(new_style)
395 },
396 IndependentFormattingContextContents::Table(table) => {
397 table.repair_style(context, new_style)
398 },
399 }
400 }
401
402 #[inline]
403 pub(crate) fn is_block_container(&self) -> bool {
404 matches!(self.contents, IndependentFormattingContextContents::Flow(_))
405 }
406
407 #[inline]
408 pub(crate) fn is_replaced(&self) -> bool {
409 matches!(
410 self.contents,
411 IndependentFormattingContextContents::Replaced(_, _)
412 )
413 }
414
415 #[inline]
416 pub(crate) fn is_table(&self) -> bool {
417 matches!(
418 &self.contents,
419 IndependentFormattingContextContents::Table(_)
420 )
421 }
422
423 #[inline]
424 pub(crate) fn is_grid(&self) -> bool {
425 matches!(
426 &self.contents,
427 IndependentFormattingContextContents::Grid(_)
428 )
429 }
430
431 #[servo_tracing::instrument(
432 name = "IndependentFormattingContext::layout_without_caching",
433 skip_all
434 )]
435 fn layout_without_caching(
436 &self,
437 layout_context: &LayoutContext,
438 positioning_context: &mut PositioningContext,
439 containing_block_for_children: &ContainingBlock,
440 containing_block: &ContainingBlock,
441 preferred_aspect_ratio: Option<AspectRatio>,
442 lazy_block_size: &LazySize,
443 ) -> IndependentFormattingContextLayoutResult {
444 match &self.contents {
445 IndependentFormattingContextContents::Replaced(replaced, widget) => {
446 let mut replaced_layout = replaced.layout(
447 layout_context,
448 containing_block_for_children,
449 preferred_aspect_ratio,
450 &self.base,
451 lazy_block_size,
452 );
453 if let Some(widget) = widget {
454 let mut widget_layout = widget.borrow().layout(
455 layout_context,
456 positioning_context,
457 containing_block_for_children,
458 containing_block_for_children,
459 None,
460 &LazySize::intrinsic(),
461 );
462 replaced_layout
463 .fragments
464 .append(&mut widget_layout.fragments);
465 }
466 replaced_layout
467 },
468 IndependentFormattingContextContents::Flow(bfc) => bfc.layout(
469 layout_context,
470 positioning_context,
471 containing_block_for_children,
472 lazy_block_size,
473 Some(&self.base),
474 ),
475 IndependentFormattingContextContents::Flex(fc) => fc.layout(
476 layout_context,
477 positioning_context,
478 containing_block_for_children,
479 lazy_block_size,
480 ),
481 IndependentFormattingContextContents::Grid(fc) => fc.layout(
482 layout_context,
483 positioning_context,
484 containing_block_for_children,
485 containing_block,
486 ),
487 IndependentFormattingContextContents::Table(table) => table.layout(
488 layout_context,
489 positioning_context,
490 containing_block_for_children,
491 containing_block,
492 ),
493 }
494 }
495
496 pub(crate) fn layout_and_is_cached(
497 &self,
498 layout_context: &LayoutContext,
499 positioning_context: &mut PositioningContext,
500 containing_block_for_children: &ContainingBlock,
501 containing_block: &ContainingBlock,
502 preferred_aspect_ratio: Option<AspectRatio>,
503 lazy_block_size: &LazySize,
504 ) -> (IndependentFormattingContextLayoutResult, bool) {
505 if let Some(cached_layout_result) = self
506 .base
507 .cached_independent_formatting_context_layout_if_applicable(
508 positioning_context,
509 containing_block_for_children,
510 )
511 {
512 return (cached_layout_result, true);
513 }
514
515 #[cfg(feature = "tracing")]
516 tracing::debug!(
517 name: "IndependentFormattingContext::layout cache miss",
518 required = ?containing_block_for_children.size,
519 );
520 let mut child_positioning_context = PositioningContext::default();
521 let result = self.layout_without_caching(
522 layout_context,
523 &mut child_positioning_context,
524 containing_block_for_children,
525 containing_block,
526 preferred_aspect_ratio,
527 lazy_block_size,
528 );
529 self.base.cache_independent_formatting_context_layout(
530 containing_block_for_children,
531 &child_positioning_context,
532 &result,
533 );
534 positioning_context.append(child_positioning_context);
535 (result, false)
536 }
537
538 pub(crate) fn layout(
539 &self,
540 layout_context: &LayoutContext,
541 positioning_context: &mut PositioningContext,
542 containing_block_for_children: &ContainingBlock,
543 containing_block: &ContainingBlock,
544 preferred_aspect_ratio: Option<AspectRatio>,
545 lazy_block_size: &LazySize,
546 ) -> IndependentFormattingContextLayoutResult {
547 self.layout_and_is_cached(
548 layout_context,
549 positioning_context,
550 containing_block_for_children,
551 containing_block,
552 preferred_aspect_ratio,
553 lazy_block_size,
554 )
555 .0
556 }
557
558 #[inline]
559 pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
560 match &self.contents {
561 IndependentFormattingContextContents::Replaced(replaced, _) => {
562 replaced.layout_style(&self.base)
563 },
564 IndependentFormattingContextContents::Flow(fc) => fc.layout_style(&self.base),
565 IndependentFormattingContextContents::Flex(fc) => fc.layout_style(),
566 IndependentFormattingContextContents::Grid(fc) => fc.layout_style(),
567 IndependentFormattingContextContents::Table(fc) => fc.layout_style(None),
568 }
569 }
570
571 #[inline]
572 pub(crate) fn preferred_aspect_ratio(
573 &self,
574 padding_border_sums: &LogicalVec2<Au>,
575 ) -> Option<AspectRatio> {
576 match &self.contents {
577 IndependentFormattingContextContents::Replaced(replaced, _) => {
578 replaced.preferred_aspect_ratio(self.style(), padding_border_sums)
579 },
580 _ => None,
582 }
583 }
584
585 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
586 match &self.contents {
587 IndependentFormattingContextContents::Replaced(_, widget) => {
588 if let Some(widget) = widget {
589 widget.borrow_mut().base.parent_box.replace(layout_box);
590 }
591 },
592 IndependentFormattingContextContents::Flow(contents) => {
593 contents.attached_to_tree(layout_box)
594 },
595 IndependentFormattingContextContents::Flex(contents) => {
596 contents.attached_to_tree(layout_box)
597 },
598 IndependentFormattingContextContents::Grid(contents) => {
599 contents.attached_to_tree(layout_box)
600 },
601 IndependentFormattingContextContents::Table(contents) => {
602 contents.attached_to_tree(layout_box)
603 },
604 }
605 }
606
607 pub(crate) fn subtree_size(&self) -> usize {
608 self.base.subtree_size()
609 }
610}
611
612impl ComputeInlineContentSizes for IndependentFormattingContextContents {
613 fn compute_inline_content_sizes(
614 &self,
615 layout_context: &LayoutContext,
616 constraint_space: &ConstraintSpace,
617 ) -> InlineContentSizesResult {
618 match self {
619 Self::Replaced(inner, _) => {
620 inner.compute_inline_content_sizes(layout_context, constraint_space)
621 },
622 Self::Flow(inner) => inner
623 .contents
624 .compute_inline_content_sizes(layout_context, constraint_space),
625 Self::Flex(inner) => {
626 inner.compute_inline_content_sizes(layout_context, constraint_space)
627 },
628 Self::Grid(inner) => {
629 inner.compute_inline_content_sizes(layout_context, constraint_space)
630 },
631 Self::Table(inner) => {
632 inner.compute_inline_content_sizes(layout_context, constraint_space)
633 },
634 }
635 }
636}