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
15macro_rules! expanded {
16 ( $( $name: ident: $value: expr ),+ ) => {
17 expanded!( $( $name: $value, )+ )
18 };
19 ( $( $name: ident: $value: expr, )+ ) => {
20 Longhands {
21 $(
22 $name: $crate::properties::MaybeBoxed::maybe_boxed($value),
23 )+
24 }
25 }
26}
27
28pub(crate) use expanded;
29
30#[macro_use]
33#[allow(unsafe_code)]
34#[deny(missing_docs)]
35pub mod generated {
36 include!(concat!(env!("OUT_DIR"), "/properties.rs"));
37}
38
39use crate::custom_properties::{self, ComputedCustomProperties};
40use crate::derives::*;
41use crate::dom::AttributeTracker;
42#[cfg(feature = "gecko")]
43use crate::gecko_bindings::structs::{CSSPropertyId, NonCustomCSSPropertyId, RefPtr};
44use crate::logical_geometry::WritingMode;
45use crate::parser::ParserContext;
46use crate::stylesheets::CssRuleType;
47use crate::stylesheets::Origin;
48use crate::stylist::Stylist;
49use crate::values::{computed, serialize_atom_name};
50use arrayvec::{ArrayVec, Drain as ArrayVecDrain};
51use cssparser::{match_ignore_ascii_case, Parser, ParserInput};
52use rustc_hash::FxHashMap;
53use servo_arc::Arc;
54use std::{
55 borrow::Cow,
56 fmt::{self, Write},
57 mem,
58};
59use style_traits::{
60 CssString, CssWriter, KeywordsCollectFn, ParseError, ParsingMode, SpecifiedValueInfo, ToCss,
61 ToTyped, TypedValue,
62};
63
64bitflags! {
65 #[derive(Clone, Copy)]
67 pub struct PropertyFlags: u16 {
68 const APPLIES_TO_FIRST_LETTER = 1 << 1;
70 const APPLIES_TO_FIRST_LINE = 1 << 2;
72 const APPLIES_TO_PLACEHOLDER = 1 << 3;
74 const APPLIES_TO_CUE = 1 << 4;
76 const APPLIES_TO_MARKER = 1 << 5;
78 const IS_LEGACY_SHORTHAND = 1 << 6;
82
83 const CAN_ANIMATE_ON_COMPOSITOR = 0;
89 const AFFECTS_LAYOUT = 0;
91 #[allow(missing_docs)]
92 const AFFECTS_OVERFLOW = 0;
93 #[allow(missing_docs)]
94 const AFFECTS_PAINT = 0;
95 }
96}
97
98#[derive(
100 Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
101)]
102pub enum CSSWideKeyword {
103 Initial,
105 Inherit,
107 Unset,
109 Revert,
111 RevertLayer,
113}
114
115impl CSSWideKeyword {
116 pub fn to_str(&self) -> &'static str {
118 match *self {
119 CSSWideKeyword::Initial => "initial",
120 CSSWideKeyword::Inherit => "inherit",
121 CSSWideKeyword::Unset => "unset",
122 CSSWideKeyword::Revert => "revert",
123 CSSWideKeyword::RevertLayer => "revert-layer",
124 }
125 }
126}
127
128impl CSSWideKeyword {
129 pub fn from_ident(ident: &str) -> Result<Self, ()> {
131 Ok(match_ignore_ascii_case! { ident,
132 "initial" => CSSWideKeyword::Initial,
133 "inherit" => CSSWideKeyword::Inherit,
134 "unset" => CSSWideKeyword::Unset,
135 "revert" => CSSWideKeyword::Revert,
136 "revert-layer" => CSSWideKeyword::RevertLayer,
137 _ => return Err(()),
138 })
139 }
140
141 pub fn parse(input: &mut Parser) -> Result<Self, ()> {
143 let keyword = {
144 let ident = input.expect_ident().map_err(|_| ())?;
145 Self::from_ident(ident)?
146 };
147 input.expect_exhausted().map_err(|_| ())?;
148 Ok(keyword)
149 }
150}
151
152#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf)]
154pub struct WideKeywordDeclaration {
155 #[css(skip)]
156 id: LonghandId,
157 pub keyword: CSSWideKeyword,
159}
160
161impl ToTyped for WideKeywordDeclaration {
164 fn to_typed(&self) -> Option<TypedValue> {
165 self.keyword.to_typed()
166 }
167}
168
169#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
171pub struct VariableDeclaration {
172 #[css(skip)]
174 id: LonghandId,
175 #[ignore_malloc_size_of = "Arc"]
177 pub value: Arc<UnparsedValue>,
178}
179
180#[derive(Clone, PartialEq, ToCss, ToShmem)]
183pub enum CustomDeclarationValue {
184 Unparsed(Arc<custom_properties::SpecifiedValue>),
186 Parsed(Arc<crate::properties_and_values::value::SpecifiedValue>),
188 CSSWideKeyword(CSSWideKeyword),
190}
191
192#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]
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(_) => cfg!(feature = "gecko"),
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
738fn parse_non_custom_property_declaration_value_into<'i>(
739 declarations: &mut SourcePropertyDeclaration,
740 context: &ParserContext,
741 input: &mut Parser<'i, '_>,
742 start: &cssparser::ParserState,
743 parse_entirely_into: impl FnOnce(
744 &mut SourcePropertyDeclaration,
745 &mut Parser<'i, '_>,
746 ) -> Result<(), ParseError<'i>>,
747 parsed_wide_keyword: impl FnOnce(&mut SourcePropertyDeclaration, CSSWideKeyword),
748 parsed_custom: impl FnOnce(&mut SourcePropertyDeclaration, custom_properties::VariableValue),
749) -> Result<(), ParseError<'i>> {
750 let mut starts_with_curly_block = false;
751 if let Ok(token) = input.next() {
752 match token {
753 cssparser::Token::Ident(ref ident) => match CSSWideKeyword::from_ident(ident) {
754 Ok(wk) => {
755 if input.expect_exhausted().is_ok() {
756 return Ok(parsed_wide_keyword(declarations, wk));
757 }
758 },
759 Err(()) => {},
760 },
761 cssparser::Token::CurlyBracketBlock => {
762 starts_with_curly_block = true;
763 },
764 _ => {},
765 }
766 };
767
768 input.reset(&start);
769 input.look_for_arbitrary_substitution_functions(
770 if static_prefs::pref!("layout.css.attr.enabled") {
771 &["var", "env", "attr"]
772 } else {
773 &["var", "env"]
774 },
775 );
776
777 let err = match parse_entirely_into(declarations, input) {
778 Ok(()) => {
779 input.seen_arbitrary_substitution_functions();
780 return Ok(());
781 },
782 Err(e) => e,
783 };
784
785 let start_pos = start.position();
787 let mut at_start = start_pos == input.position();
788 let mut invalid = false;
789 while let Ok(token) = input.next() {
790 if matches!(token, cssparser::Token::CurlyBracketBlock) {
791 if !starts_with_curly_block || !at_start {
792 invalid = true;
793 break;
794 }
795 } else if starts_with_curly_block {
796 invalid = true;
797 break;
798 }
799 at_start = false;
800 }
801 if !input.seen_arbitrary_substitution_functions() || invalid {
802 return Err(err);
803 }
804 input.reset(start);
805 let value = custom_properties::VariableValue::parse(input, &context.url_data)?;
806 parsed_custom(declarations, value);
807 Ok(())
808}
809
810impl PropertyDeclaration {
811 fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option<&str> {
812 match *self {
813 PropertyDeclaration::WithVariables(ref declaration) => {
814 let s = declaration.value.from_shorthand?;
815 if s != shorthand {
816 return None;
817 }
818 Some(&*declaration.value.variable_value.css)
819 },
820 _ => None,
821 }
822 }
823
824 #[inline]
826 pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
827 Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
828 }
829
830 #[inline]
832 pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
833 match *self {
834 PropertyDeclaration::CSSWideKeyword(ref declaration) => Some(declaration.keyword),
835 _ => None,
836 }
837 }
838
839 pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
852 match *self {
853 PropertyDeclaration::CSSWideKeyword(..) | PropertyDeclaration::WithVariables(..) => {
854 false
855 },
856 PropertyDeclaration::Custom(..) => {
857 unreachable!("Serializing a custom property as part of shorthand?")
858 },
859 _ => true,
860 }
861 }
862
863 pub fn is_animatable(&self) -> bool {
865 self.id().is_animatable()
866 }
867
868 pub fn is_custom(&self) -> bool {
871 matches!(*self, PropertyDeclaration::Custom(..))
872 }
873
874 pub fn parse_into<'i, 't>(
885 declarations: &mut SourcePropertyDeclaration,
886 id: PropertyId,
887 context: &ParserContext,
888 input: &mut Parser<'i, 't>,
889 ) -> Result<(), ParseError<'i>> {
890 assert!(declarations.is_empty());
891 debug_assert!(id.allowed_in(context), "{:?}", id);
892 input.skip_whitespace();
893
894 let start = input.state();
895 let non_custom_id = match id {
896 PropertyId::Custom(property_name) => {
897 let value = match input.try_parse(CSSWideKeyword::parse) {
898 Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
899 Err(()) => CustomDeclarationValue::Unparsed(Arc::new(
900 custom_properties::VariableValue::parse(input, &context.url_data)?,
901 )),
902 };
903 declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
904 name: property_name,
905 value,
906 }));
907 return Ok(());
908 },
909 PropertyId::NonCustom(id) => id,
910 };
911 match non_custom_id.longhand_or_shorthand() {
912 Ok(longhand_id) => {
913 parse_non_custom_property_declaration_value_into(
914 declarations,
915 context,
916 input,
917 &start,
918 |declarations, input| {
919 let decl = input
920 .parse_entirely(|input| longhand_id.parse_value(context, input))?;
921 declarations.push(decl);
922 Ok(())
923 },
924 |declarations, wk| {
925 declarations.push(PropertyDeclaration::css_wide_keyword(longhand_id, wk));
926 },
927 |declarations, variable_value| {
928 declarations.push(PropertyDeclaration::WithVariables(VariableDeclaration {
929 id: longhand_id,
930 value: Arc::new(UnparsedValue {
931 variable_value,
932 from_shorthand: None,
933 }),
934 }))
935 },
936 )?;
937 },
938 Err(shorthand_id) => {
939 parse_non_custom_property_declaration_value_into(
940 declarations,
941 context,
942 input,
943 &start,
944 |declarations, input| shorthand_id.parse_into(declarations, context, input),
947 |declarations, wk| {
948 if shorthand_id == ShorthandId::All {
949 declarations.all_shorthand = AllShorthand::CSSWideKeyword(wk)
950 } else {
951 for longhand in shorthand_id.longhands() {
952 declarations
953 .push(PropertyDeclaration::css_wide_keyword(longhand, wk));
954 }
955 }
956 },
957 |declarations, variable_value| {
958 let unparsed = Arc::new(UnparsedValue {
959 variable_value,
960 from_shorthand: Some(shorthand_id),
961 });
962 if shorthand_id == ShorthandId::All {
963 declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
964 } else {
965 for id in shorthand_id.longhands() {
966 declarations.push(PropertyDeclaration::WithVariables(
967 VariableDeclaration {
968 id,
969 value: unparsed.clone(),
970 },
971 ))
972 }
973 }
974 },
975 )?;
976 },
977 }
978 if let Some(use_counters) = context.use_counters {
979 use_counters.non_custom_properties.record(non_custom_id);
980 }
981 Ok(())
982 }
983}
984
985#[derive(Clone, Debug, PartialEq, Eq, Hash)]
987pub enum OwnedPropertyDeclarationId {
988 Longhand(LonghandId),
990 Custom(custom_properties::Name),
992}
993
994impl OwnedPropertyDeclarationId {
995 #[inline]
997 pub fn is_logical(&self) -> bool {
998 self.as_borrowed().is_logical()
999 }
1000
1001 #[inline]
1003 pub fn as_borrowed(&self) -> PropertyDeclarationId<'_> {
1004 match self {
1005 Self::Longhand(id) => PropertyDeclarationId::Longhand(*id),
1006 Self::Custom(name) => PropertyDeclarationId::Custom(name),
1007 }
1008 }
1009
1010 #[cfg(feature = "gecko")]
1012 #[inline]
1013 pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {
1014 Some(match PropertyId::from_gecko_css_property_id(property)? {
1015 PropertyId::Custom(name) => Self::Custom(name),
1016 PropertyId::NonCustom(id) => Self::Longhand(id.as_longhand()?),
1017 })
1018 }
1019}
1020
1021#[derive(Clone, Copy, Debug, PartialEq, MallocSizeOf)]
1024pub enum PropertyDeclarationId<'a> {
1025 Longhand(LonghandId),
1027 Custom(&'a custom_properties::Name),
1029}
1030
1031impl<'a> ToCss for PropertyDeclarationId<'a> {
1032 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1033 where
1034 W: Write,
1035 {
1036 match *self {
1037 PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
1038 PropertyDeclarationId::Custom(name) => {
1039 dest.write_str("--")?;
1040 serialize_atom_name(name, dest)
1041 },
1042 }
1043 }
1044}
1045
1046impl<'a> PropertyDeclarationId<'a> {
1047 #[inline(always)]
1049 pub fn flags(&self) -> PropertyFlags {
1050 match self {
1051 Self::Longhand(id) => id.flags(),
1052 Self::Custom(_) => PropertyFlags::empty(),
1053 }
1054 }
1055
1056 pub fn to_owned(&self) -> OwnedPropertyDeclarationId {
1058 match self {
1059 PropertyDeclarationId::Longhand(id) => OwnedPropertyDeclarationId::Longhand(*id),
1060 PropertyDeclarationId::Custom(name) => {
1061 OwnedPropertyDeclarationId::Custom((*name).clone())
1062 },
1063 }
1064 }
1065
1066 pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
1069 match *self {
1070 PropertyDeclarationId::Longhand(id) => match *other {
1071 PropertyId::NonCustom(non_custom_id) => id.is_or_is_longhand_of(non_custom_id),
1072 PropertyId::Custom(_) => false,
1073 },
1074 PropertyDeclarationId::Custom(name) => {
1075 matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
1076 },
1077 }
1078 }
1079
1080 pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
1083 match *self {
1084 PropertyDeclarationId::Longhand(ref id) => id.is_longhand_of(shorthand),
1085 _ => false,
1086 }
1087 }
1088
1089 pub fn name(&self) -> Cow<'static, str> {
1091 match *self {
1092 PropertyDeclarationId::Longhand(id) => id.name().into(),
1093 PropertyDeclarationId::Custom(name) => {
1094 let mut s = String::new();
1095 write!(&mut s, "--{}", name).unwrap();
1096 s.into()
1097 },
1098 }
1099 }
1100
1101 #[inline]
1103 pub fn as_longhand(&self) -> Option<LonghandId> {
1104 match *self {
1105 PropertyDeclarationId::Longhand(id) => Some(id),
1106 _ => None,
1107 }
1108 }
1109
1110 #[inline]
1112 pub fn is_logical(&self) -> bool {
1113 match self {
1114 PropertyDeclarationId::Longhand(id) => id.is_logical(),
1115 PropertyDeclarationId::Custom(_) => false,
1116 }
1117 }
1118
1119 #[inline]
1124 pub fn to_physical(&self, wm: WritingMode) -> Self {
1125 match self {
1126 Self::Longhand(id) => Self::Longhand(id.to_physical(wm)),
1127 Self::Custom(_) => self.clone(),
1128 }
1129 }
1130
1131 #[inline]
1133 pub fn is_animatable(&self) -> bool {
1134 match self {
1135 Self::Longhand(id) => id.is_animatable(),
1136 Self::Custom(_) => cfg!(feature = "gecko"),
1137 }
1138 }
1139
1140 #[inline]
1142 pub fn is_discrete_animatable(&self) -> bool {
1143 match self {
1144 Self::Longhand(longhand) => longhand.is_discrete_animatable(),
1145 Self::Custom(_) => cfg!(feature = "gecko"),
1147 }
1148 }
1149
1150 #[cfg(feature = "gecko")]
1153 #[inline]
1154 pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {
1155 match self {
1156 PropertyDeclarationId::Longhand(id) => id.to_noncustomcsspropertyid(),
1157 PropertyDeclarationId::Custom(_) => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1158 }
1159 }
1160
1161 #[cfg(feature = "gecko")]
1166 #[inline]
1167 pub fn to_gecko_css_property_id(&self) -> CSSPropertyId {
1168 match self {
1169 Self::Longhand(id) => CSSPropertyId {
1170 mId: id.to_noncustomcsspropertyid(),
1171 mCustomName: RefPtr::null(),
1172 },
1173 Self::Custom(name) => {
1174 let mut property_id = CSSPropertyId {
1175 mId: NonCustomCSSPropertyId::eCSSPropertyExtra_variable,
1176 mCustomName: RefPtr::null(),
1177 };
1178 property_id.mCustomName.mRawPtr = (*name).clone().into_addrefed();
1179 property_id
1180 },
1181 }
1182 }
1183}
1184
1185#[derive(Clone, PartialEq, Default)]
1187pub struct NonCustomPropertyIdSet {
1188 storage: [u32; ((property_counts::NON_CUSTOM as usize) - 1 + 32) / 32],
1189}
1190
1191impl NonCustomPropertyIdSet {
1192 pub fn new() -> Self {
1194 Self {
1195 storage: Default::default(),
1196 }
1197 }
1198
1199 #[inline]
1201 pub fn insert(&mut self, id: NonCustomPropertyId) {
1202 let bit = id.0 as usize;
1203 self.storage[bit / 32] |= 1 << (bit % 32);
1204 }
1205
1206 #[inline]
1208 pub fn contains(&self, id: NonCustomPropertyId) -> bool {
1209 let bit = id.0 as usize;
1210 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1211 }
1212}
1213
1214#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
1216pub struct LonghandIdSet {
1217 storage: [u32; ((property_counts::LONGHANDS as usize) - 1 + 32) / 32],
1218}
1219
1220to_shmem::impl_trivial_to_shmem!(LonghandIdSet);
1221
1222impl LonghandIdSet {
1223 #[inline]
1225 pub fn new() -> Self {
1226 Self {
1227 storage: Default::default(),
1228 }
1229 }
1230
1231 pub fn iter(&self) -> LonghandIdSetIterator<'_> {
1233 LonghandIdSetIterator {
1234 chunks: &self.storage,
1235 cur_chunk: 0,
1236 cur_bit: 0,
1237 }
1238 }
1239
1240 pub fn contains_all(&self, other: &Self) -> bool {
1243 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1244 if (*self_cell & *other_cell) != *other_cell {
1245 return false;
1246 }
1247 }
1248 true
1249 }
1250
1251 pub fn contains_any(&self, other: &Self) -> bool {
1253 for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
1254 if (*self_cell & *other_cell) != 0 {
1255 return true;
1256 }
1257 }
1258 false
1259 }
1260
1261 #[inline]
1263 pub fn remove_all(&mut self, other: &Self) {
1264 for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
1265 *self_cell &= !*other_cell;
1266 }
1267 }
1268
1269 #[inline]
1271 pub fn contains(&self, id: LonghandId) -> bool {
1272 let bit = id as usize;
1273 (self.storage[bit / 32] & (1 << (bit % 32))) != 0
1274 }
1275
1276 #[inline]
1278 pub fn contains_any_reset(&self) -> bool {
1279 self.contains_any(Self::reset())
1280 }
1281
1282 #[inline]
1284 pub fn insert(&mut self, id: LonghandId) {
1285 let bit = id as usize;
1286 self.storage[bit / 32] |= 1 << (bit % 32);
1287 }
1288
1289 #[inline]
1291 pub fn remove(&mut self, id: LonghandId) {
1292 let bit = id as usize;
1293 self.storage[bit / 32] &= !(1 << (bit % 32));
1294 }
1295
1296 #[inline]
1298 pub fn clear(&mut self) {
1299 for cell in &mut self.storage {
1300 *cell = 0
1301 }
1302 }
1303
1304 #[inline]
1306 pub fn is_empty(&self) -> bool {
1307 self.storage.iter().all(|c| *c == 0)
1308 }
1309}
1310
1311pub struct LonghandIdSetIterator<'a> {
1313 chunks: &'a [u32],
1314 cur_chunk: u32,
1315 cur_bit: u32, }
1317
1318impl<'a> Iterator for LonghandIdSetIterator<'a> {
1319 type Item = LonghandId;
1320
1321 fn next(&mut self) -> Option<Self::Item> {
1322 loop {
1323 debug_assert!(self.cur_bit < 32);
1324 let cur_chunk = self.cur_chunk;
1325 let cur_bit = self.cur_bit;
1326 let chunk = *self.chunks.get(cur_chunk as usize)?;
1327 let next_bit = (chunk >> cur_bit).trailing_zeros();
1328 if next_bit == 32 {
1329 self.cur_bit = 0;
1331 self.cur_chunk += 1;
1332 continue;
1333 }
1334 debug_assert!(cur_bit + next_bit < 32);
1335 let longhand_id = cur_chunk * 32 + cur_bit + next_bit;
1336 debug_assert!(longhand_id as usize <= property_counts::LONGHANDS);
1337 let id: LonghandId = unsafe { mem::transmute(longhand_id as u16) };
1338 self.cur_bit += next_bit + 1;
1339 if self.cur_bit == 32 {
1340 self.cur_bit = 0;
1341 self.cur_chunk += 1;
1342 }
1343 return Some(id);
1344 }
1345 }
1346}
1347
1348pub type SubpropertiesVec<T> = ArrayVec<T, { property_counts::MAX_SHORTHAND_EXPANDED }>;
1350
1351#[derive(Default)]
1355pub struct SourcePropertyDeclaration {
1356 pub declarations: SubpropertiesVec<PropertyDeclaration>,
1358 pub all_shorthand: AllShorthand,
1360}
1361
1362#[cfg(feature = "gecko")]
1365size_of_test!(SourcePropertyDeclaration, 632);
1366#[cfg(feature = "servo")]
1367size_of_test!(SourcePropertyDeclaration, 568);
1368
1369impl SourcePropertyDeclaration {
1370 #[inline]
1372 pub fn with_one(decl: PropertyDeclaration) -> Self {
1373 let mut result = Self::default();
1374 result.declarations.push(decl);
1375 result
1376 }
1377
1378 pub fn drain(&mut self) -> SourcePropertyDeclarationDrain<'_> {
1380 SourcePropertyDeclarationDrain {
1381 declarations: self.declarations.drain(..),
1382 all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
1383 }
1384 }
1385
1386 pub fn clear(&mut self) {
1388 self.declarations.clear();
1389 self.all_shorthand = AllShorthand::NotSet;
1390 }
1391
1392 pub fn is_empty(&self) -> bool {
1394 self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
1395 }
1396
1397 pub fn push(&mut self, declaration: PropertyDeclaration) {
1399 let _result = self.declarations.try_push(declaration);
1400 debug_assert!(_result.is_ok());
1401 }
1402}
1403
1404pub struct SourcePropertyDeclarationDrain<'a> {
1406 pub declarations:
1408 ArrayVecDrain<'a, PropertyDeclaration, { property_counts::MAX_SHORTHAND_EXPANDED }>,
1409 pub all_shorthand: AllShorthand,
1411}
1412
1413#[derive(Debug, Eq, PartialEq, ToShmem)]
1415pub struct UnparsedValue {
1416 pub(super) variable_value: custom_properties::VariableValue,
1418 from_shorthand: Option<ShorthandId>,
1420}
1421
1422impl ToCss for UnparsedValue {
1423 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1424 where
1425 W: Write,
1426 {
1427 if self.from_shorthand.is_none() {
1429 self.variable_value.to_css(dest)?;
1430 }
1431 Ok(())
1432 }
1433}
1434
1435pub type ShorthandsWithPropertyReferencesCache =
1442 FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
1443
1444impl UnparsedValue {
1445 fn substitute_variables<'cache>(
1446 &self,
1447 longhand_id: LonghandId,
1448 custom_properties: &ComputedCustomProperties,
1449 stylist: &Stylist,
1450 computed_context: &computed::Context,
1451 shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
1452 attribute_tracker: &mut AttributeTracker,
1453 ) -> Cow<'cache, PropertyDeclaration> {
1454 let invalid_at_computed_value_time = || {
1455 let keyword = if longhand_id.inherited() {
1456 CSSWideKeyword::Inherit
1457 } else {
1458 CSSWideKeyword::Initial
1459 };
1460 Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
1461 };
1462
1463 if computed_context
1464 .builder
1465 .invalid_non_custom_properties
1466 .contains(longhand_id)
1467 {
1468 return invalid_at_computed_value_time();
1469 }
1470
1471 if let Some(shorthand_id) = self.from_shorthand {
1472 let key = (shorthand_id, longhand_id);
1473 if shorthand_cache.contains_key(&key) {
1474 return Cow::Borrowed(&shorthand_cache[&key]);
1479 }
1480 }
1481
1482 let css = match custom_properties::substitute(
1483 &self.variable_value,
1484 custom_properties,
1485 stylist,
1486 computed_context,
1487 attribute_tracker,
1488 ) {
1489 Ok(css) => css,
1490 Err(..) => return invalid_at_computed_value_time(),
1491 };
1492
1493 let context = ParserContext::new(
1504 Origin::Author,
1505 &self.variable_value.url_data,
1506 None,
1507 ParsingMode::DEFAULT,
1508 computed_context.quirks_mode,
1509 Default::default(),
1510 None,
1511 None,
1512 );
1513
1514 let mut input = ParserInput::new(&css);
1515 let mut input = Parser::new(&mut input);
1516 input.skip_whitespace();
1517
1518 if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
1519 return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
1520 }
1521
1522 let shorthand = match self.from_shorthand {
1523 None => {
1524 return match input.parse_entirely(|input| longhand_id.parse_value(&context, input))
1525 {
1526 Ok(decl) => Cow::Owned(decl),
1527 Err(..) => invalid_at_computed_value_time(),
1528 }
1529 },
1530 Some(shorthand) => shorthand,
1531 };
1532
1533 let mut decls = SourcePropertyDeclaration::default();
1534 if shorthand
1536 .parse_into(&mut decls, &context, &mut input)
1537 .is_err()
1538 {
1539 return invalid_at_computed_value_time();
1540 }
1541
1542 for declaration in decls.declarations.drain(..) {
1543 let longhand = declaration.id().as_longhand().unwrap();
1544 if longhand.is_logical() {
1545 let writing_mode = computed_context.builder.writing_mode;
1546 shorthand_cache.insert(
1547 (shorthand, longhand.to_physical(writing_mode)),
1548 declaration.clone(),
1549 );
1550 }
1551 shorthand_cache.insert((shorthand, longhand), declaration);
1552 }
1553
1554 let key = (shorthand, longhand_id);
1555 match shorthand_cache.get(&key) {
1556 Some(decl) => Cow::Borrowed(decl),
1557 None => invalid_at_computed_value_time(),
1569 }
1570 }
1571}
1572pub enum AllShorthand {
1574 NotSet,
1576 CSSWideKeyword(CSSWideKeyword),
1578 WithVariables(Arc<UnparsedValue>),
1580}
1581
1582impl Default for AllShorthand {
1583 fn default() -> Self {
1584 Self::NotSet
1585 }
1586}
1587
1588impl AllShorthand {
1589 #[inline]
1591 pub fn declarations(&self) -> AllShorthandDeclarationIterator<'_> {
1592 AllShorthandDeclarationIterator {
1593 all_shorthand: self,
1594 longhands: ShorthandId::All.longhands(),
1595 }
1596 }
1597}
1598
1599pub struct AllShorthandDeclarationIterator<'a> {
1601 all_shorthand: &'a AllShorthand,
1602 longhands: NonCustomPropertyIterator<LonghandId>,
1603}
1604
1605impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
1606 type Item = PropertyDeclaration;
1607
1608 #[inline]
1609 fn next(&mut self) -> Option<Self::Item> {
1610 match *self.all_shorthand {
1611 AllShorthand::NotSet => None,
1612 AllShorthand::CSSWideKeyword(ref keyword) => Some(
1613 PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword),
1614 ),
1615 AllShorthand::WithVariables(ref unparsed) => {
1616 Some(PropertyDeclaration::WithVariables(VariableDeclaration {
1617 id: self.longhands.next()?,
1618 value: unparsed.clone(),
1619 }))
1620 },
1621 }
1622 }
1623}
1624
1625pub struct NonCustomPropertyIterator<Item: 'static> {
1628 filter: bool,
1629 iter: std::slice::Iter<'static, Item>,
1630}
1631
1632impl<Item> Iterator for NonCustomPropertyIterator<Item>
1633where
1634 Item: 'static + Copy + Into<NonCustomPropertyId>,
1635{
1636 type Item = Item;
1637
1638 fn next(&mut self) -> Option<Self::Item> {
1639 loop {
1640 let id = *self.iter.next()?;
1641 if !self.filter || id.into().enabled_for_all_content() {
1642 return Some(id);
1643 }
1644 }
1645 }
1646}