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 
7 use super::{Context, Number, ToComputedValue};
8 use crate::computed_value_flags::ComputedValueFlags;
9 use crate::values::animated::ToAnimatedValue;
10 use crate::values::computed::NonNegativeNumber;
11 use crate::values::generics::length as generics;
12 use crate::values::generics::length::{
13     GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
14 };
15 use crate::values::generics::NonNegative;
16 use crate::values::specified::length::{AbsoluteLength, FontBaseSize};
17 use crate::values::{specified, CSSFloat};
18 use crate::Zero;
19 use app_units::Au;
20 use std::fmt::{self, Write};
21 use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub};
22 use style_traits::{CSSPixel, CssWriter, ToCss};
23 
24 pub use super::image::Image;
25 pub use super::length_percentage::{LengthPercentage, NonNegativeLengthPercentage};
26 pub use crate::values::specified::url::UrlOrNone;
27 pub use crate::values::specified::{Angle, BorderStyle, Time};
28 
29 impl ToComputedValue for specified::NoCalcLength {
30     type ComputedValue = Length;
31 
32     #[inline]
33     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
34         match *self {
35             specified::NoCalcLength::Absolute(length) => length.to_computed_value(context),
36             specified::NoCalcLength::FontRelative(length) => {
37                 length.to_computed_value(context, FontBaseSize::CurrentStyle)
38             },
39             specified::NoCalcLength::ViewportPercentage(length) => {
40                 context.builder.add_flags(ComputedValueFlags::USES_VIEWPORT_UNITS);
41                 length.to_computed_value(context.viewport_size_for_viewport_unit_resolution())
42             },
43             specified::NoCalcLength::ServoCharacterWidth(length) => {
44                 length.to_computed_value(context.style().get_font().clone_font_size().size())
45             },
46         }
47     }
48 
49     #[inline]
50     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
51         specified::NoCalcLength::Absolute(AbsoluteLength::Px(computed.px()))
52     }
53 }
54 
55 impl ToComputedValue for specified::Length {
56     type ComputedValue = Length;
57 
58     #[inline]
59     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
60         match *self {
61             specified::Length::NoCalc(l) => l.to_computed_value(context),
62             specified::Length::Calc(ref calc) => {
63                 calc.to_computed_value(context).to_length().unwrap()
64             },
65         }
66     }
67 
68     #[inline]
69     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
70         specified::Length::NoCalc(specified::NoCalcLength::from_computed_value(computed))
71     }
72 }
73 
74 /// Some boilerplate to share between negative and non-negative
75 /// length-percentage or auto.
76 macro_rules! computed_length_percentage_or_auto {
77     ($inner:ty) => {
78         /// Returns the used value.
79         #[inline]
80         pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {
81             match *self {
82                 generics::GenericLengthPercentageOrAuto::Auto => None,
83                 generics::GenericLengthPercentageOrAuto::LengthPercentage(ref lp) => {
84                     Some(lp.to_used_value(percentage_basis))
85                 },
86             }
87         }
88 
89         /// Returns true if the computed value is absolute 0 or 0%.
90         #[inline]
91         pub fn is_definitely_zero(&self) -> bool {
92             use crate::values::generics::length::LengthPercentageOrAuto::*;
93             match *self {
94                 LengthPercentage(ref l) => l.is_definitely_zero(),
95                 Auto => false,
96             }
97         }
98     };
99 }
100 
101 /// A computed type for `<length-percentage> | auto`.
102 pub type LengthPercentageOrAuto = generics::GenericLengthPercentageOrAuto<LengthPercentage>;
103 
104 impl LengthPercentageOrAuto {
105     /// Clamps the value to a non-negative value.
106     pub fn clamp_to_non_negative(self) -> Self {
107         use crate::values::generics::length::LengthPercentageOrAuto::*;
108         match self {
109             LengthPercentage(l) => LengthPercentage(l.clamp_to_non_negative()),
110             Auto => Auto,
111         }
112     }
113 
114     /// Convert to have a borrow inside the enum
115     pub fn as_ref(&self) -> generics::GenericLengthPercentageOrAuto<&LengthPercentage> {
116         use crate::values::generics::length::LengthPercentageOrAuto::*;
117         match *self {
118             LengthPercentage(ref lp) => LengthPercentage(lp),
119             Auto => Auto,
120         }
121     }
122 
123     computed_length_percentage_or_auto!(LengthPercentage);
124 }
125 
126 impl generics::GenericLengthPercentageOrAuto<&LengthPercentage> {
127     /// Resolves the percentage.
128     #[inline]
129     pub fn percentage_relative_to(&self, basis: Length) -> LengthOrAuto {
130         use crate::values::generics::length::LengthPercentageOrAuto::*;
131         match self {
132             LengthPercentage(length_percentage) => {
133                 LengthPercentage(length_percentage.percentage_relative_to(basis))
134             },
135             Auto => Auto,
136         }
137     }
138 
139     /// Maybe resolves the percentage.
140     #[inline]
141     pub fn maybe_percentage_relative_to(&self, basis: Option<Length>) -> LengthOrAuto {
142         use crate::values::generics::length::LengthPercentageOrAuto::*;
143         match self {
144             LengthPercentage(length_percentage) => length_percentage
145                 .maybe_percentage_relative_to(basis)
146                 .map_or(Auto, LengthPercentage),
147             Auto => Auto,
148         }
149     }
150 }
151 
152 /// A wrapper of LengthPercentageOrAuto, whose value must be >= 0.
153 pub type NonNegativeLengthPercentageOrAuto =
154     generics::GenericLengthPercentageOrAuto<NonNegativeLengthPercentage>;
155 
156 impl NonNegativeLengthPercentageOrAuto {
157     computed_length_percentage_or_auto!(NonNegativeLengthPercentage);
158 }
159 
160 #[cfg(feature = "servo")]
161 impl MaxSize {
162     /// Convert the computed value into used value.
163     #[inline]
164     pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {
165         match *self {
166             GenericMaxSize::None => None,
167             GenericMaxSize::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)),
168         }
169     }
170 }
171 
172 impl Size {
173     /// Convert the computed value into used value.
174     #[inline]
175     #[cfg(feature = "servo")]
176     pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {
177         match *self {
178             GenericSize::Auto => None,
179             GenericSize::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)),
180         }
181     }
182 
183     /// Returns true if the computed value is absolute 0 or 0%.
184     #[inline]
185     pub fn is_definitely_zero(&self) -> bool {
186         match *self {
187             GenericSize::Auto => false,
188             GenericSize::LengthPercentage(ref lp) => lp.is_definitely_zero(),
189             #[cfg(feature = "gecko")]
190             GenericSize::MinContent |
191             GenericSize::MaxContent |
192             GenericSize::MozFitContent |
193             GenericSize::MozAvailable |
194             GenericSize::FitContentFunction(_) => false
195         }
196     }
197 }
198 
199 /// The computed `<length>` value.
200 #[derive(
201     Animate,
202     Clone,
203     ComputeSquaredDistance,
204     Copy,
205     Deserialize,
206     MallocSizeOf,
207     PartialEq,
208     PartialOrd,
209     Serialize,
210     ToAnimatedValue,
211     ToAnimatedZero,
212     ToComputedValue,
213     ToResolvedValue,
214     ToShmem,
215 )]
216 #[repr(C)]
217 pub struct CSSPixelLength(CSSFloat);
218 
219 impl 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 
226 impl 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     /// 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     /// Return the length with app_unit i32 type.
252     #[inline]
253     pub fn to_i32_au(self) -> i32 {
254         Au::from(self).0
255     }
256 
257     /// Return the absolute value of this length.
258     #[inline]
259     pub fn abs(self) -> Self {
260         CSSPixelLength::new(self.0.abs())
261     }
262 
263     /// Return the clamped value of this length.
264     #[inline]
265     pub fn clamp_to_non_negative(self) -> Self {
266         CSSPixelLength::new(self.0.max(0.))
267     }
268 
269     /// Returns the minimum between `self` and `other`.
270     #[inline]
271     pub fn min(self, other: Self) -> Self {
272         CSSPixelLength::new(self.0.min(other.0))
273     }
274 
275     /// Returns the maximum between `self` and `other`.
276     #[inline]
277     pub fn max(self, other: Self) -> Self {
278         CSSPixelLength::new(self.0.max(other.0))
279     }
280 
281     /// Sets `self` to the maximum between `self` and `other`.
282     #[inline]
283     pub fn max_assign(&mut self, other: Self) {
284         *self = self.max(other);
285     }
286 
287     /// Clamp the value to a lower bound and an optional upper bound.
288     ///
289     /// Can be used for example with `min-width` and `max-width`.
290     #[inline]
291     pub fn clamp_between_extremums(self, min_size: Self, max_size: Option<Self>) -> Self {
292         self.clamp_below_max(max_size).max(min_size)
293     }
294 
295     /// Clamp the value to an optional upper bound.
296     ///
297     /// Can be used for example with `max-width`.
298     #[inline]
299     pub fn clamp_below_max(self, max_size: Option<Self>) -> Self {
300         match max_size {
301             None => self,
302             Some(max_size) => self.min(max_size),
303         }
304     }
305 }
306 
307 impl num_traits::Zero for CSSPixelLength {
308     fn zero() -> Self {
309         CSSPixelLength::new(0.)
310     }
311 
312     fn is_zero(&self) -> bool {
313         self.px() == 0.
314     }
315 }
316 
317 impl ToCss for CSSPixelLength {
318     #[inline]
319     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
320     where
321         W: Write,
322     {
323         self.0.to_css(dest)?;
324         dest.write_str("px")
325     }
326 }
327 
328 impl std::iter::Sum for CSSPixelLength {
329     fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
330         iter.fold(Length::zero(), Add::add)
331     }
332 }
333 
334 impl Add for CSSPixelLength {
335     type Output = Self;
336 
337     #[inline]
338     fn add(self, other: Self) -> Self {
339         Self::new(self.px() + other.px())
340     }
341 }
342 
343 impl AddAssign for CSSPixelLength {
344     #[inline]
345     fn add_assign(&mut self, other: Self) {
346         self.0 += other.0;
347     }
348 }
349 
350 impl Div for CSSPixelLength {
351     type Output = CSSFloat;
352 
353     #[inline]
354     fn div(self, other: Self) -> CSSFloat {
355         self.px() / other.px()
356     }
357 }
358 
359 impl Div<CSSFloat> for CSSPixelLength {
360     type Output = Self;
361 
362     #[inline]
363     fn div(self, other: CSSFloat) -> Self {
364         Self::new(self.px() / other)
365     }
366 }
367 
368 impl MulAssign<CSSFloat> for CSSPixelLength {
369     #[inline]
370     fn mul_assign(&mut self, other: CSSFloat) {
371         self.0 *= other;
372     }
373 }
374 
375 impl Mul<CSSFloat> for CSSPixelLength {
376     type Output = Self;
377 
378     #[inline]
379     fn mul(self, other: CSSFloat) -> Self {
380         Self::new(self.px() * other)
381     }
382 }
383 
384 impl Neg for CSSPixelLength {
385     type Output = Self;
386 
387     #[inline]
388     fn neg(self) -> Self {
389         CSSPixelLength::new(-self.0)
390     }
391 }
392 
393 impl Sub for CSSPixelLength {
394     type Output = Self;
395 
396     #[inline]
397     fn sub(self, other: Self) -> Self {
398         Self::new(self.px() - other.px())
399     }
400 }
401 
402 impl From<CSSPixelLength> for Au {
403     #[inline]
404     fn from(len: CSSPixelLength) -> Self {
405         Au::from_f32_px(len.0)
406     }
407 }
408 
409 impl From<Au> for CSSPixelLength {
410     #[inline]
411     fn from(len: Au) -> Self {
412         CSSPixelLength::new(len.to_f32_px())
413     }
414 }
415 
416 impl From<CSSPixelLength> for euclid::Length<CSSFloat, CSSPixel> {
417     #[inline]
418     fn from(length: CSSPixelLength) -> Self {
419         Self::new(length.0)
420     }
421 }
422 
423 /// An alias of computed `<length>` value.
424 pub type Length = CSSPixelLength;
425 
426 /// Either a computed `<length>` or the `auto` keyword.
427 pub type LengthOrAuto = generics::GenericLengthPercentageOrAuto<Length>;
428 
429 /// Either a non-negative `<length>` or the `auto` keyword.
430 pub type NonNegativeLengthOrAuto = generics::GenericLengthPercentageOrAuto<NonNegativeLength>;
431 
432 /// Either a computed `<length>` or a `<number>` value.
433 pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
434 
435 /// A wrapper of Length, whose value must be >= 0.
436 pub type NonNegativeLength = NonNegative<Length>;
437 
438 impl ToAnimatedValue for NonNegativeLength {
439     type AnimatedValue = Length;
440 
441     #[inline]
442     fn to_animated_value(self) -> Self::AnimatedValue {
443         self.0
444     }
445 
446     #[inline]
447     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
448         NonNegativeLength::new(animated.px().max(0.))
449     }
450 }
451 
452 impl NonNegativeLength {
453     /// Create a NonNegativeLength.
454     #[inline]
455     pub fn new(px: CSSFloat) -> Self {
456         NonNegative(Length::new(px.max(0.)))
457     }
458 
459     /// Return the pixel value of |NonNegativeLength|.
460     #[inline]
461     pub fn px(&self) -> CSSFloat {
462         self.0.px()
463     }
464 
465     #[inline]
466     /// Ensures it is non negative
467     pub fn clamp(self) -> Self {
468         if (self.0).0 < 0. {
469             Self::zero()
470         } else {
471             self
472         }
473     }
474 }
475 
476 impl From<Length> for NonNegativeLength {
477     #[inline]
478     fn from(len: Length) -> Self {
479         NonNegative(len)
480     }
481 }
482 
483 impl From<Au> for NonNegativeLength {
484     #[inline]
485     fn from(au: Au) -> Self {
486         NonNegative(au.into())
487     }
488 }
489 
490 impl From<NonNegativeLength> for Au {
491     #[inline]
492     fn from(non_negative_len: NonNegativeLength) -> Self {
493         Au::from(non_negative_len.0)
494     }
495 }
496 
497 /// Either a computed NonNegativeLengthPercentage or the `normal` keyword.
498 pub type NonNegativeLengthPercentageOrNormal =
499     GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;
500 
501 /// Either a non-negative `<length>` or a `<number>`.
502 pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;
503 
504 /// A computed value for `min-width`, `min-height`, `width` or `height` property.
505 pub type Size = GenericSize<NonNegativeLengthPercentage>;
506 
507 /// A computed value for `max-width` or `min-height` property.
508 pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;
509