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