1pub mod construct;
72pub mod inline_box;
73pub mod line;
74mod line_breaker;
75pub mod text_run;
76
77use std::cell::{Cell, OnceCell};
78use std::mem;
79use std::rc::Rc;
80use std::sync::{Arc, OnceLock};
81
82use app_units::{Au, MAX_AU};
83use atomic_refcell::AtomicRef;
84use bitflags::bitflags;
85use construct::InlineFormattingContextBuilder;
86use fonts::{FontMetrics, FontRef, ShapedTextSlice};
87use icu_locid::LanguageIdentifier;
88use icu_locid::subtags::{Language, language};
89use icu_properties::{self, LineBreak as ICULineBreak};
90use icu_segmenter::{LineBreakOptions, LineBreakStrictness, LineBreakWordOption};
91use inline_box::{InlineBox, InlineBoxContainerState, InlineBoxIdentifier, InlineBoxes};
92use layout_api::{LayoutNode, SharedSelection};
93use line::{
94 AbsolutelyPositionedLineItem, AtomicLineItem, FloatLineItem, LineItem, LineItemLayout,
95 TextRunLineItem,
96};
97use line_breaker::LineBreaker;
98use malloc_size_of_derive::MallocSizeOf;
99use script::layout_dom::ServoLayoutNode;
100use servo_arc::Arc as ServoArc;
101use style::Zero;
102use style::computed_values::line_break::T as LineBreak;
103use style::computed_values::text_wrap_mode::T as TextWrapMode;
104use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
105use style::computed_values::word_break::T as WordBreak;
106use style::context::{QuirksMode, SharedStyleContext};
107use style::properties::ComputedValues;
108use style::properties::style_structs::InheritedText;
109use style::values::computed::BaselineShift;
110use style::values::generics::box_::BaselineShiftKeyword;
111use style::values::generics::font::LineHeight;
112use style::values::specified::box_::BaselineSource;
113use style::values::specified::text::TextAlignKeyword;
114use style::values::specified::{AlignmentBaseline, TextAlignLast, TextJustify};
115use text_run::{TextRun, get_font_for_first_font_for_style};
116use unicode_bidi::{BidiInfo, Level};
117
118use super::float::{Clear, PlacementAmongFloats};
119use super::{IndependentFloatOrAtomicLayoutResult, IndependentFormattingContextLayoutResult};
120use crate::cell::{ArcRefCell, WeakRefCell};
121use crate::context::LayoutContext;
122use crate::dom::WeakLayoutBox;
123use crate::dom_traversal::NodeAndStyleInfo;
124use crate::flow::float::{FloatBox, SequentialLayoutState};
125use crate::flow::inline::line::TextRunOffsets;
126use crate::flow::inline::text_run::{FontAndScriptInfo, TextRunItem, TextRunSegment};
127use crate::flow::{
128 BlockLevelBox, CollapsibleWithParentStartMargin, FloatSide, PlacementState,
129 compute_inline_content_sizes_for_block_level_boxes, layout_block_level_child,
130};
131use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
132use crate::fragment_tree::{
133 BaseFragmentInfo, CollapsedMargin, Fragment, FragmentFlags, PositioningFragment,
134};
135use crate::geom::{LogicalRect, LogicalSides1D, LogicalVec2, ToLogical};
136use crate::layout_box_base::LayoutBoxBase;
137use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
138use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
139use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
140use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SharedStyle};
141
142static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
144static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
145
146#[derive(Debug, MallocSizeOf)]
147pub(crate) struct InlineFormattingContext {
148 inline_items: Vec<InlineItem>,
153
154 inline_boxes: InlineBoxes,
157
158 text_content: String,
160
161 shared_inline_styles: SharedInlineStyles,
164
165 default_font: Option<FontRef>,
169
170 has_first_formatted_line: bool,
173
174 pub(super) contains_floats: bool,
176
177 is_single_line_text_input: bool,
180
181 has_right_to_left_content: bool,
184
185 #[ignore_malloc_size_of = "This is stored primarily in the DOM"]
188 shared_selection: Option<SharedSelection>,
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 LogicalSides1D::new(false, false),
237 true, );
239
240 let Some(fragment) = fragment.retrieve_box_fragment() else {
241 unreachable!("The fragment should be a Fragment::Box()");
242 };
243
244 layout.depends_on_block_constraints |= fragment.base.flags.contains(
247 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
248 );
249
250 layout.push_line_item_to_unbreakable_segment(LineItem::BlockLevel(
251 layout.current_inline_box_identifier(),
252 fragment.clone(),
253 ));
254
255 layout.commit_current_segment_to_line();
256 layout.process_line_break(
257 true, false, );
260 }
261}
262
263#[derive(Clone, Debug, MallocSizeOf)]
264pub(crate) enum InlineItem {
265 StartInlineBox(ArcRefCell<InlineBox>),
266 EndInlineBox(ArcRefCell<InlineBox>),
267 TextRun(ArcRefCell<TextRun>),
268 OutOfFlowAbsolutelyPositionedBox(
269 ArcRefCell<AbsolutelyPositionedBox>,
270 usize, ),
272 OutOfFlowFloatBox(ArcRefCell<FloatBox>),
273 Atomic(
274 ArcRefCell<IndependentFormattingContext>,
275 usize, Level, ),
278 BlockLevel(ArcRefCell<BlockLevelBox>),
279}
280
281impl InlineItem {
282 pub(crate) fn repair_style(
283 &self,
284 context: &SharedStyleContext,
285 node: &ServoLayoutNode,
286 new_style: &ServoArc<ComputedValues>,
287 ) {
288 match self {
289 InlineItem::StartInlineBox(inline_box) => {
290 inline_box
291 .borrow_mut()
292 .repair_style(context, node, new_style);
293 },
294 InlineItem::EndInlineBox(..) => {},
295 InlineItem::TextRun(..) => {},
298 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box
299 .borrow_mut()
300 .context
301 .repair_style(context, node, new_style),
302 InlineItem::OutOfFlowFloatBox(float_box) => float_box
303 .borrow_mut()
304 .contents
305 .repair_style(context, node, new_style),
306 InlineItem::Atomic(atomic, ..) => {
307 atomic.borrow_mut().repair_style(context, node, new_style)
308 },
309 InlineItem::BlockLevel(block_level) => block_level
310 .borrow_mut()
311 .repair_style(context, node, new_style),
312 }
313 }
314
315 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
316 match self {
317 InlineItem::StartInlineBox(inline_box) => callback(&inline_box.borrow().base),
318 InlineItem::EndInlineBox(..) | InlineItem::TextRun(..) => {
319 unreachable!("Should never have these kind of fragments attached to a DOM node")
320 },
321 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
322 callback(&positioned_box.borrow().context.base)
323 },
324 InlineItem::OutOfFlowFloatBox(float_box) => callback(&float_box.borrow().contents.base),
325 InlineItem::Atomic(independent_formatting_context, ..) => {
326 callback(&independent_formatting_context.borrow().base)
327 },
328 InlineItem::BlockLevel(block_level) => block_level.borrow().with_base(callback),
329 }
330 }
331
332 pub(crate) fn with_base_mut<T>(&self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
333 match self {
334 InlineItem::StartInlineBox(inline_box) => callback(&mut inline_box.borrow_mut().base),
335 InlineItem::EndInlineBox(..) | InlineItem::TextRun(..) => {
336 unreachable!("Should never have these kind of fragments attached to a DOM node")
337 },
338 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
339 callback(&mut positioned_box.borrow_mut().context.base)
340 },
341 InlineItem::OutOfFlowFloatBox(float_box) => {
342 callback(&mut float_box.borrow_mut().contents.base)
343 },
344 InlineItem::Atomic(independent_formatting_context, ..) => {
345 callback(&mut independent_formatting_context.borrow_mut().base)
346 },
347 InlineItem::BlockLevel(block_level) => block_level.borrow_mut().with_base_mut(callback),
348 }
349 }
350
351 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
352 match self {
353 Self::StartInlineBox(_) | InlineItem::EndInlineBox(..) => {
354 },
357 Self::TextRun(_) => {
358 },
360 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
361 positioned_box.borrow().context.attached_to_tree(layout_box)
362 },
363 Self::OutOfFlowFloatBox(float_box) => {
364 float_box.borrow().contents.attached_to_tree(layout_box)
365 },
366 Self::Atomic(atomic, ..) => atomic.borrow().attached_to_tree(layout_box),
367 Self::BlockLevel(block_level) => block_level.borrow().attached_to_tree(layout_box),
368 }
369 }
370
371 pub(crate) fn downgrade(&self) -> WeakInlineItem {
372 match self {
373 Self::StartInlineBox(inline_box) => {
374 WeakInlineItem::StartInlineBox(inline_box.downgrade())
375 },
376 Self::EndInlineBox(inline_box) => WeakInlineItem::EndInlineBox(inline_box.downgrade()),
377 Self::TextRun(text_run) => WeakInlineItem::TextRun(text_run.downgrade()),
378 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
379 WeakInlineItem::OutOfFlowAbsolutelyPositionedBox(
380 positioned_box.downgrade(),
381 *offset_in_text,
382 )
383 },
384 Self::OutOfFlowFloatBox(float_box) => {
385 WeakInlineItem::OutOfFlowFloatBox(float_box.downgrade())
386 },
387 Self::Atomic(atomic, offset_in_text, bidi_level) => {
388 WeakInlineItem::Atomic(atomic.downgrade(), *offset_in_text, *bidi_level)
389 },
390 Self::BlockLevel(block_level) => WeakInlineItem::BlockLevel(block_level.downgrade()),
391 }
392 }
393}
394
395#[derive(Clone, Debug, MallocSizeOf)]
396pub(crate) enum WeakInlineItem {
397 StartInlineBox(WeakRefCell<InlineBox>),
398 EndInlineBox(WeakRefCell<InlineBox>),
399 TextRun(WeakRefCell<TextRun>),
400 OutOfFlowAbsolutelyPositionedBox(
401 WeakRefCell<AbsolutelyPositionedBox>,
402 usize, ),
404 OutOfFlowFloatBox(WeakRefCell<FloatBox>),
405 Atomic(
406 WeakRefCell<IndependentFormattingContext>,
407 usize, Level, ),
410 BlockLevel(WeakRefCell<BlockLevelBox>),
411}
412
413impl WeakInlineItem {
414 pub(crate) fn upgrade(&self) -> Option<InlineItem> {
415 Some(match self {
416 Self::StartInlineBox(inline_box) => InlineItem::StartInlineBox(inline_box.upgrade()?),
417 Self::EndInlineBox(inline_box) => InlineItem::EndInlineBox(inline_box.upgrade()?),
418 Self::TextRun(text_run) => InlineItem::TextRun(text_run.upgrade()?),
419 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
420 InlineItem::OutOfFlowAbsolutelyPositionedBox(
421 positioned_box.upgrade()?,
422 *offset_in_text,
423 )
424 },
425 Self::OutOfFlowFloatBox(float_box) => {
426 InlineItem::OutOfFlowFloatBox(float_box.upgrade()?)
427 },
428 Self::Atomic(atomic, offset_in_text, bidi_level) => {
429 InlineItem::Atomic(atomic.upgrade()?, *offset_in_text, *bidi_level)
430 },
431 Self::BlockLevel(block_level) => InlineItem::BlockLevel(block_level.upgrade()?),
432 })
433 }
434}
435
436struct LineUnderConstruction {
443 start_position: LogicalVec2<Au>,
446
447 inline_position: Au,
450
451 max_block_size: LineBlockSizes,
455
456 has_content: bool,
459
460 has_inline_pbm: bool,
463
464 has_floats_waiting_to_be_placed: bool,
468
469 placement_among_floats: OnceCell<LogicalRect<Au>>,
474
475 line_items: Vec<LineItem>,
478
479 for_block_level: bool,
481
482 starting_character_offset: usize,
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_inline_pbm: false,
501 has_floats_waiting_to_be_placed: false,
502 placement_among_floats: OnceCell::new(),
503 line_items: Vec::new(),
504 for_block_level: false,
505 starting_character_offset: 0,
506 }
507 }
508
509 fn replace_placement_among_floats(&mut self, new_placement: LogicalRect<Au>) {
510 self.placement_among_floats.take();
511 let _ = self.placement_among_floats.set(new_placement);
512 }
513
514 fn trim_trailing_whitespace(&mut self) -> Au {
516 let mut whitespace_trimmed = Au::zero();
521 for item in self.line_items.iter_mut().rev() {
522 if !item.trim_whitespace_at_end(&mut whitespace_trimmed) {
523 break;
524 }
525 }
526
527 whitespace_trimmed
528 }
529
530 fn count_justification_opportunities(&self) -> usize {
532 self.line_items
533 .iter()
534 .filter_map(|item| match item {
535 LineItem::TextRun(_, text_run) => Some(
536 text_run
537 .text
538 .iter()
539 .map(|shaped_text_slice| shaped_text_slice.total_word_separators())
540 .sum::<usize>(),
541 ),
542 _ => None,
543 })
544 .sum()
545 }
546
547 fn is_phantom(&self) -> bool {
550 !self.has_content && !self.has_inline_pbm
552 }
553}
554
555#[derive(Clone, Debug)]
561struct BaselineRelativeSize {
562 ascent: Au,
566
567 descent: Au,
571}
572
573impl BaselineRelativeSize {
574 fn zero() -> Self {
575 Self {
576 ascent: Au::zero(),
577 descent: Au::zero(),
578 }
579 }
580
581 fn max(&self, other: &Self) -> Self {
582 BaselineRelativeSize {
583 ascent: self.ascent.max(other.ascent),
584 descent: self.descent.max(other.descent),
585 }
586 }
587
588 fn adjust_for_nested_baseline_offset(&mut self, baseline_offset: Au) {
602 self.ascent -= baseline_offset;
603 self.descent += baseline_offset;
604 }
605}
606
607#[derive(Clone, Debug)]
608struct LineBlockSizes {
609 line_height: Au,
610 baseline_relative_size_for_line_height: Option<BaselineRelativeSize>,
611 size_for_baseline_positioning: BaselineRelativeSize,
612}
613
614impl LineBlockSizes {
615 fn zero() -> Self {
616 LineBlockSizes {
617 line_height: Au::zero(),
618 baseline_relative_size_for_line_height: None,
619 size_for_baseline_positioning: BaselineRelativeSize::zero(),
620 }
621 }
622
623 fn resolve(&self) -> Au {
624 let height_from_ascent_and_descent = self
625 .baseline_relative_size_for_line_height
626 .as_ref()
627 .map(|size| (size.ascent + size.descent).abs())
628 .unwrap_or_else(Au::zero);
629 self.line_height.max(height_from_ascent_and_descent)
630 }
631
632 fn max(&self, other: &LineBlockSizes) -> LineBlockSizes {
633 let baseline_relative_size = match (
634 self.baseline_relative_size_for_line_height.as_ref(),
635 other.baseline_relative_size_for_line_height.as_ref(),
636 ) {
637 (Some(our_size), Some(other_size)) => Some(our_size.max(other_size)),
638 (our_size, other_size) => our_size.or(other_size).cloned(),
639 };
640 Self {
641 line_height: self.line_height.max(other.line_height),
642 baseline_relative_size_for_line_height: baseline_relative_size,
643 size_for_baseline_positioning: self
644 .size_for_baseline_positioning
645 .max(&other.size_for_baseline_positioning),
646 }
647 }
648
649 fn max_assign(&mut self, other: &LineBlockSizes) {
650 *self = self.max(other);
651 }
652
653 fn adjust_for_baseline_offset(&mut self, baseline_offset: Au) {
654 if let Some(size) = self.baseline_relative_size_for_line_height.as_mut() {
655 size.adjust_for_nested_baseline_offset(baseline_offset)
656 }
657 self.size_for_baseline_positioning
658 .adjust_for_nested_baseline_offset(baseline_offset);
659 }
660
661 fn find_baseline_offset(&self) -> Au {
668 match self.baseline_relative_size_for_line_height.as_ref() {
669 Some(size) => size.ascent,
670 None => {
671 let leading = self.resolve() -
674 (self.size_for_baseline_positioning.ascent +
675 self.size_for_baseline_positioning.descent);
676 leading.scale_by(0.5) + self.size_for_baseline_positioning.ascent
677 },
678 }
679 }
680}
681
682struct UnbreakableSegmentUnderConstruction {
686 inline_size: Au,
688
689 max_block_size: LineBlockSizes,
692
693 line_items: Vec<LineItem>,
695
696 inline_box_hierarchy_depth: Option<usize>,
699
700 has_content: bool,
704
705 has_inline_pbm: bool,
708
709 trailing_whitespace_size: Au,
711}
712
713impl UnbreakableSegmentUnderConstruction {
714 fn new() -> Self {
715 Self {
716 inline_size: Au::zero(),
717 max_block_size: LineBlockSizes {
718 line_height: Au::zero(),
719 baseline_relative_size_for_line_height: None,
720 size_for_baseline_positioning: BaselineRelativeSize::zero(),
721 },
722 line_items: Vec::new(),
723 inline_box_hierarchy_depth: None,
724 has_content: false,
725 has_inline_pbm: false,
726 trailing_whitespace_size: Au::zero(),
727 }
728 }
729
730 fn reset(&mut self) {
732 assert!(self.line_items.is_empty()); self.inline_size = Au::zero();
734 self.max_block_size = LineBlockSizes::zero();
735 self.inline_box_hierarchy_depth = None;
736 self.has_content = false;
737 self.has_inline_pbm = false;
738 self.trailing_whitespace_size = Au::zero();
739 }
740
741 fn push_line_item(&mut self, line_item: LineItem, inline_box_hierarchy_depth: usize) {
746 if self.line_items.is_empty() {
747 self.inline_box_hierarchy_depth = Some(inline_box_hierarchy_depth);
748 }
749 self.line_items.push(line_item);
750 }
751
752 fn trim_leading_whitespace(&mut self) {
763 let mut whitespace_trimmed = Au::zero();
764 for item in self.line_items.iter_mut() {
765 if !item.trim_whitespace_at_start(&mut whitespace_trimmed) {
766 break;
767 }
768 }
769 self.inline_size -= whitespace_trimmed;
770 }
771
772 fn is_phantom(&self) -> bool {
775 !self.has_content && !self.has_inline_pbm
777 }
778}
779
780bitflags! {
781 struct InlineContainerStateFlags: u8 {
782 const CREATE_STRUT = 0b0001;
783 const IS_SINGLE_LINE_TEXT_INPUT = 0b0010;
784 }
785}
786
787struct InlineContainerState {
788 style: ServoArc<ComputedValues>,
790
791 flags: InlineContainerStateFlags,
793
794 has_content: Cell<bool>,
797
798 strut_block_sizes: LineBlockSizes,
803
804 nested_strut_block_sizes: LineBlockSizes,
808
809 pub baseline_offset: Au,
815
816 default_font: Option<FontRef>,
819
820 font_metrics: Arc<FontMetrics>,
822}
823
824struct InlineFormattingContextLayout<'layout_data> {
825 positioning_context: &'layout_data mut PositioningContext,
826 placement_state: PlacementState<'layout_data>,
827 sequential_layout_state: Option<&'layout_data mut SequentialLayoutState>,
828 layout_context: &'layout_data LayoutContext<'layout_data>,
829
830 ifc: &'layout_data InlineFormattingContext,
832
833 root_nesting_level: InlineContainerState,
843
844 inline_box_state_stack: Vec<Rc<InlineBoxContainerState>>,
848
849 cloneable_inline_box_end_pbm_size: Au,
852
853 inline_box_states: Vec<Rc<InlineBoxContainerState>>,
858
859 fragments: Vec<Fragment>,
863
864 current_line: LineUnderConstruction,
866
867 current_line_segment: UnbreakableSegmentUnderConstruction,
869
870 force_line_break_before_new_content: Option<usize>,
893
894 deferred_br_clear: Clear,
898
899 pub have_deferred_soft_wrap_opportunity: bool,
903
904 depends_on_block_constraints: bool,
907
908 white_space_collapse: WhiteSpaceCollapse,
913
914 text_wrap_mode: TextWrapMode,
919}
920
921impl InlineFormattingContextLayout<'_> {
922 fn current_inline_container_state(&self) -> &InlineContainerState {
923 match self.inline_box_state_stack.last() {
924 Some(inline_box_state) => &inline_box_state.base,
925 None => &self.root_nesting_level,
926 }
927 }
928
929 fn current_inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
930 self.inline_box_state_stack
931 .last()
932 .map(|state| state.identifier)
933 }
934
935 fn current_line_max_block_size_including_nested_containers(&self) -> LineBlockSizes {
936 self.current_inline_container_state()
937 .nested_strut_block_sizes
938 .max(&self.current_line.max_block_size)
939 }
940
941 fn current_line_block_start_considering_placement_among_floats(&self) -> Au {
942 self.current_line.placement_among_floats.get().map_or(
943 self.current_line.start_position.block,
944 |placement_among_floats| placement_among_floats.start_corner.block,
945 )
946 }
947
948 fn propagate_current_nesting_level_white_space_style(&mut self) {
949 let style = match self.inline_box_state_stack.last() {
950 Some(inline_box_state) => &inline_box_state.base.style,
951 None => self.placement_state.containing_block.style,
952 };
953 let style_text = style.get_inherited_text();
954 self.white_space_collapse = style_text.white_space_collapse;
955 self.text_wrap_mode = style_text.text_wrap_mode;
956 }
957
958 fn processing_br_element(&self) -> bool {
959 self.inline_box_state_stack.last().is_some_and(|state| {
960 state
961 .base_fragment_info
962 .flags
963 .contains(FragmentFlags::IS_BR_ELEMENT)
964 })
965 }
966
967 fn start_inline_box(&mut self, inline_box: &InlineBox) {
970 let containing_block = self.containing_block();
971 let inline_box_state = InlineBoxContainerState::new(
972 inline_box,
973 containing_block,
974 self.layout_context,
975 self.current_inline_container_state(),
976 inline_box.default_font.clone(),
977 );
978
979 self.depends_on_block_constraints |= inline_box
980 .base
981 .style
982 .depends_on_block_constraints_due_to_relative_positioning(
983 containing_block.style.writing_mode,
984 );
985
986 if inline_box_state
991 .base_fragment_info
992 .flags
993 .contains(FragmentFlags::IS_BR_ELEMENT) &&
994 self.deferred_br_clear == Clear::None
995 {
996 self.deferred_br_clear = Clear::from_style_and_container_writing_mode(
997 &inline_box_state.base.style,
998 self.containing_block().style.writing_mode,
999 );
1000 }
1001
1002 let padding = inline_box_state.pbm.padding.inline_start;
1003 let border = inline_box_state.pbm.border.inline_start;
1004 let margin = inline_box_state.pbm.margin.inline_start.auto_is(Au::zero);
1005 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
1008 self.current_line_segment.has_inline_pbm = true;
1009 }
1010 self.current_line_segment.inline_size += padding + border + margin;
1011 self.current_line_segment
1012 .line_items
1013 .push(LineItem::InlineStartBoxPaddingBorderMargin(
1014 inline_box.identifier,
1015 ));
1016
1017 let inline_box_state = Rc::new(inline_box_state);
1018 if inline_box_state.should_clone_pbm() {
1019 self.cloneable_inline_box_end_pbm_size += inline_box_state.pbm.padding.inline_end;
1020 self.cloneable_inline_box_end_pbm_size += inline_box_state.pbm.border.inline_end;
1021 self.cloneable_inline_box_end_pbm_size +=
1022 inline_box_state.pbm.margin.inline_end.auto_is(Au::zero);
1023 }
1024
1025 assert_eq!(
1029 self.inline_box_states.len(),
1030 inline_box.identifier.index_in_inline_boxes as usize
1031 );
1032 self.inline_box_states.push(inline_box_state.clone());
1033 self.inline_box_state_stack.push(inline_box_state);
1034 }
1035
1036 fn finish_inline_box(&mut self) {
1039 let inline_box_state = match self.inline_box_state_stack.pop() {
1040 Some(inline_box_state) => inline_box_state,
1041 None => return, };
1043 if inline_box_state.should_clone_pbm() {
1044 self.cloneable_inline_box_end_pbm_size -= inline_box_state.pbm.padding.inline_end;
1045 self.cloneable_inline_box_end_pbm_size -= inline_box_state.pbm.border.inline_end;
1046 self.cloneable_inline_box_end_pbm_size -=
1047 inline_box_state.pbm.margin.inline_end.auto_is(Au::zero);
1048 }
1049
1050 self.current_line_segment
1051 .max_block_size
1052 .max_assign(&inline_box_state.base.nested_strut_block_sizes);
1053
1054 if inline_box_state.base.has_content.get() {
1059 self.propagate_current_nesting_level_white_space_style();
1060 }
1061
1062 let padding = inline_box_state.pbm.padding.inline_end;
1063 let border = inline_box_state.pbm.border.inline_end;
1064 let margin = inline_box_state.pbm.margin.inline_end.auto_is(Au::zero);
1065 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
1068 self.current_line_segment.has_inline_pbm = true;
1069 }
1070 self.current_line_segment.inline_size += padding + border + margin;
1071 self.current_line_segment
1072 .line_items
1073 .push(LineItem::InlineEndBoxPaddingBorderMargin(
1074 inline_box_state.identifier,
1075 ))
1076 }
1077
1078 fn finish_last_line(&mut self) {
1079 self.possibly_flush_deferred_forced_line_break();
1081
1082 self.process_soft_wrap_opportunity();
1088
1089 self.commit_current_segment_to_line();
1092
1093 self.finish_current_line_and_reset(
1096 true, false, );
1099 }
1100
1101 fn finish_current_line_and_reset(
1105 &mut self,
1106 last_line_or_forced_line_break: bool,
1107 for_block_level: bool,
1108 ) {
1109 self.possibly_push_empty_text_run_to_line_for_text_caret();
1110
1111 let whitespace_trimmed = self.current_line.trim_trailing_whitespace();
1112 if !self.current_line.for_block_level {
1115 for inline_box in self.inline_box_state_stack.iter().rev() {
1116 if inline_box.should_clone_pbm() {
1117 self.current_line_segment.line_items.push(
1118 LineItem::InlineEndBoxPaddingBorderMargin(inline_box.identifier),
1119 );
1120 }
1121 }
1122 }
1123 let (inline_start_position, justification_adjustment) = self
1124 .calculate_current_line_inline_start_and_justification_adjustment(
1125 whitespace_trimmed,
1126 last_line_or_forced_line_break,
1127 );
1128
1129 let is_phantom_line = self.current_line.is_phantom();
1138 if !is_phantom_line {
1139 self.current_line.start_position.block += self.placement_state.current_margin.solve();
1140 self.placement_state.current_margin = CollapsedMargin::zero();
1141 }
1142 let block_start_position =
1143 self.current_line_block_start_considering_placement_among_floats();
1144
1145 let effective_block_advance = if is_phantom_line {
1146 LineBlockSizes::zero()
1147 } else {
1148 self.current_line_max_block_size_including_nested_containers()
1149 };
1150
1151 let resolved_block_advance = effective_block_advance.resolve();
1152 let block_end_position = if self.current_line.for_block_level {
1153 self.placement_state.current_block_direction_position
1154 } else {
1155 let mut block_end_position = block_start_position + resolved_block_advance;
1156 if let Some(sequential_layout_state) = self.sequential_layout_state.as_mut() {
1157 if !is_phantom_line {
1158 sequential_layout_state.commit_margin();
1159 }
1160
1161 let increment = block_end_position - self.current_line.start_position.block;
1164 sequential_layout_state.advance_block_position(increment);
1165
1166 if let Some(clearance) = sequential_layout_state
1170 .calculate_clearance(self.deferred_br_clear, &CollapsedMargin::zero())
1171 {
1172 sequential_layout_state.advance_block_position(clearance);
1173 block_end_position += clearance;
1174 };
1175 self.deferred_br_clear = Clear::None;
1176 }
1177 block_end_position
1178 };
1179
1180 let line_to_layout = std::mem::replace(
1182 &mut self.current_line,
1183 LineUnderConstruction::new(LogicalVec2 {
1184 inline: Au::zero(),
1185 block: block_end_position,
1186 }),
1187 );
1188 self.current_line.for_block_level = for_block_level;
1189
1190 if !for_block_level {
1193 for inline_box in self.inline_box_state_stack.iter() {
1194 if inline_box.should_clone_pbm() {
1195 self.current_line_segment.line_items.push(
1196 LineItem::InlineStartBoxPaddingBorderMargin(inline_box.identifier),
1197 );
1198 }
1199 }
1200 }
1201
1202 if !line_to_layout.for_block_level {
1203 self.placement_state.current_block_direction_position = block_end_position;
1204 }
1205
1206 if line_to_layout.has_floats_waiting_to_be_placed {
1207 place_pending_floats(self, &line_to_layout.line_items);
1208 }
1209
1210 let start_position = LogicalVec2 {
1211 block: block_start_position,
1212 inline: inline_start_position,
1213 };
1214
1215 let baseline_offset = effective_block_advance.find_baseline_offset();
1216 let start_positioning_context_length = self.positioning_context.len();
1217 let fragments = LineItemLayout::layout_line_items(
1218 self,
1219 line_to_layout.line_items,
1220 start_position,
1221 &effective_block_advance,
1222 justification_adjustment,
1223 is_phantom_line,
1224 line_to_layout.for_block_level,
1225 );
1226
1227 if !is_phantom_line {
1228 let baseline = baseline_offset + block_start_position;
1229 self.placement_state
1230 .inflow_baselines
1231 .first
1232 .get_or_insert(baseline);
1233 self.placement_state.inflow_baselines.last = Some(baseline);
1234 self.placement_state
1235 .next_in_flow_margin_collapses_with_parent_start_margin = false;
1236 }
1237
1238 if fragments.is_empty() &&
1240 self.positioning_context.len() == start_positioning_context_length
1241 {
1242 return;
1243 }
1244
1245 let start_corner = LogicalVec2 {
1249 inline: Au::zero(),
1250 block: block_start_position,
1251 };
1252
1253 let logical_origin_in_physical_coordinates =
1254 start_corner.to_physical_vector(self.containing_block().style.writing_mode);
1255 self.positioning_context
1256 .adjust_static_position_of_hoisted_fragments_with_offset(
1257 &logical_origin_in_physical_coordinates,
1258 start_positioning_context_length,
1259 );
1260
1261 let containing_block = self.containing_block();
1262 let physical_line_rect = LogicalRect {
1263 start_corner,
1264 size: LogicalVec2 {
1265 inline: containing_block.size.inline,
1266 block: effective_block_advance.resolve(),
1267 },
1268 }
1269 .as_physical(Some(containing_block));
1270 self.fragments
1271 .push(Fragment::Positioning(PositioningFragment::new_anonymous(
1272 self.root_nesting_level.style.clone(),
1273 physical_line_rect,
1274 fragments,
1275 true, )));
1277 }
1278
1279 fn calculate_current_line_inline_start_and_justification_adjustment(
1284 &self,
1285 whitespace_trimmed: Au,
1286 last_line_or_forced_line_break: bool,
1287 ) -> (Au, Au) {
1288 enum TextAlign {
1289 Start,
1290 Center,
1291 End,
1292 }
1293 let containing_block = self.containing_block();
1294 let style = containing_block.style;
1295 let mut text_align_keyword = style.clone_text_align();
1296
1297 if last_line_or_forced_line_break {
1298 text_align_keyword = match style.clone_text_align_last() {
1299 TextAlignLast::Auto if text_align_keyword == TextAlignKeyword::Justify => {
1300 TextAlignKeyword::Start
1301 },
1302 TextAlignLast::Auto => text_align_keyword,
1303 TextAlignLast::Start => TextAlignKeyword::Start,
1304 TextAlignLast::End => TextAlignKeyword::End,
1305 TextAlignLast::Left => TextAlignKeyword::Left,
1306 TextAlignLast::Right => TextAlignKeyword::Right,
1307 TextAlignLast::Center => TextAlignKeyword::Center,
1308 TextAlignLast::Justify => TextAlignKeyword::Justify,
1309 };
1310 }
1311
1312 let text_align = match text_align_keyword {
1313 TextAlignKeyword::Start => TextAlign::Start,
1314 TextAlignKeyword::Center | TextAlignKeyword::MozCenter => TextAlign::Center,
1315 TextAlignKeyword::End => TextAlign::End,
1316 TextAlignKeyword::Left | TextAlignKeyword::MozLeft => {
1317 if style.writing_mode.line_left_is_inline_start() {
1318 TextAlign::Start
1319 } else {
1320 TextAlign::End
1321 }
1322 },
1323 TextAlignKeyword::Right | TextAlignKeyword::MozRight => {
1324 if style.writing_mode.line_left_is_inline_start() {
1325 TextAlign::End
1326 } else {
1327 TextAlign::Start
1328 }
1329 },
1330 TextAlignKeyword::Justify => TextAlign::Start,
1331 };
1332
1333 let (line_start, available_space) = match self.current_line.placement_among_floats.get() {
1334 Some(placement_among_floats) => (
1335 placement_among_floats.start_corner.inline,
1336 placement_among_floats.size.inline,
1337 ),
1338 None => (Au::zero(), containing_block.size.inline),
1339 };
1340
1341 let text_indent = self.current_line.start_position.inline;
1348 let line_length = self.current_line.inline_position - whitespace_trimmed - text_indent;
1349 let adjusted_line_start = line_start +
1350 match text_align {
1351 TextAlign::Start => text_indent,
1352 TextAlign::End => (available_space - line_length).max(text_indent),
1353 TextAlign::Center => (available_space - line_length + text_indent)
1354 .scale_by(0.5)
1355 .max(text_indent),
1356 };
1357
1358 let text_justify = containing_block.style.clone_text_justify();
1362 let justification_adjustment = match (text_align_keyword, text_justify) {
1363 (TextAlignKeyword::Justify, TextJustify::None) => Au::zero(),
1366 (TextAlignKeyword::Justify, _) => {
1367 match self.current_line.count_justification_opportunities() {
1368 0 => Au::zero(),
1369 num_justification_opportunities => {
1370 (available_space - text_indent - line_length)
1371 .scale_by(1. / num_justification_opportunities as f32)
1372 },
1373 }
1374 },
1375 _ => Au::zero(),
1376 };
1377
1378 let justification_adjustment = justification_adjustment.max(Au::zero());
1381
1382 (adjusted_line_start, justification_adjustment)
1383 }
1384
1385 fn place_float_fragment(&mut self, float: &FloatLineItem) {
1386 let state = self
1387 .sequential_layout_state
1388 .as_mut()
1389 .expect("Tried to lay out a float with no sequential placement state!");
1390
1391 let block_offset_from_containining_block_top = state
1392 .current_block_position_including_margins() -
1393 state.current_containing_block_offset();
1394 state.place_float_fragment(
1395 &float.fragment,
1396 self.placement_state.containing_block,
1397 CollapsedMargin::zero(),
1398 block_offset_from_containining_block_top,
1399 );
1400 self.positioning_context
1401 .adjust_static_position_of_hoisted_fragments_in_range(
1402 &float.fragment.base.rect().origin.to_vector(),
1403 &float.range,
1404 )
1405 }
1406
1407 fn place_float_line_item_for_commit_to_line(
1416 &mut self,
1417 float_item: &mut FloatLineItem,
1418 line_inline_size_without_trailing_whitespace: Au,
1419 ) {
1420 let containing_block = self.containing_block();
1421 let float_fragment = &float_item.fragment;
1422 let logical_margin_rect_size = float_fragment
1423 .margin_rect()
1424 .size
1425 .to_logical(containing_block.style.writing_mode);
1426 let inline_size = logical_margin_rect_size.inline.max(Au::zero());
1427
1428 let available_inline_size = match self.current_line.placement_among_floats.get() {
1429 Some(placement_among_floats) => placement_among_floats.size.inline,
1430 None => containing_block.size.inline,
1431 } - line_inline_size_without_trailing_whitespace;
1432
1433 let has_content = self.current_line.has_content || self.current_line_segment.has_content;
1439 let fits_on_line = !has_content || inline_size <= available_inline_size;
1440 let needs_placement_later =
1441 self.current_line.has_floats_waiting_to_be_placed || !fits_on_line;
1442
1443 if needs_placement_later {
1444 self.current_line.has_floats_waiting_to_be_placed = true;
1445 } else {
1446 self.place_float_fragment(float_item);
1447 float_item.needs_placement = false;
1448 }
1449
1450 let new_placement = self.place_line_among_floats(&LogicalVec2 {
1455 inline: line_inline_size_without_trailing_whitespace,
1456 block: self.current_line.max_block_size.resolve(),
1457 });
1458 self.current_line
1459 .replace_placement_among_floats(new_placement);
1460 }
1461
1462 fn place_line_among_floats(&self, potential_line_size: &LogicalVec2<Au>) -> LogicalRect<Au> {
1467 let sequential_layout_state = self
1468 .sequential_layout_state
1469 .as_ref()
1470 .expect("Should not have called this function without having floats.");
1471
1472 let ifc_offset_in_float_container = LogicalVec2 {
1473 inline: sequential_layout_state
1474 .floats
1475 .containing_block_info
1476 .inline_start,
1477 block: sequential_layout_state.current_containing_block_offset(),
1478 };
1479
1480 let ceiling = self.current_line_block_start_considering_placement_among_floats();
1481 let mut placement = PlacementAmongFloats::new(
1482 &sequential_layout_state.floats,
1483 ceiling + ifc_offset_in_float_container.block,
1484 LogicalVec2 {
1485 inline: potential_line_size.inline,
1486 block: potential_line_size.block,
1487 },
1488 &PaddingBorderMargin::zero(),
1489 );
1490
1491 let mut placement_rect = placement.place();
1492 placement_rect.start_corner -= ifc_offset_in_float_container;
1493 placement_rect
1494 }
1495
1496 fn new_potential_line_size_causes_line_break(
1503 &mut self,
1504 potential_line_size: &LogicalVec2<Au>,
1505 ) -> bool {
1506 let containing_block = self.containing_block();
1507 let available_line_space = if self.sequential_layout_state.is_some() {
1508 self.current_line
1509 .placement_among_floats
1510 .get_or_init(|| self.place_line_among_floats(potential_line_size))
1511 .size
1512 } else {
1513 LogicalVec2 {
1514 inline: containing_block.size.inline,
1515 block: MAX_AU,
1516 }
1517 };
1518
1519 let inline_would_overflow = potential_line_size.inline > available_line_space.inline;
1520 let block_would_overflow = potential_line_size.block > available_line_space.block;
1521
1522 let can_break = self.current_line.has_content;
1525
1526 if !can_break {
1532 if self.sequential_layout_state.is_some() &&
1535 (inline_would_overflow || block_would_overflow)
1536 {
1537 let new_placement = self.place_line_among_floats(potential_line_size);
1538 self.current_line
1539 .replace_placement_among_floats(new_placement);
1540 }
1541
1542 return false;
1543 }
1544
1545 if potential_line_size.inline > containing_block.size.inline {
1548 return true;
1549 }
1550
1551 if block_would_overflow {
1555 assert!(self.sequential_layout_state.is_some());
1557 let new_placement = self.place_line_among_floats(potential_line_size);
1558 if new_placement.start_corner.block !=
1559 self.current_line_block_start_considering_placement_among_floats()
1560 {
1561 return true;
1562 } else {
1563 self.current_line
1564 .replace_placement_among_floats(new_placement);
1565 return false;
1566 }
1567 }
1568
1569 potential_line_size.inline + self.cloneable_inline_box_end_pbm_size >
1573 available_line_space.inline
1574 }
1575
1576 fn defer_forced_line_break_at_character_offset(&mut self, line_break_offset: usize) {
1577 if !self.unbreakable_segment_fits_on_line() {
1580 self.process_line_break(
1581 false, false, );
1584 }
1585
1586 self.force_line_break_before_new_content = Some(line_break_offset);
1588
1589 let line_is_empty =
1597 !self.current_line_segment.has_content && !self.current_line.has_content;
1598 if !self.processing_br_element() || line_is_empty {
1599 let strut_size = self
1600 .current_inline_container_state()
1601 .strut_block_sizes
1602 .clone();
1603 self.update_unbreakable_segment_for_new_content(
1604 &strut_size,
1605 Au::zero(),
1606 SegmentContentFlags::empty(),
1607 );
1608 }
1609 }
1610
1611 fn possibly_flush_deferred_forced_line_break(&mut self) {
1612 let Some(line_break_character_offset) = self.force_line_break_before_new_content.take()
1613 else {
1614 return;
1615 };
1616
1617 self.commit_current_segment_to_line();
1618 self.process_line_break(
1619 true, false, );
1622
1623 self.current_line.starting_character_offset = line_break_character_offset + 1;
1624 }
1625
1626 fn push_line_item_to_unbreakable_segment(&mut self, line_item: LineItem) {
1627 self.current_line_segment
1628 .push_line_item(line_item, self.inline_box_state_stack.len());
1629 }
1630
1631 fn push_glyph_store_to_unbreakable_segment(
1632 &mut self,
1633 glyph_store: Arc<ShapedTextSlice>,
1634 text_run: &TextRun,
1635 info: &FontAndScriptInfo,
1636 offsets: Option<TextRunOffsets>,
1637 ) {
1638 let inline_advance = glyph_store.total_advance();
1639 let flags = if glyph_store.is_whitespace() {
1640 SegmentContentFlags::from(text_run.inline_styles.style.borrow().get_inherited_text())
1641 } else {
1642 SegmentContentFlags::empty()
1643 };
1644
1645 let mut block_contribution = LineBlockSizes::zero();
1646 let quirks_mode = self.layout_context.style_context.quirks_mode() != QuirksMode::NoQuirks;
1647 let current_inline_container_state = self.current_inline_container_state();
1648 if quirks_mode && !flags.is_collapsible_whitespace() {
1649 block_contribution.max_assign(¤t_inline_container_state.strut_block_sizes);
1654 }
1655
1656 let font_metrics = &info.font_info.font.metrics;
1660 if current_inline_container_state
1661 .font_metrics
1662 .block_metrics_meaningfully_differ(font_metrics)
1663 {
1664 let baseline_shift = effective_baseline_shift(
1666 ¤t_inline_container_state.style,
1667 self.inline_box_state_stack.last().map(|c| &c.base),
1668 );
1669 let mut font_block_conribution = current_inline_container_state
1670 .get_block_size_contribution(
1671 baseline_shift,
1672 font_metrics,
1673 ¤t_inline_container_state.font_metrics,
1674 );
1675 font_block_conribution
1676 .adjust_for_baseline_offset(current_inline_container_state.baseline_offset);
1677 block_contribution.max_assign(&font_block_conribution);
1678 }
1679
1680 self.update_unbreakable_segment_for_new_content(&block_contribution, inline_advance, flags);
1681
1682 let current_inline_box_identifier = self.current_inline_box_identifier();
1683 if let Some(LineItem::TextRun(inline_box_identifier, line_item)) =
1684 self.current_line_segment.line_items.last_mut() &&
1685 *inline_box_identifier == current_inline_box_identifier &&
1686 line_item.merge_if_possible(info, &glyph_store, &offsets, &text_run.inline_styles)
1687 {
1688 return;
1689 }
1690
1691 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1692 current_inline_box_identifier,
1693 TextRunLineItem {
1694 text: vec![glyph_store],
1695 base_fragment_info: text_run.base_fragment_info,
1696 inline_styles: text_run.inline_styles.clone(),
1697 info: info.clone(),
1698 offsets: offsets.map(Box::new),
1699 is_empty_for_text_cursor: false,
1700 },
1701 ));
1702 }
1703
1704 fn possibly_push_empty_text_run_to_line_for_text_caret(&mut self) {
1707 let line_start_offset = self.current_line.starting_character_offset;
1708 let Some(shared_selection) = self.ifc.shared_selection.clone() else {
1709 return;
1710 };
1711 let offsets = TextRunOffsets {
1712 shared_selection,
1713 character_range: line_start_offset..line_start_offset + 1,
1714 };
1715
1716 if self
1718 .current_line
1719 .line_items
1720 .iter()
1721 .rev()
1722 .find(|line_item| line_item.is_in_flow_content())
1723 .is_some_and(|line_item| matches!(line_item, LineItem::TextRun(..)))
1724 {
1725 return;
1726 }
1727
1728 let inline_container_state = self.current_inline_container_state();
1729 let Some(font) = inline_container_state.default_font.clone() else {
1730 return;
1731 };
1732
1733 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1734 self.current_inline_box_identifier(),
1735 TextRunLineItem {
1736 text: Default::default(),
1737 base_fragment_info: BaseFragmentInfo::anonymous(),
1738 inline_styles: self.ifc.shared_inline_styles.clone(),
1739 info: FontAndScriptInfo::simple_for_font(font),
1740 offsets: Some(Box::new(offsets)),
1741 is_empty_for_text_cursor: true,
1742 },
1743 ));
1744 self.current_line_segment.has_content = true;
1745 self.commit_current_segment_to_line();
1746 }
1747
1748 fn update_unbreakable_segment_for_new_content(
1749 &mut self,
1750 block_sizes_of_content: &LineBlockSizes,
1751 inline_size: Au,
1752 flags: SegmentContentFlags,
1753 ) {
1754 if flags.is_collapsible_whitespace() || flags.is_wrappable_and_hangable() {
1755 self.current_line_segment.trailing_whitespace_size = inline_size;
1756 } else {
1757 self.current_line_segment.trailing_whitespace_size = Au::zero();
1758 }
1759 if !flags.is_collapsible_whitespace() {
1760 self.current_line_segment.has_content = true;
1761 }
1762
1763 let container_max_block_size = &self
1765 .current_inline_container_state()
1766 .nested_strut_block_sizes
1767 .clone();
1768 self.current_line_segment
1769 .max_block_size
1770 .max_assign(container_max_block_size);
1771 self.current_line_segment
1772 .max_block_size
1773 .max_assign(block_sizes_of_content);
1774
1775 self.current_line_segment.inline_size += inline_size;
1776
1777 self.current_inline_container_state().has_content.set(true);
1779 self.propagate_current_nesting_level_white_space_style();
1780 }
1781
1782 fn process_line_break(&mut self, forced_line_break: bool, for_block_level: bool) {
1783 self.current_line_segment.trim_leading_whitespace();
1784 self.finish_current_line_and_reset(forced_line_break, for_block_level);
1785 }
1786
1787 fn potential_line_size(&self) -> LogicalVec2<Au> {
1788 LogicalVec2 {
1789 inline: self.current_line.inline_position + self.current_line_segment.inline_size,
1790 block: self
1791 .current_line_max_block_size_including_nested_containers()
1792 .max(&self.current_line_segment.max_block_size)
1793 .resolve(),
1794 }
1795 }
1796
1797 fn unbreakable_segment_fits_on_line(&mut self) -> bool {
1798 let potential_line_size_without_hanging_whitespace = self.potential_line_size() -
1799 LogicalVec2 {
1800 inline: self.current_line_segment.trailing_whitespace_size,
1801 block: Au::zero(),
1802 };
1803 !self.new_potential_line_size_causes_line_break(
1804 &potential_line_size_without_hanging_whitespace,
1805 )
1806 }
1807
1808 fn process_soft_wrap_opportunity(&mut self) {
1812 if self.current_line_segment.line_items.is_empty() {
1813 return;
1814 }
1815 if self.text_wrap_mode == TextWrapMode::Nowrap {
1816 return;
1817 }
1818 if !self.unbreakable_segment_fits_on_line() {
1819 self.process_line_break(
1820 false, false, );
1823 }
1824 self.commit_current_segment_to_line();
1825 }
1826
1827 fn commit_current_segment_to_line(&mut self) {
1830 if self.current_line_segment.line_items.is_empty() && !self.current_line_segment.has_content
1833 {
1834 return;
1835 }
1836
1837 if !self.current_line.has_content {
1838 self.current_line_segment.trim_leading_whitespace();
1839 }
1840
1841 self.current_line.inline_position += self.current_line_segment.inline_size;
1842 self.current_line.max_block_size = self
1843 .current_line_max_block_size_including_nested_containers()
1844 .max(&self.current_line_segment.max_block_size);
1845 let line_inline_size_without_trailing_whitespace =
1846 self.current_line.inline_position - self.current_line_segment.trailing_whitespace_size;
1847
1848 let mut segment_items = mem::take(&mut self.current_line_segment.line_items);
1850 for item in segment_items.iter_mut() {
1851 if let LineItem::Float(_, float_item) = item {
1852 self.place_float_line_item_for_commit_to_line(
1853 float_item,
1854 line_inline_size_without_trailing_whitespace,
1855 );
1856 }
1857 }
1858
1859 if self.current_line.line_items.is_empty() {
1864 let will_break = self.new_potential_line_size_causes_line_break(&LogicalVec2 {
1865 inline: line_inline_size_without_trailing_whitespace,
1866 block: self.current_line_segment.max_block_size.resolve(),
1867 });
1868 assert!(!will_break);
1869 }
1870
1871 self.current_line.line_items.extend(segment_items);
1872 self.current_line.has_content |= self.current_line_segment.has_content;
1873 self.current_line.has_inline_pbm |= self.current_line_segment.has_inline_pbm;
1874
1875 self.current_line_segment.reset();
1876 }
1877
1878 #[inline]
1879 fn containing_block(&self) -> &ContainingBlock<'_> {
1880 self.placement_state.containing_block
1881 }
1882}
1883
1884bitflags! {
1885 struct SegmentContentFlags: u8 {
1886 const COLLAPSIBLE_WHITESPACE = 0b00000001;
1887 const WRAPPABLE_AND_HANGABLE_WHITESPACE = 0b00000010;
1888 }
1889}
1890
1891impl SegmentContentFlags {
1892 fn is_collapsible_whitespace(&self) -> bool {
1893 self.contains(Self::COLLAPSIBLE_WHITESPACE)
1894 }
1895
1896 fn is_wrappable_and_hangable(&self) -> bool {
1897 self.contains(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE)
1898 }
1899}
1900
1901impl From<&InheritedText> for SegmentContentFlags {
1902 fn from(style_text: &InheritedText) -> Self {
1903 let mut flags = Self::empty();
1904
1905 if !matches!(
1908 style_text.white_space_collapse,
1909 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
1910 ) {
1911 flags.insert(Self::COLLAPSIBLE_WHITESPACE);
1912 }
1913
1914 if style_text.text_wrap_mode == TextWrapMode::Wrap &&
1917 style_text.white_space_collapse != WhiteSpaceCollapse::BreakSpaces
1918 {
1919 flags.insert(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE);
1920 }
1921 flags
1922 }
1923}
1924
1925impl InlineFormattingContext {
1926 #[servo_tracing::instrument(name = "InlineFormattingContext::new_with_builder", skip_all)]
1927 fn new_with_builder(
1928 mut builder: InlineFormattingContextBuilder,
1929 layout_context: &LayoutContext,
1930 has_first_formatted_line: bool,
1931 is_single_line_text_input: bool,
1932 starting_bidi_level: Level,
1933 ) -> Self {
1934 let text_content: String = builder.text_segments.into_iter().collect();
1936
1937 let bidi_levels = BidiLevels {
1938 info: builder
1939 .has_right_to_left_content
1940 .then(|| BidiInfo::new(&text_content, Some(starting_bidi_level))),
1941 };
1942
1943 let shared_inline_styles = builder
1944 .shared_inline_styles_stack
1945 .last()
1946 .expect("Should have at least one SharedInlineStyle for the root of an IFC")
1947 .clone();
1948 let (word_break, line_break, lang) = {
1949 let styles = shared_inline_styles.style.borrow();
1950 let text_style = styles.get_inherited_text();
1951 (
1952 text_style.word_break,
1953 text_style.line_break,
1954 styles.get_font()._x_lang.clone(),
1955 )
1956 };
1957
1958 let mut options = LineBreakOptions::default();
1959
1960 options.strictness = match line_break {
1961 LineBreak::Loose => LineBreakStrictness::Loose,
1962 LineBreak::Normal => LineBreakStrictness::Normal,
1963 LineBreak::Strict => LineBreakStrictness::Strict,
1964 LineBreak::Anywhere => LineBreakStrictness::Anywhere,
1965 LineBreak::Auto => LineBreakStrictness::Normal,
1968 };
1969 options.word_option = match word_break {
1970 WordBreak::Normal => LineBreakWordOption::Normal,
1971 WordBreak::BreakAll => LineBreakWordOption::BreakAll,
1972 WordBreak::KeepAll => LineBreakWordOption::KeepAll,
1973 };
1974 options.ja_zh = {
1977 lang.0.parse::<LanguageIdentifier>().is_ok_and(|lang_id| {
1978 const JA: Language = language!("ja");
1979 const ZH: Language = language!("zh");
1980 matches!(lang_id.language, JA | ZH)
1981 })
1982 };
1983
1984 let mut new_linebreaker = LineBreaker::new(text_content.as_str(), options);
1985 for item in &mut builder.inline_items {
1986 match item {
1987 InlineItem::TextRun(text_run) => {
1988 text_run.borrow_mut().segment_and_shape(
1989 &text_content,
1990 layout_context,
1991 &mut new_linebreaker,
1992 &bidi_levels,
1993 );
1994 },
1995 InlineItem::StartInlineBox(inline_box) => {
1996 let inline_box = &mut *inline_box.borrow_mut();
1997 if let Some(font) = get_font_for_first_font_for_style(
1998 &inline_box.base.style,
1999 &layout_context.font_context,
2000 ) {
2001 inline_box.default_font = Some(font);
2002 }
2003 },
2004 InlineItem::Atomic(_, index_in_text, bidi_level) => {
2005 *bidi_level = bidi_levels.level(*index_in_text);
2006 },
2007 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) |
2008 InlineItem::OutOfFlowFloatBox(_) |
2009 InlineItem::EndInlineBox(..) |
2010 InlineItem::BlockLevel { .. } => {},
2011 }
2012 }
2013
2014 let default_font = get_font_for_first_font_for_style(
2015 &shared_inline_styles.style.borrow(),
2016 &layout_context.font_context,
2017 );
2018
2019 let has_right_to_left_content = bidi_levels.info.as_ref().is_some_and(BidiInfo::has_rtl);
2020 InlineFormattingContext {
2021 text_content,
2022 inline_items: builder.inline_items,
2023 inline_boxes: builder.inline_boxes,
2024 shared_inline_styles,
2025 default_font,
2026 has_first_formatted_line,
2027 contains_floats: builder.contains_floats,
2028 is_single_line_text_input,
2029 has_right_to_left_content,
2030 shared_selection: builder.shared_selection,
2031 tab_size_multiplier: Default::default(),
2032 }
2033 }
2034
2035 pub(crate) fn repair_style(
2036 &self,
2037 context: &SharedStyleContext,
2038 node: &ServoLayoutNode,
2039 new_style: &ServoArc<ComputedValues>,
2040 ) {
2041 *self.shared_inline_styles.style.borrow_mut() = new_style.clone();
2042 *self.shared_inline_styles.selected.borrow_mut() = node.selected_style(context);
2043 }
2044
2045 fn inline_start_for_first_line(&self, containing_block: IndefiniteContainingBlock) -> Au {
2046 if !self.has_first_formatted_line {
2047 return Au::zero();
2048 }
2049 containing_block
2050 .style
2051 .get_inherited_text()
2052 .text_indent
2053 .length
2054 .to_used_value(containing_block.size.inline.unwrap_or_default())
2055 }
2056
2057 pub(super) fn layout(
2058 &self,
2059 layout_context: &LayoutContext,
2060 positioning_context: &mut PositioningContext,
2061 containing_block: &ContainingBlock,
2062 sequential_layout_state: Option<&mut SequentialLayoutState>,
2063 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
2064 ) -> IndependentFormattingContextLayoutResult {
2065 for inline_box in self.inline_boxes.iter() {
2067 inline_box.borrow().base.clear_fragments();
2068 }
2069
2070 let style = containing_block.style;
2071
2072 let style_text = containing_block.style.get_inherited_text();
2073 let mut inline_container_state_flags = InlineContainerStateFlags::empty();
2074 if inline_container_needs_strut(style, layout_context, None) {
2075 inline_container_state_flags.insert(InlineContainerStateFlags::CREATE_STRUT);
2076 }
2077 if self.is_single_line_text_input {
2078 inline_container_state_flags
2079 .insert(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT);
2080 }
2081 let placement_state =
2082 PlacementState::new(collapsible_with_parent_start_margin, containing_block);
2083
2084 let mut layout = InlineFormattingContextLayout {
2085 positioning_context,
2086 placement_state,
2087 sequential_layout_state,
2088 layout_context,
2089 ifc: self,
2090 fragments: Vec::new(),
2091 current_line: LineUnderConstruction::new(LogicalVec2 {
2092 inline: self.inline_start_for_first_line(containing_block.into()),
2093 block: Au::zero(),
2094 }),
2095 root_nesting_level: InlineContainerState::new(
2096 style.to_arc(),
2097 inline_container_state_flags,
2098 None, self.default_font.clone(),
2100 ),
2101 inline_box_state_stack: Vec::new(),
2102 cloneable_inline_box_end_pbm_size: Au::zero(),
2103 inline_box_states: Vec::with_capacity(self.inline_boxes.len()),
2104 current_line_segment: UnbreakableSegmentUnderConstruction::new(),
2105 force_line_break_before_new_content: None,
2106 deferred_br_clear: Clear::None,
2107 have_deferred_soft_wrap_opportunity: false,
2108 depends_on_block_constraints: false,
2109 white_space_collapse: style_text.white_space_collapse,
2110 text_wrap_mode: style_text.text_wrap_mode,
2111 };
2112
2113 for item in self.inline_items.iter() {
2114 if !matches!(item, InlineItem::EndInlineBox(..)) {
2116 layout.possibly_flush_deferred_forced_line_break();
2117 }
2118
2119 match item {
2120 InlineItem::StartInlineBox(inline_box) => {
2121 layout.start_inline_box(&inline_box.borrow());
2122 },
2123 InlineItem::EndInlineBox(..) => layout.finish_inline_box(),
2124 InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout),
2125 InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
2126 atomic_formatting_context.borrow().layout_into_line_items(
2127 &mut layout,
2128 *offset_in_text,
2129 *bidi_level,
2130 );
2131 },
2132 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, _) => {
2133 layout.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
2134 layout.current_inline_box_identifier(),
2135 AbsolutelyPositionedLineItem {
2136 absolutely_positioned_box: positioned_box.clone(),
2137 preceding_line_content_would_produce_phantom_line: layout
2138 .current_line
2139 .is_phantom() &&
2140 layout.current_line_segment.is_phantom(),
2141 },
2142 ));
2143 },
2144 InlineItem::OutOfFlowFloatBox(float_box) => {
2145 float_box.borrow().layout_into_line_items(&mut layout);
2146 },
2147 InlineItem::BlockLevel(block_level) => {
2148 block_level.borrow().layout_into_line_items(&mut layout);
2149 },
2150 }
2151 }
2152
2153 layout.finish_last_line();
2154 let (content_block_size, collapsible_margins_in_children, baselines) =
2155 layout.placement_state.finish();
2156
2157 IndependentFormattingContextLayoutResult {
2158 fragments: layout.fragments,
2159 content_block_size,
2160 collapsible_margins_in_children,
2161 baselines,
2162 depends_on_block_constraints: layout.depends_on_block_constraints,
2163 content_inline_size_for_table: None,
2164 specific_layout_info: None,
2165 }
2166 }
2167
2168 pub(crate) fn subtree_size(&self) -> usize {
2169 self.inline_items
2170 .iter()
2171 .map(|item| match item {
2172 InlineItem::StartInlineBox(..) => 1,
2173 InlineItem::EndInlineBox(..) => 0,
2174 InlineItem::TextRun(..) => 1,
2175 InlineItem::OutOfFlowAbsolutelyPositionedBox(absolutely_positioned_box, _) => {
2176 absolutely_positioned_box
2177 .borrow()
2178 .context
2179 .base
2180 .subtree_size()
2181 },
2182 InlineItem::OutOfFlowFloatBox(..) => 1,
2183 InlineItem::Atomic(..) => 1,
2184 InlineItem::BlockLevel(block_level_box) => block_level_box.borrow().subtree_size(),
2185 })
2186 .sum()
2187 }
2188
2189 fn next_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
2190 let Some(character) = self.text_content[index..].chars().nth(1) else {
2191 return false;
2192 };
2193 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2194 }
2195
2196 fn previous_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
2197 let Some(character) = self.text_content[0..index].chars().next_back() else {
2198 return false;
2199 };
2200 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2201 }
2202
2203 pub(crate) fn find_block_margin_collapsing_with_parent(
2204 &self,
2205 layout_context: &LayoutContext,
2206 collected_margin: &mut CollapsedMargin,
2207 containing_block_for_children: &ContainingBlock,
2208 ) -> bool {
2209 let mut items_iter = self.inline_items.iter();
2215 items_iter.all(|inline_item| match inline_item {
2216 InlineItem::StartInlineBox(inline_box) => {
2217 let pbm = inline_box
2218 .borrow()
2219 .layout_style()
2220 .padding_border_margin(containing_block_for_children);
2221 pbm.padding.inline_start.is_zero() &&
2222 pbm.border.inline_start.is_zero() &&
2223 pbm.margin.inline_start.auto_is(Au::zero).is_zero()
2224 },
2225 InlineItem::EndInlineBox(inline_box) => {
2226 let pbm = inline_box
2227 .borrow()
2228 .layout_style()
2229 .padding_border_margin(containing_block_for_children);
2230 pbm.padding.inline_end.is_zero() &&
2231 pbm.border.inline_end.is_zero() &&
2232 pbm.margin.inline_end.auto_is(Au::zero).is_zero()
2233 },
2234 InlineItem::TextRun(text_run) => {
2235 let text_run = &*text_run.borrow();
2236 let parent_style = text_run.inline_styles.style.borrow();
2237 text_run.items.iter().all(|item| match item {
2238 TextRunItem::LineBreak { .. } => false,
2239 TextRunItem::Tab { .. } => false,
2240 TextRunItem::TextSegment(segment) => segment.runs.iter().all(|run| {
2241 run.is_whitespace() &&
2242 !matches!(
2243 parent_style.get_inherited_text().white_space_collapse,
2244 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
2245 )
2246 }),
2247 })
2248 },
2249 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => true,
2250 InlineItem::OutOfFlowFloatBox(..) => true,
2251 InlineItem::Atomic(..) => false,
2252 InlineItem::BlockLevel(block_level) => block_level
2253 .borrow()
2254 .find_block_margin_collapsing_with_parent(
2255 layout_context,
2256 collected_margin,
2257 containing_block_for_children,
2258 ),
2259 })
2260 }
2261
2262 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
2263 let mut parent_box_stack = Vec::new();
2264 let current_parent_box = |parent_box_stack: &[WeakLayoutBox]| {
2265 parent_box_stack.last().unwrap_or(&layout_box).clone()
2266 };
2267 for inline_item in &self.inline_items {
2268 match inline_item {
2269 InlineItem::StartInlineBox(inline_box) => {
2270 inline_box
2271 .borrow_mut()
2272 .base
2273 .parent_box
2274 .replace(current_parent_box(&parent_box_stack));
2275 parent_box_stack.push(WeakLayoutBox::InlineLevel(
2276 WeakInlineItem::StartInlineBox(inline_box.downgrade()),
2277 ));
2278 },
2279 InlineItem::EndInlineBox(..) => {
2280 parent_box_stack.pop();
2281 },
2282 InlineItem::TextRun(text_run) => {
2283 text_run
2284 .borrow_mut()
2285 .parent_box
2286 .replace(current_parent_box(&parent_box_stack));
2287 },
2288 _ => inline_item.with_base_mut(|base| {
2289 base.parent_box
2290 .replace(current_parent_box(&parent_box_stack));
2291 }),
2292 }
2293 }
2294 }
2295
2296 pub(crate) fn next_tab_stop_after_inline_advance(
2297 &self,
2298 style: &ServoArc<ComputedValues>,
2299 current_inline_advance: Au,
2300 ) -> Au {
2301 let Some(font) = self.default_font.as_ref() else {
2302 return Au::zero();
2303 };
2304
2305 let tab_size_multiplier = *self.tab_size_multiplier.get_or_init(|| {
2306 let root_style = self.shared_inline_styles.style.borrow();
2307 let inherited_text_style = root_style.get_inherited_text();
2308 let font_size = root_style.get_font().font_size.computed_size().into();
2309 let letter_spacing = inherited_text_style
2310 .letter_spacing
2311 .0
2312 .to_used_value(font_size);
2313 let word_spacing = inherited_text_style.word_spacing.to_used_value(font_size);
2314
2315 font.metrics.space_advance + word_spacing + letter_spacing
2318 });
2319
2320 let tab_stop_advance = match style.get_inherited_text().tab_size {
2321 style::values::generics::length::LengthOrNumber::Number(number_of_spaces) => {
2322 tab_size_multiplier.scale_by(number_of_spaces.0)
2323 },
2324 style::values::generics::length::LengthOrNumber::Length(length) => length.into(),
2326 };
2327
2328 if tab_stop_advance.is_zero() {
2329 return Au::zero();
2330 }
2331
2332 let half_ch_advance = font
2338 .metrics
2339 .zero_horizontal_advance
2340 .unwrap_or(font.metrics.em_size.scale_by(0.5))
2341 .scale_by(0.5);
2342 let number_of_tab_stops =
2343 (current_inline_advance + half_ch_advance).to_f32_px() / tab_stop_advance.to_f32_px();
2344 let number_of_tab_stops = number_of_tab_stops.ceil();
2345 tab_stop_advance.scale_by(number_of_tab_stops) - current_inline_advance
2346 }
2347}
2348
2349impl InlineContainerState {
2350 fn new(
2351 style: ServoArc<ComputedValues>,
2352 flags: InlineContainerStateFlags,
2353 parent_container: Option<&InlineContainerState>,
2354 default_font: Option<FontRef>,
2355 ) -> Self {
2356 let font_metrics = default_font
2357 .as_ref()
2358 .map(|font| font.metrics.clone())
2359 .unwrap_or_else(FontMetrics::empty);
2360 let mut baseline_offset = Au::zero();
2361 let mut strut_block_sizes = {
2362 Self::get_block_sizes_with_style(
2363 effective_baseline_shift(&style, parent_container),
2364 &style,
2365 &font_metrics,
2366 &font_metrics,
2367 &flags,
2368 )
2369 };
2370
2371 if let Some(parent_container) = parent_container {
2372 baseline_offset = parent_container.get_cumulative_baseline_offset_for_child(
2375 style.clone_alignment_baseline(),
2376 style.clone_baseline_shift(),
2377 &strut_block_sizes,
2378 );
2379 strut_block_sizes.adjust_for_baseline_offset(baseline_offset);
2380 }
2381
2382 let mut nested_block_sizes = parent_container
2383 .map(|container| container.nested_strut_block_sizes.clone())
2384 .unwrap_or_else(LineBlockSizes::zero);
2385 if flags.contains(InlineContainerStateFlags::CREATE_STRUT) {
2386 nested_block_sizes.max_assign(&strut_block_sizes);
2387 }
2388
2389 Self {
2390 style,
2391 flags,
2392 has_content: Cell::new(false),
2393 nested_strut_block_sizes: nested_block_sizes,
2394 strut_block_sizes,
2395 baseline_offset,
2396 default_font,
2397 font_metrics,
2398 }
2399 }
2400
2401 fn get_block_sizes_with_style(
2402 baseline_shift: BaselineShift,
2403 style: &ComputedValues,
2404 font_metrics: &FontMetrics,
2405 font_metrics_of_first_font: &FontMetrics,
2406 flags: &InlineContainerStateFlags,
2407 ) -> LineBlockSizes {
2408 let line_height = line_height(style, font_metrics, flags);
2409
2410 if !is_baseline_relative(baseline_shift) {
2411 return LineBlockSizes {
2412 line_height,
2413 baseline_relative_size_for_line_height: None,
2414 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2415 };
2416 }
2417
2418 let mut ascent = font_metrics.ascent;
2427 let mut descent = font_metrics.descent;
2428 if style.get_font().line_height == LineHeight::Normal {
2429 let half_leading_from_line_gap =
2430 (font_metrics.line_gap - descent - ascent).scale_by(0.5);
2431 ascent += half_leading_from_line_gap;
2432 descent += half_leading_from_line_gap;
2433 }
2434
2435 let size_for_baseline_positioning = BaselineRelativeSize { ascent, descent };
2439
2440 if style.get_font().line_height != LineHeight::Normal {
2456 ascent = font_metrics_of_first_font.ascent;
2457 descent = font_metrics_of_first_font.descent;
2458 let half_leading = (line_height - (ascent + descent)).scale_by(0.5);
2459 ascent += half_leading;
2464 descent = line_height - ascent;
2465 }
2466
2467 LineBlockSizes {
2468 line_height,
2469 baseline_relative_size_for_line_height: Some(BaselineRelativeSize { ascent, descent }),
2470 size_for_baseline_positioning,
2471 }
2472 }
2473
2474 fn get_block_size_contribution(
2475 &self,
2476 baseline_shift: BaselineShift,
2477 font_metrics: &FontMetrics,
2478 font_metrics_of_first_font: &FontMetrics,
2479 ) -> LineBlockSizes {
2480 Self::get_block_sizes_with_style(
2481 baseline_shift,
2482 &self.style,
2483 font_metrics,
2484 font_metrics_of_first_font,
2485 &self.flags,
2486 )
2487 }
2488
2489 fn get_cumulative_baseline_offset_for_child(
2490 &self,
2491 child_alignment_baseline: AlignmentBaseline,
2492 child_baseline_shift: BaselineShift,
2493 child_block_size: &LineBlockSizes,
2494 ) -> Au {
2495 let block_size = self.get_block_size_contribution(
2496 child_baseline_shift.clone(),
2497 &self.font_metrics,
2498 &self.font_metrics,
2499 );
2500 self.baseline_offset +
2501 match child_alignment_baseline {
2502 AlignmentBaseline::Baseline => Au::zero(),
2503 AlignmentBaseline::TextTop => {
2504 child_block_size.size_for_baseline_positioning.ascent - self.font_metrics.ascent
2505 },
2506 AlignmentBaseline::Middle => {
2507 (child_block_size.size_for_baseline_positioning.ascent -
2510 child_block_size.size_for_baseline_positioning.descent -
2511 self.font_metrics.x_height)
2512 .scale_by(0.5)
2513 },
2514 AlignmentBaseline::TextBottom => {
2515 self.font_metrics.descent -
2516 child_block_size.size_for_baseline_positioning.descent
2517 },
2518 } +
2519 match child_baseline_shift {
2520 BaselineShift::Keyword(
2525 BaselineShiftKeyword::Top |
2526 BaselineShiftKeyword::Bottom |
2527 BaselineShiftKeyword::Center,
2528 ) => Au::zero(),
2529 BaselineShift::Keyword(BaselineShiftKeyword::Sub) => {
2530 block_size.resolve().scale_by(FONT_SUBSCRIPT_OFFSET_RATIO)
2531 },
2532 BaselineShift::Keyword(BaselineShiftKeyword::Super) => {
2533 -block_size.resolve().scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO)
2534 },
2535 BaselineShift::Length(length_percentage) => {
2536 -length_percentage.to_used_value(child_block_size.line_height)
2537 },
2538 }
2539 }
2540}
2541
2542impl IndependentFormattingContext {
2543 fn layout_into_line_items(
2544 &self,
2545 layout: &mut InlineFormattingContextLayout,
2546 offset_in_text: usize,
2547 bidi_level: Level,
2548 ) {
2549 let mut child_positioning_context = PositioningContext::default();
2551 let IndependentFloatOrAtomicLayoutResult {
2552 mut fragment,
2553 baselines,
2554 pbm_sums,
2555 } = self.layout_float_or_atomic_inline(
2556 layout.layout_context,
2557 &mut child_positioning_context,
2558 layout.containing_block(),
2559 );
2560
2561 layout.depends_on_block_constraints |= fragment.base.flags.contains(
2564 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2565 );
2566
2567 let container_writing_mode = layout.containing_block().style.writing_mode;
2569 let pbm_physical_offset = pbm_sums
2570 .start_offset()
2571 .to_physical_size(container_writing_mode);
2572 fragment.base.translate_rect(pbm_physical_offset);
2573
2574 fragment = fragment.with_baselines(baselines);
2576
2577 let positioning_context = if self.is_replaced() {
2580 None
2581 } else {
2582 if fragment
2583 .style()
2584 .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
2585 {
2586 child_positioning_context
2587 .layout_collected_children(layout.layout_context, &mut fragment);
2588 }
2589 Some(child_positioning_context)
2590 };
2591
2592 if layout.text_wrap_mode == TextWrapMode::Wrap &&
2593 !layout
2594 .ifc
2595 .previous_character_prevents_soft_wrap_opportunity(offset_in_text)
2596 {
2597 layout.process_soft_wrap_opportunity();
2598 }
2599
2600 let size = pbm_sums.sum() + fragment.base.rect().size.to_logical(container_writing_mode);
2601 let baseline_offset = self
2602 .pick_baseline(&fragment.baselines(container_writing_mode))
2603 .map(|baseline| pbm_sums.block_start + baseline)
2604 .unwrap_or(size.block);
2605
2606 let (block_sizes, baseline_offset_in_parent) =
2607 self.get_block_sizes_and_baseline_offset(layout, size.block, baseline_offset);
2608 layout.update_unbreakable_segment_for_new_content(
2609 &block_sizes,
2610 size.inline,
2611 SegmentContentFlags::empty(),
2612 );
2613
2614 let fragment = Arc::new(fragment);
2615 self.base.set_fragment(Fragment::Box(fragment.clone()));
2616
2617 layout.push_line_item_to_unbreakable_segment(LineItem::Atomic(
2618 layout.current_inline_box_identifier(),
2619 AtomicLineItem {
2620 fragment,
2621 size,
2622 positioning_context,
2623 baseline_offset_in_parent,
2624 baseline_offset_in_item: baseline_offset,
2625 bidi_level,
2626 },
2627 ));
2628
2629 if !layout
2632 .ifc
2633 .next_character_prevents_soft_wrap_opportunity(offset_in_text)
2634 {
2635 layout.have_deferred_soft_wrap_opportunity = true;
2636 }
2637 }
2638
2639 fn pick_baseline(&self, baselines: &Baselines) -> Option<Au> {
2643 match self.style().clone_baseline_source() {
2644 BaselineSource::First => baselines.first,
2645 BaselineSource::Last => baselines.last,
2646 BaselineSource::Auto if self.is_block_container() => baselines.last,
2647 BaselineSource::Auto => baselines.first,
2648 }
2649 }
2650
2651 fn get_block_sizes_and_baseline_offset(
2652 &self,
2653 ifc: &InlineFormattingContextLayout,
2654 block_size: Au,
2655 baseline_offset_in_content_area: Au,
2656 ) -> (LineBlockSizes, Au) {
2657 let mut contribution = if !is_baseline_relative(self.style().clone_baseline_shift()) {
2658 LineBlockSizes {
2659 line_height: block_size,
2660 baseline_relative_size_for_line_height: None,
2661 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2662 }
2663 } else {
2664 let baseline_relative_size = BaselineRelativeSize {
2665 ascent: baseline_offset_in_content_area,
2666 descent: block_size - baseline_offset_in_content_area,
2667 };
2668 LineBlockSizes {
2669 line_height: block_size,
2670 baseline_relative_size_for_line_height: Some(baseline_relative_size.clone()),
2671 size_for_baseline_positioning: baseline_relative_size,
2672 }
2673 };
2674
2675 let style = self.style();
2676 let baseline_offset = ifc
2677 .current_inline_container_state()
2678 .get_cumulative_baseline_offset_for_child(
2679 style.clone_alignment_baseline(),
2680 style.clone_baseline_shift(),
2681 &contribution,
2682 );
2683 contribution.adjust_for_baseline_offset(baseline_offset);
2684
2685 (contribution, baseline_offset)
2686 }
2687}
2688
2689impl FloatBox {
2690 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
2691 let old_len = layout.positioning_context.len();
2692 let fragment = Arc::new(self.layout(
2693 layout.layout_context,
2694 layout.positioning_context,
2695 layout.placement_state.containing_block,
2696 ));
2697 let new_len = layout.positioning_context.len();
2698
2699 self.contents
2700 .base
2701 .set_fragment(Fragment::Box(fragment.clone()));
2702 layout.push_line_item_to_unbreakable_segment(LineItem::Float(
2703 layout.current_inline_box_identifier(),
2704 FloatLineItem {
2705 fragment,
2706 needs_placement: true,
2707 range: old_len..new_len,
2708 },
2709 ));
2710 }
2711}
2712
2713fn place_pending_floats(ifc: &mut InlineFormattingContextLayout, line_items: &[LineItem]) {
2714 for item in line_items.iter() {
2715 if let LineItem::Float(_, float_line_item) = item &&
2716 float_line_item.needs_placement
2717 {
2718 ifc.place_float_fragment(float_line_item);
2719 }
2720 }
2721}
2722
2723fn line_height(
2724 parent_style: &ComputedValues,
2725 font_metrics: &FontMetrics,
2726 flags: &InlineContainerStateFlags,
2727) -> Au {
2728 let font = parent_style.get_font();
2729 let font_size = font.font_size.computed_size();
2730 let mut line_height = match font.line_height {
2731 LineHeight::Normal => font_metrics.line_gap,
2732 LineHeight::Number(number) => (font_size * number.0).into(),
2733 LineHeight::Length(length) => length.0.into(),
2734 };
2735
2736 if flags.contains(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT) {
2740 line_height.max_assign(font_metrics.line_gap);
2741 }
2742
2743 line_height
2744}
2745
2746fn effective_baseline_shift(
2747 style: &ComputedValues,
2748 container: Option<&InlineContainerState>,
2749) -> BaselineShift {
2750 if container.is_none() {
2751 BaselineShift::zero()
2755 } else {
2756 style.clone_baseline_shift()
2757 }
2758}
2759
2760fn is_baseline_relative(baseline_shift: BaselineShift) -> bool {
2761 !matches!(
2762 baseline_shift,
2763 BaselineShift::Keyword(
2764 BaselineShiftKeyword::Top | BaselineShiftKeyword::Bottom | BaselineShiftKeyword::Center
2765 )
2766 )
2767}
2768
2769fn inline_container_needs_strut(
2795 style: &ComputedValues,
2796 layout_context: &LayoutContext,
2797 pbm: Option<&PaddingBorderMargin>,
2798) -> bool {
2799 if layout_context.style_context.quirks_mode() == QuirksMode::NoQuirks {
2800 return true;
2801 }
2802
2803 if style.get_box().display.is_list_item() {
2806 return true;
2807 }
2808
2809 pbm.is_some_and(|pbm| !pbm.padding_border_sums.inline.is_zero())
2810}
2811
2812impl ComputeInlineContentSizes for InlineFormattingContext {
2813 fn compute_inline_content_sizes(
2817 &self,
2818 layout_context: &LayoutContext,
2819 constraint_space: &ConstraintSpace,
2820 ) -> InlineContentSizesResult {
2821 ContentSizesComputation::compute(self, layout_context, constraint_space)
2822 }
2823}
2824
2825struct ContentSizesComputation<'layout_data> {
2827 layout_context: &'layout_data LayoutContext<'layout_data>,
2828 constraint_space: &'layout_data ConstraintSpace<'layout_data>,
2829 paragraph: ContentSizes,
2830 current_line: ContentSizes,
2831 pending_whitespace: ContentSizes,
2833 uncleared_floats: LogicalSides1D<ContentSizes>,
2835 cleared_floats: LogicalSides1D<ContentSizes>,
2837 had_content_yet_for_min_content: bool,
2840 had_content_yet_for_max_content: bool,
2843 ending_inline_pbm_stack: Vec<Au>,
2846 depends_on_block_constraints: bool,
2848}
2849
2850impl<'layout_data> ContentSizesComputation<'layout_data> {
2851 fn traverse(
2852 mut self,
2853 inline_formatting_context: &InlineFormattingContext,
2854 ) -> InlineContentSizesResult {
2855 self.add_inline_size(
2856 inline_formatting_context.inline_start_for_first_line(self.constraint_space.into()),
2857 );
2858 for inline_item in &inline_formatting_context.inline_items {
2859 self.process_item(inline_item, inline_formatting_context);
2860 }
2861 self.forced_line_break();
2862 self.flush_floats();
2863
2864 InlineContentSizesResult {
2865 sizes: self.paragraph,
2866 depends_on_block_constraints: self.depends_on_block_constraints,
2867 }
2868 }
2869
2870 fn process_item(
2871 &mut self,
2872 inline_item: &InlineItem,
2873 inline_formatting_context: &InlineFormattingContext,
2874 ) {
2875 match inline_item {
2876 InlineItem::StartInlineBox(inline_box) => {
2877 let inline_box = inline_box.borrow();
2881 let zero = Au::zero();
2882 let writing_mode = self.constraint_space.style.writing_mode;
2883 let layout_style = inline_box.layout_style();
2884 let padding = layout_style
2885 .padding(writing_mode)
2886 .percentages_relative_to(zero);
2887 let border = layout_style.border_width(writing_mode);
2888 let margin = inline_box
2889 .base
2890 .style
2891 .margin(writing_mode)
2892 .percentages_relative_to(zero)
2893 .auto_is(Au::zero);
2894
2895 let pbm = margin + padding + border;
2896 self.add_inline_size(pbm.inline_start);
2897 self.ending_inline_pbm_stack.push(pbm.inline_end);
2898 },
2899 InlineItem::EndInlineBox(..) => {
2900 let length = self.ending_inline_pbm_stack.pop().unwrap_or_else(Au::zero);
2901 self.add_inline_size(length);
2902 },
2903 InlineItem::TextRun(text_run) => {
2904 let text_run = &*text_run.borrow();
2905 let parent_style = text_run.inline_styles.style.borrow();
2906 for item in text_run.items.iter() {
2907 match item {
2908 TextRunItem::LineBreak { .. } => {
2909 self.forced_line_break();
2912 },
2913 TextRunItem::Tab { .. } => {
2914 self.process_preserved_tab(&parent_style, inline_formatting_context)
2915 },
2916 TextRunItem::TextSegment(segment) => {
2917 self.process_text_segment(&parent_style, segment)
2918 },
2919 }
2920 }
2921 },
2922 InlineItem::Atomic(atomic, offset_in_text, _level) => {
2923 if self.had_content_yet_for_min_content &&
2925 !inline_formatting_context
2926 .previous_character_prevents_soft_wrap_opportunity(*offset_in_text)
2927 {
2928 self.line_break_opportunity();
2929 }
2930
2931 self.commit_pending_whitespace();
2932 let outer = self.outer_inline_content_sizes_of_float_or_atomic(&atomic.borrow());
2933 self.current_line += outer;
2934
2935 if !inline_formatting_context
2937 .next_character_prevents_soft_wrap_opportunity(*offset_in_text)
2938 {
2939 self.line_break_opportunity();
2940 }
2941 },
2942 InlineItem::OutOfFlowFloatBox(float_box) => {
2943 let float_box = float_box.borrow();
2944 let sizes = self.outer_inline_content_sizes_of_float_or_atomic(&float_box.contents);
2945 let style = &float_box.contents.style();
2946 let container_writing_mode = self.constraint_space.style.writing_mode;
2947 let clear =
2948 Clear::from_style_and_container_writing_mode(style, container_writing_mode);
2949 self.clear_floats(clear);
2950 let float_side =
2951 FloatSide::from_style_and_container_writing_mode(style, container_writing_mode);
2952 match float_side.expect("A float box needs to float to some side") {
2953 FloatSide::InlineStart => self.uncleared_floats.start.union_assign(&sizes),
2954 FloatSide::InlineEnd => self.uncleared_floats.end.union_assign(&sizes),
2955 }
2956 },
2957 InlineItem::BlockLevel(block_level) => {
2958 self.forced_line_break();
2959 self.flush_floats();
2960 let inline_content_sizes_result =
2961 compute_inline_content_sizes_for_block_level_boxes(
2962 std::slice::from_ref(block_level),
2963 self.layout_context,
2964 &self.constraint_space.into(),
2965 );
2966 self.depends_on_block_constraints |=
2967 inline_content_sizes_result.depends_on_block_constraints;
2968 self.current_line = inline_content_sizes_result.sizes;
2969 self.forced_line_break();
2970 },
2971 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => {},
2972 }
2973 }
2974
2975 fn process_text_segment(
2976 &mut self,
2977 parent_style: &AtomicRef<'_, ServoArc<ComputedValues>>,
2978 segment: &TextRunSegment,
2979 ) {
2980 let style_text = parent_style.get_inherited_text();
2981 let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap;
2982
2983 let break_at_start = segment.break_at_start && self.had_content_yet_for_min_content;
2986
2987 for (run_index, run) in segment.runs.iter().enumerate() {
2988 if can_wrap && (run_index != 0 || break_at_start) {
2991 self.line_break_opportunity();
2992 }
2993
2994 let advance = run.total_advance();
2995 if run.is_whitespace() {
2996 if !matches!(
2997 style_text.white_space_collapse,
2998 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
2999 ) {
3000 if self.had_content_yet_for_min_content {
3001 if can_wrap {
3002 self.line_break_opportunity();
3003 } else {
3004 self.pending_whitespace.min_content += advance;
3005 }
3006 }
3007 if self.had_content_yet_for_max_content {
3008 self.pending_whitespace.max_content += advance;
3009 }
3010 continue;
3011 }
3012 if can_wrap {
3013 self.pending_whitespace.max_content += advance;
3014 self.commit_pending_whitespace();
3015 self.line_break_opportunity();
3016 continue;
3017 }
3018 }
3019
3020 self.commit_pending_whitespace();
3021 self.add_inline_size(advance);
3022
3023 if can_wrap && run.ends_with_whitespace() {
3028 self.line_break_opportunity();
3029 }
3030 }
3031 }
3032
3033 fn process_preserved_tab(
3034 &mut self,
3035 parent_style: &AtomicRef<'_, ServoArc<ComputedValues>>,
3036 inline_formatting_context: &InlineFormattingContext,
3037 ) {
3038 self.commit_pending_whitespace();
3040
3041 self.current_line.min_content += inline_formatting_context
3042 .next_tab_stop_after_inline_advance(parent_style, self.current_line.min_content);
3043 self.current_line.max_content += inline_formatting_context
3044 .next_tab_stop_after_inline_advance(parent_style, self.current_line.max_content);
3045 if parent_style.get_inherited_text().text_wrap_mode == TextWrapMode::Wrap {
3046 self.line_break_opportunity();
3047 }
3048 }
3049
3050 fn add_inline_size(&mut self, l: Au) {
3051 self.current_line.min_content += l;
3052 self.current_line.max_content += l;
3053 }
3054
3055 fn line_break_opportunity(&mut self) {
3056 self.pending_whitespace.min_content = Au::zero();
3060 let current_min_content = mem::take(&mut self.current_line.min_content);
3061 self.paragraph.min_content.max_assign(current_min_content);
3062 self.had_content_yet_for_min_content = false;
3063 }
3064
3065 fn forced_line_break(&mut self) {
3066 self.line_break_opportunity();
3068
3069 self.pending_whitespace.max_content = Au::zero();
3071 let current_max_content = mem::take(&mut self.current_line.max_content);
3072 self.paragraph.max_content.max_assign(current_max_content);
3073 self.had_content_yet_for_max_content = false;
3074 }
3075
3076 fn commit_pending_whitespace(&mut self) {
3077 self.current_line += mem::take(&mut self.pending_whitespace);
3078 self.had_content_yet_for_min_content = true;
3079 self.had_content_yet_for_max_content = true;
3080 }
3081
3082 fn outer_inline_content_sizes_of_float_or_atomic(
3083 &mut self,
3084 context: &IndependentFormattingContext,
3085 ) -> ContentSizes {
3086 let result = context.outer_inline_content_sizes(
3087 self.layout_context,
3088 &self.constraint_space.into(),
3089 &LogicalVec2::zero(),
3090 false, );
3092 self.depends_on_block_constraints |= result.depends_on_block_constraints;
3093 result.sizes
3094 }
3095
3096 fn clear_floats(&mut self, clear: Clear) {
3097 match clear {
3098 Clear::InlineStart => {
3099 let start_floats = mem::take(&mut self.uncleared_floats.start);
3100 self.cleared_floats.start.max_assign(start_floats);
3101 },
3102 Clear::InlineEnd => {
3103 let end_floats = mem::take(&mut self.uncleared_floats.end);
3104 self.cleared_floats.end.max_assign(end_floats);
3105 },
3106 Clear::Both => {
3107 let start_floats = mem::take(&mut self.uncleared_floats.start);
3108 let end_floats = mem::take(&mut self.uncleared_floats.end);
3109 self.cleared_floats.start.max_assign(start_floats);
3110 self.cleared_floats.end.max_assign(end_floats);
3111 },
3112 Clear::None => {},
3113 }
3114 }
3115
3116 fn flush_floats(&mut self) {
3117 self.clear_floats(Clear::Both);
3118 let start_floats = mem::take(&mut self.cleared_floats.start);
3119 let end_floats = mem::take(&mut self.cleared_floats.end);
3120 self.paragraph.union_assign(&start_floats);
3121 self.paragraph.union_assign(&end_floats);
3122 }
3123
3124 fn compute(
3126 inline_formatting_context: &InlineFormattingContext,
3127 layout_context: &'layout_data LayoutContext,
3128 constraint_space: &'layout_data ConstraintSpace,
3129 ) -> InlineContentSizesResult {
3130 Self {
3131 layout_context,
3132 constraint_space,
3133 paragraph: ContentSizes::zero(),
3134 current_line: ContentSizes::zero(),
3135 pending_whitespace: ContentSizes::zero(),
3136 uncleared_floats: LogicalSides1D::default(),
3137 cleared_floats: LogicalSides1D::default(),
3138 had_content_yet_for_min_content: false,
3139 had_content_yet_for_max_content: false,
3140 ending_inline_pbm_stack: Vec::new(),
3141 depends_on_block_constraints: false,
3142 }
3143 .traverse(inline_formatting_context)
3144 }
3145}
3146
3147pub(crate) struct BidiLevels<'a> {
3148 info: Option<BidiInfo<'a>>,
3149}
3150
3151impl BidiLevels<'_> {
3152 fn level(&self, byte_offset_in_ifc_text: usize) -> Level {
3153 self.info
3154 .as_ref()
3155 .map_or_else(Level::ltr, |info| info.levels[byte_offset_in_ifc_text])
3156 }
3157}
3158
3159fn char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character: char) -> bool {
3171 if character == '\u{00A0}' {
3172 return false;
3173 }
3174 matches!(
3175 icu_properties::maps::line_break().get(character),
3176 ICULineBreak::Glue | ICULineBreak::WordJoiner | ICULineBreak::ZWJ
3177 )
3178}