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::{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 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
133pub(crate) enum IntermediateBlockContainer {
141 InlineFormattingContext(BlockContainer),
142 Deferred {
143 contents: NonReplacedContents,
144 propagated_data: PropagatedBoxTreeData,
145 is_list_item: bool,
146 },
147}
148
149pub(crate) struct BlockContainerBuilder<'dom, 'style> {
154 context: &'style LayoutContext<'style>,
155
156 info: &'style NodeAndStyleInfo<'dom>,
159
160 block_level_boxes: Vec<BlockLevelJob<'dom>>,
174
175 have_already_seen_first_line_for_text_indent: bool,
178
179 propagated_data: PropagatedBoxTreeData,
181
182 inline_formatting_context_builder: Option<InlineFormattingContextBuilder>,
187
188 anonymous_box_info: Option<NodeAndStyleInfo<'dom>>,
191
192 anonymous_table_content: Vec<AnonymousTableContent<'dom>>,
196
197 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 if let Some((marker_info, marker_contents)) = crate::lists::make_marker(context, info) {
215 match marker_info.style.clone_list_style_position() {
216 ListStylePosition::Inside => {
217 builder.handle_list_item_marker_inside(&marker_info, marker_contents)
218 },
219 ListStylePosition::Outside => builder.handle_list_item_marker_outside(
220 &marker_info,
221 marker_contents,
222 info.style.clone(),
223 ),
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 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.context.use_rayon {
295 self.block_level_boxes
296 .into_par_iter()
297 .map(|block_level_job| block_level_job.finish(context))
298 .collect()
299 } else {
300 self.block_level_boxes
301 .into_iter()
302 .map(|block_level_job| block_level_job.finish(context))
303 .collect()
304 };
305
306 BlockContainer::BlockLevelBoxes(block_level_boxes)
307 }
308
309 fn finish_anonymous_table_if_needed(&mut self) {
310 if self.anonymous_table_content.is_empty() {
311 return;
312 }
313
314 let inline_table = self.currently_processing_inline_box();
322
323 let mut contents: Vec<AnonymousTableContent<'dom>> =
324 self.anonymous_table_content.drain(..).collect();
325 let last_element_index = contents
326 .iter()
327 .rposition(|content| matches!(content, AnonymousTableContent::Element { .. }))
328 .expect("Anonymous table contents should include some table-level element");
329 let trailing_contents = contents.split_off(last_element_index + 1);
330
331 let (table_info, ifc) = Table::construct_anonymous(
332 self.context,
333 self,
334 self.info,
335 contents,
336 self.propagated_data,
337 );
338
339 if inline_table {
340 self.ensure_inline_formatting_context_builder()
341 .push_atomic(|| ArcRefCell::new(ifc), None);
342 } else {
343 let table_block = ArcRefCell::new(BlockLevelBox::Independent(ifc));
344
345 if let Some(inline_formatting_context) = self.finish_ongoing_inline_formatting_context()
346 {
347 self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
348 }
349
350 let box_slot = table_info.node.box_slot();
351 self.block_level_boxes.push(BlockLevelJob {
352 info: table_info,
353 box_slot,
354 kind: BlockLevelCreator::AnonymousTable { table_block },
355 propagated_data: self.propagated_data,
356 });
357 }
358
359 for content in trailing_contents {
369 match content {
370 AnonymousTableContent::Text(info, text) => self.handle_text(&info, text),
371 AnonymousTableContent::EnterDisplayContents(styles) => {
372 self.enter_display_contents(styles)
373 },
374 AnonymousTableContent::LeaveDisplayContents => self.leave_display_contents(),
375 AnonymousTableContent::Element { .. } => {
376 unreachable!("All elements were placed inside the table")
377 },
378 }
379 }
380 }
381}
382
383impl<'dom> TraversalHandler<'dom> for BlockContainerBuilder<'dom, '_> {
384 fn handle_element(
385 &mut self,
386 info: &NodeAndStyleInfo<'dom>,
387 display: DisplayGeneratingBox,
388 contents: Contents,
389 box_slot: BoxSlot<'dom>,
390 ) {
391 match display {
392 DisplayGeneratingBox::OutsideInside { outside, inside } => {
393 self.finish_anonymous_table_if_needed();
394
395 match outside {
396 DisplayOutside::Inline => {
397 self.handle_inline_level_element(info, inside, contents, box_slot)
398 },
399 DisplayOutside::Block => {
400 let box_style = info.style.get_box();
401 if box_style.position.is_absolutely_positioned() {
404 self.handle_absolutely_positioned_element(
405 info, inside, contents, box_slot,
406 )
407 } else if box_style.float.is_floating() {
408 self.handle_float_element(info, inside, contents, box_slot)
409 } else {
410 self.handle_block_level_element(info, inside, contents, box_slot)
411 }
412 },
413 };
414 },
415 DisplayGeneratingBox::LayoutInternal(_) => {
416 self.anonymous_table_content
417 .push(AnonymousTableContent::Element {
418 info: info.clone(),
419 display,
420 contents,
421 box_slot,
422 });
423 },
424 }
425 }
426
427 fn handle_text(&mut self, info: &NodeAndStyleInfo<'dom>, text: Cow<'dom, str>) {
428 if text.is_empty() {
429 return;
430 }
431
432 if !self.anonymous_table_content.is_empty() && text.chars().all(char_is_whitespace) {
436 self.anonymous_table_content
437 .push(AnonymousTableContent::Text(info.clone(), text));
438 return;
439 } else {
440 self.finish_anonymous_table_if_needed();
441 }
442
443 self.ensure_inline_formatting_context_builder();
444 self.inline_formatting_context_builder
445 .as_mut()
446 .expect("Should be guaranteed by line above")
447 .push_text_with_possible_first_letter(text, info, self.info, self.context);
448 }
449
450 fn enter_display_contents(&mut self, styles: SharedInlineStyles) {
451 if !self.anonymous_table_content.is_empty() {
452 self.anonymous_table_content
453 .push(AnonymousTableContent::EnterDisplayContents(styles));
454 return;
455 }
456 self.display_contents_shared_styles.push(styles.clone());
457 if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
458 builder.enter_display_contents(styles);
459 }
460 }
461
462 fn leave_display_contents(&mut self) {
463 if !self.anonymous_table_content.is_empty() {
464 self.anonymous_table_content
465 .push(AnonymousTableContent::LeaveDisplayContents);
466 return;
467 }
468 self.display_contents_shared_styles.pop();
469 if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
470 builder.leave_display_contents();
471 }
472 }
473}
474
475impl<'dom> BlockContainerBuilder<'dom, '_> {
476 fn handle_list_item_marker_inside(
477 &mut self,
478 marker_info: &NodeAndStyleInfo<'dom>,
479 contents: Vec<crate::dom_traversal::PseudoElementContentItem>,
480 ) {
481 let box_slot = marker_info.node.box_slot();
482 self.handle_inline_level_element(
483 marker_info,
484 DisplayInside::Flow {
485 is_list_item: false,
486 },
487 Contents::for_pseudo_element(contents),
488 box_slot,
489 );
490 }
491
492 fn handle_list_item_marker_outside(
493 &mut self,
494 marker_info: &NodeAndStyleInfo<'dom>,
495 contents: Vec<crate::dom_traversal::PseudoElementContentItem>,
496 list_item_style: Arc<ComputedValues>,
497 ) {
498 let box_slot = marker_info.node.box_slot();
499 self.block_level_boxes.push(BlockLevelJob {
500 info: marker_info.clone(),
501 box_slot,
502 kind: BlockLevelCreator::OutsideMarker {
503 contents,
504 list_item_style,
505 },
506 propagated_data: self.propagated_data,
507 });
508 }
509
510 fn handle_inline_level_element(
511 &mut self,
512 info: &NodeAndStyleInfo<'dom>,
513 display_inside: DisplayInside,
514 contents: Contents,
515 box_slot: BoxSlot<'dom>,
516 ) {
517 let context = self.context;
518 let old_layout_box = box_slot.take_layout_box();
519 let (is_list_item, non_replaced_contents) = match (display_inside, contents) {
520 (
521 DisplayInside::Flow { is_list_item },
522 Contents::NonReplaced(non_replaced_contents),
523 ) => (is_list_item, non_replaced_contents),
524 (_, contents) => {
525 let propagated_data = self.propagated_data;
527
528 let construction_callback = || {
529 ArcRefCell::new(IndependentFormattingContext::construct(
530 context,
531 info,
532 display_inside,
533 contents,
534 propagated_data,
535 ))
536 };
537
538 let atomic = self
539 .ensure_inline_formatting_context_builder()
540 .push_atomic(construction_callback, old_layout_box);
541 box_slot.set(LayoutBox::InlineLevel(atomic));
542 return;
543 },
544 };
545
546 let inline_builder = self.ensure_inline_formatting_context_builder();
549 let inline_item = inline_builder.start_inline_box(
550 || ArcRefCell::new(InlineBox::new(info, context)),
551 old_layout_box,
552 );
553 box_slot.set(LayoutBox::InlineLevel(inline_item));
554
555 if is_list_item {
556 if let Some((marker_info, marker_contents)) =
557 crate::lists::make_marker(self.context, info)
558 {
559 self.handle_list_item_marker_inside(&marker_info, marker_contents)
563 }
564 }
565
566 non_replaced_contents.traverse(self.context, info, self);
568
569 self.finish_anonymous_table_if_needed();
570
571 self.inline_formatting_context_builder
572 .as_mut()
573 .expect("Should be building an InlineFormattingContext")
574 .end_inline_box();
575 }
576
577 fn handle_block_level_element(
578 &mut self,
579 info: &NodeAndStyleInfo<'dom>,
580 display_inside: DisplayInside,
581 contents: Contents,
582 box_slot: BoxSlot<'dom>,
583 ) {
584 let propagated_data = self.propagated_data;
585 let kind = BlockLevelCreator::new_for_inflow_block_level_element(
586 info,
587 display_inside,
588 contents,
589 propagated_data,
590 );
591 let job = BlockLevelJob {
592 info: info.clone(),
593 box_slot,
594 kind,
595 propagated_data,
596 };
597 if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
598 if builder.currently_processing_inline_box() {
599 builder.push_block_level_box(job.finish(self.context));
600 return;
601 }
602 if let Some(context) = self.finish_ongoing_inline_formatting_context() {
603 self.push_block_level_job_for_inline_formatting_context(context);
604 }
605 }
606 self.block_level_boxes.push(job);
607
608 self.have_already_seen_first_line_for_text_indent = true;
611 }
612
613 fn handle_absolutely_positioned_element(
614 &mut self,
615 info: &NodeAndStyleInfo<'dom>,
616 display_inside: DisplayInside,
617 contents: Contents,
618 box_slot: BoxSlot<'dom>,
619 ) {
620 let needs_inline_builder =
626 info.style.get_box().original_display.outside() == StyloDisplayOutside::Inline;
627 if needs_inline_builder {
628 self.ensure_inline_formatting_context_builder();
629 }
630 let inline_builder = self
631 .inline_formatting_context_builder
632 .as_mut()
633 .filter(|builder| needs_inline_builder || !builder.is_empty);
634 if let Some(inline_builder) = inline_builder {
635 let constructor = || {
636 ArcRefCell::new(AbsolutelyPositionedBox::construct(
637 self.context,
638 info,
639 display_inside,
640 contents,
641 ))
642 };
643 let old_layout_box = box_slot.take_layout_box();
644 let inline_level_box =
645 inline_builder.push_absolutely_positioned_box(constructor, old_layout_box);
646 box_slot.set(LayoutBox::InlineLevel(inline_level_box));
647 return;
648 }
649
650 let kind = BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox {
651 contents,
652 display_inside,
653 };
654 self.block_level_boxes.push(BlockLevelJob {
655 info: info.clone(),
656 box_slot,
657 kind,
658 propagated_data: self.propagated_data,
659 });
660 }
661
662 fn handle_float_element(
663 &mut self,
664 info: &NodeAndStyleInfo<'dom>,
665 display_inside: DisplayInside,
666 contents: Contents,
667 box_slot: BoxSlot<'dom>,
668 ) {
669 if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
670 if !builder.is_empty {
671 let constructor = || {
672 ArcRefCell::new(FloatBox::construct(
673 self.context,
674 info,
675 display_inside,
676 contents,
677 self.propagated_data,
678 ))
679 };
680 let old_layout_box = box_slot.take_layout_box();
681 let inline_level_box = builder.push_float_box(constructor, old_layout_box);
682 box_slot.set(LayoutBox::InlineLevel(inline_level_box));
683 return;
684 }
685 }
686
687 let kind = BlockLevelCreator::OutOfFlowFloatBox {
688 contents,
689 display_inside,
690 };
691 self.block_level_boxes.push(BlockLevelJob {
692 info: info.clone(),
693 box_slot,
694 kind,
695 propagated_data: self.propagated_data,
696 });
697 }
698
699 fn push_block_level_job_for_inline_formatting_context(
700 &mut self,
701 inline_formatting_context: InlineFormattingContext,
702 ) {
703 let layout_context = self.context;
704 let anonymous_info = self
705 .anonymous_box_info
706 .get_or_insert_with(|| {
707 self.info
708 .with_pseudo_element(layout_context, PseudoElement::ServoAnonymousBox)
709 .expect("Should never fail to create anonymous box")
710 })
711 .clone();
712
713 let box_slot = anonymous_info.node.box_slot();
714 self.block_level_boxes.push(BlockLevelJob {
715 info: anonymous_info,
716 box_slot,
717 kind: BlockLevelCreator::SameFormattingContextBlock(
718 IntermediateBlockContainer::InlineFormattingContext(
719 BlockContainer::InlineFormattingContext(inline_formatting_context),
720 ),
721 ),
722 propagated_data: self.propagated_data,
723 });
724
725 self.have_already_seen_first_line_for_text_indent = true;
726 }
727}
728
729impl BlockLevelJob<'_> {
730 fn finish(self, context: &LayoutContext) -> ArcRefCell<BlockLevelBox> {
731 let info = &self.info;
732
733 if let Some(block_level_box) = match &*self.box_slot.slot.borrow() {
736 Some(LayoutBox::BlockLevel(block_level_box)) => Some(block_level_box.clone()),
737 _ => None,
738 } {
739 return block_level_box;
740 }
741
742 let block_level_box = match self.kind {
743 BlockLevelCreator::SameFormattingContextBlock(intermediate_block_container) => {
744 let contents = intermediate_block_container.finish(context, info);
745 let contains_floats = contents.contains_floats();
746 ArcRefCell::new(BlockLevelBox::SameFormattingContextBlock {
747 base: LayoutBoxBase::new(info.into(), info.style.clone()),
748 contents,
749 contains_floats,
750 })
751 },
752 BlockLevelCreator::Independent {
753 display_inside,
754 contents,
755 } => {
756 let context = IndependentFormattingContext::construct(
757 context,
758 info,
759 display_inside,
760 contents,
761 self.propagated_data,
762 );
763 ArcRefCell::new(BlockLevelBox::Independent(context))
764 },
765 BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox {
766 display_inside,
767 contents,
768 } => ArcRefCell::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
769 ArcRefCell::new(AbsolutelyPositionedBox::construct(
770 context,
771 info,
772 display_inside,
773 contents,
774 )),
775 )),
776 BlockLevelCreator::OutOfFlowFloatBox {
777 display_inside,
778 contents,
779 } => ArcRefCell::new(BlockLevelBox::OutOfFlowFloatBox(FloatBox::construct(
780 context,
781 info,
782 display_inside,
783 contents,
784 self.propagated_data,
785 ))),
786 BlockLevelCreator::OutsideMarker {
787 contents,
788 list_item_style,
789 } => {
790 let contents = NonReplacedContents::OfPseudoElement(contents);
791 let block_container = BlockContainer::construct(
792 context,
793 info,
794 contents,
795 self.propagated_data,
796 false, );
798 let block_formatting_context = BlockFormattingContext {
800 contents: block_container,
801 contains_floats: false,
802 };
803 ArcRefCell::new(BlockLevelBox::OutsideMarker(OutsideMarker {
804 context: IndependentFormattingContext::new(
805 LayoutBoxBase::new(info.into(), info.style.clone()),
806 IndependentFormattingContextContents::Flow(block_formatting_context),
807 self.propagated_data,
808 ),
809 list_item_style,
810 }))
811 },
812 BlockLevelCreator::AnonymousTable { table_block } => table_block,
813 };
814 self.box_slot
815 .set(LayoutBox::BlockLevel(block_level_box.clone()));
816 block_level_box
817 }
818}
819
820impl IntermediateBlockContainer {
821 fn finish(self, context: &LayoutContext, info: &NodeAndStyleInfo<'_>) -> BlockContainer {
822 match self {
823 IntermediateBlockContainer::Deferred {
824 contents,
825 propagated_data,
826 is_list_item,
827 } => BlockContainer::construct(context, info, contents, propagated_data, is_list_item),
828 IntermediateBlockContainer::InlineFormattingContext(block_container) => block_container,
829 }
830 }
831}