1use app_units::Au;
6use malloc_size_of_derive::MallocSizeOf;
7use style::Zero;
8use style::color::AbsoluteColor;
9use style::computed_values::direction::T as Direction;
10use style::computed_values::isolation::T as ComputedIsolation;
11use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode;
12use style::computed_values::position::T as ComputedPosition;
13use style::computed_values::transform_style::T as ComputedTransformStyle;
14use style::computed_values::unicode_bidi::T as UnicodeBidi;
15use style::logical_geometry::{Direction as AxisDirection, PhysicalSide, WritingMode};
16use style::properties::ComputedValues;
17use style::properties::longhands::backface_visibility::computed_value::T as BackfaceVisiblity;
18use style::properties::longhands::box_sizing::computed_value::T as BoxSizing;
19use style::properties::longhands::column_span::computed_value::T as ColumnSpan;
20use style::properties::style_structs::Border;
21use style::servo::selector_parser::PseudoElement;
22use style::values::CSSFloat;
23use style::values::computed::basic_shape::ClipPath;
24use style::values::computed::image::Image as ComputedImageLayer;
25use style::values::computed::{AlignItems, BorderStyle, Color, Inset, LengthPercentage, Margin};
26use style::values::generics::box_::Perspective;
27use style::values::generics::position::{GenericAspectRatio, PreferredRatio};
28use style::values::generics::transform::{GenericRotate, GenericScale, GenericTranslate};
29use style::values::specified::align::AlignFlags;
30use style::values::specified::{Overflow, WillChangeBits, box_ as stylo};
31use unicode_bidi::Level;
32use webrender_api as wr;
33use webrender_api::units::LayoutTransform;
34
35use crate::dom_traversal::{Contents, NonReplacedContents};
36use crate::fragment_tree::FragmentFlags;
37use crate::geom::{
38 AuOrAuto, LengthPercentageOrAuto, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalSides,
39 PhysicalSize,
40};
41use crate::sizing::{Size, Sizes};
42use crate::table::TableLayoutStyle;
43use crate::{ContainingBlock, IndefiniteContainingBlock};
44
45#[derive(Clone, Copy, Eq, PartialEq)]
46pub(crate) enum Display {
47 None,
48 Contents,
49 GeneratingBox(DisplayGeneratingBox),
50}
51
52#[derive(Clone, Copy, Debug, Eq, PartialEq)]
53pub(crate) enum DisplayGeneratingBox {
54 OutsideInside {
55 outside: DisplayOutside,
56 inside: DisplayInside,
57 },
58 LayoutInternal(DisplayLayoutInternal),
60}
61#[derive(Clone, Copy, Debug)]
62pub struct AxesOverflow {
63 pub x: Overflow,
64 pub y: Overflow,
65}
66
67impl Default for AxesOverflow {
68 fn default() -> Self {
69 Self {
70 x: Overflow::Visible,
71 y: Overflow::Visible,
72 }
73 }
74}
75
76impl From<&ComputedValues> for AxesOverflow {
77 fn from(style: &ComputedValues) -> Self {
78 Self {
79 x: style.clone_overflow_x(),
80 y: style.clone_overflow_y(),
81 }
82 }
83}
84
85impl DisplayGeneratingBox {
86 pub(crate) fn display_inside(&self) -> DisplayInside {
87 match *self {
88 DisplayGeneratingBox::OutsideInside { inside, .. } => inside,
89 DisplayGeneratingBox::LayoutInternal(layout_internal) => {
90 layout_internal.display_inside()
91 },
92 }
93 }
94
95 pub(crate) fn used_value_for_contents(&self, contents: &Contents) -> Self {
96 if matches!(self, Self::LayoutInternal(_)) && contents.is_replaced() {
101 Self::OutsideInside {
102 outside: DisplayOutside::Inline,
103 inside: DisplayInside::Flow {
104 is_list_item: false,
105 },
106 }
107 } else if matches!(
108 contents,
109 Contents::NonReplaced(NonReplacedContents::OfTextControl)
110 ) {
111 if let DisplayGeneratingBox::OutsideInside { outside, .. } = self {
114 DisplayGeneratingBox::OutsideInside {
115 outside: *outside,
116 inside: DisplayInside::FlowRoot {
117 is_list_item: false,
118 },
119 }
120 } else {
121 *self
122 }
123 } else {
124 *self
125 }
126 }
127}
128
129#[derive(Clone, Copy, Debug, Eq, PartialEq)]
130pub(crate) enum DisplayOutside {
131 Block,
132 Inline,
133}
134
135#[derive(Clone, Copy, Debug, Eq, PartialEq)]
136pub(crate) enum DisplayInside {
137 Flow { is_list_item: bool },
140 FlowRoot { is_list_item: bool },
141 Flex,
142 Grid,
143 Table,
144}
145
146#[derive(Clone, Copy, Debug, Eq, PartialEq)]
147#[allow(clippy::enum_variant_names)]
148pub(crate) enum DisplayLayoutInternal {
150 TableCaption,
151 TableCell,
152 TableColumn,
153 TableColumnGroup,
154 TableFooterGroup,
155 TableHeaderGroup,
156 TableRow,
157 TableRowGroup,
158}
159
160impl DisplayLayoutInternal {
161 pub(crate) fn display_inside(&self) -> DisplayInside {
163 DisplayInside::FlowRoot {
167 is_list_item: false,
168 }
169 }
170}
171
172#[derive(Clone, Debug)]
174pub(crate) struct PaddingBorderMargin {
175 pub padding: LogicalSides<Au>,
176 pub border: LogicalSides<Au>,
177 pub margin: LogicalSides<AuOrAuto>,
178
179 pub padding_border_sums: LogicalVec2<Au>,
181}
182
183impl PaddingBorderMargin {
184 pub(crate) fn zero() -> Self {
185 Self {
186 padding: LogicalSides::zero(),
187 border: LogicalSides::zero(),
188 margin: LogicalSides::zero(),
189 padding_border_sums: LogicalVec2::zero(),
190 }
191 }
192
193 pub(crate) fn sums_auto_is_zero(
194 &self,
195 ignore_block_margins: LogicalSides1D<bool>,
196 ) -> LogicalVec2<Au> {
197 let margin = self.margin.auto_is(Au::zero);
198 let mut sums = self.padding_border_sums;
199 sums.inline += margin.inline_sum();
200 if !ignore_block_margins.start {
201 sums.block += margin.block_start;
202 }
203 if !ignore_block_margins.end {
204 sums.block += margin.block_end;
205 }
206 sums
207 }
208}
209
210#[derive(Clone, Copy, Debug)]
214pub(crate) struct AspectRatio {
215 box_sizing_adjustment: LogicalVec2<Au>,
220 i_over_b: CSSFloat,
222}
223
224impl AspectRatio {
225 pub(crate) fn compute_dependent_size(
227 &self,
228 ratio_dependent_axis: AxisDirection,
229 ratio_determining_size: Au,
230 ) -> Au {
231 match ratio_dependent_axis {
232 AxisDirection::Inline => {
234 (ratio_determining_size + self.box_sizing_adjustment.block).scale_by(self.i_over_b) -
235 self.box_sizing_adjustment.inline
236 },
237 AxisDirection::Block => {
239 (ratio_determining_size + self.box_sizing_adjustment.inline)
240 .scale_by(1.0 / self.i_over_b) -
241 self.box_sizing_adjustment.block
242 },
243 }
244 }
245
246 pub(crate) fn from_logical_content_ratio(i_over_b: CSSFloat) -> Self {
247 Self {
248 box_sizing_adjustment: LogicalVec2::zero(),
249 i_over_b,
250 }
251 }
252}
253
254#[derive(Clone)]
255pub(crate) struct ContentBoxSizesAndPBM {
256 pub content_box_sizes: LogicalVec2<Sizes>,
257 pub pbm: PaddingBorderMargin,
258 pub depends_on_block_constraints: bool,
259 pub preferred_size_computes_to_auto: LogicalVec2<bool>,
260}
261
262#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
263pub(crate) struct BorderStyleColor {
264 pub style: BorderStyle,
265 pub color: AbsoluteColor,
266}
267
268impl BorderStyleColor {
269 pub(crate) fn new(style: BorderStyle, color: AbsoluteColor) -> Self {
270 Self { style, color }
271 }
272
273 pub(crate) fn from_border(
274 border: &Border,
275 current_color: &AbsoluteColor,
276 ) -> PhysicalSides<Self> {
277 let resolve = |color: &Color| color.resolve_to_absolute(current_color);
278 PhysicalSides::<Self>::new(
279 Self::new(border.border_top_style, resolve(&border.border_top_color)),
280 Self::new(
281 border.border_right_style,
282 resolve(&border.border_right_color),
283 ),
284 Self::new(
285 border.border_bottom_style,
286 resolve(&border.border_bottom_color),
287 ),
288 Self::new(border.border_left_style, resolve(&border.border_left_color)),
289 )
290 }
291
292 pub(crate) fn hidden() -> Self {
293 Self::new(BorderStyle::Hidden, AbsoluteColor::TRANSPARENT_BLACK)
294 }
295}
296
297impl Default for BorderStyleColor {
298 fn default() -> Self {
299 Self::new(BorderStyle::None, AbsoluteColor::TRANSPARENT_BLACK)
300 }
301}
302
303pub(crate) struct OverflowDirection {
307 pub rightward: bool,
309 pub downward: bool,
311}
312
313pub(crate) trait ComputedValuesExt {
314 fn physical_box_offsets(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>>;
315 fn box_offsets(&self, writing_mode: WritingMode) -> LogicalSides<LengthPercentageOrAuto<'_>>;
316 fn box_size(
317 &self,
318 containing_block_writing_mode: WritingMode,
319 ) -> LogicalVec2<Size<LengthPercentage>>;
320 fn min_box_size(
321 &self,
322 containing_block_writing_mode: WritingMode,
323 ) -> LogicalVec2<Size<LengthPercentage>>;
324 fn max_box_size(
325 &self,
326 containing_block_writing_mode: WritingMode,
327 ) -> LogicalVec2<Size<LengthPercentage>>;
328 fn content_box_size_for_box_size(
329 &self,
330 box_size: LogicalVec2<Size<Au>>,
331 pbm: &PaddingBorderMargin,
332 ) -> LogicalVec2<Size<Au>>;
333 fn content_min_box_size_for_min_size(
334 &self,
335 box_size: LogicalVec2<Size<Au>>,
336 pbm: &PaddingBorderMargin,
337 ) -> LogicalVec2<Size<Au>>;
338 fn content_max_box_size_for_max_size(
339 &self,
340 box_size: LogicalVec2<Size<Au>>,
341 pbm: &PaddingBorderMargin,
342 ) -> LogicalVec2<Size<Au>>;
343 fn border_style_color(
344 &self,
345 containing_block_writing_mode: WritingMode,
346 ) -> LogicalSides<BorderStyleColor>;
347 fn physical_margin(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>>;
348 fn margin(
349 &self,
350 containing_block_writing_mode: WritingMode,
351 ) -> LogicalSides<LengthPercentageOrAuto<'_>>;
352 fn is_transformable(&self, fragment_flags: FragmentFlags) -> bool;
353 fn has_transform_or_perspective_style(&self) -> bool;
354 fn has_effective_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool;
355 fn z_index_applies(&self, fragment_flags: FragmentFlags) -> bool;
356 fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32;
357 fn effective_overflow(&self, fragment_flags: FragmentFlags) -> AxesOverflow;
358 fn establishes_block_formatting_context(&self, fragment_flags: FragmentFlags) -> bool;
359 fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool;
360 fn establishes_scroll_container(&self, fragment_flags: FragmentFlags) -> bool;
361 fn establishes_containing_block_for_absolute_descendants(
362 &self,
363 fragment_flags: FragmentFlags,
364 ) -> bool;
365 fn establishes_containing_block_for_all_descendants(
366 &self,
367 fragment_flags: FragmentFlags,
368 ) -> bool;
369 fn preferred_aspect_ratio(
370 &self,
371 natural_aspect_ratio: Option<CSSFloat>,
372 padding_border_sums: &LogicalVec2<Au>,
373 ) -> Option<AspectRatio>;
374 fn background_is_transparent(&self) -> bool;
375 fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags;
376 fn bidi_control_chars(&self) -> (&'static str, &'static str);
377 fn resolve_align_self(
378 &self,
379 resolved_auto_value: AlignItems,
380 resolved_normal_value: AlignItems,
381 ) -> AlignItems;
382 fn depends_on_block_constraints_due_to_relative_positioning(
383 &self,
384 writing_mode: WritingMode,
385 ) -> bool;
386 fn is_inline_box(&self, fragment_flags: FragmentFlags) -> bool;
387 fn overflow_direction(&self) -> OverflowDirection;
388 fn to_bidi_level(&self) -> Level;
389}
390
391impl ComputedValuesExt for ComputedValues {
392 fn physical_box_offsets(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>> {
393 fn convert(inset: &Inset) -> LengthPercentageOrAuto<'_> {
394 match inset {
395 Inset::LengthPercentage(v) => LengthPercentageOrAuto::LengthPercentage(v),
396 Inset::Auto => LengthPercentageOrAuto::Auto,
397 Inset::AnchorFunction(_) => unreachable!("anchor() should be disabled"),
398 Inset::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"),
399 Inset::AnchorContainingCalcFunction(_) => {
400 unreachable!("anchor() and anchor-size() should be disabled")
401 },
402 }
403 }
404 let position = self.get_position();
405 PhysicalSides::new(
406 convert(&position.top),
407 convert(&position.right),
408 convert(&position.bottom),
409 convert(&position.left),
410 )
411 }
412
413 fn box_offsets(&self, writing_mode: WritingMode) -> LogicalSides<LengthPercentageOrAuto<'_>> {
414 LogicalSides::from_physical(&self.physical_box_offsets(), writing_mode)
415 }
416
417 fn box_size(
418 &self,
419 containing_block_writing_mode: WritingMode,
420 ) -> LogicalVec2<Size<LengthPercentage>> {
421 let position = self.get_position();
422 LogicalVec2::from_physical_size(
423 &PhysicalSize::new(
424 position.clone_width().into(),
425 position.clone_height().into(),
426 ),
427 containing_block_writing_mode,
428 )
429 }
430
431 fn min_box_size(
432 &self,
433 containing_block_writing_mode: WritingMode,
434 ) -> LogicalVec2<Size<LengthPercentage>> {
435 let position = self.get_position();
436 LogicalVec2::from_physical_size(
437 &PhysicalSize::new(
438 position.clone_min_width().into(),
439 position.clone_min_height().into(),
440 ),
441 containing_block_writing_mode,
442 )
443 }
444
445 fn max_box_size(
446 &self,
447 containing_block_writing_mode: WritingMode,
448 ) -> LogicalVec2<Size<LengthPercentage>> {
449 let position = self.get_position();
450 LogicalVec2::from_physical_size(
451 &PhysicalSize::new(
452 position.clone_max_width().into(),
453 position.clone_max_height().into(),
454 ),
455 containing_block_writing_mode,
456 )
457 }
458
459 fn content_box_size_for_box_size(
460 &self,
461 box_size: LogicalVec2<Size<Au>>,
462 pbm: &PaddingBorderMargin,
463 ) -> LogicalVec2<Size<Au>> {
464 match self.get_position().box_sizing {
465 BoxSizing::ContentBox => box_size,
466 BoxSizing::BorderBox => box_size.map_inline_and_block_sizes(
469 |value| value - pbm.padding_border_sums.inline,
470 |value| value - pbm.padding_border_sums.block,
471 ),
472 }
473 }
474
475 fn content_min_box_size_for_min_size(
476 &self,
477 min_box_size: LogicalVec2<Size<Au>>,
478 pbm: &PaddingBorderMargin,
479 ) -> LogicalVec2<Size<Au>> {
480 match self.get_position().box_sizing {
481 BoxSizing::ContentBox => min_box_size,
482 BoxSizing::BorderBox => min_box_size.map_inline_and_block_sizes(
484 |value| Au::zero().max(value - pbm.padding_border_sums.inline),
485 |value| Au::zero().max(value - pbm.padding_border_sums.block),
486 ),
487 }
488 }
489
490 fn content_max_box_size_for_max_size(
491 &self,
492 max_box_size: LogicalVec2<Size<Au>>,
493 pbm: &PaddingBorderMargin,
494 ) -> LogicalVec2<Size<Au>> {
495 match self.get_position().box_sizing {
496 BoxSizing::ContentBox => max_box_size,
497 BoxSizing::BorderBox => max_box_size.map_inline_and_block_sizes(
500 |value| value - pbm.padding_border_sums.inline,
501 |value| value - pbm.padding_border_sums.block,
502 ),
503 }
504 }
505
506 fn border_style_color(
507 &self,
508 containing_block_writing_mode: WritingMode,
509 ) -> LogicalSides<BorderStyleColor> {
510 let current_color = self.get_inherited_text().clone_color();
511 LogicalSides::from_physical(
512 &BorderStyleColor::from_border(self.get_border(), ¤t_color),
513 containing_block_writing_mode,
514 )
515 }
516
517 fn physical_margin(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>> {
518 fn convert(inset: &Margin) -> LengthPercentageOrAuto<'_> {
519 match inset {
520 Margin::LengthPercentage(v) => LengthPercentageOrAuto::LengthPercentage(v),
521 Margin::Auto => LengthPercentageOrAuto::Auto,
522 Margin::AnchorSizeFunction(_) | Margin::AnchorContainingCalcFunction(_) => {
523 unreachable!("anchor-size() should be disabled")
524 },
525 }
526 }
527 let margin = self.get_margin();
528 PhysicalSides::new(
529 convert(&margin.margin_top),
530 convert(&margin.margin_right),
531 convert(&margin.margin_bottom),
532 convert(&margin.margin_left),
533 )
534 }
535
536 fn margin(
537 &self,
538 containing_block_writing_mode: WritingMode,
539 ) -> LogicalSides<LengthPercentageOrAuto<'_>> {
540 LogicalSides::from_physical(&self.physical_margin(), containing_block_writing_mode)
541 }
542
543 fn is_inline_box(&self, fragment_flags: FragmentFlags) -> bool {
544 self.get_box().display.is_inline_flow() &&
545 !fragment_flags
546 .intersects(FragmentFlags::IS_REPLACED | FragmentFlags::IS_TEXT_CONTROL)
547 }
548
549 fn is_transformable(&self, fragment_flags: FragmentFlags) -> bool {
551 !self.is_inline_box(fragment_flags)
561 }
562
563 fn has_transform_or_perspective_style(&self) -> bool {
565 !self.get_box().transform.0.is_empty() ||
566 self.get_box().scale != GenericScale::None ||
567 self.get_box().rotate != GenericRotate::None ||
568 self.get_box().translate != GenericTranslate::None ||
569 self.get_box().perspective != Perspective::None
570 }
571
572 #[inline]
575 fn has_effective_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool {
576 self.is_transformable(fragment_flags) && self.has_transform_or_perspective_style()
577 }
578
579 fn z_index_applies(&self, fragment_flags: FragmentFlags) -> bool {
581 if self.get_box().position != ComputedPosition::Static {
584 return true;
585 }
586 fragment_flags.contains(FragmentFlags::IS_FLEX_OR_GRID_ITEM)
598 }
599
600 fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32 {
604 if self.z_index_applies(fragment_flags) {
605 self.get_position().z_index.integer_or(0)
606 } else {
607 0
608 }
609 }
610
611 fn effective_overflow(&self, fragment_flags: FragmentFlags) -> AxesOverflow {
615 if fragment_flags.contains(FragmentFlags::PROPAGATED_OVERFLOW_TO_VIEWPORT) {
618 return AxesOverflow::default();
619 }
620
621 let mut overflow = AxesOverflow::from(self);
622
623 if fragment_flags.contains(FragmentFlags::IS_REPLACED) {
626 if overflow.x != Overflow::Visible {
627 overflow.x = Overflow::Clip;
628 }
629 if overflow.y != Overflow::Visible {
630 overflow.y = Overflow::Clip;
631 }
632 return overflow;
633 }
634
635 let ignores_overflow = match self.get_box().display.inside() {
636 stylo::DisplayInside::Flow => self.is_inline_box(fragment_flags),
639
640 stylo::DisplayInside::Table => {
648 !matches!(self.pseudo(), Some(PseudoElement::ServoTableGrid)) ||
649 matches!(overflow.x, Overflow::Auto | Overflow::Scroll) ||
650 matches!(overflow.y, Overflow::Auto | Overflow::Scroll)
651 },
652
653 stylo::DisplayInside::TableColumn |
656 stylo::DisplayInside::TableColumnGroup |
657 stylo::DisplayInside::TableRow |
658 stylo::DisplayInside::TableRowGroup |
659 stylo::DisplayInside::TableHeaderGroup |
660 stylo::DisplayInside::TableFooterGroup => true,
661
662 _ => false,
663 };
664 if ignores_overflow {
665 return AxesOverflow::default();
666 }
667
668 overflow
669 }
670
671 fn establishes_block_formatting_context(&self, fragment_flags: FragmentFlags) -> bool {
678 if self.establishes_scroll_container(fragment_flags) {
679 return true;
680 }
681
682 if self.get_column().is_multicol() {
683 return true;
684 }
685
686 if self.get_column().column_span == ColumnSpan::All {
687 return true;
688 }
689
690 if self.get_position().align_content.0.primary() != AlignFlags::NORMAL {
696 return true;
697 }
698
699 false
701 }
702
703 fn establishes_scroll_container(&self, fragment_flags: FragmentFlags) -> bool {
705 self.effective_overflow(fragment_flags).x.is_scrollable()
708 }
709
710 fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool {
712 let will_change_bits = self.clone_will_change().bits;
716 if will_change_bits
717 .intersects(WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::OPACITY)
718 {
719 return true;
720 }
721
722 if self.z_index_applies(fragment_flags) &&
725 (!self.get_position().z_index.is_auto() ||
726 will_change_bits.intersects(WillChangeBits::Z_INDEX))
727 {
728 return true;
729 }
730
731 if matches!(
734 self.get_box().position,
735 ComputedPosition::Fixed | ComputedPosition::Sticky
736 ) {
737 return true;
738 }
739
740 if self.is_transformable(fragment_flags) &&
755 (self.has_transform_or_perspective_style() ||
756 self.get_box().transform_style == ComputedTransformStyle::Preserve3d ||
757 will_change_bits
758 .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
759 {
760 return true;
761 }
762
763 let effects = self.get_effects();
767 if effects.opacity != 1.0 {
768 return true;
769 }
770
771 if !effects.filter.0.is_empty() {
775 return true;
776 }
777
778 if effects.mix_blend_mode != ComputedMixBlendMode::Normal {
782 return true;
783 }
784
785 if self.get_svg().clip_path != ClipPath::None {
789 return true;
790 }
791
792 if self.get_box().isolation == ComputedIsolation::Isolate {
796 return true;
797 }
798
799 if fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT) {
802 return true;
803 }
804
805 false
807 }
808
809 fn establishes_containing_block_for_absolute_descendants(
816 &self,
817 fragment_flags: FragmentFlags,
818 ) -> bool {
819 if self.establishes_containing_block_for_all_descendants(fragment_flags) {
820 return true;
821 }
822
823 if self
828 .clone_will_change()
829 .bits
830 .intersects(WillChangeBits::POSITION)
831 {
832 return true;
833 }
834
835 self.clone_position() != ComputedPosition::Static
836 }
837
838 fn establishes_containing_block_for_all_descendants(
843 &self,
844 fragment_flags: FragmentFlags,
845 ) -> bool {
846 let will_change_bits = self.clone_will_change().bits;
851
852 if self.is_transformable(fragment_flags) &&
868 (self.has_transform_or_perspective_style() ||
869 self.get_box().transform_style == ComputedTransformStyle::Preserve3d ||
870 will_change_bits
871 .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
872 {
873 return true;
874 }
875
876 if !fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT) &&
881 (!self.get_effects().filter.0.is_empty() ||
882 will_change_bits.intersects(WillChangeBits::FIXPOS_CB_NON_SVG))
883 {
884 return true;
885 }
886
887 false
889 }
890
891 fn preferred_aspect_ratio(
895 &self,
896 natural_aspect_ratio: Option<CSSFloat>,
897 padding_border_sums: &LogicalVec2<Au>,
898 ) -> Option<AspectRatio> {
899 let GenericAspectRatio {
900 auto,
901 ratio: mut preferred_ratio,
902 } = self.clone_aspect_ratio();
903
904 if matches!(preferred_ratio, PreferredRatio::Ratio(ratio) if ratio.is_degenerate()) {
907 preferred_ratio = PreferredRatio::None;
908 }
909
910 let to_logical_ratio = |physical_ratio| {
911 if self.writing_mode.is_horizontal() {
912 physical_ratio
913 } else {
914 1.0 / physical_ratio
915 }
916 };
917
918 match (auto, preferred_ratio) {
919 (_, PreferredRatio::None) => natural_aspect_ratio
927 .map(to_logical_ratio)
928 .map(AspectRatio::from_logical_content_ratio),
929 (true, PreferredRatio::Ratio(preferred_ratio)) => Some({
936 let physical_ratio = natural_aspect_ratio
937 .unwrap_or_else(|| (preferred_ratio.0).0 / (preferred_ratio.1).0);
938 AspectRatio::from_logical_content_ratio(to_logical_ratio(physical_ratio))
939 }),
940
941 (false, PreferredRatio::Ratio(preferred_ratio)) => {
945 let box_sizing_adjustment = match self.clone_box_sizing() {
948 BoxSizing::ContentBox => LogicalVec2::zero(),
949 BoxSizing::BorderBox => *padding_border_sums,
950 };
951 Some(AspectRatio {
952 i_over_b: to_logical_ratio((preferred_ratio.0).0 / (preferred_ratio.1).0),
953 box_sizing_adjustment,
954 })
955 },
956 }
957 }
958
959 fn background_is_transparent(&self) -> bool {
961 let background = self.get_background();
962 let color = self.resolve_color(&background.background_color);
963 color.alpha == 0.0 &&
964 background
965 .background_image
966 .0
967 .iter()
968 .all(|layer| matches!(layer, ComputedImageLayer::None))
969 }
970
971 fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags {
974 match self.get_box().backface_visibility {
975 BackfaceVisiblity::Visible => wr::PrimitiveFlags::default(),
976 BackfaceVisiblity::Hidden => wr::PrimitiveFlags::empty(),
977 }
978 }
979
980 fn bidi_control_chars(&self) -> (&'static str, &'static str) {
984 match (
985 self.get_text().unicode_bidi,
986 self.get_inherited_box().direction,
987 ) {
988 (UnicodeBidi::Normal, _) => ("", ""),
989 (UnicodeBidi::Embed, Direction::Ltr) => ("\u{202a}", "\u{202c}"),
990 (UnicodeBidi::Embed, Direction::Rtl) => ("\u{202b}", "\u{202c}"),
991 (UnicodeBidi::Isolate, Direction::Ltr) => ("\u{2066}", "\u{2069}"),
992 (UnicodeBidi::Isolate, Direction::Rtl) => ("\u{2067}", "\u{2069}"),
993 (UnicodeBidi::BidiOverride, Direction::Ltr) => ("\u{202d}", "\u{202c}"),
994 (UnicodeBidi::BidiOverride, Direction::Rtl) => ("\u{202e}", "\u{202c}"),
995 (UnicodeBidi::IsolateOverride, Direction::Ltr) => {
996 ("\u{2068}\u{202d}", "\u{202c}\u{2069}")
997 },
998 (UnicodeBidi::IsolateOverride, Direction::Rtl) => {
999 ("\u{2068}\u{202e}", "\u{202c}\u{2069}")
1000 },
1001 (UnicodeBidi::Plaintext, _) => ("\u{2068}", "\u{2069}"),
1002 }
1003 }
1004
1005 fn resolve_align_self(
1006 &self,
1007 resolved_auto_value: AlignItems,
1008 resolved_normal_value: AlignItems,
1009 ) -> AlignItems {
1010 match self.clone_align_self().0.0 {
1011 AlignFlags::AUTO => resolved_auto_value,
1012 AlignFlags::NORMAL => resolved_normal_value,
1013 value => AlignItems(value),
1014 }
1015 }
1016
1017 fn depends_on_block_constraints_due_to_relative_positioning(
1018 &self,
1019 writing_mode: WritingMode,
1020 ) -> bool {
1021 if !matches!(
1022 self.get_box().position,
1023 ComputedPosition::Relative | ComputedPosition::Sticky
1024 ) {
1025 return false;
1026 }
1027 let box_offsets = self.box_offsets(writing_mode);
1028 let has_percentage = |offset: LengthPercentageOrAuto<'_>| {
1029 offset
1030 .non_auto()
1031 .is_some_and(LengthPercentage::has_percentage)
1032 };
1033 has_percentage(box_offsets.block_start) || has_percentage(box_offsets.block_end)
1034 }
1035
1036 fn overflow_direction(&self) -> OverflowDirection {
1038 let inline_end_direction = self.writing_mode.inline_end_physical_side();
1039 let block_end_direction = self.writing_mode.block_end_physical_side();
1040
1041 let rightward = inline_end_direction == PhysicalSide::Right ||
1042 block_end_direction == PhysicalSide::Right;
1043 let downward = inline_end_direction == PhysicalSide::Bottom ||
1044 block_end_direction == PhysicalSide::Bottom;
1045
1046 OverflowDirection {
1048 rightward,
1049 downward,
1050 }
1051 }
1052
1053 fn to_bidi_level(&self) -> Level {
1057 if self.writing_mode.is_bidi_ltr() {
1058 Level::ltr()
1059 } else {
1060 Level::rtl()
1061 }
1062 }
1063}
1064
1065pub(crate) enum LayoutStyle<'a> {
1066 Default(&'a ComputedValues),
1067 Table(TableLayoutStyle<'a>),
1068}
1069
1070impl LayoutStyle<'_> {
1071 #[inline]
1072 pub(crate) fn style(&self) -> &ComputedValues {
1073 match self {
1074 Self::Default(style) => style,
1075 Self::Table(table) => table.style(),
1076 }
1077 }
1078
1079 #[inline]
1080 pub(crate) fn is_table(&self) -> bool {
1081 matches!(self, Self::Table(_))
1082 }
1083
1084 pub(crate) fn content_box_sizes_and_padding_border_margin(
1085 &self,
1086 containing_block: &IndefiniteContainingBlock,
1087 ) -> ContentBoxSizesAndPBM {
1088 let containing_block_size_or_zero =
1094 containing_block.size.map(|value| value.unwrap_or_default());
1095 let writing_mode = containing_block.writing_mode;
1096 let pbm = self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1097 writing_mode,
1098 containing_block_size_or_zero.inline,
1099 );
1100 let style = self.style();
1101 let box_size = style.box_size(writing_mode);
1102 let min_size = style.min_box_size(writing_mode);
1103 let max_size = style.max_box_size(writing_mode);
1104 let preferred_size_computes_to_auto = box_size.map(|size| size.is_initial());
1105
1106 let depends_on_block_constraints = |size: &Size<LengthPercentage>| {
1107 match size {
1108 Size::Stretch => true,
1116 Size::Numeric(length_percentage) => length_percentage.has_percentage(),
1117 _ => false,
1118 }
1119 };
1120 let depends_on_block_constraints = depends_on_block_constraints(&box_size.block) ||
1121 depends_on_block_constraints(&min_size.block) ||
1122 depends_on_block_constraints(&max_size.block) ||
1123 style.depends_on_block_constraints_due_to_relative_positioning(writing_mode);
1124
1125 let box_size = box_size.map_with(&containing_block.size, |size, basis| {
1126 size.resolve_percentages_for_preferred(*basis)
1127 });
1128 let content_box_size = style.content_box_size_for_box_size(box_size, &pbm);
1129 let min_size = min_size.percentages_relative_to_basis(&containing_block_size_or_zero);
1130 let content_min_box_size = style.content_min_box_size_for_min_size(min_size, &pbm);
1131 let max_size = max_size.map_with(&containing_block.size, |size, basis| {
1132 size.resolve_percentages_for_max(*basis)
1133 });
1134 let content_max_box_size = style.content_max_box_size_for_max_size(max_size, &pbm);
1135 ContentBoxSizesAndPBM {
1136 content_box_sizes: LogicalVec2 {
1137 block: Sizes::new(
1138 content_box_size.block,
1139 content_min_box_size.block,
1140 content_max_box_size.block,
1141 ),
1142 inline: Sizes::new(
1143 content_box_size.inline,
1144 content_min_box_size.inline,
1145 content_max_box_size.inline,
1146 ),
1147 },
1148 pbm,
1149 depends_on_block_constraints,
1150 preferred_size_computes_to_auto,
1151 }
1152 }
1153
1154 pub(crate) fn padding_border_margin(
1155 &self,
1156 containing_block: &ContainingBlock,
1157 ) -> PaddingBorderMargin {
1158 self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1159 containing_block.style.writing_mode,
1160 containing_block.size.inline,
1161 )
1162 }
1163
1164 pub(crate) fn padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1165 &self,
1166 writing_mode: WritingMode,
1167 containing_block_inline_size: Au,
1168 ) -> PaddingBorderMargin {
1169 let padding = self
1170 .padding(writing_mode)
1171 .percentages_relative_to(containing_block_inline_size);
1172 let style = self.style();
1173 let border = self.border_width(writing_mode);
1174 let margin = style
1175 .margin(writing_mode)
1176 .percentages_relative_to(containing_block_inline_size);
1177 PaddingBorderMargin {
1178 padding_border_sums: LogicalVec2 {
1179 inline: padding.inline_sum() + border.inline_sum(),
1180 block: padding.block_sum() + border.block_sum(),
1181 },
1182 padding,
1183 border,
1184 margin,
1185 }
1186 }
1187
1188 pub(crate) fn padding(
1189 &self,
1190 containing_block_writing_mode: WritingMode,
1191 ) -> LogicalSides<LengthPercentage> {
1192 if matches!(self, Self::Table(table) if table.collapses_borders()) {
1193 return LogicalSides::zero();
1196 }
1197 let padding = self.style().get_padding().clone();
1198 LogicalSides::from_physical(
1199 &PhysicalSides::new(
1200 padding.padding_top.0,
1201 padding.padding_right.0,
1202 padding.padding_bottom.0,
1203 padding.padding_left.0,
1204 ),
1205 containing_block_writing_mode,
1206 )
1207 }
1208
1209 pub(crate) fn border_width(
1210 &self,
1211 containing_block_writing_mode: WritingMode,
1212 ) -> LogicalSides<Au> {
1213 let border_width = match self {
1214 Self::Table(table) if table.collapses_borders() => table
1218 .halved_collapsed_border_widths()
1219 .to_physical(self.style().writing_mode),
1220 _ => {
1221 let border = self.style().get_border();
1222 PhysicalSides::new(
1223 border.border_top_width,
1224 border.border_right_width,
1225 border.border_bottom_width,
1226 border.border_left_width,
1227 )
1228 },
1229 };
1230 LogicalSides::from_physical(&border_width, containing_block_writing_mode)
1231 }
1232}
1233
1234impl From<stylo::Display> for Display {
1235 fn from(packed: stylo::Display) -> Self {
1236 let outside = packed.outside();
1237 let inside = packed.inside();
1238
1239 let outside = match outside {
1240 stylo::DisplayOutside::Block => DisplayOutside::Block,
1241 stylo::DisplayOutside::Inline => DisplayOutside::Inline,
1242 stylo::DisplayOutside::TableCaption => {
1243 return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(
1244 DisplayLayoutInternal::TableCaption,
1245 ));
1246 },
1247 stylo::DisplayOutside::InternalTable => {
1248 let internal = match inside {
1249 stylo::DisplayInside::TableRowGroup => DisplayLayoutInternal::TableRowGroup,
1250 stylo::DisplayInside::TableColumn => DisplayLayoutInternal::TableColumn,
1251 stylo::DisplayInside::TableColumnGroup => {
1252 DisplayLayoutInternal::TableColumnGroup
1253 },
1254 stylo::DisplayInside::TableHeaderGroup => {
1255 DisplayLayoutInternal::TableHeaderGroup
1256 },
1257 stylo::DisplayInside::TableFooterGroup => {
1258 DisplayLayoutInternal::TableFooterGroup
1259 },
1260 stylo::DisplayInside::TableRow => DisplayLayoutInternal::TableRow,
1261 stylo::DisplayInside::TableCell => DisplayLayoutInternal::TableCell,
1262 _ => unreachable!("Non-internal DisplayInside found"),
1263 };
1264 return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(internal));
1265 },
1266 stylo::DisplayOutside::None if inside == stylo::DisplayInside::Contents => {
1269 return Display::Contents;
1270 },
1271 stylo::DisplayOutside::None => return Display::None,
1272 };
1273
1274 let inside = match packed.inside() {
1275 stylo::DisplayInside::Flow => DisplayInside::Flow {
1276 is_list_item: packed.is_list_item(),
1277 },
1278 stylo::DisplayInside::FlowRoot => DisplayInside::FlowRoot {
1279 is_list_item: packed.is_list_item(),
1280 },
1281 stylo::DisplayInside::Flex => DisplayInside::Flex,
1282 stylo::DisplayInside::Grid => DisplayInside::Grid,
1283
1284 stylo::DisplayInside::None => return Display::None,
1286 stylo::DisplayInside::Contents => return Display::Contents,
1287
1288 stylo::DisplayInside::Table => DisplayInside::Table,
1289 stylo::DisplayInside::TableRowGroup |
1290 stylo::DisplayInside::TableColumn |
1291 stylo::DisplayInside::TableColumnGroup |
1292 stylo::DisplayInside::TableHeaderGroup |
1293 stylo::DisplayInside::TableFooterGroup |
1294 stylo::DisplayInside::TableRow |
1295 stylo::DisplayInside::TableCell => unreachable!("Internal DisplayInside found"),
1296 };
1297 Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { outside, inside })
1298 }
1299}
1300
1301pub(crate) trait Clamp: Sized {
1302 fn clamp_below_max(self, max: Option<Self>) -> Self;
1303 fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self;
1304}
1305
1306impl Clamp for Au {
1307 fn clamp_below_max(self, max: Option<Self>) -> Self {
1308 match max {
1309 None => self,
1310 Some(max) => self.min(max),
1311 }
1312 }
1313
1314 fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self {
1315 self.clamp_below_max(max).max(min)
1316 }
1317}
1318
1319pub(crate) trait TransformExt {
1320 fn change_basis(&self, x: f32, y: f32, z: f32) -> Self;
1321}
1322
1323impl TransformExt for LayoutTransform {
1324 fn change_basis(&self, x: f32, y: f32, z: f32) -> Self {
1326 let pre_translation = Self::translation(x, y, z);
1327 let post_translation = Self::translation(-x, -y, -z);
1328 post_translation.then(self).then(&pre_translation)
1329 }
1330}