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::values::{computed, serialize_atom_name};
36use arrayvec::{ArrayVec, Drain as ArrayVecDrain};
37use cssparser::{match_ignore_ascii_case, Parser, ParserInput};
38use rustc_hash::FxHashMap;
39use servo_arc::Arc;
40use std::{
41 borrow::Cow,
42 fmt::{self, Write},
43 mem,
44};
45use style_traits::{
46 CssString, CssWriter, KeywordsCollectFn, ParseError, ParsingMode, SpecifiedValueInfo, ToCss,
47 ToTyped, TypedValue,
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
335impl From<LonghandId> for NonCustomPropertyId {
336 #[inline]
337 fn from(id: LonghandId) -> Self {
338 Self::from_longhand(id)
339 }
340}
341
342impl From<ShorthandId> for NonCustomPropertyId {
343 #[inline]
344 fn from(id: ShorthandId) -> Self {
345 Self::from_shorthand(id)
346 }
347}
348
349impl From<AliasId> for NonCustomPropertyId {
350 #[inline]
351 fn from(id: AliasId) -> Self {
352 Self::from_alias(id)
353 }
354}
355
356#[derive(Clone, Eq, PartialEq, Debug)]
359pub enum PropertyId {
360 NonCustom(NonCustomPropertyId),
362 Custom(custom_properties::Name),
364}
365
366impl ToCss for PropertyId {
367 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
368 where
369 W: Write,
370 {
371 match *self {
372 PropertyId::NonCustom(id) => dest.write_str(id.name()),
373 PropertyId::Custom(ref name) => {
374 dest.write_str("--")?;
375 serialize_atom_name(name, dest)
376 },
377 }
378 }
379}
380
381impl PropertyId {
382 #[inline]
384 pub fn longhand_id(&self) -> Option<LonghandId> {
385 self.non_custom_non_alias_id()?.as_longhand()
386 }
387
388 pub fn is_animatable(&self) -> bool {
390 match self {
391 Self::NonCustom(id) => id.is_animatable(),
392 Self::Custom(_) => true,
393 }
394 }
395
396 pub fn parse_unchecked_for_testing(name: &str) -> Result<Self, ()> {
401 Self::parse_unchecked(name, None)
402 }
403
404 #[inline]
407 pub fn parse_enabled_for_all_content(name: &str) -> Result<Self, ()> {
408 let id = Self::parse_unchecked(name, None)?;
409
410 if !id.enabled_for_all_content() {
411 return Err(());
412 }
413
414 Ok(id)
415 }
416
417 #[inline]
420 pub fn parse(name: &str, context: &ParserContext) -> Result<Self, ()> {
421 let id = Self::parse_unchecked(name, context.use_counters)?;
422 if !id.allowed_in(context) {
423 return Err(());
424 }
425 Ok(id)
426 }
427
428 #[inline]
433 pub fn parse_ignoring_rule_type(name: &str, context: &ParserContext) -> Result<Self, ()> {
434 let id = Self::parse_unchecked(name, None)?;
435 if !id.allowed_in_ignoring_rule_type(context) {
436 return Err(());
437 }
438 Ok(id)
439 }
440
441 #[cfg(feature = "gecko")]
443 #[inline]
444 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
445 Some(NonCustomPropertyId::from_noncustomcsspropertyid(id)?.to_property_id())
446 }
447
448 #[cfg(feature = "gecko")]
450 #[inline]
451 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
452 Some(
453 if property.mId == NonCustomCSSPropertyId::eCSSPropertyExtra_variable {
454 debug_assert!(!property.mCustomName.mRawPtr.is_null());
455 Self::Custom(unsafe { crate::Atom::from_raw(property.mCustomName.mRawPtr) })
456 } else {
457 Self::NonCustom(NonCustomPropertyId::from_noncustomcsspropertyid(
458 property.mId,
459 )?)
460 },
461 )
462 }
463
464 #[inline]
466 pub fn is_shorthand(&self) -> bool {
467 self.as_shorthand().is_ok()
468 }
469
470 pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId<'_>> {
473 match *self {
474 Self::NonCustom(id) => match id.longhand_or_shorthand() {
475 Ok(lh) => Err(PropertyDeclarationId::Longhand(lh)),
476 Err(sh) => Ok(sh),
477 },
478 Self::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),
479 }
480 }
481
482 pub fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
484 match *self {
485 Self::Custom(_) => None,
486 Self::NonCustom(id) => Some(id),
487 }
488 }
489
490 fn non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId> {
493 self.non_custom_id().map(NonCustomPropertyId::unaliased)
494 }
495
496 #[inline]
499 pub fn enabled_for_all_content(&self) -> bool {
500 let id = match self.non_custom_id() {
501 None => return true,
503 Some(id) => id,
504 };
505
506 id.enabled_for_all_content()
507 }
508
509 #[cfg(feature = "gecko")]
513 #[inline]
514 pub fn to_noncustomcsspropertyid_resolving_aliases(&self) -> NonCustomCSSPropertyId {
515 match self.non_custom_non_alias_id() {
516 Some(id) => id.to_noncustomcsspropertyid(),
517 None => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
518 }
519 }
520
521 fn allowed_in(&self, context: &ParserContext) -> bool {
522 let id = match self.non_custom_id() {
523 None => {
525 return !context
526 .nesting_context
527 .rule_types
528 .contains(CssRuleType::PositionTry)
529 },
530 Some(id) => id,
531 };
532 id.allowed_in(context)
533 }
534
535 #[inline]
536 fn allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool {
537 let id = match self.non_custom_id() {
538 None => return true,
540 Some(id) => id,
541 };
542 id.allowed_in_ignoring_rule_type(context)
543 }
544
545 pub fn supports_type(&self, ty: u8) -> bool {
548 let id = self.non_custom_non_alias_id();
549 id.map_or(0, |id| id.supported_types()) & ty != 0
550 }
551
552 pub fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
557 if let Some(id) = self.non_custom_non_alias_id() {
558 id.collect_property_completion_keywords(f);
559 }
560 CSSWideKeyword::collect_completion_keywords(f);
561 }
562}
563
564impl ToCss for LonghandId {
565 #[inline]
566 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
567 where
568 W: Write,
569 {
570 dest.write_str(self.name())
571 }
572}
573
574impl fmt::Debug for LonghandId {
575 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
576 formatter.write_str(self.name())
577 }
578}
579
580impl LonghandId {
581 #[inline]
583 pub fn name(&self) -> &'static str {
584 NonCustomPropertyId::from(*self).name()
585 }
586
587 #[inline]
589 pub fn inherited(self) -> bool {
590 !LonghandIdSet::reset().contains(self)
591 }
592
593 #[inline]
595 pub fn zoom_dependent(self) -> bool {
596 LonghandIdSet::zoom_dependent().contains(self)
597 }
598
599 #[inline]
602 pub fn ignored_when_document_colors_disabled(self) -> bool {
603 LonghandIdSet::ignored_when_colors_disabled().contains(self)
604 }
605
606 pub fn is_or_is_longhand_of(self, non_custom: NonCustomPropertyId) -> bool {
608 match non_custom.longhand_or_shorthand() {
609 Ok(lh) => self == lh,
610 Err(sh) => self.is_longhand_of(sh),
611 }
612 }
613
614 pub fn is_longhand_of(self, shorthand: ShorthandId) -> bool {
616 self.shorthands().any(|s| s == shorthand)
617 }
618
619 #[inline]
621 pub fn is_animatable(self) -> bool {
622 NonCustomPropertyId::from(self).is_animatable()
623 }
624
625 #[inline]
627 pub fn is_discrete_animatable(self) -> bool {
628 LonghandIdSet::discrete_animatable().contains(self)
629 }
630
631 #[cfg(feature = "gecko")]
633 #[inline]
634 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
635 NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
636 }
637
638 #[cfg(feature = "gecko")]
639 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
641 NonCustomPropertyId::from_noncustomcsspropertyid(id)?
642 .unaliased()
643 .as_longhand()
644 }
645
646 #[inline]
648 pub fn is_logical(self) -> bool {
649 LonghandIdSet::logical().contains(self)
650 }
651}
652
653impl ToCss for ShorthandId {
654 #[inline]
655 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
656 where
657 W: Write,
658 {
659 dest.write_str(self.name())
660 }
661}
662
663impl ShorthandId {
664 #[inline]
666 pub fn name(&self) -> &'static str {
667 NonCustomPropertyId::from(*self).name()
668 }
669
670 #[cfg(feature = "gecko")]
672 #[inline]
673 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
674 NonCustomPropertyId::from(self).to_noncustomcsspropertyid()
675 }
676
677 #[cfg(feature = "gecko")]
679 #[inline]
680 pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {
681 NonCustomPropertyId::from_noncustomcsspropertyid(id)?
682 .unaliased()
683 .as_shorthand()
684 }
685
686 pub fn get_shorthand_appendable_value<'a, 'b: 'a>(
690 self,
691 declarations: &'a [&'b PropertyDeclaration],
692 ) -> Option<AppendableValue<'a, 'b>> {
693 let first_declaration = declarations.get(0)?;
694 let rest = || declarations.iter().skip(1);
695
696 if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
698 if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
699 return Some(AppendableValue::Css(css));
700 }
701 return None;
702 }
703
704 if let Some(keyword) = first_declaration.get_css_wide_keyword() {
706 if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {
707 return Some(AppendableValue::Css(keyword.to_str()));
708 }
709 return None;
710 }
711
712 if self == ShorthandId::All {
713 return None;
715 }
716
717 if declarations
719 .iter()
720 .all(|d| d.may_serialize_as_part_of_shorthand())
721 {
722 return Some(AppendableValue::DeclarationsForShorthand(
723 self,
724 declarations,
725 ));
726 }
727
728 None
729 }
730
731 #[inline]
733 pub fn is_legacy_shorthand(self) -> bool {
734 self.flags().contains(PropertyFlags::IS_LEGACY_SHORTHAND)
735 }
736}
737
738pub fn enabled_arbitrary_substitution_functions() -> &'static [&'static str] {
740 if static_prefs::pref!("layout.css.attr.enabled") {
741 &["var", "env", "attr"]
742 } else {
743 &["var", "env"]
744 }
745}
746
747fn parse_non_custom_property_declaration_value_into<'i>(
748 declarations: &mut SourcePropertyDeclaration,
749 context: &ParserContext,
750 input: &mut Parser<'i, '_>,
751 start: &cssparser::ParserState,
752 parse_entirely_into: impl FnOnce(
753 &mut SourcePropertyDeclaration,
754 &mut Parser<'i, '_>,
755 ) -> Result<(), ParseError<'i>>,
756 parsed_wide_keyword: impl FnOnce(&mut SourcePropertyDeclaration, CSSWideKeyword),
757 parsed_custom: impl FnOnce(&mut SourcePropertyDeclaration, custom_properties::VariableValue),
758) -> Result<(), ParseError<'i>> {
759 let mut starts_with_curly_block = false;
760 if let Ok(token) = input.next() {
761 match token {
762 cssparser::Token::Ident(ref ident) => match CSSWideKeyword::from_ident(ident) {
763 Ok(wk) => {
764 if input.expect_exhausted().is_ok() {
765 return Ok(parsed_wide_keyword(declarations, wk));
766 }
767 },
768 Err(()) => {},
769 },
770 cssparser::Token::CurlyBracketBlock => {
771 starts_with_curly_block = true;
772 },
773 _ => {},
774 }
775 };
776
777 input.reset(&start);
778 input.look_for_arbitrary_substitution_functions(enabled_arbitrary_substitution_functions());
779
780 let err = match parse_entirely_into(declarations, input) {
781 Ok(()) => {
782 input.seen_arbitrary_substitution_functions();
783 return Ok(());
784 },
785 Err(e) => e,
786 };
787
788 let start_pos = start.position();
790 let mut at_start = start_pos == input.position();
791 let mut invalid = false;
792 while let Ok(token) = input.next() {
793 if matches!(token, cssparser::Token::CurlyBracketBlock) {
794 if !starts_with_curly_block || !at_start {
795 invalid = true;
796 break;
797 }
798 } else if starts_with_curly_block {
799 invalid = true;
800 break;
801 }
802 at_start = false;
803 }
804 if !input.seen_arbitrary_substitution_functions() || invalid {
805 return Err(err);
806 }
807 input.reset(start);
808 let value = custom_properties::VariableValue::parse(
809 input,
810 Some(&context.namespaces.prefixes),
811 &context.url_data,
812 )?;
813 parsed_custom(declarations, value);
814 Ok(())
815}
816
817impl PropertyDeclaration {
818 fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option<&str> {
819 match *self {
820 PropertyDeclaration::WithVariables(ref declaration) => {
821 let s = declaration.value.from_shorthand?;
822 if s != shorthand {
823 return None;
824 }
825 Some(&*declaration.value.variable_value.css)
826 },
827 _ => None,
828 }
829 }
830
831 #[inline]
833 pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
834 Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
835 }
836
837 #[inline]
839 pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
840 match *self {
841 PropertyDeclaration::CSSWideKeyword(ref declaration) => Some(declaration.keyword),
842 _ => None,
843 }
844 }
845
846 pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
859 match *self {
860 PropertyDeclaration::CSSWideKeyword(..) | PropertyDeclaration::WithVariables(..) => {
861 false
862 },
863 PropertyDeclaration::Custom(..) => {
864 unreachable!("Serializing a custom property as part of shorthand?")
865 },
866 _ => true,
867 }
868 }
869
870 pub fn is_animatable(&self) -> bool {
872 self.id().is_animatable()
873 }
874
875 pub fn is_custom(&self) -> bool {
878 matches!(*self, PropertyDeclaration::Custom(..))
879 }
880
881 pub fn parse_into<'i, 't>(
892 declarations: &mut SourcePropertyDeclaration,
893 id: PropertyId,
894 context: &ParserContext,
895 input: &mut Parser<'i, 't>,
896 ) -> Result<(), ParseError<'i>> {
897 assert!(declarations.is_empty());
898 debug_assert!(id.allowed_in(context), "{:?}", id);
899 input.skip_whitespace();
900
901 let start = input.state();
902 let non_custom_id = match id {
903 PropertyId::Custom(property_name) => {
904 let value = match input.try_parse(CSSWideKeyword::parse) {
905 Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
906 Err(()) => CustomDeclarationValue::Unparsed(Arc::new(
907 custom_properties::VariableValue::parse(
908 input,
909 Some(&context.namespaces.prefixes),
910 &context.url_data,
911 )?,
912 )),
913 };
914 declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
915 name: property_name,
916 value,
917 }));
918 return Ok(());
919 },
920 PropertyId::NonCustom(id) => id,
921 };
922 match non_custom_id.longhand_or_shorthand() {
923 Ok(longhand_id) => {
924 parse_non_custom_property_declaration_value_into(
925 declarations,
926 context,
927 input,
928 &start,
929 |declarations, input| {
930 let decl = input
931 .parse_entirely(|input| longhand_id.parse_value(context, input))?;
932 declarations.push(decl);
933 Ok(())
934 },
935 |declarations, wk| {
936 declarations.push(PropertyDeclaration::css_wide_keyword(longhand_id, wk));
937 },
938 |declarations, variable_value| {
939 declarations.push(PropertyDeclaration::WithVariables(VariableDeclaration {
940 id: longhand_id,
941 value: Arc::new(UnparsedValue {
942 variable_value,
943 from_shorthand: None,
944 }),
945 }))
946 },
947 )?;
948 },
949 Err(shorthand_id) => {
950 parse_non_custom_property_declaration_value_into(
951 declarations,
952 context,
953 input,
954 &start,
955 |declarations, input| shorthand_id.parse_into(declarations, context, input),
958 |declarations, wk| {
959 if shorthand_id == ShorthandId::All {
960 declarations.all_shorthand = AllShorthand::CSSWideKeyword(wk)
961 } else {
962 for longhand in shorthand_id.longhands() {
963 declarations
964 .push(PropertyDeclaration::css_wide_keyword(longhand, wk));
965 }
966 }
967 },
968 |declarations, variable_value| {
969 let unparsed = Arc::new(UnparsedValue {
970 variable_value,
971 from_shorthand: Some(shorthand_id),
972 });
973 if shorthand_id == ShorthandId::All {
974 declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
975 } else {
976 for id in shorthand_id.longhands() {
977 declarations.push(PropertyDeclaration::WithVariables(
978 VariableDeclaration {
979 id,
980 value: unparsed.clone(),
981 },
982 ))
983 }
984 }
985 },
986 )?;
987 },
988 }
989 if let Some(use_counters) = context.use_counters {
990 use_counters.non_custom_properties.record(non_custom_id);
991 }
992 Ok(())
993 }
994}
995
996#[derive(Clone, Debug, PartialEq, Eq, Hash)]
998pub enum OwnedPropertyDeclarationId {
999 Longhand(LonghandId),
1001 Custom(custom_properties::Name),
1003}
1004
1005impl OwnedPropertyDeclarationId {
1006 #[inline]
1008 pub fn is_logical(&self) -> bool {
1009 self.as_borrowed().is_logical()
1010 }
1011
1012 #[inline]
1014 pub fn as_borrowed(&self) -> PropertyDeclarationId<'_> {
1015 match self {
1016 Self::Longhand(id) => PropertyDeclarationId::Longhand(*id),
1017 Self::Custom(name) => PropertyDeclarationId::Custom(name),
1018 }
1019 }
1020
1021 #[cfg(feature = "gecko")]
1023 #[inline]
1024 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
1025 Some(match PropertyId::from_gecko_css_property_id(property)? {
1026 PropertyId::Custom(name) => Self::Custom(name),
1027 PropertyId::NonCustom(id) => Self::Longhand(id.as_longhand()?),
1028 })
1029 }
1030}
1031
1032#[derive(Clone, Copy, Debug, PartialEq, MallocSizeOf)]
1035pub enum PropertyDeclarationId<'a> {
1036 Longhand(LonghandId),
1038 Custom(&'a custom_properties::Name),
1040}
1041
1042impl<'a> ToCss for PropertyDeclarationId<'a> {
1043 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1044 where
1045 W: Write,
1046 {
1047 match *self {
1048 PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
1049 PropertyDeclarationId::Custom(name) => {
1050 dest.write_str("--")?;
1051 serialize_atom_name(name, dest)
1052 },
1053 }
1054 }
1055}
1056
1057impl<'a> PropertyDeclarationId<'a> {
1058 #[inline(always)]
1060 pub fn flags(&self) -> PropertyFlags {
1061 match self {
1062 Self::Longhand(id) => id.flags(),
1063 Self::Custom(_) => PropertyFlags::empty(),
1064 }
1065 }
1066
1067 pub fn to_owned(&self) -> OwnedPropertyDeclarationId {
1069 match self {
1070 PropertyDeclarationId::Longhand(id) => OwnedPropertyDeclarationId::Longhand(*id),
1071 PropertyDeclarationId::Custom(name) => {
1072 OwnedPropertyDeclarationId::Custom((*name).clone())
1073 },
1074 }
1075 }
1076
1077 pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
1080 match *self {
1081 PropertyDeclarationId::Longhand(id) => match *other {
1082 PropertyId::NonCustom(non_custom_id) => id.is_or_is_longhand_of(non_custom_id),
1083 PropertyId::Custom(_) => false,
1084 },
1085 PropertyDeclarationId::Custom(name) => {
1086 matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
1087 },
1088 }
1089 }
1090
1091 pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
1094 match *self {
1095 PropertyDeclarationId::Longhand(ref id) => id.is_longhand_of(shorthand),
1096 _ => false,
1097 }
1098 }
1099
1100 pub fn name(&self) -> Cow<'static, str> {
1102 match *self {
1103 PropertyDeclarationId::Longhand(id) => id.name().into(),
1104 PropertyDeclarationId::Custom(name) => {
1105 let mut s = String::new();
1106 write!(&mut s, "--{}", name).unwrap();
1107 s.into()
1108 },
1109 }
1110 }
1111
1112 #[inline]
1114 pub fn as_longhand(&self) -> Option<LonghandId> {
1115 match *self {
1116 PropertyDeclarationId::Longhand(id) => Some(id),
1117 _ => None,
1118 }
1119 }
1120
1121 #[inline]
1123 pub fn is_logical(&self) -> bool {
1124 match self {
1125 PropertyDeclarationId::Longhand(id) => id.is_logical(),
1126 PropertyDeclarationId::Custom(_) => false,
1127 }
1128 }
1129
1130 #[inline]
1135 pub fn to_physical(&self, wm: WritingMode) -> Self {
1136 match self {
1137 Self::Longhand(id) => Self::Longhand(id.to_physical(wm)),
1138 Self::Custom(_) => self.clone(),
1139 }
1140 }
1141
1142 #[inline]
1144 pub fn is_animatable(&self) -> bool {
1145 match self {
1146 Self::Longhand(id) => id.is_animatable(),
1147 Self::Custom(_) => true,
1148 }
1149 }
1150
1151 #[inline]
1153 pub fn is_discrete_animatable(&self) -> bool {
1154 match self {
1155 Self::Longhand(longhand) => longhand.is_discrete_animatable(),
1156 Self::Custom(_) => true,
1158 }
1159 }
1160
1161 #[cfg(feature = "gecko")]
1164 #[inline]
1165 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
1166 match self {
1167 PropertyDeclarationId::Longhand(id) => id.to_noncustomcsspropertyid(),
1168 PropertyDeclarationId::Custom(_) => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1169 }
1170 }
1171
1172 #[cfg(feature = "gecko")]
1177 #[inline]
1178 pub fn to_gecko_css_property_id(&self) -> CSSPropertyId {
1179 match self {
1180 Self::Longhand(id) => CSSPropertyId {
1181 mId: id.to_noncustomcsspropertyid(),
1182 mCustomName: RefPtr::null(),
1183 },
1184 Self::Custom(name) => {
1185 let mut property_id = CSSPropertyId {
1186 mId: NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1187 mCustomName: RefPtr::null(),
1188 };
1189 property_id.mCustomName.mRawPtr = (*name).clone().into_addrefed();
1190 property_id
1191 },
1192 }
1193 }
1194}
1195
1196#[derive(Clone, PartialEq, Default)]
1198pub struct NonCustomPropertyIdSet {
1199 storage: [u32; ((property_counts::NON_CUSTOM as usize) - 1 + 32) / 32],
1200}
1201
1202impl NonCustomPropertyIdSet {
1203 pub fn new() -> Self {
1205 Self {
1206 storage: Default::default(),
1207 }
1208 }
1209
1210 #[inline]
1212 pub fn insert(&mut self, id: NonCustomPropertyId) {
1213 let bit = id.0 as usize;
1214 self.storage[bit / 32] |= 1 << (bit % 32);
1215 }
1216
1217 #[inline]
1219 pub fn contains(&self, id: NonCustomPropertyId) -> bool {
1220 let bit = id.0 as usize;
1221 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1222 }
1223}
1224
1225#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
1227pub struct LonghandIdSet {
1228 storage: [u32; ((property_counts::LONGHANDS as usize) - 1 + 32) / 32],
1229}
1230
1231to_shmem::impl_trivial_to_shmem!(LonghandIdSet);
1232
1233impl LonghandIdSet {
1234 #[inline]
1236 pub fn new() -> Self {
1237 Self {
1238 storage: Default::default(),
1239 }
1240 }
1241
1242 pub fn iter(&self) -> LonghandIdSetIterator<'_> {
1244 LonghandIdSetIterator {
1245 chunks: &self.storage,
1246 cur_chunk: 0,
1247 cur_bit: 0,
1248 }
1249 }
1250
1251 pub fn contains_all(&self, other: &Self) -> bool {
1254 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1255 if (*self_cell & *other_cell) != *other_cell {
1256 return false;
1257 }
1258 }
1259 true
1260 }
1261
1262 pub fn contains_any(&self, other: &Self) -> bool {
1264 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1265 if (*self_cell & *other_cell) != 0 {
1266 return true;
1267 }
1268 }
1269 false
1270 }
1271
1272 #[inline]
1274 pub fn remove_all(&mut self, other: &Self) {
1275 for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
1276 *self_cell &= !*other_cell;
1277 }
1278 }
1279
1280 #[inline]
1282 pub fn contains(&self, id: LonghandId) -> bool {
1283 let bit = id as usize;
1284 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1285 }
1286
1287 #[inline]
1289 pub fn contains_any_reset(&self) -> bool {
1290 self.contains_any(Self::reset())
1291 }
1292
1293 #[inline]
1295 pub fn insert(&mut self, id: LonghandId) {
1296 let bit = id as usize;
1297 self.storage[bit / 32] |= 1 << (bit % 32);
1298 }
1299
1300 #[inline]
1302 pub fn remove(&mut self, id: LonghandId) {
1303 let bit = id as usize;
1304 self.storage[bit / 32] &= !(1 << (bit % 32));
1305 }
1306
1307 #[inline]
1309 pub fn clear(&mut self) {
1310 for cell in &mut self.storage {
1311 *cell = 0
1312 }
1313 }
1314
1315 #[inline]
1317 pub fn is_empty(&self) -> bool {
1318 self.storage.iter().all(|c| *c == 0)
1319 }
1320}
1321
1322pub struct LonghandIdSetIterator<'a> {
1324 chunks: &'a [u32],
1325 cur_chunk: u32,
1326 cur_bit: u32, }
1328
1329impl<'a> Iterator for LonghandIdSetIterator<'a> {
1330 type Item = LonghandId;
1331
1332 fn next(&mut self) -> Option<Self::Item> {
1333 loop {
1334 debug_assert!(self.cur_bit < 32);
1335 let cur_chunk = self.cur_chunk;
1336 let cur_bit = self.cur_bit;
1337 let chunk = *self.chunks.get(cur_chunk as usize)?;
1338 let next_bit = (chunk >> cur_bit).trailing_zeros();
1339 if next_bit == 32 {
1340 self.cur_bit = 0;
1342 self.cur_chunk += 1;
1343 continue;
1344 }
1345 debug_assert!(cur_bit + next_bit < 32);
1346 let longhand_id = cur_chunk * 32 + cur_bit + next_bit;
1347 debug_assert!(longhand_id as usize <= property_counts::LONGHANDS);
1348 let id: LonghandId = unsafe { mem::transmute(longhand_id as u16) };
1349 self.cur_bit += next_bit + 1;
1350 if self.cur_bit == 32 {
1351 self.cur_bit = 0;
1352 self.cur_chunk += 1;
1353 }
1354 return Some(id);
1355 }
1356 }
1357}
1358
1359pub type SubpropertiesVec<T> = ArrayVec<T, { property_counts::MAX_SHORTHAND_EXPANDED }>;
1361
1362#[derive(Default)]
1366pub struct SourcePropertyDeclaration {
1367 pub declarations: SubpropertiesVec<PropertyDeclaration>,
1369 pub all_shorthand: AllShorthand,
1371}
1372
1373#[cfg(feature = "gecko")]
1376size_of_test!(SourcePropertyDeclaration, 632);
1377#[cfg(feature = "servo")]
1378size_of_test!(SourcePropertyDeclaration, 568);
1379
1380impl SourcePropertyDeclaration {
1381 #[inline]
1383 pub fn with_one(decl: PropertyDeclaration) -> Self {
1384 let mut result = Self::default();
1385 result.declarations.push(decl);
1386 result
1387 }
1388
1389 pub fn drain(&mut self) -> SourcePropertyDeclarationDrain<'_> {
1391 SourcePropertyDeclarationDrain {
1392 declarations: self.declarations.drain(..),
1393 all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
1394 }
1395 }
1396
1397 pub fn clear(&mut self) {
1399 self.declarations.clear();
1400 self.all_shorthand = AllShorthand::NotSet;
1401 }
1402
1403 pub fn is_empty(&self) -> bool {
1405 self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
1406 }
1407
1408 pub fn push(&mut self, declaration: PropertyDeclaration) {
1410 let _result = self.declarations.try_push(declaration);
1411 debug_assert!(_result.is_ok());
1412 }
1413}
1414
1415pub struct SourcePropertyDeclarationDrain<'a> {
1417 pub declarations:
1419 ArrayVecDrain<'a, PropertyDeclaration, { property_counts::MAX_SHORTHAND_EXPANDED }>,
1420 pub all_shorthand: AllShorthand,
1422}
1423
1424#[derive(Debug, Eq, PartialEq, ToShmem)]
1426pub struct UnparsedValue {
1427 pub(super) variable_value: custom_properties::VariableValue,
1429 from_shorthand: Option<ShorthandId>,
1431}
1432
1433impl ToCss for UnparsedValue {
1434 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1435 where
1436 W: Write,
1437 {
1438 if self.from_shorthand.is_none() {
1440 self.variable_value.to_css(dest)?;
1441 }
1442 Ok(())
1443 }
1444}
1445
1446impl ToTyped for UnparsedValue {
1447 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
1448 if self.from_shorthand.is_none() {
1449 self.variable_value.to_typed(dest)?;
1450 return Ok(());
1451 }
1452 Err(())
1453 }
1454}
1455
1456pub type ShorthandsWithPropertyReferencesCache =
1463 FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
1464
1465impl UnparsedValue {
1466 fn substitute_variables<'cache>(
1467 &self,
1468 longhand_id: LonghandId,
1469 substitution_functions: &ComputedSubstitutionFunctions,
1470 stylist: &Stylist,
1471 computed_context: &computed::Context,
1472 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
1473 attribute_tracker: &mut AttributeTracker,
1474 ) -> Cow<'cache, PropertyDeclaration> {
1475 let invalid_at_computed_value_time = || {
1476 let keyword = if longhand_id.inherited() {
1477 CSSWideKeyword::Inherit
1478 } else {
1479 CSSWideKeyword::Initial
1480 };
1481 Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
1482 };
1483
1484 if computed_context
1485 .builder
1486 .invalid_non_custom_properties
1487 .contains(longhand_id)
1488 {
1489 return invalid_at_computed_value_time();
1490 }
1491
1492 if let Some(shorthand_id) = self.from_shorthand {
1493 let key = (shorthand_id, longhand_id);
1494 if shorthand_cache.contains_key(&key) {
1495 return Cow::Borrowed(&shorthand_cache[&key]);
1500 }
1501 }
1502
1503 let SubstitutionResult { css, attr_taint } = match custom_properties::substitute(
1504 &self.variable_value,
1505 substitution_functions,
1506 stylist,
1507 computed_context,
1508 attribute_tracker,
1509 ) {
1510 Ok(css) => css,
1511 Err(..) => return invalid_at_computed_value_time(),
1512 };
1513
1514 let context = ParserContext::new(
1525 Origin::Author,
1526 &self.variable_value.url_data,
1527 None,
1528 ParsingMode::DEFAULT,
1529 computed_context.quirks_mode,
1530 Default::default(),
1531 None,
1532 None,
1533 attr_taint,
1534 );
1535
1536 let mut input = ParserInput::new(&css);
1537 let mut input = Parser::new(&mut input);
1538 input.skip_whitespace();
1539
1540 if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
1541 return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
1542 }
1543
1544 let shorthand = match self.from_shorthand {
1545 None => {
1546 return match input.parse_entirely(|input| longhand_id.parse_value(&context, input))
1547 {
1548 Ok(decl) => Cow::Owned(decl),
1549 Err(..) => invalid_at_computed_value_time(),
1550 }
1551 },
1552 Some(shorthand) => shorthand,
1553 };
1554
1555 let mut decls = SourcePropertyDeclaration::default();
1556 if shorthand
1558 .parse_into(&mut decls, &context, &mut input)
1559 .is_err()
1560 {
1561 return invalid_at_computed_value_time();
1562 }
1563
1564 for declaration in decls.declarations.drain(..) {
1565 let longhand = declaration.id().as_longhand().unwrap();
1566 if longhand.is_logical() {
1567 let writing_mode = computed_context.builder.writing_mode;
1568 shorthand_cache.insert(
1569 (shorthand, longhand.to_physical(writing_mode)),
1570 declaration.clone(),
1571 );
1572 }
1573 shorthand_cache.insert((shorthand, longhand), declaration);
1574 }
1575
1576 let key = (shorthand, longhand_id);
1577 match shorthand_cache.get(&key) {
1578 Some(decl) => Cow::Borrowed(decl),
1579 None => invalid_at_computed_value_time(),
1591 }
1592 }
1593}
1594pub enum AllShorthand {
1596 NotSet,
1598 CSSWideKeyword(CSSWideKeyword),
1600 WithVariables(Arc<UnparsedValue>),
1602}
1603
1604impl Default for AllShorthand {
1605 fn default() -> Self {
1606 Self::NotSet
1607 }
1608}
1609
1610impl AllShorthand {
1611 #[inline]
1613 pub fn declarations(&self) -> AllShorthandDeclarationIterator<'_> {
1614 AllShorthandDeclarationIterator {
1615 all_shorthand: self,
1616 longhands: ShorthandId::All.longhands(),
1617 }
1618 }
1619}
1620
1621pub struct AllShorthandDeclarationIterator<'a> {
1623 all_shorthand: &'a AllShorthand,
1624 longhands: NonCustomPropertyIterator<LonghandId>,
1625}
1626
1627impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
1628 type Item = PropertyDeclaration;
1629
1630 #[inline]
1631 fn next(&mut self) -> Option<Self::Item> {
1632 match *self.all_shorthand {
1633 AllShorthand::NotSet => None,
1634 AllShorthand::CSSWideKeyword(ref keyword) => Some(
1635 PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword),
1636 ),
1637 AllShorthand::WithVariables(ref unparsed) => {
1638 Some(PropertyDeclaration::WithVariables(VariableDeclaration {
1639 id: self.longhands.next()?,
1640 value: unparsed.clone(),
1641 }))
1642 },
1643 }
1644 }
1645}
1646
1647pub struct NonCustomPropertyIterator<Item: 'static> {
1650 filter: bool,
1651 iter: std::slice::Iter<'static, Item>,
1652}
1653
1654impl<Item> Iterator for NonCustomPropertyIterator<Item>
1655where
1656 Item: 'static + Copy + Into<NonCustomPropertyId>,
1657{
1658 type Item = Item;
1659
1660 fn next(&mut self) -> Option<Self::Item> {
1661 loop {
1662 let id = *self.iter.next()?;
1663 if !self.filter || id.into().enabled_for_all_content() {
1664 return Some(id);
1665 }
1666 }
1667 }
1668}
1669
1670pub struct TransitionPropertyIterator<'a> {
1672 style: &'a ComputedValues,
1673 index_range: core::ops::Range<usize>,
1674 longhand_iterator: Option<NonCustomPropertyIterator<LonghandId>>,
1675}
1676
1677impl<'a> TransitionPropertyIterator<'a> {
1678 pub fn from_style(style: &'a ComputedValues) -> Self {
1680 Self {
1681 style,
1682 index_range: 0..style.get_ui().transition_property_count(),
1683 longhand_iterator: None,
1684 }
1685 }
1686}
1687
1688pub struct TransitionPropertyIteration {
1690 pub property: OwnedPropertyDeclarationId,
1692 pub index: usize,
1695}
1696
1697impl<'a> Iterator for TransitionPropertyIterator<'a> {
1698 type Item = TransitionPropertyIteration;
1699
1700 fn next(&mut self) -> Option<Self::Item> {
1701 use crate::values::computed::TransitionProperty;
1702 loop {
1703 if let Some(ref mut longhand_iterator) = self.longhand_iterator {
1704 if let Some(longhand_id) = longhand_iterator.next() {
1705 return Some(TransitionPropertyIteration {
1706 property: OwnedPropertyDeclarationId::Longhand(longhand_id),
1707 index: self.index_range.start - 1,
1708 });
1709 }
1710 self.longhand_iterator = None;
1711 }
1712
1713 let index = self.index_range.next()?;
1714 match self.style.get_ui().transition_property_at(index) {
1715 TransitionProperty::NonCustom(id) => {
1716 match id.longhand_or_shorthand() {
1717 Ok(longhand_id) => {
1718 return Some(TransitionPropertyIteration {
1719 property: OwnedPropertyDeclarationId::Longhand(longhand_id),
1720 index,
1721 });
1722 },
1723 Err(shorthand_id) => {
1724 self.longhand_iterator = Some(shorthand_id.longhands());
1728 },
1729 }
1730 },
1731 TransitionProperty::Custom(name) => {
1732 return Some(TransitionPropertyIteration {
1733 property: OwnedPropertyDeclarationId::Custom(name),
1734 index,
1735 })
1736 },
1737 TransitionProperty::Unsupported(..) => {},
1738 }
1739 }
1740 }
1741}