mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
* Upgrade Stylo to 2024-10-04 Signed-off-by: Oriol Brufau <obrufau@igalia.com> * Fixup for https://phabricator.services.mozilla.com/D220285 Signed-off-by: Oriol Brufau <obrufau@igalia.com> * Fixup for https://bugzilla.mozilla.org/show_bug.cgi?id=1918093 Signed-off-by: Oriol Brufau <obrufau@igalia.com> * Fixup for https://phabricator.services.mozilla.com/D222817 Signed-off-by: Oriol Brufau <obrufau@igalia.com> * Fixup for https://phabricator.services.mozilla.com/D222856 Signed-off-by: Oriol Brufau <obrufau@igalia.com> * Fixup for https://phabricator.services.mozilla.com/D222532 Signed-off-by: Oriol Brufau <obrufau@igalia.com> * Fixup for https://phabricator.services.mozilla.com/D222533 Signed-off-by: Oriol Brufau <obrufau@igalia.com> * Fixup for https://phabricator.services.mozilla.com/D222534 Signed-off-by: Oriol Brufau <obrufau@igalia.com> * Fixup for https://phabricator.services.mozilla.com/D223878 Signed-off-by: Oriol Brufau <obrufau@igalia.com> --------- Signed-off-by: Oriol Brufau <obrufau@igalia.com>
641 lines
23 KiB
Rust
641 lines
23 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/. */
|
||
|
||
//! Borders, padding, and margins.
|
||
|
||
use std::cmp::{max, min};
|
||
use std::fmt;
|
||
|
||
use app_units::Au;
|
||
use euclid::SideOffsets2D;
|
||
use serde::Serialize;
|
||
use style::logical_geometry::{LogicalMargin, WritingMode};
|
||
use style::properties::ComputedValues;
|
||
use style::values::computed::{Inset, LengthPercentageOrAuto, Margin, MaxSize, Size};
|
||
|
||
use crate::fragment::Fragment;
|
||
|
||
/// A collapsible margin. See CSS 2.1 § 8.3.1.
|
||
#[derive(Clone, Copy, Debug)]
|
||
pub struct AdjoiningMargins {
|
||
/// The value of the greatest positive margin.
|
||
pub most_positive: Au,
|
||
|
||
/// The actual value (not the absolute value) of the negative margin with the largest absolute
|
||
/// value. Since this is not the absolute value, this is always zero or negative.
|
||
pub most_negative: Au,
|
||
}
|
||
|
||
impl AdjoiningMargins {
|
||
pub fn new() -> AdjoiningMargins {
|
||
AdjoiningMargins {
|
||
most_positive: Au(0),
|
||
most_negative: Au(0),
|
||
}
|
||
}
|
||
|
||
pub fn from_margin(margin_value: Au) -> AdjoiningMargins {
|
||
if margin_value >= Au(0) {
|
||
AdjoiningMargins {
|
||
most_positive: margin_value,
|
||
most_negative: Au(0),
|
||
}
|
||
} else {
|
||
AdjoiningMargins {
|
||
most_positive: Au(0),
|
||
most_negative: margin_value,
|
||
}
|
||
}
|
||
}
|
||
|
||
pub fn union(&mut self, other: AdjoiningMargins) {
|
||
self.most_positive = max(self.most_positive, other.most_positive);
|
||
self.most_negative = min(self.most_negative, other.most_negative)
|
||
}
|
||
|
||
pub fn collapse(&self) -> Au {
|
||
self.most_positive + self.most_negative
|
||
}
|
||
}
|
||
|
||
impl Default for AdjoiningMargins {
|
||
fn default() -> Self {
|
||
Self::new()
|
||
}
|
||
}
|
||
|
||
/// Represents the block-start and block-end margins of a flow with collapsible margins. See CSS 2.1 § 8.3.1.
|
||
#[derive(Clone, Copy, Debug)]
|
||
pub enum CollapsibleMargins {
|
||
/// Margins may not collapse with this flow.
|
||
None(Au, Au),
|
||
|
||
/// Both the block-start and block-end margins (specified here in that order) may collapse, but the
|
||
/// margins do not collapse through this flow.
|
||
Collapse(AdjoiningMargins, AdjoiningMargins),
|
||
|
||
/// Margins collapse *through* this flow. This means, essentially, that the flow doesn’t
|
||
/// have any border, padding, or out-of-flow (floating or positioned) content
|
||
CollapseThrough(AdjoiningMargins),
|
||
}
|
||
|
||
impl CollapsibleMargins {
|
||
pub fn new() -> CollapsibleMargins {
|
||
CollapsibleMargins::None(Au(0), Au(0))
|
||
}
|
||
|
||
/// Returns the amount of margin that should be applied in a noncollapsible context. This is
|
||
/// currently used to apply block-start margin for hypothetical boxes, since we do not collapse
|
||
/// margins of hypothetical boxes.
|
||
pub fn block_start_margin_for_noncollapsible_context(&self) -> Au {
|
||
match *self {
|
||
CollapsibleMargins::None(block_start, _) => block_start,
|
||
CollapsibleMargins::Collapse(ref block_start, _) |
|
||
CollapsibleMargins::CollapseThrough(ref block_start) => block_start.collapse(),
|
||
}
|
||
}
|
||
|
||
pub fn block_end_margin_for_noncollapsible_context(&self) -> Au {
|
||
match *self {
|
||
CollapsibleMargins::None(_, block_end) => block_end,
|
||
CollapsibleMargins::Collapse(_, ref block_end) |
|
||
CollapsibleMargins::CollapseThrough(ref block_end) => block_end.collapse(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl Default for CollapsibleMargins {
|
||
fn default() -> Self {
|
||
Self::new()
|
||
}
|
||
}
|
||
|
||
enum FinalMarginState {
|
||
MarginsCollapseThrough,
|
||
BottomMarginCollapses,
|
||
}
|
||
|
||
pub struct MarginCollapseInfo {
|
||
pub state: MarginCollapseState,
|
||
pub block_start_margin: AdjoiningMargins,
|
||
pub margin_in: AdjoiningMargins,
|
||
}
|
||
|
||
impl MarginCollapseInfo {
|
||
pub fn initialize_block_start_margin(
|
||
fragment: &Fragment,
|
||
can_collapse_block_start_margin_with_kids: bool,
|
||
) -> MarginCollapseInfo {
|
||
MarginCollapseInfo {
|
||
state: if can_collapse_block_start_margin_with_kids {
|
||
MarginCollapseState::AccumulatingCollapsibleTopMargin
|
||
} else {
|
||
MarginCollapseState::AccumulatingMarginIn
|
||
},
|
||
block_start_margin: AdjoiningMargins::from_margin(fragment.margin.block_start),
|
||
margin_in: AdjoiningMargins::new(),
|
||
}
|
||
}
|
||
|
||
pub fn finish_and_compute_collapsible_margins(
|
||
mut self,
|
||
fragment: &Fragment,
|
||
containing_block_size: Option<Au>,
|
||
can_collapse_block_end_margin_with_kids: bool,
|
||
mut may_collapse_through: bool,
|
||
) -> (CollapsibleMargins, Au) {
|
||
let state = match self.state {
|
||
MarginCollapseState::AccumulatingCollapsibleTopMargin => {
|
||
let content_block_size = fragment.style().content_block_size();
|
||
may_collapse_through = may_collapse_through &&
|
||
content_block_size.is_definitely_zero() ||
|
||
content_block_size
|
||
.maybe_to_used_value(containing_block_size)
|
||
.is_none();
|
||
|
||
if may_collapse_through {
|
||
if fragment.style.min_block_size().is_auto() ||
|
||
fragment.style().min_block_size().is_definitely_zero()
|
||
{
|
||
FinalMarginState::MarginsCollapseThrough
|
||
} else {
|
||
// If the fragment has non-zero min-block-size, margins may not
|
||
// collapse through it.
|
||
FinalMarginState::BottomMarginCollapses
|
||
}
|
||
} else {
|
||
// If the fragment has an explicitly specified block-size, margins may not
|
||
// collapse through it.
|
||
FinalMarginState::BottomMarginCollapses
|
||
}
|
||
},
|
||
MarginCollapseState::AccumulatingMarginIn => FinalMarginState::BottomMarginCollapses,
|
||
};
|
||
|
||
// Different logic is needed here depending on whether this flow can collapse its block-end
|
||
// margin with its children.
|
||
let block_end_margin = fragment.margin.block_end;
|
||
if !can_collapse_block_end_margin_with_kids {
|
||
match state {
|
||
FinalMarginState::MarginsCollapseThrough => {
|
||
let advance = self.block_start_margin.collapse();
|
||
self.margin_in
|
||
.union(AdjoiningMargins::from_margin(block_end_margin));
|
||
(
|
||
CollapsibleMargins::Collapse(self.block_start_margin, self.margin_in),
|
||
advance,
|
||
)
|
||
},
|
||
FinalMarginState::BottomMarginCollapses => {
|
||
let advance = self.margin_in.collapse();
|
||
self.margin_in
|
||
.union(AdjoiningMargins::from_margin(block_end_margin));
|
||
(
|
||
CollapsibleMargins::Collapse(self.block_start_margin, self.margin_in),
|
||
advance,
|
||
)
|
||
},
|
||
}
|
||
} else {
|
||
match state {
|
||
FinalMarginState::MarginsCollapseThrough => {
|
||
self.block_start_margin
|
||
.union(AdjoiningMargins::from_margin(block_end_margin));
|
||
(
|
||
CollapsibleMargins::CollapseThrough(self.block_start_margin),
|
||
Au(0),
|
||
)
|
||
},
|
||
FinalMarginState::BottomMarginCollapses => {
|
||
self.margin_in
|
||
.union(AdjoiningMargins::from_margin(block_end_margin));
|
||
(
|
||
CollapsibleMargins::Collapse(self.block_start_margin, self.margin_in),
|
||
Au(0),
|
||
)
|
||
},
|
||
}
|
||
}
|
||
}
|
||
|
||
pub fn current_float_ceiling(&mut self) -> Au {
|
||
match self.state {
|
||
MarginCollapseState::AccumulatingCollapsibleTopMargin => {
|
||
// We do not include the top margin in the float ceiling, because the float flow
|
||
// needs to be positioned relative to our *border box*, not our margin box. See
|
||
// `tests/ref/float_under_top_margin_a.html`.
|
||
Au(0)
|
||
},
|
||
MarginCollapseState::AccumulatingMarginIn => self.margin_in.collapse(),
|
||
}
|
||
}
|
||
|
||
/// Adds the child's potentially collapsible block-start margin to the current margin state and
|
||
/// advances the Y offset by the appropriate amount to handle that margin. Returns the amount
|
||
/// that should be added to the Y offset during block layout.
|
||
pub fn advance_block_start_margin(
|
||
&mut self,
|
||
child_collapsible_margins: &CollapsibleMargins,
|
||
can_collapse_block_start_margin: bool,
|
||
) -> Au {
|
||
if !can_collapse_block_start_margin {
|
||
self.state = MarginCollapseState::AccumulatingMarginIn
|
||
}
|
||
|
||
match (self.state, *child_collapsible_margins) {
|
||
(
|
||
MarginCollapseState::AccumulatingCollapsibleTopMargin,
|
||
CollapsibleMargins::None(block_start, _),
|
||
) => {
|
||
self.state = MarginCollapseState::AccumulatingMarginIn;
|
||
block_start
|
||
},
|
||
(
|
||
MarginCollapseState::AccumulatingCollapsibleTopMargin,
|
||
CollapsibleMargins::Collapse(block_start, _),
|
||
) => {
|
||
self.block_start_margin.union(block_start);
|
||
self.state = MarginCollapseState::AccumulatingMarginIn;
|
||
Au(0)
|
||
},
|
||
(
|
||
MarginCollapseState::AccumulatingMarginIn,
|
||
CollapsibleMargins::None(block_start, _),
|
||
) => {
|
||
let previous_margin_value = self.margin_in.collapse();
|
||
self.margin_in = AdjoiningMargins::new();
|
||
previous_margin_value + block_start
|
||
},
|
||
(
|
||
MarginCollapseState::AccumulatingMarginIn,
|
||
CollapsibleMargins::Collapse(block_start, _),
|
||
) => {
|
||
self.margin_in.union(block_start);
|
||
let margin_value = self.margin_in.collapse();
|
||
self.margin_in = AdjoiningMargins::new();
|
||
margin_value
|
||
},
|
||
(_, CollapsibleMargins::CollapseThrough(_)) => {
|
||
// For now, we ignore this; this will be handled by `advance_block_end_margin`
|
||
// below.
|
||
Au(0)
|
||
},
|
||
}
|
||
}
|
||
|
||
/// Adds the child's potentially collapsible block-end margin to the current margin state and
|
||
/// advances the Y offset by the appropriate amount to handle that margin. Returns the amount
|
||
/// that should be added to the Y offset during block layout.
|
||
pub fn advance_block_end_margin(
|
||
&mut self,
|
||
child_collapsible_margins: &CollapsibleMargins,
|
||
) -> Au {
|
||
match (self.state, *child_collapsible_margins) {
|
||
(
|
||
MarginCollapseState::AccumulatingCollapsibleTopMargin,
|
||
CollapsibleMargins::None(..),
|
||
) |
|
||
(
|
||
MarginCollapseState::AccumulatingCollapsibleTopMargin,
|
||
CollapsibleMargins::Collapse(..),
|
||
) => {
|
||
// Can't happen because the state will have been replaced with
|
||
// `MarginCollapseState::AccumulatingMarginIn` above.
|
||
panic!("should not be accumulating collapsible block_start margins anymore!")
|
||
},
|
||
(
|
||
MarginCollapseState::AccumulatingCollapsibleTopMargin,
|
||
CollapsibleMargins::CollapseThrough(margin),
|
||
) => {
|
||
self.block_start_margin.union(margin);
|
||
Au(0)
|
||
},
|
||
(MarginCollapseState::AccumulatingMarginIn, CollapsibleMargins::None(_, block_end)) => {
|
||
assert_eq!(self.margin_in.most_positive, Au(0));
|
||
assert_eq!(self.margin_in.most_negative, Au(0));
|
||
block_end
|
||
},
|
||
(
|
||
MarginCollapseState::AccumulatingMarginIn,
|
||
CollapsibleMargins::Collapse(_, block_end),
|
||
) |
|
||
(
|
||
MarginCollapseState::AccumulatingMarginIn,
|
||
CollapsibleMargins::CollapseThrough(block_end),
|
||
) => {
|
||
self.margin_in.union(block_end);
|
||
Au(0)
|
||
},
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, Copy, Debug)]
|
||
pub enum MarginCollapseState {
|
||
/// We are accumulating margin on the logical top of this flow.
|
||
AccumulatingCollapsibleTopMargin,
|
||
/// We are accumulating margin between two blocks.
|
||
AccumulatingMarginIn,
|
||
}
|
||
|
||
/// Intrinsic inline-sizes, which consist of minimum and preferred.
|
||
#[derive(Clone, Copy, Serialize)]
|
||
pub struct IntrinsicISizes {
|
||
/// The *minimum inline-size* of the content.
|
||
pub minimum_inline_size: Au,
|
||
/// The *preferred inline-size* of the content.
|
||
pub preferred_inline_size: Au,
|
||
}
|
||
|
||
impl fmt::Debug for IntrinsicISizes {
|
||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||
write!(
|
||
f,
|
||
"min={:?}, pref={:?}",
|
||
self.minimum_inline_size, self.preferred_inline_size
|
||
)
|
||
}
|
||
}
|
||
|
||
impl IntrinsicISizes {
|
||
pub fn new() -> IntrinsicISizes {
|
||
IntrinsicISizes {
|
||
minimum_inline_size: Au(0),
|
||
preferred_inline_size: Au(0),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl Default for IntrinsicISizes {
|
||
fn default() -> Self {
|
||
Self::new()
|
||
}
|
||
}
|
||
|
||
/// The temporary result of the computation of intrinsic inline-sizes.
|
||
#[derive(Debug)]
|
||
pub struct IntrinsicISizesContribution {
|
||
/// Intrinsic sizes for the content only (not counting borders, padding, or margins).
|
||
pub content_intrinsic_sizes: IntrinsicISizes,
|
||
/// The inline size of borders and padding, as well as margins if appropriate.
|
||
pub surrounding_size: Au,
|
||
}
|
||
|
||
impl IntrinsicISizesContribution {
|
||
/// Creates and initializes an inline size computation with all sizes set to zero.
|
||
pub fn new() -> IntrinsicISizesContribution {
|
||
IntrinsicISizesContribution {
|
||
content_intrinsic_sizes: IntrinsicISizes::new(),
|
||
surrounding_size: Au(0),
|
||
}
|
||
}
|
||
|
||
/// Adds the content intrinsic sizes and the surrounding size together to yield the final
|
||
/// intrinsic size computation.
|
||
pub fn finish(self) -> IntrinsicISizes {
|
||
IntrinsicISizes {
|
||
minimum_inline_size: self.content_intrinsic_sizes.minimum_inline_size +
|
||
self.surrounding_size,
|
||
preferred_inline_size: self.content_intrinsic_sizes.preferred_inline_size +
|
||
self.surrounding_size,
|
||
}
|
||
}
|
||
|
||
/// Updates the computation so that the minimum is the maximum of the current minimum and the
|
||
/// given minimum and the preferred is the sum of the current preferred and the given
|
||
/// preferred. This is used when laying out fragments in the inline direction.
|
||
///
|
||
/// FIXME(pcwalton): This is incorrect when the inline fragment contains forced line breaks
|
||
/// (e.g. `<br>` or `white-space: pre`).
|
||
pub fn union_inline(&mut self, sizes: &IntrinsicISizes) {
|
||
self.content_intrinsic_sizes.minimum_inline_size = max(
|
||
self.content_intrinsic_sizes.minimum_inline_size,
|
||
sizes.minimum_inline_size,
|
||
);
|
||
self.content_intrinsic_sizes.preferred_inline_size += sizes.preferred_inline_size
|
||
}
|
||
|
||
/// Updates the computation so that the minimum is the sum of the current minimum and the
|
||
/// given minimum and the preferred is the sum of the current preferred and the given
|
||
/// preferred. This is used when laying out fragments in the inline direction when
|
||
/// `white-space` is `pre` or `nowrap`.
|
||
pub fn union_nonbreaking_inline(&mut self, sizes: &IntrinsicISizes) {
|
||
self.content_intrinsic_sizes.minimum_inline_size += sizes.minimum_inline_size;
|
||
self.content_intrinsic_sizes.preferred_inline_size += sizes.preferred_inline_size
|
||
}
|
||
|
||
/// Updates the computation so that the minimum is the maximum of the current minimum and the
|
||
/// given minimum and the preferred is the maximum of the current preferred and the given
|
||
/// preferred. This can be useful when laying out fragments in the block direction (but note
|
||
/// that it does not take floats into account, so `BlockFlow` does not use it).
|
||
///
|
||
/// This is used when contributing the intrinsic sizes for individual fragments.
|
||
pub fn union_block(&mut self, sizes: &IntrinsicISizes) {
|
||
self.content_intrinsic_sizes.minimum_inline_size = max(
|
||
self.content_intrinsic_sizes.minimum_inline_size,
|
||
sizes.minimum_inline_size,
|
||
);
|
||
self.content_intrinsic_sizes.preferred_inline_size = max(
|
||
self.content_intrinsic_sizes.preferred_inline_size,
|
||
sizes.preferred_inline_size,
|
||
)
|
||
}
|
||
}
|
||
|
||
impl Default for IntrinsicISizesContribution {
|
||
fn default() -> Self {
|
||
Self::new()
|
||
}
|
||
}
|
||
|
||
/// Useful helper data type when computing values for blocks and positioned elements.
|
||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||
pub enum MaybeAuto {
|
||
Auto,
|
||
Specified(Au),
|
||
}
|
||
|
||
impl MaybeAuto {
|
||
#[inline]
|
||
pub fn from_style(length: &LengthPercentageOrAuto, containing_length: Au) -> MaybeAuto {
|
||
match length {
|
||
LengthPercentageOrAuto::Auto => MaybeAuto::Auto,
|
||
LengthPercentageOrAuto::LengthPercentage(ref lp) => {
|
||
MaybeAuto::Specified(lp.to_used_value(containing_length))
|
||
},
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn from_inset(length: &Inset, containing_length: Au) -> MaybeAuto {
|
||
match length {
|
||
Inset::Auto => MaybeAuto::Auto,
|
||
Inset::LengthPercentage(ref lp) => {
|
||
MaybeAuto::Specified(lp.to_used_value(containing_length))
|
||
},
|
||
Inset::AnchorFunction(_) => unreachable!("anchor() should be disabled"),
|
||
Inset::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"),
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn from_margin(length: &Margin, containing_length: Au) -> MaybeAuto {
|
||
match length {
|
||
Margin::Auto => MaybeAuto::Auto,
|
||
Margin::LengthPercentage(ref lp) => {
|
||
MaybeAuto::Specified(lp.to_used_value(containing_length))
|
||
},
|
||
Margin::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"),
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn from_option(au: Option<Au>) -> MaybeAuto {
|
||
match au {
|
||
Some(l) => MaybeAuto::Specified(l),
|
||
_ => MaybeAuto::Auto,
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn as_option(&self) -> Option<Au> {
|
||
match *self {
|
||
MaybeAuto::Specified(value) => Some(value),
|
||
MaybeAuto::Auto => None,
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn specified_or_default(&self, default: Au) -> Au {
|
||
match *self {
|
||
MaybeAuto::Auto => default,
|
||
MaybeAuto::Specified(value) => value,
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn specified_or_zero(&self) -> Au {
|
||
self.specified_or_default(Au::new(0))
|
||
}
|
||
|
||
#[inline]
|
||
pub fn is_auto(&self) -> bool {
|
||
match *self {
|
||
MaybeAuto::Auto => true,
|
||
MaybeAuto::Specified(..) => false,
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn map<F>(&self, mapper: F) -> MaybeAuto
|
||
where
|
||
F: FnOnce(Au) -> Au,
|
||
{
|
||
match *self {
|
||
MaybeAuto::Auto => MaybeAuto::Auto,
|
||
MaybeAuto::Specified(value) => MaybeAuto::Specified(mapper(value)),
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Receive an optional container size and return used value for width or height.
|
||
///
|
||
/// `style_length`: content size as given in the CSS.
|
||
pub fn style_length(style_length: &Size, container_size: Option<Au>) -> MaybeAuto {
|
||
MaybeAuto::from_option(style_length.maybe_to_used_value(container_size))
|
||
}
|
||
|
||
#[inline]
|
||
pub fn padding_from_style(
|
||
style: &ComputedValues,
|
||
containing_block_inline_size: Au,
|
||
writing_mode: WritingMode,
|
||
) -> LogicalMargin<Au> {
|
||
let padding_style = style.get_padding();
|
||
LogicalMargin::from_physical(
|
||
writing_mode,
|
||
SideOffsets2D::new(
|
||
padding_style
|
||
.padding_top
|
||
.to_used_value(containing_block_inline_size),
|
||
padding_style
|
||
.padding_right
|
||
.to_used_value(containing_block_inline_size),
|
||
padding_style
|
||
.padding_bottom
|
||
.to_used_value(containing_block_inline_size),
|
||
padding_style
|
||
.padding_left
|
||
.to_used_value(containing_block_inline_size),
|
||
),
|
||
)
|
||
}
|
||
|
||
/// Returns the explicitly-specified margin lengths from the given style. Percentage and auto
|
||
/// margins are returned as zero.
|
||
///
|
||
/// This is used when calculating intrinsic inline sizes.
|
||
#[inline]
|
||
pub fn specified_margin_from_style(
|
||
style: &ComputedValues,
|
||
writing_mode: WritingMode,
|
||
) -> LogicalMargin<Au> {
|
||
let margin_style = style.get_margin();
|
||
LogicalMargin::from_physical(
|
||
writing_mode,
|
||
SideOffsets2D::new(
|
||
MaybeAuto::from_margin(&margin_style.margin_top, Au(0)).specified_or_zero(),
|
||
MaybeAuto::from_margin(&margin_style.margin_right, Au(0)).specified_or_zero(),
|
||
MaybeAuto::from_margin(&margin_style.margin_bottom, Au(0)).specified_or_zero(),
|
||
MaybeAuto::from_margin(&margin_style.margin_left, Au(0)).specified_or_zero(),
|
||
),
|
||
)
|
||
}
|
||
|
||
/// A min-size and max-size constraint. The constructor has a optional `border`
|
||
/// parameter, and when it is present the constraint will be subtracted. This is
|
||
/// used to adjust the constraint for `box-sizing: border-box`, and when you do so
|
||
/// make sure the size you want to clamp is intended to be used for content box.
|
||
#[derive(Clone, Copy, Debug, Serialize)]
|
||
pub struct SizeConstraint {
|
||
min_size: Au,
|
||
max_size: Option<Au>,
|
||
}
|
||
|
||
impl SizeConstraint {
|
||
/// Create a `SizeConstraint` for an axis.
|
||
pub fn new(
|
||
container_size: Option<Au>,
|
||
min_size: &Size,
|
||
max_size: &MaxSize,
|
||
border: Option<Au>,
|
||
) -> SizeConstraint {
|
||
let mut min_size = min_size
|
||
.maybe_to_used_value(container_size)
|
||
.unwrap_or(Au(0));
|
||
let mut max_size = max_size.maybe_to_used_value(container_size);
|
||
|
||
// Make sure max size is not smaller than min size.
|
||
max_size = max_size.map(|x| max(x, min_size));
|
||
|
||
if let Some(border) = border {
|
||
min_size = max(min_size - border, Au(0));
|
||
max_size = max_size.map(|x| max(x - border, Au(0)));
|
||
}
|
||
|
||
SizeConstraint { min_size, max_size }
|
||
}
|
||
|
||
/// Clamp the given size by the given min size and max size constraint.
|
||
pub fn clamp(&self, other: Au) -> Au {
|
||
if other < self.min_size {
|
||
self.min_size
|
||
} else {
|
||
match self.max_size {
|
||
Some(max_size) if max_size < other => max_size,
|
||
_ => other,
|
||
}
|
||
}
|
||
}
|
||
}
|