1pub mod construct;
72pub mod inline_box;
73pub mod line;
74mod line_breaker;
75pub mod text_run;
76
77use std::cell::{OnceCell, RefCell};
78use std::mem;
79use std::rc::Rc;
80use std::sync::{Arc, OnceLock};
81
82use app_units::{Au, MAX_AU};
83use atomic_refcell::AtomicRef;
84use bitflags::bitflags;
85use construct::InlineFormattingContextBuilder;
86use fonts::{FontMetrics, FontRef, ShapedTextSlice};
87use icu_locid::LanguageIdentifier;
88use icu_locid::subtags::{Language, language};
89use icu_properties::{self, LineBreak as ICULineBreak};
90use icu_segmenter::{LineBreakOptions, LineBreakStrictness, LineBreakWordOption};
91use inline_box::{InlineBox, InlineBoxContainerState, InlineBoxIdentifier, InlineBoxes};
92use layout_api::{LayoutNode, SharedSelection};
93use line::{
94 AbsolutelyPositionedLineItem, AtomicLineItem, FloatLineItem, LineItem, LineItemLayout,
95 TextRunLineItem,
96};
97use line_breaker::LineBreaker;
98use malloc_size_of_derive::MallocSizeOf;
99use script::layout_dom::ServoLayoutNode;
100use servo_arc::Arc as ServoArc;
101use style::Zero;
102use style::computed_values::line_break::T as LineBreak;
103use style::computed_values::text_wrap_mode::T as TextWrapMode;
104use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
105use style::computed_values::word_break::T as WordBreak;
106use style::context::{QuirksMode, SharedStyleContext};
107use style::properties::ComputedValues;
108use style::properties::style_structs::InheritedText;
109use style::values::computed::BaselineShift;
110use style::values::generics::box_::BaselineShiftKeyword;
111use style::values::generics::font::LineHeight;
112use style::values::specified::box_::BaselineSource;
113use style::values::specified::text::TextAlignKeyword;
114use style::values::specified::{AlignmentBaseline, TextAlignLast, TextJustify};
115use text_run::{TextRun, get_font_for_first_font_for_style};
116use unicode_bidi::{BidiInfo, Level};
117
118use super::float::{Clear, PlacementAmongFloats};
119use super::{IndependentFloatOrAtomicLayoutResult, IndependentFormattingContextLayoutResult};
120use crate::cell::{ArcRefCell, WeakRefCell};
121use crate::context::LayoutContext;
122use crate::dom::WeakLayoutBox;
123use crate::dom_traversal::NodeAndStyleInfo;
124use crate::flow::float::{FloatBox, SequentialLayoutState};
125use crate::flow::inline::line::TextRunOffsets;
126use crate::flow::inline::text_run::{FontAndScriptInfo, TextRunItem, TextRunSegment};
127use crate::flow::{
128 BlockLevelBox, CollapsibleWithParentStartMargin, FloatSide, PlacementState,
129 compute_inline_content_sizes_for_block_level_boxes, layout_block_level_child,
130};
131use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
132use crate::fragment_tree::{
133 BaseFragmentInfo, BoxFragment, CollapsedMargin, Fragment, FragmentFlags, PositioningFragment,
134};
135use crate::geom::{LogicalRect, LogicalSides1D, LogicalVec2, ToLogical};
136use crate::layout_box_base::LayoutBoxBase;
137use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
138use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
139use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
140use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SharedStyle};
141
142static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
144static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
145
146#[derive(Debug, MallocSizeOf)]
147pub(crate) struct InlineFormattingContext {
148 inline_items: Vec<InlineItem>,
153
154 inline_boxes: InlineBoxes,
157
158 text_content: String,
160
161 shared_inline_styles: SharedInlineStyles,
164
165 default_font: Option<FontRef>,
169
170 has_first_formatted_line: bool,
173
174 pub(super) contains_floats: bool,
176
177 is_single_line_text_input: bool,
180
181 has_right_to_left_content: bool,
184
185 #[ignore_malloc_size_of = "This is stored primarily in the DOM"]
188 shared_selection: Option<SharedSelection>,
189
190 tab_stop_advance: OnceLock<Au>,
192}
193
194#[derive(Clone, Debug, MallocSizeOf)]
199pub(crate) struct SharedInlineStyles {
200 pub style: SharedStyle,
201 pub selected: SharedStyle,
202}
203
204impl SharedInlineStyles {
205 pub(crate) fn ptr_eq(&self, other: &Self) -> bool {
206 self.style.ptr_eq(&other.style) && self.selected.ptr_eq(&other.selected)
207 }
208
209 pub(crate) fn from_info_and_context(info: &NodeAndStyleInfo, context: &LayoutContext) -> Self {
210 Self {
211 style: SharedStyle::new(info.style.clone()),
212 selected: SharedStyle::new(info.node.selected_style(&context.style_context)),
213 }
214 }
215}
216
217impl BlockLevelBox {
218 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
219 layout.process_soft_wrap_opportunity();
220 layout.commit_current_segment_to_line();
221 layout.process_line_break(true);
222 layout.current_line.for_block_level = true;
223
224 let fragment = layout_block_level_child(
225 layout.layout_context,
226 layout.positioning_context,
227 self,
228 layout.sequential_layout_state.as_deref_mut(),
229 &mut layout.placement_state,
230 LogicalSides1D::new(false, false),
232 true, );
234
235 let Fragment::Box(fragment) = fragment else {
236 unreachable!("The fragment should be a Fragment::Box()");
237 };
238
239 layout.depends_on_block_constraints |= fragment.borrow().base.flags.contains(
242 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
243 );
244
245 layout.push_line_item_to_unbreakable_segment(LineItem::BlockLevel(
246 layout.current_inline_box_identifier(),
247 fragment,
248 ));
249
250 layout.commit_current_segment_to_line();
251 layout.process_line_break(true);
252 layout.current_line.for_block_level = false;
253 }
254}
255
256#[derive(Clone, Debug, MallocSizeOf)]
257pub(crate) enum InlineItem {
258 StartInlineBox(ArcRefCell<InlineBox>),
259 EndInlineBox,
260 TextRun(ArcRefCell<TextRun>),
261 OutOfFlowAbsolutelyPositionedBox(
262 ArcRefCell<AbsolutelyPositionedBox>,
263 usize, ),
265 OutOfFlowFloatBox(ArcRefCell<FloatBox>),
266 Atomic(
267 ArcRefCell<IndependentFormattingContext>,
268 usize, Level, ),
271 BlockLevel(ArcRefCell<BlockLevelBox>),
272}
273
274impl InlineItem {
275 pub(crate) fn repair_style(
276 &self,
277 context: &SharedStyleContext,
278 node: &ServoLayoutNode,
279 new_style: &ServoArc<ComputedValues>,
280 ) {
281 match self {
282 InlineItem::StartInlineBox(inline_box) => {
283 inline_box
284 .borrow_mut()
285 .repair_style(context, node, new_style);
286 },
287 InlineItem::EndInlineBox => {},
288 InlineItem::TextRun(..) => {},
291 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box
292 .borrow_mut()
293 .context
294 .repair_style(context, node, new_style),
295 InlineItem::OutOfFlowFloatBox(float_box) => float_box
296 .borrow_mut()
297 .contents
298 .repair_style(context, node, new_style),
299 InlineItem::Atomic(atomic, ..) => {
300 atomic.borrow_mut().repair_style(context, node, new_style)
301 },
302 InlineItem::BlockLevel(block_level) => block_level
303 .borrow_mut()
304 .repair_style(context, node, new_style),
305 }
306 }
307
308 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
309 match self {
310 InlineItem::StartInlineBox(inline_box) => callback(&inline_box.borrow().base),
311 InlineItem::EndInlineBox | InlineItem::TextRun(..) => {
312 unreachable!("Should never have these kind of fragments attached to a DOM node")
313 },
314 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
315 callback(&positioned_box.borrow().context.base)
316 },
317 InlineItem::OutOfFlowFloatBox(float_box) => callback(&float_box.borrow().contents.base),
318 InlineItem::Atomic(independent_formatting_context, ..) => {
319 callback(&independent_formatting_context.borrow().base)
320 },
321 InlineItem::BlockLevel(block_level) => block_level.borrow().with_base(callback),
322 }
323 }
324
325 pub(crate) fn with_base_mut<T>(&self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
326 match self {
327 InlineItem::StartInlineBox(inline_box) => callback(&mut inline_box.borrow_mut().base),
328 InlineItem::EndInlineBox | InlineItem::TextRun(..) => {
329 unreachable!("Should never have these kind of fragments attached to a DOM node")
330 },
331 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
332 callback(&mut positioned_box.borrow_mut().context.base)
333 },
334 InlineItem::OutOfFlowFloatBox(float_box) => {
335 callback(&mut float_box.borrow_mut().contents.base)
336 },
337 InlineItem::Atomic(independent_formatting_context, ..) => {
338 callback(&mut independent_formatting_context.borrow_mut().base)
339 },
340 InlineItem::BlockLevel(block_level) => block_level.borrow_mut().with_base_mut(callback),
341 }
342 }
343
344 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
345 match self {
346 Self::StartInlineBox(_) | InlineItem::EndInlineBox => {
347 },
350 Self::TextRun(_) => {
351 },
353 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
354 positioned_box.borrow().context.attached_to_tree(layout_box)
355 },
356 Self::OutOfFlowFloatBox(float_box) => {
357 float_box.borrow().contents.attached_to_tree(layout_box)
358 },
359 Self::Atomic(atomic, ..) => atomic.borrow().attached_to_tree(layout_box),
360 Self::BlockLevel(block_level) => block_level.borrow().attached_to_tree(layout_box),
361 }
362 }
363
364 pub(crate) fn downgrade(&self) -> WeakInlineItem {
365 match self {
366 Self::StartInlineBox(inline_box) => {
367 WeakInlineItem::StartInlineBox(inline_box.downgrade())
368 },
369 Self::EndInlineBox => WeakInlineItem::EndInlineBox,
370 Self::TextRun(text_run) => WeakInlineItem::TextRun(text_run.downgrade()),
371 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
372 WeakInlineItem::OutOfFlowAbsolutelyPositionedBox(
373 positioned_box.downgrade(),
374 *offset_in_text,
375 )
376 },
377 Self::OutOfFlowFloatBox(float_box) => {
378 WeakInlineItem::OutOfFlowFloatBox(float_box.downgrade())
379 },
380 Self::Atomic(atomic, offset_in_text, bidi_level) => {
381 WeakInlineItem::Atomic(atomic.downgrade(), *offset_in_text, *bidi_level)
382 },
383 Self::BlockLevel(block_level) => WeakInlineItem::BlockLevel(block_level.downgrade()),
384 }
385 }
386}
387
388#[derive(Clone, Debug, MallocSizeOf)]
389pub(crate) enum WeakInlineItem {
390 StartInlineBox(WeakRefCell<InlineBox>),
391 EndInlineBox,
392 TextRun(WeakRefCell<TextRun>),
393 OutOfFlowAbsolutelyPositionedBox(
394 WeakRefCell<AbsolutelyPositionedBox>,
395 usize, ),
397 OutOfFlowFloatBox(WeakRefCell<FloatBox>),
398 Atomic(
399 WeakRefCell<IndependentFormattingContext>,
400 usize, Level, ),
403 BlockLevel(WeakRefCell<BlockLevelBox>),
404}
405
406impl WeakInlineItem {
407 pub(crate) fn upgrade(&self) -> Option<InlineItem> {
408 Some(match self {
409 Self::StartInlineBox(inline_box) => InlineItem::StartInlineBox(inline_box.upgrade()?),
410 Self::EndInlineBox => InlineItem::EndInlineBox,
411 Self::TextRun(text_run) => InlineItem::TextRun(text_run.upgrade()?),
412 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
413 InlineItem::OutOfFlowAbsolutelyPositionedBox(
414 positioned_box.upgrade()?,
415 *offset_in_text,
416 )
417 },
418 Self::OutOfFlowFloatBox(float_box) => {
419 InlineItem::OutOfFlowFloatBox(float_box.upgrade()?)
420 },
421 Self::Atomic(atomic, offset_in_text, bidi_level) => {
422 InlineItem::Atomic(atomic.upgrade()?, *offset_in_text, *bidi_level)
423 },
424 Self::BlockLevel(block_level) => InlineItem::BlockLevel(block_level.upgrade()?),
425 })
426 }
427}
428
429struct LineUnderConstruction {
436 start_position: LogicalVec2<Au>,
439
440 inline_position: Au,
443
444 max_block_size: LineBlockSizes,
448
449 has_content: bool,
452
453 has_inline_pbm: bool,
456
457 has_floats_waiting_to_be_placed: bool,
461
462 placement_among_floats: OnceCell<LogicalRect<Au>>,
467
468 line_items: Vec<LineItem>,
471
472 for_block_level: bool,
474
475 starting_character_offset: usize,
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 starting_character_offset: 0,
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: RefCell<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 inline_box_states: Vec<Rc<InlineBoxContainerState>>,
847
848 fragments: Vec<Fragment>,
852
853 current_line: LineUnderConstruction,
855
856 current_line_segment: UnbreakableSegmentUnderConstruction,
858
859 force_line_break_before_new_content: Option<usize>,
882
883 deferred_br_clear: Clear,
887
888 pub have_deferred_soft_wrap_opportunity: bool,
892
893 depends_on_block_constraints: bool,
896
897 white_space_collapse: WhiteSpaceCollapse,
902
903 text_wrap_mode: TextWrapMode,
908}
909
910impl InlineFormattingContextLayout<'_> {
911 fn current_inline_container_state(&self) -> &InlineContainerState {
912 match self.inline_box_state_stack.last() {
913 Some(inline_box_state) => &inline_box_state.base,
914 None => &self.root_nesting_level,
915 }
916 }
917
918 fn current_inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
919 self.inline_box_state_stack
920 .last()
921 .map(|state| state.identifier)
922 }
923
924 fn current_line_max_block_size_including_nested_containers(&self) -> LineBlockSizes {
925 self.current_inline_container_state()
926 .nested_strut_block_sizes
927 .max(&self.current_line.max_block_size)
928 }
929
930 fn current_line_block_start_considering_placement_among_floats(&self) -> Au {
931 self.current_line.placement_among_floats.get().map_or(
932 self.current_line.start_position.block,
933 |placement_among_floats| placement_among_floats.start_corner.block,
934 )
935 }
936
937 fn propagate_current_nesting_level_white_space_style(&mut self) {
938 let style = match self.inline_box_state_stack.last() {
939 Some(inline_box_state) => &inline_box_state.base.style,
940 None => self.placement_state.containing_block.style,
941 };
942 let style_text = style.get_inherited_text();
943 self.white_space_collapse = style_text.white_space_collapse;
944 self.text_wrap_mode = style_text.text_wrap_mode;
945 }
946
947 fn processing_br_element(&self) -> bool {
948 self.inline_box_state_stack.last().is_some_and(|state| {
949 state
950 .base_fragment_info
951 .flags
952 .contains(FragmentFlags::IS_BR_ELEMENT)
953 })
954 }
955
956 fn start_inline_box(&mut self, inline_box: &InlineBox) {
959 let containing_block = self.containing_block();
960 let inline_box_state = InlineBoxContainerState::new(
961 inline_box,
962 containing_block,
963 self.layout_context,
964 self.current_inline_container_state(),
965 inline_box.default_font.clone(),
966 );
967
968 self.depends_on_block_constraints |= inline_box
969 .base
970 .style
971 .depends_on_block_constraints_due_to_relative_positioning(
972 containing_block.style.writing_mode,
973 );
974
975 if inline_box_state
980 .base_fragment_info
981 .flags
982 .contains(FragmentFlags::IS_BR_ELEMENT) &&
983 self.deferred_br_clear == Clear::None
984 {
985 self.deferred_br_clear = Clear::from_style_and_container_writing_mode(
986 &inline_box_state.base.style,
987 self.containing_block().style.writing_mode,
988 );
989 }
990
991 let padding = inline_box_state.pbm.padding.inline_start;
992 let border = inline_box_state.pbm.border.inline_start;
993 let margin = inline_box_state.pbm.margin.inline_start.auto_is(Au::zero);
994 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
997 self.current_line_segment.has_inline_pbm = true;
998 }
999 self.current_line_segment.inline_size += padding + border + margin;
1000 self.current_line_segment
1001 .line_items
1002 .push(LineItem::InlineStartBoxPaddingBorderMargin(
1003 inline_box.identifier,
1004 ));
1005
1006 let inline_box_state = Rc::new(inline_box_state);
1007
1008 assert_eq!(
1012 self.inline_box_states.len(),
1013 inline_box.identifier.index_in_inline_boxes as usize
1014 );
1015 self.inline_box_states.push(inline_box_state.clone());
1016 self.inline_box_state_stack.push(inline_box_state);
1017 }
1018
1019 fn finish_inline_box(&mut self) {
1022 let inline_box_state = match self.inline_box_state_stack.pop() {
1023 Some(inline_box_state) => inline_box_state,
1024 None => return, };
1026
1027 self.current_line_segment
1028 .max_block_size
1029 .max_assign(&inline_box_state.base.nested_strut_block_sizes);
1030
1031 if *inline_box_state.base.has_content.borrow() {
1036 self.propagate_current_nesting_level_white_space_style();
1037 }
1038
1039 let padding = inline_box_state.pbm.padding.inline_end;
1040 let border = inline_box_state.pbm.border.inline_end;
1041 let margin = inline_box_state.pbm.margin.inline_end.auto_is(Au::zero);
1042 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
1045 self.current_line_segment.has_inline_pbm = true;
1046 }
1047 self.current_line_segment.inline_size += padding + border + margin;
1048 self.current_line_segment
1049 .line_items
1050 .push(LineItem::InlineEndBoxPaddingBorderMargin(
1051 inline_box_state.identifier,
1052 ))
1053 }
1054
1055 fn finish_last_line(&mut self) {
1056 self.possibly_flush_deferred_forced_line_break();
1058
1059 self.process_soft_wrap_opportunity();
1065
1066 self.commit_current_segment_to_line();
1069
1070 self.finish_current_line_and_reset(true );
1073 }
1074
1075 fn finish_current_line_and_reset(&mut self, last_line_or_forced_line_break: bool) {
1079 self.possibly_push_empty_text_run_to_line_for_text_caret();
1080
1081 let whitespace_trimmed = self.current_line.trim_trailing_whitespace();
1082 let (inline_start_position, justification_adjustment) = self
1083 .calculate_current_line_inline_start_and_justification_adjustment(
1084 whitespace_trimmed,
1085 last_line_or_forced_line_break,
1086 );
1087
1088 let is_phantom_line = self.current_line.is_phantom();
1097 if !is_phantom_line {
1098 self.current_line.start_position.block += self.placement_state.current_margin.solve();
1099 self.placement_state.current_margin = CollapsedMargin::zero();
1100 }
1101 let block_start_position =
1102 self.current_line_block_start_considering_placement_among_floats();
1103
1104 let effective_block_advance = if is_phantom_line {
1105 LineBlockSizes::zero()
1106 } else {
1107 self.current_line_max_block_size_including_nested_containers()
1108 };
1109
1110 let resolved_block_advance = effective_block_advance.resolve();
1111 let block_end_position = if self.current_line.for_block_level {
1112 self.placement_state.current_block_direction_position
1113 } else {
1114 let mut block_end_position = block_start_position + resolved_block_advance;
1115 if let Some(sequential_layout_state) = self.sequential_layout_state.as_mut() {
1116 if !is_phantom_line {
1117 sequential_layout_state.commit_margin();
1118 }
1119
1120 let increment = block_end_position - self.current_line.start_position.block;
1123 sequential_layout_state.advance_block_position(increment);
1124
1125 if let Some(clearance) = sequential_layout_state
1129 .calculate_clearance(self.deferred_br_clear, &CollapsedMargin::zero())
1130 {
1131 sequential_layout_state.advance_block_position(clearance);
1132 block_end_position += clearance;
1133 };
1134 self.deferred_br_clear = Clear::None;
1135 }
1136 block_end_position
1137 };
1138
1139 let mut line_to_layout = std::mem::replace(
1141 &mut self.current_line,
1142 LineUnderConstruction::new(LogicalVec2 {
1143 inline: Au::zero(),
1144 block: block_end_position,
1145 }),
1146 );
1147 if !line_to_layout.for_block_level {
1148 self.placement_state.current_block_direction_position = block_end_position;
1149 }
1150
1151 if line_to_layout.has_floats_waiting_to_be_placed {
1152 place_pending_floats(self, &mut line_to_layout.line_items);
1153 }
1154
1155 let start_position = LogicalVec2 {
1156 block: block_start_position,
1157 inline: inline_start_position,
1158 };
1159
1160 let baseline_offset = effective_block_advance.find_baseline_offset();
1161 let start_positioning_context_length = self.positioning_context.len();
1162 let fragments = LineItemLayout::layout_line_items(
1163 self,
1164 line_to_layout.line_items,
1165 start_position,
1166 &effective_block_advance,
1167 justification_adjustment,
1168 is_phantom_line,
1169 );
1170
1171 if !is_phantom_line {
1172 let baseline = baseline_offset + block_start_position;
1173 self.placement_state
1174 .inflow_baselines
1175 .first
1176 .get_or_insert(baseline);
1177 self.placement_state.inflow_baselines.last = Some(baseline);
1178 self.placement_state
1179 .next_in_flow_margin_collapses_with_parent_start_margin = false;
1180 }
1181
1182 if fragments.is_empty() &&
1184 self.positioning_context.len() == start_positioning_context_length
1185 {
1186 return;
1187 }
1188
1189 let start_corner = LogicalVec2 {
1193 inline: Au::zero(),
1194 block: block_start_position,
1195 };
1196
1197 let logical_origin_in_physical_coordinates =
1198 start_corner.to_physical_vector(self.containing_block().style.writing_mode);
1199 self.positioning_context
1200 .adjust_static_position_of_hoisted_fragments_with_offset(
1201 &logical_origin_in_physical_coordinates,
1202 start_positioning_context_length,
1203 );
1204
1205 let containing_block = self.containing_block();
1206 let physical_line_rect = LogicalRect {
1207 start_corner,
1208 size: LogicalVec2 {
1209 inline: containing_block.size.inline,
1210 block: effective_block_advance.resolve(),
1211 },
1212 }
1213 .as_physical(Some(containing_block));
1214 self.fragments
1215 .push(Fragment::Positioning(PositioningFragment::new_anonymous(
1216 self.root_nesting_level.style.clone(),
1217 physical_line_rect,
1218 fragments,
1219 )));
1220 }
1221
1222 fn calculate_current_line_inline_start_and_justification_adjustment(
1227 &self,
1228 whitespace_trimmed: Au,
1229 last_line_or_forced_line_break: bool,
1230 ) -> (Au, Au) {
1231 enum TextAlign {
1232 Start,
1233 Center,
1234 End,
1235 }
1236 let containing_block = self.containing_block();
1237 let style = containing_block.style;
1238 let mut text_align_keyword = style.clone_text_align();
1239
1240 if last_line_or_forced_line_break {
1241 text_align_keyword = match style.clone_text_align_last() {
1242 TextAlignLast::Auto if text_align_keyword == TextAlignKeyword::Justify => {
1243 TextAlignKeyword::Start
1244 },
1245 TextAlignLast::Auto => text_align_keyword,
1246 TextAlignLast::Start => TextAlignKeyword::Start,
1247 TextAlignLast::End => TextAlignKeyword::End,
1248 TextAlignLast::Left => TextAlignKeyword::Left,
1249 TextAlignLast::Right => TextAlignKeyword::Right,
1250 TextAlignLast::Center => TextAlignKeyword::Center,
1251 TextAlignLast::Justify => TextAlignKeyword::Justify,
1252 };
1253 }
1254
1255 let text_align = match text_align_keyword {
1256 TextAlignKeyword::Start => TextAlign::Start,
1257 TextAlignKeyword::Center | TextAlignKeyword::MozCenter => TextAlign::Center,
1258 TextAlignKeyword::End => TextAlign::End,
1259 TextAlignKeyword::Left | TextAlignKeyword::MozLeft => {
1260 if style.writing_mode.line_left_is_inline_start() {
1261 TextAlign::Start
1262 } else {
1263 TextAlign::End
1264 }
1265 },
1266 TextAlignKeyword::Right | TextAlignKeyword::MozRight => {
1267 if style.writing_mode.line_left_is_inline_start() {
1268 TextAlign::End
1269 } else {
1270 TextAlign::Start
1271 }
1272 },
1273 TextAlignKeyword::Justify => TextAlign::Start,
1274 };
1275
1276 let (line_start, available_space) = match self.current_line.placement_among_floats.get() {
1277 Some(placement_among_floats) => (
1278 placement_among_floats.start_corner.inline,
1279 placement_among_floats.size.inline,
1280 ),
1281 None => (Au::zero(), containing_block.size.inline),
1282 };
1283
1284 let text_indent = self.current_line.start_position.inline;
1291 let line_length = self.current_line.inline_position - whitespace_trimmed - text_indent;
1292 let adjusted_line_start = line_start +
1293 match text_align {
1294 TextAlign::Start => text_indent,
1295 TextAlign::End => (available_space - line_length).max(text_indent),
1296 TextAlign::Center => (available_space - line_length + text_indent)
1297 .scale_by(0.5)
1298 .max(text_indent),
1299 };
1300
1301 let text_justify = containing_block.style.clone_text_justify();
1305 let justification_adjustment = match (text_align_keyword, text_justify) {
1306 (TextAlignKeyword::Justify, TextJustify::None) => Au::zero(),
1309 (TextAlignKeyword::Justify, _) => {
1310 match self.current_line.count_justification_opportunities() {
1311 0 => Au::zero(),
1312 num_justification_opportunities => {
1313 (available_space - text_indent - line_length)
1314 .scale_by(1. / num_justification_opportunities as f32)
1315 },
1316 }
1317 },
1318 _ => Au::zero(),
1319 };
1320
1321 let justification_adjustment = justification_adjustment.max(Au::zero());
1324
1325 (adjusted_line_start, justification_adjustment)
1326 }
1327
1328 fn place_float_fragment(&mut self, fragment: &mut BoxFragment) {
1329 let state = self
1330 .sequential_layout_state
1331 .as_mut()
1332 .expect("Tried to lay out a float with no sequential placement state!");
1333
1334 let block_offset_from_containining_block_top = state
1335 .current_block_position_including_margins() -
1336 state.current_containing_block_offset();
1337 state.place_float_fragment(
1338 fragment,
1339 self.placement_state.containing_block,
1340 CollapsedMargin::zero(),
1341 block_offset_from_containining_block_top,
1342 );
1343 }
1344
1345 fn place_float_line_item_for_commit_to_line(
1354 &mut self,
1355 float_item: &mut FloatLineItem,
1356 line_inline_size_without_trailing_whitespace: Au,
1357 ) {
1358 let containing_block = self.containing_block();
1359 let mut float_fragment = float_item.fragment.borrow_mut();
1360 let logical_margin_rect_size = float_fragment
1361 .margin_rect()
1362 .size
1363 .to_logical(containing_block.style.writing_mode);
1364 let inline_size = logical_margin_rect_size.inline.max(Au::zero());
1365
1366 let available_inline_size = match self.current_line.placement_among_floats.get() {
1367 Some(placement_among_floats) => placement_among_floats.size.inline,
1368 None => containing_block.size.inline,
1369 } - line_inline_size_without_trailing_whitespace;
1370
1371 let has_content = self.current_line.has_content || self.current_line_segment.has_content;
1377 let fits_on_line = !has_content || inline_size <= available_inline_size;
1378 let needs_placement_later =
1379 self.current_line.has_floats_waiting_to_be_placed || !fits_on_line;
1380
1381 if needs_placement_later {
1382 self.current_line.has_floats_waiting_to_be_placed = true;
1383 } else {
1384 self.place_float_fragment(&mut float_fragment);
1385 float_item.needs_placement = false;
1386 }
1387
1388 let new_placement = self.place_line_among_floats(&LogicalVec2 {
1393 inline: line_inline_size_without_trailing_whitespace,
1394 block: self.current_line.max_block_size.resolve(),
1395 });
1396 self.current_line
1397 .replace_placement_among_floats(new_placement);
1398 }
1399
1400 fn place_line_among_floats(&self, potential_line_size: &LogicalVec2<Au>) -> LogicalRect<Au> {
1405 let sequential_layout_state = self
1406 .sequential_layout_state
1407 .as_ref()
1408 .expect("Should not have called this function without having floats.");
1409
1410 let ifc_offset_in_float_container = LogicalVec2 {
1411 inline: sequential_layout_state
1412 .floats
1413 .containing_block_info
1414 .inline_start,
1415 block: sequential_layout_state.current_containing_block_offset(),
1416 };
1417
1418 let ceiling = self.current_line_block_start_considering_placement_among_floats();
1419 let mut placement = PlacementAmongFloats::new(
1420 &sequential_layout_state.floats,
1421 ceiling + ifc_offset_in_float_container.block,
1422 LogicalVec2 {
1423 inline: potential_line_size.inline,
1424 block: potential_line_size.block,
1425 },
1426 &PaddingBorderMargin::zero(),
1427 );
1428
1429 let mut placement_rect = placement.place();
1430 placement_rect.start_corner -= ifc_offset_in_float_container;
1431 placement_rect
1432 }
1433
1434 fn new_potential_line_size_causes_line_break(
1441 &mut self,
1442 potential_line_size: &LogicalVec2<Au>,
1443 ) -> bool {
1444 let containing_block = self.containing_block();
1445 let available_line_space = if self.sequential_layout_state.is_some() {
1446 self.current_line
1447 .placement_among_floats
1448 .get_or_init(|| self.place_line_among_floats(potential_line_size))
1449 .size
1450 } else {
1451 LogicalVec2 {
1452 inline: containing_block.size.inline,
1453 block: MAX_AU,
1454 }
1455 };
1456
1457 let inline_would_overflow = potential_line_size.inline > available_line_space.inline;
1458 let block_would_overflow = potential_line_size.block > available_line_space.block;
1459
1460 let can_break = self.current_line.has_content;
1463
1464 if !can_break {
1470 if self.sequential_layout_state.is_some() &&
1473 (inline_would_overflow || block_would_overflow)
1474 {
1475 let new_placement = self.place_line_among_floats(potential_line_size);
1476 self.current_line
1477 .replace_placement_among_floats(new_placement);
1478 }
1479
1480 return false;
1481 }
1482
1483 if potential_line_size.inline > containing_block.size.inline {
1486 return true;
1487 }
1488
1489 if block_would_overflow {
1493 assert!(self.sequential_layout_state.is_some());
1495 let new_placement = self.place_line_among_floats(potential_line_size);
1496 if new_placement.start_corner.block !=
1497 self.current_line_block_start_considering_placement_among_floats()
1498 {
1499 return true;
1500 } else {
1501 self.current_line
1502 .replace_placement_among_floats(new_placement);
1503 return false;
1504 }
1505 }
1506
1507 inline_would_overflow
1511 }
1512
1513 fn defer_forced_line_break_at_character_offset(&mut self, line_break_offset: usize) {
1514 if !self.unbreakable_segment_fits_on_line() {
1517 self.process_line_break(false );
1518 }
1519
1520 self.force_line_break_before_new_content = Some(line_break_offset);
1522
1523 let line_is_empty =
1531 !self.current_line_segment.has_content && !self.current_line.has_content;
1532 if !self.processing_br_element() || line_is_empty {
1533 let strut_size = self
1534 .current_inline_container_state()
1535 .strut_block_sizes
1536 .clone();
1537 self.update_unbreakable_segment_for_new_content(
1538 &strut_size,
1539 Au::zero(),
1540 SegmentContentFlags::empty(),
1541 );
1542 }
1543 }
1544
1545 fn possibly_flush_deferred_forced_line_break(&mut self) {
1546 let Some(line_break_character_offset) = self.force_line_break_before_new_content.take()
1547 else {
1548 return;
1549 };
1550
1551 self.commit_current_segment_to_line();
1552 self.process_line_break(true );
1553
1554 self.current_line.starting_character_offset = line_break_character_offset + 1;
1555 }
1556
1557 fn push_line_item_to_unbreakable_segment(&mut self, line_item: LineItem) {
1558 self.current_line_segment
1559 .push_line_item(line_item, self.inline_box_state_stack.len());
1560 }
1561
1562 fn push_glyph_store_to_unbreakable_segment(
1563 &mut self,
1564 glyph_store: Arc<ShapedTextSlice>,
1565 text_run: &TextRun,
1566 info: &Arc<FontAndScriptInfo>,
1567 offsets: Option<TextRunOffsets>,
1568 ) {
1569 let inline_advance = glyph_store.total_advance();
1570 let flags = if glyph_store.is_whitespace() {
1571 SegmentContentFlags::from(text_run.inline_styles.style.borrow().get_inherited_text())
1572 } else {
1573 SegmentContentFlags::empty()
1574 };
1575
1576 let mut block_contribution = LineBlockSizes::zero();
1577 let quirks_mode = self.layout_context.style_context.quirks_mode() != QuirksMode::NoQuirks;
1578 if quirks_mode && !flags.is_collapsible_whitespace() {
1579 block_contribution.max_assign(&self.current_inline_container_state().strut_block_sizes);
1584 }
1585
1586 let font_metrics = &info.font.metrics;
1590 if self
1591 .current_inline_container_state()
1592 .font_metrics
1593 .block_metrics_meaningfully_differ(font_metrics)
1594 {
1595 let container_state = self.current_inline_container_state();
1597 let baseline_shift = effective_baseline_shift(
1598 &container_state.style,
1599 self.inline_box_state_stack.last().map(|c| &c.base),
1600 );
1601 let mut font_block_conribution = container_state.get_block_size_contribution(
1602 baseline_shift,
1603 font_metrics,
1604 &container_state.font_metrics,
1605 );
1606 font_block_conribution.adjust_for_baseline_offset(container_state.baseline_offset);
1607 block_contribution.max_assign(&font_block_conribution);
1608 }
1609
1610 self.update_unbreakable_segment_for_new_content(&block_contribution, inline_advance, flags);
1611
1612 let current_inline_box_identifier = self.current_inline_box_identifier();
1613 if let Some(LineItem::TextRun(inline_box_identifier, line_item)) =
1614 self.current_line_segment.line_items.last_mut()
1615 {
1616 if *inline_box_identifier == current_inline_box_identifier &&
1617 line_item.merge_if_possible(
1618 info,
1619 &glyph_store,
1620 &offsets,
1621 &text_run.inline_styles,
1622 )
1623 {
1624 return;
1625 }
1626 }
1627
1628 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1629 current_inline_box_identifier,
1630 TextRunLineItem {
1631 text: vec![glyph_store],
1632 base_fragment_info: text_run.base_fragment_info,
1633 inline_styles: text_run.inline_styles.clone(),
1634 info: info.clone(),
1635 offsets: offsets.map(Box::new),
1636 is_empty_for_text_cursor: false,
1637 },
1638 ));
1639 }
1640
1641 fn possibly_push_empty_text_run_to_line_for_text_caret(&mut self) {
1644 let line_start_offset = self.current_line.starting_character_offset;
1645 let Some(shared_selection) = self.ifc.shared_selection.clone() else {
1646 return;
1647 };
1648 let offsets = TextRunOffsets {
1649 shared_selection,
1650 character_range: line_start_offset..line_start_offset + 1,
1651 };
1652
1653 if self
1655 .current_line
1656 .line_items
1657 .iter()
1658 .rev()
1659 .find(|line_item| line_item.is_in_flow_content())
1660 .is_some_and(|line_item| matches!(line_item, LineItem::TextRun(..)))
1661 {
1662 return;
1663 }
1664
1665 let inline_container_state = self.current_inline_container_state();
1666 let Some(font) = inline_container_state.default_font.clone() else {
1667 return;
1668 };
1669
1670 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1671 self.current_inline_box_identifier(),
1672 TextRunLineItem {
1673 text: Default::default(),
1674 base_fragment_info: BaseFragmentInfo::anonymous(),
1675 inline_styles: self.ifc.shared_inline_styles.clone(),
1676 info: Arc::new(FontAndScriptInfo::simple_for_font(font)),
1677 offsets: Some(Box::new(offsets)),
1678 is_empty_for_text_cursor: true,
1679 },
1680 ));
1681 self.current_line_segment.has_content = true;
1682 self.commit_current_segment_to_line();
1683 }
1684
1685 fn update_unbreakable_segment_for_new_content(
1686 &mut self,
1687 block_sizes_of_content: &LineBlockSizes,
1688 inline_size: Au,
1689 flags: SegmentContentFlags,
1690 ) {
1691 if flags.is_collapsible_whitespace() || flags.is_wrappable_and_hangable() {
1692 self.current_line_segment.trailing_whitespace_size = inline_size;
1693 } else {
1694 self.current_line_segment.trailing_whitespace_size = Au::zero();
1695 }
1696 if !flags.is_collapsible_whitespace() {
1697 self.current_line_segment.has_content = true;
1698 }
1699
1700 let container_max_block_size = &self
1702 .current_inline_container_state()
1703 .nested_strut_block_sizes
1704 .clone();
1705 self.current_line_segment
1706 .max_block_size
1707 .max_assign(container_max_block_size);
1708 self.current_line_segment
1709 .max_block_size
1710 .max_assign(block_sizes_of_content);
1711
1712 self.current_line_segment.inline_size += inline_size;
1713
1714 *self
1716 .current_inline_container_state()
1717 .has_content
1718 .borrow_mut() = true;
1719 self.propagate_current_nesting_level_white_space_style();
1720 }
1721
1722 fn process_line_break(&mut self, forced_line_break: bool) {
1723 self.current_line_segment.trim_leading_whitespace();
1724 self.finish_current_line_and_reset(forced_line_break);
1725 }
1726
1727 fn potential_line_size(&self) -> LogicalVec2<Au> {
1728 LogicalVec2 {
1729 inline: self.current_line.inline_position + self.current_line_segment.inline_size,
1730 block: self
1731 .current_line_max_block_size_including_nested_containers()
1732 .max(&self.current_line_segment.max_block_size)
1733 .resolve(),
1734 }
1735 }
1736
1737 fn unbreakable_segment_fits_on_line(&mut self) -> bool {
1738 let potential_line_size_without_hanging_whitespace = self.potential_line_size() -
1739 LogicalVec2 {
1740 inline: self.current_line_segment.trailing_whitespace_size,
1741 block: Au::zero(),
1742 };
1743 !self.new_potential_line_size_causes_line_break(
1744 &potential_line_size_without_hanging_whitespace,
1745 )
1746 }
1747
1748 fn process_soft_wrap_opportunity(&mut self) {
1752 if self.current_line_segment.line_items.is_empty() {
1753 return;
1754 }
1755 if self.text_wrap_mode == TextWrapMode::Nowrap {
1756 return;
1757 }
1758 if !self.unbreakable_segment_fits_on_line() {
1759 self.process_line_break(false );
1760 }
1761 self.commit_current_segment_to_line();
1762 }
1763
1764 fn commit_current_segment_to_line(&mut self) {
1767 if self.current_line_segment.line_items.is_empty() && !self.current_line_segment.has_content
1770 {
1771 return;
1772 }
1773
1774 if !self.current_line.has_content {
1775 self.current_line_segment.trim_leading_whitespace();
1776 }
1777
1778 self.current_line.inline_position += self.current_line_segment.inline_size;
1779 self.current_line.max_block_size = self
1780 .current_line_max_block_size_including_nested_containers()
1781 .max(&self.current_line_segment.max_block_size);
1782 let line_inline_size_without_trailing_whitespace =
1783 self.current_line.inline_position - self.current_line_segment.trailing_whitespace_size;
1784
1785 let mut segment_items = mem::take(&mut self.current_line_segment.line_items);
1787 for item in segment_items.iter_mut() {
1788 if let LineItem::Float(_, float_item) = item {
1789 self.place_float_line_item_for_commit_to_line(
1790 float_item,
1791 line_inline_size_without_trailing_whitespace,
1792 );
1793 }
1794 }
1795
1796 if self.current_line.line_items.is_empty() {
1801 let will_break = self.new_potential_line_size_causes_line_break(&LogicalVec2 {
1802 inline: line_inline_size_without_trailing_whitespace,
1803 block: self.current_line_segment.max_block_size.resolve(),
1804 });
1805 assert!(!will_break);
1806 }
1807
1808 self.current_line.line_items.extend(segment_items);
1809 self.current_line.has_content |= self.current_line_segment.has_content;
1810 self.current_line.has_inline_pbm |= self.current_line_segment.has_inline_pbm;
1811
1812 self.current_line_segment.reset();
1813 }
1814
1815 #[inline]
1816 fn containing_block(&self) -> &ContainingBlock<'_> {
1817 self.placement_state.containing_block
1818 }
1819}
1820
1821bitflags! {
1822 struct SegmentContentFlags: u8 {
1823 const COLLAPSIBLE_WHITESPACE = 0b00000001;
1824 const WRAPPABLE_AND_HANGABLE_WHITESPACE = 0b00000010;
1825 }
1826}
1827
1828impl SegmentContentFlags {
1829 fn is_collapsible_whitespace(&self) -> bool {
1830 self.contains(Self::COLLAPSIBLE_WHITESPACE)
1831 }
1832
1833 fn is_wrappable_and_hangable(&self) -> bool {
1834 self.contains(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE)
1835 }
1836}
1837
1838impl From<&InheritedText> for SegmentContentFlags {
1839 fn from(style_text: &InheritedText) -> Self {
1840 let mut flags = Self::empty();
1841
1842 if !matches!(
1845 style_text.white_space_collapse,
1846 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
1847 ) {
1848 flags.insert(Self::COLLAPSIBLE_WHITESPACE);
1849 }
1850
1851 if style_text.text_wrap_mode == TextWrapMode::Wrap &&
1854 style_text.white_space_collapse != WhiteSpaceCollapse::BreakSpaces
1855 {
1856 flags.insert(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE);
1857 }
1858 flags
1859 }
1860}
1861
1862impl InlineFormattingContext {
1863 #[servo_tracing::instrument(name = "InlineFormattingContext::new_with_builder", skip_all)]
1864 fn new_with_builder(
1865 mut builder: InlineFormattingContextBuilder,
1866 layout_context: &LayoutContext,
1867 has_first_formatted_line: bool,
1868 is_single_line_text_input: bool,
1869 starting_bidi_level: Level,
1870 ) -> Self {
1871 let text_content: String = builder.text_segments.into_iter().collect();
1873
1874 let bidi_info = BidiInfo::new(&text_content, Some(starting_bidi_level));
1875 let has_right_to_left_content = bidi_info.has_rtl();
1876 let shared_inline_styles = builder
1877 .shared_inline_styles_stack
1878 .last()
1879 .expect("Should have at least one SharedInlineStyle for the root of an IFC")
1880 .clone();
1881 let (word_break, line_break, lang) = {
1882 let styles = shared_inline_styles.style.borrow();
1883 let text_style = styles.get_inherited_text();
1884 (
1885 text_style.word_break,
1886 text_style.line_break,
1887 styles.get_font()._x_lang.clone(),
1888 )
1889 };
1890
1891 let mut options = LineBreakOptions::default();
1892
1893 options.strictness = match line_break {
1894 LineBreak::Loose => LineBreakStrictness::Loose,
1895 LineBreak::Normal => LineBreakStrictness::Normal,
1896 LineBreak::Strict => LineBreakStrictness::Strict,
1897 LineBreak::Anywhere => LineBreakStrictness::Anywhere,
1898 LineBreak::Auto => LineBreakStrictness::Normal,
1901 };
1902 options.word_option = match word_break {
1903 WordBreak::Normal => LineBreakWordOption::Normal,
1904 WordBreak::BreakAll => LineBreakWordOption::BreakAll,
1905 WordBreak::KeepAll => LineBreakWordOption::KeepAll,
1906 };
1907 options.ja_zh = {
1910 lang.0.parse::<LanguageIdentifier>().is_ok_and(|lang_id| {
1911 const JA: Language = language!("ja");
1912 const ZH: Language = language!("zh");
1913 matches!(lang_id.language, JA | ZH)
1914 })
1915 };
1916
1917 let mut new_linebreaker = LineBreaker::new(text_content.as_str(), options);
1918 for item in &mut builder.inline_items {
1919 match item {
1920 InlineItem::TextRun(text_run) => {
1921 text_run.borrow_mut().segment_and_shape(
1922 &text_content,
1923 layout_context,
1924 &mut new_linebreaker,
1925 &bidi_info,
1926 );
1927 },
1928 InlineItem::StartInlineBox(inline_box) => {
1929 let inline_box = &mut *inline_box.borrow_mut();
1930 if let Some(font) = get_font_for_first_font_for_style(
1931 &inline_box.base.style,
1932 &layout_context.font_context,
1933 ) {
1934 inline_box.default_font = Some(font);
1935 }
1936 },
1937 InlineItem::Atomic(_, index_in_text, bidi_level) => {
1938 *bidi_level = bidi_info.levels[*index_in_text];
1939 },
1940 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) |
1941 InlineItem::OutOfFlowFloatBox(_) |
1942 InlineItem::EndInlineBox |
1943 InlineItem::BlockLevel { .. } => {},
1944 }
1945 }
1946
1947 let default_font = get_font_for_first_font_for_style(
1948 &shared_inline_styles.style.borrow(),
1949 &layout_context.font_context,
1950 );
1951
1952 InlineFormattingContext {
1953 text_content,
1954 inline_items: builder.inline_items,
1955 inline_boxes: builder.inline_boxes,
1956 shared_inline_styles,
1957 default_font,
1958 has_first_formatted_line,
1959 contains_floats: builder.contains_floats,
1960 is_single_line_text_input,
1961 has_right_to_left_content,
1962 shared_selection: builder.shared_selection,
1963 tab_stop_advance: Default::default(),
1964 }
1965 }
1966
1967 pub(crate) fn repair_style(
1968 &self,
1969 context: &SharedStyleContext,
1970 node: &ServoLayoutNode,
1971 new_style: &ServoArc<ComputedValues>,
1972 ) {
1973 *self.shared_inline_styles.style.borrow_mut() = new_style.clone();
1974 *self.shared_inline_styles.selected.borrow_mut() = node.selected_style(context);
1975 }
1976
1977 fn inline_start_for_first_line(&self, containing_block: IndefiniteContainingBlock) -> Au {
1978 if !self.has_first_formatted_line {
1979 return Au::zero();
1980 }
1981 containing_block
1982 .style
1983 .get_inherited_text()
1984 .text_indent
1985 .length
1986 .to_used_value(containing_block.size.inline.unwrap_or_default())
1987 }
1988
1989 pub(super) fn layout(
1990 &self,
1991 layout_context: &LayoutContext,
1992 positioning_context: &mut PositioningContext,
1993 containing_block: &ContainingBlock,
1994 sequential_layout_state: Option<&mut SequentialLayoutState>,
1995 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
1996 ) -> IndependentFormattingContextLayoutResult {
1997 for inline_box in self.inline_boxes.iter() {
1999 inline_box.borrow().base.clear_fragments();
2000 }
2001
2002 let style = containing_block.style;
2003
2004 let style_text = containing_block.style.get_inherited_text();
2005 let mut inline_container_state_flags = InlineContainerStateFlags::empty();
2006 if inline_container_needs_strut(style, layout_context, None) {
2007 inline_container_state_flags.insert(InlineContainerStateFlags::CREATE_STRUT);
2008 }
2009 if self.is_single_line_text_input {
2010 inline_container_state_flags
2011 .insert(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT);
2012 }
2013 let placement_state =
2014 PlacementState::new(collapsible_with_parent_start_margin, containing_block);
2015
2016 let mut layout = InlineFormattingContextLayout {
2017 positioning_context,
2018 placement_state,
2019 sequential_layout_state,
2020 layout_context,
2021 ifc: self,
2022 fragments: Vec::new(),
2023 current_line: LineUnderConstruction::new(LogicalVec2 {
2024 inline: self.inline_start_for_first_line(containing_block.into()),
2025 block: Au::zero(),
2026 }),
2027 root_nesting_level: InlineContainerState::new(
2028 style.to_arc(),
2029 inline_container_state_flags,
2030 None, self.default_font.clone(),
2032 ),
2033 inline_box_state_stack: Vec::new(),
2034 inline_box_states: Vec::with_capacity(self.inline_boxes.len()),
2035 current_line_segment: UnbreakableSegmentUnderConstruction::new(),
2036 force_line_break_before_new_content: None,
2037 deferred_br_clear: Clear::None,
2038 have_deferred_soft_wrap_opportunity: false,
2039 depends_on_block_constraints: false,
2040 white_space_collapse: style_text.white_space_collapse,
2041 text_wrap_mode: style_text.text_wrap_mode,
2042 };
2043
2044 for item in self.inline_items.iter() {
2045 if !matches!(item, InlineItem::EndInlineBox) {
2047 layout.possibly_flush_deferred_forced_line_break();
2048 }
2049
2050 match item {
2051 InlineItem::StartInlineBox(inline_box) => {
2052 layout.start_inline_box(&inline_box.borrow());
2053 },
2054 InlineItem::EndInlineBox => layout.finish_inline_box(),
2055 InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout),
2056 InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
2057 atomic_formatting_context.borrow().layout_into_line_items(
2058 &mut layout,
2059 *offset_in_text,
2060 *bidi_level,
2061 );
2062 },
2063 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, _) => {
2064 layout.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
2065 layout.current_inline_box_identifier(),
2066 AbsolutelyPositionedLineItem {
2067 absolutely_positioned_box: positioned_box.clone(),
2068 preceding_line_content_would_produce_phantom_line: layout
2069 .current_line
2070 .is_phantom() &&
2071 layout.current_line_segment.is_phantom(),
2072 },
2073 ));
2074 },
2075 InlineItem::OutOfFlowFloatBox(float_box) => {
2076 float_box.borrow().layout_into_line_items(&mut layout);
2077 },
2078 InlineItem::BlockLevel(block_level) => {
2079 block_level.borrow().layout_into_line_items(&mut layout);
2080 },
2081 }
2082 }
2083
2084 layout.finish_last_line();
2085 let (content_block_size, collapsible_margins_in_children, baselines) =
2086 layout.placement_state.finish();
2087
2088 IndependentFormattingContextLayoutResult {
2089 fragments: layout.fragments,
2090 content_block_size,
2091 collapsible_margins_in_children,
2092 baselines,
2093 depends_on_block_constraints: layout.depends_on_block_constraints,
2094 content_inline_size_for_table: None,
2095 specific_layout_info: None,
2096 }
2097 }
2098
2099 fn next_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
2100 let Some(character) = self.text_content[index..].chars().nth(1) else {
2101 return false;
2102 };
2103 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2104 }
2105
2106 fn previous_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
2107 let Some(character) = self.text_content[0..index].chars().next_back() else {
2108 return false;
2109 };
2110 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2111 }
2112
2113 pub(crate) fn find_block_margin_collapsing_with_parent(
2114 &self,
2115 layout_context: &LayoutContext,
2116 collected_margin: &mut CollapsedMargin,
2117 containing_block_for_children: &ContainingBlock,
2118 ) -> bool {
2119 let mut nesting_levels_from_nonzero_end_pbm: u32 = 1;
2125 let mut items_iter = self.inline_items.iter();
2126 items_iter.all(|inline_item| match inline_item {
2127 InlineItem::StartInlineBox(inline_box) => {
2128 let pbm = inline_box
2129 .borrow()
2130 .layout_style()
2131 .padding_border_margin(containing_block_for_children);
2132 if pbm.padding.inline_end.is_zero() &&
2133 pbm.border.inline_end.is_zero() &&
2134 pbm.margin.inline_end.auto_is(Au::zero).is_zero()
2135 {
2136 nesting_levels_from_nonzero_end_pbm += 1;
2137 } else {
2138 nesting_levels_from_nonzero_end_pbm = 0;
2139 }
2140 pbm.padding.inline_start.is_zero() &&
2141 pbm.border.inline_start.is_zero() &&
2142 pbm.margin.inline_start.auto_is(Au::zero).is_zero()
2143 },
2144 InlineItem::EndInlineBox => {
2145 if nesting_levels_from_nonzero_end_pbm == 0 {
2146 false
2147 } else {
2148 nesting_levels_from_nonzero_end_pbm -= 1;
2149 true
2150 }
2151 },
2152 InlineItem::TextRun(text_run) => {
2153 let text_run = &*text_run.borrow();
2154 let parent_style = text_run.inline_styles.style.borrow();
2155 text_run.items.iter().all(|item| match item {
2156 TextRunItem::LineBreak { .. } => false,
2157 TextRunItem::Tab { .. } => false,
2158 TextRunItem::TextSegment(segment) => segment.runs.iter().all(|run| {
2159 run.is_whitespace() &&
2160 !matches!(
2161 parent_style.get_inherited_text().white_space_collapse,
2162 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
2163 )
2164 }),
2165 })
2166 },
2167 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => true,
2168 InlineItem::OutOfFlowFloatBox(..) => true,
2169 InlineItem::Atomic(..) => false,
2170 InlineItem::BlockLevel(block_level) => block_level
2171 .borrow()
2172 .find_block_margin_collapsing_with_parent(
2173 layout_context,
2174 collected_margin,
2175 containing_block_for_children,
2176 ),
2177 })
2178 }
2179
2180 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
2181 let mut parent_box_stack = Vec::new();
2182 let current_parent_box = |parent_box_stack: &[WeakLayoutBox]| {
2183 parent_box_stack.last().unwrap_or(&layout_box).clone()
2184 };
2185 for inline_item in &self.inline_items {
2186 match inline_item {
2187 InlineItem::StartInlineBox(inline_box) => {
2188 inline_box
2189 .borrow_mut()
2190 .base
2191 .parent_box
2192 .replace(current_parent_box(&parent_box_stack));
2193 parent_box_stack.push(WeakLayoutBox::InlineLevel(
2194 WeakInlineItem::StartInlineBox(inline_box.downgrade()),
2195 ));
2196 },
2197 InlineItem::EndInlineBox => {
2198 parent_box_stack.pop();
2199 },
2200 InlineItem::TextRun(text_run) => {
2201 text_run
2202 .borrow_mut()
2203 .parent_box
2204 .replace(current_parent_box(&parent_box_stack));
2205 },
2206 _ => inline_item.with_base_mut(|base| {
2207 base.parent_box
2208 .replace(current_parent_box(&parent_box_stack));
2209 }),
2210 }
2211 }
2212 }
2213
2214 pub(crate) fn next_tab_stop_after_inline_advance(&self, current_inline_advance: Au) -> Au {
2215 let Some(font) = self.default_font.as_ref() else {
2216 return Au::zero();
2217 };
2218
2219 let tab_stop_advance = *self.tab_stop_advance.get_or_init(|| {
2220 let root_style = self.shared_inline_styles.style.borrow();
2221 let inherited_text_style = root_style.get_inherited_text();
2222 let font_size = root_style.get_font().font_size.computed_size().into();
2223 let letter_spacing = inherited_text_style
2224 .letter_spacing
2225 .0
2226 .to_used_value(font_size);
2227 let word_spacing = inherited_text_style.word_spacing.to_used_value(font_size);
2228
2229 match root_style.get_inherited_text().tab_size {
2230 style::values::generics::length::LengthOrNumber::Number(number_of_spaces) => {
2233 (font.metrics.space_advance + word_spacing + letter_spacing)
2234 .scale_by(number_of_spaces.0)
2235 },
2236 style::values::generics::length::LengthOrNumber::Length(length) => length.into(),
2238 }
2239 });
2240
2241 if tab_stop_advance.is_zero() {
2242 return Au::zero();
2243 }
2244
2245 let half_ch_advance = font
2251 .metrics
2252 .zero_horizontal_advance
2253 .unwrap_or(font.metrics.em_size.scale_by(0.5))
2254 .scale_by(0.5);
2255 let number_of_tab_stops =
2256 (current_inline_advance + half_ch_advance).to_f32_px() / tab_stop_advance.to_f32_px();
2257 let number_of_tab_stops = number_of_tab_stops.ceil();
2258 tab_stop_advance.scale_by(number_of_tab_stops) - current_inline_advance
2259 }
2260}
2261
2262impl InlineContainerState {
2263 fn new(
2264 style: ServoArc<ComputedValues>,
2265 flags: InlineContainerStateFlags,
2266 parent_container: Option<&InlineContainerState>,
2267 default_font: Option<FontRef>,
2268 ) -> Self {
2269 let font_metrics = default_font
2270 .as_ref()
2271 .map(|font| font.metrics.clone())
2272 .unwrap_or_else(FontMetrics::empty);
2273 let mut baseline_offset = Au::zero();
2274 let mut strut_block_sizes = {
2275 Self::get_block_sizes_with_style(
2276 effective_baseline_shift(&style, parent_container),
2277 &style,
2278 &font_metrics,
2279 &font_metrics,
2280 &flags,
2281 )
2282 };
2283
2284 if let Some(parent_container) = parent_container {
2285 baseline_offset = parent_container.get_cumulative_baseline_offset_for_child(
2288 style.clone_alignment_baseline(),
2289 style.clone_baseline_shift(),
2290 &strut_block_sizes,
2291 );
2292 strut_block_sizes.adjust_for_baseline_offset(baseline_offset);
2293 }
2294
2295 let mut nested_block_sizes = parent_container
2296 .map(|container| container.nested_strut_block_sizes.clone())
2297 .unwrap_or_else(LineBlockSizes::zero);
2298 if flags.contains(InlineContainerStateFlags::CREATE_STRUT) {
2299 nested_block_sizes.max_assign(&strut_block_sizes);
2300 }
2301
2302 Self {
2303 style,
2304 flags,
2305 has_content: RefCell::new(false),
2306 nested_strut_block_sizes: nested_block_sizes,
2307 strut_block_sizes,
2308 baseline_offset,
2309 default_font,
2310 font_metrics,
2311 }
2312 }
2313
2314 fn get_block_sizes_with_style(
2315 baseline_shift: BaselineShift,
2316 style: &ComputedValues,
2317 font_metrics: &FontMetrics,
2318 font_metrics_of_first_font: &FontMetrics,
2319 flags: &InlineContainerStateFlags,
2320 ) -> LineBlockSizes {
2321 let line_height = line_height(style, font_metrics, flags);
2322
2323 if !is_baseline_relative(baseline_shift) {
2324 return LineBlockSizes {
2325 line_height,
2326 baseline_relative_size_for_line_height: None,
2327 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2328 };
2329 }
2330
2331 let mut ascent = font_metrics.ascent;
2340 let mut descent = font_metrics.descent;
2341 if style.get_font().line_height == LineHeight::Normal {
2342 let half_leading_from_line_gap =
2343 (font_metrics.line_gap - descent - ascent).scale_by(0.5);
2344 ascent += half_leading_from_line_gap;
2345 descent += half_leading_from_line_gap;
2346 }
2347
2348 let size_for_baseline_positioning = BaselineRelativeSize { ascent, descent };
2352
2353 if style.get_font().line_height != LineHeight::Normal {
2369 ascent = font_metrics_of_first_font.ascent;
2370 descent = font_metrics_of_first_font.descent;
2371 let half_leading = (line_height - (ascent + descent)).scale_by(0.5);
2372 ascent += half_leading;
2377 descent = line_height - ascent;
2378 }
2379
2380 LineBlockSizes {
2381 line_height,
2382 baseline_relative_size_for_line_height: Some(BaselineRelativeSize { ascent, descent }),
2383 size_for_baseline_positioning,
2384 }
2385 }
2386
2387 fn get_block_size_contribution(
2388 &self,
2389 baseline_shift: BaselineShift,
2390 font_metrics: &FontMetrics,
2391 font_metrics_of_first_font: &FontMetrics,
2392 ) -> LineBlockSizes {
2393 Self::get_block_sizes_with_style(
2394 baseline_shift,
2395 &self.style,
2396 font_metrics,
2397 font_metrics_of_first_font,
2398 &self.flags,
2399 )
2400 }
2401
2402 fn get_cumulative_baseline_offset_for_child(
2403 &self,
2404 child_alignment_baseline: AlignmentBaseline,
2405 child_baseline_shift: BaselineShift,
2406 child_block_size: &LineBlockSizes,
2407 ) -> Au {
2408 let block_size = self.get_block_size_contribution(
2409 child_baseline_shift.clone(),
2410 &self.font_metrics,
2411 &self.font_metrics,
2412 );
2413 self.baseline_offset +
2414 match child_alignment_baseline {
2415 AlignmentBaseline::Baseline => Au::zero(),
2416 AlignmentBaseline::TextTop => {
2417 child_block_size.size_for_baseline_positioning.ascent - self.font_metrics.ascent
2418 },
2419 AlignmentBaseline::Middle => {
2420 (child_block_size.size_for_baseline_positioning.ascent -
2423 child_block_size.size_for_baseline_positioning.descent -
2424 self.font_metrics.x_height)
2425 .scale_by(0.5)
2426 },
2427 AlignmentBaseline::TextBottom => {
2428 self.font_metrics.descent -
2429 child_block_size.size_for_baseline_positioning.descent
2430 },
2431 AlignmentBaseline::Alphabetic |
2432 AlignmentBaseline::Ideographic |
2433 AlignmentBaseline::Central |
2434 AlignmentBaseline::Mathematical |
2435 AlignmentBaseline::Hanging => {
2436 unreachable!("Got alignment-baseline value that should be disabled in Stylo")
2437 },
2438 } +
2439 match child_baseline_shift {
2440 BaselineShift::Keyword(
2445 BaselineShiftKeyword::Top |
2446 BaselineShiftKeyword::Bottom |
2447 BaselineShiftKeyword::Center,
2448 ) => Au::zero(),
2449 BaselineShift::Keyword(BaselineShiftKeyword::Sub) => {
2450 block_size.resolve().scale_by(FONT_SUBSCRIPT_OFFSET_RATIO)
2451 },
2452 BaselineShift::Keyword(BaselineShiftKeyword::Super) => {
2453 -block_size.resolve().scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO)
2454 },
2455 BaselineShift::Length(length_percentage) => {
2456 -length_percentage.to_used_value(child_block_size.line_height)
2457 },
2458 }
2459 }
2460}
2461
2462impl IndependentFormattingContext {
2463 fn layout_into_line_items(
2464 &self,
2465 layout: &mut InlineFormattingContextLayout,
2466 offset_in_text: usize,
2467 bidi_level: Level,
2468 ) {
2469 let mut child_positioning_context = PositioningContext::default();
2471 let IndependentFloatOrAtomicLayoutResult {
2472 mut fragment,
2473 baselines,
2474 pbm_sums,
2475 } = self.layout_float_or_atomic_inline(
2476 layout.layout_context,
2477 &mut child_positioning_context,
2478 layout.containing_block(),
2479 );
2480
2481 layout.depends_on_block_constraints |= fragment.base.flags.contains(
2484 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2485 );
2486
2487 let container_writing_mode = layout.containing_block().style.writing_mode;
2489 let pbm_physical_offset = pbm_sums
2490 .start_offset()
2491 .to_physical_size(container_writing_mode);
2492 fragment.base.rect.origin += pbm_physical_offset.to_vector();
2493
2494 fragment = fragment.with_baselines(baselines);
2496
2497 let positioning_context = if self.is_replaced() {
2500 None
2501 } else {
2502 if fragment
2503 .style()
2504 .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
2505 {
2506 child_positioning_context
2507 .layout_collected_children(layout.layout_context, &mut fragment);
2508 }
2509 Some(child_positioning_context)
2510 };
2511
2512 if layout.text_wrap_mode == TextWrapMode::Wrap &&
2513 !layout
2514 .ifc
2515 .previous_character_prevents_soft_wrap_opportunity(offset_in_text)
2516 {
2517 layout.process_soft_wrap_opportunity();
2518 }
2519
2520 let size = pbm_sums.sum() + fragment.base.rect.size.to_logical(container_writing_mode);
2521 let baseline_offset = self
2522 .pick_baseline(&fragment.baselines(container_writing_mode))
2523 .map(|baseline| pbm_sums.block_start + baseline)
2524 .unwrap_or(size.block);
2525
2526 let (block_sizes, baseline_offset_in_parent) =
2527 self.get_block_sizes_and_baseline_offset(layout, size.block, baseline_offset);
2528 layout.update_unbreakable_segment_for_new_content(
2529 &block_sizes,
2530 size.inline,
2531 SegmentContentFlags::empty(),
2532 );
2533
2534 let fragment = ArcRefCell::new(fragment);
2535 self.base.set_fragment(Fragment::Box(fragment.clone()));
2536
2537 layout.push_line_item_to_unbreakable_segment(LineItem::Atomic(
2538 layout.current_inline_box_identifier(),
2539 AtomicLineItem {
2540 fragment,
2541 size,
2542 positioning_context,
2543 baseline_offset_in_parent,
2544 baseline_offset_in_item: baseline_offset,
2545 bidi_level,
2546 },
2547 ));
2548
2549 if !layout
2552 .ifc
2553 .next_character_prevents_soft_wrap_opportunity(offset_in_text)
2554 {
2555 layout.have_deferred_soft_wrap_opportunity = true;
2556 }
2557 }
2558
2559 fn pick_baseline(&self, baselines: &Baselines) -> Option<Au> {
2563 match self.style().clone_baseline_source() {
2564 BaselineSource::First => baselines.first,
2565 BaselineSource::Last => baselines.last,
2566 BaselineSource::Auto if self.is_block_container() => baselines.last,
2567 BaselineSource::Auto => baselines.first,
2568 }
2569 }
2570
2571 fn get_block_sizes_and_baseline_offset(
2572 &self,
2573 ifc: &InlineFormattingContextLayout,
2574 block_size: Au,
2575 baseline_offset_in_content_area: Au,
2576 ) -> (LineBlockSizes, Au) {
2577 let mut contribution = if !is_baseline_relative(self.style().clone_baseline_shift()) {
2578 LineBlockSizes {
2579 line_height: block_size,
2580 baseline_relative_size_for_line_height: None,
2581 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2582 }
2583 } else {
2584 let baseline_relative_size = BaselineRelativeSize {
2585 ascent: baseline_offset_in_content_area,
2586 descent: block_size - baseline_offset_in_content_area,
2587 };
2588 LineBlockSizes {
2589 line_height: block_size,
2590 baseline_relative_size_for_line_height: Some(baseline_relative_size.clone()),
2591 size_for_baseline_positioning: baseline_relative_size,
2592 }
2593 };
2594
2595 let style = self.style();
2596 let baseline_offset = ifc
2597 .current_inline_container_state()
2598 .get_cumulative_baseline_offset_for_child(
2599 style.clone_alignment_baseline(),
2600 style.clone_baseline_shift(),
2601 &contribution,
2602 );
2603 contribution.adjust_for_baseline_offset(baseline_offset);
2604
2605 (contribution, baseline_offset)
2606 }
2607}
2608
2609impl FloatBox {
2610 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
2611 let fragment = ArcRefCell::new(self.layout(
2612 layout.layout_context,
2613 layout.positioning_context,
2614 layout.placement_state.containing_block,
2615 ));
2616
2617 self.contents
2618 .base
2619 .set_fragment(Fragment::Box(fragment.clone()));
2620 layout.push_line_item_to_unbreakable_segment(LineItem::Float(
2621 layout.current_inline_box_identifier(),
2622 FloatLineItem {
2623 fragment,
2624 needs_placement: true,
2625 },
2626 ));
2627 }
2628}
2629
2630fn place_pending_floats(ifc: &mut InlineFormattingContextLayout, line_items: &mut [LineItem]) {
2631 for item in line_items.iter_mut() {
2632 if let LineItem::Float(_, float_line_item) = item {
2633 if float_line_item.needs_placement {
2634 ifc.place_float_fragment(&mut float_line_item.fragment.borrow_mut());
2635 }
2636 }
2637 }
2638}
2639
2640fn line_height(
2641 parent_style: &ComputedValues,
2642 font_metrics: &FontMetrics,
2643 flags: &InlineContainerStateFlags,
2644) -> Au {
2645 let font = parent_style.get_font();
2646 let font_size = font.font_size.computed_size();
2647 let mut line_height = match font.line_height {
2648 LineHeight::Normal => font_metrics.line_gap,
2649 LineHeight::Number(number) => (font_size * number.0).into(),
2650 LineHeight::Length(length) => length.0.into(),
2651 };
2652
2653 if flags.contains(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT) {
2657 line_height.max_assign(font_metrics.line_gap);
2658 }
2659
2660 line_height
2661}
2662
2663fn effective_baseline_shift(
2664 style: &ComputedValues,
2665 container: Option<&InlineContainerState>,
2666) -> BaselineShift {
2667 if container.is_none() {
2668 BaselineShift::zero()
2672 } else {
2673 style.clone_baseline_shift()
2674 }
2675}
2676
2677fn is_baseline_relative(baseline_shift: BaselineShift) -> bool {
2678 !matches!(
2679 baseline_shift,
2680 BaselineShift::Keyword(
2681 BaselineShiftKeyword::Top | BaselineShiftKeyword::Bottom | BaselineShiftKeyword::Center
2682 )
2683 )
2684}
2685
2686fn inline_container_needs_strut(
2712 style: &ComputedValues,
2713 layout_context: &LayoutContext,
2714 pbm: Option<&PaddingBorderMargin>,
2715) -> bool {
2716 if layout_context.style_context.quirks_mode() == QuirksMode::NoQuirks {
2717 return true;
2718 }
2719
2720 if style.get_box().display.is_list_item() {
2723 return true;
2724 }
2725
2726 pbm.is_some_and(|pbm| !pbm.padding_border_sums.inline.is_zero())
2727}
2728
2729impl ComputeInlineContentSizes for InlineFormattingContext {
2730 fn compute_inline_content_sizes(
2734 &self,
2735 layout_context: &LayoutContext,
2736 constraint_space: &ConstraintSpace,
2737 ) -> InlineContentSizesResult {
2738 ContentSizesComputation::compute(self, layout_context, constraint_space)
2739 }
2740}
2741
2742struct ContentSizesComputation<'layout_data> {
2744 layout_context: &'layout_data LayoutContext<'layout_data>,
2745 constraint_space: &'layout_data ConstraintSpace<'layout_data>,
2746 paragraph: ContentSizes,
2747 current_line: ContentSizes,
2748 pending_whitespace: ContentSizes,
2750 uncleared_floats: LogicalSides1D<ContentSizes>,
2752 cleared_floats: LogicalSides1D<ContentSizes>,
2754 had_content_yet_for_min_content: bool,
2757 had_content_yet_for_max_content: bool,
2760 ending_inline_pbm_stack: Vec<Au>,
2763 depends_on_block_constraints: bool,
2765}
2766
2767impl<'layout_data> ContentSizesComputation<'layout_data> {
2768 fn traverse(
2769 mut self,
2770 inline_formatting_context: &InlineFormattingContext,
2771 ) -> InlineContentSizesResult {
2772 self.add_inline_size(
2773 inline_formatting_context.inline_start_for_first_line(self.constraint_space.into()),
2774 );
2775 for inline_item in &inline_formatting_context.inline_items {
2776 self.process_item(inline_item, inline_formatting_context);
2777 }
2778 self.forced_line_break();
2779 self.flush_floats();
2780
2781 InlineContentSizesResult {
2782 sizes: self.paragraph,
2783 depends_on_block_constraints: self.depends_on_block_constraints,
2784 }
2785 }
2786
2787 fn process_item(
2788 &mut self,
2789 inline_item: &InlineItem,
2790 inline_formatting_context: &InlineFormattingContext,
2791 ) {
2792 match inline_item {
2793 InlineItem::StartInlineBox(inline_box) => {
2794 let inline_box = inline_box.borrow();
2798 let zero = Au::zero();
2799 let writing_mode = self.constraint_space.style.writing_mode;
2800 let layout_style = inline_box.layout_style();
2801 let padding = layout_style
2802 .padding(writing_mode)
2803 .percentages_relative_to(zero);
2804 let border = layout_style.border_width(writing_mode);
2805 let margin = inline_box
2806 .base
2807 .style
2808 .margin(writing_mode)
2809 .percentages_relative_to(zero)
2810 .auto_is(Au::zero);
2811
2812 let pbm = margin + padding + border;
2813 self.add_inline_size(pbm.inline_start);
2814 self.ending_inline_pbm_stack.push(pbm.inline_end);
2815 },
2816 InlineItem::EndInlineBox => {
2817 let length = self.ending_inline_pbm_stack.pop().unwrap_or_else(Au::zero);
2818 self.add_inline_size(length);
2819 },
2820 InlineItem::TextRun(text_run) => {
2821 let text_run = &*text_run.borrow();
2822 let parent_style = text_run.inline_styles.style.borrow();
2823 for item in text_run.items.iter() {
2824 match item {
2825 TextRunItem::LineBreak { .. } => {
2826 self.forced_line_break();
2829 },
2830 TextRunItem::Tab { .. } => {
2831 self.process_preserved_tab(&parent_style, inline_formatting_context)
2832 },
2833 TextRunItem::TextSegment(segment) => {
2834 self.process_text_segment(&parent_style, segment)
2835 },
2836 }
2837 }
2838 },
2839 InlineItem::Atomic(atomic, offset_in_text, _level) => {
2840 if self.had_content_yet_for_min_content &&
2842 !inline_formatting_context
2843 .previous_character_prevents_soft_wrap_opportunity(*offset_in_text)
2844 {
2845 self.line_break_opportunity();
2846 }
2847
2848 self.commit_pending_whitespace();
2849 let outer = self.outer_inline_content_sizes_of_float_or_atomic(&atomic.borrow());
2850 self.current_line += outer;
2851
2852 if !inline_formatting_context
2854 .next_character_prevents_soft_wrap_opportunity(*offset_in_text)
2855 {
2856 self.line_break_opportunity();
2857 }
2858 },
2859 InlineItem::OutOfFlowFloatBox(float_box) => {
2860 let float_box = float_box.borrow();
2861 let sizes = self.outer_inline_content_sizes_of_float_or_atomic(&float_box.contents);
2862 let style = &float_box.contents.style();
2863 let container_writing_mode = self.constraint_space.style.writing_mode;
2864 let clear =
2865 Clear::from_style_and_container_writing_mode(style, container_writing_mode);
2866 self.clear_floats(clear);
2867 let float_side =
2868 FloatSide::from_style_and_container_writing_mode(style, container_writing_mode);
2869 match float_side.expect("A float box needs to float to some side") {
2870 FloatSide::InlineStart => self.uncleared_floats.start.union_assign(&sizes),
2871 FloatSide::InlineEnd => self.uncleared_floats.end.union_assign(&sizes),
2872 }
2873 },
2874 InlineItem::BlockLevel(block_level) => {
2875 self.forced_line_break();
2876 self.flush_floats();
2877 let inline_content_sizes_result =
2878 compute_inline_content_sizes_for_block_level_boxes(
2879 std::slice::from_ref(block_level),
2880 self.layout_context,
2881 &self.constraint_space.into(),
2882 );
2883 self.depends_on_block_constraints |=
2884 inline_content_sizes_result.depends_on_block_constraints;
2885 self.current_line = inline_content_sizes_result.sizes;
2886 self.forced_line_break();
2887 },
2888 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => {},
2889 }
2890 }
2891
2892 fn process_text_segment(
2893 &mut self,
2894 parent_style: &AtomicRef<'_, ServoArc<ComputedValues>>,
2895 segment: &TextRunSegment,
2896 ) {
2897 let style_text = parent_style.get_inherited_text();
2898 let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap;
2899
2900 let break_at_start = segment.break_at_start && self.had_content_yet_for_min_content;
2903
2904 for (run_index, run) in segment.runs.iter().enumerate() {
2905 if can_wrap && (run_index != 0 || break_at_start) {
2908 self.line_break_opportunity();
2909 }
2910
2911 let advance = run.total_advance();
2912 if run.is_whitespace() {
2913 if !matches!(
2914 style_text.white_space_collapse,
2915 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
2916 ) {
2917 if self.had_content_yet_for_min_content {
2918 if can_wrap {
2919 self.line_break_opportunity();
2920 } else {
2921 self.pending_whitespace.min_content += advance;
2922 }
2923 }
2924 if self.had_content_yet_for_max_content {
2925 self.pending_whitespace.max_content += advance;
2926 }
2927 continue;
2928 }
2929 if can_wrap {
2930 self.pending_whitespace.max_content += advance;
2931 self.commit_pending_whitespace();
2932 self.line_break_opportunity();
2933 continue;
2934 }
2935 }
2936
2937 self.commit_pending_whitespace();
2938 self.add_inline_size(advance);
2939
2940 if can_wrap && run.ends_with_whitespace() {
2945 self.line_break_opportunity();
2946 }
2947 }
2948 }
2949
2950 fn process_preserved_tab(
2951 &mut self,
2952 parent_style: &AtomicRef<'_, ServoArc<ComputedValues>>,
2953 inline_formatting_context: &InlineFormattingContext,
2954 ) {
2955 self.commit_pending_whitespace();
2957
2958 self.current_line.min_content += inline_formatting_context
2959 .next_tab_stop_after_inline_advance(self.current_line.min_content);
2960 self.current_line.max_content += inline_formatting_context
2961 .next_tab_stop_after_inline_advance(self.current_line.max_content);
2962 if parent_style.get_inherited_text().text_wrap_mode == TextWrapMode::Wrap {
2963 self.line_break_opportunity();
2964 }
2965 }
2966
2967 fn add_inline_size(&mut self, l: Au) {
2968 self.current_line.min_content += l;
2969 self.current_line.max_content += l;
2970 }
2971
2972 fn line_break_opportunity(&mut self) {
2973 self.pending_whitespace.min_content = Au::zero();
2977 let current_min_content = mem::take(&mut self.current_line.min_content);
2978 self.paragraph.min_content.max_assign(current_min_content);
2979 self.had_content_yet_for_min_content = false;
2980 }
2981
2982 fn forced_line_break(&mut self) {
2983 self.line_break_opportunity();
2985
2986 self.pending_whitespace.max_content = Au::zero();
2988 let current_max_content = mem::take(&mut self.current_line.max_content);
2989 self.paragraph.max_content.max_assign(current_max_content);
2990 self.had_content_yet_for_max_content = false;
2991 }
2992
2993 fn commit_pending_whitespace(&mut self) {
2994 self.current_line += mem::take(&mut self.pending_whitespace);
2995 self.had_content_yet_for_min_content = true;
2996 self.had_content_yet_for_max_content = true;
2997 }
2998
2999 fn outer_inline_content_sizes_of_float_or_atomic(
3000 &mut self,
3001 context: &IndependentFormattingContext,
3002 ) -> ContentSizes {
3003 let result = context.outer_inline_content_sizes(
3004 self.layout_context,
3005 &self.constraint_space.into(),
3006 &LogicalVec2::zero(),
3007 false, );
3009 self.depends_on_block_constraints |= result.depends_on_block_constraints;
3010 result.sizes
3011 }
3012
3013 fn clear_floats(&mut self, clear: Clear) {
3014 match clear {
3015 Clear::InlineStart => {
3016 let start_floats = mem::take(&mut self.uncleared_floats.start);
3017 self.cleared_floats.start.max_assign(start_floats);
3018 },
3019 Clear::InlineEnd => {
3020 let end_floats = mem::take(&mut self.uncleared_floats.end);
3021 self.cleared_floats.end.max_assign(end_floats);
3022 },
3023 Clear::Both => {
3024 let start_floats = mem::take(&mut self.uncleared_floats.start);
3025 let end_floats = mem::take(&mut self.uncleared_floats.end);
3026 self.cleared_floats.start.max_assign(start_floats);
3027 self.cleared_floats.end.max_assign(end_floats);
3028 },
3029 Clear::None => {},
3030 }
3031 }
3032
3033 fn flush_floats(&mut self) {
3034 self.clear_floats(Clear::Both);
3035 let start_floats = mem::take(&mut self.cleared_floats.start);
3036 let end_floats = mem::take(&mut self.cleared_floats.end);
3037 self.paragraph.union_assign(&start_floats);
3038 self.paragraph.union_assign(&end_floats);
3039 }
3040
3041 fn compute(
3043 inline_formatting_context: &InlineFormattingContext,
3044 layout_context: &'layout_data LayoutContext,
3045 constraint_space: &'layout_data ConstraintSpace,
3046 ) -> InlineContentSizesResult {
3047 Self {
3048 layout_context,
3049 constraint_space,
3050 paragraph: ContentSizes::zero(),
3051 current_line: ContentSizes::zero(),
3052 pending_whitespace: ContentSizes::zero(),
3053 uncleared_floats: LogicalSides1D::default(),
3054 cleared_floats: LogicalSides1D::default(),
3055 had_content_yet_for_min_content: false,
3056 had_content_yet_for_max_content: false,
3057 ending_inline_pbm_stack: Vec::new(),
3058 depends_on_block_constraints: false,
3059 }
3060 .traverse(inline_formatting_context)
3061 }
3062}
3063
3064fn char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character: char) -> bool {
3076 if character == '\u{00A0}' {
3077 return false;
3078 }
3079 matches!(
3080 icu_properties::maps::line_break().get(character),
3081 ICULineBreak::Glue | ICULineBreak::WordJoiner | ICULineBreak::ZWJ
3082 )
3083}