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