Adding support for correct shorthand serialization

This commit is contained in:
David Raifaizen 2016-08-16 22:28:51 -04:00
parent 49431be44a
commit 3831c0650c
23 changed files with 1186 additions and 643 deletions

View file

@ -403,9 +403,11 @@
%> %>
% if shorthand: % if shorthand:
pub mod ${shorthand.ident} { pub mod ${shorthand.ident} {
use cssparser::Parser; #[allow(unused_imports)]
use cssparser::{Parser, ToCss};
use parser::ParserContext; use parser::ParserContext;
use properties::{longhands, PropertyDeclaration, DeclaredValue, Shorthand}; use properties::{longhands, PropertyDeclaration, DeclaredValue, Shorthand};
use std::fmt;
pub struct Longhands { pub struct Longhands {
% for sub_property in shorthand.sub_properties: % for sub_property in shorthand.sub_properties:
@ -414,6 +416,55 @@
% endfor % endfor
} }
/// Represents a serializable set of all of the longhand properties that correspond to a shorthand
pub struct LonghandsToSerialize<'a> {
% for sub_property in shorthand.sub_properties:
pub ${sub_property.ident}: &'a DeclaredValue<longhands::${sub_property.ident}::SpecifiedValue>,
% endfor
}
impl<'a> LonghandsToSerialize<'a> {
pub fn from_iter<I: Iterator<Item=&'a PropertyDeclaration>>(iter: I) -> Result<Self, ()> {
// Define all of the expected variables that correspond to the shorthand
% for sub_property in shorthand.sub_properties:
let mut ${sub_property.ident} = None;
% endfor
// Attempt to assign the incoming declarations to the expected variables
for longhand in iter {
match *longhand {
% for sub_property in shorthand.sub_properties:
PropertyDeclaration::${sub_property.camel_case}(ref value) => {
${sub_property.ident} = Some(value)
},
% endfor
_ => {}
};
}
// If any of the expected variables are missing, return an error
match (
% for sub_property in shorthand.sub_properties:
${sub_property.ident},
% endfor
) {
(
% for sub_property in shorthand.sub_properties:
Some(${sub_property.ident}),
% endfor
) =>
Ok(LonghandsToSerialize {
% for sub_property in shorthand.sub_properties:
${sub_property.ident}: ${sub_property.ident},
% endfor
}),
_ => Err(())
}
}
}
pub fn parse(context: &ParserContext, input: &mut Parser, pub fn parse(context: &ParserContext, input: &mut Parser,
declarations: &mut Vec<PropertyDeclaration>) declarations: &mut Vec<PropertyDeclaration>)
-> Result<(), ()> { -> Result<(), ()> {
@ -454,10 +505,7 @@
} }
} }
#[allow(unused_variables)] ${caller.body()}
pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
${caller.body()}
}
} }
% endif % endif
</%def> </%def>
@ -468,12 +516,26 @@
for side in ['top', 'right', 'bottom', 'left'])}"> for side in ['top', 'right', 'bottom', 'left'])}">
use super::parse_four_sides; use super::parse_four_sides;
use values::specified; use values::specified;
let _unused = context;
let (top, right, bottom, left) = try!(parse_four_sides(input, ${parser_function})); pub fn parse_value(_: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
Ok(Longhands { let (top, right, bottom, left) = try!(parse_four_sides(input, ${parser_function}));
% for side in ["top", "right", "bottom", "left"]: Ok(Longhands {
${to_rust_ident(sub_property_pattern % side)}: Some(${side}), % for side in ["top", "right", "bottom", "left"]:
% endfor ${to_rust_ident(sub_property_pattern % side)}: Some(${side}),
}) % endfor
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
super::serialize_four_sides(
dest,
self.${to_rust_ident(sub_property_pattern % 'top')},
self.${to_rust_ident(sub_property_pattern % 'right')},
self.${to_rust_ident(sub_property_pattern % 'bottom')},
self.${to_rust_ident(sub_property_pattern % 'left')}
)
}
}
</%self:shorthand> </%self:shorthand>
</%def> </%def>

View file

@ -6,7 +6,7 @@
// Please note that valid Rust syntax may be mangled by the Mako parser. // Please note that valid Rust syntax may be mangled by the Mako parser.
// For example, Vec<&Foo> will be mangled as Vec&Foo>. To work around these issues, the code // For example, Vec<&Foo> will be mangled as Vec&Foo>. To work around these issues, the code
// can be escaped. In the above example, Vec<<&Foo> achieves the desired result of Vec<&Foo>. // can be escaped. In the above example, Vec<<&Foo> or Vec< &Foo> achieves the desired result of Vec<&Foo>.
<%namespace name="helpers" file="/helpers.mako.rs" /> <%namespace name="helpers" file="/helpers.mako.rs" />
@ -74,8 +74,7 @@ pub mod longhands {
} }
pub mod shorthands { pub mod shorthands {
use cssparser::{Parser, ToCss}; use cssparser::Parser;
use std::fmt;
use parser::ParserContext; use parser::ParserContext;
use values::specified; use values::specified;
@ -122,33 +121,7 @@ pub mod shorthands {
Ok((top, right, bottom, left)) Ok((top, right, bottom, left))
} }
/// Serialize a set of top,left,bottom,right values, in <margin>-shorthand style, <%include file="/shorthand/serialize.mako.rs" />
/// attempting to minimize the output
pub fn serialize_four_sides<T, W>(sides: (&T, &T, &T, &T), dest: &mut W) -> fmt::Result
where W: fmt::Write, T: ToCss+PartialEq {
if sides.0 == sides.1 && sides.0 == sides.2 && sides.0 == sides.3 {
sides.0.to_css(dest)
} else if sides.0 == sides.2 && sides.1 == sides.3 {
try!(sides.0.to_css(dest));
try!(dest.write_str(" "));
sides.1.to_css(dest)
} else if sides.1 == sides.3 {
try!(sides.0.to_css(dest));
try!(dest.write_str(" "));
try!(sides.1.to_css(dest));
try!(dest.write_str(" "));
sides.2.to_css(dest)
} else {
try!(sides.0.to_css(dest));
try!(dest.write_str(" "));
try!(sides.1.to_css(dest));
try!(dest.write_str(" "));
try!(sides.2.to_css(dest));
try!(dest.write_str(" "));
sides.3.to_css(dest)
}
}
<%include file="/shorthand/background.mako.rs" /> <%include file="/shorthand/background.mako.rs" />
<%include file="/shorthand/border.mako.rs" /> <%include file="/shorthand/border.mako.rs" />
<%include file="/shorthand/box.mako.rs" /> <%include file="/shorthand/box.mako.rs" />
@ -435,18 +408,14 @@ impl ToCss for PropertyDeclarationBlock {
} }
} }
enum AppendableValue<'a, I> pub enum AppendableValue<'a, I>
where I: Iterator<Item=&'a PropertyDeclaration> { where I: Iterator<Item=&'a PropertyDeclaration> {
Declaration(&'a PropertyDeclaration), Declaration(&'a PropertyDeclaration),
DeclarationsForShorthand(I), DeclarationsForShorthand(Shorthand, I),
Css(&'a str) Css(&'a str)
} }
fn append_property_name<W>(dest: &mut W, fn handle_first_serialization<W>(dest: &mut W, is_first_serialization: &mut bool) -> fmt::Result where W: fmt::Write {
property_name: &str,
is_first_serialization: &mut bool)
-> fmt::Result where W: fmt::Write {
// after first serialization(key: value;) add whitespace between the pairs // after first serialization(key: value;) add whitespace between the pairs
if !*is_first_serialization { if !*is_first_serialization {
try!(write!(dest, " ")); try!(write!(dest, " "));
@ -455,7 +424,7 @@ fn append_property_name<W>(dest: &mut W,
*is_first_serialization = false; *is_first_serialization = false;
} }
write!(dest, "{}", property_name) Ok(())
} }
fn append_declaration_value<'a, W, I> fn append_declaration_value<'a, W, I>
@ -471,15 +440,8 @@ fn append_declaration_value<'a, W, I>
AppendableValue::Declaration(decl) => { AppendableValue::Declaration(decl) => {
try!(decl.to_css(dest)); try!(decl.to_css(dest));
}, },
AppendableValue::DeclarationsForShorthand(decls) => { AppendableValue::DeclarationsForShorthand(shorthand, decls) => {
let mut decls = decls.peekable(); try!(shorthand.longhands_to_css(decls, dest));
while let Some(decl) = decls.next() {
try!(decl.to_css(dest));
if decls.peek().is_some() {
try!(write!(dest, " "));
}
}
} }
} }
@ -498,8 +460,15 @@ fn append_serialization<'a, W, I>(dest: &mut W,
-> fmt::Result -> fmt::Result
where W: fmt::Write, I: Iterator<Item=&'a PropertyDeclaration> { where W: fmt::Write, I: Iterator<Item=&'a PropertyDeclaration> {
try!(append_property_name(dest, property_name, is_first_serialization)); try!(handle_first_serialization(dest, is_first_serialization));
try!(write!(dest, ":"));
// Overflow does not behave like a normal shorthand. When overflow-x and overflow-y are not of equal
// values, they no longer use the shared property name "overflow" and must be handled differently
if shorthands::is_overflow_shorthand(&appendable_value) {
return append_declaration_value(dest, appendable_value, is_important);
}
write!(dest, "{}:", property_name);
// for normal parsed values, add a space between key: and value // for normal parsed values, add a space between key: and value
match &appendable_value { match &appendable_value {
@ -512,7 +481,7 @@ fn append_serialization<'a, W, I>(dest: &mut W,
try!(write!(dest, " ")); try!(write!(dest, " "));
} }
}, },
&AppendableValue::DeclarationsForShorthand(_) => try!(write!(dest, " ")) &AppendableValue::DeclarationsForShorthand(..) => try!(write!(dest, " "))
} }
try!(append_declaration_value(dest, appendable_value, is_important)); try!(append_declaration_value(dest, appendable_value, is_important));
@ -691,6 +660,20 @@ impl Shorthand {
} }
} }
pub fn longhands_to_css<'a, W, I>(&self, declarations: I, dest: &mut W) -> fmt::Result
where W: fmt::Write, I: Iterator<Item=&'a PropertyDeclaration> {
match *self {
% for property in data.shorthands:
Shorthand::${property.camel_case} => {
match shorthands::${property.ident}::LonghandsToSerialize::from_iter(declarations) {
Ok(longhands) => longhands.to_css(dest),
Err(_) => Err(fmt::Error)
}
},
% endfor
}
}
/// Serializes possible shorthand value to String. /// Serializes possible shorthand value to String.
pub fn serialize_shorthand_value_to_string<'a, I>(self, declarations: I, is_important: bool) -> String pub fn serialize_shorthand_value_to_string<'a, I>(self, declarations: I, is_important: bool) -> String
where I: Iterator<Item=&'a PropertyDeclaration> + Clone { where I: Iterator<Item=&'a PropertyDeclaration> + Clone {
@ -747,10 +730,7 @@ impl Shorthand {
} }
if !declarations3.any(|d| d.with_variables()) { if !declarations3.any(|d| d.with_variables()) {
return Some(AppendableValue::DeclarationsForShorthand(declarations)); return Some(AppendableValue::DeclarationsForShorthand(self, declarations));
// FIXME: this needs property-specific code, which probably should be in style/
// "as appropriate according to the grammar of shorthand "
// https://drafts.csswg.org/cssom/#serialize-a-css-value
} }
None None

