1use app_units::Au;
6use layout_api::{AxesOverflow, LayoutElementType, LayoutNode, LayoutNodeType};
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::{LogicalAxis, 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, TextWrapMode, WhiteSpaceCollapse,
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, NodeAndStyleInfo};
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(
76 &self,
77 contents: &Contents,
78 info: &NodeAndStyleInfo,
79 ) -> Self {
80 if matches!(self, Self::LayoutInternal(_)) && contents.is_replaced() {
85 Self::OutsideInside {
86 outside: DisplayOutside::Inline,
87 inside: DisplayInside::Flow {
88 is_list_item: false,
89 },
90 }
91 } else if matches!(contents, Contents::Widget(_)) {
92 if let DisplayGeneratingBox::OutsideInside { outside, inside } = self &&
99 (matches!(
100 inside,
101 DisplayInside::Flow { .. } | DisplayInside::FlowRoot { .. }
102 ) || info.node.type_id() !=
103 Some(LayoutNodeType::Element(
104 LayoutElementType::HTMLButtonElement,
105 )))
106 {
107 DisplayGeneratingBox::OutsideInside {
108 outside: *outside,
109 inside: DisplayInside::FlowRoot {
110 is_list_item: false,
111 },
112 }
113 } else {
114 *self
115 }
116 } else {
117 *self
118 }
119 }
120}
121
122#[derive(Clone, Copy, Debug, Eq, PartialEq)]
123pub(crate) enum DisplayOutside {
124 Block,
125 Inline,
126}
127
128#[derive(Clone, Copy, Debug, Eq, PartialEq)]
129pub(crate) enum DisplayInside {
130 Flow { is_list_item: bool },
133 FlowRoot { is_list_item: bool },
134 Flex,
135 Grid,
136 Table,
137}
138
139#[derive(Clone, Copy, Debug, Eq, PartialEq)]
140#[expect(clippy::enum_variant_names)]
141pub(crate) enum DisplayLayoutInternal {
143 TableCaption,
144 TableCell,
145 TableColumn,
146 TableColumnGroup,
147 TableFooterGroup,
148 TableHeaderGroup,
149 TableRow,
150 TableRowGroup,
151}
152
153impl DisplayLayoutInternal {
154 pub(crate) fn display_inside(&self) -> DisplayInside {
156 DisplayInside::FlowRoot {
160 is_list_item: false,
161 }
162 }
163}
164
165#[derive(Clone, Debug)]
167pub(crate) struct PaddingBorderMargin {
168 pub padding: LogicalSides<Au>,
169 pub border: LogicalSides<Au>,
170 pub margin: LogicalSides<AuOrAuto>,
171
172 pub padding_border_sums: LogicalVec2<Au>,
174}
175
176impl PaddingBorderMargin {
177 pub(crate) fn zero() -> Self {
178 Self {
179 padding: LogicalSides::zero(),
180 border: LogicalSides::zero(),
181 margin: LogicalSides::zero(),
182 padding_border_sums: LogicalVec2::zero(),
183 }
184 }
185
186 pub(crate) fn sums_auto_is_zero(
187 &self,
188 ignore_block_margins: LogicalSides1D<bool>,
189 ) -> LogicalVec2<Au> {
190 let margin = self.margin.auto_is(Au::zero);
191 let mut sums = self.padding_border_sums;
192 sums.inline += margin.inline_sum();
193 if !ignore_block_margins.start {
194 sums.block += margin.block_start;
195 }
196 if !ignore_block_margins.end {
197 sums.block += margin.block_end;
198 }
199 sums
200 }
201}
202
203#[derive(Clone, Copy, Debug)]
207pub(crate) struct AspectRatio {
208 box_sizing_adjustment: LogicalVec2<Au>,
213 i_over_b: CSSFloat,
215}
216
217impl AspectRatio {
218 pub(crate) fn compute_dependent_size(
220 &self,
221 ratio_dependent_axis: LogicalAxis,
222 ratio_determining_size: Au,
223 ) -> Au {
224 match ratio_dependent_axis {
225 LogicalAxis::Inline => {
227 (ratio_determining_size + self.box_sizing_adjustment.block).scale_by(self.i_over_b) -
228 self.box_sizing_adjustment.inline
229 },
230 LogicalAxis::Block => {
232 (ratio_determining_size + self.box_sizing_adjustment.inline)
233 .scale_by(1.0 / self.i_over_b) -
234 self.box_sizing_adjustment.block
235 },
236 }
237 }
238
239 pub(crate) fn from_logical_content_ratio(i_over_b: CSSFloat) -> Self {
240 Self {
241 box_sizing_adjustment: LogicalVec2::zero(),
242 i_over_b,
243 }
244 }
245}
246
247#[derive(Clone)]
248pub(crate) struct ContentBoxSizesAndPBM {
249 pub content_box_sizes: LogicalVec2<Sizes>,
250 pub pbm: PaddingBorderMargin,
251 pub depends_on_block_constraints: bool,
252 pub preferred_size_computes_to_auto: LogicalVec2<bool>,
253}
254
255#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
256pub(crate) struct BorderStyleColor {
257 pub style: BorderStyle,
258 pub color: AbsoluteColor,
259}
260
261impl BorderStyleColor {
262 pub(crate) fn new(style: BorderStyle, color: AbsoluteColor) -> Self {
263 Self { style, color }
264 }
265
266 pub(crate) fn from_border(
267 border: &Border,
268 current_color: &AbsoluteColor,
269 ) -> PhysicalSides<Self> {
270 let resolve = |color: &Color| color.resolve_to_absolute(current_color);
271 PhysicalSides::<Self>::new(
272 Self::new(border.border_top_style, resolve(&border.border_top_color)),
273 Self::new(
274 border.border_right_style,
275 resolve(&border.border_right_color),
276 ),
277 Self::new(
278 border.border_bottom_style,
279 resolve(&border.border_bottom_color),
280 ),
281 Self::new(border.border_left_style, resolve(&border.border_left_color)),
282 )
283 }
284
285 pub(crate) fn hidden() -> Self {
286 Self::new(BorderStyle::Hidden, AbsoluteColor::TRANSPARENT_BLACK)
287 }
288}
289
290impl Default for BorderStyleColor {
291 fn default() -> Self {
292 Self::new(BorderStyle::None, AbsoluteColor::TRANSPARENT_BLACK)
293 }
294}
295
296pub(crate) struct OverflowDirection {
300 pub rightward: bool,
302 pub downward: bool,
304}
305
306pub(crate) trait ComputedValuesExt {
307 fn physical_box_offsets(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>>;
308 fn box_offsets(&self, writing_mode: WritingMode) -> LogicalSides<LengthPercentageOrAuto<'_>>;
309 fn box_size(
310 &self,
311 containing_block_writing_mode: WritingMode,
312 ) -> LogicalVec2<Size<LengthPercentage>>;
313 fn min_box_size(
314 &self,
315 containing_block_writing_mode: WritingMode,
316 ) -> LogicalVec2<Size<LengthPercentage>>;
317 fn max_box_size(
318 &self,
319 containing_block_writing_mode: WritingMode,
320 ) -> LogicalVec2<Size<LengthPercentage>>;
321 fn content_box_size_for_box_size(
322 &self,
323 box_size: LogicalVec2<Size<Au>>,
324 pbm: &PaddingBorderMargin,
325 ) -> LogicalVec2<Size<Au>>;
326 fn content_min_box_size_for_min_size(
327 &self,
328 box_size: LogicalVec2<Size<Au>>,
329 pbm: &PaddingBorderMargin,
330 ) -> LogicalVec2<Size<Au>>;
331 fn content_max_box_size_for_max_size(
332 &self,
333 box_size: LogicalVec2<Size<Au>>,
334 pbm: &PaddingBorderMargin,
335 ) -> LogicalVec2<Size<Au>>;
336 fn border_style_color(
337 &self,
338 containing_block_writing_mode: WritingMode,
339 ) -> LogicalSides<BorderStyleColor>;
340 fn physical_margin(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>>;
341 fn margin(
342 &self,
343 containing_block_writing_mode: WritingMode,
344 ) -> LogicalSides<LengthPercentageOrAuto<'_>>;
345 fn is_transformable(&self, fragment_flags: FragmentFlags) -> bool;
346 fn has_transform_or_perspective_style(&self) -> bool;
347 fn has_effective_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool;
348 fn z_index_applies(&self, fragment_flags: FragmentFlags) -> bool;
349 fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32;
350 fn effective_overflow(&self, fragment_flags: FragmentFlags) -> AxesOverflow;
351 fn used_transform_style(&self, fragment_flags: FragmentFlags) -> ComputedTransformStyle;
352 fn establishes_block_formatting_context(&self, fragment_flags: FragmentFlags) -> bool;
353 fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool;
354 fn establishes_scroll_container(&self, fragment_flags: FragmentFlags) -> bool;
355 fn establishes_or_extends_3d_rendering_context(&self, fragment_flags: FragmentFlags) -> bool;
356 fn establishes_containing_block_for_absolute_descendants(
357 &self,
358 fragment_flags: FragmentFlags,
359 ) -> bool;
360 fn establishes_containing_block_for_all_descendants(
361 &self,
362 fragment_flags: FragmentFlags,
363 ) -> bool;
364 fn preferred_aspect_ratio(
365 &self,
366 natural_aspect_ratio: Option<CSSFloat>,
367 padding_border_sums: &LogicalVec2<Au>,
368 ) -> Option<AspectRatio>;
369 fn background_is_transparent(&self) -> bool;
370 fn webrender_primitive_flags(&self) -> wr::PrimitiveFlags;
371 fn bidi_control_chars(&self) -> (&'static str, &'static str);
372 fn resolve_align_self(
373 &self,
374 resolved_auto_value: ItemPlacement,
375 resolved_normal_value: AlignFlags,
376 ) -> SelfAlignment;
377 fn depends_on_block_constraints_due_to_relative_positioning(
378 &self,
379 writing_mode: WritingMode,
380 ) -> bool;
381 fn is_inline_box(&self, fragment_flags: FragmentFlags) -> bool;
382 fn is_atomic_inline_level(&self, fragment_flags: FragmentFlags) -> bool;
383 fn overflow_direction(&self) -> OverflowDirection;
384 fn to_bidi_level(&self) -> Level;
385 fn white_space_may_hang_conditionally(&self) -> bool;
388}
389
390impl ComputedValuesExt for ComputedValues {
391 fn physical_box_offsets(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>> {
392 fn convert(inset: &Inset) -> LengthPercentageOrAuto<'_> {
393 match inset {
394 Inset::LengthPercentage(v) => LengthPercentageOrAuto::LengthPercentage(v),
395 Inset::Auto => LengthPercentageOrAuto::Auto,
396 Inset::AnchorFunction(_) => unreachable!("anchor() should be disabled"),
397 Inset::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"),
398 Inset::AnchorContainingCalcFunction(_) => {
399 unreachable!("anchor() and anchor-size() should be disabled")
400 },
401 }
402 }
403 let position = self.get_position();
404 PhysicalSides::new(
405 convert(&position.top),
406 convert(&position.right),
407 convert(&position.bottom),
408 convert(&position.left),
409 )
410 }
411
412 fn box_offsets(&self, writing_mode: WritingMode) -> LogicalSides<LengthPercentageOrAuto<'_>> {
413 LogicalSides::from_physical(&self.physical_box_offsets(), writing_mode)
414 }
415
416 fn box_size(
417 &self,
418 containing_block_writing_mode: WritingMode,
419 ) -> LogicalVec2<Size<LengthPercentage>> {
420 let position = self.get_position();
421 LogicalVec2::from_physical_size(
422 &PhysicalSize::new(position.get_width().into(), position.get_height().into()),
423 containing_block_writing_mode,
424 )
425 }
426
427 fn min_box_size(
428 &self,
429 containing_block_writing_mode: WritingMode,
430 ) -> LogicalVec2<Size<LengthPercentage>> {
431 let position = self.get_position();
432 LogicalVec2::from_physical_size(
433 &PhysicalSize::new(
434 position.get_min_width().into(),
435 position.get_min_height().into(),
436 ),
437 containing_block_writing_mode,
438 )
439 }
440
441 fn max_box_size(
442 &self,
443 containing_block_writing_mode: WritingMode,
444 ) -> LogicalVec2<Size<LengthPercentage>> {
445 let position = self.get_position();
446 LogicalVec2::from_physical_size(
447 &PhysicalSize::new(
448 position.get_max_width().into(),
449 position.get_max_height().into(),
450 ),
451 containing_block_writing_mode,
452 )
453 }
454
455 fn content_box_size_for_box_size(
456 &self,
457 box_size: LogicalVec2<Size<Au>>,
458 pbm: &PaddingBorderMargin,
459 ) -> LogicalVec2<Size<Au>> {
460 match self.get_position().box_sizing {
461 BoxSizing::ContentBox => box_size,
462 BoxSizing::BorderBox => box_size.map_inline_and_block_sizes(
465 |value| value - pbm.padding_border_sums.inline,
466 |value| value - pbm.padding_border_sums.block,
467 ),
468 }
469 }
470
471 fn content_min_box_size_for_min_size(
472 &self,
473 min_box_size: LogicalVec2<Size<Au>>,
474 pbm: &PaddingBorderMargin,
475 ) -> LogicalVec2<Size<Au>> {
476 match self.get_position().box_sizing {
477 BoxSizing::ContentBox => min_box_size,
478 BoxSizing::BorderBox => min_box_size.map_inline_and_block_sizes(
480 |value| Au::zero().max(value - pbm.padding_border_sums.inline),
481 |value| Au::zero().max(value - pbm.padding_border_sums.block),
482 ),
483 }
484 }
485
486 fn content_max_box_size_for_max_size(
487 &self,
488 max_box_size: LogicalVec2<Size<Au>>,
489 pbm: &PaddingBorderMargin,
490 ) -> LogicalVec2<Size<Au>> {
491 match self.get_position().box_sizing {
492 BoxSizing::ContentBox => max_box_size,
493 BoxSizing::BorderBox => max_box_size.map_inline_and_block_sizes(
496 |value| value - pbm.padding_border_sums.inline,
497 |value| value - pbm.padding_border_sums.block,
498 ),
499 }
500 }
501
502 fn border_style_color(
503 &self,
504 containing_block_writing_mode: WritingMode,
505 ) -> LogicalSides<BorderStyleColor> {
506 let current_color = self.get_color();
507 LogicalSides::from_physical(
508 &BorderStyleColor::from_border(self.get_border(), current_color),
509 containing_block_writing_mode,
510 )
511 }
512
513 fn physical_margin(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>> {
514 fn convert(inset: &Margin) -> LengthPercentageOrAuto<'_> {
515 match inset {
516 Margin::LengthPercentage(v) => LengthPercentageOrAuto::LengthPercentage(v),
517 Margin::Auto => LengthPercentageOrAuto::Auto,
518 Margin::AnchorSizeFunction(_) | Margin::AnchorContainingCalcFunction(_) => {
519 unreachable!("anchor-size() should be disabled")
520 },
521 }
522 }
523 let margin = self.get_margin();
524 PhysicalSides::new(
525 convert(&margin.margin_top),
526 convert(&margin.margin_right),
527 convert(&margin.margin_bottom),
528 convert(&margin.margin_left),
529 )
530 }
531
532 fn margin(
533 &self,
534 containing_block_writing_mode: WritingMode,
535 ) -> LogicalSides<LengthPercentageOrAuto<'_>> {
536 LogicalSides::from_physical(&self.physical_margin(), containing_block_writing_mode)
537 }
538
539 fn is_inline_box(&self, fragment_flags: FragmentFlags) -> bool {
540 (self.get_box().display.is_inline_flow() &&
541 !fragment_flags.intersects(
542 FragmentFlags::IS_REPLACED |
543 FragmentFlags::IS_WIDGET |
544 FragmentFlags::IS_FLEX_OR_GRID_ITEM,
545 )) ||
546 matches!(self.pseudo(), Some(PseudoElement::FirstLetter))
547 }
548
549 fn is_atomic_inline_level(&self, fragment_flags: FragmentFlags) -> bool {
550 self.get_box().display.outside() == stylo::DisplayOutside::Inline &&
551 !self.is_inline_box(fragment_flags) &&
552 !fragment_flags.intersects(FragmentFlags::IS_FLEX_OR_GRID_ITEM)
553 }
554
555 fn is_transformable(&self, fragment_flags: FragmentFlags) -> bool {
557 !self.is_inline_box(fragment_flags)
567 }
568
569 fn has_transform_or_perspective_style(&self) -> bool {
571 !self.get_box().transform.0.is_empty() ||
572 self.get_box().scale != GenericScale::None ||
573 self.get_box().rotate != GenericRotate::None ||
574 self.get_box().translate != GenericTranslate::None ||
575 self.get_box().perspective != Perspective::None
576 }
577
578 #[inline]
581 fn has_effective_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool {
582 self.is_transformable(fragment_flags) && self.has_transform_or_perspective_style()
583 }
584
585 fn z_index_applies(&self, fragment_flags: FragmentFlags) -> bool {
587 if self.get_box().position != ComputedPosition::Static {
590 return true;
591 }
592 fragment_flags.contains(FragmentFlags::IS_FLEX_OR_GRID_ITEM)
604 }
605
606 fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32 {
610 if self.z_index_applies(fragment_flags) {
611 self.get_position().z_index.integer_or(0)
612 } else {
613 0
614 }
615 }
616
617 fn effective_overflow(&self, fragment_flags: FragmentFlags) -> AxesOverflow {
621 if fragment_flags.contains(FragmentFlags::PROPAGATED_OVERFLOW_TO_VIEWPORT) {
624 return AxesOverflow::default();
625 }
626
627 let mut overflow = AxesOverflow::from(self);
628
629 if fragment_flags.contains(FragmentFlags::IS_REPLACED) {
632 if overflow.x != Overflow::Visible {
633 overflow.x = Overflow::Clip;
634 }
635 if overflow.y != Overflow::Visible {
636 overflow.y = Overflow::Clip;
637 }
638 return overflow;
639 }
640
641 let ignores_overflow = match self.get_box().display.inside() {
642 stylo::DisplayInside::Flow => self.is_inline_box(fragment_flags),
645
646 stylo::DisplayInside::Table => {
654 !matches!(self.pseudo(), Some(PseudoElement::ServoTableGrid)) ||
655 matches!(overflow.x, Overflow::Auto | Overflow::Scroll) ||
656 matches!(overflow.y, Overflow::Auto | Overflow::Scroll)
657 },
658
659 stylo::DisplayInside::TableColumn |
662 stylo::DisplayInside::TableColumnGroup |
663 stylo::DisplayInside::TableRow |
664 stylo::DisplayInside::TableRowGroup |
665 stylo::DisplayInside::TableHeaderGroup |
666 stylo::DisplayInside::TableFooterGroup => true,
667
668 _ => false,
669 };
670 if ignores_overflow {
671 return AxesOverflow::default();
672 }
673
674 overflow
675 }
676
677 fn used_transform_style(&self, fragment_flags: FragmentFlags) -> ComputedTransformStyle {
680 let box_style = self.get_box();
683 if box_style.transform_style == ComputedTransformStyle::Flat {
684 return ComputedTransformStyle::Flat;
685 }
686
687 let effects = self.get_effects();
704 let overflow = self.effective_overflow(fragment_flags);
705 if !matches!(overflow.x, Overflow::Visible | Overflow::Clip) ||
706 !matches!(overflow.y, Overflow::Visible | Overflow::Clip) ||
707 effects.opacity < 1.0 ||
708 !effects.filter.0.is_empty() ||
709 !effects.clip.is_auto() ||
710 self.get_svg().clip_path != ClipPath::None ||
711 self.get_box().isolation == ComputedIsolation::Isolate ||
712 effects.mix_blend_mode != ComputedMixBlendMode::Normal
713 {
714 return ComputedTransformStyle::Flat;
715 }
716
717 box_style.transform_style
719 }
720
721 fn establishes_block_formatting_context(&self, fragment_flags: FragmentFlags) -> bool {
728 if self.establishes_scroll_container(fragment_flags) {
729 return true;
730 }
731
732 if self.get_column().is_multicol() {
733 return true;
734 }
735
736 if self.get_column().column_span == ColumnSpan::All {
737 return true;
738 }
739
740 if self.get_position().align_content.primary() != AlignFlags::NORMAL {
746 return true;
747 }
748
749 false
751 }
752
753 fn establishes_scroll_container(&self, fragment_flags: FragmentFlags) -> bool {
755 self.effective_overflow(fragment_flags)
756 .establishes_scroll_container()
757 }
758
759 fn establishes_or_extends_3d_rendering_context(&self, fragment_flags: FragmentFlags) -> bool {
761 self.is_transformable(fragment_flags) &&
766 self.used_transform_style(fragment_flags) == ComputedTransformStyle::Preserve3d
767 }
768
769 fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool {
771 let will_change_bits = self.get_will_change().bits;
775 if will_change_bits
776 .intersects(WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::OPACITY)
777 {
778 return true;
779 }
780
781 if self.z_index_applies(fragment_flags) &&
784 (!self.get_position().z_index.is_auto() ||
785 will_change_bits.intersects(WillChangeBits::Z_INDEX))
786 {
787 return true;
788 }
789
790 if matches!(
793 self.get_box().position,
794 ComputedPosition::Fixed | ComputedPosition::Sticky
795 ) {
796 return true;
797 }
798
799 if self.is_transformable(fragment_flags) &&
814 (self.has_transform_or_perspective_style() ||
815 self.used_transform_style(fragment_flags) ==
816 ComputedTransformStyle::Preserve3d ||
817 will_change_bits
818 .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
819 {
820 return true;
821 }
822
823 let effects = self.get_effects();
827 if effects.opacity != 1.0 {
828 return true;
829 }
830
831 if !effects.filter.0.is_empty() {
835 return true;
836 }
837
838 if !effects.backdrop_filter.0.is_empty() &&
847 !fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT)
848 {
849 return true;
850 }
851
852 if effects.mix_blend_mode != ComputedMixBlendMode::Normal {
856 return true;
857 }
858
859 if self.get_svg().clip_path != ClipPath::None {
863 return true;
864 }
865
866 if self.get_box().isolation == ComputedIsolation::Isolate {
870 return true;
871 }
872
873 if fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT) {
876 return true;
877 }
878
879 false
881 }
882
883 fn establishes_containing_block_for_absolute_descendants(
890 &self,
891 fragment_flags: FragmentFlags,
892 ) -> bool {
893 if self.establishes_containing_block_for_all_descendants(fragment_flags) {
894 return true;
895 }
896
897 if self
902 .get_will_change()
903 .bits
904 .intersects(WillChangeBits::POSITION)
905 {
906 return true;
907 }
908
909 self.get_box().position != ComputedPosition::Static
910 }
911
912 fn establishes_containing_block_for_all_descendants(
917 &self,
918 fragment_flags: FragmentFlags,
919 ) -> bool {
920 let will_change_bits = self.get_will_change().bits;
925
926 if self.is_transformable(fragment_flags) &&
942 (self.has_transform_or_perspective_style() ||
943 self.used_transform_style(fragment_flags) ==
944 ComputedTransformStyle::Preserve3d ||
945 will_change_bits
946 .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
947 {
948 return true;
949 }
950
951 if !fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT) &&
962 (!self.get_effects().filter.0.is_empty() ||
963 !self.get_effects().backdrop_filter.0.is_empty() ||
964 will_change_bits.intersects(WillChangeBits::FIXPOS_CB_NON_SVG))
965 {
966 return true;
967 }
968
969 false
971 }
972
973 fn preferred_aspect_ratio(
977 &self,
978 natural_aspect_ratio: Option<CSSFloat>,
979 padding_border_sums: &LogicalVec2<Au>,
980 ) -> Option<AspectRatio> {
981 let &GenericAspectRatio {
982 ref auto,
983 ratio: mut preferred_ratio,
984 } = self.get_aspect_ratio();
985
986 if matches!(preferred_ratio, PreferredRatio::Ratio(ratio) if ratio.is_degenerate()) {
989 preferred_ratio = PreferredRatio::None;
990 }
991
992 let to_logical_ratio = |physical_ratio| {
993 if self.writing_mode.is_horizontal() {
994 physical_ratio
995 } else {
996 1.0 / physical_ratio
997 }
998 };
999
1000 match (auto, preferred_ratio) {
1001 (_, PreferredRatio::None) => natural_aspect_ratio
1009 .map(to_logical_ratio)
1010 .map(AspectRatio::from_logical_content_ratio),
1011 (true, PreferredRatio::Ratio(preferred_ratio)) => Some({
1018 let physical_ratio = natural_aspect_ratio
1019 .unwrap_or_else(|| (preferred_ratio.0).0 / (preferred_ratio.1).0);
1020 AspectRatio::from_logical_content_ratio(to_logical_ratio(physical_ratio))
1021 }),
1022
1023 (false, PreferredRatio::Ratio(preferred_ratio)) => {
1027 let box_sizing_adjustment = match self.get_box_sizing() {
1030 BoxSizing::ContentBox => LogicalVec2::zero(),
1031 BoxSizing::BorderBox => *padding_border_sums,
1032 };
1033 Some(AspectRatio {
1034 i_over_b: to_logical_ratio((preferred_ratio.0).0 / (preferred_ratio.1).0),
1035 box_sizing_adjustment,
1036 })
1037 },
1038 }
1039 }
1040
1041 fn background_is_transparent(&self) -> bool {
1043 let background = self.get_background();
1044 let color = self.resolve_color(&background.background_color);
1045 color.alpha == 0.0 &&
1046 background
1047 .background_image
1048 .0
1049 .iter()
1050 .all(|layer| matches!(layer, ComputedImageLayer::None))
1051 }
1052
1053 fn webrender_primitive_flags(&self) -> wr::PrimitiveFlags {
1056 match self.get_box().backface_visibility {
1057 BackfaceVisiblity::Visible => wr::PrimitiveFlags::default(),
1058 BackfaceVisiblity::Hidden => wr::PrimitiveFlags::empty(),
1059 }
1060 }
1061
1062 fn bidi_control_chars(&self) -> (&'static str, &'static str) {
1066 match (
1067 self.get_text().unicode_bidi,
1068 self.get_inherited_box().direction,
1069 ) {
1070 (UnicodeBidi::Normal, _) => ("", ""),
1071 (UnicodeBidi::Embed, Direction::Ltr) => ("\u{202a}", "\u{202c}"),
1072 (UnicodeBidi::Embed, Direction::Rtl) => ("\u{202b}", "\u{202c}"),
1073 (UnicodeBidi::Isolate, Direction::Ltr) => ("\u{2066}", "\u{2069}"),
1074 (UnicodeBidi::Isolate, Direction::Rtl) => ("\u{2067}", "\u{2069}"),
1075 (UnicodeBidi::BidiOverride, Direction::Ltr) => ("\u{202d}", "\u{202c}"),
1076 (UnicodeBidi::BidiOverride, Direction::Rtl) => ("\u{202e}", "\u{202c}"),
1077 (UnicodeBidi::IsolateOverride, Direction::Ltr) => {
1078 ("\u{2068}\u{202d}", "\u{202c}\u{2069}")
1079 },
1080 (UnicodeBidi::IsolateOverride, Direction::Rtl) => {
1081 ("\u{2068}\u{202e}", "\u{202c}\u{2069}")
1082 },
1083 (UnicodeBidi::Plaintext, _) => ("\u{2068}", "\u{2069}"),
1084 }
1085 }
1086
1087 fn resolve_align_self(
1088 &self,
1089 resolved_auto_value: ItemPlacement,
1090 resolved_normal_value: AlignFlags,
1091 ) -> SelfAlignment {
1092 SelfAlignment(match self.get_align_self().0 {
1093 AlignFlags::AUTO => resolved_auto_value.0,
1094 AlignFlags::NORMAL => resolved_normal_value,
1095 value => value,
1096 })
1097 }
1098
1099 fn depends_on_block_constraints_due_to_relative_positioning(
1100 &self,
1101 writing_mode: WritingMode,
1102 ) -> bool {
1103 if !matches!(
1104 self.get_box().position,
1105 ComputedPosition::Relative | ComputedPosition::Sticky
1106 ) {
1107 return false;
1108 }
1109 let box_offsets = self.box_offsets(writing_mode);
1110 let has_percentage = |offset: LengthPercentageOrAuto<'_>| {
1111 offset
1112 .non_auto()
1113 .is_some_and(LengthPercentage::has_percentage)
1114 };
1115 has_percentage(box_offsets.block_start) || has_percentage(box_offsets.block_end)
1116 }
1117
1118 fn overflow_direction(&self) -> OverflowDirection {
1120 let inline_end_direction = self.writing_mode.inline_end_physical_side();
1121 let block_end_direction = self.writing_mode.block_end_physical_side();
1122
1123 let rightward = inline_end_direction == PhysicalSide::Right ||
1124 block_end_direction == PhysicalSide::Right;
1125 let downward = inline_end_direction == PhysicalSide::Bottom ||
1126 block_end_direction == PhysicalSide::Bottom;
1127
1128 OverflowDirection {
1130 rightward,
1131 downward,
1132 }
1133 }
1134
1135 fn to_bidi_level(&self) -> Level {
1139 if self.writing_mode.is_bidi_ltr() {
1140 Level::ltr()
1141 } else {
1142 Level::rtl()
1143 }
1144 }
1145
1146 fn white_space_may_hang_conditionally(&self) -> bool {
1147 *self.get_white_space_collapse() == WhiteSpaceCollapse::Preserve &&
1148 *self.get_text_wrap_mode() == TextWrapMode::Wrap
1149 }
1150}
1151
1152pub(crate) enum LayoutStyle<'a> {
1153 Default(&'a ComputedValues),
1154 Table(TableLayoutStyle<'a>),
1155}
1156
1157impl LayoutStyle<'_> {
1158 #[inline]
1159 pub(crate) fn style(&self) -> &ComputedValues {
1160 match self {
1161 Self::Default(style) => style,
1162 Self::Table(table) => table.style(),
1163 }
1164 }
1165
1166 #[inline]
1167 pub(crate) fn is_table(&self) -> bool {
1168 matches!(self, Self::Table(_))
1169 }
1170
1171 pub(crate) fn content_box_sizes_and_padding_border_margin(
1172 &self,
1173 containing_block: &IndefiniteContainingBlock,
1174 ) -> ContentBoxSizesAndPBM {
1175 let containing_block_size_or_zero =
1181 containing_block.size.map(|value| value.unwrap_or_default());
1182 let writing_mode = containing_block.style.writing_mode;
1183 let pbm = self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1184 writing_mode,
1185 containing_block_size_or_zero.inline,
1186 );
1187 let style = self.style();
1188 let box_size = style.box_size(writing_mode);
1189 let min_size = style.min_box_size(writing_mode);
1190 let max_size = style.max_box_size(writing_mode);
1191 let preferred_size_computes_to_auto = box_size.map(|size| size.is_initial());
1192
1193 let depends_on_block_constraints = |size: &Size<LengthPercentage>| {
1194 match size {
1195 Size::Stretch => true,
1203 Size::Numeric(length_percentage) => length_percentage.has_percentage(),
1204 _ => false,
1205 }
1206 };
1207 let depends_on_block_constraints = depends_on_block_constraints(&box_size.block) ||
1208 depends_on_block_constraints(&min_size.block) ||
1209 depends_on_block_constraints(&max_size.block) ||
1210 style.depends_on_block_constraints_due_to_relative_positioning(writing_mode);
1211
1212 let box_size = box_size.map_with(&containing_block.size, |size, basis| {
1213 size.resolve_percentages_for_preferred(*basis)
1214 });
1215 let content_box_size = style.content_box_size_for_box_size(box_size, &pbm);
1216 let min_size = min_size.percentages_relative_to_basis(&containing_block_size_or_zero);
1217 let content_min_box_size = style.content_min_box_size_for_min_size(min_size, &pbm);
1218 let max_size = max_size.map_with(&containing_block.size, |size, basis| {
1219 size.resolve_percentages_for_max(*basis)
1220 });
1221 let content_max_box_size = style.content_max_box_size_for_max_size(max_size, &pbm);
1222 ContentBoxSizesAndPBM {
1223 content_box_sizes: LogicalVec2 {
1224 block: Sizes::new(
1225 content_box_size.block,
1226 content_min_box_size.block,
1227 content_max_box_size.block,
1228 ),
1229 inline: Sizes::new(
1230 content_box_size.inline,
1231 content_min_box_size.inline,
1232 content_max_box_size.inline,
1233 ),
1234 },
1235 pbm,
1236 depends_on_block_constraints,
1237 preferred_size_computes_to_auto,
1238 }
1239 }
1240
1241 pub(crate) fn padding_border_margin(
1242 &self,
1243 containing_block: &ContainingBlock,
1244 ) -> PaddingBorderMargin {
1245 self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1246 containing_block.style.writing_mode,
1247 containing_block.size.inline,
1248 )
1249 }
1250
1251 pub(crate) fn padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1252 &self,
1253 writing_mode: WritingMode,
1254 containing_block_inline_size: Au,
1255 ) -> PaddingBorderMargin {
1256 let padding = self
1257 .padding(writing_mode)
1258 .percentages_relative_to(containing_block_inline_size);
1259 let style = self.style();
1260 let border = self.border_width(writing_mode);
1261 let margin = style
1262 .margin(writing_mode)
1263 .percentages_relative_to(containing_block_inline_size);
1264 PaddingBorderMargin {
1265 padding_border_sums: LogicalVec2 {
1266 inline: padding.inline_sum() + border.inline_sum(),
1267 block: padding.block_sum() + border.block_sum(),
1268 },
1269 padding,
1270 border,
1271 margin,
1272 }
1273 }
1274
1275 pub(crate) fn padding(
1276 &self,
1277 containing_block_writing_mode: WritingMode,
1278 ) -> LogicalSides<LengthPercentage> {
1279 if matches!(self, Self::Table(table) if table.collapses_borders()) {
1280 return LogicalSides::zero();
1283 }
1284 let padding = self.style().get_padding().clone();
1285 LogicalSides::from_physical(
1286 &PhysicalSides::new(
1287 padding.padding_top.0,
1288 padding.padding_right.0,
1289 padding.padding_bottom.0,
1290 padding.padding_left.0,
1291 ),
1292 containing_block_writing_mode,
1293 )
1294 }
1295
1296 pub(crate) fn border_width(
1297 &self,
1298 containing_block_writing_mode: WritingMode,
1299 ) -> LogicalSides<Au> {
1300 let border_width = match self {
1301 Self::Table(table) if table.collapses_borders() => table
1305 .halved_collapsed_border_widths()
1306 .to_physical(self.style().writing_mode),
1307 _ => {
1308 let border = self.style().get_border();
1309 let resolve = |width: &BorderSideWidth, style: BorderStyle| {
1310 if style.none_or_hidden() {
1311 Au::zero()
1312 } else {
1313 width.0
1314 }
1315 };
1316 PhysicalSides::new(
1317 resolve(&border.border_top_width, border.border_top_style),
1318 resolve(&border.border_right_width, border.border_right_style),
1319 resolve(&border.border_bottom_width, border.border_bottom_style),
1320 resolve(&border.border_left_width, border.border_left_style),
1321 )
1322 },
1323 };
1324 LogicalSides::from_physical(&border_width, containing_block_writing_mode)
1325 }
1326}
1327
1328impl From<stylo::Display> for Display {
1329 fn from(packed: stylo::Display) -> Self {
1330 let outside = packed.outside();
1331 let inside = packed.inside();
1332
1333 let outside = match outside {
1334 stylo::DisplayOutside::Block => DisplayOutside::Block,
1335 stylo::DisplayOutside::Inline => DisplayOutside::Inline,
1336 stylo::DisplayOutside::TableCaption => {
1337 return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(
1338 DisplayLayoutInternal::TableCaption,
1339 ));
1340 },
1341 stylo::DisplayOutside::InternalTable => {
1342 let internal = match inside {
1343 stylo::DisplayInside::TableRowGroup => DisplayLayoutInternal::TableRowGroup,
1344 stylo::DisplayInside::TableColumn => DisplayLayoutInternal::TableColumn,
1345 stylo::DisplayInside::TableColumnGroup => {
1346 DisplayLayoutInternal::TableColumnGroup
1347 },
1348 stylo::DisplayInside::TableHeaderGroup => {
1349 DisplayLayoutInternal::TableHeaderGroup
1350 },
1351 stylo::DisplayInside::TableFooterGroup => {
1352 DisplayLayoutInternal::TableFooterGroup
1353 },
1354 stylo::DisplayInside::TableRow => DisplayLayoutInternal::TableRow,
1355 stylo::DisplayInside::TableCell => DisplayLayoutInternal::TableCell,
1356 _ => unreachable!("Non-internal DisplayInside found"),
1357 };
1358 return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(internal));
1359 },
1360 stylo::DisplayOutside::None if inside == stylo::DisplayInside::Contents => {
1363 return Display::Contents;
1364 },
1365 stylo::DisplayOutside::None => return Display::None,
1366 };
1367
1368 let inside = match inside {
1369 stylo::DisplayInside::Flow => DisplayInside::Flow {
1370 is_list_item: packed.is_list_item(),
1371 },
1372 stylo::DisplayInside::FlowRoot => DisplayInside::FlowRoot {
1373 is_list_item: packed.is_list_item(),
1374 },
1375 stylo::DisplayInside::Flex => DisplayInside::Flex,
1376 stylo::DisplayInside::Grid => DisplayInside::Grid,
1377 stylo::DisplayInside::GridLanes => unreachable!("Should be disabled behind a pref"),
1378 stylo::DisplayInside::Table => DisplayInside::Table,
1379
1380 stylo::DisplayInside::None => return Display::None,
1382 stylo::DisplayInside::Contents => return Display::Contents,
1383
1384 stylo::DisplayInside::TableRowGroup |
1385 stylo::DisplayInside::TableColumn |
1386 stylo::DisplayInside::TableColumnGroup |
1387 stylo::DisplayInside::TableHeaderGroup |
1388 stylo::DisplayInside::TableFooterGroup |
1389 stylo::DisplayInside::TableRow |
1390 stylo::DisplayInside::TableCell => unreachable!("Internal DisplayInside found"),
1391 };
1392 Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { outside, inside })
1393 }
1394}
1395
1396pub(crate) trait Clamp: Sized {
1397 fn clamp_below_max(self, max: Option<Self>) -> Self;
1398 fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self;
1399}
1400
1401impl Clamp for Au {
1402 fn clamp_below_max(self, max: Option<Self>) -> Self {
1403 match max {
1404 None => self,
1405 Some(max) => self.min(max),
1406 }
1407 }
1408
1409 fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self {
1410 self.clamp_below_max(max).max(min)
1411 }
1412}
1413
1414pub(crate) trait TransformExt {
1415 fn change_basis(&self, x: f32, y: f32, z: f32) -> Self;
1416}
1417
1418impl TransformExt for LayoutTransform {
1419 fn change_basis(&self, x: f32, y: f32, z: f32) -> Self {
1421 let pre_translation = Self::translation(x, y, z);
1422 let post_translation = Self::translation(-x, -y, -z);
1423 post_translation.then(self).then(&pre_translation)
1424 }
1425}