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::FxHashMap;
25use crate::applicable_declarations::RevertKind;
26use crate::custom_properties::{self, ComputedSubstitutionFunctions};
27use crate::derives::*;
28use crate::dom::AttributeTracker;
29#[cfg(feature = "gecko")]
30use crate::gecko_bindings::structs::{CSSPropertyId, NonCustomCSSPropertyId, RefPtr};
31use crate::logical_geometry::WritingMode;
32use crate::parser::ParserContext;
33use crate::stylesheets::{CssRuleType, CssRuleTypes, Origin};
34use crate::stylist::Stylist;
35use crate::substitution::{self, SubstitutionFunctionKind, SubstitutionResult};
36use crate::typed_om::{ToTyped, TypedValue};
37use crate::values::{computed, serialize_atom_name};
38use arrayvec::{ArrayVec, Drain as ArrayVecDrain};
39use cssparser::{Parser, match_ignore_ascii_case};
40use servo_arc::Arc;
41use std::{
42 borrow::Cow,
43 fmt::{self, Write},
44 mem,
45};
46use strum::VariantNames;
47use style_traits::{
48 CssString, CssWriter, KeywordsCollectFn, ParseError, ParsingMode, SpecifiedValueInfo, ToCss,
49};
50use thin_vec::ThinVec;
51
52bitflags! {
53 #[derive(Clone, Copy)]
55 pub struct PropertyFlags: u16 {
56 const APPLIES_TO_FIRST_LETTER = 1 << 1;
58 const APPLIES_TO_FIRST_LINE = 1 << 2;
60 const APPLIES_TO_PLACEHOLDER = 1 << 3;
62 const APPLIES_TO_CUE = 1 << 4;
64 const APPLIES_TO_MARKER = 1 << 5;
66 const APPLIES_TO_HIGHLIGHT = 1 << 6;
69 const IS_LEGACY_SHORTHAND = 1 << 7;
73 const ALLOWS_DISABLED_SUBPROPERTIES = 1 << 8;
75
76 const CAN_ANIMATE_ON_COMPOSITOR = 0;
82 const SCROLL_LINKED_EFFECTIVE = 0;
84 const AFFECTS_LAYOUT = 0;
86 #[allow(missing_docs)]
87 const AFFECTS_OVERFLOW = 0;
88 #[allow(missing_docs)]
89 const AFFECTS_PAINT = 0;
90 }
91}
92
93#[derive(
95 Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
96)]
97pub enum CSSWideKeyword {
98 Initial,
100 Inherit,
102 Unset,
104 Revert,
106 RevertLayer,
108 RevertRule,
110}
111
112impl CSSWideKeyword {
113 pub fn to_str(&self) -> &'static str {
115 match *self {
116 Self::Initial => "initial",
117 Self::Inherit => "inherit",
118 Self::Unset => "unset",
119 Self::Revert => "revert",
120 Self::RevertLayer => "revert-layer",
121 Self::RevertRule => "revert-rule",
122 }
123 }
124
125 pub fn from_ident(ident: &str) -> Result<Self, ()> {
127 Ok(match_ignore_ascii_case! { ident,
128 "initial" => Self::Initial,
129 "inherit" => Self::Inherit,
130 "unset" => Self::Unset,
131 "revert" => Self::Revert,
132 "revert-layer" => Self::RevertLayer,
133 "revert-rule" if crate::pref!("layout.css.revert-rule.enabled") => Self::RevertRule,
134 _ => return Err(()),
135 })
136 }
137
138 pub fn parse(input: &mut Parser) -> Result<Self, ()> {
140 let keyword = {
141 let ident = input.expect_ident().map_err(|_| ())?;
142 Self::from_ident(ident)?
143 };
144 input.expect_exhausted().map_err(|_| ())?;
145 Ok(keyword)
146 }
147
148 pub fn revert_kind(self) -> Option<RevertKind> {
150 Some(match self {
151 Self::Initial | Self::Inherit | Self::Unset => return None,
152 Self::Revert => RevertKind::Origin,
153 Self::RevertLayer => RevertKind::Layer,
154 Self::RevertRule => RevertKind::Rule,
155 })
156 }
157}
158
159#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf)]
161pub struct WideKeywordDeclaration {
162 #[css(skip)]
163 id: LonghandId,
164 pub keyword: CSSWideKeyword,
166}
167
168impl ToTyped for WideKeywordDeclaration {
171 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
172 self.keyword.to_typed(dest)
173 }
174}
175
176#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
178pub struct VariableDeclaration {
179 #[css(skip)]
181 id: LonghandId,
182 #[ignore_malloc_size_of = "Arc"]
184 pub value: Arc<UnparsedValue>,
185}
186
187#[derive(Clone, PartialEq, ToCss, ToShmem, ToTyped)]
190pub enum CustomDeclarationValue {
191 Unparsed(Arc<custom_properties::SpecifiedValue>),
193 Parsed(Arc<crate::properties_and_values::value::SpecifiedValue>),
195 CSSWideKeyword(CSSWideKeyword),
197}
198
199#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
201pub struct CustomDeclaration {
202 #[css(skip)]
204 pub name: custom_properties::Name,
205 #[ignore_malloc_size_of = "Arc"]
207 pub value: CustomDeclarationValue,
208}
209
210impl fmt::Debug for PropertyDeclaration {
211 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
212 self.id().to_css(&mut CssWriter::new(f))?;
213 f.write_str(": ")?;
214
215 let mut s = CssString::new();
219 self.to_css(&mut s)?;
220 write!(f, "{}", s)
221 }
222}
223
224#[derive(
226 Clone, Copy, Debug, PartialEq, Eq, Hash, ToComputedValue, ToResolvedValue, ToShmem, MallocSizeOf,
227)]
228#[repr(C)]
229pub struct NonCustomPropertyId(u16);
230
231impl ToCss for NonCustomPropertyId {
232 #[inline]
233 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
234 where
235 W: Write,
236 {
237 dest.write_str(self.name())
238 }
239}
240
241impl NonCustomPropertyId {
242 pub fn bit(self) -> usize {
244 self.0 as usize
245 }
246
247 #[cfg(feature = "gecko")]
249 #[inline]
250 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
251 unsafe { mem::transmute(self.0) }
253 }
254
255 #[cfg(feature = "gecko")]
257 #[inline]
258 pub fn from_noncustomcsspropertyid(prop: NonCustomCSSPropertyId) -> Option<Self> {
259 let prop = prop as u16;
260 if prop >= property_counts::NON_CUSTOM as u16 {
261 return None;
262 }
263 Some(NonCustomPropertyId(prop))
265 }
266
267 pub fn unaliased(self) -> Self {
269 let Some(alias_id) = self.as_alias() else {
270 return self;
271 };
272 alias_id.aliased_property()
273 }
274
275 #[inline]
277 pub fn to_property_id(self) -> PropertyId {
278 PropertyId::NonCustom(self)
279 }
280
281 #[inline]
283 pub fn as_longhand(self) -> Option<LonghandId> {
284 if self.0 < property_counts::LONGHANDS as u16 {
285 return Some(unsafe { mem::transmute(self.0) });
286 }
287 None
288 }
289
290 #[inline]
292 pub fn as_shorthand(self) -> Option<ShorthandId> {
293 if self.0 >= property_counts::LONGHANDS as u16
294 && self.0 < property_counts::LONGHANDS_AND_SHORTHANDS as u16
295 {
296 return Some(unsafe { mem::transmute(self.0 - (property_counts::LONGHANDS as u16)) });
297 }
298 None
299 }
300
301 #[inline]
303 pub fn as_alias(self) -> Option<AliasId> {
304 debug_assert!((self.0 as usize) < property_counts::NON_CUSTOM);
305 if self.0 >= property_counts::LONGHANDS_AND_SHORTHANDS as u16 {
306 return Some(unsafe {
307 mem::transmute(self.0 - (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
308 });
309 }
310 None
311 }
312
313 #[inline]
315 pub fn longhand_or_shorthand(self) -> Result<LonghandId, ShorthandId> {
316 let id = self.unaliased();
317 match id.as_longhand() {
318 Some(lh) => Ok(lh),
319 None => Err(id.as_shorthand().unwrap()),
320 }
321 }
322
323 #[inline]
325 pub const fn from_longhand(id: LonghandId) -> Self {
326 Self(id as u16)
327 }
328
329 #[inline]
331 pub const fn from_shorthand(id: ShorthandId) -> Self {
332 Self((id as u16) + (property_counts::LONGHANDS as u16))
333 }
334
335 #[inline]
337 pub const fn from_alias(id: AliasId) -> Self {
338 Self((id as u16) + (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
339 }
340
341 pub fn iter() -> impl Iterator<Item = Self> {
343 (0..property_counts::NON_CUSTOM as u16).map(Self)
344 }
345}
346
347impl From<LonghandId> for NonCustomPropertyId {
348 #[inline]
349 fn from(id: LonghandId) -> Self {
350 Self::from_longhand(id)
351 }
352}
353
354impl From<ShorthandId> for NonCustomPropertyId {
355 #[inline]
356 fn from(id: ShorthandId) -> Self {
357 Self::from_shorthand(id)
358 }
359}
360
361impl From<AliasId> for NonCustomPropertyId {
362 #[inline]
363 fn from(id: AliasId) -> Self {
364 Self::from_alias(id)
365 }
366}
367
368#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
371pub enum PropertyId {
372 NonCustom(NonCustomPropertyId),
374 Custom(custom_properties::Name),
376}
377
378impl ToCss for PropertyId {
379 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
380 where
381 W: Write,
382 {
383 match *self {
384 PropertyId::NonCustom(id) => dest.write_str(id.name()),
385 PropertyId::Custom(ref name) => {
386 dest.write_str("--")?;
387 serialize_atom_name(name, dest)
388 },
389 }
390 }
391}
392
393impl PropertyId {
394 #[inline]
396 pub fn longhand_id(&self) -> Option<LonghandId> {
397 self.non_custom_non_alias_id()?.as_longhand()
398 }
399
400 pub fn is_animatable(&self) -> bool {
402 match self {
403 Self::NonCustom(id) => id.is_animatable(),
404 Self::Custom(_) => true,
405 }
406 }
407
408 pub fn parse_unchecked_for_testing(name: &str) -> Result<Self, ()> {
413 Self::parse_unchecked(name, None)
414 }
415
416 #[inline]
419 pub fn parse_enabled_for_all_content(name: &str) -> Result<Self, ()> {
420 let id = Self::parse_unchecked(name, None)?;
421
422 if !id.enabled_for_all_content() {
423 return Err(());
424 }
425
426 Ok(id)
427 }
428
429 #[inline]
432 pub fn parse(name: &str, context: &ParserContext) -> Result<Self, ()> {
433 let id = Self::parse_unchecked(name, context.use_counters)?;
434 if !id.allowed_in(context) {
435 return Err(());
436 }
437 Ok(id)
438 }
439
440 #[inline]
445 pub fn parse_ignoring_rule_type(name: &str, context: &ParserContext) -> Result<Self, ()> {
446 let id = Self::parse_unchecked(name, None)?;
447 if !id.allowed_in_ignoring_rule_type(context) {
448 return Err(());
449 }
450 Ok(id)
451 }
452
453 #[cfg(feature = "gecko")]
455 #[inline]
456 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
457 Some(NonCustomPropertyId::from_noncustomcsspropertyid(id)?.to_property_id())
458 }
459
460 #[cfg(feature = "gecko")]
462 #[inline]
463 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
464 Some(
465 if property.mId == NonCustomCSSPropertyId::eCSSPropertyExtra_variable {
466 debug_assert!(!property.mCustomName.mRawPtr.is_null());
467 Self::Custom(unsafe { crate::Atom::from_raw(property.mCustomName.mRawPtr) })
468 } else {
469 Self::NonCustom(NonCustomPropertyId::from_noncustomcsspropertyid(
470 property.mId,
471 )?)
472 },
473 )
474 }
475
476 #[inline]
478 pub fn is_shorthand(&self) -> bool {
479 self.as_shorthand().is_ok()
480 }
481
482 pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId<'_>> {
485 match *self {
486 Self::NonCustom(id) => match id.longhand_or_shorthand() {
487 Ok(lh) => Err(PropertyDeclarationId::Longhand(lh)),
488 Err(sh) => Ok(sh),
489 },
490 Self::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),
491 }
492 }
493
494 pub fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
496 match *self {
497 Self::Custom(_) => None,
498 Self::NonCustom(id) => Some(id),
499 }
500 }
501
502 fn non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId> {
505 self.non_custom_id().map(NonCustomPropertyId::unaliased)
506 }
507
508 #[inline]
511 pub fn enabled_for_all_content(&self) -> bool {
512 let id = match self.non_custom_id() {
513 None => return true,
515 Some(id) => id,
516 };
517
518 id.enabled_for_all_content()
519 }
520
521 #[cfg(feature = "gecko")]
525 #[inline]
526 pub fn to_noncustomcsspropertyid_resolving_aliases(&self) -> NonCustomCSSPropertyId {
527 match self.non_custom_non_alias_id() {
528 Some(id) => id.to_noncustomcsspropertyid(),
529 None => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
530 }
531 }
532
533 pub fn allowed_in_rule(&self, rule_types: CssRuleTypes) -> bool {
535 match self.non_custom_id() {
536 None => !rule_types.contains(CssRuleType::PositionTry),
538 Some(id) => id.allowed_in_rule(rule_types),
539 }
540 }
541
542 fn allowed_in(&self, context: &ParserContext) -> bool {
543 self.allowed_in_rule(context.rule_types()) && self.allowed_in_ignoring_rule_type(context)
544 }
545
546 #[inline]
547 fn allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool {
548 let id = match self.non_custom_id() {
549 None => return true,
551 Some(id) => id,
552 };
553 id.allowed_in_ignoring_rule_type(context)
554 }
555
556 pub fn supports_type(&self, ty: u8) -> bool {
559 let id = self.non_custom_non_alias_id();
560 id.map_or(0, |id| id.supported_types()) & ty != 0
561 }
562
563 pub fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
568 if let Some(id) = self.non_custom_non_alias_id() {
569 id.collect_property_completion_keywords(f);
570 }
571 CSSWideKeyword::collect_completion_keywords(f);
572 }
573}
574
575#[derive(Copy, Clone, Debug)]
579pub enum PropertyIdRef<'a> {
580 NonCustom(NonCustomPropertyId),
582 Custom(&'a custom_properties::Name),
584}
585
586impl<'a> From<&'a PropertyId> for PropertyIdRef<'a> {
587 fn from(value: &'a PropertyId) -> Self {
588 match value {
589 PropertyId::NonCustom(id) => Self::NonCustom(*id),
590 PropertyId::Custom(name) => Self::Custom(name),
591 }
592 }
593}
594
595impl<'a> From<&'a custom_properties::Name> for PropertyIdRef<'a> {
596 fn from(value: &'a custom_properties::Name) -> Self {
597 Self::Custom(value)
598 }
599}
600
601impl<'a> From<NonCustomPropertyId> for PropertyIdRef<'a> {
602 fn from(value: NonCustomPropertyId) -> Self {
603 Self::NonCustom(value)
604 }
605}
606
607impl ToCss for LonghandId {
608 #[inline]
609 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
610 where
611 W: Write,
612 {
613 dest.write_str(self.name())
614 }
615}
616
617impl fmt::Debug for LonghandId {
618 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
619 formatter.write_str(self.name())
620 }
621}
622
623impl LonghandId {
624 #[inline]
626 pub fn name(&self) -> &'static str {
627 NonCustomPropertyId::from(*self).name()
628 }
629
630 #[inline]
632 pub fn inherited(self) -> bool {
633 !LonghandIdSet::reset().contains(self)
634 }
635
636 #[inline]
638 pub fn zoom_dependent(self) -> bool {
639 LonghandIdSet::zoom_dependent().contains(self)
640 }
641
642 #[inline]
645 pub fn ignored_when_document_colors_disabled(self) -> bool {
646 LonghandIdSet::ignored_when_colors_disabled().contains(self)
647 }
648
649 pub fn is_or_is_longhand_of(self, non_custom: NonCustomPropertyId) -> bool {
651 match non_custom.longhand_or_shorthand() {
652 Ok(lh) => self == lh,
653 Err(sh) => self.is_longhand_of(sh),
654 }
655 }
656
657 pub fn is_longhand_of(self, shorthand: ShorthandId) -> bool {
659 self.shorthands().any(|s| s == shorthand)
660 }
661
662 #[inline]
664 pub fn is_animatable(self) -> bool {
665 NonCustomPropertyId::from(self).is_animatable()
666 }
667
668 #[inline]
670 pub fn is_discrete_animatable(self) -> bool {
671 LonghandIdSet::discrete_animatable().contains(self) || self == LonghandId::Display
673 }
674
675 #[cfg(feature = "gecko")]
677 #[inline]
678 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
679 NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
680 }
681
682 #[cfg(feature = "gecko")]
683 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
685 NonCustomPropertyId::from_noncustomcsspropertyid(id)?
686 .unaliased()
687 .as_longhand()
688 }
689
690 #[inline]
692 pub fn is_logical(self) -> bool {
693 LonghandIdSet::logical().contains(self)
694 }
695}
696
697impl ToCss for ShorthandId {
698 #[inline]
699 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
700 where
701 W: Write,
702 {
703 dest.write_str(self.name())
704 }
705}
706
707impl ShorthandId {
708 #[inline]
710 pub fn name(&self) -> &'static str {
711 NonCustomPropertyId::from(*self).name()
712 }
713
714 #[cfg(feature = "gecko")]
716 #[inline]
717 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
718 NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
719 }
720
721 #[cfg(feature = "gecko")]
723 #[inline]
724 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
725 NonCustomPropertyId::from_noncustomcsspropertyid(id)?
726 .unaliased()
727 .as_shorthand()
728 }
729
730 pub fn get_shorthand_appendable_value<'a, 'b: 'a>(
734 self,
735 declarations: &'a [&'b PropertyDeclaration],
736 ) -> Option<AppendableValue<'a, 'b>> {
737 let first_declaration = declarations.first()?;
738 let rest = || declarations.iter().skip(1);
739
740 if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
742 if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
743 return Some(AppendableValue::Css(css));
744 }
745 return None;
746 }
747
748 if let Some(keyword) = first_declaration.get_css_wide_keyword() {
750 if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {
751 return Some(AppendableValue::Css(keyword.to_str()));
752 }
753 return None;
754 }
755
756 if self == ShorthandId::All {
757 return None;
759 }
760
761 if declarations
763 .iter()
764 .all(|d| d.may_serialize_as_part_of_shorthand())
765 {
766 return Some(AppendableValue::DeclarationsForShorthand(
767 self,
768 declarations,
769 ));
770 }
771
772 None
773 }
774
775 #[inline]
777 pub fn is_legacy_shorthand(self) -> bool {
778 self.flags().contains(PropertyFlags::IS_LEGACY_SHORTHAND)
779 }
780
781 #[inline]
783 pub fn allows_disabled_subproperties(self) -> bool {
784 self.flags()
785 .contains(PropertyFlags::ALLOWS_DISABLED_SUBPROPERTIES)
786 }
787}
788
789pub const ARBITRARY_SUBSTITUTION_FUNCTIONS: &[&str] = SubstitutionFunctionKind::VARIANTS;
791
792fn parse_non_custom_property_declaration_value_into(
793 declarations: &mut SourcePropertyDeclaration,
794 context: &ParserContext,
795 input: &mut Parser,
796 start: &cssparser::ParserState,
797 parse_entirely_into: impl FnOnce(
798 &mut SourcePropertyDeclaration,
799 &mut Parser,
800 ) -> Result<(), ParseError>,
801 parsed_wide_keyword: impl FnOnce(&mut SourcePropertyDeclaration, CSSWideKeyword),
802 parsed_custom: impl FnOnce(&mut SourcePropertyDeclaration, custom_properties::VariableValue),
803) -> Result<(), ParseError> {
804 let mut starts_with_curly_block = false;
805 input.look_for_arbitrary_substitution_functions(ARBITRARY_SUBSTITUTION_FUNCTIONS);
806 if let Ok(token) = input.next() {
807 match token {
808 cssparser::Token::Ident(ident) => {
809 if let Ok(wk) = CSSWideKeyword::from_ident(ident)
810 && input.expect_exhausted().is_ok()
811 {
812 parsed_wide_keyword(declarations, wk);
813 let _ = input.seen_arbitrary_substitution_functions();
814 return Ok(());
815 }
816 },
817 cssparser::Token::CurlyBracketBlock => {
818 starts_with_curly_block = true;
819 },
820 _ => {},
821 }
822 };
823
824 input.reset(start);
825
826 let mut saw_arbitrary_substitution_functions = false;
827 let err = match parse_entirely_into(declarations, input) {
828 Ok(()) => {
829 saw_arbitrary_substitution_functions = input.seen_arbitrary_substitution_functions();
830 if !saw_arbitrary_substitution_functions {
831 return Ok(());
832 }
833 ParseError::custom(style_traits::StyleParseErrorKind::UnspecifiedError)
834 },
835 Err(e) => e,
836 };
837
838 let start_pos = start.position();
840 let mut at_start = start_pos == input.position();
841 let mut invalid = false;
842 while let Ok(token) = input.next() {
843 if matches!(token, cssparser::Token::CurlyBracketBlock) {
844 if !starts_with_curly_block || !at_start {
845 invalid = true;
846 break;
847 }
848 } else if starts_with_curly_block {
849 invalid = true;
850 break;
851 }
852 at_start = false;
853 }
854 saw_arbitrary_substitution_functions =
855 saw_arbitrary_substitution_functions || input.seen_arbitrary_substitution_functions();
856 if !saw_arbitrary_substitution_functions || invalid {
857 return Err(err);
858 }
859 input.reset(start);
860 let value = custom_properties::VariableValue::parse(context, input)?;
861 parsed_custom(declarations, value);
862 Ok(())
863}
864
865impl PropertyDeclaration {
866 fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option<&str> {
867 match *self {
868 PropertyDeclaration::WithVariables(ref declaration) => {
869 let s = declaration.value.from_shorthand?;
870 if s != shorthand {
871 return None;
872 }
873 Some(&*declaration.value.variable_value.css)
874 },
875 _ => None,
876 }
877 }
878
879 #[inline]
881 pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
882 Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
883 }
884
885 #[inline]
887 pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
888 match *self {
889 PropertyDeclaration::CSSWideKeyword(ref declaration) => Some(declaration.keyword),
890 _ => None,
891 }
892 }
893
894 pub fn is_unparsed(&self) -> bool {
897 matches!(*self, PropertyDeclaration::WithVariables(..))
898 }
899
900 pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
913 match *self {
914 PropertyDeclaration::CSSWideKeyword(..) | PropertyDeclaration::WithVariables(..) => {
915 false
916 },
917 PropertyDeclaration::Custom(..) => {
918 unreachable!("Serializing a custom property as part of shorthand?")
919 },
920 _ => true,
921 }
922 }
923
924 pub fn is_animatable(&self) -> bool {
926 self.id().is_animatable()
927 }
928
929 pub fn is_custom(&self) -> bool {
932 matches!(*self, PropertyDeclaration::Custom(..))
933 }
934
935 pub fn parse_into(
946 declarations: &mut SourcePropertyDeclaration,
947 id: PropertyId,
948 context: &mut ParserContext,
949 input: &mut Parser,
950 ) -> Result<(), ParseError> {
951 assert!(declarations.is_empty());
952 debug_assert!(
953 id.allowed_in(context),
954 "{id:?} is not allowed in this context {:?}",
955 context.rule_types()
956 );
957 input.skip_whitespace();
958
959 let start = input.state();
960 let non_custom_id = match id {
961 PropertyId::Custom(property_name) => {
962 let value = match input.try_parse(CSSWideKeyword::parse) {
963 Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
964 Err(()) => {
965 CustomDeclarationValue::Unparsed(Arc::new(context.with_property_id(
966 Some(PropertyIdRef::from(&property_name)),
967 |context| custom_properties::VariableValue::parse(context, input),
968 )?))
969 },
970 };
971 declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
972 name: property_name,
973 value,
974 }));
975 return Ok(());
976 },
977 PropertyId::NonCustom(id) => id,
978 };
979
980 context.with_property_id(Some(PropertyIdRef::from(non_custom_id)), |context| {
981 match non_custom_id.longhand_or_shorthand() {
982 Ok(longhand_id) => {
983 parse_non_custom_property_declaration_value_into(
984 declarations,
985 context,
986 input,
987 &start,
988 |declarations, input| {
989 let decl = input
990 .parse_entirely(|input| longhand_id.parse_value(context, input))?;
991 declarations.push(decl);
992 Ok(())
993 },
994 |declarations, wk| {
995 declarations
996 .push(PropertyDeclaration::css_wide_keyword(longhand_id, wk));
997 },
998 |declarations, variable_value| {
999 declarations.push(PropertyDeclaration::WithVariables(
1000 VariableDeclaration {
1001 id: longhand_id,
1002 value: Arc::new(UnparsedValue {
1003 variable_value,
1004 from_shorthand: None,
1005 }),
1006 },
1007 ))
1008 },
1009 )?;
1010 },
1011 Err(shorthand_id) => {
1012 parse_non_custom_property_declaration_value_into(
1013 declarations,
1014 context,
1015 input,
1016 &start,
1017 |declarations, input| shorthand_id.parse_into(declarations, context, input),
1020 |declarations, wk| {
1021 if shorthand_id == ShorthandId::All {
1022 declarations.all_shorthand = AllShorthand::CSSWideKeyword(wk)
1023 } else {
1024 for longhand in shorthand_id.longhands() {
1025 declarations
1026 .push(PropertyDeclaration::css_wide_keyword(longhand, wk));
1027 }
1028 }
1029 },
1030 |declarations, variable_value| {
1031 let unparsed = Arc::new(UnparsedValue {
1032 variable_value,
1033 from_shorthand: Some(shorthand_id),
1034 });
1035 if shorthand_id == ShorthandId::All {
1036 declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
1037 } else {
1038 for id in shorthand_id.longhands() {
1039 declarations.push(PropertyDeclaration::WithVariables(
1040 VariableDeclaration {
1041 id,
1042 value: unparsed.clone(),
1043 },
1044 ))
1045 }
1046 }
1047 },
1048 )?;
1049 },
1050 }
1051 if let Some(use_counters) = context.use_counters {
1052 use_counters.non_custom_properties.record(non_custom_id);
1053 }
1054 Ok(())
1055 })
1056 }
1057}
1058
1059#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1061pub enum OwnedPropertyDeclarationId {
1062 Longhand(LonghandId),
1064 Custom(custom_properties::Name),
1066}
1067
1068impl OwnedPropertyDeclarationId {
1069 #[inline]
1071 pub fn is_logical(&self) -> bool {
1072 self.as_borrowed().is_logical()
1073 }
1074
1075 #[inline]
1077 pub fn as_borrowed(&self) -> PropertyDeclarationId<'_> {
1078 match self {
1079 Self::Longhand(id) => PropertyDeclarationId::Longhand(*id),
1080 Self::Custom(name) => PropertyDeclarationId::Custom(name),
1081 }
1082 }
1083
1084 #[cfg(feature = "gecko")]
1086 #[inline]
1087 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
1088 Some(match PropertyId::from_gecko_css_property_id(property)? {
1089 PropertyId::Custom(name) => Self::Custom(name),
1090 PropertyId::NonCustom(id) => Self::Longhand(id.as_longhand()?),
1091 })
1092 }
1093}
1094
1095#[derive(Clone, Copy, Debug, PartialEq, MallocSizeOf)]
1098pub enum PropertyDeclarationId<'a> {
1099 Longhand(LonghandId),
1101 Custom(&'a custom_properties::Name),
1103}
1104
1105impl<'a> ToCss for PropertyDeclarationId<'a> {
1106 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1107 where
1108 W: Write,
1109 {
1110 match *self {
1111 PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
1112 PropertyDeclarationId::Custom(name) => {
1113 dest.write_str("--")?;
1114 serialize_atom_name(name, dest)
1115 },
1116 }
1117 }
1118}
1119
1120impl<'a> PropertyDeclarationId<'a> {
1121 #[inline(always)]
1123 pub fn flags(&self) -> PropertyFlags {
1124 match self {
1125 Self::Longhand(id) => id.flags(),
1126 Self::Custom(_) => PropertyFlags::empty(),
1127 }
1128 }
1129
1130 pub fn to_owned(&self) -> OwnedPropertyDeclarationId {
1132 match self {
1133 PropertyDeclarationId::Longhand(id) => OwnedPropertyDeclarationId::Longhand(*id),
1134 PropertyDeclarationId::Custom(name) => {
1135 OwnedPropertyDeclarationId::Custom((*name).clone())
1136 },
1137 }
1138 }
1139
1140 pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
1143 match *self {
1144 PropertyDeclarationId::Longhand(id) => match *other {
1145 PropertyId::NonCustom(non_custom_id) => id.is_or_is_longhand_of(non_custom_id),
1146 PropertyId::Custom(_) => false,
1147 },
1148 PropertyDeclarationId::Custom(name) => {
1149 matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
1150 },
1151 }
1152 }
1153
1154 pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
1157 match *self {
1158 PropertyDeclarationId::Longhand(ref id) => id.is_longhand_of(shorthand),
1159 _ => false,
1160 }
1161 }
1162
1163 pub fn name(&self) -> Cow<'static, str> {
1165 match *self {
1166 PropertyDeclarationId::Longhand(id) => id.name().into(),
1167 PropertyDeclarationId::Custom(name) => {
1168 let mut s = String::new();
1169 write!(&mut s, "--{}", name).unwrap();
1170 s.into()
1171 },
1172 }
1173 }
1174
1175 #[inline]
1177 pub fn as_longhand(&self) -> Option<LonghandId> {
1178 match *self {
1179 PropertyDeclarationId::Longhand(id) => Some(id),
1180 _ => None,
1181 }
1182 }
1183
1184 #[inline]
1186 pub fn is_logical(&self) -> bool {
1187 match self {
1188 PropertyDeclarationId::Longhand(id) => id.is_logical(),
1189 PropertyDeclarationId::Custom(_) => false,
1190 }
1191 }
1192
1193 #[inline]
1198 pub fn to_physical(&self, wm: WritingMode) -> Self {
1199 match self {
1200 Self::Longhand(id) => Self::Longhand(id.to_physical(wm)),
1201 Self::Custom(_) => *self,
1202 }
1203 }
1204
1205 #[inline]
1207 pub fn is_animatable(&self) -> bool {
1208 match self {
1209 Self::Longhand(id) => id.is_animatable(),
1210 Self::Custom(_) => true,
1211 }
1212 }
1213
1214 #[inline]
1216 pub fn is_discrete_animatable(&self) -> bool {
1217 match self {
1218 Self::Longhand(longhand) => longhand.is_discrete_animatable(),
1219 Self::Custom(_) => true,
1221 }
1222 }
1223
1224 #[cfg(feature = "gecko")]
1227 #[inline]
1228 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
1229 match self {
1230 PropertyDeclarationId::Longhand(id) => id.to_noncustomcsspropertyid(),
1231 PropertyDeclarationId::Custom(_) => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1232 }
1233 }
1234
1235 #[cfg(feature = "gecko")]
1240 #[inline]
1241 pub fn to_gecko_css_property_id(&self) -> CSSPropertyId {
1242 match self {
1243 Self::Longhand(id) => CSSPropertyId {
1244 mId: id.to_noncustomcsspropertyid(),
1245 mCustomName: RefPtr::null(),
1246 },
1247 Self::Custom(name) => {
1248 let mut property_id = CSSPropertyId {
1249 mId: NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1250 mCustomName: RefPtr::null(),
1251 };
1252 property_id.mCustomName.mRawPtr = (*name).clone().into_addrefed();
1253 property_id
1254 },
1255 }
1256 }
1257}
1258
1259pub trait IndexedId: Copy {
1262 const COUNT: usize;
1264 unsafe fn from_index_release_unchecked(index: usize) -> Self;
1267 fn to_index(self) -> usize;
1269}
1270
1271impl IndexedId for NonCustomPropertyId {
1272 const COUNT: usize = property_counts::NON_CUSTOM;
1273
1274 #[inline(always)]
1275 unsafe fn from_index_release_unchecked(index: usize) -> Self {
1276 debug_assert!(index < Self::COUNT);
1277 NonCustomPropertyId(index as u16)
1278 }
1279
1280 #[inline(always)]
1281 fn to_index(self) -> usize {
1282 self.0 as usize
1283 }
1284}
1285
1286impl IndexedId for PrioritaryPropertyId {
1287 const COUNT: usize = property_counts::PRIORITARY;
1288
1289 #[inline(always)]
1290 unsafe fn from_index_release_unchecked(index: usize) -> Self {
1291 unsafe {
1292 debug_assert!(index < Self::COUNT);
1293 std::mem::transmute(index as u8)
1294 }
1295 }
1296
1297 #[inline(always)]
1298 fn to_index(self) -> usize {
1299 self as usize
1300 }
1301}
1302
1303impl IndexedId for LonghandId {
1304 const COUNT: usize = property_counts::LONGHANDS;
1305
1306 #[inline(always)]
1307 unsafe fn from_index_release_unchecked(index: usize) -> Self {
1308 unsafe {
1309 debug_assert!(index < Self::COUNT);
1310 std::mem::transmute(index as u16)
1311 }
1312 }
1313
1314 #[inline(always)]
1315 fn to_index(self) -> usize {
1316 self as usize
1317 }
1318}
1319
1320pub type NonCustomPropertyIdSet =
1322 IdSet<NonCustomPropertyId, { (property_counts::NON_CUSTOM - 1 + 32) / 32 }>;
1323pub type NonCustomPropertyIdSetIterator<'a> = IdSetIterator<'a, NonCustomPropertyId>;
1325pub type PrioritaryPropertyIdSet =
1327 IdSet<PrioritaryPropertyId, { (property_counts::PRIORITARY - 1 + 32) / 32 }>;
1328pub type PrioritaryPropertyIdSetIterator<'a> = IdSetIterator<'a, PrioritaryPropertyId>;
1330pub type LonghandIdSet = IdSet<LonghandId, { (property_counts::LONGHANDS - 1 + 32) / 32 }>;
1332pub type LonghandIdSetIterator<'a> = IdSetIterator<'a, LonghandId>;
1334
1335pub struct IdSet<Id: IndexedId, const W: usize> {
1342 storage: [u32; W],
1343 _phantom: std::marker::PhantomData<Id>,
1344}
1345
1346impl<Id: IndexedId, const W: usize> Clone for IdSet<Id, W> {
1347 #[inline]
1348 fn clone(&self) -> Self {
1349 *self
1350 }
1351}
1352
1353impl<Id: IndexedId, const W: usize> Copy for IdSet<Id, W> {}
1354
1355impl<Id: IndexedId, const W: usize> Default for IdSet<Id, W> {
1356 #[inline]
1357 fn default() -> Self {
1358 Self {
1359 storage: [0; W],
1360 _phantom: std::marker::PhantomData,
1361 }
1362 }
1363}
1364
1365impl<Id: IndexedId, const W: usize> PartialEq for IdSet<Id, W> {
1366 #[inline]
1367 fn eq(&self, other: &Self) -> bool {
1368 self.storage == other.storage
1369 }
1370}
1371
1372impl<Id: IndexedId, const W: usize> fmt::Debug for IdSet<Id, W> {
1373 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1374 self.storage.fmt(f)
1375 }
1376}
1377
1378impl<Id: IndexedId, const W: usize> malloc_size_of::MallocSizeOf for IdSet<Id, W> {
1379 #[inline(always)]
1380 fn size_of(&self, _: &mut malloc_size_of::MallocSizeOfOps) -> usize {
1381 0
1382 }
1383}
1384
1385impl<Id: IndexedId, const W: usize> IdSet<Id, W> {
1386 #[inline]
1388 pub fn new() -> Self {
1389 Self::default()
1390 }
1391
1392 pub(crate) const fn from_storage(storage: [u32; W]) -> Self {
1394 Self {
1395 storage,
1396 _phantom: std::marker::PhantomData,
1397 }
1398 }
1399
1400 #[inline]
1402 pub fn insert(&mut self, id: Id) {
1403 let bit = id.to_index();
1404 self.storage[bit / 32] |= 1 << (bit % 32);
1405 }
1406
1407 #[inline]
1409 pub fn remove(&mut self, id: Id) {
1410 let bit = id.to_index();
1411 self.storage[bit / 32] &= !(1 << (bit % 32));
1412 }
1413
1414 #[inline]
1416 pub fn contains(&self, id: Id) -> bool {
1417 let bit = id.to_index();
1418 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1419 }
1420
1421 pub fn iter(&self) -> IdSetIterator<'_, Id> {
1423 IdSetIterator {
1424 chunks: &self.storage,
1425 cur_chunk: 0,
1426 cur_bit: 0,
1427 _phantom: std::marker::PhantomData,
1428 }
1429 }
1430
1431 pub fn contains_all(&self, other: &Self) -> bool {
1433 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1434 if (*self_cell & *other_cell) != *other_cell {
1435 return false;
1436 }
1437 }
1438 true
1439 }
1440
1441 pub fn contains_any(&self, other: &Self) -> bool {
1443 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1444 if (*self_cell & *other_cell) != 0 {
1445 return true;
1446 }
1447 }
1448 false
1449 }
1450
1451 #[inline]
1453 pub fn remove_all(&mut self, other: &Self) {
1454 for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
1455 *self_cell &= !*other_cell;
1456 }
1457 }
1458
1459 #[inline]
1461 pub fn clear(&mut self) {
1462 self.storage.fill(0);
1463 }
1464
1465 #[inline]
1467 pub fn is_empty(&self) -> bool {
1468 self.storage.iter().all(|c| *c == 0)
1469 }
1470}
1471
1472to_shmem::impl_trivial_to_shmem!(LonghandIdSet);
1473impl LonghandIdSet {
1474 #[inline]
1476 pub fn contains_any_reset(&self) -> bool {
1477 self.contains_any(Self::reset())
1478 }
1479}
1480
1481pub struct IdSetIterator<'a, Id: IndexedId> {
1483 chunks: &'a [u32],
1484 cur_chunk: u32,
1485 cur_bit: u32, _phantom: std::marker::PhantomData<Id>,
1487}
1488
1489impl<'a, Id: IndexedId> Iterator for IdSetIterator<'a, Id> {
1490 type Item = Id;
1491
1492 fn next(&mut self) -> Option<Self::Item> {
1493 loop {
1494 debug_assert!(self.cur_bit < 32);
1495 let cur_chunk = self.cur_chunk;
1496 let cur_bit = self.cur_bit;
1497 let chunk = *self.chunks.get(cur_chunk as usize)?;
1498 let next_bit = (chunk >> cur_bit).trailing_zeros();
1499 if next_bit == 32 {
1500 self.cur_bit = 0;
1502 self.cur_chunk += 1;
1503 continue;
1504 }
1505 debug_assert!(cur_bit + next_bit < 32);
1506 let index = (cur_chunk * 32 + cur_bit + next_bit) as usize;
1507 debug_assert!(index < Id::COUNT);
1508 let id = unsafe { Id::from_index_release_unchecked(index) };
1509 self.cur_bit += next_bit + 1;
1510 if self.cur_bit == 32 {
1511 self.cur_bit = 0;
1512 self.cur_chunk += 1;
1513 }
1514 return Some(id);
1515 }
1516 }
1517}
1518
1519pub type SubpropertiesVec<T> = ArrayVec<T, { property_counts::MAX_SHORTHAND_EXPANDED }>;
1521
1522#[derive(Default)]
1526pub struct SourcePropertyDeclaration {
1527 pub declarations: SubpropertiesVec<PropertyDeclaration>,
1529 pub all_shorthand: AllShorthand,
1531}
1532
1533size_of_test!(SourcePropertyDeclaration, 632);
1536
1537impl SourcePropertyDeclaration {
1538 #[inline]
1540 pub fn with_one(decl: PropertyDeclaration) -> Self {
1541 let mut result = Self::default();
1542 result.declarations.push(decl);
1543 result
1544 }
1545
1546 pub fn drain(&mut self) -> SourcePropertyDeclarationDrain<'_> {
1548 SourcePropertyDeclarationDrain {
1549 declarations: self.declarations.drain(..),
1550 all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
1551 }
1552 }
1553
1554 pub fn clear(&mut self) {
1556 self.declarations.clear();
1557 self.all_shorthand = AllShorthand::NotSet;
1558 }
1559
1560 pub fn is_empty(&self) -> bool {
1562 self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
1563 }
1564
1565 pub fn push(&mut self, declaration: PropertyDeclaration) {
1567 let _result = self.declarations.try_push(declaration);
1568 debug_assert!(_result.is_ok());
1569 }
1570}
1571
1572pub struct SourcePropertyDeclarationDrain<'a> {
1574 pub declarations:
1576 ArrayVecDrain<'a, PropertyDeclaration, { property_counts::MAX_SHORTHAND_EXPANDED }>,
1577 pub all_shorthand: AllShorthand,
1579}
1580
1581#[derive(Debug, Eq, PartialEq, ToShmem)]
1583pub struct UnparsedValue {
1584 pub(super) variable_value: custom_properties::VariableValue,
1586 from_shorthand: Option<ShorthandId>,
1588}
1589
1590impl ToCss for UnparsedValue {
1591 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1592 where
1593 W: Write,
1594 {
1595 if self.from_shorthand.is_none() {
1597 self.variable_value.to_css(dest)?;
1598 }
1599 Ok(())
1600 }
1601}
1602
1603impl ToTyped for UnparsedValue {
1604 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
1605 if self.from_shorthand.is_none() {
1606 self.variable_value.to_typed(dest)?;
1607 return Ok(());
1608 }
1609 Err(())
1610 }
1611}
1612
1613pub type ShorthandsWithPropertyReferencesCache =
1620 FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
1621
1622impl UnparsedValue {
1623 fn substitute_variables<'cache>(
1624 &self,
1625 longhand_id: LonghandId,
1626 substitution_functions: &ComputedSubstitutionFunctions,
1627 stylist: &Stylist,
1628 computed_context: &computed::Context,
1629 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
1630 attribute_tracker: &mut AttributeTracker,
1631 ) -> Cow<'cache, PropertyDeclaration> {
1632 let invalid_at_computed_value_time = || {
1633 let keyword = if longhand_id.inherited() {
1634 CSSWideKeyword::Inherit
1635 } else {
1636 CSSWideKeyword::Initial
1637 };
1638 Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
1639 };
1640
1641 if computed_context
1642 .builder
1643 .invalid_non_custom_properties
1644 .contains(longhand_id)
1645 {
1646 return invalid_at_computed_value_time();
1647 }
1648
1649 if let Some(shorthand_id) = self.from_shorthand {
1650 let key = (shorthand_id, longhand_id);
1651 if shorthand_cache.contains_key(&key) {
1652 return Cow::Borrowed(&shorthand_cache[&key]);
1657 }
1658 }
1659
1660 let property_id = PropertyIdRef::from(match self.from_shorthand {
1661 Some(shorthand_id) => NonCustomPropertyId::from(shorthand_id),
1662 None => NonCustomPropertyId::from(longhand_id),
1663 });
1664
1665 let SubstitutionResult { css, attr_taint } = match substitution::substitute(
1666 &self.variable_value,
1667 Some(property_id),
1668 substitution_functions,
1669 stylist,
1670 computed_context,
1671 attribute_tracker,
1672 ) {
1673 Ok(css) => css,
1674 Err(..) => return invalid_at_computed_value_time(),
1675 };
1676
1677 let context = ParserContext::new_with_property_id(
1688 Origin::Author,
1689 &self.variable_value.url_data,
1690 None,
1691 ParsingMode::DEFAULT,
1692 computed_context.quirks_mode,
1693 Default::default(),
1694 None,
1695 None,
1696 attr_taint,
1697 Some(property_id),
1698 );
1699
1700 let mut input = Parser::new(&css);
1701 input.skip_whitespace();
1702
1703 if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
1704 return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
1705 }
1706
1707 let shorthand = match self.from_shorthand {
1708 None => {
1709 return match input.parse_entirely(|input| longhand_id.parse_value(&context, input))
1710 {
1711 Ok(decl) => Cow::Owned(decl),
1712 Err(..) => invalid_at_computed_value_time(),
1713 };
1714 },
1715 Some(shorthand) => shorthand,
1716 };
1717
1718 let mut decls = SourcePropertyDeclaration::default();
1719 if shorthand
1721 .parse_into(&mut decls, &context, &mut input)
1722 .is_err()
1723 {
1724 return invalid_at_computed_value_time();
1725 }
1726
1727 for declaration in decls.declarations.drain(..) {
1728 let longhand = declaration.id().as_longhand().unwrap();
1729 if longhand.is_logical() {
1730 let writing_mode = computed_context.builder.writing_mode;
1731 shorthand_cache.insert(
1732 (shorthand, longhand.to_physical(writing_mode)),
1733 declaration.clone(),
1734 );
1735 }
1736 shorthand_cache.insert((shorthand, longhand), declaration);
1737 }
1738
1739 let key = (shorthand, longhand_id);
1740 match shorthand_cache.get(&key) {
1741 Some(decl) => Cow::Borrowed(decl),
1742 None => invalid_at_computed_value_time(),
1754 }
1755 }
1756}
1757#[derive(Default)]
1759pub enum AllShorthand {
1760 #[default]
1762 NotSet,
1763 CSSWideKeyword(CSSWideKeyword),
1765 WithVariables(Arc<UnparsedValue>),
1767}
1768
1769impl AllShorthand {
1770 #[inline]
1772 pub fn declarations(&self) -> AllShorthandDeclarationIterator<'_> {
1773 AllShorthandDeclarationIterator {
1774 all_shorthand: self,
1775 longhands: ShorthandId::All.longhands(),
1776 }
1777 }
1778}
1779
1780pub struct AllShorthandDeclarationIterator<'a> {
1782 all_shorthand: &'a AllShorthand,
1783 longhands: NonCustomPropertyIterator<LonghandId>,
1784}
1785
1786impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
1787 type Item = PropertyDeclaration;
1788
1789 #[inline]
1790 fn next(&mut self) -> Option<Self::Item> {
1791 match *self.all_shorthand {
1792 AllShorthand::NotSet => None,
1793 AllShorthand::CSSWideKeyword(ref keyword) => Some(
1794 PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword),
1795 ),
1796 AllShorthand::WithVariables(ref unparsed) => {
1797 Some(PropertyDeclaration::WithVariables(VariableDeclaration {
1798 id: self.longhands.next()?,
1799 value: unparsed.clone(),
1800 }))
1801 },
1802 }
1803 }
1804}
1805
1806pub struct NonCustomPropertyIterator<Item: 'static> {
1809 filter: bool,
1810 iter: std::slice::Iter<'static, Item>,
1811}
1812
1813impl<Item> Iterator for NonCustomPropertyIterator<Item>
1814where
1815 Item: 'static + Copy + Into<NonCustomPropertyId>,
1816{
1817 type Item = Item;
1818
1819 fn next(&mut self) -> Option<Self::Item> {
1820 loop {
1821 let id = *self.iter.next()?;
1822 if !self.filter || id.into().enabled_for_all_content() {
1823 return Some(id);
1824 }
1825 }
1826 }
1827}
1828
1829pub struct TransitionPropertyIterator<'a> {
1831 style: &'a ComputedValues,
1832 index_range: core::ops::Range<usize>,
1833 longhand_iterator: Option<NonCustomPropertyIterator<LonghandId>>,
1834}
1835
1836impl<'a> TransitionPropertyIterator<'a> {
1837 pub fn from_style(style: &'a ComputedValues) -> Self {
1839 Self {
1840 style,
1841 index_range: 0..style.get_ui().transition_property_count(),
1842 longhand_iterator: None,
1843 }
1844 }
1845}
1846
1847pub struct TransitionPropertyIteration {
1849 pub property: OwnedPropertyDeclarationId,
1851 pub index: usize,
1854}
1855
1856impl<'a> Iterator for TransitionPropertyIterator<'a> {
1857 type Item = TransitionPropertyIteration;
1858
1859 fn next(&mut self) -> Option<Self::Item> {
1860 use crate::values::computed::TransitionProperty;
1861 loop {
1862 if let Some(ref mut longhand_iterator) = self.longhand_iterator {
1863 if let Some(longhand_id) = longhand_iterator.next() {
1864 return Some(TransitionPropertyIteration {
1865 property: OwnedPropertyDeclarationId::Longhand(longhand_id),
1866 index: self.index_range.start - 1,
1867 });
1868 }
1869 self.longhand_iterator = None;
1870 }
1871
1872 let index = self.index_range.next()?;
1873 match self.style.get_ui().transition_property_at(index) {
1874 TransitionProperty::NonCustom(id) => {
1875 match id.longhand_or_shorthand() {
1876 Ok(longhand_id) => {
1877 return Some(TransitionPropertyIteration {
1878 property: OwnedPropertyDeclarationId::Longhand(longhand_id),
1879 index,
1880 });
1881 },
1882 Err(shorthand_id) => {
1883 self.longhand_iterator = Some(shorthand_id.longhands());
1887 },
1888 }
1889 },
1890 TransitionProperty::Custom(name) => {
1891 return Some(TransitionPropertyIteration {
1892 property: OwnedPropertyDeclarationId::Custom(name),
1893 index,
1894 });
1895 },
1896 TransitionProperty::Unsupported(..) => {},
1897 }
1898 }
1899 }
1900}