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