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 fn reorder_line_items_for_bidi(
221 &self,
222 mut line_items: Vec<LineItem>,
223 ) -> impl Iterator<Item = LineItem> + use<> {
224 let iterator = |line_items: Vec<LineItem>| {
225 if self.containing_block().style.writing_mode.is_bidi_ltr() {
232 Either::Left(line_items.into_iter())
233 } else {
234 Either::Right(line_items.into_iter().rev())
235 }
236 };
237
238 if !self.layout.ifc.has_right_to_left_content {
239 return iterator(line_items);
244 }
245
246 let mut last_level = Level::ltr();
247 let levels: Vec<_> = line_items
248 .iter()
249 .map(|item| {
250 let level = match item {
251 LineItem::TextRun(_, text_run) => text_run.info.bidi_level,
252 LineItem::InlineStartBoxPaddingBorderMargin(_) => last_level,
256 LineItem::InlineEndBoxPaddingBorderMargin(_) => last_level,
257 LineItem::Atomic(_, atomic) => atomic.bidi_level,
258 LineItem::AbsolutelyPositioned(..) => last_level,
259 LineItem::Float(..) => {
260 last_level
263 },
264 LineItem::BlockLevel(..) => last_level,
265 LineItem::Tab { bidi_level, .. } => *bidi_level,
266 };
267 last_level = level;
268 level
269 })
270 .collect();
271
272 sort_by_indices_in_place(&mut line_items, BidiInfo::reorder_visual(&levels));
273 iterator(line_items)
274 }
275
276 pub(super) fn layout(&mut self, line_items: Vec<LineItem>) -> Vec<Fragment> {
277 let line_item_iterator = self.reorder_line_items_for_bidi(line_items);
278 for item in line_item_iterator.into_iter().by_ref() {
279 self.prepare_layout_for_inline_box(item.inline_box_identifier());
284
285 self.current_state
286 .flags
287 .insert(LineLayoutInlineContainerFlags::HAD_ANY_LINE_ITEMS);
288 match item {
289 LineItem::InlineStartBoxPaddingBorderMargin(_) => {
290 self.current_state
291 .flags
292 .insert(LineLayoutInlineContainerFlags::HAD_INLINE_START_PBM);
293 },
294 LineItem::InlineEndBoxPaddingBorderMargin(_) => {
295 self.current_state
296 .flags
297 .insert(LineLayoutInlineContainerFlags::HAD_INLINE_END_PBM);
298 },
299 LineItem::TextRun(_, text_run) => self.layout_text_run(text_run),
300 LineItem::Atomic(_, atomic) => self.layout_atomic(atomic),
301 LineItem::AbsolutelyPositioned(_, absolute) => self.layout_absolute(absolute),
302 LineItem::Float(_, float) => self.layout_float(float),
303 LineItem::BlockLevel(_, block_level) => self.layout_block_level(block_level),
304 LineItem::Tab { advance, .. } => self.layout_tab(advance),
305 }
306 }
307
308 self.prepare_layout_for_inline_box(None);
310
311 let fragments_and_rectangles = std::mem::take(&mut self.current_state.fragments);
312 let containing_block = self.containing_block();
313 fragments_and_rectangles
314 .into_iter()
315 .map(|(fragment, logical_rect)| {
316 if matches!(fragment, Fragment::Float(_)) {
317 return fragment;
318 }
319
320 if let Some(base) = fragment.base() {
324 base.set_rect(logical_rect.as_physical(Some(containing_block)));
325 }
326
327 fragment
328 })
329 .collect()
330 }
331
332 fn current_positioning_context_mut(&mut self) -> &mut PositioningContext {
333 if let Either::Left(ref mut positioning_context) = self
334 .current_state
335 .positioning_context_or_start_offset_in_parent
336 {
337 return positioning_context;
338 }
339 self.state_stack
340 .iter_mut()
341 .rev()
342 .find_map(
343 |state| match state.positioning_context_or_start_offset_in_parent {
344 Either::Left(ref mut positioning_context) => Some(positioning_context),
345 Either::Right(_) => None,
346 },
347 )
348 .unwrap_or(self.layout.positioning_context)
349 }
350
351 fn start_inline_box(&mut self, identifier: &InlineBoxIdentifier) {
352 let inline_box_state =
353 &*self.layout.inline_box_states[identifier.index_in_inline_boxes as usize];
354 let inline_box = self.layout.ifc.inline_boxes.get(identifier);
355 let inline_box = &*(inline_box.borrow());
356
357 let space_above_baseline = inline_box_state.calculate_space_above_baseline();
358 let block_start_offset =
359 self.calculate_inline_box_block_start(inline_box_state, space_above_baseline);
360
361 let positioning_context_or_start_offset_in_parent =
362 match PositioningContext::new_for_layout_box_base(&inline_box.base) {
363 Some(positioning_context) => Either::Left(positioning_context),
364 None => Either::Right(self.current_positioning_context_mut().len()),
365 };
366
367 let parent_offset = LogicalVec2 {
368 inline: self.current_state.inline_advance + self.current_state.parent_offset.inline,
369 block: block_start_offset,
370 };
371
372 let outer_state = std::mem::replace(
373 &mut self.current_state,
374 LineItemLayoutInlineContainerState::new(
375 Some(*identifier),
376 parent_offset,
377 block_start_offset + space_above_baseline,
378 positioning_context_or_start_offset_in_parent,
379 ),
380 );
381
382 self.state_stack.push(outer_state);
383 }
384
385 fn end_inline_box(&mut self) {
386 let outer_state = self.state_stack.pop().expect("Ended unknown inline box");
387 let inner_state = std::mem::replace(&mut self.current_state, outer_state);
388
389 let identifier = inner_state.identifier.expect("Ended unknown inline box");
390 let inline_box_state =
391 &*self.layout.inline_box_states[identifier.index_in_inline_boxes as usize];
392 let inline_box = self.layout.ifc.inline_boxes.get(&identifier);
393 let inline_box = &*(inline_box.borrow());
394
395 let mut had_start = inner_state
396 .flags
397 .contains(LineLayoutInlineContainerFlags::HAD_INLINE_START_PBM);
398 let mut had_end = inner_state
399 .flags
400 .contains(LineLayoutInlineContainerFlags::HAD_INLINE_END_PBM);
401
402 let containing_block = self.containing_block();
403 let containing_block_writing_mode = containing_block.style.writing_mode;
404 if containing_block_writing_mode.is_bidi_ltr() !=
405 inline_box.base.style.writing_mode.is_bidi_ltr()
406 {
407 std::mem::swap(&mut had_start, &mut had_end)
408 }
409
410 let mut padding = inline_box_state.pbm.padding;
411 let mut border = inline_box_state.pbm.border;
412 let mut margin = inline_box_state.pbm.margin.auto_is(Au::zero);
413 if !had_start {
414 padding.inline_start = Au::zero();
415 border.inline_start = Au::zero();
416 margin.inline_start = Au::zero();
417 }
418 if !had_end {
419 padding.inline_end = Au::zero();
420 border.inline_end = Au::zero();
421 margin.inline_end = Au::zero();
422 }
423 let pbm_sums = padding + border + margin;
424
425 let mut content_rect = LogicalRect {
427 start_corner: LogicalVec2 {
428 inline: self.current_state.inline_advance + pbm_sums.inline_start,
429 block: inner_state.parent_offset.block - self.current_state.parent_offset.block,
430 },
431 size: LogicalVec2 {
432 inline: inner_state.inline_advance,
433 block: if self.is_phantom_line {
434 Au::zero()
435 } else {
436 inline_box_state.base.font_metrics.line_gap
437 },
438 },
439 };
440
441 let style = &inline_box.base.style;
444 if style.get_box().position == Position::Relative {
445 content_rect.start_corner += relative_adjustement(style, containing_block);
446 }
447
448 let inline_box_containing_block = ContainingBlock {
449 size: ContainingBlockSize {
450 inline: content_rect.size.inline,
451 block: Default::default(),
452 },
453 style: containing_block.style,
454 };
455 let fragments = inner_state
456 .fragments
457 .into_iter()
458 .map(|(fragment, logical_rect)| {
459 let is_float = matches!(fragment, Fragment::Float(_));
460 if let Some(base) = fragment.base() {
461 if is_float {
462 base.translate_rect(
463 -pbm_sums
464 .start_offset()
465 .to_physical_size(containing_block_writing_mode),
466 );
467 } else {
468 base.set_rect(logical_rect.as_physical(Some(&inline_box_containing_block)));
472 }
473 }
474 fragment
475 })
476 .collect();
477
478 let physical_content_rect = content_rect.as_physical(Some(containing_block));
481 let mut fragment = BoxFragment::new(
482 inline_box.base.base_fragment_info,
483 style.clone(),
484 fragments,
485 physical_content_rect,
486 padding.to_physical(containing_block_writing_mode),
487 border.to_physical(containing_block_writing_mode),
488 margin.to_physical(containing_block_writing_mode),
489 None, );
491
492 let offset_from_parent_ifc = LogicalVec2 {
493 inline: pbm_sums.inline_start + self.current_state.inline_advance,
494 block: content_rect.start_corner.block,
495 }
496 .to_physical_vector(containing_block_writing_mode);
497
498 match inner_state.positioning_context_or_start_offset_in_parent {
499 Either::Left(mut positioning_context) => {
500 positioning_context
501 .layout_collected_children(self.layout.layout_context, &mut fragment);
502 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
503 &offset_from_parent_ifc,
504 PositioningContextLength::zero(),
505 );
506 self.current_positioning_context_mut()
507 .append(positioning_context);
508 },
509 Either::Right(start_offset) => {
510 self.current_positioning_context_mut()
511 .adjust_static_position_of_hoisted_fragments_with_offset(
512 &offset_from_parent_ifc,
513 start_offset,
514 );
515 },
516 }
517
518 self.current_state.inline_advance += inner_state.inline_advance + pbm_sums.inline_sum();
519
520 let fragment = Fragment::Box(Arc::new(fragment));
521 inline_box.base.add_fragment(fragment.clone());
522
523 self.current_state.fragments.push((fragment, content_rect));
524 }
525
526 fn calculate_inline_box_block_start(
527 &self,
528 inline_box_state: &InlineBoxContainerState,
529 space_above_baseline: Au,
530 ) -> Au {
531 if self.is_phantom_line {
532 return Au::zero();
533 };
534 let font_metrics = &inline_box_state.base.font_metrics;
535 let style = &inline_box_state.base.style;
536 let line_gap = font_metrics.line_gap;
537
538 match inline_box_state.base.style.clone_baseline_shift() {
541 BaselineShift::Keyword(BaselineShiftKeyword::Top) => {
542 let line_height = line_height(style, font_metrics, &inline_box_state.base.flags);
543 (line_height - line_gap).scale_by(0.5)
544 },
545 BaselineShift::Keyword(BaselineShiftKeyword::Center) => {
546 (self.line_metrics.block_size - line_gap).scale_by(0.5)
547 },
548 BaselineShift::Keyword(BaselineShiftKeyword::Bottom) => {
549 let line_height = line_height(style, font_metrics, &inline_box_state.base.flags);
550 let half_leading = (line_height - line_gap).scale_by(0.5);
551 self.line_metrics.block_size - line_height + half_leading
552 },
553 _ => {
554 self.line_metrics.baseline_block_offset + inline_box_state.base.baseline_offset -
555 space_above_baseline
556 },
557 }
558 }
559
560 fn layout_text_run(&mut self, text_item: TextRunLineItem) {
561 if text_item.text.is_empty() && !text_item.is_empty_for_text_cursor {
562 return;
563 }
564
565 let mut number_of_justification_opportunities = 0;
566 let mut inline_advance = text_item
567 .text
568 .iter()
569 .map(|shaped_text_slice| {
570 number_of_justification_opportunities += shaped_text_slice.total_word_separators();
571 shaped_text_slice.total_advance()
572 })
573 .sum();
574
575 if !self.justification_adjustment.is_zero() {
576 inline_advance += self
577 .justification_adjustment
578 .scale_by(number_of_justification_opportunities as f32);
579 }
580
581 let font_metrics = &text_item.info.font.metrics;
585 let start_corner = LogicalVec2 {
586 inline: self.current_state.inline_advance,
587 block: self.current_state.baseline_offset -
588 font_metrics.ascent -
589 self.current_state.parent_offset.block,
590 };
591 let content_rect = LogicalRect {
592 start_corner,
593 size: LogicalVec2 {
594 block: font_metrics.line_gap,
595 inline: inline_advance,
596 },
597 };
598
599 let font_key = text_item.info.font.key(
600 self.layout.layout_context.painter_id,
601 &self.layout.layout_context.font_context,
602 );
603
604 self.current_state.inline_advance += inline_advance;
605 self.current_state.fragments.push((
606 Fragment::Text(Arc::new(TextFragment {
607 base: BaseFragment::new(
608 text_item.base_fragment_info,
609 text_item.inline_styles.style.clone(),
610 PhysicalRect::zero(),
611 ),
612 selected_style: text_item.inline_styles.selected.clone(),
613 font_metrics: font_metrics.clone(),
614 font_key,
615 glyphs: text_item.text,
616 justification_adjustment: self.justification_adjustment,
617 offsets: text_item.offsets,
618 is_empty_for_text_cursor: text_item.is_empty_for_text_cursor,
619 })),
620 content_rect,
621 ));
622 }
623
624 fn layout_atomic(&mut self, atomic: AtomicLineItem) {
625 let containing_block = self.containing_block();
630 let ifc_writing_mode = containing_block.style.writing_mode;
631 let content_rect = {
632 let atomic_fragment = &atomic.fragment;
633 let block_start = atomic.calculate_block_start(&self.line_metrics);
634 let padding_border_margin_sides = atomic_fragment
635 .padding_border_margin()
636 .to_logical(ifc_writing_mode);
637
638 let mut atomic_offset = LogicalVec2 {
639 inline: self.current_state.inline_advance +
640 padding_border_margin_sides.inline_start,
641 block: block_start - self.current_state.parent_offset.block +
642 padding_border_margin_sides.block_start,
643 };
644
645 let style = atomic_fragment.style();
646 if style.get_box().position == Position::Relative {
647 atomic_offset += relative_adjustement(&style, containing_block);
648 }
649
650 LogicalRect {
653 start_corner: atomic_offset,
654 size: atomic_fragment
655 .content_rect()
656 .size
657 .to_logical(ifc_writing_mode),
658 }
659 };
660
661 if let Some(mut positioning_context) = atomic.positioning_context {
662 let physical_rect_as_if_in_root = content_rect.as_physical(Some(containing_block));
663 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
664 &physical_rect_as_if_in_root.origin.to_vector(),
665 PositioningContextLength::zero(),
666 );
667
668 self.current_positioning_context_mut()
669 .append(positioning_context);
670 }
671
672 self.current_state.inline_advance += atomic.size.inline;
673
674 self.current_state
675 .fragments
676 .push((Fragment::Box(atomic.fragment), content_rect));
677 }
678
679 fn layout_absolute(&mut self, absolute: AbsolutelyPositionedLineItem) {
680 let absolutely_positioned_box = (*absolute.absolutely_positioned_box).borrow();
681 let style = absolutely_positioned_box.context.style();
682
683 let block_position = self.layout.placement_state.current_margin.solve() -
696 self.current_state.parent_offset.block;
697 let initial_start_corner =
698 if style.get_box().original_display.outside() == DisplayOutside::Inline {
699 LogicalVec2 {
701 inline: self.current_state.inline_advance,
702 block: block_position,
703 }
704 } else {
705 LogicalVec2 {
709 inline: -self.current_state.parent_offset.inline,
710 block: if absolute.preceding_line_content_would_produce_phantom_line {
711 block_position
712 } else {
713 block_position + self.line_metrics.block_size
714 },
715 }
716 };
717
718 let containing_block = self.containing_block();
721 let static_position_rect = LogicalRect {
722 start_corner: initial_start_corner,
723 size: LogicalVec2::zero(),
724 }
725 .as_physical(Some(containing_block));
726
727 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
728 absolute.absolutely_positioned_box.clone(),
729 static_position_rect,
730 LogicalVec2 {
731 inline: AlignFlags::START,
732 block: AlignFlags::START,
733 },
734 containing_block.style.writing_mode,
735 );
736
737 let hoisted_fragment = hoisted_box.fragment.clone();
738 self.current_positioning_context_mut().push(hoisted_box);
739 self.current_state.fragments.push((
740 Fragment::AbsoluteOrFixedPositionedPlaceholder(hoisted_fragment),
741 LogicalRect::zero(),
742 ));
743 }
744
745 fn layout_float(&mut self, float: FloatLineItem) {
746 self.current_state
747 .flags
748 .insert(LineLayoutInlineContainerFlags::HAD_ANY_FLOATS);
749
750 let distance_from_parent_to_ifc = LogicalVec2 {
756 inline: self.current_state.parent_offset.inline,
757 block: self.line_metrics.block_offset + self.current_state.parent_offset.block,
758 };
759 float.fragment.base.translate_rect(
760 -distance_from_parent_to_ifc
761 .to_physical_size(self.containing_block().style.writing_mode),
762 );
763
764 self.current_state
765 .fragments
766 .push((Fragment::Float(float.fragment), LogicalRect::zero()));
767 }
768
769 fn layout_block_level(&mut self, block_level: Arc<BoxFragment>) {
770 let containing_block = self.containing_block();
771 let mut content_rect = block_level.content_rect().to_logical(containing_block);
772 content_rect.start_corner.inline -= self.current_state.parent_offset.inline;
774 content_rect.start_corner.block -= self.line_metrics.block_offset;
775 let fragment_and_rect = (Fragment::Box(block_level), content_rect);
776 self.current_state.fragments.push(fragment_and_rect);
777 }
778
779 #[inline]
780 fn containing_block(&self) -> &ContainingBlock<'_> {
781 self.layout.containing_block()
782 }
783
784 fn layout_tab(&mut self, advance: Au) {
785 self.current_state.inline_advance += advance;
786 }
787}
788
789pub(super) enum LineItem {
790 InlineStartBoxPaddingBorderMargin(InlineBoxIdentifier),
791 InlineEndBoxPaddingBorderMargin(InlineBoxIdentifier),
792 TextRun(Option<InlineBoxIdentifier>, TextRunLineItem),
793 Atomic(Option<InlineBoxIdentifier>, AtomicLineItem),
794 AbsolutelyPositioned(Option<InlineBoxIdentifier>, AbsolutelyPositionedLineItem),
795 Float(Option<InlineBoxIdentifier>, FloatLineItem),
796 BlockLevel(Option<InlineBoxIdentifier>, Arc<BoxFragment>),
797 Tab {
798 inline_box_identifier: Option<InlineBoxIdentifier>,
799 advance: Au,
800 bidi_level: Level,
801 },
802}
803
804impl LineItem {
805 pub(crate) fn is_in_flow_content(&self) -> bool {
806 matches!(
807 self,
808 Self::TextRun(..) | Self::Atomic(..) | Self::BlockLevel(..)
809 )
810 }
811
812 fn inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
813 match self {
814 LineItem::InlineStartBoxPaddingBorderMargin(identifier) => Some(*identifier),
815 LineItem::InlineEndBoxPaddingBorderMargin(identifier) => Some(*identifier),
816 LineItem::TextRun(identifier, _) => *identifier,
817 LineItem::Atomic(identifier, _) => *identifier,
818 LineItem::AbsolutelyPositioned(identifier, _) => *identifier,
819 LineItem::Float(identifier, _) => *identifier,
820 LineItem::BlockLevel(identifier, _) => *identifier,
821 LineItem::Tab {
822 inline_box_identifier,
823 ..
824 } => *inline_box_identifier,
825 }
826 }
827
828 pub(super) fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
829 match self {
830 LineItem::InlineStartBoxPaddingBorderMargin(_) => true,
831 LineItem::InlineEndBoxPaddingBorderMargin(_) => true,
832 LineItem::TextRun(_, item) => item.trim_whitespace_at_end(whitespace_trimmed),
833 LineItem::Atomic(..) => false,
834 LineItem::AbsolutelyPositioned(..) => true,
835 LineItem::Float(..) => true,
836 LineItem::BlockLevel(..) => true,
837 LineItem::Tab { .. } => false,
838 }
839 }
840
841 pub(super) fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
842 match self {
843 LineItem::InlineStartBoxPaddingBorderMargin(_) => true,
844 LineItem::InlineEndBoxPaddingBorderMargin(_) => true,
845 LineItem::TextRun(_, item) => item.trim_whitespace_at_start(whitespace_trimmed),
846 LineItem::Atomic(..) => false,
847 LineItem::AbsolutelyPositioned(..) => true,
848 LineItem::Float(..) => true,
849 LineItem::BlockLevel(..) => true,
850 LineItem::Tab { .. } => false,
851 }
852 }
853}
854
855#[derive(Debug, MallocSizeOf)]
856pub(crate) struct TextRunOffsets {
857 #[ignore_malloc_size_of = "This is stored primarily in the DOM"]
859 pub shared_selection: SharedSelection,
860 pub character_range: Range<usize>,
863}
864
865pub(super) struct TextRunLineItem {
866 pub info: Arc<FontAndScriptInfo>,
867 pub base_fragment_info: BaseFragmentInfo,
868 pub inline_styles: SharedInlineStyles,
869 pub text: Vec<Arc<ShapedTextSlice>>,
870 pub offsets: Option<Box<TextRunOffsets>>,
873 pub is_empty_for_text_cursor: bool,
876}
877
878impl TextRunLineItem {
879 fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
880 if matches!(
881 self.inline_styles
882 .style
883 .borrow()
884 .get_inherited_text()
885 .white_space_collapse,
886 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
887 ) {
888 return false;
889 }
890
891 let index_of_last_non_whitespace = self
892 .text
893 .iter()
894 .rev()
895 .position(|glyph| !glyph.is_whitespace())
896 .map(|offset_from_end| self.text.len() - offset_from_end);
897
898 let first_whitespace_index = index_of_last_non_whitespace.unwrap_or(0);
899 *whitespace_trimmed += self
900 .text
901 .drain(first_whitespace_index..)
902 .map(|glyph| glyph.total_advance())
903 .sum();
904
905 index_of_last_non_whitespace.is_none()
907 }
908
909 fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
910 if matches!(
911 self.inline_styles
912 .style
913 .borrow()
914 .get_inherited_text()
915 .white_space_collapse,
916 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
917 ) {
918 return false;
919 }
920
921 let index_of_first_non_whitespace = self
922 .text
923 .iter()
924 .position(|glyph| !glyph.is_whitespace())
925 .unwrap_or(self.text.len());
926
927 *whitespace_trimmed += self
928 .text
929 .drain(0..index_of_first_non_whitespace)
930 .map(|glyph| glyph.total_advance())
931 .sum();
932
933 self.text.is_empty()
935 }
936
937 pub(crate) fn merge_if_possible(
938 &mut self,
939 new_info: &Arc<FontAndScriptInfo>,
940 new_glyph_store: &Arc<ShapedTextSlice>,
941 new_offsets: &Option<TextRunOffsets>,
942 new_inline_styles: &SharedInlineStyles,
943 ) -> bool {
944 if !Arc::ptr_eq(&self.info.font, &new_info.font) ||
945 self.info.bidi_level != new_info.bidi_level ||
946 !self.inline_styles.ptr_eq(new_inline_styles)
947 {
948 return false;
949 }
950 self.text.push(new_glyph_store.clone());
951
952 assert_eq!(self.offsets.is_some(), new_offsets.is_some());
953 if let (Some(new_offsets), Some(existing_offsets)) = (new_offsets, self.offsets.as_mut()) {
954 existing_offsets.character_range.end = new_offsets.character_range.end;
955 }
956
957 true
958 }
959}
960
961pub(super) struct AtomicLineItem {
962 pub fragment: Arc<BoxFragment>,
963 pub size: LogicalVec2<Au>,
964 pub positioning_context: Option<PositioningContext>,
965
966 pub baseline_offset_in_parent: Au,
970
971 pub baseline_offset_in_item: Au,
973
974 pub bidi_level: Level,
976}
977
978impl AtomicLineItem {
979 fn calculate_block_start(&self, line_metrics: &LineMetrics) -> Au {
982 match self.fragment.style().clone_baseline_shift() {
983 BaselineShift::Keyword(BaselineShiftKeyword::Top) => Au::zero(),
984 BaselineShift::Keyword(BaselineShiftKeyword::Center) => {
985 (line_metrics.block_size - self.size.block).scale_by(0.5)
986 },
987 BaselineShift::Keyword(BaselineShiftKeyword::Bottom) => {
988 line_metrics.block_size - self.size.block
989 },
990
991 _ => {
993 let baseline = line_metrics.baseline_block_offset + self.baseline_offset_in_parent;
994 baseline - self.baseline_offset_in_item
995 },
996 }
997 }
998}
999
1000pub(super) struct AbsolutelyPositionedLineItem {
1001 pub absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
1002 pub preceding_line_content_would_produce_phantom_line: bool,
1006}
1007
1008pub(super) struct FloatLineItem {
1009 pub fragment: Arc<BoxFragment>,
1010 pub needs_placement: bool,
1014}
1015
1016fn sort_by_indices_in_place<T>(data: &mut [T], mut indices: Vec<usize>) {
1019 for idx in 0..data.len() {
1020 if indices[idx] == idx {
1021 continue;
1022 }
1023
1024 let mut current_idx = idx;
1025 loop {
1026 let target_idx = indices[current_idx];
1027 indices[current_idx] = current_idx;
1028 if indices[target_idx] == target_idx {
1029 break;
1030 }
1031 data.swap(current_idx, target_idx);
1032 current_idx = target_idx;
1033 }
1034 }
1035}