Skip to main content

style/properties/
mod.rs

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