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 used_transform_style(&self, fragment_flags: FragmentFlags) -> ComputedTransformStyle;
337 fn establishes_block_formatting_context(&self, fragment_flags: FragmentFlags) -> bool;
338 fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool;
339 fn establishes_scroll_container(&self, fragment_flags: FragmentFlags) -> bool;
340 fn establishes_containing_block_for_absolute_descendants(
341 &self,
342 fragment_flags: FragmentFlags,
343 ) -> bool;
344 fn establishes_containing_block_for_all_descendants(
345 &self,
346 fragment_flags: FragmentFlags,
347 ) -> bool;
348 fn preferred_aspect_ratio(
349 &self,
350 natural_aspect_ratio: Option<CSSFloat>,
351 padding_border_sums: &LogicalVec2<Au>,
352 ) -> Option<AspectRatio>;
353 fn background_is_transparent(&self) -> bool;
354 fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags;
355 fn bidi_control_chars(&self) -> (&'static str, &'static str);
356 fn resolve_align_self(
357 &self,
358 resolved_auto_value: ItemPlacement,
359 resolved_normal_value: AlignFlags,
360 ) -> SelfAlignment;
361 fn depends_on_block_constraints_due_to_relative_positioning(
362 &self,
363 writing_mode: WritingMode,
364 ) -> bool;
365 fn is_inline_box(&self, fragment_flags: FragmentFlags) -> bool;
366 fn is_atomic_inline_level(&self, fragment_flags: FragmentFlags) -> bool;
367 fn overflow_direction(&self) -> OverflowDirection;
368 fn to_bidi_level(&self) -> Level;
369}
370
371impl ComputedValuesExt for ComputedValues {
372 fn physical_box_offsets(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>> {
373 fn convert(inset: &Inset) -> LengthPercentageOrAuto<'_> {
374 match inset {
375 Inset::LengthPercentage(v) => LengthPercentageOrAuto::LengthPercentage(v),
376 Inset::Auto => LengthPercentageOrAuto::Auto,
377 Inset::AnchorFunction(_) => unreachable!("anchor() should be disabled"),
378 Inset::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"),
379 Inset::AnchorContainingCalcFunction(_) => {
380 unreachable!("anchor() and anchor-size() should be disabled")
381 },
382 }
383 }
384 let position = self.get_position();
385 PhysicalSides::new(
386 convert(&position.top),
387 convert(&position.right),
388 convert(&position.bottom),
389 convert(&position.left),
390 )
391 }
392
393 fn box_offsets(&self, writing_mode: WritingMode) -> LogicalSides<LengthPercentageOrAuto<'_>> {
394 LogicalSides::from_physical(&self.physical_box_offsets(), writing_mode)
395 }
396
397 fn box_size(
398 &self,
399 containing_block_writing_mode: WritingMode,
400 ) -> LogicalVec2<Size<LengthPercentage>> {
401 let position = self.get_position();
402 LogicalVec2::from_physical_size(
403 &PhysicalSize::new(
404 position.clone_width().into(),
405 position.clone_height().into(),
406 ),
407 containing_block_writing_mode,
408 )
409 }
410
411 fn min_box_size(
412 &self,
413 containing_block_writing_mode: WritingMode,
414 ) -> LogicalVec2<Size<LengthPercentage>> {
415 let position = self.get_position();
416 LogicalVec2::from_physical_size(
417 &PhysicalSize::new(
418 position.clone_min_width().into(),
419 position.clone_min_height().into(),
420 ),
421 containing_block_writing_mode,
422 )
423 }
424
425 fn max_box_size(
426 &self,
427 containing_block_writing_mode: WritingMode,
428 ) -> LogicalVec2<Size<LengthPercentage>> {
429 let position = self.get_position();
430 LogicalVec2::from_physical_size(
431 &PhysicalSize::new(
432 position.clone_max_width().into(),
433 position.clone_max_height().into(),
434 ),
435 containing_block_writing_mode,
436 )
437 }
438
439 fn content_box_size_for_box_size(
440 &self,
441 box_size: LogicalVec2<Size<Au>>,
442 pbm: &PaddingBorderMargin,
443 ) -> LogicalVec2<Size<Au>> {
444 match self.get_position().box_sizing {
445 BoxSizing::ContentBox => box_size,
446 BoxSizing::BorderBox => box_size.map_inline_and_block_sizes(
449 |value| value - pbm.padding_border_sums.inline,
450 |value| value - pbm.padding_border_sums.block,
451 ),
452 }
453 }
454
455 fn content_min_box_size_for_min_size(
456 &self,
457 min_box_size: LogicalVec2<Size<Au>>,
458 pbm: &PaddingBorderMargin,
459 ) -> LogicalVec2<Size<Au>> {
460 match self.get_position().box_sizing {
461 BoxSizing::ContentBox => min_box_size,
462 BoxSizing::BorderBox => min_box_size.map_inline_and_block_sizes(
464 |value| Au::zero().max(value - pbm.padding_border_sums.inline),
465 |value| Au::zero().max(value - pbm.padding_border_sums.block),
466 ),
467 }
468 }
469
470 fn content_max_box_size_for_max_size(
471 &self,
472 max_box_size: LogicalVec2<Size<Au>>,
473 pbm: &PaddingBorderMargin,
474 ) -> LogicalVec2<Size<Au>> {
475 match self.get_position().box_sizing {
476 BoxSizing::ContentBox => max_box_size,
477 BoxSizing::BorderBox => max_box_size.map_inline_and_block_sizes(
480 |value| value - pbm.padding_border_sums.inline,
481 |value| value - pbm.padding_border_sums.block,
482 ),
483 }
484 }
485
486 fn border_style_color(
487 &self,
488 containing_block_writing_mode: WritingMode,
489 ) -> LogicalSides<BorderStyleColor> {
490 let current_color = self.get_inherited_text().clone_color();
491 LogicalSides::from_physical(
492 &BorderStyleColor::from_border(self.get_border(), ¤t_color),
493 containing_block_writing_mode,
494 )
495 }
496
497 fn physical_margin(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>> {
498 fn convert(inset: &Margin) -> LengthPercentageOrAuto<'_> {
499 match inset {
500 Margin::LengthPercentage(v) => LengthPercentageOrAuto::LengthPercentage(v),
501 Margin::Auto => LengthPercentageOrAuto::Auto,
502 Margin::AnchorSizeFunction(_) | Margin::AnchorContainingCalcFunction(_) => {
503 unreachable!("anchor-size() should be disabled")
504 },
505 }
506 }
507 let margin = self.get_margin();
508 PhysicalSides::new(
509 convert(&margin.margin_top),
510 convert(&margin.margin_right),
511 convert(&margin.margin_bottom),
512 convert(&margin.margin_left),
513 )
514 }
515
516 fn margin(
517 &self,
518 containing_block_writing_mode: WritingMode,
519 ) -> LogicalSides<LengthPercentageOrAuto<'_>> {
520 LogicalSides::from_physical(&self.physical_margin(), containing_block_writing_mode)
521 }
522
523 fn is_inline_box(&self, fragment_flags: FragmentFlags) -> bool {
524 (self.get_box().display.is_inline_flow() &&
525 !fragment_flags.intersects(
526 FragmentFlags::IS_REPLACED |
527 FragmentFlags::IS_WIDGET |
528 FragmentFlags::IS_FLEX_OR_GRID_ITEM,
529 )) ||
530 matches!(self.pseudo(), Some(PseudoElement::FirstLetter))
531 }
532
533 fn is_atomic_inline_level(&self, fragment_flags: FragmentFlags) -> bool {
534 self.get_box().display.outside() == stylo::DisplayOutside::Inline &&
535 !self.is_inline_box(fragment_flags) &&
536 !fragment_flags.intersects(FragmentFlags::IS_FLEX_OR_GRID_ITEM)
537 }
538
539 fn is_transformable(&self, fragment_flags: FragmentFlags) -> bool {
541 !self.is_inline_box(fragment_flags)
551 }
552
553 fn has_transform_or_perspective_style(&self) -> bool {
555 !self.get_box().transform.0.is_empty() ||
556 self.get_box().scale != GenericScale::None ||
557 self.get_box().rotate != GenericRotate::None ||
558 self.get_box().translate != GenericTranslate::None ||
559 self.get_box().perspective != Perspective::None
560 }
561
562 #[inline]
565 fn has_effective_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool {
566 self.is_transformable(fragment_flags) && self.has_transform_or_perspective_style()
567 }
568
569 fn z_index_applies(&self, fragment_flags: FragmentFlags) -> bool {
571 if self.get_box().position != ComputedPosition::Static {
574 return true;
575 }
576 fragment_flags.contains(FragmentFlags::IS_FLEX_OR_GRID_ITEM)
588 }
589
590 fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32 {
594 if self.z_index_applies(fragment_flags) {
595 self.get_position().z_index.integer_or(0)
596 } else {
597 0
598 }
599 }
600
601 fn effective_overflow(&self, fragment_flags: FragmentFlags) -> AxesOverflow {
605 if fragment_flags.contains(FragmentFlags::PROPAGATED_OVERFLOW_TO_VIEWPORT) {
608 return AxesOverflow::default();
609 }
610
611 let mut overflow = AxesOverflow::from(self);
612
613 if fragment_flags.contains(FragmentFlags::IS_REPLACED) {
616 if overflow.x != Overflow::Visible {
617 overflow.x = Overflow::Clip;
618 }
619 if overflow.y != Overflow::Visible {
620 overflow.y = Overflow::Clip;
621 }
622 return overflow;
623 }
624
625 let ignores_overflow = match self.get_box().display.inside() {
626 stylo::DisplayInside::Flow => self.is_inline_box(fragment_flags),
629
630 stylo::DisplayInside::Table => {
638 !matches!(self.pseudo(), Some(PseudoElement::ServoTableGrid)) ||
639 matches!(overflow.x, Overflow::Auto | Overflow::Scroll) ||
640 matches!(overflow.y, Overflow::Auto | Overflow::Scroll)
641 },
642
643 stylo::DisplayInside::TableColumn |
646 stylo::DisplayInside::TableColumnGroup |
647 stylo::DisplayInside::TableRow |
648 stylo::DisplayInside::TableRowGroup |
649 stylo::DisplayInside::TableHeaderGroup |
650 stylo::DisplayInside::TableFooterGroup => true,
651
652 _ => false,
653 };
654 if ignores_overflow {
655 return AxesOverflow::default();
656 }
657
658 overflow
659 }
660
661 fn used_transform_style(&self, fragment_flags: FragmentFlags) -> ComputedTransformStyle {
664 let box_style = self.get_box();
667 if box_style.transform_style == ComputedTransformStyle::Flat {
668 return ComputedTransformStyle::Flat;
669 }
670
671 let effects = self.get_effects();
688 let overflow = self.effective_overflow(fragment_flags);
689 if !matches!(overflow.x, Overflow::Visible | Overflow::Clip) ||
690 !matches!(overflow.y, Overflow::Visible | Overflow::Clip) ||
691 effects.opacity < 1.0 ||
692 !effects.filter.0.is_empty() ||
693 !effects.clip.is_auto() ||
694 self.get_svg().clip_path != ClipPath::None ||
695 self.get_box().isolation == ComputedIsolation::Isolate ||
696 effects.mix_blend_mode != ComputedMixBlendMode::Normal
697 {
698 return ComputedTransformStyle::Flat;
699 }
700
701 box_style.transform_style
703 }
704
705 fn establishes_block_formatting_context(&self, fragment_flags: FragmentFlags) -> bool {
712 if self.establishes_scroll_container(fragment_flags) {
713 return true;
714 }
715
716 if self.get_column().is_multicol() {
717 return true;
718 }
719
720 if self.get_column().column_span == ColumnSpan::All {
721 return true;
722 }
723
724 if self.get_position().align_content.primary() != AlignFlags::NORMAL {
730 return true;
731 }
732
733 false
735 }
736
737 fn establishes_scroll_container(&self, fragment_flags: FragmentFlags) -> bool {
739 self.effective_overflow(fragment_flags)
740 .establishes_scroll_container()
741 }
742
743 fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool {
745 let will_change_bits = self.clone_will_change().bits;
749 if will_change_bits
750 .intersects(WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::OPACITY)
751 {
752 return true;
753 }
754
755 if self.z_index_applies(fragment_flags) &&
758 (!self.get_position().z_index.is_auto() ||
759 will_change_bits.intersects(WillChangeBits::Z_INDEX))
760 {
761 return true;
762 }
763
764 if matches!(
767 self.get_box().position,
768 ComputedPosition::Fixed | ComputedPosition::Sticky
769 ) {
770 return true;
771 }
772
773 if self.is_transformable(fragment_flags) &&
788 (self.has_transform_or_perspective_style() ||
789 self.used_transform_style(fragment_flags) ==
790 ComputedTransformStyle::Preserve3d ||
791 will_change_bits
792 .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
793 {
794 return true;
795 }
796
797 let effects = self.get_effects();
801 if effects.opacity != 1.0 {
802 return true;
803 }
804
805 if !effects.filter.0.is_empty() {
809 return true;
810 }
811
812 if effects.mix_blend_mode != ComputedMixBlendMode::Normal {
816 return true;
817 }
818
819 if self.get_svg().clip_path != ClipPath::None {
823 return true;
824 }
825
826 if self.get_box().isolation == ComputedIsolation::Isolate {
830 return true;
831 }
832
833 if fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT) {
836 return true;
837 }
838
839 false
841 }
842
843 fn establishes_containing_block_for_absolute_descendants(
850 &self,
851 fragment_flags: FragmentFlags,
852 ) -> bool {
853 if self.establishes_containing_block_for_all_descendants(fragment_flags) {
854 return true;
855 }
856
857 if self
862 .clone_will_change()
863 .bits
864 .intersects(WillChangeBits::POSITION)
865 {
866 return true;
867 }
868
869 self.clone_position() != ComputedPosition::Static
870 }
871
872 fn establishes_containing_block_for_all_descendants(
877 &self,
878 fragment_flags: FragmentFlags,
879 ) -> bool {
880 let will_change_bits = self.clone_will_change().bits;
885
886 if self.is_transformable(fragment_flags) &&
902 (self.has_transform_or_perspective_style() ||
903 self.used_transform_style(fragment_flags) ==
904 ComputedTransformStyle::Preserve3d ||
905 will_change_bits
906 .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
907 {
908 return true;
909 }
910
911 if !fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT) &&
916 (!self.get_effects().filter.0.is_empty() ||
917 will_change_bits.intersects(WillChangeBits::FIXPOS_CB_NON_SVG))
918 {
919 return true;
920 }
921
922 false
924 }
925
926 fn preferred_aspect_ratio(
930 &self,
931 natural_aspect_ratio: Option<CSSFloat>,
932 padding_border_sums: &LogicalVec2<Au>,
933 ) -> Option<AspectRatio> {
934 let GenericAspectRatio {
935 auto,
936 ratio: mut preferred_ratio,
937 } = self.clone_aspect_ratio();
938
939 if matches!(preferred_ratio, PreferredRatio::Ratio(ratio) if ratio.is_degenerate()) {
942 preferred_ratio = PreferredRatio::None;
943 }
944
945 let to_logical_ratio = |physical_ratio| {
946 if self.writing_mode.is_horizontal() {
947 physical_ratio
948 } else {
949 1.0 / physical_ratio
950 }
951 };
952
953 match (auto, preferred_ratio) {
954 (_, PreferredRatio::None) => natural_aspect_ratio
962 .map(to_logical_ratio)
963 .map(AspectRatio::from_logical_content_ratio),
964 (true, PreferredRatio::Ratio(preferred_ratio)) => Some({
971 let physical_ratio = natural_aspect_ratio
972 .unwrap_or_else(|| (preferred_ratio.0).0 / (preferred_ratio.1).0);
973 AspectRatio::from_logical_content_ratio(to_logical_ratio(physical_ratio))
974 }),
975
976 (false, PreferredRatio::Ratio(preferred_ratio)) => {
980 let box_sizing_adjustment = match self.clone_box_sizing() {
983 BoxSizing::ContentBox => LogicalVec2::zero(),
984 BoxSizing::BorderBox => *padding_border_sums,
985 };
986 Some(AspectRatio {
987 i_over_b: to_logical_ratio((preferred_ratio.0).0 / (preferred_ratio.1).0),
988 box_sizing_adjustment,
989 })
990 },
991 }
992 }
993
994 fn background_is_transparent(&self) -> bool {
996 let background = self.get_background();
997 let color = self.resolve_color(&background.background_color);
998 color.alpha == 0.0 &&
999 background
1000 .background_image
1001 .0
1002 .iter()
1003 .all(|layer| matches!(layer, ComputedImageLayer::None))
1004 }
1005
1006 fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags {
1009 match self.get_box().backface_visibility {
1010 BackfaceVisiblity::Visible => wr::PrimitiveFlags::default(),
1011 BackfaceVisiblity::Hidden => wr::PrimitiveFlags::empty(),
1012 }
1013 }
1014
1015 fn bidi_control_chars(&self) -> (&'static str, &'static str) {
1019 match (
1020 self.get_text().unicode_bidi,
1021 self.get_inherited_box().direction,
1022 ) {
1023 (UnicodeBidi::Normal, _) => ("", ""),
1024 (UnicodeBidi::Embed, Direction::Ltr) => ("\u{202a}", "\u{202c}"),
1025 (UnicodeBidi::Embed, Direction::Rtl) => ("\u{202b}", "\u{202c}"),
1026 (UnicodeBidi::Isolate, Direction::Ltr) => ("\u{2066}", "\u{2069}"),
1027 (UnicodeBidi::Isolate, Direction::Rtl) => ("\u{2067}", "\u{2069}"),
1028 (UnicodeBidi::BidiOverride, Direction::Ltr) => ("\u{202d}", "\u{202c}"),
1029 (UnicodeBidi::BidiOverride, Direction::Rtl) => ("\u{202e}", "\u{202c}"),
1030 (UnicodeBidi::IsolateOverride, Direction::Ltr) => {
1031 ("\u{2068}\u{202d}", "\u{202c}\u{2069}")
1032 },
1033 (UnicodeBidi::IsolateOverride, Direction::Rtl) => {
1034 ("\u{2068}\u{202e}", "\u{202c}\u{2069}")
1035 },
1036 (UnicodeBidi::Plaintext, _) => ("\u{2068}", "\u{2069}"),
1037 }
1038 }
1039
1040 fn resolve_align_self(
1041 &self,
1042 resolved_auto_value: ItemPlacement,
1043 resolved_normal_value: AlignFlags,
1044 ) -> SelfAlignment {
1045 SelfAlignment(match self.clone_align_self().0 {
1046 AlignFlags::AUTO => resolved_auto_value.0,
1047 AlignFlags::NORMAL => resolved_normal_value,
1048 value => value,
1049 })
1050 }
1051
1052 fn depends_on_block_constraints_due_to_relative_positioning(
1053 &self,
1054 writing_mode: WritingMode,
1055 ) -> bool {
1056 if !matches!(
1057 self.get_box().position,
1058 ComputedPosition::Relative | ComputedPosition::Sticky
1059 ) {
1060 return false;
1061 }
1062 let box_offsets = self.box_offsets(writing_mode);
1063 let has_percentage = |offset: LengthPercentageOrAuto<'_>| {
1064 offset
1065 .non_auto()
1066 .is_some_and(LengthPercentage::has_percentage)
1067 };
1068 has_percentage(box_offsets.block_start) || has_percentage(box_offsets.block_end)
1069 }
1070
1071 fn overflow_direction(&self) -> OverflowDirection {
1073 let inline_end_direction = self.writing_mode.inline_end_physical_side();
1074 let block_end_direction = self.writing_mode.block_end_physical_side();
1075
1076 let rightward = inline_end_direction == PhysicalSide::Right ||
1077 block_end_direction == PhysicalSide::Right;
1078 let downward = inline_end_direction == PhysicalSide::Bottom ||
1079 block_end_direction == PhysicalSide::Bottom;
1080
1081 OverflowDirection {
1083 rightward,
1084 downward,
1085 }
1086 }
1087
1088 fn to_bidi_level(&self) -> Level {
1092 if self.writing_mode.is_bidi_ltr() {
1093 Level::ltr()
1094 } else {
1095 Level::rtl()
1096 }
1097 }
1098}
1099
1100pub(crate) enum LayoutStyle<'a> {
1101 Default(&'a ComputedValues),
1102 Table(TableLayoutStyle<'a>),
1103}
1104
1105impl LayoutStyle<'_> {
1106 #[inline]
1107 pub(crate) fn style(&self) -> &ComputedValues {
1108 match self {
1109 Self::Default(style) => style,
1110 Self::Table(table) => table.style(),
1111 }
1112 }
1113
1114 #[inline]
1115 pub(crate) fn is_table(&self) -> bool {
1116 matches!(self, Self::Table(_))
1117 }
1118
1119 pub(crate) fn content_box_sizes_and_padding_border_margin(
1120 &self,
1121 containing_block: &IndefiniteContainingBlock,
1122 ) -> ContentBoxSizesAndPBM {
1123 let containing_block_size_or_zero =
1129 containing_block.size.map(|value| value.unwrap_or_default());
1130 let writing_mode = containing_block.style.writing_mode;
1131 let pbm = self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1132 writing_mode,
1133 containing_block_size_or_zero.inline,
1134 );
1135 let style = self.style();
1136 let box_size = style.box_size(writing_mode);
1137 let min_size = style.min_box_size(writing_mode);
1138 let max_size = style.max_box_size(writing_mode);
1139 let preferred_size_computes_to_auto = box_size.map(|size| size.is_initial());
1140
1141 let depends_on_block_constraints = |size: &Size<LengthPercentage>| {
1142 match size {
1143 Size::Stretch => true,
1151 Size::Numeric(length_percentage) => length_percentage.has_percentage(),
1152 _ => false,
1153 }
1154 };
1155 let depends_on_block_constraints = depends_on_block_constraints(&box_size.block) ||
1156 depends_on_block_constraints(&min_size.block) ||
1157 depends_on_block_constraints(&max_size.block) ||
1158 style.depends_on_block_constraints_due_to_relative_positioning(writing_mode);
1159
1160 let box_size = box_size.map_with(&containing_block.size, |size, basis| {
1161 size.resolve_percentages_for_preferred(*basis)
1162 });
1163 let content_box_size = style.content_box_size_for_box_size(box_size, &pbm);
1164 let min_size = min_size.percentages_relative_to_basis(&containing_block_size_or_zero);
1165 let content_min_box_size = style.content_min_box_size_for_min_size(min_size, &pbm);
1166 let max_size = max_size.map_with(&containing_block.size, |size, basis| {
1167 size.resolve_percentages_for_max(*basis)
1168 });
1169 let content_max_box_size = style.content_max_box_size_for_max_size(max_size, &pbm);
1170 ContentBoxSizesAndPBM {
1171 content_box_sizes: LogicalVec2 {
1172 block: Sizes::new(
1173 content_box_size.block,
1174 content_min_box_size.block,
1175 content_max_box_size.block,
1176 ),
1177 inline: Sizes::new(
1178 content_box_size.inline,
1179 content_min_box_size.inline,
1180 content_max_box_size.inline,
1181 ),
1182 },
1183 pbm,
1184 depends_on_block_constraints,
1185 preferred_size_computes_to_auto,
1186 }
1187 }
1188
1189 pub(crate) fn padding_border_margin(
1190 &self,
1191 containing_block: &ContainingBlock,
1192 ) -> PaddingBorderMargin {
1193 self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1194 containing_block.style.writing_mode,
1195 containing_block.size.inline,
1196 )
1197 }
1198
1199 pub(crate) fn padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1200 &self,
1201 writing_mode: WritingMode,
1202 containing_block_inline_size: Au,
1203 ) -> PaddingBorderMargin {
1204 let padding = self
1205 .padding(writing_mode)
1206 .percentages_relative_to(containing_block_inline_size);
1207 let style = self.style();
1208 let border = self.border_width(writing_mode);
1209 let margin = style
1210 .margin(writing_mode)
1211 .percentages_relative_to(containing_block_inline_size);
1212 PaddingBorderMargin {
1213 padding_border_sums: LogicalVec2 {
1214 inline: padding.inline_sum() + border.inline_sum(),
1215 block: padding.block_sum() + border.block_sum(),
1216 },
1217 padding,
1218 border,
1219 margin,
1220 }
1221 }
1222
1223 pub(crate) fn padding(
1224 &self,
1225 containing_block_writing_mode: WritingMode,
1226 ) -> LogicalSides<LengthPercentage> {
1227 if matches!(self, Self::Table(table) if table.collapses_borders()) {
1228 return LogicalSides::zero();
1231 }
1232 let padding = self.style().get_padding().clone();
1233 LogicalSides::from_physical(
1234 &PhysicalSides::new(
1235 padding.padding_top.0,
1236 padding.padding_right.0,
1237 padding.padding_bottom.0,
1238 padding.padding_left.0,
1239 ),
1240 containing_block_writing_mode,
1241 )
1242 }
1243
1244 pub(crate) fn border_width(
1245 &self,
1246 containing_block_writing_mode: WritingMode,
1247 ) -> LogicalSides<Au> {
1248 let border_width = match self {
1249 Self::Table(table) if table.collapses_borders() => table
1253 .halved_collapsed_border_widths()
1254 .to_physical(self.style().writing_mode),
1255 _ => {
1256 let border = self.style().get_border();
1257 let resolve = |width: &BorderSideWidth, style: BorderStyle| {
1258 if style.none_or_hidden() {
1259 Au::zero()
1260 } else {
1261 width.0
1262 }
1263 };
1264 PhysicalSides::new(
1265 resolve(&border.border_top_width, border.border_top_style),
1266 resolve(&border.border_right_width, border.border_right_style),
1267 resolve(&border.border_bottom_width, border.border_bottom_style),
1268 resolve(&border.border_left_width, border.border_left_style),
1269 )
1270 },
1271 };
1272 LogicalSides::from_physical(&border_width, containing_block_writing_mode)
1273 }
1274}
1275
1276impl From<stylo::Display> for Display {
1277 fn from(packed: stylo::Display) -> Self {
1278 let outside = packed.outside();
1279 let inside = packed.inside();
1280
1281 let outside = match outside {
1282 stylo::DisplayOutside::Block => DisplayOutside::Block,
1283 stylo::DisplayOutside::Inline => DisplayOutside::Inline,
1284 stylo::DisplayOutside::TableCaption => {
1285 return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(
1286 DisplayLayoutInternal::TableCaption,
1287 ));
1288 },
1289 stylo::DisplayOutside::InternalTable => {
1290 let internal = match inside {
1291 stylo::DisplayInside::TableRowGroup => DisplayLayoutInternal::TableRowGroup,
1292 stylo::DisplayInside::TableColumn => DisplayLayoutInternal::TableColumn,
1293 stylo::DisplayInside::TableColumnGroup => {
1294 DisplayLayoutInternal::TableColumnGroup
1295 },
1296 stylo::DisplayInside::TableHeaderGroup => {
1297 DisplayLayoutInternal::TableHeaderGroup
1298 },
1299 stylo::DisplayInside::TableFooterGroup => {
1300 DisplayLayoutInternal::TableFooterGroup
1301 },
1302 stylo::DisplayInside::TableRow => DisplayLayoutInternal::TableRow,
1303 stylo::DisplayInside::TableCell => DisplayLayoutInternal::TableCell,
1304 _ => unreachable!("Non-internal DisplayInside found"),
1305 };
1306 return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(internal));
1307 },
1308 stylo::DisplayOutside::None if inside == stylo::DisplayInside::Contents => {
1311 return Display::Contents;
1312 },
1313 stylo::DisplayOutside::None => return Display::None,
1314 };
1315
1316 let inside = match inside {
1317 stylo::DisplayInside::Flow => DisplayInside::Flow {
1318 is_list_item: packed.is_list_item(),
1319 },
1320 stylo::DisplayInside::FlowRoot => DisplayInside::FlowRoot {
1321 is_list_item: packed.is_list_item(),
1322 },
1323 stylo::DisplayInside::Flex => DisplayInside::Flex,
1324 stylo::DisplayInside::Grid => DisplayInside::Grid,
1325 stylo::DisplayInside::Table => DisplayInside::Table,
1326
1327 stylo::DisplayInside::None => return Display::None,
1329 stylo::DisplayInside::Contents => return Display::Contents,
1330
1331 stylo::DisplayInside::TableRowGroup |
1332 stylo::DisplayInside::TableColumn |
1333 stylo::DisplayInside::TableColumnGroup |
1334 stylo::DisplayInside::TableHeaderGroup |
1335 stylo::DisplayInside::TableFooterGroup |
1336 stylo::DisplayInside::TableRow |
1337 stylo::DisplayInside::TableCell => unreachable!("Internal DisplayInside found"),
1338 };
1339 Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { outside, inside })
1340 }
1341}
1342
1343pub(crate) trait Clamp: Sized {
1344 fn clamp_below_max(self, max: Option<Self>) -> Self;
1345 fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self;
1346}
1347
1348impl Clamp for Au {
1349 fn clamp_below_max(self, max: Option<Self>) -> Self {
1350 match max {
1351 None => self,
1352 Some(max) => self.min(max),
1353 }
1354 }
1355
1356 fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self {
1357 self.clamp_below_max(max).max(min)
1358 }
1359}
1360
1361pub(crate) trait TransformExt {
1362 fn change_basis(&self, x: f32, y: f32, z: f32) -> Self;
1363}
1364
1365impl TransformExt for LayoutTransform {
1366 fn change_basis(&self, x: f32, y: f32, z: f32) -> Self {
1368 let pre_translation = Self::translation(x, y, z);
1369 let post_translation = Self::translation(-x, -y, -z);
1370 post_translation.then(self).then(&pre_translation)
1371 }
1372}