summaryrefslogtreecommitdiffstats
path: root/servo/components/style/values/generics/image.rs
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components/style/values/generics/image.rs')
-rw-r--r--servo/components/style/values/generics/image.rs631
1 files changed, 631 insertions, 0 deletions
diff --git a/servo/components/style/values/generics/image.rs b/servo/components/style/values/generics/image.rs
new file mode 100644
index 0000000000..6fc0870e15
--- /dev/null
+++ b/servo/components/style/values/generics/image.rs
@@ -0,0 +1,631 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Generic types for the handling of [images].
+//!
+//! [images]: https://drafts.csswg.org/css-images/#image-values
+
+use crate::color::mix::ColorInterpolationMethod;
+use crate::custom_properties;
+use crate::values::generics::position::PositionComponent;
+use crate::values::generics::Optional;
+use crate::values::serialize_atom_identifier;
+use crate::Atom;
+use crate::Zero;
+use servo_arc::Arc;
+use std::fmt::{self, Write};
+use style_traits::{CssWriter, ToCss};
+
+/// An `<image> | none` value.
+///
+/// https://drafts.csswg.org/css-images/#image-values
+#[derive(
+ Clone, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem,
+)]
+#[repr(C, u8)]
+pub enum GenericImage<G, ImageUrl, Color, Percentage, Resolution> {
+ /// `none` variant.
+ None,
+ /// A `<url()>` image.
+ Url(ImageUrl),
+
+ /// A `<gradient>` image. Gradients are rather large, and not nearly as
+ /// common as urls, so we box them here to keep the size of this enum sane.
+ Gradient(Box<G>),
+
+ /// A `-moz-element(# <element-id>)`
+ #[cfg(feature = "gecko")]
+ #[css(function = "-moz-element")]
+ Element(Atom),
+
+ /// A paint worklet image.
+ /// <https://drafts.css-houdini.org/css-paint-api/>
+ #[cfg(feature = "servo-layout-2013")]
+ PaintWorklet(PaintWorklet),
+
+ /// A `<cross-fade()>` image. Storing this directly inside of
+ /// GenericImage increases the size by 8 bytes so we box it here
+ /// and store images directly inside of cross-fade instead of
+ /// boxing them there.
+ CrossFade(Box<GenericCrossFade<Self, Color, Percentage>>),
+
+ /// An `image-set()` function.
+ ImageSet(#[compute(field_bound)] Box<GenericImageSet<Self, Resolution>>),
+}
+
+pub use self::GenericImage as Image;
+
+/// <https://drafts.csswg.org/css-images-4/#cross-fade-function>
+#[derive(
+ Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem, ToCss, ToComputedValue,
+)]
+#[css(comma, function = "cross-fade")]
+#[repr(C)]
+pub struct GenericCrossFade<Image, Color, Percentage> {
+ /// All of the image percent pairings passed as arguments to
+ /// cross-fade.
+ #[css(iterable)]
+ pub elements: crate::OwnedSlice<GenericCrossFadeElement<Image, Color, Percentage>>,
+}
+
+/// An optional percent and a cross fade image.
+#[derive(
+ Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
+)]
+#[repr(C)]
+pub struct GenericCrossFadeElement<Image, Color, Percentage> {
+ /// The percent of the final image that `image` will be.
+ pub percent: Optional<Percentage>,
+ /// A color or image that will be blended when cross-fade is
+ /// evaluated.
+ pub image: GenericCrossFadeImage<Image, Color>,
+}
+
+/// An image or a color. `cross-fade` takes either when blending
+/// images together.
+#[derive(
+ Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
+)]
+#[repr(C, u8)]
+pub enum GenericCrossFadeImage<I, C> {
+ /// A boxed image value. Boxing provides indirection so images can
+ /// be cross-fades and cross-fades can be images.
+ Image(I),
+ /// A color value.
+ Color(C),
+}
+
+pub use self::GenericCrossFade as CrossFade;
+pub use self::GenericCrossFadeElement as CrossFadeElement;
+pub use self::GenericCrossFadeImage as CrossFadeImage;
+
+/// https://drafts.csswg.org/css-images-4/#image-set-notation
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]
+#[css(comma, function = "image-set")]
+#[repr(C)]
+pub struct GenericImageSet<Image, Resolution> {
+ /// The index of the selected candidate. usize::MAX for specified values or invalid images.
+ #[css(skip)]
+ pub selected_index: usize,
+
+ /// All of the image and resolution pairs.
+ #[css(iterable)]
+ pub items: crate::OwnedSlice<GenericImageSetItem<Image, Resolution>>,
+}
+
+/// An optional percent and a cross fade image.
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
+#[repr(C)]
+pub struct GenericImageSetItem<Image, Resolution> {
+ /// `<image>`. `<string>` is converted to `Image::Url` at parse time.
+ pub image: Image,
+ /// The `<resolution>`.
+ ///
+ /// TODO: Skip serialization if it is 1x.
+ pub resolution: Resolution,
+
+ /// The `type(<string>)`
+ /// (Optional) Specify the image's MIME type
+ pub mime_type: crate::OwnedStr,
+
+ /// True if mime_type has been specified
+ pub has_mime_type: bool,
+}
+
+impl<I: style_traits::ToCss, R: style_traits::ToCss> ToCss for GenericImageSetItem<I, R> {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
+ self.image.to_css(dest)?;
+ dest.write_char(' ')?;
+ self.resolution.to_css(dest)?;
+
+ if self.has_mime_type {
+ dest.write_char(' ')?;
+ dest.write_str("type(")?;
+ self.mime_type.to_css(dest)?;
+ dest.write_char(')')?;
+ }
+ Ok(())
+ }
+}
+
+pub use self::GenericImageSet as ImageSet;
+pub use self::GenericImageSetItem as ImageSetItem;
+
+/// State flags stored on each variant of a Gradient.
+#[derive(
+ Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
+)]
+#[repr(C)]
+pub struct GradientFlags(u8);
+bitflags! {
+ impl GradientFlags: u8 {
+ /// Set if this is a repeating gradient.
+ const REPEATING = 1 << 0;
+ /// Set if the color interpolation method matches the default for the items.
+ const HAS_DEFAULT_COLOR_INTERPOLATION_METHOD = 1 << 1;
+ }
+}
+
+/// A CSS gradient.
+/// <https://drafts.csswg.org/css-images/#gradients>
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
+#[repr(C)]
+pub enum GenericGradient<
+ LineDirection,
+ LengthPercentage,
+ NonNegativeLength,
+ NonNegativeLengthPercentage,
+ Position,
+ Angle,
+ AngleOrPercentage,
+ Color,
+> {
+ /// A linear gradient.
+ Linear {
+ /// Line direction
+ direction: LineDirection,
+ /// Method to use for color interpolation.
+ color_interpolation_method: ColorInterpolationMethod,
+ /// The color stops and interpolation hints.
+ items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
+ /// State flags for the gradient.
+ flags: GradientFlags,
+ /// Compatibility mode.
+ compat_mode: GradientCompatMode,
+ },
+ /// A radial gradient.
+ Radial {
+ /// Shape of gradient
+ shape: GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>,
+ /// Center of gradient
+ position: Position,
+ /// Method to use for color interpolation.
+ color_interpolation_method: ColorInterpolationMethod,
+ /// The color stops and interpolation hints.
+ items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
+ /// State flags for the gradient.
+ flags: GradientFlags,
+ /// Compatibility mode.
+ compat_mode: GradientCompatMode,
+ },
+ /// A conic gradient.
+ Conic {
+ /// Start angle of gradient
+ angle: Angle,
+ /// Center of gradient
+ position: Position,
+ /// Method to use for color interpolation.
+ color_interpolation_method: ColorInterpolationMethod,
+ /// The color stops and interpolation hints.
+ items: crate::OwnedSlice<GenericGradientItem<Color, AngleOrPercentage>>,
+ /// State flags for the gradient.
+ flags: GradientFlags,
+ },
+}
+
+pub use self::GenericGradient as Gradient;
+
+#[derive(
+ Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
+)]
+#[repr(u8)]
+/// Whether we used the modern notation or the compatibility `-webkit`, `-moz` prefixes.
+pub enum GradientCompatMode {
+ /// Modern syntax.
+ Modern,
+ /// `-webkit` prefix.
+ WebKit,
+ /// `-moz` prefix
+ Moz,
+}
+
+/// A radial gradient's ending shape.
+#[derive(
+ Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
+)]
+#[repr(C, u8)]
+pub enum GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage> {
+ /// A circular gradient.
+ Circle(GenericCircle<NonNegativeLength>),
+ /// An elliptic gradient.
+ Ellipse(GenericEllipse<NonNegativeLengthPercentage>),
+}
+
+pub use self::GenericEndingShape as EndingShape;
+
+/// A circle shape.
+#[derive(
+ Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
+)]
+#[repr(C, u8)]
+pub enum GenericCircle<NonNegativeLength> {
+ /// A circle radius.
+ Radius(NonNegativeLength),
+ /// A circle extent.
+ Extent(ShapeExtent),
+}
+
+pub use self::GenericCircle as Circle;
+
+/// An ellipse shape.
+#[derive(
+ Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
+)]
+#[repr(C, u8)]
+pub enum GenericEllipse<NonNegativeLengthPercentage> {
+ /// An ellipse pair of radii.
+ Radii(NonNegativeLengthPercentage, NonNegativeLengthPercentage),
+ /// An ellipse extent.
+ Extent(ShapeExtent),
+}
+
+pub use self::GenericEllipse as Ellipse;
+
+/// <https://drafts.csswg.org/css-images/#typedef-extent-keyword>
+#[allow(missing_docs)]
+#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
+#[derive(
+ Clone,
+ Copy,
+ Debug,
+ Eq,
+ MallocSizeOf,
+ Parse,
+ PartialEq,
+ ToComputedValue,
+ ToCss,
+ ToResolvedValue,
+ ToShmem,
+)]
+#[repr(u8)]
+pub enum ShapeExtent {
+ ClosestSide,
+ FarthestSide,
+ ClosestCorner,
+ FarthestCorner,
+ Contain,
+ Cover,
+}
+
+/// A gradient item.
+/// <https://drafts.csswg.org/css-images-4/#color-stop-syntax>
+#[derive(
+ Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
+)]
+#[repr(C, u8)]
+pub enum GenericGradientItem<Color, T> {
+ /// A simple color stop, without position.
+ SimpleColorStop(Color),
+ /// A complex color stop, with a position.
+ ComplexColorStop {
+ /// The color for the stop.
+ color: Color,
+ /// The position for the stop.
+ position: T,
+ },
+ /// An interpolation hint.
+ InterpolationHint(T),
+}
+
+pub use self::GenericGradientItem as GradientItem;
+
+/// A color stop.
+/// <https://drafts.csswg.org/css-images/#typedef-color-stop-list>
+#[derive(
+ Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
+)]
+pub struct ColorStop<Color, T> {
+ /// The color of this stop.
+ pub color: Color,
+ /// The position of this stop.
+ pub position: Option<T>,
+}
+
+impl<Color, T> ColorStop<Color, T> {
+ /// Convert the color stop into an appropriate `GradientItem`.
+ #[inline]
+ pub fn into_item(self) -> GradientItem<Color, T> {
+ match self.position {
+ Some(position) => GradientItem::ComplexColorStop {
+ color: self.color,
+ position,
+ },
+ None => GradientItem::SimpleColorStop(self.color),
+ }
+ }
+}
+
+/// Specified values for a paint worklet.
+/// <https://drafts.css-houdini.org/css-paint-api/>
+#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
+#[derive(Clone, Debug, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
+pub struct PaintWorklet {
+ /// The name the worklet was registered with.
+ pub name: Atom,
+ /// The arguments for the worklet.
+ /// TODO: store a parsed representation of the arguments.
+ #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
+ #[compute(no_field_bound)]
+ #[resolve(no_field_bound)]
+ pub arguments: Vec<Arc<custom_properties::SpecifiedValue>>,
+}
+
+impl ::style_traits::SpecifiedValueInfo for PaintWorklet {}
+
+impl ToCss for PaintWorklet {
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ dest.write_str("paint(")?;
+ serialize_atom_identifier(&self.name, dest)?;
+ for argument in &self.arguments {
+ dest.write_str(", ")?;
+ argument.to_css(dest)?;
+ }
+ dest.write_char(')')
+ }
+}
+
+impl<G, U, C, P, Resolution> fmt::Debug for Image<G, U, C, P, Resolution>
+where
+ Image<G, U, C, P, Resolution>: ToCss,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.to_css(&mut CssWriter::new(f))
+ }
+}
+
+impl<G, U, C, P, Resolution> ToCss for Image<G, U, C, P, Resolution>
+where
+ G: ToCss,
+ U: ToCss,
+ C: ToCss,
+ P: ToCss,
+ Resolution: ToCss,
+{
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ match *self {
+ Image::None => dest.write_str("none"),
+ Image::Url(ref url) => url.to_css(dest),
+ Image::Gradient(ref gradient) => gradient.to_css(dest),
+ #[cfg(feature = "servo-layout-2013")]
+ Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),
+ #[cfg(feature = "gecko")]
+ Image::Element(ref selector) => {
+ dest.write_str("-moz-element(#")?;
+ serialize_atom_identifier(selector, dest)?;
+ dest.write_char(')')
+ },
+ Image::ImageSet(ref is) => is.to_css(dest),
+ Image::CrossFade(ref cf) => cf.to_css(dest),
+ }
+ }
+}
+
+impl<D, LP, NL, NLP, P, A: Zero, AoP, C> ToCss for Gradient<D, LP, NL, NLP, P, A, AoP, C>
+where
+ D: LineDirection,
+ LP: ToCss,
+ NL: ToCss,
+ NLP: ToCss,
+ P: PositionComponent + ToCss,
+ A: ToCss,
+ AoP: ToCss,
+ C: ToCss,
+{
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ let (compat_mode, repeating, has_default_color_interpolation_method) = match *self {
+ Gradient::Linear {
+ compat_mode, flags, ..
+ } |
+ Gradient::Radial {
+ compat_mode, flags, ..
+ } => (
+ compat_mode,
+ flags.contains(GradientFlags::REPEATING),
+ flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),
+ ),
+ Gradient::Conic { flags, .. } => (
+ GradientCompatMode::Modern,
+ flags.contains(GradientFlags::REPEATING),
+ flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),
+ ),
+ };
+
+ match compat_mode {
+ GradientCompatMode::WebKit => dest.write_str("-webkit-")?,
+ GradientCompatMode::Moz => dest.write_str("-moz-")?,
+ _ => {},
+ }
+
+ if repeating {
+ dest.write_str("repeating-")?;
+ }
+
+ match *self {
+ Gradient::Linear {
+ ref direction,
+ ref color_interpolation_method,
+ ref items,
+ compat_mode,
+ ..
+ } => {
+ dest.write_str("linear-gradient(")?;
+ let mut skip_comma = true;
+ if !direction.points_downwards(compat_mode) {
+ direction.to_css(dest, compat_mode)?;
+ skip_comma = false;
+ }
+ if !has_default_color_interpolation_method {
+ if !skip_comma {
+ dest.write_char(' ')?;
+ }
+ color_interpolation_method.to_css(dest)?;
+ skip_comma = false;
+ }
+ for item in &**items {
+ if !skip_comma {
+ dest.write_str(", ")?;
+ }
+ skip_comma = false;
+ item.to_css(dest)?;
+ }
+ },
+ Gradient::Radial {
+ ref shape,
+ ref position,
+ ref color_interpolation_method,
+ ref items,
+ compat_mode,
+ ..
+ } => {
+ dest.write_str("radial-gradient(")?;
+ let omit_shape = match *shape {
+ EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) |
+ EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => true,
+ _ => false,
+ };
+ let omit_position = position.is_center();
+ if compat_mode == GradientCompatMode::Modern {
+ if !omit_shape {
+ shape.to_css(dest)?;
+ if !omit_position {
+ dest.write_char(' ')?;
+ }
+ }
+ if !omit_position {
+ dest.write_str("at ")?;
+ position.to_css(dest)?;
+ }
+ } else {
+ if !omit_position {
+ position.to_css(dest)?;
+ if !omit_shape {
+ dest.write_str(", ")?;
+ }
+ }
+ if !omit_shape {
+ shape.to_css(dest)?;
+ }
+ }
+ if !has_default_color_interpolation_method {
+ if !omit_shape || !omit_position {
+ dest.write_char(' ')?;
+ }
+ color_interpolation_method.to_css(dest)?;
+ }
+
+ let mut skip_comma =
+ omit_shape && omit_position && has_default_color_interpolation_method;
+ for item in &**items {
+ if !skip_comma {
+ dest.write_str(", ")?;
+ }
+ skip_comma = false;
+ item.to_css(dest)?;
+ }
+ },
+ Gradient::Conic {
+ ref angle,
+ ref position,
+ ref color_interpolation_method,
+ ref items,
+ ..
+ } => {
+ dest.write_str("conic-gradient(")?;
+ let omit_angle = angle.is_zero();
+ let omit_position = position.is_center();
+ if !omit_angle {
+ dest.write_str("from ")?;
+ angle.to_css(dest)?;
+ if !omit_position {
+ dest.write_char(' ')?;
+ }
+ }
+ if !omit_position {
+ dest.write_str("at ")?;
+ position.to_css(dest)?;
+ }
+ if !has_default_color_interpolation_method {
+ if !omit_angle || !omit_position {
+ dest.write_char(' ')?;
+ }
+ color_interpolation_method.to_css(dest)?;
+ }
+ let mut skip_comma =
+ omit_angle && omit_position && has_default_color_interpolation_method;
+ for item in &**items {
+ if !skip_comma {
+ dest.write_str(", ")?;
+ }
+ skip_comma = false;
+ item.to_css(dest)?;
+ }
+ },
+ }
+ dest.write_char(')')
+ }
+}
+
+/// The direction of a linear gradient.
+pub trait LineDirection {
+ /// Whether this direction points towards, and thus can be omitted.
+ fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool;
+
+ /// Serialises this direction according to the compatibility mode.
+ fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result
+ where
+ W: Write;
+}
+
+impl<L> ToCss for Circle<L>
+where
+ L: ToCss,
+{
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ match *self {
+ Circle::Extent(ShapeExtent::FarthestCorner) | Circle::Extent(ShapeExtent::Cover) => {
+ dest.write_str("circle")
+ },
+ Circle::Extent(keyword) => {
+ dest.write_str("circle ")?;
+ keyword.to_css(dest)
+ },
+ Circle::Radius(ref length) => length.to_css(dest),
+ }
+ }
+}