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