1use 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 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
134pub(crate) enum IntermediateBlockContainer {
142 InlineFormattingContext(BlockContainer),
143 Deferred {
144 contents: NonReplacedContents,
145 propagated_data: PropagatedBoxTreeData,
146 is_list_item: bool,
147 },
148}
149
150pub(crate) struct BlockContainerBuilder<'dom, 'style> {
155 context: &'style LayoutContext<'style>,
156
157 info: &'style NodeAndStyleInfo<'dom>,
160
161 block_level_boxes: Vec<BlockLevelJob<'dom>>,
175
176 have_already_seen_first_line_for_text_indent: bool,
179
180 propagated_data: PropagatedBoxTreeData,
182
183 inline_formatting_context_builder: Option<InlineFormattingContextBuilder>,
188
189 anonymous_box_info: Option<NodeAndStyleInfo<'dom>>,
192
193 anonymous_table_content: Vec<AnonymousTableContent<'dom>>,
197
198 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 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 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 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 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 !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 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 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 self.handle_list_item_marker_inside(&marker_info, marker_contents)
567 }
568
569 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 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 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 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, );
803 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}