1#![deny(missing_docs)]
10
11use crate::derives::*;
12use crate::parser::{Parse, ParserContext};
13use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
14use crate::values::generics::position::IsTreeScoped;
15use crate::Atom;
16pub use cssparser::{serialize_identifier, serialize_name, CowRcStr, Parser};
17pub use cssparser::{SourceLocation, Token};
18use precomputed_hash::PrecomputedHash;
19use selectors::parser::SelectorParseErrorKind;
20use std::fmt::{self, Debug, Write};
21use style_traits::{
22 CssString, CssWriter, KeywordValue, MathSum, NumericValue, ParseError, StyleParseErrorKind,
23 ToCss, ToTyped, TypedValue, UnitValue,
24};
25use thin_vec::ThinVec;
26use to_shmem::impl_trivial_to_shmem;
27
28#[cfg(feature = "gecko")]
29pub use crate::gecko::url::CssUrl;
30#[cfg(feature = "servo")]
31pub use crate::servo::url::CssUrl;
32
33pub mod animated;
34pub mod computed;
35pub mod distance;
36pub mod generics;
37pub mod resolved;
38pub mod specified;
39
40pub type CSSFloat = f32;
42
43#[inline]
46pub fn normalize(v: CSSFloat) -> CSSFloat {
47 if v.is_nan() {
48 0.0
49 } else {
50 v
51 }
52}
53
54pub type CSSInteger = i32;
56
57#[cfg(feature = "gecko")]
59pub fn serialize_atom_identifier<W>(ident: &Atom, dest: &mut W) -> fmt::Result
60where
61 W: Write,
62{
63 ident.with_str(|s| serialize_identifier(s, dest))
64}
65
66#[cfg(feature = "servo")]
68pub fn serialize_atom_identifier<Static, W>(
69 ident: &::string_cache::Atom<Static>,
70 dest: &mut W,
71) -> fmt::Result
72where
73 Static: string_cache::StaticAtomSet,
74 W: Write,
75{
76 serialize_identifier(&ident, dest)
77}
78
79#[cfg(feature = "gecko")]
81pub fn serialize_atom_name<W>(ident: &Atom, dest: &mut W) -> fmt::Result
82where
83 W: Write,
84{
85 ident.with_str(|s| serialize_name(s, dest))
86}
87
88#[cfg(feature = "servo")]
90pub fn serialize_atom_name<Static, W>(
91 ident: &::string_cache::Atom<Static>,
92 dest: &mut W,
93) -> fmt::Result
94where
95 Static: string_cache::StaticAtomSet,
96 W: Write,
97{
98 serialize_name(&ident, dest)
99}
100
101pub fn serialize_number<W>(v: f32, was_calc: bool, dest: &mut CssWriter<W>) -> fmt::Result
103where
104 W: Write,
105{
106 serialize_specified_dimension(v, "", was_calc, dest)
107}
108
109pub fn reify_number(v: f32, was_calc: bool, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
111 let numeric_value = NumericValue::Unit(UnitValue {
112 value: v,
113 unit: CssString::from("number"),
114 });
115
116 if was_calc {
118 dest.push(TypedValue::Numeric(NumericValue::Sum(MathSum {
119 values: ThinVec::from([numeric_value]),
120 })));
121 } else {
122 dest.push(TypedValue::Numeric(numeric_value));
123 }
124
125 Ok(())
126}
127
128pub fn serialize_specified_dimension<W>(
130 v: f32,
131 unit: &str,
132 was_calc: bool,
133 dest: &mut CssWriter<W>,
134) -> fmt::Result
135where
136 W: Write,
137{
138 if was_calc {
139 dest.write_str("calc(")?;
140 }
141
142 if !v.is_finite() {
143 if v.is_nan() {
149 dest.write_str("NaN")?;
150 } else if v == f32::INFINITY {
151 dest.write_str("infinity")?;
152 } else if v == f32::NEG_INFINITY {
153 dest.write_str("-infinity")?;
154 }
155
156 if !unit.is_empty() {
157 dest.write_str(" * 1")?;
158 }
159 } else {
160 v.to_css(dest)?;
161 }
162
163 dest.write_str(unit)?;
164
165 if was_calc {
166 dest.write_char(')')?;
167 }
168 Ok(())
169}
170
171#[repr(transparent)]
173#[derive(
174 Clone,
175 Debug,
176 Default,
177 Deref,
178 Eq,
179 Hash,
180 MallocSizeOf,
181 PartialEq,
182 SpecifiedValueInfo,
183 ToComputedValue,
184 ToResolvedValue,
185 ToShmem,
186)]
187pub struct AtomString(pub Atom);
188
189#[cfg(feature = "servo")]
190impl AsRef<str> for AtomString {
191 fn as_ref(&self) -> &str {
192 &*self.0
193 }
194}
195
196impl Parse for AtomString {
197 fn parse<'i>(_: &ParserContext, input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
198 Ok(Self(Atom::from(input.expect_string()?.as_ref())))
199 }
200}
201
202impl cssparser::ToCss for AtomString {
203 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
204 where
205 W: Write,
206 {
207 dest.write_char('"')?;
209 #[cfg(feature = "servo")]
210 {
211 cssparser::CssStringWriter::new(dest).write_str(self.as_ref())?;
212 }
213 #[cfg(feature = "gecko")]
214 {
215 self.0
216 .with_str(|s| cssparser::CssStringWriter::new(dest).write_str(s))?;
217 }
218 dest.write_char('"')
219 }
220}
221
222impl style_traits::ToCss for AtomString {
223 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
224 where
225 W: Write,
226 {
227 cssparser::ToCss::to_css(self, dest)
228 }
229}
230
231impl PrecomputedHash for AtomString {
232 #[inline]
233 fn precomputed_hash(&self) -> u32 {
234 self.0.precomputed_hash()
235 }
236}
237
238impl<'a> From<&'a str> for AtomString {
239 #[inline]
240 fn from(string: &str) -> Self {
241 Self(Atom::from(string))
242 }
243}
244
245#[cfg(feature = "servo")]
247#[repr(transparent)]
248#[derive(Deref)]
249pub struct GenericAtomIdent<Set>(pub string_cache::Atom<Set>)
250where
251 Set: string_cache::StaticAtomSet;
252
253#[cfg(feature = "servo")]
255pub type AtomIdent = GenericAtomIdent<stylo_atoms::AtomStaticSet>;
256
257#[cfg(feature = "servo")]
258impl<Set: string_cache::StaticAtomSet> style_traits::SpecifiedValueInfo for GenericAtomIdent<Set> {}
259
260#[cfg(feature = "servo")]
261impl<Set: string_cache::StaticAtomSet> Default for GenericAtomIdent<Set> {
262 fn default() -> Self {
263 Self(string_cache::Atom::default())
264 }
265}
266
267#[cfg(feature = "servo")]
268impl<Set: string_cache::StaticAtomSet> std::fmt::Debug for GenericAtomIdent<Set> {
269 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
270 self.0.fmt(f)
271 }
272}
273
274#[cfg(feature = "servo")]
275impl<Set: string_cache::StaticAtomSet> std::hash::Hash for GenericAtomIdent<Set> {
276 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
277 self.0.hash(state)
278 }
279}
280
281#[cfg(feature = "servo")]
282impl<Set: string_cache::StaticAtomSet> Eq for GenericAtomIdent<Set> {}
283
284#[cfg(feature = "servo")]
285impl<Set: string_cache::StaticAtomSet> PartialEq for GenericAtomIdent<Set> {
286 fn eq(&self, other: &Self) -> bool {
287 self.0 == other.0
288 }
289}
290
291#[cfg(feature = "servo")]
292impl<Set: string_cache::StaticAtomSet> Clone for GenericAtomIdent<Set> {
293 fn clone(&self) -> Self {
294 Self(self.0.clone())
295 }
296}
297
298#[cfg(feature = "servo")]
299impl<Set: string_cache::StaticAtomSet> to_shmem::ToShmem for GenericAtomIdent<Set> {
300 fn to_shmem(&self, builder: &mut to_shmem::SharedMemoryBuilder) -> to_shmem::Result<Self> {
301 use std::mem::ManuallyDrop;
302
303 let atom = self.0.to_shmem(builder)?;
304 Ok(ManuallyDrop::new(Self(ManuallyDrop::into_inner(atom))))
305 }
306}
307
308#[cfg(feature = "servo")]
309impl<Set: string_cache::StaticAtomSet> malloc_size_of::MallocSizeOf for GenericAtomIdent<Set> {
310 fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
311 self.0.size_of(ops)
312 }
313}
314
315#[cfg(feature = "servo")]
316impl<Set: string_cache::StaticAtomSet> cssparser::ToCss for GenericAtomIdent<Set> {
317 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
318 where
319 W: Write,
320 {
321 serialize_atom_identifier(&self.0, dest)
322 }
323}
324
325#[cfg(feature = "servo")]
326impl<Set: string_cache::StaticAtomSet> style_traits::ToCss for GenericAtomIdent<Set> {
327 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
328 where
329 W: Write,
330 {
331 serialize_atom_identifier(&self.0, dest)
332 }
333}
334
335#[cfg(feature = "servo")]
336impl<Set: string_cache::StaticAtomSet> PrecomputedHash for GenericAtomIdent<Set> {
337 #[inline]
338 fn precomputed_hash(&self) -> u32 {
339 self.0.precomputed_hash()
340 }
341}
342
343#[cfg(feature = "servo")]
344impl<'a, Set: string_cache::StaticAtomSet> From<&'a str> for GenericAtomIdent<Set> {
345 #[inline]
346 fn from(string: &str) -> Self {
347 Self(string_cache::Atom::from(string))
348 }
349}
350
351#[cfg(feature = "servo")]
352impl<Set: string_cache::StaticAtomSet> std::borrow::Borrow<string_cache::Atom<Set>>
353 for GenericAtomIdent<Set>
354{
355 #[inline]
356 fn borrow(&self) -> &string_cache::Atom<Set> {
357 &self.0
358 }
359}
360
361#[cfg(feature = "servo")]
362impl<Set: string_cache::StaticAtomSet> GenericAtomIdent<Set> {
363 #[inline]
365 pub fn new(atom: string_cache::Atom<Set>) -> Self {
366 Self(atom)
367 }
368
369 #[inline]
371 pub fn cast<'a>(atom: &'a string_cache::Atom<Set>) -> &'a Self {
372 let ptr = atom as *const _ as *const Self;
373 unsafe { &*ptr }
375 }
376}
377
378#[cfg(feature = "gecko")]
380#[repr(transparent)]
381#[derive(
382 Clone, Debug, Default, Deref, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem,
383)]
384pub struct AtomIdent(pub Atom);
385
386#[cfg(feature = "gecko")]
387impl cssparser::ToCss for AtomIdent {
388 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
389 where
390 W: Write,
391 {
392 serialize_atom_identifier(&self.0, dest)
393 }
394}
395
396#[cfg(feature = "gecko")]
397impl style_traits::ToCss for AtomIdent {
398 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
399 where
400 W: Write,
401 {
402 cssparser::ToCss::to_css(self, dest)
403 }
404}
405
406#[cfg(feature = "gecko")]
407impl PrecomputedHash for AtomIdent {
408 #[inline]
409 fn precomputed_hash(&self) -> u32 {
410 self.0.precomputed_hash()
411 }
412}
413
414#[cfg(feature = "gecko")]
415impl<'a> From<&'a str> for AtomIdent {
416 #[inline]
417 fn from(string: &str) -> Self {
418 Self(Atom::from(string))
419 }
420}
421
422#[cfg(feature = "gecko")]
423impl AtomIdent {
424 #[inline]
426 pub fn new(atom: Atom) -> Self {
427 Self(atom)
428 }
429
430 pub unsafe fn with<F, R>(ptr: *const crate::gecko_bindings::structs::nsAtom, callback: F) -> R
432 where
433 F: FnOnce(&Self) -> R,
434 {
435 Atom::with(ptr, |atom: &Atom| {
436 let atom = atom as *const Atom as *const AtomIdent;
438 callback(&*atom)
439 })
440 }
441
442 #[inline]
444 pub fn cast<'a>(atom: &'a Atom) -> &'a Self {
445 let ptr = atom as *const _ as *const Self;
446 unsafe { &*ptr }
448 }
449}
450
451#[cfg(feature = "gecko")]
452impl std::borrow::Borrow<crate::gecko_string_cache::WeakAtom> for AtomIdent {
453 #[inline]
454 fn borrow(&self) -> &crate::gecko_string_cache::WeakAtom {
455 self.0.borrow()
456 }
457}
458
459pub fn serialize_percentage<W>(value: CSSFloat, dest: &mut CssWriter<W>) -> fmt::Result
461where
462 W: Write,
463{
464 serialize_specified_dimension(value * 100., "%", false, dest)
465}
466
467pub fn serialize_normalized_percentage<W>(value: CSSFloat, dest: &mut CssWriter<W>) -> fmt::Result
469where
470 W: Write,
471{
472 (value * 100.).to_css(dest)?;
473 dest.write_char('%')
474}
475
476pub fn reify_percentage(
478 value: CSSFloat,
479 was_calc: bool,
480 dest: &mut ThinVec<TypedValue>,
481) -> Result<(), ()> {
482 let numeric_value = NumericValue::Unit(UnitValue {
483 value: value * 100.,
484 unit: CssString::from("percent"),
485 });
486
487 if was_calc {
489 dest.push(TypedValue::Numeric(NumericValue::Sum(MathSum {
490 values: ThinVec::from([numeric_value]),
491 })));
492 } else {
493 dest.push(TypedValue::Numeric(numeric_value));
494 }
495
496 Ok(())
497}
498
499#[cfg_attr(feature = "servo", derive(Deserialize, MallocSizeOf, Serialize))]
501#[derive(
502 Clone,
503 Copy,
504 Debug,
505 PartialEq,
506 SpecifiedValueInfo,
507 ToAnimatedValue,
508 ToComputedValue,
509 ToCss,
510 ToResolvedValue,
511)]
512pub enum Impossible {}
513
514impl ComputeSquaredDistance for Impossible {
517 #[inline]
518 fn compute_squared_distance(&self, _other: &Self) -> Result<SquaredDistance, ()> {
519 match *self {}
520 }
521}
522
523impl_trivial_to_shmem!(Impossible);
524
525impl Parse for Impossible {
526 fn parse<'i, 't>(
527 _context: &ParserContext,
528 input: &mut Parser<'i, 't>,
529 ) -> Result<Self, ParseError<'i>> {
530 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
531 }
532}
533
534#[derive(
536 Animate,
537 Clone,
538 ComputeSquaredDistance,
539 Copy,
540 MallocSizeOf,
541 PartialEq,
542 Parse,
543 SpecifiedValueInfo,
544 ToAnimatedValue,
545 ToAnimatedZero,
546 ToComputedValue,
547 ToCss,
548 ToResolvedValue,
549 ToShmem,
550)]
551pub enum Either<A, B> {
552 First(A),
554 Second(B),
556}
557
558impl<A: Debug, B: Debug> Debug for Either<A, B> {
559 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
560 match *self {
561 Either::First(ref v) => v.fmt(f),
562 Either::Second(ref v) => v.fmt(f),
563 }
564 }
565}
566
567#[derive(
569 Clone,
570 Debug,
571 Default,
572 Eq,
573 Hash,
574 MallocSizeOf,
575 PartialEq,
576 SpecifiedValueInfo,
577 ToAnimatedValue,
578 ToComputedValue,
579 ToResolvedValue,
580 ToShmem,
581)]
582#[repr(C)]
583pub struct CustomIdent(pub Atom);
584
585impl CustomIdent {
586 pub fn parse<'i, 't>(
591 input: &mut Parser<'i, 't>,
592 invalid: &[&str],
593 ) -> Result<Self, ParseError<'i>> {
594 let location = input.current_source_location();
595 let ident = input.expect_ident()?;
596 CustomIdent::from_ident(location, ident, invalid)
597 }
598
599 pub fn from_ident<'i>(
601 location: SourceLocation,
602 ident: &CowRcStr<'i>,
603 excluding: &[&str],
604 ) -> Result<Self, ParseError<'i>> {
605 if !Self::is_valid(ident, excluding) {
606 return Err(
607 location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))
608 );
609 }
610 if excluding.iter().any(|s| ident.eq_ignore_ascii_case(s)) {
611 Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
612 } else {
613 Ok(CustomIdent(Atom::from(ident.as_ref())))
614 }
615 }
616
617 fn is_valid(ident: &str, excluding: &[&str]) -> bool {
618 use crate::properties::CSSWideKeyword;
619 if CSSWideKeyword::from_ident(ident).is_ok() || ident.eq_ignore_ascii_case("default") {
624 return false;
625 }
626
627 !excluding.iter().any(|s| ident.eq_ignore_ascii_case(s))
631 }
632}
633
634impl ToCss for CustomIdent {
635 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
636 where
637 W: Write,
638 {
639 serialize_atom_identifier(&self.0, dest)
640 }
641}
642
643impl ToTyped for CustomIdent {
644 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
645 let s = ToCss::to_css_cssstring(self);
647 dest.push(TypedValue::Keyword(KeywordValue(s)));
648 Ok(())
649 }
650}
651
652#[repr(transparent)]
655#[derive(
656 Clone,
657 Debug,
658 Eq,
659 Hash,
660 MallocSizeOf,
661 PartialEq,
662 SpecifiedValueInfo,
663 ToAnimatedValue,
664 ToComputedValue,
665 ToResolvedValue,
666 ToShmem,
667 Serialize,
668 Deserialize,
669)]
670pub struct DashedIdent(pub Atom);
671
672impl DashedIdent {
673 pub fn from_ident<'i>(
675 location: SourceLocation,
676 ident: &CowRcStr<'i>,
677 ) -> Result<Self, ParseError<'i>> {
678 if !ident.starts_with("--") {
679 return Err(
680 location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))
681 );
682 }
683 Ok(Self(Atom::from(ident.as_ref())))
684 }
685
686 pub fn empty() -> Self {
688 Self(atom!(""))
689 }
690
691 pub fn is_empty(&self) -> bool {
693 self.0 == atom!("")
694 }
695
696 pub(crate) fn undashed(&self) -> Atom {
702 assert!(!self.is_empty(), "Can't undash the empty DashedIdent");
703 #[cfg(feature = "gecko")]
704 let name = &self.0.as_slice()[2..];
705 #[cfg(feature = "servo")]
706 let name = &self.0[2..];
707 Atom::from(name)
708 }
709}
710
711impl IsTreeScoped for DashedIdent {
712 fn is_tree_scoped(&self) -> bool {
713 !self.is_empty()
714 }
715}
716
717impl Parse for DashedIdent {
718 fn parse<'i, 't>(
719 _: &ParserContext,
720 input: &mut Parser<'i, 't>,
721 ) -> Result<Self, ParseError<'i>> {
722 let location = input.current_source_location();
723 let ident = input.expect_ident()?;
724 Self::from_ident(location, ident)
725 }
726}
727
728impl ToCss for DashedIdent {
729 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
730 where
731 W: Write,
732 {
733 serialize_atom_identifier(&self.0, dest)
734 }
735}
736
737#[repr(transparent)]
743#[derive(
744 Clone,
745 Debug,
746 Eq,
747 Hash,
748 PartialEq,
749 MallocSizeOf,
750 SpecifiedValueInfo,
751 ToComputedValue,
752 ToResolvedValue,
753 ToShmem,
754)]
755pub struct KeyframesName(Atom);
756
757impl KeyframesName {
758 pub fn from_ident(value: &str) -> Self {
760 Self(Atom::from(value))
761 }
762
763 pub fn none() -> Self {
765 Self(atom!(""))
766 }
767
768 pub fn is_none(&self) -> bool {
770 self.0 == atom!("")
771 }
772
773 #[cfg(feature = "gecko")]
775 pub fn from_atom(atom: Atom) -> Self {
776 Self(atom)
777 }
778
779 pub fn as_atom(&self) -> &Atom {
781 &self.0
782 }
783}
784
785impl Parse for KeyframesName {
786 fn parse<'i, 't>(
787 _: &ParserContext,
788 input: &mut Parser<'i, 't>,
789 ) -> Result<Self, ParseError<'i>> {
790 let location = input.current_source_location();
791 Ok(match *input.next()? {
792 Token::Ident(ref s) => Self(CustomIdent::from_ident(location, s, &["none"])?.0),
793 Token::QuotedString(ref s) if !s.as_ref().is_empty() => Self(Atom::from(s.as_ref())),
795 ref t => return Err(location.new_unexpected_token_error(t.clone())),
796 })
797 }
798}
799
800impl ToCss for KeyframesName {
801 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
802 where
803 W: Write,
804 {
805 if self.is_none() {
806 return dest.write_str("none");
807 }
808
809 fn serialize<W: Write>(string: &str, dest: &mut CssWriter<W>) -> fmt::Result {
810 if CustomIdent::is_valid(string, &["none"]) {
811 serialize_identifier(string, dest)
812 } else {
813 string.to_css(dest)
814 }
815 }
816
817 #[cfg(feature = "gecko")]
818 return self.0.with_str(|s| serialize(s, dest));
819
820 #[cfg(feature = "servo")]
821 return serialize(self.0.as_ref(), dest);
822 }
823}
824
825impl ToTyped for KeyframesName {
826 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
827 let s = ToCss::to_css_cssstring(self);
828 dest.push(TypedValue::Keyword(KeywordValue(s)));
829 Ok(())
830 }
831}