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)]
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(#[css(skip)] Box<GenericLightDark<Self>>),
65}
66
67pub use self::GenericImage as Image;
68
69#[derive(
71 Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem, ToCss, ToComputedValue,
72)]
73#[css(comma, function = "cross-fade")]
74#[repr(C)]
75pub struct GenericCrossFade<Image, Color, Percentage> {
76 #[css(iterable)]
79 pub elements: crate::OwnedSlice<GenericCrossFadeElement<Image, Color, Percentage>>,
80}
81
82#[derive(
84 Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
85)]
86#[repr(C)]
87pub struct GenericCrossFadeElement<Image, Color, Percentage> {
88 pub percent: Optional<Percentage>,
90 pub image: GenericCrossFadeImage<Image, Color>,
93}
94
95#[derive(
98 Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
99)]
100#[repr(C, u8)]
101pub enum GenericCrossFadeImage<I, C> {
102 Image(I),
105 Color(C),
107}
108
109pub use self::GenericCrossFade as CrossFade;
110pub use self::GenericCrossFadeElement as CrossFadeElement;
111pub use self::GenericCrossFadeImage as CrossFadeImage;
112
113#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]
115#[css(comma, function = "image-set")]
116#[repr(C)]
117pub struct GenericImageSet<Image, Resolution> {
118 #[css(skip)]
120 pub selected_index: usize,
121
122 #[css(iterable)]
124 pub items: crate::OwnedSlice<GenericImageSetItem<Image, Resolution>>,
125}
126
127#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
129#[repr(C)]
130pub struct GenericImageSetItem<Image, Resolution> {
131 pub image: Image,
133 pub resolution: Resolution,
137
138 pub mime_type: crate::OwnedStr,
141
142 pub has_mime_type: bool,
144}
145
146impl<I: style_traits::ToCss, R: style_traits::ToCss> ToCss for GenericImageSetItem<I, R> {
147 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
148 where
149 W: fmt::Write,
150 {
151 self.image.to_css(dest)?;
152 dest.write_char(' ')?;
153 self.resolution.to_css(dest)?;
154
155 if self.has_mime_type {
156 dest.write_char(' ')?;
157 dest.write_str("type(")?;
158 self.mime_type.to_css(dest)?;
159 dest.write_char(')')?;
160 }
161 Ok(())
162 }
163}
164
165pub use self::GenericImageSet as ImageSet;
166pub use self::GenericImageSetItem as ImageSetItem;
167
168#[derive(
170 Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
171)]
172#[repr(C)]
173pub struct GradientFlags(u8);
174bitflags! {
175 impl GradientFlags: u8 {
176 const REPEATING = 1 << 0;
178 const HAS_DEFAULT_COLOR_INTERPOLATION_METHOD = 1 << 1;
180 }
181}
182
183#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
186#[repr(C)]
187pub enum GenericGradient<
188 LineDirection,
189 Length,
190 LengthPercentage,
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<NonNegative<Length>, NonNegative<LengthPercentage>>,
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, L, LP, P, A: Zero, AoP, C> ToCss for Gradient<D, L, LP, P, A, AoP, C>
450where
451 D: LineDirection,
452 L: ToCss,
453 LP: ToCss,
454 P: PositionComponent + ToCss,
455 A: ToCss,
456 AoP: ToCss,
457 C: ToCss,
458{
459 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
460 where
461 W: Write,
462 {
463 let (compat_mode, repeating, has_default_color_interpolation_method) = match *self {
464 Gradient::Linear {
465 compat_mode, flags, ..
466 }
467 | Gradient::Radial {
468 compat_mode, flags, ..
469 } => (
470 compat_mode,
471 flags.contains(GradientFlags::REPEATING),
472 flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),
473 ),
474 Gradient::Conic { flags, .. } => (
475 GradientCompatMode::Modern,
476 flags.contains(GradientFlags::REPEATING),
477 flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),
478 ),
479 };
480
481 match compat_mode {
482 GradientCompatMode::WebKit => dest.write_str("-webkit-")?,
483 GradientCompatMode::Moz => dest.write_str("-moz-")?,
484 _ => {},
485 }
486
487 if repeating {
488 dest.write_str("repeating-")?;
489 }
490
491 match *self {
492 Gradient::Linear {
493 ref direction,
494 ref color_interpolation_method,
495 ref items,
496 compat_mode,
497 ..
498 } => {
499 dest.write_str("linear-gradient(")?;
500 let mut skip_comma = true;
501 if !direction.points_downwards(compat_mode) {
502 direction.to_css(dest, compat_mode)?;
503 skip_comma = false;
504 }
505 if !has_default_color_interpolation_method {
506 if !skip_comma {
507 dest.write_char(' ')?;
508 }
509 color_interpolation_method.to_css(dest)?;
510 skip_comma = false;
511 }
512 for item in &**items {
513 if !skip_comma {
514 dest.write_str(", ")?;
515 }
516 skip_comma = false;
517 item.to_css(dest)?;
518 }
519 },
520 Gradient::Radial {
521 ref shape,
522 ref position,
523 ref color_interpolation_method,
524 ref items,
525 compat_mode,
526 ..
527 } => {
528 dest.write_str("radial-gradient(")?;
529 let omit_shape = match *shape {
530 EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover))
531 | EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => true,
532 _ => false,
533 };
534 let omit_position = position.is_center();
535 if compat_mode == GradientCompatMode::Modern {
536 if !omit_shape {
537 shape.to_css(dest)?;
538 if !omit_position {
539 dest.write_char(' ')?;
540 }
541 }
542 if !omit_position {
543 dest.write_str("at ")?;
544 position.to_css(dest)?;
545 }
546 } else {
547 if !omit_position {
548 position.to_css(dest)?;
549 if !omit_shape {
550 dest.write_str(", ")?;
551 }
552 }
553 if !omit_shape {
554 shape.to_css(dest)?;
555 }
556 }
557 if !has_default_color_interpolation_method {
558 if !omit_shape || !omit_position {
559 dest.write_char(' ')?;
560 }
561 color_interpolation_method.to_css(dest)?;
562 }
563
564 let mut skip_comma =
565 omit_shape && omit_position && has_default_color_interpolation_method;
566 for item in &**items {
567 if !skip_comma {
568 dest.write_str(", ")?;
569 }
570 skip_comma = false;
571 item.to_css(dest)?;
572 }
573 },
574 Gradient::Conic {
575 ref angle,
576 ref position,
577 ref color_interpolation_method,
578 ref items,
579 ..
580 } => {
581 dest.write_str("conic-gradient(")?;
582 let omit_angle = angle.is_zero();
583 let omit_position = position.is_center();
584 if !omit_angle {
585 dest.write_str("from ")?;
586 angle.to_css(dest)?;
587 if !omit_position {
588 dest.write_char(' ')?;
589 }
590 }
591 if !omit_position {
592 dest.write_str("at ")?;
593 position.to_css(dest)?;
594 }
595 if !has_default_color_interpolation_method {
596 if !omit_angle || !omit_position {
597 dest.write_char(' ')?;
598 }
599 color_interpolation_method.to_css(dest)?;
600 }
601 let mut skip_comma =
602 omit_angle && omit_position && has_default_color_interpolation_method;
603 for item in &**items {
604 if !skip_comma {
605 dest.write_str(", ")?;
606 }
607 skip_comma = false;
608 item.to_css(dest)?;
609 }
610 },
611 }
612 dest.write_char(')')
613 }
614}
615
616pub trait LineDirection {
618 fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool;
620
621 fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result
623 where
624 W: Write;
625}
626
627impl<L> ToCss for Circle<L>
628where
629 L: ToCss,
630{
631 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
632 where
633 W: Write,
634 {
635 match *self {
636 Circle::Extent(ShapeExtent::FarthestCorner) | Circle::Extent(ShapeExtent::Cover) => {
637 dest.write_str("circle")
638 },
639 Circle::Extent(keyword) => {
640 dest.write_str("circle ")?;
641 keyword.to_css(dest)
642 },
643 Circle::Radius(ref length) => length.to_css(dest),
644 }
645 }
646}