1pub mod cascade;
8pub mod declaration_block;
9pub mod shorthands;
10
11pub use self::cascade::*;
12pub use self::declaration_block::*;
13pub use self::generated::*;
14
15#[macro_use]
18#[allow(unsafe_code)]
19#[deny(missing_docs)]
20pub mod generated {
21 include!(concat!(env!("OUT_DIR"), "/properties.rs"));
22}
23
24use crate::applicable_declarations::RevertKind;
25use crate::custom_properties::{self, ComputedSubstitutionFunctions, SubstitutionResult};
26use crate::derives::*;
27use crate::dom::AttributeTracker;
28#[cfg(feature = "gecko")]
29use crate::gecko_bindings::structs::{CSSPropertyId, NonCustomCSSPropertyId, RefPtr};
30use crate::logical_geometry::WritingMode;
31use crate::parser::ParserContext;
32use crate::stylesheets::CssRuleType;
33use crate::stylesheets::Origin;
34use crate::stylist::Stylist;
35use crate::values::{computed, serialize_atom_name};
36use arrayvec::{ArrayVec, Drain as ArrayVecDrain};
37use cssparser::{match_ignore_ascii_case, Parser, ParserInput};
38use rustc_hash::FxHashMap;
39use servo_arc::Arc;
40use std::{
41 borrow::Cow,
42 fmt::{self, Write},
43 mem,
44};
45use style_traits::{
46 CssString, CssWriter, KeywordsCollectFn, ParseError, ParsingMode, SpecifiedValueInfo, ToCss,
47 ToTyped, TypedValue,
48};
49use thin_vec::ThinVec;
50
51bitflags! {
52 #[derive(Clone, Copy)]
54 pub struct PropertyFlags: u16 {
55 const APPLIES_TO_FIRST_LETTER = 1 << 1;
57 const APPLIES_TO_FIRST_LINE = 1 << 2;
59 const APPLIES_TO_PLACEHOLDER = 1 << 3;
61 const APPLIES_TO_CUE = 1 << 4;
63 const APPLIES_TO_MARKER = 1 << 5;
65 const IS_LEGACY_SHORTHAND = 1 << 6;
69
70 const CAN_ANIMATE_ON_COMPOSITOR = 0;
76 const AFFECTS_LAYOUT = 0;
78 #[allow(missing_docs)]
79 const AFFECTS_OVERFLOW = 0;
80 #[allow(missing_docs)]
81 const AFFECTS_PAINT = 0;
82 }
83}
84
85#[derive(
87 Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
88)]
89pub enum CSSWideKeyword {
90 Initial,
92 Inherit,
94 Unset,
96 Revert,
98 RevertLayer,
100 RevertRule,
102}
103
104impl CSSWideKeyword {
105 pub fn to_str(&self) -> &'static str {
107 match *self {
108 Self::Initial => "initial",
109 Self::Inherit => "inherit",
110 Self::Unset => "unset",
111 Self::Revert => "revert",
112 Self::RevertLayer => "revert-layer",
113 Self::RevertRule => "revert-rule",
114 }
115 }
116
117 pub fn from_ident(ident: &str) -> Result<Self, ()> {
119 Ok(match_ignore_ascii_case! { ident,
120 "initial" => Self::Initial,
121 "inherit" => Self::Inherit,
122 "unset" => Self::Unset,
123 "revert" => Self::Revert,
124 "revert-layer" => Self::RevertLayer,
125 "revert-rule" if static_prefs::pref!("layout.css.revert-rule.enabled") => Self::RevertRule,
126 _ => return Err(()),
127 })
128 }
129
130 pub fn parse(input: &mut Parser) -> Result<Self, ()> {
132 let keyword = {
133 let ident = input.expect_ident().map_err(|_| ())?;
134 Self::from_ident(ident)?
135 };
136 input.expect_exhausted().map_err(|_| ())?;
137 Ok(keyword)
138 }
139
140 pub fn revert_kind(self) -> Option<RevertKind> {
142 Some(match self {
143 Self::Initial | Self::Inherit | Self::Unset => return None,
144 Self::Revert => RevertKind::Origin,
145 Self::RevertLayer => RevertKind::Layer,
146 Self::RevertRule => RevertKind::Rule,
147 })
148 }
149}
150
151#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf)]
153pub struct WideKeywordDeclaration {
154 #[css(skip)]
155 id: LonghandId,
156 pub keyword: CSSWideKeyword,
158}
159
160impl ToTyped for WideKeywordDeclaration {
163 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
164 self.keyword.to_typed(dest)
165 }
166}
167
168#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
170pub struct VariableDeclaration {
171 #[css(skip)]
173 id: LonghandId,
174 #[ignore_malloc_size_of = "Arc"]
176 pub value: Arc<UnparsedValue>,
177}
178
179#[derive(Clone, PartialEq, ToCss, ToShmem)]
182pub enum CustomDeclarationValue {
183 Unparsed(Arc<custom_properties::SpecifiedValue>),
185 Parsed(Arc<crate::properties_and_values::value::SpecifiedValue>),
187 CSSWideKeyword(CSSWideKeyword),
189}
190
191#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
193pub struct CustomDeclaration {
194 #[css(skip)]
196 pub name: custom_properties::Name,
197 #[ignore_malloc_size_of = "Arc"]
199 pub value: CustomDeclarationValue,
200}
201
202impl fmt::Debug for PropertyDeclaration {
203 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
204 self.id().to_css(&mut CssWriter::new(f))?;
205 f.write_str(": ")?;
206
207 let mut s = CssString::new();
211 self.to_css(&mut s)?;
212 write!(f, "{}", s)
213 }
214}
215
216#[derive(
218 Clone, Copy, Debug, PartialEq, Eq, Hash, ToComputedValue, ToResolvedValue, ToShmem, MallocSizeOf,
219)]
220#[repr(C)]
221pub struct NonCustomPropertyId(u16);
222
223impl ToCss for NonCustomPropertyId {
224 #[inline]
225 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
226 where
227 W: Write,
228 {
229 dest.write_str(self.name())
230 }
231}
232
233impl NonCustomPropertyId {
234 pub fn bit(self) -> usize {
236 self.0 as usize
237 }
238
239 #[cfg(feature = "gecko")]
241 #[inline]
242 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
243 unsafe { mem::transmute(self.0) }
245 }
246
247 #[cfg(feature = "gecko")]
249 #[inline]
250 pub fn from_noncustomcsspropertyid(prop: NonCustomCSSPropertyId) -> Option<Self> {
251 let prop = prop as u16;
252 if prop >= property_counts::NON_CUSTOM as u16 {
253 return None;
254 }
255 Some(NonCustomPropertyId(prop))
257 }
258
259 pub fn unaliased(self) -> Self {
261 let Some(alias_id) = self.as_alias() else {
262 return self;
263 };
264 alias_id.aliased_property()
265 }
266
267 #[inline]
269 pub fn to_property_id(self) -> PropertyId {
270 PropertyId::NonCustom(self)
271 }
272
273 #[inline]
275 pub fn as_longhand(self) -> Option<LonghandId> {
276 if self.0 < property_counts::LONGHANDS as u16 {
277 return Some(unsafe { mem::transmute(self.0 as u16) });
278 }
279 None
280 }
281
282 #[inline]
284 pub fn as_shorthand(self) -> Option<ShorthandId> {
285 if self.0 >= property_counts::LONGHANDS as u16
286 && self.0 < property_counts::LONGHANDS_AND_SHORTHANDS as u16
287 {
288 return Some(unsafe { mem::transmute(self.0 - (property_counts::LONGHANDS as u16)) });
289 }
290 None
291 }
292
293 #[inline]
295 pub fn as_alias(self) -> Option<AliasId> {
296 debug_assert!((self.0 as usize) < property_counts::NON_CUSTOM);
297 if self.0 >= property_counts::LONGHANDS_AND_SHORTHANDS as u16 {
298 return Some(unsafe {
299 mem::transmute(self.0 - (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
300 });
301 }
302 None
303 }
304
305 #[inline]
307 pub fn longhand_or_shorthand(self) -> Result<LonghandId, ShorthandId> {
308 let id = self.unaliased();
309 match id.as_longhand() {
310 Some(lh) => Ok(lh),
311 None => Err(id.as_shorthand().unwrap()),
312 }
313 }
314
315 #[inline]
317 pub const fn from_longhand(id: LonghandId) -> Self {
318 Self(id as u16)
319 }
320
321 #[inline]
323 pub const fn from_shorthand(id: ShorthandId) -> Self {
324 Self((id as u16) + (property_counts::LONGHANDS as u16))
325 }
326
327 #[inline]
329 pub const fn from_alias(id: AliasId) -> Self {
330 Self((id as u16) + (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
331 }
332}
333
334impl From<LonghandId> for NonCustomPropertyId {
335 #[inline]
336 fn from(id: LonghandId) -> Self {
337 Self::from_longhand(id)
338 }
339}
340
341impl From<ShorthandId> for NonCustomPropertyId {
342 #[inline]
343 fn from(id: ShorthandId) -> Self {
344 Self::from_shorthand(id)
345 }
346}
347
348impl From<AliasId> for NonCustomPropertyId {
349 #[inline]
350 fn from(id: AliasId) -> Self {
351 Self::from_alias(id)
352 }
353}
354
355#[derive(Clone, Eq, PartialEq, Debug)]
358pub enum PropertyId {
359 NonCustom(NonCustomPropertyId),
361 Custom(custom_properties::Name),
363}
364
365impl ToCss for PropertyId {
366 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
367 where
368 W: Write,
369 {
370 match *self {
371 PropertyId::NonCustom(id) => dest.write_str(id.name()),
372 PropertyId::Custom(ref name) => {
373 dest.write_str("--")?;
374 serialize_atom_name(name, dest)
375 },
376 }
377 }
378}
379
380impl PropertyId {
381 #[inline]
383 pub fn longhand_id(&self) -> Option<LonghandId> {
384 self.non_custom_non_alias_id()?.as_longhand()
385 }
386
387 pub fn is_animatable(&self) -> bool {
389 match self {
390 Self::NonCustom(id) => id.is_animatable(),
391 Self::Custom(_) => true,
392 }
393 }
394
395 pub fn parse_unchecked_for_testing(name: &str) -> Result<Self, ()> {
400 Self::parse_unchecked(name, None)
401 }
402
403 #[inline]
406 pub fn parse_enabled_for_all_content(name: &str) -> Result<Self, ()> {
407 let id = Self::parse_unchecked(name, None)?;
408
409 if !id.enabled_for_all_content() {
410 return Err(());
411 }
412
413 Ok(id)
414 }
415
416 #[inline]
419 pub fn parse(name: &str, context: &ParserContext) -> Result<Self, ()> {
420 let id = Self::parse_unchecked(name, context.use_counters)?;
421 if !id.allowed_in(context) {
422 return Err(());
423 }
424 Ok(id)
425 }
426
427 #[inline]
432 pub fn parse_ignoring_rule_type(name: &str, context: &ParserContext) -> Result<Self, ()> {
433 let id = Self::parse_unchecked(name, None)?;
434 if !id.allowed_in_ignoring_rule_type(context) {
435 return Err(());
436 }
437 Ok(id)
438 }
439
440 #[cfg(feature = "gecko")]
442 #[inline]
443 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
444 Some(NonCustomPropertyId::from_noncustomcsspropertyid(id)?.to_property_id())
445 }
446
447 #[cfg(feature = "gecko")]
449 #[inline]
450 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
451 Some(
452 if property.mId == NonCustomCSSPropertyId::eCSSPropertyExtra_variable {
453 debug_assert!(!property.mCustomName.mRawPtr.is_null());
454 Self::Custom(unsafe { crate::Atom::from_raw(property.mCustomName.mRawPtr) })
455 } else {
456 Self::NonCustom(NonCustomPropertyId::from_noncustomcsspropertyid(
457 property.mId,
458 )?)
459 },
460 )
461 }
462
463 #[inline]
465 pub fn is_shorthand(&self) -> bool {
466 self.as_shorthand().is_ok()
467 }
468
469 pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId<'_>> {
472 match *self {
473 Self::NonCustom(id) => match id.longhand_or_shorthand() {
474 Ok(lh) => Err(PropertyDeclarationId::Longhand(lh)),
475 Err(sh) => Ok(sh),
476 },
477 Self::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),
478 }
479 }
480
481 pub fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
483 match *self {
484 Self::Custom(_) => None,
485 Self::NonCustom(id) => Some(id),
486 }
487 }
488
489 fn non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId> {
492 self.non_custom_id().map(NonCustomPropertyId::unaliased)
493 }
494
495 #[inline]
498 pub fn enabled_for_all_content(&self) -> bool {
499 let id = match self.non_custom_id() {
500 None => return true,
502 Some(id) => id,
503 };
504
505 id.enabled_for_all_content()
506 }
507
508 #[cfg(feature = "gecko")]
512 #[inline]
513 pub fn to_noncustomcsspropertyid_resolving_aliases(&self) -> NonCustomCSSPropertyId {
514 match self.non_custom_non_alias_id() {
515 Some(id) => id.to_noncustomcsspropertyid(),
516 None => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
517 }
518 }
519
520 fn allowed_in(&self, context: &ParserContext) -> bool {
521 let id = match self.non_custom_id() {
522 None => {
524 return !context
525 .nesting_context
526 .rule_types
527 .contains(CssRuleType::PositionTry)
528 },
529 Some(id) => id,
530 };
531 id.allowed_in(context)
532 }
533
534 #[inline]
535 fn allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool {
536 let id = match self.non_custom_id() {
537 None => return true,
539 Some(id) => id,
540 };
541 id.allowed_in_ignoring_rule_type(context)
542 }
543
544 pub fn supports_type(&self, ty: u8) -> bool {
547 let id = self.non_custom_non_alias_id();
548 id.map_or(0, |id| id.supported_types()) & ty != 0
549 }
550
551 pub fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
556 if let Some(id) = self.non_custom_non_alias_id() {
557 id.collect_property_completion_keywords(f);
558 }
559 CSSWideKeyword::collect_completion_keywords(f);
560 }
561}
562
563impl ToCss for LonghandId {
564 #[inline]
565 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
566 where
567 W: Write,
568 {
569 dest.write_str(self.name())
570 }
571}
572
573impl fmt::Debug for LonghandId {
574 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
575 formatter.write_str(self.name())
576 }
577}
578
579impl LonghandId {
580 #[inline]
582 pub fn name(&self) -> &'static str {
583 NonCustomPropertyId::from(*self).name()
584 }
585
586 #[inline]
588 pub fn inherited(self) -> bool {
589 !LonghandIdSet::reset().contains(self)
590 }
591
592 #[inline]
594 pub fn zoom_dependent(self) -> bool {
595 LonghandIdSet::zoom_dependent().contains(self)
596 }
597
598 #[inline]
601 pub fn ignored_when_document_colors_disabled(self) -> bool {
602 LonghandIdSet::ignored_when_colors_disabled().contains(self)
603 }
604
605 pub fn is_or_is_longhand_of(self, non_custom: NonCustomPropertyId) -> bool {
607 match non_custom.longhand_or_shorthand() {
608 Ok(lh) => self == lh,
609 Err(sh) => self.is_longhand_of(sh),
610 }
611 }
612
613 pub fn is_longhand_of(self, shorthand: ShorthandId) -> bool {
615 self.shorthands().any(|s| s == shorthand)
616 }
617
618 #[inline]
620 pub fn is_animatable(self) -> bool {
621 NonCustomPropertyId::from(self).is_animatable()
622 }
623
624 #[inline]
626 pub fn is_discrete_animatable(self) -> bool {
627 LonghandIdSet::discrete_animatable().contains(self)
628 }
629
630 #[cfg(feature = "gecko")]
632 #[inline]
633 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
634 NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
635 }
636
637 #[cfg(feature = "gecko")]
638 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
640 NonCustomPropertyId::from_noncustomcsspropertyid(id)?
641 .unaliased()
642 .as_longhand()
643 }
644
645 #[inline]
647 pub fn is_logical(self) -> bool {
648 LonghandIdSet::logical().contains(self)
649 }
650}
651
652impl ToCss for ShorthandId {
653 #[inline]
654 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
655 where
656 W: Write,
657 {
658 dest.write_str(self.name())
659 }
660}
661
662impl ShorthandId {
663 #[inline]
665 pub fn name(&self) -> &'static str {
666 NonCustomPropertyId::from(*self).name()
667 }
668
669 #[cfg(feature = "gecko")]
671 #[inline]
672 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
673 NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
674 }
675
676 #[cfg(feature = "gecko")]
678 #[inline]
679 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
680 NonCustomPropertyId::from_noncustomcsspropertyid(id)?
681 .unaliased()
682 .as_shorthand()
683 }
684
685 pub fn get_shorthand_appendable_value<'a, 'b: 'a>(
689 self,
690 declarations: &'a [&'b PropertyDeclaration],
691 ) -> Option<AppendableValue<'a, 'b>> {
692 let first_declaration = declarations.get(0)?;
693 let rest = || declarations.iter().skip(1);
694
695 if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
697 if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
698 return Some(AppendableValue::Css(css));
699 }
700 return None;
701 }
702
703 if let Some(keyword) = first_declaration.get_css_wide_keyword() {
705 if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {
706 return Some(AppendableValue::Css(keyword.to_str()));
707 }
708 return None;
709 }
710
711 if self == ShorthandId::All {
712 return None;
714 }
715
716 if declarations
718 .iter()
719 .all(|d| d.may_serialize_as_part_of_shorthand())
720 {
721 return Some(AppendableValue::DeclarationsForShorthand(
722 self,
723 declarations,
724 ));
725 }
726
727 None
728 }
729
730 #[inline]
732 pub fn is_legacy_shorthand(self) -> bool {
733 self.flags().contains(PropertyFlags::IS_LEGACY_SHORTHAND)
734 }
735}
736
737fn parse_non_custom_property_declaration_value_into<'i>(
738 declarations: &mut SourcePropertyDeclaration,
739 context: &ParserContext,
740 input: &mut Parser<'i, '_>,
741 start: &cssparser::ParserState,
742 parse_entirely_into: impl FnOnce(
743 &mut SourcePropertyDeclaration,
744 &mut Parser<'i, '_>,
745 ) -> Result<(), ParseError<'i>>,
746 parsed_wide_keyword: impl FnOnce(&mut SourcePropertyDeclaration, CSSWideKeyword),
747 parsed_custom: impl FnOnce(&mut SourcePropertyDeclaration, custom_properties::VariableValue),
748) -> Result<(), ParseError<'i>> {
749 let mut starts_with_curly_block = false;
750 if let Ok(token) = input.next() {
751 match token {
752 cssparser::Token::Ident(ref ident) => match CSSWideKeyword::from_ident(ident) {
753 Ok(wk) => {
754 if input.expect_exhausted().is_ok() {
755 return Ok(parsed_wide_keyword(declarations, wk));
756 }
757 },
758 Err(()) => {},
759 },
760 cssparser::Token::CurlyBracketBlock => {
761 starts_with_curly_block = true;
762 },
763 _ => {},
764 }
765 };
766
767 input.reset(&start);
768 input.look_for_arbitrary_substitution_functions(
769 if static_prefs::pref!("layout.css.attr.enabled") {
770 &["var", "env", "attr"]
771 } else {
772 &["var", "env"]
773 },
774 );
775
776 let err = match parse_entirely_into(declarations, input) {
777 Ok(()) => {
778 input.seen_arbitrary_substitution_functions();
779 return Ok(());
780 },
781 Err(e) => e,
782 };
783
784 let start_pos = start.position();
786 let mut at_start = start_pos == input.position();
787 let mut invalid = false;
788 while let Ok(token) = input.next() {
789 if matches!(token, cssparser::Token::CurlyBracketBlock) {
790 if !starts_with_curly_block || !at_start {
791 invalid = true;
792 break;
793 }
794 } else if starts_with_curly_block {
795 invalid = true;
796 break;
797 }
798 at_start = false;
799 }
800 if !input.seen_arbitrary_substitution_functions() || invalid {
801 return Err(err);
802 }
803 input.reset(start);
804 let value = custom_properties::VariableValue::parse(
805 input,
806 Some(&context.namespaces.prefixes),
807 &context.url_data,
808 )?;
809 parsed_custom(declarations, value);
810 Ok(())
811}
812
813impl PropertyDeclaration {
814 fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option<&str> {
815 match *self {
816 PropertyDeclaration::WithVariables(ref declaration) => {
817 let s = declaration.value.from_shorthand?;
818 if s != shorthand {
819 return None;
820 }
821 Some(&*declaration.value.variable_value.css)
822 },
823 _ => None,
824 }
825 }
826
827 #[inline]
829 pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
830 Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
831 }
832
833 #[inline]
835 pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
836 match *self {
837 PropertyDeclaration::CSSWideKeyword(ref declaration) => Some(declaration.keyword),
838 _ => None,
839 }
840 }
841
842 pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
855 match *self {
856 PropertyDeclaration::CSSWideKeyword(..) | PropertyDeclaration::WithVariables(..) => {
857 false
858 },
859 PropertyDeclaration::Custom(..) => {
860 unreachable!("Serializing a custom property as part of shorthand?")
861 },
862 _ => true,
863 }
864 }
865
866 pub fn is_animatable(&self) -> bool {
868 self.id().is_animatable()
869 }
870
871 pub fn is_custom(&self) -> bool {
874 matches!(*self, PropertyDeclaration::Custom(..))
875 }
876
877 pub fn parse_into<'i, 't>(
888 declarations: &mut SourcePropertyDeclaration,
889 id: PropertyId,
890 context: &ParserContext,
891 input: &mut Parser<'i, 't>,
892 ) -> Result<(), ParseError<'i>> {
893 assert!(declarations.is_empty());
894 debug_assert!(id.allowed_in(context), "{:?}", id);
895 input.skip_whitespace();
896
897 let start = input.state();
898 let non_custom_id = match id {
899 PropertyId::Custom(property_name) => {
900 let value = match input.try_parse(CSSWideKeyword::parse) {
901 Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
902 Err(()) => CustomDeclarationValue::Unparsed(Arc::new(
903 custom_properties::VariableValue::parse(
904 input,
905 Some(&context.namespaces.prefixes),
906 &context.url_data,
907 )?,
908 )),
909 };
910 declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
911 name: property_name,
912 value,
913 }));
914 return Ok(());
915 },
916 PropertyId::NonCustom(id) => id,
917 };
918 match non_custom_id.longhand_or_shorthand() {
919 Ok(longhand_id) => {
920 parse_non_custom_property_declaration_value_into(
921 declarations,
922 context,
923 input,
924 &start,
925 |declarations, input| {
926 let decl = input
927 .parse_entirely(|input| longhand_id.parse_value(context, input))?;
928 declarations.push(decl);
929 Ok(())
930 },
931 |declarations, wk| {
932 declarations.push(PropertyDeclaration::css_wide_keyword(longhand_id, wk));
933 },
934 |declarations, variable_value| {
935 declarations.push(PropertyDeclaration::WithVariables(VariableDeclaration {
936 id: longhand_id,
937 value: Arc::new(UnparsedValue {
938 variable_value,
939 from_shorthand: None,
940 }),
941 }))
942 },
943 )?;
944 },
945 Err(shorthand_id) => {
946 parse_non_custom_property_declaration_value_into(
947 declarations,
948 context,
949 input,
950 &start,
951 |declarations, input| shorthand_id.parse_into(declarations, context, input),
954 |declarations, wk| {
955 if shorthand_id == ShorthandId::All {
956 declarations.all_shorthand = AllShorthand::CSSWideKeyword(wk)
957 } else {
958 for longhand in shorthand_id.longhands() {
959 declarations
960 .push(PropertyDeclaration::css_wide_keyword(longhand, wk));
961 }
962 }
963 },
964 |declarations, variable_value| {
965 let unparsed = Arc::new(UnparsedValue {
966 variable_value,
967 from_shorthand: Some(shorthand_id),
968 });
969 if shorthand_id == ShorthandId::All {
970 declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
971 } else {
972 for id in shorthand_id.longhands() {
973 declarations.push(PropertyDeclaration::WithVariables(
974 VariableDeclaration {
975 id,
976 value: unparsed.clone(),
977 },
978 ))
979 }
980 }
981 },
982 )?;
983 },
984 }
985 if let Some(use_counters) = context.use_counters {
986 use_counters.non_custom_properties.record(non_custom_id);
987 }
988 Ok(())
989 }
990}
991
992#[derive(Clone, Debug, PartialEq, Eq, Hash)]
994pub enum OwnedPropertyDeclarationId {
995 Longhand(LonghandId),
997 Custom(custom_properties::Name),
999}
1000
1001impl OwnedPropertyDeclarationId {
1002 #[inline]
1004 pub fn is_logical(&self) -> bool {
1005 self.as_borrowed().is_logical()
1006 }
1007
1008 #[inline]
1010 pub fn as_borrowed(&self) -> PropertyDeclarationId<'_> {
1011 match self {
1012 Self::Longhand(id) => PropertyDeclarationId::Longhand(*id),
1013 Self::Custom(name) => PropertyDeclarationId::Custom(name),
1014 }
1015 }
1016
1017 #[cfg(feature = "gecko")]
1019 #[inline]
1020 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
1021 Some(match PropertyId::from_gecko_css_property_id(property)? {
1022 PropertyId::Custom(name) => Self::Custom(name),
1023 PropertyId::NonCustom(id) => Self::Longhand(id.as_longhand()?),
1024 })
1025 }
1026}
1027
1028#[derive(Clone, Copy, Debug, PartialEq, MallocSizeOf)]
1031pub enum PropertyDeclarationId<'a> {
1032 Longhand(LonghandId),
1034 Custom(&'a custom_properties::Name),
1036}
1037
1038impl<'a> ToCss for PropertyDeclarationId<'a> {
1039 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1040 where
1041 W: Write,
1042 {
1043 match *self {
1044 PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
1045 PropertyDeclarationId::Custom(name) => {
1046 dest.write_str("--")?;
1047 serialize_atom_name(name, dest)
1048 },
1049 }
1050 }
1051}
1052
1053impl<'a> PropertyDeclarationId<'a> {
1054 #[inline(always)]
1056 pub fn flags(&self) -> PropertyFlags {
1057 match self {
1058 Self::Longhand(id) => id.flags(),
1059 Self::Custom(_) => PropertyFlags::empty(),
1060 }
1061 }
1062
1063 pub fn to_owned(&self) -> OwnedPropertyDeclarationId {
1065 match self {
1066 PropertyDeclarationId::Longhand(id) => OwnedPropertyDeclarationId::Longhand(*id),
1067 PropertyDeclarationId::Custom(name) => {
1068 OwnedPropertyDeclarationId::Custom((*name).clone())
1069 },
1070 }
1071 }
1072
1073 pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
1076 match *self {
1077 PropertyDeclarationId::Longhand(id) => match *other {
1078 PropertyId::NonCustom(non_custom_id) => id.is_or_is_longhand_of(non_custom_id),
1079 PropertyId::Custom(_) => false,
1080 },
1081 PropertyDeclarationId::Custom(name) => {
1082 matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
1083 },
1084 }
1085 }
1086
1087 pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
1090 match *self {
1091 PropertyDeclarationId::Longhand(ref id) => id.is_longhand_of(shorthand),
1092 _ => false,
1093 }
1094 }
1095
1096 pub fn name(&self) -> Cow<'static, str> {
1098 match *self {
1099 PropertyDeclarationId::Longhand(id) => id.name().into(),
1100 PropertyDeclarationId::Custom(name) => {
1101 let mut s = String::new();
1102 write!(&mut s, "--{}", name).unwrap();
1103 s.into()
1104 },
1105 }
1106 }
1107
1108 #[inline]
1110 pub fn as_longhand(&self) -> Option<LonghandId> {
1111 match *self {
1112 PropertyDeclarationId::Longhand(id) => Some(id),
1113 _ => None,
1114 }
1115 }
1116
1117 #[inline]
1119 pub fn is_logical(&self) -> bool {
1120 match self {
1121 PropertyDeclarationId::Longhand(id) => id.is_logical(),
1122 PropertyDeclarationId::Custom(_) => false,
1123 }
1124 }
1125
1126 #[inline]
1131 pub fn to_physical(&self, wm: WritingMode) -> Self {
1132 match self {
1133 Self::Longhand(id) => Self::Longhand(id.to_physical(wm)),
1134 Self::Custom(_) => self.clone(),
1135 }
1136 }
1137
1138 #[inline]
1140 pub fn is_animatable(&self) -> bool {
1141 match self {
1142 Self::Longhand(id) => id.is_animatable(),
1143 Self::Custom(_) => true,
1144 }
1145 }
1146
1147 #[inline]
1149 pub fn is_discrete_animatable(&self) -> bool {
1150 match self {
1151 Self::Longhand(longhand) => longhand.is_discrete_animatable(),
1152 Self::Custom(_) => true,
1154 }
1155 }
1156
1157 #[cfg(feature = "gecko")]
1160 #[inline]
1161 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
1162 match self {
1163 PropertyDeclarationId::Longhand(id) => id.to_noncustomcsspropertyid(),
1164 PropertyDeclarationId::Custom(_) => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1165 }
1166 }
1167
1168 #[cfg(feature = "gecko")]
1173 #[inline]
1174 pub fn to_gecko_css_property_id(&self) -> CSSPropertyId {
1175 match self {
1176 Self::Longhand(id) => CSSPropertyId {
1177 mId: id.to_noncustomcsspropertyid(),
1178 mCustomName: RefPtr::null(),
1179 },
1180 Self::Custom(name) => {
1181 let mut property_id = CSSPropertyId {
1182 mId: NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1183 mCustomName: RefPtr::null(),
1184 };
1185 property_id.mCustomName.mRawPtr = (*name).clone().into_addrefed();
1186 property_id
1187 },
1188 }
1189 }
1190}
1191
1192#[derive(Clone, PartialEq, Default)]
1194pub struct NonCustomPropertyIdSet {
1195 storage: [u32; ((property_counts::NON_CUSTOM as usize) - 1 + 32) / 32],
1196}
1197
1198impl NonCustomPropertyIdSet {
1199 pub fn new() -> Self {
1201 Self {
1202 storage: Default::default(),
1203 }
1204 }
1205
1206 #[inline]
1208 pub fn insert(&mut self, id: NonCustomPropertyId) {
1209 let bit = id.0 as usize;
1210 self.storage[bit / 32] |= 1 << (bit % 32);
1211 }
1212
1213 #[inline]
1215 pub fn contains(&self, id: NonCustomPropertyId) -> bool {
1216 let bit = id.0 as usize;
1217 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1218 }
1219}
1220
1221#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
1223pub struct LonghandIdSet {
1224 storage: [u32; ((property_counts::LONGHANDS as usize) - 1 + 32) / 32],
1225}
1226
1227to_shmem::impl_trivial_to_shmem!(LonghandIdSet);
1228
1229impl LonghandIdSet {
1230 #[inline]
1232 pub fn new() -> Self {
1233 Self {
1234 storage: Default::default(),
1235 }
1236 }
1237
1238 pub fn iter(&self) -> LonghandIdSetIterator<'_> {
1240 LonghandIdSetIterator {
1241 chunks: &self.storage,
1242 cur_chunk: 0,
1243 cur_bit: 0,
1244 }
1245 }
1246
1247 pub fn contains_all(&self, other: &Self) -> bool {
1250 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1251 if (*self_cell & *other_cell) != *other_cell {
1252 return false;
1253 }
1254 }
1255 true
1256 }
1257
1258 pub fn contains_any(&self, other: &Self) -> bool {
1260 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1261 if (*self_cell & *other_cell) != 0 {
1262 return true;
1263 }
1264 }
1265 false
1266 }
1267
1268 #[inline]
1270 pub fn remove_all(&mut self, other: &Self) {
1271 for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
1272 *self_cell &= !*other_cell;
1273 }
1274 }
1275
1276 #[inline]
1278 pub fn contains(&self, id: LonghandId) -> bool {
1279 let bit = id as usize;
1280 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1281 }
1282
1283 #[inline]
1285 pub fn contains_any_reset(&self) -> bool {
1286 self.contains_any(Self::reset())
1287 }
1288
1289 #[inline]
1291 pub fn insert(&mut self, id: LonghandId) {
1292 let bit = id as usize;
1293 self.storage[bit / 32] |= 1 << (bit % 32);
1294 }
1295
1296 #[inline]
1298 pub fn remove(&mut self, id: LonghandId) {
1299 let bit = id as usize;
1300 self.storage[bit / 32] &= !(1 << (bit % 32));
1301 }
1302
1303 #[inline]
1305 pub fn clear(&mut self) {
1306 for cell in &mut self.storage {
1307 *cell = 0
1308 }
1309 }
1310
1311 #[inline]
1313 pub fn is_empty(&self) -> bool {
1314 self.storage.iter().all(|c| *c == 0)
1315 }
1316}
1317
1318pub struct LonghandIdSetIterator<'a> {
1320 chunks: &'a [u32],
1321 cur_chunk: u32,
1322 cur_bit: u32, }
1324
1325impl<'a> Iterator for LonghandIdSetIterator<'a> {
1326 type Item = LonghandId;
1327
1328 fn next(&mut self) -> Option<Self::Item> {
1329 loop {
1330 debug_assert!(self.cur_bit < 32);
1331 let cur_chunk = self.cur_chunk;
1332 let cur_bit = self.cur_bit;
1333 let chunk = *self.chunks.get(cur_chunk as usize)?;
1334 let next_bit = (chunk >> cur_bit).trailing_zeros();
1335 if next_bit == 32 {
1336 self.cur_bit = 0;
1338 self.cur_chunk += 1;
1339 continue;
1340 }
1341 debug_assert!(cur_bit + next_bit < 32);
1342 let longhand_id = cur_chunk * 32 + cur_bit + next_bit;
1343 debug_assert!(longhand_id as usize <= property_counts::LONGHANDS);
1344 let id: LonghandId = unsafe { mem::transmute(longhand_id as u16) };
1345 self.cur_bit += next_bit + 1;
1346 if self.cur_bit == 32 {
1347 self.cur_bit = 0;
1348 self.cur_chunk += 1;
1349 }
1350 return Some(id);
1351 }
1352 }
1353}
1354
1355pub type SubpropertiesVec<T> = ArrayVec<T, { property_counts::MAX_SHORTHAND_EXPANDED }>;
1357
1358#[derive(Default)]
1362pub struct SourcePropertyDeclaration {
1363 pub declarations: SubpropertiesVec<PropertyDeclaration>,
1365 pub all_shorthand: AllShorthand,
1367}
1368
1369#[cfg(feature = "gecko")]
1372size_of_test!(SourcePropertyDeclaration, 632);
1373#[cfg(feature = "servo")]
1374size_of_test!(SourcePropertyDeclaration, 568);
1375
1376impl SourcePropertyDeclaration {
1377 #[inline]
1379 pub fn with_one(decl: PropertyDeclaration) -> Self {
1380 let mut result = Self::default();
1381 result.declarations.push(decl);
1382 result
1383 }
1384
1385 pub fn drain(&mut self) -> SourcePropertyDeclarationDrain<'_> {
1387 SourcePropertyDeclarationDrain {
1388 declarations: self.declarations.drain(..),
1389 all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
1390 }
1391 }
1392
1393 pub fn clear(&mut self) {
1395 self.declarations.clear();
1396 self.all_shorthand = AllShorthand::NotSet;
1397 }
1398
1399 pub fn is_empty(&self) -> bool {
1401 self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
1402 }
1403
1404 pub fn push(&mut self, declaration: PropertyDeclaration) {
1406 let _result = self.declarations.try_push(declaration);
1407 debug_assert!(_result.is_ok());
1408 }
1409}
1410
1411pub struct SourcePropertyDeclarationDrain<'a> {
1413 pub declarations:
1415 ArrayVecDrain<'a, PropertyDeclaration, { property_counts::MAX_SHORTHAND_EXPANDED }>,
1416 pub all_shorthand: AllShorthand,
1418}
1419
1420#[derive(Debug, Eq, PartialEq, ToShmem)]
1422pub struct UnparsedValue {
1423 pub(super) variable_value: custom_properties::VariableValue,
1425 from_shorthand: Option<ShorthandId>,
1427}
1428
1429impl ToCss for UnparsedValue {
1430 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1431 where
1432 W: Write,
1433 {
1434 if self.from_shorthand.is_none() {
1436 self.variable_value.to_css(dest)?;
1437 }
1438 Ok(())
1439 }
1440}
1441
1442pub type ShorthandsWithPropertyReferencesCache =
1449 FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
1450
1451impl UnparsedValue {
1452 fn substitute_variables<'cache>(
1453 &self,
1454 longhand_id: LonghandId,
1455 substitution_functions: &ComputedSubstitutionFunctions,
1456 stylist: &Stylist,
1457 computed_context: &computed::Context,
1458 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
1459 attribute_tracker: &mut AttributeTracker,
1460 ) -> Cow<'cache, PropertyDeclaration> {
1461 let invalid_at_computed_value_time = || {
1462 let keyword = if longhand_id.inherited() {
1463 CSSWideKeyword::Inherit
1464 } else {
1465 CSSWideKeyword::Initial
1466 };
1467 Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
1468 };
1469
1470 if computed_context
1471 .builder
1472 .invalid_non_custom_properties
1473 .contains(longhand_id)
1474 {
1475 return invalid_at_computed_value_time();
1476 }
1477
1478 if let Some(shorthand_id) = self.from_shorthand {
1479 let key = (shorthand_id, longhand_id);
1480 if shorthand_cache.contains_key(&key) {
1481 return Cow::Borrowed(&shorthand_cache[&key]);
1486 }
1487 }
1488
1489 let SubstitutionResult {
1490 css,
1491 attribute_tainted,
1492 } = match custom_properties::substitute(
1493 &self.variable_value,
1494 substitution_functions,
1495 stylist,
1496 computed_context,
1497 attribute_tracker,
1498 ) {
1499 Ok(css) => css,
1500 Err(..) => return invalid_at_computed_value_time(),
1501 };
1502
1503 let mut parsing_mode = ParsingMode::DEFAULT;
1514 if attribute_tainted {
1515 parsing_mode.insert(ParsingMode::DISALLOW_URLS);
1516 }
1517 let context = ParserContext::new(
1518 Origin::Author,
1519 &self.variable_value.url_data,
1520 None,
1521 parsing_mode,
1522 computed_context.quirks_mode,
1523 Default::default(),
1524 None,
1525 None,
1526 );
1527
1528 let mut input = ParserInput::new(&css);
1529 let mut input = Parser::new(&mut input);
1530 input.skip_whitespace();
1531
1532 if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
1533 return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
1534 }
1535
1536 let shorthand = match self.from_shorthand {
1537 None => {
1538 return match input.parse_entirely(|input| longhand_id.parse_value(&context, input))
1539 {
1540 Ok(decl) => Cow::Owned(decl),
1541 Err(..) => invalid_at_computed_value_time(),
1542 }
1543 },
1544 Some(shorthand) => shorthand,
1545 };
1546
1547 let mut decls = SourcePropertyDeclaration::default();
1548 if shorthand
1550 .parse_into(&mut decls, &context, &mut input)
1551 .is_err()
1552 {
1553 return invalid_at_computed_value_time();
1554 }
1555
1556 for declaration in decls.declarations.drain(..) {
1557 let longhand = declaration.id().as_longhand().unwrap();
1558 if longhand.is_logical() {
1559 let writing_mode = computed_context.builder.writing_mode;
1560 shorthand_cache.insert(
1561 (shorthand, longhand.to_physical(writing_mode)),
1562 declaration.clone(),
1563 );
1564 }
1565 shorthand_cache.insert((shorthand, longhand), declaration);
1566 }
1567
1568 let key = (shorthand, longhand_id);
1569 match shorthand_cache.get(&key) {
1570 Some(decl) => Cow::Borrowed(decl),
1571 None => invalid_at_computed_value_time(),
1583 }
1584 }
1585}
1586pub enum AllShorthand {
1588 NotSet,
1590 CSSWideKeyword(CSSWideKeyword),
1592 WithVariables(Arc<UnparsedValue>),
1594}
1595
1596impl Default for AllShorthand {
1597 fn default() -> Self {
1598 Self::NotSet
1599 }
1600}
1601
1602impl AllShorthand {
1603 #[inline]
1605 pub fn declarations(&self) -> AllShorthandDeclarationIterator<'_> {
1606 AllShorthandDeclarationIterator {
1607 all_shorthand: self,
1608 longhands: ShorthandId::All.longhands(),
1609 }
1610 }
1611}
1612
1613pub struct AllShorthandDeclarationIterator<'a> {
1615 all_shorthand: &'a AllShorthand,
1616 longhands: NonCustomPropertyIterator<LonghandId>,
1617}
1618
1619impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
1620 type Item = PropertyDeclaration;
1621
1622 #[inline]
1623 fn next(&mut self) -> Option<Self::Item> {
1624 match *self.all_shorthand {
1625 AllShorthand::NotSet => None,
1626 AllShorthand::CSSWideKeyword(ref keyword) => Some(
1627 PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword),
1628 ),
1629 AllShorthand::WithVariables(ref unparsed) => {
1630 Some(PropertyDeclaration::WithVariables(VariableDeclaration {
1631 id: self.longhands.next()?,
1632 value: unparsed.clone(),
1633 }))
1634 },
1635 }
1636 }
1637}
1638
1639pub struct NonCustomPropertyIterator<Item: 'static> {
1642 filter: bool,
1643 iter: std::slice::Iter<'static, Item>,
1644}
1645
1646impl<Item> Iterator for NonCustomPropertyIterator<Item>
1647where
1648 Item: 'static + Copy + Into<NonCustomPropertyId>,
1649{
1650 type Item = Item;
1651
1652 fn next(&mut self) -> Option<Self::Item> {
1653 loop {
1654 let id = *self.iter.next()?;
1655 if !self.filter || id.into().enabled_for_all_content() {
1656 return Some(id);
1657 }
1658 }
1659 }
1660}
1661
1662pub struct TransitionPropertyIterator<'a> {
1664 style: &'a ComputedValues,
1665 index_range: core::ops::Range<usize>,
1666 longhand_iterator: Option<NonCustomPropertyIterator<LonghandId>>,
1667}
1668
1669impl<'a> TransitionPropertyIterator<'a> {
1670 pub fn from_style(style: &'a ComputedValues) -> Self {
1672 Self {
1673 style,
1674 index_range: 0..style.get_ui().transition_property_count(),
1675 longhand_iterator: None,
1676 }
1677 }
1678}
1679
1680pub struct TransitionPropertyIteration {
1682 pub property: OwnedPropertyDeclarationId,
1684 pub index: usize,
1687}
1688
1689impl<'a> Iterator for TransitionPropertyIterator<'a> {
1690 type Item = TransitionPropertyIteration;
1691
1692 fn next(&mut self) -> Option<Self::Item> {
1693 use crate::values::computed::TransitionProperty;
1694 loop {
1695 if let Some(ref mut longhand_iterator) = self.longhand_iterator {
1696 if let Some(longhand_id) = longhand_iterator.next() {
1697 return Some(TransitionPropertyIteration {
1698 property: OwnedPropertyDeclarationId::Longhand(longhand_id),
1699 index: self.index_range.start - 1,
1700 });
1701 }
1702 self.longhand_iterator = None;
1703 }
1704
1705 let index = self.index_range.next()?;
1706 match self.style.get_ui().transition_property_at(index) {
1707 TransitionProperty::NonCustom(id) => {
1708 match id.longhand_or_shorthand() {
1709 Ok(longhand_id) => {
1710 return Some(TransitionPropertyIteration {
1711 property: OwnedPropertyDeclarationId::Longhand(longhand_id),
1712 index,
1713 });
1714 },
1715 Err(shorthand_id) => {
1716 self.longhand_iterator = Some(shorthand_id.longhands());
1720 },
1721 }
1722 },
1723 TransitionProperty::Custom(name) => {
1724 return Some(TransitionPropertyIteration {
1725 property: OwnedPropertyDeclarationId::Custom(name),
1726 index,
1727 })
1728 },
1729 TransitionProperty::Unsupported(..) => {},
1730 }
1731 }
1732 }
1733}