1pub mod construct;
72pub mod inline_box;
73pub mod line;
74mod line_breaker;
75mod shaping_queue;
76pub mod text_run;
77pub mod text_transform;
78
79use std::cell::{Cell, OnceCell};
80use std::mem;
81use std::rc::Rc;
82use std::sync::{Arc, OnceLock};
83
84use app_units::{Au, MAX_AU};
85use atomic_refcell::AtomicRef;
86use bitflags::bitflags;
87use construct::InlineFormattingContextBuilder;
88use fonts::{FontMetrics, FontRef, ShapedTextSlice};
89use icu_locid::LanguageIdentifier;
90use icu_locid::subtags::{Language, language};
91use icu_properties::{self, LineBreak as ICULineBreak};
92use icu_segmenter::{LineBreakOptions, LineBreakStrictness, LineBreakWordOption};
93use inline_box::{InlineBox, InlineBoxContainerState, InlineBoxIdentifier, InlineBoxes};
94use layout_api::{LayoutNode, SharedSelection};
95use line::{
96 AbsolutelyPositionedLineItem, AtomicLineItem, FloatLineItem, LineItem, LineItemLayout,
97 TextRunLineItem,
98};
99use malloc_size_of_derive::MallocSizeOf;
100use script::layout_dom::ServoLayoutNode;
101use servo_arc::Arc as ServoArc;
102use style::Zero;
103use style::computed_values::line_break::T as LineBreak;
104use style::computed_values::text_wrap_mode::T as TextWrapMode;
105use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
106use style::computed_values::word_break::T as WordBreak;
107use style::context::{QuirksMode, SharedStyleContext};
108use style::properties::ComputedValues;
109use style::properties::style_structs::InheritedText;
110use style::values::computed::BaselineShift;
111use style::values::generics::box_::BaselineShiftKeyword;
112use style::values::generics::font::LineHeight;
113use style::values::specified::box_::BaselineSource;
114use style::values::specified::text::TextAlignKeyword;
115use style::values::specified::{AlignmentBaseline, TextAlignLast, TextJustify};
116use text_run::{TextRun, get_font_for_first_font_for_style};
117use unicode_bidi::{BidiInfo, Level};
118
119use super::float::{Clear, PlacementAmongFloats};
120use super::{IndependentFloatOrAtomicLayoutResult, IndependentFormattingContextLayoutResult};
121use crate::cell::{ArcRefCell, WeakRefCell};
122use crate::context::LayoutContext;
123use crate::dom::WeakLayoutBox;
124use crate::dom_traversal::NodeAndStyleInfo;
125use crate::flow::float::{FloatBox, SequentialLayoutState};
126use crate::flow::inline::line::TextRunOffsets;
127use crate::flow::inline::shaping_queue::ShapingQueue;
128use crate::flow::inline::text_run::{FontAndScriptInfo, TextRunItem, TextRunSegment};
129use crate::flow::{
130 BlockLevelBox, CollapsibleWithParentStartMargin, FloatSide, PlacementState,
131 compute_inline_content_sizes_for_block_level_boxes, layout_block_level_child,
132};
133use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
134use crate::fragment_tree::{
135 BaseFragmentInfo, CollapsedMargin, Fragment, FragmentFlags, PositioningFragment,
136};
137use crate::geom::{LogicalRect, LogicalSides1D, LogicalVec2, ToLogical};
138use crate::layout_box_base::LayoutBoxBase;
139use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
140use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
141use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
142use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SharedStyle};
143
144static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
146static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
147
148#[derive(Debug, MallocSizeOf)]
149pub(crate) struct InlineFormattingContext {
150 inline_items: Vec<InlineItem>,
155
156 inline_boxes: InlineBoxes,
159
160 text_content: String,
162
163 shared_inline_styles: SharedInlineStyles,
166
167 default_font: Option<FontRef>,
171
172 has_first_formatted_line: bool,
175
176 pub(super) contains_floats: bool,
178
179 is_single_line_text_input: bool,
182
183 has_right_to_left_content: bool,
186
187 #[ignore_malloc_size_of = "This is stored primarily in the DOM"]
190 shared_selection: Option<SharedSelection>,
191
192 tab_size_multiplier: OnceLock<Au>,
197}
198
199#[derive(Clone, Debug, MallocSizeOf)]
204pub(crate) struct SharedInlineStyles {
205 pub style: SharedStyle,
206 pub selected: SharedStyle,
207}
208
209impl SharedInlineStyles {
210 pub(crate) fn ptr_eq(&self, other: &Self) -> bool {
211 self.style.ptr_eq(&other.style) && self.selected.ptr_eq(&other.selected)
212 }
213
214 pub(crate) fn from_info_and_context(info: &NodeAndStyleInfo, context: &LayoutContext) -> Self {
215 Self {
216 style: SharedStyle::new(info.style.clone()),
217 selected: SharedStyle::new(info.node.selected_style(&context.style_context)),
218 }
219 }
220}
221
222impl BlockLevelBox {
223 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
224 layout.process_soft_wrap_opportunity();
225 layout.commit_current_segment_to_line();
226 layout.process_line_break(
227 true, true, );
230
231 let fragment = layout_block_level_child(
232 layout.layout_context,
233 layout.positioning_context,
234 self,
235 layout.sequential_layout_state.as_deref_mut(),
236 &mut layout.placement_state,
237 LogicalSides1D::new(false, false),
239 true, );
241
242 let Some(fragment) = fragment.retrieve_box_fragment() else {
243 unreachable!("The fragment should be a Fragment::Box()");
244 };
245
246 layout.depends_on_block_constraints |= fragment.base.flags.contains(
249 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
250 );
251
252 layout.push_line_item_to_unbreakable_segment(LineItem::BlockLevel(
253 layout.current_inline_box_identifier(),
254 fragment.clone(),
255 ));
256
257 layout.commit_current_segment_to_line();
258 layout.process_line_break(
259 true, false, );
262 }
263}
264
265#[derive(Clone, Debug, MallocSizeOf)]
266pub(crate) enum InlineItem {
267 StartInlineBox(ArcRefCell<InlineBox>),
268 EndInlineBox(ArcRefCell<InlineBox>),
269 TextRun(ArcRefCell<TextRun>),
270 OutOfFlowAbsolutelyPositionedBox(
271 ArcRefCell<AbsolutelyPositionedBox>,
272 usize, ),
274 OutOfFlowFloatBox(ArcRefCell<FloatBox>),
275 Atomic(
276 ArcRefCell<IndependentFormattingContext>,
277 usize, Level, ),
280 BlockLevel(ArcRefCell<BlockLevelBox>),
281}
282
283impl InlineItem {
284 pub(crate) fn repair_style(
285 &self,
286 context: &SharedStyleContext,
287 node: &ServoLayoutNode,
288 new_style: &ServoArc<ComputedValues>,
289 ) {
290 match self {
291 InlineItem::StartInlineBox(inline_box) => {
292 inline_box
293 .borrow_mut()
294 .repair_style(context, node, new_style);
295 },
296 InlineItem::EndInlineBox(..) => {},
297 InlineItem::TextRun(..) => {},
300 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box
301 .borrow_mut()
302 .context
303 .repair_style(context, node, new_style),
304 InlineItem::OutOfFlowFloatBox(float_box) => float_box
305 .borrow_mut()
306 .contents
307 .repair_style(context, node, new_style),
308 InlineItem::Atomic(atomic, ..) => {
309 atomic.borrow_mut().repair_style(context, node, new_style)
310 },
311 InlineItem::BlockLevel(block_level) => block_level
312 .borrow_mut()
313 .repair_style(context, node, new_style),
314 }
315 }
316
317 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
318 match self {
319 InlineItem::StartInlineBox(inline_box) => callback(&inline_box.borrow().base),
320 InlineItem::EndInlineBox(..) | InlineItem::TextRun(..) => {
321 unreachable!("Should never have these kind of fragments attached to a DOM node")
322 },
323 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
324 callback(&positioned_box.borrow().context.base)
325 },
326 InlineItem::OutOfFlowFloatBox(float_box) => callback(&float_box.borrow().contents.base),
327 InlineItem::Atomic(independent_formatting_context, ..) => {
328 callback(&independent_formatting_context.borrow().base)
329 },
330 InlineItem::BlockLevel(block_level) => block_level.borrow().with_base(callback),
331 }
332 }
333
334 pub(crate) fn with_base_mut<T>(&self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
335 match self {
336 InlineItem::StartInlineBox(inline_box) => callback(&mut inline_box.borrow_mut().base),
337 InlineItem::EndInlineBox(..) | InlineItem::TextRun(..) => {
338 unreachable!("Should never have these kind of fragments attached to a DOM node")
339 },
340 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
341 callback(&mut positioned_box.borrow_mut().context.base)
342 },
343 InlineItem::OutOfFlowFloatBox(float_box) => {
344 callback(&mut float_box.borrow_mut().contents.base)
345 },
346 InlineItem::Atomic(independent_formatting_context, ..) => {
347 callback(&mut independent_formatting_context.borrow_mut().base)
348 },
349 InlineItem::BlockLevel(block_level) => block_level.borrow_mut().with_base_mut(callback),
350 }
351 }
352
353 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
354 match self {
355 Self::StartInlineBox(_) | InlineItem::EndInlineBox(..) => {
356 },
359 Self::TextRun(_) => {
360 },
362 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
363 positioned_box.borrow().context.attached_to_tree(layout_box)
364 },
365 Self::OutOfFlowFloatBox(float_box) => {
366 float_box.borrow().contents.attached_to_tree(layout_box)
367 },
368 Self::Atomic(atomic, ..) => atomic.borrow().attached_to_tree(layout_box),
369 Self::BlockLevel(block_level) => block_level.borrow().attached_to_tree(layout_box),
370 }
371 }
372
373 pub(crate) fn downgrade(&self) -> WeakInlineItem {
374 match self {
375 Self::StartInlineBox(inline_box) => {
376 WeakInlineItem::StartInlineBox(inline_box.downgrade())
377 },
378 Self::EndInlineBox(inline_box) => WeakInlineItem::EndInlineBox(inline_box.downgrade()),
379 Self::TextRun(text_run) => WeakInlineItem::TextRun(text_run.downgrade()),
380 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
381 WeakInlineItem::OutOfFlowAbsolutelyPositionedBox(
382 positioned_box.downgrade(),
383 *offset_in_text,
384 )
385 },
386 Self::OutOfFlowFloatBox(float_box) => {
387 WeakInlineItem::OutOfFlowFloatBox(float_box.downgrade())
388 },
389 Self::Atomic(atomic, offset_in_text, bidi_level) => {
390 WeakInlineItem::Atomic(atomic.downgrade(), *offset_in_text, *bidi_level)
391 },
392 Self::BlockLevel(block_level) => WeakInlineItem::BlockLevel(block_level.downgrade()),
393 }
394 }
395}
396
397#[derive(Clone, Debug, MallocSizeOf)]
398pub(crate) enum WeakInlineItem {
399 StartInlineBox(WeakRefCell<InlineBox>),
400 EndInlineBox(WeakRefCell<InlineBox>),
401 TextRun(WeakRefCell<TextRun>),
402 OutOfFlowAbsolutelyPositionedBox(
403 WeakRefCell<AbsolutelyPositionedBox>,
404 usize, ),
406 OutOfFlowFloatBox(WeakRefCell<FloatBox>),
407 Atomic(
408 WeakRefCell<IndependentFormattingContext>,
409 usize, Level, ),
412 BlockLevel(WeakRefCell<BlockLevelBox>),
413}
414
415impl WeakInlineItem {
416 pub(crate) fn upgrade(&self) -> Option<InlineItem> {
417 Some(match self {
418 Self::StartInlineBox(inline_box) => InlineItem::StartInlineBox(inline_box.upgrade()?),
419 Self::EndInlineBox(inline_box) => InlineItem::EndInlineBox(inline_box.upgrade()?),
420 Self::TextRun(text_run) => InlineItem::TextRun(text_run.upgrade()?),
421 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
422 InlineItem::OutOfFlowAbsolutelyPositionedBox(
423 positioned_box.upgrade()?,
424 *offset_in_text,
425 )
426 },
427 Self::OutOfFlowFloatBox(float_box) => {
428 InlineItem::OutOfFlowFloatBox(float_box.upgrade()?)
429 },
430 Self::Atomic(atomic, offset_in_text, bidi_level) => {
431 InlineItem::Atomic(atomic.upgrade()?, *offset_in_text, *bidi_level)
432 },
433 Self::BlockLevel(block_level) => InlineItem::BlockLevel(block_level.upgrade()?),
434 })
435 }
436}
437
438struct LineUnderConstruction {
445 start_position: LogicalVec2<Au>,
448
449 inline_position: Au,
452
453 max_block_size: LineBlockSizes,
457
458 has_content: bool,
461
462 has_inline_pbm: bool,
465
466 has_floats_waiting_to_be_placed: bool,
470
471 placement_among_floats: OnceCell<LogicalRect<Au>>,
476
477 line_items: Vec<LineItem>,
480
481 for_block_level: bool,
483
484 starting_character_offset: usize,
493}
494
495impl LineUnderConstruction {
496 fn new(start_position: LogicalVec2<Au>) -> Self {
497 Self {
498 inline_position: start_position.inline,
499 start_position,
500 max_block_size: LineBlockSizes::zero(),
501 has_content: false,
502 has_inline_pbm: false,
503 has_floats_waiting_to_be_placed: false,
504 placement_among_floats: OnceCell::new(),
505 line_items: Vec::new(),
506 for_block_level: false,
507 starting_character_offset: 0,
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_whitespace(&mut self) -> Au {
518 let mut whitespace_trimmed = Au::zero();
523 for item in self.line_items.iter_mut().rev() {
524 if !item.trim_whitespace_at_end(&mut whitespace_trimmed) {
525 break;
526 }
527 }
528
529 whitespace_trimmed
530 }
531
532 fn count_justification_opportunities(&self) -> usize {
534 self.line_items
535 .iter()
536 .filter_map(|item| match item {
537 LineItem::TextRun(_, text_run) => Some(
538 text_run
539 .text
540 .iter()
541 .map(|shaped_text_slice| shaped_text_slice.total_word_separators())
542 .sum::<usize>(),
543 ),
544 _ => None,
545 })
546 .sum()
547 }
548
549 fn is_phantom(&self) -> bool {
552 !self.has_content && !self.has_inline_pbm
554 }
555}
556
557#[derive(Clone, Debug)]
563struct BaselineRelativeSize {
564 ascent: Au,
568
569 descent: Au,
573}
574
575impl BaselineRelativeSize {
576 fn zero() -> Self {
577 Self {
578 ascent: Au::zero(),
579 descent: Au::zero(),
580 }
581 }
582
583 fn max(&self, other: &Self) -> Self {
584 BaselineRelativeSize {
585 ascent: self.ascent.max(other.ascent),
586 descent: self.descent.max(other.descent),
587 }
588 }
589
590 fn adjust_for_nested_baseline_offset(&mut self, baseline_offset: Au) {
604 self.ascent -= baseline_offset;
605 self.descent += baseline_offset;
606 }
607}
608
609#[derive(Clone, Debug)]
610struct LineBlockSizes {
611 line_height: Au,
612 baseline_relative_size_for_line_height: Option<BaselineRelativeSize>,
613 size_for_baseline_positioning: BaselineRelativeSize,
614}
615
616impl LineBlockSizes {
617 fn zero() -> Self {
618 LineBlockSizes {
619 line_height: Au::zero(),
620 baseline_relative_size_for_line_height: None,
621 size_for_baseline_positioning: BaselineRelativeSize::zero(),
622 }
623 }
624
625 fn resolve(&self) -> Au {
626 let height_from_ascent_and_descent = self
627 .baseline_relative_size_for_line_height
628 .as_ref()
629 .map(|size| (size.ascent + size.descent).abs())
630 .unwrap_or_else(Au::zero);
631 self.line_height.max(height_from_ascent_and_descent)
632 }
633
634 fn max(&self, other: &LineBlockSizes) -> LineBlockSizes {
635 let baseline_relative_size = match (
636 self.baseline_relative_size_for_line_height.as_ref(),
637 other.baseline_relative_size_for_line_height.as_ref(),
638 ) {
639 (Some(our_size), Some(other_size)) => Some(our_size.max(other_size)),
640 (our_size, other_size) => our_size.or(other_size).cloned(),
641 };
642 Self {
643 line_height: self.line_height.max(other.line_height),
644 baseline_relative_size_for_line_height: baseline_relative_size,
645 size_for_baseline_positioning: self
646 .size_for_baseline_positioning
647 .max(&other.size_for_baseline_positioning),
648 }
649 }
650
651 fn max_assign(&mut self, other: &LineBlockSizes) {
652 *self = self.max(other);
653 }
654
655 fn adjust_for_baseline_offset(&mut self, baseline_offset: Au) {
656 if let Some(size) = self.baseline_relative_size_for_line_height.as_mut() {
657 size.adjust_for_nested_baseline_offset(baseline_offset)
658 }
659 self.size_for_baseline_positioning
660 .adjust_for_nested_baseline_offset(baseline_offset);
661 }
662
663 fn find_baseline_offset(&self) -> Au {
670 match self.baseline_relative_size_for_line_height.as_ref() {
671 Some(size) => size.ascent,
672 None => {
673 let leading = self.resolve() -
676 (self.size_for_baseline_positioning.ascent +
677 self.size_for_baseline_positioning.descent);
678 leading.scale_by(0.5) + self.size_for_baseline_positioning.ascent
679 },
680 }
681 }
682}
683
684struct UnbreakableSegmentUnderConstruction {
688 inline_size: Au,
690
691 max_block_size: LineBlockSizes,
694
695 line_items: Vec<LineItem>,
697
698 inline_box_hierarchy_depth: Option<usize>,
701
702 has_content: bool,
706
707 has_inline_pbm: bool,
710
711 trailing_whitespace_size: Au,
713}
714
715impl UnbreakableSegmentUnderConstruction {
716 fn new() -> Self {
717 Self {
718 inline_size: Au::zero(),
719 max_block_size: LineBlockSizes {
720 line_height: Au::zero(),
721 baseline_relative_size_for_line_height: None,
722 size_for_baseline_positioning: BaselineRelativeSize::zero(),
723 },
724 line_items: Vec::new(),
725 inline_box_hierarchy_depth: None,
726 has_content: false,
727 has_inline_pbm: false,
728 trailing_whitespace_size: Au::zero(),
729 }
730 }
731
732 fn reset(&mut self) {
734 assert!(self.line_items.is_empty()); self.inline_size = Au::zero();
736 self.max_block_size = LineBlockSizes::zero();
737 self.inline_box_hierarchy_depth = None;
738 self.has_content = false;
739 self.has_inline_pbm = false;
740 self.trailing_whitespace_size = Au::zero();
741 }
742
743 fn push_line_item(&mut self, line_item: LineItem, inline_box_hierarchy_depth: usize) {
748 if self.line_items.is_empty() {
749 self.inline_box_hierarchy_depth = Some(inline_box_hierarchy_depth);
750 }
751 self.line_items.push(line_item);
752 }
753
754 fn trim_leading_whitespace(&mut self) {
765 let mut whitespace_trimmed = Au::zero();
766 for item in self.line_items.iter_mut() {
767 if !item.trim_whitespace_at_start(&mut whitespace_trimmed) {
768 break;
769 }
770 }
771 self.inline_size -= whitespace_trimmed;
772 }
773
774 fn is_phantom(&self) -> bool {
777 !self.has_content && !self.has_inline_pbm
779 }
780}
781
782bitflags! {
783 struct InlineContainerStateFlags: u8 {
784 const CREATE_STRUT = 0b0001;
785 const IS_SINGLE_LINE_TEXT_INPUT = 0b0010;
786 }
787}
788
789struct InlineContainerState {
790 style: ServoArc<ComputedValues>,
792
793 flags: InlineContainerStateFlags,
795
796 has_content: Cell<bool>,
799
800 strut_block_sizes: LineBlockSizes,
805
806 nested_strut_block_sizes: LineBlockSizes,
810
811 pub baseline_offset: Au,
817
818 default_font: Option<FontRef>,
821
822 font_metrics: Arc<FontMetrics>,
824}
825
826struct InlineFormattingContextLayout<'layout_data> {
827 positioning_context: &'layout_data mut PositioningContext,
828 placement_state: PlacementState<'layout_data>,
829 sequential_layout_state: Option<&'layout_data mut SequentialLayoutState>,
830 layout_context: &'layout_data LayoutContext<'layout_data>,
831
832 ifc: &'layout_data InlineFormattingContext,
834
835 root_nesting_level: InlineContainerState,
845
846 inline_box_state_stack: Vec<Rc<InlineBoxContainerState>>,
850
851 cloneable_inline_box_end_pbm_size: Au,
854
855 inline_box_states: Vec<Rc<InlineBoxContainerState>>,
860
861 fragments: Vec<Fragment>,
865
866 current_line: LineUnderConstruction,
868
869 current_line_segment: UnbreakableSegmentUnderConstruction,
871
872 force_line_break_before_new_content: Option<usize>,
895
896 deferred_br_clear: Clear,
900
901 pub have_deferred_soft_wrap_opportunity: bool,
905
906 depends_on_block_constraints: bool,
909
910 white_space_collapse: WhiteSpaceCollapse,
915
916 text_wrap_mode: TextWrapMode,
921}
922
923impl InlineFormattingContextLayout<'_> {
924 fn current_inline_container_state(&self) -> &InlineContainerState {
925 match self.inline_box_state_stack.last() {
926 Some(inline_box_state) => &inline_box_state.base,
927 None => &self.root_nesting_level,
928 }
929 }
930
931 fn current_inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
932 self.inline_box_state_stack
933 .last()
934 .map(|state| state.identifier)
935 }
936
937 fn current_line_max_block_size_including_nested_containers(&self) -> LineBlockSizes {
938 self.current_inline_container_state()
939 .nested_strut_block_sizes
940 .max(&self.current_line.max_block_size)
941 }
942
943 fn current_line_block_start_considering_placement_among_floats(&self) -> Au {
944 self.current_line.placement_among_floats.get().map_or(
945 self.current_line.start_position.block,
946 |placement_among_floats| placement_among_floats.start_corner.block,
947 )
948 }
949
950 fn propagate_current_nesting_level_white_space_style(&mut self) {
951 let style = match self.inline_box_state_stack.last() {
952 Some(inline_box_state) => &inline_box_state.base.style,
953 None => self.placement_state.containing_block.style,
954 };
955 let style_text = style.get_inherited_text();
956 self.white_space_collapse = style_text.white_space_collapse;
957 self.text_wrap_mode = style_text.text_wrap_mode;
958 }
959
960 fn processing_br_element(&self) -> bool {
961 self.inline_box_state_stack.last().is_some_and(|state| {
962 state
963 .base_fragment_info
964 .flags
965 .contains(FragmentFlags::IS_BR_ELEMENT)
966 })
967 }
968
969 fn start_inline_box(&mut self, inline_box: &InlineBox) {
972 let containing_block = self.containing_block();
973 let inline_box_state = InlineBoxContainerState::new(
974 inline_box,
975 containing_block,
976 self.layout_context,
977 self.current_inline_container_state(),
978 inline_box.default_font.clone(),
979 );
980
981 self.depends_on_block_constraints |= inline_box
982 .base
983 .style
984 .depends_on_block_constraints_due_to_relative_positioning(
985 containing_block.style.writing_mode,
986 );
987
988 if inline_box_state
993 .base_fragment_info
994 .flags
995 .contains(FragmentFlags::IS_BR_ELEMENT) &&
996 self.deferred_br_clear == Clear::None
997 {
998 self.deferred_br_clear = Clear::from_style_and_container_writing_mode(
999 &inline_box_state.base.style,
1000 self.containing_block().style.writing_mode,
1001 );
1002 }
1003
1004 let padding = inline_box_state.pbm.padding.inline_start;
1005 let border = inline_box_state.pbm.border.inline_start;
1006 let margin = inline_box_state.pbm.margin.inline_start.auto_is(Au::zero);
1007 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
1010 self.current_line_segment.has_inline_pbm = true;
1011 }
1012 self.current_line_segment.inline_size += padding + border + margin;
1013 self.current_line_segment
1014 .line_items
1015 .push(LineItem::InlineStartBoxPaddingBorderMargin(
1016 inline_box.identifier,
1017 ));
1018
1019 let inline_box_state = Rc::new(inline_box_state);
1020 if inline_box_state.should_clone_pbm() {
1021 self.cloneable_inline_box_end_pbm_size += inline_box_state.pbm.padding.inline_end;
1022 self.cloneable_inline_box_end_pbm_size += inline_box_state.pbm.border.inline_end;
1023 self.cloneable_inline_box_end_pbm_size +=
1024 inline_box_state.pbm.margin.inline_end.auto_is(Au::zero);
1025 }
1026
1027 assert_eq!(
1031 self.inline_box_states.len(),
1032 inline_box.identifier.index_in_inline_boxes as usize
1033 );
1034 self.inline_box_states.push(inline_box_state.clone());
1035 self.inline_box_state_stack.push(inline_box_state);
1036 }
1037
1038 fn finish_inline_box(&mut self) {
1041 let inline_box_state = match self.inline_box_state_stack.pop() {
1042 Some(inline_box_state) => inline_box_state,
1043 None => return, };
1045 if inline_box_state.should_clone_pbm() {
1046 self.cloneable_inline_box_end_pbm_size -= inline_box_state.pbm.padding.inline_end;
1047 self.cloneable_inline_box_end_pbm_size -= inline_box_state.pbm.border.inline_end;
1048 self.cloneable_inline_box_end_pbm_size -=
1049 inline_box_state.pbm.margin.inline_end.auto_is(Au::zero);
1050 }
1051
1052 self.current_line_segment
1053 .max_block_size
1054 .max_assign(&inline_box_state.base.nested_strut_block_sizes);
1055
1056 if inline_box_state.base.has_content.get() {
1061 self.propagate_current_nesting_level_white_space_style();
1062 }
1063
1064 let padding = inline_box_state.pbm.padding.inline_end;
1065 let border = inline_box_state.pbm.border.inline_end;
1066 let margin = inline_box_state.pbm.margin.inline_end.auto_is(Au::zero);
1067 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
1070 self.current_line_segment.has_inline_pbm = true;
1071 }
1072 self.current_line_segment.inline_size += padding + border + margin;
1073 self.current_line_segment
1074 .line_items
1075 .push(LineItem::InlineEndBoxPaddingBorderMargin(
1076 inline_box_state.identifier,
1077 ))
1078 }
1079
1080 fn finish_last_line(&mut self) {
1081 self.possibly_flush_deferred_forced_line_break();
1083
1084 self.process_soft_wrap_opportunity();
1090
1091 self.commit_current_segment_to_line();
1094
1095 self.finish_current_line_and_reset(
1098 true, false, );
1101 }
1102
1103 fn finish_current_line_and_reset(
1107 &mut self,
1108 last_line_or_forced_line_break: bool,
1109 for_block_level: bool,
1110 ) {
1111 self.possibly_push_empty_text_run_to_line_for_text_caret();
1112
1113 let whitespace_trimmed = self.current_line.trim_trailing_whitespace();
1114 if !self.current_line.for_block_level {
1117 for inline_box in self.inline_box_state_stack.iter().rev() {
1118 if inline_box.should_clone_pbm() {
1119 self.current_line_segment.line_items.push(
1120 LineItem::InlineEndBoxPaddingBorderMargin(inline_box.identifier),
1121 );
1122 }
1123 }
1124 }
1125 let (inline_start_position, justification_adjustment) = self
1126 .calculate_current_line_inline_start_and_justification_adjustment(
1127 whitespace_trimmed,
1128 last_line_or_forced_line_break,
1129 );
1130
1131 let is_phantom_line = self.current_line.is_phantom();
1140 if !is_phantom_line {
1141 self.current_line.start_position.block += self.placement_state.current_margin.solve();
1142 self.placement_state.current_margin = CollapsedMargin::zero();
1143 }
1144 let block_start_position =
1145 self.current_line_block_start_considering_placement_among_floats();
1146
1147 let effective_block_advance = if is_phantom_line {
1148 LineBlockSizes::zero()
1149 } else {
1150 self.current_line_max_block_size_including_nested_containers()
1151 };
1152
1153 let resolved_block_advance = effective_block_advance.resolve();
1154 let block_end_position = if self.current_line.for_block_level {
1155 self.placement_state.current_block_direction_position
1156 } else {
1157 let mut block_end_position = block_start_position + resolved_block_advance;
1158 if let Some(sequential_layout_state) = self.sequential_layout_state.as_mut() {
1159 if !is_phantom_line {
1160 sequential_layout_state.commit_margin();
1161 }
1162
1163 let increment = block_end_position - self.current_line.start_position.block;
1166 sequential_layout_state.advance_block_position(increment);
1167
1168 if let Some(clearance) = sequential_layout_state
1172 .calculate_clearance(self.deferred_br_clear, &CollapsedMargin::zero())
1173 {
1174 sequential_layout_state.advance_block_position(clearance);
1175 block_end_position += clearance;
1176 };
1177 self.deferred_br_clear = Clear::None;
1178 }
1179 block_end_position
1180 };
1181
1182 let line_to_layout = std::mem::replace(
1184 &mut self.current_line,
1185 LineUnderConstruction::new(LogicalVec2 {
1186 inline: Au::zero(),
1187 block: block_end_position,
1188 }),
1189 );
1190 self.current_line.for_block_level = for_block_level;
1191
1192 if !for_block_level {
1195 for inline_box in self.inline_box_state_stack.iter() {
1196 if inline_box.should_clone_pbm() {
1197 self.current_line_segment.line_items.push(
1198 LineItem::InlineStartBoxPaddingBorderMargin(inline_box.identifier),
1199 );
1200 }
1201 }
1202 }
1203
1204 if !line_to_layout.for_block_level {
1205 self.placement_state.current_block_direction_position = block_end_position;
1206 }
1207
1208 if line_to_layout.has_floats_waiting_to_be_placed {
1209 place_pending_floats(self, &line_to_layout.line_items);
1210 }
1211
1212 let start_position = LogicalVec2 {
1213 block: block_start_position,
1214 inline: inline_start_position,
1215 };
1216
1217 let baseline_offset = effective_block_advance.find_baseline_offset();
1218 let start_positioning_context_length = self.positioning_context.len();
1219 let fragments = LineItemLayout::layout_line_items(
1220 self,
1221 line_to_layout.line_items,
1222 start_position,
1223 &effective_block_advance,
1224 justification_adjustment,
1225 is_phantom_line,
1226 line_to_layout.for_block_level,
1227 );
1228
1229 if !is_phantom_line {
1230 let baseline = baseline_offset + block_start_position;
1231 self.placement_state
1232 .inflow_baselines
1233 .first
1234 .get_or_insert(baseline);
1235 self.placement_state.inflow_baselines.last = Some(baseline);
1236 self.placement_state
1237 .next_in_flow_margin_collapses_with_parent_start_margin = false;
1238 }
1239
1240 if fragments.is_empty() &&
1242 self.positioning_context.len() == start_positioning_context_length
1243 {
1244 return;
1245 }
1246
1247 let start_corner = LogicalVec2 {
1251 inline: Au::zero(),
1252 block: block_start_position,
1253 };
1254
1255 let logical_origin_in_physical_coordinates =
1256 start_corner.to_physical_vector(self.containing_block().style.writing_mode);
1257 self.positioning_context
1258 .adjust_static_position_of_hoisted_fragments_with_offset(
1259 &logical_origin_in_physical_coordinates,
1260 start_positioning_context_length,
1261 );
1262
1263 let containing_block = self.containing_block();
1264 let physical_line_rect = LogicalRect {
1265 start_corner,
1266 size: LogicalVec2 {
1267 inline: containing_block.size.inline,
1268 block: effective_block_advance.resolve(),
1269 },
1270 }
1271 .as_physical(Some(containing_block));
1272 self.fragments
1273 .push(Fragment::Positioning(PositioningFragment::new_anonymous(
1274 self.root_nesting_level.style.clone(),
1275 physical_line_rect,
1276 fragments,
1277 true, )));
1279 }
1280
1281 fn calculate_current_line_inline_start_and_justification_adjustment(
1286 &self,
1287 whitespace_trimmed: Au,
1288 last_line_or_forced_line_break: bool,
1289 ) -> (Au, Au) {
1290 enum TextAlign {
1291 Start,
1292 Center,
1293 End,
1294 }
1295 let containing_block = self.containing_block();
1296 let style = containing_block.style;
1297 let mut text_align_keyword = style.clone_text_align();
1298
1299 if last_line_or_forced_line_break {
1300 text_align_keyword = match style.clone_text_align_last() {
1301 TextAlignLast::Auto if text_align_keyword == TextAlignKeyword::Justify => {
1302 TextAlignKeyword::Start
1303 },
1304 TextAlignLast::Auto => text_align_keyword,
1305 TextAlignLast::Start => TextAlignKeyword::Start,
1306 TextAlignLast::End => TextAlignKeyword::End,
1307 TextAlignLast::Left => TextAlignKeyword::Left,
1308 TextAlignLast::Right => TextAlignKeyword::Right,
1309 TextAlignLast::Center => TextAlignKeyword::Center,
1310 TextAlignLast::Justify => TextAlignKeyword::Justify,
1311 };
1312 }
1313
1314 let text_align = match text_align_keyword {
1315 TextAlignKeyword::Start => TextAlign::Start,
1316 TextAlignKeyword::Center | TextAlignKeyword::MozCenter => TextAlign::Center,
1317 TextAlignKeyword::End => TextAlign::End,
1318 TextAlignKeyword::Left | TextAlignKeyword::MozLeft => {
1319 if style.writing_mode.line_left_is_inline_start() {
1320 TextAlign::Start
1321 } else {
1322 TextAlign::End
1323 }
1324 },
1325 TextAlignKeyword::Right | TextAlignKeyword::MozRight => {
1326 if style.writing_mode.line_left_is_inline_start() {
1327 TextAlign::End
1328 } else {
1329 TextAlign::Start
1330 }
1331 },
1332 TextAlignKeyword::Justify => TextAlign::Start,
1333 };
1334
1335 let (line_start, available_space) = match self.current_line.placement_among_floats.get() {
1336 Some(placement_among_floats) => (
1337 placement_among_floats.start_corner.inline,
1338 placement_among_floats.size.inline,
1339 ),
1340 None => (Au::zero(), containing_block.size.inline),
1341 };
1342
1343 let text_indent = self.current_line.start_position.inline;
1350 let line_length = self.current_line.inline_position - whitespace_trimmed - text_indent;
1351 let adjusted_line_start = line_start +
1352 match text_align {
1353 TextAlign::Start => text_indent,
1354 TextAlign::End => (available_space - line_length).max(text_indent),
1355 TextAlign::Center => (available_space - line_length + text_indent)
1356 .scale_by(0.5)
1357 .max(text_indent),
1358 };
1359
1360 let text_justify = containing_block.style.clone_text_justify();
1364 let justification_adjustment = match (text_align_keyword, text_justify) {
1365 (TextAlignKeyword::Justify, TextJustify::None) => Au::zero(),
1368 (TextAlignKeyword::Justify, _) => {
1369 match self.current_line.count_justification_opportunities() {
1370 0 => Au::zero(),
1371 num_justification_opportunities => {
1372 (available_space - text_indent - line_length)
1373 .scale_by(1. / num_justification_opportunities as f32)
1374 },
1375 }
1376 },
1377 _ => Au::zero(),
1378 };
1379
1380 let justification_adjustment = justification_adjustment.max(Au::zero());
1383
1384 (adjusted_line_start, justification_adjustment)
1385 }
1386
1387 fn place_float_fragment(&mut self, float: &FloatLineItem) {
1388 let state = self
1389 .sequential_layout_state
1390 .as_mut()
1391 .expect("Tried to lay out a float with no sequential placement state!");
1392
1393 let block_offset_from_containining_block_top = state
1394 .current_block_position_including_margins() -
1395 state.current_containing_block_offset();
1396 state.place_float_fragment(
1397 &float.fragment,
1398 self.placement_state.containing_block,
1399 CollapsedMargin::zero(),
1400 block_offset_from_containining_block_top,
1401 );
1402 self.positioning_context
1403 .adjust_static_position_of_hoisted_fragments_in_range(
1404 &float.fragment.base.rect().origin.to_vector(),
1405 &float.range,
1406 )
1407 }
1408
1409 fn place_float_line_item_for_commit_to_line(
1418 &mut self,
1419 float_item: &mut FloatLineItem,
1420 line_inline_size_without_trailing_whitespace: Au,
1421 ) {
1422 let containing_block = self.containing_block();
1423 let float_fragment = &float_item.fragment;
1424 let logical_margin_rect_size = float_fragment
1425 .margin_rect()
1426 .size
1427 .to_logical(containing_block.style.writing_mode);
1428 let inline_size = logical_margin_rect_size.inline.max(Au::zero());
1429
1430 let available_inline_size = match self.current_line.placement_among_floats.get() {
1431 Some(placement_among_floats) => placement_among_floats.size.inline,
1432 None => containing_block.size.inline,
1433 } - line_inline_size_without_trailing_whitespace;
1434
1435 let has_content = self.current_line.has_content || self.current_line_segment.has_content;
1441 let fits_on_line = !has_content || inline_size <= available_inline_size;
1442 let needs_placement_later =
1443 self.current_line.has_floats_waiting_to_be_placed || !fits_on_line;
1444
1445 if needs_placement_later {
1446 self.current_line.has_floats_waiting_to_be_placed = true;
1447 } else {
1448 self.place_float_fragment(float_item);
1449 float_item.needs_placement = false;
1450 }
1451
1452 let new_placement = self.place_line_among_floats(&LogicalVec2 {
1457 inline: line_inline_size_without_trailing_whitespace,
1458 block: self.current_line.max_block_size.resolve(),
1459 });
1460 self.current_line
1461 .replace_placement_among_floats(new_placement);
1462 }
1463
1464 fn place_line_among_floats(&self, potential_line_size: &LogicalVec2<Au>) -> LogicalRect<Au> {
1469 let sequential_layout_state = self
1470 .sequential_layout_state
1471 .as_ref()
1472 .expect("Should not have called this function without having floats.");
1473
1474 let ifc_offset_in_float_container = LogicalVec2 {
1475 inline: sequential_layout_state
1476 .floats
1477 .containing_block_info
1478 .inline_start,
1479 block: sequential_layout_state.current_containing_block_offset(),
1480 };
1481
1482 let ceiling = self.current_line_block_start_considering_placement_among_floats();
1483 let mut placement = PlacementAmongFloats::new(
1484 &sequential_layout_state.floats,
1485 ceiling + ifc_offset_in_float_container.block,
1486 LogicalVec2 {
1487 inline: potential_line_size.inline,
1488 block: potential_line_size.block,
1489 },
1490 &PaddingBorderMargin::zero(),
1491 );
1492
1493 let mut placement_rect = placement.place();
1494 placement_rect.start_corner -= ifc_offset_in_float_container;
1495 placement_rect
1496 }
1497
1498 fn new_potential_line_size_causes_line_break(
1505 &mut self,
1506 potential_line_size: &LogicalVec2<Au>,
1507 ) -> bool {
1508 let containing_block = self.containing_block();
1509 let available_line_space = if self.sequential_layout_state.is_some() {
1510 self.current_line
1511 .placement_among_floats
1512 .get_or_init(|| self.place_line_among_floats(potential_line_size))
1513 .size
1514 } else {
1515 LogicalVec2 {
1516 inline: containing_block.size.inline,
1517 block: MAX_AU,
1518 }
1519 };
1520
1521 let inline_would_overflow = potential_line_size.inline > available_line_space.inline;
1522 let block_would_overflow = potential_line_size.block > available_line_space.block;
1523
1524 let can_break = self.current_line.has_content;
1527
1528 if !can_break {
1534 if self.sequential_layout_state.is_some() &&
1537 (inline_would_overflow || block_would_overflow)
1538 {
1539 let new_placement = self.place_line_among_floats(potential_line_size);
1540 self.current_line
1541 .replace_placement_among_floats(new_placement);
1542 }
1543
1544 return false;
1545 }
1546
1547 if potential_line_size.inline > containing_block.size.inline {
1550 return true;
1551 }
1552
1553 if block_would_overflow {
1557 assert!(self.sequential_layout_state.is_some());
1559 let new_placement = self.place_line_among_floats(potential_line_size);
1560 if new_placement.start_corner.block !=
1561 self.current_line_block_start_considering_placement_among_floats()
1562 {
1563 return true;
1564 } else {
1565 self.current_line
1566 .replace_placement_among_floats(new_placement);
1567 return false;
1568 }
1569 }
1570
1571 potential_line_size.inline + self.cloneable_inline_box_end_pbm_size >
1575 available_line_space.inline
1576 }
1577
1578 fn defer_forced_line_break_at_character_offset(&mut self, line_break_offset: usize) {
1579 if !self.unbreakable_segment_fits_on_line() {
1582 self.process_line_break(
1583 false, false, );
1586 }
1587
1588 self.force_line_break_before_new_content = Some(line_break_offset);
1590
1591 let line_is_empty =
1599 !self.current_line_segment.has_content && !self.current_line.has_content;
1600 if !self.processing_br_element() || line_is_empty {
1601 let strut_size = self
1602 .current_inline_container_state()
1603 .strut_block_sizes
1604 .clone();
1605 self.update_unbreakable_segment_for_new_content(
1606 &strut_size,
1607 Au::zero(),
1608 SegmentContentFlags::empty(),
1609 );
1610 }
1611 }
1612
1613 fn possibly_flush_deferred_forced_line_break(&mut self) {
1614 let Some(line_break_character_offset) = self.force_line_break_before_new_content.take()
1615 else {
1616 return;
1617 };
1618
1619 self.commit_current_segment_to_line();
1620 self.process_line_break(
1621 true, false, );
1624
1625 self.current_line.starting_character_offset = line_break_character_offset + 1;
1626 }
1627
1628 fn push_line_item_to_unbreakable_segment(&mut self, line_item: LineItem) {
1629 self.current_line_segment
1630 .push_line_item(line_item, self.inline_box_state_stack.len());
1631 }
1632
1633 fn push_glyph_store_to_unbreakable_segment(
1634 &mut self,
1635 glyph_store: Arc<ShapedTextSlice>,
1636 text_run: &TextRun,
1637 info: &FontAndScriptInfo,
1638 offsets: Option<TextRunOffsets>,
1639 ) {
1640 let inline_advance = glyph_store.total_advance();
1641 let flags = if glyph_store.is_whitespace() {
1642 SegmentContentFlags::from(text_run.inline_styles.style.borrow().get_inherited_text())
1643 } else {
1644 SegmentContentFlags::empty()
1645 };
1646
1647 let mut block_contribution = LineBlockSizes::zero();
1648 let quirks_mode = self.layout_context.style_context.quirks_mode() != QuirksMode::NoQuirks;
1649 let current_inline_container_state = self.current_inline_container_state();
1650 if quirks_mode && !flags.is_collapsible_whitespace() {
1651 block_contribution.max_assign(¤t_inline_container_state.strut_block_sizes);
1656 }
1657
1658 let font_metrics = &info.font_info.font.metrics;
1662 if current_inline_container_state
1663 .font_metrics
1664 .block_metrics_meaningfully_differ(font_metrics)
1665 {
1666 let baseline_shift = effective_baseline_shift(
1668 ¤t_inline_container_state.style,
1669 self.inline_box_state_stack.last().map(|c| &c.base),
1670 );
1671 let mut font_block_conribution = current_inline_container_state
1672 .get_block_size_contribution(
1673 baseline_shift,
1674 font_metrics,
1675 ¤t_inline_container_state.font_metrics,
1676 );
1677 font_block_conribution
1678 .adjust_for_baseline_offset(current_inline_container_state.baseline_offset);
1679 block_contribution.max_assign(&font_block_conribution);
1680 }
1681
1682 self.update_unbreakable_segment_for_new_content(&block_contribution, inline_advance, flags);
1683
1684 let current_inline_box_identifier = self.current_inline_box_identifier();
1685 if let Some(LineItem::TextRun(inline_box_identifier, line_item)) =
1686 self.current_line_segment.line_items.last_mut() &&
1687 *inline_box_identifier == current_inline_box_identifier &&
1688 line_item.merge_if_possible(info, &glyph_store, &offsets, &text_run.inline_styles)
1689 {
1690 return;
1691 }
1692
1693 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1694 current_inline_box_identifier,
1695 TextRunLineItem {
1696 text: vec![glyph_store],
1697 base_fragment_info: text_run.base_fragment_info,
1698 inline_styles: text_run.inline_styles.clone(),
1699 info: info.clone(),
1700 offsets: offsets.map(Box::new),
1701 is_empty_for_text_cursor: false,
1702 },
1703 ));
1704 }
1705
1706 fn possibly_push_empty_text_run_to_line_for_text_caret(&mut self) {
1709 let line_start_offset = self.current_line.starting_character_offset;
1710 let Some(shared_selection) = self.ifc.shared_selection.clone() else {
1711 return;
1712 };
1713 let offsets = TextRunOffsets {
1714 shared_selection,
1715 character_range: line_start_offset..line_start_offset + 1,
1716 };
1717
1718 if self
1720 .current_line
1721 .line_items
1722 .iter()
1723 .rev()
1724 .find(|line_item| line_item.is_in_flow_content())
1725 .is_some_and(|line_item| matches!(line_item, LineItem::TextRun(..)))
1726 {
1727 return;
1728 }
1729
1730 let inline_container_state = self.current_inline_container_state();
1731 let Some(font) = inline_container_state.default_font.clone() else {
1732 return;
1733 };
1734
1735 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1736 self.current_inline_box_identifier(),
1737 TextRunLineItem {
1738 text: Default::default(),
1739 base_fragment_info: BaseFragmentInfo::anonymous(),
1740 inline_styles: self.ifc.shared_inline_styles.clone(),
1741 info: FontAndScriptInfo::simple_for_font(font),
1742 offsets: Some(Box::new(offsets)),
1743 is_empty_for_text_cursor: true,
1744 },
1745 ));
1746 self.current_line_segment.has_content = true;
1747 self.commit_current_segment_to_line();
1748 }
1749
1750 fn update_unbreakable_segment_for_new_content(
1751 &mut self,
1752 block_sizes_of_content: &LineBlockSizes,
1753 inline_size: Au,
1754 flags: SegmentContentFlags,
1755 ) {
1756 if flags.is_collapsible_whitespace() || flags.is_wrappable_and_hangable() {
1757 self.current_line_segment.trailing_whitespace_size = inline_size;
1758 } else {
1759 self.current_line_segment.trailing_whitespace_size = Au::zero();
1760 }
1761 if !flags.is_collapsible_whitespace() {
1762 self.current_line_segment.has_content = true;
1763 }
1764
1765 let container_max_block_size = &self
1767 .current_inline_container_state()
1768 .nested_strut_block_sizes
1769 .clone();
1770 self.current_line_segment
1771 .max_block_size
1772 .max_assign(container_max_block_size);
1773 self.current_line_segment
1774 .max_block_size
1775 .max_assign(block_sizes_of_content);
1776
1777 self.current_line_segment.inline_size += inline_size;
1778
1779 self.current_inline_container_state().has_content.set(true);
1781 self.propagate_current_nesting_level_white_space_style();
1782 }
1783
1784 fn process_line_break(&mut self, forced_line_break: bool, for_block_level: bool) {
1785 self.current_line_segment.trim_leading_whitespace();
1786 self.finish_current_line_and_reset(forced_line_break, for_block_level);
1787 }
1788
1789 fn potential_line_size(&self) -> LogicalVec2<Au> {
1790 LogicalVec2 {
1791 inline: self.current_line.inline_position + self.current_line_segment.inline_size,
1792 block: self
1793 .current_line_max_block_size_including_nested_containers()
1794 .max(&self.current_line_segment.max_block_size)
1795 .resolve(),
1796 }
1797 }
1798
1799 fn unbreakable_segment_fits_on_line(&mut self) -> bool {
1800 let potential_line_size_without_hanging_whitespace = self.potential_line_size() -
1801 LogicalVec2 {
1802 inline: self.current_line_segment.trailing_whitespace_size,
1803 block: Au::zero(),
1804 };
1805 !self.new_potential_line_size_causes_line_break(
1806 &potential_line_size_without_hanging_whitespace,
1807 )
1808 }
1809
1810 fn process_soft_wrap_opportunity(&mut self) {
1814 if self.current_line_segment.line_items.is_empty() {
1815 return;
1816 }
1817 if self.text_wrap_mode == TextWrapMode::Nowrap {
1818 return;
1819 }
1820 if !self.unbreakable_segment_fits_on_line() {
1821 self.process_line_break(
1822 false, false, );
1825 }
1826 self.commit_current_segment_to_line();
1827 }
1828
1829 fn commit_current_segment_to_line(&mut self) {
1832 if self.current_line_segment.line_items.is_empty() && !self.current_line_segment.has_content
1835 {
1836 return;
1837 }
1838
1839 if !self.current_line.has_content {
1840 self.current_line_segment.trim_leading_whitespace();
1841 }
1842
1843 self.current_line.inline_position += self.current_line_segment.inline_size;
1844 self.current_line.max_block_size = self
1845 .current_line_max_block_size_including_nested_containers()
1846 .max(&self.current_line_segment.max_block_size);
1847 let line_inline_size_without_trailing_whitespace =
1848 self.current_line.inline_position - self.current_line_segment.trailing_whitespace_size;
1849
1850 let mut segment_items = mem::take(&mut self.current_line_segment.line_items);
1852 for item in segment_items.iter_mut() {
1853 if let LineItem::Float(_, float_item) = item {
1854 self.place_float_line_item_for_commit_to_line(
1855 float_item,
1856 line_inline_size_without_trailing_whitespace,
1857 );
1858 }
1859 }
1860
1861 if self.current_line.line_items.is_empty() {
1866 let will_break = self.new_potential_line_size_causes_line_break(&LogicalVec2 {
1867 inline: line_inline_size_without_trailing_whitespace,
1868 block: self.current_line_segment.max_block_size.resolve(),
1869 });
1870 assert!(!will_break);
1871 }
1872
1873 self.current_line.line_items.extend(segment_items);
1874 self.current_line.has_content |= self.current_line_segment.has_content;
1875 self.current_line.has_inline_pbm |= self.current_line_segment.has_inline_pbm;
1876
1877 self.current_line_segment.reset();
1878 }
1879
1880 #[inline]
1881 fn containing_block(&self) -> &ContainingBlock<'_> {
1882 self.placement_state.containing_block
1883 }
1884}
1885
1886bitflags! {
1887 struct SegmentContentFlags: u8 {
1888 const COLLAPSIBLE_WHITESPACE = 0b00000001;
1889 const WRAPPABLE_AND_HANGABLE_WHITESPACE = 0b00000010;
1890 }
1891}
1892
1893impl SegmentContentFlags {
1894 fn is_collapsible_whitespace(&self) -> bool {
1895 self.contains(Self::COLLAPSIBLE_WHITESPACE)
1896 }
1897
1898 fn is_wrappable_and_hangable(&self) -> bool {
1899 self.contains(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE)
1900 }
1901}
1902
1903impl From<&InheritedText> for SegmentContentFlags {
1904 fn from(style_text: &InheritedText) -> Self {
1905 let mut flags = Self::empty();
1906
1907 if !matches!(
1910 style_text.white_space_collapse,
1911 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
1912 ) {
1913 flags.insert(Self::COLLAPSIBLE_WHITESPACE);
1914 }
1915
1916 if style_text.text_wrap_mode == TextWrapMode::Wrap &&
1919 style_text.white_space_collapse != WhiteSpaceCollapse::BreakSpaces
1920 {
1921 flags.insert(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE);
1922 }
1923 flags
1924 }
1925}
1926
1927impl InlineFormattingContext {
1928 #[servo_tracing::instrument(name = "InlineFormattingContext::new_with_builder", skip_all)]
1929 fn new_with_builder(
1930 mut builder: InlineFormattingContextBuilder,
1931 layout_context: &LayoutContext,
1932 has_first_formatted_line: bool,
1933 is_single_line_text_input: bool,
1934 starting_bidi_level: Level,
1935 ) -> Self {
1936 let text_content: String = builder.text_segments.into_iter().collect();
1938
1939 let bidi_levels = BidiLevels {
1940 info: builder
1941 .has_right_to_left_content
1942 .then(|| BidiInfo::new(&text_content, Some(starting_bidi_level))),
1943 };
1944
1945 let shared_inline_styles = builder
1946 .shared_inline_styles_stack
1947 .last()
1948 .expect("Should have at least one SharedInlineStyle for the root of an IFC")
1949 .clone();
1950 let (word_break, line_break, lang) = {
1951 let styles = shared_inline_styles.style.borrow();
1952 let text_style = styles.get_inherited_text();
1953 (
1954 text_style.word_break,
1955 text_style.line_break,
1956 styles.get_font()._x_lang.clone(),
1957 )
1958 };
1959
1960 let mut options = LineBreakOptions::default();
1961
1962 options.strictness = match line_break {
1963 LineBreak::Loose => LineBreakStrictness::Loose,
1964 LineBreak::Normal => LineBreakStrictness::Normal,
1965 LineBreak::Strict => LineBreakStrictness::Strict,
1966 LineBreak::Anywhere => LineBreakStrictness::Anywhere,
1967 LineBreak::Auto => LineBreakStrictness::Normal,
1970 };
1971 options.word_option = match word_break {
1972 WordBreak::Normal => LineBreakWordOption::Normal,
1973 WordBreak::BreakAll => LineBreakWordOption::BreakAll,
1974 WordBreak::KeepAll => LineBreakWordOption::KeepAll,
1975 };
1976 options.ja_zh = {
1979 lang.0.parse::<LanguageIdentifier>().is_ok_and(|lang_id| {
1980 const JA: Language = language!("ja");
1981 const ZH: Language = language!("zh");
1982 matches!(lang_id.language, JA | ZH)
1983 })
1984 };
1985
1986 let mut shaping_queue = ShapingQueue::new(&text_content, options);
1987 for item in &mut builder.inline_items {
1988 match item {
1989 InlineItem::TextRun(text_run) => {
1990 let shaping_queue_entries = text_run.borrow_mut().segment(
1991 text_run.clone(),
1992 &text_content,
1993 layout_context,
1994 &bidi_levels,
1995 );
1996 for entry in shaping_queue_entries.into_iter() {
1997 shaping_queue.push(entry);
1998 }
1999 },
2000 InlineItem::StartInlineBox(inline_box) => {
2001 let inline_box = &mut *inline_box.borrow_mut();
2002 if let Some(font) = get_font_for_first_font_for_style(
2003 &inline_box.base.style,
2004 &layout_context.font_context,
2005 ) {
2006 inline_box.default_font = Some(font);
2007 }
2008
2009 if inline_box.breaks_shaping_at_start {
2010 shaping_queue.flush();
2011 }
2012 },
2013 InlineItem::Atomic(_, index_in_text, bidi_level) => {
2014 shaping_queue.flush();
2015 *bidi_level = bidi_levels.level(*index_in_text);
2016 },
2017 InlineItem::EndInlineBox(inline_box) => {
2018 if inline_box.borrow().breaks_shaping_at_end {
2019 shaping_queue.flush();
2020 }
2021 },
2022 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) |
2023 InlineItem::OutOfFlowFloatBox(_) |
2024 InlineItem::BlockLevel { .. } => {},
2025 }
2026 }
2027
2028 shaping_queue.flush();
2029
2030 let default_font = get_font_for_first_font_for_style(
2031 &shared_inline_styles.style.borrow(),
2032 &layout_context.font_context,
2033 );
2034
2035 let has_right_to_left_content = bidi_levels.info.as_ref().is_some_and(BidiInfo::has_rtl);
2036 InlineFormattingContext {
2037 text_content,
2038 inline_items: builder.inline_items,
2039 inline_boxes: builder.inline_boxes,
2040 shared_inline_styles,
2041 default_font,
2042 has_first_formatted_line,
2043 contains_floats: builder.contains_floats,
2044 is_single_line_text_input,
2045 has_right_to_left_content,
2046 shared_selection: builder.shared_selection,
2047 tab_size_multiplier: Default::default(),
2048 }
2049 }
2050
2051 pub(crate) fn repair_style(
2052 &self,
2053 context: &SharedStyleContext,
2054 node: &ServoLayoutNode,
2055 new_style: &ServoArc<ComputedValues>,
2056 ) {
2057 *self.shared_inline_styles.style.borrow_mut() = new_style.clone();
2058 *self.shared_inline_styles.selected.borrow_mut() = node.selected_style(context);
2059 }
2060
2061 fn inline_start_for_first_line(&self, containing_block: IndefiniteContainingBlock) -> Au {
2062 if !self.has_first_formatted_line {
2063 return Au::zero();
2064 }
2065 containing_block
2066 .style
2067 .get_inherited_text()
2068 .text_indent
2069 .length
2070 .to_used_value(containing_block.size.inline.unwrap_or_default())
2071 }
2072
2073 pub(super) fn layout(
2074 &self,
2075 layout_context: &LayoutContext,
2076 positioning_context: &mut PositioningContext,
2077 containing_block: &ContainingBlock,
2078 sequential_layout_state: Option<&mut SequentialLayoutState>,
2079 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
2080 ) -> IndependentFormattingContextLayoutResult {
2081 for inline_box in self.inline_boxes.iter() {
2083 inline_box.borrow().base.clear_fragments();
2084 }
2085
2086 let style = containing_block.style;
2087
2088 let style_text = containing_block.style.get_inherited_text();
2089 let mut inline_container_state_flags = InlineContainerStateFlags::empty();
2090 if inline_container_needs_strut(style, layout_context, None) {
2091 inline_container_state_flags.insert(InlineContainerStateFlags::CREATE_STRUT);
2092 }
2093 if self.is_single_line_text_input {
2094 inline_container_state_flags
2095 .insert(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT);
2096 }
2097 let placement_state =
2098 PlacementState::new(collapsible_with_parent_start_margin, containing_block);
2099
2100 let mut layout = InlineFormattingContextLayout {
2101 positioning_context,
2102 placement_state,
2103 sequential_layout_state,
2104 layout_context,
2105 ifc: self,
2106 fragments: Vec::new(),
2107 current_line: LineUnderConstruction::new(LogicalVec2 {
2108 inline: self.inline_start_for_first_line(containing_block.into()),
2109 block: Au::zero(),
2110 }),
2111 root_nesting_level: InlineContainerState::new(
2112 style.to_arc(),
2113 inline_container_state_flags,
2114 None, self.default_font.clone(),
2116 ),
2117 inline_box_state_stack: Vec::new(),
2118 cloneable_inline_box_end_pbm_size: Au::zero(),
2119 inline_box_states: Vec::with_capacity(self.inline_boxes.len()),
2120 current_line_segment: UnbreakableSegmentUnderConstruction::new(),
2121 force_line_break_before_new_content: None,
2122 deferred_br_clear: Clear::None,
2123 have_deferred_soft_wrap_opportunity: false,
2124 depends_on_block_constraints: false,
2125 white_space_collapse: style_text.white_space_collapse,
2126 text_wrap_mode: style_text.text_wrap_mode,
2127 };
2128
2129 for item in self.inline_items.iter() {
2130 if !matches!(item, InlineItem::EndInlineBox(..)) {
2132 layout.possibly_flush_deferred_forced_line_break();
2133 }
2134
2135 match item {
2136 InlineItem::StartInlineBox(inline_box) => {
2137 layout.start_inline_box(&inline_box.borrow());
2138 },
2139 InlineItem::EndInlineBox(..) => layout.finish_inline_box(),
2140 InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout),
2141 InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
2142 atomic_formatting_context.borrow().layout_into_line_items(
2143 &mut layout,
2144 *offset_in_text,
2145 *bidi_level,
2146 );
2147 },
2148 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, _) => {
2149 layout.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
2150 layout.current_inline_box_identifier(),
2151 AbsolutelyPositionedLineItem {
2152 absolutely_positioned_box: positioned_box.clone(),
2153 preceding_line_content_would_produce_phantom_line: layout
2154 .current_line
2155 .is_phantom() &&
2156 layout.current_line_segment.is_phantom(),
2157 },
2158 ));
2159 },
2160 InlineItem::OutOfFlowFloatBox(float_box) => {
2161 float_box.borrow().layout_into_line_items(&mut layout);
2162 },
2163 InlineItem::BlockLevel(block_level) => {
2164 block_level.borrow().layout_into_line_items(&mut layout);
2165 },
2166 }
2167 }
2168
2169 layout.finish_last_line();
2170 let (content_block_size, collapsible_margins_in_children, baselines) =
2171 layout.placement_state.finish();
2172
2173 IndependentFormattingContextLayoutResult {
2174 fragments: layout.fragments,
2175 content_block_size,
2176 collapsible_margins_in_children,
2177 baselines,
2178 depends_on_block_constraints: layout.depends_on_block_constraints,
2179 content_inline_size_for_table: None,
2180 specific_layout_info: None,
2181 }
2182 }
2183
2184 pub(crate) fn subtree_size(&self) -> usize {
2185 self.inline_items
2186 .iter()
2187 .map(|item| match item {
2188 InlineItem::StartInlineBox(..) => 1,
2189 InlineItem::EndInlineBox(..) => 0,
2190 InlineItem::TextRun(..) => 1,
2191 InlineItem::OutOfFlowAbsolutelyPositionedBox(absolutely_positioned_box, _) => {
2192 absolutely_positioned_box
2193 .borrow()
2194 .context
2195 .base
2196 .subtree_size()
2197 },
2198 InlineItem::OutOfFlowFloatBox(..) => 1,
2199 InlineItem::Atomic(..) => 1,
2200 InlineItem::BlockLevel(block_level_box) => block_level_box.borrow().subtree_size(),
2201 })
2202 .sum()
2203 }
2204
2205 fn next_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
2206 let Some(character) = self.text_content[index..].chars().nth(1) else {
2207 return false;
2208 };
2209 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2210 }
2211
2212 fn previous_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
2213 let Some(character) = self.text_content[0..index].chars().next_back() else {
2214 return false;
2215 };
2216 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2217 }
2218
2219 pub(crate) fn find_block_margin_collapsing_with_parent(
2220 &self,
2221 layout_context: &LayoutContext,
2222 collected_margin: &mut CollapsedMargin,
2223 containing_block_for_children: &ContainingBlock,
2224 ) -> bool {
2225 let mut items_iter = self.inline_items.iter();
2231 items_iter.all(|inline_item| match inline_item {
2232 InlineItem::StartInlineBox(inline_box) => {
2233 let pbm = inline_box
2234 .borrow()
2235 .layout_style()
2236 .padding_border_margin(containing_block_for_children);
2237 pbm.padding.inline_start.is_zero() &&
2238 pbm.border.inline_start.is_zero() &&
2239 pbm.margin.inline_start.auto_is(Au::zero).is_zero()
2240 },
2241 InlineItem::EndInlineBox(inline_box) => {
2242 let pbm = inline_box
2243 .borrow()
2244 .layout_style()
2245 .padding_border_margin(containing_block_for_children);
2246 pbm.padding.inline_end.is_zero() &&
2247 pbm.border.inline_end.is_zero() &&
2248 pbm.margin.inline_end.auto_is(Au::zero).is_zero()
2249 },
2250 InlineItem::TextRun(text_run) => {
2251 let text_run = &*text_run.borrow();
2252 let parent_style = text_run.inline_styles.style.borrow();
2253 text_run.items.iter().all(|item| match item {
2254 TextRunItem::LineBreak { .. } => false,
2255 TextRunItem::Tab { .. } => false,
2256 TextRunItem::TextSegment(segment) => segment.runs.iter().all(|run| {
2257 run.is_whitespace() &&
2258 !matches!(
2259 parent_style.get_inherited_text().white_space_collapse,
2260 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
2261 )
2262 }),
2263 })
2264 },
2265 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => true,
2266 InlineItem::OutOfFlowFloatBox(..) => true,
2267 InlineItem::Atomic(..) => false,
2268 InlineItem::BlockLevel(block_level) => block_level
2269 .borrow()
2270 .find_block_margin_collapsing_with_parent(
2271 layout_context,
2272 collected_margin,
2273 containing_block_for_children,
2274 ),
2275 })
2276 }
2277
2278 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
2279 let mut parent_box_stack = Vec::new();
2280 let current_parent_box = |parent_box_stack: &[WeakLayoutBox]| {
2281 parent_box_stack.last().unwrap_or(&layout_box).clone()
2282 };
2283 for inline_item in &self.inline_items {
2284 match inline_item {
2285 InlineItem::StartInlineBox(inline_box) => {
2286 inline_box
2287 .borrow_mut()
2288 .base
2289 .parent_box
2290 .replace(current_parent_box(&parent_box_stack));
2291 parent_box_stack.push(WeakLayoutBox::InlineLevel(
2292 WeakInlineItem::StartInlineBox(inline_box.downgrade()),
2293 ));
2294 },
2295 InlineItem::EndInlineBox(..) => {
2296 parent_box_stack.pop();
2297 },
2298 InlineItem::TextRun(text_run) => {
2299 text_run
2300 .borrow_mut()
2301 .parent_box
2302 .replace(current_parent_box(&parent_box_stack));
2303 },
2304 _ => inline_item.with_base_mut(|base| {
2305 base.parent_box
2306 .replace(current_parent_box(&parent_box_stack));
2307 }),
2308 }
2309 }
2310 }
2311
2312 pub(crate) fn next_tab_stop_after_inline_advance(
2313 &self,
2314 style: &ServoArc<ComputedValues>,
2315 current_inline_advance: Au,
2316 ) -> Au {
2317 let Some(font) = self.default_font.as_ref() else {
2318 return Au::zero();
2319 };
2320
2321 let tab_size_multiplier = *self.tab_size_multiplier.get_or_init(|| {
2322 let root_style = self.shared_inline_styles.style.borrow();
2323 let inherited_text_style = root_style.get_inherited_text();
2324 let font_size = root_style.get_font().font_size.computed_size().into();
2325 let letter_spacing = inherited_text_style
2326 .letter_spacing
2327 .0
2328 .to_used_value(font_size);
2329 let word_spacing = inherited_text_style.word_spacing.to_used_value(font_size);
2330
2331 font.metrics.space_advance + word_spacing + letter_spacing
2334 });
2335
2336 let tab_stop_advance = match style.get_inherited_text().tab_size {
2337 style::values::generics::length::LengthOrNumber::Number(number_of_spaces) => {
2338 tab_size_multiplier.scale_by(number_of_spaces.0)
2339 },
2340 style::values::generics::length::LengthOrNumber::Length(length) => length.into(),
2342 };
2343
2344 if tab_stop_advance.is_zero() {
2345 return Au::zero();
2346 }
2347
2348 let half_ch_advance = font
2354 .metrics
2355 .zero_horizontal_advance
2356 .unwrap_or(font.metrics.em_size.scale_by(0.5))
2357 .scale_by(0.5);
2358 let number_of_tab_stops =
2359 (current_inline_advance + half_ch_advance).to_f32_px() / tab_stop_advance.to_f32_px();
2360 let number_of_tab_stops = number_of_tab_stops.ceil();
2361 tab_stop_advance.scale_by(number_of_tab_stops) - current_inline_advance
2362 }
2363}
2364
2365impl InlineContainerState {
2366 fn new(
2367 style: ServoArc<ComputedValues>,
2368 flags: InlineContainerStateFlags,
2369 parent_container: Option<&InlineContainerState>,
2370 default_font: Option<FontRef>,
2371 ) -> Self {
2372 let font_metrics = default_font
2373 .as_ref()
2374 .map(|font| font.metrics.clone())
2375 .unwrap_or_else(FontMetrics::empty);
2376 let mut baseline_offset = Au::zero();
2377 let mut strut_block_sizes = {
2378 Self::get_block_sizes_with_style(
2379 effective_baseline_shift(&style, parent_container),
2380 &style,
2381 &font_metrics,
2382 &font_metrics,
2383 &flags,
2384 )
2385 };
2386
2387 if let Some(parent_container) = parent_container {
2388 baseline_offset = parent_container.get_cumulative_baseline_offset_for_child(
2391 style.clone_alignment_baseline(),
2392 style.clone_baseline_shift(),
2393 &strut_block_sizes,
2394 );
2395 strut_block_sizes.adjust_for_baseline_offset(baseline_offset);
2396 }
2397
2398 let mut nested_block_sizes = parent_container
2399 .map(|container| container.nested_strut_block_sizes.clone())
2400 .unwrap_or_else(LineBlockSizes::zero);
2401 if flags.contains(InlineContainerStateFlags::CREATE_STRUT) {
2402 nested_block_sizes.max_assign(&strut_block_sizes);
2403 }
2404
2405 Self {
2406 style,
2407 flags,
2408 has_content: Cell::new(false),
2409 nested_strut_block_sizes: nested_block_sizes,
2410 strut_block_sizes,
2411 baseline_offset,
2412 default_font,
2413 font_metrics,
2414 }
2415 }
2416
2417 fn get_block_sizes_with_style(
2418 baseline_shift: BaselineShift,
2419 style: &ComputedValues,
2420 font_metrics: &FontMetrics,
2421 font_metrics_of_first_font: &FontMetrics,
2422 flags: &InlineContainerStateFlags,
2423 ) -> LineBlockSizes {
2424 let line_height = line_height(style, font_metrics, flags);
2425
2426 if !is_baseline_relative(baseline_shift) {
2427 return LineBlockSizes {
2428 line_height,
2429 baseline_relative_size_for_line_height: None,
2430 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2431 };
2432 }
2433
2434 let mut ascent = font_metrics.ascent;
2443 let mut descent = font_metrics.descent;
2444 if style.get_font().line_height == LineHeight::Normal {
2445 let half_leading_from_line_gap =
2446 (font_metrics.line_gap - descent - ascent).scale_by(0.5);
2447 ascent += half_leading_from_line_gap;
2448 descent += half_leading_from_line_gap;
2449 }
2450
2451 let size_for_baseline_positioning = BaselineRelativeSize { ascent, descent };
2455
2456 if style.get_font().line_height != LineHeight::Normal {
2472 ascent = font_metrics_of_first_font.ascent;
2473 descent = font_metrics_of_first_font.descent;
2474 let half_leading = (line_height - (ascent + descent)).scale_by(0.5);
2475 ascent += half_leading;
2480 descent = line_height - ascent;
2481 }
2482
2483 LineBlockSizes {
2484 line_height,
2485 baseline_relative_size_for_line_height: Some(BaselineRelativeSize { ascent, descent }),
2486 size_for_baseline_positioning,
2487 }
2488 }
2489
2490 fn get_block_size_contribution(
2491 &self,
2492 baseline_shift: BaselineShift,
2493 font_metrics: &FontMetrics,
2494 font_metrics_of_first_font: &FontMetrics,
2495 ) -> LineBlockSizes {
2496 Self::get_block_sizes_with_style(
2497 baseline_shift,
2498 &self.style,
2499 font_metrics,
2500 font_metrics_of_first_font,
2501 &self.flags,
2502 )
2503 }
2504
2505 fn get_cumulative_baseline_offset_for_child(
2506 &self,
2507 child_alignment_baseline: AlignmentBaseline,
2508 child_baseline_shift: BaselineShift,
2509 child_block_size: &LineBlockSizes,
2510 ) -> Au {
2511 let block_size = self.get_block_size_contribution(
2512 child_baseline_shift.clone(),
2513 &self.font_metrics,
2514 &self.font_metrics,
2515 );
2516 self.baseline_offset +
2517 match child_alignment_baseline {
2518 AlignmentBaseline::Baseline => Au::zero(),
2519 AlignmentBaseline::TextTop => {
2520 child_block_size.size_for_baseline_positioning.ascent - self.font_metrics.ascent
2521 },
2522 AlignmentBaseline::Middle => {
2523 (child_block_size.size_for_baseline_positioning.ascent -
2526 child_block_size.size_for_baseline_positioning.descent -
2527 self.font_metrics.x_height)
2528 .scale_by(0.5)
2529 },
2530 AlignmentBaseline::TextBottom => {
2531 self.font_metrics.descent -
2532 child_block_size.size_for_baseline_positioning.descent
2533 },
2534 } +
2535 match child_baseline_shift {
2536 BaselineShift::Keyword(
2541 BaselineShiftKeyword::Top |
2542 BaselineShiftKeyword::Bottom |
2543 BaselineShiftKeyword::Center,
2544 ) => Au::zero(),
2545 BaselineShift::Keyword(BaselineShiftKeyword::Sub) => {
2546 block_size.resolve().scale_by(FONT_SUBSCRIPT_OFFSET_RATIO)
2547 },
2548 BaselineShift::Keyword(BaselineShiftKeyword::Super) => {
2549 -block_size.resolve().scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO)
2550 },
2551 BaselineShift::Length(length_percentage) => {
2552 -length_percentage.to_used_value(child_block_size.line_height)
2553 },
2554 }
2555 }
2556}
2557
2558impl IndependentFormattingContext {
2559 fn layout_into_line_items(
2560 &self,
2561 layout: &mut InlineFormattingContextLayout,
2562 offset_in_text: usize,
2563 bidi_level: Level,
2564 ) {
2565 let mut child_positioning_context = PositioningContext::default();
2567 let IndependentFloatOrAtomicLayoutResult {
2568 mut fragment,
2569 baselines,
2570 pbm_sums,
2571 } = self.layout_float_or_atomic_inline(
2572 layout.layout_context,
2573 &mut child_positioning_context,
2574 layout.containing_block(),
2575 );
2576
2577 layout.depends_on_block_constraints |= fragment.base.flags.contains(
2580 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2581 );
2582
2583 let container_writing_mode = layout.containing_block().style.writing_mode;
2585 let pbm_physical_offset = pbm_sums
2586 .start_offset()
2587 .to_physical_size(container_writing_mode);
2588 fragment.base.translate_rect(pbm_physical_offset);
2589
2590 fragment = fragment.with_baselines(baselines);
2592
2593 let positioning_context = if self.is_replaced() {
2596 None
2597 } else {
2598 if fragment
2599 .style()
2600 .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
2601 {
2602 child_positioning_context
2603 .layout_collected_children(layout.layout_context, &mut fragment);
2604 }
2605 Some(child_positioning_context)
2606 };
2607
2608 if layout.text_wrap_mode == TextWrapMode::Wrap &&
2609 !layout
2610 .ifc
2611 .previous_character_prevents_soft_wrap_opportunity(offset_in_text)
2612 {
2613 layout.process_soft_wrap_opportunity();
2614 }
2615
2616 let size = pbm_sums.sum() + fragment.base.rect().size.to_logical(container_writing_mode);
2617 let baseline_offset = self
2618 .pick_baseline(&fragment.baselines(container_writing_mode))
2619 .map(|baseline| pbm_sums.block_start + baseline)
2620 .unwrap_or(size.block);
2621
2622 let (block_sizes, baseline_offset_in_parent) =
2623 self.get_block_sizes_and_baseline_offset(layout, size.block, baseline_offset);
2624 layout.update_unbreakable_segment_for_new_content(
2625 &block_sizes,
2626 size.inline,
2627 SegmentContentFlags::empty(),
2628 );
2629
2630 let fragment = Arc::new(fragment);
2631 self.base.set_fragment(Fragment::Box(fragment.clone()));
2632
2633 layout.push_line_item_to_unbreakable_segment(LineItem::Atomic(
2634 layout.current_inline_box_identifier(),
2635 AtomicLineItem {
2636 fragment,
2637 size,
2638 positioning_context,
2639 baseline_offset_in_parent,
2640 baseline_offset_in_item: baseline_offset,
2641 bidi_level,
2642 },
2643 ));
2644
2645 if !layout
2648 .ifc
2649 .next_character_prevents_soft_wrap_opportunity(offset_in_text)
2650 {
2651 layout.have_deferred_soft_wrap_opportunity = true;
2652 }
2653 }
2654
2655 fn pick_baseline(&self, baselines: &Baselines) -> Option<Au> {
2659 match self.style().clone_baseline_source() {
2660 BaselineSource::First => baselines.first,
2661 BaselineSource::Last => baselines.last,
2662 BaselineSource::Auto if self.is_block_container() => baselines.last,
2663 BaselineSource::Auto => baselines.first,
2664 }
2665 }
2666
2667 fn get_block_sizes_and_baseline_offset(
2668 &self,
2669 ifc: &InlineFormattingContextLayout,
2670 block_size: Au,
2671 baseline_offset_in_content_area: Au,
2672 ) -> (LineBlockSizes, Au) {
2673 let mut contribution = if !is_baseline_relative(self.style().clone_baseline_shift()) {
2674 LineBlockSizes {
2675 line_height: block_size,
2676 baseline_relative_size_for_line_height: None,
2677 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2678 }
2679 } else {
2680 let baseline_relative_size = BaselineRelativeSize {
2681 ascent: baseline_offset_in_content_area,
2682 descent: block_size - baseline_offset_in_content_area,
2683 };
2684 LineBlockSizes {
2685 line_height: block_size,
2686 baseline_relative_size_for_line_height: Some(baseline_relative_size.clone()),
2687 size_for_baseline_positioning: baseline_relative_size,
2688 }
2689 };
2690
2691 let style = self.style();
2692 let baseline_offset = ifc
2693 .current_inline_container_state()
2694 .get_cumulative_baseline_offset_for_child(
2695 style.clone_alignment_baseline(),
2696 style.clone_baseline_shift(),
2697 &contribution,
2698 );
2699 contribution.adjust_for_baseline_offset(baseline_offset);
2700
2701 (contribution, baseline_offset)
2702 }
2703}
2704
2705impl FloatBox {
2706 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
2707 let old_len = layout.positioning_context.len();
2708 let fragment = Arc::new(self.layout(
2709 layout.layout_context,
2710 layout.positioning_context,
2711 layout.placement_state.containing_block,
2712 ));
2713 let new_len = layout.positioning_context.len();
2714
2715 self.contents
2716 .base
2717 .set_fragment(Fragment::Box(fragment.clone()));
2718 layout.push_line_item_to_unbreakable_segment(LineItem::Float(
2719 layout.current_inline_box_identifier(),
2720 FloatLineItem {
2721 fragment,
2722 needs_placement: true,
2723 range: old_len..new_len,
2724 },
2725 ));
2726 }
2727}
2728
2729fn place_pending_floats(ifc: &mut InlineFormattingContextLayout, line_items: &[LineItem]) {
2730 for item in line_items.iter() {
2731 if let LineItem::Float(_, float_line_item) = item &&
2732 float_line_item.needs_placement
2733 {
2734 ifc.place_float_fragment(float_line_item);
2735 }
2736 }
2737}
2738
2739fn line_height(
2740 parent_style: &ComputedValues,
2741 font_metrics: &FontMetrics,
2742 flags: &InlineContainerStateFlags,
2743) -> Au {
2744 let font = parent_style.get_font();
2745 let font_size = font.font_size.computed_size();
2746 let mut line_height = match font.line_height {
2747 LineHeight::Normal => font_metrics.line_gap,
2748 LineHeight::Number(number) => (font_size * number.0).into(),
2749 LineHeight::Length(length) => length.0.into(),
2750 };
2751
2752 if flags.contains(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT) {
2756 line_height.max_assign(font_metrics.line_gap);
2757 }
2758
2759 line_height
2760}
2761
2762fn effective_baseline_shift(
2763 style: &ComputedValues,
2764 container: Option<&InlineContainerState>,
2765) -> BaselineShift {
2766 if container.is_none() {
2767 BaselineShift::zero()
2771 } else {
2772 style.clone_baseline_shift()
2773 }
2774}
2775
2776fn is_baseline_relative(baseline_shift: BaselineShift) -> bool {
2777 !matches!(
2778 baseline_shift,
2779 BaselineShift::Keyword(
2780 BaselineShiftKeyword::Top | BaselineShiftKeyword::Bottom | BaselineShiftKeyword::Center
2781 )
2782 )
2783}
2784
2785fn inline_container_needs_strut(
2811 style: &ComputedValues,
2812 layout_context: &LayoutContext,
2813 pbm: Option<&PaddingBorderMargin>,
2814) -> bool {
2815 if layout_context.style_context.quirks_mode() == QuirksMode::NoQuirks {
2816 return true;
2817 }
2818
2819 if style.get_box().display.is_list_item() {
2822 return true;
2823 }
2824
2825 pbm.is_some_and(|pbm| !pbm.padding_border_sums.inline.is_zero())
2826}
2827
2828impl ComputeInlineContentSizes for InlineFormattingContext {
2829 fn compute_inline_content_sizes(
2833 &self,
2834 layout_context: &LayoutContext,
2835 constraint_space: &ConstraintSpace,
2836 ) -> InlineContentSizesResult {
2837 ContentSizesComputation::compute(self, layout_context, constraint_space)
2838 }
2839}
2840
2841struct ContentSizesComputation<'layout_data> {
2843 layout_context: &'layout_data LayoutContext<'layout_data>,
2844 constraint_space: &'layout_data ConstraintSpace<'layout_data>,
2845 paragraph: ContentSizes,
2846 current_line: ContentSizes,
2847 pending_whitespace: ContentSizes,
2849 uncleared_floats: LogicalSides1D<ContentSizes>,
2851 cleared_floats: LogicalSides1D<ContentSizes>,
2853 had_content_yet_for_min_content: bool,
2856 had_content_yet_for_max_content: bool,
2859 ending_inline_pbm_stack: Vec<Au>,
2862 depends_on_block_constraints: bool,
2864}
2865
2866impl<'layout_data> ContentSizesComputation<'layout_data> {
2867 fn traverse(
2868 mut self,
2869 inline_formatting_context: &InlineFormattingContext,
2870 ) -> InlineContentSizesResult {
2871 self.add_inline_size(
2872 inline_formatting_context.inline_start_for_first_line(self.constraint_space.into()),
2873 );
2874 for inline_item in &inline_formatting_context.inline_items {
2875 self.process_item(inline_item, inline_formatting_context);
2876 }
2877 self.forced_line_break();
2878 self.flush_floats();
2879
2880 InlineContentSizesResult {
2881 sizes: self.paragraph,
2882 depends_on_block_constraints: self.depends_on_block_constraints,
2883 }
2884 }
2885
2886 fn process_item(
2887 &mut self,
2888 inline_item: &InlineItem,
2889 inline_formatting_context: &InlineFormattingContext,
2890 ) {
2891 match inline_item {
2892 InlineItem::StartInlineBox(inline_box) => {
2893 let inline_box = inline_box.borrow();
2897 let zero = Au::zero();
2898 let writing_mode = self.constraint_space.style.writing_mode;
2899 let layout_style = inline_box.layout_style();
2900 let padding = layout_style
2901 .padding(writing_mode)
2902 .percentages_relative_to(zero);
2903 let border = layout_style.border_width(writing_mode);
2904 let margin = inline_box
2905 .base
2906 .style
2907 .margin(writing_mode)
2908 .percentages_relative_to(zero)
2909 .auto_is(Au::zero);
2910
2911 let pbm = margin + padding + border;
2912 self.add_inline_size(pbm.inline_start);
2913 self.ending_inline_pbm_stack.push(pbm.inline_end);
2914 },
2915 InlineItem::EndInlineBox(..) => {
2916 let length = self.ending_inline_pbm_stack.pop().unwrap_or_else(Au::zero);
2917 self.add_inline_size(length);
2918 },
2919 InlineItem::TextRun(text_run) => {
2920 let text_run = &*text_run.borrow();
2921 let parent_style = text_run.inline_styles.style.borrow();
2922 for item in text_run.items.iter() {
2923 match item {
2924 TextRunItem::LineBreak { .. } => {
2925 self.forced_line_break();
2928 },
2929 TextRunItem::Tab { .. } => {
2930 self.process_preserved_tab(&parent_style, inline_formatting_context)
2931 },
2932 TextRunItem::TextSegment(segment) => {
2933 self.process_text_segment(&parent_style, segment)
2934 },
2935 }
2936 }
2937 },
2938 InlineItem::Atomic(atomic, offset_in_text, _level) => {
2939 if self.had_content_yet_for_min_content &&
2941 !inline_formatting_context
2942 .previous_character_prevents_soft_wrap_opportunity(*offset_in_text)
2943 {
2944 self.line_break_opportunity();
2945 }
2946
2947 self.commit_pending_whitespace();
2948 let outer = self.outer_inline_content_sizes_of_float_or_atomic(&atomic.borrow());
2949 self.current_line += outer;
2950
2951 if !inline_formatting_context
2953 .next_character_prevents_soft_wrap_opportunity(*offset_in_text)
2954 {
2955 self.line_break_opportunity();
2956 }
2957 },
2958 InlineItem::OutOfFlowFloatBox(float_box) => {
2959 let float_box = float_box.borrow();
2960 let sizes = self.outer_inline_content_sizes_of_float_or_atomic(&float_box.contents);
2961 let style = &float_box.contents.style();
2962 let container_writing_mode = self.constraint_space.style.writing_mode;
2963 let clear =
2964 Clear::from_style_and_container_writing_mode(style, container_writing_mode);
2965 self.clear_floats(clear);
2966 let float_side =
2967 FloatSide::from_style_and_container_writing_mode(style, container_writing_mode);
2968 match float_side.expect("A float box needs to float to some side") {
2969 FloatSide::InlineStart => self.uncleared_floats.start.union_assign(&sizes),
2970 FloatSide::InlineEnd => self.uncleared_floats.end.union_assign(&sizes),
2971 }
2972 },
2973 InlineItem::BlockLevel(block_level) => {
2974 self.forced_line_break();
2975 self.flush_floats();
2976 let inline_content_sizes_result =
2977 compute_inline_content_sizes_for_block_level_boxes(
2978 std::slice::from_ref(block_level),
2979 self.layout_context,
2980 &self.constraint_space.into(),
2981 );
2982 self.depends_on_block_constraints |=
2983 inline_content_sizes_result.depends_on_block_constraints;
2984 self.current_line = inline_content_sizes_result.sizes;
2985 self.forced_line_break();
2986 },
2987 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => {},
2988 }
2989 }
2990
2991 fn process_text_segment(
2992 &mut self,
2993 parent_style: &AtomicRef<'_, ServoArc<ComputedValues>>,
2994 segment: &TextRunSegment,
2995 ) {
2996 let style_text = parent_style.get_inherited_text();
2997 let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap;
2998
2999 let break_at_start = segment.break_at_start && self.had_content_yet_for_min_content;
3002
3003 for (run_index, run) in segment.runs.iter().enumerate() {
3004 if can_wrap && (run_index != 0 || break_at_start) {
3007 self.line_break_opportunity();
3008 }
3009
3010 let advance = run.total_advance();
3011 if run.is_whitespace() {
3012 if !matches!(
3013 style_text.white_space_collapse,
3014 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
3015 ) {
3016 if self.had_content_yet_for_min_content {
3017 if can_wrap {
3018 self.line_break_opportunity();
3019 } else {
3020 self.pending_whitespace.min_content += advance;
3021 }
3022 }
3023 if self.had_content_yet_for_max_content {
3024 self.pending_whitespace.max_content += advance;
3025 }
3026 continue;
3027 }
3028 if can_wrap {
3029 self.pending_whitespace.max_content += advance;
3030 self.commit_pending_whitespace();
3031 self.line_break_opportunity();
3032 continue;
3033 }
3034 }
3035
3036 self.commit_pending_whitespace();
3037 self.add_inline_size(advance);
3038
3039 if can_wrap && run.ends_with_whitespace() {
3044 self.line_break_opportunity();
3045 }
3046 }
3047 }
3048
3049 fn process_preserved_tab(
3050 &mut self,
3051 parent_style: &AtomicRef<'_, ServoArc<ComputedValues>>,
3052 inline_formatting_context: &InlineFormattingContext,
3053 ) {
3054 self.commit_pending_whitespace();
3056
3057 self.current_line.min_content += inline_formatting_context
3058 .next_tab_stop_after_inline_advance(parent_style, self.current_line.min_content);
3059 self.current_line.max_content += inline_formatting_context
3060 .next_tab_stop_after_inline_advance(parent_style, self.current_line.max_content);
3061 if parent_style.get_inherited_text().text_wrap_mode == TextWrapMode::Wrap {
3062 self.line_break_opportunity();
3063 }
3064 }
3065
3066 fn add_inline_size(&mut self, l: Au) {
3067 self.current_line.min_content += l;
3068 self.current_line.max_content += l;
3069 }
3070
3071 fn line_break_opportunity(&mut self) {
3072 self.pending_whitespace.min_content = Au::zero();
3076 let current_min_content = mem::take(&mut self.current_line.min_content);
3077 self.paragraph.min_content.max_assign(current_min_content);
3078 self.had_content_yet_for_min_content = false;
3079 }
3080
3081 fn forced_line_break(&mut self) {
3082 self.line_break_opportunity();
3084
3085 self.pending_whitespace.max_content = Au::zero();
3087 let current_max_content = mem::take(&mut self.current_line.max_content);
3088 self.paragraph.max_content.max_assign(current_max_content);
3089 self.had_content_yet_for_max_content = false;
3090 }
3091
3092 fn commit_pending_whitespace(&mut self) {
3093 self.current_line += mem::take(&mut self.pending_whitespace);
3094 self.had_content_yet_for_min_content = true;
3095 self.had_content_yet_for_max_content = true;
3096 }
3097
3098 fn outer_inline_content_sizes_of_float_or_atomic(
3099 &mut self,
3100 context: &IndependentFormattingContext,
3101 ) -> ContentSizes {
3102 let result = context.outer_inline_content_sizes(
3103 self.layout_context,
3104 &self.constraint_space.into(),
3105 &LogicalVec2::zero(),
3106 false, );
3108 self.depends_on_block_constraints |= result.depends_on_block_constraints;
3109 result.sizes
3110 }
3111
3112 fn clear_floats(&mut self, clear: Clear) {
3113 match clear {
3114 Clear::InlineStart => {
3115 let start_floats = mem::take(&mut self.uncleared_floats.start);
3116 self.cleared_floats.start.max_assign(start_floats);
3117 },
3118 Clear::InlineEnd => {
3119 let end_floats = mem::take(&mut self.uncleared_floats.end);
3120 self.cleared_floats.end.max_assign(end_floats);
3121 },
3122 Clear::Both => {
3123 let start_floats = mem::take(&mut self.uncleared_floats.start);
3124 let end_floats = mem::take(&mut self.uncleared_floats.end);
3125 self.cleared_floats.start.max_assign(start_floats);
3126 self.cleared_floats.end.max_assign(end_floats);
3127 },
3128 Clear::None => {},
3129 }
3130 }
3131
3132 fn flush_floats(&mut self) {
3133 self.clear_floats(Clear::Both);
3134 let start_floats = mem::take(&mut self.cleared_floats.start);
3135 let end_floats = mem::take(&mut self.cleared_floats.end);
3136 self.paragraph.union_assign(&start_floats);
3137 self.paragraph.union_assign(&end_floats);
3138 }
3139
3140 fn compute(
3142 inline_formatting_context: &InlineFormattingContext,
3143 layout_context: &'layout_data LayoutContext,
3144 constraint_space: &'layout_data ConstraintSpace,
3145 ) -> InlineContentSizesResult {
3146 Self {
3147 layout_context,
3148 constraint_space,
3149 paragraph: ContentSizes::zero(),
3150 current_line: ContentSizes::zero(),
3151 pending_whitespace: ContentSizes::zero(),
3152 uncleared_floats: LogicalSides1D::default(),
3153 cleared_floats: LogicalSides1D::default(),
3154 had_content_yet_for_min_content: false,
3155 had_content_yet_for_max_content: false,
3156 ending_inline_pbm_stack: Vec::new(),
3157 depends_on_block_constraints: false,
3158 }
3159 .traverse(inline_formatting_context)
3160 }
3161}
3162
3163pub(crate) struct BidiLevels<'a> {
3164 info: Option<BidiInfo<'a>>,
3165}
3166
3167impl BidiLevels<'_> {
3168 fn level(&self, byte_offset_in_ifc_text: usize) -> Level {
3169 self.info
3170 .as_ref()
3171 .map_or_else(Level::ltr, |info| info.levels[byte_offset_in_ifc_text])
3172 }
3173}
3174
3175fn char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character: char) -> bool {
3187 if character == '\u{00A0}' {
3188 return false;
3189 }
3190 matches!(
3191 icu_properties::maps::line_break().get(character),
3192 ICULineBreak::Glue | ICULineBreak::WordJoiner | ICULineBreak::ZWJ
3193 )
3194}