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};
23use crate::derives::*;
24use crate::dom::AttributeProvider;
25#[cfg(feature = "gecko")]
26use crate::gecko_bindings::structs::{CSSPropertyId, NonCustomCSSPropertyId, RefPtr};
27use crate::logical_geometry::WritingMode;
28use crate::parser::ParserContext;
29use crate::stylesheets::CssRuleType;
30use crate::stylesheets::Origin;
31use crate::stylist::Stylist;
32use crate::values::{computed, serialize_atom_name};
33use arrayvec::{ArrayVec, Drain as ArrayVecDrain};
34use cssparser::{match_ignore_ascii_case, Parser, ParserInput};
35use rustc_hash::FxHashMap;
36use servo_arc::Arc;
37use std::{
38 borrow::Cow,
39 fmt::{self, Write},
40 mem,
41};
42use style_traits::{
43 CssString, CssWriter, KeywordsCollectFn, ParseError, ParsingMode, SpecifiedValueInfo, ToCss,
44 ToTyped, TypedValue,
45};
46
47bitflags! {
48 #[derive(Clone, Copy)]
50 pub struct PropertyFlags: u16 {
51 const APPLIES_TO_FIRST_LETTER = 1 << 1;
53 const APPLIES_TO_FIRST_LINE = 1 << 2;
55 const APPLIES_TO_PLACEHOLDER = 1 << 3;
57 const APPLIES_TO_CUE = 1 << 4;
59 const APPLIES_TO_MARKER = 1 << 5;
61 const IS_LEGACY_SHORTHAND = 1 << 6;
65
66 const CAN_ANIMATE_ON_COMPOSITOR = 0;
72 const AFFECTS_LAYOUT = 0;
74 #[allow(missing_docs)]
75 const AFFECTS_OVERFLOW = 0;
76 #[allow(missing_docs)]
77 const AFFECTS_PAINT = 0;
78 }
79}
80
81#[derive(
83 Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
84)]
85pub enum CSSWideKeyword {
86 Initial,
88 Inherit,
90 Unset,
92 Revert,
94 RevertLayer,
96}
97
98impl CSSWideKeyword {
99 pub fn to_str(&self) -> &'static str {
101 match *self {
102 CSSWideKeyword::Initial => "initial",
103 CSSWideKeyword::Inherit => "inherit",
104 CSSWideKeyword::Unset => "unset",
105 CSSWideKeyword::Revert => "revert",
106 CSSWideKeyword::RevertLayer => "revert-layer",
107 }
108 }
109}
110
111impl CSSWideKeyword {
112 pub fn from_ident(ident: &str) -> Result<Self, ()> {
114 Ok(match_ignore_ascii_case! { ident,
115 "initial" => CSSWideKeyword::Initial,
116 "inherit" => CSSWideKeyword::Inherit,
117 "unset" => CSSWideKeyword::Unset,
118 "revert" => CSSWideKeyword::Revert,
119 "revert-layer" => CSSWideKeyword::RevertLayer,
120 _ => return Err(()),
121 })
122 }
123
124 pub fn parse(input: &mut Parser) -> Result<Self, ()> {
126 let keyword = {
127 let ident = input.expect_ident().map_err(|_| ())?;
128 Self::from_ident(ident)?
129 };
130 input.expect_exhausted().map_err(|_| ())?;
131 Ok(keyword)
132 }
133}
134
135#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf)]
137pub struct WideKeywordDeclaration {
138 #[css(skip)]
139 id: LonghandId,
140 pub keyword: CSSWideKeyword,
142}
143
144impl ToTyped for WideKeywordDeclaration {
147 fn to_typed(&self) -> Option<TypedValue> {
148 self.keyword.to_typed()
149 }
150}
151
152#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
154pub struct VariableDeclaration {
155 #[css(skip)]
157 id: LonghandId,
158 #[ignore_malloc_size_of = "Arc"]
160 pub value: Arc<UnparsedValue>,
161}
162
163#[derive(Clone, PartialEq, ToCss, ToShmem)]
166pub enum CustomDeclarationValue {
167 Unparsed(Arc<custom_properties::SpecifiedValue>),
169 Parsed(Arc<crate::properties_and_values::value::SpecifiedValue>),
171 CSSWideKeyword(CSSWideKeyword),
173}
174
175#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
177pub struct CustomDeclaration {
178 #[css(skip)]
180 pub name: custom_properties::Name,
181 #[ignore_malloc_size_of = "Arc"]
183 pub value: CustomDeclarationValue,
184}
185
186impl fmt::Debug for PropertyDeclaration {
187 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
188 self.id().to_css(&mut CssWriter::new(f))?;
189 f.write_str(": ")?;
190
191 let mut s = CssString::new();
195 self.to_css(&mut s)?;
196 write!(f, "{}", s)
197 }
198}
199
200#[derive(
202 Clone, Copy, Debug, PartialEq, Eq, Hash, ToComputedValue, ToResolvedValue, ToShmem, MallocSizeOf,
203)]
204#[repr(C)]
205pub struct NonCustomPropertyId(u16);
206
207impl ToCss for NonCustomPropertyId {
208 #[inline]
209 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
210 where
211 W: Write,
212 {
213 dest.write_str(self.name())
214 }
215}
216
217impl NonCustomPropertyId {
218 pub fn bit(self) -> usize {
220 self.0 as usize
221 }
222
223 #[cfg(feature = "gecko")]
225 #[inline]
226 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
227 unsafe { mem::transmute(self.0) }
229 }
230
231 #[cfg(feature = "gecko")]
233 #[inline]
234 pub fn from_noncustomcsspropertyid(prop: NonCustomCSSPropertyId) -> Option<Self> {
235 let prop = prop as u16;
236 if prop >= property_counts::NON_CUSTOM as u16 {
237 return None;
238 }
239 Some(NonCustomPropertyId(prop))
241 }
242
243 pub fn unaliased(self) -> Self {
245 let Some(alias_id) = self.as_alias() else {
246 return self;
247 };
248 alias_id.aliased_property()
249 }
250
251 #[inline]
253 pub fn to_property_id(self) -> PropertyId {
254 PropertyId::NonCustom(self)
255 }
256
257 #[inline]
259 pub fn as_longhand(self) -> Option<LonghandId> {
260 if self.0 < property_counts::LONGHANDS as u16 {
261 return Some(unsafe { mem::transmute(self.0 as u16) });
262 }
263 None
264 }
265
266 #[inline]
268 pub fn as_shorthand(self) -> Option<ShorthandId> {
269 if self.0 >= property_counts::LONGHANDS as u16
270 && self.0 < property_counts::LONGHANDS_AND_SHORTHANDS as u16
271 {
272 return Some(unsafe { mem::transmute(self.0 - (property_counts::LONGHANDS as u16)) });
273 }
274 None
275 }
276
277 #[inline]
279 pub fn as_alias(self) -> Option<AliasId> {
280 debug_assert!((self.0 as usize) < property_counts::NON_CUSTOM);
281 if self.0 >= property_counts::LONGHANDS_AND_SHORTHANDS as u16 {
282 return Some(unsafe {
283 mem::transmute(self.0 - (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
284 });
285 }
286 None
287 }
288
289 #[inline]
291 pub fn longhand_or_shorthand(self) -> Result<LonghandId, ShorthandId> {
292 let id = self.unaliased();
293 match id.as_longhand() {
294 Some(lh) => Ok(lh),
295 None => Err(id.as_shorthand().unwrap()),
296 }
297 }
298
299 #[inline]
301 pub const fn from_longhand(id: LonghandId) -> Self {
302 Self(id as u16)
303 }
304
305 #[inline]
307 pub const fn from_shorthand(id: ShorthandId) -> Self {
308 Self((id as u16) + (property_counts::LONGHANDS as u16))
309 }
310
311 #[inline]
313 pub const fn from_alias(id: AliasId) -> Self {
314 Self((id as u16) + (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
315 }
316}
317
318impl From<LonghandId> for NonCustomPropertyId {
319 #[inline]
320 fn from(id: LonghandId) -> Self {
321 Self::from_longhand(id)
322 }
323}
324
325impl From<ShorthandId> for NonCustomPropertyId {
326 #[inline]
327 fn from(id: ShorthandId) -> Self {
328 Self::from_shorthand(id)
329 }
330}
331
332impl From<AliasId> for NonCustomPropertyId {
333 #[inline]
334 fn from(id: AliasId) -> Self {
335 Self::from_alias(id)
336 }
337}
338
339#[derive(Clone, Eq, PartialEq, Debug)]
342pub enum PropertyId {
343 NonCustom(NonCustomPropertyId),
345 Custom(custom_properties::Name),
347}
348
349impl ToCss for PropertyId {
350 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
351 where
352 W: Write,
353 {
354 match *self {
355 PropertyId::NonCustom(id) => dest.write_str(id.name()),
356 PropertyId::Custom(ref name) => {
357 dest.write_str("--")?;
358 serialize_atom_name(name, dest)
359 },
360 }
361 }
362}
363
364impl PropertyId {
365 #[inline]
367 pub fn longhand_id(&self) -> Option<LonghandId> {
368 self.non_custom_non_alias_id()?.as_longhand()
369 }
370
371 pub fn is_animatable(&self) -> bool {
373 match self {
374 Self::NonCustom(id) => id.is_animatable(),
375 Self::Custom(_) => cfg!(feature = "gecko"),
376 }
377 }
378
379 pub fn parse_unchecked_for_testing(name: &str) -> Result<Self, ()> {
384 Self::parse_unchecked(name, None)
385 }
386
387 #[inline]
390 pub fn parse_enabled_for_all_content(name: &str) -> Result<Self, ()> {
391 let id = Self::parse_unchecked(name, None)?;
392
393 if !id.enabled_for_all_content() {
394 return Err(());
395 }
396
397 Ok(id)
398 }
399
400 #[inline]
403 pub fn parse(name: &str, context: &ParserContext) -> Result<Self, ()> {
404 let id = Self::parse_unchecked(name, context.use_counters)?;
405 if !id.allowed_in(context) {
406 return Err(());
407 }
408 Ok(id)
409 }
410
411 #[inline]
416 pub fn parse_ignoring_rule_type(name: &str, context: &ParserContext) -> Result<Self, ()> {
417 let id = Self::parse_unchecked(name, None)?;
418 if !id.allowed_in_ignoring_rule_type(context) {
419 return Err(());
420 }
421 Ok(id)
422 }
423
424 #[cfg(feature = "gecko")]
426 #[inline]
427 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
428 Some(NonCustomPropertyId::from_noncustomcsspropertyid(id)?.to_property_id())
429 }
430
431 #[cfg(feature = "gecko")]
433 #[inline]
434 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
435 Some(
436 if property.mId == NonCustomCSSPropertyId::eCSSPropertyExtra_variable {
437 debug_assert!(!property.mCustomName.mRawPtr.is_null());
438 Self::Custom(unsafe { crate::Atom::from_raw(property.mCustomName.mRawPtr) })
439 } else {
440 Self::NonCustom(NonCustomPropertyId::from_noncustomcsspropertyid(
441 property.mId,
442 )?)
443 },
444 )
445 }
446
447 #[inline]
449 pub fn is_shorthand(&self) -> bool {
450 self.as_shorthand().is_ok()
451 }
452
453 pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId<'_>> {
456 match *self {
457 Self::NonCustom(id) => match id.longhand_or_shorthand() {
458 Ok(lh) => Err(PropertyDeclarationId::Longhand(lh)),
459 Err(sh) => Ok(sh),
460 },
461 Self::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),
462 }
463 }
464
465 pub fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
467 match *self {
468 Self::Custom(_) => None,
469 Self::NonCustom(id) => Some(id),
470 }
471 }
472
473 fn non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId> {
476 self.non_custom_id().map(NonCustomPropertyId::unaliased)
477 }
478
479 #[inline]
482 pub fn enabled_for_all_content(&self) -> bool {
483 let id = match self.non_custom_id() {
484 None => return true,
486 Some(id) => id,
487 };
488
489 id.enabled_for_all_content()
490 }
491
492 #[cfg(feature = "gecko")]
496 #[inline]
497 pub fn to_noncustomcsspropertyid_resolving_aliases(&self) -> NonCustomCSSPropertyId {
498 match self.non_custom_non_alias_id() {
499 Some(id) => id.to_noncustomcsspropertyid(),
500 None => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
501 }
502 }
503
504 fn allowed_in(&self, context: &ParserContext) -> bool {
505 let id = match self.non_custom_id() {
506 None => {
508 return !context
509 .nesting_context
510 .rule_types
511 .contains(CssRuleType::PositionTry)
512 },
513 Some(id) => id,
514 };
515 id.allowed_in(context)
516 }
517
518 #[inline]
519 fn allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool {
520 let id = match self.non_custom_id() {
521 None => return true,
523 Some(id) => id,
524 };
525 id.allowed_in_ignoring_rule_type(context)
526 }
527
528 pub fn supports_type(&self, ty: u8) -> bool {
531 let id = self.non_custom_non_alias_id();
532 id.map_or(0, |id| id.supported_types()) & ty != 0
533 }
534
535 pub fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
540 if let Some(id) = self.non_custom_non_alias_id() {
541 id.collect_property_completion_keywords(f);
542 }
543 CSSWideKeyword::collect_completion_keywords(f);
544 }
545}
546
547impl ToCss for LonghandId {
548 #[inline]
549 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
550 where
551 W: Write,
552 {
553 dest.write_str(self.name())
554 }
555}
556
557impl fmt::Debug for LonghandId {
558 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
559 formatter.write_str(self.name())
560 }
561}
562
563impl LonghandId {
564 #[inline]
566 pub fn name(&self) -> &'static str {
567 NonCustomPropertyId::from(*self).name()
568 }
569
570 #[inline]
572 pub fn inherited(self) -> bool {
573 !LonghandIdSet::reset().contains(self)
574 }
575
576 #[inline]
578 pub fn zoom_dependent(self) -> bool {
579 LonghandIdSet::zoom_dependent().contains(self)
580 }
581
582 #[inline]
585 pub fn ignored_when_document_colors_disabled(self) -> bool {
586 LonghandIdSet::ignored_when_colors_disabled().contains(self)
587 }
588
589 pub fn is_or_is_longhand_of(self, non_custom: NonCustomPropertyId) -> bool {
591 match non_custom.longhand_or_shorthand() {
592 Ok(lh) => self == lh,
593 Err(sh) => self.is_longhand_of(sh),
594 }
595 }
596
597 pub fn is_longhand_of(self, shorthand: ShorthandId) -> bool {
599 self.shorthands().any(|s| s == shorthand)
600 }
601
602 #[inline]
604 pub fn is_animatable(self) -> bool {
605 NonCustomPropertyId::from(self).is_animatable()
606 }
607
608 #[inline]
610 pub fn is_discrete_animatable(self) -> bool {
611 LonghandIdSet::discrete_animatable().contains(self)
612 }
613
614 #[cfg(feature = "gecko")]
616 #[inline]
617 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
618 NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
619 }
620
621 #[cfg(feature = "gecko")]
622 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
624 NonCustomPropertyId::from_noncustomcsspropertyid(id)?
625 .unaliased()
626 .as_longhand()
627 }
628
629 #[inline]
631 pub fn is_logical(self) -> bool {
632 LonghandIdSet::logical().contains(self)
633 }
634}
635
636impl ToCss for ShorthandId {
637 #[inline]
638 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
639 where
640 W: Write,
641 {
642 dest.write_str(self.name())
643 }
644}
645
646impl ShorthandId {
647 #[inline]
649 pub fn name(&self) -> &'static str {
650 NonCustomPropertyId::from(*self).name()
651 }
652
653 #[cfg(feature = "gecko")]
655 #[inline]
656 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
657 NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
658 }
659
660 #[cfg(feature = "gecko")]
662 #[inline]
663 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
664 NonCustomPropertyId::from_noncustomcsspropertyid(id)?
665 .unaliased()
666 .as_shorthand()
667 }
668
669 pub fn get_shorthand_appendable_value<'a, 'b: 'a>(
673 self,
674 declarations: &'a [&'b PropertyDeclaration],
675 ) -> Option<AppendableValue<'a, 'b>> {
676 let first_declaration = declarations.get(0)?;
677 let rest = || declarations.iter().skip(1);
678
679 if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
681 if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
682 return Some(AppendableValue::Css(css));
683 }
684 return None;
685 }
686
687 if let Some(keyword) = first_declaration.get_css_wide_keyword() {
689 if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {
690 return Some(AppendableValue::Css(keyword.to_str()));
691 }
692 return None;
693 }
694
695 if self == ShorthandId::All {
696 return None;
698 }
699
700 if declarations
702 .iter()
703 .all(|d| d.may_serialize_as_part_of_shorthand())
704 {
705 return Some(AppendableValue::DeclarationsForShorthand(
706 self,
707 declarations,
708 ));
709 }
710
711 None
712 }
713
714 #[inline]
716 pub fn is_legacy_shorthand(self) -> bool {
717 self.flags().contains(PropertyFlags::IS_LEGACY_SHORTHAND)
718 }
719}
720
721fn parse_non_custom_property_declaration_value_into<'i>(
722 declarations: &mut SourcePropertyDeclaration,
723 context: &ParserContext,
724 input: &mut Parser<'i, '_>,
725 start: &cssparser::ParserState,
726 parse_entirely_into: impl FnOnce(
727 &mut SourcePropertyDeclaration,
728 &mut Parser<'i, '_>,
729 ) -> Result<(), ParseError<'i>>,
730 parsed_wide_keyword: impl FnOnce(&mut SourcePropertyDeclaration, CSSWideKeyword),
731 parsed_custom: impl FnOnce(&mut SourcePropertyDeclaration, custom_properties::VariableValue),
732) -> Result<(), ParseError<'i>> {
733 let mut starts_with_curly_block = false;
734 if let Ok(token) = input.next() {
735 match token {
736 cssparser::Token::Ident(ref ident) => match CSSWideKeyword::from_ident(ident) {
737 Ok(wk) => {
738 if input.expect_exhausted().is_ok() {
739 return Ok(parsed_wide_keyword(declarations, wk));
740 }
741 },
742 Err(()) => {},
743 },
744 cssparser::Token::CurlyBracketBlock => {
745 starts_with_curly_block = true;
746 },
747 _ => {},
748 }
749 };
750
751 input.reset(&start);
752 input.look_for_arbitrary_substitution_functions(
753 if static_prefs::pref!("layout.css.attr.enabled") {
754 &["var", "env", "attr"]
755 } else {
756 &["var", "env"]
757 },
758 );
759
760 let err = match parse_entirely_into(declarations, input) {
761 Ok(()) => {
762 input.seen_arbitrary_substitution_functions();
763 return Ok(());
764 },
765 Err(e) => e,
766 };
767
768 let start_pos = start.position();
770 let mut at_start = start_pos == input.position();
771 let mut invalid = false;
772 while let Ok(token) = input.next() {
773 if matches!(token, cssparser::Token::CurlyBracketBlock) {
774 if !starts_with_curly_block || !at_start {
775 invalid = true;
776 break;
777 }
778 } else if starts_with_curly_block {
779 invalid = true;
780 break;
781 }
782 at_start = false;
783 }
784 if !input.seen_arbitrary_substitution_functions() || invalid {
785 return Err(err);
786 }
787 input.reset(start);
788 let value = custom_properties::VariableValue::parse(input, &context.url_data)?;
789 parsed_custom(declarations, value);
790 Ok(())
791}
792
793impl PropertyDeclaration {
794 fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option<&str> {
795 match *self {
796 PropertyDeclaration::WithVariables(ref declaration) => {
797 let s = declaration.value.from_shorthand?;
798 if s != shorthand {
799 return None;
800 }
801 Some(&*declaration.value.variable_value.css)
802 },
803 _ => None,
804 }
805 }
806
807 #[inline]
809 pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
810 Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
811 }
812
813 #[inline]
815 pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
816 match *self {
817 PropertyDeclaration::CSSWideKeyword(ref declaration) => Some(declaration.keyword),
818 _ => None,
819 }
820 }
821
822 pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
835 match *self {
836 PropertyDeclaration::CSSWideKeyword(..) | PropertyDeclaration::WithVariables(..) => {
837 false
838 },
839 PropertyDeclaration::Custom(..) => {
840 unreachable!("Serializing a custom property as part of shorthand?")
841 },
842 _ => true,
843 }
844 }
845
846 pub fn is_animatable(&self) -> bool {
848 self.id().is_animatable()
849 }
850
851 pub fn is_custom(&self) -> bool {
854 matches!(*self, PropertyDeclaration::Custom(..))
855 }
856
857 pub fn parse_into<'i, 't>(
868 declarations: &mut SourcePropertyDeclaration,
869 id: PropertyId,
870 context: &ParserContext,
871 input: &mut Parser<'i, 't>,
872 ) -> Result<(), ParseError<'i>> {
873 assert!(declarations.is_empty());
874 debug_assert!(id.allowed_in(context), "{:?}", id);
875 input.skip_whitespace();
876
877 let start = input.state();
878 let non_custom_id = match id {
879 PropertyId::Custom(property_name) => {
880 let value = match input.try_parse(CSSWideKeyword::parse) {
881 Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
882 Err(()) => CustomDeclarationValue::Unparsed(Arc::new(
883 custom_properties::VariableValue::parse(input, &context.url_data)?,
884 )),
885 };
886 declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
887 name: property_name,
888 value,
889 }));
890 return Ok(());
891 },
892 PropertyId::NonCustom(id) => id,
893 };
894 match non_custom_id.longhand_or_shorthand() {
895 Ok(longhand_id) => {
896 parse_non_custom_property_declaration_value_into(
897 declarations,
898 context,
899 input,
900 &start,
901 |declarations, input| {
902 let decl = input
903 .parse_entirely(|input| longhand_id.parse_value(context, input))?;
904 declarations.push(decl);
905 Ok(())
906 },
907 |declarations, wk| {
908 declarations.push(PropertyDeclaration::css_wide_keyword(longhand_id, wk));
909 },
910 |declarations, variable_value| {
911 declarations.push(PropertyDeclaration::WithVariables(VariableDeclaration {
912 id: longhand_id,
913 value: Arc::new(UnparsedValue {
914 variable_value,
915 from_shorthand: None,
916 }),
917 }))
918 },
919 )?;
920 },
921 Err(shorthand_id) => {
922 parse_non_custom_property_declaration_value_into(
923 declarations,
924 context,
925 input,
926 &start,
927 |declarations, input| shorthand_id.parse_into(declarations, context, input),
930 |declarations, wk| {
931 if shorthand_id == ShorthandId::All {
932 declarations.all_shorthand = AllShorthand::CSSWideKeyword(wk)
933 } else {
934 for longhand in shorthand_id.longhands() {
935 declarations
936 .push(PropertyDeclaration::css_wide_keyword(longhand, wk));
937 }
938 }
939 },
940 |declarations, variable_value| {
941 let unparsed = Arc::new(UnparsedValue {
942 variable_value,
943 from_shorthand: Some(shorthand_id),
944 });
945 if shorthand_id == ShorthandId::All {
946 declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
947 } else {
948 for id in shorthand_id.longhands() {
949 declarations.push(PropertyDeclaration::WithVariables(
950 VariableDeclaration {
951 id,
952 value: unparsed.clone(),
953 },
954 ))
955 }
956 }
957 },
958 )?;
959 },
960 }
961 if let Some(use_counters) = context.use_counters {
962 use_counters.non_custom_properties.record(non_custom_id);
963 }
964 Ok(())
965 }
966}
967
968#[derive(Clone, Debug, PartialEq, Eq, Hash)]
970pub enum OwnedPropertyDeclarationId {
971 Longhand(LonghandId),
973 Custom(custom_properties::Name),
975}
976
977impl OwnedPropertyDeclarationId {
978 #[inline]
980 pub fn is_logical(&self) -> bool {
981 self.as_borrowed().is_logical()
982 }
983
984 #[inline]
986 pub fn as_borrowed(&self) -> PropertyDeclarationId<'_> {
987 match self {
988 Self::Longhand(id) => PropertyDeclarationId::Longhand(*id),
989 Self::Custom(name) => PropertyDeclarationId::Custom(name),
990 }
991 }
992
993 #[cfg(feature = "gecko")]
995 #[inline]
996 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
997 Some(match PropertyId::from_gecko_css_property_id(property)? {
998 PropertyId::Custom(name) => Self::Custom(name),
999 PropertyId::NonCustom(id) => Self::Longhand(id.as_longhand()?),
1000 })
1001 }
1002}
1003
1004#[derive(Clone, Copy, Debug, PartialEq, MallocSizeOf)]
1007pub enum PropertyDeclarationId<'a> {
1008 Longhand(LonghandId),
1010 Custom(&'a custom_properties::Name),
1012}
1013
1014impl<'a> ToCss for PropertyDeclarationId<'a> {
1015 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1016 where
1017 W: Write,
1018 {
1019 match *self {
1020 PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
1021 PropertyDeclarationId::Custom(name) => {
1022 dest.write_str("--")?;
1023 serialize_atom_name(name, dest)
1024 },
1025 }
1026 }
1027}
1028
1029impl<'a> PropertyDeclarationId<'a> {
1030 #[inline(always)]
1032 pub fn flags(&self) -> PropertyFlags {
1033 match self {
1034 Self::Longhand(id) => id.flags(),
1035 Self::Custom(_) => PropertyFlags::empty(),
1036 }
1037 }
1038
1039 pub fn to_owned(&self) -> OwnedPropertyDeclarationId {
1041 match self {
1042 PropertyDeclarationId::Longhand(id) => OwnedPropertyDeclarationId::Longhand(*id),
1043 PropertyDeclarationId::Custom(name) => {
1044 OwnedPropertyDeclarationId::Custom((*name).clone())
1045 },
1046 }
1047 }
1048
1049 pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
1052 match *self {
1053 PropertyDeclarationId::Longhand(id) => match *other {
1054 PropertyId::NonCustom(non_custom_id) => id.is_or_is_longhand_of(non_custom_id),
1055 PropertyId::Custom(_) => false,
1056 },
1057 PropertyDeclarationId::Custom(name) => {
1058 matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
1059 },
1060 }
1061 }
1062
1063 pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
1066 match *self {
1067 PropertyDeclarationId::Longhand(ref id) => id.is_longhand_of(shorthand),
1068 _ => false,
1069 }
1070 }
1071
1072 pub fn name(&self) -> Cow<'static, str> {
1074 match *self {
1075 PropertyDeclarationId::Longhand(id) => id.name().into(),
1076 PropertyDeclarationId::Custom(name) => {
1077 let mut s = String::new();
1078 write!(&mut s, "--{}", name).unwrap();
1079 s.into()
1080 },
1081 }
1082 }
1083
1084 #[inline]
1086 pub fn as_longhand(&self) -> Option<LonghandId> {
1087 match *self {
1088 PropertyDeclarationId::Longhand(id) => Some(id),
1089 _ => None,
1090 }
1091 }
1092
1093 #[inline]
1095 pub fn is_logical(&self) -> bool {
1096 match self {
1097 PropertyDeclarationId::Longhand(id) => id.is_logical(),
1098 PropertyDeclarationId::Custom(_) => false,
1099 }
1100 }
1101
1102 #[inline]
1107 pub fn to_physical(&self, wm: WritingMode) -> Self {
1108 match self {
1109 Self::Longhand(id) => Self::Longhand(id.to_physical(wm)),
1110 Self::Custom(_) => self.clone(),
1111 }
1112 }
1113
1114 #[inline]
1116 pub fn is_animatable(&self) -> bool {
1117 match self {
1118 Self::Longhand(id) => id.is_animatable(),
1119 Self::Custom(_) => cfg!(feature = "gecko"),
1120 }
1121 }
1122
1123 #[inline]
1125 pub fn is_discrete_animatable(&self) -> bool {
1126 match self {
1127 Self::Longhand(longhand) => longhand.is_discrete_animatable(),
1128 Self::Custom(_) => cfg!(feature = "gecko"),
1130 }
1131 }
1132
1133 #[cfg(feature = "gecko")]
1136 #[inline]
1137 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
1138 match self {
1139 PropertyDeclarationId::Longhand(id) => id.to_noncustomcsspropertyid(),
1140 PropertyDeclarationId::Custom(_) => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1141 }
1142 }
1143
1144 #[cfg(feature = "gecko")]
1149 #[inline]
1150 pub fn to_gecko_css_property_id(&self) -> CSSPropertyId {
1151 match self {
1152 Self::Longhand(id) => CSSPropertyId {
1153 mId: id.to_noncustomcsspropertyid(),
1154 mCustomName: RefPtr::null(),
1155 },
1156 Self::Custom(name) => {
1157 let mut property_id = CSSPropertyId {
1158 mId: NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1159 mCustomName: RefPtr::null(),
1160 };
1161 property_id.mCustomName.mRawPtr = (*name).clone().into_addrefed();
1162 property_id
1163 },
1164 }
1165 }
1166}
1167
1168#[derive(Clone, PartialEq, Default)]
1170pub struct NonCustomPropertyIdSet {
1171 storage: [u32; ((property_counts::NON_CUSTOM as usize) - 1 + 32) / 32],
1172}
1173
1174impl NonCustomPropertyIdSet {
1175 pub fn new() -> Self {
1177 Self {
1178 storage: Default::default(),
1179 }
1180 }
1181
1182 #[inline]
1184 pub fn insert(&mut self, id: NonCustomPropertyId) {
1185 let bit = id.0 as usize;
1186 self.storage[bit / 32] |= 1 << (bit % 32);
1187 }
1188
1189 #[inline]
1191 pub fn contains(&self, id: NonCustomPropertyId) -> bool {
1192 let bit = id.0 as usize;
1193 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1194 }
1195}
1196
1197#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
1199pub struct LonghandIdSet {
1200 storage: [u32; ((property_counts::LONGHANDS as usize) - 1 + 32) / 32],
1201}
1202
1203to_shmem::impl_trivial_to_shmem!(LonghandIdSet);
1204
1205impl LonghandIdSet {
1206 #[inline]
1208 pub fn new() -> Self {
1209 Self {
1210 storage: Default::default(),
1211 }
1212 }
1213
1214 pub fn iter(&self) -> LonghandIdSetIterator<'_> {
1216 LonghandIdSetIterator {
1217 chunks: &self.storage,
1218 cur_chunk: 0,
1219 cur_bit: 0,
1220 }
1221 }
1222
1223 pub fn contains_all(&self, other: &Self) -> bool {
1226 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1227 if (*self_cell & *other_cell) != *other_cell {
1228 return false;
1229 }
1230 }
1231 true
1232 }
1233
1234 pub fn contains_any(&self, other: &Self) -> bool {
1236 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1237 if (*self_cell & *other_cell) != 0 {
1238 return true;
1239 }
1240 }
1241 false
1242 }
1243
1244 #[inline]
1246 pub fn remove_all(&mut self, other: &Self) {
1247 for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
1248 *self_cell &= !*other_cell;
1249 }
1250 }
1251
1252 #[inline]
1254 pub fn contains(&self, id: LonghandId) -> bool {
1255 let bit = id as usize;
1256 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1257 }
1258
1259 #[inline]
1261 pub fn contains_any_reset(&self) -> bool {
1262 self.contains_any(Self::reset())
1263 }
1264
1265 #[inline]
1267 pub fn insert(&mut self, id: LonghandId) {
1268 let bit = id as usize;
1269 self.storage[bit / 32] |= 1 << (bit % 32);
1270 }
1271
1272 #[inline]
1274 pub fn remove(&mut self, id: LonghandId) {
1275 let bit = id as usize;
1276 self.storage[bit / 32] &= !(1 << (bit % 32));
1277 }
1278
1279 #[inline]
1281 pub fn clear(&mut self) {
1282 for cell in &mut self.storage {
1283 *cell = 0
1284 }
1285 }
1286
1287 #[inline]
1289 pub fn is_empty(&self) -> bool {
1290 self.storage.iter().all(|c| *c == 0)
1291 }
1292}
1293
1294pub struct LonghandIdSetIterator<'a> {
1296 chunks: &'a [u32],
1297 cur_chunk: u32,
1298 cur_bit: u32, }
1300
1301impl<'a> Iterator for LonghandIdSetIterator<'a> {
1302 type Item = LonghandId;
1303
1304 fn next(&mut self) -> Option<Self::Item> {
1305 loop {
1306 debug_assert!(self.cur_bit < 32);
1307 let cur_chunk = self.cur_chunk;
1308 let cur_bit = self.cur_bit;
1309 let chunk = *self.chunks.get(cur_chunk as usize)?;
1310 let next_bit = (chunk >> cur_bit).trailing_zeros();
1311 if next_bit == 32 {
1312 self.cur_bit = 0;
1314 self.cur_chunk += 1;
1315 continue;
1316 }
1317 debug_assert!(cur_bit + next_bit < 32);
1318 let longhand_id = cur_chunk * 32 + cur_bit + next_bit;
1319 debug_assert!(longhand_id as usize <= property_counts::LONGHANDS);
1320 let id: LonghandId = unsafe { mem::transmute(longhand_id as u16) };
1321 self.cur_bit += next_bit + 1;
1322 if self.cur_bit == 32 {
1323 self.cur_bit = 0;
1324 self.cur_chunk += 1;
1325 }
1326 return Some(id);
1327 }
1328 }
1329}
1330
1331pub type SubpropertiesVec<T> = ArrayVec<T, { property_counts::MAX_SHORTHAND_EXPANDED }>;
1333
1334#[derive(Default)]
1338pub struct SourcePropertyDeclaration {
1339 pub declarations: SubpropertiesVec<PropertyDeclaration>,
1341 pub all_shorthand: AllShorthand,
1343}
1344
1345#[cfg(feature = "gecko")]
1348size_of_test!(SourcePropertyDeclaration, 632);
1349#[cfg(feature = "servo")]
1350size_of_test!(SourcePropertyDeclaration, 568);
1351
1352impl SourcePropertyDeclaration {
1353 #[inline]
1355 pub fn with_one(decl: PropertyDeclaration) -> Self {
1356 let mut result = Self::default();
1357 result.declarations.push(decl);
1358 result
1359 }
1360
1361 pub fn drain(&mut self) -> SourcePropertyDeclarationDrain<'_> {
1363 SourcePropertyDeclarationDrain {
1364 declarations: self.declarations.drain(..),
1365 all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
1366 }
1367 }
1368
1369 pub fn clear(&mut self) {
1371 self.declarations.clear();
1372 self.all_shorthand = AllShorthand::NotSet;
1373 }
1374
1375 pub fn is_empty(&self) -> bool {
1377 self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
1378 }
1379
1380 pub fn push(&mut self, declaration: PropertyDeclaration) {
1382 let _result = self.declarations.try_push(declaration);
1383 debug_assert!(_result.is_ok());
1384 }
1385}
1386
1387pub struct SourcePropertyDeclarationDrain<'a> {
1389 pub declarations:
1391 ArrayVecDrain<'a, PropertyDeclaration, { property_counts::MAX_SHORTHAND_EXPANDED }>,
1392 pub all_shorthand: AllShorthand,
1394}
1395
1396#[derive(Debug, Eq, PartialEq, ToShmem)]
1398pub struct UnparsedValue {
1399 pub(super) variable_value: custom_properties::VariableValue,
1401 from_shorthand: Option<ShorthandId>,
1403}
1404
1405impl ToCss for UnparsedValue {
1406 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1407 where
1408 W: Write,
1409 {
1410 if self.from_shorthand.is_none() {
1412 self.variable_value.to_css(dest)?;
1413 }
1414 Ok(())
1415 }
1416}
1417
1418pub type ShorthandsWithPropertyReferencesCache =
1425 FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
1426
1427impl UnparsedValue {
1428 fn substitute_variables<'cache>(
1429 &self,
1430 longhand_id: LonghandId,
1431 custom_properties: &ComputedCustomProperties,
1432 stylist: &Stylist,
1433 computed_context: &computed::Context,
1434 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
1435 attr_provider: &dyn AttributeProvider,
1436 ) -> Cow<'cache, PropertyDeclaration> {
1437 let invalid_at_computed_value_time = || {
1438 let keyword = if longhand_id.inherited() {
1439 CSSWideKeyword::Inherit
1440 } else {
1441 CSSWideKeyword::Initial
1442 };
1443 Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
1444 };
1445
1446 if computed_context
1447 .builder
1448 .invalid_non_custom_properties
1449 .contains(longhand_id)
1450 {
1451 return invalid_at_computed_value_time();
1452 }
1453
1454 if let Some(shorthand_id) = self.from_shorthand {
1455 let key = (shorthand_id, longhand_id);
1456 if shorthand_cache.contains_key(&key) {
1457 return Cow::Borrowed(&shorthand_cache[&key]);
1462 }
1463 }
1464
1465 let css = match custom_properties::substitute(
1466 &self.variable_value,
1467 custom_properties,
1468 stylist,
1469 computed_context,
1470 attr_provider,
1471 ) {
1472 Ok(css) => css,
1473 Err(..) => return invalid_at_computed_value_time(),
1474 };
1475
1476 let context = ParserContext::new(
1487 Origin::Author,
1488 &self.variable_value.url_data,
1489 None,
1490 ParsingMode::DEFAULT,
1491 computed_context.quirks_mode,
1492 Default::default(),
1493 None,
1494 None,
1495 );
1496
1497 let mut input = ParserInput::new(&css);
1498 let mut input = Parser::new(&mut input);
1499 input.skip_whitespace();
1500
1501 if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
1502 return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
1503 }
1504
1505 let shorthand = match self.from_shorthand {
1506 None => {
1507 return match input.parse_entirely(|input| longhand_id.parse_value(&context, input))
1508 {
1509 Ok(decl) => Cow::Owned(decl),
1510 Err(..) => invalid_at_computed_value_time(),
1511 }
1512 },
1513 Some(shorthand) => shorthand,
1514 };
1515
1516 let mut decls = SourcePropertyDeclaration::default();
1517 if shorthand
1519 .parse_into(&mut decls, &context, &mut input)
1520 .is_err()
1521 {
1522 return invalid_at_computed_value_time();
1523 }
1524
1525 for declaration in decls.declarations.drain(..) {
1526 let longhand = declaration.id().as_longhand().unwrap();
1527 if longhand.is_logical() {
1528 let writing_mode = computed_context.builder.writing_mode;
1529 shorthand_cache.insert(
1530 (shorthand, longhand.to_physical(writing_mode)),
1531 declaration.clone(),
1532 );
1533 }
1534 shorthand_cache.insert((shorthand, longhand), declaration);
1535 }
1536
1537 let key = (shorthand, longhand_id);
1538 match shorthand_cache.get(&key) {
1539 Some(decl) => Cow::Borrowed(decl),
1540 None => invalid_at_computed_value_time(),
1552 }
1553 }
1554}
1555pub enum AllShorthand {
1557 NotSet,
1559 CSSWideKeyword(CSSWideKeyword),
1561 WithVariables(Arc<UnparsedValue>),
1563}
1564
1565impl Default for AllShorthand {
1566 fn default() -> Self {
1567 Self::NotSet
1568 }
1569}
1570
1571impl AllShorthand {
1572 #[inline]
1574 pub fn declarations(&self) -> AllShorthandDeclarationIterator<'_> {
1575 AllShorthandDeclarationIterator {
1576 all_shorthand: self,
1577 longhands: ShorthandId::All.longhands(),
1578 }
1579 }
1580}
1581
1582pub struct AllShorthandDeclarationIterator<'a> {
1584 all_shorthand: &'a AllShorthand,
1585 longhands: NonCustomPropertyIterator<LonghandId>,
1586}
1587
1588impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
1589 type Item = PropertyDeclaration;
1590
1591 #[inline]
1592 fn next(&mut self) -> Option<Self::Item> {
1593 match *self.all_shorthand {
1594 AllShorthand::NotSet => None,
1595 AllShorthand::CSSWideKeyword(ref keyword) => Some(
1596 PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword),
1597 ),
1598 AllShorthand::WithVariables(ref unparsed) => {
1599 Some(PropertyDeclaration::WithVariables(VariableDeclaration {
1600 id: self.longhands.next()?,
1601 value: unparsed.clone(),
1602 }))
1603 },
1604 }
1605 }
1606}
1607
1608pub struct NonCustomPropertyIterator<Item: 'static> {
1611 filter: bool,
1612 iter: std::slice::Iter<'static, Item>,
1613}
1614
1615impl<Item> Iterator for NonCustomPropertyIterator<Item>
1616where
1617 Item: 'static + Copy + Into<NonCustomPropertyId>,
1618{
1619 type Item = Item;
1620
1621 fn next(&mut self) -> Option<Self::Item> {
1622 loop {
1623 let id = *self.iter.next()?;
1624 if !self.filter || id.into().enabled_for_all_content() {
1625 return Some(id);
1626 }
1627 }
1628 }
1629}