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)]
23#[repr(C, u8)]
24pub enum GenericImage<G, ImageUrl, Color, Percentage, Resolution> {
25 None,
27
28 Url(ImageUrl),
30
31 Gradient(Box<G>),
34
35 #[cfg(feature = "gecko")]
37 #[css(function = "-moz-element")]
38 Element(Atom),
39
40 #[cfg(feature = "gecko")]
44 #[css(function, skip)]
45 MozSymbolicIcon(Atom),
46
47 #[cfg(feature = "servo")]
50 PaintWorklet(Box<PaintWorklet>),
51
52 CrossFade(Box<GenericCrossFade<Self, Color, Percentage>>),
57
58 ImageSet(Box<GenericImageSet<Self, Resolution>>),
60
61 LightDark(Box<GenericLightDark<Self>>),
63
64 #[css(function)]
66 Image(#[value_info(skip)] Box<Color>),
67}
68
69pub use self::GenericImage as Image;
70
71#[derive(
73 Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem, ToCss, ToComputedValue,
74)]
75#[css(comma, function = "cross-fade")]
76#[repr(C)]
77pub struct GenericCrossFade<Image, Color, Percentage> {
78 #[css(iterable)]
81 pub elements: crate::OwnedSlice<GenericCrossFadeElement<Image, Color, Percentage>>,
82}
83
84#[derive(
86 Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
87)]
88#[repr(C)]
89pub struct GenericCrossFadeElement<Image, Color, Percentage> {
90 pub percent: Optional<Percentage>,
92 pub image: GenericCrossFadeImage<Image, Color>,
95}
96
97#[derive(
100 Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
101)]
102#[repr(C, u8)]
103pub enum GenericCrossFadeImage<I, C> {
104 Image(I),
107 Color(C),
109}
110
111pub use self::GenericCrossFade as CrossFade;
112pub use self::GenericCrossFadeElement as CrossFadeElement;
113pub use self::GenericCrossFadeImage as CrossFadeImage;
114
115#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]
117#[css(comma, function = "image-set")]
118#[repr(C)]
119pub struct GenericImageSet<Image, Resolution> {
120 #[css(skip)]
122 pub selected_index: usize,
123
124 #[css(iterable)]
126 pub items: crate::OwnedSlice<GenericImageSetItem<Image, Resolution>>,
127}
128
129#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
131#[repr(C)]
132pub struct GenericImageSetItem<Image, Resolution> {
133 pub image: Image,
135 pub resolution: Resolution,
139
140 pub mime_type: crate::OwnedStr,
143
144 pub has_mime_type: bool,
146}
147
148impl<I: style_traits::ToCss, R: style_traits::ToCss> ToCss for GenericImageSetItem<I, R> {
149 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
150 where
151 W: fmt::Write,
152 {
153 self.image.to_css(dest)?;
154 dest.write_char(' ')?;
155 self.resolution.to_css(dest)?;
156
157 if self.has_mime_type {
158 dest.write_char(' ')?;
159 dest.write_str("type(")?;
160 self.mime_type.to_css(dest)?;
161 dest.write_char(')')?;
162 }
163 Ok(())
164 }
165}
166
167pub use self::GenericImageSet as ImageSet;
168pub use self::GenericImageSetItem as ImageSetItem;
169
170#[derive(
172 Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
173)]
174#[repr(C)]
175pub struct GradientFlags(u8);
176bitflags! {
177 impl GradientFlags: u8 {
178 const REPEATING = 1 << 0;
180 const HAS_DEFAULT_COLOR_INTERPOLATION_METHOD = 1 << 1;
182 }
183}
184
185#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
188#[repr(C)]
189pub enum GenericGradient<
190 LineDirection,
191 Length,
192 LengthPercentage,
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<NonNegative<Length>, NonNegative<LengthPercentage>>,
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::Image(ref color) => {
445 dest.write_str("image(")?;
446 color.to_css(dest)?;
447 dest.write_char(')')
448 },
449 Image::ImageSet(ref is) => is.to_css(dest),
450 Image::CrossFade(ref cf) => cf.to_css(dest),
451 Image::LightDark(ref ld) => ld.to_css(dest),
452 }
453 }
454}
455
456impl<D, L, LP, P, A: Zero, AoP, C> ToCss for Gradient<D, L, LP, P, A, AoP, C>
457where
458 D: LineDirection,
459 L: ToCss,
460 LP: ToCss,
461 P: PositionComponent + ToCss,
462 A: ToCss,
463 AoP: ToCss,
464 C: ToCss,
465{
466 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
467 where
468 W: Write,
469 {
470 let (compat_mode, repeating, has_default_color_interpolation_method) = match *self {
471 Gradient::Linear {
472 compat_mode, flags, ..
473 }
474 | Gradient::Radial {
475 compat_mode, flags, ..
476 } => (
477 compat_mode,
478 flags.contains(GradientFlags::REPEATING),
479 flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),
480 ),
481 Gradient::Conic { flags, .. } => (
482 GradientCompatMode::Modern,
483 flags.contains(GradientFlags::REPEATING),
484 flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),
485 ),
486 };
487
488 match compat_mode {
489 GradientCompatMode::WebKit => dest.write_str("-webkit-")?,
490 GradientCompatMode::Moz => dest.write_str("-moz-")?,
491 _ => {},
492 }
493
494 if repeating {
495 dest.write_str("repeating-")?;
496 }
497
498 match *self {
499 Gradient::Linear {
500 ref direction,
501 ref color_interpolation_method,
502 ref items,
503 compat_mode,
504 ..
505 } => {
506 dest.write_str("linear-gradient(")?;
507 let mut skip_comma = true;
508 if !direction.points_downwards(compat_mode) {
509 direction.to_css(dest, compat_mode)?;
510 skip_comma = false;
511 }
512 if !has_default_color_interpolation_method {
513 if !skip_comma {
514 dest.write_char(' ')?;
515 }
516 color_interpolation_method.to_css(dest)?;
517 skip_comma = false;
518 }
519 for item in &**items {
520 if !skip_comma {
521 dest.write_str(", ")?;
522 }
523 skip_comma = false;
524 item.to_css(dest)?;
525 }
526 },
527 Gradient::Radial {
528 ref shape,
529 ref position,
530 ref color_interpolation_method,
531 ref items,
532 compat_mode,
533 ..
534 } => {
535 dest.write_str("radial-gradient(")?;
536 let omit_shape = match *shape {
537 EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover))
538 | EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => true,
539 _ => false,
540 };
541 let omit_position = position.is_center();
542 if compat_mode == GradientCompatMode::Modern {
543 if !omit_shape {
544 shape.to_css(dest)?;
545 if !omit_position {
546 dest.write_char(' ')?;
547 }
548 }
549 if !omit_position {
550 dest.write_str("at ")?;
551 position.to_css(dest)?;
552 }
553 } else {
554 if !omit_position {
555 position.to_css(dest)?;
556 if !omit_shape {
557 dest.write_str(", ")?;
558 }
559 }
560 if !omit_shape {
561 shape.to_css(dest)?;
562 }
563 }
564 if !has_default_color_interpolation_method {
565 if !omit_shape || !omit_position {
566 dest.write_char(' ')?;
567 }
568 color_interpolation_method.to_css(dest)?;
569 }
570
571 let mut skip_comma =
572 omit_shape && omit_position && has_default_color_interpolation_method;
573 for item in &**items {
574 if !skip_comma {
575 dest.write_str(", ")?;
576 }
577 skip_comma = false;
578 item.to_css(dest)?;
579 }
580 },
581 Gradient::Conic {
582 ref angle,
583 ref position,
584 ref color_interpolation_method,
585 ref items,
586 ..
587 } => {
588 dest.write_str("conic-gradient(")?;
589 let omit_angle = angle.is_zero();
590 let omit_position = position.is_center();
591 if !omit_angle {
592 dest.write_str("from ")?;
593 angle.to_css(dest)?;
594 if !omit_position {
595 dest.write_char(' ')?;
596 }
597 }
598 if !omit_position {
599 dest.write_str("at ")?;
600 position.to_css(dest)?;
601 }
602 if !has_default_color_interpolation_method {
603 if !omit_angle || !omit_position {
604 dest.write_char(' ')?;
605 }
606 color_interpolation_method.to_css(dest)?;
607 }
608 let mut skip_comma =
609 omit_angle && omit_position && has_default_color_interpolation_method;
610 for item in &**items {
611 if !skip_comma {
612 dest.write_str(", ")?;
613 }
614 skip_comma = false;
615 item.to_css(dest)?;
616 }
617 },
618 }
619 dest.write_char(')')
620 }
621}
622
623pub trait LineDirection {
625 fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool;
627
628 fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result
630 where
631 W: Write;
632}
633
634impl<L> ToCss for Circle<L>
635where
636 L: ToCss,
637{
638 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
639 where
640 W: Write,
641 {
642 match *self {
643 Circle::Extent(ShapeExtent::FarthestCorner) | Circle::Extent(ShapeExtent::Cover) => {
644 dest.write_str("circle")
645 },
646 Circle::Extent(keyword) => {
647 dest.write_str("circle ")?;
648 keyword.to_css(dest)
649 },
650 Circle::Radius(ref length) => length.to_css(dest),
651 }
652 }
653}