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