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