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, FontRef, GlyphStore};
86use icu_segmenter::{LineBreakOptions, LineBreakStrictness, LineBreakWordOption};
87use inline_box::{InlineBox, InlineBoxContainerState, InlineBoxIdentifier, InlineBoxes};
88use layout_api::wrapper_traits::SharedSelection;
89use line::{
90 AbsolutelyPositionedLineItem, AtomicLineItem, FloatLineItem, LineItem, LineItemLayout,
91 TextRunLineItem,
92};
93use line_breaker::LineBreaker;
94use malloc_size_of_derive::MallocSizeOf;
95use script::layout_dom::ServoThreadSafeLayoutNode;
96use servo_arc::Arc as ServoArc;
97use style::Zero;
98use style::computed_values::line_break::T as LineBreak;
99use style::computed_values::text_wrap_mode::T as TextWrapMode;
100use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
101use style::computed_values::word_break::T as WordBreak;
102use style::context::{QuirksMode, SharedStyleContext};
103use style::properties::ComputedValues;
104use style::properties::style_structs::InheritedText;
105use style::values::computed::BaselineShift;
106use style::values::generics::box_::BaselineShiftKeyword;
107use style::values::generics::font::LineHeight;
108use style::values::specified::box_::BaselineSource;
109use style::values::specified::text::TextAlignKeyword;
110use style::values::specified::{AlignmentBaseline, TextAlignLast, TextJustify};
111use text_run::{
112 TextRun, XI_LINE_BREAKING_CLASS_GL, XI_LINE_BREAKING_CLASS_WJ, XI_LINE_BREAKING_CLASS_ZWJ,
113 get_font_for_first_font_for_style,
114};
115use unicode_bidi::{BidiInfo, Level};
116use xi_unicode::linebreak_property;
117
118use super::float::{Clear, PlacementAmongFloats};
119use super::{CacheableLayoutResult, IndependentFloatOrAtomicLayoutResult};
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::{
127 BlockContainer, CollapsibleWithParentStartMargin, FloatSide, PlacementState,
128 layout_in_flow_non_replaced_block_level_same_formatting_context,
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::{
138 ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, outer_inline,
139};
140use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
141use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SharedStyle};
142
143static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
145static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
146
147#[derive(Debug, MallocSizeOf)]
148pub(crate) struct InlineFormattingContext {
149 inline_items: Vec<InlineItem>,
154
155 inline_boxes: InlineBoxes,
158
159 text_content: String,
161
162 shared_inline_styles: SharedInlineStyles,
165
166 has_first_formatted_line: bool,
169
170 pub(super) contains_floats: bool,
172
173 is_single_line_text_input: bool,
176
177 has_right_to_left_content: bool,
180
181 #[ignore_malloc_size_of = "This is stored primarily in the DOM"]
184 shared_selection: Option<SharedSelection>,
185}
186
187#[derive(Clone, Debug, MallocSizeOf)]
192pub(crate) struct SharedInlineStyles {
193 pub style: SharedStyle,
194 pub selected: SharedStyle,
195}
196
197impl SharedInlineStyles {
198 pub(crate) fn ptr_eq(&self, other: &Self) -> bool {
199 self.style.ptr_eq(&other.style) && self.selected.ptr_eq(&other.selected)
200 }
201
202 pub(crate) fn from_info_and_context(info: &NodeAndStyleInfo, context: &LayoutContext) -> Self {
203 Self {
204 style: SharedStyle::new(info.style.clone()),
205 selected: SharedStyle::new(info.node.selected_style(&context.style_context)),
206 }
207 }
208}
209
210#[derive(Debug, MallocSizeOf)]
214pub(crate) struct AnonymousBlockBox {
215 base: LayoutBoxBase,
216 contents: BlockContainer,
217}
218
219impl AnonymousBlockBox {
220 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
221 layout.process_soft_wrap_opportunity();
222 layout.commit_current_segment_to_line();
223 layout.process_line_break(true);
224 layout.current_line.for_block_level = true;
225
226 let fragment = layout
227 .positioning_context
228 .layout_maybe_position_relative_fragment(
229 layout.layout_context,
230 layout.placement_state.containing_block,
231 &self.base,
232 |positioning_context| {
233 layout_in_flow_non_replaced_block_level_same_formatting_context(
234 layout.layout_context,
235 positioning_context,
236 layout.placement_state.containing_block,
237 &self.base,
238 &self.contents,
239 layout.sequential_layout_state.as_deref_mut(),
240 Some(CollapsibleWithParentStartMargin(
241 layout
242 .placement_state
243 .next_in_flow_margin_collapses_with_parent_start_margin,
244 )),
245 LogicalSides1D::new(false, false),
249 )
250 },
251 );
252
253 layout.depends_on_block_constraints |= fragment.base.flags.contains(
256 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
257 );
258
259 let mut fragment = Fragment::Box(ArcRefCell::new(fragment));
260 layout.placement_state.place_fragment_and_update_baseline(
261 &mut fragment,
262 layout.sequential_layout_state.as_deref_mut(),
263 );
264
265 let Fragment::Box(fragment) = fragment else {
266 unreachable!("The fragment should still be a Fragment::Box()");
267 };
268 layout.push_line_item_to_unbreakable_segment(LineItem::AnonymousBlockBox(
269 layout.current_inline_box_identifier(),
270 fragment,
271 ));
272
273 layout.commit_current_segment_to_line();
274 layout.process_line_break(true);
275 layout.current_line.for_block_level = false;
276 }
277}
278
279#[derive(Clone, Debug, MallocSizeOf)]
280pub(crate) enum InlineItem {
281 StartInlineBox(ArcRefCell<InlineBox>),
282 EndInlineBox,
283 TextRun(ArcRefCell<TextRun>),
284 OutOfFlowAbsolutelyPositionedBox(
285 ArcRefCell<AbsolutelyPositionedBox>,
286 usize, ),
288 OutOfFlowFloatBox(ArcRefCell<FloatBox>),
289 Atomic(
290 ArcRefCell<IndependentFormattingContext>,
291 usize, Level, ),
294 AnonymousBlock(ArcRefCell<AnonymousBlockBox>),
295}
296
297impl InlineItem {
298 pub(crate) fn repair_style(
299 &self,
300 context: &SharedStyleContext,
301 node: &ServoThreadSafeLayoutNode,
302 new_style: &ServoArc<ComputedValues>,
303 ) {
304 match self {
305 InlineItem::StartInlineBox(inline_box) => {
306 inline_box
307 .borrow_mut()
308 .repair_style(context, node, new_style);
309 },
310 InlineItem::EndInlineBox => {},
311 InlineItem::TextRun(..) => {},
314 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box
315 .borrow_mut()
316 .context
317 .repair_style(context, node, new_style),
318 InlineItem::OutOfFlowFloatBox(float_box) => float_box
319 .borrow_mut()
320 .contents
321 .repair_style(context, node, new_style),
322 InlineItem::Atomic(atomic, ..) => {
323 atomic.borrow_mut().repair_style(context, node, new_style)
324 },
325 InlineItem::AnonymousBlock(block_box) => {
326 let mut block_box = block_box.borrow_mut();
327 block_box.base.repair_style(new_style);
328 block_box.contents.repair_style(context, node, new_style);
329 },
330 }
331 }
332
333 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
334 match self {
335 InlineItem::StartInlineBox(inline_box) => callback(&inline_box.borrow().base),
336 InlineItem::EndInlineBox | InlineItem::TextRun(..) => {
337 unreachable!("Should never have these kind of fragments attached to a DOM node")
338 },
339 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
340 callback(&positioned_box.borrow().context.base)
341 },
342 InlineItem::OutOfFlowFloatBox(float_box) => callback(&float_box.borrow().contents.base),
343 InlineItem::Atomic(independent_formatting_context, ..) => {
344 callback(&independent_formatting_context.borrow().base)
345 },
346 InlineItem::AnonymousBlock(block_box) => callback(&block_box.borrow().base),
347 }
348 }
349
350 pub(crate) fn with_base_mut<T>(&self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
351 match self {
352 InlineItem::StartInlineBox(inline_box) => callback(&mut inline_box.borrow_mut().base),
353 InlineItem::EndInlineBox | InlineItem::TextRun(..) => {
354 unreachable!("Should never have these kind of fragments attached to a DOM node")
355 },
356 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
357 callback(&mut positioned_box.borrow_mut().context.base)
358 },
359 InlineItem::OutOfFlowFloatBox(float_box) => {
360 callback(&mut float_box.borrow_mut().contents.base)
361 },
362 InlineItem::Atomic(independent_formatting_context, ..) => {
363 callback(&mut independent_formatting_context.borrow_mut().base)
364 },
365 InlineItem::AnonymousBlock(block_box) => callback(&mut block_box.borrow_mut().base),
366 }
367 }
368
369 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
370 match self {
371 Self::StartInlineBox(_) | InlineItem::EndInlineBox => {
372 },
375 Self::TextRun(_) => {
376 },
378 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
379 positioned_box.borrow().context.attached_to_tree(layout_box)
380 },
381 Self::OutOfFlowFloatBox(float_box) => {
382 float_box.borrow().contents.attached_to_tree(layout_box)
383 },
384 Self::Atomic(atomic, ..) => atomic.borrow().attached_to_tree(layout_box),
385 Self::AnonymousBlock(block_box) => {
386 block_box.borrow().contents.attached_to_tree(layout_box)
387 },
388 }
389 }
390
391 pub(crate) fn downgrade(&self) -> WeakInlineItem {
392 match self {
393 Self::StartInlineBox(inline_box) => {
394 WeakInlineItem::StartInlineBox(inline_box.downgrade())
395 },
396 Self::EndInlineBox => WeakInlineItem::EndInlineBox,
397 Self::TextRun(text_run) => WeakInlineItem::TextRun(text_run.downgrade()),
398 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
399 WeakInlineItem::OutOfFlowAbsolutelyPositionedBox(
400 positioned_box.downgrade(),
401 *offset_in_text,
402 )
403 },
404 Self::OutOfFlowFloatBox(float_box) => {
405 WeakInlineItem::OutOfFlowFloatBox(float_box.downgrade())
406 },
407 Self::Atomic(atomic, offset_in_text, bidi_level) => {
408 WeakInlineItem::Atomic(atomic.downgrade(), *offset_in_text, *bidi_level)
409 },
410 Self::AnonymousBlock(block_box) => {
411 WeakInlineItem::AnonymousBlock(block_box.downgrade())
412 },
413 }
414 }
415}
416
417#[derive(Clone, Debug, MallocSizeOf)]
418pub(crate) enum WeakInlineItem {
419 StartInlineBox(WeakRefCell<InlineBox>),
420 EndInlineBox,
421 TextRun(WeakRefCell<TextRun>),
422 OutOfFlowAbsolutelyPositionedBox(
423 WeakRefCell<AbsolutelyPositionedBox>,
424 usize, ),
426 OutOfFlowFloatBox(WeakRefCell<FloatBox>),
427 Atomic(
428 WeakRefCell<IndependentFormattingContext>,
429 usize, Level, ),
432 AnonymousBlock(WeakRefCell<AnonymousBlockBox>),
433}
434
435impl WeakInlineItem {
436 pub(crate) fn upgrade(&self) -> Option<InlineItem> {
437 Some(match self {
438 Self::StartInlineBox(inline_box) => InlineItem::StartInlineBox(inline_box.upgrade()?),
439 Self::EndInlineBox => InlineItem::EndInlineBox,
440 Self::TextRun(text_run) => InlineItem::TextRun(text_run.upgrade()?),
441 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
442 InlineItem::OutOfFlowAbsolutelyPositionedBox(
443 positioned_box.upgrade()?,
444 *offset_in_text,
445 )
446 },
447 Self::OutOfFlowFloatBox(float_box) => {
448 InlineItem::OutOfFlowFloatBox(float_box.upgrade()?)
449 },
450 Self::Atomic(atomic, offset_in_text, bidi_level) => {
451 InlineItem::Atomic(atomic.upgrade()?, *offset_in_text, *bidi_level)
452 },
453 Self::AnonymousBlock(block_box) => InlineItem::AnonymousBlock(block_box.upgrade()?),
454 })
455 }
456}
457
458struct LineUnderConstruction {
465 start_position: LogicalVec2<Au>,
468
469 inline_position: Au,
472
473 max_block_size: LineBlockSizes,
477
478 has_content: bool,
481
482 has_inline_pbm: bool,
485
486 has_floats_waiting_to_be_placed: bool,
490
491 placement_among_floats: OnceCell<LogicalRect<Au>>,
496
497 line_items: Vec<LineItem>,
500
501 for_block_level: bool,
503}
504
505impl LineUnderConstruction {
506 fn new(start_position: LogicalVec2<Au>) -> Self {
507 Self {
508 inline_position: start_position.inline,
509 start_position,
510 max_block_size: LineBlockSizes::zero(),
511 has_content: false,
512 has_inline_pbm: false,
513 has_floats_waiting_to_be_placed: false,
514 placement_among_floats: OnceCell::new(),
515 line_items: Vec::new(),
516 for_block_level: false,
517 }
518 }
519
520 fn replace_placement_among_floats(&mut self, new_placement: LogicalRect<Au>) {
521 self.placement_among_floats.take();
522 let _ = self.placement_among_floats.set(new_placement);
523 }
524
525 fn trim_trailing_whitespace(&mut self) -> Au {
527 let mut whitespace_trimmed = Au::zero();
532 for item in self.line_items.iter_mut().rev() {
533 if !item.trim_whitespace_at_end(&mut whitespace_trimmed) {
534 break;
535 }
536 }
537
538 whitespace_trimmed
539 }
540
541 fn count_justification_opportunities(&self) -> usize {
543 self.line_items
544 .iter()
545 .filter_map(|item| match item {
546 LineItem::TextRun(_, text_run) => Some(
547 text_run
548 .text
549 .iter()
550 .map(|glyph_store| glyph_store.total_word_separators())
551 .sum::<usize>(),
552 ),
553 _ => None,
554 })
555 .sum()
556 }
557
558 fn is_phantom(&self) -> bool {
561 !self.has_content && !self.has_inline_pbm
563 }
564}
565
566#[derive(Clone, Debug)]
572struct BaselineRelativeSize {
573 ascent: Au,
577
578 descent: Au,
582}
583
584impl BaselineRelativeSize {
585 fn zero() -> Self {
586 Self {
587 ascent: Au::zero(),
588 descent: Au::zero(),
589 }
590 }
591
592 fn max(&self, other: &Self) -> Self {
593 BaselineRelativeSize {
594 ascent: self.ascent.max(other.ascent),
595 descent: self.descent.max(other.descent),
596 }
597 }
598
599 fn adjust_for_nested_baseline_offset(&mut self, baseline_offset: Au) {
613 self.ascent -= baseline_offset;
614 self.descent += baseline_offset;
615 }
616}
617
618#[derive(Clone, Debug)]
619struct LineBlockSizes {
620 line_height: Au,
621 baseline_relative_size_for_line_height: Option<BaselineRelativeSize>,
622 size_for_baseline_positioning: BaselineRelativeSize,
623}
624
625impl LineBlockSizes {
626 fn zero() -> Self {
627 LineBlockSizes {
628 line_height: Au::zero(),
629 baseline_relative_size_for_line_height: None,
630 size_for_baseline_positioning: BaselineRelativeSize::zero(),
631 }
632 }
633
634 fn resolve(&self) -> Au {
635 let height_from_ascent_and_descent = self
636 .baseline_relative_size_for_line_height
637 .as_ref()
638 .map(|size| (size.ascent + size.descent).abs())
639 .unwrap_or_else(Au::zero);
640 self.line_height.max(height_from_ascent_and_descent)
641 }
642
643 fn max(&self, other: &LineBlockSizes) -> LineBlockSizes {
644 let baseline_relative_size = match (
645 self.baseline_relative_size_for_line_height.as_ref(),
646 other.baseline_relative_size_for_line_height.as_ref(),
647 ) {
648 (Some(our_size), Some(other_size)) => Some(our_size.max(other_size)),
649 (our_size, other_size) => our_size.or(other_size).cloned(),
650 };
651 Self {
652 line_height: self.line_height.max(other.line_height),
653 baseline_relative_size_for_line_height: baseline_relative_size,
654 size_for_baseline_positioning: self
655 .size_for_baseline_positioning
656 .max(&other.size_for_baseline_positioning),
657 }
658 }
659
660 fn max_assign(&mut self, other: &LineBlockSizes) {
661 *self = self.max(other);
662 }
663
664 fn adjust_for_baseline_offset(&mut self, baseline_offset: Au) {
665 if let Some(size) = self.baseline_relative_size_for_line_height.as_mut() {
666 size.adjust_for_nested_baseline_offset(baseline_offset)
667 }
668 self.size_for_baseline_positioning
669 .adjust_for_nested_baseline_offset(baseline_offset);
670 }
671
672 fn find_baseline_offset(&self) -> Au {
679 match self.baseline_relative_size_for_line_height.as_ref() {
680 Some(size) => size.ascent,
681 None => {
682 let leading = self.resolve() -
685 (self.size_for_baseline_positioning.ascent +
686 self.size_for_baseline_positioning.descent);
687 leading.scale_by(0.5) + self.size_for_baseline_positioning.ascent
688 },
689 }
690 }
691}
692
693struct UnbreakableSegmentUnderConstruction {
697 inline_size: Au,
699
700 max_block_size: LineBlockSizes,
703
704 line_items: Vec<LineItem>,
706
707 inline_box_hierarchy_depth: Option<usize>,
710
711 has_content: bool,
715
716 has_inline_pbm: bool,
719
720 trailing_whitespace_size: Au,
722}
723
724impl UnbreakableSegmentUnderConstruction {
725 fn new() -> Self {
726 Self {
727 inline_size: Au::zero(),
728 max_block_size: LineBlockSizes {
729 line_height: Au::zero(),
730 baseline_relative_size_for_line_height: None,
731 size_for_baseline_positioning: BaselineRelativeSize::zero(),
732 },
733 line_items: Vec::new(),
734 inline_box_hierarchy_depth: None,
735 has_content: false,
736 has_inline_pbm: false,
737 trailing_whitespace_size: Au::zero(),
738 }
739 }
740
741 fn reset(&mut self) {
743 assert!(self.line_items.is_empty()); self.inline_size = Au::zero();
745 self.max_block_size = LineBlockSizes::zero();
746 self.inline_box_hierarchy_depth = None;
747 self.has_content = false;
748 self.has_inline_pbm = false;
749 self.trailing_whitespace_size = Au::zero();
750 }
751
752 fn push_line_item(&mut self, line_item: LineItem, inline_box_hierarchy_depth: usize) {
757 if self.line_items.is_empty() {
758 self.inline_box_hierarchy_depth = Some(inline_box_hierarchy_depth);
759 }
760 self.line_items.push(line_item);
761 }
762
763 fn trim_leading_whitespace(&mut self) {
774 let mut whitespace_trimmed = Au::zero();
775 for item in self.line_items.iter_mut() {
776 if !item.trim_whitespace_at_start(&mut whitespace_trimmed) {
777 break;
778 }
779 }
780 self.inline_size -= whitespace_trimmed;
781 }
782
783 fn is_phantom(&self) -> bool {
786 !self.has_content && !self.has_inline_pbm
788 }
789}
790
791bitflags! {
792 struct InlineContainerStateFlags: u8 {
793 const CREATE_STRUT = 0b0001;
794 const IS_SINGLE_LINE_TEXT_INPUT = 0b0010;
795 }
796}
797
798struct InlineContainerState {
799 style: ServoArc<ComputedValues>,
801
802 flags: InlineContainerStateFlags,
804
805 has_content: RefCell<bool>,
808
809 strut_block_sizes: LineBlockSizes,
814
815 nested_strut_block_sizes: LineBlockSizes,
819
820 pub baseline_offset: Au,
826
827 font_metrics: Arc<FontMetrics>,
829}
830
831struct InlineFormattingContextLayout<'layout_data> {
832 positioning_context: &'layout_data mut PositioningContext,
833 placement_state: PlacementState<'layout_data>,
834 sequential_layout_state: Option<&'layout_data mut SequentialLayoutState>,
835 layout_context: &'layout_data LayoutContext<'layout_data>,
836
837 ifc: &'layout_data InlineFormattingContext,
839
840 root_nesting_level: InlineContainerState,
850
851 inline_box_state_stack: Vec<Rc<InlineBoxContainerState>>,
855
856 inline_box_states: Vec<Rc<InlineBoxContainerState>>,
861
862 fragments: Vec<Fragment>,
866
867 current_line: LineUnderConstruction,
869
870 current_line_segment: UnbreakableSegmentUnderConstruction,
872
873 linebreak_before_new_content: bool,
892
893 deferred_br_clear: Clear,
897
898 pub have_deferred_soft_wrap_opportunity: bool,
902
903 depends_on_block_constraints: bool,
906
907 white_space_collapse: WhiteSpaceCollapse,
912
913 text_wrap_mode: TextWrapMode,
918}
919
920impl InlineFormattingContextLayout<'_> {
921 fn current_inline_container_state(&self) -> &InlineContainerState {
922 match self.inline_box_state_stack.last() {
923 Some(inline_box_state) => &inline_box_state.base,
924 None => &self.root_nesting_level,
925 }
926 }
927
928 fn current_inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
929 self.inline_box_state_stack
930 .last()
931 .map(|state| state.identifier)
932 }
933
934 fn current_line_max_block_size_including_nested_containers(&self) -> LineBlockSizes {
935 self.current_inline_container_state()
936 .nested_strut_block_sizes
937 .max(&self.current_line.max_block_size)
938 }
939
940 fn current_line_block_start_considering_placement_among_floats(&self) -> Au {
941 self.current_line.placement_among_floats.get().map_or(
942 self.current_line.start_position.block,
943 |placement_among_floats| placement_among_floats.start_corner.block,
944 )
945 }
946
947 fn propagate_current_nesting_level_white_space_style(&mut self) {
948 let style = match self.inline_box_state_stack.last() {
949 Some(inline_box_state) => &inline_box_state.base.style,
950 None => self.placement_state.containing_block.style,
951 };
952 let style_text = style.get_inherited_text();
953 self.white_space_collapse = style_text.white_space_collapse;
954 self.text_wrap_mode = style_text.text_wrap_mode;
955 }
956
957 fn processing_br_element(&self) -> bool {
958 self.inline_box_state_stack.last().is_some_and(|state| {
959 state
960 .base_fragment_info
961 .flags
962 .contains(FragmentFlags::IS_BR_ELEMENT)
963 })
964 }
965
966 fn start_inline_box(&mut self, inline_box: &InlineBox) {
969 let containing_block = self.containing_block();
970 let inline_box_state = InlineBoxContainerState::new(
971 inline_box,
972 containing_block,
973 self.layout_context,
974 self.current_inline_container_state(),
975 inline_box
976 .default_font
977 .as_ref()
978 .map(|font| font.metrics.clone()),
979 );
980
981 self.depends_on_block_constraints |= inline_box
982 .base
983 .style
984 .depends_on_block_constraints_due_to_relative_positioning(
985 containing_block.style.writing_mode,
986 );
987
988 if inline_box_state
993 .base_fragment_info
994 .flags
995 .contains(FragmentFlags::IS_BR_ELEMENT) &&
996 self.deferred_br_clear == Clear::None
997 {
998 self.deferred_br_clear = Clear::from_style_and_container_writing_mode(
999 &inline_box_state.base.style,
1000 self.containing_block().style.writing_mode,
1001 );
1002 }
1003
1004 let padding = inline_box_state.pbm.padding.inline_start;
1005 let border = inline_box_state.pbm.border.inline_start;
1006 let margin = inline_box_state.pbm.margin.inline_start.auto_is(Au::zero);
1007 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
1010 self.current_line_segment.has_inline_pbm = true;
1011 }
1012 self.current_line_segment.inline_size += padding + border + margin;
1013 self.current_line_segment
1014 .line_items
1015 .push(LineItem::InlineStartBoxPaddingBorderMargin(
1016 inline_box.identifier,
1017 ));
1018
1019 let inline_box_state = Rc::new(inline_box_state);
1020
1021 assert_eq!(
1025 self.inline_box_states.len(),
1026 inline_box.identifier.index_in_inline_boxes as usize
1027 );
1028 self.inline_box_states.push(inline_box_state.clone());
1029 self.inline_box_state_stack.push(inline_box_state);
1030 }
1031
1032 fn finish_inline_box(&mut self) {
1035 let inline_box_state = match self.inline_box_state_stack.pop() {
1036 Some(inline_box_state) => inline_box_state,
1037 None => return, };
1039
1040 self.current_line_segment
1041 .max_block_size
1042 .max_assign(&inline_box_state.base.nested_strut_block_sizes);
1043
1044 if *inline_box_state.base.has_content.borrow() {
1049 self.propagate_current_nesting_level_white_space_style();
1050 }
1051
1052 let padding = inline_box_state.pbm.padding.inline_end;
1053 let border = inline_box_state.pbm.border.inline_end;
1054 let margin = inline_box_state.pbm.margin.inline_end.auto_is(Au::zero);
1055 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
1058 self.current_line_segment.has_inline_pbm = true;
1059 }
1060 self.current_line_segment.inline_size += padding + border + margin;
1061 self.current_line_segment
1062 .line_items
1063 .push(LineItem::InlineEndBoxPaddingBorderMargin(
1064 inline_box_state.identifier,
1065 ))
1066 }
1067
1068 fn finish_last_line(&mut self) {
1069 self.process_soft_wrap_opportunity();
1075
1076 self.commit_current_segment_to_line();
1079
1080 self.finish_current_line_and_reset(true );
1083 }
1084
1085 fn finish_current_line_and_reset(&mut self, last_line_or_forced_line_break: bool) {
1089 let whitespace_trimmed = self.current_line.trim_trailing_whitespace();
1090 let (inline_start_position, justification_adjustment) = self
1091 .calculate_current_line_inline_start_and_justification_adjustment(
1092 whitespace_trimmed,
1093 last_line_or_forced_line_break,
1094 );
1095
1096 let is_phantom_line = self.current_line.is_phantom();
1105 if !is_phantom_line {
1106 self.current_line.start_position.block += self.placement_state.current_margin.solve();
1107 self.placement_state.current_margin = CollapsedMargin::zero();
1108 }
1109 let block_start_position =
1110 self.current_line_block_start_considering_placement_among_floats();
1111
1112 let effective_block_advance = if is_phantom_line {
1113 LineBlockSizes::zero()
1114 } else {
1115 self.current_line_max_block_size_including_nested_containers()
1116 };
1117
1118 let resolved_block_advance = effective_block_advance.resolve();
1119 let block_end_position = if self.current_line.for_block_level {
1120 self.placement_state.current_block_direction_position
1121 } else {
1122 let mut block_end_position = block_start_position + resolved_block_advance;
1123 if let Some(sequential_layout_state) = self.sequential_layout_state.as_mut() {
1124 if !is_phantom_line {
1125 sequential_layout_state.collapse_margins();
1126 }
1127
1128 let increment = block_end_position - self.current_line.start_position.block;
1131 sequential_layout_state.advance_block_position(increment);
1132
1133 if let Some(clearance) = sequential_layout_state
1137 .calculate_clearance(self.deferred_br_clear, &CollapsedMargin::zero())
1138 {
1139 sequential_layout_state.advance_block_position(clearance);
1140 block_end_position += clearance;
1141 };
1142 self.deferred_br_clear = Clear::None;
1143 }
1144 block_end_position
1145 };
1146
1147 let mut line_to_layout = std::mem::replace(
1149 &mut self.current_line,
1150 LineUnderConstruction::new(LogicalVec2 {
1151 inline: Au::zero(),
1152 block: block_end_position,
1153 }),
1154 );
1155 if !line_to_layout.for_block_level {
1156 self.placement_state.current_block_direction_position = block_end_position;
1157 }
1158
1159 if line_to_layout.has_floats_waiting_to_be_placed {
1160 place_pending_floats(self, &mut line_to_layout.line_items);
1161 }
1162
1163 let start_position = LogicalVec2 {
1164 block: block_start_position,
1165 inline: inline_start_position,
1166 };
1167
1168 let baseline_offset = effective_block_advance.find_baseline_offset();
1169 let start_positioning_context_length = self.positioning_context.len();
1170 let fragments = LineItemLayout::layout_line_items(
1171 self,
1172 line_to_layout.line_items,
1173 start_position,
1174 &effective_block_advance,
1175 justification_adjustment,
1176 is_phantom_line,
1177 );
1178
1179 if !is_phantom_line {
1180 let baseline = baseline_offset + block_start_position;
1181 self.placement_state
1182 .inflow_baselines
1183 .first
1184 .get_or_insert(baseline);
1185 self.placement_state.inflow_baselines.last = Some(baseline);
1186 self.placement_state
1187 .next_in_flow_margin_collapses_with_parent_start_margin = false;
1188 }
1189
1190 if fragments.is_empty() &&
1192 self.positioning_context.len() == start_positioning_context_length
1193 {
1194 return;
1195 }
1196
1197 let start_corner = LogicalVec2 {
1201 inline: Au::zero(),
1202 block: block_start_position,
1203 };
1204
1205 let logical_origin_in_physical_coordinates =
1206 start_corner.to_physical_vector(self.containing_block().style.writing_mode);
1207 self.positioning_context
1208 .adjust_static_position_of_hoisted_fragments_with_offset(
1209 &logical_origin_in_physical_coordinates,
1210 start_positioning_context_length,
1211 );
1212
1213 let containing_block = self.containing_block();
1214 let physical_line_rect = LogicalRect {
1215 start_corner,
1216 size: LogicalVec2 {
1217 inline: containing_block.size.inline,
1218 block: effective_block_advance.resolve(),
1219 },
1220 }
1221 .as_physical(Some(containing_block));
1222 self.fragments
1223 .push(Fragment::Positioning(PositioningFragment::new_anonymous(
1224 self.root_nesting_level.style.clone(),
1225 physical_line_rect,
1226 fragments,
1227 )));
1228 }
1229
1230 fn calculate_current_line_inline_start_and_justification_adjustment(
1235 &self,
1236 whitespace_trimmed: Au,
1237 last_line_or_forced_line_break: bool,
1238 ) -> (Au, Au) {
1239 enum TextAlign {
1240 Start,
1241 Center,
1242 End,
1243 }
1244 let containing_block = self.containing_block();
1245 let style = containing_block.style;
1246 let mut text_align_keyword = style.clone_text_align();
1247
1248 if last_line_or_forced_line_break {
1249 text_align_keyword = match style.clone_text_align_last() {
1250 TextAlignLast::Auto if text_align_keyword == TextAlignKeyword::Justify => {
1251 TextAlignKeyword::Start
1252 },
1253 TextAlignLast::Auto => text_align_keyword,
1254 TextAlignLast::Start => TextAlignKeyword::Start,
1255 TextAlignLast::End => TextAlignKeyword::End,
1256 TextAlignLast::Left => TextAlignKeyword::Left,
1257 TextAlignLast::Right => TextAlignKeyword::Right,
1258 TextAlignLast::Center => TextAlignKeyword::Center,
1259 TextAlignLast::Justify => TextAlignKeyword::Justify,
1260 };
1261 }
1262
1263 let text_align = match text_align_keyword {
1264 TextAlignKeyword::Start => TextAlign::Start,
1265 TextAlignKeyword::Center | TextAlignKeyword::MozCenter => TextAlign::Center,
1266 TextAlignKeyword::End => TextAlign::End,
1267 TextAlignKeyword::Left | TextAlignKeyword::MozLeft => {
1268 if style.writing_mode.line_left_is_inline_start() {
1269 TextAlign::Start
1270 } else {
1271 TextAlign::End
1272 }
1273 },
1274 TextAlignKeyword::Right | TextAlignKeyword::MozRight => {
1275 if style.writing_mode.line_left_is_inline_start() {
1276 TextAlign::End
1277 } else {
1278 TextAlign::Start
1279 }
1280 },
1281 TextAlignKeyword::Justify => TextAlign::Start,
1282 };
1283
1284 let (line_start, available_space) = match self.current_line.placement_among_floats.get() {
1285 Some(placement_among_floats) => (
1286 placement_among_floats.start_corner.inline,
1287 placement_among_floats.size.inline,
1288 ),
1289 None => (Au::zero(), containing_block.size.inline),
1290 };
1291
1292 let text_indent = self.current_line.start_position.inline;
1299 let line_length = self.current_line.inline_position - whitespace_trimmed - text_indent;
1300 let adjusted_line_start = line_start +
1301 match text_align {
1302 TextAlign::Start => text_indent,
1303 TextAlign::End => (available_space - line_length).max(text_indent),
1304 TextAlign::Center => (available_space - line_length + text_indent)
1305 .scale_by(0.5)
1306 .max(text_indent),
1307 };
1308
1309 let text_justify = containing_block.style.clone_text_justify();
1313 let justification_adjustment = match (text_align_keyword, text_justify) {
1314 (TextAlignKeyword::Justify, TextJustify::None) => Au::zero(),
1317 (TextAlignKeyword::Justify, _) => {
1318 match self.current_line.count_justification_opportunities() {
1319 0 => Au::zero(),
1320 num_justification_opportunities => {
1321 (available_space - text_indent - line_length)
1322 .scale_by(1. / num_justification_opportunities as f32)
1323 },
1324 }
1325 },
1326 _ => Au::zero(),
1327 };
1328
1329 let justification_adjustment = justification_adjustment.max(Au::zero());
1332
1333 (adjusted_line_start, justification_adjustment)
1334 }
1335
1336 fn place_float_fragment(&mut self, fragment: &mut BoxFragment) {
1337 let state = self
1338 .sequential_layout_state
1339 .as_mut()
1340 .expect("Tried to lay out a float with no sequential placement state!");
1341
1342 let block_offset_from_containining_block_top = state
1343 .current_block_position_including_margins() -
1344 state.current_containing_block_offset();
1345 state.place_float_fragment(
1346 fragment,
1347 self.placement_state.containing_block,
1348 CollapsedMargin::zero(),
1349 block_offset_from_containining_block_top,
1350 );
1351 }
1352
1353 fn place_float_line_item_for_commit_to_line(
1362 &mut self,
1363 float_item: &mut FloatLineItem,
1364 line_inline_size_without_trailing_whitespace: Au,
1365 ) {
1366 let containing_block = self.containing_block();
1367 let mut float_fragment = float_item.fragment.borrow_mut();
1368 let logical_margin_rect_size = float_fragment
1369 .margin_rect()
1370 .size
1371 .to_logical(containing_block.style.writing_mode);
1372 let inline_size = logical_margin_rect_size.inline.max(Au::zero());
1373
1374 let available_inline_size = match self.current_line.placement_among_floats.get() {
1375 Some(placement_among_floats) => placement_among_floats.size.inline,
1376 None => containing_block.size.inline,
1377 } - line_inline_size_without_trailing_whitespace;
1378
1379 let has_content = self.current_line.has_content || self.current_line_segment.has_content;
1385 let fits_on_line = !has_content || inline_size <= available_inline_size;
1386 let needs_placement_later =
1387 self.current_line.has_floats_waiting_to_be_placed || !fits_on_line;
1388
1389 if needs_placement_later {
1390 self.current_line.has_floats_waiting_to_be_placed = true;
1391 } else {
1392 self.place_float_fragment(&mut float_fragment);
1393 float_item.needs_placement = false;
1394 }
1395
1396 let new_placement = self.place_line_among_floats(&LogicalVec2 {
1401 inline: line_inline_size_without_trailing_whitespace,
1402 block: self.current_line.max_block_size.resolve(),
1403 });
1404 self.current_line
1405 .replace_placement_among_floats(new_placement);
1406 }
1407
1408 fn place_line_among_floats(&self, potential_line_size: &LogicalVec2<Au>) -> LogicalRect<Au> {
1413 let sequential_layout_state = self
1414 .sequential_layout_state
1415 .as_ref()
1416 .expect("Should not have called this function without having floats.");
1417
1418 let ifc_offset_in_float_container = LogicalVec2 {
1419 inline: sequential_layout_state
1420 .floats
1421 .containing_block_info
1422 .inline_start,
1423 block: sequential_layout_state.current_containing_block_offset(),
1424 };
1425
1426 let ceiling = self.current_line_block_start_considering_placement_among_floats();
1427 let mut placement = PlacementAmongFloats::new(
1428 &sequential_layout_state.floats,
1429 ceiling + ifc_offset_in_float_container.block,
1430 LogicalVec2 {
1431 inline: potential_line_size.inline,
1432 block: potential_line_size.block,
1433 },
1434 &PaddingBorderMargin::zero(),
1435 );
1436
1437 let mut placement_rect = placement.place();
1438 placement_rect.start_corner -= ifc_offset_in_float_container;
1439 placement_rect
1440 }
1441
1442 fn new_potential_line_size_causes_line_break(
1449 &mut self,
1450 potential_line_size: &LogicalVec2<Au>,
1451 ) -> bool {
1452 let containing_block = self.containing_block();
1453 let available_line_space = if self.sequential_layout_state.is_some() {
1454 self.current_line
1455 .placement_among_floats
1456 .get_or_init(|| self.place_line_among_floats(potential_line_size))
1457 .size
1458 } else {
1459 LogicalVec2 {
1460 inline: containing_block.size.inline,
1461 block: MAX_AU,
1462 }
1463 };
1464
1465 let inline_would_overflow = potential_line_size.inline > available_line_space.inline;
1466 let block_would_overflow = potential_line_size.block > available_line_space.block;
1467
1468 let can_break = self.current_line.has_content;
1471
1472 if !can_break {
1478 if self.sequential_layout_state.is_some() &&
1481 (inline_would_overflow || block_would_overflow)
1482 {
1483 let new_placement = self.place_line_among_floats(potential_line_size);
1484 self.current_line
1485 .replace_placement_among_floats(new_placement);
1486 }
1487
1488 return false;
1489 }
1490
1491 if potential_line_size.inline > containing_block.size.inline {
1494 return true;
1495 }
1496
1497 if block_would_overflow {
1501 assert!(self.sequential_layout_state.is_some());
1503 let new_placement = self.place_line_among_floats(potential_line_size);
1504 if new_placement.start_corner.block !=
1505 self.current_line_block_start_considering_placement_among_floats()
1506 {
1507 return true;
1508 } else {
1509 self.current_line
1510 .replace_placement_among_floats(new_placement);
1511 return false;
1512 }
1513 }
1514
1515 inline_would_overflow
1519 }
1520
1521 fn defer_forced_line_break(&mut self) {
1522 if !self.unbreakable_segment_fits_on_line() {
1525 self.process_line_break(false );
1526 }
1527
1528 self.linebreak_before_new_content = true;
1530
1531 let line_is_empty =
1539 !self.current_line_segment.has_content && !self.current_line.has_content;
1540 if !self.processing_br_element() || line_is_empty {
1541 let strut_size = self
1542 .current_inline_container_state()
1543 .strut_block_sizes
1544 .clone();
1545 self.update_unbreakable_segment_for_new_content(
1546 &strut_size,
1547 Au::zero(),
1548 SegmentContentFlags::empty(),
1549 );
1550 }
1551 }
1552
1553 fn possibly_flush_deferred_forced_line_break(&mut self) {
1554 if !self.linebreak_before_new_content {
1555 return;
1556 }
1557
1558 self.commit_current_segment_to_line();
1559 self.process_line_break(true );
1560 self.linebreak_before_new_content = false;
1561 }
1562
1563 fn push_line_item_to_unbreakable_segment(&mut self, line_item: LineItem) {
1564 self.current_line_segment
1565 .push_line_item(line_item, self.inline_box_state_stack.len());
1566 }
1567
1568 fn push_glyph_store_to_unbreakable_segment(
1569 &mut self,
1570 glyph_store: Arc<GlyphStore>,
1571 text_run: &TextRun,
1572 font: &FontRef,
1573 bidi_level: Level,
1574 offsets: Option<TextRunOffsets>,
1575 ) {
1576 let inline_advance = glyph_store.total_advance();
1577 let flags = if glyph_store.is_whitespace() {
1578 SegmentContentFlags::from(text_run.inline_styles.style.borrow().get_inherited_text())
1579 } else {
1580 SegmentContentFlags::empty()
1581 };
1582
1583 let font_metrics = &font.metrics;
1584 let font_key = font.key(
1585 self.layout_context.painter_id,
1586 &self.layout_context.font_context,
1587 );
1588
1589 let mut block_contribution = LineBlockSizes::zero();
1590 let quirks_mode = self.layout_context.style_context.quirks_mode() != QuirksMode::NoQuirks;
1591 if quirks_mode && !flags.is_collapsible_whitespace() {
1592 block_contribution.max_assign(&self.current_inline_container_state().strut_block_sizes);
1597 }
1598
1599 if self
1603 .current_inline_container_state()
1604 .font_metrics
1605 .block_metrics_meaningfully_differ(&font.metrics)
1606 {
1607 let container_state = self.current_inline_container_state();
1609 let baseline_shift = effective_baseline_shift(
1610 &container_state.style,
1611 self.inline_box_state_stack.last().map(|c| &c.base),
1612 );
1613 let mut font_block_conribution = container_state.get_block_size_contribution(
1614 baseline_shift,
1615 font_metrics,
1616 &container_state.font_metrics,
1617 );
1618 font_block_conribution.adjust_for_baseline_offset(container_state.baseline_offset);
1619 block_contribution.max_assign(&font_block_conribution);
1620 }
1621
1622 self.update_unbreakable_segment_for_new_content(&block_contribution, inline_advance, flags);
1623
1624 let current_inline_box_identifier = self.current_inline_box_identifier();
1625 if let Some(LineItem::TextRun(inline_box_identifier, line_item)) =
1626 self.current_line_segment.line_items.last_mut()
1627 {
1628 if *inline_box_identifier == current_inline_box_identifier &&
1629 line_item.merge_if_possible(font_key, bidi_level, &glyph_store, &offsets)
1630 {
1631 return;
1632 }
1633 }
1634
1635 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1636 current_inline_box_identifier,
1637 TextRunLineItem {
1638 text: vec![glyph_store],
1639 base_fragment_info: text_run.base_fragment_info,
1640 inline_styles: text_run.inline_styles.clone(),
1641 font_metrics: font_metrics.clone(),
1642 font_key,
1643 bidi_level,
1644 offsets: offsets.map(Box::new),
1645 is_empty_for_text_cursor: false,
1646 },
1647 ));
1648 }
1649
1650 fn possibly_push_empty_text_run_to_unbreakable_segment(
1654 &mut self,
1655 text_run: &TextRun,
1656 font: &FontRef,
1657 bidi_level: Level,
1658 offsets: Option<TextRunOffsets>,
1659 ) {
1660 if offsets.is_none() || self.current_line_segment.has_content {
1661 return;
1662 }
1663
1664 let font_metrics = &font.metrics;
1665 let font_key = font.key(
1666 self.layout_context.painter_id,
1667 &self.layout_context.font_context,
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: text_run.base_fragment_info,
1675 inline_styles: text_run.inline_styles.clone(),
1676 font_metrics: font_metrics.clone(),
1677 font_key,
1678 bidi_level,
1679 offsets: offsets.map(Box::new),
1680 is_empty_for_text_cursor: true,
1681 },
1682 ));
1683 self.current_line_segment.has_content = true;
1684 }
1685
1686 fn update_unbreakable_segment_for_new_content(
1687 &mut self,
1688 block_sizes_of_content: &LineBlockSizes,
1689 inline_size: Au,
1690 flags: SegmentContentFlags,
1691 ) {
1692 if flags.is_collapsible_whitespace() || flags.is_wrappable_and_hangable() {
1693 self.current_line_segment.trailing_whitespace_size = inline_size;
1694 } else {
1695 self.current_line_segment.trailing_whitespace_size = Au::zero();
1696 }
1697 if !flags.is_collapsible_whitespace() {
1698 self.current_line_segment.has_content = true;
1699 }
1700
1701 let container_max_block_size = &self
1703 .current_inline_container_state()
1704 .nested_strut_block_sizes
1705 .clone();
1706 self.current_line_segment
1707 .max_block_size
1708 .max_assign(container_max_block_size);
1709 self.current_line_segment
1710 .max_block_size
1711 .max_assign(block_sizes_of_content);
1712
1713 self.current_line_segment.inline_size += inline_size;
1714
1715 *self
1717 .current_inline_container_state()
1718 .has_content
1719 .borrow_mut() = true;
1720 self.propagate_current_nesting_level_white_space_style();
1721 }
1722
1723 fn process_line_break(&mut self, forced_line_break: bool) {
1724 self.current_line_segment.trim_leading_whitespace();
1725 self.finish_current_line_and_reset(forced_line_break);
1726 }
1727
1728 fn unbreakable_segment_fits_on_line(&mut self) -> bool {
1729 let potential_line_size = LogicalVec2 {
1730 inline: self.current_line.inline_position + self.current_line_segment.inline_size -
1731 self.current_line_segment.trailing_whitespace_size,
1732 block: self
1733 .current_line_max_block_size_including_nested_containers()
1734 .max(&self.current_line_segment.max_block_size)
1735 .resolve(),
1736 };
1737
1738 !self.new_potential_line_size_causes_line_break(&potential_line_size)
1739 }
1740
1741 fn process_soft_wrap_opportunity(&mut self) {
1745 if self.current_line_segment.line_items.is_empty() {
1746 return;
1747 }
1748 if self.text_wrap_mode == TextWrapMode::Nowrap {
1749 return;
1750 }
1751 if !self.unbreakable_segment_fits_on_line() {
1752 self.process_line_break(false );
1753 }
1754 self.commit_current_segment_to_line();
1755 }
1756
1757 fn commit_current_segment_to_line(&mut self) {
1760 if self.current_line_segment.line_items.is_empty() && !self.current_line_segment.has_content
1763 {
1764 return;
1765 }
1766
1767 if !self.current_line.has_content {
1768 self.current_line_segment.trim_leading_whitespace();
1769 }
1770
1771 self.current_line.inline_position += self.current_line_segment.inline_size;
1772 self.current_line.max_block_size = self
1773 .current_line_max_block_size_including_nested_containers()
1774 .max(&self.current_line_segment.max_block_size);
1775 let line_inline_size_without_trailing_whitespace =
1776 self.current_line.inline_position - self.current_line_segment.trailing_whitespace_size;
1777
1778 let mut segment_items = mem::take(&mut self.current_line_segment.line_items);
1780 for item in segment_items.iter_mut() {
1781 if let LineItem::Float(_, float_item) = item {
1782 self.place_float_line_item_for_commit_to_line(
1783 float_item,
1784 line_inline_size_without_trailing_whitespace,
1785 );
1786 }
1787 }
1788
1789 if self.current_line.line_items.is_empty() {
1794 let will_break = self.new_potential_line_size_causes_line_break(&LogicalVec2 {
1795 inline: line_inline_size_without_trailing_whitespace,
1796 block: self.current_line_segment.max_block_size.resolve(),
1797 });
1798 assert!(!will_break);
1799 }
1800
1801 self.current_line.line_items.extend(segment_items);
1802 self.current_line.has_content |= self.current_line_segment.has_content;
1803 self.current_line.has_inline_pbm |= self.current_line_segment.has_inline_pbm;
1804
1805 self.current_line_segment.reset();
1806 }
1807
1808 #[inline]
1809 fn containing_block(&self) -> &ContainingBlock<'_> {
1810 self.placement_state.containing_block
1811 }
1812}
1813
1814bitflags! {
1815 struct SegmentContentFlags: u8 {
1816 const COLLAPSIBLE_WHITESPACE = 0b00000001;
1817 const WRAPPABLE_AND_HANGABLE_WHITESPACE = 0b00000010;
1818 }
1819}
1820
1821impl SegmentContentFlags {
1822 fn is_collapsible_whitespace(&self) -> bool {
1823 self.contains(Self::COLLAPSIBLE_WHITESPACE)
1824 }
1825
1826 fn is_wrappable_and_hangable(&self) -> bool {
1827 self.contains(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE)
1828 }
1829}
1830
1831impl From<&InheritedText> for SegmentContentFlags {
1832 fn from(style_text: &InheritedText) -> Self {
1833 let mut flags = Self::empty();
1834
1835 if !matches!(
1838 style_text.white_space_collapse,
1839 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
1840 ) {
1841 flags.insert(Self::COLLAPSIBLE_WHITESPACE);
1842 }
1843
1844 if style_text.text_wrap_mode == TextWrapMode::Wrap &&
1847 style_text.white_space_collapse != WhiteSpaceCollapse::BreakSpaces
1848 {
1849 flags.insert(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE);
1850 }
1851 flags
1852 }
1853}
1854
1855impl InlineFormattingContext {
1856 #[servo_tracing::instrument(name = "InlineFormattingContext::new_with_builder", skip_all)]
1857 fn new_with_builder(
1858 mut builder: InlineFormattingContextBuilder,
1859 layout_context: &LayoutContext,
1860 has_first_formatted_line: bool,
1861 is_single_line_text_input: bool,
1862 starting_bidi_level: Level,
1863 ) -> Self {
1864 let text_content: String = builder.text_segments.into_iter().collect();
1866
1867 let bidi_info = BidiInfo::new(&text_content, Some(starting_bidi_level));
1868 let has_right_to_left_content = bidi_info.has_rtl();
1869 let shared_inline_styles = builder
1870 .shared_inline_styles_stack
1871 .last()
1872 .expect("Should have at least one SharedInlineStyle for the root of an IFC")
1873 .clone();
1874 let (word_break, line_break) = {
1875 let styles = shared_inline_styles.style.borrow();
1876 let text_style = styles.get_inherited_text();
1877 (text_style.word_break, text_style.line_break)
1878 };
1879
1880 let mut options = LineBreakOptions::default();
1881
1882 options.strictness = match line_break {
1883 LineBreak::Loose => LineBreakStrictness::Loose,
1884 LineBreak::Normal => LineBreakStrictness::Normal,
1885 LineBreak::Strict => LineBreakStrictness::Strict,
1886 LineBreak::Anywhere => LineBreakStrictness::Anywhere,
1887 LineBreak::Auto => LineBreakStrictness::Normal,
1890 };
1891 options.word_option = match word_break {
1892 WordBreak::Normal => LineBreakWordOption::Normal,
1893 WordBreak::BreakAll => LineBreakWordOption::BreakAll,
1894 WordBreak::KeepAll => LineBreakWordOption::KeepAll,
1895 };
1896 options.ja_zh = false; let mut new_linebreaker = LineBreaker::new(text_content.as_str(), options);
1899 for item in &mut builder.inline_items {
1900 match item {
1901 InlineItem::TextRun(text_run) => {
1902 text_run.borrow_mut().segment_and_shape(
1903 &text_content,
1904 layout_context,
1905 &mut new_linebreaker,
1906 &bidi_info,
1907 );
1908 },
1909 InlineItem::StartInlineBox(inline_box) => {
1910 let inline_box = &mut *inline_box.borrow_mut();
1911 if let Some(font) = get_font_for_first_font_for_style(
1912 &inline_box.base.style,
1913 &layout_context.font_context,
1914 ) {
1915 inline_box.default_font = Some(font);
1916 }
1917 },
1918 InlineItem::Atomic(_, index_in_text, bidi_level) => {
1919 *bidi_level = bidi_info.levels[*index_in_text];
1920 },
1921 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) |
1922 InlineItem::OutOfFlowFloatBox(_) |
1923 InlineItem::EndInlineBox |
1924 InlineItem::AnonymousBlock { .. } => {},
1925 }
1926 }
1927
1928 InlineFormattingContext {
1929 text_content,
1930 inline_items: builder.inline_items,
1931 inline_boxes: builder.inline_boxes,
1932 shared_inline_styles,
1933 has_first_formatted_line,
1934 contains_floats: builder.contains_floats,
1935 is_single_line_text_input,
1936 has_right_to_left_content,
1937 shared_selection: builder.shared_selection,
1938 }
1939 }
1940
1941 pub(crate) fn repair_style(
1942 &self,
1943 context: &SharedStyleContext,
1944 node: &ServoThreadSafeLayoutNode,
1945 new_style: &ServoArc<ComputedValues>,
1946 ) {
1947 *self.shared_inline_styles.style.borrow_mut() = new_style.clone();
1948 *self.shared_inline_styles.selected.borrow_mut() = node.selected_style(context);
1949 }
1950
1951 fn inline_start_for_first_line(&self, containing_block: IndefiniteContainingBlock) -> Au {
1952 if !self.has_first_formatted_line {
1953 return Au::zero();
1954 }
1955 containing_block
1956 .style
1957 .get_inherited_text()
1958 .text_indent
1959 .length
1960 .to_used_value(containing_block.size.inline.unwrap_or_default())
1961 }
1962
1963 pub(super) fn layout(
1964 &self,
1965 layout_context: &LayoutContext,
1966 positioning_context: &mut PositioningContext,
1967 containing_block: &ContainingBlock,
1968 sequential_layout_state: Option<&mut SequentialLayoutState>,
1969 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
1970 ) -> CacheableLayoutResult {
1971 for inline_box in self.inline_boxes.iter() {
1973 inline_box.borrow().base.clear_fragments();
1974 }
1975
1976 let style = containing_block.style;
1977
1978 let default_font_metrics =
1981 get_font_for_first_font_for_style(style, &layout_context.font_context)
1982 .map(|font| font.metrics.clone());
1983
1984 let style_text = containing_block.style.get_inherited_text();
1985 let mut inline_container_state_flags = InlineContainerStateFlags::empty();
1986 if inline_container_needs_strut(style, layout_context, None) {
1987 inline_container_state_flags.insert(InlineContainerStateFlags::CREATE_STRUT);
1988 }
1989 if self.is_single_line_text_input {
1990 inline_container_state_flags
1991 .insert(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT);
1992 }
1993 let placement_state =
1994 PlacementState::new(collapsible_with_parent_start_margin, containing_block);
1995
1996 let mut layout = InlineFormattingContextLayout {
1997 positioning_context,
1998 placement_state,
1999 sequential_layout_state,
2000 layout_context,
2001 ifc: self,
2002 fragments: Vec::new(),
2003 current_line: LineUnderConstruction::new(LogicalVec2 {
2004 inline: self.inline_start_for_first_line(containing_block.into()),
2005 block: Au::zero(),
2006 }),
2007 root_nesting_level: InlineContainerState::new(
2008 style.to_arc(),
2009 inline_container_state_flags,
2010 None, default_font_metrics,
2012 ),
2013 inline_box_state_stack: Vec::new(),
2014 inline_box_states: Vec::with_capacity(self.inline_boxes.len()),
2015 current_line_segment: UnbreakableSegmentUnderConstruction::new(),
2016 linebreak_before_new_content: false,
2017 deferred_br_clear: Clear::None,
2018 have_deferred_soft_wrap_opportunity: false,
2019 depends_on_block_constraints: false,
2020 white_space_collapse: style_text.white_space_collapse,
2021 text_wrap_mode: style_text.text_wrap_mode,
2022 };
2023
2024 for item in self.inline_items.iter() {
2025 if !matches!(item, InlineItem::EndInlineBox) {
2027 layout.possibly_flush_deferred_forced_line_break();
2028 }
2029
2030 match item {
2031 InlineItem::StartInlineBox(inline_box) => {
2032 layout.start_inline_box(&inline_box.borrow());
2033 },
2034 InlineItem::EndInlineBox => layout.finish_inline_box(),
2035 InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout),
2036 InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
2037 atomic_formatting_context.borrow().layout_into_line_items(
2038 &mut layout,
2039 *offset_in_text,
2040 *bidi_level,
2041 );
2042 },
2043 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, _) => {
2044 layout.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
2045 layout.current_inline_box_identifier(),
2046 AbsolutelyPositionedLineItem {
2047 absolutely_positioned_box: positioned_box.clone(),
2048 preceding_line_content_would_produce_phantom_line: layout
2049 .current_line
2050 .is_phantom() &&
2051 layout.current_line_segment.is_phantom(),
2052 },
2053 ));
2054 },
2055 InlineItem::OutOfFlowFloatBox(float_box) => {
2056 float_box.borrow().layout_into_line_items(&mut layout);
2057 },
2058 InlineItem::AnonymousBlock(block_box) => {
2059 block_box.borrow().layout_into_line_items(&mut layout);
2060 },
2061 }
2062 }
2063
2064 layout.finish_last_line();
2065 let (content_block_size, collapsible_margins_in_children, baselines) =
2066 layout.placement_state.finish();
2067
2068 CacheableLayoutResult {
2069 fragments: layout.fragments,
2070 content_block_size,
2071 collapsible_margins_in_children,
2072 baselines,
2073 depends_on_block_constraints: layout.depends_on_block_constraints,
2074 content_inline_size_for_table: None,
2075 specific_layout_info: None,
2076 }
2077 }
2078
2079 fn next_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
2080 let Some(character) = self.text_content[index..].chars().nth(1) else {
2081 return false;
2082 };
2083 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2084 }
2085
2086 fn previous_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
2087 let Some(character) = self.text_content[0..index].chars().next_back() else {
2088 return false;
2089 };
2090 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2091 }
2092
2093 pub(crate) fn find_block_margin_collapsing_with_parent(
2094 &self,
2095 layout_context: &LayoutContext,
2096 collected_margin: &mut CollapsedMargin,
2097 containing_block_for_children: &ContainingBlock,
2098 ) -> bool {
2099 let mut nesting_levels_from_nonzero_end_pbm: u32 = 1;
2105 let mut items_iter = self.inline_items.iter();
2106 items_iter.all(|inline_item| match inline_item {
2107 InlineItem::StartInlineBox(inline_box) => {
2108 let pbm = inline_box
2109 .borrow()
2110 .layout_style()
2111 .padding_border_margin(containing_block_for_children);
2112 if pbm.padding.inline_end.is_zero() &&
2113 pbm.border.inline_end.is_zero() &&
2114 pbm.margin.inline_end.auto_is(Au::zero).is_zero()
2115 {
2116 nesting_levels_from_nonzero_end_pbm += 1;
2117 } else {
2118 nesting_levels_from_nonzero_end_pbm = 0;
2119 }
2120 pbm.padding.inline_start.is_zero() &&
2121 pbm.border.inline_start.is_zero() &&
2122 pbm.margin.inline_start.auto_is(Au::zero).is_zero()
2123 },
2124 InlineItem::EndInlineBox => {
2125 if nesting_levels_from_nonzero_end_pbm == 0 {
2126 false
2127 } else {
2128 nesting_levels_from_nonzero_end_pbm -= 1;
2129 true
2130 }
2131 },
2132 InlineItem::TextRun(text_run) => {
2133 let text_run = &*text_run.borrow();
2134 let parent_style = text_run.inline_styles.style.borrow();
2135 text_run.shaped_text.iter().all(|segment| {
2136 segment.runs.iter().all(|run| {
2137 run.is_whitespace() &&
2138 !run.is_single_preserved_newline() &&
2139 !matches!(
2140 parent_style.get_inherited_text().white_space_collapse,
2141 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
2142 )
2143 })
2144 })
2145 },
2146 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => true,
2147 InlineItem::OutOfFlowFloatBox(..) => true,
2148 InlineItem::Atomic(..) => false,
2149 InlineItem::AnonymousBlock(block_box) => block_box
2150 .borrow()
2151 .contents
2152 .find_block_margin_collapsing_with_parent(
2153 layout_context,
2154 collected_margin,
2155 containing_block_for_children,
2156 ),
2157 })
2158 }
2159
2160 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
2161 let mut parent_box_stack = Vec::new();
2162 let current_parent_box = |parent_box_stack: &[WeakLayoutBox]| {
2163 parent_box_stack.last().unwrap_or(&layout_box).clone()
2164 };
2165 for inline_item in &self.inline_items {
2166 match inline_item {
2167 InlineItem::StartInlineBox(inline_box) => {
2168 inline_box
2169 .borrow_mut()
2170 .base
2171 .parent_box
2172 .replace(current_parent_box(&parent_box_stack));
2173 parent_box_stack.push(WeakLayoutBox::InlineLevel(
2174 WeakInlineItem::StartInlineBox(inline_box.downgrade()),
2175 ));
2176 },
2177 InlineItem::EndInlineBox => {
2178 parent_box_stack.pop();
2179 },
2180 InlineItem::TextRun(text_run) => {
2181 text_run
2182 .borrow_mut()
2183 .parent_box
2184 .replace(current_parent_box(&parent_box_stack));
2185 },
2186 _ => inline_item.with_base_mut(|base| {
2187 base.parent_box
2188 .replace(current_parent_box(&parent_box_stack));
2189 }),
2190 }
2191 }
2192 }
2193}
2194
2195impl InlineContainerState {
2196 fn new(
2197 style: ServoArc<ComputedValues>,
2198 flags: InlineContainerStateFlags,
2199 parent_container: Option<&InlineContainerState>,
2200 font_metrics: Option<Arc<FontMetrics>>,
2201 ) -> Self {
2202 let font_metrics = font_metrics.unwrap_or_else(FontMetrics::empty);
2203 let mut baseline_offset = Au::zero();
2204 let mut strut_block_sizes = Self::get_block_sizes_with_style(
2205 effective_baseline_shift(&style, parent_container),
2206 &style,
2207 &font_metrics,
2208 &font_metrics,
2209 &flags,
2210 );
2211 if let Some(parent_container) = parent_container {
2212 baseline_offset = parent_container.get_cumulative_baseline_offset_for_child(
2215 style.clone_alignment_baseline(),
2216 style.clone_baseline_shift(),
2217 &strut_block_sizes,
2218 );
2219 strut_block_sizes.adjust_for_baseline_offset(baseline_offset);
2220 }
2221
2222 let mut nested_block_sizes = parent_container
2223 .map(|container| container.nested_strut_block_sizes.clone())
2224 .unwrap_or_else(LineBlockSizes::zero);
2225 if flags.contains(InlineContainerStateFlags::CREATE_STRUT) {
2226 nested_block_sizes.max_assign(&strut_block_sizes);
2227 }
2228
2229 Self {
2230 style,
2231 flags,
2232 has_content: RefCell::new(false),
2233 nested_strut_block_sizes: nested_block_sizes,
2234 strut_block_sizes,
2235 baseline_offset,
2236 font_metrics,
2237 }
2238 }
2239
2240 fn get_block_sizes_with_style(
2241 baseline_shift: BaselineShift,
2242 style: &ComputedValues,
2243 font_metrics: &FontMetrics,
2244 font_metrics_of_first_font: &FontMetrics,
2245 flags: &InlineContainerStateFlags,
2246 ) -> LineBlockSizes {
2247 let line_height = line_height(style, font_metrics, flags);
2248
2249 if !is_baseline_relative(baseline_shift) {
2250 return LineBlockSizes {
2251 line_height,
2252 baseline_relative_size_for_line_height: None,
2253 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2254 };
2255 }
2256
2257 let mut ascent = font_metrics.ascent;
2266 let mut descent = font_metrics.descent;
2267 if style.get_font().line_height == LineHeight::Normal {
2268 let half_leading_from_line_gap =
2269 (font_metrics.line_gap - descent - ascent).scale_by(0.5);
2270 ascent += half_leading_from_line_gap;
2271 descent += half_leading_from_line_gap;
2272 }
2273
2274 let size_for_baseline_positioning = BaselineRelativeSize { ascent, descent };
2278
2279 if style.get_font().line_height != LineHeight::Normal {
2295 ascent = font_metrics_of_first_font.ascent;
2296 descent = font_metrics_of_first_font.descent;
2297 let half_leading = (line_height - (ascent + descent)).scale_by(0.5);
2298 ascent += half_leading;
2303 descent = line_height - ascent;
2304 }
2305
2306 LineBlockSizes {
2307 line_height,
2308 baseline_relative_size_for_line_height: Some(BaselineRelativeSize { ascent, descent }),
2309 size_for_baseline_positioning,
2310 }
2311 }
2312
2313 fn get_block_size_contribution(
2314 &self,
2315 baseline_shift: BaselineShift,
2316 font_metrics: &FontMetrics,
2317 font_metrics_of_first_font: &FontMetrics,
2318 ) -> LineBlockSizes {
2319 Self::get_block_sizes_with_style(
2320 baseline_shift,
2321 &self.style,
2322 font_metrics,
2323 font_metrics_of_first_font,
2324 &self.flags,
2325 )
2326 }
2327
2328 fn get_cumulative_baseline_offset_for_child(
2329 &self,
2330 child_alignment_baseline: AlignmentBaseline,
2331 child_baseline_shift: BaselineShift,
2332 child_block_size: &LineBlockSizes,
2333 ) -> Au {
2334 let block_size = self.get_block_size_contribution(
2335 child_baseline_shift.clone(),
2336 &self.font_metrics,
2337 &self.font_metrics,
2338 );
2339 self.baseline_offset +
2340 match child_alignment_baseline {
2341 AlignmentBaseline::Baseline => Au::zero(),
2342 AlignmentBaseline::TextTop => {
2343 child_block_size.size_for_baseline_positioning.ascent - self.font_metrics.ascent
2344 },
2345 AlignmentBaseline::Middle => {
2346 (child_block_size.size_for_baseline_positioning.ascent -
2349 child_block_size.size_for_baseline_positioning.descent -
2350 self.font_metrics.x_height)
2351 .scale_by(0.5)
2352 },
2353 AlignmentBaseline::TextBottom => {
2354 self.font_metrics.descent -
2355 child_block_size.size_for_baseline_positioning.descent
2356 },
2357 AlignmentBaseline::Alphabetic |
2358 AlignmentBaseline::Ideographic |
2359 AlignmentBaseline::Central |
2360 AlignmentBaseline::Mathematical |
2361 AlignmentBaseline::Hanging => {
2362 unreachable!("Got alignment-baseline value that should be disabled in Stylo")
2363 },
2364 } +
2365 match child_baseline_shift {
2366 BaselineShift::Keyword(
2371 BaselineShiftKeyword::Top |
2372 BaselineShiftKeyword::Bottom |
2373 BaselineShiftKeyword::Center,
2374 ) => Au::zero(),
2375 BaselineShift::Keyword(BaselineShiftKeyword::Sub) => {
2376 block_size.resolve().scale_by(FONT_SUBSCRIPT_OFFSET_RATIO)
2377 },
2378 BaselineShift::Keyword(BaselineShiftKeyword::Super) => {
2379 -block_size.resolve().scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO)
2380 },
2381 BaselineShift::Length(length_percentage) => {
2382 -length_percentage.to_used_value(child_block_size.line_height)
2383 },
2384 }
2385 }
2386}
2387
2388impl IndependentFormattingContext {
2389 fn layout_into_line_items(
2390 &self,
2391 layout: &mut InlineFormattingContextLayout,
2392 offset_in_text: usize,
2393 bidi_level: Level,
2394 ) {
2395 let mut child_positioning_context = PositioningContext::default();
2397 let IndependentFloatOrAtomicLayoutResult {
2398 mut fragment,
2399 baselines,
2400 pbm_sums,
2401 } = self.layout_float_or_atomic_inline(
2402 layout.layout_context,
2403 &mut child_positioning_context,
2404 layout.containing_block(),
2405 );
2406
2407 layout.depends_on_block_constraints |= fragment.base.flags.contains(
2410 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2411 );
2412
2413 let container_writing_mode = layout.containing_block().style.writing_mode;
2415 let pbm_physical_offset = pbm_sums
2416 .start_offset()
2417 .to_physical_size(container_writing_mode);
2418 fragment.base.rect.origin += pbm_physical_offset.to_vector();
2419
2420 fragment = fragment.with_baselines(baselines);
2422
2423 let positioning_context = if self.is_replaced() {
2426 None
2427 } else {
2428 if fragment
2429 .style()
2430 .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
2431 {
2432 child_positioning_context
2433 .layout_collected_children(layout.layout_context, &mut fragment);
2434 }
2435 Some(child_positioning_context)
2436 };
2437
2438 if layout.text_wrap_mode == TextWrapMode::Wrap &&
2439 !layout
2440 .ifc
2441 .previous_character_prevents_soft_wrap_opportunity(offset_in_text)
2442 {
2443 layout.process_soft_wrap_opportunity();
2444 }
2445
2446 let size = pbm_sums.sum() + fragment.base.rect.size.to_logical(container_writing_mode);
2447 let baseline_offset = self
2448 .pick_baseline(&fragment.baselines(container_writing_mode))
2449 .map(|baseline| pbm_sums.block_start + baseline)
2450 .unwrap_or(size.block);
2451
2452 let (block_sizes, baseline_offset_in_parent) =
2453 self.get_block_sizes_and_baseline_offset(layout, size.block, baseline_offset);
2454 layout.update_unbreakable_segment_for_new_content(
2455 &block_sizes,
2456 size.inline,
2457 SegmentContentFlags::empty(),
2458 );
2459
2460 let fragment = ArcRefCell::new(fragment);
2461 self.base.set_fragment(Fragment::Box(fragment.clone()));
2462
2463 layout.push_line_item_to_unbreakable_segment(LineItem::Atomic(
2464 layout.current_inline_box_identifier(),
2465 AtomicLineItem {
2466 fragment,
2467 size,
2468 positioning_context,
2469 baseline_offset_in_parent,
2470 baseline_offset_in_item: baseline_offset,
2471 bidi_level,
2472 },
2473 ));
2474
2475 if !layout
2478 .ifc
2479 .next_character_prevents_soft_wrap_opportunity(offset_in_text)
2480 {
2481 layout.have_deferred_soft_wrap_opportunity = true;
2482 }
2483 }
2484
2485 fn pick_baseline(&self, baselines: &Baselines) -> Option<Au> {
2489 match self.style().clone_baseline_source() {
2490 BaselineSource::First => baselines.first,
2491 BaselineSource::Last => baselines.last,
2492 BaselineSource::Auto if self.is_block_container() => baselines.last,
2493 BaselineSource::Auto => baselines.first,
2494 }
2495 }
2496
2497 fn get_block_sizes_and_baseline_offset(
2498 &self,
2499 ifc: &InlineFormattingContextLayout,
2500 block_size: Au,
2501 baseline_offset_in_content_area: Au,
2502 ) -> (LineBlockSizes, Au) {
2503 let mut contribution = if !is_baseline_relative(self.style().clone_baseline_shift()) {
2504 LineBlockSizes {
2505 line_height: block_size,
2506 baseline_relative_size_for_line_height: None,
2507 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2508 }
2509 } else {
2510 let baseline_relative_size = BaselineRelativeSize {
2511 ascent: baseline_offset_in_content_area,
2512 descent: block_size - baseline_offset_in_content_area,
2513 };
2514 LineBlockSizes {
2515 line_height: block_size,
2516 baseline_relative_size_for_line_height: Some(baseline_relative_size.clone()),
2517 size_for_baseline_positioning: baseline_relative_size,
2518 }
2519 };
2520
2521 let style = self.style();
2522 let baseline_offset = ifc
2523 .current_inline_container_state()
2524 .get_cumulative_baseline_offset_for_child(
2525 style.clone_alignment_baseline(),
2526 style.clone_baseline_shift(),
2527 &contribution,
2528 );
2529 contribution.adjust_for_baseline_offset(baseline_offset);
2530
2531 (contribution, baseline_offset)
2532 }
2533}
2534
2535impl FloatBox {
2536 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
2537 let fragment = ArcRefCell::new(self.layout(
2538 layout.layout_context,
2539 layout.positioning_context,
2540 layout.placement_state.containing_block,
2541 ));
2542
2543 self.contents
2544 .base
2545 .set_fragment(Fragment::Box(fragment.clone()));
2546 layout.push_line_item_to_unbreakable_segment(LineItem::Float(
2547 layout.current_inline_box_identifier(),
2548 FloatLineItem {
2549 fragment,
2550 needs_placement: true,
2551 },
2552 ));
2553 }
2554}
2555
2556fn place_pending_floats(ifc: &mut InlineFormattingContextLayout, line_items: &mut [LineItem]) {
2557 for item in line_items.iter_mut() {
2558 if let LineItem::Float(_, float_line_item) = item {
2559 if float_line_item.needs_placement {
2560 ifc.place_float_fragment(&mut float_line_item.fragment.borrow_mut());
2561 }
2562 }
2563 }
2564}
2565
2566fn line_height(
2567 parent_style: &ComputedValues,
2568 font_metrics: &FontMetrics,
2569 flags: &InlineContainerStateFlags,
2570) -> Au {
2571 let font = parent_style.get_font();
2572 let font_size = font.font_size.computed_size();
2573 let mut line_height = match font.line_height {
2574 LineHeight::Normal => font_metrics.line_gap,
2575 LineHeight::Number(number) => (font_size * number.0).into(),
2576 LineHeight::Length(length) => length.0.into(),
2577 };
2578
2579 if flags.contains(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT) {
2583 line_height.max_assign(font_metrics.line_gap);
2584 }
2585
2586 line_height
2587}
2588
2589fn effective_baseline_shift(
2590 style: &ComputedValues,
2591 container: Option<&InlineContainerState>,
2592) -> BaselineShift {
2593 if container.is_none() {
2594 BaselineShift::zero()
2598 } else {
2599 style.clone_baseline_shift()
2600 }
2601}
2602
2603fn is_baseline_relative(baseline_shift: BaselineShift) -> bool {
2604 !matches!(
2605 baseline_shift,
2606 BaselineShift::Keyword(
2607 BaselineShiftKeyword::Top | BaselineShiftKeyword::Bottom | BaselineShiftKeyword::Center
2608 )
2609 )
2610}
2611
2612fn inline_container_needs_strut(
2638 style: &ComputedValues,
2639 layout_context: &LayoutContext,
2640 pbm: Option<&PaddingBorderMargin>,
2641) -> bool {
2642 if layout_context.style_context.quirks_mode() == QuirksMode::NoQuirks {
2643 return true;
2644 }
2645
2646 if style.get_box().display.is_list_item() {
2649 return true;
2650 }
2651
2652 pbm.is_some_and(|pbm| !pbm.padding_border_sums.inline.is_zero())
2653}
2654
2655impl ComputeInlineContentSizes for InlineFormattingContext {
2656 fn compute_inline_content_sizes(
2660 &self,
2661 layout_context: &LayoutContext,
2662 constraint_space: &ConstraintSpace,
2663 ) -> InlineContentSizesResult {
2664 ContentSizesComputation::compute(self, layout_context, constraint_space)
2665 }
2666}
2667
2668struct ContentSizesComputation<'layout_data> {
2670 layout_context: &'layout_data LayoutContext<'layout_data>,
2671 constraint_space: &'layout_data ConstraintSpace<'layout_data>,
2672 paragraph: ContentSizes,
2673 current_line: ContentSizes,
2674 pending_whitespace: ContentSizes,
2676 uncleared_floats: LogicalSides1D<ContentSizes>,
2678 cleared_floats: LogicalSides1D<ContentSizes>,
2680 had_content_yet_for_min_content: bool,
2683 had_content_yet_for_max_content: bool,
2686 ending_inline_pbm_stack: Vec<Au>,
2689 depends_on_block_constraints: bool,
2690}
2691
2692impl<'layout_data> ContentSizesComputation<'layout_data> {
2693 fn traverse(
2694 mut self,
2695 inline_formatting_context: &InlineFormattingContext,
2696 ) -> InlineContentSizesResult {
2697 self.add_inline_size(
2698 inline_formatting_context.inline_start_for_first_line(self.constraint_space.into()),
2699 );
2700 for inline_item in &inline_formatting_context.inline_items {
2701 self.process_item(inline_item, inline_formatting_context);
2702 }
2703 self.forced_line_break();
2704 self.flush_floats();
2705
2706 InlineContentSizesResult {
2707 sizes: self.paragraph,
2708 depends_on_block_constraints: self.depends_on_block_constraints,
2709 }
2710 }
2711
2712 fn process_item(
2713 &mut self,
2714 inline_item: &InlineItem,
2715 inline_formatting_context: &InlineFormattingContext,
2716 ) {
2717 match inline_item {
2718 InlineItem::StartInlineBox(inline_box) => {
2719 let inline_box = inline_box.borrow();
2723 let zero = Au::zero();
2724 let writing_mode = self.constraint_space.style.writing_mode;
2725 let layout_style = inline_box.layout_style();
2726 let padding = layout_style
2727 .padding(writing_mode)
2728 .percentages_relative_to(zero);
2729 let border = layout_style.border_width(writing_mode);
2730 let margin = inline_box
2731 .base
2732 .style
2733 .margin(writing_mode)
2734 .percentages_relative_to(zero)
2735 .auto_is(Au::zero);
2736
2737 let pbm = margin + padding + border;
2738 self.add_inline_size(pbm.inline_start);
2739 self.ending_inline_pbm_stack.push(pbm.inline_end);
2740 },
2741 InlineItem::EndInlineBox => {
2742 let length = self.ending_inline_pbm_stack.pop().unwrap_or_else(Au::zero);
2743 self.add_inline_size(length);
2744 },
2745 InlineItem::TextRun(text_run) => {
2746 let text_run = &*text_run.borrow();
2747 let parent_style = text_run.inline_styles.style.borrow();
2748 for segment in text_run.shaped_text.iter() {
2749 let style_text = parent_style.get_inherited_text();
2750 let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap;
2751
2752 let break_at_start =
2755 segment.break_at_start && self.had_content_yet_for_min_content;
2756
2757 for (run_index, run) in segment.runs.iter().enumerate() {
2758 if can_wrap && (run_index != 0 || break_at_start) {
2761 self.line_break_opportunity();
2762 }
2763
2764 let advance = run.total_advance();
2765 if run.is_whitespace() {
2766 if run.is_single_preserved_newline() {
2769 self.forced_line_break();
2770 continue;
2771 }
2772 if !matches!(
2773 style_text.white_space_collapse,
2774 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
2775 ) {
2776 if self.had_content_yet_for_min_content {
2777 if can_wrap {
2778 self.line_break_opportunity();
2779 } else {
2780 self.pending_whitespace.min_content += advance;
2781 }
2782 }
2783 if self.had_content_yet_for_max_content {
2784 self.pending_whitespace.max_content += advance;
2785 }
2786 continue;
2787 }
2788 if can_wrap {
2789 self.pending_whitespace.max_content += advance;
2790 self.commit_pending_whitespace();
2791 self.line_break_opportunity();
2792 continue;
2793 }
2794 }
2795
2796 self.commit_pending_whitespace();
2797 self.add_inline_size(advance);
2798
2799 if can_wrap && run.ends_with_whitespace() {
2804 self.line_break_opportunity();
2805 }
2806 }
2807 }
2808 },
2809 InlineItem::Atomic(atomic, offset_in_text, _level) => {
2810 if self.had_content_yet_for_min_content &&
2812 !inline_formatting_context
2813 .previous_character_prevents_soft_wrap_opportunity(*offset_in_text)
2814 {
2815 self.line_break_opportunity();
2816 }
2817
2818 self.commit_pending_whitespace();
2819 let outer = self.outer_inline_content_sizes_of_float_or_atomic(&atomic.borrow());
2820 self.current_line += outer;
2821
2822 if !inline_formatting_context
2824 .next_character_prevents_soft_wrap_opportunity(*offset_in_text)
2825 {
2826 self.line_break_opportunity();
2827 }
2828 },
2829 InlineItem::OutOfFlowFloatBox(float_box) => {
2830 let float_box = float_box.borrow();
2831 let sizes = self.outer_inline_content_sizes_of_float_or_atomic(&float_box.contents);
2832 let style = &float_box.contents.style();
2833 let container_writing_mode = self.constraint_space.style.writing_mode;
2834 let clear =
2835 Clear::from_style_and_container_writing_mode(style, container_writing_mode);
2836 self.clear_floats(clear);
2837 let float_side =
2838 FloatSide::from_style_and_container_writing_mode(style, container_writing_mode);
2839 match float_side.expect("A float box needs to float to some side") {
2840 FloatSide::InlineStart => self.uncleared_floats.start.union_assign(&sizes),
2841 FloatSide::InlineEnd => self.uncleared_floats.end.union_assign(&sizes),
2842 }
2843 },
2844 InlineItem::AnonymousBlock(block) => {
2845 self.forced_line_break();
2846 self.flush_floats();
2847 let borrowed_block = block.borrow();
2848 let AnonymousBlockBox {
2849 ref base,
2850 ref contents,
2851 ..
2852 } = *borrowed_block;
2853 let inline_content_sizes_result = outer_inline(
2854 base,
2855 &contents.layout_style(base),
2856 &self.constraint_space.into(),
2857 &LogicalVec2::zero(),
2858 false, false, false, |_| None, |constraint_space| {
2863 base.inline_content_sizes(self.layout_context, constraint_space, contents)
2864 },
2865 |_aspect_ratio| None,
2866 );
2867 self.depends_on_block_constraints |=
2868 inline_content_sizes_result.depends_on_block_constraints;
2869 self.current_line = inline_content_sizes_result.sizes;
2870 self.forced_line_break();
2871 },
2872 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => {},
2873 }
2874 }
2875
2876 fn add_inline_size(&mut self, l: Au) {
2877 self.current_line.min_content += l;
2878 self.current_line.max_content += l;
2879 }
2880
2881 fn line_break_opportunity(&mut self) {
2882 self.pending_whitespace.min_content = Au::zero();
2886 let current_min_content = mem::take(&mut self.current_line.min_content);
2887 self.paragraph.min_content.max_assign(current_min_content);
2888 self.had_content_yet_for_min_content = false;
2889 }
2890
2891 fn forced_line_break(&mut self) {
2892 self.line_break_opportunity();
2894
2895 self.pending_whitespace.max_content = Au::zero();
2897 let current_max_content = mem::take(&mut self.current_line.max_content);
2898 self.paragraph.max_content.max_assign(current_max_content);
2899 self.had_content_yet_for_max_content = false;
2900 }
2901
2902 fn commit_pending_whitespace(&mut self) {
2903 self.current_line += mem::take(&mut self.pending_whitespace);
2904 self.had_content_yet_for_min_content = true;
2905 self.had_content_yet_for_max_content = true;
2906 }
2907
2908 fn outer_inline_content_sizes_of_float_or_atomic(
2909 &mut self,
2910 context: &IndependentFormattingContext,
2911 ) -> ContentSizes {
2912 let result = context.outer_inline_content_sizes(
2913 self.layout_context,
2914 &self.constraint_space.into(),
2915 &LogicalVec2::zero(),
2916 false, );
2918 self.depends_on_block_constraints |= result.depends_on_block_constraints;
2919 result.sizes
2920 }
2921
2922 fn clear_floats(&mut self, clear: Clear) {
2923 match clear {
2924 Clear::InlineStart => {
2925 let start_floats = mem::take(&mut self.uncleared_floats.start);
2926 self.cleared_floats.start.max_assign(start_floats);
2927 },
2928 Clear::InlineEnd => {
2929 let end_floats = mem::take(&mut self.uncleared_floats.end);
2930 self.cleared_floats.end.max_assign(end_floats);
2931 },
2932 Clear::Both => {
2933 let start_floats = mem::take(&mut self.uncleared_floats.start);
2934 let end_floats = mem::take(&mut self.uncleared_floats.end);
2935 self.cleared_floats.start.max_assign(start_floats);
2936 self.cleared_floats.end.max_assign(end_floats);
2937 },
2938 Clear::None => {},
2939 }
2940 }
2941
2942 fn flush_floats(&mut self) {
2943 self.clear_floats(Clear::Both);
2944 let start_floats = mem::take(&mut self.cleared_floats.start);
2945 let end_floats = mem::take(&mut self.cleared_floats.end);
2946 self.paragraph.union_assign(&start_floats);
2947 self.paragraph.union_assign(&end_floats);
2948 }
2949
2950 fn compute(
2952 inline_formatting_context: &InlineFormattingContext,
2953 layout_context: &'layout_data LayoutContext,
2954 constraint_space: &'layout_data ConstraintSpace,
2955 ) -> InlineContentSizesResult {
2956 Self {
2957 layout_context,
2958 constraint_space,
2959 paragraph: ContentSizes::zero(),
2960 current_line: ContentSizes::zero(),
2961 pending_whitespace: ContentSizes::zero(),
2962 uncleared_floats: LogicalSides1D::default(),
2963 cleared_floats: LogicalSides1D::default(),
2964 had_content_yet_for_min_content: false,
2965 had_content_yet_for_max_content: false,
2966 ending_inline_pbm_stack: Vec::new(),
2967 depends_on_block_constraints: false,
2968 }
2969 .traverse(inline_formatting_context)
2970 }
2971}
2972
2973fn char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character: char) -> bool {
2985 if character == '\u{00A0}' {
2986 return false;
2987 }
2988 let class = linebreak_property(character);
2989 class == XI_LINE_BREAKING_CLASS_GL ||
2990 class == XI_LINE_BREAKING_CLASS_WJ ||
2991 class == XI_LINE_BREAKING_CLASS_ZWJ
2992}