1use app_units::Au;
6use layout_api::wrapper_traits::ThreadSafeLayoutNode;
7use malloc_size_of_derive::MallocSizeOf;
8use script::layout_dom::{ServoLayoutElement, ServoThreadSafeLayoutNode};
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 CacheableLayoutResult, CacheableLayoutResultAndInputs, LayoutBoxBase,
23};
24use crate::positioned::PositioningContext;
25use crate::replaced::ReplacedContents;
26use crate::sizing::{
27 self, ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, LazySize,
28};
29use crate::style_ext::{AspectRatio, Display, DisplayInside, LayoutStyle};
30use crate::table::Table;
31use crate::taffy::TaffyContainer;
32use crate::{
33 ArcRefCell, ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, LogicalVec2,
34 PropagatedBoxTreeData,
35};
36
37#[derive(Debug, MallocSizeOf)]
39pub(crate) struct IndependentFormattingContext {
40 pub base: LayoutBoxBase,
41 contents: IndependentFormattingContextContents,
44 pub propagated_data: PropagatedBoxTreeData,
47}
48
49#[derive(Debug, MallocSizeOf)]
50pub(crate) enum IndependentFormattingContextContents {
51 Replaced(
53 ReplacedContents,
54 Option<ArcRefCell<IndependentFormattingContext>>,
55 ),
56 Flow(BlockFormattingContext),
57 Flex(FlexContainer),
58 Grid(TaffyContainer),
59 Table(Table),
60 }
62
63#[derive(Clone, Copy, Debug, Default, MallocSizeOf)]
66pub(crate) struct Baselines {
67 pub first: Option<Au>,
68 pub last: Option<Au>,
69}
70
71impl Baselines {
72 pub(crate) fn offset(&self, block_offset: Au) -> Baselines {
73 Self {
74 first: self.first.map(|first| first + block_offset),
75 last: self.last.map(|last| last + block_offset),
76 }
77 }
78}
79
80impl IndependentFormattingContext {
81 pub(crate) fn new(
82 base: LayoutBoxBase,
83 contents: IndependentFormattingContextContents,
84 propagated_data: PropagatedBoxTreeData,
85 ) -> Self {
86 Self {
87 base,
88 contents,
89 propagated_data,
90 }
91 }
92
93 pub(crate) fn rebuild(
94 &mut self,
95 layout_context: &LayoutContext,
96 node_and_style_info: &NodeAndStyleInfo,
97 ) {
98 let contents = Contents::for_element(node_and_style_info.node, layout_context);
99 let display = match Display::from(node_and_style_info.style.get_box().display) {
100 Display::None | Display::Contents => {
101 unreachable!("Should never try to rebuild IndependentFormattingContext with no box")
102 },
103 Display::GeneratingBox(display) => display.used_value_for_contents(&contents),
104 };
105 self.contents = Self::construct_contents(
106 layout_context,
107 node_and_style_info,
108 &mut self.base.base_fragment_info,
109 display.display_inside(),
110 contents,
111 self.propagated_data,
112 );
113
114 self.base.clear_fragments_and_fragment_cache();
115 *self.base.cached_inline_content_size.borrow_mut() = None;
116 self.base.repair_style(&node_and_style_info.style);
117 }
118
119 pub(crate) fn construct(
120 context: &LayoutContext,
121 node_and_style_info: &NodeAndStyleInfo,
122 display_inside: DisplayInside,
123 contents: Contents,
124 propagated_data: PropagatedBoxTreeData,
125 ) -> Self {
126 let mut base_fragment_info: BaseFragmentInfo = node_and_style_info.into();
127 let contents = Self::construct_contents(
128 context,
129 node_and_style_info,
130 &mut base_fragment_info,
131 display_inside,
132 contents,
133 propagated_data,
134 );
135 Self {
136 base: LayoutBoxBase::new(base_fragment_info, node_and_style_info.style.clone()),
137 contents,
138 propagated_data,
139 }
140 }
141
142 fn construct_contents(
143 context: &LayoutContext,
144 node_and_style_info: &NodeAndStyleInfo,
145 base_fragment_info: &mut BaseFragmentInfo,
146 display_inside: DisplayInside,
147 contents: Contents,
148 propagated_data: PropagatedBoxTreeData,
149 ) -> IndependentFormattingContextContents {
150 let non_replaced_contents = match contents {
151 Contents::Replaced(contents) => {
152 base_fragment_info.flags.insert(FragmentFlags::IS_REPLACED);
153 let widget = Some(node_and_style_info.node)
155 .filter(|node| node.pseudo_element_chain().is_empty())
156 .and_then(|node| node.as_element())
157 .and_then(|element| element.shadow_root())
158 .is_some_and(|shadow_root| shadow_root.is_ua_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 =
174 LayoutBoxBase::new((&widget_info).into(), widget_info.style);
175 ArcRefCell::new(IndependentFormattingContext::new(
176 widget_base,
177 widget_contents,
178 propagated_data,
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::<ServoLayoutElement>(
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: &ServoThreadSafeLayoutNode,
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 fn layout_without_caching(
388 &self,
389 layout_context: &LayoutContext,
390 positioning_context: &mut PositioningContext,
391 containing_block_for_children: &ContainingBlock,
392 containing_block: &ContainingBlock,
393 preferred_aspect_ratio: Option<AspectRatio>,
394 lazy_block_size: &LazySize,
395 ) -> CacheableLayoutResult {
396 match &self.contents {
397 IndependentFormattingContextContents::Replaced(replaced, widget) => {
398 let mut replaced_layout = replaced.layout(
399 layout_context,
400 containing_block_for_children,
401 preferred_aspect_ratio,
402 &self.base,
403 lazy_block_size,
404 );
405 if let Some(widget) = widget {
406 let mut widget_layout = widget.borrow().layout(
407 layout_context,
408 positioning_context,
409 containing_block_for_children,
410 containing_block_for_children,
411 None,
412 &LazySize::intrinsic(),
413 );
414 replaced_layout
415 .fragments
416 .append(&mut widget_layout.fragments);
417 }
418 replaced_layout
419 },
420 IndependentFormattingContextContents::Flow(bfc) => bfc.layout(
421 layout_context,
422 positioning_context,
423 containing_block_for_children,
424 ),
425 IndependentFormattingContextContents::Flex(fc) => fc.layout(
426 layout_context,
427 positioning_context,
428 containing_block_for_children,
429 lazy_block_size,
430 ),
431 IndependentFormattingContextContents::Grid(fc) => fc.layout(
432 layout_context,
433 positioning_context,
434 containing_block_for_children,
435 containing_block,
436 ),
437 IndependentFormattingContextContents::Table(table) => table.layout(
438 layout_context,
439 positioning_context,
440 containing_block_for_children,
441 containing_block,
442 ),
443 }
444 }
445
446 #[servo_tracing::instrument(name = "IndependentFormattingContext::layout", skip_all)]
447 pub(crate) fn layout(
448 &self,
449 layout_context: &LayoutContext,
450 positioning_context: &mut PositioningContext,
451 containing_block_for_children: &ContainingBlock,
452 containing_block: &ContainingBlock,
453 preferred_aspect_ratio: Option<AspectRatio>,
454 lazy_block_size: &LazySize,
455 ) -> CacheableLayoutResult {
456 if let Some(cache) = self.base.cached_layout_result.borrow().as_ref() {
457 let cache = &**cache;
458 if cache.containing_block_for_children_size.inline ==
459 containing_block_for_children.size.inline &&
460 (cache.containing_block_for_children_size.block ==
461 containing_block_for_children.size.block ||
462 !cache.result.depends_on_block_constraints)
463 {
464 positioning_context.append(cache.positioning_context.clone());
465 return cache.result.clone();
466 }
467 #[cfg(feature = "tracing")]
468 tracing::debug!(
469 name: "IndependentFormattingContext::layout cache miss",
470 cached = ?cache.containing_block_for_children_size,
471 required = ?containing_block_for_children.size,
472 );
473 }
474
475 let mut child_positioning_context = PositioningContext::default();
476 let result = self.layout_without_caching(
477 layout_context,
478 &mut child_positioning_context,
479 containing_block_for_children,
480 containing_block,
481 preferred_aspect_ratio,
482 lazy_block_size,
483 );
484
485 *self.base.cached_layout_result.borrow_mut() =
486 Some(Box::new(CacheableLayoutResultAndInputs {
487 result: result.clone(),
488 positioning_context: child_positioning_context.clone(),
489 containing_block_for_children_size: containing_block_for_children.size.clone(),
490 }));
491 positioning_context.append(child_positioning_context);
492
493 result
494 }
495
496 #[inline]
497 pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
498 match &self.contents {
499 IndependentFormattingContextContents::Replaced(replaced, _) => {
500 replaced.layout_style(&self.base)
501 },
502 IndependentFormattingContextContents::Flow(fc) => fc.layout_style(&self.base),
503 IndependentFormattingContextContents::Flex(fc) => fc.layout_style(),
504 IndependentFormattingContextContents::Grid(fc) => fc.layout_style(),
505 IndependentFormattingContextContents::Table(fc) => fc.layout_style(None),
506 }
507 }
508
509 #[inline]
510 pub(crate) fn preferred_aspect_ratio(
511 &self,
512 padding_border_sums: &LogicalVec2<Au>,
513 ) -> Option<AspectRatio> {
514 match &self.contents {
515 IndependentFormattingContextContents::Replaced(replaced, _) => {
516 replaced.preferred_aspect_ratio(self.style(), padding_border_sums)
517 },
518 _ => None,
520 }
521 }
522
523 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
524 match &self.contents {
525 IndependentFormattingContextContents::Replaced(_, widget) => {
526 if let Some(widget) = widget {
527 widget.borrow_mut().base.parent_box.replace(layout_box);
528 }
529 },
530 IndependentFormattingContextContents::Flow(contents) => {
531 contents.attached_to_tree(layout_box)
532 },
533 IndependentFormattingContextContents::Flex(contents) => {
534 contents.attached_to_tree(layout_box)
535 },
536 IndependentFormattingContextContents::Grid(contents) => {
537 contents.attached_to_tree(layout_box)
538 },
539 IndependentFormattingContextContents::Table(contents) => {
540 contents.attached_to_tree(layout_box)
541 },
542 }
543 }
544}
545
546impl ComputeInlineContentSizes for IndependentFormattingContextContents {
547 fn compute_inline_content_sizes(
548 &self,
549 layout_context: &LayoutContext,
550 constraint_space: &ConstraintSpace,
551 ) -> InlineContentSizesResult {
552 match self {
553 Self::Replaced(inner, _) => {
554 inner.compute_inline_content_sizes(layout_context, constraint_space)
555 },
556 Self::Flow(inner) => inner
557 .contents
558 .compute_inline_content_sizes(layout_context, constraint_space),
559 Self::Flex(inner) => {
560 inner.compute_inline_content_sizes(layout_context, constraint_space)
561 },
562 Self::Grid(inner) => {
563 inner.compute_inline_content_sizes(layout_context, constraint_space)
564 },
565 Self::Table(inner) => {
566 inner.compute_inline_content_sizes(layout_context, constraint_space)
567 },
568 }
569 }
570}