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