View file

@ -11,89 +11,176 @@
use properties::longhands::{background_color, background_position, background_repeat, background_attachment}; use properties::longhands::{background_color, background_position, background_repeat, background_attachment};
use properties::longhands::{background_image, background_size, background_origin, background_clip}; use properties::longhands::{background_image, background_size, background_origin, background_clip};
let mut color = None; pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
let mut image = None; let mut color = None;
let mut position = None; let mut image = None;
let mut repeat = None; let mut position = None;
let mut size = None; let mut repeat = None;
let mut attachment = None; let mut size = None;
let mut any = false; let mut attachment = None;
let mut origin = None; let mut any = false;
let mut clip = None; let mut origin = None;
let mut clip = None;
loop { loop {
if position.is_none() { if position.is_none() {
if let Ok(value) = input.try(|input| background_position::parse(context, input)) { if let Ok(value) = input.try(|input| background_position::parse(context, input)) {
position = Some(value); position = Some(value);
any = true; any = true;
// Parse background size, if applicable. // Parse background size, if applicable.
size = input.try(|input| { size = input.try(|input| {
try!(input.expect_delim('/')); try!(input.expect_delim('/'));
background_size::parse(context, input) background_size::parse(context, input)
}).ok(); }).ok();
continue continue
}
} }
} if color.is_none() {
if color.is_none() { if let Ok(value) = input.try(|input| background_color::parse(context, input)) {
if let Ok(value) = input.try(|input| background_color::parse(context, input)) { color = Some(value);
color = Some(value); any = true;
any = true; continue
continue }
} }
} if image.is_none() {
if image.is_none() { if let Ok(value) = input.try(|input| background_image::parse(context, input)) {
if let Ok(value) = input.try(|input| background_image::parse(context, input)) { image = Some(value);
image = Some(value); any = true;
any = true; continue
continue }
} }
} if repeat.is_none() {
if repeat.is_none() { if let Ok(value) = input.try(|input| background_repeat::parse(context, input)) {
if let Ok(value) = input.try(|input| background_repeat::parse(context, input)) { repeat = Some(value);
repeat = Some(value); any = true;
any = true; continue
continue }
} }
} if attachment.is_none() {
if attachment.is_none() { if let Ok(value) = input.try(|input| background_attachment::parse(context, input)) {
if let Ok(value) = input.try(|input| background_attachment::parse(context, input)) { attachment = Some(value);
attachment = Some(value); any = true;
any = true; continue
continue }
} }
} if origin.is_none() {
if origin.is_none() { if let Ok(value) = input.try(|input| background_origin::parse(context, input)) {
if let Ok(value) = input.try(|input| background_origin::parse(context, input)) { origin = Some(value);
origin = Some(value); any = true;
any = true; continue
continue }
} }
} if clip.is_none() {
if clip.is_none() { if let Ok(value) = input.try(|input| background_clip::parse(context, input)) {
if let Ok(value) = input.try(|input| background_clip::parse(context, input)) { clip = Some(value);
clip = Some(value); any = true;
any = true; continue
continue }
} }
break
}
if any {
Ok(Longhands {
background_color: color,
background_image: image,
background_position: position,
background_repeat: repeat,
background_attachment: attachment,
background_size: size,
background_origin: origin,
background_clip: clip,
})
} else {
Err(())
} }
break
} }
if any { impl<'a> ToCss for LonghandsToSerialize<'a> {
Ok(Longhands { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
background_color: color, match *self.background_color {
background_image: image, DeclaredValue::Value(ref color) => {
background_position: position, try!(color.to_css(dest));
background_repeat: repeat, },
background_attachment: attachment, _ => {
background_size: size, try!(write!(dest, "transparent"));
background_origin: origin, }
background_clip: clip, };
})
} else { try!(write!(dest, " "));
Err(())
match *self.background_image {
DeclaredValue::Value(ref image) => {
try!(image.to_css(dest));
},
_ => {
try!(write!(dest, "none"));
}
};
try!(write!(dest, " "));
try!(self.background_repeat.to_css(dest));
try!(write!(dest, " "));
match *self.background_attachment {
DeclaredValue::Value(ref attachment) => {
try!(attachment.to_css(dest));
},
_ => {
try!(write!(dest, "scroll"));
}
};
try!(write!(dest, " "));
try!(self.background_position.to_css(dest));
if let DeclaredValue::Value(ref size) = *self.background_size {
try!(write!(dest, " / "));
try!(size.to_css(dest));
}
match (self.background_origin, self.background_clip) {
(&DeclaredValue::Value(ref origin), &DeclaredValue::Value(ref clip)) => {
use properties::longhands::background_origin::computed_value::T as Origin;
use properties::longhands::background_clip::computed_value::T as Clip;
try!(write!(dest, " "));
match (origin, clip) {
(&Origin::padding_box, &Clip::padding_box) => {
try!(origin.to_css(dest));
},
(&Origin::border_box, &Clip::border_box) => {
try!(origin.to_css(dest));
},
(&Origin::content_box, &Clip::content_box) => {
try!(origin.to_css(dest));
},
_ => {
try!(origin.to_css(dest));
try!(write!(dest, " "));
try!(clip.to_css(dest));
}
}
},
(&DeclaredValue::Value(ref origin), _) => {
try!(write!(dest, " "));
try!(origin.to_css(dest));
},
(_, &DeclaredValue::Value(ref clip)) => {
try!(write!(dest, " "));
try!(clip.to_css(dest));
},
_ => {}
};
Ok(())
}
} }
</%helpers:shorthand> </%helpers:shorthand>

