Skip to main content

style/values/computed/
mod.rs

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