Skip to main content

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