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, ParserInput};
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
70 const CAN_ANIMATE_ON_COMPOSITOR = 0;
76 const AFFECTS_LAYOUT = 0;
78 #[allow(missing_docs)]
79 const AFFECTS_OVERFLOW = 0;
80 #[allow(missing_docs)]
81 const AFFECTS_PAINT = 0;
82 }
83}
84
85#[derive(
87 Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
88)]
89pub enum CSSWideKeyword {
90 Initial,
92 Inherit,
94 Unset,
96 Revert,
98 RevertLayer,
100 RevertRule,
102}
103
104impl CSSWideKeyword {
105 pub fn to_str(&self) -> &'static str {
107 match *self {
108 Self::Initial => "initial",
109 Self::Inherit => "inherit",
110 Self::Unset => "unset",
111 Self::Revert => "revert",
112 Self::RevertLayer => "revert-layer",
113 Self::RevertRule => "revert-rule",
114 }
115 }
116
117 pub fn from_ident(ident: &str) -> Result<Self, ()> {
119 Ok(match_ignore_ascii_case! { ident,
120 "initial" => Self::Initial,
121 "inherit" => Self::Inherit,
122 "unset" => Self::Unset,
123 "revert" => Self::Revert,
124 "revert-layer" => Self::RevertLayer,
125 "revert-rule" if static_prefs::pref!("layout.css.revert-rule.enabled") => Self::RevertRule,
126 _ => return Err(()),
127 })
128 }
129
130 pub fn parse(input: &mut Parser) -> Result<Self, ()> {
132 let keyword = {
133 let ident = input.expect_ident().map_err(|_| ())?;
134 Self::from_ident(ident)?
135 };
136 input.expect_exhausted().map_err(|_| ())?;
137 Ok(keyword)
138 }
139
140 pub fn revert_kind(self) -> Option<RevertKind> {
142 Some(match self {
143 Self::Initial | Self::Inherit | Self::Unset => return None,
144 Self::Revert => RevertKind::Origin,
145 Self::RevertLayer => RevertKind::Layer,
146 Self::RevertRule => RevertKind::Rule,
147 })
148 }
149}
150
151#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf)]
153pub struct WideKeywordDeclaration {
154 #[css(skip)]
155 id: LonghandId,
156 pub keyword: CSSWideKeyword,
158}
159
160impl ToTyped for WideKeywordDeclaration {
163 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
164 self.keyword.to_typed(dest)
165 }
166}
167
168#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
170pub struct VariableDeclaration {
171 #[css(skip)]
173 id: LonghandId,
174 #[ignore_malloc_size_of = "Arc"]
176 pub value: Arc<UnparsedValue>,
177}
178
179#[derive(Clone, PartialEq, ToCss, ToShmem)]
182pub enum CustomDeclarationValue {
183 Unparsed(Arc<custom_properties::SpecifiedValue>),
185 Parsed(Arc<crate::properties_and_values::value::SpecifiedValue>),
187 CSSWideKeyword(CSSWideKeyword),
189}
190
191#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
193#[typed(todo_derive_fields)]
194pub struct CustomDeclaration {
195 #[css(skip)]
197 pub name: custom_properties::Name,
198 #[ignore_malloc_size_of = "Arc"]
200 pub value: CustomDeclarationValue,
201}
202
203impl fmt::Debug for PropertyDeclaration {
204 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
205 self.id().to_css(&mut CssWriter::new(f))?;
206 f.write_str(": ")?;
207
208 let mut s = CssString::new();
212 self.to_css(&mut s)?;
213 write!(f, "{}", s)
214 }
215}
216
217#[derive(
219 Clone, Copy, Debug, PartialEq, Eq, Hash, ToComputedValue, ToResolvedValue, ToShmem, MallocSizeOf,
220)]
221#[repr(C)]
222pub struct NonCustomPropertyId(u16);
223
224impl ToCss for NonCustomPropertyId {
225 #[inline]
226 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
227 where
228 W: Write,
229 {
230 dest.write_str(self.name())
231 }
232}
233
234impl NonCustomPropertyId {
235 pub fn bit(self) -> usize {
237 self.0 as usize
238 }
239
240 #[cfg(feature = "gecko")]
242 #[inline]
243 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
244 unsafe { mem::transmute(self.0) }
246 }
247
248 #[cfg(feature = "gecko")]
250 #[inline]
251 pub fn from_noncustomcsspropertyid(prop: NonCustomCSSPropertyId) -> Option<Self> {
252 let prop = prop as u16;
253 if prop >= property_counts::NON_CUSTOM as u16 {
254 return None;
255 }
256 Some(NonCustomPropertyId(prop))
258 }
259
260 pub fn unaliased(self) -> Self {
262 let Some(alias_id) = self.as_alias() else {
263 return self;
264 };
265 alias_id.aliased_property()
266 }
267
268 #[inline]
270 pub fn to_property_id(self) -> PropertyId {
271 PropertyId::NonCustom(self)
272 }
273
274 #[inline]
276 pub fn as_longhand(self) -> Option<LonghandId> {
277 if self.0 < property_counts::LONGHANDS as u16 {
278 return Some(unsafe { mem::transmute(self.0 as u16) });
279 }
280 None
281 }
282
283 #[inline]
285 pub fn as_shorthand(self) -> Option<ShorthandId> {
286 if self.0 >= property_counts::LONGHANDS as u16
287 && self.0 < property_counts::LONGHANDS_AND_SHORTHANDS as u16
288 {
289 return Some(unsafe { mem::transmute(self.0 - (property_counts::LONGHANDS as u16)) });
290 }
291 None
292 }
293
294 #[inline]
296 pub fn as_alias(self) -> Option<AliasId> {
297 debug_assert!((self.0 as usize) < property_counts::NON_CUSTOM);
298 if self.0 >= property_counts::LONGHANDS_AND_SHORTHANDS as u16 {
299 return Some(unsafe {
300 mem::transmute(self.0 - (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
301 });
302 }
303 None
304 }
305
306 #[inline]
308 pub fn longhand_or_shorthand(self) -> Result<LonghandId, ShorthandId> {
309 let id = self.unaliased();
310 match id.as_longhand() {
311 Some(lh) => Ok(lh),
312 None => Err(id.as_shorthand().unwrap()),
313 }
314 }
315
316 #[inline]
318 pub const fn from_longhand(id: LonghandId) -> Self {
319 Self(id as u16)
320 }
321
322 #[inline]
324 pub const fn from_shorthand(id: ShorthandId) -> Self {
325 Self((id as u16) + (property_counts::LONGHANDS as u16))
326 }
327
328 #[inline]
330 pub const fn from_alias(id: AliasId) -> Self {
331 Self((id as u16) + (property_counts::LONGHANDS_AND_SHORTHANDS as u16))
332 }
333
334 #[cfg(feature = "servo")]
335 pub fn iter() -> impl Iterator<Item=Self> {
337 (0..property_counts::NON_CUSTOM as u16).map(|index| Self(index))
338 }
339}
340
341impl From<LonghandId> for NonCustomPropertyId {
342 #[inline]
343 fn from(id: LonghandId) -> Self {
344 Self::from_longhand(id)
345 }
346}
347
348impl From<ShorthandId> for NonCustomPropertyId {
349 #[inline]
350 fn from(id: ShorthandId) -> Self {
351 Self::from_shorthand(id)
352 }
353}
354
355impl From<AliasId> for NonCustomPropertyId {
356 #[inline]
357 fn from(id: AliasId) -> Self {
358 Self::from_alias(id)
359 }
360}
361
362#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
365pub enum PropertyId {
366 NonCustom(NonCustomPropertyId),
368 Custom(custom_properties::Name),
370}
371
372impl ToCss for PropertyId {
373 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
374 where
375 W: Write,
376 {
377 match *self {
378 PropertyId::NonCustom(id) => dest.write_str(id.name()),
379 PropertyId::Custom(ref name) => {
380 dest.write_str("--")?;
381 serialize_atom_name(name, dest)
382 },
383 }
384 }
385}
386
387impl PropertyId {
388 #[inline]
390 pub fn longhand_id(&self) -> Option<LonghandId> {
391 self.non_custom_non_alias_id()?.as_longhand()
392 }
393
394 pub fn is_animatable(&self) -> bool {
396 match self {
397 Self::NonCustom(id) => id.is_animatable(),
398 Self::Custom(_) => true,
399 }
400 }
401
402 pub fn parse_unchecked_for_testing(name: &str) -> Result<Self, ()> {
407 Self::parse_unchecked(name, None)
408 }
409
410 #[inline]
413 pub fn parse_enabled_for_all_content(name: &str) -> Result<Self, ()> {
414 let id = Self::parse_unchecked(name, None)?;
415
416 if !id.enabled_for_all_content() {
417 return Err(());
418 }
419
420 Ok(id)
421 }
422
423 #[inline]
426 pub fn parse(name: &str, context: &ParserContext) -> Result<Self, ()> {
427 let id = Self::parse_unchecked(name, context.use_counters)?;
428 if !id.allowed_in(context) {
429 return Err(());
430 }
431 Ok(id)
432 }
433
434 #[inline]
439 pub fn parse_ignoring_rule_type(name: &str, context: &ParserContext) -> Result<Self, ()> {
440 let id = Self::parse_unchecked(name, None)?;
441 if !id.allowed_in_ignoring_rule_type(context) {
442 return Err(());
443 }
444 Ok(id)
445 }
446
447 #[cfg(feature = "gecko")]
449 #[inline]
450 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
451 Some(NonCustomPropertyId::from_noncustomcsspropertyid(id)?.to_property_id())
452 }
453
454 #[cfg(feature = "gecko")]
456 #[inline]
457 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
458 Some(
459 if property.mId == NonCustomCSSPropertyId::eCSSPropertyExtra_variable {
460 debug_assert!(!property.mCustomName.mRawPtr.is_null());
461 Self::Custom(unsafe { crate::Atom::from_raw(property.mCustomName.mRawPtr) })
462 } else {
463 Self::NonCustom(NonCustomPropertyId::from_noncustomcsspropertyid(
464 property.mId,
465 )?)
466 },
467 )
468 }
469
470 #[inline]
472 pub fn is_shorthand(&self) -> bool {
473 self.as_shorthand().is_ok()
474 }
475
476 pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId<'_>> {
479 match *self {
480 Self::NonCustom(id) => match id.longhand_or_shorthand() {
481 Ok(lh) => Err(PropertyDeclarationId::Longhand(lh)),
482 Err(sh) => Ok(sh),
483 },
484 Self::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),
485 }
486 }
487
488 pub fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
490 match *self {
491 Self::Custom(_) => None,
492 Self::NonCustom(id) => Some(id),
493 }
494 }
495
496 fn non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId> {
499 self.non_custom_id().map(NonCustomPropertyId::unaliased)
500 }
501
502 #[inline]
505 pub fn enabled_for_all_content(&self) -> bool {
506 let id = match self.non_custom_id() {
507 None => return true,
509 Some(id) => id,
510 };
511
512 id.enabled_for_all_content()
513 }
514
515 #[cfg(feature = "gecko")]
519 #[inline]
520 pub fn to_noncustomcsspropertyid_resolving_aliases(&self) -> NonCustomCSSPropertyId {
521 match self.non_custom_non_alias_id() {
522 Some(id) => id.to_noncustomcsspropertyid(),
523 None => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
524 }
525 }
526
527 fn allowed_in(&self, context: &ParserContext) -> bool {
528 let id = match self.non_custom_id() {
529 None => {
531 return !context
532 .nesting_context
533 .rule_types
534 .contains(CssRuleType::PositionTry)
535 },
536 Some(id) => id,
537 };
538 id.allowed_in(context)
539 }
540
541 #[inline]
542 fn allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool {
543 let id = match self.non_custom_id() {
544 None => return true,
546 Some(id) => id,
547 };
548 id.allowed_in_ignoring_rule_type(context)
549 }
550
551 pub fn supports_type(&self, ty: u8) -> bool {
554 let id = self.non_custom_non_alias_id();
555 id.map_or(0, |id| id.supported_types()) & ty != 0
556 }
557
558 pub fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
563 if let Some(id) = self.non_custom_non_alias_id() {
564 id.collect_property_completion_keywords(f);
565 }
566 CSSWideKeyword::collect_completion_keywords(f);
567 }
568}
569
570impl ToCss for LonghandId {
571 #[inline]
572 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
573 where
574 W: Write,
575 {
576 dest.write_str(self.name())
577 }
578}
579
580impl fmt::Debug for LonghandId {
581 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
582 formatter.write_str(self.name())
583 }
584}
585
586impl LonghandId {
587 #[inline]
589 pub fn name(&self) -> &'static str {
590 NonCustomPropertyId::from(*self).name()
591 }
592
593 #[inline]
595 pub fn inherited(self) -> bool {
596 !LonghandIdSet::reset().contains(self)
597 }
598
599 #[inline]
601 pub fn zoom_dependent(self) -> bool {
602 LonghandIdSet::zoom_dependent().contains(self)
603 }
604
605 #[inline]
608 pub fn ignored_when_document_colors_disabled(self) -> bool {
609 LonghandIdSet::ignored_when_colors_disabled().contains(self)
610 }
611
612 pub fn is_or_is_longhand_of(self, non_custom: NonCustomPropertyId) -> bool {
614 match non_custom.longhand_or_shorthand() {
615 Ok(lh) => self == lh,
616 Err(sh) => self.is_longhand_of(sh),
617 }
618 }
619
620 pub fn is_longhand_of(self, shorthand: ShorthandId) -> bool {
622 self.shorthands().any(|s| s == shorthand)
623 }
624
625 #[inline]
627 pub fn is_animatable(self) -> bool {
628 NonCustomPropertyId::from(self).is_animatable()
629 }
630
631 #[inline]
633 pub fn is_discrete_animatable(self) -> bool {
634 LonghandIdSet::discrete_animatable().contains(self)
635 }
636
637 #[cfg(feature = "gecko")]
639 #[inline]
640 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
641 NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
642 }
643
644 #[cfg(feature = "gecko")]
645 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
647 NonCustomPropertyId::from_noncustomcsspropertyid(id)?
648 .unaliased()
649 .as_longhand()
650 }
651
652 #[inline]
654 pub fn is_logical(self) -> bool {
655 LonghandIdSet::logical().contains(self)
656 }
657}
658
659impl ToCss for ShorthandId {
660 #[inline]
661 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
662 where
663 W: Write,
664 {
665 dest.write_str(self.name())
666 }
667}
668
669impl ShorthandId {
670 #[inline]
672 pub fn name(&self) -> &'static str {
673 NonCustomPropertyId::from(*self).name()
674 }
675
676 #[cfg(feature = "gecko")]
678 #[inline]
679 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
680 NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
681 }
682
683 #[cfg(feature = "gecko")]
685 #[inline]
686 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
687 NonCustomPropertyId::from_noncustomcsspropertyid(id)?
688 .unaliased()
689 .as_shorthand()
690 }
691
692 pub fn get_shorthand_appendable_value<'a, 'b: 'a>(
696 self,
697 declarations: &'a [&'b PropertyDeclaration],
698 ) -> Option<AppendableValue<'a, 'b>> {
699 let first_declaration = declarations.get(0)?;
700 let rest = || declarations.iter().skip(1);
701
702 if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
704 if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
705 return Some(AppendableValue::Css(css));
706 }
707 return None;
708 }
709
710 if let Some(keyword) = first_declaration.get_css_wide_keyword() {
712 if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {
713 return Some(AppendableValue::Css(keyword.to_str()));
714 }
715 return None;
716 }
717
718 if self == ShorthandId::All {
719 return None;
721 }
722
723 if declarations
725 .iter()
726 .all(|d| d.may_serialize_as_part_of_shorthand())
727 {
728 return Some(AppendableValue::DeclarationsForShorthand(
729 self,
730 declarations,
731 ));
732 }
733
734 None
735 }
736
737 #[inline]
739 pub fn is_legacy_shorthand(self) -> bool {
740 self.flags().contains(PropertyFlags::IS_LEGACY_SHORTHAND)
741 }
742}
743
744pub fn enabled_arbitrary_substitution_functions() -> &'static [&'static str] {
746 if static_prefs::pref!("layout.css.attr.enabled") {
747 &["var", "env", "attr"]
748 } else {
749 &["var", "env"]
750 }
751}
752
753fn parse_non_custom_property_declaration_value_into<'i>(
754 declarations: &mut SourcePropertyDeclaration,
755 context: &ParserContext,
756 input: &mut Parser<'i, '_>,
757 start: &cssparser::ParserState,
758 parse_entirely_into: impl FnOnce(
759 &mut SourcePropertyDeclaration,
760 &mut Parser<'i, '_>,
761 ) -> Result<(), ParseError<'i>>,
762 parsed_wide_keyword: impl FnOnce(&mut SourcePropertyDeclaration, CSSWideKeyword),
763 parsed_custom: impl FnOnce(&mut SourcePropertyDeclaration, custom_properties::VariableValue),
764) -> Result<(), ParseError<'i>> {
765 let mut starts_with_curly_block = false;
766 if let Ok(token) = input.next() {
767 match token {
768 cssparser::Token::Ident(ref ident) => match CSSWideKeyword::from_ident(ident) {
769 Ok(wk) => {
770 if input.expect_exhausted().is_ok() {
771 return Ok(parsed_wide_keyword(declarations, wk));
772 }
773 },
774 Err(()) => {},
775 },
776 cssparser::Token::CurlyBracketBlock => {
777 starts_with_curly_block = true;
778 },
779 _ => {},
780 }
781 };
782
783 input.reset(&start);
784 input.look_for_arbitrary_substitution_functions(enabled_arbitrary_substitution_functions());
785
786 let err = match parse_entirely_into(declarations, input) {
787 Ok(()) => {
788 input.seen_arbitrary_substitution_functions();
789 return Ok(());
790 },
791 Err(e) => e,
792 };
793
794 let start_pos = start.position();
796 let mut at_start = start_pos == input.position();
797 let mut invalid = false;
798 while let Ok(token) = input.next() {
799 if matches!(token, cssparser::Token::CurlyBracketBlock) {
800 if !starts_with_curly_block || !at_start {
801 invalid = true;
802 break;
803 }
804 } else if starts_with_curly_block {
805 invalid = true;
806 break;
807 }
808 at_start = false;
809 }
810 if !input.seen_arbitrary_substitution_functions() || invalid {
811 return Err(err);
812 }
813 input.reset(start);
814 let value = custom_properties::VariableValue::parse(
815 input,
816 Some(&context.namespaces.prefixes),
817 &context.url_data,
818 )?;
819 parsed_custom(declarations, value);
820 Ok(())
821}
822
823impl PropertyDeclaration {
824 fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option<&str> {
825 match *self {
826 PropertyDeclaration::WithVariables(ref declaration) => {
827 let s = declaration.value.from_shorthand?;
828 if s != shorthand {
829 return None;
830 }
831 Some(&*declaration.value.variable_value.css)
832 },
833 _ => None,
834 }
835 }
836
837 #[inline]
839 pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
840 Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
841 }
842
843 #[inline]
845 pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
846 match *self {
847 PropertyDeclaration::CSSWideKeyword(ref declaration) => Some(declaration.keyword),
848 _ => None,
849 }
850 }
851
852 pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
865 match *self {
866 PropertyDeclaration::CSSWideKeyword(..) | PropertyDeclaration::WithVariables(..) => {
867 false
868 },
869 PropertyDeclaration::Custom(..) => {
870 unreachable!("Serializing a custom property as part of shorthand?")
871 },
872 _ => true,
873 }
874 }
875
876 pub fn is_animatable(&self) -> bool {
878 self.id().is_animatable()
879 }
880
881 pub fn is_custom(&self) -> bool {
884 matches!(*self, PropertyDeclaration::Custom(..))
885 }
886
887 pub fn parse_into<'i, 't>(
898 declarations: &mut SourcePropertyDeclaration,
899 id: PropertyId,
900 context: &ParserContext,
901 input: &mut Parser<'i, 't>,
902 ) -> Result<(), ParseError<'i>> {
903 assert!(declarations.is_empty());
904 debug_assert!(id.allowed_in(context), "{:?}", id);
905 input.skip_whitespace();
906
907 let start = input.state();
908 let non_custom_id = match id {
909 PropertyId::Custom(property_name) => {
910 let value = match input.try_parse(CSSWideKeyword::parse) {
911 Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
912 Err(()) => CustomDeclarationValue::Unparsed(Arc::new(
913 custom_properties::VariableValue::parse(
914 input,
915 Some(&context.namespaces.prefixes),
916 &context.url_data,
917 )?,
918 )),
919 };
920 declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
921 name: property_name,
922 value,
923 }));
924 return Ok(());
925 },
926 PropertyId::NonCustom(id) => id,
927 };
928 match non_custom_id.longhand_or_shorthand() {
929 Ok(longhand_id) => {
930 parse_non_custom_property_declaration_value_into(
931 declarations,
932 context,
933 input,
934 &start,
935 |declarations, input| {
936 let decl = input
937 .parse_entirely(|input| longhand_id.parse_value(context, input))?;
938 declarations.push(decl);
939 Ok(())
940 },
941 |declarations, wk| {
942 declarations.push(PropertyDeclaration::css_wide_keyword(longhand_id, wk));
943 },
944 |declarations, variable_value| {
945 declarations.push(PropertyDeclaration::WithVariables(VariableDeclaration {
946 id: longhand_id,
947 value: Arc::new(UnparsedValue {
948 variable_value,
949 from_shorthand: None,
950 }),
951 }))
952 },
953 )?;
954 },
955 Err(shorthand_id) => {
956 parse_non_custom_property_declaration_value_into(
957 declarations,
958 context,
959 input,
960 &start,
961 |declarations, input| shorthand_id.parse_into(declarations, context, input),
964 |declarations, wk| {
965 if shorthand_id == ShorthandId::All {
966 declarations.all_shorthand = AllShorthand::CSSWideKeyword(wk)
967 } else {
968 for longhand in shorthand_id.longhands() {
969 declarations
970 .push(PropertyDeclaration::css_wide_keyword(longhand, wk));
971 }
972 }
973 },
974 |declarations, variable_value| {
975 let unparsed = Arc::new(UnparsedValue {
976 variable_value,
977 from_shorthand: Some(shorthand_id),
978 });
979 if shorthand_id == ShorthandId::All {
980 declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
981 } else {
982 for id in shorthand_id.longhands() {
983 declarations.push(PropertyDeclaration::WithVariables(
984 VariableDeclaration {
985 id,
986 value: unparsed.clone(),
987 },
988 ))
989 }
990 }
991 },
992 )?;
993 },
994 }
995 if let Some(use_counters) = context.use_counters {
996 use_counters.non_custom_properties.record(non_custom_id);
997 }
998 Ok(())
999 }
1000}
1001
1002#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1004pub enum OwnedPropertyDeclarationId {
1005 Longhand(LonghandId),
1007 Custom(custom_properties::Name),
1009}
1010
1011impl OwnedPropertyDeclarationId {
1012 #[inline]
1014 pub fn is_logical(&self) -> bool {
1015 self.as_borrowed().is_logical()
1016 }
1017
1018 #[inline]
1020 pub fn as_borrowed(&self) -> PropertyDeclarationId<'_> {
1021 match self {
1022 Self::Longhand(id) => PropertyDeclarationId::Longhand(*id),
1023 Self::Custom(name) => PropertyDeclarationId::Custom(name),
1024 }
1025 }
1026
1027 #[cfg(feature = "gecko")]
1029 #[inline]
1030 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
1031 Some(match PropertyId::from_gecko_css_property_id(property)? {
1032 PropertyId::Custom(name) => Self::Custom(name),
1033 PropertyId::NonCustom(id) => Self::Longhand(id.as_longhand()?),
1034 })
1035 }
1036}
1037
1038#[derive(Clone, Copy, Debug, PartialEq, MallocSizeOf)]
1041pub enum PropertyDeclarationId<'a> {
1042 Longhand(LonghandId),
1044 Custom(&'a custom_properties::Name),
1046}
1047
1048impl<'a> ToCss for PropertyDeclarationId<'a> {
1049 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1050 where
1051 W: Write,
1052 {
1053 match *self {
1054 PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
1055 PropertyDeclarationId::Custom(name) => {
1056 dest.write_str("--")?;
1057 serialize_atom_name(name, dest)
1058 },
1059 }
1060 }
1061}
1062
1063impl<'a> PropertyDeclarationId<'a> {
1064 #[inline(always)]
1066 pub fn flags(&self) -> PropertyFlags {
1067 match self {
1068 Self::Longhand(id) => id.flags(),
1069 Self::Custom(_) => PropertyFlags::empty(),
1070 }
1071 }
1072
1073 pub fn to_owned(&self) -> OwnedPropertyDeclarationId {
1075 match self {
1076 PropertyDeclarationId::Longhand(id) => OwnedPropertyDeclarationId::Longhand(*id),
1077 PropertyDeclarationId::Custom(name) => {
1078 OwnedPropertyDeclarationId::Custom((*name).clone())
1079 },
1080 }
1081 }
1082
1083 pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
1086 match *self {
1087 PropertyDeclarationId::Longhand(id) => match *other {
1088 PropertyId::NonCustom(non_custom_id) => id.is_or_is_longhand_of(non_custom_id),
1089 PropertyId::Custom(_) => false,
1090 },
1091 PropertyDeclarationId::Custom(name) => {
1092 matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
1093 },
1094 }
1095 }
1096
1097 pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
1100 match *self {
1101 PropertyDeclarationId::Longhand(ref id) => id.is_longhand_of(shorthand),
1102 _ => false,
1103 }
1104 }
1105
1106 pub fn name(&self) -> Cow<'static, str> {
1108 match *self {
1109 PropertyDeclarationId::Longhand(id) => id.name().into(),
1110 PropertyDeclarationId::Custom(name) => {
1111 let mut s = String::new();
1112 write!(&mut s, "--{}", name).unwrap();
1113 s.into()
1114 },
1115 }
1116 }
1117
1118 #[inline]
1120 pub fn as_longhand(&self) -> Option<LonghandId> {
1121 match *self {
1122 PropertyDeclarationId::Longhand(id) => Some(id),
1123 _ => None,
1124 }
1125 }
1126
1127 #[inline]
1129 pub fn is_logical(&self) -> bool {
1130 match self {
1131 PropertyDeclarationId::Longhand(id) => id.is_logical(),
1132 PropertyDeclarationId::Custom(_) => false,
1133 }
1134 }
1135
1136 #[inline]
1141 pub fn to_physical(&self, wm: WritingMode) -> Self {
1142 match self {
1143 Self::Longhand(id) => Self::Longhand(id.to_physical(wm)),
1144 Self::Custom(_) => self.clone(),
1145 }
1146 }
1147
1148 #[inline]
1150 pub fn is_animatable(&self) -> bool {
1151 match self {
1152 Self::Longhand(id) => id.is_animatable(),
1153 Self::Custom(_) => true,
1154 }
1155 }
1156
1157 #[inline]
1159 pub fn is_discrete_animatable(&self) -> bool {
1160 match self {
1161 Self::Longhand(longhand) => longhand.is_discrete_animatable(),
1162 Self::Custom(_) => true,
1164 }
1165 }
1166
1167 #[cfg(feature = "gecko")]
1170 #[inline]
1171 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
1172 match self {
1173 PropertyDeclarationId::Longhand(id) => id.to_noncustomcsspropertyid(),
1174 PropertyDeclarationId::Custom(_) => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1175 }
1176 }
1177
1178 #[cfg(feature = "gecko")]
1183 #[inline]
1184 pub fn to_gecko_css_property_id(&self) -> CSSPropertyId {
1185 match self {
1186 Self::Longhand(id) => CSSPropertyId {
1187 mId: id.to_noncustomcsspropertyid(),
1188 mCustomName: RefPtr::null(),
1189 },
1190 Self::Custom(name) => {
1191 let mut property_id = CSSPropertyId {
1192 mId: NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1193 mCustomName: RefPtr::null(),
1194 };
1195 property_id.mCustomName.mRawPtr = (*name).clone().into_addrefed();
1196 property_id
1197 },
1198 }
1199 }
1200}
1201
1202pub trait IndexedId: Copy {
1205 const COUNT: usize;
1207 unsafe fn from_index_release_unchecked(index: usize) -> Self;
1210 fn to_index(self) -> usize;
1212}
1213
1214impl IndexedId for NonCustomPropertyId {
1215 const COUNT: usize = property_counts::NON_CUSTOM;
1216
1217 #[inline(always)]
1218 unsafe fn from_index_release_unchecked(index: usize) -> Self {
1219 debug_assert!(index < Self::COUNT);
1220 NonCustomPropertyId(index as u16)
1221 }
1222
1223 #[inline(always)]
1224 fn to_index(self) -> usize {
1225 self.0 as usize
1226 }
1227}
1228
1229impl IndexedId for PrioritaryPropertyId {
1230 const COUNT: usize = property_counts::PRIORITARY;
1231
1232 #[inline(always)]
1233 unsafe fn from_index_release_unchecked(index: usize) -> Self {
1234 debug_assert!(index < Self::COUNT);
1235 std::mem::transmute(index as u8)
1236 }
1237
1238 #[inline(always)]
1239 fn to_index(self) -> usize {
1240 self as usize
1241 }
1242}
1243
1244impl IndexedId for LonghandId {
1245 const COUNT: usize = property_counts::LONGHANDS;
1246
1247 #[inline(always)]
1248 unsafe fn from_index_release_unchecked(index: usize) -> Self {
1249 debug_assert!(index < Self::COUNT);
1250 std::mem::transmute(index as u16)
1251 }
1252
1253 #[inline(always)]
1254 fn to_index(self) -> usize {
1255 self as usize
1256 }
1257}
1258
1259pub type NonCustomPropertyIdSet =
1261 IdSet<NonCustomPropertyId, { (property_counts::NON_CUSTOM - 1 + 32) / 32 }>;
1262pub type NonCustomPropertyIdSetIterator<'a> = IdSetIterator<'a, NonCustomPropertyId>;
1264pub type PrioritaryPropertyIdSet =
1266 IdSet<PrioritaryPropertyId, { (property_counts::PRIORITARY - 1 + 32) / 32 }>;
1267pub type PrioritaryPropertyIdSetIterator<'a> = IdSetIterator<'a, PrioritaryPropertyId>;
1269pub type LonghandIdSet = IdSet<LonghandId, { (property_counts::LONGHANDS - 1 + 32) / 32 }>;
1271pub type LonghandIdSetIterator<'a> = IdSetIterator<'a, LonghandId>;
1273
1274pub struct IdSet<Id: IndexedId, const W: usize> {
1281 storage: [u32; W],
1282 _phantom: std::marker::PhantomData<Id>,
1283}
1284
1285impl<Id: IndexedId, const W: usize> Clone for IdSet<Id, W> {
1286 #[inline]
1287 fn clone(&self) -> Self {
1288 *self
1289 }
1290}
1291
1292impl<Id: IndexedId, const W: usize> Copy for IdSet<Id, W> {}
1293
1294impl<Id: IndexedId, const W: usize> Default for IdSet<Id, W> {
1295 #[inline]
1296 fn default() -> Self {
1297 Self {
1298 storage: [0; W],
1299 _phantom: std::marker::PhantomData,
1300 }
1301 }
1302}
1303
1304impl<Id: IndexedId, const W: usize> PartialEq for IdSet<Id, W> {
1305 #[inline]
1306 fn eq(&self, other: &Self) -> bool {
1307 self.storage == other.storage
1308 }
1309}
1310
1311impl<Id: IndexedId, const W: usize> fmt::Debug for IdSet<Id, W> {
1312 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1313 self.storage.fmt(f)
1314 }
1315}
1316
1317impl<Id: IndexedId, const W: usize> malloc_size_of::MallocSizeOf for IdSet<Id, W> {
1318 #[inline(always)]
1319 fn size_of(&self, _: &mut malloc_size_of::MallocSizeOfOps) -> usize {
1320 0
1321 }
1322}
1323
1324impl<Id: IndexedId, const W: usize> IdSet<Id, W> {
1325 #[inline]
1327 pub fn new() -> Self {
1328 Self::default()
1329 }
1330
1331 pub(crate) const fn from_storage(storage: [u32; W]) -> Self {
1333 Self {
1334 storage,
1335 _phantom: std::marker::PhantomData,
1336 }
1337 }
1338
1339 #[inline]
1341 pub fn insert(&mut self, id: Id) {
1342 let bit = id.to_index();
1343 self.storage[bit / 32] |= 1 << (bit % 32);
1344 }
1345
1346 #[inline]
1348 pub fn remove(&mut self, id: Id) {
1349 let bit = id.to_index();
1350 self.storage[bit / 32] &= !(1 << (bit % 32));
1351 }
1352
1353 #[inline]
1355 pub fn contains(&self, id: Id) -> bool {
1356 let bit = id.to_index();
1357 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1358 }
1359
1360 pub fn iter(&self) -> IdSetIterator<'_, Id> {
1362 IdSetIterator {
1363 chunks: &self.storage,
1364 cur_chunk: 0,
1365 cur_bit: 0,
1366 _phantom: std::marker::PhantomData,
1367 }
1368 }
1369
1370 pub fn contains_all(&self, other: &Self) -> bool {
1372 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1373 if (*self_cell & *other_cell) != *other_cell {
1374 return false;
1375 }
1376 }
1377 true
1378 }
1379
1380 pub fn contains_any(&self, other: &Self) -> bool {
1382 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1383 if (*self_cell & *other_cell) != 0 {
1384 return true;
1385 }
1386 }
1387 false
1388 }
1389
1390 #[inline]
1392 pub fn remove_all(&mut self, other: &Self) {
1393 for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
1394 *self_cell &= !*other_cell;
1395 }
1396 }
1397
1398 #[inline]
1400 pub fn clear(&mut self) {
1401 for cell in &mut self.storage {
1402 *cell = 0
1403 }
1404 }
1405
1406 #[inline]
1408 pub fn is_empty(&self) -> bool {
1409 self.storage.iter().all(|c| *c == 0)
1410 }
1411}
1412
1413to_shmem::impl_trivial_to_shmem!(LonghandIdSet);
1414impl LonghandIdSet {
1415 #[inline]
1417 pub fn contains_any_reset(&self) -> bool {
1418 self.contains_any(Self::reset())
1419 }
1420}
1421
1422pub struct IdSetIterator<'a, Id: IndexedId> {
1424 chunks: &'a [u32],
1425 cur_chunk: u32,
1426 cur_bit: u32, _phantom: std::marker::PhantomData<Id>,
1428}
1429
1430impl<'a, Id: IndexedId> Iterator for IdSetIterator<'a, Id> {
1431 type Item = Id;
1432
1433 fn next(&mut self) -> Option<Self::Item> {
1434 loop {
1435 debug_assert!(self.cur_bit < 32);
1436 let cur_chunk = self.cur_chunk;
1437 let cur_bit = self.cur_bit;
1438 let chunk = *self.chunks.get(cur_chunk as usize)?;
1439 let next_bit = (chunk >> cur_bit).trailing_zeros();
1440 if next_bit == 32 {
1441 self.cur_bit = 0;
1443 self.cur_chunk += 1;
1444 continue;
1445 }
1446 debug_assert!(cur_bit + next_bit < 32);
1447 let index = (cur_chunk * 32 + cur_bit + next_bit) as usize;
1448 debug_assert!(index < Id::COUNT);
1449 let id = unsafe { Id::from_index_release_unchecked(index) };
1450 self.cur_bit += next_bit + 1;
1451 if self.cur_bit == 32 {
1452 self.cur_bit = 0;
1453 self.cur_chunk += 1;
1454 }
1455 return Some(id);
1456 }
1457 }
1458}
1459
1460pub type SubpropertiesVec<T> = ArrayVec<T, { property_counts::MAX_SHORTHAND_EXPANDED }>;
1462
1463#[derive(Default)]
1467pub struct SourcePropertyDeclaration {
1468 pub declarations: SubpropertiesVec<PropertyDeclaration>,
1470 pub all_shorthand: AllShorthand,
1472}
1473
1474#[cfg(feature = "gecko")]
1477size_of_test!(SourcePropertyDeclaration, 632);
1478#[cfg(feature = "servo")]
1479size_of_test!(SourcePropertyDeclaration, 568);
1480
1481impl SourcePropertyDeclaration {
1482 #[inline]
1484 pub fn with_one(decl: PropertyDeclaration) -> Self {
1485 let mut result = Self::default();
1486 result.declarations.push(decl);
1487 result
1488 }
1489
1490 pub fn drain(&mut self) -> SourcePropertyDeclarationDrain<'_> {
1492 SourcePropertyDeclarationDrain {
1493 declarations: self.declarations.drain(..),
1494 all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
1495 }
1496 }
1497
1498 pub fn clear(&mut self) {
1500 self.declarations.clear();
1501 self.all_shorthand = AllShorthand::NotSet;
1502 }
1503
1504 pub fn is_empty(&self) -> bool {
1506 self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
1507 }
1508
1509 pub fn push(&mut self, declaration: PropertyDeclaration) {
1511 let _result = self.declarations.try_push(declaration);
1512 debug_assert!(_result.is_ok());
1513 }
1514}
1515
1516pub struct SourcePropertyDeclarationDrain<'a> {
1518 pub declarations:
1520 ArrayVecDrain<'a, PropertyDeclaration, { property_counts::MAX_SHORTHAND_EXPANDED }>,
1521 pub all_shorthand: AllShorthand,
1523}
1524
1525#[derive(Debug, Eq, PartialEq, ToShmem)]
1527pub struct UnparsedValue {
1528 pub(super) variable_value: custom_properties::VariableValue,
1530 from_shorthand: Option<ShorthandId>,
1532}
1533
1534impl ToCss for UnparsedValue {
1535 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1536 where
1537 W: Write,
1538 {
1539 if self.from_shorthand.is_none() {
1541 self.variable_value.to_css(dest)?;
1542 }
1543 Ok(())
1544 }
1545}
1546
1547impl ToTyped for UnparsedValue {
1548 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
1549 if self.from_shorthand.is_none() {
1550 self.variable_value.to_typed(dest)?;
1551 return Ok(());
1552 }
1553 Err(())
1554 }
1555}
1556
1557pub type ShorthandsWithPropertyReferencesCache =
1564 FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
1565
1566impl UnparsedValue {
1567 fn substitute_variables<'cache>(
1568 &self,
1569 longhand_id: LonghandId,
1570 substitution_functions: &ComputedSubstitutionFunctions,
1571 stylist: &Stylist,
1572 computed_context: &computed::Context,
1573 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
1574 attribute_tracker: &mut AttributeTracker,
1575 ) -> Cow<'cache, PropertyDeclaration> {
1576 let invalid_at_computed_value_time = || {
1577 let keyword = if longhand_id.inherited() {
1578 CSSWideKeyword::Inherit
1579 } else {
1580 CSSWideKeyword::Initial
1581 };
1582 Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
1583 };
1584
1585 if computed_context
1586 .builder
1587 .invalid_non_custom_properties
1588 .contains(longhand_id)
1589 {
1590 return invalid_at_computed_value_time();
1591 }
1592
1593 if let Some(shorthand_id) = self.from_shorthand {
1594 let key = (shorthand_id, longhand_id);
1595 if shorthand_cache.contains_key(&key) {
1596 return Cow::Borrowed(&shorthand_cache[&key]);
1601 }
1602 }
1603
1604 let SubstitutionResult { css, attr_taint } = match custom_properties::substitute(
1605 &self.variable_value,
1606 substitution_functions,
1607 stylist,
1608 computed_context,
1609 attribute_tracker,
1610 ) {
1611 Ok(css) => css,
1612 Err(..) => return invalid_at_computed_value_time(),
1613 };
1614
1615 let context = ParserContext::new(
1626 Origin::Author,
1627 &self.variable_value.url_data,
1628 None,
1629 ParsingMode::DEFAULT,
1630 computed_context.quirks_mode,
1631 Default::default(),
1632 None,
1633 None,
1634 attr_taint,
1635 );
1636
1637 let mut input = ParserInput::new(&css);
1638 let mut input = Parser::new(&mut input);
1639 input.skip_whitespace();
1640
1641 if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
1642 return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
1643 }
1644
1645 let shorthand = match self.from_shorthand {
1646 None => {
1647 return match input.parse_entirely(|input| longhand_id.parse_value(&context, input))
1648 {
1649 Ok(decl) => Cow::Owned(decl),
1650 Err(..) => invalid_at_computed_value_time(),
1651 }
1652 },
1653 Some(shorthand) => shorthand,
1654 };
1655
1656 let mut decls = SourcePropertyDeclaration::default();
1657 if shorthand
1659 .parse_into(&mut decls, &context, &mut input)
1660 .is_err()
1661 {
1662 return invalid_at_computed_value_time();
1663 }
1664
1665 for declaration in decls.declarations.drain(..) {
1666 let longhand = declaration.id().as_longhand().unwrap();
1667 if longhand.is_logical() {
1668 let writing_mode = computed_context.builder.writing_mode;
1669 shorthand_cache.insert(
1670 (shorthand, longhand.to_physical(writing_mode)),
1671 declaration.clone(),
1672 );
1673 }
1674 shorthand_cache.insert((shorthand, longhand), declaration);
1675 }
1676
1677 let key = (shorthand, longhand_id);
1678 match shorthand_cache.get(&key) {
1679 Some(decl) => Cow::Borrowed(decl),
1680 None => invalid_at_computed_value_time(),
1692 }
1693 }
1694}
1695pub enum AllShorthand {
1697 NotSet,
1699 CSSWideKeyword(CSSWideKeyword),
1701 WithVariables(Arc<UnparsedValue>),
1703}
1704
1705impl Default for AllShorthand {
1706 fn default() -> Self {
1707 Self::NotSet
1708 }
1709}
1710
1711impl AllShorthand {
1712 #[inline]
1714 pub fn declarations(&self) -> AllShorthandDeclarationIterator<'_> {
1715 AllShorthandDeclarationIterator {
1716 all_shorthand: self,
1717 longhands: ShorthandId::All.longhands(),
1718 }
1719 }
1720}
1721
1722pub struct AllShorthandDeclarationIterator<'a> {
1724 all_shorthand: &'a AllShorthand,
1725 longhands: NonCustomPropertyIterator<LonghandId>,
1726}
1727
1728impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
1729 type Item = PropertyDeclaration;
1730
1731 #[inline]
1732 fn next(&mut self) -> Option<Self::Item> {
1733 match *self.all_shorthand {
1734 AllShorthand::NotSet => None,
1735 AllShorthand::CSSWideKeyword(ref keyword) => Some(
1736 PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword),
1737 ),
1738 AllShorthand::WithVariables(ref unparsed) => {
1739 Some(PropertyDeclaration::WithVariables(VariableDeclaration {
1740 id: self.longhands.next()?,
1741 value: unparsed.clone(),
1742 }))
1743 },
1744 }
1745 }
1746}
1747
1748pub struct NonCustomPropertyIterator<Item: 'static> {
1751 filter: bool,
1752 iter: std::slice::Iter<'static, Item>,
1753}
1754
1755impl<Item> Iterator for NonCustomPropertyIterator<Item>
1756where
1757 Item: 'static + Copy + Into<NonCustomPropertyId>,
1758{
1759 type Item = Item;
1760
1761 fn next(&mut self) -> Option<Self::Item> {
1762 loop {
1763 let id = *self.iter.next()?;
1764 if !self.filter || id.into().enabled_for_all_content() {
1765 return Some(id);
1766 }
1767 }
1768 }
1769}
1770
1771pub struct TransitionPropertyIterator<'a> {
1773 style: &'a ComputedValues,
1774 index_range: core::ops::Range<usize>,
1775 longhand_iterator: Option<NonCustomPropertyIterator<LonghandId>>,
1776}
1777
1778impl<'a> TransitionPropertyIterator<'a> {
1779 pub fn from_style(style: &'a ComputedValues) -> Self {
1781 Self {
1782 style,
1783 index_range: 0..style.get_ui().transition_property_count(),
1784 longhand_iterator: None,
1785 }
1786 }
1787}
1788
1789pub struct TransitionPropertyIteration {
1791 pub property: OwnedPropertyDeclarationId,
1793 pub index: usize,
1796}
1797
1798impl<'a> Iterator for TransitionPropertyIterator<'a> {
1799 type Item = TransitionPropertyIteration;
1800
1801 fn next(&mut self) -> Option<Self::Item> {
1802 use crate::values::computed::TransitionProperty;
1803 loop {
1804 if let Some(ref mut longhand_iterator) = self.longhand_iterator {
1805 if let Some(longhand_id) = longhand_iterator.next() {
1806 return Some(TransitionPropertyIteration {
1807 property: OwnedPropertyDeclarationId::Longhand(longhand_id),
1808 index: self.index_range.start - 1,
1809 });
1810 }
1811 self.longhand_iterator = None;
1812 }
1813
1814 let index = self.index_range.next()?;
1815 match self.style.get_ui().transition_property_at(index) {
1816 TransitionProperty::NonCustom(id) => {
1817 match id.longhand_or_shorthand() {
1818 Ok(longhand_id) => {
1819 return Some(TransitionPropertyIteration {
1820 property: OwnedPropertyDeclarationId::Longhand(longhand_id),
1821 index,
1822 });
1823 },
1824 Err(shorthand_id) => {
1825 self.longhand_iterator = Some(shorthand_id.longhands());
1829 },
1830 }
1831 },
1832 TransitionProperty::Custom(name) => {
1833 return Some(TransitionPropertyIteration {
1834 property: OwnedPropertyDeclarationId::Custom(name),
1835 index,
1836 })
1837 },
1838 TransitionProperty::Unsupported(..) => {},
1839 }
1840 }
1841 }
1842}