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