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