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