layout/flow/
construct.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::borrow::Cow;
6
7use rayon::iter::{IntoParallelIterator, ParallelIterator};
8use servo_arc::Arc;
9use style::properties::ComputedValues;
10use style::properties::longhands::list_style_position::computed_value::T as ListStylePosition;
11use style::selector_parser::PseudoElement;
12use style::str::char_is_whitespace;
13use style::values::specified::box_::DisplayOutside as StyloDisplayOutside;
14
15use super::OutsideMarker;
16use super::inline::construct::InlineFormattingContextBuilder;
17use super::inline::inline_box::InlineBox;
18use super::inline::{InlineFormattingContext, SharedInlineStyles};
19use crate::PropagatedBoxTreeData;
20use crate::cell::ArcRefCell;
21use crate::context::LayoutContext;
22use crate::dom::{BoxSlot, LayoutBox, NodeExt};
23use crate::dom_traversal::{
24    Contents, NodeAndStyleInfo, NonReplacedContents, PseudoElementContentItem, TraversalHandler,
25};
26use crate::flow::float::FloatBox;
27use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox};
28use crate::formatting_contexts::IndependentFormattingContext;
29use crate::fragment_tree::FragmentFlags;
30use crate::layout_box_base::LayoutBoxBase;
31use crate::positioned::AbsolutelyPositionedBox;
32use crate::style_ext::{ComputedValuesExt, DisplayGeneratingBox, DisplayInside, DisplayOutside};
33use crate::table::{AnonymousTableContent, Table};
34
35impl BlockFormattingContext {
36    pub(crate) fn construct(
37        context: &LayoutContext,
38        info: &NodeAndStyleInfo<'_>,
39        contents: NonReplacedContents,
40        propagated_data: PropagatedBoxTreeData,
41        is_list_item: bool,
42    ) -> Self {
43        Self::from_block_container(BlockContainer::construct(
44            context,
45            info,
46            contents,
47            propagated_data,
48            is_list_item,
49        ))
50    }
51
52    pub(crate) fn from_block_container(contents: BlockContainer) -> Self {
53        let contains_floats = contents.contains_floats();
54        Self {
55            contents,
56            contains_floats,
57        }
58    }
59}
60
61struct BlockLevelJob<'dom> {
62    info: NodeAndStyleInfo<'dom>,
63    box_slot: BoxSlot<'dom>,
64    propagated_data: PropagatedBoxTreeData,
65    kind: BlockLevelCreator,
66}
67
68enum BlockLevelCreator {
69    SameFormattingContextBlock(IntermediateBlockContainer),
70    Independent {
71        display_inside: DisplayInside,
72        contents: Contents,
73    },
74    OutOfFlowAbsolutelyPositionedBox {
75        display_inside: DisplayInside,
76        contents: Contents,
77    },
78    OutOfFlowFloatBox {
79        display_inside: DisplayInside,
80        contents: Contents,
81    },
82    OutsideMarker {
83        list_item_style: Arc<ComputedValues>,
84        contents: Vec<PseudoElementContentItem>,
85    },
86    AnonymousTable {
87        table_block: ArcRefCell<BlockLevelBox>,
88    },
89}
90
91/// A block container that may still have to be constructed.
92///
93/// Represents either the inline formatting context of an anonymous block
94/// box or the yet-to-be-computed block container generated from the children
95/// of a given element.
96///
97/// Deferring allows using rayon’s `into_par_iter`.
98enum IntermediateBlockContainer {
99    InlineFormattingContext(BlockContainer),
100    Deferred {
101        contents: NonReplacedContents,
102        propagated_data: PropagatedBoxTreeData,
103        is_list_item: bool,
104    },
105}
106
107/// A builder for a block container.
108///
109/// This builder starts from the first child of a given DOM node
110/// and does a preorder traversal of all of its inclusive siblings.
111pub(crate) struct BlockContainerBuilder<'dom, 'style> {
112    context: &'style LayoutContext<'style>,
113
114    /// This NodeAndStyleInfo contains the root node, the corresponding pseudo
115    /// content designator, and the block container style.
116    info: &'style NodeAndStyleInfo<'dom>,
117
118    /// The list of block-level boxes to be built for the final block container.
119    ///
120    /// Contains all the block-level jobs we found traversing the tree
121    /// so far, if this is empty at the end of the traversal and the ongoing
122    /// inline formatting context is not empty, the block container establishes
123    /// an inline formatting context (see end of `build`).
124    ///
125    /// DOM nodes which represent block-level boxes are immediately pushed
126    /// to this list with their style without ever being traversed at this
127    /// point, instead we just move to their next sibling. If the DOM node
128    /// doesn't have a next sibling, we either reached the end of the container
129    /// root or there are ongoing inline-level boxes
130    /// (see `handle_block_level_element`).
131    block_level_boxes: Vec<BlockLevelJob<'dom>>,
132
133    /// Whether or not this builder has yet produced a block which would be
134    /// be considered the first line for the purposes of `text-indent`.
135    have_already_seen_first_line_for_text_indent: bool,
136
137    /// The propagated data to use for BoxTree construction.
138    propagated_data: PropagatedBoxTreeData,
139
140    /// The [`InlineFormattingContextBuilder`] if we have encountered any inline items,
141    /// otherwise None.
142    ///
143    /// TODO: This can be `OnceCell` once `OnceCell::get_mut_or_init` is stabilized.
144    inline_formatting_context_builder: Option<InlineFormattingContextBuilder>,
145
146    /// The [`NodeAndStyleInfo`] to use for anonymous block boxes pushed to the list of
147    /// block-level boxes, lazily initialized.
148    anonymous_box_info: Option<NodeAndStyleInfo<'dom>>,
149
150    /// A collection of content that is being added to an anonymous table. This is
151    /// composed of any sequence of internal table elements or table captions that
152    /// are found outside of a table.
153    anonymous_table_content: Vec<AnonymousTableContent<'dom>>,
154
155    /// Any [`InlineFormattingContexts`] created need to know about the ongoing `display: contents`
156    /// ancestors that have been processed. This `Vec` allows passing those into new
157    /// [`InlineFormattingContext`]s that we create.
158    display_contents_shared_styles: Vec<SharedInlineStyles>,
159}
160
161impl BlockContainer {
162    pub fn construct(
163        context: &LayoutContext,
164        info: &NodeAndStyleInfo<'_>,
165        contents: NonReplacedContents,
166        propagated_data: PropagatedBoxTreeData,
167        is_list_item: bool,
168    ) -> BlockContainer {
169        let mut builder = BlockContainerBuilder::new(context, info, propagated_data);
170
171        if is_list_item {
172            if let Some((marker_info, marker_contents)) = crate::lists::make_marker(context, info) {
173                match marker_info.style.clone_list_style_position() {
174                    ListStylePosition::Inside => {
175                        builder.handle_list_item_marker_inside(&marker_info, marker_contents)
176                    },
177                    ListStylePosition::Outside => builder.handle_list_item_marker_outside(
178                        &marker_info,
179                        marker_contents,
180                        info.style.clone(),
181                    ),
182                }
183            }
184        }
185
186        contents.traverse(context, info, &mut builder);
187        builder.finish()
188    }
189}
190
191impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> {
192    pub(crate) fn new(
193        context: &'style LayoutContext,
194        info: &'style NodeAndStyleInfo<'dom>,
195        propagated_data: PropagatedBoxTreeData,
196    ) -> Self {
197        BlockContainerBuilder {
198            context,
199            info,
200            block_level_boxes: Vec::new(),
201            propagated_data,
202            have_already_seen_first_line_for_text_indent: false,
203            anonymous_box_info: None,
204            anonymous_table_content: Vec::new(),
205            inline_formatting_context_builder: None,
206            display_contents_shared_styles: Vec::new(),
207        }
208    }
209
210    fn currently_processing_inline_box(&self) -> bool {
211        self.inline_formatting_context_builder
212            .as_ref()
213            .is_some_and(InlineFormattingContextBuilder::currently_processing_inline_box)
214    }
215
216    fn ensure_inline_formatting_context_builder(&mut self) -> &mut InlineFormattingContextBuilder {
217        self.inline_formatting_context_builder
218            .get_or_insert_with(|| {
219                let mut builder = InlineFormattingContextBuilder::new(self.info);
220                for shared_inline_styles in self.display_contents_shared_styles.iter() {
221                    builder.enter_display_contents(shared_inline_styles.clone());
222                }
223                builder
224            })
225    }
226
227    fn finish_ongoing_inline_formatting_context(&mut self) -> Option<InlineFormattingContext> {
228        self.inline_formatting_context_builder.take()?.finish(
229            self.context,
230            !self.have_already_seen_first_line_for_text_indent,
231            self.info.node.is_single_line_text_input(),
232            self.info.style.to_bidi_level(),
233            self.context.rendering_group_id,
234        )
235    }
236
237    pub(crate) fn finish(mut self) -> BlockContainer {
238        debug_assert!(!self.currently_processing_inline_box());
239
240        self.finish_anonymous_table_if_needed();
241
242        if let Some(inline_formatting_context) = self.finish_ongoing_inline_formatting_context() {
243            // There are two options here. This block was composed of both one or more inline formatting contexts
244            // and child blocks OR this block was a single inline formatting context. In the latter case, we
245            // just return the inline formatting context as the block itself.
246            if self.block_level_boxes.is_empty() {
247                return BlockContainer::InlineFormattingContext(inline_formatting_context);
248            }
249            self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
250        }
251
252        let context = self.context;
253        let block_level_boxes = if self.context.use_rayon {
254            self.block_level_boxes
255                .into_par_iter()
256                .map(|block_level_job| block_level_job.finish(context))
257                .collect()
258        } else {
259            self.block_level_boxes
260                .into_iter()
261                .map(|block_level_job| block_level_job.finish(context))
262                .collect()
263        };
264
265        BlockContainer::BlockLevelBoxes(block_level_boxes)
266    }
267
268    fn finish_anonymous_table_if_needed(&mut self) {
269        if self.anonymous_table_content.is_empty() {
270            return;
271        }
272
273        // From https://drafts.csswg.org/css-tables/#fixup-algorithm:
274        //  > If the box’s parent is an inline, run-in, or ruby box (or any box that would perform
275        //  > inlinification of its children), then an inline-table box must be generated; otherwise
276        //  > it must be a table box.
277        //
278        // Note that text content in the inline formatting context isn't enough to force the
279        // creation of an inline table. It requires the parent to be an inline box.
280        let inline_table = self.currently_processing_inline_box();
281
282        let contents: Vec<AnonymousTableContent<'dom>> =
283            self.anonymous_table_content.drain(..).collect();
284        let last_text = match contents.last() {
285            Some(AnonymousTableContent::Text(info, text)) => Some((info.clone(), text.clone())),
286            _ => None,
287        };
288
289        let (table_info, ifc) =
290            Table::construct_anonymous(self.context, self.info, contents, self.propagated_data);
291
292        if inline_table {
293            self.ensure_inline_formatting_context_builder()
294                .push_atomic(|| ArcRefCell::new(ifc), None);
295        } else {
296            let table_block = ArcRefCell::new(BlockLevelBox::Independent(ifc));
297
298            if let Some(inline_formatting_context) = self.finish_ongoing_inline_formatting_context()
299            {
300                self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
301            }
302
303            let box_slot = table_info.node.box_slot();
304            self.block_level_boxes.push(BlockLevelJob {
305                info: table_info,
306                box_slot,
307                kind: BlockLevelCreator::AnonymousTable { table_block },
308                propagated_data: self.propagated_data,
309            });
310        }
311
312        // If the last element in the anonymous table content is whitespace, that
313        // whitespace doesn't actually belong to the table. It should be processed outside
314        // ie become a space between the anonymous table and the rest of the block
315        // content. Anonymous tables are really only constructed around internal table
316        // elements and the whitespace between them, so this trailing whitespace should
317        // not be included.
318        //
319        // See https://drafts.csswg.org/css-tables/#fixup-algorithm sections "Remove
320        // irrelevant boxes" and "Generate missing parents."
321        if let Some((info, text)) = last_text {
322            self.handle_text(&info, text);
323        }
324    }
325}
326
327impl<'dom> TraversalHandler<'dom> for BlockContainerBuilder<'dom, '_> {
328    fn handle_element(
329        &mut self,
330        info: &NodeAndStyleInfo<'dom>,
331        display: DisplayGeneratingBox,
332        contents: Contents,
333        box_slot: BoxSlot<'dom>,
334    ) {
335        match display {
336            DisplayGeneratingBox::OutsideInside { outside, inside } => {
337                self.finish_anonymous_table_if_needed();
338
339                match outside {
340                    DisplayOutside::Inline => {
341                        self.handle_inline_level_element(info, inside, contents, box_slot)
342                    },
343                    DisplayOutside::Block => {
344                        let box_style = info.style.get_box();
345                        // Floats and abspos cause blockification, so they only happen in this case.
346                        // https://drafts.csswg.org/css2/visuren.html#dis-pos-flo
347                        if box_style.position.is_absolutely_positioned() {
348                            self.handle_absolutely_positioned_element(
349                                info, inside, contents, box_slot,
350                            )
351                        } else if box_style.float.is_floating() {
352                            self.handle_float_element(info, inside, contents, box_slot)
353                        } else {
354                            self.handle_block_level_element(info, inside, contents, box_slot)
355                        }
356                    },
357                };
358            },
359            DisplayGeneratingBox::LayoutInternal(_) => {
360                self.anonymous_table_content
361                    .push(AnonymousTableContent::Element {
362                        info: info.clone(),
363                        display,
364                        contents,
365                        box_slot,
366                    });
367            },
368        }
369    }
370
371    fn handle_text(&mut self, info: &NodeAndStyleInfo<'dom>, text: Cow<'dom, str>) {
372        if text.is_empty() {
373            return;
374        }
375
376        // If we are building an anonymous table ie this text directly followed internal
377        // table elements that did not have a `<table>` ancestor, then we forward all
378        // whitespace to the table builder.
379        if !self.anonymous_table_content.is_empty() && text.chars().all(char_is_whitespace) {
380            self.anonymous_table_content
381                .push(AnonymousTableContent::Text(info.clone(), text));
382            return;
383        } else {
384            self.finish_anonymous_table_if_needed();
385        }
386
387        self.ensure_inline_formatting_context_builder()
388            .push_text(text, info);
389    }
390
391    fn enter_display_contents(&mut self, styles: SharedInlineStyles) {
392        self.display_contents_shared_styles.push(styles.clone());
393        if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
394            builder.enter_display_contents(styles);
395        }
396    }
397
398    fn leave_display_contents(&mut self) {
399        self.display_contents_shared_styles.pop();
400        if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
401            builder.leave_display_contents();
402        }
403    }
404}
405
406impl<'dom> BlockContainerBuilder<'dom, '_> {
407    fn handle_list_item_marker_inside(
408        &mut self,
409        marker_info: &NodeAndStyleInfo<'dom>,
410        contents: Vec<crate::dom_traversal::PseudoElementContentItem>,
411    ) {
412        let box_slot = marker_info.node.box_slot();
413        self.handle_inline_level_element(
414            marker_info,
415            DisplayInside::Flow {
416                is_list_item: false,
417            },
418            NonReplacedContents::OfPseudoElement(contents).into(),
419            box_slot,
420        );
421    }
422
423    fn handle_list_item_marker_outside(
424        &mut self,
425        marker_info: &NodeAndStyleInfo<'dom>,
426        contents: Vec<crate::dom_traversal::PseudoElementContentItem>,
427        list_item_style: Arc<ComputedValues>,
428    ) {
429        let box_slot = marker_info.node.box_slot();
430        self.block_level_boxes.push(BlockLevelJob {
431            info: marker_info.clone(),
432            box_slot,
433            kind: BlockLevelCreator::OutsideMarker {
434                contents,
435                list_item_style,
436            },
437            propagated_data: self.propagated_data,
438        });
439    }
440
441    fn handle_inline_level_element(
442        &mut self,
443        info: &NodeAndStyleInfo<'dom>,
444        display_inside: DisplayInside,
445        contents: Contents,
446        box_slot: BoxSlot<'dom>,
447    ) {
448        let old_layout_box = box_slot.take_layout_box_if_undamaged(info.damage);
449        let (is_list_item, non_replaced_contents) = match (display_inside, contents) {
450            (
451                DisplayInside::Flow { is_list_item },
452                Contents::NonReplaced(non_replaced_contents),
453            ) => (is_list_item, non_replaced_contents),
454            (_, contents) => {
455                // If this inline element is an atomic, handle it and return.
456                let context = self.context;
457                let propagated_data = self.propagated_data;
458
459                let construction_callback = || {
460                    ArcRefCell::new(IndependentFormattingContext::construct(
461                        context,
462                        info,
463                        display_inside,
464                        contents,
465                        propagated_data,
466                    ))
467                };
468
469                let atomic = self
470                    .ensure_inline_formatting_context_builder()
471                    .push_atomic(construction_callback, old_layout_box);
472                box_slot.set(LayoutBox::InlineLevel(vec![atomic]));
473                return;
474            },
475        };
476
477        // Otherwise, this is just a normal inline box. Whatever happened before, all we need to do
478        // before recurring is to remember this ongoing inline level box.
479        self.ensure_inline_formatting_context_builder()
480            .start_inline_box(
481                || ArcRefCell::new(InlineBox::new(info)),
482                None,
483                old_layout_box,
484            );
485
486        if is_list_item {
487            if let Some((marker_info, marker_contents)) =
488                crate::lists::make_marker(self.context, info)
489            {
490                // Ignore `list-style-position` here:
491                // “If the list item is an inline box: this value is equivalent to `inside`.”
492                // https://drafts.csswg.org/css-lists/#list-style-position-outside
493                self.handle_list_item_marker_inside(&marker_info, marker_contents)
494            }
495        }
496
497        // `unwrap` doesn’t panic here because `is_replaced` returned `false`.
498        non_replaced_contents.traverse(self.context, info, self);
499
500        self.finish_anonymous_table_if_needed();
501
502        // As we are ending this inline box, during the course of the `traverse()` above, the ongoing
503        // inline formatting context may have been split around block-level elements. In that case,
504        // more than a single inline box tree item may have been produced for this inline-level box.
505        // `InlineFormattingContextBuilder::end_inline_box()` is returning all of those box tree
506        // items.
507        box_slot.set(LayoutBox::InlineLevel(
508            self.inline_formatting_context_builder
509                .as_mut()
510                .expect("Should be building an InlineFormattingContext")
511                .end_inline_box(),
512        ));
513    }
514
515    fn handle_block_level_element(
516        &mut self,
517        info: &NodeAndStyleInfo<'dom>,
518        display_inside: DisplayInside,
519        contents: Contents,
520        box_slot: BoxSlot<'dom>,
521    ) {
522        // We just found a block level element, all ongoing inline level boxes
523        // need to be split around it.
524        //
525        // After calling `split_around_block_and_finish`,
526        // `self.inline_formatting_context_builder` is set up with the state
527        // that we want to have after we push the block below.
528        if let Some(inline_formatting_context) = self
529            .inline_formatting_context_builder
530            .as_mut()
531            .and_then(|builder| {
532                builder.split_around_block_and_finish(
533                    self.context,
534                    !self.have_already_seen_first_line_for_text_indent,
535                    self.info.style.to_bidi_level(),
536                )
537            })
538        {
539            self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
540        }
541
542        let propagated_data = self.propagated_data;
543        let kind = match contents {
544            Contents::NonReplaced(contents) => match display_inside {
545                DisplayInside::Flow { is_list_item }
546                    // Fragment flags are just used to indicate that the element is not replaced, so empty
547                    // flags are okay here.
548                    if !info.style.establishes_block_formatting_context(
549                        FragmentFlags::empty()
550                    ) =>
551                {
552                    BlockLevelCreator::SameFormattingContextBlock(
553                        IntermediateBlockContainer::Deferred {
554                            contents,
555                            propagated_data,
556                            is_list_item,
557                        },
558                    )
559                },
560                _ => BlockLevelCreator::Independent {
561                    display_inside,
562                    contents: contents.into(),
563                },
564            },
565            Contents::Replaced(contents) => {
566                let contents = Contents::Replaced(contents);
567                BlockLevelCreator::Independent {
568                    display_inside,
569                    contents,
570                }
571            },
572        };
573        self.block_level_boxes.push(BlockLevelJob {
574            info: info.clone(),
575            box_slot,
576            kind,
577            propagated_data,
578        });
579
580        // Any block also counts as the first line for the purposes of text indent. Even if
581        // they don't actually indent.
582        self.have_already_seen_first_line_for_text_indent = true;
583    }
584
585    fn handle_absolutely_positioned_element(
586        &mut self,
587        info: &NodeAndStyleInfo<'dom>,
588        display_inside: DisplayInside,
589        contents: Contents,
590        box_slot: BoxSlot<'dom>,
591    ) {
592        // If the original display was inline-level, then we need an inline formatting context
593        // in order to compute the static position correctly.
594        // If it was block-level, we don't want to break an existing inline formatting context,
595        // so push it there (`LineItemLayout::layout_absolute` can handle this well). But if
596        // there is no inline formatting context, then we can avoid creating one.
597        let needs_inline_builder =
598            info.style.get_box().original_display.outside() == StyloDisplayOutside::Inline;
599        if needs_inline_builder {
600            self.ensure_inline_formatting_context_builder();
601        }
602        let inline_builder = self
603            .inline_formatting_context_builder
604            .as_mut()
605            .filter(|builder| needs_inline_builder || !builder.is_empty);
606        if let Some(inline_builder) = inline_builder {
607            let constructor = || {
608                ArcRefCell::new(AbsolutelyPositionedBox::construct(
609                    self.context,
610                    info,
611                    display_inside,
612                    contents,
613                ))
614            };
615            let old_layout_box = box_slot.take_layout_box_if_undamaged(info.damage);
616            let inline_level_box =
617                inline_builder.push_absolutely_positioned_box(constructor, old_layout_box);
618            box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box]));
619            return;
620        }
621
622        let kind = BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox {
623            contents,
624            display_inside,
625        };
626        self.block_level_boxes.push(BlockLevelJob {
627            info: info.clone(),
628            box_slot,
629            kind,
630            propagated_data: self.propagated_data,
631        });
632    }
633
634    fn handle_float_element(
635        &mut self,
636        info: &NodeAndStyleInfo<'dom>,
637        display_inside: DisplayInside,
638        contents: Contents,
639        box_slot: BoxSlot<'dom>,
640    ) {
641        if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
642            if !builder.is_empty {
643                let constructor = || {
644                    ArcRefCell::new(FloatBox::construct(
645                        self.context,
646                        info,
647                        display_inside,
648                        contents,
649                        self.propagated_data,
650                    ))
651                };
652                let old_layout_box = box_slot.take_layout_box_if_undamaged(info.damage);
653                let inline_level_box = builder.push_float_box(constructor, old_layout_box);
654                box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box]));
655                return;
656            }
657        }
658
659        let kind = BlockLevelCreator::OutOfFlowFloatBox {
660            contents,
661            display_inside,
662        };
663        self.block_level_boxes.push(BlockLevelJob {
664            info: info.clone(),
665            box_slot,
666            kind,
667            propagated_data: self.propagated_data,
668        });
669    }
670
671    fn push_block_level_job_for_inline_formatting_context(
672        &mut self,
673        inline_formatting_context: InlineFormattingContext,
674    ) {
675        let layout_context = self.context;
676        let anonymous_info = self
677            .anonymous_box_info
678            .get_or_insert_with(|| {
679                self.info
680                    .with_pseudo_element(layout_context, PseudoElement::ServoAnonymousBox)
681                    .expect("Should never fail to create anonymous box")
682            })
683            .clone();
684
685        let box_slot = anonymous_info.node.box_slot();
686        self.block_level_boxes.push(BlockLevelJob {
687            info: anonymous_info,
688            box_slot,
689            kind: BlockLevelCreator::SameFormattingContextBlock(
690                IntermediateBlockContainer::InlineFormattingContext(
691                    BlockContainer::InlineFormattingContext(inline_formatting_context),
692                ),
693            ),
694            propagated_data: self.propagated_data,
695        });
696
697        self.have_already_seen_first_line_for_text_indent = true;
698    }
699}
700
701impl BlockLevelJob<'_> {
702    fn finish(self, context: &LayoutContext) -> ArcRefCell<BlockLevelBox> {
703        let info = &self.info;
704
705        // If this `BlockLevelBox` is undamaged and it has been laid out before, reuse
706        // the old one, while being sure to clear the layout cache.
707        if !info.damage.has_box_damage() {
708            if let Some(block_level_box) = match self.box_slot.slot.as_ref() {
709                Some(box_slot) => match &*box_slot.borrow() {
710                    Some(LayoutBox::BlockLevel(block_level_box)) => Some(block_level_box.clone()),
711                    _ => None,
712                },
713                None => None,
714            } {
715                return block_level_box;
716            }
717        }
718
719        let block_level_box = match self.kind {
720            BlockLevelCreator::SameFormattingContextBlock(intermediate_block_container) => {
721                let contents = intermediate_block_container.finish(context, info);
722                let contains_floats = contents.contains_floats();
723                ArcRefCell::new(BlockLevelBox::SameFormattingContextBlock {
724                    base: LayoutBoxBase::new(info.into(), info.style.clone()),
725                    contents,
726                    contains_floats,
727                })
728            },
729            BlockLevelCreator::Independent {
730                display_inside,
731                contents,
732            } => {
733                let context = IndependentFormattingContext::construct(
734                    context,
735                    info,
736                    display_inside,
737                    contents,
738                    self.propagated_data,
739                );
740                ArcRefCell::new(BlockLevelBox::Independent(context))
741            },
742            BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox {
743                display_inside,
744                contents,
745            } => ArcRefCell::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
746                ArcRefCell::new(AbsolutelyPositionedBox::construct(
747                    context,
748                    info,
749                    display_inside,
750                    contents,
751                )),
752            )),
753            BlockLevelCreator::OutOfFlowFloatBox {
754                display_inside,
755                contents,
756            } => ArcRefCell::new(BlockLevelBox::OutOfFlowFloatBox(FloatBox::construct(
757                context,
758                info,
759                display_inside,
760                contents,
761                self.propagated_data,
762            ))),
763            BlockLevelCreator::OutsideMarker {
764                contents,
765                list_item_style,
766            } => {
767                let contents = NonReplacedContents::OfPseudoElement(contents);
768                let block_container = BlockContainer::construct(
769                    context,
770                    info,
771                    contents,
772                    self.propagated_data,
773                    false, /* is_list_item */
774                );
775                // An outside ::marker must establish a BFC, and can't contain floats.
776                let block_formatting_context = BlockFormattingContext {
777                    contents: block_container,
778                    contains_floats: false,
779                };
780                ArcRefCell::new(BlockLevelBox::OutsideMarker(OutsideMarker {
781                    base: LayoutBoxBase::new(info.into(), info.style.clone()),
782                    block_formatting_context,
783                    list_item_style,
784                }))
785            },
786            BlockLevelCreator::AnonymousTable { table_block } => table_block,
787        };
788        self.box_slot
789            .set(LayoutBox::BlockLevel(block_level_box.clone()));
790        block_level_box
791    }
792}
793
794impl IntermediateBlockContainer {
795    fn finish(self, context: &LayoutContext, info: &NodeAndStyleInfo<'_>) -> BlockContainer {
796        match self {
797            IntermediateBlockContainer::Deferred {
798                contents,
799                propagated_data,
800                is_list_item,
801            } => BlockContainer::construct(context, info, contents, propagated_data, is_list_item),
802            IntermediateBlockContainer::InlineFormattingContext(block_container) => block_container,
803        }
804    }
805}