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;
80
81use app_units::{Au, MAX_AU};
82use base::id::RenderingGroupId;
83use bitflags::bitflags;
84use construct::InlineFormattingContextBuilder;
85use fonts::{ByteIndex, FontMetrics, GlyphStore};
86use inline_box::{InlineBox, InlineBoxContainerState, InlineBoxIdentifier, InlineBoxes};
87use line::{
88 AbsolutelyPositionedLineItem, AtomicLineItem, FloatLineItem, LineItem, LineItemLayout,
89 TextRunLineItem,
90};
91use line_breaker::LineBreaker;
92use malloc_size_of_derive::MallocSizeOf;
93use range::Range;
94use script::layout_dom::ServoThreadSafeLayoutNode;
95use servo_arc::Arc;
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 add_or_get_font, get_font_for_first_font_for_style,
111};
112use unicode_bidi::{BidiInfo, Level};
113use webrender_api::FontInstanceKey;
114use xi_unicode::linebreak_property;
115
116use super::float::{Clear, PlacementAmongFloats};
117use super::{CacheableLayoutResult, IndependentFloatOrAtomicLayoutResult};
118use crate::cell::ArcRefCell;
119use crate::context::LayoutContext;
120use crate::dom_traversal::NodeAndStyleInfo;
121use crate::flow::CollapsibleWithParentStartMargin;
122use crate::flow::float::{FloatBox, SequentialLayoutState};
123use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
124use crate::fragment_tree::{
125 BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, FragmentFlags,
126 PositioningFragment,
127};
128use crate::geom::{LogicalRect, LogicalVec2, ToLogical};
129use crate::layout_box_base::LayoutBoxBase;
130use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
131use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
132use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
133use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SharedStyle};
134
135static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
137static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
138
139#[derive(Debug, MallocSizeOf)]
140pub(crate) struct InlineFormattingContext {
141 pub(super) inline_items: Vec<ArcRefCell<InlineItem>>,
146
147 pub(super) inline_boxes: InlineBoxes,
150
151 pub(super) text_content: String,
153
154 pub font_metrics: Vec<FontKeyAndMetrics>,
157
158 pub(super) shared_inline_styles: SharedInlineStyles,
161
162 pub(super) has_first_formatted_line: bool,
165
166 pub(super) contains_floats: bool,
168
169 pub(super) is_single_line_text_input: bool,
172
173 pub(super) has_right_to_left_content: bool,
176}
177
178#[derive(Clone, Debug, MallocSizeOf)]
183pub(crate) struct SharedInlineStyles {
184 pub style: SharedStyle,
185 pub selected: SharedStyle,
186}
187
188impl From<&NodeAndStyleInfo<'_>> for SharedInlineStyles {
189 fn from(info: &NodeAndStyleInfo) -> Self {
190 Self {
191 style: SharedStyle::new(info.style.clone()),
192 selected: SharedStyle::new(info.node.selected_style()),
193 }
194 }
195}
196
197#[derive(Debug, MallocSizeOf)]
199pub(crate) struct FontKeyAndMetrics {
200 pub key: FontInstanceKey,
201 pub pt_size: Au,
202 pub metrics: FontMetrics,
203}
204
205#[derive(Debug, MallocSizeOf)]
206pub(crate) enum InlineItem {
207 StartInlineBox(ArcRefCell<InlineBox>),
208 EndInlineBox,
209 TextRun(ArcRefCell<TextRun>),
210 OutOfFlowAbsolutelyPositionedBox(
211 ArcRefCell<AbsolutelyPositionedBox>,
212 usize, ),
214 OutOfFlowFloatBox(ArcRefCell<FloatBox>),
215 Atomic(
216 ArcRefCell<IndependentFormattingContext>,
217 usize, Level, ),
220}
221
222impl InlineItem {
223 pub(crate) fn repair_style(
224 &self,
225 context: &SharedStyleContext,
226 node: &ServoThreadSafeLayoutNode,
227 new_style: &Arc<ComputedValues>,
228 ) {
229 match self {
230 InlineItem::StartInlineBox(inline_box) => {
231 inline_box.borrow_mut().repair_style(node, new_style);
232 },
233 InlineItem::EndInlineBox => {},
234 InlineItem::TextRun(..) => {},
237 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box
238 .borrow_mut()
239 .context
240 .repair_style(context, node, new_style),
241 InlineItem::OutOfFlowFloatBox(float_box) => float_box
242 .borrow_mut()
243 .contents
244 .repair_style(context, node, new_style),
245 InlineItem::Atomic(atomic, ..) => {
246 atomic.borrow_mut().repair_style(context, node, new_style)
247 },
248 }
249 }
250
251 pub(crate) fn clear_fragment_layout_cache(&self) {
252 match self {
253 InlineItem::StartInlineBox(inline_box) => {
254 inline_box.borrow().base.clear_fragment_layout_cache()
255 },
256 InlineItem::EndInlineBox | InlineItem::TextRun(..) => {},
257 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
258 positioned_box
259 .borrow()
260 .context
261 .base
262 .clear_fragment_layout_cache();
263 },
264 InlineItem::OutOfFlowFloatBox(float_box) => float_box
265 .borrow()
266 .contents
267 .base
268 .clear_fragment_layout_cache(),
269 InlineItem::Atomic(independent_formatting_context, ..) => {
270 independent_formatting_context
271 .borrow()
272 .base
273 .clear_fragment_layout_cache()
274 },
275 }
276 }
277
278 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
279 match self {
280 InlineItem::StartInlineBox(inline_box) => callback(&inline_box.borrow().base),
281 InlineItem::EndInlineBox | InlineItem::TextRun(..) => {
282 unreachable!("Should never have these kind of fragments attached to a DOM node")
283 },
284 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
285 callback(&positioned_box.borrow().context.base)
286 },
287 InlineItem::OutOfFlowFloatBox(float_box) => callback(&float_box.borrow().contents.base),
288 InlineItem::Atomic(independent_formatting_context, ..) => {
289 callback(&independent_formatting_context.borrow().base)
290 },
291 }
292 }
293
294 pub(crate) fn with_base_mut<T>(&mut self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
295 match self {
296 InlineItem::StartInlineBox(inline_box) => callback(&mut inline_box.borrow_mut().base),
297 InlineItem::EndInlineBox | InlineItem::TextRun(..) => {
298 unreachable!("Should never have these kind of fragments attached to a DOM node")
299 },
300 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
301 callback(&mut positioned_box.borrow_mut().context.base)
302 },
303 InlineItem::OutOfFlowFloatBox(float_box) => {
304 callback(&mut float_box.borrow_mut().contents.base)
305 },
306 InlineItem::Atomic(independent_formatting_context, ..) => {
307 callback(&mut independent_formatting_context.borrow_mut().base)
308 },
309 }
310 }
311}
312
313struct LineUnderConstruction {
320 start_position: LogicalVec2<Au>,
323
324 inline_position: Au,
327
328 max_block_size: LineBlockSizes,
332
333 has_content: bool,
336
337 has_inline_pbm: bool,
340
341 has_floats_waiting_to_be_placed: bool,
345
346 placement_among_floats: OnceCell<LogicalRect<Au>>,
351
352 line_items: Vec<LineItem>,
355}
356
357impl LineUnderConstruction {
358 fn new(start_position: LogicalVec2<Au>) -> Self {
359 Self {
360 inline_position: start_position.inline,
361 start_position,
362 max_block_size: LineBlockSizes::zero(),
363 has_content: false,
364 has_inline_pbm: false,
365 has_floats_waiting_to_be_placed: false,
366 placement_among_floats: OnceCell::new(),
367 line_items: Vec::new(),
368 }
369 }
370
371 fn line_block_start_considering_placement_among_floats(&self) -> Au {
372 match self.placement_among_floats.get() {
373 Some(placement_among_floats) => placement_among_floats.start_corner.block,
374 None => self.start_position.block,
375 }
376 }
377
378 fn replace_placement_among_floats(&mut self, new_placement: LogicalRect<Au>) {
379 self.placement_among_floats.take();
380 let _ = self.placement_among_floats.set(new_placement);
381 }
382
383 fn trim_trailing_whitespace(&mut self) -> Au {
385 let mut whitespace_trimmed = Au::zero();
390 for item in self.line_items.iter_mut().rev() {
391 if !item.trim_whitespace_at_end(&mut whitespace_trimmed) {
392 break;
393 }
394 }
395
396 whitespace_trimmed
397 }
398
399 fn count_justification_opportunities(&self) -> usize {
401 self.line_items
402 .iter()
403 .filter_map(|item| match item {
404 LineItem::TextRun(_, text_run) => Some(
405 text_run
406 .text
407 .iter()
408 .map(|glyph_store| glyph_store.total_word_separators())
409 .sum::<usize>(),
410 ),
411 _ => None,
412 })
413 .sum()
414 }
415}
416
417#[derive(Clone, Debug)]
423struct BaselineRelativeSize {
424 ascent: Au,
428
429 descent: Au,
433}
434
435impl BaselineRelativeSize {
436 fn zero() -> Self {
437 Self {
438 ascent: Au::zero(),
439 descent: Au::zero(),
440 }
441 }
442
443 fn max(&self, other: &Self) -> Self {
444 BaselineRelativeSize {
445 ascent: self.ascent.max(other.ascent),
446 descent: self.descent.max(other.descent),
447 }
448 }
449
450 fn adjust_for_nested_baseline_offset(&mut self, baseline_offset: Au) {
464 self.ascent -= baseline_offset;
465 self.descent += baseline_offset;
466 }
467}
468
469#[derive(Clone, Debug)]
470struct LineBlockSizes {
471 line_height: Au,
472 baseline_relative_size_for_line_height: Option<BaselineRelativeSize>,
473 size_for_baseline_positioning: BaselineRelativeSize,
474}
475
476impl LineBlockSizes {
477 fn zero() -> Self {
478 LineBlockSizes {
479 line_height: Au::zero(),
480 baseline_relative_size_for_line_height: None,
481 size_for_baseline_positioning: BaselineRelativeSize::zero(),
482 }
483 }
484
485 fn resolve(&self) -> Au {
486 let height_from_ascent_and_descent = self
487 .baseline_relative_size_for_line_height
488 .as_ref()
489 .map(|size| (size.ascent + size.descent).abs())
490 .unwrap_or_else(Au::zero);
491 self.line_height.max(height_from_ascent_and_descent)
492 }
493
494 fn max(&self, other: &LineBlockSizes) -> LineBlockSizes {
495 let baseline_relative_size = match (
496 self.baseline_relative_size_for_line_height.as_ref(),
497 other.baseline_relative_size_for_line_height.as_ref(),
498 ) {
499 (Some(our_size), Some(other_size)) => Some(our_size.max(other_size)),
500 (our_size, other_size) => our_size.or(other_size).cloned(),
501 };
502 Self {
503 line_height: self.line_height.max(other.line_height),
504 baseline_relative_size_for_line_height: baseline_relative_size,
505 size_for_baseline_positioning: self
506 .size_for_baseline_positioning
507 .max(&other.size_for_baseline_positioning),
508 }
509 }
510
511 fn max_assign(&mut self, other: &LineBlockSizes) {
512 *self = self.max(other);
513 }
514
515 fn adjust_for_baseline_offset(&mut self, baseline_offset: Au) {
516 if let Some(size) = self.baseline_relative_size_for_line_height.as_mut() {
517 size.adjust_for_nested_baseline_offset(baseline_offset)
518 }
519 self.size_for_baseline_positioning
520 .adjust_for_nested_baseline_offset(baseline_offset);
521 }
522
523 fn find_baseline_offset(&self) -> Au {
530 match self.baseline_relative_size_for_line_height.as_ref() {
531 Some(size) => size.ascent,
532 None => {
533 let leading = self.resolve() -
536 (self.size_for_baseline_positioning.ascent +
537 self.size_for_baseline_positioning.descent);
538 leading.scale_by(0.5) + self.size_for_baseline_positioning.ascent
539 },
540 }
541 }
542}
543
544struct UnbreakableSegmentUnderConstruction {
548 inline_size: Au,
550
551 max_block_size: LineBlockSizes,
554
555 line_items: Vec<LineItem>,
557
558 inline_box_hierarchy_depth: Option<usize>,
561
562 has_content: bool,
566
567 has_inline_pbm: bool,
570
571 trailing_whitespace_size: Au,
573}
574
575impl UnbreakableSegmentUnderConstruction {
576 fn new() -> Self {
577 Self {
578 inline_size: Au::zero(),
579 max_block_size: LineBlockSizes {
580 line_height: Au::zero(),
581 baseline_relative_size_for_line_height: None,
582 size_for_baseline_positioning: BaselineRelativeSize::zero(),
583 },
584 line_items: Vec::new(),
585 inline_box_hierarchy_depth: None,
586 has_content: false,
587 has_inline_pbm: false,
588 trailing_whitespace_size: Au::zero(),
589 }
590 }
591
592 fn reset(&mut self) {
594 assert!(self.line_items.is_empty()); self.inline_size = Au::zero();
596 self.max_block_size = LineBlockSizes::zero();
597 self.inline_box_hierarchy_depth = None;
598 self.has_content = false;
599 self.trailing_whitespace_size = Au::zero();
600 }
601
602 fn push_line_item(&mut self, line_item: LineItem, inline_box_hierarchy_depth: usize) {
607 if self.line_items.is_empty() {
608 self.inline_box_hierarchy_depth = Some(inline_box_hierarchy_depth);
609 }
610 self.line_items.push(line_item);
611 }
612
613 fn trim_leading_whitespace(&mut self) {
624 let mut whitespace_trimmed = Au::zero();
625 for item in self.line_items.iter_mut() {
626 if !item.trim_whitespace_at_start(&mut whitespace_trimmed) {
627 break;
628 }
629 }
630 self.inline_size -= whitespace_trimmed;
631 }
632}
633
634bitflags! {
635 pub struct InlineContainerStateFlags: u8 {
636 const CREATE_STRUT = 0b0001;
637 const IS_SINGLE_LINE_TEXT_INPUT = 0b0010;
638 }
639}
640
641pub(super) struct InlineContainerState {
642 style: Arc<ComputedValues>,
644
645 flags: InlineContainerStateFlags,
647
648 has_content: RefCell<bool>,
651
652 strut_block_sizes: LineBlockSizes,
657
658 nested_strut_block_sizes: LineBlockSizes,
662
663 pub baseline_offset: Au,
669
670 font_metrics: FontMetrics,
672}
673
674pub(super) struct InlineFormattingContextLayout<'layout_data> {
675 positioning_context: &'layout_data mut PositioningContext,
676 containing_block: &'layout_data ContainingBlock<'layout_data>,
677 sequential_layout_state: Option<&'layout_data mut SequentialLayoutState>,
678 layout_context: &'layout_data LayoutContext<'layout_data>,
679
680 ifc: &'layout_data InlineFormattingContext,
682
683 root_nesting_level: InlineContainerState,
693
694 inline_box_state_stack: Vec<Rc<InlineBoxContainerState>>,
698
699 inline_box_states: Vec<Rc<InlineBoxContainerState>>,
704
705 fragments: Vec<Fragment>,
709
710 current_line: LineUnderConstruction,
712
713 current_line_segment: UnbreakableSegmentUnderConstruction,
715
716 linebreak_before_new_content: bool,
735
736 deferred_br_clear: Clear,
740
741 pub have_deferred_soft_wrap_opportunity: bool,
745
746 has_line_boxes: bool,
749
750 depends_on_block_constraints: bool,
753
754 white_space_collapse: WhiteSpaceCollapse,
759
760 text_wrap_mode: TextWrapMode,
765
766 baselines: Baselines,
770}
771
772impl InlineFormattingContextLayout<'_> {
773 fn current_inline_container_state(&self) -> &InlineContainerState {
774 match self.inline_box_state_stack.last() {
775 Some(inline_box_state) => &inline_box_state.base,
776 None => &self.root_nesting_level,
777 }
778 }
779
780 fn current_inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
781 self.inline_box_state_stack
782 .last()
783 .map(|state| state.identifier)
784 }
785
786 fn current_line_max_block_size_including_nested_containers(&self) -> LineBlockSizes {
787 self.current_inline_container_state()
788 .nested_strut_block_sizes
789 .max(&self.current_line.max_block_size)
790 }
791
792 fn propagate_current_nesting_level_white_space_style(&mut self) {
793 let style = match self.inline_box_state_stack.last() {
794 Some(inline_box_state) => &inline_box_state.base.style,
795 None => self.containing_block.style,
796 };
797 let style_text = style.get_inherited_text();
798 self.white_space_collapse = style_text.white_space_collapse;
799 self.text_wrap_mode = style_text.text_wrap_mode;
800 }
801
802 fn processing_br_element(&self) -> bool {
803 self.inline_box_state_stack
804 .last()
805 .map(|state| {
806 state
807 .base_fragment_info
808 .flags
809 .contains(FragmentFlags::IS_BR_ELEMENT)
810 })
811 .unwrap_or(false)
812 }
813
814 fn start_inline_box(&mut self, inline_box: &InlineBox) {
817 let inline_box_state = InlineBoxContainerState::new(
818 inline_box,
819 self.containing_block,
820 self.layout_context,
821 self.current_inline_container_state(),
822 inline_box.is_last_split,
823 inline_box
824 .default_font_index
825 .map(|index| &self.ifc.font_metrics[index].metrics),
826 );
827
828 self.depends_on_block_constraints |= inline_box
829 .base
830 .style
831 .depends_on_block_constraints_due_to_relative_positioning(
832 self.containing_block.style.writing_mode,
833 );
834
835 if inline_box_state
840 .base_fragment_info
841 .flags
842 .contains(FragmentFlags::IS_BR_ELEMENT) &&
843 self.deferred_br_clear == Clear::None
844 {
845 self.deferred_br_clear = Clear::from_style_and_container_writing_mode(
846 &inline_box_state.base.style,
847 self.containing_block.style.writing_mode,
848 );
849 }
850
851 if inline_box.is_first_split {
852 let padding = inline_box_state.pbm.padding.inline_start;
853 let border = inline_box_state.pbm.border.inline_start;
854 let margin = inline_box_state.pbm.margin.inline_start.auto_is(Au::zero);
855 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
858 self.current_line_segment.has_inline_pbm = true;
859 }
860 self.current_line_segment.inline_size += padding + border + margin;
861 self.current_line_segment
862 .line_items
863 .push(LineItem::InlineStartBoxPaddingBorderMargin(
864 inline_box.identifier,
865 ));
866 }
867
868 let inline_box_state = Rc::new(inline_box_state);
869
870 assert_eq!(
874 self.inline_box_states.len(),
875 inline_box.identifier.index_in_inline_boxes as usize
876 );
877 self.inline_box_states.push(inline_box_state.clone());
878 self.inline_box_state_stack.push(inline_box_state);
879 }
880
881 fn finish_inline_box(&mut self) {
884 let inline_box_state = match self.inline_box_state_stack.pop() {
885 Some(inline_box_state) => inline_box_state,
886 None => return, };
888
889 self.current_line_segment
890 .max_block_size
891 .max_assign(&inline_box_state.base.nested_strut_block_sizes);
892
893 if *inline_box_state.base.has_content.borrow() {
898 self.propagate_current_nesting_level_white_space_style();
899 }
900
901 if inline_box_state.is_last_fragment {
902 let padding = inline_box_state.pbm.padding.inline_end;
903 let border = inline_box_state.pbm.border.inline_end;
904 let margin = inline_box_state.pbm.margin.inline_end.auto_is(Au::zero);
905 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
908 self.current_line_segment.has_inline_pbm = true;
909 }
910 self.current_line_segment.inline_size += padding + border + margin;
911 self.current_line_segment
912 .line_items
913 .push(LineItem::InlineEndBoxPaddingBorderMargin(
914 inline_box_state.identifier,
915 ))
916 }
917 }
918
919 fn finish_last_line(&mut self) {
920 self.process_soft_wrap_opportunity();
926
927 self.commit_current_segment_to_line();
930
931 self.finish_current_line_and_reset(true );
934 }
935
936 fn finish_current_line_and_reset(&mut self, last_line_or_forced_line_break: bool) {
940 let whitespace_trimmed = self.current_line.trim_trailing_whitespace();
941 let (inline_start_position, justification_adjustment) = self
942 .calculate_current_line_inline_start_and_justification_adjustment(
943 whitespace_trimmed,
944 last_line_or_forced_line_break,
945 );
946
947 let block_start_position = self
948 .current_line
949 .line_block_start_considering_placement_among_floats();
950 let had_inline_advance =
951 self.current_line.inline_position != self.current_line.start_position.inline;
952
953 let effective_block_advance = if self.current_line.has_content ||
954 had_inline_advance ||
955 self.linebreak_before_new_content
956 {
957 self.current_line_max_block_size_including_nested_containers()
958 } else {
959 LineBlockSizes::zero()
960 };
961
962 let resolved_block_advance = effective_block_advance.resolve();
963 let mut block_end_position = block_start_position + resolved_block_advance;
964 if let Some(sequential_layout_state) = self.sequential_layout_state.as_mut() {
965 let increment = block_end_position - self.current_line.start_position.block;
968 sequential_layout_state.advance_block_position(increment);
969
970 if let Some(clearance) = sequential_layout_state
974 .calculate_clearance(self.deferred_br_clear, &CollapsedMargin::zero())
975 {
976 sequential_layout_state.advance_block_position(clearance);
977 block_end_position += clearance;
978 };
979 self.deferred_br_clear = Clear::None;
980 }
981
982 let mut line_to_layout = std::mem::replace(
984 &mut self.current_line,
985 LineUnderConstruction::new(LogicalVec2 {
986 inline: Au::zero(),
987 block: block_end_position,
988 }),
989 );
990
991 if line_to_layout.has_floats_waiting_to_be_placed {
992 place_pending_floats(self, &mut line_to_layout.line_items);
993 }
994
995 let start_position = LogicalVec2 {
996 block: block_start_position,
997 inline: inline_start_position,
998 };
999
1000 let baseline_offset = effective_block_advance.find_baseline_offset();
1001 let start_positioning_context_length = self.positioning_context.len();
1002 let fragments = LineItemLayout::layout_line_items(
1003 self,
1004 line_to_layout.line_items,
1005 start_position,
1006 &effective_block_advance,
1007 justification_adjustment,
1008 );
1009
1010 if line_to_layout.has_content || line_to_layout.has_inline_pbm {
1016 let baseline = baseline_offset + block_start_position;
1017 self.baselines.first.get_or_insert(baseline);
1018 self.baselines.last = Some(baseline);
1019 self.has_line_boxes = true;
1020 }
1021
1022 if fragments.is_empty() &&
1024 self.positioning_context.len() == start_positioning_context_length
1025 {
1026 return;
1027 }
1028
1029 let start_corner = LogicalVec2 {
1033 inline: Au::zero(),
1034 block: block_start_position,
1035 };
1036
1037 let logical_origin_in_physical_coordinates =
1038 start_corner.to_physical_vector(self.containing_block.style.writing_mode);
1039 self.positioning_context
1040 .adjust_static_position_of_hoisted_fragments_with_offset(
1041 &logical_origin_in_physical_coordinates,
1042 start_positioning_context_length,
1043 );
1044
1045 let physical_line_rect = LogicalRect {
1046 start_corner,
1047 size: LogicalVec2 {
1048 inline: self.containing_block.size.inline,
1049 block: effective_block_advance.resolve(),
1050 },
1051 }
1052 .as_physical(Some(self.containing_block));
1053 self.fragments
1054 .push(Fragment::Positioning(PositioningFragment::new_anonymous(
1055 self.root_nesting_level.style.clone(),
1056 physical_line_rect,
1057 fragments,
1058 )));
1059 }
1060
1061 fn calculate_current_line_inline_start_and_justification_adjustment(
1066 &self,
1067 whitespace_trimmed: Au,
1068 last_line_or_forced_line_break: bool,
1069 ) -> (Au, Au) {
1070 enum TextAlign {
1071 Start,
1072 Center,
1073 End,
1074 }
1075 let style = self.containing_block.style;
1076 let mut text_align_keyword = style.clone_text_align();
1077
1078 if last_line_or_forced_line_break {
1079 text_align_keyword = match style.clone_text_align_last() {
1080 TextAlignLast::Auto if text_align_keyword == TextAlignKeyword::Justify => {
1081 TextAlignKeyword::Start
1082 },
1083 TextAlignLast::Auto => text_align_keyword,
1084 TextAlignLast::Start => TextAlignKeyword::Start,
1085 TextAlignLast::End => TextAlignKeyword::End,
1086 TextAlignLast::Left => TextAlignKeyword::Left,
1087 TextAlignLast::Right => TextAlignKeyword::Right,
1088 TextAlignLast::Center => TextAlignKeyword::Center,
1089 TextAlignLast::Justify => TextAlignKeyword::Justify,
1090 };
1091 }
1092
1093 let text_align = match text_align_keyword {
1094 TextAlignKeyword::Start => TextAlign::Start,
1095 TextAlignKeyword::Center | TextAlignKeyword::MozCenter => TextAlign::Center,
1096 TextAlignKeyword::End => TextAlign::End,
1097 TextAlignKeyword::Left | TextAlignKeyword::MozLeft => {
1098 if style.writing_mode.line_left_is_inline_start() {
1099 TextAlign::Start
1100 } else {
1101 TextAlign::End
1102 }
1103 },
1104 TextAlignKeyword::Right | TextAlignKeyword::MozRight => {
1105 if style.writing_mode.line_left_is_inline_start() {
1106 TextAlign::End
1107 } else {
1108 TextAlign::Start
1109 }
1110 },
1111 TextAlignKeyword::Justify => TextAlign::Start,
1112 };
1113
1114 let (line_start, available_space) = match self.current_line.placement_among_floats.get() {
1115 Some(placement_among_floats) => (
1116 placement_among_floats.start_corner.inline,
1117 placement_among_floats.size.inline,
1118 ),
1119 None => (Au::zero(), self.containing_block.size.inline),
1120 };
1121
1122 let text_indent = self.current_line.start_position.inline;
1129 let line_length = self.current_line.inline_position - whitespace_trimmed - text_indent;
1130 let adjusted_line_start = line_start +
1131 match text_align {
1132 TextAlign::Start => text_indent,
1133 TextAlign::End => (available_space - line_length).max(text_indent),
1134 TextAlign::Center => (available_space - line_length + text_indent)
1135 .scale_by(0.5)
1136 .max(text_indent),
1137 };
1138
1139 let text_justify = self.containing_block.style.clone_text_justify();
1143 let justification_adjustment = match (text_align_keyword, text_justify) {
1144 (TextAlignKeyword::Justify, TextJustify::None) => Au::zero(),
1147 (TextAlignKeyword::Justify, _) => {
1148 match self.current_line.count_justification_opportunities() {
1149 0 => Au::zero(),
1150 num_justification_opportunities => {
1151 (available_space - text_indent - line_length)
1152 .scale_by(1. / num_justification_opportunities as f32)
1153 },
1154 }
1155 },
1156 _ => Au::zero(),
1157 };
1158
1159 let justification_adjustment = justification_adjustment.max(Au::zero());
1162
1163 (adjusted_line_start, justification_adjustment)
1164 }
1165
1166 fn place_float_fragment(&mut self, fragment: &mut BoxFragment) {
1167 let state = self
1168 .sequential_layout_state
1169 .as_mut()
1170 .expect("Tried to lay out a float with no sequential placement state!");
1171
1172 let block_offset_from_containining_block_top = state
1173 .current_block_position_including_margins() -
1174 state.current_containing_block_offset();
1175 state.place_float_fragment(
1176 fragment,
1177 self.containing_block,
1178 CollapsedMargin::zero(),
1179 block_offset_from_containining_block_top,
1180 );
1181 }
1182
1183 fn place_float_line_item_for_commit_to_line(
1192 &mut self,
1193 float_item: &mut FloatLineItem,
1194 line_inline_size_without_trailing_whitespace: Au,
1195 ) {
1196 let mut float_fragment = float_item.fragment.borrow_mut();
1197 let logical_margin_rect_size = float_fragment
1198 .margin_rect()
1199 .size
1200 .to_logical(self.containing_block.style.writing_mode);
1201 let inline_size = logical_margin_rect_size.inline.max(Au::zero());
1202
1203 let available_inline_size = match self.current_line.placement_among_floats.get() {
1204 Some(placement_among_floats) => placement_among_floats.size.inline,
1205 None => self.containing_block.size.inline,
1206 } - line_inline_size_without_trailing_whitespace;
1207
1208 let has_content = self.current_line.has_content || self.current_line_segment.has_content;
1214 let fits_on_line = !has_content || inline_size <= available_inline_size;
1215 let needs_placement_later =
1216 self.current_line.has_floats_waiting_to_be_placed || !fits_on_line;
1217
1218 if needs_placement_later {
1219 self.current_line.has_floats_waiting_to_be_placed = true;
1220 } else {
1221 self.place_float_fragment(&mut float_fragment);
1222 float_item.needs_placement = false;
1223 }
1224
1225 let new_placement = self.place_line_among_floats(&LogicalVec2 {
1230 inline: line_inline_size_without_trailing_whitespace,
1231 block: self.current_line.max_block_size.resolve(),
1232 });
1233 self.current_line
1234 .replace_placement_among_floats(new_placement);
1235 }
1236
1237 fn place_line_among_floats(&self, potential_line_size: &LogicalVec2<Au>) -> LogicalRect<Au> {
1242 let sequential_layout_state = self
1243 .sequential_layout_state
1244 .as_ref()
1245 .expect("Should not have called this function without having floats.");
1246
1247 let ifc_offset_in_float_container = LogicalVec2 {
1248 inline: sequential_layout_state
1249 .floats
1250 .containing_block_info
1251 .inline_start,
1252 block: sequential_layout_state.current_containing_block_offset(),
1253 };
1254
1255 let ceiling = self
1256 .current_line
1257 .line_block_start_considering_placement_among_floats();
1258 let mut placement = PlacementAmongFloats::new(
1259 &sequential_layout_state.floats,
1260 ceiling + ifc_offset_in_float_container.block,
1261 LogicalVec2 {
1262 inline: potential_line_size.inline,
1263 block: potential_line_size.block,
1264 },
1265 &PaddingBorderMargin::zero(),
1266 );
1267
1268 let mut placement_rect = placement.place();
1269 placement_rect.start_corner -= ifc_offset_in_float_container;
1270 placement_rect
1271 }
1272
1273 fn new_potential_line_size_causes_line_break(
1280 &mut self,
1281 potential_line_size: &LogicalVec2<Au>,
1282 ) -> bool {
1283 let available_line_space = if self.sequential_layout_state.is_some() {
1284 self.current_line
1285 .placement_among_floats
1286 .get_or_init(|| self.place_line_among_floats(potential_line_size))
1287 .size
1288 } else {
1289 LogicalVec2 {
1290 inline: self.containing_block.size.inline,
1291 block: MAX_AU,
1292 }
1293 };
1294
1295 let inline_would_overflow = potential_line_size.inline > available_line_space.inline;
1296 let block_would_overflow = potential_line_size.block > available_line_space.block;
1297
1298 let can_break = self.current_line.has_content;
1301
1302 if !can_break {
1308 if self.sequential_layout_state.is_some() &&
1311 (inline_would_overflow || block_would_overflow)
1312 {
1313 let new_placement = self.place_line_among_floats(potential_line_size);
1314 self.current_line
1315 .replace_placement_among_floats(new_placement);
1316 }
1317
1318 return false;
1319 }
1320
1321 if potential_line_size.inline > self.containing_block.size.inline {
1324 return true;
1325 }
1326
1327 if block_would_overflow {
1331 assert!(self.sequential_layout_state.is_some());
1333 let new_placement = self.place_line_among_floats(potential_line_size);
1334 if new_placement.start_corner.block !=
1335 self.current_line
1336 .line_block_start_considering_placement_among_floats()
1337 {
1338 return true;
1339 } else {
1340 self.current_line
1341 .replace_placement_among_floats(new_placement);
1342 return false;
1343 }
1344 }
1345
1346 inline_would_overflow
1350 }
1351
1352 pub(super) fn defer_forced_line_break(&mut self) {
1353 if !self.unbreakable_segment_fits_on_line() {
1356 self.process_line_break(false );
1357 }
1358
1359 self.linebreak_before_new_content = true;
1361
1362 let line_is_empty =
1370 !self.current_line_segment.has_content && !self.current_line.has_content;
1371 if !self.processing_br_element() || line_is_empty {
1372 let strut_size = self
1373 .current_inline_container_state()
1374 .strut_block_sizes
1375 .clone();
1376 self.update_unbreakable_segment_for_new_content(
1377 &strut_size,
1378 Au::zero(),
1379 SegmentContentFlags::empty(),
1380 );
1381 }
1382 }
1383
1384 pub(super) fn possibly_flush_deferred_forced_line_break(&mut self) {
1385 if !self.linebreak_before_new_content {
1386 return;
1387 }
1388
1389 self.commit_current_segment_to_line();
1390 self.process_line_break(true );
1391 self.linebreak_before_new_content = false;
1392 }
1393
1394 fn push_line_item_to_unbreakable_segment(&mut self, line_item: LineItem) {
1395 self.current_line_segment
1396 .push_line_item(line_item, self.inline_box_state_stack.len());
1397 }
1398
1399 pub(super) fn push_glyph_store_to_unbreakable_segment(
1400 &mut self,
1401 glyph_store: std::sync::Arc<GlyphStore>,
1402 text_run: &TextRun,
1403 font_index: usize,
1404 bidi_level: Level,
1405 range: range::Range<ByteIndex>,
1406 ) {
1407 let inline_advance = glyph_store.total_advance();
1408 let flags = if glyph_store.is_whitespace() {
1409 SegmentContentFlags::from(text_run.inline_styles.style.borrow().get_inherited_text())
1410 } else {
1411 SegmentContentFlags::empty()
1412 };
1413
1414 let ifc_font_info = &self.ifc.font_metrics[font_index];
1418 let font_metrics = ifc_font_info.metrics.clone();
1419 let using_fallback_font =
1420 self.current_inline_container_state().font_metrics != font_metrics;
1421
1422 let quirks_mode = self.layout_context.style_context.quirks_mode() != QuirksMode::NoQuirks;
1423 let strut_size = if using_fallback_font {
1424 let container_state = self.current_inline_container_state();
1426 let vertical_align = effective_vertical_align(
1427 &container_state.style,
1428 self.inline_box_state_stack.last().map(|c| &c.base),
1429 );
1430 let mut block_size = container_state.get_block_size_contribution(
1431 vertical_align,
1432 &font_metrics,
1433 &container_state.font_metrics,
1434 );
1435 block_size.adjust_for_baseline_offset(container_state.baseline_offset);
1436 block_size
1437 } else if quirks_mode && !flags.is_collapsible_whitespace() {
1438 self.current_inline_container_state()
1443 .strut_block_sizes
1444 .clone()
1445 } else {
1446 LineBlockSizes::zero()
1447 };
1448 self.update_unbreakable_segment_for_new_content(&strut_size, inline_advance, flags);
1449
1450 let current_inline_box_identifier = self.current_inline_box_identifier();
1451 match self.current_line_segment.line_items.last_mut() {
1452 Some(LineItem::TextRun(inline_box_identifier, line_item))
1453 if *inline_box_identifier == current_inline_box_identifier &&
1454 line_item.can_merge(ifc_font_info.key, bidi_level) =>
1455 {
1456 line_item.text.push(glyph_store);
1457 return;
1458 },
1459 _ => {},
1460 }
1461
1462 let selection_range = if let Some(selection) = &text_run.selection_range {
1463 let intersection = selection.intersect(&range);
1464 if intersection.is_empty() {
1465 let insertion_point_index = selection.begin();
1466 if insertion_point_index >= range.begin() &&
1469 insertion_point_index <= range.end() &&
1470 (range.begin() != insertion_point_index || range.begin().0 == 0)
1471 {
1472 Some(Range::new(
1473 insertion_point_index - range.begin(),
1474 ByteIndex(0),
1475 ))
1476 } else {
1477 None
1478 }
1479 } else {
1480 Some(Range::new(
1481 intersection.begin() - range.begin(),
1482 intersection.length(),
1483 ))
1484 }
1485 } else {
1486 None
1487 };
1488
1489 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1490 current_inline_box_identifier,
1491 TextRunLineItem {
1492 text: vec![glyph_store],
1493 base_fragment_info: text_run.base_fragment_info,
1494 inline_styles: text_run.inline_styles.clone(),
1495 font_metrics,
1496 font_key: ifc_font_info.key,
1497 bidi_level,
1498 selection_range,
1499 },
1500 ));
1501 }
1502
1503 fn update_unbreakable_segment_for_new_content(
1504 &mut self,
1505 block_sizes_of_content: &LineBlockSizes,
1506 inline_size: Au,
1507 flags: SegmentContentFlags,
1508 ) {
1509 if flags.is_collapsible_whitespace() || flags.is_wrappable_and_hangable() {
1510 self.current_line_segment.trailing_whitespace_size = inline_size;
1511 } else {
1512 self.current_line_segment.trailing_whitespace_size = Au::zero();
1513 }
1514 if !flags.is_collapsible_whitespace() {
1515 self.current_line_segment.has_content = true;
1516 }
1517
1518 let container_max_block_size = &self
1520 .current_inline_container_state()
1521 .nested_strut_block_sizes
1522 .clone();
1523 self.current_line_segment
1524 .max_block_size
1525 .max_assign(container_max_block_size);
1526 self.current_line_segment
1527 .max_block_size
1528 .max_assign(block_sizes_of_content);
1529
1530 self.current_line_segment.inline_size += inline_size;
1531
1532 *self
1534 .current_inline_container_state()
1535 .has_content
1536 .borrow_mut() = true;
1537 self.propagate_current_nesting_level_white_space_style();
1538 }
1539
1540 fn process_line_break(&mut self, forced_line_break: bool) {
1541 self.current_line_segment.trim_leading_whitespace();
1542 self.finish_current_line_and_reset(forced_line_break);
1543 }
1544
1545 pub(super) fn unbreakable_segment_fits_on_line(&mut self) -> bool {
1546 let potential_line_size = LogicalVec2 {
1547 inline: self.current_line.inline_position + self.current_line_segment.inline_size -
1548 self.current_line_segment.trailing_whitespace_size,
1549 block: self
1550 .current_line_max_block_size_including_nested_containers()
1551 .max(&self.current_line_segment.max_block_size)
1552 .resolve(),
1553 };
1554
1555 !self.new_potential_line_size_causes_line_break(&potential_line_size)
1556 }
1557
1558 pub(super) fn process_soft_wrap_opportunity(&mut self) {
1562 if self.current_line_segment.line_items.is_empty() {
1563 return;
1564 }
1565 if self.text_wrap_mode == TextWrapMode::Nowrap {
1566 return;
1567 }
1568
1569 let potential_line_size = LogicalVec2 {
1570 inline: self.current_line.inline_position + self.current_line_segment.inline_size -
1571 self.current_line_segment.trailing_whitespace_size,
1572 block: self
1573 .current_line_max_block_size_including_nested_containers()
1574 .max(&self.current_line_segment.max_block_size)
1575 .resolve(),
1576 };
1577
1578 if self.new_potential_line_size_causes_line_break(&potential_line_size) {
1579 self.process_line_break(false );
1580 }
1581 self.commit_current_segment_to_line();
1582 }
1583
1584 fn commit_current_segment_to_line(&mut self) {
1587 if self.current_line_segment.line_items.is_empty() && !self.current_line_segment.has_content
1590 {
1591 return;
1592 }
1593
1594 if !self.current_line.has_content {
1595 self.current_line_segment.trim_leading_whitespace();
1596 }
1597
1598 self.current_line.inline_position += self.current_line_segment.inline_size;
1599 self.current_line.max_block_size = self
1600 .current_line_max_block_size_including_nested_containers()
1601 .max(&self.current_line_segment.max_block_size);
1602 let line_inline_size_without_trailing_whitespace =
1603 self.current_line.inline_position - self.current_line_segment.trailing_whitespace_size;
1604
1605 let mut segment_items = mem::take(&mut self.current_line_segment.line_items);
1607 for item in segment_items.iter_mut() {
1608 if let LineItem::Float(_, float_item) = item {
1609 self.place_float_line_item_for_commit_to_line(
1610 float_item,
1611 line_inline_size_without_trailing_whitespace,
1612 );
1613 }
1614 }
1615
1616 if self.current_line.line_items.is_empty() {
1621 let will_break = self.new_potential_line_size_causes_line_break(&LogicalVec2 {
1622 inline: line_inline_size_without_trailing_whitespace,
1623 block: self.current_line_segment.max_block_size.resolve(),
1624 });
1625 assert!(!will_break);
1626 }
1627
1628 self.current_line.line_items.extend(segment_items);
1629 self.current_line.has_content |= self.current_line_segment.has_content;
1630 self.current_line.has_inline_pbm |= self.current_line_segment.has_inline_pbm;
1631
1632 self.current_line_segment.reset();
1633 }
1634}
1635
1636bitflags! {
1637 pub struct SegmentContentFlags: u8 {
1638 const COLLAPSIBLE_WHITESPACE = 0b00000001;
1639 const WRAPPABLE_AND_HANGABLE_WHITESPACE = 0b00000010;
1640 }
1641}
1642
1643impl SegmentContentFlags {
1644 fn is_collapsible_whitespace(&self) -> bool {
1645 self.contains(Self::COLLAPSIBLE_WHITESPACE)
1646 }
1647
1648 fn is_wrappable_and_hangable(&self) -> bool {
1649 self.contains(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE)
1650 }
1651}
1652
1653impl From<&InheritedText> for SegmentContentFlags {
1654 fn from(style_text: &InheritedText) -> Self {
1655 let mut flags = Self::empty();
1656
1657 if !matches!(
1660 style_text.white_space_collapse,
1661 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
1662 ) {
1663 flags.insert(Self::COLLAPSIBLE_WHITESPACE);
1664 }
1665
1666 if style_text.text_wrap_mode == TextWrapMode::Wrap &&
1669 style_text.white_space_collapse != WhiteSpaceCollapse::BreakSpaces
1670 {
1671 flags.insert(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE);
1672 }
1673 flags
1674 }
1675}
1676
1677impl InlineFormattingContext {
1678 #[servo_tracing::instrument(name = "InlineFormattingContext::new_with_builder", skip_all)]
1679 pub(super) fn new_with_builder(
1680 builder: InlineFormattingContextBuilder,
1681 layout_context: &LayoutContext,
1682 has_first_formatted_line: bool,
1683 is_single_line_text_input: bool,
1684 starting_bidi_level: Level,
1685 rendering_group_id: RenderingGroupId,
1686 ) -> Self {
1687 let text_content: String = builder.text_segments.into_iter().collect();
1689 let mut font_metrics = Vec::new();
1690
1691 let bidi_info = BidiInfo::new(&text_content, Some(starting_bidi_level));
1692 let has_right_to_left_content = bidi_info.has_rtl();
1693
1694 let mut new_linebreaker = LineBreaker::new(text_content.as_str());
1695 for item in builder.inline_items.iter() {
1696 match &mut *item.borrow_mut() {
1697 InlineItem::TextRun(text_run) => {
1698 text_run.borrow_mut().segment_and_shape(
1699 &text_content,
1700 &layout_context.font_context,
1701 &mut new_linebreaker,
1702 &mut font_metrics,
1703 &bidi_info,
1704 rendering_group_id,
1705 );
1706 },
1707 InlineItem::StartInlineBox(inline_box) => {
1708 let inline_box = &mut *inline_box.borrow_mut();
1709 if let Some(font) = get_font_for_first_font_for_style(
1710 &inline_box.base.style,
1711 &layout_context.font_context,
1712 ) {
1713 inline_box.default_font_index = Some(add_or_get_font(
1714 &font,
1715 &mut font_metrics,
1716 &layout_context.font_context,
1717 rendering_group_id,
1718 ));
1719 }
1720 },
1721 InlineItem::Atomic(_, index_in_text, bidi_level) => {
1722 *bidi_level = bidi_info.levels[*index_in_text];
1723 },
1724 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) |
1725 InlineItem::OutOfFlowFloatBox(_) |
1726 InlineItem::EndInlineBox => {},
1727 }
1728 }
1729
1730 InlineFormattingContext {
1731 text_content,
1732 inline_items: builder.inline_items,
1733 inline_boxes: builder.inline_boxes,
1734 font_metrics,
1735 shared_inline_styles: builder
1736 .shared_inline_styles_stack
1737 .last()
1738 .expect("Should have at least one SharedInlineStyle for the root of an IFC")
1739 .clone(),
1740 has_first_formatted_line,
1741 contains_floats: builder.contains_floats,
1742 is_single_line_text_input,
1743 has_right_to_left_content,
1744 }
1745 }
1746
1747 pub(crate) fn repair_style(
1748 &self,
1749 node: &ServoThreadSafeLayoutNode,
1750 new_style: &Arc<ComputedValues>,
1751 ) {
1752 *self.shared_inline_styles.style.borrow_mut() = new_style.clone();
1753 *self.shared_inline_styles.selected.borrow_mut() = node.selected_style();
1754 }
1755
1756 pub(crate) fn inline_start_for_first_line(
1757 &self,
1758 containing_block: IndefiniteContainingBlock,
1759 ) -> Au {
1760 if !self.has_first_formatted_line {
1761 return Au::zero();
1762 }
1763 containing_block
1764 .style
1765 .get_inherited_text()
1766 .text_indent
1767 .length
1768 .to_used_value(containing_block.size.inline.unwrap_or_default())
1769 }
1770
1771 pub(super) fn layout(
1772 &self,
1773 layout_context: &LayoutContext,
1774 positioning_context: &mut PositioningContext,
1775 containing_block: &ContainingBlock,
1776 sequential_layout_state: Option<&mut SequentialLayoutState>,
1777 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
1778 ) -> CacheableLayoutResult {
1779 for inline_box in self.inline_boxes.iter() {
1781 inline_box.borrow().base.clear_fragments();
1782 }
1783
1784 let style = containing_block.style;
1785
1786 let default_font_metrics =
1789 get_font_for_first_font_for_style(style, &layout_context.font_context)
1790 .map(|font| font.metrics.clone());
1791
1792 let style_text = containing_block.style.get_inherited_text();
1793 let mut inline_container_state_flags = InlineContainerStateFlags::empty();
1794 if inline_container_needs_strut(style, layout_context, None) {
1795 inline_container_state_flags.insert(InlineContainerStateFlags::CREATE_STRUT);
1796 }
1797 if self.is_single_line_text_input {
1798 inline_container_state_flags
1799 .insert(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT);
1800 }
1801
1802 let mut layout = InlineFormattingContextLayout {
1803 positioning_context,
1804 containing_block,
1805 sequential_layout_state,
1806 layout_context,
1807 ifc: self,
1808 fragments: Vec::new(),
1809 current_line: LineUnderConstruction::new(LogicalVec2 {
1810 inline: self.inline_start_for_first_line(containing_block.into()),
1811 block: Au::zero(),
1812 }),
1813 root_nesting_level: InlineContainerState::new(
1814 style.to_arc(),
1815 inline_container_state_flags,
1816 None, default_font_metrics.as_ref(),
1818 ),
1819 inline_box_state_stack: Vec::new(),
1820 inline_box_states: Vec::with_capacity(self.inline_boxes.len()),
1821 current_line_segment: UnbreakableSegmentUnderConstruction::new(),
1822 linebreak_before_new_content: false,
1823 deferred_br_clear: Clear::None,
1824 have_deferred_soft_wrap_opportunity: false,
1825 has_line_boxes: false,
1826 depends_on_block_constraints: false,
1827 white_space_collapse: style_text.white_space_collapse,
1828 text_wrap_mode: style_text.text_wrap_mode,
1829 baselines: Baselines::default(),
1830 };
1831
1832 if let Some(ref mut sequential_layout_state) = layout.sequential_layout_state {
1836 sequential_layout_state.collapse_margins();
1837 }
1839
1840 for item in self.inline_items.iter() {
1841 let item = &*item.borrow();
1842
1843 if !matches!(item, InlineItem::EndInlineBox) {
1845 layout.possibly_flush_deferred_forced_line_break();
1846 }
1847
1848 match item {
1849 InlineItem::StartInlineBox(inline_box) => {
1850 layout.start_inline_box(&inline_box.borrow());
1851 },
1852 InlineItem::EndInlineBox => layout.finish_inline_box(),
1853 InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout),
1854 InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
1855 atomic_formatting_context.borrow().layout_into_line_items(
1856 &mut layout,
1857 *offset_in_text,
1858 *bidi_level,
1859 );
1860 },
1861 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, _) => {
1862 layout.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
1863 layout.current_inline_box_identifier(),
1864 AbsolutelyPositionedLineItem {
1865 absolutely_positioned_box: positioned_box.clone(),
1866 },
1867 ));
1868 },
1869 InlineItem::OutOfFlowFloatBox(float_box) => {
1870 float_box.borrow().layout_into_line_items(&mut layout);
1871 },
1872 }
1873 }
1874
1875 layout.finish_last_line();
1876
1877 let mut collapsible_margins_in_children = CollapsedBlockMargins::zero();
1878 let content_block_size = layout.current_line.start_position.block;
1879 collapsible_margins_in_children.collapsed_through = !layout.has_line_boxes &&
1880 content_block_size.is_zero() &&
1881 collapsible_with_parent_start_margin.0;
1882
1883 CacheableLayoutResult {
1884 fragments: layout.fragments,
1885 content_block_size,
1886 collapsible_margins_in_children,
1887 baselines: layout.baselines,
1888 depends_on_block_constraints: layout.depends_on_block_constraints,
1889 content_inline_size_for_table: None,
1890 specific_layout_info: None,
1891 }
1892 }
1893
1894 fn next_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
1895 let Some(character) = self.text_content[index..].chars().nth(1) else {
1896 return false;
1897 };
1898 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
1899 }
1900
1901 fn previous_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
1902 let Some(character) = self.text_content[0..index].chars().next_back() else {
1903 return false;
1904 };
1905 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
1906 }
1907}
1908
1909impl InlineContainerState {
1910 fn new(
1911 style: Arc<ComputedValues>,
1912 flags: InlineContainerStateFlags,
1913 parent_container: Option<&InlineContainerState>,
1914 font_metrics: Option<&FontMetrics>,
1915 ) -> Self {
1916 let font_metrics = font_metrics.cloned().unwrap_or_else(FontMetrics::empty);
1917 let line_height = line_height(
1918 &style,
1919 &font_metrics,
1920 flags.contains(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT),
1921 );
1922
1923 let mut baseline_offset = Au::zero();
1924 let mut strut_block_sizes = Self::get_block_sizes_with_style(
1925 effective_vertical_align(&style, parent_container),
1926 &style,
1927 &font_metrics,
1928 &font_metrics,
1929 line_height,
1930 );
1931 if let Some(parent_container) = parent_container {
1932 baseline_offset = parent_container.get_cumulative_baseline_offset_for_child(
1935 style.clone_vertical_align(),
1936 &strut_block_sizes,
1937 );
1938 strut_block_sizes.adjust_for_baseline_offset(baseline_offset);
1939 }
1940
1941 let mut nested_block_sizes = parent_container
1942 .map(|container| container.nested_strut_block_sizes.clone())
1943 .unwrap_or_else(LineBlockSizes::zero);
1944 if flags.contains(InlineContainerStateFlags::CREATE_STRUT) {
1945 nested_block_sizes.max_assign(&strut_block_sizes);
1946 }
1947
1948 Self {
1949 style,
1950 flags,
1951 has_content: RefCell::new(false),
1952 nested_strut_block_sizes: nested_block_sizes,
1953 strut_block_sizes,
1954 baseline_offset,
1955 font_metrics,
1956 }
1957 }
1958
1959 fn get_block_sizes_with_style(
1960 vertical_align: VerticalAlign,
1961 style: &ComputedValues,
1962 font_metrics: &FontMetrics,
1963 font_metrics_of_first_font: &FontMetrics,
1964 line_height: Au,
1965 ) -> LineBlockSizes {
1966 if !is_baseline_relative(vertical_align) {
1967 return LineBlockSizes {
1968 line_height,
1969 baseline_relative_size_for_line_height: None,
1970 size_for_baseline_positioning: BaselineRelativeSize::zero(),
1971 };
1972 }
1973
1974 let mut ascent = font_metrics.ascent;
1983 let mut descent = font_metrics.descent;
1984 if style.get_font().line_height == LineHeight::Normal {
1985 let half_leading_from_line_gap =
1986 (font_metrics.line_gap - descent - ascent).scale_by(0.5);
1987 ascent += half_leading_from_line_gap;
1988 descent += half_leading_from_line_gap;
1989 }
1990
1991 let size_for_baseline_positioning = BaselineRelativeSize { ascent, descent };
1995
1996 if style.get_font().line_height != LineHeight::Normal {
2012 ascent = font_metrics_of_first_font.ascent;
2013 descent = font_metrics_of_first_font.descent;
2014 let half_leading = (line_height - (ascent + descent)).scale_by(0.5);
2015 ascent += half_leading;
2020 descent = line_height - ascent;
2021 }
2022
2023 LineBlockSizes {
2024 line_height,
2025 baseline_relative_size_for_line_height: Some(BaselineRelativeSize { ascent, descent }),
2026 size_for_baseline_positioning,
2027 }
2028 }
2029
2030 fn get_block_size_contribution(
2031 &self,
2032 vertical_align: VerticalAlign,
2033 font_metrics: &FontMetrics,
2034 font_metrics_of_first_font: &FontMetrics,
2035 ) -> LineBlockSizes {
2036 Self::get_block_sizes_with_style(
2037 vertical_align,
2038 &self.style,
2039 font_metrics,
2040 font_metrics_of_first_font,
2041 line_height(
2042 &self.style,
2043 font_metrics,
2044 self.flags
2045 .contains(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT),
2046 ),
2047 )
2048 }
2049
2050 fn get_cumulative_baseline_offset_for_child(
2051 &self,
2052 child_vertical_align: VerticalAlign,
2053 child_block_size: &LineBlockSizes,
2054 ) -> Au {
2055 let block_size = self.get_block_size_contribution(
2056 child_vertical_align.clone(),
2057 &self.font_metrics,
2058 &self.font_metrics,
2059 );
2060 self.baseline_offset +
2061 match child_vertical_align {
2062 VerticalAlign::Keyword(VerticalAlignKeyword::Baseline) |
2067 VerticalAlign::Keyword(VerticalAlignKeyword::Top) |
2068 VerticalAlign::Keyword(VerticalAlignKeyword::Bottom) => Au::zero(),
2069 VerticalAlign::Keyword(VerticalAlignKeyword::Sub) => {
2070 block_size.resolve().scale_by(FONT_SUBSCRIPT_OFFSET_RATIO)
2071 },
2072 VerticalAlign::Keyword(VerticalAlignKeyword::Super) => {
2073 -block_size.resolve().scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO)
2074 },
2075 VerticalAlign::Keyword(VerticalAlignKeyword::TextTop) => {
2076 child_block_size.size_for_baseline_positioning.ascent - self.font_metrics.ascent
2077 },
2078 VerticalAlign::Keyword(VerticalAlignKeyword::Middle) => {
2079 (child_block_size.size_for_baseline_positioning.ascent -
2082 child_block_size.size_for_baseline_positioning.descent -
2083 self.font_metrics.x_height)
2084 .scale_by(0.5)
2085 },
2086 VerticalAlign::Keyword(VerticalAlignKeyword::TextBottom) => {
2087 self.font_metrics.descent -
2088 child_block_size.size_for_baseline_positioning.descent
2089 },
2090 VerticalAlign::Length(length_percentage) => {
2091 -length_percentage.to_used_value(child_block_size.line_height)
2092 },
2093 }
2094 }
2095}
2096
2097impl IndependentFormattingContext {
2098 fn layout_into_line_items(
2099 &self,
2100 layout: &mut InlineFormattingContextLayout,
2101 offset_in_text: usize,
2102 bidi_level: Level,
2103 ) {
2104 let mut child_positioning_context = PositioningContext::default();
2106 let IndependentFloatOrAtomicLayoutResult {
2107 mut fragment,
2108 baselines,
2109 pbm_sums,
2110 } = self.layout_float_or_atomic_inline(
2111 layout.layout_context,
2112 &mut child_positioning_context,
2113 layout.containing_block,
2114 );
2115
2116 layout.depends_on_block_constraints |= fragment.base.flags.contains(
2119 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2120 );
2121
2122 let container_writing_mode = layout.containing_block.style.writing_mode;
2124 let pbm_physical_offset = pbm_sums
2125 .start_offset()
2126 .to_physical_size(container_writing_mode);
2127 fragment.content_rect = fragment
2128 .content_rect
2129 .translate(pbm_physical_offset.to_vector());
2130
2131 fragment = fragment.with_baselines(baselines);
2133
2134 let positioning_context = if self.is_replaced() {
2137 None
2138 } else {
2139 if fragment
2140 .style
2141 .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
2142 {
2143 child_positioning_context
2144 .layout_collected_children(layout.layout_context, &mut fragment);
2145 }
2146 Some(child_positioning_context)
2147 };
2148
2149 if layout.text_wrap_mode == TextWrapMode::Wrap &&
2150 !layout
2151 .ifc
2152 .previous_character_prevents_soft_wrap_opportunity(offset_in_text)
2153 {
2154 layout.process_soft_wrap_opportunity();
2155 }
2156
2157 let size = pbm_sums.sum() +
2158 fragment
2159 .content_rect
2160 .size
2161 .to_logical(container_writing_mode);
2162 let baseline_offset = self
2163 .pick_baseline(&fragment.baselines(container_writing_mode))
2164 .map(|baseline| pbm_sums.block_start + baseline)
2165 .unwrap_or(size.block);
2166
2167 let (block_sizes, baseline_offset_in_parent) =
2168 self.get_block_sizes_and_baseline_offset(layout, size.block, baseline_offset);
2169 layout.update_unbreakable_segment_for_new_content(
2170 &block_sizes,
2171 size.inline,
2172 SegmentContentFlags::empty(),
2173 );
2174
2175 let fragment = ArcRefCell::new(fragment);
2176 self.base.set_fragment(Fragment::Box(fragment.clone()));
2177
2178 layout.push_line_item_to_unbreakable_segment(LineItem::Atomic(
2179 layout.current_inline_box_identifier(),
2180 AtomicLineItem {
2181 fragment,
2182 size,
2183 positioning_context,
2184 baseline_offset_in_parent,
2185 baseline_offset_in_item: baseline_offset,
2186 bidi_level,
2187 },
2188 ));
2189
2190 if !layout
2193 .ifc
2194 .next_character_prevents_soft_wrap_opportunity(offset_in_text)
2195 {
2196 layout.have_deferred_soft_wrap_opportunity = true;
2197 }
2198 }
2199
2200 fn pick_baseline(&self, baselines: &Baselines) -> Option<Au> {
2204 match self.style().clone_baseline_source() {
2205 BaselineSource::First => baselines.first,
2206 BaselineSource::Last => baselines.last,
2207 BaselineSource::Auto if self.is_block_container() => baselines.last,
2208 BaselineSource::Auto => baselines.first,
2209 }
2210 }
2211
2212 fn get_block_sizes_and_baseline_offset(
2213 &self,
2214 ifc: &InlineFormattingContextLayout,
2215 block_size: Au,
2216 baseline_offset_in_content_area: Au,
2217 ) -> (LineBlockSizes, Au) {
2218 let mut contribution = if !is_baseline_relative(self.style().clone_vertical_align()) {
2219 LineBlockSizes {
2220 line_height: block_size,
2221 baseline_relative_size_for_line_height: None,
2222 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2223 }
2224 } else {
2225 let baseline_relative_size = BaselineRelativeSize {
2226 ascent: baseline_offset_in_content_area,
2227 descent: block_size - baseline_offset_in_content_area,
2228 };
2229 LineBlockSizes {
2230 line_height: block_size,
2231 baseline_relative_size_for_line_height: Some(baseline_relative_size.clone()),
2232 size_for_baseline_positioning: baseline_relative_size,
2233 }
2234 };
2235
2236 let baseline_offset = ifc
2237 .current_inline_container_state()
2238 .get_cumulative_baseline_offset_for_child(
2239 self.style().clone_vertical_align(),
2240 &contribution,
2241 );
2242 contribution.adjust_for_baseline_offset(baseline_offset);
2243
2244 (contribution, baseline_offset)
2245 }
2246}
2247
2248impl FloatBox {
2249 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
2250 let fragment = ArcRefCell::new(self.layout(
2251 layout.layout_context,
2252 layout.positioning_context,
2253 layout.containing_block,
2254 ));
2255
2256 self.contents
2257 .base
2258 .set_fragment(Fragment::Box(fragment.clone()));
2259 layout.push_line_item_to_unbreakable_segment(LineItem::Float(
2260 layout.current_inline_box_identifier(),
2261 FloatLineItem {
2262 fragment,
2263 needs_placement: true,
2264 },
2265 ));
2266 }
2267}
2268
2269fn place_pending_floats(ifc: &mut InlineFormattingContextLayout, line_items: &mut [LineItem]) {
2270 for item in line_items.iter_mut() {
2271 if let LineItem::Float(_, float_line_item) = item {
2272 if float_line_item.needs_placement {
2273 ifc.place_float_fragment(&mut float_line_item.fragment.borrow_mut());
2274 }
2275 }
2276 }
2277}
2278
2279fn line_height(
2280 parent_style: &ComputedValues,
2281 font_metrics: &FontMetrics,
2282 is_single_line_text_input: bool,
2283) -> Au {
2284 let font = parent_style.get_font();
2285 let font_size = font.font_size.computed_size();
2286 let mut line_height = match font.line_height {
2287 LineHeight::Normal => font_metrics.line_gap,
2288 LineHeight::Number(number) => (font_size * number.0).into(),
2289 LineHeight::Length(length) => length.0.into(),
2290 };
2291
2292 if is_single_line_text_input {
2296 line_height.max_assign(font_metrics.line_gap);
2297 }
2298
2299 line_height
2300}
2301
2302fn effective_vertical_align(
2303 style: &ComputedValues,
2304 container: Option<&InlineContainerState>,
2305) -> VerticalAlign {
2306 if container.is_none() {
2307 VerticalAlign::Keyword(VerticalAlignKeyword::Baseline)
2311 } else {
2312 style.clone_vertical_align()
2313 }
2314}
2315
2316fn is_baseline_relative(vertical_align: VerticalAlign) -> bool {
2317 !matches!(
2318 vertical_align,
2319 VerticalAlign::Keyword(VerticalAlignKeyword::Top) |
2320 VerticalAlign::Keyword(VerticalAlignKeyword::Bottom)
2321 )
2322}
2323
2324fn inline_container_needs_strut(
2350 style: &ComputedValues,
2351 layout_context: &LayoutContext,
2352 pbm: Option<&PaddingBorderMargin>,
2353) -> bool {
2354 if layout_context.style_context.quirks_mode() == QuirksMode::NoQuirks {
2355 return true;
2356 }
2357
2358 if style.get_box().display.is_list_item() {
2361 return true;
2362 }
2363
2364 pbm.map(|pbm| !pbm.padding_border_sums.inline.is_zero())
2365 .unwrap_or(false)
2366}
2367
2368impl ComputeInlineContentSizes for InlineFormattingContext {
2369 fn compute_inline_content_sizes(
2373 &self,
2374 layout_context: &LayoutContext,
2375 constraint_space: &ConstraintSpace,
2376 ) -> InlineContentSizesResult {
2377 ContentSizesComputation::compute(self, layout_context, constraint_space)
2378 }
2379}
2380
2381struct ContentSizesComputation<'layout_data> {
2383 layout_context: &'layout_data LayoutContext<'layout_data>,
2384 constraint_space: &'layout_data ConstraintSpace<'layout_data>,
2385 paragraph: ContentSizes,
2386 current_line: ContentSizes,
2387 pending_whitespace: ContentSizes,
2389 had_content_yet_for_min_content: bool,
2392 had_content_yet_for_max_content: bool,
2395 ending_inline_pbm_stack: Vec<Au>,
2398 depends_on_block_constraints: bool,
2399}
2400
2401impl<'layout_data> ContentSizesComputation<'layout_data> {
2402 fn traverse(
2403 mut self,
2404 inline_formatting_context: &InlineFormattingContext,
2405 ) -> InlineContentSizesResult {
2406 self.add_inline_size(
2407 inline_formatting_context.inline_start_for_first_line(self.constraint_space.into()),
2408 );
2409 for inline_item in inline_formatting_context.inline_items.iter() {
2410 self.process_item(&inline_item.borrow(), inline_formatting_context);
2411 }
2412 self.forced_line_break();
2413
2414 InlineContentSizesResult {
2415 sizes: self.paragraph,
2416 depends_on_block_constraints: self.depends_on_block_constraints,
2417 }
2418 }
2419
2420 fn process_item(
2421 &mut self,
2422 inline_item: &InlineItem,
2423 inline_formatting_context: &InlineFormattingContext,
2424 ) {
2425 match inline_item {
2426 InlineItem::StartInlineBox(inline_box) => {
2427 let inline_box = inline_box.borrow();
2431 let zero = Au::zero();
2432 let writing_mode = self.constraint_space.style.writing_mode;
2433 let layout_style = inline_box.layout_style();
2434 let padding = layout_style
2435 .padding(writing_mode)
2436 .percentages_relative_to(zero);
2437 let border = layout_style.border_width(writing_mode);
2438 let margin = inline_box
2439 .base
2440 .style
2441 .margin(writing_mode)
2442 .percentages_relative_to(zero)
2443 .auto_is(Au::zero);
2444
2445 let pbm = margin + padding + border;
2446 if inline_box.is_first_split {
2447 self.add_inline_size(pbm.inline_start);
2448 }
2449 if inline_box.is_last_split {
2450 self.ending_inline_pbm_stack.push(pbm.inline_end);
2451 } else {
2452 self.ending_inline_pbm_stack.push(Au::zero());
2453 }
2454 },
2455 InlineItem::EndInlineBox => {
2456 let length = self.ending_inline_pbm_stack.pop().unwrap_or_else(Au::zero);
2457 self.add_inline_size(length);
2458 },
2459 InlineItem::TextRun(text_run) => {
2460 let text_run = &*text_run.borrow();
2461 let parent_style = text_run.inline_styles.style.borrow();
2462 for segment in text_run.shaped_text.iter() {
2463 let style_text = parent_style.get_inherited_text();
2464 let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap;
2465
2466 if can_wrap && segment.break_at_start && self.had_content_yet_for_min_content {
2469 self.line_break_opportunity()
2470 }
2471
2472 for run in segment.runs.iter() {
2473 let advance = run.glyph_store.total_advance();
2474 if run.glyph_store.is_whitespace() {
2475 if run.is_single_preserved_newline() {
2478 self.forced_line_break();
2479 continue;
2480 }
2481 if !matches!(
2482 style_text.white_space_collapse,
2483 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
2484 ) {
2485 if self.had_content_yet_for_min_content {
2486 if can_wrap {
2487 self.line_break_opportunity();
2488 } else {
2489 self.pending_whitespace.min_content += advance;
2490 }
2491 }
2492 if self.had_content_yet_for_max_content {
2493 self.pending_whitespace.max_content += advance;
2494 }
2495 continue;
2496 }
2497 if can_wrap {
2498 self.pending_whitespace.max_content += advance;
2499 self.commit_pending_whitespace();
2500 self.line_break_opportunity();
2501 continue;
2502 }
2503 }
2504
2505 self.commit_pending_whitespace();
2506 self.add_inline_size(advance);
2507
2508 if can_wrap && run.glyph_store.ends_with_whitespace() {
2513 self.line_break_opportunity();
2514 }
2515 }
2516 }
2517 },
2518 InlineItem::Atomic(atomic, offset_in_text, _level) => {
2519 let InlineContentSizesResult {
2520 sizes: outer,
2521 depends_on_block_constraints,
2522 } = atomic.borrow().outer_inline_content_sizes(
2523 self.layout_context,
2524 &self.constraint_space.into(),
2525 &LogicalVec2::zero(),
2526 false, );
2528 self.depends_on_block_constraints |= depends_on_block_constraints;
2529
2530 if self.had_content_yet_for_min_content &&
2532 !inline_formatting_context
2533 .previous_character_prevents_soft_wrap_opportunity(*offset_in_text)
2534 {
2535 self.line_break_opportunity();
2536 }
2537
2538 self.commit_pending_whitespace();
2539 self.current_line += outer;
2540
2541 if !inline_formatting_context
2543 .next_character_prevents_soft_wrap_opportunity(*offset_in_text)
2544 {
2545 self.line_break_opportunity();
2546 }
2547 },
2548 _ => {},
2549 }
2550 }
2551
2552 fn add_inline_size(&mut self, l: Au) {
2553 self.current_line.min_content += l;
2554 self.current_line.max_content += l;
2555 }
2556
2557 fn line_break_opportunity(&mut self) {
2558 self.pending_whitespace.min_content = Au::zero();
2562 let current_min_content = mem::take(&mut self.current_line.min_content);
2563 self.paragraph.min_content.max_assign(current_min_content);
2564 self.had_content_yet_for_min_content = false;
2565 }
2566
2567 fn forced_line_break(&mut self) {
2568 self.line_break_opportunity();
2570
2571 self.pending_whitespace.max_content = Au::zero();
2573 let current_max_content = mem::take(&mut self.current_line.max_content);
2574 self.paragraph.max_content.max_assign(current_max_content);
2575 self.had_content_yet_for_max_content = false;
2576 }
2577
2578 fn commit_pending_whitespace(&mut self) {
2579 self.current_line += mem::take(&mut self.pending_whitespace);
2580 self.had_content_yet_for_min_content = true;
2581 self.had_content_yet_for_max_content = true;
2582 }
2583
2584 fn compute(
2586 inline_formatting_context: &InlineFormattingContext,
2587 layout_context: &'layout_data LayoutContext,
2588 constraint_space: &'layout_data ConstraintSpace,
2589 ) -> InlineContentSizesResult {
2590 Self {
2591 layout_context,
2592 constraint_space,
2593 paragraph: ContentSizes::zero(),
2594 current_line: ContentSizes::zero(),
2595 pending_whitespace: ContentSizes::zero(),
2596 had_content_yet_for_min_content: false,
2597 had_content_yet_for_max_content: false,
2598 ending_inline_pbm_stack: Vec::new(),
2599 depends_on_block_constraints: false,
2600 }
2601 .traverse(inline_formatting_context)
2602 }
2603}
2604
2605fn char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character: char) -> bool {
2617 if character == '\u{00A0}' {
2618 return false;
2619 }
2620 let class = linebreak_property(character);
2621 class == XI_LINE_BREAKING_CLASS_GL ||
2622 class == XI_LINE_BREAKING_CLASS_WJ ||
2623 class == XI_LINE_BREAKING_CLASS_ZWJ
2624}