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 //! Computed values.
6 
7 use self::transform::DirectionVector;
8 use super::animated::ToAnimatedValue;
9 use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
10 use super::generics::grid::ImplicitGridTracks as GenericImplicitGridTracks;
11 use super::generics::grid::{GenericGridLine, GenericTrackBreadth};
12 use super::generics::grid::{GenericTrackSize, TrackList as GenericTrackList};
13 use super::generics::transform::IsParallelTo;
14 use super::generics::{self, GreaterThanOrEqualToOne, NonNegative, ZeroToOne};
15 use super::specified;
16 use super::{CSSFloat, CSSInteger};
17 use crate::context::QuirksMode;
18 use crate::font_metrics::{get_metrics_provider_for_product, FontMetricsProvider};
19 use crate::media_queries::Device;
20 #[cfg(feature = "gecko")]
21 use crate::properties;
22 use crate::properties::{ComputedValues, LonghandId, StyleBuilder};
23 use crate::rule_cache::RuleCacheConditions;
24 use crate::{ArcSlice, Atom, One};
25 use euclid::{default, Point2D, Rect, Size2D};
26 use servo_arc::Arc;
27 use std::cell::RefCell;
28 use std::cmp;
29 use std::f32;
30 use std::ops::{Add, Sub};
31 
32 #[cfg(feature = "gecko")]
33 pub use self::align::{
34     AlignContent, AlignItems, AlignTracks, JustifyContent, JustifyItems, JustifyTracks,
35     SelfAlignment,
36 };
37 #[cfg(feature = "gecko")]
38 pub use self::align::{AlignSelf, JustifySelf};
39 pub use self::angle::Angle;
40 pub use self::background::{BackgroundRepeat, BackgroundSize};
41 pub use self::basic_shape::FillRule;
42 pub use self::border::{BorderCornerRadius, BorderRadius, BorderSpacing};
43 pub use self::border::{BorderImageRepeat, BorderImageSideWidth};
44 pub use self::border::{BorderImageSlice, BorderImageWidth};
45 pub use self::box_::{AnimationIterationCount, AnimationName, AnimationTimeline, Contain};
46 pub use self::box_::{Appearance, BreakBetween, BreakWithin, Clear, Float};
47 pub use self::box_::{Display, Overflow, OverflowAnchor, TransitionProperty};
48 pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize, ScrollbarGutter};
49 pub use self::box_::{ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStrictness, ScrollSnapType};
50 pub use self::box_::{TouchAction, VerticalAlign, WillChange};
51 pub use self::color::{Color, ColorOrAuto, ColorPropertyValue, ColorScheme, PrintColorAdjust};
52 pub use self::column::ColumnCount;
53 pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset, CounterSet};
54 pub use self::easing::TimingFunction;
55 pub use self::effects::{BoxShadow, Filter, SimpleShadow};
56 pub use self::flex::FlexBasis;
57 pub use self::font::{FontFamily, FontLanguageOverride, FontStyle};
58 pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric};
59 pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis};
60 pub use self::font::{FontVariantAlternates, FontWeight};
61 pub use self::font::{FontVariantEastAsian, FontVariationSettings};
62 pub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom};
63 pub use self::image::{Gradient, Image, ImageRendering, LineDirection, MozImageRect};
64 pub use self::length::{CSSPixelLength, NonNegativeLength};
65 pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber};
66 pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, MaxSize, Size};
67 pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto};
68 #[cfg(feature = "gecko")]
69 pub use self::list::ListStyleType;
70 pub use self::list::Quotes;
71 pub use self::motion::{OffsetPath, OffsetRotate};
72 pub use self::outline::OutlineStyle;
73 pub use self::page::{Orientation, PageName, PageSize, PaperSize};
74 pub use self::percentage::{NonNegativePercentage, Percentage};
75 pub use self::position::AspectRatio;
76 pub use self::position::{
77     GridAutoFlow, GridTemplateAreas, MasonryAutoFlow, Position, PositionOrAuto, ZIndex,
78 };
79 pub use self::ratio::Ratio;
80 pub use self::rect::NonNegativeLengthOrNumberRect;
81 pub use self::resolution::Resolution;
82 pub use self::svg::{DProperty, MozContextProperties};
83 pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};
84 pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
85 pub use self::text::HyphenateCharacter;
86 pub use self::text::TextUnderlinePosition;
87 pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight};
88 pub use self::text::{OverflowWrap, RubyPosition, TextOverflow, WordBreak, WordSpacing};
89 pub use self::text::{TextAlign, TextAlignLast, TextEmphasisPosition, TextEmphasisStyle};
90 pub use self::text::{TextDecorationLength, TextDecorationSkipInk, TextJustify};
91 pub use self::time::Time;
92 pub use self::transform::{Rotate, Scale, Transform, TransformOperation};
93 pub use self::transform::{TransformOrigin, TransformStyle, Translate};
94 #[cfg(feature = "gecko")]
95 pub use self::ui::CursorImage;
96 pub use self::ui::{Cursor, MozForceBrokenImageIcon, UserSelect};
97 pub use super::specified::TextTransform;
98 pub use super::specified::{BorderStyle, TextDecorationLine};
99 pub use super::{Auto, Either, None_};
100 pub use app_units::Au;
101 
102 #[cfg(feature = "gecko")]
103 pub mod align;
104 pub mod angle;
105 pub mod background;
106 pub mod basic_shape;
107 pub mod border;
108 #[path = "box.rs"]
109 pub mod box_;
110 pub mod color;
111 pub mod column;
112 pub mod counters;
113 pub mod easing;
114 pub mod effects;
115 pub mod flex;
116 pub mod font;
117 pub mod image;
118 pub mod length;
119 pub mod length_percentage;
120 pub mod list;
121 pub mod motion;
122 pub mod outline;
123 pub mod page;
124 pub mod percentage;
125 pub mod position;
126 pub mod ratio;
127 pub mod rect;
128 pub mod resolution;
129 pub mod svg;
130 pub mod table;
131 pub mod text;
132 pub mod time;
133 pub mod transform;
134 pub mod ui;
135 pub mod url;
136 
137 /// A `Context` is all the data a specified value could ever need to compute
138 /// itself and be transformed to a computed value.
139 pub struct Context<'a> {
140     /// Values accessed through this need to be in the properties "computed
141     /// early": color, text-decoration, font-size, display, position, float,
142     /// border-*-style, outline-style, font-family, writing-mode...
143     pub builder: StyleBuilder<'a>,
144 
145     /// A cached computed system font value, for use by gecko.
146     ///
147     /// See properties/longhands/font.mako.rs
148     #[cfg(feature = "gecko")]
149     pub cached_system_font: Option<properties::longhands::system_font::ComputedSystemFont>,
150 
151     /// A dummy option for servo so initializing a computed::Context isn't
152     /// painful.
153     ///
154     /// TODO(emilio): Make constructors for Context, and drop this.
155     #[cfg(feature = "servo")]
156     pub cached_system_font: Option<()>,
157 
158     /// A font metrics provider, used to access font metrics to implement
159     /// font-relative units.
160     pub font_metrics_provider: &'a dyn FontMetricsProvider,
161 
162     /// Whether or not we are computing the media list in a media query
163     pub in_media_query: bool,
164 
165     /// The quirks mode of this context.
166     pub quirks_mode: QuirksMode,
167 
168     /// Whether this computation is being done for a SMIL animation.
169     ///
170     /// This is used to allow certain properties to generate out-of-range
171     /// values, which SMIL allows.
172     pub for_smil_animation: bool,
173 
174     /// The property we are computing a value for, if it is a non-inherited
175     /// property.  None if we are computed a value for an inherited property
176     /// or not computing for a property at all (e.g. in a media query
177     /// evaluation).
178     pub for_non_inherited_property: Option<LonghandId>,
179 
180     /// The conditions to cache a rule node on the rule cache.
181     ///
182     /// FIXME(emilio): Drop the refcell.
183     pub rule_cache_conditions: RefCell<&'a mut RuleCacheConditions>,
184 }
185 
186 impl<'a> Context<'a> {
187     /// Creates a suitable context for media query evaluation, in which
188     /// font-relative units compute against the system_font, and executes `f`
189     /// with it.
for_media_query_evaluation<F, R>(device: &Device, quirks_mode: QuirksMode, f: F) -> R where F: FnOnce(&Context) -> R,190     pub fn for_media_query_evaluation<F, R>(device: &Device, quirks_mode: QuirksMode, f: F) -> R
191     where
192         F: FnOnce(&Context) -> R,
193     {
194         let mut conditions = RuleCacheConditions::default();
195         let provider = get_metrics_provider_for_product();
196 
197         let context = Context {
198             builder: StyleBuilder::for_inheritance(device, None, None),
199             font_metrics_provider: &provider,
200             cached_system_font: None,
201             in_media_query: true,
202             quirks_mode,
203             for_smil_animation: false,
204             for_non_inherited_property: None,
205             rule_cache_conditions: RefCell::new(&mut conditions),
206         };
207 
208         f(&context)
209     }
210 
211     /// The current device.
device(&self) -> &Device212     pub fn device(&self) -> &Device {
213         self.builder.device
214     }
215 
216     /// The current viewport size, used to resolve viewport units.
viewport_size_for_viewport_unit_resolution(&self) -> default::Size2D<Au>217     pub fn viewport_size_for_viewport_unit_resolution(&self) -> default::Size2D<Au> {
218         self.builder
219             .device
220             .au_viewport_size_for_viewport_unit_resolution()
221     }
222 
223     /// The default computed style we're getting our reset style from.
default_style(&self) -> &ComputedValues224     pub fn default_style(&self) -> &ComputedValues {
225         self.builder.default_style()
226     }
227 
228     /// The current style.
style(&self) -> &StyleBuilder229     pub fn style(&self) -> &StyleBuilder {
230         &self.builder
231     }
232 
233     /// Apply text-zoom if enabled.
234     #[cfg(feature = "gecko")]
maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength235     pub fn maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength {
236         // We disable zoom for <svg:text> by unsetting the
237         // -x-text-zoom property, which leads to a false value
238         // in mAllowZoomAndMinSize
239         if self.style().get_font().gecko.mAllowZoomAndMinSize {
240             self.device().zoom_text(size)
241         } else {
242             size
243         }
244     }
245 
246     /// (Servo doesn't do text-zoom)
247     #[cfg(feature = "servo")]
maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength248     pub fn maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength {
249         size
250     }
251 }
252 
253 /// An iterator over a slice of computed values
254 #[derive(Clone)]
255 pub struct ComputedVecIter<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> {
256     cx: &'cx Context<'cx_a>,
257     values: &'a [S],
258 }
259 
260 impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> ComputedVecIter<'a, 'cx, 'cx_a, S> {
261     /// Construct an iterator from a slice of specified values and a context
new(cx: &'cx Context<'cx_a>, values: &'a [S]) -> Self262     pub fn new(cx: &'cx Context<'cx_a>, values: &'a [S]) -> Self {
263         ComputedVecIter {
264             cx: cx,
265             values: values,
266         }
267     }
268 }
269 
270 impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> ExactSizeIterator
271     for ComputedVecIter<'a, 'cx, 'cx_a, S>
272 {
len(&self) -> usize273     fn len(&self) -> usize {
274         self.values.len()
275     }
276 }
277 
278 impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> Iterator for ComputedVecIter<'a, 'cx, 'cx_a, S> {
279     type Item = S::ComputedValue;
next(&mut self) -> Option<Self::Item>280     fn next(&mut self) -> Option<Self::Item> {
281         if let Some((next, rest)) = self.values.split_first() {
282             let ret = next.to_computed_value(self.cx);
283             self.values = rest;
284             Some(ret)
285         } else {
286             None
287         }
288     }
289 
size_hint(&self) -> (usize, Option<usize>)290     fn size_hint(&self) -> (usize, Option<usize>) {
291         (self.values.len(), Some(self.values.len()))
292     }
293 }
294 
295 /// A trait to represent the conversion between computed and specified values.
296 ///
297 /// This trait is derivable with `#[derive(ToComputedValue)]`. The derived
298 /// implementation just calls `ToComputedValue::to_computed_value` on each field
299 /// of the passed value. The deriving code assumes that if the type isn't
300 /// generic, then the trait can be implemented as simple `Clone::clone` calls,
301 /// this means that a manual implementation with `ComputedValue = Self` is bogus
302 /// if it returns anything else than a clone.
303 pub trait ToComputedValue {
304     /// The computed value type we're going to be converted to.
305     type ComputedValue;
306 
307     /// Convert a specified value to a computed value, using itself and the data
308     /// inside the `Context`.
to_computed_value(&self, context: &Context) -> Self::ComputedValue309     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue;
310 
311     /// Convert a computed value to specified value form.
312     ///
313     /// This will be used for recascading during animation.
314     /// Such from_computed_valued values should recompute to the same value.
from_computed_value(computed: &Self::ComputedValue) -> Self315     fn from_computed_value(computed: &Self::ComputedValue) -> Self;
316 }
317 
318 impl<A, B> ToComputedValue for (A, B)
319 where
320     A: ToComputedValue,
321     B: ToComputedValue,
322 {
323     type ComputedValue = (
324         <A as ToComputedValue>::ComputedValue,
325         <B as ToComputedValue>::ComputedValue,
326     );
327 
328     #[inline]
to_computed_value(&self, context: &Context) -> Self::ComputedValue329     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
330         (
331             self.0.to_computed_value(context),
332             self.1.to_computed_value(context),
333         )
334     }
335 
336     #[inline]
from_computed_value(computed: &Self::ComputedValue) -> Self337     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
338         (
339             A::from_computed_value(&computed.0),
340             B::from_computed_value(&computed.1),
341         )
342     }
343 }
344 
345 impl<T> ToComputedValue for Option<T>
346 where
347     T: ToComputedValue,
348 {
349     type ComputedValue = Option<<T as ToComputedValue>::ComputedValue>;
350 
351     #[inline]
to_computed_value(&self, context: &Context) -> Self::ComputedValue352     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
353         self.as_ref().map(|item| item.to_computed_value(context))
354     }
355 
356     #[inline]
from_computed_value(computed: &Self::ComputedValue) -> Self357     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
358         computed.as_ref().map(T::from_computed_value)
359     }
360 }
361 
362 impl<T> ToComputedValue for default::Size2D<T>
363 where
364     T: ToComputedValue,
365 {
366     type ComputedValue = default::Size2D<<T as ToComputedValue>::ComputedValue>;
367 
368     #[inline]
to_computed_value(&self, context: &Context) -> Self::ComputedValue369     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
370         Size2D::new(
371             self.width.to_computed_value(context),
372             self.height.to_computed_value(context),
373         )
374     }
375 
376     #[inline]
from_computed_value(computed: &Self::ComputedValue) -> Self377     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
378         Size2D::new(
379             T::from_computed_value(&computed.width),
380             T::from_computed_value(&computed.height),
381         )
382     }
383 }
384 
385 impl<T> ToComputedValue for Vec<T>
386 where
387     T: ToComputedValue,
388 {
389     type ComputedValue = Vec<<T as ToComputedValue>::ComputedValue>;
390 
391     #[inline]
to_computed_value(&self, context: &Context) -> Self::ComputedValue392     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
393         self.iter()
394             .map(|item| item.to_computed_value(context))
395             .collect()
396     }
397 
398     #[inline]
from_computed_value(computed: &Self::ComputedValue) -> Self399     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
400         computed.iter().map(T::from_computed_value).collect()
401     }
402 }
403 
404 impl<T> ToComputedValue for Box<T>
405 where
406     T: ToComputedValue,
407 {
408     type ComputedValue = Box<<T as ToComputedValue>::ComputedValue>;
409 
410     #[inline]
to_computed_value(&self, context: &Context) -> Self::ComputedValue411     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
412         Box::new(T::to_computed_value(self, context))
413     }
414 
415     #[inline]
from_computed_value(computed: &Self::ComputedValue) -> Self416     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
417         Box::new(T::from_computed_value(computed))
418     }
419 }
420 
421 impl<T> ToComputedValue for Box<[T]>
422 where
423     T: ToComputedValue,
424 {
425     type ComputedValue = Box<[<T as ToComputedValue>::ComputedValue]>;
426 
427     #[inline]
to_computed_value(&self, context: &Context) -> Self::ComputedValue428     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
429         self.iter()
430             .map(|item| item.to_computed_value(context))
431             .collect::<Vec<_>>()
432             .into_boxed_slice()
433     }
434 
435     #[inline]
from_computed_value(computed: &Self::ComputedValue) -> Self436     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
437         computed
438             .iter()
439             .map(T::from_computed_value)
440             .collect::<Vec<_>>()
441             .into_boxed_slice()
442     }
443 }
444 
445 impl<T> ToComputedValue for crate::OwnedSlice<T>
446 where
447     T: ToComputedValue,
448 {
449     type ComputedValue = crate::OwnedSlice<<T as ToComputedValue>::ComputedValue>;
450 
451     #[inline]
to_computed_value(&self, context: &Context) -> Self::ComputedValue452     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
453         self.iter()
454             .map(|item| item.to_computed_value(context))
455             .collect()
456     }
457 
458     #[inline]
from_computed_value(computed: &Self::ComputedValue) -> Self459     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
460         computed.iter().map(T::from_computed_value).collect()
461     }
462 }
463 
464 // NOTE(emilio): This is implementable more generically, but it's unlikely
465 // what you want there, as it forces you to have an extra allocation.
466 //
467 // We could do that if needed, ideally with specialization for the case where
468 // ComputedValue = T. But we don't need it for now.
469 impl<T> ToComputedValue for Arc<T>
470 where
471     T: ToComputedValue<ComputedValue = T>,
472 {
473     type ComputedValue = Self;
474 
475     #[inline]
to_computed_value(&self, _: &Context) -> Self476     fn to_computed_value(&self, _: &Context) -> Self {
477         self.clone()
478     }
479 
480     #[inline]
from_computed_value(computed: &Self) -> Self481     fn from_computed_value(computed: &Self) -> Self {
482         computed.clone()
483     }
484 }
485 
486 // Same caveat as above applies.
487 impl<T> ToComputedValue for ArcSlice<T>
488 where
489     T: ToComputedValue<ComputedValue = T>,
490 {
491     type ComputedValue = Self;
492 
493     #[inline]
to_computed_value(&self, _: &Context) -> Self494     fn to_computed_value(&self, _: &Context) -> Self {
495         self.clone()
496     }
497 
498     #[inline]
from_computed_value(computed: &Self) -> Self499     fn from_computed_value(computed: &Self) -> Self {
500         computed.clone()
501     }
502 }
503 
504 trivial_to_computed_value!(());
505 trivial_to_computed_value!(bool);
506 trivial_to_computed_value!(f32);
507 trivial_to_computed_value!(i32);
508 trivial_to_computed_value!(u8);
509 trivial_to_computed_value!(u16);
510 trivial_to_computed_value!(u32);
511 trivial_to_computed_value!(usize);
512 trivial_to_computed_value!(Atom);
513 trivial_to_computed_value!(crate::values::AtomIdent);
514 #[cfg(feature = "servo")]
515 trivial_to_computed_value!(crate::Namespace);
516 #[cfg(feature = "servo")]
517 trivial_to_computed_value!(crate::Prefix);
518 trivial_to_computed_value!(String);
519 trivial_to_computed_value!(Box<str>);
520 trivial_to_computed_value!(crate::OwnedStr);
521 trivial_to_computed_value!(style_traits::values::specified::AllowedNumericType);
522 
523 #[allow(missing_docs)]
524 #[derive(
525     Animate,
526     Clone,
527     ComputeSquaredDistance,
528     Copy,
529     Debug,
530     MallocSizeOf,
531     PartialEq,
532     ToAnimatedZero,
533     ToCss,
534     ToResolvedValue,
535 )]
536 #[repr(C, u8)]
537 pub enum AngleOrPercentage {
538     Percentage(Percentage),
539     Angle(Angle),
540 }
541 
542 impl ToComputedValue for specified::AngleOrPercentage {
543     type ComputedValue = AngleOrPercentage;
544 
545     #[inline]
to_computed_value(&self, context: &Context) -> AngleOrPercentage546     fn to_computed_value(&self, context: &Context) -> AngleOrPercentage {
547         match *self {
548             specified::AngleOrPercentage::Percentage(percentage) => {
549                 AngleOrPercentage::Percentage(percentage.to_computed_value(context))
550             },
551             specified::AngleOrPercentage::Angle(angle) => {
552                 AngleOrPercentage::Angle(angle.to_computed_value(context))
553             },
554         }
555     }
556     #[inline]
from_computed_value(computed: &AngleOrPercentage) -> Self557     fn from_computed_value(computed: &AngleOrPercentage) -> Self {
558         match *computed {
559             AngleOrPercentage::Percentage(percentage) => specified::AngleOrPercentage::Percentage(
560                 ToComputedValue::from_computed_value(&percentage),
561             ),
562             AngleOrPercentage::Angle(angle) => {
563                 specified::AngleOrPercentage::Angle(ToComputedValue::from_computed_value(&angle))
564             },
565         }
566     }
567 }
568 
569 /// A `<number>` value.
570 pub type Number = CSSFloat;
571 
572 impl IsParallelTo for (Number, Number, Number) {
is_parallel_to(&self, vector: &DirectionVector) -> bool573     fn is_parallel_to(&self, vector: &DirectionVector) -> bool {
574         use euclid::approxeq::ApproxEq;
575         // If a and b is parallel, the angle between them is 0deg, so
576         // a x b = |a|*|b|*sin(0)*n = 0 * n, |a x b| == 0.
577         let self_vector = DirectionVector::new(self.0, self.1, self.2);
578         self_vector
579             .cross(*vector)
580             .square_length()
581             .approx_eq(&0.0f32)
582     }
583 }
584 
585 /// A wrapper of Number, but the value >= 0.
586 pub type NonNegativeNumber = NonNegative<CSSFloat>;
587 
588 impl ToAnimatedValue for NonNegativeNumber {
589     type AnimatedValue = CSSFloat;
590 
591     #[inline]
to_animated_value(self) -> Self::AnimatedValue592     fn to_animated_value(self) -> Self::AnimatedValue {
593         self.0
594     }
595 
596     #[inline]
from_animated_value(animated: Self::AnimatedValue) -> Self597     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
598         animated.max(0.).into()
599     }
600 }
601 
602 impl From<CSSFloat> for NonNegativeNumber {
603     #[inline]
from(number: CSSFloat) -> NonNegativeNumber604     fn from(number: CSSFloat) -> NonNegativeNumber {
605         NonNegative::<CSSFloat>(number)
606     }
607 }
608 
609 impl From<NonNegativeNumber> for CSSFloat {
610     #[inline]
from(number: NonNegativeNumber) -> CSSFloat611     fn from(number: NonNegativeNumber) -> CSSFloat {
612         number.0
613     }
614 }
615 
616 impl One for NonNegativeNumber {
617     #[inline]
one() -> Self618     fn one() -> Self {
619         NonNegative(1.0)
620     }
621 
622     #[inline]
is_one(&self) -> bool623     fn is_one(&self) -> bool {
624         self.0 == 1.0
625     }
626 }
627 
628 /// A wrapper of Number, but the value between 0 and 1
629 pub type ZeroToOneNumber = ZeroToOne<CSSFloat>;
630 
631 impl ToAnimatedValue for ZeroToOneNumber {
632     type AnimatedValue = CSSFloat;
633 
634     #[inline]
to_animated_value(self) -> Self::AnimatedValue635     fn to_animated_value(self) -> Self::AnimatedValue {
636         self.0
637     }
638 
639     #[inline]
from_animated_value(animated: Self::AnimatedValue) -> Self640     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
641         Self(animated.max(0.).min(1.))
642     }
643 }
644 
645 impl From<CSSFloat> for ZeroToOneNumber {
646     #[inline]
from(number: CSSFloat) -> Self647     fn from(number: CSSFloat) -> Self {
648         Self(number)
649     }
650 }
651 
652 /// A wrapper of Number, but the value >= 1.
653 pub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<CSSFloat>;
654 
655 impl ToAnimatedValue for GreaterThanOrEqualToOneNumber {
656     type AnimatedValue = CSSFloat;
657 
658     #[inline]
to_animated_value(self) -> Self::AnimatedValue659     fn to_animated_value(self) -> Self::AnimatedValue {
660         self.0
661     }
662 
663     #[inline]
from_animated_value(animated: Self::AnimatedValue) -> Self664     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
665         animated.max(1.).into()
666     }
667 }
668 
669 impl From<CSSFloat> for GreaterThanOrEqualToOneNumber {
670     #[inline]
from(number: CSSFloat) -> GreaterThanOrEqualToOneNumber671     fn from(number: CSSFloat) -> GreaterThanOrEqualToOneNumber {
672         GreaterThanOrEqualToOne::<CSSFloat>(number)
673     }
674 }
675 
676 impl From<GreaterThanOrEqualToOneNumber> for CSSFloat {
677     #[inline]
from(number: GreaterThanOrEqualToOneNumber) -> CSSFloat678     fn from(number: GreaterThanOrEqualToOneNumber) -> CSSFloat {
679         number.0
680     }
681 }
682 
683 #[allow(missing_docs)]
684 #[derive(
685     Animate,
686     Clone,
687     ComputeSquaredDistance,
688     Copy,
689     Debug,
690     MallocSizeOf,
691     PartialEq,
692     ToAnimatedZero,
693     ToCss,
694     ToResolvedValue,
695 )]
696 #[repr(C, u8)]
697 pub enum NumberOrPercentage {
698     Percentage(Percentage),
699     Number(Number),
700 }
701 
702 impl NumberOrPercentage {
clamp_to_non_negative(self) -> Self703     fn clamp_to_non_negative(self) -> Self {
704         match self {
705             NumberOrPercentage::Percentage(p) => {
706                 NumberOrPercentage::Percentage(p.clamp_to_non_negative())
707             },
708             NumberOrPercentage::Number(n) => NumberOrPercentage::Number(n.max(0.)),
709         }
710     }
711 }
712 
713 impl ToComputedValue for specified::NumberOrPercentage {
714     type ComputedValue = NumberOrPercentage;
715 
716     #[inline]
to_computed_value(&self, context: &Context) -> NumberOrPercentage717     fn to_computed_value(&self, context: &Context) -> NumberOrPercentage {
718         match *self {
719             specified::NumberOrPercentage::Percentage(percentage) => {
720                 NumberOrPercentage::Percentage(percentage.to_computed_value(context))
721             },
722             specified::NumberOrPercentage::Number(number) => {
723                 NumberOrPercentage::Number(number.to_computed_value(context))
724             },
725         }
726     }
727     #[inline]
from_computed_value(computed: &NumberOrPercentage) -> Self728     fn from_computed_value(computed: &NumberOrPercentage) -> Self {
729         match *computed {
730             NumberOrPercentage::Percentage(percentage) => {
731                 specified::NumberOrPercentage::Percentage(ToComputedValue::from_computed_value(
732                     &percentage,
733                 ))
734             },
735             NumberOrPercentage::Number(number) => {
736                 specified::NumberOrPercentage::Number(ToComputedValue::from_computed_value(&number))
737             },
738         }
739     }
740 }
741 
742 /// A non-negative <number-percentage>.
743 pub type NonNegativeNumberOrPercentage = NonNegative<NumberOrPercentage>;
744 
745 impl NonNegativeNumberOrPercentage {
746     /// Returns the `100%` value.
747     #[inline]
hundred_percent() -> Self748     pub fn hundred_percent() -> Self {
749         NonNegative(NumberOrPercentage::Percentage(Percentage::hundred()))
750     }
751 }
752 
753 impl ToAnimatedValue for NonNegativeNumberOrPercentage {
754     type AnimatedValue = NumberOrPercentage;
755 
756     #[inline]
to_animated_value(self) -> Self::AnimatedValue757     fn to_animated_value(self) -> Self::AnimatedValue {
758         self.0
759     }
760 
761     #[inline]
from_animated_value(animated: Self::AnimatedValue) -> Self762     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
763         NonNegative(animated.clamp_to_non_negative())
764     }
765 }
766 
767 /// A type used for opacity.
768 pub type Opacity = CSSFloat;
769 
770 /// A `<integer>` value.
771 pub type Integer = CSSInteger;
772 
773 /// A wrapper of Integer, but only accept a value >= 1.
774 pub type PositiveInteger = GreaterThanOrEqualToOne<CSSInteger>;
775 
776 impl ToAnimatedValue for PositiveInteger {
777     type AnimatedValue = CSSInteger;
778 
779     #[inline]
to_animated_value(self) -> Self::AnimatedValue780     fn to_animated_value(self) -> Self::AnimatedValue {
781         self.0
782     }
783 
784     #[inline]
from_animated_value(animated: Self::AnimatedValue) -> Self785     fn from_animated_value(animated: Self::AnimatedValue) -> Self {
786         cmp::max(animated, 1).into()
787     }
788 }
789 
790 impl From<CSSInteger> for PositiveInteger {
791     #[inline]
from(int: CSSInteger) -> PositiveInteger792     fn from(int: CSSInteger) -> PositiveInteger {
793         GreaterThanOrEqualToOne::<CSSInteger>(int)
794     }
795 }
796 
797 /// A computed positive `<integer>` value or `none`.
798 pub type PositiveIntegerOrNone = Either<PositiveInteger, None_>;
799 
800 /// rect(...) | auto
801 pub type ClipRect = generics::GenericClipRect<LengthOrAuto>;
802 
803 /// rect(...) | auto
804 pub type ClipRectOrAuto = generics::GenericClipRectOrAuto<ClipRect>;
805 
806 /// The computed value of a grid `<track-breadth>`
807 pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;
808 
809 /// The computed value of a grid `<track-size>`
810 pub type TrackSize = GenericTrackSize<LengthPercentage>;
811 
812 /// The computed value of a grid `<track-size>+`
813 pub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>;
814 
815 /// The computed value of a grid `<track-list>`
816 /// (could also be `<auto-track-list>` or `<explicit-track-list>`)
817 pub type TrackList = GenericTrackList<LengthPercentage, Integer>;
818 
819 /// The computed value of a `<grid-line>`.
820 pub type GridLine = GenericGridLine<Integer>;
821 
822 /// `<grid-template-rows> | <grid-template-columns>`
823 pub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>;
824 
825 impl ClipRect {
826     /// Given a border box, resolves the clip rect against the border box
827     /// in the same space the border box is in
for_border_rect<T: Copy + From<Length> + Add<Output = T> + Sub<Output = T>, U>( &self, border_box: Rect<T, U>, ) -> Rect<T, U>828     pub fn for_border_rect<T: Copy + From<Length> + Add<Output = T> + Sub<Output = T>, U>(
829         &self,
830         border_box: Rect<T, U>,
831     ) -> Rect<T, U> {
832         fn extract_clip_component<T: From<Length>>(p: &LengthOrAuto, or: T) -> T {
833             match *p {
834                 LengthOrAuto::Auto => or,
835                 LengthOrAuto::LengthPercentage(ref length) => T::from(*length),
836             }
837         }
838 
839         let clip_origin = Point2D::new(
840             From::from(self.left.auto_is(|| Length::new(0.))),
841             From::from(self.top.auto_is(|| Length::new(0.))),
842         );
843         let right = extract_clip_component(&self.right, border_box.size.width);
844         let bottom = extract_clip_component(&self.bottom, border_box.size.height);
845         let clip_size = Size2D::new(right - clip_origin.x, bottom - clip_origin.y);
846 
847         Rect::new(clip_origin, clip_size).translate(border_box.origin.to_vector())
848     }
849 }
850