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