style/values/computed/
mod.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//! Computed values.
6
7use self::transform::DirectionVector;
8use super::animated::ToAnimatedValue;
9use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
10use super::generics::grid::ImplicitGridTracks as GenericImplicitGridTracks;
11use super::generics::grid::{GenericGridLine, GenericTrackBreadth};
12use super::generics::grid::{GenericTrackSize, TrackList as GenericTrackList};
13use super::generics::transform::IsParallelTo;
14use super::generics::{self, GreaterThanOrEqualToOne, NonNegative, ZeroToOne};
15use super::specified;
16use super::{CSSFloat, CSSInteger};
17use crate::computed_value_flags::ComputedValueFlags;
18use crate::context::QuirksMode;
19use crate::custom_properties::ComputedCustomProperties;
20use crate::derives::*;
21use crate::font_metrics::{FontMetrics, FontMetricsOrientation};
22use crate::media_queries::Device;
23#[cfg(feature = "gecko")]
24use crate::properties;
25use crate::properties::{ComputedValues, StyleBuilder};
26use crate::rule_cache::RuleCacheConditions;
27use crate::stylesheets::container_rule::{
28    ContainerInfo, ContainerSizeQuery, ContainerSizeQueryResult,
29};
30use crate::stylist::Stylist;
31use crate::values::generics::ClampToNonNegative;
32use crate::values::specified::font::QueryFontMetricsFlags;
33use crate::values::specified::length::FontBaseSize;
34use crate::{ArcSlice, Atom, One};
35use euclid::{default, Point2D, Rect, Size2D};
36use servo_arc::Arc;
37use std::cell::RefCell;
38use std::cmp;
39use std::f32;
40use std::ops::{Add, Sub};
41
42pub use self::align::{ContentDistribution, ItemPlacement, JustifyItems, SelfAlignment};
43pub use self::angle::Angle;
44pub use self::animation::{
45    AnimationComposition, AnimationDirection, AnimationDuration, AnimationFillMode,
46    AnimationIterationCount, AnimationName, AnimationPlayState, AnimationTimeline, ScrollAxis,
47    TimelineName, TransitionBehavior, TransitionProperty, ViewTimelineInset, ViewTransitionClass,
48    ViewTransitionName,
49};
50pub use self::background::{BackgroundRepeat, BackgroundSize};
51pub use self::basic_shape::FillRule;
52pub use self::border::{
53    BorderCornerRadius, BorderImageRepeat, BorderImageSideWidth, BorderImageSlice,
54    BorderImageWidth, BorderRadius, BorderSideOffset, BorderSideWidth, BorderSpacing, LineWidth,
55};
56pub use self::box_::{
57    Appearance, BaselineSource, BreakBetween, BreakWithin, Clear, Contain, ContainIntrinsicSize,
58    ContainerName, ContainerType, ContentVisibility, Display, Float, LineClamp, Overflow,
59    OverflowAnchor, OverflowClipMargin, OverscrollBehavior, Perspective, PositionProperty, Resize,
60    ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType,
61    ScrollbarGutter, TouchAction, VerticalAlign, WillChange, WritingModeProperty, Zoom,
62};
63pub use self::color::{
64    Color, ColorOrAuto, ColorPropertyValue, ColorScheme, ForcedColorAdjust, PrintColorAdjust,
65};
66pub use self::column::ColumnCount;
67pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset, CounterSet};
68pub use self::easing::TimingFunction;
69pub use self::effects::{BoxShadow, Filter, SimpleShadow};
70pub use self::flex::FlexBasis;
71pub use self::font::{FontFamily, FontLanguageOverride, FontPalette, FontStyle};
72pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric};
73pub use self::font::{
74    FontSize, FontSizeAdjust, FontStretch, FontSynthesis, FontSynthesisStyle, LineHeight,
75};
76pub use self::font::{FontVariantAlternates, FontWeight};
77pub use self::font::{FontVariantEastAsian, FontVariationSettings};
78pub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextScale};
79pub use self::image::{Gradient, Image, ImageRendering, LineDirection};
80pub use self::length::{CSSPixelLength, NonNegativeLength};
81pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber};
82pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, Margin, MaxSize, Size};
83pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto};
84#[cfg(feature = "gecko")]
85pub use self::list::ListStyleType;
86pub use self::list::Quotes;
87pub use self::motion::{OffsetPath, OffsetPosition, OffsetRotate};
88pub use self::outline::OutlineStyle;
89pub use self::page::{PageName, PageOrientation, PageSize, PageSizeOrientation, PaperSize};
90pub use self::percentage::{NonNegativePercentage, Percentage};
91pub use self::position::AnchorFunction;
92pub use self::position::AnchorName;
93pub use self::position::AnchorScope;
94pub use self::position::AspectRatio;
95pub use self::position::DashedIdentAndOrTryTactic;
96pub use self::position::Inset;
97pub use self::position::PositionAnchor;
98pub use self::position::PositionTryFallbacks;
99pub use self::position::PositionTryOrder;
100pub use self::position::PositionVisibility;
101pub use self::position::{
102    GridAutoFlow, GridTemplateAreas, MasonryAutoFlow, Position, PositionOrAuto, ZIndex,
103};
104pub use self::position::{PositionArea, PositionAreaKeyword};
105pub use self::ratio::Ratio;
106pub use self::rect::NonNegativeLengthOrNumberRect;
107pub use self::resolution::Resolution;
108pub use self::svg::{DProperty, MozContextProperties};
109pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};
110pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth, VectorEffect};
111pub use self::text::{HyphenateCharacter, HyphenateLimitChars};
112pub use self::text::{InitialLetter, LetterSpacing, LineBreak, TextIndent};
113pub use self::text::{OverflowWrap, RubyPosition, TextOverflow, WordBreak, WordSpacing};
114pub use self::text::{TextAlign, TextAlignLast, TextEmphasisPosition, TextEmphasisStyle};
115pub use self::text::{TextAutospace, TextUnderlinePosition};
116pub use self::text::{
117    TextDecorationInset, TextDecorationLength, TextDecorationSkipInk, TextJustify,
118};
119pub use self::time::Time;
120pub use self::transform::{Rotate, Scale, Transform, TransformBox, TransformOperation};
121pub use self::transform::{TransformOrigin, TransformStyle, Translate};
122#[cfg(feature = "gecko")]
123pub use self::ui::CursorImage;
124pub use self::ui::{
125    BoolInteger, Cursor, Inert, MozTheme, PointerEvents, ScrollbarColor, UserFocus, UserSelect,
126};
127pub use super::specified::TextTransform;
128pub use super::specified::ViewportVariant;
129pub use super::specified::{BorderStyle, TextDecorationLine};
130pub use app_units::Au;
131
132pub mod align;
133pub mod angle;
134pub mod animation;
135pub mod background;
136pub mod basic_shape;
137pub mod border;
138#[path = "box.rs"]
139pub mod box_;
140pub mod color;
141pub mod column;
142pub mod counters;
143pub mod easing;
144pub mod effects;
145pub mod flex;
146pub mod font;
147pub mod image;
148pub mod length;
149pub mod length_percentage;
150pub mod list;
151pub mod motion;
152pub mod outline;
153pub mod page;
154pub mod percentage;
155pub mod position;
156pub mod ratio;
157pub mod rect;
158pub mod resolution;
159pub mod svg;
160pub mod table;
161pub mod text;
162pub mod time;
163pub mod transform;
164pub mod ui;
165pub mod url;
166
167/// A `Context` is all the data a specified value could ever need to compute
168/// itself and be transformed to a computed value.
169pub struct Context<'a> {
170    /// Values accessed through this need to be in the properties "computed
171    /// early": color, text-decoration, font-size, display, position, float,
172    /// border-*-style, outline-style, font-family, writing-mode...
173    pub builder: StyleBuilder<'a>,
174
175    /// A cached computed system font value, for use by gecko.
176    ///
177    /// See properties/longhands/font.mako.rs
178    #[cfg(feature = "gecko")]
179    pub cached_system_font: Option<properties::longhands::system_font::ComputedSystemFont>,
180
181    /// A dummy option for servo so initializing a computed::Context isn't
182    /// painful.
183    ///
184    /// TODO(emilio): Make constructors for Context, and drop this.
185    #[cfg(feature = "servo")]
186    pub cached_system_font: Option<()>,
187
188    /// Whether or not we are computing the media list in a media query.
189    pub in_media_query: bool,
190
191    /// Whether or not we are computing the container query condition.
192    pub in_container_query: bool,
193
194    /// The quirks mode of this context.
195    pub quirks_mode: QuirksMode,
196
197    /// Whether this computation is being done for a SMIL animation.
198    ///
199    /// This is used to allow certain properties to generate out-of-range
200    /// values, which SMIL allows.
201    pub for_smil_animation: bool,
202
203    /// Returns the container information to evaluate a given container query.
204    pub container_info: Option<ContainerInfo>,
205
206    /// Whether we're computing a value for a non-inherited property.
207    /// False if we are computed a value for an inherited property or not computing for a property
208    /// at all (e.g. in a media query evaluation).
209    pub for_non_inherited_property: bool,
210
211    /// The conditions to cache a rule node on the rule cache.
212    ///
213    /// FIXME(emilio): Drop the refcell.
214    pub rule_cache_conditions: RefCell<&'a mut RuleCacheConditions>,
215
216    /// Container size query for this context.
217    container_size_query: RefCell<ContainerSizeQuery<'a>>,
218}
219
220impl<'a> Context<'a> {
221    /// Lazily evaluate the container size query, returning the result.
222    pub fn get_container_size_query(&self) -> ContainerSizeQueryResult {
223        let mut resolved = self.container_size_query.borrow_mut();
224        resolved.get().clone()
225    }
226
227    /// Creates a suitable context for media query evaluation, in which
228    /// font-relative units compute against the system_font, and executes `f`
229    /// with it.
230    pub fn for_media_query_evaluation<F, R>(device: &Device, quirks_mode: QuirksMode, f: F) -> R
231    where
232        F: FnOnce(&Context) -> R,
233    {
234        let mut conditions = RuleCacheConditions::default();
235        let context = Context {
236            builder: StyleBuilder::for_inheritance(device, None, None, None),
237            cached_system_font: None,
238            in_media_query: true,
239            in_container_query: false,
240            quirks_mode,
241            for_smil_animation: false,
242            container_info: None,
243            for_non_inherited_property: false,
244            rule_cache_conditions: RefCell::new(&mut conditions),
245            container_size_query: RefCell::new(ContainerSizeQuery::none()),
246        };
247        f(&context)
248    }
249
250    /// Creates a suitable context for container query evaluation for the style
251    /// specified.
252    pub fn for_container_query_evaluation<F, R>(
253        device: &Device,
254        stylist: Option<&Stylist>,
255        container_info_and_style: Option<(ContainerInfo, Arc<ComputedValues>)>,
256        container_size_query: ContainerSizeQuery,
257        f: F,
258    ) -> R
259    where
260        F: FnOnce(&Context) -> R,
261    {
262        let mut conditions = RuleCacheConditions::default();
263
264        let (container_info, style) = match container_info_and_style {
265            Some((ci, s)) => (Some(ci), Some(s)),
266            None => (None, None),
267        };
268
269        let style = style.as_ref().map(|s| &**s);
270        let quirks_mode = device.quirks_mode();
271        let context = Context {
272            builder: StyleBuilder::for_inheritance(device, stylist, style, None),
273            cached_system_font: None,
274            in_media_query: false,
275            in_container_query: true,
276            quirks_mode,
277            for_smil_animation: false,
278            container_info,
279            for_non_inherited_property: false,
280            rule_cache_conditions: RefCell::new(&mut conditions),
281            container_size_query: RefCell::new(container_size_query),
282        };
283
284        f(&context)
285    }
286
287    /// Creates a context suitable for more general cases.
288    pub fn new(
289        builder: StyleBuilder<'a>,
290        quirks_mode: QuirksMode,
291        rule_cache_conditions: &'a mut RuleCacheConditions,
292        container_size_query: ContainerSizeQuery<'a>,
293    ) -> Self {
294        Self {
295            builder,
296            cached_system_font: None,
297            in_media_query: false,
298            in_container_query: false,
299            quirks_mode,
300            container_info: None,
301            for_smil_animation: false,
302            for_non_inherited_property: false,
303            rule_cache_conditions: RefCell::new(rule_cache_conditions),
304            container_size_query: RefCell::new(container_size_query),
305        }
306    }
307
308    /// Creates a context suitable for computing animations.
309    pub fn new_for_animation(
310        builder: StyleBuilder<'a>,
311        for_smil_animation: bool,
312        quirks_mode: QuirksMode,
313        rule_cache_conditions: &'a mut RuleCacheConditions,
314        container_size_query: ContainerSizeQuery<'a>,
315    ) -> Self {
316        Self {
317            builder,
318            cached_system_font: None,
319            in_media_query: false,
320            in_container_query: false,
321            quirks_mode,
322            container_info: None,
323            for_smil_animation,
324            for_non_inherited_property: false,
325            rule_cache_conditions: RefCell::new(rule_cache_conditions),
326            container_size_query: RefCell::new(container_size_query),
327        }
328    }
329
330    /// Creates a context suitable for computing the initial value of @property.
331    pub fn new_for_initial_at_property_value(
332        stylist: &'a Stylist,
333        rule_cache_conditions: &'a mut RuleCacheConditions,
334    ) -> Self {
335        Self {
336            builder: StyleBuilder::new(stylist.device(), Some(stylist), None, None, None, false),
337            cached_system_font: None,
338            // Because font-relative values are disallowed in @property initial values, we do not
339            // need to keep track of whether we're in a media query, whether we're in a container
340            // query, and so on.
341            in_media_query: false,
342            in_container_query: false,
343            quirks_mode: stylist.quirks_mode(),
344            container_info: None,
345            for_smil_animation: false,
346            for_non_inherited_property: false,
347            rule_cache_conditions: RefCell::new(rule_cache_conditions),
348            container_size_query: RefCell::new(ContainerSizeQuery::none()),
349        }
350    }
351
352    /// The current device.
353    pub fn device(&self) -> &Device {
354        self.builder.device
355    }
356
357    /// Get the inherited custom properties map.
358    pub fn inherited_custom_properties(&self) -> &ComputedCustomProperties {
359        &self.builder.inherited_custom_properties()
360    }
361
362    /// Whether the style is for the root element.
363    pub fn is_root_element(&self) -> bool {
364        self.builder.is_root_element
365    }
366
367    /// Queries font metrics.
368    pub fn query_font_metrics(
369        &self,
370        base_size: FontBaseSize,
371        orientation: FontMetricsOrientation,
372        mut flags: QueryFontMetricsFlags,
373    ) -> FontMetrics {
374        if self.for_non_inherited_property {
375            self.rule_cache_conditions.borrow_mut().set_uncacheable();
376        }
377        self.builder.add_flags(match base_size {
378            FontBaseSize::CurrentStyle => ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS,
379            FontBaseSize::InheritedStyle => ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS,
380        });
381        let size = base_size.resolve(self).used_size();
382        let style = self.style();
383
384        let (wm, font) = match base_size {
385            FontBaseSize::CurrentStyle => (style.writing_mode, style.get_font()),
386            // This is only used for font-size computation.
387            FontBaseSize::InheritedStyle => {
388                (*style.inherited_writing_mode(), style.get_parent_font())
389            },
390        };
391
392        let vertical = match orientation {
393            FontMetricsOrientation::MatchContextPreferHorizontal => {
394                wm.is_vertical() && wm.is_upright()
395            },
396            FontMetricsOrientation::MatchContextPreferVertical => wm.is_text_vertical(),
397            FontMetricsOrientation::Horizontal => false,
398        };
399        if !self.in_media_query {
400            flags |= QueryFontMetricsFlags::USE_USER_FONT_SET
401        }
402        self.device()
403            .query_font_metrics(vertical, font, size, flags, /* track_changes = */ true)
404    }
405
406    /// The current viewport size, used to resolve viewport units.
407    pub fn viewport_size_for_viewport_unit_resolution(
408        &self,
409        variant: ViewportVariant,
410    ) -> default::Size2D<Au> {
411        self.builder
412            .add_flags(ComputedValueFlags::USES_VIEWPORT_UNITS);
413        self.builder
414            .device
415            .au_viewport_size_for_viewport_unit_resolution(variant)
416    }
417
418    /// Whether we're in a media or container query.
419    pub fn in_media_or_container_query(&self) -> bool {
420        self.in_media_query || self.in_container_query
421    }
422
423    /// The default computed style we're getting our reset style from.
424    pub fn default_style(&self) -> &ComputedValues {
425        self.builder.default_style()
426    }
427
428    /// The current style.
429    pub fn style(&self) -> &StyleBuilder<'a> {
430        &self.builder
431    }
432
433    /// Apply text-zoom if enabled.
434    #[cfg(feature = "gecko")]
435    pub fn maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength {
436        if self
437            .style()
438            .get_font()
439            .clone__x_text_scale()
440            .text_zoom_enabled()
441        {
442            self.device().zoom_text(size)
443        } else {
444            size
445        }
446    }
447
448    /// (Servo doesn't do text-zoom)
449    #[cfg(feature = "servo")]
450    pub fn maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength {
451        size
452    }
453}
454
455/// An iterator over a slice of computed values
456#[derive(Clone)]
457pub struct ComputedVecIter<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> {
458    cx: &'cx Context<'cx_a>,
459    values: &'a [S],
460}
461
462impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> ComputedVecIter<'a, 'cx, 'cx_a, S> {
463    /// Construct an iterator from a slice of specified values and a context
464    pub fn new(cx: &'cx Context<'cx_a>, values: &'a [S]) -> Self {
465        ComputedVecIter { cx, values }
466    }
467}
468
469impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> ExactSizeIterator
470    for ComputedVecIter<'a, 'cx, 'cx_a, S>
471{
472    fn len(&self) -> usize {
473        self.values.len()
474    }
475}
476
477impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> Iterator for ComputedVecIter<'a, 'cx, 'cx_a, S> {
478    type Item = S::ComputedValue;
479    fn next(&mut self) -> Option<Self::Item> {
480        if let Some((next, rest)) = self.values.split_first() {
481            let ret = next.to_computed_value(self.cx);
482            self.values = rest;
483            Some(ret)
484        } else {
485            None
486        }
487    }
488
489    fn size_hint(&self) -> (usize, Option<usize>) {
490        (self.values.len(), Some(self.values.len()))
491    }
492}
493
494/// A trait to represent the conversion between computed and specified values.
495///
496/// This trait is derivable with `#[derive(ToComputedValue)]`. The derived
497/// implementation just calls `ToComputedValue::to_computed_value` on each field
498/// of the passed value. The deriving code assumes that if the type isn't
499/// generic, then the trait can be implemented as simple `Clone::clone` calls,
500/// this means that a manual implementation with `ComputedValue = Self` is bogus
501/// if it returns anything else than a clone.
502pub trait ToComputedValue {
503    /// The computed value type we're going to be converted to.
504    type ComputedValue;
505
506    /// Convert a specified value to a computed value, using itself and the data
507    /// inside the `Context`.
508    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue;
509
510    /// Convert a computed value to specified value form.
511    ///
512    /// This will be used for recascading during animation.
513    /// Such from_computed_valued values should recompute to the same value.
514    fn from_computed_value(computed: &Self::ComputedValue) -> Self;
515}
516
517impl<A, B> ToComputedValue for (A, B)
518where
519    A: ToComputedValue,
520    B: ToComputedValue,
521{
522    type ComputedValue = (
523        <A as ToComputedValue>::ComputedValue,
524        <B as ToComputedValue>::ComputedValue,
525    );
526
527    #[inline]
528    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
529        (
530            self.0.to_computed_value(context),
531            self.1.to_computed_value(context),
532        )
533    }
534
535    #[inline]
536    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
537        (
538            A::from_computed_value(&computed.0),
539            B::from_computed_value(&computed.1),
540        )
541    }
542}
543
544impl<T> ToComputedValue for Option<T>
545where
546    T: ToComputedValue,
547{
548    type ComputedValue = Option<<T as ToComputedValue>::ComputedValue>;
549
550    #[inline]
551    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
552        self.as_ref().map(|item| item.to_computed_value(context))
553    }
554
555    #[inline]
556    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
557        computed.as_ref().map(T::from_computed_value)
558    }
559}
560
561impl<T> ToComputedValue for default::Size2D<T>
562where
563    T: ToComputedValue,
564{
565    type ComputedValue = default::Size2D<<T as ToComputedValue>::ComputedValue>;
566
567    #[inline]
568    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
569        Size2D::new(
570            self.width.to_computed_value(context),
571            self.height.to_computed_value(context),
572        )
573    }
574
575    #[inline]
576    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
577        Size2D::new(
578            T::from_computed_value(&computed.width),
579            T::from_computed_value(&computed.height),
580        )
581    }
582}
583
584impl<T> ToComputedValue for Vec<T>
585where
586    T: ToComputedValue,
587{
588    type ComputedValue = Vec<<T as ToComputedValue>::ComputedValue>;
589
590    #[inline]
591    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
592        self.iter()
593            .map(|item| item.to_computed_value(context))
594            .collect()
595    }
596
597    #[inline]
598    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
599        computed.iter().map(T::from_computed_value).collect()
600    }
601}
602
603impl<T> ToComputedValue for Box<T>
604where
605    T: ToComputedValue,
606{
607    type ComputedValue = Box<<T as ToComputedValue>::ComputedValue>;
608
609    #[inline]
610    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
611        Box::new(T::to_computed_value(self, context))
612    }
613
614    #[inline]
615    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
616        Box::new(T::from_computed_value(computed))
617    }
618}
619
620impl<T> ToComputedValue for Box<[T]>
621where
622    T: ToComputedValue,
623{
624    type ComputedValue = Box<[<T as ToComputedValue>::ComputedValue]>;
625
626    #[inline]
627    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
628        self.iter()
629            .map(|item| item.to_computed_value(context))
630            .collect()
631    }
632
633    #[inline]
634    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
635        computed.iter().map(T::from_computed_value).collect()
636    }
637}
638
639impl<T> ToComputedValue for crate::OwnedSlice<T>
640where
641    T: ToComputedValue,
642{
643    type ComputedValue = crate::OwnedSlice<<T as ToComputedValue>::ComputedValue>;
644
645    #[inline]
646    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
647        self.iter()
648            .map(|item| item.to_computed_value(context))
649            .collect()
650    }
651
652    #[inline]
653    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
654        computed.iter().map(T::from_computed_value).collect()
655    }
656}
657
658impl<T> ToComputedValue for thin_vec::ThinVec<T>
659where
660    T: ToComputedValue,
661{
662    type ComputedValue = thin_vec::ThinVec<<T as ToComputedValue>::ComputedValue>;
663
664    #[inline]
665    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
666        self.iter()
667            .map(|item| item.to_computed_value(context))
668            .collect()
669    }
670
671    #[inline]
672    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
673        computed.iter().map(T::from_computed_value).collect()
674    }
675}
676
677// NOTE(emilio): This is implementable more generically, but it's unlikely
678// what you want there, as it forces you to have an extra allocation.
679//
680// We could do that if needed, ideally with specialization for the case where
681// ComputedValue = T. But we don't need it for now.
682impl<T> ToComputedValue for Arc<T>
683where
684    T: ToComputedValue<ComputedValue = T>,
685{
686    type ComputedValue = Self;
687
688    #[inline]
689    fn to_computed_value(&self, _: &Context) -> Self {
690        self.clone()
691    }
692
693    #[inline]
694    fn from_computed_value(computed: &Self) -> Self {
695        computed.clone()
696    }
697}
698
699// Same caveat as above applies.
700impl<T> ToComputedValue for ArcSlice<T>
701where
702    T: ToComputedValue<ComputedValue = T>,
703{
704    type ComputedValue = Self;
705
706    #[inline]
707    fn to_computed_value(&self, _: &Context) -> Self {
708        self.clone()
709    }
710
711    #[inline]
712    fn from_computed_value(computed: &Self) -> Self {
713        computed.clone()
714    }
715}
716
717trivial_to_computed_value!(());
718trivial_to_computed_value!(bool);
719trivial_to_computed_value!(f32);
720trivial_to_computed_value!(i32);
721trivial_to_computed_value!(u8);
722trivial_to_computed_value!(u16);
723trivial_to_computed_value!(u32);
724trivial_to_computed_value!(usize);
725trivial_to_computed_value!(Atom);
726trivial_to_computed_value!(crate::values::AtomIdent);
727#[cfg(feature = "servo")]
728trivial_to_computed_value!(crate::Namespace);
729#[cfg(feature = "servo")]
730trivial_to_computed_value!(crate::Prefix);
731trivial_to_computed_value!(crate::stylesheets::UrlExtraData);
732trivial_to_computed_value!(String);
733trivial_to_computed_value!(Box<str>);
734trivial_to_computed_value!(crate::OwnedStr);
735trivial_to_computed_value!(style_traits::values::specified::AllowedNumericType);
736trivial_to_computed_value!(crate::values::generics::color::ColorMixFlags);
737
738#[allow(missing_docs)]
739#[derive(
740    Animate,
741    Clone,
742    ComputeSquaredDistance,
743    Copy,
744    Debug,
745    MallocSizeOf,
746    PartialEq,
747    ToAnimatedZero,
748    ToCss,
749    ToResolvedValue,
750)]
751#[repr(C, u8)]
752pub enum AngleOrPercentage {
753    Percentage(Percentage),
754    Angle(Angle),
755}
756
757impl ToComputedValue for specified::AngleOrPercentage {
758    type ComputedValue = AngleOrPercentage;
759
760    #[inline]
761    fn to_computed_value(&self, context: &Context) -> AngleOrPercentage {
762        match *self {
763            specified::AngleOrPercentage::Percentage(percentage) => {
764                AngleOrPercentage::Percentage(percentage.to_computed_value(context))
765            },
766            specified::AngleOrPercentage::Angle(angle) => {
767                AngleOrPercentage::Angle(angle.to_computed_value(context))
768            },
769        }
770    }
771    #[inline]
772    fn from_computed_value(computed: &AngleOrPercentage) -> Self {
773        match *computed {
774            AngleOrPercentage::Percentage(percentage) => specified::AngleOrPercentage::Percentage(
775                ToComputedValue::from_computed_value(&percentage),
776            ),
777            AngleOrPercentage::Angle(angle) => {
778                specified::AngleOrPercentage::Angle(ToComputedValue::from_computed_value(&angle))
779            },
780        }
781    }
782}
783
784/// A `<number>` value.
785pub type Number = CSSFloat;
786
787impl IsParallelTo for (Number, Number, Number) {
788    fn is_parallel_to(&self, vector: &DirectionVector) -> bool {
789        use euclid::approxeq::ApproxEq;
790        // If a and b is parallel, the angle between them is 0deg, so
791        // a x b = |a|*|b|*sin(0)*n = 0 * n, |a x b| == 0.
792        let self_vector = DirectionVector::new(self.0, self.1, self.2);
793        self_vector
794            .cross(*vector)
795            .square_length()
796            .approx_eq(&0.0f32)
797    }
798}
799
800/// A wrapper of Number, but the value >= 0.
801pub type NonNegativeNumber = NonNegative<CSSFloat>;
802
803impl From<CSSFloat> for NonNegativeNumber {
804    #[inline]
805    fn from(number: CSSFloat) -> NonNegativeNumber {
806        NonNegative::<CSSFloat>(number)
807    }
808}
809
810impl From<NonNegativeNumber> for CSSFloat {
811    #[inline]
812    fn from(number: NonNegativeNumber) -> CSSFloat {
813        number.0
814    }
815}
816
817impl One for NonNegativeNumber {
818    #[inline]
819    fn one() -> Self {
820        NonNegative(1.0)
821    }
822
823    #[inline]
824    fn is_one(&self) -> bool {
825        self.0 == 1.0
826    }
827}
828
829/// A wrapper of Number, but the value between 0 and 1
830pub type ZeroToOneNumber = ZeroToOne<CSSFloat>;
831
832impl ToAnimatedValue for ZeroToOneNumber {
833    type AnimatedValue = Self;
834
835    #[inline]
836    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
837        self
838    }
839
840    #[inline]
841    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
842        Self(animated.0.max(0.).min(1.))
843    }
844}
845
846impl From<CSSFloat> for ZeroToOneNumber {
847    #[inline]
848    fn from(number: CSSFloat) -> Self {
849        Self(number)
850    }
851}
852
853/// A wrapper of Number, but the value >= 1.
854pub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<CSSFloat>;
855
856impl ToAnimatedValue for GreaterThanOrEqualToOneNumber {
857    type AnimatedValue = CSSFloat;
858
859    #[inline]
860    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
861        self.0
862    }
863
864    #[inline]
865    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
866        animated.max(1.).into()
867    }
868}
869
870impl From<CSSFloat> for GreaterThanOrEqualToOneNumber {
871    #[inline]
872    fn from(number: CSSFloat) -> GreaterThanOrEqualToOneNumber {
873        GreaterThanOrEqualToOne::<CSSFloat>(number)
874    }
875}
876
877impl From<GreaterThanOrEqualToOneNumber> for CSSFloat {
878    #[inline]
879    fn from(number: GreaterThanOrEqualToOneNumber) -> CSSFloat {
880        number.0
881    }
882}
883
884#[allow(missing_docs)]
885#[derive(
886    Animate,
887    Clone,
888    ComputeSquaredDistance,
889    Copy,
890    Debug,
891    MallocSizeOf,
892    PartialEq,
893    ToAnimatedZero,
894    ToAnimatedValue,
895    ToCss,
896    ToResolvedValue,
897)]
898#[repr(C, u8)]
899pub enum NumberOrPercentage {
900    Percentage(Percentage),
901    Number(Number),
902}
903
904impl ClampToNonNegative for NumberOrPercentage {
905    fn clamp_to_non_negative(self) -> Self {
906        match self {
907            NumberOrPercentage::Percentage(p) => {
908                NumberOrPercentage::Percentage(p.clamp_to_non_negative())
909            },
910            NumberOrPercentage::Number(n) => NumberOrPercentage::Number(n.clamp_to_non_negative()),
911        }
912    }
913}
914
915impl ToComputedValue for specified::NumberOrPercentage {
916    type ComputedValue = NumberOrPercentage;
917
918    #[inline]
919    fn to_computed_value(&self, context: &Context) -> NumberOrPercentage {
920        match *self {
921            specified::NumberOrPercentage::Percentage(percentage) => {
922                NumberOrPercentage::Percentage(percentage.to_computed_value(context))
923            },
924            specified::NumberOrPercentage::Number(number) => {
925                NumberOrPercentage::Number(number.to_computed_value(context))
926            },
927        }
928    }
929    #[inline]
930    fn from_computed_value(computed: &NumberOrPercentage) -> Self {
931        match *computed {
932            NumberOrPercentage::Percentage(percentage) => {
933                specified::NumberOrPercentage::Percentage(ToComputedValue::from_computed_value(
934                    &percentage,
935                ))
936            },
937            NumberOrPercentage::Number(number) => {
938                specified::NumberOrPercentage::Number(ToComputedValue::from_computed_value(&number))
939            },
940        }
941    }
942}
943
944/// A non-negative <number-percentage>.
945pub type NonNegativeNumberOrPercentage = NonNegative<NumberOrPercentage>;
946
947impl NonNegativeNumberOrPercentage {
948    /// Returns the `100%` value.
949    #[inline]
950    pub fn hundred_percent() -> Self {
951        NonNegative(NumberOrPercentage::Percentage(Percentage::hundred()))
952    }
953}
954
955/// A type used for opacity.
956pub type Opacity = CSSFloat;
957
958/// A `<integer>` value.
959pub type Integer = CSSInteger;
960
961/// A wrapper of Integer, but only accept a value >= 1.
962pub type PositiveInteger = GreaterThanOrEqualToOne<CSSInteger>;
963
964impl ToAnimatedValue for PositiveInteger {
965    type AnimatedValue = CSSInteger;
966
967    #[inline]
968    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
969        self.0
970    }
971
972    #[inline]
973    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
974        cmp::max(animated, 1).into()
975    }
976}
977
978impl From<CSSInteger> for PositiveInteger {
979    #[inline]
980    fn from(int: CSSInteger) -> PositiveInteger {
981        GreaterThanOrEqualToOne::<CSSInteger>(int)
982    }
983}
984
985/// rect(...) | auto
986pub type ClipRect = generics::GenericClipRect<LengthOrAuto>;
987
988/// rect(...) | auto
989pub type ClipRectOrAuto = generics::GenericClipRectOrAuto<ClipRect>;
990
991/// The computed value of a grid `<track-breadth>`
992pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;
993
994/// The computed value of a grid `<track-size>`
995pub type TrackSize = GenericTrackSize<LengthPercentage>;
996
997/// The computed value of a grid `<track-size>+`
998pub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>;
999
1000/// The computed value of a grid `<track-list>`
1001/// (could also be `<auto-track-list>` or `<explicit-track-list>`)
1002pub type TrackList = GenericTrackList<LengthPercentage, Integer>;
1003
1004/// The computed value of a `<grid-line>`.
1005pub type GridLine = GenericGridLine<Integer>;
1006
1007/// `<grid-template-rows> | <grid-template-columns>`
1008pub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>;
1009
1010impl ClipRect {
1011    /// Given a border box, resolves the clip rect against the border box
1012    /// in the same space the border box is in
1013    pub fn for_border_rect<T: Copy + From<Length> + Add<Output = T> + Sub<Output = T>, U>(
1014        &self,
1015        border_box: Rect<T, U>,
1016    ) -> Rect<T, U> {
1017        fn extract_clip_component<T: From<Length>>(p: &LengthOrAuto, or: T) -> T {
1018            match *p {
1019                LengthOrAuto::Auto => or,
1020                LengthOrAuto::LengthPercentage(ref length) => T::from(*length),
1021            }
1022        }
1023
1024        let clip_origin = Point2D::new(
1025            From::from(self.left.auto_is(|| Length::new(0.))),
1026            From::from(self.top.auto_is(|| Length::new(0.))),
1027        );
1028        let right = extract_clip_component(&self.right, border_box.size.width);
1029        let bottom = extract_clip_component(&self.bottom, border_box.size.height);
1030        let clip_size = Size2D::new(right - clip_origin.x, bottom - clip_origin.y);
1031
1032        Rect::new(clip_origin, clip_size).translate(border_box.origin.to_vector())
1033    }
1034}