mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Auto merge of #12572 - craftytrickster:10933/shorthands, r=emilio
10933/shorthands <!-- Please describe your changes on the following line: --> This implements the serialization of nearly all of the css shorthand properties with the following exceptions: 1. font - this may be implemented correctly, but I am not 100% sure 2. border-radius - I do not know how to implement this, since I am not familiar with how the property works 3. background - this is implemented, but I think that the implementation might be a tiny bit off. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #10933 (github issue number if applicable). Also fixes issue #11448 <!-- Either: --> - [X] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/12572) <!-- Reviewable:end -->
This commit is contained in:
commit
b61c45639a
25 changed files with 2102 additions and 747 deletions
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
97
components/style/properties/shorthand/serialize.mako.rs
Normal file
97
components/style/properties/shorthand/serialize.mako.rs
Normal 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
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,114 +0,0 @@
|
||||||
/* 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 app_units::Au;
|
|
||||||
use cssparser::ToCss;
|
|
||||||
use rustc_serialize::json::Json;
|
|
||||||
use std::env;
|
|
||||||
use std::fs::{File, remove_file};
|
|
||||||
use std::path::Path;
|
|
||||||
use std::process::Command;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use style::computed_values::display::T::inline_block;
|
|
||||||
use style::properties::longhands::border_top_width;
|
|
||||||
use style::properties::{DeclaredValue, PropertyDeclaration, PropertyDeclarationBlock};
|
|
||||||
use style::values::HasViewportPercentage;
|
|
||||||
use style::values::specified::{Length, LengthOrPercentageOrAuto, LengthOrPercentage, ViewportPercentageLength};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn properties_list_json() {
|
|
||||||
let top = Path::new(file!()).parent().unwrap().join("..").join("..").join("..");
|
|
||||||
let json = top.join("target").join("doc").join("servo").join("css-properties.json");
|
|
||||||
if json.exists() {
|
|
||||||
remove_file(&json).unwrap()
|
|
||||||
}
|
|
||||||
let python = env::var("PYTHON").ok().unwrap_or_else(find_python);
|
|
||||||
let script = top.join("components").join("style").join("properties").join("build.py");
|
|
||||||
let status = Command::new(python)
|
|
||||||
.arg(&script)
|
|
||||||
.arg("servo")
|
|
||||||
.arg("html")
|
|
||||||
.status()
|
|
||||||
.unwrap();
|
|
||||||
assert!(status.success());
|
|
||||||
let properties = Json::from_reader(&mut File::open(json).unwrap()).unwrap();
|
|
||||||
assert!(properties.as_object().unwrap().len() > 100);
|
|
||||||
assert!(properties.find("margin").is_some());
|
|
||||||
assert!(properties.find("margin-top").is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
fn find_python() -> String {
|
|
||||||
if Command::new("python27.exe").arg("--version").output().is_ok() {
|
|
||||||
return "python27.exe".to_owned();
|
|
||||||
}
|
|
||||||
|
|
||||||
if Command::new("python.exe").arg("--version").output().is_ok() {
|
|
||||||
return "python.exe".to_owned();
|
|
||||||
}
|
|
||||||
|
|
||||||
panic!("Can't find python (tried python27.exe and python.exe)! Try fixing PATH or setting the PYTHON env var");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
fn find_python() -> String {
|
|
||||||
if Command::new("python2.7").arg("--version").output().unwrap().status.success() {
|
|
||||||
"python2.7"
|
|
||||||
} else {
|
|
||||||
"python"
|
|
||||||
}.to_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn property_declaration_block_should_serialize_correctly() {
|
|
||||||
let mut normal = Vec::new();
|
|
||||||
let mut important = Vec::new();
|
|
||||||
|
|
||||||
let length = LengthOrPercentageOrAuto::Length(Length::from_px(70f32));
|
|
||||||
let value = DeclaredValue::Value(length);
|
|
||||||
normal.push(PropertyDeclaration::Width(value));
|
|
||||||
|
|
||||||
let min_height = LengthOrPercentage::Length(Length::from_px(20f32));
|
|
||||||
let value = DeclaredValue::Value(min_height);
|
|
||||||
normal.push(PropertyDeclaration::MinHeight(value));
|
|
||||||
|
|
||||||
let value = DeclaredValue::Value(inline_block);
|
|
||||||
normal.push(PropertyDeclaration::Display(value));
|
|
||||||
|
|
||||||
let height = LengthOrPercentageOrAuto::Length(Length::from_px(20f32));
|
|
||||||
let value = DeclaredValue::Value(height);
|
|
||||||
important.push(PropertyDeclaration::Height(value));
|
|
||||||
|
|
||||||
normal.reverse();
|
|
||||||
important.reverse();
|
|
||||||
let block = PropertyDeclarationBlock {
|
|
||||||
normal: Arc::new(normal),
|
|
||||||
important: Arc::new(important)
|
|
||||||
};
|
|
||||||
|
|
||||||
let css_string = block.to_css_string();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
css_string,
|
|
||||||
"width: 70px; min-height: 20px; display: inline-block; height: 20px !important;"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn has_viewport_percentage_for_specified_value() {
|
|
||||||
//TODO: test all specified value with a HasViewportPercentage impl
|
|
||||||
let pvw = PropertyDeclaration::BorderTopWidth(
|
|
||||||
DeclaredValue::Value(border_top_width::SpecifiedValue(
|
|
||||||
Length::ViewportPercentage(ViewportPercentageLength::Vw(100.))
|
|
||||||
))
|
|
||||||
);
|
|
||||||
assert!(pvw.has_viewport_percentage());
|
|
||||||
|
|
||||||
let pabs = PropertyDeclaration::BorderTopWidth(
|
|
||||||
DeclaredValue::Value(border_top_width::SpecifiedValue(
|
|
||||||
Length::Absolute(Au(100))
|
|
||||||
))
|
|
||||||
);
|
|
||||||
assert!(!pabs.has_viewport_percentage());
|
|
||||||
}
|
|
7
tests/unit/style/properties/mod.rs
Normal file
7
tests/unit/style/properties/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
mod scaffolding;
|
||||||
|
mod serialization;
|
||||||
|
mod viewport;
|
53
tests/unit/style/properties/scaffolding.rs
Normal file
53
tests/unit/style/properties/scaffolding.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/* 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 rustc_serialize::json::Json;
|
||||||
|
use std::env;
|
||||||
|
use std::fs::{File, remove_file};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn properties_list_json() {
|
||||||
|
let top = Path::new(file!()).parent().unwrap().join("..").join("..").join("..").join("..");
|
||||||
|
let json = top.join("target").join("doc").join("servo").join("css-properties.json");
|
||||||
|
if json.exists() {
|
||||||
|
remove_file(&json).unwrap()
|
||||||
|
}
|
||||||
|
let python = env::var("PYTHON").ok().unwrap_or_else(find_python);
|
||||||
|
let script = top.join("components").join("style").join("properties").join("build.py");
|
||||||
|
let status = Command::new(python)
|
||||||
|
.arg(&script)
|
||||||
|
.arg("servo")
|
||||||
|
.arg("html")
|
||||||
|
.status()
|
||||||
|
.unwrap();
|
||||||
|
assert!(status.success());
|
||||||
|
let properties = Json::from_reader(&mut File::open(json).unwrap()).unwrap();
|
||||||
|
assert!(properties.as_object().unwrap().len() > 100);
|
||||||
|
assert!(properties.find("margin").is_some());
|
||||||
|
assert!(properties.find("margin-top").is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn find_python() -> String {
|
||||||
|
if Command::new("python27.exe").arg("--version").output().is_ok() {
|
||||||
|
return "python27.exe".to_owned();
|
||||||
|
}
|
||||||
|
|
||||||
|
if Command::new("python.exe").arg("--version").output().is_ok() {
|
||||||
|
return "python.exe".to_owned();
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("Can't find python (tried python27.exe and python.exe)! Try fixing PATH or setting the PYTHON env var");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
fn find_python() -> String {
|
||||||
|
if Command::new("python2.7").arg("--version").output().unwrap().status.success() {
|
||||||
|
"python2.7"
|
||||||
|
} else {
|
||||||
|
"python"
|
||||||
|
}.to_owned()
|
||||||
|
}
|
829
tests/unit/style/properties/serialization.rs
Normal file
829
tests/unit/style/properties/serialization.rs
Normal file
|
@ -0,0 +1,829 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
pub use cssparser::ToCss;
|
||||||
|
pub use std::sync::Arc;
|
||||||
|
pub use style::computed_values::display::T::inline_block;
|
||||||
|
pub use style::properties::{DeclaredValue, PropertyDeclaration, PropertyDeclarationBlock};
|
||||||
|
pub use style::values::specified::{BorderStyle, CSSColor, Length};
|
||||||
|
pub use style::values::specified::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrAutoOrContent};
|
||||||
|
pub use style::properties::longhands::outline_color::computed_value::T as ComputedColor;
|
||||||
|
pub use style::values::RGBA;
|
||||||
|
pub use style::values::specified::UrlExtraData;
|
||||||
|
pub use url::Url;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn property_declaration_block_should_serialize_correctly() {
|
||||||
|
use style::properties::longhands::overflow_x::computed_value::T as OverflowXValue;
|
||||||
|
use style::properties::longhands::overflow_y::computed_value::T as OverflowYContainer;
|
||||||
|
|
||||||
|
let mut normal = Vec::new();
|
||||||
|
let mut important = Vec::new();
|
||||||
|
|
||||||
|
let length = DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(70f32)));
|
||||||
|
normal.push(PropertyDeclaration::Width(length));
|
||||||
|
|
||||||
|
let min_height = DeclaredValue::Value(LengthOrPercentage::Length(Length::from_px(20f32)));
|
||||||
|
normal.push(PropertyDeclaration::MinHeight(min_height));
|
||||||
|
|
||||||
|
let value = DeclaredValue::Value(inline_block);
|
||||||
|
normal.push(PropertyDeclaration::Display(value));
|
||||||
|
|
||||||
|
let overflow_x = DeclaredValue::Value(OverflowXValue::auto);
|
||||||
|
normal.push(PropertyDeclaration::OverflowX(overflow_x));
|
||||||
|
|
||||||
|
let overflow_y = DeclaredValue::Value(OverflowYContainer(OverflowXValue::auto));
|
||||||
|
normal.push(PropertyDeclaration::OverflowY(overflow_y));
|
||||||
|
|
||||||
|
let height = DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(20f32)));
|
||||||
|
important.push(PropertyDeclaration::Height(height));
|
||||||
|
|
||||||
|
normal.reverse();
|
||||||
|
important.reverse();
|
||||||
|
let block = PropertyDeclarationBlock {
|
||||||
|
normal: Arc::new(normal),
|
||||||
|
important: Arc::new(important)
|
||||||
|
};
|
||||||
|
|
||||||
|
let css_string = block.to_css_string();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
css_string,
|
||||||
|
"width: 70px; min-height: 20px; display: inline-block; overflow: auto; height: 20px !important;"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod shorthand_serialization {
|
||||||
|
pub use super::*;
|
||||||
|
|
||||||
|
pub fn shorthand_properties_to_string(properties: Vec<PropertyDeclaration>) -> String {
|
||||||
|
let block = PropertyDeclarationBlock {
|
||||||
|
normal: Arc::new(properties),
|
||||||
|
important: Arc::new(Vec::new())
|
||||||
|
};
|
||||||
|
|
||||||
|
block.to_css_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Test to show error if a longhand property is missing!!!!!!
|
||||||
|
|
||||||
|
mod overflow {
|
||||||
|
pub use super::*;
|
||||||
|
use style::properties::longhands::overflow_x::computed_value::T as OverflowXValue;
|
||||||
|
use style::properties::longhands::overflow_y::computed_value::T as OverflowYContainer;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn equal_overflow_properties_should_serialize_to_single_value() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let overflow_x = DeclaredValue::Value(OverflowXValue::auto);
|
||||||
|
properties.push(PropertyDeclaration::OverflowX(overflow_x));
|
||||||
|
|
||||||
|
let overflow_y = DeclaredValue::Value(OverflowYContainer(OverflowXValue::auto));
|
||||||
|
properties.push(PropertyDeclaration::OverflowY(overflow_y));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "overflow: auto;");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn different_overflow_properties_should_serialize_to_two_values() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let overflow_x = DeclaredValue::Value(OverflowXValue::scroll);
|
||||||
|
properties.push(PropertyDeclaration::OverflowX(overflow_x));
|
||||||
|
|
||||||
|
let overflow_y = DeclaredValue::Value(OverflowYContainer(OverflowXValue::auto));
|
||||||
|
properties.push(PropertyDeclaration::OverflowY(overflow_y));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "overflow-x: scroll; overflow-y: auto;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod four_sides_shorthands {
|
||||||
|
pub use super::*;
|
||||||
|
|
||||||
|
// we can use margin as a base to test out the different combinations
|
||||||
|
// but afterwards, we only need to to one test per "four sides shorthand"
|
||||||
|
#[test]
|
||||||
|
fn all_equal_properties_should_serialize_to_one_value() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let px_70 = DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(70f32)));
|
||||||
|
properties.push(PropertyDeclaration::MarginTop(px_70.clone()));
|
||||||
|
properties.push(PropertyDeclaration::MarginRight(px_70.clone()));
|
||||||
|
properties.push(PropertyDeclaration::MarginBottom(px_70.clone()));
|
||||||
|
properties.push(PropertyDeclaration::MarginLeft(px_70));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "margin: 70px;");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn equal_vertical_and_equal_horizontal_properties_should_serialize_to_two_value() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let vertical_px = DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(10f32)));
|
||||||
|
let horizontal_px = DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(5f32)));
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::MarginTop(vertical_px.clone()));
|
||||||
|
properties.push(PropertyDeclaration::MarginRight(horizontal_px.clone()));
|
||||||
|
properties.push(PropertyDeclaration::MarginBottom(vertical_px));
|
||||||
|
properties.push(PropertyDeclaration::MarginLeft(horizontal_px));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "margin: 10px 5px;");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn different_vertical_and_equal_horizontal_properties_should_serialize_to_three_values() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let top_px = DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(8f32)));
|
||||||
|
let bottom_px = DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(10f32)));
|
||||||
|
let horizontal_px = DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(5f32)));
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::MarginTop(top_px));
|
||||||
|
properties.push(PropertyDeclaration::MarginRight(horizontal_px.clone()));
|
||||||
|
properties.push(PropertyDeclaration::MarginBottom(bottom_px));
|
||||||
|
properties.push(PropertyDeclaration::MarginLeft(horizontal_px));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "margin: 8px 5px 10px;");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn different_properties_should_serialize_to_four_values() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let top_px = DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(8f32)));
|
||||||
|
let right_px = DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(12f32)));
|
||||||
|
let bottom_px = DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(10f32)));
|
||||||
|
let left_px = DeclaredValue::Value(LengthOrPercentageOrAuto::Length(Length::from_px(14f32)));
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::MarginTop(top_px));
|
||||||
|
properties.push(PropertyDeclaration::MarginRight(right_px));
|
||||||
|
properties.push(PropertyDeclaration::MarginBottom(bottom_px));
|
||||||
|
properties.push(PropertyDeclaration::MarginLeft(left_px));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "margin: 8px 12px 10px 14px;");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn padding_should_serialize_correctly() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let px_10 = DeclaredValue::Value(LengthOrPercentage::Length(Length::from_px(10f32)));
|
||||||
|
let px_15 = DeclaredValue::Value(LengthOrPercentage::Length(Length::from_px(15f32)));
|
||||||
|
properties.push(PropertyDeclaration::PaddingTop(px_10.clone()));
|
||||||
|
properties.push(PropertyDeclaration::PaddingRight(px_15.clone()));
|
||||||
|
properties.push(PropertyDeclaration::PaddingBottom(px_10));
|
||||||
|
properties.push(PropertyDeclaration::PaddingLeft(px_15));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "padding: 10px 15px;");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn border_width_should_serialize_correctly() {
|
||||||
|
use style::properties::longhands::border_bottom_width::SpecifiedValue as BottomContainer;
|
||||||
|
use style::properties::longhands::border_left_width::SpecifiedValue as LeftContainer;
|
||||||
|
use style::properties::longhands::border_right_width::SpecifiedValue as RightContainer;
|
||||||
|
use style::properties::longhands::border_top_width::SpecifiedValue as TopContainer;
|
||||||
|
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let top_px = DeclaredValue::Value(TopContainer(Length::from_px(10f32)));
|
||||||
|
let bottom_px = DeclaredValue::Value(BottomContainer(Length::from_px(10f32)));
|
||||||
|
|
||||||
|
let right_px = DeclaredValue::Value(RightContainer(Length::from_px(15f32)));
|
||||||
|
let left_px = DeclaredValue::Value(LeftContainer(Length::from_px(15f32)));
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::BorderTopWidth(top_px));
|
||||||
|
properties.push(PropertyDeclaration::BorderRightWidth(right_px));
|
||||||
|
properties.push(PropertyDeclaration::BorderBottomWidth(bottom_px));
|
||||||
|
properties.push(PropertyDeclaration::BorderLeftWidth(left_px));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "border-width: 10px 15px;");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn border_color_should_serialize_correctly() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let red = DeclaredValue::Value(CSSColor {
|
||||||
|
parsed: ComputedColor::RGBA(RGBA { red: 1f32, green: 0f32, blue: 0f32, alpha: 1f32 }),
|
||||||
|
authored: None
|
||||||
|
});
|
||||||
|
|
||||||
|
let blue = DeclaredValue::Value(CSSColor {
|
||||||
|
parsed: ComputedColor::RGBA(RGBA { red: 0f32, green: 0f32, blue: 1f32, alpha: 1f32 }),
|
||||||
|
authored: None
|
||||||
|
});
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::BorderTopColor(blue.clone()));
|
||||||
|
properties.push(PropertyDeclaration::BorderRightColor(red.clone()));
|
||||||
|
properties.push(PropertyDeclaration::BorderBottomColor(blue));
|
||||||
|
properties.push(PropertyDeclaration::BorderLeftColor(red));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
|
||||||
|
// TODO: Make the rgb test show border-color as blue red instead of below tuples
|
||||||
|
assert_eq!(serialization, "border-color: rgb(0, 0, 255) rgb(255, 0, 0);");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn border_style_should_serialize_correctly() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let solid = DeclaredValue::Value(BorderStyle::solid);
|
||||||
|
let dotted = DeclaredValue::Value(BorderStyle::dotted);
|
||||||
|
properties.push(PropertyDeclaration::BorderTopStyle(solid.clone()));
|
||||||
|
properties.push(PropertyDeclaration::BorderRightStyle(dotted.clone()));
|
||||||
|
properties.push(PropertyDeclaration::BorderBottomStyle(solid));
|
||||||
|
properties.push(PropertyDeclaration::BorderLeftStyle(dotted));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "border-style: solid dotted;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mod border_shorthands {
|
||||||
|
use style::properties::longhands::border_bottom_width::SpecifiedValue as BottomContainer;
|
||||||
|
use style::properties::longhands::border_left_width::SpecifiedValue as LeftContainer;
|
||||||
|
use style::properties::longhands::border_right_width::SpecifiedValue as RightContainer;
|
||||||
|
use style::properties::longhands::border_top_width::SpecifiedValue as TopContainer;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// we can use border-top as a base to test out the different combinations
|
||||||
|
// but afterwards, we only need to to one test per "directional border shorthand"
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn directional_border_should_show_all_properties_when_values_are_set() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let width = DeclaredValue::Value(TopContainer(Length::from_px(4f32)));
|
||||||
|
let style = DeclaredValue::Value(BorderStyle::solid);
|
||||||
|
let color = DeclaredValue::Value(CSSColor {
|
||||||
|
parsed: ComputedColor::RGBA(RGBA { red: 1f32, green: 0f32, blue: 0f32, alpha: 1f32 }),
|
||||||
|
authored: None
|
||||||
|
});
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::BorderTopWidth(width));
|
||||||
|
properties.push(PropertyDeclaration::BorderTopStyle(style));
|
||||||
|
properties.push(PropertyDeclaration::BorderTopColor(color));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "border-top: 4px solid rgb(255, 0, 0);");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn directional_border_with_no_specified_style_will_show_style_as_none() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let width = DeclaredValue::Value(TopContainer(Length::from_px(4f32)));
|
||||||
|
let style = DeclaredValue::Initial;
|
||||||
|
let color = DeclaredValue::Value(CSSColor {
|
||||||
|
parsed: ComputedColor::RGBA(RGBA { red: 1f32, green: 0f32, blue: 0f32, alpha: 1f32 }),
|
||||||
|
authored: None
|
||||||
|
});
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::BorderTopWidth(width));
|
||||||
|
properties.push(PropertyDeclaration::BorderTopStyle(style));
|
||||||
|
properties.push(PropertyDeclaration::BorderTopColor(color));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "border-top: 4px none rgb(255, 0, 0);");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn directional_border_with_no_specified_color_will_not_show_color() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let width = DeclaredValue::Value(TopContainer(Length::from_px(4f32)));
|
||||||
|
let style = DeclaredValue::Value(BorderStyle::solid);
|
||||||
|
let color = DeclaredValue::Initial;
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::BorderTopWidth(width));
|
||||||
|
properties.push(PropertyDeclaration::BorderTopStyle(style));
|
||||||
|
properties.push(PropertyDeclaration::BorderTopColor(color));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "border-top: 4px solid;");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn border_right_should_serialize_correctly() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let width = DeclaredValue::Value(RightContainer(Length::from_px(4f32)));
|
||||||
|
let style = DeclaredValue::Value(BorderStyle::solid);
|
||||||
|
let color = DeclaredValue::Initial;
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::BorderRightWidth(width));
|
||||||
|
properties.push(PropertyDeclaration::BorderRightStyle(style));
|
||||||
|
properties.push(PropertyDeclaration::BorderRightColor(color));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "border-right: 4px solid;");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn border_bottom_should_serialize_correctly() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let width = DeclaredValue::Value(BottomContainer(Length::from_px(4f32)));
|
||||||
|
let style = DeclaredValue::Value(BorderStyle::solid);
|
||||||
|
let color = DeclaredValue::Initial;
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::BorderBottomWidth(width));
|
||||||
|
properties.push(PropertyDeclaration::BorderBottomStyle(style));
|
||||||
|
properties.push(PropertyDeclaration::BorderBottomColor(color));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "border-bottom: 4px solid;");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn border_left_should_serialize_correctly() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let width = DeclaredValue::Value(LeftContainer(Length::from_px(4f32)));
|
||||||
|
let style = DeclaredValue::Value(BorderStyle::solid);
|
||||||
|
let color = DeclaredValue::Initial;
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::BorderLeftWidth(width));
|
||||||
|
properties.push(PropertyDeclaration::BorderLeftStyle(style));
|
||||||
|
properties.push(PropertyDeclaration::BorderLeftColor(color));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "border-left: 4px solid;");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn border_should_serialize_correctly() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let top_width = DeclaredValue::Value(TopContainer(Length::from_px(4f32)));
|
||||||
|
let top_style = DeclaredValue::Value(BorderStyle::solid);
|
||||||
|
let top_color = DeclaredValue::Initial;
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::BorderTopWidth(top_width));
|
||||||
|
properties.push(PropertyDeclaration::BorderTopStyle(top_style));
|
||||||
|
properties.push(PropertyDeclaration::BorderTopColor(top_color));
|
||||||
|
|
||||||
|
let right_width = DeclaredValue::Value(RightContainer(Length::from_px(4f32)));
|
||||||
|
let right_style = DeclaredValue::Value(BorderStyle::solid);
|
||||||
|
let right_color = DeclaredValue::Initial;
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::BorderRightWidth(right_width));
|
||||||
|
properties.push(PropertyDeclaration::BorderRightStyle(right_style));
|
||||||
|
properties.push(PropertyDeclaration::BorderRightColor(right_color));
|
||||||
|
|
||||||
|
let bottom_width = DeclaredValue::Value(BottomContainer(Length::from_px(4f32)));
|
||||||
|
let bottom_style = DeclaredValue::Value(BorderStyle::solid);
|
||||||
|
let bottom_color = DeclaredValue::Initial;
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::BorderBottomWidth(bottom_width));
|
||||||
|
properties.push(PropertyDeclaration::BorderBottomStyle(bottom_style));
|
||||||
|
properties.push(PropertyDeclaration::BorderBottomColor(bottom_color));
|
||||||
|
|
||||||
|
let left_width = DeclaredValue::Value(LeftContainer(Length::from_px(4f32)));
|
||||||
|
let left_style = DeclaredValue::Value(BorderStyle::solid);
|
||||||
|
let left_color = DeclaredValue::Initial;
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::BorderLeftWidth(left_width));
|
||||||
|
properties.push(PropertyDeclaration::BorderLeftStyle(left_style));
|
||||||
|
properties.push(PropertyDeclaration::BorderLeftColor(left_color));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "border: 4px solid;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod list_style {
|
||||||
|
use style::properties::longhands::list_style_image::SpecifiedValue as ListStyleImage;
|
||||||
|
use style::properties::longhands::list_style_position::computed_value::T as ListStylePosition;
|
||||||
|
use style::properties::longhands::list_style_type::computed_value::T as ListStyleType;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_style_should_show_all_properties_when_values_are_set() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let position = DeclaredValue::Value(ListStylePosition::inside);
|
||||||
|
let image = DeclaredValue::Value(ListStyleImage::Url(
|
||||||
|
Url::parse("http://servo/test.png").unwrap()
|
||||||
|
));
|
||||||
|
let style_type = DeclaredValue::Value(ListStyleType::disc);
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::ListStylePosition(position));
|
||||||
|
properties.push(PropertyDeclaration::ListStyleImage(image));
|
||||||
|
properties.push(PropertyDeclaration::ListStyleType(style_type));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "list-style: inside url(\"http://servo/test.png\") disc;");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_style_should_show_all_properties_even_if_only_one_is_set() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let position = DeclaredValue::Initial;
|
||||||
|
let image = DeclaredValue::Initial;
|
||||||
|
let style_type = DeclaredValue::Value(ListStyleType::disc);
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::ListStylePosition(position));
|
||||||
|
properties.push(PropertyDeclaration::ListStyleImage(image));
|
||||||
|
properties.push(PropertyDeclaration::ListStyleType(style_type));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "list-style: outside none disc;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn overflow_wrap_should_only_serialize_with_a_single_property() {
|
||||||
|
use style::properties::longhands::overflow_wrap::computed_value::T as OverflowWrap;
|
||||||
|
|
||||||
|
let value = DeclaredValue::Value(OverflowWrap::break_word);
|
||||||
|
|
||||||
|
let properties = vec![
|
||||||
|
PropertyDeclaration::OverflowWrap(value)
|
||||||
|
];
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
|
||||||
|
// word-wrap is considered an outdated alternative to overflow-wrap, but it is currently
|
||||||
|
// what servo is using in its naming conventions:
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-wrap
|
||||||
|
assert_eq!(serialization, "word-wrap: break-word;");
|
||||||
|
}
|
||||||
|
|
||||||
|
mod outline {
|
||||||
|
use style::properties::longhands::outline_width::SpecifiedValue as WidthContainer;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn outline_should_show_all_properties_when_set() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let width = DeclaredValue::Value(WidthContainer(Length::from_px(4f32)));
|
||||||
|
let style = DeclaredValue::Value(BorderStyle::solid);
|
||||||
|
let color = DeclaredValue::Value(CSSColor {
|
||||||
|
parsed: ComputedColor::RGBA(RGBA { red: 1f32, green: 0f32, blue: 0f32, alpha: 1f32 }),
|
||||||
|
authored: None
|
||||||
|
});
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::OutlineWidth(width));
|
||||||
|
properties.push(PropertyDeclaration::OutlineStyle(style));
|
||||||
|
properties.push(PropertyDeclaration::OutlineColor(color));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "outline: 4px solid rgb(255, 0, 0);");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn outline_should_not_show_color_if_not_set() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let width = DeclaredValue::Value(WidthContainer(Length::from_px(4f32)));
|
||||||
|
let style = DeclaredValue::Value(BorderStyle::solid);
|
||||||
|
let color = DeclaredValue::Initial;
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::OutlineWidth(width));
|
||||||
|
properties.push(PropertyDeclaration::OutlineStyle(style));
|
||||||
|
properties.push(PropertyDeclaration::OutlineColor(color));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "outline: 4px solid;");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn outline_should_serialize_correctly_when_style_is_not_set() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let width = DeclaredValue::Value(WidthContainer(Length::from_px(4f32)));
|
||||||
|
let style = DeclaredValue::Initial;
|
||||||
|
let color = DeclaredValue::Value(CSSColor {
|
||||||
|
parsed: ComputedColor::RGBA(RGBA { red: 1f32, green: 0f32, blue: 0f32, alpha: 1f32 }),
|
||||||
|
authored: None
|
||||||
|
});
|
||||||
|
properties.push(PropertyDeclaration::OutlineWidth(width));
|
||||||
|
properties.push(PropertyDeclaration::OutlineStyle(style));
|
||||||
|
properties.push(PropertyDeclaration::OutlineColor(color));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "outline: 4px none rgb(255, 0, 0);");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn columns_should_serialize_correctly() {
|
||||||
|
use style::properties::longhands::column_count::SpecifiedValue as ColumnCount;
|
||||||
|
use style::properties::longhands::column_width::SpecifiedValue as ColumnWidth;
|
||||||
|
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let width = DeclaredValue::Value(ColumnWidth::Auto);
|
||||||
|
let count = DeclaredValue::Value(ColumnCount::Auto);
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::ColumnWidth(width));
|
||||||
|
properties.push(PropertyDeclaration::ColumnCount(count));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "columns: auto auto;");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transition_should_serialize_all_available_properties() {
|
||||||
|
use euclid::point::Point2D;
|
||||||
|
use style::properties::animated_properties::TransitionProperty;
|
||||||
|
use style::properties::longhands::transition_duration::computed_value::T as DurationContainer;
|
||||||
|
use style::properties::longhands::transition_property::computed_value::T as PropertyContainer;
|
||||||
|
use style::properties::longhands::transition_timing_function::computed_value::T as TimingContainer;
|
||||||
|
use style::properties::longhands::transition_timing_function::computed_value::TransitionTimingFunction;
|
||||||
|
use style::values::specified::Time as TimeContainer;
|
||||||
|
|
||||||
|
let property_name = DeclaredValue::Value(
|
||||||
|
PropertyContainer(vec![TransitionProperty::MarginLeft])
|
||||||
|
);
|
||||||
|
|
||||||
|
let duration = DeclaredValue::Value(
|
||||||
|
DurationContainer(vec![TimeContainer(3f32)])
|
||||||
|
);
|
||||||
|
|
||||||
|
let delay = DeclaredValue::Value(
|
||||||
|
DurationContainer(vec![TimeContainer(4f32)])
|
||||||
|
);
|
||||||
|
|
||||||
|
let timing_function = DeclaredValue::Value(
|
||||||
|
TimingContainer(vec![
|
||||||
|
TransitionTimingFunction::CubicBezier(Point2D::new(0f32, 5f32), Point2D::new(5f32, 10f32))
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::TransitionProperty(property_name));
|
||||||
|
properties.push(PropertyDeclaration::TransitionDelay(delay));
|
||||||
|
properties.push(PropertyDeclaration::TransitionDuration(duration));
|
||||||
|
properties.push(PropertyDeclaration::TransitionTimingFunction(timing_function));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "transition: margin-left 3s cubic-bezier(0, 5, 5, 10) 4s;");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn flex_should_serialize_all_available_properties() {
|
||||||
|
use style::values::specified::Number as NumberContainer;
|
||||||
|
use style::values::specified::Percentage as PercentageContainer;
|
||||||
|
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let grow = DeclaredValue::Value(NumberContainer(2f32));
|
||||||
|
let shrink = DeclaredValue::Value(NumberContainer(3f32));
|
||||||
|
let basis = DeclaredValue::Value(
|
||||||
|
LengthOrPercentageOrAutoOrContent::Percentage(PercentageContainer(0.5f32))
|
||||||
|
);
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::FlexGrow(grow));
|
||||||
|
properties.push(PropertyDeclaration::FlexShrink(shrink));
|
||||||
|
properties.push(PropertyDeclaration::FlexBasis(basis));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "flex: 2 3 50%;");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn flex_flow_should_serialize_all_available_properties() {
|
||||||
|
use style::properties::longhands::flex_direction::computed_value::T as FlexDirection;
|
||||||
|
use style::properties::longhands::flex_wrap::computed_value::T as FlexWrap;
|
||||||
|
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let direction = DeclaredValue::Value(FlexDirection::row);
|
||||||
|
let wrap = DeclaredValue::Value(FlexWrap::wrap);
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::FlexDirection(direction));
|
||||||
|
properties.push(PropertyDeclaration::FlexWrap(wrap));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "flex-flow: row wrap;");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Populate Atom Cache for testing so that the font shorthand can be tested
|
||||||
|
/*
|
||||||
|
mod font {
|
||||||
|
use super::*;
|
||||||
|
use style::properties::longhands::font_family::computed_value::T as FamilyContainer;
|
||||||
|
use style::properties::longhands::font_family::computed_value::FontFamily;
|
||||||
|
use style::properties::longhands::font_style::computed_value::T as FontStyle;
|
||||||
|
use style::properties::longhands::font_variant::computed_value::T as FontVariant;
|
||||||
|
use style::properties::longhands::font_weight::SpecifiedValue as FontWeight;
|
||||||
|
use style::properties::longhands::font_size::SpecifiedValue as FontSizeContainer;
|
||||||
|
use style::properties::longhands::font_stretch::computed_value::T as FontStretch;
|
||||||
|
use style::properties::longhands::line_height::SpecifiedValue as LineHeight;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn font_should_serialize_all_available_properties() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
|
||||||
|
let font_family = DeclaredValue::Value(
|
||||||
|
FamilyContainer(vec![FontFamily::Generic(atom!("serif"))])
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
let font_style = DeclaredValue::Value(FontStyle::italic);
|
||||||
|
let font_variant = DeclaredValue::Value(FontVariant::normal);
|
||||||
|
let font_weight = DeclaredValue::Value(FontWeight::Bolder);
|
||||||
|
let font_size = DeclaredValue::Value(FontSizeContainer(
|
||||||
|
LengthOrPercentage::Length(Length::from_px(4f32)))
|
||||||
|
);
|
||||||
|
let font_stretch = DeclaredValue::Value(FontStretch::expanded);
|
||||||
|
let line_height = DeclaredValue::Value(LineHeight::Number(3f32));
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::FontFamily(font_family));
|
||||||
|
properties.push(PropertyDeclaration::FontStyle(font_style));
|
||||||
|
properties.push(PropertyDeclaration::FontVariant(font_variant));
|
||||||
|
properties.push(PropertyDeclaration::FontWeight(font_weight));
|
||||||
|
properties.push(PropertyDeclaration::FontSize(font_size));
|
||||||
|
properties.push(PropertyDeclaration::FontStretch(font_stretch));
|
||||||
|
properties.push(PropertyDeclaration::LineHeight(line_height));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "font:;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: Populate Atom Cache for testing so that the animation shorthand can be tested
|
||||||
|
/*
|
||||||
|
#[test]
|
||||||
|
fn animation_should_serialize_all_available_properties() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
assert_eq!(serialization, "animation;");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
mod background {
|
||||||
|
use style::properties::longhands::background_attachment::computed_value::T as Attachment;
|
||||||
|
use style::properties::longhands::background_clip::computed_value::T as Clip;
|
||||||
|
use style::properties::longhands::background_image::SpecifiedValue as ImageContainer;
|
||||||
|
use style::properties::longhands::background_origin::computed_value::T as Origin;
|
||||||
|
use style::properties::longhands::background_position::SpecifiedValue as PositionContainer;
|
||||||
|
use style::properties::longhands::background_repeat::computed_value::T as Repeat;
|
||||||
|
use style::properties::longhands::background_size::SpecifiedExplicitSize;
|
||||||
|
use style::properties::longhands::background_size::SpecifiedValue as Size;
|
||||||
|
use style::values::specified::Image;
|
||||||
|
use style::values::specified::position::Position;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn background_should_serialize_all_available_properties_when_specified() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let color = DeclaredValue::Value(CSSColor {
|
||||||
|
parsed: ComputedColor::RGBA(RGBA { red: 1f32, green: 0f32, blue: 0f32, alpha: 1f32 }),
|
||||||
|
authored: None
|
||||||
|
});
|
||||||
|
|
||||||
|
let position = DeclaredValue::Value(PositionContainer(
|
||||||
|
Position {
|
||||||
|
horizontal: LengthOrPercentage::Length(Length::from_px(7f32)),
|
||||||
|
vertical: LengthOrPercentage::Length(Length::from_px(4f32))
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
let repeat = DeclaredValue::Value(Repeat::repeat_x);
|
||||||
|
let attachment = DeclaredValue::Value(Attachment::scroll);
|
||||||
|
|
||||||
|
let image = DeclaredValue::Value(ImageContainer(
|
||||||
|
Some(Image::Url(Url::parse("http://servo/test.png").unwrap(), UrlExtraData {}))
|
||||||
|
));
|
||||||
|
|
||||||
|
let size = DeclaredValue::Value(
|
||||||
|
Size::Explicit(SpecifiedExplicitSize {
|
||||||
|
width: LengthOrPercentageOrAuto::Length(Length::from_px(70f32)),
|
||||||
|
height: LengthOrPercentageOrAuto::Length(Length::from_px(50f32))
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
let origin = DeclaredValue::Value(Origin::border_box);
|
||||||
|
let clip = DeclaredValue::Value(Clip::padding_box);
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::BackgroundColor(color));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundPosition(position));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundRepeat(repeat));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundAttachment(attachment));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundImage(image));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundSize(size));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundOrigin(origin));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundClip(clip));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
serialization,
|
||||||
|
"background: rgb(255, 0, 0) url(\"http://servo/test.png\") repeat-x \
|
||||||
|
scroll 7px 4px / 70px 50px border-box padding-box;"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn background_should_combine_origin_and_clip_properties_when_equal() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let color = DeclaredValue::Value(CSSColor {
|
||||||
|
parsed: ComputedColor::RGBA(RGBA { red: 1f32, green: 0f32, blue: 0f32, alpha: 1f32 }),
|
||||||
|
authored: None
|
||||||
|
});
|
||||||
|
|
||||||
|
let position = DeclaredValue::Value(PositionContainer(
|
||||||
|
Position {
|
||||||
|
horizontal: LengthOrPercentage::Length(Length::from_px(7f32)),
|
||||||
|
vertical: LengthOrPercentage::Length(Length::from_px(4f32))
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
let repeat = DeclaredValue::Value(Repeat::repeat_x);
|
||||||
|
let attachment = DeclaredValue::Value(Attachment::scroll);
|
||||||
|
|
||||||
|
let image = DeclaredValue::Value(ImageContainer(
|
||||||
|
Some(Image::Url(Url::parse("http://servo/test.png").unwrap(), UrlExtraData {}))
|
||||||
|
));
|
||||||
|
|
||||||
|
let size = DeclaredValue::Value(
|
||||||
|
Size::Explicit(SpecifiedExplicitSize {
|
||||||
|
width: LengthOrPercentageOrAuto::Length(Length::from_px(70f32)),
|
||||||
|
height: LengthOrPercentageOrAuto::Length(Length::from_px(50f32))
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let origin = DeclaredValue::Value(Origin::padding_box);
|
||||||
|
let clip = DeclaredValue::Value(Clip::padding_box);
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::BackgroundColor(color));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundPosition(position));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundRepeat(repeat));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundAttachment(attachment));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundImage(image));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundSize(size));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundOrigin(origin));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundClip(clip));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(
|
||||||
|
serialization,
|
||||||
|
"background: rgb(255, 0, 0) url(\"http://servo/test.png\") repeat-x \
|
||||||
|
scroll 7px 4px / 70px 50px padding-box;"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn background_should_always_print_color_and_url_and_repeat_and_attachment_and_position() {
|
||||||
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
|
let color = DeclaredValue::Value(CSSColor {
|
||||||
|
parsed: ComputedColor::RGBA(RGBA { red: 1f32, green: 0f32, blue: 0f32, alpha: 1f32 }),
|
||||||
|
authored: None
|
||||||
|
});
|
||||||
|
|
||||||
|
let position = DeclaredValue::Value(PositionContainer(
|
||||||
|
Position {
|
||||||
|
horizontal: LengthOrPercentage::Length(Length::from_px(0f32)),
|
||||||
|
vertical: LengthOrPercentage::Length(Length::from_px(0f32))
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
let repeat = DeclaredValue::Value(Repeat::repeat_x);
|
||||||
|
let attachment = DeclaredValue::Value(Attachment::scroll);
|
||||||
|
|
||||||
|
let image = DeclaredValue::Value(ImageContainer(None));
|
||||||
|
|
||||||
|
let size = DeclaredValue::Initial;
|
||||||
|
|
||||||
|
let origin = DeclaredValue::Initial;
|
||||||
|
let clip = DeclaredValue::Initial;
|
||||||
|
|
||||||
|
properties.push(PropertyDeclaration::BackgroundColor(color));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundPosition(position));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundRepeat(repeat));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundAttachment(attachment));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundImage(image));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundSize(size));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundOrigin(origin));
|
||||||
|
properties.push(PropertyDeclaration::BackgroundClip(clip));
|
||||||
|
|
||||||
|
let serialization = shorthand_properties_to_string(properties);
|
||||||
|
assert_eq!(serialization, "background: rgb(255, 0, 0) none repeat-x scroll 0px 0px;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
tests/unit/style/properties/viewport.rs
Normal file
27
tests/unit/style/properties/viewport.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/* 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 app_units::Au;
|
||||||
|
use style::properties::longhands::border_top_width;
|
||||||
|
use style::properties::{DeclaredValue, PropertyDeclaration};
|
||||||
|
use style::values::HasViewportPercentage;
|
||||||
|
use style::values::specified::{Length, ViewportPercentageLength};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn has_viewport_percentage_for_specified_value() {
|
||||||
|
//TODO: test all specified value with a HasViewportPercentage impl
|
||||||
|
let pvw = PropertyDeclaration::BorderTopWidth(
|
||||||
|
DeclaredValue::Value(border_top_width::SpecifiedValue(
|
||||||
|
Length::ViewportPercentage(ViewportPercentageLength::Vw(100.))
|
||||||
|
))
|
||||||
|
);
|
||||||
|
assert!(pvw.has_viewport_percentage());
|
||||||
|
|
||||||
|
let pabs = PropertyDeclaration::BorderTopWidth(
|
||||||
|
DeclaredValue::Value(border_top_width::SpecifiedValue(
|
||||||
|
Length::Absolute(Au(100))
|
||||||
|
))
|
||||||
|
);
|
||||||
|
assert!(!pabs.has_viewport_percentage());
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[index-001.htm]
|
|
||||||
type: testharness
|
|
||||||
[margin_20px_20px]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -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;]
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
[style_no_trailing_space.html]
|
||||||
|
type: testharness
|
||||||
|
[Untitled]
|
||||||
|
expected: FAIL
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue