#![warn(unused_must_use)]
use crate::compat::Feature;
use crate::css_values::length::LengthPercentageOrAuto;
use crate::logical::PropertyCategory;
use crate::properties::{Property, PropertyId, PropertyIdTag};
use crate::{DeclarationList, PropertyHandlerContext};
use bun_alloc::ArenaVecExt as _;
pub trait RectShorthand {
type Value;
}
pub trait SizeShorthand {
type Value;
}
impl_size_shorthand!(InsetBlock, LengthPercentageOrAuto, block_start, block_end);
impl_size_shorthand!(
InsetInline,
LengthPercentageOrAuto,
inline_start,
inline_end
);
impl_size_shorthand!(MarginBlock, LengthPercentageOrAuto, block_start, block_end);
impl_size_shorthand!(
MarginInline,
LengthPercentageOrAuto,
inline_start,
inline_end
);
impl_size_shorthand!(PaddingBlock, LengthPercentageOrAuto, block_start, block_end);
impl_size_shorthand!(
PaddingInline,
LengthPercentageOrAuto,
inline_start,
inline_end
);
impl_size_shorthand!(
ScrollMarginBlock,
LengthPercentageOrAuto,
block_start,
block_end
);
impl_size_shorthand!(
ScrollMarginInline,
LengthPercentageOrAuto,
inline_start,
inline_end
);
impl_size_shorthand!(
ScrollPaddingBlock,
LengthPercentageOrAuto,
block_start,
block_end
);
impl_size_shorthand!(
ScrollPaddingInline,
LengthPercentageOrAuto,
inline_start,
inline_end
);
define_rect_shorthand! {
Inset, LengthPercentageOrAuto,
top: Top,
right: Right,
bottom: Bottom,
left: Left
}
#[derive(Clone, PartialEq)]
pub struct InsetBlock {
pub block_start: LengthPercentageOrAuto,
pub block_end: LengthPercentageOrAuto,
}
impl InsetBlock {
pub const PROPERTY_FIELD_MAP: &'static [(&'static str, PropertyIdTag)] = &[
("block_start", PropertyIdTag::InsetBlockStart),
("block_end", PropertyIdTag::InsetBlockEnd),
];
}
impl SizeShorthand for InsetBlock {
type Value = LengthPercentageOrAuto;
}
#[derive(Clone, PartialEq)]
pub struct InsetInline {
pub inline_start: LengthPercentageOrAuto,
pub inline_end: LengthPercentageOrAuto,
}
impl InsetInline {
pub const PROPERTY_FIELD_MAP: &'static [(&'static str, PropertyIdTag)] = &[
("inline_start", PropertyIdTag::InsetInlineStart),
("inline_end", PropertyIdTag::InsetInlineEnd),
];
}
impl SizeShorthand for InsetInline {
type Value = LengthPercentageOrAuto;
}
#[derive(Clone, PartialEq)]
pub struct MarginBlock {
pub block_start: LengthPercentageOrAuto,
pub block_end: LengthPercentageOrAuto,
}
impl MarginBlock {
pub const PROPERTY_FIELD_MAP: &'static [(&'static str, PropertyIdTag)] = &[
("block_start", PropertyIdTag::MarginBlockStart),
("block_end", PropertyIdTag::MarginBlockEnd),
];
}
impl SizeShorthand for MarginBlock {
type Value = LengthPercentageOrAuto;
}
#[derive(Clone, PartialEq)]
pub struct MarginInline {
pub inline_start: LengthPercentageOrAuto,
pub inline_end: LengthPercentageOrAuto,
}
impl MarginInline {
pub const PROPERTY_FIELD_MAP: &'static [(&'static str, PropertyIdTag)] = &[
("inline_start", PropertyIdTag::MarginInlineStart),
("inline_end", PropertyIdTag::MarginInlineEnd),
];
}
impl SizeShorthand for MarginInline {
type Value = LengthPercentageOrAuto;
}
define_rect_shorthand! {
Margin, LengthPercentageOrAuto,
top: MarginTop,
right: MarginRight,
bottom: MarginBottom,
left: MarginLeft
}
#[derive(Clone, PartialEq)]
pub struct PaddingBlock {
pub block_start: LengthPercentageOrAuto,
pub block_end: LengthPercentageOrAuto,
}
impl PaddingBlock {
pub const PROPERTY_FIELD_MAP: &'static [(&'static str, PropertyIdTag)] = &[
("block_start", PropertyIdTag::PaddingBlockStart),
("block_end", PropertyIdTag::PaddingBlockEnd),
];
}
impl SizeShorthand for PaddingBlock {
type Value = LengthPercentageOrAuto;
}
#[derive(Clone, PartialEq)]
pub struct PaddingInline {
pub inline_start: LengthPercentageOrAuto,
pub inline_end: LengthPercentageOrAuto,
}
impl PaddingInline {
pub const PROPERTY_FIELD_MAP: &'static [(&'static str, PropertyIdTag)] = &[
("inline_start", PropertyIdTag::PaddingInlineStart),
("inline_end", PropertyIdTag::PaddingInlineEnd),
];
}
impl SizeShorthand for PaddingInline {
type Value = LengthPercentageOrAuto;
}
define_rect_shorthand! {
Padding, LengthPercentageOrAuto,
top: PaddingTop,
right: PaddingRight,
bottom: PaddingBottom,
left: PaddingLeft
}
#[derive(Clone, PartialEq)]
pub struct ScrollMarginBlock {
pub block_start: LengthPercentageOrAuto,
pub block_end: LengthPercentageOrAuto,
}
impl ScrollMarginBlock {
pub const PROPERTY_FIELD_MAP: &'static [(&'static str, PropertyIdTag)] = &[
("block_start", PropertyIdTag::ScrollMarginBlockStart),
("block_end", PropertyIdTag::ScrollMarginBlockEnd),
];
}
impl SizeShorthand for ScrollMarginBlock {
type Value = LengthPercentageOrAuto;
}
#[derive(Clone, PartialEq)]
pub struct ScrollMarginInline {
pub inline_start: LengthPercentageOrAuto,
pub inline_end: LengthPercentageOrAuto,
}
impl ScrollMarginInline {
pub const PROPERTY_FIELD_MAP: &'static [(&'static str, PropertyIdTag)] = &[
("inline_start", PropertyIdTag::ScrollMarginInlineStart),
("inline_end", PropertyIdTag::ScrollMarginInlineEnd),
];
}
impl SizeShorthand for ScrollMarginInline {
type Value = LengthPercentageOrAuto;
}
define_rect_shorthand! {
ScrollMargin, LengthPercentageOrAuto,
top: ScrollMarginTop,
right: ScrollMarginRight,
bottom: ScrollMarginBottom,
left: ScrollMarginLeft
}
#[derive(Clone, PartialEq)]
pub struct ScrollPaddingBlock {
pub block_start: LengthPercentageOrAuto,
pub block_end: LengthPercentageOrAuto,
}
impl ScrollPaddingBlock {
pub const PROPERTY_FIELD_MAP: &'static [(&'static str, PropertyIdTag)] = &[
("block_start", PropertyIdTag::ScrollPaddingBlockStart),
("block_end", PropertyIdTag::ScrollPaddingBlockEnd),
];
}
impl SizeShorthand for ScrollPaddingBlock {
type Value = LengthPercentageOrAuto;
}
#[derive(Clone, PartialEq)]
pub struct ScrollPaddingInline {
pub inline_start: LengthPercentageOrAuto,
pub inline_end: LengthPercentageOrAuto,
}
impl ScrollPaddingInline {
pub const PROPERTY_FIELD_MAP: &'static [(&'static str, PropertyIdTag)] = &[
("inline_start", PropertyIdTag::ScrollPaddingInlineStart),
("inline_end", PropertyIdTag::ScrollPaddingInlineEnd),
];
}
impl SizeShorthand for ScrollPaddingInline {
type Value = LengthPercentageOrAuto;
}
define_rect_shorthand! {
ScrollPadding, LengthPercentageOrAuto,
top: ScrollPaddingTop,
right: ScrollPaddingRight,
bottom: ScrollPaddingBottom,
left: ScrollPaddingLeft
}
pub type MarginHandler = SizeHandler<MarginSpec>;
pub type PaddingHandler = SizeHandler<PaddingSpec>;
pub type ScrollMarginHandler = SizeHandler<ScrollMarginSpec>;
pub type InsetHandler = SizeHandler<InsetSpec>;
#[derive(Copy, Clone)]
enum PhysicalSlot {
Top,
Bottom,
Left,
Right,
}
#[derive(Copy, Clone)]
enum LogicalSlot {
BlockStart,
BlockEnd,
InlineStart,
InlineEnd,
}
pub trait SizeHandlerSpec {
const TOP: PropertyIdTag;
const BOTTOM: PropertyIdTag;
const LEFT: PropertyIdTag;
const RIGHT: PropertyIdTag;
const BLOCK_START: PropertyIdTag;
const BLOCK_END: PropertyIdTag;
const INLINE_START: PropertyIdTag;
const INLINE_END: PropertyIdTag;
const SHORTHAND: PropertyIdTag;
const BLOCK_SHORTHAND: PropertyIdTag;
const INLINE_SHORTHAND: PropertyIdTag;
const TOP_ID: PropertyId;
const BOTTOM_ID: PropertyId;
const LEFT_ID: PropertyId;
const RIGHT_ID: PropertyId;
const SHORTHAND_CATEGORY: PropertyCategory;
const FEATURE: Option<Feature>;
const SHORTHAND_FEATURE: Option<Feature>;
type Shorthand;
type BlockShorthand;
type InlineShorthand;
fn extract_top(p: &Property) -> &LengthPercentageOrAuto;
fn extract_bottom(p: &Property) -> &LengthPercentageOrAuto;
fn extract_left(p: &Property) -> &LengthPercentageOrAuto;
fn extract_right(p: &Property) -> &LengthPercentageOrAuto;
fn extract_block_start(p: &Property) -> &LengthPercentageOrAuto;
fn extract_block_end(p: &Property) -> &LengthPercentageOrAuto;
fn extract_inline_start(p: &Property) -> &LengthPercentageOrAuto;
fn extract_inline_end(p: &Property) -> &LengthPercentageOrAuto;
fn extract_shorthand(p: &Property) -> &Self::Shorthand;
fn extract_block_shorthand(p: &Property) -> &Self::BlockShorthand;
fn extract_inline_shorthand(p: &Property) -> &Self::InlineShorthand;
fn make_top(v: LengthPercentageOrAuto) -> Property;
fn make_bottom(v: LengthPercentageOrAuto) -> Property;
fn make_left(v: LengthPercentageOrAuto) -> Property;
fn make_right(v: LengthPercentageOrAuto) -> Property;
fn make_block_start(v: LengthPercentageOrAuto) -> Property;
fn make_block_end(v: LengthPercentageOrAuto) -> Property;
fn make_inline_start(v: LengthPercentageOrAuto) -> Property;
fn make_inline_end(v: LengthPercentageOrAuto) -> Property;
fn make_shorthand(
top: LengthPercentageOrAuto,
bottom: LengthPercentageOrAuto,
left: LengthPercentageOrAuto,
right: LengthPercentageOrAuto,
) -> Property;
fn make_block_shorthand(
block_start: LengthPercentageOrAuto,
block_end: LengthPercentageOrAuto,
) -> Property;
fn make_inline_shorthand(
inline_start: LengthPercentageOrAuto,
inline_end: LengthPercentageOrAuto,
) -> Property;
fn shorthand_top(v: &Self::Shorthand) -> &LengthPercentageOrAuto;
fn shorthand_right(v: &Self::Shorthand) -> &LengthPercentageOrAuto;
fn shorthand_bottom(v: &Self::Shorthand) -> &LengthPercentageOrAuto;
fn shorthand_left(v: &Self::Shorthand) -> &LengthPercentageOrAuto;
fn block_shorthand_start(v: &Self::BlockShorthand) -> &LengthPercentageOrAuto;
fn block_shorthand_end(v: &Self::BlockShorthand) -> &LengthPercentageOrAuto;
fn inline_shorthand_start(v: &Self::InlineShorthand) -> &LengthPercentageOrAuto;
fn inline_shorthand_end(v: &Self::InlineShorthand) -> &LengthPercentageOrAuto;
}
pub struct SizeHandler<S: SizeHandlerSpec> {
pub top: Option<LengthPercentageOrAuto>,
pub bottom: Option<LengthPercentageOrAuto>,
pub left: Option<LengthPercentageOrAuto>,
pub right: Option<LengthPercentageOrAuto>,
pub block_start: Option<Property>,
pub block_end: Option<Property>,
pub inline_start: Option<Property>,
pub inline_end: Option<Property>,
pub has_any: bool,
pub category: PropertyCategory,
_spec: core::marker::PhantomData<S>,
}
impl<S: SizeHandlerSpec> Default for SizeHandler<S> {
fn default() -> Self {
Self {
top: None,
bottom: None,
left: None,
right: None,
block_start: None,
block_end: None,
inline_start: None,
inline_end: None,
has_any: false,
category: PropertyCategory::default(),
_spec: core::marker::PhantomData,
}
}
}
impl<S: SizeHandlerSpec> SizeHandler<S> {
fn physical_slot(&mut self, slot: PhysicalSlot) -> &mut Option<LengthPercentageOrAuto> {
match slot {
PhysicalSlot::Top => &mut self.top,
PhysicalSlot::Bottom => &mut self.bottom,
PhysicalSlot::Left => &mut self.left,
PhysicalSlot::Right => &mut self.right,
}
}
fn physical_slot_is_some(&self, slot: PhysicalSlot) -> bool {
match slot {
PhysicalSlot::Top => self.top.is_some(),
PhysicalSlot::Bottom => self.bottom.is_some(),
PhysicalSlot::Left => self.left.is_some(),
PhysicalSlot::Right => self.right.is_some(),
}
}
fn logical_slot(&mut self, slot: LogicalSlot) -> &mut Option<Property> {
match slot {
LogicalSlot::BlockStart => &mut self.block_start,
LogicalSlot::BlockEnd => &mut self.block_end,
LogicalSlot::InlineStart => &mut self.inline_start,
LogicalSlot::InlineEnd => &mut self.inline_end,
}
}
fn logical_slot_is_some(&self, slot: LogicalSlot) -> bool {
match slot {
LogicalSlot::BlockStart => self.block_start.is_some(),
LogicalSlot::BlockEnd => self.block_end.is_some(),
LogicalSlot::InlineStart => self.inline_start.is_some(),
LogicalSlot::InlineEnd => self.inline_end.is_some(),
}
}
pub fn handle_property(
&mut self,
property: &Property,
dest: &mut DeclarationList,
context: &mut PropertyHandlerContext,
) -> bool {
if let Property::Unparsed(unparsed) = property {
let id = unparsed.property_id.tag();
if id == S::TOP
|| id == S::BOTTOM
|| id == S::LEFT
|| id == S::RIGHT
|| id == S::BLOCK_START
|| id == S::BLOCK_END
|| id == S::INLINE_START
|| id == S::INLINE_END
|| id == S::BLOCK_SHORTHAND
|| id == S::INLINE_SHORTHAND
|| id == S::SHORTHAND
{
let bump = dest.bump();
if id == S::BLOCK_START {
self.logical_property_helper(
LogicalSlot::BlockStart,
Property::Unparsed(unparsed.deep_clone(bump)),
dest,
context,
);
} else if id == S::BLOCK_END {
self.logical_property_helper(
LogicalSlot::BlockEnd,
Property::Unparsed(unparsed.deep_clone(bump)),
dest,
context,
);
} else if id == S::INLINE_START {
self.logical_property_helper(
LogicalSlot::InlineStart,
Property::Unparsed(unparsed.deep_clone(bump)),
dest,
context,
);
} else if id == S::INLINE_END {
self.logical_property_helper(
LogicalSlot::InlineEnd,
Property::Unparsed(unparsed.deep_clone(bump)),
dest,
context,
);
} else {
self.flush(dest, context);
dest.push(Property::Unparsed(unparsed.deep_clone(bump)));
}
} else {
return false;
}
return true;
}
let tag = property.variant_tag();
if tag == S::TOP {
self.property_helper(
PhysicalSlot::Top,
S::extract_top(property),
PropertyCategory::Physical,
dest,
context,
);
} else if tag == S::BOTTOM {
self.property_helper(
PhysicalSlot::Bottom,
S::extract_bottom(property),
PropertyCategory::Physical,
dest,
context,
);
} else if tag == S::LEFT {
self.property_helper(
PhysicalSlot::Left,
S::extract_left(property),
PropertyCategory::Physical,
dest,
context,
);
} else if tag == S::RIGHT {
self.property_helper(
PhysicalSlot::Right,
S::extract_right(property),
PropertyCategory::Physical,
dest,
context,
);
} else if tag == S::BLOCK_START {
self.flush_helper_logical(
LogicalSlot::BlockStart,
S::extract_block_start(property),
PropertyCategory::Logical,
dest,
context,
);
self.logical_property_helper(
LogicalSlot::BlockStart,
S::make_block_start(S::extract_block_start(property).clone()),
dest,
context,
);
} else if tag == S::BLOCK_END {
self.flush_helper_logical(
LogicalSlot::BlockEnd,
S::extract_block_end(property),
PropertyCategory::Logical,
dest,
context,
);
self.logical_property_helper(
LogicalSlot::BlockEnd,
S::make_block_end(S::extract_block_end(property).clone()),
dest,
context,
);
} else if tag == S::INLINE_START {
self.flush_helper_logical(
LogicalSlot::InlineStart,
S::extract_inline_start(property),
PropertyCategory::Logical,
dest,
context,
);
self.logical_property_helper(
LogicalSlot::InlineStart,
S::make_inline_start(S::extract_inline_start(property).clone()),
dest,
context,
);
} else if tag == S::INLINE_END {
self.flush_helper_logical(
LogicalSlot::InlineEnd,
S::extract_inline_end(property),
PropertyCategory::Logical,
dest,
context,
);
self.logical_property_helper(
LogicalSlot::InlineEnd,
S::make_inline_end(S::extract_inline_end(property).clone()),
dest,
context,
);
} else if tag == S::BLOCK_SHORTHAND {
let val = S::extract_block_shorthand(property);
self.flush_helper_logical(
LogicalSlot::BlockStart,
S::block_shorthand_start(val),
PropertyCategory::Logical,
dest,
context,
);
self.flush_helper_logical(
LogicalSlot::BlockEnd,
S::block_shorthand_end(val),
PropertyCategory::Logical,
dest,
context,
);
self.logical_property_helper(
LogicalSlot::BlockStart,
S::make_block_start(S::block_shorthand_start(val).clone()),
dest,
context,
);
self.logical_property_helper(
LogicalSlot::BlockEnd,
S::make_block_end(S::block_shorthand_end(val).clone()),
dest,
context,
);
} else if tag == S::INLINE_SHORTHAND {
let val = S::extract_inline_shorthand(property);
self.flush_helper_logical(
LogicalSlot::InlineStart,
S::inline_shorthand_start(val),
PropertyCategory::Logical,
dest,
context,
);
self.flush_helper_logical(
LogicalSlot::InlineEnd,
S::inline_shorthand_end(val),
PropertyCategory::Logical,
dest,
context,
);
self.logical_property_helper(
LogicalSlot::InlineStart,
S::make_inline_start(S::inline_shorthand_start(val).clone()),
dest,
context,
);
self.logical_property_helper(
LogicalSlot::InlineEnd,
S::make_inline_end(S::inline_shorthand_end(val).clone()),
dest,
context,
);
} else if tag == S::SHORTHAND {
let val = S::extract_shorthand(property);
self.flush_helper_physical(
PhysicalSlot::Top,
S::shorthand_top(val),
S::SHORTHAND_CATEGORY,
dest,
context,
);
self.flush_helper_physical(
PhysicalSlot::Right,
S::shorthand_right(val),
S::SHORTHAND_CATEGORY,
dest,
context,
);
self.flush_helper_physical(
PhysicalSlot::Bottom,
S::shorthand_bottom(val),
S::SHORTHAND_CATEGORY,
dest,
context,
);
self.flush_helper_physical(
PhysicalSlot::Left,
S::shorthand_left(val),
S::SHORTHAND_CATEGORY,
dest,
context,
);
self.top = Some(S::shorthand_top(val).clone());
self.right = Some(S::shorthand_right(val).clone());
self.bottom = Some(S::shorthand_bottom(val).clone());
self.left = Some(S::shorthand_left(val).clone());
self.block_start = None;
self.block_end = None;
self.inline_start = None;
self.inline_end = None;
self.has_any = true;
} else {
return false;
}
true
}
pub fn finalize(&mut self, dest: &mut DeclarationList, context: &mut PropertyHandlerContext) {
self.flush(dest, context);
}
fn flush_helper_physical(
&mut self,
field: PhysicalSlot,
val: &LengthPercentageOrAuto,
category: PropertyCategory,
dest: &mut DeclarationList,
context: &mut PropertyHandlerContext,
) {
if category != self.category
|| (self.physical_slot_is_some(field)
&& context.targets.browsers.is_some()
&& !val.is_compatible(&context.targets.browsers.unwrap()))
{
self.flush(dest, context);
}
}
fn flush_helper_logical(
&mut self,
field: LogicalSlot,
val: &LengthPercentageOrAuto,
category: PropertyCategory,
dest: &mut DeclarationList,
context: &mut PropertyHandlerContext,
) {
if category != self.category
|| (self.logical_slot_is_some(field)
&& context.targets.browsers.is_some()
&& !val.is_compatible(&context.targets.browsers.unwrap()))
{
self.flush(dest, context);
}
}
fn property_helper(
&mut self,
field: PhysicalSlot,
val: &LengthPercentageOrAuto,
category: PropertyCategory,
dest: &mut DeclarationList,
context: &mut PropertyHandlerContext,
) {
self.flush_helper_physical(field, val, category, dest, context);
*self.physical_slot(field) = Some(val.clone());
self.category = category;
self.has_any = true;
}
fn logical_property_helper(
&mut self,
field: LogicalSlot,
val: Property,
dest: &mut DeclarationList,
context: &mut PropertyHandlerContext,
) {
if self.category != PropertyCategory::Logical
|| (self.logical_slot_is_some(field) && matches!(val, Property::Unparsed(_)))
{
self.flush(dest, context);
}
*self.logical_slot(field) = Some(val);
self.category = PropertyCategory::Logical;
self.has_any = true;
}
fn flush(&mut self, dest: &mut DeclarationList, context: &mut PropertyHandlerContext) {
if !self.has_any {
return;
}
self.has_any = false;
let top = self.top.take();
let bottom = self.bottom.take();
let left = self.left.take();
let right = self.right.take();
let logical_supported = match S::FEATURE {
Some(feature) => !context.should_compile_logical(feature),
None => true,
};
match (top, bottom, left, right) {
(Some(top), Some(bottom), Some(left), Some(right))
if S::SHORTHAND_CATEGORY != PropertyCategory::Logical || logical_supported =>
{
dest.push(S::make_shorthand(top, bottom, left, right));
}
(top, bottom, left, right) => {
if let Some(t) = top {
dest.push(S::make_top(t));
}
if let Some(b) = bottom {
dest.push(S::make_bottom(b));
}
if let Some(b) = left {
dest.push(S::make_left(b));
}
if let Some(b) = right {
dest.push(S::make_right(b));
}
}
}
let mut block_start = self.block_start.take();
let mut block_end = self.block_end.take();
let mut inline_start = self.inline_start.take();
let mut inline_end = self.inline_end.take();
if logical_supported {
Self::logical_side_helper(
&mut block_start,
&mut block_end,
LogicalSidePair::Block,
logical_supported,
dest,
context,
);
} else {
Self::prop(
&mut block_start,
S::BLOCK_START,
S::extract_block_start,
S::make_top,
S::TOP_ID,
dest,
context,
);
Self::prop(
&mut block_end,
S::BLOCK_END,
S::extract_block_end,
S::make_bottom,
S::BOTTOM_ID,
dest,
context,
);
}
if logical_supported {
Self::logical_side_helper(
&mut inline_start,
&mut inline_end,
LogicalSidePair::Inline,
logical_supported,
dest,
context,
);
} else if inline_start.is_some() || inline_end.is_some() {
let start_matches = inline_start
.as_ref()
.map(|p| p.variant_tag() == S::INLINE_START)
.unwrap_or(false);
let end_matches = inline_end
.as_ref()
.map(|p| p.variant_tag() == S::INLINE_END)
.unwrap_or(false);
let values_equal = if start_matches && end_matches {
S::extract_inline_start(inline_start.as_ref().unwrap())
== S::extract_inline_end(inline_end.as_ref().unwrap())
} else {
false
};
if start_matches && end_matches && values_equal {
Self::prop(
&mut inline_start,
S::INLINE_START,
S::extract_inline_start,
S::make_left,
S::LEFT_ID,
dest,
context,
);
Self::prop(
&mut inline_end,
S::INLINE_END,
S::extract_inline_end,
S::make_right,
S::RIGHT_ID,
dest,
context,
);
} else {
Self::logical_prop_helper(
&mut inline_start,
S::INLINE_START,
S::extract_inline_start,
S::make_left,
S::LEFT_ID,
S::make_right,
S::RIGHT_ID,
dest,
context,
);
Self::logical_prop_helper(
&mut inline_end,
S::INLINE_END,
S::extract_inline_end,
S::make_right,
S::RIGHT_ID,
S::make_left,
S::LEFT_ID,
dest,
context,
);
}
}
}
#[inline]
#[allow(clippy::too_many_arguments)]
fn logical_prop_helper(
val: &mut Option<Property>,
logical: PropertyIdTag,
extract_logical: fn(&Property) -> &LengthPercentageOrAuto,
make_ltr: fn(LengthPercentageOrAuto) -> Property,
ltr: PropertyId,
make_rtl: fn(LengthPercentageOrAuto) -> Property,
rtl: PropertyId,
dest: &mut DeclarationList,
context: &mut PropertyHandlerContext,
) {
let bump = dest.bump();
if let Some(v_) = val.as_ref() {
if v_.variant_tag() == logical {
let v = extract_logical(v_);
context.add_logical_rule(make_ltr(v.clone()), make_rtl(v.clone()));
} else if let Property::Unparsed(v) = v_ {
context.add_logical_rule(
Property::Unparsed(v.with_property_id(bump, ltr)),
Property::Unparsed(v.with_property_id(bump, rtl)),
);
}
}
}
#[inline]
fn logical_side_helper(
start: &mut Option<Property>,
end: &mut Option<Property>,
pair: LogicalSidePair,
logical_supported: bool,
dest: &mut DeclarationList,
context: &mut PropertyHandlerContext,
) {
let shorthand_supported = logical_supported
&& match S::SHORTHAND_FEATURE {
Some(f) => !context.should_compile_logical(f),
None => true,
};
let (start_prop, end_prop) = match pair {
LogicalSidePair::Block => (S::BLOCK_START, S::BLOCK_END),
LogicalSidePair::Inline => (S::INLINE_START, S::INLINE_END),
};
if start
.as_ref()
.map(|p| p.variant_tag() == start_prop)
.unwrap_or(false)
&& end
.as_ref()
.map(|p| p.variant_tag() == end_prop)
.unwrap_or(false)
&& shorthand_supported
{
let start_v = match pair {
LogicalSidePair::Block => S::extract_block_start(start.as_ref().unwrap()).clone(),
LogicalSidePair::Inline => S::extract_inline_start(start.as_ref().unwrap()).clone(),
};
let end_v = match pair {
LogicalSidePair::Block => S::extract_block_end(end.as_ref().unwrap()).clone(),
LogicalSidePair::Inline => S::extract_inline_end(end.as_ref().unwrap()).clone(),
};
let prop = match pair {
LogicalSidePair::Block => S::make_block_shorthand(start_v, end_v),
LogicalSidePair::Inline => S::make_inline_shorthand(start_v, end_v),
};
dest.push(prop);
} else {
if let Some(s) = start.take() {
dest.push(s);
}
if let Some(e) = end.take() {
dest.push(e);
}
}
}
#[inline]
fn prop(
val: &mut Option<Property>,
logical: PropertyIdTag,
extract_logical: fn(&Property) -> &LengthPercentageOrAuto,
make_physical: fn(LengthPercentageOrAuto) -> Property,
physical: PropertyId,
dest: &mut DeclarationList,
context: &mut PropertyHandlerContext,
) {
let _ = context;
let bump = dest.bump();
if let Some(v) = val.as_ref() {
if v.variant_tag() == logical {
dest.push(make_physical(extract_logical(v).clone()));
} else if let Property::Unparsed(u) = v {
dest.push(Property::Unparsed(u.with_property_id(bump, physical)));
}
}
}
}
#[derive(Copy, Clone)]
enum LogicalSidePair {
Block,
Inline,
}
macro_rules! size_handler_spec_projections {
(
$Top:ident, $Bottom:ident, $Left:ident, $Right:ident,
$BlockStart:ident, $BlockEnd:ident, $InlineStart:ident, $InlineEnd:ident,
$Shorthand:ident, $BlockShorthand:ident, $InlineShorthand:ident,
$ShorthandTy:ident, $BlockShorthandTy:ident, $InlineShorthandTy:ident
) => {
const TOP_ID: PropertyId = PropertyId::$Top;
const BOTTOM_ID: PropertyId = PropertyId::$Bottom;
const LEFT_ID: PropertyId = PropertyId::$Left;
const RIGHT_ID: PropertyId = PropertyId::$Right;
fn extract_top(p: &Property) -> &LengthPercentageOrAuto {
match p {
Property::$Top(v) => v,
_ => unreachable!(),
}
}
fn extract_bottom(p: &Property) -> &LengthPercentageOrAuto {
match p {
Property::$Bottom(v) => v,
_ => unreachable!(),
}
}
fn extract_left(p: &Property) -> &LengthPercentageOrAuto {
match p {
Property::$Left(v) => v,
_ => unreachable!(),
}
}
fn extract_right(p: &Property) -> &LengthPercentageOrAuto {
match p {
Property::$Right(v) => v,
_ => unreachable!(),
}
}
fn extract_block_start(p: &Property) -> &LengthPercentageOrAuto {
match p {
Property::$BlockStart(v) => v,
_ => unreachable!(),
}
}
fn extract_block_end(p: &Property) -> &LengthPercentageOrAuto {
match p {
Property::$BlockEnd(v) => v,
_ => unreachable!(),
}
}
fn extract_inline_start(p: &Property) -> &LengthPercentageOrAuto {
match p {
Property::$InlineStart(v) => v,
_ => unreachable!(),
}
}
fn extract_inline_end(p: &Property) -> &LengthPercentageOrAuto {
match p {
Property::$InlineEnd(v) => v,
_ => unreachable!(),
}
}
fn extract_shorthand(p: &Property) -> &Self::Shorthand {
match p {
Property::$Shorthand(v) => v,
_ => unreachable!(),
}
}
fn extract_block_shorthand(p: &Property) -> &Self::BlockShorthand {
match p {
Property::$BlockShorthand(v) => v,
_ => unreachable!(),
}
}
fn extract_inline_shorthand(p: &Property) -> &Self::InlineShorthand {
match p {
Property::$InlineShorthand(v) => v,
_ => unreachable!(),
}
}
fn make_top(v: LengthPercentageOrAuto) -> Property {
Property::$Top(v)
}
fn make_bottom(v: LengthPercentageOrAuto) -> Property {
Property::$Bottom(v)
}
fn make_left(v: LengthPercentageOrAuto) -> Property {
Property::$Left(v)
}
fn make_right(v: LengthPercentageOrAuto) -> Property {
Property::$Right(v)
}
fn make_block_start(v: LengthPercentageOrAuto) -> Property {
Property::$BlockStart(v)
}
fn make_block_end(v: LengthPercentageOrAuto) -> Property {
Property::$BlockEnd(v)
}
fn make_inline_start(v: LengthPercentageOrAuto) -> Property {
Property::$InlineStart(v)
}
fn make_inline_end(v: LengthPercentageOrAuto) -> Property {
Property::$InlineEnd(v)
}
fn make_shorthand(
top: LengthPercentageOrAuto,
bottom: LengthPercentageOrAuto,
left: LengthPercentageOrAuto,
right: LengthPercentageOrAuto,
) -> Property {
Property::$Shorthand($ShorthandTy {
top,
right,
bottom,
left,
})
}
fn make_block_shorthand(s: LengthPercentageOrAuto, e: LengthPercentageOrAuto) -> Property {
Property::$BlockShorthand($BlockShorthandTy {
block_start: s,
block_end: e,
})
}
fn make_inline_shorthand(s: LengthPercentageOrAuto, e: LengthPercentageOrAuto) -> Property {
Property::$InlineShorthand($InlineShorthandTy {
inline_start: s,
inline_end: e,
})
}
fn shorthand_top(v: &Self::Shorthand) -> &LengthPercentageOrAuto {
&v.top
}
fn shorthand_right(v: &Self::Shorthand) -> &LengthPercentageOrAuto {
&v.right
}
fn shorthand_bottom(v: &Self::Shorthand) -> &LengthPercentageOrAuto {
&v.bottom
}
fn shorthand_left(v: &Self::Shorthand) -> &LengthPercentageOrAuto {
&v.left
}
fn block_shorthand_start(v: &Self::BlockShorthand) -> &LengthPercentageOrAuto {
&v.block_start
}
fn block_shorthand_end(v: &Self::BlockShorthand) -> &LengthPercentageOrAuto {
&v.block_end
}
fn inline_shorthand_start(v: &Self::InlineShorthand) -> &LengthPercentageOrAuto {
&v.inline_start
}
fn inline_shorthand_end(v: &Self::InlineShorthand) -> &LengthPercentageOrAuto {
&v.inline_end
}
};
}
pub struct MarginSpec;
impl SizeHandlerSpec for MarginSpec {
const TOP: PropertyIdTag = PropertyIdTag::MarginTop;
const BOTTOM: PropertyIdTag = PropertyIdTag::MarginBottom;
const LEFT: PropertyIdTag = PropertyIdTag::MarginLeft;
const RIGHT: PropertyIdTag = PropertyIdTag::MarginRight;
const BLOCK_START: PropertyIdTag = PropertyIdTag::MarginBlockStart;
const BLOCK_END: PropertyIdTag = PropertyIdTag::MarginBlockEnd;
const INLINE_START: PropertyIdTag = PropertyIdTag::MarginInlineStart;
const INLINE_END: PropertyIdTag = PropertyIdTag::MarginInlineEnd;
const SHORTHAND: PropertyIdTag = PropertyIdTag::Margin;
const BLOCK_SHORTHAND: PropertyIdTag = PropertyIdTag::MarginBlock;
const INLINE_SHORTHAND: PropertyIdTag = PropertyIdTag::MarginInline;
const SHORTHAND_CATEGORY: PropertyCategory = PropertyCategory::Physical;
const FEATURE: Option<Feature> = Some(Feature::LogicalMargin);
const SHORTHAND_FEATURE: Option<Feature> = Some(Feature::LogicalMarginShorthand);
type Shorthand = Margin;
type BlockShorthand = MarginBlock;
type InlineShorthand = MarginInline;
size_handler_spec_projections!(
MarginTop,
MarginBottom,
MarginLeft,
MarginRight,
MarginBlockStart,
MarginBlockEnd,
MarginInlineStart,
MarginInlineEnd,
Margin,
MarginBlock,
MarginInline,
Margin,
MarginBlock,
MarginInline
);
}
pub struct PaddingSpec;
impl SizeHandlerSpec for PaddingSpec {
const TOP: PropertyIdTag = PropertyIdTag::PaddingTop;
const BOTTOM: PropertyIdTag = PropertyIdTag::PaddingBottom;
const LEFT: PropertyIdTag = PropertyIdTag::PaddingLeft;
const RIGHT: PropertyIdTag = PropertyIdTag::PaddingRight;
const BLOCK_START: PropertyIdTag = PropertyIdTag::PaddingBlockStart;
const BLOCK_END: PropertyIdTag = PropertyIdTag::PaddingBlockEnd;
const INLINE_START: PropertyIdTag = PropertyIdTag::PaddingInlineStart;
const INLINE_END: PropertyIdTag = PropertyIdTag::PaddingInlineEnd;
const SHORTHAND: PropertyIdTag = PropertyIdTag::Padding;
const BLOCK_SHORTHAND: PropertyIdTag = PropertyIdTag::PaddingBlock;
const INLINE_SHORTHAND: PropertyIdTag = PropertyIdTag::PaddingInline;
const SHORTHAND_CATEGORY: PropertyCategory = PropertyCategory::Physical;
const FEATURE: Option<Feature> = Some(Feature::LogicalPadding);
const SHORTHAND_FEATURE: Option<Feature> = Some(Feature::LogicalPaddingShorthand);
type Shorthand = Padding;
type BlockShorthand = PaddingBlock;
type InlineShorthand = PaddingInline;
size_handler_spec_projections!(
PaddingTop,
PaddingBottom,
PaddingLeft,
PaddingRight,
PaddingBlockStart,
PaddingBlockEnd,
PaddingInlineStart,
PaddingInlineEnd,
Padding,
PaddingBlock,
PaddingInline,
Padding,
PaddingBlock,
PaddingInline
);
}
pub struct ScrollMarginSpec;
impl SizeHandlerSpec for ScrollMarginSpec {
const TOP: PropertyIdTag = PropertyIdTag::ScrollMarginTop;
const BOTTOM: PropertyIdTag = PropertyIdTag::ScrollMarginBottom;
const LEFT: PropertyIdTag = PropertyIdTag::ScrollMarginLeft;
const RIGHT: PropertyIdTag = PropertyIdTag::ScrollMarginRight;
const BLOCK_START: PropertyIdTag = PropertyIdTag::ScrollMarginBlockStart;
const BLOCK_END: PropertyIdTag = PropertyIdTag::ScrollMarginBlockEnd;
const INLINE_START: PropertyIdTag = PropertyIdTag::ScrollMarginInlineStart;
const INLINE_END: PropertyIdTag = PropertyIdTag::ScrollMarginInlineEnd;
const SHORTHAND: PropertyIdTag = PropertyIdTag::ScrollMargin;
const BLOCK_SHORTHAND: PropertyIdTag = PropertyIdTag::ScrollMarginBlock;
const INLINE_SHORTHAND: PropertyIdTag = PropertyIdTag::ScrollMarginInline;
const SHORTHAND_CATEGORY: PropertyCategory = PropertyCategory::Physical;
const FEATURE: Option<Feature> = None;
const SHORTHAND_FEATURE: Option<Feature> = None;
type Shorthand = ScrollMargin;
type BlockShorthand = ScrollMarginBlock;
type InlineShorthand = ScrollMarginInline;
size_handler_spec_projections!(
ScrollMarginTop,
ScrollMarginBottom,
ScrollMarginLeft,
ScrollMarginRight,
ScrollMarginBlockStart,
ScrollMarginBlockEnd,
ScrollMarginInlineStart,
ScrollMarginInlineEnd,
ScrollMargin,
ScrollMarginBlock,
ScrollMarginInline,
ScrollMargin,
ScrollMarginBlock,
ScrollMarginInline
);
}
pub struct InsetSpec;
impl SizeHandlerSpec for InsetSpec {
const TOP: PropertyIdTag = PropertyIdTag::Top;
const BOTTOM: PropertyIdTag = PropertyIdTag::Bottom;
const LEFT: PropertyIdTag = PropertyIdTag::Left;
const RIGHT: PropertyIdTag = PropertyIdTag::Right;
const BLOCK_START: PropertyIdTag = PropertyIdTag::InsetBlockStart;
const BLOCK_END: PropertyIdTag = PropertyIdTag::InsetBlockEnd;
const INLINE_START: PropertyIdTag = PropertyIdTag::InsetInlineStart;
const INLINE_END: PropertyIdTag = PropertyIdTag::InsetInlineEnd;
const SHORTHAND: PropertyIdTag = PropertyIdTag::Inset;
const BLOCK_SHORTHAND: PropertyIdTag = PropertyIdTag::InsetBlock;
const INLINE_SHORTHAND: PropertyIdTag = PropertyIdTag::InsetInline;
const SHORTHAND_CATEGORY: PropertyCategory = PropertyCategory::Physical;
const FEATURE: Option<Feature> = Some(Feature::LogicalInset);
const SHORTHAND_FEATURE: Option<Feature> = Some(Feature::LogicalInset);
type Shorthand = Inset;
type BlockShorthand = InsetBlock;
type InlineShorthand = InsetInline;
size_handler_spec_projections!(
Top,
Bottom,
Left,
Right,
InsetBlockStart,
InsetBlockEnd,
InsetInlineStart,
InsetInlineEnd,
Inset,
InsetBlock,
InsetInline,
Inset,
InsetBlock,
InsetInline
);
}