1use app_units::Au;
6use layout_api::AxesOverflow;
7use malloc_size_of_derive::MallocSizeOf;
8use style::Zero;
9use style::color::AbsoluteColor;
10use style::computed_values::direction::T as Direction;
11use style::computed_values::isolation::T as ComputedIsolation;
12use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode;
13use style::computed_values::position::T as ComputedPosition;
14use style::computed_values::transform_style::T as ComputedTransformStyle;
15use style::computed_values::unicode_bidi::T as UnicodeBidi;
16use style::logical_geometry::{Direction as AxisDirection, PhysicalSide, WritingMode};
17use style::properties::ComputedValues;
18use style::properties::longhands::backface_visibility::computed_value::T as BackfaceVisiblity;
19use style::properties::longhands::box_sizing::computed_value::T as BoxSizing;
20use style::properties::longhands::column_span::computed_value::T as ColumnSpan;
21use style::properties::style_structs::Border;
22use style::servo::selector_parser::PseudoElement;
23use style::values::CSSFloat;
24use style::values::computed::basic_shape::ClipPath;
25use style::values::computed::image::Image as ComputedImageLayer;
26use style::values::computed::{
27 BorderSideWidth, BorderStyle, Color, Inset, ItemPlacement, LengthPercentage, Margin,
28 SelfAlignment,
29};
30use style::values::generics::box_::Perspective;
31use style::values::generics::position::{GenericAspectRatio, PreferredRatio};
32use style::values::generics::transform::{GenericRotate, GenericScale, GenericTranslate};
33use style::values::specified::align::AlignFlags;
34use style::values::specified::{Overflow, WillChangeBits, box_ as stylo};
35use unicode_bidi::Level;
36use webrender_api as wr;
37use webrender_api::units::LayoutTransform;
38
39use crate::dom_traversal::Contents;
40use crate::fragment_tree::FragmentFlags;
41use crate::geom::{
42 AuOrAuto, LengthPercentageOrAuto, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalSides,
43 PhysicalSize,
44};
45use crate::sizing::{Size, Sizes};
46use crate::table::TableLayoutStyle;
47use crate::{ContainingBlock, IndefiniteContainingBlock};
48
49#[derive(Clone, Copy, Eq, PartialEq)]
50pub(crate) enum Display {
51 None,
52 Contents,
53 GeneratingBox(DisplayGeneratingBox),
54}
55
56#[derive(Clone, Copy, Debug, Eq, PartialEq)]
57pub(crate) enum DisplayGeneratingBox {
58 OutsideInside {
59 outside: DisplayOutside,
60 inside: DisplayInside,
61 },
62 LayoutInternal(DisplayLayoutInternal),
64}
65impl DisplayGeneratingBox {
66 pub(crate) fn display_inside(&self) -> DisplayInside {
67 match *self {
68 DisplayGeneratingBox::OutsideInside { inside, .. } => inside,
69 DisplayGeneratingBox::LayoutInternal(layout_internal) => {
70 layout_internal.display_inside()
71 },
72 }
73 }
74
75 pub(crate) fn used_value_for_contents(&self, contents: &Contents) -> Self {
76 if matches!(self, Self::LayoutInternal(_)) && contents.is_replaced() {
81 Self::OutsideInside {
82 outside: DisplayOutside::Inline,
83 inside: DisplayInside::Flow {
84 is_list_item: false,
85 },
86 }
87 } else if matches!(contents, Contents::Widget(_)) {
88 if let DisplayGeneratingBox::OutsideInside { outside, .. } = self {
92 DisplayGeneratingBox::OutsideInside {
93 outside: *outside,
94 inside: DisplayInside::FlowRoot {
95 is_list_item: false,
96 },
97 }
98 } else {
99 *self
100 }
101 } else {
102 *self
103 }
104 }
105}
106
107#[derive(Clone, Copy, Debug, Eq, PartialEq)]
108pub(crate) enum DisplayOutside {
109 Block,
110 Inline,
111}
112
113#[derive(Clone, Copy, Debug, Eq, PartialEq)]
114pub(crate) enum DisplayInside {
115 Flow { is_list_item: bool },
118 FlowRoot { is_list_item: bool },
119 Flex,
120 Grid,
121 Table,
122}
123
124#[derive(Clone, Copy, Debug, Eq, PartialEq)]
125#[expect(clippy::enum_variant_names)]
126pub(crate) enum DisplayLayoutInternal {
128 TableCaption,
129 TableCell,
130 TableColumn,
131 TableColumnGroup,
132 TableFooterGroup,
133 TableHeaderGroup,
134 TableRow,
135 TableRowGroup,
136}
137
138impl DisplayLayoutInternal {
139 pub(crate) fn display_inside(&self) -> DisplayInside {
141 DisplayInside::FlowRoot {
145 is_list_item: false,
146 }
147 }
148}
149
150#[derive(Clone, Debug)]
152pub(crate) struct PaddingBorderMargin {
153 pub padding: LogicalSides<Au>,
154 pub border: LogicalSides<Au>,
155 pub margin: LogicalSides<AuOrAuto>,
156
157 pub padding_border_sums: LogicalVec2<Au>,
159}
160
161impl PaddingBorderMargin {
162 pub(crate) fn zero() -> Self {
163 Self {
164 padding: LogicalSides::zero(),
165 border: LogicalSides::zero(),
166 margin: LogicalSides::zero(),
167 padding_border_sums: LogicalVec2::zero(),
168 }
169 }
170
171 pub(crate) fn sums_auto_is_zero(
172 &self,
173 ignore_block_margins: LogicalSides1D<bool>,
174 ) -> LogicalVec2<Au> {
175 let margin = self.margin.auto_is(Au::zero);
176 let mut sums = self.padding_border_sums;
177 sums.inline += margin.inline_sum();
178 if !ignore_block_margins.start {
179 sums.block += margin.block_start;
180 }
181 if !ignore_block_margins.end {
182 sums.block += margin.block_end;
183 }
184 sums
185 }
186}
187
188#[derive(Clone, Copy, Debug)]
192pub(crate) struct AspectRatio {
193 box_sizing_adjustment: LogicalVec2<Au>,
198 i_over_b: CSSFloat,
200}
201
202impl AspectRatio {
203 pub(crate) fn compute_dependent_size(
205 &self,
206 ratio_dependent_axis: AxisDirection,
207 ratio_determining_size: Au,
208 ) -> Au {
209 match ratio_dependent_axis {
210 AxisDirection::Inline => {
212 (ratio_determining_size + self.box_sizing_adjustment.block).scale_by(self.i_over_b) -
213 self.box_sizing_adjustment.inline
214 },
215 AxisDirection::Block => {
217 (ratio_determining_size + self.box_sizing_adjustment.inline)
218 .scale_by(1.0 / self.i_over_b) -
219 self.box_sizing_adjustment.block
220 },
221 }
222 }
223
224 pub(crate) fn from_logical_content_ratio(i_over_b: CSSFloat) -> Self {
225 Self {
226 box_sizing_adjustment: LogicalVec2::zero(),
227 i_over_b,
228 }
229 }
230}
231
232#[derive(Clone)]
233pub(crate) struct ContentBoxSizesAndPBM {
234 pub content_box_sizes: LogicalVec2<Sizes>,
235 pub pbm: PaddingBorderMargin,
236 pub depends_on_block_constraints: bool,
237 pub preferred_size_computes_to_auto: LogicalVec2<bool>,
238}
239
240#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
241pub(crate) struct BorderStyleColor {
242 pub style: BorderStyle,
243 pub color: AbsoluteColor,
244}
245
246impl BorderStyleColor {
247 pub(crate) fn new(style: BorderStyle, color: AbsoluteColor) -> Self {
248 Self { style, color }
249 }
250
251 pub(crate) fn from_border(
252 border: &Border,
253 current_color: &AbsoluteColor,
254 ) -> PhysicalSides<Self> {
255 let resolve = |color: &Color| color.resolve_to_absolute(current_color);
256 PhysicalSides::<Self>::new(
257 Self::new(border.border_top_style, resolve(&border.border_top_color)),
258 Self::new(
259 border.border_right_style,
260 resolve(&border.border_right_color),
261 ),
262 Self::new(
263 border.border_bottom_style,
264 resolve(&border.border_bottom_color),
265 ),
266 Self::new(border.border_left_style, resolve(&border.border_left_color)),
267 )
268 }
269
270 pub(crate) fn hidden() -> Self {
271 Self::new(BorderStyle::Hidden, AbsoluteColor::TRANSPARENT_BLACK)
272 }
273}
274
275impl Default for BorderStyleColor {
276 fn default() -> Self {
277 Self::new(BorderStyle::None, AbsoluteColor::TRANSPARENT_BLACK)
278 }
279}
280
281pub(crate) struct OverflowDirection {
285 pub rightward: bool,
287 pub downward: bool,
289}
290
291pub(crate) trait ComputedValuesExt {
292 fn physical_box_offsets(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>>;
293 fn box_offsets(&self, writing_mode: WritingMode) -> LogicalSides<LengthPercentageOrAuto<'_>>;
294 fn box_size(
295 &self,
296 containing_block_writing_mode: WritingMode,
297 ) -> LogicalVec2<Size<LengthPercentage>>;
298 fn min_box_size(
299 &self,
300 containing_block_writing_mode: WritingMode,
301 ) -> LogicalVec2<Size<LengthPercentage>>;
302 fn max_box_size(
303 &self,
304 containing_block_writing_mode: WritingMode,
305 ) -> LogicalVec2<Size<LengthPercentage>>;
306 fn content_box_size_for_box_size(
307 &self,
308 box_size: LogicalVec2<Size<Au>>,
309 pbm: &PaddingBorderMargin,
310 ) -> LogicalVec2<Size<Au>>;
311 fn content_min_box_size_for_min_size(
312 &self,
313 box_size: LogicalVec2<Size<Au>>,
314 pbm: &PaddingBorderMargin,
315 ) -> LogicalVec2<Size<Au>>;
316 fn content_max_box_size_for_max_size(
317 &self,
318 box_size: LogicalVec2<Size<Au>>,
319 pbm: &PaddingBorderMargin,
320 ) -> LogicalVec2<Size<Au>>;
321 fn border_style_color(
322 &self,
323 containing_block_writing_mode: WritingMode,
324 ) -> LogicalSides<BorderStyleColor>;
325 fn physical_margin(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>>;
326 fn margin(
327 &self,
328 containing_block_writing_mode: WritingMode,
329 ) -> LogicalSides<LengthPercentageOrAuto<'_>>;
330 fn is_transformable(&self, fragment_flags: FragmentFlags) -> bool;
331 fn has_transform_or_perspective_style(&self) -> bool;
332 fn has_effective_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool;
333 fn z_index_applies(&self, fragment_flags: FragmentFlags) -> bool;
334 fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32;
335 fn effective_overflow(&self, fragment_flags: FragmentFlags) -> AxesOverflow;
336 fn establishes_block_formatting_context(&self, fragment_flags: FragmentFlags) -> bool;
337 fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool;
338 fn establishes_scroll_container(&self, fragment_flags: FragmentFlags) -> bool;
339 fn establishes_containing_block_for_absolute_descendants(
340 &self,
341 fragment_flags: FragmentFlags,
342 ) -> bool;
343 fn establishes_containing_block_for_all_descendants(
344 &self,
345 fragment_flags: FragmentFlags,
346 ) -> bool;
347 fn preferred_aspect_ratio(
348 &self,
349 natural_aspect_ratio: Option<CSSFloat>,
350 padding_border_sums: &LogicalVec2<Au>,
351 ) -> Option<AspectRatio>;
352 fn background_is_transparent(&self) -> bool;
353 fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags;
354 fn bidi_control_chars(&self) -> (&'static str, &'static str);
355 fn resolve_align_self(
356 &self,
357 resolved_auto_value: ItemPlacement,
358 resolved_normal_value: AlignFlags,
359 ) -> SelfAlignment;
360 fn depends_on_block_constraints_due_to_relative_positioning(
361 &self,
362 writing_mode: WritingMode,
363 ) -> bool;
364 fn is_inline_box(&self, fragment_flags: FragmentFlags) -> bool;
365 fn is_atomic_inline_level(&self, fragment_flags: FragmentFlags) -> bool;
366 fn overflow_direction(&self) -> OverflowDirection;
367 fn to_bidi_level(&self) -> Level;
368}
369
370impl ComputedValuesExt for ComputedValues {
371 fn physical_box_offsets(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>> {
372 fn convert(inset: &Inset) -> LengthPercentageOrAuto<'_> {
373 match inset {
374 Inset::LengthPercentage(v) => LengthPercentageOrAuto::LengthPercentage(v),
375 Inset::Auto => LengthPercentageOrAuto::Auto,
376 Inset::AnchorFunction(_) => unreachable!("anchor() should be disabled"),
377 Inset::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"),
378 Inset::AnchorContainingCalcFunction(_) => {
379 unreachable!("anchor() and anchor-size() should be disabled")
380 },
381 }
382 }
383 let position = self.get_position();
384 PhysicalSides::new(
385 convert(&position.top),
386 convert(&position.right),
387 convert(&position.bottom),
388 convert(&position.left),
389 )
390 }
391
392 fn box_offsets(&self, writing_mode: WritingMode) -> LogicalSides<LengthPercentageOrAuto<'_>> {
393 LogicalSides::from_physical(&self.physical_box_offsets(), writing_mode)
394 }
395
396 fn box_size(
397 &self,
398 containing_block_writing_mode: WritingMode,
399 ) -> LogicalVec2<Size<LengthPercentage>> {
400 let position = self.get_position();
401 LogicalVec2::from_physical_size(
402 &PhysicalSize::new(
403 position.clone_width().into(),
404 position.clone_height().into(),
405 ),
406 containing_block_writing_mode,
407 )
408 }
409
410 fn min_box_size(
411 &self,
412 containing_block_writing_mode: WritingMode,
413 ) -> LogicalVec2<Size<LengthPercentage>> {
414 let position = self.get_position();
415 LogicalVec2::from_physical_size(
416 &PhysicalSize::new(
417 position.clone_min_width().into(),
418 position.clone_min_height().into(),
419 ),
420 containing_block_writing_mode,
421 )
422 }
423
424 fn max_box_size(
425 &self,
426 containing_block_writing_mode: WritingMode,
427 ) -> LogicalVec2<Size<LengthPercentage>> {
428 let position = self.get_position();
429 LogicalVec2::from_physical_size(
430 &PhysicalSize::new(
431 position.clone_max_width().into(),
432 position.clone_max_height().into(),
433 ),
434 containing_block_writing_mode,
435 )
436 }
437
438 fn content_box_size_for_box_size(
439 &self,
440 box_size: LogicalVec2<Size<Au>>,
441 pbm: &PaddingBorderMargin,
442 ) -> LogicalVec2<Size<Au>> {
443 match self.get_position().box_sizing {
444 BoxSizing::ContentBox => box_size,
445 BoxSizing::BorderBox => box_size.map_inline_and_block_sizes(
448 |value| value - pbm.padding_border_sums.inline,
449 |value| value - pbm.padding_border_sums.block,
450 ),
451 }
452 }
453
454 fn content_min_box_size_for_min_size(
455 &self,
456 min_box_size: LogicalVec2<Size<Au>>,
457 pbm: &PaddingBorderMargin,
458 ) -> LogicalVec2<Size<Au>> {
459 match self.get_position().box_sizing {
460 BoxSizing::ContentBox => min_box_size,
461 BoxSizing::BorderBox => min_box_size.map_inline_and_block_sizes(
463 |value| Au::zero().max(value - pbm.padding_border_sums.inline),
464 |value| Au::zero().max(value - pbm.padding_border_sums.block),
465 ),
466 }
467 }
468
469 fn content_max_box_size_for_max_size(
470 &self,
471 max_box_size: LogicalVec2<Size<Au>>,
472 pbm: &PaddingBorderMargin,
473 ) -> LogicalVec2<Size<Au>> {
474 match self.get_position().box_sizing {
475 BoxSizing::ContentBox => max_box_size,
476 BoxSizing::BorderBox => max_box_size.map_inline_and_block_sizes(
479 |value| value - pbm.padding_border_sums.inline,
480 |value| value - pbm.padding_border_sums.block,
481 ),
482 }
483 }
484
485 fn border_style_color(
486 &self,
487 containing_block_writing_mode: WritingMode,
488 ) -> LogicalSides<BorderStyleColor> {
489 let current_color = self.get_inherited_text().clone_color();
490 LogicalSides::from_physical(
491 &BorderStyleColor::from_border(self.get_border(), ¤t_color),
492 containing_block_writing_mode,
493 )
494 }
495
496 fn physical_margin(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>> {
497 fn convert(inset: &Margin) -> LengthPercentageOrAuto<'_> {
498 match inset {
499 Margin::LengthPercentage(v) => LengthPercentageOrAuto::LengthPercentage(v),
500 Margin::Auto => LengthPercentageOrAuto::Auto,
501 Margin::AnchorSizeFunction(_) | Margin::AnchorContainingCalcFunction(_) => {
502 unreachable!("anchor-size() should be disabled")
503 },
504 }
505 }
506 let margin = self.get_margin();
507 PhysicalSides::new(
508 convert(&margin.margin_top),
509 convert(&margin.margin_right),
510 convert(&margin.margin_bottom),
511 convert(&margin.margin_left),
512 )
513 }
514
515 fn margin(
516 &self,
517 containing_block_writing_mode: WritingMode,
518 ) -> LogicalSides<LengthPercentageOrAuto<'_>> {
519 LogicalSides::from_physical(&self.physical_margin(), containing_block_writing_mode)
520 }
521
522 fn is_inline_box(&self, fragment_flags: FragmentFlags) -> bool {
523 self.get_box().display.is_inline_flow() &&
524 !fragment_flags.intersects(FragmentFlags::IS_REPLACED | FragmentFlags::IS_WIDGET)
525 }
526
527 fn is_atomic_inline_level(&self, fragment_flags: FragmentFlags) -> bool {
528 self.get_box().display.outside() == stylo::DisplayOutside::Inline &&
529 !self.is_inline_box(fragment_flags)
530 }
531
532 fn is_transformable(&self, fragment_flags: FragmentFlags) -> bool {
534 !self.is_inline_box(fragment_flags)
544 }
545
546 fn has_transform_or_perspective_style(&self) -> bool {
548 !self.get_box().transform.0.is_empty() ||
549 self.get_box().scale != GenericScale::None ||
550 self.get_box().rotate != GenericRotate::None ||
551 self.get_box().translate != GenericTranslate::None ||
552 self.get_box().perspective != Perspective::None
553 }
554
555 #[inline]
558 fn has_effective_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool {
559 self.is_transformable(fragment_flags) && self.has_transform_or_perspective_style()
560 }
561
562 fn z_index_applies(&self, fragment_flags: FragmentFlags) -> bool {
564 if self.get_box().position != ComputedPosition::Static {
567 return true;
568 }
569 fragment_flags.contains(FragmentFlags::IS_FLEX_OR_GRID_ITEM)
581 }
582
583 fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32 {
587 if self.z_index_applies(fragment_flags) {
588 self.get_position().z_index.integer_or(0)
589 } else {
590 0
591 }
592 }
593
594 fn effective_overflow(&self, fragment_flags: FragmentFlags) -> AxesOverflow {
598 if fragment_flags.contains(FragmentFlags::PROPAGATED_OVERFLOW_TO_VIEWPORT) {
601 return AxesOverflow::default();
602 }
603
604 let mut overflow = AxesOverflow::from(self);
605
606 if fragment_flags.contains(FragmentFlags::IS_REPLACED) {
609 if overflow.x != Overflow::Visible {
610 overflow.x = Overflow::Clip;
611 }
612 if overflow.y != Overflow::Visible {
613 overflow.y = Overflow::Clip;
614 }
615 return overflow;
616 }
617
618 let ignores_overflow = match self.get_box().display.inside() {
619 stylo::DisplayInside::Flow => self.is_inline_box(fragment_flags),
622
623 stylo::DisplayInside::Table => {
631 !matches!(self.pseudo(), Some(PseudoElement::ServoTableGrid)) ||
632 matches!(overflow.x, Overflow::Auto | Overflow::Scroll) ||
633 matches!(overflow.y, Overflow::Auto | Overflow::Scroll)
634 },
635
636 stylo::DisplayInside::TableColumn |
639 stylo::DisplayInside::TableColumnGroup |
640 stylo::DisplayInside::TableRow |
641 stylo::DisplayInside::TableRowGroup |
642 stylo::DisplayInside::TableHeaderGroup |
643 stylo::DisplayInside::TableFooterGroup => true,
644
645 _ => false,
646 };
647 if ignores_overflow {
648 return AxesOverflow::default();
649 }
650
651 overflow
652 }
653
654 fn establishes_block_formatting_context(&self, fragment_flags: FragmentFlags) -> bool {
661 if self.establishes_scroll_container(fragment_flags) {
662 return true;
663 }
664
665 if self.get_column().is_multicol() {
666 return true;
667 }
668
669 if self.get_column().column_span == ColumnSpan::All {
670 return true;
671 }
672
673 if self.get_position().align_content.primary() != AlignFlags::NORMAL {
679 return true;
680 }
681
682 false
684 }
685
686 fn establishes_scroll_container(&self, fragment_flags: FragmentFlags) -> bool {
688 self.effective_overflow(fragment_flags)
689 .establishes_scroll_container()
690 }
691
692 fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool {
694 let will_change_bits = self.clone_will_change().bits;
698 if will_change_bits
699 .intersects(WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::OPACITY)
700 {
701 return true;
702 }
703
704 if self.z_index_applies(fragment_flags) &&
707 (!self.get_position().z_index.is_auto() ||
708 will_change_bits.intersects(WillChangeBits::Z_INDEX))
709 {
710 return true;
711 }
712
713 if matches!(
716 self.get_box().position,
717 ComputedPosition::Fixed | ComputedPosition::Sticky
718 ) {
719 return true;
720 }
721
722 if self.is_transformable(fragment_flags) &&
737 (self.has_transform_or_perspective_style() ||
738 self.get_box().transform_style == ComputedTransformStyle::Preserve3d ||
739 will_change_bits
740 .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
741 {
742 return true;
743 }
744
745 let effects = self.get_effects();
749 if effects.opacity != 1.0 {
750 return true;
751 }
752
753 if !effects.filter.0.is_empty() {
757 return true;
758 }
759
760 if effects.mix_blend_mode != ComputedMixBlendMode::Normal {
764 return true;
765 }
766
767 if self.get_svg().clip_path != ClipPath::None {
771 return true;
772 }
773
774 if self.get_box().isolation == ComputedIsolation::Isolate {
778 return true;
779 }
780
781 if fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT) {
784 return true;
785 }
786
787 false
789 }
790
791 fn establishes_containing_block_for_absolute_descendants(
798 &self,
799 fragment_flags: FragmentFlags,
800 ) -> bool {
801 if self.establishes_containing_block_for_all_descendants(fragment_flags) {
802 return true;
803 }
804
805 if self
810 .clone_will_change()
811 .bits
812 .intersects(WillChangeBits::POSITION)
813 {
814 return true;
815 }
816
817 self.clone_position() != ComputedPosition::Static
818 }
819
820 fn establishes_containing_block_for_all_descendants(
825 &self,
826 fragment_flags: FragmentFlags,
827 ) -> bool {
828 let will_change_bits = self.clone_will_change().bits;
833
834 if self.is_transformable(fragment_flags) &&
850 (self.has_transform_or_perspective_style() ||
851 self.get_box().transform_style == ComputedTransformStyle::Preserve3d ||
852 will_change_bits
853 .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
854 {
855 return true;
856 }
857
858 if !fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT) &&
863 (!self.get_effects().filter.0.is_empty() ||
864 will_change_bits.intersects(WillChangeBits::FIXPOS_CB_NON_SVG))
865 {
866 return true;
867 }
868
869 false
871 }
872
873 fn preferred_aspect_ratio(
877 &self,
878 natural_aspect_ratio: Option<CSSFloat>,
879 padding_border_sums: &LogicalVec2<Au>,
880 ) -> Option<AspectRatio> {
881 let GenericAspectRatio {
882 auto,
883 ratio: mut preferred_ratio,
884 } = self.clone_aspect_ratio();
885
886 if matches!(preferred_ratio, PreferredRatio::Ratio(ratio) if ratio.is_degenerate()) {
889 preferred_ratio = PreferredRatio::None;
890 }
891
892 let to_logical_ratio = |physical_ratio| {
893 if self.writing_mode.is_horizontal() {
894 physical_ratio
895 } else {
896 1.0 / physical_ratio
897 }
898 };
899
900 match (auto, preferred_ratio) {
901 (_, PreferredRatio::None) => natural_aspect_ratio
909 .map(to_logical_ratio)
910 .map(AspectRatio::from_logical_content_ratio),
911 (true, PreferredRatio::Ratio(preferred_ratio)) => Some({
918 let physical_ratio = natural_aspect_ratio
919 .unwrap_or_else(|| (preferred_ratio.0).0 / (preferred_ratio.1).0);
920 AspectRatio::from_logical_content_ratio(to_logical_ratio(physical_ratio))
921 }),
922
923 (false, PreferredRatio::Ratio(preferred_ratio)) => {
927 let box_sizing_adjustment = match self.clone_box_sizing() {
930 BoxSizing::ContentBox => LogicalVec2::zero(),
931 BoxSizing::BorderBox => *padding_border_sums,
932 };
933 Some(AspectRatio {
934 i_over_b: to_logical_ratio((preferred_ratio.0).0 / (preferred_ratio.1).0),
935 box_sizing_adjustment,
936 })
937 },
938 }
939 }
940
941 fn background_is_transparent(&self) -> bool {
943 let background = self.get_background();
944 let color = self.resolve_color(&background.background_color);
945 color.alpha == 0.0 &&
946 background
947 .background_image
948 .0
949 .iter()
950 .all(|layer| matches!(layer, ComputedImageLayer::None))
951 }
952
953 fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags {
956 match self.get_box().backface_visibility {
957 BackfaceVisiblity::Visible => wr::PrimitiveFlags::default(),
958 BackfaceVisiblity::Hidden => wr::PrimitiveFlags::empty(),
959 }
960 }
961
962 fn bidi_control_chars(&self) -> (&'static str, &'static str) {
966 match (
967 self.get_text().unicode_bidi,
968 self.get_inherited_box().direction,
969 ) {
970 (UnicodeBidi::Normal, _) => ("", ""),
971 (UnicodeBidi::Embed, Direction::Ltr) => ("\u{202a}", "\u{202c}"),
972 (UnicodeBidi::Embed, Direction::Rtl) => ("\u{202b}", "\u{202c}"),
973 (UnicodeBidi::Isolate, Direction::Ltr) => ("\u{2066}", "\u{2069}"),
974 (UnicodeBidi::Isolate, Direction::Rtl) => ("\u{2067}", "\u{2069}"),
975 (UnicodeBidi::BidiOverride, Direction::Ltr) => ("\u{202d}", "\u{202c}"),
976 (UnicodeBidi::BidiOverride, Direction::Rtl) => ("\u{202e}", "\u{202c}"),
977 (UnicodeBidi::IsolateOverride, Direction::Ltr) => {
978 ("\u{2068}\u{202d}", "\u{202c}\u{2069}")
979 },
980 (UnicodeBidi::IsolateOverride, Direction::Rtl) => {
981 ("\u{2068}\u{202e}", "\u{202c}\u{2069}")
982 },
983 (UnicodeBidi::Plaintext, _) => ("\u{2068}", "\u{2069}"),
984 }
985 }
986
987 fn resolve_align_self(
988 &self,
989 resolved_auto_value: ItemPlacement,
990 resolved_normal_value: AlignFlags,
991 ) -> SelfAlignment {
992 SelfAlignment(match self.clone_align_self().0 {
993 AlignFlags::AUTO => resolved_auto_value.0,
994 AlignFlags::NORMAL => resolved_normal_value,
995 value => value,
996 })
997 }
998
999 fn depends_on_block_constraints_due_to_relative_positioning(
1000 &self,
1001 writing_mode: WritingMode,
1002 ) -> bool {
1003 if !matches!(
1004 self.get_box().position,
1005 ComputedPosition::Relative | ComputedPosition::Sticky
1006 ) {
1007 return false;
1008 }
1009 let box_offsets = self.box_offsets(writing_mode);
1010 let has_percentage = |offset: LengthPercentageOrAuto<'_>| {
1011 offset
1012 .non_auto()
1013 .is_some_and(LengthPercentage::has_percentage)
1014 };
1015 has_percentage(box_offsets.block_start) || has_percentage(box_offsets.block_end)
1016 }
1017
1018 fn overflow_direction(&self) -> OverflowDirection {
1020 let inline_end_direction = self.writing_mode.inline_end_physical_side();
1021 let block_end_direction = self.writing_mode.block_end_physical_side();
1022
1023 let rightward = inline_end_direction == PhysicalSide::Right ||
1024 block_end_direction == PhysicalSide::Right;
1025 let downward = inline_end_direction == PhysicalSide::Bottom ||
1026 block_end_direction == PhysicalSide::Bottom;
1027
1028 OverflowDirection {
1030 rightward,
1031 downward,
1032 }
1033 }
1034
1035 fn to_bidi_level(&self) -> Level {
1039 if self.writing_mode.is_bidi_ltr() {
1040 Level::ltr()
1041 } else {
1042 Level::rtl()
1043 }
1044 }
1045}
1046
1047pub(crate) enum LayoutStyle<'a> {
1048 Default(&'a ComputedValues),
1049 Table(TableLayoutStyle<'a>),
1050}
1051
1052impl LayoutStyle<'_> {
1053 #[inline]
1054 pub(crate) fn style(&self) -> &ComputedValues {
1055 match self {
1056 Self::Default(style) => style,
1057 Self::Table(table) => table.style(),
1058 }
1059 }
1060
1061 #[inline]
1062 pub(crate) fn is_table(&self) -> bool {
1063 matches!(self, Self::Table(_))
1064 }
1065
1066 pub(crate) fn content_box_sizes_and_padding_border_margin(
1067 &self,
1068 containing_block: &IndefiniteContainingBlock,
1069 ) -> ContentBoxSizesAndPBM {
1070 let containing_block_size_or_zero =
1076 containing_block.size.map(|value| value.unwrap_or_default());
1077 let writing_mode = containing_block.style.writing_mode;
1078 let pbm = self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1079 writing_mode,
1080 containing_block_size_or_zero.inline,
1081 );
1082 let style = self.style();
1083 let box_size = style.box_size(writing_mode);
1084 let min_size = style.min_box_size(writing_mode);
1085 let max_size = style.max_box_size(writing_mode);
1086 let preferred_size_computes_to_auto = box_size.map(|size| size.is_initial());
1087
1088 let depends_on_block_constraints = |size: &Size<LengthPercentage>| {
1089 match size {
1090 Size::Stretch => true,
1098 Size::Numeric(length_percentage) => length_percentage.has_percentage(),
1099 _ => false,
1100 }
1101 };
1102 let depends_on_block_constraints = depends_on_block_constraints(&box_size.block) ||
1103 depends_on_block_constraints(&min_size.block) ||
1104 depends_on_block_constraints(&max_size.block) ||
1105 style.depends_on_block_constraints_due_to_relative_positioning(writing_mode);
1106
1107 let box_size = box_size.map_with(&containing_block.size, |size, basis| {
1108 size.resolve_percentages_for_preferred(*basis)
1109 });
1110 let content_box_size = style.content_box_size_for_box_size(box_size, &pbm);
1111 let min_size = min_size.percentages_relative_to_basis(&containing_block_size_or_zero);
1112 let content_min_box_size = style.content_min_box_size_for_min_size(min_size, &pbm);
1113 let max_size = max_size.map_with(&containing_block.size, |size, basis| {
1114 size.resolve_percentages_for_max(*basis)
1115 });
1116 let content_max_box_size = style.content_max_box_size_for_max_size(max_size, &pbm);
1117 ContentBoxSizesAndPBM {
1118 content_box_sizes: LogicalVec2 {
1119 block: Sizes::new(
1120 content_box_size.block,
1121 content_min_box_size.block,
1122 content_max_box_size.block,
1123 ),
1124 inline: Sizes::new(
1125 content_box_size.inline,
1126 content_min_box_size.inline,
1127 content_max_box_size.inline,
1128 ),
1129 },
1130 pbm,
1131 depends_on_block_constraints,
1132 preferred_size_computes_to_auto,
1133 }
1134 }
1135
1136 pub(crate) fn padding_border_margin(
1137 &self,
1138 containing_block: &ContainingBlock,
1139 ) -> PaddingBorderMargin {
1140 self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1141 containing_block.style.writing_mode,
1142 containing_block.size.inline,
1143 )
1144 }
1145
1146 pub(crate) fn padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1147 &self,
1148 writing_mode: WritingMode,
1149 containing_block_inline_size: Au,
1150 ) -> PaddingBorderMargin {
1151 let padding = self
1152 .padding(writing_mode)
1153 .percentages_relative_to(containing_block_inline_size);
1154 let style = self.style();
1155 let border = self.border_width(writing_mode);
1156 let margin = style
1157 .margin(writing_mode)
1158 .percentages_relative_to(containing_block_inline_size);
1159 PaddingBorderMargin {
1160 padding_border_sums: LogicalVec2 {
1161 inline: padding.inline_sum() + border.inline_sum(),
1162 block: padding.block_sum() + border.block_sum(),
1163 },
1164 padding,
1165 border,
1166 margin,
1167 }
1168 }
1169
1170 pub(crate) fn padding(
1171 &self,
1172 containing_block_writing_mode: WritingMode,
1173 ) -> LogicalSides<LengthPercentage> {
1174 if matches!(self, Self::Table(table) if table.collapses_borders()) {
1175 return LogicalSides::zero();
1178 }
1179 let padding = self.style().get_padding().clone();
1180 LogicalSides::from_physical(
1181 &PhysicalSides::new(
1182 padding.padding_top.0,
1183 padding.padding_right.0,
1184 padding.padding_bottom.0,
1185 padding.padding_left.0,
1186 ),
1187 containing_block_writing_mode,
1188 )
1189 }
1190
1191 pub(crate) fn border_width(
1192 &self,
1193 containing_block_writing_mode: WritingMode,
1194 ) -> LogicalSides<Au> {
1195 let border_width = match self {
1196 Self::Table(table) if table.collapses_borders() => table
1200 .halved_collapsed_border_widths()
1201 .to_physical(self.style().writing_mode),
1202 _ => {
1203 let border = self.style().get_border();
1204 let resolve = |width: &BorderSideWidth, style: BorderStyle| {
1205 if style.none_or_hidden() {
1206 Au::zero()
1207 } else {
1208 width.0
1209 }
1210 };
1211 PhysicalSides::new(
1212 resolve(&border.border_top_width, border.border_top_style),
1213 resolve(&border.border_right_width, border.border_right_style),
1214 resolve(&border.border_bottom_width, border.border_bottom_style),
1215 resolve(&border.border_left_width, border.border_left_style),
1216 )
1217 },
1218 };
1219 LogicalSides::from_physical(&border_width, containing_block_writing_mode)
1220 }
1221}
1222
1223impl From<stylo::Display> for Display {
1224 fn from(packed: stylo::Display) -> Self {
1225 let outside = packed.outside();
1226 let inside = packed.inside();
1227
1228 let outside = match outside {
1229 stylo::DisplayOutside::Block => DisplayOutside::Block,
1230 stylo::DisplayOutside::Inline => DisplayOutside::Inline,
1231 stylo::DisplayOutside::TableCaption => {
1232 return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(
1233 DisplayLayoutInternal::TableCaption,
1234 ));
1235 },
1236 stylo::DisplayOutside::InternalTable => {
1237 let internal = match inside {
1238 stylo::DisplayInside::TableRowGroup => DisplayLayoutInternal::TableRowGroup,
1239 stylo::DisplayInside::TableColumn => DisplayLayoutInternal::TableColumn,
1240 stylo::DisplayInside::TableColumnGroup => {
1241 DisplayLayoutInternal::TableColumnGroup
1242 },
1243 stylo::DisplayInside::TableHeaderGroup => {
1244 DisplayLayoutInternal::TableHeaderGroup
1245 },
1246 stylo::DisplayInside::TableFooterGroup => {
1247 DisplayLayoutInternal::TableFooterGroup
1248 },
1249 stylo::DisplayInside::TableRow => DisplayLayoutInternal::TableRow,
1250 stylo::DisplayInside::TableCell => DisplayLayoutInternal::TableCell,
1251 _ => unreachable!("Non-internal DisplayInside found"),
1252 };
1253 return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(internal));
1254 },
1255 stylo::DisplayOutside::None if inside == stylo::DisplayInside::Contents => {
1258 return Display::Contents;
1259 },
1260 stylo::DisplayOutside::None => return Display::None,
1261 };
1262
1263 let inside = match inside {
1264 stylo::DisplayInside::Flow => DisplayInside::Flow {
1265 is_list_item: packed.is_list_item(),
1266 },
1267 stylo::DisplayInside::FlowRoot => DisplayInside::FlowRoot {
1268 is_list_item: packed.is_list_item(),
1269 },
1270 stylo::DisplayInside::Flex => DisplayInside::Flex,
1271 stylo::DisplayInside::Grid => DisplayInside::Grid,
1272 stylo::DisplayInside::Table => DisplayInside::Table,
1273
1274 stylo::DisplayInside::None => return Display::None,
1276 stylo::DisplayInside::Contents => return Display::Contents,
1277
1278 stylo::DisplayInside::TableRowGroup |
1279 stylo::DisplayInside::TableColumn |
1280 stylo::DisplayInside::TableColumnGroup |
1281 stylo::DisplayInside::TableHeaderGroup |
1282 stylo::DisplayInside::TableFooterGroup |
1283 stylo::DisplayInside::TableRow |
1284 stylo::DisplayInside::TableCell => unreachable!("Internal DisplayInside found"),
1285 };
1286 Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { outside, inside })
1287 }
1288}
1289
1290pub(crate) trait Clamp: Sized {
1291 fn clamp_below_max(self, max: Option<Self>) -> Self;
1292 fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self;
1293}
1294
1295impl Clamp for Au {
1296 fn clamp_below_max(self, max: Option<Self>) -> Self {
1297 match max {
1298 None => self,
1299 Some(max) => self.min(max),
1300 }
1301 }
1302
1303 fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self {
1304 self.clamp_below_max(max).max(min)
1305 }
1306}
1307
1308pub(crate) trait TransformExt {
1309 fn change_basis(&self, x: f32, y: f32, z: f32) -> Self;
1310}
1311
1312impl TransformExt for LayoutTransform {
1313 fn change_basis(&self, x: f32, y: f32, z: f32) -> Self {
1315 let pre_translation = Self::translation(x, y, z);
1316 let post_translation = Self::translation(-x, -y, -z);
1317 post_translation.then(self).then(&pre_translation)
1318 }
1319}