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) => display.used_value_for_contents(&contents),
128 };
129
130 self.base.base_fragment_info = node_and_style_info.into();
133
134 self.contents = Self::construct_contents(
135 layout_context,
136 node_and_style_info,
137 &mut self.base.base_fragment_info,
138 display.display_inside(),
139 contents,
140 self.propagated_data,
141 );
142
143 self.base.clear_fragments_and_dirty_fragment_cache();
144 *self.base.cached_inline_content_size.borrow_mut() = None;
145 self.base.repair_style(&node_and_style_info.style);
146 }
147
148 pub(crate) fn construct(
149 context: &LayoutContext,
150 node_and_style_info: &NodeAndStyleInfo,
151 display_inside: DisplayInside,
152 contents: Contents,
153 propagated_data: PropagatedBoxTreeData,
154 ) -> Self {
155 let mut base_fragment_info: BaseFragmentInfo = node_and_style_info.into();
156 let contents = Self::construct_contents(
157 context,
158 node_and_style_info,
159 &mut base_fragment_info,
160 display_inside,
161 contents,
162 propagated_data,
163 );
164
165 let base = LayoutBoxBase::new(base_fragment_info, node_and_style_info.style.clone());
166 base.set_subtree_size(contents.subtree_size() + 1);
167
168 Self {
169 base,
170 contents,
171 propagated_data,
172 layout_root_layout_inputs: None.into(),
173 }
174 }
175
176 fn construct_contents(
177 context: &LayoutContext,
178 node_and_style_info: &NodeAndStyleInfo,
179 base_fragment_info: &mut BaseFragmentInfo,
180 display_inside: DisplayInside,
181 contents: Contents,
182 propagated_data: PropagatedBoxTreeData,
183 ) -> IndependentFormattingContextContents {
184 let non_replaced_contents = match contents {
185 Contents::Replaced(contents) => {
186 base_fragment_info.flags.insert(FragmentFlags::IS_REPLACED);
187
188 let node = node_and_style_info.node;
190 let should_make_widget = node.pseudo_element_chain().is_empty() &&
191 node.is_root_of_user_agent_widget() &&
192 !contents.is_content_replacement;
193 let widget = should_make_widget.then(|| {
194 let widget_info = node_and_style_info
195 .with_pseudo_element(context, PseudoElement::ServoAnonymousBox)
196 .expect("Should always be able to construct info for anonymous boxes.");
197 let widget_contents = IndependentFormattingContextContents::Flow(
199 BlockFormattingContext::construct(
200 context,
201 &widget_info,
202 NonReplacedContents::OfElement,
203 propagated_data,
204 false, ),
206 );
207 let widget_base = LayoutBoxBase::new((&widget_info).into(), widget_info.style);
208 ArcRefCell::new(IndependentFormattingContext::new(
209 widget_base,
210 widget_contents,
211 propagated_data,
212 ))
213 });
214
215 return IndependentFormattingContextContents::Replaced(contents, widget);
216 },
217 Contents::Widget(non_replaced_contents) => {
218 base_fragment_info.flags.insert(FragmentFlags::IS_WIDGET);
219 non_replaced_contents
220 },
221 Contents::NonReplaced(non_replaced_contents) => non_replaced_contents,
222 };
223
224 match display_inside {
225 DisplayInside::Flow { is_list_item } | DisplayInside::FlowRoot { is_list_item } => {
226 IndependentFormattingContextContents::Flow(BlockFormattingContext::construct(
227 context,
228 node_and_style_info,
229 non_replaced_contents,
230 propagated_data,
231 is_list_item,
232 ))
233 },
234 DisplayInside::Grid => {
235 IndependentFormattingContextContents::Grid(TaffyContainer::construct(
236 context,
237 node_and_style_info,
238 non_replaced_contents,
239 propagated_data,
240 ))
241 },
242 DisplayInside::Flex => {
243 IndependentFormattingContextContents::Flex(FlexContainer::construct(
244 context,
245 node_and_style_info,
246 non_replaced_contents,
247 propagated_data,
248 ))
249 },
250 DisplayInside::Table => {
251 let table_grid_style = context
252 .style_context
253 .stylist
254 .style_for_anonymous::<ServoDangerousStyleElement>(
255 &context.style_context.guards,
256 &PseudoElement::ServoTableGrid,
257 &node_and_style_info.style,
258 );
259 base_fragment_info.flags.insert(FragmentFlags::DO_NOT_PAINT);
260 IndependentFormattingContextContents::Table(Table::construct(
261 context,
262 node_and_style_info,
263 table_grid_style,
264 non_replaced_contents,
265 propagated_data,
266 ))
267 },
268 }
269 }
270
271 #[inline]
272 pub fn style(&self) -> &Arc<ComputedValues> {
273 &self.base.style
274 }
275
276 #[inline]
277 pub fn base_fragment_info(&self) -> BaseFragmentInfo {
278 self.base.base_fragment_info
279 }
280
281 pub(crate) fn inline_content_sizes(
282 &self,
283 layout_context: &LayoutContext,
284 constraint_space: &ConstraintSpace,
285 ) -> InlineContentSizesResult {
286 self.base
287 .inline_content_sizes(layout_context, constraint_space, &self.contents)
288 }
289
290 pub(crate) fn tentative_block_content_size(
301 &self,
302 preferred_aspect_ratio: Option<AspectRatio>,
303 inline_stretch_size: Au,
304 ) -> Option<ContentSizes> {
305 let result = self.tentative_block_content_size_with_dependency(
306 preferred_aspect_ratio,
307 inline_stretch_size,
308 );
309 Some(result?.0)
310 }
311
312 pub(crate) fn tentative_block_content_size_with_dependency(
316 &self,
317 preferred_aspect_ratio: Option<AspectRatio>,
318 inline_stretch_size: Au,
319 ) -> Option<(ContentSizes, bool)> {
320 match &self.contents {
323 IndependentFormattingContextContents::Replaced(contents, _) => {
324 let ratio = preferred_aspect_ratio?;
326 let writing_mode = self.style().writing_mode;
327 let natural_sizes = contents.logical_natural_sizes(writing_mode);
328 let (block_size, depends_on_inline_stretch_size) =
329 match (natural_sizes.block, natural_sizes.inline) {
330 (Some(block_size), None) => (block_size, false),
331 (_, Some(inline_size)) => (
332 ratio.compute_dependent_size(Direction::Block, inline_size),
333 false,
334 ),
335 (None, None) => (
336 ratio.compute_dependent_size(Direction::Block, inline_stretch_size),
337 true,
338 ),
339 };
340 Some((block_size.into(), depends_on_inline_stretch_size))
341 },
342 _ => None,
343 }
344 }
345
346 pub(crate) fn outer_inline_content_sizes(
347 &self,
348 layout_context: &LayoutContext,
349 containing_block: &IndefiniteContainingBlock,
350 auto_minimum: &LogicalVec2<Au>,
351 auto_block_size_stretches_to_containing_block: bool,
352 ) -> InlineContentSizesResult {
353 sizing::outer_inline(
354 &self.base,
355 &self.layout_style(),
356 containing_block,
357 auto_minimum,
358 auto_block_size_stretches_to_containing_block,
359 self.is_replaced(),
360 true, |padding_border_sums| self.preferred_aspect_ratio(padding_border_sums),
362 |constraint_space| self.inline_content_sizes(layout_context, constraint_space),
363 |preferred_aspect_ratio| {
364 self.tentative_block_content_size(preferred_aspect_ratio, Au(0))
365 },
366 )
367 }
368
369 pub(crate) fn repair_style(
370 &mut self,
371 context: &SharedStyleContext,
372 node: &ServoLayoutNode,
373 new_style: &Arc<ComputedValues>,
374 ) {
375 self.base.repair_style(new_style);
376 match &mut self.contents {
377 IndependentFormattingContextContents::Replaced(_, widget) => {
378 if let Some(widget) = widget {
379 let node = node
380 .with_pseudo(PseudoElement::ServoAnonymousBox)
381 .expect("Should always be able to construct info for anonymous boxes.");
382 widget.borrow_mut().repair_style(context, &node, new_style);
383 }
384 },
385 IndependentFormattingContextContents::Flow(block_formatting_context) => {
386 block_formatting_context.repair_style(context, node, new_style);
387 },
388 IndependentFormattingContextContents::Flex(flex_container) => {
389 flex_container.repair_style(new_style)
390 },
391 IndependentFormattingContextContents::Grid(taffy_container) => {
392 taffy_container.repair_style(new_style)
393 },
394 IndependentFormattingContextContents::Table(table) => {
395 table.repair_style(context, new_style)
396 },
397 }
398 }
399
400 #[inline]
401 pub(crate) fn is_block_container(&self) -> bool {
402 matches!(self.contents, IndependentFormattingContextContents::Flow(_))
403 }
404
405 #[inline]
406 pub(crate) fn is_replaced(&self) -> bool {
407 matches!(
408 self.contents,
409 IndependentFormattingContextContents::Replaced(_, _)
410 )
411 }
412
413 #[inline]
414 pub(crate) fn is_table(&self) -> bool {
415 matches!(
416 &self.contents,
417 IndependentFormattingContextContents::Table(_)
418 )
419 }
420
421 #[inline]
422 pub(crate) fn is_grid(&self) -> bool {
423 matches!(
424 &self.contents,
425 IndependentFormattingContextContents::Grid(_)
426 )
427 }
428
429 #[servo_tracing::instrument(
430 name = "IndependentFormattingContext::layout_without_caching",
431 skip_all
432 )]
433 fn layout_without_caching(
434 &self,
435 layout_context: &LayoutContext,
436 positioning_context: &mut PositioningContext,
437 containing_block_for_children: &ContainingBlock,
438 containing_block: &ContainingBlock,
439 preferred_aspect_ratio: Option<AspectRatio>,
440 lazy_block_size: &LazySize,
441 ) -> IndependentFormattingContextLayoutResult {
442 match &self.contents {
443 IndependentFormattingContextContents::Replaced(replaced, widget) => {
444 let mut replaced_layout = replaced.layout(
445 layout_context,
446 containing_block_for_children,
447 preferred_aspect_ratio,
448 &self.base,
449 lazy_block_size,
450 );
451 if let Some(widget) = widget {
452 let mut widget_layout = widget.borrow().layout(
453 layout_context,
454 positioning_context,
455 containing_block_for_children,
456 containing_block_for_children,
457 None,
458 &LazySize::intrinsic(),
459 );
460 replaced_layout
461 .fragments
462 .append(&mut widget_layout.fragments);
463 }
464 replaced_layout
465 },
466 IndependentFormattingContextContents::Flow(bfc) => bfc.layout(
467 layout_context,
468 positioning_context,
469 containing_block_for_children,
470 ),
471 IndependentFormattingContextContents::Flex(fc) => fc.layout(
472 layout_context,
473 positioning_context,
474 containing_block_for_children,
475 lazy_block_size,
476 ),
477 IndependentFormattingContextContents::Grid(fc) => fc.layout(
478 layout_context,
479 positioning_context,
480 containing_block_for_children,
481 containing_block,
482 ),
483 IndependentFormattingContextContents::Table(table) => table.layout(
484 layout_context,
485 positioning_context,
486 containing_block_for_children,
487 containing_block,
488 ),
489 }
490 }
491
492 pub(crate) fn layout_and_is_cached(
493 &self,
494 layout_context: &LayoutContext,
495 positioning_context: &mut PositioningContext,
496 containing_block_for_children: &ContainingBlock,
497 containing_block: &ContainingBlock,
498 preferred_aspect_ratio: Option<AspectRatio>,
499 lazy_block_size: &LazySize,
500 ) -> (IndependentFormattingContextLayoutResult, bool) {
501 if let Some(cached_layout_result) = self
502 .base
503 .cached_independent_formatting_context_layout_if_applicable(
504 positioning_context,
505 containing_block_for_children,
506 )
507 {
508 return (cached_layout_result, true);
509 }
510
511 #[cfg(feature = "tracing")]
512 tracing::debug!(
513 name: "IndependentFormattingContext::layout cache miss",
514 required = ?containing_block_for_children.size,
515 );
516 let mut child_positioning_context = PositioningContext::default();
517 let result = self.layout_without_caching(
518 layout_context,
519 &mut child_positioning_context,
520 containing_block_for_children,
521 containing_block,
522 preferred_aspect_ratio,
523 lazy_block_size,
524 );
525 self.base.cache_independent_formatting_context_layout(
526 containing_block_for_children,
527 &child_positioning_context,
528 &result,
529 );
530 positioning_context.append(child_positioning_context);
531 (result, false)
532 }
533
534 pub(crate) fn layout(
535 &self,
536 layout_context: &LayoutContext,
537 positioning_context: &mut PositioningContext,
538 containing_block_for_children: &ContainingBlock,
539 containing_block: &ContainingBlock,
540 preferred_aspect_ratio: Option<AspectRatio>,
541 lazy_block_size: &LazySize,
542 ) -> IndependentFormattingContextLayoutResult {
543 self.layout_and_is_cached(
544 layout_context,
545 positioning_context,
546 containing_block_for_children,
547 containing_block,
548 preferred_aspect_ratio,
549 lazy_block_size,
550 )
551 .0
552 }
553
554 #[inline]
555 pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
556 match &self.contents {
557 IndependentFormattingContextContents::Replaced(replaced, _) => {
558 replaced.layout_style(&self.base)
559 },
560 IndependentFormattingContextContents::Flow(fc) => fc.layout_style(&self.base),
561 IndependentFormattingContextContents::Flex(fc) => fc.layout_style(),
562 IndependentFormattingContextContents::Grid(fc) => fc.layout_style(),
563 IndependentFormattingContextContents::Table(fc) => fc.layout_style(None),
564 }
565 }
566
567 #[inline]
568 pub(crate) fn preferred_aspect_ratio(
569 &self,
570 padding_border_sums: &LogicalVec2<Au>,
571 ) -> Option<AspectRatio> {
572 match &self.contents {
573 IndependentFormattingContextContents::Replaced(replaced, _) => {
574 replaced.preferred_aspect_ratio(self.style(), padding_border_sums)
575 },
576 _ => None,
578 }
579 }
580
581 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
582 match &self.contents {
583 IndependentFormattingContextContents::Replaced(_, widget) => {
584 if let Some(widget) = widget {
585 widget.borrow_mut().base.parent_box.replace(layout_box);
586 }
587 },
588 IndependentFormattingContextContents::Flow(contents) => {
589 contents.attached_to_tree(layout_box)
590 },
591 IndependentFormattingContextContents::Flex(contents) => {
592 contents.attached_to_tree(layout_box)
593 },
594 IndependentFormattingContextContents::Grid(contents) => {
595 contents.attached_to_tree(layout_box)
596 },
597 IndependentFormattingContextContents::Table(contents) => {
598 contents.attached_to_tree(layout_box)
599 },
600 }
601 }
602
603 pub(crate) fn subtree_size(&self) -> usize {
604 self.base.subtree_size()
605 }
606}
607
608impl ComputeInlineContentSizes for IndependentFormattingContextContents {
609 fn compute_inline_content_sizes(
610 &self,
611 layout_context: &LayoutContext,
612 constraint_space: &ConstraintSpace,
613 ) -> InlineContentSizesResult {
614 match self {
615 Self::Replaced(inner, _) => {
616 inner.compute_inline_content_sizes(layout_context, constraint_space)
617 },
618 Self::Flow(inner) => inner
619 .contents
620 .compute_inline_content_sizes(layout_context, constraint_space),
621 Self::Flex(inner) => {
622 inner.compute_inline_content_sizes(layout_context, constraint_space)
623 },
624 Self::Grid(inner) => {
625 inner.compute_inline_content_sizes(layout_context, constraint_space)
626 },
627 Self::Table(inner) => {
628 inner.compute_inline_content_sizes(layout_context, constraint_space)
629 },
630 }
631 }
632}