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