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::{CSSPropertyId, NonCustomCSSPropertyId, 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_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
225 unsafe { mem::transmute(self.0) }
227 }
228
229 #[cfg(feature = "gecko")]
231 #[inline]
232 pub fn from_noncustomcsspropertyid(prop: NonCustomCSSPropertyId) -> Option<Self> {
233 let prop = prop as u16;
234 if prop >= property_counts::NON_CUSTOM as u16 {
235 return None;
236 }
237 Some(NonCustomPropertyId(prop))
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_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
426 Some(NonCustomPropertyId::from_noncustomcsspropertyid(id)?.to_property_id())
427 }
428
429 #[cfg(feature = "gecko")]
431 #[inline]
432 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
433 Some(
434 if property.mId == NonCustomCSSPropertyId::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_noncustomcsspropertyid(
439 property.mId,
440 )?)
441 },
442 )
443 }
444
445 #[inline]
447 pub fn is_shorthand(&self) -> bool {
448 self.as_shorthand().is_ok()
449 }
450
451 pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId<'_>> {
454 match *self {
455 Self::NonCustom(id) => match id.longhand_or_shorthand() {
456 Ok(lh) => Err(PropertyDeclarationId::Longhand(lh)),
457 Err(sh) => Ok(sh),
458 },
459 Self::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),
460 }
461 }
462
463 pub fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
465 match *self {
466 Self::Custom(_) => None,
467 Self::NonCustom(id) => Some(id),
468 }
469 }
470
471 fn non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId> {
474 self.non_custom_id().map(NonCustomPropertyId::unaliased)
475 }
476
477 #[inline]
480 pub fn enabled_for_all_content(&self) -> bool {
481 let id = match self.non_custom_id() {
482 None => return true,
484 Some(id) => id,
485 };
486
487 id.enabled_for_all_content()
488 }
489
490 #[cfg(feature = "gecko")]
494 #[inline]
495 pub fn to_noncustomcsspropertyid_resolving_aliases(&self) -> NonCustomCSSPropertyId {
496 match self.non_custom_non_alias_id() {
497 Some(id) => id.to_noncustomcsspropertyid(),
498 None => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
499 }
500 }
501
502 fn allowed_in(&self, context: &ParserContext) -> bool {
503 let id = match self.non_custom_id() {
504 None => {
506 return !context
507 .nesting_context
508 .rule_types
509 .contains(CssRuleType::PositionTry)
510 },
511 Some(id) => id,
512 };
513 id.allowed_in(context)
514 }
515
516 #[inline]
517 fn allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool {
518 let id = match self.non_custom_id() {
519 None => return true,
521 Some(id) => id,
522 };
523 id.allowed_in_ignoring_rule_type(context)
524 }
525
526 pub fn supports_type(&self, ty: u8) -> bool {
529 let id = self.non_custom_non_alias_id();
530 id.map_or(0, |id| id.supported_types()) & ty != 0
531 }
532
533 pub fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
538 if let Some(id) = self.non_custom_non_alias_id() {
539 id.collect_property_completion_keywords(f);
540 }
541 CSSWideKeyword::collect_completion_keywords(f);
542 }
543}
544
545impl ToCss for LonghandId {
546 #[inline]
547 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
548 where
549 W: Write,
550 {
551 dest.write_str(self.name())
552 }
553}
554
555impl fmt::Debug for LonghandId {
556 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
557 formatter.write_str(self.name())
558 }
559}
560
561impl LonghandId {
562 #[inline]
564 pub fn name(&self) -> &'static str {
565 NonCustomPropertyId::from(*self).name()
566 }
567
568 #[inline]
570 pub fn inherited(self) -> bool {
571 !LonghandIdSet::reset().contains(self)
572 }
573
574 #[inline]
576 pub fn zoom_dependent(self) -> bool {
577 LonghandIdSet::zoom_dependent().contains(self)
578 }
579
580 #[inline]
583 pub fn ignored_when_document_colors_disabled(self) -> bool {
584 LonghandIdSet::ignored_when_colors_disabled().contains(self)
585 }
586
587 pub fn is_or_is_longhand_of(self, non_custom: NonCustomPropertyId) -> bool {
589 match non_custom.longhand_or_shorthand() {
590 Ok(lh) => self == lh,
591 Err(sh) => self.is_longhand_of(sh),
592 }
593 }
594
595 pub fn is_longhand_of(self, shorthand: ShorthandId) -> bool {
597 self.shorthands().any(|s| s == shorthand)
598 }
599
600 #[inline]
602 pub fn is_animatable(self) -> bool {
603 NonCustomPropertyId::from(self).is_animatable()
604 }
605
606 #[inline]
608 pub fn is_discrete_animatable(self) -> bool {
609 LonghandIdSet::discrete_animatable().contains(self)
610 }
611
612 #[cfg(feature = "gecko")]
614 #[inline]
615 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
616 NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
617 }
618
619 #[cfg(feature = "gecko")]
620 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
622 NonCustomPropertyId::from_noncustomcsspropertyid(id)?
623 .unaliased()
624 .as_longhand()
625 }
626
627 #[inline]
629 pub fn is_logical(self) -> bool {
630 LonghandIdSet::logical().contains(self)
631 }
632}
633
634impl ToCss for ShorthandId {
635 #[inline]
636 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
637 where
638 W: Write,
639 {
640 dest.write_str(self.name())
641 }
642}
643
644impl ShorthandId {
645 #[inline]
647 pub fn name(&self) -> &'static str {
648 NonCustomPropertyId::from(*self).name()
649 }
650
651 #[cfg(feature = "gecko")]
653 #[inline]
654 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
655 NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
656 }
657
658 #[cfg(feature = "gecko")]
660 #[inline]
661 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
662 NonCustomPropertyId::from_noncustomcsspropertyid(id)?
663 .unaliased()
664 .as_shorthand()
665 }
666
667 pub fn get_shorthand_appendable_value<'a, 'b: 'a>(
671 self,
672 declarations: &'a [&'b PropertyDeclaration],
673 ) -> Option<AppendableValue<'a, 'b>> {
674 let first_declaration = declarations.get(0)?;
675 let rest = || declarations.iter().skip(1);
676
677 if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
679 if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
680 return Some(AppendableValue::Css(css));
681 }
682 return None;
683 }
684
685 if let Some(keyword) = first_declaration.get_css_wide_keyword() {
687 if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {
688 return Some(AppendableValue::Css(keyword.to_str()));
689 }
690 return None;
691 }
692
693 if self == ShorthandId::All {
694 return None;
696 }
697
698 if declarations
700 .iter()
701 .all(|d| d.may_serialize_as_part_of_shorthand())
702 {
703 return Some(AppendableValue::DeclarationsForShorthand(
704 self,
705 declarations,
706 ));
707 }
708
709 None
710 }
711
712 #[inline]
714 pub fn is_legacy_shorthand(self) -> bool {
715 self.flags().contains(PropertyFlags::IS_LEGACY_SHORTHAND)
716 }
717}
718
719fn parse_non_custom_property_declaration_value_into<'i>(
720 declarations: &mut SourcePropertyDeclaration,
721 context: &ParserContext,
722 input: &mut Parser<'i, '_>,
723 start: &cssparser::ParserState,
724 parse_entirely_into: impl FnOnce(
725 &mut SourcePropertyDeclaration,
726 &mut Parser<'i, '_>,
727 ) -> Result<(), ParseError<'i>>,
728 parsed_wide_keyword: impl FnOnce(&mut SourcePropertyDeclaration, CSSWideKeyword),
729 parsed_custom: impl FnOnce(&mut SourcePropertyDeclaration, custom_properties::VariableValue),
730) -> Result<(), ParseError<'i>> {
731 let mut starts_with_curly_block = false;
732 if let Ok(token) = input.next() {
733 match token {
734 cssparser::Token::Ident(ref ident) => match CSSWideKeyword::from_ident(ident) {
735 Ok(wk) => {
736 if input.expect_exhausted().is_ok() {
737 return Ok(parsed_wide_keyword(declarations, wk));
738 }
739 },
740 Err(()) => {},
741 },
742 cssparser::Token::CurlyBracketBlock => {
743 starts_with_curly_block = true;
744 },
745 _ => {},
746 }
747 };
748
749 input.reset(&start);
750 input.look_for_arbitrary_substitution_functions(
751 if static_prefs::pref!("layout.css.attr.enabled") {
752 &["var", "env", "attr"]
753 } else {
754 &["var", "env"]
755 },
756 );
757
758 let err = match parse_entirely_into(declarations, input) {
759 Ok(()) => {
760 input.seen_arbitrary_substitution_functions();
761 return Ok(());
762 },
763 Err(e) => e,
764 };
765
766 let start_pos = start.position();
768 let mut at_start = start_pos == input.position();
769 let mut invalid = false;
770 while let Ok(token) = input.next() {
771 if matches!(token, cssparser::Token::CurlyBracketBlock) {
772 if !starts_with_curly_block || !at_start {
773 invalid = true;
774 break;
775 }
776 } else if starts_with_curly_block {
777 invalid = true;
778 break;
779 }
780 at_start = false;
781 }
782 if !input.seen_arbitrary_substitution_functions() || invalid {
783 return Err(err);
784 }
785 input.reset(start);
786 let value = custom_properties::VariableValue::parse(input, &context.url_data)?;
787 parsed_custom(declarations, value);
788 Ok(())
789}
790
791impl PropertyDeclaration {
792 fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option<&str> {
793 match *self {
794 PropertyDeclaration::WithVariables(ref declaration) => {
795 let s = declaration.value.from_shorthand?;
796 if s != shorthand {
797 return None;
798 }
799 Some(&*declaration.value.variable_value.css)
800 },
801 _ => None,
802 }
803 }
804
805 #[inline]
807 pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
808 Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
809 }
810
811 #[inline]
813 pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
814 match *self {
815 PropertyDeclaration::CSSWideKeyword(ref declaration) => Some(declaration.keyword),
816 _ => None,
817 }
818 }
819
820 pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
833 match *self {
834 PropertyDeclaration::CSSWideKeyword(..) | PropertyDeclaration::WithVariables(..) => {
835 false
836 },
837 PropertyDeclaration::Custom(..) => {
838 unreachable!("Serializing a custom property as part of shorthand?")
839 },
840 _ => true,
841 }
842 }
843
844 pub fn is_animatable(&self) -> bool {
846 self.id().is_animatable()
847 }
848
849 pub fn is_custom(&self) -> bool {
852 matches!(*self, PropertyDeclaration::Custom(..))
853 }
854
855 pub fn parse_into<'i, 't>(
866 declarations: &mut SourcePropertyDeclaration,
867 id: PropertyId,
868 context: &ParserContext,
869 input: &mut Parser<'i, 't>,
870 ) -> Result<(), ParseError<'i>> {
871 assert!(declarations.is_empty());
872 debug_assert!(id.allowed_in(context), "{:?}", id);
873 input.skip_whitespace();
874
875 let start = input.state();
876 let non_custom_id = match id {
877 PropertyId::Custom(property_name) => {
878 let value = match input.try_parse(CSSWideKeyword::parse) {
879 Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
880 Err(()) => CustomDeclarationValue::Unparsed(Arc::new(
881 custom_properties::VariableValue::parse(input, &context.url_data)?,
882 )),
883 };
884 declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
885 name: property_name,
886 value,
887 }));
888 return Ok(());
889 },
890 PropertyId::NonCustom(id) => id,
891 };
892 match non_custom_id.longhand_or_shorthand() {
893 Ok(longhand_id) => {
894 parse_non_custom_property_declaration_value_into(
895 declarations,
896 context,
897 input,
898 &start,
899 |declarations, input| {
900 let decl = input
901 .parse_entirely(|input| longhand_id.parse_value(context, input))?;
902 declarations.push(decl);
903 Ok(())
904 },
905 |declarations, wk| {
906 declarations.push(PropertyDeclaration::css_wide_keyword(longhand_id, wk));
907 },
908 |declarations, variable_value| {
909 declarations.push(PropertyDeclaration::WithVariables(VariableDeclaration {
910 id: longhand_id,
911 value: Arc::new(UnparsedValue {
912 variable_value,
913 from_shorthand: None,
914 }),
915 }))
916 },
917 )?;
918 },
919 Err(shorthand_id) => {
920 parse_non_custom_property_declaration_value_into(
921 declarations,
922 context,
923 input,
924 &start,
925 |declarations, input| shorthand_id.parse_into(declarations, context, input),
928 |declarations, wk| {
929 if shorthand_id == ShorthandId::All {
930 declarations.all_shorthand = AllShorthand::CSSWideKeyword(wk)
931 } else {
932 for longhand in shorthand_id.longhands() {
933 declarations
934 .push(PropertyDeclaration::css_wide_keyword(longhand, wk));
935 }
936 }
937 },
938 |declarations, variable_value| {
939 let unparsed = Arc::new(UnparsedValue {
940 variable_value,
941 from_shorthand: Some(shorthand_id),
942 });
943 if shorthand_id == ShorthandId::All {
944 declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
945 } else {
946 for id in shorthand_id.longhands() {
947 declarations.push(PropertyDeclaration::WithVariables(
948 VariableDeclaration {
949 id,
950 value: unparsed.clone(),
951 },
952 ))
953 }
954 }
955 },
956 )?;
957 },
958 }
959 if let Some(use_counters) = context.use_counters {
960 use_counters.non_custom_properties.record(non_custom_id);
961 }
962 Ok(())
963 }
964}
965
966#[derive(Clone, Debug, PartialEq, Eq, Hash)]
968pub enum OwnedPropertyDeclarationId {
969 Longhand(LonghandId),
971 Custom(custom_properties::Name),
973}
974
975impl OwnedPropertyDeclarationId {
976 #[inline]
978 pub fn is_logical(&self) -> bool {
979 self.as_borrowed().is_logical()
980 }
981
982 #[inline]
984 pub fn as_borrowed(&self) -> PropertyDeclarationId<'_> {
985 match self {
986 Self::Longhand(id) => PropertyDeclarationId::Longhand(*id),
987 Self::Custom(name) => PropertyDeclarationId::Custom(name),
988 }
989 }
990
991 #[cfg(feature = "gecko")]
993 #[inline]
994 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
995 Some(match PropertyId::from_gecko_css_property_id(property)? {
996 PropertyId::Custom(name) => Self::Custom(name),
997 PropertyId::NonCustom(id) => Self::Longhand(id.as_longhand()?),
998 })
999 }
1000}
1001
1002#[derive(Clone, Copy, Debug, PartialEq, MallocSizeOf)]
1005pub enum PropertyDeclarationId<'a> {
1006 Longhand(LonghandId),
1008 Custom(&'a custom_properties::Name),
1010}
1011
1012impl<'a> ToCss for PropertyDeclarationId<'a> {
1013 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1014 where
1015 W: Write,
1016 {
1017 match *self {
1018 PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
1019 PropertyDeclarationId::Custom(name) => {
1020 dest.write_str("--")?;
1021 serialize_atom_name(name, dest)
1022 },
1023 }
1024 }
1025}
1026
1027impl<'a> PropertyDeclarationId<'a> {
1028 #[inline(always)]
1030 pub fn flags(&self) -> PropertyFlags {
1031 match self {
1032 Self::Longhand(id) => id.flags(),
1033 Self::Custom(_) => PropertyFlags::empty(),
1034 }
1035 }
1036
1037 pub fn to_owned(&self) -> OwnedPropertyDeclarationId {
1039 match self {
1040 PropertyDeclarationId::Longhand(id) => OwnedPropertyDeclarationId::Longhand(*id),
1041 PropertyDeclarationId::Custom(name) => {
1042 OwnedPropertyDeclarationId::Custom((*name).clone())
1043 },
1044 }
1045 }
1046
1047 pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
1050 match *self {
1051 PropertyDeclarationId::Longhand(id) => match *other {
1052 PropertyId::NonCustom(non_custom_id) => id.is_or_is_longhand_of(non_custom_id),
1053 PropertyId::Custom(_) => false,
1054 },
1055 PropertyDeclarationId::Custom(name) => {
1056 matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
1057 },
1058 }
1059 }
1060
1061 pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
1064 match *self {
1065 PropertyDeclarationId::Longhand(ref id) => id.is_longhand_of(shorthand),
1066 _ => false,
1067 }
1068 }
1069
1070 pub fn name(&self) -> Cow<'static, str> {
1072 match *self {
1073 PropertyDeclarationId::Longhand(id) => id.name().into(),
1074 PropertyDeclarationId::Custom(name) => {
1075 let mut s = String::new();
1076 write!(&mut s, "--{}", name).unwrap();
1077 s.into()
1078 },
1079 }
1080 }
1081
1082 #[inline]
1084 pub fn as_longhand(&self) -> Option<LonghandId> {
1085 match *self {
1086 PropertyDeclarationId::Longhand(id) => Some(id),
1087 _ => None,
1088 }
1089 }
1090
1091 #[inline]
1093 pub fn is_logical(&self) -> bool {
1094 match self {
1095 PropertyDeclarationId::Longhand(id) => id.is_logical(),
1096 PropertyDeclarationId::Custom(_) => false,
1097 }
1098 }
1099
1100 #[inline]
1105 pub fn to_physical(&self, wm: WritingMode) -> Self {
1106 match self {
1107 Self::Longhand(id) => Self::Longhand(id.to_physical(wm)),
1108 Self::Custom(_) => self.clone(),
1109 }
1110 }
1111
1112 #[inline]
1114 pub fn is_animatable(&self) -> bool {
1115 match self {
1116 Self::Longhand(id) => id.is_animatable(),
1117 Self::Custom(_) => cfg!(feature = "gecko"),
1118 }
1119 }
1120
1121 #[inline]
1123 pub fn is_discrete_animatable(&self) -> bool {
1124 match self {
1125 Self::Longhand(longhand) => longhand.is_discrete_animatable(),
1126 Self::Custom(_) => cfg!(feature = "gecko"),
1128 }
1129 }
1130
1131 #[cfg(feature = "gecko")]
1134 #[inline]
1135 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
1136 match self {
1137 PropertyDeclarationId::Longhand(id) => id.to_noncustomcsspropertyid(),
1138 PropertyDeclarationId::Custom(_) => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1139 }
1140 }
1141
1142 #[cfg(feature = "gecko")]
1147 #[inline]
1148 pub fn to_gecko_css_property_id(&self) -> CSSPropertyId {
1149 match self {
1150 Self::Longhand(id) => CSSPropertyId {
1151 mId: id.to_noncustomcsspropertyid(),
1152 mCustomName: RefPtr::null(),
1153 },
1154 Self::Custom(name) => {
1155 let mut property_id = CSSPropertyId {
1156 mId: NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1157 mCustomName: RefPtr::null(),
1158 };
1159 property_id.mCustomName.mRawPtr = (*name).clone().into_addrefed();
1160 property_id
1161 },
1162 }
1163 }
1164}
1165
1166#[derive(Clone, PartialEq, Default)]
1168pub struct NonCustomPropertyIdSet {
1169 storage: [u32; ((property_counts::NON_CUSTOM as usize) - 1 + 32) / 32],
1170}
1171
1172impl NonCustomPropertyIdSet {
1173 pub fn new() -> Self {
1175 Self {
1176 storage: Default::default(),
1177 }
1178 }
1179
1180 #[inline]
1182 pub fn insert(&mut self, id: NonCustomPropertyId) {
1183 let bit = id.0 as usize;
1184 self.storage[bit / 32] |= 1 << (bit % 32);
1185 }
1186
1187 #[inline]
1189 pub fn contains(&self, id: NonCustomPropertyId) -> bool {
1190 let bit = id.0 as usize;
1191 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1192 }
1193}
1194
1195#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
1197pub struct LonghandIdSet {
1198 storage: [u32; ((property_counts::LONGHANDS as usize) - 1 + 32) / 32],
1199}
1200
1201to_shmem::impl_trivial_to_shmem!(LonghandIdSet);
1202
1203impl LonghandIdSet {
1204 #[inline]
1206 pub fn new() -> Self {
1207 Self {
1208 storage: Default::default(),
1209 }
1210 }
1211
1212 pub fn iter(&self) -> LonghandIdSetIterator<'_> {
1214 LonghandIdSetIterator {
1215 chunks: &self.storage,
1216 cur_chunk: 0,
1217 cur_bit: 0,
1218 }
1219 }
1220
1221 pub fn contains_all(&self, other: &Self) -> bool {
1224 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1225 if (*self_cell & *other_cell) != *other_cell {
1226 return false;
1227 }
1228 }
1229 true
1230 }
1231
1232 pub fn contains_any(&self, other: &Self) -> bool {
1234 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1235 if (*self_cell & *other_cell) != 0 {
1236 return true;
1237 }
1238 }
1239 false
1240 }
1241
1242 #[inline]
1244 pub fn remove_all(&mut self, other: &Self) {
1245 for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
1246 *self_cell &= !*other_cell;
1247 }
1248 }
1249
1250 #[inline]
1252 pub fn contains(&self, id: LonghandId) -> bool {
1253 let bit = id as usize;
1254 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1255 }
1256
1257 #[inline]
1259 pub fn contains_any_reset(&self) -> bool {
1260 self.contains_any(Self::reset())
1261 }
1262
1263 #[inline]
1265 pub fn insert(&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 remove(&mut self, id: LonghandId) {
1273 let bit = id as usize;
1274 self.storage[bit / 32] &= !(1 << (bit % 32));
1275 }
1276
1277 #[inline]
1279 pub fn clear(&mut self) {
1280 for cell in &mut self.storage {
1281 *cell = 0
1282 }
1283 }
1284
1285 #[inline]
1287 pub fn is_empty(&self) -> bool {
1288 self.storage.iter().all(|c| *c == 0)
1289 }
1290}
1291
1292pub struct LonghandIdSetIterator<'a> {
1294 chunks: &'a [u32],
1295 cur_chunk: u32,
1296 cur_bit: u32, }
1298
1299impl<'a> Iterator for LonghandIdSetIterator<'a> {
1300 type Item = LonghandId;
1301
1302 fn next(&mut self) -> Option<Self::Item> {
1303 loop {
1304 debug_assert!(self.cur_bit < 32);
1305 let cur_chunk = self.cur_chunk;
1306 let cur_bit = self.cur_bit;
1307 let chunk = *self.chunks.get(cur_chunk as usize)?;
1308 let next_bit = (chunk >> cur_bit).trailing_zeros();
1309 if next_bit == 32 {
1310 self.cur_bit = 0;
1312 self.cur_chunk += 1;
1313 continue;
1314 }
1315 debug_assert!(cur_bit + next_bit < 32);
1316 let longhand_id = cur_chunk * 32 + cur_bit + next_bit;
1317 debug_assert!(longhand_id as usize <= property_counts::LONGHANDS);
1318 let id: LonghandId = unsafe { mem::transmute(longhand_id as u16) };
1319 self.cur_bit += next_bit + 1;
1320 if self.cur_bit == 32 {
1321 self.cur_bit = 0;
1322 self.cur_chunk += 1;
1323 }
1324 return Some(id);
1325 }
1326 }
1327}
1328
1329pub type SubpropertiesVec<T> = ArrayVec<T, { property_counts::MAX_SHORTHAND_EXPANDED }>;
1331
1332#[derive(Default)]
1336pub struct SourcePropertyDeclaration {
1337 pub declarations: SubpropertiesVec<PropertyDeclaration>,
1339 pub all_shorthand: AllShorthand,
1341}
1342
1343#[cfg(feature = "gecko")]
1346size_of_test!(SourcePropertyDeclaration, 632);
1347#[cfg(feature = "servo")]
1348size_of_test!(SourcePropertyDeclaration, 568);
1349
1350impl SourcePropertyDeclaration {
1351 #[inline]
1353 pub fn with_one(decl: PropertyDeclaration) -> Self {
1354 let mut result = Self::default();
1355 result.declarations.push(decl);
1356 result
1357 }
1358
1359 pub fn drain(&mut self) -> SourcePropertyDeclarationDrain<'_> {
1361 SourcePropertyDeclarationDrain {
1362 declarations: self.declarations.drain(..),
1363 all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
1364 }
1365 }
1366
1367 pub fn clear(&mut self) {
1369 self.declarations.clear();
1370 self.all_shorthand = AllShorthand::NotSet;
1371 }
1372
1373 pub fn is_empty(&self) -> bool {
1375 self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
1376 }
1377
1378 pub fn push(&mut self, declaration: PropertyDeclaration) {
1380 let _result = self.declarations.try_push(declaration);
1381 debug_assert!(_result.is_ok());
1382 }
1383}
1384
1385pub struct SourcePropertyDeclarationDrain<'a> {
1387 pub declarations:
1389 ArrayVecDrain<'a, PropertyDeclaration, { property_counts::MAX_SHORTHAND_EXPANDED }>,
1390 pub all_shorthand: AllShorthand,
1392}
1393
1394#[derive(Debug, Eq, PartialEq, ToShmem)]
1396pub struct UnparsedValue {
1397 pub(super) variable_value: custom_properties::VariableValue,
1399 from_shorthand: Option<ShorthandId>,
1401}
1402
1403impl ToCss for UnparsedValue {
1404 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1405 where
1406 W: Write,
1407 {
1408 if self.from_shorthand.is_none() {
1410 self.variable_value.to_css(dest)?;
1411 }
1412 Ok(())
1413 }
1414}
1415
1416pub type ShorthandsWithPropertyReferencesCache =
1423 FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
1424
1425impl UnparsedValue {
1426 fn substitute_variables<'cache>(
1427 &self,
1428 longhand_id: LonghandId,
1429 custom_properties: &ComputedCustomProperties,
1430 stylist: &Stylist,
1431 computed_context: &computed::Context,
1432 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
1433 ) -> Cow<'cache, PropertyDeclaration> {
1434 let invalid_at_computed_value_time = || {
1435 let keyword = if longhand_id.inherited() {
1436 CSSWideKeyword::Inherit
1437 } else {
1438 CSSWideKeyword::Initial
1439 };
1440 Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
1441 };
1442
1443 if computed_context
1444 .builder
1445 .invalid_non_custom_properties
1446 .contains(longhand_id)
1447 {
1448 return invalid_at_computed_value_time();
1449 }
1450
1451 if let Some(shorthand_id) = self.from_shorthand {
1452 let key = (shorthand_id, longhand_id);
1453 if shorthand_cache.contains_key(&key) {
1454 return Cow::Borrowed(&shorthand_cache[&key]);
1459 }
1460 }
1461
1462 let css = match custom_properties::substitute(
1463 &self.variable_value,
1464 custom_properties,
1465 stylist,
1466 computed_context,
1467 ) {
1468 Ok(css) => css,
1469 Err(..) => return invalid_at_computed_value_time(),
1470 };
1471
1472 let context = ParserContext::new(
1483 Origin::Author,
1484 &self.variable_value.url_data,
1485 None,
1486 ParsingMode::DEFAULT,
1487 computed_context.quirks_mode,
1488 Default::default(),
1489 None,
1490 None,
1491 );
1492
1493 let mut input = ParserInput::new(&css);
1494 let mut input = Parser::new(&mut input);
1495 input.skip_whitespace();
1496
1497 if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
1498 return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
1499 }
1500
1501 let shorthand = match self.from_shorthand {
1502 None => {
1503 return match input.parse_entirely(|input| longhand_id.parse_value(&context, input))
1504 {
1505 Ok(decl) => Cow::Owned(decl),
1506 Err(..) => invalid_at_computed_value_time(),
1507 }
1508 },
1509 Some(shorthand) => shorthand,
1510 };
1511
1512 let mut decls = SourcePropertyDeclaration::default();
1513 if shorthand
1515 .parse_into(&mut decls, &context, &mut input)
1516 .is_err()
1517 {
1518 return invalid_at_computed_value_time();
1519 }
1520
1521 for declaration in decls.declarations.drain(..) {
1522 let longhand = declaration.id().as_longhand().unwrap();
1523 if longhand.is_logical() {
1524 let writing_mode = computed_context.builder.writing_mode;
1525 shorthand_cache.insert(
1526 (shorthand, longhand.to_physical(writing_mode)),
1527 declaration.clone(),
1528 );
1529 }
1530 shorthand_cache.insert((shorthand, longhand), declaration);
1531 }
1532
1533 let key = (shorthand, longhand_id);
1534 match shorthand_cache.get(&key) {
1535 Some(decl) => Cow::Borrowed(decl),
1536 None => invalid_at_computed_value_time(),
1548 }
1549 }
1550}
1551pub enum AllShorthand {
1553 NotSet,
1555 CSSWideKeyword(CSSWideKeyword),
1557 WithVariables(Arc<UnparsedValue>),
1559}
1560
1561impl Default for AllShorthand {
1562 fn default() -> Self {
1563 Self::NotSet
1564 }
1565}
1566
1567impl AllShorthand {
1568 #[inline]
1570 pub fn declarations(&self) -> AllShorthandDeclarationIterator<'_> {
1571 AllShorthandDeclarationIterator {
1572 all_shorthand: self,
1573 longhands: ShorthandId::All.longhands(),
1574 }
1575 }
1576}
1577
1578pub struct AllShorthandDeclarationIterator<'a> {
1580 all_shorthand: &'a AllShorthand,
1581 longhands: NonCustomPropertyIterator<LonghandId>,
1582}
1583
1584impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
1585 type Item = PropertyDeclaration;
1586
1587 #[inline]
1588 fn next(&mut self) -> Option<Self::Item> {
1589 match *self.all_shorthand {
1590 AllShorthand::NotSet => None,
1591 AllShorthand::CSSWideKeyword(ref keyword) => Some(
1592 PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword),
1593 ),
1594 AllShorthand::WithVariables(ref unparsed) => {
1595 Some(PropertyDeclaration::WithVariables(VariableDeclaration {
1596 id: self.longhands.next()?,
1597 value: unparsed.clone(),
1598 }))
1599 },
1600 }
1601 }
1602}
1603
1604pub struct NonCustomPropertyIterator<Item: 'static> {
1607 filter: bool,
1608 iter: std::slice::Iter<'static, Item>,
1609}
1610
1611impl<Item> Iterator for NonCustomPropertyIterator<Item>
1612where
1613 Item: 'static + Copy + Into<NonCustomPropertyId>,
1614{
1615 type Item = Item;
1616
1617 fn next(&mut self) -> Option<Self::Item> {
1618 loop {
1619 let id = *self.iter.next()?;
1620 if !self.filter || id.into().enabled_for_all_content() {
1621 return Some(id);
1622 }
1623 }
1624 }
1625}