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::{AlignItems, BorderStyle, Color, Inset, LengthPercentage, Margin};
27use style::values::generics::box_::Perspective;
28use style::values::generics::position::{GenericAspectRatio, PreferredRatio};
29use style::values::generics::transform::{GenericRotate, GenericScale, GenericTranslate};
30use style::values::specified::align::AlignFlags;
31use style::values::specified::{Overflow, WillChangeBits, box_ as stylo};
32use unicode_bidi::Level;
33use webrender_api as wr;
34use webrender_api::units::LayoutTransform;
35
36use crate::dom_traversal::{Contents, NonReplacedContents};
37use crate::fragment_tree::FragmentFlags;
38use crate::geom::{
39 AuOrAuto, LengthPercentageOrAuto, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalSides,
40 PhysicalSize,
41};
42use crate::sizing::{Size, Sizes};
43use crate::table::TableLayoutStyle;
44use crate::{ContainingBlock, IndefiniteContainingBlock};
45
46#[derive(Clone, Copy, Eq, PartialEq)]
47pub(crate) enum Display {
48 None,
49 Contents,
50 GeneratingBox(DisplayGeneratingBox),
51}
52
53#[derive(Clone, Copy, Debug, Eq, PartialEq)]
54pub(crate) enum DisplayGeneratingBox {
55 OutsideInside {
56 outside: DisplayOutside,
57 inside: DisplayInside,
58 },
59 LayoutInternal(DisplayLayoutInternal),
61}
62impl DisplayGeneratingBox {
63 pub(crate) fn display_inside(&self) -> DisplayInside {
64 match *self {
65 DisplayGeneratingBox::OutsideInside { inside, .. } => inside,
66 DisplayGeneratingBox::LayoutInternal(layout_internal) => {
67 layout_internal.display_inside()
68 },
69 }
70 }
71
72 pub(crate) fn used_value_for_contents(&self, contents: &Contents) -> Self {
73 if matches!(self, Self::LayoutInternal(_)) && contents.is_replaced() {
78 Self::OutsideInside {
79 outside: DisplayOutside::Inline,
80 inside: DisplayInside::Flow {
81 is_list_item: false,
82 },
83 }
84 } else if matches!(
85 contents,
86 Contents::NonReplaced(NonReplacedContents::OfTextControl)
87 ) {
88 if let DisplayGeneratingBox::OutsideInside { outside, .. } = self {
91 DisplayGeneratingBox::OutsideInside {
92 outside: *outside,
93 inside: DisplayInside::FlowRoot {
94 is_list_item: false,
95 },
96 }
97 } else {
98 *self
99 }
100 } else {
101 *self
102 }
103 }
104}
105
106#[derive(Clone, Copy, Debug, Eq, PartialEq)]
107pub(crate) enum DisplayOutside {
108 Block,
109 Inline,
110}
111
112#[derive(Clone, Copy, Debug, Eq, PartialEq)]
113pub(crate) enum DisplayInside {
114 Flow { is_list_item: bool },
117 FlowRoot { is_list_item: bool },
118 Flex,
119 Grid,
120 Table,
121}
122
123#[derive(Clone, Copy, Debug, Eq, PartialEq)]
124#[allow(clippy::enum_variant_names)]
125pub(crate) enum DisplayLayoutInternal {
127 TableCaption,
128 TableCell,
129 TableColumn,
130 TableColumnGroup,
131 TableFooterGroup,
132 TableHeaderGroup,
133 TableRow,
134 TableRowGroup,
135}
136
137impl DisplayLayoutInternal {
138 pub(crate) fn display_inside(&self) -> DisplayInside {
140 DisplayInside::FlowRoot {
144 is_list_item: false,
145 }
146 }
147}
148
149#[derive(Clone, Debug)]
151pub(crate) struct PaddingBorderMargin {
152 pub padding: LogicalSides<Au>,
153 pub border: LogicalSides<Au>,
154 pub margin: LogicalSides<AuOrAuto>,
155
156 pub padding_border_sums: LogicalVec2<Au>,
158}
159
160impl PaddingBorderMargin {
161 pub(crate) fn zero() -> Self {
162 Self {
163 padding: LogicalSides::zero(),
164 border: LogicalSides::zero(),
165 margin: LogicalSides::zero(),
166 padding_border_sums: LogicalVec2::zero(),
167 }
168 }
169
170 pub(crate) fn sums_auto_is_zero(
171 &self,
172 ignore_block_margins: LogicalSides1D<bool>,
173 ) -> LogicalVec2<Au> {
174 let margin = self.margin.auto_is(Au::zero);
175 let mut sums = self.padding_border_sums;
176 sums.inline += margin.inline_sum();
177 if !ignore_block_margins.start {
178 sums.block += margin.block_start;
179 }
180 if !ignore_block_margins.end {
181 sums.block += margin.block_end;
182 }
183 sums
184 }
185}
186
187#[derive(Clone, Copy, Debug)]
191pub(crate) struct AspectRatio {
192 box_sizing_adjustment: LogicalVec2<Au>,
197 i_over_b: CSSFloat,
199}
200
201impl AspectRatio {
202 pub(crate) fn compute_dependent_size(
204 &self,
205 ratio_dependent_axis: AxisDirection,
206 ratio_determining_size: Au,
207 ) -> Au {
208 match ratio_dependent_axis {
209 AxisDirection::Inline => {
211 (ratio_determining_size + self.box_sizing_adjustment.block).scale_by(self.i_over_b) -
212 self.box_sizing_adjustment.inline
213 },
214 AxisDirection::Block => {
216 (ratio_determining_size + self.box_sizing_adjustment.inline)
217 .scale_by(1.0 / self.i_over_b) -
218 self.box_sizing_adjustment.block
219 },
220 }
221 }
222
223 pub(crate) fn from_logical_content_ratio(i_over_b: CSSFloat) -> Self {
224 Self {
225 box_sizing_adjustment: LogicalVec2::zero(),
226 i_over_b,
227 }
228 }
229}
230
231#[derive(Clone)]
232pub(crate) struct ContentBoxSizesAndPBM {
233 pub content_box_sizes: LogicalVec2<Sizes>,
234 pub pbm: PaddingBorderMargin,
235 pub depends_on_block_constraints: bool,
236 pub preferred_size_computes_to_auto: LogicalVec2<bool>,
237}
238
239#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
240pub(crate) struct BorderStyleColor {
241 pub style: BorderStyle,
242 pub color: AbsoluteColor,
243}
244
245impl BorderStyleColor {
246 pub(crate) fn new(style: BorderStyle, color: AbsoluteColor) -> Self {
247 Self { style, color }
248 }
249
250 pub(crate) fn from_border(
251 border: &Border,
252 current_color: &AbsoluteColor,
253 ) -> PhysicalSides<Self> {
254 let resolve = |color: &Color| color.resolve_to_absolute(current_color);
255 PhysicalSides::<Self>::new(
256 Self::new(border.border_top_style, resolve(&border.border_top_color)),
257 Self::new(
258 border.border_right_style,
259 resolve(&border.border_right_color),
260 ),
261 Self::new(
262 border.border_bottom_style,
263 resolve(&border.border_bottom_color),
264 ),
265 Self::new(border.border_left_style, resolve(&border.border_left_color)),
266 )
267 }
268
269 pub(crate) fn hidden() -> Self {
270 Self::new(BorderStyle::Hidden, AbsoluteColor::TRANSPARENT_BLACK)
271 }
272}
273
274impl Default for BorderStyleColor {
275 fn default() -> Self {
276 Self::new(BorderStyle::None, AbsoluteColor::TRANSPARENT_BLACK)
277 }
278}
279
280pub(crate) struct OverflowDirection {
284 pub rightward: bool,
286 pub downward: bool,
288}
289
290pub(crate) trait ComputedValuesExt {
291 fn physical_box_offsets(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>>;
292 fn box_offsets(&self, writing_mode: WritingMode) -> LogicalSides<LengthPercentageOrAuto<'_>>;
293 fn box_size(
294 &self,
295 containing_block_writing_mode: WritingMode,
296 ) -> LogicalVec2<Size<LengthPercentage>>;
297 fn min_box_size(
298 &self,
299 containing_block_writing_mode: WritingMode,
300 ) -> LogicalVec2<Size<LengthPercentage>>;
301 fn max_box_size(
302 &self,
303 containing_block_writing_mode: WritingMode,
304 ) -> LogicalVec2<Size<LengthPercentage>>;
305 fn content_box_size_for_box_size(
306 &self,
307 box_size: LogicalVec2<Size<Au>>,
308 pbm: &PaddingBorderMargin,
309 ) -> LogicalVec2<Size<Au>>;
310 fn content_min_box_size_for_min_size(
311 &self,
312 box_size: LogicalVec2<Size<Au>>,
313 pbm: &PaddingBorderMargin,
314 ) -> LogicalVec2<Size<Au>>;
315 fn content_max_box_size_for_max_size(
316 &self,
317 box_size: LogicalVec2<Size<Au>>,
318 pbm: &PaddingBorderMargin,
319 ) -> LogicalVec2<Size<Au>>;
320 fn border_style_color(
321 &self,
322 containing_block_writing_mode: WritingMode,
323 ) -> LogicalSides<BorderStyleColor>;
324 fn physical_margin(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>>;
325 fn margin(
326 &self,
327 containing_block_writing_mode: WritingMode,
328 ) -> LogicalSides<LengthPercentageOrAuto<'_>>;
329 fn is_transformable(&self, fragment_flags: FragmentFlags) -> bool;
330 fn has_transform_or_perspective_style(&self) -> bool;
331 fn has_effective_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool;
332 fn z_index_applies(&self, fragment_flags: FragmentFlags) -> bool;
333 fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32;
334 fn effective_overflow(&self, fragment_flags: FragmentFlags) -> AxesOverflow;
335 fn establishes_block_formatting_context(&self, fragment_flags: FragmentFlags) -> bool;
336 fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool;
337 fn establishes_scroll_container(&self, fragment_flags: FragmentFlags) -> bool;
338 fn establishes_containing_block_for_absolute_descendants(
339 &self,
340 fragment_flags: FragmentFlags,
341 ) -> bool;
342 fn establishes_containing_block_for_all_descendants(
343 &self,
344 fragment_flags: FragmentFlags,
345 ) -> bool;
346 fn preferred_aspect_ratio(
347 &self,
348 natural_aspect_ratio: Option<CSSFloat>,
349 padding_border_sums: &LogicalVec2<Au>,
350 ) -> Option<AspectRatio>;
351 fn background_is_transparent(&self) -> bool;
352 fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags;
353 fn bidi_control_chars(&self) -> (&'static str, &'static str);
354 fn resolve_align_self(
355 &self,
356 resolved_auto_value: AlignItems,
357 resolved_normal_value: AlignItems,
358 ) -> AlignItems;
359 fn depends_on_block_constraints_due_to_relative_positioning(
360 &self,
361 writing_mode: WritingMode,
362 ) -> bool;
363 fn is_inline_box(&self, fragment_flags: FragmentFlags) -> bool;
364 fn overflow_direction(&self) -> OverflowDirection;
365 fn to_bidi_level(&self) -> Level;
366}
367
368impl ComputedValuesExt for ComputedValues {
369 fn physical_box_offsets(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>> {
370 fn convert(inset: &Inset) -> LengthPercentageOrAuto<'_> {
371 match inset {
372 Inset::LengthPercentage(v) => LengthPercentageOrAuto::LengthPercentage(v),
373 Inset::Auto => LengthPercentageOrAuto::Auto,
374 Inset::AnchorFunction(_) => unreachable!("anchor() should be disabled"),
375 Inset::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"),
376 Inset::AnchorContainingCalcFunction(_) => {
377 unreachable!("anchor() and anchor-size() should be disabled")
378 },
379 }
380 }
381 let position = self.get_position();
382 PhysicalSides::new(
383 convert(&position.top),
384 convert(&position.right),
385 convert(&position.bottom),
386 convert(&position.left),
387 )
388 }
389
390 fn box_offsets(&self, writing_mode: WritingMode) -> LogicalSides<LengthPercentageOrAuto<'_>> {
391 LogicalSides::from_physical(&self.physical_box_offsets(), writing_mode)
392 }
393
394 fn box_size(
395 &self,
396 containing_block_writing_mode: WritingMode,
397 ) -> LogicalVec2<Size<LengthPercentage>> {
398 let position = self.get_position();
399 LogicalVec2::from_physical_size(
400 &PhysicalSize::new(
401 position.clone_width().into(),
402 position.clone_height().into(),
403 ),
404 containing_block_writing_mode,
405 )
406 }
407
408 fn min_box_size(
409 &self,
410 containing_block_writing_mode: WritingMode,
411 ) -> LogicalVec2<Size<LengthPercentage>> {
412 let position = self.get_position();
413 LogicalVec2::from_physical_size(
414 &PhysicalSize::new(
415 position.clone_min_width().into(),
416 position.clone_min_height().into(),
417 ),
418 containing_block_writing_mode,
419 )
420 }
421
422 fn max_box_size(
423 &self,
424 containing_block_writing_mode: WritingMode,
425 ) -> LogicalVec2<Size<LengthPercentage>> {
426 let position = self.get_position();
427 LogicalVec2::from_physical_size(
428 &PhysicalSize::new(
429 position.clone_max_width().into(),
430 position.clone_max_height().into(),
431 ),
432 containing_block_writing_mode,
433 )
434 }
435
436 fn content_box_size_for_box_size(
437 &self,
438 box_size: LogicalVec2<Size<Au>>,
439 pbm: &PaddingBorderMargin,
440 ) -> LogicalVec2<Size<Au>> {
441 match self.get_position().box_sizing {
442 BoxSizing::ContentBox => box_size,
443 BoxSizing::BorderBox => box_size.map_inline_and_block_sizes(
446 |value| value - pbm.padding_border_sums.inline,
447 |value| value - pbm.padding_border_sums.block,
448 ),
449 }
450 }
451
452 fn content_min_box_size_for_min_size(
453 &self,
454 min_box_size: LogicalVec2<Size<Au>>,
455 pbm: &PaddingBorderMargin,
456 ) -> LogicalVec2<Size<Au>> {
457 match self.get_position().box_sizing {
458 BoxSizing::ContentBox => min_box_size,
459 BoxSizing::BorderBox => min_box_size.map_inline_and_block_sizes(
461 |value| Au::zero().max(value - pbm.padding_border_sums.inline),
462 |value| Au::zero().max(value - pbm.padding_border_sums.block),
463 ),
464 }
465 }
466
467 fn content_max_box_size_for_max_size(
468 &self,
469 max_box_size: LogicalVec2<Size<Au>>,
470 pbm: &PaddingBorderMargin,
471 ) -> LogicalVec2<Size<Au>> {
472 match self.get_position().box_sizing {
473 BoxSizing::ContentBox => max_box_size,
474 BoxSizing::BorderBox => max_box_size.map_inline_and_block_sizes(
477 |value| value - pbm.padding_border_sums.inline,
478 |value| value - pbm.padding_border_sums.block,
479 ),
480 }
481 }
482
483 fn border_style_color(
484 &self,
485 containing_block_writing_mode: WritingMode,
486 ) -> LogicalSides<BorderStyleColor> {
487 let current_color = self.get_inherited_text().clone_color();
488 LogicalSides::from_physical(
489 &BorderStyleColor::from_border(self.get_border(), ¤t_color),
490 containing_block_writing_mode,
491 )
492 }
493
494 fn physical_margin(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>> {
495 fn convert(inset: &Margin) -> LengthPercentageOrAuto<'_> {
496 match inset {
497 Margin::LengthPercentage(v) => LengthPercentageOrAuto::LengthPercentage(v),
498 Margin::Auto => LengthPercentageOrAuto::Auto,
499 Margin::AnchorSizeFunction(_) | Margin::AnchorContainingCalcFunction(_) => {
500 unreachable!("anchor-size() should be disabled")
501 },
502 }
503 }
504 let margin = self.get_margin();
505 PhysicalSides::new(
506 convert(&margin.margin_top),
507 convert(&margin.margin_right),
508 convert(&margin.margin_bottom),
509 convert(&margin.margin_left),
510 )
511 }
512
513 fn margin(
514 &self,
515 containing_block_writing_mode: WritingMode,
516 ) -> LogicalSides<LengthPercentageOrAuto<'_>> {
517 LogicalSides::from_physical(&self.physical_margin(), containing_block_writing_mode)
518 }
519
520 fn is_inline_box(&self, fragment_flags: FragmentFlags) -> bool {
521 self.get_box().display.is_inline_flow() &&
522 !fragment_flags
523 .intersects(FragmentFlags::IS_REPLACED | FragmentFlags::IS_TEXT_CONTROL)
524 }
525
526 fn is_transformable(&self, fragment_flags: FragmentFlags) -> bool {
528 !self.is_inline_box(fragment_flags)
538 }
539
540 fn has_transform_or_perspective_style(&self) -> bool {
542 !self.get_box().transform.0.is_empty() ||
543 self.get_box().scale != GenericScale::None ||
544 self.get_box().rotate != GenericRotate::None ||
545 self.get_box().translate != GenericTranslate::None ||
546 self.get_box().perspective != Perspective::None
547 }
548
549 #[inline]
552 fn has_effective_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool {
553 self.is_transformable(fragment_flags) && self.has_transform_or_perspective_style()
554 }
555
556 fn z_index_applies(&self, fragment_flags: FragmentFlags) -> bool {
558 if self.get_box().position != ComputedPosition::Static {
561 return true;
562 }
563 fragment_flags.contains(FragmentFlags::IS_FLEX_OR_GRID_ITEM)
575 }
576
577 fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32 {
581 if self.z_index_applies(fragment_flags) {
582 self.get_position().z_index.integer_or(0)
583 } else {
584 0
585 }
586 }
587
588 fn effective_overflow(&self, fragment_flags: FragmentFlags) -> AxesOverflow {
592 if fragment_flags.contains(FragmentFlags::PROPAGATED_OVERFLOW_TO_VIEWPORT) {
595 return AxesOverflow::default();
596 }
597
598 let mut overflow = AxesOverflow::from(self);
599
600 if fragment_flags.contains(FragmentFlags::IS_REPLACED) {
603 if overflow.x != Overflow::Visible {
604 overflow.x = Overflow::Clip;
605 }
606 if overflow.y != Overflow::Visible {
607 overflow.y = Overflow::Clip;
608 }
609 return overflow;
610 }
611
612 let ignores_overflow = match self.get_box().display.inside() {
613 stylo::DisplayInside::Flow => self.is_inline_box(fragment_flags),
616
617 stylo::DisplayInside::Table => {
625 !matches!(self.pseudo(), Some(PseudoElement::ServoTableGrid)) ||
626 matches!(overflow.x, Overflow::Auto | Overflow::Scroll) ||
627 matches!(overflow.y, Overflow::Auto | Overflow::Scroll)
628 },
629
630 stylo::DisplayInside::TableColumn |
633 stylo::DisplayInside::TableColumnGroup |
634 stylo::DisplayInside::TableRow |
635 stylo::DisplayInside::TableRowGroup |
636 stylo::DisplayInside::TableHeaderGroup |
637 stylo::DisplayInside::TableFooterGroup => true,
638
639 _ => false,
640 };
641 if ignores_overflow {
642 return AxesOverflow::default();
643 }
644
645 overflow
646 }
647
648 fn establishes_block_formatting_context(&self, fragment_flags: FragmentFlags) -> bool {
655 if self.establishes_scroll_container(fragment_flags) {
656 return true;
657 }
658
659 if self.get_column().is_multicol() {
660 return true;
661 }
662
663 if self.get_column().column_span == ColumnSpan::All {
664 return true;
665 }
666
667 if self.get_position().align_content.0.primary() != AlignFlags::NORMAL {
673 return true;
674 }
675
676 false
678 }
679
680 fn establishes_scroll_container(&self, fragment_flags: FragmentFlags) -> bool {
682 self.effective_overflow(fragment_flags).x.is_scrollable()
685 }
686
687 fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool {
689 let will_change_bits = self.clone_will_change().bits;
693 if will_change_bits
694 .intersects(WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::OPACITY)
695 {
696 return true;
697 }
698
699 if self.z_index_applies(fragment_flags) &&
702 (!self.get_position().z_index.is_auto() ||
703 will_change_bits.intersects(WillChangeBits::Z_INDEX))
704 {
705 return true;
706 }
707
708 if matches!(
711 self.get_box().position,
712 ComputedPosition::Fixed | ComputedPosition::Sticky
713 ) {
714 return true;
715 }
716
717 if self.is_transformable(fragment_flags) &&
732 (self.has_transform_or_perspective_style() ||
733 self.get_box().transform_style == ComputedTransformStyle::Preserve3d ||
734 will_change_bits
735 .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
736 {
737 return true;
738 }
739
740 let effects = self.get_effects();
744 if effects.opacity != 1.0 {
745 return true;
746 }
747
748 if !effects.filter.0.is_empty() {
752 return true;
753 }
754
755 if effects.mix_blend_mode != ComputedMixBlendMode::Normal {
759 return true;
760 }
761
762 if self.get_svg().clip_path != ClipPath::None {
766 return true;
767 }
768
769 if self.get_box().isolation == ComputedIsolation::Isolate {
773 return true;
774 }
775
776 if fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT) {
779 return true;
780 }
781
782 false
784 }
785
786 fn establishes_containing_block_for_absolute_descendants(
793 &self,
794 fragment_flags: FragmentFlags,
795 ) -> bool {
796 if self.establishes_containing_block_for_all_descendants(fragment_flags) {
797 return true;
798 }
799
800 if self
805 .clone_will_change()
806 .bits
807 .intersects(WillChangeBits::POSITION)
808 {
809 return true;
810 }
811
812 self.clone_position() != ComputedPosition::Static
813 }
814
815 fn establishes_containing_block_for_all_descendants(
820 &self,
821 fragment_flags: FragmentFlags,
822 ) -> bool {
823 let will_change_bits = self.clone_will_change().bits;
828
829 if self.is_transformable(fragment_flags) &&
845 (self.has_transform_or_perspective_style() ||
846 self.get_box().transform_style == ComputedTransformStyle::Preserve3d ||
847 will_change_bits
848 .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
849 {
850 return true;
851 }
852
853 if !fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT) &&
858 (!self.get_effects().filter.0.is_empty() ||
859 will_change_bits.intersects(WillChangeBits::FIXPOS_CB_NON_SVG))
860 {
861 return true;
862 }
863
864 false
866 }
867
868 fn preferred_aspect_ratio(
872 &self,
873 natural_aspect_ratio: Option<CSSFloat>,
874 padding_border_sums: &LogicalVec2<Au>,
875 ) -> Option<AspectRatio> {
876 let GenericAspectRatio {
877 auto,
878 ratio: mut preferred_ratio,
879 } = self.clone_aspect_ratio();
880
881 if matches!(preferred_ratio, PreferredRatio::Ratio(ratio) if ratio.is_degenerate()) {
884 preferred_ratio = PreferredRatio::None;
885 }
886
887 let to_logical_ratio = |physical_ratio| {
888 if self.writing_mode.is_horizontal() {
889 physical_ratio
890 } else {
891 1.0 / physical_ratio
892 }
893 };
894
895 match (auto, preferred_ratio) {
896 (_, PreferredRatio::None) => natural_aspect_ratio
904 .map(to_logical_ratio)
905 .map(AspectRatio::from_logical_content_ratio),
906 (true, PreferredRatio::Ratio(preferred_ratio)) => Some({
913 let physical_ratio = natural_aspect_ratio
914 .unwrap_or_else(|| (preferred_ratio.0).0 / (preferred_ratio.1).0);
915 AspectRatio::from_logical_content_ratio(to_logical_ratio(physical_ratio))
916 }),
917
918 (false, PreferredRatio::Ratio(preferred_ratio)) => {
922 let box_sizing_adjustment = match self.clone_box_sizing() {
925 BoxSizing::ContentBox => LogicalVec2::zero(),
926 BoxSizing::BorderBox => *padding_border_sums,
927 };
928 Some(AspectRatio {
929 i_over_b: to_logical_ratio((preferred_ratio.0).0 / (preferred_ratio.1).0),
930 box_sizing_adjustment,
931 })
932 },
933 }
934 }
935
936 fn background_is_transparent(&self) -> bool {
938 let background = self.get_background();
939 let color = self.resolve_color(&background.background_color);
940 color.alpha == 0.0 &&
941 background
942 .background_image
943 .0
944 .iter()
945 .all(|layer| matches!(layer, ComputedImageLayer::None))
946 }
947
948 fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags {
951 match self.get_box().backface_visibility {
952 BackfaceVisiblity::Visible => wr::PrimitiveFlags::default(),
953 BackfaceVisiblity::Hidden => wr::PrimitiveFlags::empty(),
954 }
955 }
956
957 fn bidi_control_chars(&self) -> (&'static str, &'static str) {
961 match (
962 self.get_text().unicode_bidi,
963 self.get_inherited_box().direction,
964 ) {
965 (UnicodeBidi::Normal, _) => ("", ""),
966 (UnicodeBidi::Embed, Direction::Ltr) => ("\u{202a}", "\u{202c}"),
967 (UnicodeBidi::Embed, Direction::Rtl) => ("\u{202b}", "\u{202c}"),
968 (UnicodeBidi::Isolate, Direction::Ltr) => ("\u{2066}", "\u{2069}"),
969 (UnicodeBidi::Isolate, Direction::Rtl) => ("\u{2067}", "\u{2069}"),
970 (UnicodeBidi::BidiOverride, Direction::Ltr) => ("\u{202d}", "\u{202c}"),
971 (UnicodeBidi::BidiOverride, Direction::Rtl) => ("\u{202e}", "\u{202c}"),
972 (UnicodeBidi::IsolateOverride, Direction::Ltr) => {
973 ("\u{2068}\u{202d}", "\u{202c}\u{2069}")
974 },
975 (UnicodeBidi::IsolateOverride, Direction::Rtl) => {
976 ("\u{2068}\u{202e}", "\u{202c}\u{2069}")
977 },
978 (UnicodeBidi::Plaintext, _) => ("\u{2068}", "\u{2069}"),
979 }
980 }
981
982 fn resolve_align_self(
983 &self,
984 resolved_auto_value: AlignItems,
985 resolved_normal_value: AlignItems,
986 ) -> AlignItems {
987 match self.clone_align_self().0.0 {
988 AlignFlags::AUTO => resolved_auto_value,
989 AlignFlags::NORMAL => resolved_normal_value,
990 value => AlignItems(value),
991 }
992 }
993
994 fn depends_on_block_constraints_due_to_relative_positioning(
995 &self,
996 writing_mode: WritingMode,
997 ) -> bool {
998 if !matches!(
999 self.get_box().position,
1000 ComputedPosition::Relative | ComputedPosition::Sticky
1001 ) {
1002 return false;
1003 }
1004 let box_offsets = self.box_offsets(writing_mode);
1005 let has_percentage = |offset: LengthPercentageOrAuto<'_>| {
1006 offset
1007 .non_auto()
1008 .is_some_and(LengthPercentage::has_percentage)
1009 };
1010 has_percentage(box_offsets.block_start) || has_percentage(box_offsets.block_end)
1011 }
1012
1013 fn overflow_direction(&self) -> OverflowDirection {
1015 let inline_end_direction = self.writing_mode.inline_end_physical_side();
1016 let block_end_direction = self.writing_mode.block_end_physical_side();
1017
1018 let rightward = inline_end_direction == PhysicalSide::Right ||
1019 block_end_direction == PhysicalSide::Right;
1020 let downward = inline_end_direction == PhysicalSide::Bottom ||
1021 block_end_direction == PhysicalSide::Bottom;
1022
1023 OverflowDirection {
1025 rightward,
1026 downward,
1027 }
1028 }
1029
1030 fn to_bidi_level(&self) -> Level {
1034 if self.writing_mode.is_bidi_ltr() {
1035 Level::ltr()
1036 } else {
1037 Level::rtl()
1038 }
1039 }
1040}
1041
1042pub(crate) enum LayoutStyle<'a> {
1043 Default(&'a ComputedValues),
1044 Table(TableLayoutStyle<'a>),
1045}
1046
1047impl LayoutStyle<'_> {
1048 #[inline]
1049 pub(crate) fn style(&self) -> &ComputedValues {
1050 match self {
1051 Self::Default(style) => style,
1052 Self::Table(table) => table.style(),
1053 }
1054 }
1055
1056 #[inline]
1057 pub(crate) fn is_table(&self) -> bool {
1058 matches!(self, Self::Table(_))
1059 }
1060
1061 pub(crate) fn content_box_sizes_and_padding_border_margin(
1062 &self,
1063 containing_block: &IndefiniteContainingBlock,
1064 ) -> ContentBoxSizesAndPBM {
1065 let containing_block_size_or_zero =
1071 containing_block.size.map(|value| value.unwrap_or_default());
1072 let writing_mode = containing_block.style.writing_mode;
1073 let pbm = self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1074 writing_mode,
1075 containing_block_size_or_zero.inline,
1076 );
1077 let style = self.style();
1078 let box_size = style.box_size(writing_mode);
1079 let min_size = style.min_box_size(writing_mode);
1080 let max_size = style.max_box_size(writing_mode);
1081 let preferred_size_computes_to_auto = box_size.map(|size| size.is_initial());
1082
1083 let depends_on_block_constraints = |size: &Size<LengthPercentage>| {
1084 match size {
1085 Size::Stretch => true,
1093 Size::Numeric(length_percentage) => length_percentage.has_percentage(),
1094 _ => false,
1095 }
1096 };
1097 let depends_on_block_constraints = depends_on_block_constraints(&box_size.block) ||
1098 depends_on_block_constraints(&min_size.block) ||
1099 depends_on_block_constraints(&max_size.block) ||
1100 style.depends_on_block_constraints_due_to_relative_positioning(writing_mode);
1101
1102 let box_size = box_size.map_with(&containing_block.size, |size, basis| {
1103 size.resolve_percentages_for_preferred(*basis)
1104 });
1105 let content_box_size = style.content_box_size_for_box_size(box_size, &pbm);
1106 let min_size = min_size.percentages_relative_to_basis(&containing_block_size_or_zero);
1107 let content_min_box_size = style.content_min_box_size_for_min_size(min_size, &pbm);
1108 let max_size = max_size.map_with(&containing_block.size, |size, basis| {
1109 size.resolve_percentages_for_max(*basis)
1110 });
1111 let content_max_box_size = style.content_max_box_size_for_max_size(max_size, &pbm);
1112 ContentBoxSizesAndPBM {
1113 content_box_sizes: LogicalVec2 {
1114 block: Sizes::new(
1115 content_box_size.block,
1116 content_min_box_size.block,
1117 content_max_box_size.block,
1118 ),
1119 inline: Sizes::new(
1120 content_box_size.inline,
1121 content_min_box_size.inline,
1122 content_max_box_size.inline,
1123 ),
1124 },
1125 pbm,
1126 depends_on_block_constraints,
1127 preferred_size_computes_to_auto,
1128 }
1129 }
1130
1131 pub(crate) fn padding_border_margin(
1132 &self,
1133 containing_block: &ContainingBlock,
1134 ) -> PaddingBorderMargin {
1135 self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1136 containing_block.style.writing_mode,
1137 containing_block.size.inline,
1138 )
1139 }
1140
1141 pub(crate) fn padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1142 &self,
1143 writing_mode: WritingMode,
1144 containing_block_inline_size: Au,
1145 ) -> PaddingBorderMargin {
1146 let padding = self
1147 .padding(writing_mode)
1148 .percentages_relative_to(containing_block_inline_size);
1149 let style = self.style();
1150 let border = self.border_width(writing_mode);
1151 let margin = style
1152 .margin(writing_mode)
1153 .percentages_relative_to(containing_block_inline_size);
1154 PaddingBorderMargin {
1155 padding_border_sums: LogicalVec2 {
1156 inline: padding.inline_sum() + border.inline_sum(),
1157 block: padding.block_sum() + border.block_sum(),
1158 },
1159 padding,
1160 border,
1161 margin,
1162 }
1163 }
1164
1165 pub(crate) fn padding(
1166 &self,
1167 containing_block_writing_mode: WritingMode,
1168 ) -> LogicalSides<LengthPercentage> {
1169 if matches!(self, Self::Table(table) if table.collapses_borders()) {
1170 return LogicalSides::zero();
1173 }
1174 let padding = self.style().get_padding().clone();
1175 LogicalSides::from_physical(
1176 &PhysicalSides::new(
1177 padding.padding_top.0,
1178 padding.padding_right.0,
1179 padding.padding_bottom.0,
1180 padding.padding_left.0,
1181 ),
1182 containing_block_writing_mode,
1183 )
1184 }
1185
1186 pub(crate) fn border_width(
1187 &self,
1188 containing_block_writing_mode: WritingMode,
1189 ) -> LogicalSides<Au> {
1190 let border_width = match self {
1191 Self::Table(table) if table.collapses_borders() => table
1195 .halved_collapsed_border_widths()
1196 .to_physical(self.style().writing_mode),
1197 _ => {
1198 let border = self.style().get_border();
1199 PhysicalSides::new(
1200 border.border_top_width,
1201 border.border_right_width,
1202 border.border_bottom_width,
1203 border.border_left_width,
1204 )
1205 },
1206 };
1207 LogicalSides::from_physical(&border_width, containing_block_writing_mode)
1208 }
1209}
1210
1211impl From<stylo::Display> for Display {
1212 fn from(packed: stylo::Display) -> Self {
1213 let outside = packed.outside();
1214 let inside = packed.inside();
1215
1216 let outside = match outside {
1217 stylo::DisplayOutside::Block => DisplayOutside::Block,
1218 stylo::DisplayOutside::Inline => DisplayOutside::Inline,
1219 stylo::DisplayOutside::TableCaption => {
1220 return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(
1221 DisplayLayoutInternal::TableCaption,
1222 ));
1223 },
1224 stylo::DisplayOutside::InternalTable => {
1225 let internal = match inside {
1226 stylo::DisplayInside::TableRowGroup => DisplayLayoutInternal::TableRowGroup,
1227 stylo::DisplayInside::TableColumn => DisplayLayoutInternal::TableColumn,
1228 stylo::DisplayInside::TableColumnGroup => {
1229 DisplayLayoutInternal::TableColumnGroup
1230 },
1231 stylo::DisplayInside::TableHeaderGroup => {
1232 DisplayLayoutInternal::TableHeaderGroup
1233 },
1234 stylo::DisplayInside::TableFooterGroup => {
1235 DisplayLayoutInternal::TableFooterGroup
1236 },
1237 stylo::DisplayInside::TableRow => DisplayLayoutInternal::TableRow,
1238 stylo::DisplayInside::TableCell => DisplayLayoutInternal::TableCell,
1239 _ => unreachable!("Non-internal DisplayInside found"),
1240 };
1241 return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(internal));
1242 },
1243 stylo::DisplayOutside::None if inside == stylo::DisplayInside::Contents => {
1246 return Display::Contents;
1247 },
1248 stylo::DisplayOutside::None => return Display::None,
1249 };
1250
1251 let inside = match packed.inside() {
1252 stylo::DisplayInside::Flow => DisplayInside::Flow {
1253 is_list_item: packed.is_list_item(),
1254 },
1255 stylo::DisplayInside::FlowRoot => DisplayInside::FlowRoot {
1256 is_list_item: packed.is_list_item(),
1257 },
1258 stylo::DisplayInside::Flex => DisplayInside::Flex,
1259 stylo::DisplayInside::Grid => DisplayInside::Grid,
1260
1261 stylo::DisplayInside::None => return Display::None,
1263 stylo::DisplayInside::Contents => return Display::Contents,
1264
1265 stylo::DisplayInside::Table => DisplayInside::Table,
1266 stylo::DisplayInside::TableRowGroup |
1267 stylo::DisplayInside::TableColumn |
1268 stylo::DisplayInside::TableColumnGroup |
1269 stylo::DisplayInside::TableHeaderGroup |
1270 stylo::DisplayInside::TableFooterGroup |
1271 stylo::DisplayInside::TableRow |
1272 stylo::DisplayInside::TableCell => unreachable!("Internal DisplayInside found"),
1273 };
1274 Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { outside, inside })
1275 }
1276}
1277
1278pub(crate) trait Clamp: Sized {
1279 fn clamp_below_max(self, max: Option<Self>) -> Self;
1280 fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self;
1281}
1282
1283impl Clamp for Au {
1284 fn clamp_below_max(self, max: Option<Self>) -> Self {
1285 match max {
1286 None => self,
1287 Some(max) => self.min(max),
1288 }
1289 }
1290
1291 fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self {
1292 self.clamp_below_max(max).max(min)
1293 }
1294}
1295
1296pub(crate) trait TransformExt {
1297 fn change_basis(&self, x: f32, y: f32, z: f32) -> Self;
1298}
1299
1300impl TransformExt for LayoutTransform {
1301 fn change_basis(&self, x: f32, y: f32, z: f32) -> Self {
1303 let pre_translation = Self::translation(x, y, z);
1304 let post_translation = Self::translation(-x, -y, -z);
1305 post_translation.then(self).then(&pre_translation)
1306 }
1307}