Source code
Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
//! Manual shorthand parsing and serialization
#![allow(missing_docs)]
use super::expanded;
use crate::parser::{Parse, ParserContext};
use crate::values::specified;
use cssparser::Parser;
use std::fmt::{self, Write};
use style_traits::{
values::SequenceWriter, CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo,
StyleParseErrorKind, ToCss,
};
macro_rules! try_parse_one {
($context: expr, $input: expr, $var: ident, $parse_path: path) => {
if $var.is_none() {
if let Ok(value) = $input.try_parse(|i| $parse_path($context, i)) {
$var = Some(value);
continue;
}
}
};
($input: expr, $var: ident, $parse_path: path) => {
if $var.is_none() {
if let Ok(value) = $input.try_parse(|i| $parse_path(i)) {
$var = Some(value);
continue;
}
}
};
}
macro_rules! unwrap_or_initial {
($prop: ident) => {
unwrap_or_initial!($prop, $prop)
};
($prop: ident, $expr: expr) => {
$expr.unwrap_or_else(|| $prop::get_initial_specified_value())
};
}
/// Serializes a border shorthand value composed of width/style/color.
pub fn serialize_directional_border<W>(
dest: &mut CssWriter<W>,
width: &specified::BorderSideWidth,
style: &specified::BorderStyle,
color: &specified::Color,
) -> fmt::Result
where
W: Write,
{
use specified::{BorderSideWidth, BorderStyle, Color};
let has_style = *style != BorderStyle::None;
let has_color = *color != Color::CurrentColor;
let has_width = *width != BorderSideWidth::medium();
if !has_style && !has_color && !has_width {
return width.to_css(dest);
}
let mut writer = SequenceWriter::new(dest, " ");
if has_width {
writer.item(width)?;
}
if has_style {
writer.item(style)?;
}
if has_color {
writer.item(color)?;
}
Ok(())
}
pub fn parse_border<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<
(
specified::BorderSideWidth,
specified::BorderStyle,
specified::Color,
),
ParseError<'i>,
> {
use crate::values::specified::{BorderSideWidth, BorderStyle, Color};
let mut color = None;
let mut style = None;
let mut width = None;
let mut parsed = 0;
loop {
parsed += 1;
try_parse_one!(context, input, width, BorderSideWidth::parse);
try_parse_one!(input, style, BorderStyle::parse);
try_parse_one!(context, input, color, Color::parse);
parsed -= 1;
break;
}
if parsed == 0 {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok((
width.unwrap_or(BorderSideWidth::medium()),
style.unwrap_or(BorderStyle::None),
color.unwrap_or(Color::CurrentColor),
))
}
pub mod border_block {
use super::*;
pub use crate::properties::shorthands_generated::border_block::*;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let (width, style, color) = super::parse_border(context, input)?;
Ok(Longhands {
border_block_start_width: width.clone(),
border_block_start_style: style.clone(),
border_block_start_color: color.clone(),
border_block_end_width: width,
border_block_end_style: style,
border_block_end_color: color,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
// FIXME: Should serialize empty if start != end, right?
super::serialize_directional_border(
dest,
&self.border_block_start_width,
&self.border_block_start_style,
&self.border_block_start_color,
)
}
}
}
pub mod border_inline {
use super::*;
pub use crate::properties::shorthands_generated::border_inline::*;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let (width, style, color) = super::parse_border(context, input)?;
Ok(Longhands {
border_inline_start_width: width.clone(),
border_inline_start_style: style.clone(),
border_inline_start_color: color.clone(),
border_inline_end_width: width,
border_inline_end_style: style,
border_inline_end_color: color,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
// FIXME: Should serialize empty if start != end, right?
super::serialize_directional_border(
dest,
&self.border_inline_start_width,
&self.border_inline_start_style,
&self.border_inline_start_color,
)
}
}
}
pub mod border_radius {
pub use crate::properties::shorthands_generated::border_radius::*;
use super::*;
use crate::values::generics::border::BorderCornerRadius;
use crate::values::generics::rect::Rect;
use crate::values::specified::BorderRadius;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let radii = BorderRadius::parse(context, input)?;
Ok(expanded! {
border_top_left_radius: radii.top_left,
border_top_right_radius: radii.top_right,
border_bottom_right_radius: radii.bottom_right,
border_bottom_left_radius: radii.bottom_left,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
let LonghandsToSerialize {
border_top_left_radius: &BorderCornerRadius(ref tl),
border_top_right_radius: &BorderCornerRadius(ref tr),
border_bottom_right_radius: &BorderCornerRadius(ref br),
border_bottom_left_radius: &BorderCornerRadius(ref bl),
} = *self;
let widths = Rect::new(tl.width(), tr.width(), br.width(), bl.width());
let heights = Rect::new(tl.height(), tr.height(), br.height(), bl.height());
BorderRadius::serialize_rects(widths, heights, dest)
}
}
}
pub mod border_image {
pub use crate::properties::shorthands_generated::border_image::*;
use super::*;
use crate::properties::longhands::{
border_image_outset, border_image_repeat, border_image_slice, border_image_source,
border_image_width,
};
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let mut outset = border_image_outset::get_initial_specified_value();
let mut repeat = border_image_repeat::get_initial_specified_value();
let mut slice = border_image_slice::get_initial_specified_value();
let mut source = border_image_source::get_initial_specified_value();
let mut width = border_image_width::get_initial_specified_value();
let mut any = false;
let mut parsed_slice = false;
let mut parsed_source = false;
let mut parsed_repeat = false;
loop {
if !parsed_slice {
if let Ok(value) =
input.try_parse(|input| border_image_slice::parse(context, input))
{
parsed_slice = true;
any = true;
slice = value;
// Parse border image width and outset, if applicable.
let maybe_width_outset: Result<_, ParseError> = input.try_parse(|input| {
input.expect_delim('/')?;
// Parse border image width, if applicable.
let w = input
.try_parse(|input| border_image_width::parse(context, input))
.ok();
// Parse border image outset if applicable.
let o = input
.try_parse(|input| {
input.expect_delim('/')?;
border_image_outset::parse(context, input)
})
.ok();
if w.is_none() && o.is_none() {
return Err(
input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
);
}
Ok((w, o))
});
if let Ok((w, o)) = maybe_width_outset {
if let Some(w) = w {
width = w;
}
if let Some(o) = o {
outset = o;
}
}
continue;
}
}
if !parsed_source {
if let Ok(value) =
input.try_parse(|input| border_image_source::parse(context, input))
{
source = value;
parsed_source = true;
any = true;
continue;
}
}
if !parsed_repeat {
if let Ok(value) =
input.try_parse(|input| border_image_repeat::parse(context, input))
{
repeat = value;
parsed_repeat = true;
any = true;
continue;
}
}
break;
}
if !any {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(expanded! {
border_image_outset: outset,
border_image_repeat: repeat,
border_image_slice: slice,
border_image_source: source,
border_image_width: width,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
let mut has_any = false;
let has_source =
*self.border_image_source != border_image_source::get_initial_specified_value();
has_any = has_any || has_source;
let has_slice =
*self.border_image_slice != border_image_slice::get_initial_specified_value();
has_any = has_any || has_slice;
let has_outset =
*self.border_image_outset != border_image_outset::get_initial_specified_value();
has_any = has_any || has_outset;
let has_width =
*self.border_image_width != border_image_width::get_initial_specified_value();
has_any = has_any || has_width;
let has_repeat =
*self.border_image_repeat != border_image_repeat::get_initial_specified_value();
has_any = has_any || has_repeat;
if has_source || !has_any {
self.border_image_source.to_css(dest)?;
if !has_any {
return Ok(());
}
}
let needs_slice = has_slice || has_width || has_outset;
if needs_slice {
if has_source {
dest.write_char(' ')?;
}
self.border_image_slice.to_css(dest)?;
if has_width || has_outset {
dest.write_str(" /")?;
if has_width {
dest.write_char(' ')?;
self.border_image_width.to_css(dest)?;
}
if has_outset {
dest.write_str(" / ")?;
self.border_image_outset.to_css(dest)?;
}
}
}
if has_repeat {
if has_source || needs_slice {
dest.write_char(' ')?;
}
self.border_image_repeat.to_css(dest)?;
}
Ok(())
}
}
}
pub mod border {
pub use crate::properties::shorthands_generated::border::*;
use super::*;
use crate::properties::longhands::{
border_image_outset, border_image_repeat, border_image_slice, border_image_source,
border_image_width,
};
pub use crate::properties::shorthands_generated::border_left;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let (width, style, color) = super::parse_border(context, input)?;
Ok(expanded! {
border_top_width: width.clone(),
border_top_style: style,
border_top_color: color.clone(),
border_right_width: width.clone(),
border_right_style: style,
border_right_color: color.clone(),
border_bottom_width: width.clone(),
border_bottom_style: style,
border_bottom_color: color.clone(),
border_left_width: width.clone(),
border_left_style: style,
border_left_color: color.clone(),
// The 'border' shorthand resets 'border-image' to its initial value.
border_image_outset: border_image_outset::get_initial_specified_value(),
border_image_repeat: border_image_repeat::get_initial_specified_value(),
border_image_slice: border_image_slice::get_initial_specified_value(),
border_image_source: border_image_source::get_initial_specified_value(),
border_image_width: border_image_width::get_initial_specified_value(),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
use crate::properties::longhands;
// If any of the border-image longhands differ from their initial specified values we should not
// invoke serialize_directional_border(), so there is no point in continuing on to compute all_equal.
if *self.border_image_outset
!= longhands::border_image_outset::get_initial_specified_value()
{
return Ok(());
}
if *self.border_image_repeat
!= longhands::border_image_repeat::get_initial_specified_value()
{
return Ok(());
}
if *self.border_image_slice
!= longhands::border_image_slice::get_initial_specified_value()
{
return Ok(());
}
if *self.border_image_source
!= longhands::border_image_source::get_initial_specified_value()
{
return Ok(());
}
if *self.border_image_width
!= longhands::border_image_width::get_initial_specified_value()
{
return Ok(());
}
let all_equal = {
let border_top_width = self.border_top_width;
let border_top_style = self.border_top_style;
let border_top_color = self.border_top_color;
let border_right_width = self.border_right_width;
let border_right_style = self.border_right_style;
let border_right_color = self.border_right_color;
let border_bottom_width = self.border_bottom_width;
let border_bottom_style = self.border_bottom_style;
let border_bottom_color = self.border_bottom_color;
let border_left_width = self.border_left_width;
let border_left_style = self.border_left_style;
let border_left_color = self.border_left_color;
border_top_width == border_right_width
&& border_right_width == border_bottom_width
&& border_bottom_width == border_left_width
&& border_top_style == border_right_style
&& border_right_style == border_bottom_style
&& border_bottom_style == border_left_style
&& border_top_color == border_right_color
&& border_right_color == border_bottom_color
&& border_bottom_color == border_left_color
};
// If all longhands are all present, then all sides should be the same,
// so we can just one set of color/style/width
if !all_equal {
return Ok(());
}
super::serialize_directional_border(
dest,
self.border_left_width,
self.border_left_style,
self.border_left_color,
)
}
}
// We need to implement this by hand because deriving this would also derive border-image,
// which this property only resets. Just use the same as border-left for simplicity.
impl SpecifiedValueInfo for Longhands {
const SUPPORTED_TYPES: u8 = border_left::Longhands::SUPPORTED_TYPES;
fn collect_completion_keywords(f: KeywordsCollectFn) {
border_left::Longhands::collect_completion_keywords(f);
}
}
}
pub mod container {
use super::*;
pub use crate::properties::shorthands_generated::container::*;
use crate::values::specified::{ContainerName, ContainerType};
pub fn parse_value<'i>(
context: &ParserContext,
input: &mut Parser<'i, '_>,
) -> Result<Longhands, ParseError<'i>> {
let container_name = ContainerName::parse(context, input)?;
let container_type = if input.try_parse(|input| input.expect_delim('/')).is_ok() {
ContainerType::parse(context, input)?
} else {
ContainerType::NORMAL
};
Ok(expanded! {
container_name: container_name,
container_type: container_type,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
self.container_name.to_css(dest)?;
if !self.container_type.is_normal() {
dest.write_str(" / ")?;
self.container_type.to_css(dest)?;
}
Ok(())
}
}
}
pub mod vertical_align {
use super::*;
pub use crate::properties::shorthands_generated::vertical_align::*;
use crate::values::specified::{AlignmentBaseline, BaselineShift, BaselineSource};
pub fn parse_value<'i>(
context: &ParserContext,
input: &mut Parser<'i, '_>,
) -> Result<Longhands, ParseError<'i>> {
let mut baseline_source = None;
let mut alignment_baseline = None;
let mut baseline_shift = None;
let mut parsed = 0;
loop {
parsed += 1;
try_parse_one!(input, baseline_source, BaselineSource::parse_non_auto);
try_parse_one!(input, alignment_baseline, AlignmentBaseline::parse);
try_parse_one!(context, input, baseline_shift, BaselineShift::parse);
parsed -= 1;
break;
}
if parsed == 0 {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(expanded! {
baseline_source: baseline_source.unwrap_or(BaselineSource::Auto),
alignment_baseline: alignment_baseline.unwrap_or(AlignmentBaseline::Baseline),
baseline_shift: baseline_shift.unwrap_or(BaselineShift::zero()),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
let mut writer = SequenceWriter::new(dest, " ");
if *self.baseline_source != BaselineSource::Auto {
writer.item(self.baseline_source)?;
}
if *self.alignment_baseline != AlignmentBaseline::Baseline {
writer.item(self.alignment_baseline)?;
}
if *self.baseline_shift != BaselineShift::zero() {
writer.item(self.baseline_shift)?;
}
if !writer.has_written() {
self.alignment_baseline.to_css(dest)?;
}
Ok(())
}
}
}
pub mod page_break_before {
use super::*;
pub use crate::properties::shorthands_generated::page_break_before::*;
use crate::values::specified::BreakBetween;
pub fn parse_value<'i>(
context: &ParserContext,
input: &mut Parser<'i, '_>,
) -> Result<Longhands, ParseError<'i>> {
Ok(expanded! {
break_before: BreakBetween::parse_legacy(context, input)?,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
self.break_before.to_css_legacy(dest)
}
}
}
pub mod page_break_after {
pub use crate::properties::shorthands_generated::page_break_after::*;
use super::*;
use crate::values::specified::BreakBetween;
pub fn parse_value<'i>(
context: &ParserContext,
input: &mut Parser<'i, '_>,
) -> Result<Longhands, ParseError<'i>> {
Ok(expanded! {
break_after: BreakBetween::parse_legacy(context, input)?,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
self.break_after.to_css_legacy(dest)
}
}
}
pub mod page_break_inside {
use super::*;
pub use crate::properties::shorthands_generated::page_break_inside::*;
use crate::values::specified::BreakWithin;
pub fn parse_value<'i>(
context: &ParserContext,
input: &mut Parser<'i, '_>,
) -> Result<Longhands, ParseError<'i>> {
Ok(expanded! {
break_inside: BreakWithin::parse_legacy(context, input)?,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
self.break_inside.to_css_legacy(dest)
}
}
}
pub mod offset {
use super::*;
pub use crate::properties::shorthands_generated::offset::*;
use crate::values::specified::{
LengthPercentage, OffsetPath, OffsetPosition, OffsetRotate, PositionOrAuto,
};
use crate::Zero;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let offset_position = input.try_parse(|i| OffsetPosition::parse(context, i)).ok();
let offset_path = input.try_parse(|i| OffsetPath::parse(context, i)).ok();
// Must have one of [offset-position, offset-path].
// FIXME: The syntax is out-of-date after the update of the spec.
if offset_position.is_none() && offset_path.is_none() {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
let mut offset_distance = None;
let mut offset_rotate = None;
// offset-distance and offset-rotate are grouped with offset-path.
if offset_path.is_some() {
loop {
if offset_distance.is_none() {
if let Ok(value) = input.try_parse(|i| LengthPercentage::parse(context, i)) {
offset_distance = Some(value);
}
}
if offset_rotate.is_none() {
if let Ok(value) = input.try_parse(|i| OffsetRotate::parse(context, i)) {
offset_rotate = Some(value);
continue;
}
}
break;
}
}
let offset_anchor = input
.try_parse(|i| {
i.expect_delim('/')?;
PositionOrAuto::parse(context, i)
})
.ok();
Ok(expanded! {
offset_position: offset_position.unwrap_or(OffsetPosition::normal()),
offset_path: offset_path.unwrap_or(OffsetPath::none()),
offset_distance: offset_distance.unwrap_or(LengthPercentage::zero()),
offset_rotate: offset_rotate.unwrap_or(OffsetRotate::auto()),
offset_anchor: offset_anchor.unwrap_or(PositionOrAuto::auto()),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
// The basic concept is: we must serialize offset-position or offset-path group.
// offset-path group means "offset-path offset-distance offset-rotate".
let must_serialize_path = *self.offset_path != OffsetPath::None
|| (!self.offset_distance.is_zero() || !self.offset_rotate.is_auto());
let position_is_default = matches!(self.offset_position, OffsetPosition::Normal);
if !position_is_default || !must_serialize_path {
self.offset_position.to_css(dest)?;
}
if must_serialize_path {
if !position_is_default {
dest.write_char(' ')?;
}
self.offset_path.to_css(dest)?;
}
if !self.offset_distance.is_zero() {
dest.write_char(' ')?;
self.offset_distance.to_css(dest)?;
}
if !self.offset_rotate.is_auto() {
dest.write_char(' ')?;
self.offset_rotate.to_css(dest)?;
}
if *self.offset_anchor != PositionOrAuto::auto() {
dest.write_str(" / ")?;
self.offset_anchor.to_css(dest)?;
}
Ok(())
}
}
}
pub mod _webkit_perspective {
pub use crate::properties::shorthands_generated::_webkit_perspective::*;
use super::*;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
use crate::properties::longhands::perspective;
use crate::values::generics::NonNegative;
use crate::values::specified::{AllowQuirks, Length, Perspective};
if let Ok(l) = input.try_parse(|input| {
Length::parse_non_negative_quirky(context, input, AllowQuirks::Always)
}) {
Ok(expanded! {
perspective: Perspective::Length(NonNegative(l)),
})
} else {
Ok(expanded! {
perspective: perspective::parse(context, input)?
})
}
}
}
pub mod _webkit_transform {
pub use crate::properties::shorthands_generated::_webkit_transform::*;
use super::*;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
use crate::values::specified::Transform;
Ok(expanded! {
transform: Transform::parse_legacy(context, input)?,
})
}
}
pub mod columns {
pub use crate::properties::shorthands_generated::columns::*;
use super::*;
use crate::properties::longhands::{column_count, column_width};
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let mut column_count = None;
let mut column_width = None;
let mut autos = 0;
loop {
if input
.try_parse(|input| input.expect_ident_matching("auto"))
.is_ok()
{
// Leave the options to None, 'auto' is the initial value.
autos += 1;
continue;
}
if column_count.is_none() {
if let Ok(value) = input.try_parse(|input| column_count::parse(context, input)) {
column_count = Some(value);
continue;
}
}
if column_width.is_none() {
if let Ok(value) = input.try_parse(|input| column_width::parse(context, input)) {
column_width = Some(value);
continue;
}
}
break;
}
let values = autos + column_count.iter().len() + column_width.iter().len();
if values == 0 || values > 2 {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
} else {
Ok(expanded! {
column_count: unwrap_or_initial!(column_count),
column_width: unwrap_or_initial!(column_width),
})
}
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
if self.column_width.is_auto() {
return self.column_count.to_css(dest);
}
self.column_width.to_css(dest)?;
if !self.column_count.is_auto() {
dest.write_char(' ')?;
self.column_count.to_css(dest)?;
}
Ok(())
}
}
}
pub mod column_rule {
pub use crate::properties::shorthands_generated::column_rule::*;
use super::*;
use crate::properties::longhands::column_rule_color;
use crate::properties::longhands::{column_rule_style, column_rule_width};
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let mut column_rule_width = None;
let mut column_rule_style = None;
let mut column_rule_color = None;
let mut parsed = 0;
loop {
parsed += 1;
try_parse_one!(context, input, column_rule_width, column_rule_width::parse);
try_parse_one!(context, input, column_rule_style, column_rule_style::parse);
try_parse_one!(context, input, column_rule_color, column_rule_color::parse);
parsed -= 1;
break;
}
if parsed == 0 {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(expanded! {
column_rule_width: unwrap_or_initial!(column_rule_width),
column_rule_style: unwrap_or_initial!(column_rule_style),
column_rule_color: unwrap_or_initial!(column_rule_color),
})
}
}
pub mod text_wrap {
pub use crate::properties::shorthands_generated::text_wrap::*;
use super::*;
use crate::properties::longhands::{text_wrap_mode, text_wrap_style};
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let mut mode = None;
let mut style = None;
loop {
if mode.is_none() {
if let Ok(value) = input.try_parse(|input| text_wrap_mode::parse(context, input)) {
mode = Some(value);
continue;
}
}
if style.is_none() {
if let Ok(value) = input.try_parse(|input| text_wrap_style::parse(context, input)) {
style = Some(value);
continue;
}
}
break;
}
if mode.is_some() || style.is_some() {
Ok(expanded! {
text_wrap_mode: unwrap_or_initial!(text_wrap_mode, mode),
text_wrap_style: unwrap_or_initial!(text_wrap_style, style),
})
} else {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
}
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
use text_wrap_mode::computed_value::T as Mode;
use text_wrap_style::computed_value::T as Style;
if matches!(self.text_wrap_style, &Style::Auto) {
return self.text_wrap_mode.to_css(dest);
}
if *self.text_wrap_mode != Mode::Wrap {
self.text_wrap_mode.to_css(dest)?;
dest.write_char(' ')?;
}
self.text_wrap_style.to_css(dest)
}
}
}
pub mod white_space {
pub use crate::properties::shorthands_generated::white_space::*;
use super::*;
use crate::properties::longhands::{text_wrap_mode, white_space_collapse};
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
use text_wrap_mode::computed_value::T as Wrap;
use white_space_collapse::computed_value::T as Collapse;
fn parse_special_shorthands<'i, 't>(
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let (mode, collapse) = try_match_ident_ignore_ascii_case! { input,
"normal" => (Wrap::Wrap, Collapse::Collapse),
"pre" => (Wrap::Nowrap, Collapse::Preserve),
"pre-wrap" => (Wrap::Wrap, Collapse::Preserve),
"pre-line" => (Wrap::Wrap, Collapse::PreserveBreaks),
};
Ok(expanded! {
text_wrap_mode: mode,
white_space_collapse: collapse,
})
}
if let Ok(result) = input.try_parse(parse_special_shorthands) {
return Ok(result);
}
let mut wrap = None;
let mut collapse = None;
loop {
if wrap.is_none() {
if let Ok(value) = input.try_parse(|input| text_wrap_mode::parse(context, input)) {
wrap = Some(value);
continue;
}
}
if collapse.is_none() {
if let Ok(value) =
input.try_parse(|input| white_space_collapse::parse(context, input))
{
collapse = Some(value);
continue;
}
}
break;
}
if wrap.is_some() || collapse.is_some() {
Ok(expanded! {
text_wrap_mode: unwrap_or_initial!(text_wrap_mode, wrap),
white_space_collapse: unwrap_or_initial!(white_space_collapse, collapse),
})
} else {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
}
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
use text_wrap_mode::computed_value::T as Wrap;
use white_space_collapse::computed_value::T as Collapse;
match *self.text_wrap_mode {
Wrap::Wrap => match *self.white_space_collapse {
Collapse::Collapse => return dest.write_str("normal"),
Collapse::Preserve => return dest.write_str("pre-wrap"),
Collapse::PreserveBreaks => return dest.write_str("pre-line"),
_ => (),
},
Wrap::Nowrap => {
if let Collapse::Preserve = *self.white_space_collapse {
return dest.write_str("pre");
}
},
}
let mut has_value = false;
if *self.white_space_collapse != Collapse::Collapse {
self.white_space_collapse.to_css(dest)?;
has_value = true;
}
if *self.text_wrap_mode != Wrap::Wrap {
if has_value {
dest.write_char(' ')?;
}
self.text_wrap_mode.to_css(dest)?;
}
Ok(())
}
}
impl SpecifiedValueInfo for Longhands {
fn collect_completion_keywords(f: KeywordsCollectFn) {
// Collect keywords from our longhands.
text_wrap_mode::SpecifiedValue::collect_completion_keywords(f);
white_space_collapse::SpecifiedValue::collect_completion_keywords(f);
// Add the special values supported only by the shorthand
// (see parse_special_shorthands() above).
f(&["normal", "pre", "pre-wrap", "pre-line"])
}
}
}
pub mod _webkit_text_stroke {
pub use crate::properties::shorthands_generated::_webkit_text_stroke::*;
use super::*;
use crate::properties::longhands::{_webkit_text_stroke_color, _webkit_text_stroke_width};
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let mut color = None;
let mut width = None;
loop {
if color.is_none() {
if let Ok(value) =
input.try_parse(|input| _webkit_text_stroke_color::parse(context, input))
{
color = Some(value);
continue;
}
}
if width.is_none() {
if let Ok(value) =
input.try_parse(|input| _webkit_text_stroke_width::parse(context, input))
{
width = Some(value);
continue;
}
}
break;
}
if color.is_some() || width.is_some() {
Ok(expanded! {
_webkit_text_stroke_color: unwrap_or_initial!(_webkit_text_stroke_color, color),
_webkit_text_stroke_width: unwrap_or_initial!(_webkit_text_stroke_width, width),
})
} else {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
}
}
}
pub mod list_style {
pub use crate::properties::shorthands_generated::list_style::*;
use super::*;
use crate::properties::longhands::{list_style_image, list_style_position, list_style_type};
use crate::values::specified::Image;
use selectors::parser::SelectorParseErrorKind;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
// `none` is ambiguous until we've finished parsing the shorthands, so we count the number
// of times we see it.
let mut nones = 0u8;
let (mut image, mut position, mut list_style_type) = (None, None, None);
let mut parsed = 0;
loop {
parsed += 1;
if input
.try_parse(|input| input.expect_ident_matching("none"))
.is_ok()
{
nones += 1;
if nones > 2 {
return Err(input
.new_custom_error(SelectorParseErrorKind::UnexpectedIdent("none".into())));
}
continue;
}
try_parse_one!(context, input, image, list_style_image::parse);
try_parse_one!(context, input, position, list_style_position::parse);
// list-style-type must be checked the last, because it accepts
// arbitrary identifier for custom counter style, and thus may
// affect values of list-style-position.
try_parse_one!(context, input, list_style_type, list_style_type::parse);
parsed -= 1;
break;
}
let position = unwrap_or_initial!(list_style_position, position);
if parsed == 0 {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
// If there are two `none`s, then we can't have a type or image; if there is one `none`,
// then we can't have both a type *and* an image; if there is no `none` then we're fine as
// long as we parsed something.
use self::list_style_type::SpecifiedValue as ListStyleType;
match (nones, list_style_type, image) {
(2, None, None) => Ok(expanded! {
list_style_position: position,
list_style_image: Image::None,
list_style_type: ListStyleType::none(),
}),
(1, None, Some(image)) => Ok(expanded! {
list_style_position: position,
list_style_image: image,
list_style_type: ListStyleType::none(),
}),
(1, Some(list_style_type), None) => Ok(expanded! {
list_style_position: position,
list_style_image: Image::None,
list_style_type: list_style_type,
}),
(1, None, None) => Ok(expanded! {
list_style_position: position,
list_style_image: Image::None,
list_style_type: ListStyleType::none(),
}),
(0, list_style_type, image) => Ok(expanded! {
list_style_position: position,
list_style_image: unwrap_or_initial!(list_style_image, image),
list_style_type: unwrap_or_initial!(list_style_type),
}),
_ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
}
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
use list_style_image::SpecifiedValue as ListStyleImage;
use list_style_position::SpecifiedValue as ListStylePosition;
use list_style_type::SpecifiedValue as ListStyleType;
let mut writer = SequenceWriter::new(dest, " ");
if *self.list_style_position != ListStylePosition::Outside {
writer.item(self.list_style_position)?;
}
if *self.list_style_image != ListStyleImage::None {
writer.item(self.list_style_image)?;
}
if *self.list_style_type != ListStyleType::disc() {
writer.item(self.list_style_type)?;
}
if !writer.has_written() {
self.list_style_position.to_css(dest)?;
}
Ok(())
}
}
}
pub mod gap {
pub use crate::properties::shorthands_generated::gap::*;
use super::*;
use crate::properties::longhands::{column_gap, row_gap};
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let r_gap = row_gap::parse(context, input)?;
let c_gap = input
.try_parse(|input| column_gap::parse(context, input))
.unwrap_or(r_gap.clone());
Ok(expanded! {
row_gap: r_gap,
column_gap: c_gap,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
if self.row_gap == self.column_gap {
self.row_gap.to_css(dest)
} else {
self.row_gap.to_css(dest)?;
dest.write_char(' ')?;
self.column_gap.to_css(dest)
}
}
}
}
pub mod marker {
pub use crate::properties::shorthands_generated::marker::*;
use super::*;
use crate::values::specified::url::UrlOrNone;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let url = UrlOrNone::parse(context, input)?;
Ok(expanded! {
marker_start: url.clone(),
marker_mid: url.clone(),
marker_end: url,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
if self.marker_start == self.marker_mid && self.marker_mid == self.marker_end {
self.marker_start.to_css(dest)
} else {
Ok(())
}
}
}
}
pub mod flex_flow {
pub use crate::properties::shorthands_generated::flex_flow::*;
use super::*;
use crate::properties::longhands::{flex_direction, flex_wrap};
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let mut direction = None;
let mut wrap = None;
loop {
if direction.is_none() {
if let Ok(value) = input.try_parse(|input| flex_direction::parse(context, input)) {
direction = Some(value);
continue;
}
}
if wrap.is_none() {
if let Ok(value) = input.try_parse(|input| flex_wrap::parse(context, input)) {
wrap = Some(value);
continue;
}
}
break;
}
if direction.is_none() && wrap.is_none() {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(expanded! {
flex_direction: unwrap_or_initial!(flex_direction, direction),
flex_wrap: unwrap_or_initial!(flex_wrap, wrap),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
if *self.flex_direction == flex_direction::get_initial_specified_value()
&& *self.flex_wrap != flex_wrap::get_initial_specified_value()
{
return self.flex_wrap.to_css(dest);
}
self.flex_direction.to_css(dest)?;
if *self.flex_wrap != flex_wrap::get_initial_specified_value() {
dest.write_char(' ')?;
self.flex_wrap.to_css(dest)?;
}
Ok(())
}
}
}
pub mod flex {
pub use crate::properties::shorthands_generated::flex::*;
use super::*;
use crate::properties::longhands::flex_basis::SpecifiedValue as FlexBasis;
use crate::values::specified::NonNegativeNumber;
fn parse_flexibility<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<(NonNegativeNumber, Option<NonNegativeNumber>), ParseError<'i>> {
let grow = NonNegativeNumber::parse(context, input)?;
let shrink = input
.try_parse(|i| NonNegativeNumber::parse(context, i))
.ok();
Ok((grow, shrink))
}
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let mut grow = None;
let mut shrink = None;
let mut basis = None;
if input
.try_parse(|input| input.expect_ident_matching("none"))
.is_ok()
{
return Ok(expanded! {
flex_grow: NonNegativeNumber::new(0.0),
flex_shrink: NonNegativeNumber::new(0.0),
flex_basis: FlexBasis::auto(),
});
}
loop {
if grow.is_none() {
if let Ok((flex_grow, flex_shrink)) =
input.try_parse(|i| parse_flexibility(context, i))
{
grow = Some(flex_grow);
shrink = flex_shrink;
continue;
}
}
if basis.is_none() {
if let Ok(value) = input.try_parse(|input| FlexBasis::parse(context, input)) {
basis = Some(value);
continue;
}
}
break;
}
if grow.is_none() && basis.is_none() {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(expanded! {
flex_grow: grow.unwrap_or(NonNegativeNumber::new(1.0)),
flex_shrink: shrink.unwrap_or(NonNegativeNumber::new(1.0)),
flex_basis: basis.unwrap_or(FlexBasis::zero_percent()),
})
}
}
pub mod place_content {
pub use crate::properties::shorthands_generated::place_content::*;
use super::*;
use crate::values::specified::align::ContentDistribution;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let align_content = ContentDistribution::parse_block(context, input)?;
let justify_content =
input.try_parse(|input| ContentDistribution::parse_inline(context, input));
let justify_content = match justify_content {
Ok(v) => v,
Err(..) => {
if !align_content.is_baseline_position() {
align_content
} else {
ContentDistribution::start()
}
},
};
Ok(expanded! {
align_content: align_content,
justify_content: justify_content,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
self.align_content.to_css(dest)?;
if self.align_content != self.justify_content {
dest.write_char(' ')?;
self.justify_content.to_css(dest)?;
}
Ok(())
}
}
}
pub mod place_self {
pub use crate::properties::shorthands_generated::place_self::*;
use super::*;
use crate::values::specified::align::SelfAlignment;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let align = SelfAlignment::parse_block(context, input)?;
let justify = input.try_parse(|input| SelfAlignment::parse_inline(context, input));
let justify = match justify {
Ok(v) => v,
Err(..) => {
debug_assert!(align.is_valid_on_both_axes());
align
},
};
Ok(expanded! {
align_self: align,
justify_self: justify,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
self.align_self.to_css(dest)?;
if self.align_self != self.justify_self {
dest.write_char(' ')?;
self.justify_self.to_css(dest)?;
}
Ok(())
}
}
}
pub mod place_items {
pub use crate::properties::shorthands_generated::place_items::*;
use super::*;
use crate::values::specified::align::{ItemPlacement, JustifyItems};
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let align = ItemPlacement::parse_block(context, input)?;
let justify = input
.try_parse(|input| ItemPlacement::parse_inline(context, input))
.unwrap_or_else(|_| align.clone());
Ok(expanded! {
align_items: align,
justify_items: JustifyItems(justify),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
self.align_items.to_css(dest)?;
if self.align_items != &self.justify_items.0 {
dest.write_char(' ')?;
self.justify_items.to_css(dest)?;
}
Ok(())
}
}
}
pub mod grid_row {
pub use crate::properties::shorthands_generated::grid_row::*;
use super::*;
use crate::values::specified::GridLine;
use crate::Zero;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let start = input.try_parse(|i| GridLine::parse(context, i))?;
let end = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
GridLine::parse(context, input)?
} else {
let mut line = GridLine::auto();
if start.line_num.is_zero() && !start.is_span {
line.ident = start.ident.clone();
}
line
};
Ok(expanded! {
grid_row_start: start,
grid_row_end: end,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
self.grid_row_start.to_css(dest)?;
if self.grid_row_start.can_omit(self.grid_row_end) {
return Ok(());
}
dest.write_str(" / ")?;
self.grid_row_end.to_css(dest)
}
}
}
pub mod grid_column {
pub use crate::properties::shorthands_generated::grid_column::*;
use super::*;
use crate::values::specified::GridLine;
use crate::Zero;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let start = input.try_parse(|i| GridLine::parse(context, i))?;
let end = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
GridLine::parse(context, input)?
} else {
let mut line = GridLine::auto();
if start.line_num.is_zero() && !start.is_span {
line.ident = start.ident.clone();
}
line
};
Ok(expanded! {
grid_column_start: start,
grid_column_end: end,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
self.grid_column_start.to_css(dest)?;
if self.grid_column_start.can_omit(self.grid_column_end) {
return Ok(());
}
dest.write_str(" / ")?;
self.grid_column_end.to_css(dest)
}
}
}
pub mod grid_area {
pub use crate::properties::shorthands_generated::grid_area::*;
use super::*;
use crate::values::specified::GridLine;
use crate::Zero;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
fn line_with_ident_from(other: &GridLine) -> GridLine {
let mut this = GridLine::auto();
if other.line_num.is_zero() && !other.is_span {
this.ident = other.ident.clone();
}
this
}
let row_start = input.try_parse(|i| GridLine::parse(context, i))?;
let (column_start, row_end, column_end) =
if input.try_parse(|i| i.expect_delim('/')).is_ok() {
let column_start = GridLine::parse(context, input)?;
let (row_end, column_end) = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
let row_end = GridLine::parse(context, input)?;
let column_end = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
GridLine::parse(context, input)?
} else {
line_with_ident_from(&column_start)
};
(row_end, column_end)
} else {
let row_end = line_with_ident_from(&row_start);
let column_end = line_with_ident_from(&column_start);
(row_end, column_end)
};
(column_start, row_end, column_end)
} else {
let line = line_with_ident_from(&row_start);
(line.clone(), line.clone(), line)
};
Ok(expanded! {
grid_row_start: row_start,
grid_row_end: row_end,
grid_column_start: column_start,
grid_column_end: column_end,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
self.grid_row_start.to_css(dest)?;
let mut trailing_values = 3;
if self.grid_column_start.can_omit(self.grid_column_end) {
trailing_values -= 1;
if self.grid_row_start.can_omit(self.grid_row_end) {
trailing_values -= 1;
if self.grid_row_start.can_omit(self.grid_column_start) {
trailing_values -= 1;
}
}
}
let values = [
&self.grid_column_start,
&self.grid_row_end,
&self.grid_column_end,
];
for value in values.iter().take(trailing_values) {
dest.write_str(" / ")?;
value.to_css(dest)?;
}
Ok(())
}
}
}
pub mod position_try {
pub use crate::properties::shorthands_generated::position_try::*;
use super::*;
use crate::values::specified::position::{PositionTryFallbacks, PositionTryOrder};
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let order =
if static_prefs::pref!("layout.css.anchor-positioning.position-try-order.enabled") {
input.try_parse(PositionTryOrder::parse).ok()
} else {
None
};
let fallbacks = PositionTryFallbacks::parse(context, input)?;
Ok(expanded! {
position_try_order: order.unwrap_or(PositionTryOrder::normal()),
position_try_fallbacks: fallbacks,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
if let Some(o) = self.position_try_order {
if *o != PositionTryOrder::Normal {
o.to_css(dest)?;
dest.write_char(' ')?;
}
}
self.position_try_fallbacks.to_css(dest)
}
}
}
fn timeline_to_css<W>(
name: &[specified::TimelineName],
axes: &[specified::ScrollAxis],
dest: &mut CssWriter<W>,
) -> fmt::Result
where
W: fmt::Write,
{
if name.len() != axes.len() {
return Ok(());
}
for (i, (name, axis)) in std::iter::zip(name.iter(), axes.iter()).enumerate() {
if i != 0 {
dest.write_str(", ")?;
}
name.to_css(dest)?;
if !axis.is_default() {
dest.write_char(' ')?;
axis.to_css(dest)?;
}
}
Ok(())
}
pub mod scroll_timeline {
pub use crate::properties::shorthands_generated::scroll_timeline::*;
use super::*;
use crate::properties::longhands::{scroll_timeline_axis, scroll_timeline_name};
pub fn parse_value<'i>(
context: &ParserContext,
input: &mut Parser<'i, '_>,
) -> Result<Longhands, ParseError<'i>> {
let mut names = Vec::with_capacity(1);
let mut axes = Vec::with_capacity(1);
input.parse_comma_separated(|input| {
let name = scroll_timeline_name::single_value::parse(context, input)?;
let axis = input.try_parse(|i| scroll_timeline_axis::single_value::parse(context, i));
names.push(name);
axes.push(axis.unwrap_or_default());
Ok(())
})?;
Ok(expanded! {
scroll_timeline_name: scroll_timeline_name::SpecifiedValue(names.into()),
scroll_timeline_axis: scroll_timeline_axis::SpecifiedValue(axes.into()),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
super::timeline_to_css(
&self.scroll_timeline_name.0,
&self.scroll_timeline_axis.0,
dest,
)
}
}
}
pub mod view_timeline {
pub use crate::properties::shorthands_generated::view_timeline::*;
use super::*;
use crate::properties::longhands::{view_timeline_axis, view_timeline_name};
pub fn parse_value<'i>(
context: &ParserContext,
input: &mut Parser<'i, '_>,
) -> Result<Longhands, ParseError<'i>> {
let mut names = Vec::with_capacity(1);
let mut axes = Vec::with_capacity(1);
input.parse_comma_separated(|input| {
let name = view_timeline_name::single_value::parse(context, input)?;
let axis = input.try_parse(|i| view_timeline_axis::single_value::parse(context, i));
names.push(name);
axes.push(axis.unwrap_or_default());
Ok(())
})?;
Ok(expanded! {
view_timeline_name: view_timeline_name::SpecifiedValue(names.into()),
view_timeline_axis: view_timeline_axis::SpecifiedValue(axes.into()),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
super::timeline_to_css(&self.view_timeline_name.0, &self.view_timeline_axis.0, dest)
}
}
}
pub mod transition {
pub use crate::properties::shorthands_generated::transition::*;
use super::*;
use crate::properties::longhands::{
transition_behavior, transition_delay, transition_duration, transition_property,
transition_timing_function,
};
use crate::values::specified::TransitionProperty;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
struct SingleTransition {
transition_property: transition_property::SingleSpecifiedValue,
transition_duration: transition_duration::SingleSpecifiedValue,
transition_timing_function: transition_timing_function::SingleSpecifiedValue,
transition_delay: transition_delay::SingleSpecifiedValue,
transition_behavior: transition_behavior::SingleSpecifiedValue,
}
fn parse_one_transition<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
first: bool,
) -> Result<SingleTransition, ParseError<'i>> {
let mut property = None;
let mut duration = None;
let mut timing_function = None;
let mut delay = None;
let mut behavior = None;
let mut parsed = 0;
loop {
parsed += 1;
try_parse_one!(
context,
input,
duration,
transition_duration::single_value::parse
);
try_parse_one!(
context,
input,
timing_function,
transition_timing_function::single_value::parse
);
try_parse_one!(context, input, delay, transition_delay::single_value::parse);
try_parse_one!(
context,
input,
behavior,
transition_behavior::single_value::parse
);
if property.is_none() {
if let Ok(value) = input.try_parse(|i| TransitionProperty::parse(context, i)) {
property = Some(value);
continue;
}
if first && input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
property = Some(TransitionProperty::none());
continue;
}
}
parsed -= 1;
break;
}
if parsed != 0 {
Ok(SingleTransition {
transition_property: property.unwrap_or_else(
transition_property::single_value::get_initial_specified_value,
),
transition_duration: duration.unwrap_or_else(
transition_duration::single_value::get_initial_specified_value,
),
transition_timing_function: timing_function.unwrap_or_else(
transition_timing_function::single_value::get_initial_specified_value,
),
transition_delay: delay.unwrap_or_else(
transition_delay::single_value::get_initial_specified_value,
),
transition_behavior: behavior.unwrap_or_else(
transition_behavior::single_value::get_initial_specified_value,
),
})
} else {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
}
}
let mut propertys = Vec::new();
let mut durations = Vec::new();
let mut timing_functions = Vec::new();
let mut delays = Vec::new();
let mut behaviors = Vec::new();
let mut first = true;
let mut has_transition_property_none = false;
let results = input.parse_comma_separated(|i| {
if has_transition_property_none {
return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
let transition = parse_one_transition(context, i, first)?;
first = false;
has_transition_property_none = transition.transition_property.is_none();
Ok(transition)
})?;
for result in results {
propertys.push(result.transition_property);
durations.push(result.transition_duration);
timing_functions.push(result.transition_timing_function);
delays.push(result.transition_delay);
behaviors.push(result.transition_behavior);
}
Ok(expanded! {
transition_property: transition_property::SpecifiedValue(propertys.into()),
transition_duration: transition_duration::SpecifiedValue(durations.into()),
transition_timing_function: transition_timing_function::SpecifiedValue(timing_functions.into()),
transition_delay: transition_delay::SpecifiedValue(delays.into()),
transition_behavior: transition_behavior::SpecifiedValue(behaviors.into()),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
use crate::Zero;
use style_traits::values::SequenceWriter;
let property_len = self.transition_property.0.len();
if property_len == 0 {
if self.transition_duration.0.len() != 1 {
return Ok(());
}
if self.transition_delay.0.len() != 1 {
return Ok(());
}
if self.transition_timing_function.0.len() != 1 {
return Ok(());
}
if self.transition_behavior.0.len() != 1 {
return Ok(());
}
} else {
if self.transition_duration.0.len() != property_len {
return Ok(());
}
if self.transition_delay.0.len() != property_len {
return Ok(());
}
if self.transition_timing_function.0.len() != property_len {
return Ok(());
}
if self.transition_behavior.0.len() != property_len {
return Ok(());
}
}
let len = self.transition_duration.0.len();
for i in 0..len {
if i != 0 {
dest.write_str(", ")?;
}
let has_duration = !self.transition_duration.0[i].is_zero();
let has_timing = !self.transition_timing_function.0[i].is_ease();
let has_delay = !self.transition_delay.0[i].is_zero();
let has_behavior = !self.transition_behavior.0[i].is_normal();
let has_any = has_duration || has_timing || has_delay || has_behavior;
let mut writer = SequenceWriter::new(dest, " ");
if property_len == 0 {
writer.raw_item("none")?;
} else if !self.transition_property.0[i].is_all() || !has_any {
writer.item(&self.transition_property.0[i])?;
}
if has_duration || has_delay {
writer.item(&self.transition_duration.0[i])?;
}
if has_timing {
writer.item(&self.transition_timing_function.0[i])?;
}
if has_delay {
writer.item(&self.transition_delay.0[i])?;
}
if has_behavior {
writer.item(&self.transition_behavior.0[i])?;
}
}
Ok(())
}
}
}
pub mod outline {
pub use crate::properties::shorthands_generated::outline::*;
use super::*;
use crate::properties::longhands::{outline_color, outline_style, outline_width};
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let _unused = context;
let mut color = None;
let mut style = None;
let mut width = None;
let mut parsed = 0;
loop {
parsed += 1;
try_parse_one!(context, input, color, specified::Color::parse);
try_parse_one!(context, input, style, outline_style::parse);
try_parse_one!(context, input, width, outline_width::parse);
parsed -= 1;
break;
}
if parsed == 0 {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(expanded! {
outline_color: unwrap_or_initial!(outline_color, color),
outline_style: unwrap_or_initial!(outline_style, style),
outline_width: unwrap_or_initial!(outline_width, width),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
let mut writer = SequenceWriter::new(dest, " ");
if *self.outline_color != outline_color::get_initial_specified_value() {
writer.item(self.outline_color)?;
}
if *self.outline_style != outline_style::get_initial_specified_value() {
writer.item(self.outline_style)?;
}
if *self.outline_width != outline_width::get_initial_specified_value() {
writer.item(self.outline_width)?;
}
if !writer.has_written() {
self.outline_style.to_css(dest)?;
}
Ok(())
}
}
}
pub mod background_position {
pub use crate::properties::shorthands_generated::background_position::*;
use super::*;
use crate::properties::longhands::{background_position_x, background_position_y};
use crate::values::specified::position::Position;
use crate::values::specified::AllowQuirks;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let mut position_x = Vec::with_capacity(1);
let mut position_y = Vec::with_capacity(1);
let mut any = false;
input.parse_comma_separated(|input| {
let value = Position::parse_three_value_quirky(context, input, AllowQuirks::Yes)?;
position_x.push(value.horizontal);
position_y.push(value.vertical);
any = true;
Ok(())
})?;
if !any {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(expanded! {
background_position_x: background_position_x::SpecifiedValue(position_x.into()),
background_position_y: background_position_y::SpecifiedValue(position_y.into()),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
let len = self.background_position_x.0.len();
if len == 0 || len != self.background_position_y.0.len() {
return Ok(());
}
for i in 0..len {
Position {
horizontal: self.background_position_x.0[i].clone(),
vertical: self.background_position_y.0[i].clone(),
}
.to_css(dest)?;
if i < len - 1 {
dest.write_str(", ")?;
}
}
Ok(())
}
}
}
pub mod background {
pub use crate::properties::shorthands_generated::background::*;
use super::*;
use crate::properties::longhands::background_clip;
use crate::properties::longhands::background_clip::single_value::computed_value::T as Clip;
use crate::properties::longhands::background_origin::single_value::computed_value::T as Origin;
use crate::properties::longhands::{
background_attachment, background_color, background_image, background_origin,
background_size,
};
use crate::properties::longhands::{
background_position_x, background_position_y, background_repeat,
};
use crate::values::specified::{AllowQuirks, Color, Position, PositionComponent};
impl From<background_origin::single_value::SpecifiedValue>
for background_clip::single_value::SpecifiedValue
{
fn from(
origin: background_origin::single_value::SpecifiedValue,
) -> background_clip::single_value::SpecifiedValue {
match origin {
background_origin::single_value::SpecifiedValue::ContentBox => {
background_clip::single_value::SpecifiedValue::ContentBox
},
background_origin::single_value::SpecifiedValue::PaddingBox => {
background_clip::single_value::SpecifiedValue::PaddingBox
},
background_origin::single_value::SpecifiedValue::BorderBox => {
background_clip::single_value::SpecifiedValue::BorderBox
},
}
}
}
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let mut background_color = None;
let mut background_image = Vec::with_capacity(1);
let mut background_position_x = Vec::with_capacity(1);
let mut background_position_y = Vec::with_capacity(1);
let mut background_repeat = Vec::with_capacity(1);
let mut background_size = Vec::with_capacity(1);
let mut background_attachment = Vec::with_capacity(1);
let mut background_origin = Vec::with_capacity(1);
let mut background_clip = Vec::with_capacity(1);
input.parse_comma_separated(|input| {
if background_color.is_some() {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
let mut image = None;
let mut position = None;
let mut repeat = None;
let mut size = None;
let mut attachment = None;
let mut origin = None;
let mut clip = None;
let mut parsed = 0;
loop {
parsed += 1;
try_parse_one!(context, input, background_color, Color::parse);
if position.is_none() {
if let Ok(value) = input.try_parse(|input| {
Position::parse_three_value_quirky(context, input, AllowQuirks::No)
}) {
position = Some(value);
size = input
.try_parse(|input| {
input.expect_delim('/')?;
background_size::single_value::parse(context, input)
})
.ok();
continue;
}
}
try_parse_one!(context, input, image, background_image::single_value::parse);
try_parse_one!(
context,
input,
repeat,
background_repeat::single_value::parse
);
try_parse_one!(
context,
input,
attachment,
background_attachment::single_value::parse
);
try_parse_one!(
context,
input,
origin,
background_origin::single_value::parse
);
try_parse_one!(context, input, clip, background_clip::single_value::parse);
parsed -= 1;
break;
}
if parsed == 0 {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
if clip.is_none() {
if let Some(origin) = origin {
clip = Some(background_clip::single_value::SpecifiedValue::from(origin));
}
}
if let Some(position) = position {
background_position_x.push(position.horizontal);
background_position_y.push(position.vertical);
} else {
background_position_x.push(PositionComponent::zero());
background_position_y.push(PositionComponent::zero());
}
if let Some(bg_image) = image {
background_image.push(bg_image);
} else {
background_image
.push(background_image::single_value::get_initial_specified_value());
}
if let Some(bg_repeat) = repeat {
background_repeat.push(bg_repeat);
} else {
background_repeat
.push(background_repeat::single_value::get_initial_specified_value());
}
if let Some(bg_size) = size {
background_size.push(bg_size);
} else {
background_size.push(background_size::single_value::get_initial_specified_value());
}
if let Some(bg_attachment) = attachment {
background_attachment.push(bg_attachment);
} else {
background_attachment
.push(background_attachment::single_value::get_initial_specified_value());
}
if let Some(bg_origin) = origin {
background_origin.push(bg_origin);
} else {
background_origin
.push(background_origin::single_value::get_initial_specified_value());
}
if let Some(bg_clip) = clip {
background_clip.push(bg_clip);
} else {
background_clip.push(background_clip::single_value::get_initial_specified_value());
}
Ok(())
})?;
Ok(expanded! {
background_color: background_color.unwrap_or(Color::transparent()),
background_image: background_image::SpecifiedValue(background_image.into()),
background_position_x: background_position_x::SpecifiedValue(background_position_x.into()),
background_position_y: background_position_y::SpecifiedValue(background_position_y.into()),
background_repeat: background_repeat::SpecifiedValue(background_repeat.into()),
background_size: background_size::SpecifiedValue(background_size.into()),
background_attachment: background_attachment::SpecifiedValue(background_attachment.into()),
background_origin: background_origin::SpecifiedValue(background_origin.into()),
background_clip: background_clip::SpecifiedValue(background_clip.into()),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
let len = self.background_image.0.len();
if len == 0 {
return Ok(());
}
if len != self.background_image.0.len() {
return Ok(());
}
if len != self.background_position_x.0.len() {
return Ok(());
}
if len != self.background_position_y.0.len() {
return Ok(());
}
if len != self.background_size.0.len() {
return Ok(());
}
if len != self.background_repeat.0.len() {
return Ok(());
}
if len != self.background_origin.0.len() {
return Ok(());
}
if len != self.background_clip.0.len() {
return Ok(());
}
if len != self.background_attachment.0.len() {
return Ok(());
}
for i in 0..len {
let image = &self.background_image.0[i];
let position_x = &self.background_position_x.0[i];
let position_y = &self.background_position_y.0[i];
let repeat = &self.background_repeat.0[i];
let size = &self.background_size.0[i];
let attachment = &self.background_attachment.0[i];
let origin = &self.background_origin.0[i];
let clip = &self.background_clip.0[i];
if i != 0 {
dest.write_str(", ")?;
}
let mut writer = SequenceWriter::new(dest, " ");
if *image != background_image::single_value::get_initial_specified_value() {
writer.item(image)?;
}
if *position_x != PositionComponent::zero()
|| *position_y != PositionComponent::zero()
|| *size != background_size::single_value::get_initial_specified_value()
{
writer.write_item(|dest| {
Position {
horizontal: position_x.clone(),
vertical: position_y.clone(),
}
.to_css(dest)?;
if *size != background_size::single_value::get_initial_specified_value() {
dest.write_str(" / ")?;
size.to_css(dest)?;
}
Ok(())
})?;
}
if *repeat != background_repeat::single_value::get_initial_specified_value() {
writer.item(repeat)?;
}
if *attachment != background_attachment::single_value::get_initial_specified_value()
{
writer.item(attachment)?;
}
if *origin != Origin::PaddingBox || *clip != Clip::BorderBox {
writer.item(origin)?;
if *clip != From::from(*origin) {
writer.item(clip)?;
}
}
if i == len - 1 {
if *self.background_color != background_color::get_initial_specified_value() {
writer.item(self.background_color)?;
}
}
if !writer.has_written() {
image.to_css(dest)?;
}
}
Ok(())
}
}
}
pub mod font {
pub use crate::properties::shorthands_generated::font::*;
use super::*;
use crate::properties::longhands::{
font_family, font_feature_settings, font_kerning, font_language_override,
font_optical_sizing, font_size, font_size_adjust, font_stretch, font_style,
font_variant_alternates, font_variant_caps, font_variant_east_asian, font_variant_emoji,
font_variant_ligatures, font_variant_numeric, font_variant_position,
font_variation_settings, font_weight,
};
#[cfg(feature = "gecko")]
use crate::values::specified::font::SystemFont;
use crate::values::specified::font::{
FontFamily, FontSize, FontStretch, FontStretchKeyword, FontStyle, FontWeight, LineHeight,
};
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let mut nb_normals = 0;
let mut style = None;
let mut variant_caps = None;
let mut weight = None;
let mut stretch = None;
#[cfg(feature = "gecko")]
if let Ok(sys) = input.try_parse(|i| SystemFont::parse(context, i)) {
return Ok(Longhands {
font_family: font_family::SpecifiedValue::system_font(sys),
font_size: font_size::SpecifiedValue::system_font(sys),
font_style: font_style::SpecifiedValue::system_font(sys),
font_stretch: font_stretch::SpecifiedValue::system_font(sys),
font_weight: font_weight::SpecifiedValue::system_font(sys),
line_height: LineHeight::normal(),
font_kerning: font_kerning::get_initial_specified_value(),
font_language_override: font_language_override::get_initial_specified_value(),
font_size_adjust: font_size_adjust::get_initial_specified_value(),
font_variant_alternates: font_variant_alternates::get_initial_specified_value(),
font_variant_east_asian: font_variant_east_asian::get_initial_specified_value(),
font_variant_emoji: font_variant_emoji::get_initial_specified_value(),
font_variant_ligatures: font_variant_ligatures::get_initial_specified_value(),
font_variant_numeric: font_variant_numeric::get_initial_specified_value(),
font_variant_position: font_variant_position::get_initial_specified_value(),
font_feature_settings: font_feature_settings::get_initial_specified_value(),
font_optical_sizing: font_optical_sizing::get_initial_specified_value(),
font_variant_caps: font_variant_caps::get_initial_specified_value(),
font_variation_settings: font_variation_settings::get_initial_specified_value(),
});
}
let size;
loop {
if input
.try_parse(|input| input.expect_ident_matching("normal"))
.is_ok()
{
nb_normals += 1;
continue;
}
try_parse_one!(context, input, style, font_style::parse);
try_parse_one!(context, input, weight, font_weight::parse);
if variant_caps.is_none() {
if input
.try_parse(|input| input.expect_ident_matching("small-caps"))
.is_ok()
{
variant_caps = Some(font_variant_caps::SpecifiedValue::SmallCaps);
continue;
}
}
try_parse_one!(input, stretch, FontStretchKeyword::parse);
size = FontSize::parse(context, input)?;
break;
}
let line_height = if input.try_parse(|input| input.expect_delim('/')).is_ok() {
Some(LineHeight::parse(context, input)?)
} else {
None
};
#[inline]
fn count<T>(opt: &Option<T>) -> u8 {
if opt.is_some() {
1
} else {
0
}
}
if (count(&style) + count(&weight) + count(&variant_caps) + count(&stretch) + nb_normals)
> 4
{
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
let family = FontFamily::parse(context, input)?;
let stretch = stretch.map(FontStretch::Keyword);
Ok(Longhands {
font_style: unwrap_or_initial!(font_style, style),
font_weight: unwrap_or_initial!(font_weight, weight),
font_stretch: unwrap_or_initial!(font_stretch, stretch),
font_variant_caps: unwrap_or_initial!(font_variant_caps, variant_caps),
font_size: size,
line_height: line_height.unwrap_or(LineHeight::normal()),
font_family: family,
font_optical_sizing: font_optical_sizing::get_initial_specified_value(),
font_variation_settings: font_variation_settings::get_initial_specified_value(),
font_kerning: font_kerning::get_initial_specified_value(),
font_language_override: font_language_override::get_initial_specified_value(),
font_size_adjust: font_size_adjust::get_initial_specified_value(),
font_variant_alternates: font_variant_alternates::get_initial_specified_value(),
font_variant_east_asian: font_variant_east_asian::get_initial_specified_value(),
font_variant_emoji: font_variant_emoji::get_initial_specified_value(),
font_variant_ligatures: font_variant_ligatures::get_initial_specified_value(),
font_variant_numeric: font_variant_numeric::get_initial_specified_value(),
font_variant_position: font_variant_position::get_initial_specified_value(),
font_feature_settings: font_feature_settings::get_initial_specified_value(),
})
}
enum CheckSystemResult {
AllSystem(SystemFont),
SomeSystem,
None,
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
match self.check_system() {
CheckSystemResult::AllSystem(sys) => return sys.to_css(dest),
CheckSystemResult::SomeSystem => return Ok(()),
CheckSystemResult::None => {},
}
if let Some(v) = self.font_optical_sizing {
if v != &font_optical_sizing::get_initial_specified_value() {
return Ok(());
}
}
if let Some(v) = self.font_variation_settings {
if v != &font_variation_settings::get_initial_specified_value() {
return Ok(());
}
}
if let Some(v) = self.font_variant_emoji {
if v != &font_variant_emoji::get_initial_specified_value() {
return Ok(());
}
}
if self.font_kerning != &font_kerning::get_initial_specified_value() {
return Ok(());
}
if self.font_language_override != &font_language_override::get_initial_specified_value()
{
return Ok(());
}
if self.font_size_adjust != &font_size_adjust::get_initial_specified_value() {
return Ok(());
}
if self.font_variant_alternates
!= &font_variant_alternates::get_initial_specified_value()
{
return Ok(());
}
if self.font_variant_east_asian
!= &font_variant_east_asian::get_initial_specified_value()
{
return Ok(());
}
if self.font_variant_ligatures != &font_variant_ligatures::get_initial_specified_value()
{
return Ok(());
}
if self.font_variant_numeric != &font_variant_numeric::get_initial_specified_value() {
return Ok(());
}
if self.font_variant_position != &font_variant_position::get_initial_specified_value() {
return Ok(());
}
if self.font_feature_settings != &font_feature_settings::get_initial_specified_value() {
return Ok(());
}
let font_stretch = match *self.font_stretch {
FontStretch::Keyword(kw) => kw,
FontStretch::Stretch(percentage) => {
match FontStretchKeyword::from_percentage(percentage.0.get()) {
Some(kw) => kw,
None => return Ok(()),
}
},
FontStretch::System(..) => return Ok(()),
};
if self.font_variant_caps != &font_variant_caps::get_initial_specified_value()
&& *self.font_variant_caps != font_variant_caps::SpecifiedValue::SmallCaps
{
return Ok(());
}
if self.font_style != &font_style::get_initial_specified_value() {
self.font_style.to_css(dest)?;
dest.write_char(' ')?;
}
if self.font_variant_caps != &font_variant_caps::get_initial_specified_value() {
self.font_variant_caps.to_css(dest)?;
dest.write_char(' ')?;
}
if self.font_weight != &FontWeight::normal()
&& self.font_weight != &FontWeight::from_gecko_keyword(400)
{
self.font_weight.to_css(dest)?;
dest.write_char(' ')?;
}
if font_stretch != FontStretchKeyword::Normal {
font_stretch.to_css(dest)?;
dest.write_char(' ')?;
}
self.font_size.to_css(dest)?;
if *self.line_height != LineHeight::normal() {
dest.write_str(" / ")?;
self.line_height.to_css(dest)?;
}
dest.write_char(' ')?;
self.font_family.to_css(dest)?;
Ok(())
}
}
impl<'a> LonghandsToSerialize<'a> {
fn check_system(&self) -> CheckSystemResult {
let mut sys = None;
let mut all = true;
macro_rules! check {
($v:expr) => {
match $v.get_system() {
Some(s) => {
debug_assert!(sys.is_none() || s == sys.unwrap());
sys = Some(s);
},
None => {
all = false;
},
}
};
($e:expr, $($es:expr),+) => { check!($e); check!($($es),*); };
}
check!(
self.font_family,
self.font_size,
self.font_style,
self.font_stretch,
self.font_weight
);
if self.line_height != &LineHeight::normal() {
all = false
}
if all {
CheckSystemResult::AllSystem(sys.unwrap())
} else if sys.is_some() {
CheckSystemResult::SomeSystem
} else {
CheckSystemResult::None
}
}
}
impl SpecifiedValueInfo for Longhands {
const SUPPORTED_TYPES: u8 = FontStyle::SUPPORTED_TYPES
| FontWeight::SUPPORTED_TYPES
| FontStretch::SUPPORTED_TYPES
| font_variant_caps::SpecifiedValue::SUPPORTED_TYPES
| FontSize::SUPPORTED_TYPES
| FontFamily::SUPPORTED_TYPES;
fn collect_completion_keywords(f: KeywordsCollectFn) {
FontStyle::collect_completion_keywords(f);
FontWeight::collect_completion_keywords(f);
FontStretch::collect_completion_keywords(f);
font_variant_caps::SpecifiedValue::collect_completion_keywords(f);
FontSize::collect_completion_keywords(f);
FontFamily::collect_completion_keywords(f);
#[cfg(feature = "gecko")]
SystemFont::collect_completion_keywords(f);
}
}
}
pub mod font_variant {
pub use crate::properties::shorthands_generated::font_variant::*;
use super::*;
use crate::properties::longhands::font_variant_alternates;
use crate::properties::longhands::font_variant_caps;
use crate::properties::longhands::font_variant_east_asian;
use crate::properties::longhands::font_variant_emoji;
use crate::properties::longhands::font_variant_ligatures;
use crate::properties::longhands::font_variant_numeric;
use crate::properties::longhands::font_variant_position;
#[allow(unused_imports)]
use crate::values::specified::FontVariantLigatures;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let mut ligatures = None;
let mut caps = None;
let mut alternates = None;
let mut numeric = None;
let mut east_asian = None;
let mut position = None;
let mut emoji = None;
if input
.try_parse(|input| input.expect_ident_matching("normal"))
.is_ok()
{
} else if input
.try_parse(|input| input.expect_ident_matching("none"))
.is_ok()
{
ligatures = Some(FontVariantLigatures::NONE);
} else {
let mut parsed = 0;
loop {
parsed += 1;
if input
.try_parse(|input| input.expect_ident_matching("normal"))
.is_ok()
|| input
.try_parse(|input| input.expect_ident_matching("none"))
.is_ok()
{
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
try_parse_one!(context, input, ligatures, font_variant_ligatures::parse);
try_parse_one!(context, input, caps, font_variant_caps::parse);
try_parse_one!(context, input, alternates, font_variant_alternates::parse);
try_parse_one!(context, input, numeric, font_variant_numeric::parse);
try_parse_one!(context, input, east_asian, font_variant_east_asian::parse);
try_parse_one!(context, input, position, font_variant_position::parse);
try_parse_one!(context, input, emoji, font_variant_emoji::parse);
parsed -= 1;
break;
}
if parsed == 0 {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
}
Ok(expanded! {
font_variant_ligatures: unwrap_or_initial!(font_variant_ligatures, ligatures),
font_variant_caps: unwrap_or_initial!(font_variant_caps, caps),
font_variant_alternates: unwrap_or_initial!(font_variant_alternates, alternates),
font_variant_numeric: unwrap_or_initial!(font_variant_numeric, numeric),
font_variant_east_asian: unwrap_or_initial!(font_variant_east_asian, east_asian),
font_variant_position: unwrap_or_initial!(font_variant_position, position),
font_variant_emoji: unwrap_or_initial!(font_variant_emoji, emoji),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
#[allow(unused_assignments)]
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
let has_none_ligatures = self.font_variant_ligatures == &FontVariantLigatures::NONE;
const TOTAL_SUBPROPS: usize = 7;
let mut nb_normals = 0;
macro_rules! count_normal {
($e: expr, $p: ident) => {
if *$e == $p::get_initial_specified_value() {
nb_normals += 1;
}
};
($v: ident) => {
count_normal!(self.$v, $v);
};
}
count_normal!(font_variant_ligatures);
count_normal!(font_variant_caps);
count_normal!(font_variant_alternates);
count_normal!(font_variant_numeric);
count_normal!(font_variant_east_asian);
count_normal!(font_variant_position);
if let Some(value) = self.font_variant_emoji {
if value == &font_variant_emoji::get_initial_specified_value() {
nb_normals += 1;
}
} else {
nb_normals += 1;
}
if nb_normals == TOTAL_SUBPROPS {
return dest.write_str("normal");
}
if has_none_ligatures {
if nb_normals == TOTAL_SUBPROPS - 1 {
dest.write_str("none")?;
}
return Ok(());
}
let mut writer = SequenceWriter::new(dest, " ");
macro_rules! write {
($e: expr, $p: ident) => {
if *$e != $p::get_initial_specified_value() {
writer.item($e)?;
}
};
($v: ident) => {
write!(self.$v, $v);
};
}
write!(font_variant_ligatures);
write!(font_variant_caps);
write!(font_variant_alternates);
write!(font_variant_numeric);
write!(font_variant_east_asian);
write!(font_variant_position);
if let Some(v) = self.font_variant_emoji {
write!(v, font_variant_emoji);
}
Ok(())
}
}
}
pub mod font_synthesis {
pub use crate::properties::shorthands_generated::font_synthesis::*;
use super::*;
use crate::values::specified::{FontSynthesis, FontSynthesisStyle};
pub fn parse_value<'i, 't>(
_context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let mut weight = FontSynthesis::None;
let mut style = FontSynthesisStyle::None;
let mut small_caps = FontSynthesis::None;
let mut position = FontSynthesis::None;
if !input
.try_parse(|input| input.expect_ident_matching("none"))
.is_ok()
{
let mut has_custom_value = false;
while !input.is_exhausted() {
try_match_ident_ignore_ascii_case! { input,
"weight" if weight == FontSynthesis::None => {
has_custom_value = true;
weight = FontSynthesis::Auto;
continue;
},
"style" if style == FontSynthesisStyle::None => {
has_custom_value = true;
style = FontSynthesisStyle::Auto;
continue;
},
"small-caps" if small_caps == FontSynthesis::None => {
has_custom_value = true;
small_caps = FontSynthesis::Auto;
continue;
},
"position" if position == FontSynthesis::None => {
has_custom_value = true;
position = FontSynthesis::Auto;
continue;
},
"oblique-only" if style == FontSynthesisStyle::None => {
has_custom_value = true;
style = FontSynthesisStyle::ObliqueOnly;
continue;
},
}
}
if !has_custom_value {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
}
Ok(expanded! {
font_synthesis_weight: weight,
font_synthesis_style: style,
font_synthesis_small_caps: small_caps,
font_synthesis_position: position,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
let mut writer = SequenceWriter::new(dest, " ");
if self.font_synthesis_weight == &FontSynthesis::Auto {
writer.raw_item("weight")?;
}
if self.font_synthesis_style != &FontSynthesisStyle::None {
if self.font_synthesis_style == &FontSynthesisStyle::Auto {
writer.raw_item("style")?;
} else {
writer.raw_item("oblique-only")?;
}
}
if self.font_synthesis_small_caps == &FontSynthesis::Auto {
writer.raw_item("small-caps")?;
}
if self.font_synthesis_position == &FontSynthesis::Auto {
writer.raw_item("position")?;
}
if !writer.has_written() {
writer.raw_item("none")?;
}
Ok(())
}
}
// The shorthand takes the sub-property names of the longhands, and not the
// 'auto' keyword like they do, so we can't automatically derive this.
impl SpecifiedValueInfo for Longhands {
fn collect_completion_keywords(f: KeywordsCollectFn) {
f(&[
"none",
"oblique-only",
"small-caps",
"position",
"style",
"weight",
]);
}
}
}
pub mod text_box {
pub use crate::properties::shorthands_generated::text_box::*;
use super::*;
use crate::values::specified::{TextBoxEdge, TextBoxTrim};
pub fn parse_value<'i>(
context: &ParserContext,
input: &mut Parser<'i, '_>,
) -> Result<Longhands, ParseError<'i>> {
let mut trim = None;
let mut edge = None;
if input
.try_parse(|input| input.expect_ident_matching("normal"))
.is_ok()
{
return Ok(Longhands {
text_box_trim: TextBoxTrim::NONE,
text_box_edge: TextBoxEdge::Auto,
});
}
loop {
try_parse_one!(context, input, trim, TextBoxTrim::parse);
try_parse_one!(context, input, edge, TextBoxEdge::parse);
break;
}
if trim.is_none() && edge.is_none() {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
// > Omitting the 'text-box-trim' value sets it to 'trim-both'
// > (not the initial value), while omitting the 'text-box-edge'
// > value sets it to auto (the initial value).
Ok(Longhands {
text_box_trim: trim.unwrap_or(TextBoxTrim::TRIM_BOTH),
text_box_edge: edge.unwrap_or(TextBoxEdge::Auto),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
if *self.text_box_trim == TextBoxTrim::NONE && *self.text_box_edge == TextBoxEdge::Auto
{
return dest.write_str("normal");
}
let mut writer = SequenceWriter::new(dest, " ");
if *self.text_box_trim != specified::TextBoxTrim::TRIM_BOTH {
writer.item(self.text_box_trim)?;
}
if *self.text_box_edge != specified::TextBoxEdge::Auto {
writer.item(self.text_box_edge)?;
}
if !writer.has_written() {
self.text_box_trim.to_css(dest)?;
}
Ok(())
}
}
}
pub mod text_emphasis {
pub use crate::properties::shorthands_generated::text_emphasis::*;
use super::*;
use crate::properties::longhands::{text_emphasis_color, text_emphasis_style};
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let mut color = None;
let mut style = None;
let mut parsed = 0;
loop {
parsed += 1;
try_parse_one!(context, input, color, text_emphasis_color::parse);
try_parse_one!(context, input, style, text_emphasis_style::parse);
parsed -= 1;
break;
}
if parsed == 0 {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(expanded! {
text_emphasis_color: unwrap_or_initial!(text_emphasis_color, color),
text_emphasis_style: unwrap_or_initial!(text_emphasis_style, style),
})
}
}
pub mod text_decoration {
pub use crate::properties::shorthands_generated::text_decoration::*;
use super::*;
use crate::properties::longhands::text_decoration_thickness;
use crate::properties::longhands::{
text_decoration_color, text_decoration_line, text_decoration_style,
};
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let mut line = None;
let mut style = None;
let mut color = None;
let mut thickness = None;
let mut parsed = 0;
loop {
parsed += 1;
try_parse_one!(context, input, line, text_decoration_line::parse);
try_parse_one!(context, input, style, text_decoration_style::parse);
try_parse_one!(context, input, color, text_decoration_color::parse);
try_parse_one!(context, input, thickness, text_decoration_thickness::parse);
parsed -= 1;
break;
}
if parsed == 0 {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(expanded! {
text_decoration_line: unwrap_or_initial!(text_decoration_line, line),
text_decoration_style: unwrap_or_initial!(text_decoration_style, style),
text_decoration_color: unwrap_or_initial!(text_decoration_color, color),
text_decoration_thickness: unwrap_or_initial!(text_decoration_thickness, thickness),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
#[allow(unused)]
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
use crate::values::specified::Color;
use crate::values::specified::TextDecorationLine;
let is_solid_style =
*self.text_decoration_style == text_decoration_style::SpecifiedValue::Solid;
let is_current_color = *self.text_decoration_color == Color::CurrentColor;
let is_auto_thickness = self.text_decoration_thickness.is_auto();
let is_none = *self.text_decoration_line == TextDecorationLine::none();
let mut writer = SequenceWriter::new(dest, " ");
if (is_solid_style && is_current_color && is_auto_thickness) || !is_none {
writer.item(self.text_decoration_line)?;
}
if !is_auto_thickness {
writer.item(self.text_decoration_thickness)?;
}
if !is_solid_style {
writer.item(self.text_decoration_style)?;
}
if !is_current_color {
writer.item(self.text_decoration_color)?;
}
Ok(())
}
}
}
pub mod animation {
pub use crate::properties::shorthands_generated::animation::*;
use super::*;
use crate::properties::longhands::{
animation_delay, animation_direction, animation_duration, animation_fill_mode,
animation_iteration_count, animation_name, animation_play_state, animation_timeline,
animation_timing_function,
};
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
struct SingleAnimation {
animation_name: animation_name::SingleSpecifiedValue,
animation_duration: animation_duration::SingleSpecifiedValue,
animation_timing_function: animation_timing_function::SingleSpecifiedValue,
animation_delay: animation_delay::SingleSpecifiedValue,
animation_iteration_count: animation_iteration_count::SingleSpecifiedValue,
animation_direction: animation_direction::SingleSpecifiedValue,
animation_fill_mode: animation_fill_mode::SingleSpecifiedValue,
animation_play_state: animation_play_state::SingleSpecifiedValue,
}
fn parse_one_animation<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<SingleAnimation, ParseError<'i>> {
let mut name = None;
let mut duration = None;
let mut timing_function = None;
let mut delay = None;
let mut iteration_count = None;
let mut direction = None;
let mut fill_mode = None;
let mut play_state = None;
let mut parsed = 0;
loop {
parsed += 1;
try_parse_one!(
context,
input,
duration,
animation_duration::single_value::parse
);
try_parse_one!(
context,
input,
timing_function,
animation_timing_function::single_value::parse
);
try_parse_one!(context, input, delay, animation_delay::single_value::parse);
try_parse_one!(
context,
input,
iteration_count,
animation_iteration_count::single_value::parse
);
try_parse_one!(
context,
input,
direction,
animation_direction::single_value::parse
);
try_parse_one!(
context,
input,
fill_mode,
animation_fill_mode::single_value::parse
);
try_parse_one!(
context,
input,
play_state,
animation_play_state::single_value::parse
);
try_parse_one!(context, input, name, animation_name::single_value::parse);
parsed -= 1;
break;
}
if parsed == 0 {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(SingleAnimation {
animation_name: name
.unwrap_or_else(animation_name::single_value::get_initial_specified_value),
animation_duration: duration
.unwrap_or_else(animation_duration::single_value::get_initial_specified_value),
animation_timing_function: timing_function.unwrap_or_else(
animation_timing_function::single_value::get_initial_specified_value,
),
animation_delay: delay
.unwrap_or_else(animation_delay::single_value::get_initial_specified_value),
animation_iteration_count: iteration_count.unwrap_or_else(
animation_iteration_count::single_value::get_initial_specified_value,
),
animation_direction: direction
.unwrap_or_else(animation_direction::single_value::get_initial_specified_value),
animation_fill_mode: fill_mode
.unwrap_or_else(animation_fill_mode::single_value::get_initial_specified_value),
animation_play_state: play_state.unwrap_or_else(
animation_play_state::single_value::get_initial_specified_value,
),
})
}
let mut names = vec![];
let mut durations = vec![];
let mut timing_functions = vec![];
let mut delays = vec![];
let mut iteration_counts = vec![];
let mut directions = vec![];
let mut fill_modes = vec![];
let mut play_states = vec![];
let results = input.parse_comma_separated(|i| parse_one_animation(context, i))?;
for result in results.into_iter() {
names.push(result.animation_name);
durations.push(result.animation_duration);
timing_functions.push(result.animation_timing_function);
delays.push(result.animation_delay);
iteration_counts.push(result.animation_iteration_count);
directions.push(result.animation_direction);
fill_modes.push(result.animation_fill_mode);
play_states.push(result.animation_play_state);
}
Ok(expanded! {
animation_name: animation_name::SpecifiedValue(names.into()),
animation_duration: animation_duration::SpecifiedValue(durations.into()),
animation_timing_function: animation_timing_function::SpecifiedValue(timing_functions.into()),
animation_delay: animation_delay::SpecifiedValue(delays.into()),
animation_iteration_count: animation_iteration_count::SpecifiedValue(iteration_counts.into()),
animation_direction: animation_direction::SpecifiedValue(directions.into()),
animation_fill_mode: animation_fill_mode::SpecifiedValue(fill_modes.into()),
animation_play_state: animation_play_state::SpecifiedValue(play_states.into()),
animation_timeline: animation_timeline::SpecifiedValue(
vec![animation_timeline::single_value::get_initial_specified_value()].into()
),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
use crate::values::specified::easing::TimingFunction;
use crate::values::specified::{
AnimationDirection, AnimationFillMode, AnimationPlayState,
};
use crate::Zero;
use style_traits::values::SequenceWriter;
let len = self.animation_name.0.len();
if len == 0 {
return Ok(());
}
if len != self.animation_duration.0.len() {
return Ok(());
}
if len != self.animation_timing_function.0.len() {
return Ok(());
}
if len != self.animation_delay.0.len() {
return Ok(());
}
if len != self.animation_iteration_count.0.len() {
return Ok(());
}
if len != self.animation_direction.0.len() {
return Ok(());
}
if len != self.animation_fill_mode.0.len() {
return Ok(());
}
if len != self.animation_play_state.0.len() {
return Ok(());
}
if self
.animation_timeline
.map_or(false, |v| v.0.len() != 1 || !v.0[0].is_auto())
{
return Ok(());
}
for i in 0..len {
if i != 0 {
dest.write_str(", ")?;
}
let has_duration = !self.animation_duration.0[i].is_auto()
&& !self.animation_duration.0[i].is_zero();
let has_timing_function = !self.animation_timing_function.0[i].is_ease();
let has_delay = !self.animation_delay.0[i].is_zero();
let has_iteration_count = !self.animation_iteration_count.0[i].is_one();
let has_direction =
!matches!(self.animation_direction.0[i], AnimationDirection::Normal);
let has_fill_mode =
!matches!(self.animation_fill_mode.0[i], AnimationFillMode::None);
let has_play_state =
!matches!(self.animation_play_state.0[i], AnimationPlayState::Running);
let animation_name = &self.animation_name.0[i];
let has_name = !animation_name.is_none();
let mut writer = SequenceWriter::new(dest, " ");
if has_duration || has_delay {
writer.item(&self.animation_duration.0[i])?;
}
if has_timing_function || TimingFunction::match_keywords(animation_name) {
writer.item(&self.animation_timing_function.0[i])?;
}
if has_delay {
writer.item(&self.animation_delay.0[i])?;
}
if has_iteration_count {
writer.item(&self.animation_iteration_count.0[i])?;
}
if has_direction || AnimationDirection::match_keywords(animation_name) {
writer.item(&self.animation_direction.0[i])?;
}
if has_fill_mode || AnimationFillMode::match_keywords(animation_name) {
writer.item(&self.animation_fill_mode.0[i])?;
}
if has_play_state || AnimationPlayState::match_keywords(animation_name) {
writer.item(&self.animation_play_state.0[i])?;
}
let has_any = {
has_duration
|| has_timing_function
|| has_delay
|| has_iteration_count
|| has_direction
|| has_fill_mode
|| has_play_state
};
if has_name || !has_any {
writer.item(animation_name)?;
}
}
Ok(())
}
}
}
pub mod mask {
pub use crate::properties::shorthands_generated::mask::*;
use super::*;
use crate::parser::Parse;
use crate::properties::longhands::{
mask_clip, mask_composite, mask_mode, mask_origin, mask_position_x, mask_position_y,
mask_repeat,
};
use crate::properties::longhands::{mask_image, mask_size};
use crate::values::specified::{Position, PositionComponent};
impl From<mask_origin::single_value::SpecifiedValue> for mask_clip::single_value::SpecifiedValue {
fn from(
origin: mask_origin::single_value::SpecifiedValue,
) -> mask_clip::single_value::SpecifiedValue {
match origin {
mask_origin::single_value::SpecifiedValue::ContentBox => {
mask_clip::single_value::SpecifiedValue::ContentBox
},
mask_origin::single_value::SpecifiedValue::PaddingBox => {
mask_clip::single_value::SpecifiedValue::PaddingBox
},
mask_origin::single_value::SpecifiedValue::BorderBox => {
mask_clip::single_value::SpecifiedValue::BorderBox
},
mask_origin::single_value::SpecifiedValue::FillBox => {
mask_clip::single_value::SpecifiedValue::FillBox
},
mask_origin::single_value::SpecifiedValue::StrokeBox => {
mask_clip::single_value::SpecifiedValue::StrokeBox
},
mask_origin::single_value::SpecifiedValue::ViewBox => {
mask_clip::single_value::SpecifiedValue::ViewBox
},
}
}
}
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let mut mask_image = Vec::with_capacity(1);
let mut mask_mode = Vec::with_capacity(1);
let mut mask_position_x = Vec::with_capacity(1);
let mut mask_position_y = Vec::with_capacity(1);
let mut mask_size = Vec::with_capacity(1);
let mut mask_repeat = Vec::with_capacity(1);
let mut mask_origin = Vec::with_capacity(1);
let mut mask_clip = Vec::with_capacity(1);
let mut mask_composite = Vec::with_capacity(1);
input.parse_comma_separated(|input| {
let mut image = None;
let mut mode = None;
let mut position = None;
let mut size = None;
let mut repeat = None;
let mut origin = None;
let mut clip = None;
let mut composite = None;
let mut parsed = 0;
loop {
parsed += 1;
try_parse_one!(context, input, image, mask_image::single_value::parse);
if position.is_none() {
if let Ok(value) = input.try_parse(|input| Position::parse(context, input)) {
position = Some(value);
size = input
.try_parse(|input| {
input.expect_delim('/')?;
mask_size::single_value::parse(context, input)
})
.ok();
continue;
}
}
try_parse_one!(context, input, repeat, mask_repeat::single_value::parse);
try_parse_one!(context, input, origin, mask_origin::single_value::parse);
try_parse_one!(context, input, clip, mask_clip::single_value::parse);
try_parse_one!(
context,
input,
composite,
mask_composite::single_value::parse
);
try_parse_one!(context, input, mode, mask_mode::single_value::parse);
parsed -= 1;
break;
}
if clip.is_none() {
if let Some(origin) = origin {
clip = Some(mask_clip::single_value::SpecifiedValue::from(origin));
}
}
if parsed == 0 {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
if let Some(position) = position {
mask_position_x.push(position.horizontal);
mask_position_y.push(position.vertical);
} else {
mask_position_x.push(PositionComponent::zero());
mask_position_y.push(PositionComponent::zero());
}
if let Some(m_image) = image {
mask_image.push(m_image);
} else {
mask_image.push(mask_image::single_value::get_initial_specified_value());
}
if let Some(m_mode) = mode {
mask_mode.push(m_mode);
} else {
mask_mode.push(mask_mode::single_value::get_initial_specified_value());
}
if let Some(m_size) = size {
mask_size.push(m_size);
} else {
mask_size.push(mask_size::single_value::get_initial_specified_value());
}
if let Some(m_repeat) = repeat {
mask_repeat.push(m_repeat);
} else {
mask_repeat.push(mask_repeat::single_value::get_initial_specified_value());
}
if let Some(m_origin) = origin {
mask_origin.push(m_origin);
} else {
mask_origin.push(mask_origin::single_value::get_initial_specified_value());
}
if let Some(m_clip) = clip {
mask_clip.push(m_clip);
} else {
mask_clip.push(mask_clip::single_value::get_initial_specified_value());
}
if let Some(m_composite) = composite {
mask_composite.push(m_composite);
} else {
mask_composite.push(mask_composite::single_value::get_initial_specified_value());
}
Ok(())
})?;
Ok(expanded! {
mask_image: mask_image::SpecifiedValue(mask_image.into()),
mask_mode: mask_mode::SpecifiedValue(mask_mode.into()),
mask_position_x: mask_position_x::SpecifiedValue(mask_position_x.into()),
mask_position_y: mask_position_y::SpecifiedValue(mask_position_y.into()),
mask_size: mask_size::SpecifiedValue(mask_size.into()),
mask_repeat: mask_repeat::SpecifiedValue(mask_repeat.into()),
mask_origin: mask_origin::SpecifiedValue(mask_origin.into()),
mask_clip: mask_clip::SpecifiedValue(mask_clip.into()),
mask_composite: mask_composite::SpecifiedValue(mask_composite.into()),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
use crate::properties::longhands::mask_clip::single_value::computed_value::T as Clip;
use crate::properties::longhands::mask_origin::single_value::computed_value::T as Origin;
use style_traits::values::SequenceWriter;
let len = self.mask_image.0.len();
if len == 0 {
return Ok(());
}
if self.mask_mode.0.len() != len {
return Ok(());
}
if self.mask_position_x.0.len() != len {
return Ok(());
}
if self.mask_position_y.0.len() != len {
return Ok(());
}
if self.mask_size.0.len() != len {
return Ok(());
}
if self.mask_repeat.0.len() != len {
return Ok(());
}
if self.mask_origin.0.len() != len {
return Ok(());
}
if self.mask_clip.0.len() != len {
return Ok(());
}
if self.mask_composite.0.len() != len {
return Ok(());
}
for i in 0..len {
if i > 0 {
dest.write_str(", ")?;
}
let image = &self.mask_image.0[i];
let mode = &self.mask_mode.0[i];
let position_x = &self.mask_position_x.0[i];
let position_y = &self.mask_position_y.0[i];
let size = &self.mask_size.0[i];
let repeat = &self.mask_repeat.0[i];
let origin = &self.mask_origin.0[i];
let clip = &self.mask_clip.0[i];
let composite = &self.mask_composite.0[i];
let mut has_other = false;
let has_image = *image != mask_image::single_value::get_initial_specified_value();
has_other |= has_image;
let has_mode = *mode != mask_mode::single_value::get_initial_specified_value();
has_other |= has_mode;
let has_size = *size != mask_size::single_value::get_initial_specified_value();
has_other |= has_size;
let has_repeat =
*repeat != mask_repeat::single_value::get_initial_specified_value();
has_other |= has_repeat;
let has_composite =
*composite != mask_composite::single_value::get_initial_specified_value();
has_other |= has_composite;
let has_position = *position_x != PositionComponent::zero()
|| *position_y != PositionComponent::zero();
let has_origin = *origin != Origin::BorderBox;
let has_clip = *clip != Clip::BorderBox;
if !has_other && !has_position && !has_origin && !has_clip {
return image.to_css(dest);
}
let mut writer = SequenceWriter::new(dest, " ");
if has_image {
writer.item(image)?;
}
if has_position || has_size {
writer.write_item(|dest| {
Position {
horizontal: position_x.clone(),
vertical: position_y.clone(),
}
.to_css(dest)?;
if has_size {
dest.write_str(" / ")?;
size.to_css(dest)?;
}
Ok(())
})?;
}
if has_repeat {
writer.item(repeat)?;
}
if has_origin || (has_clip && *clip != Clip::NoClip) {
writer.item(origin)?;
}
if has_clip && *clip != From::from(*origin) {
writer.item(clip)?;
}
if has_composite {
writer.item(composite)?;
}
if has_mode {
writer.item(mode)?;
}
}
Ok(())
}
}
}
pub mod mask_position {
pub use crate::properties::shorthands_generated::mask_position::*;
use super::*;
use crate::properties::longhands::{mask_position_x, mask_position_y};
use crate::values::specified::Position;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
// Vec grows from 0 to 4 by default on first push(). So allocate with capacity 1, so in
// the common case of only one item we don't way overallocate, then shrink. Note that we
// always push at least one item if parsing succeeds.
let mut position_x = Vec::with_capacity(1);
let mut position_y = Vec::with_capacity(1);
let mut any = false;
input.parse_comma_separated(|input| {
let value = Position::parse(context, input)?;
position_x.push(value.horizontal);
position_y.push(value.vertical);
any = true;
Ok(())
})?;
if !any {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(expanded! {
mask_position_x: mask_position_x::SpecifiedValue(position_x.into()),
mask_position_y: mask_position_y::SpecifiedValue(position_y.into()),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
let len = self.mask_position_x.0.len();
if len == 0 || self.mask_position_y.0.len() != len {
return Ok(());
}
for i in 0..len {
Position {
horizontal: self.mask_position_x.0[i].clone(),
vertical: self.mask_position_y.0[i].clone(),
}
.to_css(dest)?;
if i < len - 1 {
dest.write_str(", ")?;
}
}
Ok(())
}
}
}
pub mod grid_template {
pub use crate::properties::shorthands_generated::grid_template::*;
use super::*;
use crate::parser::Parse;
use crate::values::generics::grid::{concat_serialize_idents, TrackListValue};
use crate::values::generics::grid::{TrackList, TrackSize};
use crate::values::specified::grid::parse_line_names;
use crate::values::specified::position::{
GridTemplateAreas, TemplateAreasArc, TemplateAreasParser,
};
use crate::values::specified::{GenericGridTemplateComponent, GridTemplateComponent};
use servo_arc::Arc;
pub fn parse_grid_template<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<
(
GridTemplateComponent,
GridTemplateComponent,
GridTemplateAreas,
),
ParseError<'i>,
> {
if let Ok(x) = input.try_parse(|i| {
if i.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
if !i.is_exhausted() {
return Err(());
}
return Ok((
GenericGridTemplateComponent::None,
GenericGridTemplateComponent::None,
GridTemplateAreas::None,
));
}
Err(())
}) {
return Ok(x);
}
let first_line_names = input.try_parse(parse_line_names).unwrap_or_default();
let mut areas_parser = TemplateAreasParser::default();
if areas_parser.try_parse_string(input).is_ok() {
let mut values = vec![];
let mut line_names = vec![];
line_names.push(first_line_names);
loop {
let size = input
.try_parse(|i| TrackSize::parse(context, i))
.unwrap_or_default();
values.push(TrackListValue::TrackSize(size));
let mut names = input.try_parse(parse_line_names).unwrap_or_default();
let more_names = input.try_parse(parse_line_names);
match areas_parser.try_parse_string(input) {
Ok(()) => {
if let Ok(v) = more_names {
let mut names_vec = names.into_vec();
names_vec.extend(v.into_iter());
names = names_vec.into();
}
line_names.push(names);
},
Err(e) => {
if more_names.is_ok() {
return Err(e);
}
line_names.push(names);
break;
},
};
}
if line_names.len() == values.len() {
line_names.push(Default::default());
}
let template_areas = areas_parser
.finish()
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?;
let template_rows = TrackList {
values: values.into(),
line_names: line_names.into(),
auto_repeat_index: std::usize::MAX,
};
let template_cols = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
let value = GridTemplateComponent::parse_without_none(context, input)?;
if let GenericGridTemplateComponent::TrackList(ref list) = value {
if !list.is_explicit() {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
}
value
} else {
GridTemplateComponent::default()
};
Ok((
GenericGridTemplateComponent::TrackList(Box::new(template_rows)),
template_cols,
GridTemplateAreas::Areas(TemplateAreasArc(Arc::new(template_areas))),
))
} else {
let mut template_rows = GridTemplateComponent::parse(context, input)?;
if let GenericGridTemplateComponent::TrackList(ref mut list) = template_rows {
if list.line_names[0].is_empty() {
list.line_names[0] = first_line_names;
} else {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
}
input.expect_delim('/')?;
Ok((
template_rows,
GridTemplateComponent::parse(context, input)?,
GridTemplateAreas::None,
))
}
}
#[inline]
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let (rows, columns, areas) = parse_grid_template(context, input)?;
Ok(expanded! {
grid_template_rows: rows,
grid_template_columns: columns,
grid_template_areas: areas,
})
}
pub fn serialize_grid_template<W>(
template_rows: &GridTemplateComponent,
template_columns: &GridTemplateComponent,
template_areas: &GridTemplateAreas,
dest: &mut CssWriter<W>,
) -> fmt::Result
where
W: fmt::Write,
{
match *template_areas {
GridTemplateAreas::None => {
if template_rows.is_initial() && template_columns.is_initial() {
return GridTemplateComponent::default().to_css(dest);
}
template_rows.to_css(dest)?;
dest.write_str(" / ")?;
template_columns.to_css(dest)
},
GridTemplateAreas::Areas(ref areas) => {
if areas.0.strings.len() != template_rows.track_list_len() {
return Ok(());
}
let track_list = match *template_rows {
GenericGridTemplateComponent::TrackList(ref list) => {
if !list.is_explicit() {
return Ok(());
}
list
},
_ => return Ok(()),
};
match *template_columns {
GenericGridTemplateComponent::TrackList(ref list) => {
if !list.is_explicit() {
return Ok(());
}
},
GenericGridTemplateComponent::Subgrid(_) => {
return Ok(());
},
_ => {},
}
let mut names_iter = track_list.line_names.iter();
for (((i, string), names), value) in areas
.0
.strings
.iter()
.enumerate()
.zip(&mut names_iter)
.zip(track_list.values.iter())
{
if i > 0 {
dest.write_char(' ')?;
}
if !names.is_empty() {
concat_serialize_idents("[", "] ", names, " ", dest)?;
}
string.to_css(dest)?;
if !value.is_initial() {
dest.write_char(' ')?;
value.to_css(dest)?;
}
}
if let Some(names) = names_iter.next() {
concat_serialize_idents(" [", "]", names, " ", dest)?;
}
if let GenericGridTemplateComponent::TrackList(ref list) = *template_columns {
dest.write_str(" / ")?;
list.to_css(dest)?;
}
Ok(())
},
}
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
#[inline]
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
serialize_grid_template(
self.grid_template_rows,
self.grid_template_columns,
self.grid_template_areas,
dest,
)
}
}
}
pub mod grid {
pub use crate::properties::shorthands_generated::grid::*;
use super::*;
use crate::parser::Parse;
use crate::properties::longhands::{grid_auto_columns, grid_auto_flow, grid_auto_rows};
use crate::values::generics::grid::GridTemplateComponent;
use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas};
use crate::values::specified::{GenericGridTemplateComponent, ImplicitGridTracks};
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
let mut temp_rows = GridTemplateComponent::default();
let mut temp_cols = GridTemplateComponent::default();
let mut temp_areas = GridTemplateAreas::None;
let mut auto_rows = ImplicitGridTracks::default();
let mut auto_cols = ImplicitGridTracks::default();
let mut flow = grid_auto_flow::get_initial_value();
fn parse_auto_flow<'i, 't>(
input: &mut Parser<'i, 't>,
is_row: bool,
) -> Result<GridAutoFlow, ParseError<'i>> {
let mut track = None;
let mut dense = GridAutoFlow::empty();
for _ in 0..2 {
if input
.try_parse(|i| i.expect_ident_matching("auto-flow"))
.is_ok()
{
track = if is_row {
Some(GridAutoFlow::ROW)
} else {
Some(GridAutoFlow::COLUMN)
};
} else if input
.try_parse(|i| i.expect_ident_matching("dense"))
.is_ok()
{
dense = GridAutoFlow::DENSE
} else {
break;
}
}
if track.is_some() {
Ok(track.unwrap() | dense)
} else {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
}
}
if let Ok((rows, cols, areas)) =
input.try_parse(|i| super::grid_template::parse_grid_template(context, i))
{
temp_rows = rows;
temp_cols = cols;
temp_areas = areas;
} else if let Ok(rows) = input.try_parse(|i| GridTemplateComponent::parse(context, i)) {
temp_rows = rows;
input.expect_delim('/')?;
flow = parse_auto_flow(input, false)?;
auto_cols = input
.try_parse(|i| grid_auto_columns::parse(context, i))
.unwrap_or_default();
} else {
flow = parse_auto_flow(input, true)?;
auto_rows = input
.try_parse(|i| grid_auto_rows::parse(context, i))
.unwrap_or_default();
input.expect_delim('/')?;
temp_cols = GridTemplateComponent::parse(context, input)?;
}
Ok(expanded! {
grid_template_rows: temp_rows,
grid_template_columns: temp_cols,
grid_template_areas: temp_areas,
grid_auto_rows: auto_rows,
grid_auto_columns: auto_cols,
grid_auto_flow: flow,
})
}
impl<'a> LonghandsToSerialize<'a> {
fn is_grid_template(&self) -> bool {
self.grid_auto_rows.is_initial()
&& self.grid_auto_columns.is_initial()
&& *self.grid_auto_flow == grid_auto_flow::get_initial_value()
}
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
if self.is_grid_template() {
return super::grid_template::serialize_grid_template(
self.grid_template_rows,
self.grid_template_columns,
self.grid_template_areas,
dest,
);
}
if *self.grid_template_areas != GridTemplateAreas::None {
return Ok(());
}
if self.grid_auto_flow.contains(GridAutoFlow::COLUMN) {
if !self.grid_auto_rows.is_initial() || !self.grid_template_columns.is_initial() {
return Ok(());
}
if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_rows
{
if !list.is_explicit() {
return Ok(());
}
}
self.grid_template_rows.to_css(dest)?;
dest.write_str(" / auto-flow")?;
if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {
dest.write_str(" dense")?;
}
if !self.grid_auto_columns.is_initial() {
dest.write_char(' ')?;
self.grid_auto_columns.to_css(dest)?;
}
return Ok(());
}
if !self.grid_auto_columns.is_initial() || !self.grid_template_rows.is_initial() {
return Ok(());
}
if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_columns {
if !list.is_explicit() {
return Ok(());
}
}
dest.write_str("auto-flow")?;
if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {
dest.write_str(" dense")?;
}
if !self.grid_auto_rows.is_initial() {
dest.write_char(' ')?;
self.grid_auto_rows.to_css(dest)?;
}
dest.write_str(" / ")?;
self.grid_template_columns.to_css(dest)?;
Ok(())
}
}
}