tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

length.rs (18135B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 //! `<length>` computed values, and related ones.
      6 
      7 use super::{Context, Number, ToComputedValue};
      8 use crate::derives::*;
      9 use crate::logical_geometry::PhysicalSide;
     10 use crate::values::animated::{Context as AnimatedContext, ToAnimatedValue};
     11 use crate::values::computed::position::TryTacticAdjustment;
     12 use crate::values::computed::{NonNegativeNumber, Percentage, Zoom};
     13 use crate::values::generics::length::{
     14    GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
     15 };
     16 use crate::values::generics::NonNegative;
     17 use crate::values::generics::{length as generics, ClampToNonNegative};
     18 use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};
     19 use crate::values::specified::length::{AbsoluteLength, FontBaseSize, LineHeightBase};
     20 use crate::values::{specified, CSSFloat};
     21 use crate::Zero;
     22 use app_units::Au;
     23 use std::fmt::{self, Write};
     24 use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign};
     25 use style_traits::{CSSPixel, CssString, CssWriter, NumericValue, ToCss, ToTyped, TypedValue};
     26 
     27 pub use super::image::Image;
     28 pub use super::length_percentage::{LengthPercentage, NonNegativeLengthPercentage};
     29 pub use crate::values::specified::url::UrlOrNone;
     30 pub use crate::values::specified::{Angle, BorderStyle, Time};
     31 
     32 impl ToComputedValue for specified::NoCalcLength {
     33    type ComputedValue = Length;
     34 
     35    #[inline]
     36    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
     37        self.to_computed_value_with_base_size(
     38            context,
     39            FontBaseSize::CurrentStyle,
     40            LineHeightBase::CurrentStyle,
     41        )
     42    }
     43 
     44    #[inline]
     45    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
     46        Self::Absolute(AbsoluteLength::Px(computed.px()))
     47    }
     48 }
     49 
     50 impl specified::NoCalcLength {
     51    /// Computes a length with a given font-relative base size.
     52    pub fn to_computed_value_with_base_size(
     53        &self,
     54        context: &Context,
     55        base_size: FontBaseSize,
     56        line_height_base: LineHeightBase,
     57    ) -> Length {
     58        match *self {
     59            Self::Absolute(length) => length.to_computed_value(context),
     60            Self::FontRelative(length) => {
     61                length.to_computed_value(context, base_size, line_height_base)
     62            },
     63            Self::ViewportPercentage(length) => length.to_computed_value(context),
     64            Self::ContainerRelative(length) => length.to_computed_value(context),
     65            Self::ServoCharacterWidth(length) => length
     66                .to_computed_value(context.style().get_font().clone_font_size().computed_size()),
     67        }
     68    }
     69 }
     70 
     71 impl ToComputedValue for specified::Length {
     72    type ComputedValue = Length;
     73 
     74    #[inline]
     75    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
     76        match *self {
     77            Self::NoCalc(l) => l.to_computed_value(context),
     78            Self::Calc(ref calc) => {
     79                let result = calc.to_computed_value(context);
     80                debug_assert!(
     81                    result.to_length().is_some(),
     82                    "{:?} didn't resolve to a length: {:?}",
     83                    calc,
     84                    result,
     85                );
     86                result.to_length().unwrap_or_else(Length::zero)
     87            },
     88        }
     89    }
     90 
     91    #[inline]
     92    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
     93        Self::NoCalc(specified::NoCalcLength::from_computed_value(computed))
     94    }
     95 }
     96 
     97 /// Some boilerplate to share between negative and non-negative
     98 /// length-percentage or auto.
     99 macro_rules! computed_length_percentage_or_auto {
    100    ($inner:ty) => {
    101        /// Returns the used value.
    102        #[inline]
    103        pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {
    104            match *self {
    105                Self::Auto => None,
    106                Self::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)),
    107            }
    108        }
    109    };
    110 }
    111 
    112 /// A computed type for `<length-percentage> | auto`.
    113 pub type LengthPercentageOrAuto = generics::GenericLengthPercentageOrAuto<LengthPercentage>;
    114 
    115 impl LengthPercentageOrAuto {
    116    /// Clamps the value to a non-negative value.
    117    pub fn clamp_to_non_negative(self) -> Self {
    118        use crate::values::generics::length::LengthPercentageOrAuto::*;
    119        match self {
    120            LengthPercentage(l) => LengthPercentage(l.clamp_to_non_negative()),
    121            Auto => Auto,
    122        }
    123    }
    124 
    125    /// Convert to have a borrow inside the enum
    126    pub fn as_ref(&self) -> generics::GenericLengthPercentageOrAuto<&LengthPercentage> {
    127        use crate::values::generics::length::LengthPercentageOrAuto::*;
    128        match *self {
    129            LengthPercentage(ref lp) => LengthPercentage(lp),
    130            Auto => Auto,
    131        }
    132    }
    133 
    134    computed_length_percentage_or_auto!(LengthPercentage);
    135 }
    136 
    137 impl generics::GenericLengthPercentageOrAuto<&LengthPercentage> {
    138    /// Resolves the percentage.
    139    #[inline]
    140    pub fn percentage_relative_to(&self, basis: Length) -> LengthOrAuto {
    141        use crate::values::generics::length::LengthPercentageOrAuto::*;
    142        match self {
    143            LengthPercentage(length_percentage) => {
    144                LengthPercentage(length_percentage.percentage_relative_to(basis))
    145            },
    146            Auto => Auto,
    147        }
    148    }
    149 
    150    /// Maybe resolves the percentage.
    151    #[inline]
    152    pub fn maybe_percentage_relative_to(&self, basis: Option<Length>) -> LengthOrAuto {
    153        use crate::values::generics::length::LengthPercentageOrAuto::*;
    154        match self {
    155            LengthPercentage(length_percentage) => length_percentage
    156                .maybe_percentage_relative_to(basis)
    157                .map_or(Auto, LengthPercentage),
    158            Auto => Auto,
    159        }
    160    }
    161 }
    162 
    163 /// A wrapper of LengthPercentageOrAuto, whose value must be >= 0.
    164 pub type NonNegativeLengthPercentageOrAuto =
    165    generics::GenericLengthPercentageOrAuto<NonNegativeLengthPercentage>;
    166 
    167 impl NonNegativeLengthPercentageOrAuto {
    168    computed_length_percentage_or_auto!(NonNegativeLengthPercentage);
    169 }
    170 
    171 /// The computed `<length>` value.
    172 #[derive(
    173    Animate,
    174    Clone,
    175    ComputeSquaredDistance,
    176    Copy,
    177    Deserialize,
    178    MallocSizeOf,
    179    PartialEq,
    180    PartialOrd,
    181    Serialize,
    182    ToAnimatedZero,
    183    ToComputedValue,
    184    ToShmem,
    185 )]
    186 #[repr(C)]
    187 pub struct CSSPixelLength(CSSFloat);
    188 
    189 impl ToResolvedValue for CSSPixelLength {
    190    type ResolvedValue = Self;
    191 
    192    fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
    193        Self(context.style.effective_zoom.unzoom(self.0))
    194    }
    195 
    196    #[inline]
    197    fn from_resolved_value(value: Self::ResolvedValue) -> Self {
    198        value
    199    }
    200 }
    201 
    202 impl ToAnimatedValue for CSSPixelLength {
    203    type AnimatedValue = Self;
    204 
    205    fn to_animated_value(self, context: &AnimatedContext) -> Self::AnimatedValue {
    206        Self(context.style.effective_zoom.unzoom(self.0))
    207    }
    208 
    209    #[inline]
    210    fn from_animated_value(value: Self::AnimatedValue) -> Self {
    211        value
    212    }
    213 }
    214 
    215 impl fmt::Debug for CSSPixelLength {
    216    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    217        self.0.fmt(f)?;
    218        f.write_str(" px")
    219    }
    220 }
    221 
    222 impl CSSPixelLength {
    223    /// Return a new CSSPixelLength.
    224    #[inline]
    225    pub fn new(px: CSSFloat) -> Self {
    226        CSSPixelLength(px)
    227    }
    228 
    229    /// Returns a normalized (NaN turned to zero) version of this length.
    230    #[inline]
    231    pub fn normalized(self) -> Self {
    232        Self::new(crate::values::normalize(self.0))
    233    }
    234 
    235    /// Returns a finite (normalized and clamped to float min and max) version of this length.
    236    #[inline]
    237    pub fn finite(self) -> Self {
    238        Self::new(crate::values::normalize(self.0).min(f32::MAX).max(f32::MIN))
    239    }
    240 
    241    /// Scale the length by a given amount.
    242    #[inline]
    243    pub fn scale_by(self, scale: CSSFloat) -> Self {
    244        CSSPixelLength(self.0 * scale)
    245    }
    246 
    247    /// Return the containing pixel value.
    248    #[inline]
    249    pub fn px(self) -> CSSFloat {
    250        self.0
    251    }
    252 
    253    /// Zooms a particular length.
    254    #[inline]
    255    pub fn zoom(self, zoom: Zoom) -> Self {
    256        Self::new(zoom.zoom(self.px()))
    257    }
    258 
    259    /// Return the length with app_unit i32 type.
    260    #[inline]
    261    pub fn to_i32_au(self) -> i32 {
    262        Au::from(self).0
    263    }
    264 
    265    /// Return the absolute value of this length.
    266    #[inline]
    267    pub fn abs(self) -> Self {
    268        CSSPixelLength::new(self.0.abs())
    269    }
    270 
    271    /// Return the clamped value of this length.
    272    #[inline]
    273    pub fn clamp_to_non_negative(self) -> Self {
    274        CSSPixelLength::new(self.0.max(0.))
    275    }
    276 
    277    /// Returns the minimum between `self` and `other`.
    278    #[inline]
    279    pub fn min(self, other: Self) -> Self {
    280        CSSPixelLength::new(self.0.min(other.0))
    281    }
    282 
    283    /// Returns the maximum between `self` and `other`.
    284    #[inline]
    285    pub fn max(self, other: Self) -> Self {
    286        CSSPixelLength::new(self.0.max(other.0))
    287    }
    288 
    289    /// Sets `self` to the maximum between `self` and `other`.
    290    #[inline]
    291    pub fn max_assign(&mut self, other: Self) {
    292        *self = self.max(other);
    293    }
    294 
    295    /// Clamp the value to a lower bound and an optional upper bound.
    296    ///
    297    /// Can be used for example with `min-width` and `max-width`.
    298    #[inline]
    299    pub fn clamp_between_extremums(self, min_size: Self, max_size: Option<Self>) -> Self {
    300        self.clamp_below_max(max_size).max(min_size)
    301    }
    302 
    303    /// Clamp the value to an optional upper bound.
    304    ///
    305    /// Can be used for example with `max-width`.
    306    #[inline]
    307    pub fn clamp_below_max(self, max_size: Option<Self>) -> Self {
    308        match max_size {
    309            None => self,
    310            Some(max_size) => self.min(max_size),
    311        }
    312    }
    313 }
    314 
    315 impl num_traits::Zero for CSSPixelLength {
    316    fn zero() -> Self {
    317        CSSPixelLength::new(0.)
    318    }
    319 
    320    fn is_zero(&self) -> bool {
    321        self.px() == 0.
    322    }
    323 }
    324 
    325 impl ToCss for CSSPixelLength {
    326    #[inline]
    327    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    328    where
    329        W: Write,
    330    {
    331        self.0.to_css(dest)?;
    332        dest.write_str("px")
    333    }
    334 }
    335 
    336 impl ToTyped for CSSPixelLength {
    337    fn to_typed(&self) -> Option<TypedValue> {
    338        Some(TypedValue::Numeric(NumericValue::Unit {
    339            value: self.0 as f32,
    340            unit: CssString::from("px"),
    341        }))
    342    }
    343 }
    344 
    345 impl std::iter::Sum for CSSPixelLength {
    346    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
    347        iter.fold(Length::zero(), Add::add)
    348    }
    349 }
    350 
    351 impl Add for CSSPixelLength {
    352    type Output = Self;
    353 
    354    #[inline]
    355    fn add(self, other: Self) -> Self {
    356        Self::new(self.px() + other.px())
    357    }
    358 }
    359 
    360 impl AddAssign for CSSPixelLength {
    361    #[inline]
    362    fn add_assign(&mut self, other: Self) {
    363        self.0 += other.0;
    364    }
    365 }
    366 
    367 impl Div for CSSPixelLength {
    368    type Output = CSSFloat;
    369 
    370    #[inline]
    371    fn div(self, other: Self) -> CSSFloat {
    372        self.px() / other.px()
    373    }
    374 }
    375 
    376 impl Div<CSSFloat> for CSSPixelLength {
    377    type Output = Self;
    378 
    379    #[inline]
    380    fn div(self, other: CSSFloat) -> Self {
    381        Self::new(self.px() / other)
    382    }
    383 }
    384 
    385 impl MulAssign<CSSFloat> for CSSPixelLength {
    386    #[inline]
    387    fn mul_assign(&mut self, other: CSSFloat) {
    388        self.0 *= other;
    389    }
    390 }
    391 
    392 impl Mul<CSSFloat> for CSSPixelLength {
    393    type Output = Self;
    394 
    395    #[inline]
    396    fn mul(self, other: CSSFloat) -> Self {
    397        Self::new(self.px() * other)
    398    }
    399 }
    400 
    401 impl Neg for CSSPixelLength {
    402    type Output = Self;
    403 
    404    #[inline]
    405    fn neg(self) -> Self {
    406        CSSPixelLength::new(-self.0)
    407    }
    408 }
    409 
    410 impl Sub for CSSPixelLength {
    411    type Output = Self;
    412 
    413    #[inline]
    414    fn sub(self, other: Self) -> Self {
    415        Self::new(self.px() - other.px())
    416    }
    417 }
    418 
    419 impl SubAssign for CSSPixelLength {
    420    #[inline]
    421    fn sub_assign(&mut self, other: Self) {
    422        self.0 -= other.0;
    423    }
    424 }
    425 
    426 impl From<CSSPixelLength> for Au {
    427    #[inline]
    428    fn from(len: CSSPixelLength) -> Self {
    429        Au::from_f32_px(len.0)
    430    }
    431 }
    432 
    433 impl From<Au> for CSSPixelLength {
    434    #[inline]
    435    fn from(len: Au) -> Self {
    436        CSSPixelLength::new(len.to_f32_px())
    437    }
    438 }
    439 
    440 impl From<CSSPixelLength> for euclid::Length<CSSFloat, CSSPixel> {
    441    #[inline]
    442    fn from(length: CSSPixelLength) -> Self {
    443        Self::new(length.0)
    444    }
    445 }
    446 
    447 /// An alias of computed `<length>` value.
    448 pub type Length = CSSPixelLength;
    449 
    450 /// Either a computed `<length>` or the `auto` keyword.
    451 pub type LengthOrAuto = generics::GenericLengthPercentageOrAuto<Length>;
    452 
    453 /// Either a non-negative `<length>` or the `auto` keyword.
    454 pub type NonNegativeLengthOrAuto = generics::GenericLengthPercentageOrAuto<NonNegativeLength>;
    455 
    456 /// Either a computed `<length>` or a `<number>` value.
    457 pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
    458 
    459 /// A wrapper of Length, whose value must be >= 0.
    460 pub type NonNegativeLength = NonNegative<Length>;
    461 
    462 impl ClampToNonNegative for Length {
    463    fn clamp_to_non_negative(self) -> Self {
    464        Self::new(self.px().max(0.))
    465    }
    466 }
    467 
    468 impl NonNegativeLength {
    469    /// Create a NonNegativeLength.
    470    #[inline]
    471    pub fn new(px: CSSFloat) -> Self {
    472        NonNegative(Length::new(px.max(0.)))
    473    }
    474 
    475    /// Return the pixel value of |NonNegativeLength|.
    476    #[inline]
    477    pub fn px(&self) -> CSSFloat {
    478        self.0.px()
    479    }
    480 
    481    #[inline]
    482    /// Ensures it is non negative
    483    pub fn clamp(self) -> Self {
    484        if (self.0).0 < 0. {
    485            Self::zero()
    486        } else {
    487            self
    488        }
    489    }
    490 }
    491 
    492 impl From<Length> for NonNegativeLength {
    493    #[inline]
    494    fn from(len: Length) -> Self {
    495        NonNegative(len)
    496    }
    497 }
    498 
    499 impl From<Au> for NonNegativeLength {
    500    #[inline]
    501    fn from(au: Au) -> Self {
    502        NonNegative(au.into())
    503    }
    504 }
    505 
    506 impl From<NonNegativeLength> for Au {
    507    #[inline]
    508    fn from(non_negative_len: NonNegativeLength) -> Self {
    509        Au::from(non_negative_len.0)
    510    }
    511 }
    512 
    513 /// Either a computed NonNegativeLengthPercentage or the `normal` keyword.
    514 pub type NonNegativeLengthPercentageOrNormal =
    515    GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;
    516 
    517 /// Either a non-negative `<length>` or a `<number>`.
    518 pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;
    519 
    520 /// A computed value for `min-width`, `min-height`, `width` or `height` property.
    521 pub type Size = GenericSize<NonNegativeLengthPercentage>;
    522 
    523 /// A computed value for `max-width` or `max-height` property.
    524 pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;
    525 
    526 #[cfg(feature = "gecko")]
    527 use crate::{
    528    gecko_bindings::structs::AnchorPosResolutionParams, logical_geometry::PhysicalAxis,
    529    values::generics::length::AnchorSizeKeyword, values::DashedIdent,
    530 };
    531 
    532 /// Resolve the anchor function with the given resolver. Returns `Err()` if no anchor is found.
    533 /// `prop_axis`, axis of the property (e.g. `margin-left` -> Horizontal axis), is used if the
    534 /// anchor size keyword is not specified.
    535 #[cfg(feature = "gecko")]
    536 pub fn resolve_anchor_size(
    537    anchor_name: &DashedIdent,
    538    prop_axis: PhysicalAxis,
    539    anchor_size_keyword: AnchorSizeKeyword,
    540    params: &AnchorPosResolutionParams,
    541 ) -> Result<Length, ()> {
    542    use crate::gecko_bindings::structs::Gecko_GetAnchorPosSize;
    543 
    544    let mut offset = Length::zero();
    545    let valid = unsafe {
    546        Gecko_GetAnchorPosSize(
    547            params,
    548            anchor_name.0.as_ptr(),
    549            prop_axis as u8,
    550            anchor_size_keyword as u8,
    551            &mut offset,
    552        )
    553    };
    554 
    555    if !valid {
    556        return Err(());
    557    }
    558 
    559    Ok(offset)
    560 }
    561 
    562 /// A computed type for `margin` properties.
    563 pub type Margin = generics::GenericMargin<LengthPercentage>;
    564 
    565 impl TryTacticAdjustment for MaxSize {
    566    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
    567        debug_assert!(
    568            old_side.orthogonal_to(new_side),
    569            "Sizes should only change axes"
    570        );
    571        match self {
    572            Self::FitContentFunction(lp)
    573            | Self::LengthPercentage(lp)
    574            | Self::AnchorContainingCalcFunction(lp) => {
    575                lp.try_tactic_adjustment(old_side, new_side);
    576            },
    577            Self::AnchorSizeFunction(s) => s.try_tactic_adjustment(old_side, new_side),
    578            Self::None
    579            | Self::MaxContent
    580            | Self::MinContent
    581            | Self::FitContent
    582            | Self::MozAvailable
    583            | Self::WebkitFillAvailable
    584            | Self::Stretch => {},
    585        }
    586    }
    587 }
    588 
    589 impl TryTacticAdjustment for Size {
    590    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
    591        debug_assert!(
    592            old_side.orthogonal_to(new_side),
    593            "Sizes should only change axes"
    594        );
    595        match self {
    596            Self::FitContentFunction(lp)
    597            | Self::LengthPercentage(lp)
    598            | Self::AnchorContainingCalcFunction(lp) => {
    599                lp.try_tactic_adjustment(old_side, new_side);
    600            },
    601            Self::AnchorSizeFunction(s) => s.try_tactic_adjustment(old_side, new_side),
    602            Self::Auto
    603            | Self::MaxContent
    604            | Self::MinContent
    605            | Self::FitContent
    606            | Self::MozAvailable
    607            | Self::WebkitFillAvailable
    608            | Self::Stretch => {},
    609        }
    610    }
    611 }
    612 
    613 impl TryTacticAdjustment for Percentage {
    614    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
    615        if old_side.parallel_to(new_side) {
    616            self.0 = 1.0 - self.0;
    617        }
    618    }
    619 }
    620 
    621 impl TryTacticAdjustment for Margin {
    622    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
    623        match self {
    624            Self::Auto => {},
    625            Self::LengthPercentage(lp) | Self::AnchorContainingCalcFunction(lp) => {
    626                lp.try_tactic_adjustment(old_side, new_side)
    627            },
    628            Self::AnchorSizeFunction(anchor) => anchor.try_tactic_adjustment(old_side, new_side),
    629        }
    630    }
    631 }