View file

@ -13,14 +13,39 @@ ${helpers.four_sides_shorthand("border-style", "border-%s-style",
for side in ['top', 'right', 'bottom', 'left'])}"> for side in ['top', 'right', 'bottom', 'left'])}">
use super::parse_four_sides; use super::parse_four_sides;
use values::specified; use values::specified;
let _unused = context;
let (top, right, bottom, left) = try!(parse_four_sides(input, specified::parse_border_width)); pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
Ok(Longhands { let _unused = context;
% for side in ["top", "right", "bottom", "left"]: let (top, right, bottom, left) = try!(parse_four_sides(input, specified::parse_border_width));
${to_rust_ident('border-%s-width' % side)}: Ok(Longhands {
Some(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue(${side})), % for side in ["top", "right", "bottom", "left"]:
% endfor ${to_rust_ident('border-%s-width' % side)}:
}) Some(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue(${side})),
% endfor
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
// extract tuple container values so that the different border widths
// can be compared via partial eq
% for side in ["top", "right", "bottom", "left"]:
let ${side} = match self.border_${side}_width {
&DeclaredValue::Value(ref value) => DeclaredValue::Value(value.0),
&DeclaredValue::WithVariables {
css: ref a, first_token_type: ref b, base_url: ref c, from_shorthand: ref d
} => DeclaredValue::WithVariables {
// WithVariables should not be reachable during serialization
css: a.clone(), first_token_type: b.clone(), base_url: c.clone(), from_shorthand: d.clone()
},
&DeclaredValue::Initial => DeclaredValue::Initial,
&DeclaredValue::Inherit => DeclaredValue::Inherit,
};
% endfor
super::serialize_four_sides(dest, &top, &right, &bottom, &left)
}
}
</%helpers:shorthand> </%helpers:shorthand>
@ -61,12 +86,13 @@ pub fn parse_border(context: &ParserContext, input: &mut Parser)
if any { Ok((color, style, width)) } else { Err(()) } if any { Ok((color, style, width)) } else { Err(()) }
} }
% for side in ["top", "right", "bottom", "left"]: % for side in ["top", "right", "bottom", "left"]:
<%helpers:shorthand name="border-${side}" sub_properties="${' '.join( <%helpers:shorthand name="border-${side}" sub_properties="${' '.join(
'border-%s-%s' % (side, prop) 'border-%s-%s' % (side, prop)
for prop in ['color', 'style', 'width'] for prop in ['color', 'style', 'width']
)}"> )}">
pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
let (color, style, width) = try!(super::parse_border(context, input)); let (color, style, width) = try!(super::parse_border(context, input));
Ok(Longhands { Ok(Longhands {
border_${side}_color: color, border_${side}_color: color,
@ -74,6 +100,19 @@ pub fn parse_border(context: &ParserContext, input: &mut Parser)
border_${side}_width: border_${side}_width:
width.map(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue), width.map(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue),
}) })
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
super::serialize_directional_border(
dest,
self.border_${side}_width,
self.border_${side}_style,
self.border_${side}_color
)
}
}
</%helpers:shorthand> </%helpers:shorthand>
% endfor % endfor
@ -82,15 +121,32 @@ pub fn parse_border(context: &ParserContext, input: &mut Parser)
for side in ['top', 'right', 'bottom', 'left'] for side in ['top', 'right', 'bottom', 'left']
for prop in ['color', 'style', 'width'] for prop in ['color', 'style', 'width']
)}"> )}">
let (color, style, width) = try!(super::parse_border(context, input));
Ok(Longhands { pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
% for side in ["top", "right", "bottom", "left"]: let (color, style, width) = try!(super::parse_border(context, input));
border_${side}_color: color.clone(), Ok(Longhands {
border_${side}_style: style, % for side in ["top", "right", "bottom", "left"]:
border_${side}_width: border_${side}_color: color.clone(),
width.map(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue), border_${side}_style: style,
% endfor border_${side}_width:
}) width.map(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue),
% endfor
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
// If all longhands are all present, then all sides should be the same,
// so we can just one set of color/style/width
super::serialize_directional_border(
dest,
self.border_${side}_width,
self.border_${side}_style,
self.border_${side}_color
)
}
}
</%helpers:shorthand> </%helpers:shorthand>
<%helpers:shorthand name="border-radius" sub_properties="${' '.join( <%helpers:shorthand name="border-radius" sub_properties="${' '.join(
@ -99,13 +155,33 @@ pub fn parse_border(context: &ParserContext, input: &mut Parser)
)}"> )}">
use values::specified::basic_shape::BorderRadius; use values::specified::basic_shape::BorderRadius;
let _ignored = context; pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
let _ignored = context;
let radii = try!(BorderRadius::parse(input)); let radii = try!(BorderRadius::parse(input));
Ok(Longhands { Ok(Longhands {
border_top_left_radius: Some(radii.top_left), border_top_left_radius: Some(radii.top_left),
border_top_right_radius: Some(radii.top_right), border_top_right_radius: Some(radii.top_right),
border_bottom_right_radius: Some(radii.bottom_right), border_bottom_right_radius: Some(radii.bottom_right),
border_bottom_left_radius: Some(radii.bottom_left), border_bottom_left_radius: Some(radii.bottom_left),
}) })
}
// TODO: I do not understand how border radius works with respect to the slashes /,
// so putting a default generic impl for now
// https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.border_top_left_radius.to_css(dest));
try!(write!(dest, " "));
try!(self.border_top_right_radius.to_css(dest));
try!(write!(dest, " "));
try!(self.border_bottom_right_radius.to_css(dest));
try!(write!(dest, " "));
self.border_bottom_left_radius.to_css(dest)
}
}
</%helpers:shorthand> </%helpers:shorthand>

View file

