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