1pub mod cascade;
8pub mod declaration_block;
9
10pub use self::cascade::*;
11pub use self::declaration_block::*;
12pub use self::generated::*;
13#[macro_use]
16#[allow(unsafe_code)]
17#[deny(missing_docs)]
18pub mod generated {
19 include!(concat!(env!("OUT_DIR"), "/properties.rs"));
20}
21
22use crate::custom_properties::{self, ComputedCustomProperties};
23#[cfg(feature = "gecko")]
24use crate::gecko_bindings::structs::{nsCSSPropertyID, AnimatedPropertyID, RefPtr};
25use crate::logical_geometry::WritingMode;
26use crate::parser::ParserContext;
27use crate::str::CssString;
28use crate::stylesheets::CssRuleType;
29use crate::stylesheets::Origin;
30use crate::stylist::Stylist;
31use crate::values::{computed, serialize_atom_name};
32use arrayvec::{ArrayVec, Drain as ArrayVecDrain};
33use cssparser::{Parser, ParserInput};
34use fxhash::FxHashMap;
35use servo_arc::Arc;
36use std::{
37 borrow::Cow,
38 fmt::{self, Write},
39 mem,
40};
41use style_traits::{
42 CssWriter, KeywordsCollectFn, ParseError, ParsingMode, SpecifiedValueInfo, ToCss,
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(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
81pub enum CSSWideKeyword {
82 Initial,
84 Inherit,
86 Unset,
88 Revert,
90 RevertLayer,
92}
93
94impl CSSWideKeyword {
95 pub fn to_str(&self) -> &'static str {
97 match *self {
98 CSSWideKeyword::Initial => "initial",
99 CSSWideKeyword::Inherit => "inherit",
100 CSSWideKeyword::Unset => "unset",
101 CSSWideKeyword::Revert => "revert",
102 CSSWideKeyword::RevertLayer => "revert-layer",
103 }
104 }
105}
106
107impl CSSWideKeyword {
108 pub fn from_ident(ident: &str) -> Result<Self, ()> {
110 Ok(match_ignore_ascii_case! { ident,
111 "initial" => CSSWideKeyword::Initial,
112 "inherit" => CSSWideKeyword::Inherit,
113 "unset" => CSSWideKeyword::Unset,
114 "revert" => CSSWideKeyword::Revert,
115 "revert-layer" => CSSWideKeyword::RevertLayer,
116 _ => return Err(()),
117 })
118 }
119
120 pub fn parse(input: &mut Parser) -> Result<Self, ()> {
122 let keyword = {
123 let ident = input.expect_ident().map_err(|_| ())?;
124 Self::from_ident(ident)?
125 };
126 input.expect_exhausted().map_err(|_| ())?;
127 Ok(keyword)
128 }
129}
130
131#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf)]
133pub struct WideKeywordDeclaration {
134 #[css(skip)]
135 id: LonghandId,
136 pub keyword: CSSWideKeyword,
138}
139
140#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf)]
142pub struct VariableDeclaration {
143 #[css(skip)]
145 id: LonghandId,
146 #[ignore_malloc_size_of = "Arc"]
148 pub value: Arc<UnparsedValue>,
149}
150
151#[derive(Clone, PartialEq, ToCss, ToShmem)]
154pub enum CustomDeclarationValue {
155 Unparsed(Arc<custom_properties::SpecifiedValue>),
157 Parsed(Arc<crate::properties_and_values::value::SpecifiedValue>),
159 CSSWideKeyword(CSSWideKeyword),
161}
162
163#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf)]
165pub struct CustomDeclaration {
166 #[css(skip)]
168 pub name: custom_properties::Name,
169 #[ignore_malloc_size_of = "Arc"]
171 pub value: CustomDeclarationValue,
172}
173
174impl fmt::Debug for PropertyDeclaration {
175 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
176 self.id().to_css(&mut CssWriter::new(f))?;
177 f.write_str(": ")?;
178
179 let mut s = CssString::new();
183 self.to_css(&mut s)?;
184 write!(f, "{}", s)
185 }
186}
187
188#[derive(
190 Clone, Copy, Debug, PartialEq, Eq, Hash, ToComputedValue, ToResolvedValue, ToShmem, MallocSizeOf,
191)]
192#[repr(C)]
193pub struct NonCustomPropertyId(u16);
194
195impl ToCss for NonCustomPropertyId {
196 #[inline]
197 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
198 where
199 W: Write,
200 {
201 dest.write_str(self.name())
202 }
203}
204
205impl NonCustomPropertyId {
206 pub fn bit(self) -> usize {
208 self.0 as usize
209 }
210
211 #[cfg(feature = "gecko")]
213 #[inline]
214 pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
215 unsafe { mem::transmute(self.0 as i32) }
217 }
218
219 #[cfg(feature = "gecko")]
221 #[inline]
222 pub fn from_nscsspropertyid(prop: nsCSSPropertyID) -> Option<Self> {
223 let prop = prop as i32;
224 if prop < 0 || prop >= property_counts::NON_CUSTOM as i32 {
225 return None;
226 }
227 Some(NonCustomPropertyId(prop as u16))
229 }
230
231 pub fn unaliased(self) -> Self {
233 let Some(alias_id) = self.as_alias() else {
234 return self;
235 };
236 alias_id.aliased_property()
237 }
238
239 #[inline]
241 pub fn to_property_id(self) -> PropertyId {
242 PropertyId::NonCustom(self)
243 }
244
245 #[inline]
247 pub fn as_longhand(self) -> Option<LonghandId> {
248 if self.0 < property_counts::LONGHANDS as u16 {
249 return Some(unsafe { mem::transmute(self.0 as u16) });
250 }
251 None
252 }
253
254 #[inline]
256 pub fn as_shorthand(self) -> Option<ShorthandId> {
257 if self.0 >= property_counts::LONGHANDS as u16 &&
258 self.0 < property_counts::LONGHANDS_AND_SHORTHANDS as u16
259 {
260 return Some(unsafe { mem::transmute(self.0 - (property_counts::LONGHANDS as u16)) });
261 }
262 None
263 }
264
265 #[inline]
267 pub fn as_alias(self) -> Option<AliasId> {
268 debug_assert!((self.0 as usize) < property_counts::NON_CUSTOM);
269 if self.0 >= property_counts::LONGHANDS_AND_SHORTHANDS as u16 {
270 return Some(unsafe {
271 mem::transmute(self.0 - (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
272 });
273 }
274 None
275 }
276
277 #[inline]
279 pub fn longhand_or_shorthand(self) -> Result<LonghandId, ShorthandId> {
280 let id = self.unaliased();
281 match id.as_longhand() {
282 Some(lh) => Ok(lh),
283 None => Err(id.as_shorthand().unwrap()),
284 }
285 }
286
287 #[inline]
289 pub const fn from_longhand(id: LonghandId) -> Self {
290 Self(id as u16)
291 }
292
293 #[inline]
295 pub const fn from_shorthand(id: ShorthandId) -> Self {
296 Self((id as u16) + (property_counts::LONGHANDS as u16))
297 }
298
299 #[inline]
301 pub const fn from_alias(id: AliasId) -> Self {
302 Self((id as u16) + (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
303 }
304}
305
306impl From<LonghandId> for NonCustomPropertyId {
307 #[inline]
308 fn from(id: LonghandId) -> Self {
309 Self::from_longhand(id)
310 }
311}
312
313impl From<ShorthandId> for NonCustomPropertyId {
314 #[inline]
315 fn from(id: ShorthandId) -> Self {
316 Self::from_shorthand(id)
317 }
318}
319
320impl From<AliasId> for NonCustomPropertyId {
321 #[inline]
322 fn from(id: AliasId) -> Self {
323 Self::from_alias(id)
324 }
325}
326
327#[derive(Clone, Eq, PartialEq, Debug)]
330pub enum PropertyId {
331 NonCustom(NonCustomPropertyId),
333 Custom(custom_properties::Name),
335}
336
337impl ToCss for PropertyId {
338 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
339 where
340 W: Write,
341 {
342 match *self {
343 PropertyId::NonCustom(id) => dest.write_str(id.name()),
344 PropertyId::Custom(ref name) => {
345 dest.write_str("--")?;
346 serialize_atom_name(name, dest)
347 },
348 }
349 }
350}
351
352impl PropertyId {
353 #[inline]
355 pub fn longhand_id(&self) -> Option<LonghandId> {
356 self.non_custom_non_alias_id()?.as_longhand()
357 }
358
359 pub fn is_animatable(&self) -> bool {
361 match self {
362 Self::NonCustom(id) => id.is_animatable(),
363 Self::Custom(_) => cfg!(feature = "gecko"),
364 }
365 }
366
367 pub fn parse_unchecked_for_testing(name: &str) -> Result<Self, ()> {
372 Self::parse_unchecked(name, None)
373 }
374
375 #[inline]
378 pub fn parse_enabled_for_all_content(name: &str) -> Result<Self, ()> {
379 let id = Self::parse_unchecked(name, None)?;
380
381 if !id.enabled_for_all_content() {
382 return Err(());
383 }
384
385 Ok(id)
386 }
387
388 #[inline]
391 pub fn parse(name: &str, context: &ParserContext) -> Result<Self, ()> {
392 let id = Self::parse_unchecked(name, context.use_counters)?;
393 if !id.allowed_in(context) {
394 return Err(());
395 }
396 Ok(id)
397 }
398
399 #[inline]
404 pub fn parse_ignoring_rule_type(name: &str, context: &ParserContext) -> Result<Self, ()> {
405 let id = Self::parse_unchecked(name, None)?;
406 if !id.allowed_in_ignoring_rule_type(context) {
407 return Err(());
408 }
409 Ok(id)
410 }
411
412 #[cfg(feature = "gecko")]
414 #[inline]
415 pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Option<Self> {
416 Some(NonCustomPropertyId::from_nscsspropertyid(id)?.to_property_id())
417 }
418
419 #[cfg(feature = "gecko")]
421 #[inline]
422 pub fn from_gecko_animated_property_id(property: &AnimatedPropertyID) -> Option<Self> {
423 Some(
424 if property.mID == nsCSSPropertyID::eCSSPropertyExtra_variable {
425 debug_assert!(!property.mCustomName.mRawPtr.is_null());
426 Self::Custom(unsafe { crate::Atom::from_raw(property.mCustomName.mRawPtr) })
427 } else {
428 Self::NonCustom(NonCustomPropertyId::from_nscsspropertyid(property.mID)?)
429 },
430 )
431 }
432
433 #[inline]
435 pub fn is_shorthand(&self) -> bool {
436 self.as_shorthand().is_ok()
437 }
438
439 pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId> {
442 match *self {
443 Self::NonCustom(id) => match id.longhand_or_shorthand() {
444 Ok(lh) => Err(PropertyDeclarationId::Longhand(lh)),
445 Err(sh) => Ok(sh),
446 },
447 Self::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),
448 }
449 }
450
451 pub fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
453 match *self {
454 Self::Custom(_) => None,
455 Self::NonCustom(id) => Some(id),
456 }
457 }
458
459 fn non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId> {
462 self.non_custom_id().map(NonCustomPropertyId::unaliased)
463 }
464
465 #[inline]
468 pub fn enabled_for_all_content(&self) -> bool {
469 let id = match self.non_custom_id() {
470 None => return true,
472 Some(id) => id,
473 };
474
475 id.enabled_for_all_content()
476 }
477
478 #[cfg(feature = "gecko")]
482 #[inline]
483 pub fn to_nscsspropertyid_resolving_aliases(&self) -> nsCSSPropertyID {
484 match self.non_custom_non_alias_id() {
485 Some(id) => id.to_nscsspropertyid(),
486 None => nsCSSPropertyID::eCSSPropertyExtra_variable,
487 }
488 }
489
490 fn allowed_in(&self, context: &ParserContext) -> bool {
491 let id = match self.non_custom_id() {
492 None => return !context.nesting_context.rule_types.contains(CssRuleType::PositionTry),
494 Some(id) => id,
495 };
496 id.allowed_in(context)
497 }
498
499 #[inline]
500 fn allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool {
501 let id = match self.non_custom_id() {
502 None => return true,
504 Some(id) => id,
505 };
506 id.allowed_in_ignoring_rule_type(context)
507 }
508
509 pub fn supports_type(&self, ty: u8) -> bool {
512 let id = self.non_custom_non_alias_id();
513 id.map_or(0, |id| id.supported_types()) & ty != 0
514 }
515
516 pub fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
521 if let Some(id) = self.non_custom_non_alias_id() {
522 id.collect_property_completion_keywords(f);
523 }
524 CSSWideKeyword::collect_completion_keywords(f);
525 }
526}
527
528impl ToCss for LonghandId {
529 #[inline]
530 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
531 where
532 W: Write,
533 {
534 dest.write_str(self.name())
535 }
536}
537
538impl fmt::Debug for LonghandId {
539 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
540 formatter.write_str(self.name())
541 }
542}
543
544impl LonghandId {
545 #[inline]
547 pub fn name(&self) -> &'static str {
548 NonCustomPropertyId::from(*self).name()
549 }
550
551 #[inline]
553 pub fn inherited(self) -> bool {
554 !LonghandIdSet::reset().contains(self)
555 }
556
557 #[inline]
559 pub fn zoom_dependent(self) -> bool {
560 LonghandIdSet::zoom_dependent().contains(self)
561 }
562
563 #[inline]
566 pub fn ignored_when_document_colors_disabled(self) -> bool {
567 LonghandIdSet::ignored_when_colors_disabled().contains(self)
568 }
569
570 pub fn is_or_is_longhand_of(self, non_custom: NonCustomPropertyId) -> bool {
572 match non_custom.longhand_or_shorthand() {
573 Ok(lh) => self == lh,
574 Err(sh) => self.is_longhand_of(sh),
575 }
576 }
577
578 pub fn is_longhand_of(self, shorthand: ShorthandId) -> bool {
580 self.shorthands().any(|s| s == shorthand)
581 }
582
583 #[inline]
585 pub fn is_animatable(self) -> bool {
586 NonCustomPropertyId::from(self).is_animatable()
587 }
588
589 #[inline]
591 pub fn is_discrete_animatable(self) -> bool {
592 LonghandIdSet::discrete_animatable().contains(self)
593 }
594
595 #[cfg(feature = "gecko")]
597 #[inline]
598 pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
599 NonCustomPropertyId::from(self).to_nscsspropertyid()
600 }
601
602 #[cfg(feature = "gecko")]
603 pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Option<Self> {
605 NonCustomPropertyId::from_nscsspropertyid(id)?
606 .unaliased()
607 .as_longhand()
608 }
609
610 #[inline]
612 pub fn is_logical(self) -> bool {
613 LonghandIdSet::logical().contains(self)
614 }
615}
616
617impl ToCss for ShorthandId {
618 #[inline]
619 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
620 where
621 W: Write,
622 {
623 dest.write_str(self.name())
624 }
625}
626
627impl ShorthandId {
628 #[inline]
630 pub fn name(&self) -> &'static str {
631 NonCustomPropertyId::from(*self).name()
632 }
633
634 #[cfg(feature = "gecko")]
636 #[inline]
637 pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
638 NonCustomPropertyId::from(self).to_nscsspropertyid()
639 }
640
641 #[cfg(feature = "gecko")]
643 #[inline]
644 pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Option<Self> {
645 NonCustomPropertyId::from_nscsspropertyid(id)?
646 .unaliased()
647 .as_shorthand()
648 }
649
650 pub fn get_shorthand_appendable_value<'a, 'b: 'a>(
654 self,
655 declarations: &'a [&'b PropertyDeclaration],
656 ) -> Option<AppendableValue<'a, 'b>> {
657 let first_declaration = declarations.get(0)?;
658 let rest = || declarations.iter().skip(1);
659
660 if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
662 if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
663 return Some(AppendableValue::Css(css));
664 }
665 return None;
666 }
667
668 if let Some(keyword) = first_declaration.get_css_wide_keyword() {
670 if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {
671 return Some(AppendableValue::Css(keyword.to_str()));
672 }
673 return None;
674 }
675
676 if self == ShorthandId::All {
677 return None;
679 }
680
681 if declarations
683 .iter()
684 .all(|d| d.may_serialize_as_part_of_shorthand())
685 {
686 return Some(AppendableValue::DeclarationsForShorthand(
687 self,
688 declarations,
689 ));
690 }
691
692 None
693 }
694
695 #[inline]
697 pub fn is_legacy_shorthand(self) -> bool {
698 self.flags().contains(PropertyFlags::IS_LEGACY_SHORTHAND)
699 }
700}
701
702fn parse_non_custom_property_declaration_value_into<'i>(
703 declarations: &mut SourcePropertyDeclaration,
704 context: &ParserContext,
705 input: &mut Parser<'i, '_>,
706 start: &cssparser::ParserState,
707 parse_entirely_into: impl FnOnce(
708 &mut SourcePropertyDeclaration,
709 &mut Parser<'i, '_>,
710 ) -> Result<(), ParseError<'i>>,
711 parsed_wide_keyword: impl FnOnce(&mut SourcePropertyDeclaration, CSSWideKeyword),
712 parsed_custom: impl FnOnce(&mut SourcePropertyDeclaration, custom_properties::VariableValue),
713) -> Result<(), ParseError<'i>> {
714 let mut starts_with_curly_block = false;
715 if let Ok(token) = input.next() {
716 match token {
717 cssparser::Token::Ident(ref ident) => match CSSWideKeyword::from_ident(ident) {
718 Ok(wk) => {
719 if input.expect_exhausted().is_ok() {
720 return Ok(parsed_wide_keyword(declarations, wk));
721 }
722 },
723 Err(()) => {},
724 },
725 cssparser::Token::CurlyBracketBlock => {
726 starts_with_curly_block = true;
727 },
728 _ => {},
729 }
730 };
731
732 input.reset(&start);
733 input.look_for_var_or_env_functions();
734 let err = match parse_entirely_into(declarations, input) {
735 Ok(()) => {
736 input.seen_var_or_env_functions();
737 return Ok(());
738 },
739 Err(e) => e,
740 };
741
742 let start_pos = start.position();
744 let mut at_start = start_pos == input.position();
745 let mut invalid = false;
746 while let Ok(token) = input.next() {
747 if matches!(token, cssparser::Token::CurlyBracketBlock) {
748 if !starts_with_curly_block || !at_start {
749 invalid = true;
750 break;
751 }
752 } else if starts_with_curly_block {
753 invalid = true;
754 break;
755 }
756 at_start = false;
757 }
758 if !input.seen_var_or_env_functions() || invalid {
759 return Err(err);
760 }
761 input.reset(start);
762 let value = custom_properties::VariableValue::parse(input, &context.url_data)?;
763 parsed_custom(declarations, value);
764 Ok(())
765}
766
767impl PropertyDeclaration {
768 fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option<&str> {
769 match *self {
770 PropertyDeclaration::WithVariables(ref declaration) => {
771 let s = declaration.value.from_shorthand?;
772 if s != shorthand {
773 return None;
774 }
775 Some(&*declaration.value.variable_value.css)
776 },
777 _ => None,
778 }
779 }
780
781 #[inline]
783 pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
784 Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
785 }
786
787 #[inline]
789 pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
790 match *self {
791 PropertyDeclaration::CSSWideKeyword(ref declaration) => Some(declaration.keyword),
792 _ => None,
793 }
794 }
795
796 pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
809 match *self {
810 PropertyDeclaration::CSSWideKeyword(..) | PropertyDeclaration::WithVariables(..) => {
811 false
812 },
813 PropertyDeclaration::Custom(..) => {
814 unreachable!("Serializing a custom property as part of shorthand?")
815 },
816 _ => true,
817 }
818 }
819
820 pub fn is_animatable(&self) -> bool {
822 self.id().is_animatable()
823 }
824
825 pub fn is_custom(&self) -> bool {
828 matches!(*self, PropertyDeclaration::Custom(..))
829 }
830
831 pub fn parse_into<'i, 't>(
842 declarations: &mut SourcePropertyDeclaration,
843 id: PropertyId,
844 context: &ParserContext,
845 input: &mut Parser<'i, 't>,
846 ) -> Result<(), ParseError<'i>> {
847 assert!(declarations.is_empty());
848 debug_assert!(id.allowed_in(context), "{:?}", id);
849 input.skip_whitespace();
850
851 let start = input.state();
852 let non_custom_id = match id {
853 PropertyId::Custom(property_name) => {
854 let value = match input.try_parse(CSSWideKeyword::parse) {
855 Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
856 Err(()) => CustomDeclarationValue::Unparsed(Arc::new(
857 custom_properties::VariableValue::parse(input, &context.url_data)?,
858 )),
859 };
860 declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
861 name: property_name,
862 value,
863 }));
864 return Ok(());
865 },
866 PropertyId::NonCustom(id) => id,
867 };
868 match non_custom_id.longhand_or_shorthand() {
869 Ok(longhand_id) => {
870 parse_non_custom_property_declaration_value_into(
871 declarations,
872 context,
873 input,
874 &start,
875 |declarations, input| {
876 let decl = input
877 .parse_entirely(|input| longhand_id.parse_value(context, input))?;
878 declarations.push(decl);
879 Ok(())
880 },
881 |declarations, wk| {
882 declarations.push(PropertyDeclaration::css_wide_keyword(longhand_id, wk));
883 },
884 |declarations, variable_value| {
885 declarations.push(PropertyDeclaration::WithVariables(VariableDeclaration {
886 id: longhand_id,
887 value: Arc::new(UnparsedValue {
888 variable_value,
889 from_shorthand: None,
890 }),
891 }))
892 },
893 )?;
894 },
895 Err(shorthand_id) => {
896 parse_non_custom_property_declaration_value_into(
897 declarations,
898 context,
899 input,
900 &start,
901 |declarations, input| shorthand_id.parse_into(declarations, context, input),
904 |declarations, wk| {
905 if shorthand_id == ShorthandId::All {
906 declarations.all_shorthand = AllShorthand::CSSWideKeyword(wk)
907 } else {
908 for longhand in shorthand_id.longhands() {
909 declarations
910 .push(PropertyDeclaration::css_wide_keyword(longhand, wk));
911 }
912 }
913 },
914 |declarations, variable_value| {
915 let unparsed = Arc::new(UnparsedValue {
916 variable_value,
917 from_shorthand: Some(shorthand_id),
918 });
919 if shorthand_id == ShorthandId::All {
920 declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
921 } else {
922 for id in shorthand_id.longhands() {
923 declarations.push(PropertyDeclaration::WithVariables(
924 VariableDeclaration {
925 id,
926 value: unparsed.clone(),
927 },
928 ))
929 }
930 }
931 },
932 )?;
933 },
934 }
935 if let Some(use_counters) = context.use_counters {
936 use_counters.non_custom_properties.record(non_custom_id);
937 }
938 Ok(())
939 }
940}
941
942#[derive(Clone, Debug, PartialEq, Eq, Hash)]
944pub enum OwnedPropertyDeclarationId {
945 Longhand(LonghandId),
947 Custom(custom_properties::Name),
949}
950
951impl OwnedPropertyDeclarationId {
952 #[inline]
954 pub fn is_logical(&self) -> bool {
955 self.as_borrowed().is_logical()
956 }
957
958 #[inline]
960 pub fn as_borrowed(&self) -> PropertyDeclarationId {
961 match self {
962 Self::Longhand(id) => PropertyDeclarationId::Longhand(*id),
963 Self::Custom(name) => PropertyDeclarationId::Custom(name),
964 }
965 }
966
967 #[cfg(feature = "gecko")]
969 #[inline]
970 pub fn from_gecko_animated_property_id(property: &AnimatedPropertyID) -> Option<Self> {
971 Some(
972 match PropertyId::from_gecko_animated_property_id(property)? {
973 PropertyId::Custom(name) => Self::Custom(name),
974 PropertyId::NonCustom(id) => Self::Longhand(id.as_longhand()?),
975 },
976 )
977 }
978}
979
980#[derive(Clone, Copy, Debug, PartialEq, MallocSizeOf)]
983pub enum PropertyDeclarationId<'a> {
984 Longhand(LonghandId),
986 Custom(&'a custom_properties::Name),
988}
989
990impl<'a> ToCss for PropertyDeclarationId<'a> {
991 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
992 where
993 W: Write,
994 {
995 match *self {
996 PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
997 PropertyDeclarationId::Custom(name) => {
998 dest.write_str("--")?;
999 serialize_atom_name(name, dest)
1000 },
1001 }
1002 }
1003}
1004
1005impl<'a> PropertyDeclarationId<'a> {
1006 #[inline(always)]
1008 pub fn flags(&self) -> PropertyFlags {
1009 match self {
1010 Self::Longhand(id) => id.flags(),
1011 Self::Custom(_) => PropertyFlags::empty(),
1012 }
1013 }
1014
1015 pub fn to_owned(&self) -> OwnedPropertyDeclarationId {
1017 match self {
1018 PropertyDeclarationId::Longhand(id) => OwnedPropertyDeclarationId::Longhand(*id),
1019 PropertyDeclarationId::Custom(name) => {
1020 OwnedPropertyDeclarationId::Custom((*name).clone())
1021 },
1022 }
1023 }
1024
1025 pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
1028 match *self {
1029 PropertyDeclarationId::Longhand(id) => match *other {
1030 PropertyId::NonCustom(non_custom_id) => id.is_or_is_longhand_of(non_custom_id),
1031 PropertyId::Custom(_) => false,
1032 },
1033 PropertyDeclarationId::Custom(name) => {
1034 matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
1035 },
1036 }
1037 }
1038
1039 pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
1042 match *self {
1043 PropertyDeclarationId::Longhand(ref id) => id.is_longhand_of(shorthand),
1044 _ => false,
1045 }
1046 }
1047
1048 pub fn name(&self) -> Cow<'static, str> {
1050 match *self {
1051 PropertyDeclarationId::Longhand(id) => id.name().into(),
1052 PropertyDeclarationId::Custom(name) => {
1053 let mut s = String::new();
1054 write!(&mut s, "--{}", name).unwrap();
1055 s.into()
1056 },
1057 }
1058 }
1059
1060 #[inline]
1062 pub fn as_longhand(&self) -> Option<LonghandId> {
1063 match *self {
1064 PropertyDeclarationId::Longhand(id) => Some(id),
1065 _ => None,
1066 }
1067 }
1068
1069 #[inline]
1071 pub fn is_logical(&self) -> bool {
1072 match self {
1073 PropertyDeclarationId::Longhand(id) => id.is_logical(),
1074 PropertyDeclarationId::Custom(_) => false,
1075 }
1076 }
1077
1078 #[inline]
1083 pub fn to_physical(&self, wm: WritingMode) -> Self {
1084 match self {
1085 Self::Longhand(id) => Self::Longhand(id.to_physical(wm)),
1086 Self::Custom(_) => self.clone(),
1087 }
1088 }
1089
1090 #[inline]
1092 pub fn is_animatable(&self) -> bool {
1093 match self {
1094 Self::Longhand(id) => id.is_animatable(),
1095 Self::Custom(_) => cfg!(feature = "gecko"),
1096 }
1097 }
1098
1099 #[inline]
1101 pub fn is_discrete_animatable(&self) -> bool {
1102 match self {
1103 Self::Longhand(longhand) => longhand.is_discrete_animatable(),
1104 Self::Custom(_) => cfg!(feature = "gecko")
1106 }
1107 }
1108
1109 #[cfg(feature = "gecko")]
1112 #[inline]
1113 pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
1114 match self {
1115 PropertyDeclarationId::Longhand(id) => id.to_nscsspropertyid(),
1116 PropertyDeclarationId::Custom(_) => nsCSSPropertyID::eCSSPropertyExtra_variable,
1117 }
1118 }
1119
1120 #[cfg(feature = "gecko")]
1125 #[inline]
1126 pub fn to_gecko_animated_property_id(&self) -> AnimatedPropertyID {
1127 match self {
1128 Self::Longhand(id) => AnimatedPropertyID {
1129 mID: id.to_nscsspropertyid(),
1130 mCustomName: RefPtr::null(),
1131 },
1132 Self::Custom(name) => {
1133 let mut property_id = AnimatedPropertyID {
1134 mID: nsCSSPropertyID::eCSSPropertyExtra_variable,
1135 mCustomName: RefPtr::null(),
1136 };
1137 property_id.mCustomName.mRawPtr = (*name).clone().into_addrefed();
1138 property_id
1139 },
1140 }
1141 }
1142}
1143
1144#[derive(Clone, PartialEq, Default)]
1146pub struct NonCustomPropertyIdSet {
1147 storage: [u32; ((property_counts::NON_CUSTOM as usize) - 1 + 32) / 32],
1148}
1149
1150impl NonCustomPropertyIdSet {
1151 pub fn new() -> Self {
1153 Self {
1154 storage: Default::default(),
1155 }
1156 }
1157
1158 #[inline]
1160 pub fn insert(&mut self, id: NonCustomPropertyId) {
1161 let bit = id.0 as usize;
1162 self.storage[bit / 32] |= 1 << (bit % 32);
1163 }
1164
1165 #[inline]
1167 pub fn contains(&self, id: NonCustomPropertyId) -> bool {
1168 let bit = id.0 as usize;
1169 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1170 }
1171}
1172
1173#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
1175pub struct LonghandIdSet {
1176 storage: [u32; ((property_counts::LONGHANDS as usize) - 1 + 32) / 32],
1177}
1178
1179to_shmem::impl_trivial_to_shmem!(LonghandIdSet);
1180
1181impl LonghandIdSet {
1182 #[inline]
1184 pub fn new() -> Self {
1185 Self {
1186 storage: Default::default(),
1187 }
1188 }
1189
1190 pub fn iter(&self) -> LonghandIdSetIterator {
1192 LonghandIdSetIterator {
1193 chunks: &self.storage,
1194 cur_chunk: 0,
1195 cur_bit: 0,
1196 }
1197 }
1198
1199 pub fn contains_all(&self, other: &Self) -> bool {
1202 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1203 if (*self_cell & *other_cell) != *other_cell {
1204 return false;
1205 }
1206 }
1207 true
1208 }
1209
1210 pub fn contains_any(&self, other: &Self) -> bool {
1212 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1213 if (*self_cell & *other_cell) != 0 {
1214 return true;
1215 }
1216 }
1217 false
1218 }
1219
1220 #[inline]
1222 pub fn remove_all(&mut self, other: &Self) {
1223 for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
1224 *self_cell &= !*other_cell;
1225 }
1226 }
1227
1228 #[inline]
1230 pub fn contains(&self, id: LonghandId) -> bool {
1231 let bit = id as usize;
1232 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1233 }
1234
1235 #[inline]
1237 pub fn contains_any_reset(&self) -> bool {
1238 self.contains_any(Self::reset())
1239 }
1240
1241 #[inline]
1243 pub fn insert(&mut self, id: LonghandId) {
1244 let bit = id as usize;
1245 self.storage[bit / 32] |= 1 << (bit % 32);
1246 }
1247
1248 #[inline]
1250 pub fn remove(&mut self, id: LonghandId) {
1251 let bit = id as usize;
1252 self.storage[bit / 32] &= !(1 << (bit % 32));
1253 }
1254
1255 #[inline]
1257 pub fn clear(&mut self) {
1258 for cell in &mut self.storage {
1259 *cell = 0
1260 }
1261 }
1262
1263 #[inline]
1265 pub fn is_empty(&self) -> bool {
1266 self.storage.iter().all(|c| *c == 0)
1267 }
1268}
1269
1270pub struct LonghandIdSetIterator<'a> {
1272 chunks: &'a [u32],
1273 cur_chunk: u32,
1274 cur_bit: u32, }
1276
1277impl<'a> Iterator for LonghandIdSetIterator<'a> {
1278 type Item = LonghandId;
1279
1280 fn next(&mut self) -> Option<Self::Item> {
1281 loop {
1282 debug_assert!(self.cur_bit < 32);
1283 let cur_chunk = self.cur_chunk;
1284 let cur_bit = self.cur_bit;
1285 let chunk = *self.chunks.get(cur_chunk as usize)?;
1286 let next_bit = (chunk >> cur_bit).trailing_zeros();
1287 if next_bit == 32 {
1288 self.cur_bit = 0;
1290 self.cur_chunk += 1;
1291 continue;
1292 }
1293 debug_assert!(cur_bit + next_bit < 32);
1294 let longhand_id = cur_chunk * 32 + cur_bit + next_bit;
1295 debug_assert!(longhand_id as usize <= property_counts::LONGHANDS);
1296 let id: LonghandId = unsafe { mem::transmute(longhand_id as u16) };
1297 self.cur_bit += next_bit + 1;
1298 if self.cur_bit == 32 {
1299 self.cur_bit = 0;
1300 self.cur_chunk += 1;
1301 }
1302 return Some(id);
1303 }
1304 }
1305}
1306
1307pub type SubpropertiesVec<T> = ArrayVec<T, { property_counts::MAX_SHORTHAND_EXPANDED }>;
1309
1310#[derive(Default)]
1314pub struct SourcePropertyDeclaration {
1315 pub declarations: SubpropertiesVec<PropertyDeclaration>,
1317 pub all_shorthand: AllShorthand,
1319}
1320
1321#[cfg(feature = "gecko")]
1324size_of_test!(SourcePropertyDeclaration, 632);
1325#[cfg(feature = "servo")]
1326size_of_test!(SourcePropertyDeclaration, 568);
1327
1328impl SourcePropertyDeclaration {
1329 #[inline]
1331 pub fn with_one(decl: PropertyDeclaration) -> Self {
1332 let mut result = Self::default();
1333 result.declarations.push(decl);
1334 result
1335 }
1336
1337 pub fn drain(&mut self) -> SourcePropertyDeclarationDrain {
1339 SourcePropertyDeclarationDrain {
1340 declarations: self.declarations.drain(..),
1341 all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
1342 }
1343 }
1344
1345 pub fn clear(&mut self) {
1347 self.declarations.clear();
1348 self.all_shorthand = AllShorthand::NotSet;
1349 }
1350
1351 pub fn is_empty(&self) -> bool {
1353 self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
1354 }
1355
1356 pub fn push(&mut self, declaration: PropertyDeclaration) {
1358 let _result = self.declarations.try_push(declaration);
1359 debug_assert!(_result.is_ok());
1360 }
1361}
1362
1363pub struct SourcePropertyDeclarationDrain<'a> {
1365 pub declarations:
1367 ArrayVecDrain<'a, PropertyDeclaration, { property_counts::MAX_SHORTHAND_EXPANDED }>,
1368 pub all_shorthand: AllShorthand,
1370}
1371
1372#[derive(Debug, Eq, PartialEq, ToShmem)]
1374pub struct UnparsedValue {
1375 pub(super) variable_value: custom_properties::VariableValue,
1377 from_shorthand: Option<ShorthandId>,
1379}
1380
1381impl ToCss for UnparsedValue {
1382 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1383 where
1384 W: Write,
1385 {
1386 if self.from_shorthand.is_none() {
1388 self.variable_value.to_css(dest)?;
1389 }
1390 Ok(())
1391 }
1392}
1393
1394pub type ShorthandsWithPropertyReferencesCache =
1401 FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
1402
1403impl UnparsedValue {
1404 fn substitute_variables<'cache>(
1405 &self,
1406 longhand_id: LonghandId,
1407 custom_properties: &ComputedCustomProperties,
1408 stylist: &Stylist,
1409 computed_context: &computed::Context,
1410 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
1411 ) -> Cow<'cache, PropertyDeclaration> {
1412 let invalid_at_computed_value_time = || {
1413 let keyword = if longhand_id.inherited() {
1414 CSSWideKeyword::Inherit
1415 } else {
1416 CSSWideKeyword::Initial
1417 };
1418 Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
1419 };
1420
1421 if computed_context
1422 .builder
1423 .invalid_non_custom_properties
1424 .contains(longhand_id)
1425 {
1426 return invalid_at_computed_value_time();
1427 }
1428
1429 if let Some(shorthand_id) = self.from_shorthand {
1430 let key = (shorthand_id, longhand_id);
1431 if shorthand_cache.contains_key(&key) {
1432 return Cow::Borrowed(&shorthand_cache[&key]);
1437 }
1438 }
1439
1440 let css = match custom_properties::substitute(
1441 &self.variable_value,
1442 custom_properties,
1443 stylist,
1444 computed_context,
1445 ) {
1446 Ok(css) => css,
1447 Err(..) => return invalid_at_computed_value_time(),
1448 };
1449
1450 let context = ParserContext::new(
1461 Origin::Author,
1462 &self.variable_value.url_data,
1463 None,
1464 ParsingMode::DEFAULT,
1465 computed_context.quirks_mode,
1466 Default::default(),
1467 None,
1468 None,
1469 );
1470
1471 let mut input = ParserInput::new(&css);
1472 let mut input = Parser::new(&mut input);
1473 input.skip_whitespace();
1474
1475 if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
1476 return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
1477 }
1478
1479 let shorthand = match self.from_shorthand {
1480 None => {
1481 return match input.parse_entirely(|input| longhand_id.parse_value(&context, input))
1482 {
1483 Ok(decl) => Cow::Owned(decl),
1484 Err(..) => invalid_at_computed_value_time(),
1485 }
1486 },
1487 Some(shorthand) => shorthand,
1488 };
1489
1490 let mut decls = SourcePropertyDeclaration::default();
1491 if shorthand
1493 .parse_into(&mut decls, &context, &mut input)
1494 .is_err()
1495 {
1496 return invalid_at_computed_value_time();
1497 }
1498
1499 for declaration in decls.declarations.drain(..) {
1500 let longhand = declaration.id().as_longhand().unwrap();
1501 if longhand.is_logical() {
1502 let writing_mode = computed_context.builder.writing_mode;
1503 shorthand_cache.insert(
1504 (shorthand, longhand.to_physical(writing_mode)),
1505 declaration.clone(),
1506 );
1507 }
1508 shorthand_cache.insert((shorthand, longhand), declaration);
1509 }
1510
1511 let key = (shorthand, longhand_id);
1512 match shorthand_cache.get(&key) {
1513 Some(decl) => Cow::Borrowed(decl),
1514 None => invalid_at_computed_value_time(),
1526 }
1527 }
1528}
1529pub enum AllShorthand {
1531 NotSet,
1533 CSSWideKeyword(CSSWideKeyword),
1535 WithVariables(Arc<UnparsedValue>),
1537}
1538
1539impl Default for AllShorthand {
1540 fn default() -> Self {
1541 Self::NotSet
1542 }
1543}
1544
1545impl AllShorthand {
1546 #[inline]
1548 pub fn declarations(&self) -> AllShorthandDeclarationIterator {
1549 AllShorthandDeclarationIterator {
1550 all_shorthand: self,
1551 longhands: ShorthandId::All.longhands(),
1552 }
1553 }
1554}
1555
1556pub struct AllShorthandDeclarationIterator<'a> {
1558 all_shorthand: &'a AllShorthand,
1559 longhands: NonCustomPropertyIterator<LonghandId>,
1560}
1561
1562impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
1563 type Item = PropertyDeclaration;
1564
1565 #[inline]
1566 fn next(&mut self) -> Option<Self::Item> {
1567 match *self.all_shorthand {
1568 AllShorthand::NotSet => None,
1569 AllShorthand::CSSWideKeyword(ref keyword) => Some(
1570 PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword),
1571 ),
1572 AllShorthand::WithVariables(ref unparsed) => {
1573 Some(PropertyDeclaration::WithVariables(VariableDeclaration {
1574 id: self.longhands.next()?,
1575 value: unparsed.clone(),
1576 }))
1577 },
1578 }
1579 }
1580}
1581
1582pub struct NonCustomPropertyIterator<Item: 'static> {
1585 filter: bool,
1586 iter: std::slice::Iter<'static, Item>,
1587}
1588
1589impl<Item> Iterator for NonCustomPropertyIterator<Item>
1590where
1591 Item: 'static + Copy + Into<NonCustomPropertyId>,
1592{
1593 type Item = Item;
1594
1595 fn next(&mut self) -> Option<Self::Item> {
1596 loop {
1597 let id = *self.iter.next()?;
1598 if !self.filter || id.into().enabled_for_all_content() {
1599 return Some(id);
1600 }
1601 }
1602 }
1603}