1use crate::color::mix::ColorInterpolationMethod;
10use crate::custom_properties;
11use crate::derives::*;
12use crate::values::generics::NonNegative;
13use crate::values::generics::{color::GenericLightDark, position::PositionComponent, Optional};
14use crate::values::serialize_atom_identifier;
15use crate::{Atom, Zero};
16use servo_arc::Arc;
17use std::fmt::{self, Write};
18use style_traits::{CssWriter, ToCss};
19#[derive(Clone, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToResolvedValue, ToShmem, ToTyped)]
23#[repr(C, u8)]
24#[typed(todo_derive_fields)]
25pub enum GenericImage<G, ImageUrl, Color, Percentage, Resolution> {
26 None,
28
29 Url(ImageUrl),
31
32 Gradient(Box<G>),
35
36 #[cfg(feature = "gecko")]
38 #[css(function = "-moz-element")]
39 Element(Atom),
40
41 #[cfg(feature = "gecko")]
45 #[css(function, skip)]
46 MozSymbolicIcon(Atom),
47
48 #[cfg(feature = "servo")]
51 PaintWorklet(Box<PaintWorklet>),
52
53 CrossFade(Box<GenericCrossFade<Self, Color, Percentage>>),
58
59 ImageSet(Box<GenericImageSet<Self, Resolution>>),
61
62 LightDark(#[css(skip)] Box<GenericLightDark<Self>>),
66}
67
68pub use self::GenericImage as Image;
69
70#[derive(
72 Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem, ToCss, ToComputedValue,
73)]
74#[css(comma, function = "cross-fade")]
75#[repr(C)]
76pub struct GenericCrossFade<Image, Color, Percentage> {
77 #[css(iterable)]
80 pub elements: crate::OwnedSlice<GenericCrossFadeElement<Image, Color, Percentage>>,
81}
82
83#[derive(
85 Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
86)]
87#[repr(C)]
88pub struct GenericCrossFadeElement<Image, Color, Percentage> {
89 pub percent: Optional<Percentage>,
91 pub image: GenericCrossFadeImage<Image, Color>,
94}
95
96#[derive(
99 Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
100)]
101#[repr(C, u8)]
102pub enum GenericCrossFadeImage<I, C> {
103 Image(I),
106 Color(C),
108}
109
110pub use self::GenericCrossFade as CrossFade;
111pub use self::GenericCrossFadeElement as CrossFadeElement;
112pub use self::GenericCrossFadeImage as CrossFadeImage;
113
114#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]
116#[css(comma, function = "image-set")]
117#[repr(C)]
118pub struct GenericImageSet<Image, Resolution> {
119 #[css(skip)]
121 pub selected_index: usize,
122
123 #[css(iterable)]
125 pub items: crate::OwnedSlice<GenericImageSetItem<Image, Resolution>>,
126}
127
128#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
130#[repr(C)]
131pub struct GenericImageSetItem<Image, Resolution> {
132 pub image: Image,
134 pub resolution: Resolution,
138
139 pub mime_type: crate::OwnedStr,
142
143 pub has_mime_type: bool,
145}
146
147impl<I: style_traits::ToCss, R: style_traits::ToCss> ToCss for GenericImageSetItem<I, R> {
148 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
149 where
150 W: fmt::Write,
151 {
152 self.image.to_css(dest)?;
153 dest.write_char(' ')?;
154 self.resolution.to_css(dest)?;
155
156 if self.has_mime_type {
157 dest.write_char(' ')?;
158 dest.write_str("type(")?;
159 self.mime_type.to_css(dest)?;
160 dest.write_char(')')?;
161 }
162 Ok(())
163 }
164}
165
166pub use self::GenericImageSet as ImageSet;
167pub use self::GenericImageSetItem as ImageSetItem;
168
169#[derive(
171 Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
172)]
173#[repr(C)]
174pub struct GradientFlags(u8);
175bitflags! {
176 impl GradientFlags: u8 {
177 const REPEATING = 1 << 0;
179 const HAS_DEFAULT_COLOR_INTERPOLATION_METHOD = 1 << 1;
181 }
182}
183
184#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
187#[repr(C)]
188pub enum GenericGradient<
189 LineDirection,
190 Length,
191 LengthPercentage,
192 Position,
193 Angle,
194 AngleOrPercentage,
195 Color,
196> {
197 Linear {
199 direction: LineDirection,
201 color_interpolation_method: ColorInterpolationMethod,
203 items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
205 flags: GradientFlags,
207 compat_mode: GradientCompatMode,
209 },
210 Radial {
212 shape: GenericEndingShape<NonNegative<Length>, NonNegative<LengthPercentage>>,
214 position: Position,
216 color_interpolation_method: ColorInterpolationMethod,
218 items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
220 flags: GradientFlags,
222 compat_mode: GradientCompatMode,
224 },
225 Conic {
227 angle: Angle,
229 position: Position,
231 color_interpolation_method: ColorInterpolationMethod,
233 items: crate::OwnedSlice<GenericGradientItem<Color, AngleOrPercentage>>,
235 flags: GradientFlags,
237 },
238}
239
240pub use self::GenericGradient as Gradient;
241
242#[derive(
243 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
244)]
245#[repr(u8)]
246pub enum GradientCompatMode {
248 Modern,
250 WebKit,
252 Moz,
254}
255
256#[derive(
258 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
259)]
260#[repr(C, u8)]
261pub enum GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage> {
262 Circle(GenericCircle<NonNegativeLength>),
264 Ellipse(GenericEllipse<NonNegativeLengthPercentage>),
266}
267
268pub use self::GenericEndingShape as EndingShape;
269
270#[derive(
272 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
273)]
274#[repr(C, u8)]
275pub enum GenericCircle<NonNegativeLength> {
276 Radius(NonNegativeLength),
278 Extent(ShapeExtent),
280}
281
282pub use self::GenericCircle as Circle;
283
284#[derive(
286 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
287)]
288#[repr(C, u8)]
289pub enum GenericEllipse<NonNegativeLengthPercentage> {
290 Radii(NonNegativeLengthPercentage, NonNegativeLengthPercentage),
292 Extent(ShapeExtent),
294}
295
296pub use self::GenericEllipse as Ellipse;
297
298#[allow(missing_docs)]
300#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
301#[derive(
302 Clone,
303 Copy,
304 Debug,
305 Eq,
306 MallocSizeOf,
307 Parse,
308 PartialEq,
309 ToComputedValue,
310 ToCss,
311 ToResolvedValue,
312 ToShmem,
313)]
314#[repr(u8)]
315pub enum ShapeExtent {
316 ClosestSide,
317 FarthestSide,
318 ClosestCorner,
319 FarthestCorner,
320 Contain,
321 Cover,
322}
323
324#[derive(
327 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
328)]
329#[repr(C, u8)]
330pub enum GenericGradientItem<Color, T> {
331 SimpleColorStop(Color),
333 ComplexColorStop {
335 color: Color,
337 position: T,
339 },
340 InterpolationHint(T),
342}
343
344pub use self::GenericGradientItem as GradientItem;
345
346#[derive(
349 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
350)]
351pub struct ColorStop<Color, T> {
352 pub color: Color,
354 pub position: Option<T>,
356}
357
358impl<Color, T> ColorStop<Color, T> {
359 #[inline]
361 pub fn into_item(self) -> GradientItem<Color, T> {
362 match self.position {
363 Some(position) => GradientItem::ComplexColorStop {
364 color: self.color,
365 position,
366 },
367 None => GradientItem::SimpleColorStop(self.color),
368 }
369 }
370}
371
372#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
375#[derive(Clone, Debug, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
376pub struct PaintWorklet {
377 pub name: Atom,
379 #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
382 #[compute(no_field_bound)]
383 #[resolve(no_field_bound)]
384 pub arguments: Vec<Arc<custom_properties::SpecifiedValue>>,
385}
386
387impl ::style_traits::SpecifiedValueInfo for PaintWorklet {}
388
389impl ToCss for PaintWorklet {
390 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
391 where
392 W: Write,
393 {
394 dest.write_str("paint(")?;
395 serialize_atom_identifier(&self.name, dest)?;
396 for argument in &self.arguments {
397 dest.write_str(", ")?;
398 argument.to_css(dest)?;
399 }
400 dest.write_char(')')
401 }
402}
403
404impl<G, U, C, P, Resolution> fmt::Debug for Image<G, U, C, P, Resolution>
405where
406 Image<G, U, C, P, Resolution>: ToCss,
407{
408 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
409 self.to_css(&mut CssWriter::new(f))
410 }
411}
412
413impl<G, U, C, P, Resolution> ToCss for Image<G, U, C, P, Resolution>
414where
415 G: ToCss,
416 U: ToCss,
417 C: ToCss,
418 P: ToCss,
419 Resolution: ToCss,
420{
421 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
422 where
423 W: Write,
424 {
425 match *self {
426 Image::None => dest.write_str("none"),
427 Image::Url(ref url) => url.to_css(dest),
428 Image::Gradient(ref gradient) => gradient.to_css(dest),
429 #[cfg(feature = "servo")]
430 Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),
431 #[cfg(feature = "gecko")]
432 Image::Element(ref selector) => {
433 dest.write_str("-moz-element(#")?;
434 serialize_atom_identifier(selector, dest)?;
435 dest.write_char(')')
436 },
437 #[cfg(feature = "gecko")]
438 Image::MozSymbolicIcon(ref id) => {
439 dest.write_str("-moz-symbolic-icon(")?;
440 serialize_atom_identifier(id, dest)?;
441 dest.write_char(')')
442 },
443 Image::ImageSet(ref is) => is.to_css(dest),
444 Image::CrossFade(ref cf) => cf.to_css(dest),
445 Image::LightDark(ref ld) => ld.to_css(dest),
446 }
447 }
448}
449
450impl<D, L, LP, P, A: Zero, AoP, C> ToCss for Gradient<D, L, LP, P, A, AoP, C>
451where
452 D: LineDirection,
453 L: ToCss,
454 LP: 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}