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 Url(ImageUrl),
30
31 Gradient(Box<G>),
34
35 #[cfg(feature = "gecko")]
37 #[css(function = "-moz-element")]
38 Element(Atom),
39
40 #[cfg(feature = "servo")]
43 PaintWorklet(Box<PaintWorklet>),
44
45 CrossFade(Box<GenericCrossFade<Self, Color, Percentage>>),
50
51 ImageSet(Box<GenericImageSet<Self, Resolution>>),
53
54 LightDark(#[css(skip)] Box<GenericLightDark<Self>>),
58}
59
60pub use self::GenericImage as Image;
61
62#[derive(
64 Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem, ToCss, ToComputedValue,
65)]
66#[css(comma, function = "cross-fade")]
67#[repr(C)]
68pub struct GenericCrossFade<Image, Color, Percentage> {
69 #[css(iterable)]
72 pub elements: crate::OwnedSlice<GenericCrossFadeElement<Image, Color, Percentage>>,
73}
74
75#[derive(
77 Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
78)]
79#[repr(C)]
80pub struct GenericCrossFadeElement<Image, Color, Percentage> {
81 pub percent: Optional<Percentage>,
83 pub image: GenericCrossFadeImage<Image, Color>,
86}
87
88#[derive(
91 Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
92)]
93#[repr(C, u8)]
94pub enum GenericCrossFadeImage<I, C> {
95 Image(I),
98 Color(C),
100}
101
102pub use self::GenericCrossFade as CrossFade;
103pub use self::GenericCrossFadeElement as CrossFadeElement;
104pub use self::GenericCrossFadeImage as CrossFadeImage;
105
106#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]
108#[css(comma, function = "image-set")]
109#[repr(C)]
110pub struct GenericImageSet<Image, Resolution> {
111 #[css(skip)]
113 pub selected_index: usize,
114
115 #[css(iterable)]
117 pub items: crate::OwnedSlice<GenericImageSetItem<Image, Resolution>>,
118}
119
120#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
122#[repr(C)]
123pub struct GenericImageSetItem<Image, Resolution> {
124 pub image: Image,
126 pub resolution: Resolution,
130
131 pub mime_type: crate::OwnedStr,
134
135 pub has_mime_type: bool,
137}
138
139impl<I: style_traits::ToCss, R: style_traits::ToCss> ToCss for GenericImageSetItem<I, R> {
140 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
141 where
142 W: fmt::Write,
143 {
144 self.image.to_css(dest)?;
145 dest.write_char(' ')?;
146 self.resolution.to_css(dest)?;
147
148 if self.has_mime_type {
149 dest.write_char(' ')?;
150 dest.write_str("type(")?;
151 self.mime_type.to_css(dest)?;
152 dest.write_char(')')?;
153 }
154 Ok(())
155 }
156}
157
158pub use self::GenericImageSet as ImageSet;
159pub use self::GenericImageSetItem as ImageSetItem;
160
161#[derive(
163 Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
164)]
165#[repr(C)]
166pub struct GradientFlags(u8);
167bitflags! {
168 impl GradientFlags: u8 {
169 const REPEATING = 1 << 0;
171 const HAS_DEFAULT_COLOR_INTERPOLATION_METHOD = 1 << 1;
173 }
174}
175
176#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
179#[repr(C)]
180pub enum GenericGradient<
181 LineDirection,
182 LengthPercentage,
183 NonNegativeLength,
184 NonNegativeLengthPercentage,
185 Position,
186 Angle,
187 AngleOrPercentage,
188 Color,
189> {
190 Linear {
192 direction: LineDirection,
194 color_interpolation_method: ColorInterpolationMethod,
196 items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
198 flags: GradientFlags,
200 compat_mode: GradientCompatMode,
202 },
203 Radial {
205 shape: GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>,
207 position: Position,
209 color_interpolation_method: ColorInterpolationMethod,
211 items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
213 flags: GradientFlags,
215 compat_mode: GradientCompatMode,
217 },
218 Conic {
220 angle: Angle,
222 position: Position,
224 color_interpolation_method: ColorInterpolationMethod,
226 items: crate::OwnedSlice<GenericGradientItem<Color, AngleOrPercentage>>,
228 flags: GradientFlags,
230 },
231}
232
233pub use self::GenericGradient as Gradient;
234
235#[derive(
236 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
237)]
238#[repr(u8)]
239pub enum GradientCompatMode {
241 Modern,
243 WebKit,
245 Moz,
247}
248
249#[derive(
251 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
252)]
253#[repr(C, u8)]
254pub enum GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage> {
255 Circle(GenericCircle<NonNegativeLength>),
257 Ellipse(GenericEllipse<NonNegativeLengthPercentage>),
259}
260
261pub use self::GenericEndingShape as EndingShape;
262
263#[derive(
265 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
266)]
267#[repr(C, u8)]
268pub enum GenericCircle<NonNegativeLength> {
269 Radius(NonNegativeLength),
271 Extent(ShapeExtent),
273}
274
275pub use self::GenericCircle as Circle;
276
277#[derive(
279 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
280)]
281#[repr(C, u8)]
282pub enum GenericEllipse<NonNegativeLengthPercentage> {
283 Radii(NonNegativeLengthPercentage, NonNegativeLengthPercentage),
285 Extent(ShapeExtent),
287}
288
289pub use self::GenericEllipse as Ellipse;
290
291#[allow(missing_docs)]
293#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
294#[derive(
295 Clone,
296 Copy,
297 Debug,
298 Eq,
299 MallocSizeOf,
300 Parse,
301 PartialEq,
302 ToComputedValue,
303 ToCss,
304 ToResolvedValue,
305 ToShmem,
306)]
307#[repr(u8)]
308pub enum ShapeExtent {
309 ClosestSide,
310 FarthestSide,
311 ClosestCorner,
312 FarthestCorner,
313 Contain,
314 Cover,
315}
316
317#[derive(
320 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
321)]
322#[repr(C, u8)]
323pub enum GenericGradientItem<Color, T> {
324 SimpleColorStop(Color),
326 ComplexColorStop {
328 color: Color,
330 position: T,
332 },
333 InterpolationHint(T),
335}
336
337pub use self::GenericGradientItem as GradientItem;
338
339#[derive(
342 Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
343)]
344pub struct ColorStop<Color, T> {
345 pub color: Color,
347 pub position: Option<T>,
349}
350
351impl<Color, T> ColorStop<Color, T> {
352 #[inline]
354 pub fn into_item(self) -> GradientItem<Color, T> {
355 match self.position {
356 Some(position) => GradientItem::ComplexColorStop {
357 color: self.color,
358 position,
359 },
360 None => GradientItem::SimpleColorStop(self.color),
361 }
362 }
363}
364
365#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
368#[derive(Clone, Debug, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
369pub struct PaintWorklet {
370 pub name: Atom,
372 #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
375 #[compute(no_field_bound)]
376 #[resolve(no_field_bound)]
377 pub arguments: Vec<Arc<custom_properties::SpecifiedValue>>,
378}
379
380impl ::style_traits::SpecifiedValueInfo for PaintWorklet {}
381
382impl ToCss for PaintWorklet {
383 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
384 where
385 W: Write,
386 {
387 dest.write_str("paint(")?;
388 serialize_atom_identifier(&self.name, dest)?;
389 for argument in &self.arguments {
390 dest.write_str(", ")?;
391 argument.to_css(dest)?;
392 }
393 dest.write_char(')')
394 }
395}
396
397impl<G, U, C, P, Resolution> fmt::Debug for Image<G, U, C, P, Resolution>
398where
399 Image<G, U, C, P, Resolution>: ToCss,
400{
401 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
402 self.to_css(&mut CssWriter::new(f))
403 }
404}
405
406impl<G, U, C, P, Resolution> ToCss for Image<G, U, C, P, Resolution>
407where
408 G: ToCss,
409 U: ToCss,
410 C: ToCss,
411 P: ToCss,
412 Resolution: ToCss,
413{
414 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
415 where
416 W: Write,
417 {
418 match *self {
419 Image::None => dest.write_str("none"),
420 Image::Url(ref url) => url.to_css(dest),
421 Image::Gradient(ref gradient) => gradient.to_css(dest),
422 #[cfg(feature = "servo")]
423 Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),
424 #[cfg(feature = "gecko")]
425 Image::Element(ref selector) => {
426 dest.write_str("-moz-element(#")?;
427 serialize_atom_identifier(selector, dest)?;
428 dest.write_char(')')
429 },
430 Image::ImageSet(ref is) => is.to_css(dest),
431 Image::CrossFade(ref cf) => cf.to_css(dest),
432 Image::LightDark(ref ld) => ld.to_css(dest),
433 }
434 }
435}
436
437impl<D, LP, NL, NLP, P, A: Zero, AoP, C> ToCss for Gradient<D, LP, NL, NLP, P, A, AoP, C>
438where
439 D: LineDirection,
440 LP: ToCss,
441 NL: ToCss,
442 NLP: ToCss,
443 P: PositionComponent + ToCss,
444 A: ToCss,
445 AoP: ToCss,
446 C: ToCss,
447{
448 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
449 where
450 W: Write,
451 {
452 let (compat_mode, repeating, has_default_color_interpolation_method) = match *self {
453 Gradient::Linear {
454 compat_mode, flags, ..
455 } |
456 Gradient::Radial {
457 compat_mode, flags, ..
458 } => (
459 compat_mode,
460 flags.contains(GradientFlags::REPEATING),
461 flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),
462 ),
463 Gradient::Conic { flags, .. } => (
464 GradientCompatMode::Modern,
465 flags.contains(GradientFlags::REPEATING),
466 flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),
467 ),
468 };
469
470 match compat_mode {
471 GradientCompatMode::WebKit => dest.write_str("-webkit-")?,
472 GradientCompatMode::Moz => dest.write_str("-moz-")?,
473 _ => {},
474 }
475
476 if repeating {
477 dest.write_str("repeating-")?;
478 }
479
480 match *self {
481 Gradient::Linear {
482 ref direction,
483 ref color_interpolation_method,
484 ref items,
485 compat_mode,
486 ..
487 } => {
488 dest.write_str("linear-gradient(")?;
489 let mut skip_comma = true;
490 if !direction.points_downwards(compat_mode) {
491 direction.to_css(dest, compat_mode)?;
492 skip_comma = false;
493 }
494 if !has_default_color_interpolation_method {
495 if !skip_comma {
496 dest.write_char(' ')?;
497 }
498 color_interpolation_method.to_css(dest)?;
499 skip_comma = false;
500 }
501 for item in &**items {
502 if !skip_comma {
503 dest.write_str(", ")?;
504 }
505 skip_comma = false;
506 item.to_css(dest)?;
507 }
508 },
509 Gradient::Radial {
510 ref shape,
511 ref position,
512 ref color_interpolation_method,
513 ref items,
514 compat_mode,
515 ..
516 } => {
517 dest.write_str("radial-gradient(")?;
518 let omit_shape = match *shape {
519 EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) |
520 EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => true,
521 _ => false,
522 };
523 let omit_position = position.is_center();
524 if compat_mode == GradientCompatMode::Modern {
525 if !omit_shape {
526 shape.to_css(dest)?;
527 if !omit_position {
528 dest.write_char(' ')?;
529 }
530 }
531 if !omit_position {
532 dest.write_str("at ")?;
533 position.to_css(dest)?;
534 }
535 } else {
536 if !omit_position {
537 position.to_css(dest)?;
538 if !omit_shape {
539 dest.write_str(", ")?;
540 }
541 }
542 if !omit_shape {
543 shape.to_css(dest)?;
544 }
545 }
546 if !has_default_color_interpolation_method {
547 if !omit_shape || !omit_position {
548 dest.write_char(' ')?;
549 }
550 color_interpolation_method.to_css(dest)?;
551 }
552
553 let mut skip_comma =
554 omit_shape && omit_position && has_default_color_interpolation_method;
555 for item in &**items {
556 if !skip_comma {
557 dest.write_str(", ")?;
558 }
559 skip_comma = false;
560 item.to_css(dest)?;
561 }
562 },
563 Gradient::Conic {
564 ref angle,
565 ref position,
566 ref color_interpolation_method,
567 ref items,
568 ..
569 } => {
570 dest.write_str("conic-gradient(")?;
571 let omit_angle = angle.is_zero();
572 let omit_position = position.is_center();
573 if !omit_angle {
574 dest.write_str("from ")?;
575 angle.to_css(dest)?;
576 if !omit_position {
577 dest.write_char(' ')?;
578 }
579 }
580 if !omit_position {
581 dest.write_str("at ")?;
582 position.to_css(dest)?;
583 }
584 if !has_default_color_interpolation_method {
585 if !omit_angle || !omit_position {
586 dest.write_char(' ')?;
587 }
588 color_interpolation_method.to_css(dest)?;
589 }
590 let mut skip_comma =
591 omit_angle && omit_position && has_default_color_interpolation_method;
592 for item in &**items {
593 if !skip_comma {
594 dest.write_str(", ")?;
595 }
596 skip_comma = false;
597 item.to_css(dest)?;
598 }
599 },
600 }
601 dest.write_char(')')
602 }
603}
604
605pub trait LineDirection {
607 fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool;
609
610 fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result
612 where
613 W: Write;
614}
615
616impl<L> ToCss for Circle<L>
617where
618 L: ToCss,
619{
620 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
621 where
622 W: Write,
623 {
624 match *self {
625 Circle::Extent(ShapeExtent::FarthestCorner) | Circle::Extent(ShapeExtent::Cover) => {
626 dest.write_str("circle")
627 },
628 Circle::Extent(keyword) => {
629 dest.write_str("circle ")?;
630 keyword.to_css(dest)
631 },
632 Circle::Radius(ref length) => length.to_css(dest),
633 }
634 }
635}