@ -7,11 +7,47 @@
<%helpers:shorthand name="overflow" sub_properties="overflow-x overflow-y"> <%helpers:shorthand name="overflow" sub_properties="overflow-x overflow-y">
use properties::longhands::{overflow_x, overflow_y}; use properties::longhands::{overflow_x, overflow_y};
let overflow = try!(overflow_x::parse(context, input)); pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
Ok(Longhands { let overflow = try!(overflow_x::parse(context, input));
overflow_x: Some(overflow), Ok(Longhands {
overflow_y: Some(overflow_y::SpecifiedValue(overflow)), overflow_x: Some(overflow),
}) overflow_y: Some(overflow_y::SpecifiedValue(overflow)),
})
}
// Overflow does not behave like a normal shorthand. When overflow-x and overflow-y are not of equal
// values, they no longer use the shared property name "overflow".
// Other shorthands do not include their name in the to_css method
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let x_and_y_equal = match (self.overflow_x, self.overflow_y) {
(&DeclaredValue::Value(ref x_value), &DeclaredValue::Value(ref y_container)) => {
*x_value == y_container.0
},
(&DeclaredValue::WithVariables { .. }, &DeclaredValue::WithVariables { .. }) => true,
(&DeclaredValue::Initial, &DeclaredValue::Initial) => true,
(&DeclaredValue::Inherit, &DeclaredValue::Inherit) => true,
_ => false
};
if x_and_y_equal {
try!(write!(dest, "overflow"));
try!(write!(dest, ": "));
try!(self.overflow_x.to_css(dest));
} else {
try!(write!(dest, "overflow-x"));
try!(write!(dest, ": "));
try!(self.overflow_x.to_css(dest));
try!(write!(dest, "; "));
try!(write!(dest, "overflow-y: "));
try!(self.overflow_y.to_css(dest));
}
write!(dest, ";")
}
}
</%helpers:shorthand> </%helpers:shorthand>
macro_rules! try_parse_one { macro_rules! try_parse_one {
@ -32,67 +68,84 @@ macro_rules! try_parse_one {
use properties::longhands::{transition_delay, transition_duration, transition_property}; use properties::longhands::{transition_delay, transition_duration, transition_property};
use properties::longhands::{transition_timing_function}; use properties::longhands::{transition_timing_function};
struct SingleTransition { pub fn parse_value(_: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
transition_property: transition_property::SingleSpecifiedValue, struct SingleTransition {
transition_duration: transition_duration::SingleSpecifiedValue, transition_property: transition_property::SingleSpecifiedValue,
transition_timing_function: transition_timing_function::SingleSpecifiedValue, transition_duration: transition_duration::SingleSpecifiedValue,
transition_delay: transition_delay::SingleSpecifiedValue, transition_timing_function: transition_timing_function::SingleSpecifiedValue,
} transition_delay: transition_delay::SingleSpecifiedValue,
fn parse_one_transition(input: &mut Parser) -> Result<SingleTransition,()> {
let (mut property, mut duration) = (None, None);
let (mut timing_function, mut delay) = (None, None);
loop {
try_parse_one!(input, property, transition_property);
try_parse_one!(input, duration, transition_duration);
try_parse_one!(input, timing_function, transition_timing_function);
try_parse_one!(input, delay, transition_delay);
break
} }
if let Some(property) = property { fn parse_one_transition(input: &mut Parser) -> Result<SingleTransition,()> {
Ok(SingleTransition { let (mut property, mut duration) = (None, None);
transition_property: property, let (mut timing_function, mut delay) = (None, None);
transition_duration: loop {
duration.unwrap_or_else(transition_duration::get_initial_single_value), try_parse_one!(input, property, transition_property);
transition_timing_function: try_parse_one!(input, duration, transition_duration);
timing_function.unwrap_or_else( try_parse_one!(input, timing_function, transition_timing_function);
transition_timing_function::get_initial_single_value), try_parse_one!(input, delay, transition_delay);
transition_delay:
delay.unwrap_or_else(transition_delay::get_initial_single_value), break
}
if let Some(property) = property {
Ok(SingleTransition {
transition_property: property,
transition_duration:
duration.unwrap_or_else(transition_duration::get_initial_single_value),
transition_timing_function:
timing_function.unwrap_or_else(
transition_timing_function::get_initial_single_value),
transition_delay:
delay.unwrap_or_else(transition_delay::get_initial_single_value),
})
} else {
Err(())
}
}
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
return Ok(Longhands {
transition_property: None,
transition_duration: None,
transition_timing_function: None,
transition_delay: None,
}) })
} else {
Err(())
} }
}
if input.try(|input| input.expect_ident_matching("none")).is_ok() { let results = try!(input.parse_comma_separated(parse_one_transition));
return Ok(Longhands { let (mut properties, mut durations) = (Vec::new(), Vec::new());
transition_property: None, let (mut timing_functions, mut delays) = (Vec::new(), Vec::new());
transition_duration: None, for result in results {
transition_timing_function: None, properties.push(result.transition_property);
transition_delay: None, durations.push(result.transition_duration);
timing_functions.push(result.transition_timing_function);
delays.push(result.transition_delay);
}
Ok(Longhands {
transition_property: Some(transition_property::SpecifiedValue(properties)),
transition_duration: Some(transition_duration::SpecifiedValue(durations)),
transition_timing_function:
Some(transition_timing_function::SpecifiedValue(timing_functions)),
transition_delay: Some(transition_delay::SpecifiedValue(delays)),
}) })
} }
let results = try!(input.parse_comma_separated(parse_one_transition)); impl<'a> ToCss for LonghandsToSerialize<'a> {
let (mut properties, mut durations) = (Vec::new(), Vec::new()); fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
let (mut timing_functions, mut delays) = (Vec::new(), Vec::new()); try!(self.transition_property.to_css(dest));
for result in results { try!(write!(dest, " "));
properties.push(result.transition_property);
durations.push(result.transition_duration);
timing_functions.push(result.transition_timing_function);
delays.push(result.transition_delay);
}
Ok(Longhands { try!(self.transition_duration.to_css(dest));
transition_property: Some(transition_property::SpecifiedValue(properties)), try!(write!(dest, " "));
transition_duration: Some(transition_duration::SpecifiedValue(durations)),
transition_timing_function: try!(self.transition_timing_function.to_css(dest));
Some(transition_timing_function::SpecifiedValue(timing_functions)), try!(write!(dest, " "));
transition_delay: Some(transition_delay::SpecifiedValue(delays)),
}) self.transition_delay.to_css(dest)
}
}
</%helpers:shorthand> </%helpers:shorthand>
<%helpers:shorthand name="animation" <%helpers:shorthand name="animation"
@ -104,111 +157,142 @@ macro_rules! try_parse_one {
use properties::longhands::{animation_delay, animation_iteration_count, animation_direction}; use properties::longhands::{animation_delay, animation_iteration_count, animation_direction};
use properties::longhands::{animation_fill_mode, animation_play_state}; use properties::longhands::{animation_fill_mode, animation_play_state};
struct SingleAnimation { pub fn parse_value(_: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
animation_name: animation_name::SingleSpecifiedValue, struct SingleAnimation {
animation_duration: animation_duration::SingleSpecifiedValue, animation_name: animation_name::SingleSpecifiedValue,
animation_timing_function: animation_timing_function::SingleSpecifiedValue, animation_duration: animation_duration::SingleSpecifiedValue,
animation_delay: animation_delay::SingleSpecifiedValue, animation_timing_function: animation_timing_function::SingleSpecifiedValue,
animation_iteration_count: animation_iteration_count::SingleSpecifiedValue, animation_delay: animation_delay::SingleSpecifiedValue,
animation_direction: animation_direction::SingleSpecifiedValue, animation_iteration_count: animation_iteration_count::SingleSpecifiedValue,
animation_fill_mode: animation_fill_mode::SingleSpecifiedValue, animation_direction: animation_direction::SingleSpecifiedValue,
animation_play_state: animation_play_state::SingleSpecifiedValue, animation_fill_mode: animation_fill_mode::SingleSpecifiedValue,
} animation_play_state: animation_play_state::SingleSpecifiedValue,
fn parse_one_animation(input: &mut Parser) -> Result<SingleAnimation,()> {
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 name = None;
// NB: Name must be the last one here so that keywords valid for other
// longhands are not interpreted as names.
//
// Also, duration must be before delay, see
// https://drafts.csswg.org/css-animations/#typedef-single-animation
loop {
try_parse_one!(input, duration, animation_duration);
try_parse_one!(input, timing_function, animation_timing_function);
try_parse_one!(input, delay, animation_delay);
try_parse_one!(input, iteration_count, animation_iteration_count);
try_parse_one!(input, direction, animation_direction);
try_parse_one!(input, fill_mode, animation_fill_mode);
try_parse_one!(input, play_state, animation_play_state);
try_parse_one!(input, name, animation_name);
break
} }
if let Some(name) = name { fn parse_one_animation(input: &mut Parser) -> Result<SingleAnimation,()> {
Ok(SingleAnimation { let mut duration = None;
animation_name: name, let mut timing_function = None;
animation_duration: let mut delay = None;
duration.unwrap_or_else(animation_duration::get_initial_single_value), let mut iteration_count = None;
animation_timing_function: let mut direction = None;
timing_function.unwrap_or_else(animation_timing_function::get_initial_single_value), let mut fill_mode = None;
animation_delay: let mut play_state = None;
delay.unwrap_or_else(animation_delay::get_initial_single_value), let mut name = None;
animation_iteration_count:
iteration_count.unwrap_or_else(animation_iteration_count::get_initial_single_value), // NB: Name must be the last one here so that keywords valid for other
animation_direction: // longhands are not interpreted as names.
direction.unwrap_or_else(animation_direction::get_initial_single_value), //
animation_fill_mode: // Also, duration must be before delay, see
fill_mode.unwrap_or_else(animation_fill_mode::get_initial_single_value), // https://drafts.csswg.org/css-animations/#typedef-single-animation
animation_play_state: loop {
play_state.unwrap_or_else(animation_play_state::get_initial_single_value), try_parse_one!(input, duration, animation_duration);
try_parse_one!(input, timing_function, animation_timing_function);
try_parse_one!(input, delay, animation_delay);
try_parse_one!(input, iteration_count, animation_iteration_count);
try_parse_one!(input, direction, animation_direction);
try_parse_one!(input, fill_mode, animation_fill_mode);
try_parse_one!(input, play_state, animation_play_state);
try_parse_one!(input, name, animation_name);
break
}
if let Some(name) = name {
Ok(SingleAnimation {
animation_name: name,
animation_duration:
duration.unwrap_or_else(animation_duration::get_initial_single_value),
animation_timing_function:
timing_function.unwrap_or_else(animation_timing_function::get_initial_single_value),
animation_delay:
delay.unwrap_or_else(animation_delay::get_initial_single_value),
animation_iteration_count:
iteration_count.unwrap_or_else(animation_iteration_count::get_initial_single_value),
animation_direction:
direction.unwrap_or_else(animation_direction::get_initial_single_value),
animation_fill_mode:
fill_mode.unwrap_or_else(animation_fill_mode::get_initial_single_value),
animation_play_state:
play_state.unwrap_or_else(animation_play_state::get_initial_single_value),
})
} else {
Err(())
}
}
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
return Ok(Longhands {
animation_name: None,
animation_duration: None,
animation_timing_function: None,
animation_delay: None,
animation_iteration_count: None,
animation_direction: None,
animation_fill_mode: None,
animation_play_state: None,
}) })
} else {
Err(())
} }
}
if input.try(|input| input.expect_ident_matching("none")).is_ok() { let results = try!(input.parse_comma_separated(parse_one_animation));
return Ok(Longhands {
animation_name: None, let mut names = vec![];
animation_duration: None, let mut durations = vec![];
animation_timing_function: None, let mut timing_functions = vec![];
animation_delay: None, let mut delays = vec![];
animation_iteration_count: None, let mut iteration_counts = vec![];
animation_direction: None, let mut directions = vec![];
animation_fill_mode: None, let mut fill_modes = vec![];
animation_play_state: None, let mut play_states = vec![];
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(Longhands {
animation_name: Some(animation_name::SpecifiedValue(names)),
animation_duration: Some(animation_duration::SpecifiedValue(durations)),
animation_timing_function: Some(animation_timing_function::SpecifiedValue(timing_functions)),
animation_delay: Some(animation_delay::SpecifiedValue(delays)),
animation_iteration_count: Some(animation_iteration_count::SpecifiedValue(iteration_counts)),
animation_direction: Some(animation_direction::SpecifiedValue(directions)),
animation_fill_mode: Some(animation_fill_mode::SpecifiedValue(fill_modes)),
animation_play_state: Some(animation_play_state::SpecifiedValue(play_states)),
}) })
} }
let results = try!(input.parse_comma_separated(parse_one_animation)); impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.animation_duration.to_css(dest));
try!(write!(dest, " "));
let mut names = vec![]; // FIXME: timing function is displaying the actual mathematical name "cubic-bezier(0.25, 0.1, 0.25, 1)"
let mut durations = vec![]; // instead of the common name "ease"
let mut timing_functions = vec![]; try!(self.animation_timing_function.to_css(dest));
let mut delays = vec![]; try!(write!(dest, " "));
let mut iteration_counts = vec![];
let mut directions = vec![];
let mut fill_modes = vec![];
let mut play_states = vec![];
for result in results.into_iter() { try!(self.animation_delay.to_css(dest));
names.push(result.animation_name); try!(write!(dest, " "));
durations.push(result.animation_duration);
timing_functions.push(result.animation_timing_function); try!(self.animation_direction.to_css(dest));
delays.push(result.animation_delay); try!(write!(dest, " "));
iteration_counts.push(result.animation_iteration_count);
directions.push(result.animation_direction); try!(self.animation_fill_mode.to_css(dest));
fill_modes.push(result.animation_fill_mode); try!(write!(dest, " "));
play_states.push(result.animation_play_state);
try!(self.animation_iteration_count.to_css(dest));
try!(write!(dest, " "));
try!(self.animation_play_state.to_css(dest));
try!(write!(dest, " "));
self.animation_name.to_css(dest)
}
} }
Ok(Longhands {
animation_name: Some(animation_name::SpecifiedValue(names)),
animation_duration: Some(animation_duration::SpecifiedValue(durations)),
animation_timing_function: Some(animation_timing_function::SpecifiedValue(timing_functions)),
animation_delay: Some(animation_delay::SpecifiedValue(delays)),
animation_iteration_count: Some(animation_iteration_count::SpecifiedValue(iteration_counts)),
animation_direction: Some(animation_direction::SpecifiedValue(directions)),
animation_fill_mode: Some(animation_fill_mode::SpecifiedValue(fill_modes)),
animation_play_state: Some(animation_play_state::SpecifiedValue(play_states)),
})
</%helpers:shorthand> </%helpers:shorthand>

