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