1pub mod construct;
72pub mod inline_box;
73pub mod line;
74mod line_breaker;
75pub mod text_run;
76
77use std::cell::{OnceCell, RefCell};
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, BoxFragment, 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(true);
225 layout.current_line.for_block_level = true;
226
227 let fragment = layout_block_level_child(
228 layout.layout_context,
229 layout.positioning_context,
230 self,
231 layout.sequential_layout_state.as_deref_mut(),
232 &mut layout.placement_state,
233 LogicalSides1D::new(false, false),
235 true, );
237
238 let Some(fragment) = fragment.retrieve_box_fragment() else {
239 unreachable!("The fragment should be a Fragment::Box()");
240 };
241
242 layout.depends_on_block_constraints |= fragment.base.flags.contains(
245 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
246 );
247
248 layout.push_line_item_to_unbreakable_segment(LineItem::BlockLevel(
249 layout.current_inline_box_identifier(),
250 fragment.clone(),
251 ));
252
253 layout.commit_current_segment_to_line();
254 layout.process_line_break(true);
255 layout.current_line.for_block_level = false;
256 }
257}
258
259#[derive(Clone, Debug, MallocSizeOf)]
260pub(crate) enum InlineItem {
261 StartInlineBox(ArcRefCell<InlineBox>),
262 EndInlineBox,
263 TextRun(ArcRefCell<TextRun>),
264 OutOfFlowAbsolutelyPositionedBox(
265 ArcRefCell<AbsolutelyPositionedBox>,
266 usize, ),
268 OutOfFlowFloatBox(ArcRefCell<FloatBox>),
269 Atomic(
270 ArcRefCell<IndependentFormattingContext>,
271 usize, Level, ),
274 BlockLevel(ArcRefCell<BlockLevelBox>),
275}
276
277impl InlineItem {
278 pub(crate) fn repair_style(
279 &self,
280 context: &SharedStyleContext,
281 node: &ServoLayoutNode,
282 new_style: &ServoArc<ComputedValues>,
283 ) {
284 match self {
285 InlineItem::StartInlineBox(inline_box) => {
286 inline_box
287 .borrow_mut()
288 .repair_style(context, node, new_style);
289 },
290 InlineItem::EndInlineBox => {},
291 InlineItem::TextRun(..) => {},
294 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box
295 .borrow_mut()
296 .context
297 .repair_style(context, node, new_style),
298 InlineItem::OutOfFlowFloatBox(float_box) => float_box
299 .borrow_mut()
300 .contents
301 .repair_style(context, node, new_style),
302 InlineItem::Atomic(atomic, ..) => {
303 atomic.borrow_mut().repair_style(context, node, new_style)
304 },
305 InlineItem::BlockLevel(block_level) => block_level
306 .borrow_mut()
307 .repair_style(context, node, new_style),
308 }
309 }
310
311 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
312 match self {
313 InlineItem::StartInlineBox(inline_box) => callback(&inline_box.borrow().base),
314 InlineItem::EndInlineBox | InlineItem::TextRun(..) => {
315 unreachable!("Should never have these kind of fragments attached to a DOM node")
316 },
317 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
318 callback(&positioned_box.borrow().context.base)
319 },
320 InlineItem::OutOfFlowFloatBox(float_box) => callback(&float_box.borrow().contents.base),
321 InlineItem::Atomic(independent_formatting_context, ..) => {
322 callback(&independent_formatting_context.borrow().base)
323 },
324 InlineItem::BlockLevel(block_level) => block_level.borrow().with_base(callback),
325 }
326 }
327
328 pub(crate) fn with_base_mut<T>(&self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
329 match self {
330 InlineItem::StartInlineBox(inline_box) => callback(&mut inline_box.borrow_mut().base),
331 InlineItem::EndInlineBox | InlineItem::TextRun(..) => {
332 unreachable!("Should never have these kind of fragments attached to a DOM node")
333 },
334 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
335 callback(&mut positioned_box.borrow_mut().context.base)
336 },
337 InlineItem::OutOfFlowFloatBox(float_box) => {
338 callback(&mut float_box.borrow_mut().contents.base)
339 },
340 InlineItem::Atomic(independent_formatting_context, ..) => {
341 callback(&mut independent_formatting_context.borrow_mut().base)
342 },
343 InlineItem::BlockLevel(block_level) => block_level.borrow_mut().with_base_mut(callback),
344 }
345 }
346
347 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
348 match self {
349 Self::StartInlineBox(_) | InlineItem::EndInlineBox => {
350 },
353 Self::TextRun(_) => {
354 },
356 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
357 positioned_box.borrow().context.attached_to_tree(layout_box)
358 },
359 Self::OutOfFlowFloatBox(float_box) => {
360 float_box.borrow().contents.attached_to_tree(layout_box)
361 },
362 Self::Atomic(atomic, ..) => atomic.borrow().attached_to_tree(layout_box),
363 Self::BlockLevel(block_level) => block_level.borrow().attached_to_tree(layout_box),
364 }
365 }
366
367 pub(crate) fn downgrade(&self) -> WeakInlineItem {
368 match self {
369 Self::StartInlineBox(inline_box) => {
370 WeakInlineItem::StartInlineBox(inline_box.downgrade())
371 },
372 Self::EndInlineBox => WeakInlineItem::EndInlineBox,
373 Self::TextRun(text_run) => WeakInlineItem::TextRun(text_run.downgrade()),
374 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
375 WeakInlineItem::OutOfFlowAbsolutelyPositionedBox(
376 positioned_box.downgrade(),
377 *offset_in_text,
378 )
379 },
380 Self::OutOfFlowFloatBox(float_box) => {
381 WeakInlineItem::OutOfFlowFloatBox(float_box.downgrade())
382 },
383 Self::Atomic(atomic, offset_in_text, bidi_level) => {
384 WeakInlineItem::Atomic(atomic.downgrade(), *offset_in_text, *bidi_level)
385 },
386 Self::BlockLevel(block_level) => WeakInlineItem::BlockLevel(block_level.downgrade()),
387 }
388 }
389}
390
391#[derive(Clone, Debug, MallocSizeOf)]
392pub(crate) enum WeakInlineItem {
393 StartInlineBox(WeakRefCell<InlineBox>),
394 EndInlineBox,
395 TextRun(WeakRefCell<TextRun>),
396 OutOfFlowAbsolutelyPositionedBox(
397 WeakRefCell<AbsolutelyPositionedBox>,
398 usize, ),
400 OutOfFlowFloatBox(WeakRefCell<FloatBox>),
401 Atomic(
402 WeakRefCell<IndependentFormattingContext>,
403 usize, Level, ),
406 BlockLevel(WeakRefCell<BlockLevelBox>),
407}
408
409impl WeakInlineItem {
410 pub(crate) fn upgrade(&self) -> Option<InlineItem> {
411 Some(match self {
412 Self::StartInlineBox(inline_box) => InlineItem::StartInlineBox(inline_box.upgrade()?),
413 Self::EndInlineBox => InlineItem::EndInlineBox,
414 Self::TextRun(text_run) => InlineItem::TextRun(text_run.upgrade()?),
415 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
416 InlineItem::OutOfFlowAbsolutelyPositionedBox(
417 positioned_box.upgrade()?,
418 *offset_in_text,
419 )
420 },
421 Self::OutOfFlowFloatBox(float_box) => {
422 InlineItem::OutOfFlowFloatBox(float_box.upgrade()?)
423 },
424 Self::Atomic(atomic, offset_in_text, bidi_level) => {
425 InlineItem::Atomic(atomic.upgrade()?, *offset_in_text, *bidi_level)
426 },
427 Self::BlockLevel(block_level) => InlineItem::BlockLevel(block_level.upgrade()?),
428 })
429 }
430}
431
432struct LineUnderConstruction {
439 start_position: LogicalVec2<Au>,
442
443 inline_position: Au,
446
447 max_block_size: LineBlockSizes,
451
452 has_content: bool,
455
456 has_inline_pbm: bool,
459
460 has_floats_waiting_to_be_placed: bool,
464
465 placement_among_floats: OnceCell<LogicalRect<Au>>,
470
471 line_items: Vec<LineItem>,
474
475 for_block_level: bool,
477
478 starting_character_offset: usize,
487}
488
489impl LineUnderConstruction {
490 fn new(start_position: LogicalVec2<Au>) -> Self {
491 Self {
492 inline_position: start_position.inline,
493 start_position,
494 max_block_size: LineBlockSizes::zero(),
495 has_content: false,
496 has_inline_pbm: false,
497 has_floats_waiting_to_be_placed: false,
498 placement_among_floats: OnceCell::new(),
499 line_items: Vec::new(),
500 for_block_level: false,
501 starting_character_offset: 0,
502 }
503 }
504
505 fn replace_placement_among_floats(&mut self, new_placement: LogicalRect<Au>) {
506 self.placement_among_floats.take();
507 let _ = self.placement_among_floats.set(new_placement);
508 }
509
510 fn trim_trailing_whitespace(&mut self) -> Au {
512 let mut whitespace_trimmed = Au::zero();
517 for item in self.line_items.iter_mut().rev() {
518 if !item.trim_whitespace_at_end(&mut whitespace_trimmed) {
519 break;
520 }
521 }
522
523 whitespace_trimmed
524 }
525
526 fn count_justification_opportunities(&self) -> usize {
528 self.line_items
529 .iter()
530 .filter_map(|item| match item {
531 LineItem::TextRun(_, text_run) => Some(
532 text_run
533 .text
534 .iter()
535 .map(|shaped_text_slice| shaped_text_slice.total_word_separators())
536 .sum::<usize>(),
537 ),
538 _ => None,
539 })
540 .sum()
541 }
542
543 fn is_phantom(&self) -> bool {
546 !self.has_content && !self.has_inline_pbm
548 }
549}
550
551#[derive(Clone, Debug)]
557struct BaselineRelativeSize {
558 ascent: Au,
562
563 descent: Au,
567}
568
569impl BaselineRelativeSize {
570 fn zero() -> Self {
571 Self {
572 ascent: Au::zero(),
573 descent: Au::zero(),
574 }
575 }
576
577 fn max(&self, other: &Self) -> Self {
578 BaselineRelativeSize {
579 ascent: self.ascent.max(other.ascent),
580 descent: self.descent.max(other.descent),
581 }
582 }
583
584 fn adjust_for_nested_baseline_offset(&mut self, baseline_offset: Au) {
598 self.ascent -= baseline_offset;
599 self.descent += baseline_offset;
600 }
601}
602
603#[derive(Clone, Debug)]
604struct LineBlockSizes {
605 line_height: Au,
606 baseline_relative_size_for_line_height: Option<BaselineRelativeSize>,
607 size_for_baseline_positioning: BaselineRelativeSize,
608}
609
610impl LineBlockSizes {
611 fn zero() -> Self {
612 LineBlockSizes {
613 line_height: Au::zero(),
614 baseline_relative_size_for_line_height: None,
615 size_for_baseline_positioning: BaselineRelativeSize::zero(),
616 }
617 }
618
619 fn resolve(&self) -> Au {
620 let height_from_ascent_and_descent = self
621 .baseline_relative_size_for_line_height
622 .as_ref()
623 .map(|size| (size.ascent + size.descent).abs())
624 .unwrap_or_else(Au::zero);
625 self.line_height.max(height_from_ascent_and_descent)
626 }
627
628 fn max(&self, other: &LineBlockSizes) -> LineBlockSizes {
629 let baseline_relative_size = match (
630 self.baseline_relative_size_for_line_height.as_ref(),
631 other.baseline_relative_size_for_line_height.as_ref(),
632 ) {
633 (Some(our_size), Some(other_size)) => Some(our_size.max(other_size)),
634 (our_size, other_size) => our_size.or(other_size).cloned(),
635 };
636 Self {
637 line_height: self.line_height.max(other.line_height),
638 baseline_relative_size_for_line_height: baseline_relative_size,
639 size_for_baseline_positioning: self
640 .size_for_baseline_positioning
641 .max(&other.size_for_baseline_positioning),
642 }
643 }
644
645 fn max_assign(&mut self, other: &LineBlockSizes) {
646 *self = self.max(other);
647 }
648
649 fn adjust_for_baseline_offset(&mut self, baseline_offset: Au) {
650 if let Some(size) = self.baseline_relative_size_for_line_height.as_mut() {
651 size.adjust_for_nested_baseline_offset(baseline_offset)
652 }
653 self.size_for_baseline_positioning
654 .adjust_for_nested_baseline_offset(baseline_offset);
655 }
656
657 fn find_baseline_offset(&self) -> Au {
664 match self.baseline_relative_size_for_line_height.as_ref() {
665 Some(size) => size.ascent,
666 None => {
667 let leading = self.resolve() -
670 (self.size_for_baseline_positioning.ascent +
671 self.size_for_baseline_positioning.descent);
672 leading.scale_by(0.5) + self.size_for_baseline_positioning.ascent
673 },
674 }
675 }
676}
677
678struct UnbreakableSegmentUnderConstruction {
682 inline_size: Au,
684
685 max_block_size: LineBlockSizes,
688
689 line_items: Vec<LineItem>,
691
692 inline_box_hierarchy_depth: Option<usize>,
695
696 has_content: bool,
700
701 has_inline_pbm: bool,
704
705 trailing_whitespace_size: Au,
707}
708
709impl UnbreakableSegmentUnderConstruction {
710 fn new() -> Self {
711 Self {
712 inline_size: Au::zero(),
713 max_block_size: LineBlockSizes {
714 line_height: Au::zero(),
715 baseline_relative_size_for_line_height: None,
716 size_for_baseline_positioning: BaselineRelativeSize::zero(),
717 },
718 line_items: Vec::new(),
719 inline_box_hierarchy_depth: None,
720 has_content: false,
721 has_inline_pbm: false,
722 trailing_whitespace_size: Au::zero(),
723 }
724 }
725
726 fn reset(&mut self) {
728 assert!(self.line_items.is_empty()); self.inline_size = Au::zero();
730 self.max_block_size = LineBlockSizes::zero();
731 self.inline_box_hierarchy_depth = None;
732 self.has_content = false;
733 self.has_inline_pbm = false;
734 self.trailing_whitespace_size = Au::zero();
735 }
736
737 fn push_line_item(&mut self, line_item: LineItem, inline_box_hierarchy_depth: usize) {
742 if self.line_items.is_empty() {
743 self.inline_box_hierarchy_depth = Some(inline_box_hierarchy_depth);
744 }
745 self.line_items.push(line_item);
746 }
747
748 fn trim_leading_whitespace(&mut self) {
759 let mut whitespace_trimmed = Au::zero();
760 for item in self.line_items.iter_mut() {
761 if !item.trim_whitespace_at_start(&mut whitespace_trimmed) {
762 break;
763 }
764 }
765 self.inline_size -= whitespace_trimmed;
766 }
767
768 fn is_phantom(&self) -> bool {
771 !self.has_content && !self.has_inline_pbm
773 }
774}
775
776bitflags! {
777 struct InlineContainerStateFlags: u8 {
778 const CREATE_STRUT = 0b0001;
779 const IS_SINGLE_LINE_TEXT_INPUT = 0b0010;
780 }
781}
782
783struct InlineContainerState {
784 style: ServoArc<ComputedValues>,
786
787 flags: InlineContainerStateFlags,
789
790 has_content: RefCell<bool>,
793
794 strut_block_sizes: LineBlockSizes,
799
800 nested_strut_block_sizes: LineBlockSizes,
804
805 pub baseline_offset: Au,
811
812 default_font: Option<FontRef>,
815
816 font_metrics: Arc<FontMetrics>,
818}
819
820struct InlineFormattingContextLayout<'layout_data> {
821 positioning_context: &'layout_data mut PositioningContext,
822 placement_state: PlacementState<'layout_data>,
823 sequential_layout_state: Option<&'layout_data mut SequentialLayoutState>,
824 layout_context: &'layout_data LayoutContext<'layout_data>,
825
826 ifc: &'layout_data InlineFormattingContext,
828
829 root_nesting_level: InlineContainerState,
839
840 inline_box_state_stack: Vec<Rc<InlineBoxContainerState>>,
844
845 inline_box_states: Vec<Rc<InlineBoxContainerState>>,
850
851 fragments: Vec<Fragment>,
855
856 current_line: LineUnderConstruction,
858
859 current_line_segment: UnbreakableSegmentUnderConstruction,
861
862 force_line_break_before_new_content: Option<usize>,
885
886 deferred_br_clear: Clear,
890
891 pub have_deferred_soft_wrap_opportunity: bool,
895
896 depends_on_block_constraints: bool,
899
900 white_space_collapse: WhiteSpaceCollapse,
905
906 text_wrap_mode: TextWrapMode,
911}
912
913impl InlineFormattingContextLayout<'_> {
914 fn current_inline_container_state(&self) -> &InlineContainerState {
915 match self.inline_box_state_stack.last() {
916 Some(inline_box_state) => &inline_box_state.base,
917 None => &self.root_nesting_level,
918 }
919 }
920
921 fn current_inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
922 self.inline_box_state_stack
923 .last()
924 .map(|state| state.identifier)
925 }
926
927 fn current_line_max_block_size_including_nested_containers(&self) -> LineBlockSizes {
928 self.current_inline_container_state()
929 .nested_strut_block_sizes
930 .max(&self.current_line.max_block_size)
931 }
932
933 fn current_line_block_start_considering_placement_among_floats(&self) -> Au {
934 self.current_line.placement_among_floats.get().map_or(
935 self.current_line.start_position.block,
936 |placement_among_floats| placement_among_floats.start_corner.block,
937 )
938 }
939
940 fn propagate_current_nesting_level_white_space_style(&mut self) {
941 let style = match self.inline_box_state_stack.last() {
942 Some(inline_box_state) => &inline_box_state.base.style,
943 None => self.placement_state.containing_block.style,
944 };
945 let style_text = style.get_inherited_text();
946 self.white_space_collapse = style_text.white_space_collapse;
947 self.text_wrap_mode = style_text.text_wrap_mode;
948 }
949
950 fn processing_br_element(&self) -> bool {
951 self.inline_box_state_stack.last().is_some_and(|state| {
952 state
953 .base_fragment_info
954 .flags
955 .contains(FragmentFlags::IS_BR_ELEMENT)
956 })
957 }
958
959 fn start_inline_box(&mut self, inline_box: &InlineBox) {
962 let containing_block = self.containing_block();
963 let inline_box_state = InlineBoxContainerState::new(
964 inline_box,
965 containing_block,
966 self.layout_context,
967 self.current_inline_container_state(),
968 inline_box.default_font.clone(),
969 );
970
971 self.depends_on_block_constraints |= inline_box
972 .base
973 .style
974 .depends_on_block_constraints_due_to_relative_positioning(
975 containing_block.style.writing_mode,
976 );
977
978 if inline_box_state
983 .base_fragment_info
984 .flags
985 .contains(FragmentFlags::IS_BR_ELEMENT) &&
986 self.deferred_br_clear == Clear::None
987 {
988 self.deferred_br_clear = Clear::from_style_and_container_writing_mode(
989 &inline_box_state.base.style,
990 self.containing_block().style.writing_mode,
991 );
992 }
993
994 let padding = inline_box_state.pbm.padding.inline_start;
995 let border = inline_box_state.pbm.border.inline_start;
996 let margin = inline_box_state.pbm.margin.inline_start.auto_is(Au::zero);
997 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
1000 self.current_line_segment.has_inline_pbm = true;
1001 }
1002 self.current_line_segment.inline_size += padding + border + margin;
1003 self.current_line_segment
1004 .line_items
1005 .push(LineItem::InlineStartBoxPaddingBorderMargin(
1006 inline_box.identifier,
1007 ));
1008
1009 let inline_box_state = Rc::new(inline_box_state);
1010
1011 assert_eq!(
1015 self.inline_box_states.len(),
1016 inline_box.identifier.index_in_inline_boxes as usize
1017 );
1018 self.inline_box_states.push(inline_box_state.clone());
1019 self.inline_box_state_stack.push(inline_box_state);
1020 }
1021
1022 fn finish_inline_box(&mut self) {
1025 let inline_box_state = match self.inline_box_state_stack.pop() {
1026 Some(inline_box_state) => inline_box_state,
1027 None => return, };
1029
1030 self.current_line_segment
1031 .max_block_size
1032 .max_assign(&inline_box_state.base.nested_strut_block_sizes);
1033
1034 if *inline_box_state.base.has_content.borrow() {
1039 self.propagate_current_nesting_level_white_space_style();
1040 }
1041
1042 let padding = inline_box_state.pbm.padding.inline_end;
1043 let border = inline_box_state.pbm.border.inline_end;
1044 let margin = inline_box_state.pbm.margin.inline_end.auto_is(Au::zero);
1045 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
1048 self.current_line_segment.has_inline_pbm = true;
1049 }
1050 self.current_line_segment.inline_size += padding + border + margin;
1051 self.current_line_segment
1052 .line_items
1053 .push(LineItem::InlineEndBoxPaddingBorderMargin(
1054 inline_box_state.identifier,
1055 ))
1056 }
1057
1058 fn finish_last_line(&mut self) {
1059 self.possibly_flush_deferred_forced_line_break();
1061
1062 self.process_soft_wrap_opportunity();
1068
1069 self.commit_current_segment_to_line();
1072
1073 self.finish_current_line_and_reset(true );
1076 }
1077
1078 fn finish_current_line_and_reset(&mut self, last_line_or_forced_line_break: bool) {
1082 self.possibly_push_empty_text_run_to_line_for_text_caret();
1083
1084 let whitespace_trimmed = self.current_line.trim_trailing_whitespace();
1085 let (inline_start_position, justification_adjustment) = self
1086 .calculate_current_line_inline_start_and_justification_adjustment(
1087 whitespace_trimmed,
1088 last_line_or_forced_line_break,
1089 );
1090
1091 let is_phantom_line = self.current_line.is_phantom();
1100 if !is_phantom_line {
1101 self.current_line.start_position.block += self.placement_state.current_margin.solve();
1102 self.placement_state.current_margin = CollapsedMargin::zero();
1103 }
1104 let block_start_position =
1105 self.current_line_block_start_considering_placement_among_floats();
1106
1107 let effective_block_advance = if is_phantom_line {
1108 LineBlockSizes::zero()
1109 } else {
1110 self.current_line_max_block_size_including_nested_containers()
1111 };
1112
1113 let resolved_block_advance = effective_block_advance.resolve();
1114 let block_end_position = if self.current_line.for_block_level {
1115 self.placement_state.current_block_direction_position
1116 } else {
1117 let mut block_end_position = block_start_position + resolved_block_advance;
1118 if let Some(sequential_layout_state) = self.sequential_layout_state.as_mut() {
1119 if !is_phantom_line {
1120 sequential_layout_state.commit_margin();
1121 }
1122
1123 let increment = block_end_position - self.current_line.start_position.block;
1126 sequential_layout_state.advance_block_position(increment);
1127
1128 if let Some(clearance) = sequential_layout_state
1132 .calculate_clearance(self.deferred_br_clear, &CollapsedMargin::zero())
1133 {
1134 sequential_layout_state.advance_block_position(clearance);
1135 block_end_position += clearance;
1136 };
1137 self.deferred_br_clear = Clear::None;
1138 }
1139 block_end_position
1140 };
1141
1142 let line_to_layout = std::mem::replace(
1144 &mut self.current_line,
1145 LineUnderConstruction::new(LogicalVec2 {
1146 inline: Au::zero(),
1147 block: block_end_position,
1148 }),
1149 );
1150 if !line_to_layout.for_block_level {
1151 self.placement_state.current_block_direction_position = block_end_position;
1152 }
1153
1154 if line_to_layout.has_floats_waiting_to_be_placed {
1155 place_pending_floats(self, &line_to_layout.line_items);
1156 }
1157
1158 let start_position = LogicalVec2 {
1159 block: block_start_position,
1160 inline: inline_start_position,
1161 };
1162
1163 let baseline_offset = effective_block_advance.find_baseline_offset();
1164 let start_positioning_context_length = self.positioning_context.len();
1165 let fragments = LineItemLayout::layout_line_items(
1166 self,
1167 line_to_layout.line_items,
1168 start_position,
1169 &effective_block_advance,
1170 justification_adjustment,
1171 is_phantom_line,
1172 );
1173
1174 if !is_phantom_line {
1175 let baseline = baseline_offset + block_start_position;
1176 self.placement_state
1177 .inflow_baselines
1178 .first
1179 .get_or_insert(baseline);
1180 self.placement_state.inflow_baselines.last = Some(baseline);
1181 self.placement_state
1182 .next_in_flow_margin_collapses_with_parent_start_margin = false;
1183 }
1184
1185 if fragments.is_empty() &&
1187 self.positioning_context.len() == start_positioning_context_length
1188 {
1189 return;
1190 }
1191
1192 let start_corner = LogicalVec2 {
1196 inline: Au::zero(),
1197 block: block_start_position,
1198 };
1199
1200 let logical_origin_in_physical_coordinates =
1201 start_corner.to_physical_vector(self.containing_block().style.writing_mode);
1202 self.positioning_context
1203 .adjust_static_position_of_hoisted_fragments_with_offset(
1204 &logical_origin_in_physical_coordinates,
1205 start_positioning_context_length,
1206 );
1207
1208 let containing_block = self.containing_block();
1209 let physical_line_rect = LogicalRect {
1210 start_corner,
1211 size: LogicalVec2 {
1212 inline: containing_block.size.inline,
1213 block: effective_block_advance.resolve(),
1214 },
1215 }
1216 .as_physical(Some(containing_block));
1217 self.fragments
1218 .push(Fragment::Positioning(PositioningFragment::new_anonymous(
1219 self.root_nesting_level.style.clone(),
1220 physical_line_rect,
1221 fragments,
1222 true, )));
1224 }
1225
1226 fn calculate_current_line_inline_start_and_justification_adjustment(
1231 &self,
1232 whitespace_trimmed: Au,
1233 last_line_or_forced_line_break: bool,
1234 ) -> (Au, Au) {
1235 enum TextAlign {
1236 Start,
1237 Center,
1238 End,
1239 }
1240 let containing_block = self.containing_block();
1241 let style = containing_block.style;
1242 let mut text_align_keyword = style.clone_text_align();
1243
1244 if last_line_or_forced_line_break {
1245 text_align_keyword = match style.clone_text_align_last() {
1246 TextAlignLast::Auto if text_align_keyword == TextAlignKeyword::Justify => {
1247 TextAlignKeyword::Start
1248 },
1249 TextAlignLast::Auto => text_align_keyword,
1250 TextAlignLast::Start => TextAlignKeyword::Start,
1251 TextAlignLast::End => TextAlignKeyword::End,
1252 TextAlignLast::Left => TextAlignKeyword::Left,
1253 TextAlignLast::Right => TextAlignKeyword::Right,
1254 TextAlignLast::Center => TextAlignKeyword::Center,
1255 TextAlignLast::Justify => TextAlignKeyword::Justify,
1256 };
1257 }
1258
1259 let text_align = match text_align_keyword {
1260 TextAlignKeyword::Start => TextAlign::Start,
1261 TextAlignKeyword::Center | TextAlignKeyword::MozCenter => TextAlign::Center,
1262 TextAlignKeyword::End => TextAlign::End,
1263 TextAlignKeyword::Left | TextAlignKeyword::MozLeft => {
1264 if style.writing_mode.line_left_is_inline_start() {
1265 TextAlign::Start
1266 } else {
1267 TextAlign::End
1268 }
1269 },
1270 TextAlignKeyword::Right | TextAlignKeyword::MozRight => {
1271 if style.writing_mode.line_left_is_inline_start() {
1272 TextAlign::End
1273 } else {
1274 TextAlign::Start
1275 }
1276 },
1277 TextAlignKeyword::Justify => TextAlign::Start,
1278 };
1279
1280 let (line_start, available_space) = match self.current_line.placement_among_floats.get() {
1281 Some(placement_among_floats) => (
1282 placement_among_floats.start_corner.inline,
1283 placement_among_floats.size.inline,
1284 ),
1285 None => (Au::zero(), containing_block.size.inline),
1286 };
1287
1288 let text_indent = self.current_line.start_position.inline;
1295 let line_length = self.current_line.inline_position - whitespace_trimmed - text_indent;
1296 let adjusted_line_start = line_start +
1297 match text_align {
1298 TextAlign::Start => text_indent,
1299 TextAlign::End => (available_space - line_length).max(text_indent),
1300 TextAlign::Center => (available_space - line_length + text_indent)
1301 .scale_by(0.5)
1302 .max(text_indent),
1303 };
1304
1305 let text_justify = containing_block.style.clone_text_justify();
1309 let justification_adjustment = match (text_align_keyword, text_justify) {
1310 (TextAlignKeyword::Justify, TextJustify::None) => Au::zero(),
1313 (TextAlignKeyword::Justify, _) => {
1314 match self.current_line.count_justification_opportunities() {
1315 0 => Au::zero(),
1316 num_justification_opportunities => {
1317 (available_space - text_indent - line_length)
1318 .scale_by(1. / num_justification_opportunities as f32)
1319 },
1320 }
1321 },
1322 _ => Au::zero(),
1323 };
1324
1325 let justification_adjustment = justification_adjustment.max(Au::zero());
1328
1329 (adjusted_line_start, justification_adjustment)
1330 }
1331
1332 fn place_float_fragment(&mut self, fragment: &BoxFragment) {
1333 let state = self
1334 .sequential_layout_state
1335 .as_mut()
1336 .expect("Tried to lay out a float with no sequential placement state!");
1337
1338 let block_offset_from_containining_block_top = state
1339 .current_block_position_including_margins() -
1340 state.current_containing_block_offset();
1341 state.place_float_fragment(
1342 fragment,
1343 self.placement_state.containing_block,
1344 CollapsedMargin::zero(),
1345 block_offset_from_containining_block_top,
1346 );
1347 }
1348
1349 fn place_float_line_item_for_commit_to_line(
1358 &mut self,
1359 float_item: &mut FloatLineItem,
1360 line_inline_size_without_trailing_whitespace: Au,
1361 ) {
1362 let containing_block = self.containing_block();
1363 let float_fragment = &float_item.fragment;
1364 let logical_margin_rect_size = float_fragment
1365 .margin_rect()
1366 .size
1367 .to_logical(containing_block.style.writing_mode);
1368 let inline_size = logical_margin_rect_size.inline.max(Au::zero());
1369
1370 let available_inline_size = match self.current_line.placement_among_floats.get() {
1371 Some(placement_among_floats) => placement_among_floats.size.inline,
1372 None => containing_block.size.inline,
1373 } - line_inline_size_without_trailing_whitespace;
1374
1375 let has_content = self.current_line.has_content || self.current_line_segment.has_content;
1381 let fits_on_line = !has_content || inline_size <= available_inline_size;
1382 let needs_placement_later =
1383 self.current_line.has_floats_waiting_to_be_placed || !fits_on_line;
1384
1385 if needs_placement_later {
1386 self.current_line.has_floats_waiting_to_be_placed = true;
1387 } else {
1388 self.place_float_fragment(float_fragment);
1389 float_item.needs_placement = false;
1390 }
1391
1392 let new_placement = self.place_line_among_floats(&LogicalVec2 {
1397 inline: line_inline_size_without_trailing_whitespace,
1398 block: self.current_line.max_block_size.resolve(),
1399 });
1400 self.current_line
1401 .replace_placement_among_floats(new_placement);
1402 }
1403
1404 fn place_line_among_floats(&self, potential_line_size: &LogicalVec2<Au>) -> LogicalRect<Au> {
1409 let sequential_layout_state = self
1410 .sequential_layout_state
1411 .as_ref()
1412 .expect("Should not have called this function without having floats.");
1413
1414 let ifc_offset_in_float_container = LogicalVec2 {
1415 inline: sequential_layout_state
1416 .floats
1417 .containing_block_info
1418 .inline_start,
1419 block: sequential_layout_state.current_containing_block_offset(),
1420 };
1421
1422 let ceiling = self.current_line_block_start_considering_placement_among_floats();
1423 let mut placement = PlacementAmongFloats::new(
1424 &sequential_layout_state.floats,
1425 ceiling + ifc_offset_in_float_container.block,
1426 LogicalVec2 {
1427 inline: potential_line_size.inline,
1428 block: potential_line_size.block,
1429 },
1430 &PaddingBorderMargin::zero(),
1431 );
1432
1433 let mut placement_rect = placement.place();
1434 placement_rect.start_corner -= ifc_offset_in_float_container;
1435 placement_rect
1436 }
1437
1438 fn new_potential_line_size_causes_line_break(
1445 &mut self,
1446 potential_line_size: &LogicalVec2<Au>,
1447 ) -> bool {
1448 let containing_block = self.containing_block();
1449 let available_line_space = if self.sequential_layout_state.is_some() {
1450 self.current_line
1451 .placement_among_floats
1452 .get_or_init(|| self.place_line_among_floats(potential_line_size))
1453 .size
1454 } else {
1455 LogicalVec2 {
1456 inline: containing_block.size.inline,
1457 block: MAX_AU,
1458 }
1459 };
1460
1461 let inline_would_overflow = potential_line_size.inline > available_line_space.inline;
1462 let block_would_overflow = potential_line_size.block > available_line_space.block;
1463
1464 let can_break = self.current_line.has_content;
1467
1468 if !can_break {
1474 if self.sequential_layout_state.is_some() &&
1477 (inline_would_overflow || block_would_overflow)
1478 {
1479 let new_placement = self.place_line_among_floats(potential_line_size);
1480 self.current_line
1481 .replace_placement_among_floats(new_placement);
1482 }
1483
1484 return false;
1485 }
1486
1487 if potential_line_size.inline > containing_block.size.inline {
1490 return true;
1491 }
1492
1493 if block_would_overflow {
1497 assert!(self.sequential_layout_state.is_some());
1499 let new_placement = self.place_line_among_floats(potential_line_size);
1500 if new_placement.start_corner.block !=
1501 self.current_line_block_start_considering_placement_among_floats()
1502 {
1503 return true;
1504 } else {
1505 self.current_line
1506 .replace_placement_among_floats(new_placement);
1507 return false;
1508 }
1509 }
1510
1511 inline_would_overflow
1515 }
1516
1517 fn defer_forced_line_break_at_character_offset(&mut self, line_break_offset: usize) {
1518 if !self.unbreakable_segment_fits_on_line() {
1521 self.process_line_break(false );
1522 }
1523
1524 self.force_line_break_before_new_content = Some(line_break_offset);
1526
1527 let line_is_empty =
1535 !self.current_line_segment.has_content && !self.current_line.has_content;
1536 if !self.processing_br_element() || line_is_empty {
1537 let strut_size = self
1538 .current_inline_container_state()
1539 .strut_block_sizes
1540 .clone();
1541 self.update_unbreakable_segment_for_new_content(
1542 &strut_size,
1543 Au::zero(),
1544 SegmentContentFlags::empty(),
1545 );
1546 }
1547 }
1548
1549 fn possibly_flush_deferred_forced_line_break(&mut self) {
1550 let Some(line_break_character_offset) = self.force_line_break_before_new_content.take()
1551 else {
1552 return;
1553 };
1554
1555 self.commit_current_segment_to_line();
1556 self.process_line_break(true );
1557
1558 self.current_line.starting_character_offset = line_break_character_offset + 1;
1559 }
1560
1561 fn push_line_item_to_unbreakable_segment(&mut self, line_item: LineItem) {
1562 self.current_line_segment
1563 .push_line_item(line_item, self.inline_box_state_stack.len());
1564 }
1565
1566 fn push_glyph_store_to_unbreakable_segment(
1567 &mut self,
1568 glyph_store: Arc<ShapedTextSlice>,
1569 text_run: &TextRun,
1570 info: &Arc<FontAndScriptInfo>,
1571 offsets: Option<TextRunOffsets>,
1572 ) {
1573 let inline_advance = glyph_store.total_advance();
1574 let flags = if glyph_store.is_whitespace() {
1575 SegmentContentFlags::from(text_run.inline_styles.style.borrow().get_inherited_text())
1576 } else {
1577 SegmentContentFlags::empty()
1578 };
1579
1580 let mut block_contribution = LineBlockSizes::zero();
1581 let quirks_mode = self.layout_context.style_context.quirks_mode() != QuirksMode::NoQuirks;
1582 let current_inline_container_state = self.current_inline_container_state();
1583 if quirks_mode && !flags.is_collapsible_whitespace() {
1584 block_contribution.max_assign(¤t_inline_container_state.strut_block_sizes);
1589 }
1590
1591 let font_metrics = &info.font.metrics;
1595 if current_inline_container_state
1596 .font_metrics
1597 .block_metrics_meaningfully_differ(font_metrics)
1598 {
1599 let baseline_shift = effective_baseline_shift(
1601 ¤t_inline_container_state.style,
1602 self.inline_box_state_stack.last().map(|c| &c.base),
1603 );
1604 let mut font_block_conribution = current_inline_container_state
1605 .get_block_size_contribution(
1606 baseline_shift,
1607 font_metrics,
1608 ¤t_inline_container_state.font_metrics,
1609 );
1610 font_block_conribution
1611 .adjust_for_baseline_offset(current_inline_container_state.baseline_offset);
1612 block_contribution.max_assign(&font_block_conribution);
1613 }
1614
1615 self.update_unbreakable_segment_for_new_content(&block_contribution, inline_advance, flags);
1616
1617 let current_inline_box_identifier = self.current_inline_box_identifier();
1618 if let Some(LineItem::TextRun(inline_box_identifier, line_item)) =
1619 self.current_line_segment.line_items.last_mut() &&
1620 *inline_box_identifier == current_inline_box_identifier &&
1621 line_item.merge_if_possible(info, &glyph_store, &offsets, &text_run.inline_styles)
1622 {
1623 return;
1624 }
1625
1626 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1627 current_inline_box_identifier,
1628 TextRunLineItem {
1629 text: vec![glyph_store],
1630 base_fragment_info: text_run.base_fragment_info,
1631 inline_styles: text_run.inline_styles.clone(),
1632 info: info.clone(),
1633 offsets: offsets.map(Box::new),
1634 is_empty_for_text_cursor: false,
1635 },
1636 ));
1637 }
1638
1639 fn possibly_push_empty_text_run_to_line_for_text_caret(&mut self) {
1642 let line_start_offset = self.current_line.starting_character_offset;
1643 let Some(shared_selection) = self.ifc.shared_selection.clone() else {
1644 return;
1645 };
1646 let offsets = TextRunOffsets {
1647 shared_selection,
1648 character_range: line_start_offset..line_start_offset + 1,
1649 };
1650
1651 if self
1653 .current_line
1654 .line_items
1655 .iter()
1656 .rev()
1657 .find(|line_item| line_item.is_in_flow_content())
1658 .is_some_and(|line_item| matches!(line_item, LineItem::TextRun(..)))
1659 {
1660 return;
1661 }
1662
1663 let inline_container_state = self.current_inline_container_state();
1664 let Some(font) = inline_container_state.default_font.clone() else {
1665 return;
1666 };
1667
1668 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1669 self.current_inline_box_identifier(),
1670 TextRunLineItem {
1671 text: Default::default(),
1672 base_fragment_info: BaseFragmentInfo::anonymous(),
1673 inline_styles: self.ifc.shared_inline_styles.clone(),
1674 info: Arc::new(FontAndScriptInfo::simple_for_font(font)),
1675 offsets: Some(Box::new(offsets)),
1676 is_empty_for_text_cursor: true,
1677 },
1678 ));
1679 self.current_line_segment.has_content = true;
1680 self.commit_current_segment_to_line();
1681 }
1682
1683 fn update_unbreakable_segment_for_new_content(
1684 &mut self,
1685 block_sizes_of_content: &LineBlockSizes,
1686 inline_size: Au,
1687 flags: SegmentContentFlags,
1688 ) {
1689 if flags.is_collapsible_whitespace() || flags.is_wrappable_and_hangable() {
1690 self.current_line_segment.trailing_whitespace_size = inline_size;
1691 } else {
1692 self.current_line_segment.trailing_whitespace_size = Au::zero();
1693 }
1694 if !flags.is_collapsible_whitespace() {
1695 self.current_line_segment.has_content = true;
1696 }
1697
1698 let container_max_block_size = &self
1700 .current_inline_container_state()
1701 .nested_strut_block_sizes
1702 .clone();
1703 self.current_line_segment
1704 .max_block_size
1705 .max_assign(container_max_block_size);
1706 self.current_line_segment
1707 .max_block_size
1708 .max_assign(block_sizes_of_content);
1709
1710 self.current_line_segment.inline_size += inline_size;
1711
1712 *self
1714 .current_inline_container_state()
1715 .has_content
1716 .borrow_mut() = true;
1717 self.propagate_current_nesting_level_white_space_style();
1718 }
1719
1720 fn process_line_break(&mut self, forced_line_break: bool) {
1721 self.current_line_segment.trim_leading_whitespace();
1722 self.finish_current_line_and_reset(forced_line_break);
1723 }
1724
1725 fn potential_line_size(&self) -> LogicalVec2<Au> {
1726 LogicalVec2 {
1727 inline: self.current_line.inline_position + self.current_line_segment.inline_size,
1728 block: self
1729 .current_line_max_block_size_including_nested_containers()
1730 .max(&self.current_line_segment.max_block_size)
1731 .resolve(),
1732 }
1733 }
1734
1735 fn unbreakable_segment_fits_on_line(&mut self) -> bool {
1736 let potential_line_size_without_hanging_whitespace = self.potential_line_size() -
1737 LogicalVec2 {
1738 inline: self.current_line_segment.trailing_whitespace_size,
1739 block: Au::zero(),
1740 };
1741 !self.new_potential_line_size_causes_line_break(
1742 &potential_line_size_without_hanging_whitespace,
1743 )
1744 }
1745
1746 fn process_soft_wrap_opportunity(&mut self) {
1750 if self.current_line_segment.line_items.is_empty() {
1751 return;
1752 }
1753 if self.text_wrap_mode == TextWrapMode::Nowrap {
1754 return;
1755 }
1756 if !self.unbreakable_segment_fits_on_line() {
1757 self.process_line_break(false );
1758 }
1759 self.commit_current_segment_to_line();
1760 }
1761
1762 fn commit_current_segment_to_line(&mut self) {
1765 if self.current_line_segment.line_items.is_empty() && !self.current_line_segment.has_content
1768 {
1769 return;
1770 }
1771
1772 if !self.current_line.has_content {
1773 self.current_line_segment.trim_leading_whitespace();
1774 }
1775
1776 self.current_line.inline_position += self.current_line_segment.inline_size;
1777 self.current_line.max_block_size = self
1778 .current_line_max_block_size_including_nested_containers()
1779 .max(&self.current_line_segment.max_block_size);
1780 let line_inline_size_without_trailing_whitespace =
1781 self.current_line.inline_position - self.current_line_segment.trailing_whitespace_size;
1782
1783 let mut segment_items = mem::take(&mut self.current_line_segment.line_items);
1785 for item in segment_items.iter_mut() {
1786 if let LineItem::Float(_, float_item) = item {
1787 self.place_float_line_item_for_commit_to_line(
1788 float_item,
1789 line_inline_size_without_trailing_whitespace,
1790 );
1791 }
1792 }
1793
1794 if self.current_line.line_items.is_empty() {
1799 let will_break = self.new_potential_line_size_causes_line_break(&LogicalVec2 {
1800 inline: line_inline_size_without_trailing_whitespace,
1801 block: self.current_line_segment.max_block_size.resolve(),
1802 });
1803 assert!(!will_break);
1804 }
1805
1806 self.current_line.line_items.extend(segment_items);
1807 self.current_line.has_content |= self.current_line_segment.has_content;
1808 self.current_line.has_inline_pbm |= self.current_line_segment.has_inline_pbm;
1809
1810 self.current_line_segment.reset();
1811 }
1812
1813 #[inline]
1814 fn containing_block(&self) -> &ContainingBlock<'_> {
1815 self.placement_state.containing_block
1816 }
1817}
1818
1819bitflags! {
1820 struct SegmentContentFlags: u8 {
1821 const COLLAPSIBLE_WHITESPACE = 0b00000001;
1822 const WRAPPABLE_AND_HANGABLE_WHITESPACE = 0b00000010;
1823 }
1824}
1825
1826impl SegmentContentFlags {
1827 fn is_collapsible_whitespace(&self) -> bool {
1828 self.contains(Self::COLLAPSIBLE_WHITESPACE)
1829 }
1830
1831 fn is_wrappable_and_hangable(&self) -> bool {
1832 self.contains(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE)
1833 }
1834}
1835
1836impl From<&InheritedText> for SegmentContentFlags {
1837 fn from(style_text: &InheritedText) -> Self {
1838 let mut flags = Self::empty();
1839
1840 if !matches!(
1843 style_text.white_space_collapse,
1844 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
1845 ) {
1846 flags.insert(Self::COLLAPSIBLE_WHITESPACE);
1847 }
1848
1849 if style_text.text_wrap_mode == TextWrapMode::Wrap &&
1852 style_text.white_space_collapse != WhiteSpaceCollapse::BreakSpaces
1853 {
1854 flags.insert(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE);
1855 }
1856 flags
1857 }
1858}
1859
1860impl InlineFormattingContext {
1861 #[servo_tracing::instrument(name = "InlineFormattingContext::new_with_builder", skip_all)]
1862 fn new_with_builder(
1863 mut builder: InlineFormattingContextBuilder,
1864 layout_context: &LayoutContext,
1865 has_first_formatted_line: bool,
1866 is_single_line_text_input: bool,
1867 starting_bidi_level: Level,
1868 ) -> Self {
1869 let text_content: String = builder.text_segments.into_iter().collect();
1871
1872 let bidi_levels = BidiLevels {
1873 info: builder
1874 .has_right_to_left_content
1875 .then(|| BidiInfo::new(&text_content, Some(starting_bidi_level))),
1876 };
1877
1878 let shared_inline_styles = builder
1879 .shared_inline_styles_stack
1880 .last()
1881 .expect("Should have at least one SharedInlineStyle for the root of an IFC")
1882 .clone();
1883 let (word_break, line_break, lang) = {
1884 let styles = shared_inline_styles.style.borrow();
1885 let text_style = styles.get_inherited_text();
1886 (
1887 text_style.word_break,
1888 text_style.line_break,
1889 styles.get_font()._x_lang.clone(),
1890 )
1891 };
1892
1893 let mut options = LineBreakOptions::default();
1894
1895 options.strictness = match line_break {
1896 LineBreak::Loose => LineBreakStrictness::Loose,
1897 LineBreak::Normal => LineBreakStrictness::Normal,
1898 LineBreak::Strict => LineBreakStrictness::Strict,
1899 LineBreak::Anywhere => LineBreakStrictness::Anywhere,
1900 LineBreak::Auto => LineBreakStrictness::Normal,
1903 };
1904 options.word_option = match word_break {
1905 WordBreak::Normal => LineBreakWordOption::Normal,
1906 WordBreak::BreakAll => LineBreakWordOption::BreakAll,
1907 WordBreak::KeepAll => LineBreakWordOption::KeepAll,
1908 };
1909 options.ja_zh = {
1912 lang.0.parse::<LanguageIdentifier>().is_ok_and(|lang_id| {
1913 const JA: Language = language!("ja");
1914 const ZH: Language = language!("zh");
1915 matches!(lang_id.language, JA | ZH)
1916 })
1917 };
1918
1919 let mut new_linebreaker = LineBreaker::new(text_content.as_str(), options);
1920 for item in &mut builder.inline_items {
1921 match item {
1922 InlineItem::TextRun(text_run) => {
1923 text_run.borrow_mut().segment_and_shape(
1924 &text_content,
1925 layout_context,
1926 &mut new_linebreaker,
1927 &bidi_levels,
1928 );
1929 },
1930 InlineItem::StartInlineBox(inline_box) => {
1931 let inline_box = &mut *inline_box.borrow_mut();
1932 if let Some(font) = get_font_for_first_font_for_style(
1933 &inline_box.base.style,
1934 &layout_context.font_context,
1935 ) {
1936 inline_box.default_font = Some(font);
1937 }
1938 },
1939 InlineItem::Atomic(_, index_in_text, bidi_level) => {
1940 *bidi_level = bidi_levels.level(*index_in_text);
1941 },
1942 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) |
1943 InlineItem::OutOfFlowFloatBox(_) |
1944 InlineItem::EndInlineBox |
1945 InlineItem::BlockLevel { .. } => {},
1946 }
1947 }
1948
1949 let default_font = get_font_for_first_font_for_style(
1950 &shared_inline_styles.style.borrow(),
1951 &layout_context.font_context,
1952 );
1953
1954 let has_right_to_left_content = bidi_levels.info.as_ref().is_some_and(BidiInfo::has_rtl);
1955 InlineFormattingContext {
1956 text_content,
1957 inline_items: builder.inline_items,
1958 inline_boxes: builder.inline_boxes,
1959 shared_inline_styles,
1960 default_font,
1961 has_first_formatted_line,
1962 contains_floats: builder.contains_floats,
1963 is_single_line_text_input,
1964 has_right_to_left_content,
1965 shared_selection: builder.shared_selection,
1966 tab_size_multiplier: Default::default(),
1967 }
1968 }
1969
1970 pub(crate) fn repair_style(
1971 &self,
1972 context: &SharedStyleContext,
1973 node: &ServoLayoutNode,
1974 new_style: &ServoArc<ComputedValues>,
1975 ) {
1976 *self.shared_inline_styles.style.borrow_mut() = new_style.clone();
1977 *self.shared_inline_styles.selected.borrow_mut() = node.selected_style(context);
1978 }
1979
1980 fn inline_start_for_first_line(&self, containing_block: IndefiniteContainingBlock) -> Au {
1981 if !self.has_first_formatted_line {
1982 return Au::zero();
1983 }
1984 containing_block
1985 .style
1986 .get_inherited_text()
1987 .text_indent
1988 .length
1989 .to_used_value(containing_block.size.inline.unwrap_or_default())
1990 }
1991
1992 pub(super) fn layout(
1993 &self,
1994 layout_context: &LayoutContext,
1995 positioning_context: &mut PositioningContext,
1996 containing_block: &ContainingBlock,
1997 sequential_layout_state: Option<&mut SequentialLayoutState>,
1998 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
1999 ) -> IndependentFormattingContextLayoutResult {
2000 for inline_box in self.inline_boxes.iter() {
2002 inline_box.borrow().base.clear_fragments();
2003 }
2004
2005 let style = containing_block.style;
2006
2007 let style_text = containing_block.style.get_inherited_text();
2008 let mut inline_container_state_flags = InlineContainerStateFlags::empty();
2009 if inline_container_needs_strut(style, layout_context, None) {
2010 inline_container_state_flags.insert(InlineContainerStateFlags::CREATE_STRUT);
2011 }
2012 if self.is_single_line_text_input {
2013 inline_container_state_flags
2014 .insert(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT);
2015 }
2016 let placement_state =
2017 PlacementState::new(collapsible_with_parent_start_margin, containing_block);
2018
2019 let mut layout = InlineFormattingContextLayout {
2020 positioning_context,
2021 placement_state,
2022 sequential_layout_state,
2023 layout_context,
2024 ifc: self,
2025 fragments: Vec::new(),
2026 current_line: LineUnderConstruction::new(LogicalVec2 {
2027 inline: self.inline_start_for_first_line(containing_block.into()),
2028 block: Au::zero(),
2029 }),
2030 root_nesting_level: InlineContainerState::new(
2031 style.to_arc(),
2032 inline_container_state_flags,
2033 None, self.default_font.clone(),
2035 ),
2036 inline_box_state_stack: Vec::new(),
2037 inline_box_states: Vec::with_capacity(self.inline_boxes.len()),
2038 current_line_segment: UnbreakableSegmentUnderConstruction::new(),
2039 force_line_break_before_new_content: None,
2040 deferred_br_clear: Clear::None,
2041 have_deferred_soft_wrap_opportunity: false,
2042 depends_on_block_constraints: false,
2043 white_space_collapse: style_text.white_space_collapse,
2044 text_wrap_mode: style_text.text_wrap_mode,
2045 };
2046
2047 for item in self.inline_items.iter() {
2048 if !matches!(item, InlineItem::EndInlineBox) {
2050 layout.possibly_flush_deferred_forced_line_break();
2051 }
2052
2053 match item {
2054 InlineItem::StartInlineBox(inline_box) => {
2055 layout.start_inline_box(&inline_box.borrow());
2056 },
2057 InlineItem::EndInlineBox => layout.finish_inline_box(),
2058 InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout),
2059 InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
2060 atomic_formatting_context.borrow().layout_into_line_items(
2061 &mut layout,
2062 *offset_in_text,
2063 *bidi_level,
2064 );
2065 },
2066 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, _) => {
2067 layout.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
2068 layout.current_inline_box_identifier(),
2069 AbsolutelyPositionedLineItem {
2070 absolutely_positioned_box: positioned_box.clone(),
2071 preceding_line_content_would_produce_phantom_line: layout
2072 .current_line
2073 .is_phantom() &&
2074 layout.current_line_segment.is_phantom(),
2075 },
2076 ));
2077 },
2078 InlineItem::OutOfFlowFloatBox(float_box) => {
2079 float_box.borrow().layout_into_line_items(&mut layout);
2080 },
2081 InlineItem::BlockLevel(block_level) => {
2082 block_level.borrow().layout_into_line_items(&mut layout);
2083 },
2084 }
2085 }
2086
2087 layout.finish_last_line();
2088 let (content_block_size, collapsible_margins_in_children, baselines) =
2089 layout.placement_state.finish();
2090
2091 IndependentFormattingContextLayoutResult {
2092 fragments: layout.fragments,
2093 content_block_size,
2094 collapsible_margins_in_children,
2095 baselines,
2096 depends_on_block_constraints: layout.depends_on_block_constraints,
2097 content_inline_size_for_table: None,
2098 specific_layout_info: None,
2099 }
2100 }
2101
2102 pub(crate) fn subtree_size(&self) -> usize {
2103 self.inline_items
2104 .iter()
2105 .map(|item| match item {
2106 InlineItem::StartInlineBox(..) => 1,
2107 InlineItem::EndInlineBox => 0,
2108 InlineItem::TextRun(..) => 1,
2109 InlineItem::OutOfFlowAbsolutelyPositionedBox(absolutely_positioned_box, _) => {
2110 absolutely_positioned_box
2111 .borrow()
2112 .context
2113 .base
2114 .subtree_size()
2115 },
2116 InlineItem::OutOfFlowFloatBox(..) => 1,
2117 InlineItem::Atomic(..) => 1,
2118 InlineItem::BlockLevel(block_level_box) => block_level_box.borrow().subtree_size(),
2119 })
2120 .sum()
2121 }
2122
2123 fn next_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
2124 let Some(character) = self.text_content[index..].chars().nth(1) else {
2125 return false;
2126 };
2127 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2128 }
2129
2130 fn previous_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
2131 let Some(character) = self.text_content[0..index].chars().next_back() else {
2132 return false;
2133 };
2134 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2135 }
2136
2137 pub(crate) fn find_block_margin_collapsing_with_parent(
2138 &self,
2139 layout_context: &LayoutContext,
2140 collected_margin: &mut CollapsedMargin,
2141 containing_block_for_children: &ContainingBlock,
2142 ) -> bool {
2143 let mut nesting_levels_from_nonzero_end_pbm: u32 = 1;
2149 let mut items_iter = self.inline_items.iter();
2150 items_iter.all(|inline_item| match inline_item {
2151 InlineItem::StartInlineBox(inline_box) => {
2152 let pbm = inline_box
2153 .borrow()
2154 .layout_style()
2155 .padding_border_margin(containing_block_for_children);
2156 if pbm.padding.inline_end.is_zero() &&
2157 pbm.border.inline_end.is_zero() &&
2158 pbm.margin.inline_end.auto_is(Au::zero).is_zero()
2159 {
2160 nesting_levels_from_nonzero_end_pbm += 1;
2161 } else {
2162 nesting_levels_from_nonzero_end_pbm = 0;
2163 }
2164 pbm.padding.inline_start.is_zero() &&
2165 pbm.border.inline_start.is_zero() &&
2166 pbm.margin.inline_start.auto_is(Au::zero).is_zero()
2167 },
2168 InlineItem::EndInlineBox => {
2169 if nesting_levels_from_nonzero_end_pbm == 0 {
2170 false
2171 } else {
2172 nesting_levels_from_nonzero_end_pbm -= 1;
2173 true
2174 }
2175 },
2176 InlineItem::TextRun(text_run) => {
2177 let text_run = &*text_run.borrow();
2178 let parent_style = text_run.inline_styles.style.borrow();
2179 text_run.items.iter().all(|item| match item {
2180 TextRunItem::LineBreak { .. } => false,
2181 TextRunItem::Tab { .. } => false,
2182 TextRunItem::TextSegment(segment) => segment.runs.iter().all(|run| {
2183 run.is_whitespace() &&
2184 !matches!(
2185 parent_style.get_inherited_text().white_space_collapse,
2186 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
2187 )
2188 }),
2189 })
2190 },
2191 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => true,
2192 InlineItem::OutOfFlowFloatBox(..) => true,
2193 InlineItem::Atomic(..) => false,
2194 InlineItem::BlockLevel(block_level) => block_level
2195 .borrow()
2196 .find_block_margin_collapsing_with_parent(
2197 layout_context,
2198 collected_margin,
2199 containing_block_for_children,
2200 ),
2201 })
2202 }
2203
2204 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
2205 let mut parent_box_stack = Vec::new();
2206 let current_parent_box = |parent_box_stack: &[WeakLayoutBox]| {
2207 parent_box_stack.last().unwrap_or(&layout_box).clone()
2208 };
2209 for inline_item in &self.inline_items {
2210 match inline_item {
2211 InlineItem::StartInlineBox(inline_box) => {
2212 inline_box
2213 .borrow_mut()
2214 .base
2215 .parent_box
2216 .replace(current_parent_box(&parent_box_stack));
2217 parent_box_stack.push(WeakLayoutBox::InlineLevel(
2218 WeakInlineItem::StartInlineBox(inline_box.downgrade()),
2219 ));
2220 },
2221 InlineItem::EndInlineBox => {
2222 parent_box_stack.pop();
2223 },
2224 InlineItem::TextRun(text_run) => {
2225 text_run
2226 .borrow_mut()
2227 .parent_box
2228 .replace(current_parent_box(&parent_box_stack));
2229 },
2230 _ => inline_item.with_base_mut(|base| {
2231 base.parent_box
2232 .replace(current_parent_box(&parent_box_stack));
2233 }),
2234 }
2235 }
2236 }
2237
2238 pub(crate) fn next_tab_stop_after_inline_advance(
2239 &self,
2240 style: &ServoArc<ComputedValues>,
2241 current_inline_advance: Au,
2242 ) -> Au {
2243 let Some(font) = self.default_font.as_ref() else {
2244 return Au::zero();
2245 };
2246
2247 let tab_size_multiplier = *self.tab_size_multiplier.get_or_init(|| {
2248 let root_style = self.shared_inline_styles.style.borrow();
2249 let inherited_text_style = root_style.get_inherited_text();
2250 let font_size = root_style.get_font().font_size.computed_size().into();
2251 let letter_spacing = inherited_text_style
2252 .letter_spacing
2253 .0
2254 .to_used_value(font_size);
2255 let word_spacing = inherited_text_style.word_spacing.to_used_value(font_size);
2256
2257 font.metrics.space_advance + word_spacing + letter_spacing
2260 });
2261
2262 let tab_stop_advance = match style.get_inherited_text().tab_size {
2263 style::values::generics::length::LengthOrNumber::Number(number_of_spaces) => {
2264 tab_size_multiplier.scale_by(number_of_spaces.0)
2265 },
2266 style::values::generics::length::LengthOrNumber::Length(length) => length.into(),
2268 };
2269
2270 if tab_stop_advance.is_zero() {
2271 return Au::zero();
2272 }
2273
2274 let half_ch_advance = font
2280 .metrics
2281 .zero_horizontal_advance
2282 .unwrap_or(font.metrics.em_size.scale_by(0.5))
2283 .scale_by(0.5);
2284 let number_of_tab_stops =
2285 (current_inline_advance + half_ch_advance).to_f32_px() / tab_stop_advance.to_f32_px();
2286 let number_of_tab_stops = number_of_tab_stops.ceil();
2287 tab_stop_advance.scale_by(number_of_tab_stops) - current_inline_advance
2288 }
2289}
2290
2291impl InlineContainerState {
2292 fn new(
2293 style: ServoArc<ComputedValues>,
2294 flags: InlineContainerStateFlags,
2295 parent_container: Option<&InlineContainerState>,
2296 default_font: Option<FontRef>,
2297 ) -> Self {
2298 let font_metrics = default_font
2299 .as_ref()
2300 .map(|font| font.metrics.clone())
2301 .unwrap_or_else(FontMetrics::empty);
2302 let mut baseline_offset = Au::zero();
2303 let mut strut_block_sizes = {
2304 Self::get_block_sizes_with_style(
2305 effective_baseline_shift(&style, parent_container),
2306 &style,
2307 &font_metrics,
2308 &font_metrics,
2309 &flags,
2310 )
2311 };
2312
2313 if let Some(parent_container) = parent_container {
2314 baseline_offset = parent_container.get_cumulative_baseline_offset_for_child(
2317 style.clone_alignment_baseline(),
2318 style.clone_baseline_shift(),
2319 &strut_block_sizes,
2320 );
2321 strut_block_sizes.adjust_for_baseline_offset(baseline_offset);
2322 }
2323
2324 let mut nested_block_sizes = parent_container
2325 .map(|container| container.nested_strut_block_sizes.clone())
2326 .unwrap_or_else(LineBlockSizes::zero);
2327 if flags.contains(InlineContainerStateFlags::CREATE_STRUT) {
2328 nested_block_sizes.max_assign(&strut_block_sizes);
2329 }
2330
2331 Self {
2332 style,
2333 flags,
2334 has_content: RefCell::new(false),
2335 nested_strut_block_sizes: nested_block_sizes,
2336 strut_block_sizes,
2337 baseline_offset,
2338 default_font,
2339 font_metrics,
2340 }
2341 }
2342
2343 fn get_block_sizes_with_style(
2344 baseline_shift: BaselineShift,
2345 style: &ComputedValues,
2346 font_metrics: &FontMetrics,
2347 font_metrics_of_first_font: &FontMetrics,
2348 flags: &InlineContainerStateFlags,
2349 ) -> LineBlockSizes {
2350 let line_height = line_height(style, font_metrics, flags);
2351
2352 if !is_baseline_relative(baseline_shift) {
2353 return LineBlockSizes {
2354 line_height,
2355 baseline_relative_size_for_line_height: None,
2356 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2357 };
2358 }
2359
2360 let mut ascent = font_metrics.ascent;
2369 let mut descent = font_metrics.descent;
2370 if style.get_font().line_height == LineHeight::Normal {
2371 let half_leading_from_line_gap =
2372 (font_metrics.line_gap - descent - ascent).scale_by(0.5);
2373 ascent += half_leading_from_line_gap;
2374 descent += half_leading_from_line_gap;
2375 }
2376
2377 let size_for_baseline_positioning = BaselineRelativeSize { ascent, descent };
2381
2382 if style.get_font().line_height != LineHeight::Normal {
2398 ascent = font_metrics_of_first_font.ascent;
2399 descent = font_metrics_of_first_font.descent;
2400 let half_leading = (line_height - (ascent + descent)).scale_by(0.5);
2401 ascent += half_leading;
2406 descent = line_height - ascent;
2407 }
2408
2409 LineBlockSizes {
2410 line_height,
2411 baseline_relative_size_for_line_height: Some(BaselineRelativeSize { ascent, descent }),
2412 size_for_baseline_positioning,
2413 }
2414 }
2415
2416 fn get_block_size_contribution(
2417 &self,
2418 baseline_shift: BaselineShift,
2419 font_metrics: &FontMetrics,
2420 font_metrics_of_first_font: &FontMetrics,
2421 ) -> LineBlockSizes {
2422 Self::get_block_sizes_with_style(
2423 baseline_shift,
2424 &self.style,
2425 font_metrics,
2426 font_metrics_of_first_font,
2427 &self.flags,
2428 )
2429 }
2430
2431 fn get_cumulative_baseline_offset_for_child(
2432 &self,
2433 child_alignment_baseline: AlignmentBaseline,
2434 child_baseline_shift: BaselineShift,
2435 child_block_size: &LineBlockSizes,
2436 ) -> Au {
2437 let block_size = self.get_block_size_contribution(
2438 child_baseline_shift.clone(),
2439 &self.font_metrics,
2440 &self.font_metrics,
2441 );
2442 self.baseline_offset +
2443 match child_alignment_baseline {
2444 AlignmentBaseline::Baseline => Au::zero(),
2445 AlignmentBaseline::TextTop => {
2446 child_block_size.size_for_baseline_positioning.ascent - self.font_metrics.ascent
2447 },
2448 AlignmentBaseline::Middle => {
2449 (child_block_size.size_for_baseline_positioning.ascent -
2452 child_block_size.size_for_baseline_positioning.descent -
2453 self.font_metrics.x_height)
2454 .scale_by(0.5)
2455 },
2456 AlignmentBaseline::TextBottom => {
2457 self.font_metrics.descent -
2458 child_block_size.size_for_baseline_positioning.descent
2459 },
2460 } +
2461 match child_baseline_shift {
2462 BaselineShift::Keyword(
2467 BaselineShiftKeyword::Top |
2468 BaselineShiftKeyword::Bottom |
2469 BaselineShiftKeyword::Center,
2470 ) => Au::zero(),
2471 BaselineShift::Keyword(BaselineShiftKeyword::Sub) => {
2472 block_size.resolve().scale_by(FONT_SUBSCRIPT_OFFSET_RATIO)
2473 },
2474 BaselineShift::Keyword(BaselineShiftKeyword::Super) => {
2475 -block_size.resolve().scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO)
2476 },
2477 BaselineShift::Length(length_percentage) => {
2478 -length_percentage.to_used_value(child_block_size.line_height)
2479 },
2480 }
2481 }
2482}
2483
2484impl IndependentFormattingContext {
2485 fn layout_into_line_items(
2486 &self,
2487 layout: &mut InlineFormattingContextLayout,
2488 offset_in_text: usize,
2489 bidi_level: Level,
2490 ) {
2491 let mut child_positioning_context = PositioningContext::default();
2493 let IndependentFloatOrAtomicLayoutResult {
2494 mut fragment,
2495 baselines,
2496 pbm_sums,
2497 } = self.layout_float_or_atomic_inline(
2498 layout.layout_context,
2499 &mut child_positioning_context,
2500 layout.containing_block(),
2501 );
2502
2503 layout.depends_on_block_constraints |= fragment.base.flags.contains(
2506 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2507 );
2508
2509 let container_writing_mode = layout.containing_block().style.writing_mode;
2511 let pbm_physical_offset = pbm_sums
2512 .start_offset()
2513 .to_physical_size(container_writing_mode);
2514 fragment.base.translate_rect(pbm_physical_offset);
2515
2516 fragment = fragment.with_baselines(baselines);
2518
2519 let positioning_context = if self.is_replaced() {
2522 None
2523 } else {
2524 if fragment
2525 .style()
2526 .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
2527 {
2528 child_positioning_context
2529 .layout_collected_children(layout.layout_context, &mut fragment);
2530 }
2531 Some(child_positioning_context)
2532 };
2533
2534 if layout.text_wrap_mode == TextWrapMode::Wrap &&
2535 !layout
2536 .ifc
2537 .previous_character_prevents_soft_wrap_opportunity(offset_in_text)
2538 {
2539 layout.process_soft_wrap_opportunity();
2540 }
2541
2542 let size = pbm_sums.sum() + fragment.base.rect().size.to_logical(container_writing_mode);
2543 let baseline_offset = self
2544 .pick_baseline(&fragment.baselines(container_writing_mode))
2545 .map(|baseline| pbm_sums.block_start + baseline)
2546 .unwrap_or(size.block);
2547
2548 let (block_sizes, baseline_offset_in_parent) =
2549 self.get_block_sizes_and_baseline_offset(layout, size.block, baseline_offset);
2550 layout.update_unbreakable_segment_for_new_content(
2551 &block_sizes,
2552 size.inline,
2553 SegmentContentFlags::empty(),
2554 );
2555
2556 let fragment = Arc::new(fragment);
2557 self.base.set_fragment(Fragment::Box(fragment.clone()));
2558
2559 layout.push_line_item_to_unbreakable_segment(LineItem::Atomic(
2560 layout.current_inline_box_identifier(),
2561 AtomicLineItem {
2562 fragment,
2563 size,
2564 positioning_context,
2565 baseline_offset_in_parent,
2566 baseline_offset_in_item: baseline_offset,
2567 bidi_level,
2568 },
2569 ));
2570
2571 if !layout
2574 .ifc
2575 .next_character_prevents_soft_wrap_opportunity(offset_in_text)
2576 {
2577 layout.have_deferred_soft_wrap_opportunity = true;
2578 }
2579 }
2580
2581 fn pick_baseline(&self, baselines: &Baselines) -> Option<Au> {
2585 match self.style().clone_baseline_source() {
2586 BaselineSource::First => baselines.first,
2587 BaselineSource::Last => baselines.last,
2588 BaselineSource::Auto if self.is_block_container() => baselines.last,
2589 BaselineSource::Auto => baselines.first,
2590 }
2591 }
2592
2593 fn get_block_sizes_and_baseline_offset(
2594 &self,
2595 ifc: &InlineFormattingContextLayout,
2596 block_size: Au,
2597 baseline_offset_in_content_area: Au,
2598 ) -> (LineBlockSizes, Au) {
2599 let mut contribution = if !is_baseline_relative(self.style().clone_baseline_shift()) {
2600 LineBlockSizes {
2601 line_height: block_size,
2602 baseline_relative_size_for_line_height: None,
2603 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2604 }
2605 } else {
2606 let baseline_relative_size = BaselineRelativeSize {
2607 ascent: baseline_offset_in_content_area,
2608 descent: block_size - baseline_offset_in_content_area,
2609 };
2610 LineBlockSizes {
2611 line_height: block_size,
2612 baseline_relative_size_for_line_height: Some(baseline_relative_size.clone()),
2613 size_for_baseline_positioning: baseline_relative_size,
2614 }
2615 };
2616
2617 let style = self.style();
2618 let baseline_offset = ifc
2619 .current_inline_container_state()
2620 .get_cumulative_baseline_offset_for_child(
2621 style.clone_alignment_baseline(),
2622 style.clone_baseline_shift(),
2623 &contribution,
2624 );
2625 contribution.adjust_for_baseline_offset(baseline_offset);
2626
2627 (contribution, baseline_offset)
2628 }
2629}
2630
2631impl FloatBox {
2632 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
2633 let fragment = Arc::new(self.layout(
2634 layout.layout_context,
2635 layout.positioning_context,
2636 layout.placement_state.containing_block,
2637 ));
2638
2639 self.contents
2640 .base
2641 .set_fragment(Fragment::Box(fragment.clone()));
2642 layout.push_line_item_to_unbreakable_segment(LineItem::Float(
2643 layout.current_inline_box_identifier(),
2644 FloatLineItem {
2645 fragment,
2646 needs_placement: true,
2647 },
2648 ));
2649 }
2650}
2651
2652fn place_pending_floats(ifc: &mut InlineFormattingContextLayout, line_items: &[LineItem]) {
2653 for item in line_items.iter() {
2654 if let LineItem::Float(_, float_line_item) = item &&
2655 float_line_item.needs_placement
2656 {
2657 ifc.place_float_fragment(&float_line_item.fragment);
2658 }
2659 }
2660}
2661
2662fn line_height(
2663 parent_style: &ComputedValues,
2664 font_metrics: &FontMetrics,
2665 flags: &InlineContainerStateFlags,
2666) -> Au {
2667 let font = parent_style.get_font();
2668 let font_size = font.font_size.computed_size();
2669 let mut line_height = match font.line_height {
2670 LineHeight::Normal => font_metrics.line_gap,
2671 LineHeight::Number(number) => (font_size * number.0).into(),
2672 LineHeight::Length(length) => length.0.into(),
2673 };
2674
2675 if flags.contains(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT) {
2679 line_height.max_assign(font_metrics.line_gap);
2680 }
2681
2682 line_height
2683}
2684
2685fn effective_baseline_shift(
2686 style: &ComputedValues,
2687 container: Option<&InlineContainerState>,
2688) -> BaselineShift {
2689 if container.is_none() {
2690 BaselineShift::zero()
2694 } else {
2695 style.clone_baseline_shift()
2696 }
2697}
2698
2699fn is_baseline_relative(baseline_shift: BaselineShift) -> bool {
2700 !matches!(
2701 baseline_shift,
2702 BaselineShift::Keyword(
2703 BaselineShiftKeyword::Top | BaselineShiftKeyword::Bottom | BaselineShiftKeyword::Center
2704 )
2705 )
2706}
2707
2708fn inline_container_needs_strut(
2734 style: &ComputedValues,
2735 layout_context: &LayoutContext,
2736 pbm: Option<&PaddingBorderMargin>,
2737) -> bool {
2738 if layout_context.style_context.quirks_mode() == QuirksMode::NoQuirks {
2739 return true;
2740 }
2741
2742 if style.get_box().display.is_list_item() {
2745 return true;
2746 }
2747
2748 pbm.is_some_and(|pbm| !pbm.padding_border_sums.inline.is_zero())
2749}
2750
2751impl ComputeInlineContentSizes for InlineFormattingContext {
2752 fn compute_inline_content_sizes(
2756 &self,
2757 layout_context: &LayoutContext,
2758 constraint_space: &ConstraintSpace,
2759 ) -> InlineContentSizesResult {
2760 ContentSizesComputation::compute(self, layout_context, constraint_space)
2761 }
2762}
2763
2764struct ContentSizesComputation<'layout_data> {
2766 layout_context: &'layout_data LayoutContext<'layout_data>,
2767 constraint_space: &'layout_data ConstraintSpace<'layout_data>,
2768 paragraph: ContentSizes,
2769 current_line: ContentSizes,
2770 pending_whitespace: ContentSizes,
2772 uncleared_floats: LogicalSides1D<ContentSizes>,
2774 cleared_floats: LogicalSides1D<ContentSizes>,
2776 had_content_yet_for_min_content: bool,
2779 had_content_yet_for_max_content: bool,
2782 ending_inline_pbm_stack: Vec<Au>,
2785 depends_on_block_constraints: bool,
2787}
2788
2789impl<'layout_data> ContentSizesComputation<'layout_data> {
2790 fn traverse(
2791 mut self,
2792 inline_formatting_context: &InlineFormattingContext,
2793 ) -> InlineContentSizesResult {
2794 self.add_inline_size(
2795 inline_formatting_context.inline_start_for_first_line(self.constraint_space.into()),
2796 );
2797 for inline_item in &inline_formatting_context.inline_items {
2798 self.process_item(inline_item, inline_formatting_context);
2799 }
2800 self.forced_line_break();
2801 self.flush_floats();
2802
2803 InlineContentSizesResult {
2804 sizes: self.paragraph,
2805 depends_on_block_constraints: self.depends_on_block_constraints,
2806 }
2807 }
2808
2809 fn process_item(
2810 &mut self,
2811 inline_item: &InlineItem,
2812 inline_formatting_context: &InlineFormattingContext,
2813 ) {
2814 match inline_item {
2815 InlineItem::StartInlineBox(inline_box) => {
2816 let inline_box = inline_box.borrow();
2820 let zero = Au::zero();
2821 let writing_mode = self.constraint_space.style.writing_mode;
2822 let layout_style = inline_box.layout_style();
2823 let padding = layout_style
2824 .padding(writing_mode)
2825 .percentages_relative_to(zero);
2826 let border = layout_style.border_width(writing_mode);
2827 let margin = inline_box
2828 .base
2829 .style
2830 .margin(writing_mode)
2831 .percentages_relative_to(zero)
2832 .auto_is(Au::zero);
2833
2834 let pbm = margin + padding + border;
2835 self.add_inline_size(pbm.inline_start);
2836 self.ending_inline_pbm_stack.push(pbm.inline_end);
2837 },
2838 InlineItem::EndInlineBox => {
2839 let length = self.ending_inline_pbm_stack.pop().unwrap_or_else(Au::zero);
2840 self.add_inline_size(length);
2841 },
2842 InlineItem::TextRun(text_run) => {
2843 let text_run = &*text_run.borrow();
2844 let parent_style = text_run.inline_styles.style.borrow();
2845 for item in text_run.items.iter() {
2846 match item {
2847 TextRunItem::LineBreak { .. } => {
2848 self.forced_line_break();
2851 },
2852 TextRunItem::Tab { .. } => {
2853 self.process_preserved_tab(&parent_style, inline_formatting_context)
2854 },
2855 TextRunItem::TextSegment(segment) => {
2856 self.process_text_segment(&parent_style, segment)
2857 },
2858 }
2859 }
2860 },
2861 InlineItem::Atomic(atomic, offset_in_text, _level) => {
2862 if self.had_content_yet_for_min_content &&
2864 !inline_formatting_context
2865 .previous_character_prevents_soft_wrap_opportunity(*offset_in_text)
2866 {
2867 self.line_break_opportunity();
2868 }
2869
2870 self.commit_pending_whitespace();
2871 let outer = self.outer_inline_content_sizes_of_float_or_atomic(&atomic.borrow());
2872 self.current_line += outer;
2873
2874 if !inline_formatting_context
2876 .next_character_prevents_soft_wrap_opportunity(*offset_in_text)
2877 {
2878 self.line_break_opportunity();
2879 }
2880 },
2881 InlineItem::OutOfFlowFloatBox(float_box) => {
2882 let float_box = float_box.borrow();
2883 let sizes = self.outer_inline_content_sizes_of_float_or_atomic(&float_box.contents);
2884 let style = &float_box.contents.style();
2885 let container_writing_mode = self.constraint_space.style.writing_mode;
2886 let clear =
2887 Clear::from_style_and_container_writing_mode(style, container_writing_mode);
2888 self.clear_floats(clear);
2889 let float_side =
2890 FloatSide::from_style_and_container_writing_mode(style, container_writing_mode);
2891 match float_side.expect("A float box needs to float to some side") {
2892 FloatSide::InlineStart => self.uncleared_floats.start.union_assign(&sizes),
2893 FloatSide::InlineEnd => self.uncleared_floats.end.union_assign(&sizes),
2894 }
2895 },
2896 InlineItem::BlockLevel(block_level) => {
2897 self.forced_line_break();
2898 self.flush_floats();
2899 let inline_content_sizes_result =
2900 compute_inline_content_sizes_for_block_level_boxes(
2901 std::slice::from_ref(block_level),
2902 self.layout_context,
2903 &self.constraint_space.into(),
2904 );
2905 self.depends_on_block_constraints |=
2906 inline_content_sizes_result.depends_on_block_constraints;
2907 self.current_line = inline_content_sizes_result.sizes;
2908 self.forced_line_break();
2909 },
2910 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => {},
2911 }
2912 }
2913
2914 fn process_text_segment(
2915 &mut self,
2916 parent_style: &AtomicRef<'_, ServoArc<ComputedValues>>,
2917 segment: &TextRunSegment,
2918 ) {
2919 let style_text = parent_style.get_inherited_text();
2920 let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap;
2921
2922 let break_at_start = segment.break_at_start && self.had_content_yet_for_min_content;
2925
2926 for (run_index, run) in segment.runs.iter().enumerate() {
2927 if can_wrap && (run_index != 0 || break_at_start) {
2930 self.line_break_opportunity();
2931 }
2932
2933 let advance = run.total_advance();
2934 if run.is_whitespace() {
2935 if !matches!(
2936 style_text.white_space_collapse,
2937 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
2938 ) {
2939 if self.had_content_yet_for_min_content {
2940 if can_wrap {
2941 self.line_break_opportunity();
2942 } else {
2943 self.pending_whitespace.min_content += advance;
2944 }
2945 }
2946 if self.had_content_yet_for_max_content {
2947 self.pending_whitespace.max_content += advance;
2948 }
2949 continue;
2950 }
2951 if can_wrap {
2952 self.pending_whitespace.max_content += advance;
2953 self.commit_pending_whitespace();
2954 self.line_break_opportunity();
2955 continue;
2956 }
2957 }
2958
2959 self.commit_pending_whitespace();
2960 self.add_inline_size(advance);
2961
2962 if can_wrap && run.ends_with_whitespace() {
2967 self.line_break_opportunity();
2968 }
2969 }
2970 }
2971
2972 fn process_preserved_tab(
2973 &mut self,
2974 parent_style: &AtomicRef<'_, ServoArc<ComputedValues>>,
2975 inline_formatting_context: &InlineFormattingContext,
2976 ) {
2977 self.commit_pending_whitespace();
2979
2980 self.current_line.min_content += inline_formatting_context
2981 .next_tab_stop_after_inline_advance(parent_style, self.current_line.min_content);
2982 self.current_line.max_content += inline_formatting_context
2983 .next_tab_stop_after_inline_advance(parent_style, self.current_line.max_content);
2984 if parent_style.get_inherited_text().text_wrap_mode == TextWrapMode::Wrap {
2985 self.line_break_opportunity();
2986 }
2987 }
2988
2989 fn add_inline_size(&mut self, l: Au) {
2990 self.current_line.min_content += l;
2991 self.current_line.max_content += l;
2992 }
2993
2994 fn line_break_opportunity(&mut self) {
2995 self.pending_whitespace.min_content = Au::zero();
2999 let current_min_content = mem::take(&mut self.current_line.min_content);
3000 self.paragraph.min_content.max_assign(current_min_content);
3001 self.had_content_yet_for_min_content = false;
3002 }
3003
3004 fn forced_line_break(&mut self) {
3005 self.line_break_opportunity();
3007
3008 self.pending_whitespace.max_content = Au::zero();
3010 let current_max_content = mem::take(&mut self.current_line.max_content);
3011 self.paragraph.max_content.max_assign(current_max_content);
3012 self.had_content_yet_for_max_content = false;
3013 }
3014
3015 fn commit_pending_whitespace(&mut self) {
3016 self.current_line += mem::take(&mut self.pending_whitespace);
3017 self.had_content_yet_for_min_content = true;
3018 self.had_content_yet_for_max_content = true;
3019 }
3020
3021 fn outer_inline_content_sizes_of_float_or_atomic(
3022 &mut self,
3023 context: &IndependentFormattingContext,
3024 ) -> ContentSizes {
3025 let result = context.outer_inline_content_sizes(
3026 self.layout_context,
3027 &self.constraint_space.into(),
3028 &LogicalVec2::zero(),
3029 false, );
3031 self.depends_on_block_constraints |= result.depends_on_block_constraints;
3032 result.sizes
3033 }
3034
3035 fn clear_floats(&mut self, clear: Clear) {
3036 match clear {
3037 Clear::InlineStart => {
3038 let start_floats = mem::take(&mut self.uncleared_floats.start);
3039 self.cleared_floats.start.max_assign(start_floats);
3040 },
3041 Clear::InlineEnd => {
3042 let end_floats = mem::take(&mut self.uncleared_floats.end);
3043 self.cleared_floats.end.max_assign(end_floats);
3044 },
3045 Clear::Both => {
3046 let start_floats = mem::take(&mut self.uncleared_floats.start);
3047 let end_floats = mem::take(&mut self.uncleared_floats.end);
3048 self.cleared_floats.start.max_assign(start_floats);
3049 self.cleared_floats.end.max_assign(end_floats);
3050 },
3051 Clear::None => {},
3052 }
3053 }
3054
3055 fn flush_floats(&mut self) {
3056 self.clear_floats(Clear::Both);
3057 let start_floats = mem::take(&mut self.cleared_floats.start);
3058 let end_floats = mem::take(&mut self.cleared_floats.end);
3059 self.paragraph.union_assign(&start_floats);
3060 self.paragraph.union_assign(&end_floats);
3061 }
3062
3063 fn compute(
3065 inline_formatting_context: &InlineFormattingContext,
3066 layout_context: &'layout_data LayoutContext,
3067 constraint_space: &'layout_data ConstraintSpace,
3068 ) -> InlineContentSizesResult {
3069 Self {
3070 layout_context,
3071 constraint_space,
3072 paragraph: ContentSizes::zero(),
3073 current_line: ContentSizes::zero(),
3074 pending_whitespace: ContentSizes::zero(),
3075 uncleared_floats: LogicalSides1D::default(),
3076 cleared_floats: LogicalSides1D::default(),
3077 had_content_yet_for_min_content: false,
3078 had_content_yet_for_max_content: false,
3079 ending_inline_pbm_stack: Vec::new(),
3080 depends_on_block_constraints: false,
3081 }
3082 .traverse(inline_formatting_context)
3083 }
3084}
3085
3086pub(crate) struct BidiLevels<'a> {
3087 info: Option<BidiInfo<'a>>,
3088}
3089
3090impl BidiLevels<'_> {
3091 fn level(&self, byte_offset_in_ifc_text: usize) -> Level {
3092 self.info
3093 .as_ref()
3094 .map_or_else(Level::ltr, |info| info.levels[byte_offset_in_ifc_text])
3095 }
3096}
3097
3098fn char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character: char) -> bool {
3110 if character == '\u{00A0}' {
3111 return false;
3112 }
3113 matches!(
3114 icu_properties::maps::line_break().get(character),
3115 ICULineBreak::Glue | ICULineBreak::WordJoiner | ICULineBreak::ZWJ
3116 )
3117}