use crate::compat::Feature;
use crate::context::PropertyHandlerContext;
use crate::declaration::{DeclarationBlock, DeclarationList};
use crate::error::{ParserError, PrinterError};
use crate::logical::PropertyCategory;
use crate::macros::{define_shorthand, rect_shorthand, size_shorthand};
use crate::printer::Printer;
use crate::properties::{Property, PropertyId};
use crate::traits::{IsCompatible, Parse, PropertyHandler, Shorthand, ToCss};
use crate::values::{length::LengthPercentageOrAuto, rect::Rect, size::Size2D};
#[cfg(feature = "visitor")]
use crate::visitor::Visit;
use cssparser::*;
rect_shorthand! {
pub struct Margin<LengthPercentageOrAuto> {
MarginTop,
MarginRight,
MarginBottom,
MarginLeft
}
}
rect_shorthand! {
pub struct Padding<LengthPercentageOrAuto> {
PaddingTop,
PaddingRight,
PaddingBottom,
PaddingLeft
}
}
rect_shorthand! {
pub struct ScrollMargin<LengthPercentageOrAuto> {
ScrollMarginTop,
ScrollMarginRight,
ScrollMarginBottom,
ScrollMarginLeft
}
}
rect_shorthand! {
pub struct ScrollPadding<LengthPercentageOrAuto> {
ScrollPaddingTop,
ScrollPaddingRight,
ScrollPaddingBottom,
ScrollPaddingLeft
}
}
rect_shorthand! {
pub struct Inset<LengthPercentageOrAuto> {
Top,
Right,
Bottom,
Left
}
}
size_shorthand! {
pub struct MarginBlock<LengthPercentageOrAuto> {
block_start: MarginBlockStart,
block_end: MarginBlockEnd,
}
}
size_shorthand! {
pub struct MarginInline<LengthPercentageOrAuto> {
inline_start: MarginInlineStart,
inline_end: MarginInlineEnd,
}
}
size_shorthand! {
pub struct PaddingBlock<LengthPercentageOrAuto> {
block_start: PaddingBlockStart,
block_end: PaddingBlockEnd,
}
}
size_shorthand! {
pub struct PaddingInline<LengthPercentageOrAuto> {
inline_start: PaddingInlineStart,
inline_end: PaddingInlineEnd,
}
}
size_shorthand! {
pub struct ScrollMarginBlock<LengthPercentageOrAuto> {
block_start: ScrollMarginBlockStart,
block_end: ScrollMarginBlockEnd,
}
}
size_shorthand! {
pub struct ScrollMarginInline<LengthPercentageOrAuto> {
inline_start: ScrollMarginInlineStart,
inline_end: ScrollMarginInlineEnd,
}
}
size_shorthand! {
pub struct ScrollPaddingBlock<LengthPercentageOrAuto> {
block_start: ScrollPaddingBlockStart,
block_end: ScrollPaddingBlockEnd,
}
}
size_shorthand! {
pub struct ScrollPaddingInline<LengthPercentageOrAuto> {
inline_start: ScrollPaddingInlineStart,
inline_end: ScrollPaddingInlineEnd,
}
}
size_shorthand! {
pub struct InsetBlock<LengthPercentageOrAuto> {
block_start: InsetBlockStart,
block_end: InsetBlockEnd,
}
}
size_shorthand! {
pub struct InsetInline<LengthPercentageOrAuto> {
inline_start: InsetInlineStart,
inline_end: InsetInlineEnd,
}
}
macro_rules! side_handler {
($name: ident, $top: ident, $bottom: ident, $left: ident, $right: ident, $block_start: ident, $block_end: ident, $inline_start: ident, $inline_end: ident, $shorthand: ident, $block_shorthand: ident, $inline_shorthand: ident, $shorthand_category: ident $(, $feature: ident, $shorthand_feature: ident)?) => {
#[derive(Debug, Default)]
pub(crate) struct $name<'i> {
top: Option<LengthPercentageOrAuto>,
bottom: Option<LengthPercentageOrAuto>,
left: Option<LengthPercentageOrAuto>,
right: Option<LengthPercentageOrAuto>,
block_start: Option<Property<'i>>,
block_end: Option<Property<'i>>,
inline_start: Option<Property<'i>>,
inline_end: Option<Property<'i>>,
has_any: bool,
category: PropertyCategory
}
impl<'i> PropertyHandler<'i> for $name<'i> {
fn handle_property(&mut self, property: &Property<'i>, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>) -> bool {
use Property::*;
macro_rules! flush {
($key: ident, $val: expr, $category: ident) => {{
if PropertyCategory::$category != self.category || (self.$key.is_some() && matches!(context.targets.browsers, Some(targets) if !$val.is_compatible(targets))) {
self.flush(dest, context);
}
}}
}
macro_rules! property {
($key: ident, $val: ident, $category: ident) => {{
flush!($key, $val, $category);
self.$key = Some($val.clone());
self.category = PropertyCategory::$category;
self.has_any = true;
}};
}
macro_rules! logical_property {
($prop: ident, $val: expr) => {{
if self.category != PropertyCategory::Logical || (self.$prop.is_some() && matches!($val, Property::Unparsed(_))) {
self.flush(dest, context);
}
self.$prop = Some($val);
self.category = PropertyCategory::Logical;
self.has_any = true;
}};
}
match &property {
$top(val) => property!(top, val, Physical),
$bottom(val) => property!(bottom, val, Physical),
$left(val) => property!(left, val, Physical),
$right(val) => property!(right, val, Physical),
$block_start(val) => {
flush!(block_start, val, Logical);
logical_property!(block_start, property.clone());
},
$block_end(val) => {
flush!(block_end, val, Logical);
logical_property!(block_end, property.clone());
},
$inline_start(val) => {
flush!(inline_start, val, Logical);
logical_property!(inline_start, property.clone())
},
$inline_end(val) => {
flush!(inline_end, val, Logical);
logical_property!(inline_end, property.clone());
},
$block_shorthand(val) => {
flush!(block_start, val.block_start, Logical);
flush!(block_end, val.block_end, Logical);
logical_property!(block_start, Property::$block_start(val.block_start.clone()));
logical_property!(block_end, Property::$block_end(val.block_end.clone()));
},
$inline_shorthand(val) => {
flush!(inline_start, val.inline_start, Logical);
flush!(inline_end, val.inline_end, Logical);
logical_property!(inline_start, Property::$inline_start(val.inline_start.clone()));
logical_property!(inline_end, Property::$inline_end(val.inline_end.clone()));
},
$shorthand(val) => {
flush!(top, val.top, $shorthand_category);
flush!(right, val.right, $shorthand_category);
flush!(bottom, val.bottom, $shorthand_category);
flush!(left, val.left, $shorthand_category);
self.top = Some(val.top.clone());
self.right = Some(val.right.clone());
self.bottom = Some(val.bottom.clone());
self.left = Some(val.left.clone());
self.block_start = None;
self.block_end = None;
self.inline_start = None;
self.inline_end = None;
self.has_any = true;
}
Unparsed(val) if matches!(val.property_id, PropertyId::$top | PropertyId::$bottom | PropertyId::$left | PropertyId::$right | PropertyId::$block_start | PropertyId::$block_end | PropertyId::$inline_start | PropertyId::$inline_end | PropertyId::$block_shorthand | PropertyId::$inline_shorthand | PropertyId::$shorthand) => {
match &val.property_id {
PropertyId::$block_start => logical_property!(block_start, property.clone()),
PropertyId::$block_end => logical_property!(block_end, property.clone()),
PropertyId::$inline_start => logical_property!(inline_start, property.clone()),
PropertyId::$inline_end => logical_property!(inline_end, property.clone()),
_ => {
self.flush(dest, context);
dest.push(property.clone());
}
}
}
_ => return false
}
true
}
fn finalize(&mut self, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>) {
self.flush(dest, context);
}
}
impl<'i> $name<'i> {
fn flush(&mut self, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>) {
if !self.has_any {
return
}
self.has_any = false;
let top = std::mem::take(&mut self.top);
let bottom = std::mem::take(&mut self.bottom);
let left = std::mem::take(&mut self.left);
let right = std::mem::take(&mut self.right);
let logical_supported = true $(&& !context.should_compile_logical(Feature::$feature))?;
if (PropertyCategory::$shorthand_category != PropertyCategory::Logical || logical_supported) && top.is_some() && bottom.is_some() && left.is_some() && right.is_some() {
dest.push(Property::$shorthand($shorthand {
top: top.unwrap(),
right: right.unwrap(),
bottom: bottom.unwrap(),
left: left.unwrap()
}));
} else {
if let Some(val) = top {
dest.push(Property::$top(val));
}
if let Some(val) = bottom {
dest.push(Property::$bottom(val));
}
if let Some(val) = left {
dest.push(Property::$left(val));
}
if let Some(val) = right {
dest.push(Property::$right(val));
}
}
let block_start = std::mem::take(&mut self.block_start);
let block_end = std::mem::take(&mut self.block_end);
let inline_start = std::mem::take(&mut self.inline_start);
let inline_end = std::mem::take(&mut self.inline_end);
macro_rules! logical_side {
($start: ident, $end: ident, $shorthand_prop: ident, $start_prop: ident, $end_prop: ident) => {
let shorthand_supported = logical_supported $(&& !context.should_compile_logical(Feature::$shorthand_feature))?;
if let (Some(Property::$start_prop(start)), Some(Property::$end_prop(end)), true) = (&$start, &$end, shorthand_supported) {
dest.push(Property::$shorthand_prop($shorthand_prop {
$start: start.clone(),
$end: end.clone()
}));
} else {
if let Some(val) = $start {
dest.push(val);
}
if let Some(val) = $end {
dest.push(val);
}
}
};
}
macro_rules! prop {
($val: ident, $logical: ident, $physical: ident) => {
match $val {
Some(Property::$logical(val)) => {
dest.push(Property::$physical(val));
}
Some(Property::Unparsed(val)) => {
dest.push(Property::Unparsed(val.with_property_id(PropertyId::$physical)));
}
_ => {}
}
}
}
if logical_supported {
logical_side!(block_start, block_end, $block_shorthand, $block_start, $block_end);
} else {
prop!(block_start, $block_start, $top);
prop!(block_end, $block_end, $bottom);
}
if logical_supported {
logical_side!(inline_start, inline_end, $inline_shorthand, $inline_start, $inline_end);
} else if inline_start.is_some() || inline_end.is_some() {
if matches!((&inline_start, &inline_end), (Some(Property::$inline_start(start)), Some(Property::$inline_end(end))) if start == end) {
prop!(inline_start, $inline_start, $left);
prop!(inline_end, $inline_end, $right);
} else {
macro_rules! logical_prop {
($val: ident, $logical: ident, $ltr: ident, $rtl: ident) => {
match $val {
Some(Property::$logical(val)) => {
context.add_logical_rule(
Property::$ltr(val.clone()),
Property::$rtl(val)
);
}
Some(Property::Unparsed(val)) => {
context.add_logical_rule(
Property::Unparsed(val.with_property_id(PropertyId::$ltr)),
Property::Unparsed(val.with_property_id(PropertyId::$rtl))
);
}
_ => {}
}
}
}
logical_prop!(inline_start, $inline_start, $left, $right);
logical_prop!(inline_end, $inline_end, $right, $left);
}
}
}
}
};
}
side_handler!(
MarginHandler,
MarginTop,
MarginBottom,
MarginLeft,
MarginRight,
MarginBlockStart,
MarginBlockEnd,
MarginInlineStart,
MarginInlineEnd,
Margin,
MarginBlock,
MarginInline,
Physical,
LogicalMargin,
LogicalMarginShorthand
);
side_handler!(
PaddingHandler,
PaddingTop,
PaddingBottom,
PaddingLeft,
PaddingRight,
PaddingBlockStart,
PaddingBlockEnd,
PaddingInlineStart,
PaddingInlineEnd,
Padding,
PaddingBlock,
PaddingInline,
Physical,
LogicalPadding,
LogicalPaddingShorthand
);
side_handler!(
ScrollMarginHandler,
ScrollMarginTop,
ScrollMarginBottom,
ScrollMarginLeft,
ScrollMarginRight,
ScrollMarginBlockStart,
ScrollMarginBlockEnd,
ScrollMarginInlineStart,
ScrollMarginInlineEnd,
ScrollMargin,
ScrollMarginBlock,
ScrollMarginInline,
Physical
);
side_handler!(
ScrollPaddingHandler,
ScrollPaddingTop,
ScrollPaddingBottom,
ScrollPaddingLeft,
ScrollPaddingRight,
ScrollPaddingBlockStart,
ScrollPaddingBlockEnd,
ScrollPaddingInlineStart,
ScrollPaddingInlineEnd,
ScrollPadding,
ScrollPaddingBlock,
ScrollPaddingInline,
Physical
);
side_handler!(
InsetHandler,
Top,
Bottom,
Left,
Right,
InsetBlockStart,
InsetBlockEnd,
InsetInlineStart,
InsetInlineEnd,
Inset,
InsetBlock,
InsetInline,
Logical,
LogicalInset,
LogicalInset
);