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::specified::font::QueryFontMetricsFlags;
31use crate::values::specified::length::FontBaseSize;
32use crate::{ArcSlice, Atom, One};
33use euclid::{default, Point2D, Rect, Size2D};
34use servo_arc::Arc;
35use std::cell::RefCell;
36use std::cmp;
37use std::f32;
38use std::ops::{Add, Sub};
39
40pub use self::align::{AlignContent, AlignItems, JustifyContent, JustifyItems, SelfAlignment};
41pub use self::align::{AlignSelf, JustifySelf};
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, 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    TextDecorationLength, TextDecorationSkipInk, TextDecorationTrim, 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 {
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 ToAnimatedValue for NonNegativeNumber {
803    type AnimatedValue = CSSFloat;
804
805    #[inline]
806    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
807        self.0
808    }
809
810    #[inline]
811    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
812        animated.max(0.).into()
813    }
814}
815
816impl From<CSSFloat> for NonNegativeNumber {
817    #[inline]
818    fn from(number: CSSFloat) -> NonNegativeNumber {
819        NonNegative::<CSSFloat>(number)
820    }
821}
822
823impl From<NonNegativeNumber> for CSSFloat {
824    #[inline]
825    fn from(number: NonNegativeNumber) -> CSSFloat {
826        number.0
827    }
828}
829
830impl One for NonNegativeNumber {
831    #[inline]
832    fn one() -> Self {
833        NonNegative(1.0)
834    }
835
836    #[inline]
837    fn is_one(&self) -> bool {
838        self.0 == 1.0
839    }
840}
841
842/// A wrapper of Number, but the value between 0 and 1
843pub type ZeroToOneNumber = ZeroToOne<CSSFloat>;
844
845impl ToAnimatedValue for ZeroToOneNumber {
846    type AnimatedValue = CSSFloat;
847
848    #[inline]
849    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
850        self.0
851    }
852
853    #[inline]
854    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
855        Self(animated.max(0.).min(1.))
856    }
857}
858
859impl From<CSSFloat> for ZeroToOneNumber {
860    #[inline]
861    fn from(number: CSSFloat) -> Self {
862        Self(number)
863    }
864}
865
866/// A wrapper of Number, but the value >= 1.
867pub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<CSSFloat>;
868
869impl ToAnimatedValue for GreaterThanOrEqualToOneNumber {
870    type AnimatedValue = CSSFloat;
871
872    #[inline]
873    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
874        self.0
875    }
876
877    #[inline]
878    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
879        animated.max(1.).into()
880    }
881}
882
883impl From<CSSFloat> for GreaterThanOrEqualToOneNumber {
884    #[inline]
885    fn from(number: CSSFloat) -> GreaterThanOrEqualToOneNumber {
886        GreaterThanOrEqualToOne::<CSSFloat>(number)
887    }
888}
889
890impl From<GreaterThanOrEqualToOneNumber> for CSSFloat {
891    #[inline]
892    fn from(number: GreaterThanOrEqualToOneNumber) -> CSSFloat {
893        number.0
894    }
895}
896
897#[allow(missing_docs)]
898#[derive(
899    Animate,
900    Clone,
901    ComputeSquaredDistance,
902    Copy,
903    Debug,
904    MallocSizeOf,
905    PartialEq,
906    ToAnimatedZero,
907    ToCss,
908    ToResolvedValue,
909)]
910#[repr(C, u8)]
911pub enum NumberOrPercentage {
912    Percentage(Percentage),
913    Number(Number),
914}
915
916impl NumberOrPercentage {
917    fn clamp_to_non_negative(self) -> Self {
918        match self {
919            NumberOrPercentage::Percentage(p) => {
920                NumberOrPercentage::Percentage(p.clamp_to_non_negative())
921            },
922            NumberOrPercentage::Number(n) => NumberOrPercentage::Number(n.max(0.)),
923        }
924    }
925}
926
927impl ToComputedValue for specified::NumberOrPercentage {
928    type ComputedValue = NumberOrPercentage;
929
930    #[inline]
931    fn to_computed_value(&self, context: &Context) -> NumberOrPercentage {
932        match *self {
933            specified::NumberOrPercentage::Percentage(percentage) => {
934                NumberOrPercentage::Percentage(percentage.to_computed_value(context))
935            },
936            specified::NumberOrPercentage::Number(number) => {
937                NumberOrPercentage::Number(number.to_computed_value(context))
938            },
939        }
940    }
941    #[inline]
942    fn from_computed_value(computed: &NumberOrPercentage) -> Self {
943        match *computed {
944            NumberOrPercentage::Percentage(percentage) => {
945                specified::NumberOrPercentage::Percentage(ToComputedValue::from_computed_value(
946                    &percentage,
947                ))
948            },
949            NumberOrPercentage::Number(number) => {
950                specified::NumberOrPercentage::Number(ToComputedValue::from_computed_value(&number))
951            },
952        }
953    }
954}
955
956/// A non-negative <number-percentage>.
957pub type NonNegativeNumberOrPercentage = NonNegative<NumberOrPercentage>;
958
959impl NonNegativeNumberOrPercentage {
960    /// Returns the `100%` value.
961    #[inline]
962    pub fn hundred_percent() -> Self {
963        NonNegative(NumberOrPercentage::Percentage(Percentage::hundred()))
964    }
965}
966
967impl ToAnimatedValue for NonNegativeNumberOrPercentage {
968    type AnimatedValue = NumberOrPercentage;
969
970    #[inline]
971    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
972        self.0
973    }
974
975    #[inline]
976    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
977        NonNegative(animated.clamp_to_non_negative())
978    }
979}
980
981/// A type used for opacity.
982pub type Opacity = CSSFloat;
983
984/// A `<integer>` value.
985pub type Integer = CSSInteger;
986
987/// A wrapper of Integer, but only accept a value >= 1.
988pub type PositiveInteger = GreaterThanOrEqualToOne<CSSInteger>;
989
990impl ToAnimatedValue for PositiveInteger {
991    type AnimatedValue = CSSInteger;
992
993    #[inline]
994    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
995        self.0
996    }
997
998    #[inline]
999    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1000        cmp::max(animated, 1).into()
1001    }
1002}
1003
1004impl From<CSSInteger> for PositiveInteger {
1005    #[inline]
1006    fn from(int: CSSInteger) -> PositiveInteger {
1007        GreaterThanOrEqualToOne::<CSSInteger>(int)
1008    }
1009}
1010
1011/// rect(...) | auto
1012pub type ClipRect = generics::GenericClipRect<LengthOrAuto>;
1013
1014/// rect(...) | auto
1015pub type ClipRectOrAuto = generics::GenericClipRectOrAuto<ClipRect>;
1016
1017/// The computed value of a grid `<track-breadth>`
1018pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;
1019
1020/// The computed value of a grid `<track-size>`
1021pub type TrackSize = GenericTrackSize<LengthPercentage>;
1022
1023/// The computed value of a grid `<track-size>+`
1024pub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>;
1025
1026/// The computed value of a grid `<track-list>`
1027/// (could also be `<auto-track-list>` or `<explicit-track-list>`)
1028pub type TrackList = GenericTrackList<LengthPercentage, Integer>;
1029
1030/// The computed value of a `<grid-line>`.
1031pub type GridLine = GenericGridLine<Integer>;
1032
1033/// `<grid-template-rows> | <grid-template-columns>`
1034pub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>;
1035
1036impl ClipRect {
1037    /// Given a border box, resolves the clip rect against the border box
1038    /// in the same space the border box is in
1039    pub fn for_border_rect<T: Copy + From<Length> + Add<Output = T> + Sub<Output = T>, U>(
1040        &self,
1041        border_box: Rect<T, U>,
1042    ) -> Rect<T, U> {
1043        fn extract_clip_component<T: From<Length>>(p: &LengthOrAuto, or: T) -> T {
1044            match *p {
1045                LengthOrAuto::Auto => or,
1046                LengthOrAuto::LengthPercentage(ref length) => T::from(*length),
1047            }
1048        }
1049
1050        let clip_origin = Point2D::new(
1051            From::from(self.left.auto_is(|| Length::new(0.))),
1052            From::from(self.top.auto_is(|| Length::new(0.))),
1053        );
1054        let right = extract_clip_component(&self.right, border_box.size.width);
1055        let bottom = extract_clip_component(&self.bottom, border_box.size.height);
1056        let clip_size = Size2D::new(right - clip_origin.x, bottom - clip_origin.y);
1057
1058        Rect::new(clip_origin, clip_size).translate(border_box.origin.to_vector())
1059    }
1060}