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