Skip to main content

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