/* 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 https://mozilla.org/MPL/2.0/. */ <%namespace name="helpers" file="/helpers.mako.rs" /> <% from data import to_rust_ident, ALL_SIDES, PHYSICAL_SIDES, maybe_moz_logical_alias %> ${helpers.four_sides_shorthand( "border-color", "border-%s-color", "specified::Color::parse", engines="gecko servo-2013 servo-2020", spec="https://drafts.csswg.org/css-backgrounds/#border-color", allow_quirks="Yes", )} ${helpers.four_sides_shorthand( "border-style", "border-%s-style", engines="gecko servo-2013 servo-2020", spec="https://drafts.csswg.org/css-backgrounds/#border-style", )} <%helpers:shorthand name="border-width" engines="gecko servo-2013 servo-2020" sub_properties="${ ' '.join('border-%s-width' % side for side in PHYSICAL_SIDES)}" spec="https://drafts.csswg.org/css-backgrounds/#border-width"> use crate::values::generics::rect::Rect; use crate::values::specified::{AllowQuirks, BorderSideWidth}; pub fn parse_value<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { let rect = Rect::parse_with(context, input, |_, i| { BorderSideWidth::parse_quirky(context, i, AllowQuirks::Yes) })?; Ok(expanded! { border_top_width: rect.0, border_right_width: rect.1, border_bottom_width: rect.2, border_left_width: rect.3, }) } impl<'a> ToCss for LonghandsToSerialize<'a> { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { % for side in PHYSICAL_SIDES: let ${side} = &self.border_${side}_width; % endfor Rect::new(top, right, bottom, left).to_css(dest) } } pub fn parse_border<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<(specified::Color, specified::BorderStyle, specified::BorderSideWidth), ParseError<'i>> { use crate::values::specified::{Color, BorderStyle, BorderSideWidth}; let _unused = context; let mut color = None; let mut style = None; let mut width = None; let mut any = false; loop { if width.is_none() { if let Ok(value) = input.try_parse(|i| BorderSideWidth::parse(context, i)) { width = Some(value); any = true; } } if style.is_none() { if let Ok(value) = input.try_parse(BorderStyle::parse) { style = Some(value); any = true; continue } } if color.is_none() { if let Ok(value) = input.try_parse(|i| Color::parse(context, i)) { color = Some(value); any = true; continue } } break } if !any { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) } Ok((color.unwrap_or(Color::CurrentColor), style.unwrap_or(BorderStyle::None), width.unwrap_or(BorderSideWidth::medium()))) } % for side, logical in ALL_SIDES: <% spec = "https://drafts.csswg.org/css-backgrounds/#border-%s" % side if logical: spec = "https://drafts.csswg.org/css-logical-props/#propdef-border-%s" % side %> <%helpers:shorthand name="border-${side}" engines="gecko servo-2013 servo-2020" sub_properties="${' '.join( 'border-%s-%s' % (side, prop) for prop in ['width', 'style', 'color'] )}" aliases="${maybe_moz_logical_alias(engine, (side, logical), '-moz-border-%s')}" spec="${spec}"> pub fn parse_value<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { let (color, style, width) = super::parse_border(context, input)?; Ok(expanded! { border_${to_rust_ident(side)}_color: color, border_${to_rust_ident(side)}_style: style, border_${to_rust_ident(side)}_width: width }) } impl<'a> ToCss for LonghandsToSerialize<'a> { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { crate::values::specified::border::serialize_directional_border( dest, self.border_${to_rust_ident(side)}_width, self.border_${to_rust_ident(side)}_style, self.border_${to_rust_ident(side)}_color ) } } % endfor <%helpers:shorthand name="border" engines="gecko servo-2013 servo-2020" sub_properties="${' '.join('border-%s-%s' % (side, prop) for side in PHYSICAL_SIDES for prop in ['width', 'style', 'color'] )} ${' '.join('border-image-%s' % name for name in ['outset', 'repeat', 'slice', 'source', 'width'])}" derive_value_info="False" spec="https://drafts.csswg.org/css-backgrounds/#border"> pub fn parse_value<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { use crate::properties::longhands::{border_image_outset, border_image_repeat, border_image_slice}; use crate::properties::longhands::{border_image_source, border_image_width}; let (color, style, width) = super::parse_border(context, input)?; Ok(expanded! { % for side in PHYSICAL_SIDES: border_${side}_color: color.clone(), border_${side}_style: style, border_${side}_width: width.clone(), % endfor // The ‘border’ shorthand resets ‘border-image’ to its initial value. // See https://drafts.csswg.org/css-backgrounds-3/#the-border-shorthands % for name in "outset repeat slice source width".split(): border_image_${name}: border_image_${name}::get_initial_specified_value(), % endfor }) } impl<'a> ToCss for LonghandsToSerialize<'a> { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { use crate::properties::longhands; // If any of the border-image longhands differ from their initial specified values we should not // invoke serialize_directional_border(), so there is no point in continuing on to compute all_equal. % for name in "outset repeat slice source width".split(): if *self.border_image_${name} != longhands::border_image_${name}::get_initial_specified_value() { return Ok(()); } % endfor let all_equal = { % for side in PHYSICAL_SIDES: let border_${side}_width = self.border_${side}_width; let border_${side}_style = self.border_${side}_style; let border_${side}_color = self.border_${side}_color; % endfor border_top_width == border_right_width && border_right_width == border_bottom_width && border_bottom_width == border_left_width && border_top_style == border_right_style && border_right_style == border_bottom_style && border_bottom_style == border_left_style && border_top_color == border_right_color && border_right_color == border_bottom_color && border_bottom_color == border_left_color }; // If all longhands are all present, then all sides should be the same, // so we can just one set of color/style/width if !all_equal { return Ok(()) } crate::values::specified::border::serialize_directional_border( dest, self.border_${side}_width, self.border_${side}_style, self.border_${side}_color ) } } // Just use the same as border-left. The border shorthand can't accept // any value that the sub-shorthand couldn't. <% border_left = "" %> impl SpecifiedValueInfo for Longhands { const SUPPORTED_TYPES: u8 = ${border_left}::SUPPORTED_TYPES; fn collect_completion_keywords(f: KeywordsCollectFn) { ${border_left}::collect_completion_keywords(f); } } <%helpers:shorthand name="border-radius" engines="gecko servo-2013 servo-2020" sub_properties="${' '.join( 'border-%s-radius' % (corner) for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left'] )}" extra_prefixes="webkit" spec="https://drafts.csswg.org/css-backgrounds/#border-radius" > use crate::values::generics::rect::Rect; use crate::values::generics::border::BorderCornerRadius; use crate::values::specified::border::BorderRadius; use crate::parser::Parse; pub fn parse_value<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { let radii = BorderRadius::parse(context, input)?; Ok(expanded! { border_top_left_radius: radii.top_left, border_top_right_radius: radii.top_right, border_bottom_right_radius: radii.bottom_right, border_bottom_left_radius: radii.bottom_left, }) } impl<'a> ToCss for LonghandsToSerialize<'a> { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { let LonghandsToSerialize { border_top_left_radius: &BorderCornerRadius(ref tl), border_top_right_radius: &BorderCornerRadius(ref tr), border_bottom_right_radius: &BorderCornerRadius(ref br), border_bottom_left_radius: &BorderCornerRadius(ref bl), } = *self; let widths = Rect::new(tl.width(), tr.width(), br.width(), bl.width()); let heights = Rect::new(tl.height(), tr.height(), br.height(), bl.height()); BorderRadius::serialize_rects(widths, heights, dest) } } <%helpers:shorthand name="border-image" engines="gecko servo-2013" sub_properties="border-image-outset border-image-repeat border-image-slice border-image-source border-image-width" extra_prefixes="moz:layout.css.prefixes.border-image webkit" spec="https://drafts.csswg.org/css-backgrounds-3/#border-image" > use crate::properties::longhands::{border_image_outset, border_image_repeat, border_image_slice}; use crate::properties::longhands::{border_image_source, border_image_width}; pub fn parse_value<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { % for name in "outset repeat slice source width".split(): let mut ${name} = border_image_${name}::get_initial_specified_value(); % endfor let mut any = false; let mut parsed_slice = false; let mut parsed_source = false; let mut parsed_repeat = false; loop { if !parsed_slice { if let Ok(value) = input.try_parse(|input| border_image_slice::parse(context, input)) { parsed_slice = true; any = true; slice = value; // Parse border image width and outset, if applicable. let maybe_width_outset: Result<_, ParseError> = input.try_parse(|input| { input.expect_delim('/')?; // Parse border image width, if applicable. let w = input.try_parse(|input| border_image_width::parse(context, input)).ok(); // Parse border image outset if applicable. let o = input.try_parse(|input| { input.expect_delim('/')?; border_image_outset::parse(context, input) }).ok(); if w.is_none() && o.is_none() { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) } Ok((w, o)) }); if let Ok((w, o)) = maybe_width_outset { if let Some(w) = w { width = w; } if let Some(o) = o { outset = o; } } continue; } } % for name in "source repeat".split(): if !parsed_${name} { if let Ok(value) = input.try_parse(|input| border_image_${name}::parse(context, input)) { ${name} = value; parsed_${name} = true; any = true; continue } } % endfor break } if !any { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) } Ok(expanded! { % for name in "outset repeat slice source width".split(): border_image_${name}: ${name}, % endfor }) } impl<'a> ToCss for LonghandsToSerialize<'a> { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { let mut has_any = false; % for name in "source slice outset width repeat".split(): let has_${name} = *self.border_image_${name} != border_image_${name}::get_initial_specified_value(); has_any = has_any || has_${name}; % endfor if has_source || !has_any { self.border_image_source.to_css(dest)?; if !has_any { return Ok(()); } } let needs_slice = has_slice || has_width || has_outset; if needs_slice { if has_source { dest.write_char(' ')?; } self.border_image_slice.to_css(dest)?; if has_width || has_outset { dest.write_str(" /")?; if has_width { dest.write_char(' ')?; self.border_image_width.to_css(dest)?; } if has_outset { dest.write_str(" / ")?; self.border_image_outset.to_css(dest)?; } } } if has_repeat { if has_source || needs_slice { dest.write_char(' ')?; } self.border_image_repeat.to_css(dest)?; } Ok(()) } } % for axis in ["block", "inline"]: % for prop in ["width", "style", "color"]: <% spec = "https://drafts.csswg.org/css-logical/#propdef-border-%s-%s" % (axis, prop) %> <%helpers:shorthand engines="gecko servo-2013 servo-2020" name="border-${axis}-${prop}" sub_properties="${' '.join( 'border-%s-%s-%s' % (axis, side, prop) for side in ['start', 'end'] )}" spec="${spec}"> use crate::properties::longhands::border_${axis}_start_${prop}; pub fn parse_value<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { let start_value = border_${axis}_start_${prop}::parse(context, input)?; let end_value = input.try_parse(|input| border_${axis}_start_${prop}::parse(context, input)) .unwrap_or_else(|_| start_value.clone()); Ok(expanded! { border_${axis}_start_${prop}: start_value, border_${axis}_end_${prop}: end_value, }) } impl<'a> ToCss for LonghandsToSerialize<'a> { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { self.border_${axis}_start_${prop}.to_css(dest)?; if self.border_${axis}_end_${prop} != self.border_${axis}_start_${prop} { dest.write_char(' ')?; self.border_${axis}_end_${prop}.to_css(dest)?; } Ok(()) } } % endfor % endfor % for axis in ["block", "inline"]: <% spec = "https://drafts.csswg.org/css-logical/#propdef-border-%s" % (axis) %> <%helpers:shorthand name="border-${axis}" engines="gecko servo-2013 servo-2020" sub_properties="${' '.join( 'border-%s-%s-width' % (axis, side) for side in ['start', 'end'] )} ${' '.join( 'border-%s-%s-style' % (axis, side) for side in ['start', 'end'] )} ${' '.join( 'border-%s-%s-color' % (axis, side) for side in ['start', 'end'] )}" spec="${spec}"> use crate::properties::shorthands::border_${axis}_start; pub fn parse_value<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { let start_value = border_${axis}_start::parse_value(context, input)?; Ok(expanded! { border_${axis}_start_width: start_value.border_${axis}_start_width.clone(), border_${axis}_end_width: start_value.border_${axis}_start_width, border_${axis}_start_style: start_value.border_${axis}_start_style.clone(), border_${axis}_end_style: start_value.border_${axis}_start_style, border_${axis}_start_color: start_value.border_${axis}_start_color.clone(), border_${axis}_end_color: start_value.border_${axis}_start_color, }) } impl<'a> ToCss for LonghandsToSerialize<'a> { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { crate::values::specified::border::serialize_directional_border( dest, self.border_${axis}_start_width, self.border_${axis}_start_style, self.border_${axis}_start_color ) } } % endfor