tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

mod.rs (32020B)


      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 //! Specified values.
      6 //!
      7 //! TODO(emilio): Enhance docs.
      8 
      9 use super::computed::transform::DirectionVector;
     10 use super::computed::{Context, ToComputedValue};
     11 use super::generics::grid::ImplicitGridTracks as GenericImplicitGridTracks;
     12 use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
     13 use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
     14 use super::generics::transform::IsParallelTo;
     15 use super::generics::{self, GreaterThanOrEqualToOne, NonNegative};
     16 use super::{CSSFloat, CSSInteger};
     17 use crate::context::QuirksMode;
     18 use crate::derives::*;
     19 use crate::parser::{Parse, ParserContext};
     20 use crate::values::specified::calc::CalcNode;
     21 use crate::values::{serialize_atom_identifier, serialize_number, AtomString};
     22 use crate::{Atom, Namespace, One, Prefix, Zero};
     23 use cssparser::{Parser, Token};
     24 use std::fmt::{self, Write};
     25 use std::ops::Add;
     26 use style_traits::values::specified::AllowedNumericType;
     27 use style_traits::{
     28    CssString, CssWriter, NumericValue, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss,
     29    ToTyped, TypedValue,
     30 };
     31 
     32 pub use self::align::{ContentDistribution, ItemPlacement, JustifyItems, SelfAlignment};
     33 pub use self::angle::{AllowUnitlessZeroAngle, Angle};
     34 pub use self::animation::{
     35    AnimationComposition, AnimationDirection, AnimationDuration, AnimationFillMode,
     36    AnimationIterationCount, AnimationName, AnimationPlayState, AnimationTimeline, ScrollAxis,
     37    TimelineName, TransitionBehavior, TransitionProperty, ViewTimelineInset, ViewTransitionClass,
     38    ViewTransitionName,
     39 };
     40 pub use self::background::{BackgroundRepeat, BackgroundSize};
     41 pub use self::basic_shape::FillRule;
     42 pub use self::border::{
     43    BorderCornerRadius, BorderImageRepeat, BorderImageSideWidth, BorderImageSlice,
     44    BorderImageWidth, BorderRadius, BorderSideOffset, BorderSideWidth, BorderSpacing, BorderStyle,
     45    LineWidth,
     46 };
     47 pub use self::box_::{
     48    Appearance, BaselineSource, BreakBetween, BreakWithin, Clear, Contain, ContainIntrinsicSize,
     49    ContainerName, ContainerType, ContentVisibility, Display, Float, LineClamp, Overflow,
     50    OverflowAnchor, OverflowClipMargin, OverscrollBehavior, Perspective, PositionProperty, Resize,
     51    ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType,
     52    ScrollbarGutter, TouchAction, VerticalAlign, WillChange, WillChangeBits, WritingModeProperty,
     53    Zoom,
     54 };
     55 pub use self::color::{
     56    Color, ColorOrAuto, ColorPropertyValue, ColorScheme, ForcedColorAdjust, PrintColorAdjust,
     57 };
     58 pub use self::column::ColumnCount;
     59 pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset, CounterSet};
     60 pub use self::easing::TimingFunction;
     61 pub use self::effects::{BoxShadow, Filter, SimpleShadow};
     62 pub use self::flex::FlexBasis;
     63 pub use self::font::{FontFamily, FontLanguageOverride, FontPalette, FontStyle};
     64 pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric};
     65 pub use self::font::{
     66    FontSize, FontSizeAdjust, FontSizeAdjustFactor, FontSizeKeyword, FontStretch, FontSynthesis,
     67    FontSynthesisStyle,
     68 };
     69 pub use self::font::{FontVariantAlternates, FontWeight};
     70 pub use self::font::{FontVariantEastAsian, FontVariationSettings, LineHeight};
     71 pub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextScale};
     72 pub use self::image::{EndingShape as GradientEndingShape, Gradient, Image, ImageRendering};
     73 pub use self::length::{AbsoluteLength, CalcLengthPercentage, CharacterWidth};
     74 pub use self::length::{FontRelativeLength, Length, LengthOrNumber, NonNegativeLengthOrNumber};
     75 pub use self::length::{LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
     76 pub use self::length::{Margin, MaxSize, Size};
     77 pub use self::length::{NoCalcLength, ViewportPercentageLength, ViewportVariant};
     78 pub use self::length::{
     79    NonNegativeLength, NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto,
     80 };
     81 #[cfg(feature = "gecko")]
     82 pub use self::list::ListStyleType;
     83 pub use self::list::Quotes;
     84 pub use self::motion::{OffsetPath, OffsetPosition, OffsetRotate};
     85 pub use self::outline::OutlineStyle;
     86 pub use self::page::{PageName, PageOrientation, PageSize, PageSizeOrientation, PaperSize};
     87 pub use self::percentage::{NonNegativePercentage, Percentage};
     88 pub use self::position::AnchorFunction;
     89 pub use self::position::AnchorName;
     90 pub use self::position::AnchorScope;
     91 pub use self::position::AspectRatio;
     92 pub use self::position::Inset;
     93 pub use self::position::PositionAnchor;
     94 pub use self::position::PositionTryFallbacks;
     95 pub use self::position::PositionTryOrder;
     96 pub use self::position::PositionVisibility;
     97 pub use self::position::{GridAutoFlow, GridTemplateAreas, Position, PositionOrAuto};
     98 pub use self::position::{MasonryAutoFlow, MasonryItemOrder, MasonryPlacement};
     99 pub use self::position::{PositionArea, PositionAreaKeyword};
    100 pub use self::position::{PositionComponent, ZIndex};
    101 pub use self::ratio::Ratio;
    102 pub use self::rect::NonNegativeLengthOrNumberRect;
    103 pub use self::resolution::Resolution;
    104 pub use self::svg::{DProperty, MozContextProperties};
    105 pub use self::svg::{SVGLength, SVGOpacity, SVGPaint};
    106 pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth, VectorEffect};
    107 pub use self::svg_path::SVGPathData;
    108 pub use self::text::RubyPosition;
    109 pub use self::text::{HyphenateCharacter, HyphenateLimitChars};
    110 pub use self::text::{InitialLetter, LetterSpacing, LineBreak, TextAlign, TextIndent};
    111 pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak};
    112 pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing};
    113 pub use self::text::{TextAlignLast, TextAutospace, TextUnderlinePosition};
    114 pub use self::text::{
    115    TextDecorationInset, TextDecorationLength, TextDecorationSkipInk, TextJustify, TextTransform,
    116 };
    117 pub use self::time::Time;
    118 pub use self::transform::{Rotate, Scale, Transform};
    119 pub use self::transform::{TransformBox, TransformOrigin, TransformStyle, Translate};
    120 #[cfg(feature = "gecko")]
    121 pub use self::ui::CursorImage;
    122 pub use self::ui::{
    123    BoolInteger, Cursor, Inert, MozTheme, PointerEvents, ScrollbarColor, UserFocus, UserSelect,
    124 };
    125 pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
    126 
    127 pub mod align;
    128 pub mod angle;
    129 pub mod animation;
    130 pub mod background;
    131 pub mod basic_shape;
    132 pub mod border;
    133 #[path = "box.rs"]
    134 pub mod box_;
    135 pub mod calc;
    136 pub mod color;
    137 pub mod column;
    138 pub mod counters;
    139 pub mod easing;
    140 pub mod effects;
    141 pub mod flex;
    142 pub mod font;
    143 pub mod grid;
    144 pub mod image;
    145 pub mod intersection_observer;
    146 pub mod length;
    147 pub mod list;
    148 pub mod motion;
    149 pub mod outline;
    150 pub mod page;
    151 pub mod percentage;
    152 pub mod position;
    153 pub mod ratio;
    154 pub mod rect;
    155 pub mod resolution;
    156 pub mod source_size_list;
    157 pub mod svg;
    158 pub mod svg_path;
    159 pub mod table;
    160 pub mod text;
    161 pub mod time;
    162 pub mod transform;
    163 pub mod ui;
    164 pub mod url;
    165 
    166 /// <angle> | <percentage>
    167 /// https://drafts.csswg.org/css-values/#typedef-angle-percentage
    168 #[allow(missing_docs)]
    169 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
    170 pub enum AngleOrPercentage {
    171    Percentage(Percentage),
    172    Angle(Angle),
    173 }
    174 
    175 impl AngleOrPercentage {
    176    fn parse_internal<'i, 't>(
    177        context: &ParserContext,
    178        input: &mut Parser<'i, 't>,
    179        allow_unitless_zero: AllowUnitlessZeroAngle,
    180    ) -> Result<Self, ParseError<'i>> {
    181        if let Ok(per) = input.try_parse(|i| Percentage::parse(context, i)) {
    182            return Ok(AngleOrPercentage::Percentage(per));
    183        }
    184 
    185        Angle::parse_internal(context, input, allow_unitless_zero).map(AngleOrPercentage::Angle)
    186    }
    187 
    188    /// Allow unitless angles, used for conic-gradients as specified by the spec.
    189    /// https://drafts.csswg.org/css-images-4/#valdef-conic-gradient-angle
    190    pub fn parse_with_unitless<'i, 't>(
    191        context: &ParserContext,
    192        input: &mut Parser<'i, 't>,
    193    ) -> Result<Self, ParseError<'i>> {
    194        AngleOrPercentage::parse_internal(context, input, AllowUnitlessZeroAngle::Yes)
    195    }
    196 }
    197 
    198 impl Parse for AngleOrPercentage {
    199    fn parse<'i, 't>(
    200        context: &ParserContext,
    201        input: &mut Parser<'i, 't>,
    202    ) -> Result<Self, ParseError<'i>> {
    203        AngleOrPercentage::parse_internal(context, input, AllowUnitlessZeroAngle::No)
    204    }
    205 }
    206 
    207 /// Parse a `<number>` value, with a given clamping mode.
    208 fn parse_number_with_clamping_mode<'i, 't>(
    209    context: &ParserContext,
    210    input: &mut Parser<'i, 't>,
    211    clamping_mode: AllowedNumericType,
    212 ) -> Result<Number, ParseError<'i>> {
    213    let location = input.current_source_location();
    214    match *input.next()? {
    215        Token::Number { value, .. } if clamping_mode.is_ok(context.parsing_mode, value) => {
    216            Ok(Number {
    217                value,
    218                calc_clamping_mode: None,
    219            })
    220        },
    221        Token::Function(ref name) => {
    222            let function = CalcNode::math_function(context, name, location)?;
    223            let value = CalcNode::parse_number(context, input, function)?;
    224            Ok(Number {
    225                value,
    226                calc_clamping_mode: Some(clamping_mode),
    227            })
    228        },
    229        ref t => Err(location.new_unexpected_token_error(t.clone())),
    230    }
    231 }
    232 
    233 /// A CSS `<number>` specified value.
    234 ///
    235 /// https://drafts.csswg.org/css-values-3/#number-value
    236 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialOrd, ToShmem)]
    237 pub struct Number {
    238    /// The numeric value itself.
    239    value: CSSFloat,
    240    /// If this number came from a calc() expression, this tells how clamping
    241    /// should be done on the value.
    242    calc_clamping_mode: Option<AllowedNumericType>,
    243 }
    244 
    245 impl Parse for Number {
    246    fn parse<'i, 't>(
    247        context: &ParserContext,
    248        input: &mut Parser<'i, 't>,
    249    ) -> Result<Self, ParseError<'i>> {
    250        parse_number_with_clamping_mode(context, input, AllowedNumericType::All)
    251    }
    252 }
    253 
    254 impl PartialEq<Number> for Number {
    255    fn eq(&self, other: &Number) -> bool {
    256        if self.calc_clamping_mode != other.calc_clamping_mode {
    257            return false;
    258        }
    259 
    260        self.value == other.value || (self.value.is_nan() && other.value.is_nan())
    261    }
    262 }
    263 
    264 impl Number {
    265    /// Returns a new number with the value `val`.
    266    #[inline]
    267    fn new_with_clamping_mode(
    268        value: CSSFloat,
    269        calc_clamping_mode: Option<AllowedNumericType>,
    270    ) -> Self {
    271        Self {
    272            value,
    273            calc_clamping_mode,
    274        }
    275    }
    276 
    277    /// Returns this percentage as a number.
    278    pub fn to_percentage(&self) -> Percentage {
    279        Percentage::new_with_clamping_mode(self.value, self.calc_clamping_mode)
    280    }
    281 
    282    /// Returns a new number with the value `val`.
    283    #[inline]
    284    pub fn new(val: CSSFloat) -> Self {
    285        Self::new_with_clamping_mode(val, None)
    286    }
    287 
    288    /// Returns whether this number came from a `calc()` expression.
    289    #[inline]
    290    pub fn was_calc(&self) -> bool {
    291        self.calc_clamping_mode.is_some()
    292    }
    293 
    294    /// Returns the numeric value, clamped if needed.
    295    #[inline]
    296    pub fn get(&self) -> f32 {
    297        crate::values::normalize(
    298            self.calc_clamping_mode
    299                .map_or(self.value, |mode| mode.clamp(self.value)),
    300        )
    301        .min(f32::MAX)
    302        .max(f32::MIN)
    303    }
    304 
    305    #[allow(missing_docs)]
    306    pub fn parse_non_negative<'i, 't>(
    307        context: &ParserContext,
    308        input: &mut Parser<'i, 't>,
    309    ) -> Result<Number, ParseError<'i>> {
    310        parse_number_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
    311    }
    312 
    313    #[allow(missing_docs)]
    314    pub fn parse_at_least_one<'i, 't>(
    315        context: &ParserContext,
    316        input: &mut Parser<'i, 't>,
    317    ) -> Result<Number, ParseError<'i>> {
    318        parse_number_with_clamping_mode(context, input, AllowedNumericType::AtLeastOne)
    319    }
    320 
    321    /// Clamp to 1.0 if the value is over 1.0.
    322    #[inline]
    323    pub fn clamp_to_one(self) -> Self {
    324        Number {
    325            value: self.value.min(1.),
    326            calc_clamping_mode: self.calc_clamping_mode,
    327        }
    328    }
    329 }
    330 
    331 impl ToComputedValue for Number {
    332    type ComputedValue = CSSFloat;
    333 
    334    #[inline]
    335    fn to_computed_value(&self, _: &Context) -> CSSFloat {
    336        self.get()
    337    }
    338 
    339    #[inline]
    340    fn from_computed_value(computed: &CSSFloat) -> Self {
    341        Number {
    342            value: *computed,
    343            calc_clamping_mode: None,
    344        }
    345    }
    346 }
    347 
    348 impl ToCss for Number {
    349    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    350    where
    351        W: Write,
    352    {
    353        serialize_number(self.value, self.calc_clamping_mode.is_some(), dest)
    354    }
    355 }
    356 
    357 impl ToTyped for Number {
    358    fn to_typed(&self) -> Option<TypedValue> {
    359        let value = self.value;
    360        let unit = CssString::from("number");
    361        Some(TypedValue::Numeric(NumericValue::Unit { value, unit }))
    362    }
    363 }
    364 
    365 impl IsParallelTo for (Number, Number, Number) {
    366    fn is_parallel_to(&self, vector: &DirectionVector) -> bool {
    367        use euclid::approxeq::ApproxEq;
    368        // If a and b is parallel, the angle between them is 0deg, so
    369        // a x b = |a|*|b|*sin(0)*n = 0 * n, |a x b| == 0.
    370        let self_vector = DirectionVector::new(self.0.get(), self.1.get(), self.2.get());
    371        self_vector
    372            .cross(*vector)
    373            .square_length()
    374            .approx_eq(&0.0f32)
    375    }
    376 }
    377 
    378 impl SpecifiedValueInfo for Number {}
    379 
    380 impl Add for Number {
    381    type Output = Self;
    382 
    383    fn add(self, other: Self) -> Self {
    384        Self::new(self.get() + other.get())
    385    }
    386 }
    387 
    388 impl Zero for Number {
    389    #[inline]
    390    fn zero() -> Self {
    391        Self::new(0.)
    392    }
    393 
    394    #[inline]
    395    fn is_zero(&self) -> bool {
    396        self.get() == 0.
    397    }
    398 }
    399 
    400 impl From<Number> for f32 {
    401    #[inline]
    402    fn from(n: Number) -> Self {
    403        n.get()
    404    }
    405 }
    406 
    407 impl From<Number> for f64 {
    408    #[inline]
    409    fn from(n: Number) -> Self {
    410        n.get() as f64
    411    }
    412 }
    413 
    414 /// A Number which is >= 0.0.
    415 pub type NonNegativeNumber = NonNegative<Number>;
    416 
    417 impl Parse for NonNegativeNumber {
    418    fn parse<'i, 't>(
    419        context: &ParserContext,
    420        input: &mut Parser<'i, 't>,
    421    ) -> Result<Self, ParseError<'i>> {
    422        parse_number_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
    423            .map(NonNegative::<Number>)
    424    }
    425 }
    426 
    427 impl One for NonNegativeNumber {
    428    #[inline]
    429    fn one() -> Self {
    430        NonNegativeNumber::new(1.0)
    431    }
    432 
    433    #[inline]
    434    fn is_one(&self) -> bool {
    435        self.get() == 1.0
    436    }
    437 }
    438 
    439 impl NonNegativeNumber {
    440    /// Returns a new non-negative number with the value `val`.
    441    pub fn new(val: CSSFloat) -> Self {
    442        NonNegative::<Number>(Number::new(val.max(0.)))
    443    }
    444 
    445    /// Returns the numeric value.
    446    #[inline]
    447    pub fn get(&self) -> f32 {
    448        self.0.get()
    449    }
    450 }
    451 
    452 /// An Integer which is >= 0.
    453 pub type NonNegativeInteger = NonNegative<Integer>;
    454 
    455 impl Parse for NonNegativeInteger {
    456    fn parse<'i, 't>(
    457        context: &ParserContext,
    458        input: &mut Parser<'i, 't>,
    459    ) -> Result<Self, ParseError<'i>> {
    460        Ok(NonNegative(Integer::parse_non_negative(context, input)?))
    461    }
    462 }
    463 
    464 /// A Number which is >= 1.0.
    465 pub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<Number>;
    466 
    467 impl Parse for GreaterThanOrEqualToOneNumber {
    468    fn parse<'i, 't>(
    469        context: &ParserContext,
    470        input: &mut Parser<'i, 't>,
    471    ) -> Result<Self, ParseError<'i>> {
    472        parse_number_with_clamping_mode(context, input, AllowedNumericType::AtLeastOne)
    473            .map(GreaterThanOrEqualToOne::<Number>)
    474    }
    475 }
    476 
    477 /// <number> | <percentage>
    478 ///
    479 /// Accepts only non-negative numbers.
    480 #[allow(missing_docs)]
    481 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
    482 pub enum NumberOrPercentage {
    483    Percentage(Percentage),
    484    Number(Number),
    485 }
    486 
    487 impl NumberOrPercentage {
    488    fn parse_with_clamping_mode<'i, 't>(
    489        context: &ParserContext,
    490        input: &mut Parser<'i, 't>,
    491        type_: AllowedNumericType,
    492    ) -> Result<Self, ParseError<'i>> {
    493        if let Ok(per) =
    494            input.try_parse(|i| Percentage::parse_with_clamping_mode(context, i, type_))
    495        {
    496            return Ok(NumberOrPercentage::Percentage(per));
    497        }
    498 
    499        parse_number_with_clamping_mode(context, input, type_).map(NumberOrPercentage::Number)
    500    }
    501 
    502    /// Parse a non-negative number or percentage.
    503    pub fn parse_non_negative<'i, 't>(
    504        context: &ParserContext,
    505        input: &mut Parser<'i, 't>,
    506    ) -> Result<Self, ParseError<'i>> {
    507        Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
    508    }
    509 
    510    /// Convert the number or the percentage to a number.
    511    pub fn to_percentage(self) -> Percentage {
    512        match self {
    513            Self::Percentage(p) => p,
    514            Self::Number(n) => n.to_percentage(),
    515        }
    516    }
    517 
    518    /// Convert the number or the percentage to a number.
    519    pub fn to_number(self) -> Number {
    520        match self {
    521            Self::Percentage(p) => p.to_number(),
    522            Self::Number(n) => n,
    523        }
    524    }
    525 }
    526 
    527 impl Parse for NumberOrPercentage {
    528    fn parse<'i, 't>(
    529        context: &ParserContext,
    530        input: &mut Parser<'i, 't>,
    531    ) -> Result<Self, ParseError<'i>> {
    532        Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
    533    }
    534 }
    535 
    536 /// A non-negative <number> | <percentage>.
    537 pub type NonNegativeNumberOrPercentage = NonNegative<NumberOrPercentage>;
    538 
    539 impl NonNegativeNumberOrPercentage {
    540    /// Returns the `100%` value.
    541    #[inline]
    542    pub fn hundred_percent() -> Self {
    543        NonNegative(NumberOrPercentage::Percentage(Percentage::hundred()))
    544    }
    545 
    546    /// Return a particular number.
    547    #[inline]
    548    pub fn new_number(n: f32) -> Self {
    549        NonNegative(NumberOrPercentage::Number(Number::new(n)))
    550    }
    551 }
    552 
    553 impl Parse for NonNegativeNumberOrPercentage {
    554    fn parse<'i, 't>(
    555        context: &ParserContext,
    556        input: &mut Parser<'i, 't>,
    557    ) -> Result<Self, ParseError<'i>> {
    558        Ok(NonNegative(NumberOrPercentage::parse_non_negative(
    559            context, input,
    560        )?))
    561    }
    562 }
    563 
    564 /// The value of Opacity is <alpha-value>, which is "<number> | <percentage>".
    565 /// However, we serialize the specified value as number, so it's ok to store
    566 /// the Opacity as Number.
    567 #[derive(
    568    Clone,
    569    Copy,
    570    Debug,
    571    MallocSizeOf,
    572    PartialEq,
    573    PartialOrd,
    574    SpecifiedValueInfo,
    575    ToCss,
    576    ToShmem,
    577    ToTyped,
    578 )]
    579 pub struct Opacity(Number);
    580 
    581 impl Parse for Opacity {
    582    /// Opacity accepts <number> | <percentage>, so we parse it as NumberOrPercentage,
    583    /// and then convert into an Number if it's a Percentage.
    584    /// https://drafts.csswg.org/cssom/#serializing-css-values
    585    fn parse<'i, 't>(
    586        context: &ParserContext,
    587        input: &mut Parser<'i, 't>,
    588    ) -> Result<Self, ParseError<'i>> {
    589        let number = NumberOrPercentage::parse(context, input)?.to_number();
    590        Ok(Opacity(number))
    591    }
    592 }
    593 
    594 impl ToComputedValue for Opacity {
    595    type ComputedValue = CSSFloat;
    596 
    597    #[inline]
    598    fn to_computed_value(&self, context: &Context) -> CSSFloat {
    599        let value = self.0.to_computed_value(context);
    600        if context.for_smil_animation {
    601            // SMIL expects to be able to interpolate between out-of-range
    602            // opacity values.
    603            value
    604        } else {
    605            value.min(1.0).max(0.0)
    606        }
    607    }
    608 
    609    #[inline]
    610    fn from_computed_value(computed: &CSSFloat) -> Self {
    611        Opacity(Number::from_computed_value(computed))
    612    }
    613 }
    614 
    615 /// A specified `<integer>`, either a simple integer value or a calc expression.
    616 /// Note that a calc expression may not actually be an integer; it will be rounded
    617 /// at computed-value time.
    618 ///
    619 /// <https://drafts.csswg.org/css-values/#integers>
    620 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToShmem, ToTyped)]
    621 pub enum Integer {
    622    /// A literal integer value.
    623    Literal(CSSInteger),
    624    /// A calc expression, whose value will be rounded later if necessary.
    625    Calc(CSSFloat),
    626 }
    627 
    628 impl Zero for Integer {
    629    #[inline]
    630    fn zero() -> Self {
    631        Self::new(0)
    632    }
    633 
    634    #[inline]
    635    fn is_zero(&self) -> bool {
    636        *self == 0
    637    }
    638 }
    639 
    640 impl One for Integer {
    641    #[inline]
    642    fn one() -> Self {
    643        Self::new(1)
    644    }
    645 
    646    #[inline]
    647    fn is_one(&self) -> bool {
    648        *self == 1
    649    }
    650 }
    651 
    652 impl PartialEq<i32> for Integer {
    653    fn eq(&self, value: &i32) -> bool {
    654        self.value() == *value
    655    }
    656 }
    657 
    658 impl Integer {
    659    /// Trivially constructs a new `Integer` value.
    660    pub fn new(val: CSSInteger) -> Self {
    661        Self::Literal(val)
    662    }
    663 
    664    /// Returns the (rounded) integer value associated with this value.
    665    pub fn value(&self) -> CSSInteger {
    666        match *self {
    667            Self::Literal(i) => i,
    668            Self::Calc(n) => (n + 0.5).floor() as CSSInteger,
    669        }
    670    }
    671 
    672    /// Trivially constructs a new integer value from a `calc()` expression.
    673    fn from_calc(val: CSSFloat) -> Self {
    674        Self::Calc(val)
    675    }
    676 }
    677 
    678 impl Parse for Integer {
    679    fn parse<'i, 't>(
    680        context: &ParserContext,
    681        input: &mut Parser<'i, 't>,
    682    ) -> Result<Self, ParseError<'i>> {
    683        let location = input.current_source_location();
    684        match *input.next()? {
    685            Token::Number {
    686                int_value: Some(v), ..
    687            } => Ok(Integer::new(v)),
    688            Token::Function(ref name) => {
    689                let function = CalcNode::math_function(context, name, location)?;
    690                let result = CalcNode::parse_number(context, input, function)?;
    691                Ok(Integer::from_calc(result))
    692            },
    693            ref t => Err(location.new_unexpected_token_error(t.clone())),
    694        }
    695    }
    696 }
    697 
    698 impl Integer {
    699    /// Parse an integer value which is at least `min`.
    700    pub fn parse_with_minimum<'i, 't>(
    701        context: &ParserContext,
    702        input: &mut Parser<'i, 't>,
    703        min: i32,
    704    ) -> Result<Integer, ParseError<'i>> {
    705        let value = Integer::parse(context, input)?;
    706        // FIXME(emilio): The spec asks us to avoid rejecting it at parse
    707        // time except until computed value time.
    708        //
    709        // It's not totally clear it's worth it though, and no other browser
    710        // does this.
    711        if value.value() < min {
    712            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    713        }
    714        Ok(value)
    715    }
    716 
    717    /// Parse a non-negative integer.
    718    pub fn parse_non_negative<'i, 't>(
    719        context: &ParserContext,
    720        input: &mut Parser<'i, 't>,
    721    ) -> Result<Integer, ParseError<'i>> {
    722        Integer::parse_with_minimum(context, input, 0)
    723    }
    724 
    725    /// Parse a positive integer (>= 1).
    726    pub fn parse_positive<'i, 't>(
    727        context: &ParserContext,
    728        input: &mut Parser<'i, 't>,
    729    ) -> Result<Integer, ParseError<'i>> {
    730        Integer::parse_with_minimum(context, input, 1)
    731    }
    732 }
    733 
    734 impl ToComputedValue for Integer {
    735    type ComputedValue = i32;
    736 
    737    #[inline]
    738    fn to_computed_value(&self, _: &Context) -> i32 {
    739        self.value()
    740    }
    741 
    742    #[inline]
    743    fn from_computed_value(computed: &i32) -> Self {
    744        Integer::new(*computed)
    745    }
    746 }
    747 
    748 impl ToCss for Integer {
    749    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    750    where
    751        W: Write,
    752    {
    753        match *self {
    754            Integer::Literal(i) => i.to_css(dest),
    755            Integer::Calc(n) => {
    756                dest.write_str("calc(")?;
    757                n.to_css(dest)?;
    758                dest.write_char(')')
    759            },
    760        }
    761    }
    762 }
    763 
    764 impl SpecifiedValueInfo for Integer {}
    765 
    766 /// A wrapper of Integer, with value >= 1.
    767 pub type PositiveInteger = GreaterThanOrEqualToOne<Integer>;
    768 
    769 impl Parse for PositiveInteger {
    770    #[inline]
    771    fn parse<'i, 't>(
    772        context: &ParserContext,
    773        input: &mut Parser<'i, 't>,
    774    ) -> Result<Self, ParseError<'i>> {
    775        Integer::parse_positive(context, input).map(GreaterThanOrEqualToOne)
    776    }
    777 }
    778 
    779 /// The specified value of a grid `<track-breadth>`
    780 pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;
    781 
    782 /// The specified value of a grid `<track-size>`
    783 pub type TrackSize = GenericTrackSize<LengthPercentage>;
    784 
    785 /// The specified value of a grid `<track-size>+`
    786 pub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>;
    787 
    788 /// The specified value of a grid `<track-list>`
    789 /// (could also be `<auto-track-list>` or `<explicit-track-list>`)
    790 pub type TrackList = GenericTrackList<LengthPercentage, Integer>;
    791 
    792 /// The specified value of a `<grid-line>`.
    793 pub type GridLine = GenericGridLine<Integer>;
    794 
    795 /// `<grid-template-rows> | <grid-template-columns>`
    796 pub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>;
    797 
    798 /// rect(...)
    799 pub type ClipRect = generics::GenericClipRect<LengthOrAuto>;
    800 
    801 impl Parse for ClipRect {
    802    fn parse<'i, 't>(
    803        context: &ParserContext,
    804        input: &mut Parser<'i, 't>,
    805    ) -> Result<Self, ParseError<'i>> {
    806        Self::parse_quirky(context, input, AllowQuirks::No)
    807    }
    808 }
    809 
    810 impl ClipRect {
    811    /// Parses a rect(<top>, <left>, <bottom>, <right>), allowing quirks.
    812    fn parse_quirky<'i, 't>(
    813        context: &ParserContext,
    814        input: &mut Parser<'i, 't>,
    815        allow_quirks: AllowQuirks,
    816    ) -> Result<Self, ParseError<'i>> {
    817        input.expect_function_matching("rect")?;
    818 
    819        fn parse_argument<'i, 't>(
    820            context: &ParserContext,
    821            input: &mut Parser<'i, 't>,
    822            allow_quirks: AllowQuirks,
    823        ) -> Result<LengthOrAuto, ParseError<'i>> {
    824            LengthOrAuto::parse_quirky(context, input, allow_quirks)
    825        }
    826 
    827        input.parse_nested_block(|input| {
    828            let top = parse_argument(context, input, allow_quirks)?;
    829            let right;
    830            let bottom;
    831            let left;
    832 
    833            if input.try_parse(|input| input.expect_comma()).is_ok() {
    834                right = parse_argument(context, input, allow_quirks)?;
    835                input.expect_comma()?;
    836                bottom = parse_argument(context, input, allow_quirks)?;
    837                input.expect_comma()?;
    838                left = parse_argument(context, input, allow_quirks)?;
    839            } else {
    840                right = parse_argument(context, input, allow_quirks)?;
    841                bottom = parse_argument(context, input, allow_quirks)?;
    842                left = parse_argument(context, input, allow_quirks)?;
    843            }
    844 
    845            Ok(ClipRect {
    846                top,
    847                right,
    848                bottom,
    849                left,
    850            })
    851        })
    852    }
    853 }
    854 
    855 /// rect(...) | auto
    856 pub type ClipRectOrAuto = generics::GenericClipRectOrAuto<ClipRect>;
    857 
    858 impl ClipRectOrAuto {
    859    /// Parses a ClipRect or Auto, allowing quirks.
    860    pub fn parse_quirky<'i, 't>(
    861        context: &ParserContext,
    862        input: &mut Parser<'i, 't>,
    863        allow_quirks: AllowQuirks,
    864    ) -> Result<Self, ParseError<'i>> {
    865        if let Ok(v) = input.try_parse(|i| ClipRect::parse_quirky(context, i, allow_quirks)) {
    866            return Ok(generics::GenericClipRectOrAuto::Rect(v));
    867        }
    868        input.expect_ident_matching("auto")?;
    869        Ok(generics::GenericClipRectOrAuto::Auto)
    870    }
    871 }
    872 
    873 /// Whether quirks are allowed in this context.
    874 #[derive(Clone, Copy, PartialEq)]
    875 pub enum AllowQuirks {
    876    /// Quirks are not allowed.
    877    No,
    878    /// Quirks are allowed, in quirks mode.
    879    Yes,
    880    /// Quirks are always allowed, used for SVG lengths.
    881    Always,
    882 }
    883 
    884 impl AllowQuirks {
    885    /// Returns `true` if quirks are allowed in this context.
    886    pub fn allowed(self, quirks_mode: QuirksMode) -> bool {
    887        match self {
    888            AllowQuirks::Always => true,
    889            AllowQuirks::No => false,
    890            AllowQuirks::Yes => quirks_mode == QuirksMode::Quirks,
    891        }
    892    }
    893 }
    894 
    895 /// An attr(...) rule
    896 ///
    897 /// `[namespace? `|`]? ident`
    898 #[derive(
    899    Clone,
    900    Debug,
    901    Eq,
    902    MallocSizeOf,
    903    PartialEq,
    904    SpecifiedValueInfo,
    905    ToComputedValue,
    906    ToResolvedValue,
    907    ToShmem,
    908 )]
    909 #[css(function)]
    910 #[repr(C)]
    911 pub struct Attr {
    912    /// Optional namespace prefix.
    913    pub namespace_prefix: Prefix,
    914    /// Optional namespace URL.
    915    pub namespace_url: Namespace,
    916    /// Attribute name
    917    pub attribute: Atom,
    918    /// Fallback value
    919    pub fallback: AtomString,
    920 }
    921 
    922 impl Parse for Attr {
    923    fn parse<'i, 't>(
    924        context: &ParserContext,
    925        input: &mut Parser<'i, 't>,
    926    ) -> Result<Attr, ParseError<'i>> {
    927        input.expect_function_matching("attr")?;
    928        input.parse_nested_block(|i| Attr::parse_function(context, i))
    929    }
    930 }
    931 
    932 /// Get the Namespace for a given prefix from the namespace map.
    933 fn get_namespace_for_prefix(prefix: &Prefix, context: &ParserContext) -> Option<Namespace> {
    934    context.namespaces.prefixes.get(prefix).cloned()
    935 }
    936 
    937 /// Try to parse a namespace and return it if parsed, or none if there was not one present
    938 fn parse_namespace<'i, 't>(
    939    context: &ParserContext,
    940    input: &mut Parser<'i, 't>,
    941 ) -> Result<(Prefix, Namespace), ParseError<'i>> {
    942    let ns_prefix = match input.next()? {
    943        Token::Ident(ref prefix) => Some(Prefix::from(prefix.as_ref())),
    944        Token::Delim('|') => None,
    945        _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
    946    };
    947 
    948    if ns_prefix.is_some() && !matches!(*input.next_including_whitespace()?, Token::Delim('|')) {
    949        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    950    }
    951 
    952    if let Some(prefix) = ns_prefix {
    953        let ns = match get_namespace_for_prefix(&prefix, context) {
    954            Some(ns) => ns,
    955            None => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
    956        };
    957        Ok((prefix, ns))
    958    } else {
    959        Ok((Prefix::default(), Namespace::default()))
    960    }
    961 }
    962 
    963 impl Attr {
    964    /// Parse contents of attr() assuming we have already parsed `attr` and are
    965    /// within a parse_nested_block()
    966    pub fn parse_function<'i, 't>(
    967        context: &ParserContext,
    968        input: &mut Parser<'i, 't>,
    969    ) -> Result<Attr, ParseError<'i>> {
    970        // Syntax is `[namespace? '|']? ident [',' fallback]?`
    971        let namespace = input
    972            .try_parse(|input| parse_namespace(context, input))
    973            .ok();
    974        let namespace_is_some = namespace.is_some();
    975        let (namespace_prefix, namespace_url) = namespace.unwrap_or_default();
    976 
    977        // If there is a namespace, ensure no whitespace following '|'
    978        let attribute = Atom::from(if namespace_is_some {
    979            let location = input.current_source_location();
    980            match *input.next_including_whitespace()? {
    981                Token::Ident(ref ident) => ident.as_ref(),
    982                ref t => return Err(location.new_unexpected_token_error(t.clone())),
    983            }
    984        } else {
    985            input.expect_ident()?.as_ref()
    986        });
    987 
    988        // Fallback will always be a string value for now as we do not support
    989        // attr() types yet.
    990        let fallback = input
    991            .try_parse(|input| -> Result<AtomString, ParseError<'i>> {
    992                input.expect_comma()?;
    993                Ok(input.expect_string()?.as_ref().into())
    994            })
    995            .unwrap_or_default();
    996 
    997        Ok(Attr {
    998            namespace_prefix,
    999            namespace_url,
   1000            attribute,
   1001            fallback,
   1002        })
   1003    }
   1004 }
   1005 
   1006 impl ToCss for Attr {
   1007    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
   1008    where
   1009        W: Write,
   1010    {
   1011        dest.write_str("attr(")?;
   1012        if !self.namespace_prefix.is_empty() {
   1013            serialize_atom_identifier(&self.namespace_prefix, dest)?;
   1014            dest.write_char('|')?;
   1015        }
   1016        serialize_atom_identifier(&self.attribute, dest)?;
   1017 
   1018        if !self.fallback.is_empty() {
   1019            dest.write_str(", ")?;
   1020            self.fallback.to_css(dest)?;
   1021        }
   1022 
   1023        dest.write_char(')')
   1024    }
   1025 }