1pub mod construct;
72pub mod inline_box;
73pub mod line;
74mod line_breaker;
75mod shaping_queue;
76mod small_kana;
77pub mod text_run;
78pub mod text_transform;
79
80use std::cell::{Cell, OnceCell};
81use std::mem;
82use std::ops::Range;
83use std::rc::Rc;
84use std::sync::{Arc, OnceLock};
85
86use app_units::{Au, MAX_AU};
87use atomic_refcell::AtomicRef;
88use bitflags::bitflags;
89use construct::InlineFormattingContextBuilder;
90use fonts::{FontMetrics, FontRef, ShapedTextSlice};
91use icu_locid::LanguageIdentifier;
92use icu_locid::subtags::{Language, language};
93use icu_properties::{self, LineBreak as ICULineBreak};
94use icu_segmenter::{LineBreakOptions, LineBreakStrictness, LineBreakWordOption};
95use inline_box::{InlineBox, InlineBoxContainerState, InlineBoxIdentifier, InlineBoxes};
96use layout_api::LayoutNode;
97use line::{
98 AbsolutelyPositionedLineItem, AtomicLineItem, FloatLineItem, LineItem, LineItemLayout,
99 TextRunLineItem,
100};
101use malloc_size_of_derive::MallocSizeOf;
102use script::layout_dom::ServoLayoutNode;
103use servo_arc::Arc as ServoArc;
104use servo_base::text::Utf32CodeUnits;
105use style::Zero;
106use style::computed_values::line_break::T as LineBreak;
107use style::computed_values::text_wrap_mode::T as TextWrapMode;
108use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
109use style::computed_values::word_break::T as WordBreak;
110use style::context::{QuirksMode, SharedStyleContext};
111use style::properties::ComputedValues;
112use style::properties::style_structs::InheritedText;
113use style::values::computed::BaselineShift;
114use style::values::generics::box_::BaselineShiftKeyword;
115use style::values::generics::font::LineHeight;
116use style::values::specified::box_::BaselineSource;
117use style::values::specified::text::TextAlignKeyword;
118use style::values::specified::{AlignmentBaseline, TextAlignLast, TextJustify};
119use text_run::{TextRun, get_font_for_first_font_for_style};
120use unicode_bidi::{BidiInfo, Level};
121
122use super::float::{Clear, PlacementAmongFloats};
123use super::{IndependentFloatOrAtomicLayoutResult, IndependentFormattingContextLayoutResult};
124use crate::cell::{ArcRefCell, WeakRefCell};
125use crate::context::LayoutContext;
126use crate::dom::WeakLayoutBox;
127use crate::dom_traversal::NodeAndStyleInfo;
128use crate::flow::float::{FloatBox, SequentialLayoutState};
129use crate::flow::inline::shaping_queue::ShapingQueue;
130use crate::flow::inline::text_run::{
131 CaretPlaceholder, FontAndScriptInfo, TextRunItem, TextRunSegment,
132};
133use crate::flow::{
134 BlockLevelBox, CollapsibleWithParentStartMargin, FloatSide, PlacementState,
135 compute_inline_content_sizes_for_block_level_boxes, layout_block_level_child,
136};
137use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
138use crate::fragment_tree::{CollapsedMargin, Fragment, FragmentFlags, PositioningFragment};
139use crate::geom::{LogicalRect, LogicalSides1D, LogicalVec2, ToLogical};
140use crate::layout_box_base::LayoutBoxBase;
141use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
142use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
143use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
144use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SharedStyle};
145
146static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
148static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
149
150#[derive(Debug, MallocSizeOf)]
151pub(crate) struct InlineFormattingContext {
152 inline_items: Vec<InlineItem>,
157
158 inline_boxes: InlineBoxes,
161
162 text_content: String,
164
165 shared_inline_styles: SharedInlineStyles,
168
169 default_font: Option<FontRef>,
173
174 has_first_formatted_line: bool,
177
178 pub(super) contains_floats: bool,
180
181 is_single_line_text_input: bool,
184
185 has_right_to_left_content: bool,
188
189 tab_size_multiplier: OnceLock<Au>,
194}
195
196#[derive(Clone, Debug, MallocSizeOf)]
201pub(crate) struct SharedInlineStyles {
202 pub style: SharedStyle,
203 pub selected: SharedStyle,
204}
205
206impl SharedInlineStyles {
207 pub(crate) fn ptr_eq(&self, other: &Self) -> bool {
208 self.style.ptr_eq(&other.style) && self.selected.ptr_eq(&other.selected)
209 }
210
211 pub(crate) fn from_info_and_context(info: &NodeAndStyleInfo, context: &LayoutContext) -> Self {
212 Self {
213 style: SharedStyle::new(info.style.clone()),
214 selected: SharedStyle::new(info.node.selected_style(&context.style_context)),
215 }
216 }
217}
218
219impl BlockLevelBox {
220 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
221 layout.process_soft_wrap_opportunity();
222 layout.commit_current_segment_to_line();
223 layout.process_line_break(
224 true, true, );
227
228 let fragment = layout_block_level_child(
229 layout.layout_context,
230 layout.positioning_context,
231 self,
232 layout.sequential_layout_state.as_deref_mut(),
233 &mut layout.placement_state,
234 layout.ignore_block_margins_for_stretch,
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(
255 true, false, );
258 }
259}
260
261#[derive(Clone, Debug, MallocSizeOf)]
262pub(crate) enum InlineItem {
263 StartInlineBox(ArcRefCell<InlineBox>),
264 EndInlineBox(ArcRefCell<InlineBox>),
265 TextRun(ArcRefCell<TextRun>),
266 OutOfFlowAbsolutelyPositionedBox(
267 ArcRefCell<AbsolutelyPositionedBox>,
268 usize, ),
270 OutOfFlowFloatBox(ArcRefCell<FloatBox>),
271 Atomic(
272 ArcRefCell<IndependentFormattingContext>,
273 usize, Level, ),
276 BlockLevel(ArcRefCell<BlockLevelBox>),
277}
278
279impl InlineItem {
280 pub(crate) fn repair_style(
281 &self,
282 context: &SharedStyleContext,
283 node: &ServoLayoutNode,
284 new_style: &ServoArc<ComputedValues>,
285 ) {
286 match self {
287 InlineItem::StartInlineBox(inline_box) => {
288 inline_box
289 .borrow_mut()
290 .repair_style(context, node, new_style);
291 },
292 InlineItem::EndInlineBox(..) => {},
293 InlineItem::TextRun(..) => {},
296 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box
297 .borrow_mut()
298 .context
299 .repair_style(context, node, new_style),
300 InlineItem::OutOfFlowFloatBox(float_box) => float_box
301 .borrow_mut()
302 .contents
303 .repair_style(context, node, new_style),
304 InlineItem::Atomic(atomic, ..) => {
305 atomic.borrow_mut().repair_style(context, node, new_style)
306 },
307 InlineItem::BlockLevel(block_level) => block_level
308 .borrow_mut()
309 .repair_style(context, node, new_style),
310 }
311 }
312
313 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
314 match self {
315 InlineItem::StartInlineBox(inline_box) => callback(&inline_box.borrow().base),
316 InlineItem::EndInlineBox(..) | InlineItem::TextRun(..) => {
317 unreachable!("Should never have these kind of fragments attached to a DOM node")
318 },
319 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
320 callback(&positioned_box.borrow().context.base)
321 },
322 InlineItem::OutOfFlowFloatBox(float_box) => callback(&float_box.borrow().contents.base),
323 InlineItem::Atomic(independent_formatting_context, ..) => {
324 callback(&independent_formatting_context.borrow().base)
325 },
326 InlineItem::BlockLevel(block_level) => block_level.borrow().with_base(callback),
327 }
328 }
329
330 pub(crate) fn with_base_mut<T>(&self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
331 match self {
332 InlineItem::StartInlineBox(inline_box) => callback(&mut inline_box.borrow_mut().base),
333 InlineItem::EndInlineBox(..) | InlineItem::TextRun(..) => {
334 unreachable!("Should never have these kind of fragments attached to a DOM node")
335 },
336 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
337 callback(&mut positioned_box.borrow_mut().context.base)
338 },
339 InlineItem::OutOfFlowFloatBox(float_box) => {
340 callback(&mut float_box.borrow_mut().contents.base)
341 },
342 InlineItem::Atomic(independent_formatting_context, ..) => {
343 callback(&mut independent_formatting_context.borrow_mut().base)
344 },
345 InlineItem::BlockLevel(block_level) => block_level.borrow_mut().with_base_mut(callback),
346 }
347 }
348
349 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
350 match self {
351 Self::StartInlineBox(_) | InlineItem::EndInlineBox(..) => {
352 },
355 Self::TextRun(_) => {
356 },
358 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
359 positioned_box.borrow().context.attached_to_tree(layout_box)
360 },
361 Self::OutOfFlowFloatBox(float_box) => {
362 float_box.borrow().contents.attached_to_tree(layout_box)
363 },
364 Self::Atomic(atomic, ..) => atomic.borrow().attached_to_tree(layout_box),
365 Self::BlockLevel(block_level) => block_level.borrow().attached_to_tree(layout_box),
366 }
367 }
368
369 pub(crate) fn downgrade(&self) -> WeakInlineItem {
370 match self {
371 Self::StartInlineBox(inline_box) => {
372 WeakInlineItem::StartInlineBox(inline_box.downgrade())
373 },
374 Self::EndInlineBox(inline_box) => WeakInlineItem::EndInlineBox(inline_box.downgrade()),
375 Self::TextRun(text_run) => WeakInlineItem::TextRun(text_run.downgrade()),
376 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
377 WeakInlineItem::OutOfFlowAbsolutelyPositionedBox(
378 positioned_box.downgrade(),
379 *offset_in_text,
380 )
381 },
382 Self::OutOfFlowFloatBox(float_box) => {
383 WeakInlineItem::OutOfFlowFloatBox(float_box.downgrade())
384 },
385 Self::Atomic(atomic, offset_in_text, bidi_level) => {
386 WeakInlineItem::Atomic(atomic.downgrade(), *offset_in_text, *bidi_level)
387 },
388 Self::BlockLevel(block_level) => WeakInlineItem::BlockLevel(block_level.downgrade()),
389 }
390 }
391}
392
393#[derive(Clone, Debug, MallocSizeOf)]
394pub(crate) enum WeakInlineItem {
395 StartInlineBox(WeakRefCell<InlineBox>),
396 EndInlineBox(WeakRefCell<InlineBox>),
397 TextRun(WeakRefCell<TextRun>),
398 OutOfFlowAbsolutelyPositionedBox(
399 WeakRefCell<AbsolutelyPositionedBox>,
400 usize, ),
402 OutOfFlowFloatBox(WeakRefCell<FloatBox>),
403 Atomic(
404 WeakRefCell<IndependentFormattingContext>,
405 usize, Level, ),
408 BlockLevel(WeakRefCell<BlockLevelBox>),
409}
410
411impl WeakInlineItem {
412 pub(crate) fn upgrade(&self) -> Option<InlineItem> {
413 Some(match self {
414 Self::StartInlineBox(inline_box) => InlineItem::StartInlineBox(inline_box.upgrade()?),
415 Self::EndInlineBox(inline_box) => InlineItem::EndInlineBox(inline_box.upgrade()?),
416 Self::TextRun(text_run) => InlineItem::TextRun(text_run.upgrade()?),
417 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
418 InlineItem::OutOfFlowAbsolutelyPositionedBox(
419 positioned_box.upgrade()?,
420 *offset_in_text,
421 )
422 },
423 Self::OutOfFlowFloatBox(float_box) => {
424 InlineItem::OutOfFlowFloatBox(float_box.upgrade()?)
425 },
426 Self::Atomic(atomic, offset_in_text, bidi_level) => {
427 InlineItem::Atomic(atomic.upgrade()?, *offset_in_text, *bidi_level)
428 },
429 Self::BlockLevel(block_level) => InlineItem::BlockLevel(block_level.upgrade()?),
430 })
431 }
432}
433
434struct LineUnderConstruction {
441 start_position: LogicalVec2<Au>,
444
445 inline_position: Au,
448
449 max_block_size: LineBlockSizes,
453
454 has_content: bool,
457
458 has_inline_pbm: bool,
461
462 has_floats_waiting_to_be_placed: bool,
466
467 placement_among_floats: OnceCell<LogicalRect<Au>>,
472
473 line_items: Vec<LineItem>,
476
477 for_block_level: bool,
479
480 caret_placeholder: Option<CaretPlaceholder>,
483}
484
485impl LineUnderConstruction {
486 fn new(start_position: LogicalVec2<Au>) -> Self {
487 Self {
488 inline_position: start_position.inline,
489 start_position,
490 max_block_size: LineBlockSizes::zero(),
491 has_content: false,
492 has_inline_pbm: false,
493 has_floats_waiting_to_be_placed: false,
494 placement_among_floats: OnceCell::new(),
495 line_items: Vec::new(),
496 for_block_level: false,
497 caret_placeholder: None,
498 }
499 }
500
501 fn replace_placement_among_floats(&mut self, new_placement: LogicalRect<Au>) {
502 self.placement_among_floats.take();
503 let _ = self.placement_among_floats.set(new_placement);
504 }
505
506 fn trim_trailing_whitespace(&mut self) -> Au {
508 let mut whitespace_trimmed = Au::zero();
513 for item in self.line_items.iter_mut().rev() {
514 if !item.trim_whitespace_at_end(&mut whitespace_trimmed) {
515 break;
516 }
517 }
518
519 whitespace_trimmed
520 }
521
522 fn count_justification_opportunities(&self) -> usize {
524 self.line_items
525 .iter()
526 .filter_map(|item| match item {
527 LineItem::TextRun(_, text_run) => Some(
528 text_run
529 .text
530 .iter()
531 .map(|shaped_text_slice| shaped_text_slice.total_word_separators())
532 .sum::<usize>(),
533 ),
534 _ => None,
535 })
536 .sum()
537 }
538
539 fn is_phantom(&self) -> bool {
542 !self.has_content && !self.has_inline_pbm
544 }
545}
546
547#[derive(Clone, Debug)]
553struct BaselineRelativeSize {
554 ascent: Au,
558
559 descent: Au,
563}
564
565impl BaselineRelativeSize {
566 fn zero() -> Self {
567 Self {
568 ascent: Au::zero(),
569 descent: Au::zero(),
570 }
571 }
572
573 fn max(&self, other: &Self) -> Self {
574 BaselineRelativeSize {
575 ascent: self.ascent.max(other.ascent),
576 descent: self.descent.max(other.descent),
577 }
578 }
579
580 fn adjust_for_nested_baseline_offset(&mut self, baseline_offset: Au) {
594 self.ascent -= baseline_offset;
595 self.descent += baseline_offset;
596 }
597}
598
599#[derive(Clone, Debug)]
600struct LineBlockSizes {
601 line_height: Au,
602 baseline_relative_size_for_line_height: Option<BaselineRelativeSize>,
603 size_for_baseline_positioning: BaselineRelativeSize,
604}
605
606impl LineBlockSizes {
607 fn zero() -> Self {
608 LineBlockSizes {
609 line_height: Au::zero(),
610 baseline_relative_size_for_line_height: None,
611 size_for_baseline_positioning: BaselineRelativeSize::zero(),
612 }
613 }
614
615 fn resolve(&self) -> Au {
616 let height_from_ascent_and_descent = self
617 .baseline_relative_size_for_line_height
618 .as_ref()
619 .map(|size| (size.ascent + size.descent).abs())
620 .unwrap_or_else(Au::zero);
621 self.line_height.max(height_from_ascent_and_descent)
622 }
623
624 fn max(&self, other: &LineBlockSizes) -> LineBlockSizes {
625 let baseline_relative_size = match (
626 self.baseline_relative_size_for_line_height.as_ref(),
627 other.baseline_relative_size_for_line_height.as_ref(),
628 ) {
629 (Some(our_size), Some(other_size)) => Some(our_size.max(other_size)),
630 (our_size, other_size) => our_size.or(other_size).cloned(),
631 };
632 Self {
633 line_height: self.line_height.max(other.line_height),
634 baseline_relative_size_for_line_height: baseline_relative_size,
635 size_for_baseline_positioning: self
636 .size_for_baseline_positioning
637 .max(&other.size_for_baseline_positioning),
638 }
639 }
640
641 fn max_assign(&mut self, other: &LineBlockSizes) {
642 *self = self.max(other);
643 }
644
645 fn adjust_for_baseline_offset(&mut self, baseline_offset: Au) {
646 if let Some(size) = self.baseline_relative_size_for_line_height.as_mut() {
647 size.adjust_for_nested_baseline_offset(baseline_offset)
648 }
649 self.size_for_baseline_positioning
650 .adjust_for_nested_baseline_offset(baseline_offset);
651 }
652
653 fn find_baseline_offset(&self) -> Au {
660 match self.baseline_relative_size_for_line_height.as_ref() {
661 Some(size) => size.ascent,
662 None => {
663 let leading = self.resolve() -
666 (self.size_for_baseline_positioning.ascent +
667 self.size_for_baseline_positioning.descent);
668 leading.scale_by(0.5) + self.size_for_baseline_positioning.ascent
669 },
670 }
671 }
672}
673
674struct UnbreakableSegmentUnderConstruction {
678 inline_size: Au,
680
681 max_block_size: LineBlockSizes,
684
685 line_items: Vec<LineItem>,
687
688 inline_box_hierarchy_depth: Option<usize>,
691
692 has_content: bool,
696
697 has_inline_pbm: bool,
700
701 trailing_whitespace_size: Au,
703}
704
705impl UnbreakableSegmentUnderConstruction {
706 fn new() -> Self {
707 Self {
708 inline_size: Au::zero(),
709 max_block_size: LineBlockSizes {
710 line_height: Au::zero(),
711 baseline_relative_size_for_line_height: None,
712 size_for_baseline_positioning: BaselineRelativeSize::zero(),
713 },
714 line_items: Vec::new(),
715 inline_box_hierarchy_depth: None,
716 has_content: false,
717 has_inline_pbm: false,
718 trailing_whitespace_size: Au::zero(),
719 }
720 }
721
722 fn reset(&mut self) {
724 assert!(self.line_items.is_empty()); self.inline_size = Au::zero();
726 self.max_block_size = LineBlockSizes::zero();
727 self.inline_box_hierarchy_depth = None;
728 self.has_content = false;
729 self.has_inline_pbm = false;
730 self.trailing_whitespace_size = Au::zero();
731 }
732
733 fn push_line_item(&mut self, line_item: LineItem, inline_box_hierarchy_depth: usize) {
738 if self.line_items.is_empty() {
739 self.inline_box_hierarchy_depth = Some(inline_box_hierarchy_depth);
740 }
741 self.line_items.push(line_item);
742 }
743
744 fn trim_leading_whitespace(&mut self) {
755 let mut whitespace_trimmed = Au::zero();
756 for item in self.line_items.iter_mut() {
757 if !item.trim_whitespace_at_start(&mut whitespace_trimmed) {
758 break;
759 }
760 }
761 self.inline_size -= whitespace_trimmed;
762 }
763
764 fn is_phantom(&self) -> bool {
767 !self.has_content && !self.has_inline_pbm
769 }
770}
771
772bitflags! {
773 struct InlineContainerStateFlags: u8 {
774 const CREATE_STRUT = 0b0001;
775 const IS_SINGLE_LINE_TEXT_INPUT = 0b0010;
776 }
777}
778
779struct InlineContainerState {
780 style: ServoArc<ComputedValues>,
782
783 flags: InlineContainerStateFlags,
785
786 has_content: Cell<bool>,
789
790 strut_block_sizes: LineBlockSizes,
795
796 nested_strut_block_sizes: LineBlockSizes,
800
801 pub baseline_offset: Au,
807
808 default_font: Option<FontRef>,
811
812 font_metrics: Arc<FontMetrics>,
814}
815
816struct InlineFormattingContextLayout<'layout_data> {
817 positioning_context: &'layout_data mut PositioningContext,
818 placement_state: PlacementState<'layout_data>,
819 sequential_layout_state: Option<&'layout_data mut SequentialLayoutState>,
820 layout_context: &'layout_data LayoutContext<'layout_data>,
821
822 ifc: &'layout_data InlineFormattingContext,
824
825 root_nesting_level: InlineContainerState,
835
836 inline_box_state_stack: Vec<Rc<InlineBoxContainerState>>,
840
841 cloneable_inline_box_end_pbm_size: Au,
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: bool,
883
884 caret_placeholder: Option<CaretPlaceholder>,
887
888 deferred_br_clear: Clear,
892
893 pub have_deferred_soft_wrap_opportunity: bool,
897
898 depends_on_block_constraints: bool,
901
902 white_space_collapse: WhiteSpaceCollapse,
907
908 text_wrap_mode: TextWrapMode,
913
914 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
917}
918
919impl InlineFormattingContextLayout<'_> {
920 fn current_inline_container_state(&self) -> &InlineContainerState {
921 match self.inline_box_state_stack.last() {
922 Some(inline_box_state) => &inline_box_state.base,
923 None => &self.root_nesting_level,
924 }
925 }
926
927 fn current_inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
928 self.inline_box_state_stack
929 .last()
930 .map(|state| state.identifier)
931 }
932
933 fn current_line_max_block_size_including_nested_containers(&self) -> LineBlockSizes {
934 self.current_inline_container_state()
935 .nested_strut_block_sizes
936 .max(&self.current_line.max_block_size)
937 }
938
939 fn current_line_block_start_considering_placement_among_floats(&self) -> Au {
940 self.current_line.placement_among_floats.get().map_or(
941 self.current_line.start_position.block,
942 |placement_among_floats| placement_among_floats.start_corner.block,
943 )
944 }
945
946 fn propagate_current_nesting_level_white_space_style(&mut self) {
947 let style = match self.inline_box_state_stack.last() {
948 Some(inline_box_state) => &inline_box_state.base.style,
949 None => self.placement_state.containing_block.style,
950 };
951 let style_text = style.get_inherited_text();
952 self.white_space_collapse = style_text.white_space_collapse;
953 self.text_wrap_mode = style_text.text_wrap_mode;
954 }
955
956 fn processing_br_element(&self) -> bool {
957 self.inline_box_state_stack.last().is_some_and(|state| {
958 state
959 .base_fragment_info
960 .flags
961 .contains(FragmentFlags::IS_BR_ELEMENT)
962 })
963 }
964
965 fn start_inline_box(&mut self, inline_box: &InlineBox) {
968 let containing_block = self.containing_block();
969 let inline_box_state = InlineBoxContainerState::new(
970 inline_box,
971 containing_block,
972 self.layout_context,
973 self.current_inline_container_state(),
974 inline_box.default_font.clone(),
975 );
976
977 self.depends_on_block_constraints |= inline_box
978 .base
979 .style
980 .depends_on_block_constraints_due_to_relative_positioning(
981 containing_block.style.writing_mode,
982 );
983
984 if inline_box_state
989 .base_fragment_info
990 .flags
991 .contains(FragmentFlags::IS_BR_ELEMENT) &&
992 self.deferred_br_clear == Clear::None
993 {
994 self.deferred_br_clear = Clear::from_style_and_container_writing_mode(
995 &inline_box_state.base.style,
996 self.containing_block().style.writing_mode,
997 );
998 }
999
1000 let padding = inline_box_state.pbm.padding.inline_start;
1001 let border = inline_box_state.pbm.border.inline_start;
1002 let margin = inline_box_state.pbm.margin.inline_start.auto_is(Au::zero);
1003 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
1006 self.current_line_segment.has_inline_pbm = true;
1007 }
1008 self.current_line_segment.inline_size += padding + border + margin;
1009 self.current_line_segment
1010 .line_items
1011 .push(LineItem::InlineStartBoxPaddingBorderMargin(
1012 inline_box.identifier,
1013 ));
1014
1015 let inline_box_state = Rc::new(inline_box_state);
1016 if inline_box_state.should_clone_pbm() {
1017 self.cloneable_inline_box_end_pbm_size += inline_box_state.pbm.padding.inline_end;
1018 self.cloneable_inline_box_end_pbm_size += inline_box_state.pbm.border.inline_end;
1019 self.cloneable_inline_box_end_pbm_size +=
1020 inline_box_state.pbm.margin.inline_end.auto_is(Au::zero);
1021 }
1022
1023 assert_eq!(
1027 self.inline_box_states.len(),
1028 inline_box.identifier.index_in_inline_boxes as usize
1029 );
1030 self.inline_box_states.push(inline_box_state.clone());
1031 self.inline_box_state_stack.push(inline_box_state);
1032 }
1033
1034 fn finish_inline_box(&mut self) {
1037 let inline_box_state = match self.inline_box_state_stack.pop() {
1038 Some(inline_box_state) => inline_box_state,
1039 None => return, };
1041 if inline_box_state.should_clone_pbm() {
1042 self.cloneable_inline_box_end_pbm_size -= inline_box_state.pbm.padding.inline_end;
1043 self.cloneable_inline_box_end_pbm_size -= inline_box_state.pbm.border.inline_end;
1044 self.cloneable_inline_box_end_pbm_size -=
1045 inline_box_state.pbm.margin.inline_end.auto_is(Au::zero);
1046 }
1047
1048 self.current_line_segment
1049 .max_block_size
1050 .max_assign(&inline_box_state.base.nested_strut_block_sizes);
1051
1052 if inline_box_state.base.has_content.get() {
1057 self.propagate_current_nesting_level_white_space_style();
1058 }
1059
1060 let padding = inline_box_state.pbm.padding.inline_end;
1061 let border = inline_box_state.pbm.border.inline_end;
1062 let margin = inline_box_state.pbm.margin.inline_end.auto_is(Au::zero);
1063 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
1066 self.current_line_segment.has_inline_pbm = true;
1067 }
1068 self.current_line_segment.inline_size += padding + border + margin;
1069 self.current_line_segment
1070 .line_items
1071 .push(LineItem::InlineEndBoxPaddingBorderMargin(
1072 inline_box_state.identifier,
1073 ));
1074 }
1075
1076 fn finish_last_line(&mut self) {
1077 self.possibly_flush_deferred_forced_line_break();
1079
1080 self.process_soft_wrap_opportunity();
1086
1087 self.commit_current_segment_to_line();
1090
1091 self.finish_current_line_and_reset(
1094 true, false, );
1097 }
1098
1099 fn finish_current_line_and_reset(
1103 &mut self,
1104 last_line_or_forced_line_break: bool,
1105 for_block_level: bool,
1106 ) {
1107 self.possibly_push_empty_text_run_to_line_for_text_caret();
1108
1109 let whitespace_trimmed = self.current_line.trim_trailing_whitespace();
1110 if !self.current_line.for_block_level {
1113 for inline_box in self.inline_box_state_stack.iter().rev() {
1114 if inline_box.should_clone_pbm() {
1115 self.current_line_segment.line_items.push(
1116 LineItem::InlineEndBoxPaddingBorderMargin(inline_box.identifier),
1117 );
1118 }
1119 }
1120 }
1121 let (inline_start_position, justification_adjustment) = self
1122 .calculate_current_line_inline_start_and_justification_adjustment(
1123 whitespace_trimmed,
1124 last_line_or_forced_line_break,
1125 );
1126
1127 let is_phantom_line = self.current_line.is_phantom();
1136 if !is_phantom_line {
1137 self.current_line.start_position.block += self.placement_state.current_margin.solve();
1138 self.placement_state.current_margin = CollapsedMargin::zero();
1139 }
1140 let block_start_position =
1141 self.current_line_block_start_considering_placement_among_floats();
1142
1143 let effective_block_advance = if is_phantom_line {
1144 LineBlockSizes::zero()
1145 } else {
1146 self.current_line_max_block_size_including_nested_containers()
1147 };
1148
1149 let resolved_block_advance = effective_block_advance.resolve();
1150 let block_end_position = if self.current_line.for_block_level {
1151 self.placement_state.current_block_direction_position
1152 } else {
1153 let mut block_end_position = block_start_position + resolved_block_advance;
1154 if let Some(sequential_layout_state) = self.sequential_layout_state.as_mut() {
1155 if !is_phantom_line {
1156 sequential_layout_state.commit_margin();
1157 }
1158
1159 let increment = block_end_position - self.current_line.start_position.block;
1162 sequential_layout_state.advance_block_position(increment);
1163
1164 if let Some(clearance) = sequential_layout_state
1168 .calculate_clearance(self.deferred_br_clear, &CollapsedMargin::zero())
1169 {
1170 sequential_layout_state.advance_block_position(clearance);
1171 block_end_position += clearance;
1172 };
1173 self.deferred_br_clear = Clear::None;
1174 }
1175 block_end_position
1176 };
1177
1178 let line_to_layout = std::mem::replace(
1180 &mut self.current_line,
1181 LineUnderConstruction::new(LogicalVec2 {
1182 inline: Au::zero(),
1183 block: block_end_position,
1184 }),
1185 );
1186 self.current_line.for_block_level = for_block_level;
1187
1188 if !for_block_level {
1191 for inline_box in self.inline_box_state_stack.iter() {
1192 if inline_box.should_clone_pbm() {
1193 self.current_line_segment.line_items.push(
1194 LineItem::InlineStartBoxPaddingBorderMargin(inline_box.identifier),
1195 );
1196 }
1197 }
1198 }
1199
1200 if !line_to_layout.for_block_level {
1201 self.placement_state.current_block_direction_position = block_end_position;
1202 }
1203
1204 if line_to_layout.has_floats_waiting_to_be_placed {
1205 place_pending_floats(self, &line_to_layout.line_items);
1206 }
1207
1208 let start_position = LogicalVec2 {
1209 block: block_start_position,
1210 inline: inline_start_position,
1211 };
1212
1213 let baseline_offset = effective_block_advance.find_baseline_offset();
1214 let start_positioning_context_length = self.positioning_context.len();
1215 let fragments = LineItemLayout::layout_line_items(
1216 self,
1217 line_to_layout.line_items,
1218 start_position,
1219 &effective_block_advance,
1220 justification_adjustment,
1221 is_phantom_line,
1222 line_to_layout.for_block_level,
1223 );
1224
1225 if !is_phantom_line {
1226 let baseline = baseline_offset + block_start_position;
1227 self.placement_state
1228 .inflow_baselines
1229 .first
1230 .get_or_insert(baseline);
1231 self.placement_state.inflow_baselines.last = Some(baseline);
1232 self.placement_state
1233 .next_in_flow_margin_collapses_with_parent_start_margin = false;
1234 }
1235
1236 if fragments.is_empty() &&
1238 self.positioning_context.len() == start_positioning_context_length
1239 {
1240 return;
1241 }
1242
1243 let start_corner = LogicalVec2 {
1247 inline: Au::zero(),
1248 block: block_start_position,
1249 };
1250
1251 let logical_origin_in_physical_coordinates =
1252 start_corner.to_physical_vector(self.containing_block().style.writing_mode);
1253 self.positioning_context
1254 .adjust_static_position_of_hoisted_fragments_with_offset(
1255 &logical_origin_in_physical_coordinates,
1256 start_positioning_context_length,
1257 );
1258
1259 let containing_block = self.containing_block();
1260 let physical_line_rect = LogicalRect {
1261 start_corner,
1262 size: LogicalVec2 {
1263 inline: containing_block.size.inline,
1264 block: effective_block_advance.resolve(),
1265 },
1266 }
1267 .as_physical(Some(containing_block));
1268 self.fragments
1269 .push(Fragment::Positioning(PositioningFragment::new_anonymous(
1270 self.root_nesting_level.style.clone(),
1271 physical_line_rect,
1272 fragments,
1273 true, )));
1275 }
1276
1277 fn calculate_current_line_inline_start_and_justification_adjustment(
1282 &self,
1283 whitespace_trimmed: Au,
1284 last_line_or_forced_line_break: bool,
1285 ) -> (Au, Au) {
1286 enum TextAlign {
1287 Start,
1288 Center,
1289 End,
1290 }
1291 let containing_block = self.containing_block();
1292 let style = containing_block.style;
1293 let mut text_align_keyword = style.clone_text_align();
1294
1295 if last_line_or_forced_line_break {
1296 text_align_keyword = match style.clone_text_align_last() {
1297 TextAlignLast::Auto if text_align_keyword == TextAlignKeyword::Justify => {
1298 TextAlignKeyword::Start
1299 },
1300 TextAlignLast::Auto => text_align_keyword,
1301 TextAlignLast::Start => TextAlignKeyword::Start,
1302 TextAlignLast::End => TextAlignKeyword::End,
1303 TextAlignLast::Left => TextAlignKeyword::Left,
1304 TextAlignLast::Right => TextAlignKeyword::Right,
1305 TextAlignLast::Center => TextAlignKeyword::Center,
1306 TextAlignLast::Justify => TextAlignKeyword::Justify,
1307 };
1308 }
1309
1310 let text_align = match text_align_keyword {
1311 TextAlignKeyword::Start => TextAlign::Start,
1312 TextAlignKeyword::Center | TextAlignKeyword::MozCenter => TextAlign::Center,
1313 TextAlignKeyword::End => TextAlign::End,
1314 TextAlignKeyword::Left | TextAlignKeyword::MozLeft => {
1315 if style.writing_mode.line_left_is_inline_start() {
1316 TextAlign::Start
1317 } else {
1318 TextAlign::End
1319 }
1320 },
1321 TextAlignKeyword::Right | TextAlignKeyword::MozRight => {
1322 if style.writing_mode.line_left_is_inline_start() {
1323 TextAlign::End
1324 } else {
1325 TextAlign::Start
1326 }
1327 },
1328 TextAlignKeyword::Justify => TextAlign::Start,
1329 };
1330
1331 let (line_start, available_space) = match self.current_line.placement_among_floats.get() {
1332 Some(placement_among_floats) => (
1333 placement_among_floats.start_corner.inline,
1334 placement_among_floats.size.inline,
1335 ),
1336 None => (Au::zero(), containing_block.size.inline),
1337 };
1338
1339 let text_indent = self.current_line.start_position.inline;
1346 let line_length = self.current_line.inline_position - whitespace_trimmed - text_indent;
1347 let adjusted_line_start = line_start +
1348 match text_align {
1349 TextAlign::Start => text_indent,
1350 TextAlign::End => (available_space - line_length).max(text_indent),
1351 TextAlign::Center => (available_space - line_length + text_indent)
1352 .scale_by(0.5)
1353 .max(text_indent),
1354 };
1355
1356 let text_justify = containing_block.style.clone_text_justify();
1360 let justification_adjustment = match (text_align_keyword, text_justify) {
1361 (TextAlignKeyword::Justify, TextJustify::None) => Au::zero(),
1364 (TextAlignKeyword::Justify, _) => {
1365 match self.current_line.count_justification_opportunities() {
1366 0 => Au::zero(),
1367 num_justification_opportunities => {
1368 (available_space - text_indent - line_length)
1369 .scale_by(1. / num_justification_opportunities as f32)
1370 },
1371 }
1372 },
1373 _ => Au::zero(),
1374 };
1375
1376 let justification_adjustment = justification_adjustment.max(Au::zero());
1379
1380 (adjusted_line_start, justification_adjustment)
1381 }
1382
1383 fn place_float_fragment(&mut self, float: &FloatLineItem) {
1384 let state = self
1385 .sequential_layout_state
1386 .as_mut()
1387 .expect("Tried to lay out a float with no sequential placement state!");
1388
1389 let block_offset_from_containining_block_top = state
1390 .current_block_position_including_margins() -
1391 state.current_containing_block_offset();
1392 state.place_float_fragment(
1393 &float.fragment,
1394 self.placement_state.containing_block,
1395 CollapsedMargin::zero(),
1396 block_offset_from_containining_block_top,
1397 );
1398 self.positioning_context
1399 .adjust_static_position_of_hoisted_fragments_in_range(
1400 &float.fragment.base.rect().origin.to_vector(),
1401 &float.range,
1402 )
1403 }
1404
1405 fn place_float_line_item_for_commit_to_line(
1414 &mut self,
1415 float_item: &mut FloatLineItem,
1416 line_inline_size_without_trailing_whitespace: Au,
1417 ) {
1418 let containing_block = self.containing_block();
1419 let float_fragment = &float_item.fragment;
1420 let logical_margin_rect_size = float_fragment
1421 .margin_rect()
1422 .size
1423 .to_logical(containing_block.style.writing_mode);
1424 let inline_size = logical_margin_rect_size.inline.max(Au::zero());
1425
1426 let available_inline_size = match self.current_line.placement_among_floats.get() {
1427 Some(placement_among_floats) => placement_among_floats.size.inline,
1428 None => containing_block.size.inline,
1429 } - line_inline_size_without_trailing_whitespace;
1430
1431 let has_content = self.current_line.has_content || self.current_line_segment.has_content;
1437 let fits_on_line = !has_content || inline_size <= available_inline_size;
1438 let needs_placement_later =
1439 self.current_line.has_floats_waiting_to_be_placed || !fits_on_line;
1440
1441 if needs_placement_later {
1442 self.current_line.has_floats_waiting_to_be_placed = true;
1443 } else {
1444 self.place_float_fragment(float_item);
1445 float_item.needs_placement = false;
1446 }
1447
1448 let new_placement = self.place_line_among_floats(&LogicalVec2 {
1453 inline: line_inline_size_without_trailing_whitespace,
1454 block: self.current_line.max_block_size.resolve(),
1455 });
1456 self.current_line
1457 .replace_placement_among_floats(new_placement);
1458 }
1459
1460 fn place_line_among_floats(&self, potential_line_size: &LogicalVec2<Au>) -> LogicalRect<Au> {
1465 let sequential_layout_state = self
1466 .sequential_layout_state
1467 .as_ref()
1468 .expect("Should not have called this function without having floats.");
1469
1470 let ifc_offset_in_float_container = LogicalVec2 {
1471 inline: sequential_layout_state
1472 .floats
1473 .containing_block_info
1474 .inline_start,
1475 block: sequential_layout_state.current_containing_block_offset(),
1476 };
1477
1478 let ceiling = self.current_line_block_start_considering_placement_among_floats();
1479 let mut placement = PlacementAmongFloats::new(
1480 &sequential_layout_state.floats,
1481 ceiling + ifc_offset_in_float_container.block,
1482 LogicalVec2 {
1483 inline: potential_line_size.inline,
1484 block: potential_line_size.block,
1485 },
1486 &PaddingBorderMargin::zero(),
1487 );
1488
1489 let mut placement_rect = placement.place();
1490 placement_rect.start_corner -= ifc_offset_in_float_container;
1491 placement_rect
1492 }
1493
1494 fn new_potential_line_size_causes_line_break(
1501 &mut self,
1502 potential_line_size: &LogicalVec2<Au>,
1503 ) -> bool {
1504 let containing_block = self.containing_block();
1505 let available_line_space = if self.sequential_layout_state.is_some() {
1506 self.current_line
1507 .placement_among_floats
1508 .get_or_init(|| self.place_line_among_floats(potential_line_size))
1509 .size
1510 } else {
1511 LogicalVec2 {
1512 inline: containing_block.size.inline,
1513 block: MAX_AU,
1514 }
1515 };
1516
1517 let inline_would_overflow = potential_line_size.inline > available_line_space.inline;
1518 let block_would_overflow = potential_line_size.block > available_line_space.block;
1519
1520 let can_break = self.current_line.has_content;
1523
1524 if !can_break {
1530 if self.sequential_layout_state.is_some() &&
1533 (inline_would_overflow || block_would_overflow)
1534 {
1535 let new_placement = self.place_line_among_floats(potential_line_size);
1536 self.current_line
1537 .replace_placement_among_floats(new_placement);
1538 }
1539
1540 return false;
1541 }
1542
1543 if potential_line_size.inline > containing_block.size.inline {
1546 return true;
1547 }
1548
1549 if block_would_overflow {
1553 assert!(self.sequential_layout_state.is_some());
1555 let new_placement = self.place_line_among_floats(potential_line_size);
1556 if new_placement.start_corner.block !=
1557 self.current_line_block_start_considering_placement_among_floats()
1558 {
1559 return true;
1560 } else {
1561 self.current_line
1562 .replace_placement_among_floats(new_placement);
1563 return false;
1564 }
1565 }
1566
1567 potential_line_size.inline + self.cloneable_inline_box_end_pbm_size >
1571 available_line_space.inline
1572 }
1573
1574 fn defer_forced_line_break_at_character_offset(
1575 &mut self,
1576 caret_placeholder: &Option<CaretPlaceholder>,
1577 ) {
1578 if !self.unbreakable_segment_fits_on_line() {
1581 self.process_line_break(
1582 false, false, );
1585 }
1586
1587 self.force_line_break_before_new_content = true;
1589 self.caret_placeholder = caret_placeholder.clone();
1590
1591 let line_is_empty =
1599 !self.current_line_segment.has_content && !self.current_line.has_content;
1600 if !self.processing_br_element() || line_is_empty {
1601 let strut_size = self
1602 .current_inline_container_state()
1603 .strut_block_sizes
1604 .clone();
1605 self.update_unbreakable_segment_for_new_content(
1606 &strut_size,
1607 Au::zero(),
1608 SegmentContentFlags::empty(),
1609 );
1610 }
1611 }
1612
1613 fn possibly_flush_deferred_forced_line_break(&mut self) {
1614 if !self.force_line_break_before_new_content {
1615 return;
1616 }
1617 self.force_line_break_before_new_content = false;
1618
1619 self.commit_current_segment_to_line();
1620 self.process_line_break(
1621 true, false, );
1624
1625 self.current_line.caret_placeholder = self.caret_placeholder.take();
1626 }
1627
1628 fn push_line_item_to_unbreakable_segment(&mut self, line_item: LineItem) {
1629 self.current_line_segment
1630 .push_line_item(line_item, self.inline_box_state_stack.len());
1631 }
1632
1633 fn push_glyph_store_to_unbreakable_segment(
1634 &mut self,
1635 glyph_store: Arc<ShapedTextSlice>,
1636 text_run: &TextRun,
1637 info: &FontAndScriptInfo,
1638 character_range: Range<Utf32CodeUnits>,
1639 ) {
1640 let inline_advance = glyph_store.total_advance();
1641 let flags = if glyph_store.is_whitespace() {
1642 SegmentContentFlags::from(text_run.inline_styles().style.borrow().get_inherited_text())
1643 } else {
1644 SegmentContentFlags::empty()
1645 };
1646
1647 let mut block_contribution = LineBlockSizes::zero();
1648 let quirks_mode = self.layout_context.style_context.quirks_mode() != QuirksMode::NoQuirks;
1649 let current_inline_container_state = self.current_inline_container_state();
1650 if quirks_mode && !flags.is_collapsible_whitespace() {
1651 block_contribution.max_assign(¤t_inline_container_state.strut_block_sizes);
1656 }
1657
1658 let font_metrics = &info.font_info.font.metrics;
1662 if current_inline_container_state
1663 .font_metrics
1664 .block_metrics_meaningfully_differ(font_metrics)
1665 {
1666 let baseline_shift = effective_baseline_shift(
1668 ¤t_inline_container_state.style,
1669 self.inline_box_state_stack.last().map(|c| &c.base),
1670 );
1671 let mut font_block_conribution = current_inline_container_state
1672 .get_block_size_contribution(
1673 baseline_shift,
1674 font_metrics,
1675 ¤t_inline_container_state.font_metrics,
1676 );
1677 font_block_conribution
1678 .adjust_for_baseline_offset(current_inline_container_state.baseline_offset);
1679 block_contribution.max_assign(&font_block_conribution);
1680 }
1681
1682 self.update_unbreakable_segment_for_new_content(&block_contribution, inline_advance, flags);
1683
1684 let current_inline_box_identifier = self.current_inline_box_identifier();
1685 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1686 current_inline_box_identifier,
1687 TextRunLineItem {
1688 text: vec![glyph_store],
1689 text_fragment_run_data: text_run.run_data.clone(),
1690 base_fragment_info: text_run.base_fragment_info,
1691 info: info.clone(),
1692 character_range_in_dom_node: character_range,
1693 is_empty_for_text_cursor: false,
1694 },
1695 ));
1696 }
1697
1698 fn possibly_push_empty_text_run_to_line_for_text_caret(&mut self) {
1701 let Some(caret_placeholder) = self.current_line.caret_placeholder.take() else {
1702 return;
1703 };
1704
1705 if self
1707 .current_line
1708 .line_items
1709 .iter()
1710 .rev()
1711 .find(|line_item| line_item.is_in_flow_content())
1712 .is_some_and(|line_item| matches!(line_item, LineItem::TextRun(..)))
1713 {
1714 return;
1715 }
1716
1717 let inline_container_state = self.current_inline_container_state();
1718 let Some(font) = inline_container_state.default_font.clone() else {
1719 return;
1720 };
1721
1722 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1723 self.current_inline_box_identifier(),
1724 TextRunLineItem {
1725 text: Default::default(),
1726 text_fragment_run_data: caret_placeholder.run_data,
1727 base_fragment_info: caret_placeholder.base_fragment_info,
1728 info: FontAndScriptInfo::simple_for_font(font),
1729 character_range_in_dom_node: Utf32CodeUnits(caret_placeholder.character_index)..
1730 Utf32CodeUnits(caret_placeholder.character_index + 1),
1731 is_empty_for_text_cursor: true,
1732 },
1733 ));
1734 self.current_line_segment.has_content = true;
1735 self.commit_current_segment_to_line();
1736 }
1737
1738 fn update_unbreakable_segment_for_new_content(
1739 &mut self,
1740 block_sizes_of_content: &LineBlockSizes,
1741 inline_size: Au,
1742 flags: SegmentContentFlags,
1743 ) {
1744 if flags.is_collapsible_whitespace() || flags.is_wrappable_and_hangable() {
1745 self.current_line_segment.trailing_whitespace_size = inline_size;
1746 } else {
1747 self.current_line_segment.trailing_whitespace_size = Au::zero();
1748 }
1749 if !flags.is_collapsible_whitespace() {
1750 self.current_line_segment.has_content = true;
1751 }
1752
1753 let container_max_block_size = &self
1755 .current_inline_container_state()
1756 .nested_strut_block_sizes
1757 .clone();
1758 self.current_line_segment
1759 .max_block_size
1760 .max_assign(container_max_block_size);
1761 self.current_line_segment
1762 .max_block_size
1763 .max_assign(block_sizes_of_content);
1764
1765 self.current_line_segment.inline_size += inline_size;
1766
1767 self.current_inline_container_state().has_content.set(true);
1769 self.propagate_current_nesting_level_white_space_style();
1770 }
1771
1772 fn process_line_break(&mut self, forced_line_break: bool, for_block_level: bool) {
1773 self.current_line_segment.trim_leading_whitespace();
1774 self.finish_current_line_and_reset(forced_line_break, for_block_level);
1775 }
1776
1777 fn potential_line_size(&self) -> LogicalVec2<Au> {
1778 LogicalVec2 {
1779 inline: self.current_line.inline_position + self.current_line_segment.inline_size,
1780 block: self
1781 .current_line_max_block_size_including_nested_containers()
1782 .max(&self.current_line_segment.max_block_size)
1783 .resolve(),
1784 }
1785 }
1786
1787 fn unbreakable_segment_fits_on_line(&mut self) -> bool {
1788 let potential_line_size_without_hanging_whitespace = self.potential_line_size() -
1789 LogicalVec2 {
1790 inline: self.current_line_segment.trailing_whitespace_size,
1791 block: Au::zero(),
1792 };
1793 !self.new_potential_line_size_causes_line_break(
1794 &potential_line_size_without_hanging_whitespace,
1795 )
1796 }
1797
1798 fn process_soft_wrap_opportunity(&mut self) {
1802 if self.current_line_segment.line_items.is_empty() {
1803 return;
1804 }
1805 if self.text_wrap_mode == TextWrapMode::Nowrap {
1806 return;
1807 }
1808 if !self.unbreakable_segment_fits_on_line() {
1809 self.process_line_break(
1810 false, false, );
1813 }
1814 self.commit_current_segment_to_line();
1815 }
1816
1817 fn commit_current_segment_to_line(&mut self) {
1820 if self.current_line_segment.line_items.is_empty() && !self.current_line_segment.has_content
1823 {
1824 return;
1825 }
1826
1827 if !self.current_line.has_content {
1828 self.current_line_segment.trim_leading_whitespace();
1829 }
1830
1831 self.current_line.inline_position += self.current_line_segment.inline_size;
1832 self.current_line.max_block_size = self
1833 .current_line_max_block_size_including_nested_containers()
1834 .max(&self.current_line_segment.max_block_size);
1835 let line_inline_size_without_trailing_whitespace =
1836 self.current_line.inline_position - self.current_line_segment.trailing_whitespace_size;
1837
1838 let mut segment_items = mem::take(&mut self.current_line_segment.line_items);
1840 for item in segment_items.iter_mut() {
1841 if let LineItem::Float(_, float_item) = item {
1842 self.place_float_line_item_for_commit_to_line(
1843 float_item,
1844 line_inline_size_without_trailing_whitespace,
1845 );
1846 }
1847 }
1848
1849 if self.current_line.line_items.is_empty() {
1854 let will_break = self.new_potential_line_size_causes_line_break(&LogicalVec2 {
1855 inline: line_inline_size_without_trailing_whitespace,
1856 block: self.current_line_segment.max_block_size.resolve(),
1857 });
1858 assert!(!will_break);
1859 }
1860
1861 self.current_line.line_items.extend(segment_items);
1862 self.current_line.has_content |= self.current_line_segment.has_content;
1863 self.current_line.has_inline_pbm |= self.current_line_segment.has_inline_pbm;
1864
1865 self.current_line_segment.reset();
1866 }
1867
1868 #[inline]
1869 fn containing_block(&self) -> &ContainingBlock<'_> {
1870 self.placement_state.containing_block
1871 }
1872}
1873
1874bitflags! {
1875 struct SegmentContentFlags: u8 {
1876 const COLLAPSIBLE_WHITESPACE = 0b00000001;
1877 const WRAPPABLE_AND_HANGABLE_WHITESPACE = 0b00000010;
1878 }
1879}
1880
1881impl SegmentContentFlags {
1882 fn is_collapsible_whitespace(&self) -> bool {
1883 self.contains(Self::COLLAPSIBLE_WHITESPACE)
1884 }
1885
1886 fn is_wrappable_and_hangable(&self) -> bool {
1887 self.contains(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE)
1888 }
1889}
1890
1891impl From<&InheritedText> for SegmentContentFlags {
1892 fn from(style_text: &InheritedText) -> Self {
1893 let mut flags = Self::empty();
1894
1895 if !matches!(
1898 style_text.white_space_collapse,
1899 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
1900 ) {
1901 flags.insert(Self::COLLAPSIBLE_WHITESPACE);
1902 }
1903
1904 if style_text.text_wrap_mode == TextWrapMode::Wrap &&
1907 style_text.white_space_collapse != WhiteSpaceCollapse::BreakSpaces
1908 {
1909 flags.insert(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE);
1910 }
1911 flags
1912 }
1913}
1914
1915impl InlineFormattingContext {
1916 #[servo_tracing::instrument(name = "InlineFormattingContext::new_with_builder", skip_all)]
1917 fn new_with_builder(
1918 mut builder: InlineFormattingContextBuilder,
1919 layout_context: &LayoutContext,
1920 has_first_formatted_line: bool,
1921 is_single_line_text_input: bool,
1922 starting_bidi_level: Level,
1923 ) -> Self {
1924 let text_content: String = builder.text_segments.into_iter().collect();
1926
1927 let bidi_levels = BidiLevels {
1928 info: builder
1929 .has_right_to_left_content
1930 .then(|| BidiInfo::new(&text_content, Some(starting_bidi_level))),
1931 };
1932
1933 let shared_inline_styles = builder
1934 .shared_inline_styles_stack
1935 .last()
1936 .expect("Should have at least one SharedInlineStyle for the root of an IFC")
1937 .clone();
1938 let (word_break, line_break, lang) = {
1939 let styles = shared_inline_styles.style.borrow();
1940 let text_style = styles.get_inherited_text();
1941 (
1942 text_style.word_break,
1943 text_style.line_break,
1944 styles.get_font()._x_lang.clone(),
1945 )
1946 };
1947
1948 let mut options = LineBreakOptions::default();
1949
1950 options.strictness = match line_break {
1951 LineBreak::Loose => LineBreakStrictness::Loose,
1952 LineBreak::Normal => LineBreakStrictness::Normal,
1953 LineBreak::Strict => LineBreakStrictness::Strict,
1954 LineBreak::Anywhere => LineBreakStrictness::Anywhere,
1955 LineBreak::Auto => LineBreakStrictness::Normal,
1958 };
1959 options.word_option = match word_break {
1960 WordBreak::Normal => LineBreakWordOption::Normal,
1961 WordBreak::BreakAll => LineBreakWordOption::BreakAll,
1962 WordBreak::KeepAll => LineBreakWordOption::KeepAll,
1963 };
1964 options.ja_zh = {
1967 lang.0.parse::<LanguageIdentifier>().is_ok_and(|lang_id| {
1968 const JA: Language = language!("ja");
1969 const ZH: Language = language!("zh");
1970 matches!(lang_id.language, JA | ZH)
1971 })
1972 };
1973
1974 let mut shaping_queue = ShapingQueue::new(&text_content, options);
1975 for item in &mut builder.inline_items {
1976 match item {
1977 InlineItem::TextRun(text_run) => {
1978 let shaping_queue_entries = text_run.borrow_mut().segment(
1979 text_run.clone(),
1980 &text_content,
1981 layout_context,
1982 &bidi_levels,
1983 );
1984 for entry in shaping_queue_entries.into_iter() {
1985 shaping_queue.push(entry);
1986 }
1987 },
1988 InlineItem::StartInlineBox(inline_box) => {
1989 let inline_box = &mut *inline_box.borrow_mut();
1990 if let Some(font) = get_font_for_first_font_for_style(
1991 &inline_box.base.style,
1992 &layout_context.font_context,
1993 ) {
1994 inline_box.default_font = Some(font);
1995 }
1996
1997 if inline_box.breaks_shaping_at_start {
1998 shaping_queue.flush();
1999 }
2000 },
2001 InlineItem::Atomic(_, index_in_text, bidi_level) => {
2002 shaping_queue.flush();
2003 *bidi_level = bidi_levels.level(*index_in_text);
2004 },
2005 InlineItem::EndInlineBox(inline_box) => {
2006 if inline_box.borrow().breaks_shaping_at_end {
2007 shaping_queue.flush();
2008 }
2009 },
2010 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) |
2011 InlineItem::OutOfFlowFloatBox(_) |
2012 InlineItem::BlockLevel { .. } => {},
2013 }
2014 }
2015
2016 shaping_queue.flush();
2017
2018 let default_font = get_font_for_first_font_for_style(
2019 &shared_inline_styles.style.borrow(),
2020 &layout_context.font_context,
2021 );
2022
2023 let has_right_to_left_content = bidi_levels.info.as_ref().is_some_and(BidiInfo::has_rtl);
2024 InlineFormattingContext {
2025 text_content,
2026 inline_items: builder.inline_items,
2027 inline_boxes: builder.inline_boxes,
2028 shared_inline_styles,
2029 default_font,
2030 has_first_formatted_line,
2031 contains_floats: builder.contains_floats,
2032 is_single_line_text_input,
2033 has_right_to_left_content,
2034 tab_size_multiplier: Default::default(),
2035 }
2036 }
2037
2038 pub(crate) fn repair_style(
2039 &self,
2040 context: &SharedStyleContext,
2041 node: &ServoLayoutNode,
2042 new_style: &ServoArc<ComputedValues>,
2043 ) {
2044 *self.shared_inline_styles.style.borrow_mut() = new_style.clone();
2045 *self.shared_inline_styles.selected.borrow_mut() = node.selected_style(context);
2046 }
2047
2048 fn inline_start_for_first_line(&self, containing_block: IndefiniteContainingBlock) -> Au {
2049 if !self.has_first_formatted_line {
2050 return Au::zero();
2051 }
2052 containing_block
2053 .style
2054 .get_inherited_text()
2055 .text_indent
2056 .length
2057 .to_used_value(containing_block.size.inline.unwrap_or_default())
2058 }
2059
2060 pub(super) fn layout(
2061 &self,
2062 layout_context: &LayoutContext,
2063 positioning_context: &mut PositioningContext,
2064 containing_block: &ContainingBlock,
2065 sequential_layout_state: Option<&mut SequentialLayoutState>,
2066 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
2067 ignore_block_margins_for_stretch: LogicalSides1D<bool>,
2068 ) -> IndependentFormattingContextLayoutResult {
2069 for inline_box in self.inline_boxes.iter() {
2071 inline_box.borrow().base.clear_fragments();
2072 }
2073
2074 let style = containing_block.style;
2075
2076 let style_text = containing_block.style.get_inherited_text();
2077 let mut inline_container_state_flags = InlineContainerStateFlags::empty();
2078 if inline_container_needs_strut(style, layout_context, None) {
2079 inline_container_state_flags.insert(InlineContainerStateFlags::CREATE_STRUT);
2080 }
2081 if self.is_single_line_text_input {
2082 inline_container_state_flags
2083 .insert(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT);
2084 }
2085 let placement_state =
2086 PlacementState::new(collapsible_with_parent_start_margin, containing_block);
2087
2088 let mut layout = InlineFormattingContextLayout {
2089 positioning_context,
2090 placement_state,
2091 sequential_layout_state,
2092 layout_context,
2093 ifc: self,
2094 fragments: Vec::new(),
2095 current_line: LineUnderConstruction::new(LogicalVec2 {
2096 inline: self.inline_start_for_first_line(containing_block.into()),
2097 block: Au::zero(),
2098 }),
2099 root_nesting_level: InlineContainerState::new(
2100 style.to_arc(),
2101 inline_container_state_flags,
2102 None, self.default_font.clone(),
2104 ),
2105 inline_box_state_stack: Vec::new(),
2106 cloneable_inline_box_end_pbm_size: Au::zero(),
2107 inline_box_states: Vec::with_capacity(self.inline_boxes.len()),
2108 current_line_segment: UnbreakableSegmentUnderConstruction::new(),
2109 force_line_break_before_new_content: false,
2110 caret_placeholder: None,
2111 deferred_br_clear: Clear::None,
2112 have_deferred_soft_wrap_opportunity: false,
2113 depends_on_block_constraints: false,
2114 white_space_collapse: style_text.white_space_collapse,
2115 text_wrap_mode: style_text.text_wrap_mode,
2116 ignore_block_margins_for_stretch,
2117 };
2118
2119 for item in self.inline_items.iter() {
2120 if !matches!(item, InlineItem::EndInlineBox(..)) {
2122 layout.possibly_flush_deferred_forced_line_break();
2123 }
2124
2125 match item {
2126 InlineItem::StartInlineBox(inline_box) => {
2127 layout.start_inline_box(&inline_box.borrow());
2128 },
2129 InlineItem::EndInlineBox(..) => layout.finish_inline_box(),
2130 InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout),
2131 InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
2132 atomic_formatting_context.borrow().layout_into_line_items(
2133 &mut layout,
2134 *offset_in_text,
2135 *bidi_level,
2136 );
2137 },
2138 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, _) => {
2139 layout.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
2140 layout.current_inline_box_identifier(),
2141 AbsolutelyPositionedLineItem {
2142 absolutely_positioned_box: positioned_box.clone(),
2143 preceding_line_content_would_produce_phantom_line: layout
2144 .current_line
2145 .is_phantom() &&
2146 layout.current_line_segment.is_phantom(),
2147 },
2148 ));
2149 },
2150 InlineItem::OutOfFlowFloatBox(float_box) => {
2151 float_box.borrow().layout_into_line_items(&mut layout);
2152 },
2153 InlineItem::BlockLevel(block_level) => {
2154 block_level.borrow().layout_into_line_items(&mut layout);
2155 },
2156 }
2157 }
2158
2159 layout.finish_last_line();
2160 let (content_block_size, collapsible_margins_in_children, baselines) =
2161 layout.placement_state.finish();
2162
2163 IndependentFormattingContextLayoutResult {
2164 fragments: layout.fragments,
2165 content_block_size,
2166 collapsible_margins_in_children,
2167 baselines,
2168 depends_on_block_constraints: layout.depends_on_block_constraints,
2169 content_inline_size_for_table: None,
2170 specific_layout_info: None,
2171 }
2172 }
2173
2174 pub(crate) fn subtree_size(&self) -> usize {
2175 self.inline_items
2176 .iter()
2177 .map(|item| match item {
2178 InlineItem::StartInlineBox(..) => 1,
2179 InlineItem::EndInlineBox(..) => 0,
2180 InlineItem::TextRun(..) => 1,
2181 InlineItem::OutOfFlowAbsolutelyPositionedBox(absolutely_positioned_box, _) => {
2182 absolutely_positioned_box
2183 .borrow()
2184 .context
2185 .base
2186 .subtree_size()
2187 },
2188 InlineItem::OutOfFlowFloatBox(..) => 1,
2189 InlineItem::Atomic(..) => 1,
2190 InlineItem::BlockLevel(block_level_box) => block_level_box.borrow().subtree_size(),
2191 })
2192 .sum()
2193 }
2194
2195 fn next_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
2196 let Some(character) = self.text_content[index..].chars().nth(1) else {
2197 return false;
2198 };
2199 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2200 }
2201
2202 fn previous_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
2203 let Some(character) = self.text_content[0..index].chars().next_back() else {
2204 return false;
2205 };
2206 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2207 }
2208
2209 pub(crate) fn find_block_margin_collapsing_with_parent(
2210 &self,
2211 layout_context: &LayoutContext,
2212 collected_margin: &mut CollapsedMargin,
2213 containing_block_for_children: &ContainingBlock,
2214 ) -> bool {
2215 let mut items_iter = self.inline_items.iter();
2221 items_iter.all(|inline_item| match inline_item {
2222 InlineItem::StartInlineBox(inline_box) => {
2223 let pbm = inline_box
2224 .borrow()
2225 .layout_style()
2226 .padding_border_margin(containing_block_for_children);
2227 pbm.padding.inline_start.is_zero() &&
2228 pbm.border.inline_start.is_zero() &&
2229 pbm.margin.inline_start.auto_is(Au::zero).is_zero()
2230 },
2231 InlineItem::EndInlineBox(inline_box) => {
2232 let pbm = inline_box
2233 .borrow()
2234 .layout_style()
2235 .padding_border_margin(containing_block_for_children);
2236 pbm.padding.inline_end.is_zero() &&
2237 pbm.border.inline_end.is_zero() &&
2238 pbm.margin.inline_end.auto_is(Au::zero).is_zero()
2239 },
2240 InlineItem::TextRun(text_run) => {
2241 let text_run = &*text_run.borrow();
2242 let parent_style = text_run.inline_styles().style.borrow();
2243 text_run.items.iter().all(|item| match item {
2244 TextRunItem::LineBreak { .. } => false,
2245 TextRunItem::Tab { .. } => false,
2246 TextRunItem::TextSegment(segment) => segment.runs.iter().all(|run| {
2247 run.is_whitespace() &&
2248 !matches!(
2249 parent_style.get_inherited_text().white_space_collapse,
2250 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
2251 )
2252 }),
2253 })
2254 },
2255 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => true,
2256 InlineItem::OutOfFlowFloatBox(..) => true,
2257 InlineItem::Atomic(..) => false,
2258 InlineItem::BlockLevel(block_level) => block_level
2259 .borrow()
2260 .find_block_margin_collapsing_with_parent(
2261 layout_context,
2262 collected_margin,
2263 containing_block_for_children,
2264 ),
2265 })
2266 }
2267
2268 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
2269 let mut parent_box_stack = Vec::new();
2270 let current_parent_box = |parent_box_stack: &[WeakLayoutBox]| {
2271 parent_box_stack.last().unwrap_or(&layout_box).clone()
2272 };
2273 for inline_item in &self.inline_items {
2274 match inline_item {
2275 InlineItem::StartInlineBox(inline_box) => {
2276 inline_box
2277 .borrow_mut()
2278 .base
2279 .parent_box
2280 .replace(current_parent_box(&parent_box_stack));
2281 parent_box_stack.push(WeakLayoutBox::InlineLevel(
2282 WeakInlineItem::StartInlineBox(inline_box.downgrade()),
2283 ));
2284 },
2285 InlineItem::EndInlineBox(..) => {
2286 parent_box_stack.pop();
2287 },
2288 InlineItem::TextRun(text_run) => {
2289 text_run
2290 .borrow_mut()
2291 .parent_box
2292 .replace(current_parent_box(&parent_box_stack));
2293 },
2294 _ => inline_item.with_base_mut(|base| {
2295 base.parent_box
2296 .replace(current_parent_box(&parent_box_stack));
2297 }),
2298 }
2299 }
2300 }
2301
2302 pub(crate) fn next_tab_stop_after_inline_advance(
2303 &self,
2304 style: &ServoArc<ComputedValues>,
2305 current_inline_advance: Au,
2306 ) -> Au {
2307 let Some(font) = self.default_font.as_ref() else {
2308 return Au::zero();
2309 };
2310
2311 let tab_size_multiplier = *self.tab_size_multiplier.get_or_init(|| {
2312 let root_style = self.shared_inline_styles.style.borrow();
2313 let inherited_text_style = root_style.get_inherited_text();
2314 let font_size = root_style.get_font().font_size.computed_size().into();
2315 let letter_spacing = inherited_text_style
2316 .letter_spacing
2317 .0
2318 .to_used_value(font_size);
2319 let word_spacing = inherited_text_style.word_spacing.to_used_value(font_size);
2320
2321 font.metrics.space_advance + word_spacing + letter_spacing
2324 });
2325
2326 let tab_stop_advance = match style.get_inherited_text().tab_size {
2327 style::values::generics::length::LengthOrNumber::Number(number_of_spaces) => {
2328 tab_size_multiplier.scale_by(number_of_spaces.0)
2329 },
2330 style::values::generics::length::LengthOrNumber::Length(length) => length.into(),
2332 };
2333
2334 if tab_stop_advance.is_zero() {
2335 return Au::zero();
2336 }
2337
2338 let half_ch_advance = font
2344 .metrics
2345 .zero_horizontal_advance
2346 .unwrap_or(font.metrics.em_size.scale_by(0.5))
2347 .scale_by(0.5);
2348 let number_of_tab_stops =
2349 (current_inline_advance + half_ch_advance).to_f32_px() / tab_stop_advance.to_f32_px();
2350 let number_of_tab_stops = number_of_tab_stops.ceil();
2351 tab_stop_advance.scale_by(number_of_tab_stops) - current_inline_advance
2352 }
2353}
2354
2355impl InlineContainerState {
2356 fn new(
2357 style: ServoArc<ComputedValues>,
2358 flags: InlineContainerStateFlags,
2359 parent_container: Option<&InlineContainerState>,
2360 default_font: Option<FontRef>,
2361 ) -> Self {
2362 let font_metrics = default_font
2363 .as_ref()
2364 .map(|font| font.metrics.clone())
2365 .unwrap_or_else(FontMetrics::empty);
2366 let mut baseline_offset = Au::zero();
2367 let mut strut_block_sizes = {
2368 Self::get_block_sizes_with_style(
2369 effective_baseline_shift(&style, parent_container),
2370 &style,
2371 &font_metrics,
2372 &font_metrics,
2373 &flags,
2374 )
2375 };
2376
2377 if let Some(parent_container) = parent_container {
2378 baseline_offset = parent_container.get_cumulative_baseline_offset_for_child(
2381 style.clone_alignment_baseline(),
2382 style.clone_baseline_shift(),
2383 &strut_block_sizes,
2384 );
2385 strut_block_sizes.adjust_for_baseline_offset(baseline_offset);
2386 }
2387
2388 let mut nested_block_sizes = parent_container
2389 .map(|container| container.nested_strut_block_sizes.clone())
2390 .unwrap_or_else(LineBlockSizes::zero);
2391 if flags.contains(InlineContainerStateFlags::CREATE_STRUT) {
2392 nested_block_sizes.max_assign(&strut_block_sizes);
2393 }
2394
2395 Self {
2396 style,
2397 flags,
2398 has_content: Cell::new(false),
2399 nested_strut_block_sizes: nested_block_sizes,
2400 strut_block_sizes,
2401 baseline_offset,
2402 default_font,
2403 font_metrics,
2404 }
2405 }
2406
2407 fn get_block_sizes_with_style(
2408 baseline_shift: BaselineShift,
2409 style: &ComputedValues,
2410 font_metrics: &FontMetrics,
2411 font_metrics_of_first_font: &FontMetrics,
2412 flags: &InlineContainerStateFlags,
2413 ) -> LineBlockSizes {
2414 let line_height = line_height(style, font_metrics, flags);
2415
2416 if !is_baseline_relative(baseline_shift) {
2417 return LineBlockSizes {
2418 line_height,
2419 baseline_relative_size_for_line_height: None,
2420 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2421 };
2422 }
2423
2424 let mut ascent = font_metrics.ascent;
2433 let mut descent = font_metrics.descent;
2434 if style.get_font().line_height == LineHeight::Normal {
2435 let half_leading_from_line_gap =
2436 (font_metrics.line_gap - descent - ascent).scale_by(0.5);
2437 ascent += half_leading_from_line_gap;
2438 descent += half_leading_from_line_gap;
2439 }
2440
2441 let size_for_baseline_positioning = BaselineRelativeSize { ascent, descent };
2445
2446 if style.get_font().line_height != LineHeight::Normal {
2462 ascent = font_metrics_of_first_font.ascent;
2463 descent = font_metrics_of_first_font.descent;
2464 let half_leading = (line_height - (ascent + descent)).scale_by(0.5);
2465 ascent += half_leading;
2470 descent = line_height - ascent;
2471 }
2472
2473 LineBlockSizes {
2474 line_height,
2475 baseline_relative_size_for_line_height: Some(BaselineRelativeSize { ascent, descent }),
2476 size_for_baseline_positioning,
2477 }
2478 }
2479
2480 fn get_block_size_contribution(
2481 &self,
2482 baseline_shift: BaselineShift,
2483 font_metrics: &FontMetrics,
2484 font_metrics_of_first_font: &FontMetrics,
2485 ) -> LineBlockSizes {
2486 Self::get_block_sizes_with_style(
2487 baseline_shift,
2488 &self.style,
2489 font_metrics,
2490 font_metrics_of_first_font,
2491 &self.flags,
2492 )
2493 }
2494
2495 fn get_cumulative_baseline_offset_for_child(
2496 &self,
2497 child_alignment_baseline: AlignmentBaseline,
2498 child_baseline_shift: BaselineShift,
2499 child_block_size: &LineBlockSizes,
2500 ) -> Au {
2501 let block_size = self.get_block_size_contribution(
2502 child_baseline_shift.clone(),
2503 &self.font_metrics,
2504 &self.font_metrics,
2505 );
2506 self.baseline_offset +
2507 match child_alignment_baseline {
2508 AlignmentBaseline::Baseline => Au::zero(),
2509 AlignmentBaseline::TextTop => {
2510 child_block_size.size_for_baseline_positioning.ascent - self.font_metrics.ascent
2511 },
2512 AlignmentBaseline::Middle => {
2513 (child_block_size.size_for_baseline_positioning.ascent -
2516 child_block_size.size_for_baseline_positioning.descent -
2517 self.font_metrics.x_height)
2518 .scale_by(0.5)
2519 },
2520 AlignmentBaseline::TextBottom => {
2521 self.font_metrics.descent -
2522 child_block_size.size_for_baseline_positioning.descent
2523 },
2524 } +
2525 match child_baseline_shift {
2526 BaselineShift::Keyword(
2531 BaselineShiftKeyword::Top |
2532 BaselineShiftKeyword::Bottom |
2533 BaselineShiftKeyword::Center,
2534 ) => Au::zero(),
2535 BaselineShift::Keyword(BaselineShiftKeyword::Sub) => {
2536 block_size.resolve().scale_by(FONT_SUBSCRIPT_OFFSET_RATIO)
2537 },
2538 BaselineShift::Keyword(BaselineShiftKeyword::Super) => {
2539 -block_size.resolve().scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO)
2540 },
2541 BaselineShift::Length(length_percentage) => {
2542 -length_percentage.to_used_value(child_block_size.line_height)
2543 },
2544 }
2545 }
2546}
2547
2548impl IndependentFormattingContext {
2549 fn layout_into_line_items(
2550 &self,
2551 layout: &mut InlineFormattingContextLayout,
2552 offset_in_text: usize,
2553 bidi_level: Level,
2554 ) {
2555 let mut child_positioning_context = PositioningContext::default();
2557 let IndependentFloatOrAtomicLayoutResult {
2558 mut fragment,
2559 baselines,
2560 pbm_sums,
2561 } = self.layout_float_or_atomic_inline(
2562 layout.layout_context,
2563 &mut child_positioning_context,
2564 layout.containing_block(),
2565 );
2566
2567 layout.depends_on_block_constraints |= fragment.base.flags.contains(
2570 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2571 );
2572
2573 let container_writing_mode = layout.containing_block().style.writing_mode;
2575 let pbm_physical_offset = pbm_sums
2576 .start_offset()
2577 .to_physical_size(container_writing_mode);
2578 fragment.base.translate_rect(pbm_physical_offset);
2579
2580 fragment = fragment.with_baselines(baselines);
2582
2583 let positioning_context = if self.is_replaced() {
2586 None
2587 } else {
2588 if fragment
2589 .style()
2590 .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
2591 {
2592 child_positioning_context
2593 .layout_collected_children(layout.layout_context, &mut fragment);
2594 }
2595 Some(child_positioning_context)
2596 };
2597
2598 if layout.text_wrap_mode == TextWrapMode::Wrap &&
2599 !layout
2600 .ifc
2601 .previous_character_prevents_soft_wrap_opportunity(offset_in_text)
2602 {
2603 layout.process_soft_wrap_opportunity();
2604 }
2605
2606 let size = pbm_sums.sum() + fragment.base.rect().size.to_logical(container_writing_mode);
2607 let baseline_offset = self
2608 .pick_baseline(&fragment.baselines(container_writing_mode))
2609 .map(|baseline| pbm_sums.block_start + baseline)
2610 .unwrap_or(size.block);
2611
2612 let (block_sizes, baseline_offset_in_parent) =
2613 self.get_block_sizes_and_baseline_offset(layout, size.block, baseline_offset);
2614 layout.update_unbreakable_segment_for_new_content(
2615 &block_sizes,
2616 size.inline,
2617 SegmentContentFlags::empty(),
2618 );
2619
2620 let fragment = Arc::new(fragment);
2621 self.base.set_fragment(Fragment::Box(fragment.clone()));
2622
2623 layout.push_line_item_to_unbreakable_segment(LineItem::Atomic(
2624 layout.current_inline_box_identifier(),
2625 AtomicLineItem {
2626 fragment,
2627 size,
2628 positioning_context,
2629 baseline_offset_in_parent,
2630 baseline_offset_in_item: baseline_offset,
2631 bidi_level,
2632 },
2633 ));
2634
2635 if !layout
2638 .ifc
2639 .next_character_prevents_soft_wrap_opportunity(offset_in_text)
2640 {
2641 layout.have_deferred_soft_wrap_opportunity = true;
2642 }
2643 }
2644
2645 fn pick_baseline(&self, baselines: &Baselines) -> Option<Au> {
2649 match self.style().clone_baseline_source() {
2650 BaselineSource::First => baselines.first,
2651 BaselineSource::Last => baselines.last,
2652 BaselineSource::Auto if self.is_block_container() => baselines.last,
2653 BaselineSource::Auto => baselines.first,
2654 }
2655 }
2656
2657 fn get_block_sizes_and_baseline_offset(
2658 &self,
2659 ifc: &InlineFormattingContextLayout,
2660 block_size: Au,
2661 baseline_offset_in_content_area: Au,
2662 ) -> (LineBlockSizes, Au) {
2663 let mut contribution = if !is_baseline_relative(self.style().clone_baseline_shift()) {
2664 LineBlockSizes {
2665 line_height: block_size,
2666 baseline_relative_size_for_line_height: None,
2667 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2668 }
2669 } else {
2670 let baseline_relative_size = BaselineRelativeSize {
2671 ascent: baseline_offset_in_content_area,
2672 descent: block_size - baseline_offset_in_content_area,
2673 };
2674 LineBlockSizes {
2675 line_height: block_size,
2676 baseline_relative_size_for_line_height: Some(baseline_relative_size.clone()),
2677 size_for_baseline_positioning: baseline_relative_size,
2678 }
2679 };
2680
2681 let style = self.style();
2682 let baseline_offset = ifc
2683 .current_inline_container_state()
2684 .get_cumulative_baseline_offset_for_child(
2685 style.clone_alignment_baseline(),
2686 style.clone_baseline_shift(),
2687 &contribution,
2688 );
2689 contribution.adjust_for_baseline_offset(baseline_offset);
2690
2691 (contribution, baseline_offset)
2692 }
2693}
2694
2695impl FloatBox {
2696 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
2697 let old_len = layout.positioning_context.len();
2698 let fragment = Arc::new(self.layout(
2699 layout.layout_context,
2700 layout.positioning_context,
2701 layout.placement_state.containing_block,
2702 ));
2703 let new_len = layout.positioning_context.len();
2704
2705 self.contents
2706 .base
2707 .set_fragment(Fragment::Box(fragment.clone()));
2708 layout.push_line_item_to_unbreakable_segment(LineItem::Float(
2709 layout.current_inline_box_identifier(),
2710 FloatLineItem {
2711 fragment,
2712 needs_placement: true,
2713 range: old_len..new_len,
2714 },
2715 ));
2716 }
2717}
2718
2719fn place_pending_floats(ifc: &mut InlineFormattingContextLayout, line_items: &[LineItem]) {
2720 for item in line_items.iter() {
2721 if let LineItem::Float(_, float_line_item) = item &&
2722 float_line_item.needs_placement
2723 {
2724 ifc.place_float_fragment(float_line_item);
2725 }
2726 }
2727}
2728
2729fn line_height(
2730 parent_style: &ComputedValues,
2731 font_metrics: &FontMetrics,
2732 flags: &InlineContainerStateFlags,
2733) -> Au {
2734 let font = parent_style.get_font();
2735 let font_size = font.font_size.computed_size();
2736 let mut line_height = match font.line_height {
2737 LineHeight::Normal => font_metrics.line_gap,
2738 LineHeight::Number(number) => (font_size * number.0).into(),
2739 LineHeight::Length(length) => length.0.into(),
2740 };
2741
2742 if flags.contains(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT) {
2746 line_height.max_assign(font_metrics.line_gap);
2747 }
2748
2749 line_height
2750}
2751
2752fn effective_baseline_shift(
2753 style: &ComputedValues,
2754 container: Option<&InlineContainerState>,
2755) -> BaselineShift {
2756 if container.is_none() {
2757 BaselineShift::zero()
2761 } else {
2762 style.clone_baseline_shift()
2763 }
2764}
2765
2766fn is_baseline_relative(baseline_shift: BaselineShift) -> bool {
2767 !matches!(
2768 baseline_shift,
2769 BaselineShift::Keyword(
2770 BaselineShiftKeyword::Top | BaselineShiftKeyword::Bottom | BaselineShiftKeyword::Center
2771 )
2772 )
2773}
2774
2775fn inline_container_needs_strut(
2801 style: &ComputedValues,
2802 layout_context: &LayoutContext,
2803 pbm: Option<&PaddingBorderMargin>,
2804) -> bool {
2805 if layout_context.style_context.quirks_mode() == QuirksMode::NoQuirks {
2806 return true;
2807 }
2808
2809 if style.get_box().display.is_list_item() {
2812 return true;
2813 }
2814
2815 pbm.is_some_and(|pbm| !pbm.padding_border_sums.inline.is_zero())
2816}
2817
2818impl ComputeInlineContentSizes for InlineFormattingContext {
2819 fn compute_inline_content_sizes(
2823 &self,
2824 layout_context: &LayoutContext,
2825 constraint_space: &ConstraintSpace,
2826 ) -> InlineContentSizesResult {
2827 ContentSizesComputation::compute(self, layout_context, constraint_space)
2828 }
2829}
2830
2831struct ContentSizesComputation<'layout_data> {
2833 layout_context: &'layout_data LayoutContext<'layout_data>,
2834 constraint_space: &'layout_data ConstraintSpace<'layout_data>,
2835 paragraph: ContentSizes,
2836 current_line: ContentSizes,
2837 pending_whitespace: ContentSizes,
2839 uncleared_floats: LogicalSides1D<ContentSizes>,
2841 cleared_floats: LogicalSides1D<ContentSizes>,
2843 had_content_yet_for_min_content: bool,
2846 had_content_yet_for_max_content: bool,
2849 ending_inline_pbm_stack: Vec<Au>,
2852 depends_on_block_constraints: bool,
2854}
2855
2856impl<'layout_data> ContentSizesComputation<'layout_data> {
2857 fn traverse(
2858 mut self,
2859 inline_formatting_context: &InlineFormattingContext,
2860 ) -> InlineContentSizesResult {
2861 self.add_inline_size(
2862 inline_formatting_context.inline_start_for_first_line(self.constraint_space.into()),
2863 );
2864 for inline_item in &inline_formatting_context.inline_items {
2865 self.process_item(inline_item, inline_formatting_context);
2866 }
2867 self.forced_line_break();
2868 self.flush_floats();
2869
2870 InlineContentSizesResult {
2871 sizes: self.paragraph,
2872 depends_on_block_constraints: self.depends_on_block_constraints,
2873 }
2874 }
2875
2876 fn process_item(
2877 &mut self,
2878 inline_item: &InlineItem,
2879 inline_formatting_context: &InlineFormattingContext,
2880 ) {
2881 match inline_item {
2882 InlineItem::StartInlineBox(inline_box) => {
2883 let inline_box = inline_box.borrow();
2887 let zero = Au::zero();
2888 let writing_mode = self.constraint_space.style.writing_mode;
2889 let layout_style = inline_box.layout_style();
2890 let padding = layout_style
2891 .padding(writing_mode)
2892 .percentages_relative_to(zero);
2893 let border = layout_style.border_width(writing_mode);
2894 let margin = inline_box
2895 .base
2896 .style
2897 .margin(writing_mode)
2898 .percentages_relative_to(zero)
2899 .auto_is(Au::zero);
2900
2901 let pbm = margin + padding + border;
2902 self.add_inline_size(pbm.inline_start);
2903 self.ending_inline_pbm_stack.push(pbm.inline_end);
2904 },
2905 InlineItem::EndInlineBox(..) => {
2906 let length = self.ending_inline_pbm_stack.pop().unwrap_or_else(Au::zero);
2907 self.add_inline_size(length);
2908 },
2909 InlineItem::TextRun(text_run) => {
2910 let text_run = &*text_run.borrow();
2911 let parent_style = text_run.inline_styles().style.borrow();
2912 for item in text_run.items.iter() {
2913 match item {
2914 TextRunItem::LineBreak { .. } => {
2915 self.forced_line_break();
2918 },
2919 TextRunItem::Tab { .. } => {
2920 self.process_preserved_tab(&parent_style, inline_formatting_context)
2921 },
2922 TextRunItem::TextSegment(segment) => {
2923 self.process_text_segment(&parent_style, segment)
2924 },
2925 }
2926 }
2927 },
2928 InlineItem::Atomic(atomic, offset_in_text, _level) => {
2929 if self.had_content_yet_for_min_content &&
2931 !inline_formatting_context
2932 .previous_character_prevents_soft_wrap_opportunity(*offset_in_text)
2933 {
2934 self.line_break_opportunity();
2935 }
2936
2937 self.commit_pending_whitespace();
2938 let outer = self.outer_inline_content_sizes_of_float_or_atomic(&atomic.borrow());
2939 self.current_line += outer;
2940
2941 if !inline_formatting_context
2943 .next_character_prevents_soft_wrap_opportunity(*offset_in_text)
2944 {
2945 self.line_break_opportunity();
2946 }
2947 },
2948 InlineItem::OutOfFlowFloatBox(float_box) => {
2949 let float_box = float_box.borrow();
2950 let sizes = self.outer_inline_content_sizes_of_float_or_atomic(&float_box.contents);
2951 let style = &float_box.contents.style();
2952 let container_writing_mode = self.constraint_space.style.writing_mode;
2953 let clear =
2954 Clear::from_style_and_container_writing_mode(style, container_writing_mode);
2955 self.clear_floats(clear);
2956 let float_side =
2957 FloatSide::from_style_and_container_writing_mode(style, container_writing_mode);
2958 match float_side.expect("A float box needs to float to some side") {
2959 FloatSide::InlineStart => self.uncleared_floats.start.union_assign(&sizes),
2960 FloatSide::InlineEnd => self.uncleared_floats.end.union_assign(&sizes),
2961 }
2962 },
2963 InlineItem::BlockLevel(block_level) => {
2964 self.forced_line_break();
2965 self.flush_floats();
2966 let inline_content_sizes_result =
2967 compute_inline_content_sizes_for_block_level_boxes(
2968 std::slice::from_ref(block_level),
2969 self.layout_context,
2970 &self.constraint_space.into(),
2971 );
2972 self.depends_on_block_constraints |=
2973 inline_content_sizes_result.depends_on_block_constraints;
2974 self.current_line = inline_content_sizes_result.sizes;
2975 self.forced_line_break();
2976 },
2977 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => {},
2978 }
2979 }
2980
2981 fn process_text_segment(
2982 &mut self,
2983 parent_style: &AtomicRef<'_, ServoArc<ComputedValues>>,
2984 segment: &TextRunSegment,
2985 ) {
2986 let style_text = parent_style.get_inherited_text();
2987 let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap;
2988
2989 let break_at_start = segment.break_at_start && self.had_content_yet_for_min_content;
2992
2993 for (run_index, run) in segment.runs.iter().enumerate() {
2994 if can_wrap && (run_index != 0 || break_at_start) {
2997 self.line_break_opportunity();
2998 }
2999
3000 let advance = run.total_advance();
3001 if run.is_whitespace() {
3002 if !matches!(
3003 style_text.white_space_collapse,
3004 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
3005 ) {
3006 if self.had_content_yet_for_min_content {
3007 if can_wrap {
3008 self.line_break_opportunity();
3009 } else {
3010 self.pending_whitespace.min_content += advance;
3011 }
3012 }
3013 if self.had_content_yet_for_max_content {
3014 self.pending_whitespace.max_content += advance;
3015 }
3016 continue;
3017 }
3018 if can_wrap {
3019 self.pending_whitespace.max_content += advance;
3020 self.commit_pending_whitespace();
3021 self.line_break_opportunity();
3022 continue;
3023 }
3024 }
3025
3026 self.commit_pending_whitespace();
3027 self.add_inline_size(advance);
3028
3029 if can_wrap && run.ends_with_whitespace() {
3034 self.line_break_opportunity();
3035 }
3036 }
3037 }
3038
3039 fn process_preserved_tab(
3040 &mut self,
3041 parent_style: &AtomicRef<'_, ServoArc<ComputedValues>>,
3042 inline_formatting_context: &InlineFormattingContext,
3043 ) {
3044 self.commit_pending_whitespace();
3046
3047 self.current_line.min_content += inline_formatting_context
3048 .next_tab_stop_after_inline_advance(parent_style, self.current_line.min_content);
3049 self.current_line.max_content += inline_formatting_context
3050 .next_tab_stop_after_inline_advance(parent_style, self.current_line.max_content);
3051 if parent_style.get_inherited_text().text_wrap_mode == TextWrapMode::Wrap {
3052 self.line_break_opportunity();
3053 }
3054 }
3055
3056 fn add_inline_size(&mut self, l: Au) {
3057 self.current_line.min_content += l;
3058 self.current_line.max_content += l;
3059 }
3060
3061 fn line_break_opportunity(&mut self) {
3062 self.pending_whitespace.min_content = Au::zero();
3066 let current_min_content = mem::take(&mut self.current_line.min_content);
3067 self.paragraph.min_content.max_assign(current_min_content);
3068 self.had_content_yet_for_min_content = false;
3069 }
3070
3071 fn forced_line_break(&mut self) {
3072 self.line_break_opportunity();
3074
3075 self.pending_whitespace.max_content = Au::zero();
3077 let current_max_content = mem::take(&mut self.current_line.max_content);
3078 self.paragraph.max_content.max_assign(current_max_content);
3079 self.had_content_yet_for_max_content = false;
3080 }
3081
3082 fn commit_pending_whitespace(&mut self) {
3083 self.current_line += mem::take(&mut self.pending_whitespace);
3084 self.had_content_yet_for_min_content = true;
3085 self.had_content_yet_for_max_content = true;
3086 }
3087
3088 fn outer_inline_content_sizes_of_float_or_atomic(
3089 &mut self,
3090 context: &IndependentFormattingContext,
3091 ) -> ContentSizes {
3092 let result = context.outer_inline_content_sizes(
3093 self.layout_context,
3094 &self.constraint_space.into(),
3095 &LogicalVec2::zero(),
3096 false, );
3098 self.depends_on_block_constraints |= result.depends_on_block_constraints;
3099 result.sizes
3100 }
3101
3102 fn clear_floats(&mut self, clear: Clear) {
3103 match clear {
3104 Clear::InlineStart => {
3105 let start_floats = mem::take(&mut self.uncleared_floats.start);
3106 self.cleared_floats.start.max_assign(start_floats);
3107 },
3108 Clear::InlineEnd => {
3109 let end_floats = mem::take(&mut self.uncleared_floats.end);
3110 self.cleared_floats.end.max_assign(end_floats);
3111 },
3112 Clear::Both => {
3113 let start_floats = mem::take(&mut self.uncleared_floats.start);
3114 let end_floats = mem::take(&mut self.uncleared_floats.end);
3115 self.cleared_floats.start.max_assign(start_floats);
3116 self.cleared_floats.end.max_assign(end_floats);
3117 },
3118 Clear::None => {},
3119 }
3120 }
3121
3122 fn flush_floats(&mut self) {
3123 self.clear_floats(Clear::Both);
3124 let start_floats = mem::take(&mut self.cleared_floats.start);
3125 let end_floats = mem::take(&mut self.cleared_floats.end);
3126 self.paragraph.union_assign(&start_floats);
3127 self.paragraph.union_assign(&end_floats);
3128 }
3129
3130 fn compute(
3132 inline_formatting_context: &InlineFormattingContext,
3133 layout_context: &'layout_data LayoutContext,
3134 constraint_space: &'layout_data ConstraintSpace,
3135 ) -> InlineContentSizesResult {
3136 Self {
3137 layout_context,
3138 constraint_space,
3139 paragraph: ContentSizes::zero(),
3140 current_line: ContentSizes::zero(),
3141 pending_whitespace: ContentSizes::zero(),
3142 uncleared_floats: LogicalSides1D::default(),
3143 cleared_floats: LogicalSides1D::default(),
3144 had_content_yet_for_min_content: false,
3145 had_content_yet_for_max_content: false,
3146 ending_inline_pbm_stack: Vec::new(),
3147 depends_on_block_constraints: false,
3148 }
3149 .traverse(inline_formatting_context)
3150 }
3151}
3152
3153pub(crate) struct BidiLevels<'a> {
3154 info: Option<BidiInfo<'a>>,
3155}
3156
3157impl BidiLevels<'_> {
3158 fn level(&self, byte_offset_in_ifc_text: usize) -> Level {
3159 self.info
3160 .as_ref()
3161 .map_or_else(Level::ltr, |info| info.levels[byte_offset_in_ifc_text])
3162 }
3163}
3164
3165fn char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character: char) -> bool {
3177 if character == '\u{00A0}' {
3178 return false;
3179 }
3180 matches!(
3181 icu_properties::maps::line_break().get(character),
3182 ICULineBreak::Glue | ICULineBreak::WordJoiner | ICULineBreak::ZWJ
3183 )
3184}