Skip to main content

style/values/computed/
length.rs

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