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, LogicalVec2, PhysicalRect, ToLogical, 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
64pub(super) struct LineItemLayoutInlineContainerState {
68 pub identifier: Option<InlineBoxIdentifier>,
71
72 pub fragments: Vec<(Fragment, LogicalRect<Au>)>,
77
78 pub inline_advance: Au,
80
81 flags: LineLayoutInlineContainerFlags,
83
84 pub parent_offset: LogicalVec2<Au>,
88
89 pub baseline_offset: Au,
93
94 pub positioning_context_or_start_offset_in_parent:
99 Either<PositioningContext, PositioningContextLength>,
100}
101
102impl LineItemLayoutInlineContainerState {
103 fn new(
104 identifier: Option<InlineBoxIdentifier>,
105 parent_offset: LogicalVec2<Au>,
106 baseline_offset: Au,
107 positioning_context_or_start_offset_in_parent: Either<
108 PositioningContext,
109 PositioningContextLength,
110 >,
111 ) -> Self {
112 Self {
113 identifier,
114 fragments: Vec::new(),
115 inline_advance: Au::zero(),
116 flags: LineLayoutInlineContainerFlags::empty(),
117 parent_offset,
118 baseline_offset,
119 positioning_context_or_start_offset_in_parent,
120 }
121 }
122
123 fn root(starting_inline_advance: Au, baseline_offset: Au) -> Self {
124 let mut state = Self::new(
125 None,
126 LogicalVec2::zero(),
127 baseline_offset,
128 Either::Right(PositioningContextLength::zero()),
129 );
130 state.inline_advance = starting_inline_advance;
131 state
132 }
133}
134
135pub(super) struct LineItemLayout<'layout_data, 'layout> {
139 layout: &'layout mut InlineFormattingContextLayout<'layout_data>,
141
142 pub state_stack: Vec<LineItemLayoutInlineContainerState>,
145
146 pub current_state: LineItemLayoutInlineContainerState,
148
149 pub line_metrics: LineMetrics,
152
153 pub justification_adjustment: Au,
156
157 is_phantom_line: bool,
160}
161
162impl LineItemLayout<'_, '_> {
163 pub(super) fn layout_line_items(
164 layout: &mut InlineFormattingContextLayout,
165 line_items: Vec<LineItem>,
166 start_position: LogicalVec2<Au>,
167 effective_block_advance: &LineBlockSizes,
168 justification_adjustment: Au,
169 is_phantom_line: bool,
170 ) -> Vec<Fragment> {
171 let baseline_offset = effective_block_advance.find_baseline_offset();
172 LineItemLayout {
173 layout,
174 state_stack: Vec::new(),
175 current_state: LineItemLayoutInlineContainerState::root(
176 start_position.inline,
177 baseline_offset,
178 ),
179 line_metrics: LineMetrics {
180 block_offset: start_position.block,
181 block_size: effective_block_advance.resolve(),
182 baseline_block_offset: baseline_offset,
183 },
184 justification_adjustment,
185 is_phantom_line,
186 }
187 .layout(line_items)
188 }
189
190 fn prepare_layout_for_inline_box(&mut self, new_inline_box: Option<InlineBoxIdentifier>) {
192 let Some(new_inline_box) = new_inline_box else {
194 while !self.state_stack.is_empty() {
195 self.end_inline_box();
196 }
197 return;
198 };
199
200 let path = self
203 .layout
204 .ifc
205 .inline_boxes
206 .get_path(self.current_state.identifier, new_inline_box);
207 for token in path {
208 match token {
209 InlineBoxTreePathToken::Start(ref identifier) => self.start_inline_box(identifier),
210 InlineBoxTreePathToken::End(_) => self.end_inline_box(),
211 }
212 }
213 }
214
215 pub(super) fn layout(&mut self, mut line_items: Vec<LineItem>) -> Vec<Fragment> {
216 let mut last_level = Level::ltr();
217 let levels: Vec<_> = line_items
218 .iter()
219 .map(|item| {
220 let level = match item {
221 LineItem::TextRun(_, text_run) => text_run.info.bidi_level,
222 LineItem::InlineStartBoxPaddingBorderMargin(_) => last_level,
226 LineItem::InlineEndBoxPaddingBorderMargin(_) => last_level,
227 LineItem::Atomic(_, atomic) => atomic.bidi_level,
228 LineItem::AbsolutelyPositioned(..) => last_level,
229 LineItem::Float(..) => {
230 last_level
233 },
234 LineItem::BlockLevel(..) => last_level,
235 LineItem::Tab { bidi_level, .. } => *bidi_level,
236 };
237 last_level = level;
238 level
239 })
240 .collect();
241
242 if self.layout.ifc.has_right_to_left_content {
243 sort_by_indices_in_place(&mut line_items, BidiInfo::reorder_visual(&levels));
244 }
245
246 let containing_block = self.containing_block();
253 let line_item_iterator = if containing_block.style.writing_mode.is_bidi_ltr() {
254 Either::Left(line_items.into_iter())
255 } else {
256 Either::Right(line_items.into_iter().rev())
257 };
258
259 for item in line_item_iterator.into_iter().by_ref() {
260 self.prepare_layout_for_inline_box(item.inline_box_identifier());
265
266 self.current_state
267 .flags
268 .insert(LineLayoutInlineContainerFlags::HAD_ANY_LINE_ITEMS);
269 match item {
270 LineItem::InlineStartBoxPaddingBorderMargin(_) => {
271 self.current_state
272 .flags
273 .insert(LineLayoutInlineContainerFlags::HAD_INLINE_START_PBM);
274 },
275 LineItem::InlineEndBoxPaddingBorderMargin(_) => {
276 self.current_state
277 .flags
278 .insert(LineLayoutInlineContainerFlags::HAD_INLINE_END_PBM);
279 },
280 LineItem::TextRun(_, text_run) => self.layout_text_run(text_run),
281 LineItem::Atomic(_, atomic) => self.layout_atomic(atomic),
282 LineItem::AbsolutelyPositioned(_, absolute) => self.layout_absolute(absolute),
283 LineItem::Float(_, float) => self.layout_float(float),
284 LineItem::BlockLevel(_, block_level) => self.layout_block_level(block_level),
285 LineItem::Tab { advance, .. } => self.layout_tab(advance),
286 }
287 }
288
289 self.prepare_layout_for_inline_box(None);
291
292 let fragments_and_rectangles = std::mem::take(&mut self.current_state.fragments);
293 let containing_block = self.containing_block();
294 fragments_and_rectangles
295 .into_iter()
296 .map(|(fragment, logical_rect)| {
297 if matches!(fragment, Fragment::Float(_)) {
298 return fragment;
299 }
300
301 if let Some(base) = fragment.base() {
305 base.set_rect(logical_rect.as_physical(Some(containing_block)));
306 }
307
308 fragment
309 })
310 .collect()
311 }
312
313 fn current_positioning_context_mut(&mut self) -> &mut PositioningContext {
314 if let Either::Left(ref mut positioning_context) = self
315 .current_state
316 .positioning_context_or_start_offset_in_parent
317 {
318 return positioning_context;
319 }
320 self.state_stack
321 .iter_mut()
322 .rev()
323 .find_map(
324 |state| match state.positioning_context_or_start_offset_in_parent {
325 Either::Left(ref mut positioning_context) => Some(positioning_context),
326 Either::Right(_) => None,
327 },
328 )
329 .unwrap_or(self.layout.positioning_context)
330 }
331
332 fn start_inline_box(&mut self, identifier: &InlineBoxIdentifier) {
333 let inline_box_state =
334 &*self.layout.inline_box_states[identifier.index_in_inline_boxes as usize];
335 let inline_box = self.layout.ifc.inline_boxes.get(identifier);
336 let inline_box = &*(inline_box.borrow());
337
338 let space_above_baseline = inline_box_state.calculate_space_above_baseline();
339 let block_start_offset =
340 self.calculate_inline_box_block_start(inline_box_state, space_above_baseline);
341
342 let positioning_context_or_start_offset_in_parent =
343 match PositioningContext::new_for_layout_box_base(&inline_box.base) {
344 Some(positioning_context) => Either::Left(positioning_context),
345 None => Either::Right(self.current_positioning_context_mut().len()),
346 };
347
348 let parent_offset = LogicalVec2 {
349 inline: self.current_state.inline_advance + self.current_state.parent_offset.inline,
350 block: block_start_offset,
351 };
352
353 let outer_state = std::mem::replace(
354 &mut self.current_state,
355 LineItemLayoutInlineContainerState::new(
356 Some(*identifier),
357 parent_offset,
358 block_start_offset + space_above_baseline,
359 positioning_context_or_start_offset_in_parent,
360 ),
361 );
362
363 self.state_stack.push(outer_state);
364 }
365
366 fn end_inline_box(&mut self) {
367 let outer_state = self.state_stack.pop().expect("Ended unknown inline box");
368 let inner_state = std::mem::replace(&mut self.current_state, outer_state);
369
370 let identifier = inner_state.identifier.expect("Ended unknown inline box");
371 let inline_box_state =
372 &*self.layout.inline_box_states[identifier.index_in_inline_boxes as usize];
373 let inline_box = self.layout.ifc.inline_boxes.get(&identifier);
374 let inline_box = &*(inline_box.borrow());
375
376 let mut had_start = inner_state
377 .flags
378 .contains(LineLayoutInlineContainerFlags::HAD_INLINE_START_PBM);
379 let mut had_end = inner_state
380 .flags
381 .contains(LineLayoutInlineContainerFlags::HAD_INLINE_END_PBM);
382
383 let containing_block = self.containing_block();
384 let containing_block_writing_mode = containing_block.style.writing_mode;
385 if containing_block_writing_mode.is_bidi_ltr() !=
386 inline_box.base.style.writing_mode.is_bidi_ltr()
387 {
388 std::mem::swap(&mut had_start, &mut had_end)
389 }
390
391 let mut padding = inline_box_state.pbm.padding;
392 let mut border = inline_box_state.pbm.border;
393 let mut margin = inline_box_state.pbm.margin.auto_is(Au::zero);
394 if !had_start {
395 padding.inline_start = Au::zero();
396 border.inline_start = Au::zero();
397 margin.inline_start = Au::zero();
398 }
399 if !had_end {
400 padding.inline_end = Au::zero();
401 border.inline_end = Au::zero();
402 margin.inline_end = Au::zero();
403 }
404 let pbm_sums = padding + border + margin;
405
406 let mut content_rect = LogicalRect {
408 start_corner: LogicalVec2 {
409 inline: self.current_state.inline_advance + pbm_sums.inline_start,
410 block: inner_state.parent_offset.block - self.current_state.parent_offset.block,
411 },
412 size: LogicalVec2 {
413 inline: inner_state.inline_advance,
414 block: if self.is_phantom_line {
415 Au::zero()
416 } else {
417 inline_box_state.base.font_metrics.line_gap
418 },
419 },
420 };
421
422 let style = &inline_box.base.style;
425 if style.get_box().position == Position::Relative {
426 content_rect.start_corner += relative_adjustement(style, containing_block);
427 }
428
429 let inline_box_containing_block = ContainingBlock {
430 size: ContainingBlockSize {
431 inline: content_rect.size.inline,
432 block: Default::default(),
433 },
434 style: containing_block.style,
435 };
436 let fragments = inner_state
437 .fragments
438 .into_iter()
439 .map(|(fragment, logical_rect)| {
440 let is_float = matches!(fragment, Fragment::Float(_));
441 if let Some(base) = fragment.base() {
442 if is_float {
443 base.translate_rect(
444 -pbm_sums
445 .start_offset()
446 .to_physical_size(containing_block_writing_mode),
447 );
448 } else {
449 base.set_rect(logical_rect.as_physical(Some(&inline_box_containing_block)));
453 }
454 }
455 fragment
456 })
457 .collect();
458
459 let physical_content_rect = content_rect.as_physical(Some(containing_block));
462 let mut fragment = BoxFragment::new(
463 inline_box.base.base_fragment_info,
464 style.clone(),
465 fragments,
466 physical_content_rect,
467 padding.to_physical(containing_block_writing_mode),
468 border.to_physical(containing_block_writing_mode),
469 margin.to_physical(containing_block_writing_mode),
470 None, );
472
473 let offset_from_parent_ifc = LogicalVec2 {
474 inline: pbm_sums.inline_start + self.current_state.inline_advance,
475 block: content_rect.start_corner.block,
476 }
477 .to_physical_vector(containing_block_writing_mode);
478
479 match inner_state.positioning_context_or_start_offset_in_parent {
480 Either::Left(mut positioning_context) => {
481 positioning_context
482 .layout_collected_children(self.layout.layout_context, &mut fragment);
483 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
484 &offset_from_parent_ifc,
485 PositioningContextLength::zero(),
486 );
487 self.current_positioning_context_mut()
488 .append(positioning_context);
489 },
490 Either::Right(start_offset) => {
491 self.current_positioning_context_mut()
492 .adjust_static_position_of_hoisted_fragments_with_offset(
493 &offset_from_parent_ifc,
494 start_offset,
495 );
496 },
497 }
498
499 self.current_state.inline_advance += inner_state.inline_advance + pbm_sums.inline_sum();
500
501 let fragment = Fragment::Box(Arc::new(fragment));
502 inline_box.base.add_fragment(fragment.clone());
503
504 self.current_state.fragments.push((fragment, content_rect));
505 }
506
507 fn calculate_inline_box_block_start(
508 &self,
509 inline_box_state: &InlineBoxContainerState,
510 space_above_baseline: Au,
511 ) -> Au {
512 if self.is_phantom_line {
513 return Au::zero();
514 };
515 let font_metrics = &inline_box_state.base.font_metrics;
516 let style = &inline_box_state.base.style;
517 let line_gap = font_metrics.line_gap;
518
519 match inline_box_state.base.style.clone_baseline_shift() {
522 BaselineShift::Keyword(BaselineShiftKeyword::Top) => {
523 let line_height = line_height(style, font_metrics, &inline_box_state.base.flags);
524 (line_height - line_gap).scale_by(0.5)
525 },
526 BaselineShift::Keyword(BaselineShiftKeyword::Center) => {
527 (self.line_metrics.block_size - line_gap).scale_by(0.5)
528 },
529 BaselineShift::Keyword(BaselineShiftKeyword::Bottom) => {
530 let line_height = line_height(style, font_metrics, &inline_box_state.base.flags);
531 let half_leading = (line_height - line_gap).scale_by(0.5);
532 self.line_metrics.block_size - line_height + half_leading
533 },
534 _ => {
535 self.line_metrics.baseline_block_offset + inline_box_state.base.baseline_offset -
536 space_above_baseline
537 },
538 }
539 }
540
541 fn layout_text_run(&mut self, text_item: TextRunLineItem) {
542 if text_item.text.is_empty() && !text_item.is_empty_for_text_cursor {
543 return;
544 }
545
546 let mut number_of_justification_opportunities = 0;
547 let mut inline_advance = text_item
548 .text
549 .iter()
550 .map(|shaped_text_slice| {
551 number_of_justification_opportunities += shaped_text_slice.total_word_separators();
552 shaped_text_slice.total_advance()
553 })
554 .sum();
555
556 if !self.justification_adjustment.is_zero() {
557 inline_advance += self
558 .justification_adjustment
559 .scale_by(number_of_justification_opportunities as f32);
560 }
561
562 let font_metrics = &text_item.info.font.metrics;
566 let start_corner = LogicalVec2 {
567 inline: self.current_state.inline_advance,
568 block: self.current_state.baseline_offset -
569 font_metrics.ascent -
570 self.current_state.parent_offset.block,
571 };
572 let content_rect = LogicalRect {
573 start_corner,
574 size: LogicalVec2 {
575 block: font_metrics.line_gap,
576 inline: inline_advance,
577 },
578 };
579
580 let font_key = text_item.info.font.key(
581 self.layout.layout_context.painter_id,
582 &self.layout.layout_context.font_context,
583 );
584
585 self.current_state.inline_advance += inline_advance;
586 self.current_state.fragments.push((
587 Fragment::Text(Arc::new(TextFragment {
588 base: BaseFragment::new(
589 text_item.base_fragment_info,
590 text_item.inline_styles.style.clone(),
591 PhysicalRect::zero(),
592 ),
593 selected_style: text_item.inline_styles.selected.clone(),
594 font_metrics: font_metrics.clone(),
595 font_key,
596 glyphs: text_item.text,
597 justification_adjustment: self.justification_adjustment,
598 offsets: text_item.offsets,
599 is_empty_for_text_cursor: text_item.is_empty_for_text_cursor,
600 })),
601 content_rect,
602 ));
603 }
604
605 fn layout_atomic(&mut self, atomic: AtomicLineItem) {
606 let containing_block = self.containing_block();
611 let ifc_writing_mode = containing_block.style.writing_mode;
612 let content_rect = {
613 let atomic_fragment = &atomic.fragment;
614 let block_start = atomic.calculate_block_start(&self.line_metrics);
615 let padding_border_margin_sides = atomic_fragment
616 .padding_border_margin()
617 .to_logical(ifc_writing_mode);
618
619 let mut atomic_offset = LogicalVec2 {
620 inline: self.current_state.inline_advance +
621 padding_border_margin_sides.inline_start,
622 block: block_start - self.current_state.parent_offset.block +
623 padding_border_margin_sides.block_start,
624 };
625
626 let style = atomic_fragment.style();
627 if style.get_box().position == Position::Relative {
628 atomic_offset += relative_adjustement(&style, containing_block);
629 }
630
631 LogicalRect {
634 start_corner: atomic_offset,
635 size: atomic_fragment
636 .content_rect()
637 .size
638 .to_logical(ifc_writing_mode),
639 }
640 };
641
642 if let Some(mut positioning_context) = atomic.positioning_context {
643 let physical_rect_as_if_in_root = content_rect.as_physical(Some(containing_block));
644 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
645 &physical_rect_as_if_in_root.origin.to_vector(),
646 PositioningContextLength::zero(),
647 );
648
649 self.current_positioning_context_mut()
650 .append(positioning_context);
651 }
652
653 self.current_state.inline_advance += atomic.size.inline;
654
655 self.current_state
656 .fragments
657 .push((Fragment::Box(atomic.fragment), content_rect));
658 }
659
660 fn layout_absolute(&mut self, absolute: AbsolutelyPositionedLineItem) {
661 let absolutely_positioned_box = (*absolute.absolutely_positioned_box).borrow();
662 let style = absolutely_positioned_box.context.style();
663
664 let block_position = self.layout.placement_state.current_margin.solve() -
677 self.current_state.parent_offset.block;
678 let initial_start_corner =
679 if style.get_box().original_display.outside() == DisplayOutside::Inline {
680 LogicalVec2 {
682 inline: self.current_state.inline_advance,
683 block: block_position,
684 }
685 } else {
686 LogicalVec2 {
690 inline: -self.current_state.parent_offset.inline,
691 block: if absolute.preceding_line_content_would_produce_phantom_line {
692 block_position
693 } else {
694 block_position + self.line_metrics.block_size
695 },
696 }
697 };
698
699 let containing_block = self.containing_block();
702 let static_position_rect = LogicalRect {
703 start_corner: initial_start_corner,
704 size: LogicalVec2::zero(),
705 }
706 .as_physical(Some(containing_block));
707
708 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
709 absolute.absolutely_positioned_box.clone(),
710 static_position_rect,
711 LogicalVec2 {
712 inline: AlignFlags::START,
713 block: AlignFlags::START,
714 },
715 containing_block.style.writing_mode,
716 );
717
718 let hoisted_fragment = hoisted_box.fragment.clone();
719 self.current_positioning_context_mut().push(hoisted_box);
720 self.current_state.fragments.push((
721 Fragment::AbsoluteOrFixedPositioned(hoisted_fragment),
722 LogicalRect::zero(),
723 ));
724 }
725
726 fn layout_float(&mut self, float: FloatLineItem) {
727 self.current_state
728 .flags
729 .insert(LineLayoutInlineContainerFlags::HAD_ANY_FLOATS);
730
731 let distance_from_parent_to_ifc = LogicalVec2 {
737 inline: self.current_state.parent_offset.inline,
738 block: self.line_metrics.block_offset + self.current_state.parent_offset.block,
739 };
740 float.fragment.base.translate_rect(
741 -distance_from_parent_to_ifc
742 .to_physical_size(self.containing_block().style.writing_mode),
743 );
744
745 self.current_state
746 .fragments
747 .push((Fragment::Float(float.fragment), LogicalRect::zero()));
748 }
749
750 fn layout_block_level(&mut self, block_level: Arc<BoxFragment>) {
751 let containing_block = self.containing_block();
752 let mut content_rect = block_level.content_rect().to_logical(containing_block);
753 content_rect.start_corner.inline -= self.current_state.parent_offset.inline;
755 content_rect.start_corner.block -= self.line_metrics.block_offset;
756 let fragment_and_rect = (Fragment::Box(block_level), content_rect);
757 self.current_state.fragments.push(fragment_and_rect);
758 }
759
760 #[inline]
761 fn containing_block(&self) -> &ContainingBlock<'_> {
762 self.layout.containing_block()
763 }
764
765 fn layout_tab(&mut self, advance: Au) {
766 self.current_state.inline_advance += advance;
767 }
768}
769
770pub(super) enum LineItem {
771 InlineStartBoxPaddingBorderMargin(InlineBoxIdentifier),
772 InlineEndBoxPaddingBorderMargin(InlineBoxIdentifier),
773 TextRun(Option<InlineBoxIdentifier>, TextRunLineItem),
774 Atomic(Option<InlineBoxIdentifier>, AtomicLineItem),
775 AbsolutelyPositioned(Option<InlineBoxIdentifier>, AbsolutelyPositionedLineItem),
776 Float(Option<InlineBoxIdentifier>, FloatLineItem),
777 BlockLevel(Option<InlineBoxIdentifier>, Arc<BoxFragment>),
778 Tab {
779 inline_box_identifier: Option<InlineBoxIdentifier>,
780 advance: Au,
781 bidi_level: Level,
782 },
783}
784
785impl LineItem {
786 pub(crate) fn is_in_flow_content(&self) -> bool {
787 matches!(
788 self,
789 Self::TextRun(..) | Self::Atomic(..) | Self::BlockLevel(..)
790 )
791 }
792
793 fn inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
794 match self {
795 LineItem::InlineStartBoxPaddingBorderMargin(identifier) => Some(*identifier),
796 LineItem::InlineEndBoxPaddingBorderMargin(identifier) => Some(*identifier),
797 LineItem::TextRun(identifier, _) => *identifier,
798 LineItem::Atomic(identifier, _) => *identifier,
799 LineItem::AbsolutelyPositioned(identifier, _) => *identifier,
800 LineItem::Float(identifier, _) => *identifier,
801 LineItem::BlockLevel(identifier, _) => *identifier,
802 LineItem::Tab {
803 inline_box_identifier,
804 ..
805 } => *inline_box_identifier,
806 }
807 }
808
809 pub(super) fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
810 match self {
811 LineItem::InlineStartBoxPaddingBorderMargin(_) => true,
812 LineItem::InlineEndBoxPaddingBorderMargin(_) => true,
813 LineItem::TextRun(_, item) => item.trim_whitespace_at_end(whitespace_trimmed),
814 LineItem::Atomic(..) => false,
815 LineItem::AbsolutelyPositioned(..) => true,
816 LineItem::Float(..) => true,
817 LineItem::BlockLevel(..) => true,
818 LineItem::Tab { .. } => false,
819 }
820 }
821
822 pub(super) fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
823 match self {
824 LineItem::InlineStartBoxPaddingBorderMargin(_) => true,
825 LineItem::InlineEndBoxPaddingBorderMargin(_) => true,
826 LineItem::TextRun(_, item) => item.trim_whitespace_at_start(whitespace_trimmed),
827 LineItem::Atomic(..) => false,
828 LineItem::AbsolutelyPositioned(..) => true,
829 LineItem::Float(..) => true,
830 LineItem::BlockLevel(..) => true,
831 LineItem::Tab { .. } => false,
832 }
833 }
834}
835
836#[derive(Debug, MallocSizeOf)]
837pub(crate) struct TextRunOffsets {
838 #[ignore_malloc_size_of = "This is stored primarily in the DOM"]
840 pub shared_selection: SharedSelection,
841 pub character_range: Range<usize>,
844}
845
846pub(super) struct TextRunLineItem {
847 pub info: Arc<FontAndScriptInfo>,
848 pub base_fragment_info: BaseFragmentInfo,
849 pub inline_styles: SharedInlineStyles,
850 pub text: Vec<Arc<ShapedTextSlice>>,
851 pub offsets: Option<Box<TextRunOffsets>>,
854 pub is_empty_for_text_cursor: bool,
857}
858
859impl TextRunLineItem {
860 fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
861 if matches!(
862 self.inline_styles
863 .style
864 .borrow()
865 .get_inherited_text()
866 .white_space_collapse,
867 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
868 ) {
869 return false;
870 }
871
872 let index_of_last_non_whitespace = self
873 .text
874 .iter()
875 .rev()
876 .position(|glyph| !glyph.is_whitespace())
877 .map(|offset_from_end| self.text.len() - offset_from_end);
878
879 let first_whitespace_index = index_of_last_non_whitespace.unwrap_or(0);
880 *whitespace_trimmed += self
881 .text
882 .drain(first_whitespace_index..)
883 .map(|glyph| glyph.total_advance())
884 .sum();
885
886 index_of_last_non_whitespace.is_none()
888 }
889
890 fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
891 if matches!(
892 self.inline_styles
893 .style
894 .borrow()
895 .get_inherited_text()
896 .white_space_collapse,
897 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
898 ) {
899 return false;
900 }
901
902 let index_of_first_non_whitespace = self
903 .text
904 .iter()
905 .position(|glyph| !glyph.is_whitespace())
906 .unwrap_or(self.text.len());
907
908 *whitespace_trimmed += self
909 .text
910 .drain(0..index_of_first_non_whitespace)
911 .map(|glyph| glyph.total_advance())
912 .sum();
913
914 self.text.is_empty()
916 }
917
918 pub(crate) fn merge_if_possible(
919 &mut self,
920 new_info: &Arc<FontAndScriptInfo>,
921 new_glyph_store: &Arc<ShapedTextSlice>,
922 new_offsets: &Option<TextRunOffsets>,
923 new_inline_styles: &SharedInlineStyles,
924 ) -> bool {
925 if !Arc::ptr_eq(&self.info.font, &new_info.font) ||
926 self.info.bidi_level != new_info.bidi_level ||
927 !self.inline_styles.ptr_eq(new_inline_styles)
928 {
929 return false;
930 }
931 self.text.push(new_glyph_store.clone());
932
933 assert_eq!(self.offsets.is_some(), new_offsets.is_some());
934 if let (Some(new_offsets), Some(existing_offsets)) = (new_offsets, self.offsets.as_mut()) {
935 existing_offsets.character_range.end = new_offsets.character_range.end;
936 }
937
938 true
939 }
940}
941
942pub(super) struct AtomicLineItem {
943 pub fragment: Arc<BoxFragment>,
944 pub size: LogicalVec2<Au>,
945 pub positioning_context: Option<PositioningContext>,
946
947 pub baseline_offset_in_parent: Au,
951
952 pub baseline_offset_in_item: Au,
954
955 pub bidi_level: Level,
957}
958
959impl AtomicLineItem {
960 fn calculate_block_start(&self, line_metrics: &LineMetrics) -> Au {
963 match self.fragment.style().clone_baseline_shift() {
964 BaselineShift::Keyword(BaselineShiftKeyword::Top) => Au::zero(),
965 BaselineShift::Keyword(BaselineShiftKeyword::Center) => {
966 (line_metrics.block_size - self.size.block).scale_by(0.5)
967 },
968 BaselineShift::Keyword(BaselineShiftKeyword::Bottom) => {
969 line_metrics.block_size - self.size.block
970 },
971
972 _ => {
974 let baseline = line_metrics.baseline_block_offset + self.baseline_offset_in_parent;
975 baseline - self.baseline_offset_in_item
976 },
977 }
978 }
979}
980
981pub(super) struct AbsolutelyPositionedLineItem {
982 pub absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
983 pub preceding_line_content_would_produce_phantom_line: bool,
987}
988
989pub(super) struct FloatLineItem {
990 pub fragment: Arc<BoxFragment>,
991 pub needs_placement: bool,
995}
996
997fn sort_by_indices_in_place<T>(data: &mut [T], mut indices: Vec<usize>) {
1000 for idx in 0..data.len() {
1001 if indices[idx] == idx {
1002 continue;
1003 }
1004
1005 let mut current_idx = idx;
1006 loop {
1007 let target_idx = indices[current_idx];
1008 indices[current_idx] = current_idx;
1009 if indices[target_idx] == target_idx {
1010 break;
1011 }
1012 data.swap(current_idx, target_idx);
1013 current_idx = target_idx;
1014 }
1015 }
1016}