View file

@ -6,41 +6,54 @@
<%helpers:shorthand name="columns" sub_properties="column-count column-width" experimental="True"> <%helpers:shorthand name="columns" sub_properties="column-count column-width" experimental="True">
use properties::longhands::{column_count, column_width}; use properties::longhands::{column_count, column_width};
let mut column_count = None;
let mut column_width = None;
let mut autos = 0;
loop { pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
if input.try(|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() { let mut column_count = None;
if let Ok(value) = input.try(|input| column_count::parse(context, input)) { let mut column_width = None;
column_count = Some(value); let mut autos = 0;
loop {
if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
// Leave the options to None, 'auto' is the initial value.
autos += 1;
continue continue
} }
}
if column_width.is_none() { if column_count.is_none() {
if let Ok(value) = input.try(|input| column_width::parse(context, input)) { if let Ok(value) = input.try(|input| column_count::parse(context, input)) {
column_width = Some(value); column_count = Some(value);
continue continue
}
} }
if column_width.is_none() {
if let Ok(value) = input.try(|input| column_width::parse(context, input)) {
column_width = Some(value);
continue
}
}
break
} }
break let values = autos + column_count.iter().len() + column_width.iter().len();
if values == 0 || values > 2 {
Err(())
} else {
Ok(Longhands {
column_count: column_count,
column_width: column_width,
})
}
} }
let values = autos + column_count.iter().len() + column_width.iter().len(); impl<'a> ToCss for LonghandsToSerialize<'a> {
if values == 0 || values > 2 { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
Err(()) try!(self.column_width.to_css(dest));
} else { try!(write!(dest, " "));
Ok(Longhands {
column_count: column_count, self.column_count.to_css(dest)
column_width: column_width, }
})
} }
</%helpers:shorthand> </%helpers:shorthand>

View file

@ -8,59 +8,97 @@
font-size line-height font-family"> font-size line-height font-family">
use properties::longhands::{font_style, font_variant, font_weight, font_size, use properties::longhands::{font_style, font_variant, font_weight, font_size,
line_height, font_family}; line_height, font_family};
let mut nb_normals = 0;
let mut style = None; pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
let mut variant = None; let mut nb_normals = 0;
let mut weight = None; let mut style = None;
let size; let mut variant = None;
loop { let mut weight = None;
// Special-case 'normal' because it is valid in each of let size;
// font-style, font-weight and font-variant. loop {
// Leaves the values to None, 'normal' is the initial value for each of them. // Special-case 'normal' because it is valid in each of
if input.try(|input| input.expect_ident_matching("normal")).is_ok() { // font-style, font-weight and font-variant.
nb_normals += 1; // Leaves the values to None, 'normal' is the initial value for each of them.
continue; if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
} nb_normals += 1;
if style.is_none() { continue;
if let Ok(value) = input.try(|input| font_style::parse(context, input)) {
style = Some(value);
continue
} }
} if style.is_none() {
if weight.is_none() { if let Ok(value) = input.try(|input| font_style::parse(context, input)) {
if let Ok(value) = input.try(|input| font_weight::parse(context, input)) { style = Some(value);
weight = Some(value); continue
continue }
} }
} if weight.is_none() {
if variant.is_none() { if let Ok(value) = input.try(|input| font_weight::parse(context, input)) {
if let Ok(value) = input.try(|input| font_variant::parse(context, input)) { weight = Some(value);
variant = Some(value); continue
continue }
} }
if variant.is_none() {
if let Ok(value) = input.try(|input| font_variant::parse(context, input)) {
variant = Some(value);
continue
}
}
size = Some(try!(font_size::parse(context, input)));
break
} }
size = Some(try!(font_size::parse(context, input))); #[inline]
break fn count<T>(opt: &Option<T>) -> u8 {
if opt.is_some() { 1 } else { 0 }
}
if size.is_none() || (count(&style) + count(&weight) + count(&variant) + nb_normals) > 3 {
return Err(())
}
let line_height = if input.try(|input| input.expect_delim('/')).is_ok() {
Some(try!(line_height::parse(context, input)))
} else {
None
};
let family = try!(input.parse_comma_separated(font_family::parse_one_family));
Ok(Longhands {
font_style: style,
font_variant: variant,
font_weight: weight,
font_size: size,
line_height: line_height,
font_family: Some(font_family::SpecifiedValue(family))
})
} }
#[inline]
fn count<T>(opt: &Option<T>) -> u8 { // This may be a bit off, unsure, possibly needs changes
if opt.is_some() { 1 } else { 0 } impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
if let DeclaredValue::Value(ref style) = *self.font_style {
try!(style.to_css(dest));
try!(write!(dest, " "));
}
if let DeclaredValue::Value(ref variant) = *self.font_variant {
try!(variant.to_css(dest));
try!(write!(dest, " "));
}
if let DeclaredValue::Value(ref weight) = *self.font_weight {
try!(weight.to_css(dest));
try!(write!(dest, " "));
}
try!(self.font_size.to_css(dest));
if let DeclaredValue::Value(ref height) = *self.line_height {
match *height {
line_height::SpecifiedValue::Normal => {},
_ => {
try!(write!(dest, "/"));
try!(height.to_css(dest));
}
}
}
try!(write!(dest, " "));
self.font_family.to_css(dest)
}
} }
if size.is_none() || (count(&style) + count(&weight) + count(&variant) + nb_normals) > 3 {
return Err(())
}
let line_height = if input.try(|input| input.expect_delim('/')).is_ok() {
Some(try!(line_height::parse(context, input)))
} else {
None
};
let family = try!(input.parse_comma_separated(font_family::parse_one_family));
Ok(Longhands {
font_style: style,
font_variant: variant,
font_weight: weight,
font_size: size,
line_height: line_height,
font_family: Some(font_family::SpecifiedValue(family))
})
</%helpers:shorthand> </%helpers:shorthand>

