mirror of
https://github.com/servo/servo.git
synced 2025-06-11 01:50:10 +00:00
At the root of an inline formatting context, we used its vertical-align in order to compute the strut. That was wrong, since vertical-align on a block container shouldn't affect the contents, it should only affect the alignment of the block container (if it's inline-level) within the parent IFC. This was only working well if the block container was block-level, since effective_vertical_align_for_inline_layout returned `baseline` for block-level boxes. Instead of the outer display type, this patch changes the logic to check whether we are at the root of the IFC.
647 lines
24 KiB
Rust
647 lines
24 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
use app_units::Au;
|
|
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::logical_geometry::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::values::computed::image::Image as ComputedImageLayer;
|
|
use style::values::computed::{Length, LengthPercentage, NonNegativeLengthPercentage, Size};
|
|
use style::values::generics::box_::Perspective;
|
|
use style::values::generics::length::MaxSize;
|
|
use style::values::specified::{box_ as stylo, Overflow};
|
|
use style::Zero;
|
|
use webrender_api as wr;
|
|
|
|
use crate::dom_traversal::Contents;
|
|
use crate::geom::{
|
|
AuOrAuto, LengthOrAuto, LengthPercentageOrAuto, LogicalSides, LogicalVec2, PhysicalSides,
|
|
PhysicalSize,
|
|
};
|
|
use crate::ContainingBlock;
|
|
|
|
#[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,
|
|
},
|
|
/// <https://drafts.csswg.org/css-display-3/#layout-specific-display>
|
|
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 {
|
|
// From <https://www.w3.org/TR/css-display-3/#layout-specific-display>:
|
|
// > When the display property of a replaced element computes to one of
|
|
// > the layout-internal values, it is handled as having a used value of
|
|
// > inline.
|
|
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 {
|
|
// “list-items are limited to the Flow Layout display types”
|
|
// <https://drafts.csswg.org/css-display/#list-items>
|
|
Flow { is_list_item: bool },
|
|
FlowRoot { is_list_item: bool },
|
|
Flex,
|
|
Table,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
#[allow(clippy::enum_variant_names)]
|
|
/// <https://drafts.csswg.org/css-display-3/#layout-specific-display>
|
|
pub(crate) enum DisplayLayoutInternal {
|
|
TableCaption,
|
|
TableCell,
|
|
TableColumn,
|
|
TableColumnGroup,
|
|
TableFooterGroup,
|
|
TableHeaderGroup,
|
|
TableRow,
|
|
TableRowGroup,
|
|
}
|
|
|
|
impl DisplayLayoutInternal {
|
|
/// <https://drafts.csswg.org/css-display-3/#layout-specific-displa>
|
|
pub(crate) fn display_inside(&self) -> DisplayInside {
|
|
// When we add ruby, the display_inside of ruby must be Flow.
|
|
// TODO: this should be unreachable for everything but
|
|
// table cell and caption, once we have box tree fixups.
|
|
DisplayInside::FlowRoot {
|
|
is_list_item: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Percentages resolved but not `auto` margins
|
|
#[derive(Clone)]
|
|
pub(crate) struct PaddingBorderMargin {
|
|
pub padding: LogicalSides<Au>,
|
|
pub border: LogicalSides<Au>,
|
|
pub margin: LogicalSides<AuOrAuto>,
|
|
|
|
/// Pre-computed sums in each axis
|
|
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(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) trait ComputedValuesExt {
|
|
fn inline_size_is_length(&self) -> bool;
|
|
fn inline_box_offsets_are_both_non_auto(&self) -> bool;
|
|
fn box_offsets(
|
|
&self,
|
|
containing_block: &ContainingBlock,
|
|
) -> LogicalSides<LengthPercentageOrAuto<'_>>;
|
|
fn box_size(
|
|
&self,
|
|
containing_block_writing_mode: WritingMode,
|
|
) -> LogicalVec2<LengthPercentageOrAuto<'_>>;
|
|
fn min_box_size(
|
|
&self,
|
|
containing_block_writing_mode: WritingMode,
|
|
) -> LogicalVec2<LengthPercentageOrAuto<'_>>;
|
|
fn max_box_size(
|
|
&self,
|
|
containing_block_writing_mode: WritingMode,
|
|
) -> LogicalVec2<Option<&LengthPercentage>>;
|
|
fn content_box_size(
|
|
&self,
|
|
containing_block: &ContainingBlock,
|
|
pbm: &PaddingBorderMargin,
|
|
) -> LogicalVec2<LengthOrAuto>;
|
|
fn content_min_box_size(
|
|
&self,
|
|
containing_block: &ContainingBlock,
|
|
pbm: &PaddingBorderMargin,
|
|
) -> LogicalVec2<LengthOrAuto>;
|
|
fn content_max_box_size(
|
|
&self,
|
|
containing_block: &ContainingBlock,
|
|
pbm: &PaddingBorderMargin,
|
|
) -> LogicalVec2<Option<Length>>;
|
|
fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin;
|
|
fn padding(
|
|
&self,
|
|
containing_block_writing_mode: WritingMode,
|
|
) -> LogicalSides<&LengthPercentage>;
|
|
fn border_width(&self, containing_block_writing_mode: WritingMode) -> LogicalSides<Length>;
|
|
fn margin(
|
|
&self,
|
|
containing_block_writing_mode: WritingMode,
|
|
) -> LogicalSides<LengthPercentageOrAuto<'_>>;
|
|
fn has_transform_or_perspective(&self) -> bool;
|
|
fn effective_z_index(&self) -> i32;
|
|
fn establishes_block_formatting_context(&self) -> bool;
|
|
fn establishes_stacking_context(&self) -> bool;
|
|
fn establishes_scroll_container(&self) -> bool;
|
|
fn establishes_containing_block_for_absolute_descendants(&self) -> bool;
|
|
fn establishes_containing_block_for_all_descendants(&self) -> bool;
|
|
fn background_is_transparent(&self) -> bool;
|
|
fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags;
|
|
}
|
|
|
|
impl ComputedValuesExt for ComputedValues {
|
|
fn inline_size_is_length(&self) -> bool {
|
|
let position = self.get_position();
|
|
// FIXME: this is the wrong writing mode but we plan to remove eager content size computation.
|
|
let size = if self.writing_mode.is_horizontal() {
|
|
&position.width
|
|
} else {
|
|
&position.height
|
|
};
|
|
matches!(size, Size::LengthPercentage(lp) if lp.0.to_length().is_some())
|
|
}
|
|
|
|
fn inline_box_offsets_are_both_non_auto(&self) -> bool {
|
|
let position = self.get_position();
|
|
// FIXME: this is the wrong writing mode but we plan to remove eager content size computation.
|
|
let (a, b) = if self.writing_mode.is_horizontal() {
|
|
(&position.left, &position.right)
|
|
} else {
|
|
(&position.top, &position.bottom)
|
|
};
|
|
!a.is_auto() && !b.is_auto()
|
|
}
|
|
|
|
fn box_offsets(
|
|
&self,
|
|
containing_block: &ContainingBlock,
|
|
) -> LogicalSides<LengthPercentageOrAuto<'_>> {
|
|
let position = self.get_position();
|
|
LogicalSides::from_physical(
|
|
&PhysicalSides::new(
|
|
position.top.as_ref(),
|
|
position.right.as_ref(),
|
|
position.bottom.as_ref(),
|
|
position.left.as_ref(),
|
|
),
|
|
containing_block.style.writing_mode,
|
|
)
|
|
}
|
|
|
|
fn box_size(
|
|
&self,
|
|
containing_block_writing_mode: WritingMode,
|
|
) -> LogicalVec2<LengthPercentageOrAuto<'_>> {
|
|
let position = self.get_position();
|
|
LogicalVec2::from_physical_size(
|
|
&PhysicalSize::new(
|
|
size_to_length(&position.width),
|
|
size_to_length(&position.height),
|
|
),
|
|
containing_block_writing_mode,
|
|
)
|
|
}
|
|
|
|
fn min_box_size(
|
|
&self,
|
|
containing_block_writing_mode: WritingMode,
|
|
) -> LogicalVec2<LengthPercentageOrAuto<'_>> {
|
|
let position = self.get_position();
|
|
LogicalVec2::from_physical_size(
|
|
&PhysicalSize::new(
|
|
size_to_length(&position.min_width),
|
|
size_to_length(&position.min_height),
|
|
),
|
|
containing_block_writing_mode,
|
|
)
|
|
}
|
|
|
|
fn max_box_size(
|
|
&self,
|
|
containing_block_writing_mode: WritingMode,
|
|
) -> LogicalVec2<Option<&LengthPercentage>> {
|
|
fn unwrap(max_size: &MaxSize<NonNegativeLengthPercentage>) -> Option<&LengthPercentage> {
|
|
match max_size {
|
|
MaxSize::LengthPercentage(length) => Some(&length.0),
|
|
MaxSize::None => None,
|
|
}
|
|
}
|
|
let position = self.get_position();
|
|
LogicalVec2::from_physical_size(
|
|
&PhysicalSize::new(unwrap(&position.max_width), unwrap(&position.max_height)),
|
|
containing_block_writing_mode,
|
|
)
|
|
}
|
|
|
|
fn content_box_size(
|
|
&self,
|
|
containing_block: &ContainingBlock,
|
|
pbm: &PaddingBorderMargin,
|
|
) -> LogicalVec2<LengthOrAuto> {
|
|
let box_size = self
|
|
.box_size(containing_block.style.writing_mode)
|
|
.percentages_relative_to(containing_block);
|
|
match self.get_position().box_sizing {
|
|
BoxSizing::ContentBox => box_size,
|
|
BoxSizing::BorderBox => LogicalVec2 {
|
|
// These may be negative, but will later be clamped by `min-width`/`min-height`
|
|
// which is clamped to zero.
|
|
inline: box_size
|
|
.inline
|
|
.map(|i| i - pbm.padding_border_sums.inline.into()),
|
|
block: box_size
|
|
.block
|
|
.map(|b| b - pbm.padding_border_sums.block.into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
fn content_min_box_size(
|
|
&self,
|
|
containing_block: &ContainingBlock,
|
|
pbm: &PaddingBorderMargin,
|
|
) -> LogicalVec2<LengthOrAuto> {
|
|
let min_box_size = self
|
|
.min_box_size(containing_block.style.writing_mode)
|
|
.percentages_relative_to(containing_block);
|
|
match self.get_position().box_sizing {
|
|
BoxSizing::ContentBox => min_box_size,
|
|
BoxSizing::BorderBox => LogicalVec2 {
|
|
// Clamp to zero to make sure the used size components are non-negative
|
|
inline: min_box_size
|
|
.inline
|
|
.map(|i| (i - pbm.padding_border_sums.inline.into()).max(Length::zero())),
|
|
block: min_box_size
|
|
.block
|
|
.map(|b| (b - pbm.padding_border_sums.block.into()).max(Length::zero())),
|
|
},
|
|
}
|
|
}
|
|
|
|
fn content_max_box_size(
|
|
&self,
|
|
containing_block: &ContainingBlock,
|
|
pbm: &PaddingBorderMargin,
|
|
) -> LogicalVec2<Option<Length>> {
|
|
let max_box_size = self
|
|
.max_box_size(containing_block.style.writing_mode)
|
|
.percentages_relative_to(containing_block);
|
|
match self.get_position().box_sizing {
|
|
BoxSizing::ContentBox => max_box_size,
|
|
BoxSizing::BorderBox => {
|
|
// This may be negative, but will later be clamped by `min-width`
|
|
// which itself is clamped to zero.
|
|
LogicalVec2 {
|
|
inline: max_box_size
|
|
.inline
|
|
.map(|i| i - pbm.padding_border_sums.inline.into()),
|
|
block: max_box_size
|
|
.block
|
|
.map(|b| b - pbm.padding_border_sums.block.into()),
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin {
|
|
let cbis = containing_block.inline_size;
|
|
let padding = self
|
|
.padding(containing_block.style.writing_mode)
|
|
.percentages_relative_to(cbis.into());
|
|
let border = self.border_width(containing_block.style.writing_mode);
|
|
let margin = self
|
|
.margin(containing_block.style.writing_mode)
|
|
.percentages_relative_to(cbis.into());
|
|
PaddingBorderMargin {
|
|
padding_border_sums: LogicalVec2 {
|
|
inline: (padding.inline_sum() + border.inline_sum()).into(),
|
|
block: (padding.block_sum() + border.block_sum()).into(),
|
|
},
|
|
padding: padding.into(),
|
|
border: border.into(),
|
|
margin: margin.map(|t| t.map(|m| m.into())),
|
|
}
|
|
}
|
|
|
|
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_width(&self, containing_block_writing_mode: WritingMode) -> LogicalSides<Length> {
|
|
let border = self.get_border();
|
|
LogicalSides::from_physical(
|
|
&PhysicalSides::new(
|
|
border.border_top_width.into(),
|
|
border.border_right_width.into(),
|
|
border.border_bottom_width.into(),
|
|
border.border_left_width.into(),
|
|
),
|
|
containing_block_writing_mode,
|
|
)
|
|
}
|
|
|
|
fn margin(
|
|
&self,
|
|
containing_block_writing_mode: WritingMode,
|
|
) -> LogicalSides<LengthPercentageOrAuto<'_>> {
|
|
let margin = self.get_margin();
|
|
LogicalSides::from_physical(
|
|
&PhysicalSides::new(
|
|
margin.margin_top.as_ref(),
|
|
margin.margin_right.as_ref(),
|
|
margin.margin_bottom.as_ref(),
|
|
margin.margin_left.as_ref(),
|
|
),
|
|
containing_block_writing_mode,
|
|
)
|
|
}
|
|
|
|
/// Returns true if this style has a transform, or perspective property set and
|
|
/// it applies to this element.
|
|
fn has_transform_or_perspective(&self) -> bool {
|
|
// "A transformable element is an element in one of these categories:
|
|
// * all elements whose layout is governed by the CSS box model except for
|
|
// non-replaced inline boxes, table-column boxes, and table-column-group
|
|
// boxes,
|
|
// * all SVG paint server elements, the clipPath element and SVG renderable
|
|
// elements with the exception of any descendant element of text content
|
|
// elements."
|
|
// https://drafts.csswg.org/css-transforms/#transformable-element
|
|
// FIXME(mrobinson): Properly handle tables and replaced elements here.
|
|
if self.get_box().display.is_inline_flow() {
|
|
return false;
|
|
}
|
|
|
|
!self.get_box().transform.0.is_empty() || self.get_box().perspective != Perspective::None
|
|
}
|
|
|
|
/// Get the effective z-index of this fragment. Z-indices only apply to positioned elements
|
|
/// per CSS 2 9.9.1 (<http://www.w3.org/TR/CSS2/visuren.html#z-index>), so this value may differ
|
|
/// from the value specified in the style.
|
|
fn effective_z_index(&self) -> i32 {
|
|
match self.get_box().position {
|
|
ComputedPosition::Static => 0,
|
|
_ => self.get_position().z_index.integer_or(0),
|
|
}
|
|
}
|
|
|
|
/// Return true if this style is a normal block and establishes
|
|
/// a new block formatting context.
|
|
fn establishes_block_formatting_context(&self) -> bool {
|
|
if self.get_box().overflow_x.is_scrollable() {
|
|
return true;
|
|
}
|
|
|
|
if self.get_column().is_multicol() {
|
|
return true;
|
|
}
|
|
|
|
if self.get_column().column_span == ColumnSpan::All {
|
|
return true;
|
|
}
|
|
|
|
// TODO: We need to handle CSS Contain here.
|
|
false
|
|
}
|
|
|
|
/// Whether or not the `overflow` value of this style establishes a scroll container.
|
|
fn establishes_scroll_container(&self) -> bool {
|
|
self.get_box().overflow_x != Overflow::Visible ||
|
|
self.get_box().overflow_y != Overflow::Visible
|
|
}
|
|
|
|
/// Returns true if this fragment establishes a new stacking context and false otherwise.
|
|
fn establishes_stacking_context(&self) -> 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() {
|
|
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;
|
|
}
|
|
|
|
// Fixed position and sticky position always create stacking contexts.
|
|
// TODO(mrobinson): We need to handle sticky positioning here when we support it.
|
|
if self.get_box().position == ComputedPosition::Fixed {
|
|
return true;
|
|
}
|
|
|
|
// Statically positioned fragments don't establish stacking contexts if the previous
|
|
// conditions are not fulfilled. Furthermore, z-index doesn't apply to statically
|
|
// positioned fragments.
|
|
if self.get_box().position == ComputedPosition::Static {
|
|
return false;
|
|
}
|
|
|
|
// For absolutely and relatively positioned fragments we only establish a stacking
|
|
// context if there is a z-index set.
|
|
// See https://www.w3.org/TR/CSS2/visuren.html#z-index
|
|
!self.get_position().z_index.is_auto()
|
|
}
|
|
|
|
/// Returns true if this style establishes a containing block for absolute
|
|
/// descendants (`position: absolute`). If this style happens to establish a
|
|
/// containing block for “all descendants” (ie including `position: fixed`
|
|
/// descendants) this method will return true, but a true return value does
|
|
/// not imply that the style establishes a containing block for all descendants.
|
|
/// Use `establishes_containing_block_for_all_descendants()` instead.
|
|
fn establishes_containing_block_for_absolute_descendants(&self) -> bool {
|
|
if self.establishes_containing_block_for_all_descendants() {
|
|
return true;
|
|
}
|
|
|
|
self.clone_position() != ComputedPosition::Static
|
|
}
|
|
|
|
/// Returns true if this style establishes a containing block for
|
|
/// all descendants, including fixed descendants (`position: fixed`).
|
|
/// Note that this also implies that it establishes a containing block
|
|
/// for absolute descendants (`position: absolute`).
|
|
fn establishes_containing_block_for_all_descendants(&self) -> bool {
|
|
if self.has_transform_or_perspective() {
|
|
return true;
|
|
}
|
|
|
|
if !self.get_effects().filter.0.is_empty() {
|
|
return true;
|
|
}
|
|
|
|
// TODO: We need to handle CSS Contain here.
|
|
false
|
|
}
|
|
|
|
/// Whether or not this style specifies a non-transparent background.
|
|
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))
|
|
}
|
|
|
|
/// Generate appropriate WebRender `PrimitiveFlags` that should be used
|
|
/// for display items generated by the `Fragment` which owns this style.
|
|
fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags {
|
|
match self.get_box().backface_visibility {
|
|
BackfaceVisiblity::Visible => wr::PrimitiveFlags::default(),
|
|
BackfaceVisiblity::Hidden => wr::PrimitiveFlags::empty(),
|
|
}
|
|
}
|
|
}
|
|
|
|
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));
|
|
},
|
|
// This should not be a value of DisplayInside, but oh well
|
|
// special-case display: contents because we still want it to work despite the early return
|
|
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,
|
|
|
|
// These should not be values of DisplayInside, but oh well
|
|
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 })
|
|
}
|
|
}
|
|
|
|
fn size_to_length(size: &Size) -> LengthPercentageOrAuto {
|
|
match size {
|
|
Size::LengthPercentage(length) => LengthPercentageOrAuto::LengthPercentage(&length.0),
|
|
Size::Auto => LengthPercentageOrAuto::Auto,
|
|
}
|
|
}
|
|
|
|
pub(crate) trait Clamp: Sized {
|
|
fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self;
|
|
}
|
|
|
|
impl Clamp for Au {
|
|
fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self {
|
|
match max {
|
|
Some(max_value) => self.min(max_value).max(min),
|
|
None => self.max(min),
|
|
}
|
|
}
|
|
}
|