1pub mod cascade;
8pub mod declaration_block;
9
10pub use self::cascade::*;
11pub use self::declaration_block::*;
12pub use self::generated::*;
13#[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::stylesheets::CssRuleType;
28use crate::stylesheets::Origin;
29use crate::stylist::Stylist;
30use crate::values::{computed, serialize_atom_name};
31use arrayvec::{ArrayVec, Drain as ArrayVecDrain};
32use cssparser::{Parser, ParserInput};
33use rustc_hash::FxHashMap;
34use servo_arc::Arc;
35use std::{
36    borrow::Cow,
37    fmt::{self, Write},
38    mem,
39};
40use style_traits::{
41    CssString, CssWriter, KeywordsCollectFn, ParseError, ParsingMode, SpecifiedValueInfo, ToCss,
42    ToTyped, TypedValue,
43};
44
45bitflags! {
46    #[derive(Clone, Copy)]
48    pub struct PropertyFlags: u16 {
49        const APPLIES_TO_FIRST_LETTER = 1 << 1;
51        const APPLIES_TO_FIRST_LINE = 1 << 2;
53        const APPLIES_TO_PLACEHOLDER = 1 << 3;
55        const APPLIES_TO_CUE = 1 << 4;
57        const APPLIES_TO_MARKER = 1 << 5;
59        const IS_LEGACY_SHORTHAND = 1 << 6;
63
64        const CAN_ANIMATE_ON_COMPOSITOR = 0;
70        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#[derive(
81    Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
82)]
83pub enum CSSWideKeyword {
84    Initial,
86    Inherit,
88    Unset,
90    Revert,
92    RevertLayer,
94}
95
96impl CSSWideKeyword {
97    pub fn to_str(&self) -> &'static str {
99        match *self {
100            CSSWideKeyword::Initial => "initial",
101            CSSWideKeyword::Inherit => "inherit",
102            CSSWideKeyword::Unset => "unset",
103            CSSWideKeyword::Revert => "revert",
104            CSSWideKeyword::RevertLayer => "revert-layer",
105        }
106    }
107}
108
109impl CSSWideKeyword {
110    pub fn from_ident(ident: &str) -> Result<Self, ()> {
112        Ok(match_ignore_ascii_case! { ident,
113            "initial" => CSSWideKeyword::Initial,
114            "inherit" => CSSWideKeyword::Inherit,
115            "unset" => CSSWideKeyword::Unset,
116            "revert" => CSSWideKeyword::Revert,
117            "revert-layer" => CSSWideKeyword::RevertLayer,
118            _ => return Err(()),
119        })
120    }
121
122    pub fn parse(input: &mut Parser) -> Result<Self, ()> {
124        let keyword = {
125            let ident = input.expect_ident().map_err(|_| ())?;
126            Self::from_ident(ident)?
127        };
128        input.expect_exhausted().map_err(|_| ())?;
129        Ok(keyword)
130    }
131}
132
133#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf)]
135pub struct WideKeywordDeclaration {
136    #[css(skip)]
137    id: LonghandId,
138    pub keyword: CSSWideKeyword,
140}
141
142impl ToTyped for WideKeywordDeclaration {
145    fn to_typed(&self) -> Option<TypedValue> {
146        self.keyword.to_typed()
147    }
148}
149
150#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
152pub struct VariableDeclaration {
153    #[css(skip)]
155    id: LonghandId,
156    #[ignore_malloc_size_of = "Arc"]
158    pub value: Arc<UnparsedValue>,
159}
160
161#[derive(Clone, PartialEq, ToCss, ToShmem)]
164pub enum CustomDeclarationValue {
165    Unparsed(Arc<custom_properties::SpecifiedValue>),
167    Parsed(Arc<crate::properties_and_values::value::SpecifiedValue>),
169    CSSWideKeyword(CSSWideKeyword),
171}
172
173#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
175pub struct CustomDeclaration {
176    #[css(skip)]
178    pub name: custom_properties::Name,
179    #[ignore_malloc_size_of = "Arc"]
181    pub value: CustomDeclarationValue,
182}
183
184impl fmt::Debug for PropertyDeclaration {
185    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
186        self.id().to_css(&mut CssWriter::new(f))?;
187        f.write_str(": ")?;
188
189        let mut s = CssString::new();
193        self.to_css(&mut s)?;
194        write!(f, "{}", s)
195    }
196}
197
198#[derive(
200    Clone, Copy, Debug, PartialEq, Eq, Hash, ToComputedValue, ToResolvedValue, ToShmem, MallocSizeOf,
201)]
202#[repr(C)]
203pub struct NonCustomPropertyId(u16);
204
205impl ToCss for NonCustomPropertyId {
206    #[inline]
207    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
208    where
209        W: Write,
210    {
211        dest.write_str(self.name())
212    }
213}
214
215impl NonCustomPropertyId {
216    pub fn bit(self) -> usize {
218        self.0 as usize
219    }
220
221    #[cfg(feature = "gecko")]
223    #[inline]
224    pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
225        unsafe { mem::transmute(self.0 as i32) }
227    }
228
229    #[cfg(feature = "gecko")]
231    #[inline]
232    pub fn from_nscsspropertyid(prop: nsCSSPropertyID) -> Option<Self> {
233        let prop = prop as i32;
234        if prop < 0 || prop >= property_counts::NON_CUSTOM as i32 {
235            return None;
236        }
237        Some(NonCustomPropertyId(prop as u16))
239    }
240
241    pub fn unaliased(self) -> Self {
243        let Some(alias_id) = self.as_alias() else {
244            return self;
245        };
246        alias_id.aliased_property()
247    }
248
249    #[inline]
251    pub fn to_property_id(self) -> PropertyId {
252        PropertyId::NonCustom(self)
253    }
254
255    #[inline]
257    pub fn as_longhand(self) -> Option<LonghandId> {
258        if self.0 < property_counts::LONGHANDS as u16 {
259            return Some(unsafe { mem::transmute(self.0 as u16) });
260        }
261        None
262    }
263
264    #[inline]
266    pub fn as_shorthand(self) -> Option<ShorthandId> {
267        if self.0 >= property_counts::LONGHANDS as u16
268            && self.0 < property_counts::LONGHANDS_AND_SHORTHANDS as u16
269        {
270            return Some(unsafe { mem::transmute(self.0 - (property_counts::LONGHANDS as u16)) });
271        }
272        None
273    }
274
275    #[inline]
277    pub fn as_alias(self) -> Option<AliasId> {
278        debug_assert!((self.0 as usize) < property_counts::NON_CUSTOM);
279        if self.0 >= property_counts::LONGHANDS_AND_SHORTHANDS as u16 {
280            return Some(unsafe {
281                mem::transmute(self.0 - (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
282            });
283        }
284        None
285    }
286
287    #[inline]
289    pub fn longhand_or_shorthand(self) -> Result<LonghandId, ShorthandId> {
290        let id = self.unaliased();
291        match id.as_longhand() {
292            Some(lh) => Ok(lh),
293            None => Err(id.as_shorthand().unwrap()),
294        }
295    }
296
297    #[inline]
299    pub const fn from_longhand(id: LonghandId) -> Self {
300        Self(id as u16)
301    }
302
303    #[inline]
305    pub const fn from_shorthand(id: ShorthandId) -> Self {
306        Self((id as u16) + (property_counts::LONGHANDS as u16))
307    }
308
309    #[inline]
311    pub const fn from_alias(id: AliasId) -> Self {
312        Self((id as u16) + (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
313    }
314}
315
316impl From<LonghandId> for NonCustomPropertyId {
317    #[inline]
318    fn from(id: LonghandId) -> Self {
319        Self::from_longhand(id)
320    }
321}
322
323impl From<ShorthandId> for NonCustomPropertyId {
324    #[inline]
325    fn from(id: ShorthandId) -> Self {
326        Self::from_shorthand(id)
327    }
328}
329
330impl From<AliasId> for NonCustomPropertyId {
331    #[inline]
332    fn from(id: AliasId) -> Self {
333        Self::from_alias(id)
334    }
335}
336
337#[derive(Clone, Eq, PartialEq, Debug)]
340pub enum PropertyId {
341    NonCustom(NonCustomPropertyId),
343    Custom(custom_properties::Name),
345}
346
347impl ToCss for PropertyId {
348    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
349    where
350        W: Write,
351    {
352        match *self {
353            PropertyId::NonCustom(id) => dest.write_str(id.name()),
354            PropertyId::Custom(ref name) => {
355                dest.write_str("--")?;
356                serialize_atom_name(name, dest)
357            },
358        }
359    }
360}
361
362impl PropertyId {
363    #[inline]
365    pub fn longhand_id(&self) -> Option<LonghandId> {
366        self.non_custom_non_alias_id()?.as_longhand()
367    }
368
369    pub fn is_animatable(&self) -> bool {
371        match self {
372            Self::NonCustom(id) => id.is_animatable(),
373            Self::Custom(_) => cfg!(feature = "gecko"),
374        }
375    }
376
377    pub fn parse_unchecked_for_testing(name: &str) -> Result<Self, ()> {
382        Self::parse_unchecked(name, None)
383    }
384
385    #[inline]
388    pub fn parse_enabled_for_all_content(name: &str) -> Result<Self, ()> {
389        let id = Self::parse_unchecked(name, None)?;
390
391        if !id.enabled_for_all_content() {
392            return Err(());
393        }
394
395        Ok(id)
396    }
397
398    #[inline]
401    pub fn parse(name: &str, context: &ParserContext) -> Result<Self, ()> {
402        let id = Self::parse_unchecked(name, context.use_counters)?;
403        if !id.allowed_in(context) {
404            return Err(());
405        }
406        Ok(id)
407    }
408
409    #[inline]
414    pub fn parse_ignoring_rule_type(name: &str, context: &ParserContext) -> Result<Self, ()> {
415        let id = Self::parse_unchecked(name, None)?;
416        if !id.allowed_in_ignoring_rule_type(context) {
417            return Err(());
418        }
419        Ok(id)
420    }
421
422    #[cfg(feature = "gecko")]
424    #[inline]
425    pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Option<Self> {
426        Some(NonCustomPropertyId::from_nscsspropertyid(id)?.to_property_id())
427    }
428
429    #[cfg(feature = "gecko")]
431    #[inline]
432    pub fn from_gecko_animated_property_id(property: &AnimatedPropertyID) -> Option<Self> {
433        Some(
434            if property.mID == nsCSSPropertyID::eCSSPropertyExtra_variable {
435                debug_assert!(!property.mCustomName.mRawPtr.is_null());
436                Self::Custom(unsafe { crate::Atom::from_raw(property.mCustomName.mRawPtr) })
437            } else {
438                Self::NonCustom(NonCustomPropertyId::from_nscsspropertyid(property.mID)?)
439            },
440        )
441    }
442
443    #[inline]
445    pub fn is_shorthand(&self) -> bool {
446        self.as_shorthand().is_ok()
447    }
448
449    pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId<'_>> {
452        match *self {
453            Self::NonCustom(id) => match id.longhand_or_shorthand() {
454                Ok(lh) => Err(PropertyDeclarationId::Longhand(lh)),
455                Err(sh) => Ok(sh),
456            },
457            Self::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),
458        }
459    }
460
461    pub fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
463        match *self {
464            Self::Custom(_) => None,
465            Self::NonCustom(id) => Some(id),
466        }
467    }
468
469    fn non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId> {
472        self.non_custom_id().map(NonCustomPropertyId::unaliased)
473    }
474
475    #[inline]
478    pub fn enabled_for_all_content(&self) -> bool {
479        let id = match self.non_custom_id() {
480            None => return true,
482            Some(id) => id,
483        };
484
485        id.enabled_for_all_content()
486    }
487
488    #[cfg(feature = "gecko")]
492    #[inline]
493    pub fn to_nscsspropertyid_resolving_aliases(&self) -> nsCSSPropertyID {
494        match self.non_custom_non_alias_id() {
495            Some(id) => id.to_nscsspropertyid(),
496            None => nsCSSPropertyID::eCSSPropertyExtra_variable,
497        }
498    }
499
500    fn allowed_in(&self, context: &ParserContext) -> bool {
501        let id = match self.non_custom_id() {
502            None => {
504                return !context
505                    .nesting_context
506                    .rule_types
507                    .contains(CssRuleType::PositionTry)
508            },
509            Some(id) => id,
510        };
511        id.allowed_in(context)
512    }
513
514    #[inline]
515    fn allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool {
516        let id = match self.non_custom_id() {
517            None => return true,
519            Some(id) => id,
520        };
521        id.allowed_in_ignoring_rule_type(context)
522    }
523
524    pub fn supports_type(&self, ty: u8) -> bool {
527        let id = self.non_custom_non_alias_id();
528        id.map_or(0, |id| id.supported_types()) & ty != 0
529    }
530
531    pub fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
536        if let Some(id) = self.non_custom_non_alias_id() {
537            id.collect_property_completion_keywords(f);
538        }
539        CSSWideKeyword::collect_completion_keywords(f);
540    }
541}
542
543impl ToCss for LonghandId {
544    #[inline]
545    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
546    where
547        W: Write,
548    {
549        dest.write_str(self.name())
550    }
551}
552
553impl fmt::Debug for LonghandId {
554    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
555        formatter.write_str(self.name())
556    }
557}
558
559impl LonghandId {
560    #[inline]
562    pub fn name(&self) -> &'static str {
563        NonCustomPropertyId::from(*self).name()
564    }
565
566    #[inline]
568    pub fn inherited(self) -> bool {
569        !LonghandIdSet::reset().contains(self)
570    }
571
572    #[inline]
574    pub fn zoom_dependent(self) -> bool {
575        LonghandIdSet::zoom_dependent().contains(self)
576    }
577
578    #[inline]
581    pub fn ignored_when_document_colors_disabled(self) -> bool {
582        LonghandIdSet::ignored_when_colors_disabled().contains(self)
583    }
584
585    pub fn is_or_is_longhand_of(self, non_custom: NonCustomPropertyId) -> bool {
587        match non_custom.longhand_or_shorthand() {
588            Ok(lh) => self == lh,
589            Err(sh) => self.is_longhand_of(sh),
590        }
591    }
592
593    pub fn is_longhand_of(self, shorthand: ShorthandId) -> bool {
595        self.shorthands().any(|s| s == shorthand)
596    }
597
598    #[inline]
600    pub fn is_animatable(self) -> bool {
601        NonCustomPropertyId::from(self).is_animatable()
602    }
603
604    #[inline]
606    pub fn is_discrete_animatable(self) -> bool {
607        LonghandIdSet::discrete_animatable().contains(self)
608    }
609
610    #[cfg(feature = "gecko")]
612    #[inline]
613    pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
614        NonCustomPropertyId::from(self).to_nscsspropertyid()
615    }
616
617    #[cfg(feature = "gecko")]
618    pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Option<Self> {
620        NonCustomPropertyId::from_nscsspropertyid(id)?
621            .unaliased()
622            .as_longhand()
623    }
624
625    #[inline]
627    pub fn is_logical(self) -> bool {
628        LonghandIdSet::logical().contains(self)
629    }
630}
631
632impl ToCss for ShorthandId {
633    #[inline]
634    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
635    where
636        W: Write,
637    {
638        dest.write_str(self.name())
639    }
640}
641
642impl ShorthandId {
643    #[inline]
645    pub fn name(&self) -> &'static str {
646        NonCustomPropertyId::from(*self).name()
647    }
648
649    #[cfg(feature = "gecko")]
651    #[inline]
652    pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
653        NonCustomPropertyId::from(self).to_nscsspropertyid()
654    }
655
656    #[cfg(feature = "gecko")]
658    #[inline]
659    pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Option<Self> {
660        NonCustomPropertyId::from_nscsspropertyid(id)?
661            .unaliased()
662            .as_shorthand()
663    }
664
665    pub fn get_shorthand_appendable_value<'a, 'b: 'a>(
669        self,
670        declarations: &'a [&'b PropertyDeclaration],
671    ) -> Option<AppendableValue<'a, 'b>> {
672        let first_declaration = declarations.get(0)?;
673        let rest = || declarations.iter().skip(1);
674
675        if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
677            if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
678                return Some(AppendableValue::Css(css));
679            }
680            return None;
681        }
682
683        if let Some(keyword) = first_declaration.get_css_wide_keyword() {
685            if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {
686                return Some(AppendableValue::Css(keyword.to_str()));
687            }
688            return None;
689        }
690
691        if self == ShorthandId::All {
692            return None;
694        }
695
696        if declarations
698            .iter()
699            .all(|d| d.may_serialize_as_part_of_shorthand())
700        {
701            return Some(AppendableValue::DeclarationsForShorthand(
702                self,
703                declarations,
704            ));
705        }
706
707        None
708    }
709
710    #[inline]
712    pub fn is_legacy_shorthand(self) -> bool {
713        self.flags().contains(PropertyFlags::IS_LEGACY_SHORTHAND)
714    }
715}
716
717fn parse_non_custom_property_declaration_value_into<'i>(
718    declarations: &mut SourcePropertyDeclaration,
719    context: &ParserContext,
720    input: &mut Parser<'i, '_>,
721    start: &cssparser::ParserState,
722    parse_entirely_into: impl FnOnce(
723        &mut SourcePropertyDeclaration,
724        &mut Parser<'i, '_>,
725    ) -> Result<(), ParseError<'i>>,
726    parsed_wide_keyword: impl FnOnce(&mut SourcePropertyDeclaration, CSSWideKeyword),
727    parsed_custom: impl FnOnce(&mut SourcePropertyDeclaration, custom_properties::VariableValue),
728) -> Result<(), ParseError<'i>> {
729    let mut starts_with_curly_block = false;
730    if let Ok(token) = input.next() {
731        match token {
732            cssparser::Token::Ident(ref ident) => match CSSWideKeyword::from_ident(ident) {
733                Ok(wk) => {
734                    if input.expect_exhausted().is_ok() {
735                        return Ok(parsed_wide_keyword(declarations, wk));
736                    }
737                },
738                Err(()) => {},
739            },
740            cssparser::Token::CurlyBracketBlock => {
741                starts_with_curly_block = true;
742            },
743            _ => {},
744        }
745    };
746
747    input.reset(&start);
748    input.look_for_var_or_env_functions();
749    let err = match parse_entirely_into(declarations, input) {
750        Ok(()) => {
751            input.seen_var_or_env_functions();
752            return Ok(());
753        },
754        Err(e) => e,
755    };
756
757    let start_pos = start.position();
759    let mut at_start = start_pos == input.position();
760    let mut invalid = false;
761    while let Ok(token) = input.next() {
762        if matches!(token, cssparser::Token::CurlyBracketBlock) {
763            if !starts_with_curly_block || !at_start {
764                invalid = true;
765                break;
766            }
767        } else if starts_with_curly_block {
768            invalid = true;
769            break;
770        }
771        at_start = false;
772    }
773    if !input.seen_var_or_env_functions() || invalid {
774        return Err(err);
775    }
776    input.reset(start);
777    let value = custom_properties::VariableValue::parse(input, &context.url_data)?;
778    parsed_custom(declarations, value);
779    Ok(())
780}
781
782impl PropertyDeclaration {
783    fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option<&str> {
784        match *self {
785            PropertyDeclaration::WithVariables(ref declaration) => {
786                let s = declaration.value.from_shorthand?;
787                if s != shorthand {
788                    return None;
789                }
790                Some(&*declaration.value.variable_value.css)
791            },
792            _ => None,
793        }
794    }
795
796    #[inline]
798    pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
799        Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
800    }
801
802    #[inline]
804    pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
805        match *self {
806            PropertyDeclaration::CSSWideKeyword(ref declaration) => Some(declaration.keyword),
807            _ => None,
808        }
809    }
810
811    pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
824        match *self {
825            PropertyDeclaration::CSSWideKeyword(..) | PropertyDeclaration::WithVariables(..) => {
826                false
827            },
828            PropertyDeclaration::Custom(..) => {
829                unreachable!("Serializing a custom property as part of shorthand?")
830            },
831            _ => true,
832        }
833    }
834
835    pub fn is_animatable(&self) -> bool {
837        self.id().is_animatable()
838    }
839
840    pub fn is_custom(&self) -> bool {
843        matches!(*self, PropertyDeclaration::Custom(..))
844    }
845
846    pub fn parse_into<'i, 't>(
857        declarations: &mut SourcePropertyDeclaration,
858        id: PropertyId,
859        context: &ParserContext,
860        input: &mut Parser<'i, 't>,
861    ) -> Result<(), ParseError<'i>> {
862        assert!(declarations.is_empty());
863        debug_assert!(id.allowed_in(context), "{:?}", id);
864        input.skip_whitespace();
865
866        let start = input.state();
867        let non_custom_id = match id {
868            PropertyId::Custom(property_name) => {
869                let value = match input.try_parse(CSSWideKeyword::parse) {
870                    Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
871                    Err(()) => CustomDeclarationValue::Unparsed(Arc::new(
872                        custom_properties::VariableValue::parse(input, &context.url_data)?,
873                    )),
874                };
875                declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
876                    name: property_name,
877                    value,
878                }));
879                return Ok(());
880            },
881            PropertyId::NonCustom(id) => id,
882        };
883        match non_custom_id.longhand_or_shorthand() {
884            Ok(longhand_id) => {
885                parse_non_custom_property_declaration_value_into(
886                    declarations,
887                    context,
888                    input,
889                    &start,
890                    |declarations, input| {
891                        let decl = input
892                            .parse_entirely(|input| longhand_id.parse_value(context, input))?;
893                        declarations.push(decl);
894                        Ok(())
895                    },
896                    |declarations, wk| {
897                        declarations.push(PropertyDeclaration::css_wide_keyword(longhand_id, wk));
898                    },
899                    |declarations, variable_value| {
900                        declarations.push(PropertyDeclaration::WithVariables(VariableDeclaration {
901                            id: longhand_id,
902                            value: Arc::new(UnparsedValue {
903                                variable_value,
904                                from_shorthand: None,
905                            }),
906                        }))
907                    },
908                )?;
909            },
910            Err(shorthand_id) => {
911                parse_non_custom_property_declaration_value_into(
912                    declarations,
913                    context,
914                    input,
915                    &start,
916                    |declarations, input| shorthand_id.parse_into(declarations, context, input),
919                    |declarations, wk| {
920                        if shorthand_id == ShorthandId::All {
921                            declarations.all_shorthand = AllShorthand::CSSWideKeyword(wk)
922                        } else {
923                            for longhand in shorthand_id.longhands() {
924                                declarations
925                                    .push(PropertyDeclaration::css_wide_keyword(longhand, wk));
926                            }
927                        }
928                    },
929                    |declarations, variable_value| {
930                        let unparsed = Arc::new(UnparsedValue {
931                            variable_value,
932                            from_shorthand: Some(shorthand_id),
933                        });
934                        if shorthand_id == ShorthandId::All {
935                            declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
936                        } else {
937                            for id in shorthand_id.longhands() {
938                                declarations.push(PropertyDeclaration::WithVariables(
939                                    VariableDeclaration {
940                                        id,
941                                        value: unparsed.clone(),
942                                    },
943                                ))
944                            }
945                        }
946                    },
947                )?;
948            },
949        }
950        if let Some(use_counters) = context.use_counters {
951            use_counters.non_custom_properties.record(non_custom_id);
952        }
953        Ok(())
954    }
955}
956
957#[derive(Clone, Debug, PartialEq, Eq, Hash)]
959pub enum OwnedPropertyDeclarationId {
960    Longhand(LonghandId),
962    Custom(custom_properties::Name),
964}
965
966impl OwnedPropertyDeclarationId {
967    #[inline]
969    pub fn is_logical(&self) -> bool {
970        self.as_borrowed().is_logical()
971    }
972
973    #[inline]
975    pub fn as_borrowed(&self) -> PropertyDeclarationId<'_> {
976        match self {
977            Self::Longhand(id) => PropertyDeclarationId::Longhand(*id),
978            Self::Custom(name) => PropertyDeclarationId::Custom(name),
979        }
980    }
981
982    #[cfg(feature = "gecko")]
984    #[inline]
985    pub fn from_gecko_animated_property_id(property: &AnimatedPropertyID) -> Option<Self> {
986        Some(
987            match PropertyId::from_gecko_animated_property_id(property)? {
988                PropertyId::Custom(name) => Self::Custom(name),
989                PropertyId::NonCustom(id) => Self::Longhand(id.as_longhand()?),
990            },
991        )
992    }
993}
994
995#[derive(Clone, Copy, Debug, PartialEq, MallocSizeOf)]
998pub enum PropertyDeclarationId<'a> {
999    Longhand(LonghandId),
1001    Custom(&'a custom_properties::Name),
1003}
1004
1005impl<'a> ToCss for PropertyDeclarationId<'a> {
1006    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1007    where
1008        W: Write,
1009    {
1010        match *self {
1011            PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
1012            PropertyDeclarationId::Custom(name) => {
1013                dest.write_str("--")?;
1014                serialize_atom_name(name, dest)
1015            },
1016        }
1017    }
1018}
1019
1020impl<'a> PropertyDeclarationId<'a> {
1021    #[inline(always)]
1023    pub fn flags(&self) -> PropertyFlags {
1024        match self {
1025            Self::Longhand(id) => id.flags(),
1026            Self::Custom(_) => PropertyFlags::empty(),
1027        }
1028    }
1029
1030    pub fn to_owned(&self) -> OwnedPropertyDeclarationId {
1032        match self {
1033            PropertyDeclarationId::Longhand(id) => OwnedPropertyDeclarationId::Longhand(*id),
1034            PropertyDeclarationId::Custom(name) => {
1035                OwnedPropertyDeclarationId::Custom((*name).clone())
1036            },
1037        }
1038    }
1039
1040    pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
1043        match *self {
1044            PropertyDeclarationId::Longhand(id) => match *other {
1045                PropertyId::NonCustom(non_custom_id) => id.is_or_is_longhand_of(non_custom_id),
1046                PropertyId::Custom(_) => false,
1047            },
1048            PropertyDeclarationId::Custom(name) => {
1049                matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
1050            },
1051        }
1052    }
1053
1054    pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
1057        match *self {
1058            PropertyDeclarationId::Longhand(ref id) => id.is_longhand_of(shorthand),
1059            _ => false,
1060        }
1061    }
1062
1063    pub fn name(&self) -> Cow<'static, str> {
1065        match *self {
1066            PropertyDeclarationId::Longhand(id) => id.name().into(),
1067            PropertyDeclarationId::Custom(name) => {
1068                let mut s = String::new();
1069                write!(&mut s, "--{}", name).unwrap();
1070                s.into()
1071            },
1072        }
1073    }
1074
1075    #[inline]
1077    pub fn as_longhand(&self) -> Option<LonghandId> {
1078        match *self {
1079            PropertyDeclarationId::Longhand(id) => Some(id),
1080            _ => None,
1081        }
1082    }
1083
1084    #[inline]
1086    pub fn is_logical(&self) -> bool {
1087        match self {
1088            PropertyDeclarationId::Longhand(id) => id.is_logical(),
1089            PropertyDeclarationId::Custom(_) => false,
1090        }
1091    }
1092
1093    #[inline]
1098    pub fn to_physical(&self, wm: WritingMode) -> Self {
1099        match self {
1100            Self::Longhand(id) => Self::Longhand(id.to_physical(wm)),
1101            Self::Custom(_) => self.clone(),
1102        }
1103    }
1104
1105    #[inline]
1107    pub fn is_animatable(&self) -> bool {
1108        match self {
1109            Self::Longhand(id) => id.is_animatable(),
1110            Self::Custom(_) => cfg!(feature = "gecko"),
1111        }
1112    }
1113
1114    #[inline]
1116    pub fn is_discrete_animatable(&self) -> bool {
1117        match self {
1118            Self::Longhand(longhand) => longhand.is_discrete_animatable(),
1119            Self::Custom(_) => cfg!(feature = "gecko"),
1121        }
1122    }
1123
1124    #[cfg(feature = "gecko")]
1127    #[inline]
1128    pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
1129        match self {
1130            PropertyDeclarationId::Longhand(id) => id.to_nscsspropertyid(),
1131            PropertyDeclarationId::Custom(_) => nsCSSPropertyID::eCSSPropertyExtra_variable,
1132        }
1133    }
1134
1135    #[cfg(feature = "gecko")]
1140    #[inline]
1141    pub fn to_gecko_animated_property_id(&self) -> AnimatedPropertyID {
1142        match self {
1143            Self::Longhand(id) => AnimatedPropertyID {
1144                mID: id.to_nscsspropertyid(),
1145                mCustomName: RefPtr::null(),
1146            },
1147            Self::Custom(name) => {
1148                let mut property_id = AnimatedPropertyID {
1149                    mID: nsCSSPropertyID::eCSSPropertyExtra_variable,
1150                    mCustomName: RefPtr::null(),
1151                };
1152                property_id.mCustomName.mRawPtr = (*name).clone().into_addrefed();
1153                property_id
1154            },
1155        }
1156    }
1157}
1158
1159#[derive(Clone, PartialEq, Default)]
1161pub struct NonCustomPropertyIdSet {
1162    storage: [u32; ((property_counts::NON_CUSTOM as usize) - 1 + 32) / 32],
1163}
1164
1165impl NonCustomPropertyIdSet {
1166    pub fn new() -> Self {
1168        Self {
1169            storage: Default::default(),
1170        }
1171    }
1172
1173    #[inline]
1175    pub fn insert(&mut self, id: NonCustomPropertyId) {
1176        let bit = id.0 as usize;
1177        self.storage[bit / 32] |= 1 << (bit % 32);
1178    }
1179
1180    #[inline]
1182    pub fn contains(&self, id: NonCustomPropertyId) -> bool {
1183        let bit = id.0 as usize;
1184        (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1185    }
1186}
1187
1188#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
1190pub struct LonghandIdSet {
1191    storage: [u32; ((property_counts::LONGHANDS as usize) - 1 + 32) / 32],
1192}
1193
1194to_shmem::impl_trivial_to_shmem!(LonghandIdSet);
1195
1196impl LonghandIdSet {
1197    #[inline]
1199    pub fn new() -> Self {
1200        Self {
1201            storage: Default::default(),
1202        }
1203    }
1204
1205    pub fn iter(&self) -> LonghandIdSetIterator<'_> {
1207        LonghandIdSetIterator {
1208            chunks: &self.storage,
1209            cur_chunk: 0,
1210            cur_bit: 0,
1211        }
1212    }
1213
1214    pub fn contains_all(&self, other: &Self) -> bool {
1217        for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1218            if (*self_cell & *other_cell) != *other_cell {
1219                return false;
1220            }
1221        }
1222        true
1223    }
1224
1225    pub fn contains_any(&self, other: &Self) -> bool {
1227        for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1228            if (*self_cell & *other_cell) != 0 {
1229                return true;
1230            }
1231        }
1232        false
1233    }
1234
1235    #[inline]
1237    pub fn remove_all(&mut self, other: &Self) {
1238        for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
1239            *self_cell &= !*other_cell;
1240        }
1241    }
1242
1243    #[inline]
1245    pub fn contains(&self, id: LonghandId) -> bool {
1246        let bit = id as usize;
1247        (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1248    }
1249
1250    #[inline]
1252    pub fn contains_any_reset(&self) -> bool {
1253        self.contains_any(Self::reset())
1254    }
1255
1256    #[inline]
1258    pub fn insert(&mut self, id: LonghandId) {
1259        let bit = id as usize;
1260        self.storage[bit / 32] |= 1 << (bit % 32);
1261    }
1262
1263    #[inline]
1265    pub fn remove(&mut self, id: LonghandId) {
1266        let bit = id as usize;
1267        self.storage[bit / 32] &= !(1 << (bit % 32));
1268    }
1269
1270    #[inline]
1272    pub fn clear(&mut self) {
1273        for cell in &mut self.storage {
1274            *cell = 0
1275        }
1276    }
1277
1278    #[inline]
1280    pub fn is_empty(&self) -> bool {
1281        self.storage.iter().all(|c| *c == 0)
1282    }
1283}
1284
1285pub struct LonghandIdSetIterator<'a> {
1287    chunks: &'a [u32],
1288    cur_chunk: u32,
1289    cur_bit: u32, }
1291
1292impl<'a> Iterator for LonghandIdSetIterator<'a> {
1293    type Item = LonghandId;
1294
1295    fn next(&mut self) -> Option<Self::Item> {
1296        loop {
1297            debug_assert!(self.cur_bit < 32);
1298            let cur_chunk = self.cur_chunk;
1299            let cur_bit = self.cur_bit;
1300            let chunk = *self.chunks.get(cur_chunk as usize)?;
1301            let next_bit = (chunk >> cur_bit).trailing_zeros();
1302            if next_bit == 32 {
1303                self.cur_bit = 0;
1305                self.cur_chunk += 1;
1306                continue;
1307            }
1308            debug_assert!(cur_bit + next_bit < 32);
1309            let longhand_id = cur_chunk * 32 + cur_bit + next_bit;
1310            debug_assert!(longhand_id as usize <= property_counts::LONGHANDS);
1311            let id: LonghandId = unsafe { mem::transmute(longhand_id as u16) };
1312            self.cur_bit += next_bit + 1;
1313            if self.cur_bit == 32 {
1314                self.cur_bit = 0;
1315                self.cur_chunk += 1;
1316            }
1317            return Some(id);
1318        }
1319    }
1320}
1321
1322pub type SubpropertiesVec<T> = ArrayVec<T, { property_counts::MAX_SHORTHAND_EXPANDED }>;
1324
1325#[derive(Default)]
1329pub struct SourcePropertyDeclaration {
1330    pub declarations: SubpropertiesVec<PropertyDeclaration>,
1332    pub all_shorthand: AllShorthand,
1334}
1335
1336#[cfg(feature = "gecko")]
1339size_of_test!(SourcePropertyDeclaration, 632);
1340#[cfg(feature = "servo")]
1341size_of_test!(SourcePropertyDeclaration, 568);
1342
1343impl SourcePropertyDeclaration {
1344    #[inline]
1346    pub fn with_one(decl: PropertyDeclaration) -> Self {
1347        let mut result = Self::default();
1348        result.declarations.push(decl);
1349        result
1350    }
1351
1352    pub fn drain(&mut self) -> SourcePropertyDeclarationDrain<'_> {
1354        SourcePropertyDeclarationDrain {
1355            declarations: self.declarations.drain(..),
1356            all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
1357        }
1358    }
1359
1360    pub fn clear(&mut self) {
1362        self.declarations.clear();
1363        self.all_shorthand = AllShorthand::NotSet;
1364    }
1365
1366    pub fn is_empty(&self) -> bool {
1368        self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
1369    }
1370
1371    pub fn push(&mut self, declaration: PropertyDeclaration) {
1373        let _result = self.declarations.try_push(declaration);
1374        debug_assert!(_result.is_ok());
1375    }
1376}
1377
1378pub struct SourcePropertyDeclarationDrain<'a> {
1380    pub declarations:
1382        ArrayVecDrain<'a, PropertyDeclaration, { property_counts::MAX_SHORTHAND_EXPANDED }>,
1383    pub all_shorthand: AllShorthand,
1385}
1386
1387#[derive(Debug, Eq, PartialEq, ToShmem)]
1389pub struct UnparsedValue {
1390    pub(super) variable_value: custom_properties::VariableValue,
1392    from_shorthand: Option<ShorthandId>,
1394}
1395
1396impl ToCss for UnparsedValue {
1397    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1398    where
1399        W: Write,
1400    {
1401        if self.from_shorthand.is_none() {
1403            self.variable_value.to_css(dest)?;
1404        }
1405        Ok(())
1406    }
1407}
1408
1409pub type ShorthandsWithPropertyReferencesCache =
1416    FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
1417
1418impl UnparsedValue {
1419    fn substitute_variables<'cache>(
1420        &self,
1421        longhand_id: LonghandId,
1422        custom_properties: &ComputedCustomProperties,
1423        stylist: &Stylist,
1424        computed_context: &computed::Context,
1425        shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
1426    ) -> Cow<'cache, PropertyDeclaration> {
1427        let invalid_at_computed_value_time = || {
1428            let keyword = if longhand_id.inherited() {
1429                CSSWideKeyword::Inherit
1430            } else {
1431                CSSWideKeyword::Initial
1432            };
1433            Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
1434        };
1435
1436        if computed_context
1437            .builder
1438            .invalid_non_custom_properties
1439            .contains(longhand_id)
1440        {
1441            return invalid_at_computed_value_time();
1442        }
1443
1444        if let Some(shorthand_id) = self.from_shorthand {
1445            let key = (shorthand_id, longhand_id);
1446            if shorthand_cache.contains_key(&key) {
1447                return Cow::Borrowed(&shorthand_cache[&key]);
1452            }
1453        }
1454
1455        let css = match custom_properties::substitute(
1456            &self.variable_value,
1457            custom_properties,
1458            stylist,
1459            computed_context,
1460        ) {
1461            Ok(css) => css,
1462            Err(..) => return invalid_at_computed_value_time(),
1463        };
1464
1465        let context = ParserContext::new(
1476            Origin::Author,
1477            &self.variable_value.url_data,
1478            None,
1479            ParsingMode::DEFAULT,
1480            computed_context.quirks_mode,
1481            Default::default(),
1482            None,
1483            None,
1484        );
1485
1486        let mut input = ParserInput::new(&css);
1487        let mut input = Parser::new(&mut input);
1488        input.skip_whitespace();
1489
1490        if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
1491            return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
1492        }
1493
1494        let shorthand = match self.from_shorthand {
1495            None => {
1496                return match input.parse_entirely(|input| longhand_id.parse_value(&context, input))
1497                {
1498                    Ok(decl) => Cow::Owned(decl),
1499                    Err(..) => invalid_at_computed_value_time(),
1500                }
1501            },
1502            Some(shorthand) => shorthand,
1503        };
1504
1505        let mut decls = SourcePropertyDeclaration::default();
1506        if shorthand
1508            .parse_into(&mut decls, &context, &mut input)
1509            .is_err()
1510        {
1511            return invalid_at_computed_value_time();
1512        }
1513
1514        for declaration in decls.declarations.drain(..) {
1515            let longhand = declaration.id().as_longhand().unwrap();
1516            if longhand.is_logical() {
1517                let writing_mode = computed_context.builder.writing_mode;
1518                shorthand_cache.insert(
1519                    (shorthand, longhand.to_physical(writing_mode)),
1520                    declaration.clone(),
1521                );
1522            }
1523            shorthand_cache.insert((shorthand, longhand), declaration);
1524        }
1525
1526        let key = (shorthand, longhand_id);
1527        match shorthand_cache.get(&key) {
1528            Some(decl) => Cow::Borrowed(decl),
1529            None => invalid_at_computed_value_time(),
1541        }
1542    }
1543}
1544pub enum AllShorthand {
1546    NotSet,
1548    CSSWideKeyword(CSSWideKeyword),
1550    WithVariables(Arc<UnparsedValue>),
1552}
1553
1554impl Default for AllShorthand {
1555    fn default() -> Self {
1556        Self::NotSet
1557    }
1558}
1559
1560impl AllShorthand {
1561    #[inline]
1563    pub fn declarations(&self) -> AllShorthandDeclarationIterator<'_> {
1564        AllShorthandDeclarationIterator {
1565            all_shorthand: self,
1566            longhands: ShorthandId::All.longhands(),
1567        }
1568    }
1569}
1570
1571pub struct AllShorthandDeclarationIterator<'a> {
1573    all_shorthand: &'a AllShorthand,
1574    longhands: NonCustomPropertyIterator<LonghandId>,
1575}
1576
1577impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
1578    type Item = PropertyDeclaration;
1579
1580    #[inline]
1581    fn next(&mut self) -> Option<Self::Item> {
1582        match *self.all_shorthand {
1583            AllShorthand::NotSet => None,
1584            AllShorthand::CSSWideKeyword(ref keyword) => Some(
1585                PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword),
1586            ),
1587            AllShorthand::WithVariables(ref unparsed) => {
1588                Some(PropertyDeclaration::WithVariables(VariableDeclaration {
1589                    id: self.longhands.next()?,
1590                    value: unparsed.clone(),
1591                }))
1592            },
1593        }
1594    }
1595}
1596
1597pub struct NonCustomPropertyIterator<Item: 'static> {
1600    filter: bool,
1601    iter: std::slice::Iter<'static, Item>,
1602}
1603
1604impl<Item> Iterator for NonCustomPropertyIterator<Item>
1605where
1606    Item: 'static + Copy + Into<NonCustomPropertyId>,
1607{
1608    type Item = Item;
1609
1610    fn next(&mut self) -> Option<Self::Item> {
1611        loop {
1612            let id = *self.iter.next()?;
1613            if !self.filter || id.into().enabled_for_all_content() {
1614                return Some(id);
1615            }
1616        }
1617    }
1618}