blob: 7ad2dfe8e4a4e23143efce8d23c39fc4300596a1 [file] [log] [blame]
#![allow(non_snake_case)]
use super::{Property, PropertyId};
use crate::context::PropertyHandlerContext;
use crate::declaration::DeclarationList;
use crate::prefixes::Feature;
use crate::traits::{FallbackValues, IsCompatible, PropertyHandler};
use crate::vendor_prefix::VendorPrefix;
macro_rules! define_prefixes {
(
$( $name: ident, )+
) => {
#[derive(Default)]
pub(crate) struct PrefixHandler {
$(
$name: Option<usize>,
)+
}
impl<'i> PropertyHandler<'i> for PrefixHandler {
fn handle_property(&mut self, property: &Property<'i>, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext) -> bool {
match property {
$(
Property::$name(val, prefix) => {
if let Some(i) = self.$name {
if let Some(decl) = dest.get_mut(i) {
if let Property::$name(cur, prefixes) = decl {
// If the value is the same, update the prefix.
// If the prefix is the same, then update the value.
if val == cur || prefixes.contains(*prefix) {
*cur = val.clone();
*prefixes |= *prefix;
*prefixes = context.targets.prefixes(*prefixes, Feature::$name);
return true
}
}
}
}
// Update the prefixes based on the targets.
let prefixes = context.targets.prefixes(*prefix, Feature::$name);
// Store the index of the property, so we can update it later.
self.$name = Some(dest.len());
dest.push(Property::$name(val.clone(), prefixes))
}
)+
_ => return false
}
true
}
fn finalize(&mut self, _: &mut DeclarationList, _: &mut PropertyHandlerContext) {}
}
};
}
define_prefixes! {
TransformOrigin,
TransformStyle,
BackfaceVisibility,
Perspective,
PerspectiveOrigin,
BoxSizing,
TabSize,
Hyphens,
TextAlignLast,
TextDecorationSkipInk,
TextOverflow,
UserSelect,
Appearance,
ClipPath,
BoxDecorationBreak,
TextSizeAdjust,
}
macro_rules! define_fallbacks {
(
$( $name: ident$(($p: ident))?, )+
) => {
paste::paste! {
#[derive(Default)]
pub(crate) struct FallbackHandler {
$(
[<$name:snake>]: Option<usize>
),+
}
}
impl<'i> PropertyHandler<'i> for FallbackHandler {
fn handle_property(&mut self, property: &Property<'i>, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>) -> bool {
match property {
$(
Property::$name(val $(, mut $p)?) => {
let mut val = val.clone();
$(
$p = context.targets.prefixes($p, Feature::$name);
)?
if paste::paste! { self.[<$name:snake>] }.is_none() {
let fallbacks = val.get_fallbacks(context.targets);
#[allow(unused_variables)]
let has_fallbacks = !fallbacks.is_empty();
for fallback in fallbacks {
dest.push(Property::$name(fallback $(, $p)?))
}
$(
if has_fallbacks && $p.contains(VendorPrefix::None) {
$p = VendorPrefix::None;
}
)?
}
if paste::paste! { self.[<$name:snake>] }.is_none() || matches!(context.targets.browsers, Some(targets) if !val.is_compatible(targets)) {
paste::paste! { self.[<$name:snake>] = Some(dest.len()) };
dest.push(Property::$name(val $(, $p)?));
} else if let Some(index) = paste::paste! { self.[<$name:snake>] } {
dest[index] = Property::$name(val $(, $p)?);
}
}
)+
Property::Unparsed(val) => {
let (mut unparsed, index) = match val.property_id {
$(
PropertyId::$name$(($p))? => {
macro_rules! get_prefixed {
($vp: ident) => {
if $vp.contains(VendorPrefix::None) {
val.get_prefixed(context.targets, Feature::$name)
} else {
val.clone()
}
};
() => {
val.clone()
};
}
let val = get_prefixed!($($p)?);
(val, paste::paste! { &mut self.[<$name:snake>] })
}
)+
_ => return false
};
// Unparsed properties are always "valid", meaning they always override previous declarations.
context.add_unparsed_fallbacks(&mut unparsed);
if let Some(index) = *index {
dest[index] = Property::Unparsed(unparsed);
} else {
*index = Some(dest.len());
dest.push(Property::Unparsed(unparsed));
}
}
_ => return false
}
true
}
fn finalize(&mut self, _: &mut DeclarationList, _: &mut PropertyHandlerContext) {
$(
paste::paste! { self.[<$name:snake>] = None };
)+
}
}
};
}
define_fallbacks! {
Color,
TextShadow,
Filter(prefix),
BackdropFilter(prefix),
Fill,
Stroke,
CaretColor,
Caret,
}