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