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 BorderStyle, Color, Inset, ItemPlacement, LengthPercentage, Margin, SelfAlignment,
28};
29use style::values::generics::box_::Perspective;
30use style::values::generics::position::{GenericAspectRatio, PreferredRatio};
31use style::values::generics::transform::{GenericRotate, GenericScale, GenericTranslate};
32use style::values::specified::align::AlignFlags;
33use style::values::specified::{Overflow, WillChangeBits, box_ as stylo};
34use unicode_bidi::Level;
35use webrender_api as wr;
36use webrender_api::units::LayoutTransform;
37
38use crate::dom_traversal::Contents;
39use crate::fragment_tree::FragmentFlags;
40use crate::geom::{
41 AuOrAuto, LengthPercentageOrAuto, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalSides,
42 PhysicalSize,
43};
44use crate::sizing::{Size, Sizes};
45use crate::table::TableLayoutStyle;
46use crate::{ContainingBlock, IndefiniteContainingBlock};
47
48#[derive(Clone, Copy, Eq, PartialEq)]
49pub(crate) enum Display {
50 None,
51 Contents,
52 GeneratingBox(DisplayGeneratingBox),
53}
54
55#[derive(Clone, Copy, Debug, Eq, PartialEq)]
56pub(crate) enum DisplayGeneratingBox {
57 OutsideInside {
58 outside: DisplayOutside,
59 inside: DisplayInside,
60 },
61 LayoutInternal(DisplayLayoutInternal),
63}
64impl DisplayGeneratingBox {
65 pub(crate) fn display_inside(&self) -> DisplayInside {
66 match *self {
67 DisplayGeneratingBox::OutsideInside { inside, .. } => inside,
68 DisplayGeneratingBox::LayoutInternal(layout_internal) => {
69 layout_internal.display_inside()
70 },
71 }
72 }
73
74 pub(crate) fn used_value_for_contents(&self, contents: &Contents) -> Self {
75 if matches!(self, Self::LayoutInternal(_)) && contents.is_replaced() {
80 Self::OutsideInside {
81 outside: DisplayOutside::Inline,
82 inside: DisplayInside::Flow {
83 is_list_item: false,
84 },
85 }
86 } else if matches!(contents, Contents::Widget(_)) {
87 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: ItemPlacement,
357 resolved_normal_value: AlignFlags,
358 ) -> SelfAlignment;
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.intersects(FragmentFlags::IS_REPLACED | FragmentFlags::IS_WIDGET)
523 }
524
525 fn is_transformable(&self, fragment_flags: FragmentFlags) -> bool {
527 !self.is_inline_box(fragment_flags)
537 }
538
539 fn has_transform_or_perspective_style(&self) -> bool {
541 !self.get_box().transform.0.is_empty() ||
542 self.get_box().scale != GenericScale::None ||
543 self.get_box().rotate != GenericRotate::None ||
544 self.get_box().translate != GenericTranslate::None ||
545 self.get_box().perspective != Perspective::None
546 }
547
548 #[inline]
551 fn has_effective_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool {
552 self.is_transformable(fragment_flags) && self.has_transform_or_perspective_style()
553 }
554
555 fn z_index_applies(&self, fragment_flags: FragmentFlags) -> bool {
557 if self.get_box().position != ComputedPosition::Static {
560 return true;
561 }
562 fragment_flags.contains(FragmentFlags::IS_FLEX_OR_GRID_ITEM)
574 }
575
576 fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32 {
580 if self.z_index_applies(fragment_flags) {
581 self.get_position().z_index.integer_or(0)
582 } else {
583 0
584 }
585 }
586
587 fn effective_overflow(&self, fragment_flags: FragmentFlags) -> AxesOverflow {
591 if fragment_flags.contains(FragmentFlags::PROPAGATED_OVERFLOW_TO_VIEWPORT) {
594 return AxesOverflow::default();
595 }
596
597 let mut overflow = AxesOverflow::from(self);
598
599 if fragment_flags.contains(FragmentFlags::IS_REPLACED) {
602 if overflow.x != Overflow::Visible {
603 overflow.x = Overflow::Clip;
604 }
605 if overflow.y != Overflow::Visible {
606 overflow.y = Overflow::Clip;
607 }
608 return overflow;
609 }
610
611 let ignores_overflow = match self.get_box().display.inside() {
612 stylo::DisplayInside::Flow => self.is_inline_box(fragment_flags),
615
616 stylo::DisplayInside::Table => {
624 !matches!(self.pseudo(), Some(PseudoElement::ServoTableGrid)) ||
625 matches!(overflow.x, Overflow::Auto | Overflow::Scroll) ||
626 matches!(overflow.y, Overflow::Auto | Overflow::Scroll)
627 },
628
629 stylo::DisplayInside::TableColumn |
632 stylo::DisplayInside::TableColumnGroup |
633 stylo::DisplayInside::TableRow |
634 stylo::DisplayInside::TableRowGroup |
635 stylo::DisplayInside::TableHeaderGroup |
636 stylo::DisplayInside::TableFooterGroup => true,
637
638 _ => false,
639 };
640 if ignores_overflow {
641 return AxesOverflow::default();
642 }
643
644 overflow
645 }
646
647 fn establishes_block_formatting_context(&self, fragment_flags: FragmentFlags) -> bool {
654 if self.establishes_scroll_container(fragment_flags) {
655 return true;
656 }
657
658 if self.get_column().is_multicol() {
659 return true;
660 }
661
662 if self.get_column().column_span == ColumnSpan::All {
663 return true;
664 }
665
666 if self.get_position().align_content.primary() != AlignFlags::NORMAL {
672 return true;
673 }
674
675 false
677 }
678
679 fn establishes_scroll_container(&self, fragment_flags: FragmentFlags) -> bool {
681 self.effective_overflow(fragment_flags).x.is_scrollable()
684 }
685
686 fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool {
688 let will_change_bits = self.clone_will_change().bits;
692 if will_change_bits
693 .intersects(WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::OPACITY)
694 {
695 return true;
696 }
697
698 if self.z_index_applies(fragment_flags) &&
701 (!self.get_position().z_index.is_auto() ||
702 will_change_bits.intersects(WillChangeBits::Z_INDEX))
703 {
704 return true;
705 }
706
707 if matches!(
710 self.get_box().position,
711 ComputedPosition::Fixed | ComputedPosition::Sticky
712 ) {
713 return true;
714 }
715
716 if self.is_transformable(fragment_flags) &&
731 (self.has_transform_or_perspective_style() ||
732 self.get_box().transform_style == ComputedTransformStyle::Preserve3d ||
733 will_change_bits
734 .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
735 {
736 return true;
737 }
738
739 let effects = self.get_effects();
743 if effects.opacity != 1.0 {
744 return true;
745 }
746
747 if !effects.filter.0.is_empty() {
751 return true;
752 }
753
754 if effects.mix_blend_mode != ComputedMixBlendMode::Normal {
758 return true;
759 }
760
761 if self.get_svg().clip_path != ClipPath::None {
765 return true;
766 }
767
768 if self.get_box().isolation == ComputedIsolation::Isolate {
772 return true;
773 }
774
775 if fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT) {
778 return true;
779 }
780
781 false
783 }
784
785 fn establishes_containing_block_for_absolute_descendants(
792 &self,
793 fragment_flags: FragmentFlags,
794 ) -> bool {
795 if self.establishes_containing_block_for_all_descendants(fragment_flags) {
796 return true;
797 }
798
799 if self
804 .clone_will_change()
805 .bits
806 .intersects(WillChangeBits::POSITION)
807 {
808 return true;
809 }
810
811 self.clone_position() != ComputedPosition::Static
812 }
813
814 fn establishes_containing_block_for_all_descendants(
819 &self,
820 fragment_flags: FragmentFlags,
821 ) -> bool {
822 let will_change_bits = self.clone_will_change().bits;
827
828 if self.is_transformable(fragment_flags) &&
844 (self.has_transform_or_perspective_style() ||
845 self.get_box().transform_style == ComputedTransformStyle::Preserve3d ||
846 will_change_bits
847 .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
848 {
849 return true;
850 }
851
852 if !fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT) &&
857 (!self.get_effects().filter.0.is_empty() ||
858 will_change_bits.intersects(WillChangeBits::FIXPOS_CB_NON_SVG))
859 {
860 return true;
861 }
862
863 false
865 }
866
867 fn preferred_aspect_ratio(
871 &self,
872 natural_aspect_ratio: Option<CSSFloat>,
873 padding_border_sums: &LogicalVec2<Au>,
874 ) -> Option<AspectRatio> {
875 let GenericAspectRatio {
876 auto,
877 ratio: mut preferred_ratio,
878 } = self.clone_aspect_ratio();
879
880 if matches!(preferred_ratio, PreferredRatio::Ratio(ratio) if ratio.is_degenerate()) {
883 preferred_ratio = PreferredRatio::None;
884 }
885
886 let to_logical_ratio = |physical_ratio| {
887 if self.writing_mode.is_horizontal() {
888 physical_ratio
889 } else {
890 1.0 / physical_ratio
891 }
892 };
893
894 match (auto, preferred_ratio) {
895 (_, PreferredRatio::None) => natural_aspect_ratio
903 .map(to_logical_ratio)
904 .map(AspectRatio::from_logical_content_ratio),
905 (true, PreferredRatio::Ratio(preferred_ratio)) => Some({
912 let physical_ratio = natural_aspect_ratio
913 .unwrap_or_else(|| (preferred_ratio.0).0 / (preferred_ratio.1).0);
914 AspectRatio::from_logical_content_ratio(to_logical_ratio(physical_ratio))
915 }),
916
917 (false, PreferredRatio::Ratio(preferred_ratio)) => {
921 let box_sizing_adjustment = match self.clone_box_sizing() {
924 BoxSizing::ContentBox => LogicalVec2::zero(),
925 BoxSizing::BorderBox => *padding_border_sums,
926 };
927 Some(AspectRatio {
928 i_over_b: to_logical_ratio((preferred_ratio.0).0 / (preferred_ratio.1).0),
929 box_sizing_adjustment,
930 })
931 },
932 }
933 }
934
935 fn background_is_transparent(&self) -> bool {
937 let background = self.get_background();
938 let color = self.resolve_color(&background.background_color);
939 color.alpha == 0.0 &&
940 background
941 .background_image
942 .0
943 .iter()
944 .all(|layer| matches!(layer, ComputedImageLayer::None))
945 }
946
947 fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags {
950 match self.get_box().backface_visibility {
951 BackfaceVisiblity::Visible => wr::PrimitiveFlags::default(),
952 BackfaceVisiblity::Hidden => wr::PrimitiveFlags::empty(),
953 }
954 }
955
956 fn bidi_control_chars(&self) -> (&'static str, &'static str) {
960 match (
961 self.get_text().unicode_bidi,
962 self.get_inherited_box().direction,
963 ) {
964 (UnicodeBidi::Normal, _) => ("", ""),
965 (UnicodeBidi::Embed, Direction::Ltr) => ("\u{202a}", "\u{202c}"),
966 (UnicodeBidi::Embed, Direction::Rtl) => ("\u{202b}", "\u{202c}"),
967 (UnicodeBidi::Isolate, Direction::Ltr) => ("\u{2066}", "\u{2069}"),
968 (UnicodeBidi::Isolate, Direction::Rtl) => ("\u{2067}", "\u{2069}"),
969 (UnicodeBidi::BidiOverride, Direction::Ltr) => ("\u{202d}", "\u{202c}"),
970 (UnicodeBidi::BidiOverride, Direction::Rtl) => ("\u{202e}", "\u{202c}"),
971 (UnicodeBidi::IsolateOverride, Direction::Ltr) => {
972 ("\u{2068}\u{202d}", "\u{202c}\u{2069}")
973 },
974 (UnicodeBidi::IsolateOverride, Direction::Rtl) => {
975 ("\u{2068}\u{202e}", "\u{202c}\u{2069}")
976 },
977 (UnicodeBidi::Plaintext, _) => ("\u{2068}", "\u{2069}"),
978 }
979 }
980
981 fn resolve_align_self(
982 &self,
983 resolved_auto_value: ItemPlacement,
984 resolved_normal_value: AlignFlags,
985 ) -> SelfAlignment {
986 SelfAlignment(match self.clone_align_self().0 {
987 AlignFlags::AUTO => resolved_auto_value.0,
988 AlignFlags::NORMAL => resolved_normal_value,
989 value => value,
990 })
991 }
992
993 fn depends_on_block_constraints_due_to_relative_positioning(
994 &self,
995 writing_mode: WritingMode,
996 ) -> bool {
997 if !matches!(
998 self.get_box().position,
999 ComputedPosition::Relative | ComputedPosition::Sticky
1000 ) {
1001 return false;
1002 }
1003 let box_offsets = self.box_offsets(writing_mode);
1004 let has_percentage = |offset: LengthPercentageOrAuto<'_>| {
1005 offset
1006 .non_auto()
1007 .is_some_and(LengthPercentage::has_percentage)
1008 };
1009 has_percentage(box_offsets.block_start) || has_percentage(box_offsets.block_end)
1010 }
1011
1012 fn overflow_direction(&self) -> OverflowDirection {
1014 let inline_end_direction = self.writing_mode.inline_end_physical_side();
1015 let block_end_direction = self.writing_mode.block_end_physical_side();
1016
1017 let rightward = inline_end_direction == PhysicalSide::Right ||
1018 block_end_direction == PhysicalSide::Right;
1019 let downward = inline_end_direction == PhysicalSide::Bottom ||
1020 block_end_direction == PhysicalSide::Bottom;
1021
1022 OverflowDirection {
1024 rightward,
1025 downward,
1026 }
1027 }
1028
1029 fn to_bidi_level(&self) -> Level {
1033 if self.writing_mode.is_bidi_ltr() {
1034 Level::ltr()
1035 } else {
1036 Level::rtl()
1037 }
1038 }
1039}
1040
1041pub(crate) enum LayoutStyle<'a> {
1042 Default(&'a ComputedValues),
1043 Table(TableLayoutStyle<'a>),
1044}
1045
1046impl LayoutStyle<'_> {
1047 #[inline]
1048 pub(crate) fn style(&self) -> &ComputedValues {
1049 match self {
1050 Self::Default(style) => style,
1051 Self::Table(table) => table.style(),
1052 }
1053 }
1054
1055 #[inline]
1056 pub(crate) fn is_table(&self) -> bool {
1057 matches!(self, Self::Table(_))
1058 }
1059
1060 pub(crate) fn content_box_sizes_and_padding_border_margin(
1061 &self,
1062 containing_block: &IndefiniteContainingBlock,
1063 ) -> ContentBoxSizesAndPBM {
1064 let containing_block_size_or_zero =
1070 containing_block.size.map(|value| value.unwrap_or_default());
1071 let writing_mode = containing_block.style.writing_mode;
1072 let pbm = self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1073 writing_mode,
1074 containing_block_size_or_zero.inline,
1075 );
1076 let style = self.style();
1077 let box_size = style.box_size(writing_mode);
1078 let min_size = style.min_box_size(writing_mode);
1079 let max_size = style.max_box_size(writing_mode);
1080 let preferred_size_computes_to_auto = box_size.map(|size| size.is_initial());
1081
1082 let depends_on_block_constraints = |size: &Size<LengthPercentage>| {
1083 match size {
1084 Size::Stretch => true,
1092 Size::Numeric(length_percentage) => length_percentage.has_percentage(),
1093 _ => false,
1094 }
1095 };
1096 let depends_on_block_constraints = depends_on_block_constraints(&box_size.block) ||
1097 depends_on_block_constraints(&min_size.block) ||
1098 depends_on_block_constraints(&max_size.block) ||
1099 style.depends_on_block_constraints_due_to_relative_positioning(writing_mode);
1100
1101 let box_size = box_size.map_with(&containing_block.size, |size, basis| {
1102 size.resolve_percentages_for_preferred(*basis)
1103 });
1104 let content_box_size = style.content_box_size_for_box_size(box_size, &pbm);
1105 let min_size = min_size.percentages_relative_to_basis(&containing_block_size_or_zero);
1106 let content_min_box_size = style.content_min_box_size_for_min_size(min_size, &pbm);
1107 let max_size = max_size.map_with(&containing_block.size, |size, basis| {
1108 size.resolve_percentages_for_max(*basis)
1109 });
1110 let content_max_box_size = style.content_max_box_size_for_max_size(max_size, &pbm);
1111 ContentBoxSizesAndPBM {
1112 content_box_sizes: LogicalVec2 {
1113 block: Sizes::new(
1114 content_box_size.block,
1115 content_min_box_size.block,
1116 content_max_box_size.block,
1117 ),
1118 inline: Sizes::new(
1119 content_box_size.inline,
1120 content_min_box_size.inline,
1121 content_max_box_size.inline,
1122 ),
1123 },
1124 pbm,
1125 depends_on_block_constraints,
1126 preferred_size_computes_to_auto,
1127 }
1128 }
1129
1130 pub(crate) fn padding_border_margin(
1131 &self,
1132 containing_block: &ContainingBlock,
1133 ) -> PaddingBorderMargin {
1134 self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1135 containing_block.style.writing_mode,
1136 containing_block.size.inline,
1137 )
1138 }
1139
1140 pub(crate) fn padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1141 &self,
1142 writing_mode: WritingMode,
1143 containing_block_inline_size: Au,
1144 ) -> PaddingBorderMargin {
1145 let padding = self
1146 .padding(writing_mode)
1147 .percentages_relative_to(containing_block_inline_size);
1148 let style = self.style();
1149 let border = self.border_width(writing_mode);
1150 let margin = style
1151 .margin(writing_mode)
1152 .percentages_relative_to(containing_block_inline_size);
1153 PaddingBorderMargin {
1154 padding_border_sums: LogicalVec2 {
1155 inline: padding.inline_sum() + border.inline_sum(),
1156 block: padding.block_sum() + border.block_sum(),
1157 },
1158 padding,
1159 border,
1160 margin,
1161 }
1162 }
1163
1164 pub(crate) fn padding(
1165 &self,
1166 containing_block_writing_mode: WritingMode,
1167 ) -> LogicalSides<LengthPercentage> {
1168 if matches!(self, Self::Table(table) if table.collapses_borders()) {
1169 return LogicalSides::zero();
1172 }
1173 let padding = self.style().get_padding().clone();
1174 LogicalSides::from_physical(
1175 &PhysicalSides::new(
1176 padding.padding_top.0,
1177 padding.padding_right.0,
1178 padding.padding_bottom.0,
1179 padding.padding_left.0,
1180 ),
1181 containing_block_writing_mode,
1182 )
1183 }
1184
1185 pub(crate) fn border_width(
1186 &self,
1187 containing_block_writing_mode: WritingMode,
1188 ) -> LogicalSides<Au> {
1189 let border_width = match self {
1190 Self::Table(table) if table.collapses_borders() => table
1194 .halved_collapsed_border_widths()
1195 .to_physical(self.style().writing_mode),
1196 _ => {
1197 let border = self.style().get_border();
1198 PhysicalSides::new(
1199 border.border_top_width,
1200 border.border_right_width,
1201 border.border_bottom_width,
1202 border.border_left_width,
1203 )
1204 },
1205 };
1206 LogicalSides::from_physical(&border_width, containing_block_writing_mode)
1207 }
1208}
1209
1210impl From<stylo::Display> for Display {
1211 fn from(packed: stylo::Display) -> Self {
1212 let outside = packed.outside();
1213 let inside = packed.inside();
1214
1215 let outside = match outside {
1216 stylo::DisplayOutside::Block => DisplayOutside::Block,
1217 stylo::DisplayOutside::Inline => DisplayOutside::Inline,
1218 stylo::DisplayOutside::TableCaption => {
1219 return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(
1220 DisplayLayoutInternal::TableCaption,
1221 ));
1222 },
1223 stylo::DisplayOutside::InternalTable => {
1224 let internal = match inside {
1225 stylo::DisplayInside::TableRowGroup => DisplayLayoutInternal::TableRowGroup,
1226 stylo::DisplayInside::TableColumn => DisplayLayoutInternal::TableColumn,
1227 stylo::DisplayInside::TableColumnGroup => {
1228 DisplayLayoutInternal::TableColumnGroup
1229 },
1230 stylo::DisplayInside::TableHeaderGroup => {
1231 DisplayLayoutInternal::TableHeaderGroup
1232 },
1233 stylo::DisplayInside::TableFooterGroup => {
1234 DisplayLayoutInternal::TableFooterGroup
1235 },
1236 stylo::DisplayInside::TableRow => DisplayLayoutInternal::TableRow,
1237 stylo::DisplayInside::TableCell => DisplayLayoutInternal::TableCell,
1238 _ => unreachable!("Non-internal DisplayInside found"),
1239 };
1240 return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(internal));
1241 },
1242 stylo::DisplayOutside::None if inside == stylo::DisplayInside::Contents => {
1245 return Display::Contents;
1246 },
1247 stylo::DisplayOutside::None => return Display::None,
1248 };
1249
1250 let inside = match packed.inside() {
1251 stylo::DisplayInside::Flow => DisplayInside::Flow {
1252 is_list_item: packed.is_list_item(),
1253 },
1254 stylo::DisplayInside::FlowRoot => DisplayInside::FlowRoot {
1255 is_list_item: packed.is_list_item(),
1256 },
1257 stylo::DisplayInside::Flex => DisplayInside::Flex,
1258 stylo::DisplayInside::Grid => DisplayInside::Grid,
1259
1260 stylo::DisplayInside::None => return Display::None,
1262 stylo::DisplayInside::Contents => return Display::Contents,
1263
1264 stylo::DisplayInside::Table => DisplayInside::Table,
1265 stylo::DisplayInside::TableRowGroup |
1266 stylo::DisplayInside::TableColumn |
1267 stylo::DisplayInside::TableColumnGroup |
1268 stylo::DisplayInside::TableHeaderGroup |
1269 stylo::DisplayInside::TableFooterGroup |
1270 stylo::DisplayInside::TableRow |
1271 stylo::DisplayInside::TableCell => unreachable!("Internal DisplayInside found"),
1272 };
1273 Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { outside, inside })
1274 }
1275}
1276
1277pub(crate) trait Clamp: Sized {
1278 fn clamp_below_max(self, max: Option<Self>) -> Self;
1279 fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self;
1280}
1281
1282impl Clamp for Au {
1283 fn clamp_below_max(self, max: Option<Self>) -> Self {
1284 match max {
1285 None => self,
1286 Some(max) => self.min(max),
1287 }
1288 }
1289
1290 fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self {
1291 self.clamp_below_max(max).max(min)
1292 }
1293}
1294
1295pub(crate) trait TransformExt {
1296 fn change_basis(&self, x: f32, y: f32, z: f32) -> Self;
1297}
1298
1299impl TransformExt for LayoutTransform {
1300 fn change_basis(&self, x: f32, y: f32, z: f32) -> Self {
1302 let pre_translation = Self::translation(x, y, z);
1303 let post_translation = Self::translation(-x, -y, -z);
1304 post_translation.then(self).then(&pre_translation)
1305 }
1306}