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