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 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.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 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 non_replaced_contents.traverse(self.context, info, self);
567
568 self.finish_anonymous_table_if_needed();
569
570 self.inline_formatting_context_builder
571 .as_mut()
572 .expect("Should be building an InlineFormattingContext")
573 .end_inline_box();
574 }
575
576 fn handle_block_level_element(
577 &mut self,
578 info: &NodeAndStyleInfo<'dom>,
579 display_inside: DisplayInside,
580 contents: Contents,
581 box_slot: BoxSlot<'dom>,
582 ) {
583 let propagated_data = self.propagated_data;
584 let kind = BlockLevelCreator::new_for_inflow_block_level_element(
585 info,
586 display_inside,
587 contents,
588 propagated_data,
589 );
590 let job = BlockLevelJob {
591 info: info.clone(),
592 box_slot,
593 kind,
594 propagated_data,
595 };
596 if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
597 if builder.currently_processing_inline_box() {
598 builder.push_block_level_box(job.finish(self.context));
599 return;
600 }
601 if let Some(context) = self.finish_ongoing_inline_formatting_context() {
602 self.push_block_level_job_for_inline_formatting_context(context);
603 }
604 }
605 self.block_level_boxes.push(job);
606
607 self.have_already_seen_first_line_for_text_indent = true;
610 }
611
612 fn handle_absolutely_positioned_element(
613 &mut self,
614 info: &NodeAndStyleInfo<'dom>,
615 display_inside: DisplayInside,
616 contents: Contents,
617 box_slot: BoxSlot<'dom>,
618 ) {
619 let needs_inline_builder =
625 info.style.get_box().original_display.outside() == StyloDisplayOutside::Inline;
626 if needs_inline_builder {
627 self.ensure_inline_formatting_context_builder();
628 }
629 let inline_builder = self
630 .inline_formatting_context_builder
631 .as_mut()
632 .filter(|builder| needs_inline_builder || !builder.is_empty);
633 if let Some(inline_builder) = inline_builder {
634 let constructor = || {
635 ArcRefCell::new(AbsolutelyPositionedBox::construct(
636 self.context,
637 info,
638 display_inside,
639 contents,
640 ))
641 };
642 let old_layout_box = box_slot.take_layout_box();
643 let inline_level_box =
644 inline_builder.push_absolutely_positioned_box(constructor, old_layout_box);
645 box_slot.set(LayoutBox::InlineLevel(inline_level_box));
646 return;
647 }
648
649 let kind = BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox {
650 contents,
651 display_inside,
652 };
653 self.block_level_boxes.push(BlockLevelJob {
654 info: info.clone(),
655 box_slot,
656 kind,
657 propagated_data: self.propagated_data,
658 });
659 }
660
661 fn handle_float_element(
662 &mut self,
663 info: &NodeAndStyleInfo<'dom>,
664 display_inside: DisplayInside,
665 contents: Contents,
666 box_slot: BoxSlot<'dom>,
667 ) {
668 if let Some(builder) = self.inline_formatting_context_builder.as_mut() &&
669 !builder.is_empty
670 {
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 let kind = BlockLevelCreator::OutOfFlowFloatBox {
687 contents,
688 display_inside,
689 };
690 self.block_level_boxes.push(BlockLevelJob {
691 info: info.clone(),
692 box_slot,
693 kind,
694 propagated_data: self.propagated_data,
695 });
696 }
697
698 fn push_block_level_job_for_inline_formatting_context(
699 &mut self,
700 inline_formatting_context: InlineFormattingContext,
701 ) {
702 let layout_context = self.context;
703 let anonymous_info = self
704 .anonymous_box_info
705 .get_or_insert_with(|| {
706 self.info
707 .with_pseudo_element(layout_context, PseudoElement::ServoAnonymousBox)
708 .expect("Should never fail to create anonymous box")
709 })
710 .clone();
711
712 let box_slot = anonymous_info.node.box_slot();
713 self.block_level_boxes.push(BlockLevelJob {
714 info: anonymous_info,
715 box_slot,
716 kind: BlockLevelCreator::SameFormattingContextBlock(
717 IntermediateBlockContainer::InlineFormattingContext(
718 BlockContainer::InlineFormattingContext(inline_formatting_context),
719 ),
720 ),
721 propagated_data: self.propagated_data,
722 });
723
724 self.have_already_seen_first_line_for_text_indent = true;
725 }
726}
727
728impl BlockLevelJob<'_> {
729 fn finish(self, context: &LayoutContext) -> ArcRefCell<BlockLevelBox> {
730 let info = &self.info;
731
732 if let Some(block_level_box) = match &*self.box_slot.slot.borrow() {
735 Some(LayoutBox::BlockLevel(block_level_box)) => Some(block_level_box.clone()),
736 _ => None,
737 } {
738 return block_level_box;
739 }
740
741 let block_level_box = match self.kind {
742 BlockLevelCreator::SameFormattingContextBlock(intermediate_block_container) => {
743 let contents = intermediate_block_container.finish(context, info);
744 let contains_floats = contents.contains_floats();
745 ArcRefCell::new(BlockLevelBox::SameFormattingContextBlock {
746 base: LayoutBoxBase::new(info.into(), info.style.clone()),
747 contents,
748 contains_floats,
749 })
750 },
751 BlockLevelCreator::Independent {
752 display_inside,
753 contents,
754 } => {
755 let context = IndependentFormattingContext::construct(
756 context,
757 info,
758 display_inside,
759 contents,
760 self.propagated_data,
761 );
762 ArcRefCell::new(BlockLevelBox::Independent(context))
763 },
764 BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox {
765 display_inside,
766 contents,
767 } => ArcRefCell::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
768 ArcRefCell::new(AbsolutelyPositionedBox::construct(
769 context,
770 info,
771 display_inside,
772 contents,
773 )),
774 )),
775 BlockLevelCreator::OutOfFlowFloatBox {
776 display_inside,
777 contents,
778 } => ArcRefCell::new(BlockLevelBox::OutOfFlowFloatBox(FloatBox::construct(
779 context,
780 info,
781 display_inside,
782 contents,
783 self.propagated_data,
784 ))),
785 BlockLevelCreator::OutsideMarker {
786 contents,
787 list_item_style,
788 } => {
789 let contents = NonReplacedContents::OfPseudoElement(contents);
790 let block_container = BlockContainer::construct(
791 context,
792 info,
793 contents,
794 self.propagated_data,
795 false, );
797 let block_formatting_context = BlockFormattingContext {
799 contents: block_container,
800 contains_floats: false,
801 };
802 ArcRefCell::new(BlockLevelBox::OutsideMarker(OutsideMarker {
803 context: IndependentFormattingContext::new(
804 LayoutBoxBase::new(info.into(), info.style.clone()),
805 IndependentFormattingContextContents::Flow(block_formatting_context),
806 self.propagated_data,
807 ),
808 list_item_style,
809 }))
810 },
811 BlockLevelCreator::AnonymousTable { table_block } => table_block,
812 };
813 self.box_slot
814 .set(LayoutBox::BlockLevel(block_level_box.clone()));
815 block_level_box
816 }
817}
818
819impl IntermediateBlockContainer {
820 fn finish(self, context: &LayoutContext, info: &NodeAndStyleInfo<'_>) -> BlockContainer {
821 match self {
822 IntermediateBlockContainer::Deferred {
823 contents,
824 propagated_data,
825 is_list_item,
826 } => BlockContainer::construct(context, info, contents, propagated_data, is_list_item),
827 IntermediateBlockContainer::InlineFormattingContext(block_container) => block_container,
828 }
829 }
830}