| //! CSS properties related to flexbox layout. |
| |
| use super::align::{ |
| AlignContent, AlignItems, AlignSelf, ContentDistribution, ContentPosition, JustifyContent, SelfPosition, |
| }; |
| use super::{Property, PropertyId}; |
| use crate::context::PropertyHandlerContext; |
| use crate::declaration::{DeclarationBlock, DeclarationList}; |
| use crate::error::{ParserError, PrinterError}; |
| use crate::macros::*; |
| use crate::prefixes::{is_flex_2009, Feature}; |
| use crate::printer::Printer; |
| use crate::traits::{FromStandard, Parse, PropertyHandler, Shorthand, ToCss, Zero}; |
| use crate::values::number::{CSSInteger, CSSNumber}; |
| use crate::values::{ |
| length::{LengthPercentage, LengthPercentageOrAuto}, |
| percentage::Percentage, |
| }; |
| use crate::vendor_prefix::VendorPrefix; |
| #[cfg(feature = "visitor")] |
| use crate::visitor::Visit; |
| use cssparser::*; |
| |
| enum_property! { |
| /// A value for the [flex-direction](https://www.w3.org/TR/2018/CR-css-flexbox-1-20181119/#propdef-flex-direction) property. |
| pub enum FlexDirection { |
| /// Flex items are laid out in a row. |
| Row, |
| /// Flex items are laid out in a row, and reversed. |
| RowReverse, |
| /// Flex items are laid out in a column. |
| Column, |
| /// Flex items are laid out in a column, and reversed. |
| ColumnReverse, |
| } |
| } |
| |
| impl Default for FlexDirection { |
| fn default() -> FlexDirection { |
| FlexDirection::Row |
| } |
| } |
| |
| enum_property! { |
| /// A value for the [flex-wrap](https://www.w3.org/TR/2018/CR-css-flexbox-1-20181119/#flex-wrap-property) property. |
| pub enum FlexWrap { |
| /// The flex items do not wrap. |
| "nowrap": NoWrap, |
| /// The flex items wrap. |
| "wrap": Wrap, |
| /// The flex items wrap, in reverse. |
| "wrap-reverse": WrapReverse, |
| } |
| } |
| |
| impl Default for FlexWrap { |
| fn default() -> FlexWrap { |
| FlexWrap::NoWrap |
| } |
| } |
| |
| impl FromStandard<FlexWrap> for FlexWrap { |
| fn from_standard(wrap: &FlexWrap) -> Option<FlexWrap> { |
| Some(wrap.clone()) |
| } |
| } |
| |
| define_shorthand! { |
| /// A value for the [flex-flow](https://www.w3.org/TR/2018/CR-css-flexbox-1-20181119/#flex-flow-property) shorthand property. |
| pub struct FlexFlow(VendorPrefix) { |
| /// The direction that flex items flow. |
| direction: FlexDirection(FlexDirection, VendorPrefix), |
| /// How the flex items wrap. |
| wrap: FlexWrap(FlexWrap, VendorPrefix), |
| } |
| } |
| |
| impl<'i> Parse<'i> for FlexFlow { |
| fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> { |
| let mut direction = None; |
| let mut wrap = None; |
| loop { |
| if direction.is_none() { |
| if let Ok(value) = input.try_parse(FlexDirection::parse) { |
| direction = Some(value); |
| continue; |
| } |
| } |
| if wrap.is_none() { |
| if let Ok(value) = input.try_parse(FlexWrap::parse) { |
| wrap = Some(value); |
| continue; |
| } |
| } |
| break; |
| } |
| |
| Ok(FlexFlow { |
| direction: direction.unwrap_or_default(), |
| wrap: wrap.unwrap_or_default(), |
| }) |
| } |
| } |
| |
| impl ToCss for FlexFlow { |
| fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError> |
| where |
| W: std::fmt::Write, |
| { |
| let mut needs_space = false; |
| if self.direction != FlexDirection::default() || self.wrap == FlexWrap::default() { |
| self.direction.to_css(dest)?; |
| needs_space = true; |
| } |
| |
| if self.wrap != FlexWrap::default() { |
| if needs_space { |
| dest.write_str(" ")?; |
| } |
| self.wrap.to_css(dest)?; |
| } |
| |
| Ok(()) |
| } |
| } |
| |
| define_shorthand! { |
| /// A value for the [flex](https://www.w3.org/TR/2018/CR-css-flexbox-1-20181119/#flex-property) shorthand property. |
| pub struct Flex(VendorPrefix) { |
| /// The flex grow factor. |
| grow: FlexGrow(CSSNumber, VendorPrefix), |
| /// The flex shrink factor. |
| shrink: FlexShrink(CSSNumber, VendorPrefix), |
| /// The flex basis. |
| basis: FlexBasis(LengthPercentageOrAuto, VendorPrefix), |
| } |
| } |
| |
| impl<'i> Parse<'i> for Flex { |
| fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> { |
| if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() { |
| return Ok(Flex { |
| grow: 0.0, |
| shrink: 0.0, |
| basis: LengthPercentageOrAuto::Auto, |
| }); |
| } |
| |
| let mut grow = None; |
| let mut shrink = None; |
| let mut basis = None; |
| |
| loop { |
| if grow.is_none() { |
| if let Ok(val) = input.try_parse(CSSNumber::parse) { |
| grow = Some(val); |
| shrink = input.try_parse(CSSNumber::parse).ok(); |
| continue; |
| } |
| } |
| |
| if basis.is_none() { |
| if let Ok(val) = input.try_parse(LengthPercentageOrAuto::parse) { |
| basis = Some(val); |
| continue; |
| } |
| } |
| |
| break; |
| } |
| |
| Ok(Flex { |
| grow: grow.unwrap_or(1.0), |
| shrink: shrink.unwrap_or(1.0), |
| basis: basis.unwrap_or(LengthPercentageOrAuto::LengthPercentage(LengthPercentage::Percentage( |
| Percentage(0.0), |
| ))), |
| }) |
| } |
| } |
| |
| impl ToCss for Flex { |
| fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError> |
| where |
| W: std::fmt::Write, |
| { |
| if self.grow == 0.0 && self.shrink == 0.0 && self.basis == LengthPercentageOrAuto::Auto { |
| dest.write_str("none")?; |
| return Ok(()); |
| } |
| |
| #[derive(PartialEq)] |
| enum ZeroKind { |
| NonZero, |
| Length, |
| Percentage, |
| } |
| |
| // If the basis is unitless 0, we must write all three components to disambiguate. |
| // If the basis is 0%, we can omit the basis. |
| let basis_kind = match &self.basis { |
| LengthPercentageOrAuto::LengthPercentage(lp) => match lp { |
| LengthPercentage::Dimension(l) if l.is_zero() => ZeroKind::Length, |
| LengthPercentage::Percentage(p) if p.is_zero() => ZeroKind::Percentage, |
| _ => ZeroKind::NonZero, |
| }, |
| _ => ZeroKind::NonZero, |
| }; |
| |
| if self.grow != 1.0 || self.shrink != 1.0 || basis_kind != ZeroKind::NonZero { |
| self.grow.to_css(dest)?; |
| if self.shrink != 1.0 || basis_kind == ZeroKind::Length { |
| dest.write_str(" ")?; |
| self.shrink.to_css(dest)?; |
| } |
| } |
| |
| if basis_kind != ZeroKind::Percentage { |
| if self.grow != 1.0 || self.shrink != 1.0 || basis_kind == ZeroKind::Length { |
| dest.write_str(" ")?; |
| } |
| self.basis.to_css(dest)?; |
| } |
| |
| Ok(()) |
| } |
| } |
| |
| // Old flex (2009): https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/ |
| |
| enum_property! { |
| /// A value for the legacy (prefixed) [box-orient](https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#orientation) property. |
| /// Partially equivalent to `flex-direction` in the standard syntax. |
| pub enum BoxOrient { |
| /// Items are laid out horizontally. |
| Horizontal, |
| /// Items are laid out vertically. |
| Vertical, |
| /// Items are laid out along the inline axis, according to the writing direction. |
| InlineAxis, |
| /// Items are laid out along the block axis, according to the writing direction. |
| BlockAxis, |
| } |
| } |
| |
| impl FlexDirection { |
| fn to_2009(&self) -> (BoxOrient, BoxDirection) { |
| match self { |
| FlexDirection::Row => (BoxOrient::Horizontal, BoxDirection::Normal), |
| FlexDirection::Column => (BoxOrient::Vertical, BoxDirection::Normal), |
| FlexDirection::RowReverse => (BoxOrient::Horizontal, BoxDirection::Reverse), |
| FlexDirection::ColumnReverse => (BoxOrient::Vertical, BoxDirection::Reverse), |
| } |
| } |
| } |
| |
| enum_property! { |
| /// A value for the legacy (prefixed) [box-direction](https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#displayorder) property. |
| /// Partially equivalent to the `flex-direction` property in the standard syntax. |
| pub enum BoxDirection { |
| /// Items flow in the natural direction. |
| Normal, |
| /// Items flow in the reverse direction. |
| Reverse, |
| } |
| } |
| |
| enum_property! { |
| /// A value for the legacy (prefixed) [box-align](https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#alignment) property. |
| /// Equivalent to the `align-items` property in the standard syntax. |
| pub enum BoxAlign { |
| /// Items are aligned to the start. |
| Start, |
| /// Items are aligned to the end. |
| End, |
| /// Items are centered. |
| Center, |
| /// Items are aligned to the baseline. |
| Baseline, |
| /// Items are stretched. |
| Stretch, |
| } |
| } |
| |
| impl FromStandard<AlignItems> for BoxAlign { |
| fn from_standard(align: &AlignItems) -> Option<BoxAlign> { |
| match align { |
| AlignItems::SelfPosition { overflow: None, value } => match value { |
| SelfPosition::Start | SelfPosition::FlexStart => Some(BoxAlign::Start), |
| SelfPosition::End | SelfPosition::FlexEnd => Some(BoxAlign::End), |
| SelfPosition::Center => Some(BoxAlign::Center), |
| _ => None, |
| }, |
| AlignItems::Stretch => Some(BoxAlign::Stretch), |
| _ => None, |
| } |
| } |
| } |
| |
| enum_property! { |
| /// A value for the legacy (prefixed) [box-pack](https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#packing) property. |
| /// Equivalent to the `justify-content` property in the standard syntax. |
| pub enum BoxPack { |
| /// Items are justified to the start. |
| Start, |
| /// Items are justified to the end. |
| End, |
| /// Items are centered. |
| Center, |
| /// Items are justified to the start and end. |
| Justify, |
| } |
| } |
| |
| impl FromStandard<JustifyContent> for BoxPack { |
| fn from_standard(justify: &JustifyContent) -> Option<BoxPack> { |
| match justify { |
| JustifyContent::ContentDistribution(cd) => match cd { |
| ContentDistribution::SpaceBetween => Some(BoxPack::Justify), |
| _ => None, |
| }, |
| JustifyContent::ContentPosition { overflow: None, value } => match value { |
| ContentPosition::Start | ContentPosition::FlexStart => Some(BoxPack::Start), |
| ContentPosition::End | ContentPosition::FlexEnd => Some(BoxPack::End), |
| ContentPosition::Center => Some(BoxPack::Center), |
| }, |
| _ => None, |
| } |
| } |
| } |
| |
| enum_property! { |
| /// A value for the legacy (prefixed) [box-lines](https://www.w3.org/TR/2009/WD-css3-flexbox-20090723/#multiple) property. |
| /// Equivalent to the `flex-wrap` property in the standard syntax. |
| pub enum BoxLines { |
| /// Items are laid out in a single line. |
| Single, |
| /// Items may wrap into multiple lines. |
| Multiple, |
| } |
| } |
| |
| impl FromStandard<FlexWrap> for BoxLines { |
| fn from_standard(wrap: &FlexWrap) -> Option<BoxLines> { |
| match wrap { |
| FlexWrap::NoWrap => Some(BoxLines::Single), |
| FlexWrap::Wrap => Some(BoxLines::Multiple), |
| _ => None, |
| } |
| } |
| } |
| |
| type BoxOrdinalGroup = CSSInteger; |
| impl FromStandard<CSSInteger> for BoxOrdinalGroup { |
| fn from_standard(order: &CSSInteger) -> Option<BoxOrdinalGroup> { |
| Some(*order) |
| } |
| } |
| |
| // Old flex (2012): https://www.w3.org/TR/2012/WD-css3-flexbox-20120322/ |
| |
| enum_property! { |
| /// A value for the legacy (prefixed) [flex-pack](https://www.w3.org/TR/2012/WD-css3-flexbox-20120322/#flex-pack) property. |
| /// Equivalent to the `justify-content` property in the standard syntax. |
| pub enum FlexPack { |
| /// Items are justified to the start. |
| Start, |
| /// Items are justified to the end. |
| End, |
| /// Items are centered. |
| Center, |
| /// Items are justified to the start and end. |
| Justify, |
| /// Items are distributed evenly, with half size spaces on either end. |
| Distribute, |
| } |
| } |
| |
| impl FromStandard<JustifyContent> for FlexPack { |
| fn from_standard(justify: &JustifyContent) -> Option<FlexPack> { |
| match justify { |
| JustifyContent::ContentDistribution(cd) => match cd { |
| ContentDistribution::SpaceBetween => Some(FlexPack::Justify), |
| ContentDistribution::SpaceAround => Some(FlexPack::Distribute), |
| _ => None, |
| }, |
| JustifyContent::ContentPosition { overflow: None, value } => match value { |
| ContentPosition::Start | ContentPosition::FlexStart => Some(FlexPack::Start), |
| ContentPosition::End | ContentPosition::FlexEnd => Some(FlexPack::End), |
| ContentPosition::Center => Some(FlexPack::Center), |
| }, |
| _ => None, |
| } |
| } |
| } |
| |
| /// A value for the legacy (prefixed) [flex-align](https://www.w3.org/TR/2012/WD-css3-flexbox-20120322/#flex-align) property. |
| pub type FlexAlign = BoxAlign; |
| |
| enum_property! { |
| /// A value for the legacy (prefixed) [flex-item-align](https://www.w3.org/TR/2012/WD-css3-flexbox-20120322/#flex-align) property. |
| /// Equivalent to the `align-self` property in the standard syntax. |
| pub enum FlexItemAlign { |
| /// Equivalent to the value of `flex-align`. |
| Auto, |
| /// The item is aligned to the start. |
| Start, |
| /// The item is aligned to the end. |
| End, |
| /// The item is centered. |
| Center, |
| /// The item is aligned to the baseline. |
| Baseline, |
| /// The item is stretched. |
| Stretch, |
| } |
| } |
| |
| impl FromStandard<AlignSelf> for FlexItemAlign { |
| fn from_standard(justify: &AlignSelf) -> Option<FlexItemAlign> { |
| match justify { |
| AlignSelf::Auto => Some(FlexItemAlign::Auto), |
| AlignSelf::Stretch => Some(FlexItemAlign::Stretch), |
| AlignSelf::SelfPosition { overflow: None, value } => match value { |
| SelfPosition::Start | SelfPosition::FlexStart => Some(FlexItemAlign::Start), |
| SelfPosition::End | SelfPosition::FlexEnd => Some(FlexItemAlign::End), |
| SelfPosition::Center => Some(FlexItemAlign::Center), |
| _ => None, |
| }, |
| _ => None, |
| } |
| } |
| } |
| |
| enum_property! { |
| /// A value for the legacy (prefixed) [flex-line-pack](https://www.w3.org/TR/2012/WD-css3-flexbox-20120322/#flex-line-pack) property. |
| /// Equivalent to the `align-content` property in the standard syntax. |
| pub enum FlexLinePack { |
| /// Content is aligned to the start. |
| Start, |
| /// Content is aligned to the end. |
| End, |
| /// Content is centered. |
| Center, |
| /// Content is justified. |
| Justify, |
| /// Content is distributed evenly, with half size spaces on either end. |
| Distribute, |
| /// Content is stretched. |
| Stretch, |
| } |
| } |
| |
| impl FromStandard<AlignContent> for FlexLinePack { |
| fn from_standard(justify: &AlignContent) -> Option<FlexLinePack> { |
| match justify { |
| AlignContent::ContentDistribution(cd) => match cd { |
| ContentDistribution::SpaceBetween => Some(FlexLinePack::Justify), |
| ContentDistribution::SpaceAround => Some(FlexLinePack::Distribute), |
| ContentDistribution::Stretch => Some(FlexLinePack::Stretch), |
| _ => None, |
| }, |
| AlignContent::ContentPosition { overflow: None, value } => match value { |
| ContentPosition::Start | ContentPosition::FlexStart => Some(FlexLinePack::Start), |
| ContentPosition::End | ContentPosition::FlexEnd => Some(FlexLinePack::End), |
| ContentPosition::Center => Some(FlexLinePack::Center), |
| }, |
| _ => None, |
| } |
| } |
| } |
| |
| #[derive(Default, Debug)] |
| pub(crate) struct FlexHandler { |
| direction: Option<(FlexDirection, VendorPrefix)>, |
| box_orient: Option<(BoxOrient, VendorPrefix)>, |
| box_direction: Option<(BoxDirection, VendorPrefix)>, |
| wrap: Option<(FlexWrap, VendorPrefix)>, |
| box_lines: Option<(BoxLines, VendorPrefix)>, |
| grow: Option<(CSSNumber, VendorPrefix)>, |
| box_flex: Option<(CSSNumber, VendorPrefix)>, |
| flex_positive: Option<(CSSNumber, VendorPrefix)>, |
| shrink: Option<(CSSNumber, VendorPrefix)>, |
| flex_negative: Option<(CSSNumber, VendorPrefix)>, |
| basis: Option<(LengthPercentageOrAuto, VendorPrefix)>, |
| preferred_size: Option<(LengthPercentageOrAuto, VendorPrefix)>, |
| order: Option<(CSSInteger, VendorPrefix)>, |
| box_ordinal_group: Option<(BoxOrdinalGroup, VendorPrefix)>, |
| flex_order: Option<(CSSInteger, VendorPrefix)>, |
| has_any: bool, |
| } |
| |
| impl<'i> PropertyHandler<'i> for FlexHandler { |
| fn handle_property( |
| &mut self, |
| property: &Property<'i>, |
| dest: &mut DeclarationList<'i>, |
| context: &mut PropertyHandlerContext<'i, '_>, |
| ) -> bool { |
| use Property::*; |
| |
| macro_rules! maybe_flush { |
| ($prop: ident, $val: expr, $vp: ident) => {{ |
| // If two vendor prefixes for the same property have different |
| // values, we need to flush what we have immediately to preserve order. |
| if let Some((val, prefixes)) = &self.$prop { |
| if val != $val && !prefixes.contains(*$vp) { |
| self.flush(dest, context); |
| } |
| } |
| }}; |
| } |
| |
| macro_rules! property { |
| ($prop: ident, $val: expr, $vp: ident) => {{ |
| maybe_flush!($prop, $val, $vp); |
| |
| // Otherwise, update the value and add the prefix. |
| if let Some((val, prefixes)) = &mut self.$prop { |
| *val = $val.clone(); |
| *prefixes |= *$vp; |
| } else { |
| self.$prop = Some(($val.clone(), *$vp)); |
| self.has_any = true; |
| } |
| }}; |
| } |
| |
| match property { |
| FlexDirection(val, vp) => { |
| if context.targets.browsers.is_some() { |
| self.box_direction = None; |
| self.box_orient = None; |
| } |
| property!(direction, val, vp); |
| } |
| BoxOrient(val, vp) => property!(box_orient, val, vp), |
| BoxDirection(val, vp) => property!(box_direction, val, vp), |
| FlexWrap(val, vp) => { |
| if context.targets.browsers.is_some() { |
| self.box_lines = None; |
| } |
| property!(wrap, val, vp); |
| } |
| BoxLines(val, vp) => property!(box_lines, val, vp), |
| FlexFlow(val, vp) => { |
| if context.targets.browsers.is_some() { |
| self.box_direction = None; |
| self.box_orient = None; |
| } |
| property!(direction, &val.direction, vp); |
| property!(wrap, &val.wrap, vp); |
| } |
| FlexGrow(val, vp) => { |
| if context.targets.browsers.is_some() { |
| self.box_flex = None; |
| self.flex_positive = None; |
| } |
| property!(grow, val, vp); |
| } |
| BoxFlex(val, vp) => property!(box_flex, val, vp), |
| FlexPositive(val, vp) => property!(flex_positive, val, vp), |
| FlexShrink(val, vp) => { |
| if context.targets.browsers.is_some() { |
| self.flex_negative = None; |
| } |
| property!(shrink, val, vp); |
| } |
| FlexNegative(val, vp) => property!(flex_negative, val, vp), |
| FlexBasis(val, vp) => { |
| if context.targets.browsers.is_some() { |
| self.preferred_size = None; |
| } |
| property!(basis, val, vp); |
| } |
| FlexPreferredSize(val, vp) => property!(preferred_size, val, vp), |
| Flex(val, vp) => { |
| if context.targets.browsers.is_some() { |
| self.box_flex = None; |
| self.flex_positive = None; |
| self.flex_negative = None; |
| self.preferred_size = None; |
| } |
| maybe_flush!(grow, &val.grow, vp); |
| maybe_flush!(shrink, &val.shrink, vp); |
| maybe_flush!(basis, &val.basis, vp); |
| property!(grow, &val.grow, vp); |
| property!(shrink, &val.shrink, vp); |
| property!(basis, &val.basis, vp); |
| } |
| Order(val, vp) => { |
| if context.targets.browsers.is_some() { |
| self.box_ordinal_group = None; |
| self.flex_order = None; |
| } |
| property!(order, val, vp); |
| } |
| BoxOrdinalGroup(val, vp) => property!(box_ordinal_group, val, vp), |
| FlexOrder(val, vp) => property!(flex_order, val, vp), |
| Unparsed(val) if is_flex_property(&val.property_id) => { |
| self.flush(dest, context); |
| dest.push(property.clone()) // TODO: prefix? |
| } |
| _ => return false, |
| } |
| |
| true |
| } |
| |
| fn finalize(&mut self, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>) { |
| self.flush(dest, context); |
| } |
| } |
| |
| impl FlexHandler { |
| fn flush<'i>(&mut self, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>) { |
| if !self.has_any { |
| return; |
| } |
| |
| self.has_any = false; |
| |
| let mut direction = std::mem::take(&mut self.direction); |
| let mut wrap = std::mem::take(&mut self.wrap); |
| let mut grow = std::mem::take(&mut self.grow); |
| let mut shrink = std::mem::take(&mut self.shrink); |
| let mut basis = std::mem::take(&mut self.basis); |
| let box_orient = std::mem::take(&mut self.box_orient); |
| let box_direction = std::mem::take(&mut self.box_direction); |
| let box_flex = std::mem::take(&mut self.box_flex); |
| let box_ordinal_group = std::mem::take(&mut self.box_ordinal_group); |
| let box_lines = std::mem::take(&mut self.box_lines); |
| let flex_positive = std::mem::take(&mut self.flex_positive); |
| let flex_negative = std::mem::take(&mut self.flex_negative); |
| let preferred_size = std::mem::take(&mut self.preferred_size); |
| let order = std::mem::take(&mut self.order); |
| let flex_order = std::mem::take(&mut self.flex_order); |
| |
| macro_rules! single_property { |
| ($prop: ident, $key: ident $(, 2012: $prop_2012: ident )? $(, 2009: $prop_2009: ident )?) => { |
| if let Some((val, prefix)) = $key { |
| if !prefix.is_empty() { |
| let mut prefix = context.targets.prefixes(prefix, Feature::$prop); |
| if prefix.contains(VendorPrefix::None) { |
| $( |
| // 2009 spec, implemented by webkit and firefox. |
| if let Some(targets) = context.targets.browsers { |
| let mut prefixes_2009 = VendorPrefix::empty(); |
| if is_flex_2009(targets) { |
| prefixes_2009 |= VendorPrefix::WebKit; |
| } |
| if prefix.contains(VendorPrefix::Moz) { |
| prefixes_2009 |= VendorPrefix::Moz; |
| } |
| if !prefixes_2009.is_empty() { |
| if let Some(v) = $prop_2009::from_standard(&val) { |
| dest.push(Property::$prop_2009(v, prefixes_2009)); |
| } |
| } |
| } |
| )? |
| } |
| |
| $( |
| let mut ms = true; |
| if prefix.contains(VendorPrefix::Ms) { |
| dest.push(Property::$prop_2012(val.clone(), VendorPrefix::Ms)); |
| ms = false; |
| } |
| if !ms { |
| prefix.remove(VendorPrefix::Ms); |
| } |
| )? |
| |
| // Firefox only implemented the 2009 spec prefixed. |
| prefix.remove(VendorPrefix::Moz); |
| dest.push(Property::$prop(val, prefix)) |
| } |
| } |
| }; |
| } |
| |
| macro_rules! legacy_property { |
| ($prop: ident, $key: expr) => { |
| if let Some((val, prefix)) = $key { |
| if !prefix.is_empty() { |
| dest.push(Property::$prop(val, prefix)) |
| } |
| } |
| }; |
| } |
| |
| // Legacy properties. These are only set if the final standard properties were unset. |
| legacy_property!(BoxOrient, box_orient); |
| legacy_property!(BoxDirection, box_direction); |
| legacy_property!(BoxOrdinalGroup, box_ordinal_group); |
| legacy_property!(BoxFlex, box_flex); |
| legacy_property!(BoxLines, box_lines); |
| legacy_property!(FlexPositive, flex_positive); |
| legacy_property!(FlexNegative, flex_negative); |
| legacy_property!(FlexPreferredSize, preferred_size.clone()); |
| legacy_property!(FlexOrder, flex_order.clone()); |
| |
| if let Some((direction, _)) = direction { |
| if let Some(targets) = context.targets.browsers { |
| let prefixes = context.targets.prefixes(VendorPrefix::None, Feature::FlexDirection); |
| let mut prefixes_2009 = VendorPrefix::empty(); |
| if is_flex_2009(targets) { |
| prefixes_2009 |= VendorPrefix::WebKit; |
| } |
| if prefixes.contains(VendorPrefix::Moz) { |
| prefixes_2009 |= VendorPrefix::Moz; |
| } |
| if !prefixes_2009.is_empty() { |
| let (orient, dir) = direction.to_2009(); |
| dest.push(Property::BoxOrient(orient, prefixes_2009)); |
| dest.push(Property::BoxDirection(dir, prefixes_2009)); |
| } |
| } |
| } |
| |
| if let (Some((direction, dir_prefix)), Some((wrap, wrap_prefix))) = (&mut direction, &mut wrap) { |
| let intersection = *dir_prefix & *wrap_prefix; |
| if !intersection.is_empty() { |
| let mut prefix = context.targets.prefixes(intersection, Feature::FlexFlow); |
| // Firefox only implemented the 2009 spec prefixed. |
| prefix.remove(VendorPrefix::Moz); |
| dest.push(Property::FlexFlow( |
| FlexFlow { |
| direction: *direction, |
| wrap: *wrap, |
| }, |
| prefix, |
| )); |
| dir_prefix.remove(intersection); |
| wrap_prefix.remove(intersection); |
| } |
| } |
| |
| single_property!(FlexDirection, direction); |
| single_property!(FlexWrap, wrap, 2009: BoxLines); |
| |
| if let Some(targets) = context.targets.browsers { |
| if let Some((grow, _)) = grow { |
| let prefixes = context.targets.prefixes(VendorPrefix::None, Feature::FlexGrow); |
| let mut prefixes_2009 = VendorPrefix::empty(); |
| if is_flex_2009(targets) { |
| prefixes_2009 |= VendorPrefix::WebKit; |
| } |
| if prefixes.contains(VendorPrefix::Moz) { |
| prefixes_2009 |= VendorPrefix::Moz; |
| } |
| if !prefixes_2009.is_empty() { |
| dest.push(Property::BoxFlex(grow, prefixes_2009)); |
| } |
| } |
| } |
| |
| if let (Some((grow, grow_prefix)), Some((shrink, shrink_prefix)), Some((basis, basis_prefix))) = |
| (&mut grow, &mut shrink, &mut basis) |
| { |
| let intersection = *grow_prefix & *shrink_prefix & *basis_prefix; |
| if !intersection.is_empty() { |
| let mut prefix = context.targets.prefixes(intersection, Feature::Flex); |
| // Firefox only implemented the 2009 spec prefixed. |
| prefix.remove(VendorPrefix::Moz); |
| dest.push(Property::Flex( |
| Flex { |
| grow: *grow, |
| shrink: *shrink, |
| basis: basis.clone(), |
| }, |
| prefix, |
| )); |
| grow_prefix.remove(intersection); |
| shrink_prefix.remove(intersection); |
| basis_prefix.remove(intersection); |
| } |
| } |
| |
| single_property!(FlexGrow, grow, 2012: FlexPositive); |
| single_property!(FlexShrink, shrink, 2012: FlexNegative); |
| single_property!(FlexBasis, basis, 2012: FlexPreferredSize); |
| single_property!(Order, order, 2012: FlexOrder, 2009: BoxOrdinalGroup); |
| } |
| } |
| |
| #[inline] |
| fn is_flex_property(property_id: &PropertyId) -> bool { |
| match property_id { |
| PropertyId::FlexDirection(_) |
| | PropertyId::BoxOrient(_) |
| | PropertyId::BoxDirection(_) |
| | PropertyId::FlexWrap(_) |
| | PropertyId::BoxLines(_) |
| | PropertyId::FlexFlow(_) |
| | PropertyId::FlexGrow(_) |
| | PropertyId::BoxFlex(_) |
| | PropertyId::FlexPositive(_) |
| | PropertyId::FlexShrink(_) |
| | PropertyId::FlexNegative(_) |
| | PropertyId::FlexBasis(_) |
| | PropertyId::FlexPreferredSize(_) |
| | PropertyId::Flex(_) |
| | PropertyId::Order(_) |
| | PropertyId::BoxOrdinalGroup(_) |
| | PropertyId::FlexOrder(_) => true, |
| _ => false, |
| } |
| } |