1use crate::color::mix::ColorInterpolationMethod;
10use crate::custom_properties;
11use crate::values::generics::{position::PositionComponent, color::GenericLightDark, 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(
22 Clone, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToResolvedValue, ToShmem,
23)]
24#[repr(C, u8)]
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 LengthPercentage,
191 NonNegativeLength,
192 NonNegativeLengthPercentage,
193 Position,
194 Angle,
195 AngleOrPercentage,
196 Color,
197> {
198 Linear {
200 direction: LineDirection,
202 color_interpolation_method: ColorInterpolationMethod,
204 items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
206 flags: GradientFlags,
208 compat_mode: GradientCompatMode,
210 },
211 Radial {
213 shape: GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>,
215 position: Position,
217 color_interpolation_method: ColorInterpolationMethod,
219 items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
221 flags: GradientFlags,
223 compat_mode: GradientCompatMode,
225 },
226 Conic {
228 angle: Angle,
230 position: Position,
232 color_interpolation_method: ColorInterpolationMethod,
234 items: crate::OwnedSlice<GenericGradientItem<Color, AngleOrPercentage>>,
236 flags: GradientFlags,
238 },
239}
240
241pub use self::GenericGradient as Gradient;
242
243#[derive(
244 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
245)]
246#[repr(u8)]
247pub enum GradientCompatMode {
249 Modern,
251 WebKit,
253 Moz,
255}
256
257#[derive(
259 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
260)]
261#[repr(C, u8)]
262pub enum GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage> {
263 Circle(GenericCircle<NonNegativeLength>),
265 Ellipse(GenericEllipse<NonNegativeLengthPercentage>),
267}
268
269pub use self::GenericEndingShape as EndingShape;
270
271#[derive(
273 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
274)]
275#[repr(C, u8)]
276pub enum GenericCircle<NonNegativeLength> {
277 Radius(NonNegativeLength),
279 Extent(ShapeExtent),
281}
282
283pub use self::GenericCircle as Circle;
284
285#[derive(
287 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
288)]
289#[repr(C, u8)]
290pub enum GenericEllipse<NonNegativeLengthPercentage> {
291 Radii(NonNegativeLengthPercentage, NonNegativeLengthPercentage),
293 Extent(ShapeExtent),
295}
296
297pub use self::GenericEllipse as Ellipse;
298
299#[allow(missing_docs)]
301#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
302#[derive(
303 Clone,
304 Copy,
305 Debug,
306 Eq,
307 MallocSizeOf,
308 Parse,
309 PartialEq,
310 ToComputedValue,
311 ToCss,
312 ToResolvedValue,
313 ToShmem,
314)]
315#[repr(u8)]
316pub enum ShapeExtent {
317 ClosestSide,
318 FarthestSide,
319 ClosestCorner,
320 FarthestCorner,
321 Contain,
322 Cover,
323}
324
325#[derive(
328 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
329)]
330#[repr(C, u8)]
331pub enum GenericGradientItem<Color, T> {
332 SimpleColorStop(Color),
334 ComplexColorStop {
336 color: Color,
338 position: T,
340 },
341 InterpolationHint(T),
343}
344
345pub use self::GenericGradientItem as GradientItem;
346
347#[derive(
350 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
351)]
352pub struct ColorStop<Color, T> {
353 pub color: Color,
355 pub position: Option<T>,
357}
358
359impl<Color, T> ColorStop<Color, T> {
360 #[inline]
362 pub fn into_item(self) -> GradientItem<Color, T> {
363 match self.position {
364 Some(position) => GradientItem::ComplexColorStop {
365 color: self.color,
366 position,
367 },
368 None => GradientItem::SimpleColorStop(self.color),
369 }
370 }
371}
372
373#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
376#[derive(Clone, Debug, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
377pub struct PaintWorklet {
378 pub name: Atom,
380 #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
383 #[compute(no_field_bound)]
384 #[resolve(no_field_bound)]
385 pub arguments: Vec<Arc<custom_properties::SpecifiedValue>>,
386}
387
388impl ::style_traits::SpecifiedValueInfo for PaintWorklet {}
389
390impl ToCss for PaintWorklet {
391 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
392 where
393 W: Write,
394 {
395 dest.write_str("paint(")?;
396 serialize_atom_identifier(&self.name, dest)?;
397 for argument in &self.arguments {
398 dest.write_str(", ")?;
399 argument.to_css(dest)?;
400 }
401 dest.write_char(')')
402 }
403}
404
405impl<G, U, C, P, Resolution> fmt::Debug for Image<G, U, C, P, Resolution>
406where
407 Image<G, U, C, P, Resolution>: ToCss,
408{
409 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
410 self.to_css(&mut CssWriter::new(f))
411 }
412}
413
414impl<G, U, C, P, Resolution> ToCss for Image<G, U, C, P, Resolution>
415where
416 G: ToCss,
417 U: ToCss,
418 C: ToCss,
419 P: ToCss,
420 Resolution: ToCss,
421{
422 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
423 where
424 W: Write,
425 {
426 match *self {
427 Image::None => dest.write_str("none"),
428 Image::Url(ref url) => url.to_css(dest),
429 Image::Gradient(ref gradient) => gradient.to_css(dest),
430 #[cfg(feature = "servo")]
431 Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),
432 #[cfg(feature = "gecko")]
433 Image::Element(ref selector) => {
434 dest.write_str("-moz-element(#")?;
435 serialize_atom_identifier(selector, dest)?;
436 dest.write_char(')')
437 },
438 #[cfg(feature = "gecko")]
439 Image::MozSymbolicIcon(ref id) => {
440 dest.write_str("-moz-symbolic-icon(")?;
441 serialize_atom_identifier(id, dest)?;
442 dest.write_char(')')
443 },
444 Image::ImageSet(ref is) => is.to_css(dest),
445 Image::CrossFade(ref cf) => cf.to_css(dest),
446 Image::LightDark(ref ld) => ld.to_css(dest),
447 }
448 }
449}
450
451impl<D, LP, NL, NLP, P, A: Zero, AoP, C> ToCss for Gradient<D, LP, NL, NLP, P, A, AoP, C>
452where
453 D: LineDirection,
454 LP: ToCss,
455 NL: ToCss,
456 NLP: ToCss,
457 P: PositionComponent + ToCss,
458 A: ToCss,
459 AoP: ToCss,
460 C: ToCss,
461{
462 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
463 where
464 W: Write,
465 {
466 let (compat_mode, repeating, has_default_color_interpolation_method) = match *self {
467 Gradient::Linear {
468 compat_mode, flags, ..
469 } |
470 Gradient::Radial {
471 compat_mode, flags, ..
472 } => (
473 compat_mode,
474 flags.contains(GradientFlags::REPEATING),
475 flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),
476 ),
477 Gradient::Conic { flags, .. } => (
478 GradientCompatMode::Modern,
479 flags.contains(GradientFlags::REPEATING),
480 flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),
481 ),
482 };
483
484 match compat_mode {
485 GradientCompatMode::WebKit => dest.write_str("-webkit-")?,
486 GradientCompatMode::Moz => dest.write_str("-moz-")?,
487 _ => {},
488 }
489
490 if repeating {
491 dest.write_str("repeating-")?;
492 }
493
494 match *self {
495 Gradient::Linear {
496 ref direction,
497 ref color_interpolation_method,
498 ref items,
499 compat_mode,
500 ..
501 } => {
502 dest.write_str("linear-gradient(")?;
503 let mut skip_comma = true;
504 if !direction.points_downwards(compat_mode) {
505 direction.to_css(dest, compat_mode)?;
506 skip_comma = false;
507 }
508 if !has_default_color_interpolation_method {
509 if !skip_comma {
510 dest.write_char(' ')?;
511 }
512 color_interpolation_method.to_css(dest)?;
513 skip_comma = false;
514 }
515 for item in &**items {
516 if !skip_comma {
517 dest.write_str(", ")?;
518 }
519 skip_comma = false;
520 item.to_css(dest)?;
521 }
522 },
523 Gradient::Radial {
524 ref shape,
525 ref position,
526 ref color_interpolation_method,
527 ref items,
528 compat_mode,
529 ..
530 } => {
531 dest.write_str("radial-gradient(")?;
532 let omit_shape = match *shape {
533 EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) |
534 EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => true,
535 _ => false,
536 };
537 let omit_position = position.is_center();
538 if compat_mode == GradientCompatMode::Modern {
539 if !omit_shape {
540 shape.to_css(dest)?;
541 if !omit_position {
542 dest.write_char(' ')?;
543 }
544 }
545 if !omit_position {
546 dest.write_str("at ")?;
547 position.to_css(dest)?;
548 }
549 } else {
550 if !omit_position {
551 position.to_css(dest)?;
552 if !omit_shape {
553 dest.write_str(", ")?;
554 }
555 }
556 if !omit_shape {
557 shape.to_css(dest)?;
558 }
559 }
560 if !has_default_color_interpolation_method {
561 if !omit_shape || !omit_position {
562 dest.write_char(' ')?;
563 }
564 color_interpolation_method.to_css(dest)?;
565 }
566
567 let mut skip_comma =
568 omit_shape && omit_position && has_default_color_interpolation_method;
569 for item in &**items {
570 if !skip_comma {
571 dest.write_str(", ")?;
572 }
573 skip_comma = false;
574 item.to_css(dest)?;
575 }
576 },
577 Gradient::Conic {
578 ref angle,
579 ref position,
580 ref color_interpolation_method,
581 ref items,
582 ..
583 } => {
584 dest.write_str("conic-gradient(")?;
585 let omit_angle = angle.is_zero();
586 let omit_position = position.is_center();
587 if !omit_angle {
588 dest.write_str("from ")?;
589 angle.to_css(dest)?;
590 if !omit_position {
591 dest.write_char(' ')?;
592 }
593 }
594 if !omit_position {
595 dest.write_str("at ")?;
596 position.to_css(dest)?;
597 }
598 if !has_default_color_interpolation_method {
599 if !omit_angle || !omit_position {
600 dest.write_char(' ')?;
601 }
602 color_interpolation_method.to_css(dest)?;
603 }
604 let mut skip_comma =
605 omit_angle && omit_position && has_default_color_interpolation_method;
606 for item in &**items {
607 if !skip_comma {
608 dest.write_str(", ")?;
609 }
610 skip_comma = false;
611 item.to_css(dest)?;
612 }
613 },
614 }
615 dest.write_char(')')
616 }
617}
618
619pub trait LineDirection {
621 fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool;
623
624 fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result
626 where
627 W: Write;
628}
629
630impl<L> ToCss for Circle<L>
631where
632 L: ToCss,
633{
634 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
635 where
636 W: Write,
637 {
638 match *self {
639 Circle::Extent(ShapeExtent::FarthestCorner) | Circle::Extent(ShapeExtent::Cover) => {
640 dest.write_str("circle")
641 },
642 Circle::Extent(keyword) => {
643 dest.write_str("circle ")?;
644 keyword.to_css(dest)
645 },
646 Circle::Radius(ref length) => length.to_css(dest),
647 }
648 }
649}