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;
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!(contents, Contents::Widget(_)) {
85 if let DisplayGeneratingBox::OutsideInside { outside, .. } = self {
89 DisplayGeneratingBox::OutsideInside {
90 outside: *outside,
91 inside: DisplayInside::FlowRoot {
92 is_list_item: false,
93 },
94 }
95 } else {
96 *self
97 }
98 } else {
99 *self
100 }
101 }
102}
103
104#[derive(Clone, Copy, Debug, Eq, PartialEq)]
105pub(crate) enum DisplayOutside {
106 Block,
107 Inline,
108}
109
110#[derive(Clone, Copy, Debug, Eq, PartialEq)]
111pub(crate) enum DisplayInside {
112 Flow { is_list_item: bool },
115 FlowRoot { is_list_item: bool },
116 Flex,
117 Grid,
118 Table,
119}
120
121#[derive(Clone, Copy, Debug, Eq, PartialEq)]
122#[allow(clippy::enum_variant_names)]
123pub(crate) enum DisplayLayoutInternal {
125 TableCaption,
126 TableCell,
127 TableColumn,
128 TableColumnGroup,
129 TableFooterGroup,
130 TableHeaderGroup,
131 TableRow,
132 TableRowGroup,
133}
134
135impl DisplayLayoutInternal {
136 pub(crate) fn display_inside(&self) -> DisplayInside {
138 DisplayInside::FlowRoot {
142 is_list_item: false,
143 }
144 }
145}
146
147#[derive(Clone, Debug)]
149pub(crate) struct PaddingBorderMargin {
150 pub padding: LogicalSides<Au>,
151 pub border: LogicalSides<Au>,
152 pub margin: LogicalSides<AuOrAuto>,
153
154 pub padding_border_sums: LogicalVec2<Au>,
156}
157
158impl PaddingBorderMargin {
159 pub(crate) fn zero() -> Self {
160 Self {
161 padding: LogicalSides::zero(),
162 border: LogicalSides::zero(),
163 margin: LogicalSides::zero(),
164 padding_border_sums: LogicalVec2::zero(),
165 }
166 }
167
168 pub(crate) fn sums_auto_is_zero(
169 &self,
170 ignore_block_margins: LogicalSides1D<bool>,
171 ) -> LogicalVec2<Au> {
172 let margin = self.margin.auto_is(Au::zero);
173 let mut sums = self.padding_border_sums;
174 sums.inline += margin.inline_sum();
175 if !ignore_block_margins.start {
176 sums.block += margin.block_start;
177 }
178 if !ignore_block_margins.end {
179 sums.block += margin.block_end;
180 }
181 sums
182 }
183}
184
185#[derive(Clone, Copy, Debug)]
189pub(crate) struct AspectRatio {
190 box_sizing_adjustment: LogicalVec2<Au>,
195 i_over_b: CSSFloat,
197}
198
199impl AspectRatio {
200 pub(crate) fn compute_dependent_size(
202 &self,
203 ratio_dependent_axis: AxisDirection,
204 ratio_determining_size: Au,
205 ) -> Au {
206 match ratio_dependent_axis {
207 AxisDirection::Inline => {
209 (ratio_determining_size + self.box_sizing_adjustment.block).scale_by(self.i_over_b) -
210 self.box_sizing_adjustment.inline
211 },
212 AxisDirection::Block => {
214 (ratio_determining_size + self.box_sizing_adjustment.inline)
215 .scale_by(1.0 / self.i_over_b) -
216 self.box_sizing_adjustment.block
217 },
218 }
219 }
220
221 pub(crate) fn from_logical_content_ratio(i_over_b: CSSFloat) -> Self {
222 Self {
223 box_sizing_adjustment: LogicalVec2::zero(),
224 i_over_b,
225 }
226 }
227}
228
229#[derive(Clone)]
230pub(crate) struct ContentBoxSizesAndPBM {
231 pub content_box_sizes: LogicalVec2<Sizes>,
232 pub pbm: PaddingBorderMargin,
233 pub depends_on_block_constraints: bool,
234 pub preferred_size_computes_to_auto: LogicalVec2<bool>,
235}
236
237#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
238pub(crate) struct BorderStyleColor {
239 pub style: BorderStyle,
240 pub color: AbsoluteColor,
241}
242
243impl BorderStyleColor {
244 pub(crate) fn new(style: BorderStyle, color: AbsoluteColor) -> Self {
245 Self { style, color }
246 }
247
248 pub(crate) fn from_border(
249 border: &Border,
250 current_color: &AbsoluteColor,
251 ) -> PhysicalSides<Self> {
252 let resolve = |color: &Color| color.resolve_to_absolute(current_color);
253 PhysicalSides::<Self>::new(
254 Self::new(border.border_top_style, resolve(&border.border_top_color)),
255 Self::new(
256 border.border_right_style,
257 resolve(&border.border_right_color),
258 ),
259 Self::new(
260 border.border_bottom_style,
261 resolve(&border.border_bottom_color),
262 ),
263 Self::new(border.border_left_style, resolve(&border.border_left_color)),
264 )
265 }
266
267 pub(crate) fn hidden() -> Self {
268 Self::new(BorderStyle::Hidden, AbsoluteColor::TRANSPARENT_BLACK)
269 }
270}
271
272impl Default for BorderStyleColor {
273 fn default() -> Self {
274 Self::new(BorderStyle::None, AbsoluteColor::TRANSPARENT_BLACK)
275 }
276}
277
278pub(crate) struct OverflowDirection {
282 pub rightward: bool,
284 pub downward: bool,
286}
287
288pub(crate) trait ComputedValuesExt {
289 fn physical_box_offsets(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>>;
290 fn box_offsets(&self, writing_mode: WritingMode) -> LogicalSides<LengthPercentageOrAuto<'_>>;
291 fn box_size(
292 &self,
293 containing_block_writing_mode: WritingMode,
294 ) -> LogicalVec2<Size<LengthPercentage>>;
295 fn min_box_size(
296 &self,
297 containing_block_writing_mode: WritingMode,
298 ) -> LogicalVec2<Size<LengthPercentage>>;
299 fn max_box_size(
300 &self,
301 containing_block_writing_mode: WritingMode,
302 ) -> LogicalVec2<Size<LengthPercentage>>;
303 fn content_box_size_for_box_size(
304 &self,
305 box_size: LogicalVec2<Size<Au>>,
306 pbm: &PaddingBorderMargin,
307 ) -> LogicalVec2<Size<Au>>;
308 fn content_min_box_size_for_min_size(
309 &self,
310 box_size: LogicalVec2<Size<Au>>,
311 pbm: &PaddingBorderMargin,
312 ) -> LogicalVec2<Size<Au>>;
313 fn content_max_box_size_for_max_size(
314 &self,
315 box_size: LogicalVec2<Size<Au>>,
316 pbm: &PaddingBorderMargin,
317 ) -> LogicalVec2<Size<Au>>;
318 fn border_style_color(
319 &self,
320 containing_block_writing_mode: WritingMode,
321 ) -> LogicalSides<BorderStyleColor>;
322 fn physical_margin(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>>;
323 fn margin(
324 &self,
325 containing_block_writing_mode: WritingMode,
326 ) -> LogicalSides<LengthPercentageOrAuto<'_>>;
327 fn is_transformable(&self, fragment_flags: FragmentFlags) -> bool;
328 fn has_transform_or_perspective_style(&self) -> bool;
329 fn has_effective_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool;
330 fn z_index_applies(&self, fragment_flags: FragmentFlags) -> bool;
331 fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32;
332 fn effective_overflow(&self, fragment_flags: FragmentFlags) -> AxesOverflow;
333 fn establishes_block_formatting_context(&self, fragment_flags: FragmentFlags) -> bool;
334 fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool;
335 fn establishes_scroll_container(&self, fragment_flags: FragmentFlags) -> bool;
336 fn establishes_containing_block_for_absolute_descendants(
337 &self,
338 fragment_flags: FragmentFlags,
339 ) -> bool;
340 fn establishes_containing_block_for_all_descendants(
341 &self,
342 fragment_flags: FragmentFlags,
343 ) -> bool;
344 fn preferred_aspect_ratio(
345 &self,
346 natural_aspect_ratio: Option<CSSFloat>,
347 padding_border_sums: &LogicalVec2<Au>,
348 ) -> Option<AspectRatio>;
349 fn background_is_transparent(&self) -> bool;
350 fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags;
351 fn bidi_control_chars(&self) -> (&'static str, &'static str);
352 fn resolve_align_self(
353 &self,
354 resolved_auto_value: AlignItems,
355 resolved_normal_value: AlignItems,
356 ) -> AlignItems;
357 fn depends_on_block_constraints_due_to_relative_positioning(
358 &self,
359 writing_mode: WritingMode,
360 ) -> bool;
361 fn is_inline_box(&self, fragment_flags: FragmentFlags) -> bool;
362 fn overflow_direction(&self) -> OverflowDirection;
363 fn to_bidi_level(&self) -> Level;
364}
365
366impl ComputedValuesExt for ComputedValues {
367 fn physical_box_offsets(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>> {
368 fn convert(inset: &Inset) -> LengthPercentageOrAuto<'_> {
369 match inset {
370 Inset::LengthPercentage(v) => LengthPercentageOrAuto::LengthPercentage(v),
371 Inset::Auto => LengthPercentageOrAuto::Auto,
372 Inset::AnchorFunction(_) => unreachable!("anchor() should be disabled"),
373 Inset::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"),
374 Inset::AnchorContainingCalcFunction(_) => {
375 unreachable!("anchor() and anchor-size() should be disabled")
376 },
377 }
378 }
379 let position = self.get_position();
380 PhysicalSides::new(
381 convert(&position.top),
382 convert(&position.right),
383 convert(&position.bottom),
384 convert(&position.left),
385 )
386 }
387
388 fn box_offsets(&self, writing_mode: WritingMode) -> LogicalSides<LengthPercentageOrAuto<'_>> {
389 LogicalSides::from_physical(&self.physical_box_offsets(), writing_mode)
390 }
391
392 fn box_size(
393 &self,
394 containing_block_writing_mode: WritingMode,
395 ) -> LogicalVec2<Size<LengthPercentage>> {
396 let position = self.get_position();
397 LogicalVec2::from_physical_size(
398 &PhysicalSize::new(
399 position.clone_width().into(),
400 position.clone_height().into(),
401 ),
402 containing_block_writing_mode,
403 )
404 }
405
406 fn min_box_size(
407 &self,
408 containing_block_writing_mode: WritingMode,
409 ) -> LogicalVec2<Size<LengthPercentage>> {
410 let position = self.get_position();
411 LogicalVec2::from_physical_size(
412 &PhysicalSize::new(
413 position.clone_min_width().into(),
414 position.clone_min_height().into(),
415 ),
416 containing_block_writing_mode,
417 )
418 }
419
420 fn max_box_size(
421 &self,
422 containing_block_writing_mode: WritingMode,
423 ) -> LogicalVec2<Size<LengthPercentage>> {
424 let position = self.get_position();
425 LogicalVec2::from_physical_size(
426 &PhysicalSize::new(
427 position.clone_max_width().into(),
428 position.clone_max_height().into(),
429 ),
430 containing_block_writing_mode,
431 )
432 }
433
434 fn content_box_size_for_box_size(
435 &self,
436 box_size: LogicalVec2<Size<Au>>,
437 pbm: &PaddingBorderMargin,
438 ) -> LogicalVec2<Size<Au>> {
439 match self.get_position().box_sizing {
440 BoxSizing::ContentBox => box_size,
441 BoxSizing::BorderBox => box_size.map_inline_and_block_sizes(
444 |value| value - pbm.padding_border_sums.inline,
445 |value| value - pbm.padding_border_sums.block,
446 ),
447 }
448 }
449
450 fn content_min_box_size_for_min_size(
451 &self,
452 min_box_size: LogicalVec2<Size<Au>>,
453 pbm: &PaddingBorderMargin,
454 ) -> LogicalVec2<Size<Au>> {
455 match self.get_position().box_sizing {
456 BoxSizing::ContentBox => min_box_size,
457 BoxSizing::BorderBox => min_box_size.map_inline_and_block_sizes(
459 |value| Au::zero().max(value - pbm.padding_border_sums.inline),
460 |value| Au::zero().max(value - pbm.padding_border_sums.block),
461 ),
462 }
463 }
464
465 fn content_max_box_size_for_max_size(
466 &self,
467 max_box_size: LogicalVec2<Size<Au>>,
468 pbm: &PaddingBorderMargin,
469 ) -> LogicalVec2<Size<Au>> {
470 match self.get_position().box_sizing {
471 BoxSizing::ContentBox => max_box_size,
472 BoxSizing::BorderBox => max_box_size.map_inline_and_block_sizes(
475 |value| value - pbm.padding_border_sums.inline,
476 |value| value - pbm.padding_border_sums.block,
477 ),
478 }
479 }
480
481 fn border_style_color(
482 &self,
483 containing_block_writing_mode: WritingMode,
484 ) -> LogicalSides<BorderStyleColor> {
485 let current_color = self.get_inherited_text().clone_color();
486 LogicalSides::from_physical(
487 &BorderStyleColor::from_border(self.get_border(), ¤t_color),
488 containing_block_writing_mode,
489 )
490 }
491
492 fn physical_margin(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>> {
493 fn convert(inset: &Margin) -> LengthPercentageOrAuto<'_> {
494 match inset {
495 Margin::LengthPercentage(v) => LengthPercentageOrAuto::LengthPercentage(v),
496 Margin::Auto => LengthPercentageOrAuto::Auto,
497 Margin::AnchorSizeFunction(_) | Margin::AnchorContainingCalcFunction(_) => {
498 unreachable!("anchor-size() should be disabled")
499 },
500 }
501 }
502 let margin = self.get_margin();
503 PhysicalSides::new(
504 convert(&margin.margin_top),
505 convert(&margin.margin_right),
506 convert(&margin.margin_bottom),
507 convert(&margin.margin_left),
508 )
509 }
510
511 fn margin(
512 &self,
513 containing_block_writing_mode: WritingMode,
514 ) -> LogicalSides<LengthPercentageOrAuto<'_>> {
515 LogicalSides::from_physical(&self.physical_margin(), containing_block_writing_mode)
516 }
517
518 fn is_inline_box(&self, fragment_flags: FragmentFlags) -> bool {
519 self.get_box().display.is_inline_flow() &&
520 !fragment_flags.intersects(FragmentFlags::IS_REPLACED | FragmentFlags::IS_WIDGET)
521 }
522
523 fn is_transformable(&self, fragment_flags: FragmentFlags) -> bool {
525 !self.is_inline_box(fragment_flags)
535 }
536
537 fn has_transform_or_perspective_style(&self) -> bool {
539 !self.get_box().transform.0.is_empty() ||
540 self.get_box().scale != GenericScale::None ||
541 self.get_box().rotate != GenericRotate::None ||
542 self.get_box().translate != GenericTranslate::None ||
543 self.get_box().perspective != Perspective::None
544 }
545
546 #[inline]
549 fn has_effective_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool {
550 self.is_transformable(fragment_flags) && self.has_transform_or_perspective_style()
551 }
552
553 fn z_index_applies(&self, fragment_flags: FragmentFlags) -> bool {
555 if self.get_box().position != ComputedPosition::Static {
558 return true;
559 }
560 fragment_flags.contains(FragmentFlags::IS_FLEX_OR_GRID_ITEM)
572 }
573
574 fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32 {
578 if self.z_index_applies(fragment_flags) {
579 self.get_position().z_index.integer_or(0)
580 } else {
581 0
582 }
583 }
584
585 fn effective_overflow(&self, fragment_flags: FragmentFlags) -> AxesOverflow {
589 if fragment_flags.contains(FragmentFlags::PROPAGATED_OVERFLOW_TO_VIEWPORT) {
592 return AxesOverflow::default();
593 }
594
595 let mut overflow = AxesOverflow::from(self);
596
597 if fragment_flags.contains(FragmentFlags::IS_REPLACED) {
600 if overflow.x != Overflow::Visible {
601 overflow.x = Overflow::Clip;
602 }
603 if overflow.y != Overflow::Visible {
604 overflow.y = Overflow::Clip;
605 }
606 return overflow;
607 }
608
609 let ignores_overflow = match self.get_box().display.inside() {
610 stylo::DisplayInside::Flow => self.is_inline_box(fragment_flags),
613
614 stylo::DisplayInside::Table => {
622 !matches!(self.pseudo(), Some(PseudoElement::ServoTableGrid)) ||
623 matches!(overflow.x, Overflow::Auto | Overflow::Scroll) ||
624 matches!(overflow.y, Overflow::Auto | Overflow::Scroll)
625 },
626
627 stylo::DisplayInside::TableColumn |
630 stylo::DisplayInside::TableColumnGroup |
631 stylo::DisplayInside::TableRow |
632 stylo::DisplayInside::TableRowGroup |
633 stylo::DisplayInside::TableHeaderGroup |
634 stylo::DisplayInside::TableFooterGroup => true,
635
636 _ => false,
637 };
638 if ignores_overflow {
639 return AxesOverflow::default();
640 }
641
642 overflow
643 }
644
645 fn establishes_block_formatting_context(&self, fragment_flags: FragmentFlags) -> bool {
652 if self.establishes_scroll_container(fragment_flags) {
653 return true;
654 }
655
656 if self.get_column().is_multicol() {
657 return true;
658 }
659
660 if self.get_column().column_span == ColumnSpan::All {
661 return true;
662 }
663
664 if self.get_position().align_content.0.primary() != AlignFlags::NORMAL {
670 return true;
671 }
672
673 false
675 }
676
677 fn establishes_scroll_container(&self, fragment_flags: FragmentFlags) -> bool {
679 self.effective_overflow(fragment_flags).x.is_scrollable()
682 }
683
684 fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool {
686 let will_change_bits = self.clone_will_change().bits;
690 if will_change_bits
691 .intersects(WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::OPACITY)
692 {
693 return true;
694 }
695
696 if self.z_index_applies(fragment_flags) &&
699 (!self.get_position().z_index.is_auto() ||
700 will_change_bits.intersects(WillChangeBits::Z_INDEX))
701 {
702 return true;
703 }
704
705 if matches!(
708 self.get_box().position,
709 ComputedPosition::Fixed | ComputedPosition::Sticky
710 ) {
711 return true;
712 }
713
714 if self.is_transformable(fragment_flags) &&
729 (self.has_transform_or_perspective_style() ||
730 self.get_box().transform_style == ComputedTransformStyle::Preserve3d ||
731 will_change_bits
732 .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
733 {
734 return true;
735 }
736
737 let effects = self.get_effects();
741 if effects.opacity != 1.0 {
742 return true;
743 }
744
745 if !effects.filter.0.is_empty() {
749 return true;
750 }
751
752 if effects.mix_blend_mode != ComputedMixBlendMode::Normal {
756 return true;
757 }
758
759 if self.get_svg().clip_path != ClipPath::None {
763 return true;
764 }
765
766 if self.get_box().isolation == ComputedIsolation::Isolate {
770 return true;
771 }
772
773 if fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT) {
776 return true;
777 }
778
779 false
781 }
782
783 fn establishes_containing_block_for_absolute_descendants(
790 &self,
791 fragment_flags: FragmentFlags,
792 ) -> bool {
793 if self.establishes_containing_block_for_all_descendants(fragment_flags) {
794 return true;
795 }
796
797 if self
802 .clone_will_change()
803 .bits
804 .intersects(WillChangeBits::POSITION)
805 {
806 return true;
807 }
808
809 self.clone_position() != ComputedPosition::Static
810 }
811
812 fn establishes_containing_block_for_all_descendants(
817 &self,
818 fragment_flags: FragmentFlags,
819 ) -> bool {
820 let will_change_bits = self.clone_will_change().bits;
825
826 if self.is_transformable(fragment_flags) &&
842 (self.has_transform_or_perspective_style() ||
843 self.get_box().transform_style == ComputedTransformStyle::Preserve3d ||
844 will_change_bits
845 .intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
846 {
847 return true;
848 }
849
850 if !fragment_flags.contains(FragmentFlags::IS_ROOT_ELEMENT) &&
855 (!self.get_effects().filter.0.is_empty() ||
856 will_change_bits.intersects(WillChangeBits::FIXPOS_CB_NON_SVG))
857 {
858 return true;
859 }
860
861 false
863 }
864
865 fn preferred_aspect_ratio(
869 &self,
870 natural_aspect_ratio: Option<CSSFloat>,
871 padding_border_sums: &LogicalVec2<Au>,
872 ) -> Option<AspectRatio> {
873 let GenericAspectRatio {
874 auto,
875 ratio: mut preferred_ratio,
876 } = self.clone_aspect_ratio();
877
878 if matches!(preferred_ratio, PreferredRatio::Ratio(ratio) if ratio.is_degenerate()) {
881 preferred_ratio = PreferredRatio::None;
882 }
883
884 let to_logical_ratio = |physical_ratio| {
885 if self.writing_mode.is_horizontal() {
886 physical_ratio
887 } else {
888 1.0 / physical_ratio
889 }
890 };
891
892 match (auto, preferred_ratio) {
893 (_, PreferredRatio::None) => natural_aspect_ratio
901 .map(to_logical_ratio)
902 .map(AspectRatio::from_logical_content_ratio),
903 (true, PreferredRatio::Ratio(preferred_ratio)) => Some({
910 let physical_ratio = natural_aspect_ratio
911 .unwrap_or_else(|| (preferred_ratio.0).0 / (preferred_ratio.1).0);
912 AspectRatio::from_logical_content_ratio(to_logical_ratio(physical_ratio))
913 }),
914
915 (false, PreferredRatio::Ratio(preferred_ratio)) => {
919 let box_sizing_adjustment = match self.clone_box_sizing() {
922 BoxSizing::ContentBox => LogicalVec2::zero(),
923 BoxSizing::BorderBox => *padding_border_sums,
924 };
925 Some(AspectRatio {
926 i_over_b: to_logical_ratio((preferred_ratio.0).0 / (preferred_ratio.1).0),
927 box_sizing_adjustment,
928 })
929 },
930 }
931 }
932
933 fn background_is_transparent(&self) -> bool {
935 let background = self.get_background();
936 let color = self.resolve_color(&background.background_color);
937 color.alpha == 0.0 &&
938 background
939 .background_image
940 .0
941 .iter()
942 .all(|layer| matches!(layer, ComputedImageLayer::None))
943 }
944
945 fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags {
948 match self.get_box().backface_visibility {
949 BackfaceVisiblity::Visible => wr::PrimitiveFlags::default(),
950 BackfaceVisiblity::Hidden => wr::PrimitiveFlags::empty(),
951 }
952 }
953
954 fn bidi_control_chars(&self) -> (&'static str, &'static str) {
958 match (
959 self.get_text().unicode_bidi,
960 self.get_inherited_box().direction,
961 ) {
962 (UnicodeBidi::Normal, _) => ("", ""),
963 (UnicodeBidi::Embed, Direction::Ltr) => ("\u{202a}", "\u{202c}"),
964 (UnicodeBidi::Embed, Direction::Rtl) => ("\u{202b}", "\u{202c}"),
965 (UnicodeBidi::Isolate, Direction::Ltr) => ("\u{2066}", "\u{2069}"),
966 (UnicodeBidi::Isolate, Direction::Rtl) => ("\u{2067}", "\u{2069}"),
967 (UnicodeBidi::BidiOverride, Direction::Ltr) => ("\u{202d}", "\u{202c}"),
968 (UnicodeBidi::BidiOverride, Direction::Rtl) => ("\u{202e}", "\u{202c}"),
969 (UnicodeBidi::IsolateOverride, Direction::Ltr) => {
970 ("\u{2068}\u{202d}", "\u{202c}\u{2069}")
971 },
972 (UnicodeBidi::IsolateOverride, Direction::Rtl) => {
973 ("\u{2068}\u{202e}", "\u{202c}\u{2069}")
974 },
975 (UnicodeBidi::Plaintext, _) => ("\u{2068}", "\u{2069}"),
976 }
977 }
978
979 fn resolve_align_self(
980 &self,
981 resolved_auto_value: AlignItems,
982 resolved_normal_value: AlignItems,
983 ) -> AlignItems {
984 match self.clone_align_self().0.0 {
985 AlignFlags::AUTO => resolved_auto_value,
986 AlignFlags::NORMAL => resolved_normal_value,
987 value => AlignItems(value),
988 }
989 }
990
991 fn depends_on_block_constraints_due_to_relative_positioning(
992 &self,
993 writing_mode: WritingMode,
994 ) -> bool {
995 if !matches!(
996 self.get_box().position,
997 ComputedPosition::Relative | ComputedPosition::Sticky
998 ) {
999 return false;
1000 }
1001 let box_offsets = self.box_offsets(writing_mode);
1002 let has_percentage = |offset: LengthPercentageOrAuto<'_>| {
1003 offset
1004 .non_auto()
1005 .is_some_and(LengthPercentage::has_percentage)
1006 };
1007 has_percentage(box_offsets.block_start) || has_percentage(box_offsets.block_end)
1008 }
1009
1010 fn overflow_direction(&self) -> OverflowDirection {
1012 let inline_end_direction = self.writing_mode.inline_end_physical_side();
1013 let block_end_direction = self.writing_mode.block_end_physical_side();
1014
1015 let rightward = inline_end_direction == PhysicalSide::Right ||
1016 block_end_direction == PhysicalSide::Right;
1017 let downward = inline_end_direction == PhysicalSide::Bottom ||
1018 block_end_direction == PhysicalSide::Bottom;
1019
1020 OverflowDirection {
1022 rightward,
1023 downward,
1024 }
1025 }
1026
1027 fn to_bidi_level(&self) -> Level {
1031 if self.writing_mode.is_bidi_ltr() {
1032 Level::ltr()
1033 } else {
1034 Level::rtl()
1035 }
1036 }
1037}
1038
1039pub(crate) enum LayoutStyle<'a> {
1040 Default(&'a ComputedValues),
1041 Table(TableLayoutStyle<'a>),
1042}
1043
1044impl LayoutStyle<'_> {
1045 #[inline]
1046 pub(crate) fn style(&self) -> &ComputedValues {
1047 match self {
1048 Self::Default(style) => style,
1049 Self::Table(table) => table.style(),
1050 }
1051 }
1052
1053 #[inline]
1054 pub(crate) fn is_table(&self) -> bool {
1055 matches!(self, Self::Table(_))
1056 }
1057
1058 pub(crate) fn content_box_sizes_and_padding_border_margin(
1059 &self,
1060 containing_block: &IndefiniteContainingBlock,
1061 ) -> ContentBoxSizesAndPBM {
1062 let containing_block_size_or_zero =
1068 containing_block.size.map(|value| value.unwrap_or_default());
1069 let writing_mode = containing_block.style.writing_mode;
1070 let pbm = self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1071 writing_mode,
1072 containing_block_size_or_zero.inline,
1073 );
1074 let style = self.style();
1075 let box_size = style.box_size(writing_mode);
1076 let min_size = style.min_box_size(writing_mode);
1077 let max_size = style.max_box_size(writing_mode);
1078 let preferred_size_computes_to_auto = box_size.map(|size| size.is_initial());
1079
1080 let depends_on_block_constraints = |size: &Size<LengthPercentage>| {
1081 match size {
1082 Size::Stretch => true,
1090 Size::Numeric(length_percentage) => length_percentage.has_percentage(),
1091 _ => false,
1092 }
1093 };
1094 let depends_on_block_constraints = depends_on_block_constraints(&box_size.block) ||
1095 depends_on_block_constraints(&min_size.block) ||
1096 depends_on_block_constraints(&max_size.block) ||
1097 style.depends_on_block_constraints_due_to_relative_positioning(writing_mode);
1098
1099 let box_size = box_size.map_with(&containing_block.size, |size, basis| {
1100 size.resolve_percentages_for_preferred(*basis)
1101 });
1102 let content_box_size = style.content_box_size_for_box_size(box_size, &pbm);
1103 let min_size = min_size.percentages_relative_to_basis(&containing_block_size_or_zero);
1104 let content_min_box_size = style.content_min_box_size_for_min_size(min_size, &pbm);
1105 let max_size = max_size.map_with(&containing_block.size, |size, basis| {
1106 size.resolve_percentages_for_max(*basis)
1107 });
1108 let content_max_box_size = style.content_max_box_size_for_max_size(max_size, &pbm);
1109 ContentBoxSizesAndPBM {
1110 content_box_sizes: LogicalVec2 {
1111 block: Sizes::new(
1112 content_box_size.block,
1113 content_min_box_size.block,
1114 content_max_box_size.block,
1115 ),
1116 inline: Sizes::new(
1117 content_box_size.inline,
1118 content_min_box_size.inline,
1119 content_max_box_size.inline,
1120 ),
1121 },
1122 pbm,
1123 depends_on_block_constraints,
1124 preferred_size_computes_to_auto,
1125 }
1126 }
1127
1128 pub(crate) fn padding_border_margin(
1129 &self,
1130 containing_block: &ContainingBlock,
1131 ) -> PaddingBorderMargin {
1132 self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1133 containing_block.style.writing_mode,
1134 containing_block.size.inline,
1135 )
1136 }
1137
1138 pub(crate) fn padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1139 &self,
1140 writing_mode: WritingMode,
1141 containing_block_inline_size: Au,
1142 ) -> PaddingBorderMargin {
1143 let padding = self
1144 .padding(writing_mode)
1145 .percentages_relative_to(containing_block_inline_size);
1146 let style = self.style();
1147 let border = self.border_width(writing_mode);
1148 let margin = style
1149 .margin(writing_mode)
1150 .percentages_relative_to(containing_block_inline_size);
1151 PaddingBorderMargin {
1152 padding_border_sums: LogicalVec2 {
1153 inline: padding.inline_sum() + border.inline_sum(),
1154 block: padding.block_sum() + border.block_sum(),
1155 },
1156 padding,
1157 border,
1158 margin,
1159 }
1160 }
1161
1162 pub(crate) fn padding(
1163 &self,
1164 containing_block_writing_mode: WritingMode,
1165 ) -> LogicalSides<LengthPercentage> {
1166 if matches!(self, Self::Table(table) if table.collapses_borders()) {
1167 return LogicalSides::zero();
1170 }
1171 let padding = self.style().get_padding().clone();
1172 LogicalSides::from_physical(
1173 &PhysicalSides::new(
1174 padding.padding_top.0,
1175 padding.padding_right.0,
1176 padding.padding_bottom.0,
1177 padding.padding_left.0,
1178 ),
1179 containing_block_writing_mode,
1180 )
1181 }
1182
1183 pub(crate) fn border_width(
1184 &self,
1185 containing_block_writing_mode: WritingMode,
1186 ) -> LogicalSides<Au> {
1187 let border_width = match self {
1188 Self::Table(table) if table.collapses_borders() => table
1192 .halved_collapsed_border_widths()
1193 .to_physical(self.style().writing_mode),
1194 _ => {
1195 let border = self.style().get_border();
1196 PhysicalSides::new(
1197 border.border_top_width,
1198 border.border_right_width,
1199 border.border_bottom_width,
1200 border.border_left_width,
1201 )
1202 },
1203 };
1204 LogicalSides::from_physical(&border_width, containing_block_writing_mode)
1205 }
1206}
1207
1208impl From<stylo::Display> for Display {
1209 fn from(packed: stylo::Display) -> Self {
1210 let outside = packed.outside();
1211 let inside = packed.inside();
1212
1213 let outside = match outside {
1214 stylo::DisplayOutside::Block => DisplayOutside::Block,
1215 stylo::DisplayOutside::Inline => DisplayOutside::Inline,
1216 stylo::DisplayOutside::TableCaption => {
1217 return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(
1218 DisplayLayoutInternal::TableCaption,
1219 ));
1220 },
1221 stylo::DisplayOutside::InternalTable => {
1222 let internal = match inside {
1223 stylo::DisplayInside::TableRowGroup => DisplayLayoutInternal::TableRowGroup,
1224 stylo::DisplayInside::TableColumn => DisplayLayoutInternal::TableColumn,
1225 stylo::DisplayInside::TableColumnGroup => {
1226 DisplayLayoutInternal::TableColumnGroup
1227 },
1228 stylo::DisplayInside::TableHeaderGroup => {
1229 DisplayLayoutInternal::TableHeaderGroup
1230 },
1231 stylo::DisplayInside::TableFooterGroup => {
1232 DisplayLayoutInternal::TableFooterGroup
1233 },
1234 stylo::DisplayInside::TableRow => DisplayLayoutInternal::TableRow,
1235 stylo::DisplayInside::TableCell => DisplayLayoutInternal::TableCell,
1236 _ => unreachable!("Non-internal DisplayInside found"),
1237 };
1238 return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(internal));
1239 },
1240 stylo::DisplayOutside::None if inside == stylo::DisplayInside::Contents => {
1243 return Display::Contents;
1244 },
1245 stylo::DisplayOutside::None => return Display::None,
1246 };
1247
1248 let inside = match packed.inside() {
1249 stylo::DisplayInside::Flow => DisplayInside::Flow {
1250 is_list_item: packed.is_list_item(),
1251 },
1252 stylo::DisplayInside::FlowRoot => DisplayInside::FlowRoot {
1253 is_list_item: packed.is_list_item(),
1254 },
1255 stylo::DisplayInside::Flex => DisplayInside::Flex,
1256 stylo::DisplayInside::Grid => DisplayInside::Grid,
1257
1258 stylo::DisplayInside::None => return Display::None,
1260 stylo::DisplayInside::Contents => return Display::Contents,
1261
1262 stylo::DisplayInside::Table => DisplayInside::Table,
1263 stylo::DisplayInside::TableRowGroup |
1264 stylo::DisplayInside::TableColumn |
1265 stylo::DisplayInside::TableColumnGroup |
1266 stylo::DisplayInside::TableHeaderGroup |
1267 stylo::DisplayInside::TableFooterGroup |
1268 stylo::DisplayInside::TableRow |
1269 stylo::DisplayInside::TableCell => unreachable!("Internal DisplayInside found"),
1270 };
1271 Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { outside, inside })
1272 }
1273}
1274
1275pub(crate) trait Clamp: Sized {
1276 fn clamp_below_max(self, max: Option<Self>) -> Self;
1277 fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self;
1278}
1279
1280impl Clamp for Au {
1281 fn clamp_below_max(self, max: Option<Self>) -> Self {
1282 match max {
1283 None => self,
1284 Some(max) => self.min(max),
1285 }
1286 }
1287
1288 fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self {
1289 self.clamp_below_max(max).max(min)
1290 }
1291}
1292
1293pub(crate) trait TransformExt {
1294 fn change_basis(&self, x: f32, y: f32, z: f32) -> Self;
1295}
1296
1297impl TransformExt for LayoutTransform {
1298 fn change_basis(&self, x: f32, y: f32, z: f32) -> Self {
1300 let pre_translation = Self::translation(x, y, z);
1301 let post_translation = Self::translation(-x, -y, -z);
1302 post_translation.then(self).then(&pre_translation)
1303 }
1304}