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