Bug 1766655 - Introduce Optional<T> to represent optional values in the style system. r=dshin

cross-fade() was kinda doing this in its own way with PercentOrNone, but
since now we have more use-cases for this we should probably make this a
slightly more general solution.

I added some convenience APIs, but they're unused as of this patch so
let me know if you want them gone.

Differential Revision: https://phabricator.services.mozilla.com/D144831
This commit is contained in:
Emilio Cobos Álvarez 2022-04-27 15:30:54 +00:00
Родитель 642b5e7837
Коммит 14f8cafbc3
4 изменённых файлов: 90 добавлений и 39 удалений

Просмотреть файл

@ -47,8 +47,6 @@ pub type Gradient = generic::GenericGradient<
/// Computed values for CSS cross-fade
/// <https://drafts.csswg.org/css-images-4/#cross-fade-function>
pub type CrossFade = generic::CrossFade<Image, Color, Percentage>;
/// A computed percentage or nothing.
pub type PercentOrNone = generic::PercentOrNone<Percentage>;
/// A computed radial gradient ending shape.
pub type EndingShape = generic::GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>;

Просмотреть файл

@ -8,6 +8,7 @@
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;
@ -71,20 +72,6 @@ pub struct GenericCrossFade<Image, Color, Percentage> {
pub elements: crate::OwnedSlice<GenericCrossFadeElement<Image, Color, Percentage>>,
}
/// A `<percent> | none` value. Represents optional percentage values
/// assosicated with cross-fade images.
#[derive(
Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
)]
#[repr(C, u8)]
pub enum PercentOrNone<Percentage> {
/// `none` variant.
#[css(skip)]
None,
/// A percentage variant.
Percent(Percentage),
}
/// An optional percent and a cross fade image.
#[derive(
Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
@ -92,7 +79,7 @@ pub enum PercentOrNone<Percentage> {
#[repr(C)]
pub struct GenericCrossFadeElement<Image, Color, Percentage> {
/// The percent of the final image that `image` will be.
pub percent: PercentOrNone<Percentage>,
pub percent: Optional<Percentage>,
/// A color or image that will be blended when cross-fade is
/// evaluated.
pub image: GenericCrossFadeImage<Image, Color>,

Просмотреть файл

@ -310,3 +310,74 @@ impl<L> ClipRectOrAuto<L> {
}
pub use page::PageSize;
/// An optional value, much like `Option<T>`, but with a defined struct layout
/// to be able to use it from C++ as well.
///
/// Note that this is relatively inefficient, struct-layout-wise, as you have
/// one byte for the tag, but padding to the alignment of T. If you have
/// multiple optional values and care about struct compactness, you might be
/// better off "coalescing" the combinations into a parent enum. But that
/// shouldn't matter for most use cases.
#[allow(missing_docs)]
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(C, u8)]
pub enum Optional<T> {
#[css(skip)]
None,
Some(T),
}
impl<T> Optional<T> {
/// Returns whether this value is present.
pub fn is_some(&self) -> bool {
matches!(*self, Self::Some(..))
}
/// Returns whether this value is not present.
pub fn is_none(&self) -> bool {
matches!(*self, Self::None)
}
/// Turns this Optional<> into a regular rust Option<>.
pub fn into_rust(self) -> Option<T> {
match self {
Self::Some(v) => Some(v),
Self::None => None,
}
}
/// Return a reference to the containing value, if any, as a plain rust
/// Option<>.
pub fn as_ref(&self) -> Option<&T> {
match *self {
Self::Some(ref v) => Some(v),
Self::None => None,
}
}
}
impl<T> From<Option<T>> for Optional<T> {
fn from(rust: Option<T>) -> Self {
match rust {
Some(t) => Self::Some(t),
None => Self::None,
}
}
}

Просмотреть файл

@ -60,8 +60,6 @@ pub type CrossFade = generic::CrossFade<Image, Color, Percentage>;
pub type CrossFadeElement = generic::CrossFadeElement<Image, Color, Percentage>;
/// CrossFadeImage = image | color
pub type CrossFadeImage = generic::CrossFadeImage<Image, Color>;
/// A specified percentage or nothing.
pub type PercentOrNone = generic::PercentOrNone<Percentage>;
/// `image-set()`
pub type ImageSet = generic::ImageSet<Image, Resolution>;
@ -303,6 +301,16 @@ impl CrossFade {
}
impl CrossFadeElement {
fn parse_percentage<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Option<Percentage> {
// We clamp our values here as this is the way that Safari and Chrome's
// implementation handle out-of-bounds percentages but whether or not
// this behavior follows the specification is still being discussed.
// See: <https://github.com/w3c/csswg-drafts/issues/5333>
input.try_parse(|input| Percentage::parse_non_negative(context, input))
.ok()
.map(|p| p.clamp_to_hundred())
}
/// <cf-image> = <percentage>? && [ <image> | <color> ]
fn parse<'i, 't>(
context: &ParserContext,
@ -310,14 +318,17 @@ impl CrossFadeElement {
cors_mode: CorsMode,
) -> Result<Self, ParseError<'i>> {
// Try and parse a leading percent sign.
let mut percent = PercentOrNone::parse_or_none(context, input);
let mut percent = Self::parse_percentage(context, input);
// Parse the image
let image = CrossFadeImage::parse(context, input, cors_mode)?;
// Try and parse a trailing percent sign.
if percent == PercentOrNone::None {
percent = PercentOrNone::parse_or_none(context, input);
if percent.is_none() {
percent = Self::parse_percentage(context, input);
}
Ok(Self { percent, image })
Ok(Self {
percent: percent.into(),
image,
})
}
}
@ -339,22 +350,6 @@ impl CrossFadeImage {
}
}
impl PercentOrNone {
fn parse_or_none<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Self {
// We clamp our values here as this is the way that Safari and
// Chrome's implementation handle out-of-bounds percentages
// but whether or not this behavior follows the specification
// is still being discussed. See:
// <https://github.com/w3c/csswg-drafts/issues/5333>
if let Ok(percent) = input.try_parse(|input| Percentage::parse_non_negative(context, input))
{
Self::Percent(percent.clamp_to_hundred())
} else {
Self::None
}
}
}
impl ImageSet {
fn parse<'i, 't>(
context: &ParserContext,