1pub mod construct;
72pub mod inline_box;
73pub mod line;
74mod line_breaker;
75pub mod text_run;
76
77use std::cell::{OnceCell, RefCell};
78use std::mem;
79use std::rc::Rc;
80use std::sync::Arc;
81
82use app_units::{Au, MAX_AU};
83use bitflags::bitflags;
84use construct::InlineFormattingContextBuilder;
85use fonts::{FontMetrics, FontRef, GlyphStore};
86use icu_segmenter::{LineBreakOptions, LineBreakStrictness, LineBreakWordOption};
87use inline_box::{InlineBox, InlineBoxContainerState, InlineBoxIdentifier, InlineBoxes};
88use layout_api::wrapper_traits::SharedSelection;
89use line::{
90 AbsolutelyPositionedLineItem, AtomicLineItem, FloatLineItem, LineItem, LineItemLayout,
91 TextRunLineItem,
92};
93use line_breaker::LineBreaker;
94use malloc_size_of_derive::MallocSizeOf;
95use script::layout_dom::ServoThreadSafeLayoutNode;
96use servo_arc::Arc as ServoArc;
97use style::Zero;
98use style::computed_values::line_break::T as LineBreak;
99use style::computed_values::text_wrap_mode::T as TextWrapMode;
100use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
101use style::computed_values::word_break::T as WordBreak;
102use style::context::{QuirksMode, SharedStyleContext};
103use style::properties::ComputedValues;
104use style::properties::style_structs::InheritedText;
105use style::values::computed::BaselineShift;
106use style::values::generics::box_::BaselineShiftKeyword;
107use style::values::generics::font::LineHeight;
108use style::values::specified::box_::BaselineSource;
109use style::values::specified::text::TextAlignKeyword;
110use style::values::specified::{AlignmentBaseline, TextAlignLast, TextJustify};
111use text_run::{
112 TextRun, XI_LINE_BREAKING_CLASS_GL, XI_LINE_BREAKING_CLASS_WJ, XI_LINE_BREAKING_CLASS_ZWJ,
113 get_font_for_first_font_for_style,
114};
115use unicode_bidi::{BidiInfo, Level};
116use xi_unicode::linebreak_property;
117
118use super::float::{Clear, PlacementAmongFloats};
119use super::{CacheableLayoutResult, IndependentFloatOrAtomicLayoutResult};
120use crate::cell::{ArcRefCell, WeakRefCell};
121use crate::context::LayoutContext;
122use crate::dom::WeakLayoutBox;
123use crate::dom_traversal::NodeAndStyleInfo;
124use crate::flow::float::{FloatBox, SequentialLayoutState};
125use crate::flow::inline::line::TextRunOffsets;
126use crate::flow::{
127 BlockContainer, CollapsibleWithParentStartMargin, FloatSide, PlacementState,
128 layout_in_flow_non_replaced_block_level_same_formatting_context,
129};
130use crate::formatting_contexts::{Baselines, IndependentFormattingContext};
131use crate::fragment_tree::{
132 BoxFragment, CollapsedMargin, Fragment, FragmentFlags, PositioningFragment,
133};
134use crate::geom::{LogicalRect, LogicalSides1D, LogicalVec2, ToLogical};
135use crate::layout_box_base::LayoutBoxBase;
136use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
137use crate::sizing::{
138 ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, outer_inline,
139};
140use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
141use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SharedStyle};
142
143static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
145static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
146
147#[derive(Debug, MallocSizeOf)]
148pub(crate) struct InlineFormattingContext {
149 inline_items: Vec<InlineItem>,
154
155 inline_boxes: InlineBoxes,
158
159 text_content: String,
161
162 shared_inline_styles: SharedInlineStyles,
165
166 has_first_formatted_line: bool,
169
170 pub(super) contains_floats: bool,
172
173 is_single_line_text_input: bool,
176
177 has_right_to_left_content: bool,
180
181 #[ignore_malloc_size_of = "This is stored primarily in the DOM"]
184 shared_selection: Option<SharedSelection>,
185}
186
187#[derive(Clone, Debug, MallocSizeOf)]
192pub(crate) struct SharedInlineStyles {
193 pub style: SharedStyle,
194 pub selected: SharedStyle,
195}
196
197impl SharedInlineStyles {
198 pub(crate) fn ptr_eq(&self, other: &Self) -> bool {
199 self.style.ptr_eq(&other.style) && self.selected.ptr_eq(&other.selected)
200 }
201}
202
203impl From<&NodeAndStyleInfo<'_>> for SharedInlineStyles {
204 fn from(info: &NodeAndStyleInfo) -> Self {
205 Self {
206 style: SharedStyle::new(info.style.clone()),
207 selected: SharedStyle::new(info.node.selected_style()),
208 }
209 }
210}
211
212#[derive(Debug, MallocSizeOf)]
216pub(crate) struct AnonymousBlockBox {
217 base: LayoutBoxBase,
218 contents: BlockContainer,
219}
220
221impl AnonymousBlockBox {
222 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
223 layout.process_soft_wrap_opportunity();
224 layout.commit_current_segment_to_line();
225 layout.process_line_break(true);
226 layout.current_line.for_block_level = true;
227
228 let fragment = layout
229 .positioning_context
230 .layout_maybe_position_relative_fragment(
231 layout.layout_context,
232 layout.placement_state.containing_block,
233 &self.base,
234 |positioning_context| {
235 layout_in_flow_non_replaced_block_level_same_formatting_context(
236 layout.layout_context,
237 positioning_context,
238 layout.placement_state.containing_block,
239 &self.base,
240 &self.contents,
241 layout.sequential_layout_state.as_deref_mut(),
242 Some(CollapsibleWithParentStartMargin(
243 layout
244 .placement_state
245 .next_in_flow_margin_collapses_with_parent_start_margin,
246 )),
247 LogicalSides1D::new(false, false),
251 )
252 },
253 );
254
255 layout.depends_on_block_constraints |= fragment.base.flags.contains(
258 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
259 );
260
261 let mut fragment = Fragment::Box(ArcRefCell::new(fragment));
262 layout.placement_state.place_fragment_and_update_baseline(
263 &mut fragment,
264 layout.sequential_layout_state.as_deref_mut(),
265 );
266
267 let Fragment::Box(fragment) = fragment else {
268 unreachable!("The fragment should still be a Fragment::Box()");
269 };
270 layout.push_line_item_to_unbreakable_segment(LineItem::AnonymousBlockBox(
271 layout.current_inline_box_identifier(),
272 fragment,
273 ));
274
275 layout.commit_current_segment_to_line();
276 layout.process_line_break(true);
277 layout.current_line.for_block_level = false;
278 }
279}
280
281#[derive(Clone, Debug, MallocSizeOf)]
282pub(crate) enum InlineItem {
283 StartInlineBox(ArcRefCell<InlineBox>),
284 EndInlineBox,
285 TextRun(ArcRefCell<TextRun>),
286 OutOfFlowAbsolutelyPositionedBox(
287 ArcRefCell<AbsolutelyPositionedBox>,
288 usize, ),
290 OutOfFlowFloatBox(ArcRefCell<FloatBox>),
291 Atomic(
292 ArcRefCell<IndependentFormattingContext>,
293 usize, Level, ),
296 AnonymousBlock(ArcRefCell<AnonymousBlockBox>),
297}
298
299impl InlineItem {
300 pub(crate) fn repair_style(
301 &self,
302 context: &SharedStyleContext,
303 node: &ServoThreadSafeLayoutNode,
304 new_style: &ServoArc<ComputedValues>,
305 ) {
306 match self {
307 InlineItem::StartInlineBox(inline_box) => {
308 inline_box.borrow_mut().repair_style(node, new_style);
309 },
310 InlineItem::EndInlineBox => {},
311 InlineItem::TextRun(..) => {},
314 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box
315 .borrow_mut()
316 .context
317 .repair_style(context, node, new_style),
318 InlineItem::OutOfFlowFloatBox(float_box) => float_box
319 .borrow_mut()
320 .contents
321 .repair_style(context, node, new_style),
322 InlineItem::Atomic(atomic, ..) => {
323 atomic.borrow_mut().repair_style(context, node, new_style)
324 },
325 InlineItem::AnonymousBlock(block_box) => {
326 let mut block_box = block_box.borrow_mut();
327 block_box.base.repair_style(new_style);
328 block_box.contents.repair_style(node, new_style);
329 },
330 }
331 }
332
333 pub(crate) fn with_base<T>(&self, callback: impl FnOnce(&LayoutBoxBase) -> T) -> T {
334 match self {
335 InlineItem::StartInlineBox(inline_box) => callback(&inline_box.borrow().base),
336 InlineItem::EndInlineBox | InlineItem::TextRun(..) => {
337 unreachable!("Should never have these kind of fragments attached to a DOM node")
338 },
339 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
340 callback(&positioned_box.borrow().context.base)
341 },
342 InlineItem::OutOfFlowFloatBox(float_box) => callback(&float_box.borrow().contents.base),
343 InlineItem::Atomic(independent_formatting_context, ..) => {
344 callback(&independent_formatting_context.borrow().base)
345 },
346 InlineItem::AnonymousBlock(block_box) => callback(&block_box.borrow().base),
347 }
348 }
349
350 pub(crate) fn with_base_mut<T>(&self, callback: impl FnOnce(&mut LayoutBoxBase) -> T) -> T {
351 match self {
352 InlineItem::StartInlineBox(inline_box) => callback(&mut inline_box.borrow_mut().base),
353 InlineItem::EndInlineBox | InlineItem::TextRun(..) => {
354 unreachable!("Should never have these kind of fragments attached to a DOM node")
355 },
356 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
357 callback(&mut positioned_box.borrow_mut().context.base)
358 },
359 InlineItem::OutOfFlowFloatBox(float_box) => {
360 callback(&mut float_box.borrow_mut().contents.base)
361 },
362 InlineItem::Atomic(independent_formatting_context, ..) => {
363 callback(&mut independent_formatting_context.borrow_mut().base)
364 },
365 InlineItem::AnonymousBlock(block_box) => callback(&mut block_box.borrow_mut().base),
366 }
367 }
368
369 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
370 match self {
371 Self::StartInlineBox(_) | InlineItem::EndInlineBox => {
372 },
375 Self::TextRun(_) => {
376 },
378 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
379 positioned_box.borrow().context.attached_to_tree(layout_box)
380 },
381 Self::OutOfFlowFloatBox(float_box) => {
382 float_box.borrow().contents.attached_to_tree(layout_box)
383 },
384 Self::Atomic(atomic, ..) => atomic.borrow().attached_to_tree(layout_box),
385 Self::AnonymousBlock(block_box) => {
386 block_box.borrow().contents.attached_to_tree(layout_box)
387 },
388 }
389 }
390
391 pub(crate) fn downgrade(&self) -> WeakInlineItem {
392 match self {
393 Self::StartInlineBox(inline_box) => {
394 WeakInlineItem::StartInlineBox(inline_box.downgrade())
395 },
396 Self::EndInlineBox => WeakInlineItem::EndInlineBox,
397 Self::TextRun(text_run) => WeakInlineItem::TextRun(text_run.downgrade()),
398 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
399 WeakInlineItem::OutOfFlowAbsolutelyPositionedBox(
400 positioned_box.downgrade(),
401 *offset_in_text,
402 )
403 },
404 Self::OutOfFlowFloatBox(float_box) => {
405 WeakInlineItem::OutOfFlowFloatBox(float_box.downgrade())
406 },
407 Self::Atomic(atomic, offset_in_text, bidi_level) => {
408 WeakInlineItem::Atomic(atomic.downgrade(), *offset_in_text, *bidi_level)
409 },
410 Self::AnonymousBlock(block_box) => {
411 WeakInlineItem::AnonymousBlock(block_box.downgrade())
412 },
413 }
414 }
415}
416
417#[derive(Clone, Debug, MallocSizeOf)]
418pub(crate) enum WeakInlineItem {
419 StartInlineBox(WeakRefCell<InlineBox>),
420 EndInlineBox,
421 TextRun(WeakRefCell<TextRun>),
422 OutOfFlowAbsolutelyPositionedBox(
423 WeakRefCell<AbsolutelyPositionedBox>,
424 usize, ),
426 OutOfFlowFloatBox(WeakRefCell<FloatBox>),
427 Atomic(
428 WeakRefCell<IndependentFormattingContext>,
429 usize, Level, ),
432 AnonymousBlock(WeakRefCell<AnonymousBlockBox>),
433}
434
435impl WeakInlineItem {
436 pub(crate) fn upgrade(&self) -> Option<InlineItem> {
437 Some(match self {
438 Self::StartInlineBox(inline_box) => InlineItem::StartInlineBox(inline_box.upgrade()?),
439 Self::EndInlineBox => InlineItem::EndInlineBox,
440 Self::TextRun(text_run) => InlineItem::TextRun(text_run.upgrade()?),
441 Self::OutOfFlowAbsolutelyPositionedBox(positioned_box, offset_in_text) => {
442 InlineItem::OutOfFlowAbsolutelyPositionedBox(
443 positioned_box.upgrade()?,
444 *offset_in_text,
445 )
446 },
447 Self::OutOfFlowFloatBox(float_box) => {
448 InlineItem::OutOfFlowFloatBox(float_box.upgrade()?)
449 },
450 Self::Atomic(atomic, offset_in_text, bidi_level) => {
451 InlineItem::Atomic(atomic.upgrade()?, *offset_in_text, *bidi_level)
452 },
453 Self::AnonymousBlock(block_box) => InlineItem::AnonymousBlock(block_box.upgrade()?),
454 })
455 }
456}
457
458struct LineUnderConstruction {
465 start_position: LogicalVec2<Au>,
468
469 inline_position: Au,
472
473 max_block_size: LineBlockSizes,
477
478 has_content: bool,
481
482 has_inline_pbm: bool,
485
486 has_floats_waiting_to_be_placed: bool,
490
491 placement_among_floats: OnceCell<LogicalRect<Au>>,
496
497 line_items: Vec<LineItem>,
500
501 for_block_level: bool,
503}
504
505impl LineUnderConstruction {
506 fn new(start_position: LogicalVec2<Au>) -> Self {
507 Self {
508 inline_position: start_position.inline,
509 start_position,
510 max_block_size: LineBlockSizes::zero(),
511 has_content: false,
512 has_inline_pbm: false,
513 has_floats_waiting_to_be_placed: false,
514 placement_among_floats: OnceCell::new(),
515 line_items: Vec::new(),
516 for_block_level: false,
517 }
518 }
519
520 fn replace_placement_among_floats(&mut self, new_placement: LogicalRect<Au>) {
521 self.placement_among_floats.take();
522 let _ = self.placement_among_floats.set(new_placement);
523 }
524
525 fn trim_trailing_whitespace(&mut self) -> Au {
527 let mut whitespace_trimmed = Au::zero();
532 for item in self.line_items.iter_mut().rev() {
533 if !item.trim_whitespace_at_end(&mut whitespace_trimmed) {
534 break;
535 }
536 }
537
538 whitespace_trimmed
539 }
540
541 fn count_justification_opportunities(&self) -> usize {
543 self.line_items
544 .iter()
545 .filter_map(|item| match item {
546 LineItem::TextRun(_, text_run) => Some(
547 text_run
548 .text
549 .iter()
550 .map(|glyph_store| glyph_store.total_word_separators())
551 .sum::<usize>(),
552 ),
553 _ => None,
554 })
555 .sum()
556 }
557}
558
559#[derive(Clone, Debug)]
565struct BaselineRelativeSize {
566 ascent: Au,
570
571 descent: Au,
575}
576
577impl BaselineRelativeSize {
578 fn zero() -> Self {
579 Self {
580 ascent: Au::zero(),
581 descent: Au::zero(),
582 }
583 }
584
585 fn max(&self, other: &Self) -> Self {
586 BaselineRelativeSize {
587 ascent: self.ascent.max(other.ascent),
588 descent: self.descent.max(other.descent),
589 }
590 }
591
592 fn adjust_for_nested_baseline_offset(&mut self, baseline_offset: Au) {
606 self.ascent -= baseline_offset;
607 self.descent += baseline_offset;
608 }
609}
610
611#[derive(Clone, Debug)]
612struct LineBlockSizes {
613 line_height: Au,
614 baseline_relative_size_for_line_height: Option<BaselineRelativeSize>,
615 size_for_baseline_positioning: BaselineRelativeSize,
616}
617
618impl LineBlockSizes {
619 fn zero() -> Self {
620 LineBlockSizes {
621 line_height: Au::zero(),
622 baseline_relative_size_for_line_height: None,
623 size_for_baseline_positioning: BaselineRelativeSize::zero(),
624 }
625 }
626
627 fn resolve(&self) -> Au {
628 let height_from_ascent_and_descent = self
629 .baseline_relative_size_for_line_height
630 .as_ref()
631 .map(|size| (size.ascent + size.descent).abs())
632 .unwrap_or_else(Au::zero);
633 self.line_height.max(height_from_ascent_and_descent)
634 }
635
636 fn max(&self, other: &LineBlockSizes) -> LineBlockSizes {
637 let baseline_relative_size = match (
638 self.baseline_relative_size_for_line_height.as_ref(),
639 other.baseline_relative_size_for_line_height.as_ref(),
640 ) {
641 (Some(our_size), Some(other_size)) => Some(our_size.max(other_size)),
642 (our_size, other_size) => our_size.or(other_size).cloned(),
643 };
644 Self {
645 line_height: self.line_height.max(other.line_height),
646 baseline_relative_size_for_line_height: baseline_relative_size,
647 size_for_baseline_positioning: self
648 .size_for_baseline_positioning
649 .max(&other.size_for_baseline_positioning),
650 }
651 }
652
653 fn max_assign(&mut self, other: &LineBlockSizes) {
654 *self = self.max(other);
655 }
656
657 fn adjust_for_baseline_offset(&mut self, baseline_offset: Au) {
658 if let Some(size) = self.baseline_relative_size_for_line_height.as_mut() {
659 size.adjust_for_nested_baseline_offset(baseline_offset)
660 }
661 self.size_for_baseline_positioning
662 .adjust_for_nested_baseline_offset(baseline_offset);
663 }
664
665 fn find_baseline_offset(&self) -> Au {
672 match self.baseline_relative_size_for_line_height.as_ref() {
673 Some(size) => size.ascent,
674 None => {
675 let leading = self.resolve() -
678 (self.size_for_baseline_positioning.ascent +
679 self.size_for_baseline_positioning.descent);
680 leading.scale_by(0.5) + self.size_for_baseline_positioning.ascent
681 },
682 }
683 }
684}
685
686struct UnbreakableSegmentUnderConstruction {
690 inline_size: Au,
692
693 max_block_size: LineBlockSizes,
696
697 line_items: Vec<LineItem>,
699
700 inline_box_hierarchy_depth: Option<usize>,
703
704 has_content: bool,
708
709 has_inline_pbm: bool,
712
713 trailing_whitespace_size: Au,
715}
716
717impl UnbreakableSegmentUnderConstruction {
718 fn new() -> Self {
719 Self {
720 inline_size: Au::zero(),
721 max_block_size: LineBlockSizes {
722 line_height: Au::zero(),
723 baseline_relative_size_for_line_height: None,
724 size_for_baseline_positioning: BaselineRelativeSize::zero(),
725 },
726 line_items: Vec::new(),
727 inline_box_hierarchy_depth: None,
728 has_content: false,
729 has_inline_pbm: false,
730 trailing_whitespace_size: Au::zero(),
731 }
732 }
733
734 fn reset(&mut self) {
736 assert!(self.line_items.is_empty()); self.inline_size = Au::zero();
738 self.max_block_size = LineBlockSizes::zero();
739 self.inline_box_hierarchy_depth = None;
740 self.has_content = false;
741 self.has_inline_pbm = false;
742 self.trailing_whitespace_size = Au::zero();
743 }
744
745 fn push_line_item(&mut self, line_item: LineItem, inline_box_hierarchy_depth: usize) {
750 if self.line_items.is_empty() {
751 self.inline_box_hierarchy_depth = Some(inline_box_hierarchy_depth);
752 }
753 self.line_items.push(line_item);
754 }
755
756 fn trim_leading_whitespace(&mut self) {
767 let mut whitespace_trimmed = Au::zero();
768 for item in self.line_items.iter_mut() {
769 if !item.trim_whitespace_at_start(&mut whitespace_trimmed) {
770 break;
771 }
772 }
773 self.inline_size -= whitespace_trimmed;
774 }
775}
776
777bitflags! {
778 struct InlineContainerStateFlags: u8 {
779 const CREATE_STRUT = 0b0001;
780 const IS_SINGLE_LINE_TEXT_INPUT = 0b0010;
781 }
782}
783
784struct InlineContainerState {
785 style: ServoArc<ComputedValues>,
787
788 flags: InlineContainerStateFlags,
790
791 has_content: RefCell<bool>,
794
795 strut_block_sizes: LineBlockSizes,
800
801 nested_strut_block_sizes: LineBlockSizes,
805
806 pub baseline_offset: Au,
812
813 font_metrics: Arc<FontMetrics>,
815}
816
817struct InlineFormattingContextLayout<'layout_data> {
818 positioning_context: &'layout_data mut PositioningContext,
819 placement_state: PlacementState<'layout_data>,
820 sequential_layout_state: Option<&'layout_data mut SequentialLayoutState>,
821 layout_context: &'layout_data LayoutContext<'layout_data>,
822
823 ifc: &'layout_data InlineFormattingContext,
825
826 root_nesting_level: InlineContainerState,
836
837 inline_box_state_stack: Vec<Rc<InlineBoxContainerState>>,
841
842 inline_box_states: Vec<Rc<InlineBoxContainerState>>,
847
848 fragments: Vec<Fragment>,
852
853 current_line: LineUnderConstruction,
855
856 current_line_segment: UnbreakableSegmentUnderConstruction,
858
859 linebreak_before_new_content: bool,
878
879 deferred_br_clear: Clear,
883
884 pub have_deferred_soft_wrap_opportunity: bool,
888
889 depends_on_block_constraints: bool,
892
893 white_space_collapse: WhiteSpaceCollapse,
898
899 text_wrap_mode: TextWrapMode,
904}
905
906impl InlineFormattingContextLayout<'_> {
907 fn current_inline_container_state(&self) -> &InlineContainerState {
908 match self.inline_box_state_stack.last() {
909 Some(inline_box_state) => &inline_box_state.base,
910 None => &self.root_nesting_level,
911 }
912 }
913
914 fn current_inline_box_identifier(&self) -> Option<InlineBoxIdentifier> {
915 self.inline_box_state_stack
916 .last()
917 .map(|state| state.identifier)
918 }
919
920 fn current_line_max_block_size_including_nested_containers(&self) -> LineBlockSizes {
921 self.current_inline_container_state()
922 .nested_strut_block_sizes
923 .max(&self.current_line.max_block_size)
924 }
925
926 fn current_line_block_start_considering_placement_among_floats(&self) -> Au {
927 self.current_line.placement_among_floats.get().map_or(
928 self.current_line.start_position.block,
929 |placement_among_floats| placement_among_floats.start_corner.block,
930 )
931 }
932
933 fn propagate_current_nesting_level_white_space_style(&mut self) {
934 let style = match self.inline_box_state_stack.last() {
935 Some(inline_box_state) => &inline_box_state.base.style,
936 None => self.placement_state.containing_block.style,
937 };
938 let style_text = style.get_inherited_text();
939 self.white_space_collapse = style_text.white_space_collapse;
940 self.text_wrap_mode = style_text.text_wrap_mode;
941 }
942
943 fn processing_br_element(&self) -> bool {
944 self.inline_box_state_stack.last().is_some_and(|state| {
945 state
946 .base_fragment_info
947 .flags
948 .contains(FragmentFlags::IS_BR_ELEMENT)
949 })
950 }
951
952 fn start_inline_box(&mut self, inline_box: &InlineBox) {
955 let containing_block = self.containing_block();
956 let inline_box_state = InlineBoxContainerState::new(
957 inline_box,
958 containing_block,
959 self.layout_context,
960 self.current_inline_container_state(),
961 inline_box
962 .default_font
963 .as_ref()
964 .map(|font| font.metrics.clone()),
965 );
966
967 self.depends_on_block_constraints |= inline_box
968 .base
969 .style
970 .depends_on_block_constraints_due_to_relative_positioning(
971 containing_block.style.writing_mode,
972 );
973
974 if inline_box_state
979 .base_fragment_info
980 .flags
981 .contains(FragmentFlags::IS_BR_ELEMENT) &&
982 self.deferred_br_clear == Clear::None
983 {
984 self.deferred_br_clear = Clear::from_style_and_container_writing_mode(
985 &inline_box_state.base.style,
986 self.containing_block().style.writing_mode,
987 );
988 }
989
990 let padding = inline_box_state.pbm.padding.inline_start;
991 let border = inline_box_state.pbm.border.inline_start;
992 let margin = inline_box_state.pbm.margin.inline_start.auto_is(Au::zero);
993 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
996 self.current_line_segment.has_inline_pbm = true;
997 }
998 self.current_line_segment.inline_size += padding + border + margin;
999 self.current_line_segment
1000 .line_items
1001 .push(LineItem::InlineStartBoxPaddingBorderMargin(
1002 inline_box.identifier,
1003 ));
1004
1005 let inline_box_state = Rc::new(inline_box_state);
1006
1007 assert_eq!(
1011 self.inline_box_states.len(),
1012 inline_box.identifier.index_in_inline_boxes as usize
1013 );
1014 self.inline_box_states.push(inline_box_state.clone());
1015 self.inline_box_state_stack.push(inline_box_state);
1016 }
1017
1018 fn finish_inline_box(&mut self) {
1021 let inline_box_state = match self.inline_box_state_stack.pop() {
1022 Some(inline_box_state) => inline_box_state,
1023 None => return, };
1025
1026 self.current_line_segment
1027 .max_block_size
1028 .max_assign(&inline_box_state.base.nested_strut_block_sizes);
1029
1030 if *inline_box_state.base.has_content.borrow() {
1035 self.propagate_current_nesting_level_white_space_style();
1036 }
1037
1038 let padding = inline_box_state.pbm.padding.inline_end;
1039 let border = inline_box_state.pbm.border.inline_end;
1040 let margin = inline_box_state.pbm.margin.inline_end.auto_is(Au::zero);
1041 if !padding.is_zero() || !border.is_zero() || !margin.is_zero() {
1044 self.current_line_segment.has_inline_pbm = true;
1045 }
1046 self.current_line_segment.inline_size += padding + border + margin;
1047 self.current_line_segment
1048 .line_items
1049 .push(LineItem::InlineEndBoxPaddingBorderMargin(
1050 inline_box_state.identifier,
1051 ))
1052 }
1053
1054 fn finish_last_line(&mut self) {
1055 self.process_soft_wrap_opportunity();
1061
1062 self.commit_current_segment_to_line();
1065
1066 self.finish_current_line_and_reset(true );
1069 }
1070
1071 fn finish_current_line_and_reset(&mut self, last_line_or_forced_line_break: bool) {
1075 let whitespace_trimmed = self.current_line.trim_trailing_whitespace();
1076 let (inline_start_position, justification_adjustment) = self
1077 .calculate_current_line_inline_start_and_justification_adjustment(
1078 whitespace_trimmed,
1079 last_line_or_forced_line_break,
1080 );
1081
1082 let is_phantom_line = !self.current_line.has_content && !self.current_line.has_inline_pbm;
1091 if !is_phantom_line {
1092 self.current_line.start_position.block += self.placement_state.current_margin.solve();
1093 self.placement_state.current_margin = CollapsedMargin::zero();
1094 }
1095 let block_start_position =
1096 self.current_line_block_start_considering_placement_among_floats();
1097
1098 let effective_block_advance = if is_phantom_line {
1099 LineBlockSizes::zero()
1100 } else {
1101 self.current_line_max_block_size_including_nested_containers()
1102 };
1103
1104 let resolved_block_advance = effective_block_advance.resolve();
1105 let block_end_position = if self.current_line.for_block_level {
1106 self.placement_state.current_block_direction_position
1107 } else {
1108 let mut block_end_position = block_start_position + resolved_block_advance;
1109 if let Some(sequential_layout_state) = self.sequential_layout_state.as_mut() {
1110 if !is_phantom_line {
1111 sequential_layout_state.collapse_margins();
1112 }
1113
1114 let increment = block_end_position - self.current_line.start_position.block;
1117 sequential_layout_state.advance_block_position(increment);
1118
1119 if let Some(clearance) = sequential_layout_state
1123 .calculate_clearance(self.deferred_br_clear, &CollapsedMargin::zero())
1124 {
1125 sequential_layout_state.advance_block_position(clearance);
1126 block_end_position += clearance;
1127 };
1128 self.deferred_br_clear = Clear::None;
1129 }
1130 block_end_position
1131 };
1132
1133 let mut line_to_layout = std::mem::replace(
1135 &mut self.current_line,
1136 LineUnderConstruction::new(LogicalVec2 {
1137 inline: Au::zero(),
1138 block: block_end_position,
1139 }),
1140 );
1141 if !line_to_layout.for_block_level {
1142 self.placement_state.current_block_direction_position = block_end_position;
1143 }
1144
1145 if line_to_layout.has_floats_waiting_to_be_placed {
1146 place_pending_floats(self, &mut line_to_layout.line_items);
1147 }
1148
1149 let start_position = LogicalVec2 {
1150 block: block_start_position,
1151 inline: inline_start_position,
1152 };
1153
1154 let baseline_offset = effective_block_advance.find_baseline_offset();
1155 let start_positioning_context_length = self.positioning_context.len();
1156 let fragments = LineItemLayout::layout_line_items(
1157 self,
1158 line_to_layout.line_items,
1159 start_position,
1160 &effective_block_advance,
1161 justification_adjustment,
1162 is_phantom_line,
1163 );
1164
1165 if !is_phantom_line {
1166 let baseline = baseline_offset + block_start_position;
1167 self.placement_state
1168 .inflow_baselines
1169 .first
1170 .get_or_insert(baseline);
1171 self.placement_state.inflow_baselines.last = Some(baseline);
1172 self.placement_state
1173 .next_in_flow_margin_collapses_with_parent_start_margin = false;
1174 }
1175
1176 if fragments.is_empty() &&
1178 self.positioning_context.len() == start_positioning_context_length
1179 {
1180 return;
1181 }
1182
1183 let start_corner = LogicalVec2 {
1187 inline: Au::zero(),
1188 block: block_start_position,
1189 };
1190
1191 let logical_origin_in_physical_coordinates =
1192 start_corner.to_physical_vector(self.containing_block().style.writing_mode);
1193 self.positioning_context
1194 .adjust_static_position_of_hoisted_fragments_with_offset(
1195 &logical_origin_in_physical_coordinates,
1196 start_positioning_context_length,
1197 );
1198
1199 let containing_block = self.containing_block();
1200 let physical_line_rect = LogicalRect {
1201 start_corner,
1202 size: LogicalVec2 {
1203 inline: containing_block.size.inline,
1204 block: effective_block_advance.resolve(),
1205 },
1206 }
1207 .as_physical(Some(containing_block));
1208 self.fragments
1209 .push(Fragment::Positioning(PositioningFragment::new_anonymous(
1210 self.root_nesting_level.style.clone(),
1211 physical_line_rect,
1212 fragments,
1213 )));
1214 }
1215
1216 fn calculate_current_line_inline_start_and_justification_adjustment(
1221 &self,
1222 whitespace_trimmed: Au,
1223 last_line_or_forced_line_break: bool,
1224 ) -> (Au, Au) {
1225 enum TextAlign {
1226 Start,
1227 Center,
1228 End,
1229 }
1230 let containing_block = self.containing_block();
1231 let style = containing_block.style;
1232 let mut text_align_keyword = style.clone_text_align();
1233
1234 if last_line_or_forced_line_break {
1235 text_align_keyword = match style.clone_text_align_last() {
1236 TextAlignLast::Auto if text_align_keyword == TextAlignKeyword::Justify => {
1237 TextAlignKeyword::Start
1238 },
1239 TextAlignLast::Auto => text_align_keyword,
1240 TextAlignLast::Start => TextAlignKeyword::Start,
1241 TextAlignLast::End => TextAlignKeyword::End,
1242 TextAlignLast::Left => TextAlignKeyword::Left,
1243 TextAlignLast::Right => TextAlignKeyword::Right,
1244 TextAlignLast::Center => TextAlignKeyword::Center,
1245 TextAlignLast::Justify => TextAlignKeyword::Justify,
1246 };
1247 }
1248
1249 let text_align = match text_align_keyword {
1250 TextAlignKeyword::Start => TextAlign::Start,
1251 TextAlignKeyword::Center | TextAlignKeyword::MozCenter => TextAlign::Center,
1252 TextAlignKeyword::End => TextAlign::End,
1253 TextAlignKeyword::Left | TextAlignKeyword::MozLeft => {
1254 if style.writing_mode.line_left_is_inline_start() {
1255 TextAlign::Start
1256 } else {
1257 TextAlign::End
1258 }
1259 },
1260 TextAlignKeyword::Right | TextAlignKeyword::MozRight => {
1261 if style.writing_mode.line_left_is_inline_start() {
1262 TextAlign::End
1263 } else {
1264 TextAlign::Start
1265 }
1266 },
1267 TextAlignKeyword::Justify => TextAlign::Start,
1268 };
1269
1270 let (line_start, available_space) = match self.current_line.placement_among_floats.get() {
1271 Some(placement_among_floats) => (
1272 placement_among_floats.start_corner.inline,
1273 placement_among_floats.size.inline,
1274 ),
1275 None => (Au::zero(), containing_block.size.inline),
1276 };
1277
1278 let text_indent = self.current_line.start_position.inline;
1285 let line_length = self.current_line.inline_position - whitespace_trimmed - text_indent;
1286 let adjusted_line_start = line_start +
1287 match text_align {
1288 TextAlign::Start => text_indent,
1289 TextAlign::End => (available_space - line_length).max(text_indent),
1290 TextAlign::Center => (available_space - line_length + text_indent)
1291 .scale_by(0.5)
1292 .max(text_indent),
1293 };
1294
1295 let text_justify = containing_block.style.clone_text_justify();
1299 let justification_adjustment = match (text_align_keyword, text_justify) {
1300 (TextAlignKeyword::Justify, TextJustify::None) => Au::zero(),
1303 (TextAlignKeyword::Justify, _) => {
1304 match self.current_line.count_justification_opportunities() {
1305 0 => Au::zero(),
1306 num_justification_opportunities => {
1307 (available_space - text_indent - line_length)
1308 .scale_by(1. / num_justification_opportunities as f32)
1309 },
1310 }
1311 },
1312 _ => Au::zero(),
1313 };
1314
1315 let justification_adjustment = justification_adjustment.max(Au::zero());
1318
1319 (adjusted_line_start, justification_adjustment)
1320 }
1321
1322 fn place_float_fragment(&mut self, fragment: &mut BoxFragment) {
1323 let state = self
1324 .sequential_layout_state
1325 .as_mut()
1326 .expect("Tried to lay out a float with no sequential placement state!");
1327
1328 let block_offset_from_containining_block_top = state
1329 .current_block_position_including_margins() -
1330 state.current_containing_block_offset();
1331 state.place_float_fragment(
1332 fragment,
1333 self.placement_state.containing_block,
1334 CollapsedMargin::zero(),
1335 block_offset_from_containining_block_top,
1336 );
1337 }
1338
1339 fn place_float_line_item_for_commit_to_line(
1348 &mut self,
1349 float_item: &mut FloatLineItem,
1350 line_inline_size_without_trailing_whitespace: Au,
1351 ) {
1352 let containing_block = self.containing_block();
1353 let mut float_fragment = float_item.fragment.borrow_mut();
1354 let logical_margin_rect_size = float_fragment
1355 .margin_rect()
1356 .size
1357 .to_logical(containing_block.style.writing_mode);
1358 let inline_size = logical_margin_rect_size.inline.max(Au::zero());
1359
1360 let available_inline_size = match self.current_line.placement_among_floats.get() {
1361 Some(placement_among_floats) => placement_among_floats.size.inline,
1362 None => containing_block.size.inline,
1363 } - line_inline_size_without_trailing_whitespace;
1364
1365 let has_content = self.current_line.has_content || self.current_line_segment.has_content;
1371 let fits_on_line = !has_content || inline_size <= available_inline_size;
1372 let needs_placement_later =
1373 self.current_line.has_floats_waiting_to_be_placed || !fits_on_line;
1374
1375 if needs_placement_later {
1376 self.current_line.has_floats_waiting_to_be_placed = true;
1377 } else {
1378 self.place_float_fragment(&mut float_fragment);
1379 float_item.needs_placement = false;
1380 }
1381
1382 let new_placement = self.place_line_among_floats(&LogicalVec2 {
1387 inline: line_inline_size_without_trailing_whitespace,
1388 block: self.current_line.max_block_size.resolve(),
1389 });
1390 self.current_line
1391 .replace_placement_among_floats(new_placement);
1392 }
1393
1394 fn place_line_among_floats(&self, potential_line_size: &LogicalVec2<Au>) -> LogicalRect<Au> {
1399 let sequential_layout_state = self
1400 .sequential_layout_state
1401 .as_ref()
1402 .expect("Should not have called this function without having floats.");
1403
1404 let ifc_offset_in_float_container = LogicalVec2 {
1405 inline: sequential_layout_state
1406 .floats
1407 .containing_block_info
1408 .inline_start,
1409 block: sequential_layout_state.current_containing_block_offset(),
1410 };
1411
1412 let ceiling = self.current_line_block_start_considering_placement_among_floats();
1413 let mut placement = PlacementAmongFloats::new(
1414 &sequential_layout_state.floats,
1415 ceiling + ifc_offset_in_float_container.block,
1416 LogicalVec2 {
1417 inline: potential_line_size.inline,
1418 block: potential_line_size.block,
1419 },
1420 &PaddingBorderMargin::zero(),
1421 );
1422
1423 let mut placement_rect = placement.place();
1424 placement_rect.start_corner -= ifc_offset_in_float_container;
1425 placement_rect
1426 }
1427
1428 fn new_potential_line_size_causes_line_break(
1435 &mut self,
1436 potential_line_size: &LogicalVec2<Au>,
1437 ) -> bool {
1438 let containing_block = self.containing_block();
1439 let available_line_space = if self.sequential_layout_state.is_some() {
1440 self.current_line
1441 .placement_among_floats
1442 .get_or_init(|| self.place_line_among_floats(potential_line_size))
1443 .size
1444 } else {
1445 LogicalVec2 {
1446 inline: containing_block.size.inline,
1447 block: MAX_AU,
1448 }
1449 };
1450
1451 let inline_would_overflow = potential_line_size.inline > available_line_space.inline;
1452 let block_would_overflow = potential_line_size.block > available_line_space.block;
1453
1454 let can_break = self.current_line.has_content;
1457
1458 if !can_break {
1464 if self.sequential_layout_state.is_some() &&
1467 (inline_would_overflow || block_would_overflow)
1468 {
1469 let new_placement = self.place_line_among_floats(potential_line_size);
1470 self.current_line
1471 .replace_placement_among_floats(new_placement);
1472 }
1473
1474 return false;
1475 }
1476
1477 if potential_line_size.inline > containing_block.size.inline {
1480 return true;
1481 }
1482
1483 if block_would_overflow {
1487 assert!(self.sequential_layout_state.is_some());
1489 let new_placement = self.place_line_among_floats(potential_line_size);
1490 if new_placement.start_corner.block !=
1491 self.current_line_block_start_considering_placement_among_floats()
1492 {
1493 return true;
1494 } else {
1495 self.current_line
1496 .replace_placement_among_floats(new_placement);
1497 return false;
1498 }
1499 }
1500
1501 inline_would_overflow
1505 }
1506
1507 fn defer_forced_line_break(&mut self) {
1508 if !self.unbreakable_segment_fits_on_line() {
1511 self.process_line_break(false );
1512 }
1513
1514 self.linebreak_before_new_content = true;
1516
1517 let line_is_empty =
1525 !self.current_line_segment.has_content && !self.current_line.has_content;
1526 if !self.processing_br_element() || line_is_empty {
1527 let strut_size = self
1528 .current_inline_container_state()
1529 .strut_block_sizes
1530 .clone();
1531 self.update_unbreakable_segment_for_new_content(
1532 &strut_size,
1533 Au::zero(),
1534 SegmentContentFlags::empty(),
1535 );
1536 }
1537 }
1538
1539 fn possibly_flush_deferred_forced_line_break(&mut self) {
1540 if !self.linebreak_before_new_content {
1541 return;
1542 }
1543
1544 self.commit_current_segment_to_line();
1545 self.process_line_break(true );
1546 self.linebreak_before_new_content = false;
1547 }
1548
1549 fn push_line_item_to_unbreakable_segment(&mut self, line_item: LineItem) {
1550 self.current_line_segment
1551 .push_line_item(line_item, self.inline_box_state_stack.len());
1552 }
1553
1554 fn push_glyph_store_to_unbreakable_segment(
1555 &mut self,
1556 glyph_store: Arc<GlyphStore>,
1557 text_run: &TextRun,
1558 font: &FontRef,
1559 bidi_level: Level,
1560 offsets: Option<TextRunOffsets>,
1561 ) {
1562 let inline_advance = glyph_store.total_advance();
1563 let flags = if glyph_store.is_whitespace() {
1564 SegmentContentFlags::from(text_run.inline_styles.style.borrow().get_inherited_text())
1565 } else {
1566 SegmentContentFlags::empty()
1567 };
1568
1569 let font_metrics = &font.metrics;
1573 let font_key = font.key(
1574 self.layout_context.painter_id,
1575 &self.layout_context.font_context,
1576 );
1577 let using_fallback_font = !Arc::ptr_eq(
1578 &self.current_inline_container_state().font_metrics,
1579 font_metrics,
1580 );
1581
1582 let quirks_mode = self.layout_context.style_context.quirks_mode() != QuirksMode::NoQuirks;
1583 let strut_size = if using_fallback_font {
1584 let container_state = self.current_inline_container_state();
1586 let baseline_shift = effective_baseline_shift(
1587 &container_state.style,
1588 self.inline_box_state_stack.last().map(|c| &c.base),
1589 );
1590 let mut block_size = container_state.get_block_size_contribution(
1591 baseline_shift,
1592 font_metrics,
1593 &container_state.font_metrics,
1594 );
1595 block_size.adjust_for_baseline_offset(container_state.baseline_offset);
1596 block_size
1597 } else if quirks_mode && !flags.is_collapsible_whitespace() {
1598 self.current_inline_container_state()
1603 .strut_block_sizes
1604 .clone()
1605 } else {
1606 LineBlockSizes::zero()
1607 };
1608 self.update_unbreakable_segment_for_new_content(&strut_size, inline_advance, flags);
1609
1610 let current_inline_box_identifier = self.current_inline_box_identifier();
1611 if let Some(LineItem::TextRun(inline_box_identifier, line_item)) =
1612 self.current_line_segment.line_items.last_mut()
1613 {
1614 if *inline_box_identifier == current_inline_box_identifier &&
1615 line_item.merge_if_possible(font_key, bidi_level, &glyph_store, &offsets)
1616 {
1617 return;
1618 }
1619 }
1620
1621 self.push_line_item_to_unbreakable_segment(LineItem::TextRun(
1622 current_inline_box_identifier,
1623 TextRunLineItem {
1624 text: vec![glyph_store],
1625 base_fragment_info: text_run.base_fragment_info,
1626 inline_styles: text_run.inline_styles.clone(),
1627 font_metrics: font_metrics.clone(),
1628 font_key,
1629 bidi_level,
1630 offsets: offsets.map(Box::new),
1631 },
1632 ));
1633 }
1634
1635 fn update_unbreakable_segment_for_new_content(
1636 &mut self,
1637 block_sizes_of_content: &LineBlockSizes,
1638 inline_size: Au,
1639 flags: SegmentContentFlags,
1640 ) {
1641 if flags.is_collapsible_whitespace() || flags.is_wrappable_and_hangable() {
1642 self.current_line_segment.trailing_whitespace_size = inline_size;
1643 } else {
1644 self.current_line_segment.trailing_whitespace_size = Au::zero();
1645 }
1646 if !flags.is_collapsible_whitespace() {
1647 self.current_line_segment.has_content = true;
1648 }
1649
1650 let container_max_block_size = &self
1652 .current_inline_container_state()
1653 .nested_strut_block_sizes
1654 .clone();
1655 self.current_line_segment
1656 .max_block_size
1657 .max_assign(container_max_block_size);
1658 self.current_line_segment
1659 .max_block_size
1660 .max_assign(block_sizes_of_content);
1661
1662 self.current_line_segment.inline_size += inline_size;
1663
1664 *self
1666 .current_inline_container_state()
1667 .has_content
1668 .borrow_mut() = true;
1669 self.propagate_current_nesting_level_white_space_style();
1670 }
1671
1672 fn process_line_break(&mut self, forced_line_break: bool) {
1673 self.current_line_segment.trim_leading_whitespace();
1674 self.finish_current_line_and_reset(forced_line_break);
1675 }
1676
1677 fn unbreakable_segment_fits_on_line(&mut self) -> bool {
1678 let potential_line_size = LogicalVec2 {
1679 inline: self.current_line.inline_position + self.current_line_segment.inline_size -
1680 self.current_line_segment.trailing_whitespace_size,
1681 block: self
1682 .current_line_max_block_size_including_nested_containers()
1683 .max(&self.current_line_segment.max_block_size)
1684 .resolve(),
1685 };
1686
1687 !self.new_potential_line_size_causes_line_break(&potential_line_size)
1688 }
1689
1690 fn process_soft_wrap_opportunity(&mut self) {
1694 if self.current_line_segment.line_items.is_empty() {
1695 return;
1696 }
1697 if self.text_wrap_mode == TextWrapMode::Nowrap {
1698 return;
1699 }
1700 if !self.unbreakable_segment_fits_on_line() {
1701 self.process_line_break(false );
1702 }
1703 self.commit_current_segment_to_line();
1704 }
1705
1706 fn commit_current_segment_to_line(&mut self) {
1709 if self.current_line_segment.line_items.is_empty() && !self.current_line_segment.has_content
1712 {
1713 return;
1714 }
1715
1716 if !self.current_line.has_content {
1717 self.current_line_segment.trim_leading_whitespace();
1718 }
1719
1720 self.current_line.inline_position += self.current_line_segment.inline_size;
1721 self.current_line.max_block_size = self
1722 .current_line_max_block_size_including_nested_containers()
1723 .max(&self.current_line_segment.max_block_size);
1724 let line_inline_size_without_trailing_whitespace =
1725 self.current_line.inline_position - self.current_line_segment.trailing_whitespace_size;
1726
1727 let mut segment_items = mem::take(&mut self.current_line_segment.line_items);
1729 for item in segment_items.iter_mut() {
1730 if let LineItem::Float(_, float_item) = item {
1731 self.place_float_line_item_for_commit_to_line(
1732 float_item,
1733 line_inline_size_without_trailing_whitespace,
1734 );
1735 }
1736 }
1737
1738 if self.current_line.line_items.is_empty() {
1743 let will_break = self.new_potential_line_size_causes_line_break(&LogicalVec2 {
1744 inline: line_inline_size_without_trailing_whitespace,
1745 block: self.current_line_segment.max_block_size.resolve(),
1746 });
1747 assert!(!will_break);
1748 }
1749
1750 self.current_line.line_items.extend(segment_items);
1751 self.current_line.has_content |= self.current_line_segment.has_content;
1752 self.current_line.has_inline_pbm |= self.current_line_segment.has_inline_pbm;
1753
1754 self.current_line_segment.reset();
1755 }
1756
1757 #[inline]
1758 fn containing_block(&self) -> &ContainingBlock<'_> {
1759 self.placement_state.containing_block
1760 }
1761}
1762
1763bitflags! {
1764 struct SegmentContentFlags: u8 {
1765 const COLLAPSIBLE_WHITESPACE = 0b00000001;
1766 const WRAPPABLE_AND_HANGABLE_WHITESPACE = 0b00000010;
1767 }
1768}
1769
1770impl SegmentContentFlags {
1771 fn is_collapsible_whitespace(&self) -> bool {
1772 self.contains(Self::COLLAPSIBLE_WHITESPACE)
1773 }
1774
1775 fn is_wrappable_and_hangable(&self) -> bool {
1776 self.contains(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE)
1777 }
1778}
1779
1780impl From<&InheritedText> for SegmentContentFlags {
1781 fn from(style_text: &InheritedText) -> Self {
1782 let mut flags = Self::empty();
1783
1784 if !matches!(
1787 style_text.white_space_collapse,
1788 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
1789 ) {
1790 flags.insert(Self::COLLAPSIBLE_WHITESPACE);
1791 }
1792
1793 if style_text.text_wrap_mode == TextWrapMode::Wrap &&
1796 style_text.white_space_collapse != WhiteSpaceCollapse::BreakSpaces
1797 {
1798 flags.insert(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE);
1799 }
1800 flags
1801 }
1802}
1803
1804impl InlineFormattingContext {
1805 #[servo_tracing::instrument(name = "InlineFormattingContext::new_with_builder", skip_all)]
1806 fn new_with_builder(
1807 mut builder: InlineFormattingContextBuilder,
1808 layout_context: &LayoutContext,
1809 has_first_formatted_line: bool,
1810 is_single_line_text_input: bool,
1811 starting_bidi_level: Level,
1812 ) -> Self {
1813 let text_content: String = builder.text_segments.into_iter().collect();
1815
1816 let bidi_info = BidiInfo::new(&text_content, Some(starting_bidi_level));
1817 let has_right_to_left_content = bidi_info.has_rtl();
1818 let shared_inline_styles = builder
1819 .shared_inline_styles_stack
1820 .last()
1821 .expect("Should have at least one SharedInlineStyle for the root of an IFC")
1822 .clone();
1823 let (word_break, line_break) = {
1824 let styles = shared_inline_styles.style.borrow();
1825 let text_style = styles.get_inherited_text();
1826 (text_style.word_break, text_style.line_break)
1827 };
1828
1829 let mut options = LineBreakOptions::default();
1830
1831 options.strictness = match line_break {
1832 LineBreak::Loose => LineBreakStrictness::Loose,
1833 LineBreak::Normal => LineBreakStrictness::Normal,
1834 LineBreak::Strict => LineBreakStrictness::Strict,
1835 LineBreak::Anywhere => LineBreakStrictness::Anywhere,
1836 LineBreak::Auto => LineBreakStrictness::Normal,
1839 };
1840 options.word_option = match word_break {
1841 WordBreak::Normal => LineBreakWordOption::Normal,
1842 WordBreak::BreakAll => LineBreakWordOption::BreakAll,
1843 WordBreak::KeepAll => LineBreakWordOption::KeepAll,
1844 };
1845 options.ja_zh = false; let mut new_linebreaker = LineBreaker::new(text_content.as_str(), options);
1848 for item in &mut builder.inline_items {
1849 match item {
1850 InlineItem::TextRun(text_run) => {
1851 text_run.borrow_mut().segment_and_shape(
1852 &text_content,
1853 layout_context,
1854 &mut new_linebreaker,
1855 &bidi_info,
1856 );
1857 },
1858 InlineItem::StartInlineBox(inline_box) => {
1859 let inline_box = &mut *inline_box.borrow_mut();
1860 if let Some(font) = get_font_for_first_font_for_style(
1861 &inline_box.base.style,
1862 &layout_context.font_context,
1863 ) {
1864 inline_box.default_font = Some(font);
1865 }
1866 },
1867 InlineItem::Atomic(_, index_in_text, bidi_level) => {
1868 *bidi_level = bidi_info.levels[*index_in_text];
1869 },
1870 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) |
1871 InlineItem::OutOfFlowFloatBox(_) |
1872 InlineItem::EndInlineBox |
1873 InlineItem::AnonymousBlock { .. } => {},
1874 }
1875 }
1876
1877 InlineFormattingContext {
1878 text_content,
1879 inline_items: builder.inline_items,
1880 inline_boxes: builder.inline_boxes,
1881 shared_inline_styles,
1882 has_first_formatted_line,
1883 contains_floats: builder.contains_floats,
1884 is_single_line_text_input,
1885 has_right_to_left_content,
1886 shared_selection: builder.shared_selection,
1887 }
1888 }
1889
1890 pub(crate) fn repair_style(
1891 &self,
1892 node: &ServoThreadSafeLayoutNode,
1893 new_style: &ServoArc<ComputedValues>,
1894 ) {
1895 *self.shared_inline_styles.style.borrow_mut() = new_style.clone();
1896 *self.shared_inline_styles.selected.borrow_mut() = node.selected_style();
1897 }
1898
1899 fn inline_start_for_first_line(&self, containing_block: IndefiniteContainingBlock) -> Au {
1900 if !self.has_first_formatted_line {
1901 return Au::zero();
1902 }
1903 containing_block
1904 .style
1905 .get_inherited_text()
1906 .text_indent
1907 .length
1908 .to_used_value(containing_block.size.inline.unwrap_or_default())
1909 }
1910
1911 pub(super) fn layout(
1912 &self,
1913 layout_context: &LayoutContext,
1914 positioning_context: &mut PositioningContext,
1915 containing_block: &ContainingBlock,
1916 sequential_layout_state: Option<&mut SequentialLayoutState>,
1917 collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
1918 ) -> CacheableLayoutResult {
1919 for inline_box in self.inline_boxes.iter() {
1921 inline_box.borrow().base.clear_fragments();
1922 }
1923
1924 let style = containing_block.style;
1925
1926 let default_font_metrics =
1929 get_font_for_first_font_for_style(style, &layout_context.font_context)
1930 .map(|font| font.metrics.clone());
1931
1932 let style_text = containing_block.style.get_inherited_text();
1933 let mut inline_container_state_flags = InlineContainerStateFlags::empty();
1934 if inline_container_needs_strut(style, layout_context, None) {
1935 inline_container_state_flags.insert(InlineContainerStateFlags::CREATE_STRUT);
1936 }
1937 if self.is_single_line_text_input {
1938 inline_container_state_flags
1939 .insert(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT);
1940 }
1941 let placement_state =
1942 PlacementState::new(collapsible_with_parent_start_margin, containing_block);
1943
1944 let mut layout = InlineFormattingContextLayout {
1945 positioning_context,
1946 placement_state,
1947 sequential_layout_state,
1948 layout_context,
1949 ifc: self,
1950 fragments: Vec::new(),
1951 current_line: LineUnderConstruction::new(LogicalVec2 {
1952 inline: self.inline_start_for_first_line(containing_block.into()),
1953 block: Au::zero(),
1954 }),
1955 root_nesting_level: InlineContainerState::new(
1956 style.to_arc(),
1957 inline_container_state_flags,
1958 None, default_font_metrics,
1960 ),
1961 inline_box_state_stack: Vec::new(),
1962 inline_box_states: Vec::with_capacity(self.inline_boxes.len()),
1963 current_line_segment: UnbreakableSegmentUnderConstruction::new(),
1964 linebreak_before_new_content: false,
1965 deferred_br_clear: Clear::None,
1966 have_deferred_soft_wrap_opportunity: false,
1967 depends_on_block_constraints: false,
1968 white_space_collapse: style_text.white_space_collapse,
1969 text_wrap_mode: style_text.text_wrap_mode,
1970 };
1971
1972 for item in self.inline_items.iter() {
1973 if !matches!(item, InlineItem::EndInlineBox) {
1975 layout.possibly_flush_deferred_forced_line_break();
1976 }
1977
1978 match item {
1979 InlineItem::StartInlineBox(inline_box) => {
1980 layout.start_inline_box(&inline_box.borrow());
1981 },
1982 InlineItem::EndInlineBox => layout.finish_inline_box(),
1983 InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout),
1984 InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
1985 atomic_formatting_context.borrow().layout_into_line_items(
1986 &mut layout,
1987 *offset_in_text,
1988 *bidi_level,
1989 );
1990 },
1991 InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, _) => {
1992 layout.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
1993 layout.current_inline_box_identifier(),
1994 AbsolutelyPositionedLineItem {
1995 absolutely_positioned_box: positioned_box.clone(),
1996 },
1997 ));
1998 },
1999 InlineItem::OutOfFlowFloatBox(float_box) => {
2000 float_box.borrow().layout_into_line_items(&mut layout);
2001 },
2002 InlineItem::AnonymousBlock(block_box) => {
2003 block_box.borrow().layout_into_line_items(&mut layout);
2004 },
2005 }
2006 }
2007
2008 layout.finish_last_line();
2009 let (content_block_size, collapsible_margins_in_children, baselines) =
2010 layout.placement_state.finish();
2011
2012 CacheableLayoutResult {
2013 fragments: layout.fragments,
2014 content_block_size,
2015 collapsible_margins_in_children,
2016 baselines,
2017 depends_on_block_constraints: layout.depends_on_block_constraints,
2018 content_inline_size_for_table: None,
2019 specific_layout_info: None,
2020 }
2021 }
2022
2023 fn next_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
2024 let Some(character) = self.text_content[index..].chars().nth(1) else {
2025 return false;
2026 };
2027 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2028 }
2029
2030 fn previous_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
2031 let Some(character) = self.text_content[0..index].chars().next_back() else {
2032 return false;
2033 };
2034 char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
2035 }
2036
2037 pub(crate) fn find_block_margin_collapsing_with_parent(
2038 &self,
2039 layout_context: &LayoutContext,
2040 collected_margin: &mut CollapsedMargin,
2041 containing_block_for_children: &ContainingBlock,
2042 ) -> bool {
2043 let mut nesting_levels_from_nonzero_end_pbm: u32 = 1;
2049 let mut items_iter = self.inline_items.iter();
2050 items_iter.all(|inline_item| match inline_item {
2051 InlineItem::StartInlineBox(inline_box) => {
2052 let pbm = inline_box
2053 .borrow()
2054 .layout_style()
2055 .padding_border_margin(containing_block_for_children);
2056 if pbm.padding.inline_end.is_zero() &&
2057 pbm.border.inline_end.is_zero() &&
2058 pbm.margin.inline_end.auto_is(Au::zero).is_zero()
2059 {
2060 nesting_levels_from_nonzero_end_pbm += 1;
2061 } else {
2062 nesting_levels_from_nonzero_end_pbm = 0;
2063 }
2064 pbm.padding.inline_start.is_zero() &&
2065 pbm.border.inline_start.is_zero() &&
2066 pbm.margin.inline_start.auto_is(Au::zero).is_zero()
2067 },
2068 InlineItem::EndInlineBox => {
2069 if nesting_levels_from_nonzero_end_pbm == 0 {
2070 false
2071 } else {
2072 nesting_levels_from_nonzero_end_pbm -= 1;
2073 true
2074 }
2075 },
2076 InlineItem::TextRun(text_run) => {
2077 let text_run = &*text_run.borrow();
2078 let parent_style = text_run.inline_styles.style.borrow();
2079 text_run.shaped_text.iter().all(|segment| {
2080 segment.runs.iter().all(|run| {
2081 run.is_whitespace() &&
2082 !run.is_single_preserved_newline() &&
2083 !matches!(
2084 parent_style.get_inherited_text().white_space_collapse,
2085 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
2086 )
2087 })
2088 })
2089 },
2090 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => true,
2091 InlineItem::OutOfFlowFloatBox(..) => true,
2092 InlineItem::Atomic(..) => false,
2093 InlineItem::AnonymousBlock(block_box) => block_box
2094 .borrow()
2095 .contents
2096 .find_block_margin_collapsing_with_parent(
2097 layout_context,
2098 collected_margin,
2099 containing_block_for_children,
2100 ),
2101 })
2102 }
2103
2104 pub(crate) fn attached_to_tree(&self, layout_box: WeakLayoutBox) {
2105 let mut parent_box_stack = Vec::new();
2106 let current_parent_box = |parent_box_stack: &[WeakLayoutBox]| {
2107 parent_box_stack.last().unwrap_or(&layout_box).clone()
2108 };
2109 for inline_item in &self.inline_items {
2110 match inline_item {
2111 InlineItem::StartInlineBox(inline_box) => {
2112 inline_box
2113 .borrow_mut()
2114 .base
2115 .parent_box
2116 .replace(current_parent_box(&parent_box_stack));
2117 parent_box_stack.push(WeakLayoutBox::InlineLevel(
2118 WeakInlineItem::StartInlineBox(inline_box.downgrade()),
2119 ));
2120 },
2121 InlineItem::EndInlineBox => {
2122 parent_box_stack.pop();
2123 },
2124 InlineItem::TextRun(text_run) => {
2125 text_run
2126 .borrow_mut()
2127 .parent_box
2128 .replace(current_parent_box(&parent_box_stack));
2129 },
2130 _ => inline_item.with_base_mut(|base| {
2131 base.parent_box
2132 .replace(current_parent_box(&parent_box_stack));
2133 }),
2134 }
2135 }
2136 }
2137}
2138
2139impl InlineContainerState {
2140 fn new(
2141 style: ServoArc<ComputedValues>,
2142 flags: InlineContainerStateFlags,
2143 parent_container: Option<&InlineContainerState>,
2144 font_metrics: Option<Arc<FontMetrics>>,
2145 ) -> Self {
2146 let font_metrics = font_metrics.unwrap_or_else(FontMetrics::empty);
2147 let mut baseline_offset = Au::zero();
2148 let mut strut_block_sizes = Self::get_block_sizes_with_style(
2149 effective_baseline_shift(&style, parent_container),
2150 &style,
2151 &font_metrics,
2152 &font_metrics,
2153 &flags,
2154 );
2155 if let Some(parent_container) = parent_container {
2156 baseline_offset = parent_container.get_cumulative_baseline_offset_for_child(
2159 style.clone_alignment_baseline(),
2160 style.clone_baseline_shift(),
2161 &strut_block_sizes,
2162 );
2163 strut_block_sizes.adjust_for_baseline_offset(baseline_offset);
2164 }
2165
2166 let mut nested_block_sizes = parent_container
2167 .map(|container| container.nested_strut_block_sizes.clone())
2168 .unwrap_or_else(LineBlockSizes::zero);
2169 if flags.contains(InlineContainerStateFlags::CREATE_STRUT) {
2170 nested_block_sizes.max_assign(&strut_block_sizes);
2171 }
2172
2173 Self {
2174 style,
2175 flags,
2176 has_content: RefCell::new(false),
2177 nested_strut_block_sizes: nested_block_sizes,
2178 strut_block_sizes,
2179 baseline_offset,
2180 font_metrics,
2181 }
2182 }
2183
2184 fn get_block_sizes_with_style(
2185 baseline_shift: BaselineShift,
2186 style: &ComputedValues,
2187 font_metrics: &FontMetrics,
2188 font_metrics_of_first_font: &FontMetrics,
2189 flags: &InlineContainerStateFlags,
2190 ) -> LineBlockSizes {
2191 let line_height = line_height(style, font_metrics, flags);
2192
2193 if !is_baseline_relative(baseline_shift) {
2194 return LineBlockSizes {
2195 line_height,
2196 baseline_relative_size_for_line_height: None,
2197 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2198 };
2199 }
2200
2201 let mut ascent = font_metrics.ascent;
2210 let mut descent = font_metrics.descent;
2211 if style.get_font().line_height == LineHeight::Normal {
2212 let half_leading_from_line_gap =
2213 (font_metrics.line_gap - descent - ascent).scale_by(0.5);
2214 ascent += half_leading_from_line_gap;
2215 descent += half_leading_from_line_gap;
2216 }
2217
2218 let size_for_baseline_positioning = BaselineRelativeSize { ascent, descent };
2222
2223 if style.get_font().line_height != LineHeight::Normal {
2239 ascent = font_metrics_of_first_font.ascent;
2240 descent = font_metrics_of_first_font.descent;
2241 let half_leading = (line_height - (ascent + descent)).scale_by(0.5);
2242 ascent += half_leading;
2247 descent = line_height - ascent;
2248 }
2249
2250 LineBlockSizes {
2251 line_height,
2252 baseline_relative_size_for_line_height: Some(BaselineRelativeSize { ascent, descent }),
2253 size_for_baseline_positioning,
2254 }
2255 }
2256
2257 fn get_block_size_contribution(
2258 &self,
2259 baseline_shift: BaselineShift,
2260 font_metrics: &FontMetrics,
2261 font_metrics_of_first_font: &FontMetrics,
2262 ) -> LineBlockSizes {
2263 Self::get_block_sizes_with_style(
2264 baseline_shift,
2265 &self.style,
2266 font_metrics,
2267 font_metrics_of_first_font,
2268 &self.flags,
2269 )
2270 }
2271
2272 fn get_cumulative_baseline_offset_for_child(
2273 &self,
2274 child_alignment_baseline: AlignmentBaseline,
2275 child_baseline_shift: BaselineShift,
2276 child_block_size: &LineBlockSizes,
2277 ) -> Au {
2278 let block_size = self.get_block_size_contribution(
2279 child_baseline_shift.clone(),
2280 &self.font_metrics,
2281 &self.font_metrics,
2282 );
2283 self.baseline_offset +
2284 match child_alignment_baseline {
2285 AlignmentBaseline::Baseline => Au::zero(),
2286 AlignmentBaseline::TextTop => {
2287 child_block_size.size_for_baseline_positioning.ascent - self.font_metrics.ascent
2288 },
2289 AlignmentBaseline::Middle => {
2290 (child_block_size.size_for_baseline_positioning.ascent -
2293 child_block_size.size_for_baseline_positioning.descent -
2294 self.font_metrics.x_height)
2295 .scale_by(0.5)
2296 },
2297 AlignmentBaseline::TextBottom => {
2298 self.font_metrics.descent -
2299 child_block_size.size_for_baseline_positioning.descent
2300 },
2301 } +
2302 match child_baseline_shift {
2303 BaselineShift::Keyword(
2308 BaselineShiftKeyword::Top |
2309 BaselineShiftKeyword::Bottom |
2310 BaselineShiftKeyword::Center,
2311 ) => Au::zero(),
2312 BaselineShift::Keyword(BaselineShiftKeyword::Sub) => {
2313 block_size.resolve().scale_by(FONT_SUBSCRIPT_OFFSET_RATIO)
2314 },
2315 BaselineShift::Keyword(BaselineShiftKeyword::Super) => {
2316 -block_size.resolve().scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO)
2317 },
2318 BaselineShift::Length(length_percentage) => {
2319 -length_percentage.to_used_value(child_block_size.line_height)
2320 },
2321 }
2322 }
2323}
2324
2325impl IndependentFormattingContext {
2326 fn layout_into_line_items(
2327 &self,
2328 layout: &mut InlineFormattingContextLayout,
2329 offset_in_text: usize,
2330 bidi_level: Level,
2331 ) {
2332 let mut child_positioning_context = PositioningContext::default();
2334 let IndependentFloatOrAtomicLayoutResult {
2335 mut fragment,
2336 baselines,
2337 pbm_sums,
2338 } = self.layout_float_or_atomic_inline(
2339 layout.layout_context,
2340 &mut child_positioning_context,
2341 layout.containing_block(),
2342 );
2343
2344 layout.depends_on_block_constraints |= fragment.base.flags.contains(
2347 FragmentFlags::SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM,
2348 );
2349
2350 let container_writing_mode = layout.containing_block().style.writing_mode;
2352 let pbm_physical_offset = pbm_sums
2353 .start_offset()
2354 .to_physical_size(container_writing_mode);
2355 fragment.base.rect.origin += pbm_physical_offset.to_vector();
2356
2357 fragment = fragment.with_baselines(baselines);
2359
2360 let positioning_context = if self.is_replaced() {
2363 None
2364 } else {
2365 if fragment
2366 .style()
2367 .establishes_containing_block_for_absolute_descendants(fragment.base.flags)
2368 {
2369 child_positioning_context
2370 .layout_collected_children(layout.layout_context, &mut fragment);
2371 }
2372 Some(child_positioning_context)
2373 };
2374
2375 if layout.text_wrap_mode == TextWrapMode::Wrap &&
2376 !layout
2377 .ifc
2378 .previous_character_prevents_soft_wrap_opportunity(offset_in_text)
2379 {
2380 layout.process_soft_wrap_opportunity();
2381 }
2382
2383 let size = pbm_sums.sum() + fragment.base.rect.size.to_logical(container_writing_mode);
2384 let baseline_offset = self
2385 .pick_baseline(&fragment.baselines(container_writing_mode))
2386 .map(|baseline| pbm_sums.block_start + baseline)
2387 .unwrap_or(size.block);
2388
2389 let (block_sizes, baseline_offset_in_parent) =
2390 self.get_block_sizes_and_baseline_offset(layout, size.block, baseline_offset);
2391 layout.update_unbreakable_segment_for_new_content(
2392 &block_sizes,
2393 size.inline,
2394 SegmentContentFlags::empty(),
2395 );
2396
2397 let fragment = ArcRefCell::new(fragment);
2398 self.base.set_fragment(Fragment::Box(fragment.clone()));
2399
2400 layout.push_line_item_to_unbreakable_segment(LineItem::Atomic(
2401 layout.current_inline_box_identifier(),
2402 AtomicLineItem {
2403 fragment,
2404 size,
2405 positioning_context,
2406 baseline_offset_in_parent,
2407 baseline_offset_in_item: baseline_offset,
2408 bidi_level,
2409 },
2410 ));
2411
2412 if !layout
2415 .ifc
2416 .next_character_prevents_soft_wrap_opportunity(offset_in_text)
2417 {
2418 layout.have_deferred_soft_wrap_opportunity = true;
2419 }
2420 }
2421
2422 fn pick_baseline(&self, baselines: &Baselines) -> Option<Au> {
2426 match self.style().clone_baseline_source() {
2427 BaselineSource::First => baselines.first,
2428 BaselineSource::Last => baselines.last,
2429 BaselineSource::Auto if self.is_block_container() => baselines.last,
2430 BaselineSource::Auto => baselines.first,
2431 }
2432 }
2433
2434 fn get_block_sizes_and_baseline_offset(
2435 &self,
2436 ifc: &InlineFormattingContextLayout,
2437 block_size: Au,
2438 baseline_offset_in_content_area: Au,
2439 ) -> (LineBlockSizes, Au) {
2440 let mut contribution = if !is_baseline_relative(self.style().clone_baseline_shift()) {
2441 LineBlockSizes {
2442 line_height: block_size,
2443 baseline_relative_size_for_line_height: None,
2444 size_for_baseline_positioning: BaselineRelativeSize::zero(),
2445 }
2446 } else {
2447 let baseline_relative_size = BaselineRelativeSize {
2448 ascent: baseline_offset_in_content_area,
2449 descent: block_size - baseline_offset_in_content_area,
2450 };
2451 LineBlockSizes {
2452 line_height: block_size,
2453 baseline_relative_size_for_line_height: Some(baseline_relative_size.clone()),
2454 size_for_baseline_positioning: baseline_relative_size,
2455 }
2456 };
2457
2458 let style = self.style();
2459 let baseline_offset = ifc
2460 .current_inline_container_state()
2461 .get_cumulative_baseline_offset_for_child(
2462 style.clone_alignment_baseline(),
2463 style.clone_baseline_shift(),
2464 &contribution,
2465 );
2466 contribution.adjust_for_baseline_offset(baseline_offset);
2467
2468 (contribution, baseline_offset)
2469 }
2470}
2471
2472impl FloatBox {
2473 fn layout_into_line_items(&self, layout: &mut InlineFormattingContextLayout) {
2474 let fragment = ArcRefCell::new(self.layout(
2475 layout.layout_context,
2476 layout.positioning_context,
2477 layout.placement_state.containing_block,
2478 ));
2479
2480 self.contents
2481 .base
2482 .set_fragment(Fragment::Box(fragment.clone()));
2483 layout.push_line_item_to_unbreakable_segment(LineItem::Float(
2484 layout.current_inline_box_identifier(),
2485 FloatLineItem {
2486 fragment,
2487 needs_placement: true,
2488 },
2489 ));
2490 }
2491}
2492
2493fn place_pending_floats(ifc: &mut InlineFormattingContextLayout, line_items: &mut [LineItem]) {
2494 for item in line_items.iter_mut() {
2495 if let LineItem::Float(_, float_line_item) = item {
2496 if float_line_item.needs_placement {
2497 ifc.place_float_fragment(&mut float_line_item.fragment.borrow_mut());
2498 }
2499 }
2500 }
2501}
2502
2503fn line_height(
2504 parent_style: &ComputedValues,
2505 font_metrics: &FontMetrics,
2506 flags: &InlineContainerStateFlags,
2507) -> Au {
2508 let font = parent_style.get_font();
2509 let font_size = font.font_size.computed_size();
2510 let mut line_height = match font.line_height {
2511 LineHeight::Normal => font_metrics.line_gap,
2512 LineHeight::Number(number) => (font_size * number.0).into(),
2513 LineHeight::Length(length) => length.0.into(),
2514 };
2515
2516 if flags.contains(InlineContainerStateFlags::IS_SINGLE_LINE_TEXT_INPUT) {
2520 line_height.max_assign(font_metrics.line_gap);
2521 }
2522
2523 line_height
2524}
2525
2526fn effective_baseline_shift(
2527 style: &ComputedValues,
2528 container: Option<&InlineContainerState>,
2529) -> BaselineShift {
2530 if container.is_none() {
2531 BaselineShift::zero()
2535 } else {
2536 style.clone_baseline_shift()
2537 }
2538}
2539
2540fn is_baseline_relative(baseline_shift: BaselineShift) -> bool {
2541 !matches!(
2542 baseline_shift,
2543 BaselineShift::Keyword(
2544 BaselineShiftKeyword::Top | BaselineShiftKeyword::Bottom | BaselineShiftKeyword::Center
2545 )
2546 )
2547}
2548
2549fn inline_container_needs_strut(
2575 style: &ComputedValues,
2576 layout_context: &LayoutContext,
2577 pbm: Option<&PaddingBorderMargin>,
2578) -> bool {
2579 if layout_context.style_context.quirks_mode() == QuirksMode::NoQuirks {
2580 return true;
2581 }
2582
2583 if style.get_box().display.is_list_item() {
2586 return true;
2587 }
2588
2589 pbm.is_some_and(|pbm| !pbm.padding_border_sums.inline.is_zero())
2590}
2591
2592impl ComputeInlineContentSizes for InlineFormattingContext {
2593 fn compute_inline_content_sizes(
2597 &self,
2598 layout_context: &LayoutContext,
2599 constraint_space: &ConstraintSpace,
2600 ) -> InlineContentSizesResult {
2601 ContentSizesComputation::compute(self, layout_context, constraint_space)
2602 }
2603}
2604
2605struct ContentSizesComputation<'layout_data> {
2607 layout_context: &'layout_data LayoutContext<'layout_data>,
2608 constraint_space: &'layout_data ConstraintSpace<'layout_data>,
2609 paragraph: ContentSizes,
2610 current_line: ContentSizes,
2611 pending_whitespace: ContentSizes,
2613 uncleared_floats: LogicalSides1D<ContentSizes>,
2615 cleared_floats: LogicalSides1D<ContentSizes>,
2617 had_content_yet_for_min_content: bool,
2620 had_content_yet_for_max_content: bool,
2623 ending_inline_pbm_stack: Vec<Au>,
2626 depends_on_block_constraints: bool,
2627}
2628
2629impl<'layout_data> ContentSizesComputation<'layout_data> {
2630 fn traverse(
2631 mut self,
2632 inline_formatting_context: &InlineFormattingContext,
2633 ) -> InlineContentSizesResult {
2634 self.add_inline_size(
2635 inline_formatting_context.inline_start_for_first_line(self.constraint_space.into()),
2636 );
2637 for inline_item in &inline_formatting_context.inline_items {
2638 self.process_item(inline_item, inline_formatting_context);
2639 }
2640 self.forced_line_break();
2641 self.flush_floats();
2642
2643 InlineContentSizesResult {
2644 sizes: self.paragraph,
2645 depends_on_block_constraints: self.depends_on_block_constraints,
2646 }
2647 }
2648
2649 fn process_item(
2650 &mut self,
2651 inline_item: &InlineItem,
2652 inline_formatting_context: &InlineFormattingContext,
2653 ) {
2654 match inline_item {
2655 InlineItem::StartInlineBox(inline_box) => {
2656 let inline_box = inline_box.borrow();
2660 let zero = Au::zero();
2661 let writing_mode = self.constraint_space.style.writing_mode;
2662 let layout_style = inline_box.layout_style();
2663 let padding = layout_style
2664 .padding(writing_mode)
2665 .percentages_relative_to(zero);
2666 let border = layout_style.border_width(writing_mode);
2667 let margin = inline_box
2668 .base
2669 .style
2670 .margin(writing_mode)
2671 .percentages_relative_to(zero)
2672 .auto_is(Au::zero);
2673
2674 let pbm = margin + padding + border;
2675 self.add_inline_size(pbm.inline_start);
2676 self.ending_inline_pbm_stack.push(pbm.inline_end);
2677 },
2678 InlineItem::EndInlineBox => {
2679 let length = self.ending_inline_pbm_stack.pop().unwrap_or_else(Au::zero);
2680 self.add_inline_size(length);
2681 },
2682 InlineItem::TextRun(text_run) => {
2683 let text_run = &*text_run.borrow();
2684 let parent_style = text_run.inline_styles.style.borrow();
2685 for segment in text_run.shaped_text.iter() {
2686 let style_text = parent_style.get_inherited_text();
2687 let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap;
2688
2689 let break_at_start =
2692 segment.break_at_start && self.had_content_yet_for_min_content;
2693
2694 for (run_index, run) in segment.runs.iter().enumerate() {
2695 if can_wrap && (run_index != 0 || break_at_start) {
2698 self.line_break_opportunity();
2699 }
2700
2701 let advance = run.total_advance();
2702 if run.is_whitespace() {
2703 if run.is_single_preserved_newline() {
2706 self.forced_line_break();
2707 continue;
2708 }
2709 if !matches!(
2710 style_text.white_space_collapse,
2711 WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
2712 ) {
2713 if self.had_content_yet_for_min_content {
2714 if can_wrap {
2715 self.line_break_opportunity();
2716 } else {
2717 self.pending_whitespace.min_content += advance;
2718 }
2719 }
2720 if self.had_content_yet_for_max_content {
2721 self.pending_whitespace.max_content += advance;
2722 }
2723 continue;
2724 }
2725 if can_wrap {
2726 self.pending_whitespace.max_content += advance;
2727 self.commit_pending_whitespace();
2728 self.line_break_opportunity();
2729 continue;
2730 }
2731 }
2732
2733 self.commit_pending_whitespace();
2734 self.add_inline_size(advance);
2735
2736 if can_wrap && run.ends_with_whitespace() {
2741 self.line_break_opportunity();
2742 }
2743 }
2744 }
2745 },
2746 InlineItem::Atomic(atomic, offset_in_text, _level) => {
2747 if self.had_content_yet_for_min_content &&
2749 !inline_formatting_context
2750 .previous_character_prevents_soft_wrap_opportunity(*offset_in_text)
2751 {
2752 self.line_break_opportunity();
2753 }
2754
2755 self.commit_pending_whitespace();
2756 let outer = self.outer_inline_content_sizes_of_float_or_atomic(&atomic.borrow());
2757 self.current_line += outer;
2758
2759 if !inline_formatting_context
2761 .next_character_prevents_soft_wrap_opportunity(*offset_in_text)
2762 {
2763 self.line_break_opportunity();
2764 }
2765 },
2766 InlineItem::OutOfFlowFloatBox(float_box) => {
2767 let float_box = float_box.borrow();
2768 let sizes = self.outer_inline_content_sizes_of_float_or_atomic(&float_box.contents);
2769 let style = &float_box.contents.style();
2770 let container_writing_mode = self.constraint_space.style.writing_mode;
2771 let clear =
2772 Clear::from_style_and_container_writing_mode(style, container_writing_mode);
2773 self.clear_floats(clear);
2774 let float_side =
2775 FloatSide::from_style_and_container_writing_mode(style, container_writing_mode);
2776 match float_side.expect("A float box needs to float to some side") {
2777 FloatSide::InlineStart => self.uncleared_floats.start.union_assign(&sizes),
2778 FloatSide::InlineEnd => self.uncleared_floats.end.union_assign(&sizes),
2779 }
2780 },
2781 InlineItem::AnonymousBlock(block) => {
2782 self.forced_line_break();
2783 self.flush_floats();
2784 let borrowed_block = block.borrow();
2785 let AnonymousBlockBox {
2786 ref base,
2787 ref contents,
2788 ..
2789 } = *borrowed_block;
2790 let inline_content_sizes_result = outer_inline(
2791 base,
2792 &contents.layout_style(base),
2793 &self.constraint_space.into(),
2794 &LogicalVec2::zero(),
2795 false, false, false, |_| None, |constraint_space| {
2800 base.inline_content_sizes(self.layout_context, constraint_space, contents)
2801 },
2802 |_aspect_ratio| None,
2803 );
2804 self.depends_on_block_constraints |=
2805 inline_content_sizes_result.depends_on_block_constraints;
2806 self.current_line = inline_content_sizes_result.sizes;
2807 self.forced_line_break();
2808 },
2809 InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => {},
2810 }
2811 }
2812
2813 fn add_inline_size(&mut self, l: Au) {
2814 self.current_line.min_content += l;
2815 self.current_line.max_content += l;
2816 }
2817
2818 fn line_break_opportunity(&mut self) {
2819 self.pending_whitespace.min_content = Au::zero();
2823 let current_min_content = mem::take(&mut self.current_line.min_content);
2824 self.paragraph.min_content.max_assign(current_min_content);
2825 self.had_content_yet_for_min_content = false;
2826 }
2827
2828 fn forced_line_break(&mut self) {
2829 self.line_break_opportunity();
2831
2832 self.pending_whitespace.max_content = Au::zero();
2834 let current_max_content = mem::take(&mut self.current_line.max_content);
2835 self.paragraph.max_content.max_assign(current_max_content);
2836 self.had_content_yet_for_max_content = false;
2837 }
2838
2839 fn commit_pending_whitespace(&mut self) {
2840 self.current_line += mem::take(&mut self.pending_whitespace);
2841 self.had_content_yet_for_min_content = true;
2842 self.had_content_yet_for_max_content = true;
2843 }
2844
2845 fn outer_inline_content_sizes_of_float_or_atomic(
2846 &mut self,
2847 context: &IndependentFormattingContext,
2848 ) -> ContentSizes {
2849 let result = context.outer_inline_content_sizes(
2850 self.layout_context,
2851 &self.constraint_space.into(),
2852 &LogicalVec2::zero(),
2853 false, );
2855 self.depends_on_block_constraints |= result.depends_on_block_constraints;
2856 result.sizes
2857 }
2858
2859 fn clear_floats(&mut self, clear: Clear) {
2860 match clear {
2861 Clear::InlineStart => {
2862 let start_floats = mem::take(&mut self.uncleared_floats.start);
2863 self.cleared_floats.start.max_assign(start_floats);
2864 },
2865 Clear::InlineEnd => {
2866 let end_floats = mem::take(&mut self.uncleared_floats.end);
2867 self.cleared_floats.end.max_assign(end_floats);
2868 },
2869 Clear::Both => {
2870 let start_floats = mem::take(&mut self.uncleared_floats.start);
2871 let end_floats = mem::take(&mut self.uncleared_floats.end);
2872 self.cleared_floats.start.max_assign(start_floats);
2873 self.cleared_floats.end.max_assign(end_floats);
2874 },
2875 Clear::None => {},
2876 }
2877 }
2878
2879 fn flush_floats(&mut self) {
2880 self.clear_floats(Clear::Both);
2881 let start_floats = mem::take(&mut self.cleared_floats.start);
2882 let end_floats = mem::take(&mut self.cleared_floats.end);
2883 self.paragraph.union_assign(&start_floats);
2884 self.paragraph.union_assign(&end_floats);
2885 }
2886
2887 fn compute(
2889 inline_formatting_context: &InlineFormattingContext,
2890 layout_context: &'layout_data LayoutContext,
2891 constraint_space: &'layout_data ConstraintSpace,
2892 ) -> InlineContentSizesResult {
2893 Self {
2894 layout_context,
2895 constraint_space,
2896 paragraph: ContentSizes::zero(),
2897 current_line: ContentSizes::zero(),
2898 pending_whitespace: ContentSizes::zero(),
2899 uncleared_floats: LogicalSides1D::default(),
2900 cleared_floats: LogicalSides1D::default(),
2901 had_content_yet_for_min_content: false,
2902 had_content_yet_for_max_content: false,
2903 ending_inline_pbm_stack: Vec::new(),
2904 depends_on_block_constraints: false,
2905 }
2906 .traverse(inline_formatting_context)
2907 }
2908}
2909
2910fn char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character: char) -> bool {
2922 if character == '\u{00A0}' {
2923 return false;
2924 }
2925 let class = linebreak_property(character);
2926 class == XI_LINE_BREAKING_CLASS_GL ||
2927 class == XI_LINE_BREAKING_CLASS_WJ ||
2928 class == XI_LINE_BREAKING_CLASS_ZWJ
2929}