Source code

Revision control

Copy as Markdown

Other Tools

/* 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/. */
//! CSS handling for the computed value of
//! [`image`][image]s
//!
use crate::values::computed::percentage::Percentage;
use crate::values::computed::position::Position;
use crate::values::computed::url::ComputedImageUrl;
use crate::values::computed::{Angle, Color, Context};
use crate::values::computed::{
AngleOrPercentage, LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage,
Resolution, ToComputedValue,
};
use crate::values::generics::image::{self as generic, GradientCompatMode};
use crate::values::specified::image as specified;
use crate::values::specified::position::{HorizontalPositionKeyword, VerticalPositionKeyword};
use std::f32::consts::PI;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
pub use specified::ImageRendering;
/// Computed values for an image according to CSS-IMAGES.
pub type Image = generic::GenericImage<Gradient, ComputedImageUrl, Color, Percentage, Resolution>;
// Images should remain small, see https://github.com/servo/servo/pull/18430
size_of_test!(Image, 16);
/// Computed values for a CSS gradient.
pub type Gradient = generic::GenericGradient<
LineDirection,
LengthPercentage,
NonNegativeLength,
NonNegativeLengthPercentage,
Position,
Angle,
AngleOrPercentage,
Color,
>;
/// Computed values for CSS cross-fade
pub type CrossFade = generic::CrossFade<Image, Color, Percentage>;
/// A computed radial gradient ending shape.
pub type EndingShape = generic::GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>;
/// A computed gradient line direction.
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToResolvedValue)]
#[repr(C, u8)]
pub enum LineDirection {
/// An angle.
Angle(Angle),
/// A horizontal direction.
Horizontal(HorizontalPositionKeyword),
/// A vertical direction.
Vertical(VerticalPositionKeyword),
/// A corner.
Corner(HorizontalPositionKeyword, VerticalPositionKeyword),
}
/// The computed value for an `image-set()` image.
pub type ImageSet = generic::GenericImageSet<Image, Resolution>;
impl ToComputedValue for specified::ImageSet {
type ComputedValue = ImageSet;
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
let items = self.items.to_computed_value(context);
let dpr = context.device().device_pixel_ratio().get();
let mut supported_image = false;
let mut selected_index = std::usize::MAX;
let mut selected_resolution = 0.0;
for (i, item) in items.iter().enumerate() {
if item.has_mime_type && !context.device().is_supported_mime_type(&item.mime_type) {
// If the MIME type is not supported, we discard the ImageSetItem.
continue;
}
let candidate_resolution = item.resolution.dppx();
debug_assert!(
candidate_resolution >= 0.0,
"Resolutions should be non-negative"
);
if candidate_resolution == 0.0 {
// If the resolution is 0, we also treat it as an invalid image.
continue;
}
//
// Make a UA-specific choice of which to load, based on whatever criteria deemed
// relevant (such as the resolution of the display, connection speed, etc).
//
// For now, select the lowest resolution greater than display density, otherwise the
// greatest resolution available.
let better_candidate = || {
if selected_resolution < dpr && candidate_resolution > selected_resolution {
return true;
}
if candidate_resolution < selected_resolution && candidate_resolution >= dpr {
return true;
}
false
};
// The first item with a supported MIME type is obviously the current best candidate
if !supported_image || better_candidate() {
supported_image = true;
selected_index = i;
selected_resolution = candidate_resolution;
}
}
ImageSet {
selected_index,
items,
}
}
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
Self {
selected_index: std::usize::MAX,
items: ToComputedValue::from_computed_value(&computed.items),
}
}
}
impl generic::LineDirection for LineDirection {
fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool {
match *self {
LineDirection::Angle(angle) => angle.radians() == PI,
LineDirection::Vertical(VerticalPositionKeyword::Bottom) => {
compat_mode == GradientCompatMode::Modern
},
LineDirection::Vertical(VerticalPositionKeyword::Top) => {
compat_mode != GradientCompatMode::Modern
},
_ => false,
}
}
fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result
where
W: Write,
{
match *self {
LineDirection::Angle(ref angle) => angle.to_css(dest),
LineDirection::Horizontal(x) => {
if compat_mode == GradientCompatMode::Modern {
dest.write_str("to ")?;
}
x.to_css(dest)
},
LineDirection::Vertical(y) => {
if compat_mode == GradientCompatMode::Modern {
dest.write_str("to ")?;
}
y.to_css(dest)
},
LineDirection::Corner(x, y) => {
if compat_mode == GradientCompatMode::Modern {
dest.write_str("to ")?;
}
x.to_css(dest)?;
dest.write_char(' ')?;
y.to_css(dest)
},
}
}
}
impl ToComputedValue for specified::LineDirection {
type ComputedValue = LineDirection;
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
match *self {
specified::LineDirection::Angle(ref angle) => {
LineDirection::Angle(angle.to_computed_value(context))
},
specified::LineDirection::Horizontal(x) => LineDirection::Horizontal(x),
specified::LineDirection::Vertical(y) => LineDirection::Vertical(y),
specified::LineDirection::Corner(x, y) => LineDirection::Corner(x, y),
}
}
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
match *computed {
LineDirection::Angle(ref angle) => {
specified::LineDirection::Angle(ToComputedValue::from_computed_value(angle))
},
LineDirection::Horizontal(x) => specified::LineDirection::Horizontal(x),
LineDirection::Vertical(y) => specified::LineDirection::Vertical(y),
LineDirection::Corner(x, y) => specified::LineDirection::Corner(x, y),
}
}
}