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