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}