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 = node_and_style_info
96 .node
97 .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 inline_size = contents.fallback_inline_size(writing_mode);
224 let block_size = ratio.compute_dependent_size(Direction::Block, inline_size);
225 Some(block_size.into())
226 },
227 _ => None,
228 }
229 }
230
231 pub(crate) fn outer_inline_content_sizes(
232 &self,
233 layout_context: &LayoutContext,
234 containing_block: &IndefiniteContainingBlock,
235 auto_minimum: &LogicalVec2<Au>,
236 auto_block_size_stretches_to_containing_block: bool,
237 ) -> InlineContentSizesResult {
238 sizing::outer_inline(
239 &self.base,
240 &self.layout_style(),
241 containing_block,
242 auto_minimum,
243 auto_block_size_stretches_to_containing_block,
244 self.is_replaced(),
245 true, |padding_border_sums| self.preferred_aspect_ratio(padding_border_sums),
247 |constraint_space| self.inline_content_sizes(layout_context, constraint_space),
248 |preferred_aspect_ratio| self.tentative_block_content_size(preferred_aspect_ratio),
249 )
250 }
251
252 pub(crate) fn repair_style(
253 &mut self,
254 context: &SharedStyleContext,
255 node: &ServoThreadSafeLayoutNode,
256 new_style: &Arc<ComputedValues>,
257 ) {
258 self.base.repair_style(new_style);
259 match &mut self.contents {
260 IndependentFormattingContextContents::Replaced(_, widget) => {
261 if let Some(widget) = widget {
262 let node = node
263 .with_pseudo(PseudoElement::ServoAnonymousBox)
264 .expect("Should always be able to construct info for anonymous boxes.");
265 widget.borrow_mut().repair_style(context, &node, new_style);
266 }
267 },
268 IndependentFormattingContextContents::Flow(block_formatting_context) => {
269 block_formatting_context.repair_style(node, new_style);
270 },
271 IndependentFormattingContextContents::Flex(flex_container) => {
272 flex_container.repair_style(new_style)
273 },
274 IndependentFormattingContextContents::Grid(taffy_container) => {
275 taffy_container.repair_style(new_style)
276 },
277 IndependentFormattingContextContents::Table(table) => {
278 table.repair_style(context, new_style)
279 },
280 }
281 }
282
283 #[inline]
284 pub(crate) fn is_block_container(&self) -> bool {
285 matches!(self.contents, IndependentFormattingContextContents::Flow(_))
286 }
287
288 #[inline]
289 pub(crate) fn is_replaced(&self) -> bool {
290 matches!(
291 self.contents,
292 IndependentFormattingContextContents::Replaced(_, _)
293 )
294 }
295
296 #[inline]
297 pub(crate) fn is_table(&self) -> bool {
298 matches!(
299 &self.contents,
300 IndependentFormattingContextContents::Table(_)
301 )
302 }
303
304 fn layout_without_caching(
305 &self,
306 layout_context: &LayoutContext,
307 positioning_context: &mut PositioningContext,
308 containing_block_for_children: &ContainingBlock,
309 containing_block: &ContainingBlock,
310 preferred_aspect_ratio: Option<AspectRatio>,
311 lazy_block_size: &LazySize,
312 ) -> CacheableLayoutResult {
313 match &self.contents {
314 IndependentFormattingContextContents::Replaced(replaced, widget) => {
315 let mut replaced_layout = replaced.layout(
316 layout_context,
317 containing_block_for_children,
318 preferred_aspect_ratio,
319 &self.base,
320 lazy_block_size,
321 );
322 if let Some(widget) = widget {
323 let mut widget_layout = widget.borrow().layout(
324 layout_context,
325 positioning_context,
326 containing_block_for_children,
327 containing_block_for_children,
328 None,
329 &LazySize::intrinsic(),
330 );
331 replaced_layout
332 .fragments
333 .append(&mut widget_layout.fragments);
334 }
335 replaced_layout
336 },
337 IndependentFormattingContextContents::Flow(bfc) => bfc.layout(
338 layout_context,
339 positioning_context,
340 containing_block_for_children,
341 ),
342 IndependentFormattingContextContents::Flex(fc) => fc.layout(
343 layout_context,
344 positioning_context,
345 containing_block_for_children,
346 lazy_block_size,
347 ),
348 IndependentFormattingContextContents::Grid(fc) => fc.layout(
349 layout_context,
350 positioning_context,
351 containing_block_for_children,
352 containing_block,
353 ),
354 IndependentFormattingContextContents::Table(table) => table.layout(
355 layout_context,
356 positioning_context,
357 containing_block_for_children,
358 containing_block,
359 ),
360 }
361 }
362
363 #[servo_tracing::instrument(name = "IndependentFormattingContext::layout", skip_all)]
364 pub(crate) fn layout(
365 &self,
366 layout_context: &LayoutContext,
367 positioning_context: &mut PositioningContext,
368 containing_block_for_children: &ContainingBlock,
369 containing_block: &ContainingBlock,
370 preferred_aspect_ratio: Option<AspectRatio>,
371 lazy_block_size: &LazySize,
372 ) -> CacheableLayoutResult {
373 if let Some(cache) = self.base.cached_layout_result.borrow().as_ref() {
374 let cache = &**cache;
375 if cache.containing_block_for_children_size.inline ==
376 containing_block_for_children.size.inline &&
377 (cache.containing_block_for_children_size.block ==
378 containing_block_for_children.size.block ||
379 !cache.result.depends_on_block_constraints)
380 {
381 positioning_context.append(cache.positioning_context.clone());
382 return cache.result.clone();
383 }
384 #[cfg(feature = "tracing")]
385 tracing::debug!(
386 name: "IndependentFormattingContext::layout cache miss",
387 cached = ?cache.containing_block_for_children_size,
388 required = ?containing_block_for_children.size,
389 );
390 }
391
392 let mut child_positioning_context = PositioningContext::default();
393 let result = self.layout_without_caching(
394 layout_context,
395 &mut child_positioning_context,
396 containing_block_for_children,
397 containing_block,
398 preferred_aspect_ratio,
399 lazy_block_size,
400 );
401
402 *self.base.cached_layout_result.borrow_mut() =
403 Some(Box::new(CacheableLayoutResultAndInputs {
404 result: result.clone(),
405 positioning_context: child_positioning_context.clone(),
406 containing_block_for_children_size: containing_block_for_children.size.clone(),
407 }));
408 positioning_context.append(child_positioning_context);
409
410 result
411 }
412
413 #[inline]
414 pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
415 match &self.contents {
416 IndependentFormattingContextContents::Replaced(replaced, _) => {
417 replaced.layout_style(&self.base)
418 },
419 IndependentFormattingContextContents::Flow(fc) => fc.layout_style(&self.base),
420 IndependentFormattingContextContents::Flex(fc) => fc.layout_style(),
421 IndependentFormattingContextContents::Grid(fc) => fc.layout_style(),
422 IndependentFormattingContextContents::Table(fc) => fc.layout_style(None),
423 }
424 }
425
426 #[inline]
427 pub(crate) fn preferred_aspect_ratio(
428 &self,
429 padding_border_sums: &LogicalVec2<Au>,
430 ) -> Option<AspectRatio> {
431 match &self.contents {
432 IndependentFormattingContextContents::Replaced(replaced, _) => {
433 replaced.preferred_aspect_ratio(self.style(), padding_border_sums)
434 },
435 _ => None,
437 }
438 }
439
440 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
441 match &self.contents {
442 IndependentFormattingContextContents::Replaced(_, widget) => {
443 if let Some(widget) = widget {
444 widget.borrow_mut().base.parent_box.replace(layout_box);
445 }
446 },
447 IndependentFormattingContextContents::Flow(contents) => {
448 contents.attached_to_tree(layout_box)
449 },
450 IndependentFormattingContextContents::Flex(contents) => {
451 contents.attached_to_tree(layout_box)
452 },
453 IndependentFormattingContextContents::Grid(contents) => {
454 contents.attached_to_tree(layout_box)
455 },
456 IndependentFormattingContextContents::Table(contents) => {
457 contents.attached_to_tree(layout_box)
458 },
459 }
460 }
461}
462
463impl ComputeInlineContentSizes for IndependentFormattingContextContents {
464 fn compute_inline_content_sizes(
465 &self,
466 layout_context: &LayoutContext,
467 constraint_space: &ConstraintSpace,
468 ) -> InlineContentSizesResult {
469 match self {
470 Self::Replaced(inner, _) => {
471 inner.compute_inline_content_sizes(layout_context, constraint_space)
472 },
473 Self::Flow(inner) => inner
474 .contents
475 .compute_inline_content_sizes(layout_context, constraint_space),
476 Self::Flex(inner) => {
477 inner.compute_inline_content_sizes(layout_context, constraint_space)
478 },
479 Self::Grid(inner) => {
480 inner.compute_inline_content_sizes(layout_context, constraint_space)
481 },
482 Self::Table(inner) => {
483 inner.compute_inline_content_sizes(layout_context, constraint_space)
484 },
485 }
486 }
487}