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, SubstitutionResult};
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;
34use crate::stylesheets::Origin;
35use crate::stylist::Stylist;
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 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 APPLIES_TO_HIGHLIGHT = 1 << 6;
68 const IS_LEGACY_SHORTHAND = 1 << 7;
72 const ALLOWS_DISABLED_SUBPROPERTIES = 1 << 8;
74
75 const CAN_ANIMATE_ON_COMPOSITOR = 0;
81 const SCROLL_LINKED_EFFECTIVE = 0;
83 const AFFECTS_LAYOUT = 0;
85 #[allow(missing_docs)]
86 const AFFECTS_OVERFLOW = 0;
87 #[allow(missing_docs)]
88 const AFFECTS_PAINT = 0;
89 }
90}
91
92#[derive(
94 Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
95)]
96pub enum CSSWideKeyword {
97 Initial,
99 Inherit,
101 Unset,
103 Revert,
105 RevertLayer,
107 RevertRule,
109}
110
111impl CSSWideKeyword {
112 pub fn to_str(&self) -> &'static str {
114 match *self {
115 Self::Initial => "initial",
116 Self::Inherit => "inherit",
117 Self::Unset => "unset",
118 Self::Revert => "revert",
119 Self::RevertLayer => "revert-layer",
120 Self::RevertRule => "revert-rule",
121 }
122 }
123
124 pub fn from_ident(ident: &str) -> Result<Self, ()> {
126 Ok(match_ignore_ascii_case! { ident,
127 "initial" => Self::Initial,
128 "inherit" => Self::Inherit,
129 "unset" => Self::Unset,
130 "revert" => Self::Revert,
131 "revert-layer" => Self::RevertLayer,
132 "revert-rule" if crate::pref!("layout.css.revert-rule.enabled") => Self::RevertRule,
133 _ => return Err(()),
134 })
135 }
136
137 pub fn parse(input: &mut Parser) -> Result<Self, ()> {
139 let keyword = {
140 let ident = input.expect_ident().map_err(|_| ())?;
141 Self::from_ident(ident)?
142 };
143 input.expect_exhausted().map_err(|_| ())?;
144 Ok(keyword)
145 }
146
147 pub fn revert_kind(self) -> Option<RevertKind> {
149 Some(match self {
150 Self::Initial | Self::Inherit | Self::Unset => return None,
151 Self::Revert => RevertKind::Origin,
152 Self::RevertLayer => RevertKind::Layer,
153 Self::RevertRule => RevertKind::Rule,
154 })
155 }
156}
157
158#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf)]
160pub struct WideKeywordDeclaration {
161 #[css(skip)]
162 id: LonghandId,
163 pub keyword: CSSWideKeyword,
165}
166
167impl ToTyped for WideKeywordDeclaration {
170 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
171 self.keyword.to_typed(dest)
172 }
173}
174
175#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
177pub struct VariableDeclaration {
178 #[css(skip)]
180 id: LonghandId,
181 #[ignore_malloc_size_of = "Arc"]
183 pub value: Arc<UnparsedValue>,
184}
185
186#[derive(Clone, PartialEq, ToCss, ToShmem, ToTyped)]
189pub enum CustomDeclarationValue {
190 Unparsed(Arc<custom_properties::SpecifiedValue>),
192 Parsed(Arc<crate::properties_and_values::value::SpecifiedValue>),
194 CSSWideKeyword(CSSWideKeyword),
196}
197
198#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
200pub struct CustomDeclaration {
201 #[css(skip)]
203 pub name: custom_properties::Name,
204 #[ignore_malloc_size_of = "Arc"]
206 pub value: CustomDeclarationValue,
207}
208
209impl fmt::Debug for PropertyDeclaration {
210 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
211 self.id().to_css(&mut CssWriter::new(f))?;
212 f.write_str(": ")?;
213
214 let mut s = CssString::new();
218 self.to_css(&mut s)?;
219 write!(f, "{}", s)
220 }
221}
222
223#[derive(
225 Clone, Copy, Debug, PartialEq, Eq, Hash, ToComputedValue, ToResolvedValue, ToShmem, MallocSizeOf,
226)]
227#[repr(C)]
228pub struct NonCustomPropertyId(u16);
229
230impl ToCss for NonCustomPropertyId {
231 #[inline]
232 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
233 where
234 W: Write,
235 {
236 dest.write_str(self.name())
237 }
238}
239
240impl NonCustomPropertyId {
241 pub fn bit(self) -> usize {
243 self.0 as usize
244 }
245
246 #[cfg(feature = "gecko")]
248 #[inline]
249 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
250 unsafe { mem::transmute(self.0) }
252 }
253
254 #[cfg(feature = "gecko")]
256 #[inline]
257 pub fn from_noncustomcsspropertyid(prop: NonCustomCSSPropertyId) -> Option<Self> {
258 let prop = prop as u16;
259 if prop >= property_counts::NON_CUSTOM as u16 {
260 return None;
261 }
262 Some(NonCustomPropertyId(prop))
264 }
265
266 pub fn unaliased(self) -> Self {
268 let Some(alias_id) = self.as_alias() else {
269 return self;
270 };
271 alias_id.aliased_property()
272 }
273
274 #[inline]
276 pub fn to_property_id(self) -> PropertyId {
277 PropertyId::NonCustom(self)
278 }
279
280 #[inline]
282 pub fn as_longhand(self) -> Option<LonghandId> {
283 if self.0 < property_counts::LONGHANDS as u16 {
284 return Some(unsafe { mem::transmute(self.0) });
285 }
286 None
287 }
288
289 #[inline]
291 pub fn as_shorthand(self) -> Option<ShorthandId> {
292 if self.0 >= property_counts::LONGHANDS as u16
293 && self.0 < property_counts::LONGHANDS_AND_SHORTHANDS as u16
294 {
295 return Some(unsafe { mem::transmute(self.0 - (property_counts::LONGHANDS as u16)) });
296 }
297 None
298 }
299
300 #[inline]
302 pub fn as_alias(self) -> Option<AliasId> {
303 debug_assert!((self.0 as usize) < property_counts::NON_CUSTOM);
304 if self.0 >= property_counts::LONGHANDS_AND_SHORTHANDS as u16 {
305 return Some(unsafe {
306 mem::transmute(self.0 - (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
307 });
308 }
309 None
310 }
311
312 #[inline]
314 pub fn longhand_or_shorthand(self) -> Result<LonghandId, ShorthandId> {
315 let id = self.unaliased();
316 match id.as_longhand() {
317 Some(lh) => Ok(lh),
318 None => Err(id.as_shorthand().unwrap()),
319 }
320 }
321
322 #[inline]
324 pub const fn from_longhand(id: LonghandId) -> Self {
325 Self(id as u16)
326 }
327
328 #[inline]
330 pub const fn from_shorthand(id: ShorthandId) -> Self {
331 Self((id as u16) + (property_counts::LONGHANDS as u16))
332 }
333
334 #[inline]
336 pub const fn from_alias(id: AliasId) -> Self {
337 Self((id as u16) + (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
338 }
339
340 pub fn iter() -> impl Iterator<Item = Self> {
342 (0..property_counts::NON_CUSTOM as u16).map(Self)
343 }
344}
345
346impl From<LonghandId> for NonCustomPropertyId {
347 #[inline]
348 fn from(id: LonghandId) -> Self {
349 Self::from_longhand(id)
350 }
351}
352
353impl From<ShorthandId> for NonCustomPropertyId {
354 #[inline]
355 fn from(id: ShorthandId) -> Self {
356 Self::from_shorthand(id)
357 }
358}
359
360impl From<AliasId> for NonCustomPropertyId {
361 #[inline]
362 fn from(id: AliasId) -> Self {
363 Self::from_alias(id)
364 }
365}
366
367#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
370pub enum PropertyId {
371 NonCustom(NonCustomPropertyId),
373 Custom(custom_properties::Name),
375}
376
377impl ToCss for PropertyId {
378 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
379 where
380 W: Write,
381 {
382 match *self {
383 PropertyId::NonCustom(id) => dest.write_str(id.name()),
384 PropertyId::Custom(ref name) => {
385 dest.write_str("--")?;
386 serialize_atom_name(name, dest)
387 },
388 }
389 }
390}
391
392impl PropertyId {
393 #[inline]
395 pub fn longhand_id(&self) -> Option<LonghandId> {
396 self.non_custom_non_alias_id()?.as_longhand()
397 }
398
399 pub fn is_animatable(&self) -> bool {
401 match self {
402 Self::NonCustom(id) => id.is_animatable(),
403 Self::Custom(_) => true,
404 }
405 }
406
407 pub fn parse_unchecked_for_testing(name: &str) -> Result<Self, ()> {
412 Self::parse_unchecked(name, None)
413 }
414
415 #[inline]
418 pub fn parse_enabled_for_all_content(name: &str) -> Result<Self, ()> {
419 let id = Self::parse_unchecked(name, None)?;
420
421 if !id.enabled_for_all_content() {
422 return Err(());
423 }
424
425 Ok(id)
426 }
427
428 #[inline]
431 pub fn parse(name: &str, context: &ParserContext) -> Result<Self, ()> {
432 let id = Self::parse_unchecked(name, context.use_counters)?;
433 if !id.allowed_in(context) {
434 return Err(());
435 }
436 Ok(id)
437 }
438
439 #[inline]
444 pub fn parse_ignoring_rule_type(name: &str, context: &ParserContext) -> Result<Self, ()> {
445 let id = Self::parse_unchecked(name, None)?;
446 if !id.allowed_in_ignoring_rule_type(context) {
447 return Err(());
448 }
449 Ok(id)
450 }
451
452 #[cfg(feature = "gecko")]
454 #[inline]
455 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
456 Some(NonCustomPropertyId::from_noncustomcsspropertyid(id)?.to_property_id())
457 }
458
459 #[cfg(feature = "gecko")]
461 #[inline]
462 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
463 Some(
464 if property.mId == NonCustomCSSPropertyId::eCSSPropertyExtra_variable {
465 debug_assert!(!property.mCustomName.mRawPtr.is_null());
466 Self::Custom(unsafe { crate::Atom::from_raw(property.mCustomName.mRawPtr) })
467 } else {
468 Self::NonCustom(NonCustomPropertyId::from_noncustomcsspropertyid(
469 property.mId,
470 )?)
471 },
472 )
473 }
474
475 #[inline]
477 pub fn is_shorthand(&self) -> bool {
478 self.as_shorthand().is_ok()
479 }
480
481 pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId<'_>> {
484 match *self {
485 Self::NonCustom(id) => match id.longhand_or_shorthand() {
486 Ok(lh) => Err(PropertyDeclarationId::Longhand(lh)),
487 Err(sh) => Ok(sh),
488 },
489 Self::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),
490 }
491 }
492
493 pub fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
495 match *self {
496 Self::Custom(_) => None,
497 Self::NonCustom(id) => Some(id),
498 }
499 }
500
501 fn non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId> {
504 self.non_custom_id().map(NonCustomPropertyId::unaliased)
505 }
506
507 #[inline]
510 pub fn enabled_for_all_content(&self) -> bool {
511 let id = match self.non_custom_id() {
512 None => return true,
514 Some(id) => id,
515 };
516
517 id.enabled_for_all_content()
518 }
519
520 #[cfg(feature = "gecko")]
524 #[inline]
525 pub fn to_noncustomcsspropertyid_resolving_aliases(&self) -> NonCustomCSSPropertyId {
526 match self.non_custom_non_alias_id() {
527 Some(id) => id.to_noncustomcsspropertyid(),
528 None => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
529 }
530 }
531
532 fn allowed_in(&self, context: &ParserContext) -> bool {
533 let id = match self.non_custom_id() {
534 None => {
536 return !context
537 .nesting_context
538 .rule_types
539 .contains(CssRuleType::PositionTry);
540 },
541 Some(id) => id,
542 };
543 id.allowed_in(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] = &["var", "env", "attr"];
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 if let Ok(token) = input.next() {
806 match token {
807 cssparser::Token::Ident(ident) => {
808 if let Ok(wk) = CSSWideKeyword::from_ident(ident)
809 && input.expect_exhausted().is_ok()
810 {
811 return {
812 parsed_wide_keyword(declarations, wk);
813 Ok(())
814 };
815 }
816 },
817 cssparser::Token::CurlyBracketBlock => {
818 starts_with_curly_block = true;
819 },
820 _ => {},
821 }
822 };
823
824 input.reset(start);
825 input.look_for_arbitrary_substitution_functions(ARBITRARY_SUBSTITUTION_FUNCTIONS);
826
827 let mut saw_arbitrary_substitution_functions = false;
828 let err = match parse_entirely_into(declarations, input) {
829 Ok(()) => {
830 saw_arbitrary_substitution_functions = input.seen_arbitrary_substitution_functions();
831 if !saw_arbitrary_substitution_functions {
832 return Ok(());
833 }
834 ParseError::custom(style_traits::StyleParseErrorKind::UnspecifiedError)
835 },
836 Err(e) => e,
837 };
838
839 let start_pos = start.position();
841 let mut at_start = start_pos == input.position();
842 let mut invalid = false;
843 while let Ok(token) = input.next() {
844 if matches!(token, cssparser::Token::CurlyBracketBlock) {
845 if !starts_with_curly_block || !at_start {
846 invalid = true;
847 break;
848 }
849 } else if starts_with_curly_block {
850 invalid = true;
851 break;
852 }
853 at_start = false;
854 }
855 saw_arbitrary_substitution_functions =
856 saw_arbitrary_substitution_functions || input.seen_arbitrary_substitution_functions();
857 if !saw_arbitrary_substitution_functions || invalid {
858 return Err(err);
859 }
860 input.reset(start);
861 let value = custom_properties::VariableValue::parse(
862 input,
863 Some(&context.namespaces.prefixes),
864 context.url_data,
865 )?;
866 parsed_custom(declarations, value);
867 Ok(())
868}
869
870impl PropertyDeclaration {
871 fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option<&str> {
872 match *self {
873 PropertyDeclaration::WithVariables(ref declaration) => {
874 let s = declaration.value.from_shorthand?;
875 if s != shorthand {
876 return None;
877 }
878 Some(&*declaration.value.variable_value.css)
879 },
880 _ => None,
881 }
882 }
883
884 #[inline]
886 pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
887 Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
888 }
889
890 #[inline]
892 pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
893 match *self {
894 PropertyDeclaration::CSSWideKeyword(ref declaration) => Some(declaration.keyword),
895 _ => None,
896 }
897 }
898
899 pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
912 match *self {
913 PropertyDeclaration::CSSWideKeyword(..) | PropertyDeclaration::WithVariables(..) => {
914 false
915 },
916 PropertyDeclaration::Custom(..) => {
917 unreachable!("Serializing a custom property as part of shorthand?")
918 },
919 _ => true,
920 }
921 }
922
923 pub fn is_animatable(&self) -> bool {
925 self.id().is_animatable()
926 }
927
928 pub fn is_custom(&self) -> bool {
931 matches!(*self, PropertyDeclaration::Custom(..))
932 }
933
934 pub fn parse_into(
945 declarations: &mut SourcePropertyDeclaration,
946 id: PropertyId,
947 context: &mut ParserContext,
948 input: &mut Parser,
949 ) -> Result<(), ParseError> {
950 assert!(declarations.is_empty());
951 debug_assert!(id.allowed_in(context), "{:?}", id);
952 input.skip_whitespace();
953
954 let start = input.state();
955 let non_custom_id = match id {
956 PropertyId::Custom(property_name) => {
957 let value = match input.try_parse(CSSWideKeyword::parse) {
958 Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
959 Err(()) => CustomDeclarationValue::Unparsed(Arc::new(
960 custom_properties::VariableValue::parse(
961 input,
962 Some(&context.namespaces.prefixes),
963 context.url_data,
964 )?,
965 )),
966 };
967 declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
968 name: property_name,
969 value,
970 }));
971 return Ok(());
972 },
973 PropertyId::NonCustom(id) => id,
974 };
975
976 context.with_property_declaration(PropertyIdRef::from(non_custom_id), |context| {
977 match non_custom_id.longhand_or_shorthand() {
978 Ok(longhand_id) => {
979 parse_non_custom_property_declaration_value_into(
980 declarations,
981 context,
982 input,
983 &start,
984 |declarations, input| {
985 let decl = input
986 .parse_entirely(|input| longhand_id.parse_value(context, input))?;
987 declarations.push(decl);
988 Ok(())
989 },
990 |declarations, wk| {
991 declarations
992 .push(PropertyDeclaration::css_wide_keyword(longhand_id, wk));
993 },
994 |declarations, variable_value| {
995 declarations.push(PropertyDeclaration::WithVariables(
996 VariableDeclaration {
997 id: longhand_id,
998 value: Arc::new(UnparsedValue {
999 variable_value,
1000 from_shorthand: None,
1001 }),
1002 },
1003 ))
1004 },
1005 )?;
1006 },
1007 Err(shorthand_id) => {
1008 parse_non_custom_property_declaration_value_into(
1009 declarations,
1010 context,
1011 input,
1012 &start,
1013 |declarations, input| shorthand_id.parse_into(declarations, context, input),
1016 |declarations, wk| {
1017 if shorthand_id == ShorthandId::All {
1018 declarations.all_shorthand = AllShorthand::CSSWideKeyword(wk)
1019 } else {
1020 for longhand in shorthand_id.longhands() {
1021 declarations
1022 .push(PropertyDeclaration::css_wide_keyword(longhand, wk));
1023 }
1024 }
1025 },
1026 |declarations, variable_value| {
1027 let unparsed = Arc::new(UnparsedValue {
1028 variable_value,
1029 from_shorthand: Some(shorthand_id),
1030 });
1031 if shorthand_id == ShorthandId::All {
1032 declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
1033 } else {
1034 for id in shorthand_id.longhands() {
1035 declarations.push(PropertyDeclaration::WithVariables(
1036 VariableDeclaration {
1037 id,
1038 value: unparsed.clone(),
1039 },
1040 ))
1041 }
1042 }
1043 },
1044 )?;
1045 },
1046 }
1047 if let Some(use_counters) = context.use_counters {
1048 use_counters.non_custom_properties.record(non_custom_id);
1049 }
1050 Ok(())
1051 })
1052 }
1053}
1054
1055#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1057pub enum OwnedPropertyDeclarationId {
1058 Longhand(LonghandId),
1060 Custom(custom_properties::Name),
1062}
1063
1064impl OwnedPropertyDeclarationId {
1065 #[inline]
1067 pub fn is_logical(&self) -> bool {
1068 self.as_borrowed().is_logical()
1069 }
1070
1071 #[inline]
1073 pub fn as_borrowed(&self) -> PropertyDeclarationId<'_> {
1074 match self {
1075 Self::Longhand(id) => PropertyDeclarationId::Longhand(*id),
1076 Self::Custom(name) => PropertyDeclarationId::Custom(name),
1077 }
1078 }
1079
1080 #[cfg(feature = "gecko")]
1082 #[inline]
1083 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
1084 Some(match PropertyId::from_gecko_css_property_id(property)? {
1085 PropertyId::Custom(name) => Self::Custom(name),
1086 PropertyId::NonCustom(id) => Self::Longhand(id.as_longhand()?),
1087 })
1088 }
1089}
1090
1091#[derive(Clone, Copy, Debug, PartialEq, MallocSizeOf)]
1094pub enum PropertyDeclarationId<'a> {
1095 Longhand(LonghandId),
1097 Custom(&'a custom_properties::Name),
1099}
1100
1101impl<'a> ToCss for PropertyDeclarationId<'a> {
1102 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1103 where
1104 W: Write,
1105 {
1106 match *self {
1107 PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
1108 PropertyDeclarationId::Custom(name) => {
1109 dest.write_str("--")?;
1110 serialize_atom_name(name, dest)
1111 },
1112 }
1113 }
1114}
1115
1116impl<'a> PropertyDeclarationId<'a> {
1117 #[inline(always)]
1119 pub fn flags(&self) -> PropertyFlags {
1120 match self {
1121 Self::Longhand(id) => id.flags(),
1122 Self::Custom(_) => PropertyFlags::empty(),
1123 }
1124 }
1125
1126 pub fn to_owned(&self) -> OwnedPropertyDeclarationId {
1128 match self {
1129 PropertyDeclarationId::Longhand(id) => OwnedPropertyDeclarationId::Longhand(*id),
1130 PropertyDeclarationId::Custom(name) => {
1131 OwnedPropertyDeclarationId::Custom((*name).clone())
1132 },
1133 }
1134 }
1135
1136 pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
1139 match *self {
1140 PropertyDeclarationId::Longhand(id) => match *other {
1141 PropertyId::NonCustom(non_custom_id) => id.is_or_is_longhand_of(non_custom_id),
1142 PropertyId::Custom(_) => false,
1143 },
1144 PropertyDeclarationId::Custom(name) => {
1145 matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
1146 },
1147 }
1148 }
1149
1150 pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
1153 match *self {
1154 PropertyDeclarationId::Longhand(ref id) => id.is_longhand_of(shorthand),
1155 _ => false,
1156 }
1157 }
1158
1159 pub fn name(&self) -> Cow<'static, str> {
1161 match *self {
1162 PropertyDeclarationId::Longhand(id) => id.name().into(),
1163 PropertyDeclarationId::Custom(name) => {
1164 let mut s = String::new();
1165 write!(&mut s, "--{}", name).unwrap();
1166 s.into()
1167 },
1168 }
1169 }
1170
1171 #[inline]
1173 pub fn as_longhand(&self) -> Option<LonghandId> {
1174 match *self {
1175 PropertyDeclarationId::Longhand(id) => Some(id),
1176 _ => None,
1177 }
1178 }
1179
1180 #[inline]
1182 pub fn is_logical(&self) -> bool {
1183 match self {
1184 PropertyDeclarationId::Longhand(id) => id.is_logical(),
1185 PropertyDeclarationId::Custom(_) => false,
1186 }
1187 }
1188
1189 #[inline]
1194 pub fn to_physical(&self, wm: WritingMode) -> Self {
1195 match self {
1196 Self::Longhand(id) => Self::Longhand(id.to_physical(wm)),
1197 Self::Custom(_) => *self,
1198 }
1199 }
1200
1201 #[inline]
1203 pub fn is_animatable(&self) -> bool {
1204 match self {
1205 Self::Longhand(id) => id.is_animatable(),
1206 Self::Custom(_) => true,
1207 }
1208 }
1209
1210 #[inline]
1212 pub fn is_discrete_animatable(&self) -> bool {
1213 match self {
1214 Self::Longhand(longhand) => longhand.is_discrete_animatable(),
1215 Self::Custom(_) => true,
1217 }
1218 }
1219
1220 #[cfg(feature = "gecko")]
1223 #[inline]
1224 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
1225 match self {
1226 PropertyDeclarationId::Longhand(id) => id.to_noncustomcsspropertyid(),
1227 PropertyDeclarationId::Custom(_) => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1228 }
1229 }
1230
1231 #[cfg(feature = "gecko")]
1236 #[inline]
1237 pub fn to_gecko_css_property_id(&self) -> CSSPropertyId {
1238 match self {
1239 Self::Longhand(id) => CSSPropertyId {
1240 mId: id.to_noncustomcsspropertyid(),
1241 mCustomName: RefPtr::null(),
1242 },
1243 Self::Custom(name) => {
1244 let mut property_id = CSSPropertyId {
1245 mId: NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1246 mCustomName: RefPtr::null(),
1247 };
1248 property_id.mCustomName.mRawPtr = (*name).clone().into_addrefed();
1249 property_id
1250 },
1251 }
1252 }
1253}
1254
1255pub trait IndexedId: Copy {
1258 const COUNT: usize;
1260 unsafe fn from_index_release_unchecked(index: usize) -> Self;
1263 fn to_index(self) -> usize;
1265}
1266
1267impl IndexedId for NonCustomPropertyId {
1268 const COUNT: usize = property_counts::NON_CUSTOM;
1269
1270 #[inline(always)]
1271 unsafe fn from_index_release_unchecked(index: usize) -> Self {
1272 debug_assert!(index < Self::COUNT);
1273 NonCustomPropertyId(index as u16)
1274 }
1275
1276 #[inline(always)]
1277 fn to_index(self) -> usize {
1278 self.0 as usize
1279 }
1280}
1281
1282impl IndexedId for PrioritaryPropertyId {
1283 const COUNT: usize = property_counts::PRIORITARY;
1284
1285 #[inline(always)]
1286 unsafe fn from_index_release_unchecked(index: usize) -> Self {
1287 unsafe {
1288 debug_assert!(index < Self::COUNT);
1289 std::mem::transmute(index as u8)
1290 }
1291 }
1292
1293 #[inline(always)]
1294 fn to_index(self) -> usize {
1295 self as usize
1296 }
1297}
1298
1299impl IndexedId for LonghandId {
1300 const COUNT: usize = property_counts::LONGHANDS;
1301
1302 #[inline(always)]
1303 unsafe fn from_index_release_unchecked(index: usize) -> Self {
1304 unsafe {
1305 debug_assert!(index < Self::COUNT);
1306 std::mem::transmute(index as u16)
1307 }
1308 }
1309
1310 #[inline(always)]
1311 fn to_index(self) -> usize {
1312 self as usize
1313 }
1314}
1315
1316pub type NonCustomPropertyIdSet =
1318 IdSet<NonCustomPropertyId, { (property_counts::NON_CUSTOM - 1 + 32) / 32 }>;
1319pub type NonCustomPropertyIdSetIterator<'a> = IdSetIterator<'a, NonCustomPropertyId>;
1321pub type PrioritaryPropertyIdSet =
1323 IdSet<PrioritaryPropertyId, { (property_counts::PRIORITARY - 1 + 32) / 32 }>;
1324pub type PrioritaryPropertyIdSetIterator<'a> = IdSetIterator<'a, PrioritaryPropertyId>;
1326pub type LonghandIdSet = IdSet<LonghandId, { (property_counts::LONGHANDS - 1 + 32) / 32 }>;
1328pub type LonghandIdSetIterator<'a> = IdSetIterator<'a, LonghandId>;
1330
1331pub struct IdSet<Id: IndexedId, const W: usize> {
1338 storage: [u32; W],
1339 _phantom: std::marker::PhantomData<Id>,
1340}
1341
1342impl<Id: IndexedId, const W: usize> Clone for IdSet<Id, W> {
1343 #[inline]
1344 fn clone(&self) -> Self {
1345 *self
1346 }
1347}
1348
1349impl<Id: IndexedId, const W: usize> Copy for IdSet<Id, W> {}
1350
1351impl<Id: IndexedId, const W: usize> Default for IdSet<Id, W> {
1352 #[inline]
1353 fn default() -> Self {
1354 Self {
1355 storage: [0; W],
1356 _phantom: std::marker::PhantomData,
1357 }
1358 }
1359}
1360
1361impl<Id: IndexedId, const W: usize> PartialEq for IdSet<Id, W> {
1362 #[inline]
1363 fn eq(&self, other: &Self) -> bool {
1364 self.storage == other.storage
1365 }
1366}
1367
1368impl<Id: IndexedId, const W: usize> fmt::Debug for IdSet<Id, W> {
1369 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1370 self.storage.fmt(f)
1371 }
1372}
1373
1374impl<Id: IndexedId, const W: usize> malloc_size_of::MallocSizeOf for IdSet<Id, W> {
1375 #[inline(always)]
1376 fn size_of(&self, _: &mut malloc_size_of::MallocSizeOfOps) -> usize {
1377 0
1378 }
1379}
1380
1381impl<Id: IndexedId, const W: usize> IdSet<Id, W> {
1382 #[inline]
1384 pub fn new() -> Self {
1385 Self::default()
1386 }
1387
1388 pub(crate) const fn from_storage(storage: [u32; W]) -> Self {
1390 Self {
1391 storage,
1392 _phantom: std::marker::PhantomData,
1393 }
1394 }
1395
1396 #[inline]
1398 pub fn insert(&mut self, id: Id) {
1399 let bit = id.to_index();
1400 self.storage[bit / 32] |= 1 << (bit % 32);
1401 }
1402
1403 #[inline]
1405 pub fn remove(&mut self, id: Id) {
1406 let bit = id.to_index();
1407 self.storage[bit / 32] &= !(1 << (bit % 32));
1408 }
1409
1410 #[inline]
1412 pub fn contains(&self, id: Id) -> bool {
1413 let bit = id.to_index();
1414 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1415 }
1416
1417 pub fn iter(&self) -> IdSetIterator<'_, Id> {
1419 IdSetIterator {
1420 chunks: &self.storage,
1421 cur_chunk: 0,
1422 cur_bit: 0,
1423 _phantom: std::marker::PhantomData,
1424 }
1425 }
1426
1427 pub fn contains_all(&self, other: &Self) -> bool {
1429 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1430 if (*self_cell & *other_cell) != *other_cell {
1431 return false;
1432 }
1433 }
1434 true
1435 }
1436
1437 pub fn contains_any(&self, other: &Self) -> bool {
1439 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1440 if (*self_cell & *other_cell) != 0 {
1441 return true;
1442 }
1443 }
1444 false
1445 }
1446
1447 #[inline]
1449 pub fn remove_all(&mut self, other: &Self) {
1450 for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
1451 *self_cell &= !*other_cell;
1452 }
1453 }
1454
1455 #[inline]
1457 pub fn clear(&mut self) {
1458 self.storage.fill(0);
1459 }
1460
1461 #[inline]
1463 pub fn is_empty(&self) -> bool {
1464 self.storage.iter().all(|c| *c == 0)
1465 }
1466}
1467
1468to_shmem::impl_trivial_to_shmem!(LonghandIdSet);
1469impl LonghandIdSet {
1470 #[inline]
1472 pub fn contains_any_reset(&self) -> bool {
1473 self.contains_any(Self::reset())
1474 }
1475}
1476
1477pub struct IdSetIterator<'a, Id: IndexedId> {
1479 chunks: &'a [u32],
1480 cur_chunk: u32,
1481 cur_bit: u32, _phantom: std::marker::PhantomData<Id>,
1483}
1484
1485impl<'a, Id: IndexedId> Iterator for IdSetIterator<'a, Id> {
1486 type Item = Id;
1487
1488 fn next(&mut self) -> Option<Self::Item> {
1489 loop {
1490 debug_assert!(self.cur_bit < 32);
1491 let cur_chunk = self.cur_chunk;
1492 let cur_bit = self.cur_bit;
1493 let chunk = *self.chunks.get(cur_chunk as usize)?;
1494 let next_bit = (chunk >> cur_bit).trailing_zeros();
1495 if next_bit == 32 {
1496 self.cur_bit = 0;
1498 self.cur_chunk += 1;
1499 continue;
1500 }
1501 debug_assert!(cur_bit + next_bit < 32);
1502 let index = (cur_chunk * 32 + cur_bit + next_bit) as usize;
1503 debug_assert!(index < Id::COUNT);
1504 let id = unsafe { Id::from_index_release_unchecked(index) };
1505 self.cur_bit += next_bit + 1;
1506 if self.cur_bit == 32 {
1507 self.cur_bit = 0;
1508 self.cur_chunk += 1;
1509 }
1510 return Some(id);
1511 }
1512 }
1513}
1514
1515pub type SubpropertiesVec<T> = ArrayVec<T, { property_counts::MAX_SHORTHAND_EXPANDED }>;
1517
1518#[derive(Default)]
1522pub struct SourcePropertyDeclaration {
1523 pub declarations: SubpropertiesVec<PropertyDeclaration>,
1525 pub all_shorthand: AllShorthand,
1527}
1528
1529size_of_test!(SourcePropertyDeclaration, 632);
1532
1533impl SourcePropertyDeclaration {
1534 #[inline]
1536 pub fn with_one(decl: PropertyDeclaration) -> Self {
1537 let mut result = Self::default();
1538 result.declarations.push(decl);
1539 result
1540 }
1541
1542 pub fn drain(&mut self) -> SourcePropertyDeclarationDrain<'_> {
1544 SourcePropertyDeclarationDrain {
1545 declarations: self.declarations.drain(..),
1546 all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
1547 }
1548 }
1549
1550 pub fn clear(&mut self) {
1552 self.declarations.clear();
1553 self.all_shorthand = AllShorthand::NotSet;
1554 }
1555
1556 pub fn is_empty(&self) -> bool {
1558 self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
1559 }
1560
1561 pub fn push(&mut self, declaration: PropertyDeclaration) {
1563 let _result = self.declarations.try_push(declaration);
1564 debug_assert!(_result.is_ok());
1565 }
1566}
1567
1568pub struct SourcePropertyDeclarationDrain<'a> {
1570 pub declarations:
1572 ArrayVecDrain<'a, PropertyDeclaration, { property_counts::MAX_SHORTHAND_EXPANDED }>,
1573 pub all_shorthand: AllShorthand,
1575}
1576
1577#[derive(Debug, Eq, PartialEq, ToShmem)]
1579pub struct UnparsedValue {
1580 pub(super) variable_value: custom_properties::VariableValue,
1582 from_shorthand: Option<ShorthandId>,
1584}
1585
1586impl ToCss for UnparsedValue {
1587 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1588 where
1589 W: Write,
1590 {
1591 if self.from_shorthand.is_none() {
1593 self.variable_value.to_css(dest)?;
1594 }
1595 Ok(())
1596 }
1597}
1598
1599impl ToTyped for UnparsedValue {
1600 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
1601 if self.from_shorthand.is_none() {
1602 self.variable_value.to_typed(dest)?;
1603 return Ok(());
1604 }
1605 Err(())
1606 }
1607}
1608
1609pub type ShorthandsWithPropertyReferencesCache =
1616 FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
1617
1618impl UnparsedValue {
1619 fn substitute_variables<'cache>(
1620 &self,
1621 longhand_id: LonghandId,
1622 substitution_functions: &ComputedSubstitutionFunctions,
1623 stylist: &Stylist,
1624 computed_context: &computed::Context,
1625 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
1626 attribute_tracker: &mut AttributeTracker,
1627 ) -> Cow<'cache, PropertyDeclaration> {
1628 let invalid_at_computed_value_time = || {
1629 let keyword = if longhand_id.inherited() {
1630 CSSWideKeyword::Inherit
1631 } else {
1632 CSSWideKeyword::Initial
1633 };
1634 Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
1635 };
1636
1637 if computed_context
1638 .builder
1639 .invalid_non_custom_properties
1640 .contains(longhand_id)
1641 {
1642 return invalid_at_computed_value_time();
1643 }
1644
1645 if let Some(shorthand_id) = self.from_shorthand {
1646 let key = (shorthand_id, longhand_id);
1647 if shorthand_cache.contains_key(&key) {
1648 return Cow::Borrowed(&shorthand_cache[&key]);
1653 }
1654 }
1655
1656 let property_id = PropertyIdRef::from(match self.from_shorthand {
1657 Some(shorthand_id) => NonCustomPropertyId::from(shorthand_id),
1658 None => NonCustomPropertyId::from(longhand_id),
1659 });
1660
1661 let SubstitutionResult { css, attr_taint } = match custom_properties::substitute(
1662 &self.variable_value,
1663 Some(property_id),
1664 substitution_functions,
1665 stylist,
1666 computed_context,
1667 attribute_tracker,
1668 ) {
1669 Ok(css) => css,
1670 Err(..) => return invalid_at_computed_value_time(),
1671 };
1672
1673 let mut context = ParserContext::new(
1684 Origin::Author,
1685 &self.variable_value.url_data,
1686 None,
1687 ParsingMode::DEFAULT,
1688 computed_context.quirks_mode,
1689 Default::default(),
1690 None,
1691 None,
1692 attr_taint,
1693 );
1694
1695 let mut input = Parser::new(&css);
1696 input.skip_whitespace();
1697
1698 if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
1699 return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
1700 }
1701
1702 let shorthand = match self.from_shorthand {
1703 None => {
1704 return match context.with_property_declaration(property_id, |context| {
1705 input.parse_entirely(|input| longhand_id.parse_value(context, input))
1706 }) {
1707 Ok(decl) => Cow::Owned(decl),
1708 Err(..) => invalid_at_computed_value_time(),
1709 };
1710 },
1711 Some(shorthand) => shorthand,
1712 };
1713
1714 let mut decls = SourcePropertyDeclaration::default();
1715 if context
1717 .with_property_declaration(property_id, |context| {
1718 shorthand.parse_into(&mut decls, context, &mut input)
1719 })
1720 .is_err()
1721 {
1722 return invalid_at_computed_value_time();
1723 }
1724
1725 for declaration in decls.declarations.drain(..) {
1726 let longhand = declaration.id().as_longhand().unwrap();
1727 if longhand.is_logical() {
1728 let writing_mode = computed_context.builder.writing_mode;
1729 shorthand_cache.insert(
1730 (shorthand, longhand.to_physical(writing_mode)),
1731 declaration.clone(),
1732 );
1733 }
1734 shorthand_cache.insert((shorthand, longhand), declaration);
1735 }
1736
1737 let key = (shorthand, longhand_id);
1738 match shorthand_cache.get(&key) {
1739 Some(decl) => Cow::Borrowed(decl),
1740 None => invalid_at_computed_value_time(),
1752 }
1753 }
1754}
1755#[derive(Default)]
1757pub enum AllShorthand {
1758 #[default]
1760 NotSet,
1761 CSSWideKeyword(CSSWideKeyword),
1763 WithVariables(Arc<UnparsedValue>),
1765}
1766
1767impl AllShorthand {
1768 #[inline]
1770 pub fn declarations(&self) -> AllShorthandDeclarationIterator<'_> {
1771 AllShorthandDeclarationIterator {
1772 all_shorthand: self,
1773 longhands: ShorthandId::All.longhands(),
1774 }
1775 }
1776}
1777
1778pub struct AllShorthandDeclarationIterator<'a> {
1780 all_shorthand: &'a AllShorthand,
1781 longhands: NonCustomPropertyIterator<LonghandId>,
1782}
1783
1784impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
1785 type Item = PropertyDeclaration;
1786
1787 #[inline]
1788 fn next(&mut self) -> Option<Self::Item> {
1789 match *self.all_shorthand {
1790 AllShorthand::NotSet => None,
1791 AllShorthand::CSSWideKeyword(ref keyword) => Some(
1792 PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword),
1793 ),
1794 AllShorthand::WithVariables(ref unparsed) => {
1795 Some(PropertyDeclaration::WithVariables(VariableDeclaration {
1796 id: self.longhands.next()?,
1797 value: unparsed.clone(),
1798 }))
1799 },
1800 }
1801 }
1802}
1803
1804pub struct NonCustomPropertyIterator<Item: 'static> {
1807 filter: bool,
1808 iter: std::slice::Iter<'static, Item>,
1809}
1810
1811impl<Item> Iterator for NonCustomPropertyIterator<Item>
1812where
1813 Item: 'static + Copy + Into<NonCustomPropertyId>,
1814{
1815 type Item = Item;
1816
1817 fn next(&mut self) -> Option<Self::Item> {
1818 loop {
1819 let id = *self.iter.next()?;
1820 if !self.filter || id.into().enabled_for_all_content() {
1821 return Some(id);
1822 }
1823 }
1824 }
1825}
1826
1827pub struct TransitionPropertyIterator<'a> {
1829 style: &'a ComputedValues,
1830 index_range: core::ops::Range<usize>,
1831 longhand_iterator: Option<NonCustomPropertyIterator<LonghandId>>,
1832}
1833
1834impl<'a> TransitionPropertyIterator<'a> {
1835 pub fn from_style(style: &'a ComputedValues) -> Self {
1837 Self {
1838 style,
1839 index_range: 0..style.get_ui().transition_property_count(),
1840 longhand_iterator: None,
1841 }
1842 }
1843}
1844
1845pub struct TransitionPropertyIteration {
1847 pub property: OwnedPropertyDeclarationId,
1849 pub index: usize,
1852}
1853
1854impl<'a> Iterator for TransitionPropertyIterator<'a> {
1855 type Item = TransitionPropertyIteration;
1856
1857 fn next(&mut self) -> Option<Self::Item> {
1858 use crate::values::computed::TransitionProperty;
1859 loop {
1860 if let Some(ref mut longhand_iterator) = self.longhand_iterator {
1861 if let Some(longhand_id) = longhand_iterator.next() {
1862 return Some(TransitionPropertyIteration {
1863 property: OwnedPropertyDeclarationId::Longhand(longhand_id),
1864 index: self.index_range.start - 1,
1865 });
1866 }
1867 self.longhand_iterator = None;
1868 }
1869
1870 let index = self.index_range.next()?;
1871 match self.style.get_ui().transition_property_at(index) {
1872 TransitionProperty::NonCustom(id) => {
1873 match id.longhand_or_shorthand() {
1874 Ok(longhand_id) => {
1875 return Some(TransitionPropertyIteration {
1876 property: OwnedPropertyDeclarationId::Longhand(longhand_id),
1877 index,
1878 });
1879 },
1880 Err(shorthand_id) => {
1881 self.longhand_iterator = Some(shorthand_id.longhands());
1885 },
1886 }
1887 },
1888 TransitionProperty::Custom(name) => {
1889 return Some(TransitionPropertyIteration {
1890 property: OwnedPropertyDeclarationId::Custom(name),
1891 index,
1892 });
1893 },
1894 TransitionProperty::Unsupported(..) => {},
1895 }
1896 }
1897 }
1898}