1#![deny(missing_docs)]
10
11use crate::parser::{Parse, ParserContext};
12use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
13use crate::Atom;
14pub use cssparser::{serialize_identifier, serialize_name, CowRcStr, Parser};
15pub use cssparser::{SourceLocation, Token};
16use precomputed_hash::PrecomputedHash;
17use selectors::parser::SelectorParseErrorKind;
18use std::fmt::{self, Debug, Write};
19use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
20use to_shmem::impl_trivial_to_shmem;
21
22#[cfg(feature = "gecko")]
23pub use crate::gecko::url::CssUrl;
24#[cfg(feature = "servo")]
25pub use crate::servo::url::CssUrl;
26
27pub mod animated;
28pub mod computed;
29pub mod distance;
30pub mod generics;
31pub mod resolved;
32pub mod specified;
33
34pub type CSSFloat = f32;
36
37#[inline]
40pub fn normalize(v: CSSFloat) -> CSSFloat {
41 if v.is_nan() {
42 0.0
43 } else {
44 v
45 }
46}
47
48pub type CSSInteger = i32;
50
51#[cfg(feature = "gecko")]
53pub fn serialize_atom_identifier<W>(ident: &Atom, dest: &mut W) -> fmt::Result
54where
55 W: Write,
56{
57 ident.with_str(|s| serialize_identifier(s, dest))
58}
59
60#[cfg(feature = "servo")]
62pub fn serialize_atom_identifier<Static, W>(
63 ident: &::string_cache::Atom<Static>,
64 dest: &mut W,
65) -> fmt::Result
66where
67 Static: string_cache::StaticAtomSet,
68 W: Write,
69{
70 serialize_identifier(&ident, dest)
71}
72
73#[cfg(feature = "gecko")]
75pub fn serialize_atom_name<W>(ident: &Atom, dest: &mut W) -> fmt::Result
76where
77 W: Write,
78{
79 ident.with_str(|s| serialize_name(s, dest))
80}
81
82#[cfg(feature = "servo")]
84pub fn serialize_atom_name<Static, W>(
85 ident: &::string_cache::Atom<Static>,
86 dest: &mut W,
87) -> fmt::Result
88where
89 Static: string_cache::StaticAtomSet,
90 W: Write,
91{
92 serialize_name(&ident, dest)
93}
94
95pub fn serialize_number<W>(v: f32, was_calc: bool, dest: &mut CssWriter<W>) -> fmt::Result
97where
98 W: Write,
99{
100 serialize_specified_dimension(v, "", was_calc, dest)
101}
102
103pub fn serialize_specified_dimension<W>(
105 v: f32,
106 unit: &str,
107 was_calc: bool,
108 dest: &mut CssWriter<W>,
109) -> fmt::Result
110where
111 W: Write,
112{
113 if was_calc {
114 dest.write_str("calc(")?;
115 }
116
117 if !v.is_finite() {
118 if v.is_nan() {
124 dest.write_str("NaN")?;
125 } else if v == f32::INFINITY {
126 dest.write_str("infinity")?;
127 } else if v == f32::NEG_INFINITY {
128 dest.write_str("-infinity")?;
129 }
130
131 if !unit.is_empty() {
132 dest.write_str(" * 1")?;
133 }
134 } else {
135 v.to_css(dest)?;
136 }
137
138 dest.write_str(unit)?;
139
140 if was_calc {
141 dest.write_char(')')?;
142 }
143 Ok(())
144}
145
146#[repr(transparent)]
148#[derive(
149 Clone,
150 Debug,
151 Default,
152 Deref,
153 Eq,
154 Hash,
155 MallocSizeOf,
156 PartialEq,
157 SpecifiedValueInfo,
158 ToComputedValue,
159 ToResolvedValue,
160 ToShmem,
161)]
162pub struct AtomString(pub Atom);
163
164#[cfg(feature = "servo")]
165impl AsRef<str> for AtomString {
166 fn as_ref(&self) -> &str {
167 &*self.0
168 }
169}
170
171impl Parse for AtomString {
172 fn parse<'i>(_: &ParserContext, input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
173 Ok(Self(Atom::from(input.expect_string()?.as_ref())))
174 }
175}
176
177impl cssparser::ToCss for AtomString {
178 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
179 where
180 W: Write,
181 {
182 dest.write_char('"')?;
184 #[cfg(feature = "servo")]
185 {
186 cssparser::CssStringWriter::new(dest).write_str(self.as_ref())?;
187 }
188 #[cfg(feature = "gecko")]
189 {
190 self.0
191 .with_str(|s| cssparser::CssStringWriter::new(dest).write_str(s))?;
192 }
193 dest.write_char('"')
194 }
195}
196
197impl style_traits::ToCss for AtomString {
198 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
199 where
200 W: Write,
201 {
202 cssparser::ToCss::to_css(self, dest)
203 }
204}
205
206impl PrecomputedHash for AtomString {
207 #[inline]
208 fn precomputed_hash(&self) -> u32 {
209 self.0.precomputed_hash()
210 }
211}
212
213impl<'a> From<&'a str> for AtomString {
214 #[inline]
215 fn from(string: &str) -> Self {
216 Self(Atom::from(string))
217 }
218}
219
220#[cfg(feature = "servo")]
222#[repr(transparent)]
223#[derive(Deref)]
224pub struct GenericAtomIdent<Set>(pub string_cache::Atom<Set>)
225where
226 Set: string_cache::StaticAtomSet;
227
228#[cfg(feature = "servo")]
230pub type AtomIdent = GenericAtomIdent<stylo_atoms::AtomStaticSet>;
231
232#[cfg(feature = "servo")]
233impl<Set: string_cache::StaticAtomSet> style_traits::SpecifiedValueInfo for GenericAtomIdent<Set> {}
234
235#[cfg(feature = "servo")]
236impl<Set: string_cache::StaticAtomSet> Default for GenericAtomIdent<Set> {
237 fn default() -> Self {
238 Self(string_cache::Atom::default())
239 }
240}
241
242#[cfg(feature = "servo")]
243impl<Set: string_cache::StaticAtomSet> std::fmt::Debug for GenericAtomIdent<Set> {
244 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
245 self.0.fmt(f)
246 }
247}
248
249#[cfg(feature = "servo")]
250impl<Set: string_cache::StaticAtomSet> std::hash::Hash for GenericAtomIdent<Set> {
251 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
252 self.0.hash(state)
253 }
254}
255
256#[cfg(feature = "servo")]
257impl<Set: string_cache::StaticAtomSet> Eq for GenericAtomIdent<Set> {}
258
259#[cfg(feature = "servo")]
260impl<Set: string_cache::StaticAtomSet> PartialEq for GenericAtomIdent<Set> {
261 fn eq(&self, other: &Self) -> bool {
262 self.0 == other.0
263 }
264}
265
266#[cfg(feature = "servo")]
267impl<Set: string_cache::StaticAtomSet> Clone for GenericAtomIdent<Set> {
268 fn clone(&self) -> Self {
269 Self(self.0.clone())
270 }
271}
272
273#[cfg(feature = "servo")]
274impl<Set: string_cache::StaticAtomSet> to_shmem::ToShmem for GenericAtomIdent<Set> {
275 fn to_shmem(&self, builder: &mut to_shmem::SharedMemoryBuilder) -> to_shmem::Result<Self> {
276 use std::mem::ManuallyDrop;
277
278 let atom = self.0.to_shmem(builder)?;
279 Ok(ManuallyDrop::new(Self(ManuallyDrop::into_inner(atom))))
280 }
281}
282
283#[cfg(feature = "servo")]
284impl<Set: string_cache::StaticAtomSet> malloc_size_of::MallocSizeOf for GenericAtomIdent<Set> {
285 fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
286 self.0.size_of(ops)
287 }
288}
289
290#[cfg(feature = "servo")]
291impl<Set: string_cache::StaticAtomSet> cssparser::ToCss for GenericAtomIdent<Set> {
292 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
293 where
294 W: Write,
295 {
296 serialize_atom_identifier(&self.0, dest)
297 }
298}
299
300#[cfg(feature = "servo")]
301impl<Set: string_cache::StaticAtomSet> style_traits::ToCss for GenericAtomIdent<Set> {
302 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
303 where
304 W: Write,
305 {
306 serialize_atom_identifier(&self.0, dest)
307 }
308}
309
310#[cfg(feature = "servo")]
311impl<Set: string_cache::StaticAtomSet> PrecomputedHash for GenericAtomIdent<Set> {
312 #[inline]
313 fn precomputed_hash(&self) -> u32 {
314 self.0.precomputed_hash()
315 }
316}
317
318#[cfg(feature = "servo")]
319impl<'a, Set: string_cache::StaticAtomSet> From<&'a str> for GenericAtomIdent<Set> {
320 #[inline]
321 fn from(string: &str) -> Self {
322 Self(string_cache::Atom::from(string))
323 }
324}
325
326#[cfg(feature = "servo")]
327impl<Set: string_cache::StaticAtomSet> std::borrow::Borrow<string_cache::Atom<Set>>
328 for GenericAtomIdent<Set>
329{
330 #[inline]
331 fn borrow(&self) -> &string_cache::Atom<Set> {
332 &self.0
333 }
334}
335
336#[cfg(feature = "servo")]
337impl<Set: string_cache::StaticAtomSet> GenericAtomIdent<Set> {
338 #[inline]
340 pub fn new(atom: string_cache::Atom<Set>) -> Self {
341 Self(atom)
342 }
343
344 #[inline]
346 pub fn cast<'a>(atom: &'a string_cache::Atom<Set>) -> &'a Self {
347 let ptr = atom as *const _ as *const Self;
348 unsafe { &*ptr }
350 }
351}
352
353#[cfg(feature = "gecko")]
355#[repr(transparent)]
356#[derive(
357 Clone, Debug, Default, Deref, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem,
358)]
359pub struct AtomIdent(pub Atom);
360
361#[cfg(feature = "gecko")]
362impl cssparser::ToCss for AtomIdent {
363 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
364 where
365 W: Write,
366 {
367 serialize_atom_identifier(&self.0, dest)
368 }
369}
370
371#[cfg(feature = "gecko")]
372impl style_traits::ToCss for AtomIdent {
373 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
374 where
375 W: Write,
376 {
377 cssparser::ToCss::to_css(self, dest)
378 }
379}
380
381#[cfg(feature = "gecko")]
382impl PrecomputedHash for AtomIdent {
383 #[inline]
384 fn precomputed_hash(&self) -> u32 {
385 self.0.precomputed_hash()
386 }
387}
388
389#[cfg(feature = "gecko")]
390impl<'a> From<&'a str> for AtomIdent {
391 #[inline]
392 fn from(string: &str) -> Self {
393 Self(Atom::from(string))
394 }
395}
396
397#[cfg(feature = "gecko")]
398impl AtomIdent {
399 #[inline]
401 pub fn new(atom: Atom) -> Self {
402 Self(atom)
403 }
404
405 pub unsafe fn with<F, R>(ptr: *const crate::gecko_bindings::structs::nsAtom, callback: F) -> R
407 where
408 F: FnOnce(&Self) -> R,
409 {
410 Atom::with(ptr, |atom: &Atom| {
411 let atom = atom as *const Atom as *const AtomIdent;
413 callback(&*atom)
414 })
415 }
416
417 #[inline]
419 pub fn cast<'a>(atom: &'a Atom) -> &'a Self {
420 let ptr = atom as *const _ as *const Self;
421 unsafe { &*ptr }
423 }
424}
425
426#[cfg(feature = "gecko")]
427impl std::borrow::Borrow<crate::gecko_string_cache::WeakAtom> for AtomIdent {
428 #[inline]
429 fn borrow(&self) -> &crate::gecko_string_cache::WeakAtom {
430 self.0.borrow()
431 }
432}
433
434pub fn serialize_percentage<W>(value: CSSFloat, dest: &mut CssWriter<W>) -> fmt::Result
436where
437 W: Write,
438{
439 serialize_specified_dimension(value * 100., "%", false, dest)
440}
441
442pub fn serialize_normalized_percentage<W>(value: CSSFloat, dest: &mut CssWriter<W>) -> fmt::Result
444where
445 W: Write,
446{
447 (value * 100.).to_css(dest)?;
448 dest.write_char('%')
449}
450
451#[cfg_attr(feature = "servo", derive(Deserialize, MallocSizeOf, Serialize))]
453#[derive(
454 Clone,
455 Copy,
456 Debug,
457 PartialEq,
458 SpecifiedValueInfo,
459 ToAnimatedValue,
460 ToComputedValue,
461 ToCss,
462 ToResolvedValue,
463)]
464pub enum Impossible {}
465
466impl ComputeSquaredDistance for Impossible {
469 #[inline]
470 fn compute_squared_distance(&self, _other: &Self) -> Result<SquaredDistance, ()> {
471 match *self {}
472 }
473}
474
475impl_trivial_to_shmem!(Impossible);
476
477impl Parse for Impossible {
478 fn parse<'i, 't>(
479 _context: &ParserContext,
480 input: &mut Parser<'i, 't>,
481 ) -> Result<Self, ParseError<'i>> {
482 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
483 }
484}
485
486#[derive(
488 Animate,
489 Clone,
490 ComputeSquaredDistance,
491 Copy,
492 MallocSizeOf,
493 PartialEq,
494 Parse,
495 SpecifiedValueInfo,
496 ToAnimatedValue,
497 ToAnimatedZero,
498 ToComputedValue,
499 ToCss,
500 ToResolvedValue,
501 ToShmem,
502)]
503pub enum Either<A, B> {
504 First(A),
506 Second(B),
508}
509
510impl<A: Debug, B: Debug> Debug for Either<A, B> {
511 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
512 match *self {
513 Either::First(ref v) => v.fmt(f),
514 Either::Second(ref v) => v.fmt(f),
515 }
516 }
517}
518
519#[derive(
521 Clone,
522 Debug,
523 Default,
524 Eq,
525 Hash,
526 MallocSizeOf,
527 PartialEq,
528 SpecifiedValueInfo,
529 ToAnimatedValue,
530 ToComputedValue,
531 ToResolvedValue,
532 ToShmem,
533)]
534#[repr(C)]
535pub struct CustomIdent(pub Atom);
536
537impl CustomIdent {
538 pub fn parse<'i, 't>(
543 input: &mut Parser<'i, 't>,
544 invalid: &[&str],
545 ) -> Result<Self, ParseError<'i>> {
546 let location = input.current_source_location();
547 let ident = input.expect_ident()?;
548 CustomIdent::from_ident(location, ident, invalid)
549 }
550
551 pub fn from_ident<'i>(
553 location: SourceLocation,
554 ident: &CowRcStr<'i>,
555 excluding: &[&str],
556 ) -> Result<Self, ParseError<'i>> {
557 if !Self::is_valid(ident, excluding) {
558 return Err(
559 location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))
560 );
561 }
562 if excluding.iter().any(|s| ident.eq_ignore_ascii_case(s)) {
563 Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
564 } else {
565 Ok(CustomIdent(Atom::from(ident.as_ref())))
566 }
567 }
568
569 fn is_valid(ident: &str, excluding: &[&str]) -> bool {
570 use crate::properties::CSSWideKeyword;
571 if CSSWideKeyword::from_ident(ident).is_ok() || ident.eq_ignore_ascii_case("default") {
576 return false;
577 }
578
579 !excluding.iter().any(|s| ident.eq_ignore_ascii_case(s))
583 }
584}
585
586impl ToCss for CustomIdent {
587 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
588 where
589 W: Write,
590 {
591 serialize_atom_identifier(&self.0, dest)
592 }
593}
594
595#[repr(transparent)]
598#[derive(
599 Clone,
600 Debug,
601 Eq,
602 Hash,
603 MallocSizeOf,
604 PartialEq,
605 SpecifiedValueInfo,
606 ToAnimatedValue,
607 ToComputedValue,
608 ToResolvedValue,
609 ToShmem,
610 Serialize,
611 Deserialize,
612)]
613pub struct DashedIdent(pub Atom);
614
615impl DashedIdent {
616 pub fn from_ident<'i>(
618 location: SourceLocation,
619 ident: &CowRcStr<'i>,
620 ) -> Result<Self, ParseError<'i>> {
621 if !ident.starts_with("--") {
622 return Err(
623 location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))
624 );
625 }
626 Ok(Self(Atom::from(ident.as_ref())))
627 }
628
629 pub fn empty() -> Self {
631 Self(atom!(""))
632 }
633
634 pub fn is_empty(&self) -> bool {
636 self.0 == atom!("")
637 }
638}
639
640impl Parse for DashedIdent {
641 fn parse<'i, 't>(
642 _: &ParserContext,
643 input: &mut Parser<'i, 't>,
644 ) -> Result<Self, ParseError<'i>> {
645 let location = input.current_source_location();
646 let ident = input.expect_ident()?;
647 Self::from_ident(location, ident)
648 }
649}
650
651impl ToCss for DashedIdent {
652 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
653 where
654 W: Write,
655 {
656 serialize_atom_identifier(&self.0, dest)
657 }
658}
659
660#[repr(transparent)]
666#[derive(
667 Clone,
668 Debug,
669 Eq,
670 Hash,
671 PartialEq,
672 MallocSizeOf,
673 SpecifiedValueInfo,
674 ToComputedValue,
675 ToResolvedValue,
676 ToShmem,
677)]
678pub struct KeyframesName(Atom);
679
680impl KeyframesName {
681 pub fn from_ident(value: &str) -> Self {
683 Self(Atom::from(value))
684 }
685
686 pub fn none() -> Self {
688 Self(atom!(""))
689 }
690
691 pub fn is_none(&self) -> bool {
693 self.0 == atom!("")
694 }
695
696 #[cfg(feature = "gecko")]
698 pub fn from_atom(atom: Atom) -> Self {
699 Self(atom)
700 }
701
702 pub fn as_atom(&self) -> &Atom {
704 &self.0
705 }
706}
707
708impl Parse for KeyframesName {
709 fn parse<'i, 't>(
710 _: &ParserContext,
711 input: &mut Parser<'i, 't>,
712 ) -> Result<Self, ParseError<'i>> {
713 let location = input.current_source_location();
714 Ok(match *input.next()? {
715 Token::Ident(ref s) => Self(CustomIdent::from_ident(location, s, &["none"])?.0),
716 Token::QuotedString(ref s) => Self(Atom::from(s.as_ref())),
717 ref t => return Err(location.new_unexpected_token_error(t.clone())),
718 })
719 }
720}
721
722impl ToCss for KeyframesName {
723 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
724 where
725 W: Write,
726 {
727 if self.is_none() {
728 return dest.write_str("none");
729 }
730
731 fn serialize<W: Write>(string: &str, dest: &mut CssWriter<W>) -> fmt::Result {
732 if CustomIdent::is_valid(string, &["none"]) {
733 serialize_identifier(string, dest)
734 } else {
735 string.to_css(dest)
736 }
737 }
738
739 #[cfg(feature = "gecko")]
740 return self.0.with_str(|s| serialize(s, dest));
741
742 #[cfg(feature = "servo")]
743 return serialize(self.0.as_ref(), dest);
744 }
745}