use app_units::Au;
use cssparser::ToCss as CssparserToCss;
use cssparser::{serialize_string, ParseError, Parser, Token, UnicodeRange};
use servo_arc::Arc;
use std::fmt::{self, Write};
use thin_vec::ThinVec;
pub trait ToCss {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write;
#[inline]
fn to_css_string(&self) -> String {
let mut s = String::new();
self.to_css(&mut CssWriter::new(&mut s)).unwrap();
s
}
#[inline]
fn to_css_cssstring(&self) -> CssString {
let mut s = CssString::new();
self.to_css(&mut CssWriter::new(&mut s)).unwrap();
s
}
}
impl<'a, T> ToCss for &'a T
where
T: ToCss + ?Sized,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
(*self).to_css(dest)
}
}
impl ToCss for crate::owned_str::OwnedStr {
#[inline]
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
serialize_string(self, dest)
}
}
impl ToCss for str {
#[inline]
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
serialize_string(self, dest)
}
}
impl ToCss for String {
#[inline]
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
serialize_string(self, dest)
}
}
impl<T> ToCss for Option<T>
where
T: ToCss,
{
#[inline]
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
self.as_ref().map_or(Ok(()), |value| value.to_css(dest))
}
}
impl ToCss for () {
#[inline]
fn to_css<W>(&self, _: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
Ok(())
}
}
pub struct CssWriter<'w, W: 'w> {
inner: &'w mut W,
prefix: Option<&'static str>,
}
impl<'w, W> CssWriter<'w, W>
where
W: Write,
{
#[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() {
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() {
if !prefix.is_empty() {
self.inner.write_str(prefix)?;
}
}
self.inner.write_char(c)
}
}
#[cfg(feature = "gecko")]
pub type CssStringWriter = ::nsstring::nsACString;
#[cfg(feature = "gecko")]
pub type CssString = ::nsstring::nsCString;
#[cfg(feature = "servo")]
pub type CssStringWriter = String;
#[cfg(feature = "servo")]
pub type CssString = String;
pub struct SequenceWriter<'a, 'b: 'a, W: 'b> {
inner: &'a mut CssWriter<'b, W>,
separator: &'static str,
}
impl<'a, 'b, W> SequenceWriter<'a, 'b, W>
where
W: Write + 'b,
{
pub fn has_written(&self) -> bool {
self.inner.prefix.is_none()
}
#[inline]
pub fn new(inner: &'a mut CssWriter<'b, W>, separator: &'static str) -> Self {
if inner.prefix.is_none() {
inner.prefix = Some("");
}
Self { inner, separator }
}
#[inline]
pub fn write_item<F>(&mut self, f: F) -> fmt::Result
where
F: FnOnce(&mut CssWriter<'b, W>) -> fmt::Result,
{
fn before(
prefix: &mut Option<&'static str>,
separator: &'static str,
) -> Option<&'static str> {
let old_prefix = *prefix;
if old_prefix.is_none() {
*prefix = Some(separator);
}
old_prefix
}
fn after(
old_prefix: Option<&'static str>,
prefix: &mut Option<&'static str>,
separator: &'static str,
) {
match (old_prefix, *prefix) {
(_, None) => {
},
(None, Some(p)) => {
debug_assert_eq!(separator, p);
*prefix = None;
},
(Some(old), Some(new)) => {
debug_assert_eq!(old, new);
},
}
}
let old_prefix = before(&mut self.inner.prefix, self.separator);
f(self.inner)?;
after(old_prefix, &mut self.inner.prefix, self.separator);
Ok(())
}
#[inline]
pub fn item<T>(&mut self, item: &T) -> fmt::Result
where
T: ToCss,
{
self.write_item(|inner| item.to_css(inner))
}
#[inline]
pub fn raw_item(&mut self, item: &str) -> fmt::Result {
self.write_item(|inner| inner.write_str(item))
}
}
pub struct Comma;
pub struct Space;
pub struct CommaWithSpace;
pub trait Separator {
fn separator() -> &'static str;
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>>;
}
impl Separator for Comma {
fn separator() -> &'static str {
", "
}
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)
}
}
impl Separator for Space {
fn separator() -> &'static str {
" "
}
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>>,
{
input.skip_whitespace(); let mut results = vec![parse_one(input)?];
loop {
input.skip_whitespace(); if let Ok(item) = input.try_parse(&mut parse_one) {
results.push(item);
} else {
return Ok(results);
}
}
}
}
impl Separator for CommaWithSpace {
fn separator() -> &'static str {
", "
}
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>>,
{
input.skip_whitespace(); let mut results = vec![parse_one(input)?];
loop {
input.skip_whitespace(); let comma_location = input.current_source_location();
let comma = input.try_parse(|i| i.expect_comma()).is_ok();
input.skip_whitespace(); if let Ok(item) = input.try_parse(&mut parse_one) {
results.push(item);
} else if comma {
return Err(comma_location.new_unexpected_token_error(Token::Comma));
} else {
break;
}
}
Ok(results)
}
}
pub trait OneOrMoreSeparated {
type S: Separator;
}
impl OneOrMoreSeparated for UnicodeRange {
type S = Comma;
}
impl<T> ToCss for Vec<T>
where
T: ToCss + OneOrMoreSeparated,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
let mut iter = self.iter();
iter.next().unwrap().to_css(dest)?;
for item in iter {
dest.write_str(<T as OneOrMoreSeparated>::S::separator())?;
item.to_css(dest)?;
}
Ok(())
}
}
impl<T> ToCss for Box<T>
where
T: ?Sized + ToCss,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
(**self).to_css(dest)
}
}
impl<T> ToCss for Arc<T>
where
T: ?Sized + ToCss,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
(**self).to_css(dest)
}
}
impl ToCss for Au {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
self.to_f64_px().to_css(dest)?;
dest.write_str("px")
}
}
macro_rules! impl_to_css_for_predefined_type {
($name: ty) => {
impl<'a> ToCss for $name {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
::cssparser::ToCss::to_css(self, dest)
}
}
};
}
impl_to_css_for_predefined_type!(f32);
impl_to_css_for_predefined_type!(i8);
impl_to_css_for_predefined_type!(i32);
impl_to_css_for_predefined_type!(u8);
impl_to_css_for_predefined_type!(u16);
impl_to_css_for_predefined_type!(u32);
impl_to_css_for_predefined_type!(::cssparser::Token<'a>);
impl_to_css_for_predefined_type!(::cssparser::UnicodeRange);
pub mod specified {
use crate::ParsingMode;
#[repr(u8)]
#[derive(
Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, PartialOrd, Serialize, ToShmem,
)]
pub enum AllowedNumericType {
All,
NonNegative,
AtLeastOne,
ZeroToOne,
}
impl Default for AllowedNumericType {
#[inline]
fn default() -> Self {
AllowedNumericType::All
}
}
impl AllowedNumericType {
#[inline]
pub fn is_ok(&self, parsing_mode: ParsingMode, val: f32) -> bool {
if parsing_mode.allows_all_numeric_values() {
return true;
}
match *self {
AllowedNumericType::All => true,
AllowedNumericType::NonNegative => val >= 0.0,
AllowedNumericType::AtLeastOne => val >= 1.0,
AllowedNumericType::ZeroToOne => val >= 0.0 && val <= 1.0,
}
}
#[inline]
pub fn clamp(&self, val: f32) -> f32 {
match *self {
AllowedNumericType::All => val,
AllowedNumericType::NonNegative => val.max(0.),
AllowedNumericType::AtLeastOne => val.max(1.),
AllowedNumericType::ZeroToOne => val.max(0.).min(1.),
}
}
}
}
#[derive(Clone, Debug)]
#[repr(C)]
pub enum NumericValue {
Unit {
value: f32,
unit: CssString,
},
Sum {
values: ThinVec<NumericValue>,
},
}
#[derive(Clone, Debug)]
#[repr(C)]
pub enum TypedValue {
Keyword(CssString),
Numeric(NumericValue),
}
pub trait ToTyped {
fn to_typed(&self) -> Option<TypedValue> {
None
}
}
impl<T> ToTyped for Box<T>
where
T: ?Sized + ToTyped,
{
fn to_typed(&self) -> Option<TypedValue> {
(**self).to_typed()
}
}
impl ToTyped for Au {
fn to_typed(&self) -> Option<TypedValue> {
let value = self.to_f32_px();
let unit = CssString::from("px");
Some(TypedValue::Numeric(NumericValue::Unit { value, unit }))
}
}
macro_rules! impl_to_typed_for_predefined_type {
($name: ty) => {
impl<'a> ToTyped for $name {
fn to_typed(&self) -> Option<TypedValue> {
None
}
}
};
}
impl_to_typed_for_predefined_type!(f32);
impl_to_typed_for_predefined_type!(i8);
impl_to_typed_for_predefined_type!(i32);