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