1use std::borrow::Cow;
6
7use rayon::iter::{IntoParallelIterator, ParallelIterator};
8use servo_arc::Arc;
9use style::properties::ComputedValues;
10use style::properties::longhands::list_style_position::computed_value::T as ListStylePosition;
11use style::selector_parser::PseudoElement;
12use style::str::char_is_whitespace;
13use style::values::specified::box_::DisplayOutside as StyloDisplayOutside;
14
15use super::OutsideMarker;
16use super::inline::construct::InlineFormattingContextBuilder;
17use super::inline::inline_box::InlineBox;
18use super::inline::{InlineFormattingContext, SharedInlineStyles};
19use crate::PropagatedBoxTreeData;
20use crate::cell::ArcRefCell;
21use crate::context::LayoutContext;
22use crate::dom::{BoxSlot, LayoutBox, NodeExt};
23use crate::dom_traversal::{
24 Contents, NodeAndStyleInfo, NonReplacedContents, PseudoElementContentItem, TraversalHandler,
25};
26use crate::flow::float::FloatBox;
27use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox};
28use crate::formatting_contexts::IndependentFormattingContext;
29use crate::fragment_tree::FragmentFlags;
30use crate::layout_box_base::LayoutBoxBase;
31use crate::positioned::AbsolutelyPositionedBox;
32use crate::style_ext::{ComputedValuesExt, DisplayGeneratingBox, DisplayInside, DisplayOutside};
33use crate::table::{AnonymousTableContent, Table};
34
35impl BlockFormattingContext {
36 pub(crate) fn construct(
37 context: &LayoutContext,
38 info: &NodeAndStyleInfo<'_>,
39 contents: NonReplacedContents,
40 propagated_data: PropagatedBoxTreeData,
41 is_list_item: bool,
42 ) -> Self {
43 Self::from_block_container(BlockContainer::construct(
44 context,
45 info,
46 contents,
47 propagated_data,
48 is_list_item,
49 ))
50 }
51
52 pub(crate) fn from_block_container(contents: BlockContainer) -> Self {
53 let contains_floats = contents.contains_floats();
54 Self {
55 contents,
56 contains_floats,
57 }
58 }
59}
60
61struct BlockLevelJob<'dom> {
62 info: NodeAndStyleInfo<'dom>,
63 box_slot: BoxSlot<'dom>,
64 propagated_data: PropagatedBoxTreeData,
65 kind: BlockLevelCreator,
66}
67
68enum BlockLevelCreator {
69 SameFormattingContextBlock(IntermediateBlockContainer),
70 Independent {
71 display_inside: DisplayInside,
72 contents: Contents,
73 },
74 OutOfFlowAbsolutelyPositionedBox {
75 display_inside: DisplayInside,
76 contents: Contents,
77 },
78 OutOfFlowFloatBox {
79 display_inside: DisplayInside,
80 contents: Contents,
81 },
82 OutsideMarker {
83 list_item_style: Arc<ComputedValues>,
84 contents: Vec<PseudoElementContentItem>,
85 },
86 AnonymousTable {
87 table_block: ArcRefCell<BlockLevelBox>,
88 },
89}
90
91enum IntermediateBlockContainer {
99 InlineFormattingContext(BlockContainer),
100 Deferred {
101 contents: NonReplacedContents,
102 propagated_data: PropagatedBoxTreeData,
103 is_list_item: bool,
104 },
105}
106
107pub(crate) struct BlockContainerBuilder<'dom, 'style> {
112 context: &'style LayoutContext<'style>,
113
114 info: &'style NodeAndStyleInfo<'dom>,
117
118 block_level_boxes: Vec<BlockLevelJob<'dom>>,
132
133 have_already_seen_first_line_for_text_indent: bool,
136
137 propagated_data: PropagatedBoxTreeData,
139
140 inline_formatting_context_builder: Option<InlineFormattingContextBuilder>,
145
146 anonymous_box_info: Option<NodeAndStyleInfo<'dom>>,
149
150 anonymous_table_content: Vec<AnonymousTableContent<'dom>>,
154
155 display_contents_shared_styles: Vec<SharedInlineStyles>,
159}
160
161impl BlockContainer {
162 pub fn construct(
163 context: &LayoutContext,
164 info: &NodeAndStyleInfo<'_>,
165 contents: NonReplacedContents,
166 propagated_data: PropagatedBoxTreeData,
167 is_list_item: bool,
168 ) -> BlockContainer {
169 let mut builder = BlockContainerBuilder::new(context, info, propagated_data);
170
171 if is_list_item {
172 if let Some((marker_info, marker_contents)) = crate::lists::make_marker(context, info) {
173 match marker_info.style.clone_list_style_position() {
174 ListStylePosition::Inside => {
175 builder.handle_list_item_marker_inside(&marker_info, marker_contents)
176 },
177 ListStylePosition::Outside => builder.handle_list_item_marker_outside(
178 &marker_info,
179 marker_contents,
180 info.style.clone(),
181 ),
182 }
183 }
184 }
185
186 contents.traverse(context, info, &mut builder);
187 builder.finish()
188 }
189}
190
191impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> {
192 pub(crate) fn new(
193 context: &'style LayoutContext,
194 info: &'style NodeAndStyleInfo<'dom>,
195 propagated_data: PropagatedBoxTreeData,
196 ) -> Self {
197 BlockContainerBuilder {
198 context,
199 info,
200 block_level_boxes: Vec::new(),
201 propagated_data,
202 have_already_seen_first_line_for_text_indent: false,
203 anonymous_box_info: None,
204 anonymous_table_content: Vec::new(),
205 inline_formatting_context_builder: None,
206 display_contents_shared_styles: Vec::new(),
207 }
208 }
209
210 fn currently_processing_inline_box(&self) -> bool {
211 self.inline_formatting_context_builder
212 .as_ref()
213 .is_some_and(InlineFormattingContextBuilder::currently_processing_inline_box)
214 }
215
216 fn ensure_inline_formatting_context_builder(&mut self) -> &mut InlineFormattingContextBuilder {
217 self.inline_formatting_context_builder
218 .get_or_insert_with(|| {
219 let mut builder = InlineFormattingContextBuilder::new(self.info);
220 for shared_inline_styles in self.display_contents_shared_styles.iter() {
221 builder.enter_display_contents(shared_inline_styles.clone());
222 }
223 builder
224 })
225 }
226
227 fn finish_ongoing_inline_formatting_context(&mut self) -> Option<InlineFormattingContext> {
228 self.inline_formatting_context_builder.take()?.finish(
229 self.context,
230 !self.have_already_seen_first_line_for_text_indent,
231 self.info.node.is_single_line_text_input(),
232 self.info.style.to_bidi_level(),
233 self.context.rendering_group_id,
234 )
235 }
236
237 pub(crate) fn finish(mut self) -> BlockContainer {
238 debug_assert!(!self.currently_processing_inline_box());
239
240 self.finish_anonymous_table_if_needed();
241
242 if let Some(inline_formatting_context) = self.finish_ongoing_inline_formatting_context() {
243 if self.block_level_boxes.is_empty() {
247 return BlockContainer::InlineFormattingContext(inline_formatting_context);
248 }
249 self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
250 }
251
252 let context = self.context;
253 let block_level_boxes = if self.context.use_rayon {
254 self.block_level_boxes
255 .into_par_iter()
256 .map(|block_level_job| block_level_job.finish(context))
257 .collect()
258 } else {
259 self.block_level_boxes
260 .into_iter()
261 .map(|block_level_job| block_level_job.finish(context))
262 .collect()
263 };
264
265 BlockContainer::BlockLevelBoxes(block_level_boxes)
266 }
267
268 fn finish_anonymous_table_if_needed(&mut self) {
269 if self.anonymous_table_content.is_empty() {
270 return;
271 }
272
273 let inline_table = self.currently_processing_inline_box();
281
282 let contents: Vec<AnonymousTableContent<'dom>> =
283 self.anonymous_table_content.drain(..).collect();
284 let last_text = match contents.last() {
285 Some(AnonymousTableContent::Text(info, text)) => Some((info.clone(), text.clone())),
286 _ => None,
287 };
288
289 let (table_info, ifc) =
290 Table::construct_anonymous(self.context, self.info, contents, self.propagated_data);
291
292 if inline_table {
293 self.ensure_inline_formatting_context_builder()
294 .push_atomic(|| ArcRefCell::new(ifc), None);
295 } else {
296 let table_block = ArcRefCell::new(BlockLevelBox::Independent(ifc));
297
298 if let Some(inline_formatting_context) = self.finish_ongoing_inline_formatting_context()
299 {
300 self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
301 }
302
303 let box_slot = table_info.node.box_slot();
304 self.block_level_boxes.push(BlockLevelJob {
305 info: table_info,
306 box_slot,
307 kind: BlockLevelCreator::AnonymousTable { table_block },
308 propagated_data: self.propagated_data,
309 });
310 }
311
312 if let Some((info, text)) = last_text {
322 self.handle_text(&info, text);
323 }
324 }
325}
326
327impl<'dom> TraversalHandler<'dom> for BlockContainerBuilder<'dom, '_> {
328 fn handle_element(
329 &mut self,
330 info: &NodeAndStyleInfo<'dom>,
331 display: DisplayGeneratingBox,
332 contents: Contents,
333 box_slot: BoxSlot<'dom>,
334 ) {
335 match display {
336 DisplayGeneratingBox::OutsideInside { outside, inside } => {
337 self.finish_anonymous_table_if_needed();
338
339 match outside {
340 DisplayOutside::Inline => {
341 self.handle_inline_level_element(info, inside, contents, box_slot)
342 },
343 DisplayOutside::Block => {
344 let box_style = info.style.get_box();
345 if box_style.position.is_absolutely_positioned() {
348 self.handle_absolutely_positioned_element(
349 info, inside, contents, box_slot,
350 )
351 } else if box_style.float.is_floating() {
352 self.handle_float_element(info, inside, contents, box_slot)
353 } else {
354 self.handle_block_level_element(info, inside, contents, box_slot)
355 }
356 },
357 };
358 },
359 DisplayGeneratingBox::LayoutInternal(_) => {
360 self.anonymous_table_content
361 .push(AnonymousTableContent::Element {
362 info: info.clone(),
363 display,
364 contents,
365 box_slot,
366 });
367 },
368 }
369 }
370
371 fn handle_text(&mut self, info: &NodeAndStyleInfo<'dom>, text: Cow<'dom, str>) {
372 if text.is_empty() {
373 return;
374 }
375
376 if !self.anonymous_table_content.is_empty() && text.chars().all(char_is_whitespace) {
380 self.anonymous_table_content
381 .push(AnonymousTableContent::Text(info.clone(), text));
382 return;
383 } else {
384 self.finish_anonymous_table_if_needed();
385 }
386
387 self.ensure_inline_formatting_context_builder()
388 .push_text(text, info);
389 }
390
391 fn enter_display_contents(&mut self, styles: SharedInlineStyles) {
392 self.display_contents_shared_styles.push(styles.clone());
393 if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
394 builder.enter_display_contents(styles);
395 }
396 }
397
398 fn leave_display_contents(&mut self) {
399 self.display_contents_shared_styles.pop();
400 if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
401 builder.leave_display_contents();
402 }
403 }
404}
405
406impl<'dom> BlockContainerBuilder<'dom, '_> {
407 fn handle_list_item_marker_inside(
408 &mut self,
409 marker_info: &NodeAndStyleInfo<'dom>,
410 contents: Vec<crate::dom_traversal::PseudoElementContentItem>,
411 ) {
412 let box_slot = marker_info.node.box_slot();
413 self.handle_inline_level_element(
414 marker_info,
415 DisplayInside::Flow {
416 is_list_item: false,
417 },
418 NonReplacedContents::OfPseudoElement(contents).into(),
419 box_slot,
420 );
421 }
422
423 fn handle_list_item_marker_outside(
424 &mut self,
425 marker_info: &NodeAndStyleInfo<'dom>,
426 contents: Vec<crate::dom_traversal::PseudoElementContentItem>,
427 list_item_style: Arc<ComputedValues>,
428 ) {
429 let box_slot = marker_info.node.box_slot();
430 self.block_level_boxes.push(BlockLevelJob {
431 info: marker_info.clone(),
432 box_slot,
433 kind: BlockLevelCreator::OutsideMarker {
434 contents,
435 list_item_style,
436 },
437 propagated_data: self.propagated_data,
438 });
439 }
440
441 fn handle_inline_level_element(
442 &mut self,
443 info: &NodeAndStyleInfo<'dom>,
444 display_inside: DisplayInside,
445 contents: Contents,
446 box_slot: BoxSlot<'dom>,
447 ) {
448 let old_layout_box = box_slot.take_layout_box_if_undamaged(info.damage);
449 let (is_list_item, non_replaced_contents) = match (display_inside, contents) {
450 (
451 DisplayInside::Flow { is_list_item },
452 Contents::NonReplaced(non_replaced_contents),
453 ) => (is_list_item, non_replaced_contents),
454 (_, contents) => {
455 let context = self.context;
457 let propagated_data = self.propagated_data;
458
459 let construction_callback = || {
460 ArcRefCell::new(IndependentFormattingContext::construct(
461 context,
462 info,
463 display_inside,
464 contents,
465 propagated_data,
466 ))
467 };
468
469 let atomic = self
470 .ensure_inline_formatting_context_builder()
471 .push_atomic(construction_callback, old_layout_box);
472 box_slot.set(LayoutBox::InlineLevel(vec![atomic]));
473 return;
474 },
475 };
476
477 self.ensure_inline_formatting_context_builder()
480 .start_inline_box(
481 || ArcRefCell::new(InlineBox::new(info)),
482 None,
483 old_layout_box,
484 );
485
486 if is_list_item {
487 if let Some((marker_info, marker_contents)) =
488 crate::lists::make_marker(self.context, info)
489 {
490 self.handle_list_item_marker_inside(&marker_info, marker_contents)
494 }
495 }
496
497 non_replaced_contents.traverse(self.context, info, self);
499
500 self.finish_anonymous_table_if_needed();
501
502 box_slot.set(LayoutBox::InlineLevel(
508 self.inline_formatting_context_builder
509 .as_mut()
510 .expect("Should be building an InlineFormattingContext")
511 .end_inline_box(),
512 ));
513 }
514
515 fn handle_block_level_element(
516 &mut self,
517 info: &NodeAndStyleInfo<'dom>,
518 display_inside: DisplayInside,
519 contents: Contents,
520 box_slot: BoxSlot<'dom>,
521 ) {
522 if let Some(inline_formatting_context) = self
529 .inline_formatting_context_builder
530 .as_mut()
531 .and_then(|builder| {
532 builder.split_around_block_and_finish(
533 self.context,
534 !self.have_already_seen_first_line_for_text_indent,
535 self.info.style.to_bidi_level(),
536 )
537 })
538 {
539 self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
540 }
541
542 let propagated_data = self.propagated_data;
543 let kind = match contents {
544 Contents::NonReplaced(contents) => match display_inside {
545 DisplayInside::Flow { is_list_item }
546 if !info.style.establishes_block_formatting_context(
549 FragmentFlags::empty()
550 ) =>
551 {
552 BlockLevelCreator::SameFormattingContextBlock(
553 IntermediateBlockContainer::Deferred {
554 contents,
555 propagated_data,
556 is_list_item,
557 },
558 )
559 },
560 _ => BlockLevelCreator::Independent {
561 display_inside,
562 contents: contents.into(),
563 },
564 },
565 Contents::Replaced(contents) => {
566 let contents = Contents::Replaced(contents);
567 BlockLevelCreator::Independent {
568 display_inside,
569 contents,
570 }
571 },
572 };
573 self.block_level_boxes.push(BlockLevelJob {
574 info: info.clone(),
575 box_slot,
576 kind,
577 propagated_data,
578 });
579
580 self.have_already_seen_first_line_for_text_indent = true;
583 }
584
585 fn handle_absolutely_positioned_element(
586 &mut self,
587 info: &NodeAndStyleInfo<'dom>,
588 display_inside: DisplayInside,
589 contents: Contents,
590 box_slot: BoxSlot<'dom>,
591 ) {
592 let needs_inline_builder =
598 info.style.get_box().original_display.outside() == StyloDisplayOutside::Inline;
599 if needs_inline_builder {
600 self.ensure_inline_formatting_context_builder();
601 }
602 let inline_builder = self
603 .inline_formatting_context_builder
604 .as_mut()
605 .filter(|builder| needs_inline_builder || !builder.is_empty);
606 if let Some(inline_builder) = inline_builder {
607 let constructor = || {
608 ArcRefCell::new(AbsolutelyPositionedBox::construct(
609 self.context,
610 info,
611 display_inside,
612 contents,
613 ))
614 };
615 let old_layout_box = box_slot.take_layout_box_if_undamaged(info.damage);
616 let inline_level_box =
617 inline_builder.push_absolutely_positioned_box(constructor, old_layout_box);
618 box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box]));
619 return;
620 }
621
622 let kind = BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox {
623 contents,
624 display_inside,
625 };
626 self.block_level_boxes.push(BlockLevelJob {
627 info: info.clone(),
628 box_slot,
629 kind,
630 propagated_data: self.propagated_data,
631 });
632 }
633
634 fn handle_float_element(
635 &mut self,
636 info: &NodeAndStyleInfo<'dom>,
637 display_inside: DisplayInside,
638 contents: Contents,
639 box_slot: BoxSlot<'dom>,
640 ) {
641 if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
642 if !builder.is_empty {
643 let constructor = || {
644 ArcRefCell::new(FloatBox::construct(
645 self.context,
646 info,
647 display_inside,
648 contents,
649 self.propagated_data,
650 ))
651 };
652 let old_layout_box = box_slot.take_layout_box_if_undamaged(info.damage);
653 let inline_level_box = builder.push_float_box(constructor, old_layout_box);
654 box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box]));
655 return;
656 }
657 }
658
659 let kind = BlockLevelCreator::OutOfFlowFloatBox {
660 contents,
661 display_inside,
662 };
663 self.block_level_boxes.push(BlockLevelJob {
664 info: info.clone(),
665 box_slot,
666 kind,
667 propagated_data: self.propagated_data,
668 });
669 }
670
671 fn push_block_level_job_for_inline_formatting_context(
672 &mut self,
673 inline_formatting_context: InlineFormattingContext,
674 ) {
675 let layout_context = self.context;
676 let anonymous_info = self
677 .anonymous_box_info
678 .get_or_insert_with(|| {
679 self.info
680 .with_pseudo_element(layout_context, PseudoElement::ServoAnonymousBox)
681 .expect("Should never fail to create anonymous box")
682 })
683 .clone();
684
685 let box_slot = anonymous_info.node.box_slot();
686 self.block_level_boxes.push(BlockLevelJob {
687 info: anonymous_info,
688 box_slot,
689 kind: BlockLevelCreator::SameFormattingContextBlock(
690 IntermediateBlockContainer::InlineFormattingContext(
691 BlockContainer::InlineFormattingContext(inline_formatting_context),
692 ),
693 ),
694 propagated_data: self.propagated_data,
695 });
696
697 self.have_already_seen_first_line_for_text_indent = true;
698 }
699}
700
701impl BlockLevelJob<'_> {
702 fn finish(self, context: &LayoutContext) -> ArcRefCell<BlockLevelBox> {
703 let info = &self.info;
704
705 if !info.damage.has_box_damage() {
708 if let Some(block_level_box) = match self.box_slot.slot.as_ref() {
709 Some(box_slot) => match &*box_slot.borrow() {
710 Some(LayoutBox::BlockLevel(block_level_box)) => Some(block_level_box.clone()),
711 _ => None,
712 },
713 None => None,
714 } {
715 return block_level_box;
716 }
717 }
718
719 let block_level_box = match self.kind {
720 BlockLevelCreator::SameFormattingContextBlock(intermediate_block_container) => {
721 let contents = intermediate_block_container.finish(context, info);
722 let contains_floats = contents.contains_floats();
723 ArcRefCell::new(BlockLevelBox::SameFormattingContextBlock {
724 base: LayoutBoxBase::new(info.into(), info.style.clone()),
725 contents,
726 contains_floats,
727 })
728 },
729 BlockLevelCreator::Independent {
730 display_inside,
731 contents,
732 } => {
733 let context = IndependentFormattingContext::construct(
734 context,
735 info,
736 display_inside,
737 contents,
738 self.propagated_data,
739 );
740 ArcRefCell::new(BlockLevelBox::Independent(context))
741 },
742 BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox {
743 display_inside,
744 contents,
745 } => ArcRefCell::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
746 ArcRefCell::new(AbsolutelyPositionedBox::construct(
747 context,
748 info,
749 display_inside,
750 contents,
751 )),
752 )),
753 BlockLevelCreator::OutOfFlowFloatBox {
754 display_inside,
755 contents,
756 } => ArcRefCell::new(BlockLevelBox::OutOfFlowFloatBox(FloatBox::construct(
757 context,
758 info,
759 display_inside,
760 contents,
761 self.propagated_data,
762 ))),
763 BlockLevelCreator::OutsideMarker {
764 contents,
765 list_item_style,
766 } => {
767 let contents = NonReplacedContents::OfPseudoElement(contents);
768 let block_container = BlockContainer::construct(
769 context,
770 info,
771 contents,
772 self.propagated_data,
773 false, );
775 let block_formatting_context = BlockFormattingContext {
777 contents: block_container,
778 contains_floats: false,
779 };
780 ArcRefCell::new(BlockLevelBox::OutsideMarker(OutsideMarker {
781 base: LayoutBoxBase::new(info.into(), info.style.clone()),
782 block_formatting_context,
783 list_item_style,
784 }))
785 },
786 BlockLevelCreator::AnonymousTable { table_block } => table_block,
787 };
788 self.box_slot
789 .set(LayoutBox::BlockLevel(block_level_box.clone()));
790 block_level_box
791 }
792}
793
794impl IntermediateBlockContainer {
795 fn finish(self, context: &LayoutContext, info: &NodeAndStyleInfo<'_>) -> BlockContainer {
796 match self {
797 IntermediateBlockContainer::Deferred {
798 contents,
799 propagated_data,
800 is_list_item,
801 } => BlockContainer::construct(context, info, contents, propagated_data, is_list_item),
802 IntermediateBlockContainer::InlineFormattingContext(block_container) => block_container,
803 }
804 }
805}