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(mut base) = fragment.base_mut() {
305 base.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(mut base) = fragment.base_mut() {
442 if is_float {
443 base.rect.origin -= pbm_sums
444 .start_offset()
445 .to_physical_size(containing_block_writing_mode);
446 } else {
447 base.rect = logical_rect.as_physical(Some(&inline_box_containing_block))
451 }
452 }
453 fragment
454 })
455 .collect();
456
457 let physical_content_rect = content_rect.as_physical(Some(containing_block));
460 let mut fragment = BoxFragment::new(
461 inline_box.base.base_fragment_info,
462 style.clone(),
463 fragments,
464 physical_content_rect,
465 padding.to_physical(containing_block_writing_mode),
466 border.to_physical(containing_block_writing_mode),
467 margin.to_physical(containing_block_writing_mode),
468 None, );
470
471 let offset_from_parent_ifc = LogicalVec2 {
472 inline: pbm_sums.inline_start + self.current_state.inline_advance,
473 block: content_rect.start_corner.block,
474 }
475 .to_physical_vector(containing_block_writing_mode);
476
477 match inner_state.positioning_context_or_start_offset_in_parent {
478 Either::Left(mut positioning_context) => {
479 positioning_context
480 .layout_collected_children(self.layout.layout_context, &mut fragment);
481 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
482 &offset_from_parent_ifc,
483 PositioningContextLength::zero(),
484 );
485 self.current_positioning_context_mut()
486 .append(positioning_context);
487 },
488 Either::Right(start_offset) => {
489 self.current_positioning_context_mut()
490 .adjust_static_position_of_hoisted_fragments_with_offset(
491 &offset_from_parent_ifc,
492 start_offset,
493 );
494 },
495 }
496
497 self.current_state.inline_advance += inner_state.inline_advance + pbm_sums.inline_sum();
498
499 let fragment = Fragment::Box(ArcRefCell::new(fragment));
500 inline_box.base.add_fragment(fragment.clone());
501
502 self.current_state.fragments.push((fragment, content_rect));
503 }
504
505 fn calculate_inline_box_block_start(
506 &self,
507 inline_box_state: &InlineBoxContainerState,
508 space_above_baseline: Au,
509 ) -> Au {
510 if self.is_phantom_line {
511 return Au::zero();
512 };
513 let font_metrics = &inline_box_state.base.font_metrics;
514 let style = &inline_box_state.base.style;
515 let line_gap = font_metrics.line_gap;
516
517 match inline_box_state.base.style.clone_baseline_shift() {
520 BaselineShift::Keyword(BaselineShiftKeyword::Top) => {
521 let line_height = line_height(style, font_metrics, &inline_box_state.base.flags);
522 (line_height - line_gap).scale_by(0.5)
523 },
524 BaselineShift::Keyword(BaselineShiftKeyword::Center) => {
525 (self.line_metrics.block_size - line_gap).scale_by(0.5)
526 },
527 BaselineShift::Keyword(BaselineShiftKeyword::Bottom) => {
528 let line_height = line_height(style, font_metrics, &inline_box_state.base.flags);
529 let half_leading = (line_height - line_gap).scale_by(0.5);
530 self.line_metrics.block_size - line_height + half_leading
531 },
532 _ => {
533 self.line_metrics.baseline_block_offset + inline_box_state.base.baseline_offset -
534 space_above_baseline
535 },
536 }
537 }
538
539 fn layout_text_run(&mut self, text_item: TextRunLineItem) {
540 if text_item.text.is_empty() && !text_item.is_empty_for_text_cursor {
541 return;
542 }
543
544 let mut number_of_justification_opportunities = 0;
545 let mut inline_advance = text_item
546 .text
547 .iter()
548 .map(|shaped_text_slice| {
549 number_of_justification_opportunities += shaped_text_slice.total_word_separators();
550 shaped_text_slice.total_advance()
551 })
552 .sum();
553
554 if !self.justification_adjustment.is_zero() {
555 inline_advance += self
556 .justification_adjustment
557 .scale_by(number_of_justification_opportunities as f32);
558 }
559
560 let font_metrics = &text_item.info.font.metrics;
564 let start_corner = LogicalVec2 {
565 inline: self.current_state.inline_advance,
566 block: self.current_state.baseline_offset -
567 font_metrics.ascent -
568 self.current_state.parent_offset.block,
569 };
570 let content_rect = LogicalRect {
571 start_corner,
572 size: LogicalVec2 {
573 block: font_metrics.line_gap,
574 inline: inline_advance,
575 },
576 };
577
578 let font_key = text_item.info.font.key(
579 self.layout.layout_context.painter_id,
580 &self.layout.layout_context.font_context,
581 );
582
583 self.current_state.inline_advance += inline_advance;
584 self.current_state.fragments.push((
585 Fragment::Text(ArcRefCell::new(TextFragment {
586 base: BaseFragment::new(
587 text_item.base_fragment_info,
588 text_item.inline_styles.style.clone().into(),
589 PhysicalRect::zero(),
590 ),
591 selected_style: text_item.inline_styles.selected.clone(),
592 font_metrics: font_metrics.clone(),
593 font_key,
594 glyphs: text_item.text,
595 justification_adjustment: self.justification_adjustment,
596 offsets: text_item.offsets,
597 is_empty_for_text_cursor: text_item.is_empty_for_text_cursor,
598 })),
599 content_rect,
600 ));
601 }
602
603 fn layout_atomic(&mut self, atomic: AtomicLineItem) {
604 let containing_block = self.containing_block();
609 let ifc_writing_mode = containing_block.style.writing_mode;
610 let content_rect = {
611 let block_start = atomic.calculate_block_start(&self.line_metrics);
612 let atomic_fragment = atomic.fragment.borrow_mut();
613 let padding_border_margin_sides = atomic_fragment
614 .padding_border_margin()
615 .to_logical(ifc_writing_mode);
616
617 let mut atomic_offset = LogicalVec2 {
618 inline: self.current_state.inline_advance +
619 padding_border_margin_sides.inline_start,
620 block: block_start - self.current_state.parent_offset.block +
621 padding_border_margin_sides.block_start,
622 };
623
624 let style = atomic_fragment.style();
625 if style.get_box().position == Position::Relative {
626 atomic_offset += relative_adjustement(&style, containing_block);
627 }
628
629 LogicalRect {
632 start_corner: atomic_offset,
633 size: atomic_fragment
634 .content_rect()
635 .size
636 .to_logical(ifc_writing_mode),
637 }
638 };
639
640 if let Some(mut positioning_context) = atomic.positioning_context {
641 let physical_rect_as_if_in_root = content_rect.as_physical(Some(containing_block));
642 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
643 &physical_rect_as_if_in_root.origin.to_vector(),
644 PositioningContextLength::zero(),
645 );
646
647 self.current_positioning_context_mut()
648 .append(positioning_context);
649 }
650
651 self.current_state.inline_advance += atomic.size.inline;
652
653 self.current_state
654 .fragments
655 .push((Fragment::Box(atomic.fragment), content_rect));
656 }
657
658 fn layout_absolute(&mut self, absolute: AbsolutelyPositionedLineItem) {
659 let absolutely_positioned_box = (*absolute.absolutely_positioned_box).borrow();
660 let style = absolutely_positioned_box.context.style();
661
662 let block_position = self.layout.placement_state.current_margin.solve() -
675 self.current_state.parent_offset.block;
676 let initial_start_corner =
677 if style.get_box().original_display.outside() == DisplayOutside::Inline {
678 LogicalVec2 {
680 inline: self.current_state.inline_advance,
681 block: block_position,
682 }
683 } else {
684 LogicalVec2 {
688 inline: -self.current_state.parent_offset.inline,
689 block: if absolute.preceding_line_content_would_produce_phantom_line {
690 block_position
691 } else {
692 block_position + self.line_metrics.block_size
693 },
694 }
695 };
696
697 let containing_block = self.containing_block();
700 let static_position_rect = LogicalRect {
701 start_corner: initial_start_corner,
702 size: LogicalVec2::zero(),
703 }
704 .as_physical(Some(containing_block));
705
706 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
707 absolute.absolutely_positioned_box.clone(),
708 static_position_rect,
709 LogicalVec2 {
710 inline: AlignFlags::START,
711 block: AlignFlags::START,
712 },
713 containing_block.style.writing_mode,
714 );
715
716 let hoisted_fragment = hoisted_box.fragment.clone();
717 self.current_positioning_context_mut().push(hoisted_box);
718 self.current_state.fragments.push((
719 Fragment::AbsoluteOrFixedPositioned(hoisted_fragment),
720 LogicalRect::zero(),
721 ));
722 }
723
724 fn layout_float(&mut self, float: FloatLineItem) {
725 self.current_state
726 .flags
727 .insert(LineLayoutInlineContainerFlags::HAD_ANY_FLOATS);
728
729 let distance_from_parent_to_ifc = LogicalVec2 {
735 inline: self.current_state.parent_offset.inline,
736 block: self.line_metrics.block_offset + self.current_state.parent_offset.block,
737 };
738 float.fragment.borrow_mut().base.rect.origin -= distance_from_parent_to_ifc
739 .to_physical_size(self.containing_block().style.writing_mode);
740
741 self.current_state
742 .fragments
743 .push((Fragment::Float(float.fragment), LogicalRect::zero()));
744 }
745
746 fn layout_block_level(&mut self, block_level: ArcRefCell<BoxFragment>) {
747 let containing_block = self.containing_block();
748 let mut content_rect = block_level
749 .borrow()
750 .content_rect()
751 .to_logical(containing_block);
752 content_rect.start_corner.inline -= self.current_state.parent_offset.inline;
754 content_rect.start_corner.block -= self.line_metrics.block_offset;
755 let fragment_and_rect = (Fragment::Box(block_level), content_rect);
756 self.current_state.fragments.push(fragment_and_rect);
757 }
758
759 #[inline]
760 fn containing_block(&self) -> &ContainingBlock<'_> {
761 self.layout.containing_block()
762 }
763
764 fn layout_tab(&mut self, advance: Au) {
765 self.current_state.inline_advance += advance;
766 }
767}
768
769pub(super) enum LineItem {
770 InlineStartBoxPaddingBorderMargin(InlineBoxIdentifier),
771 InlineEndBoxPaddingBorderMargin(InlineBoxIdentifier),
772 TextRun(Option<InlineBoxIdentifier>, TextRunLineItem),
773 Atomic(Option<InlineBoxIdentifier>, AtomicLineItem),
774 AbsolutelyPositioned(Option<InlineBoxIdentifier>, AbsolutelyPositionedLineItem),
775 Float(Option<InlineBoxIdentifier>, FloatLineItem),
776 BlockLevel(Option<InlineBoxIdentifier>, ArcRefCell<BoxFragment>),
777 Tab {
778 inline_box_identifier: Option<InlineBoxIdentifier>,
779 advance: Au,
780 bidi_level: Level,
781 },
782}
783
784impl LineItem {
785 pub(crate) fn is_in_flow_content(&self) -> bool {
786 matches!(
787 self,
788 Self::TextRun(..) | Self::Atomic(..) | Self::BlockLevel(..)
789 )
790 }
791
792 fn inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
793 match self {
794 LineItem::InlineStartBoxPaddingBorderMargin(identifier) => Some(*identifier),
795 LineItem::InlineEndBoxPaddingBorderMargin(identifier) => Some(*identifier),
796 LineItem::TextRun(identifier, _) => *identifier,
797 LineItem::Atomic(identifier, _) => *identifier,
798 LineItem::AbsolutelyPositioned(identifier, _) => *identifier,
799 LineItem::Float(identifier, _) => *identifier,
800 LineItem::BlockLevel(identifier, _) => *identifier,
801 LineItem::Tab {
802 inline_box_identifier,
803 ..
804 } => *inline_box_identifier,
805 }
806 }
807
808 pub(super) fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
809 match self {
810 LineItem::InlineStartBoxPaddingBorderMargin(_) => true,
811 LineItem::InlineEndBoxPaddingBorderMargin(_) => true,
812 LineItem::TextRun(_, item) => item.trim_whitespace_at_end(whitespace_trimmed),
813 LineItem::Atomic(..) => false,
814 LineItem::AbsolutelyPositioned(..) => true,
815 LineItem::Float(..) => true,
816 LineItem::BlockLevel(..) => true,
817 LineItem::Tab { .. } => false,
818 }
819 }
820
821 pub(super) fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
822 match self {
823 LineItem::InlineStartBoxPaddingBorderMargin(_) => true,
824 LineItem::InlineEndBoxPaddingBorderMargin(_) => true,
825 LineItem::TextRun(_, item) => item.trim_whitespace_at_start(whitespace_trimmed),
826 LineItem::Atomic(..) => false,
827 LineItem::AbsolutelyPositioned(..) => true,
828 LineItem::Float(..) => true,
829 LineItem::BlockLevel(..) => true,
830 LineItem::Tab { .. } => false,
831 }
832 }
833}
834
835#[derive(Debug, MallocSizeOf)]
836pub(crate) struct TextRunOffsets {
837 #[ignore_malloc_size_of = "This is stored primarily in the DOM"]
839 pub shared_selection: SharedSelection,
840 pub character_range: Range<usize>,
843}
844
845pub(super) struct TextRunLineItem {
846 pub info: Arc<FontAndScriptInfo>,
847 pub base_fragment_info: BaseFragmentInfo,
848 pub inline_styles: SharedInlineStyles,
849 pub text: Vec<Arc<ShapedTextSlice>>,
850 pub offsets: Option<Box<TextRunOffsets>>,
853 pub is_empty_for_text_cursor: bool,
856}
857
858impl TextRunLineItem {
859 fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
860 if matches!(
861 self.inline_styles
862 .style
863 .borrow()
864 .get_inherited_text()
865 .white_space_collapse,
866 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
867 ) {
868 return false;
869 }
870
871 let index_of_last_non_whitespace = self
872 .text
873 .iter()
874 .rev()
875 .position(|glyph| !glyph.is_whitespace())
876 .map(|offset_from_end| self.text.len() - offset_from_end);
877
878 let first_whitespace_index = index_of_last_non_whitespace.unwrap_or(0);
879 *whitespace_trimmed += self
880 .text
881 .drain(first_whitespace_index..)
882 .map(|glyph| glyph.total_advance())
883 .sum();
884
885 index_of_last_non_whitespace.is_none()
887 }
888
889 fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
890 if matches!(
891 self.inline_styles
892 .style
893 .borrow()
894 .get_inherited_text()
895 .white_space_collapse,
896 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
897 ) {
898 return false;
899 }
900
901 let index_of_first_non_whitespace = self
902 .text
903 .iter()
904 .position(|glyph| !glyph.is_whitespace())
905 .unwrap_or(self.text.len());
906
907 *whitespace_trimmed += self
908 .text
909 .drain(0..index_of_first_non_whitespace)
910 .map(|glyph| glyph.total_advance())
911 .sum();
912
913 self.text.is_empty()
915 }
916
917 pub(crate) fn merge_if_possible(
918 &mut self,
919 new_info: &Arc<FontAndScriptInfo>,
920 new_glyph_store: &Arc<ShapedTextSlice>,
921 new_offsets: &Option<TextRunOffsets>,
922 new_inline_styles: &SharedInlineStyles,
923 ) -> bool {
924 if !Arc::ptr_eq(&self.info.font, &new_info.font) ||
925 self.info.bidi_level != new_info.bidi_level ||
926 !self.inline_styles.ptr_eq(new_inline_styles)
927 {
928 return false;
929 }
930 self.text.push(new_glyph_store.clone());
931
932 assert_eq!(self.offsets.is_some(), new_offsets.is_some());
933 if let (Some(new_offsets), Some(existing_offsets)) = (new_offsets, self.offsets.as_mut()) {
934 existing_offsets.character_range.end = new_offsets.character_range.end;
935 }
936
937 true
938 }
939}
940
941pub(super) struct AtomicLineItem {
942 pub fragment: ArcRefCell<BoxFragment>,
943 pub size: LogicalVec2<Au>,
944 pub positioning_context: Option<PositioningContext>,
945
946 pub baseline_offset_in_parent: Au,
950
951 pub baseline_offset_in_item: Au,
953
954 pub bidi_level: Level,
956}
957
958impl AtomicLineItem {
959 fn calculate_block_start(&self, line_metrics: &LineMetrics) -> Au {
962 match self.fragment.borrow().style().clone_baseline_shift() {
963 BaselineShift::Keyword(BaselineShiftKeyword::Top) => Au::zero(),
964 BaselineShift::Keyword(BaselineShiftKeyword::Center) => {
965 (line_metrics.block_size - self.size.block).scale_by(0.5)
966 },
967 BaselineShift::Keyword(BaselineShiftKeyword::Bottom) => {
968 line_metrics.block_size - self.size.block
969 },
970
971 _ => {
973 let baseline = line_metrics.baseline_block_offset + self.baseline_offset_in_parent;
974 baseline - self.baseline_offset_in_item
975 },
976 }
977 }
978}
979
980pub(super) struct AbsolutelyPositionedLineItem {
981 pub absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
982 pub preceding_line_content_would_produce_phantom_line: bool,
986}
987
988pub(super) struct FloatLineItem {
989 pub fragment: ArcRefCell<BoxFragment>,
990 pub needs_placement: bool,
994}
995
996fn sort_by_indices_in_place<T>(data: &mut [T], mut indices: Vec<usize>) {
999 for idx in 0..data.len() {
1000 if indices[idx] == idx {
1001 continue;
1002 }
1003
1004 let mut current_idx = idx;
1005 loop {
1006 let target_idx = indices[current_idx];
1007 indices[current_idx] = current_idx;
1008 if indices[target_idx] == target_idx {
1009 break;
1010 }
1011 data.swap(current_idx, target_idx);
1012 current_idx = target_idx;
1013 }
1014 }
1015}