Make stylo use counter-style for list-style-type and counter functions.

This commit is contained in:
Xidorn Quan 2017-05-16 09:47:28 +10:00
parent 3e00a91e20
commit ff1ac8346f
5 changed files with 200 additions and 69 deletions

View file

@ -2987,29 +2987,12 @@ fn static_assert() {
}
pub fn set_list_style_type(&mut self, v: longhands::list_style_type::computed_value::T) {
use properties::longhands::list_style_type::computed_value::T as Keyword;
<%
keyword = data.longhands_by_name["list-style-type"].keyword
# The first four are @counter-styles
# The rest have special fallback behavior
special = """upper-roman lower-roman upper-alpha lower-alpha
japanese-informal japanese-formal korean-hangul-formal korean-hanja-informal
korean-hanja-formal simp-chinese-informal simp-chinese-formal
trad-chinese-informal trad-chinese-formal""".split()
%>
let result = match v {
% for value in keyword.values_for('gecko'):
% if value in special:
// Special keywords are implemented as @counter-styles
// and need to be manually set as strings
Keyword::${to_rust_ident(value)} => structs::${keyword.gecko_constant("none")},
% else:
Keyword::${to_rust_ident(value)} =>
structs::${keyword.gecko_constant(value)},
% endif
% endfor
use values::generics::CounterStyleOrNone;
let name = match v.0 {
CounterStyleOrNone::None_ => atom!("none"),
CounterStyleOrNone::Name(name) => name.0,
};
unsafe { Gecko_SetListStyleType(&mut self.gecko, result as u32); }
unsafe { Gecko_SetListStyleType(&mut self.gecko, name.as_ptr()); }
}
@ -4072,7 +4055,8 @@ clip-path
pub fn set_content(&mut self, v: longhands::content::computed_value::T) {
use properties::longhands::content::computed_value::T;
use properties::longhands::content::computed_value::ContentItem;
use style_traits::ToCss;
use values::generics::CounterStyleOrNone;
use gecko_bindings::structs::nsCSSValue;
use gecko_bindings::structs::nsStyleContentType::*;
use gecko_bindings::bindings::Gecko_ClearAndResizeStyleContents;
@ -4086,6 +4070,13 @@ clip-path
ptr
}
fn set_counter_style(style: CounterStyleOrNone, dest: &mut nsCSSValue) {
dest.set_atom_ident(match style {
CounterStyleOrNone::None_ => atom!("none"),
CounterStyleOrNone::Name(name) => name.0,
});
}
match v {
T::none |
T::normal => {
@ -4147,8 +4138,7 @@ clip-path
}
let mut array = unsafe { &mut **self.gecko.mContents[i].mContent.mCounters.as_mut() };
array[0].set_string(&name);
// When we support <custom-ident> values for list-style-type this will need to be updated
array[1].set_atom_ident(style.to_css_string().into());
set_counter_style(style, &mut array[1]);
}
ContentItem::Counters(name, sep, style) => {
unsafe {
@ -4158,8 +4148,7 @@ clip-path
let mut array = unsafe { &mut **self.gecko.mContents[i].mContent.mCounters.as_mut() };
array[0].set_string(&name);
array[1].set_string(&sep);
// When we support <custom-ident> values for list-style-type this will need to be updated
array[2].set_atom_ident(style.to_css_string().into());
set_counter_style(style, &mut array[2]);
}
ContentItem::Url(ref url) => {
unsafe {

View file

@ -11,9 +11,12 @@
use cssparser::Token;
use std::ascii::AsciiExt;
use values::computed::ComputedValueAsSpecified;
#[cfg(feature = "gecko")]
use values::generics::CounterStyleOrNone;
use values::specified::url::SpecifiedUrl;
use values::HasViewportPercentage;
#[cfg(feature = "servo")]
use super::list_style_type;
pub use self::computed_value::T as SpecifiedValue;
@ -23,22 +26,25 @@
no_viewport_percentage!(SpecifiedValue);
pub mod computed_value {
use super::super::list_style_type;
use cssparser;
use std::fmt;
use style_traits::ToCss;
use values::specified::url::SpecifiedUrl;
#[cfg(feature = "servo")]
type CounterStyleType = super::super::list_style_type::computed_value::T;
#[cfg(feature = "gecko")]
type CounterStyleType = ::values::generics::CounterStyleOrNone;
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum ContentItem {
/// Literal string content.
String(String),
/// `counter(name, style)`.
Counter(String, list_style_type::computed_value::T),
Counter(String, CounterStyleType),
/// `counters(name, separator, style)`.
Counters(String, String, list_style_type::computed_value::T),
Counters(String, String, CounterStyleType),
/// `open-quote`.
OpenQuote,
/// `close-quote`.
@ -64,20 +70,20 @@
ContentItem::String(ref s) => {
cssparser::serialize_string(&**s, dest)
}
ContentItem::Counter(ref s, ref list_style_type) => {
ContentItem::Counter(ref s, ref counter_style) => {
try!(dest.write_str("counter("));
try!(cssparser::serialize_identifier(&**s, dest));
try!(dest.write_str(", "));
try!(list_style_type.to_css(dest));
try!(counter_style.to_css(dest));
dest.write_str(")")
}
ContentItem::Counters(ref s, ref separator, ref list_style_type) => {
ContentItem::Counters(ref s, ref separator, ref counter_style) => {
try!(dest.write_str("counters("));
try!(cssparser::serialize_identifier(&**s, dest));
try!(dest.write_str(", "));
try!(cssparser::serialize_string(&**separator, dest));
try!(dest.write_str(", "));
try!(list_style_type.to_css(dest));
try!(counter_style.to_css(dest));
dest.write_str(")")
}
ContentItem::OpenQuote => dest.write_str("open-quote"),
@ -134,6 +140,22 @@
computed_value::T::normal
}
#[cfg(feature = "servo")]
fn parse_counter_style(context: &ParserContext, input: &mut Parser) -> list_style_type::computed_value::T {
input.try(|input| {
input.expect_comma()?;
list_style_type::parse(context, input)
}).unwrap_or(list_style_type::computed_value::T::decimal)
}
#[cfg(feature = "gecko")]
fn parse_counter_style(context: &ParserContext, input: &mut Parser) -> CounterStyleOrNone {
input.try(|input| {
input.expect_comma()?;
CounterStyleOrNone::parse(context, input)
}).unwrap_or(CounterStyleOrNone::decimal())
}
// normal | none | [ <string> | <counter> | open-quote | close-quote | no-open-quote |
// no-close-quote ]+
// TODO: <uri>, attr(<identifier>)
@ -162,20 +184,14 @@
content.push(try!(match_ignore_ascii_case! { &name,
"counter" => input.parse_nested_block(|input| {
let name = try!(input.expect_ident()).into_owned();
let style = input.try(|input| {
try!(input.expect_comma());
list_style_type::parse(context, input)
}).unwrap_or(list_style_type::computed_value::T::decimal);
let style = parse_counter_style(context, input);
Ok(ContentItem::Counter(name, style))
}),
"counters" => input.parse_nested_block(|input| {
let name = try!(input.expect_ident()).into_owned();
try!(input.expect_comma());
let separator = try!(input.expect_string()).into_owned();
let style = input.try(|input| {
try!(input.expect_comma());
list_style_type::parse(context, input)
}).unwrap_or(list_style_type::computed_value::T::decimal);
let style = parse_counter_style(context, input);
Ok(ContentItem::Counters(name, separator, style))
}),
% if product == "gecko":

View file

@ -21,21 +21,85 @@ ${helpers.single_keyword("list-style-position", "outside inside", animation_valu
// we may need to look into this and handle these differently.
//
// [1]: http://dev.w3.org/csswg/css-counter-styles/
${helpers.single_keyword("list-style-type", """
disc none circle square decimal disclosure-open disclosure-closed lower-alpha upper-alpha
""", extra_servo_values="""arabic-indic bengali cambodian cjk-decimal devanagari
gujarati gurmukhi kannada khmer lao malayalam mongolian
myanmar oriya persian telugu thai tibetan cjk-earthly-branch
cjk-heavenly-stem lower-greek hiragana hiragana-iroha katakana
katakana-iroha""",
extra_gecko_values="""japanese-informal japanese-formal korean-hangul-formal
korean-hanja-formal korean-hanja-informal simp-chinese-informal simp-chinese-formal
trad-chinese-informal trad-chinese-formal ethiopic-numeric upper-roman lower-roman
""",
gecko_constant_prefix="NS_STYLE_LIST_STYLE",
needs_conversion="True",
animation_value_type="none",
spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type")}
% if product == "servo":
${helpers.single_keyword("list-style-type", """
disc none circle square decimal disclosure-open disclosure-closed lower-alpha upper-alpha
arabic-indic bengali cambodian cjk-decimal devanagari gujarati gurmukhi kannada khmer lao
malayalam mongolian myanmar oriya persian telugu thai tibetan cjk-earthly-branch
cjk-heavenly-stem lower-greek hiragana hiragana-iroha katakana katakana-iroha""",
needs_conversion="True",
animation_value_type="none",
spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type")}
% else:
<%helpers:longhand name="list-style-type" animation_value_type="none"
spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type">
use std::fmt;
use style_traits::ToCss;
use values::CustomIdent;
use values::HasViewportPercentage;
use values::computed::ComputedValueAsSpecified;
use values::generics::CounterStyleOrNone;
pub use self::computed_value::T as SpecifiedValue;
pub mod computed_value {
use values::generics::CounterStyleOrNone;
/// <counter-style> | none
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct T(pub CounterStyleOrNone);
}
impl ComputedValueAsSpecified for SpecifiedValue {}
no_viewport_percentage!(SpecifiedValue);
impl ToCss for SpecifiedValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
self.0.to_css(dest)
}
}
#[cfg(feature = "gecko")]
impl SpecifiedValue {
/// Convert from gecko keyword to list-style-type.
///
/// This should only be used for mapping type attribute to
/// list-style-type, and thus only values possible in that
/// attribute is considered here.
pub fn from_gecko_keyword(value: u32) -> Self {
use gecko_bindings::structs;
SpecifiedValue(if value == structs::NS_STYLE_LIST_STYLE_NONE {
CounterStyleOrNone::None_
} else {
<%
values = """disc circle square decimal lower-roman
upper-roman lower-alpha upper-alpha""".split()
%>
CounterStyleOrNone::Name(CustomIdent(match value {
% for style in values:
structs::NS_STYLE_LIST_STYLE_${style.replace('-', '_').upper()} => atom!("${style}"),
% endfor
_ => unreachable!("Unknown counter style keyword value"),
}))
})
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T(CounterStyleOrNone::disc())
}
#[inline]
pub fn get_initial_specified_value() -> SpecifiedValue {
SpecifiedValue(CounterStyleOrNone::disc())
}
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
CounterStyleOrNone::parse(context, input).map(SpecifiedValue)
}
</%helpers:longhand>
% endif
<%helpers:longhand name="list-style-image" animation_value_type="none"
boxed="${product == 'gecko'}"

View file

@ -25,14 +25,6 @@
continue
}
if list_style_type.is_none() {
if let Ok(value) = input.try(|input| list_style_type::parse(context, input)) {
list_style_type = Some(value);
any = true;
continue
}
}
if image.is_none() {
if let Ok(value) = input.try(|input| list_style_image::parse(context, input)) {
image = Some(value);
@ -48,11 +40,31 @@
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(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.
@ -61,14 +73,14 @@
Ok(Longhands {
list_style_position: position,
list_style_image: list_style_image::SpecifiedValue(Either::Second(None_)),
list_style_type: list_style_type::SpecifiedValue::none,
list_style_type: list_style_type_none(),
})
}
(true, 1, None, Some(image)) => {
Ok(Longhands {
list_style_position: position,
list_style_image: image,
list_style_type: list_style_type::SpecifiedValue::none,
list_style_type: list_style_type_none(),
})
}
(true, 1, Some(list_style_type), None) => {
@ -82,7 +94,7 @@
Ok(Longhands {
list_style_position: position,
list_style_image: list_style_image::SpecifiedValue(Either::Second(None_)),
list_style_type: list_style_type::SpecifiedValue::none,
list_style_type: list_style_type_none(),
})
}
(true, 0, list_style_type, image) => {

View file

@ -5,9 +5,13 @@
//! Generic types that share their serialization implementations
//! for both specified and computed values.
use counter_style::parse_counter_style_name;
use cssparser::Parser;
use euclid::size::Size2D;
use parser::{Parse, ParserContext};
use std::fmt;
use style_traits::ToCss;
use super::CustomIdent;
use super::HasViewportPercentage;
use super::computed::{Context, ToComputedValue};
@ -75,3 +79,49 @@ impl<L: ToComputedValue> ToComputedValue for BorderRadiusSize<L> {
BorderRadiusSize(Size2D::new(w, h))
}
}
/// https://drafts.csswg.org/css-counter-styles/#typedef-counter-style
///
/// Since wherever <counter-style> is used, 'none' is a valid value as
/// well, we combine them into one type to make code simpler.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum CounterStyleOrNone {
/// none
None_,
/// <counter-style-name>
Name(CustomIdent),
}
impl CounterStyleOrNone {
/// disc value
pub fn disc() -> Self {
CounterStyleOrNone::Name(CustomIdent(atom!("disc")))
}
/// decimal value
pub fn decimal() -> Self {
CounterStyleOrNone::Name(CustomIdent(atom!("decimal")))
}
}
no_viewport_percentage!(CounterStyleOrNone);
impl Parse for CounterStyleOrNone {
fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
input.try(|input| {
parse_counter_style_name(input).map(CounterStyleOrNone::Name)
}).or_else(|_| {
input.expect_ident_matching("none").map(|_| CounterStyleOrNone::None_)
})
}
}
impl ToCss for CounterStyleOrNone {
#[inline]
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match self {
&CounterStyleOrNone::None_ => dest.write_str("none"),
&CounterStyleOrNone::Name(ref name) => name.to_css(dest),
}
}
}