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;
81
82use app_units::{Au, MAX_AU};
83use bitflags::bitflags;
84use construct::InlineFormattingContextBuilder;
85use fonts::{FontMetrics, GlyphStore};
86use icu_locid::LanguageIdentifier;
87use icu_locid::subtags::{Language, language};
88use icu_properties::{self, LineBreak as ICULineBreak};
89use icu_segmenter::{LineBreakOptions, LineBreakStrictness, LineBreakWordOption};
90use inline_box::{InlineBox, InlineBoxContainerState, InlineBoxIdentifier, InlineBoxes};
91use layout_api::{LayoutNode, SharedSelection};
92use line::{
93 AbsolutelyPositionedLineItem, AtomicLineItem, FloatLineItem, LineItem, LineItemLayout,
94 TextRunLineItem,
95};
96use line_breaker::LineBreaker;
97use malloc_size_of_derive::MallocSizeOf;
98use script::layout_dom::ServoLayoutNode;
99use servo_arc::Arc as ServoArc;
100use style::Zero;
101use style::computed_values::line_break::T as LineBreak;
102use style::computed_values::text_wrap_mode::T as TextWrapMode;
103use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
104use style::computed_values::word_break::T as WordBreak;
105use style::context::{QuirksMode, SharedStyleContext};
106use style::properties::ComputedValues;
107use style::properties::style_structs::InheritedText;
108use style::values::computed::BaselineShift;
109use style::values::generics::box_::BaselineShiftKeyword;
110use style::values::generics::font::LineHeight;
111use style::values::specified::box_::BaselineSource;
112use style::values::specified::text::TextAlignKeyword;
113use style::values::specified::{AlignmentBaseline, TextAlignLast, TextJustify};
114use text_run::{TextRun, get_font_for_first_font_for_style};
115use unicode_bidi::{BidiInfo, Level};
116
117use super::float::{Clear, PlacementAmongFloats};
118use super::{IndependentFloatOrAtomicLayoutResult, IndependentFormattingContextLayoutResult};
119use crate::cell::{ArcRefCell, WeakRefCell};
120use crate::context::LayoutContext;
121use crate::dom::WeakLayoutBox;
122use crate::dom_traversal::NodeAndStyleInfo;
123use crate::flow::float::{FloatBox, SequentialLayoutState};
124use crate::flow::inline::line::TextRunOffsets;
125use crate::flow::inline::text_run::FontAndScriptInfo;
126use crate::flow::{
127 BlockLevelBox, CollapsibleWithParentStartMargin, FloatSide, PlacementState,
128 compute_inline_content_sizes_for_block_level_boxes, layout_block_level_child,
129};
130use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
131use crate::fragment_tree::{
132 BoxFragment, CollapsedMargin, Fragment, FragmentFlags, PositioningFragment,
133};
134use crate::geom::{LogicalRect, LogicalSides1D, LogicalVec2, ToLogical};
135use crate::layout_box_base::LayoutBoxBase;
136use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
137use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
138use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
139use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SharedStyle};
140
141static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
143static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
144
145#[derive(Debug, MallocSizeOf)]
146pub(crate) struct InlineFormattingContext {
147 inline_items: Vec<InlineItem>,
152
153 inline_boxes: InlineBoxes,
156
157 text_content: String,
159
160 shared_inline_styles: SharedInlineStyles,
163
164 has_first_formatted_line: bool,
167
168 pub(super) contains_floats: bool,
170
171 is_single_line_text_input: bool,
174
175 has_right_to_left_content: bool,
178
179 #[ignore_malloc_size_of = "This is stored primarily in the DOM"]
182 shared_selection: Option<SharedSelection>,
183}
184
185#[derive(Clone, Debug, MallocSizeOf)]
190pub(crate) struct SharedInlineStyles {
191 pub style: SharedStyle,
192 pub selected: SharedStyle,
193}
194
195impl SharedInlineStyles {
196 pub(crate) fn ptr_eq(&self, other: &Self) -> bool {
197 self.style.ptr_eq(&other.style) && self.selected.ptr_eq(&other.selected)
198 }
199
200 pub(crate) fn from_info_and_context(info: &NodeAndStyleInfo, context: &LayoutContext) -> Self {
201 Self {
202 style: SharedStyle::new(info.style.clone()),
203 selected: SharedStyle::new(info.node.selected_style(&context.style_context)),
204 }
205 }
206}
207
208impl BlockLevelBox {
209 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
210 layout.process_soft_wrap_opportunity();
211 layout.commit_current_segment_to_line();
212 layout.process_line_break(true);
213 layout.current_line.for_block_level = true;
214
215 let fragment = layout_block_level_child(
216 layout.layout_context,
217 layout.positioning_context,
218 self,
219 layout.sequential_layout_state.as_deref_mut(),
220 &mut layout.placement_state,
221 LogicalSides1D::new(false, false),
223 true, );
225
226 let Fragment::Box(fragment) = fragment else {
227 unreachable!("The fragment should be a Fragment::Box()");
228 };
229
230 layout.depends_on_block_constraints |= fragment.borrow().base.flags.contains(
233 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
234 );
235
236 layout.push_line_item_to_unbreakable_segment(LineItem::BlockLevel(
237 layout.current_inline_box_identifier(),
238 fragment,
239 ));
240
241 layout.commit_current_segment_to_line();
242 layout.process_line_break(true);
243 layout.current_line.for_block_level = false;
244 }
245}
246
247#[derive(Clone, Debug, MallocSizeOf)]
248pub(crate) enum InlineItem {
249 StartInlineBox(ArcRefCell<InlineBox>),
250 EndInlineBox,
251 TextRun(ArcRefCell<TextRun>),
252 OutOfFlowAbsolutelyPositionedBox(
253 ArcRefCell<AbsolutelyPositionedBox>,
254 usize, ),
256 OutOfFlowFloatBox(ArcRefCell<FloatBox>),
257 Atomic(
258 ArcRefCell<IndependentFormattingContext>,
259 usize, Level, ),
262 BlockLevel(ArcRefCell<BlockLevelBox>),
263}
264
265impl InlineItem {
266 pub(crate) fn repair_style(
267 &self,
268 context: &SharedStyleContext,
269 node: &ServoLayoutNode,
270 new_style: &ServoArc<ComputedValues>,
271 ) {
272 match self {
273 InlineItem::StartInlineBox(inline_box) => {
274 inline_box
275 .borrow_mut()
276 .repair_style(context, node, new_style);
277 },
278 InlineItem::EndInlineBox => {},
279 InlineItem::TextRun(..) => {},
282 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box
283 .borrow_mut()
284 .context
285 .repair_style(context, node, new_style),
286 InlineItem::OutOfFlowFloatBox(float_box) => float_box
287 .borrow_mut()
288 .contents
289 .repair_style(context, node, new_style),
290 InlineItem::Atomic(atomic, ..) => {
291 atomic.borrow_mut().repair_style(context, node, new_style)
292 },
293 InlineItem::BlockLevel(block_level) => block_level
294 .borrow_mut()
295 .repair_style(context, node, new_style),
296 }
297 }
298
299 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
300 match self {
301 InlineItem::StartInlineBox(inline_box) => callback(&inline_box.borrow().base),
302 InlineItem::EndInlineBox | InlineItem::TextRun(..) => {
303 unreachable!("Should never have these kind of fragments attached to a DOM node")
304 },
305 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
306 callback(&positioned_box.borrow().context.base)
307 },
308 InlineItem::OutOfFlowFloatBox(float_box) => callback(&float_box.borrow().contents.base),
309 InlineItem::Atomic(independent_formatting_context, ..) => {
310 callback(&independent_formatting_context.borrow().base)
311 },
312 InlineItem::BlockLevel(block_level) => block_level.borrow().with_base(callback),
313 }
314 }
315
316 pub(crate) fn with_base_mut<T>(&self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
317 match self {
318 InlineItem::StartInlineBox(inline_box) => callback(&mut inline_box.borrow_mut().base),
319 InlineItem::EndInlineBox | InlineItem::TextRun(..) => {
320 unreachable!("Should never have these kind of fragments attached to a DOM node")
321 },
322 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
323 callback(&mut positioned_box.borrow_mut().context.base)
324 },
325 InlineItem::OutOfFlowFloatBox(float_box) => {
326 callback(&mut float_box.borrow_mut().contents.base)
327 },
328 InlineItem::Atomic(independent_formatting_context, ..) => {
329 callback(&mut independent_formatting_context.borrow_mut().base)
330 },
331 InlineItem::BlockLevel(block_level) => block_level.borrow_mut().with_base_mut(callback),
332 }
333 }
334
335 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
336 match self {
337 Self::StartInlineBox(_) | InlineItem::EndInlineBox => {
338 },
341 Self::TextRun(_) => {
342 },
344 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
345 positioned_box.borrow().context.attached_to_tree(layout_box)
346 },
347 Self::OutOfFlowFloatBox(float_box) => {
348 float_box.borrow().contents.attached_to_tree(layout_box)
349 },
350 Self::Atomic(atomic, ..) => atomic.borrow().attached_to_tree(layout_box),
351 Self::BlockLevel(block_level) => block_level.borrow().attached_to_tree(layout_box),
352 }
353 }
354
355 pub(crate) fn downgrade(&self) -> WeakInlineItem {
356 match self {
357 Self::StartInlineBox(inline_box) => {
358 WeakInlineItem::StartInlineBox(inline_box.downgrade())
359 },
360 Self::EndInlineBox => WeakInlineItem::EndInlineBox,
361 Self::TextRun(text_run) => WeakInlineItem::TextRun(text_run.downgrade()),
362 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
363 WeakInlineItem::OutOfFlowAbsolutelyPositionedBox(
364 positioned_box.downgrade(),
365 *offset_in_text,
366 )
367 },
368 Self::OutOfFlowFloatBox(float_box) => {
369 WeakInlineItem::OutOfFlowFloatBox(float_box.downgrade())
370 },
371 Self::Atomic(atomic, offset_in_text, bidi_level) => {
372 WeakInlineItem::Atomic(atomic.downgrade(), *offset_in_text, *bidi_level)
373 },
374 Self::BlockLevel(block_level) => WeakInlineItem::BlockLevel(block_level.downgrade()),
375 }
376 }
377}
378
379#[derive(Clone, Debug, MallocSizeOf)]
380pub(crate) enum WeakInlineItem {
381 StartInlineBox(WeakRefCell<InlineBox>),
382 EndInlineBox,
383 TextRun(WeakRefCell<TextRun>),
384 OutOfFlowAbsolutelyPositionedBox(
385 WeakRefCell<AbsolutelyPositionedBox>,
386 usize, ),
388 OutOfFlowFloatBox(WeakRefCell<FloatBox>),
389 Atomic(
390 WeakRefCell<IndependentFormattingContext>,
391 usize, Level, ),
394 BlockLevel(WeakRefCell<BlockLevelBox>),
395}
396
397impl WeakInlineItem {
398 pub(crate) fn upgrade(&self) -> Option<InlineItem> {
399 Some(match self {
400 Self::StartInlineBox(inline_box) => InlineItem::StartInlineBox(inline_box.upgrade()?),
401 Self::EndInlineBox => InlineItem::EndInlineBox,
402 Self::TextRun(text_run) => InlineItem::TextRun(text_run.upgrade()?),
403 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
404 InlineItem::OutOfFlowAbsolutelyPositionedBox(
405 positioned_box.upgrade()?,
406 *offset_in_text,
407 )
408 },
409 Self::OutOfFlowFloatBox(float_box) => {
410 InlineItem::OutOfFlowFloatBox(float_box.upgrade()?)
411 },
412 Self::Atomic(atomic, offset_in_text, bidi_level) => {
413 InlineItem::Atomic(atomic.upgrade()?, *offset_in_text, *bidi_level)
414 },
415 Self::BlockLevel(block_level) => InlineItem::BlockLevel(block_level.upgrade()?),
416 })
417 }
418}
419
420struct LineUnderConstruction {
427 start_position: LogicalVec2<Au>,
430
431 inline_position: Au,
434
435 max_block_size: LineBlockSizes,
439
440 has_content: bool,
443
444 has_inline_pbm: bool,
447
448 has_floats_waiting_to_be_placed: bool,
452
453 placement_among_floats: OnceCell<LogicalRect<Au>>,
458
459 line_items: Vec<LineItem>,
462
463 for_block_level: bool,
465}
466
467impl LineUnderConstruction {
468 fn new(start_position: LogicalVec2<Au>) -> Self {
469 Self {
470 inline_position: start_position.inline,
471 start_position,
472 max_block_size: LineBlockSizes::zero(),
473 has_content: false,
474 has_inline_pbm: false,
475 has_floats_waiting_to_be_placed: false,
476 placement_among_floats: OnceCell::new(),
477 line_items: Vec::new(),
478 for_block_level: false,
479 }
480 }
481
482 fn replace_placement_among_floats(&mut self, new_placement: LogicalRect<Au>) {
483 self.placement_among_floats.take();
484 let _ = self.placement_among_floats.set(new_placement);
485 }
486
487 fn trim_trailing_whitespace(&mut self) -> Au {
489 let mut whitespace_trimmed = Au::zero();
494 for item in self.line_items.iter_mut().rev() {
495 if !item.trim_whitespace_at_end(&mut whitespace_trimmed) {
496 break;
497 }
498 }
499
500 whitespace_trimmed
501 }
502
503 fn count_justification_opportunities(&self) -> usize {
505 self.line_items
506 .iter()
507 .filter_map(|item| match item {
508 LineItem::TextRun(_, text_run) => Some(
509 text_run
510 .text
511 .iter()
512 .map(|glyph_store| glyph_store.total_word_separators())
513 .sum::<usize>(),
514 ),
515 _ => None,
516 })
517 .sum()
518 }
519
520 fn is_phantom(&self) -> bool {
523 !self.has_content && !self.has_inline_pbm
525 }
526}
527
528#[derive(Clone, Debug)]
534struct BaselineRelativeSize {
535 ascent: Au,
539
540 descent: Au,
544}
545
546impl BaselineRelativeSize {
547 fn zero() -> Self {
548 Self {
549 ascent: Au::zero(),
550 descent: Au::zero(),
551 }
552 }
553
554 fn max(&self, other: &Self) -> Self {
555 BaselineRelativeSize {
556 ascent: self.ascent.max(other.ascent),
557 descent: self.descent.max(other.descent),
558 }
559 }
560
561 fn adjust_for_nested_baseline_offset(&mut self, baseline_offset: Au) {
575 self.ascent -= baseline_offset;
576 self.descent += baseline_offset;
577 }
578}
579
580#[derive(Clone, Debug)]
581struct LineBlockSizes {
582 line_height: Au,
583 baseline_relative_size_for_line_height: Option<BaselineRelativeSize>,
584 size_for_baseline_positioning: BaselineRelativeSize,
585}
586
587impl LineBlockSizes {
588 fn zero() -> Self {
589 LineBlockSizes {
590 line_height: Au::zero(),
591 baseline_relative_size_for_line_height: None,
592 size_for_baseline_positioning: BaselineRelativeSize::zero(),
593 }
594 }
595
596 fn resolve(&self) -> Au {
597 let height_from_ascent_and_descent = self
598 .baseline_relative_size_for_line_height
599 .as_ref()
600 .map(|size| (size.ascent + size.descent).abs())
601 .unwrap_or_else(Au::zero);
602 self.line_height.max(height_from_ascent_and_descent)
603 }
604
605 fn max(&self, other: &LineBlockSizes) -> LineBlockSizes {
606 let baseline_relative_size = match (
607 self.baseline_relative_size_for_line_height.as_ref(),
608 other.baseline_relative_size_for_line_height.as_ref(),
609 ) {
610 (Some(our_size), Some(other_size)) => Some(our_size.max(other_size)),
611 (our_size, other_size) => our_size.or(other_size).cloned(),
612 };
613 Self {
614 line_height: self.line_height.max(other.line_height),
615 baseline_relative_size_for_line_height: baseline_relative_size,
616 size_for_baseline_positioning: self
617 .size_for_baseline_positioning
618 .max(&other.size_for_baseline_positioning),
619 }
620 }
621
622 fn max_assign(&mut self, other: &LineBlockSizes) {
623 *self = self.max(other);
624 }
625
626 fn adjust_for_baseline_offset(&mut self, baseline_offset: Au) {
627 if let Some(size) = self.baseline_relative_size_for_line_height.as_mut() {
628 size.adjust_for_nested_baseline_offset(baseline_offset)
629 }
630 self.size_for_baseline_positioning
631 .adjust_for_nested_baseline_offset(baseline_offset);
632 }
633
634 fn find_baseline_offset(&self) -> Au {
641 match self.baseline_relative_size_for_line_height.as_ref() {
642 Some(size) => size.ascent,
643 None => {
644 let leading = self.resolve() -
647 (self.size_for_baseline_positioning.ascent +
648 self.size_for_baseline_positioning.descent);
649 leading.scale_by(0.5) + self.size_for_baseline_positioning.ascent
650 },
651 }
652 }
653}
654
655struct UnbreakableSegmentUnderConstruction {
659 inline_size: Au,
661
662 max_block_size: LineBlockSizes,
665
666 line_items: Vec<LineItem>,
668
669 inline_box_hierarchy_depth: Option<usize>,
672
673 has_content: bool,
677
678 has_inline_pbm: bool,
681
682 trailing_whitespace_size: Au,
684}
685
686impl UnbreakableSegmentUnderConstruction {
687 fn new() -> Self {
688 Self {
689 inline_size: Au::zero(),
690 max_block_size: LineBlockSizes {
691 line_height: Au::zero(),
692 baseline_relative_size_for_line_height: None,
693 size_for_baseline_positioning: BaselineRelativeSize::zero(),
694 },
695 line_items: Vec::new(),
696 inline_box_hierarchy_depth: None,
697 has_content: false,
698 has_inline_pbm: false,
699 trailing_whitespace_size: Au::zero(),
700 }
701 }
702
703 fn reset(&mut self) {
705 assert!(self.line_items.is_empty()); self.inline_size = Au::zero();
707 self.max_block_size = LineBlockSizes::zero();
708 self.inline_box_hierarchy_depth = None;
709 self.has_content = false;
710 self.has_inline_pbm = false;
711 self.trailing_whitespace_size = Au::zero();
712 }
713
714 fn push_line_item(&mut self, line_item: LineItem, inline_box_hierarchy_depth: usize) {
719 if self.line_items.is_empty() {
720 self.inline_box_hierarchy_depth = Some(inline_box_hierarchy_depth);
721 }
722 self.line_items.push(line_item);
723 }
724
725 fn trim_leading_whitespace(&mut self) {
736 let mut whitespace_trimmed = Au::zero();
737 for item in self.line_items.iter_mut() {
738 if !item.trim_whitespace_at_start(&mut whitespace_trimmed) {
739 break;
740 }
741 }
742 self.inline_size -= whitespace_trimmed;
743 }
744
745 fn is_phantom(&self) -> bool {
748 !self.has_content && !self.has_inline_pbm
750 }
751}
752
753bitflags! {
754 struct InlineContainerStateFlags: u8 {
755 const CREATE_STRUT = 0b0001;
756 const IS_SINGLE_LINE_TEXT_INPUT = 0b0010;
757 }
758}
759
760struct InlineContainerState {
761 style: ServoArc<ComputedValues>,
763
764 flags: InlineContainerStateFlags,
766
767 has_content: RefCell<bool>,
770
771 strut_block_sizes: LineBlockSizes,
776
777 nested_strut_block_sizes: LineBlockSizes,
781
782 pub baseline_offset: Au,
788
789 font_metrics: Arc<FontMetrics>,
791}
792
793struct InlineFormattingContextLayout<'layout_data> {
794 positioning_context: &'layout_data mut PositioningContext,
795 placement_state: PlacementState<'layout_data>,
796 sequential_layout_state: Option<&'layout_data mut SequentialLayoutState>,
797 layout_context: &'layout_data LayoutContext<'layout_data>,
798
799 ifc: &'layout_data InlineFormattingContext,
801
802 root_nesting_level: InlineContainerState,
812
813 inline_box_state_stack: Vec<Rc<InlineBoxContainerState>>,
817
818 inline_box_states: Vec<Rc<InlineBoxContainerState>>,
823
824 fragments: Vec<Fragment>,
828
829 current_line: LineUnderConstruction,
831
832 current_line_segment: UnbreakableSegmentUnderConstruction,
834
835 linebreak_before_new_content: bool,
854
855 deferred_br_clear: Clear,
859
860 pub have_deferred_soft_wrap_opportunity: bool,
864
865 depends_on_block_constraints: bool,
868
869 white_space_collapse: WhiteSpaceCollapse,
874
875 text_wrap_mode: TextWrapMode,
880}
881
882impl InlineFormattingContextLayout<'_> {
883 fn current_inline_container_state(&self) -> &InlineContainerState {
884 match self.inline_box_state_stack.last() {
885 Some(inline_box_state) => &inline_box_state.base,
886 None => &self.root_nesting_level,
887 }
888 }
889
890 fn current_inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
891 self.inline_box_state_stack
892 .last()
893 .map(|state| state.identifier)
894 }
895
896 fn current_line_max_block_size_including_nested_containers(&self) -> LineBlockSizes {
897 self.current_inline_container_state()
898 .nested_strut_block_sizes
899 .max(&self.current_line.max_block_size)
900 }
901
902 fn current_line_block_start_considering_placement_among_floats(&self) -> Au {
903 self.current_line.placement_among_floats.get().map_or(
904 self.current_line.start_position.block,
905 |placement_among_floats| placement_among_floats.start_corner.block,
906 )
907 }
908
909 fn propagate_current_nesting_level_white_space_style(&mut self) {
910 let style = match self.inline_box_state_stack.last() {
911 Some(inline_box_state) => &inline_box_state.base.style,
912 None => self.placement_state.containing_block.style,
913 };
914 let style_text = style.get_inherited_text();
915 self.white_space_collapse = style_text.white_space_collapse;
916 self.text_wrap_mode = style_text.text_wrap_mode;
917 }
918
919 fn processing_br_element(&self) -> bool {
920 self.inline_box_state_stack.last().is_some_and(|state| {
921 state
922 .base_fragment_info
923 .flags
924 .contains(FragmentFlags::IS_BR_ELEMENT)
925 })
926 }
927
928 fn start_inline_box(&mut self, inline_box: &InlineBox) {
931 let containing_block = self.containing_block();
932 let inline_box_state = InlineBoxContainerState::new(
933 inline_box,
934 containing_block,
935 self.layout_context,
936 self.current_inline_container_state(),
937 inline_box
938 .default_font
939 .as_ref()
940 .map(|font| font.metrics.clone()),
941 );
942
943 self.depends_on_block_constraints |= inline_box
944 .base
945 .style
946 .depends_on_block_constraints_due_to_relative_positioning(
947 containing_block.style.writing_mode,
948 );
949
950 if inline_box_state
955 .base_fragment_info
956 .flags
957 .contains(FragmentFlags::IS_BR_ELEMENT) &&
958 self.deferred_br_clear == Clear::None
959 {
960 self.deferred_br_clear = Clear::from_style_and_container_writing_mode(
961 &inline_box_state.base.style,
962 self.containing_block().style.writing_mode,
963 );
964 }
965
966 let padding = inline_box_state.pbm.padding.inline_start;
967 let border = inline_box_state.pbm.border.inline_start;
968 let margin = inline_box_state.pbm.margin.inline_start.auto_is(Au::zero);
969 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
972 self.current_line_segment.has_inline_pbm = true;
973 }
974 self.current_line_segment.inline_size += padding + border + margin;
975 self.current_line_segment
976 .line_items
977 .push(LineItem::InlineStartBoxPaddingBorderMargin(
978 inline_box.identifier,
979 ));
980
981 let inline_box_state = Rc::new(inline_box_state);
982
983 assert_eq!(
987 self.inline_box_states.len(),
988 inline_box.identifier.index_in_inline_boxes as usize
989 );
990 self.inline_box_states.push(inline_box_state.clone());
991 self.inline_box_state_stack.push(inline_box_state);
992 }
993
994 fn finish_inline_box(&mut self) {
997 let inline_box_state = match self.inline_box_state_stack.pop() {
998 Some(inline_box_state) => inline_box_state,
999 None => return, };
1001
1002 self.current_line_segment
1003 .max_block_size
1004 .max_assign(&inline_box_state.base.nested_strut_block_sizes);
1005
1006 if *inline_box_state.base.has_content.borrow() {
1011 self.propagate_current_nesting_level_white_space_style();
1012 }
1013
1014 let padding = inline_box_state.pbm.padding.inline_end;
1015 let border = inline_box_state.pbm.border.inline_end;
1016 let margin = inline_box_state.pbm.margin.inline_end.auto_is(Au::zero);
1017 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
1020 self.current_line_segment.has_inline_pbm = true;
1021 }
1022 self.current_line_segment.inline_size += padding + border + margin;
1023 self.current_line_segment
1024 .line_items
1025 .push(LineItem::InlineEndBoxPaddingBorderMargin(
1026 inline_box_state.identifier,
1027 ))
1028 }
1029
1030 fn finish_last_line(&mut self) {
1031 self.process_soft_wrap_opportunity();
1037
1038 self.commit_current_segment_to_line();
1041
1042 self.finish_current_line_and_reset(true );
1045 }
1046
1047 fn finish_current_line_and_reset(&mut self, last_line_or_forced_line_break: bool) {
1051 let whitespace_trimmed = self.current_line.trim_trailing_whitespace();
1052 let (inline_start_position, justification_adjustment) = self
1053 .calculate_current_line_inline_start_and_justification_adjustment(
1054 whitespace_trimmed,
1055 last_line_or_forced_line_break,
1056 );
1057
1058 let is_phantom_line = self.current_line.is_phantom();
1067 if !is_phantom_line {
1068 self.current_line.start_position.block += self.placement_state.current_margin.solve();
1069 self.placement_state.current_margin = CollapsedMargin::zero();
1070 }
1071 let block_start_position =
1072 self.current_line_block_start_considering_placement_among_floats();
1073
1074 let effective_block_advance = if is_phantom_line {
1075 LineBlockSizes::zero()
1076 } else {
1077 self.current_line_max_block_size_including_nested_containers()
1078 };
1079
1080 let resolved_block_advance = effective_block_advance.resolve();
1081 let block_end_position = if self.current_line.for_block_level {
1082 self.placement_state.current_block_direction_position
1083 } else {
1084 let mut block_end_position = block_start_position + resolved_block_advance;
1085 if let Some(sequential_layout_state) = self.sequential_layout_state.as_mut() {
1086 if !is_phantom_line {
1087 sequential_layout_state.commit_margin();
1088 }
1089
1090 let increment = block_end_position - self.current_line.start_position.block;
1093 sequential_layout_state.advance_block_position(increment);
1094
1095 if let Some(clearance) = sequential_layout_state
1099 .calculate_clearance(self.deferred_br_clear, &CollapsedMargin::zero())
1100 {
1101 sequential_layout_state.advance_block_position(clearance);
1102 block_end_position += clearance;
1103 };
1104 self.deferred_br_clear = Clear::None;
1105 }
1106 block_end_position
1107 };
1108
1109 let mut line_to_layout = std::mem::replace(
1111 &mut self.current_line,
1112 LineUnderConstruction::new(LogicalVec2 {
1113 inline: Au::zero(),
1114 block: block_end_position,
1115 }),
1116 );
1117 if !line_to_layout.for_block_level {
1118 self.placement_state.current_block_direction_position = block_end_position;
1119 }
1120
1121 if line_to_layout.has_floats_waiting_to_be_placed {
1122 place_pending_floats(self, &mut line_to_layout.line_items);
1123 }
1124
1125 let start_position = LogicalVec2 {
1126 block: block_start_position,
1127 inline: inline_start_position,
1128 };
1129
1130 let baseline_offset = effective_block_advance.find_baseline_offset();
1131 let start_positioning_context_length = self.positioning_context.len();
1132 let fragments = LineItemLayout::layout_line_items(
1133 self,
1134 line_to_layout.line_items,
1135 start_position,
1136 &effective_block_advance,
1137 justification_adjustment,
1138 is_phantom_line,
1139 );
1140
1141 if !is_phantom_line {
1142 let baseline = baseline_offset + block_start_position;
1143 self.placement_state
1144 .inflow_baselines
1145 .first
1146 .get_or_insert(baseline);
1147 self.placement_state.inflow_baselines.last = Some(baseline);
1148 self.placement_state
1149 .next_in_flow_margin_collapses_with_parent_start_margin = false;
1150 }
1151
1152 if fragments.is_empty() &&
1154 self.positioning_context.len() == start_positioning_context_length
1155 {
1156 return;
1157 }
1158
1159 let start_corner = LogicalVec2 {
1163 inline: Au::zero(),
1164 block: block_start_position,
1165 };
1166
1167 let logical_origin_in_physical_coordinates =
1168 start_corner.to_physical_vector(self.containing_block().style.writing_mode);
1169 self.positioning_context
1170 .adjust_static_position_of_hoisted_fragments_with_offset(
1171 &logical_origin_in_physical_coordinates,
1172 start_positioning_context_length,
1173 );
1174
1175 let containing_block = self.containing_block();
1176 let physical_line_rect = LogicalRect {
1177 start_corner,
1178 size: LogicalVec2 {
1179 inline: containing_block.size.inline,
1180 block: effective_block_advance.resolve(),
1181 },
1182 }
1183 .as_physical(Some(containing_block));
1184 self.fragments
1185 .push(Fragment::Positioning(PositioningFragment::new_anonymous(
1186 self.root_nesting_level.style.clone(),
1187 physical_line_rect,
1188 fragments,
1189 )));
1190 }
1191
1192 fn calculate_current_line_inline_start_and_justification_adjustment(
1197 &self,
1198 whitespace_trimmed: Au,
1199 last_line_or_forced_line_break: bool,
1200 ) -> (Au, Au) {
1201 enum TextAlign {
1202 Start,
1203 Center,
1204 End,
1205 }
1206 let containing_block = self.containing_block();
1207 let style = containing_block.style;
1208 let mut text_align_keyword = style.clone_text_align();
1209
1210 if last_line_or_forced_line_break {
1211 text_align_keyword = match style.clone_text_align_last() {
1212 TextAlignLast::Auto if text_align_keyword == TextAlignKeyword::Justify => {
1213 TextAlignKeyword::Start
1214 },
1215 TextAlignLast::Auto => text_align_keyword,
1216 TextAlignLast::Start => TextAlignKeyword::Start,
1217 TextAlignLast::End => TextAlignKeyword::End,
1218 TextAlignLast::Left => TextAlignKeyword::Left,
1219 TextAlignLast::Right => TextAlignKeyword::Right,
1220 TextAlignLast::Center => TextAlignKeyword::Center,
1221 TextAlignLast::Justify => TextAlignKeyword::Justify,
1222 };
1223 }
1224
1225 let text_align = match text_align_keyword {
1226 TextAlignKeyword::Start => TextAlign::Start,
1227 TextAlignKeyword::Center | TextAlignKeyword::MozCenter => TextAlign::Center,
1228 TextAlignKeyword::End => TextAlign::End,
1229 TextAlignKeyword::Left | TextAlignKeyword::MozLeft => {
1230 if style.writing_mode.line_left_is_inline_start() {
1231 TextAlign::Start
1232 } else {
1233 TextAlign::End
1234 }
1235 },
1236 TextAlignKeyword::Right | TextAlignKeyword::MozRight => {
1237 if style.writing_mode.line_left_is_inline_start() {
1238 TextAlign::End
1239 } else {
1240 TextAlign::Start
1241 }
1242 },
1243 TextAlignKeyword::Justify => TextAlign::Start,
1244 };
1245
1246 let (line_start, available_space) = match self.current_line.placement_among_floats.get() {
1247 Some(placement_among_floats) => (
1248 placement_among_floats.start_corner.inline,
1249 placement_among_floats.size.inline,
1250 ),
1251 None => (Au::zero(), containing_block.size.inline),
1252 };
1253
1254 let text_indent = self.current_line.start_position.inline;
1261 let line_length = self.current_line.inline_position - whitespace_trimmed - text_indent;
1262 let adjusted_line_start = line_start +
1263 match text_align {
1264 TextAlign::Start => text_indent,
1265 TextAlign::End => (available_space - line_length).max(text_indent),
1266 TextAlign::Center => (available_space - line_length + text_indent)
1267 .scale_by(0.5)
1268 .max(text_indent),
1269 };
1270
1271 let text_justify = containing_block.style.clone_text_justify();
1275 let justification_adjustment = match (text_align_keyword, text_justify) {
1276 (TextAlignKeyword::Justify, TextJustify::None) => Au::zero(),
1279 (TextAlignKeyword::Justify, _) => {
1280 match self.current_line.count_justification_opportunities() {
1281 0 => Au::zero(),
1282 num_justification_opportunities => {
1283 (available_space - text_indent - line_length)
1284 .scale_by(1. / num_justification_opportunities as f32)
1285 },
1286 }
1287 },
1288 _ => Au::zero(),
1289 };
1290
1291 let justification_adjustment = justification_adjustment.max(Au::zero());
1294
1295 (adjusted_line_start, justification_adjustment)
1296 }
1297
1298 fn place_float_fragment(&mut self, fragment: &mut BoxFragment) {
1299 let state = self
1300 .sequential_layout_state
1301 .as_mut()
1302 .expect("Tried to lay out a float with no sequential placement state!");
1303
1304 let block_offset_from_containining_block_top = state
1305 .current_block_position_including_margins() -
1306 state.current_containing_block_offset();
1307 state.place_float_fragment(
1308 fragment,
1309 self.placement_state.containing_block,
1310 CollapsedMargin::zero(),
1311 block_offset_from_containining_block_top,
1312 );
1313 }
1314
1315 fn place_float_line_item_for_commit_to_line(
1324 &mut self,
1325 float_item: &mut FloatLineItem,
1326 line_inline_size_without_trailing_whitespace: Au,
1327 ) {
1328 let containing_block = self.containing_block();
1329 let mut float_fragment = float_item.fragment.borrow_mut();
1330 let logical_margin_rect_size = float_fragment
1331 .margin_rect()
1332 .size
1333 .to_logical(containing_block.style.writing_mode);
1334 let inline_size = logical_margin_rect_size.inline.max(Au::zero());
1335
1336 let available_inline_size = match self.current_line.placement_among_floats.get() {
1337 Some(placement_among_floats) => placement_among_floats.size.inline,
1338 None => containing_block.size.inline,
1339 } - line_inline_size_without_trailing_whitespace;
1340
1341 let has_content = self.current_line.has_content || self.current_line_segment.has_content;
1347 let fits_on_line = !has_content || inline_size <= available_inline_size;
1348 let needs_placement_later =
1349 self.current_line.has_floats_waiting_to_be_placed || !fits_on_line;
1350
1351 if needs_placement_later {
1352 self.current_line.has_floats_waiting_to_be_placed = true;
1353 } else {
1354 self.place_float_fragment(&mut float_fragment);
1355 float_item.needs_placement = false;
1356 }
1357
1358 let new_placement = self.place_line_among_floats(&LogicalVec2 {
1363 inline: line_inline_size_without_trailing_whitespace,
1364 block: self.current_line.max_block_size.resolve(),
1365 });
1366 self.current_line
1367 .replace_placement_among_floats(new_placement);
1368 }
1369
1370 fn place_line_among_floats(&self, potential_line_size: &LogicalVec2<Au>) -> LogicalRect<Au> {
1375 let sequential_layout_state = self
1376 .sequential_layout_state
1377 .as_ref()
1378 .expect("Should not have called this function without having floats.");
1379
1380 let ifc_offset_in_float_container = LogicalVec2 {
1381 inline: sequential_layout_state
1382 .floats
1383 .containing_block_info
1384 .inline_start,
1385 block: sequential_layout_state.current_containing_block_offset(),
1386 };
1387
1388 let ceiling = self.current_line_block_start_considering_placement_among_floats();
1389 let mut placement = PlacementAmongFloats::new(
1390 &sequential_layout_state.floats,
1391 ceiling + ifc_offset_in_float_container.block,
1392 LogicalVec2 {
1393 inline: potential_line_size.inline,
1394 block: potential_line_size.block,
1395 },
1396 &PaddingBorderMargin::zero(),
1397 );
1398
1399 let mut placement_rect = placement.place();
1400 placement_rect.start_corner -= ifc_offset_in_float_container;
1401 placement_rect
1402 }
1403
1404 fn new_potential_line_size_causes_line_break(
1411 &mut self,
1412 potential_line_size: &LogicalVec2<Au>,
1413 ) -> bool {
1414 let containing_block = self.containing_block();
1415 let available_line_space = if self.sequential_layout_state.is_some() {
1416 self.current_line
1417 .placement_among_floats
1418 .get_or_init(|| self.place_line_among_floats(potential_line_size))
1419 .size
1420 } else {
1421 LogicalVec2 {
1422 inline: containing_block.size.inline,
1423 block: MAX_AU,
1424 }
1425 };
1426
1427 let inline_would_overflow = potential_line_size.inline > available_line_space.inline;
1428 let block_would_overflow = potential_line_size.block > available_line_space.block;
1429
1430 let can_break = self.current_line.has_content;
1433
1434 if !can_break {
1440 if self.sequential_layout_state.is_some() &&
1443 (inline_would_overflow || block_would_overflow)
1444 {
1445 let new_placement = self.place_line_among_floats(potential_line_size);
1446 self.current_line
1447 .replace_placement_among_floats(new_placement);
1448 }
1449
1450 return false;
1451 }
1452
1453 if potential_line_size.inline > containing_block.size.inline {
1456 return true;
1457 }
1458
1459 if block_would_overflow {
1463 assert!(self.sequential_layout_state.is_some());
1465 let new_placement = self.place_line_among_floats(potential_line_size);
1466 if new_placement.start_corner.block !=
1467 self.current_line_block_start_considering_placement_among_floats()
1468 {
1469 return true;
1470 } else {
1471 self.current_line
1472 .replace_placement_among_floats(new_placement);
1473 return false;
1474 }
1475 }
1476
1477 inline_would_overflow
1481 }
1482
1483 fn defer_forced_line_break(&mut self) {
1484 if !self.unbreakable_segment_fits_on_line() {
1487 self.process_line_break(false );
1488 }
1489
1490 self.linebreak_before_new_content = true;
1492
1493 let line_is_empty =
1501 !self.current_line_segment.has_content && !self.current_line.has_content;
1502 if !self.processing_br_element() || line_is_empty {
1503 let strut_size = self
1504 .current_inline_container_state()
1505 .strut_block_sizes
1506 .clone();
1507 self.update_unbreakable_segment_for_new_content(
1508 &strut_size,
1509 Au::zero(),
1510 SegmentContentFlags::empty(),
1511 );
1512 }
1513 }
1514
1515 fn possibly_flush_deferred_forced_line_break(&mut self) {
1516 if !self.linebreak_before_new_content {
1517 return;
1518 }
1519
1520 self.commit_current_segment_to_line();
1521 self.process_line_break(true );
1522 self.linebreak_before_new_content = false;
1523 }
1524
1525 fn push_line_item_to_unbreakable_segment(&mut self, line_item: LineItem) {
1526 self.current_line_segment
1527 .push_line_item(line_item, self.inline_box_state_stack.len());
1528 }
1529
1530 fn push_glyph_store_to_unbreakable_segment(
1531 &mut self,
1532 glyph_store: Arc<GlyphStore>,
1533 text_run: &TextRun,
1534 info: &Arc<FontAndScriptInfo>,
1535 offsets: Option<TextRunOffsets>,
1536 ) {
1537 let inline_advance = glyph_store.total_advance();
1538 let flags = if glyph_store.is_whitespace() {
1539 SegmentContentFlags::from(text_run.inline_styles.style.borrow().get_inherited_text())
1540 } else {
1541 SegmentContentFlags::empty()
1542 };
1543
1544 let mut block_contribution = LineBlockSizes::zero();
1545 let quirks_mode = self.layout_context.style_context.quirks_mode() != QuirksMode::NoQuirks;
1546 if quirks_mode && !flags.is_collapsible_whitespace() {
1547 block_contribution.max_assign(&self.current_inline_container_state().strut_block_sizes);
1552 }
1553
1554 let font_metrics = &info.font.metrics;
1558 if self
1559 .current_inline_container_state()
1560 .font_metrics
1561 .block_metrics_meaningfully_differ(font_metrics)
1562 {
1563 let container_state = self.current_inline_container_state();
1565 let baseline_shift = effective_baseline_shift(
1566 &container_state.style,
1567 self.inline_box_state_stack.last().map(|c| &c.base),
1568 );
1569 let mut font_block_conribution = container_state.get_block_size_contribution(
1570 baseline_shift,
1571 font_metrics,
1572 &container_state.font_metrics,
1573 );
1574 font_block_conribution.adjust_for_baseline_offset(container_state.baseline_offset);
1575 block_contribution.max_assign(&font_block_conribution);
1576 }
1577
1578 self.update_unbreakable_segment_for_new_content(&block_contribution, inline_advance, flags);
1579
1580 let current_inline_box_identifier = self.current_inline_box_identifier();
1581 if let Some(LineItem::TextRun(inline_box_identifier, line_item)) =
1582 self.current_line_segment.line_items.last_mut()
1583 {
1584 if *inline_box_identifier == current_inline_box_identifier &&
1585 line_item.merge_if_possible(
1586 info,
1587 &glyph_store,
1588 &offsets,
1589 &text_run.inline_styles,
1590 )
1591 {
1592 return;
1593 }
1594 }
1595
1596 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1597 current_inline_box_identifier,
1598 TextRunLineItem {
1599 text: vec![glyph_store],
1600 base_fragment_info: text_run.base_fragment_info,
1601 inline_styles: text_run.inline_styles.clone(),
1602 info: info.clone(),
1603 offsets: offsets.map(Box::new),
1604 is_empty_for_text_cursor: false,
1605 },
1606 ));
1607 }
1608
1609 fn possibly_push_empty_text_run_to_unbreakable_segment(
1613 &mut self,
1614 text_run: &TextRun,
1615 info: &Arc<FontAndScriptInfo>,
1616 offsets: Option<TextRunOffsets>,
1617 ) {
1618 if offsets.is_none() || self.current_line_segment.has_content {
1619 return;
1620 }
1621
1622 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1623 self.current_inline_box_identifier(),
1624 TextRunLineItem {
1625 text: Default::default(),
1626 base_fragment_info: text_run.base_fragment_info,
1627 inline_styles: text_run.inline_styles.clone(),
1628 info: info.clone(),
1629 offsets: offsets.map(Box::new),
1630 is_empty_for_text_cursor: true,
1631 },
1632 ));
1633 self.current_line_segment.has_content = true;
1634 }
1635
1636 fn update_unbreakable_segment_for_new_content(
1637 &mut self,
1638 block_sizes_of_content: &LineBlockSizes,
1639 inline_size: Au,
1640 flags: SegmentContentFlags,
1641 ) {
1642 if flags.is_collapsible_whitespace() || flags.is_wrappable_and_hangable() {
1643 self.current_line_segment.trailing_whitespace_size = inline_size;
1644 } else {
1645 self.current_line_segment.trailing_whitespace_size = Au::zero();
1646 }
1647 if !flags.is_collapsible_whitespace() {
1648 self.current_line_segment.has_content = true;
1649 }
1650
1651 let container_max_block_size = &self
1653 .current_inline_container_state()
1654 .nested_strut_block_sizes
1655 .clone();
1656 self.current_line_segment
1657 .max_block_size
1658 .max_assign(container_max_block_size);
1659 self.current_line_segment
1660 .max_block_size
1661 .max_assign(block_sizes_of_content);
1662
1663 self.current_line_segment.inline_size += inline_size;
1664
1665 *self
1667 .current_inline_container_state()
1668 .has_content
1669 .borrow_mut() = true;
1670 self.propagate_current_nesting_level_white_space_style();
1671 }
1672
1673 fn process_line_break(&mut self, forced_line_break: bool) {
1674 self.current_line_segment.trim_leading_whitespace();
1675 self.finish_current_line_and_reset(forced_line_break);
1676 }
1677
1678 fn unbreakable_segment_fits_on_line(&mut self) -> bool {
1679 let potential_line_size = LogicalVec2 {
1680 inline: self.current_line.inline_position + self.current_line_segment.inline_size -
1681 self.current_line_segment.trailing_whitespace_size,
1682 block: self
1683 .current_line_max_block_size_including_nested_containers()
1684 .max(&self.current_line_segment.max_block_size)
1685 .resolve(),
1686 };
1687
1688 !self.new_potential_line_size_causes_line_break(&potential_line_size)
1689 }
1690
1691 fn process_soft_wrap_opportunity(&mut self) {
1695 if self.current_line_segment.line_items.is_empty() {
1696 return;
1697 }
1698 if self.text_wrap_mode == TextWrapMode::Nowrap {
1699 return;
1700 }
1701 if !self.unbreakable_segment_fits_on_line() {
1702 self.process_line_break(false );
1703 }
1704 self.commit_current_segment_to_line();
1705 }
1706
1707 fn commit_current_segment_to_line(&mut self) {
1710 if self.current_line_segment.line_items.is_empty() && !self.current_line_segment.has_content
1713 {
1714 return;
1715 }
1716
1717 if !self.current_line.has_content {
1718 self.current_line_segment.trim_leading_whitespace();
1719 }
1720
1721 self.current_line.inline_position += self.current_line_segment.inline_size;
1722 self.current_line.max_block_size = self
1723 .current_line_max_block_size_including_nested_containers()
1724 .max(&self.current_line_segment.max_block_size);
1725 let line_inline_size_without_trailing_whitespace =
1726 self.current_line.inline_position - self.current_line_segment.trailing_whitespace_size;
1727
1728 let mut segment_items = mem::take(&mut self.current_line_segment.line_items);
1730 for item in segment_items.iter_mut() {
1731 if let LineItem::Float(_, float_item) = item {
1732 self.place_float_line_item_for_commit_to_line(
1733 float_item,
1734 line_inline_size_without_trailing_whitespace,
1735 );
1736 }
1737 }
1738
1739 if self.current_line.line_items.is_empty() {
1744 let will_break = self.new_potential_line_size_causes_line_break(&LogicalVec2 {
1745 inline: line_inline_size_without_trailing_whitespace,
1746 block: self.current_line_segment.max_block_size.resolve(),
1747 });
1748 assert!(!will_break);
1749 }
1750
1751 self.current_line.line_items.extend(segment_items);
1752 self.current_line.has_content |= self.current_line_segment.has_content;
1753 self.current_line.has_inline_pbm |= self.current_line_segment.has_inline_pbm;
1754
1755 self.current_line_segment.reset();
1756 }
1757
1758 #[inline]
1759 fn containing_block(&self) -> &ContainingBlock<'_> {
1760 self.placement_state.containing_block
1761 }
1762}
1763
1764bitflags! {
1765 struct SegmentContentFlags: u8 {
1766 const COLLAPSIBLE_WHITESPACE = 0b00000001;
1767 const WRAPPABLE_AND_HANGABLE_WHITESPACE = 0b00000010;
1768 }
1769}
1770
1771impl SegmentContentFlags {
1772 fn is_collapsible_whitespace(&self) -> bool {
1773 self.contains(Self::COLLAPSIBLE_WHITESPACE)
1774 }
1775
1776 fn is_wrappable_and_hangable(&self) -> bool {
1777 self.contains(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE)
1778 }
1779}
1780
1781impl From<&InheritedText> for SegmentContentFlags {
1782 fn from(style_text: &InheritedText) -> Self {
1783 let mut flags = Self::empty();
1784
1785 if !matches!(
1788 style_text.white_space_collapse,
1789 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
1790 ) {
1791 flags.insert(Self::COLLAPSIBLE_WHITESPACE);
1792 }
1793
1794 if style_text.text_wrap_mode == TextWrapMode::Wrap &&
1797 style_text.white_space_collapse != WhiteSpaceCollapse::BreakSpaces
1798 {
1799 flags.insert(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE);
1800 }
1801 flags
1802 }
1803}
1804
1805impl InlineFormattingContext {
1806 #[servo_tracing::instrument(name = "InlineFormattingContext::new_with_builder", skip_all)]
1807 fn new_with_builder(
1808 mut builder: InlineFormattingContextBuilder,
1809 layout_context: &LayoutContext,
1810 has_first_formatted_line: bool,
1811 is_single_line_text_input: bool,
1812 starting_bidi_level: Level,
1813 ) -> Self {
1814 let text_content: String = builder.text_segments.into_iter().collect();
1816
1817 let bidi_info = BidiInfo::new(&text_content, Some(starting_bidi_level));
1818 let has_right_to_left_content = bidi_info.has_rtl();
1819 let shared_inline_styles = builder
1820 .shared_inline_styles_stack
1821 .last()
1822 .expect("Should have at least one SharedInlineStyle for the root of an IFC")
1823 .clone();
1824 let (word_break, line_break, lang) = {
1825 let styles = shared_inline_styles.style.borrow();
1826 let text_style = styles.get_inherited_text();
1827 (
1828 text_style.word_break,
1829 text_style.line_break,
1830 styles.get_font()._x_lang.clone(),
1831 )
1832 };
1833
1834 let mut options = LineBreakOptions::default();
1835
1836 options.strictness = match line_break {
1837 LineBreak::Loose => LineBreakStrictness::Loose,
1838 LineBreak::Normal => LineBreakStrictness::Normal,
1839 LineBreak::Strict => LineBreakStrictness::Strict,
1840 LineBreak::Anywhere => LineBreakStrictness::Anywhere,
1841 LineBreak::Auto => LineBreakStrictness::Normal,
1844 };
1845 options.word_option = match word_break {
1846 WordBreak::Normal => LineBreakWordOption::Normal,
1847 WordBreak::BreakAll => LineBreakWordOption::BreakAll,
1848 WordBreak::KeepAll => LineBreakWordOption::KeepAll,
1849 };
1850 options.ja_zh = {
1853 lang.0.parse::<LanguageIdentifier>().is_ok_and(|lang_id| {
1854 const JA: Language = language!("ja");
1855 const ZH: Language = language!("zh");
1856 matches!(lang_id.language, JA | ZH)
1857 })
1858 };
1859
1860 let mut new_linebreaker = LineBreaker::new(text_content.as_str(), options);
1861 for item in &mut builder.inline_items {
1862 match item {
1863 InlineItem::TextRun(text_run) => {
1864 text_run.borrow_mut().segment_and_shape(
1865 &text_content,
1866 layout_context,
1867 &mut new_linebreaker,
1868 &bidi_info,
1869 );
1870 },
1871 InlineItem::StartInlineBox(inline_box) => {
1872 let inline_box = &mut *inline_box.borrow_mut();
1873 if let Some(font) = get_font_for_first_font_for_style(
1874 &inline_box.base.style,
1875 &layout_context.font_context,
1876 ) {
1877 inline_box.default_font = Some(font);
1878 }
1879 },
1880 InlineItem::Atomic(_, index_in_text, bidi_level) => {
1881 *bidi_level = bidi_info.levels[*index_in_text];
1882 },
1883 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) |
1884 InlineItem::OutOfFlowFloatBox(_) |
1885 InlineItem::EndInlineBox |
1886 InlineItem::BlockLevel { .. } => {},
1887 }
1888 }
1889
1890 InlineFormattingContext {
1891 text_content,
1892 inline_items: builder.inline_items,
1893 inline_boxes: builder.inline_boxes,
1894 shared_inline_styles,
1895 has_first_formatted_line,
1896 contains_floats: builder.contains_floats,
1897 is_single_line_text_input,
1898 has_right_to_left_content,
1899 shared_selection: builder.shared_selection,
1900 }
1901 }
1902
1903 pub(crate) fn repair_style(
1904 &self,
1905 context: &SharedStyleContext,
1906 node: &ServoLayoutNode,
1907 new_style: &ServoArc<ComputedValues>,
1908 ) {
1909 *self.shared_inline_styles.style.borrow_mut() = new_style.clone();
1910 *self.shared_inline_styles.selected.borrow_mut() = node.selected_style(context);
1911 }
1912
1913 fn inline_start_for_first_line(&self, containing_block: IndefiniteContainingBlock) -> Au {
1914 if !self.has_first_formatted_line {
1915 return Au::zero();
1916 }
1917 containing_block
1918 .style
1919 .get_inherited_text()
1920 .text_indent
1921 .length
1922 .to_used_value(containing_block.size.inline.unwrap_or_default())
1923 }
1924
1925 pub(super) fn layout(
1926 &self,
1927 layout_context: &LayoutContext,
1928 positioning_context: &mut PositioningContext,
1929 containing_block: &ContainingBlock,
1930 sequential_layout_state: Option<&mut SequentialLayoutState>,
1931 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
1932 ) -> IndependentFormattingContextLayoutResult {
1933 for inline_box in self.inline_boxes.iter() {
1935 inline_box.borrow().base.clear_fragments();
1936 }
1937
1938 let style = containing_block.style;
1939
1940 let default_font_metrics =
1943 get_font_for_first_font_for_style(style, &layout_context.font_context)
1944 .map(|font| font.metrics.clone());
1945
1946 let style_text = containing_block.style.get_inherited_text();
1947 let mut inline_container_state_flags = InlineContainerStateFlags::empty();
1948 if inline_container_needs_strut(style, layout_context, None) {
1949 inline_container_state_flags.insert(InlineContainerStateFlags::CREATE_STRUT);
1950 }
1951 if self.is_single_line_text_input {
1952 inline_container_state_flags
1953 .insert(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT);
1954 }
1955 let placement_state =
1956 PlacementState::new(collapsible_with_parent_start_margin, containing_block);
1957
1958 let mut layout = InlineFormattingContextLayout {
1959 positioning_context,
1960 placement_state,
1961 sequential_layout_state,
1962 layout_context,
1963 ifc: self,
1964 fragments: Vec::new(),
1965 current_line: LineUnderConstruction::new(LogicalVec2 {
1966 inline: self.inline_start_for_first_line(containing_block.into()),
1967 block: Au::zero(),
1968 }),
1969 root_nesting_level: InlineContainerState::new(
1970 style.to_arc(),
1971 inline_container_state_flags,
1972 None, default_font_metrics,
1974 ),
1975 inline_box_state_stack: Vec::new(),
1976 inline_box_states: Vec::with_capacity(self.inline_boxes.len()),
1977 current_line_segment: UnbreakableSegmentUnderConstruction::new(),
1978 linebreak_before_new_content: false,
1979 deferred_br_clear: Clear::None,
1980 have_deferred_soft_wrap_opportunity: false,
1981 depends_on_block_constraints: false,
1982 white_space_collapse: style_text.white_space_collapse,
1983 text_wrap_mode: style_text.text_wrap_mode,
1984 };
1985
1986 for item in self.inline_items.iter() {
1987 if !matches!(item, InlineItem::EndInlineBox) {
1989 layout.possibly_flush_deferred_forced_line_break();
1990 }
1991
1992 match item {
1993 InlineItem::StartInlineBox(inline_box) => {
1994 layout.start_inline_box(&inline_box.borrow());
1995 },
1996 InlineItem::EndInlineBox => layout.finish_inline_box(),
1997 InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout),
1998 InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
1999 atomic_formatting_context.borrow().layout_into_line_items(
2000 &mut layout,
2001 *offset_in_text,
2002 *bidi_level,
2003 );
2004 },
2005 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, _) => {
2006 layout.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
2007 layout.current_inline_box_identifier(),
2008 AbsolutelyPositionedLineItem {
2009 absolutely_positioned_box: positioned_box.clone(),
2010 preceding_line_content_would_produce_phantom_line: layout
2011 .current_line
2012 .is_phantom() &&
2013 layout.current_line_segment.is_phantom(),
2014 },
2015 ));
2016 },
2017 InlineItem::OutOfFlowFloatBox(float_box) => {
2018 float_box.borrow().layout_into_line_items(&mut layout);
2019 },
2020 InlineItem::BlockLevel(block_level) => {
2021 block_level.borrow().layout_into_line_items(&mut layout);
2022 },
2023 }
2024 }
2025
2026 layout.finish_last_line();
2027 let (content_block_size, collapsible_margins_in_children, baselines) =
2028 layout.placement_state.finish();
2029
2030 IndependentFormattingContextLayoutResult {
2031 fragments: layout.fragments,
2032 content_block_size,
2033 collapsible_margins_in_children,
2034 baselines,
2035 depends_on_block_constraints: layout.depends_on_block_constraints,
2036 content_inline_size_for_table: None,
2037 specific_layout_info: None,
2038 }
2039 }
2040
2041 fn next_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
2042 let Some(character) = self.text_content[index..].chars().nth(1) else {
2043 return false;
2044 };
2045 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2046 }
2047
2048 fn previous_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
2049 let Some(character) = self.text_content[0..index].chars().next_back() else {
2050 return false;
2051 };
2052 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2053 }
2054
2055 pub(crate) fn find_block_margin_collapsing_with_parent(
2056 &self,
2057 layout_context: &LayoutContext,
2058 collected_margin: &mut CollapsedMargin,
2059 containing_block_for_children: &ContainingBlock,
2060 ) -> bool {
2061 let mut nesting_levels_from_nonzero_end_pbm: u32 = 1;
2067 let mut items_iter = self.inline_items.iter();
2068 items_iter.all(|inline_item| match inline_item {
2069 InlineItem::StartInlineBox(inline_box) => {
2070 let pbm = inline_box
2071 .borrow()
2072 .layout_style()
2073 .padding_border_margin(containing_block_for_children);
2074 if pbm.padding.inline_end.is_zero() &&
2075 pbm.border.inline_end.is_zero() &&
2076 pbm.margin.inline_end.auto_is(Au::zero).is_zero()
2077 {
2078 nesting_levels_from_nonzero_end_pbm += 1;
2079 } else {
2080 nesting_levels_from_nonzero_end_pbm = 0;
2081 }
2082 pbm.padding.inline_start.is_zero() &&
2083 pbm.border.inline_start.is_zero() &&
2084 pbm.margin.inline_start.auto_is(Au::zero).is_zero()
2085 },
2086 InlineItem::EndInlineBox => {
2087 if nesting_levels_from_nonzero_end_pbm == 0 {
2088 false
2089 } else {
2090 nesting_levels_from_nonzero_end_pbm -= 1;
2091 true
2092 }
2093 },
2094 InlineItem::TextRun(text_run) => {
2095 let text_run = &*text_run.borrow();
2096 let parent_style = text_run.inline_styles.style.borrow();
2097 text_run.shaped_text.iter().all(|segment| {
2098 segment.runs.iter().all(|run| {
2099 run.is_whitespace() &&
2100 !run.is_single_preserved_newline() &&
2101 !matches!(
2102 parent_style.get_inherited_text().white_space_collapse,
2103 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
2104 )
2105 })
2106 })
2107 },
2108 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => true,
2109 InlineItem::OutOfFlowFloatBox(..) => true,
2110 InlineItem::Atomic(..) => false,
2111 InlineItem::BlockLevel(block_level) => block_level
2112 .borrow()
2113 .find_block_margin_collapsing_with_parent(
2114 layout_context,
2115 collected_margin,
2116 containing_block_for_children,
2117 ),
2118 })
2119 }
2120
2121 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
2122 let mut parent_box_stack = Vec::new();
2123 let current_parent_box = |parent_box_stack: &[WeakLayoutBox]| {
2124 parent_box_stack.last().unwrap_or(&layout_box).clone()
2125 };
2126 for inline_item in &self.inline_items {
2127 match inline_item {
2128 InlineItem::StartInlineBox(inline_box) => {
2129 inline_box
2130 .borrow_mut()
2131 .base
2132 .parent_box
2133 .replace(current_parent_box(&parent_box_stack));
2134 parent_box_stack.push(WeakLayoutBox::InlineLevel(
2135 WeakInlineItem::StartInlineBox(inline_box.downgrade()),
2136 ));
2137 },
2138 InlineItem::EndInlineBox => {
2139 parent_box_stack.pop();
2140 },
2141 InlineItem::TextRun(text_run) => {
2142 text_run
2143 .borrow_mut()
2144 .parent_box
2145 .replace(current_parent_box(&parent_box_stack));
2146 },
2147 _ => inline_item.with_base_mut(|base| {
2148 base.parent_box
2149 .replace(current_parent_box(&parent_box_stack));
2150 }),
2151 }
2152 }
2153 }
2154}
2155
2156impl InlineContainerState {
2157 fn new(
2158 style: ServoArc<ComputedValues>,
2159 flags: InlineContainerStateFlags,
2160 parent_container: Option<&InlineContainerState>,
2161 font_metrics: Option<Arc<FontMetrics>>,
2162 ) -> Self {
2163 let font_metrics = font_metrics.unwrap_or_else(FontMetrics::empty);
2164 let mut baseline_offset = Au::zero();
2165 let mut strut_block_sizes = Self::get_block_sizes_with_style(
2166 effective_baseline_shift(&style, parent_container),
2167 &style,
2168 &font_metrics,
2169 &font_metrics,
2170 &flags,
2171 );
2172 if let Some(parent_container) = parent_container {
2173 baseline_offset = parent_container.get_cumulative_baseline_offset_for_child(
2176 style.clone_alignment_baseline(),
2177 style.clone_baseline_shift(),
2178 &strut_block_sizes,
2179 );
2180 strut_block_sizes.adjust_for_baseline_offset(baseline_offset);
2181 }
2182
2183 let mut nested_block_sizes = parent_container
2184 .map(|container| container.nested_strut_block_sizes.clone())
2185 .unwrap_or_else(LineBlockSizes::zero);
2186 if flags.contains(InlineContainerStateFlags::CREATE_STRUT) {
2187 nested_block_sizes.max_assign(&strut_block_sizes);
2188 }
2189
2190 Self {
2191 style,
2192 flags,
2193 has_content: RefCell::new(false),
2194 nested_strut_block_sizes: nested_block_sizes,
2195 strut_block_sizes,
2196 baseline_offset,
2197 font_metrics,
2198 }
2199 }
2200
2201 fn get_block_sizes_with_style(
2202 baseline_shift: BaselineShift,
2203 style: &ComputedValues,
2204 font_metrics: &FontMetrics,
2205 font_metrics_of_first_font: &FontMetrics,
2206 flags: &InlineContainerStateFlags,
2207 ) -> LineBlockSizes {
2208 let line_height = line_height(style, font_metrics, flags);
2209
2210 if !is_baseline_relative(baseline_shift) {
2211 return LineBlockSizes {
2212 line_height,
2213 baseline_relative_size_for_line_height: None,
2214 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2215 };
2216 }
2217
2218 let mut ascent = font_metrics.ascent;
2227 let mut descent = font_metrics.descent;
2228 if style.get_font().line_height == LineHeight::Normal {
2229 let half_leading_from_line_gap =
2230 (font_metrics.line_gap - descent - ascent).scale_by(0.5);
2231 ascent += half_leading_from_line_gap;
2232 descent += half_leading_from_line_gap;
2233 }
2234
2235 let size_for_baseline_positioning = BaselineRelativeSize { ascent, descent };
2239
2240 if style.get_font().line_height != LineHeight::Normal {
2256 ascent = font_metrics_of_first_font.ascent;
2257 descent = font_metrics_of_first_font.descent;
2258 let half_leading = (line_height - (ascent + descent)).scale_by(0.5);
2259 ascent += half_leading;
2264 descent = line_height - ascent;
2265 }
2266
2267 LineBlockSizes {
2268 line_height,
2269 baseline_relative_size_for_line_height: Some(BaselineRelativeSize { ascent, descent }),
2270 size_for_baseline_positioning,
2271 }
2272 }
2273
2274 fn get_block_size_contribution(
2275 &self,
2276 baseline_shift: BaselineShift,
2277 font_metrics: &FontMetrics,
2278 font_metrics_of_first_font: &FontMetrics,
2279 ) -> LineBlockSizes {
2280 Self::get_block_sizes_with_style(
2281 baseline_shift,
2282 &self.style,
2283 font_metrics,
2284 font_metrics_of_first_font,
2285 &self.flags,
2286 )
2287 }
2288
2289 fn get_cumulative_baseline_offset_for_child(
2290 &self,
2291 child_alignment_baseline: AlignmentBaseline,
2292 child_baseline_shift: BaselineShift,
2293 child_block_size: &LineBlockSizes,
2294 ) -> Au {
2295 let block_size = self.get_block_size_contribution(
2296 child_baseline_shift.clone(),
2297 &self.font_metrics,
2298 &self.font_metrics,
2299 );
2300 self.baseline_offset +
2301 match child_alignment_baseline {
2302 AlignmentBaseline::Baseline => Au::zero(),
2303 AlignmentBaseline::TextTop => {
2304 child_block_size.size_for_baseline_positioning.ascent - self.font_metrics.ascent
2305 },
2306 AlignmentBaseline::Middle => {
2307 (child_block_size.size_for_baseline_positioning.ascent -
2310 child_block_size.size_for_baseline_positioning.descent -
2311 self.font_metrics.x_height)
2312 .scale_by(0.5)
2313 },
2314 AlignmentBaseline::TextBottom => {
2315 self.font_metrics.descent -
2316 child_block_size.size_for_baseline_positioning.descent
2317 },
2318 AlignmentBaseline::Alphabetic |
2319 AlignmentBaseline::Ideographic |
2320 AlignmentBaseline::Central |
2321 AlignmentBaseline::Mathematical |
2322 AlignmentBaseline::Hanging => {
2323 unreachable!("Got alignment-baseline value that should be disabled in Stylo")
2324 },
2325 } +
2326 match child_baseline_shift {
2327 BaselineShift::Keyword(
2332 BaselineShiftKeyword::Top |
2333 BaselineShiftKeyword::Bottom |
2334 BaselineShiftKeyword::Center,
2335 ) => Au::zero(),
2336 BaselineShift::Keyword(BaselineShiftKeyword::Sub) => {
2337 block_size.resolve().scale_by(FONT_SUBSCRIPT_OFFSET_RATIO)
2338 },
2339 BaselineShift::Keyword(BaselineShiftKeyword::Super) => {
2340 -block_size.resolve().scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO)
2341 },
2342 BaselineShift::Length(length_percentage) => {
2343 -length_percentage.to_used_value(child_block_size.line_height)
2344 },
2345 }
2346 }
2347}
2348
2349impl IndependentFormattingContext {
2350 fn layout_into_line_items(
2351 &self,
2352 layout: &mut InlineFormattingContextLayout,
2353 offset_in_text: usize,
2354 bidi_level: Level,
2355 ) {
2356 let mut child_positioning_context = PositioningContext::default();
2358 let IndependentFloatOrAtomicLayoutResult {
2359 mut fragment,
2360 baselines,
2361 pbm_sums,
2362 } = self.layout_float_or_atomic_inline(
2363 layout.layout_context,
2364 &mut child_positioning_context,
2365 layout.containing_block(),
2366 );
2367
2368 layout.depends_on_block_constraints |= fragment.base.flags.contains(
2371 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2372 );
2373
2374 let container_writing_mode = layout.containing_block().style.writing_mode;
2376 let pbm_physical_offset = pbm_sums
2377 .start_offset()
2378 .to_physical_size(container_writing_mode);
2379 fragment.base.rect.origin += pbm_physical_offset.to_vector();
2380
2381 fragment = fragment.with_baselines(baselines);
2383
2384 let positioning_context = if self.is_replaced() {
2387 None
2388 } else {
2389 if fragment
2390 .style()
2391 .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
2392 {
2393 child_positioning_context
2394 .layout_collected_children(layout.layout_context, &mut fragment);
2395 }
2396 Some(child_positioning_context)
2397 };
2398
2399 if layout.text_wrap_mode == TextWrapMode::Wrap &&
2400 !layout
2401 .ifc
2402 .previous_character_prevents_soft_wrap_opportunity(offset_in_text)
2403 {
2404 layout.process_soft_wrap_opportunity();
2405 }
2406
2407 let size = pbm_sums.sum() + fragment.base.rect.size.to_logical(container_writing_mode);
2408 let baseline_offset = self
2409 .pick_baseline(&fragment.baselines(container_writing_mode))
2410 .map(|baseline| pbm_sums.block_start + baseline)
2411 .unwrap_or(size.block);
2412
2413 let (block_sizes, baseline_offset_in_parent) =
2414 self.get_block_sizes_and_baseline_offset(layout, size.block, baseline_offset);
2415 layout.update_unbreakable_segment_for_new_content(
2416 &block_sizes,
2417 size.inline,
2418 SegmentContentFlags::empty(),
2419 );
2420
2421 let fragment = ArcRefCell::new(fragment);
2422 self.base.set_fragment(Fragment::Box(fragment.clone()));
2423
2424 layout.push_line_item_to_unbreakable_segment(LineItem::Atomic(
2425 layout.current_inline_box_identifier(),
2426 AtomicLineItem {
2427 fragment,
2428 size,
2429 positioning_context,
2430 baseline_offset_in_parent,
2431 baseline_offset_in_item: baseline_offset,
2432 bidi_level,
2433 },
2434 ));
2435
2436 if !layout
2439 .ifc
2440 .next_character_prevents_soft_wrap_opportunity(offset_in_text)
2441 {
2442 layout.have_deferred_soft_wrap_opportunity = true;
2443 }
2444 }
2445
2446 fn pick_baseline(&self, baselines: &Baselines) -> Option<Au> {
2450 match self.style().clone_baseline_source() {
2451 BaselineSource::First => baselines.first,
2452 BaselineSource::Last => baselines.last,
2453 BaselineSource::Auto if self.is_block_container() => baselines.last,
2454 BaselineSource::Auto => baselines.first,
2455 }
2456 }
2457
2458 fn get_block_sizes_and_baseline_offset(
2459 &self,
2460 ifc: &InlineFormattingContextLayout,
2461 block_size: Au,
2462 baseline_offset_in_content_area: Au,
2463 ) -> (LineBlockSizes, Au) {
2464 let mut contribution = if !is_baseline_relative(self.style().clone_baseline_shift()) {
2465 LineBlockSizes {
2466 line_height: block_size,
2467 baseline_relative_size_for_line_height: None,
2468 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2469 }
2470 } else {
2471 let baseline_relative_size = BaselineRelativeSize {
2472 ascent: baseline_offset_in_content_area,
2473 descent: block_size - baseline_offset_in_content_area,
2474 };
2475 LineBlockSizes {
2476 line_height: block_size,
2477 baseline_relative_size_for_line_height: Some(baseline_relative_size.clone()),
2478 size_for_baseline_positioning: baseline_relative_size,
2479 }
2480 };
2481
2482 let style = self.style();
2483 let baseline_offset = ifc
2484 .current_inline_container_state()
2485 .get_cumulative_baseline_offset_for_child(
2486 style.clone_alignment_baseline(),
2487 style.clone_baseline_shift(),
2488 &contribution,
2489 );
2490 contribution.adjust_for_baseline_offset(baseline_offset);
2491
2492 (contribution, baseline_offset)
2493 }
2494}
2495
2496impl FloatBox {
2497 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
2498 let fragment = ArcRefCell::new(self.layout(
2499 layout.layout_context,
2500 layout.positioning_context,
2501 layout.placement_state.containing_block,
2502 ));
2503
2504 self.contents
2505 .base
2506 .set_fragment(Fragment::Box(fragment.clone()));
2507 layout.push_line_item_to_unbreakable_segment(LineItem::Float(
2508 layout.current_inline_box_identifier(),
2509 FloatLineItem {
2510 fragment,
2511 needs_placement: true,
2512 },
2513 ));
2514 }
2515}
2516
2517fn place_pending_floats(ifc: &mut InlineFormattingContextLayout, line_items: &mut [LineItem]) {
2518 for item in line_items.iter_mut() {
2519 if let LineItem::Float(_, float_line_item) = item {
2520 if float_line_item.needs_placement {
2521 ifc.place_float_fragment(&mut float_line_item.fragment.borrow_mut());
2522 }
2523 }
2524 }
2525}
2526
2527fn line_height(
2528 parent_style: &ComputedValues,
2529 font_metrics: &FontMetrics,
2530 flags: &InlineContainerStateFlags,
2531) -> Au {
2532 let font = parent_style.get_font();
2533 let font_size = font.font_size.computed_size();
2534 let mut line_height = match font.line_height {
2535 LineHeight::Normal => font_metrics.line_gap,
2536 LineHeight::Number(number) => (font_size * number.0).into(),
2537 LineHeight::Length(length) => length.0.into(),
2538 };
2539
2540 if flags.contains(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT) {
2544 line_height.max_assign(font_metrics.line_gap);
2545 }
2546
2547 line_height
2548}
2549
2550fn effective_baseline_shift(
2551 style: &ComputedValues,
2552 container: Option<&InlineContainerState>,
2553) -> BaselineShift {
2554 if container.is_none() {
2555 BaselineShift::zero()
2559 } else {
2560 style.clone_baseline_shift()
2561 }
2562}
2563
2564fn is_baseline_relative(baseline_shift: BaselineShift) -> bool {
2565 !matches!(
2566 baseline_shift,
2567 BaselineShift::Keyword(
2568 BaselineShiftKeyword::Top | BaselineShiftKeyword::Bottom | BaselineShiftKeyword::Center
2569 )
2570 )
2571}
2572
2573fn inline_container_needs_strut(
2599 style: &ComputedValues,
2600 layout_context: &LayoutContext,
2601 pbm: Option<&PaddingBorderMargin>,
2602) -> bool {
2603 if layout_context.style_context.quirks_mode() == QuirksMode::NoQuirks {
2604 return true;
2605 }
2606
2607 if style.get_box().display.is_list_item() {
2610 return true;
2611 }
2612
2613 pbm.is_some_and(|pbm| !pbm.padding_border_sums.inline.is_zero())
2614}
2615
2616impl ComputeInlineContentSizes for InlineFormattingContext {
2617 fn compute_inline_content_sizes(
2621 &self,
2622 layout_context: &LayoutContext,
2623 constraint_space: &ConstraintSpace,
2624 ) -> InlineContentSizesResult {
2625 ContentSizesComputation::compute(self, layout_context, constraint_space)
2626 }
2627}
2628
2629struct ContentSizesComputation<'layout_data> {
2631 layout_context: &'layout_data LayoutContext<'layout_data>,
2632 constraint_space: &'layout_data ConstraintSpace<'layout_data>,
2633 paragraph: ContentSizes,
2634 current_line: ContentSizes,
2635 pending_whitespace: ContentSizes,
2637 uncleared_floats: LogicalSides1D<ContentSizes>,
2639 cleared_floats: LogicalSides1D<ContentSizes>,
2641 had_content_yet_for_min_content: bool,
2644 had_content_yet_for_max_content: bool,
2647 ending_inline_pbm_stack: Vec<Au>,
2650 depends_on_block_constraints: bool,
2651}
2652
2653impl<'layout_data> ContentSizesComputation<'layout_data> {
2654 fn traverse(
2655 mut self,
2656 inline_formatting_context: &InlineFormattingContext,
2657 ) -> InlineContentSizesResult {
2658 self.add_inline_size(
2659 inline_formatting_context.inline_start_for_first_line(self.constraint_space.into()),
2660 );
2661 for inline_item in &inline_formatting_context.inline_items {
2662 self.process_item(inline_item, inline_formatting_context);
2663 }
2664 self.forced_line_break();
2665 self.flush_floats();
2666
2667 InlineContentSizesResult {
2668 sizes: self.paragraph,
2669 depends_on_block_constraints: self.depends_on_block_constraints,
2670 }
2671 }
2672
2673 fn process_item(
2674 &mut self,
2675 inline_item: &InlineItem,
2676 inline_formatting_context: &InlineFormattingContext,
2677 ) {
2678 match inline_item {
2679 InlineItem::StartInlineBox(inline_box) => {
2680 let inline_box = inline_box.borrow();
2684 let zero = Au::zero();
2685 let writing_mode = self.constraint_space.style.writing_mode;
2686 let layout_style = inline_box.layout_style();
2687 let padding = layout_style
2688 .padding(writing_mode)
2689 .percentages_relative_to(zero);
2690 let border = layout_style.border_width(writing_mode);
2691 let margin = inline_box
2692 .base
2693 .style
2694 .margin(writing_mode)
2695 .percentages_relative_to(zero)
2696 .auto_is(Au::zero);
2697
2698 let pbm = margin + padding + border;
2699 self.add_inline_size(pbm.inline_start);
2700 self.ending_inline_pbm_stack.push(pbm.inline_end);
2701 },
2702 InlineItem::EndInlineBox => {
2703 let length = self.ending_inline_pbm_stack.pop().unwrap_or_else(Au::zero);
2704 self.add_inline_size(length);
2705 },
2706 InlineItem::TextRun(text_run) => {
2707 let text_run = &*text_run.borrow();
2708 let parent_style = text_run.inline_styles.style.borrow();
2709 for segment in text_run.shaped_text.iter() {
2710 let style_text = parent_style.get_inherited_text();
2711 let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap;
2712
2713 let break_at_start =
2716 segment.break_at_start && self.had_content_yet_for_min_content;
2717
2718 for (run_index, run) in segment.runs.iter().enumerate() {
2719 if can_wrap && (run_index != 0 || break_at_start) {
2722 self.line_break_opportunity();
2723 }
2724
2725 let advance = run.total_advance();
2726 if run.is_whitespace() {
2727 if run.is_single_preserved_newline() {
2730 self.forced_line_break();
2731 continue;
2732 }
2733 if !matches!(
2734 style_text.white_space_collapse,
2735 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
2736 ) {
2737 if self.had_content_yet_for_min_content {
2738 if can_wrap {
2739 self.line_break_opportunity();
2740 } else {
2741 self.pending_whitespace.min_content += advance;
2742 }
2743 }
2744 if self.had_content_yet_for_max_content {
2745 self.pending_whitespace.max_content += advance;
2746 }
2747 continue;
2748 }
2749 if can_wrap {
2750 self.pending_whitespace.max_content += advance;
2751 self.commit_pending_whitespace();
2752 self.line_break_opportunity();
2753 continue;
2754 }
2755 }
2756
2757 self.commit_pending_whitespace();
2758 self.add_inline_size(advance);
2759
2760 if can_wrap && run.ends_with_whitespace() {
2765 self.line_break_opportunity();
2766 }
2767 }
2768 }
2769 },
2770 InlineItem::Atomic(atomic, offset_in_text, _level) => {
2771 if self.had_content_yet_for_min_content &&
2773 !inline_formatting_context
2774 .previous_character_prevents_soft_wrap_opportunity(*offset_in_text)
2775 {
2776 self.line_break_opportunity();
2777 }
2778
2779 self.commit_pending_whitespace();
2780 let outer = self.outer_inline_content_sizes_of_float_or_atomic(&atomic.borrow());
2781 self.current_line += outer;
2782
2783 if !inline_formatting_context
2785 .next_character_prevents_soft_wrap_opportunity(*offset_in_text)
2786 {
2787 self.line_break_opportunity();
2788 }
2789 },
2790 InlineItem::OutOfFlowFloatBox(float_box) => {
2791 let float_box = float_box.borrow();
2792 let sizes = self.outer_inline_content_sizes_of_float_or_atomic(&float_box.contents);
2793 let style = &float_box.contents.style();
2794 let container_writing_mode = self.constraint_space.style.writing_mode;
2795 let clear =
2796 Clear::from_style_and_container_writing_mode(style, container_writing_mode);
2797 self.clear_floats(clear);
2798 let float_side =
2799 FloatSide::from_style_and_container_writing_mode(style, container_writing_mode);
2800 match float_side.expect("A float box needs to float to some side") {
2801 FloatSide::InlineStart => self.uncleared_floats.start.union_assign(&sizes),
2802 FloatSide::InlineEnd => self.uncleared_floats.end.union_assign(&sizes),
2803 }
2804 },
2805 InlineItem::BlockLevel(block_level) => {
2806 self.forced_line_break();
2807 self.flush_floats();
2808 let inline_content_sizes_result =
2809 compute_inline_content_sizes_for_block_level_boxes(
2810 std::slice::from_ref(block_level),
2811 self.layout_context,
2812 &self.constraint_space.into(),
2813 );
2814 self.depends_on_block_constraints |=
2815 inline_content_sizes_result.depends_on_block_constraints;
2816 self.current_line = inline_content_sizes_result.sizes;
2817 self.forced_line_break();
2818 },
2819 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => {},
2820 }
2821 }
2822
2823 fn add_inline_size(&mut self, l: Au) {
2824 self.current_line.min_content += l;
2825 self.current_line.max_content += l;
2826 }
2827
2828 fn line_break_opportunity(&mut self) {
2829 self.pending_whitespace.min_content = Au::zero();
2833 let current_min_content = mem::take(&mut self.current_line.min_content);
2834 self.paragraph.min_content.max_assign(current_min_content);
2835 self.had_content_yet_for_min_content = false;
2836 }
2837
2838 fn forced_line_break(&mut self) {
2839 self.line_break_opportunity();
2841
2842 self.pending_whitespace.max_content = Au::zero();
2844 let current_max_content = mem::take(&mut self.current_line.max_content);
2845 self.paragraph.max_content.max_assign(current_max_content);
2846 self.had_content_yet_for_max_content = false;
2847 }
2848
2849 fn commit_pending_whitespace(&mut self) {
2850 self.current_line += mem::take(&mut self.pending_whitespace);
2851 self.had_content_yet_for_min_content = true;
2852 self.had_content_yet_for_max_content = true;
2853 }
2854
2855 fn outer_inline_content_sizes_of_float_or_atomic(
2856 &mut self,
2857 context: &IndependentFormattingContext,
2858 ) -> ContentSizes {
2859 let result = context.outer_inline_content_sizes(
2860 self.layout_context,
2861 &self.constraint_space.into(),
2862 &LogicalVec2::zero(),
2863 false, );
2865 self.depends_on_block_constraints |= result.depends_on_block_constraints;
2866 result.sizes
2867 }
2868
2869 fn clear_floats(&mut self, clear: Clear) {
2870 match clear {
2871 Clear::InlineStart => {
2872 let start_floats = mem::take(&mut self.uncleared_floats.start);
2873 self.cleared_floats.start.max_assign(start_floats);
2874 },
2875 Clear::InlineEnd => {
2876 let end_floats = mem::take(&mut self.uncleared_floats.end);
2877 self.cleared_floats.end.max_assign(end_floats);
2878 },
2879 Clear::Both => {
2880 let start_floats = mem::take(&mut self.uncleared_floats.start);
2881 let end_floats = mem::take(&mut self.uncleared_floats.end);
2882 self.cleared_floats.start.max_assign(start_floats);
2883 self.cleared_floats.end.max_assign(end_floats);
2884 },
2885 Clear::None => {},
2886 }
2887 }
2888
2889 fn flush_floats(&mut self) {
2890 self.clear_floats(Clear::Both);
2891 let start_floats = mem::take(&mut self.cleared_floats.start);
2892 let end_floats = mem::take(&mut self.cleared_floats.end);
2893 self.paragraph.union_assign(&start_floats);
2894 self.paragraph.union_assign(&end_floats);
2895 }
2896
2897 fn compute(
2899 inline_formatting_context: &InlineFormattingContext,
2900 layout_context: &'layout_data LayoutContext,
2901 constraint_space: &'layout_data ConstraintSpace,
2902 ) -> InlineContentSizesResult {
2903 Self {
2904 layout_context,
2905 constraint_space,
2906 paragraph: ContentSizes::zero(),
2907 current_line: ContentSizes::zero(),
2908 pending_whitespace: ContentSizes::zero(),
2909 uncleared_floats: LogicalSides1D::default(),
2910 cleared_floats: LogicalSides1D::default(),
2911 had_content_yet_for_min_content: false,
2912 had_content_yet_for_max_content: false,
2913 ending_inline_pbm_stack: Vec::new(),
2914 depends_on_block_constraints: false,
2915 }
2916 .traverse(inline_formatting_context)
2917 }
2918}
2919
2920fn char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character: char) -> bool {
2932 if character == '\u{00A0}' {
2933 return false;
2934 }
2935 matches!(
2936 icu_properties::maps::line_break().get(character),
2937 ICULineBreak::Glue | ICULineBreak::WordJoiner | ICULineBreak::ZWJ
2938 )
2939}