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, 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}
45
46#[derive(Debug, MallocSizeOf)]
47pub(crate) enum IndependentFormattingContextContents {
48 Replaced(
50 ReplacedContents,
51 Option<ArcRefCell<IndependentFormattingContext>>,
52 ),
53 Flow(BlockFormattingContext),
54 Flex(FlexContainer),
55 Grid(TaffyContainer),
56 Table(Table),
57 }
59
60#[derive(Clone, Copy, Debug, Default, MallocSizeOf)]
63pub(crate) struct Baselines {
64 pub first: Option<Au>,
65 pub last: Option<Au>,
66}
67
68impl Baselines {
69 pub(crate) fn offset(&self, block_offset: Au) -> Baselines {
70 Self {
71 first: self.first.map(|first| first + block_offset),
72 last: self.last.map(|last| last + block_offset),
73 }
74 }
75}
76
77impl IndependentFormattingContext {
78 pub(crate) fn new(base: LayoutBoxBase, contents: IndependentFormattingContextContents) -> Self {
79 Self { base, contents }
80 }
81
82 pub fn construct(
83 context: &LayoutContext,
84 node_and_style_info: &NodeAndStyleInfo,
85 display_inside: DisplayInside,
86 contents: Contents,
87 propagated_data: PropagatedBoxTreeData,
88 ) -> Self {
89 let mut base_fragment_info: BaseFragmentInfo = node_and_style_info.into();
90
91 let non_replaced_contents = match contents {
92 Contents::Replaced(contents) => {
93 base_fragment_info.flags.insert(FragmentFlags::IS_REPLACED);
94 let widget = Some(node_and_style_info.node)
96 .filter(|node| node.pseudo_element_chain().is_empty())
97 .and_then(|node| node.as_element())
98 .and_then(|element| element.shadow_root())
99 .is_some_and(|shadow_root| shadow_root.is_ua_widget())
100 .then(|| {
101 let widget_info = node_and_style_info
102 .with_pseudo_element(context, PseudoElement::ServoAnonymousBox)
103 .expect("Should always be able to construct info for anonymous boxes.");
104 let widget_contents = IndependentFormattingContextContents::Flow(
106 BlockFormattingContext::construct(
107 context,
108 &widget_info,
109 NonReplacedContents::OfElement,
110 propagated_data,
111 false, ),
113 );
114 let widget_base =
115 LayoutBoxBase::new((&widget_info).into(), widget_info.style);
116 ArcRefCell::new(IndependentFormattingContext::new(
117 widget_base,
118 widget_contents,
119 ))
120 });
121 return Self {
122 base: LayoutBoxBase::new(base_fragment_info, node_and_style_info.style.clone()),
123 contents: IndependentFormattingContextContents::Replaced(contents, widget),
124 };
125 },
126 Contents::Widget(non_replaced_contents) => {
127 base_fragment_info.flags.insert(FragmentFlags::IS_WIDGET);
128 non_replaced_contents
129 },
130 Contents::NonReplaced(non_replaced_contents) => non_replaced_contents,
131 };
132 let contents = match display_inside {
133 DisplayInside::Flow { is_list_item } | DisplayInside::FlowRoot { is_list_item } => {
134 IndependentFormattingContextContents::Flow(BlockFormattingContext::construct(
135 context,
136 node_and_style_info,
137 non_replaced_contents,
138 propagated_data,
139 is_list_item,
140 ))
141 },
142 DisplayInside::Grid => {
143 IndependentFormattingContextContents::Grid(TaffyContainer::construct(
144 context,
145 node_and_style_info,
146 non_replaced_contents,
147 propagated_data,
148 ))
149 },
150 DisplayInside::Flex => {
151 IndependentFormattingContextContents::Flex(FlexContainer::construct(
152 context,
153 node_and_style_info,
154 non_replaced_contents,
155 propagated_data,
156 ))
157 },
158 DisplayInside::Table => {
159 let table_grid_style = context
160 .style_context
161 .stylist
162 .style_for_anonymous::<ServoLayoutElement>(
163 &context.style_context.guards,
164 &PseudoElement::ServoTableGrid,
165 &node_and_style_info.style,
166 );
167 base_fragment_info.flags.insert(FragmentFlags::DO_NOT_PAINT);
168 IndependentFormattingContextContents::Table(Table::construct(
169 context,
170 node_and_style_info,
171 table_grid_style,
172 non_replaced_contents,
173 propagated_data,
174 ))
175 },
176 };
177 Self {
178 base: LayoutBoxBase::new(base_fragment_info, node_and_style_info.style.clone()),
179 contents,
180 }
181 }
182
183 #[inline]
184 pub fn style(&self) -> &Arc<ComputedValues> {
185 &self.base.style
186 }
187
188 #[inline]
189 pub fn base_fragment_info(&self) -> BaseFragmentInfo {
190 self.base.base_fragment_info
191 }
192
193 pub(crate) fn inline_content_sizes(
194 &self,
195 layout_context: &LayoutContext,
196 constraint_space: &ConstraintSpace,
197 ) -> InlineContentSizesResult {
198 self.base
199 .inline_content_sizes(layout_context, constraint_space, &self.contents)
200 }
201
202 pub(crate) fn tentative_block_content_size(
213 &self,
214 preferred_aspect_ratio: Option<AspectRatio>,
215 ) -> Option<ContentSizes> {
216 match &self.contents {
219 IndependentFormattingContextContents::Replaced(contents, _) => {
220 let ratio = preferred_aspect_ratio?;
222 let writing_mode = self.style().writing_mode;
223 let natural_sizes = contents.logical_natural_sizes(writing_mode);
224 let block_size = match (natural_sizes.block, natural_sizes.inline) {
225 (Some(block_size), None) => block_size,
226 _ => {
227 let inline_size = contents.fallback_inline_size(writing_mode);
228 ratio.compute_dependent_size(Direction::Block, inline_size)
229 },
230 };
231 Some(block_size.into())
232 },
233 _ => None,
234 }
235 }
236
237 pub(crate) fn outer_inline_content_sizes(
238 &self,
239 layout_context: &LayoutContext,
240 containing_block: &IndefiniteContainingBlock,
241 auto_minimum: &LogicalVec2<Au>,
242 auto_block_size_stretches_to_containing_block: bool,
243 ) -> InlineContentSizesResult {
244 sizing::outer_inline(
245 &self.base,
246 &self.layout_style(),
247 containing_block,
248 auto_minimum,
249 auto_block_size_stretches_to_containing_block,
250 self.is_replaced(),
251 true, |padding_border_sums| self.preferred_aspect_ratio(padding_border_sums),
253 |constraint_space| self.inline_content_sizes(layout_context, constraint_space),
254 |preferred_aspect_ratio| self.tentative_block_content_size(preferred_aspect_ratio),
255 )
256 }
257
258 pub(crate) fn repair_style(
259 &mut self,
260 context: &SharedStyleContext,
261 node: &ServoThreadSafeLayoutNode,
262 new_style: &Arc<ComputedValues>,
263 ) {
264 self.base.repair_style(new_style);
265 match &mut self.contents {
266 IndependentFormattingContextContents::Replaced(_, widget) => {
267 if let Some(widget) = widget {
268 let node = node
269 .with_pseudo(PseudoElement::ServoAnonymousBox)
270 .expect("Should always be able to construct info for anonymous boxes.");
271 widget.borrow_mut().repair_style(context, &node, new_style);
272 }
273 },
274 IndependentFormattingContextContents::Flow(block_formatting_context) => {
275 block_formatting_context.repair_style(node, new_style);
276 },
277 IndependentFormattingContextContents::Flex(flex_container) => {
278 flex_container.repair_style(new_style)
279 },
280 IndependentFormattingContextContents::Grid(taffy_container) => {
281 taffy_container.repair_style(new_style)
282 },
283 IndependentFormattingContextContents::Table(table) => {
284 table.repair_style(context, new_style)
285 },
286 }
287 }
288
289 #[inline]
290 pub(crate) fn is_block_container(&self) -> bool {
291 matches!(self.contents, IndependentFormattingContextContents::Flow(_))
292 }
293
294 #[inline]
295 pub(crate) fn is_replaced(&self) -> bool {
296 matches!(
297 self.contents,
298 IndependentFormattingContextContents::Replaced(_, _)
299 )
300 }
301
302 #[inline]
303 pub(crate) fn is_table(&self) -> bool {
304 matches!(
305 &self.contents,
306 IndependentFormattingContextContents::Table(_)
307 )
308 }
309
310 fn layout_without_caching(
311 &self,
312 layout_context: &LayoutContext,
313 positioning_context: &mut PositioningContext,
314 containing_block_for_children: &ContainingBlock,
315 containing_block: &ContainingBlock,
316 preferred_aspect_ratio: Option<AspectRatio>,
317 lazy_block_size: &LazySize,
318 ) -> CacheableLayoutResult {
319 match &self.contents {
320 IndependentFormattingContextContents::Replaced(replaced, widget) => {
321 let mut replaced_layout = replaced.layout(
322 layout_context,
323 containing_block_for_children,
324 preferred_aspect_ratio,
325 &self.base,
326 lazy_block_size,
327 );
328 if let Some(widget) = widget {
329 let mut widget_layout = widget.borrow().layout(
330 layout_context,
331 positioning_context,
332 containing_block_for_children,
333 containing_block_for_children,
334 None,
335 &LazySize::intrinsic(),
336 );
337 replaced_layout
338 .fragments
339 .append(&mut widget_layout.fragments);
340 }
341 replaced_layout
342 },
343 IndependentFormattingContextContents::Flow(bfc) => bfc.layout(
344 layout_context,
345 positioning_context,
346 containing_block_for_children,
347 ),
348 IndependentFormattingContextContents::Flex(fc) => fc.layout(
349 layout_context,
350 positioning_context,
351 containing_block_for_children,
352 lazy_block_size,
353 ),
354 IndependentFormattingContextContents::Grid(fc) => fc.layout(
355 layout_context,
356 positioning_context,
357 containing_block_for_children,
358 containing_block,
359 ),
360 IndependentFormattingContextContents::Table(table) => table.layout(
361 layout_context,
362 positioning_context,
363 containing_block_for_children,
364 containing_block,
365 ),
366 }
367 }
368
369 #[servo_tracing::instrument(name = "IndependentFormattingContext::layout", skip_all)]
370 pub(crate) fn layout(
371 &self,
372 layout_context: &LayoutContext,
373 positioning_context: &mut PositioningContext,
374 containing_block_for_children: &ContainingBlock,
375 containing_block: &ContainingBlock,
376 preferred_aspect_ratio: Option<AspectRatio>,
377 lazy_block_size: &LazySize,
378 ) -> CacheableLayoutResult {
379 if let Some(cache) = self.base.cached_layout_result.borrow().as_ref() {
380 let cache = &**cache;
381 if cache.containing_block_for_children_size.inline ==
382 containing_block_for_children.size.inline &&
383 (cache.containing_block_for_children_size.block ==
384 containing_block_for_children.size.block ||
385 !cache.result.depends_on_block_constraints)
386 {
387 positioning_context.append(cache.positioning_context.clone());
388 return cache.result.clone();
389 }
390 #[cfg(feature = "tracing")]
391 tracing::debug!(
392 name: "IndependentFormattingContext::layout cache miss",
393 cached = ?cache.containing_block_for_children_size,
394 required = ?containing_block_for_children.size,
395 );
396 }
397
398 let mut child_positioning_context = PositioningContext::default();
399 let result = self.layout_without_caching(
400 layout_context,
401 &mut child_positioning_context,
402 containing_block_for_children,
403 containing_block,
404 preferred_aspect_ratio,
405 lazy_block_size,
406 );
407
408 *self.base.cached_layout_result.borrow_mut() =
409 Some(Box::new(CacheableLayoutResultAndInputs {
410 result: result.clone(),
411 positioning_context: child_positioning_context.clone(),
412 containing_block_for_children_size: containing_block_for_children.size.clone(),
413 }));
414 positioning_context.append(child_positioning_context);
415
416 result
417 }
418
419 #[inline]
420 pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
421 match &self.contents {
422 IndependentFormattingContextContents::Replaced(replaced, _) => {
423 replaced.layout_style(&self.base)
424 },
425 IndependentFormattingContextContents::Flow(fc) => fc.layout_style(&self.base),
426 IndependentFormattingContextContents::Flex(fc) => fc.layout_style(),
427 IndependentFormattingContextContents::Grid(fc) => fc.layout_style(),
428 IndependentFormattingContextContents::Table(fc) => fc.layout_style(None),
429 }
430 }
431
432 #[inline]
433 pub(crate) fn preferred_aspect_ratio(
434 &self,
435 padding_border_sums: &LogicalVec2<Au>,
436 ) -> Option<AspectRatio> {
437 match &self.contents {
438 IndependentFormattingContextContents::Replaced(replaced, _) => {
439 replaced.preferred_aspect_ratio(self.style(), padding_border_sums)
440 },
441 _ => None,
443 }
444 }
445
446 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
447 match &self.contents {
448 IndependentFormattingContextContents::Replaced(_, widget) => {
449 if let Some(widget) = widget {
450 widget.borrow_mut().base.parent_box.replace(layout_box);
451 }
452 },
453 IndependentFormattingContextContents::Flow(contents) => {
454 contents.attached_to_tree(layout_box)
455 },
456 IndependentFormattingContextContents::Flex(contents) => {
457 contents.attached_to_tree(layout_box)
458 },
459 IndependentFormattingContextContents::Grid(contents) => {
460 contents.attached_to_tree(layout_box)
461 },
462 IndependentFormattingContextContents::Table(contents) => {
463 contents.attached_to_tree(layout_box)
464 },
465 }
466 }
467}
468
469impl ComputeInlineContentSizes for IndependentFormattingContextContents {
470 fn compute_inline_content_sizes(
471 &self,
472 layout_context: &LayoutContext,
473 constraint_space: &ConstraintSpace,
474 ) -> InlineContentSizesResult {
475 match self {
476 Self::Replaced(inner, _) => {
477 inner.compute_inline_content_sizes(layout_context, constraint_space)
478 },
479 Self::Flow(inner) => inner
480 .contents
481 .compute_inline_content_sizes(layout_context, constraint_space),
482 Self::Flex(inner) => {
483 inner.compute_inline_content_sizes(layout_context, constraint_space)
484 },
485 Self::Grid(inner) => {
486 inner.compute_inline_content_sizes(layout_context, constraint_space)
487 },
488 Self::Table(inner) => {
489 inner.compute_inline_content_sizes(layout_context, constraint_space)
490 },
491 }
492 }
493}