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