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