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