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