use app_units::Au;
use style::computed_values::direction::T as Direction;
use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode;
use style::computed_values::position::T as ComputedPosition;
use style::computed_values::transform_style::T as ComputedTransformStyle;
use style::computed_values::unicode_bidi::T as UnicodeBidi;
use style::logical_geometry::{Direction as AxisDirection, WritingMode};
use style::properties::longhands::backface_visibility::computed_value::T as BackfaceVisiblity;
use style::properties::longhands::box_sizing::computed_value::T as BoxSizing;
use style::properties::longhands::column_span::computed_value::T as ColumnSpan;
use style::properties::ComputedValues;
use style::servo::selector_parser::PseudoElement;
use style::values::computed::basic_shape::ClipPath;
use style::values::computed::image::Image as ComputedImageLayer;
use style::values::computed::{AlignItems, BorderStyle, Inset, LengthPercentage, Margin};
use style::values::generics::box_::Perspective;
use style::values::generics::position::{GenericAspectRatio, PreferredRatio};
use style::values::specified::align::AlignFlags;
use style::values::specified::{box_ as stylo, Overflow};
use style::values::CSSFloat;
use style::Zero;
use webrender_api as wr;
use crate::dom_traversal::Contents;
use crate::fragment_tree::FragmentFlags;
use crate::geom::{
AuOrAuto, LengthPercentageOrAuto, LogicalSides, LogicalVec2, PhysicalSides, PhysicalSize,
PhysicalVec, Size,
};
use crate::{ContainingBlock, IndefiniteContainingBlock};
#[derive(Clone, Copy, Eq, PartialEq)]
pub(crate) enum Display {
None,
Contents,
GeneratingBox(DisplayGeneratingBox),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum DisplayGeneratingBox {
OutsideInside {
outside: DisplayOutside,
inside: DisplayInside,
},
LayoutInternal(DisplayLayoutInternal),
}
impl DisplayGeneratingBox {
pub(crate) fn display_inside(&self) -> DisplayInside {
match *self {
DisplayGeneratingBox::OutsideInside { inside, .. } => inside,
DisplayGeneratingBox::LayoutInternal(layout_internal) => {
layout_internal.display_inside()
},
}
}
pub(crate) fn used_value_for_contents(&self, contents: &Contents) -> Self {
if matches!(self, Self::LayoutInternal(_)) && contents.is_replaced() {
Self::OutsideInside {
outside: DisplayOutside::Inline,
inside: DisplayInside::Flow {
is_list_item: false,
},
}
} else {
*self
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum DisplayOutside {
Block,
Inline,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum DisplayInside {
Flow { is_list_item: bool },
FlowRoot { is_list_item: bool },
Flex,
Table,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[allow(clippy::enum_variant_names)]
pub(crate) enum DisplayLayoutInternal {
TableCaption,
TableCell,
TableColumn,
TableColumnGroup,
TableFooterGroup,
TableHeaderGroup,
TableRow,
TableRowGroup,
}
impl DisplayLayoutInternal {
pub(crate) fn display_inside(&self) -> DisplayInside {
DisplayInside::FlowRoot {
is_list_item: false,
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct PaddingBorderMargin {
pub padding: LogicalSides<Au>,
pub border: LogicalSides<Au>,
pub margin: LogicalSides<AuOrAuto>,
pub padding_border_sums: LogicalVec2<Au>,
}
impl PaddingBorderMargin {
pub(crate) fn zero() -> Self {
Self {
padding: LogicalSides::zero(),
border: LogicalSides::zero(),
margin: LogicalSides::zero(),
padding_border_sums: LogicalVec2::zero(),
}
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct AspectRatio {
box_sizing_adjustment: LogicalVec2<Au>,
i_over_b: CSSFloat,
}
impl AspectRatio {
pub(crate) fn compute_dependent_size(
&self,
ratio_dependent_axis: AxisDirection,
ratio_determining_size: Au,
) -> Au {
match ratio_dependent_axis {
AxisDirection::Inline => {
(ratio_determining_size + self.box_sizing_adjustment.block).scale_by(self.i_over_b) -
self.box_sizing_adjustment.inline
},
AxisDirection::Block => {
(ratio_determining_size + self.box_sizing_adjustment.inline)
.scale_by(1.0 / self.i_over_b) -
self.box_sizing_adjustment.block
},
}
}
pub(crate) fn from_content_ratio(i_over_b: CSSFloat) -> Self {
Self {
box_sizing_adjustment: LogicalVec2::zero(),
i_over_b,
}
}
}
#[derive(Clone)]
pub(crate) struct ContentBoxSizesAndPBM {
pub content_box_size: LogicalVec2<Size<Au>>,
pub content_min_box_size: LogicalVec2<Size<Au>>,
pub content_max_box_size: LogicalVec2<Size<Au>>,
pub pbm: PaddingBorderMargin,
pub depends_on_block_constraints: bool,
}
impl From<ContentBoxSizesAndPBM> for ContentBoxSizesAndPBMDeprecated {
fn from(sizes: ContentBoxSizesAndPBM) -> Self {
Self {
content_box_size: sizes.content_box_size.map(Size::to_auto_or),
content_min_box_size: sizes.content_min_box_size.map(Size::to_auto_or),
content_max_box_size: sizes.content_max_box_size.map(Size::to_numeric),
pbm: sizes.pbm.clone(),
depends_on_block_constraints: sizes.depends_on_block_constraints,
}
}
}
pub(crate) struct ContentBoxSizesAndPBMDeprecated {
pub content_box_size: LogicalVec2<AuOrAuto>,
pub content_min_box_size: LogicalVec2<AuOrAuto>,
pub content_max_box_size: LogicalVec2<Option<Au>>,
pub pbm: PaddingBorderMargin,
pub depends_on_block_constraints: bool,
}
pub(crate) trait ComputedValuesExt {
fn physical_box_offsets(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>>;
fn box_offsets(&self, writing_mode: WritingMode) -> LogicalSides<LengthPercentageOrAuto<'_>>;
fn box_size(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalVec2<Size<LengthPercentage>>;
fn min_box_size(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalVec2<Size<LengthPercentage>>;
fn max_box_size(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalVec2<Size<LengthPercentage>>;
fn content_box_size(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>>;
fn content_box_size_deprecated(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<AuOrAuto>;
fn content_box_size_for_box_size(
&self,
box_size: LogicalVec2<Size<Au>>,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>>;
fn content_min_box_size(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>>;
fn content_min_box_size_deprecated(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<AuOrAuto>;
fn content_min_box_size_for_min_size(
&self,
box_size: LogicalVec2<Size<Au>>,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>>;
fn content_max_box_size(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>>;
fn content_max_box_size_deprecated(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Option<Au>>;
fn content_max_box_size_for_max_size(
&self,
box_size: LogicalVec2<Size<Au>>,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>>;
fn content_box_sizes_and_padding_border_margin(
&self,
containing_block: &IndefiniteContainingBlock,
) -> ContentBoxSizesAndPBM;
fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin;
fn padding_border_margin_for_intrinsic_size(
&self,
writing_mode: WritingMode,
) -> PaddingBorderMargin;
fn padding_border_margin_with_writing_mode_and_containing_block_inline_size(
&self,
writing_mode: WritingMode,
containing_block_inline_size: Au,
) -> PaddingBorderMargin;
fn padding(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalSides<&LengthPercentage>;
fn border_style(&self, containing_block_writing_mode: WritingMode)
-> LogicalSides<BorderStyle>;
fn border_width(&self, containing_block_writing_mode: WritingMode) -> LogicalSides<Au>;
fn physical_margin(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>>;
fn margin(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalSides<LengthPercentageOrAuto<'_>>;
fn has_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool;
fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32;
fn effective_overflow(&self) -> PhysicalVec<Overflow>;
fn establishes_block_formatting_context(&self) -> bool;
fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool;
fn establishes_scroll_container(&self) -> bool;
fn establishes_containing_block_for_absolute_descendants(
&self,
fragment_flags: FragmentFlags,
) -> bool;
fn establishes_containing_block_for_all_descendants(
&self,
fragment_flags: FragmentFlags,
) -> bool;
fn preferred_aspect_ratio(
&self,
natural_aspect_ratio: Option<CSSFloat>,
containing_block: &IndefiniteContainingBlock,
) -> Option<AspectRatio>;
fn background_is_transparent(&self) -> bool;
fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags;
fn bidi_control_chars(&self) -> (&'static str, &'static str);
fn resolve_align_self(
&self,
resolved_auto_value: AlignItems,
resolved_normal_value: AlignItems,
) -> AlignItems;
fn depends_on_block_constraints_due_to_relative_positioning(
&self,
writing_mode: WritingMode,
) -> bool;
}
impl ComputedValuesExt for ComputedValues {
fn physical_box_offsets(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>> {
fn convert(inset: &Inset) -> LengthPercentageOrAuto<'_> {
match inset {
Inset::LengthPercentage(ref v) => LengthPercentageOrAuto::LengthPercentage(v),
Inset::Auto => LengthPercentageOrAuto::Auto,
Inset::AnchorFunction(_) => unreachable!("anchor() should be disabled"),
Inset::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"),
}
}
let position = self.get_position();
PhysicalSides::new(
convert(&position.top),
convert(&position.right),
convert(&position.bottom),
convert(&position.left),
)
}
fn box_offsets(&self, writing_mode: WritingMode) -> LogicalSides<LengthPercentageOrAuto<'_>> {
LogicalSides::from_physical(&self.physical_box_offsets(), writing_mode)
}
fn box_size(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalVec2<Size<LengthPercentage>> {
let position = self.get_position();
LogicalVec2::from_physical_size(
&PhysicalSize::new(
position.clone_width().into(),
position.clone_height().into(),
),
containing_block_writing_mode,
)
}
fn min_box_size(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalVec2<Size<LengthPercentage>> {
let position = self.get_position();
LogicalVec2::from_physical_size(
&PhysicalSize::new(
position.clone_min_width().into(),
position.clone_min_height().into(),
),
containing_block_writing_mode,
)
}
fn max_box_size(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalVec2<Size<LengthPercentage>> {
let position = self.get_position();
LogicalVec2::from_physical_size(
&PhysicalSize::new(
position.clone_max_width().into(),
position.clone_max_height().into(),
),
containing_block_writing_mode,
)
}
fn content_box_size(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>> {
let box_size = self
.box_size(containing_block.style.writing_mode)
.percentages_relative_to(containing_block);
self.content_box_size_for_box_size(box_size, pbm)
}
fn content_box_size_deprecated(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<AuOrAuto> {
self.content_box_size(containing_block, pbm)
.map(Size::to_auto_or)
}
fn content_box_size_for_box_size(
&self,
box_size: LogicalVec2<Size<Au>>,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>> {
match self.get_position().box_sizing {
BoxSizing::ContentBox => box_size,
BoxSizing::BorderBox => box_size.map_inline_and_block_sizes(
|value| value - pbm.padding_border_sums.inline,
|value| value - pbm.padding_border_sums.block,
),
}
}
fn content_min_box_size(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>> {
let min_size = self
.min_box_size(containing_block.style.writing_mode)
.map_inline_and_block_sizes(
|lp| lp.to_used_value(containing_block.inline_size),
|lp| lp.to_used_value(containing_block.block_size.auto_is(Au::zero)),
);
self.content_min_box_size_for_min_size(min_size, pbm)
}
fn content_min_box_size_deprecated(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<AuOrAuto> {
self.content_min_box_size(containing_block, pbm)
.map(Size::to_auto_or)
}
fn content_min_box_size_for_min_size(
&self,
min_box_size: LogicalVec2<Size<Au>>,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>> {
match self.get_position().box_sizing {
BoxSizing::ContentBox => min_box_size,
BoxSizing::BorderBox => min_box_size.map_inline_and_block_sizes(
|value| Au::zero().max(value - pbm.padding_border_sums.inline),
|value| Au::zero().max(value - pbm.padding_border_sums.block),
),
}
}
fn content_max_box_size(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>> {
let max_box_size = self
.max_box_size(containing_block.style.writing_mode)
.percentages_relative_to(containing_block);
self.content_max_box_size_for_max_size(max_box_size, pbm)
}
fn content_max_box_size_deprecated(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Option<Au>> {
self.content_max_box_size(containing_block, pbm)
.map(Size::to_numeric)
}
fn content_max_box_size_for_max_size(
&self,
max_box_size: LogicalVec2<Size<Au>>,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>> {
match self.get_position().box_sizing {
BoxSizing::ContentBox => max_box_size,
BoxSizing::BorderBox => max_box_size.map_inline_and_block_sizes(
|value| value - pbm.padding_border_sums.inline,
|value| value - pbm.padding_border_sums.block,
),
}
}
fn content_box_sizes_and_padding_border_margin(
&self,
containing_block: &IndefiniteContainingBlock,
) -> ContentBoxSizesAndPBM {
let containing_block_size = containing_block.size.map(|value| value.non_auto());
let containing_block_size_auto_is_zero =
containing_block_size.map(|value| value.unwrap_or_else(Au::zero));
let writing_mode = containing_block.writing_mode;
let pbm = self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
writing_mode,
containing_block.size.inline.auto_is(Au::zero),
);
let box_size = self.box_size(writing_mode);
let min_size = self.min_box_size(writing_mode);
let max_size = self.max_box_size(writing_mode);
let depends_on_block_constraints = |size: &Size<LengthPercentage>| {
match size {
Size::Stretch => true,
Size::Numeric(length_percentage) => length_percentage.has_percentage(),
_ => false,
}
};
let depends_on_block_constraints = depends_on_block_constraints(&box_size.block) ||
depends_on_block_constraints(&min_size.block) ||
depends_on_block_constraints(&max_size.block) ||
self.depends_on_block_constraints_due_to_relative_positioning(writing_mode);
let box_size = box_size.maybe_percentages_relative_to_basis(&containing_block_size);
let content_box_size = self
.content_box_size_for_box_size(box_size, &pbm)
.map(|v| v.map(Au::from));
let min_size = min_size.percentages_relative_to_basis(&containing_block_size_auto_is_zero);
let content_min_box_size = self
.content_min_box_size_for_min_size(min_size, &pbm)
.map(|v| v.map(Au::from));
let max_size = max_size.maybe_percentages_relative_to_basis(&containing_block_size);
let content_max_box_size = self
.content_max_box_size_for_max_size(max_size, &pbm)
.map(|v| v.map(Au::from));
ContentBoxSizesAndPBM {
content_box_size,
content_min_box_size,
content_max_box_size,
pbm,
depends_on_block_constraints,
}
}
fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin {
self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
containing_block.style.writing_mode,
containing_block.inline_size,
)
}
fn padding_border_margin_for_intrinsic_size(
&self,
writing_mode: WritingMode,
) -> PaddingBorderMargin {
self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
writing_mode,
Au::zero(),
)
}
fn padding_border_margin_with_writing_mode_and_containing_block_inline_size(
&self,
writing_mode: WritingMode,
containing_block_inline_size: Au,
) -> PaddingBorderMargin {
let padding = self
.padding(writing_mode)
.percentages_relative_to(containing_block_inline_size);
let border = self.border_width(writing_mode);
let margin = self
.margin(writing_mode)
.percentages_relative_to(containing_block_inline_size);
PaddingBorderMargin {
padding_border_sums: LogicalVec2 {
inline: padding.inline_sum() + border.inline_sum(),
block: padding.block_sum() + border.block_sum(),
},
padding,
border,
margin,
}
}
fn padding(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalSides<&LengthPercentage> {
let padding = self.get_padding();
LogicalSides::from_physical(
&PhysicalSides::new(
&padding.padding_top.0,
&padding.padding_right.0,
&padding.padding_bottom.0,
&padding.padding_left.0,
),
containing_block_writing_mode,
)
}
fn border_style(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalSides<BorderStyle> {
let border = self.get_border();
LogicalSides::from_physical(
&PhysicalSides::new(
border.border_top_style,
border.border_right_style,
border.border_bottom_style,
border.border_left_style,
),
containing_block_writing_mode,
)
}
fn border_width(&self, containing_block_writing_mode: WritingMode) -> LogicalSides<Au> {
let border = self.get_border();
LogicalSides::from_physical(
&PhysicalSides::new(
border.border_top_width,
border.border_right_width,
border.border_bottom_width,
border.border_left_width,
),
containing_block_writing_mode,
)
}
fn physical_margin(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>> {
fn convert(inset: &Margin) -> LengthPercentageOrAuto<'_> {
match inset {
Margin::LengthPercentage(ref v) => LengthPercentageOrAuto::LengthPercentage(v),
Margin::Auto => LengthPercentageOrAuto::Auto,
Margin::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"),
}
}
let margin = self.get_margin();
PhysicalSides::new(
convert(&margin.margin_top),
convert(&margin.margin_right),
convert(&margin.margin_bottom),
convert(&margin.margin_left),
)
}
fn margin(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalSides<LengthPercentageOrAuto<'_>> {
LogicalSides::from_physical(&self.physical_margin(), containing_block_writing_mode)
}
fn has_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool {
if self.get_box().display.is_inline_flow() &&
!fragment_flags.contains(FragmentFlags::IS_REPLACED)
{
return false;
}
!self.get_box().transform.0.is_empty() || self.get_box().perspective != Perspective::None
}
fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32 {
match self.get_box().position {
ComputedPosition::Static if !fragment_flags.contains(FragmentFlags::IS_FLEX_ITEM) => 0,
_ => self.get_position().z_index.integer_or(0),
}
}
fn effective_overflow(&self) -> PhysicalVec<Overflow> {
let style_box = self.get_box();
let overflow_x = style_box.overflow_x;
let overflow_y = style_box.overflow_y;
let ignores_overflow = match style_box.display.inside() {
stylo::DisplayInside::Table => {
!matches!(self.pseudo(), Some(PseudoElement::ServoTableGrid)) ||
matches!(overflow_x, Overflow::Auto | Overflow::Scroll) ||
matches!(overflow_y, Overflow::Auto | Overflow::Scroll)
},
stylo::DisplayInside::TableColumn |
stylo::DisplayInside::TableColumnGroup |
stylo::DisplayInside::TableRow |
stylo::DisplayInside::TableRowGroup |
stylo::DisplayInside::TableHeaderGroup |
stylo::DisplayInside::TableFooterGroup => {
true
},
_ => false,
};
if ignores_overflow {
PhysicalVec::new(Overflow::Visible, Overflow::Visible)
} else {
PhysicalVec::new(overflow_x, overflow_y)
}
}
fn establishes_block_formatting_context(&self) -> bool {
if self.establishes_scroll_container() {
return true;
}
if self.get_column().is_multicol() {
return true;
}
if self.get_column().column_span == ColumnSpan::All {
return true;
}
false
}
fn establishes_scroll_container(&self) -> bool {
self.effective_overflow().x.is_scrollable()
}
fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool {
let effects = self.get_effects();
if effects.opacity != 1.0 {
return true;
}
if effects.mix_blend_mode != ComputedMixBlendMode::Normal {
return true;
}
if self.has_transform_or_perspective(fragment_flags) {
return true;
}
if !self.get_effects().filter.0.is_empty() {
return true;
}
if self.get_box().transform_style == ComputedTransformStyle::Preserve3d ||
self.overrides_transform_style()
{
return true;
}
if self.get_box().position == ComputedPosition::Fixed {
return true;
}
if self.get_svg().clip_path != ClipPath::None {
return true;
}
if self.get_box().position == ComputedPosition::Static &&
!fragment_flags.contains(FragmentFlags::IS_FLEX_ITEM)
{
return false;
}
!self.get_position().z_index.is_auto()
}
fn establishes_containing_block_for_absolute_descendants(
&self,
fragment_flags: FragmentFlags,
) -> bool {
if self.establishes_containing_block_for_all_descendants(fragment_flags) {
return true;
}
self.clone_position() != ComputedPosition::Static
}
fn establishes_containing_block_for_all_descendants(
&self,
fragment_flags: FragmentFlags,
) -> bool {
if self.has_transform_or_perspective(fragment_flags) {
return true;
}
if !self.get_effects().filter.0.is_empty() {
return true;
}
false
}
fn preferred_aspect_ratio(
&self,
natural_aspect_ratio: Option<CSSFloat>,
containing_block: &IndefiniteContainingBlock,
) -> Option<AspectRatio> {
let GenericAspectRatio {
auto,
ratio: mut preferred_ratio,
} = self.clone_aspect_ratio();
if matches!(preferred_ratio, PreferredRatio::Ratio(ratio) if ratio.is_degenerate()) {
preferred_ratio = PreferredRatio::None;
}
match (auto, preferred_ratio) {
(_, PreferredRatio::None) => natural_aspect_ratio.map(AspectRatio::from_content_ratio),
(true, PreferredRatio::Ratio(preferred_ratio)) => {
Some(AspectRatio::from_content_ratio(
natural_aspect_ratio
.unwrap_or_else(|| (preferred_ratio.0).0 / (preferred_ratio.1).0),
))
},
(false, PreferredRatio::Ratio(preferred_ratio)) => {
let box_sizing_adjustment = match self.clone_box_sizing() {
BoxSizing::ContentBox => LogicalVec2::zero(),
BoxSizing::BorderBox => containing_block.size.inline.non_auto().map_or_else(
|| self.padding_border_margin_for_intrinsic_size(containing_block.writing_mode),
|containing_block_inline_size| self
.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
containing_block.writing_mode,
containing_block_inline_size,
)
)
.padding_border_sums,
};
Some(AspectRatio {
i_over_b: (preferred_ratio.0).0 / (preferred_ratio.1).0,
box_sizing_adjustment,
})
},
}
}
fn background_is_transparent(&self) -> bool {
let background = self.get_background();
let color = self.resolve_color(background.background_color.clone());
color.alpha == 0.0 &&
background
.background_image
.0
.iter()
.all(|layer| matches!(layer, ComputedImageLayer::None))
}
fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags {
match self.get_box().backface_visibility {
BackfaceVisiblity::Visible => wr::PrimitiveFlags::default(),
BackfaceVisiblity::Hidden => wr::PrimitiveFlags::empty(),
}
}
fn bidi_control_chars(&self) -> (&'static str, &'static str) {
match (
self.get_text().unicode_bidi,
self.get_inherited_box().direction,
) {
(UnicodeBidi::Normal, _) => ("", ""),
(UnicodeBidi::Embed, Direction::Ltr) => ("\u{202a}", "\u{202c}"),
(UnicodeBidi::Embed, Direction::Rtl) => ("\u{202b}", "\u{202c}"),
(UnicodeBidi::Isolate, Direction::Ltr) => ("\u{2066}", "\u{2069}"),
(UnicodeBidi::Isolate, Direction::Rtl) => ("\u{2067}", "\u{2069}"),
(UnicodeBidi::BidiOverride, Direction::Ltr) => ("\u{202d}", "\u{202c}"),
(UnicodeBidi::BidiOverride, Direction::Rtl) => ("\u{202e}", "\u{202c}"),
(UnicodeBidi::IsolateOverride, Direction::Ltr) => {
("\u{2068}\u{202d}", "\u{202c}\u{2069}")
},
(UnicodeBidi::IsolateOverride, Direction::Rtl) => {
("\u{2068}\u{202e}", "\u{202c}\u{2069}")
},
(UnicodeBidi::Plaintext, _) => ("\u{2068}", "\u{2069}"),
}
}
fn resolve_align_self(
&self,
resolved_auto_value: AlignItems,
resolved_normal_value: AlignItems,
) -> AlignItems {
match self.clone_align_self().0 .0 {
AlignFlags::AUTO => resolved_auto_value,
AlignFlags::NORMAL => resolved_normal_value,
value => AlignItems(value),
}
}
fn depends_on_block_constraints_due_to_relative_positioning(
&self,
writing_mode: WritingMode,
) -> bool {
if !matches!(
self.get_box().position,
ComputedPosition::Relative | ComputedPosition::Sticky
) {
return false;
}
let box_offsets = self.box_offsets(writing_mode);
let has_percentage = |offset: LengthPercentageOrAuto<'_>| {
offset
.non_auto()
.is_some_and(LengthPercentage::has_percentage)
};
has_percentage(box_offsets.block_start) || has_percentage(box_offsets.block_end)
}
}
impl From<stylo::Display> for Display {
fn from(packed: stylo::Display) -> Self {
let outside = packed.outside();
let inside = packed.inside();
let outside = match outside {
stylo::DisplayOutside::Block => DisplayOutside::Block,
stylo::DisplayOutside::Inline => DisplayOutside::Inline,
stylo::DisplayOutside::TableCaption => {
return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(
DisplayLayoutInternal::TableCaption,
));
},
stylo::DisplayOutside::InternalTable => {
let internal = match inside {
stylo::DisplayInside::TableRowGroup => DisplayLayoutInternal::TableRowGroup,
stylo::DisplayInside::TableColumn => DisplayLayoutInternal::TableColumn,
stylo::DisplayInside::TableColumnGroup => {
DisplayLayoutInternal::TableColumnGroup
},
stylo::DisplayInside::TableHeaderGroup => {
DisplayLayoutInternal::TableHeaderGroup
},
stylo::DisplayInside::TableFooterGroup => {
DisplayLayoutInternal::TableFooterGroup
},
stylo::DisplayInside::TableRow => DisplayLayoutInternal::TableRow,
stylo::DisplayInside::TableCell => DisplayLayoutInternal::TableCell,
_ => unreachable!("Non-internal DisplayInside found"),
};
return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(internal));
},
stylo::DisplayOutside::None if inside == stylo::DisplayInside::Contents => {
return Display::Contents
},
stylo::DisplayOutside::None => return Display::None,
};
let inside = match packed.inside() {
stylo::DisplayInside::Flow => DisplayInside::Flow {
is_list_item: packed.is_list_item(),
},
stylo::DisplayInside::FlowRoot => DisplayInside::FlowRoot {
is_list_item: packed.is_list_item(),
},
stylo::DisplayInside::Flex => DisplayInside::Flex,
stylo::DisplayInside::Grid => todo!("Grid support is not yet implemented."),
stylo::DisplayInside::None => return Display::None,
stylo::DisplayInside::Contents => return Display::Contents,
stylo::DisplayInside::Table => DisplayInside::Table,
stylo::DisplayInside::TableRowGroup |
stylo::DisplayInside::TableColumn |
stylo::DisplayInside::TableColumnGroup |
stylo::DisplayInside::TableHeaderGroup |
stylo::DisplayInside::TableFooterGroup |
stylo::DisplayInside::TableRow |
stylo::DisplayInside::TableCell => unreachable!("Internal DisplayInside found"),
};
Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { outside, inside })
}
}
pub(crate) trait Clamp: Sized {
fn clamp_below_max(self, max: Option<Self>) -> Self;
fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self;
}
impl Clamp for Au {
fn clamp_below_max(self, max: Option<Self>) -> Self {
match max {
None => self,
Some(max) => self.min(max),
}
}
fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self {
self.clamp_below_max(max).max(min)
}
}