View file

@ -8,7 +8,16 @@
// the `overflow-wrap` property, as if it were a shorthand of `overflow-wrap`." // the `overflow-wrap` property, as if it were a shorthand of `overflow-wrap`."
<%helpers:shorthand name="word-wrap" sub_properties="overflow-wrap"> <%helpers:shorthand name="word-wrap" sub_properties="overflow-wrap">
use properties::longhands::overflow_wrap; use properties::longhands::overflow_wrap;
Ok(Longhands {
overflow_wrap: Some(try!(overflow_wrap::parse(context, input))), pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
}) Ok(Longhands {
overflow_wrap: Some(try!(overflow_wrap::parse(context, input))),
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
self.overflow_wrap.to_css(dest)
}
}
</%helpers:shorthand> </%helpers:shorthand>

View file

@ -8,85 +8,110 @@
sub_properties="list-style-image list-style-position list-style-type"> sub_properties="list-style-image list-style-position list-style-type">
use properties::longhands::{list_style_image, list_style_position, list_style_type}; use properties::longhands::{list_style_image, list_style_position, list_style_type};
// `none` is ambiguous until we've finished parsing the shorthands, so we count the number pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
// of times we see it. // `none` is ambiguous until we've finished parsing the shorthands, so we count the number
let mut nones = 0u8; // of times we see it.
let (mut image, mut position, mut list_style_type, mut any) = (None, None, None, false); let mut nones = 0u8;
loop { let (mut image, mut position, mut list_style_type, mut any) = (None, None, None, false);
if input.try(|input| input.expect_ident_matching("none")).is_ok() { loop {
nones = nones + 1; if input.try(|input| input.expect_ident_matching("none")).is_ok() {
if nones > 2 { nones = nones + 1;
return Err(()) if nones > 2 {
} return Err(())
any = true; }
continue
}
if list_style_type.is_none() {
if let Ok(value) = input.try(|input| list_style_type::parse(context, input)) {
list_style_type = Some(value);
any = true; any = true;
continue continue
} }
if list_style_type.is_none() {
if let Ok(value) = input.try(|input| list_style_type::parse(context, input)) {
list_style_type = Some(value);
any = true;
continue
}
}
if image.is_none() {
if let Ok(value) = input.try(|input| list_style_image::parse(context, input)) {
image = Some(value);
any = true;
continue
}
}
if position.is_none() {
if let Ok(value) = input.try(|input| list_style_position::parse(context, input)) {
position = Some(value);
any = true;
continue
}
}
break
} }
if image.is_none() { // If there are two `none`s, then we can't have a type or image; if there is one `none`,
if let Ok(value) = input.try(|input| list_style_image::parse(context, input)) { // then we can't have both a type *and* an image; if there is no `none` then we're fine as
image = Some(value); // long as we parsed something.
any = true; match (any, nones, list_style_type, image) {
continue (true, 2, None, None) => {
Ok(Longhands {
list_style_position: position,
list_style_image: Some(list_style_image::SpecifiedValue::None),
list_style_type: Some(list_style_type::SpecifiedValue::none),
})
} }
} (true, 1, None, Some(image)) => {
Ok(Longhands {
if position.is_none() { list_style_position: position,
if let Ok(value) = input.try(|input| list_style_position::parse(context, input)) { list_style_image: Some(image),
position = Some(value); list_style_type: Some(list_style_type::SpecifiedValue::none),
any = true; })
continue
} }
(true, 1, Some(list_style_type), None) => {
Ok(Longhands {
list_style_position: position,
list_style_image: Some(list_style_image::SpecifiedValue::None),
list_style_type: Some(list_style_type),
})
}
(true, 1, None, None) => {
Ok(Longhands {
list_style_position: position,
list_style_image: Some(list_style_image::SpecifiedValue::None),
list_style_type: Some(list_style_type::SpecifiedValue::none),
})
}
(true, 0, list_style_type, image) => {
Ok(Longhands {
list_style_position: position,
list_style_image: image,
list_style_type: list_style_type,
})
}
_ => Err(()),
} }
break
} }
// If there are two `none`s, then we can't have a type or image; if there is one `none`, impl<'a> ToCss for LonghandsToSerialize<'a> {
// then we can't have both a type *and* an image; if there is no `none` then we're fine as fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
// long as we parsed something. match *self.list_style_position {
match (any, nones, list_style_type, image) { DeclaredValue::Initial => try!(write!(dest, "outside")),
(true, 2, None, None) => { _ => try!(self.list_style_position.to_css(dest))
Ok(Longhands { }
list_style_position: position,
list_style_image: Some(list_style_image::SpecifiedValue::None), try!(write!(dest, " "));
list_style_type: Some(list_style_type::SpecifiedValue::none),
}) match *self.list_style_image {
DeclaredValue::Initial => try!(write!(dest, "none")),
_ => try!(self.list_style_image.to_css(dest))
};
try!(write!(dest, " "));
match *self.list_style_type {
DeclaredValue::Initial => write!(dest, "disc"),
_ => self.list_style_type.to_css(dest)
}
} }
(true, 1, None, Some(image)) => {
Ok(Longhands {
list_style_position: position,
list_style_image: Some(image),
list_style_type: Some(list_style_type::SpecifiedValue::none),
})
}
(true, 1, Some(list_style_type), None) => {
Ok(Longhands {
list_style_position: position,
list_style_image: Some(list_style_image::SpecifiedValue::None),
list_style_type: Some(list_style_type),
})
}
(true, 1, None, None) => {
Ok(Longhands {
list_style_position: position,
list_style_image: Some(list_style_image::SpecifiedValue::None),
list_style_type: Some(list_style_type::SpecifiedValue::none),
})
}
(true, 0, list_style_type, image) => {
Ok(Longhands {
list_style_position: position,
list_style_image: image,
list_style_type: list_style_type,
})
}
_ => Err(()),
} }
</%helpers:shorthand> </%helpers:shorthand>

View file

