1use std::ops::Range;
6use std::sync::Arc;
7
8use app_units::Au;
9use bitflags::bitflags;
10use fonts::{FontMetrics, GlyphStore};
11use itertools::Either;
12use layout_api::wrapper_traits::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};
22use webrender_api::FontInstanceKey;
23
24use super::inline_box::{InlineBoxContainerState, InlineBoxIdentifier, InlineBoxTreePathToken};
25use super::{InlineFormattingContextLayout, LineBlockSizes, SharedInlineStyles, line_height};
26use crate::cell::ArcRefCell;
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.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::AnonymousBlockBox(..) => 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::AnonymousBlockBox(_, block_box) => self.layout_block(block_box),
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() {
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 start_corner = LogicalVec2 {
562 inline: self.current_state.inline_advance,
563 block: self.current_state.baseline_offset -
564 text_item.font_metrics.ascent -
565 self.current_state.parent_offset.block,
566 };
567 let content_rect = LogicalRect {
568 start_corner,
569 size: LogicalVec2 {
570 block: text_item.font_metrics.line_gap,
571 inline: inline_advance,
572 },
573 };
574
575 self.current_state.inline_advance += inline_advance;
576 self.current_state.fragments.push((
577 Fragment::Text(ArcRefCell::new(TextFragment {
578 base: BaseFragment::new(
579 text_item.base_fragment_info,
580 text_item.inline_styles.style.clone().into(),
581 PhysicalRect::zero(),
582 ),
583 selected_style: text_item.inline_styles.selected.clone(),
584 font_metrics: text_item.font_metrics,
585 font_key: text_item.font_key,
586 glyphs: text_item.text,
587 justification_adjustment: self.justification_adjustment,
588 offsets: text_item.offsets,
589 })),
590 content_rect,
591 ));
592 }
593
594 fn layout_atomic(&mut self, atomic: AtomicLineItem) {
595 let containing_block = self.containing_block();
600 let ifc_writing_mode = containing_block.style.writing_mode;
601 let content_rect = {
602 let block_start = atomic.calculate_block_start(&self.line_metrics);
603 let atomic_fragment = atomic.fragment.borrow_mut();
604 let padding_border_margin_sides = atomic_fragment
605 .padding_border_margin()
606 .to_logical(ifc_writing_mode);
607
608 let mut atomic_offset = LogicalVec2 {
609 inline: self.current_state.inline_advance +
610 padding_border_margin_sides.inline_start,
611 block: block_start - self.current_state.parent_offset.block +
612 padding_border_margin_sides.block_start,
613 };
614
615 let style = atomic_fragment.style();
616 if style.get_box().position == Position::Relative {
617 atomic_offset += relative_adjustement(&style, containing_block);
618 }
619
620 LogicalRect {
623 start_corner: atomic_offset,
624 size: atomic_fragment
625 .content_rect()
626 .size
627 .to_logical(ifc_writing_mode),
628 }
629 };
630
631 if let Some(mut positioning_context) = atomic.positioning_context {
632 let physical_rect_as_if_in_root = content_rect.as_physical(Some(containing_block));
633 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
634 &physical_rect_as_if_in_root.origin.to_vector(),
635 PositioningContextLength::zero(),
636 );
637
638 self.current_positioning_context_mut()
639 .append(positioning_context);
640 }
641
642 self.current_state.inline_advance += atomic.size.inline;
643
644 self.current_state
645 .fragments
646 .push((Fragment::Box(atomic.fragment), content_rect));
647 }
648
649 fn layout_absolute(&mut self, absolute: AbsolutelyPositionedLineItem) {
650 let absolutely_positioned_box = (*absolute.absolutely_positioned_box).borrow();
651 let style = absolutely_positioned_box.context.style();
652
653 let block_position = self.layout.placement_state.current_margin.solve() -
666 self.current_state.parent_offset.block;
667 let initial_start_corner =
668 if style.get_box().original_display.outside() == DisplayOutside::Inline {
669 LogicalVec2 {
671 inline: self.current_state.inline_advance,
672 block: block_position,
673 }
674 } else {
675 LogicalVec2 {
677 inline: -self.current_state.parent_offset.inline,
678 block: block_position + self.line_metrics.block_size,
679 }
680 };
681
682 let containing_block = self.containing_block();
685 let static_position_rect = LogicalRect {
686 start_corner: initial_start_corner,
687 size: LogicalVec2::zero(),
688 }
689 .as_physical(Some(containing_block));
690
691 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
692 absolute.absolutely_positioned_box.clone(),
693 static_position_rect,
694 LogicalVec2 {
695 inline: AlignFlags::START,
696 block: AlignFlags::START,
697 },
698 containing_block.style.writing_mode,
699 );
700
701 let hoisted_fragment = hoisted_box.fragment.clone();
702 self.current_positioning_context_mut().push(hoisted_box);
703 self.current_state.fragments.push((
704 Fragment::AbsoluteOrFixedPositioned(hoisted_fragment),
705 LogicalRect::zero(),
706 ));
707 }
708
709 fn layout_float(&mut self, float: FloatLineItem) {
710 self.current_state
711 .flags
712 .insert(LineLayoutInlineContainerFlags::HAD_ANY_FLOATS);
713
714 let distance_from_parent_to_ifc = LogicalVec2 {
720 inline: self.current_state.parent_offset.inline,
721 block: self.line_metrics.block_offset + self.current_state.parent_offset.block,
722 };
723 float.fragment.borrow_mut().base.rect.origin -= distance_from_parent_to_ifc
724 .to_physical_size(self.containing_block().style.writing_mode);
725
726 self.current_state
727 .fragments
728 .push((Fragment::Float(float.fragment), LogicalRect::zero()));
729 }
730
731 fn layout_block(&mut self, block: ArcRefCell<BoxFragment>) {
732 let containing_block = self.containing_block();
733 let mut content_rect = block.borrow().content_rect().to_logical(containing_block);
734 content_rect.start_corner.inline -= self.current_state.parent_offset.inline;
736 content_rect.start_corner.block -= self.line_metrics.block_offset;
737 let fragment_and_rect = (Fragment::Box(block), content_rect);
738 self.current_state.fragments.push(fragment_and_rect);
739 }
740
741 #[inline]
742 fn containing_block(&self) -> &ContainingBlock<'_> {
743 self.layout.containing_block()
744 }
745}
746
747pub(super) enum LineItem {
748 InlineStartBoxPaddingBorderMargin(InlineBoxIdentifier),
749 InlineEndBoxPaddingBorderMargin(InlineBoxIdentifier),
750 TextRun(Option<InlineBoxIdentifier>, TextRunLineItem),
751 Atomic(Option<InlineBoxIdentifier>, AtomicLineItem),
752 AbsolutelyPositioned(Option<InlineBoxIdentifier>, AbsolutelyPositionedLineItem),
753 Float(Option<InlineBoxIdentifier>, FloatLineItem),
754 AnonymousBlockBox(Option<InlineBoxIdentifier>, ArcRefCell<BoxFragment>),
755}
756
757impl LineItem {
758 fn inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
759 match self {
760 LineItem::InlineStartBoxPaddingBorderMargin(identifier) => Some(*identifier),
761 LineItem::InlineEndBoxPaddingBorderMargin(identifier) => Some(*identifier),
762 LineItem::TextRun(identifier, _) => *identifier,
763 LineItem::Atomic(identifier, _) => *identifier,
764 LineItem::AbsolutelyPositioned(identifier, _) => *identifier,
765 LineItem::Float(identifier, _) => *identifier,
766 LineItem::AnonymousBlockBox(identifier, _) => *identifier,
767 }
768 }
769
770 pub(super) fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
771 match self {
772 LineItem::InlineStartBoxPaddingBorderMargin(_) => true,
773 LineItem::InlineEndBoxPaddingBorderMargin(_) => true,
774 LineItem::TextRun(_, item) => item.trim_whitespace_at_end(whitespace_trimmed),
775 LineItem::Atomic(..) => false,
776 LineItem::AbsolutelyPositioned(..) => true,
777 LineItem::Float(..) => true,
778 LineItem::AnonymousBlockBox(..) => true,
779 }
780 }
781
782 pub(super) fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
783 match self {
784 LineItem::InlineStartBoxPaddingBorderMargin(_) => true,
785 LineItem::InlineEndBoxPaddingBorderMargin(_) => true,
786 LineItem::TextRun(_, item) => item.trim_whitespace_at_start(whitespace_trimmed),
787 LineItem::Atomic(..) => false,
788 LineItem::AbsolutelyPositioned(..) => true,
789 LineItem::Float(..) => true,
790 LineItem::AnonymousBlockBox(..) => true,
791 }
792 }
793}
794
795#[derive(MallocSizeOf)]
796pub(crate) struct TextRunOffsets {
797 #[ignore_malloc_size_of = "This is stored primarily in the DOM"]
799 pub shared_selection: SharedSelection,
800 pub character_range: Range<usize>,
803}
804
805pub(super) struct TextRunLineItem {
806 pub base_fragment_info: BaseFragmentInfo,
807 pub inline_styles: SharedInlineStyles,
808 pub text: Vec<std::sync::Arc<GlyphStore>>,
809 pub font_metrics: Arc<FontMetrics>,
810 pub font_key: FontInstanceKey,
811 pub bidi_level: Level,
813 pub offsets: Option<Box<TextRunOffsets>>,
816}
817
818impl TextRunLineItem {
819 fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
820 if matches!(
821 self.inline_styles
822 .style
823 .borrow()
824 .get_inherited_text()
825 .white_space_collapse,
826 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
827 ) {
828 return false;
829 }
830
831 let index_of_last_non_whitespace = self
832 .text
833 .iter()
834 .rev()
835 .position(|glyph| !glyph.is_whitespace())
836 .map(|offset_from_end| self.text.len() - offset_from_end);
837
838 let first_whitespace_index = index_of_last_non_whitespace.unwrap_or(0);
839 *whitespace_trimmed += self
840 .text
841 .drain(first_whitespace_index..)
842 .map(|glyph| glyph.total_advance())
843 .sum();
844
845 index_of_last_non_whitespace.is_none()
847 }
848
849 fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
850 if matches!(
851 self.inline_styles
852 .style
853 .borrow()
854 .get_inherited_text()
855 .white_space_collapse,
856 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
857 ) {
858 return false;
859 }
860
861 let index_of_first_non_whitespace = self
862 .text
863 .iter()
864 .position(|glyph| !glyph.is_whitespace())
865 .unwrap_or(self.text.len());
866
867 *whitespace_trimmed += self
868 .text
869 .drain(0..index_of_first_non_whitespace)
870 .map(|glyph| glyph.total_advance())
871 .sum();
872
873 self.text.is_empty()
875 }
876
877 pub(crate) fn merge_if_possible(
878 &mut self,
879 new_font_key: FontInstanceKey,
880 new_bidi_level: Level,
881 new_glyph_store: &Arc<GlyphStore>,
882 new_offsets: &Option<TextRunOffsets>,
883 ) -> bool {
884 if self.font_key != new_font_key || self.bidi_level != new_bidi_level {
885 return false;
886 }
887 self.text.push(new_glyph_store.clone());
888
889 assert_eq!(self.offsets.is_some(), new_offsets.is_some());
890 if let (Some(new_offsets), Some(existing_offsets)) = (new_offsets, self.offsets.as_mut()) {
891 existing_offsets.character_range.end = new_offsets.character_range.end;
892 }
893
894 true
895 }
896}
897
898pub(super) struct AtomicLineItem {
899 pub fragment: ArcRefCell<BoxFragment>,
900 pub size: LogicalVec2<Au>,
901 pub positioning_context: Option<PositioningContext>,
902
903 pub baseline_offset_in_parent: Au,
907
908 pub baseline_offset_in_item: Au,
910
911 pub bidi_level: Level,
913}
914
915impl AtomicLineItem {
916 fn calculate_block_start(&self, line_metrics: &LineMetrics) -> Au {
919 match self.fragment.borrow().style().clone_baseline_shift() {
920 BaselineShift::Keyword(BaselineShiftKeyword::Top) => Au::zero(),
921 BaselineShift::Keyword(BaselineShiftKeyword::Center) => {
922 (line_metrics.block_size - self.size.block).scale_by(0.5)
923 },
924 BaselineShift::Keyword(BaselineShiftKeyword::Bottom) => {
925 line_metrics.block_size - self.size.block
926 },
927
928 _ => {
930 let baseline = line_metrics.baseline_block_offset + self.baseline_offset_in_parent;
931 baseline - self.baseline_offset_in_item
932 },
933 }
934 }
935}
936
937pub(super) struct AbsolutelyPositionedLineItem {
938 pub absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
939}
940
941pub(super) struct FloatLineItem {
942 pub fragment: ArcRefCell<BoxFragment>,
943 pub needs_placement: bool,
947}
948
949fn sort_by_indices_in_place<T>(data: &mut [T], mut indices: Vec<usize>) {
952 for idx in 0..data.len() {
953 if indices[idx] == idx {
954 continue;
955 }
956
957 let mut current_idx = idx;
958 loop {
959 let target_idx = indices[current_idx];
960 indices[current_idx] = current_idx;
961 if indices[target_idx] == target_idx {
962 break;
963 }
964 data.swap(current_idx, target_idx);
965 current_idx = target_idx;
966 }
967 }
968}