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::{ContentDistribution, ItemPlacement, JustifyItems, SelfAlignment};
41pub use self::angle::Angle;
42pub use self::animation::{
43    AnimationComposition, AnimationDirection, AnimationDuration, AnimationFillMode,
44    AnimationIterationCount, AnimationName, AnimationPlayState, AnimationTimeline, ScrollAxis,
45    TimelineName, TransitionBehavior, TransitionProperty, ViewTimelineInset, ViewTransitionClass,
46    ViewTransitionName,
47};
48pub use self::background::{BackgroundRepeat, BackgroundSize};
49pub use self::basic_shape::FillRule;
50pub use self::border::{
51    BorderCornerRadius, BorderImageRepeat, BorderImageSideWidth, BorderImageSlice,
52    BorderImageWidth, BorderRadius, BorderSideOffset, BorderSideWidth, BorderSpacing, LineWidth,
53};
54pub use self::box_::{
55    Appearance, BaselineSource, BreakBetween, BreakWithin, Clear, Contain, ContainIntrinsicSize,
56    ContainerName, ContainerType, ContentVisibility, Display, Float, LineClamp, Overflow,
57    OverflowAnchor, OverflowClipBox, OverscrollBehavior, Perspective, PositionProperty, Resize,
58    ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType,
59    ScrollbarGutter, TouchAction, VerticalAlign, WillChange, WritingModeProperty, Zoom,
60};
61pub use self::color::{
62    Color, ColorOrAuto, ColorPropertyValue, ColorScheme, ForcedColorAdjust, PrintColorAdjust,
63};
64pub use self::column::ColumnCount;
65pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset, CounterSet};
66pub use self::easing::TimingFunction;
67pub use self::effects::{BoxShadow, Filter, SimpleShadow};
68pub use self::flex::FlexBasis;
69pub use self::font::{FontFamily, FontLanguageOverride, FontPalette, FontStyle};
70pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric};
71pub use self::font::{
72    FontSize, FontSizeAdjust, FontStretch, FontSynthesis, FontSynthesisStyle, LineHeight,
73};
74pub use self::font::{FontVariantAlternates, FontWeight};
75pub use self::font::{FontVariantEastAsian, FontVariationSettings};
76pub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextScale};
77pub use self::image::{Gradient, Image, ImageRendering, LineDirection};
78pub use self::length::{CSSPixelLength, NonNegativeLength};
79pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber};
80pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, Margin, MaxSize, Size};
81pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto};
82#[cfg(feature = "gecko")]
83pub use self::list::ListStyleType;
84pub use self::list::Quotes;
85pub use self::motion::{OffsetPath, OffsetPosition, OffsetRotate};
86pub use self::outline::OutlineStyle;
87pub use self::page::{PageName, PageOrientation, PageSize, PageSizeOrientation, PaperSize};
88pub use self::percentage::{NonNegativePercentage, Percentage};
89pub use self::position::AnchorFunction;
90pub use self::position::AnchorName;
91pub use self::position::AnchorScope;
92pub use self::position::AspectRatio;
93pub use self::position::DashedIdentAndOrTryTactic;
94pub use self::position::Inset;
95pub use self::position::PositionAnchor;
96pub use self::position::PositionTryFallbacks;
97pub use self::position::PositionTryOrder;
98pub use self::position::PositionVisibility;
99pub use self::position::{
100    GridAutoFlow, GridTemplateAreas, MasonryAutoFlow, Position, PositionOrAuto, ZIndex,
101};
102pub use self::position::{PositionArea, PositionAreaKeyword};
103pub use self::ratio::Ratio;
104pub use self::rect::NonNegativeLengthOrNumberRect;
105pub use self::resolution::Resolution;
106pub use self::svg::{DProperty, MozContextProperties};
107pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};
108pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth, VectorEffect};
109pub use self::text::{HyphenateCharacter, HyphenateLimitChars};
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::{TextAutospace, TextUnderlinePosition};
114pub use self::text::{
115    TextDecorationInset, TextDecorationLength, TextDecorationSkipInk, TextJustify,
116};
117pub use self::time::Time;
118pub use self::transform::{Rotate, Scale, Transform, TransformBox, TransformOperation};
119pub use self::transform::{TransformOrigin, TransformStyle, Translate};
120#[cfg(feature = "gecko")]
121pub use self::ui::CursorImage;
122pub use self::ui::{
123    BoolInteger, Cursor, Inert, MozTheme, PointerEvents, ScrollbarColor, UserFocus, UserSelect,
124};
125pub use super::specified::TextTransform;
126pub use super::specified::ViewportVariant;
127pub use super::specified::{BorderStyle, TextDecorationLine};
128pub use app_units::Au;
129
130pub mod align;
131pub mod angle;
132pub mod animation;
133pub mod background;
134pub mod basic_shape;
135pub mod border;
136#[path = "box.rs"]
137pub mod box_;
138pub mod color;
139pub mod column;
140pub mod counters;
141pub mod easing;
142pub mod effects;
143pub mod flex;
144pub mod font;
145pub mod image;
146pub mod length;
147pub mod length_percentage;
148pub mod list;
149pub mod motion;
150pub mod outline;
151pub mod page;
152pub mod percentage;
153pub mod position;
154pub mod ratio;
155pub mod rect;
156pub mod resolution;
157pub mod svg;
158pub mod table;
159pub mod text;
160pub mod time;
161pub mod transform;
162pub mod ui;
163pub mod url;
164
165/// A `Context` is all the data a specified value could ever need to compute
166/// itself and be transformed to a computed value.
167pub struct Context<'a> {
168    /// Values accessed through this need to be in the properties "computed
169    /// early": color, text-decoration, font-size, display, position, float,
170    /// border-*-style, outline-style, font-family, writing-mode...
171    pub builder: StyleBuilder<'a>,
172
173    /// A cached computed system font value, for use by gecko.
174    ///
175    /// See properties/longhands/font.mako.rs
176    #[cfg(feature = "gecko")]
177    pub cached_system_font: Option<properties::longhands::system_font::ComputedSystemFont>,
178
179    /// A dummy option for servo so initializing a computed::Context isn't
180    /// painful.
181    ///
182    /// TODO(emilio): Make constructors for Context, and drop this.
183    #[cfg(feature = "servo")]
184    pub cached_system_font: Option<()>,
185
186    /// Whether or not we are computing the media list in a media query.
187    pub in_media_query: bool,
188
189    /// Whether or not we are computing the container query condition.
190    pub in_container_query: bool,
191
192    /// The quirks mode of this context.
193    pub quirks_mode: QuirksMode,
194
195    /// Whether this computation is being done for a SMIL animation.
196    ///
197    /// This is used to allow certain properties to generate out-of-range
198    /// values, which SMIL allows.
199    pub for_smil_animation: bool,
200
201    /// Returns the container information to evaluate a given container query.
202    pub container_info: Option<ContainerInfo>,
203
204    /// Whether we're computing a value for a non-inherited property.
205    /// False if we are computed a value for an inherited property or not computing for a property
206    /// at all (e.g. in a media query evaluation).
207    pub for_non_inherited_property: bool,
208
209    /// The conditions to cache a rule node on the rule cache.
210    ///
211    /// FIXME(emilio): Drop the refcell.
212    pub rule_cache_conditions: RefCell<&'a mut RuleCacheConditions>,
213
214    /// Container size query for this context.
215    container_size_query: RefCell<ContainerSizeQuery<'a>>,
216}
217
218impl<'a> Context<'a> {
219    /// Lazily evaluate the container size query, returning the result.
220    pub fn get_container_size_query(&self) -> ContainerSizeQueryResult {
221        let mut resolved = self.container_size_query.borrow_mut();
222        resolved.get().clone()
223    }
224
225    /// Creates a suitable context for media query evaluation, in which
226    /// font-relative units compute against the system_font, and executes `f`
227    /// with it.
228    pub fn for_media_query_evaluation<F, R>(device: &Device, quirks_mode: QuirksMode, f: F) -> R
229    where
230        F: FnOnce(&Context) -> R,
231    {
232        let mut conditions = RuleCacheConditions::default();
233        let context = Context {
234            builder: StyleBuilder::for_inheritance(device, None, None, None),
235            cached_system_font: None,
236            in_media_query: true,
237            in_container_query: false,
238            quirks_mode,
239            for_smil_animation: false,
240            container_info: None,
241            for_non_inherited_property: false,
242            rule_cache_conditions: RefCell::new(&mut conditions),
243            container_size_query: RefCell::new(ContainerSizeQuery::none()),
244        };
245        f(&context)
246    }
247
248    /// Creates a suitable context for container query evaluation for the style
249    /// specified.
250    pub fn for_container_query_evaluation<F, R>(
251        device: &Device,
252        stylist: Option<&Stylist>,
253        container_info_and_style: Option<(ContainerInfo, Arc<ComputedValues>)>,
254        container_size_query: ContainerSizeQuery,
255        f: F,
256    ) -> R
257    where
258        F: FnOnce(&Context) -> R,
259    {
260        let mut conditions = RuleCacheConditions::default();
261
262        let (container_info, style) = match container_info_and_style {
263            Some((ci, s)) => (Some(ci), Some(s)),
264            None => (None, None),
265        };
266
267        let style = style.as_ref().map(|s| &**s);
268        let quirks_mode = device.quirks_mode();
269        let context = Context {
270            builder: StyleBuilder::for_inheritance(device, stylist, style, None),
271            cached_system_font: None,
272            in_media_query: false,
273            in_container_query: true,
274            quirks_mode,
275            for_smil_animation: false,
276            container_info,
277            for_non_inherited_property: false,
278            rule_cache_conditions: RefCell::new(&mut conditions),
279            container_size_query: RefCell::new(container_size_query),
280        };
281
282        f(&context)
283    }
284
285    /// Creates a context suitable for more general cases.
286    pub fn new(
287        builder: StyleBuilder<'a>,
288        quirks_mode: QuirksMode,
289        rule_cache_conditions: &'a mut RuleCacheConditions,
290        container_size_query: ContainerSizeQuery<'a>,
291    ) -> Self {
292        Self {
293            builder,
294            cached_system_font: None,
295            in_media_query: false,
296            in_container_query: false,
297            quirks_mode,
298            container_info: None,
299            for_smil_animation: false,
300            for_non_inherited_property: false,
301            rule_cache_conditions: RefCell::new(rule_cache_conditions),
302            container_size_query: RefCell::new(container_size_query),
303        }
304    }
305
306    /// Creates a context suitable for computing animations.
307    pub fn new_for_animation(
308        builder: StyleBuilder<'a>,
309        for_smil_animation: bool,
310        quirks_mode: QuirksMode,
311        rule_cache_conditions: &'a mut RuleCacheConditions,
312        container_size_query: ContainerSizeQuery<'a>,
313    ) -> Self {
314        Self {
315            builder,
316            cached_system_font: None,
317            in_media_query: false,
318            in_container_query: false,
319            quirks_mode,
320            container_info: None,
321            for_smil_animation,
322            for_non_inherited_property: false,
323            rule_cache_conditions: RefCell::new(rule_cache_conditions),
324            container_size_query: RefCell::new(container_size_query),
325        }
326    }
327
328    /// Creates a context suitable for computing the initial value of @property.
329    pub fn new_for_initial_at_property_value(
330        stylist: &'a Stylist,
331        rule_cache_conditions: &'a mut RuleCacheConditions,
332    ) -> Self {
333        Self {
334            builder: StyleBuilder::new(stylist.device(), Some(stylist), None, None, None, false),
335            cached_system_font: None,
336            // Because font-relative values are disallowed in @property initial values, we do not
337            // need to keep track of whether we're in a media query, whether we're in a container
338            // query, and so on.
339            in_media_query: false,
340            in_container_query: false,
341            quirks_mode: stylist.quirks_mode(),
342            container_info: None,
343            for_smil_animation: false,
344            for_non_inherited_property: false,
345            rule_cache_conditions: RefCell::new(rule_cache_conditions),
346            container_size_query: RefCell::new(ContainerSizeQuery::none()),
347        }
348    }
349
350    /// The current device.
351    pub fn device(&self) -> &Device {
352        self.builder.device
353    }
354
355    /// Get the inherited custom properties map.
356    pub fn inherited_custom_properties(&self) -> &ComputedCustomProperties {
357        &self.builder.inherited_custom_properties()
358    }
359
360    /// Whether the style is for the root element.
361    pub fn is_root_element(&self) -> bool {
362        self.builder.is_root_element
363    }
364
365    /// Queries font metrics.
366    pub fn query_font_metrics(
367        &self,
368        base_size: FontBaseSize,
369        orientation: FontMetricsOrientation,
370        mut flags: QueryFontMetricsFlags,
371    ) -> FontMetrics {
372        if self.for_non_inherited_property {
373            self.rule_cache_conditions.borrow_mut().set_uncacheable();
374        }
375        self.builder.add_flags(match base_size {
376            FontBaseSize::CurrentStyle => ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS,
377            FontBaseSize::InheritedStyle => ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS,
378        });
379        let size = base_size.resolve(self).used_size();
380        let style = self.style();
381
382        let (wm, font) = match base_size {
383            FontBaseSize::CurrentStyle => (style.writing_mode, style.get_font()),
384            // This is only used for font-size computation.
385            FontBaseSize::InheritedStyle => {
386                (*style.inherited_writing_mode(), style.get_parent_font())
387            },
388        };
389
390        let vertical = match orientation {
391            FontMetricsOrientation::MatchContextPreferHorizontal => {
392                wm.is_vertical() && wm.is_upright()
393            },
394            FontMetricsOrientation::MatchContextPreferVertical => wm.is_text_vertical(),
395            FontMetricsOrientation::Horizontal => false,
396        };
397        if !self.in_media_query {
398            flags |= QueryFontMetricsFlags::USE_USER_FONT_SET
399        }
400        self.device()
401            .query_font_metrics(vertical, font, size, flags)
402    }
403
404    /// The current viewport size, used to resolve viewport units.
405    pub fn viewport_size_for_viewport_unit_resolution(
406        &self,
407        variant: ViewportVariant,
408    ) -> default::Size2D<Au> {
409        self.builder
410            .add_flags(ComputedValueFlags::USES_VIEWPORT_UNITS);
411        self.builder
412            .device
413            .au_viewport_size_for_viewport_unit_resolution(variant)
414    }
415
416    /// Whether we're in a media or container query.
417    pub fn in_media_or_container_query(&self) -> bool {
418        self.in_media_query || self.in_container_query
419    }
420
421    /// The default computed style we're getting our reset style from.
422    pub fn default_style(&self) -> &ComputedValues {
423        self.builder.default_style()
424    }
425
426    /// The current style.
427    pub fn style(&self) -> &StyleBuilder<'a> {
428        &self.builder
429    }
430
431    /// Apply text-zoom if enabled.
432    #[cfg(feature = "gecko")]
433    pub fn maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength {
434        if self
435            .style()
436            .get_font()
437            .clone__x_text_scale()
438            .text_zoom_enabled()
439        {
440            self.device().zoom_text(size)
441        } else {
442            size
443        }
444    }
445
446    /// (Servo doesn't do text-zoom)
447    #[cfg(feature = "servo")]
448    pub fn maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength {
449        size
450    }
451}
452
453/// An iterator over a slice of computed values
454#[derive(Clone)]
455pub struct ComputedVecIter<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> {
456    cx: &'cx Context<'cx_a>,
457    values: &'a [S],
458}
459
460impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> ComputedVecIter<'a, 'cx, 'cx_a, S> {
461    /// Construct an iterator from a slice of specified values and a context
462    pub fn new(cx: &'cx Context<'cx_a>, values: &'a [S]) -> Self {
463        ComputedVecIter { cx, values }
464    }
465}
466
467impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> ExactSizeIterator
468    for ComputedVecIter<'a, 'cx, 'cx_a, S>
469{
470    fn len(&self) -> usize {
471        self.values.len()
472    }
473}
474
475impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> Iterator for ComputedVecIter<'a, 'cx, 'cx_a, S> {
476    type Item = S::ComputedValue;
477    fn next(&mut self) -> Option<Self::Item> {
478        if let Some((next, rest)) = self.values.split_first() {
479            let ret = next.to_computed_value(self.cx);
480            self.values = rest;
481            Some(ret)
482        } else {
483            None
484        }
485    }
486
487    fn size_hint(&self) -> (usize, Option<usize>) {
488        (self.values.len(), Some(self.values.len()))
489    }
490}
491
492/// A trait to represent the conversion between computed and specified values.
493///
494/// This trait is derivable with `#[derive(ToComputedValue)]`. The derived
495/// implementation just calls `ToComputedValue::to_computed_value` on each field
496/// of the passed value. The deriving code assumes that if the type isn't
497/// generic, then the trait can be implemented as simple `Clone::clone` calls,
498/// this means that a manual implementation with `ComputedValue = Self` is bogus
499/// if it returns anything else than a clone.
500pub trait ToComputedValue {
501    /// The computed value type we're going to be converted to.
502    type ComputedValue;
503
504    /// Convert a specified value to a computed value, using itself and the data
505    /// inside the `Context`.
506    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue;
507
508    /// Convert a computed value to specified value form.
509    ///
510    /// This will be used for recascading during animation.
511    /// Such from_computed_valued values should recompute to the same value.
512    fn from_computed_value(computed: &Self::ComputedValue) -> Self;
513}
514
515impl<A, B> ToComputedValue for (A, B)
516where
517    A: ToComputedValue,
518    B: ToComputedValue,
519{
520    type ComputedValue = (
521        <A as ToComputedValue>::ComputedValue,
522        <B as ToComputedValue>::ComputedValue,
523    );
524
525    #[inline]
526    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
527        (
528            self.0.to_computed_value(context),
529            self.1.to_computed_value(context),
530        )
531    }
532
533    #[inline]
534    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
535        (
536            A::from_computed_value(&computed.0),
537            B::from_computed_value(&computed.1),
538        )
539    }
540}
541
542impl<T> ToComputedValue for Option<T>
543where
544    T: ToComputedValue,
545{
546    type ComputedValue = Option<<T as ToComputedValue>::ComputedValue>;
547
548    #[inline]
549    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
550        self.as_ref().map(|item| item.to_computed_value(context))
551    }
552
553    #[inline]
554    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
555        computed.as_ref().map(T::from_computed_value)
556    }
557}
558
559impl<T> ToComputedValue for default::Size2D<T>
560where
561    T: ToComputedValue,
562{
563    type ComputedValue = default::Size2D<<T as ToComputedValue>::ComputedValue>;
564
565    #[inline]
566    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
567        Size2D::new(
568            self.width.to_computed_value(context),
569            self.height.to_computed_value(context),
570        )
571    }
572
573    #[inline]
574    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
575        Size2D::new(
576            T::from_computed_value(&computed.width),
577            T::from_computed_value(&computed.height),
578        )
579    }
580}
581
582impl<T> ToComputedValue for Vec<T>
583where
584    T: ToComputedValue,
585{
586    type ComputedValue = Vec<<T as ToComputedValue>::ComputedValue>;
587
588    #[inline]
589    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
590        self.iter()
591            .map(|item| item.to_computed_value(context))
592            .collect()
593    }
594
595    #[inline]
596    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
597        computed.iter().map(T::from_computed_value).collect()
598    }
599}
600
601impl<T> ToComputedValue for Box<T>
602where
603    T: ToComputedValue,
604{
605    type ComputedValue = Box<<T as ToComputedValue>::ComputedValue>;
606
607    #[inline]
608    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
609        Box::new(T::to_computed_value(self, context))
610    }
611
612    #[inline]
613    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
614        Box::new(T::from_computed_value(computed))
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        self.iter()
627            .map(|item| item.to_computed_value(context))
628            .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}