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};
137use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
138use crate::fragment_tree::{CollapsedMargin, Fragment, FragmentFlags, PositioningFragment};
139use crate::geom::{LogicalRect, LogicalSides1D, LogicalVec2, ToLogical};
140use crate::layout_box_base::LayoutBoxBase;
141use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
142use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
143use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
144use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SharedStyle};
145
146static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
148static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
149
150#[derive(Debug, MallocSizeOf)]
151pub(crate) struct InlineFormattingContext {
152 inline_items: Vec<InlineItem>,
157
158 inline_boxes: InlineBoxes,
161
162 #[conditional_malloc_size_of]
164 text_content: Arc<OnceLock<String>>,
165
166 shared_inline_styles: SharedInlineStyles,
169
170 default_font: Option<FontRef>,
174
175 has_first_formatted_line: bool,
178
179 pub(super) contains_floats: bool,
181
182 is_single_line_text_input: bool,
185
186 has_right_to_left_content: bool,
189
190 tab_size_multiplier: OnceLock<Au>,
195}
196
197#[derive(Clone, Debug, MallocSizeOf)]
202pub(crate) struct SharedInlineStyles {
203 pub style: SharedStyle,
204 pub selected: SharedStyle,
205}
206
207impl SharedInlineStyles {
208 pub(crate) fn ptr_eq(&self, other: &Self) -> bool {
209 self.style.ptr_eq(&other.style) && self.selected.ptr_eq(&other.selected)
210 }
211
212 pub(crate) fn from_info_and_context(info: &NodeAndStyleInfo, context: &LayoutContext) -> Self {
213 Self {
214 style: SharedStyle::new(info.style.clone()),
215 selected: SharedStyle::new(info.node.selected_style(&context.style_context)),
216 }
217 }
218}
219
220impl BlockLevelBox {
221 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
222 layout.process_soft_wrap_opportunity();
223 layout.commit_current_segment_to_line();
224 layout.process_line_break(
225 true, true, );
228
229 let fragment = layout_block_level_child(
230 layout.layout_context,
231 layout.positioning_context,
232 self,
233 layout.sequential_layout_state.as_deref_mut(),
234 &mut layout.placement_state,
235 layout.ignore_block_margins_for_stretch,
236 true, );
238
239 let Some(fragment) = fragment.retrieve_box_fragment() else {
240 unreachable!("The fragment should be a Fragment::Box()");
241 };
242
243 layout.depends_on_block_constraints |= fragment.base.flags.contains(
246 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
247 );
248
249 layout.push_line_item_to_unbreakable_segment(LineItem::BlockLevel(
250 layout.current_inline_box_identifier(),
251 fragment.clone(),
252 ));
253
254 layout.commit_current_segment_to_line();
255 layout.process_line_break(
256 true, false, );
259 }
260}
261
262#[derive(Clone, Debug, MallocSizeOf)]
263pub(crate) enum InlineItem {
264 StartInlineBox(ArcRefCell<InlineBox>),
265 EndInlineBox(ArcRefCell<InlineBox>),
266 TextRun(ArcRefCell<TextRun>),
267 OutOfFlowAbsolutelyPositionedBox(
268 ArcRefCell<AbsolutelyPositionedBox>,
269 Utf8CodeUnits, ),
271 OutOfFlowFloatBox(ArcRefCell<FloatBox>),
272 Atomic(
273 ArcRefCell<IndependentFormattingContext>,
274 Utf8CodeUnits, Level, ),
277 BlockLevel(ArcRefCell<BlockLevelBox>),
278}
279
280impl InlineItem {
281 pub(crate) fn repair_style(
282 &self,
283 context: &SharedStyleContext,
284 node: &ServoLayoutNode,
285 new_style: &ServoArc<ComputedValues>,
286 ) {
287 match self {
288 InlineItem::StartInlineBox(inline_box) => {
289 inline_box
290 .borrow_mut()
291 .repair_style(context, node, new_style);
292 },
293 InlineItem::EndInlineBox(..) => {},
294 InlineItem::TextRun(..) => {},
297 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box
298 .borrow_mut()
299 .context
300 .repair_style(context, node, new_style),
301 InlineItem::OutOfFlowFloatBox(float_box) => float_box
302 .borrow_mut()
303 .contents
304 .repair_style(context, node, new_style),
305 InlineItem::Atomic(atomic, ..) => {
306 atomic.borrow_mut().repair_style(context, node, new_style)
307 },
308 InlineItem::BlockLevel(block_level) => block_level
309 .borrow_mut()
310 .repair_style(context, node, new_style),
311 }
312 }
313
314 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
315 match self {
316 InlineItem::StartInlineBox(inline_box) => callback(&inline_box.borrow().base),
317 InlineItem::EndInlineBox(..) | InlineItem::TextRun(..) => {
318 unreachable!("Should never have these kind of fragments attached to a DOM node")
319 },
320 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
321 callback(&positioned_box.borrow().context.base)
322 },
323 InlineItem::OutOfFlowFloatBox(float_box) => callback(&float_box.borrow().contents.base),
324 InlineItem::Atomic(independent_formatting_context, ..) => {
325 callback(&independent_formatting_context.borrow().base)
326 },
327 InlineItem::BlockLevel(block_level) => block_level.borrow().with_base(callback),
328 }
329 }
330
331 pub(crate) fn with_base_mut<T>(&self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
332 match self {
333 InlineItem::StartInlineBox(inline_box) => callback(&mut inline_box.borrow_mut().base),
334 InlineItem::EndInlineBox(..) | InlineItem::TextRun(..) => {
335 unreachable!("Should never have these kind of fragments attached to a DOM node")
336 },
337 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
338 callback(&mut positioned_box.borrow_mut().context.base)
339 },
340 InlineItem::OutOfFlowFloatBox(float_box) => {
341 callback(&mut float_box.borrow_mut().contents.base)
342 },
343 InlineItem::Atomic(independent_formatting_context, ..) => {
344 callback(&mut independent_formatting_context.borrow_mut().base)
345 },
346 InlineItem::BlockLevel(block_level) => block_level.borrow_mut().with_base_mut(callback),
347 }
348 }
349
350 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
351 match self {
352 Self::StartInlineBox(_) | InlineItem::EndInlineBox(..) => {
353 },
356 Self::TextRun(_) => {
357 },
359 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
360 positioned_box.borrow().context.attached_to_tree(layout_box)
361 },
362 Self::OutOfFlowFloatBox(float_box) => {
363 float_box.borrow().contents.attached_to_tree(layout_box)
364 },
365 Self::Atomic(atomic, ..) => atomic.borrow().attached_to_tree(layout_box),
366 Self::BlockLevel(block_level) => block_level.borrow().attached_to_tree(layout_box),
367 }
368 }
369
370 pub(crate) fn downgrade(&self) -> WeakInlineItem {
371 match self {
372 Self::StartInlineBox(inline_box) => {
373 WeakInlineItem::StartInlineBox(inline_box.downgrade())
374 },
375 Self::EndInlineBox(inline_box) => WeakInlineItem::EndInlineBox(inline_box.downgrade()),
376 Self::TextRun(text_run) => WeakInlineItem::TextRun(text_run.downgrade()),
377 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
378 WeakInlineItem::OutOfFlowAbsolutelyPositionedBox(
379 positioned_box.downgrade(),
380 *offset_in_text,
381 )
382 },
383 Self::OutOfFlowFloatBox(float_box) => {
384 WeakInlineItem::OutOfFlowFloatBox(float_box.downgrade())
385 },
386 Self::Atomic(atomic, offset_in_text, bidi_level) => {
387 WeakInlineItem::Atomic(atomic.downgrade(), *offset_in_text, *bidi_level)
388 },
389 Self::BlockLevel(block_level) => WeakInlineItem::BlockLevel(block_level.downgrade()),
390 }
391 }
392}
393
394#[derive(Clone, Debug, MallocSizeOf)]
395pub(crate) enum WeakInlineItem {
396 StartInlineBox(WeakRefCell<InlineBox>),
397 EndInlineBox(WeakRefCell<InlineBox>),
398 TextRun(WeakRefCell<TextRun>),
399 OutOfFlowAbsolutelyPositionedBox(
400 WeakRefCell<AbsolutelyPositionedBox>,
401 Utf8CodeUnits, ),
403 OutOfFlowFloatBox(WeakRefCell<FloatBox>),
404 Atomic(
405 WeakRefCell<IndependentFormattingContext>,
406 Utf8CodeUnits, Level, ),
409 BlockLevel(WeakRefCell<BlockLevelBox>),
410}
411
412impl WeakInlineItem {
413 pub(crate) fn upgrade(&self) -> Option<InlineItem> {
414 Some(match self {
415 Self::StartInlineBox(inline_box) => InlineItem::StartInlineBox(inline_box.upgrade()?),
416 Self::EndInlineBox(inline_box) => InlineItem::EndInlineBox(inline_box.upgrade()?),
417 Self::TextRun(text_run) => InlineItem::TextRun(text_run.upgrade()?),
418 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
419 InlineItem::OutOfFlowAbsolutelyPositionedBox(
420 positioned_box.upgrade()?,
421 *offset_in_text,
422 )
423 },
424 Self::OutOfFlowFloatBox(float_box) => {
425 InlineItem::OutOfFlowFloatBox(float_box.upgrade()?)
426 },
427 Self::Atomic(atomic, offset_in_text, bidi_level) => {
428 InlineItem::Atomic(atomic.upgrade()?, *offset_in_text, *bidi_level)
429 },
430 Self::BlockLevel(block_level) => InlineItem::BlockLevel(block_level.upgrade()?),
431 })
432 }
433}
434
435struct LineUnderConstruction {
442 start_position: LogicalVec2<Au>,
445
446 inline_position: Au,
449
450 max_block_size: LineBlockSizes,
454
455 has_content: bool,
458
459 has_floats_waiting_to_be_placed: bool,
463
464 placement_among_floats: OnceCell<LogicalRect<Au>>,
469
470 line_items: Vec<LineItem>,
473
474 for_block_level: bool,
476
477 caret_placeholder: Option<CaretPlaceholder>,
480
481 cloneable_inline_box_pbm_size: LogicalSides1D<Au>,
485
486 trailing_hangable_white_space: Au,
488
489 trailing_removable_white_space: Au,
491}
492
493impl LineUnderConstruction {
494 fn new(start_position: LogicalVec2<Au>) -> Self {
495 Self {
496 inline_position: start_position.inline,
497 start_position,
498 max_block_size: LineBlockSizes::zero(),
499 has_content: false,
500 has_floats_waiting_to_be_placed: false,
501 placement_among_floats: OnceCell::new(),
502 line_items: Vec::new(),
503 for_block_level: false,
504 caret_placeholder: None,
505 cloneable_inline_box_pbm_size: Default::default(),
506 trailing_hangable_white_space: Au::zero(),
507 trailing_removable_white_space: Au::zero(),
508 }
509 }
510
511 fn replace_placement_among_floats(&mut self, new_placement: LogicalRect<Au>) {
512 self.placement_among_floats.take();
513 let _ = self.placement_among_floats.set(new_placement);
514 }
515
516 fn trim_trailing_white_space_and_calculate_hanging(
537 &mut self,
538 available_inline_size: Au,
539 last_line_or_forced_line_break: bool,
540 ) -> Au {
541 let mut conditionally_hanging = Au::zero();
542 let mut unconditionally_hanging = Au::zero();
543 let mut removed = Au::zero();
544 let mut white_space_still_hanging_conditionally = last_line_or_forced_line_break;
545
546 for item in self.line_items.iter_mut().rev() {
547 let text_run = match item {
548 LineItem::InlineStartBoxPaddingBorderMargin(_) |
549 LineItem::InlineEndBoxPaddingBorderMargin(_) |
550 LineItem::Float(..) |
551 LineItem::AbsolutelyPositioned(..) => continue,
552 LineItem::Atomic(..) | LineItem::BlockLevel(..) | LineItem::Tab { .. } => break,
553 LineItem::TextRun(_, text_run) => text_run,
554 };
555
556 let (mut count, preceded_by_content) = text_run.trailing_white_space();
557 if conditionally_hanging.is_zero() && unconditionally_hanging.is_zero() {
558 text_run.trim_removable_white_space_at_end();
559 removed += count.removable;
560 } else {
561 count.hangable += count.removable;
562 }
563
564 if white_space_still_hanging_conditionally && text_run.white_space_hangs_conditionally()
565 {
566 conditionally_hanging += count.hangable;
567 } else {
568 white_space_still_hanging_conditionally = false;
569 unconditionally_hanging += count.hangable;
570 }
571
572 if preceded_by_content {
573 break;
574 }
575 }
576
577 self.trailing_removable_white_space = Au::zero();
578 self.inline_position -= removed;
579
580 if !last_line_or_forced_line_break {
581 return unconditionally_hanging + conditionally_hanging;
582 }
583
584 let overflow = (self.inline_position - available_inline_size).max(Au::zero());
585 if overflow < conditionally_hanging {
586 overflow
587 } else {
588 unconditionally_hanging + conditionally_hanging
589 }
590 }
591
592 fn count_justification_opportunities(&self) -> usize {
594 let mut saw_content = false;
595 self.line_items
596 .iter()
597 .rev()
598 .filter_map(|item| match item {
599 LineItem::TextRun(_, text_run) => Some(
600 text_run
601 .text
602 .iter()
603 .rev()
604 .map(|shaped_text_slice| {
605 let mut word_separators = shaped_text_slice.total_word_separators();
606 if !saw_content {
607 word_separators -=
608 shaped_text_slice.hanging_and_removable_word_separators();
609 }
610 saw_content |=
611 shaped_text_slice.has_non_hangable_non_removable_content();
612 word_separators
613 })
614 .sum::<usize>(),
615 ),
616 LineItem::Atomic(..) | LineItem::Tab { .. } => {
617 saw_content = true;
618 None
619 },
620 _ => None,
621 })
622 .sum()
623 }
624
625 fn is_phantom(&self) -> bool {
628 Self::is_phantom_inner(self.has_content, &self.line_items)
629 }
630
631 fn is_phantom_inner(has_content: bool, line_items: &[LineItem]) -> bool {
632 if has_content {
633 return false;
634 }
635
636 !line_items.iter().any(|item| match item {
639 LineItem::InlineStartBoxPaddingBorderMargin(state) => state.has_pbm_inline_start(),
640 LineItem::InlineEndBoxPaddingBorderMargin(state) => state.has_pbm_inline_end(),
641 _ => false,
642 })
643 }
644
645 fn tentative_hanging_and_removable_white_space_advance(&self) -> Au {
651 self.trailing_hangable_white_space + self.trailing_removable_white_space
652 }
653}
654
655#[derive(Clone, Debug)]
661struct BaselineRelativeSize {
662 ascent: Au,
666
667 descent: Au,
671}
672
673impl BaselineRelativeSize {
674 fn zero() -> Self {
675 Self {
676 ascent: Au::zero(),
677 descent: Au::zero(),
678 }
679 }
680
681 fn max(&self, other: &Self) -> Self {
682 BaselineRelativeSize {
683 ascent: self.ascent.max(other.ascent),
684 descent: self.descent.max(other.descent),
685 }
686 }
687
688 fn adjust_for_nested_baseline_offset(&mut self, baseline_offset: Au) {
702 self.ascent -= baseline_offset;
703 self.descent += baseline_offset;
704 }
705}
706
707#[derive(Clone, Debug)]
708struct LineBlockSizes {
709 line_height: Au,
710 baseline_relative_size_for_line_height: Option<BaselineRelativeSize>,
711 size_for_baseline_positioning: BaselineRelativeSize,
712}
713
714impl LineBlockSizes {
715 fn zero() -> Self {
716 LineBlockSizes {
717 line_height: Au::zero(),
718 baseline_relative_size_for_line_height: None,
719 size_for_baseline_positioning: BaselineRelativeSize::zero(),
720 }
721 }
722
723 fn resolve(&self) -> Au {
724 let height_from_ascent_and_descent = self
725 .baseline_relative_size_for_line_height
726 .as_ref()
727 .map(|size| (size.ascent + size.descent).abs())
728 .unwrap_or_else(Au::zero);
729 self.line_height.max(height_from_ascent_and_descent)
730 }
731
732 fn max(&self, other: &LineBlockSizes) -> LineBlockSizes {
733 let baseline_relative_size = match (
734 self.baseline_relative_size_for_line_height.as_ref(),
735 other.baseline_relative_size_for_line_height.as_ref(),
736 ) {
737 (Some(our_size), Some(other_size)) => Some(our_size.max(other_size)),
738 (our_size, other_size) => our_size.or(other_size).cloned(),
739 };
740 Self {
741 line_height: self.line_height.max(other.line_height),
742 baseline_relative_size_for_line_height: baseline_relative_size,
743 size_for_baseline_positioning: self
744 .size_for_baseline_positioning
745 .max(&other.size_for_baseline_positioning),
746 }
747 }
748
749 fn max_assign(&mut self, other: &LineBlockSizes) {
750 *self = self.max(other);
751 }
752
753 fn adjust_for_baseline_offset(&mut self, baseline_offset: Au) {
754 if let Some(size) = self.baseline_relative_size_for_line_height.as_mut() {
755 size.adjust_for_nested_baseline_offset(baseline_offset)
756 }
757 self.size_for_baseline_positioning
758 .adjust_for_nested_baseline_offset(baseline_offset);
759 }
760
761 fn find_baseline_offset(&self) -> Au {
768 match self.baseline_relative_size_for_line_height.as_ref() {
769 Some(size) => size.ascent,
770 None => {
771 let leading = self.resolve() -
774 (self.size_for_baseline_positioning.ascent +
775 self.size_for_baseline_positioning.descent);
776 leading.scale_by(0.5) + self.size_for_baseline_positioning.ascent
777 },
778 }
779 }
780}
781
782struct UnbreakableSegmentUnderConstruction {
786 inline_size: Au,
788
789 max_block_size: LineBlockSizes,
792
793 line_items: Vec<LineItem>,
795
796 has_content: bool,
800
801 incorporates_trailing_white_space: bool,
804
805 trailing_hangable_white_space: Au,
807
808 trailing_removable_white_space: Au,
810}
811
812impl UnbreakableSegmentUnderConstruction {
813 fn new() -> Self {
814 Self {
815 inline_size: Au::zero(),
816 max_block_size: LineBlockSizes {
817 line_height: Au::zero(),
818 baseline_relative_size_for_line_height: None,
819 size_for_baseline_positioning: BaselineRelativeSize::zero(),
820 },
821 line_items: Vec::new(),
822 has_content: false,
823 trailing_hangable_white_space: Au::zero(),
824 trailing_removable_white_space: Au::zero(),
825 incorporates_trailing_white_space: false,
826 }
827 }
828
829 fn reset(&mut self) {
831 assert!(self.line_items.is_empty()); self.inline_size = Au::zero();
833 self.max_block_size = LineBlockSizes::zero();
834 self.has_content = false;
835 self.trailing_hangable_white_space = Au::zero();
836 self.trailing_removable_white_space = Au::zero();
837 self.incorporates_trailing_white_space = false;
838 }
839
840 fn push_line_item(&mut self, line_item: LineItem) {
842 self.line_items.push(line_item);
843 }
844
845 fn trim_leading_whitespace(&mut self) {
856 let mut whitespace_trimmed = Au::zero();
857 for item in self.line_items.iter_mut() {
858 if !item.trim_whitespace_at_start(&mut whitespace_trimmed) {
859 break;
860 }
861 }
862 self.inline_size -= whitespace_trimmed;
863
864 if !self.incorporates_trailing_white_space {
868 let trimmed_from_hanging = whitespace_trimmed.min(self.trailing_hangable_white_space);
869 self.trailing_hangable_white_space -= trimmed_from_hanging;
870 self.trailing_removable_white_space -= whitespace_trimmed - trimmed_from_hanging;
871 }
872 }
873
874 fn is_phantom(&self) -> bool {
877 LineUnderConstruction::is_phantom_inner(self.has_content, &self.line_items)
878 }
879
880 fn trailing_inline_box_start_size(&self) -> Au {
885 self.line_items
886 .iter()
887 .rev()
888 .map_while(|item| match item {
889 LineItem::InlineStartBoxPaddingBorderMargin(state) => Some(state),
890 _ => None,
891 })
892 .map(|state| {
893 if state.should_clone_pbm() {
894 state.pbm_inline_start() + state.pbm_inline_end()
895 } else {
896 state.pbm_inline_start()
897 }
898 })
899 .sum()
900 }
901
902 fn hanging_and_removable_white_space_advance(&self) -> Au {
905 self.trailing_hangable_white_space + self.trailing_removable_white_space
906 }
907}
908
909bitflags! {
910 struct InlineContainerStateFlags: u8 {
911 const CREATE_STRUT = 0b0001;
912 const IS_SINGLE_LINE_TEXT_INPUT = 0b0010;
913 }
914}
915
916struct InlineContainerState {
917 style: ServoArc<ComputedValues>,
919
920 flags: InlineContainerStateFlags,
922
923 has_content: Cell<bool>,
926
927 strut_block_sizes: LineBlockSizes,
932
933 nested_strut_block_sizes: LineBlockSizes,
937
938 pub baseline_offset: Au,
944
945 default_font: Option<FontRef>,
948
949 font_metrics: Arc<FontMetrics>,
951}
952
953struct InlineFormattingContextLayout<'layout_data> {
954 positioning_context: &'layout_data mut PositioningContext,
955 placement_state: PlacementState<'layout_data>,
956 sequential_layout_state: Option<&'layout_data mut SequentialLayoutState>,
957 layout_context: &'layout_data LayoutContext<'layout_data>,
958
959 ifc: &'layout_data InlineFormattingContext,
961
962 root_nesting_level: InlineContainerState,
972
973 inline_box_state_stack: Vec<Rc<InlineBoxContainerState>>,
977
978 cloneable_inline_box_pbm_size: LogicalSides1D<Au>,
982
983 inline_box_states: Vec<Rc<InlineBoxContainerState>>,
988
989 fragments: Vec<Fragment>,
993
994 current_line: LineUnderConstruction,
996
997 current_line_segment: UnbreakableSegmentUnderConstruction,
999
1000 force_line_break_before_new_content: bool,
1021
1022 caret_placeholder: Option<CaretPlaceholder>,
1025
1026 deferred_br_clear: Clear,
1030
1031 pub have_deferred_soft_wrap_opportunity: bool,
1035
1036 depends_on_block_constraints: bool,
1039
1040 white_space_collapse: WhiteSpaceCollapse,
1045
1046 text_wrap_mode: TextWrapMode,
1051
1052 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
1055}
1056
1057impl InlineFormattingContextLayout<'_> {
1058 fn current_inline_container_state(&self) -> &InlineContainerState {
1059 match self.inline_box_state_stack.last() {
1060 Some(inline_box_state) => &inline_box_state.base,
1061 None => &self.root_nesting_level,
1062 }
1063 }
1064
1065 fn current_inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
1066 self.inline_box_state_stack
1067 .last()
1068 .map(|state| state.identifier)
1069 }
1070
1071 fn current_line_max_block_size_including_nested_containers(&self) -> LineBlockSizes {
1072 self.current_inline_container_state()
1073 .nested_strut_block_sizes
1074 .max(&self.current_line.max_block_size)
1075 }
1076
1077 fn current_line_block_start_considering_placement_among_floats(&self) -> Au {
1078 self.current_line.placement_among_floats.get().map_or(
1079 self.current_line.start_position.block,
1080 |placement_among_floats| placement_among_floats.start_corner.block,
1081 )
1082 }
1083
1084 fn propagate_current_nesting_level_white_space_style(&mut self) {
1085 let style = match self.inline_box_state_stack.last() {
1086 Some(inline_box_state) => &inline_box_state.base.style,
1087 None => self.placement_state.containing_block.style,
1088 };
1089 let style_text = style.get_inherited_text();
1090 self.white_space_collapse = style_text.white_space_collapse;
1091 self.text_wrap_mode = style_text.text_wrap_mode;
1092 }
1093
1094 fn processing_br_element(&self) -> bool {
1095 self.inline_box_state_stack.last().is_some_and(|state| {
1096 state
1097 .base_fragment_info
1098 .flags
1099 .contains(FragmentFlags::IS_BR_ELEMENT)
1100 })
1101 }
1102
1103 fn start_inline_box(&mut self, inline_box: &InlineBox) {
1106 let containing_block = self.containing_block();
1107 let inline_box_state = InlineBoxContainerState::new(
1108 inline_box,
1109 containing_block,
1110 self.layout_context,
1111 self.current_inline_container_state(),
1112 inline_box.default_font.clone(),
1113 );
1114
1115 self.depends_on_block_constraints |= inline_box
1116 .base
1117 .style
1118 .depends_on_block_constraints_due_to_relative_positioning(
1119 containing_block.style.writing_mode,
1120 );
1121
1122 if inline_box_state
1127 .base_fragment_info
1128 .flags
1129 .contains(FragmentFlags::IS_BR_ELEMENT) &&
1130 self.deferred_br_clear == Clear::None
1131 {
1132 self.deferred_br_clear = Clear::from_style_and_container_writing_mode(
1133 &inline_box_state.base.style,
1134 self.containing_block().style.writing_mode,
1135 );
1136 }
1137
1138 let inline_box_state = Rc::new(inline_box_state);
1139 let pbm_inline_start = inline_box_state.pbm_inline_start();
1140 self.current_line_segment.inline_size += pbm_inline_start;
1141
1142 if inline_box_state.should_clone_pbm() {
1143 self.cloneable_inline_box_pbm_size.start += pbm_inline_start;
1144 self.cloneable_inline_box_pbm_size.end += inline_box_state.pbm_inline_end();
1145 }
1146
1147 self.current_line_segment
1148 .line_items
1149 .push(LineItem::InlineStartBoxPaddingBorderMargin(
1150 inline_box_state.clone(),
1151 ));
1152
1153 assert_eq!(
1157 self.inline_box_states.len(),
1158 inline_box.identifier.index_in_inline_boxes as usize
1159 );
1160 self.inline_box_states.push(inline_box_state.clone());
1161 self.inline_box_state_stack.push(inline_box_state);
1162 }
1163
1164 fn finish_inline_box(&mut self) {
1167 let inline_box_state = match self.inline_box_state_stack.pop() {
1168 Some(inline_box_state) => inline_box_state,
1169 None => return, };
1171
1172 self.current_line_segment
1173 .max_block_size
1174 .max_assign(&inline_box_state.base.nested_strut_block_sizes);
1175
1176 if inline_box_state.base.has_content.get() {
1181 self.propagate_current_nesting_level_white_space_style();
1182 }
1183
1184 let pbm_inline_end = inline_box_state.pbm_inline_end();
1185 self.current_line_segment.inline_size += pbm_inline_end;
1186
1187 if inline_box_state.should_clone_pbm() {
1188 self.cloneable_inline_box_pbm_size.start -= inline_box_state.pbm_inline_start();
1189 self.cloneable_inline_box_pbm_size.end -= pbm_inline_end;
1190 }
1191
1192 self.current_line_segment
1193 .line_items
1194 .push(LineItem::InlineEndBoxPaddingBorderMargin(inline_box_state));
1195 }
1196
1197 fn finish_last_line(&mut self) {
1198 self.possibly_flush_deferred_forced_line_break();
1200
1201 self.process_soft_wrap_opportunity();
1207
1208 self.commit_current_segment_to_line();
1211
1212 self.finish_current_line_and_reset(
1215 true, false, );
1218 }
1219
1220 fn finish_current_line_and_reset(
1224 &mut self,
1225 last_line_or_forced_line_break: bool,
1226 for_block_level: bool,
1227 ) {
1228 let hanging_white_space = self
1229 .current_line
1230 .trim_trailing_white_space_and_calculate_hanging(
1231 self.available_line_space(),
1232 last_line_or_forced_line_break,
1233 );
1234
1235 self.possibly_push_empty_text_run_to_line_for_text_caret();
1236
1237 if !self.current_line.for_block_level {
1240 self.current_line.inline_position +=
1241 self.current_line.cloneable_inline_box_pbm_size.end;
1242 }
1243
1244 let (inline_start_position, justification_adjustment) = self
1245 .calculate_current_line_inline_start_and_justification_adjustment(
1246 hanging_white_space,
1247 last_line_or_forced_line_break,
1248 );
1249
1250 let is_phantom_line = self.current_line.is_phantom();
1259 if !is_phantom_line {
1260 self.current_line.start_position.block += self.placement_state.current_margin.solve();
1261 self.placement_state.current_margin = CollapsedMargin::zero();
1262 }
1263 let block_start_position =
1264 self.current_line_block_start_considering_placement_among_floats();
1265
1266 let effective_block_advance = if is_phantom_line {
1267 LineBlockSizes::zero()
1268 } else {
1269 self.current_line_max_block_size_including_nested_containers()
1270 };
1271
1272 let resolved_block_advance = effective_block_advance.resolve();
1273 let block_end_position = if self.current_line.for_block_level {
1274 self.placement_state.current_block_direction_position
1275 } else {
1276 let mut block_end_position = block_start_position + resolved_block_advance;
1277 if let Some(sequential_layout_state) = self.sequential_layout_state.as_mut() {
1278 if !is_phantom_line {
1279 sequential_layout_state.commit_margin();
1280 }
1281
1282 let increment = block_end_position - self.current_line.start_position.block;
1285 sequential_layout_state.advance_block_position(increment);
1286
1287 if let Some(clearance) = sequential_layout_state
1291 .calculate_clearance(self.deferred_br_clear, &CollapsedMargin::zero())
1292 {
1293 sequential_layout_state.advance_block_position(clearance);
1294 block_end_position += clearance;
1295 };
1296 self.deferred_br_clear = Clear::None;
1297 }
1298 block_end_position
1299 };
1300
1301 let line_to_layout = std::mem::replace(
1303 &mut self.current_line,
1304 LineUnderConstruction::new(LogicalVec2 {
1305 inline: Au::zero(),
1306 block: block_end_position,
1307 }),
1308 );
1309 self.current_line.for_block_level = for_block_level;
1310
1311 self.current_line.cloneable_inline_box_pbm_size =
1313 line_to_layout.cloneable_inline_box_pbm_size;
1314 if !self.current_line.for_block_level {
1317 self.current_line.inline_position +=
1318 self.current_line.cloneable_inline_box_pbm_size.start;
1319 }
1320
1321 if !line_to_layout.for_block_level {
1322 self.placement_state.current_block_direction_position = block_end_position;
1323 }
1324
1325 if line_to_layout.has_floats_waiting_to_be_placed {
1326 place_pending_floats(self, &line_to_layout.line_items);
1327 }
1328
1329 let start_position = LogicalVec2 {
1330 block: block_start_position,
1331 inline: inline_start_position,
1332 };
1333
1334 let baseline_offset = effective_block_advance.find_baseline_offset();
1335 let start_positioning_context_length = self.positioning_context.len();
1336 let fragments = LineItemLayout::layout_line_items(
1337 self,
1338 line_to_layout.line_items,
1339 start_position,
1340 &effective_block_advance,
1341 justification_adjustment,
1342 is_phantom_line,
1343 line_to_layout.for_block_level,
1344 );
1345
1346 if !is_phantom_line {
1347 let baseline = baseline_offset + block_start_position;
1348 self.placement_state
1349 .inflow_baselines
1350 .first
1351 .get_or_insert(baseline);
1352 self.placement_state.inflow_baselines.last = Some(baseline);
1353 self.placement_state
1354 .next_in_flow_margin_collapses_with_parent_start_margin = false;
1355 }
1356
1357 if fragments.is_empty() &&
1359 self.positioning_context.len() == start_positioning_context_length
1360 {
1361 return;
1362 }
1363
1364 let start_corner = LogicalVec2 {
1368 inline: Au::zero(),
1369 block: block_start_position,
1370 };
1371
1372 let logical_origin_in_physical_coordinates =
1373 start_corner.to_physical_vector(self.containing_block().style.writing_mode);
1374 self.positioning_context
1375 .adjust_static_position_of_hoisted_fragments_with_offset(
1376 &logical_origin_in_physical_coordinates,
1377 start_positioning_context_length,
1378 );
1379
1380 let containing_block = self.containing_block();
1381 let physical_line_rect = LogicalRect {
1382 start_corner,
1383 size: LogicalVec2 {
1384 inline: containing_block.size.inline,
1385 block: effective_block_advance.resolve(),
1386 },
1387 }
1388 .as_physical(Some(containing_block));
1389 self.fragments
1390 .push(Fragment::Positioning(PositioningFragment::new_anonymous(
1391 self.root_nesting_level.style.clone(),
1392 physical_line_rect,
1393 fragments,
1394 true, )));
1396 }
1397
1398 fn available_line_space(&self) -> Au {
1399 match self.current_line.placement_among_floats.get() {
1400 Some(placement_among_floats) => placement_among_floats.size.inline,
1401 None => self.containing_block().size.inline,
1402 }
1403 }
1404
1405 fn calculate_current_line_inline_start_and_justification_adjustment(
1410 &self,
1411 hanging_white_space: Au,
1412 last_line_or_forced_line_break: bool,
1413 ) -> (Au, Au) {
1414 enum TextAlign {
1415 Start,
1416 Center,
1417 End,
1418 }
1419 let containing_block = self.containing_block();
1420 let style = containing_block.style;
1421 let mut text_align_keyword = style.get_text_align();
1422
1423 if last_line_or_forced_line_break {
1424 text_align_keyword = match style.get_text_align_last() {
1425 TextAlignLast::Auto if text_align_keyword == &TextAlignKeyword::Justify => {
1426 &TextAlignKeyword::Start
1427 },
1428 TextAlignLast::Auto => text_align_keyword,
1429 TextAlignLast::Start => &TextAlignKeyword::Start,
1430 TextAlignLast::End => &TextAlignKeyword::End,
1431 TextAlignLast::Left => &TextAlignKeyword::Left,
1432 TextAlignLast::Right => &TextAlignKeyword::Right,
1433 TextAlignLast::Center => &TextAlignKeyword::Center,
1434 TextAlignLast::Justify => &TextAlignKeyword::Justify,
1435 };
1436 }
1437
1438 let text_align = match text_align_keyword {
1439 TextAlignKeyword::Start => TextAlign::Start,
1440 TextAlignKeyword::Center | TextAlignKeyword::MozCenter => TextAlign::Center,
1441 TextAlignKeyword::End => TextAlign::End,
1442 TextAlignKeyword::Left | TextAlignKeyword::MozLeft => {
1443 if style.writing_mode.line_left_is_inline_start() {
1444 TextAlign::Start
1445 } else {
1446 TextAlign::End
1447 }
1448 },
1449 TextAlignKeyword::Right | TextAlignKeyword::MozRight => {
1450 if style.writing_mode.line_left_is_inline_start() {
1451 TextAlign::End
1452 } else {
1453 TextAlign::Start
1454 }
1455 },
1456 TextAlignKeyword::Justify => TextAlign::Start,
1457 };
1458
1459 let available_space = self.available_line_space();
1460 let line_start = match self.current_line.placement_among_floats.get() {
1461 Some(placement_among_floats) => placement_among_floats.start_corner.inline,
1462 None => Au::zero(),
1463 };
1464
1465 let text_indent = self.current_line.start_position.inline;
1472 let line_length = self.current_line.inline_position - hanging_white_space - text_indent;
1473 let adjusted_line_start = line_start +
1474 match text_align {
1475 TextAlign::Start => text_indent,
1476 TextAlign::End => (available_space - line_length).max(text_indent),
1477 TextAlign::Center => (available_space - line_length + text_indent)
1478 .scale_by(0.5)
1479 .max(text_indent),
1480 };
1481
1482 let text_justify = containing_block.style.get_text_justify();
1486 let justification_adjustment = match (text_align_keyword, text_justify) {
1487 (TextAlignKeyword::Justify, TextJustify::None) => Au::zero(),
1490 (TextAlignKeyword::Justify, _) => {
1491 match self.current_line.count_justification_opportunities() {
1492 0 => Au::zero(),
1493 num_justification_opportunities => {
1494 (available_space - text_indent - line_length)
1495 .scale_by(1. / num_justification_opportunities as f32)
1496 },
1497 }
1498 },
1499 _ => Au::zero(),
1500 };
1501
1502 let justification_adjustment = justification_adjustment.max(Au::zero());
1505
1506 (adjusted_line_start, justification_adjustment)
1507 }
1508
1509 fn place_float_fragment(&mut self, float: &FloatLineItem) {
1510 let state = self
1511 .sequential_layout_state
1512 .as_mut()
1513 .expect("Tried to lay out a float with no sequential placement state!");
1514
1515 let block_offset_from_containining_block_top = state
1516 .current_block_position_including_margins() -
1517 state.current_containing_block_offset();
1518 state.place_float_fragment(
1519 &float.fragment,
1520 self.placement_state.containing_block,
1521 CollapsedMargin::zero(),
1522 block_offset_from_containining_block_top,
1523 );
1524 self.positioning_context
1525 .adjust_static_position_of_hoisted_fragments_in_range(
1526 &float.fragment.base.rect().origin.to_vector(),
1527 &float.range,
1528 )
1529 }
1530
1531 fn place_float_line_item_for_commit_to_line(
1540 &mut self,
1541 float_item: &mut FloatLineItem,
1542 potential_line_length: Au,
1543 ) {
1544 let containing_block = self.containing_block();
1545 let float_fragment = &float_item.fragment;
1546 let logical_margin_rect_size = float_fragment
1547 .margin_rect()
1548 .size
1549 .to_logical(containing_block.style.writing_mode);
1550 let inline_size = logical_margin_rect_size.inline.max(Au::zero());
1551
1552 let available_inline_size = match self.current_line.placement_among_floats.get() {
1553 Some(placement_among_floats) => placement_among_floats.size.inline,
1554 None => containing_block.size.inline,
1555 } - potential_line_length;
1556
1557 let has_content = self.current_line.has_content || self.current_line_segment.has_content;
1563 let fits_on_line = !has_content || inline_size <= available_inline_size;
1564 let needs_placement_later =
1565 self.current_line.has_floats_waiting_to_be_placed || !fits_on_line;
1566
1567 if needs_placement_later {
1568 self.current_line.has_floats_waiting_to_be_placed = true;
1569 } else {
1570 self.place_float_fragment(float_item);
1571 float_item.needs_placement = false;
1572 }
1573
1574 let new_placement = self.place_line_among_floats(&LogicalVec2 {
1579 inline: potential_line_length,
1580 block: self.current_line.max_block_size.resolve(),
1581 });
1582 self.current_line
1583 .replace_placement_among_floats(new_placement);
1584 }
1585
1586 fn place_line_among_floats(&self, potential_line_size: &LogicalVec2<Au>) -> LogicalRect<Au> {
1591 let sequential_layout_state = self
1592 .sequential_layout_state
1593 .as_ref()
1594 .expect("Should not have called this function without having floats.");
1595
1596 let ifc_offset_in_float_container = LogicalVec2 {
1597 inline: sequential_layout_state
1598 .floats
1599 .containing_block_info
1600 .inline_start,
1601 block: sequential_layout_state.current_containing_block_offset(),
1602 };
1603
1604 let ceiling = self.current_line_block_start_considering_placement_among_floats();
1605 let mut placement = PlacementAmongFloats::new(
1606 &sequential_layout_state.floats,
1607 ceiling + ifc_offset_in_float_container.block,
1608 LogicalVec2 {
1609 inline: potential_line_size.inline,
1610 block: potential_line_size.block,
1611 },
1612 &PaddingBorderMargin::zero(),
1613 );
1614
1615 let mut placement_rect = placement.place();
1616 placement_rect.start_corner -= ifc_offset_in_float_container;
1617 placement_rect
1618 }
1619
1620 fn new_potential_line_size_causes_line_break(
1627 &mut self,
1628 potential_line_size: &LogicalVec2<Au>,
1629 ) -> bool {
1630 let containing_block = self.containing_block();
1631 let available_line_space = if self.sequential_layout_state.is_some() {
1632 self.current_line
1633 .placement_among_floats
1634 .get_or_init(|| self.place_line_among_floats(potential_line_size))
1635 .size
1636 } else {
1637 LogicalVec2 {
1638 inline: containing_block.size.inline,
1639 block: MAX_AU,
1640 }
1641 };
1642
1643 let inline_would_overflow = potential_line_size.inline > available_line_space.inline;
1644 let block_would_overflow = potential_line_size.block > available_line_space.block;
1645
1646 let can_break = self.current_line.has_content;
1649
1650 if !can_break {
1656 if self.sequential_layout_state.is_some() &&
1659 (inline_would_overflow || block_would_overflow)
1660 {
1661 let new_placement = self.place_line_among_floats(potential_line_size);
1662 self.current_line
1663 .replace_placement_among_floats(new_placement);
1664 }
1665
1666 return false;
1667 }
1668
1669 if potential_line_size.inline > containing_block.size.inline {
1672 return true;
1673 }
1674
1675 if block_would_overflow {
1679 assert!(self.sequential_layout_state.is_some());
1681 let new_placement = self.place_line_among_floats(potential_line_size);
1682 if new_placement.start_corner.block !=
1683 self.current_line_block_start_considering_placement_among_floats()
1684 {
1685 return true;
1686 } else {
1687 self.current_line
1688 .replace_placement_among_floats(new_placement);
1689 return false;
1690 }
1691 }
1692
1693 potential_line_size.inline > available_line_space.inline
1697 }
1698
1699 fn defer_forced_line_break_at_character_offset(
1700 &mut self,
1701 caret_placeholder: &Option<CaretPlaceholder>,
1702 ) {
1703 if !self.unbreakable_segment_fits_on_line() {
1706 self.rewind_trailing_inline_box_starts();
1707 self.process_line_break(
1708 false, false, );
1711 }
1712
1713 self.force_line_break_before_new_content = true;
1715 self.caret_placeholder = caret_placeholder.clone();
1716
1717 let line_is_empty =
1725 !self.current_line_segment.has_content && !self.current_line.has_content;
1726 if !self.processing_br_element() || line_is_empty {
1727 let strut_size = self
1728 .current_inline_container_state()
1729 .strut_block_sizes
1730 .clone();
1731 self.update_unbreakable_segment_for_new_content(
1732 &strut_size,
1733 Au::zero(),
1734 Au::zero(),
1735 Au::zero(),
1736 SegmentContentFlags::Contentful,
1737 );
1738 }
1739 }
1740
1741 fn possibly_flush_deferred_forced_line_break(&mut self) {
1742 if !self.force_line_break_before_new_content {
1743 return;
1744 }
1745 self.force_line_break_before_new_content = false;
1746
1747 self.commit_current_segment_to_line();
1748 self.process_line_break(
1749 true, false, );
1752
1753 self.current_line.caret_placeholder = self.caret_placeholder.take();
1754 }
1755
1756 fn push_line_item_to_unbreakable_segment(&mut self, line_item: LineItem) {
1757 self.current_line_segment.push_line_item(line_item);
1758 }
1759
1760 fn push_shaped_text_slice_to_unbreakable_segment(
1761 &mut self,
1762 slice: Arc<ShapedTextSlice>,
1763 text_run: &TextRun,
1764 info: &FontAndScriptInfo,
1765 character_range: Range<Utf32CodeUnits>,
1766 ) {
1767 let mut block_contribution = LineBlockSizes::zero();
1768 let quirks_mode = self.layout_context.style_context.quirks_mode() != QuirksMode::NoQuirks;
1769 let current_inline_container_state = self.current_inline_container_state();
1770 if quirks_mode && !slice.all_removable() {
1771 block_contribution.max_assign(¤t_inline_container_state.strut_block_sizes);
1776 }
1777
1778 let font_metrics = info.font_info.font.metrics();
1782 if current_inline_container_state
1783 .font_metrics
1784 .block_metrics_meaningfully_differ(font_metrics)
1785 {
1786 let baseline_shift = effective_baseline_shift(
1788 ¤t_inline_container_state.style,
1789 self.inline_box_state_stack.last().map(|c| &c.base),
1790 );
1791 let mut font_block_conribution = current_inline_container_state
1792 .get_block_size_contribution(
1793 &baseline_shift,
1794 font_metrics,
1795 ¤t_inline_container_state.font_metrics,
1796 );
1797 font_block_conribution
1798 .adjust_for_baseline_offset(current_inline_container_state.baseline_offset);
1799 block_contribution.max_assign(&font_block_conribution);
1800 }
1801
1802 let mut segment_flags = SegmentContentFlags::empty();
1803 if slice.has_non_hangable_non_removable_content() {
1804 segment_flags.insert(SegmentContentFlags::IncorporateTrailingWhiteSpace);
1805 }
1806 if !slice.all_removable() {
1807 segment_flags.insert(SegmentContentFlags::Contentful);
1808 }
1809
1810 self.update_unbreakable_segment_for_new_content(
1811 &block_contribution,
1812 slice.total_advance(),
1813 slice.hangable_advance(),
1814 slice.removable_advance(),
1815 segment_flags,
1816 );
1817
1818 let current_inline_box_identifier = self.current_inline_box_identifier();
1819 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1820 current_inline_box_identifier,
1821 TextRunLineItem {
1822 text: vec![slice],
1823 text_fragment_run_data: text_run.run_data.clone(),
1824 base_fragment_info: text_run.base_fragment_info,
1825 info: info.clone(),
1826 character_range_in_dom_node: character_range,
1827 is_empty_for_text_cursor: false,
1828 },
1829 ));
1830 }
1831
1832 fn possibly_push_empty_text_run_to_line_for_text_caret(&mut self) {
1835 let Some(caret_placeholder) = self.current_line.caret_placeholder.take() else {
1836 return;
1837 };
1838
1839 if self
1841 .current_line
1842 .line_items
1843 .iter()
1844 .rev()
1845 .find(|line_item| line_item.is_in_flow_content())
1846 .is_some_and(|line_item| matches!(line_item, LineItem::TextRun(..)))
1847 {
1848 return;
1849 }
1850
1851 let inline_container_state = self.current_inline_container_state();
1852 let Some(font) = inline_container_state.default_font.clone() else {
1853 return;
1854 };
1855
1856 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1857 self.current_inline_box_identifier(),
1858 TextRunLineItem {
1859 text: Default::default(),
1860 text_fragment_run_data: caret_placeholder.run_data,
1861 base_fragment_info: caret_placeholder.base_fragment_info,
1862 info: FontAndScriptInfo::simple_for_font(font),
1863 character_range_in_dom_node: caret_placeholder.character_index..
1864 caret_placeholder.character_index + Utf32CodeUnits(1),
1865 is_empty_for_text_cursor: true,
1866 },
1867 ));
1868
1869 let strut_size = self
1870 .current_inline_container_state()
1871 .strut_block_sizes
1872 .clone();
1873 self.update_unbreakable_segment_for_new_content(
1874 &strut_size,
1875 Au::zero(),
1876 Au::zero(),
1877 Au::zero(),
1878 SegmentContentFlags::empty(),
1879 );
1880
1881 self.current_line_segment.has_content = true;
1882 self.commit_current_segment_to_line();
1883 }
1884
1885 fn update_unbreakable_segment_for_new_content(
1886 &mut self,
1887 block_sizes_of_content: &LineBlockSizes,
1888 inline_size: Au,
1889 hangable_size: Au,
1890 removable_size: Au,
1891 segment_flags: SegmentContentFlags,
1892 ) {
1893 if segment_flags.contains(SegmentContentFlags::IncorporateTrailingWhiteSpace) {
1894 self.current_line_segment.trailing_hangable_white_space = Au::zero();
1895 self.current_line_segment.trailing_removable_white_space = Au::zero();
1896 self.current_line_segment.incorporates_trailing_white_space = true;
1897 }
1898
1899 if segment_flags.contains(SegmentContentFlags::Contentful) {
1900 self.current_line_segment.has_content = true;
1901 }
1902
1903 if hangable_size > Au::zero() {
1904 let old_removable_space =
1907 std::mem::take(&mut self.current_line_segment.trailing_removable_white_space);
1908 self.current_line_segment.trailing_hangable_white_space +=
1909 old_removable_space + hangable_size;
1910 }
1911 self.current_line_segment.trailing_removable_white_space += removable_size;
1912
1913 let container_max_block_size = &self
1915 .current_inline_container_state()
1916 .nested_strut_block_sizes
1917 .clone();
1918 self.current_line_segment
1919 .max_block_size
1920 .max_assign(container_max_block_size);
1921 self.current_line_segment
1922 .max_block_size
1923 .max_assign(block_sizes_of_content);
1924
1925 self.current_line_segment.inline_size += inline_size;
1926
1927 self.current_inline_container_state().has_content.set(true);
1929 self.propagate_current_nesting_level_white_space_style();
1930 }
1931
1932 fn process_line_break(&mut self, forced_line_break: bool, for_block_level: bool) {
1933 self.current_line_segment.trim_leading_whitespace();
1934 self.finish_current_line_and_reset(forced_line_break, for_block_level);
1935 }
1936
1937 fn hypothetical_line_size(&self) -> LogicalVec2<Au> {
1938 let mut hanging_and_removable_whitespace = self
1939 .current_line_segment
1940 .hanging_and_removable_white_space_advance();
1941 if !self.current_line_segment.incorporates_trailing_white_space {
1942 hanging_and_removable_whitespace += self
1943 .current_line
1944 .tentative_hanging_and_removable_white_space_advance();
1945 }
1946 let inline = self.current_line.inline_position +
1947 self.current_line_segment.inline_size +
1948 self.cloneable_inline_box_pbm_size.end -
1949 hanging_and_removable_whitespace -
1950 self.current_line_segment.trailing_inline_box_start_size();
1951
1952 LogicalVec2 {
1953 inline,
1954 block: self
1955 .current_line_max_block_size_including_nested_containers()
1956 .max(&self.current_line_segment.max_block_size)
1957 .resolve(),
1958 }
1959 }
1960
1961 fn unbreakable_segment_fits_on_line(&mut self) -> bool {
1962 !self.new_potential_line_size_causes_line_break(&self.hypothetical_line_size())
1963 }
1964
1965 fn rewind_trailing_inline_box_starts(&mut self) {
1971 let mut removal_start = self.current_line.line_items.len();
1972 for item in self.current_line.line_items.iter().rev() {
1973 let LineItem::InlineStartBoxPaddingBorderMargin(state) = item else {
1974 break;
1975 };
1976
1977 removal_start -= 1;
1978
1979 let inline_start = state.pbm_inline_start();
1980 self.current_line.inline_position -= inline_start;
1981 self.current_line_segment.inline_size += inline_start;
1982
1983 if state.should_clone_pbm() {
1984 self.current_line.cloneable_inline_box_pbm_size.start -= inline_start;
1985 self.current_line.cloneable_inline_box_pbm_size.end -= state.pbm_inline_end();
1986 }
1987 }
1988
1989 self.current_line_segment
1990 .line_items
1991 .splice(0..0, self.current_line.line_items.drain(removal_start..));
1992 }
1993
1994 fn process_soft_wrap_opportunity(&mut self) {
1998 if self.current_line_segment.line_items.is_empty() {
1999 return;
2000 }
2001 if self.text_wrap_mode == TextWrapMode::Nowrap {
2002 return;
2003 }
2004 if !self.unbreakable_segment_fits_on_line() {
2005 self.rewind_trailing_inline_box_starts();
2006 self.process_line_break(
2007 false, false, );
2010 }
2011 self.commit_current_segment_to_line();
2012 }
2013
2014 fn commit_current_segment_to_line(&mut self) {
2017 self.current_line.cloneable_inline_box_pbm_size = self.cloneable_inline_box_pbm_size;
2020
2021 if self.current_line_segment.line_items.is_empty() && !self.current_line_segment.has_content
2024 {
2025 return;
2026 }
2027
2028 if !self.current_line.has_content {
2029 self.current_line_segment.trim_leading_whitespace();
2030 }
2031
2032 let potential_line_length = self.hypothetical_line_size().inline;
2033
2034 let old_removable_space =
2035 std::mem::take(&mut self.current_line.trailing_removable_white_space);
2036 let old_hangable_space =
2037 std::mem::take(&mut self.current_line.trailing_hangable_white_space);
2038 let mut new_removable_space = self.current_line_segment.trailing_removable_white_space;
2039 let mut new_hangable_space = self.current_line_segment.trailing_hangable_white_space;
2040
2041 if !self.current_line_segment.incorporates_trailing_white_space {
2044 if new_hangable_space > Au::zero() {
2047 new_hangable_space += old_removable_space;
2048 } else {
2049 new_removable_space += old_removable_space;
2050 }
2051 new_hangable_space += old_hangable_space;
2052 }
2053
2054 self.current_line.trailing_hangable_white_space = new_hangable_space;
2055 self.current_line.trailing_removable_white_space = new_removable_space;
2056
2057 self.current_line.inline_position += self.current_line_segment.inline_size;
2058 self.current_line.max_block_size = self
2059 .current_line_max_block_size_including_nested_containers()
2060 .max(&self.current_line_segment.max_block_size);
2061
2062 let mut segment_items = {
2064 let new_line_items = Vec::with_capacity(self.current_line_segment.line_items.len());
2066 mem::replace(&mut self.current_line_segment.line_items, new_line_items)
2067 };
2068
2069 for item in segment_items.iter_mut() {
2070 if let LineItem::Float(_, float_item) = item {
2071 self.place_float_line_item_for_commit_to_line(float_item, potential_line_length);
2072 }
2073 }
2074
2075 if self.current_line.line_items.is_empty() {
2080 let will_break = self.new_potential_line_size_causes_line_break(&LogicalVec2 {
2081 inline: potential_line_length,
2082 block: self.current_line_segment.max_block_size.resolve(),
2083 });
2084 assert!(!will_break);
2085 }
2086
2087 self.current_line.line_items.extend(segment_items);
2088 self.current_line.has_content |= self.current_line_segment.has_content;
2089
2090 self.current_line_segment.reset();
2091 }
2092
2093 #[inline]
2094 fn containing_block(&self) -> &ContainingBlock<'_> {
2095 self.placement_state.containing_block
2096 }
2097}
2098
2099bitflags! {
2100 struct SegmentContentFlags: u8 {
2101 const IncorporateTrailingWhiteSpace = 1 << 0;
2102 const Contentful = 1 << 1;
2103 }
2104}
2105
2106impl InlineFormattingContext {
2107 #[servo_tracing::instrument(name = "InlineFormattingContext::new_with_builder", skip_all)]
2108 fn new_with_builder(
2109 mut builder: InlineFormattingContextBuilder,
2110 layout_context: &LayoutContext,
2111 has_first_formatted_line: bool,
2112 is_single_line_text_input: bool,
2113 starting_bidi_level: Level,
2114 ) -> Self {
2115 let text_content: String = builder.text_segments.into_iter().collect();
2117
2118 let bidi_levels = BidiLevels {
2119 info: builder
2120 .has_right_to_left_content
2121 .then(|| BidiInfo::new(&text_content, Some(starting_bidi_level))),
2122 };
2123
2124 let shared_inline_styles = builder
2125 .shared_inline_styles_stack
2126 .last()
2127 .expect("Should have at least one SharedInlineStyle for the root of an IFC")
2128 .clone();
2129 let (word_break, line_break, lang) = {
2130 let styles = shared_inline_styles.style.borrow();
2131 let text_style = styles.get_inherited_text();
2132 (
2133 text_style.word_break,
2134 text_style.line_break,
2135 styles.get_font()._x_lang.clone(),
2136 )
2137 };
2138
2139 let mut options = LineBreakOptions::default();
2140
2141 options.strictness = Some(match line_break {
2144 LineBreak::Loose => LineBreakStrictness::Loose,
2145 LineBreak::Normal => LineBreakStrictness::Normal,
2146 LineBreak::Strict => LineBreakStrictness::Strict,
2147 LineBreak::Anywhere => LineBreakStrictness::Anywhere,
2148 LineBreak::Auto => LineBreakStrictness::Normal,
2151 });
2152 options.word_option = Some(match word_break {
2153 WordBreak::Normal | WordBreak::KeepAll => LineBreakWordOption::Normal,
2154 WordBreak::BreakAll => LineBreakWordOption::BreakAll,
2155 });
2156
2157 let content_locale = lang.0.parse::<LanguageIdentifier>().ok();
2160 options.content_locale = content_locale.as_ref();
2161
2162 let mut shaping_queue = ShapingQueue::new(&text_content, options);
2163 for item in &mut builder.inline_items {
2164 match item {
2165 InlineItem::TextRun(text_run) => {
2166 let shaping_queue_entries = text_run.borrow_mut().segment(
2167 text_run.clone(),
2168 &text_content,
2169 layout_context,
2170 &bidi_levels,
2171 );
2172 for entry in shaping_queue_entries.into_iter() {
2173 shaping_queue.push(entry);
2174 }
2175 },
2176 InlineItem::StartInlineBox(inline_box) => {
2177 let inline_box = &mut *inline_box.borrow_mut();
2178 if let Some(font) = get_font_for_first_font_for_style(
2179 &inline_box.base.style,
2180 &layout_context.font_context,
2181 ) {
2182 inline_box.default_font = Some(font);
2183 }
2184
2185 if inline_box.breaks_shaping_at_start {
2186 shaping_queue.flush();
2187 }
2188 },
2189 InlineItem::Atomic(_, index_in_text, bidi_level) => {
2190 shaping_queue.flush();
2191 *bidi_level = bidi_levels.level(usize::from(*index_in_text));
2192 },
2193 InlineItem::EndInlineBox(inline_box) => {
2194 if inline_box.borrow().breaks_shaping_at_end {
2195 shaping_queue.flush();
2196 }
2197 },
2198 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) |
2199 InlineItem::OutOfFlowFloatBox(_) |
2200 InlineItem::BlockLevel { .. } => {},
2201 }
2202 }
2203
2204 shaping_queue.flush();
2205
2206 let default_font = get_font_for_first_font_for_style(
2207 &shared_inline_styles.style.borrow(),
2208 &layout_context.font_context,
2209 );
2210
2211 let has_right_to_left_content = bidi_levels.info.as_ref().is_some_and(BidiInfo::has_rtl);
2212 builder
2213 .text_content_slot
2214 .set(text_content)
2215 .expect("Text content should not yet be set.");
2216
2217 InlineFormattingContext {
2218 text_content: builder.text_content_slot,
2219 inline_items: builder.inline_items,
2220 inline_boxes: builder.inline_boxes,
2221 shared_inline_styles,
2222 default_font,
2223 has_first_formatted_line,
2224 contains_floats: builder.contains_floats,
2225 is_single_line_text_input,
2226 has_right_to_left_content,
2227 tab_size_multiplier: Default::default(),
2228 }
2229 }
2230
2231 pub(crate) fn text_content(&self) -> &str {
2232 self.text_content.get().map_or("", String::as_str)
2233 }
2234
2235 pub(crate) fn repair_style(
2236 &self,
2237 context: &SharedStyleContext,
2238 node: &ServoLayoutNode,
2239 new_style: &ServoArc<ComputedValues>,
2240 ) {
2241 *self.shared_inline_styles.style.borrow_mut() = new_style.clone();
2242 *self.shared_inline_styles.selected.borrow_mut() = node.selected_style(context);
2243 }
2244
2245 fn inline_start_for_first_line(&self, containing_block: IndefiniteContainingBlock) -> Au {
2246 if !self.has_first_formatted_line {
2247 return Au::zero();
2248 }
2249 containing_block
2250 .style
2251 .get_inherited_text()
2252 .text_indent
2253 .length
2254 .to_used_value(containing_block.size.inline.unwrap_or_default())
2255 }
2256
2257 pub(super) fn layout(
2258 &self,
2259 layout_context: &LayoutContext,
2260 positioning_context: &mut PositioningContext,
2261 containing_block: &ContainingBlock,
2262 sequential_layout_state: Option<&mut SequentialLayoutState>,
2263 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
2264 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
2265 ) -> IndependentFormattingContextLayoutResult {
2266 for inline_box in self.inline_boxes.iter() {
2268 inline_box.borrow().base.clear_fragments();
2269 }
2270
2271 let style = containing_block.style;
2272
2273 let style_text = containing_block.style.get_inherited_text();
2274 let mut inline_container_state_flags = InlineContainerStateFlags::empty();
2275 if inline_container_needs_strut(style, layout_context, None) {
2276 inline_container_state_flags.insert(InlineContainerStateFlags::CREATE_STRUT);
2277 }
2278 if self.is_single_line_text_input {
2279 inline_container_state_flags
2280 .insert(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT);
2281 }
2282 let placement_state =
2283 PlacementState::new(collapsible_with_parent_start_margin, containing_block);
2284
2285 let mut layout = InlineFormattingContextLayout {
2286 positioning_context,
2287 placement_state,
2288 sequential_layout_state,
2289 layout_context,
2290 ifc: self,
2291 fragments: Vec::new(),
2292 current_line: LineUnderConstruction::new(LogicalVec2 {
2293 inline: self.inline_start_for_first_line(containing_block.into()),
2294 block: Au::zero(),
2295 }),
2296 root_nesting_level: InlineContainerState::new(
2297 style.to_arc(),
2298 inline_container_state_flags,
2299 None, self.default_font.clone(),
2301 ),
2302 inline_box_state_stack: Vec::new(),
2303 cloneable_inline_box_pbm_size: Default::default(),
2304 inline_box_states: Vec::with_capacity(self.inline_boxes.len()),
2305 current_line_segment: UnbreakableSegmentUnderConstruction::new(),
2306 force_line_break_before_new_content: false,
2307 caret_placeholder: None,
2308 deferred_br_clear: Clear::None,
2309 have_deferred_soft_wrap_opportunity: false,
2310 depends_on_block_constraints: false,
2311 white_space_collapse: style_text.white_space_collapse,
2312 text_wrap_mode: style_text.text_wrap_mode,
2313 ignore_block_margins_for_stretch,
2314 };
2315
2316 for item in self.inline_items.iter() {
2317 if !matches!(item, InlineItem::EndInlineBox(..)) {
2319 layout.possibly_flush_deferred_forced_line_break();
2320 }
2321
2322 match item {
2323 InlineItem::StartInlineBox(inline_box) => {
2324 layout.start_inline_box(&inline_box.borrow());
2325 },
2326 InlineItem::EndInlineBox(..) => layout.finish_inline_box(),
2327 InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout),
2328 InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
2329 atomic_formatting_context.borrow().layout_into_line_items(
2330 &mut layout,
2331 *offset_in_text,
2332 *bidi_level,
2333 );
2334 },
2335 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, _) => {
2336 layout.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
2337 layout.current_inline_box_identifier(),
2338 AbsolutelyPositionedLineItem {
2339 absolutely_positioned_box: positioned_box.clone(),
2340 preceding_line_content_would_produce_phantom_line: layout
2341 .current_line
2342 .is_phantom() &&
2343 layout.current_line_segment.is_phantom(),
2344 },
2345 ));
2346 },
2347 InlineItem::OutOfFlowFloatBox(float_box) => {
2348 float_box.borrow().layout_into_line_items(&mut layout);
2349 },
2350 InlineItem::BlockLevel(block_level) => {
2351 block_level.borrow().layout_into_line_items(&mut layout);
2352 },
2353 }
2354 }
2355
2356 layout.finish_last_line();
2357 let (content_block_size, collapsible_margins_in_children, baselines) =
2358 layout.placement_state.finish();
2359
2360 IndependentFormattingContextLayoutResult {
2361 fragments: layout.fragments,
2362 content_block_size,
2363 collapsible_margins_in_children,
2364 baselines,
2365 depends_on_block_constraints: layout.depends_on_block_constraints,
2366 content_inline_size_for_table: None,
2367 specific_layout_info: None,
2368 }
2369 }
2370
2371 pub(crate) fn subtree_size(&self) -> usize {
2372 self.inline_items
2373 .iter()
2374 .map(|item| match item {
2375 InlineItem::StartInlineBox(..) => 1,
2376 InlineItem::EndInlineBox(..) => 0,
2377 InlineItem::TextRun(..) => 1,
2378 InlineItem::OutOfFlowAbsolutelyPositionedBox(absolutely_positioned_box, _) => {
2379 absolutely_positioned_box
2380 .borrow()
2381 .context
2382 .base
2383 .subtree_size()
2384 },
2385 InlineItem::OutOfFlowFloatBox(..) => 1,
2386 InlineItem::Atomic(..) => 1,
2387 InlineItem::BlockLevel(block_level_box) => block_level_box.borrow().subtree_size(),
2388 })
2389 .sum()
2390 }
2391
2392 fn next_character_prevents_soft_wrap_opportunity(&self, index: Utf8CodeUnits) -> bool {
2393 let Some(second_character) = self.text_content()[usize::from(index)..].chars().nth(1)
2394 else {
2395 return false;
2396 };
2397 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(second_character)
2398 }
2399
2400 fn previous_character_prevents_soft_wrap_opportunity(&self, index: Utf8CodeUnits) -> bool {
2401 let Some(character) = self.text_content()[..usize::from(index)]
2402 .chars()
2403 .next_back()
2404 else {
2405 return false;
2406 };
2407 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2408 }
2409
2410 pub(crate) fn find_block_margin_collapsing_with_parent(
2411 &self,
2412 layout_context: &LayoutContext,
2413 collected_margin: &mut CollapsedMargin,
2414 containing_block_for_children: &ContainingBlock,
2415 ) -> bool {
2416 let mut items_iter = self.inline_items.iter();
2422 items_iter.all(|inline_item| match inline_item {
2423 InlineItem::StartInlineBox(inline_box) => {
2424 let pbm = inline_box
2425 .borrow()
2426 .layout_style()
2427 .padding_border_margin(containing_block_for_children);
2428 pbm.padding.inline_start.is_zero() &&
2429 pbm.border.inline_start.is_zero() &&
2430 pbm.margin.inline_start.auto_is(Au::zero).is_zero()
2431 },
2432 InlineItem::EndInlineBox(inline_box) => {
2433 let pbm = inline_box
2434 .borrow()
2435 .layout_style()
2436 .padding_border_margin(containing_block_for_children);
2437 pbm.padding.inline_end.is_zero() &&
2438 pbm.border.inline_end.is_zero() &&
2439 pbm.margin.inline_end.auto_is(Au::zero).is_zero()
2440 },
2441 InlineItem::TextRun(text_run) => {
2442 let text_run = &*text_run.borrow();
2443 text_run.items.iter().all(|item| match item {
2444 TextRunItem::LineBreak { .. } => false,
2445 TextRunItem::Tab { .. } => false,
2446 TextRunItem::TextSegment(segment) => {
2447 segment.runs.iter().all(|run| run.all_removable())
2448 },
2449 })
2450 },
2451 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => true,
2452 InlineItem::OutOfFlowFloatBox(..) => true,
2453 InlineItem::Atomic(..) => false,
2454 InlineItem::BlockLevel(block_level) => block_level
2455 .borrow()
2456 .find_block_margin_collapsing_with_parent(
2457 layout_context,
2458 collected_margin,
2459 containing_block_for_children,
2460 ),
2461 })
2462 }
2463
2464 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
2465 let mut parent_box_stack = Vec::new();
2466 let current_parent_box = |parent_box_stack: &[WeakLayoutBox]| {
2467 parent_box_stack.last().unwrap_or(&layout_box).clone()
2468 };
2469 for inline_item in &self.inline_items {
2470 match inline_item {
2471 InlineItem::StartInlineBox(inline_box) => {
2472 inline_box
2473 .borrow_mut()
2474 .base
2475 .parent_box
2476 .replace(current_parent_box(&parent_box_stack));
2477 parent_box_stack.push(WeakLayoutBox::InlineLevel(
2478 WeakInlineItem::StartInlineBox(inline_box.downgrade()),
2479 ));
2480 },
2481 InlineItem::EndInlineBox(..) => {
2482 parent_box_stack.pop();
2483 },
2484 InlineItem::TextRun(text_run) => {
2485 text_run
2486 .borrow_mut()
2487 .parent_box
2488 .replace(current_parent_box(&parent_box_stack));
2489 },
2490 _ => inline_item.with_base_mut(|base| {
2491 base.parent_box
2492 .replace(current_parent_box(&parent_box_stack));
2493 }),
2494 }
2495 }
2496 }
2497
2498 pub(crate) fn next_tab_stop_after_inline_advance(
2499 &self,
2500 style: &ServoArc<ComputedValues>,
2501 current_inline_advance: Au,
2502 ) -> Au {
2503 let Some(font) = self.default_font.as_ref() else {
2504 return Au::zero();
2505 };
2506
2507 let tab_size_multiplier = *self.tab_size_multiplier.get_or_init(|| {
2508 let root_style = self.shared_inline_styles.style.borrow();
2509 let inherited_text_style = root_style.get_inherited_text();
2510 let font_size = root_style.get_font().font_size.computed_size().into();
2511 let letter_spacing = inherited_text_style
2512 .letter_spacing
2513 .0
2514 .to_used_value(font_size);
2515 let word_spacing = inherited_text_style.word_spacing.to_used_value(font_size);
2516
2517 font.metrics().space_advance + word_spacing + letter_spacing
2520 });
2521
2522 let tab_stop_advance = match style.get_inherited_text().tab_size {
2523 style::values::generics::length::LengthOrNumber::Number(number_of_spaces) => {
2524 tab_size_multiplier.scale_by(number_of_spaces.0)
2525 },
2526 style::values::generics::length::LengthOrNumber::Length(length) => length.into(),
2528 };
2529
2530 if tab_stop_advance.is_zero() {
2531 return Au::zero();
2532 }
2533
2534 let half_ch_advance = font
2540 .metrics()
2541 .zero_horizontal_advance
2542 .unwrap_or(font.metrics().em_size.scale_by(0.5))
2543 .scale_by(0.5);
2544 let number_of_tab_stops =
2545 (current_inline_advance + half_ch_advance).to_f32_px() / tab_stop_advance.to_f32_px();
2546 let number_of_tab_stops = number_of_tab_stops.ceil();
2547 tab_stop_advance.scale_by(number_of_tab_stops) - current_inline_advance
2548 }
2549}
2550
2551impl InlineContainerState {
2552 fn new(
2553 style: ServoArc<ComputedValues>,
2554 flags: InlineContainerStateFlags,
2555 parent_container: Option<&InlineContainerState>,
2556 default_font: Option<FontRef>,
2557 ) -> Self {
2558 let font_metrics = default_font
2559 .as_ref()
2560 .map(|font| font.metrics().clone())
2561 .unwrap_or_else(FontMetrics::empty);
2562 let mut baseline_offset = Au::zero();
2563 let mut strut_block_sizes = {
2564 Self::get_block_sizes_with_style(
2565 &effective_baseline_shift(&style, parent_container),
2566 &style,
2567 &font_metrics,
2568 &font_metrics,
2569 &flags,
2570 )
2571 };
2572
2573 if let Some(parent_container) = parent_container {
2574 baseline_offset = parent_container.get_cumulative_baseline_offset_for_child(
2577 style.get_alignment_baseline(),
2578 style.get_baseline_shift(),
2579 &strut_block_sizes,
2580 );
2581 strut_block_sizes.adjust_for_baseline_offset(baseline_offset);
2582 }
2583
2584 let mut nested_block_sizes = parent_container
2585 .map(|container| container.nested_strut_block_sizes.clone())
2586 .unwrap_or_else(LineBlockSizes::zero);
2587 if flags.contains(InlineContainerStateFlags::CREATE_STRUT) {
2588 nested_block_sizes.max_assign(&strut_block_sizes);
2589 }
2590
2591 Self {
2592 style,
2593 flags,
2594 has_content: Cell::new(false),
2595 nested_strut_block_sizes: nested_block_sizes,
2596 strut_block_sizes,
2597 baseline_offset,
2598 default_font,
2599 font_metrics,
2600 }
2601 }
2602
2603 fn get_block_sizes_with_style(
2604 baseline_shift: &BaselineShift,
2605 style: &ComputedValues,
2606 font_metrics: &FontMetrics,
2607 font_metrics_of_first_font: &FontMetrics,
2608 flags: &InlineContainerStateFlags,
2609 ) -> LineBlockSizes {
2610 let line_height = line_height(style, font_metrics, flags);
2611
2612 if !is_baseline_relative(baseline_shift) {
2613 return LineBlockSizes {
2614 line_height,
2615 baseline_relative_size_for_line_height: None,
2616 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2617 };
2618 }
2619
2620 let mut ascent = font_metrics.ascent;
2629 let mut descent = font_metrics.descent;
2630 if style.get_font().line_height == LineHeight::Normal {
2631 let half_leading_from_line_gap =
2632 (font_metrics.line_gap - descent - ascent).scale_by(0.5);
2633 ascent += half_leading_from_line_gap;
2634 descent += half_leading_from_line_gap;
2635 }
2636
2637 let size_for_baseline_positioning = BaselineRelativeSize { ascent, descent };
2641
2642 if style.get_font().line_height != LineHeight::Normal {
2658 ascent = font_metrics_of_first_font.ascent;
2659 descent = font_metrics_of_first_font.descent;
2660 let half_leading = (line_height - (ascent + descent)).scale_by(0.5);
2661 ascent += half_leading;
2666 descent = line_height - ascent;
2667 }
2668
2669 LineBlockSizes {
2670 line_height,
2671 baseline_relative_size_for_line_height: Some(BaselineRelativeSize { ascent, descent }),
2672 size_for_baseline_positioning,
2673 }
2674 }
2675
2676 fn get_block_size_contribution(
2677 &self,
2678 baseline_shift: &BaselineShift,
2679 font_metrics: &FontMetrics,
2680 font_metrics_of_first_font: &FontMetrics,
2681 ) -> LineBlockSizes {
2682 Self::get_block_sizes_with_style(
2683 baseline_shift,
2684 &self.style,
2685 font_metrics,
2686 font_metrics_of_first_font,
2687 &self.flags,
2688 )
2689 }
2690
2691 fn get_cumulative_baseline_offset_for_child(
2692 &self,
2693 child_alignment_baseline: &AlignmentBaseline,
2694 child_baseline_shift: &BaselineShift,
2695 child_block_size: &LineBlockSizes,
2696 ) -> Au {
2697 let block_size = self.get_block_size_contribution(
2698 child_baseline_shift,
2699 &self.font_metrics,
2700 &self.font_metrics,
2701 );
2702 self.baseline_offset +
2703 match child_alignment_baseline {
2704 AlignmentBaseline::Baseline => Au::zero(),
2705 AlignmentBaseline::TextTop => {
2706 child_block_size.size_for_baseline_positioning.ascent - self.font_metrics.ascent
2707 },
2708 AlignmentBaseline::Middle => {
2709 (child_block_size.size_for_baseline_positioning.ascent -
2712 child_block_size.size_for_baseline_positioning.descent -
2713 self.font_metrics.x_height)
2714 .scale_by(0.5)
2715 },
2716 AlignmentBaseline::TextBottom => {
2717 self.font_metrics.descent -
2718 child_block_size.size_for_baseline_positioning.descent
2719 },
2720 } +
2721 match child_baseline_shift {
2722 BaselineShift::Keyword(
2727 BaselineShiftKeyword::Top |
2728 BaselineShiftKeyword::Bottom |
2729 BaselineShiftKeyword::Center,
2730 ) => Au::zero(),
2731 BaselineShift::Keyword(BaselineShiftKeyword::Sub) => {
2732 block_size.resolve().scale_by(FONT_SUBSCRIPT_OFFSET_RATIO)
2733 },
2734 BaselineShift::Keyword(BaselineShiftKeyword::Super) => {
2735 -block_size.resolve().scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO)
2736 },
2737 BaselineShift::Length(length_percentage) => {
2738 -length_percentage.to_used_value(child_block_size.line_height)
2739 },
2740 }
2741 }
2742}
2743
2744impl IndependentFormattingContext {
2745 fn layout_into_line_items(
2746 &self,
2747 layout: &mut InlineFormattingContextLayout,
2748 offset_in_text: Utf8CodeUnits,
2749 bidi_level: Level,
2750 ) {
2751 let mut child_positioning_context = PositioningContext::default();
2753 let IndependentFloatOrAtomicLayoutResult {
2754 mut fragment,
2755 baselines,
2756 pbm_sums,
2757 } = self.layout_float_or_atomic_inline(
2758 layout.layout_context,
2759 &mut child_positioning_context,
2760 layout.containing_block(),
2761 );
2762
2763 layout.depends_on_block_constraints |= fragment.base.flags.contains(
2766 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2767 );
2768
2769 let container_writing_mode = layout.containing_block().style.writing_mode;
2771 let pbm_physical_offset = pbm_sums
2772 .start_offset()
2773 .to_physical_size(container_writing_mode);
2774 fragment.base.translate_rect(pbm_physical_offset);
2775
2776 fragment = fragment.with_baselines(baselines);
2778
2779 let positioning_context = if self.is_replaced() {
2782 None
2783 } else {
2784 if fragment
2785 .style()
2786 .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
2787 {
2788 child_positioning_context
2789 .layout_collected_children(layout.layout_context, &mut fragment);
2790 }
2791 Some(child_positioning_context)
2792 };
2793
2794 if layout.text_wrap_mode == TextWrapMode::Wrap &&
2795 !layout
2796 .ifc
2797 .previous_character_prevents_soft_wrap_opportunity(offset_in_text)
2798 {
2799 layout.process_soft_wrap_opportunity();
2800 }
2801
2802 let size = pbm_sums.sum() + fragment.base.rect().size.to_logical(container_writing_mode);
2803 let baseline_offset = self
2804 .pick_baseline(&fragment.baselines(container_writing_mode))
2805 .map(|baseline| pbm_sums.block_start + baseline)
2806 .unwrap_or(size.block);
2807
2808 let (block_sizes, baseline_offset_in_parent) =
2809 self.get_block_sizes_and_baseline_offset(layout, size.block, baseline_offset);
2810
2811 layout.update_unbreakable_segment_for_new_content(
2812 &block_sizes,
2813 size.inline,
2814 Au::zero(),
2815 Au::zero(),
2816 SegmentContentFlags::Contentful | SegmentContentFlags::IncorporateTrailingWhiteSpace,
2817 );
2818
2819 let fragment = Arc::new(fragment);
2820 self.base.set_fragment(Fragment::Box(fragment.clone()));
2821
2822 layout.push_line_item_to_unbreakable_segment(LineItem::Atomic(
2823 layout.current_inline_box_identifier(),
2824 AtomicLineItem {
2825 fragment,
2826 size,
2827 positioning_context,
2828 baseline_offset_in_parent,
2829 baseline_offset_in_item: baseline_offset,
2830 bidi_level,
2831 },
2832 ));
2833
2834 if !layout
2837 .ifc
2838 .next_character_prevents_soft_wrap_opportunity(offset_in_text)
2839 {
2840 layout.have_deferred_soft_wrap_opportunity = true;
2841 }
2842 }
2843
2844 fn pick_baseline(&self, baselines: &Baselines) -> Option<Au> {
2848 match self.style().get_baseline_source() {
2849 BaselineSource::First => baselines.first,
2850 BaselineSource::Last => baselines.last,
2851 BaselineSource::Auto if self.is_block_container() => baselines.last,
2852 BaselineSource::Auto => baselines.first,
2853 }
2854 }
2855
2856 fn get_block_sizes_and_baseline_offset(
2857 &self,
2858 ifc: &InlineFormattingContextLayout,
2859 block_size: Au,
2860 baseline_offset_in_content_area: Au,
2861 ) -> (LineBlockSizes, Au) {
2862 let mut contribution = if !is_baseline_relative(self.style().get_baseline_shift()) {
2863 LineBlockSizes {
2864 line_height: block_size,
2865 baseline_relative_size_for_line_height: None,
2866 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2867 }
2868 } else {
2869 let baseline_relative_size = BaselineRelativeSize {
2870 ascent: baseline_offset_in_content_area,
2871 descent: block_size - baseline_offset_in_content_area,
2872 };
2873 LineBlockSizes {
2874 line_height: block_size,
2875 baseline_relative_size_for_line_height: Some(baseline_relative_size.clone()),
2876 size_for_baseline_positioning: baseline_relative_size,
2877 }
2878 };
2879
2880 let style = self.style();
2881 let baseline_offset = ifc
2882 .current_inline_container_state()
2883 .get_cumulative_baseline_offset_for_child(
2884 style.get_alignment_baseline(),
2885 style.get_baseline_shift(),
2886 &contribution,
2887 );
2888 contribution.adjust_for_baseline_offset(baseline_offset);
2889
2890 (contribution, baseline_offset)
2891 }
2892}
2893
2894impl FloatBox {
2895 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
2896 let old_len = layout.positioning_context.len();
2897 let fragment = Arc::new(self.layout(
2898 layout.layout_context,
2899 layout.positioning_context,
2900 layout.placement_state.containing_block,
2901 ));
2902 let new_len = layout.positioning_context.len();
2903
2904 self.contents
2905 .base
2906 .set_fragment(Fragment::Box(fragment.clone()));
2907 layout.push_line_item_to_unbreakable_segment(LineItem::Float(
2908 layout.current_inline_box_identifier(),
2909 FloatLineItem {
2910 fragment,
2911 needs_placement: true,
2912 range: old_len..new_len,
2913 },
2914 ));
2915 }
2916}
2917
2918fn place_pending_floats(ifc: &mut InlineFormattingContextLayout, line_items: &[LineItem]) {
2919 for item in line_items.iter() {
2920 if let LineItem::Float(_, float_line_item) = item &&
2921 float_line_item.needs_placement
2922 {
2923 ifc.place_float_fragment(float_line_item);
2924 }
2925 }
2926}
2927
2928fn line_height(
2929 parent_style: &ComputedValues,
2930 font_metrics: &FontMetrics,
2931 flags: &InlineContainerStateFlags,
2932) -> Au {
2933 let font = parent_style.get_font();
2934 let font_size = font.font_size.computed_size();
2935 let mut line_height = match font.line_height {
2936 LineHeight::Normal => font_metrics.line_gap,
2937 LineHeight::Number(number) => (font_size * number.0).into(),
2938 LineHeight::Length(length) => length.0.into(),
2939 };
2940
2941 if flags.contains(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT) {
2945 line_height.max_assign(font_metrics.line_gap);
2946 }
2947
2948 line_height
2949}
2950
2951fn effective_baseline_shift(
2952 style: &ComputedValues,
2953 container: Option<&InlineContainerState>,
2954) -> BaselineShift {
2955 if container.is_none() {
2956 BaselineShift::zero()
2960 } else {
2961 style.slow_clone_baseline_shift()
2962 }
2963}
2964
2965fn is_baseline_relative(baseline_shift: &BaselineShift) -> bool {
2966 !matches!(
2967 baseline_shift,
2968 BaselineShift::Keyword(
2969 BaselineShiftKeyword::Top | BaselineShiftKeyword::Bottom | BaselineShiftKeyword::Center
2970 )
2971 )
2972}
2973
2974fn inline_container_needs_strut(
3000 style: &ComputedValues,
3001 layout_context: &LayoutContext,
3002 pbm: Option<&PaddingBorderMargin>,
3003) -> bool {
3004 if layout_context.style_context.quirks_mode() == QuirksMode::NoQuirks {
3005 return true;
3006 }
3007
3008 if style.get_box().display.is_list_item() {
3011 return true;
3012 }
3013
3014 pbm.is_some_and(|pbm| !pbm.padding_border_sums.inline.is_zero())
3015}
3016
3017impl ComputeInlineContentSizes for InlineFormattingContext {
3018 fn compute_inline_content_sizes(
3022 &self,
3023 layout_context: &LayoutContext,
3024 constraint_space: &ConstraintSpace,
3025 ) -> InlineContentSizesResult {
3026 ContentSizesComputation::compute(self, layout_context, constraint_space)
3027 }
3028}
3029
3030struct ContentSizesComputation<'layout_data> {
3032 layout_context: &'layout_data LayoutContext<'layout_data>,
3033 constraint_space: &'layout_data ConstraintSpace<'layout_data>,
3034 paragraph: ContentSizes,
3035 current_line: ContentSizes,
3036 pending_whitespace: ContentSizes,
3038 pending_opening_pbm_for_inline_boxes: Au,
3041 uncleared_floats: LogicalSides1D<ContentSizes>,
3043 cleared_floats: LogicalSides1D<ContentSizes>,
3045 had_content_yet_for_min_content: bool,
3048 had_content_yet_for_max_content: bool,
3051 ending_inline_pbm_stack: Vec<Au>,
3054 text_wrap_mode_stack: Vec<TextWrapMode>,
3056 depends_on_block_constraints: bool,
3058}
3059
3060impl<'layout_data> ContentSizesComputation<'layout_data> {
3061 fn traverse(
3062 mut self,
3063 inline_formatting_context: &InlineFormattingContext,
3064 ) -> InlineContentSizesResult {
3065 self.add_inline_size(
3066 inline_formatting_context.inline_start_for_first_line(self.constraint_space.into()),
3067 );
3068 for inline_item in &inline_formatting_context.inline_items {
3069 self.process_item(inline_item, inline_formatting_context);
3070 }
3071 self.forced_line_break();
3072 self.flush_floats();
3073
3074 InlineContentSizesResult {
3075 sizes: self.paragraph,
3076 depends_on_block_constraints: self.depends_on_block_constraints,
3077 }
3078 }
3079
3080 fn process_item(
3081 &mut self,
3082 inline_item: &InlineItem,
3083 inline_formatting_context: &InlineFormattingContext,
3084 ) {
3085 match inline_item {
3086 InlineItem::StartInlineBox(inline_box) => {
3087 let inline_box = inline_box.borrow();
3091 let zero = Au::zero();
3092 let writing_mode = self.constraint_space.style.writing_mode;
3093 let layout_style = inline_box.layout_style();
3094 let padding = layout_style
3095 .padding(writing_mode)
3096 .percentages_relative_to(zero);
3097 let border = layout_style.border_width(writing_mode);
3098 let margin = inline_box
3099 .base
3100 .style
3101 .margin(writing_mode)
3102 .percentages_relative_to(zero)
3103 .auto_is(Au::zero);
3104
3105 let pbm = margin + padding + border;
3106 self.pending_opening_pbm_for_inline_boxes += pbm.inline_start;
3107 self.current_line.max_content += pbm.inline_start;
3108 self.ending_inline_pbm_stack.push(pbm.inline_end);
3109
3110 self.text_wrap_mode_stack
3111 .push(layout_style.style().slow_clone_text_wrap_mode());
3112 },
3113 InlineItem::EndInlineBox(..) => {
3114 let length = self.ending_inline_pbm_stack.pop().unwrap_or_else(Au::zero);
3115 self.commit_pending_opening_pbm_for_inline_boxes();
3116 self.add_inline_size(length);
3117 self.text_wrap_mode_stack.pop();
3118 },
3119 InlineItem::TextRun(text_run) => {
3120 let text_run = &*text_run.borrow();
3121 let parent_style = text_run.inline_styles().style.borrow();
3122 for item in text_run.items.iter() {
3123 match item {
3124 TextRunItem::LineBreak { .. } => {
3125 self.forced_line_break();
3128 },
3129 TextRunItem::Tab { .. } => {
3130 self.process_preserved_tab(&parent_style, inline_formatting_context)
3131 },
3132 TextRunItem::TextSegment(segment) => {
3133 self.process_text_segment(&parent_style, segment)
3134 },
3135 }
3136 }
3137 },
3138 InlineItem::Atomic(atomic, offset_in_text, _level) => {
3139 let can_wrap = self.text_wrap_mode() == &TextWrapMode::Wrap;
3140 if can_wrap &&
3141 self.had_content_yet_for_min_content &&
3142 !inline_formatting_context
3143 .previous_character_prevents_soft_wrap_opportunity(*offset_in_text)
3144 {
3145 self.line_break_opportunity();
3146 }
3147
3148 self.commit_pending_opening_pbm_for_inline_boxes();
3149 self.commit_pending_whitespace();
3150 let outer = self.outer_inline_content_sizes_of_float_or_atomic(&atomic.borrow());
3151 self.current_line += outer;
3152
3153 if can_wrap &&
3154 !inline_formatting_context
3155 .next_character_prevents_soft_wrap_opportunity(*offset_in_text)
3156 {
3157 self.line_break_opportunity();
3158 }
3159 },
3160 InlineItem::OutOfFlowFloatBox(float_box) => {
3161 let float_box = float_box.borrow();
3162 let sizes = self.outer_inline_content_sizes_of_float_or_atomic(&float_box.contents);
3163 let style = &float_box.contents.style();
3164 let container_writing_mode = self.constraint_space.style.writing_mode;
3165 let clear =
3166 Clear::from_style_and_container_writing_mode(style, container_writing_mode);
3167 self.clear_floats(clear);
3168 let float_side =
3169 FloatSide::from_style_and_container_writing_mode(style, container_writing_mode);
3170 match float_side.expect("A float box needs to float to some side") {
3171 FloatSide::InlineStart => self.uncleared_floats.start.union_assign(&sizes),
3172 FloatSide::InlineEnd => self.uncleared_floats.end.union_assign(&sizes),
3173 }
3174 },
3175 InlineItem::BlockLevel(block_level) => {
3176 self.forced_line_break();
3177 self.flush_floats();
3178 let inline_content_sizes_result =
3179 compute_inline_content_sizes_for_block_level_boxes(
3180 std::slice::from_ref(block_level),
3181 self.layout_context,
3182 &self.constraint_space.into(),
3183 );
3184 self.depends_on_block_constraints |=
3185 inline_content_sizes_result.depends_on_block_constraints;
3186 self.current_line = inline_content_sizes_result.sizes;
3187 self.forced_line_break();
3188 },
3189 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => {},
3190 }
3191 }
3192
3193 fn process_text_segment(
3194 &mut self,
3195 parent_style: &AtomicRef<'_, ServoArc<ComputedValues>>,
3196 segment: &TextRunSegment,
3197 ) {
3198 let style_text = parent_style.get_inherited_text();
3199 let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap;
3200
3201 let break_at_start = segment.break_at_start && self.had_content_yet_for_min_content;
3204
3205 let hangable_characters_count_for_max_content = style_text.white_space_collapse ==
3220 WhiteSpaceCollapse::Preserve &&
3221 style_text.text_wrap_mode == TextWrapMode::Wrap;
3222
3223 for (run_index, run) in segment.runs.iter().enumerate() {
3224 if can_wrap && (run_index != 0 || break_at_start) {
3227 self.line_break_opportunity();
3228 }
3229
3230 let advance = run.total_advance();
3231 let removable_advance = run.removable_advance();
3232 let hangable_advance = run.hangable_advance();
3233 if run.has_non_hangable_non_removable_content() {
3234 self.commit_pending_opening_pbm_for_inline_boxes();
3235 self.commit_pending_whitespace();
3236 self.add_inline_size(advance - removable_advance - hangable_advance);
3237 }
3238
3239 self.pending_whitespace.min_content += hangable_advance;
3240 if hangable_characters_count_for_max_content {
3241 self.current_line.max_content += hangable_advance;
3242 } else {
3243 self.pending_whitespace.max_content += hangable_advance;
3244 }
3245
3246 if self.had_content_yet_for_min_content {
3247 self.pending_whitespace.min_content += removable_advance;
3248 }
3249 if self.had_content_yet_for_max_content {
3250 self.pending_whitespace.max_content += removable_advance;
3251 }
3252 }
3253 }
3254
3255 fn process_preserved_tab(
3256 &mut self,
3257 parent_style: &AtomicRef<'_, ServoArc<ComputedValues>>,
3258 inline_formatting_context: &InlineFormattingContext,
3259 ) {
3260 self.commit_pending_opening_pbm_for_inline_boxes();
3261 self.commit_pending_whitespace();
3262
3263 self.current_line.min_content += inline_formatting_context
3264 .next_tab_stop_after_inline_advance(parent_style, self.current_line.min_content);
3265 self.current_line.max_content += inline_formatting_context
3266 .next_tab_stop_after_inline_advance(parent_style, self.current_line.max_content);
3267 if parent_style.get_inherited_text().text_wrap_mode == TextWrapMode::Wrap {
3268 self.line_break_opportunity();
3269 }
3270 }
3271
3272 fn add_inline_size(&mut self, size: Au) {
3273 self.current_line.min_content += size;
3274 self.current_line.max_content += size;
3275 }
3276
3277 fn line_break_opportunity(&mut self) {
3278 self.pending_whitespace.min_content = Au::zero();
3282 let current_min_content = mem::take(&mut self.current_line.min_content);
3283 self.paragraph.min_content.max_assign(current_min_content);
3284 self.had_content_yet_for_min_content = false;
3285 }
3286
3287 fn forced_line_break(&mut self) {
3288 self.commit_pending_opening_pbm_for_inline_boxes();
3291
3292 self.line_break_opportunity();
3294
3295 self.pending_whitespace.max_content = Au::zero();
3297 let current_max_content = mem::take(&mut self.current_line.max_content);
3298 self.paragraph.max_content.max_assign(current_max_content);
3299 self.had_content_yet_for_max_content = false;
3300 }
3301
3302 fn commit_pending_whitespace(&mut self) {
3303 self.current_line += mem::take(&mut self.pending_whitespace);
3304 self.had_content_yet_for_min_content = true;
3305 self.had_content_yet_for_max_content = true;
3306 }
3307
3308 fn commit_pending_opening_pbm_for_inline_boxes(&mut self) {
3309 self.current_line.min_content +=
3310 std::mem::take(&mut self.pending_opening_pbm_for_inline_boxes);
3311 }
3312
3313 fn outer_inline_content_sizes_of_float_or_atomic(
3314 &mut self,
3315 context: &IndependentFormattingContext,
3316 ) -> ContentSizes {
3317 let result = context.outer_inline_content_sizes(
3318 self.layout_context,
3319 &self.constraint_space.into(),
3320 &LogicalVec2::zero(),
3321 false, );
3323 self.depends_on_block_constraints |= result.depends_on_block_constraints;
3324 result.sizes
3325 }
3326
3327 fn clear_floats(&mut self, clear: Clear) {
3328 match clear {
3329 Clear::InlineStart => {
3330 let start_floats = mem::take(&mut self.uncleared_floats.start);
3331 self.cleared_floats.start.max_assign(start_floats);
3332 },
3333 Clear::InlineEnd => {
3334 let end_floats = mem::take(&mut self.uncleared_floats.end);
3335 self.cleared_floats.end.max_assign(end_floats);
3336 },
3337 Clear::Both => {
3338 let start_floats = mem::take(&mut self.uncleared_floats.start);
3339 let end_floats = mem::take(&mut self.uncleared_floats.end);
3340 self.cleared_floats.start.max_assign(start_floats);
3341 self.cleared_floats.end.max_assign(end_floats);
3342 },
3343 Clear::None => {},
3344 }
3345 }
3346
3347 fn flush_floats(&mut self) {
3348 self.clear_floats(Clear::Both);
3349 let start_floats = mem::take(&mut self.cleared_floats.start);
3350 let end_floats = mem::take(&mut self.cleared_floats.end);
3351 self.paragraph.union_assign(&start_floats);
3352 self.paragraph.union_assign(&end_floats);
3353 }
3354
3355 fn text_wrap_mode(&self) -> &TextWrapMode {
3356 self.text_wrap_mode_stack
3357 .last()
3358 .unwrap_or_else(|| self.constraint_space.style.get_text_wrap_mode())
3359 }
3360
3361 fn compute(
3363 inline_formatting_context: &InlineFormattingContext,
3364 layout_context: &'layout_data LayoutContext,
3365 constraint_space: &'layout_data ConstraintSpace,
3366 ) -> InlineContentSizesResult {
3367 Self {
3368 layout_context,
3369 constraint_space,
3370 paragraph: ContentSizes::zero(),
3371 current_line: ContentSizes::zero(),
3372 pending_whitespace: ContentSizes::zero(),
3373 pending_opening_pbm_for_inline_boxes: Au::zero(),
3374 uncleared_floats: LogicalSides1D::default(),
3375 cleared_floats: LogicalSides1D::default(),
3376 had_content_yet_for_min_content: false,
3377 had_content_yet_for_max_content: false,
3378 ending_inline_pbm_stack: Vec::new(),
3379 text_wrap_mode_stack: Vec::new(),
3380 depends_on_block_constraints: false,
3381 }
3382 .traverse(inline_formatting_context)
3383 }
3384}
3385
3386pub(crate) struct BidiLevels<'a> {
3387 info: Option<BidiInfo<'a>>,
3388}
3389
3390impl BidiLevels<'_> {
3391 fn level(&self, byte_offset_in_ifc_text: usize) -> Level {
3392 self.info
3393 .as_ref()
3394 .map_or_else(Level::ltr, |info| info.levels[byte_offset_in_ifc_text])
3395 }
3396}
3397
3398fn char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character: char) -> bool {
3410 if character == '\u{00A0}' {
3411 return false;
3412 }
3413 matches!(
3414 ICULineBreak::for_char(character),
3415 ICULineBreak::Glue | ICULineBreak::WordJoiner | ICULineBreak::ZWJ
3416 )
3417}