tor-browser

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

mod.rs (55659B)


      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 //! Supported CSS properties and the cascade.
      6 
      7 pub mod cascade;
      8 pub mod declaration_block;
      9 
     10 pub use self::cascade::*;
     11 pub use self::declaration_block::*;
     12 pub use self::generated::*;
     13 /// The CSS properties supported by the style system.
     14 /// Generated from the properties.mako.rs template by build.rs
     15 #[macro_use]
     16 #[allow(unsafe_code)]
     17 #[deny(missing_docs)]
     18 pub mod generated {
     19    include!(concat!(env!("OUT_DIR"), "/properties.rs"));
     20 }
     21 
     22 use crate::custom_properties::{self, ComputedCustomProperties};
     23 use crate::derives::*;
     24 use crate::dom::AttributeProvider;
     25 #[cfg(feature = "gecko")]
     26 use crate::gecko_bindings::structs::{CSSPropertyId, NonCustomCSSPropertyId, RefPtr};
     27 use crate::logical_geometry::WritingMode;
     28 use crate::parser::ParserContext;
     29 use crate::stylesheets::CssRuleType;
     30 use crate::stylesheets::Origin;
     31 use crate::stylist::Stylist;
     32 use crate::values::{computed, serialize_atom_name};
     33 use arrayvec::{ArrayVec, Drain as ArrayVecDrain};
     34 use cssparser::{match_ignore_ascii_case, Parser, ParserInput};
     35 use rustc_hash::FxHashMap;
     36 use servo_arc::Arc;
     37 use std::{
     38    borrow::Cow,
     39    fmt::{self, Write},
     40    mem,
     41 };
     42 use style_traits::{
     43    CssString, CssWriter, KeywordsCollectFn, ParseError, ParsingMode, SpecifiedValueInfo, ToCss,
     44    ToTyped, TypedValue,
     45 };
     46 
     47 bitflags! {
     48    /// A set of flags for properties.
     49    #[derive(Clone, Copy)]
     50    pub struct PropertyFlags: u16 {
     51        /// This longhand property applies to ::first-letter.
     52        const APPLIES_TO_FIRST_LETTER = 1 << 1;
     53        /// This longhand property applies to ::first-line.
     54        const APPLIES_TO_FIRST_LINE = 1 << 2;
     55        /// This longhand property applies to ::placeholder.
     56        const APPLIES_TO_PLACEHOLDER = 1 << 3;
     57        ///  This longhand property applies to ::cue.
     58        const APPLIES_TO_CUE = 1 << 4;
     59        /// This longhand property applies to ::marker.
     60        const APPLIES_TO_MARKER = 1 << 5;
     61        /// This property is a legacy shorthand.
     62        ///
     63        /// https://drafts.csswg.org/css-cascade/#legacy-shorthand
     64        const IS_LEGACY_SHORTHAND = 1 << 6;
     65 
     66        /* The following flags are currently not used in Rust code, they
     67         * only need to be listed in corresponding properties so that
     68         * they can be checked in the C++ side via ServoCSSPropList.h. */
     69 
     70        /// This property can be animated on the compositor.
     71        const CAN_ANIMATE_ON_COMPOSITOR = 0;
     72        /// See data.py's documentation about the affects_flags.
     73        const AFFECTS_LAYOUT = 0;
     74        #[allow(missing_docs)]
     75        const AFFECTS_OVERFLOW = 0;
     76        #[allow(missing_docs)]
     77        const AFFECTS_PAINT = 0;
     78    }
     79 }
     80 
     81 /// An enum to represent a CSS Wide keyword.
     82 #[derive(
     83    Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
     84 )]
     85 pub enum CSSWideKeyword {
     86    /// The `initial` keyword.
     87    Initial,
     88    /// The `inherit` keyword.
     89    Inherit,
     90    /// The `unset` keyword.
     91    Unset,
     92    /// The `revert` keyword.
     93    Revert,
     94    /// The `revert-layer` keyword.
     95    RevertLayer,
     96 }
     97 
     98 impl CSSWideKeyword {
     99    /// Returns the string representation of the keyword.
    100    pub fn to_str(&self) -> &'static str {
    101        match *self {
    102            CSSWideKeyword::Initial => "initial",
    103            CSSWideKeyword::Inherit => "inherit",
    104            CSSWideKeyword::Unset => "unset",
    105            CSSWideKeyword::Revert => "revert",
    106            CSSWideKeyword::RevertLayer => "revert-layer",
    107        }
    108    }
    109 }
    110 
    111 impl CSSWideKeyword {
    112    /// Parses a CSS wide keyword from a CSS identifier.
    113    pub fn from_ident(ident: &str) -> Result<Self, ()> {
    114        Ok(match_ignore_ascii_case! { ident,
    115            "initial" => CSSWideKeyword::Initial,
    116            "inherit" => CSSWideKeyword::Inherit,
    117            "unset" => CSSWideKeyword::Unset,
    118            "revert" => CSSWideKeyword::Revert,
    119            "revert-layer" => CSSWideKeyword::RevertLayer,
    120            _ => return Err(()),
    121        })
    122    }
    123 
    124    /// Parses a CSS wide keyword completely.
    125    pub fn parse(input: &mut Parser) -> Result<Self, ()> {
    126        let keyword = {
    127            let ident = input.expect_ident().map_err(|_| ())?;
    128            Self::from_ident(ident)?
    129        };
    130        input.expect_exhausted().map_err(|_| ())?;
    131        Ok(keyword)
    132    }
    133 }
    134 
    135 /// A declaration using a CSS-wide keyword.
    136 #[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf)]
    137 pub struct WideKeywordDeclaration {
    138    #[css(skip)]
    139    id: LonghandId,
    140    /// The CSS-wide keyword.
    141    pub keyword: CSSWideKeyword,
    142 }
    143 
    144 // XXX Switch back to ToTyped derive once it can automatically handle structs
    145 // Tracking in bug 1991631
    146 impl ToTyped for WideKeywordDeclaration {
    147    fn to_typed(&self) -> Option<TypedValue> {
    148        self.keyword.to_typed()
    149    }
    150 }
    151 
    152 /// An unparsed declaration that contains `var()` functions.
    153 #[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
    154 pub struct VariableDeclaration {
    155    /// The id of the property this declaration represents.
    156    #[css(skip)]
    157    id: LonghandId,
    158    /// The unparsed value of the variable.
    159    #[ignore_malloc_size_of = "Arc"]
    160    pub value: Arc<UnparsedValue>,
    161 }
    162 
    163 /// A custom property declaration value is either an unparsed value or a CSS
    164 /// wide-keyword.
    165 #[derive(Clone, PartialEq, ToCss, ToShmem)]
    166 pub enum CustomDeclarationValue {
    167    /// An unparsed value.
    168    Unparsed(Arc<custom_properties::SpecifiedValue>),
    169    /// An already-parsed value.
    170    Parsed(Arc<crate::properties_and_values::value::SpecifiedValue>),
    171    /// A wide keyword.
    172    CSSWideKeyword(CSSWideKeyword),
    173 }
    174 
    175 /// A custom property declaration with the property name and the declared value.
    176 #[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
    177 pub struct CustomDeclaration {
    178    /// The name of the custom property.
    179    #[css(skip)]
    180    pub name: custom_properties::Name,
    181    /// The value of the custom property.
    182    #[ignore_malloc_size_of = "Arc"]
    183    pub value: CustomDeclarationValue,
    184 }
    185 
    186 impl fmt::Debug for PropertyDeclaration {
    187    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    188        self.id().to_css(&mut CssWriter::new(f))?;
    189        f.write_str(": ")?;
    190 
    191        // Because PropertyDeclaration::to_css requires CssStringWriter, we can't write
    192        // it directly to f, and need to allocate an intermediate string. This is
    193        // fine for debug-only code.
    194        let mut s = CssString::new();
    195        self.to_css(&mut s)?;
    196        write!(f, "{}", s)
    197    }
    198 }
    199 
    200 /// A longhand or shorthand property.
    201 #[derive(
    202    Clone, Copy, Debug, PartialEq, Eq, Hash, ToComputedValue, ToResolvedValue, ToShmem, MallocSizeOf,
    203 )]
    204 #[repr(C)]
    205 pub struct NonCustomPropertyId(u16);
    206 
    207 impl ToCss for NonCustomPropertyId {
    208    #[inline]
    209    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    210    where
    211        W: Write,
    212    {
    213        dest.write_str(self.name())
    214    }
    215 }
    216 
    217 impl NonCustomPropertyId {
    218    /// Returns the underlying index, used for use counter.
    219    pub fn bit(self) -> usize {
    220        self.0 as usize
    221    }
    222 
    223    /// Convert a `NonCustomPropertyId` into a `NonCustomCSSPropertyId`.
    224    #[cfg(feature = "gecko")]
    225    #[inline]
    226    pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
    227        // unsafe: guaranteed by static_assert_noncustomcsspropertyid.
    228        unsafe { mem::transmute(self.0) }
    229    }
    230 
    231    /// Convert an `NonCustomCSSPropertyId` into a `NonCustomPropertyId`.
    232    #[cfg(feature = "gecko")]
    233    #[inline]
    234    pub fn from_noncustomcsspropertyid(prop: NonCustomCSSPropertyId) -> Option<Self> {
    235        let prop = prop as u16;
    236        if prop >= property_counts::NON_CUSTOM as u16 {
    237            return None;
    238        }
    239        // guaranteed by static_assert_noncustomcsspropertyid above.
    240        Some(NonCustomPropertyId(prop))
    241    }
    242 
    243    /// Resolves the alias of a given property if needed.
    244    pub fn unaliased(self) -> Self {
    245        let Some(alias_id) = self.as_alias() else {
    246            return self;
    247        };
    248        alias_id.aliased_property()
    249    }
    250 
    251    /// Turns this `NonCustomPropertyId` into a `PropertyId`.
    252    #[inline]
    253    pub fn to_property_id(self) -> PropertyId {
    254        PropertyId::NonCustom(self)
    255    }
    256 
    257    /// Returns a longhand id, if this property is one.
    258    #[inline]
    259    pub fn as_longhand(self) -> Option<LonghandId> {
    260        if self.0 < property_counts::LONGHANDS as u16 {
    261            return Some(unsafe { mem::transmute(self.0 as u16) });
    262        }
    263        None
    264    }
    265 
    266    /// Returns a shorthand id, if this property is one.
    267    #[inline]
    268    pub fn as_shorthand(self) -> Option<ShorthandId> {
    269        if self.0 >= property_counts::LONGHANDS as u16
    270            && self.0 < property_counts::LONGHANDS_AND_SHORTHANDS as u16
    271        {
    272            return Some(unsafe { mem::transmute(self.0 - (property_counts::LONGHANDS as u16)) });
    273        }
    274        None
    275    }
    276 
    277    /// Returns an alias id, if this property is one.
    278    #[inline]
    279    pub fn as_alias(self) -> Option<AliasId> {
    280        debug_assert!((self.0 as usize) < property_counts::NON_CUSTOM);
    281        if self.0 >= property_counts::LONGHANDS_AND_SHORTHANDS as u16 {
    282            return Some(unsafe {
    283                mem::transmute(self.0 - (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
    284            });
    285        }
    286        None
    287    }
    288 
    289    /// Returns either a longhand or a shorthand, resolving aliases.
    290    #[inline]
    291    pub fn longhand_or_shorthand(self) -> Result<LonghandId, ShorthandId> {
    292        let id = self.unaliased();
    293        match id.as_longhand() {
    294            Some(lh) => Ok(lh),
    295            None => Err(id.as_shorthand().unwrap()),
    296        }
    297    }
    298 
    299    /// Converts a longhand id into a non-custom property id.
    300    #[inline]
    301    pub const fn from_longhand(id: LonghandId) -> Self {
    302        Self(id as u16)
    303    }
    304 
    305    /// Converts a shorthand id into a non-custom property id.
    306    #[inline]
    307    pub const fn from_shorthand(id: ShorthandId) -> Self {
    308        Self((id as u16) + (property_counts::LONGHANDS as u16))
    309    }
    310 
    311    /// Converts an alias id into a non-custom property id.
    312    #[inline]
    313    pub const fn from_alias(id: AliasId) -> Self {
    314        Self((id as u16) + (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
    315    }
    316 }
    317 
    318 impl From<LonghandId> for NonCustomPropertyId {
    319    #[inline]
    320    fn from(id: LonghandId) -> Self {
    321        Self::from_longhand(id)
    322    }
    323 }
    324 
    325 impl From<ShorthandId> for NonCustomPropertyId {
    326    #[inline]
    327    fn from(id: ShorthandId) -> Self {
    328        Self::from_shorthand(id)
    329    }
    330 }
    331 
    332 impl From<AliasId> for NonCustomPropertyId {
    333    #[inline]
    334    fn from(id: AliasId) -> Self {
    335        Self::from_alias(id)
    336    }
    337 }
    338 
    339 /// Representation of a CSS property, that is, either a longhand, a shorthand, or a custom
    340 /// property.
    341 #[derive(Clone, Eq, PartialEq, Debug)]
    342 pub enum PropertyId {
    343    /// An alias for a shorthand property.
    344    NonCustom(NonCustomPropertyId),
    345    /// A custom property.
    346    Custom(custom_properties::Name),
    347 }
    348 
    349 impl ToCss for PropertyId {
    350    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    351    where
    352        W: Write,
    353    {
    354        match *self {
    355            PropertyId::NonCustom(id) => dest.write_str(id.name()),
    356            PropertyId::Custom(ref name) => {
    357                dest.write_str("--")?;
    358                serialize_atom_name(name, dest)
    359            },
    360        }
    361    }
    362 }
    363 
    364 impl PropertyId {
    365    /// Return the longhand id that this property id represents.
    366    #[inline]
    367    pub fn longhand_id(&self) -> Option<LonghandId> {
    368        self.non_custom_non_alias_id()?.as_longhand()
    369    }
    370 
    371    /// Returns true if this property is one of the animatable properties.
    372    pub fn is_animatable(&self) -> bool {
    373        match self {
    374            Self::NonCustom(id) => id.is_animatable(),
    375            Self::Custom(_) => cfg!(feature = "gecko"),
    376        }
    377    }
    378 
    379    /// Returns a given property from the given name, _regardless of whether it is enabled or
    380    /// not_, or Err(()) for unknown properties.
    381    ///
    382    /// Do not use for non-testing purposes.
    383    pub fn parse_unchecked_for_testing(name: &str) -> Result<Self, ()> {
    384        Self::parse_unchecked(name, None)
    385    }
    386 
    387    /// Parses a property name, and returns an error if it's unknown or isn't enabled for all
    388    /// content.
    389    #[inline]
    390    pub fn parse_enabled_for_all_content(name: &str) -> Result<Self, ()> {
    391        let id = Self::parse_unchecked(name, None)?;
    392 
    393        if !id.enabled_for_all_content() {
    394            return Err(());
    395        }
    396 
    397        Ok(id)
    398    }
    399 
    400    /// Parses a property name, and returns an error if it's unknown or isn't allowed in this
    401    /// context.
    402    #[inline]
    403    pub fn parse(name: &str, context: &ParserContext) -> Result<Self, ()> {
    404        let id = Self::parse_unchecked(name, context.use_counters)?;
    405        if !id.allowed_in(context) {
    406            return Err(());
    407        }
    408        Ok(id)
    409    }
    410 
    411    /// Parses a property name, and returns an error if it's unknown or isn't allowed in this
    412    /// context, ignoring the rule_type checks.
    413    ///
    414    /// This is useful for parsing stuff from CSS values, for example.
    415    #[inline]
    416    pub fn parse_ignoring_rule_type(name: &str, context: &ParserContext) -> Result<Self, ()> {
    417        let id = Self::parse_unchecked(name, None)?;
    418        if !id.allowed_in_ignoring_rule_type(context) {
    419            return Err(());
    420        }
    421        Ok(id)
    422    }
    423 
    424    /// Returns a property id from Gecko's NonCustomCSSPropertyId.
    425    #[cfg(feature = "gecko")]
    426    #[inline]
    427    pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
    428        Some(NonCustomPropertyId::from_noncustomcsspropertyid(id)?.to_property_id())
    429    }
    430 
    431    /// Returns a property id from Gecko's CSSPropertyId.
    432    #[cfg(feature = "gecko")]
    433    #[inline]
    434    pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
    435        Some(
    436            if property.mId == NonCustomCSSPropertyId::eCSSPropertyExtra_variable {
    437                debug_assert!(!property.mCustomName.mRawPtr.is_null());
    438                Self::Custom(unsafe { crate::Atom::from_raw(property.mCustomName.mRawPtr) })
    439            } else {
    440                Self::NonCustom(NonCustomPropertyId::from_noncustomcsspropertyid(
    441                    property.mId,
    442                )?)
    443            },
    444        )
    445    }
    446 
    447    /// Returns true if the property is a shorthand or shorthand alias.
    448    #[inline]
    449    pub fn is_shorthand(&self) -> bool {
    450        self.as_shorthand().is_ok()
    451    }
    452 
    453    /// Given this property id, get it either as a shorthand or as a
    454    /// `PropertyDeclarationId`.
    455    pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId<'_>> {
    456        match *self {
    457            Self::NonCustom(id) => match id.longhand_or_shorthand() {
    458                Ok(lh) => Err(PropertyDeclarationId::Longhand(lh)),
    459                Err(sh) => Ok(sh),
    460            },
    461            Self::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),
    462        }
    463    }
    464 
    465    /// Returns the `NonCustomPropertyId` corresponding to this property id.
    466    pub fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
    467        match *self {
    468            Self::Custom(_) => None,
    469            Self::NonCustom(id) => Some(id),
    470        }
    471    }
    472 
    473    /// Returns non-alias NonCustomPropertyId corresponding to this
    474    /// property id.
    475    fn non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId> {
    476        self.non_custom_id().map(NonCustomPropertyId::unaliased)
    477    }
    478 
    479    /// Whether the property is enabled for all content regardless of the
    480    /// stylesheet it was declared on (that is, in practice only checks prefs).
    481    #[inline]
    482    pub fn enabled_for_all_content(&self) -> bool {
    483        let id = match self.non_custom_id() {
    484            // Custom properties are allowed everywhere
    485            None => return true,
    486            Some(id) => id,
    487        };
    488 
    489        id.enabled_for_all_content()
    490    }
    491 
    492    /// Converts this PropertyId in NonCustomCSSPropertyId, resolving aliases to the
    493    /// resolved property, and returning eCSSPropertyExtra_variable for custom
    494    /// properties.
    495    #[cfg(feature = "gecko")]
    496    #[inline]
    497    pub fn to_noncustomcsspropertyid_resolving_aliases(&self) -> NonCustomCSSPropertyId {
    498        match self.non_custom_non_alias_id() {
    499            Some(id) => id.to_noncustomcsspropertyid(),
    500            None => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
    501        }
    502    }
    503 
    504    fn allowed_in(&self, context: &ParserContext) -> bool {
    505        let id = match self.non_custom_id() {
    506            // Custom properties are allowed everywhere, except `position-try`.
    507            None => {
    508                return !context
    509                    .nesting_context
    510                    .rule_types
    511                    .contains(CssRuleType::PositionTry)
    512            },
    513            Some(id) => id,
    514        };
    515        id.allowed_in(context)
    516    }
    517 
    518    #[inline]
    519    fn allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool {
    520        let id = match self.non_custom_id() {
    521            // Custom properties are allowed everywhere
    522            None => return true,
    523            Some(id) => id,
    524        };
    525        id.allowed_in_ignoring_rule_type(context)
    526    }
    527 
    528    /// Whether the property supports the given CSS type.
    529    /// `ty` should a bitflags of constants in style_traits::CssType.
    530    pub fn supports_type(&self, ty: u8) -> bool {
    531        let id = self.non_custom_non_alias_id();
    532        id.map_or(0, |id| id.supported_types()) & ty != 0
    533    }
    534 
    535    /// Collect supported starting word of values of this property.
    536    ///
    537    /// See style_traits::SpecifiedValueInfo::collect_completion_keywords for more
    538    /// details.
    539    pub fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
    540        if let Some(id) = self.non_custom_non_alias_id() {
    541            id.collect_property_completion_keywords(f);
    542        }
    543        CSSWideKeyword::collect_completion_keywords(f);
    544    }
    545 }
    546 
    547 impl ToCss for LonghandId {
    548    #[inline]
    549    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    550    where
    551        W: Write,
    552    {
    553        dest.write_str(self.name())
    554    }
    555 }
    556 
    557 impl fmt::Debug for LonghandId {
    558    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
    559        formatter.write_str(self.name())
    560    }
    561 }
    562 
    563 impl LonghandId {
    564    /// Get the name of this longhand property.
    565    #[inline]
    566    pub fn name(&self) -> &'static str {
    567        NonCustomPropertyId::from(*self).name()
    568    }
    569 
    570    /// Returns whether the longhand property is inherited by default.
    571    #[inline]
    572    pub fn inherited(self) -> bool {
    573        !LonghandIdSet::reset().contains(self)
    574    }
    575 
    576    /// Returns whether the longhand property is zoom-dependent.
    577    #[inline]
    578    pub fn zoom_dependent(self) -> bool {
    579        LonghandIdSet::zoom_dependent().contains(self)
    580    }
    581 
    582    /// Returns true if the property is one that is ignored when document
    583    /// colors are disabled.
    584    #[inline]
    585    pub fn ignored_when_document_colors_disabled(self) -> bool {
    586        LonghandIdSet::ignored_when_colors_disabled().contains(self)
    587    }
    588 
    589    /// Returns whether this longhand is `non_custom` or is a longhand of it.
    590    pub fn is_or_is_longhand_of(self, non_custom: NonCustomPropertyId) -> bool {
    591        match non_custom.longhand_or_shorthand() {
    592            Ok(lh) => self == lh,
    593            Err(sh) => self.is_longhand_of(sh),
    594        }
    595    }
    596 
    597    /// Returns whether this longhand is a longhand of `shorthand`.
    598    pub fn is_longhand_of(self, shorthand: ShorthandId) -> bool {
    599        self.shorthands().any(|s| s == shorthand)
    600    }
    601 
    602    /// Returns whether this property is animatable.
    603    #[inline]
    604    pub fn is_animatable(self) -> bool {
    605        NonCustomPropertyId::from(self).is_animatable()
    606    }
    607 
    608    /// Returns whether this property is animatable in a discrete way.
    609    #[inline]
    610    pub fn is_discrete_animatable(self) -> bool {
    611        LonghandIdSet::discrete_animatable().contains(self)
    612    }
    613 
    614    /// Converts from a LonghandId to an adequate NonCustomCSSPropertyId.
    615    #[cfg(feature = "gecko")]
    616    #[inline]
    617    pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
    618        NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
    619    }
    620 
    621    #[cfg(feature = "gecko")]
    622    /// Returns a longhand id from Gecko's NonCustomCSSPropertyId.
    623    pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
    624        NonCustomPropertyId::from_noncustomcsspropertyid(id)?
    625            .unaliased()
    626            .as_longhand()
    627    }
    628 
    629    /// Return whether this property is logical.
    630    #[inline]
    631    pub fn is_logical(self) -> bool {
    632        LonghandIdSet::logical().contains(self)
    633    }
    634 }
    635 
    636 impl ToCss for ShorthandId {
    637    #[inline]
    638    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    639    where
    640        W: Write,
    641    {
    642        dest.write_str(self.name())
    643    }
    644 }
    645 
    646 impl ShorthandId {
    647    /// Get the name for this shorthand property.
    648    #[inline]
    649    pub fn name(&self) -> &'static str {
    650        NonCustomPropertyId::from(*self).name()
    651    }
    652 
    653    /// Converts from a ShorthandId to an adequate NonCustomCSSPropertyId.
    654    #[cfg(feature = "gecko")]
    655    #[inline]
    656    pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
    657        NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
    658    }
    659 
    660    /// Converts from a NonCustomCSSPropertyId to a ShorthandId.
    661    #[cfg(feature = "gecko")]
    662    #[inline]
    663    pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
    664        NonCustomPropertyId::from_noncustomcsspropertyid(id)?
    665            .unaliased()
    666            .as_shorthand()
    667    }
    668 
    669    /// Finds and returns an appendable value for the given declarations.
    670    ///
    671    /// Returns the optional appendable value.
    672    pub fn get_shorthand_appendable_value<'a, 'b: 'a>(
    673        self,
    674        declarations: &'a [&'b PropertyDeclaration],
    675    ) -> Option<AppendableValue<'a, 'b>> {
    676        let first_declaration = declarations.get(0)?;
    677        let rest = || declarations.iter().skip(1);
    678 
    679        // https://drafts.csswg.org/css-variables/#variables-in-shorthands
    680        if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
    681            if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
    682                return Some(AppendableValue::Css(css));
    683            }
    684            return None;
    685        }
    686 
    687        // Check whether they are all the same CSS-wide keyword.
    688        if let Some(keyword) = first_declaration.get_css_wide_keyword() {
    689            if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {
    690                return Some(AppendableValue::Css(keyword.to_str()));
    691            }
    692            return None;
    693        }
    694 
    695        if self == ShorthandId::All {
    696            // 'all' only supports variables and CSS wide keywords.
    697            return None;
    698        }
    699 
    700        // Check whether all declarations can be serialized as part of shorthand.
    701        if declarations
    702            .iter()
    703            .all(|d| d.may_serialize_as_part_of_shorthand())
    704        {
    705            return Some(AppendableValue::DeclarationsForShorthand(
    706                self,
    707                declarations,
    708            ));
    709        }
    710 
    711        None
    712    }
    713 
    714    /// Returns whether this property is a legacy shorthand.
    715    #[inline]
    716    pub fn is_legacy_shorthand(self) -> bool {
    717        self.flags().contains(PropertyFlags::IS_LEGACY_SHORTHAND)
    718    }
    719 }
    720 
    721 fn parse_non_custom_property_declaration_value_into<'i>(
    722    declarations: &mut SourcePropertyDeclaration,
    723    context: &ParserContext,
    724    input: &mut Parser<'i, '_>,
    725    start: &cssparser::ParserState,
    726    parse_entirely_into: impl FnOnce(
    727        &mut SourcePropertyDeclaration,
    728        &mut Parser<'i, '_>,
    729    ) -> Result<(), ParseError<'i>>,
    730    parsed_wide_keyword: impl FnOnce(&mut SourcePropertyDeclaration, CSSWideKeyword),
    731    parsed_custom: impl FnOnce(&mut SourcePropertyDeclaration, custom_properties::VariableValue),
    732 ) -> Result<(), ParseError<'i>> {
    733    let mut starts_with_curly_block = false;
    734    if let Ok(token) = input.next() {
    735        match token {
    736            cssparser::Token::Ident(ref ident) => match CSSWideKeyword::from_ident(ident) {
    737                Ok(wk) => {
    738                    if input.expect_exhausted().is_ok() {
    739                        return Ok(parsed_wide_keyword(declarations, wk));
    740                    }
    741                },
    742                Err(()) => {},
    743            },
    744            cssparser::Token::CurlyBracketBlock => {
    745                starts_with_curly_block = true;
    746            },
    747            _ => {},
    748        }
    749    };
    750 
    751    input.reset(&start);
    752    input.look_for_arbitrary_substitution_functions(
    753        if static_prefs::pref!("layout.css.attr.enabled") {
    754            &["var", "env", "attr"]
    755        } else {
    756            &["var", "env"]
    757        },
    758    );
    759 
    760    let err = match parse_entirely_into(declarations, input) {
    761        Ok(()) => {
    762            input.seen_arbitrary_substitution_functions();
    763            return Ok(());
    764        },
    765        Err(e) => e,
    766    };
    767 
    768    // Look for var(), env() and top-level curly blocks after the error.
    769    let start_pos = start.position();
    770    let mut at_start = start_pos == input.position();
    771    let mut invalid = false;
    772    while let Ok(token) = input.next() {
    773        if matches!(token, cssparser::Token::CurlyBracketBlock) {
    774            if !starts_with_curly_block || !at_start {
    775                invalid = true;
    776                break;
    777            }
    778        } else if starts_with_curly_block {
    779            invalid = true;
    780            break;
    781        }
    782        at_start = false;
    783    }
    784    if !input.seen_arbitrary_substitution_functions() || invalid {
    785        return Err(err);
    786    }
    787    input.reset(start);
    788    let value = custom_properties::VariableValue::parse(input, &context.url_data)?;
    789    parsed_custom(declarations, value);
    790    Ok(())
    791 }
    792 
    793 impl PropertyDeclaration {
    794    fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option<&str> {
    795        match *self {
    796            PropertyDeclaration::WithVariables(ref declaration) => {
    797                let s = declaration.value.from_shorthand?;
    798                if s != shorthand {
    799                    return None;
    800                }
    801                Some(&*declaration.value.variable_value.css)
    802            },
    803            _ => None,
    804        }
    805    }
    806 
    807    /// Returns a CSS-wide keyword declaration for a given property.
    808    #[inline]
    809    pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
    810        Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
    811    }
    812 
    813    /// Returns a CSS-wide keyword if the declaration's value is one.
    814    #[inline]
    815    pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
    816        match *self {
    817            PropertyDeclaration::CSSWideKeyword(ref declaration) => Some(declaration.keyword),
    818            _ => None,
    819        }
    820    }
    821 
    822    /// Returns whether the declaration may be serialized as part of a shorthand.
    823    ///
    824    /// This method returns false if this declaration contains variable or has a
    825    /// CSS-wide keyword value, since these values cannot be serialized as part
    826    /// of a shorthand.
    827    ///
    828    /// Caller should check `with_variables_from_shorthand()` and whether all
    829    /// needed declarations has the same CSS-wide keyword first.
    830    ///
    831    /// Note that, serialization of a shorthand may still fail because of other
    832    /// property-specific requirement even when this method returns true for all
    833    /// the longhand declarations.
    834    pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
    835        match *self {
    836            PropertyDeclaration::CSSWideKeyword(..) | PropertyDeclaration::WithVariables(..) => {
    837                false
    838            },
    839            PropertyDeclaration::Custom(..) => {
    840                unreachable!("Serializing a custom property as part of shorthand?")
    841            },
    842            _ => true,
    843        }
    844    }
    845 
    846    /// Returns true if this property declaration is for one of the animatable properties.
    847    pub fn is_animatable(&self) -> bool {
    848        self.id().is_animatable()
    849    }
    850 
    851    /// Returns true if this property is a custom property, false
    852    /// otherwise.
    853    pub fn is_custom(&self) -> bool {
    854        matches!(*self, PropertyDeclaration::Custom(..))
    855    }
    856 
    857    /// The `context` parameter controls this:
    858    ///
    859    /// <https://drafts.csswg.org/css-animations/#keyframes>
    860    /// > The <declaration-list> inside of <keyframe-block> accepts any CSS property
    861    /// > except those defined in this specification,
    862    /// > but does accept the `animation-play-state` property and interprets it specially.
    863    ///
    864    /// This will not actually parse Importance values, and will always set things
    865    /// to Importance::Normal. Parsing Importance values is the job of PropertyDeclarationParser,
    866    /// we only set them here so that we don't have to reallocate
    867    pub fn parse_into<'i, 't>(
    868        declarations: &mut SourcePropertyDeclaration,
    869        id: PropertyId,
    870        context: &ParserContext,
    871        input: &mut Parser<'i, 't>,
    872    ) -> Result<(), ParseError<'i>> {
    873        assert!(declarations.is_empty());
    874        debug_assert!(id.allowed_in(context), "{:?}", id);
    875        input.skip_whitespace();
    876 
    877        let start = input.state();
    878        let non_custom_id = match id {
    879            PropertyId::Custom(property_name) => {
    880                let value = match input.try_parse(CSSWideKeyword::parse) {
    881                    Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
    882                    Err(()) => CustomDeclarationValue::Unparsed(Arc::new(
    883                        custom_properties::VariableValue::parse(input, &context.url_data)?,
    884                    )),
    885                };
    886                declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
    887                    name: property_name,
    888                    value,
    889                }));
    890                return Ok(());
    891            },
    892            PropertyId::NonCustom(id) => id,
    893        };
    894        match non_custom_id.longhand_or_shorthand() {
    895            Ok(longhand_id) => {
    896                parse_non_custom_property_declaration_value_into(
    897                    declarations,
    898                    context,
    899                    input,
    900                    &start,
    901                    |declarations, input| {
    902                        let decl = input
    903                            .parse_entirely(|input| longhand_id.parse_value(context, input))?;
    904                        declarations.push(decl);
    905                        Ok(())
    906                    },
    907                    |declarations, wk| {
    908                        declarations.push(PropertyDeclaration::css_wide_keyword(longhand_id, wk));
    909                    },
    910                    |declarations, variable_value| {
    911                        declarations.push(PropertyDeclaration::WithVariables(VariableDeclaration {
    912                            id: longhand_id,
    913                            value: Arc::new(UnparsedValue {
    914                                variable_value,
    915                                from_shorthand: None,
    916                            }),
    917                        }))
    918                    },
    919                )?;
    920            },
    921            Err(shorthand_id) => {
    922                parse_non_custom_property_declaration_value_into(
    923                    declarations,
    924                    context,
    925                    input,
    926                    &start,
    927                    // Not using parse_entirely here: each ShorthandId::parse_into function needs
    928                    // to do so *before* pushing to `declarations`.
    929                    |declarations, input| shorthand_id.parse_into(declarations, context, input),
    930                    |declarations, wk| {
    931                        if shorthand_id == ShorthandId::All {
    932                            declarations.all_shorthand = AllShorthand::CSSWideKeyword(wk)
    933                        } else {
    934                            for longhand in shorthand_id.longhands() {
    935                                declarations
    936                                    .push(PropertyDeclaration::css_wide_keyword(longhand, wk));
    937                            }
    938                        }
    939                    },
    940                    |declarations, variable_value| {
    941                        let unparsed = Arc::new(UnparsedValue {
    942                            variable_value,
    943                            from_shorthand: Some(shorthand_id),
    944                        });
    945                        if shorthand_id == ShorthandId::All {
    946                            declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
    947                        } else {
    948                            for id in shorthand_id.longhands() {
    949                                declarations.push(PropertyDeclaration::WithVariables(
    950                                    VariableDeclaration {
    951                                        id,
    952                                        value: unparsed.clone(),
    953                                    },
    954                                ))
    955                            }
    956                        }
    957                    },
    958                )?;
    959            },
    960        }
    961        if let Some(use_counters) = context.use_counters {
    962            use_counters.non_custom_properties.record(non_custom_id);
    963        }
    964        Ok(())
    965    }
    966 }
    967 
    968 /// A PropertyDeclarationId without references, for use as a hash map key.
    969 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
    970 pub enum OwnedPropertyDeclarationId {
    971    /// A longhand.
    972    Longhand(LonghandId),
    973    /// A custom property declaration.
    974    Custom(custom_properties::Name),
    975 }
    976 
    977 impl OwnedPropertyDeclarationId {
    978    /// Return whether this property is logical.
    979    #[inline]
    980    pub fn is_logical(&self) -> bool {
    981        self.as_borrowed().is_logical()
    982    }
    983 
    984    /// Returns the corresponding PropertyDeclarationId.
    985    #[inline]
    986    pub fn as_borrowed(&self) -> PropertyDeclarationId<'_> {
    987        match self {
    988            Self::Longhand(id) => PropertyDeclarationId::Longhand(*id),
    989            Self::Custom(name) => PropertyDeclarationId::Custom(name),
    990        }
    991    }
    992 
    993    /// Convert an `CSSPropertyId` into an `OwnedPropertyDeclarationId`.
    994    #[cfg(feature = "gecko")]
    995    #[inline]
    996    pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
    997        Some(match PropertyId::from_gecko_css_property_id(property)? {
    998            PropertyId::Custom(name) => Self::Custom(name),
    999            PropertyId::NonCustom(id) => Self::Longhand(id.as_longhand()?),
   1000        })
   1001    }
   1002 }
   1003 
   1004 /// An identifier for a given property declaration, which can be either a
   1005 /// longhand or a custom property.
   1006 #[derive(Clone, Copy, Debug, PartialEq, MallocSizeOf)]
   1007 pub enum PropertyDeclarationId<'a> {
   1008    /// A longhand.
   1009    Longhand(LonghandId),
   1010    /// A custom property declaration.
   1011    Custom(&'a custom_properties::Name),
   1012 }
   1013 
   1014 impl<'a> ToCss for PropertyDeclarationId<'a> {
   1015    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
   1016    where
   1017        W: Write,
   1018    {
   1019        match *self {
   1020            PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
   1021            PropertyDeclarationId::Custom(name) => {
   1022                dest.write_str("--")?;
   1023                serialize_atom_name(name, dest)
   1024            },
   1025        }
   1026    }
   1027 }
   1028 
   1029 impl<'a> PropertyDeclarationId<'a> {
   1030    /// Returns PropertyFlags for given property.
   1031    #[inline(always)]
   1032    pub fn flags(&self) -> PropertyFlags {
   1033        match self {
   1034            Self::Longhand(id) => id.flags(),
   1035            Self::Custom(_) => PropertyFlags::empty(),
   1036        }
   1037    }
   1038 
   1039    /// Convert to an OwnedPropertyDeclarationId.
   1040    pub fn to_owned(&self) -> OwnedPropertyDeclarationId {
   1041        match self {
   1042            PropertyDeclarationId::Longhand(id) => OwnedPropertyDeclarationId::Longhand(*id),
   1043            PropertyDeclarationId::Custom(name) => {
   1044                OwnedPropertyDeclarationId::Custom((*name).clone())
   1045            },
   1046        }
   1047    }
   1048 
   1049    /// Whether a given declaration id is either the same as `other`, or a
   1050    /// longhand of it.
   1051    pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
   1052        match *self {
   1053            PropertyDeclarationId::Longhand(id) => match *other {
   1054                PropertyId::NonCustom(non_custom_id) => id.is_or_is_longhand_of(non_custom_id),
   1055                PropertyId::Custom(_) => false,
   1056            },
   1057            PropertyDeclarationId::Custom(name) => {
   1058                matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
   1059            },
   1060        }
   1061    }
   1062 
   1063    /// Whether a given declaration id is a longhand belonging to this
   1064    /// shorthand.
   1065    pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
   1066        match *self {
   1067            PropertyDeclarationId::Longhand(ref id) => id.is_longhand_of(shorthand),
   1068            _ => false,
   1069        }
   1070    }
   1071 
   1072    /// Returns the name of the property without CSS escaping.
   1073    pub fn name(&self) -> Cow<'static, str> {
   1074        match *self {
   1075            PropertyDeclarationId::Longhand(id) => id.name().into(),
   1076            PropertyDeclarationId::Custom(name) => {
   1077                let mut s = String::new();
   1078                write!(&mut s, "--{}", name).unwrap();
   1079                s.into()
   1080            },
   1081        }
   1082    }
   1083 
   1084    /// Returns longhand id if it is, None otherwise.
   1085    #[inline]
   1086    pub fn as_longhand(&self) -> Option<LonghandId> {
   1087        match *self {
   1088            PropertyDeclarationId::Longhand(id) => Some(id),
   1089            _ => None,
   1090        }
   1091    }
   1092 
   1093    /// Return whether this property is logical.
   1094    #[inline]
   1095    pub fn is_logical(&self) -> bool {
   1096        match self {
   1097            PropertyDeclarationId::Longhand(id) => id.is_logical(),
   1098            PropertyDeclarationId::Custom(_) => false,
   1099        }
   1100    }
   1101 
   1102    /// If this is a logical property, return the corresponding physical one in
   1103    /// the given writing mode.
   1104    ///
   1105    /// Otherwise, return unchanged.
   1106    #[inline]
   1107    pub fn to_physical(&self, wm: WritingMode) -> Self {
   1108        match self {
   1109            Self::Longhand(id) => Self::Longhand(id.to_physical(wm)),
   1110            Self::Custom(_) => self.clone(),
   1111        }
   1112    }
   1113 
   1114    /// Returns whether this property is animatable.
   1115    #[inline]
   1116    pub fn is_animatable(&self) -> bool {
   1117        match self {
   1118            Self::Longhand(id) => id.is_animatable(),
   1119            Self::Custom(_) => cfg!(feature = "gecko"),
   1120        }
   1121    }
   1122 
   1123    /// Returns whether this property is animatable in a discrete way.
   1124    #[inline]
   1125    pub fn is_discrete_animatable(&self) -> bool {
   1126        match self {
   1127            Self::Longhand(longhand) => longhand.is_discrete_animatable(),
   1128            // TODO(bug 1885995): Refine this.
   1129            Self::Custom(_) => cfg!(feature = "gecko"),
   1130        }
   1131    }
   1132 
   1133    /// Converts from a to an adequate NonCustomCSSPropertyId, returning
   1134    /// eCSSPropertyExtra_variable for custom properties.
   1135    #[cfg(feature = "gecko")]
   1136    #[inline]
   1137    pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
   1138        match self {
   1139            PropertyDeclarationId::Longhand(id) => id.to_noncustomcsspropertyid(),
   1140            PropertyDeclarationId::Custom(_) => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
   1141        }
   1142    }
   1143 
   1144    /// Convert a `PropertyDeclarationId` into an `CSSPropertyId`
   1145    ///
   1146    /// FIXME(emilio, bug 1870107): We should consider using cbindgen to generate the property id
   1147    /// representation or so.
   1148    #[cfg(feature = "gecko")]
   1149    #[inline]
   1150    pub fn to_gecko_css_property_id(&self) -> CSSPropertyId {
   1151        match self {
   1152            Self::Longhand(id) => CSSPropertyId {
   1153                mId: id.to_noncustomcsspropertyid(),
   1154                mCustomName: RefPtr::null(),
   1155            },
   1156            Self::Custom(name) => {
   1157                let mut property_id = CSSPropertyId {
   1158                    mId: NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
   1159                    mCustomName: RefPtr::null(),
   1160                };
   1161                property_id.mCustomName.mRawPtr = (*name).clone().into_addrefed();
   1162                property_id
   1163            },
   1164        }
   1165    }
   1166 }
   1167 
   1168 /// A set of all properties.
   1169 #[derive(Clone, PartialEq, Default)]
   1170 pub struct NonCustomPropertyIdSet {
   1171    storage: [u32; ((property_counts::NON_CUSTOM as usize) - 1 + 32) / 32],
   1172 }
   1173 
   1174 impl NonCustomPropertyIdSet {
   1175    /// Creates an empty `NonCustomPropertyIdSet`.
   1176    pub fn new() -> Self {
   1177        Self {
   1178            storage: Default::default(),
   1179        }
   1180    }
   1181 
   1182    /// Insert a non-custom-property in the set.
   1183    #[inline]
   1184    pub fn insert(&mut self, id: NonCustomPropertyId) {
   1185        let bit = id.0 as usize;
   1186        self.storage[bit / 32] |= 1 << (bit % 32);
   1187    }
   1188 
   1189    /// Return whether the given property is in the set
   1190    #[inline]
   1191    pub fn contains(&self, id: NonCustomPropertyId) -> bool {
   1192        let bit = id.0 as usize;
   1193        (self.storage[bit / 32] & (1 << (bit % 32))) != 0
   1194    }
   1195 }
   1196 
   1197 /// A set of longhand properties
   1198 #[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
   1199 pub struct LonghandIdSet {
   1200    storage: [u32; ((property_counts::LONGHANDS as usize) - 1 + 32) / 32],
   1201 }
   1202 
   1203 to_shmem::impl_trivial_to_shmem!(LonghandIdSet);
   1204 
   1205 impl LonghandIdSet {
   1206    /// Return an empty LonghandIdSet.
   1207    #[inline]
   1208    pub fn new() -> Self {
   1209        Self {
   1210            storage: Default::default(),
   1211        }
   1212    }
   1213 
   1214    /// Iterate over the current longhand id set.
   1215    pub fn iter(&self) -> LonghandIdSetIterator<'_> {
   1216        LonghandIdSetIterator {
   1217            chunks: &self.storage,
   1218            cur_chunk: 0,
   1219            cur_bit: 0,
   1220        }
   1221    }
   1222 
   1223    /// Returns whether this set contains at least every longhand that `other`
   1224    /// also contains.
   1225    pub fn contains_all(&self, other: &Self) -> bool {
   1226        for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
   1227            if (*self_cell & *other_cell) != *other_cell {
   1228                return false;
   1229            }
   1230        }
   1231        true
   1232    }
   1233 
   1234    /// Returns whether this set contains any longhand that `other` also contains.
   1235    pub fn contains_any(&self, other: &Self) -> bool {
   1236        for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
   1237            if (*self_cell & *other_cell) != 0 {
   1238                return true;
   1239            }
   1240        }
   1241        false
   1242    }
   1243 
   1244    /// Remove all the given properties from the set.
   1245    #[inline]
   1246    pub fn remove_all(&mut self, other: &Self) {
   1247        for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
   1248            *self_cell &= !*other_cell;
   1249        }
   1250    }
   1251 
   1252    /// Return whether the given property is in the set
   1253    #[inline]
   1254    pub fn contains(&self, id: LonghandId) -> bool {
   1255        let bit = id as usize;
   1256        (self.storage[bit / 32] & (1 << (bit % 32))) != 0
   1257    }
   1258 
   1259    /// Return whether this set contains any reset longhand.
   1260    #[inline]
   1261    pub fn contains_any_reset(&self) -> bool {
   1262        self.contains_any(Self::reset())
   1263    }
   1264 
   1265    /// Add the given property to the set
   1266    #[inline]
   1267    pub fn insert(&mut self, id: LonghandId) {
   1268        let bit = id as usize;
   1269        self.storage[bit / 32] |= 1 << (bit % 32);
   1270    }
   1271 
   1272    /// Remove the given property from the set
   1273    #[inline]
   1274    pub fn remove(&mut self, id: LonghandId) {
   1275        let bit = id as usize;
   1276        self.storage[bit / 32] &= !(1 << (bit % 32));
   1277    }
   1278 
   1279    /// Clear all bits
   1280    #[inline]
   1281    pub fn clear(&mut self) {
   1282        for cell in &mut self.storage {
   1283            *cell = 0
   1284        }
   1285    }
   1286 
   1287    /// Returns whether the set is empty.
   1288    #[inline]
   1289    pub fn is_empty(&self) -> bool {
   1290        self.storage.iter().all(|c| *c == 0)
   1291    }
   1292 }
   1293 
   1294 /// An iterator over a set of longhand ids.
   1295 pub struct LonghandIdSetIterator<'a> {
   1296    chunks: &'a [u32],
   1297    cur_chunk: u32,
   1298    cur_bit: u32, // [0..31], note that zero means the end-most bit
   1299 }
   1300 
   1301 impl<'a> Iterator for LonghandIdSetIterator<'a> {
   1302    type Item = LonghandId;
   1303 
   1304    fn next(&mut self) -> Option<Self::Item> {
   1305        loop {
   1306            debug_assert!(self.cur_bit < 32);
   1307            let cur_chunk = self.cur_chunk;
   1308            let cur_bit = self.cur_bit;
   1309            let chunk = *self.chunks.get(cur_chunk as usize)?;
   1310            let next_bit = (chunk >> cur_bit).trailing_zeros();
   1311            if next_bit == 32 {
   1312                // Totally empty chunk, skip it.
   1313                self.cur_bit = 0;
   1314                self.cur_chunk += 1;
   1315                continue;
   1316            }
   1317            debug_assert!(cur_bit + next_bit < 32);
   1318            let longhand_id = cur_chunk * 32 + cur_bit + next_bit;
   1319            debug_assert!(longhand_id as usize <= property_counts::LONGHANDS);
   1320            let id: LonghandId = unsafe { mem::transmute(longhand_id as u16) };
   1321            self.cur_bit += next_bit + 1;
   1322            if self.cur_bit == 32 {
   1323                self.cur_bit = 0;
   1324                self.cur_chunk += 1;
   1325            }
   1326            return Some(id);
   1327        }
   1328    }
   1329 }
   1330 
   1331 /// An ArrayVec of subproperties, contains space for the longest shorthand except all.
   1332 pub type SubpropertiesVec<T> = ArrayVec<T, { property_counts::MAX_SHORTHAND_EXPANDED }>;
   1333 
   1334 /// A stack-allocated vector of `PropertyDeclaration`
   1335 /// large enough to parse one CSS `key: value` declaration.
   1336 /// (Shorthands expand to multiple `PropertyDeclaration`s.)
   1337 #[derive(Default)]
   1338 pub struct SourcePropertyDeclaration {
   1339    /// The storage for the actual declarations (except for all).
   1340    pub declarations: SubpropertiesVec<PropertyDeclaration>,
   1341    /// Stored separately to keep SubpropertiesVec smaller.
   1342    pub all_shorthand: AllShorthand,
   1343 }
   1344 
   1345 // This is huge, but we allocate it on the stack and then never move it,
   1346 // we only pass `&mut SourcePropertyDeclaration` references around.
   1347 #[cfg(feature = "gecko")]
   1348 size_of_test!(SourcePropertyDeclaration, 632);
   1349 #[cfg(feature = "servo")]
   1350 size_of_test!(SourcePropertyDeclaration, 568);
   1351 
   1352 impl SourcePropertyDeclaration {
   1353    /// Create one with a single PropertyDeclaration.
   1354    #[inline]
   1355    pub fn with_one(decl: PropertyDeclaration) -> Self {
   1356        let mut result = Self::default();
   1357        result.declarations.push(decl);
   1358        result
   1359    }
   1360 
   1361    /// Similar to Vec::drain: leaves this empty when the return value is dropped.
   1362    pub fn drain(&mut self) -> SourcePropertyDeclarationDrain<'_> {
   1363        SourcePropertyDeclarationDrain {
   1364            declarations: self.declarations.drain(..),
   1365            all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
   1366        }
   1367    }
   1368 
   1369    /// Reset to initial state
   1370    pub fn clear(&mut self) {
   1371        self.declarations.clear();
   1372        self.all_shorthand = AllShorthand::NotSet;
   1373    }
   1374 
   1375    /// Whether we're empty.
   1376    pub fn is_empty(&self) -> bool {
   1377        self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
   1378    }
   1379 
   1380    /// Push a single declaration.
   1381    pub fn push(&mut self, declaration: PropertyDeclaration) {
   1382        let _result = self.declarations.try_push(declaration);
   1383        debug_assert!(_result.is_ok());
   1384    }
   1385 }
   1386 
   1387 /// Return type of SourcePropertyDeclaration::drain
   1388 pub struct SourcePropertyDeclarationDrain<'a> {
   1389    /// A drain over the non-all declarations.
   1390    pub declarations:
   1391        ArrayVecDrain<'a, PropertyDeclaration, { property_counts::MAX_SHORTHAND_EXPANDED }>,
   1392    /// The all shorthand that was set.
   1393    pub all_shorthand: AllShorthand,
   1394 }
   1395 
   1396 /// An unparsed property value that contains `var()` functions.
   1397 #[derive(Debug, Eq, PartialEq, ToShmem)]
   1398 pub struct UnparsedValue {
   1399    /// The variable value, references and so on.
   1400    pub(super) variable_value: custom_properties::VariableValue,
   1401    /// The shorthand this came from.
   1402    from_shorthand: Option<ShorthandId>,
   1403 }
   1404 
   1405 impl ToCss for UnparsedValue {
   1406    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
   1407    where
   1408        W: Write,
   1409    {
   1410        // https://drafts.csswg.org/css-variables/#variables-in-shorthands
   1411        if self.from_shorthand.is_none() {
   1412            self.variable_value.to_css(dest)?;
   1413        }
   1414        Ok(())
   1415    }
   1416 }
   1417 
   1418 /// A simple cache for properties that come from a shorthand and have variable
   1419 /// references.
   1420 ///
   1421 /// This cache works because of the fact that you can't have competing values
   1422 /// for a given longhand coming from the same shorthand (but note that this is
   1423 /// why the shorthand needs to be part of the cache key).
   1424 pub type ShorthandsWithPropertyReferencesCache =
   1425    FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
   1426 
   1427 impl UnparsedValue {
   1428    fn substitute_variables<'cache>(
   1429        &self,
   1430        longhand_id: LonghandId,
   1431        custom_properties: &ComputedCustomProperties,
   1432        stylist: &Stylist,
   1433        computed_context: &computed::Context,
   1434        shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
   1435        attr_provider: &dyn AttributeProvider,
   1436    ) -> Cow<'cache, PropertyDeclaration> {
   1437        let invalid_at_computed_value_time = || {
   1438            let keyword = if longhand_id.inherited() {
   1439                CSSWideKeyword::Inherit
   1440            } else {
   1441                CSSWideKeyword::Initial
   1442            };
   1443            Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
   1444        };
   1445 
   1446        if computed_context
   1447            .builder
   1448            .invalid_non_custom_properties
   1449            .contains(longhand_id)
   1450        {
   1451            return invalid_at_computed_value_time();
   1452        }
   1453 
   1454        if let Some(shorthand_id) = self.from_shorthand {
   1455            let key = (shorthand_id, longhand_id);
   1456            if shorthand_cache.contains_key(&key) {
   1457                // FIXME: This double lookup should be avoidable, but rustc
   1458                // doesn't like that, see:
   1459                //
   1460                // https://github.com/rust-lang/rust/issues/82146
   1461                return Cow::Borrowed(&shorthand_cache[&key]);
   1462            }
   1463        }
   1464 
   1465        let css = match custom_properties::substitute(
   1466            &self.variable_value,
   1467            custom_properties,
   1468            stylist,
   1469            computed_context,
   1470            attr_provider,
   1471        ) {
   1472            Ok(css) => css,
   1473            Err(..) => return invalid_at_computed_value_time(),
   1474        };
   1475 
   1476        // As of this writing, only the base URL is used for property
   1477        // values.
   1478        //
   1479        // NOTE(emilio): we intentionally pase `None` as the rule type here.
   1480        // If something starts depending on it, it's probably a bug, since
   1481        // it'd change how values are parsed depending on whether we're in a
   1482        // @keyframes rule or not, for example... So think twice about
   1483        // whether you want to do this!
   1484        //
   1485        // FIXME(emilio): ParsingMode is slightly fishy...
   1486        let context = ParserContext::new(
   1487            Origin::Author,
   1488            &self.variable_value.url_data,
   1489            None,
   1490            ParsingMode::DEFAULT,
   1491            computed_context.quirks_mode,
   1492            /* namespaces = */ Default::default(),
   1493            None,
   1494            None,
   1495        );
   1496 
   1497        let mut input = ParserInput::new(&css);
   1498        let mut input = Parser::new(&mut input);
   1499        input.skip_whitespace();
   1500 
   1501        if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
   1502            return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
   1503        }
   1504 
   1505        let shorthand = match self.from_shorthand {
   1506            None => {
   1507                return match input.parse_entirely(|input| longhand_id.parse_value(&context, input))
   1508                {
   1509                    Ok(decl) => Cow::Owned(decl),
   1510                    Err(..) => invalid_at_computed_value_time(),
   1511                }
   1512            },
   1513            Some(shorthand) => shorthand,
   1514        };
   1515 
   1516        let mut decls = SourcePropertyDeclaration::default();
   1517        // parse_into takes care of doing `parse_entirely` for us.
   1518        if shorthand
   1519            .parse_into(&mut decls, &context, &mut input)
   1520            .is_err()
   1521        {
   1522            return invalid_at_computed_value_time();
   1523        }
   1524 
   1525        for declaration in decls.declarations.drain(..) {
   1526            let longhand = declaration.id().as_longhand().unwrap();
   1527            if longhand.is_logical() {
   1528                let writing_mode = computed_context.builder.writing_mode;
   1529                shorthand_cache.insert(
   1530                    (shorthand, longhand.to_physical(writing_mode)),
   1531                    declaration.clone(),
   1532                );
   1533            }
   1534            shorthand_cache.insert((shorthand, longhand), declaration);
   1535        }
   1536 
   1537        let key = (shorthand, longhand_id);
   1538        match shorthand_cache.get(&key) {
   1539            Some(decl) => Cow::Borrowed(decl),
   1540            // NOTE: Under normal circumstances we should always have a value, but when prefs
   1541            // change we might hit this case. Consider something like `animation-timeline`, which
   1542            // is a conditionally-enabled longhand of `animation`:
   1543            //
   1544            // If we have a sheet with `animation: var(--foo)`, and the `animation-timeline` pref
   1545            // enabled, then that expands to an `animation-timeline` declaration at parse time.
   1546            //
   1547            // If the user disables the pref and, some time later, we get here wanting to compute
   1548            // `animation-timeline`, parse_into won't generate any declaration for it anymore, so
   1549            // we haven't inserted in the cache. Computing to invalid / initial seems like the most
   1550            // sensible thing to do here.
   1551            None => invalid_at_computed_value_time(),
   1552        }
   1553    }
   1554 }
   1555 /// A parsed all-shorthand value.
   1556 pub enum AllShorthand {
   1557    /// Not present.
   1558    NotSet,
   1559    /// A CSS-wide keyword.
   1560    CSSWideKeyword(CSSWideKeyword),
   1561    /// An all shorthand with var() references that we can't resolve right now.
   1562    WithVariables(Arc<UnparsedValue>),
   1563 }
   1564 
   1565 impl Default for AllShorthand {
   1566    fn default() -> Self {
   1567        Self::NotSet
   1568    }
   1569 }
   1570 
   1571 impl AllShorthand {
   1572    /// Iterates property declarations from the given all shorthand value.
   1573    #[inline]
   1574    pub fn declarations(&self) -> AllShorthandDeclarationIterator<'_> {
   1575        AllShorthandDeclarationIterator {
   1576            all_shorthand: self,
   1577            longhands: ShorthandId::All.longhands(),
   1578        }
   1579    }
   1580 }
   1581 
   1582 /// An iterator over the all shorthand's shorthand declarations.
   1583 pub struct AllShorthandDeclarationIterator<'a> {
   1584    all_shorthand: &'a AllShorthand,
   1585    longhands: NonCustomPropertyIterator<LonghandId>,
   1586 }
   1587 
   1588 impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
   1589    type Item = PropertyDeclaration;
   1590 
   1591    #[inline]
   1592    fn next(&mut self) -> Option<Self::Item> {
   1593        match *self.all_shorthand {
   1594            AllShorthand::NotSet => None,
   1595            AllShorthand::CSSWideKeyword(ref keyword) => Some(
   1596                PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword),
   1597            ),
   1598            AllShorthand::WithVariables(ref unparsed) => {
   1599                Some(PropertyDeclaration::WithVariables(VariableDeclaration {
   1600                    id: self.longhands.next()?,
   1601                    value: unparsed.clone(),
   1602                }))
   1603            },
   1604        }
   1605    }
   1606 }
   1607 
   1608 /// An iterator over all the property ids that are enabled for a given
   1609 /// shorthand, if that shorthand is enabled for all content too.
   1610 pub struct NonCustomPropertyIterator<Item: 'static> {
   1611    filter: bool,
   1612    iter: std::slice::Iter<'static, Item>,
   1613 }
   1614 
   1615 impl<Item> Iterator for NonCustomPropertyIterator<Item>
   1616 where
   1617    Item: 'static + Copy + Into<NonCustomPropertyId>,
   1618 {
   1619    type Item = Item;
   1620 
   1621    fn next(&mut self) -> Option<Self::Item> {
   1622        loop {
   1623            let id = *self.iter.next()?;
   1624            if !self.filter || id.into().enabled_for_all_content() {
   1625                return Some(id);
   1626            }
   1627        }
   1628    }
   1629 }