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