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    GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
13};
14use crate::values::generics::NonNegative;
15use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};
16use crate::values::specified::length::{AbsoluteLength, FontBaseSize, LineHeightBase};
17use crate::values::{specified, CSSFloat};
18use crate::Zero;
19use app_units::Au;
20use std::fmt::{self, Write};
21use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign};
22use style_traits::{CSSPixel, CssWriter, ToCss};
23
24pub use super::image::Image;
25pub use super::length_percentage::{LengthPercentage, NonNegativeLengthPercentage};
26pub use crate::values::specified::url::UrlOrNone;
27pub use crate::values::specified::{Angle, BorderStyle, Time};
28
29impl ToComputedValue for specified::NoCalcLength {
30    type ComputedValue = Length;
31
32    #[inline]
33    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
34        self.to_computed_value_with_base_size(
35            context,
36            FontBaseSize::CurrentStyle,
37            LineHeightBase::CurrentStyle,
38        )
39    }
40
41    #[inline]
42    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
43        Self::Absolute(AbsoluteLength::Px(computed.px()))
44    }
45}
46
47impl specified::NoCalcLength {
48    /// Computes a length with a given font-relative base size.
49    pub fn to_computed_value_with_base_size(
50        &self,
51        context: &Context,
52        base_size: FontBaseSize,
53        line_height_base: LineHeightBase,
54    ) -> Length {
55        match *self {
56            Self::Absolute(length) => length.to_computed_value(context),
57            Self::FontRelative(length) => {
58                length.to_computed_value(context, base_size, line_height_base)
59            },
60            Self::ViewportPercentage(length) => length.to_computed_value(context),
61            Self::ContainerRelative(length) => length.to_computed_value(context),
62            Self::ServoCharacterWidth(length) => length
63                .to_computed_value(context.style().get_font().clone_font_size().computed_size()),
64        }
65    }
66}
67
68impl ToComputedValue for specified::Length {
69    type ComputedValue = Length;
70
71    #[inline]
72    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
73        match *self {
74            Self::NoCalc(l) => l.to_computed_value(context),
75            Self::Calc(ref calc) => {
76                let result = calc.to_computed_value(context);
77                debug_assert!(
78                    result.to_length().is_some(),
79                    "{:?} didn't resolve to a length: {:?}",
80                    calc,
81                    result,
82                );
83                result.to_length().unwrap_or_else(Length::zero)
84            },
85        }
86    }
87
88    #[inline]
89    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
90        Self::NoCalc(specified::NoCalcLength::from_computed_value(computed))
91    }
92}
93
94/// Some boilerplate to share between negative and non-negative
95/// length-percentage or auto.
96macro_rules! computed_length_percentage_or_auto {
97    ($inner:ty) => {
98        /// Returns the used value.
99        #[inline]
100        pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {
101            match *self {
102                Self::Auto => None,
103                Self::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)),
104            }
105        }
106
107        /// Returns true if the computed value is absolute 0 or 0%.
108        #[inline]
109        pub fn is_definitely_zero(&self) -> bool {
110            use crate::values::generics::length::LengthPercentageOrAuto::*;
111            match *self {
112                LengthPercentage(ref l) => l.is_definitely_zero(),
113                Auto => false,
114            }
115        }
116    };
117}
118
119/// A computed type for `<length-percentage> | auto`.
120pub type LengthPercentageOrAuto = generics::GenericLengthPercentageOrAuto<LengthPercentage>;
121
122impl LengthPercentageOrAuto {
123    /// Clamps the value to a non-negative value.
124    pub fn clamp_to_non_negative(self) -> Self {
125        use crate::values::generics::length::LengthPercentageOrAuto::*;
126        match self {
127            LengthPercentage(l) => LengthPercentage(l.clamp_to_non_negative()),
128            Auto => Auto,
129        }
130    }
131
132    /// Convert to have a borrow inside the enum
133    pub fn as_ref(&self) -> generics::GenericLengthPercentageOrAuto<&LengthPercentage> {
134        use crate::values::generics::length::LengthPercentageOrAuto::*;
135        match *self {
136            LengthPercentage(ref lp) => LengthPercentage(lp),
137            Auto => Auto,
138        }
139    }
140
141    computed_length_percentage_or_auto!(LengthPercentage);
142}
143
144impl generics::GenericLengthPercentageOrAuto<&LengthPercentage> {
145    /// Resolves the percentage.
146    #[inline]
147    pub fn percentage_relative_to(&self, basis: Length) -> LengthOrAuto {
148        use crate::values::generics::length::LengthPercentageOrAuto::*;
149        match self {
150            LengthPercentage(length_percentage) => {
151                LengthPercentage(length_percentage.percentage_relative_to(basis))
152            },
153            Auto => Auto,
154        }
155    }
156
157    /// Maybe resolves the percentage.
158    #[inline]
159    pub fn maybe_percentage_relative_to(&self, basis: Option<Length>) -> LengthOrAuto {
160        use crate::values::generics::length::LengthPercentageOrAuto::*;
161        match self {
162            LengthPercentage(length_percentage) => length_percentage
163                .maybe_percentage_relative_to(basis)
164                .map_or(Auto, LengthPercentage),
165            Auto => Auto,
166        }
167    }
168}
169
170/// A wrapper of LengthPercentageOrAuto, whose value must be >= 0.
171pub type NonNegativeLengthPercentageOrAuto =
172    generics::GenericLengthPercentageOrAuto<NonNegativeLengthPercentage>;
173
174impl NonNegativeLengthPercentageOrAuto {
175    computed_length_percentage_or_auto!(NonNegativeLengthPercentage);
176}
177
178/// The computed `<length>` value.
179#[derive(
180    Animate,
181    Clone,
182    ComputeSquaredDistance,
183    Copy,
184    Deserialize,
185    MallocSizeOf,
186    PartialEq,
187    PartialOrd,
188    Serialize,
189    ToAnimatedZero,
190    ToComputedValue,
191    ToShmem,
192    ToTyped,
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#[cfg(feature = "gecko")]
534use crate::{
535    gecko_bindings::structs::AnchorPosResolutionParams, logical_geometry::PhysicalAxis,
536    values::generics::length::AnchorSizeKeyword, values::DashedIdent,
537};
538
539/// Resolve the anchor function with the given resolver. Returns `Err()` if no anchor is found.
540/// `prop_axis`, axis of the property (e.g. `margin-left` -> Horizontal axis), is used if the
541/// anchor size keyword is not specified.
542#[cfg(feature = "gecko")]
543pub fn resolve_anchor_size(
544    anchor_name: &DashedIdent,
545    prop_axis: PhysicalAxis,
546    anchor_size_keyword: AnchorSizeKeyword,
547    params: &AnchorPosResolutionParams,
548) -> Result<Length, ()> {
549    use crate::gecko_bindings::structs::Gecko_GetAnchorPosSize;
550
551    let mut offset = Length::zero();
552    let valid = unsafe {
553        Gecko_GetAnchorPosSize(
554            params,
555            anchor_name.0.as_ptr(),
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>;