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 => {
494 return !context
495 .nesting_context
496 .rule_types
497 .contains(CssRuleType::PositionTry)
498 },
499 Some(id) => id,
500 };
501 id.allowed_in(context)
502 }
503
504 #[inline]
505 fn allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool {
506 let id = match self.non_custom_id() {
507 None => return true,
509 Some(id) => id,
510 };
511 id.allowed_in_ignoring_rule_type(context)
512 }
513
514 pub fn supports_type(&self, ty: u8) -> bool {
517 let id = self.non_custom_non_alias_id();
518 id.map_or(0, |id| id.supported_types()) & ty != 0
519 }
520
521 pub fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
526 if let Some(id) = self.non_custom_non_alias_id() {
527 id.collect_property_completion_keywords(f);
528 }
529 CSSWideKeyword::collect_completion_keywords(f);
530 }
531}
532
533impl ToCss for LonghandId {
534 #[inline]
535 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
536 where
537 W: Write,
538 {
539 dest.write_str(self.name())
540 }
541}
542
543impl fmt::Debug for LonghandId {
544 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
545 formatter.write_str(self.name())
546 }
547}
548
549impl LonghandId {
550 #[inline]
552 pub fn name(&self) -> &'static str {
553 NonCustomPropertyId::from(*self).name()
554 }
555
556 #[inline]
558 pub fn inherited(self) -> bool {
559 !LonghandIdSet::reset().contains(self)
560 }
561
562 #[inline]
564 pub fn zoom_dependent(self) -> bool {
565 LonghandIdSet::zoom_dependent().contains(self)
566 }
567
568 #[inline]
571 pub fn ignored_when_document_colors_disabled(self) -> bool {
572 LonghandIdSet::ignored_when_colors_disabled().contains(self)
573 }
574
575 pub fn is_or_is_longhand_of(self, non_custom: NonCustomPropertyId) -> bool {
577 match non_custom.longhand_or_shorthand() {
578 Ok(lh) => self == lh,
579 Err(sh) => self.is_longhand_of(sh),
580 }
581 }
582
583 pub fn is_longhand_of(self, shorthand: ShorthandId) -> bool {
585 self.shorthands().any(|s| s == shorthand)
586 }
587
588 #[inline]
590 pub fn is_animatable(self) -> bool {
591 NonCustomPropertyId::from(self).is_animatable()
592 }
593
594 #[inline]
596 pub fn is_discrete_animatable(self) -> bool {
597 LonghandIdSet::discrete_animatable().contains(self)
598 }
599
600 #[cfg(feature = "gecko")]
602 #[inline]
603 pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
604 NonCustomPropertyId::from(self).to_nscsspropertyid()
605 }
606
607 #[cfg(feature = "gecko")]
608 pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Option<Self> {
610 NonCustomPropertyId::from_nscsspropertyid(id)?
611 .unaliased()
612 .as_longhand()
613 }
614
615 #[inline]
617 pub fn is_logical(self) -> bool {
618 LonghandIdSet::logical().contains(self)
619 }
620}
621
622impl ToCss for ShorthandId {
623 #[inline]
624 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
625 where
626 W: Write,
627 {
628 dest.write_str(self.name())
629 }
630}
631
632impl ShorthandId {
633 #[inline]
635 pub fn name(&self) -> &'static str {
636 NonCustomPropertyId::from(*self).name()
637 }
638
639 #[cfg(feature = "gecko")]
641 #[inline]
642 pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
643 NonCustomPropertyId::from(self).to_nscsspropertyid()
644 }
645
646 #[cfg(feature = "gecko")]
648 #[inline]
649 pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Option<Self> {
650 NonCustomPropertyId::from_nscsspropertyid(id)?
651 .unaliased()
652 .as_shorthand()
653 }
654
655 pub fn get_shorthand_appendable_value<'a, 'b: 'a>(
659 self,
660 declarations: &'a [&'b PropertyDeclaration],
661 ) -> Option<AppendableValue<'a, 'b>> {
662 let first_declaration = declarations.get(0)?;
663 let rest = || declarations.iter().skip(1);
664
665 if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
667 if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
668 return Some(AppendableValue::Css(css));
669 }
670 return None;
671 }
672
673 if let Some(keyword) = first_declaration.get_css_wide_keyword() {
675 if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {
676 return Some(AppendableValue::Css(keyword.to_str()));
677 }
678 return None;
679 }
680
681 if self == ShorthandId::All {
682 return None;
684 }
685
686 if declarations
688 .iter()
689 .all(|d| d.may_serialize_as_part_of_shorthand())
690 {
691 return Some(AppendableValue::DeclarationsForShorthand(
692 self,
693 declarations,
694 ));
695 }
696
697 None
698 }
699
700 #[inline]
702 pub fn is_legacy_shorthand(self) -> bool {
703 self.flags().contains(PropertyFlags::IS_LEGACY_SHORTHAND)
704 }
705}
706
707fn parse_non_custom_property_declaration_value_into<'i>(
708 declarations: &mut SourcePropertyDeclaration,
709 context: &ParserContext,
710 input: &mut Parser<'i, '_>,
711 start: &cssparser::ParserState,
712 parse_entirely_into: impl FnOnce(
713 &mut SourcePropertyDeclaration,
714 &mut Parser<'i, '_>,
715 ) -> Result<(), ParseError<'i>>,
716 parsed_wide_keyword: impl FnOnce(&mut SourcePropertyDeclaration, CSSWideKeyword),
717 parsed_custom: impl FnOnce(&mut SourcePropertyDeclaration, custom_properties::VariableValue),
718) -> Result<(), ParseError<'i>> {
719 let mut starts_with_curly_block = false;
720 if let Ok(token) = input.next() {
721 match token {
722 cssparser::Token::Ident(ref ident) => match CSSWideKeyword::from_ident(ident) {
723 Ok(wk) => {
724 if input.expect_exhausted().is_ok() {
725 return Ok(parsed_wide_keyword(declarations, wk));
726 }
727 },
728 Err(()) => {},
729 },
730 cssparser::Token::CurlyBracketBlock => {
731 starts_with_curly_block = true;
732 },
733 _ => {},
734 }
735 };
736
737 input.reset(&start);
738 input.look_for_var_or_env_functions();
739 let err = match parse_entirely_into(declarations, input) {
740 Ok(()) => {
741 input.seen_var_or_env_functions();
742 return Ok(());
743 },
744 Err(e) => e,
745 };
746
747 let start_pos = start.position();
749 let mut at_start = start_pos == input.position();
750 let mut invalid = false;
751 while let Ok(token) = input.next() {
752 if matches!(token, cssparser::Token::CurlyBracketBlock) {
753 if !starts_with_curly_block || !at_start {
754 invalid = true;
755 break;
756 }
757 } else if starts_with_curly_block {
758 invalid = true;
759 break;
760 }
761 at_start = false;
762 }
763 if !input.seen_var_or_env_functions() || invalid {
764 return Err(err);
765 }
766 input.reset(start);
767 let value = custom_properties::VariableValue::parse(input, &context.url_data)?;
768 parsed_custom(declarations, value);
769 Ok(())
770}
771
772impl PropertyDeclaration {
773 fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option<&str> {
774 match *self {
775 PropertyDeclaration::WithVariables(ref declaration) => {
776 let s = declaration.value.from_shorthand?;
777 if s != shorthand {
778 return None;
779 }
780 Some(&*declaration.value.variable_value.css)
781 },
782 _ => None,
783 }
784 }
785
786 #[inline]
788 pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
789 Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
790 }
791
792 #[inline]
794 pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
795 match *self {
796 PropertyDeclaration::CSSWideKeyword(ref declaration) => Some(declaration.keyword),
797 _ => None,
798 }
799 }
800
801 pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
814 match *self {
815 PropertyDeclaration::CSSWideKeyword(..) | PropertyDeclaration::WithVariables(..) => {
816 false
817 },
818 PropertyDeclaration::Custom(..) => {
819 unreachable!("Serializing a custom property as part of shorthand?")
820 },
821 _ => true,
822 }
823 }
824
825 pub fn is_animatable(&self) -> bool {
827 self.id().is_animatable()
828 }
829
830 pub fn is_custom(&self) -> bool {
833 matches!(*self, PropertyDeclaration::Custom(..))
834 }
835
836 pub fn parse_into<'i, 't>(
847 declarations: &mut SourcePropertyDeclaration,
848 id: PropertyId,
849 context: &ParserContext,
850 input: &mut Parser<'i, 't>,
851 ) -> Result<(), ParseError<'i>> {
852 assert!(declarations.is_empty());
853 debug_assert!(id.allowed_in(context), "{:?}", id);
854 input.skip_whitespace();
855
856 let start = input.state();
857 let non_custom_id = match id {
858 PropertyId::Custom(property_name) => {
859 let value = match input.try_parse(CSSWideKeyword::parse) {
860 Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
861 Err(()) => CustomDeclarationValue::Unparsed(Arc::new(
862 custom_properties::VariableValue::parse(input, &context.url_data)?,
863 )),
864 };
865 declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
866 name: property_name,
867 value,
868 }));
869 return Ok(());
870 },
871 PropertyId::NonCustom(id) => id,
872 };
873 match non_custom_id.longhand_or_shorthand() {
874 Ok(longhand_id) => {
875 parse_non_custom_property_declaration_value_into(
876 declarations,
877 context,
878 input,
879 &start,
880 |declarations, input| {
881 let decl = input
882 .parse_entirely(|input| longhand_id.parse_value(context, input))?;
883 declarations.push(decl);
884 Ok(())
885 },
886 |declarations, wk| {
887 declarations.push(PropertyDeclaration::css_wide_keyword(longhand_id, wk));
888 },
889 |declarations, variable_value| {
890 declarations.push(PropertyDeclaration::WithVariables(VariableDeclaration {
891 id: longhand_id,
892 value: Arc::new(UnparsedValue {
893 variable_value,
894 from_shorthand: None,
895 }),
896 }))
897 },
898 )?;
899 },
900 Err(shorthand_id) => {
901 parse_non_custom_property_declaration_value_into(
902 declarations,
903 context,
904 input,
905 &start,
906 |declarations, input| shorthand_id.parse_into(declarations, context, input),
909 |declarations, wk| {
910 if shorthand_id == ShorthandId::All {
911 declarations.all_shorthand = AllShorthand::CSSWideKeyword(wk)
912 } else {
913 for longhand in shorthand_id.longhands() {
914 declarations
915 .push(PropertyDeclaration::css_wide_keyword(longhand, wk));
916 }
917 }
918 },
919 |declarations, variable_value| {
920 let unparsed = Arc::new(UnparsedValue {
921 variable_value,
922 from_shorthand: Some(shorthand_id),
923 });
924 if shorthand_id == ShorthandId::All {
925 declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
926 } else {
927 for id in shorthand_id.longhands() {
928 declarations.push(PropertyDeclaration::WithVariables(
929 VariableDeclaration {
930 id,
931 value: unparsed.clone(),
932 },
933 ))
934 }
935 }
936 },
937 )?;
938 },
939 }
940 if let Some(use_counters) = context.use_counters {
941 use_counters.non_custom_properties.record(non_custom_id);
942 }
943 Ok(())
944 }
945}
946
947#[derive(Clone, Debug, PartialEq, Eq, Hash)]
949pub enum OwnedPropertyDeclarationId {
950 Longhand(LonghandId),
952 Custom(custom_properties::Name),
954}
955
956impl OwnedPropertyDeclarationId {
957 #[inline]
959 pub fn is_logical(&self) -> bool {
960 self.as_borrowed().is_logical()
961 }
962
963 #[inline]
965 pub fn as_borrowed(&self) -> PropertyDeclarationId {
966 match self {
967 Self::Longhand(id) => PropertyDeclarationId::Longhand(*id),
968 Self::Custom(name) => PropertyDeclarationId::Custom(name),
969 }
970 }
971
972 #[cfg(feature = "gecko")]
974 #[inline]
975 pub fn from_gecko_animated_property_id(property: &AnimatedPropertyID) -> Option<Self> {
976 Some(
977 match PropertyId::from_gecko_animated_property_id(property)? {
978 PropertyId::Custom(name) => Self::Custom(name),
979 PropertyId::NonCustom(id) => Self::Longhand(id.as_longhand()?),
980 },
981 )
982 }
983}
984
985#[derive(Clone, Copy, Debug, PartialEq, MallocSizeOf)]
988pub enum PropertyDeclarationId<'a> {
989 Longhand(LonghandId),
991 Custom(&'a custom_properties::Name),
993}
994
995impl<'a> ToCss for PropertyDeclarationId<'a> {
996 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
997 where
998 W: Write,
999 {
1000 match *self {
1001 PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
1002 PropertyDeclarationId::Custom(name) => {
1003 dest.write_str("--")?;
1004 serialize_atom_name(name, dest)
1005 },
1006 }
1007 }
1008}
1009
1010impl<'a> PropertyDeclarationId<'a> {
1011 #[inline(always)]
1013 pub fn flags(&self) -> PropertyFlags {
1014 match self {
1015 Self::Longhand(id) => id.flags(),
1016 Self::Custom(_) => PropertyFlags::empty(),
1017 }
1018 }
1019
1020 pub fn to_owned(&self) -> OwnedPropertyDeclarationId {
1022 match self {
1023 PropertyDeclarationId::Longhand(id) => OwnedPropertyDeclarationId::Longhand(*id),
1024 PropertyDeclarationId::Custom(name) => {
1025 OwnedPropertyDeclarationId::Custom((*name).clone())
1026 },
1027 }
1028 }
1029
1030 pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
1033 match *self {
1034 PropertyDeclarationId::Longhand(id) => match *other {
1035 PropertyId::NonCustom(non_custom_id) => id.is_or_is_longhand_of(non_custom_id),
1036 PropertyId::Custom(_) => false,
1037 },
1038 PropertyDeclarationId::Custom(name) => {
1039 matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
1040 },
1041 }
1042 }
1043
1044 pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
1047 match *self {
1048 PropertyDeclarationId::Longhand(ref id) => id.is_longhand_of(shorthand),
1049 _ => false,
1050 }
1051 }
1052
1053 pub fn name(&self) -> Cow<'static, str> {
1055 match *self {
1056 PropertyDeclarationId::Longhand(id) => id.name().into(),
1057 PropertyDeclarationId::Custom(name) => {
1058 let mut s = String::new();
1059 write!(&mut s, "--{}", name).unwrap();
1060 s.into()
1061 },
1062 }
1063 }
1064
1065 #[inline]
1067 pub fn as_longhand(&self) -> Option<LonghandId> {
1068 match *self {
1069 PropertyDeclarationId::Longhand(id) => Some(id),
1070 _ => None,
1071 }
1072 }
1073
1074 #[inline]
1076 pub fn is_logical(&self) -> bool {
1077 match self {
1078 PropertyDeclarationId::Longhand(id) => id.is_logical(),
1079 PropertyDeclarationId::Custom(_) => false,
1080 }
1081 }
1082
1083 #[inline]
1088 pub fn to_physical(&self, wm: WritingMode) -> Self {
1089 match self {
1090 Self::Longhand(id) => Self::Longhand(id.to_physical(wm)),
1091 Self::Custom(_) => self.clone(),
1092 }
1093 }
1094
1095 #[inline]
1097 pub fn is_animatable(&self) -> bool {
1098 match self {
1099 Self::Longhand(id) => id.is_animatable(),
1100 Self::Custom(_) => cfg!(feature = "gecko"),
1101 }
1102 }
1103
1104 #[inline]
1106 pub fn is_discrete_animatable(&self) -> bool {
1107 match self {
1108 Self::Longhand(longhand) => longhand.is_discrete_animatable(),
1109 Self::Custom(_) => cfg!(feature = "gecko"),
1111 }
1112 }
1113
1114 #[cfg(feature = "gecko")]
1117 #[inline]
1118 pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
1119 match self {
1120 PropertyDeclarationId::Longhand(id) => id.to_nscsspropertyid(),
1121 PropertyDeclarationId::Custom(_) => nsCSSPropertyID::eCSSPropertyExtra_variable,
1122 }
1123 }
1124
1125 #[cfg(feature = "gecko")]
1130 #[inline]
1131 pub fn to_gecko_animated_property_id(&self) -> AnimatedPropertyID {
1132 match self {
1133 Self::Longhand(id) => AnimatedPropertyID {
1134 mID: id.to_nscsspropertyid(),
1135 mCustomName: RefPtr::null(),
1136 },
1137 Self::Custom(name) => {
1138 let mut property_id = AnimatedPropertyID {
1139 mID: nsCSSPropertyID::eCSSPropertyExtra_variable,
1140 mCustomName: RefPtr::null(),
1141 };
1142 property_id.mCustomName.mRawPtr = (*name).clone().into_addrefed();
1143 property_id
1144 },
1145 }
1146 }
1147}
1148
1149#[derive(Clone, PartialEq, Default)]
1151pub struct NonCustomPropertyIdSet {
1152 storage: [u32; ((property_counts::NON_CUSTOM as usize) - 1 + 32) / 32],
1153}
1154
1155impl NonCustomPropertyIdSet {
1156 pub fn new() -> Self {
1158 Self {
1159 storage: Default::default(),
1160 }
1161 }
1162
1163 #[inline]
1165 pub fn insert(&mut self, id: NonCustomPropertyId) {
1166 let bit = id.0 as usize;
1167 self.storage[bit / 32] |= 1 << (bit % 32);
1168 }
1169
1170 #[inline]
1172 pub fn contains(&self, id: NonCustomPropertyId) -> bool {
1173 let bit = id.0 as usize;
1174 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1175 }
1176}
1177
1178#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
1180pub struct LonghandIdSet {
1181 storage: [u32; ((property_counts::LONGHANDS as usize) - 1 + 32) / 32],
1182}
1183
1184to_shmem::impl_trivial_to_shmem!(LonghandIdSet);
1185
1186impl LonghandIdSet {
1187 #[inline]
1189 pub fn new() -> Self {
1190 Self {
1191 storage: Default::default(),
1192 }
1193 }
1194
1195 pub fn iter(&self) -> LonghandIdSetIterator {
1197 LonghandIdSetIterator {
1198 chunks: &self.storage,
1199 cur_chunk: 0,
1200 cur_bit: 0,
1201 }
1202 }
1203
1204 pub fn contains_all(&self, other: &Self) -> bool {
1207 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1208 if (*self_cell & *other_cell) != *other_cell {
1209 return false;
1210 }
1211 }
1212 true
1213 }
1214
1215 pub fn contains_any(&self, other: &Self) -> bool {
1217 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1218 if (*self_cell & *other_cell) != 0 {
1219 return true;
1220 }
1221 }
1222 false
1223 }
1224
1225 #[inline]
1227 pub fn remove_all(&mut self, other: &Self) {
1228 for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
1229 *self_cell &= !*other_cell;
1230 }
1231 }
1232
1233 #[inline]
1235 pub fn contains(&self, id: LonghandId) -> bool {
1236 let bit = id as usize;
1237 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1238 }
1239
1240 #[inline]
1242 pub fn contains_any_reset(&self) -> bool {
1243 self.contains_any(Self::reset())
1244 }
1245
1246 #[inline]
1248 pub fn insert(&mut self, id: LonghandId) {
1249 let bit = id as usize;
1250 self.storage[bit / 32] |= 1 << (bit % 32);
1251 }
1252
1253 #[inline]
1255 pub fn remove(&mut self, id: LonghandId) {
1256 let bit = id as usize;
1257 self.storage[bit / 32] &= !(1 << (bit % 32));
1258 }
1259
1260 #[inline]
1262 pub fn clear(&mut self) {
1263 for cell in &mut self.storage {
1264 *cell = 0
1265 }
1266 }
1267
1268 #[inline]
1270 pub fn is_empty(&self) -> bool {
1271 self.storage.iter().all(|c| *c == 0)
1272 }
1273}
1274
1275pub struct LonghandIdSetIterator<'a> {
1277 chunks: &'a [u32],
1278 cur_chunk: u32,
1279 cur_bit: u32, }
1281
1282impl<'a> Iterator for LonghandIdSetIterator<'a> {
1283 type Item = LonghandId;
1284
1285 fn next(&mut self) -> Option<Self::Item> {
1286 loop {
1287 debug_assert!(self.cur_bit < 32);
1288 let cur_chunk = self.cur_chunk;
1289 let cur_bit = self.cur_bit;
1290 let chunk = *self.chunks.get(cur_chunk as usize)?;
1291 let next_bit = (chunk >> cur_bit).trailing_zeros();
1292 if next_bit == 32 {
1293 self.cur_bit = 0;
1295 self.cur_chunk += 1;
1296 continue;
1297 }
1298 debug_assert!(cur_bit + next_bit < 32);
1299 let longhand_id = cur_chunk * 32 + cur_bit + next_bit;
1300 debug_assert!(longhand_id as usize <= property_counts::LONGHANDS);
1301 let id: LonghandId = unsafe { mem::transmute(longhand_id as u16) };
1302 self.cur_bit += next_bit + 1;
1303 if self.cur_bit == 32 {
1304 self.cur_bit = 0;
1305 self.cur_chunk += 1;
1306 }
1307 return Some(id);
1308 }
1309 }
1310}
1311
1312pub type SubpropertiesVec<T> = ArrayVec<T, { property_counts::MAX_SHORTHAND_EXPANDED }>;
1314
1315#[derive(Default)]
1319pub struct SourcePropertyDeclaration {
1320 pub declarations: SubpropertiesVec<PropertyDeclaration>,
1322 pub all_shorthand: AllShorthand,
1324}
1325
1326#[cfg(feature = "gecko")]
1329size_of_test!(SourcePropertyDeclaration, 632);
1330#[cfg(feature = "servo")]
1331size_of_test!(SourcePropertyDeclaration, 568);
1332
1333impl SourcePropertyDeclaration {
1334 #[inline]
1336 pub fn with_one(decl: PropertyDeclaration) -> Self {
1337 let mut result = Self::default();
1338 result.declarations.push(decl);
1339 result
1340 }
1341
1342 pub fn drain(&mut self) -> SourcePropertyDeclarationDrain {
1344 SourcePropertyDeclarationDrain {
1345 declarations: self.declarations.drain(..),
1346 all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
1347 }
1348 }
1349
1350 pub fn clear(&mut self) {
1352 self.declarations.clear();
1353 self.all_shorthand = AllShorthand::NotSet;
1354 }
1355
1356 pub fn is_empty(&self) -> bool {
1358 self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
1359 }
1360
1361 pub fn push(&mut self, declaration: PropertyDeclaration) {
1363 let _result = self.declarations.try_push(declaration);
1364 debug_assert!(_result.is_ok());
1365 }
1366}
1367
1368pub struct SourcePropertyDeclarationDrain<'a> {
1370 pub declarations:
1372 ArrayVecDrain<'a, PropertyDeclaration, { property_counts::MAX_SHORTHAND_EXPANDED }>,
1373 pub all_shorthand: AllShorthand,
1375}
1376
1377#[derive(Debug, Eq, PartialEq, ToShmem)]
1379pub struct UnparsedValue {
1380 pub(super) variable_value: custom_properties::VariableValue,
1382 from_shorthand: Option<ShorthandId>,
1384}
1385
1386impl ToCss for UnparsedValue {
1387 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1388 where
1389 W: Write,
1390 {
1391 if self.from_shorthand.is_none() {
1393 self.variable_value.to_css(dest)?;
1394 }
1395 Ok(())
1396 }
1397}
1398
1399pub type ShorthandsWithPropertyReferencesCache =
1406 FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
1407
1408impl UnparsedValue {
1409 fn substitute_variables<'cache>(
1410 &self,
1411 longhand_id: LonghandId,
1412 custom_properties: &ComputedCustomProperties,
1413 stylist: &Stylist,
1414 computed_context: &computed::Context,
1415 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
1416 ) -> Cow<'cache, PropertyDeclaration> {
1417 let invalid_at_computed_value_time = || {
1418 let keyword = if longhand_id.inherited() {
1419 CSSWideKeyword::Inherit
1420 } else {
1421 CSSWideKeyword::Initial
1422 };
1423 Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
1424 };
1425
1426 if computed_context
1427 .builder
1428 .invalid_non_custom_properties
1429 .contains(longhand_id)
1430 {
1431 return invalid_at_computed_value_time();
1432 }
1433
1434 if let Some(shorthand_id) = self.from_shorthand {
1435 let key = (shorthand_id, longhand_id);
1436 if shorthand_cache.contains_key(&key) {
1437 return Cow::Borrowed(&shorthand_cache[&key]);
1442 }
1443 }
1444
1445 let css = match custom_properties::substitute(
1446 &self.variable_value,
1447 custom_properties,
1448 stylist,
1449 computed_context,
1450 ) {
1451 Ok(css) => css,
1452 Err(..) => return invalid_at_computed_value_time(),
1453 };
1454
1455 let context = ParserContext::new(
1466 Origin::Author,
1467 &self.variable_value.url_data,
1468 None,
1469 ParsingMode::DEFAULT,
1470 computed_context.quirks_mode,
1471 Default::default(),
1472 None,
1473 None,
1474 );
1475
1476 let mut input = ParserInput::new(&css);
1477 let mut input = Parser::new(&mut input);
1478 input.skip_whitespace();
1479
1480 if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
1481 return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
1482 }
1483
1484 let shorthand = match self.from_shorthand {
1485 None => {
1486 return match input.parse_entirely(|input| longhand_id.parse_value(&context, input))
1487 {
1488 Ok(decl) => Cow::Owned(decl),
1489 Err(..) => invalid_at_computed_value_time(),
1490 }
1491 },
1492 Some(shorthand) => shorthand,
1493 };
1494
1495 let mut decls = SourcePropertyDeclaration::default();
1496 if shorthand
1498 .parse_into(&mut decls, &context, &mut input)
1499 .is_err()
1500 {
1501 return invalid_at_computed_value_time();
1502 }
1503
1504 for declaration in decls.declarations.drain(..) {
1505 let longhand = declaration.id().as_longhand().unwrap();
1506 if longhand.is_logical() {
1507 let writing_mode = computed_context.builder.writing_mode;
1508 shorthand_cache.insert(
1509 (shorthand, longhand.to_physical(writing_mode)),
1510 declaration.clone(),
1511 );
1512 }
1513 shorthand_cache.insert((shorthand, longhand), declaration);
1514 }
1515
1516 let key = (shorthand, longhand_id);
1517 match shorthand_cache.get(&key) {
1518 Some(decl) => Cow::Borrowed(decl),
1519 None => invalid_at_computed_value_time(),
1531 }
1532 }
1533}
1534pub enum AllShorthand {
1536 NotSet,
1538 CSSWideKeyword(CSSWideKeyword),
1540 WithVariables(Arc<UnparsedValue>),
1542}
1543
1544impl Default for AllShorthand {
1545 fn default() -> Self {
1546 Self::NotSet
1547 }
1548}
1549
1550impl AllShorthand {
1551 #[inline]
1553 pub fn declarations(&self) -> AllShorthandDeclarationIterator {
1554 AllShorthandDeclarationIterator {
1555 all_shorthand: self,
1556 longhands: ShorthandId::All.longhands(),
1557 }
1558 }
1559}
1560
1561pub struct AllShorthandDeclarationIterator<'a> {
1563 all_shorthand: &'a AllShorthand,
1564 longhands: NonCustomPropertyIterator<LonghandId>,
1565}
1566
1567impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
1568 type Item = PropertyDeclaration;
1569
1570 #[inline]
1571 fn next(&mut self) -> Option<Self::Item> {
1572 match *self.all_shorthand {
1573 AllShorthand::NotSet => None,
1574 AllShorthand::CSSWideKeyword(ref keyword) => Some(
1575 PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword),
1576 ),
1577 AllShorthand::WithVariables(ref unparsed) => {
1578 Some(PropertyDeclaration::WithVariables(VariableDeclaration {
1579 id: self.longhands.next()?,
1580 value: unparsed.clone(),
1581 }))
1582 },
1583 }
1584 }
1585}
1586
1587pub struct NonCustomPropertyIterator<Item: 'static> {
1590 filter: bool,
1591 iter: std::slice::Iter<'static, Item>,
1592}
1593
1594impl<Item> Iterator for NonCustomPropertyIterator<Item>
1595where
1596 Item: 'static + Copy + Into<NonCustomPropertyId>,
1597{
1598 type Item = Item;
1599
1600 fn next(&mut self) -> Option<Self::Item> {
1601 loop {
1602 let id = *self.iter.next()?;
1603 if !self.filter || id.into().enabled_for_all_content() {
1604 return Some(id);
1605 }
1606 }
1607 }
1608}