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::custom_properties::{self, ComputedCustomProperties};
25use crate::derives::*;
26use crate::dom::AttributeTracker;
27#[cfg(feature = "gecko")]
28use crate::gecko_bindings::structs::{CSSPropertyId, NonCustomCSSPropertyId, RefPtr};
29use crate::logical_geometry::WritingMode;
30use crate::parser::ParserContext;
31use crate::stylesheets::CssRuleType;
32use crate::stylesheets::Origin;
33use crate::stylist::Stylist;
34use crate::values::{computed, serialize_atom_name};
35use arrayvec::{ArrayVec, Drain as ArrayVecDrain};
36use cssparser::{match_ignore_ascii_case, Parser, ParserInput};
37use rustc_hash::FxHashMap;
38use servo_arc::Arc;
39use std::{
40 borrow::Cow,
41 fmt::{self, Write},
42 mem,
43};
44use style_traits::{
45 CssString, CssWriter, KeywordsCollectFn, ParseError, ParsingMode, SpecifiedValueInfo, ToCss,
46 ToTyped, TypedValue,
47};
48
49bitflags! {
50 #[derive(Clone, Copy)]
52 pub struct PropertyFlags: u16 {
53 const APPLIES_TO_FIRST_LETTER = 1 << 1;
55 const APPLIES_TO_FIRST_LINE = 1 << 2;
57 const APPLIES_TO_PLACEHOLDER = 1 << 3;
59 const APPLIES_TO_CUE = 1 << 4;
61 const APPLIES_TO_MARKER = 1 << 5;
63 const IS_LEGACY_SHORTHAND = 1 << 6;
67
68 const CAN_ANIMATE_ON_COMPOSITOR = 0;
74 const AFFECTS_LAYOUT = 0;
76 #[allow(missing_docs)]
77 const AFFECTS_OVERFLOW = 0;
78 #[allow(missing_docs)]
79 const AFFECTS_PAINT = 0;
80 }
81}
82
83#[derive(
85 Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
86)]
87pub enum CSSWideKeyword {
88 Initial,
90 Inherit,
92 Unset,
94 Revert,
96 RevertLayer,
98}
99
100impl CSSWideKeyword {
101 pub fn to_str(&self) -> &'static str {
103 match *self {
104 CSSWideKeyword::Initial => "initial",
105 CSSWideKeyword::Inherit => "inherit",
106 CSSWideKeyword::Unset => "unset",
107 CSSWideKeyword::Revert => "revert",
108 CSSWideKeyword::RevertLayer => "revert-layer",
109 }
110 }
111}
112
113impl CSSWideKeyword {
114 pub fn from_ident(ident: &str) -> Result<Self, ()> {
116 Ok(match_ignore_ascii_case! { ident,
117 "initial" => CSSWideKeyword::Initial,
118 "inherit" => CSSWideKeyword::Inherit,
119 "unset" => CSSWideKeyword::Unset,
120 "revert" => CSSWideKeyword::Revert,
121 "revert-layer" => CSSWideKeyword::RevertLayer,
122 _ => return Err(()),
123 })
124 }
125
126 pub fn parse(input: &mut Parser) -> Result<Self, ()> {
128 let keyword = {
129 let ident = input.expect_ident().map_err(|_| ())?;
130 Self::from_ident(ident)?
131 };
132 input.expect_exhausted().map_err(|_| ())?;
133 Ok(keyword)
134 }
135}
136
137#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf)]
139pub struct WideKeywordDeclaration {
140 #[css(skip)]
141 id: LonghandId,
142 pub keyword: CSSWideKeyword,
144}
145
146impl ToTyped for WideKeywordDeclaration {
149 fn to_typed(&self) -> Option<TypedValue> {
150 self.keyword.to_typed()
151 }
152}
153
154#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
156pub struct VariableDeclaration {
157 #[css(skip)]
159 id: LonghandId,
160 #[ignore_malloc_size_of = "Arc"]
162 pub value: Arc<UnparsedValue>,
163}
164
165#[derive(Clone, PartialEq, ToCss, ToShmem)]
168pub enum CustomDeclarationValue {
169 Unparsed(Arc<custom_properties::SpecifiedValue>),
171 Parsed(Arc<crate::properties_and_values::value::SpecifiedValue>),
173 CSSWideKeyword(CSSWideKeyword),
175}
176
177#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
179pub struct CustomDeclaration {
180 #[css(skip)]
182 pub name: custom_properties::Name,
183 #[ignore_malloc_size_of = "Arc"]
185 pub value: CustomDeclarationValue,
186}
187
188impl fmt::Debug for PropertyDeclaration {
189 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
190 self.id().to_css(&mut CssWriter::new(f))?;
191 f.write_str(": ")?;
192
193 let mut s = CssString::new();
197 self.to_css(&mut s)?;
198 write!(f, "{}", s)
199 }
200}
201
202#[derive(
204 Clone, Copy, Debug, PartialEq, Eq, Hash, ToComputedValue, ToResolvedValue, ToShmem, MallocSizeOf,
205)]
206#[repr(C)]
207pub struct NonCustomPropertyId(u16);
208
209impl ToCss for NonCustomPropertyId {
210 #[inline]
211 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
212 where
213 W: Write,
214 {
215 dest.write_str(self.name())
216 }
217}
218
219impl NonCustomPropertyId {
220 pub fn bit(self) -> usize {
222 self.0 as usize
223 }
224
225 #[cfg(feature = "gecko")]
227 #[inline]
228 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
229 unsafe { mem::transmute(self.0) }
231 }
232
233 #[cfg(feature = "gecko")]
235 #[inline]
236 pub fn from_noncustomcsspropertyid(prop: NonCustomCSSPropertyId) -> Option<Self> {
237 let prop = prop as u16;
238 if prop >= property_counts::NON_CUSTOM as u16 {
239 return None;
240 }
241 Some(NonCustomPropertyId(prop))
243 }
244
245 pub fn unaliased(self) -> Self {
247 let Some(alias_id) = self.as_alias() else {
248 return self;
249 };
250 alias_id.aliased_property()
251 }
252
253 #[inline]
255 pub fn to_property_id(self) -> PropertyId {
256 PropertyId::NonCustom(self)
257 }
258
259 #[inline]
261 pub fn as_longhand(self) -> Option<LonghandId> {
262 if self.0 < property_counts::LONGHANDS as u16 {
263 return Some(unsafe { mem::transmute(self.0 as u16) });
264 }
265 None
266 }
267
268 #[inline]
270 pub fn as_shorthand(self) -> Option<ShorthandId> {
271 if self.0 >= property_counts::LONGHANDS as u16
272 && self.0 < property_counts::LONGHANDS_AND_SHORTHANDS as u16
273 {
274 return Some(unsafe { mem::transmute(self.0 - (property_counts::LONGHANDS as u16)) });
275 }
276 None
277 }
278
279 #[inline]
281 pub fn as_alias(self) -> Option<AliasId> {
282 debug_assert!((self.0 as usize) < property_counts::NON_CUSTOM);
283 if self.0 >= property_counts::LONGHANDS_AND_SHORTHANDS as u16 {
284 return Some(unsafe {
285 mem::transmute(self.0 - (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
286 });
287 }
288 None
289 }
290
291 #[inline]
293 pub fn longhand_or_shorthand(self) -> Result<LonghandId, ShorthandId> {
294 let id = self.unaliased();
295 match id.as_longhand() {
296 Some(lh) => Ok(lh),
297 None => Err(id.as_shorthand().unwrap()),
298 }
299 }
300
301 #[inline]
303 pub const fn from_longhand(id: LonghandId) -> Self {
304 Self(id as u16)
305 }
306
307 #[inline]
309 pub const fn from_shorthand(id: ShorthandId) -> Self {
310 Self((id as u16) + (property_counts::LONGHANDS as u16))
311 }
312
313 #[inline]
315 pub const fn from_alias(id: AliasId) -> Self {
316 Self((id as u16) + (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
317 }
318}
319
320impl From<LonghandId> for NonCustomPropertyId {
321 #[inline]
322 fn from(id: LonghandId) -> Self {
323 Self::from_longhand(id)
324 }
325}
326
327impl From<ShorthandId> for NonCustomPropertyId {
328 #[inline]
329 fn from(id: ShorthandId) -> Self {
330 Self::from_shorthand(id)
331 }
332}
333
334impl From<AliasId> for NonCustomPropertyId {
335 #[inline]
336 fn from(id: AliasId) -> Self {
337 Self::from_alias(id)
338 }
339}
340
341#[derive(Clone, Eq, PartialEq, Debug)]
344pub enum PropertyId {
345 NonCustom(NonCustomPropertyId),
347 Custom(custom_properties::Name),
349}
350
351impl ToCss for PropertyId {
352 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
353 where
354 W: Write,
355 {
356 match *self {
357 PropertyId::NonCustom(id) => dest.write_str(id.name()),
358 PropertyId::Custom(ref name) => {
359 dest.write_str("--")?;
360 serialize_atom_name(name, dest)
361 },
362 }
363 }
364}
365
366impl PropertyId {
367 #[inline]
369 pub fn longhand_id(&self) -> Option<LonghandId> {
370 self.non_custom_non_alias_id()?.as_longhand()
371 }
372
373 pub fn is_animatable(&self) -> bool {
375 match self {
376 Self::NonCustom(id) => id.is_animatable(),
377 Self::Custom(_) => true,
378 }
379 }
380
381 pub fn parse_unchecked_for_testing(name: &str) -> Result<Self, ()> {
386 Self::parse_unchecked(name, None)
387 }
388
389 #[inline]
392 pub fn parse_enabled_for_all_content(name: &str) -> Result<Self, ()> {
393 let id = Self::parse_unchecked(name, None)?;
394
395 if !id.enabled_for_all_content() {
396 return Err(());
397 }
398
399 Ok(id)
400 }
401
402 #[inline]
405 pub fn parse(name: &str, context: &ParserContext) -> Result<Self, ()> {
406 let id = Self::parse_unchecked(name, context.use_counters)?;
407 if !id.allowed_in(context) {
408 return Err(());
409 }
410 Ok(id)
411 }
412
413 #[inline]
418 pub fn parse_ignoring_rule_type(name: &str, context: &ParserContext) -> Result<Self, ()> {
419 let id = Self::parse_unchecked(name, None)?;
420 if !id.allowed_in_ignoring_rule_type(context) {
421 return Err(());
422 }
423 Ok(id)
424 }
425
426 #[cfg(feature = "gecko")]
428 #[inline]
429 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
430 Some(NonCustomPropertyId::from_noncustomcsspropertyid(id)?.to_property_id())
431 }
432
433 #[cfg(feature = "gecko")]
435 #[inline]
436 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
437 Some(
438 if property.mId == NonCustomCSSPropertyId::eCSSPropertyExtra_variable {
439 debug_assert!(!property.mCustomName.mRawPtr.is_null());
440 Self::Custom(unsafe { crate::Atom::from_raw(property.mCustomName.mRawPtr) })
441 } else {
442 Self::NonCustom(NonCustomPropertyId::from_noncustomcsspropertyid(
443 property.mId,
444 )?)
445 },
446 )
447 }
448
449 #[inline]
451 pub fn is_shorthand(&self) -> bool {
452 self.as_shorthand().is_ok()
453 }
454
455 pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId<'_>> {
458 match *self {
459 Self::NonCustom(id) => match id.longhand_or_shorthand() {
460 Ok(lh) => Err(PropertyDeclarationId::Longhand(lh)),
461 Err(sh) => Ok(sh),
462 },
463 Self::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),
464 }
465 }
466
467 pub fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
469 match *self {
470 Self::Custom(_) => None,
471 Self::NonCustom(id) => Some(id),
472 }
473 }
474
475 fn non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId> {
478 self.non_custom_id().map(NonCustomPropertyId::unaliased)
479 }
480
481 #[inline]
484 pub fn enabled_for_all_content(&self) -> bool {
485 let id = match self.non_custom_id() {
486 None => return true,
488 Some(id) => id,
489 };
490
491 id.enabled_for_all_content()
492 }
493
494 #[cfg(feature = "gecko")]
498 #[inline]
499 pub fn to_noncustomcsspropertyid_resolving_aliases(&self) -> NonCustomCSSPropertyId {
500 match self.non_custom_non_alias_id() {
501 Some(id) => id.to_noncustomcsspropertyid(),
502 None => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
503 }
504 }
505
506 fn allowed_in(&self, context: &ParserContext) -> bool {
507 let id = match self.non_custom_id() {
508 None => {
510 return !context
511 .nesting_context
512 .rule_types
513 .contains(CssRuleType::PositionTry)
514 },
515 Some(id) => id,
516 };
517 id.allowed_in(context)
518 }
519
520 #[inline]
521 fn allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool {
522 let id = match self.non_custom_id() {
523 None => return true,
525 Some(id) => id,
526 };
527 id.allowed_in_ignoring_rule_type(context)
528 }
529
530 pub fn supports_type(&self, ty: u8) -> bool {
533 let id = self.non_custom_non_alias_id();
534 id.map_or(0, |id| id.supported_types()) & ty != 0
535 }
536
537 pub fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
542 if let Some(id) = self.non_custom_non_alias_id() {
543 id.collect_property_completion_keywords(f);
544 }
545 CSSWideKeyword::collect_completion_keywords(f);
546 }
547}
548
549impl ToCss for LonghandId {
550 #[inline]
551 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
552 where
553 W: Write,
554 {
555 dest.write_str(self.name())
556 }
557}
558
559impl fmt::Debug for LonghandId {
560 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
561 formatter.write_str(self.name())
562 }
563}
564
565impl LonghandId {
566 #[inline]
568 pub fn name(&self) -> &'static str {
569 NonCustomPropertyId::from(*self).name()
570 }
571
572 #[inline]
574 pub fn inherited(self) -> bool {
575 !LonghandIdSet::reset().contains(self)
576 }
577
578 #[inline]
580 pub fn zoom_dependent(self) -> bool {
581 LonghandIdSet::zoom_dependent().contains(self)
582 }
583
584 #[inline]
587 pub fn ignored_when_document_colors_disabled(self) -> bool {
588 LonghandIdSet::ignored_when_colors_disabled().contains(self)
589 }
590
591 pub fn is_or_is_longhand_of(self, non_custom: NonCustomPropertyId) -> bool {
593 match non_custom.longhand_or_shorthand() {
594 Ok(lh) => self == lh,
595 Err(sh) => self.is_longhand_of(sh),
596 }
597 }
598
599 pub fn is_longhand_of(self, shorthand: ShorthandId) -> bool {
601 self.shorthands().any(|s| s == shorthand)
602 }
603
604 #[inline]
606 pub fn is_animatable(self) -> bool {
607 NonCustomPropertyId::from(self).is_animatable()
608 }
609
610 #[inline]
612 pub fn is_discrete_animatable(self) -> bool {
613 LonghandIdSet::discrete_animatable().contains(self)
614 }
615
616 #[cfg(feature = "gecko")]
618 #[inline]
619 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
620 NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
621 }
622
623 #[cfg(feature = "gecko")]
624 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
626 NonCustomPropertyId::from_noncustomcsspropertyid(id)?
627 .unaliased()
628 .as_longhand()
629 }
630
631 #[inline]
633 pub fn is_logical(self) -> bool {
634 LonghandIdSet::logical().contains(self)
635 }
636}
637
638impl ToCss for ShorthandId {
639 #[inline]
640 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
641 where
642 W: Write,
643 {
644 dest.write_str(self.name())
645 }
646}
647
648impl ShorthandId {
649 #[inline]
651 pub fn name(&self) -> &'static str {
652 NonCustomPropertyId::from(*self).name()
653 }
654
655 #[cfg(feature = "gecko")]
657 #[inline]
658 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
659 NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
660 }
661
662 #[cfg(feature = "gecko")]
664 #[inline]
665 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
666 NonCustomPropertyId::from_noncustomcsspropertyid(id)?
667 .unaliased()
668 .as_shorthand()
669 }
670
671 pub fn get_shorthand_appendable_value<'a, 'b: 'a>(
675 self,
676 declarations: &'a [&'b PropertyDeclaration],
677 ) -> Option<AppendableValue<'a, 'b>> {
678 let first_declaration = declarations.get(0)?;
679 let rest = || declarations.iter().skip(1);
680
681 if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
683 if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
684 return Some(AppendableValue::Css(css));
685 }
686 return None;
687 }
688
689 if let Some(keyword) = first_declaration.get_css_wide_keyword() {
691 if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {
692 return Some(AppendableValue::Css(keyword.to_str()));
693 }
694 return None;
695 }
696
697 if self == ShorthandId::All {
698 return None;
700 }
701
702 if declarations
704 .iter()
705 .all(|d| d.may_serialize_as_part_of_shorthand())
706 {
707 return Some(AppendableValue::DeclarationsForShorthand(
708 self,
709 declarations,
710 ));
711 }
712
713 None
714 }
715
716 #[inline]
718 pub fn is_legacy_shorthand(self) -> bool {
719 self.flags().contains(PropertyFlags::IS_LEGACY_SHORTHAND)
720 }
721}
722
723fn parse_non_custom_property_declaration_value_into<'i>(
724 declarations: &mut SourcePropertyDeclaration,
725 context: &ParserContext,
726 input: &mut Parser<'i, '_>,
727 start: &cssparser::ParserState,
728 parse_entirely_into: impl FnOnce(
729 &mut SourcePropertyDeclaration,
730 &mut Parser<'i, '_>,
731 ) -> Result<(), ParseError<'i>>,
732 parsed_wide_keyword: impl FnOnce(&mut SourcePropertyDeclaration, CSSWideKeyword),
733 parsed_custom: impl FnOnce(&mut SourcePropertyDeclaration, custom_properties::VariableValue),
734) -> Result<(), ParseError<'i>> {
735 let mut starts_with_curly_block = false;
736 if let Ok(token) = input.next() {
737 match token {
738 cssparser::Token::Ident(ref ident) => match CSSWideKeyword::from_ident(ident) {
739 Ok(wk) => {
740 if input.expect_exhausted().is_ok() {
741 return Ok(parsed_wide_keyword(declarations, wk));
742 }
743 },
744 Err(()) => {},
745 },
746 cssparser::Token::CurlyBracketBlock => {
747 starts_with_curly_block = true;
748 },
749 _ => {},
750 }
751 };
752
753 input.reset(&start);
754 input.look_for_arbitrary_substitution_functions(
755 if static_prefs::pref!("layout.css.attr.enabled") {
756 &["var", "env", "attr"]
757 } else {
758 &["var", "env"]
759 },
760 );
761
762 let err = match parse_entirely_into(declarations, input) {
763 Ok(()) => {
764 input.seen_arbitrary_substitution_functions();
765 return Ok(());
766 },
767 Err(e) => e,
768 };
769
770 let start_pos = start.position();
772 let mut at_start = start_pos == input.position();
773 let mut invalid = false;
774 while let Ok(token) = input.next() {
775 if matches!(token, cssparser::Token::CurlyBracketBlock) {
776 if !starts_with_curly_block || !at_start {
777 invalid = true;
778 break;
779 }
780 } else if starts_with_curly_block {
781 invalid = true;
782 break;
783 }
784 at_start = false;
785 }
786 if !input.seen_arbitrary_substitution_functions() || invalid {
787 return Err(err);
788 }
789 input.reset(start);
790 let value = custom_properties::VariableValue::parse(
791 input,
792 Some(&context.namespaces.prefixes),
793 &context.url_data,
794 )?;
795 parsed_custom(declarations, value);
796 Ok(())
797}
798
799impl PropertyDeclaration {
800 fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option<&str> {
801 match *self {
802 PropertyDeclaration::WithVariables(ref declaration) => {
803 let s = declaration.value.from_shorthand?;
804 if s != shorthand {
805 return None;
806 }
807 Some(&*declaration.value.variable_value.css)
808 },
809 _ => None,
810 }
811 }
812
813 #[inline]
815 pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
816 Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
817 }
818
819 #[inline]
821 pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
822 match *self {
823 PropertyDeclaration::CSSWideKeyword(ref declaration) => Some(declaration.keyword),
824 _ => None,
825 }
826 }
827
828 pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
841 match *self {
842 PropertyDeclaration::CSSWideKeyword(..) | PropertyDeclaration::WithVariables(..) => {
843 false
844 },
845 PropertyDeclaration::Custom(..) => {
846 unreachable!("Serializing a custom property as part of shorthand?")
847 },
848 _ => true,
849 }
850 }
851
852 pub fn is_animatable(&self) -> bool {
854 self.id().is_animatable()
855 }
856
857 pub fn is_custom(&self) -> bool {
860 matches!(*self, PropertyDeclaration::Custom(..))
861 }
862
863 pub fn parse_into<'i, 't>(
874 declarations: &mut SourcePropertyDeclaration,
875 id: PropertyId,
876 context: &ParserContext,
877 input: &mut Parser<'i, 't>,
878 ) -> Result<(), ParseError<'i>> {
879 assert!(declarations.is_empty());
880 debug_assert!(id.allowed_in(context), "{:?}", id);
881 input.skip_whitespace();
882
883 let start = input.state();
884 let non_custom_id = match id {
885 PropertyId::Custom(property_name) => {
886 let value = match input.try_parse(CSSWideKeyword::parse) {
887 Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
888 Err(()) => CustomDeclarationValue::Unparsed(Arc::new(
889 custom_properties::VariableValue::parse(
890 input,
891 Some(&context.namespaces.prefixes),
892 &context.url_data,
893 )?,
894 )),
895 };
896 declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
897 name: property_name,
898 value,
899 }));
900 return Ok(());
901 },
902 PropertyId::NonCustom(id) => id,
903 };
904 match non_custom_id.longhand_or_shorthand() {
905 Ok(longhand_id) => {
906 parse_non_custom_property_declaration_value_into(
907 declarations,
908 context,
909 input,
910 &start,
911 |declarations, input| {
912 let decl = input
913 .parse_entirely(|input| longhand_id.parse_value(context, input))?;
914 declarations.push(decl);
915 Ok(())
916 },
917 |declarations, wk| {
918 declarations.push(PropertyDeclaration::css_wide_keyword(longhand_id, wk));
919 },
920 |declarations, variable_value| {
921 declarations.push(PropertyDeclaration::WithVariables(VariableDeclaration {
922 id: longhand_id,
923 value: Arc::new(UnparsedValue {
924 variable_value,
925 from_shorthand: None,
926 }),
927 }))
928 },
929 )?;
930 },
931 Err(shorthand_id) => {
932 parse_non_custom_property_declaration_value_into(
933 declarations,
934 context,
935 input,
936 &start,
937 |declarations, input| shorthand_id.parse_into(declarations, context, input),
940 |declarations, wk| {
941 if shorthand_id == ShorthandId::All {
942 declarations.all_shorthand = AllShorthand::CSSWideKeyword(wk)
943 } else {
944 for longhand in shorthand_id.longhands() {
945 declarations
946 .push(PropertyDeclaration::css_wide_keyword(longhand, wk));
947 }
948 }
949 },
950 |declarations, variable_value| {
951 let unparsed = Arc::new(UnparsedValue {
952 variable_value,
953 from_shorthand: Some(shorthand_id),
954 });
955 if shorthand_id == ShorthandId::All {
956 declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
957 } else {
958 for id in shorthand_id.longhands() {
959 declarations.push(PropertyDeclaration::WithVariables(
960 VariableDeclaration {
961 id,
962 value: unparsed.clone(),
963 },
964 ))
965 }
966 }
967 },
968 )?;
969 },
970 }
971 if let Some(use_counters) = context.use_counters {
972 use_counters.non_custom_properties.record(non_custom_id);
973 }
974 Ok(())
975 }
976}
977
978#[derive(Clone, Debug, PartialEq, Eq, Hash)]
980pub enum OwnedPropertyDeclarationId {
981 Longhand(LonghandId),
983 Custom(custom_properties::Name),
985}
986
987impl OwnedPropertyDeclarationId {
988 #[inline]
990 pub fn is_logical(&self) -> bool {
991 self.as_borrowed().is_logical()
992 }
993
994 #[inline]
996 pub fn as_borrowed(&self) -> PropertyDeclarationId<'_> {
997 match self {
998 Self::Longhand(id) => PropertyDeclarationId::Longhand(*id),
999 Self::Custom(name) => PropertyDeclarationId::Custom(name),
1000 }
1001 }
1002
1003 #[cfg(feature = "gecko")]
1005 #[inline]
1006 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
1007 Some(match PropertyId::from_gecko_css_property_id(property)? {
1008 PropertyId::Custom(name) => Self::Custom(name),
1009 PropertyId::NonCustom(id) => Self::Longhand(id.as_longhand()?),
1010 })
1011 }
1012}
1013
1014#[derive(Clone, Copy, Debug, PartialEq, MallocSizeOf)]
1017pub enum PropertyDeclarationId<'a> {
1018 Longhand(LonghandId),
1020 Custom(&'a custom_properties::Name),
1022}
1023
1024impl<'a> ToCss for PropertyDeclarationId<'a> {
1025 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1026 where
1027 W: Write,
1028 {
1029 match *self {
1030 PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
1031 PropertyDeclarationId::Custom(name) => {
1032 dest.write_str("--")?;
1033 serialize_atom_name(name, dest)
1034 },
1035 }
1036 }
1037}
1038
1039impl<'a> PropertyDeclarationId<'a> {
1040 #[inline(always)]
1042 pub fn flags(&self) -> PropertyFlags {
1043 match self {
1044 Self::Longhand(id) => id.flags(),
1045 Self::Custom(_) => PropertyFlags::empty(),
1046 }
1047 }
1048
1049 pub fn to_owned(&self) -> OwnedPropertyDeclarationId {
1051 match self {
1052 PropertyDeclarationId::Longhand(id) => OwnedPropertyDeclarationId::Longhand(*id),
1053 PropertyDeclarationId::Custom(name) => {
1054 OwnedPropertyDeclarationId::Custom((*name).clone())
1055 },
1056 }
1057 }
1058
1059 pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
1062 match *self {
1063 PropertyDeclarationId::Longhand(id) => match *other {
1064 PropertyId::NonCustom(non_custom_id) => id.is_or_is_longhand_of(non_custom_id),
1065 PropertyId::Custom(_) => false,
1066 },
1067 PropertyDeclarationId::Custom(name) => {
1068 matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
1069 },
1070 }
1071 }
1072
1073 pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
1076 match *self {
1077 PropertyDeclarationId::Longhand(ref id) => id.is_longhand_of(shorthand),
1078 _ => false,
1079 }
1080 }
1081
1082 pub fn name(&self) -> Cow<'static, str> {
1084 match *self {
1085 PropertyDeclarationId::Longhand(id) => id.name().into(),
1086 PropertyDeclarationId::Custom(name) => {
1087 let mut s = String::new();
1088 write!(&mut s, "--{}", name).unwrap();
1089 s.into()
1090 },
1091 }
1092 }
1093
1094 #[inline]
1096 pub fn as_longhand(&self) -> Option<LonghandId> {
1097 match *self {
1098 PropertyDeclarationId::Longhand(id) => Some(id),
1099 _ => None,
1100 }
1101 }
1102
1103 #[inline]
1105 pub fn is_logical(&self) -> bool {
1106 match self {
1107 PropertyDeclarationId::Longhand(id) => id.is_logical(),
1108 PropertyDeclarationId::Custom(_) => false,
1109 }
1110 }
1111
1112 #[inline]
1117 pub fn to_physical(&self, wm: WritingMode) -> Self {
1118 match self {
1119 Self::Longhand(id) => Self::Longhand(id.to_physical(wm)),
1120 Self::Custom(_) => self.clone(),
1121 }
1122 }
1123
1124 #[inline]
1126 pub fn is_animatable(&self) -> bool {
1127 match self {
1128 Self::Longhand(id) => id.is_animatable(),
1129 Self::Custom(_) => true,
1130 }
1131 }
1132
1133 #[inline]
1135 pub fn is_discrete_animatable(&self) -> bool {
1136 match self {
1137 Self::Longhand(longhand) => longhand.is_discrete_animatable(),
1138 Self::Custom(_) => true,
1140 }
1141 }
1142
1143 #[cfg(feature = "gecko")]
1146 #[inline]
1147 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
1148 match self {
1149 PropertyDeclarationId::Longhand(id) => id.to_noncustomcsspropertyid(),
1150 PropertyDeclarationId::Custom(_) => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1151 }
1152 }
1153
1154 #[cfg(feature = "gecko")]
1159 #[inline]
1160 pub fn to_gecko_css_property_id(&self) -> CSSPropertyId {
1161 match self {
1162 Self::Longhand(id) => CSSPropertyId {
1163 mId: id.to_noncustomcsspropertyid(),
1164 mCustomName: RefPtr::null(),
1165 },
1166 Self::Custom(name) => {
1167 let mut property_id = CSSPropertyId {
1168 mId: NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1169 mCustomName: RefPtr::null(),
1170 };
1171 property_id.mCustomName.mRawPtr = (*name).clone().into_addrefed();
1172 property_id
1173 },
1174 }
1175 }
1176}
1177
1178#[derive(Clone, PartialEq, Default)]
1180pub struct NonCustomPropertyIdSet {
1181 storage: [u32; ((property_counts::NON_CUSTOM as usize) - 1 + 32) / 32],
1182}
1183
1184impl NonCustomPropertyIdSet {
1185 pub fn new() -> Self {
1187 Self {
1188 storage: Default::default(),
1189 }
1190 }
1191
1192 #[inline]
1194 pub fn insert(&mut self, id: NonCustomPropertyId) {
1195 let bit = id.0 as usize;
1196 self.storage[bit / 32] |= 1 << (bit % 32);
1197 }
1198
1199 #[inline]
1201 pub fn contains(&self, id: NonCustomPropertyId) -> bool {
1202 let bit = id.0 as usize;
1203 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1204 }
1205}
1206
1207#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
1209pub struct LonghandIdSet {
1210 storage: [u32; ((property_counts::LONGHANDS as usize) - 1 + 32) / 32],
1211}
1212
1213to_shmem::impl_trivial_to_shmem!(LonghandIdSet);
1214
1215impl LonghandIdSet {
1216 #[inline]
1218 pub fn new() -> Self {
1219 Self {
1220 storage: Default::default(),
1221 }
1222 }
1223
1224 pub fn iter(&self) -> LonghandIdSetIterator<'_> {
1226 LonghandIdSetIterator {
1227 chunks: &self.storage,
1228 cur_chunk: 0,
1229 cur_bit: 0,
1230 }
1231 }
1232
1233 pub fn contains_all(&self, other: &Self) -> bool {
1236 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1237 if (*self_cell & *other_cell) != *other_cell {
1238 return false;
1239 }
1240 }
1241 true
1242 }
1243
1244 pub fn contains_any(&self, other: &Self) -> bool {
1246 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1247 if (*self_cell & *other_cell) != 0 {
1248 return true;
1249 }
1250 }
1251 false
1252 }
1253
1254 #[inline]
1256 pub fn remove_all(&mut self, other: &Self) {
1257 for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
1258 *self_cell &= !*other_cell;
1259 }
1260 }
1261
1262 #[inline]
1264 pub fn contains(&self, id: LonghandId) -> bool {
1265 let bit = id as usize;
1266 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1267 }
1268
1269 #[inline]
1271 pub fn contains_any_reset(&self) -> bool {
1272 self.contains_any(Self::reset())
1273 }
1274
1275 #[inline]
1277 pub fn insert(&mut self, id: LonghandId) {
1278 let bit = id as usize;
1279 self.storage[bit / 32] |= 1 << (bit % 32);
1280 }
1281
1282 #[inline]
1284 pub fn remove(&mut self, id: LonghandId) {
1285 let bit = id as usize;
1286 self.storage[bit / 32] &= !(1 << (bit % 32));
1287 }
1288
1289 #[inline]
1291 pub fn clear(&mut self) {
1292 for cell in &mut self.storage {
1293 *cell = 0
1294 }
1295 }
1296
1297 #[inline]
1299 pub fn is_empty(&self) -> bool {
1300 self.storage.iter().all(|c| *c == 0)
1301 }
1302}
1303
1304pub struct LonghandIdSetIterator<'a> {
1306 chunks: &'a [u32],
1307 cur_chunk: u32,
1308 cur_bit: u32, }
1310
1311impl<'a> Iterator for LonghandIdSetIterator<'a> {
1312 type Item = LonghandId;
1313
1314 fn next(&mut self) -> Option<Self::Item> {
1315 loop {
1316 debug_assert!(self.cur_bit < 32);
1317 let cur_chunk = self.cur_chunk;
1318 let cur_bit = self.cur_bit;
1319 let chunk = *self.chunks.get(cur_chunk as usize)?;
1320 let next_bit = (chunk >> cur_bit).trailing_zeros();
1321 if next_bit == 32 {
1322 self.cur_bit = 0;
1324 self.cur_chunk += 1;
1325 continue;
1326 }
1327 debug_assert!(cur_bit + next_bit < 32);
1328 let longhand_id = cur_chunk * 32 + cur_bit + next_bit;
1329 debug_assert!(longhand_id as usize <= property_counts::LONGHANDS);
1330 let id: LonghandId = unsafe { mem::transmute(longhand_id as u16) };
1331 self.cur_bit += next_bit + 1;
1332 if self.cur_bit == 32 {
1333 self.cur_bit = 0;
1334 self.cur_chunk += 1;
1335 }
1336 return Some(id);
1337 }
1338 }
1339}
1340
1341pub type SubpropertiesVec<T> = ArrayVec<T, { property_counts::MAX_SHORTHAND_EXPANDED }>;
1343
1344#[derive(Default)]
1348pub struct SourcePropertyDeclaration {
1349 pub declarations: SubpropertiesVec<PropertyDeclaration>,
1351 pub all_shorthand: AllShorthand,
1353}
1354
1355#[cfg(feature = "gecko")]
1358size_of_test!(SourcePropertyDeclaration, 632);
1359#[cfg(feature = "servo")]
1360size_of_test!(SourcePropertyDeclaration, 568);
1361
1362impl SourcePropertyDeclaration {
1363 #[inline]
1365 pub fn with_one(decl: PropertyDeclaration) -> Self {
1366 let mut result = Self::default();
1367 result.declarations.push(decl);
1368 result
1369 }
1370
1371 pub fn drain(&mut self) -> SourcePropertyDeclarationDrain<'_> {
1373 SourcePropertyDeclarationDrain {
1374 declarations: self.declarations.drain(..),
1375 all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
1376 }
1377 }
1378
1379 pub fn clear(&mut self) {
1381 self.declarations.clear();
1382 self.all_shorthand = AllShorthand::NotSet;
1383 }
1384
1385 pub fn is_empty(&self) -> bool {
1387 self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
1388 }
1389
1390 pub fn push(&mut self, declaration: PropertyDeclaration) {
1392 let _result = self.declarations.try_push(declaration);
1393 debug_assert!(_result.is_ok());
1394 }
1395}
1396
1397pub struct SourcePropertyDeclarationDrain<'a> {
1399 pub declarations:
1401 ArrayVecDrain<'a, PropertyDeclaration, { property_counts::MAX_SHORTHAND_EXPANDED }>,
1402 pub all_shorthand: AllShorthand,
1404}
1405
1406#[derive(Debug, Eq, PartialEq, ToShmem)]
1408pub struct UnparsedValue {
1409 pub(super) variable_value: custom_properties::VariableValue,
1411 from_shorthand: Option<ShorthandId>,
1413}
1414
1415impl ToCss for UnparsedValue {
1416 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1417 where
1418 W: Write,
1419 {
1420 if self.from_shorthand.is_none() {
1422 self.variable_value.to_css(dest)?;
1423 }
1424 Ok(())
1425 }
1426}
1427
1428pub type ShorthandsWithPropertyReferencesCache =
1435 FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
1436
1437impl UnparsedValue {
1438 fn substitute_variables<'cache>(
1439 &self,
1440 longhand_id: LonghandId,
1441 custom_properties: &ComputedCustomProperties,
1442 stylist: &Stylist,
1443 computed_context: &computed::Context,
1444 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
1445 attribute_tracker: &mut AttributeTracker,
1446 ) -> Cow<'cache, PropertyDeclaration> {
1447 let invalid_at_computed_value_time = || {
1448 let keyword = if longhand_id.inherited() {
1449 CSSWideKeyword::Inherit
1450 } else {
1451 CSSWideKeyword::Initial
1452 };
1453 Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
1454 };
1455
1456 if computed_context
1457 .builder
1458 .invalid_non_custom_properties
1459 .contains(longhand_id)
1460 {
1461 return invalid_at_computed_value_time();
1462 }
1463
1464 if let Some(shorthand_id) = self.from_shorthand {
1465 let key = (shorthand_id, longhand_id);
1466 if shorthand_cache.contains_key(&key) {
1467 return Cow::Borrowed(&shorthand_cache[&key]);
1472 }
1473 }
1474
1475 let css = match custom_properties::substitute(
1476 &self.variable_value,
1477 custom_properties,
1478 stylist,
1479 computed_context,
1480 attribute_tracker,
1481 ) {
1482 Ok(css) => css,
1483 Err(..) => return invalid_at_computed_value_time(),
1484 };
1485
1486 let context = ParserContext::new(
1497 Origin::Author,
1498 &self.variable_value.url_data,
1499 None,
1500 ParsingMode::DEFAULT,
1501 computed_context.quirks_mode,
1502 Default::default(),
1503 None,
1504 None,
1505 );
1506
1507 let mut input = ParserInput::new(&css);
1508 let mut input = Parser::new(&mut input);
1509 input.skip_whitespace();
1510
1511 if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
1512 return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
1513 }
1514
1515 let shorthand = match self.from_shorthand {
1516 None => {
1517 return match input.parse_entirely(|input| longhand_id.parse_value(&context, input))
1518 {
1519 Ok(decl) => Cow::Owned(decl),
1520 Err(..) => invalid_at_computed_value_time(),
1521 }
1522 },
1523 Some(shorthand) => shorthand,
1524 };
1525
1526 let mut decls = SourcePropertyDeclaration::default();
1527 if shorthand
1529 .parse_into(&mut decls, &context, &mut input)
1530 .is_err()
1531 {
1532 return invalid_at_computed_value_time();
1533 }
1534
1535 for declaration in decls.declarations.drain(..) {
1536 let longhand = declaration.id().as_longhand().unwrap();
1537 if longhand.is_logical() {
1538 let writing_mode = computed_context.builder.writing_mode;
1539 shorthand_cache.insert(
1540 (shorthand, longhand.to_physical(writing_mode)),
1541 declaration.clone(),
1542 );
1543 }
1544 shorthand_cache.insert((shorthand, longhand), declaration);
1545 }
1546
1547 let key = (shorthand, longhand_id);
1548 match shorthand_cache.get(&key) {
1549 Some(decl) => Cow::Borrowed(decl),
1550 None => invalid_at_computed_value_time(),
1562 }
1563 }
1564}
1565pub enum AllShorthand {
1567 NotSet,
1569 CSSWideKeyword(CSSWideKeyword),
1571 WithVariables(Arc<UnparsedValue>),
1573}
1574
1575impl Default for AllShorthand {
1576 fn default() -> Self {
1577 Self::NotSet
1578 }
1579}
1580
1581impl AllShorthand {
1582 #[inline]
1584 pub fn declarations(&self) -> AllShorthandDeclarationIterator<'_> {
1585 AllShorthandDeclarationIterator {
1586 all_shorthand: self,
1587 longhands: ShorthandId::All.longhands(),
1588 }
1589 }
1590}
1591
1592pub struct AllShorthandDeclarationIterator<'a> {
1594 all_shorthand: &'a AllShorthand,
1595 longhands: NonCustomPropertyIterator<LonghandId>,
1596}
1597
1598impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
1599 type Item = PropertyDeclaration;
1600
1601 #[inline]
1602 fn next(&mut self) -> Option<Self::Item> {
1603 match *self.all_shorthand {
1604 AllShorthand::NotSet => None,
1605 AllShorthand::CSSWideKeyword(ref keyword) => Some(
1606 PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword),
1607 ),
1608 AllShorthand::WithVariables(ref unparsed) => {
1609 Some(PropertyDeclaration::WithVariables(VariableDeclaration {
1610 id: self.longhands.next()?,
1611 value: unparsed.clone(),
1612 }))
1613 },
1614 }
1615 }
1616}
1617
1618pub struct NonCustomPropertyIterator<Item: 'static> {
1621 filter: bool,
1622 iter: std::slice::Iter<'static, Item>,
1623}
1624
1625impl<Item> Iterator for NonCustomPropertyIterator<Item>
1626where
1627 Item: 'static + Copy + Into<NonCustomPropertyId>,
1628{
1629 type Item = Item;
1630
1631 fn next(&mut self) -> Option<Self::Item> {
1632 loop {
1633 let id = *self.iter.next()?;
1634 if !self.filter || id.into().enabled_for_all_content() {
1635 return Some(id);
1636 }
1637 }
1638 }
1639}
1640
1641pub struct TransitionPropertyIterator<'a> {
1643 style: &'a ComputedValues,
1644 index_range: core::ops::Range<usize>,
1645 longhand_iterator: Option<NonCustomPropertyIterator<LonghandId>>,
1646}
1647
1648impl<'a> TransitionPropertyIterator<'a> {
1649 pub fn from_style(style: &'a ComputedValues) -> Self {
1651 Self {
1652 style,
1653 index_range: 0..style.get_ui().transition_property_count(),
1654 longhand_iterator: None,
1655 }
1656 }
1657}
1658
1659pub struct TransitionPropertyIteration {
1661 pub property: OwnedPropertyDeclarationId,
1663 pub index: usize,
1666}
1667
1668impl<'a> Iterator for TransitionPropertyIterator<'a> {
1669 type Item = TransitionPropertyIteration;
1670
1671 fn next(&mut self) -> Option<Self::Item> {
1672 use crate::values::computed::TransitionProperty;
1673 loop {
1674 if let Some(ref mut longhand_iterator) = self.longhand_iterator {
1675 if let Some(longhand_id) = longhand_iterator.next() {
1676 return Some(TransitionPropertyIteration {
1677 property: OwnedPropertyDeclarationId::Longhand(longhand_id),
1678 index: self.index_range.start - 1,
1679 });
1680 }
1681 self.longhand_iterator = None;
1682 }
1683
1684 let index = self.index_range.next()?;
1685 match self.style.get_ui().transition_property_at(index) {
1686 TransitionProperty::NonCustom(id) => {
1687 match id.longhand_or_shorthand() {
1688 Ok(longhand_id) => {
1689 return Some(TransitionPropertyIteration {
1690 property: OwnedPropertyDeclarationId::Longhand(longhand_id),
1691 index,
1692 });
1693 },
1694 Err(shorthand_id) => {
1695 self.longhand_iterator = Some(shorthand_id.longhands());
1699 },
1700 }
1701 },
1702 TransitionProperty::Custom(name) => {
1703 return Some(TransitionPropertyIteration {
1704 property: OwnedPropertyDeclarationId::Custom(name),
1705 index,
1706 })
1707 },
1708 TransitionProperty::Unsupported(..) => {},
1709 }
1710 }
1711 }
1712}