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