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() && !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 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 is_empty_for_text_cursor: text_item.is_empty_for_text_cursor,
590 })),
591 content_rect,
592 ));
593 }
594
595 fn layout_atomic(&mut self, atomic: AtomicLineItem) {
596 let containing_block = self.containing_block();
601 let ifc_writing_mode = containing_block.style.writing_mode;
602 let content_rect = {
603 let block_start = atomic.calculate_block_start(&self.line_metrics);
604 let atomic_fragment = atomic.fragment.borrow_mut();
605 let padding_border_margin_sides = atomic_fragment
606 .padding_border_margin()
607 .to_logical(ifc_writing_mode);
608
609 let mut atomic_offset = LogicalVec2 {
610 inline: self.current_state.inline_advance +
611 padding_border_margin_sides.inline_start,
612 block: block_start - self.current_state.parent_offset.block +
613 padding_border_margin_sides.block_start,
614 };
615
616 let style = atomic_fragment.style();
617 if style.get_box().position == Position::Relative {
618 atomic_offset += relative_adjustement(&style, containing_block);
619 }
620
621 LogicalRect {
624 start_corner: atomic_offset,
625 size: atomic_fragment
626 .content_rect()
627 .size
628 .to_logical(ifc_writing_mode),
629 }
630 };
631
632 if let Some(mut positioning_context) = atomic.positioning_context {
633 let physical_rect_as_if_in_root = content_rect.as_physical(Some(containing_block));
634 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
635 &physical_rect_as_if_in_root.origin.to_vector(),
636 PositioningContextLength::zero(),
637 );
638
639 self.current_positioning_context_mut()
640 .append(positioning_context);
641 }
642
643 self.current_state.inline_advance += atomic.size.inline;
644
645 self.current_state
646 .fragments
647 .push((Fragment::Box(atomic.fragment), content_rect));
648 }
649
650 fn layout_absolute(&mut self, absolute: AbsolutelyPositionedLineItem) {
651 let absolutely_positioned_box = (*absolute.absolutely_positioned_box).borrow();
652 let style = absolutely_positioned_box.context.style();
653
654 let block_position = self.layout.placement_state.current_margin.solve() -
667 self.current_state.parent_offset.block;
668 let initial_start_corner =
669 if style.get_box().original_display.outside() == DisplayOutside::Inline {
670 LogicalVec2 {
672 inline: self.current_state.inline_advance,
673 block: block_position,
674 }
675 } else {
676 LogicalVec2 {
680 inline: -self.current_state.parent_offset.inline,
681 block: if absolute.preceding_line_content_would_produce_phantom_line {
682 block_position
683 } else {
684 block_position + self.line_metrics.block_size
685 },
686 }
687 };
688
689 let containing_block = self.containing_block();
692 let static_position_rect = LogicalRect {
693 start_corner: initial_start_corner,
694 size: LogicalVec2::zero(),
695 }
696 .as_physical(Some(containing_block));
697
698 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
699 absolute.absolutely_positioned_box.clone(),
700 static_position_rect,
701 LogicalVec2 {
702 inline: AlignFlags::START,
703 block: AlignFlags::START,
704 },
705 containing_block.style.writing_mode,
706 );
707
708 let hoisted_fragment = hoisted_box.fragment.clone();
709 self.current_positioning_context_mut().push(hoisted_box);
710 self.current_state.fragments.push((
711 Fragment::AbsoluteOrFixedPositioned(hoisted_fragment),
712 LogicalRect::zero(),
713 ));
714 }
715
716 fn layout_float(&mut self, float: FloatLineItem) {
717 self.current_state
718 .flags
719 .insert(LineLayoutInlineContainerFlags::HAD_ANY_FLOATS);
720
721 let distance_from_parent_to_ifc = LogicalVec2 {
727 inline: self.current_state.parent_offset.inline,
728 block: self.line_metrics.block_offset + self.current_state.parent_offset.block,
729 };
730 float.fragment.borrow_mut().base.rect.origin -= distance_from_parent_to_ifc
731 .to_physical_size(self.containing_block().style.writing_mode);
732
733 self.current_state
734 .fragments
735 .push((Fragment::Float(float.fragment), LogicalRect::zero()));
736 }
737
738 fn layout_block(&mut self, block: ArcRefCell<BoxFragment>) {
739 let containing_block = self.containing_block();
740 let mut content_rect = block.borrow().content_rect().to_logical(containing_block);
741 content_rect.start_corner.inline -= self.current_state.parent_offset.inline;
743 content_rect.start_corner.block -= self.line_metrics.block_offset;
744 let fragment_and_rect = (Fragment::Box(block), content_rect);
745 self.current_state.fragments.push(fragment_and_rect);
746 }
747
748 #[inline]
749 fn containing_block(&self) -> &ContainingBlock<'_> {
750 self.layout.containing_block()
751 }
752}
753
754pub(super) enum LineItem {
755 InlineStartBoxPaddingBorderMargin(InlineBoxIdentifier),
756 InlineEndBoxPaddingBorderMargin(InlineBoxIdentifier),
757 TextRun(Option<InlineBoxIdentifier>, TextRunLineItem),
758 Atomic(Option<InlineBoxIdentifier>, AtomicLineItem),
759 AbsolutelyPositioned(Option<InlineBoxIdentifier>, AbsolutelyPositionedLineItem),
760 Float(Option<InlineBoxIdentifier>, FloatLineItem),
761 AnonymousBlockBox(Option<InlineBoxIdentifier>, ArcRefCell<BoxFragment>),
762}
763
764impl LineItem {
765 fn inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
766 match self {
767 LineItem::InlineStartBoxPaddingBorderMargin(identifier) => Some(*identifier),
768 LineItem::InlineEndBoxPaddingBorderMargin(identifier) => Some(*identifier),
769 LineItem::TextRun(identifier, _) => *identifier,
770 LineItem::Atomic(identifier, _) => *identifier,
771 LineItem::AbsolutelyPositioned(identifier, _) => *identifier,
772 LineItem::Float(identifier, _) => *identifier,
773 LineItem::AnonymousBlockBox(identifier, _) => *identifier,
774 }
775 }
776
777 pub(super) fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
778 match self {
779 LineItem::InlineStartBoxPaddingBorderMargin(_) => true,
780 LineItem::InlineEndBoxPaddingBorderMargin(_) => true,
781 LineItem::TextRun(_, item) => item.trim_whitespace_at_end(whitespace_trimmed),
782 LineItem::Atomic(..) => false,
783 LineItem::AbsolutelyPositioned(..) => true,
784 LineItem::Float(..) => true,
785 LineItem::AnonymousBlockBox(..) => true,
786 }
787 }
788
789 pub(super) fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
790 match self {
791 LineItem::InlineStartBoxPaddingBorderMargin(_) => true,
792 LineItem::InlineEndBoxPaddingBorderMargin(_) => true,
793 LineItem::TextRun(_, item) => item.trim_whitespace_at_start(whitespace_trimmed),
794 LineItem::Atomic(..) => false,
795 LineItem::AbsolutelyPositioned(..) => true,
796 LineItem::Float(..) => true,
797 LineItem::AnonymousBlockBox(..) => true,
798 }
799 }
800}
801
802#[derive(Debug, MallocSizeOf)]
803pub(crate) struct TextRunOffsets {
804 #[ignore_malloc_size_of = "This is stored primarily in the DOM"]
806 pub shared_selection: SharedSelection,
807 pub character_range: Range<usize>,
810}
811
812pub(super) struct TextRunLineItem {
813 pub base_fragment_info: BaseFragmentInfo,
814 pub inline_styles: SharedInlineStyles,
815 pub text: Vec<std::sync::Arc<GlyphStore>>,
816 pub font_metrics: Arc<FontMetrics>,
817 pub font_key: FontInstanceKey,
818 pub bidi_level: Level,
820 pub offsets: Option<Box<TextRunOffsets>>,
823 pub is_empty_for_text_cursor: bool,
826}
827
828impl TextRunLineItem {
829 fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
830 if matches!(
831 self.inline_styles
832 .style
833 .borrow()
834 .get_inherited_text()
835 .white_space_collapse,
836 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
837 ) {
838 return false;
839 }
840
841 let index_of_last_non_whitespace = self
842 .text
843 .iter()
844 .rev()
845 .position(|glyph| !glyph.is_whitespace())
846 .map(|offset_from_end| self.text.len() - offset_from_end);
847
848 let first_whitespace_index = index_of_last_non_whitespace.unwrap_or(0);
849 *whitespace_trimmed += self
850 .text
851 .drain(first_whitespace_index..)
852 .map(|glyph| glyph.total_advance())
853 .sum();
854
855 index_of_last_non_whitespace.is_none()
857 }
858
859 fn trim_whitespace_at_start(&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_first_non_whitespace = self
872 .text
873 .iter()
874 .position(|glyph| !glyph.is_whitespace())
875 .unwrap_or(self.text.len());
876
877 *whitespace_trimmed += self
878 .text
879 .drain(0..index_of_first_non_whitespace)
880 .map(|glyph| glyph.total_advance())
881 .sum();
882
883 self.text.is_empty()
885 }
886
887 pub(crate) fn merge_if_possible(
888 &mut self,
889 new_font_key: FontInstanceKey,
890 new_bidi_level: Level,
891 new_glyph_store: &Arc<GlyphStore>,
892 new_offsets: &Option<TextRunOffsets>,
893 ) -> bool {
894 if self.font_key != new_font_key || self.bidi_level != new_bidi_level {
895 return false;
896 }
897 self.text.push(new_glyph_store.clone());
898
899 assert_eq!(self.offsets.is_some(), new_offsets.is_some());
900 if let (Some(new_offsets), Some(existing_offsets)) = (new_offsets, self.offsets.as_mut()) {
901 existing_offsets.character_range.end = new_offsets.character_range.end;
902 }
903
904 true
905 }
906}
907
908pub(super) struct AtomicLineItem {
909 pub fragment: ArcRefCell<BoxFragment>,
910 pub size: LogicalVec2<Au>,
911 pub positioning_context: Option<PositioningContext>,
912
913 pub baseline_offset_in_parent: Au,
917
918 pub baseline_offset_in_item: Au,
920
921 pub bidi_level: Level,
923}
924
925impl AtomicLineItem {
926 fn calculate_block_start(&self, line_metrics: &LineMetrics) -> Au {
929 match self.fragment.borrow().style().clone_baseline_shift() {
930 BaselineShift::Keyword(BaselineShiftKeyword::Top) => Au::zero(),
931 BaselineShift::Keyword(BaselineShiftKeyword::Center) => {
932 (line_metrics.block_size - self.size.block).scale_by(0.5)
933 },
934 BaselineShift::Keyword(BaselineShiftKeyword::Bottom) => {
935 line_metrics.block_size - self.size.block
936 },
937
938 _ => {
940 let baseline = line_metrics.baseline_block_offset + self.baseline_offset_in_parent;
941 baseline - self.baseline_offset_in_item
942 },
943 }
944 }
945}
946
947pub(super) struct AbsolutelyPositionedLineItem {
948 pub absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
949 pub preceding_line_content_would_produce_phantom_line: bool,
953}
954
955pub(super) struct FloatLineItem {
956 pub fragment: ArcRefCell<BoxFragment>,
957 pub needs_placement: bool,
961}
962
963fn sort_by_indices_in_place<T>(data: &mut [T], mut indices: Vec<usize>) {
966 for idx in 0..data.len() {
967 if indices[idx] == idx {
968 continue;
969 }
970
971 let mut current_idx = idx;
972 loop {
973 let target_idx = indices[current_idx];
974 indices[current_idx] = current_idx;
975 if indices[target_idx] == target_idx {
976 break;
977 }
978 data.swap(current_idx, target_idx);
979 current_idx = target_idx;
980 }
981 }
982}