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 contents: Vec<AnonymousTableContent<'dom>> =
324 self.anonymous_table_content.drain(..).collect();
325 let last_text = match contents.last() {
326 Some(AnonymousTableContent::Text(info, text)) => Some((info.clone(), text.clone())),
327 _ => None,
328 };
329
330 let (table_info, ifc) =
331 Table::construct_anonymous(self.context, self.info, contents, self.propagated_data);
332
333 if inline_table {
334 self.ensure_inline_formatting_context_builder()
335 .push_atomic(|| ArcRefCell::new(ifc), None);
336 } else {
337 let table_block = ArcRefCell::new(BlockLevelBox::Independent(ifc));
338
339 if let Some(inline_formatting_context) = self.finish_ongoing_inline_formatting_context()
340 {
341 self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
342 }
343
344 let box_slot = table_info.node.box_slot();
345 self.block_level_boxes.push(BlockLevelJob {
346 info: table_info,
347 box_slot,
348 kind: BlockLevelCreator::AnonymousTable { table_block },
349 propagated_data: self.propagated_data,
350 });
351 }
352
353 if let Some((info, text)) = last_text {
363 self.handle_text(&info, text);
364 }
365 }
366}
367
368impl<'dom> TraversalHandler<'dom> for BlockContainerBuilder<'dom, '_> {
369 fn handle_element(
370 &mut self,
371 info: &NodeAndStyleInfo<'dom>,
372 display: DisplayGeneratingBox,
373 contents: Contents,
374 box_slot: BoxSlot<'dom>,
375 ) {
376 match display {
377 DisplayGeneratingBox::OutsideInside { outside, inside } => {
378 self.finish_anonymous_table_if_needed();
379
380 match outside {
381 DisplayOutside::Inline => {
382 self.handle_inline_level_element(info, inside, contents, box_slot)
383 },
384 DisplayOutside::Block => {
385 let box_style = info.style.get_box();
386 if box_style.position.is_absolutely_positioned() {
389 self.handle_absolutely_positioned_element(
390 info, inside, contents, box_slot,
391 )
392 } else if box_style.float.is_floating() {
393 self.handle_float_element(info, inside, contents, box_slot)
394 } else {
395 self.handle_block_level_element(info, inside, contents, box_slot)
396 }
397 },
398 };
399 },
400 DisplayGeneratingBox::LayoutInternal(_) => {
401 self.anonymous_table_content
402 .push(AnonymousTableContent::Element {
403 info: info.clone(),
404 display,
405 contents,
406 box_slot,
407 });
408 },
409 }
410 }
411
412 fn handle_text(&mut self, info: &NodeAndStyleInfo<'dom>, text: Cow<'dom, str>) {
413 if text.is_empty() {
414 return;
415 }
416
417 if !self.anonymous_table_content.is_empty() && text.chars().all(char_is_whitespace) {
421 self.anonymous_table_content
422 .push(AnonymousTableContent::Text(info.clone(), text));
423 return;
424 } else {
425 self.finish_anonymous_table_if_needed();
426 }
427
428 self.ensure_inline_formatting_context_builder();
429 self.inline_formatting_context_builder
430 .as_mut()
431 .expect("Should be guaranteed by line above")
432 .push_text_with_possible_first_letter(text, info, self.info, self.context);
433 }
434
435 fn enter_display_contents(&mut self, styles: SharedInlineStyles) {
436 self.display_contents_shared_styles.push(styles.clone());
437 if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
438 builder.enter_display_contents(styles);
439 }
440 }
441
442 fn leave_display_contents(&mut self) {
443 self.display_contents_shared_styles.pop();
444 if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
445 builder.leave_display_contents();
446 }
447 }
448}
449
450impl<'dom> BlockContainerBuilder<'dom, '_> {
451 fn handle_list_item_marker_inside(
452 &mut self,
453 marker_info: &NodeAndStyleInfo<'dom>,
454 contents: Vec<crate::dom_traversal::PseudoElementContentItem>,
455 ) {
456 let box_slot = marker_info.node.box_slot();
457 self.handle_inline_level_element(
458 marker_info,
459 DisplayInside::Flow {
460 is_list_item: false,
461 },
462 Contents::for_pseudo_element(contents),
463 box_slot,
464 );
465 }
466
467 fn handle_list_item_marker_outside(
468 &mut self,
469 marker_info: &NodeAndStyleInfo<'dom>,
470 contents: Vec<crate::dom_traversal::PseudoElementContentItem>,
471 list_item_style: Arc<ComputedValues>,
472 ) {
473 let box_slot = marker_info.node.box_slot();
474 self.block_level_boxes.push(BlockLevelJob {
475 info: marker_info.clone(),
476 box_slot,
477 kind: BlockLevelCreator::OutsideMarker {
478 contents,
479 list_item_style,
480 },
481 propagated_data: self.propagated_data,
482 });
483 }
484
485 fn handle_inline_level_element(
486 &mut self,
487 info: &NodeAndStyleInfo<'dom>,
488 display_inside: DisplayInside,
489 contents: Contents,
490 box_slot: BoxSlot<'dom>,
491 ) {
492 let context = self.context;
493 let old_layout_box = box_slot.take_layout_box();
494 let (is_list_item, non_replaced_contents) = match (display_inside, contents) {
495 (
496 DisplayInside::Flow { is_list_item },
497 Contents::NonReplaced(non_replaced_contents),
498 ) => (is_list_item, non_replaced_contents),
499 (_, contents) => {
500 let propagated_data = self.propagated_data;
502
503 let construction_callback = || {
504 ArcRefCell::new(IndependentFormattingContext::construct(
505 context,
506 info,
507 display_inside,
508 contents,
509 propagated_data,
510 ))
511 };
512
513 let atomic = self
514 .ensure_inline_formatting_context_builder()
515 .push_atomic(construction_callback, old_layout_box);
516 box_slot.set(LayoutBox::InlineLevel(atomic));
517 return;
518 },
519 };
520
521 let inline_builder = self.ensure_inline_formatting_context_builder();
524 let inline_item = inline_builder.start_inline_box(
525 || ArcRefCell::new(InlineBox::new(info, context)),
526 old_layout_box,
527 );
528 box_slot.set(LayoutBox::InlineLevel(inline_item));
529
530 if is_list_item {
531 if let Some((marker_info, marker_contents)) =
532 crate::lists::make_marker(self.context, info)
533 {
534 self.handle_list_item_marker_inside(&marker_info, marker_contents)
538 }
539 }
540
541 non_replaced_contents.traverse(self.context, info, self);
543
544 self.finish_anonymous_table_if_needed();
545
546 self.inline_formatting_context_builder
547 .as_mut()
548 .expect("Should be building an InlineFormattingContext")
549 .end_inline_box();
550 }
551
552 fn handle_block_level_element(
553 &mut self,
554 info: &NodeAndStyleInfo<'dom>,
555 display_inside: DisplayInside,
556 contents: Contents,
557 box_slot: BoxSlot<'dom>,
558 ) {
559 let propagated_data = self.propagated_data;
560 let kind = BlockLevelCreator::new_for_inflow_block_level_element(
561 info,
562 display_inside,
563 contents,
564 propagated_data,
565 );
566 let job = BlockLevelJob {
567 info: info.clone(),
568 box_slot,
569 kind,
570 propagated_data,
571 };
572 if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
573 if builder.currently_processing_inline_box() {
574 builder.push_block_level_box(job.finish(self.context));
575 return;
576 }
577 if let Some(context) = self.finish_ongoing_inline_formatting_context() {
578 self.push_block_level_job_for_inline_formatting_context(context);
579 }
580 }
581 self.block_level_boxes.push(job);
582
583 self.have_already_seen_first_line_for_text_indent = true;
586 }
587
588 fn handle_absolutely_positioned_element(
589 &mut self,
590 info: &NodeAndStyleInfo<'dom>,
591 display_inside: DisplayInside,
592 contents: Contents,
593 box_slot: BoxSlot<'dom>,
594 ) {
595 let needs_inline_builder =
601 info.style.get_box().original_display.outside() == StyloDisplayOutside::Inline;
602 if needs_inline_builder {
603 self.ensure_inline_formatting_context_builder();
604 }
605 let inline_builder = self
606 .inline_formatting_context_builder
607 .as_mut()
608 .filter(|builder| needs_inline_builder || !builder.is_empty);
609 if let Some(inline_builder) = inline_builder {
610 let constructor = || {
611 ArcRefCell::new(AbsolutelyPositionedBox::construct(
612 self.context,
613 info,
614 display_inside,
615 contents,
616 ))
617 };
618 let old_layout_box = box_slot.take_layout_box();
619 let inline_level_box =
620 inline_builder.push_absolutely_positioned_box(constructor, old_layout_box);
621 box_slot.set(LayoutBox::InlineLevel(inline_level_box));
622 return;
623 }
624
625 let kind = BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox {
626 contents,
627 display_inside,
628 };
629 self.block_level_boxes.push(BlockLevelJob {
630 info: info.clone(),
631 box_slot,
632 kind,
633 propagated_data: self.propagated_data,
634 });
635 }
636
637 fn handle_float_element(
638 &mut self,
639 info: &NodeAndStyleInfo<'dom>,
640 display_inside: DisplayInside,
641 contents: Contents,
642 box_slot: BoxSlot<'dom>,
643 ) {
644 if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
645 if !builder.is_empty {
646 let constructor = || {
647 ArcRefCell::new(FloatBox::construct(
648 self.context,
649 info,
650 display_inside,
651 contents,
652 self.propagated_data,
653 ))
654 };
655 let old_layout_box = box_slot.take_layout_box();
656 let inline_level_box = builder.push_float_box(constructor, old_layout_box);
657 box_slot.set(LayoutBox::InlineLevel(inline_level_box));
658 return;
659 }
660 }
661
662 let kind = BlockLevelCreator::OutOfFlowFloatBox {
663 contents,
664 display_inside,
665 };
666 self.block_level_boxes.push(BlockLevelJob {
667 info: info.clone(),
668 box_slot,
669 kind,
670 propagated_data: self.propagated_data,
671 });
672 }
673
674 fn push_block_level_job_for_inline_formatting_context(
675 &mut self,
676 inline_formatting_context: InlineFormattingContext,
677 ) {
678 let layout_context = self.context;
679 let anonymous_info = self
680 .anonymous_box_info
681 .get_or_insert_with(|| {
682 self.info
683 .with_pseudo_element(layout_context, PseudoElement::ServoAnonymousBox)
684 .expect("Should never fail to create anonymous box")
685 })
686 .clone();
687
688 let box_slot = anonymous_info.node.box_slot();
689 self.block_level_boxes.push(BlockLevelJob {
690 info: anonymous_info,
691 box_slot,
692 kind: BlockLevelCreator::SameFormattingContextBlock(
693 IntermediateBlockContainer::InlineFormattingContext(
694 BlockContainer::InlineFormattingContext(inline_formatting_context),
695 ),
696 ),
697 propagated_data: self.propagated_data,
698 });
699
700 self.have_already_seen_first_line_for_text_indent = true;
701 }
702}
703
704impl BlockLevelJob<'_> {
705 fn finish(self, context: &LayoutContext) -> ArcRefCell<BlockLevelBox> {
706 let info = &self.info;
707
708 if let Some(block_level_box) = match &*self.box_slot.slot.borrow() {
711 Some(LayoutBox::BlockLevel(block_level_box)) => Some(block_level_box.clone()),
712 _ => None,
713 } {
714 return block_level_box;
715 }
716
717 let block_level_box = match self.kind {
718 BlockLevelCreator::SameFormattingContextBlock(intermediate_block_container) => {
719 let contents = intermediate_block_container.finish(context, info);
720 let contains_floats = contents.contains_floats();
721 ArcRefCell::new(BlockLevelBox::SameFormattingContextBlock {
722 base: LayoutBoxBase::new(info.into(), info.style.clone()),
723 contents,
724 contains_floats,
725 })
726 },
727 BlockLevelCreator::Independent {
728 display_inside,
729 contents,
730 } => {
731 let context = IndependentFormattingContext::construct(
732 context,
733 info,
734 display_inside,
735 contents,
736 self.propagated_data,
737 );
738 ArcRefCell::new(BlockLevelBox::Independent(context))
739 },
740 BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox {
741 display_inside,
742 contents,
743 } => ArcRefCell::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
744 ArcRefCell::new(AbsolutelyPositionedBox::construct(
745 context,
746 info,
747 display_inside,
748 contents,
749 )),
750 )),
751 BlockLevelCreator::OutOfFlowFloatBox {
752 display_inside,
753 contents,
754 } => ArcRefCell::new(BlockLevelBox::OutOfFlowFloatBox(FloatBox::construct(
755 context,
756 info,
757 display_inside,
758 contents,
759 self.propagated_data,
760 ))),
761 BlockLevelCreator::OutsideMarker {
762 contents,
763 list_item_style,
764 } => {
765 let contents = NonReplacedContents::OfPseudoElement(contents);
766 let block_container = BlockContainer::construct(
767 context,
768 info,
769 contents,
770 self.propagated_data,
771 false, );
773 let block_formatting_context = BlockFormattingContext {
775 contents: block_container,
776 contains_floats: false,
777 };
778 ArcRefCell::new(BlockLevelBox::OutsideMarker(OutsideMarker {
779 context: IndependentFormattingContext::new(
780 LayoutBoxBase::new(info.into(), info.style.clone()),
781 IndependentFormattingContextContents::Flow(block_formatting_context),
782 self.propagated_data,
783 ),
784 list_item_style,
785 }))
786 },
787 BlockLevelCreator::AnonymousTable { table_block } => table_block,
788 };
789 self.box_slot
790 .set(LayoutBox::BlockLevel(block_level_box.clone()));
791 block_level_box
792 }
793}
794
795impl IntermediateBlockContainer {
796 fn finish(self, context: &LayoutContext, info: &NodeAndStyleInfo<'_>) -> BlockContainer {
797 match self {
798 IntermediateBlockContainer::Deferred {
799 contents,
800 propagated_data,
801 is_list_item,
802 } => BlockContainer::construct(context, info, contents, propagated_data, is_list_item),
803 IntermediateBlockContainer::InlineFormattingContext(block_container) => block_container,
804 }
805 }
806}