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