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