1pub mod construct;
72mod full_width;
73pub mod inline_box;
74pub mod line;
75mod line_breaker;
76mod mathml_italics;
77mod shaping_queue;
78mod small_kana;
79pub mod text_run;
80pub mod text_transform;
81
82use std::cell::{Cell, OnceCell};
83use std::mem;
84use std::ops::Range;
85use std::rc::Rc;
86use std::sync::{Arc, OnceLock};
87
88use app_units::{Au, MAX_AU};
89use atomic_refcell::AtomicRef;
90use bitflags::bitflags;
91use construct::InlineFormattingContextBuilder;
92use fonts::{FontMetrics, FontRef, ShapedTextSlice};
93use icu_locale_core::LanguageIdentifier;
94use icu_properties::props::{EnumeratedProperty, LineBreak as ICULineBreak};
95use icu_segmenter::options::{LineBreakOptions, LineBreakStrictness, LineBreakWordOption};
96use inline_box::{InlineBox, InlineBoxContainerState, InlineBoxIdentifier, InlineBoxes};
97use layout_api::LayoutNode;
98use line::{
99 AbsolutelyPositionedLineItem, AtomicLineItem, FloatLineItem, LineItem, LineItemLayout,
100 TextRunLineItem,
101};
102use malloc_size_of_derive::MallocSizeOf;
103use script::layout_dom::ServoLayoutNode;
104use servo_arc::Arc as ServoArc;
105use servo_base::text::{Utf8CodeUnits, Utf32CodeUnits};
106use style::Zero;
107use style::computed_values::line_break::T as LineBreak;
108use style::computed_values::text_wrap_mode::T as TextWrapMode;
109use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
110use style::computed_values::word_break::T as WordBreak;
111use style::context::{QuirksMode, SharedStyleContext};
112use style::properties::ComputedValues;
113use style::values::computed::BaselineShift;
114use style::values::generics::box_::BaselineShiftKeyword;
115use style::values::generics::font::LineHeight;
116use style::values::specified::box_::BaselineSource;
117use style::values::specified::text::TextAlignKeyword;
118use style::values::specified::{AlignmentBaseline, TextAlignLast, TextJustify};
119use text_run::{TextRun, get_font_for_first_font_for_style};
120use unicode_bidi::{BidiInfo, Level};
121
122use super::float::{Clear, PlacementAmongFloats};
123use super::{IndependentFloatOrAtomicLayoutResult, IndependentFormattingContextLayoutResult};
124use crate::cell::{ArcRefCell, WeakRefCell};
125use crate::context::LayoutContext;
126use crate::dom::WeakLayoutBox;
127use crate::dom_traversal::NodeAndStyleInfo;
128use crate::flow::float::{FloatBox, SequentialLayoutState};
129use crate::flow::inline::shaping_queue::ShapingQueue;
130use crate::flow::inline::text_run::{
131 CaretPlaceholder, FontAndScriptInfo, TextRunItem, TextRunSegment,
132};
133use crate::flow::{
134 BlockLevelBox, CollapsibleWithParentStartMargin, FloatSide, PlacementState,
135 compute_inline_content_sizes_for_block_level_boxes, layout_block_level_child,
136 skippable_for_block_percentage_quirk,
137};
138use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
139use crate::fragment_tree::{CollapsedMargin, Fragment, FragmentFlags, PositioningFragment};
140use crate::geom::{LogicalRect, LogicalSides1D, LogicalVec2, ToLogical};
141use crate::layout_box_base::LayoutBoxBase;
142use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
143use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
144use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
145use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SharedStyle};
146
147static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
149static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
150
151#[derive(Debug, MallocSizeOf)]
152pub(crate) struct InlineFormattingContext {
153 inline_items: Vec<InlineItem>,
158
159 inline_boxes: InlineBoxes,
162
163 #[conditional_malloc_size_of]
165 text_content: Arc<OnceLock<String>>,
166
167 shared_inline_styles: SharedInlineStyles,
170
171 default_font: Option<FontRef>,
175
176 has_first_formatted_line: bool,
179
180 pub(super) contains_floats: bool,
182
183 is_single_line_text_input: bool,
186
187 has_right_to_left_content: bool,
190
191 tab_size_multiplier: OnceLock<Au>,
196}
197
198#[derive(Clone, Debug, MallocSizeOf)]
203pub(crate) struct SharedInlineStyles {
204 pub style: SharedStyle,
205 pub selected: SharedStyle,
206}
207
208impl SharedInlineStyles {
209 pub(crate) fn ptr_eq(&self, other: &Self) -> bool {
210 self.style.ptr_eq(&other.style) && self.selected.ptr_eq(&other.selected)
211 }
212
213 pub(crate) fn from_info_and_context(info: &NodeAndStyleInfo, context: &LayoutContext) -> Self {
214 Self {
215 style: SharedStyle::new(info.style.clone()),
216 selected: SharedStyle::new(info.node.selected_style(&context.style_context)),
217 }
218 }
219}
220
221impl BlockLevelBox {
222 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
223 layout.process_soft_wrap_opportunity();
224 layout.commit_current_segment_to_line();
225 layout.process_line_break(
226 true, true, );
229
230 let fragment = layout_block_level_child(
231 layout.layout_context,
232 layout.positioning_context,
233 self,
234 layout.sequential_layout_state.as_deref_mut(),
235 &mut layout.placement_state,
236 layout.ignore_block_margins_for_stretch,
237 true, );
239
240 let Some(fragment) = fragment.retrieve_box_fragment() else {
241 unreachable!("The fragment should be a Fragment::Box()");
242 };
243
244 layout.depends_on_block_constraints |= fragment.base.flags.contains(
247 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
248 );
249
250 layout.push_line_item_to_unbreakable_segment(LineItem::BlockLevel(
251 layout.current_inline_box_identifier(),
252 fragment.clone(),
253 ));
254
255 layout.commit_current_segment_to_line();
256 layout.process_line_break(
257 true, false, );
260 }
261}
262
263#[derive(Clone, Debug, MallocSizeOf)]
264pub(crate) enum InlineItem {
265 StartInlineBox(ArcRefCell<InlineBox>),
266 EndInlineBox(ArcRefCell<InlineBox>),
267 TextRun(ArcRefCell<TextRun>),
268 OutOfFlowAbsolutelyPositionedBox(
269 ArcRefCell<AbsolutelyPositionedBox>,
270 Utf8CodeUnits, ),
272 OutOfFlowFloatBox(ArcRefCell<FloatBox>),
273 Atomic(
274 ArcRefCell<IndependentFormattingContext>,
275 Utf8CodeUnits, Level, ),
278 BlockLevel(ArcRefCell<BlockLevelBox>),
279}
280
281impl InlineItem {
282 pub(crate) fn repair_style(
283 &self,
284 context: &SharedStyleContext,
285 node: &ServoLayoutNode,
286 new_style: &ServoArc<ComputedValues>,
287 ) {
288 match self {
289 InlineItem::StartInlineBox(inline_box) => {
290 inline_box
291 .borrow_mut()
292 .repair_style(context, node, new_style);
293 },
294 InlineItem::EndInlineBox(..) => {},
295 InlineItem::TextRun(..) => {},
298 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box
299 .borrow_mut()
300 .context
301 .repair_style(context, node, new_style),
302 InlineItem::OutOfFlowFloatBox(float_box) => float_box
303 .borrow_mut()
304 .contents
305 .repair_style(context, node, new_style),
306 InlineItem::Atomic(atomic, ..) => {
307 atomic.borrow_mut().repair_style(context, node, new_style)
308 },
309 InlineItem::BlockLevel(block_level) => block_level
310 .borrow_mut()
311 .repair_style(context, node, new_style),
312 }
313 }
314
315 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
316 match self {
317 InlineItem::StartInlineBox(inline_box) => callback(&inline_box.borrow().base),
318 InlineItem::EndInlineBox(..) | InlineItem::TextRun(..) => {
319 unreachable!("Should never have these kind of fragments attached to a DOM node")
320 },
321 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
322 callback(&positioned_box.borrow().context.base)
323 },
324 InlineItem::OutOfFlowFloatBox(float_box) => callback(&float_box.borrow().contents.base),
325 InlineItem::Atomic(independent_formatting_context, ..) => {
326 callback(&independent_formatting_context.borrow().base)
327 },
328 InlineItem::BlockLevel(block_level) => block_level.borrow().with_base(callback),
329 }
330 }
331
332 pub(crate) fn with_base_mut<T>(&self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
333 match self {
334 InlineItem::StartInlineBox(inline_box) => callback(&mut inline_box.borrow_mut().base),
335 InlineItem::EndInlineBox(..) | InlineItem::TextRun(..) => {
336 unreachable!("Should never have these kind of fragments attached to a DOM node")
337 },
338 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
339 callback(&mut positioned_box.borrow_mut().context.base)
340 },
341 InlineItem::OutOfFlowFloatBox(float_box) => {
342 callback(&mut float_box.borrow_mut().contents.base)
343 },
344 InlineItem::Atomic(independent_formatting_context, ..) => {
345 callback(&mut independent_formatting_context.borrow_mut().base)
346 },
347 InlineItem::BlockLevel(block_level) => block_level.borrow_mut().with_base_mut(callback),
348 }
349 }
350
351 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
352 match self {
353 Self::StartInlineBox(_) | InlineItem::EndInlineBox(..) => {
354 },
357 Self::TextRun(_) => {
358 },
360 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
361 positioned_box.borrow().context.attached_to_tree(layout_box)
362 },
363 Self::OutOfFlowFloatBox(float_box) => {
364 float_box.borrow().contents.attached_to_tree(layout_box)
365 },
366 Self::Atomic(atomic, ..) => atomic.borrow().attached_to_tree(layout_box),
367 Self::BlockLevel(block_level) => block_level.borrow().attached_to_tree(layout_box),
368 }
369 }
370
371 pub(crate) fn downgrade(&self) -> WeakInlineItem {
372 match self {
373 Self::StartInlineBox(inline_box) => {
374 WeakInlineItem::StartInlineBox(inline_box.downgrade())
375 },
376 Self::EndInlineBox(inline_box) => WeakInlineItem::EndInlineBox(inline_box.downgrade()),
377 Self::TextRun(text_run) => WeakInlineItem::TextRun(text_run.downgrade()),
378 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
379 WeakInlineItem::OutOfFlowAbsolutelyPositionedBox(
380 positioned_box.downgrade(),
381 *offset_in_text,
382 )
383 },
384 Self::OutOfFlowFloatBox(float_box) => {
385 WeakInlineItem::OutOfFlowFloatBox(float_box.downgrade())
386 },
387 Self::Atomic(atomic, offset_in_text, bidi_level) => {
388 WeakInlineItem::Atomic(atomic.downgrade(), *offset_in_text, *bidi_level)
389 },
390 Self::BlockLevel(block_level) => WeakInlineItem::BlockLevel(block_level.downgrade()),
391 }
392 }
393}
394
395#[derive(Clone, Debug, MallocSizeOf)]
396pub(crate) enum WeakInlineItem {
397 StartInlineBox(WeakRefCell<InlineBox>),
398 EndInlineBox(WeakRefCell<InlineBox>),
399 TextRun(WeakRefCell<TextRun>),
400 OutOfFlowAbsolutelyPositionedBox(
401 WeakRefCell<AbsolutelyPositionedBox>,
402 Utf8CodeUnits, ),
404 OutOfFlowFloatBox(WeakRefCell<FloatBox>),
405 Atomic(
406 WeakRefCell<IndependentFormattingContext>,
407 Utf8CodeUnits, Level, ),
410 BlockLevel(WeakRefCell<BlockLevelBox>),
411}
412
413impl WeakInlineItem {
414 pub(crate) fn upgrade(&self) -> Option<InlineItem> {
415 Some(match self {
416 Self::StartInlineBox(inline_box) => InlineItem::StartInlineBox(inline_box.upgrade()?),
417 Self::EndInlineBox(inline_box) => InlineItem::EndInlineBox(inline_box.upgrade()?),
418 Self::TextRun(text_run) => InlineItem::TextRun(text_run.upgrade()?),
419 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
420 InlineItem::OutOfFlowAbsolutelyPositionedBox(
421 positioned_box.upgrade()?,
422 *offset_in_text,
423 )
424 },
425 Self::OutOfFlowFloatBox(float_box) => {
426 InlineItem::OutOfFlowFloatBox(float_box.upgrade()?)
427 },
428 Self::Atomic(atomic, offset_in_text, bidi_level) => {
429 InlineItem::Atomic(atomic.upgrade()?, *offset_in_text, *bidi_level)
430 },
431 Self::BlockLevel(block_level) => InlineItem::BlockLevel(block_level.upgrade()?),
432 })
433 }
434}
435
436struct LineUnderConstruction {
443 start_position: LogicalVec2<Au>,
446
447 inline_position: Au,
450
451 max_block_size: LineBlockSizes,
455
456 has_content: bool,
459
460 has_floats_waiting_to_be_placed: bool,
464
465 placement_among_floats: OnceCell<LogicalRect<Au>>,
470
471 line_items: Vec<LineItem>,
474
475 for_block_level: bool,
477
478 caret_placeholder: Option<CaretPlaceholder>,
481
482 cloneable_inline_box_pbm_size: LogicalSides1D<Au>,
486
487 trailing_hangable_white_space: Au,
489
490 trailing_removable_white_space: Au,
492}
493
494impl LineUnderConstruction {
495 fn new(start_position: LogicalVec2<Au>) -> Self {
496 Self {
497 inline_position: start_position.inline,
498 start_position,
499 max_block_size: LineBlockSizes::zero(),
500 has_content: false,
501 has_floats_waiting_to_be_placed: false,
502 placement_among_floats: OnceCell::new(),
503 line_items: Vec::new(),
504 for_block_level: false,
505 caret_placeholder: None,
506 cloneable_inline_box_pbm_size: Default::default(),
507 trailing_hangable_white_space: Au::zero(),
508 trailing_removable_white_space: Au::zero(),
509 }
510 }
511
512 fn replace_placement_among_floats(&mut self, new_placement: LogicalRect<Au>) {
513 self.placement_among_floats.take();
514 let _ = self.placement_among_floats.set(new_placement);
515 }
516
517 fn trim_trailing_white_space_and_calculate_hanging(
538 &mut self,
539 available_inline_size: Au,
540 last_line_or_forced_line_break: bool,
541 ) -> Au {
542 let mut conditionally_hanging = Au::zero();
543 let mut unconditionally_hanging = Au::zero();
544 let mut removed = Au::zero();
545 let mut white_space_still_hanging_conditionally = last_line_or_forced_line_break;
546
547 for item in self.line_items.iter_mut().rev() {
548 let text_run = match item {
549 LineItem::InlineStartBoxPaddingBorderMargin(_) |
550 LineItem::InlineEndBoxPaddingBorderMargin(_) |
551 LineItem::Float(..) |
552 LineItem::AbsolutelyPositioned(..) => continue,
553 LineItem::Atomic(..) | LineItem::BlockLevel(..) => break,
554 LineItem::Tab {
555 conditionally_hangs,
556 advance,
557 ..
558 } => {
559 if !*conditionally_hangs {
560 break;
561 }
562
563 if white_space_still_hanging_conditionally {
564 conditionally_hanging += *advance;
565 } else {
566 unconditionally_hanging += *advance;
567 }
568
569 continue;
570 },
571 LineItem::TextRun(_, text_run) => text_run,
572 };
573
574 let (mut count, preceded_by_content) = text_run.trailing_white_space();
575 if conditionally_hanging.is_zero() && unconditionally_hanging.is_zero() {
576 text_run.trim_removable_white_space_at_end();
577 removed += count.removable;
578 } else {
579 count.hangable += count.removable;
580 }
581
582 let style = text_run.text_fragment_run_data.inline_styles.style.borrow();
583 if white_space_still_hanging_conditionally && style.white_space_may_hang_conditionally()
584 {
585 conditionally_hanging += count.hangable;
586 } else {
587 white_space_still_hanging_conditionally = false;
588 unconditionally_hanging += count.hangable;
589 }
590
591 if preceded_by_content {
592 break;
593 }
594 }
595
596 self.trailing_removable_white_space = Au::zero();
597 self.inline_position -= removed;
598
599 if !last_line_or_forced_line_break {
600 return unconditionally_hanging + conditionally_hanging;
601 }
602
603 let overflow = (self.inline_position - available_inline_size).max(Au::zero());
604 if overflow < conditionally_hanging {
605 overflow
606 } else {
607 unconditionally_hanging + conditionally_hanging
608 }
609 }
610
611 fn count_justification_opportunities(&self) -> usize {
613 let mut saw_content = false;
614 self.line_items
615 .iter()
616 .rev()
617 .filter_map(|item| match item {
618 LineItem::TextRun(_, text_run) => Some(
619 text_run
620 .text
621 .iter()
622 .rev()
623 .map(|shaped_text_slice| {
624 let mut word_separators = shaped_text_slice.total_word_separators();
625 if !saw_content {
626 word_separators -=
627 shaped_text_slice.hanging_and_removable_word_separators();
628 }
629 saw_content |=
630 shaped_text_slice.has_non_hangable_non_removable_content();
631 word_separators
632 })
633 .sum::<usize>(),
634 ),
635 LineItem::Atomic(..) | LineItem::Tab { .. } => {
636 saw_content = true;
637 None
638 },
639 _ => None,
640 })
641 .sum()
642 }
643
644 fn is_phantom(&self) -> bool {
647 Self::is_phantom_inner(self.has_content, &self.line_items)
648 }
649
650 fn is_phantom_inner(has_content: bool, line_items: &[LineItem]) -> bool {
651 if has_content {
652 return false;
653 }
654
655 !line_items.iter().any(|item| match item {
658 LineItem::InlineStartBoxPaddingBorderMargin(state) => state.has_pbm_inline_start(),
659 LineItem::InlineEndBoxPaddingBorderMargin(state) => state.has_pbm_inline_end(),
660 _ => false,
661 })
662 }
663
664 fn tentative_hanging_and_removable_white_space_advance(&self) -> Au {
670 self.trailing_hangable_white_space + self.trailing_removable_white_space
671 }
672}
673
674#[derive(Clone, Debug)]
680struct BaselineRelativeSize {
681 ascent: Au,
685
686 descent: Au,
690}
691
692impl BaselineRelativeSize {
693 fn zero() -> Self {
694 Self {
695 ascent: Au::zero(),
696 descent: Au::zero(),
697 }
698 }
699
700 fn max(&self, other: &Self) -> Self {
701 BaselineRelativeSize {
702 ascent: self.ascent.max(other.ascent),
703 descent: self.descent.max(other.descent),
704 }
705 }
706
707 fn adjust_for_nested_baseline_offset(&mut self, baseline_offset: Au) {
721 self.ascent -= baseline_offset;
722 self.descent += baseline_offset;
723 }
724}
725
726#[derive(Clone, Debug)]
727struct LineBlockSizes {
728 line_height: Au,
729 baseline_relative_size_for_line_height: Option<BaselineRelativeSize>,
730 size_for_baseline_positioning: BaselineRelativeSize,
731}
732
733impl LineBlockSizes {
734 fn zero() -> Self {
735 LineBlockSizes {
736 line_height: Au::zero(),
737 baseline_relative_size_for_line_height: None,
738 size_for_baseline_positioning: BaselineRelativeSize::zero(),
739 }
740 }
741
742 fn resolve(&self) -> Au {
743 let height_from_ascent_and_descent = self
744 .baseline_relative_size_for_line_height
745 .as_ref()
746 .map(|size| (size.ascent + size.descent).abs())
747 .unwrap_or_else(Au::zero);
748 self.line_height.max(height_from_ascent_and_descent)
749 }
750
751 fn max(&self, other: &LineBlockSizes) -> LineBlockSizes {
752 let baseline_relative_size = match (
753 self.baseline_relative_size_for_line_height.as_ref(),
754 other.baseline_relative_size_for_line_height.as_ref(),
755 ) {
756 (Some(our_size), Some(other_size)) => Some(our_size.max(other_size)),
757 (our_size, other_size) => our_size.or(other_size).cloned(),
758 };
759 Self {
760 line_height: self.line_height.max(other.line_height),
761 baseline_relative_size_for_line_height: baseline_relative_size,
762 size_for_baseline_positioning: self
763 .size_for_baseline_positioning
764 .max(&other.size_for_baseline_positioning),
765 }
766 }
767
768 fn max_assign(&mut self, other: &LineBlockSizes) {
769 *self = self.max(other);
770 }
771
772 fn adjust_for_baseline_offset(&mut self, baseline_offset: Au) {
773 if let Some(size) = self.baseline_relative_size_for_line_height.as_mut() {
774 size.adjust_for_nested_baseline_offset(baseline_offset)
775 }
776 self.size_for_baseline_positioning
777 .adjust_for_nested_baseline_offset(baseline_offset);
778 }
779
780 fn find_baseline_offset(&self) -> Au {
787 match self.baseline_relative_size_for_line_height.as_ref() {
788 Some(size) => size.ascent,
789 None => {
790 let leading = self.resolve() -
793 (self.size_for_baseline_positioning.ascent +
794 self.size_for_baseline_positioning.descent);
795 leading.scale_by(0.5) + self.size_for_baseline_positioning.ascent
796 },
797 }
798 }
799}
800
801struct UnbreakableSegmentUnderConstruction {
805 inline_size: Au,
807
808 max_block_size: LineBlockSizes,
811
812 line_items: Vec<LineItem>,
814
815 has_content: bool,
819
820 incorporates_trailing_white_space: bool,
823
824 trailing_hangable_white_space: Au,
826
827 trailing_removable_white_space: Au,
829}
830
831impl UnbreakableSegmentUnderConstruction {
832 fn new() -> Self {
833 Self {
834 inline_size: Au::zero(),
835 max_block_size: LineBlockSizes {
836 line_height: Au::zero(),
837 baseline_relative_size_for_line_height: None,
838 size_for_baseline_positioning: BaselineRelativeSize::zero(),
839 },
840 line_items: Vec::new(),
841 has_content: false,
842 trailing_hangable_white_space: Au::zero(),
843 trailing_removable_white_space: Au::zero(),
844 incorporates_trailing_white_space: false,
845 }
846 }
847
848 fn reset(&mut self) {
850 assert!(self.line_items.is_empty()); self.inline_size = Au::zero();
852 self.max_block_size = LineBlockSizes::zero();
853 self.has_content = false;
854 self.trailing_hangable_white_space = Au::zero();
855 self.trailing_removable_white_space = Au::zero();
856 self.incorporates_trailing_white_space = false;
857 }
858
859 fn push_line_item(&mut self, line_item: LineItem) {
861 self.line_items.push(line_item);
862 }
863
864 fn trim_leading_whitespace(&mut self) {
875 let mut whitespace_trimmed = Au::zero();
876 for item in self.line_items.iter_mut() {
877 if !item.trim_whitespace_at_start(&mut whitespace_trimmed) {
878 break;
879 }
880 }
881 self.inline_size -= whitespace_trimmed;
882
883 if !self.incorporates_trailing_white_space {
887 let trimmed_from_hanging = whitespace_trimmed.min(self.trailing_hangable_white_space);
888 self.trailing_hangable_white_space -= trimmed_from_hanging;
889 self.trailing_removable_white_space -= whitespace_trimmed - trimmed_from_hanging;
890 }
891 }
892
893 fn is_phantom(&self) -> bool {
896 LineUnderConstruction::is_phantom_inner(self.has_content, &self.line_items)
897 }
898
899 fn trailing_inline_box_start_size(&self) -> Au {
904 self.line_items
905 .iter()
906 .rev()
907 .map_while(|item| match item {
908 LineItem::InlineStartBoxPaddingBorderMargin(state) => Some(state),
909 _ => None,
910 })
911 .map(|state| {
912 if state.should_clone_pbm() {
913 state.pbm_inline_start() + state.pbm_inline_end()
914 } else {
915 state.pbm_inline_start()
916 }
917 })
918 .sum()
919 }
920
921 fn hanging_and_removable_white_space_advance(&self) -> Au {
924 self.trailing_hangable_white_space + self.trailing_removable_white_space
925 }
926}
927
928bitflags! {
929 struct InlineContainerStateFlags: u8 {
930 const CREATE_STRUT = 0b0001;
931 const IS_SINGLE_LINE_TEXT_INPUT = 0b0010;
932 }
933}
934
935struct InlineContainerState {
936 style: ServoArc<ComputedValues>,
938
939 flags: InlineContainerStateFlags,
941
942 has_content: Cell<bool>,
945
946 strut_block_sizes: LineBlockSizes,
951
952 nested_strut_block_sizes: LineBlockSizes,
956
957 pub baseline_offset: Au,
963
964 default_font: Option<FontRef>,
967
968 font_metrics: Arc<FontMetrics>,
970}
971
972struct InlineFormattingContextLayout<'layout_data> {
973 positioning_context: &'layout_data mut PositioningContext,
974 placement_state: PlacementState<'layout_data>,
975 sequential_layout_state: Option<&'layout_data mut SequentialLayoutState>,
976 layout_context: &'layout_data LayoutContext<'layout_data>,
977
978 ifc: &'layout_data InlineFormattingContext,
980
981 root_nesting_level: InlineContainerState,
991
992 inline_box_state_stack: Vec<Rc<InlineBoxContainerState>>,
996
997 cloneable_inline_box_pbm_size: LogicalSides1D<Au>,
1001
1002 inline_box_states: Vec<Rc<InlineBoxContainerState>>,
1007
1008 fragments: Vec<Fragment>,
1012
1013 current_line: LineUnderConstruction,
1015
1016 current_line_segment: UnbreakableSegmentUnderConstruction,
1018
1019 force_line_break_before_new_content: bool,
1040
1041 caret_placeholder: Option<CaretPlaceholder>,
1044
1045 deferred_br_clear: Clear,
1049
1050 pub have_deferred_soft_wrap_opportunity: bool,
1054
1055 depends_on_block_constraints: bool,
1058
1059 white_space_collapse: WhiteSpaceCollapse,
1064
1065 text_wrap_mode: TextWrapMode,
1070
1071 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1074}
1075
1076impl InlineFormattingContextLayout<'_> {
1077 fn current_inline_container_state(&self) -> &InlineContainerState {
1078 match self.inline_box_state_stack.last() {
1079 Some(inline_box_state) => &inline_box_state.base,
1080 None => &self.root_nesting_level,
1081 }
1082 }
1083
1084 fn current_inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
1085 self.inline_box_state_stack
1086 .last()
1087 .map(|state| state.identifier)
1088 }
1089
1090 fn current_line_max_block_size_including_nested_containers(&self) -> LineBlockSizes {
1091 self.current_inline_container_state()
1092 .nested_strut_block_sizes
1093 .max(&self.current_line.max_block_size)
1094 }
1095
1096 fn current_line_block_start_considering_placement_among_floats(&self) -> Au {
1097 self.current_line.placement_among_floats.get().map_or(
1098 self.current_line.start_position.block,
1099 |placement_among_floats| placement_among_floats.start_corner.block,
1100 )
1101 }
1102
1103 fn propagate_current_nesting_level_white_space_style(&mut self) {
1104 let style = match self.inline_box_state_stack.last() {
1105 Some(inline_box_state) => &inline_box_state.base.style,
1106 None => self.placement_state.containing_block.style,
1107 };
1108 let style_text = style.get_inherited_text();
1109 self.white_space_collapse = style_text.white_space_collapse;
1110 self.text_wrap_mode = style_text.text_wrap_mode;
1111 }
1112
1113 fn processing_br_element(&self) -> bool {
1114 self.inline_box_state_stack.last().is_some_and(|state| {
1115 state
1116 .base_fragment_info
1117 .flags
1118 .contains(FragmentFlags::IS_BR_ELEMENT)
1119 })
1120 }
1121
1122 fn start_inline_box(&mut self, inline_box: &InlineBox) {
1125 let containing_block = self.containing_block();
1126 let inline_box_state = InlineBoxContainerState::new(
1127 inline_box,
1128 containing_block,
1129 self.layout_context,
1130 self.current_inline_container_state(),
1131 inline_box.default_font.clone(),
1132 );
1133
1134 self.depends_on_block_constraints |= inline_box
1135 .base
1136 .style
1137 .depends_on_block_constraints_due_to_relative_positioning(
1138 containing_block.style.writing_mode,
1139 );
1140
1141 if inline_box_state
1146 .base_fragment_info
1147 .flags
1148 .contains(FragmentFlags::IS_BR_ELEMENT) &&
1149 self.deferred_br_clear == Clear::None
1150 {
1151 self.deferred_br_clear = Clear::from_style_and_container_writing_mode(
1152 &inline_box_state.base.style,
1153 self.containing_block().style.writing_mode,
1154 );
1155 }
1156
1157 let inline_box_state = Rc::new(inline_box_state);
1158 let pbm_inline_start = inline_box_state.pbm_inline_start();
1159 self.current_line_segment.inline_size += pbm_inline_start;
1160
1161 if inline_box_state.should_clone_pbm() {
1162 self.cloneable_inline_box_pbm_size.start += pbm_inline_start;
1163 self.cloneable_inline_box_pbm_size.end += inline_box_state.pbm_inline_end();
1164 }
1165
1166 self.current_line_segment
1167 .line_items
1168 .push(LineItem::InlineStartBoxPaddingBorderMargin(
1169 inline_box_state.clone(),
1170 ));
1171
1172 assert_eq!(
1176 self.inline_box_states.len(),
1177 inline_box.identifier.index_in_inline_boxes as usize
1178 );
1179 self.inline_box_states.push(inline_box_state.clone());
1180 self.inline_box_state_stack.push(inline_box_state);
1181 }
1182
1183 fn finish_inline_box(&mut self) {
1186 let inline_box_state = match self.inline_box_state_stack.pop() {
1187 Some(inline_box_state) => inline_box_state,
1188 None => return, };
1190
1191 self.current_line_segment
1192 .max_block_size
1193 .max_assign(&inline_box_state.base.nested_strut_block_sizes);
1194
1195 if inline_box_state.base.has_content.get() {
1200 self.propagate_current_nesting_level_white_space_style();
1201 }
1202
1203 let pbm_inline_end = inline_box_state.pbm_inline_end();
1204 self.current_line_segment.inline_size += pbm_inline_end;
1205
1206 if inline_box_state.should_clone_pbm() {
1207 self.cloneable_inline_box_pbm_size.start -= inline_box_state.pbm_inline_start();
1208 self.cloneable_inline_box_pbm_size.end -= pbm_inline_end;
1209 }
1210
1211 self.current_line_segment
1212 .line_items
1213 .push(LineItem::InlineEndBoxPaddingBorderMargin(inline_box_state));
1214 }
1215
1216 fn finish_last_line(&mut self) {
1217 self.possibly_flush_deferred_forced_line_break();
1219
1220 self.process_soft_wrap_opportunity();
1226
1227 self.commit_current_segment_to_line();
1230
1231 self.finish_current_line_and_reset(
1234 true, false, );
1237 }
1238
1239 fn finish_current_line_and_reset(
1243 &mut self,
1244 last_line_or_forced_line_break: bool,
1245 for_block_level: bool,
1246 ) {
1247 let hanging_white_space = self
1248 .current_line
1249 .trim_trailing_white_space_and_calculate_hanging(
1250 self.available_line_space(),
1251 last_line_or_forced_line_break,
1252 );
1253
1254 self.possibly_push_empty_text_run_to_line_for_text_caret();
1255
1256 if !self.current_line.for_block_level {
1259 self.current_line.inline_position +=
1260 self.current_line.cloneable_inline_box_pbm_size.end;
1261 }
1262
1263 let (inline_start_position, justification_adjustment) = self
1264 .calculate_current_line_inline_start_and_justification_adjustment(
1265 hanging_white_space,
1266 last_line_or_forced_line_break,
1267 );
1268
1269 let is_phantom_line = self.current_line.is_phantom();
1278 if !is_phantom_line {
1279 self.current_line.start_position.block += self.placement_state.current_margin.solve();
1280 self.placement_state.current_margin = CollapsedMargin::zero();
1281 }
1282 let block_start_position =
1283 self.current_line_block_start_considering_placement_among_floats();
1284
1285 let effective_block_advance = if is_phantom_line {
1286 LineBlockSizes::zero()
1287 } else {
1288 self.current_line_max_block_size_including_nested_containers()
1289 };
1290
1291 let resolved_block_advance = effective_block_advance.resolve();
1292 let block_end_position = if self.current_line.for_block_level {
1293 self.placement_state.current_block_direction_position
1294 } else {
1295 let mut block_end_position = block_start_position + resolved_block_advance;
1296 if let Some(sequential_layout_state) = self.sequential_layout_state.as_mut() {
1297 if !is_phantom_line {
1298 sequential_layout_state.commit_margin();
1299 }
1300
1301 let increment = block_end_position - self.current_line.start_position.block;
1304 sequential_layout_state.advance_block_position(increment);
1305
1306 if let Some(clearance) = sequential_layout_state
1310 .calculate_clearance(self.deferred_br_clear, &CollapsedMargin::zero())
1311 {
1312 sequential_layout_state.advance_block_position(clearance);
1313 block_end_position += clearance;
1314 };
1315 self.deferred_br_clear = Clear::None;
1316 }
1317 block_end_position
1318 };
1319
1320 let line_to_layout = std::mem::replace(
1322 &mut self.current_line,
1323 LineUnderConstruction::new(LogicalVec2 {
1324 inline: Au::zero(),
1325 block: block_end_position,
1326 }),
1327 );
1328 self.current_line.for_block_level = for_block_level;
1329
1330 self.current_line.cloneable_inline_box_pbm_size =
1332 line_to_layout.cloneable_inline_box_pbm_size;
1333 if !self.current_line.for_block_level {
1336 self.current_line.inline_position +=
1337 self.current_line.cloneable_inline_box_pbm_size.start;
1338 }
1339
1340 if !line_to_layout.for_block_level {
1341 self.placement_state.current_block_direction_position = block_end_position;
1342 }
1343
1344 if line_to_layout.has_floats_waiting_to_be_placed {
1345 place_pending_floats(self, &line_to_layout.line_items);
1346 }
1347
1348 let start_position = LogicalVec2 {
1349 block: block_start_position,
1350 inline: inline_start_position,
1351 };
1352
1353 let baseline_offset = effective_block_advance.find_baseline_offset();
1354 let start_positioning_context_length = self.positioning_context.len();
1355 let fragments = LineItemLayout::layout_line_items(
1356 self,
1357 line_to_layout.line_items,
1358 start_position,
1359 &effective_block_advance,
1360 justification_adjustment,
1361 is_phantom_line,
1362 line_to_layout.for_block_level,
1363 );
1364
1365 if !is_phantom_line {
1366 let baseline = baseline_offset + block_start_position;
1367 self.placement_state
1368 .inflow_baselines
1369 .first
1370 .get_or_insert(baseline);
1371 self.placement_state.inflow_baselines.last = Some(baseline);
1372 self.placement_state
1373 .next_in_flow_margin_collapses_with_parent_start_margin = false;
1374 }
1375
1376 if fragments.is_empty() &&
1378 self.positioning_context.len() == start_positioning_context_length
1379 {
1380 return;
1381 }
1382
1383 let start_corner = LogicalVec2 {
1387 inline: Au::zero(),
1388 block: block_start_position,
1389 };
1390
1391 let logical_origin_in_physical_coordinates =
1392 start_corner.to_physical_vector(self.containing_block().style.writing_mode);
1393 self.positioning_context
1394 .adjust_static_position_of_hoisted_fragments_with_offset(
1395 &logical_origin_in_physical_coordinates,
1396 start_positioning_context_length,
1397 );
1398
1399 let containing_block = self.containing_block();
1400 let physical_line_rect = LogicalRect {
1401 start_corner,
1402 size: LogicalVec2 {
1403 inline: containing_block.size.inline,
1404 block: effective_block_advance.resolve(),
1405 },
1406 }
1407 .as_physical(Some(containing_block));
1408 self.fragments
1409 .push(Fragment::Positioning(PositioningFragment::new_anonymous(
1410 self.root_nesting_level.style.clone(),
1411 physical_line_rect,
1412 fragments,
1413 true, )));
1415 }
1416
1417 fn available_line_space(&self) -> Au {
1418 match self.current_line.placement_among_floats.get() {
1419 Some(placement_among_floats) => placement_among_floats.size.inline,
1420 None => self.containing_block().size.inline,
1421 }
1422 }
1423
1424 fn calculate_current_line_inline_start_and_justification_adjustment(
1429 &self,
1430 hanging_white_space: Au,
1431 last_line_or_forced_line_break: bool,
1432 ) -> (Au, Au) {
1433 enum TextAlign {
1434 Start,
1435 Center,
1436 End,
1437 }
1438 let containing_block = self.containing_block();
1439 let style = containing_block.style;
1440 let mut text_align_keyword = style.get_text_align();
1441
1442 if last_line_or_forced_line_break {
1443 text_align_keyword = match style.get_text_align_last() {
1444 TextAlignLast::Auto if text_align_keyword == &TextAlignKeyword::Justify => {
1445 &TextAlignKeyword::Start
1446 },
1447 TextAlignLast::Auto => text_align_keyword,
1448 TextAlignLast::Start => &TextAlignKeyword::Start,
1449 TextAlignLast::End => &TextAlignKeyword::End,
1450 TextAlignLast::Left => &TextAlignKeyword::Left,
1451 TextAlignLast::Right => &TextAlignKeyword::Right,
1452 TextAlignLast::Center => &TextAlignKeyword::Center,
1453 TextAlignLast::Justify => &TextAlignKeyword::Justify,
1454 };
1455 }
1456
1457 let text_align = match text_align_keyword {
1458 TextAlignKeyword::Start => TextAlign::Start,
1459 TextAlignKeyword::Center | TextAlignKeyword::MozCenter => TextAlign::Center,
1460 TextAlignKeyword::End => TextAlign::End,
1461 TextAlignKeyword::Left | TextAlignKeyword::MozLeft => {
1462 if style.writing_mode.line_left_is_inline_start() {
1463 TextAlign::Start
1464 } else {
1465 TextAlign::End
1466 }
1467 },
1468 TextAlignKeyword::Right | TextAlignKeyword::MozRight => {
1469 if style.writing_mode.line_left_is_inline_start() {
1470 TextAlign::End
1471 } else {
1472 TextAlign::Start
1473 }
1474 },
1475 TextAlignKeyword::Justify => TextAlign::Start,
1476 };
1477
1478 let available_space = self.available_line_space();
1479 let line_start = match self.current_line.placement_among_floats.get() {
1480 Some(placement_among_floats) => placement_among_floats.start_corner.inline,
1481 None => Au::zero(),
1482 };
1483
1484 let text_indent = self.current_line.start_position.inline;
1491 let line_length = self.current_line.inline_position - hanging_white_space - text_indent;
1492 let adjusted_line_start = line_start +
1493 match text_align {
1494 TextAlign::Start => text_indent,
1495 TextAlign::End => (available_space - line_length).max(text_indent),
1496 TextAlign::Center => (available_space - line_length + text_indent)
1497 .scale_by(0.5)
1498 .max(text_indent),
1499 };
1500
1501 let text_justify = containing_block.style.get_text_justify();
1505 let justification_adjustment = match (text_align_keyword, text_justify) {
1506 (TextAlignKeyword::Justify, TextJustify::None) => Au::zero(),
1509 (TextAlignKeyword::Justify, _) => {
1510 match self.current_line.count_justification_opportunities() {
1511 0 => Au::zero(),
1512 num_justification_opportunities => {
1513 (available_space - text_indent - line_length)
1514 .scale_by(1. / num_justification_opportunities as f32)
1515 },
1516 }
1517 },
1518 _ => Au::zero(),
1519 };
1520
1521 let justification_adjustment = justification_adjustment.max(Au::zero());
1524
1525 (adjusted_line_start, justification_adjustment)
1526 }
1527
1528 fn place_float_fragment(&mut self, float: &FloatLineItem) {
1529 let state = self
1530 .sequential_layout_state
1531 .as_mut()
1532 .expect("Tried to lay out a float with no sequential placement state!");
1533
1534 let block_offset_from_containining_block_top = state
1535 .current_block_position_including_margins() -
1536 state.current_containing_block_offset();
1537 state.place_float_fragment(
1538 &float.fragment,
1539 self.placement_state.containing_block,
1540 CollapsedMargin::zero(),
1541 block_offset_from_containining_block_top,
1542 );
1543 self.positioning_context
1544 .adjust_static_position_of_hoisted_fragments_in_range(
1545 &float.fragment.base.rect().origin.to_vector(),
1546 &float.range,
1547 )
1548 }
1549
1550 fn place_float_line_item_for_commit_to_line(
1559 &mut self,
1560 float_item: &mut FloatLineItem,
1561 potential_line_length: Au,
1562 ) {
1563 let containing_block = self.containing_block();
1564 let float_fragment = &float_item.fragment;
1565 let logical_margin_rect_size = float_fragment
1566 .margin_rect()
1567 .size
1568 .to_logical(containing_block.style.writing_mode);
1569 let inline_size = logical_margin_rect_size.inline.max(Au::zero());
1570
1571 let available_inline_size = match self.current_line.placement_among_floats.get() {
1572 Some(placement_among_floats) => placement_among_floats.size.inline,
1573 None => containing_block.size.inline,
1574 } - potential_line_length;
1575
1576 let has_content = self.current_line.has_content || self.current_line_segment.has_content;
1582 let fits_on_line = !has_content || inline_size <= available_inline_size;
1583 let needs_placement_later =
1584 self.current_line.has_floats_waiting_to_be_placed || !fits_on_line;
1585
1586 if needs_placement_later {
1587 self.current_line.has_floats_waiting_to_be_placed = true;
1588 } else {
1589 self.place_float_fragment(float_item);
1590 float_item.needs_placement = false;
1591 }
1592
1593 let new_placement = self.place_line_among_floats(&LogicalVec2 {
1598 inline: potential_line_length,
1599 block: self.current_line.max_block_size.resolve(),
1600 });
1601 self.current_line
1602 .replace_placement_among_floats(new_placement);
1603 }
1604
1605 fn place_line_among_floats(&self, potential_line_size: &LogicalVec2<Au>) -> LogicalRect<Au> {
1610 let sequential_layout_state = self
1611 .sequential_layout_state
1612 .as_ref()
1613 .expect("Should not have called this function without having floats.");
1614
1615 let ifc_offset_in_float_container = LogicalVec2 {
1616 inline: sequential_layout_state
1617 .floats
1618 .containing_block_info
1619 .inline_start,
1620 block: sequential_layout_state.current_containing_block_offset(),
1621 };
1622
1623 let ceiling = self.current_line_block_start_considering_placement_among_floats();
1624 let mut placement = PlacementAmongFloats::new(
1625 &sequential_layout_state.floats,
1626 ceiling + ifc_offset_in_float_container.block,
1627 LogicalVec2 {
1628 inline: potential_line_size.inline,
1629 block: potential_line_size.block,
1630 },
1631 &PaddingBorderMargin::zero(),
1632 );
1633
1634 let mut placement_rect = placement.place();
1635 placement_rect.start_corner -= ifc_offset_in_float_container;
1636 placement_rect
1637 }
1638
1639 fn new_potential_line_size_causes_line_break(
1646 &mut self,
1647 potential_line_size: &LogicalVec2<Au>,
1648 ) -> bool {
1649 let containing_block = self.containing_block();
1650 let available_line_space = if self.sequential_layout_state.is_some() {
1651 self.current_line
1652 .placement_among_floats
1653 .get_or_init(|| self.place_line_among_floats(potential_line_size))
1654 .size
1655 } else {
1656 LogicalVec2 {
1657 inline: containing_block.size.inline,
1658 block: MAX_AU,
1659 }
1660 };
1661
1662 let inline_would_overflow = potential_line_size.inline > available_line_space.inline;
1663 let block_would_overflow = potential_line_size.block > available_line_space.block;
1664
1665 let can_break = self.current_line.has_content;
1668
1669 if !can_break {
1675 if self.sequential_layout_state.is_some() &&
1678 (inline_would_overflow || block_would_overflow)
1679 {
1680 let new_placement = self.place_line_among_floats(potential_line_size);
1681 self.current_line
1682 .replace_placement_among_floats(new_placement);
1683 }
1684
1685 return false;
1686 }
1687
1688 if potential_line_size.inline > containing_block.size.inline {
1691 return true;
1692 }
1693
1694 if block_would_overflow {
1698 assert!(self.sequential_layout_state.is_some());
1700 let new_placement = self.place_line_among_floats(potential_line_size);
1701 if new_placement.start_corner.block !=
1702 self.current_line_block_start_considering_placement_among_floats()
1703 {
1704 return true;
1705 } else {
1706 self.current_line
1707 .replace_placement_among_floats(new_placement);
1708 return false;
1709 }
1710 }
1711
1712 potential_line_size.inline > available_line_space.inline
1716 }
1717
1718 fn defer_forced_line_break_at_character_offset(
1719 &mut self,
1720 caret_placeholder: &Option<CaretPlaceholder>,
1721 ) {
1722 if !self.unbreakable_segment_fits_on_line() {
1725 self.rewind_trailing_inline_box_starts();
1726 self.process_line_break(
1727 false, false, );
1730 }
1731
1732 self.force_line_break_before_new_content = true;
1734 self.caret_placeholder = caret_placeholder.clone();
1735
1736 let line_is_empty =
1744 !self.current_line_segment.has_content && !self.current_line.has_content;
1745 if !self.processing_br_element() || line_is_empty {
1746 let strut_size = self
1747 .current_inline_container_state()
1748 .strut_block_sizes
1749 .clone();
1750 self.update_unbreakable_segment_for_new_content(
1751 &strut_size,
1752 Au::zero(),
1753 Au::zero(),
1754 Au::zero(),
1755 SegmentContentFlags::Contentful,
1756 );
1757 }
1758 }
1759
1760 fn possibly_flush_deferred_forced_line_break(&mut self) {
1761 if !self.force_line_break_before_new_content {
1762 return;
1763 }
1764 self.force_line_break_before_new_content = false;
1765
1766 self.commit_current_segment_to_line();
1767 self.process_line_break(
1768 true, false, );
1771
1772 self.current_line.caret_placeholder = self.caret_placeholder.take();
1773 }
1774
1775 fn push_line_item_to_unbreakable_segment(&mut self, line_item: LineItem) {
1776 self.current_line_segment.push_line_item(line_item);
1777 }
1778
1779 fn push_shaped_text_slice_to_unbreakable_segment(
1780 &mut self,
1781 slice: Arc<ShapedTextSlice>,
1782 text_run: &TextRun,
1783 info: &FontAndScriptInfo,
1784 character_range: Range<Utf32CodeUnits>,
1785 ) {
1786 let mut block_contribution = LineBlockSizes::zero();
1787 let quirks_mode = self.layout_context.style_context.quirks_mode() != QuirksMode::NoQuirks;
1788 let current_inline_container_state = self.current_inline_container_state();
1789 if quirks_mode && !slice.all_removable() {
1790 block_contribution.max_assign(¤t_inline_container_state.strut_block_sizes);
1795 }
1796
1797 let font_metrics = info.font_info.font.metrics();
1801 if current_inline_container_state
1802 .font_metrics
1803 .block_metrics_meaningfully_differ(font_metrics)
1804 {
1805 let baseline_shift = effective_baseline_shift(
1807 ¤t_inline_container_state.style,
1808 self.inline_box_state_stack.last().map(|c| &c.base),
1809 );
1810 let mut font_block_conribution = current_inline_container_state
1811 .get_block_size_contribution(
1812 &baseline_shift,
1813 font_metrics,
1814 ¤t_inline_container_state.font_metrics,
1815 );
1816 font_block_conribution
1817 .adjust_for_baseline_offset(current_inline_container_state.baseline_offset);
1818 block_contribution.max_assign(&font_block_conribution);
1819 }
1820
1821 let mut segment_flags = SegmentContentFlags::empty();
1822 if slice.has_non_hangable_non_removable_content() {
1823 segment_flags.insert(SegmentContentFlags::IncorporateTrailingWhiteSpace);
1824 }
1825 if !slice.all_removable() {
1826 segment_flags.insert(SegmentContentFlags::Contentful);
1827 }
1828
1829 self.update_unbreakable_segment_for_new_content(
1830 &block_contribution,
1831 slice.total_advance(),
1832 slice.hangable_advance(),
1833 slice.removable_advance(),
1834 segment_flags,
1835 );
1836
1837 let current_inline_box_identifier = self.current_inline_box_identifier();
1838 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1839 current_inline_box_identifier,
1840 TextRunLineItem {
1841 text: vec![slice],
1842 text_fragment_run_data: text_run.run_data.clone(),
1843 base_fragment_info: text_run.base_fragment_info,
1844 info: info.clone(),
1845 character_range_in_dom_node: character_range,
1846 is_empty_for_text_cursor: false,
1847 },
1848 ));
1849 }
1850
1851 fn possibly_push_empty_text_run_to_line_for_text_caret(&mut self) {
1854 let Some(caret_placeholder) = self.current_line.caret_placeholder.take() else {
1855 return;
1856 };
1857
1858 if self
1860 .current_line
1861 .line_items
1862 .iter()
1863 .rev()
1864 .find(|line_item| line_item.is_in_flow_content())
1865 .is_some_and(|line_item| matches!(line_item, LineItem::TextRun(..)))
1866 {
1867 return;
1868 }
1869
1870 let inline_container_state = self.current_inline_container_state();
1871 let Some(font) = inline_container_state.default_font.clone() else {
1872 return;
1873 };
1874
1875 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1876 self.current_inline_box_identifier(),
1877 TextRunLineItem {
1878 text: Default::default(),
1879 text_fragment_run_data: caret_placeholder.run_data,
1880 base_fragment_info: caret_placeholder.base_fragment_info,
1881 info: FontAndScriptInfo::simple_for_font(font),
1882 character_range_in_dom_node: caret_placeholder.character_index..
1883 caret_placeholder.character_index + Utf32CodeUnits(1),
1884 is_empty_for_text_cursor: true,
1885 },
1886 ));
1887
1888 let strut_size = self
1889 .current_inline_container_state()
1890 .strut_block_sizes
1891 .clone();
1892 self.update_unbreakable_segment_for_new_content(
1893 &strut_size,
1894 Au::zero(),
1895 Au::zero(),
1896 Au::zero(),
1897 SegmentContentFlags::empty(),
1898 );
1899
1900 self.current_line_segment.has_content = true;
1901 self.commit_current_segment_to_line();
1902 }
1903
1904 fn update_unbreakable_segment_for_new_content(
1905 &mut self,
1906 block_sizes_of_content: &LineBlockSizes,
1907 inline_size: Au,
1908 hangable_size: Au,
1909 removable_size: Au,
1910 segment_flags: SegmentContentFlags,
1911 ) {
1912 if segment_flags.contains(SegmentContentFlags::IncorporateTrailingWhiteSpace) {
1913 self.current_line_segment.trailing_hangable_white_space = Au::zero();
1914 self.current_line_segment.trailing_removable_white_space = Au::zero();
1915 self.current_line_segment.incorporates_trailing_white_space = true;
1916 }
1917
1918 if segment_flags.contains(SegmentContentFlags::Contentful) {
1919 self.current_line_segment.has_content = true;
1920 }
1921
1922 if hangable_size > Au::zero() {
1923 let old_removable_space =
1926 std::mem::take(&mut self.current_line_segment.trailing_removable_white_space);
1927 self.current_line_segment.trailing_hangable_white_space +=
1928 old_removable_space + hangable_size;
1929 }
1930 self.current_line_segment.trailing_removable_white_space += removable_size;
1931
1932 let container_max_block_size = &self
1934 .current_inline_container_state()
1935 .nested_strut_block_sizes
1936 .clone();
1937 self.current_line_segment
1938 .max_block_size
1939 .max_assign(container_max_block_size);
1940 self.current_line_segment
1941 .max_block_size
1942 .max_assign(block_sizes_of_content);
1943
1944 self.current_line_segment.inline_size += inline_size;
1945
1946 self.current_inline_container_state().has_content.set(true);
1948 self.propagate_current_nesting_level_white_space_style();
1949 }
1950
1951 fn process_line_break(&mut self, forced_line_break: bool, for_block_level: bool) {
1952 self.current_line_segment.trim_leading_whitespace();
1953 self.finish_current_line_and_reset(forced_line_break, for_block_level);
1954 }
1955
1956 fn hypothetical_line_size(&self) -> LogicalVec2<Au> {
1957 let mut hanging_and_removable_whitespace = self
1958 .current_line_segment
1959 .hanging_and_removable_white_space_advance();
1960 if !self.current_line_segment.incorporates_trailing_white_space {
1961 hanging_and_removable_whitespace += self
1962 .current_line
1963 .tentative_hanging_and_removable_white_space_advance();
1964 }
1965 let inline = self.current_line.inline_position +
1966 self.current_line_segment.inline_size +
1967 self.cloneable_inline_box_pbm_size.end -
1968 hanging_and_removable_whitespace -
1969 self.current_line_segment.trailing_inline_box_start_size();
1970
1971 LogicalVec2 {
1972 inline,
1973 block: self
1974 .current_line_max_block_size_including_nested_containers()
1975 .max(&self.current_line_segment.max_block_size)
1976 .resolve(),
1977 }
1978 }
1979
1980 fn unbreakable_segment_fits_on_line(&mut self) -> bool {
1981 !self.new_potential_line_size_causes_line_break(&self.hypothetical_line_size())
1982 }
1983
1984 fn rewind_trailing_inline_box_starts(&mut self) {
1990 let mut removal_start = self.current_line.line_items.len();
1991 for item in self.current_line.line_items.iter().rev() {
1992 let LineItem::InlineStartBoxPaddingBorderMargin(state) = item else {
1993 break;
1994 };
1995
1996 removal_start -= 1;
1997
1998 let inline_start = state.pbm_inline_start();
1999 self.current_line.inline_position -= inline_start;
2000 self.current_line_segment.inline_size += inline_start;
2001
2002 if state.should_clone_pbm() {
2003 self.current_line.cloneable_inline_box_pbm_size.start -= inline_start;
2004 self.current_line.cloneable_inline_box_pbm_size.end -= state.pbm_inline_end();
2005 }
2006 }
2007
2008 self.current_line_segment
2009 .line_items
2010 .splice(0..0, self.current_line.line_items.drain(removal_start..));
2011 }
2012
2013 fn process_soft_wrap_opportunity(&mut self) {
2017 if self.current_line_segment.line_items.is_empty() {
2018 return;
2019 }
2020 if self.text_wrap_mode == TextWrapMode::Nowrap {
2021 return;
2022 }
2023 if !self.unbreakable_segment_fits_on_line() {
2024 self.rewind_trailing_inline_box_starts();
2025 self.process_line_break(
2026 false, false, );
2029 }
2030 self.commit_current_segment_to_line();
2031 }
2032
2033 fn commit_current_segment_to_line(&mut self) {
2036 self.current_line.cloneable_inline_box_pbm_size = self.cloneable_inline_box_pbm_size;
2039
2040 if self.current_line_segment.line_items.is_empty() && !self.current_line_segment.has_content
2043 {
2044 return;
2045 }
2046
2047 if !self.current_line.has_content {
2048 self.current_line_segment.trim_leading_whitespace();
2049 }
2050
2051 let potential_line_length = self.hypothetical_line_size().inline;
2052
2053 let old_removable_space =
2054 std::mem::take(&mut self.current_line.trailing_removable_white_space);
2055 let old_hangable_space =
2056 std::mem::take(&mut self.current_line.trailing_hangable_white_space);
2057 let mut new_removable_space = self.current_line_segment.trailing_removable_white_space;
2058 let mut new_hangable_space = self.current_line_segment.trailing_hangable_white_space;
2059
2060 if !self.current_line_segment.incorporates_trailing_white_space {
2063 if new_hangable_space > Au::zero() {
2066 new_hangable_space += old_removable_space;
2067 } else {
2068 new_removable_space += old_removable_space;
2069 }
2070 new_hangable_space += old_hangable_space;
2071 }
2072
2073 self.current_line.trailing_hangable_white_space = new_hangable_space;
2074 self.current_line.trailing_removable_white_space = new_removable_space;
2075
2076 self.current_line.inline_position += self.current_line_segment.inline_size;
2077 self.current_line.max_block_size = self
2078 .current_line_max_block_size_including_nested_containers()
2079 .max(&self.current_line_segment.max_block_size);
2080
2081 let mut segment_items = mem::take(&mut self.current_line_segment.line_items);
2083
2084 for item in segment_items.iter_mut() {
2085 if let LineItem::Float(_, float_item) = item {
2086 self.place_float_line_item_for_commit_to_line(float_item, potential_line_length);
2087 }
2088 }
2089
2090 if self.current_line.line_items.is_empty() {
2095 let will_break = self.new_potential_line_size_causes_line_break(&LogicalVec2 {
2096 inline: potential_line_length,
2097 block: self.current_line_segment.max_block_size.resolve(),
2098 });
2099 assert!(!will_break);
2100 }
2101
2102 debug_assert!(
2103 self.current_line_segment.line_items.is_empty(),
2104 "Committing a segment should not add items to it"
2105 );
2106 self.current_line.line_items.append(&mut segment_items);
2108 self.current_line_segment.line_items = segment_items;
2109 self.current_line.has_content |= self.current_line_segment.has_content;
2110
2111 self.current_line_segment.reset();
2112 }
2113
2114 #[inline]
2115 fn containing_block(&self) -> &ContainingBlock<'_> {
2116 self.placement_state.containing_block
2117 }
2118}
2119
2120bitflags! {
2121 struct SegmentContentFlags: u8 {
2122 const IncorporateTrailingWhiteSpace = 1 << 0;
2123 const Contentful = 1 << 1;
2124 }
2125}
2126
2127impl InlineFormattingContext {
2128 #[servo_tracing::instrument(name = "InlineFormattingContext::new_with_builder", skip_all)]
2129 fn new_with_builder(
2130 mut builder: InlineFormattingContextBuilder,
2131 layout_context: &LayoutContext,
2132 has_first_formatted_line: bool,
2133 is_single_line_text_input: bool,
2134 starting_bidi_level: Level,
2135 ) -> Self {
2136 let text_content: String = builder.text_segments.into_iter().collect();
2138
2139 let bidi_levels = BidiLevels {
2140 info: builder
2141 .has_right_to_left_content
2142 .then(|| BidiInfo::new(&text_content, Some(starting_bidi_level))),
2143 };
2144
2145 let shared_inline_styles = builder
2146 .shared_inline_styles_stack
2147 .last()
2148 .expect("Should have at least one SharedInlineStyle for the root of an IFC")
2149 .clone();
2150 let (word_break, line_break, lang) = {
2151 let styles = shared_inline_styles.style.borrow();
2152 let text_style = styles.get_inherited_text();
2153 (
2154 text_style.word_break,
2155 text_style.line_break,
2156 styles.get_font()._x_lang.clone(),
2157 )
2158 };
2159
2160 let mut options = LineBreakOptions::default();
2161
2162 options.strictness = Some(match line_break {
2165 LineBreak::Loose => LineBreakStrictness::Loose,
2166 LineBreak::Normal => LineBreakStrictness::Normal,
2167 LineBreak::Strict => LineBreakStrictness::Strict,
2168 LineBreak::Anywhere => LineBreakStrictness::Anywhere,
2169 LineBreak::Auto => LineBreakStrictness::Normal,
2172 });
2173 options.word_option = Some(match word_break {
2174 WordBreak::Normal | WordBreak::KeepAll => LineBreakWordOption::Normal,
2175 WordBreak::BreakWord => LineBreakWordOption::Normal,
2177 WordBreak::BreakAll => LineBreakWordOption::BreakAll,
2178 });
2179
2180 let content_locale = lang.0.parse::<LanguageIdentifier>().ok();
2183 options.content_locale = content_locale.as_ref();
2184
2185 let mut shaping_queue = ShapingQueue::new(&text_content, options);
2186 for item in &mut builder.inline_items {
2187 match item {
2188 InlineItem::TextRun(text_run) => {
2189 let shaping_queue_entries = text_run.borrow_mut().segment(
2190 text_run.clone(),
2191 &text_content,
2192 layout_context,
2193 &bidi_levels,
2194 );
2195 for entry in shaping_queue_entries.into_iter() {
2196 shaping_queue.push(entry);
2197 }
2198 },
2199 InlineItem::StartInlineBox(inline_box) => {
2200 let inline_box = &mut *inline_box.borrow_mut();
2201 if let Some(font) = get_font_for_first_font_for_style(
2202 &inline_box.base.style,
2203 &layout_context.font_context,
2204 ) {
2205 inline_box.default_font = Some(font);
2206 }
2207
2208 if inline_box.breaks_shaping_at_start {
2209 shaping_queue.flush();
2210 }
2211 },
2212 InlineItem::Atomic(_, index_in_text, bidi_level) => {
2213 shaping_queue.flush();
2214 *bidi_level = bidi_levels.level(usize::from(*index_in_text));
2215 },
2216 InlineItem::EndInlineBox(inline_box) => {
2217 if inline_box.borrow().breaks_shaping_at_end {
2218 shaping_queue.flush();
2219 }
2220 },
2221 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) |
2222 InlineItem::OutOfFlowFloatBox(_) |
2223 InlineItem::BlockLevel { .. } => {},
2224 }
2225 }
2226
2227 shaping_queue.flush();
2228
2229 let default_font = get_font_for_first_font_for_style(
2230 &shared_inline_styles.style.borrow(),
2231 &layout_context.font_context,
2232 );
2233
2234 let has_right_to_left_content = bidi_levels.info.as_ref().is_some_and(BidiInfo::has_rtl);
2235 builder
2236 .text_content_slot
2237 .set(text_content)
2238 .expect("Text content should not yet be set.");
2239
2240 InlineFormattingContext {
2241 text_content: builder.text_content_slot,
2242 inline_items: builder.inline_items,
2243 inline_boxes: builder.inline_boxes,
2244 shared_inline_styles,
2245 default_font,
2246 has_first_formatted_line,
2247 contains_floats: builder.contains_floats,
2248 is_single_line_text_input,
2249 has_right_to_left_content,
2250 tab_size_multiplier: Default::default(),
2251 }
2252 }
2253
2254 pub(crate) fn text_content(&self) -> &str {
2255 self.text_content.get().map_or("", String::as_str)
2256 }
2257
2258 pub(crate) fn repair_style(
2259 &self,
2260 context: &SharedStyleContext,
2261 node: &ServoLayoutNode,
2262 new_style: &ServoArc<ComputedValues>,
2263 ) {
2264 *self.shared_inline_styles.style.borrow_mut() = new_style.clone();
2265 *self.shared_inline_styles.selected.borrow_mut() = node.selected_style(context);
2266 }
2267
2268 fn inline_start_for_first_line(&self, containing_block: IndefiniteContainingBlock) -> Au {
2269 if !self.has_first_formatted_line {
2270 return Au::zero();
2271 }
2272 containing_block
2273 .style
2274 .get_inherited_text()
2275 .text_indent
2276 .length
2277 .to_used_value(containing_block.size.inline.unwrap_or_default())
2278 }
2279
2280 pub(super) fn layout(
2281 &self,
2282 layout_context: &LayoutContext,
2283 positioning_context: &mut PositioningContext,
2284 containing_block: &ContainingBlock,
2285 sequential_layout_state: Option<&mut SequentialLayoutState>,
2286 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
2287 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
2288 ) -> IndependentFormattingContextLayoutResult {
2289 for inline_box in self.inline_boxes.iter() {
2291 inline_box.borrow().base.clear_fragments();
2292 }
2293
2294 let style = containing_block.style;
2295
2296 let style_text = containing_block.style.get_inherited_text();
2297 let mut inline_container_state_flags = InlineContainerStateFlags::empty();
2298 if inline_container_needs_strut(style, layout_context, None) {
2299 inline_container_state_flags.insert(InlineContainerStateFlags::CREATE_STRUT);
2300 }
2301 if self.is_single_line_text_input {
2302 inline_container_state_flags
2303 .insert(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT);
2304 }
2305 let placement_state =
2306 PlacementState::new(collapsible_with_parent_start_margin, containing_block);
2307
2308 let mut layout = InlineFormattingContextLayout {
2309 positioning_context,
2310 placement_state,
2311 sequential_layout_state,
2312 layout_context,
2313 ifc: self,
2314 fragments: Vec::new(),
2315 current_line: LineUnderConstruction::new(LogicalVec2 {
2316 inline: self.inline_start_for_first_line(containing_block.into()),
2317 block: Au::zero(),
2318 }),
2319 root_nesting_level: InlineContainerState::new(
2320 style.to_arc(),
2321 inline_container_state_flags,
2322 None, self.default_font.clone(),
2324 ),
2325 inline_box_state_stack: Vec::new(),
2326 cloneable_inline_box_pbm_size: Default::default(),
2327 inline_box_states: Vec::with_capacity(self.inline_boxes.len()),
2328 current_line_segment: UnbreakableSegmentUnderConstruction::new(),
2329 force_line_break_before_new_content: false,
2330 caret_placeholder: None,
2331 deferred_br_clear: Clear::None,
2332 have_deferred_soft_wrap_opportunity: false,
2333 depends_on_block_constraints: false,
2334 white_space_collapse: style_text.white_space_collapse,
2335 text_wrap_mode: style_text.text_wrap_mode,
2336 ignore_block_margins_for_stretch,
2337 };
2338
2339 for item in self.inline_items.iter() {
2340 if !matches!(item, InlineItem::EndInlineBox(..)) {
2342 layout.possibly_flush_deferred_forced_line_break();
2343 }
2344
2345 match item {
2346 InlineItem::StartInlineBox(inline_box) => {
2347 layout.start_inline_box(&inline_box.borrow());
2348 },
2349 InlineItem::EndInlineBox(..) => layout.finish_inline_box(),
2350 InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout),
2351 InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
2352 atomic_formatting_context.borrow().layout_into_line_items(
2353 &mut layout,
2354 *offset_in_text,
2355 *bidi_level,
2356 );
2357 },
2358 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, _) => {
2359 layout.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
2360 layout.current_inline_box_identifier(),
2361 AbsolutelyPositionedLineItem {
2362 absolutely_positioned_box: positioned_box.clone(),
2363 preceding_line_content_would_produce_phantom_line: layout
2364 .current_line
2365 .is_phantom() &&
2366 layout.current_line_segment.is_phantom(),
2367 },
2368 ));
2369 },
2370 InlineItem::OutOfFlowFloatBox(float_box) => {
2371 float_box.borrow().layout_into_line_items(&mut layout);
2372 },
2373 InlineItem::BlockLevel(block_level) => {
2374 block_level.borrow().layout_into_line_items(&mut layout);
2375 },
2376 }
2377 }
2378
2379 layout.finish_last_line();
2380 let (content_block_size, collapsible_margins_in_children, baselines) =
2381 layout.placement_state.finish();
2382
2383 IndependentFormattingContextLayoutResult {
2384 fragments: layout.fragments,
2385 content_block_size,
2386 collapsible_margins_in_children,
2387 baselines,
2388 depends_on_block_constraints: layout.depends_on_block_constraints,
2389 content_inline_size_for_table: None,
2390 specific_layout_info: None,
2391 }
2392 }
2393
2394 pub(crate) fn subtree_size(&self) -> usize {
2395 self.inline_items
2396 .iter()
2397 .map(|item| match item {
2398 InlineItem::StartInlineBox(..) => 1,
2399 InlineItem::EndInlineBox(..) => 0,
2400 InlineItem::TextRun(..) => 1,
2401 InlineItem::OutOfFlowAbsolutelyPositionedBox(absolutely_positioned_box, _) => {
2402 absolutely_positioned_box
2403 .borrow()
2404 .context
2405 .base
2406 .subtree_size()
2407 },
2408 InlineItem::OutOfFlowFloatBox(..) => 1,
2409 InlineItem::Atomic(..) => 1,
2410 InlineItem::BlockLevel(block_level_box) => block_level_box.borrow().subtree_size(),
2411 })
2412 .sum()
2413 }
2414
2415 fn next_character_prevents_soft_wrap_opportunity(&self, index: Utf8CodeUnits) -> bool {
2416 let Some(second_character) = self.text_content()[usize::from(index)..].chars().nth(1)
2417 else {
2418 return false;
2419 };
2420 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(second_character)
2421 }
2422
2423 fn previous_character_prevents_soft_wrap_opportunity(&self, index: Utf8CodeUnits) -> bool {
2424 let Some(character) = self.text_content()[..usize::from(index)]
2425 .chars()
2426 .next_back()
2427 else {
2428 return false;
2429 };
2430 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2431 }
2432
2433 pub(crate) fn find_block_margin_collapsing_with_parent(
2434 &self,
2435 layout_context: &LayoutContext,
2436 collected_margin: &mut CollapsedMargin,
2437 containing_block_for_children: &ContainingBlock,
2438 ) -> bool {
2439 let mut items_iter = self.inline_items.iter();
2445 items_iter.all(|inline_item| match inline_item {
2446 InlineItem::StartInlineBox(inline_box) => {
2447 let pbm = inline_box
2448 .borrow()
2449 .layout_style()
2450 .padding_border_margin(containing_block_for_children);
2451 pbm.padding.inline_start.is_zero() &&
2452 pbm.border.inline_start.is_zero() &&
2453 pbm.margin.inline_start.auto_is(Au::zero).is_zero()
2454 },
2455 InlineItem::EndInlineBox(inline_box) => {
2456 let pbm = inline_box
2457 .borrow()
2458 .layout_style()
2459 .padding_border_margin(containing_block_for_children);
2460 pbm.padding.inline_end.is_zero() &&
2461 pbm.border.inline_end.is_zero() &&
2462 pbm.margin.inline_end.auto_is(Au::zero).is_zero()
2463 },
2464 InlineItem::TextRun(text_run) => {
2465 let text_run = &*text_run.borrow();
2466 text_run.items.iter().all(|item| match item {
2467 TextRunItem::LineBreak { .. } => false,
2468 TextRunItem::Tab { .. } => false,
2469 TextRunItem::TextSegment(segment) => {
2470 segment.runs.iter().all(|run| run.all_removable())
2471 },
2472 })
2473 },
2474 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => true,
2475 InlineItem::OutOfFlowFloatBox(..) => true,
2476 InlineItem::Atomic(..) => false,
2477 InlineItem::BlockLevel(block_level) => block_level
2478 .borrow()
2479 .find_block_margin_collapsing_with_parent(
2480 layout_context,
2481 collected_margin,
2482 containing_block_for_children,
2483 ),
2484 })
2485 }
2486
2487 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
2488 let mut parent_box_stack = Vec::new();
2489 let current_parent_box = |parent_box_stack: &[WeakLayoutBox]| {
2490 parent_box_stack.last().unwrap_or(&layout_box).clone()
2491 };
2492 for inline_item in &self.inline_items {
2493 match inline_item {
2494 InlineItem::StartInlineBox(inline_box) => {
2495 inline_box
2496 .borrow_mut()
2497 .base
2498 .parent_box
2499 .replace(current_parent_box(&parent_box_stack));
2500 parent_box_stack.push(WeakLayoutBox::InlineLevel(
2501 WeakInlineItem::StartInlineBox(inline_box.downgrade()),
2502 ));
2503 },
2504 InlineItem::EndInlineBox(..) => {
2505 parent_box_stack.pop();
2506 },
2507 InlineItem::TextRun(text_run) => {
2508 text_run
2509 .borrow_mut()
2510 .parent_box
2511 .replace(current_parent_box(&parent_box_stack));
2512 },
2513 _ => inline_item.with_base_mut(|base| {
2514 base.parent_box
2515 .replace(current_parent_box(&parent_box_stack));
2516 }),
2517 }
2518 }
2519 }
2520
2521 pub(crate) fn next_tab_stop_after_inline_advance(
2522 &self,
2523 style: &ServoArc<ComputedValues>,
2524 current_inline_advance: Au,
2525 ) -> Au {
2526 let Some(font) = self.default_font.as_ref() else {
2527 return Au::zero();
2528 };
2529
2530 let tab_size_multiplier = *self.tab_size_multiplier.get_or_init(|| {
2531 let root_style = self.shared_inline_styles.style.borrow();
2532 let inherited_text_style = root_style.get_inherited_text();
2533 let font_size = root_style.get_font().font_size.computed_size().into();
2534 let letter_spacing = inherited_text_style
2535 .letter_spacing
2536 .0
2537 .to_used_value(font_size);
2538 let word_spacing = inherited_text_style.word_spacing.to_used_value(font_size);
2539
2540 font.metrics().space_advance + word_spacing + letter_spacing
2543 });
2544
2545 let tab_stop_advance = match style.get_inherited_text().tab_size {
2546 style::values::generics::length::LengthOrNumber::Number(number_of_spaces) => {
2547 tab_size_multiplier.scale_by(number_of_spaces.0)
2548 },
2549 style::values::generics::length::LengthOrNumber::Length(length) => length.into(),
2551 };
2552
2553 if tab_stop_advance.is_zero() {
2554 return Au::zero();
2555 }
2556
2557 let half_ch_advance = font
2563 .metrics()
2564 .zero_horizontal_advance
2565 .unwrap_or(font.metrics().em_size.scale_by(0.5))
2566 .scale_by(0.5);
2567 let number_of_tab_stops =
2568 (current_inline_advance + half_ch_advance).to_f32_px() / tab_stop_advance.to_f32_px();
2569 let number_of_tab_stops = number_of_tab_stops.ceil();
2570 tab_stop_advance.scale_by(number_of_tab_stops) - current_inline_advance
2571 }
2572
2573 pub(crate) fn non_pseudo_text_runs<'a>(
2574 &'a self,
2575 ) -> impl DoubleEndedIterator<Item = AtomicRef<'a, TextRun>> {
2576 self.inline_items
2577 .iter()
2578 .filter_map(|item| match item {
2579 InlineItem::TextRun(text_run) => Some(text_run.borrow()),
2580 _ => None,
2581 })
2582 .filter(|text_run| {
2583 text_run
2584 .base_fragment_info
2585 .tag
2586 .is_some_and(|tag| tag.pseudo_element_chain.is_empty())
2587 })
2588 }
2589}
2590
2591impl InlineContainerState {
2592 fn new(
2593 style: ServoArc<ComputedValues>,
2594 flags: InlineContainerStateFlags,
2595 parent_container: Option<&InlineContainerState>,
2596 default_font: Option<FontRef>,
2597 ) -> Self {
2598 let font_metrics = default_font
2599 .as_ref()
2600 .map(|font| font.metrics().clone())
2601 .unwrap_or_else(FontMetrics::empty);
2602 let mut baseline_offset = Au::zero();
2603 let mut strut_block_sizes = {
2604 Self::get_block_sizes_with_style(
2605 &effective_baseline_shift(&style, parent_container),
2606 &style,
2607 &font_metrics,
2608 &font_metrics,
2609 &flags,
2610 )
2611 };
2612
2613 if let Some(parent_container) = parent_container {
2614 baseline_offset = parent_container.get_cumulative_baseline_offset_for_child(
2617 style.get_alignment_baseline(),
2618 style.get_baseline_shift(),
2619 &strut_block_sizes,
2620 );
2621 strut_block_sizes.adjust_for_baseline_offset(baseline_offset);
2622 }
2623
2624 let mut nested_block_sizes = parent_container
2625 .map(|container| container.nested_strut_block_sizes.clone())
2626 .unwrap_or_else(LineBlockSizes::zero);
2627 if flags.contains(InlineContainerStateFlags::CREATE_STRUT) {
2628 nested_block_sizes.max_assign(&strut_block_sizes);
2629 }
2630
2631 Self {
2632 style,
2633 flags,
2634 has_content: Cell::new(false),
2635 nested_strut_block_sizes: nested_block_sizes,
2636 strut_block_sizes,
2637 baseline_offset,
2638 default_font,
2639 font_metrics,
2640 }
2641 }
2642
2643 fn get_block_sizes_with_style(
2644 baseline_shift: &BaselineShift,
2645 style: &ComputedValues,
2646 font_metrics: &FontMetrics,
2647 font_metrics_of_first_font: &FontMetrics,
2648 flags: &InlineContainerStateFlags,
2649 ) -> LineBlockSizes {
2650 let line_height = line_height(style, font_metrics, flags);
2651
2652 if !is_baseline_relative(baseline_shift) {
2653 return LineBlockSizes {
2654 line_height,
2655 baseline_relative_size_for_line_height: None,
2656 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2657 };
2658 }
2659
2660 let mut ascent = font_metrics.ascent;
2669 let mut descent = font_metrics.descent;
2670 if style.get_font().line_height == LineHeight::Normal {
2671 let half_leading_from_line_gap =
2672 (font_metrics.line_gap - descent - ascent).scale_by(0.5);
2673 ascent += half_leading_from_line_gap;
2674 descent += half_leading_from_line_gap;
2675 }
2676
2677 let size_for_baseline_positioning = BaselineRelativeSize { ascent, descent };
2681
2682 if style.get_font().line_height != LineHeight::Normal {
2698 ascent = font_metrics_of_first_font.ascent;
2699 descent = font_metrics_of_first_font.descent;
2700 let half_leading = (line_height - (ascent + descent)).scale_by(0.5);
2701 ascent += half_leading;
2706 descent = line_height - ascent;
2707 }
2708
2709 LineBlockSizes {
2710 line_height,
2711 baseline_relative_size_for_line_height: Some(BaselineRelativeSize { ascent, descent }),
2712 size_for_baseline_positioning,
2713 }
2714 }
2715
2716 fn get_block_size_contribution(
2717 &self,
2718 baseline_shift: &BaselineShift,
2719 font_metrics: &FontMetrics,
2720 font_metrics_of_first_font: &FontMetrics,
2721 ) -> LineBlockSizes {
2722 Self::get_block_sizes_with_style(
2723 baseline_shift,
2724 &self.style,
2725 font_metrics,
2726 font_metrics_of_first_font,
2727 &self.flags,
2728 )
2729 }
2730
2731 fn get_cumulative_baseline_offset_for_child(
2732 &self,
2733 child_alignment_baseline: &AlignmentBaseline,
2734 child_baseline_shift: &BaselineShift,
2735 child_block_size: &LineBlockSizes,
2736 ) -> Au {
2737 let block_size = self.get_block_size_contribution(
2738 child_baseline_shift,
2739 &self.font_metrics,
2740 &self.font_metrics,
2741 );
2742 self.baseline_offset +
2743 match child_alignment_baseline {
2744 AlignmentBaseline::Baseline => Au::zero(),
2745 AlignmentBaseline::TextTop => {
2746 child_block_size.size_for_baseline_positioning.ascent - self.font_metrics.ascent
2747 },
2748 AlignmentBaseline::Middle => {
2749 (child_block_size.size_for_baseline_positioning.ascent -
2752 child_block_size.size_for_baseline_positioning.descent -
2753 self.font_metrics.x_height)
2754 .scale_by(0.5)
2755 },
2756 AlignmentBaseline::TextBottom => {
2757 self.font_metrics.descent -
2758 child_block_size.size_for_baseline_positioning.descent
2759 },
2760 } +
2761 match child_baseline_shift {
2762 BaselineShift::Keyword(
2767 BaselineShiftKeyword::Top |
2768 BaselineShiftKeyword::Bottom |
2769 BaselineShiftKeyword::Center,
2770 ) => Au::zero(),
2771 BaselineShift::Keyword(BaselineShiftKeyword::Sub) => {
2772 block_size.resolve().scale_by(FONT_SUBSCRIPT_OFFSET_RATIO)
2773 },
2774 BaselineShift::Keyword(BaselineShiftKeyword::Super) => {
2775 -block_size.resolve().scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO)
2776 },
2777 BaselineShift::Length(length_percentage) => {
2778 -length_percentage.to_used_value(child_block_size.line_height)
2779 },
2780 }
2781 }
2782}
2783
2784impl IndependentFormattingContext {
2785 fn layout_into_line_items(
2786 &self,
2787 layout: &mut InlineFormattingContextLayout,
2788 offset_in_text: Utf8CodeUnits,
2789 bidi_level: Level,
2790 ) {
2791 let mut child_positioning_context = PositioningContext::default();
2793 let IndependentFloatOrAtomicLayoutResult {
2794 mut fragment,
2795 baselines,
2796 pbm_sums,
2797 } = self.layout_float_or_atomic_inline(
2798 layout.layout_context,
2799 &mut child_positioning_context,
2800 layout.containing_block(),
2801 );
2802
2803 layout.depends_on_block_constraints |= fragment.base.flags.contains(
2806 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2807 );
2808
2809 let container_writing_mode = layout.containing_block().style.writing_mode;
2811 let pbm_physical_offset = pbm_sums
2812 .start_offset()
2813 .to_physical_size(container_writing_mode);
2814 fragment.base.translate_rect(pbm_physical_offset);
2815
2816 fragment = fragment.with_baselines(baselines);
2818
2819 let positioning_context = if self.is_replaced() {
2822 None
2823 } else {
2824 if fragment
2825 .style()
2826 .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
2827 {
2828 child_positioning_context
2829 .layout_collected_children(layout.layout_context, &mut fragment);
2830 }
2831 Some(child_positioning_context)
2832 };
2833
2834 if layout.text_wrap_mode == TextWrapMode::Wrap &&
2835 !layout
2836 .ifc
2837 .previous_character_prevents_soft_wrap_opportunity(offset_in_text)
2838 {
2839 layout.process_soft_wrap_opportunity();
2840 }
2841
2842 let size = pbm_sums.sum() + fragment.base.rect().size.to_logical(container_writing_mode);
2843 let baseline_offset = self
2844 .pick_baseline(&fragment.baselines(container_writing_mode))
2845 .map(|baseline| pbm_sums.block_start + baseline)
2846 .unwrap_or(size.block);
2847
2848 let (block_sizes, baseline_offset_in_parent) =
2849 self.get_block_sizes_and_baseline_offset(layout, size.block, baseline_offset);
2850
2851 layout.update_unbreakable_segment_for_new_content(
2852 &block_sizes,
2853 size.inline,
2854 Au::zero(),
2855 Au::zero(),
2856 SegmentContentFlags::Contentful | SegmentContentFlags::IncorporateTrailingWhiteSpace,
2857 );
2858
2859 let fragment = Arc::new(fragment);
2860 self.base.set_fragment(Fragment::Box(fragment.clone()));
2861
2862 layout.push_line_item_to_unbreakable_segment(LineItem::Atomic(
2863 layout.current_inline_box_identifier(),
2864 AtomicLineItem {
2865 fragment,
2866 size,
2867 positioning_context,
2868 baseline_offset_in_parent,
2869 baseline_offset_in_item: baseline_offset,
2870 bidi_level,
2871 },
2872 ));
2873
2874 if !layout
2877 .ifc
2878 .next_character_prevents_soft_wrap_opportunity(offset_in_text)
2879 {
2880 layout.have_deferred_soft_wrap_opportunity = true;
2881 }
2882 }
2883
2884 fn pick_baseline(&self, baselines: &Baselines) -> Option<Au> {
2888 match self.style().get_baseline_source() {
2889 BaselineSource::First => baselines.first,
2890 BaselineSource::Last => baselines.last,
2891 BaselineSource::Auto if self.is_block_container() => baselines.last,
2892 BaselineSource::Auto => baselines.first,
2893 }
2894 }
2895
2896 fn get_block_sizes_and_baseline_offset(
2897 &self,
2898 ifc: &InlineFormattingContextLayout,
2899 block_size: Au,
2900 baseline_offset_in_content_area: Au,
2901 ) -> (LineBlockSizes, Au) {
2902 let mut contribution = if !is_baseline_relative(self.style().get_baseline_shift()) {
2903 LineBlockSizes {
2904 line_height: block_size,
2905 baseline_relative_size_for_line_height: None,
2906 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2907 }
2908 } else {
2909 let baseline_relative_size = BaselineRelativeSize {
2910 ascent: baseline_offset_in_content_area,
2911 descent: block_size - baseline_offset_in_content_area,
2912 };
2913 LineBlockSizes {
2914 line_height: block_size,
2915 baseline_relative_size_for_line_height: Some(baseline_relative_size.clone()),
2916 size_for_baseline_positioning: baseline_relative_size,
2917 }
2918 };
2919
2920 let style = self.style();
2921 let baseline_offset = ifc
2922 .current_inline_container_state()
2923 .get_cumulative_baseline_offset_for_child(
2924 style.get_alignment_baseline(),
2925 style.get_baseline_shift(),
2926 &contribution,
2927 );
2928 contribution.adjust_for_baseline_offset(baseline_offset);
2929
2930 (contribution, baseline_offset)
2931 }
2932}
2933
2934impl FloatBox {
2935 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
2936 let old_len = layout.positioning_context.len();
2937 let fragment = Arc::new(self.layout(
2938 layout.layout_context,
2939 layout.positioning_context,
2940 layout.placement_state.containing_block,
2941 ));
2942 let new_len = layout.positioning_context.len();
2943
2944 self.contents
2945 .base
2946 .set_fragment(Fragment::Box(fragment.clone()));
2947 layout.push_line_item_to_unbreakable_segment(LineItem::Float(
2948 layout.current_inline_box_identifier(),
2949 FloatLineItem {
2950 fragment,
2951 needs_placement: true,
2952 range: old_len..new_len,
2953 },
2954 ));
2955 }
2956}
2957
2958fn place_pending_floats(ifc: &mut InlineFormattingContextLayout, line_items: &[LineItem]) {
2959 for item in line_items.iter() {
2960 if let LineItem::Float(_, float_line_item) = item &&
2961 float_line_item.needs_placement
2962 {
2963 ifc.place_float_fragment(float_line_item);
2964 }
2965 }
2966}
2967
2968fn line_height(
2969 parent_style: &ComputedValues,
2970 font_metrics: &FontMetrics,
2971 flags: &InlineContainerStateFlags,
2972) -> Au {
2973 let font = parent_style.get_font();
2974 let font_size = font.font_size.computed_size();
2975 let mut line_height = match font.line_height {
2976 LineHeight::Normal => font_metrics.line_gap,
2977 LineHeight::Number(number) => (font_size * number.0).into(),
2978 LineHeight::Length(length) => length.0.into(),
2979 };
2980
2981 if flags.contains(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT) {
2985 line_height.max_assign(font_metrics.line_gap);
2986 }
2987
2988 line_height
2989}
2990
2991fn effective_baseline_shift(
2992 style: &ComputedValues,
2993 container: Option<&InlineContainerState>,
2994) -> BaselineShift {
2995 if container.is_none() {
2996 BaselineShift::zero()
3000 } else {
3001 style.slow_clone_baseline_shift()
3002 }
3003}
3004
3005fn is_baseline_relative(baseline_shift: &BaselineShift) -> bool {
3006 !matches!(
3007 baseline_shift,
3008 BaselineShift::Keyword(
3009 BaselineShiftKeyword::Top | BaselineShiftKeyword::Bottom | BaselineShiftKeyword::Center
3010 )
3011 )
3012}
3013
3014fn inline_container_needs_strut(
3040 style: &ComputedValues,
3041 layout_context: &LayoutContext,
3042 pbm: Option<&PaddingBorderMargin>,
3043) -> bool {
3044 if layout_context.style_context.quirks_mode() == QuirksMode::NoQuirks {
3045 return true;
3046 }
3047
3048 if style.get_box().display.is_list_item() {
3051 return true;
3052 }
3053
3054 pbm.is_some_and(|pbm| !pbm.padding_border_sums.inline.is_zero())
3055}
3056
3057impl ComputeInlineContentSizes for InlineFormattingContext {
3058 fn compute_inline_content_sizes(
3062 &self,
3063 layout_context: &LayoutContext,
3064 constraint_space: &ConstraintSpace,
3065 ) -> InlineContentSizesResult {
3066 ContentSizesComputation::compute(self, layout_context, constraint_space)
3067 }
3068}
3069
3070struct ContentSizesComputation<'layout_data> {
3072 layout_context: &'layout_data LayoutContext<'layout_data>,
3073 constraint_space: &'layout_data ConstraintSpace<'layout_data>,
3074 paragraph: ContentSizes,
3075 current_line: ContentSizes,
3076 pending_whitespace: ContentSizes,
3078 pending_opening_pbm_for_inline_boxes: Au,
3081 uncleared_floats: LogicalSides1D<ContentSizes>,
3083 cleared_floats: LogicalSides1D<ContentSizes>,
3085 had_content_yet_for_min_content: bool,
3088 had_content_yet_for_max_content: bool,
3091 ending_inline_pbm_stack: Vec<Au>,
3094 text_wrap_mode_stack: Vec<TextWrapMode>,
3096 depends_on_block_constraints: bool,
3098}
3099
3100impl<'layout_data> ContentSizesComputation<'layout_data> {
3101 fn traverse(
3102 mut self,
3103 inline_formatting_context: &InlineFormattingContext,
3104 ) -> InlineContentSizesResult {
3105 self.add_inline_size(
3106 inline_formatting_context.inline_start_for_first_line(self.constraint_space.into()),
3107 );
3108 for inline_item in &inline_formatting_context.inline_items {
3109 self.process_item(inline_item, inline_formatting_context);
3110 }
3111 self.forced_line_break();
3112 self.flush_floats();
3113
3114 InlineContentSizesResult {
3115 sizes: self.paragraph,
3116 depends_on_block_constraints: self.depends_on_block_constraints,
3117 }
3118 }
3119
3120 fn process_item(
3121 &mut self,
3122 inline_item: &InlineItem,
3123 inline_formatting_context: &InlineFormattingContext,
3124 ) {
3125 match inline_item {
3126 InlineItem::StartInlineBox(inline_box) => {
3127 let inline_box = inline_box.borrow();
3131 let zero = Au::zero();
3132 let writing_mode = self.constraint_space.style.writing_mode;
3133 let layout_style = inline_box.layout_style();
3134 let padding = layout_style
3135 .padding(writing_mode)
3136 .percentages_relative_to(zero);
3137 let border = layout_style.border_width(writing_mode);
3138 let margin = inline_box
3139 .base
3140 .style
3141 .margin(writing_mode)
3142 .percentages_relative_to(zero)
3143 .auto_is(Au::zero);
3144
3145 let pbm = margin + padding + border;
3146 self.pending_opening_pbm_for_inline_boxes += pbm.inline_start;
3147 self.current_line.max_content += pbm.inline_start;
3148 self.ending_inline_pbm_stack.push(pbm.inline_end);
3149
3150 self.text_wrap_mode_stack
3151 .push(layout_style.style().slow_clone_text_wrap_mode());
3152 },
3153 InlineItem::EndInlineBox(..) => {
3154 let length = self.ending_inline_pbm_stack.pop().unwrap_or_else(Au::zero);
3155 self.commit_pending_opening_pbm_for_inline_boxes();
3156 self.add_inline_size(length);
3157 self.text_wrap_mode_stack.pop();
3158 },
3159 InlineItem::TextRun(text_run) => {
3160 let text_run = &*text_run.borrow();
3161 let parent_style = text_run.inline_styles().style.borrow();
3162 for item in text_run.items.iter() {
3163 match item {
3164 TextRunItem::LineBreak { .. } => {
3165 self.forced_line_break();
3168 },
3169 TextRunItem::Tab { break_at_start, .. } => self.process_preserved_tab(
3170 &parent_style,
3171 inline_formatting_context,
3172 *break_at_start,
3173 ),
3174 TextRunItem::TextSegment(segment) => {
3175 self.process_text_segment(&parent_style, segment)
3176 },
3177 }
3178 }
3179 },
3180 InlineItem::Atomic(atomic, offset_in_text, _level) => {
3181 let can_wrap = self.text_wrap_mode() == &TextWrapMode::Wrap;
3182 if can_wrap &&
3183 self.had_content_yet_for_min_content &&
3184 !inline_formatting_context
3185 .previous_character_prevents_soft_wrap_opportunity(*offset_in_text)
3186 {
3187 self.line_break_opportunity();
3188 }
3189
3190 self.commit_pending_opening_pbm_for_inline_boxes();
3191 self.commit_pending_whitespace();
3192 let outer = self.outer_inline_content_sizes_of_float_or_atomic(&atomic.borrow());
3193 self.current_line += outer;
3194
3195 if can_wrap &&
3196 !inline_formatting_context
3197 .next_character_prevents_soft_wrap_opportunity(*offset_in_text)
3198 {
3199 self.line_break_opportunity();
3200 }
3201 },
3202 InlineItem::OutOfFlowFloatBox(float_box) => {
3203 let float_box = float_box.borrow();
3204 let sizes = self.outer_inline_content_sizes_of_float_or_atomic(&float_box.contents);
3205 let style = &float_box.contents.style();
3206 let container_writing_mode = self.constraint_space.style.writing_mode;
3207 let clear =
3208 Clear::from_style_and_container_writing_mode(style, container_writing_mode);
3209 self.clear_floats(clear);
3210 let float_side =
3211 FloatSide::from_style_and_container_writing_mode(style, container_writing_mode);
3212 match float_side.expect("A float box needs to float to some side") {
3213 FloatSide::InlineStart => self.uncleared_floats.start.union_assign(&sizes),
3214 FloatSide::InlineEnd => self.uncleared_floats.end.union_assign(&sizes),
3215 }
3216 },
3217 InlineItem::BlockLevel(block_level) => {
3218 self.forced_line_break();
3219 self.flush_floats();
3220 let inline_content_sizes_result =
3221 compute_inline_content_sizes_for_block_level_boxes(
3222 std::slice::from_ref(block_level),
3223 self.layout_context,
3224 &self.constraint_space.into(),
3225 );
3226 self.depends_on_block_constraints |=
3227 inline_content_sizes_result.depends_on_block_constraints;
3228 self.current_line = inline_content_sizes_result.sizes;
3229 self.forced_line_break();
3230 },
3231 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => {},
3232 }
3233 }
3234
3235 fn process_text_segment(
3236 &mut self,
3237 parent_style: &AtomicRef<'_, ServoArc<ComputedValues>>,
3238 segment: &TextRunSegment,
3239 ) {
3240 let style_text = parent_style.get_inherited_text();
3241 let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap;
3242
3243 let break_at_start = segment.break_at_start && self.had_content_yet_for_min_content;
3246
3247 let hangable_characters_count_for_max_content =
3262 parent_style.white_space_may_hang_conditionally();
3263
3264 for (run_index, run) in segment.runs.iter().enumerate() {
3265 if can_wrap && (run_index != 0 || break_at_start) {
3268 self.line_break_opportunity();
3269 }
3270
3271 let advance = run.total_advance();
3272 let removable_advance = run.removable_advance();
3273 let hangable_advance = run.hangable_advance();
3274 if run.has_non_hangable_non_removable_content() {
3275 self.commit_pending_opening_pbm_for_inline_boxes();
3276 self.commit_pending_whitespace();
3277 self.add_inline_size(advance - removable_advance - hangable_advance);
3278 }
3279
3280 self.pending_whitespace.min_content += hangable_advance;
3281 if hangable_characters_count_for_max_content {
3282 self.current_line.max_content += hangable_advance;
3283 } else {
3284 self.pending_whitespace.max_content += hangable_advance;
3285 }
3286
3287 if self.had_content_yet_for_min_content {
3288 self.pending_whitespace.min_content += removable_advance;
3289 }
3290 if self.had_content_yet_for_max_content {
3291 self.pending_whitespace.max_content += removable_advance;
3292 }
3293 }
3294 }
3295
3296 fn process_preserved_tab(
3297 &mut self,
3298 parent_style: &AtomicRef<'_, ServoArc<ComputedValues>>,
3299 inline_formatting_context: &InlineFormattingContext,
3300 break_at_start: bool,
3301 ) {
3302 let can_wrap = parent_style.get_inherited_text().text_wrap_mode == TextWrapMode::Wrap;
3303 if break_at_start && can_wrap {
3304 self.line_break_opportunity();
3305 }
3306
3307 self.commit_pending_opening_pbm_for_inline_boxes();
3308
3309 let conditionally_hangs = parent_style.white_space_may_hang_conditionally();
3310 if !conditionally_hangs {
3311 self.commit_pending_whitespace();
3312 }
3313
3314 let next_tab_stop_for_min_content = inline_formatting_context
3315 .next_tab_stop_after_inline_advance(parent_style, self.current_line.min_content);
3316 let next_tab_stop_for_max_content = inline_formatting_context
3317 .next_tab_stop_after_inline_advance(parent_style, self.current_line.max_content);
3318
3319 if conditionally_hangs {
3320 self.pending_whitespace.min_content += next_tab_stop_for_min_content;
3321 } else {
3322 self.current_line.min_content += next_tab_stop_for_min_content;
3323 }
3324 self.current_line.max_content += next_tab_stop_for_max_content;
3325
3326 if can_wrap {
3327 self.line_break_opportunity();
3328 }
3329 }
3330
3331 fn add_inline_size(&mut self, size: Au) {
3332 self.current_line.min_content += size;
3333 self.current_line.max_content += size;
3334 }
3335
3336 fn line_break_opportunity(&mut self) {
3337 self.pending_whitespace.min_content = Au::zero();
3341 let current_min_content = mem::take(&mut self.current_line.min_content);
3342 self.paragraph.min_content.max_assign(current_min_content);
3343 self.had_content_yet_for_min_content = false;
3344 }
3345
3346 fn forced_line_break(&mut self) {
3347 self.commit_pending_opening_pbm_for_inline_boxes();
3350
3351 self.line_break_opportunity();
3353
3354 self.pending_whitespace.max_content = Au::zero();
3356 let current_max_content = mem::take(&mut self.current_line.max_content);
3357 self.paragraph.max_content.max_assign(current_max_content);
3358 self.had_content_yet_for_max_content = false;
3359 }
3360
3361 fn commit_pending_whitespace(&mut self) {
3362 self.current_line += mem::take(&mut self.pending_whitespace);
3363 self.had_content_yet_for_min_content = true;
3364 self.had_content_yet_for_max_content = true;
3365 }
3366
3367 fn commit_pending_opening_pbm_for_inline_boxes(&mut self) {
3368 self.current_line.min_content +=
3369 std::mem::take(&mut self.pending_opening_pbm_for_inline_boxes);
3370 }
3371
3372 fn outer_inline_content_sizes_of_float_or_atomic(
3373 &mut self,
3374 context: &IndependentFormattingContext,
3375 ) -> ContentSizes {
3376 let result = context.outer_inline_content_sizes(
3377 self.layout_context,
3378 &self.constraint_space.into(),
3379 &LogicalVec2::zero(),
3380 false, skippable_for_block_percentage_quirk(self.layout_context, &context.base, Some(context)),
3382 );
3383 self.depends_on_block_constraints |= result.depends_on_block_constraints;
3384 result.sizes
3385 }
3386
3387 fn clear_floats(&mut self, clear: Clear) {
3388 match clear {
3389 Clear::InlineStart => {
3390 let start_floats = mem::take(&mut self.uncleared_floats.start);
3391 self.cleared_floats.start.max_assign(start_floats);
3392 },
3393 Clear::InlineEnd => {
3394 let end_floats = mem::take(&mut self.uncleared_floats.end);
3395 self.cleared_floats.end.max_assign(end_floats);
3396 },
3397 Clear::Both => {
3398 let start_floats = mem::take(&mut self.uncleared_floats.start);
3399 let end_floats = mem::take(&mut self.uncleared_floats.end);
3400 self.cleared_floats.start.max_assign(start_floats);
3401 self.cleared_floats.end.max_assign(end_floats);
3402 },
3403 Clear::None => {},
3404 }
3405 }
3406
3407 fn flush_floats(&mut self) {
3408 self.clear_floats(Clear::Both);
3409 let start_floats = mem::take(&mut self.cleared_floats.start);
3410 let end_floats = mem::take(&mut self.cleared_floats.end);
3411 self.paragraph.union_assign(&start_floats);
3412 self.paragraph.union_assign(&end_floats);
3413 }
3414
3415 fn text_wrap_mode(&self) -> &TextWrapMode {
3416 self.text_wrap_mode_stack
3417 .last()
3418 .unwrap_or_else(|| self.constraint_space.style.get_text_wrap_mode())
3419 }
3420
3421 fn compute(
3423 inline_formatting_context: &InlineFormattingContext,
3424 layout_context: &'layout_data LayoutContext,
3425 constraint_space: &'layout_data ConstraintSpace,
3426 ) -> InlineContentSizesResult {
3427 Self {
3428 layout_context,
3429 constraint_space,
3430 paragraph: ContentSizes::zero(),
3431 current_line: ContentSizes::zero(),
3432 pending_whitespace: ContentSizes::zero(),
3433 pending_opening_pbm_for_inline_boxes: Au::zero(),
3434 uncleared_floats: LogicalSides1D::default(),
3435 cleared_floats: LogicalSides1D::default(),
3436 had_content_yet_for_min_content: false,
3437 had_content_yet_for_max_content: false,
3438 ending_inline_pbm_stack: Vec::new(),
3439 text_wrap_mode_stack: Vec::new(),
3440 depends_on_block_constraints: false,
3441 }
3442 .traverse(inline_formatting_context)
3443 }
3444}
3445
3446pub(crate) struct BidiLevels<'a> {
3447 info: Option<BidiInfo<'a>>,
3448}
3449
3450impl BidiLevels<'_> {
3451 fn level(&self, byte_offset_in_ifc_text: usize) -> Level {
3452 self.info
3453 .as_ref()
3454 .map_or_else(Level::ltr, |info| info.levels[byte_offset_in_ifc_text])
3455 }
3456}
3457
3458fn char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character: char) -> bool {
3470 if character == '\u{00A0}' {
3471 return false;
3472 }
3473 matches!(
3474 ICULineBreak::for_char(character),
3475 ICULineBreak::Glue | ICULineBreak::WordJoiner | ICULineBreak::ZWJ
3476 )
3477}