@ -8,43 +8,65 @@
use properties::longhands::outline_width; use properties::longhands::outline_width;
use values::specified; use values::specified;
let _unused = context; pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
let mut color = None; let _unused = context;
let mut style = None; let mut color = None;
let mut width = None; let mut style = None;
let mut any = false; let mut width = None;
loop { let mut any = false;
if color.is_none() { loop {
if let Ok(value) = input.try(specified::CSSColor::parse) { if color.is_none() {
color = Some(value); if let Ok(value) = input.try(specified::CSSColor::parse) {
any = true; color = Some(value);
continue any = true;
continue
}
} }
} if style.is_none() {
if style.is_none() { if let Ok(value) = input.try(specified::BorderStyle::parse) {
if let Ok(value) = input.try(specified::BorderStyle::parse) { style = Some(value);
style = Some(value); any = true;
any = true; continue
continue }
} }
} if width.is_none() {
if width.is_none() { if let Ok(value) = input.try(|input| outline_width::parse(context, input)) {
if let Ok(value) = input.try(|input| outline_width::parse(context, input)) { width = Some(value);
width = Some(value); any = true;
any = true; continue
continue }
} }
break
}
if any {
Ok(Longhands {
outline_color: color,
outline_style: style,
outline_width: width,
})
} else {
Err(())
} }
break
} }
if any {
Ok(Longhands { impl<'a> ToCss for LonghandsToSerialize<'a> {
outline_color: color, fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
outline_style: style, try!(self.outline_width.to_css(dest));
outline_width: width, try!(write!(dest, " "));
})
} else { match *self.outline_style {
Err(()) DeclaredValue::Initial => try!(write!(dest, "none")),
_ => try!(self.outline_style.to_css(dest))
};
match *self.outline_color {
DeclaredValue::Initial => Ok(()),
_ => {
try!(write!(dest, " "));
self.outline_color.to_css(dest)
}
}
}
} }
</%helpers:shorthand> </%helpers:shorthand>
@ -55,12 +77,30 @@
)}" products="gecko"> )}" products="gecko">
use properties::shorthands; use properties::shorthands;
// Re-use border-radius parsing. pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
shorthands::border_radius::parse_value(context, input).map(|longhands| { // Re-use border-radius parsing.
Longhands { shorthands::border_radius::parse_value(context, input).map(|longhands| {
% for corner in ["top_left", "top_right", "bottom_right", "bottom_left"]: Longhands {
_moz_outline_radius_${corner.replace("_", "")}: longhands.border_${corner}_radius, % for corner in ["top_left", "top_right", "bottom_right", "bottom_left"]:
% endfor _moz_outline_radius_${corner.replace("_", "")}: longhands.border_${corner}_radius,
% endfor
}
})
}
// TODO: Border radius for the radius shorthand is not implemented correctly yet
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self._moz_outline_radius_topleft.to_css(dest));
try!(write!(dest, " "));
try!(self._moz_outline_radius_topright.to_css(dest));
try!(write!(dest, " "));
try!(self._moz_outline_radius_bottomright.to_css(dest));
try!(write!(dest, " "));
self._moz_outline_radius_bottomleft.to_css(dest)
} }
}) }
</%helpers:shorthand> </%helpers:shorthand>

View file

@ -9,31 +9,50 @@
experimental="True"> experimental="True">
use properties::longhands::{flex_direction, flex_wrap}; use properties::longhands::{flex_direction, flex_wrap};
let mut direction = None; pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
let mut wrap = None; let mut direction = None;
loop { let mut wrap = None;
if direction.is_none() { loop {
if let Ok(value) = input.try(|input| flex_direction::parse(context, input)) { if direction.is_none() {
direction = Some(value); if let Ok(value) = input.try(|input| flex_direction::parse(context, input)) {
continue direction = Some(value);
continue
}
} }
} if wrap.is_none() {
if wrap.is_none() { if let Ok(value) = input.try(|input| flex_wrap::parse(context, input)) {
if let Ok(value) = input.try(|input| flex_wrap::parse(context, input)) { wrap = Some(value);
wrap = Some(value); continue
continue }
} }
break
} }
break
if direction.is_none() && wrap.is_none() {
return Err(())
}
Ok(Longhands {
flex_direction: direction,
flex_wrap: wrap,
})
} }
if direction.is_none() && wrap.is_none() {
return Err(()) impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self.flex_direction {
DeclaredValue::Initial => try!(write!(dest, "row")),
_ => try!(self.flex_direction.to_css(dest))
};
try!(write!(dest, " "));
match *self.flex_wrap {
DeclaredValue::Initial => write!(dest, "nowrap"),
_ => self.flex_wrap.to_css(dest)
}
}
} }
Ok(Longhands {
flex_direction: direction,
flex_wrap: wrap,
})
</%helpers:shorthand> </%helpers:shorthand>
// https://drafts.csswg.org/css-flexbox/#flex-property // https://drafts.csswg.org/css-flexbox/#flex-property
@ -49,40 +68,54 @@
Ok((grow, shrink)) Ok((grow, shrink))
} }
let mut grow = None; pub fn parse_value(_: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
let mut shrink = None; let mut grow = None;
let mut basis = None; let mut shrink = None;
let mut basis = None;
if input.try(|input| input.expect_ident_matching("none")).is_ok() { if input.try(|input| input.expect_ident_matching("none")).is_ok() {
return Ok(Longhands { return Ok(Longhands {
flex_grow: Some(Number(0.0)), flex_grow: Some(Number(0.0)),
flex_shrink: Some(Number(0.0)), flex_shrink: Some(Number(0.0)),
flex_basis: Some(LengthOrPercentageOrAutoOrContent::Auto) flex_basis: Some(LengthOrPercentageOrAutoOrContent::Auto)
})
}
loop {
if grow.is_none() {
if let Ok((flex_grow, flex_shrink)) = input.try(parse_flexibility) {
grow = Some(flex_grow);
shrink = flex_shrink;
continue
}
}
if basis.is_none() {
if let Ok(value) = input.try(LengthOrPercentageOrAutoOrContent::parse) {
basis = Some(value);
continue
}
}
break
}
if grow.is_none() && basis.is_none() {
return Err(())
}
Ok(Longhands {
flex_grow: grow.or(Some(Number(1.0))),
flex_shrink: shrink.or(Some(Number(1.0))),
flex_basis: basis.or(Some(LengthOrPercentageOrAutoOrContent::Length(Length::Absolute(Au(0)))))
}) })
} }
loop {
if grow.is_none() {
if let Ok((flex_grow, flex_shrink)) = input.try(parse_flexibility) {
grow = Some(flex_grow);
shrink = flex_shrink;
continue
}
}
if basis.is_none() {
if let Ok(value) = input.try(LengthOrPercentageOrAutoOrContent::parse) {
basis = Some(value);
continue
}
}
break
}
if grow.is_none() && basis.is_none() { impl<'a> ToCss for LonghandsToSerialize<'a> {
return Err(()) fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.flex_grow.to_css(dest));
try!(write!(dest, " "));
try!(self.flex_shrink.to_css(dest));
try!(write!(dest, " "));
self.flex_basis.to_css(dest)
}
} }
Ok(Longhands {
flex_grow: grow.or(Some(Number(1.0))),
flex_shrink: shrink.or(Some(Number(1.0))),
flex_basis: basis.or(Some(LengthOrPercentageOrAutoOrContent::Length(Length::Absolute(Au(0)))))
})
</%helpers:shorthand> </%helpers:shorthand>

View file

