use crate::derives::*;
use crate::parser::{Parse, ParserContext};
use crate::typed_om::{KeywordValue, ToTyped, TypedValue};
use crate::values::generics::background::BackgroundSize as GenericBackgroundSize;
use crate::values::specified::length::{
NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto,
};
use cssparser::{match_ignore_ascii_case, Parser};
use selectors::parser::SelectorParseErrorKind;
use std::fmt::{self, Write};
use style_traits::{CssString, CssWriter, ParseError, StyleParseErrorKind, ToCss};
use thin_vec::ThinVec;
pub type BackgroundSize = GenericBackgroundSize<NonNegativeLengthPercentage>;
impl Parse for BackgroundSize {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
if let Ok(width) = input.try_parse(|i| NonNegativeLengthPercentageOrAuto::parse(context, i))
{
let height = input
.try_parse(|i| NonNegativeLengthPercentageOrAuto::parse(context, i))
.unwrap_or(NonNegativeLengthPercentageOrAuto::auto());
return Ok(GenericBackgroundSize::ExplicitSize { width, height });
}
Ok(try_match_ident_ignore_ascii_case! { input,
"cover" => GenericBackgroundSize::Cover,
"contain" => GenericBackgroundSize::Contain,
})
}
}
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
ToTyped,
)]
#[allow(missing_docs)]
#[value_info(other_values = "repeat-x,repeat-y")]
pub enum BackgroundRepeatKeyword {
Repeat,
Space,
Round,
NoRepeat,
}
#[derive(
Clone,
Debug,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
pub struct BackgroundRepeat(pub BackgroundRepeatKeyword, pub BackgroundRepeatKeyword);
impl BackgroundRepeat {
pub fn repeat() -> Self {
BackgroundRepeat(
BackgroundRepeatKeyword::Repeat,
BackgroundRepeatKeyword::Repeat,
)
}
}
impl ToCss for BackgroundRepeat {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
match (self.0, self.1) {
(BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat) => {
dest.write_str("repeat-x")
},
(BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat) => {
dest.write_str("repeat-y")
},
(horizontal, vertical) => {
horizontal.to_css(dest)?;
if horizontal != vertical {
dest.write_char(' ')?;
vertical.to_css(dest)?;
}
Ok(())
},
}
}
}
impl ToTyped for BackgroundRepeat {
fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
match (self.0, self.1) {
(BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat) => {
dest.push(TypedValue::Keyword(KeywordValue(CssString::from(
"repeat-x",
))));
Ok(())
},
(BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat) => {
dest.push(TypedValue::Keyword(KeywordValue(CssString::from(
"repeat-y",
))));
Ok(())
},
(horizontal, vertical) if horizontal == vertical => {
ToTyped::to_typed(&horizontal, dest)
},
_ => Err(()),
}
}
}
impl Parse for BackgroundRepeat {
fn parse<'i, 't>(
_context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let ident = input.expect_ident_cloned()?;
match_ignore_ascii_case! { &ident,
"repeat-x" => {
return Ok(BackgroundRepeat(BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat));
},
"repeat-y" => {
return Ok(BackgroundRepeat(BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat));
},
_ => {},
}
let horizontal = match BackgroundRepeatKeyword::from_ident(&ident) {
Ok(h) => h,
Err(()) => {
return Err(
input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))
);
},
};
let vertical = input.try_parse(BackgroundRepeatKeyword::parse).ok();
Ok(BackgroundRepeat(horizontal, vertical.unwrap_or(horizontal)))
}
}
fn background_clip_border_area_enabled(context: &ParserContext) -> bool {
context.chrome_rules_enabled()
|| static_prefs::pref!("layout.css.background-clip.border-area.enabled")
}
#[allow(missing_docs)]
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
ToTyped,
)]
#[repr(u8)]
pub enum BackgroundClip {
BorderBox,
PaddingBox,
ContentBox,
#[cfg(feature = "gecko")]
#[value_info(skip)]
FillBox,
#[cfg(feature = "gecko")]
#[value_info(skip)]
StrokeBox,
#[cfg(feature = "gecko")]
#[value_info(skip)]
ViewBox,
#[cfg(feature = "gecko")]
#[value_info(skip)]
NoClip,
#[cfg(feature = "gecko")]
Text,
#[parse(condition = "background_clip_border_area_enabled")]
#[value_info(skip)]
BorderArea,
}
bitflags! {
#[derive(Clone, Copy)]
struct ClipValidity: u8 {
const BACKGROUND = 1 << 0;
const MASK = 1 << 1;
const BOTH = Self::BACKGROUND.bits() | Self::MASK.bits();
}
}
impl BackgroundClip {
fn validity(&self) -> ClipValidity {
match *self {
Self::BorderBox => ClipValidity::BOTH,
Self::PaddingBox => ClipValidity::BOTH,
Self::ContentBox => ClipValidity::BOTH,
#[cfg(feature = "gecko")]
Self::FillBox => ClipValidity::MASK,
#[cfg(feature = "gecko")]
Self::StrokeBox => ClipValidity::MASK,
#[cfg(feature = "gecko")]
Self::ViewBox => ClipValidity::MASK,
#[cfg(feature = "gecko")]
Self::NoClip => ClipValidity::MASK,
#[cfg(feature = "gecko")]
Self::Text => ClipValidity::BACKGROUND,
Self::BorderArea => ClipValidity::BACKGROUND,
}
}
pub fn parse_for_background<'i>(
context: &ParserContext,
input: &mut Parser<'i, '_>,
) -> Result<Self, ParseError<'i>> {
let clip = Self::parse(context, input)?;
if !clip.validity().intersects(ClipValidity::BACKGROUND) {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(clip)
}
pub fn parse_for_mask<'i>(
context: &ParserContext,
input: &mut Parser<'i, '_>,
) -> Result<Self, ParseError<'i>> {
let clip = Self::parse(context, input)?;
if !clip.validity().intersects(ClipValidity::MASK) {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(clip)
}
}