1pub mod cascade;
8pub mod declaration_block;
9pub mod shorthands;
10
11pub use self::cascade::*;
12pub use self::declaration_block::*;
13pub use self::generated::*;
14
15#[macro_use]
18#[allow(unsafe_code)]
19#[deny(missing_docs)]
20pub mod generated {
21 include!(concat!(env!("OUT_DIR"), "/properties.rs"));
22}
23
24use crate::applicable_declarations::RevertKind;
25use crate::custom_properties::{self, ComputedSubstitutionFunctions, SubstitutionResult};
26use crate::derives::*;
27use crate::dom::AttributeTracker;
28#[cfg(feature = "gecko")]
29use crate::gecko_bindings::structs::{CSSPropertyId, NonCustomCSSPropertyId, RefPtr};
30use crate::logical_geometry::WritingMode;
31use crate::parser::ParserContext;
32use crate::stylesheets::CssRuleType;
33use crate::stylesheets::Origin;
34use crate::stylist::Stylist;
35use crate::typed_om::{ToTyped, TypedValue};
36use crate::values::{computed, serialize_atom_name};
37use arrayvec::{ArrayVec, Drain as ArrayVecDrain};
38use cssparser::{match_ignore_ascii_case, Parser};
39use rustc_hash::FxHashMap;
40use servo_arc::Arc;
41use std::{
42 borrow::Cow,
43 fmt::{self, Write},
44 mem,
45};
46use style_traits::{
47 CssString, CssWriter, KeywordsCollectFn, ParseError, ParsingMode, SpecifiedValueInfo, ToCss,
48};
49use thin_vec::ThinVec;
50
51bitflags! {
52 #[derive(Clone, Copy)]
54 pub struct PropertyFlags: u16 {
55 const APPLIES_TO_FIRST_LETTER = 1 << 1;
57 const APPLIES_TO_FIRST_LINE = 1 << 2;
59 const APPLIES_TO_PLACEHOLDER = 1 << 3;
61 const APPLIES_TO_CUE = 1 << 4;
63 const APPLIES_TO_MARKER = 1 << 5;
65 const IS_LEGACY_SHORTHAND = 1 << 6;
69 const ALLOWS_DISABLED_SUBPROPERTIES = 1 << 7;
71
72 const CAN_ANIMATE_ON_COMPOSITOR = 0;
78 const SCROLL_LINKED_EFFECTIVE = 0;
80 const AFFECTS_LAYOUT = 0;
82 #[allow(missing_docs)]
83 const AFFECTS_OVERFLOW = 0;
84 #[allow(missing_docs)]
85 const AFFECTS_PAINT = 0;
86 }
87}
88
89#[derive(
91 Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
92)]
93pub enum CSSWideKeyword {
94 Initial,
96 Inherit,
98 Unset,
100 Revert,
102 RevertLayer,
104 RevertRule,
106}
107
108impl CSSWideKeyword {
109 pub fn to_str(&self) -> &'static str {
111 match *self {
112 Self::Initial => "initial",
113 Self::Inherit => "inherit",
114 Self::Unset => "unset",
115 Self::Revert => "revert",
116 Self::RevertLayer => "revert-layer",
117 Self::RevertRule => "revert-rule",
118 }
119 }
120
121 pub fn from_ident(ident: &str) -> Result<Self, ()> {
123 Ok(match_ignore_ascii_case! { ident,
124 "initial" => Self::Initial,
125 "inherit" => Self::Inherit,
126 "unset" => Self::Unset,
127 "revert" => Self::Revert,
128 "revert-layer" => Self::RevertLayer,
129 "revert-rule" if crate::pref!("layout.css.revert-rule.enabled") => Self::RevertRule,
130 _ => return Err(()),
131 })
132 }
133
134 pub fn parse(input: &mut Parser) -> Result<Self, ()> {
136 let keyword = {
137 let ident = input.expect_ident().map_err(|_| ())?;
138 Self::from_ident(ident)?
139 };
140 input.expect_exhausted().map_err(|_| ())?;
141 Ok(keyword)
142 }
143
144 pub fn revert_kind(self) -> Option<RevertKind> {
146 Some(match self {
147 Self::Initial | Self::Inherit | Self::Unset => return None,
148 Self::Revert => RevertKind::Origin,
149 Self::RevertLayer => RevertKind::Layer,
150 Self::RevertRule => RevertKind::Rule,
151 })
152 }
153}
154
155#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf)]
157pub struct WideKeywordDeclaration {
158 #[css(skip)]
159 id: LonghandId,
160 pub keyword: CSSWideKeyword,
162}
163
164impl ToTyped for WideKeywordDeclaration {
167 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
168 self.keyword.to_typed(dest)
169 }
170}
171
172#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
174pub struct VariableDeclaration {
175 #[css(skip)]
177 id: LonghandId,
178 #[ignore_malloc_size_of = "Arc"]
180 pub value: Arc<UnparsedValue>,
181}
182
183#[derive(Clone, PartialEq, ToCss, ToShmem, ToTyped)]
186pub enum CustomDeclarationValue {
187 Unparsed(Arc<custom_properties::SpecifiedValue>),
189 Parsed(Arc<crate::properties_and_values::value::SpecifiedValue>),
191 CSSWideKeyword(CSSWideKeyword),
193}
194
195#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
197pub struct CustomDeclaration {
198 #[css(skip)]
200 pub name: custom_properties::Name,
201 #[ignore_malloc_size_of = "Arc"]
203 pub value: CustomDeclarationValue,
204}
205
206impl fmt::Debug for PropertyDeclaration {
207 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
208 self.id().to_css(&mut CssWriter::new(f))?;
209 f.write_str(": ")?;
210
211 let mut s = CssString::new();
215 self.to_css(&mut s)?;
216 write!(f, "{}", s)
217 }
218}
219
220#[derive(
222 Clone, Copy, Debug, PartialEq, Eq, Hash, ToComputedValue, ToResolvedValue, ToShmem, MallocSizeOf,
223)]
224#[repr(C)]
225pub struct NonCustomPropertyId(u16);
226
227impl ToCss for NonCustomPropertyId {
228 #[inline]
229 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
230 where
231 W: Write,
232 {
233 dest.write_str(self.name())
234 }
235}
236
237impl NonCustomPropertyId {
238 pub fn bit(self) -> usize {
240 self.0 as usize
241 }
242
243 #[cfg(feature = "gecko")]
245 #[inline]
246 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
247 unsafe { mem::transmute(self.0) }
249 }
250
251 #[cfg(feature = "gecko")]
253 #[inline]
254 pub fn from_noncustomcsspropertyid(prop: NonCustomCSSPropertyId) -> Option<Self> {
255 let prop = prop as u16;
256 if prop >= property_counts::NON_CUSTOM as u16 {
257 return None;
258 }
259 Some(NonCustomPropertyId(prop))
261 }
262
263 pub fn unaliased(self) -> Self {
265 let Some(alias_id) = self.as_alias() else {
266 return self;
267 };
268 alias_id.aliased_property()
269 }
270
271 #[inline]
273 pub fn to_property_id(self) -> PropertyId {
274 PropertyId::NonCustom(self)
275 }
276
277 #[inline]
279 pub fn as_longhand(self) -> Option<LonghandId> {
280 if self.0 < property_counts::LONGHANDS as u16 {
281 return Some(unsafe { mem::transmute(self.0) });
282 }
283 None
284 }
285
286 #[inline]
288 pub fn as_shorthand(self) -> Option<ShorthandId> {
289 if self.0 >= property_counts::LONGHANDS as u16
290 && self.0 < property_counts::LONGHANDS_AND_SHORTHANDS as u16
291 {
292 return Some(unsafe { mem::transmute(self.0 - (property_counts::LONGHANDS as u16)) });
293 }
294 None
295 }
296
297 #[inline]
299 pub fn as_alias(self) -> Option<AliasId> {
300 debug_assert!((self.0 as usize) < property_counts::NON_CUSTOM);
301 if self.0 >= property_counts::LONGHANDS_AND_SHORTHANDS as u16 {
302 return Some(unsafe {
303 mem::transmute(self.0 - (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
304 });
305 }
306 None
307 }
308
309 #[inline]
311 pub fn longhand_or_shorthand(self) -> Result<LonghandId, ShorthandId> {
312 let id = self.unaliased();
313 match id.as_longhand() {
314 Some(lh) => Ok(lh),
315 None => Err(id.as_shorthand().unwrap()),
316 }
317 }
318
319 #[inline]
321 pub const fn from_longhand(id: LonghandId) -> Self {
322 Self(id as u16)
323 }
324
325 #[inline]
327 pub const fn from_shorthand(id: ShorthandId) -> Self {
328 Self((id as u16) + (property_counts::LONGHANDS as u16))
329 }
330
331 #[inline]
333 pub const fn from_alias(id: AliasId) -> Self {
334 Self((id as u16) + (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
335 }
336
337 pub fn iter() -> impl Iterator<Item = Self> {
339 (0..property_counts::NON_CUSTOM as u16).map(|index| Self(index))
340 }
341}
342
343impl From<LonghandId> for NonCustomPropertyId {
344 #[inline]
345 fn from(id: LonghandId) -> Self {
346 Self::from_longhand(id)
347 }
348}
349
350impl From<ShorthandId> for NonCustomPropertyId {
351 #[inline]
352 fn from(id: ShorthandId) -> Self {
353 Self::from_shorthand(id)
354 }
355}
356
357impl From<AliasId> for NonCustomPropertyId {
358 #[inline]
359 fn from(id: AliasId) -> Self {
360 Self::from_alias(id)
361 }
362}
363
364#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
367pub enum PropertyId {
368 NonCustom(NonCustomPropertyId),
370 Custom(custom_properties::Name),
372}
373
374impl ToCss for PropertyId {
375 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
376 where
377 W: Write,
378 {
379 match *self {
380 PropertyId::NonCustom(id) => dest.write_str(id.name()),
381 PropertyId::Custom(ref name) => {
382 dest.write_str("--")?;
383 serialize_atom_name(name, dest)
384 },
385 }
386 }
387}
388
389impl PropertyId {
390 #[inline]
392 pub fn longhand_id(&self) -> Option<LonghandId> {
393 self.non_custom_non_alias_id()?.as_longhand()
394 }
395
396 pub fn is_animatable(&self) -> bool {
398 match self {
399 Self::NonCustom(id) => id.is_animatable(),
400 Self::Custom(_) => true,
401 }
402 }
403
404 pub fn parse_unchecked_for_testing(name: &str) -> Result<Self, ()> {
409 Self::parse_unchecked(name, None)
410 }
411
412 #[inline]
415 pub fn parse_enabled_for_all_content(name: &str) -> Result<Self, ()> {
416 let id = Self::parse_unchecked(name, None)?;
417
418 if !id.enabled_for_all_content() {
419 return Err(());
420 }
421
422 Ok(id)
423 }
424
425 #[inline]
428 pub fn parse(name: &str, context: &ParserContext) -> Result<Self, ()> {
429 let id = Self::parse_unchecked(name, context.use_counters)?;
430 if !id.allowed_in(context) {
431 return Err(());
432 }
433 Ok(id)
434 }
435
436 #[inline]
441 pub fn parse_ignoring_rule_type(name: &str, context: &ParserContext) -> Result<Self, ()> {
442 let id = Self::parse_unchecked(name, None)?;
443 if !id.allowed_in_ignoring_rule_type(context) {
444 return Err(());
445 }
446 Ok(id)
447 }
448
449 #[cfg(feature = "gecko")]
451 #[inline]
452 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
453 Some(NonCustomPropertyId::from_noncustomcsspropertyid(id)?.to_property_id())
454 }
455
456 #[cfg(feature = "gecko")]
458 #[inline]
459 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
460 Some(
461 if property.mId == NonCustomCSSPropertyId::eCSSPropertyExtra_variable {
462 debug_assert!(!property.mCustomName.mRawPtr.is_null());
463 Self::Custom(unsafe { crate::Atom::from_raw(property.mCustomName.mRawPtr) })
464 } else {
465 Self::NonCustom(NonCustomPropertyId::from_noncustomcsspropertyid(
466 property.mId,
467 )?)
468 },
469 )
470 }
471
472 #[inline]
474 pub fn is_shorthand(&self) -> bool {
475 self.as_shorthand().is_ok()
476 }
477
478 pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId<'_>> {
481 match *self {
482 Self::NonCustom(id) => match id.longhand_or_shorthand() {
483 Ok(lh) => Err(PropertyDeclarationId::Longhand(lh)),
484 Err(sh) => Ok(sh),
485 },
486 Self::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),
487 }
488 }
489
490 pub fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
492 match *self {
493 Self::Custom(_) => None,
494 Self::NonCustom(id) => Some(id),
495 }
496 }
497
498 fn non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId> {
501 self.non_custom_id().map(NonCustomPropertyId::unaliased)
502 }
503
504 #[inline]
507 pub fn enabled_for_all_content(&self) -> bool {
508 let id = match self.non_custom_id() {
509 None => return true,
511 Some(id) => id,
512 };
513
514 id.enabled_for_all_content()
515 }
516
517 #[cfg(feature = "gecko")]
521 #[inline]
522 pub fn to_noncustomcsspropertyid_resolving_aliases(&self) -> NonCustomCSSPropertyId {
523 match self.non_custom_non_alias_id() {
524 Some(id) => id.to_noncustomcsspropertyid(),
525 None => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
526 }
527 }
528
529 fn allowed_in(&self, context: &ParserContext) -> bool {
530 let id = match self.non_custom_id() {
531 None => {
533 return !context
534 .nesting_context
535 .rule_types
536 .contains(CssRuleType::PositionTry)
537 },
538 Some(id) => id,
539 };
540 id.allowed_in(context)
541 }
542
543 #[inline]
544 fn allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool {
545 let id = match self.non_custom_id() {
546 None => return true,
548 Some(id) => id,
549 };
550 id.allowed_in_ignoring_rule_type(context)
551 }
552
553 pub fn supports_type(&self, ty: u8) -> bool {
556 let id = self.non_custom_non_alias_id();
557 id.map_or(0, |id| id.supported_types()) & ty != 0
558 }
559
560 pub fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
565 if let Some(id) = self.non_custom_non_alias_id() {
566 id.collect_property_completion_keywords(f);
567 }
568 CSSWideKeyword::collect_completion_keywords(f);
569 }
570}
571
572impl ToCss for LonghandId {
573 #[inline]
574 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
575 where
576 W: Write,
577 {
578 dest.write_str(self.name())
579 }
580}
581
582impl fmt::Debug for LonghandId {
583 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
584 formatter.write_str(self.name())
585 }
586}
587
588impl LonghandId {
589 #[inline]
591 pub fn name(&self) -> &'static str {
592 NonCustomPropertyId::from(*self).name()
593 }
594
595 #[inline]
597 pub fn inherited(self) -> bool {
598 !LonghandIdSet::reset().contains(self)
599 }
600
601 #[inline]
603 pub fn zoom_dependent(self) -> bool {
604 LonghandIdSet::zoom_dependent().contains(self)
605 }
606
607 #[inline]
610 pub fn ignored_when_document_colors_disabled(self) -> bool {
611 LonghandIdSet::ignored_when_colors_disabled().contains(self)
612 }
613
614 pub fn is_or_is_longhand_of(self, non_custom: NonCustomPropertyId) -> bool {
616 match non_custom.longhand_or_shorthand() {
617 Ok(lh) => self == lh,
618 Err(sh) => self.is_longhand_of(sh),
619 }
620 }
621
622 pub fn is_longhand_of(self, shorthand: ShorthandId) -> bool {
624 self.shorthands().any(|s| s == shorthand)
625 }
626
627 #[inline]
629 pub fn is_animatable(self) -> bool {
630 NonCustomPropertyId::from(self).is_animatable()
631 }
632
633 #[inline]
635 pub fn is_discrete_animatable(self) -> bool {
636 LonghandIdSet::discrete_animatable().contains(self) || self == LonghandId::Display
638 }
639
640 #[cfg(feature = "gecko")]
642 #[inline]
643 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
644 NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
645 }
646
647 #[cfg(feature = "gecko")]
648 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
650 NonCustomPropertyId::from_noncustomcsspropertyid(id)?
651 .unaliased()
652 .as_longhand()
653 }
654
655 #[inline]
657 pub fn is_logical(self) -> bool {
658 LonghandIdSet::logical().contains(self)
659 }
660}
661
662impl ToCss for ShorthandId {
663 #[inline]
664 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
665 where
666 W: Write,
667 {
668 dest.write_str(self.name())
669 }
670}
671
672impl ShorthandId {
673 #[inline]
675 pub fn name(&self) -> &'static str {
676 NonCustomPropertyId::from(*self).name()
677 }
678
679 #[cfg(feature = "gecko")]
681 #[inline]
682 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
683 NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
684 }
685
686 #[cfg(feature = "gecko")]
688 #[inline]
689 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
690 NonCustomPropertyId::from_noncustomcsspropertyid(id)?
691 .unaliased()
692 .as_shorthand()
693 }
694
695 pub fn get_shorthand_appendable_value<'a, 'b: 'a>(
699 self,
700 declarations: &'a [&'b PropertyDeclaration],
701 ) -> Option<AppendableValue<'a, 'b>> {
702 let first_declaration = declarations.first()?;
703 let rest = || declarations.iter().skip(1);
704
705 if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
707 if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
708 return Some(AppendableValue::Css(css));
709 }
710 return None;
711 }
712
713 if let Some(keyword) = first_declaration.get_css_wide_keyword() {
715 if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {
716 return Some(AppendableValue::Css(keyword.to_str()));
717 }
718 return None;
719 }
720
721 if self == ShorthandId::All {
722 return None;
724 }
725
726 if declarations
728 .iter()
729 .all(|d| d.may_serialize_as_part_of_shorthand())
730 {
731 return Some(AppendableValue::DeclarationsForShorthand(
732 self,
733 declarations,
734 ));
735 }
736
737 None
738 }
739
740 #[inline]
742 pub fn is_legacy_shorthand(self) -> bool {
743 self.flags().contains(PropertyFlags::IS_LEGACY_SHORTHAND)
744 }
745
746 #[inline]
748 pub fn allows_disabled_subproperties(self) -> bool {
749 self.flags()
750 .contains(PropertyFlags::ALLOWS_DISABLED_SUBPROPERTIES)
751 }
752}
753
754pub const ARBITRARY_SUBSTITUTION_FUNCTIONS: &[&str] = &["var", "env", "attr"];
756
757fn parse_non_custom_property_declaration_value_into(
758 declarations: &mut SourcePropertyDeclaration,
759 context: &ParserContext,
760 input: &mut Parser,
761 start: &cssparser::ParserState,
762 parse_entirely_into: impl FnOnce(
763 &mut SourcePropertyDeclaration,
764 &mut Parser,
765 ) -> Result<(), ParseError>,
766 parsed_wide_keyword: impl FnOnce(&mut SourcePropertyDeclaration, CSSWideKeyword),
767 parsed_custom: impl FnOnce(&mut SourcePropertyDeclaration, custom_properties::VariableValue),
768) -> Result<(), ParseError> {
769 let mut starts_with_curly_block = false;
770 if let Ok(token) = input.next() {
771 match token {
772 cssparser::Token::Ident(ident) => {
773 if let Ok(wk) = CSSWideKeyword::from_ident(ident) {
774 if input.expect_exhausted().is_ok() {
775 return {
776 parsed_wide_keyword(declarations, wk);
777 Ok(())
778 };
779 }
780 }
781 },
782 cssparser::Token::CurlyBracketBlock => {
783 starts_with_curly_block = true;
784 },
785 _ => {},
786 }
787 };
788
789 input.reset(start);
790 input.look_for_arbitrary_substitution_functions(ARBITRARY_SUBSTITUTION_FUNCTIONS);
791
792 let mut saw_arbitrary_substitution_functions = false;
793 let err = match parse_entirely_into(declarations, input) {
794 Ok(()) => {
795 saw_arbitrary_substitution_functions = input.seen_arbitrary_substitution_functions();
796 if !saw_arbitrary_substitution_functions {
797 return Ok(());
798 }
799 ParseError::custom(style_traits::StyleParseErrorKind::UnspecifiedError)
800 },
801 Err(e) => e,
802 };
803
804 let start_pos = start.position();
806 let mut at_start = start_pos == input.position();
807 let mut invalid = false;
808 while let Ok(token) = input.next() {
809 if matches!(token, cssparser::Token::CurlyBracketBlock) {
810 if !starts_with_curly_block || !at_start {
811 invalid = true;
812 break;
813 }
814 } else if starts_with_curly_block {
815 invalid = true;
816 break;
817 }
818 at_start = false;
819 }
820 saw_arbitrary_substitution_functions =
821 saw_arbitrary_substitution_functions || input.seen_arbitrary_substitution_functions();
822 if !saw_arbitrary_substitution_functions || invalid {
823 return Err(err);
824 }
825 input.reset(start);
826 let value = custom_properties::VariableValue::parse(
827 input,
828 Some(&context.namespaces.prefixes),
829 context.url_data,
830 )?;
831 parsed_custom(declarations, value);
832 Ok(())
833}
834
835impl PropertyDeclaration {
836 fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option<&str> {
837 match *self {
838 PropertyDeclaration::WithVariables(ref declaration) => {
839 let s = declaration.value.from_shorthand?;
840 if s != shorthand {
841 return None;
842 }
843 Some(&*declaration.value.variable_value.css)
844 },
845 _ => None,
846 }
847 }
848
849 #[inline]
851 pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
852 Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
853 }
854
855 #[inline]
857 pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
858 match *self {
859 PropertyDeclaration::CSSWideKeyword(ref declaration) => Some(declaration.keyword),
860 _ => None,
861 }
862 }
863
864 pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
877 match *self {
878 PropertyDeclaration::CSSWideKeyword(..) | PropertyDeclaration::WithVariables(..) => {
879 false
880 },
881 PropertyDeclaration::Custom(..) => {
882 unreachable!("Serializing a custom property as part of shorthand?")
883 },
884 _ => true,
885 }
886 }
887
888 pub fn is_animatable(&self) -> bool {
890 self.id().is_animatable()
891 }
892
893 pub fn is_custom(&self) -> bool {
896 matches!(*self, PropertyDeclaration::Custom(..))
897 }
898
899 pub fn parse_into(
910 declarations: &mut SourcePropertyDeclaration,
911 id: PropertyId,
912 context: &ParserContext,
913 input: &mut Parser,
914 ) -> Result<(), ParseError> {
915 assert!(declarations.is_empty());
916 debug_assert!(id.allowed_in(context), "{:?}", id);
917 input.skip_whitespace();
918
919 let start = input.state();
920 let non_custom_id = match id {
921 PropertyId::Custom(property_name) => {
922 let value = match input.try_parse(CSSWideKeyword::parse) {
923 Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
924 Err(()) => CustomDeclarationValue::Unparsed(Arc::new(
925 custom_properties::VariableValue::parse(
926 input,
927 Some(&context.namespaces.prefixes),
928 context.url_data,
929 )?,
930 )),
931 };
932 declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
933 name: property_name,
934 value,
935 }));
936 return Ok(());
937 },
938 PropertyId::NonCustom(id) => id,
939 };
940 match non_custom_id.longhand_or_shorthand() {
941 Ok(longhand_id) => {
942 parse_non_custom_property_declaration_value_into(
943 declarations,
944 context,
945 input,
946 &start,
947 |declarations, input| {
948 let decl = input
949 .parse_entirely(|input| longhand_id.parse_value(context, input))?;
950 declarations.push(decl);
951 Ok(())
952 },
953 |declarations, wk| {
954 declarations.push(PropertyDeclaration::css_wide_keyword(longhand_id, wk));
955 },
956 |declarations, variable_value| {
957 declarations.push(PropertyDeclaration::WithVariables(VariableDeclaration {
958 id: longhand_id,
959 value: Arc::new(UnparsedValue {
960 variable_value,
961 from_shorthand: None,
962 }),
963 }))
964 },
965 )?;
966 },
967 Err(shorthand_id) => {
968 parse_non_custom_property_declaration_value_into(
969 declarations,
970 context,
971 input,
972 &start,
973 |declarations, input| shorthand_id.parse_into(declarations, context, input),
976 |declarations, wk| {
977 if shorthand_id == ShorthandId::All {
978 declarations.all_shorthand = AllShorthand::CSSWideKeyword(wk)
979 } else {
980 for longhand in shorthand_id.longhands() {
981 declarations
982 .push(PropertyDeclaration::css_wide_keyword(longhand, wk));
983 }
984 }
985 },
986 |declarations, variable_value| {
987 let unparsed = Arc::new(UnparsedValue {
988 variable_value,
989 from_shorthand: Some(shorthand_id),
990 });
991 if shorthand_id == ShorthandId::All {
992 declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
993 } else {
994 for id in shorthand_id.longhands() {
995 declarations.push(PropertyDeclaration::WithVariables(
996 VariableDeclaration {
997 id,
998 value: unparsed.clone(),
999 },
1000 ))
1001 }
1002 }
1003 },
1004 )?;
1005 },
1006 }
1007 if let Some(use_counters) = context.use_counters {
1008 use_counters.non_custom_properties.record(non_custom_id);
1009 }
1010 Ok(())
1011 }
1012}
1013
1014#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1016pub enum OwnedPropertyDeclarationId {
1017 Longhand(LonghandId),
1019 Custom(custom_properties::Name),
1021}
1022
1023impl OwnedPropertyDeclarationId {
1024 #[inline]
1026 pub fn is_logical(&self) -> bool {
1027 self.as_borrowed().is_logical()
1028 }
1029
1030 #[inline]
1032 pub fn as_borrowed(&self) -> PropertyDeclarationId<'_> {
1033 match self {
1034 Self::Longhand(id) => PropertyDeclarationId::Longhand(*id),
1035 Self::Custom(name) => PropertyDeclarationId::Custom(name),
1036 }
1037 }
1038
1039 #[cfg(feature = "gecko")]
1041 #[inline]
1042 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
1043 Some(match PropertyId::from_gecko_css_property_id(property)? {
1044 PropertyId::Custom(name) => Self::Custom(name),
1045 PropertyId::NonCustom(id) => Self::Longhand(id.as_longhand()?),
1046 })
1047 }
1048}
1049
1050#[derive(Clone, Copy, Debug, PartialEq, MallocSizeOf)]
1053pub enum PropertyDeclarationId<'a> {
1054 Longhand(LonghandId),
1056 Custom(&'a custom_properties::Name),
1058}
1059
1060impl<'a> ToCss for PropertyDeclarationId<'a> {
1061 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1062 where
1063 W: Write,
1064 {
1065 match *self {
1066 PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
1067 PropertyDeclarationId::Custom(name) => {
1068 dest.write_str("--")?;
1069 serialize_atom_name(name, dest)
1070 },
1071 }
1072 }
1073}
1074
1075impl<'a> PropertyDeclarationId<'a> {
1076 #[inline(always)]
1078 pub fn flags(&self) -> PropertyFlags {
1079 match self {
1080 Self::Longhand(id) => id.flags(),
1081 Self::Custom(_) => PropertyFlags::empty(),
1082 }
1083 }
1084
1085 pub fn to_owned(&self) -> OwnedPropertyDeclarationId {
1087 match self {
1088 PropertyDeclarationId::Longhand(id) => OwnedPropertyDeclarationId::Longhand(*id),
1089 PropertyDeclarationId::Custom(name) => {
1090 OwnedPropertyDeclarationId::Custom((*name).clone())
1091 },
1092 }
1093 }
1094
1095 pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
1098 match *self {
1099 PropertyDeclarationId::Longhand(id) => match *other {
1100 PropertyId::NonCustom(non_custom_id) => id.is_or_is_longhand_of(non_custom_id),
1101 PropertyId::Custom(_) => false,
1102 },
1103 PropertyDeclarationId::Custom(name) => {
1104 matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
1105 },
1106 }
1107 }
1108
1109 pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
1112 match *self {
1113 PropertyDeclarationId::Longhand(ref id) => id.is_longhand_of(shorthand),
1114 _ => false,
1115 }
1116 }
1117
1118 pub fn name(&self) -> Cow<'static, str> {
1120 match *self {
1121 PropertyDeclarationId::Longhand(id) => id.name().into(),
1122 PropertyDeclarationId::Custom(name) => {
1123 let mut s = String::new();
1124 write!(&mut s, "--{}", name).unwrap();
1125 s.into()
1126 },
1127 }
1128 }
1129
1130 #[inline]
1132 pub fn as_longhand(&self) -> Option<LonghandId> {
1133 match *self {
1134 PropertyDeclarationId::Longhand(id) => Some(id),
1135 _ => None,
1136 }
1137 }
1138
1139 #[inline]
1141 pub fn is_logical(&self) -> bool {
1142 match self {
1143 PropertyDeclarationId::Longhand(id) => id.is_logical(),
1144 PropertyDeclarationId::Custom(_) => false,
1145 }
1146 }
1147
1148 #[inline]
1153 pub fn to_physical(&self, wm: WritingMode) -> Self {
1154 match self {
1155 Self::Longhand(id) => Self::Longhand(id.to_physical(wm)),
1156 Self::Custom(_) => *self,
1157 }
1158 }
1159
1160 #[inline]
1162 pub fn is_animatable(&self) -> bool {
1163 match self {
1164 Self::Longhand(id) => id.is_animatable(),
1165 Self::Custom(_) => true,
1166 }
1167 }
1168
1169 #[inline]
1171 pub fn is_discrete_animatable(&self) -> bool {
1172 match self {
1173 Self::Longhand(longhand) => longhand.is_discrete_animatable(),
1174 Self::Custom(_) => true,
1176 }
1177 }
1178
1179 #[cfg(feature = "gecko")]
1182 #[inline]
1183 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
1184 match self {
1185 PropertyDeclarationId::Longhand(id) => id.to_noncustomcsspropertyid(),
1186 PropertyDeclarationId::Custom(_) => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1187 }
1188 }
1189
1190 #[cfg(feature = "gecko")]
1195 #[inline]
1196 pub fn to_gecko_css_property_id(&self) -> CSSPropertyId {
1197 match self {
1198 Self::Longhand(id) => CSSPropertyId {
1199 mId: id.to_noncustomcsspropertyid(),
1200 mCustomName: RefPtr::null(),
1201 },
1202 Self::Custom(name) => {
1203 let mut property_id = CSSPropertyId {
1204 mId: NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1205 mCustomName: RefPtr::null(),
1206 };
1207 property_id.mCustomName.mRawPtr = (*name).clone().into_addrefed();
1208 property_id
1209 },
1210 }
1211 }
1212}
1213
1214pub trait IndexedId: Copy {
1217 const COUNT: usize;
1219 unsafe fn from_index_release_unchecked(index: usize) -> Self;
1222 fn to_index(self) -> usize;
1224}
1225
1226impl IndexedId for NonCustomPropertyId {
1227 const COUNT: usize = property_counts::NON_CUSTOM;
1228
1229 #[inline(always)]
1230 unsafe fn from_index_release_unchecked(index: usize) -> Self {
1231 debug_assert!(index < Self::COUNT);
1232 NonCustomPropertyId(index as u16)
1233 }
1234
1235 #[inline(always)]
1236 fn to_index(self) -> usize {
1237 self.0 as usize
1238 }
1239}
1240
1241impl IndexedId for PrioritaryPropertyId {
1242 const COUNT: usize = property_counts::PRIORITARY;
1243
1244 #[inline(always)]
1245 unsafe fn from_index_release_unchecked(index: usize) -> Self {
1246 unsafe {
1247 debug_assert!(index < Self::COUNT);
1248 std::mem::transmute(index as u8)
1249 }
1250 }
1251
1252 #[inline(always)]
1253 fn to_index(self) -> usize {
1254 self as usize
1255 }
1256}
1257
1258impl IndexedId for LonghandId {
1259 const COUNT: usize = property_counts::LONGHANDS;
1260
1261 #[inline(always)]
1262 unsafe fn from_index_release_unchecked(index: usize) -> Self {
1263 unsafe {
1264 debug_assert!(index < Self::COUNT);
1265 std::mem::transmute(index as u16)
1266 }
1267 }
1268
1269 #[inline(always)]
1270 fn to_index(self) -> usize {
1271 self as usize
1272 }
1273}
1274
1275pub type NonCustomPropertyIdSet =
1277 IdSet<NonCustomPropertyId, { (property_counts::NON_CUSTOM - 1 + 32) / 32 }>;
1278pub type NonCustomPropertyIdSetIterator<'a> = IdSetIterator<'a, NonCustomPropertyId>;
1280pub type PrioritaryPropertyIdSet =
1282 IdSet<PrioritaryPropertyId, { (property_counts::PRIORITARY - 1 + 32) / 32 }>;
1283pub type PrioritaryPropertyIdSetIterator<'a> = IdSetIterator<'a, PrioritaryPropertyId>;
1285pub type LonghandIdSet = IdSet<LonghandId, { (property_counts::LONGHANDS - 1 + 32) / 32 }>;
1287pub type LonghandIdSetIterator<'a> = IdSetIterator<'a, LonghandId>;
1289
1290pub struct IdSet<Id: IndexedId, const W: usize> {
1297 storage: [u32; W],
1298 _phantom: std::marker::PhantomData<Id>,
1299}
1300
1301impl<Id: IndexedId, const W: usize> Clone for IdSet<Id, W> {
1302 #[inline]
1303 fn clone(&self) -> Self {
1304 *self
1305 }
1306}
1307
1308impl<Id: IndexedId, const W: usize> Copy for IdSet<Id, W> {}
1309
1310impl<Id: IndexedId, const W: usize> Default for IdSet<Id, W> {
1311 #[inline]
1312 fn default() -> Self {
1313 Self {
1314 storage: [0; W],
1315 _phantom: std::marker::PhantomData,
1316 }
1317 }
1318}
1319
1320impl<Id: IndexedId, const W: usize> PartialEq for IdSet<Id, W> {
1321 #[inline]
1322 fn eq(&self, other: &Self) -> bool {
1323 self.storage == other.storage
1324 }
1325}
1326
1327impl<Id: IndexedId, const W: usize> fmt::Debug for IdSet<Id, W> {
1328 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1329 self.storage.fmt(f)
1330 }
1331}
1332
1333impl<Id: IndexedId, const W: usize> malloc_size_of::MallocSizeOf for IdSet<Id, W> {
1334 #[inline(always)]
1335 fn size_of(&self, _: &mut malloc_size_of::MallocSizeOfOps) -> usize {
1336 0
1337 }
1338}
1339
1340impl<Id: IndexedId, const W: usize> IdSet<Id, W> {
1341 #[inline]
1343 pub fn new() -> Self {
1344 Self::default()
1345 }
1346
1347 pub(crate) const fn from_storage(storage: [u32; W]) -> Self {
1349 Self {
1350 storage,
1351 _phantom: std::marker::PhantomData,
1352 }
1353 }
1354
1355 #[inline]
1357 pub fn insert(&mut self, id: Id) {
1358 let bit = id.to_index();
1359 self.storage[bit / 32] |= 1 << (bit % 32);
1360 }
1361
1362 #[inline]
1364 pub fn remove(&mut self, id: Id) {
1365 let bit = id.to_index();
1366 self.storage[bit / 32] &= !(1 << (bit % 32));
1367 }
1368
1369 #[inline]
1371 pub fn contains(&self, id: Id) -> bool {
1372 let bit = id.to_index();
1373 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1374 }
1375
1376 pub fn iter(&self) -> IdSetIterator<'_, Id> {
1378 IdSetIterator {
1379 chunks: &self.storage,
1380 cur_chunk: 0,
1381 cur_bit: 0,
1382 _phantom: std::marker::PhantomData,
1383 }
1384 }
1385
1386 pub fn contains_all(&self, other: &Self) -> bool {
1388 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1389 if (*self_cell & *other_cell) != *other_cell {
1390 return false;
1391 }
1392 }
1393 true
1394 }
1395
1396 pub fn contains_any(&self, other: &Self) -> bool {
1398 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1399 if (*self_cell & *other_cell) != 0 {
1400 return true;
1401 }
1402 }
1403 false
1404 }
1405
1406 #[inline]
1408 pub fn remove_all(&mut self, other: &Self) {
1409 for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
1410 *self_cell &= !*other_cell;
1411 }
1412 }
1413
1414 #[inline]
1416 pub fn clear(&mut self) {
1417 for cell in &mut self.storage {
1418 *cell = 0
1419 }
1420 }
1421
1422 #[inline]
1424 pub fn is_empty(&self) -> bool {
1425 self.storage.iter().all(|c| *c == 0)
1426 }
1427}
1428
1429to_shmem::impl_trivial_to_shmem!(LonghandIdSet);
1430impl LonghandIdSet {
1431 #[inline]
1433 pub fn contains_any_reset(&self) -> bool {
1434 self.contains_any(Self::reset())
1435 }
1436}
1437
1438pub struct IdSetIterator<'a, Id: IndexedId> {
1440 chunks: &'a [u32],
1441 cur_chunk: u32,
1442 cur_bit: u32, _phantom: std::marker::PhantomData<Id>,
1444}
1445
1446impl<'a, Id: IndexedId> Iterator for IdSetIterator<'a, Id> {
1447 type Item = Id;
1448
1449 fn next(&mut self) -> Option<Self::Item> {
1450 loop {
1451 debug_assert!(self.cur_bit < 32);
1452 let cur_chunk = self.cur_chunk;
1453 let cur_bit = self.cur_bit;
1454 let chunk = *self.chunks.get(cur_chunk as usize)?;
1455 let next_bit = (chunk >> cur_bit).trailing_zeros();
1456 if next_bit == 32 {
1457 self.cur_bit = 0;
1459 self.cur_chunk += 1;
1460 continue;
1461 }
1462 debug_assert!(cur_bit + next_bit < 32);
1463 let index = (cur_chunk * 32 + cur_bit + next_bit) as usize;
1464 debug_assert!(index < Id::COUNT);
1465 let id = unsafe { Id::from_index_release_unchecked(index) };
1466 self.cur_bit += next_bit + 1;
1467 if self.cur_bit == 32 {
1468 self.cur_bit = 0;
1469 self.cur_chunk += 1;
1470 }
1471 return Some(id);
1472 }
1473 }
1474}
1475
1476pub type SubpropertiesVec<T> = ArrayVec<T, { property_counts::MAX_SHORTHAND_EXPANDED }>;
1478
1479#[derive(Default)]
1483pub struct SourcePropertyDeclaration {
1484 pub declarations: SubpropertiesVec<PropertyDeclaration>,
1486 pub all_shorthand: AllShorthand,
1488}
1489
1490#[cfg(feature = "gecko")]
1493size_of_test!(SourcePropertyDeclaration, 632);
1494#[cfg(feature = "servo")]
1495size_of_test!(SourcePropertyDeclaration, 568);
1496
1497impl SourcePropertyDeclaration {
1498 #[inline]
1500 pub fn with_one(decl: PropertyDeclaration) -> Self {
1501 let mut result = Self::default();
1502 result.declarations.push(decl);
1503 result
1504 }
1505
1506 pub fn drain(&mut self) -> SourcePropertyDeclarationDrain<'_> {
1508 SourcePropertyDeclarationDrain {
1509 declarations: self.declarations.drain(..),
1510 all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
1511 }
1512 }
1513
1514 pub fn clear(&mut self) {
1516 self.declarations.clear();
1517 self.all_shorthand = AllShorthand::NotSet;
1518 }
1519
1520 pub fn is_empty(&self) -> bool {
1522 self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
1523 }
1524
1525 pub fn push(&mut self, declaration: PropertyDeclaration) {
1527 let _result = self.declarations.try_push(declaration);
1528 debug_assert!(_result.is_ok());
1529 }
1530}
1531
1532pub struct SourcePropertyDeclarationDrain<'a> {
1534 pub declarations:
1536 ArrayVecDrain<'a, PropertyDeclaration, { property_counts::MAX_SHORTHAND_EXPANDED }>,
1537 pub all_shorthand: AllShorthand,
1539}
1540
1541#[derive(Debug, Eq, PartialEq, ToShmem)]
1543pub struct UnparsedValue {
1544 pub(super) variable_value: custom_properties::VariableValue,
1546 from_shorthand: Option<ShorthandId>,
1548}
1549
1550impl ToCss for UnparsedValue {
1551 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1552 where
1553 W: Write,
1554 {
1555 if self.from_shorthand.is_none() {
1557 self.variable_value.to_css(dest)?;
1558 }
1559 Ok(())
1560 }
1561}
1562
1563impl ToTyped for UnparsedValue {
1564 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
1565 if self.from_shorthand.is_none() {
1566 self.variable_value.to_typed(dest)?;
1567 return Ok(());
1568 }
1569 Err(())
1570 }
1571}
1572
1573pub type ShorthandsWithPropertyReferencesCache =
1580 FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
1581
1582impl UnparsedValue {
1583 fn substitute_variables<'cache>(
1584 &self,
1585 longhand_id: LonghandId,
1586 substitution_functions: &ComputedSubstitutionFunctions,
1587 stylist: &Stylist,
1588 computed_context: &computed::Context,
1589 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
1590 attribute_tracker: &mut AttributeTracker,
1591 ) -> Cow<'cache, PropertyDeclaration> {
1592 let invalid_at_computed_value_time = || {
1593 let keyword = if longhand_id.inherited() {
1594 CSSWideKeyword::Inherit
1595 } else {
1596 CSSWideKeyword::Initial
1597 };
1598 Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
1599 };
1600
1601 if computed_context
1602 .builder
1603 .invalid_non_custom_properties
1604 .contains(longhand_id)
1605 {
1606 return invalid_at_computed_value_time();
1607 }
1608
1609 if let Some(shorthand_id) = self.from_shorthand {
1610 let key = (shorthand_id, longhand_id);
1611 if shorthand_cache.contains_key(&key) {
1612 return Cow::Borrowed(&shorthand_cache[&key]);
1617 }
1618 }
1619
1620 let SubstitutionResult { css, attr_taint } = match custom_properties::substitute(
1621 &self.variable_value,
1622 substitution_functions,
1623 stylist,
1624 computed_context,
1625 attribute_tracker,
1626 ) {
1627 Ok(css) => css,
1628 Err(..) => return invalid_at_computed_value_time(),
1629 };
1630
1631 let context = ParserContext::new(
1642 Origin::Author,
1643 &self.variable_value.url_data,
1644 None,
1645 ParsingMode::DEFAULT,
1646 computed_context.quirks_mode,
1647 Default::default(),
1648 None,
1649 None,
1650 attr_taint,
1651 );
1652
1653 let mut input = Parser::new(&css);
1654 input.skip_whitespace();
1655
1656 if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
1657 return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
1658 }
1659
1660 let shorthand = match self.from_shorthand {
1661 None => {
1662 return match input.parse_entirely(|input| longhand_id.parse_value(&context, input))
1663 {
1664 Ok(decl) => Cow::Owned(decl),
1665 Err(..) => invalid_at_computed_value_time(),
1666 }
1667 },
1668 Some(shorthand) => shorthand,
1669 };
1670
1671 let mut decls = SourcePropertyDeclaration::default();
1672 if shorthand
1674 .parse_into(&mut decls, &context, &mut input)
1675 .is_err()
1676 {
1677 return invalid_at_computed_value_time();
1678 }
1679
1680 for declaration in decls.declarations.drain(..) {
1681 let longhand = declaration.id().as_longhand().unwrap();
1682 if longhand.is_logical() {
1683 let writing_mode = computed_context.builder.writing_mode;
1684 shorthand_cache.insert(
1685 (shorthand, longhand.to_physical(writing_mode)),
1686 declaration.clone(),
1687 );
1688 }
1689 shorthand_cache.insert((shorthand, longhand), declaration);
1690 }
1691
1692 let key = (shorthand, longhand_id);
1693 match shorthand_cache.get(&key) {
1694 Some(decl) => Cow::Borrowed(decl),
1695 None => invalid_at_computed_value_time(),
1707 }
1708 }
1709}
1710pub enum AllShorthand {
1712 NotSet,
1714 CSSWideKeyword(CSSWideKeyword),
1716 WithVariables(Arc<UnparsedValue>),
1718}
1719
1720impl Default for AllShorthand {
1721 fn default() -> Self {
1722 Self::NotSet
1723 }
1724}
1725
1726impl AllShorthand {
1727 #[inline]
1729 pub fn declarations(&self) -> AllShorthandDeclarationIterator<'_> {
1730 AllShorthandDeclarationIterator {
1731 all_shorthand: self,
1732 longhands: ShorthandId::All.longhands(),
1733 }
1734 }
1735}
1736
1737pub struct AllShorthandDeclarationIterator<'a> {
1739 all_shorthand: &'a AllShorthand,
1740 longhands: NonCustomPropertyIterator<LonghandId>,
1741}
1742
1743impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
1744 type Item = PropertyDeclaration;
1745
1746 #[inline]
1747 fn next(&mut self) -> Option<Self::Item> {
1748 match *self.all_shorthand {
1749 AllShorthand::NotSet => None,
1750 AllShorthand::CSSWideKeyword(ref keyword) => Some(
1751 PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword),
1752 ),
1753 AllShorthand::WithVariables(ref unparsed) => {
1754 Some(PropertyDeclaration::WithVariables(VariableDeclaration {
1755 id: self.longhands.next()?,
1756 value: unparsed.clone(),
1757 }))
1758 },
1759 }
1760 }
1761}
1762
1763pub struct NonCustomPropertyIterator<Item: 'static> {
1766 filter: bool,
1767 iter: std::slice::Iter<'static, Item>,
1768}
1769
1770impl<Item> Iterator for NonCustomPropertyIterator<Item>
1771where
1772 Item: 'static + Copy + Into<NonCustomPropertyId>,
1773{
1774 type Item = Item;
1775
1776 fn next(&mut self) -> Option<Self::Item> {
1777 loop {
1778 let id = *self.iter.next()?;
1779 if !self.filter || id.into().enabled_for_all_content() {
1780 return Some(id);
1781 }
1782 }
1783 }
1784}
1785
1786pub struct TransitionPropertyIterator<'a> {
1788 style: &'a ComputedValues,
1789 index_range: core::ops::Range<usize>,
1790 longhand_iterator: Option<NonCustomPropertyIterator<LonghandId>>,
1791}
1792
1793impl<'a> TransitionPropertyIterator<'a> {
1794 pub fn from_style(style: &'a ComputedValues) -> Self {
1796 Self {
1797 style,
1798 index_range: 0..style.get_ui().transition_property_count(),
1799 longhand_iterator: None,
1800 }
1801 }
1802}
1803
1804pub struct TransitionPropertyIteration {
1806 pub property: OwnedPropertyDeclarationId,
1808 pub index: usize,
1811}
1812
1813impl<'a> Iterator for TransitionPropertyIterator<'a> {
1814 type Item = TransitionPropertyIteration;
1815
1816 fn next(&mut self) -> Option<Self::Item> {
1817 use crate::values::computed::TransitionProperty;
1818 loop {
1819 if let Some(ref mut longhand_iterator) = self.longhand_iterator {
1820 if let Some(longhand_id) = longhand_iterator.next() {
1821 return Some(TransitionPropertyIteration {
1822 property: OwnedPropertyDeclarationId::Longhand(longhand_id),
1823 index: self.index_range.start - 1,
1824 });
1825 }
1826 self.longhand_iterator = None;
1827 }
1828
1829 let index = self.index_range.next()?;
1830 match self.style.get_ui().transition_property_at(index) {
1831 TransitionProperty::NonCustom(id) => {
1832 match id.longhand_or_shorthand() {
1833 Ok(longhand_id) => {
1834 return Some(TransitionPropertyIteration {
1835 property: OwnedPropertyDeclarationId::Longhand(longhand_id),
1836 index,
1837 });
1838 },
1839 Err(shorthand_id) => {
1840 self.longhand_iterator = Some(shorthand_id.longhands());
1844 },
1845 }
1846 },
1847 TransitionProperty::Custom(name) => {
1848 return Some(TransitionPropertyIteration {
1849 property: OwnedPropertyDeclarationId::Custom(name),
1850 index,
1851 })
1852 },
1853 TransitionProperty::Unsupported(..) => {},
1854 }
1855 }
1856 }
1857}