зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #13860 - Implement parsing/serialization and gecko glue for text-emphasis-style (from canaltinova:text-emphasis-style); r=Manishearth,emilio
<!-- Please describe your changes on the following line: --> Implementation of parsing/serialization and gecko glue for text-emphasis-style. r? @Manishearth --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #13853 (github issue number if applicable). <!-- Either: --> - [X] There are tests for these changes <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: bfd966f81998cc13f067a31182115577ba9346ca
This commit is contained in:
Родитель
42c549806a
Коммит
aa6bea5f24
|
@ -1646,7 +1646,7 @@ fn static_assert() {
|
|||
|
||||
|
||||
<%self:impl_trait style_struct_name="InheritedText"
|
||||
skip_longhands="text-align text-shadow line-height letter-spacing word-spacing">
|
||||
skip_longhands="text-align text-emphasis-style text-shadow line-height letter-spacing word-spacing">
|
||||
|
||||
<% text_align_keyword = Keyword("text-align", "start end left right center justify -moz-center -moz-left " +
|
||||
"-moz-right match-parent") %>
|
||||
|
@ -1744,6 +1744,55 @@ fn static_assert() {
|
|||
|
||||
<%call expr="impl_coord_copy('word_spacing', 'mWordSpacing')"></%call>
|
||||
|
||||
fn clear_text_emphasis_style_if_string(&mut self) {
|
||||
use nsstring::nsString;
|
||||
if self.gecko.mTextEmphasisStyle == structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING as u8 {
|
||||
self.gecko.mTextEmphasisStyleString.assign(&nsString::new());
|
||||
self.gecko.mTextEmphasisStyle = structs::NS_STYLE_TEXT_EMPHASIS_STYLE_NONE as u8;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_text_emphasis_style(&mut self, v: longhands::text_emphasis_style::computed_value::T) {
|
||||
use nsstring::nsCString;
|
||||
use properties::longhands::text_emphasis_style::computed_value::T;
|
||||
use properties::longhands::text_emphasis_style::ShapeKeyword;
|
||||
|
||||
self.clear_text_emphasis_style_if_string();
|
||||
let (te, s) = match v {
|
||||
T::None => (structs::NS_STYLE_TEXT_EMPHASIS_STYLE_NONE, ""),
|
||||
T::Keyword(ref keyword) => {
|
||||
let fill = if keyword.fill {
|
||||
structs::NS_STYLE_TEXT_EMPHASIS_STYLE_FILLED
|
||||
} else {
|
||||
structs::NS_STYLE_TEXT_EMPHASIS_STYLE_OPEN
|
||||
};
|
||||
let shape = match keyword.shape {
|
||||
ShapeKeyword::Dot => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_DOT,
|
||||
ShapeKeyword::Circle => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_CIRCLE,
|
||||
ShapeKeyword::DoubleCircle => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_DOUBLE_CIRCLE,
|
||||
ShapeKeyword::Triangle => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_TRIANGLE,
|
||||
ShapeKeyword::Sesame => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_SESAME,
|
||||
};
|
||||
|
||||
(shape | fill, keyword.shape.char(keyword.fill))
|
||||
},
|
||||
T::String(ref s) => {
|
||||
(structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING, &**s)
|
||||
},
|
||||
};
|
||||
self.gecko.mTextEmphasisStyleString.assign_utf8(&nsCString::from(s));
|
||||
self.gecko.mTextEmphasisStyle = te as u8;
|
||||
}
|
||||
|
||||
pub fn copy_text_emphasis_style_from(&mut self, other: &Self) {
|
||||
self.clear_text_emphasis_style_if_string();
|
||||
if other.gecko.mTextEmphasisStyle == structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING as u8 {
|
||||
self.gecko.mTextEmphasisStyleString
|
||||
.assign(&other.gecko.mTextEmphasisStyleString)
|
||||
}
|
||||
self.gecko.mTextEmphasisStyle = other.gecko.mTextEmphasisStyle;
|
||||
}
|
||||
|
||||
</%self:impl_trait>
|
||||
|
||||
<%self:impl_trait style_struct_name="Text"
|
||||
|
|
|
@ -737,7 +737,205 @@ ${helpers.single_keyword("text-align-last",
|
|||
}
|
||||
</%helpers:longhand>
|
||||
|
||||
<%helpers:longhand name="text-emphasis-style" products="gecko" need_clone="True" animatable="False">
|
||||
use computed_values::writing_mode::T as writing_mode;
|
||||
use cssparser::ToCss;
|
||||
use std::fmt;
|
||||
use values::LocalToCss;
|
||||
use values::NoViewportPercentage;
|
||||
|
||||
impl NoViewportPercentage for SpecifiedValue {}
|
||||
|
||||
pub mod computed_value {
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub enum T {
|
||||
Keyword(KeywordValue),
|
||||
None,
|
||||
String(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub struct KeywordValue {
|
||||
pub fill: bool,
|
||||
pub shape: super::ShapeKeyword,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub enum SpecifiedValue {
|
||||
Keyword(KeywordValue),
|
||||
None,
|
||||
String(String),
|
||||
}
|
||||
|
||||
impl ToCss for computed_value::T {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
match *self {
|
||||
computed_value::T::Keyword(ref keyword) => keyword.to_css(dest),
|
||||
computed_value::T::None => dest.write_str("none"),
|
||||
computed_value::T::String(ref string) => write!(dest, "\"{}\"", string),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ToCss for SpecifiedValue {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
match *self {
|
||||
SpecifiedValue::Keyword(ref keyword) => keyword.to_css(dest),
|
||||
SpecifiedValue::None => dest.write_str("none"),
|
||||
SpecifiedValue::String(ref string) => write!(dest, "\"{}\"", string),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub enum KeywordValue {
|
||||
Fill(bool),
|
||||
Shape(ShapeKeyword),
|
||||
FillAndShape(bool, ShapeKeyword),
|
||||
}
|
||||
|
||||
impl ToCss for KeywordValue {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
if let Some(fill) = self.fill() {
|
||||
if fill {
|
||||
try!(dest.write_str("filled"));
|
||||
} else {
|
||||
try!(dest.write_str("open"));
|
||||
}
|
||||
}
|
||||
if let Some(shape) = self.shape() {
|
||||
if self.fill().is_some() {
|
||||
try!(dest.write_str(" "));
|
||||
}
|
||||
try!(shape.to_css(dest));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl ToCss for computed_value::KeywordValue {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
if self.fill {
|
||||
try!(dest.write_str("filled"));
|
||||
} else {
|
||||
try!(dest.write_str("open"));
|
||||
}
|
||||
try!(dest.write_str(" "));
|
||||
self.shape.to_css(dest)
|
||||
}
|
||||
}
|
||||
|
||||
impl KeywordValue {
|
||||
fn fill(&self) -> Option<bool> {
|
||||
match *self {
|
||||
KeywordValue::Fill(fill) |
|
||||
KeywordValue::FillAndShape(fill,_) => Some(fill),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn shape(&self) -> Option<ShapeKeyword> {
|
||||
match *self {
|
||||
KeywordValue::Shape(shape) |
|
||||
KeywordValue::FillAndShape(_, shape) => Some(shape),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_css_keyword_enum!(ShapeKeyword:
|
||||
"dot" => Dot,
|
||||
"circle" => Circle,
|
||||
"double-circle" => DoubleCircle,
|
||||
"triangle" => Triangle,
|
||||
"sesame" => Sesame);
|
||||
|
||||
impl ShapeKeyword {
|
||||
pub fn char(&self, fill: bool) -> &str {
|
||||
match *self {
|
||||
ShapeKeyword::Dot => if fill { "\u{2022}" } else { "\u{25e6}" },
|
||||
ShapeKeyword::Circle => if fill { "\u{25cf}" } else { "\u{25cb}" },
|
||||
ShapeKeyword::DoubleCircle => if fill { "\u{25c9}" } else { "\u{25ce}" },
|
||||
ShapeKeyword::Triangle => if fill { "\u{25b2}" } else { "\u{25b3}" },
|
||||
ShapeKeyword::Sesame => if fill { "\u{fe45}" } else { "\u{fe46}" },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_initial_value() -> computed_value::T {
|
||||
computed_value::T::None
|
||||
}
|
||||
|
||||
impl ToComputedValue for SpecifiedValue {
|
||||
type ComputedValue = computed_value::T;
|
||||
|
||||
#[inline]
|
||||
fn to_computed_value(&self, context: &Context) -> computed_value::T {
|
||||
match *self {
|
||||
SpecifiedValue::Keyword(ref keyword) => {
|
||||
let default_shape = if context.style().get_inheritedbox()
|
||||
.clone_writing_mode() == writing_mode::horizontal_tb {
|
||||
ShapeKeyword::Circle
|
||||
} else {
|
||||
ShapeKeyword::Sesame
|
||||
};
|
||||
computed_value::T::Keyword(computed_value::KeywordValue {
|
||||
fill: keyword.fill().unwrap_or(true),
|
||||
shape: keyword.shape().unwrap_or(default_shape),
|
||||
})
|
||||
},
|
||||
SpecifiedValue::None => computed_value::T::None,
|
||||
SpecifiedValue::String(ref s) => {
|
||||
let string = s.chars().next().as_ref().map(ToString::to_string).unwrap_or_default();
|
||||
computed_value::T::String(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn from_computed_value(computed: &computed_value::T) -> Self {
|
||||
match *computed {
|
||||
computed_value::T::Keyword(ref keyword) =>
|
||||
SpecifiedValue::Keyword(KeywordValue::FillAndShape(keyword.fill,keyword.shape)),
|
||||
computed_value::T::None => SpecifiedValue::None,
|
||||
computed_value::T::String(ref string) => SpecifiedValue::String(string.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||||
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||||
return Ok(SpecifiedValue::None);
|
||||
}
|
||||
|
||||
if let Ok(s) = input.try(|input| input.expect_string()) {
|
||||
// Handle <string>
|
||||
return Ok(SpecifiedValue::String(s.into_owned()));
|
||||
}
|
||||
|
||||
// Handle a pair of keywords
|
||||
let mut shape = input.try(ShapeKeyword::parse);
|
||||
let fill = if input.try(|input| input.expect_ident_matching("filled")).is_ok() {
|
||||
Some(true)
|
||||
} else if input.try(|input| input.expect_ident_matching("open")).is_ok() {
|
||||
Some(false)
|
||||
} else { None };
|
||||
if shape.is_err() {
|
||||
shape = input.try(ShapeKeyword::parse);
|
||||
}
|
||||
|
||||
// At least one of shape or fill must be handled
|
||||
let keyword_value = match (fill, shape) {
|
||||
(Some(fill), Ok(shape)) => KeywordValue::FillAndShape(fill,shape),
|
||||
(Some(fill), Err(_)) => KeywordValue::Fill(fill),
|
||||
(None, Ok(shape)) => KeywordValue::Shape(shape),
|
||||
_ => return Err(()),
|
||||
};
|
||||
Ok(SpecifiedValue::Keyword(keyword_value))
|
||||
}
|
||||
</%helpers:longhand>
|
||||
|
||||
// TODO(pcwalton): `full-width`
|
||||
${helpers.single_keyword("text-transform",
|
||||
|
|
|
@ -1639,7 +1639,8 @@ pub fn cascade(viewport_size: Size2D<Au>,
|
|||
PropertyDeclaration::Color(_) |
|
||||
PropertyDeclaration::Position(_) |
|
||||
PropertyDeclaration::Float(_) |
|
||||
PropertyDeclaration::TextDecoration${'' if product == 'servo' else 'Line'}(_)
|
||||
PropertyDeclaration::TextDecoration${'' if product == 'servo' else 'Line'}(_) |
|
||||
PropertyDeclaration::WritingMode(_)
|
||||
);
|
||||
if
|
||||
% if category_to_cascade_now == "early":
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use cssparser::Parser;
|
||||
use media_queries::CSSErrorReporterTest;
|
||||
use style::parser::ParserContext;
|
||||
use style::stylesheets::Origin;
|
||||
use url::Url;
|
||||
|
||||
#[test]
|
||||
fn text_emphasis_style_longhand_should_parse_properly() {
|
||||
use style::properties::longhands::text_emphasis_style;
|
||||
use style::properties::longhands::text_emphasis_style::{ShapeKeyword, SpecifiedValue, KeywordValue};
|
||||
|
||||
let none = parse_longhand!(text_emphasis_style, "none");
|
||||
assert_eq!(none, SpecifiedValue::None);
|
||||
|
||||
let fill = parse_longhand!(text_emphasis_style, "open");
|
||||
let fill_struct = SpecifiedValue::Keyword(KeywordValue::Fill(false));
|
||||
assert_eq!(fill, fill_struct);
|
||||
|
||||
let shape = parse_longhand!(text_emphasis_style, "triangle");
|
||||
let shape_struct = SpecifiedValue::Keyword(KeywordValue::Shape(ShapeKeyword::Triangle));
|
||||
assert_eq!(shape, shape_struct);
|
||||
|
||||
let fill_shape = parse_longhand!(text_emphasis_style, "filled dot");
|
||||
let fill_shape_struct = SpecifiedValue::Keyword(KeywordValue::FillAndShape(true, ShapeKeyword::Dot));
|
||||
assert_eq!(fill_shape, fill_shape_struct);
|
||||
|
||||
let shape_fill = parse_longhand!(text_emphasis_style, "dot filled");
|
||||
let shape_fill_struct = SpecifiedValue::Keyword(KeywordValue::FillAndShape(true, ShapeKeyword::Dot));
|
||||
assert_eq!(shape_fill, shape_fill_struct);
|
||||
|
||||
let a_string = parse_longhand!(text_emphasis_style, "\"a\"");
|
||||
let a_string_struct = SpecifiedValue::String("a".to_string());
|
||||
assert_eq!(a_string, a_string_struct);
|
||||
|
||||
let chinese_string = parse_longhand!(text_emphasis_style, "\"点\"");
|
||||
let chinese_string_struct = SpecifiedValue::String("点".to_string());
|
||||
assert_eq!(chinese_string, chinese_string_struct);
|
||||
|
||||
let unicode_string = parse_longhand!(text_emphasis_style, "\"\\25B2\"");
|
||||
let unicode_string_struct = SpecifiedValue::String("▲".to_string());
|
||||
assert_eq!(unicode_string, unicode_string_struct);
|
||||
}
|
|
@ -63,6 +63,7 @@ macro_rules! parse_longhand {
|
|||
|
||||
mod basic_shape;
|
||||
mod image;
|
||||
mod inherited_text;
|
||||
mod mask;
|
||||
mod position;
|
||||
mod selectors;
|
||||
|
|
Загрузка…
Ссылка в новой задаче