mirror of
https://github.com/servo/servo.git
synced 2025-08-24 14:48:21 +01:00
style: Rename the properties directories from "shorthand" to "shorthands", "longhand" to "longhands".
Bug: 1468651 Reviewed-by: heycam MozReview-Commit-ID: CY4THCC4zkX
This commit is contained in:
parent
5c15c59f56
commit
0a95513368
38 changed files with 37 additions and 37 deletions
253
components/style/properties/shorthands/background.mako.rs
Normal file
253
components/style/properties/shorthands/background.mako.rs
Normal file
|
@ -0,0 +1,253 @@
|
|||
/* 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-x background-position-y background-repeat
|
||||
background-attachment background-image background-size background-origin
|
||||
background-clip"
|
||||
spec="https://drafts.csswg.org/css-backgrounds/#the-background">
|
||||
use properties::longhands::{background_position_x, background_position_y, background_repeat};
|
||||
use properties::longhands::{background_attachment, background_image, background_size, background_origin};
|
||||
use properties::longhands::background_clip;
|
||||
use properties::longhands::background_clip::single_value::computed_value::T as Clip;
|
||||
use properties::longhands::background_origin::single_value::computed_value::T as Origin;
|
||||
use values::specified::{Color, Position, PositionComponent};
|
||||
use parser::Parse;
|
||||
|
||||
// FIXME(emilio): Should be the same type!
|
||||
impl From<background_origin::single_value::SpecifiedValue> for background_clip::single_value::SpecifiedValue {
|
||||
fn from(origin: background_origin::single_value::SpecifiedValue) ->
|
||||
background_clip::single_value::SpecifiedValue {
|
||||
match origin {
|
||||
background_origin::single_value::SpecifiedValue::ContentBox =>
|
||||
background_clip::single_value::SpecifiedValue::ContentBox,
|
||||
background_origin::single_value::SpecifiedValue::PaddingBox =>
|
||||
background_clip::single_value::SpecifiedValue::PaddingBox,
|
||||
background_origin::single_value::SpecifiedValue::BorderBox =>
|
||||
background_clip::single_value::SpecifiedValue::BorderBox,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let mut background_color = None;
|
||||
|
||||
% for name in "image position_x position_y repeat size attachment origin clip".split():
|
||||
// Vec grows from 0 to 4 by default on first push(). So allocate
|
||||
// with capacity 1, so in the common case of only one item we don't
|
||||
// way overallocate. Note that we always push at least one item if
|
||||
// parsing succeeds.
|
||||
let mut background_${name} = background_${name}::SpecifiedValue(Vec::with_capacity(1));
|
||||
% endfor
|
||||
input.parse_comma_separated(|input| {
|
||||
// background-color can only be in the last element, so if it
|
||||
// is parsed anywhere before, the value is invalid.
|
||||
if background_color.is_some() {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
}
|
||||
|
||||
% for name in "image position repeat size attachment origin clip".split():
|
||||
let mut ${name} = None;
|
||||
% endfor
|
||||
loop {
|
||||
if background_color.is_none() {
|
||||
if let Ok(value) = input.try(|i| Color::parse(context, i)) {
|
||||
background_color = Some(value);
|
||||
continue
|
||||
}
|
||||
}
|
||||
if position.is_none() {
|
||||
if let Ok(value) = input.try(|input| Position::parse(context, input)) {
|
||||
position = Some(value);
|
||||
|
||||
// Parse background size, if applicable.
|
||||
size = input.try(|input| {
|
||||
input.expect_delim('/')?;
|
||||
background_size::single_value::parse(context, input)
|
||||
}).ok();
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
% for name in "image repeat attachment origin clip".split():
|
||||
if ${name}.is_none() {
|
||||
if let Ok(value) = input.try(|input| background_${name}::single_value
|
||||
::parse(context, input)) {
|
||||
${name} = Some(value);
|
||||
continue
|
||||
}
|
||||
}
|
||||
% endfor
|
||||
break
|
||||
}
|
||||
if clip.is_none() {
|
||||
if let Some(origin) = origin {
|
||||
clip = Some(background_clip::single_value::SpecifiedValue::from(origin));
|
||||
}
|
||||
}
|
||||
let mut any = false;
|
||||
% for name in "image position repeat size attachment origin clip".split():
|
||||
any = any || ${name}.is_some();
|
||||
% endfor
|
||||
any = any || background_color.is_some();
|
||||
if any {
|
||||
if let Some(position) = position {
|
||||
background_position_x.0.push(position.horizontal);
|
||||
background_position_y.0.push(position.vertical);
|
||||
} else {
|
||||
background_position_x.0.push(PositionComponent::zero());
|
||||
background_position_y.0.push(PositionComponent::zero());
|
||||
}
|
||||
% for name in "image repeat size attachment origin clip".split():
|
||||
if let Some(bg_${name}) = ${name} {
|
||||
background_${name}.0.push(bg_${name});
|
||||
} else {
|
||||
background_${name}.0.push(background_${name}::single_value
|
||||
::get_initial_specified_value());
|
||||
}
|
||||
% endfor
|
||||
Ok(())
|
||||
} else {
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(expanded! {
|
||||
background_color: background_color.unwrap_or(Color::transparent()),
|
||||
background_image: background_image,
|
||||
background_position_x: background_position_x,
|
||||
background_position_y: background_position_y,
|
||||
background_repeat: background_repeat,
|
||||
background_attachment: background_attachment,
|
||||
background_size: background_size,
|
||||
background_origin: background_origin,
|
||||
background_clip: background_clip,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
let len = self.background_image.0.len();
|
||||
// There should be at least one declared value
|
||||
if len == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// If a value list length is differs then we don't do a shorthand serialization.
|
||||
// The exceptions to this is color which appears once only and is serialized
|
||||
// with the last item.
|
||||
% for name in "image position_x position_y size repeat origin clip attachment".split():
|
||||
if len != self.background_${name}.0.len() {
|
||||
return Ok(());
|
||||
}
|
||||
% endfor
|
||||
|
||||
for i in 0..len {
|
||||
% for name in "image position_x position_y repeat size attachment origin clip".split():
|
||||
let ${name} = &self.background_${name}.0[i];
|
||||
% endfor
|
||||
|
||||
if i != 0 {
|
||||
dest.write_str(", ")?;
|
||||
}
|
||||
|
||||
if i == len - 1 {
|
||||
self.background_color.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
}
|
||||
|
||||
image.to_css(dest)?;
|
||||
% for name in "repeat attachment".split():
|
||||
dest.write_str(" ")?;
|
||||
${name}.to_css(dest)?;
|
||||
% endfor
|
||||
|
||||
dest.write_str(" ")?;
|
||||
Position {
|
||||
horizontal: position_x.clone(),
|
||||
vertical: position_y.clone()
|
||||
}.to_css(dest)?;
|
||||
|
||||
if *size != background_size::single_value::get_initial_specified_value() {
|
||||
dest.write_str(" / ")?;
|
||||
size.to_css(dest)?;
|
||||
}
|
||||
|
||||
if *origin != Origin::PaddingBox || *clip != Clip::BorderBox {
|
||||
dest.write_str(" ")?;
|
||||
origin.to_css(dest)?;
|
||||
if *clip != From::from(*origin) {
|
||||
dest.write_str(" ")?;
|
||||
clip.to_css(dest)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand name="background-position"
|
||||
sub_properties="background-position-x background-position-y"
|
||||
spec="https://drafts.csswg.org/css-backgrounds-4/#the-background-position">
|
||||
use properties::longhands::{background_position_x, background_position_y};
|
||||
use values::specified::AllowQuirks;
|
||||
use values::specified::position::Position;
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
// Vec grows from 0 to 4 by default on first push(). So allocate with
|
||||
// capacity 1, so in the common case of only one item we don't way
|
||||
// overallocate. Note that we always push at least one item if parsing
|
||||
// succeeds.
|
||||
let mut position_x = background_position_x::SpecifiedValue(Vec::with_capacity(1));
|
||||
let mut position_y = background_position_y::SpecifiedValue(Vec::with_capacity(1));
|
||||
let mut any = false;
|
||||
|
||||
input.parse_comma_separated(|input| {
|
||||
let value = Position::parse_quirky(context, input, AllowQuirks::Yes)?;
|
||||
position_x.0.push(value.horizontal);
|
||||
position_y.0.push(value.vertical);
|
||||
any = true;
|
||||
Ok(())
|
||||
})?;
|
||||
if !any {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
}
|
||||
|
||||
Ok(expanded! {
|
||||
background_position_x: position_x,
|
||||
background_position_y: position_y,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
let len = self.background_position_x.0.len();
|
||||
if len == 0 || len != self.background_position_y.0.len() {
|
||||
return Ok(());
|
||||
}
|
||||
for i in 0..len {
|
||||
Position {
|
||||
horizontal: self.background_position_x.0[i].clone(),
|
||||
vertical: self.background_position_y.0[i].clone()
|
||||
}.to_css(dest)?;
|
||||
|
||||
if i < len - 1 {
|
||||
dest.write_str(", ")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
357
components/style/properties/shorthands/border.mako.rs
Normal file
357
components/style/properties/shorthands/border.mako.rs
Normal file
|
@ -0,0 +1,357 @@
|
|||
/* 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, ALL_SIDES, PHYSICAL_SIDES, maybe_moz_logical_alias %>
|
||||
|
||||
${helpers.four_sides_shorthand("border-color", "border-%s-color", "specified::Color::parse",
|
||||
spec="https://drafts.csswg.org/css-backgrounds/#border-color",
|
||||
allow_quirks=True)}
|
||||
|
||||
${helpers.four_sides_shorthand(
|
||||
"border-style",
|
||||
"border-%s-style",
|
||||
"specified::BorderStyle::parse",
|
||||
needs_context=False,
|
||||
spec="https://drafts.csswg.org/css-backgrounds/#border-style",
|
||||
)}
|
||||
|
||||
<%helpers:shorthand name="border-width" sub_properties="${
|
||||
' '.join('border-%s-width' % side
|
||||
for side in PHYSICAL_SIDES)}"
|
||||
spec="https://drafts.csswg.org/css-backgrounds/#border-width">
|
||||
use values::generics::rect::Rect;
|
||||
use values::specified::{AllowQuirks, BorderSideWidth};
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
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<W>(&self, dest: &mut CssWriter<W>) -> 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)
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
|
||||
pub fn parse_border<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<(specified::Color, specified::BorderStyle, specified::BorderSideWidth), ParseError<'i>> {
|
||||
use 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 color.is_none() {
|
||||
if let Ok(value) = input.try(|i| Color::parse(context, i)) {
|
||||
color = Some(value);
|
||||
any = true;
|
||||
continue
|
||||
}
|
||||
}
|
||||
if style.is_none() {
|
||||
if let Ok(value) = input.try(BorderStyle::parse) {
|
||||
style = Some(value);
|
||||
any = true;
|
||||
continue
|
||||
}
|
||||
}
|
||||
if width.is_none() {
|
||||
if let Ok(value) = input.try(|i| BorderSideWidth::parse(context, i)) {
|
||||
width = Some(value);
|
||||
any = true;
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
if any {
|
||||
Ok((color.unwrap_or_else(|| Color::currentcolor()),
|
||||
style.unwrap_or(BorderStyle::None),
|
||||
width.unwrap_or(BorderSideWidth::Medium)))
|
||||
} else {
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
}
|
||||
|
||||
% 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}"
|
||||
sub_properties="${' '.join(
|
||||
'border-%s-%s' % (side, prop)
|
||||
for prop in ['color', 'style', 'width']
|
||||
)}"
|
||||
alias="${maybe_moz_logical_alias(product, (side, logical), '-moz-border-%s')}"
|
||||
spec="${spec}">
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
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<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
super::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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
</%helpers:shorthand>
|
||||
% endfor
|
||||
|
||||
<%helpers:shorthand name="border"
|
||||
sub_properties="${' '.join('border-%s-%s' % (side, prop)
|
||||
for side in PHYSICAL_SIDES
|
||||
for prop in ['color', 'style', 'width'])}
|
||||
${' '.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<Longhands, ParseError<'i>> {
|
||||
use properties::longhands::{border_image_outset, border_image_repeat, border_image_slice};
|
||||
use 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<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
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 {
|
||||
super::serialize_directional_border(
|
||||
dest,
|
||||
self.border_${side}_width,
|
||||
self.border_${side}_style,
|
||||
self.border_${side}_color
|
||||
)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Just use the same as border-left. The border shorthand can't accept
|
||||
// any value that the sub-shorthand couldn't.
|
||||
<%
|
||||
border_left = "<::properties::shorthands::border_left::Longhands as SpecifiedValueInfo>"
|
||||
%>
|
||||
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>
|
||||
|
||||
<%helpers:shorthand name="border-radius" 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 values::generics::rect::Rect;
|
||||
use values::generics::border::BorderCornerRadius;
|
||||
use values::specified::border::BorderRadius;
|
||||
use parser::Parse;
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let radii = BorderRadius::parse(context, input)?;
|
||||
Ok(expanded! {
|
||||
border_top_left_radius: radii.top_left,
|
||||
border_top_right_radius: radii.top_right,
|
||||
border_bottom_right_radius: radii.bottom_right,
|
||||
border_bottom_left_radius: radii.bottom_left,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
let LonghandsToSerialize {
|
||||
border_top_left_radius: &BorderCornerRadius(ref tl),
|
||||
border_top_right_radius: &BorderCornerRadius(ref tr),
|
||||
border_bottom_right_radius: &BorderCornerRadius(ref br),
|
||||
border_bottom_left_radius: &BorderCornerRadius(ref bl),
|
||||
} = *self;
|
||||
|
||||
|
||||
let widths = Rect::new(tl.width(), tr.width(), br.width(), bl.width());
|
||||
let heights = Rect::new(tl.height(), tr.height(), br.height(), bl.height());
|
||||
|
||||
BorderRadius::serialize_rects(widths, heights, dest)
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand name="border-image" 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 properties::longhands::{border_image_outset, border_image_repeat, border_image_slice};
|
||||
use properties::longhands::{border_image_source, border_image_width};
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
% for name in "outset repeat slice source width".split():
|
||||
let mut border_image_${name} = border_image_${name}::get_initial_specified_value();
|
||||
% endfor
|
||||
|
||||
let result: Result<_, ParseError> = input.try(|input| {
|
||||
% for name in "outset repeat slice source width".split():
|
||||
let mut ${name} = None;
|
||||
% endfor
|
||||
loop {
|
||||
if slice.is_none() {
|
||||
if let Ok(value) = input.try(|input| border_image_slice::parse(context, input)) {
|
||||
slice = Some(value);
|
||||
// Parse border image width and outset, if applicable.
|
||||
let maybe_width_outset: Result<_, ParseError> = input.try(|input| {
|
||||
input.expect_delim('/')?;
|
||||
|
||||
// Parse border image width, if applicable.
|
||||
let w = input.try(|input|
|
||||
border_image_width::parse(context, input)).ok();
|
||||
|
||||
// Parse border image outset if applicable.
|
||||
let o = input.try(|input| {
|
||||
input.expect_delim('/')?;
|
||||
border_image_outset::parse(context, input)
|
||||
}).ok();
|
||||
if w.is_none() && o.is_none() {
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
else {
|
||||
Ok((w, o))
|
||||
}
|
||||
});
|
||||
if let Ok((w, o)) = maybe_width_outset {
|
||||
width = w;
|
||||
outset = o;
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
% for name in "source repeat".split():
|
||||
if ${name}.is_none() {
|
||||
if let Ok(value) = input.try(|input| border_image_${name}::parse(context, input)) {
|
||||
${name} = Some(value);
|
||||
continue
|
||||
}
|
||||
}
|
||||
% endfor
|
||||
break
|
||||
}
|
||||
let mut any = false;
|
||||
% for name in "outset repeat slice source width".split():
|
||||
any = any || ${name}.is_some();
|
||||
% endfor
|
||||
if any {
|
||||
% for name in "outset repeat slice source width".split():
|
||||
if let Some(b_${name}) = ${name} {
|
||||
border_image_${name} = b_${name};
|
||||
}
|
||||
% endfor
|
||||
Ok(())
|
||||
} else {
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
});
|
||||
result?;
|
||||
|
||||
Ok(expanded! {
|
||||
% for name in "outset repeat slice source width".split():
|
||||
border_image_${name}: border_image_${name},
|
||||
% endfor
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
self.border_image_source.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
self.border_image_slice.to_css(dest)?;
|
||||
dest.write_str(" / ")?;
|
||||
self.border_image_width.to_css(dest)?;
|
||||
dest.write_str(" / ")?;
|
||||
self.border_image_outset.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
self.border_image_repeat.to_css(dest)
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
442
components/style/properties/shorthands/box.mako.rs
Normal file
442
components/style/properties/shorthands/box.mako.rs
Normal file
|
@ -0,0 +1,442 @@
|
|||
/* 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"
|
||||
spec="https://drafts.csswg.org/css-overflow/#propdef-overflow"
|
||||
>
|
||||
use properties::longhands::overflow_x::parse as parse_overflow;
|
||||
% if product == "gecko":
|
||||
use properties::longhands::overflow_x::SpecifiedValue;
|
||||
% endif
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
% if product == "gecko":
|
||||
let moz_kw_found = input.try(|input| {
|
||||
try_match_ident_ignore_ascii_case! { input,
|
||||
"-moz-scrollbars-horizontal" => {
|
||||
Ok(expanded! {
|
||||
overflow_x: SpecifiedValue::Scroll,
|
||||
overflow_y: SpecifiedValue::Hidden,
|
||||
})
|
||||
}
|
||||
"-moz-scrollbars-vertical" => {
|
||||
Ok(expanded! {
|
||||
overflow_x: SpecifiedValue::Hidden,
|
||||
overflow_y: SpecifiedValue::Scroll,
|
||||
})
|
||||
}
|
||||
"-moz-scrollbars-none" => {
|
||||
Ok(expanded! {
|
||||
overflow_x: SpecifiedValue::Hidden,
|
||||
overflow_y: SpecifiedValue::Hidden,
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
if moz_kw_found.is_ok() {
|
||||
return moz_kw_found
|
||||
}
|
||||
% endif
|
||||
let overflow_x = parse_overflow(context, input)?;
|
||||
let overflow_y =
|
||||
input.try(|i| parse_overflow(context, i)).unwrap_or(overflow_x);
|
||||
Ok(expanded! {
|
||||
overflow_x: overflow_x,
|
||||
overflow_y: overflow_y,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
self.overflow_x.to_css(dest)?;
|
||||
if self.overflow_x != self.overflow_y {
|
||||
dest.write_char(' ')?;
|
||||
self.overflow_y.to_css(dest)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand
|
||||
name="overflow-clip-box"
|
||||
sub_properties="overflow-clip-box-block overflow-clip-box-inline"
|
||||
enabled_in="ua"
|
||||
gecko_pref="layout.css.overflow-clip-box.enabled"
|
||||
spec="Internal, may be standardized in the future "
|
||||
"(https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box)"
|
||||
products="gecko"
|
||||
>
|
||||
use values::specified::OverflowClipBox;
|
||||
pub fn parse_value<'i, 't>(
|
||||
_: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let block_value = OverflowClipBox::parse(input)?;
|
||||
let inline_value =
|
||||
input.try(|input| OverflowClipBox::parse(input)).unwrap_or(block_value);
|
||||
|
||||
Ok(expanded! {
|
||||
overflow_clip_box_block: block_value,
|
||||
overflow_clip_box_inline: inline_value,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
self.overflow_clip_box_block.to_css(dest)?;
|
||||
|
||||
if self.overflow_clip_box_block != self.overflow_clip_box_inline {
|
||||
dest.write_str(" ")?;
|
||||
self.overflow_clip_box_inline.to_css(dest)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
macro_rules! try_parse_one {
|
||||
($context: expr, $input: expr, $var: ident, $prop_module: ident) => {
|
||||
if $var.is_none() {
|
||||
if let Ok(value) = $input.try(|i| {
|
||||
$prop_module::single_value::parse($context, i)
|
||||
}) {
|
||||
$var = Some(value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
<%helpers:shorthand name="transition"
|
||||
extra_prefixes="moz:layout.css.prefixes.transitions webkit"
|
||||
sub_properties="transition-property transition-duration
|
||||
transition-timing-function
|
||||
transition-delay"
|
||||
spec="https://drafts.csswg.org/css-transitions/#propdef-transition">
|
||||
use parser::Parse;
|
||||
% for prop in "delay duration property timing_function".split():
|
||||
use properties::longhands::transition_${prop};
|
||||
% endfor
|
||||
use values::specified::TransitionProperty;
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
struct SingleTransition {
|
||||
% for prop in "duration timing_function delay".split():
|
||||
transition_${prop}: transition_${prop}::SingleSpecifiedValue,
|
||||
% endfor
|
||||
// Unlike other properties, transition-property uses an Option<> to
|
||||
// represent 'none' as `None`.
|
||||
transition_property: Option<TransitionProperty>,
|
||||
}
|
||||
|
||||
fn parse_one_transition<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<SingleTransition,ParseError<'i>> {
|
||||
% for prop in "property duration timing_function delay".split():
|
||||
let mut ${prop} = None;
|
||||
% endfor
|
||||
|
||||
let mut parsed = 0;
|
||||
loop {
|
||||
parsed += 1;
|
||||
|
||||
try_parse_one!(context, input, duration, transition_duration);
|
||||
try_parse_one!(context, input, timing_function, transition_timing_function);
|
||||
try_parse_one!(context, input, delay, transition_delay);
|
||||
// Must check 'transition-property' after 'transition-timing-function' since
|
||||
// 'transition-property' accepts any keyword.
|
||||
if property.is_none() {
|
||||
if let Ok(value) = input.try(|i| TransitionProperty::parse(context, i)) {
|
||||
property = Some(Some(value));
|
||||
continue;
|
||||
}
|
||||
|
||||
if input.try(|i| i.expect_ident_matching("none")).is_ok() {
|
||||
// 'none' is not a valid value for <single-transition-property>,
|
||||
// so it's not acceptable in the function above.
|
||||
property = Some(None);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
parsed -= 1;
|
||||
break
|
||||
}
|
||||
|
||||
if parsed != 0 {
|
||||
Ok(SingleTransition {
|
||||
% for prop in "duration timing_function delay".split():
|
||||
transition_${prop}: ${prop}.unwrap_or_else(transition_${prop}::single_value
|
||||
::get_initial_specified_value),
|
||||
% endfor
|
||||
transition_property: property.unwrap_or(
|
||||
Some(transition_property::single_value::get_initial_specified_value())),
|
||||
})
|
||||
} else {
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
}
|
||||
|
||||
% for prop in "property duration timing_function delay".split():
|
||||
let mut ${prop}s = Vec::new();
|
||||
% endfor
|
||||
|
||||
let results = input.parse_comma_separated(|i| parse_one_transition(context, i))?;
|
||||
let multiple_items = results.len() >= 2;
|
||||
for result in results {
|
||||
if let Some(value) = result.transition_property {
|
||||
propertys.push(value);
|
||||
} else if multiple_items {
|
||||
// If there is more than one item, and any of transitions has 'none',
|
||||
// then it's invalid. Othersize, leave propertys to be empty (which
|
||||
// means "transition-property: none");
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
}
|
||||
|
||||
% for prop in "duration timing_function delay".split():
|
||||
${prop}s.push(result.transition_${prop});
|
||||
% endfor
|
||||
}
|
||||
|
||||
Ok(expanded! {
|
||||
% for prop in "property duration timing_function delay".split():
|
||||
transition_${prop}: transition_${prop}::SpecifiedValue(${prop}s),
|
||||
% endfor
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
let property_len = self.transition_property.0.len();
|
||||
|
||||
// There are two cases that we can do shorthand serialization:
|
||||
// * when all value lists have the same length, or
|
||||
// * when transition-property is none, and other value lists have exactly one item.
|
||||
if property_len == 0 {
|
||||
% for name in "duration delay timing_function".split():
|
||||
if self.transition_${name}.0.len() != 1 {
|
||||
return Ok(());
|
||||
}
|
||||
% endfor
|
||||
} else {
|
||||
% for name in "duration delay timing_function".split():
|
||||
if self.transition_${name}.0.len() != property_len {
|
||||
return Ok(());
|
||||
}
|
||||
% endfor
|
||||
}
|
||||
|
||||
// Representative length.
|
||||
let len = self.transition_duration.0.len();
|
||||
|
||||
for i in 0..len {
|
||||
if i != 0 {
|
||||
dest.write_str(", ")?;
|
||||
}
|
||||
if property_len == 0 {
|
||||
dest.write_str("none")?;
|
||||
} else {
|
||||
self.transition_property.0[i].to_css(dest)?;
|
||||
}
|
||||
% for name in "duration timing_function delay".split():
|
||||
dest.write_str(" ")?;
|
||||
self.transition_${name}.0[i].to_css(dest)?;
|
||||
% endfor
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand name="animation"
|
||||
extra_prefixes="moz:layout.css.prefixes.animations webkit"
|
||||
sub_properties="animation-name animation-duration
|
||||
animation-timing-function animation-delay
|
||||
animation-iteration-count animation-direction
|
||||
animation-fill-mode animation-play-state"
|
||||
allowed_in_keyframe_block="False"
|
||||
spec="https://drafts.csswg.org/css-animations/#propdef-animation">
|
||||
<%
|
||||
props = "name duration timing_function delay iteration_count \
|
||||
direction fill_mode play_state".split()
|
||||
%>
|
||||
% for prop in props:
|
||||
use properties::longhands::animation_${prop};
|
||||
% endfor
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
struct SingleAnimation {
|
||||
% for prop in props:
|
||||
animation_${prop}: animation_${prop}::SingleSpecifiedValue,
|
||||
% endfor
|
||||
}
|
||||
|
||||
fn parse_one_animation<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<SingleAnimation, ParseError<'i>> {
|
||||
% for prop in props:
|
||||
let mut ${prop} = None;
|
||||
% endfor
|
||||
|
||||
let mut parsed = 0;
|
||||
// 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 {
|
||||
parsed += 1;
|
||||
try_parse_one!(context, input, duration, animation_duration);
|
||||
try_parse_one!(context, input, timing_function, animation_timing_function);
|
||||
try_parse_one!(context, input, delay, animation_delay);
|
||||
try_parse_one!(context, input, iteration_count, animation_iteration_count);
|
||||
try_parse_one!(context, input, direction, animation_direction);
|
||||
try_parse_one!(context, input, fill_mode, animation_fill_mode);
|
||||
try_parse_one!(context, input, play_state, animation_play_state);
|
||||
try_parse_one!(context, input, name, animation_name);
|
||||
|
||||
parsed -= 1;
|
||||
break
|
||||
}
|
||||
|
||||
// If nothing is parsed, this is an invalid entry.
|
||||
if parsed == 0 {
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
} else {
|
||||
Ok(SingleAnimation {
|
||||
% for prop in props:
|
||||
animation_${prop}: ${prop}.unwrap_or_else(animation_${prop}::single_value
|
||||
::get_initial_specified_value),
|
||||
% endfor
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
% for prop in props:
|
||||
let mut ${prop}s = vec![];
|
||||
% endfor
|
||||
|
||||
let results = input.parse_comma_separated(|i| parse_one_animation(context, i))?;
|
||||
for result in results.into_iter() {
|
||||
% for prop in props:
|
||||
${prop}s.push(result.animation_${prop});
|
||||
% endfor
|
||||
}
|
||||
|
||||
Ok(expanded! {
|
||||
% for prop in props:
|
||||
animation_${prop}: animation_${prop}::SpecifiedValue(${prop}s),
|
||||
% endfor
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
let len = self.animation_name.0.len();
|
||||
// There should be at least one declared value
|
||||
if len == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// If any value list length is differs then we don't do a shorthand serialization
|
||||
// either.
|
||||
% for name in props[1:]:
|
||||
if len != self.animation_${name}.0.len() {
|
||||
return Ok(())
|
||||
}
|
||||
% endfor
|
||||
|
||||
for i in 0..len {
|
||||
if i != 0 {
|
||||
dest.write_str(", ")?;
|
||||
}
|
||||
|
||||
% for name in props[1:]:
|
||||
self.animation_${name}.0[i].to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
% endfor
|
||||
self.animation_name.0[i].to_css(dest)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand name="scroll-snap-type" products="gecko"
|
||||
gecko_pref="layout.css.scroll-snap.enabled"
|
||||
sub_properties="scroll-snap-type-x scroll-snap-type-y"
|
||||
spec="https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-snap-type">
|
||||
use properties::longhands::scroll_snap_type_x;
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let result = scroll_snap_type_x::parse(context, input)?;
|
||||
Ok(expanded! {
|
||||
scroll_snap_type_x: result,
|
||||
scroll_snap_type_y: result,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
// Serializes into the single keyword value if both scroll-snap-type-x and scroll-snap-type-y are same.
|
||||
// Otherwise into an empty string. This is done to match Gecko's behaviour.
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
if self.scroll_snap_type_x == self.scroll_snap_type_y {
|
||||
self.scroll_snap_type_x.to_css(dest)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand name="overscroll-behavior" products="gecko"
|
||||
gecko_pref="layout.css.overscroll-behavior.enabled"
|
||||
sub_properties="overscroll-behavior-x overscroll-behavior-y"
|
||||
spec="https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties">
|
||||
pub fn parse_value<'i, 't>(
|
||||
_: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
use values::specified::OverscrollBehavior;
|
||||
let behavior_x = OverscrollBehavior::parse(input)?;
|
||||
let behavior_y = input.try(OverscrollBehavior::parse).unwrap_or(behavior_x);
|
||||
Ok(expanded! {
|
||||
overscroll_behavior_x: behavior_x,
|
||||
overscroll_behavior_y: behavior_y,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
// Serializes into the single keyword value if both overscroll-behavior-x and overscroll-behavior-y are same.
|
||||
// Otherwise into two values separated by a space.
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
self.overscroll_behavior_x.to_css(dest)?;
|
||||
if self.overscroll_behavior_y != self.overscroll_behavior_x {
|
||||
dest.write_str(" ")?;
|
||||
self.overscroll_behavior_y.to_css(dest)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
98
components/style/properties/shorthands/column.mako.rs
Normal file
98
components/style/properties/shorthands/column.mako.rs
Normal file
|
@ -0,0 +1,98 @@
|
|||
/* 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-width column-count"
|
||||
servo_pref="layout.columns.enabled",
|
||||
derive_serialize="True"
|
||||
extra_prefixes="moz" spec="https://drafts.csswg.org/css-multicol/#propdef-columns">
|
||||
use properties::longhands::{column_count, column_width};
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let mut column_count = None;
|
||||
let mut column_width = None;
|
||||
let mut autos = 0;
|
||||
|
||||
loop {
|
||||
if input.try(|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(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
} else {
|
||||
Ok(expanded! {
|
||||
column_count: unwrap_or_initial!(column_count),
|
||||
column_width: unwrap_or_initial!(column_width),
|
||||
})
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand name="column-rule" products="gecko" extra_prefixes="moz"
|
||||
sub_properties="column-rule-width column-rule-style column-rule-color"
|
||||
derive_serialize="True"
|
||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule">
|
||||
use properties::longhands::{column_rule_width, column_rule_style};
|
||||
use properties::longhands::column_rule_color;
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
% for name in "width style color".split():
|
||||
let mut column_rule_${name} = None;
|
||||
% endfor
|
||||
let mut any = false;
|
||||
|
||||
loop {
|
||||
% for name in "width style color".split():
|
||||
if column_rule_${name}.is_none() {
|
||||
if let Ok(value) = input.try(|input|
|
||||
column_rule_${name}::parse(context, input)) {
|
||||
column_rule_${name} = Some(value);
|
||||
any = true;
|
||||
continue
|
||||
}
|
||||
}
|
||||
% endfor
|
||||
|
||||
break
|
||||
}
|
||||
if any {
|
||||
Ok(expanded! {
|
||||
column_rule_width: unwrap_or_initial!(column_rule_width),
|
||||
column_rule_style: unwrap_or_initial!(column_rule_style),
|
||||
column_rule_color: unwrap_or_initial!(column_rule_color),
|
||||
})
|
||||
} else {
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
402
components/style/properties/shorthands/font.mako.rs
Normal file
402
components/style/properties/shorthands/font.mako.rs
Normal file
|
@ -0,0 +1,402 @@
|
|||
/* 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 SYSTEM_FONT_LONGHANDS %>
|
||||
|
||||
<%helpers:shorthand name="font"
|
||||
sub_properties="font-style font-variant-caps font-weight font-stretch
|
||||
font-size line-height font-family
|
||||
${'font-size-adjust' if product == 'gecko' else ''}
|
||||
${'font-kerning' if product == 'gecko' else ''}
|
||||
${'font-optical-sizing' if product == 'gecko' else ''}
|
||||
${'font-variant-alternates' if product == 'gecko' else ''}
|
||||
${'font-variant-east-asian' if product == 'gecko' else ''}
|
||||
${'font-variant-ligatures' if product == 'gecko' else ''}
|
||||
${'font-variant-numeric' if product == 'gecko' else ''}
|
||||
${'font-variant-position' if product == 'gecko' else ''}
|
||||
${'font-language-override' if product == 'gecko' else ''}
|
||||
${'font-feature-settings' if product == 'gecko' else ''}
|
||||
${'font-variation-settings' if product == 'gecko' else ''}"
|
||||
derive_value_info="False"
|
||||
spec="https://drafts.csswg.org/css-fonts-3/#propdef-font">
|
||||
use parser::Parse;
|
||||
use properties::longhands::{font_family, font_style, font_weight, font_stretch};
|
||||
use properties::longhands::font_variant_caps;
|
||||
#[cfg(feature = "gecko")]
|
||||
use properties::longhands::system_font::SystemFont;
|
||||
use values::specified::text::LineHeight;
|
||||
use values::specified::FontSize;
|
||||
use values::specified::font::{FontStretch, FontStretchKeyword};
|
||||
|
||||
<%
|
||||
gecko_sub_properties = "kerning language_override size_adjust \
|
||||
variant_alternates variant_east_asian \
|
||||
variant_ligatures variant_numeric \
|
||||
variant_position feature_settings \
|
||||
variation_settings optical_sizing".split()
|
||||
%>
|
||||
% if product == "gecko":
|
||||
% for prop in gecko_sub_properties:
|
||||
use properties::longhands::font_${prop};
|
||||
% endfor
|
||||
% endif
|
||||
use self::font_family::SpecifiedValue as FontFamily;
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let mut nb_normals = 0;
|
||||
let mut style = None;
|
||||
let mut variant_caps = None;
|
||||
let mut weight = None;
|
||||
let mut stretch = None;
|
||||
let size;
|
||||
% if product == "gecko":
|
||||
if let Ok(sys) = input.try(SystemFont::parse) {
|
||||
return Ok(expanded! {
|
||||
% for name in SYSTEM_FONT_LONGHANDS:
|
||||
% if name == "font_size":
|
||||
${name}: FontSize::system_font(sys),
|
||||
% else:
|
||||
${name}: ${name}::SpecifiedValue::system_font(sys),
|
||||
% endif
|
||||
% endfor
|
||||
// line-height is just reset to initial
|
||||
line_height: LineHeight::normal(),
|
||||
})
|
||||
}
|
||||
% endif
|
||||
loop {
|
||||
// Special-case 'normal' because it is valid in each of
|
||||
// font-style, font-weight, font-variant and font-stretch.
|
||||
// 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_caps.is_none() {
|
||||
if let Ok(value) = input.try(|input| font_variant_caps::parse(context, input)) {
|
||||
variant_caps = Some(value);
|
||||
continue
|
||||
}
|
||||
}
|
||||
if stretch.is_none() {
|
||||
if let Ok(value) = input.try(FontStretchKeyword::parse) {
|
||||
stretch = Some(FontStretch::Keyword(value));
|
||||
continue
|
||||
}
|
||||
}
|
||||
size = Some(FontSize::parse(context, input)?);
|
||||
break
|
||||
}
|
||||
|
||||
let size = match size {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
};
|
||||
|
||||
let line_height = if input.try(|input| input.expect_delim('/')).is_ok() {
|
||||
Some(LineHeight::parse(context, input)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
#[inline]
|
||||
fn count<T>(opt: &Option<T>) -> u8 {
|
||||
if opt.is_some() { 1 } else { 0 }
|
||||
}
|
||||
|
||||
if (count(&style) + count(&weight) + count(&variant_caps) + count(&stretch) + nb_normals) > 4 {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
|
||||
let family = FontFamily::parse_specified(input)?;
|
||||
Ok(expanded! {
|
||||
% for name in "style weight stretch variant_caps".split():
|
||||
font_${name}: unwrap_or_initial!(font_${name}, ${name}),
|
||||
% endfor
|
||||
font_size: size,
|
||||
line_height: line_height.unwrap_or(LineHeight::normal()),
|
||||
font_family: family,
|
||||
% if product == "gecko":
|
||||
% for name in gecko_sub_properties:
|
||||
font_${name}: font_${name}::get_initial_specified_value(),
|
||||
% endfor
|
||||
% endif
|
||||
})
|
||||
}
|
||||
|
||||
% if product == "gecko":
|
||||
enum CheckSystemResult {
|
||||
AllSystem(SystemFont),
|
||||
SomeSystem,
|
||||
None
|
||||
}
|
||||
% endif
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
% if product == "gecko":
|
||||
match self.check_system() {
|
||||
CheckSystemResult::AllSystem(sys) => return sys.to_css(dest),
|
||||
CheckSystemResult::SomeSystem => return Ok(()),
|
||||
CheckSystemResult::None => {}
|
||||
}
|
||||
% endif
|
||||
|
||||
% if product == "gecko":
|
||||
if let Some(v) = self.font_optical_sizing {
|
||||
if v != &font_optical_sizing::get_initial_specified_value() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
if let Some(v) = self.font_variation_settings {
|
||||
if v != &font_variation_settings::get_initial_specified_value() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
% for name in gecko_sub_properties:
|
||||
% if name != "optical_sizing" and name != "variation_settings":
|
||||
if self.font_${name} != &font_${name}::get_initial_specified_value() {
|
||||
return Ok(());
|
||||
}
|
||||
% endif
|
||||
% endfor
|
||||
% endif
|
||||
|
||||
// Only font-stretch keywords are allowed as part as the font
|
||||
// shorthand.
|
||||
let font_stretch = match *self.font_stretch {
|
||||
FontStretch::Keyword(kw) => kw,
|
||||
FontStretch::Stretch(percentage) => {
|
||||
match FontStretchKeyword::from_percentage(percentage.get()) {
|
||||
Some(kw) => kw,
|
||||
None => return Ok(()),
|
||||
}
|
||||
}
|
||||
FontStretch::System(..) => return Ok(()),
|
||||
};
|
||||
|
||||
% for name in "style variant_caps weight".split():
|
||||
if self.font_${name} != &font_${name}::get_initial_specified_value() {
|
||||
self.font_${name}.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
}
|
||||
% endfor
|
||||
|
||||
if font_stretch != FontStretchKeyword::Normal {
|
||||
font_stretch.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
}
|
||||
|
||||
self.font_size.to_css(dest)?;
|
||||
|
||||
if *self.line_height != LineHeight::normal() {
|
||||
dest.write_str("/")?;
|
||||
self.line_height.to_css(dest)?;
|
||||
}
|
||||
|
||||
dest.write_str(" ")?;
|
||||
self.font_family.to_css(dest)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> LonghandsToSerialize<'a> {
|
||||
% if product == "gecko":
|
||||
/// Check if some or all members are system fonts
|
||||
fn check_system(&self) -> CheckSystemResult {
|
||||
let mut sys = None;
|
||||
let mut all = true;
|
||||
|
||||
% for prop in SYSTEM_FONT_LONGHANDS:
|
||||
% if prop == "font_optical_sizing" or prop == "font_variation_settings":
|
||||
if let Some(value) = self.${prop} {
|
||||
% else:
|
||||
{
|
||||
let value = self.${prop};
|
||||
% endif
|
||||
match value.get_system() {
|
||||
Some(s) => {
|
||||
debug_assert!(sys.is_none() || s == sys.unwrap());
|
||||
sys = Some(s);
|
||||
}
|
||||
None => {
|
||||
all = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
% endfor
|
||||
if self.line_height != &LineHeight::normal() {
|
||||
all = false
|
||||
}
|
||||
if all {
|
||||
CheckSystemResult::AllSystem(sys.unwrap())
|
||||
} else if sys.is_some() {
|
||||
CheckSystemResult::SomeSystem
|
||||
} else {
|
||||
CheckSystemResult::None
|
||||
}
|
||||
}
|
||||
% endif
|
||||
}
|
||||
|
||||
<%
|
||||
subprops_for_value_info = ["font_style", "font_weight", "font_stretch",
|
||||
"font_variant_caps", "font_size", "font_family"]
|
||||
subprops_for_value_info = [
|
||||
"<longhands::{}::SpecifiedValue as SpecifiedValueInfo>".format(p)
|
||||
for p in subprops_for_value_info
|
||||
]
|
||||
%>
|
||||
impl SpecifiedValueInfo for Longhands {
|
||||
const SUPPORTED_TYPES: u8 = 0
|
||||
% for p in subprops_for_value_info:
|
||||
| ${p}::SUPPORTED_TYPES
|
||||
% endfor
|
||||
;
|
||||
|
||||
fn collect_completion_keywords(f: KeywordsCollectFn) {
|
||||
% for p in subprops_for_value_info:
|
||||
${p}::collect_completion_keywords(f);
|
||||
% endfor
|
||||
<longhands::system_font::SystemFont as SpecifiedValueInfo>::collect_completion_keywords(f);
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand name="font-variant"
|
||||
sub_properties="font-variant-caps
|
||||
${'font-variant-alternates' if product == 'gecko' else ''}
|
||||
${'font-variant-east-asian' if product == 'gecko' else ''}
|
||||
${'font-variant-ligatures' if product == 'gecko' else ''}
|
||||
${'font-variant-numeric' if product == 'gecko' else ''}
|
||||
${'font-variant-position' if product == 'gecko' else ''}"
|
||||
spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-variant">
|
||||
<% gecko_sub_properties = "alternates east_asian ligatures numeric position".split() %>
|
||||
<%
|
||||
sub_properties = ["caps"]
|
||||
if product == "gecko":
|
||||
sub_properties += gecko_sub_properties
|
||||
%>
|
||||
|
||||
% for prop in sub_properties:
|
||||
use properties::longhands::font_variant_${prop};
|
||||
% endfor
|
||||
#[allow(unused_imports)]
|
||||
use values::specified::FontVariantLigatures;
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
% for prop in sub_properties:
|
||||
let mut ${prop} = None;
|
||||
% endfor
|
||||
|
||||
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
|
||||
// Leave the values to None, 'normal' is the initial value for all the sub properties.
|
||||
} else if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||||
// The 'none' value sets 'font-variant-ligatures' to 'none' and resets all other sub properties
|
||||
// to their initial value.
|
||||
% if product == "gecko":
|
||||
ligatures = Some(FontVariantLigatures::none());
|
||||
% endif
|
||||
} else {
|
||||
let mut has_custom_value: bool = false;
|
||||
loop {
|
||||
if input.try(|input| input.expect_ident_matching("normal")).is_ok() ||
|
||||
input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
% for prop in sub_properties:
|
||||
if ${prop}.is_none() {
|
||||
if let Ok(value) = input.try(|i| font_variant_${prop}::parse(context, i)) {
|
||||
has_custom_value = true;
|
||||
${prop} = Some(value);
|
||||
continue
|
||||
}
|
||||
}
|
||||
% endfor
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
if !has_custom_value {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded! {
|
||||
% for prop in sub_properties:
|
||||
font_variant_${prop}: unwrap_or_initial!(font_variant_${prop}, ${prop}),
|
||||
% endfor
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
#[allow(unused_assignments)]
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
|
||||
let has_none_ligatures =
|
||||
% if product == "gecko":
|
||||
self.font_variant_ligatures == &FontVariantLigatures::none();
|
||||
% else:
|
||||
false;
|
||||
% endif
|
||||
|
||||
const TOTAL_SUBPROPS: usize = ${len(sub_properties)};
|
||||
let mut nb_normals = 0;
|
||||
% for prop in sub_properties:
|
||||
if self.font_variant_${prop} == &font_variant_${prop}::get_initial_specified_value() {
|
||||
nb_normals += 1;
|
||||
}
|
||||
% endfor
|
||||
|
||||
|
||||
if nb_normals > 0 && nb_normals == TOTAL_SUBPROPS {
|
||||
dest.write_str("normal")?;
|
||||
} else if has_none_ligatures {
|
||||
if nb_normals == TOTAL_SUBPROPS - 1 {
|
||||
// Serialize to 'none' if 'font-variant-ligatures' is set to 'none' and all other
|
||||
// font feature properties are reset to their initial value.
|
||||
dest.write_str("none")?;
|
||||
} else {
|
||||
return Ok(())
|
||||
}
|
||||
} else {
|
||||
let mut has_any = false;
|
||||
% for prop in sub_properties:
|
||||
if self.font_variant_${prop} != &font_variant_${prop}::get_initial_specified_value() {
|
||||
if has_any {
|
||||
dest.write_str(" ")?;
|
||||
}
|
||||
has_any = true;
|
||||
self.font_variant_${prop}.to_css(dest)?;
|
||||
}
|
||||
% endfor
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
35
components/style/properties/shorthands/inherited_svg.mako.rs
Normal file
35
components/style/properties/shorthands/inherited_svg.mako.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
/* 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="marker" products="gecko"
|
||||
sub_properties="marker-start marker-end marker-mid"
|
||||
spec="https://www.w3.org/TR/SVG2/painting.html#MarkerShorthand">
|
||||
use values::specified::url::UrlOrNone;
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
use parser::Parse;
|
||||
let url = UrlOrNone::parse(context, input)?;
|
||||
|
||||
Ok(expanded! {
|
||||
marker_start: url.clone(),
|
||||
marker_mid: url.clone(),
|
||||
marker_end: url,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
if self.marker_start == self.marker_mid && self.marker_mid == self.marker_end {
|
||||
self.marker_start.to_css(dest)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
|
@ -0,0 +1,89 @@
|
|||
/* 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="text-emphasis" products="gecko"
|
||||
sub_properties="text-emphasis-style text-emphasis-color"
|
||||
derive_serialize="True"
|
||||
spec="https://drafts.csswg.org/css-text-decor-3/#text-emphasis-property">
|
||||
use properties::longhands::{text_emphasis_color, text_emphasis_style};
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let mut color = None;
|
||||
let mut style = None;
|
||||
|
||||
loop {
|
||||
if color.is_none() {
|
||||
if let Ok(value) = input.try(|input| text_emphasis_color::parse(context, input)) {
|
||||
color = Some(value);
|
||||
continue
|
||||
}
|
||||
}
|
||||
if style.is_none() {
|
||||
if let Ok(value) = input.try(|input| text_emphasis_style::parse(context, input)) {
|
||||
style = Some(value);
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
if color.is_some() || style.is_some() {
|
||||
Ok(expanded! {
|
||||
text_emphasis_color: unwrap_or_initial!(text_emphasis_color, color),
|
||||
text_emphasis_style: unwrap_or_initial!(text_emphasis_style, style),
|
||||
})
|
||||
} else {
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
// CSS Compatibility
|
||||
// https://compat.spec.whatwg.org/
|
||||
<%helpers:shorthand name="-webkit-text-stroke"
|
||||
sub_properties="-webkit-text-stroke-width
|
||||
-webkit-text-stroke-color"
|
||||
gecko_pref="layout.css.prefixes.webkit"
|
||||
products="gecko"
|
||||
derive_serialize="True"
|
||||
spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke">
|
||||
use properties::longhands::{_webkit_text_stroke_color, _webkit_text_stroke_width};
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let mut color = None;
|
||||
let mut width = None;
|
||||
loop {
|
||||
if color.is_none() {
|
||||
if let Ok(value) = input.try(|input| _webkit_text_stroke_color::parse(context, input)) {
|
||||
color = Some(value);
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if width.is_none() {
|
||||
if let Ok(value) = input.try(|input| _webkit_text_stroke_width::parse(context, input)) {
|
||||
width = Some(value);
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if color.is_some() || width.is_some() {
|
||||
Ok(expanded! {
|
||||
_webkit_text_stroke_color: unwrap_or_initial!(_webkit_text_stroke_color, color),
|
||||
_webkit_text_stroke_width: unwrap_or_initial!(_webkit_text_stroke_width, width),
|
||||
})
|
||||
} else {
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
114
components/style/properties/shorthands/list.mako.rs
Normal file
114
components/style/properties/shorthands/list.mako.rs
Normal file
|
@ -0,0 +1,114 @@
|
|||
/* 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-position list-style-image list-style-type"
|
||||
derive_serialize="True"
|
||||
spec="https://drafts.csswg.org/css-lists/#propdef-list-style">
|
||||
use properties::longhands::{list_style_image, list_style_position, list_style_type};
|
||||
use values::specified::url::ImageUrlOrNone;
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
// `none` is ambiguous until we've finished parsing the shorthands, so we count the number
|
||||
// of times we see it.
|
||||
let mut nones = 0u8;
|
||||
let (mut image, mut position, mut list_style_type, 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(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent("none".into())))
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// list-style-type must be checked the last, because it accepts
|
||||
// arbitrary identifier for custom counter style, and thus may
|
||||
// affect values of list-style-position.
|
||||
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
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
let position = unwrap_or_initial!(list_style_position, position);
|
||||
|
||||
fn list_style_type_none() -> list_style_type::SpecifiedValue {
|
||||
% if product == "servo":
|
||||
list_style_type::SpecifiedValue::None
|
||||
% else:
|
||||
use values::generics::CounterStyleOrNone;
|
||||
list_style_type::SpecifiedValue::CounterStyle(CounterStyleOrNone::None)
|
||||
% endif
|
||||
}
|
||||
|
||||
// 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(expanded! {
|
||||
list_style_position: position,
|
||||
list_style_image: ImageUrlOrNone::none(),
|
||||
list_style_type: list_style_type_none(),
|
||||
})
|
||||
}
|
||||
(true, 1, None, Some(image)) => {
|
||||
Ok(expanded! {
|
||||
list_style_position: position,
|
||||
list_style_image: image,
|
||||
list_style_type: list_style_type_none(),
|
||||
})
|
||||
}
|
||||
(true, 1, Some(list_style_type), None) => {
|
||||
Ok(expanded! {
|
||||
list_style_position: position,
|
||||
list_style_image: ImageUrlOrNone::none(),
|
||||
list_style_type: list_style_type,
|
||||
})
|
||||
}
|
||||
(true, 1, None, None) => {
|
||||
Ok(expanded! {
|
||||
list_style_position: position,
|
||||
list_style_image: ImageUrlOrNone::none(),
|
||||
list_style_type: list_style_type_none(),
|
||||
})
|
||||
}
|
||||
(true, 0, list_style_type, image) => {
|
||||
Ok(expanded! {
|
||||
list_style_position: position,
|
||||
list_style_image: unwrap_or_initial!(list_style_image, image),
|
||||
list_style_type: unwrap_or_initial!(list_style_type),
|
||||
})
|
||||
}
|
||||
_ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
10
components/style/properties/shorthands/margin.mako.rs
Normal file
10
components/style/properties/shorthands/margin.mako.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
/* 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",
|
||||
spec="https://drafts.csswg.org/css-box/#propdef-margin",
|
||||
allowed_in_page_rule=True,
|
||||
allow_quirks=True)}
|
242
components/style/properties/shorthands/mask.mako.rs
Normal file
242
components/style/properties/shorthands/mask.mako.rs
Normal file
|
@ -0,0 +1,242 @@
|
|||
/* 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="mask" products="gecko" extra_prefixes="webkit"
|
||||
sub_properties="mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x
|
||||
mask-position-y mask-size mask-image"
|
||||
spec="https://drafts.fxtf.org/css-masking/#propdef-mask">
|
||||
use properties::longhands::{mask_mode, mask_repeat, mask_clip, mask_origin, mask_composite, mask_position_x,
|
||||
mask_position_y};
|
||||
use properties::longhands::{mask_size, mask_image};
|
||||
use values::specified::{Position, PositionComponent};
|
||||
use parser::Parse;
|
||||
|
||||
// FIXME(emilio): These two mask types should be the same!
|
||||
impl From<mask_origin::single_value::SpecifiedValue> for mask_clip::single_value::SpecifiedValue {
|
||||
fn from(origin: mask_origin::single_value::SpecifiedValue) -> mask_clip::single_value::SpecifiedValue {
|
||||
match origin {
|
||||
mask_origin::single_value::SpecifiedValue::ContentBox =>
|
||||
mask_clip::single_value::SpecifiedValue::ContentBox,
|
||||
mask_origin::single_value::SpecifiedValue::PaddingBox =>
|
||||
mask_clip::single_value::SpecifiedValue::PaddingBox ,
|
||||
mask_origin::single_value::SpecifiedValue::BorderBox =>
|
||||
mask_clip::single_value::SpecifiedValue::BorderBox,
|
||||
% if product == "gecko":
|
||||
mask_origin::single_value::SpecifiedValue::FillBox =>
|
||||
mask_clip::single_value::SpecifiedValue::FillBox ,
|
||||
mask_origin::single_value::SpecifiedValue::StrokeBox =>
|
||||
mask_clip::single_value::SpecifiedValue::StrokeBox,
|
||||
mask_origin::single_value::SpecifiedValue::ViewBox=>
|
||||
mask_clip::single_value::SpecifiedValue::ViewBox,
|
||||
% endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
% for name in "image mode position_x position_y size repeat origin clip composite".split():
|
||||
// Vec grows from 0 to 4 by default on first push(). So allocate
|
||||
// with capacity 1, so in the common case of only one item we don't
|
||||
// way overallocate. Note that we always push at least one item if
|
||||
// parsing succeeds.
|
||||
let mut mask_${name} = mask_${name}::SpecifiedValue(Vec::with_capacity(1));
|
||||
% endfor
|
||||
|
||||
input.parse_comma_separated(|input| {
|
||||
% for name in "image mode position size repeat origin clip composite".split():
|
||||
let mut ${name} = None;
|
||||
% endfor
|
||||
loop {
|
||||
if image.is_none() {
|
||||
if let Ok(value) = input.try(|input| mask_image::single_value
|
||||
::parse(context, input)) {
|
||||
image = Some(value);
|
||||
continue
|
||||
}
|
||||
}
|
||||
if position.is_none() {
|
||||
if let Ok(value) = input.try(|input| Position::parse(context, input)) {
|
||||
position = Some(value);
|
||||
|
||||
// Parse mask size, if applicable.
|
||||
size = input.try(|input| {
|
||||
input.expect_delim('/')?;
|
||||
mask_size::single_value::parse(context, input)
|
||||
}).ok();
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
% for name in "repeat origin clip composite mode".split():
|
||||
if ${name}.is_none() {
|
||||
if let Ok(value) = input.try(|input| mask_${name}::single_value
|
||||
::parse(context, input)) {
|
||||
${name} = Some(value);
|
||||
continue
|
||||
}
|
||||
}
|
||||
% endfor
|
||||
break
|
||||
}
|
||||
if clip.is_none() {
|
||||
if let Some(origin) = origin {
|
||||
clip = Some(mask_clip::single_value::SpecifiedValue::from(origin));
|
||||
}
|
||||
}
|
||||
let mut any = false;
|
||||
% for name in "image mode position size repeat origin clip composite".split():
|
||||
any = any || ${name}.is_some();
|
||||
% endfor
|
||||
if any {
|
||||
if let Some(position) = position {
|
||||
mask_position_x.0.push(position.horizontal);
|
||||
mask_position_y.0.push(position.vertical);
|
||||
} else {
|
||||
mask_position_x.0.push(PositionComponent::zero());
|
||||
mask_position_y.0.push(PositionComponent::zero());
|
||||
}
|
||||
% for name in "image mode size repeat origin clip composite".split():
|
||||
if let Some(m_${name}) = ${name} {
|
||||
mask_${name}.0.push(m_${name});
|
||||
} else {
|
||||
mask_${name}.0.push(mask_${name}::single_value
|
||||
::get_initial_specified_value());
|
||||
}
|
||||
% endfor
|
||||
Ok(())
|
||||
} else {
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(expanded! {
|
||||
% for name in "image mode position_x position_y size repeat origin clip composite".split():
|
||||
mask_${name}: mask_${name},
|
||||
% endfor
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
use properties::longhands::mask_origin::single_value::computed_value::T as Origin;
|
||||
use properties::longhands::mask_clip::single_value::computed_value::T as Clip;
|
||||
|
||||
let len = self.mask_image.0.len();
|
||||
if len == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
% for name in "mode position_x position_y size repeat origin clip composite".split():
|
||||
if self.mask_${name}.0.len() != len {
|
||||
return Ok(());
|
||||
}
|
||||
% endfor
|
||||
|
||||
for i in 0..len {
|
||||
if i > 0 {
|
||||
dest.write_str(", ")?;
|
||||
}
|
||||
|
||||
% for name in "image mode position_x position_y size repeat origin clip composite".split():
|
||||
let ${name} = &self.mask_${name}.0[i];
|
||||
% endfor
|
||||
|
||||
image.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
mode.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
|
||||
Position {
|
||||
horizontal: position_x.clone(),
|
||||
vertical: position_y.clone()
|
||||
}.to_css(dest)?;
|
||||
|
||||
if *size != mask_size::single_value::get_initial_specified_value() {
|
||||
dest.write_str(" / ")?;
|
||||
size.to_css(dest)?;
|
||||
}
|
||||
dest.write_str(" ")?;
|
||||
repeat.to_css(dest)?;
|
||||
|
||||
if *origin != Origin::BorderBox || *clip != Clip::BorderBox {
|
||||
dest.write_str(" ")?;
|
||||
origin.to_css(dest)?;
|
||||
if *clip != From::from(*origin) {
|
||||
dest.write_str(" ")?;
|
||||
clip.to_css(dest)?;
|
||||
}
|
||||
}
|
||||
|
||||
dest.write_str(" ")?;
|
||||
composite.to_css(dest)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand name="mask-position" products="gecko" extra_prefixes="webkit"
|
||||
sub_properties="mask-position-x mask-position-y"
|
||||
spec="https://drafts.csswg.org/css-masks-4/#the-mask-position">
|
||||
use properties::longhands::{mask_position_x,mask_position_y};
|
||||
use values::specified::position::Position;
|
||||
use parser::Parse;
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
// Vec grows from 0 to 4 by default on first push(). So allocate with
|
||||
// capacity 1, so in the common case of only one item we don't way
|
||||
// overallocate. Note that we always push at least one item if parsing
|
||||
// succeeds.
|
||||
let mut position_x = mask_position_x::SpecifiedValue(Vec::with_capacity(1));
|
||||
let mut position_y = mask_position_y::SpecifiedValue(Vec::with_capacity(1));
|
||||
let mut any = false;
|
||||
|
||||
input.parse_comma_separated(|input| {
|
||||
let value = Position::parse(context, input)?;
|
||||
position_x.0.push(value.horizontal);
|
||||
position_y.0.push(value.vertical);
|
||||
any = true;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
if !any {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
}
|
||||
|
||||
Ok(expanded! {
|
||||
mask_position_x: position_x,
|
||||
mask_position_y: position_y,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
let len = self.mask_position_x.0.len();
|
||||
if len == 0 || self.mask_position_y.0.len() != len {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for i in 0..len {
|
||||
Position {
|
||||
horizontal: self.mask_position_x.0[i].clone(),
|
||||
vertical: self.mask_position_y.0[i].clone()
|
||||
}.to_css(dest)?;
|
||||
|
||||
if i < len - 1 {
|
||||
dest.write_str(", ")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
99
components/style/properties/shorthands/outline.mako.rs
Normal file
99
components/style/properties/shorthands/outline.mako.rs
Normal file
|
@ -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" />
|
||||
|
||||
<%helpers:shorthand name="outline"
|
||||
sub_properties="outline-color outline-style outline-width"
|
||||
derive_serialize="True"
|
||||
spec="https://drafts.csswg.org/css-ui/#propdef-outline">
|
||||
use properties::longhands::{outline_color, outline_width, outline_style};
|
||||
use values::specified;
|
||||
use parser::Parse;
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let _unused = context;
|
||||
let mut color = None;
|
||||
let mut style = None;
|
||||
let mut width = None;
|
||||
let mut any = false;
|
||||
loop {
|
||||
if color.is_none() {
|
||||
if let Ok(value) = input.try(|i| specified::Color::parse(context, i)) {
|
||||
color = Some(value);
|
||||
any = true;
|
||||
continue
|
||||
}
|
||||
}
|
||||
if style.is_none() {
|
||||
if let Ok(value) = input.try(|input| outline_style::parse(context, input)) {
|
||||
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(expanded! {
|
||||
outline_color: unwrap_or_initial!(outline_color, color),
|
||||
outline_style: unwrap_or_initial!(outline_style, style),
|
||||
outline_width: unwrap_or_initial!(outline_width, width),
|
||||
})
|
||||
} else {
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
// The -moz-outline-radius shorthand is non-standard and not on a standards track.
|
||||
<%helpers:shorthand name="-moz-outline-radius" sub_properties="${' '.join(
|
||||
'-moz-outline-radius-%s' % corner
|
||||
for corner in ['topleft', 'topright', 'bottomright', 'bottomleft']
|
||||
)}" products="gecko" spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)">
|
||||
use values::generics::rect::Rect;
|
||||
use values::specified::border::BorderRadius;
|
||||
use parser::Parse;
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let radii = BorderRadius::parse(context, input)?;
|
||||
Ok(expanded! {
|
||||
_moz_outline_radius_topleft: radii.top_left,
|
||||
_moz_outline_radius_topright: radii.top_right,
|
||||
_moz_outline_radius_bottomright: radii.bottom_right,
|
||||
_moz_outline_radius_bottomleft: radii.bottom_left,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
use values::generics::border::BorderCornerRadius;
|
||||
|
||||
let LonghandsToSerialize {
|
||||
_moz_outline_radius_topleft: &BorderCornerRadius(ref tl),
|
||||
_moz_outline_radius_topright: &BorderCornerRadius(ref tr),
|
||||
_moz_outline_radius_bottomright: &BorderCornerRadius(ref br),
|
||||
_moz_outline_radius_bottomleft: &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>
|
9
components/style/properties/shorthands/padding.mako.rs
Normal file
9
components/style/properties/shorthands/padding.mako.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
/* 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::NonNegativeLengthOrPercentage::parse",
|
||||
spec="https://drafts.csswg.org/css-box-3/#propdef-padding",
|
||||
allow_quirks=True)}
|
764
components/style/properties/shorthands/position.mako.rs
Normal file
764
components/style/properties/shorthands/position.mako.rs
Normal file
|
@ -0,0 +1,764 @@
|
|||
/* 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="flex-flow"
|
||||
sub_properties="flex-direction flex-wrap"
|
||||
extra_prefixes="webkit"
|
||||
derive_serialize="True"
|
||||
spec="https://drafts.csswg.org/css-flexbox/#flex-flow-property">
|
||||
use properties::longhands::{flex_direction, flex_wrap};
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let mut direction = None;
|
||||
let mut wrap = None;
|
||||
loop {
|
||||
if direction.is_none() {
|
||||
if let Ok(value) = input.try(|input| flex_direction::parse(context, input)) {
|
||||
direction = Some(value);
|
||||
continue
|
||||
}
|
||||
}
|
||||
if wrap.is_none() {
|
||||
if let Ok(value) = input.try(|input| flex_wrap::parse(context, input)) {
|
||||
wrap = Some(value);
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if direction.is_none() && wrap.is_none() {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
Ok(expanded! {
|
||||
flex_direction: unwrap_or_initial!(flex_direction, direction),
|
||||
flex_wrap: unwrap_or_initial!(flex_wrap, wrap),
|
||||
})
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand name="flex"
|
||||
sub_properties="flex-grow flex-shrink flex-basis"
|
||||
extra_prefixes="webkit"
|
||||
derive_serialize="True"
|
||||
spec="https://drafts.csswg.org/css-flexbox/#flex-property">
|
||||
use parser::Parse;
|
||||
use values::specified::NonNegativeNumber;
|
||||
use properties::longhands::flex_basis::SpecifiedValue as FlexBasis;
|
||||
|
||||
fn parse_flexibility<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<(NonNegativeNumber, Option<NonNegativeNumber>),ParseError<'i>> {
|
||||
let grow = NonNegativeNumber::parse(context, input)?;
|
||||
let shrink = input.try(|i| NonNegativeNumber::parse(context, i)).ok();
|
||||
Ok((grow, shrink))
|
||||
}
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let mut grow = None;
|
||||
let mut shrink = None;
|
||||
let mut basis = None;
|
||||
|
||||
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||||
return Ok(expanded! {
|
||||
flex_grow: NonNegativeNumber::new(0.0),
|
||||
flex_shrink: NonNegativeNumber::new(0.0),
|
||||
flex_basis: FlexBasis::auto(),
|
||||
})
|
||||
}
|
||||
loop {
|
||||
if grow.is_none() {
|
||||
if let Ok((flex_grow, flex_shrink)) = input.try(|i| parse_flexibility(context, i)) {
|
||||
grow = Some(flex_grow);
|
||||
shrink = flex_shrink;
|
||||
continue
|
||||
}
|
||||
}
|
||||
if basis.is_none() {
|
||||
if let Ok(value) = input.try(|input| FlexBasis::parse(context, input)) {
|
||||
basis = Some(value);
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if grow.is_none() && basis.is_none() {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
Ok(expanded! {
|
||||
flex_grow: grow.unwrap_or(NonNegativeNumber::new(1.0)),
|
||||
flex_shrink: shrink.unwrap_or(NonNegativeNumber::new(1.0)),
|
||||
// Per spec, this should be SpecifiedValue::zero(), but all
|
||||
// browsers currently agree on using `0%`. This is a spec
|
||||
// change which hasn't been adopted by browsers:
|
||||
// https://github.com/w3c/csswg-drafts/commit/2c446befdf0f686217905bdd7c92409f6bd3921b
|
||||
flex_basis: basis.unwrap_or(FlexBasis::zero_percent()),
|
||||
})
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand name="gap" alias="grid-gap" sub_properties="row-gap column-gap"
|
||||
spec="https://drafts.csswg.org/css-align-3/#gap-shorthand"
|
||||
products="gecko">
|
||||
use properties::longhands::{row_gap, column_gap};
|
||||
|
||||
pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
|
||||
-> Result<Longhands, ParseError<'i>> {
|
||||
let r_gap = row_gap::parse(context, input)?;
|
||||
let c_gap = input.try(|input| column_gap::parse(context, input)).unwrap_or(r_gap.clone());
|
||||
|
||||
Ok(expanded! {
|
||||
row_gap: r_gap,
|
||||
column_gap: c_gap,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
if self.row_gap == self.column_gap {
|
||||
self.row_gap.to_css(dest)
|
||||
} else {
|
||||
self.row_gap.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
self.column_gap.to_css(dest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</%helpers:shorthand>
|
||||
|
||||
% for kind in ["row", "column"]:
|
||||
<%helpers:shorthand name="grid-${kind}" sub_properties="grid-${kind}-start grid-${kind}-end"
|
||||
spec="https://drafts.csswg.org/css-grid/#propdef-grid-${kind}"
|
||||
products="gecko">
|
||||
use values::specified::GridLine;
|
||||
use parser::Parse;
|
||||
|
||||
// NOTE: Since both the shorthands have the same code, we should (re-)use code from one to implement
|
||||
// the other. This might not be a big deal for now, but we should consider looking into this in the future
|
||||
// to limit the amount of code generated.
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let start = input.try(|i| GridLine::parse(context, i))?;
|
||||
let end = if input.try(|i| i.expect_delim('/')).is_ok() {
|
||||
GridLine::parse(context, input)?
|
||||
} else {
|
||||
let mut line = GridLine::auto();
|
||||
if start.line_num.is_none() && !start.is_span {
|
||||
line.ident = start.ident.clone(); // ident from start value should be taken
|
||||
}
|
||||
|
||||
line
|
||||
};
|
||||
|
||||
Ok(expanded! {
|
||||
grid_${kind}_start: start,
|
||||
grid_${kind}_end: end,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
self.grid_${kind}_start.to_css(dest)?;
|
||||
dest.write_str(" / ")?;
|
||||
self.grid_${kind}_end.to_css(dest)
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
% endfor
|
||||
|
||||
<%helpers:shorthand name="grid-area"
|
||||
sub_properties="grid-row-start grid-row-end grid-column-start grid-column-end"
|
||||
spec="https://drafts.csswg.org/css-grid/#propdef-grid-area"
|
||||
products="gecko">
|
||||
use values::specified::GridLine;
|
||||
use parser::Parse;
|
||||
|
||||
// The code is the same as `grid-{row,column}` except that this can have four values at most.
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
fn line_with_ident_from(other: &GridLine) -> GridLine {
|
||||
let mut this = GridLine::auto();
|
||||
if other.line_num.is_none() && !other.is_span {
|
||||
this.ident = other.ident.clone();
|
||||
}
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
let row_start = input.try(|i| GridLine::parse(context, i))?;
|
||||
let (column_start, row_end, column_end) = if input.try(|i| i.expect_delim('/')).is_ok() {
|
||||
let column_start = GridLine::parse(context, input)?;
|
||||
let (row_end, column_end) = if input.try(|i| i.expect_delim('/')).is_ok() {
|
||||
let row_end = GridLine::parse(context, input)?;
|
||||
let column_end = if input.try(|i| i.expect_delim('/')).is_ok() {
|
||||
GridLine::parse(context, input)?
|
||||
} else { // grid-column-end has not been given
|
||||
line_with_ident_from(&column_start)
|
||||
};
|
||||
|
||||
(row_end, column_end)
|
||||
} else { // grid-row-start and grid-column-start has been given
|
||||
let row_end = line_with_ident_from(&row_start);
|
||||
let column_end = line_with_ident_from(&column_start);
|
||||
(row_end, column_end)
|
||||
};
|
||||
|
||||
(column_start, row_end, column_end)
|
||||
} else { // only grid-row-start is given
|
||||
let line = line_with_ident_from(&row_start);
|
||||
(line.clone(), line.clone(), line)
|
||||
};
|
||||
|
||||
Ok(expanded! {
|
||||
grid_row_start: row_start,
|
||||
grid_row_end: row_end,
|
||||
grid_column_start: column_start,
|
||||
grid_column_end: column_end,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
self.grid_row_start.to_css(dest)?;
|
||||
let values = [&self.grid_column_start, &self.grid_row_end, &self.grid_column_end];
|
||||
for value in &values {
|
||||
dest.write_str(" / ")?;
|
||||
value.to_css(dest)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand name="grid-template"
|
||||
sub_properties="grid-template-rows grid-template-columns grid-template-areas"
|
||||
spec="https://drafts.csswg.org/css-grid/#propdef-grid-template"
|
||||
products="gecko">
|
||||
use parser::Parse;
|
||||
use servo_arc::Arc;
|
||||
use values::{Either, None_};
|
||||
use values::generics::grid::{LineNameList, TrackSize, TrackList, TrackListType};
|
||||
use values::generics::grid::{TrackListValue, concat_serialize_idents};
|
||||
use values::specified::{GridTemplateComponent, GenericGridTemplateComponent};
|
||||
use values::specified::grid::parse_line_names;
|
||||
use values::specified::position::{TemplateAreas, TemplateAreasArc};
|
||||
|
||||
/// Parsing for `<grid-template>` shorthand (also used by `grid` shorthand).
|
||||
pub fn parse_grid_template<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<(GridTemplateComponent, GridTemplateComponent, Either<TemplateAreasArc, None_>), ParseError<'i>> {
|
||||
// Other shorthand sub properties also parse `none` and `subgrid` keywords and this
|
||||
// shorthand should know after these keywords there is nothing to parse. Otherwise it
|
||||
// gets confused and rejects the sub properties that contains `none` or `subgrid`.
|
||||
<% keywords = {
|
||||
"none": "GenericGridTemplateComponent::None",
|
||||
"subgrid": "GenericGridTemplateComponent::Subgrid(LineNameList::default())"
|
||||
}
|
||||
%>
|
||||
% for keyword, rust_type in keywords.items():
|
||||
if let Ok(x) = input.try(|i| {
|
||||
if i.try(|i| i.expect_ident_matching("${keyword}")).is_ok() {
|
||||
if i.is_exhausted() {
|
||||
return Ok((${rust_type},
|
||||
${rust_type},
|
||||
Either::Second(None_)))
|
||||
} else {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
Err(())
|
||||
}) {
|
||||
return Ok(x);
|
||||
}
|
||||
% endfor
|
||||
|
||||
let first_line_names = input.try(parse_line_names).unwrap_or(vec![].into_boxed_slice());
|
||||
if let Ok(mut string) = input.try(|i| i.expect_string().map(|s| s.as_ref().into())) {
|
||||
let mut strings = vec![];
|
||||
let mut values = vec![];
|
||||
let mut line_names = vec![];
|
||||
let mut names = first_line_names.into_vec();
|
||||
loop {
|
||||
line_names.push(names.into_boxed_slice());
|
||||
strings.push(string);
|
||||
let size = input.try(|i| TrackSize::parse(context, i)).unwrap_or_default();
|
||||
values.push(TrackListValue::TrackSize(size));
|
||||
names = input.try(parse_line_names).unwrap_or(vec![].into_boxed_slice()).into_vec();
|
||||
if let Ok(v) = input.try(parse_line_names) {
|
||||
names.extend(v.into_vec());
|
||||
}
|
||||
|
||||
string = match input.try(|i| i.expect_string().map(|s| s.as_ref().into())) {
|
||||
Ok(s) => s,
|
||||
_ => { // only the named area determines whether we should bail out
|
||||
line_names.push(names.into_boxed_slice());
|
||||
break
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if line_names.len() == values.len() {
|
||||
// should be one longer than track sizes
|
||||
line_names.push(vec![].into_boxed_slice());
|
||||
}
|
||||
|
||||
let template_areas = TemplateAreas::from_vec(strings)
|
||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?;
|
||||
let template_rows = TrackList {
|
||||
list_type: TrackListType::Normal,
|
||||
values: values,
|
||||
line_names: line_names.into_boxed_slice(),
|
||||
auto_repeat: None,
|
||||
};
|
||||
|
||||
let template_cols = if input.try(|i| i.expect_delim('/')).is_ok() {
|
||||
let value = GridTemplateComponent::parse_without_none(context, input)?;
|
||||
if let GenericGridTemplateComponent::TrackList(ref list) = value {
|
||||
if list.list_type != TrackListType::Explicit {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
}
|
||||
|
||||
value
|
||||
} else {
|
||||
GenericGridTemplateComponent::None
|
||||
};
|
||||
|
||||
Ok((GenericGridTemplateComponent::TrackList(template_rows),
|
||||
template_cols, Either::First(TemplateAreasArc(Arc::new(template_areas)))))
|
||||
} else {
|
||||
let mut template_rows = GridTemplateComponent::parse(context, input)?;
|
||||
if let GenericGridTemplateComponent::TrackList(ref mut list) = template_rows {
|
||||
// Fist line names are parsed already and it shouldn't be parsed again.
|
||||
// If line names are not empty, that means given property value is not acceptable
|
||||
if list.line_names[0].is_empty() {
|
||||
list.line_names[0] = first_line_names; // won't panic
|
||||
} else {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
}
|
||||
}
|
||||
|
||||
input.expect_delim('/')?;
|
||||
Ok((template_rows, GridTemplateComponent::parse(context, input)?, Either::Second(None_)))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let (rows, columns, areas) = parse_grid_template(context, input)?;
|
||||
Ok(expanded! {
|
||||
grid_template_rows: rows,
|
||||
grid_template_columns: columns,
|
||||
grid_template_areas: areas,
|
||||
})
|
||||
}
|
||||
|
||||
/// Serialization for `<grid-template>` shorthand (also used by `grid` shorthand).
|
||||
pub fn serialize_grid_template<W>(
|
||||
template_rows: &GridTemplateComponent,
|
||||
template_columns: &GridTemplateComponent,
|
||||
template_areas: &Either<TemplateAreasArc, None_>,
|
||||
dest: &mut CssWriter<W>,
|
||||
) -> fmt::Result
|
||||
where
|
||||
W: Write {
|
||||
match *template_areas {
|
||||
Either::Second(_none) => {
|
||||
template_rows.to_css(dest)?;
|
||||
dest.write_str(" / ")?;
|
||||
template_columns.to_css(dest)
|
||||
},
|
||||
Either::First(ref areas) => {
|
||||
// The length of template-area and template-rows values should be equal.
|
||||
if areas.0.strings.len() != template_rows.track_list_len() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let track_list = match *template_rows {
|
||||
GenericGridTemplateComponent::TrackList(ref list) => {
|
||||
// We should fail if there is a `repeat` function. `grid` and
|
||||
// `grid-template` shorthands doesn't accept that. Only longhand accepts.
|
||||
if list.auto_repeat.is_some() ||
|
||||
list.values.iter().any(|v| match *v {
|
||||
TrackListValue::TrackRepeat(_) => true,
|
||||
_ => false,
|
||||
}) {
|
||||
return Ok(());
|
||||
}
|
||||
list
|
||||
},
|
||||
// Others template components shouldn't exist with normal shorthand values.
|
||||
// But if we need to serialize a group of longhand sub-properties for
|
||||
// the shorthand, we should be able to return empty string instead of crashing.
|
||||
_ => return Ok(()),
|
||||
};
|
||||
|
||||
// We need to check some values that longhand accepts but shorthands don't.
|
||||
match *template_columns {
|
||||
// We should fail if there is a `repeat` function. `grid` and
|
||||
// `grid-template` shorthands doesn't accept that. Only longhand accepts that.
|
||||
GenericGridTemplateComponent::TrackList(ref list) => {
|
||||
if list.auto_repeat.is_some() ||
|
||||
list.values.iter().any(|v| match *v {
|
||||
TrackListValue::TrackRepeat(_) => true,
|
||||
_ => false,
|
||||
}) {
|
||||
return Ok(());
|
||||
}
|
||||
},
|
||||
// Also the shorthands don't accept subgrids unlike longhand.
|
||||
// We should fail without an error here.
|
||||
GenericGridTemplateComponent::Subgrid(_) => {
|
||||
return Ok(());
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
let mut names_iter = track_list.line_names.iter();
|
||||
for (((i, string), names), value) in areas.0.strings.iter().enumerate()
|
||||
.zip(&mut names_iter)
|
||||
.zip(track_list.values.iter()) {
|
||||
if i > 0 {
|
||||
dest.write_str(" ")?;
|
||||
}
|
||||
|
||||
if !names.is_empty() {
|
||||
concat_serialize_idents("[", "] ", names, " ", dest)?;
|
||||
}
|
||||
|
||||
string.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
value.to_css(dest)?;
|
||||
}
|
||||
|
||||
if let Some(names) = names_iter.next() {
|
||||
concat_serialize_idents(" [", "]", names, " ", dest)?;
|
||||
}
|
||||
|
||||
if let GenericGridTemplateComponent::TrackList(ref list) = *template_columns {
|
||||
dest.write_str(" / ")?;
|
||||
list.to_css(dest)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
#[inline]
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
serialize_grid_template(
|
||||
self.grid_template_rows,
|
||||
self.grid_template_columns,
|
||||
self.grid_template_areas,
|
||||
dest
|
||||
)
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand name="grid"
|
||||
sub_properties="grid-template-rows grid-template-columns grid-template-areas
|
||||
grid-auto-rows grid-auto-columns grid-auto-flow"
|
||||
spec="https://drafts.csswg.org/css-grid/#propdef-grid"
|
||||
products="gecko">
|
||||
use parser::Parse;
|
||||
use properties::longhands::{grid_auto_columns, grid_auto_rows, grid_auto_flow};
|
||||
use values::{Either, None_};
|
||||
use values::generics::grid::{GridTemplateComponent, TrackListType};
|
||||
use values::specified::{GenericGridTemplateComponent, TrackSize};
|
||||
use values::specified::position::{AutoFlow, GridAutoFlow};
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let mut temp_rows = GridTemplateComponent::None;
|
||||
let mut temp_cols = GridTemplateComponent::None;
|
||||
let mut temp_areas = Either::Second(None_);
|
||||
let mut auto_rows = TrackSize::default();
|
||||
let mut auto_cols = TrackSize::default();
|
||||
let mut flow = grid_auto_flow::get_initial_value();
|
||||
|
||||
fn parse_auto_flow<'i, 't>(
|
||||
input: &mut Parser<'i, 't>,
|
||||
is_row: bool,
|
||||
) -> Result<GridAutoFlow, ParseError<'i>> {
|
||||
let mut auto_flow = None;
|
||||
let mut dense = false;
|
||||
for _ in 0..2 {
|
||||
if input.try(|i| i.expect_ident_matching("auto-flow")).is_ok() {
|
||||
auto_flow = if is_row {
|
||||
Some(AutoFlow::Row)
|
||||
} else {
|
||||
Some(AutoFlow::Column)
|
||||
};
|
||||
} else if input.try(|i| i.expect_ident_matching("dense")).is_ok() {
|
||||
dense = true;
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
auto_flow.map(|flow| {
|
||||
GridAutoFlow {
|
||||
autoflow: flow,
|
||||
dense: dense,
|
||||
}
|
||||
}).ok_or(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
|
||||
if let Ok((rows, cols, areas)) = input.try(|i| super::grid_template::parse_grid_template(context, i)) {
|
||||
temp_rows = rows;
|
||||
temp_cols = cols;
|
||||
temp_areas = areas;
|
||||
} else if let Ok(rows) = input.try(|i| GridTemplateComponent::parse(context, i)) {
|
||||
temp_rows = rows;
|
||||
input.expect_delim('/')?;
|
||||
flow = parse_auto_flow(input, false)?;
|
||||
auto_cols = grid_auto_columns::parse(context, input).unwrap_or_default();
|
||||
} else {
|
||||
flow = parse_auto_flow(input, true)?;
|
||||
auto_rows = input.try(|i| grid_auto_rows::parse(context, i)).unwrap_or_default();
|
||||
input.expect_delim('/')?;
|
||||
temp_cols = GridTemplateComponent::parse(context, input)?;
|
||||
}
|
||||
|
||||
Ok(expanded! {
|
||||
grid_template_rows: temp_rows,
|
||||
grid_template_columns: temp_cols,
|
||||
grid_template_areas: temp_areas,
|
||||
grid_auto_rows: auto_rows,
|
||||
grid_auto_columns: auto_cols,
|
||||
grid_auto_flow: flow,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> LonghandsToSerialize<'a> {
|
||||
/// Returns true if other sub properties except template-{rows,columns} are initial.
|
||||
fn is_grid_template(&self) -> bool {
|
||||
*self.grid_template_areas == Either::Second(None_) &&
|
||||
*self.grid_auto_rows == TrackSize::default() &&
|
||||
*self.grid_auto_columns == TrackSize::default() &&
|
||||
*self.grid_auto_flow == grid_auto_flow::get_initial_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
if *self.grid_template_areas != Either::Second(None_) ||
|
||||
(*self.grid_template_rows != GridTemplateComponent::None &&
|
||||
*self.grid_template_columns != GridTemplateComponent::None) ||
|
||||
self.is_grid_template() {
|
||||
return super::grid_template::serialize_grid_template(self.grid_template_rows,
|
||||
self.grid_template_columns,
|
||||
self.grid_template_areas, dest);
|
||||
}
|
||||
|
||||
if self.grid_auto_flow.autoflow == AutoFlow::Column {
|
||||
// It should fail to serialize if other branch of the if condition's values are set.
|
||||
if *self.grid_auto_rows != TrackSize::default() ||
|
||||
*self.grid_template_columns != GridTemplateComponent::None {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// It should fail to serialize if template-rows value is not Explicit.
|
||||
if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_rows {
|
||||
if list.list_type != TrackListType::Explicit {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
self.grid_template_rows.to_css(dest)?;
|
||||
dest.write_str(" / auto-flow")?;
|
||||
if self.grid_auto_flow.dense {
|
||||
dest.write_str(" dense")?;
|
||||
}
|
||||
|
||||
if !self.grid_auto_columns.is_default() {
|
||||
dest.write_str(" ")?;
|
||||
self.grid_auto_columns.to_css(dest)?;
|
||||
}
|
||||
} else {
|
||||
// It should fail to serialize if other branch of the if condition's values are set.
|
||||
if *self.grid_auto_columns != TrackSize::default() ||
|
||||
*self.grid_template_rows != GridTemplateComponent::None {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// It should fail to serialize if template-column value is not Explicit.
|
||||
if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_columns {
|
||||
if list.list_type != TrackListType::Explicit {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
dest.write_str("auto-flow")?;
|
||||
if self.grid_auto_flow.dense {
|
||||
dest.write_str(" dense")?;
|
||||
}
|
||||
|
||||
if !self.grid_auto_rows.is_default() {
|
||||
dest.write_str(" ")?;
|
||||
self.grid_auto_rows.to_css(dest)?;
|
||||
}
|
||||
|
||||
dest.write_str(" / ")?;
|
||||
self.grid_template_columns.to_css(dest)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand name="place-content" sub_properties="align-content justify-content"
|
||||
spec="https://drafts.csswg.org/css-align/#propdef-place-content"
|
||||
products="gecko">
|
||||
use values::specified::align::{AlignContent, JustifyContent, ContentDistribution, AxisDirection};
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
_: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let align_content =
|
||||
ContentDistribution::parse(input, AxisDirection::Block)?;
|
||||
|
||||
let justify_content = input.try(|input| {
|
||||
ContentDistribution::parse(input, AxisDirection::Inline)
|
||||
});
|
||||
|
||||
let justify_content = match justify_content {
|
||||
Ok(v) => v,
|
||||
Err(..) => {
|
||||
// https://drafts.csswg.org/css-align-3/#place-content:
|
||||
//
|
||||
// The second value is assigned to justify-content; if
|
||||
// omitted, it is copied from the first value, unless that
|
||||
// value is a <baseline-position> in which case it is
|
||||
// defaulted to start.
|
||||
//
|
||||
if !align_content.is_baseline_position() {
|
||||
align_content
|
||||
} else {
|
||||
ContentDistribution::start()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(expanded! {
|
||||
align_content: AlignContent(align_content),
|
||||
justify_content: JustifyContent(justify_content),
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
self.align_content.to_css(dest)?;
|
||||
if self.align_content.0 != self.justify_content.0 {
|
||||
dest.write_str(" ")?;
|
||||
self.justify_content.to_css(dest)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand name="place-self" sub_properties="align-self justify-self"
|
||||
spec="https://drafts.csswg.org/css-align/#place-self-property"
|
||||
products="gecko">
|
||||
use values::specified::align::{AlignSelf, JustifySelf, SelfAlignment, AxisDirection};
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
_: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let align = SelfAlignment::parse(input, AxisDirection::Block)?;
|
||||
let justify = input.try(|input| SelfAlignment::parse(input, AxisDirection::Inline));
|
||||
|
||||
let justify = match justify {
|
||||
Ok(v) => v,
|
||||
Err(..) => {
|
||||
debug_assert!(align.is_valid_on_both_axes());
|
||||
align
|
||||
}
|
||||
};
|
||||
|
||||
Ok(expanded! {
|
||||
align_self: AlignSelf(align),
|
||||
justify_self: JustifySelf(justify),
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
self.align_self.to_css(dest)?;
|
||||
if self.align_self.0 != self.justify_self.0 {
|
||||
dest.write_str(" ")?;
|
||||
self.justify_self.to_css(dest)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand name="place-items" sub_properties="align-items justify-items"
|
||||
spec="https://drafts.csswg.org/css-align/#place-items-property"
|
||||
products="gecko">
|
||||
use values::specified::align::{AlignItems, JustifyItems};
|
||||
use parser::Parse;
|
||||
|
||||
impl From<AlignItems> for JustifyItems {
|
||||
fn from(align: AlignItems) -> JustifyItems {
|
||||
JustifyItems(align.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let align = AlignItems::parse(context, input)?;
|
||||
let justify =
|
||||
input.try(|input| JustifyItems::parse(context, input))
|
||||
.unwrap_or_else(|_| JustifyItems::from(align));
|
||||
|
||||
Ok(expanded! {
|
||||
align_items: align,
|
||||
justify_items: justify,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
self.align_items.to_css(dest)?;
|
||||
if self.align_items.0 != self.justify_items.0 {
|
||||
dest.write_str(" ")?;
|
||||
self.justify_items.to_css(dest)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
85
components/style/properties/shorthands/text.mako.rs
Normal file
85
components/style/properties/shorthands/text.mako.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
/* 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="text-decoration"
|
||||
sub_properties="text-decoration-line
|
||||
${' text-decoration-style text-decoration-color' if product == 'gecko' else ''}"
|
||||
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration">
|
||||
|
||||
% if product == "gecko":
|
||||
use values::specified;
|
||||
use properties::longhands::{text_decoration_line, text_decoration_style, text_decoration_color};
|
||||
% else:
|
||||
use properties::longhands::text_decoration_line;
|
||||
% endif
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
% if product == "gecko":
|
||||
let (mut line, mut style, mut color, mut any) = (None, None, None, false);
|
||||
% else:
|
||||
let (mut line, mut any) = (None, false);
|
||||
% endif
|
||||
|
||||
loop {
|
||||
macro_rules! parse_component {
|
||||
($value:ident, $module:ident) => (
|
||||
if $value.is_none() {
|
||||
if let Ok(value) = input.try(|input| $module::parse(context, input)) {
|
||||
$value = Some(value);
|
||||
any = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
parse_component!(line, text_decoration_line);
|
||||
|
||||
% if product == "gecko":
|
||||
parse_component!(style, text_decoration_style);
|
||||
parse_component!(color, text_decoration_color);
|
||||
% endif
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if !any {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
}
|
||||
|
||||
Ok(expanded! {
|
||||
text_decoration_line: unwrap_or_initial!(text_decoration_line, line),
|
||||
|
||||
% if product == "gecko":
|
||||
text_decoration_style: unwrap_or_initial!(text_decoration_style, style),
|
||||
text_decoration_color: unwrap_or_initial!(text_decoration_color, color),
|
||||
% endif
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
self.text_decoration_line.to_css(dest)?;
|
||||
|
||||
% if product == "gecko":
|
||||
if *self.text_decoration_style != text_decoration_style::SpecifiedValue::Solid {
|
||||
dest.write_str(" ")?;
|
||||
self.text_decoration_style.to_css(dest)?;
|
||||
}
|
||||
|
||||
if *self.text_decoration_color != specified::Color::CurrentColor {
|
||||
dest.write_str(" ")?;
|
||||
self.text_decoration_color.to_css(dest)?;
|
||||
}
|
||||
% endif
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
Loading…
Add table
Add a link
Reference in a new issue