1use 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 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 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 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 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 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 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 !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 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 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 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 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 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 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, );
801 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}