@ -0,0 +1,97 @@
/* 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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cssparser::ToCss;
use properties::{AppendableValue, DeclaredValue, PropertyDeclaration, Shorthand};
use values::specified::{BorderStyle, CSSColor};
use std::fmt;
pub fn serialize_four_sides<W, I>(dest: &mut W, top: &I, right: &I, bottom: &I, left: &I)
-> fmt::Result where W: fmt::Write, I: ToCss + PartialEq {
if left == right {
let horizontal_value = left;
if top == bottom {
let vertical_value = top;
if horizontal_value == vertical_value {
let single_value = horizontal_value;
try!(single_value.to_css(dest));
} else {
try!(vertical_value.to_css(dest));
try!(write!(dest, " "));
try!(horizontal_value.to_css(dest));
}
} else {
try!(top.to_css(dest));
try!(write!(dest, " "));
try!(horizontal_value.to_css(dest));
try!(write!(dest, " "));
try!(bottom.to_css(dest));
}
} else {
try!(top.to_css(dest));
try!(write!(dest, " "));
try!(right.to_css(dest));
try!(write!(dest, " "));
try!(bottom.to_css(dest));
try!(write!(dest, " "));
try!(left.to_css(dest));
}
Ok(())
}
fn serialize_directional_border<W, I>(dest: &mut W,
width: &DeclaredValue<I>,
style: &DeclaredValue<BorderStyle>,
color: &DeclaredValue<CSSColor>)
-> fmt::Result where W: fmt::Write, I: ToCss {
match *width {
DeclaredValue::Value(ref width) => {
try!(width.to_css(dest));
},
_ => {
try!(write!(dest, "medium"));
}
};
try!(write!(dest, " "));
match *style {
DeclaredValue::Value(ref style) => {
try!(style.to_css(dest));
},
_ => {
try!(write!(dest, "none"));
}
};
match *color {
DeclaredValue::Value(ref color) => {
try!(write!(dest, " "));
color.to_css(dest)
},
_ => Ok(())
}
}
pub fn is_overflow_shorthand<'a, I>(appendable_value: &AppendableValue<'a, I>) -> bool
where I: Iterator<Item=&'a PropertyDeclaration> {
if let AppendableValue::DeclarationsForShorthand(shorthand, _) = *appendable_value {
if let Shorthand::Overflow = shorthand {
return true;
}
}
false
}

View file

@ -13,34 +13,61 @@
use properties::longhands::{text_decoration_color, text_decoration_line, text_decoration_style}; use properties::longhands::{text_decoration_color, text_decoration_line, text_decoration_style};
use values::specified::CSSColor; use values::specified::CSSColor;
let (mut color, mut line, mut style, mut any) = (None, None, None, false); pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
loop { let (mut color, mut line, mut style, mut any) = (None, None, None, false);
macro_rules! parse_component { loop {
($value:ident, $module:ident) => ( macro_rules! parse_component {
if $value.is_none() { ($value:ident, $module:ident) => (
if let Ok(value) = input.try(|input| $module::parse(context, input)) { if $value.is_none() {
$value = Some(value); if let Ok(value) = input.try(|input| $module::parse(context, input)) {
any = true; $value = Some(value);
continue; any = true;
continue;
}
} }
} )
) }
parse_component!(color, text_decoration_color);
parse_component!(line, text_decoration_line);
parse_component!(style, text_decoration_style);
break;
} }
parse_component!(color, text_decoration_color); if !any {
parse_component!(line, text_decoration_line); return Err(());
parse_component!(style, text_decoration_style); }
break;
Ok(Longhands {
text_decoration_color: color.or(Some(CSSColor { parsed: CSSParserColor::CurrentColor,
authored: None })),
text_decoration_line: line.or(Some(text_decoration_line::computed_value::none)),
text_decoration_style: style.or(Some(text_decoration_style::computed_value::T::solid)),
})
} }
if !any { impl<'a> ToCss for LonghandsToSerialize<'a> {
return Err(()); fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
} match *self.text_decoration_line {
DeclaredValue::Value(ref line) => {
try!(line.to_css(dest));
},
_ => {
try!(write!(dest, "none"));
}
};
Ok(Longhands { if let DeclaredValue::Value(ref style) = *self.text_decoration_style {
text_decoration_color: color.or(Some(CSSColor { parsed: CSSParserColor::CurrentColor, try!(write!(dest, " "));
authored: None })), try!(style.to_css(dest));
text_decoration_line: line.or(Some(text_decoration_line::computed_value::none)), }
text_decoration_style: style.or(Some(text_decoration_style::computed_value::T::solid)),
}) if let DeclaredValue::Value(ref color) = *self.text_decoration_color {
try!(write!(dest, " "));
try!(color.to_css(dest));
}
Ok(())
}
}
</%helpers:shorthand> </%helpers:shorthand>

View file

@ -172,23 +172,23 @@ impl ToCss for BorderRadius {
self.top_right.0.width == self.top_right.0.height && self.top_right.0.width == self.top_right.0.height &&
self.bottom_right.0.width == self.bottom_right.0.height && self.bottom_right.0.width == self.bottom_right.0.height &&
self.bottom_left.0.width == self.bottom_left.0.height { self.bottom_left.0.width == self.bottom_left.0.height {
serialize_four_sides((&self.top_left.0.width, serialize_four_sides(dest,
&self.top_right.0.width, &self.top_left.0.width,
&self.bottom_right.0.width, &self.top_right.0.width,
&self.bottom_left.0.width), &self.bottom_right.0.width,
dest) &self.bottom_left.0.width)
} else { } else {
try!(serialize_four_sides((&self.top_left.0.width, try!(serialize_four_sides(dest,
&self.top_right.0.width, &self.top_left.0.width,
&self.bottom_right.0.width, &self.top_right.0.width,
&self.bottom_left.0.width), &self.bottom_right.0.width,
dest)); &self.bottom_left.0.width));
try!(dest.write_str(" / ")); try!(dest.write_str(" / "));
serialize_four_sides((&self.top_left.0.height, serialize_four_sides(dest,
&self.top_right.0.height, &self.top_left.0.height,
&self.bottom_right.0.height, &self.top_right.0.height,
&self.bottom_left.0.height), &self.bottom_right.0.height,
dest) &self.bottom_left.0.height)
} }
} }
} }

View file

@ -415,23 +415,23 @@ impl ToCss for BorderRadius {
self.top_right.0.width == self.top_right.0.height && self.top_right.0.width == self.top_right.0.height &&
self.bottom_right.0.width == self.bottom_right.0.height && self.bottom_right.0.width == self.bottom_right.0.height &&
self.bottom_left.0.width == self.bottom_left.0.height { self.bottom_left.0.width == self.bottom_left.0.height {
serialize_four_sides((&self.top_left.0.width, serialize_four_sides(dest,
&self.top_right.0.width, &self.top_left.0.width,
&self.bottom_right.0.width, &self.top_right.0.width,
&self.bottom_left.0.width), &self.bottom_right.0.width,
dest) &self.bottom_left.0.width)
} else { } else {
try!(serialize_four_sides((&self.top_left.0.width, try!(serialize_four_sides(dest,
&self.top_right.0.width, &self.top_left.0.width,
&self.bottom_right.0.width, &self.top_right.0.width,
&self.bottom_left.0.width), &self.bottom_right.0.width,
dest)); &self.bottom_left.0.width));
try!(dest.write_str(" / ")); try!(dest.write_str(" / "));
serialize_four_sides((&self.top_left.0.height, serialize_four_sides(dest,
&self.top_right.0.height, &self.top_left.0.height,
&self.bottom_right.0.height, &self.top_right.0.height,
&self.bottom_left.0.height), &self.bottom_right.0.height,
dest) &self.bottom_left.0.height)
} }
} }
} }

View file

@ -1,4 +0,0 @@
[flex-flexitem-percentage-prescation.htm]
type: reftest
expected:
if os == "linux": FAIL

View file

@ -1,3 +0,0 @@
[run-in-listitem-between-001.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[run-in-listitem-between-002.htm]
type: reftest
expected: FAIL

View file

@ -18,27 +18,12 @@
[shorthand border can be set with setProperty] [shorthand border can be set with setProperty]
expected: FAIL expected: FAIL
[shorthand border-color can be set with setProperty]
expected: FAIL
[shorthand border-style can be set with setProperty]
expected: FAIL
[shorthand border-width can be set with setProperty]
expected: FAIL
[shorthand list-style can be set with setProperty] [shorthand list-style can be set with setProperty]
expected: FAIL expected: FAIL
[shorthand margin can be set with setProperty]
expected: FAIL
[shorthand outline can be set with setProperty] [shorthand outline can be set with setProperty]
expected: FAIL expected: FAIL
[shorthand padding can be set with setProperty]
expected: FAIL
[shorthand background can be set with setProperty] [shorthand background can be set with setProperty]
expected: FAIL expected: FAIL

View file

@ -1,5 +0,0 @@
[index-001.htm]
type: testharness
[margin_20px_20px]
expected: FAIL

View file

@ -3,9 +3,6 @@
[border is expected to be border: 1px;] [border is expected to be border: 1px;]
expected: FAIL expected: FAIL
[border is expected to be border: 1px solid red;]
expected: FAIL
[border is expected to be border: 1px red;] [border is expected to be border: 1px red;]
expected: FAIL expected: FAIL
@ -27,10 +24,7 @@
[border is expected to be border: dotted;] [border is expected to be border: dotted;]
expected: FAIL expected: FAIL
[border is expected to be border-width: 1px;] [overflow is expected to be overflow: scroll hidden;]
expected: FAIL
[overflow is expected to be overflow: scroll;]
expected: FAIL expected: FAIL
[outline is expected to be outline: blue dotted 2px;] [outline is expected to be outline: blue dotted 2px;]

View file

@ -1,5 +1,8 @@
[calc.html] [calc.html]
type: testharness type: testharness
[calc for border-width]
expected: FAIL
[calc for column-width] [calc for column-width]
expected: FAIL expected: FAIL

View file

@ -0,0 +1,5 @@
[style_no_trailing_space.html]
type: testharness
[Untitled]
expected: FAIL