#![warn(unused_must_use)]
use crate as css;
use crate::compat;
use crate::css_values::length::LengthPercentage;
use crate::prefixes::Feature;
use crate::properties::{Property, PropertyId};
use crate::{DeclarationList, PropertyHandlerContext, VendorPrefix};
use crate::{Parser, PrintErr, Printer, Result as CssResult, Token};
use bun_alloc::ArenaVecExt as _;
use crate::css_properties::flex::{
BoxAlign, BoxPack, FlexAlign, FlexItemAlign, FlexLinePack, FlexPack,
};
#[derive(Clone, PartialEq, Eq)]
#[derive(css::Parse, css::ToCss)]
pub enum AlignContent {
Normal,
BaselinePosition(BaselinePosition),
ContentDistribution(ContentDistribution),
ContentPosition(AlignContentContentPosition),
}
#[derive(Clone, PartialEq, Eq, css::ToCss)]
#[css(generate_to_css)]
pub struct AlignContentContentPosition {
pub overflow: Option<OverflowPosition>,
pub value: ContentPosition,
}
impl AlignContentContentPosition {
pub(crate) fn parse(input: &mut Parser) -> CssResult<Self> {
let overflow = input.try_parse(OverflowPosition::parse).ok();
let value = ContentPosition::parse(input)?;
Ok(Self { overflow, value })
}
pub(crate) fn to_inner(&self) -> ContentPositionInner {
ContentPositionInner {
overflow: self.overflow,
value: self.value,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum BaselinePosition {
First,
Last,
}
impl BaselinePosition {
pub(crate) fn parse(input: &mut Parser) -> CssResult<Self> {
let location = input.current_source_location();
let ident = input.expect_ident_cloned()?;
crate::match_ignore_ascii_case! { ident, {
b"baseline" => Ok(BaselinePosition::First),
b"first" => {
input.expect_ident_matching(b"baseline")?;
Ok(BaselinePosition::First)
},
b"last" => {
input.expect_ident_matching(b"baseline")?;
Ok(BaselinePosition::Last)
},
_ => Err(location.new_unexpected_token_error(Token::Ident(ident))),
}}
}
pub(crate) fn to_css(self, dest: &mut Printer) -> Result<(), PrintErr> {
match self {
BaselinePosition::First => dest.write_str("baseline"),
BaselinePosition::Last => dest.write_str("last baseline"),
}
}
}
#[derive(Clone, PartialEq, Eq)]
pub enum JustifyContent {
Normal,
ContentDistribution(ContentDistribution),
ContentPosition(JustifyContentContentPosition),
Left {
overflow: Option<OverflowPosition>,
},
Right {
overflow: Option<OverflowPosition>,
},
}
#[derive(Clone, PartialEq, Eq)]
pub struct JustifyContentContentPosition {
pub value: ContentPosition,
pub overflow: Option<OverflowPosition>,
}
impl JustifyContentContentPosition {
pub(crate) fn to_inner(&self) -> ContentPositionInner {
ContentPositionInner {
overflow: self.overflow,
value: self.value,
}
}
}
impl JustifyContent {
pub(crate) fn parse(input: &mut Parser) -> CssResult<Self> {
if input
.try_parse(|i| i.expect_ident_matching(b"normal"))
.is_ok()
{
return Ok(JustifyContent::Normal);
}
if let Ok(val) = input.try_parse(ContentDistribution::parse) {
return Ok(JustifyContent::ContentDistribution(val));
}
let overflow = input.try_parse(OverflowPosition::parse).ok();
if let Ok(content_position) = input.try_parse(ContentPosition::parse) {
return Ok(JustifyContent::ContentPosition(
JustifyContentContentPosition {
overflow,
value: content_position,
},
));
}
let location = input.current_source_location();
let ident = input.expect_ident_cloned()?;
crate::match_ignore_ascii_case! { ident, {
b"left" => Ok(JustifyContent::Left { overflow }),
b"right" => Ok(JustifyContent::Right { overflow }),
_ => Err(location.new_unexpected_token_error(Token::Ident(ident))),
}}
}
pub(crate) fn to_css(&self, dest: &mut Printer) -> Result<(), PrintErr> {
match self {
JustifyContent::Normal => dest.write_str("normal"),
JustifyContent::ContentDistribution(value) => value.to_css(dest),
JustifyContent::ContentPosition(cp) => {
if let Some(overflow) = &cp.overflow {
overflow.to_css(dest)?;
dest.write_str(" ")?;
}
cp.value.to_css(dest)
}
JustifyContent::Left { overflow } => {
if let Some(overflow) = overflow {
overflow.to_css(dest)?;
dest.write_str(" ")?;
}
dest.write_str("left")
}
JustifyContent::Right { overflow } => {
if let Some(overflow) = overflow {
overflow.to_css(dest)?;
dest.write_str(" ")?;
}
dest.write_str("right")
}
}
}
}
#[derive(Clone, PartialEq, Eq)]
#[derive(css::Parse, css::ToCss)]
pub enum AlignSelf {
Auto,
Normal,
Stretch,
BaselinePosition(BaselinePosition),
SelfPosition(AlignSelfSelfPosition),
}
#[derive(Clone, PartialEq, Eq, css::ToCss)]
#[css(generate_to_css)]
pub struct AlignSelfSelfPosition {
pub overflow: Option<OverflowPosition>,
pub value: SelfPosition,
}
impl AlignSelfSelfPosition {
pub(crate) fn to_inner(&self) -> SelfPositionInner {
SelfPositionInner {
overflow: self.overflow,
value: self.value,
}
}
pub(crate) fn parse(input: &mut Parser) -> CssResult<Self> {
let overflow = input.try_parse(OverflowPosition::parse).ok();
let self_position = SelfPosition::parse(input)?;
Ok(Self {
overflow,
value: self_position,
})
}
}
#[derive(Clone, PartialEq, Eq)]
pub enum JustifySelf {
Auto,
Normal,
Stretch,
BaselinePosition(BaselinePosition),
SelfPosition(JustifySelfSelfPosition),
Left {
overflow: Option<OverflowPosition>,
},
Right {
overflow: Option<OverflowPosition>,
},
}
#[derive(Clone, PartialEq, Eq)]
pub struct JustifySelfSelfPosition {
pub value: SelfPosition,
pub overflow: Option<OverflowPosition>,
}
impl JustifySelfSelfPosition {
pub(crate) fn to_inner(&self) -> SelfPositionInner {
SelfPositionInner {
overflow: self.overflow,
value: self.value,
}
}
}
impl JustifySelf {
pub(crate) fn parse(input: &mut Parser) -> CssResult<Self> {
if input
.try_parse(|i| i.expect_ident_matching(b"auto"))
.is_ok()
{
return Ok(JustifySelf::Auto);
}
if input
.try_parse(|i| i.expect_ident_matching(b"normal"))
.is_ok()
{
return Ok(JustifySelf::Normal);
}
if input
.try_parse(|i| i.expect_ident_matching(b"stretch"))
.is_ok()
{
return Ok(JustifySelf::Stretch);
}
if let Ok(val) = input.try_parse(BaselinePosition::parse) {
return Ok(JustifySelf::BaselinePosition(val));
}
let overflow = input.try_parse(OverflowPosition::parse).ok();
if let Ok(self_position) = input.try_parse(SelfPosition::parse) {
return Ok(JustifySelf::SelfPosition(JustifySelfSelfPosition {
overflow,
value: self_position,
}));
}
let location = input.current_source_location();
let ident = input.expect_ident_cloned()?;
crate::match_ignore_ascii_case! { ident, {
b"left" => Ok(JustifySelf::Left { overflow }),
b"right" => Ok(JustifySelf::Right { overflow }),
_ => Err(location.new_unexpected_token_error(Token::Ident(ident))),
}}
}
pub(crate) fn to_css(&self, dest: &mut Printer) -> Result<(), PrintErr> {
match self {
JustifySelf::Auto => dest.write_str("auto"),
JustifySelf::Normal => dest.write_str("normal"),
JustifySelf::Stretch => dest.write_str("stretch"),
JustifySelf::BaselinePosition(baseline_position) => baseline_position.to_css(dest),
JustifySelf::SelfPosition(self_position) => {
if let Some(overflow) = &self_position.overflow {
overflow.to_css(dest)?;
dest.write_str(" ")?;
}
self_position.value.to_css(dest)
}
JustifySelf::Left { overflow } => {
if let Some(overflow) = overflow {
overflow.to_css(dest)?;
dest.write_str(" ")?;
}
dest.write_str("left")
}
JustifySelf::Right { overflow } => {
if let Some(overflow) = overflow {
overflow.to_css(dest)?;
dest.write_str(" ")?;
}
dest.write_str("right")
}
}
}
}
#[derive(Clone, PartialEq, Eq)]
#[derive(css::Parse, css::ToCss)]
pub enum AlignItems {
Normal,
Stretch,
BaselinePosition(BaselinePosition),
SelfPosition(AlignItemsSelfPosition),
}
#[derive(Clone, PartialEq, Eq, css::ToCss)]
#[css(generate_to_css)]
pub struct AlignItemsSelfPosition {
pub overflow: Option<OverflowPosition>,
pub value: SelfPosition,
}
impl AlignItemsSelfPosition {
pub(crate) fn to_inner(&self) -> SelfPositionInner {
SelfPositionInner {
overflow: self.overflow,
value: self.value,
}
}
pub(crate) fn parse(input: &mut Parser) -> CssResult<Self> {
let overflow = input.try_parse(OverflowPosition::parse).ok();
let self_position = SelfPosition::parse(input)?;
Ok(Self {
overflow,
value: self_position,
})
}
}
#[derive(Clone, PartialEq, Eq)]
pub enum JustifyItems {
Normal,
Stretch,
BaselinePosition(BaselinePosition),
SelfPosition(JustifyItemsSelfPosition),
Left {
overflow: Option<OverflowPosition>,
},
Right {
overflow: Option<OverflowPosition>,
},
Legacy(LegacyJustify),
}
#[derive(Clone, PartialEq, Eq)]
pub struct JustifyItemsSelfPosition {
pub value: SelfPosition,
pub overflow: Option<OverflowPosition>,
}
impl JustifyItemsSelfPosition {
pub(crate) fn to_inner(&self) -> SelfPositionInner {
SelfPositionInner {
overflow: self.overflow,
value: self.value,
}
}
}
impl JustifyItems {
pub(crate) fn parse(input: &mut Parser) -> CssResult<Self> {
if input
.try_parse(|i| i.expect_ident_matching(b"normal"))
.is_ok()
{
return Ok(JustifyItems::Normal);
}
if input
.try_parse(|i| i.expect_ident_matching(b"stretch"))
.is_ok()
{
return Ok(JustifyItems::Stretch);
}
if let Ok(val) = input.try_parse(BaselinePosition::parse) {
return Ok(JustifyItems::BaselinePosition(val));
}
if let Ok(val) = input.try_parse(LegacyJustify::parse) {
return Ok(JustifyItems::Legacy(val));
}
let overflow = input.try_parse(OverflowPosition::parse).ok();
if let Ok(self_position) = input.try_parse(SelfPosition::parse) {
return Ok(JustifyItems::SelfPosition(JustifyItemsSelfPosition {
overflow,
value: self_position,
}));
}
let location = input.current_source_location();
let ident = input.expect_ident_cloned()?;
crate::match_ignore_ascii_case! { ident, {
b"left" => Ok(JustifyItems::Left { overflow }),
b"right" => Ok(JustifyItems::Right { overflow }),
_ => Err(location.new_unexpected_token_error(Token::Ident(ident))),
}}
}
pub(crate) fn to_css(&self, dest: &mut Printer) -> Result<(), PrintErr> {
match self {
JustifyItems::Normal => dest.write_str("normal"),
JustifyItems::Stretch => dest.write_str("stretch"),
JustifyItems::BaselinePosition(val) => val.to_css(dest),
JustifyItems::SelfPosition(sp) => {
if let Some(overflow) = &sp.overflow {
overflow.to_css(dest)?;
dest.write_str(" ")?;
}
sp.value.to_css(dest)
}
JustifyItems::Left { overflow } => {
if let Some(overflow) = overflow {
overflow.to_css(dest)?;
dest.write_str(" ")?;
}
dest.write_str("left")
}
JustifyItems::Right { overflow } => {
if let Some(overflow) = overflow {
overflow.to_css(dest)?;
dest.write_str(" ")?;
}
dest.write_str("right")
}
JustifyItems::Legacy(l) => l.to_css(dest),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum LegacyJustify {
Left,
Right,
Center,
}
impl LegacyJustify {
pub(crate) fn parse(input: &mut Parser) -> CssResult<Self> {
let location = input.current_source_location();
let ident = input.expect_ident_cloned()?;
crate::match_ignore_ascii_case! { ident, {
b"legacy" => {
let inner_location = input.current_source_location();
let inner_ident = input.expect_ident_cloned()?;
crate::match_ignore_ascii_case! { inner_ident, {
b"left" => Ok(LegacyJustify::Left),
b"right" => Ok(LegacyJustify::Right),
b"center" => Ok(LegacyJustify::Center),
_ => Err(inner_location.new_unexpected_token_error(Token::Ident(inner_ident))),
}}
},
b"left" => {
input.expect_ident_matching(b"legacy")?;
Ok(LegacyJustify::Left)
},
b"right" => {
input.expect_ident_matching(b"legacy")?;
Ok(LegacyJustify::Right)
},
b"center" => {
input.expect_ident_matching(b"legacy")?;
Ok(LegacyJustify::Center)
},
_ => Err(location.new_unexpected_token_error(Token::Ident(ident))),
}}
}
pub(crate) fn to_css(self, dest: &mut Printer) -> Result<(), PrintErr> {
dest.write_str("legacy ")?;
match self {
LegacyJustify::Left => dest.write_str("left"),
LegacyJustify::Right => dest.write_str("right"),
LegacyJustify::Center => dest.write_str("center"),
}
}
}
#[derive(Clone, PartialEq)]
#[derive(css::Parse, css::ToCss)]
pub enum GapValue {
Normal,
LengthPercentage(LengthPercentage),
}
#[derive(Clone, PartialEq)]
pub struct Gap {
pub row: GapValue,
pub column: GapValue,
}
impl Gap {
pub(crate) fn parse(input: &mut Parser) -> CssResult<Self> {
let row = GapValue::parse(input)?;
let column = input
.try_parse(GapValue::parse)
.unwrap_or_else(|_| row.clone());
Ok(Self { row, column })
}
pub(crate) fn to_css(&self, dest: &mut Printer) -> Result<(), PrintErr> {
self.row.to_css(dest)?;
if self.column != self.row {
dest.write_str(" ")?;
self.column.to_css(dest)?;
}
Ok(())
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct PlaceItems {
pub align: AlignItems,
pub justify: JustifyItems,
}
impl PlaceItems {
pub(crate) fn parse(input: &mut Parser) -> CssResult<Self> {
let align = AlignItems::parse(input)?;
let justify = match input.try_parse(JustifyItems::parse) {
Ok(v) => v,
Err(_) => match &align {
AlignItems::Normal => JustifyItems::Normal,
AlignItems::Stretch => JustifyItems::Stretch,
AlignItems::BaselinePosition(p) => JustifyItems::BaselinePosition(*p),
AlignItems::SelfPosition(sp) => {
JustifyItems::SelfPosition(JustifyItemsSelfPosition {
overflow: sp.overflow,
value: sp.value,
})
}
},
};
Ok(Self { align, justify })
}
pub(crate) fn to_css(&self, dest: &mut Printer) -> Result<(), PrintErr> {
self.align.to_css(dest)?;
let is_equal = match &self.justify {
JustifyItems::Normal => self.align == AlignItems::Normal,
JustifyItems::Stretch => self.align == AlignItems::Stretch,
JustifyItems::BaselinePosition(p) => 'brk: {
if let AlignItems::BaselinePosition(ap) = &self.align {
break 'brk p == ap;
}
false
}
JustifyItems::SelfPosition(p) => 'brk: {
if let AlignItems::SelfPosition(ap) = &self.align {
break 'brk p.to_inner() == ap.to_inner();
}
false
}
_ => false,
};
if !is_equal {
dest.write_str(" ")?;
self.justify.to_css(dest)?;
}
Ok(())
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct PlaceSelf {
pub align: AlignSelf,
pub justify: JustifySelf,
}
impl PlaceSelf {
pub(crate) fn parse(input: &mut Parser) -> CssResult<Self> {
let align = AlignSelf::parse(input)?;
let justify = match input.try_parse(JustifySelf::parse) {
Ok(v) => v,
Err(_) => match &align {
AlignSelf::Auto => JustifySelf::Auto,
AlignSelf::Normal => JustifySelf::Normal,
AlignSelf::Stretch => JustifySelf::Stretch,
AlignSelf::BaselinePosition(p) => JustifySelf::BaselinePosition(*p),
AlignSelf::SelfPosition(sp) => JustifySelf::SelfPosition(JustifySelfSelfPosition {
overflow: sp.overflow,
value: sp.value,
}),
},
};
Ok(Self { align, justify })
}
pub(crate) fn to_css(&self, dest: &mut Printer) -> Result<(), PrintErr> {
self.align.to_css(dest)?;
let is_equal = match &self.justify {
JustifySelf::Auto => true,
JustifySelf::Normal => matches!(self.align, AlignSelf::Normal),
JustifySelf::Stretch => matches!(self.align, AlignSelf::Stretch),
JustifySelf::BaselinePosition(p) => match &self.align {
AlignSelf::BaselinePosition(p2) => p == p2,
_ => false,
},
JustifySelf::SelfPosition(sp) => 'brk: {
if let AlignSelf::SelfPosition(ap) = &self.align {
break 'brk sp.to_inner() == ap.to_inner();
}
false
}
_ => false,
};
if !is_equal {
dest.write_str(" ")?;
self.justify.to_css(dest)?;
}
Ok(())
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[derive(css::DefineEnumProperty)]
pub enum SelfPosition {
#[css(name = "center")]
Center,
#[css(name = "start")]
Start,
#[css(name = "end")]
End,
#[css(name = "self-start")]
SelfStart,
#[css(name = "self-end")]
SelfEnd,
#[css(name = "flex-start")]
FlexStart,
#[css(name = "flex-end")]
FlexEnd,
}
#[derive(Clone, PartialEq, Eq)]
pub struct PlaceContent {
pub align: AlignContent,
pub justify: JustifyContent,
}
impl PlaceContent {
pub(crate) fn parse(input: &mut Parser) -> CssResult<Self> {
let align = AlignContent::parse(input)?;
let justify = match JustifyContent::parse(input) {
Ok(v) => v,
Err(_) => match &align {
AlignContent::BaselinePosition(_) => {
JustifyContent::ContentPosition(JustifyContentContentPosition {
overflow: None,
value: ContentPosition::Start,
})
}
AlignContent::Normal => JustifyContent::Normal,
AlignContent::ContentDistribution(value) => {
JustifyContent::ContentDistribution(*value)
}
AlignContent::ContentPosition(pos) => {
JustifyContent::ContentPosition(JustifyContentContentPosition {
overflow: pos.overflow,
value: pos.value,
})
}
},
};
Ok(Self { align, justify })
}
pub(crate) fn to_css(&self, dest: &mut Printer) -> Result<(), PrintErr> {
self.align.to_css(dest)?;
let is_equal = match &self.justify {
JustifyContent::Normal => 'brk: {
if matches!(self.align, AlignContent::Normal) {
break 'brk true;
}
false
}
JustifyContent::ContentDistribution(d) => 'brk: {
if let AlignContent::ContentDistribution(ad) = &self.align {
break 'brk d == ad;
}
false
}
JustifyContent::ContentPosition(p) => 'brk: {
if let AlignContent::ContentPosition(ap) = &self.align {
break 'brk p.to_inner() == ap.to_inner();
}
false
}
_ => false,
};
if !is_equal {
dest.write_str(" ")?;
self.justify.to_css(dest)?;
}
Ok(())
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[derive(css::DefineEnumProperty)]
pub enum ContentDistribution {
#[css(name = "space-between")]
SpaceBetween,
#[css(name = "space-around")]
SpaceAround,
#[css(name = "space-evenly")]
SpaceEvenly,
#[css(name = "stretch")]
Stretch,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[derive(css::DefineEnumProperty)]
pub enum OverflowPosition {
#[css(name = "safe")]
Safe,
#[css(name = "unsafe")]
Unsafe,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[derive(css::DefineEnumProperty)]
pub enum ContentPosition {
#[css(name = "center")]
Center,
#[css(name = "start")]
Start,
#[css(name = "end")]
End,
#[css(name = "flex-start")]
FlexStart,
#[css(name = "flex-end")]
FlexEnd,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) struct SelfPositionInner {
pub overflow: Option<OverflowPosition>,
pub value: SelfPosition,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) struct ContentPositionInner {
pub overflow: Option<OverflowPosition>,
pub value: ContentPosition,
}
#[derive(Default)]
pub struct AlignHandler {
pub align_content: Option<(AlignContent, VendorPrefix)>,
pub flex_line_pack: Option<(FlexLinePack, VendorPrefix)>,
pub justify_content: Option<(JustifyContent, VendorPrefix)>,
pub box_pack: Option<(BoxPack, VendorPrefix)>,
pub flex_pack: Option<(FlexPack, VendorPrefix)>,
pub align_self: Option<(AlignSelf, VendorPrefix)>,
pub flex_item_align: Option<(FlexItemAlign, VendorPrefix)>,
pub justify_self: Option<JustifySelf>,
pub align_items: Option<(AlignItems, VendorPrefix)>,
pub box_align: Option<(BoxAlign, VendorPrefix)>,
pub flex_align: Option<(FlexAlign, VendorPrefix)>,
pub justify_items: Option<JustifyItems>,
pub row_gap: Option<GapValue>,
pub column_gap: Option<GapValue>,
pub has_any: bool,
}
macro_rules! handle_property_maybe_flush {
($this:expr, $dest:expr, $context:expr, $field:ident, $val:expr, $vp:expr) => {{
if let Some(v) = &$this.$field {
if !($val == &v.0) && !v.1.contains($vp) {
$this.flush($dest, $context);
}
}
}};
}
macro_rules! handle_property_helper {
($this:expr, $dest:expr, $context:expr, $field:ident, $val:expr, $vp:expr) => {{
handle_property_maybe_flush!($this, $dest, $context, $field, $val, $vp);
if let Some(tuple) = &mut $this.$field {
tuple.0 = ($val).clone();
tuple.1.insert($vp);
} else {
$this.$field = Some((($val).clone(), $vp));
$this.has_any = true;
}
}};
}
macro_rules! flush_prefixed_property {
($dest:expr, $context:expr, $variant:ident, $key:expr) => {{
if let Some((val, prefix)) = $key {
$dest.push(Property::$variant((val, prefix)));
}
}};
}
macro_rules! flush_unprefix_property {
($dest:expr, $context:expr, $variant:ident, $key:expr) => {{
if let Some(val) = $key {
$dest.push(Property::$variant(val));
}
}};
}
macro_rules! flush_standard_property_helper {
($this:expr, $dest:expr, $context:expr, $variant:ident, $key:expr, $feature:expr) => {{
if let Some((val, prefix)) = $key {
let prefix = if prefix.contains(VendorPrefix::NONE) {
$this.flush_prefixes_helper($context, $feature)
} else {
prefix
};
$dest.push(Property::$variant((val, prefix)));
}
}};
}
macro_rules! flush_legacy_property {
($dest:expr, $context:expr, $feature:expr, $key:expr, prop_2009: ($ty2009:ty, $variant2009:ident), prop_2012: ($ty2012:ty, $variant2012:ident)) => {{
if let Some((val, prefix)) = &*$key {
let mut prefix = $context.targets.prefixes(*prefix, $feature);
if prefix.contains(VendorPrefix::NONE) {
if let Some(targets) = $context.targets.browsers {
let mut prefixes_2009 = VendorPrefix::empty();
if Feature::is_flex_2009(&targets) {
prefixes_2009.insert(VendorPrefix::WEBKIT);
}
if prefix.contains(VendorPrefix::MOZ) {
prefixes_2009.insert(VendorPrefix::MOZ);
}
if !prefixes_2009.is_empty() {
let s = <$ty2009>::from_standard(val);
if let Some(a) = s {
$dest.push(Property::$variant2009((a, prefixes_2009)));
}
}
}
}
if prefix.contains(VendorPrefix::MS) {
let s = <$ty2012>::from_standard(val);
if let Some(q) = s {
$dest.push(Property::$variant2012((q, VendorPrefix::MS)));
}
}
prefix.remove(VendorPrefix::MOZ);
prefix.remove(VendorPrefix::MS);
let _ = prefix;
}
}};
($dest:expr, $context:expr, $feature:expr, $key:expr, prop_2009: None, prop_2012: ($ty2012:ty, $variant2012:ident)) => {{
if let Some((val, prefix)) = &*$key {
let mut prefix = $context.targets.prefixes(*prefix, $feature);
if prefix.contains(VendorPrefix::MS) {
let s = <$ty2012>::from_standard(val);
if let Some(q) = s {
$dest.push(Property::$variant2012((q, VendorPrefix::MS)));
}
}
prefix.remove(VendorPrefix::MOZ);
prefix.remove(VendorPrefix::MS);
let _ = prefix;
}
}};
}
macro_rules! flush_shorthand_helper {
(
$this:expr, $dest:expr, $context:expr,
prop: ($prop_variant:ident, $prop_ty:ty),
align_prop: ($align_feature:expr, $align_variant:ident),
$align_val:expr,
$justify_val:expr,
justify_prop: ($justify_feature:expr, $justify_variant:ident)
) => {{
if let Some((align, align_prefix)) = &mut *$align_val {
if let Some((justify_actual, justify_prefix)) = &mut *$justify_val {
let intersection = *align_prefix & *justify_prefix;
if intersection.contains(VendorPrefix::NONE) {
*align_prefix = $this.flush_prefixes_helper($context, $align_feature);
align_prefix.remove(VendorPrefix::NONE);
if !align_prefix.is_empty() {
$dest.push(Property::$align_variant((align.clone(), *align_prefix)));
}
*justify_prefix = $this.flush_prefixes_helper($context, $justify_feature);
justify_prefix.remove(VendorPrefix::NONE);
if !justify_prefix.is_empty() {
$dest.push(Property::$justify_variant((
justify_actual.clone(),
*justify_prefix,
)));
}
$dest.push(Property::$prop_variant(<$prop_ty>::from_align_justify(
align.clone(),
justify_actual.clone(),
)));
*$align_val = None;
*$justify_val = None;
}
}
}
}};
(
$this:expr, $dest:expr, $context:expr,
prop: ($prop_variant:ident, $prop_ty:ty),
align_prop: ($align_feature:expr, $align_variant:ident),
$align_val:expr,
$justify_val:expr,
justify_prop: None
) => {{
if let Some((align, align_prefix)) = &mut *$align_val {
if let Some(justify) = &mut *$justify_val {
let intersection = *align_prefix;
if intersection.contains(VendorPrefix::NONE) {
*align_prefix = $this.flush_prefixes_helper($context, $align_feature);
align_prefix.remove(VendorPrefix::NONE);
if !align_prefix.is_empty() {
$dest.push(Property::$align_variant((align.clone(), *align_prefix)));
}
$dest.push(Property::$prop_variant(<$prop_ty>::from_align_justify(
align.clone(),
justify.clone(),
)));
*$align_val = None;
*$justify_val = None;
}
}
}
}};
}
impl PlaceContent {
fn from_align_justify(align: AlignContent, justify: JustifyContent) -> Self {
Self { align, justify }
}
}
impl PlaceSelf {
fn from_align_justify(align: AlignSelf, justify: JustifySelf) -> Self {
Self { align, justify }
}
}
impl PlaceItems {
fn from_align_justify(align: AlignItems, justify: JustifyItems) -> Self {
Self { align, justify }
}
}
impl AlignHandler {
pub(crate) fn handle_property(
&mut self,
property: &Property,
dest: &mut DeclarationList<'_>,
context: &mut PropertyHandlerContext<'_>,
) -> bool {
match property {
Property::AlignContent((val, vp)) => {
self.flex_line_pack = None;
handle_property_helper!(self, dest, context, align_content, val, *vp);
}
Property::FlexLinePack((val, vp)) => {
handle_property_helper!(self, dest, context, flex_line_pack, val, *vp);
}
Property::JustifyContent((val, vp)) => {
self.box_pack = None;
self.flex_pack = None;
handle_property_helper!(self, dest, context, justify_content, val, *vp);
}
Property::BoxPack((val, vp)) => {
handle_property_helper!(self, dest, context, box_pack, val, *vp);
}
Property::FlexPack((val, vp)) => {
handle_property_helper!(self, dest, context, flex_pack, val, *vp);
}
Property::PlaceContent(val) => {
self.flex_line_pack = None;
self.box_pack = None;
self.flex_pack = None;
handle_property_maybe_flush!(
self,
dest,
context,
align_content,
&val.align,
VendorPrefix::NONE
);
handle_property_maybe_flush!(
self,
dest,
context,
justify_content,
&val.justify,
VendorPrefix::NONE
);
handle_property_helper!(
self,
dest,
context,
align_content,
&val.align,
VendorPrefix::NONE
);
handle_property_helper!(
self,
dest,
context,
justify_content,
&val.justify,
VendorPrefix::NONE
);
}
Property::AlignSelf((val, vp)) => {
self.flex_item_align = None;
handle_property_helper!(self, dest, context, align_self, val, *vp);
}
Property::FlexItemAlign((val, vp)) => {
handle_property_helper!(self, dest, context, flex_item_align, val, *vp);
}
Property::JustifySelf(val) => {
self.justify_self = Some(val.clone());
self.has_any = true;
}
Property::PlaceSelf(val) => {
self.flex_item_align = None;
handle_property_helper!(
self,
dest,
context,
align_self,
&val.align,
VendorPrefix::NONE
);
self.justify_self = Some(val.justify.clone());
}
Property::AlignItems((val, vp)) => {
self.box_align = None;
self.flex_align = None;
handle_property_helper!(self, dest, context, align_items, val, *vp);
}
Property::BoxAlign((val, vp)) => {
handle_property_helper!(self, dest, context, box_align, val, *vp);
}
Property::FlexAlign((val, vp)) => {
handle_property_helper!(self, dest, context, flex_align, val, *vp);
}
Property::JustifyItems(val) => {
self.justify_items = Some(val.clone());
self.has_any = true;
}
Property::PlaceItems(val) => {
self.box_align = None;
self.flex_align = None;
handle_property_helper!(
self,
dest,
context,
align_items,
&val.align,
VendorPrefix::NONE
);
self.justify_items = Some(val.justify.clone());
}
Property::RowGap(val) => {
self.row_gap = Some(val.clone());
self.has_any = true;
}
Property::ColumnGap(val) => {
self.column_gap = Some(val.clone());
self.has_any = true;
}
Property::Gap(val) => {
self.row_gap = Some(val.row.clone());
self.column_gap = Some(val.column.clone());
self.has_any = true;
}
Property::Unparsed(val) => {
if is_align_property(&val.property_id) {
self.flush(dest, context);
let bump = dest.bump();
dest.push(Property::Unparsed(val.deep_clone(bump)));
} else {
return false;
}
}
_ => return false,
}
true
}
pub(crate) fn finalize(
&mut self,
dest: &mut DeclarationList<'_>,
context: &mut PropertyHandlerContext<'_>,
) {
self.flush(dest, context);
}
fn flush(&mut self, dest: &mut DeclarationList<'_>, context: &mut PropertyHandlerContext<'_>) {
if !self.has_any {
return;
}
self.has_any = false;
let mut align_content = self.align_content.take();
let mut justify_content = self.justify_content.take();
let mut align_self = self.align_self.take();
let mut justify_self = self.justify_self.take();
let mut align_items = self.align_items.take();
let mut justify_items = self.justify_items.take();
let row_gap = self.row_gap.take();
let column_gap = self.column_gap.take();
let box_align = self.box_align.take();
let box_pack = self.box_pack.take();
let flex_line_pack = self.flex_line_pack.take();
let flex_pack = self.flex_pack.take();
let flex_align = self.flex_align.take();
let flex_item_align = self.flex_item_align.take();
flush_prefixed_property!(dest, context, BoxAlign, box_align);
flush_prefixed_property!(dest, context, BoxPack, box_pack);
flush_prefixed_property!(dest, context, FlexPack, flex_pack);
flush_prefixed_property!(dest, context, FlexAlign, flex_align);
flush_prefixed_property!(dest, context, FlexItemAlign, flex_item_align);
flush_prefixed_property!(dest, context, FlexLinePack, flex_line_pack);
flush_legacy_property!(dest, context, Feature::AlignContent, &align_content, prop_2009: None, prop_2012: (FlexLinePack, FlexLinePack));
flush_legacy_property!(dest, context, Feature::JustifyContent, &justify_content, prop_2009: (BoxPack, BoxPack), prop_2012: (FlexPack, FlexPack));
if context.targets.is_compatible(compat::Feature::PlaceContent) {
flush_shorthand_helper!(
self, dest, context,
prop: (PlaceContent, PlaceContent),
align_prop: (Feature::AlignContent, AlignContent),
&mut align_content,
&mut justify_content,
justify_prop: (Feature::JustifyContent, JustifyContent)
);
}
flush_standard_property_helper!(
self,
dest,
context,
AlignContent,
align_content.take(),
Feature::AlignContent
);
flush_standard_property_helper!(
self,
dest,
context,
JustifyContent,
justify_content.take(),
Feature::JustifyContent
);
flush_legacy_property!(dest, context, Feature::AlignSelf, &align_self, prop_2009: None, prop_2012: (FlexItemAlign, FlexItemAlign));
if context.targets.is_compatible(compat::Feature::PlaceSelf) {
flush_shorthand_helper!(
self, dest, context,
prop: (PlaceSelf, PlaceSelf),
align_prop: (Feature::AlignSelf, AlignSelf),
&mut align_self,
&mut justify_self,
justify_prop: None
);
}
flush_standard_property_helper!(
self,
dest,
context,
AlignSelf,
align_self.take(),
Feature::AlignSelf
);
flush_unprefix_property!(dest, context, JustifySelf, justify_self.take());
flush_legacy_property!(dest, context, Feature::AlignItems, &align_items, prop_2009: (BoxAlign, BoxAlign), prop_2012: (FlexAlign, FlexAlign));
if context.targets.is_compatible(compat::Feature::PlaceItems) {
flush_shorthand_helper!(
self, dest, context,
prop: (PlaceItems, PlaceItems),
align_prop: (Feature::AlignItems, AlignItems),
&mut align_items,
&mut justify_items,
justify_prop: None
);
}
flush_standard_property_helper!(
self,
dest,
context,
AlignItems,
align_items.take(),
Feature::AlignItems
);
flush_unprefix_property!(dest, context, JustifyItems, justify_items.take());
match (row_gap, column_gap) {
(Some(row), Some(column)) => dest.push(Property::Gap(Gap { row, column })),
(row, column) => {
if let Some(row) = row {
dest.push(Property::RowGap(row));
}
if let Some(column) = column {
dest.push(Property::ColumnGap(column));
}
}
}
}
fn flush_prefixes_helper(
&self,
context: &PropertyHandlerContext<'_>,
feature: Feature,
) -> VendorPrefix {
let mut prefix = context.targets.prefixes(VendorPrefix::NONE, feature);
prefix.remove(VendorPrefix::MOZ);
prefix.remove(VendorPrefix::MS);
prefix
}
}
fn is_align_property(property_id: &PropertyId) -> bool {
matches!(
property_id,
PropertyId::AlignContent(_)
| PropertyId::FlexLinePack(_)
| PropertyId::JustifyContent(_)
| PropertyId::BoxPack(_)
| PropertyId::FlexPack(_)
| PropertyId::PlaceContent
| PropertyId::AlignSelf(_)
| PropertyId::FlexItemAlign(_)
| PropertyId::JustifySelf
| PropertyId::PlaceSelf
| PropertyId::AlignItems(_)
| PropertyId::BoxAlign(_)
| PropertyId::FlexAlign(_)
| PropertyId::JustifyItems
| PropertyId::PlaceItems
| PropertyId::RowGap
| PropertyId::ColumnGap
| PropertyId::Gap
)
}