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 #[servo_tracing::instrument(
388 name = "IndependentFormattingContext::layout_without_caching",
389 skip_all
390 )]
391 fn layout_without_caching(
392 &self,
393 layout_context: &LayoutContext,
394 positioning_context: &mut PositioningContext,
395 containing_block_for_children: &ContainingBlock,
396 containing_block: &ContainingBlock,
397 preferred_aspect_ratio: Option<AspectRatio>,
398 lazy_block_size: &LazySize,
399 ) -> IndependentFormattingContextLayoutResult {
400 match &self.contents {
401 IndependentFormattingContextContents::Replaced(replaced, widget) => {
402 let mut replaced_layout = replaced.layout(
403 layout_context,
404 containing_block_for_children,
405 preferred_aspect_ratio,
406 &self.base,
407 lazy_block_size,
408 );
409 if let Some(widget) = widget {
410 let mut widget_layout = widget.borrow().layout(
411 layout_context,
412 positioning_context,
413 containing_block_for_children,
414 containing_block_for_children,
415 None,
416 &LazySize::intrinsic(),
417 );
418 replaced_layout
419 .fragments
420 .append(&mut widget_layout.fragments);
421 }
422 replaced_layout
423 },
424 IndependentFormattingContextContents::Flow(bfc) => bfc.layout(
425 layout_context,
426 positioning_context,
427 containing_block_for_children,
428 ),
429 IndependentFormattingContextContents::Flex(fc) => fc.layout(
430 layout_context,
431 positioning_context,
432 containing_block_for_children,
433 lazy_block_size,
434 ),
435 IndependentFormattingContextContents::Grid(fc) => fc.layout(
436 layout_context,
437 positioning_context,
438 containing_block_for_children,
439 containing_block,
440 ),
441 IndependentFormattingContextContents::Table(table) => table.layout(
442 layout_context,
443 positioning_context,
444 containing_block_for_children,
445 containing_block,
446 ),
447 }
448 }
449
450 pub(crate) fn layout_and_is_cached(
451 &self,
452 layout_context: &LayoutContext,
453 positioning_context: &mut PositioningContext,
454 containing_block_for_children: &ContainingBlock,
455 containing_block: &ContainingBlock,
456 preferred_aspect_ratio: Option<AspectRatio>,
457 lazy_block_size: &LazySize,
458 ) -> (IndependentFormattingContextLayoutResult, bool) {
459 if let Some(LayoutResultAndInputs::IndependentFormattingContext(cache)) =
460 self.base.cached_layout_result.borrow().as_ref()
461 {
462 let cache = &**cache;
463 if cache.containing_block_for_children_size.inline ==
464 containing_block_for_children.size.inline &&
465 (cache.containing_block_for_children_size.block ==
466 containing_block_for_children.size.block ||
467 !cache.result.depends_on_block_constraints)
468 {
469 positioning_context.append(cache.positioning_context.clone());
470 return (cache.result.clone(), true);
471 }
472 #[cfg(feature = "tracing")]
473 tracing::debug!(
474 name: "IndependentFormattingContext::layout cache miss",
475 cached = ?cache.containing_block_for_children_size,
476 required = ?containing_block_for_children.size,
477 );
478 }
479
480 let mut child_positioning_context = PositioningContext::default();
481 let result = self.layout_without_caching(
482 layout_context,
483 &mut child_positioning_context,
484 containing_block_for_children,
485 containing_block,
486 preferred_aspect_ratio,
487 lazy_block_size,
488 );
489
490 *self.base.cached_layout_result.borrow_mut() =
491 Some(LayoutResultAndInputs::IndependentFormattingContext(
492 Box::new(IndependentFormattingContextLayoutResultAndInputs {
493 result: result.clone(),
494 positioning_context: child_positioning_context.clone(),
495 containing_block_for_children_size: containing_block_for_children.size.clone(),
496 }),
497 ));
498 positioning_context.append(child_positioning_context);
499
500 (result, false)
501 }
502
503 pub(crate) fn layout(
504 &self,
505 layout_context: &LayoutContext,
506 positioning_context: &mut PositioningContext,
507 containing_block_for_children: &ContainingBlock,
508 containing_block: &ContainingBlock,
509 preferred_aspect_ratio: Option<AspectRatio>,
510 lazy_block_size: &LazySize,
511 ) -> IndependentFormattingContextLayoutResult {
512 self.layout_and_is_cached(
513 layout_context,
514 positioning_context,
515 containing_block_for_children,
516 containing_block,
517 preferred_aspect_ratio,
518 lazy_block_size,
519 )
520 .0
521 }
522
523 #[inline]
524 pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
525 match &self.contents {
526 IndependentFormattingContextContents::Replaced(replaced, _) => {
527 replaced.layout_style(&self.base)
528 },
529 IndependentFormattingContextContents::Flow(fc) => fc.layout_style(&self.base),
530 IndependentFormattingContextContents::Flex(fc) => fc.layout_style(),
531 IndependentFormattingContextContents::Grid(fc) => fc.layout_style(),
532 IndependentFormattingContextContents::Table(fc) => fc.layout_style(None),
533 }
534 }
535
536 #[inline]
537 pub(crate) fn preferred_aspect_ratio(
538 &self,
539 padding_border_sums: &LogicalVec2<Au>,
540 ) -> Option<AspectRatio> {
541 match &self.contents {
542 IndependentFormattingContextContents::Replaced(replaced, _) => {
543 replaced.preferred_aspect_ratio(self.style(), padding_border_sums)
544 },
545 _ => None,
547 }
548 }
549
550 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
551 match &self.contents {
552 IndependentFormattingContextContents::Replaced(_, widget) => {
553 if let Some(widget) = widget {
554 widget.borrow_mut().base.parent_box.replace(layout_box);
555 }
556 },
557 IndependentFormattingContextContents::Flow(contents) => {
558 contents.attached_to_tree(layout_box)
559 },
560 IndependentFormattingContextContents::Flex(contents) => {
561 contents.attached_to_tree(layout_box)
562 },
563 IndependentFormattingContextContents::Grid(contents) => {
564 contents.attached_to_tree(layout_box)
565 },
566 IndependentFormattingContextContents::Table(contents) => {
567 contents.attached_to_tree(layout_box)
568 },
569 }
570 }
571}
572
573impl ComputeInlineContentSizes for IndependentFormattingContextContents {
574 fn compute_inline_content_sizes(
575 &self,
576 layout_context: &LayoutContext,
577 constraint_space: &ConstraintSpace,
578 ) -> InlineContentSizesResult {
579 match self {
580 Self::Replaced(inner, _) => {
581 inner.compute_inline_content_sizes(layout_context, constraint_space)
582 },
583 Self::Flow(inner) => inner
584 .contents
585 .compute_inline_content_sizes(layout_context, constraint_space),
586 Self::Flex(inner) => {
587 inner.compute_inline_content_sizes(layout_context, constraint_space)
588 },
589 Self::Grid(inner) => {
590 inner.compute_inline_content_sizes(layout_context, constraint_space)
591 },
592 Self::Table(inner) => {
593 inner.compute_inline_content_sizes(layout_context, constraint_space)
594 },
595 }
596 }
597}