2015-09-04 15:46:11 +03:00
|
|
|
/* 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/. */
|
|
|
|
|
2016-12-31 14:19:02 +03:00
|
|
|
//! Helper types and traits for the handling of CSS values.
|
|
|
|
|
2016-11-07 08:26:08 +03:00
|
|
|
use app_units::Au;
|
2017-10-10 20:31:24 +03:00
|
|
|
use cssparser::{ParseError, Parser, Token, UnicodeRange, serialize_string};
|
2017-09-04 21:14:44 +03:00
|
|
|
use cssparser::ToCss as CssparserToCss;
|
2017-10-09 17:29:18 +03:00
|
|
|
use servo_arc::Arc;
|
2017-06-22 11:19:51 +03:00
|
|
|
use std::fmt::{self, Write};
|
2016-11-07 08:26:08 +03:00
|
|
|
|
2017-06-07 18:55:08 +03:00
|
|
|
/// Serialises a value according to its CSS representation.
|
|
|
|
///
|
2017-06-09 15:00:45 +03:00
|
|
|
/// This trait is implemented for `str` and its friends, serialising the string
|
|
|
|
/// contents as a CSS quoted string.
|
|
|
|
///
|
2017-06-07 18:55:08 +03:00
|
|
|
/// This trait is derivable with `#[derive(ToCss)]`, with the following behaviour:
|
|
|
|
/// * unit variants get serialised as the `snake-case` representation
|
|
|
|
/// of their name;
|
|
|
|
/// * unit variants whose name starts with "Moz" or "Webkit" are prepended
|
|
|
|
/// with a "-";
|
2017-08-28 18:13:13 +03:00
|
|
|
/// * if `#[css(comma)]` is found on a variant, its fields are separated by
|
|
|
|
/// commas, otherwise, by spaces;
|
|
|
|
/// * if `#[css(function)]` is found on a variant, the variant name gets
|
|
|
|
/// serialised like unit variants and its fields are surrounded by parentheses;
|
2017-11-12 18:41:04 +03:00
|
|
|
/// * if `#[css(iterable)]` is found on a function variant, that variant needs
|
|
|
|
/// to have a single member, and that member needs to be iterable. The
|
2018-03-06 11:33:18 +03:00
|
|
|
/// iterable will be serialized as the arguments for the function;
|
2018-03-07 19:06:09 +03:00
|
|
|
/// * an iterable field can also be annotated with `#[css(if_empty = "foo")]`
|
|
|
|
/// to print `"foo"` if the iterator is empty;
|
2017-11-12 18:41:04 +03:00
|
|
|
/// * if `#[css(dimension)]` is found on a variant, that variant needs
|
|
|
|
/// to have a single member. The variant would be serialized as a CSS
|
2018-03-06 11:33:18 +03:00
|
|
|
/// dimension token, like: <member><identifier>;
|
|
|
|
/// * if `#[css(skip)]` is found on a field, the `ToCss` call for that field
|
|
|
|
/// is skipped;
|
2017-08-28 18:13:13 +03:00
|
|
|
/// * finally, one can put `#[css(derive_debug)]` on the whole type, to
|
|
|
|
/// implement `Debug` by a single call to `ToCss::to_css`.
|
2016-11-07 08:26:08 +03:00
|
|
|
pub trait ToCss {
|
|
|
|
/// Serialize `self` in CSS syntax, writing to `dest`.
|
2018-01-23 21:03:41 +03:00
|
|
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write;
|
2016-11-07 08:26:08 +03:00
|
|
|
|
|
|
|
/// Serialize `self` in CSS syntax and return a string.
|
|
|
|
///
|
|
|
|
/// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
|
|
|
|
#[inline]
|
|
|
|
fn to_css_string(&self) -> String {
|
|
|
|
let mut s = String::new();
|
2018-01-23 21:03:41 +03:00
|
|
|
self.to_css(&mut CssWriter::new(&mut s)).unwrap();
|
2016-11-07 08:26:08 +03:00
|
|
|
s
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-04 23:30:23 +03:00
|
|
|
impl<'a, T> ToCss for &'a T where T: ToCss + ?Sized {
|
2018-01-23 21:03:41 +03:00
|
|
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write {
|
2017-04-04 23:30:23 +03:00
|
|
|
(*self).to_css(dest)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-09 15:00:45 +03:00
|
|
|
impl ToCss for str {
|
|
|
|
#[inline]
|
2018-01-23 21:03:41 +03:00
|
|
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write {
|
2017-06-09 15:00:45 +03:00
|
|
|
serialize_string(self, dest)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ToCss for String {
|
|
|
|
#[inline]
|
2018-01-23 21:03:41 +03:00
|
|
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write {
|
2017-06-09 15:00:45 +03:00
|
|
|
serialize_string(self, dest)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-22 11:19:51 +03:00
|
|
|
impl<T> ToCss for Option<T>
|
|
|
|
where
|
|
|
|
T: ToCss,
|
|
|
|
{
|
|
|
|
#[inline]
|
2018-01-23 21:03:41 +03:00
|
|
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write {
|
2017-06-22 11:19:51 +03:00
|
|
|
self.as_ref().map_or(Ok(()), |value| value.to_css(dest))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-23 21:03:41 +03:00
|
|
|
/// A writer tailored for serialising CSS.
|
|
|
|
///
|
|
|
|
/// Coupled with SequenceWriter, this allows callers to transparently handle
|
|
|
|
/// things like comma-separated values etc.
|
|
|
|
pub struct CssWriter<'w, W: 'w> {
|
|
|
|
inner: &'w mut W,
|
|
|
|
prefix: Option<&'static str>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'w, W> CssWriter<'w, W>
|
|
|
|
where
|
|
|
|
W: Write,
|
|
|
|
{
|
|
|
|
/// Creates a new `CssWriter`.
|
|
|
|
#[inline]
|
|
|
|
pub fn new(inner: &'w mut W) -> Self {
|
|
|
|
Self { inner, prefix: Some("") }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'w, W> Write for CssWriter<'w, W>
|
|
|
|
where
|
|
|
|
W: Write,
|
|
|
|
{
|
|
|
|
#[inline]
|
|
|
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
|
|
if s.is_empty() {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
if let Some(prefix) = self.prefix.take() {
|
|
|
|
// We are going to write things, but first we need to write
|
|
|
|
// the prefix that was set by `SequenceWriter::item`.
|
|
|
|
if !prefix.is_empty() {
|
|
|
|
self.inner.write_str(prefix)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.inner.write_str(s)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn write_char(&mut self, c: char) -> fmt::Result {
|
|
|
|
if let Some(prefix) = self.prefix.take() {
|
|
|
|
// See comment in `write_str`.
|
|
|
|
if !prefix.is_empty() {
|
|
|
|
self.inner.write_str(prefix)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.inner.write_char(c)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-04 21:14:44 +03:00
|
|
|
#[macro_export]
|
|
|
|
macro_rules! serialize_function {
|
|
|
|
($dest: expr, $name: ident($( $arg: expr, )+)) => {
|
|
|
|
serialize_function!($dest, $name($($arg),+))
|
|
|
|
};
|
|
|
|
($dest: expr, $name: ident($first_arg: expr $( , $arg: expr )*)) => {
|
|
|
|
{
|
|
|
|
$dest.write_str(concat!(stringify!($name), "("))?;
|
|
|
|
$first_arg.to_css($dest)?;
|
|
|
|
$(
|
|
|
|
$dest.write_str(", ")?;
|
|
|
|
$arg.to_css($dest)?;
|
|
|
|
)*
|
|
|
|
$dest.write_char(')')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-22 11:19:51 +03:00
|
|
|
/// Convenience wrapper to serialise CSS values separated by a given string.
|
2018-01-23 21:03:41 +03:00
|
|
|
pub struct SequenceWriter<'a, 'b: 'a, W: 'b> {
|
|
|
|
inner: &'a mut CssWriter<'b, W>,
|
|
|
|
separator: &'static str,
|
2017-06-22 11:19:51 +03:00
|
|
|
}
|
|
|
|
|
2018-01-23 21:03:41 +03:00
|
|
|
impl<'a, 'b, W> SequenceWriter<'a, 'b, W>
|
2017-06-22 11:19:51 +03:00
|
|
|
where
|
2018-01-23 21:03:41 +03:00
|
|
|
W: Write + 'b,
|
2017-06-22 11:19:51 +03:00
|
|
|
{
|
|
|
|
/// Create a new sequence writer.
|
|
|
|
#[inline]
|
2018-01-23 21:03:41 +03:00
|
|
|
pub fn new(inner: &'a mut CssWriter<'b, W>, separator: &'static str) -> Self {
|
|
|
|
if inner.prefix.is_none() {
|
|
|
|
// See comment in `item`.
|
|
|
|
inner.prefix = Some("");
|
2017-06-22 11:19:51 +03:00
|
|
|
}
|
2018-01-23 21:03:41 +03:00
|
|
|
Self { inner, separator }
|
2017-06-22 11:19:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Serialises a CSS value, writing any separator as necessary.
|
|
|
|
///
|
|
|
|
/// The separator is never written before any `item` produces any output,
|
|
|
|
/// and is written in subsequent calls only if the `item` produces some
|
|
|
|
/// output on its own again. This lets us handle `Option<T>` fields by
|
|
|
|
/// just not printing anything on `None`.
|
|
|
|
#[inline]
|
|
|
|
pub fn item<T>(&mut self, item: &T) -> fmt::Result
|
|
|
|
where
|
|
|
|
T: ToCss,
|
|
|
|
{
|
2018-01-23 21:03:41 +03:00
|
|
|
let old_prefix = self.inner.prefix;
|
|
|
|
if old_prefix.is_none() {
|
|
|
|
// If there is no prefix in the inner writer, a previous
|
|
|
|
// call to this method produced output, which means we need
|
|
|
|
// to write the separator next time we produce output again.
|
|
|
|
self.inner.prefix = Some(self.separator);
|
2017-06-22 11:19:51 +03:00
|
|
|
}
|
2018-01-23 21:03:41 +03:00
|
|
|
item.to_css(&mut self.inner)?;
|
|
|
|
match (old_prefix, self.inner.prefix) {
|
|
|
|
(_, None) => {
|
|
|
|
// This call produced output and cleaned up after itself.
|
|
|
|
}
|
|
|
|
(None, Some(p)) => {
|
|
|
|
// Some previous call to `item` produced output,
|
|
|
|
// but this one did not, prefix should be the same as
|
|
|
|
// the one we set.
|
|
|
|
debug_assert_eq!(self.separator, p);
|
|
|
|
// We clean up here even though it's not necessary just
|
|
|
|
// to be able to do all these assertion checks.
|
|
|
|
self.inner.prefix = None;
|
|
|
|
}
|
|
|
|
(Some(old), Some(new)) => {
|
|
|
|
// No previous call to `item` produced output, and this one
|
|
|
|
// either.
|
|
|
|
debug_assert_eq!(old, new);
|
2017-06-22 11:19:51 +03:00
|
|
|
}
|
|
|
|
}
|
2018-01-23 21:03:41 +03:00
|
|
|
Ok(())
|
2017-06-22 11:19:51 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-07 19:06:09 +03:00
|
|
|
/// A wrapper type that implements `ToCss` by printing its inner field.
|
|
|
|
pub struct Verbatim<'a, T>(pub &'a T)
|
|
|
|
where
|
|
|
|
T: ?Sized + 'a;
|
|
|
|
|
|
|
|
impl<'a, T> ToCss for Verbatim<'a, T>
|
|
|
|
where
|
|
|
|
T: AsRef<str> + ?Sized + 'a,
|
|
|
|
{
|
|
|
|
#[inline]
|
|
|
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write {
|
|
|
|
dest.write_str(self.0.as_ref())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-20 13:08:11 +03:00
|
|
|
/// Type used as the associated type in the `OneOrMoreSeparated` trait on a
|
|
|
|
/// type to indicate that a serialized list of elements of this type is
|
|
|
|
/// separated by commas.
|
2017-06-28 01:34:15 +03:00
|
|
|
pub struct Comma;
|
2017-02-03 16:09:30 +03:00
|
|
|
|
2017-06-20 13:08:11 +03:00
|
|
|
/// Type used as the associated type in the `OneOrMoreSeparated` trait on a
|
|
|
|
/// type to indicate that a serialized list of elements of this type is
|
|
|
|
/// separated by spaces.
|
2017-06-28 01:34:15 +03:00
|
|
|
pub struct Space;
|
|
|
|
|
|
|
|
/// Type used as the associated type in the `OneOrMoreSeparated` trait on a
|
|
|
|
/// type to indicate that a serialized list of elements of this type is
|
|
|
|
/// separated by commas, but spaces without commas are also allowed when
|
|
|
|
/// parsing.
|
|
|
|
pub struct CommaWithSpace;
|
2017-02-15 14:58:38 +03:00
|
|
|
|
2017-06-20 13:08:11 +03:00
|
|
|
/// A trait satisfied by the types corresponding to separators.
|
|
|
|
pub trait Separator {
|
|
|
|
/// The separator string that the satisfying separator type corresponds to.
|
|
|
|
fn separator() -> &'static str;
|
2017-06-28 01:34:15 +03:00
|
|
|
|
|
|
|
/// Parses a sequence of values separated by this separator.
|
|
|
|
///
|
|
|
|
/// The given closure is called repeatedly for each item in the sequence.
|
|
|
|
///
|
|
|
|
/// Successful results are accumulated in a vector.
|
|
|
|
///
|
|
|
|
/// This method returns `Err(_)` the first time a closure does or if
|
|
|
|
/// the separators aren't correct.
|
|
|
|
fn parse<'i, 't, F, T, E>(
|
|
|
|
parser: &mut Parser<'i, 't>,
|
|
|
|
parse_one: F,
|
|
|
|
) -> Result<Vec<T>, ParseError<'i, E>>
|
|
|
|
where
|
|
|
|
F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>;
|
2017-06-20 13:08:11 +03:00
|
|
|
}
|
|
|
|
|
2017-06-28 01:34:15 +03:00
|
|
|
impl Separator for Comma {
|
2017-06-20 13:08:11 +03:00
|
|
|
fn separator() -> &'static str {
|
|
|
|
", "
|
|
|
|
}
|
2017-06-28 01:34:15 +03:00
|
|
|
|
|
|
|
fn parse<'i, 't, F, T, E>(
|
|
|
|
input: &mut Parser<'i, 't>,
|
|
|
|
parse_one: F,
|
|
|
|
) -> Result<Vec<T>, ParseError<'i, E>>
|
|
|
|
where
|
|
|
|
F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>
|
|
|
|
{
|
|
|
|
input.parse_comma_separated(parse_one)
|
|
|
|
}
|
2017-06-20 13:08:11 +03:00
|
|
|
}
|
|
|
|
|
2017-06-28 01:34:15 +03:00
|
|
|
impl Separator for Space {
|
2017-06-20 13:08:11 +03:00
|
|
|
fn separator() -> &'static str {
|
|
|
|
" "
|
|
|
|
}
|
2017-06-28 01:34:15 +03:00
|
|
|
|
|
|
|
fn parse<'i, 't, F, T, E>(
|
|
|
|
input: &mut Parser<'i, 't>,
|
|
|
|
mut parse_one: F,
|
|
|
|
) -> Result<Vec<T>, ParseError<'i, E>>
|
|
|
|
where
|
|
|
|
F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>
|
|
|
|
{
|
2017-08-26 04:24:58 +03:00
|
|
|
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
|
2017-06-28 01:34:15 +03:00
|
|
|
let mut results = vec![parse_one(input)?];
|
2017-08-26 04:24:58 +03:00
|
|
|
loop {
|
|
|
|
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
|
|
|
|
if let Ok(item) = input.try(&mut parse_one) {
|
|
|
|
results.push(item);
|
|
|
|
} else {
|
|
|
|
return Ok(results)
|
|
|
|
}
|
2017-06-28 01:34:15 +03:00
|
|
|
}
|
|
|
|
}
|
2017-06-20 13:08:11 +03:00
|
|
|
}
|
|
|
|
|
2017-06-28 01:34:15 +03:00
|
|
|
impl Separator for CommaWithSpace {
|
|
|
|
fn separator() -> &'static str {
|
|
|
|
", "
|
|
|
|
}
|
2017-06-20 13:08:11 +03:00
|
|
|
|
2017-06-28 01:34:15 +03:00
|
|
|
fn parse<'i, 't, F, T, E>(
|
|
|
|
input: &mut Parser<'i, 't>,
|
|
|
|
mut parse_one: F,
|
|
|
|
) -> Result<Vec<T>, ParseError<'i, E>>
|
|
|
|
where
|
|
|
|
F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>
|
|
|
|
{
|
2017-08-26 04:24:58 +03:00
|
|
|
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
|
2017-06-28 01:34:15 +03:00
|
|
|
let mut results = vec![parse_one(input)?];
|
|
|
|
loop {
|
2017-08-26 04:24:58 +03:00
|
|
|
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
|
2017-10-10 20:31:24 +03:00
|
|
|
let comma_location = input.current_source_location();
|
2017-06-28 01:34:15 +03:00
|
|
|
let comma = input.try(|i| i.expect_comma()).is_ok();
|
2017-08-26 04:24:58 +03:00
|
|
|
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
|
2017-06-28 01:34:15 +03:00
|
|
|
if let Ok(item) = input.try(&mut parse_one) {
|
|
|
|
results.push(item);
|
|
|
|
} else if comma {
|
2017-10-10 20:31:24 +03:00
|
|
|
return Err(comma_location.new_unexpected_token_error(Token::Comma));
|
2017-06-28 01:34:15 +03:00
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(results)
|
|
|
|
}
|
|
|
|
}
|
2017-06-20 13:08:11 +03:00
|
|
|
|
|
|
|
/// Marker trait on T to automatically implement ToCss for Vec<T> when T's are
|
|
|
|
/// separated by some delimiter `delim`.
|
|
|
|
pub trait OneOrMoreSeparated {
|
|
|
|
/// Associated type indicating which separator is used.
|
|
|
|
type S: Separator;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl OneOrMoreSeparated for UnicodeRange {
|
2017-06-28 01:34:15 +03:00
|
|
|
type S = Comma;
|
2017-06-20 13:08:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> ToCss for Vec<T> where T: ToCss + OneOrMoreSeparated {
|
2018-01-23 21:03:41 +03:00
|
|
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write {
|
2017-02-03 16:09:30 +03:00
|
|
|
let mut iter = self.iter();
|
|
|
|
iter.next().unwrap().to_css(dest)?;
|
|
|
|
for item in iter {
|
2017-06-20 13:08:11 +03:00
|
|
|
dest.write_str(<T as OneOrMoreSeparated>::S::separator())?;
|
2017-02-03 16:09:30 +03:00
|
|
|
item.to_css(dest)?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-09 15:00:45 +03:00
|
|
|
impl<T> ToCss for Box<T> where T: ?Sized + ToCss {
|
2018-01-23 21:03:41 +03:00
|
|
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
2017-06-22 11:19:51 +03:00
|
|
|
where W: Write,
|
2017-02-09 13:21:36 +03:00
|
|
|
{
|
|
|
|
(**self).to_css(dest)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-09 17:29:18 +03:00
|
|
|
impl<T> ToCss for Arc<T> where T: ?Sized + ToCss {
|
2018-01-23 21:03:41 +03:00
|
|
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
2017-10-09 17:29:18 +03:00
|
|
|
where W: Write,
|
|
|
|
{
|
|
|
|
(**self).to_css(dest)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-07 08:26:08 +03:00
|
|
|
impl ToCss for Au {
|
2018-01-23 21:03:41 +03:00
|
|
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write {
|
2017-09-04 21:14:44 +03:00
|
|
|
self.to_f64_px().to_css(dest)?;
|
|
|
|
dest.write_str("px")
|
2016-11-07 08:26:08 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! impl_to_css_for_predefined_type {
|
|
|
|
($name: ty) => {
|
|
|
|
impl<'a> ToCss for $name {
|
2018-01-23 21:03:41 +03:00
|
|
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write {
|
2016-11-07 08:26:08 +03:00
|
|
|
::cssparser::ToCss::to_css(self, dest)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
impl_to_css_for_predefined_type!(f32);
|
|
|
|
impl_to_css_for_predefined_type!(i32);
|
2017-05-19 01:09:15 +03:00
|
|
|
impl_to_css_for_predefined_type!(u16);
|
2016-11-07 08:26:08 +03:00
|
|
|
impl_to_css_for_predefined_type!(u32);
|
|
|
|
impl_to_css_for_predefined_type!(::cssparser::Token<'a>);
|
|
|
|
impl_to_css_for_predefined_type!(::cssparser::RGBA);
|
|
|
|
impl_to_css_for_predefined_type!(::cssparser::Color);
|
2017-02-15 14:58:38 +03:00
|
|
|
impl_to_css_for_predefined_type!(::cssparser::UnicodeRange);
|
2016-11-07 08:26:08 +03:00
|
|
|
|
2015-09-04 15:46:11 +03:00
|
|
|
#[macro_export]
|
|
|
|
macro_rules! define_css_keyword_enum {
|
2018-02-01 14:52:08 +03:00
|
|
|
(pub enum $name:ident { $($variant:ident = $css:expr,)+ }) => {
|
|
|
|
#[allow(missing_docs)]
|
|
|
|
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
|
|
|
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
|
2015-09-04 15:46:11 +03:00
|
|
|
pub enum $name {
|
2018-02-01 14:52:08 +03:00
|
|
|
$($variant),+
|
2015-09-04 15:46:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl $name {
|
2016-12-31 14:19:02 +03:00
|
|
|
/// Parse this property from a CSS input stream.
|
2017-06-10 00:31:48 +03:00
|
|
|
pub fn parse<'i, 't>(input: &mut ::cssparser::Parser<'i, 't>)
|
|
|
|
-> Result<$name, $crate::ParseError<'i>> {
|
2017-10-10 20:31:24 +03:00
|
|
|
use cssparser::Token;
|
|
|
|
let location = input.current_source_location();
|
|
|
|
match *input.next()? {
|
|
|
|
Token::Ident(ref ident) => {
|
|
|
|
Self::from_ident(ident).map_err(|()| {
|
2018-02-01 14:52:08 +03:00
|
|
|
location.new_unexpected_token_error(
|
|
|
|
Token::Ident(ident.clone()),
|
|
|
|
)
|
2017-10-10 20:31:24 +03:00
|
|
|
})
|
|
|
|
}
|
2018-02-01 14:52:08 +03:00
|
|
|
ref token => {
|
|
|
|
Err(location.new_unexpected_token_error(token.clone()))
|
|
|
|
}
|
2017-10-10 20:31:24 +03:00
|
|
|
}
|
2017-03-27 03:21:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse this property from an already-tokenized identifier.
|
|
|
|
pub fn from_ident(ident: &str) -> Result<$name, ()> {
|
|
|
|
match_ignore_ascii_case! { ident,
|
2018-02-01 14:52:08 +03:00
|
|
|
$($css => Ok($name::$variant),)+
|
|
|
|
_ => Err(())
|
2015-09-04 15:46:11 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-24 20:02:05 +03:00
|
|
|
impl $crate::ToCss for $name {
|
2018-02-01 14:52:08 +03:00
|
|
|
fn to_css<W>(
|
|
|
|
&self,
|
|
|
|
dest: &mut $crate::CssWriter<W>,
|
|
|
|
) -> ::std::fmt::Result
|
|
|
|
where
|
|
|
|
W: ::std::fmt::Write,
|
2016-12-31 14:19:02 +03:00
|
|
|
{
|
|
|
|
match *self {
|
2018-01-23 21:03:41 +03:00
|
|
|
$( $name::$variant => ::std::fmt::Write::write_str(dest, $css) ),+
|
2015-09-04 15:46:11 +03:00
|
|
|
}
|
2016-12-31 14:19:02 +03:00
|
|
|
}
|
2015-09-04 15:46:11 +03:00
|
|
|
}
|
2018-02-01 14:52:08 +03:00
|
|
|
};
|
2015-09-04 15:46:11 +03:00
|
|
|
}
|
|
|
|
|
2016-12-31 14:19:02 +03:00
|
|
|
/// Helper types for the handling of specified values.
|
2015-09-04 15:46:11 +03:00
|
|
|
pub mod specified {
|
2017-06-14 05:25:09 +03:00
|
|
|
use ParsingMode;
|
2017-04-12 21:31:21 +03:00
|
|
|
|
|
|
|
/// Whether to allow negative lengths or not.
|
|
|
|
#[repr(u8)]
|
servo: Merge #18938 - Replace all uses of the `heapsize` crate with `malloc_size_of` (from nnethercote:bug-1409255); r=SimonSapin
Servo currently uses `heapsize`, but Stylo/Gecko use `malloc_size_of`.
`malloc_size_of` is better -- it handles various cases that `heapsize` does not
-- so this patch changes Servo to use `malloc_size_of`.
This patch makes the following changes to the `malloc_size_of` crate.
- Adds `MallocSizeOf` trait implementations for numerous types, some built-in
(e.g. `VecDeque`), some external and Servo-only (e.g. `string_cache`).
- Makes `enclosing_size_of_op` optional, because vanilla jemalloc doesn't
support that operation.
- For `HashSet`/`HashMap`, falls back to a computed estimate when
`enclosing_size_of_op` isn't available.
- Adds an extern "C" `malloc_size_of` function that does the actual heap
measurement; this is based on the same functions from the `heapsize` crate.
This patch makes the following changes elsewhere.
- Converts all the uses of `heapsize` to instead use `malloc_size_of`.
- Disables the "heapsize"/"heap_size" feature for the external crates that
provide it.
- Removes the `HeapSizeOf` implementation from `hashglobe`.
- Adds `ignore` annotations to a few `Rc`/`Arc`, because `malloc_size_of`
doesn't derive those types, unlike `heapsize`.
<!-- Please describe your changes on the following line: -->
---
<!-- 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
- [ ] These changes fix https://bugzilla.mozilla.org/show_bug.cgi?id=1409255
<!-- Either: -->
- [ ] There are tests for these changes OR
- [ ] These changes do not require tests because testing is on the Gecko side.
<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->
<!-- 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: 4c538b642e4bdfbf42c522c5a59c258a6d14546e
--HG--
extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear
extra : subtree_revision : f9a6feed1088d0b0be2b55d7f0c2ec9c594ac33b
2017-10-18 21:56:05 +03:00
|
|
|
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, PartialOrd)]
|
2015-09-04 15:46:11 +03:00
|
|
|
pub enum AllowedNumericType {
|
2016-12-31 14:19:02 +03:00
|
|
|
/// Allow all kind of numeric values.
|
2015-09-04 15:46:11 +03:00
|
|
|
All,
|
2017-04-12 21:31:21 +03:00
|
|
|
/// Allow only non-negative numeric values.
|
|
|
|
NonNegative,
|
|
|
|
/// Allow only numeric values greater or equal to 1.0.
|
|
|
|
AtLeastOne,
|
2015-09-04 15:46:11 +03:00
|
|
|
}
|
|
|
|
|
2017-09-13 13:20:20 +03:00
|
|
|
impl Default for AllowedNumericType {
|
|
|
|
#[inline]
|
|
|
|
fn default() -> Self {
|
|
|
|
AllowedNumericType::All
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-04 15:46:11 +03:00
|
|
|
impl AllowedNumericType {
|
2017-04-12 21:31:21 +03:00
|
|
|
/// Whether the value fits the rules of this numeric type.
|
2015-09-04 15:46:11 +03:00
|
|
|
#[inline]
|
2017-06-14 05:25:09 +03:00
|
|
|
pub fn is_ok(&self, parsing_mode: ParsingMode, val: f32) -> bool {
|
|
|
|
if parsing_mode.allows_all_numeric_values() {
|
|
|
|
return true;
|
|
|
|
}
|
2015-09-04 15:46:11 +03:00
|
|
|
match *self {
|
|
|
|
AllowedNumericType::All => true,
|
2017-04-12 21:31:21 +03:00
|
|
|
AllowedNumericType::NonNegative => val >= 0.0,
|
|
|
|
AllowedNumericType::AtLeastOne => val >= 1.0,
|
2015-09-04 15:46:11 +03:00
|
|
|
}
|
|
|
|
}
|
2016-09-02 19:10:01 +03:00
|
|
|
|
2016-12-31 14:19:02 +03:00
|
|
|
/// Clamp the value following the rules of this numeric type.
|
2016-09-02 19:10:01 +03:00
|
|
|
#[inline]
|
2017-04-12 21:31:21 +03:00
|
|
|
pub fn clamp(&self, val: f32) -> f32 {
|
2016-09-02 19:10:01 +03:00
|
|
|
match *self {
|
2017-04-12 21:31:21 +03:00
|
|
|
AllowedNumericType::NonNegative if val < 0. => 0.,
|
|
|
|
AllowedNumericType::AtLeastOne if val < 1. => 1.,
|
|
|
|
_ => val,
|
2016-09-02 19:10:01 +03:00
|
|
|
}
|
|
|
|
}
|
2015-09-04 15:46:11 +03:00
|
|
|
}
|
|
|
|
}
|