diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index 8f1a7820e1f..73ff84cadd5 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -189,3 +189,82 @@ } + +<%def name="shorthand(name, sub_properties, experimental=False)"> +<% + shorthand = data.declare_shorthand(name, sub_properties.split(), experimental=experimental) +%> + pub mod ${shorthand.ident} { + use cssparser::Parser; + use parser::ParserContext; + use properties::{longhands, PropertyDeclaration, DeclaredValue, Shorthand}; + + pub struct Longhands { + % for sub_property in shorthand.sub_properties: + pub ${sub_property.ident}: + Option, + % endfor + } + + pub fn parse(context: &ParserContext, input: &mut Parser, + declarations: &mut Vec) + -> Result<(), ()> { + input.look_for_var_functions(); + let start = input.position(); + let value = input.parse_entirely(|input| parse_value(context, input)); + if value.is_err() { + while let Ok(_) = input.next() {} // Look for var() after the error. + } + let var = input.seen_var_functions(); + if let Ok(value) = value { + % for sub_property in shorthand.sub_properties: + declarations.push(PropertyDeclaration::${sub_property.camel_case}( + match value.${sub_property.ident} { + Some(value) => DeclaredValue::Value(value), + None => DeclaredValue::Initial, + } + )); + % endfor + Ok(()) + } else if var { + input.reset(start); + let (first_token_type, css) = try!( + ::custom_properties::parse_non_custom_with_var(input)); + % for sub_property in shorthand.sub_properties: + declarations.push(PropertyDeclaration::${sub_property.camel_case}( + DeclaredValue::WithVariables { + css: css.clone().into_owned(), + first_token_type: first_token_type, + base_url: context.base_url.clone(), + from_shorthand: Some(Shorthand::${shorthand.camel_case}), + } + )); + % endfor + Ok(()) + } else { + Err(()) + } + } + + #[allow(unused_variables)] + pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result { + ${caller.body()} + } + } + + +<%def name="four_sides_shorthand(name, sub_property_pattern, parser_function)"> + <%self:shorthand name="${name}" sub_properties="${ + ' '.join(sub_property_pattern % side + for side in ['top', 'right', 'bottom', 'left'])}"> + use super::parse_four_sides; + use values::specified; + let _unused = context; + let (top, right, bottom, left) = try!(parse_four_sides(input, ${parser_function})); + Ok(Longhands { + % for side in ["top", "right", "bottom", "left"]: + ${to_rust_ident(sub_property_pattern % side)}: Some(${side}), + % endfor + }) + + diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 1717f9ae1b7..6582d5a6020 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -72,75 +72,11 @@ pub mod longhands { <%include file="/longhand/xul.mako.rs" /> } - pub mod shorthands { use cssparser::Parser; use parser::ParserContext; use values::specified; - <%def name="shorthand(name, sub_properties, experimental=False)"> - <% - shorthand = data.declare_shorthand(name, sub_properties.split(), experimental=experimental) - %> - pub mod ${shorthand.ident} { - use cssparser::Parser; - use parser::ParserContext; - use properties::{longhands, PropertyDeclaration, DeclaredValue, Shorthand}; - - pub struct Longhands { - % for sub_property in shorthand.sub_properties: - pub ${sub_property.ident}: - Option, - % endfor - } - - pub fn parse(context: &ParserContext, input: &mut Parser, - declarations: &mut Vec) - -> Result<(), ()> { - input.look_for_var_functions(); - let start = input.position(); - let value = input.parse_entirely(|input| parse_value(context, input)); - if value.is_err() { - while let Ok(_) = input.next() {} // Look for var() after the error. - } - let var = input.seen_var_functions(); - if let Ok(value) = value { - % for sub_property in shorthand.sub_properties: - declarations.push(PropertyDeclaration::${sub_property.camel_case}( - match value.${sub_property.ident} { - Some(value) => DeclaredValue::Value(value), - None => DeclaredValue::Initial, - } - )); - % endfor - Ok(()) - } else if var { - input.reset(start); - let (first_token_type, css) = try!( - ::custom_properties::parse_non_custom_with_var(input)); - % for sub_property in shorthand.sub_properties: - declarations.push(PropertyDeclaration::${sub_property.camel_case}( - DeclaredValue::WithVariables { - css: css.clone().into_owned(), - first_token_type: first_token_type, - base_url: context.base_url.clone(), - from_shorthand: Some(Shorthand::${shorthand.camel_case}), - } - )); - % endfor - Ok(()) - } else { - Err(()) - } - } - - #[allow(unused_variables)] - pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result { - ${caller.body()} - } - } - - fn parse_four_sides(input: &mut Parser, parse_one: F) -> Result<(T, T, T, T), ()> where F: Fn(&mut Parser) -> Result, F: Copy, T: Clone { // zero or more than four values is invalid. @@ -184,606 +120,16 @@ pub mod shorthands { Ok((top, right, bottom, left)) } - <%def name="four_sides_shorthand(name, sub_property_pattern, parser_function)"> - <%self:shorthand name="${name}" sub_properties="${ - ' '.join(sub_property_pattern % side - for side in ['top', 'right', 'bottom', 'left'])}"> - use super::parse_four_sides; - use values::specified; - let _unused = context; - let (top, right, bottom, left) = try!(parse_four_sides(input, ${parser_function})); - Ok(Longhands { - % for side in ["top", "right", "bottom", "left"]: - ${to_rust_ident(sub_property_pattern % side)}: Some(${side}), - % endfor - }) - - - - // TODO: other background-* properties - <%self:shorthand name="background" - sub_properties="background-color background-position background-repeat background-attachment - background-image background-size background-origin background-clip"> - use properties::longhands::{background_color, background_position, background_repeat, background_attachment}; - use properties::longhands::{background_image, background_size, background_origin, background_clip}; - - let mut color = None; - let mut image = None; - let mut position = None; - let mut repeat = None; - let mut size = None; - let mut attachment = None; - let mut any = false; - let mut origin = None; - let mut clip = None; - - loop { - if position.is_none() { - if let Ok(value) = input.try(|input| background_position::parse(context, input)) { - position = Some(value); - any = true; - - // Parse background size, if applicable. - size = input.try(|input| { - try!(input.expect_delim('/')); - background_size::parse(context, input) - }).ok(); - - continue - } - } - if color.is_none() { - if let Ok(value) = input.try(|input| background_color::parse(context, input)) { - color = Some(value); - any = true; - continue - } - } - if image.is_none() { - if let Ok(value) = input.try(|input| background_image::parse(context, input)) { - image = Some(value); - any = true; - continue - } - } - if repeat.is_none() { - if let Ok(value) = input.try(|input| background_repeat::parse(context, input)) { - repeat = Some(value); - any = true; - continue - } - } - if attachment.is_none() { - if let Ok(value) = input.try(|input| background_attachment::parse(context, input)) { - attachment = Some(value); - any = true; - continue - } - } - if origin.is_none() { - if let Ok(value) = input.try(|input| background_origin::parse(context, input)) { - origin = Some(value); - any = true; - continue - } - } - if clip.is_none() { - if let Ok(value) = input.try(|input| background_clip::parse(context, input)) { - clip = Some(value); - any = true; - 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(()) - } - - - ${four_sides_shorthand("margin", "margin-%s", "specified::LengthOrPercentageOrAuto::parse")} - ${four_sides_shorthand("padding", "padding-%s", "specified::LengthOrPercentage::parse")} - - ${four_sides_shorthand("border-color", "border-%s-color", "specified::CSSColor::parse")} - ${four_sides_shorthand("border-style", "border-%s-style", - "specified::BorderStyle::parse")} - <%self:shorthand name="border-width" sub_properties="${ - ' '.join('border-%s-width' % side - for side in ['top', 'right', 'bottom', 'left'])}"> - use super::parse_four_sides; - use values::specified; - let _unused = context; - let (top, right, bottom, left) = try!(parse_four_sides(input, specified::parse_border_width)); - Ok(Longhands { - % for side in ["top", "right", "bottom", "left"]: - ${to_rust_ident('border-%s-width' % side)}: - Some(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue(${side})), - % endfor - }) - - - - pub fn parse_border(context: &ParserContext, input: &mut Parser) - -> Result<(Option, - Option, - Option), ()> { - use values::specified; - let _unused = context; - let mut color = None; - let mut style = None; - let mut width = None; - let mut any = false; - loop { - if color.is_none() { - if let Ok(value) = input.try(specified::CSSColor::parse) { - color = Some(value); - any = true; - continue - } - } - if style.is_none() { - if let Ok(value) = input.try(specified::BorderStyle::parse) { - style = Some(value); - any = true; - continue - } - } - if width.is_none() { - if let Ok(value) = input.try(specified::parse_border_width) { - width = Some(value); - any = true; - continue - } - } - break - } - if any { Ok((color, style, width)) } else { Err(()) } - } - - - % for side in ["top", "right", "bottom", "left"]: - <%self:shorthand name="border-${side}" sub_properties="${' '.join( - 'border-%s-%s' % (side, prop) - for prop in ['color', 'style', 'width'] - )}"> - let (color, style, width) = try!(super::parse_border(context, input)); - Ok(Longhands { - border_${side}_color: color, - border_${side}_style: style, - border_${side}_width: - width.map(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue), - }) - - % endfor - - <%self:shorthand name="border" sub_properties="${' '.join( - 'border-%s-%s' % (side, prop) - for side in ['top', 'right', 'bottom', 'left'] - for prop in ['color', 'style', 'width'] - )}"> - let (color, style, width) = try!(super::parse_border(context, input)); - Ok(Longhands { - % for side in ["top", "right", "bottom", "left"]: - border_${side}_color: color.clone(), - border_${side}_style: style, - border_${side}_width: - width.map(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue), - % endfor - }) - - - <%self:shorthand name="border-radius" sub_properties="${' '.join( - 'border-%s-radius' % (corner) - for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left'] - )}"> - use app_units::Au; - use values::specified::{Length, LengthOrPercentage}; - use values::specified::BorderRadiusSize; - - let _ignored = context; - - fn parse_one_set_of_border_values(mut input: &mut Parser) - -> Result<[LengthOrPercentage; 4], ()> { - let mut count = 0; - let mut values = [LengthOrPercentage::Length(Length::Absolute(Au(0))); 4]; - while count < 4 { - if let Ok(value) = input.try(LengthOrPercentage::parse) { - values[count] = value; - count += 1; - } else { - break - } - } - - match count { - 1 => Ok([values[0], values[0], values[0], values[0]]), - 2 => Ok([values[0], values[1], values[0], values[1]]), - 3 => Ok([values[0], values[1], values[2], values[1]]), - 4 => Ok([values[0], values[1], values[2], values[3]]), - _ => Err(()), - } - } - - fn parse_one_set_of_border_radii(mut input: &mut Parser) - -> Result<[BorderRadiusSize; 4], ()> { - let widths = try!(parse_one_set_of_border_values(input)); - let mut heights = widths.clone(); - let mut radii_values = [BorderRadiusSize::zero(); 4]; - if input.try(|input| input.expect_delim('/')).is_ok() { - heights = try!(parse_one_set_of_border_values(input)); - } - for i in 0..radii_values.len() { - radii_values[i] = BorderRadiusSize::new(widths[i], heights[i]); - } - Ok(radii_values) - } - - let radii = try!(parse_one_set_of_border_radii(input)); - Ok(Longhands { - border_top_left_radius: Some(radii[0]), - border_top_right_radius: Some(radii[1]), - border_bottom_right_radius: Some(radii[2]), - border_bottom_left_radius: Some(radii[3]), - }) - - - <%self:shorthand name="outline" sub_properties="outline-color outline-style outline-width"> - use properties::longhands::outline_width; - use values::specified; - - let _unused = context; - let mut color = None; - let mut style = None; - let mut width = None; - let mut any = false; - loop { - if color.is_none() { - if let Ok(value) = input.try(specified::CSSColor::parse) { - color = Some(value); - any = true; - continue - } - } - if style.is_none() { - if let Ok(value) = input.try(specified::BorderStyle::parse) { - style = Some(value); - any = true; - continue - } - } - if width.is_none() { - if let Ok(value) = input.try(|input| outline_width::parse(context, input)) { - width = Some(value); - any = true; - continue - } - } - break - } - if any { - Ok(Longhands { - outline_color: color, - outline_style: style, - outline_width: width, - }) - } else { - Err(()) - } - - - <%self:shorthand name="font" sub_properties="font-style font-variant font-weight - font-size line-height font-family"> - use properties::longhands::{font_style, font_variant, font_weight, font_size, - line_height, font_family}; - let mut nb_normals = 0; - let mut style = None; - let mut variant = None; - let mut weight = None; - let size; - loop { - // Special-case 'normal' because it is valid in each of - // font-style, font-weight and font-variant. - // Leaves the values to None, 'normal' is the initial value for each of them. - if input.try(|input| input.expect_ident_matching("normal")).is_ok() { - nb_normals += 1; - continue; - } - if style.is_none() { - if let Ok(value) = input.try(|input| font_style::parse(context, input)) { - style = Some(value); - continue - } - } - if weight.is_none() { - if let Ok(value) = input.try(|input| font_weight::parse(context, input)) { - weight = Some(value); - 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 - } - #[inline] - fn count(opt: &Option) -> 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)) - }) - - - // Per CSS-TEXT 6.2, "for legacy reasons, UAs must treat `word-wrap` as an alternate name for - // the `overflow-wrap` property, as if it were a shorthand of `overflow-wrap`." - <%self:shorthand name="word-wrap" sub_properties="overflow-wrap"> - use properties::longhands::overflow_wrap; - Ok(Longhands { - overflow_wrap: Some(try!(overflow_wrap::parse(context, input))), - }) - - - <%self:shorthand name="list-style" - sub_properties="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 - // of times we see it. - let mut nones = 0u8; - let (mut image, mut position, mut list_style_type, mut any) = (None, None, None, false); - loop { - if input.try(|input| input.expect_ident_matching("none")).is_ok() { - nones = nones + 1; - 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; - 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 there are two `none`s, then we can't have a type or image; if there is one `none`, - // then we can't have both a type *and* an image; if there is no `none` then we're fine as - // long as we parsed something. - match (any, nones, list_style_type, image) { - (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 { - 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(()), - } - - - <%self:shorthand name="columns" sub_properties="column-count column-width" experimental="True"> - use properties::longhands::{column_count, column_width}; - let mut column_count = None; - let mut column_width = None; - 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 - } - - if column_count.is_none() { - if let Ok(value) = input.try(|input| column_count::parse(context, input)) { - column_count = Some(value); - continue - } - } - - if column_width.is_none() { - if let Ok(value) = input.try(|input| column_width::parse(context, input)) { - column_width = Some(value); - continue - } - } - - break - } - - let values = autos + column_count.iter().len() + column_width.iter().len(); - if values == 0 || values > 2 { - Err(()) - } else { - Ok(Longhands { - column_count: column_count, - column_width: column_width, - }) - } - - - <%self:shorthand name="overflow" sub_properties="overflow-x overflow-y"> - use properties::longhands::{overflow_x, overflow_y}; - - let overflow = try!(overflow_x::parse(context, input)); - Ok(Longhands { - overflow_x: Some(overflow), - overflow_y: Some(overflow_y::SpecifiedValue(overflow)), - }) - - - <%self:shorthand name="transition" - sub_properties="transition-property transition-duration transition-timing-function - transition-delay"> - use properties::longhands::{transition_delay, transition_duration, transition_property}; - use properties::longhands::{transition_timing_function}; - - struct SingleTransition { - transition_property: transition_property::SingleSpecifiedValue, - transition_duration: transition_duration::SingleSpecifiedValue, - transition_timing_function: transition_timing_function::SingleSpecifiedValue, - transition_delay: transition_delay::SingleSpecifiedValue, - } - - fn parse_one_transition(input: &mut Parser) -> Result { - let (mut property, mut duration) = (None, None); - let (mut timing_function, mut delay) = (None, None); - loop { - if property.is_none() { - if let Ok(value) = input.try(|input| transition_property::parse_one(input)) { - property = Some(value); - continue - } - } - - if duration.is_none() { - if let Ok(value) = input.try(|input| transition_duration::parse_one(input)) { - duration = Some(value); - continue - } - } - - if timing_function.is_none() { - if let Ok(value) = input.try(|input| { - transition_timing_function::parse_one(input) - }) { - timing_function = Some(value); - continue - } - } - - if delay.is_none() { - if let Ok(value) = input.try(|input| transition_delay::parse_one(input)) { - delay = Some(value); - continue; - } - } - - break - } - - if let Some(property) = property { - Ok(SingleTransition { - transition_property: property, - transition_duration: - duration.unwrap_or(transition_duration::get_initial_single_value()), - transition_timing_function: - timing_function.unwrap_or( - transition_timing_function::get_initial_single_value()), - transition_delay: - delay.unwrap_or(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, - }) - } - - let results = try!(input.parse_comma_separated(parse_one_transition)); - let (mut properties, mut durations) = (Vec::new(), Vec::new()); - let (mut timing_functions, mut delays) = (Vec::new(), Vec::new()); - for result in results { - properties.push(result.transition_property); - 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)), - }) - + <%include file="/shorthand/background.mako.rs" /> + <%include file="/shorthand/border.mako.rs" /> + <%include file="/shorthand/box.mako.rs" /> + <%include file="/shorthand/column.mako.rs" /> + <%include file="/shorthand/font.mako.rs" /> + <%include file="/shorthand/inherited_text.mako.rs" /> + <%include file="/shorthand/list.mako.rs" /> + <%include file="/shorthand/margin.mako.rs" /> + <%include file="/shorthand/outline.mako.rs" /> + <%include file="/shorthand/padding.mako.rs" /> } diff --git a/components/style/properties/shorthand/background.mako.rs b/components/style/properties/shorthand/background.mako.rs new file mode 100644 index 00000000000..4d6e3c8821a --- /dev/null +++ b/components/style/properties/shorthand/background.mako.rs @@ -0,0 +1,99 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +// TODO: other background-* properties +<%helpers:shorthand name="background" + sub_properties="background-color background-position background-repeat background-attachment + background-image background-size background-origin background-clip"> + use properties::longhands::{background_color, background_position, background_repeat, background_attachment}; + use properties::longhands::{background_image, background_size, background_origin, background_clip}; + + let mut color = None; + let mut image = None; + let mut position = None; + let mut repeat = None; + let mut size = None; + let mut attachment = None; + let mut any = false; + let mut origin = None; + let mut clip = None; + + loop { + if position.is_none() { + if let Ok(value) = input.try(|input| background_position::parse(context, input)) { + position = Some(value); + any = true; + + // Parse background size, if applicable. + size = input.try(|input| { + try!(input.expect_delim('/')); + background_size::parse(context, input) + }).ok(); + + continue + } + } + if color.is_none() { + if let Ok(value) = input.try(|input| background_color::parse(context, input)) { + color = Some(value); + any = true; + continue + } + } + if image.is_none() { + if let Ok(value) = input.try(|input| background_image::parse(context, input)) { + image = Some(value); + any = true; + continue + } + } + if repeat.is_none() { + if let Ok(value) = input.try(|input| background_repeat::parse(context, input)) { + repeat = Some(value); + any = true; + continue + } + } + if attachment.is_none() { + if let Ok(value) = input.try(|input| background_attachment::parse(context, input)) { + attachment = Some(value); + any = true; + continue + } + } + if origin.is_none() { + if let Ok(value) = input.try(|input| background_origin::parse(context, input)) { + origin = Some(value); + any = true; + continue + } + } + if clip.is_none() { + if let Ok(value) = input.try(|input| background_clip::parse(context, input)) { + clip = Some(value); + any = true; + 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(()) + } + + diff --git a/components/style/properties/shorthand/border.mako.rs b/components/style/properties/shorthand/border.mako.rs new file mode 100644 index 00000000000..f884708e76c --- /dev/null +++ b/components/style/properties/shorthand/border.mako.rs @@ -0,0 +1,149 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> +<% from data import to_rust_ident %> + +${helpers.four_sides_shorthand("border-color", "border-%s-color", "specified::CSSColor::parse")} +${helpers.four_sides_shorthand("border-style", "border-%s-style", + "specified::BorderStyle::parse")} +<%helpers:shorthand name="border-width" sub_properties="${ + ' '.join('border-%s-width' % side + for side in ['top', 'right', 'bottom', 'left'])}"> + use super::parse_four_sides; + use values::specified; + let _unused = context; + let (top, right, bottom, left) = try!(parse_four_sides(input, specified::parse_border_width)); + Ok(Longhands { + % for side in ["top", "right", "bottom", "left"]: + ${to_rust_ident('border-%s-width' % side)}: + Some(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue(${side})), + % endfor + }) + + + +pub fn parse_border(context: &ParserContext, input: &mut Parser) + -> Result<(Option, + Option, + Option), ()> { + use values::specified; + let _unused = context; + let mut color = None; + let mut style = None; + let mut width = None; + let mut any = false; + loop { + if color.is_none() { + if let Ok(value) = input.try(specified::CSSColor::parse) { + color = Some(value); + any = true; + continue + } + } + if style.is_none() { + if let Ok(value) = input.try(specified::BorderStyle::parse) { + style = Some(value); + any = true; + continue + } + } + if width.is_none() { + if let Ok(value) = input.try(specified::parse_border_width) { + width = Some(value); + any = true; + continue + } + } + break + } + if any { Ok((color, style, width)) } else { Err(()) } +} + + +% for side in ["top", "right", "bottom", "left"]: + <%helpers:shorthand name="border-${side}" sub_properties="${' '.join( + 'border-%s-%s' % (side, prop) + for prop in ['color', 'style', 'width'] + )}"> + let (color, style, width) = try!(super::parse_border(context, input)); + Ok(Longhands { + border_${side}_color: color, + border_${side}_style: style, + border_${side}_width: + width.map(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue), + }) + +% endfor + +<%helpers:shorthand name="border" sub_properties="${' '.join( + 'border-%s-%s' % (side, prop) + for side in ['top', 'right', 'bottom', 'left'] + for prop in ['color', 'style', 'width'] +)}"> + let (color, style, width) = try!(super::parse_border(context, input)); + Ok(Longhands { + % for side in ["top", "right", "bottom", "left"]: + border_${side}_color: color.clone(), + border_${side}_style: style, + border_${side}_width: + width.map(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue), + % endfor + }) + + +<%helpers:shorthand name="border-radius" sub_properties="${' '.join( + 'border-%s-radius' % (corner) + for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left'] +)}"> + use app_units::Au; + use values::specified::{Length, LengthOrPercentage}; + use values::specified::BorderRadiusSize; + + let _ignored = context; + + fn parse_one_set_of_border_values(mut input: &mut Parser) + -> Result<[LengthOrPercentage; 4], ()> { + let mut count = 0; + let mut values = [LengthOrPercentage::Length(Length::Absolute(Au(0))); 4]; + while count < 4 { + if let Ok(value) = input.try(LengthOrPercentage::parse) { + values[count] = value; + count += 1; + } else { + break + } + } + + match count { + 1 => Ok([values[0], values[0], values[0], values[0]]), + 2 => Ok([values[0], values[1], values[0], values[1]]), + 3 => Ok([values[0], values[1], values[2], values[1]]), + 4 => Ok([values[0], values[1], values[2], values[3]]), + _ => Err(()), + } + } + + fn parse_one_set_of_border_radii(mut input: &mut Parser) + -> Result<[BorderRadiusSize; 4], ()> { + let widths = try!(parse_one_set_of_border_values(input)); + let mut heights = widths.clone(); + let mut radii_values = [BorderRadiusSize::zero(); 4]; + if input.try(|input| input.expect_delim('/')).is_ok() { + heights = try!(parse_one_set_of_border_values(input)); + } + for i in 0..radii_values.len() { + radii_values[i] = BorderRadiusSize::new(widths[i], heights[i]); + } + Ok(radii_values) + } + + let radii = try!(parse_one_set_of_border_radii(input)); + Ok(Longhands { + border_top_left_radius: Some(radii[0]), + border_top_right_radius: Some(radii[1]), + border_bottom_right_radius: Some(radii[2]), + border_bottom_left_radius: Some(radii[3]), + }) + diff --git a/components/style/properties/shorthand/box.mako.rs b/components/style/properties/shorthand/box.mako.rs new file mode 100644 index 00000000000..48b49ca4811 --- /dev/null +++ b/components/style/properties/shorthand/box.mako.rs @@ -0,0 +1,109 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +<%helpers:shorthand name="overflow" sub_properties="overflow-x overflow-y"> + use properties::longhands::{overflow_x, overflow_y}; + + let overflow = try!(overflow_x::parse(context, input)); + Ok(Longhands { + overflow_x: Some(overflow), + overflow_y: Some(overflow_y::SpecifiedValue(overflow)), + }) + + +<%helpers:shorthand name="transition" + sub_properties="transition-property transition-duration transition-timing-function + transition-delay"> + use properties::longhands::{transition_delay, transition_duration, transition_property}; + use properties::longhands::{transition_timing_function}; + + struct SingleTransition { + transition_property: transition_property::SingleSpecifiedValue, + transition_duration: transition_duration::SingleSpecifiedValue, + transition_timing_function: transition_timing_function::SingleSpecifiedValue, + transition_delay: transition_delay::SingleSpecifiedValue, + } + + fn parse_one_transition(input: &mut Parser) -> Result { + let (mut property, mut duration) = (None, None); + let (mut timing_function, mut delay) = (None, None); + loop { + if property.is_none() { + if let Ok(value) = input.try(|input| transition_property::parse_one(input)) { + property = Some(value); + continue + } + } + + if duration.is_none() { + if let Ok(value) = input.try(|input| transition_duration::parse_one(input)) { + duration = Some(value); + continue + } + } + + if timing_function.is_none() { + if let Ok(value) = input.try(|input| { + transition_timing_function::parse_one(input) + }) { + timing_function = Some(value); + continue + } + } + + if delay.is_none() { + if let Ok(value) = input.try(|input| transition_delay::parse_one(input)) { + delay = Some(value); + continue; + } + } + + break + } + + if let Some(property) = property { + Ok(SingleTransition { + transition_property: property, + transition_duration: + duration.unwrap_or(transition_duration::get_initial_single_value()), + transition_timing_function: + timing_function.unwrap_or( + transition_timing_function::get_initial_single_value()), + transition_delay: + delay.unwrap_or(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, + }) + } + + let results = try!(input.parse_comma_separated(parse_one_transition)); + let (mut properties, mut durations) = (Vec::new(), Vec::new()); + let (mut timing_functions, mut delays) = (Vec::new(), Vec::new()); + for result in results { + properties.push(result.transition_property); + 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)), + }) + diff --git a/components/style/properties/shorthand/column.mako.rs b/components/style/properties/shorthand/column.mako.rs new file mode 100644 index 00000000000..8b3e91628da --- /dev/null +++ b/components/style/properties/shorthand/column.mako.rs @@ -0,0 +1,46 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +<%helpers:shorthand name="columns" sub_properties="column-count column-width" experimental="True"> + use properties::longhands::{column_count, column_width}; + let mut column_count = None; + let mut column_width = None; + 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 + } + + if column_count.is_none() { + if let Ok(value) = input.try(|input| column_count::parse(context, input)) { + column_count = Some(value); + continue + } + } + + if column_width.is_none() { + if let Ok(value) = input.try(|input| column_width::parse(context, input)) { + column_width = Some(value); + continue + } + } + + break + } + + let values = autos + column_count.iter().len() + column_width.iter().len(); + if values == 0 || values > 2 { + Err(()) + } else { + Ok(Longhands { + column_count: column_count, + column_width: column_width, + }) + } + diff --git a/components/style/properties/shorthand/font.mako.rs b/components/style/properties/shorthand/font.mako.rs new file mode 100644 index 00000000000..2268f2ff5d2 --- /dev/null +++ b/components/style/properties/shorthand/font.mako.rs @@ -0,0 +1,66 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +<%helpers:shorthand name="font" sub_properties="font-style font-variant font-weight + font-size line-height font-family"> + use properties::longhands::{font_style, font_variant, font_weight, font_size, + line_height, font_family}; + let mut nb_normals = 0; + let mut style = None; + let mut variant = None; + let mut weight = None; + let size; + loop { + // Special-case 'normal' because it is valid in each of + // font-style, font-weight and font-variant. + // Leaves the values to None, 'normal' is the initial value for each of them. + if input.try(|input| input.expect_ident_matching("normal")).is_ok() { + nb_normals += 1; + continue; + } + if style.is_none() { + if let Ok(value) = input.try(|input| font_style::parse(context, input)) { + style = Some(value); + continue + } + } + if weight.is_none() { + if let Ok(value) = input.try(|input| font_weight::parse(context, input)) { + weight = Some(value); + 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 + } + #[inline] + fn count(opt: &Option) -> 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)) + }) + diff --git a/components/style/properties/shorthand/inherited_text.mako.rs b/components/style/properties/shorthand/inherited_text.mako.rs new file mode 100644 index 00000000000..85aac33930d --- /dev/null +++ b/components/style/properties/shorthand/inherited_text.mako.rs @@ -0,0 +1,14 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +// Per CSS-TEXT 6.2, "for legacy reasons, UAs must treat `word-wrap` as an alternate name for +// the `overflow-wrap` property, as if it were a shorthand of `overflow-wrap`." +<%helpers:shorthand name="word-wrap" sub_properties="overflow-wrap"> + use properties::longhands::overflow_wrap; + Ok(Longhands { + overflow_wrap: Some(try!(overflow_wrap::parse(context, input))), + }) + diff --git a/components/style/properties/shorthand/list.mako.rs b/components/style/properties/shorthand/list.mako.rs new file mode 100644 index 00000000000..2b0f61b3a8c --- /dev/null +++ b/components/style/properties/shorthand/list.mako.rs @@ -0,0 +1,92 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +<%helpers:shorthand name="list-style" + sub_properties="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 + // of times we see it. + let mut nones = 0u8; + let (mut image, mut position, mut list_style_type, mut any) = (None, None, None, false); + loop { + if input.try(|input| input.expect_ident_matching("none")).is_ok() { + nones = nones + 1; + 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; + 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 there are two `none`s, then we can't have a type or image; if there is one `none`, + // then we can't have both a type *and* an image; if there is no `none` then we're fine as + // long as we parsed something. + match (any, nones, list_style_type, image) { + (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 { + 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(()), + } + diff --git a/components/style/properties/shorthand/margin.mako.rs b/components/style/properties/shorthand/margin.mako.rs new file mode 100644 index 00000000000..f83ccb54023 --- /dev/null +++ b/components/style/properties/shorthand/margin.mako.rs @@ -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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +${helpers.four_sides_shorthand("margin", "margin-%s", "specified::LengthOrPercentageOrAuto::parse")} diff --git a/components/style/properties/shorthand/outline.mako.rs b/components/style/properties/shorthand/outline.mako.rs new file mode 100644 index 00000000000..57481dfcb02 --- /dev/null +++ b/components/style/properties/shorthand/outline.mako.rs @@ -0,0 +1,49 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +<%helpers:shorthand name="outline" sub_properties="outline-color outline-style outline-width"> + use properties::longhands::outline_width; + use values::specified; + + let _unused = context; + let mut color = None; + let mut style = None; + let mut width = None; + let mut any = false; + loop { + if color.is_none() { + if let Ok(value) = input.try(specified::CSSColor::parse) { + color = Some(value); + any = true; + continue + } + } + if style.is_none() { + if let Ok(value) = input.try(specified::BorderStyle::parse) { + style = Some(value); + any = true; + continue + } + } + if width.is_none() { + if let Ok(value) = input.try(|input| outline_width::parse(context, input)) { + width = Some(value); + any = true; + continue + } + } + break + } + if any { + Ok(Longhands { + outline_color: color, + outline_style: style, + outline_width: width, + }) + } else { + Err(()) + } + diff --git a/components/style/properties/shorthand/padding.mako.rs b/components/style/properties/shorthand/padding.mako.rs new file mode 100644 index 00000000000..09363b950eb --- /dev/null +++ b/components/style/properties/shorthand/padding.mako.rs @@ -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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +${helpers.four_sides_shorthand("padding", "padding-%s", "specified::LengthOrPercentage::parse")}