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