1use crate::color::mix::ColorInterpolationMethod;
10use crate::custom_properties;
11use crate::values::generics::{color::GenericLightDark, position::PositionComponent, Optional};
12use crate::values::serialize_atom_identifier;
13use crate::Atom;
14use crate::Zero;
15use servo_arc::Arc;
16use std::fmt::{self, Write};
17use style_traits::{CssWriter, ToCss};
18#[derive(Clone, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToResolvedValue, ToShmem)]
22#[repr(C, u8)]
23pub enum GenericImage<G, ImageUrl, Color, Percentage, Resolution> {
24 None,
26
27 Url(ImageUrl),
29
30 Gradient(Box<G>),
33
34 #[cfg(feature = "gecko")]
36 #[css(function = "-moz-element")]
37 Element(Atom),
38
39 #[cfg(feature = "gecko")]
43 #[css(function, skip)]
44 MozSymbolicIcon(Atom),
45
46 #[cfg(feature = "servo")]
49 PaintWorklet(Box<PaintWorklet>),
50
51 CrossFade(Box<GenericCrossFade<Self, Color, Percentage>>),
56
57 ImageSet(Box<GenericImageSet<Self, Resolution>>),
59
60 LightDark(#[css(skip)] Box<GenericLightDark<Self>>),
64}
65
66pub use self::GenericImage as Image;
67
68#[derive(
70 Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem, ToCss, ToComputedValue,
71)]
72#[css(comma, function = "cross-fade")]
73#[repr(C)]
74pub struct GenericCrossFade<Image, Color, Percentage> {
75 #[css(iterable)]
78 pub elements: crate::OwnedSlice<GenericCrossFadeElement<Image, Color, Percentage>>,
79}
80
81#[derive(
83 Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
84)]
85#[repr(C)]
86pub struct GenericCrossFadeElement<Image, Color, Percentage> {
87 pub percent: Optional<Percentage>,
89 pub image: GenericCrossFadeImage<Image, Color>,
92}
93
94#[derive(
97 Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
98)]
99#[repr(C, u8)]
100pub enum GenericCrossFadeImage<I, C> {
101 Image(I),
104 Color(C),
106}
107
108pub use self::GenericCrossFade as CrossFade;
109pub use self::GenericCrossFadeElement as CrossFadeElement;
110pub use self::GenericCrossFadeImage as CrossFadeImage;
111
112#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]
114#[css(comma, function = "image-set")]
115#[repr(C)]
116pub struct GenericImageSet<Image, Resolution> {
117 #[css(skip)]
119 pub selected_index: usize,
120
121 #[css(iterable)]
123 pub items: crate::OwnedSlice<GenericImageSetItem<Image, Resolution>>,
124}
125
126#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
128#[repr(C)]
129pub struct GenericImageSetItem<Image, Resolution> {
130 pub image: Image,
132 pub resolution: Resolution,
136
137 pub mime_type: crate::OwnedStr,
140
141 pub has_mime_type: bool,
143}
144
145impl<I: style_traits::ToCss, R: style_traits::ToCss> ToCss for GenericImageSetItem<I, R> {
146 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
147 where
148 W: fmt::Write,
149 {
150 self.image.to_css(dest)?;
151 dest.write_char(' ')?;
152 self.resolution.to_css(dest)?;
153
154 if self.has_mime_type {
155 dest.write_char(' ')?;
156 dest.write_str("type(")?;
157 self.mime_type.to_css(dest)?;
158 dest.write_char(')')?;
159 }
160 Ok(())
161 }
162}
163
164pub use self::GenericImageSet as ImageSet;
165pub use self::GenericImageSetItem as ImageSetItem;
166
167#[derive(
169 Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
170)]
171#[repr(C)]
172pub struct GradientFlags(u8);
173bitflags! {
174 impl GradientFlags: u8 {
175 const REPEATING = 1 << 0;
177 const HAS_DEFAULT_COLOR_INTERPOLATION_METHOD = 1 << 1;
179 }
180}
181
182#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
185#[repr(C)]
186pub enum GenericGradient<
187 LineDirection,
188 LengthPercentage,
189 NonNegativeLength,
190 NonNegativeLengthPercentage,
191 Position,
192 Angle,
193 AngleOrPercentage,
194 Color,
195> {
196 Linear {
198 direction: LineDirection,
200 color_interpolation_method: ColorInterpolationMethod,
202 items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
204 flags: GradientFlags,
206 compat_mode: GradientCompatMode,
208 },
209 Radial {
211 shape: GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>,
213 position: Position,
215 color_interpolation_method: ColorInterpolationMethod,
217 items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
219 flags: GradientFlags,
221 compat_mode: GradientCompatMode,
223 },
224 Conic {
226 angle: Angle,
228 position: Position,
230 color_interpolation_method: ColorInterpolationMethod,
232 items: crate::OwnedSlice<GenericGradientItem<Color, AngleOrPercentage>>,
234 flags: GradientFlags,
236 },
237}
238
239pub use self::GenericGradient as Gradient;
240
241#[derive(
242 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
243)]
244#[repr(u8)]
245pub enum GradientCompatMode {
247 Modern,
249 WebKit,
251 Moz,
253}
254
255#[derive(
257 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
258)]
259#[repr(C, u8)]
260pub enum GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage> {
261 Circle(GenericCircle<NonNegativeLength>),
263 Ellipse(GenericEllipse<NonNegativeLengthPercentage>),
265}
266
267pub use self::GenericEndingShape as EndingShape;
268
269#[derive(
271 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
272)]
273#[repr(C, u8)]
274pub enum GenericCircle<NonNegativeLength> {
275 Radius(NonNegativeLength),
277 Extent(ShapeExtent),
279}
280
281pub use self::GenericCircle as Circle;
282
283#[derive(
285 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
286)]
287#[repr(C, u8)]
288pub enum GenericEllipse<NonNegativeLengthPercentage> {
289 Radii(NonNegativeLengthPercentage, NonNegativeLengthPercentage),
291 Extent(ShapeExtent),
293}
294
295pub use self::GenericEllipse as Ellipse;
296
297#[allow(missing_docs)]
299#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
300#[derive(
301 Clone,
302 Copy,
303 Debug,
304 Eq,
305 MallocSizeOf,
306 Parse,
307 PartialEq,
308 ToComputedValue,
309 ToCss,
310 ToResolvedValue,
311 ToShmem,
312)]
313#[repr(u8)]
314pub enum ShapeExtent {
315 ClosestSide,
316 FarthestSide,
317 ClosestCorner,
318 FarthestCorner,
319 Contain,
320 Cover,
321}
322
323#[derive(
326 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
327)]
328#[repr(C, u8)]
329pub enum GenericGradientItem<Color, T> {
330 SimpleColorStop(Color),
332 ComplexColorStop {
334 color: Color,
336 position: T,
338 },
339 InterpolationHint(T),
341}
342
343pub use self::GenericGradientItem as GradientItem;
344
345#[derive(
348 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
349)]
350pub struct ColorStop<Color, T> {
351 pub color: Color,
353 pub position: Option<T>,
355}
356
357impl<Color, T> ColorStop<Color, T> {
358 #[inline]
360 pub fn into_item(self) -> GradientItem<Color, T> {
361 match self.position {
362 Some(position) => GradientItem::ComplexColorStop {
363 color: self.color,
364 position,
365 },
366 None => GradientItem::SimpleColorStop(self.color),
367 }
368 }
369}
370
371#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
374#[derive(Clone, Debug, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
375pub struct PaintWorklet {
376 pub name: Atom,
378 #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
381 #[compute(no_field_bound)]
382 #[resolve(no_field_bound)]
383 pub arguments: Vec<Arc<custom_properties::SpecifiedValue>>,
384}
385
386impl ::style_traits::SpecifiedValueInfo for PaintWorklet {}
387
388impl ToCss for PaintWorklet {
389 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
390 where
391 W: Write,
392 {
393 dest.write_str("paint(")?;
394 serialize_atom_identifier(&self.name, dest)?;
395 for argument in &self.arguments {
396 dest.write_str(", ")?;
397 argument.to_css(dest)?;
398 }
399 dest.write_char(')')
400 }
401}
402
403impl<G, U, C, P, Resolution> fmt::Debug for Image<G, U, C, P, Resolution>
404where
405 Image<G, U, C, P, Resolution>: ToCss,
406{
407 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
408 self.to_css(&mut CssWriter::new(f))
409 }
410}
411
412impl<G, U, C, P, Resolution> ToCss for Image<G, U, C, P, Resolution>
413where
414 G: ToCss,
415 U: ToCss,
416 C: ToCss,
417 P: ToCss,
418 Resolution: ToCss,
419{
420 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
421 where
422 W: Write,
423 {
424 match *self {
425 Image::None => dest.write_str("none"),
426 Image::Url(ref url) => url.to_css(dest),
427 Image::Gradient(ref gradient) => gradient.to_css(dest),
428 #[cfg(feature = "servo")]
429 Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),
430 #[cfg(feature = "gecko")]
431 Image::Element(ref selector) => {
432 dest.write_str("-moz-element(#")?;
433 serialize_atom_identifier(selector, dest)?;
434 dest.write_char(')')
435 },
436 #[cfg(feature = "gecko")]
437 Image::MozSymbolicIcon(ref id) => {
438 dest.write_str("-moz-symbolic-icon(")?;
439 serialize_atom_identifier(id, dest)?;
440 dest.write_char(')')
441 },
442 Image::ImageSet(ref is) => is.to_css(dest),
443 Image::CrossFade(ref cf) => cf.to_css(dest),
444 Image::LightDark(ref ld) => ld.to_css(dest),
445 }
446 }
447}
448
449impl<D, LP, NL, NLP, P, A: Zero, AoP, C> ToCss for Gradient<D, LP, NL, NLP, P, A, AoP, C>
450where
451 D: LineDirection,
452 LP: ToCss,
453 NL: ToCss,
454 NLP: ToCss,
455 P: PositionComponent + ToCss,
456 A: ToCss,
457 AoP: ToCss,
458 C: ToCss,
459{
460 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
461 where
462 W: Write,
463 {
464 let (compat_mode, repeating, has_default_color_interpolation_method) = match *self {
465 Gradient::Linear {
466 compat_mode, flags, ..
467 }
468 | Gradient::Radial {
469 compat_mode, flags, ..
470 } => (
471 compat_mode,
472 flags.contains(GradientFlags::REPEATING),
473 flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),
474 ),
475 Gradient::Conic { flags, .. } => (
476 GradientCompatMode::Modern,
477 flags.contains(GradientFlags::REPEATING),
478 flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),
479 ),
480 };
481
482 match compat_mode {
483 GradientCompatMode::WebKit => dest.write_str("-webkit-")?,
484 GradientCompatMode::Moz => dest.write_str("-moz-")?,
485 _ => {},
486 }
487
488 if repeating {
489 dest.write_str("repeating-")?;
490 }
491
492 match *self {
493 Gradient::Linear {
494 ref direction,
495 ref color_interpolation_method,
496 ref items,
497 compat_mode,
498 ..
499 } => {
500 dest.write_str("linear-gradient(")?;
501 let mut skip_comma = true;
502 if !direction.points_downwards(compat_mode) {
503 direction.to_css(dest, compat_mode)?;
504 skip_comma = false;
505 }
506 if !has_default_color_interpolation_method {
507 if !skip_comma {
508 dest.write_char(' ')?;
509 }
510 color_interpolation_method.to_css(dest)?;
511 skip_comma = false;
512 }
513 for item in &**items {
514 if !skip_comma {
515 dest.write_str(", ")?;
516 }
517 skip_comma = false;
518 item.to_css(dest)?;
519 }
520 },
521 Gradient::Radial {
522 ref shape,
523 ref position,
524 ref color_interpolation_method,
525 ref items,
526 compat_mode,
527 ..
528 } => {
529 dest.write_str("radial-gradient(")?;
530 let omit_shape = match *shape {
531 EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover))
532 | EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => true,
533 _ => false,
534 };
535 let omit_position = position.is_center();
536 if compat_mode == GradientCompatMode::Modern {
537 if !omit_shape {
538 shape.to_css(dest)?;
539 if !omit_position {
540 dest.write_char(' ')?;
541 }
542 }
543 if !omit_position {
544 dest.write_str("at ")?;
545 position.to_css(dest)?;
546 }
547 } else {
548 if !omit_position {
549 position.to_css(dest)?;
550 if !omit_shape {
551 dest.write_str(", ")?;
552 }
553 }
554 if !omit_shape {
555 shape.to_css(dest)?;
556 }
557 }
558 if !has_default_color_interpolation_method {
559 if !omit_shape || !omit_position {
560 dest.write_char(' ')?;
561 }
562 color_interpolation_method.to_css(dest)?;
563 }
564
565 let mut skip_comma =
566 omit_shape && omit_position && has_default_color_interpolation_method;
567 for item in &**items {
568 if !skip_comma {
569 dest.write_str(", ")?;
570 }
571 skip_comma = false;
572 item.to_css(dest)?;
573 }
574 },
575 Gradient::Conic {
576 ref angle,
577 ref position,
578 ref color_interpolation_method,
579 ref items,
580 ..
581 } => {
582 dest.write_str("conic-gradient(")?;
583 let omit_angle = angle.is_zero();
584 let omit_position = position.is_center();
585 if !omit_angle {
586 dest.write_str("from ")?;
587 angle.to_css(dest)?;
588 if !omit_position {
589 dest.write_char(' ')?;
590 }
591 }
592 if !omit_position {
593 dest.write_str("at ")?;
594 position.to_css(dest)?;
595 }
596 if !has_default_color_interpolation_method {
597 if !omit_angle || !omit_position {
598 dest.write_char(' ')?;
599 }
600 color_interpolation_method.to_css(dest)?;
601 }
602 let mut skip_comma =
603 omit_angle && omit_position && has_default_color_interpolation_method;
604 for item in &**items {
605 if !skip_comma {
606 dest.write_str(", ")?;
607 }
608 skip_comma = false;
609 item.to_css(dest)?;
610 }
611 },
612 }
613 dest.write_char(')')
614 }
615}
616
617pub trait LineDirection {
619 fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool;
621
622 fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result
624 where
625 W: Write;
626}
627
628impl<L> ToCss for Circle<L>
629where
630 L: ToCss,
631{
632 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
633 where
634 W: Write,
635 {
636 match *self {
637 Circle::Extent(ShapeExtent::FarthestCorner) | Circle::Extent(ShapeExtent::Cover) => {
638 dest.write_str("circle")
639 },
640 Circle::Extent(keyword) => {
641 dest.write_str("circle ")?;
642 keyword.to_css(dest)
643 },
644 Circle::Radius(ref length) => length.to_css(dest),
645 }
646 }
647}