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 inline_box::{InlineBox, InlineBoxContainerState, InlineBoxIdentifier, InlineBoxes};
87use layout_api::wrapper_traits::SharedSelection;
88use line::{
89 AbsolutelyPositionedLineItem, AtomicLineItem, FloatLineItem, LineItem, LineItemLayout,
90 TextRunLineItem,
91};
92use line_breaker::LineBreaker;
93use malloc_size_of_derive::MallocSizeOf;
94use script::layout_dom::ServoThreadSafeLayoutNode;
95use servo_arc::Arc as ServoArc;
96use style::Zero;
97use style::computed_values::text_wrap_mode::T as TextWrapMode;
98use style::computed_values::vertical_align::T as VerticalAlign;
99use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
100use style::context::{QuirksMode, SharedStyleContext};
101use style::properties::ComputedValues;
102use style::properties::style_structs::InheritedText;
103use style::values::generics::box_::VerticalAlignKeyword;
104use style::values::generics::font::LineHeight;
105use style::values::specified::box_::BaselineSource;
106use style::values::specified::text::TextAlignKeyword;
107use style::values::specified::{TextAlignLast, TextJustify};
108use text_run::{
109 TextRun, XI_LINE_BREAKING_CLASS_GL, XI_LINE_BREAKING_CLASS_WJ, XI_LINE_BREAKING_CLASS_ZWJ,
110 get_font_for_first_font_for_style,
111};
112use unicode_bidi::{BidiInfo, Level};
113use xi_unicode::linebreak_property;
114
115use super::float::{Clear, PlacementAmongFloats};
116use super::{CacheableLayoutResult, IndependentFloatOrAtomicLayoutResult};
117use crate::cell::{ArcRefCell, WeakRefCell};
118use crate::context::LayoutContext;
119use crate::dom::WeakLayoutBox;
120use crate::dom_traversal::NodeAndStyleInfo;
121use crate::flow::float::{FloatBox, SequentialLayoutState};
122use crate::flow::inline::line::TextRunOffsets;
123use crate::flow::{
124 BlockContainer, CollapsibleWithParentStartMargin, FloatSide, PlacementState,
125 layout_in_flow_non_replaced_block_level_same_formatting_context,
126};
127use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
128use crate::fragment_tree::{
129 BoxFragment, CollapsedMargin, Fragment, FragmentFlags, PositioningFragment,
130};
131use crate::geom::{LogicalRect, LogicalSides1D, LogicalVec2, ToLogical};
132use crate::layout_box_base::LayoutBoxBase;
133use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
134use crate::sizing::{
135 ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, outer_inline,
136};
137use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
138use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SharedStyle};
139
140static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
142static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
143
144#[derive(Debug, MallocSizeOf)]
145pub(crate) struct InlineFormattingContext {
146 inline_items: Vec<InlineItem>,
151
152 inline_boxes: InlineBoxes,
155
156 text_content: String,
158
159 shared_inline_styles: SharedInlineStyles,
162
163 has_first_formatted_line: bool,
166
167 pub(super) contains_floats: bool,
169
170 is_single_line_text_input: bool,
173
174 has_right_to_left_content: bool,
177
178 #[ignore_malloc_size_of = "This is stored primarily in the DOM"]
181 shared_selection: Option<SharedSelection>,
182}
183
184#[derive(Clone, Debug, MallocSizeOf)]
189pub(crate) struct SharedInlineStyles {
190 pub style: SharedStyle,
191 pub selected: SharedStyle,
192}
193
194impl SharedInlineStyles {
195 pub(crate) fn ptr_eq(&self, other: &Self) -> bool {
196 self.style.ptr_eq(&other.style) && self.selected.ptr_eq(&other.selected)
197 }
198}
199
200impl From<&NodeAndStyleInfo<'_>> for SharedInlineStyles {
201 fn from(info: &NodeAndStyleInfo) -> Self {
202 Self {
203 style: SharedStyle::new(info.style.clone()),
204 selected: SharedStyle::new(info.node.selected_style()),
205 }
206 }
207}
208
209#[derive(Debug, MallocSizeOf)]
213pub(crate) struct AnonymousBlockBox {
214 base: LayoutBoxBase,
215 contents: BlockContainer,
216}
217
218impl AnonymousBlockBox {
219 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
220 layout.process_soft_wrap_opportunity();
221 layout.commit_current_segment_to_line();
222 layout.process_line_break(true);
223 layout.current_line.for_block_level = true;
224
225 let fragment = layout
226 .positioning_context
227 .layout_maybe_position_relative_fragment(
228 layout.layout_context,
229 layout.placement_state.containing_block,
230 &self.base,
231 |positioning_context| {
232 layout_in_flow_non_replaced_block_level_same_formatting_context(
233 layout.layout_context,
234 positioning_context,
235 layout.placement_state.containing_block,
236 &self.base,
237 &self.contents,
238 layout.sequential_layout_state.as_deref_mut(),
239 Some(CollapsibleWithParentStartMargin(
240 layout
241 .placement_state
242 .next_in_flow_margin_collapses_with_parent_start_margin,
243 )),
244 LogicalSides1D::new(false, false),
248 )
249 },
250 );
251
252 layout.depends_on_block_constraints |= fragment.base.flags.contains(
255 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
256 );
257
258 let mut fragment = Fragment::Box(ArcRefCell::new(fragment));
259 layout.placement_state.place_fragment_and_update_baseline(
260 &mut fragment,
261 layout.sequential_layout_state.as_deref_mut(),
262 );
263
264 let Fragment::Box(fragment) = fragment else {
265 unreachable!("The fragment should still be a Fragment::Box()");
266 };
267 layout.push_line_item_to_unbreakable_segment(LineItem::AnonymousBlockBox(
268 layout.current_inline_box_identifier(),
269 fragment,
270 ));
271
272 layout.commit_current_segment_to_line();
273 layout.process_line_break(true);
274 layout.current_line.for_block_level = false;
275 }
276}
277
278#[derive(Clone, Debug, MallocSizeOf)]
279pub(crate) enum InlineItem {
280 StartInlineBox(ArcRefCell<InlineBox>),
281 EndInlineBox,
282 TextRun(ArcRefCell<TextRun>),
283 OutOfFlowAbsolutelyPositionedBox(
284 ArcRefCell<AbsolutelyPositionedBox>,
285 usize, ),
287 OutOfFlowFloatBox(ArcRefCell<FloatBox>),
288 Atomic(
289 ArcRefCell<IndependentFormattingContext>,
290 usize, Level, ),
293 AnonymousBlock(ArcRefCell<AnonymousBlockBox>),
294}
295
296impl InlineItem {
297 pub(crate) fn repair_style(
298 &self,
299 context: &SharedStyleContext,
300 node: &ServoThreadSafeLayoutNode,
301 new_style: &ServoArc<ComputedValues>,
302 ) {
303 match self {
304 InlineItem::StartInlineBox(inline_box) => {
305 inline_box.borrow_mut().repair_style(node, new_style);
306 },
307 InlineItem::EndInlineBox => {},
308 InlineItem::TextRun(..) => {},
311 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box
312 .borrow_mut()
313 .context
314 .repair_style(context, node, new_style),
315 InlineItem::OutOfFlowFloatBox(float_box) => float_box
316 .borrow_mut()
317 .contents
318 .repair_style(context, node, new_style),
319 InlineItem::Atomic(atomic, ..) => {
320 atomic.borrow_mut().repair_style(context, node, new_style)
321 },
322 InlineItem::AnonymousBlock(block_box) => {
323 let mut block_box = block_box.borrow_mut();
324 block_box.base.repair_style(new_style);
325 block_box.contents.repair_style(node, new_style);
326 },
327 }
328 }
329
330 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
331 match self {
332 InlineItem::StartInlineBox(inline_box) => callback(&inline_box.borrow().base),
333 InlineItem::EndInlineBox | InlineItem::TextRun(..) => {
334 unreachable!("Should never have these kind of fragments attached to a DOM node")
335 },
336 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
337 callback(&positioned_box.borrow().context.base)
338 },
339 InlineItem::OutOfFlowFloatBox(float_box) => callback(&float_box.borrow().contents.base),
340 InlineItem::Atomic(independent_formatting_context, ..) => {
341 callback(&independent_formatting_context.borrow().base)
342 },
343 InlineItem::AnonymousBlock(block_box) => callback(&block_box.borrow().base),
344 }
345 }
346
347 pub(crate) fn with_base_mut<T>(&self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
348 match self {
349 InlineItem::StartInlineBox(inline_box) => callback(&mut inline_box.borrow_mut().base),
350 InlineItem::EndInlineBox | InlineItem::TextRun(..) => {
351 unreachable!("Should never have these kind of fragments attached to a DOM node")
352 },
353 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
354 callback(&mut positioned_box.borrow_mut().context.base)
355 },
356 InlineItem::OutOfFlowFloatBox(float_box) => {
357 callback(&mut float_box.borrow_mut().contents.base)
358 },
359 InlineItem::Atomic(independent_formatting_context, ..) => {
360 callback(&mut independent_formatting_context.borrow_mut().base)
361 },
362 InlineItem::AnonymousBlock(block_box) => callback(&mut block_box.borrow_mut().base),
363 }
364 }
365
366 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
367 match self {
368 Self::StartInlineBox(_) | InlineItem::EndInlineBox => {
369 },
372 Self::TextRun(_) => {
373 },
375 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
376 positioned_box.borrow().context.attached_to_tree(layout_box)
377 },
378 Self::OutOfFlowFloatBox(float_box) => {
379 float_box.borrow().contents.attached_to_tree(layout_box)
380 },
381 Self::Atomic(atomic, ..) => atomic.borrow().attached_to_tree(layout_box),
382 Self::AnonymousBlock(block_box) => {
383 block_box.borrow().contents.attached_to_tree(layout_box)
384 },
385 }
386 }
387
388 pub(crate) fn downgrade(&self) -> WeakInlineItem {
389 match self {
390 Self::StartInlineBox(inline_box) => {
391 WeakInlineItem::StartInlineBox(inline_box.downgrade())
392 },
393 Self::EndInlineBox => WeakInlineItem::EndInlineBox,
394 Self::TextRun(text_run) => WeakInlineItem::TextRun(text_run.downgrade()),
395 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
396 WeakInlineItem::OutOfFlowAbsolutelyPositionedBox(
397 positioned_box.downgrade(),
398 *offset_in_text,
399 )
400 },
401 Self::OutOfFlowFloatBox(float_box) => {
402 WeakInlineItem::OutOfFlowFloatBox(float_box.downgrade())
403 },
404 Self::Atomic(atomic, offset_in_text, bidi_level) => {
405 WeakInlineItem::Atomic(atomic.downgrade(), *offset_in_text, *bidi_level)
406 },
407 Self::AnonymousBlock(block_box) => {
408 WeakInlineItem::AnonymousBlock(block_box.downgrade())
409 },
410 }
411 }
412}
413
414#[derive(Clone, Debug, MallocSizeOf)]
415pub(crate) enum WeakInlineItem {
416 StartInlineBox(WeakRefCell<InlineBox>),
417 EndInlineBox,
418 TextRun(WeakRefCell<TextRun>),
419 OutOfFlowAbsolutelyPositionedBox(
420 WeakRefCell<AbsolutelyPositionedBox>,
421 usize, ),
423 OutOfFlowFloatBox(WeakRefCell<FloatBox>),
424 Atomic(
425 WeakRefCell<IndependentFormattingContext>,
426 usize, Level, ),
429 AnonymousBlock(WeakRefCell<AnonymousBlockBox>),
430}
431
432impl WeakInlineItem {
433 pub(crate) fn upgrade(&self) -> Option<InlineItem> {
434 Some(match self {
435 Self::StartInlineBox(inline_box) => InlineItem::StartInlineBox(inline_box.upgrade()?),
436 Self::EndInlineBox => InlineItem::EndInlineBox,
437 Self::TextRun(text_run) => InlineItem::TextRun(text_run.upgrade()?),
438 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
439 InlineItem::OutOfFlowAbsolutelyPositionedBox(
440 positioned_box.upgrade()?,
441 *offset_in_text,
442 )
443 },
444 Self::OutOfFlowFloatBox(float_box) => {
445 InlineItem::OutOfFlowFloatBox(float_box.upgrade()?)
446 },
447 Self::Atomic(atomic, offset_in_text, bidi_level) => {
448 InlineItem::Atomic(atomic.upgrade()?, *offset_in_text, *bidi_level)
449 },
450 Self::AnonymousBlock(block_box) => InlineItem::AnonymousBlock(block_box.upgrade()?),
451 })
452 }
453}
454
455struct LineUnderConstruction {
462 start_position: LogicalVec2<Au>,
465
466 inline_position: Au,
469
470 max_block_size: LineBlockSizes,
474
475 has_content: bool,
478
479 has_inline_pbm: bool,
482
483 has_floats_waiting_to_be_placed: bool,
487
488 placement_among_floats: OnceCell<LogicalRect<Au>>,
493
494 line_items: Vec<LineItem>,
497
498 for_block_level: bool,
500}
501
502impl LineUnderConstruction {
503 fn new(start_position: LogicalVec2<Au>) -> Self {
504 Self {
505 inline_position: start_position.inline,
506 start_position,
507 max_block_size: LineBlockSizes::zero(),
508 has_content: false,
509 has_inline_pbm: false,
510 has_floats_waiting_to_be_placed: false,
511 placement_among_floats: OnceCell::new(),
512 line_items: Vec::new(),
513 for_block_level: false,
514 }
515 }
516
517 fn replace_placement_among_floats(&mut self, new_placement: LogicalRect<Au>) {
518 self.placement_among_floats.take();
519 let _ = self.placement_among_floats.set(new_placement);
520 }
521
522 fn trim_trailing_whitespace(&mut self) -> Au {
524 let mut whitespace_trimmed = Au::zero();
529 for item in self.line_items.iter_mut().rev() {
530 if !item.trim_whitespace_at_end(&mut whitespace_trimmed) {
531 break;
532 }
533 }
534
535 whitespace_trimmed
536 }
537
538 fn count_justification_opportunities(&self) -> usize {
540 self.line_items
541 .iter()
542 .filter_map(|item| match item {
543 LineItem::TextRun(_, text_run) => Some(
544 text_run
545 .text
546 .iter()
547 .map(|glyph_store| glyph_store.total_word_separators())
548 .sum::<usize>(),
549 ),
550 _ => None,
551 })
552 .sum()
553 }
554}
555
556#[derive(Clone, Debug)]
562struct BaselineRelativeSize {
563 ascent: Au,
567
568 descent: Au,
572}
573
574impl BaselineRelativeSize {
575 fn zero() -> Self {
576 Self {
577 ascent: Au::zero(),
578 descent: Au::zero(),
579 }
580 }
581
582 fn max(&self, other: &Self) -> Self {
583 BaselineRelativeSize {
584 ascent: self.ascent.max(other.ascent),
585 descent: self.descent.max(other.descent),
586 }
587 }
588
589 fn adjust_for_nested_baseline_offset(&mut self, baseline_offset: Au) {
603 self.ascent -= baseline_offset;
604 self.descent += baseline_offset;
605 }
606}
607
608#[derive(Clone, Debug)]
609struct LineBlockSizes {
610 line_height: Au,
611 baseline_relative_size_for_line_height: Option<BaselineRelativeSize>,
612 size_for_baseline_positioning: BaselineRelativeSize,
613}
614
615impl LineBlockSizes {
616 fn zero() -> Self {
617 LineBlockSizes {
618 line_height: Au::zero(),
619 baseline_relative_size_for_line_height: None,
620 size_for_baseline_positioning: BaselineRelativeSize::zero(),
621 }
622 }
623
624 fn resolve(&self) -> Au {
625 let height_from_ascent_and_descent = self
626 .baseline_relative_size_for_line_height
627 .as_ref()
628 .map(|size| (size.ascent + size.descent).abs())
629 .unwrap_or_else(Au::zero);
630 self.line_height.max(height_from_ascent_and_descent)
631 }
632
633 fn max(&self, other: &LineBlockSizes) -> LineBlockSizes {
634 let baseline_relative_size = match (
635 self.baseline_relative_size_for_line_height.as_ref(),
636 other.baseline_relative_size_for_line_height.as_ref(),
637 ) {
638 (Some(our_size), Some(other_size)) => Some(our_size.max(other_size)),
639 (our_size, other_size) => our_size.or(other_size).cloned(),
640 };
641 Self {
642 line_height: self.line_height.max(other.line_height),
643 baseline_relative_size_for_line_height: baseline_relative_size,
644 size_for_baseline_positioning: self
645 .size_for_baseline_positioning
646 .max(&other.size_for_baseline_positioning),
647 }
648 }
649
650 fn max_assign(&mut self, other: &LineBlockSizes) {
651 *self = self.max(other);
652 }
653
654 fn adjust_for_baseline_offset(&mut self, baseline_offset: Au) {
655 if let Some(size) = self.baseline_relative_size_for_line_height.as_mut() {
656 size.adjust_for_nested_baseline_offset(baseline_offset)
657 }
658 self.size_for_baseline_positioning
659 .adjust_for_nested_baseline_offset(baseline_offset);
660 }
661
662 fn find_baseline_offset(&self) -> Au {
669 match self.baseline_relative_size_for_line_height.as_ref() {
670 Some(size) => size.ascent,
671 None => {
672 let leading = self.resolve() -
675 (self.size_for_baseline_positioning.ascent +
676 self.size_for_baseline_positioning.descent);
677 leading.scale_by(0.5) + self.size_for_baseline_positioning.ascent
678 },
679 }
680 }
681}
682
683struct UnbreakableSegmentUnderConstruction {
687 inline_size: Au,
689
690 max_block_size: LineBlockSizes,
693
694 line_items: Vec<LineItem>,
696
697 inline_box_hierarchy_depth: Option<usize>,
700
701 has_content: bool,
705
706 has_inline_pbm: bool,
709
710 trailing_whitespace_size: Au,
712}
713
714impl UnbreakableSegmentUnderConstruction {
715 fn new() -> Self {
716 Self {
717 inline_size: Au::zero(),
718 max_block_size: LineBlockSizes {
719 line_height: Au::zero(),
720 baseline_relative_size_for_line_height: None,
721 size_for_baseline_positioning: BaselineRelativeSize::zero(),
722 },
723 line_items: Vec::new(),
724 inline_box_hierarchy_depth: None,
725 has_content: false,
726 has_inline_pbm: false,
727 trailing_whitespace_size: Au::zero(),
728 }
729 }
730
731 fn reset(&mut self) {
733 assert!(self.line_items.is_empty()); self.inline_size = Au::zero();
735 self.max_block_size = LineBlockSizes::zero();
736 self.inline_box_hierarchy_depth = None;
737 self.has_content = false;
738 self.has_inline_pbm = false;
739 self.trailing_whitespace_size = Au::zero();
740 }
741
742 fn push_line_item(&mut self, line_item: LineItem, inline_box_hierarchy_depth: usize) {
747 if self.line_items.is_empty() {
748 self.inline_box_hierarchy_depth = Some(inline_box_hierarchy_depth);
749 }
750 self.line_items.push(line_item);
751 }
752
753 fn trim_leading_whitespace(&mut self) {
764 let mut whitespace_trimmed = Au::zero();
765 for item in self.line_items.iter_mut() {
766 if !item.trim_whitespace_at_start(&mut whitespace_trimmed) {
767 break;
768 }
769 }
770 self.inline_size -= whitespace_trimmed;
771 }
772}
773
774bitflags! {
775 struct InlineContainerStateFlags: u8 {
776 const CREATE_STRUT = 0b0001;
777 const IS_SINGLE_LINE_TEXT_INPUT = 0b0010;
778 }
779}
780
781struct InlineContainerState {
782 style: ServoArc<ComputedValues>,
784
785 flags: InlineContainerStateFlags,
787
788 has_content: RefCell<bool>,
791
792 strut_block_sizes: LineBlockSizes,
797
798 nested_strut_block_sizes: LineBlockSizes,
802
803 pub baseline_offset: Au,
809
810 font_metrics: Arc<FontMetrics>,
812}
813
814struct InlineFormattingContextLayout<'layout_data> {
815 positioning_context: &'layout_data mut PositioningContext,
816 placement_state: PlacementState<'layout_data>,
817 sequential_layout_state: Option<&'layout_data mut SequentialLayoutState>,
818 layout_context: &'layout_data LayoutContext<'layout_data>,
819
820 ifc: &'layout_data InlineFormattingContext,
822
823 root_nesting_level: InlineContainerState,
833
834 inline_box_state_stack: Vec<Rc<InlineBoxContainerState>>,
838
839 inline_box_states: Vec<Rc<InlineBoxContainerState>>,
844
845 fragments: Vec<Fragment>,
849
850 current_line: LineUnderConstruction,
852
853 current_line_segment: UnbreakableSegmentUnderConstruction,
855
856 linebreak_before_new_content: bool,
875
876 deferred_br_clear: Clear,
880
881 pub have_deferred_soft_wrap_opportunity: bool,
885
886 depends_on_block_constraints: bool,
889
890 white_space_collapse: WhiteSpaceCollapse,
895
896 text_wrap_mode: TextWrapMode,
901}
902
903impl InlineFormattingContextLayout<'_> {
904 fn current_inline_container_state(&self) -> &InlineContainerState {
905 match self.inline_box_state_stack.last() {
906 Some(inline_box_state) => &inline_box_state.base,
907 None => &self.root_nesting_level,
908 }
909 }
910
911 fn current_inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
912 self.inline_box_state_stack
913 .last()
914 .map(|state| state.identifier)
915 }
916
917 fn current_line_max_block_size_including_nested_containers(&self) -> LineBlockSizes {
918 self.current_inline_container_state()
919 .nested_strut_block_sizes
920 .max(&self.current_line.max_block_size)
921 }
922
923 fn current_line_block_start_considering_placement_among_floats(&self) -> Au {
924 self.current_line.placement_among_floats.get().map_or(
925 self.current_line.start_position.block,
926 |placement_among_floats| placement_among_floats.start_corner.block,
927 )
928 }
929
930 fn propagate_current_nesting_level_white_space_style(&mut self) {
931 let style = match self.inline_box_state_stack.last() {
932 Some(inline_box_state) => &inline_box_state.base.style,
933 None => self.placement_state.containing_block.style,
934 };
935 let style_text = style.get_inherited_text();
936 self.white_space_collapse = style_text.white_space_collapse;
937 self.text_wrap_mode = style_text.text_wrap_mode;
938 }
939
940 fn processing_br_element(&self) -> bool {
941 self.inline_box_state_stack.last().is_some_and(|state| {
942 state
943 .base_fragment_info
944 .flags
945 .contains(FragmentFlags::IS_BR_ELEMENT)
946 })
947 }
948
949 fn start_inline_box(&mut self, inline_box: &InlineBox) {
952 let containing_block = self.containing_block();
953 let inline_box_state = InlineBoxContainerState::new(
954 inline_box,
955 containing_block,
956 self.layout_context,
957 self.current_inline_container_state(),
958 inline_box
959 .default_font
960 .as_ref()
961 .map(|font| font.metrics.clone()),
962 );
963
964 self.depends_on_block_constraints |= inline_box
965 .base
966 .style
967 .depends_on_block_constraints_due_to_relative_positioning(
968 containing_block.style.writing_mode,
969 );
970
971 if inline_box_state
976 .base_fragment_info
977 .flags
978 .contains(FragmentFlags::IS_BR_ELEMENT) &&
979 self.deferred_br_clear == Clear::None
980 {
981 self.deferred_br_clear = Clear::from_style_and_container_writing_mode(
982 &inline_box_state.base.style,
983 self.containing_block().style.writing_mode,
984 );
985 }
986
987 let padding = inline_box_state.pbm.padding.inline_start;
988 let border = inline_box_state.pbm.border.inline_start;
989 let margin = inline_box_state.pbm.margin.inline_start.auto_is(Au::zero);
990 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
993 self.current_line_segment.has_inline_pbm = true;
994 }
995 self.current_line_segment.inline_size += padding + border + margin;
996 self.current_line_segment
997 .line_items
998 .push(LineItem::InlineStartBoxPaddingBorderMargin(
999 inline_box.identifier,
1000 ));
1001
1002 let inline_box_state = Rc::new(inline_box_state);
1003
1004 assert_eq!(
1008 self.inline_box_states.len(),
1009 inline_box.identifier.index_in_inline_boxes as usize
1010 );
1011 self.inline_box_states.push(inline_box_state.clone());
1012 self.inline_box_state_stack.push(inline_box_state);
1013 }
1014
1015 fn finish_inline_box(&mut self) {
1018 let inline_box_state = match self.inline_box_state_stack.pop() {
1019 Some(inline_box_state) => inline_box_state,
1020 None => return, };
1022
1023 self.current_line_segment
1024 .max_block_size
1025 .max_assign(&inline_box_state.base.nested_strut_block_sizes);
1026
1027 if *inline_box_state.base.has_content.borrow() {
1032 self.propagate_current_nesting_level_white_space_style();
1033 }
1034
1035 let padding = inline_box_state.pbm.padding.inline_end;
1036 let border = inline_box_state.pbm.border.inline_end;
1037 let margin = inline_box_state.pbm.margin.inline_end.auto_is(Au::zero);
1038 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
1041 self.current_line_segment.has_inline_pbm = true;
1042 }
1043 self.current_line_segment.inline_size += padding + border + margin;
1044 self.current_line_segment
1045 .line_items
1046 .push(LineItem::InlineEndBoxPaddingBorderMargin(
1047 inline_box_state.identifier,
1048 ))
1049 }
1050
1051 fn finish_last_line(&mut self) {
1052 self.process_soft_wrap_opportunity();
1058
1059 self.commit_current_segment_to_line();
1062
1063 self.finish_current_line_and_reset(true );
1066 }
1067
1068 fn finish_current_line_and_reset(&mut self, last_line_or_forced_line_break: bool) {
1072 let whitespace_trimmed = self.current_line.trim_trailing_whitespace();
1073 let (inline_start_position, justification_adjustment) = self
1074 .calculate_current_line_inline_start_and_justification_adjustment(
1075 whitespace_trimmed,
1076 last_line_or_forced_line_break,
1077 );
1078
1079 let is_phantom_line = !self.current_line.has_content && !self.current_line.has_inline_pbm;
1088 if !is_phantom_line {
1089 self.current_line.start_position.block += self.placement_state.current_margin.solve();
1090 self.placement_state.current_margin = CollapsedMargin::zero();
1091 }
1092 let block_start_position =
1093 self.current_line_block_start_considering_placement_among_floats();
1094
1095 let effective_block_advance = if is_phantom_line {
1096 LineBlockSizes::zero()
1097 } else {
1098 self.current_line_max_block_size_including_nested_containers()
1099 };
1100
1101 let resolved_block_advance = effective_block_advance.resolve();
1102 let block_end_position = if self.current_line.for_block_level {
1103 self.placement_state.current_block_direction_position
1104 } else {
1105 let mut block_end_position = block_start_position + resolved_block_advance;
1106 if let Some(sequential_layout_state) = self.sequential_layout_state.as_mut() {
1107 if !is_phantom_line {
1108 sequential_layout_state.collapse_margins();
1109 }
1110
1111 let increment = block_end_position - self.current_line.start_position.block;
1114 sequential_layout_state.advance_block_position(increment);
1115
1116 if let Some(clearance) = sequential_layout_state
1120 .calculate_clearance(self.deferred_br_clear, &CollapsedMargin::zero())
1121 {
1122 sequential_layout_state.advance_block_position(clearance);
1123 block_end_position += clearance;
1124 };
1125 self.deferred_br_clear = Clear::None;
1126 }
1127 block_end_position
1128 };
1129
1130 let mut line_to_layout = std::mem::replace(
1132 &mut self.current_line,
1133 LineUnderConstruction::new(LogicalVec2 {
1134 inline: Au::zero(),
1135 block: block_end_position,
1136 }),
1137 );
1138 if !line_to_layout.for_block_level {
1139 self.placement_state.current_block_direction_position = block_end_position;
1140 }
1141
1142 if line_to_layout.has_floats_waiting_to_be_placed {
1143 place_pending_floats(self, &mut line_to_layout.line_items);
1144 }
1145
1146 let start_position = LogicalVec2 {
1147 block: block_start_position,
1148 inline: inline_start_position,
1149 };
1150
1151 let baseline_offset = effective_block_advance.find_baseline_offset();
1152 let start_positioning_context_length = self.positioning_context.len();
1153 let fragments = LineItemLayout::layout_line_items(
1154 self,
1155 line_to_layout.line_items,
1156 start_position,
1157 &effective_block_advance,
1158 justification_adjustment,
1159 is_phantom_line,
1160 );
1161
1162 if !is_phantom_line {
1163 let baseline = baseline_offset + block_start_position;
1164 self.placement_state
1165 .inflow_baselines
1166 .first
1167 .get_or_insert(baseline);
1168 self.placement_state.inflow_baselines.last = Some(baseline);
1169 self.placement_state
1170 .next_in_flow_margin_collapses_with_parent_start_margin = false;
1171 }
1172
1173 if fragments.is_empty() &&
1175 self.positioning_context.len() == start_positioning_context_length
1176 {
1177 return;
1178 }
1179
1180 let start_corner = LogicalVec2 {
1184 inline: Au::zero(),
1185 block: block_start_position,
1186 };
1187
1188 let logical_origin_in_physical_coordinates =
1189 start_corner.to_physical_vector(self.containing_block().style.writing_mode);
1190 self.positioning_context
1191 .adjust_static_position_of_hoisted_fragments_with_offset(
1192 &logical_origin_in_physical_coordinates,
1193 start_positioning_context_length,
1194 );
1195
1196 let containing_block = self.containing_block();
1197 let physical_line_rect = LogicalRect {
1198 start_corner,
1199 size: LogicalVec2 {
1200 inline: containing_block.size.inline,
1201 block: effective_block_advance.resolve(),
1202 },
1203 }
1204 .as_physical(Some(containing_block));
1205 self.fragments
1206 .push(Fragment::Positioning(PositioningFragment::new_anonymous(
1207 self.root_nesting_level.style.clone(),
1208 physical_line_rect,
1209 fragments,
1210 )));
1211 }
1212
1213 fn calculate_current_line_inline_start_and_justification_adjustment(
1218 &self,
1219 whitespace_trimmed: Au,
1220 last_line_or_forced_line_break: bool,
1221 ) -> (Au, Au) {
1222 enum TextAlign {
1223 Start,
1224 Center,
1225 End,
1226 }
1227 let containing_block = self.containing_block();
1228 let style = containing_block.style;
1229 let mut text_align_keyword = style.clone_text_align();
1230
1231 if last_line_or_forced_line_break {
1232 text_align_keyword = match style.clone_text_align_last() {
1233 TextAlignLast::Auto if text_align_keyword == TextAlignKeyword::Justify => {
1234 TextAlignKeyword::Start
1235 },
1236 TextAlignLast::Auto => text_align_keyword,
1237 TextAlignLast::Start => TextAlignKeyword::Start,
1238 TextAlignLast::End => TextAlignKeyword::End,
1239 TextAlignLast::Left => TextAlignKeyword::Left,
1240 TextAlignLast::Right => TextAlignKeyword::Right,
1241 TextAlignLast::Center => TextAlignKeyword::Center,
1242 TextAlignLast::Justify => TextAlignKeyword::Justify,
1243 };
1244 }
1245
1246 let text_align = match text_align_keyword {
1247 TextAlignKeyword::Start => TextAlign::Start,
1248 TextAlignKeyword::Center | TextAlignKeyword::MozCenter => TextAlign::Center,
1249 TextAlignKeyword::End => TextAlign::End,
1250 TextAlignKeyword::Left | TextAlignKeyword::MozLeft => {
1251 if style.writing_mode.line_left_is_inline_start() {
1252 TextAlign::Start
1253 } else {
1254 TextAlign::End
1255 }
1256 },
1257 TextAlignKeyword::Right | TextAlignKeyword::MozRight => {
1258 if style.writing_mode.line_left_is_inline_start() {
1259 TextAlign::End
1260 } else {
1261 TextAlign::Start
1262 }
1263 },
1264 TextAlignKeyword::Justify => TextAlign::Start,
1265 };
1266
1267 let (line_start, available_space) = match self.current_line.placement_among_floats.get() {
1268 Some(placement_among_floats) => (
1269 placement_among_floats.start_corner.inline,
1270 placement_among_floats.size.inline,
1271 ),
1272 None => (Au::zero(), containing_block.size.inline),
1273 };
1274
1275 let text_indent = self.current_line.start_position.inline;
1282 let line_length = self.current_line.inline_position - whitespace_trimmed - text_indent;
1283 let adjusted_line_start = line_start +
1284 match text_align {
1285 TextAlign::Start => text_indent,
1286 TextAlign::End => (available_space - line_length).max(text_indent),
1287 TextAlign::Center => (available_space - line_length + text_indent)
1288 .scale_by(0.5)
1289 .max(text_indent),
1290 };
1291
1292 let text_justify = containing_block.style.clone_text_justify();
1296 let justification_adjustment = match (text_align_keyword, text_justify) {
1297 (TextAlignKeyword::Justify, TextJustify::None) => Au::zero(),
1300 (TextAlignKeyword::Justify, _) => {
1301 match self.current_line.count_justification_opportunities() {
1302 0 => Au::zero(),
1303 num_justification_opportunities => {
1304 (available_space - text_indent - line_length)
1305 .scale_by(1. / num_justification_opportunities as f32)
1306 },
1307 }
1308 },
1309 _ => Au::zero(),
1310 };
1311
1312 let justification_adjustment = justification_adjustment.max(Au::zero());
1315
1316 (adjusted_line_start, justification_adjustment)
1317 }
1318
1319 fn place_float_fragment(&mut self, fragment: &mut BoxFragment) {
1320 let state = self
1321 .sequential_layout_state
1322 .as_mut()
1323 .expect("Tried to lay out a float with no sequential placement state!");
1324
1325 let block_offset_from_containining_block_top = state
1326 .current_block_position_including_margins() -
1327 state.current_containing_block_offset();
1328 state.place_float_fragment(
1329 fragment,
1330 self.placement_state.containing_block,
1331 CollapsedMargin::zero(),
1332 block_offset_from_containining_block_top,
1333 );
1334 }
1335
1336 fn place_float_line_item_for_commit_to_line(
1345 &mut self,
1346 float_item: &mut FloatLineItem,
1347 line_inline_size_without_trailing_whitespace: Au,
1348 ) {
1349 let containing_block = self.containing_block();
1350 let mut float_fragment = float_item.fragment.borrow_mut();
1351 let logical_margin_rect_size = float_fragment
1352 .margin_rect()
1353 .size
1354 .to_logical(containing_block.style.writing_mode);
1355 let inline_size = logical_margin_rect_size.inline.max(Au::zero());
1356
1357 let available_inline_size = match self.current_line.placement_among_floats.get() {
1358 Some(placement_among_floats) => placement_among_floats.size.inline,
1359 None => containing_block.size.inline,
1360 } - line_inline_size_without_trailing_whitespace;
1361
1362 let has_content = self.current_line.has_content || self.current_line_segment.has_content;
1368 let fits_on_line = !has_content || inline_size <= available_inline_size;
1369 let needs_placement_later =
1370 self.current_line.has_floats_waiting_to_be_placed || !fits_on_line;
1371
1372 if needs_placement_later {
1373 self.current_line.has_floats_waiting_to_be_placed = true;
1374 } else {
1375 self.place_float_fragment(&mut float_fragment);
1376 float_item.needs_placement = false;
1377 }
1378
1379 let new_placement = self.place_line_among_floats(&LogicalVec2 {
1384 inline: line_inline_size_without_trailing_whitespace,
1385 block: self.current_line.max_block_size.resolve(),
1386 });
1387 self.current_line
1388 .replace_placement_among_floats(new_placement);
1389 }
1390
1391 fn place_line_among_floats(&self, potential_line_size: &LogicalVec2<Au>) -> LogicalRect<Au> {
1396 let sequential_layout_state = self
1397 .sequential_layout_state
1398 .as_ref()
1399 .expect("Should not have called this function without having floats.");
1400
1401 let ifc_offset_in_float_container = LogicalVec2 {
1402 inline: sequential_layout_state
1403 .floats
1404 .containing_block_info
1405 .inline_start,
1406 block: sequential_layout_state.current_containing_block_offset(),
1407 };
1408
1409 let ceiling = self.current_line_block_start_considering_placement_among_floats();
1410 let mut placement = PlacementAmongFloats::new(
1411 &sequential_layout_state.floats,
1412 ceiling + ifc_offset_in_float_container.block,
1413 LogicalVec2 {
1414 inline: potential_line_size.inline,
1415 block: potential_line_size.block,
1416 },
1417 &PaddingBorderMargin::zero(),
1418 );
1419
1420 let mut placement_rect = placement.place();
1421 placement_rect.start_corner -= ifc_offset_in_float_container;
1422 placement_rect
1423 }
1424
1425 fn new_potential_line_size_causes_line_break(
1432 &mut self,
1433 potential_line_size: &LogicalVec2<Au>,
1434 ) -> bool {
1435 let containing_block = self.containing_block();
1436 let available_line_space = if self.sequential_layout_state.is_some() {
1437 self.current_line
1438 .placement_among_floats
1439 .get_or_init(|| self.place_line_among_floats(potential_line_size))
1440 .size
1441 } else {
1442 LogicalVec2 {
1443 inline: containing_block.size.inline,
1444 block: MAX_AU,
1445 }
1446 };
1447
1448 let inline_would_overflow = potential_line_size.inline > available_line_space.inline;
1449 let block_would_overflow = potential_line_size.block > available_line_space.block;
1450
1451 let can_break = self.current_line.has_content;
1454
1455 if !can_break {
1461 if self.sequential_layout_state.is_some() &&
1464 (inline_would_overflow || block_would_overflow)
1465 {
1466 let new_placement = self.place_line_among_floats(potential_line_size);
1467 self.current_line
1468 .replace_placement_among_floats(new_placement);
1469 }
1470
1471 return false;
1472 }
1473
1474 if potential_line_size.inline > containing_block.size.inline {
1477 return true;
1478 }
1479
1480 if block_would_overflow {
1484 assert!(self.sequential_layout_state.is_some());
1486 let new_placement = self.place_line_among_floats(potential_line_size);
1487 if new_placement.start_corner.block !=
1488 self.current_line_block_start_considering_placement_among_floats()
1489 {
1490 return true;
1491 } else {
1492 self.current_line
1493 .replace_placement_among_floats(new_placement);
1494 return false;
1495 }
1496 }
1497
1498 inline_would_overflow
1502 }
1503
1504 fn defer_forced_line_break(&mut self) {
1505 if !self.unbreakable_segment_fits_on_line() {
1508 self.process_line_break(false );
1509 }
1510
1511 self.linebreak_before_new_content = true;
1513
1514 let line_is_empty =
1522 !self.current_line_segment.has_content && !self.current_line.has_content;
1523 if !self.processing_br_element() || line_is_empty {
1524 let strut_size = self
1525 .current_inline_container_state()
1526 .strut_block_sizes
1527 .clone();
1528 self.update_unbreakable_segment_for_new_content(
1529 &strut_size,
1530 Au::zero(),
1531 SegmentContentFlags::empty(),
1532 );
1533 }
1534 }
1535
1536 fn possibly_flush_deferred_forced_line_break(&mut self) {
1537 if !self.linebreak_before_new_content {
1538 return;
1539 }
1540
1541 self.commit_current_segment_to_line();
1542 self.process_line_break(true );
1543 self.linebreak_before_new_content = false;
1544 }
1545
1546 fn push_line_item_to_unbreakable_segment(&mut self, line_item: LineItem) {
1547 self.current_line_segment
1548 .push_line_item(line_item, self.inline_box_state_stack.len());
1549 }
1550
1551 fn push_glyph_store_to_unbreakable_segment(
1552 &mut self,
1553 glyph_store: Arc<GlyphStore>,
1554 text_run: &TextRun,
1555 font: &FontRef,
1556 bidi_level: Level,
1557 offsets: Option<TextRunOffsets>,
1558 ) {
1559 let inline_advance = glyph_store.total_advance();
1560 let flags = if glyph_store.is_whitespace() {
1561 SegmentContentFlags::from(text_run.inline_styles.style.borrow().get_inherited_text())
1562 } else {
1563 SegmentContentFlags::empty()
1564 };
1565
1566 let font_metrics = &font.metrics;
1570 let font_key = font.key(
1571 self.layout_context.painter_id,
1572 &self.layout_context.font_context,
1573 );
1574 let using_fallback_font = !Arc::ptr_eq(
1575 &self.current_inline_container_state().font_metrics,
1576 font_metrics,
1577 );
1578
1579 let quirks_mode = self.layout_context.style_context.quirks_mode() != QuirksMode::NoQuirks;
1580 let strut_size = if using_fallback_font {
1581 let container_state = self.current_inline_container_state();
1583 let vertical_align = effective_vertical_align(
1584 &container_state.style,
1585 self.inline_box_state_stack.last().map(|c| &c.base),
1586 );
1587 let mut block_size = container_state.get_block_size_contribution(
1588 vertical_align,
1589 font_metrics,
1590 &container_state.font_metrics,
1591 );
1592 block_size.adjust_for_baseline_offset(container_state.baseline_offset);
1593 block_size
1594 } else if quirks_mode && !flags.is_collapsible_whitespace() {
1595 self.current_inline_container_state()
1600 .strut_block_sizes
1601 .clone()
1602 } else {
1603 LineBlockSizes::zero()
1604 };
1605 self.update_unbreakable_segment_for_new_content(&strut_size, inline_advance, flags);
1606
1607 let current_inline_box_identifier = self.current_inline_box_identifier();
1608 if let Some(LineItem::TextRun(inline_box_identifier, line_item)) =
1609 self.current_line_segment.line_items.last_mut()
1610 {
1611 if *inline_box_identifier == current_inline_box_identifier &&
1612 line_item.merge_if_possible(font_key, bidi_level, &glyph_store, &offsets)
1613 {
1614 return;
1615 }
1616 }
1617
1618 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1619 current_inline_box_identifier,
1620 TextRunLineItem {
1621 text: vec![glyph_store],
1622 base_fragment_info: text_run.base_fragment_info,
1623 inline_styles: text_run.inline_styles.clone(),
1624 font_metrics: font_metrics.clone(),
1625 font_key,
1626 bidi_level,
1627 offsets: offsets.map(Box::new),
1628 },
1629 ));
1630 }
1631
1632 fn update_unbreakable_segment_for_new_content(
1633 &mut self,
1634 block_sizes_of_content: &LineBlockSizes,
1635 inline_size: Au,
1636 flags: SegmentContentFlags,
1637 ) {
1638 if flags.is_collapsible_whitespace() || flags.is_wrappable_and_hangable() {
1639 self.current_line_segment.trailing_whitespace_size = inline_size;
1640 } else {
1641 self.current_line_segment.trailing_whitespace_size = Au::zero();
1642 }
1643 if !flags.is_collapsible_whitespace() {
1644 self.current_line_segment.has_content = true;
1645 }
1646
1647 let container_max_block_size = &self
1649 .current_inline_container_state()
1650 .nested_strut_block_sizes
1651 .clone();
1652 self.current_line_segment
1653 .max_block_size
1654 .max_assign(container_max_block_size);
1655 self.current_line_segment
1656 .max_block_size
1657 .max_assign(block_sizes_of_content);
1658
1659 self.current_line_segment.inline_size += inline_size;
1660
1661 *self
1663 .current_inline_container_state()
1664 .has_content
1665 .borrow_mut() = true;
1666 self.propagate_current_nesting_level_white_space_style();
1667 }
1668
1669 fn process_line_break(&mut self, forced_line_break: bool) {
1670 self.current_line_segment.trim_leading_whitespace();
1671 self.finish_current_line_and_reset(forced_line_break);
1672 }
1673
1674 fn unbreakable_segment_fits_on_line(&mut self) -> bool {
1675 let potential_line_size = LogicalVec2 {
1676 inline: self.current_line.inline_position + self.current_line_segment.inline_size -
1677 self.current_line_segment.trailing_whitespace_size,
1678 block: self
1679 .current_line_max_block_size_including_nested_containers()
1680 .max(&self.current_line_segment.max_block_size)
1681 .resolve(),
1682 };
1683
1684 !self.new_potential_line_size_causes_line_break(&potential_line_size)
1685 }
1686
1687 fn process_soft_wrap_opportunity(&mut self) {
1691 if self.current_line_segment.line_items.is_empty() {
1692 return;
1693 }
1694 if self.text_wrap_mode == TextWrapMode::Nowrap {
1695 return;
1696 }
1697 if !self.unbreakable_segment_fits_on_line() {
1698 self.process_line_break(false );
1699 }
1700 self.commit_current_segment_to_line();
1701 }
1702
1703 fn commit_current_segment_to_line(&mut self) {
1706 if self.current_line_segment.line_items.is_empty() && !self.current_line_segment.has_content
1709 {
1710 return;
1711 }
1712
1713 if !self.current_line.has_content {
1714 self.current_line_segment.trim_leading_whitespace();
1715 }
1716
1717 self.current_line.inline_position += self.current_line_segment.inline_size;
1718 self.current_line.max_block_size = self
1719 .current_line_max_block_size_including_nested_containers()
1720 .max(&self.current_line_segment.max_block_size);
1721 let line_inline_size_without_trailing_whitespace =
1722 self.current_line.inline_position - self.current_line_segment.trailing_whitespace_size;
1723
1724 let mut segment_items = mem::take(&mut self.current_line_segment.line_items);
1726 for item in segment_items.iter_mut() {
1727 if let LineItem::Float(_, float_item) = item {
1728 self.place_float_line_item_for_commit_to_line(
1729 float_item,
1730 line_inline_size_without_trailing_whitespace,
1731 );
1732 }
1733 }
1734
1735 if self.current_line.line_items.is_empty() {
1740 let will_break = self.new_potential_line_size_causes_line_break(&LogicalVec2 {
1741 inline: line_inline_size_without_trailing_whitespace,
1742 block: self.current_line_segment.max_block_size.resolve(),
1743 });
1744 assert!(!will_break);
1745 }
1746
1747 self.current_line.line_items.extend(segment_items);
1748 self.current_line.has_content |= self.current_line_segment.has_content;
1749 self.current_line.has_inline_pbm |= self.current_line_segment.has_inline_pbm;
1750
1751 self.current_line_segment.reset();
1752 }
1753
1754 #[inline]
1755 fn containing_block(&self) -> &ContainingBlock<'_> {
1756 self.placement_state.containing_block
1757 }
1758}
1759
1760bitflags! {
1761 struct SegmentContentFlags: u8 {
1762 const COLLAPSIBLE_WHITESPACE = 0b00000001;
1763 const WRAPPABLE_AND_HANGABLE_WHITESPACE = 0b00000010;
1764 }
1765}
1766
1767impl SegmentContentFlags {
1768 fn is_collapsible_whitespace(&self) -> bool {
1769 self.contains(Self::COLLAPSIBLE_WHITESPACE)
1770 }
1771
1772 fn is_wrappable_and_hangable(&self) -> bool {
1773 self.contains(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE)
1774 }
1775}
1776
1777impl From<&InheritedText> for SegmentContentFlags {
1778 fn from(style_text: &InheritedText) -> Self {
1779 let mut flags = Self::empty();
1780
1781 if !matches!(
1784 style_text.white_space_collapse,
1785 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
1786 ) {
1787 flags.insert(Self::COLLAPSIBLE_WHITESPACE);
1788 }
1789
1790 if style_text.text_wrap_mode == TextWrapMode::Wrap &&
1793 style_text.white_space_collapse != WhiteSpaceCollapse::BreakSpaces
1794 {
1795 flags.insert(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE);
1796 }
1797 flags
1798 }
1799}
1800
1801impl InlineFormattingContext {
1802 #[servo_tracing::instrument(name = "InlineFormattingContext::new_with_builder", skip_all)]
1803 fn new_with_builder(
1804 mut builder: InlineFormattingContextBuilder,
1805 layout_context: &LayoutContext,
1806 has_first_formatted_line: bool,
1807 is_single_line_text_input: bool,
1808 starting_bidi_level: Level,
1809 ) -> Self {
1810 let text_content: String = builder.text_segments.into_iter().collect();
1812
1813 let bidi_info = BidiInfo::new(&text_content, Some(starting_bidi_level));
1814 let has_right_to_left_content = bidi_info.has_rtl();
1815
1816 let mut new_linebreaker = LineBreaker::new(text_content.as_str());
1817 for item in &mut builder.inline_items {
1818 match item {
1819 InlineItem::TextRun(text_run) => {
1820 text_run.borrow_mut().segment_and_shape(
1821 &text_content,
1822 layout_context,
1823 &mut new_linebreaker,
1824 &bidi_info,
1825 );
1826 },
1827 InlineItem::StartInlineBox(inline_box) => {
1828 let inline_box = &mut *inline_box.borrow_mut();
1829 if let Some(font) = get_font_for_first_font_for_style(
1830 &inline_box.base.style,
1831 &layout_context.font_context,
1832 ) {
1833 inline_box.default_font = Some(font);
1834 }
1835 },
1836 InlineItem::Atomic(_, index_in_text, bidi_level) => {
1837 *bidi_level = bidi_info.levels[*index_in_text];
1838 },
1839 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) |
1840 InlineItem::OutOfFlowFloatBox(_) |
1841 InlineItem::EndInlineBox |
1842 InlineItem::AnonymousBlock { .. } => {},
1843 }
1844 }
1845
1846 InlineFormattingContext {
1847 text_content,
1848 inline_items: builder.inline_items,
1849 inline_boxes: builder.inline_boxes,
1850 shared_inline_styles: builder
1851 .shared_inline_styles_stack
1852 .last()
1853 .expect("Should have at least one SharedInlineStyle for the root of an IFC")
1854 .clone(),
1855 has_first_formatted_line,
1856 contains_floats: builder.contains_floats,
1857 is_single_line_text_input,
1858 has_right_to_left_content,
1859 shared_selection: builder.shared_selection,
1860 }
1861 }
1862
1863 pub(crate) fn repair_style(
1864 &self,
1865 node: &ServoThreadSafeLayoutNode,
1866 new_style: &ServoArc<ComputedValues>,
1867 ) {
1868 *self.shared_inline_styles.style.borrow_mut() = new_style.clone();
1869 *self.shared_inline_styles.selected.borrow_mut() = node.selected_style();
1870 }
1871
1872 fn inline_start_for_first_line(&self, containing_block: IndefiniteContainingBlock) -> Au {
1873 if !self.has_first_formatted_line {
1874 return Au::zero();
1875 }
1876 containing_block
1877 .style
1878 .get_inherited_text()
1879 .text_indent
1880 .length
1881 .to_used_value(containing_block.size.inline.unwrap_or_default())
1882 }
1883
1884 pub(super) fn layout(
1885 &self,
1886 layout_context: &LayoutContext,
1887 positioning_context: &mut PositioningContext,
1888 containing_block: &ContainingBlock,
1889 sequential_layout_state: Option<&mut SequentialLayoutState>,
1890 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
1891 ) -> CacheableLayoutResult {
1892 for inline_box in self.inline_boxes.iter() {
1894 inline_box.borrow().base.clear_fragments();
1895 }
1896
1897 let style = containing_block.style;
1898
1899 let default_font_metrics =
1902 get_font_for_first_font_for_style(style, &layout_context.font_context)
1903 .map(|font| font.metrics.clone());
1904
1905 let style_text = containing_block.style.get_inherited_text();
1906 let mut inline_container_state_flags = InlineContainerStateFlags::empty();
1907 if inline_container_needs_strut(style, layout_context, None) {
1908 inline_container_state_flags.insert(InlineContainerStateFlags::CREATE_STRUT);
1909 }
1910 if self.is_single_line_text_input {
1911 inline_container_state_flags
1912 .insert(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT);
1913 }
1914 let placement_state =
1915 PlacementState::new(collapsible_with_parent_start_margin, containing_block);
1916
1917 let mut layout = InlineFormattingContextLayout {
1918 positioning_context,
1919 placement_state,
1920 sequential_layout_state,
1921 layout_context,
1922 ifc: self,
1923 fragments: Vec::new(),
1924 current_line: LineUnderConstruction::new(LogicalVec2 {
1925 inline: self.inline_start_for_first_line(containing_block.into()),
1926 block: Au::zero(),
1927 }),
1928 root_nesting_level: InlineContainerState::new(
1929 style.to_arc(),
1930 inline_container_state_flags,
1931 None, default_font_metrics,
1933 ),
1934 inline_box_state_stack: Vec::new(),
1935 inline_box_states: Vec::with_capacity(self.inline_boxes.len()),
1936 current_line_segment: UnbreakableSegmentUnderConstruction::new(),
1937 linebreak_before_new_content: false,
1938 deferred_br_clear: Clear::None,
1939 have_deferred_soft_wrap_opportunity: false,
1940 depends_on_block_constraints: false,
1941 white_space_collapse: style_text.white_space_collapse,
1942 text_wrap_mode: style_text.text_wrap_mode,
1943 };
1944
1945 for item in self.inline_items.iter() {
1946 if !matches!(item, InlineItem::EndInlineBox) {
1948 layout.possibly_flush_deferred_forced_line_break();
1949 }
1950
1951 match item {
1952 InlineItem::StartInlineBox(inline_box) => {
1953 layout.start_inline_box(&inline_box.borrow());
1954 },
1955 InlineItem::EndInlineBox => layout.finish_inline_box(),
1956 InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout),
1957 InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
1958 atomic_formatting_context.borrow().layout_into_line_items(
1959 &mut layout,
1960 *offset_in_text,
1961 *bidi_level,
1962 );
1963 },
1964 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, _) => {
1965 layout.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
1966 layout.current_inline_box_identifier(),
1967 AbsolutelyPositionedLineItem {
1968 absolutely_positioned_box: positioned_box.clone(),
1969 },
1970 ));
1971 },
1972 InlineItem::OutOfFlowFloatBox(float_box) => {
1973 float_box.borrow().layout_into_line_items(&mut layout);
1974 },
1975 InlineItem::AnonymousBlock(block_box) => {
1976 block_box.borrow().layout_into_line_items(&mut layout);
1977 },
1978 }
1979 }
1980
1981 layout.finish_last_line();
1982 let (content_block_size, collapsible_margins_in_children, baselines) =
1983 layout.placement_state.finish();
1984
1985 CacheableLayoutResult {
1986 fragments: layout.fragments,
1987 content_block_size,
1988 collapsible_margins_in_children,
1989 baselines,
1990 depends_on_block_constraints: layout.depends_on_block_constraints,
1991 content_inline_size_for_table: None,
1992 specific_layout_info: None,
1993 }
1994 }
1995
1996 fn next_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
1997 let Some(character) = self.text_content[index..].chars().nth(1) else {
1998 return false;
1999 };
2000 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2001 }
2002
2003 fn previous_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
2004 let Some(character) = self.text_content[0..index].chars().next_back() else {
2005 return false;
2006 };
2007 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2008 }
2009
2010 pub(crate) fn find_block_margin_collapsing_with_parent(
2011 &self,
2012 layout_context: &LayoutContext,
2013 collected_margin: &mut CollapsedMargin,
2014 containing_block_for_children: &ContainingBlock,
2015 ) -> bool {
2016 let mut nesting_levels_from_nonzero_end_pbm: u32 = 1;
2022 let mut items_iter = self.inline_items.iter();
2023 items_iter.all(|inline_item| match inline_item {
2024 InlineItem::StartInlineBox(inline_box) => {
2025 let pbm = inline_box
2026 .borrow()
2027 .layout_style()
2028 .padding_border_margin(containing_block_for_children);
2029 if pbm.padding.inline_end.is_zero() &&
2030 pbm.border.inline_end.is_zero() &&
2031 pbm.margin.inline_end.auto_is(Au::zero).is_zero()
2032 {
2033 nesting_levels_from_nonzero_end_pbm += 1;
2034 } else {
2035 nesting_levels_from_nonzero_end_pbm = 0;
2036 }
2037 pbm.padding.inline_start.is_zero() &&
2038 pbm.border.inline_start.is_zero() &&
2039 pbm.margin.inline_start.auto_is(Au::zero).is_zero()
2040 },
2041 InlineItem::EndInlineBox => {
2042 if nesting_levels_from_nonzero_end_pbm == 0 {
2043 false
2044 } else {
2045 nesting_levels_from_nonzero_end_pbm -= 1;
2046 true
2047 }
2048 },
2049 InlineItem::TextRun(text_run) => {
2050 let text_run = &*text_run.borrow();
2051 let parent_style = text_run.inline_styles.style.borrow();
2052 text_run.shaped_text.iter().all(|segment| {
2053 segment.runs.iter().all(|run| {
2054 run.is_whitespace() &&
2055 !run.is_single_preserved_newline() &&
2056 !matches!(
2057 parent_style.get_inherited_text().white_space_collapse,
2058 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
2059 )
2060 })
2061 })
2062 },
2063 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => true,
2064 InlineItem::OutOfFlowFloatBox(..) => true,
2065 InlineItem::Atomic(..) => false,
2066 InlineItem::AnonymousBlock(block_box) => block_box
2067 .borrow()
2068 .contents
2069 .find_block_margin_collapsing_with_parent(
2070 layout_context,
2071 collected_margin,
2072 containing_block_for_children,
2073 ),
2074 })
2075 }
2076
2077 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
2078 let mut parent_box_stack = Vec::new();
2079 let current_parent_box = |parent_box_stack: &[WeakLayoutBox]| {
2080 parent_box_stack.last().unwrap_or(&layout_box).clone()
2081 };
2082 for inline_item in &self.inline_items {
2083 match inline_item {
2084 InlineItem::StartInlineBox(inline_box) => {
2085 inline_box
2086 .borrow_mut()
2087 .base
2088 .parent_box
2089 .replace(current_parent_box(&parent_box_stack));
2090 parent_box_stack.push(WeakLayoutBox::InlineLevel(
2091 WeakInlineItem::StartInlineBox(inline_box.downgrade()),
2092 ));
2093 },
2094 InlineItem::EndInlineBox => {
2095 parent_box_stack.pop();
2096 },
2097 InlineItem::TextRun(text_run) => {
2098 text_run
2099 .borrow_mut()
2100 .parent_box
2101 .replace(current_parent_box(&parent_box_stack));
2102 },
2103 _ => inline_item.with_base_mut(|base| {
2104 base.parent_box
2105 .replace(current_parent_box(&parent_box_stack));
2106 }),
2107 }
2108 }
2109 }
2110}
2111
2112impl InlineContainerState {
2113 fn new(
2114 style: ServoArc<ComputedValues>,
2115 flags: InlineContainerStateFlags,
2116 parent_container: Option<&InlineContainerState>,
2117 font_metrics: Option<Arc<FontMetrics>>,
2118 ) -> Self {
2119 let font_metrics = font_metrics.unwrap_or_else(FontMetrics::empty);
2120 let mut baseline_offset = Au::zero();
2121 let mut strut_block_sizes = Self::get_block_sizes_with_style(
2122 effective_vertical_align(&style, parent_container),
2123 &style,
2124 &font_metrics,
2125 &font_metrics,
2126 &flags,
2127 );
2128 if let Some(parent_container) = parent_container {
2129 baseline_offset = parent_container.get_cumulative_baseline_offset_for_child(
2132 style.clone_vertical_align(),
2133 &strut_block_sizes,
2134 );
2135 strut_block_sizes.adjust_for_baseline_offset(baseline_offset);
2136 }
2137
2138 let mut nested_block_sizes = parent_container
2139 .map(|container| container.nested_strut_block_sizes.clone())
2140 .unwrap_or_else(LineBlockSizes::zero);
2141 if flags.contains(InlineContainerStateFlags::CREATE_STRUT) {
2142 nested_block_sizes.max_assign(&strut_block_sizes);
2143 }
2144
2145 Self {
2146 style,
2147 flags,
2148 has_content: RefCell::new(false),
2149 nested_strut_block_sizes: nested_block_sizes,
2150 strut_block_sizes,
2151 baseline_offset,
2152 font_metrics,
2153 }
2154 }
2155
2156 fn get_block_sizes_with_style(
2157 vertical_align: VerticalAlign,
2158 style: &ComputedValues,
2159 font_metrics: &FontMetrics,
2160 font_metrics_of_first_font: &FontMetrics,
2161 flags: &InlineContainerStateFlags,
2162 ) -> LineBlockSizes {
2163 let line_height = line_height(style, font_metrics, flags);
2164
2165 if !is_baseline_relative(vertical_align) {
2166 return LineBlockSizes {
2167 line_height,
2168 baseline_relative_size_for_line_height: None,
2169 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2170 };
2171 }
2172
2173 let mut ascent = font_metrics.ascent;
2182 let mut descent = font_metrics.descent;
2183 if style.get_font().line_height == LineHeight::Normal {
2184 let half_leading_from_line_gap =
2185 (font_metrics.line_gap - descent - ascent).scale_by(0.5);
2186 ascent += half_leading_from_line_gap;
2187 descent += half_leading_from_line_gap;
2188 }
2189
2190 let size_for_baseline_positioning = BaselineRelativeSize { ascent, descent };
2194
2195 if style.get_font().line_height != LineHeight::Normal {
2211 ascent = font_metrics_of_first_font.ascent;
2212 descent = font_metrics_of_first_font.descent;
2213 let half_leading = (line_height - (ascent + descent)).scale_by(0.5);
2214 ascent += half_leading;
2219 descent = line_height - ascent;
2220 }
2221
2222 LineBlockSizes {
2223 line_height,
2224 baseline_relative_size_for_line_height: Some(BaselineRelativeSize { ascent, descent }),
2225 size_for_baseline_positioning,
2226 }
2227 }
2228
2229 fn get_block_size_contribution(
2230 &self,
2231 vertical_align: VerticalAlign,
2232 font_metrics: &FontMetrics,
2233 font_metrics_of_first_font: &FontMetrics,
2234 ) -> LineBlockSizes {
2235 Self::get_block_sizes_with_style(
2236 vertical_align,
2237 &self.style,
2238 font_metrics,
2239 font_metrics_of_first_font,
2240 &self.flags,
2241 )
2242 }
2243
2244 fn get_cumulative_baseline_offset_for_child(
2245 &self,
2246 child_vertical_align: VerticalAlign,
2247 child_block_size: &LineBlockSizes,
2248 ) -> Au {
2249 let block_size = self.get_block_size_contribution(
2250 child_vertical_align.clone(),
2251 &self.font_metrics,
2252 &self.font_metrics,
2253 );
2254 self.baseline_offset +
2255 match child_vertical_align {
2256 VerticalAlign::Keyword(VerticalAlignKeyword::Baseline) |
2261 VerticalAlign::Keyword(VerticalAlignKeyword::Top) |
2262 VerticalAlign::Keyword(VerticalAlignKeyword::Bottom) => Au::zero(),
2263 VerticalAlign::Keyword(VerticalAlignKeyword::Sub) => {
2264 block_size.resolve().scale_by(FONT_SUBSCRIPT_OFFSET_RATIO)
2265 },
2266 VerticalAlign::Keyword(VerticalAlignKeyword::Super) => {
2267 -block_size.resolve().scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO)
2268 },
2269 VerticalAlign::Keyword(VerticalAlignKeyword::TextTop) => {
2270 child_block_size.size_for_baseline_positioning.ascent - self.font_metrics.ascent
2271 },
2272 VerticalAlign::Keyword(VerticalAlignKeyword::Middle) => {
2273 (child_block_size.size_for_baseline_positioning.ascent -
2276 child_block_size.size_for_baseline_positioning.descent -
2277 self.font_metrics.x_height)
2278 .scale_by(0.5)
2279 },
2280 VerticalAlign::Keyword(VerticalAlignKeyword::TextBottom) => {
2281 self.font_metrics.descent -
2282 child_block_size.size_for_baseline_positioning.descent
2283 },
2284 VerticalAlign::Length(length_percentage) => {
2285 -length_percentage.to_used_value(child_block_size.line_height)
2286 },
2287 }
2288 }
2289}
2290
2291impl IndependentFormattingContext {
2292 fn layout_into_line_items(
2293 &self,
2294 layout: &mut InlineFormattingContextLayout,
2295 offset_in_text: usize,
2296 bidi_level: Level,
2297 ) {
2298 let mut child_positioning_context = PositioningContext::default();
2300 let IndependentFloatOrAtomicLayoutResult {
2301 mut fragment,
2302 baselines,
2303 pbm_sums,
2304 } = self.layout_float_or_atomic_inline(
2305 layout.layout_context,
2306 &mut child_positioning_context,
2307 layout.containing_block(),
2308 );
2309
2310 layout.depends_on_block_constraints |= fragment.base.flags.contains(
2313 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2314 );
2315
2316 let container_writing_mode = layout.containing_block().style.writing_mode;
2318 let pbm_physical_offset = pbm_sums
2319 .start_offset()
2320 .to_physical_size(container_writing_mode);
2321 fragment.base.rect.origin += pbm_physical_offset.to_vector();
2322
2323 fragment = fragment.with_baselines(baselines);
2325
2326 let positioning_context = if self.is_replaced() {
2329 None
2330 } else {
2331 if fragment
2332 .style()
2333 .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
2334 {
2335 child_positioning_context
2336 .layout_collected_children(layout.layout_context, &mut fragment);
2337 }
2338 Some(child_positioning_context)
2339 };
2340
2341 if layout.text_wrap_mode == TextWrapMode::Wrap &&
2342 !layout
2343 .ifc
2344 .previous_character_prevents_soft_wrap_opportunity(offset_in_text)
2345 {
2346 layout.process_soft_wrap_opportunity();
2347 }
2348
2349 let size = pbm_sums.sum() + fragment.base.rect.size.to_logical(container_writing_mode);
2350 let baseline_offset = self
2351 .pick_baseline(&fragment.baselines(container_writing_mode))
2352 .map(|baseline| pbm_sums.block_start + baseline)
2353 .unwrap_or(size.block);
2354
2355 let (block_sizes, baseline_offset_in_parent) =
2356 self.get_block_sizes_and_baseline_offset(layout, size.block, baseline_offset);
2357 layout.update_unbreakable_segment_for_new_content(
2358 &block_sizes,
2359 size.inline,
2360 SegmentContentFlags::empty(),
2361 );
2362
2363 let fragment = ArcRefCell::new(fragment);
2364 self.base.set_fragment(Fragment::Box(fragment.clone()));
2365
2366 layout.push_line_item_to_unbreakable_segment(LineItem::Atomic(
2367 layout.current_inline_box_identifier(),
2368 AtomicLineItem {
2369 fragment,
2370 size,
2371 positioning_context,
2372 baseline_offset_in_parent,
2373 baseline_offset_in_item: baseline_offset,
2374 bidi_level,
2375 },
2376 ));
2377
2378 if !layout
2381 .ifc
2382 .next_character_prevents_soft_wrap_opportunity(offset_in_text)
2383 {
2384 layout.have_deferred_soft_wrap_opportunity = true;
2385 }
2386 }
2387
2388 fn pick_baseline(&self, baselines: &Baselines) -> Option<Au> {
2392 match self.style().clone_baseline_source() {
2393 BaselineSource::First => baselines.first,
2394 BaselineSource::Last => baselines.last,
2395 BaselineSource::Auto if self.is_block_container() => baselines.last,
2396 BaselineSource::Auto => baselines.first,
2397 }
2398 }
2399
2400 fn get_block_sizes_and_baseline_offset(
2401 &self,
2402 ifc: &InlineFormattingContextLayout,
2403 block_size: Au,
2404 baseline_offset_in_content_area: Au,
2405 ) -> (LineBlockSizes, Au) {
2406 let mut contribution = if !is_baseline_relative(self.style().clone_vertical_align()) {
2407 LineBlockSizes {
2408 line_height: block_size,
2409 baseline_relative_size_for_line_height: None,
2410 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2411 }
2412 } else {
2413 let baseline_relative_size = BaselineRelativeSize {
2414 ascent: baseline_offset_in_content_area,
2415 descent: block_size - baseline_offset_in_content_area,
2416 };
2417 LineBlockSizes {
2418 line_height: block_size,
2419 baseline_relative_size_for_line_height: Some(baseline_relative_size.clone()),
2420 size_for_baseline_positioning: baseline_relative_size,
2421 }
2422 };
2423
2424 let baseline_offset = ifc
2425 .current_inline_container_state()
2426 .get_cumulative_baseline_offset_for_child(
2427 self.style().clone_vertical_align(),
2428 &contribution,
2429 );
2430 contribution.adjust_for_baseline_offset(baseline_offset);
2431
2432 (contribution, baseline_offset)
2433 }
2434}
2435
2436impl FloatBox {
2437 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
2438 let fragment = ArcRefCell::new(self.layout(
2439 layout.layout_context,
2440 layout.positioning_context,
2441 layout.placement_state.containing_block,
2442 ));
2443
2444 self.contents
2445 .base
2446 .set_fragment(Fragment::Box(fragment.clone()));
2447 layout.push_line_item_to_unbreakable_segment(LineItem::Float(
2448 layout.current_inline_box_identifier(),
2449 FloatLineItem {
2450 fragment,
2451 needs_placement: true,
2452 },
2453 ));
2454 }
2455}
2456
2457fn place_pending_floats(ifc: &mut InlineFormattingContextLayout, line_items: &mut [LineItem]) {
2458 for item in line_items.iter_mut() {
2459 if let LineItem::Float(_, float_line_item) = item {
2460 if float_line_item.needs_placement {
2461 ifc.place_float_fragment(&mut float_line_item.fragment.borrow_mut());
2462 }
2463 }
2464 }
2465}
2466
2467fn line_height(
2468 parent_style: &ComputedValues,
2469 font_metrics: &FontMetrics,
2470 flags: &InlineContainerStateFlags,
2471) -> Au {
2472 let font = parent_style.get_font();
2473 let font_size = font.font_size.computed_size();
2474 let mut line_height = match font.line_height {
2475 LineHeight::Normal => font_metrics.line_gap,
2476 LineHeight::Number(number) => (font_size * number.0).into(),
2477 LineHeight::Length(length) => length.0.into(),
2478 };
2479
2480 if flags.contains(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT) {
2484 line_height.max_assign(font_metrics.line_gap);
2485 }
2486
2487 line_height
2488}
2489
2490fn effective_vertical_align(
2491 style: &ComputedValues,
2492 container: Option<&InlineContainerState>,
2493) -> VerticalAlign {
2494 if container.is_none() {
2495 VerticalAlign::Keyword(VerticalAlignKeyword::Baseline)
2499 } else {
2500 style.clone_vertical_align()
2501 }
2502}
2503
2504fn is_baseline_relative(vertical_align: VerticalAlign) -> bool {
2505 !matches!(
2506 vertical_align,
2507 VerticalAlign::Keyword(VerticalAlignKeyword::Top) |
2508 VerticalAlign::Keyword(VerticalAlignKeyword::Bottom)
2509 )
2510}
2511
2512fn inline_container_needs_strut(
2538 style: &ComputedValues,
2539 layout_context: &LayoutContext,
2540 pbm: Option<&PaddingBorderMargin>,
2541) -> bool {
2542 if layout_context.style_context.quirks_mode() == QuirksMode::NoQuirks {
2543 return true;
2544 }
2545
2546 if style.get_box().display.is_list_item() {
2549 return true;
2550 }
2551
2552 pbm.is_some_and(|pbm| !pbm.padding_border_sums.inline.is_zero())
2553}
2554
2555impl ComputeInlineContentSizes for InlineFormattingContext {
2556 fn compute_inline_content_sizes(
2560 &self,
2561 layout_context: &LayoutContext,
2562 constraint_space: &ConstraintSpace,
2563 ) -> InlineContentSizesResult {
2564 ContentSizesComputation::compute(self, layout_context, constraint_space)
2565 }
2566}
2567
2568struct ContentSizesComputation<'layout_data> {
2570 layout_context: &'layout_data LayoutContext<'layout_data>,
2571 constraint_space: &'layout_data ConstraintSpace<'layout_data>,
2572 paragraph: ContentSizes,
2573 current_line: ContentSizes,
2574 pending_whitespace: ContentSizes,
2576 uncleared_floats: LogicalSides1D<ContentSizes>,
2578 cleared_floats: LogicalSides1D<ContentSizes>,
2580 had_content_yet_for_min_content: bool,
2583 had_content_yet_for_max_content: bool,
2586 ending_inline_pbm_stack: Vec<Au>,
2589 depends_on_block_constraints: bool,
2590}
2591
2592impl<'layout_data> ContentSizesComputation<'layout_data> {
2593 fn traverse(
2594 mut self,
2595 inline_formatting_context: &InlineFormattingContext,
2596 ) -> InlineContentSizesResult {
2597 self.add_inline_size(
2598 inline_formatting_context.inline_start_for_first_line(self.constraint_space.into()),
2599 );
2600 for inline_item in &inline_formatting_context.inline_items {
2601 self.process_item(inline_item, inline_formatting_context);
2602 }
2603 self.forced_line_break();
2604 self.flush_floats();
2605
2606 InlineContentSizesResult {
2607 sizes: self.paragraph,
2608 depends_on_block_constraints: self.depends_on_block_constraints,
2609 }
2610 }
2611
2612 fn process_item(
2613 &mut self,
2614 inline_item: &InlineItem,
2615 inline_formatting_context: &InlineFormattingContext,
2616 ) {
2617 match inline_item {
2618 InlineItem::StartInlineBox(inline_box) => {
2619 let inline_box = inline_box.borrow();
2623 let zero = Au::zero();
2624 let writing_mode = self.constraint_space.style.writing_mode;
2625 let layout_style = inline_box.layout_style();
2626 let padding = layout_style
2627 .padding(writing_mode)
2628 .percentages_relative_to(zero);
2629 let border = layout_style.border_width(writing_mode);
2630 let margin = inline_box
2631 .base
2632 .style
2633 .margin(writing_mode)
2634 .percentages_relative_to(zero)
2635 .auto_is(Au::zero);
2636
2637 let pbm = margin + padding + border;
2638 self.add_inline_size(pbm.inline_start);
2639 self.ending_inline_pbm_stack.push(pbm.inline_end);
2640 },
2641 InlineItem::EndInlineBox => {
2642 let length = self.ending_inline_pbm_stack.pop().unwrap_or_else(Au::zero);
2643 self.add_inline_size(length);
2644 },
2645 InlineItem::TextRun(text_run) => {
2646 let text_run = &*text_run.borrow();
2647 let parent_style = text_run.inline_styles.style.borrow();
2648 for segment in text_run.shaped_text.iter() {
2649 let style_text = parent_style.get_inherited_text();
2650 let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap;
2651
2652 let break_at_start =
2655 segment.break_at_start && self.had_content_yet_for_min_content;
2656
2657 for (run_index, run) in segment.runs.iter().enumerate() {
2658 if can_wrap && (run_index != 0 || break_at_start) {
2661 self.line_break_opportunity();
2662 }
2663
2664 let advance = run.total_advance();
2665 if run.is_whitespace() {
2666 if run.is_single_preserved_newline() {
2669 self.forced_line_break();
2670 continue;
2671 }
2672 if !matches!(
2673 style_text.white_space_collapse,
2674 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
2675 ) {
2676 if self.had_content_yet_for_min_content {
2677 if can_wrap {
2678 self.line_break_opportunity();
2679 } else {
2680 self.pending_whitespace.min_content += advance;
2681 }
2682 }
2683 if self.had_content_yet_for_max_content {
2684 self.pending_whitespace.max_content += advance;
2685 }
2686 continue;
2687 }
2688 if can_wrap {
2689 self.pending_whitespace.max_content += advance;
2690 self.commit_pending_whitespace();
2691 self.line_break_opportunity();
2692 continue;
2693 }
2694 }
2695
2696 self.commit_pending_whitespace();
2697 self.add_inline_size(advance);
2698
2699 if can_wrap && run.ends_with_whitespace() {
2704 self.line_break_opportunity();
2705 }
2706 }
2707 }
2708 },
2709 InlineItem::Atomic(atomic, offset_in_text, _level) => {
2710 if self.had_content_yet_for_min_content &&
2712 !inline_formatting_context
2713 .previous_character_prevents_soft_wrap_opportunity(*offset_in_text)
2714 {
2715 self.line_break_opportunity();
2716 }
2717
2718 self.commit_pending_whitespace();
2719 let outer = self.outer_inline_content_sizes_of_float_or_atomic(&atomic.borrow());
2720 self.current_line += outer;
2721
2722 if !inline_formatting_context
2724 .next_character_prevents_soft_wrap_opportunity(*offset_in_text)
2725 {
2726 self.line_break_opportunity();
2727 }
2728 },
2729 InlineItem::OutOfFlowFloatBox(float_box) => {
2730 let float_box = float_box.borrow();
2731 let sizes = self.outer_inline_content_sizes_of_float_or_atomic(&float_box.contents);
2732 let style = &float_box.contents.style();
2733 let container_writing_mode = self.constraint_space.style.writing_mode;
2734 let clear =
2735 Clear::from_style_and_container_writing_mode(style, container_writing_mode);
2736 self.clear_floats(clear);
2737 let float_side =
2738 FloatSide::from_style_and_container_writing_mode(style, container_writing_mode);
2739 match float_side.expect("A float box needs to float to some side") {
2740 FloatSide::InlineStart => self.uncleared_floats.start.union_assign(&sizes),
2741 FloatSide::InlineEnd => self.uncleared_floats.end.union_assign(&sizes),
2742 }
2743 },
2744 InlineItem::AnonymousBlock(block) => {
2745 self.forced_line_break();
2746 self.flush_floats();
2747 let borrowed_block = block.borrow();
2748 let AnonymousBlockBox {
2749 ref base,
2750 ref contents,
2751 ..
2752 } = *borrowed_block;
2753 let inline_content_sizes_result = outer_inline(
2754 base,
2755 &contents.layout_style(base),
2756 &self.constraint_space.into(),
2757 &LogicalVec2::zero(),
2758 false, false, false, |_| None, |constraint_space| {
2763 base.inline_content_sizes(self.layout_context, constraint_space, contents)
2764 },
2765 |_aspect_ratio| None,
2766 );
2767 self.depends_on_block_constraints |=
2768 inline_content_sizes_result.depends_on_block_constraints;
2769 self.current_line = inline_content_sizes_result.sizes;
2770 self.forced_line_break();
2771 },
2772 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => {},
2773 }
2774 }
2775
2776 fn add_inline_size(&mut self, l: Au) {
2777 self.current_line.min_content += l;
2778 self.current_line.max_content += l;
2779 }
2780
2781 fn line_break_opportunity(&mut self) {
2782 self.pending_whitespace.min_content = Au::zero();
2786 let current_min_content = mem::take(&mut self.current_line.min_content);
2787 self.paragraph.min_content.max_assign(current_min_content);
2788 self.had_content_yet_for_min_content = false;
2789 }
2790
2791 fn forced_line_break(&mut self) {
2792 self.line_break_opportunity();
2794
2795 self.pending_whitespace.max_content = Au::zero();
2797 let current_max_content = mem::take(&mut self.current_line.max_content);
2798 self.paragraph.max_content.max_assign(current_max_content);
2799 self.had_content_yet_for_max_content = false;
2800 }
2801
2802 fn commit_pending_whitespace(&mut self) {
2803 self.current_line += mem::take(&mut self.pending_whitespace);
2804 self.had_content_yet_for_min_content = true;
2805 self.had_content_yet_for_max_content = true;
2806 }
2807
2808 fn outer_inline_content_sizes_of_float_or_atomic(
2809 &mut self,
2810 context: &IndependentFormattingContext,
2811 ) -> ContentSizes {
2812 let result = context.outer_inline_content_sizes(
2813 self.layout_context,
2814 &self.constraint_space.into(),
2815 &LogicalVec2::zero(),
2816 false, );
2818 self.depends_on_block_constraints |= result.depends_on_block_constraints;
2819 result.sizes
2820 }
2821
2822 fn clear_floats(&mut self, clear: Clear) {
2823 match clear {
2824 Clear::InlineStart => {
2825 let start_floats = mem::take(&mut self.uncleared_floats.start);
2826 self.cleared_floats.start.max_assign(start_floats);
2827 },
2828 Clear::InlineEnd => {
2829 let end_floats = mem::take(&mut self.uncleared_floats.end);
2830 self.cleared_floats.end.max_assign(end_floats);
2831 },
2832 Clear::Both => {
2833 let start_floats = mem::take(&mut self.uncleared_floats.start);
2834 let end_floats = mem::take(&mut self.uncleared_floats.end);
2835 self.cleared_floats.start.max_assign(start_floats);
2836 self.cleared_floats.end.max_assign(end_floats);
2837 },
2838 Clear::None => {},
2839 }
2840 }
2841
2842 fn flush_floats(&mut self) {
2843 self.clear_floats(Clear::Both);
2844 let start_floats = mem::take(&mut self.cleared_floats.start);
2845 let end_floats = mem::take(&mut self.cleared_floats.end);
2846 self.paragraph.union_assign(&start_floats);
2847 self.paragraph.union_assign(&end_floats);
2848 }
2849
2850 fn compute(
2852 inline_formatting_context: &InlineFormattingContext,
2853 layout_context: &'layout_data LayoutContext,
2854 constraint_space: &'layout_data ConstraintSpace,
2855 ) -> InlineContentSizesResult {
2856 Self {
2857 layout_context,
2858 constraint_space,
2859 paragraph: ContentSizes::zero(),
2860 current_line: ContentSizes::zero(),
2861 pending_whitespace: ContentSizes::zero(),
2862 uncleared_floats: LogicalSides1D::default(),
2863 cleared_floats: LogicalSides1D::default(),
2864 had_content_yet_for_min_content: false,
2865 had_content_yet_for_max_content: false,
2866 ending_inline_pbm_stack: Vec::new(),
2867 depends_on_block_constraints: false,
2868 }
2869 .traverse(inline_formatting_context)
2870 }
2871}
2872
2873fn char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character: char) -> bool {
2885 if character == '\u{00A0}' {
2886 return false;
2887 }
2888 let class = linebreak_property(character);
2889 class == XI_LINE_BREAKING_CLASS_GL ||
2890 class == XI_LINE_BREAKING_CLASS_WJ ||
2891 class == XI_LINE_BREAKING_CLASS_ZWJ
2892}