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::generics::box_::{GenericVerticalAlign, VerticalAlignKeyword};
18use style::values::specified::align::AlignFlags;
19use style::values::specified::box_::DisplayOutside;
20use unicode_bidi::{BidiInfo, Level};
21use webrender_api::FontInstanceKey;
22
23use super::inline_box::{InlineBoxContainerState, InlineBoxIdentifier, InlineBoxTreePathToken};
24use super::{InlineFormattingContextLayout, LineBlockSizes, SharedInlineStyles, line_height};
25use crate::cell::ArcRefCell;
26use crate::fragment_tree::{BaseFragment, BaseFragmentInfo, BoxFragment, Fragment, TextFragment};
27use crate::geom::{
28 LogicalRect, LogicalVec2, PhysicalRect, ToLogical, ToLogicalWithContainingBlock,
29};
30use crate::positioned::{
31 AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, relative_adjustement,
32};
33use crate::{ContainingBlock, ContainingBlockSize};
34
35pub(super) struct LineMetrics {
36 pub block_offset: Au,
39
40 pub block_size: Au,
42
43 pub baseline_block_offset: Au,
45}
46
47bitflags! {
48 struct LineLayoutInlineContainerFlags: u8 {
49 const HAD_ANY_LINE_ITEMS = 1 << 0;
52 const HAD_INLINE_START_PBM = 1 << 2;
55 const HAD_INLINE_END_PBM = 1 << 3;
58 const HAD_ANY_FLOATS = 1 << 4;
60 }
61}
62
63pub(super) struct LineItemLayoutInlineContainerState {
67 pub identifier: Option<InlineBoxIdentifier>,
70
71 pub fragments: Vec<(Fragment, LogicalRect<Au>)>,
76
77 pub inline_advance: Au,
79
80 flags: LineLayoutInlineContainerFlags,
82
83 pub parent_offset: LogicalVec2<Au>,
87
88 pub baseline_offset: Au,
92
93 pub positioning_context_or_start_offset_in_parent:
98 Either<PositioningContext, PositioningContextLength>,
99}
100
101impl LineItemLayoutInlineContainerState {
102 fn new(
103 identifier: Option<InlineBoxIdentifier>,
104 parent_offset: LogicalVec2<Au>,
105 baseline_offset: Au,
106 positioning_context_or_start_offset_in_parent: Either<
107 PositioningContext,
108 PositioningContextLength,
109 >,
110 ) -> Self {
111 Self {
112 identifier,
113 fragments: Vec::new(),
114 inline_advance: Au::zero(),
115 flags: LineLayoutInlineContainerFlags::empty(),
116 parent_offset,
117 baseline_offset,
118 positioning_context_or_start_offset_in_parent,
119 }
120 }
121
122 fn root(starting_inline_advance: Au, baseline_offset: Au) -> Self {
123 let mut state = Self::new(
124 None,
125 LogicalVec2::zero(),
126 baseline_offset,
127 Either::Right(PositioningContextLength::zero()),
128 );
129 state.inline_advance = starting_inline_advance;
130 state
131 }
132}
133
134pub(super) struct LineItemLayout<'layout_data, 'layout> {
138 layout: &'layout mut InlineFormattingContextLayout<'layout_data>,
140
141 pub state_stack: Vec<LineItemLayoutInlineContainerState>,
144
145 pub current_state: LineItemLayoutInlineContainerState,
147
148 pub line_metrics: LineMetrics,
151
152 pub justification_adjustment: Au,
155
156 is_phantom_line: bool,
159}
160
161impl LineItemLayout<'_, '_> {
162 pub(super) fn layout_line_items(
163 layout: &mut InlineFormattingContextLayout,
164 line_items: Vec<LineItem>,
165 start_position: LogicalVec2<Au>,
166 effective_block_advance: &LineBlockSizes,
167 justification_adjustment: Au,
168 is_phantom_line: bool,
169 ) -> Vec<Fragment> {
170 let baseline_offset = effective_block_advance.find_baseline_offset();
171 LineItemLayout {
172 layout,
173 state_stack: Vec::new(),
174 current_state: LineItemLayoutInlineContainerState::root(
175 start_position.inline,
176 baseline_offset,
177 ),
178 line_metrics: LineMetrics {
179 block_offset: start_position.block,
180 block_size: effective_block_advance.resolve(),
181 baseline_block_offset: baseline_offset,
182 },
183 justification_adjustment,
184 is_phantom_line,
185 }
186 .layout(line_items)
187 }
188
189 fn prepare_layout_for_inline_box(&mut self, new_inline_box: Option<InlineBoxIdentifier>) {
191 let Some(new_inline_box) = new_inline_box else {
193 while !self.state_stack.is_empty() {
194 self.end_inline_box();
195 }
196 return;
197 };
198
199 let path = self
202 .layout
203 .ifc
204 .inline_boxes
205 .get_path(self.current_state.identifier, new_inline_box);
206 for token in path {
207 match token {
208 InlineBoxTreePathToken::Start(ref identifier) => self.start_inline_box(identifier),
209 InlineBoxTreePathToken::End(_) => self.end_inline_box(),
210 }
211 }
212 }
213
214 pub(super) fn layout(&mut self, mut line_items: Vec<LineItem>) -> Vec<Fragment> {
215 let mut last_level = Level::ltr();
216 let levels: Vec<_> = line_items
217 .iter()
218 .map(|item| {
219 let level = match item {
220 LineItem::TextRun(_, text_run) => text_run.bidi_level,
221 LineItem::InlineStartBoxPaddingBorderMargin(_) => last_level,
225 LineItem::InlineEndBoxPaddingBorderMargin(_) => last_level,
226 LineItem::Atomic(_, atomic) => atomic.bidi_level,
227 LineItem::AbsolutelyPositioned(..) => last_level,
228 LineItem::Float(..) => {
229 last_level
232 },
233 LineItem::AnonymousBlockBox(..) => last_level,
234 };
235 last_level = level;
236 level
237 })
238 .collect();
239
240 if self.layout.ifc.has_right_to_left_content {
241 sort_by_indices_in_place(&mut line_items, BidiInfo::reorder_visual(&levels));
242 }
243
244 let containing_block = self.containing_block();
251 let line_item_iterator = if containing_block.style.writing_mode.is_bidi_ltr() {
252 Either::Left(line_items.into_iter())
253 } else {
254 Either::Right(line_items.into_iter().rev())
255 };
256
257 for item in line_item_iterator.into_iter().by_ref() {
258 self.prepare_layout_for_inline_box(item.inline_box_identifier());
263
264 self.current_state
265 .flags
266 .insert(LineLayoutInlineContainerFlags::HAD_ANY_LINE_ITEMS);
267 match item {
268 LineItem::InlineStartBoxPaddingBorderMargin(_) => {
269 self.current_state
270 .flags
271 .insert(LineLayoutInlineContainerFlags::HAD_INLINE_START_PBM);
272 },
273 LineItem::InlineEndBoxPaddingBorderMargin(_) => {
274 self.current_state
275 .flags
276 .insert(LineLayoutInlineContainerFlags::HAD_INLINE_END_PBM);
277 },
278 LineItem::TextRun(_, text_run) => self.layout_text_run(text_run),
279 LineItem::Atomic(_, atomic) => self.layout_atomic(atomic),
280 LineItem::AbsolutelyPositioned(_, absolute) => self.layout_absolute(absolute),
281 LineItem::Float(_, float) => self.layout_float(float),
282 LineItem::AnonymousBlockBox(_, block_box) => self.layout_block(block_box),
283 }
284 }
285
286 self.prepare_layout_for_inline_box(None);
288
289 let fragments_and_rectangles = std::mem::take(&mut self.current_state.fragments);
290 let containing_block = self.containing_block();
291 fragments_and_rectangles
292 .into_iter()
293 .map(|(fragment, logical_rect)| {
294 if matches!(fragment, Fragment::Float(_)) {
295 return fragment;
296 }
297
298 if let Some(mut base) = fragment.base_mut() {
302 base.rect = logical_rect.as_physical(Some(containing_block));
303 }
304
305 fragment
306 })
307 .collect()
308 }
309
310 fn current_positioning_context_mut(&mut self) -> &mut PositioningContext {
311 if let Either::Left(ref mut positioning_context) = self
312 .current_state
313 .positioning_context_or_start_offset_in_parent
314 {
315 return positioning_context;
316 }
317 self.state_stack
318 .iter_mut()
319 .rev()
320 .find_map(
321 |state| match state.positioning_context_or_start_offset_in_parent {
322 Either::Left(ref mut positioning_context) => Some(positioning_context),
323 Either::Right(_) => None,
324 },
325 )
326 .unwrap_or(self.layout.positioning_context)
327 }
328
329 fn start_inline_box(&mut self, identifier: &InlineBoxIdentifier) {
330 let inline_box_state =
331 &*self.layout.inline_box_states[identifier.index_in_inline_boxes as usize];
332 let inline_box = self.layout.ifc.inline_boxes.get(identifier);
333 let inline_box = &*(inline_box.borrow());
334
335 let space_above_baseline = inline_box_state.calculate_space_above_baseline();
336 let block_start_offset =
337 self.calculate_inline_box_block_start(inline_box_state, space_above_baseline);
338
339 let positioning_context_or_start_offset_in_parent =
340 match PositioningContext::new_for_layout_box_base(&inline_box.base) {
341 Some(positioning_context) => Either::Left(positioning_context),
342 None => Either::Right(self.current_positioning_context_mut().len()),
343 };
344
345 let parent_offset = LogicalVec2 {
346 inline: self.current_state.inline_advance + self.current_state.parent_offset.inline,
347 block: block_start_offset,
348 };
349
350 let outer_state = std::mem::replace(
351 &mut self.current_state,
352 LineItemLayoutInlineContainerState::new(
353 Some(*identifier),
354 parent_offset,
355 block_start_offset + space_above_baseline,
356 positioning_context_or_start_offset_in_parent,
357 ),
358 );
359
360 self.state_stack.push(outer_state);
361 }
362
363 fn end_inline_box(&mut self) {
364 let outer_state = self.state_stack.pop().expect("Ended unknown inline box");
365 let inner_state = std::mem::replace(&mut self.current_state, outer_state);
366
367 let identifier = inner_state.identifier.expect("Ended unknown inline box");
368 let inline_box_state =
369 &*self.layout.inline_box_states[identifier.index_in_inline_boxes as usize];
370 let inline_box = self.layout.ifc.inline_boxes.get(&identifier);
371 let inline_box = &*(inline_box.borrow());
372
373 let mut had_start = inner_state
374 .flags
375 .contains(LineLayoutInlineContainerFlags::HAD_INLINE_START_PBM);
376 let mut had_end = inner_state
377 .flags
378 .contains(LineLayoutInlineContainerFlags::HAD_INLINE_END_PBM);
379
380 let containing_block = self.containing_block();
381 let containing_block_writing_mode = containing_block.style.writing_mode;
382 if containing_block_writing_mode.is_bidi_ltr() !=
383 inline_box.base.style.writing_mode.is_bidi_ltr()
384 {
385 std::mem::swap(&mut had_start, &mut had_end)
386 }
387
388 let mut padding = inline_box_state.pbm.padding;
389 let mut border = inline_box_state.pbm.border;
390 let mut margin = inline_box_state.pbm.margin.auto_is(Au::zero);
391 if !had_start {
392 padding.inline_start = Au::zero();
393 border.inline_start = Au::zero();
394 margin.inline_start = Au::zero();
395 }
396 if !had_end {
397 padding.inline_end = Au::zero();
398 border.inline_end = Au::zero();
399 margin.inline_end = Au::zero();
400 }
401 let pbm_sums = padding + border + margin;
402
403 let mut content_rect = LogicalRect {
405 start_corner: LogicalVec2 {
406 inline: self.current_state.inline_advance + pbm_sums.inline_start,
407 block: inner_state.parent_offset.block - self.current_state.parent_offset.block,
408 },
409 size: LogicalVec2 {
410 inline: inner_state.inline_advance,
411 block: if self.is_phantom_line {
412 Au::zero()
413 } else {
414 inline_box_state.base.font_metrics.line_gap
415 },
416 },
417 };
418
419 let style = &inline_box.base.style;
422 if style.get_box().position == Position::Relative {
423 content_rect.start_corner += relative_adjustement(style, containing_block);
424 }
425
426 let inline_box_containing_block = ContainingBlock {
427 size: ContainingBlockSize {
428 inline: content_rect.size.inline,
429 block: Default::default(),
430 },
431 style: containing_block.style,
432 };
433 let fragments = inner_state
434 .fragments
435 .into_iter()
436 .map(|(fragment, logical_rect)| {
437 let is_float = matches!(fragment, Fragment::Float(_));
438 if let Some(mut base) = fragment.base_mut() {
439 if is_float {
440 base.rect.origin -= pbm_sums
441 .start_offset()
442 .to_physical_size(containing_block_writing_mode);
443 } else {
444 base.rect = logical_rect.as_physical(Some(&inline_box_containing_block))
448 }
449 }
450 fragment
451 })
452 .collect();
453
454 let physical_content_rect = content_rect.as_physical(Some(containing_block));
457 let mut fragment = BoxFragment::new(
458 inline_box.base.base_fragment_info,
459 style.clone(),
460 fragments,
461 physical_content_rect,
462 padding.to_physical(containing_block_writing_mode),
463 border.to_physical(containing_block_writing_mode),
464 margin.to_physical(containing_block_writing_mode),
465 None, );
467
468 let offset_from_parent_ifc = LogicalVec2 {
469 inline: pbm_sums.inline_start + self.current_state.inline_advance,
470 block: content_rect.start_corner.block,
471 }
472 .to_physical_vector(containing_block_writing_mode);
473
474 match inner_state.positioning_context_or_start_offset_in_parent {
475 Either::Left(mut positioning_context) => {
476 positioning_context
477 .layout_collected_children(self.layout.layout_context, &mut fragment);
478 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
479 &offset_from_parent_ifc,
480 PositioningContextLength::zero(),
481 );
482 self.current_positioning_context_mut()
483 .append(positioning_context);
484 },
485 Either::Right(start_offset) => {
486 self.current_positioning_context_mut()
487 .adjust_static_position_of_hoisted_fragments_with_offset(
488 &offset_from_parent_ifc,
489 start_offset,
490 );
491 },
492 }
493
494 self.current_state.inline_advance += inner_state.inline_advance + pbm_sums.inline_sum();
495
496 let fragment = Fragment::Box(ArcRefCell::new(fragment));
497 inline_box.base.add_fragment(fragment.clone());
498
499 self.current_state.fragments.push((fragment, content_rect));
500 }
501
502 fn calculate_inline_box_block_start(
503 &self,
504 inline_box_state: &InlineBoxContainerState,
505 space_above_baseline: Au,
506 ) -> Au {
507 if self.is_phantom_line {
508 return Au::zero();
509 };
510 let font_metrics = &inline_box_state.base.font_metrics;
511 let style = &inline_box_state.base.style;
512 let line_gap = font_metrics.line_gap;
513
514 match inline_box_state.base.style.clone_vertical_align() {
517 GenericVerticalAlign::Keyword(VerticalAlignKeyword::Top) => {
518 let line_height = line_height(style, font_metrics, &inline_box_state.base.flags);
519 (line_height - line_gap).scale_by(0.5)
520 },
521 GenericVerticalAlign::Keyword(VerticalAlignKeyword::Bottom) => {
522 let line_height = line_height(style, font_metrics, &inline_box_state.base.flags);
523 let half_leading = (line_height - line_gap).scale_by(0.5);
524 self.line_metrics.block_size - line_height + half_leading
525 },
526 _ => {
527 self.line_metrics.baseline_block_offset + inline_box_state.base.baseline_offset -
528 space_above_baseline
529 },
530 }
531 }
532
533 fn layout_text_run(&mut self, text_item: TextRunLineItem) {
534 if text_item.text.is_empty() {
535 return;
536 }
537
538 let mut number_of_justification_opportunities = 0;
539 let mut inline_advance = text_item
540 .text
541 .iter()
542 .map(|glyph_store| {
543 number_of_justification_opportunities += glyph_store.total_word_separators();
544 glyph_store.total_advance()
545 })
546 .sum();
547
548 if !self.justification_adjustment.is_zero() {
549 inline_advance += self
550 .justification_adjustment
551 .scale_by(number_of_justification_opportunities as f32);
552 }
553
554 let start_corner = LogicalVec2 {
558 inline: self.current_state.inline_advance,
559 block: self.current_state.baseline_offset -
560 text_item.font_metrics.ascent -
561 self.current_state.parent_offset.block,
562 };
563 let content_rect = LogicalRect {
564 start_corner,
565 size: LogicalVec2 {
566 block: text_item.font_metrics.line_gap,
567 inline: inline_advance,
568 },
569 };
570
571 self.current_state.inline_advance += inline_advance;
572 self.current_state.fragments.push((
573 Fragment::Text(ArcRefCell::new(TextFragment {
574 base: BaseFragment::new(
575 text_item.base_fragment_info,
576 text_item.inline_styles.style.clone().into(),
577 PhysicalRect::zero(),
578 ),
579 selected_style: text_item.inline_styles.selected.clone(),
580 font_metrics: text_item.font_metrics,
581 font_key: text_item.font_key,
582 glyphs: text_item.text,
583 justification_adjustment: self.justification_adjustment,
584 offsets: text_item.offsets,
585 })),
586 content_rect,
587 ));
588 }
589
590 fn layout_atomic(&mut self, atomic: AtomicLineItem) {
591 let containing_block = self.containing_block();
596 let ifc_writing_mode = containing_block.style.writing_mode;
597 let content_rect = {
598 let block_start = atomic.calculate_block_start(&self.line_metrics);
599 let atomic_fragment = atomic.fragment.borrow_mut();
600 let padding_border_margin_sides = atomic_fragment
601 .padding_border_margin()
602 .to_logical(ifc_writing_mode);
603
604 let mut atomic_offset = LogicalVec2 {
605 inline: self.current_state.inline_advance +
606 padding_border_margin_sides.inline_start,
607 block: block_start - self.current_state.parent_offset.block +
608 padding_border_margin_sides.block_start,
609 };
610
611 let style = atomic_fragment.style();
612 if style.get_box().position == Position::Relative {
613 atomic_offset += relative_adjustement(&style, containing_block);
614 }
615
616 LogicalRect {
619 start_corner: atomic_offset,
620 size: atomic_fragment
621 .content_rect()
622 .size
623 .to_logical(ifc_writing_mode),
624 }
625 };
626
627 if let Some(mut positioning_context) = atomic.positioning_context {
628 let physical_rect_as_if_in_root = content_rect.as_physical(Some(containing_block));
629 positioning_context.adjust_static_position_of_hoisted_fragments_with_offset(
630 &physical_rect_as_if_in_root.origin.to_vector(),
631 PositioningContextLength::zero(),
632 );
633
634 self.current_positioning_context_mut()
635 .append(positioning_context);
636 }
637
638 self.current_state.inline_advance += atomic.size.inline;
639
640 self.current_state
641 .fragments
642 .push((Fragment::Box(atomic.fragment), content_rect));
643 }
644
645 fn layout_absolute(&mut self, absolute: AbsolutelyPositionedLineItem) {
646 let absolutely_positioned_box = (*absolute.absolutely_positioned_box).borrow();
647 let style = absolutely_positioned_box.context.style();
648
649 let block_position = self.layout.placement_state.current_margin.solve() -
662 self.current_state.parent_offset.block;
663 let initial_start_corner =
664 if style.get_box().original_display.outside() == DisplayOutside::Inline {
665 LogicalVec2 {
667 inline: self.current_state.inline_advance,
668 block: block_position,
669 }
670 } else {
671 LogicalVec2 {
673 inline: -self.current_state.parent_offset.inline,
674 block: block_position + self.line_metrics.block_size,
675 }
676 };
677
678 let containing_block = self.containing_block();
681 let static_position_rect = LogicalRect {
682 start_corner: initial_start_corner,
683 size: LogicalVec2::zero(),
684 }
685 .as_physical(Some(containing_block));
686
687 let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
688 absolute.absolutely_positioned_box.clone(),
689 static_position_rect,
690 LogicalVec2 {
691 inline: AlignFlags::START,
692 block: AlignFlags::START,
693 },
694 containing_block.style.writing_mode,
695 );
696
697 let hoisted_fragment = hoisted_box.fragment.clone();
698 self.current_positioning_context_mut().push(hoisted_box);
699 self.current_state.fragments.push((
700 Fragment::AbsoluteOrFixedPositioned(hoisted_fragment),
701 LogicalRect::zero(),
702 ));
703 }
704
705 fn layout_float(&mut self, float: FloatLineItem) {
706 self.current_state
707 .flags
708 .insert(LineLayoutInlineContainerFlags::HAD_ANY_FLOATS);
709
710 let distance_from_parent_to_ifc = LogicalVec2 {
716 inline: self.current_state.parent_offset.inline,
717 block: self.line_metrics.block_offset + self.current_state.parent_offset.block,
718 };
719 float.fragment.borrow_mut().base.rect.origin -= distance_from_parent_to_ifc
720 .to_physical_size(self.containing_block().style.writing_mode);
721
722 self.current_state
723 .fragments
724 .push((Fragment::Float(float.fragment), LogicalRect::zero()));
725 }
726
727 fn layout_block(&mut self, block: ArcRefCell<BoxFragment>) {
728 let containing_block = self.containing_block();
729 let mut content_rect = block.borrow().content_rect().to_logical(containing_block);
730 content_rect.start_corner.inline -= self.current_state.parent_offset.inline;
732 content_rect.start_corner.block -= self.line_metrics.block_offset;
733 let fragment_and_rect = (Fragment::Box(block), content_rect);
734 self.current_state.fragments.push(fragment_and_rect);
735 }
736
737 #[inline]
738 fn containing_block(&self) -> &ContainingBlock<'_> {
739 self.layout.containing_block()
740 }
741}
742
743pub(super) enum LineItem {
744 InlineStartBoxPaddingBorderMargin(InlineBoxIdentifier),
745 InlineEndBoxPaddingBorderMargin(InlineBoxIdentifier),
746 TextRun(Option<InlineBoxIdentifier>, TextRunLineItem),
747 Atomic(Option<InlineBoxIdentifier>, AtomicLineItem),
748 AbsolutelyPositioned(Option<InlineBoxIdentifier>, AbsolutelyPositionedLineItem),
749 Float(Option<InlineBoxIdentifier>, FloatLineItem),
750 AnonymousBlockBox(Option<InlineBoxIdentifier>, ArcRefCell<BoxFragment>),
751}
752
753impl LineItem {
754 fn inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
755 match self {
756 LineItem::InlineStartBoxPaddingBorderMargin(identifier) => Some(*identifier),
757 LineItem::InlineEndBoxPaddingBorderMargin(identifier) => Some(*identifier),
758 LineItem::TextRun(identifier, _) => *identifier,
759 LineItem::Atomic(identifier, _) => *identifier,
760 LineItem::AbsolutelyPositioned(identifier, _) => *identifier,
761 LineItem::Float(identifier, _) => *identifier,
762 LineItem::AnonymousBlockBox(identifier, _) => *identifier,
763 }
764 }
765
766 pub(super) fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
767 match self {
768 LineItem::InlineStartBoxPaddingBorderMargin(_) => true,
769 LineItem::InlineEndBoxPaddingBorderMargin(_) => true,
770 LineItem::TextRun(_, item) => item.trim_whitespace_at_end(whitespace_trimmed),
771 LineItem::Atomic(..) => false,
772 LineItem::AbsolutelyPositioned(..) => true,
773 LineItem::Float(..) => true,
774 LineItem::AnonymousBlockBox(..) => true,
775 }
776 }
777
778 pub(super) fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
779 match self {
780 LineItem::InlineStartBoxPaddingBorderMargin(_) => true,
781 LineItem::InlineEndBoxPaddingBorderMargin(_) => true,
782 LineItem::TextRun(_, item) => item.trim_whitespace_at_start(whitespace_trimmed),
783 LineItem::Atomic(..) => false,
784 LineItem::AbsolutelyPositioned(..) => true,
785 LineItem::Float(..) => true,
786 LineItem::AnonymousBlockBox(..) => true,
787 }
788 }
789}
790
791#[derive(MallocSizeOf)]
792pub(crate) struct TextRunOffsets {
793 #[ignore_malloc_size_of = "This is stored primarily in the DOM"]
795 pub shared_selection: SharedSelection,
796 pub character_range: Range<usize>,
799}
800
801pub(super) struct TextRunLineItem {
802 pub base_fragment_info: BaseFragmentInfo,
803 pub inline_styles: SharedInlineStyles,
804 pub text: Vec<std::sync::Arc<GlyphStore>>,
805 pub font_metrics: Arc<FontMetrics>,
806 pub font_key: FontInstanceKey,
807 pub bidi_level: Level,
809 pub offsets: Option<Box<TextRunOffsets>>,
812}
813
814impl TextRunLineItem {
815 fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
816 if matches!(
817 self.inline_styles
818 .style
819 .borrow()
820 .get_inherited_text()
821 .white_space_collapse,
822 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
823 ) {
824 return false;
825 }
826
827 let index_of_last_non_whitespace = self
828 .text
829 .iter()
830 .rev()
831 .position(|glyph| !glyph.is_whitespace())
832 .map(|offset_from_end| self.text.len() - offset_from_end);
833
834 let first_whitespace_index = index_of_last_non_whitespace.unwrap_or(0);
835 *whitespace_trimmed += self
836 .text
837 .drain(first_whitespace_index..)
838 .map(|glyph| glyph.total_advance())
839 .sum();
840
841 index_of_last_non_whitespace.is_none()
843 }
844
845 fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
846 if matches!(
847 self.inline_styles
848 .style
849 .borrow()
850 .get_inherited_text()
851 .white_space_collapse,
852 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
853 ) {
854 return false;
855 }
856
857 let index_of_first_non_whitespace = self
858 .text
859 .iter()
860 .position(|glyph| !glyph.is_whitespace())
861 .unwrap_or(self.text.len());
862
863 *whitespace_trimmed += self
864 .text
865 .drain(0..index_of_first_non_whitespace)
866 .map(|glyph| glyph.total_advance())
867 .sum();
868
869 self.text.is_empty()
871 }
872
873 pub(crate) fn merge_if_possible(
874 &mut self,
875 new_font_key: FontInstanceKey,
876 new_bidi_level: Level,
877 new_glyph_store: &Arc<GlyphStore>,
878 new_offsets: &Option<TextRunOffsets>,
879 ) -> bool {
880 if self.font_key != new_font_key || self.bidi_level != new_bidi_level {
881 return false;
882 }
883 self.text.push(new_glyph_store.clone());
884
885 assert_eq!(self.offsets.is_some(), new_offsets.is_some());
886 if let (Some(new_offsets), Some(existing_offsets)) = (new_offsets, self.offsets.as_mut()) {
887 existing_offsets.character_range.end = new_offsets.character_range.end;
888 }
889
890 true
891 }
892}
893
894pub(super) struct AtomicLineItem {
895 pub fragment: ArcRefCell<BoxFragment>,
896 pub size: LogicalVec2<Au>,
897 pub positioning_context: Option<PositioningContext>,
898
899 pub baseline_offset_in_parent: Au,
903
904 pub baseline_offset_in_item: Au,
906
907 pub bidi_level: Level,
909}
910
911impl AtomicLineItem {
912 fn calculate_block_start(&self, line_metrics: &LineMetrics) -> Au {
915 match self.fragment.borrow().style().clone_vertical_align() {
916 GenericVerticalAlign::Keyword(VerticalAlignKeyword::Top) => Au::zero(),
917 GenericVerticalAlign::Keyword(VerticalAlignKeyword::Bottom) => {
918 line_metrics.block_size - self.size.block
919 },
920
921 _ => {
923 let baseline = line_metrics.baseline_block_offset + self.baseline_offset_in_parent;
924 baseline - self.baseline_offset_in_item
925 },
926 }
927 }
928}
929
930pub(super) struct AbsolutelyPositionedLineItem {
931 pub absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
932}
933
934pub(super) struct FloatLineItem {
935 pub fragment: ArcRefCell<BoxFragment>,
936 pub needs_placement: bool,
940}
941
942fn sort_by_indices_in_place<T>(data: &mut [T], mut indices: Vec<usize>) {
945 for idx in 0..data.len() {
946 if indices[idx] == idx {
947 continue;
948 }
949
950 let mut current_idx = idx;
951 loop {
952 let target_idx = indices[current_idx];
953 indices[current_idx] = current_idx;
954 if indices[target_idx] == target_idx {
955 break;
956 }
957 data.swap(current_idx, target_idx);
958 current_idx = target_idx;
959 }
960 }
961}