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