1use std::ops::Range;
6use std::rc::Rc;
7use std::sync::Arc;
8
9use app_units::Au;
10use bitflags::bitflags;
11use fonts::{ShapedTextSlice, TrailingWhiteSpace};
12use itertools::Either;
13use servo_base::text::Utf32CodeUnits;
14use style::Zero;
15use style::computed_values::position::T as Position;
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::{
27 BaseFragment, BaseFragmentInfo, BoxFragment, Fragment, FragmentFlags, TextFragment,
28};
29use crate::geom::{
30 LogicalRect, LogicalSides, LogicalVec2, PhysicalRect, PhysicalSize, ToLogical,
31 ToLogicalWithContainingBlock,
32};
33use crate::positioned::{
34 AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, relative_adjustement,
35};
36use crate::{ContainingBlock, ContainingBlockSize};
37
38pub(super) struct LineMetrics {
39 pub block_offset: Au,
42
43 pub block_size: Au,
45
46 pub baseline_block_offset: Au,
48}
49
50bitflags! {
51 struct LineLayoutInlineContainerFlags: u8 {
52 const HAD_ANY_LINE_ITEMS = 1 << 0;
55 const HAD_INLINE_START_PBM = 1 << 2;
58 const HAD_INLINE_END_PBM = 1 << 3;
61 const HAD_ANY_FLOATS = 1 << 4;
63 }
64}
65
66struct FragmentAndData {
67 fragment: Fragment,
68
69 logical_rect: LogicalRect<Au>,
74
75 propagated_floats: Vec<Arc<BoxFragment>>,
79}
80
81impl FragmentAndData {
82 fn new(fragment: Fragment, logical_rect: LogicalRect<Au>) -> Self {
83 Self::new_with_propagated_floats(fragment, logical_rect, Vec::new())
84 }
85
86 fn new_with_propagated_floats(
87 fragment: Fragment,
88 logical_rect: LogicalRect<Au>,
89 propagated_floats: Vec<Arc<BoxFragment>>,
90 ) -> Self {
91 Self {
92 fragment,
93 logical_rect,
94 propagated_floats,
95 }
96 }
97
98 fn resolve_physical_rect_and_adjust_floats(&self, container: &ContainingBlock) {
105 debug_assert!(!matches!(self.fragment, Fragment::Float(_)));
106
107 let Some(base) = self.fragment.base() else {
108 return;
109 };
110
111 let rect = self.logical_rect.as_physical(Some(container));
115 base.set_rect(rect);
116
117 let float_offset = -rect.origin.to_vector().to_size();
122 for float_fragment in &self.propagated_floats {
123 float_fragment.base.translate_rect(float_offset);
124 }
125 }
126
127 fn resolve_physical_rects_and_adjust_floats(
131 fragments_and_data: Vec<Self>,
132 container: &ContainingBlock,
133 ) -> Vec<Fragment> {
134 let mut fragments = Vec::with_capacity(fragments_and_data.len());
135 for fragment_and_data in fragments_and_data {
136 if !matches!(fragment_and_data.fragment, Fragment::Float(_)) {
137 fragment_and_data.resolve_physical_rect_and_adjust_floats(container)
138 }
139 fragments.push(fragment_and_data.fragment);
140 }
141 fragments
142 }
143
144 fn resolve_physical_rects_and_adjust_and_collect_floats(
148 fragments_and_data: Vec<Self>,
149 container: &ContainingBlock,
150 relative_adjustement: PhysicalSize<Au>,
151 ) -> (Vec<Fragment>, Vec<Arc<BoxFragment>>) {
152 let mut fragments = Vec::with_capacity(fragments_and_data.len());
153 let mut propagated_floats = Vec::new();
154 for mut fragment_and_data in fragments_and_data {
155 if let Fragment::Float(ref float) = fragment_and_data.fragment {
156 if relative_adjustement != PhysicalSize::zero() {
157 float.base.translate_rect(relative_adjustement);
158 }
159 propagated_floats.push(float.clone());
160 } else {
161 fragment_and_data.resolve_physical_rect_and_adjust_floats(container)
162 }
163 fragments.push(fragment_and_data.fragment);
164 propagated_floats.append(&mut fragment_and_data.propagated_floats);
165 }
166 (fragments, propagated_floats)
167 }
168}
169
170pub(super) struct LineItemLayoutInlineContainerState {
174 pub identifier: Option<InlineBoxIdentifier>,
177
178 fragments_and_data: Vec<FragmentAndData>,
180
181 pub inline_advance: Au,
183
184 flags: LineLayoutInlineContainerFlags,
186
187 pub parent_offset: LogicalVec2<Au>,
191
192 pub baseline_offset: Au,
196
197 pub positioning_context_or_start_offset_in_parent:
202 Either<PositioningContext, PositioningContextLength>,
203}
204
205impl LineItemLayoutInlineContainerState {
206 fn new(
207 identifier: Option<InlineBoxIdentifier>,
208 parent_offset: LogicalVec2<Au>,
209 baseline_offset: Au,
210 positioning_context_or_start_offset_in_parent: Either<
211 PositioningContext,
212 PositioningContextLength,
213 >,
214 ) -> Self {
215 Self {
216 identifier,
217 fragments_and_data: Vec::new(),
218 inline_advance: Au::zero(),
219 flags: LineLayoutInlineContainerFlags::empty(),
220 parent_offset,
221 baseline_offset,
222 positioning_context_or_start_offset_in_parent,
223 }
224 }
225
226 fn root(starting_inline_advance: Au, baseline_offset: Au) -> Self {
227 let mut state = Self::new(
228 None,
229 LogicalVec2::zero(),
230 baseline_offset,
231 Either::Right(PositioningContextLength::zero()),
232 );
233 state.inline_advance = starting_inline_advance;
234 state
235 }
236}
237
238pub(super) struct LineItemLayout<'layout_data, 'layout> {
242 layout: &'layout mut InlineFormattingContextLayout<'layout_data>,
244
245 pub state_stack: Vec<LineItemLayoutInlineContainerState>,
248
249 pub current_state: LineItemLayoutInlineContainerState,
251
252 pub line_metrics: LineMetrics,
255
256 pub justification_adjustment: Au,
259
260 is_phantom_line: bool,
263
264 for_block_level: bool,
266}
267
268impl LineItemLayout<'_, '_> {
269 pub(super) fn layout_line_items(
270 layout: &mut InlineFormattingContextLayout,
271 line_items: Vec<LineItem>,
272 start_position: LogicalVec2<Au>,
273 effective_block_advance: &LineBlockSizes,
274 justification_adjustment: Au,
275 is_phantom_line: bool,
276 for_block_level: bool,
277 ) -> Vec<Fragment> {
278 let baseline_offset = effective_block_advance.find_baseline_offset();
279 LineItemLayout {
280 layout,
281 state_stack: Vec::new(),
282 current_state: LineItemLayoutInlineContainerState::root(
283 start_position.inline,
284 baseline_offset,
285 ),
286 line_metrics: LineMetrics {
287 block_offset: start_position.block,
288 block_size: effective_block_advance.resolve(),
289 baseline_block_offset: baseline_offset,
290 },
291 justification_adjustment,
292 is_phantom_line,
293 for_block_level,
294 }
295 .layout(line_items)
296 }
297
298 fn prepare_layout_for_inline_box(&mut self, new_inline_box: Option<InlineBoxIdentifier>) {
300 let Some(new_inline_box) = new_inline_box else {
302 while !self.state_stack.is_empty() {
303 self.end_inline_box();
304 }
305 return;
306 };
307
308 let path = self
311 .layout
312 .ifc
313 .inline_boxes
314 .get_path(self.current_state.identifier, new_inline_box);
315 for token in path {
316 match token {
317 InlineBoxTreePathToken::Start(ref identifier) => self.start_inline_box(identifier),
318 InlineBoxTreePathToken::End(_) => self.end_inline_box(),
319 }
320 }
321 }
322
323 fn reorder_line_items_for_bidi(
329 &self,
330 mut line_items: Vec<LineItem>,
331 ) -> impl Iterator<Item = LineItem> + use<> {
332 let iterator = |line_items: Vec<LineItem>| {
333 if self.containing_block().style.writing_mode.is_bidi_ltr() {
340 Either::Left(line_items.into_iter())
341 } else {
342 Either::Right(line_items.into_iter().rev())
343 }
344 };
345
346 if !self.layout.ifc.has_right_to_left_content {
347 return iterator(line_items);
352 }
353
354 let mut last_level = Level::ltr();
355 let levels: Vec<_> = line_items
356 .iter()
357 .map(|item| {
358 let level = match item {
359 LineItem::TextRun(_, text_run) => text_run.info.font_info.bidi_level,
360 LineItem::InlineStartBoxPaddingBorderMargin(_) => last_level,
364 LineItem::InlineEndBoxPaddingBorderMargin(_) => last_level,
365 LineItem::Atomic(_, atomic) => atomic.bidi_level,
366 LineItem::AbsolutelyPositioned(..) => last_level,
367 LineItem::Float(..) => {
368 last_level
371 },
372 LineItem::BlockLevel(..) => last_level,
373 LineItem::Tab { bidi_level, .. } => *bidi_level,
374 };
375 last_level = level;
376 level
377 })
378 .collect();
379
380 sort_by_indices_in_place(&mut line_items, BidiInfo::reorder_visual(&levels));
381 iterator(line_items)
382 }
383
384 pub(super) fn layout(&mut self, line_items: Vec<LineItem>) -> Vec<Fragment> {
385 let line_item_iterator = self.reorder_line_items_for_bidi(line_items);
386 for item in line_item_iterator.into_iter().by_ref() {
387 self.prepare_layout_for_inline_box(item.inline_box_identifier());
392
393 self.current_state
394 .flags
395 .insert(LineLayoutInlineContainerFlags::HAD_ANY_LINE_ITEMS);
396 match item {
397 LineItem::InlineStartBoxPaddingBorderMargin(_) => {
398 self.current_state
399 .flags
400 .insert(LineLayoutInlineContainerFlags::HAD_INLINE_START_PBM);
401 },
402 LineItem::InlineEndBoxPaddingBorderMargin(_) => {
403 self.current_state
404 .flags
405 .insert(LineLayoutInlineContainerFlags::HAD_INLINE_END_PBM);
406 },
407 LineItem::TextRun(_, text_run) => self.layout_text_run(text_run),
408 LineItem::Atomic(_, atomic) => self.layout_atomic(atomic),
409 LineItem::AbsolutelyPositioned(_, absolute) => self.layout_absolute(absolute),
410 LineItem::Float(_, float) => self.layout_float(float),
411 LineItem::BlockLevel(_, block_level) => self.layout_block_level(block_level),
412 LineItem::Tab { advance, .. } => self.layout_tab(advance),
413 }
414 }
415
416 self.prepare_layout_for_inline_box(None);
418
419 FragmentAndData::resolve_physical_rects_and_adjust_floats(
420 std::mem::take(&mut self.current_state.fragments_and_data),
421 self.layout.containing_block(),
422 )
423 }
424
425 fn current_positioning_context_mut(&mut self) -> &mut PositioningContext {
426 if let Either::Left(ref mut positioning_context) = self
427 .current_state
428 .positioning_context_or_start_offset_in_parent
429 {
430 return positioning_context;
431 }
432 self.state_stack
433 .iter_mut()
434 .rev()
435 .find_map(
436 |state| match state.positioning_context_or_start_offset_in_parent {
437 Either::Left(ref mut positioning_context) => Some(positioning_context),
438 Either::Right(_) => None,
439 },
440 )
441 .unwrap_or(self.layout.positioning_context)
442 }
443
444 fn start_inline_box(&mut self, identifier: &InlineBoxIdentifier) {
445 let inline_box_state =
446 &*self.layout.inline_box_states[identifier.index_in_inline_boxes as usize];
447 let inline_box = self.layout.ifc.inline_boxes.get(identifier);
448 let inline_box = &*(inline_box.borrow());
449
450 let space_above_baseline = inline_box_state.calculate_space_above_baseline();
451 let block_start_offset =
452 self.calculate_inline_box_block_start(inline_box_state, space_above_baseline);
453
454 let positioning_context_or_start_offset_in_parent =
455 match PositioningContext::new_for_layout_box_base(&inline_box.base) {
456 Some(positioning_context) => Either::Left(positioning_context),
457 None => Either::Right(self.current_positioning_context_mut().len()),
458 };
459
460 let parent_offset = LogicalVec2 {
461 inline: self.current_state.inline_advance + self.current_state.parent_offset.inline,
462 block: block_start_offset,
463 };
464
465 let outer_state = std::mem::replace(
466 &mut self.current_state,
467 LineItemLayoutInlineContainerState::new(
468 Some(*identifier),
469 parent_offset,
470 block_start_offset + space_above_baseline,
471 positioning_context_or_start_offset_in_parent,
472 ),
473 );
474
475 self.state_stack.push(outer_state);
476 }
477
478 fn end_inline_box(&mut self) {
479 let outer_state = self.state_stack.pop().expect("Ended unknown inline box");
480 let inner_state = std::mem::replace(&mut self.current_state, outer_state);
481
482 let identifier = inner_state.identifier.expect("Ended unknown inline box");
483 let inline_box_state =
484 &*self.layout.inline_box_states[identifier.index_in_inline_boxes as usize];
485 let inline_box = self.layout.ifc.inline_boxes.get(&identifier);
486 let inline_box = &*(inline_box.borrow());
487
488 let containing_block = self.layout.containing_block();
489 let containing_block_writing_mode = containing_block.style.writing_mode;
490
491 let mut padding = inline_box_state.pbm.padding;
492 let mut border = inline_box_state.pbm.border;
493 let mut margin = inline_box_state.pbm.margin.auto_is(Au::zero);
494 if self.for_block_level {
497 padding = LogicalSides::zero();
498 border = LogicalSides::zero();
499 margin = LogicalSides::zero();
500 } else if !inline_box_state.should_clone_pbm() {
501 let mut had_start = inner_state
502 .flags
503 .contains(LineLayoutInlineContainerFlags::HAD_INLINE_START_PBM);
504 let mut had_end = inner_state
505 .flags
506 .contains(LineLayoutInlineContainerFlags::HAD_INLINE_END_PBM);
507
508 if containing_block_writing_mode.is_bidi_ltr() !=
509 inline_box.base.style.writing_mode.is_bidi_ltr()
510 {
511 std::mem::swap(&mut had_start, &mut had_end)
512 }
513
514 if !had_start {
515 padding.inline_start = Au::zero();
516 border.inline_start = Au::zero();
517 margin.inline_start = Au::zero();
518 }
519 if !had_end {
520 padding.inline_end = Au::zero();
521 border.inline_end = Au::zero();
522 margin.inline_end = Au::zero();
523 }
524 }
525 let pbm_sums = padding + border + margin;
526
527 let mut content_rect = LogicalRect {
529 start_corner: LogicalVec2 {
530 inline: self.current_state.inline_advance + pbm_sums.inline_start,
531 block: inner_state.parent_offset.block - self.current_state.parent_offset.block,
532 },
533 size: LogicalVec2 {
534 inline: inner_state.inline_advance,
535 block: if self.is_phantom_line {
536 Au::zero()
537 } else {
538 inline_box_state.base.font_metrics.line_gap
539 },
540 },
541 };
542
543 let style = &inline_box.base.style;
546 let relative_adjustement = if style.get_box().position == Position::Relative {
547 let relative_adjustement = relative_adjustement(style, containing_block);
548 content_rect.start_corner += relative_adjustement;
549 relative_adjustement
550 .to_physical_vector(containing_block_writing_mode)
551 .to_size()
552 } else {
553 PhysicalSize::zero()
554 };
555
556 let (fragments, propagated_floats) =
557 FragmentAndData::resolve_physical_rects_and_adjust_and_collect_floats(
558 inner_state.fragments_and_data,
559 &ContainingBlock {
560 size: ContainingBlockSize {
561 inline: content_rect.size.inline,
562 block: Default::default(),
563 },
564 style: containing_block.style,
565 },
566 relative_adjustement,
567 );
568
569 let physical_content_rect = content_rect.as_physical(Some(containing_block));
572
573 let mut fragment = BoxFragment::new(
574 inline_box.base.base_fragment_info,
575 style.clone(),
576 fragments,
577 physical_content_rect,
578 padding.to_physical(containing_block_writing_mode),
579 border.to_physical(containing_block_writing_mode),
580 margin.to_physical(containing_block_writing_mode),
581 None, );
583 if self.for_block_level {
584 fragment.base.flags.insert(FragmentFlags::DO_NOT_PAINT);
585 }
586
587 let offset_from_parent_ifc = LogicalVec2 {
588 inline: pbm_sums.inline_start + self.current_state.inline_advance,
589 block: content_rect.start_corner.block,
590 }
591 .to_physical_vector(containing_block_writing_mode);
592
593 match inner_state.positioning_context_or_start_offset_in_parent {
594 Either::Left(mut positioning_context) => {
595 positioning_context
596 .layout_collected_children(self.layout.layout_context, &mut fragment);
597 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
598 &offset_from_parent_ifc,
599 PositioningContextLength::zero(),
600 );
601 self.current_positioning_context_mut()
602 .append(positioning_context);
603 },
604 Either::Right(start_offset) => {
605 self.current_positioning_context_mut()
606 .adjust_static_position_of_hoisted_fragments_with_offset(
607 &offset_from_parent_ifc,
608 start_offset,
609 );
610 },
611 }
612
613 self.current_state.inline_advance += inner_state.inline_advance + pbm_sums.inline_sum();
614
615 let fragment = Fragment::Box(Arc::new(fragment));
616 inline_box.base.add_fragment(fragment.clone());
617 self.current_state
618 .fragments_and_data
619 .push(FragmentAndData::new_with_propagated_floats(
620 fragment,
621 content_rect,
622 propagated_floats,
623 ));
624 }
625
626 fn calculate_inline_box_block_start(
627 &self,
628 inline_box_state: &InlineBoxContainerState,
629 space_above_baseline: Au,
630 ) -> Au {
631 if self.is_phantom_line {
632 return Au::zero();
633 };
634 let font_metrics = &inline_box_state.base.font_metrics;
635 let style = &inline_box_state.base.style;
636 let line_gap = font_metrics.line_gap;
637
638 match inline_box_state.base.style.get_baseline_shift() {
641 BaselineShift::Keyword(BaselineShiftKeyword::Top) => {
642 let line_height = line_height(style, font_metrics, &inline_box_state.base.flags);
643 (line_height - line_gap).scale_by(0.5)
644 },
645 BaselineShift::Keyword(BaselineShiftKeyword::Center) => {
646 (self.line_metrics.block_size - line_gap).scale_by(0.5)
647 },
648 BaselineShift::Keyword(BaselineShiftKeyword::Bottom) => {
649 let line_height = line_height(style, font_metrics, &inline_box_state.base.flags);
650 let half_leading = (line_height - line_gap).scale_by(0.5);
651 self.line_metrics.block_size - line_height + half_leading
652 },
653 _ => {
654 self.line_metrics.baseline_block_offset + inline_box_state.base.baseline_offset -
655 space_above_baseline
656 },
657 }
658 }
659
660 fn layout_text_run(&mut self, text_item: TextRunLineItem) {
661 if text_item.text.is_empty() && !text_item.is_empty_for_text_cursor {
662 return;
663 }
664
665 let mut number_of_justification_opportunities = 0;
666 let mut inline_advance = text_item
667 .text
668 .iter()
669 .map(|shaped_text_slice| {
670 number_of_justification_opportunities += shaped_text_slice.total_word_separators();
671 shaped_text_slice.total_advance()
672 })
673 .sum();
674
675 if !self.justification_adjustment.is_zero() {
676 inline_advance += self
677 .justification_adjustment
678 .scale_by(number_of_justification_opportunities as f32);
679 }
680
681 let font_metrics = text_item.info.font_info.font.metrics();
685 let start_corner = LogicalVec2 {
686 inline: self.current_state.inline_advance,
687 block: self.current_state.baseline_offset -
688 font_metrics.ascent -
689 self.current_state.parent_offset.block,
690 };
691 let content_rect = LogicalRect {
692 start_corner,
693 size: LogicalVec2 {
694 block: font_metrics.line_gap,
695 inline: inline_advance,
696 },
697 };
698
699 let font_key = text_item.info.font_info.font.key(
700 self.layout.layout_context.painter_id,
701 &self.layout.layout_context.font_context,
702 );
703
704 self.current_state.inline_advance += inline_advance;
705 self.current_state
706 .fragments_and_data
707 .push(FragmentAndData::new(
708 Fragment::Text(Arc::new(TextFragment {
709 base: BaseFragment::new(text_item.base_fragment_info, PhysicalRect::zero()),
710 run_data: text_item.text_fragment_run_data,
711 font_metrics: font_metrics.clone(),
712 font_key,
713 glyphs: text_item.text,
714 justification_adjustment: self.justification_adjustment,
715 character_range_in_dom_node: text_item.character_range_in_dom_node,
716 is_empty_for_text_cursor: text_item.is_empty_for_text_cursor,
717 })),
718 content_rect,
719 ));
720 }
721
722 fn layout_atomic(&mut self, atomic: AtomicLineItem) {
723 let containing_block = self.containing_block();
728 let ifc_writing_mode = containing_block.style.writing_mode;
729 let content_rect = {
730 let atomic_fragment = &atomic.fragment;
731 let block_start = atomic.calculate_block_start(&self.line_metrics);
732 let padding_border_margin_sides = atomic_fragment
733 .padding_border_margin()
734 .to_logical(ifc_writing_mode);
735
736 let mut atomic_offset = LogicalVec2 {
737 inline: self.current_state.inline_advance +
738 padding_border_margin_sides.inline_start,
739 block: block_start - self.current_state.parent_offset.block +
740 padding_border_margin_sides.block_start,
741 };
742
743 let style = atomic_fragment.style();
744 if style.get_box().position == Position::Relative {
745 atomic_offset += relative_adjustement(&style, containing_block);
746 }
747
748 LogicalRect {
751 start_corner: atomic_offset,
752 size: atomic_fragment
753 .content_rect()
754 .size
755 .to_logical(ifc_writing_mode),
756 }
757 };
758
759 if let Some(mut positioning_context) = atomic.positioning_context {
760 let physical_rect_as_if_in_root = content_rect.as_physical(Some(containing_block));
761 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
762 &physical_rect_as_if_in_root.origin.to_vector(),
763 PositioningContextLength::zero(),
764 );
765
766 self.current_positioning_context_mut()
767 .append(positioning_context);
768 }
769
770 self.current_state.inline_advance += atomic.size.inline;
771
772 self.current_state
773 .fragments_and_data
774 .push(FragmentAndData::new(
775 Fragment::Box(atomic.fragment),
776 content_rect,
777 ));
778 }
779
780 fn layout_absolute(&mut self, absolute: AbsolutelyPositionedLineItem) {
781 let absolutely_positioned_box = (*absolute.absolutely_positioned_box).borrow();
782 let style = absolutely_positioned_box.context.style();
783
784 let block_position = self.layout.placement_state.current_margin.solve() -
797 self.current_state.parent_offset.block;
798 let initial_start_corner =
799 if style.get_box().original_display.outside() == DisplayOutside::Inline {
800 LogicalVec2 {
802 inline: self.current_state.inline_advance,
803 block: block_position,
804 }
805 } else {
806 LogicalVec2 {
810 inline: -self.current_state.parent_offset.inline,
811 block: if absolute.preceding_line_content_would_produce_phantom_line {
812 block_position
813 } else {
814 block_position + self.line_metrics.block_size
815 },
816 }
817 };
818
819 let containing_block = self.containing_block();
822 let static_position_rect = LogicalRect {
823 start_corner: initial_start_corner,
824 size: LogicalVec2::zero(),
825 }
826 .as_physical(Some(containing_block));
827
828 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
829 absolute.absolutely_positioned_box.clone(),
830 static_position_rect,
831 LogicalVec2 {
832 inline: AlignFlags::START,
833 block: AlignFlags::START,
834 },
835 containing_block.style.writing_mode,
836 );
837
838 let hoisted_fragment = hoisted_box.fragment.clone();
839 self.current_positioning_context_mut().push(hoisted_box);
840 self.current_state
841 .fragments_and_data
842 .push(FragmentAndData::new(
843 Fragment::AbsoluteOrFixedPositionedPlaceholder(hoisted_fragment),
844 LogicalRect::zero(),
845 ));
846 }
847
848 fn layout_float(&mut self, float: FloatLineItem) {
849 self.current_state
850 .flags
851 .insert(LineLayoutInlineContainerFlags::HAD_ANY_FLOATS);
852 let offset = LogicalVec2 {
862 inline: Au::zero(),
863 block: -self.line_metrics.block_offset,
864 };
865 float
866 .fragment
867 .base
868 .translate_rect(offset.to_physical_size(self.containing_block().style.writing_mode));
869 self.current_state
870 .fragments_and_data
871 .push(FragmentAndData::new(
872 Fragment::Float(float.fragment),
873 LogicalRect::zero(),
874 ));
875 }
876
877 fn layout_block_level(&mut self, block_level: Arc<BoxFragment>) {
878 let containing_block = self.containing_block();
879 let mut content_rect = block_level.content_rect().to_logical(containing_block);
880 content_rect.start_corner.inline -= self.current_state.parent_offset.inline;
882 content_rect.start_corner.block -= self.line_metrics.block_offset;
883 self.current_state
884 .fragments_and_data
885 .push(FragmentAndData::new(
886 Fragment::Box(block_level),
887 content_rect,
888 ));
889 }
890
891 #[inline]
892 fn containing_block(&self) -> &ContainingBlock<'_> {
893 self.layout.containing_block()
894 }
895
896 fn layout_tab(&mut self, advance: Au) {
897 self.current_state.inline_advance += advance;
898 }
899}
900
901pub(super) enum LineItem {
902 InlineStartBoxPaddingBorderMargin(Rc<InlineBoxContainerState>),
903 InlineEndBoxPaddingBorderMargin(Rc<InlineBoxContainerState>),
904 TextRun(Option<InlineBoxIdentifier>, TextRunLineItem),
905 Atomic(Option<InlineBoxIdentifier>, AtomicLineItem),
906 AbsolutelyPositioned(Option<InlineBoxIdentifier>, AbsolutelyPositionedLineItem),
907 Float(Option<InlineBoxIdentifier>, FloatLineItem),
908 BlockLevel(Option<InlineBoxIdentifier>, Arc<BoxFragment>),
909 Tab {
910 inline_box_identifier: Option<InlineBoxIdentifier>,
911 advance: Au,
912 bidi_level: Level,
913 conditionally_hangs: bool,
914 },
915}
916
917impl LineItem {
918 pub(crate) fn is_in_flow_content(&self) -> bool {
919 matches!(
920 self,
921 Self::TextRun(..) | Self::Atomic(..) | Self::BlockLevel(..)
922 )
923 }
924
925 fn inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
926 match self {
927 LineItem::InlineStartBoxPaddingBorderMargin(state) => Some(state.identifier),
928 LineItem::InlineEndBoxPaddingBorderMargin(state) => Some(state.identifier),
929 LineItem::TextRun(identifier, _) => *identifier,
930 LineItem::Atomic(identifier, _) => *identifier,
931 LineItem::AbsolutelyPositioned(identifier, _) => *identifier,
932 LineItem::Float(identifier, _) => *identifier,
933 LineItem::BlockLevel(identifier, _) => *identifier,
934 LineItem::Tab {
935 inline_box_identifier,
936 ..
937 } => *inline_box_identifier,
938 }
939 }
940
941 pub(super) fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
942 match self {
943 LineItem::InlineStartBoxPaddingBorderMargin(_) => true,
944 LineItem::InlineEndBoxPaddingBorderMargin(_) => true,
945 LineItem::TextRun(_, item) => item.trim_whitespace_at_start(whitespace_trimmed),
946 LineItem::Atomic(..) => false,
947 LineItem::AbsolutelyPositioned(..) => true,
948 LineItem::Float(..) => true,
949 LineItem::BlockLevel(..) => true,
950 LineItem::Tab { .. } => false,
951 }
952 }
953}
954
955pub(super) struct TextRunLineItem {
956 pub text_fragment_run_data: Arc<SharedTextRunData>,
957 pub info: FontAndScriptInfo,
958 pub base_fragment_info: BaseFragmentInfo,
959 pub text: Vec<Arc<ShapedTextSlice>>,
960 pub character_range_in_dom_node: Range<Utf32CodeUnits>,
963 pub is_empty_for_text_cursor: bool,
966}
967
968impl TextRunLineItem {
969 pub(crate) fn trim_removable_white_space_at_end(&mut self) {
971 while let Some(last) = self.text.last_mut() {
972 if last.all_removable() {
973 self.text.pop();
974 continue;
975 }
976 *last = last.without_removable_white_space();
977 break;
978 }
979 }
980
981 fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
982 let index_of_first_non_whitespace = self
983 .text
984 .iter()
985 .position(|slice| !slice.all_removable())
986 .unwrap_or(self.text.len());
987
988 *whitespace_trimmed += self
989 .text
990 .drain(0..index_of_first_non_whitespace)
991 .map(|glyph| glyph.total_advance())
992 .sum();
993
994 self.text.is_empty()
996 }
997
998 pub(crate) fn trailing_white_space(&self) -> (TrailingWhiteSpace<Au>, bool) {
1002 let mut hangable = Au::zero();
1003 let mut removable = Au::zero();
1004 let mut preceded_by_content = false;
1005
1006 for slice in self.text.iter().rev() {
1007 if hangable.is_zero() {
1008 removable += slice.removable_advance();
1009 } else {
1010 hangable += slice.removable_advance();
1011 }
1012 hangable += slice.hangable_advance();
1013
1014 if slice.has_non_hangable_non_removable_content() {
1015 preceded_by_content = true;
1016 break;
1017 }
1018 }
1019
1020 (
1021 TrailingWhiteSpace {
1022 hangable,
1023 removable,
1024 },
1025 preceded_by_content,
1026 )
1027 }
1028}
1029
1030pub(super) struct AtomicLineItem {
1031 pub fragment: Arc<BoxFragment>,
1032 pub size: LogicalVec2<Au>,
1033 pub positioning_context: Option<PositioningContext>,
1034
1035 pub baseline_offset_in_parent: Au,
1039
1040 pub baseline_offset_in_item: Au,
1042
1043 pub bidi_level: Level,
1045}
1046
1047impl AtomicLineItem {
1048 fn calculate_block_start(&self, line_metrics: &LineMetrics) -> Au {
1051 match self.fragment.style().get_baseline_shift() {
1052 BaselineShift::Keyword(BaselineShiftKeyword::Top) => Au::zero(),
1053 BaselineShift::Keyword(BaselineShiftKeyword::Center) => {
1054 (line_metrics.block_size - self.size.block).scale_by(0.5)
1055 },
1056 BaselineShift::Keyword(BaselineShiftKeyword::Bottom) => {
1057 line_metrics.block_size - self.size.block
1058 },
1059
1060 _ => {
1062 let baseline = line_metrics.baseline_block_offset + self.baseline_offset_in_parent;
1063 baseline - self.baseline_offset_in_item
1064 },
1065 }
1066 }
1067}
1068
1069pub(super) struct AbsolutelyPositionedLineItem {
1070 pub absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
1071 pub preceding_line_content_would_produce_phantom_line: bool,
1075}
1076
1077pub(super) struct FloatLineItem {
1078 pub fragment: Arc<BoxFragment>,
1079 pub needs_placement: bool,
1083 pub range: Range<PositioningContextLength>,
1087}
1088
1089fn sort_by_indices_in_place<T>(data: &mut [T], mut indices: Vec<usize>) {
1092 for idx in 0..data.len() {
1093 if indices[idx] == idx {
1094 continue;
1095 }
1096
1097 let mut current_idx = idx;
1098 loop {
1099 let target_idx = indices[current_idx];
1100 indices[current_idx] = current_idx;
1101 if indices[target_idx] == target_idx {
1102 break;
1103 }
1104 data.swap(current_idx, target_idx);
1105 current_idx = target_idx;
1106 }
1107 }
1108}