1use std::ops::Range;
6use std::sync::Arc;
7
8use app_units::Au;
9use bitflags::bitflags;
10use fonts::ShapedTextSlice;
11use itertools::Either;
12use servo_base::text::Utf32CodeUnits;
13use style::Zero;
14use style::computed_values::position::T as Position;
15use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
16use style::values::computed::BaselineShift;
17use style::values::generics::box_::BaselineShiftKeyword;
18use style::values::specified::align::AlignFlags;
19use style::values::specified::box_::DisplayOutside;
20use unicode_bidi::{BidiInfo, Level};
21
22use super::inline_box::{InlineBoxContainerState, InlineBoxIdentifier, InlineBoxTreePathToken};
23use super::{InlineFormattingContextLayout, LineBlockSizes, line_height};
24use crate::cell::ArcRefCell;
25use crate::flow::inline::text_run::{FontAndScriptInfo, SharedTextRunData};
26use crate::fragment_tree::{BaseFragment, BaseFragmentInfo, BoxFragment, Fragment, TextFragment};
27use crate::geom::{
28 LogicalRect, LogicalSides, LogicalVec2, PhysicalRect, PhysicalSize, ToLogical,
29 ToLogicalWithContainingBlock,
30};
31use crate::positioned::{
32 AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, relative_adjustement,
33};
34use crate::{ContainingBlock, ContainingBlockSize};
35
36pub(super) struct LineMetrics {
37 pub block_offset: Au,
40
41 pub block_size: Au,
43
44 pub baseline_block_offset: Au,
46}
47
48bitflags! {
49 struct LineLayoutInlineContainerFlags: u8 {
50 const HAD_ANY_LINE_ITEMS = 1 << 0;
53 const HAD_INLINE_START_PBM = 1 << 2;
56 const HAD_INLINE_END_PBM = 1 << 3;
59 const HAD_ANY_FLOATS = 1 << 4;
61 }
62}
63
64struct FragmentAndData {
65 fragment: Fragment,
66
67 logical_rect: LogicalRect<Au>,
72
73 propagated_floats: Vec<Arc<BoxFragment>>,
77}
78
79impl FragmentAndData {
80 fn new(fragment: Fragment, logical_rect: LogicalRect<Au>) -> Self {
81 Self::new_with_propagated_floats(fragment, logical_rect, Vec::new())
82 }
83
84 fn new_with_propagated_floats(
85 fragment: Fragment,
86 logical_rect: LogicalRect<Au>,
87 propagated_floats: Vec<Arc<BoxFragment>>,
88 ) -> Self {
89 Self {
90 fragment,
91 logical_rect,
92 propagated_floats,
93 }
94 }
95
96 fn resolve_physical_rect_and_adjust_floats(&self, container: &ContainingBlock) {
103 debug_assert!(!matches!(self.fragment, Fragment::Float(_)));
104
105 let Some(base) = self.fragment.base() else {
106 return;
107 };
108
109 let rect = self.logical_rect.as_physical(Some(container));
113 base.set_rect(rect);
114
115 let float_offset = -rect.origin.to_vector().to_size();
120 for float_fragment in &self.propagated_floats {
121 float_fragment.base.translate_rect(float_offset);
122 }
123 }
124
125 fn resolve_physical_rects_and_adjust_floats(
129 fragments_and_data: Vec<Self>,
130 container: &ContainingBlock,
131 ) -> Vec<Fragment> {
132 let mut fragments = Vec::with_capacity(fragments_and_data.len());
133 for fragment_and_data in fragments_and_data {
134 if !matches!(fragment_and_data.fragment, Fragment::Float(_)) {
135 fragment_and_data.resolve_physical_rect_and_adjust_floats(container)
136 }
137 fragments.push(fragment_and_data.fragment);
138 }
139 fragments
140 }
141
142 fn resolve_physical_rects_and_adjust_and_collect_floats(
146 fragments_and_data: Vec<Self>,
147 container: &ContainingBlock,
148 relative_adjustement: PhysicalSize<Au>,
149 ) -> (Vec<Fragment>, Vec<Arc<BoxFragment>>) {
150 let mut fragments = Vec::with_capacity(fragments_and_data.len());
151 let mut propagated_floats = Vec::new();
152 for mut fragment_and_data in fragments_and_data {
153 if let Fragment::Float(ref float) = fragment_and_data.fragment {
154 if relative_adjustement != PhysicalSize::zero() {
155 float.base.translate_rect(relative_adjustement);
156 }
157 propagated_floats.push(float.clone());
158 } else {
159 fragment_and_data.resolve_physical_rect_and_adjust_floats(container)
160 }
161 fragments.push(fragment_and_data.fragment);
162 propagated_floats.append(&mut fragment_and_data.propagated_floats);
163 }
164 (fragments, propagated_floats)
165 }
166}
167
168pub(super) struct LineItemLayoutInlineContainerState {
172 pub identifier: Option<InlineBoxIdentifier>,
175
176 fragments_and_data: Vec<FragmentAndData>,
178
179 pub inline_advance: Au,
181
182 flags: LineLayoutInlineContainerFlags,
184
185 pub parent_offset: LogicalVec2<Au>,
189
190 pub baseline_offset: Au,
194
195 pub positioning_context_or_start_offset_in_parent:
200 Either<PositioningContext, PositioningContextLength>,
201}
202
203impl LineItemLayoutInlineContainerState {
204 fn new(
205 identifier: Option<InlineBoxIdentifier>,
206 parent_offset: LogicalVec2<Au>,
207 baseline_offset: Au,
208 positioning_context_or_start_offset_in_parent: Either<
209 PositioningContext,
210 PositioningContextLength,
211 >,
212 ) -> Self {
213 Self {
214 identifier,
215 fragments_and_data: Vec::new(),
216 inline_advance: Au::zero(),
217 flags: LineLayoutInlineContainerFlags::empty(),
218 parent_offset,
219 baseline_offset,
220 positioning_context_or_start_offset_in_parent,
221 }
222 }
223
224 fn root(starting_inline_advance: Au, baseline_offset: Au) -> Self {
225 let mut state = Self::new(
226 None,
227 LogicalVec2::zero(),
228 baseline_offset,
229 Either::Right(PositioningContextLength::zero()),
230 );
231 state.inline_advance = starting_inline_advance;
232 state
233 }
234}
235
236pub(super) struct LineItemLayout<'layout_data, 'layout> {
240 layout: &'layout mut InlineFormattingContextLayout<'layout_data>,
242
243 pub state_stack: Vec<LineItemLayoutInlineContainerState>,
246
247 pub current_state: LineItemLayoutInlineContainerState,
249
250 pub line_metrics: LineMetrics,
253
254 pub justification_adjustment: Au,
257
258 is_phantom_line: bool,
261
262 for_block_level: bool,
264}
265
266impl LineItemLayout<'_, '_> {
267 pub(super) fn layout_line_items(
268 layout: &mut InlineFormattingContextLayout,
269 line_items: Vec<LineItem>,
270 start_position: LogicalVec2<Au>,
271 effective_block_advance: &LineBlockSizes,
272 justification_adjustment: Au,
273 is_phantom_line: bool,
274 for_block_level: bool,
275 ) -> Vec<Fragment> {
276 let baseline_offset = effective_block_advance.find_baseline_offset();
277 LineItemLayout {
278 layout,
279 state_stack: Vec::new(),
280 current_state: LineItemLayoutInlineContainerState::root(
281 start_position.inline,
282 baseline_offset,
283 ),
284 line_metrics: LineMetrics {
285 block_offset: start_position.block,
286 block_size: effective_block_advance.resolve(),
287 baseline_block_offset: baseline_offset,
288 },
289 justification_adjustment,
290 is_phantom_line,
291 for_block_level,
292 }
293 .layout(line_items)
294 }
295
296 fn prepare_layout_for_inline_box(&mut self, new_inline_box: Option<InlineBoxIdentifier>) {
298 let Some(new_inline_box) = new_inline_box else {
300 while !self.state_stack.is_empty() {
301 self.end_inline_box();
302 }
303 return;
304 };
305
306 let path = self
309 .layout
310 .ifc
311 .inline_boxes
312 .get_path(self.current_state.identifier, new_inline_box);
313 for token in path {
314 match token {
315 InlineBoxTreePathToken::Start(ref identifier) => self.start_inline_box(identifier),
316 InlineBoxTreePathToken::End(_) => self.end_inline_box(),
317 }
318 }
319 }
320
321 fn reorder_line_items_for_bidi(
327 &self,
328 mut line_items: Vec<LineItem>,
329 ) -> impl Iterator<Item = LineItem> + use<> {
330 let iterator = |line_items: Vec<LineItem>| {
331 if self.containing_block().style.writing_mode.is_bidi_ltr() {
338 Either::Left(line_items.into_iter())
339 } else {
340 Either::Right(line_items.into_iter().rev())
341 }
342 };
343
344 if !self.layout.ifc.has_right_to_left_content {
345 return iterator(line_items);
350 }
351
352 let mut last_level = Level::ltr();
353 let levels: Vec<_> = line_items
354 .iter()
355 .map(|item| {
356 let level = match item {
357 LineItem::TextRun(_, text_run) => text_run.info.font_info.bidi_level,
358 LineItem::InlineStartBoxPaddingBorderMargin(_) => last_level,
362 LineItem::InlineEndBoxPaddingBorderMargin(_) => last_level,
363 LineItem::Atomic(_, atomic) => atomic.bidi_level,
364 LineItem::AbsolutelyPositioned(..) => last_level,
365 LineItem::Float(..) => {
366 last_level
369 },
370 LineItem::BlockLevel(..) => last_level,
371 LineItem::Tab { bidi_level, .. } => *bidi_level,
372 };
373 last_level = level;
374 level
375 })
376 .collect();
377
378 sort_by_indices_in_place(&mut line_items, BidiInfo::reorder_visual(&levels));
379 iterator(line_items)
380 }
381
382 pub(super) fn layout(&mut self, line_items: Vec<LineItem>) -> Vec<Fragment> {
383 let line_item_iterator = self.reorder_line_items_for_bidi(line_items);
384 for item in line_item_iterator.into_iter().by_ref() {
385 self.prepare_layout_for_inline_box(item.inline_box_identifier());
390
391 self.current_state
392 .flags
393 .insert(LineLayoutInlineContainerFlags::HAD_ANY_LINE_ITEMS);
394 match item {
395 LineItem::InlineStartBoxPaddingBorderMargin(_) => {
396 self.current_state
397 .flags
398 .insert(LineLayoutInlineContainerFlags::HAD_INLINE_START_PBM);
399 },
400 LineItem::InlineEndBoxPaddingBorderMargin(_) => {
401 self.current_state
402 .flags
403 .insert(LineLayoutInlineContainerFlags::HAD_INLINE_END_PBM);
404 },
405 LineItem::TextRun(_, text_run) => self.layout_text_run(text_run),
406 LineItem::Atomic(_, atomic) => self.layout_atomic(atomic),
407 LineItem::AbsolutelyPositioned(_, absolute) => self.layout_absolute(absolute),
408 LineItem::Float(_, float) => self.layout_float(float),
409 LineItem::BlockLevel(_, block_level) => self.layout_block_level(block_level),
410 LineItem::Tab { advance, .. } => self.layout_tab(advance),
411 }
412 }
413
414 self.prepare_layout_for_inline_box(None);
416
417 FragmentAndData::resolve_physical_rects_and_adjust_floats(
418 std::mem::take(&mut self.current_state.fragments_and_data),
419 self.layout.containing_block(),
420 )
421 }
422
423 fn current_positioning_context_mut(&mut self) -> &mut PositioningContext {
424 if let Either::Left(ref mut positioning_context) = self
425 .current_state
426 .positioning_context_or_start_offset_in_parent
427 {
428 return positioning_context;
429 }
430 self.state_stack
431 .iter_mut()
432 .rev()
433 .find_map(
434 |state| match state.positioning_context_or_start_offset_in_parent {
435 Either::Left(ref mut positioning_context) => Some(positioning_context),
436 Either::Right(_) => None,
437 },
438 )
439 .unwrap_or(self.layout.positioning_context)
440 }
441
442 fn start_inline_box(&mut self, identifier: &InlineBoxIdentifier) {
443 let inline_box_state =
444 &*self.layout.inline_box_states[identifier.index_in_inline_boxes as usize];
445 let inline_box = self.layout.ifc.inline_boxes.get(identifier);
446 let inline_box = &*(inline_box.borrow());
447
448 let space_above_baseline = inline_box_state.calculate_space_above_baseline();
449 let block_start_offset =
450 self.calculate_inline_box_block_start(inline_box_state, space_above_baseline);
451
452 let positioning_context_or_start_offset_in_parent =
453 match PositioningContext::new_for_layout_box_base(&inline_box.base) {
454 Some(positioning_context) => Either::Left(positioning_context),
455 None => Either::Right(self.current_positioning_context_mut().len()),
456 };
457
458 let parent_offset = LogicalVec2 {
459 inline: self.current_state.inline_advance + self.current_state.parent_offset.inline,
460 block: block_start_offset,
461 };
462
463 let outer_state = std::mem::replace(
464 &mut self.current_state,
465 LineItemLayoutInlineContainerState::new(
466 Some(*identifier),
467 parent_offset,
468 block_start_offset + space_above_baseline,
469 positioning_context_or_start_offset_in_parent,
470 ),
471 );
472
473 self.state_stack.push(outer_state);
474 }
475
476 fn end_inline_box(&mut self) {
477 let outer_state = self.state_stack.pop().expect("Ended unknown inline box");
478 let inner_state = std::mem::replace(&mut self.current_state, outer_state);
479
480 let identifier = inner_state.identifier.expect("Ended unknown inline box");
481 let inline_box_state =
482 &*self.layout.inline_box_states[identifier.index_in_inline_boxes as usize];
483 let inline_box = self.layout.ifc.inline_boxes.get(&identifier);
484 let inline_box = &*(inline_box.borrow());
485
486 let containing_block = self.layout.containing_block();
487 let containing_block_writing_mode = containing_block.style.writing_mode;
488
489 let mut padding = inline_box_state.pbm.padding;
490 let mut border = inline_box_state.pbm.border;
491 let mut margin = inline_box_state.pbm.margin.auto_is(Au::zero);
492 if self.for_block_level {
495 padding = LogicalSides::zero();
496 border = LogicalSides::zero();
497 margin = LogicalSides::zero();
498 } else if !inline_box_state.should_clone_pbm() {
499 let mut had_start = inner_state
500 .flags
501 .contains(LineLayoutInlineContainerFlags::HAD_INLINE_START_PBM);
502 let mut had_end = inner_state
503 .flags
504 .contains(LineLayoutInlineContainerFlags::HAD_INLINE_END_PBM);
505
506 if containing_block_writing_mode.is_bidi_ltr() !=
507 inline_box.base.style.writing_mode.is_bidi_ltr()
508 {
509 std::mem::swap(&mut had_start, &mut had_end)
510 }
511
512 if !had_start {
513 padding.inline_start = Au::zero();
514 border.inline_start = Au::zero();
515 margin.inline_start = Au::zero();
516 }
517 if !had_end {
518 padding.inline_end = Au::zero();
519 border.inline_end = Au::zero();
520 margin.inline_end = Au::zero();
521 }
522 }
523 let pbm_sums = padding + border + margin;
524
525 let mut content_rect = LogicalRect {
527 start_corner: LogicalVec2 {
528 inline: self.current_state.inline_advance + pbm_sums.inline_start,
529 block: inner_state.parent_offset.block - self.current_state.parent_offset.block,
530 },
531 size: LogicalVec2 {
532 inline: inner_state.inline_advance,
533 block: if self.is_phantom_line {
534 Au::zero()
535 } else {
536 inline_box_state.base.font_metrics.line_gap
537 },
538 },
539 };
540
541 let style = &inline_box.base.style;
544 let relative_adjustement = if style.get_box().position == Position::Relative {
545 let relative_adjustement = relative_adjustement(style, containing_block);
546 content_rect.start_corner += relative_adjustement;
547 relative_adjustement
548 .to_physical_vector(containing_block_writing_mode)
549 .to_size()
550 } else {
551 PhysicalSize::zero()
552 };
553
554 let (fragments, propagated_floats) =
555 FragmentAndData::resolve_physical_rects_and_adjust_and_collect_floats(
556 inner_state.fragments_and_data,
557 &ContainingBlock {
558 size: ContainingBlockSize {
559 inline: content_rect.size.inline,
560 block: Default::default(),
561 },
562 style: containing_block.style,
563 },
564 relative_adjustement,
565 );
566
567 let physical_content_rect = content_rect.as_physical(Some(containing_block));
570
571 let mut fragment = BoxFragment::new(
572 inline_box.base.base_fragment_info,
573 style.clone(),
574 fragments,
575 physical_content_rect,
576 padding.to_physical(containing_block_writing_mode),
577 border.to_physical(containing_block_writing_mode),
578 margin.to_physical(containing_block_writing_mode),
579 None, );
581
582 let offset_from_parent_ifc = LogicalVec2 {
583 inline: pbm_sums.inline_start + self.current_state.inline_advance,
584 block: content_rect.start_corner.block,
585 }
586 .to_physical_vector(containing_block_writing_mode);
587
588 match inner_state.positioning_context_or_start_offset_in_parent {
589 Either::Left(mut positioning_context) => {
590 positioning_context
591 .layout_collected_children(self.layout.layout_context, &mut fragment);
592 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
593 &offset_from_parent_ifc,
594 PositioningContextLength::zero(),
595 );
596 self.current_positioning_context_mut()
597 .append(positioning_context);
598 },
599 Either::Right(start_offset) => {
600 self.current_positioning_context_mut()
601 .adjust_static_position_of_hoisted_fragments_with_offset(
602 &offset_from_parent_ifc,
603 start_offset,
604 );
605 },
606 }
607
608 self.current_state.inline_advance += inner_state.inline_advance + pbm_sums.inline_sum();
609
610 let fragment = Fragment::Box(Arc::new(fragment));
611 inline_box.base.add_fragment(fragment.clone());
612 self.current_state
613 .fragments_and_data
614 .push(FragmentAndData::new_with_propagated_floats(
615 fragment,
616 content_rect,
617 propagated_floats,
618 ));
619 }
620
621 fn calculate_inline_box_block_start(
622 &self,
623 inline_box_state: &InlineBoxContainerState,
624 space_above_baseline: Au,
625 ) -> Au {
626 if self.is_phantom_line {
627 return Au::zero();
628 };
629 let font_metrics = &inline_box_state.base.font_metrics;
630 let style = &inline_box_state.base.style;
631 let line_gap = font_metrics.line_gap;
632
633 match inline_box_state.base.style.clone_baseline_shift() {
636 BaselineShift::Keyword(BaselineShiftKeyword::Top) => {
637 let line_height = line_height(style, font_metrics, &inline_box_state.base.flags);
638 (line_height - line_gap).scale_by(0.5)
639 },
640 BaselineShift::Keyword(BaselineShiftKeyword::Center) => {
641 (self.line_metrics.block_size - line_gap).scale_by(0.5)
642 },
643 BaselineShift::Keyword(BaselineShiftKeyword::Bottom) => {
644 let line_height = line_height(style, font_metrics, &inline_box_state.base.flags);
645 let half_leading = (line_height - line_gap).scale_by(0.5);
646 self.line_metrics.block_size - line_height + half_leading
647 },
648 _ => {
649 self.line_metrics.baseline_block_offset + inline_box_state.base.baseline_offset -
650 space_above_baseline
651 },
652 }
653 }
654
655 fn layout_text_run(&mut self, text_item: TextRunLineItem) {
656 if text_item.text.is_empty() && !text_item.is_empty_for_text_cursor {
657 return;
658 }
659
660 let mut number_of_justification_opportunities = 0;
661 let mut inline_advance = text_item
662 .text
663 .iter()
664 .map(|shaped_text_slice| {
665 number_of_justification_opportunities += shaped_text_slice.total_word_separators();
666 shaped_text_slice.total_advance()
667 })
668 .sum();
669
670 if !self.justification_adjustment.is_zero() {
671 inline_advance += self
672 .justification_adjustment
673 .scale_by(number_of_justification_opportunities as f32);
674 }
675
676 let font_metrics = &text_item.info.font_info.font.metrics;
680 let start_corner = LogicalVec2 {
681 inline: self.current_state.inline_advance,
682 block: self.current_state.baseline_offset -
683 font_metrics.ascent -
684 self.current_state.parent_offset.block,
685 };
686 let content_rect = LogicalRect {
687 start_corner,
688 size: LogicalVec2 {
689 block: font_metrics.line_gap,
690 inline: inline_advance,
691 },
692 };
693
694 let font_key = text_item.info.font_info.font.key(
695 self.layout.layout_context.painter_id,
696 &self.layout.layout_context.font_context,
697 );
698
699 self.current_state.inline_advance += inline_advance;
700 self.current_state
701 .fragments_and_data
702 .push(FragmentAndData::new(
703 Fragment::Text(Arc::new(TextFragment {
704 base: BaseFragment::new(text_item.base_fragment_info, PhysicalRect::zero()),
705 run_data: text_item.text_fragment_run_data,
706 font_metrics: font_metrics.clone(),
707 font_key,
708 glyphs: text_item.text,
709 justification_adjustment: self.justification_adjustment,
710 character_range_in_dom_node: text_item.character_range_in_dom_node,
711 is_empty_for_text_cursor: text_item.is_empty_for_text_cursor,
712 })),
713 content_rect,
714 ));
715 }
716
717 fn layout_atomic(&mut self, atomic: AtomicLineItem) {
718 let containing_block = self.containing_block();
723 let ifc_writing_mode = containing_block.style.writing_mode;
724 let content_rect = {
725 let atomic_fragment = &atomic.fragment;
726 let block_start = atomic.calculate_block_start(&self.line_metrics);
727 let padding_border_margin_sides = atomic_fragment
728 .padding_border_margin()
729 .to_logical(ifc_writing_mode);
730
731 let mut atomic_offset = LogicalVec2 {
732 inline: self.current_state.inline_advance +
733 padding_border_margin_sides.inline_start,
734 block: block_start - self.current_state.parent_offset.block +
735 padding_border_margin_sides.block_start,
736 };
737
738 let style = atomic_fragment.style();
739 if style.get_box().position == Position::Relative {
740 atomic_offset += relative_adjustement(&style, containing_block);
741 }
742
743 LogicalRect {
746 start_corner: atomic_offset,
747 size: atomic_fragment
748 .content_rect()
749 .size
750 .to_logical(ifc_writing_mode),
751 }
752 };
753
754 if let Some(mut positioning_context) = atomic.positioning_context {
755 let physical_rect_as_if_in_root = content_rect.as_physical(Some(containing_block));
756 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
757 &physical_rect_as_if_in_root.origin.to_vector(),
758 PositioningContextLength::zero(),
759 );
760
761 self.current_positioning_context_mut()
762 .append(positioning_context);
763 }
764
765 self.current_state.inline_advance += atomic.size.inline;
766
767 self.current_state
768 .fragments_and_data
769 .push(FragmentAndData::new(
770 Fragment::Box(atomic.fragment),
771 content_rect,
772 ));
773 }
774
775 fn layout_absolute(&mut self, absolute: AbsolutelyPositionedLineItem) {
776 let absolutely_positioned_box = (*absolute.absolutely_positioned_box).borrow();
777 let style = absolutely_positioned_box.context.style();
778
779 let block_position = self.layout.placement_state.current_margin.solve() -
792 self.current_state.parent_offset.block;
793 let initial_start_corner =
794 if style.get_box().original_display.outside() == DisplayOutside::Inline {
795 LogicalVec2 {
797 inline: self.current_state.inline_advance,
798 block: block_position,
799 }
800 } else {
801 LogicalVec2 {
805 inline: -self.current_state.parent_offset.inline,
806 block: if absolute.preceding_line_content_would_produce_phantom_line {
807 block_position
808 } else {
809 block_position + self.line_metrics.block_size
810 },
811 }
812 };
813
814 let containing_block = self.containing_block();
817 let static_position_rect = LogicalRect {
818 start_corner: initial_start_corner,
819 size: LogicalVec2::zero(),
820 }
821 .as_physical(Some(containing_block));
822
823 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
824 absolute.absolutely_positioned_box.clone(),
825 static_position_rect,
826 LogicalVec2 {
827 inline: AlignFlags::START,
828 block: AlignFlags::START,
829 },
830 containing_block.style.writing_mode,
831 );
832
833 let hoisted_fragment = hoisted_box.fragment.clone();
834 self.current_positioning_context_mut().push(hoisted_box);
835 self.current_state
836 .fragments_and_data
837 .push(FragmentAndData::new(
838 Fragment::AbsoluteOrFixedPositionedPlaceholder(hoisted_fragment),
839 LogicalRect::zero(),
840 ));
841 }
842
843 fn layout_float(&mut self, float: FloatLineItem) {
844 self.current_state
845 .flags
846 .insert(LineLayoutInlineContainerFlags::HAD_ANY_FLOATS);
847 let offset = LogicalVec2 {
857 inline: Au::zero(),
858 block: -self.line_metrics.block_offset,
859 };
860 float
861 .fragment
862 .base
863 .translate_rect(offset.to_physical_size(self.containing_block().style.writing_mode));
864 self.current_state
865 .fragments_and_data
866 .push(FragmentAndData::new(
867 Fragment::Float(float.fragment),
868 LogicalRect::zero(),
869 ));
870 }
871
872 fn layout_block_level(&mut self, block_level: Arc<BoxFragment>) {
873 let containing_block = self.containing_block();
874 let mut content_rect = block_level.content_rect().to_logical(containing_block);
875 content_rect.start_corner.inline -= self.current_state.parent_offset.inline;
877 content_rect.start_corner.block -= self.line_metrics.block_offset;
878 self.current_state
879 .fragments_and_data
880 .push(FragmentAndData::new(
881 Fragment::Box(block_level),
882 content_rect,
883 ));
884 }
885
886 #[inline]
887 fn containing_block(&self) -> &ContainingBlock<'_> {
888 self.layout.containing_block()
889 }
890
891 fn layout_tab(&mut self, advance: Au) {
892 self.current_state.inline_advance += advance;
893 }
894}
895
896pub(super) enum LineItem {
897 InlineStartBoxPaddingBorderMargin(InlineBoxIdentifier),
898 InlineEndBoxPaddingBorderMargin(InlineBoxIdentifier),
899 TextRun(Option<InlineBoxIdentifier>, TextRunLineItem),
900 Atomic(Option<InlineBoxIdentifier>, AtomicLineItem),
901 AbsolutelyPositioned(Option<InlineBoxIdentifier>, AbsolutelyPositionedLineItem),
902 Float(Option<InlineBoxIdentifier>, FloatLineItem),
903 BlockLevel(Option<InlineBoxIdentifier>, Arc<BoxFragment>),
904 Tab {
905 inline_box_identifier: Option<InlineBoxIdentifier>,
906 advance: Au,
907 bidi_level: Level,
908 },
909}
910
911impl LineItem {
912 pub(crate) fn is_in_flow_content(&self) -> bool {
913 matches!(
914 self,
915 Self::TextRun(..) | Self::Atomic(..) | Self::BlockLevel(..)
916 )
917 }
918
919 fn inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
920 match self {
921 LineItem::InlineStartBoxPaddingBorderMargin(identifier) => Some(*identifier),
922 LineItem::InlineEndBoxPaddingBorderMargin(identifier) => Some(*identifier),
923 LineItem::TextRun(identifier, _) => *identifier,
924 LineItem::Atomic(identifier, _) => *identifier,
925 LineItem::AbsolutelyPositioned(identifier, _) => *identifier,
926 LineItem::Float(identifier, _) => *identifier,
927 LineItem::BlockLevel(identifier, _) => *identifier,
928 LineItem::Tab {
929 inline_box_identifier,
930 ..
931 } => *inline_box_identifier,
932 }
933 }
934
935 pub(super) fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
936 match self {
937 LineItem::InlineStartBoxPaddingBorderMargin(_) => true,
938 LineItem::InlineEndBoxPaddingBorderMargin(_) => true,
939 LineItem::TextRun(_, item) => item.trim_whitespace_at_end(whitespace_trimmed),
940 LineItem::Atomic(..) => false,
941 LineItem::AbsolutelyPositioned(..) => true,
942 LineItem::Float(..) => true,
943 LineItem::BlockLevel(..) => true,
944 LineItem::Tab { .. } => false,
945 }
946 }
947
948 pub(super) fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
949 match self {
950 LineItem::InlineStartBoxPaddingBorderMargin(_) => true,
951 LineItem::InlineEndBoxPaddingBorderMargin(_) => true,
952 LineItem::TextRun(_, item) => item.trim_whitespace_at_start(whitespace_trimmed),
953 LineItem::Atomic(..) => false,
954 LineItem::AbsolutelyPositioned(..) => true,
955 LineItem::Float(..) => true,
956 LineItem::BlockLevel(..) => true,
957 LineItem::Tab { .. } => false,
958 }
959 }
960}
961
962pub(super) struct TextRunLineItem {
963 pub text_fragment_run_data: Arc<SharedTextRunData>,
964 pub info: FontAndScriptInfo,
965 pub base_fragment_info: BaseFragmentInfo,
966 pub text: Vec<Arc<ShapedTextSlice>>,
967 pub character_range_in_dom_node: Range<Utf32CodeUnits>,
970 pub is_empty_for_text_cursor: bool,
973}
974
975impl TextRunLineItem {
976 fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
977 if matches!(
978 self.text_fragment_run_data
979 .inline_styles
980 .style
981 .borrow()
982 .get_inherited_text()
983 .white_space_collapse,
984 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
985 ) {
986 return false;
987 }
988
989 let index_of_last_non_whitespace = self
990 .text
991 .iter()
992 .rev()
993 .position(|glyph| !glyph.is_whitespace())
994 .map(|offset_from_end| self.text.len() - offset_from_end);
995
996 let first_whitespace_index = index_of_last_non_whitespace.unwrap_or(0);
997 *whitespace_trimmed += self
998 .text
999 .drain(first_whitespace_index..)
1000 .map(|glyph| glyph.total_advance())
1001 .sum();
1002
1003 index_of_last_non_whitespace.is_none()
1005 }
1006
1007 fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
1008 if matches!(
1009 self.text_fragment_run_data
1010 .inline_styles
1011 .style
1012 .borrow()
1013 .get_inherited_text()
1014 .white_space_collapse,
1015 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
1016 ) {
1017 return false;
1018 }
1019
1020 let index_of_first_non_whitespace = self
1021 .text
1022 .iter()
1023 .position(|glyph| !glyph.is_whitespace())
1024 .unwrap_or(self.text.len());
1025
1026 *whitespace_trimmed += self
1027 .text
1028 .drain(0..index_of_first_non_whitespace)
1029 .map(|glyph| glyph.total_advance())
1030 .sum();
1031
1032 self.text.is_empty()
1034 }
1035}
1036
1037pub(super) struct AtomicLineItem {
1038 pub fragment: Arc<BoxFragment>,
1039 pub size: LogicalVec2<Au>,
1040 pub positioning_context: Option<PositioningContext>,
1041
1042 pub baseline_offset_in_parent: Au,
1046
1047 pub baseline_offset_in_item: Au,
1049
1050 pub bidi_level: Level,
1052}
1053
1054impl AtomicLineItem {
1055 fn calculate_block_start(&self, line_metrics: &LineMetrics) -> Au {
1058 match self.fragment.style().clone_baseline_shift() {
1059 BaselineShift::Keyword(BaselineShiftKeyword::Top) => Au::zero(),
1060 BaselineShift::Keyword(BaselineShiftKeyword::Center) => {
1061 (line_metrics.block_size - self.size.block).scale_by(0.5)
1062 },
1063 BaselineShift::Keyword(BaselineShiftKeyword::Bottom) => {
1064 line_metrics.block_size - self.size.block
1065 },
1066
1067 _ => {
1069 let baseline = line_metrics.baseline_block_offset + self.baseline_offset_in_parent;
1070 baseline - self.baseline_offset_in_item
1071 },
1072 }
1073 }
1074}
1075
1076pub(super) struct AbsolutelyPositionedLineItem {
1077 pub absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
1078 pub preceding_line_content_would_produce_phantom_line: bool,
1082}
1083
1084pub(super) struct FloatLineItem {
1085 pub fragment: Arc<BoxFragment>,
1086 pub needs_placement: bool,
1090 pub range: Range<PositioningContextLength>,
1094}
1095
1096fn sort_by_indices_in_place<T>(data: &mut [T], mut indices: Vec<usize>) {
1099 for idx in 0..data.len() {
1100 if indices[idx] == idx {
1101 continue;
1102 }
1103
1104 let mut current_idx = idx;
1105 loop {
1106 let target_idx = indices[current_idx];
1107 indices[current_idx] = current_idx;
1108 if indices[target_idx] == target_idx {
1109 break;
1110 }
1111 data.swap(current_idx, target_idx);
1112 current_idx = target_idx;
1113 }
1114 }
1115}