1use std::ops::Range;
6use std::sync::Arc;
7
8use app_units::Au;
9use bitflags::bitflags;
10use fonts::ShapedTextSlice;
11use itertools::Either;
12use layout_api::SharedSelection;
13use malloc_size_of_derive::MallocSizeOf;
14use style::Zero;
15use style::computed_values::position::T as Position;
16use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
17use style::values::computed::BaselineShift;
18use style::values::generics::box_::BaselineShiftKeyword;
19use style::values::specified::align::AlignFlags;
20use style::values::specified::box_::DisplayOutside;
21use unicode_bidi::{BidiInfo, Level};
22
23use super::inline_box::{InlineBoxContainerState, InlineBoxIdentifier, InlineBoxTreePathToken};
24use super::{InlineFormattingContextLayout, LineBlockSizes, SharedInlineStyles, line_height};
25use crate::cell::ArcRefCell;
26use crate::flow::inline::text_run::FontAndScriptInfo;
27use crate::fragment_tree::{BaseFragment, BaseFragmentInfo, BoxFragment, Fragment, TextFragment};
28use crate::geom::{
29 LogicalRect, LogicalSides, LogicalVec2, PhysicalRect, PhysicalSize, ToLogical,
30 ToLogicalWithContainingBlock,
31};
32use crate::positioned::{
33 AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, relative_adjustement,
34};
35use crate::{ContainingBlock, ContainingBlockSize};
36
37pub(super) struct LineMetrics {
38 pub block_offset: Au,
41
42 pub block_size: Au,
44
45 pub baseline_block_offset: Au,
47}
48
49bitflags! {
50 struct LineLayoutInlineContainerFlags: u8 {
51 const HAD_ANY_LINE_ITEMS = 1 << 0;
54 const HAD_INLINE_START_PBM = 1 << 2;
57 const HAD_INLINE_END_PBM = 1 << 3;
60 const HAD_ANY_FLOATS = 1 << 4;
62 }
63}
64
65struct FragmentAndData {
66 fragment: Fragment,
67
68 logical_rect: LogicalRect<Au>,
73
74 propagated_floats: Vec<Arc<BoxFragment>>,
78}
79
80impl FragmentAndData {
81 fn new(fragment: Fragment, logical_rect: LogicalRect<Au>) -> Self {
82 Self::new_with_propagated_floats(fragment, logical_rect, Vec::new())
83 }
84
85 fn new_with_propagated_floats(
86 fragment: Fragment,
87 logical_rect: LogicalRect<Au>,
88 propagated_floats: Vec<Arc<BoxFragment>>,
89 ) -> Self {
90 Self {
91 fragment,
92 logical_rect,
93 propagated_floats,
94 }
95 }
96
97 fn resolve_physical_rect_and_adjust_floats(&self, container: &ContainingBlock) {
104 debug_assert!(!matches!(self.fragment, Fragment::Float(_)));
105
106 let Some(base) = self.fragment.base() else {
107 return;
108 };
109
110 let rect = self.logical_rect.as_physical(Some(container));
114 base.set_rect(rect);
115
116 let float_offset = -rect.origin.to_vector().to_size();
121 for float_fragment in &self.propagated_floats {
122 float_fragment.base.translate_rect(float_offset);
123 }
124 }
125
126 fn resolve_physical_rects_and_adjust_floats(
130 fragments_and_data: Vec<Self>,
131 container: &ContainingBlock,
132 ) -> Vec<Fragment> {
133 let mut fragments = Vec::with_capacity(fragments_and_data.len());
134 for fragment_and_data in fragments_and_data {
135 if !matches!(fragment_and_data.fragment, Fragment::Float(_)) {
136 fragment_and_data.resolve_physical_rect_and_adjust_floats(container)
137 }
138 fragments.push(fragment_and_data.fragment);
139 }
140 fragments
141 }
142
143 fn resolve_physical_rects_and_adjust_and_collect_floats(
147 fragments_and_data: Vec<Self>,
148 container: &ContainingBlock,
149 relative_adjustement: PhysicalSize<Au>,
150 ) -> (Vec<Fragment>, Vec<Arc<BoxFragment>>) {
151 let mut fragments = Vec::with_capacity(fragments_and_data.len());
152 let mut propagated_floats = Vec::new();
153 for mut fragment_and_data in fragments_and_data {
154 if let Fragment::Float(ref float) = fragment_and_data.fragment {
155 if relative_adjustement != PhysicalSize::zero() {
156 float.base.translate_rect(relative_adjustement);
157 }
158 propagated_floats.push(float.clone());
159 } else {
160 fragment_and_data.resolve_physical_rect_and_adjust_floats(container)
161 }
162 fragments.push(fragment_and_data.fragment);
163 propagated_floats.append(&mut fragment_and_data.propagated_floats);
164 }
165 (fragments, propagated_floats)
166 }
167}
168
169pub(super) struct LineItemLayoutInlineContainerState {
173 pub identifier: Option<InlineBoxIdentifier>,
176
177 fragments_and_data: Vec<FragmentAndData>,
179
180 pub inline_advance: Au,
182
183 flags: LineLayoutInlineContainerFlags,
185
186 pub parent_offset: LogicalVec2<Au>,
190
191 pub baseline_offset: Au,
195
196 pub positioning_context_or_start_offset_in_parent:
201 Either<PositioningContext, PositioningContextLength>,
202}
203
204impl LineItemLayoutInlineContainerState {
205 fn new(
206 identifier: Option<InlineBoxIdentifier>,
207 parent_offset: LogicalVec2<Au>,
208 baseline_offset: Au,
209 positioning_context_or_start_offset_in_parent: Either<
210 PositioningContext,
211 PositioningContextLength,
212 >,
213 ) -> Self {
214 Self {
215 identifier,
216 fragments_and_data: Vec::new(),
217 inline_advance: Au::zero(),
218 flags: LineLayoutInlineContainerFlags::empty(),
219 parent_offset,
220 baseline_offset,
221 positioning_context_or_start_offset_in_parent,
222 }
223 }
224
225 fn root(starting_inline_advance: Au, baseline_offset: Au) -> Self {
226 let mut state = Self::new(
227 None,
228 LogicalVec2::zero(),
229 baseline_offset,
230 Either::Right(PositioningContextLength::zero()),
231 );
232 state.inline_advance = starting_inline_advance;
233 state
234 }
235}
236
237pub(super) struct LineItemLayout<'layout_data, 'layout> {
241 layout: &'layout mut InlineFormattingContextLayout<'layout_data>,
243
244 pub state_stack: Vec<LineItemLayoutInlineContainerState>,
247
248 pub current_state: LineItemLayoutInlineContainerState,
250
251 pub line_metrics: LineMetrics,
254
255 pub justification_adjustment: Au,
258
259 is_phantom_line: bool,
262
263 for_block_level: bool,
265}
266
267impl LineItemLayout<'_, '_> {
268 pub(super) fn layout_line_items(
269 layout: &mut InlineFormattingContextLayout,
270 line_items: Vec<LineItem>,
271 start_position: LogicalVec2<Au>,
272 effective_block_advance: &LineBlockSizes,
273 justification_adjustment: Au,
274 is_phantom_line: bool,
275 for_block_level: bool,
276 ) -> Vec<Fragment> {
277 let baseline_offset = effective_block_advance.find_baseline_offset();
278 LineItemLayout {
279 layout,
280 state_stack: Vec::new(),
281 current_state: LineItemLayoutInlineContainerState::root(
282 start_position.inline,
283 baseline_offset,
284 ),
285 line_metrics: LineMetrics {
286 block_offset: start_position.block,
287 block_size: effective_block_advance.resolve(),
288 baseline_block_offset: baseline_offset,
289 },
290 justification_adjustment,
291 is_phantom_line,
292 for_block_level,
293 }
294 .layout(line_items)
295 }
296
297 fn prepare_layout_for_inline_box(&mut self, new_inline_box: Option<InlineBoxIdentifier>) {
299 let Some(new_inline_box) = new_inline_box else {
301 while !self.state_stack.is_empty() {
302 self.end_inline_box();
303 }
304 return;
305 };
306
307 let path = self
310 .layout
311 .ifc
312 .inline_boxes
313 .get_path(self.current_state.identifier, new_inline_box);
314 for token in path {
315 match token {
316 InlineBoxTreePathToken::Start(ref identifier) => self.start_inline_box(identifier),
317 InlineBoxTreePathToken::End(_) => self.end_inline_box(),
318 }
319 }
320 }
321
322 fn reorder_line_items_for_bidi(
328 &self,
329 mut line_items: Vec<LineItem>,
330 ) -> impl Iterator<Item = LineItem> + use<> {
331 let iterator = |line_items: Vec<LineItem>| {
332 if self.containing_block().style.writing_mode.is_bidi_ltr() {
339 Either::Left(line_items.into_iter())
340 } else {
341 Either::Right(line_items.into_iter().rev())
342 }
343 };
344
345 if !self.layout.ifc.has_right_to_left_content {
346 return iterator(line_items);
351 }
352
353 let mut last_level = Level::ltr();
354 let levels: Vec<_> = line_items
355 .iter()
356 .map(|item| {
357 let level = match item {
358 LineItem::TextRun(_, text_run) => text_run.info.font_info.bidi_level,
359 LineItem::InlineStartBoxPaddingBorderMargin(_) => last_level,
363 LineItem::InlineEndBoxPaddingBorderMargin(_) => last_level,
364 LineItem::Atomic(_, atomic) => atomic.bidi_level,
365 LineItem::AbsolutelyPositioned(..) => last_level,
366 LineItem::Float(..) => {
367 last_level
370 },
371 LineItem::BlockLevel(..) => last_level,
372 LineItem::Tab { bidi_level, .. } => *bidi_level,
373 };
374 last_level = level;
375 level
376 })
377 .collect();
378
379 sort_by_indices_in_place(&mut line_items, BidiInfo::reorder_visual(&levels));
380 iterator(line_items)
381 }
382
383 pub(super) fn layout(&mut self, line_items: Vec<LineItem>) -> Vec<Fragment> {
384 let line_item_iterator = self.reorder_line_items_for_bidi(line_items);
385 for item in line_item_iterator.into_iter().by_ref() {
386 self.prepare_layout_for_inline_box(item.inline_box_identifier());
391
392 self.current_state
393 .flags
394 .insert(LineLayoutInlineContainerFlags::HAD_ANY_LINE_ITEMS);
395 match item {
396 LineItem::InlineStartBoxPaddingBorderMargin(_) => {
397 self.current_state
398 .flags
399 .insert(LineLayoutInlineContainerFlags::HAD_INLINE_START_PBM);
400 },
401 LineItem::InlineEndBoxPaddingBorderMargin(_) => {
402 self.current_state
403 .flags
404 .insert(LineLayoutInlineContainerFlags::HAD_INLINE_END_PBM);
405 },
406 LineItem::TextRun(_, text_run) => self.layout_text_run(text_run),
407 LineItem::Atomic(_, atomic) => self.layout_atomic(atomic),
408 LineItem::AbsolutelyPositioned(_, absolute) => self.layout_absolute(absolute),
409 LineItem::Float(_, float) => self.layout_float(float),
410 LineItem::BlockLevel(_, block_level) => self.layout_block_level(block_level),
411 LineItem::Tab { advance, .. } => self.layout_tab(advance),
412 }
413 }
414
415 self.prepare_layout_for_inline_box(None);
417
418 FragmentAndData::resolve_physical_rects_and_adjust_floats(
419 std::mem::take(&mut self.current_state.fragments_and_data),
420 self.layout.containing_block(),
421 )
422 }
423
424 fn current_positioning_context_mut(&mut self) -> &mut PositioningContext {
425 if let Either::Left(ref mut positioning_context) = self
426 .current_state
427 .positioning_context_or_start_offset_in_parent
428 {
429 return positioning_context;
430 }
431 self.state_stack
432 .iter_mut()
433 .rev()
434 .find_map(
435 |state| match state.positioning_context_or_start_offset_in_parent {
436 Either::Left(ref mut positioning_context) => Some(positioning_context),
437 Either::Right(_) => None,
438 },
439 )
440 .unwrap_or(self.layout.positioning_context)
441 }
442
443 fn start_inline_box(&mut self, identifier: &InlineBoxIdentifier) {
444 let inline_box_state =
445 &*self.layout.inline_box_states[identifier.index_in_inline_boxes as usize];
446 let inline_box = self.layout.ifc.inline_boxes.get(identifier);
447 let inline_box = &*(inline_box.borrow());
448
449 let space_above_baseline = inline_box_state.calculate_space_above_baseline();
450 let block_start_offset =
451 self.calculate_inline_box_block_start(inline_box_state, space_above_baseline);
452
453 let positioning_context_or_start_offset_in_parent =
454 match PositioningContext::new_for_layout_box_base(&inline_box.base) {
455 Some(positioning_context) => Either::Left(positioning_context),
456 None => Either::Right(self.current_positioning_context_mut().len()),
457 };
458
459 let parent_offset = LogicalVec2 {
460 inline: self.current_state.inline_advance + self.current_state.parent_offset.inline,
461 block: block_start_offset,
462 };
463
464 let outer_state = std::mem::replace(
465 &mut self.current_state,
466 LineItemLayoutInlineContainerState::new(
467 Some(*identifier),
468 parent_offset,
469 block_start_offset + space_above_baseline,
470 positioning_context_or_start_offset_in_parent,
471 ),
472 );
473
474 self.state_stack.push(outer_state);
475 }
476
477 fn end_inline_box(&mut self) {
478 let outer_state = self.state_stack.pop().expect("Ended unknown inline box");
479 let inner_state = std::mem::replace(&mut self.current_state, outer_state);
480
481 let identifier = inner_state.identifier.expect("Ended unknown inline box");
482 let inline_box_state =
483 &*self.layout.inline_box_states[identifier.index_in_inline_boxes as usize];
484 let inline_box = self.layout.ifc.inline_boxes.get(&identifier);
485 let inline_box = &*(inline_box.borrow());
486
487 let containing_block = self.layout.containing_block();
488 let containing_block_writing_mode = containing_block.style.writing_mode;
489
490 let mut padding = inline_box_state.pbm.padding;
491 let mut border = inline_box_state.pbm.border;
492 let mut margin = inline_box_state.pbm.margin.auto_is(Au::zero);
493 if self.for_block_level {
496 padding = LogicalSides::zero();
497 border = LogicalSides::zero();
498 margin = LogicalSides::zero();
499 } else if !inline_box_state.should_clone_pbm() {
500 let mut had_start = inner_state
501 .flags
502 .contains(LineLayoutInlineContainerFlags::HAD_INLINE_START_PBM);
503 let mut had_end = inner_state
504 .flags
505 .contains(LineLayoutInlineContainerFlags::HAD_INLINE_END_PBM);
506
507 if containing_block_writing_mode.is_bidi_ltr() !=
508 inline_box.base.style.writing_mode.is_bidi_ltr()
509 {
510 std::mem::swap(&mut had_start, &mut had_end)
511 }
512
513 if !had_start {
514 padding.inline_start = Au::zero();
515 border.inline_start = Au::zero();
516 margin.inline_start = Au::zero();
517 }
518 if !had_end {
519 padding.inline_end = Au::zero();
520 border.inline_end = Au::zero();
521 margin.inline_end = Au::zero();
522 }
523 }
524 let pbm_sums = padding + border + margin;
525
526 let mut content_rect = LogicalRect {
528 start_corner: LogicalVec2 {
529 inline: self.current_state.inline_advance + pbm_sums.inline_start,
530 block: inner_state.parent_offset.block - self.current_state.parent_offset.block,
531 },
532 size: LogicalVec2 {
533 inline: inner_state.inline_advance,
534 block: if self.is_phantom_line {
535 Au::zero()
536 } else {
537 inline_box_state.base.font_metrics.line_gap
538 },
539 },
540 };
541
542 let style = &inline_box.base.style;
545 let relative_adjustement = if style.get_box().position == Position::Relative {
546 let relative_adjustement = relative_adjustement(style, containing_block);
547 content_rect.start_corner += relative_adjustement;
548 relative_adjustement
549 .to_physical_vector(containing_block_writing_mode)
550 .to_size()
551 } else {
552 PhysicalSize::zero()
553 };
554
555 let (fragments, propagated_floats) =
556 FragmentAndData::resolve_physical_rects_and_adjust_and_collect_floats(
557 inner_state.fragments_and_data,
558 &ContainingBlock {
559 size: ContainingBlockSize {
560 inline: content_rect.size.inline,
561 block: Default::default(),
562 },
563 style: containing_block.style,
564 },
565 relative_adjustement,
566 );
567
568 let physical_content_rect = content_rect.as_physical(Some(containing_block));
571
572 let mut fragment = BoxFragment::new(
573 inline_box.base.base_fragment_info,
574 style.clone(),
575 fragments,
576 physical_content_rect,
577 padding.to_physical(containing_block_writing_mode),
578 border.to_physical(containing_block_writing_mode),
579 margin.to_physical(containing_block_writing_mode),
580 None, );
582
583 let offset_from_parent_ifc = LogicalVec2 {
584 inline: pbm_sums.inline_start + self.current_state.inline_advance,
585 block: content_rect.start_corner.block,
586 }
587 .to_physical_vector(containing_block_writing_mode);
588
589 match inner_state.positioning_context_or_start_offset_in_parent {
590 Either::Left(mut positioning_context) => {
591 positioning_context
592 .layout_collected_children(self.layout.layout_context, &mut fragment);
593 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
594 &offset_from_parent_ifc,
595 PositioningContextLength::zero(),
596 );
597 self.current_positioning_context_mut()
598 .append(positioning_context);
599 },
600 Either::Right(start_offset) => {
601 self.current_positioning_context_mut()
602 .adjust_static_position_of_hoisted_fragments_with_offset(
603 &offset_from_parent_ifc,
604 start_offset,
605 );
606 },
607 }
608
609 self.current_state.inline_advance += inner_state.inline_advance + pbm_sums.inline_sum();
610
611 let fragment = Fragment::Box(Arc::new(fragment));
612 inline_box.base.add_fragment(fragment.clone());
613 self.current_state
614 .fragments_and_data
615 .push(FragmentAndData::new_with_propagated_floats(
616 fragment,
617 content_rect,
618 propagated_floats,
619 ));
620 }
621
622 fn calculate_inline_box_block_start(
623 &self,
624 inline_box_state: &InlineBoxContainerState,
625 space_above_baseline: Au,
626 ) -> Au {
627 if self.is_phantom_line {
628 return Au::zero();
629 };
630 let font_metrics = &inline_box_state.base.font_metrics;
631 let style = &inline_box_state.base.style;
632 let line_gap = font_metrics.line_gap;
633
634 match inline_box_state.base.style.clone_baseline_shift() {
637 BaselineShift::Keyword(BaselineShiftKeyword::Top) => {
638 let line_height = line_height(style, font_metrics, &inline_box_state.base.flags);
639 (line_height - line_gap).scale_by(0.5)
640 },
641 BaselineShift::Keyword(BaselineShiftKeyword::Center) => {
642 (self.line_metrics.block_size - line_gap).scale_by(0.5)
643 },
644 BaselineShift::Keyword(BaselineShiftKeyword::Bottom) => {
645 let line_height = line_height(style, font_metrics, &inline_box_state.base.flags);
646 let half_leading = (line_height - line_gap).scale_by(0.5);
647 self.line_metrics.block_size - line_height + half_leading
648 },
649 _ => {
650 self.line_metrics.baseline_block_offset + inline_box_state.base.baseline_offset -
651 space_above_baseline
652 },
653 }
654 }
655
656 fn layout_text_run(&mut self, text_item: TextRunLineItem) {
657 if text_item.text.is_empty() && !text_item.is_empty_for_text_cursor {
658 return;
659 }
660
661 let mut number_of_justification_opportunities = 0;
662 let mut inline_advance = text_item
663 .text
664 .iter()
665 .map(|shaped_text_slice| {
666 number_of_justification_opportunities += shaped_text_slice.total_word_separators();
667 shaped_text_slice.total_advance()
668 })
669 .sum();
670
671 if !self.justification_adjustment.is_zero() {
672 inline_advance += self
673 .justification_adjustment
674 .scale_by(number_of_justification_opportunities as f32);
675 }
676
677 let font_metrics = &text_item.info.font_info.font.metrics;
681 let start_corner = LogicalVec2 {
682 inline: self.current_state.inline_advance,
683 block: self.current_state.baseline_offset -
684 font_metrics.ascent -
685 self.current_state.parent_offset.block,
686 };
687 let content_rect = LogicalRect {
688 start_corner,
689 size: LogicalVec2 {
690 block: font_metrics.line_gap,
691 inline: inline_advance,
692 },
693 };
694
695 let font_key = text_item.info.font_info.font.key(
696 self.layout.layout_context.painter_id,
697 &self.layout.layout_context.font_context,
698 );
699
700 self.current_state.inline_advance += inline_advance;
701 self.current_state
702 .fragments_and_data
703 .push(FragmentAndData::new(
704 Fragment::Text(Arc::new(TextFragment {
705 base: BaseFragment::new(
706 text_item.base_fragment_info,
707 text_item.inline_styles.style.clone(),
708 PhysicalRect::zero(),
709 ),
710 selected_style: text_item.inline_styles.selected.clone(),
711 font_metrics: font_metrics.clone(),
712 font_key,
713 glyphs: text_item.text,
714 justification_adjustment: self.justification_adjustment,
715 offsets: text_item.offsets,
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(InlineBoxIdentifier),
903 InlineEndBoxPaddingBorderMargin(InlineBoxIdentifier),
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 },
914}
915
916impl LineItem {
917 pub(crate) fn is_in_flow_content(&self) -> bool {
918 matches!(
919 self,
920 Self::TextRun(..) | Self::Atomic(..) | Self::BlockLevel(..)
921 )
922 }
923
924 fn inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
925 match self {
926 LineItem::InlineStartBoxPaddingBorderMargin(identifier) => Some(*identifier),
927 LineItem::InlineEndBoxPaddingBorderMargin(identifier) => Some(*identifier),
928 LineItem::TextRun(identifier, _) => *identifier,
929 LineItem::Atomic(identifier, _) => *identifier,
930 LineItem::AbsolutelyPositioned(identifier, _) => *identifier,
931 LineItem::Float(identifier, _) => *identifier,
932 LineItem::BlockLevel(identifier, _) => *identifier,
933 LineItem::Tab {
934 inline_box_identifier,
935 ..
936 } => *inline_box_identifier,
937 }
938 }
939
940 pub(super) fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
941 match self {
942 LineItem::InlineStartBoxPaddingBorderMargin(_) => true,
943 LineItem::InlineEndBoxPaddingBorderMargin(_) => true,
944 LineItem::TextRun(_, item) => item.trim_whitespace_at_end(whitespace_trimmed),
945 LineItem::Atomic(..) => false,
946 LineItem::AbsolutelyPositioned(..) => true,
947 LineItem::Float(..) => true,
948 LineItem::BlockLevel(..) => true,
949 LineItem::Tab { .. } => false,
950 }
951 }
952
953 pub(super) fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
954 match self {
955 LineItem::InlineStartBoxPaddingBorderMargin(_) => true,
956 LineItem::InlineEndBoxPaddingBorderMargin(_) => true,
957 LineItem::TextRun(_, item) => item.trim_whitespace_at_start(whitespace_trimmed),
958 LineItem::Atomic(..) => false,
959 LineItem::AbsolutelyPositioned(..) => true,
960 LineItem::Float(..) => true,
961 LineItem::BlockLevel(..) => true,
962 LineItem::Tab { .. } => false,
963 }
964 }
965}
966
967#[derive(Debug, MallocSizeOf)]
968pub(crate) struct TextRunOffsets {
969 #[ignore_malloc_size_of = "This is stored primarily in the DOM"]
971 pub shared_selection: SharedSelection,
972 pub character_range: Range<usize>,
975}
976
977pub(super) struct TextRunLineItem {
978 pub info: FontAndScriptInfo,
979 pub base_fragment_info: BaseFragmentInfo,
980 pub inline_styles: SharedInlineStyles,
981 pub text: Vec<Arc<ShapedTextSlice>>,
982 pub offsets: Option<Box<TextRunOffsets>>,
985 pub is_empty_for_text_cursor: bool,
988}
989
990impl TextRunLineItem {
991 fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
992 if matches!(
993 self.inline_styles
994 .style
995 .borrow()
996 .get_inherited_text()
997 .white_space_collapse,
998 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
999 ) {
1000 return false;
1001 }
1002
1003 let index_of_last_non_whitespace = self
1004 .text
1005 .iter()
1006 .rev()
1007 .position(|glyph| !glyph.is_whitespace())
1008 .map(|offset_from_end| self.text.len() - offset_from_end);
1009
1010 let first_whitespace_index = index_of_last_non_whitespace.unwrap_or(0);
1011 *whitespace_trimmed += self
1012 .text
1013 .drain(first_whitespace_index..)
1014 .map(|glyph| glyph.total_advance())
1015 .sum();
1016
1017 index_of_last_non_whitespace.is_none()
1019 }
1020
1021 fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
1022 if matches!(
1023 self.inline_styles
1024 .style
1025 .borrow()
1026 .get_inherited_text()
1027 .white_space_collapse,
1028 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
1029 ) {
1030 return false;
1031 }
1032
1033 let index_of_first_non_whitespace = self
1034 .text
1035 .iter()
1036 .position(|glyph| !glyph.is_whitespace())
1037 .unwrap_or(self.text.len());
1038
1039 *whitespace_trimmed += self
1040 .text
1041 .drain(0..index_of_first_non_whitespace)
1042 .map(|glyph| glyph.total_advance())
1043 .sum();
1044
1045 self.text.is_empty()
1047 }
1048
1049 pub(crate) fn merge_if_possible(
1050 &mut self,
1051 new_info: &FontAndScriptInfo,
1052 new_glyph_store: &Arc<ShapedTextSlice>,
1053 new_offsets: &Option<TextRunOffsets>,
1054 new_inline_styles: &SharedInlineStyles,
1055 ) -> bool {
1056 if !Arc::ptr_eq(&self.info.font_info.font, &new_info.font_info.font) ||
1057 self.info.font_info.bidi_level != new_info.font_info.bidi_level ||
1058 !self.inline_styles.ptr_eq(new_inline_styles)
1059 {
1060 return false;
1061 }
1062 self.text.push(new_glyph_store.clone());
1063
1064 assert_eq!(self.offsets.is_some(), new_offsets.is_some());
1065 if let (Some(new_offsets), Some(existing_offsets)) = (new_offsets, self.offsets.as_mut()) {
1066 existing_offsets.character_range.end = new_offsets.character_range.end;
1067 }
1068
1069 true
1070 }
1071}
1072
1073pub(super) struct AtomicLineItem {
1074 pub fragment: Arc<BoxFragment>,
1075 pub size: LogicalVec2<Au>,
1076 pub positioning_context: Option<PositioningContext>,
1077
1078 pub baseline_offset_in_parent: Au,
1082
1083 pub baseline_offset_in_item: Au,
1085
1086 pub bidi_level: Level,
1088}
1089
1090impl AtomicLineItem {
1091 fn calculate_block_start(&self, line_metrics: &LineMetrics) -> Au {
1094 match self.fragment.style().clone_baseline_shift() {
1095 BaselineShift::Keyword(BaselineShiftKeyword::Top) => Au::zero(),
1096 BaselineShift::Keyword(BaselineShiftKeyword::Center) => {
1097 (line_metrics.block_size - self.size.block).scale_by(0.5)
1098 },
1099 BaselineShift::Keyword(BaselineShiftKeyword::Bottom) => {
1100 line_metrics.block_size - self.size.block
1101 },
1102
1103 _ => {
1105 let baseline = line_metrics.baseline_block_offset + self.baseline_offset_in_parent;
1106 baseline - self.baseline_offset_in_item
1107 },
1108 }
1109 }
1110}
1111
1112pub(super) struct AbsolutelyPositionedLineItem {
1113 pub absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
1114 pub preceding_line_content_would_produce_phantom_line: bool,
1118}
1119
1120pub(super) struct FloatLineItem {
1121 pub fragment: Arc<BoxFragment>,
1122 pub needs_placement: bool,
1126 pub range: Range<PositioningContextLength>,
1130}
1131
1132fn sort_by_indices_in_place<T>(data: &mut [T], mut indices: Vec<usize>) {
1135 for idx in 0..data.len() {
1136 if indices[idx] == idx {
1137 continue;
1138 }
1139
1140 let mut current_idx = idx;
1141 loop {
1142 let target_idx = indices[current_idx];
1143 indices[current_idx] = current_idx;
1144 if indices[target_idx] == target_idx {
1145 break;
1146 }
1147 data.swap(current_idx, target_idx);
1148 current_idx = target_idx;
1149 }
1150 }
1151}