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