mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
7138 lines
284 KiB
Rust
7138 lines
284 KiB
Rust
/* 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/. */
|
||
|
||
// This file is a Mako template: http://www.makotemplates.org/
|
||
|
||
// Please note that valid Rust syntax may be mangled by the Mako parser.
|
||
// For example, Vec<&Foo> will be mangled as Vec&Foo>. To work around these issues, the code
|
||
// can be escaped. In the above example, Vec<<&Foo> achieves the desired result of Vec<&Foo>.
|
||
|
||
use std::ascii::AsciiExt;
|
||
use std::collections::HashSet;
|
||
use std::fmt;
|
||
use std::intrinsics;
|
||
use std::mem;
|
||
use std::sync::Arc;
|
||
|
||
use app_units::Au;
|
||
use cssparser::{Parser, Color, RGBA, AtRuleParser, DeclarationParser, Delimiter,
|
||
DeclarationListParser, parse_important, ToCss, TokenSerializationType};
|
||
use error_reporting::ParseErrorReporter;
|
||
use url::Url;
|
||
use euclid::SideOffsets2D;
|
||
use euclid::size::Size2D;
|
||
use string_cache::Atom;
|
||
use computed_values;
|
||
use logical_geometry::{LogicalMargin, PhysicalSide, WritingMode};
|
||
use parser::{ParserContext, log_css_error};
|
||
use selectors::matching::DeclarationBlock;
|
||
use stylesheets::Origin;
|
||
use values::AuExtensionMethods;
|
||
use values::computed::{self, TContext, ToComputedValue};
|
||
use values::specified::BorderStyle;
|
||
|
||
use self::property_bit_field::PropertyBitField;
|
||
|
||
<%!
|
||
|
||
import re
|
||
|
||
def to_rust_ident(name):
|
||
name = name.replace("-", "_")
|
||
if name in ["static", "super", "box", "move"]: # Rust keywords
|
||
name += "_"
|
||
return name
|
||
|
||
def to_camel_case(ident):
|
||
return re.sub("_([a-z])", lambda m: m.group(1).upper(), ident.strip("_").capitalize())
|
||
|
||
class Longhand(object):
|
||
def __init__(self, name, derived_from=None, custom_cascade=False, experimental=False,
|
||
internal=False):
|
||
self.name = name
|
||
self.ident = to_rust_ident(name)
|
||
self.camel_case = to_camel_case(self.ident)
|
||
self.style_struct = THIS_STYLE_STRUCT
|
||
self.experimental = ("layout.%s.enabled" % name) if experimental else None
|
||
self.custom_cascade = custom_cascade
|
||
self.internal = internal
|
||
if derived_from is None:
|
||
self.derived_from = None
|
||
else:
|
||
self.derived_from = [ to_rust_ident(name) for name in derived_from ]
|
||
|
||
class Shorthand(object):
|
||
def __init__(self, name, sub_properties, experimental=False, internal=False):
|
||
self.name = name
|
||
self.ident = to_rust_ident(name)
|
||
self.camel_case = to_camel_case(self.ident)
|
||
self.derived_from = None
|
||
self.experimental = ("layout.%s.enabled" % name) if experimental else None
|
||
self.sub_properties = [LONGHANDS_BY_NAME[s] for s in sub_properties]
|
||
self.internal = internal
|
||
|
||
class StyleStruct(object):
|
||
def __init__(self, name, inherited):
|
||
self.name = name
|
||
self.ident = to_rust_ident(name.lower())
|
||
self.longhands = []
|
||
self.inherited = inherited
|
||
|
||
STYLE_STRUCTS = []
|
||
THIS_STYLE_STRUCT = None
|
||
LONGHANDS = []
|
||
LONGHANDS_BY_NAME = {}
|
||
DERIVED_LONGHANDS = {}
|
||
SHORTHANDS = []
|
||
|
||
def new_style_struct(name, is_inherited):
|
||
global THIS_STYLE_STRUCT
|
||
|
||
style_struct = StyleStruct(name, is_inherited)
|
||
STYLE_STRUCTS.append(style_struct)
|
||
THIS_STYLE_STRUCT = style_struct
|
||
return ""
|
||
|
||
def switch_to_style_struct(name):
|
||
global THIS_STYLE_STRUCT
|
||
|
||
for style_struct in STYLE_STRUCTS:
|
||
if style_struct.name == name:
|
||
THIS_STYLE_STRUCT = style_struct
|
||
return ""
|
||
fail()
|
||
%>
|
||
|
||
pub mod longhands {
|
||
use cssparser::Parser;
|
||
use parser::ParserContext;
|
||
use values::specified;
|
||
|
||
<%def name="raw_longhand(name, derived_from=None, custom_cascade=False, experimental=False,
|
||
internal=False)">
|
||
<%
|
||
if derived_from is not None:
|
||
derived_from = derived_from.split()
|
||
|
||
property = Longhand(name,
|
||
derived_from=derived_from,
|
||
custom_cascade=custom_cascade,
|
||
experimental=experimental,
|
||
internal=internal)
|
||
property.style_struct = THIS_STYLE_STRUCT
|
||
THIS_STYLE_STRUCT.longhands.append(property)
|
||
LONGHANDS.append(property)
|
||
LONGHANDS_BY_NAME[name] = property
|
||
|
||
if derived_from is not None:
|
||
for name in derived_from:
|
||
DERIVED_LONGHANDS.setdefault(name, []).append(property)
|
||
%>
|
||
pub mod ${property.ident} {
|
||
#![allow(unused_imports)]
|
||
% if derived_from is None:
|
||
use cssparser::Parser;
|
||
use parser::ParserContext;
|
||
use properties::{CSSWideKeyword, DeclaredValue, Shorthand};
|
||
% endif
|
||
use error_reporting::ParseErrorReporter;
|
||
use properties::longhands;
|
||
use properties::property_bit_field::PropertyBitField;
|
||
use properties::{ComputedValues, PropertyDeclaration, TComputedValues};
|
||
use properties::style_struct_traits::T${THIS_STYLE_STRUCT.name};
|
||
use properties::style_structs;
|
||
use std::collections::HashMap;
|
||
use std::sync::Arc;
|
||
use values::computed::{TContext, ToComputedValue};
|
||
use values::{computed, specified};
|
||
use string_cache::Atom;
|
||
${caller.body()}
|
||
#[allow(unused_variables)]
|
||
pub fn cascade_property<C: TComputedValues>(
|
||
declaration: &PropertyDeclaration,
|
||
inherited_style: &C,
|
||
context: &mut computed::Context<C>,
|
||
seen: &mut PropertyBitField,
|
||
cacheable: &mut bool,
|
||
error_reporter: &mut Box<ParseErrorReporter + Send>) {
|
||
let declared_value = match *declaration {
|
||
PropertyDeclaration::${property.camel_case}(ref declared_value) => {
|
||
declared_value
|
||
}
|
||
_ => panic!("entered the wrong cascade_property() implementation"),
|
||
};
|
||
% if property.derived_from is None:
|
||
if seen.get_${property.ident}() {
|
||
return
|
||
}
|
||
seen.set_${property.ident}();
|
||
{
|
||
let custom_props = context.style().custom_properties();
|
||
::properties::substitute_variables_${property.ident}(
|
||
declared_value, &custom_props, |value| match *value {
|
||
DeclaredValue::Value(ref specified_value) => {
|
||
let computed = specified_value.to_computed_value(context);
|
||
context.mutate_style().mutate_${THIS_STYLE_STRUCT.name.lower()}()
|
||
.set_${property.ident}(computed);
|
||
}
|
||
DeclaredValue::WithVariables { .. } => unreachable!(),
|
||
DeclaredValue::Initial => {
|
||
// We assume that it's faster to use copy_*_from rather than
|
||
// set_*(get_initial_value());
|
||
let initial_struct = C::initial_values().get_${THIS_STYLE_STRUCT.name.lower()}();
|
||
context.mutate_style().mutate_${THIS_STYLE_STRUCT.name.lower()}()
|
||
.copy_${property.ident}_from(initial_struct);
|
||
},
|
||
DeclaredValue::Inherit => {
|
||
// This is a bit slow, but this is rare so it shouldn't
|
||
// matter.
|
||
//
|
||
// FIXME: is it still?
|
||
*cacheable = false;
|
||
let inherited_struct = inherited_style.get_${THIS_STYLE_STRUCT.name.lower()}();
|
||
context.mutate_style().mutate_${THIS_STYLE_STRUCT.name.lower()}()
|
||
.copy_${property.ident}_from(inherited_struct);
|
||
}
|
||
}, error_reporter
|
||
);
|
||
}
|
||
|
||
% if custom_cascade:
|
||
cascade_property_custom(declaration,
|
||
inherited_style,
|
||
context,
|
||
seen,
|
||
cacheable,
|
||
error_reporter);
|
||
% endif
|
||
% else:
|
||
// Do not allow stylesheets to set derived properties.
|
||
% endif
|
||
}
|
||
% if derived_from is None:
|
||
pub fn parse_declared(context: &ParserContext, input: &mut Parser)
|
||
-> Result<DeclaredValue<SpecifiedValue>, ()> {
|
||
match input.try(CSSWideKeyword::parse) {
|
||
Ok(CSSWideKeyword::InheritKeyword) => Ok(DeclaredValue::Inherit),
|
||
Ok(CSSWideKeyword::InitialKeyword) => Ok(DeclaredValue::Initial),
|
||
Ok(CSSWideKeyword::UnsetKeyword) => Ok(DeclaredValue::${
|
||
"Inherit" if THIS_STYLE_STRUCT.inherited else "Initial"}),
|
||
Err(()) => {
|
||
input.look_for_var_functions();
|
||
let start = input.position();
|
||
let specified = parse_specified(context, input);
|
||
if specified.is_err() {
|
||
while let Ok(_) = input.next() {} // Look for var() after the error.
|
||
}
|
||
let var = input.seen_var_functions();
|
||
if specified.is_err() && var {
|
||
input.reset(start);
|
||
let (first_token_type, css) = try!(
|
||
::custom_properties::parse_non_custom_with_var(input));
|
||
return Ok(DeclaredValue::WithVariables {
|
||
css: css.into_owned(),
|
||
first_token_type: first_token_type,
|
||
base_url: context.base_url.clone(),
|
||
from_shorthand: None,
|
||
})
|
||
}
|
||
specified
|
||
}
|
||
}
|
||
}
|
||
% endif
|
||
}
|
||
</%def>
|
||
|
||
<%def name="longhand(name, derived_from=None, custom_cascade=False, experimental=False,
|
||
internal=False)">
|
||
<%self:raw_longhand name="${name}" derived_from="${derived_from}"
|
||
custom_cascade="${custom_cascade}" experimental="${experimental}"
|
||
internal="${internal}">
|
||
${caller.body()}
|
||
% if derived_from is None:
|
||
pub fn parse_specified(context: &ParserContext, input: &mut Parser)
|
||
-> Result<DeclaredValue<SpecifiedValue>, ()> {
|
||
parse(context, input).map(DeclaredValue::Value)
|
||
}
|
||
% endif
|
||
</%self:raw_longhand>
|
||
</%def>
|
||
|
||
<%def name="single_keyword_computed(name, values, custom_cascade=False, experimental=False,
|
||
internal=False)">
|
||
<%self:longhand name="${name}" custom_cascade="${custom_cascade}"
|
||
experimental="${experimental}" internal="${internal}">
|
||
pub use self::computed_value::T as SpecifiedValue;
|
||
${caller.body()}
|
||
pub mod computed_value {
|
||
define_css_keyword_enum! { T:
|
||
% for value in values.split():
|
||
"${value}" => ${to_rust_ident(value)},
|
||
% endfor
|
||
}
|
||
}
|
||
#[inline] pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T::${to_rust_ident(values.split()[0])}
|
||
}
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser)
|
||
-> Result<SpecifiedValue, ()> {
|
||
computed_value::T::parse(input)
|
||
}
|
||
</%self:longhand>
|
||
</%def>
|
||
|
||
<%def name="single_keyword(name, values, experimental=False, internal=False)">
|
||
<%self:single_keyword_computed name="${name}"
|
||
values="${values}"
|
||
experimental="${experimental}"
|
||
internal="${internal}">
|
||
use values::computed::ComputedValueAsSpecified;
|
||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||
</%self:single_keyword_computed>
|
||
</%def>
|
||
|
||
<%def name="predefined_type(name, type, initial_value, parse_method='parse')">
|
||
<%self:longhand name="${name}">
|
||
#[allow(unused_imports)]
|
||
use app_units::Au;
|
||
pub type SpecifiedValue = specified::${type};
|
||
pub mod computed_value {
|
||
pub use values::computed::${type} as T;
|
||
}
|
||
#[inline] pub fn get_initial_value() -> computed_value::T { ${initial_value} }
|
||
#[inline] pub fn parse(_context: &ParserContext, input: &mut Parser)
|
||
-> Result<SpecifiedValue, ()> {
|
||
specified::${type}::${parse_method}(input)
|
||
}
|
||
</%self:longhand>
|
||
</%def>
|
||
|
||
|
||
// CSS 2.1, Section 8 - Box model
|
||
|
||
${new_style_struct("Margin", is_inherited=False)}
|
||
|
||
% for side in ["top", "right", "bottom", "left"]:
|
||
${predefined_type("margin-" + side, "LengthOrPercentageOrAuto",
|
||
"computed::LengthOrPercentageOrAuto::Length(Au(0))")}
|
||
% endfor
|
||
|
||
${new_style_struct("Padding", is_inherited=False)}
|
||
|
||
% for side in ["top", "right", "bottom", "left"]:
|
||
${predefined_type("padding-" + side, "LengthOrPercentage",
|
||
"computed::LengthOrPercentage::Length(Au(0))",
|
||
"parse_non_negative")}
|
||
% endfor
|
||
|
||
${new_style_struct("Border", is_inherited=False)}
|
||
|
||
% for side in ["top", "right", "bottom", "left"]:
|
||
${predefined_type("border-%s-color" % side, "CSSColor", "::cssparser::Color::CurrentColor")}
|
||
% endfor
|
||
|
||
% for side in ["top", "right", "bottom", "left"]:
|
||
${predefined_type("border-%s-style" % side, "BorderStyle", "specified::BorderStyle::none")}
|
||
% endfor
|
||
|
||
% for side in ["top", "right", "bottom", "left"]:
|
||
<%self:longhand name="border-${side}-width">
|
||
use app_units::Au;
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
self.0.to_css(dest)
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser)
|
||
-> Result<SpecifiedValue, ()> {
|
||
specified::parse_border_width(input).map(SpecifiedValue)
|
||
}
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct SpecifiedValue(pub specified::Length);
|
||
pub mod computed_value {
|
||
use app_units::Au;
|
||
pub type T = Au;
|
||
}
|
||
#[inline] pub fn get_initial_value() -> computed_value::T {
|
||
Au::from_px(3) // medium
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
self.0.to_computed_value(context)
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
% endfor
|
||
|
||
// FIXME(#4126): when gfx supports painting it, make this Size2D<LengthOrPercentage>
|
||
% for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]:
|
||
${predefined_type("border-" + corner + "-radius", "BorderRadiusSize",
|
||
"computed::BorderRadiusSize::zero()",
|
||
"parse")}
|
||
% endfor
|
||
|
||
${new_style_struct("Outline", is_inherited=False)}
|
||
|
||
// TODO(pcwalton): `invert`
|
||
${predefined_type("outline-color", "CSSColor", "::cssparser::Color::CurrentColor")}
|
||
|
||
<%self:longhand name="outline-style">
|
||
pub use values::specified::BorderStyle as SpecifiedValue;
|
||
pub fn get_initial_value() -> SpecifiedValue { SpecifiedValue::none }
|
||
pub mod computed_value {
|
||
pub use values::specified::BorderStyle as T;
|
||
}
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
match SpecifiedValue::parse(input) {
|
||
Ok(SpecifiedValue::hidden) => Err(()),
|
||
result => result
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
<%self:longhand name="outline-width">
|
||
use app_units::Au;
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
use values::AuExtensionMethods;
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
self.0.to_css(dest)
|
||
}
|
||
}
|
||
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
specified::parse_border_width(input).map(SpecifiedValue)
|
||
}
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct SpecifiedValue(pub specified::Length);
|
||
pub mod computed_value {
|
||
use app_units::Au;
|
||
pub type T = Au;
|
||
}
|
||
pub use super::border_top_width::get_initial_value;
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
self.0.to_computed_value(context)
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
${predefined_type("outline-offset", "Length", "Au(0)")}
|
||
|
||
${new_style_struct("PositionOffsets", is_inherited=False)}
|
||
|
||
% for side in ["top", "right", "bottom", "left"]:
|
||
${predefined_type(side, "LengthOrPercentageOrAuto",
|
||
"computed::LengthOrPercentageOrAuto::Auto")}
|
||
% endfor
|
||
|
||
// CSS 2.1, Section 9 - Visual formatting model
|
||
|
||
${new_style_struct("Box", is_inherited=False)}
|
||
|
||
// TODO(SimonSapin): don't parse `inline-table`, since we don't support it
|
||
<%self:longhand name="display" custom_cascade="True">
|
||
<%
|
||
values = """inline block inline-block
|
||
table inline-table table-row-group table-header-group table-footer-group
|
||
table-row table-column-group table-column table-cell table-caption
|
||
list-item flex
|
||
none
|
||
""".split()
|
||
experimental_values = set("flex".split())
|
||
%>
|
||
pub use self::computed_value::T as SpecifiedValue;
|
||
use values::computed::{Context, ComputedValueAsSpecified};
|
||
use properties::style_struct_traits::TInheritedText;
|
||
|
||
pub mod computed_value {
|
||
#[allow(non_camel_case_types)]
|
||
#[derive(Clone, Eq, PartialEq, Copy, Hash, RustcEncodable, Debug, HeapSizeOf)]
|
||
#[derive(Deserialize, Serialize)]
|
||
pub enum T {
|
||
% for value in values:
|
||
${to_rust_ident(value)},
|
||
% endfor
|
||
}
|
||
|
||
impl ::cssparser::ToCss for T {
|
||
fn to_css<W>(&self, dest: &mut W) -> ::std::fmt::Result
|
||
where W: ::std::fmt::Write {
|
||
match *self {
|
||
% for value in values:
|
||
T::${to_rust_ident(value)} => dest.write_str("${value}"),
|
||
% endfor
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#[inline] pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T::${to_rust_ident(values[0])}
|
||
}
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser)
|
||
-> Result<SpecifiedValue, ()> {
|
||
match_ignore_ascii_case! { try!(input.expect_ident()),
|
||
% for value in values:
|
||
"${value}" => {
|
||
% if value in experimental_values:
|
||
if !::util::prefs::get_pref("layout.${value}.enabled")
|
||
.as_boolean().unwrap_or(false) {
|
||
return Err(())
|
||
}
|
||
% endif
|
||
Ok(computed_value::T::${to_rust_ident(value)})
|
||
},
|
||
% endfor
|
||
_ => Err(())
|
||
}
|
||
}
|
||
|
||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||
|
||
fn cascade_property_custom<C: TComputedValues>(
|
||
_declaration: &PropertyDeclaration,
|
||
_inherited_style: &C,
|
||
context: &mut computed::Context<C>,
|
||
_seen: &mut PropertyBitField,
|
||
_cacheable: &mut bool,
|
||
_error_reporter: &mut Box<ParseErrorReporter + Send>) {
|
||
longhands::_servo_display_for_hypothetical_box::derive_from_display(context);
|
||
longhands::_servo_text_decorations_in_effect::derive_from_display(context);
|
||
}
|
||
</%self:longhand>
|
||
|
||
${single_keyword("position", "static absolute relative fixed")}
|
||
|
||
<%self:single_keyword_computed name="float" values="none left right">
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
let positioned = matches!(context.style().get_box().clone_position(),
|
||
longhands::position::SpecifiedValue::absolute |
|
||
longhands::position::SpecifiedValue::fixed);
|
||
if positioned {
|
||
SpecifiedValue::none
|
||
} else {
|
||
*self
|
||
}
|
||
}
|
||
}
|
||
|
||
</%self:single_keyword_computed>
|
||
|
||
${single_keyword("clear", "none left right both")}
|
||
|
||
<%self:longhand name="-servo-display-for-hypothetical-box" derived_from="display">
|
||
pub use super::display::{SpecifiedValue, get_initial_value};
|
||
pub use super::display::{parse};
|
||
|
||
pub mod computed_value {
|
||
pub type T = super::SpecifiedValue;
|
||
}
|
||
|
||
#[inline]
|
||
pub fn derive_from_display<Cx: TContext>(context: &mut Cx) {
|
||
let d = context.style().get_box().clone_display();
|
||
context.mutate_style().mutate_box().set__servo_display_for_hypothetical_box(d);
|
||
}
|
||
|
||
</%self:longhand>
|
||
|
||
<%self:longhand name="z-index">
|
||
use values::computed::ComputedValueAsSpecified;
|
||
|
||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||
pub type SpecifiedValue = computed_value::T;
|
||
pub mod computed_value {
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
|
||
#[derive(PartialEq, Clone, Eq, Copy, Debug, HeapSizeOf)]
|
||
pub enum T {
|
||
Auto,
|
||
Number(i32),
|
||
}
|
||
|
||
impl ToCss for T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
T::Auto => dest.write_str("auto"),
|
||
T::Number(number) => write!(dest, "{}", number),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl T {
|
||
pub fn number_or_zero(self) -> i32 {
|
||
match self {
|
||
T::Auto => 0,
|
||
T::Number(value) => value,
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T::Auto
|
||
}
|
||
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
|
||
Ok(computed_value::T::Auto)
|
||
} else {
|
||
specified::parse_integer(input).map(computed_value::T::Number)
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
${new_style_struct("InheritedBox", is_inherited=True)}
|
||
|
||
${single_keyword("direction", "ltr rtl")}
|
||
|
||
// CSS 2.1, Section 10 - Visual formatting model details
|
||
|
||
${switch_to_style_struct("Box")}
|
||
|
||
${predefined_type("width", "LengthOrPercentageOrAuto",
|
||
"computed::LengthOrPercentageOrAuto::Auto",
|
||
"parse_non_negative")}
|
||
|
||
${predefined_type("height", "LengthOrPercentageOrAuto",
|
||
"computed::LengthOrPercentageOrAuto::Auto",
|
||
"parse_non_negative")}
|
||
|
||
${predefined_type("min-width", "LengthOrPercentage",
|
||
"computed::LengthOrPercentage::Length(Au(0))",
|
||
"parse_non_negative")}
|
||
${predefined_type("max-width", "LengthOrPercentageOrNone",
|
||
"computed::LengthOrPercentageOrNone::None",
|
||
"parse_non_negative")}
|
||
|
||
${predefined_type("min-height", "LengthOrPercentage",
|
||
"computed::LengthOrPercentage::Length(Au(0))",
|
||
"parse_non_negative")}
|
||
${predefined_type("max-height", "LengthOrPercentageOrNone",
|
||
"computed::LengthOrPercentageOrNone::None",
|
||
"parse_non_negative")}
|
||
|
||
${switch_to_style_struct("InheritedBox")}
|
||
|
||
<%self:longhand name="line-height">
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
use values::AuExtensionMethods;
|
||
use values::CSSFloat;
|
||
|
||
#[derive(Debug, Clone, PartialEq, Copy, HeapSizeOf)]
|
||
pub enum SpecifiedValue {
|
||
Normal,
|
||
Number(CSSFloat),
|
||
LengthOrPercentage(specified::LengthOrPercentage),
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
SpecifiedValue::Normal => dest.write_str("normal"),
|
||
SpecifiedValue::LengthOrPercentage(value) => value.to_css(dest),
|
||
SpecifiedValue::Number(number) => write!(dest, "{}", number),
|
||
}
|
||
}
|
||
}
|
||
/// normal | <number> | <length> | <percentage>
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
use cssparser::Token;
|
||
use std::ascii::AsciiExt;
|
||
input.try(specified::LengthOrPercentage::parse_non_negative)
|
||
.map(SpecifiedValue::LengthOrPercentage)
|
||
.or_else(|()| {
|
||
match try!(input.next()) {
|
||
Token::Number(ref value) if value.value >= 0. => {
|
||
Ok(SpecifiedValue::Number(value.value))
|
||
}
|
||
Token::Ident(ref value) if value.eq_ignore_ascii_case("normal") => {
|
||
Ok(SpecifiedValue::Normal)
|
||
}
|
||
_ => Err(()),
|
||
}
|
||
})
|
||
}
|
||
pub mod computed_value {
|
||
use app_units::Au;
|
||
use std::fmt;
|
||
use values::CSSFloat;
|
||
#[derive(PartialEq, Copy, Clone, HeapSizeOf, Debug)]
|
||
pub enum T {
|
||
Normal,
|
||
Length(Au),
|
||
Number(CSSFloat),
|
||
}
|
||
}
|
||
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::Normal => dest.write_str("normal"),
|
||
computed_value::T::Length(length) => length.to_css(dest),
|
||
computed_value::T::Number(number) => write!(dest, "{}", number),
|
||
}
|
||
}
|
||
}
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T { computed_value::T::Normal }
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
match *self {
|
||
SpecifiedValue::Normal => computed_value::T::Normal,
|
||
SpecifiedValue::Number(value) => computed_value::T::Number(value),
|
||
SpecifiedValue::LengthOrPercentage(value) => {
|
||
match value {
|
||
specified::LengthOrPercentage::Length(value) =>
|
||
computed_value::T::Length(value.to_computed_value(context)),
|
||
specified::LengthOrPercentage::Percentage(specified::Percentage(value)) => {
|
||
let fr = specified::Length::FontRelative(specified::FontRelativeLength::Em(value));
|
||
computed_value::T::Length(fr.to_computed_value(context))
|
||
},
|
||
specified::LengthOrPercentage::Calc(calc) => {
|
||
let calc = calc.to_computed_value(context);
|
||
let fr = specified::FontRelativeLength::Em(calc.percentage());
|
||
let fr = specified::Length::FontRelative(fr);
|
||
computed_value::T::Length(calc.length() + fr.to_computed_value(context))
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
${switch_to_style_struct("Box")}
|
||
|
||
<%self:longhand name="vertical-align">
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
|
||
<% vertical_align_keywords = (
|
||
"baseline sub super top text-top middle bottom text-bottom".split()) %>
|
||
#[allow(non_camel_case_types)]
|
||
#[derive(Debug, Clone, PartialEq, Copy, HeapSizeOf)]
|
||
pub enum SpecifiedValue {
|
||
% for keyword in vertical_align_keywords:
|
||
${to_rust_ident(keyword)},
|
||
% endfor
|
||
LengthOrPercentage(specified::LengthOrPercentage),
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
% for keyword in vertical_align_keywords:
|
||
SpecifiedValue::${to_rust_ident(keyword)} => dest.write_str("${keyword}"),
|
||
% endfor
|
||
SpecifiedValue::LengthOrPercentage(value) => value.to_css(dest),
|
||
}
|
||
}
|
||
}
|
||
/// baseline | sub | super | top | text-top | middle | bottom | text-bottom
|
||
/// | <percentage> | <length>
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
input.try(specified::LengthOrPercentage::parse)
|
||
.map(SpecifiedValue::LengthOrPercentage)
|
||
.or_else(|()| {
|
||
match_ignore_ascii_case! { try!(input.expect_ident()),
|
||
% for keyword in vertical_align_keywords:
|
||
"${keyword}" => Ok(SpecifiedValue::${to_rust_ident(keyword)}),
|
||
% endfor
|
||
_ => Err(())
|
||
}
|
||
})
|
||
}
|
||
pub mod computed_value {
|
||
use app_units::Au;
|
||
use std::fmt;
|
||
use values::AuExtensionMethods;
|
||
use values::{CSSFloat, computed};
|
||
#[allow(non_camel_case_types)]
|
||
#[derive(PartialEq, Copy, Clone, HeapSizeOf, Debug)]
|
||
pub enum T {
|
||
% for keyword in vertical_align_keywords:
|
||
${to_rust_ident(keyword)},
|
||
% endfor
|
||
LengthOrPercentage(computed::LengthOrPercentage),
|
||
}
|
||
impl ::cssparser::ToCss for T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
% for keyword in vertical_align_keywords:
|
||
T::${to_rust_ident(keyword)} => dest.write_str("${keyword}"),
|
||
% endfor
|
||
T::LengthOrPercentage(value) => value.to_css(dest),
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T { computed_value::T::baseline }
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
match *self {
|
||
% for keyword in vertical_align_keywords:
|
||
SpecifiedValue::${to_rust_ident(keyword)} => {
|
||
computed_value::T::${to_rust_ident(keyword)}
|
||
}
|
||
% endfor
|
||
SpecifiedValue::LengthOrPercentage(value) =>
|
||
computed_value::T::LengthOrPercentage(value.to_computed_value(context)),
|
||
}
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
|
||
// CSS 2.1, Section 11 - Visual effects
|
||
|
||
// Non-standard, see https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box#Specifications
|
||
${single_keyword("-servo-overflow-clip-box", "padding-box content-box", internal=True)}
|
||
|
||
// FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`.
|
||
${single_keyword("overflow-x", "visible hidden scroll auto")}
|
||
|
||
// FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`.
|
||
<%self:longhand name="overflow-y">
|
||
use super::overflow_x;
|
||
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
|
||
pub use self::computed_value::T as SpecifiedValue;
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
self.0.to_css(dest)
|
||
}
|
||
}
|
||
|
||
pub mod computed_value {
|
||
#[derive(Debug, Clone, Copy, PartialEq, HeapSizeOf)]
|
||
pub struct T(pub super::super::overflow_x::computed_value::T);
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
computed_value::T(self.0.to_computed_value(context))
|
||
}
|
||
}
|
||
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T(overflow_x::get_initial_value())
|
||
}
|
||
|
||
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||
overflow_x::parse(context, input).map(SpecifiedValue)
|
||
}
|
||
</%self:longhand>
|
||
|
||
${switch_to_style_struct("InheritedBox")}
|
||
|
||
// TODO: collapse. Well, do tables first.
|
||
${single_keyword("visibility", "visible hidden")}
|
||
|
||
// CSS 2.1, Section 12 - Generated content, automatic numbering, and lists
|
||
|
||
${switch_to_style_struct("Box")}
|
||
|
||
<%self:longhand name="content">
|
||
use cssparser::Token;
|
||
use std::ascii::AsciiExt;
|
||
use values::computed::ComputedValueAsSpecified;
|
||
|
||
use super::list_style_type;
|
||
|
||
pub use self::computed_value::T as SpecifiedValue;
|
||
pub use self::computed_value::ContentItem;
|
||
|
||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||
|
||
pub mod computed_value {
|
||
use super::super::list_style_type;
|
||
|
||
use cssparser::{self, ToCss};
|
||
use std::fmt;
|
||
|
||
#[derive(Debug, PartialEq, Eq, Clone, HeapSizeOf)]
|
||
pub enum ContentItem {
|
||
/// Literal string content.
|
||
String(String),
|
||
/// `counter(name, style)`.
|
||
Counter(String, list_style_type::computed_value::T),
|
||
/// `counters(name, separator, style)`.
|
||
Counters(String, String, list_style_type::computed_value::T),
|
||
/// `open-quote`.
|
||
OpenQuote,
|
||
/// `close-quote`.
|
||
CloseQuote,
|
||
/// `no-open-quote`.
|
||
NoOpenQuote,
|
||
/// `no-close-quote`.
|
||
NoCloseQuote,
|
||
}
|
||
|
||
impl ToCss for ContentItem {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
ContentItem::String(ref s) => {
|
||
cssparser::serialize_string(&**s, dest)
|
||
}
|
||
ContentItem::Counter(ref s, ref list_style_type) => {
|
||
try!(dest.write_str("counter("));
|
||
try!(cssparser::serialize_identifier(&**s, dest));
|
||
try!(dest.write_str(", "));
|
||
try!(list_style_type.to_css(dest));
|
||
dest.write_str(")")
|
||
}
|
||
ContentItem::Counters(ref s, ref separator, ref list_style_type) => {
|
||
try!(dest.write_str("counter("));
|
||
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));
|
||
dest.write_str(")")
|
||
}
|
||
ContentItem::OpenQuote => dest.write_str("open-quote"),
|
||
ContentItem::CloseQuote => dest.write_str("close-quote"),
|
||
ContentItem::NoOpenQuote => dest.write_str("no-open-quote"),
|
||
ContentItem::NoCloseQuote => dest.write_str("no-close-quote"),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[allow(non_camel_case_types)]
|
||
#[derive(Debug, PartialEq, Eq, Clone, HeapSizeOf)]
|
||
pub enum T {
|
||
normal,
|
||
none,
|
||
Content(Vec<ContentItem>),
|
||
}
|
||
|
||
impl ToCss for T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
T::normal => dest.write_str("normal"),
|
||
T::none => dest.write_str("none"),
|
||
T::Content(ref content) => {
|
||
let mut iter = content.iter();
|
||
try!(iter.next().unwrap().to_css(dest));
|
||
for c in iter {
|
||
try!(dest.write_str(" "));
|
||
try!(c.to_css(dest));
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T::normal
|
||
}
|
||
|
||
pub fn counter_name_is_illegal(name: &str) -> bool {
|
||
name.eq_ignore_ascii_case("none") || name.eq_ignore_ascii_case("inherit") ||
|
||
name.eq_ignore_ascii_case("initial")
|
||
}
|
||
|
||
// normal | none | [ <string> | <counter> | open-quote | close-quote | no-open-quote |
|
||
// no-close-quote ]+
|
||
// TODO: <uri>, attr(<identifier>)
|
||
pub fn parse(context: &ParserContext, input: &mut Parser)
|
||
-> Result<SpecifiedValue, ()> {
|
||
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
|
||
return Ok(SpecifiedValue::normal)
|
||
}
|
||
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||
return Ok(SpecifiedValue::none)
|
||
}
|
||
let mut content = vec![];
|
||
loop {
|
||
match input.next() {
|
||
Ok(Token::QuotedString(value)) => {
|
||
content.push(ContentItem::String(value.into_owned()))
|
||
}
|
||
Ok(Token::Function(name)) => {
|
||
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);
|
||
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);
|
||
Ok(ContentItem::Counters(name, separator, style))
|
||
}),
|
||
_ => return Err(())
|
||
}));
|
||
}
|
||
Ok(Token::Ident(ident)) => {
|
||
match_ignore_ascii_case! { ident,
|
||
"open-quote" => content.push(ContentItem::OpenQuote),
|
||
"close-quote" => content.push(ContentItem::CloseQuote),
|
||
"no-open-quote" => content.push(ContentItem::NoOpenQuote),
|
||
"no-close-quote" => content.push(ContentItem::NoCloseQuote),
|
||
_ => return Err(())
|
||
}
|
||
}
|
||
Err(_) => break,
|
||
_ => return Err(())
|
||
}
|
||
}
|
||
if !content.is_empty() {
|
||
Ok(SpecifiedValue::Content(content))
|
||
} else {
|
||
Err(())
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
${new_style_struct("List", is_inherited=True)}
|
||
|
||
${single_keyword("list-style-position", "outside inside")}
|
||
|
||
// TODO(pcwalton): Implement the full set of counter styles per CSS-COUNTER-STYLES [1] 6.1:
|
||
//
|
||
// decimal-leading-zero, armenian, upper-armenian, lower-armenian, georgian, lower-roman,
|
||
// upper-roman
|
||
//
|
||
// [1]: http://dev.w3.org/csswg/css-counter-styles/
|
||
${single_keyword("list-style-type", """
|
||
disc none circle square decimal arabic-indic bengali cambodian cjk-decimal devanagari
|
||
gujarati gurmukhi kannada khmer lao malayalam mongolian myanmar oriya persian telugu thai
|
||
tibetan lower-alpha upper-alpha cjk-earthly-branch cjk-heavenly-stem lower-greek hiragana
|
||
hiragana-iroha katakana katakana-iroha disclosure-open disclosure-closed
|
||
""")}
|
||
|
||
<%self:longhand name="list-style-image">
|
||
use cssparser::{ToCss, Token};
|
||
use std::fmt;
|
||
use url::Url;
|
||
use values::LocalToCss;
|
||
|
||
#[derive(Debug, Clone, PartialEq, Eq, HeapSizeOf)]
|
||
pub enum SpecifiedValue {
|
||
None,
|
||
Url(Url),
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
SpecifiedValue::None => dest.write_str("none"),
|
||
SpecifiedValue::Url(ref url) => url.to_css(dest),
|
||
}
|
||
}
|
||
}
|
||
|
||
pub mod computed_value {
|
||
use cssparser::{ToCss, Token};
|
||
use std::fmt;
|
||
use url::Url;
|
||
use values::LocalToCss;
|
||
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct T(pub Option<Url>);
|
||
|
||
impl ToCss for T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match self.0 {
|
||
None => dest.write_str("none"),
|
||
Some(ref url) => url.to_css(dest),
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, _context: &Cx) -> computed_value::T {
|
||
match *self {
|
||
SpecifiedValue::None => computed_value::T(None),
|
||
SpecifiedValue::Url(ref url) => computed_value::T(Some(url.clone())),
|
||
}
|
||
}
|
||
}
|
||
|
||
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||
Ok(SpecifiedValue::None)
|
||
} else {
|
||
Ok(SpecifiedValue::Url(context.parse_url(&*try!(input.expect_url()))))
|
||
}
|
||
}
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T(None)
|
||
}
|
||
</%self:longhand>
|
||
|
||
<%self:longhand name="quotes">
|
||
use std::borrow::Cow;
|
||
use std::fmt;
|
||
use values::computed::ComputedValueAsSpecified;
|
||
|
||
use cssparser::{ToCss, Token};
|
||
|
||
pub use self::computed_value::T as SpecifiedValue;
|
||
|
||
pub mod computed_value {
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct T(pub Vec<(String,String)>);
|
||
}
|
||
|
||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
let mut first = true;
|
||
for pair in &self.0 {
|
||
if !first {
|
||
try!(dest.write_str(" "));
|
||
}
|
||
first = false;
|
||
try!(Token::QuotedString(Cow::from(&*pair.0)).to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
try!(Token::QuotedString(Cow::from(&*pair.1)).to_css(dest));
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T(vec![
|
||
("\u{201c}".to_owned(), "\u{201d}".to_owned()),
|
||
("\u{2018}".to_owned(), "\u{2019}".to_owned()),
|
||
])
|
||
}
|
||
|
||
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||
return Ok(SpecifiedValue(Vec::new()))
|
||
}
|
||
|
||
let mut quotes = Vec::new();
|
||
loop {
|
||
let first = match input.next() {
|
||
Ok(Token::QuotedString(value)) => value.into_owned(),
|
||
Ok(_) => return Err(()),
|
||
Err(()) => break,
|
||
};
|
||
let second = match input.next() {
|
||
Ok(Token::QuotedString(value)) => value.into_owned(),
|
||
_ => return Err(()),
|
||
};
|
||
quotes.push((first, second))
|
||
}
|
||
if !quotes.is_empty() {
|
||
Ok(SpecifiedValue(quotes))
|
||
} else {
|
||
Err(())
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
${new_style_struct("Counters", is_inherited=False)}
|
||
|
||
<%self:longhand name="counter-increment">
|
||
use std::fmt;
|
||
use super::content;
|
||
use values::computed::ComputedValueAsSpecified;
|
||
|
||
use cssparser::{ToCss, Token, serialize_identifier};
|
||
use std::borrow::{Cow, ToOwned};
|
||
|
||
pub use self::computed_value::T as SpecifiedValue;
|
||
|
||
pub mod computed_value {
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct T(pub Vec<(String,i32)>);
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T(Vec::new())
|
||
}
|
||
|
||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
let mut first = true;
|
||
for pair in &self.0 {
|
||
if !first {
|
||
try!(dest.write_str(" "));
|
||
}
|
||
first = false;
|
||
try!(serialize_identifier(&pair.0, dest));
|
||
try!(write!(dest, " {}", pair.1));
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||
parse_common(1, input)
|
||
}
|
||
|
||
pub fn parse_common(default_value: i32, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||
return Ok(SpecifiedValue(Vec::new()))
|
||
}
|
||
|
||
let mut counters = Vec::new();
|
||
loop {
|
||
let counter_name = match input.next() {
|
||
Ok(Token::Ident(ident)) => (*ident).to_owned(),
|
||
Ok(_) => return Err(()),
|
||
Err(_) => break,
|
||
};
|
||
if content::counter_name_is_illegal(&counter_name) {
|
||
return Err(())
|
||
}
|
||
let counter_delta =
|
||
input.try(|input| specified::parse_integer(input)).unwrap_or(default_value);
|
||
counters.push((counter_name, counter_delta))
|
||
}
|
||
|
||
if !counters.is_empty() {
|
||
Ok(SpecifiedValue(counters))
|
||
} else {
|
||
Err(())
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
<%self:longhand name="counter-reset">
|
||
pub use super::counter_increment::{SpecifiedValue, computed_value, get_initial_value};
|
||
use super::counter_increment::{parse_common};
|
||
|
||
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||
parse_common(0, input)
|
||
}
|
||
</%self:longhand>
|
||
|
||
// CSS 2.1, Section 13 - Paged media
|
||
|
||
// CSS 2.1, Section 14 - Colors and Backgrounds
|
||
|
||
${new_style_struct("Background", is_inherited=False)}
|
||
${predefined_type(
|
||
"background-color", "CSSColor",
|
||
"::cssparser::Color::RGBA(::cssparser::RGBA { red: 0., green: 0., blue: 0., alpha: 0. }) /* transparent */")}
|
||
|
||
<%self:longhand name="background-image">
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
use values::specified::Image;
|
||
use values::LocalToCss;
|
||
|
||
pub mod computed_value {
|
||
use values::computed;
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct T(pub Option<computed::Image>);
|
||
}
|
||
|
||
impl ToCss for computed_value::T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match self.0 {
|
||
None => dest.write_str("none"),
|
||
Some(computed::Image::Url(ref url)) => url.to_css(dest),
|
||
Some(computed::Image::LinearGradient(ref gradient)) =>
|
||
gradient.to_css(dest)
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct SpecifiedValue(pub Option<Image>);
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
SpecifiedValue(Some(ref image)) => image.to_css(dest),
|
||
SpecifiedValue(None) => dest.write_str("none"),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T(None)
|
||
}
|
||
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||
Ok(SpecifiedValue(None))
|
||
} else {
|
||
Ok(SpecifiedValue(Some(try!(Image::parse(context, input)))))
|
||
}
|
||
}
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
match *self {
|
||
SpecifiedValue(None) => computed_value::T(None),
|
||
SpecifiedValue(Some(ref image)) =>
|
||
computed_value::T(Some(image.to_computed_value(context))),
|
||
}
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
<%self:longhand name="background-position">
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
use values::AuExtensionMethods;
|
||
|
||
pub mod computed_value {
|
||
use values::computed::LengthOrPercentage;
|
||
|
||
#[derive(PartialEq, Copy, Clone, Debug, HeapSizeOf)]
|
||
pub struct T {
|
||
pub horizontal: LengthOrPercentage,
|
||
pub vertical: LengthOrPercentage,
|
||
}
|
||
}
|
||
|
||
#[derive(Debug, Clone, PartialEq, Copy, HeapSizeOf)]
|
||
pub struct SpecifiedValue {
|
||
pub horizontal: specified::LengthOrPercentage,
|
||
pub vertical: specified::LengthOrPercentage,
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
try!(self.horizontal.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
try!(self.vertical.to_css(dest));
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
impl ToCss for computed_value::T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
try!(self.horizontal.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
try!(self.vertical.to_css(dest));
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
impl SpecifiedValue {
|
||
fn new(first: specified::PositionComponent, second: specified::PositionComponent)
|
||
-> Result<SpecifiedValue, ()> {
|
||
let (horiz, vert) = match (category(first), category(second)) {
|
||
// Don't allow two vertical keywords or two horizontal keywords.
|
||
(PositionCategory::HorizontalKeyword, PositionCategory::HorizontalKeyword) |
|
||
(PositionCategory::VerticalKeyword, PositionCategory::VerticalKeyword) => return Err(()),
|
||
|
||
// Swap if both are keywords and vertical precedes horizontal.
|
||
(PositionCategory::VerticalKeyword, PositionCategory::HorizontalKeyword) |
|
||
(PositionCategory::VerticalKeyword, PositionCategory::OtherKeyword) |
|
||
(PositionCategory::OtherKeyword, PositionCategory::HorizontalKeyword) => (second, first),
|
||
|
||
// By default, horizontal is first.
|
||
_ => (first, second),
|
||
};
|
||
Ok(SpecifiedValue {
|
||
horizontal: horiz.to_length_or_percentage(),
|
||
vertical: vert.to_length_or_percentage(),
|
||
})
|
||
}
|
||
}
|
||
|
||
// Collapse `Position` into a few categories to simplify the above `match` expression.
|
||
enum PositionCategory {
|
||
HorizontalKeyword,
|
||
VerticalKeyword,
|
||
OtherKeyword,
|
||
LengthOrPercentage,
|
||
}
|
||
fn category(p: specified::PositionComponent) -> PositionCategory {
|
||
match p {
|
||
specified::PositionComponent::Left |
|
||
specified::PositionComponent::Right =>
|
||
PositionCategory::HorizontalKeyword,
|
||
specified::PositionComponent::Top |
|
||
specified::PositionComponent::Bottom =>
|
||
PositionCategory::VerticalKeyword,
|
||
specified::PositionComponent::Center =>
|
||
PositionCategory::OtherKeyword,
|
||
specified::PositionComponent::LengthOrPercentage(_) =>
|
||
PositionCategory::LengthOrPercentage,
|
||
}
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
computed_value::T {
|
||
horizontal: self.horizontal.to_computed_value(context),
|
||
vertical: self.vertical.to_computed_value(context),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T {
|
||
horizontal: computed::LengthOrPercentage::Percentage(0.0),
|
||
vertical: computed::LengthOrPercentage::Percentage(0.0),
|
||
}
|
||
}
|
||
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser)
|
||
-> Result<SpecifiedValue, ()> {
|
||
let first = try!(specified::PositionComponent::parse(input));
|
||
let second = input.try(specified::PositionComponent::parse)
|
||
.unwrap_or(specified::PositionComponent::Center);
|
||
SpecifiedValue::new(first, second)
|
||
}
|
||
</%self:longhand>
|
||
|
||
${single_keyword("background-repeat", "repeat repeat-x repeat-y no-repeat")}
|
||
|
||
${single_keyword("background-attachment", "scroll fixed")}
|
||
|
||
${single_keyword("background-clip", "border-box padding-box content-box")}
|
||
|
||
${single_keyword("background-origin", "padding-box border-box content-box")}
|
||
|
||
<%self:longhand name="background-size">
|
||
use cssparser::{ToCss, Token};
|
||
use std::ascii::AsciiExt;
|
||
use std::fmt;
|
||
|
||
pub mod computed_value {
|
||
use values::computed::LengthOrPercentageOrAuto;
|
||
|
||
#[derive(PartialEq, Clone, Debug, HeapSizeOf)]
|
||
pub struct ExplicitSize {
|
||
pub width: LengthOrPercentageOrAuto,
|
||
pub height: LengthOrPercentageOrAuto,
|
||
}
|
||
|
||
#[derive(PartialEq, Clone, Debug, HeapSizeOf)]
|
||
pub enum T {
|
||
Explicit(ExplicitSize),
|
||
Cover,
|
||
Contain,
|
||
}
|
||
}
|
||
|
||
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::Explicit(ref size) => size.to_css(dest),
|
||
computed_value::T::Cover => dest.write_str("cover"),
|
||
computed_value::T::Contain => dest.write_str("contain"),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
|
||
pub struct SpecifiedExplicitSize {
|
||
pub width: specified::LengthOrPercentageOrAuto,
|
||
pub height: specified::LengthOrPercentageOrAuto,
|
||
}
|
||
|
||
impl ToCss for SpecifiedExplicitSize {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
try!(self.width.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
self.height.to_css(dest)
|
||
}
|
||
}
|
||
|
||
impl ToCss for computed_value::ExplicitSize {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
try!(self.width.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
self.height.to_css(dest)
|
||
}
|
||
}
|
||
|
||
|
||
#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
|
||
pub enum SpecifiedValue {
|
||
Explicit(SpecifiedExplicitSize),
|
||
Cover,
|
||
Contain,
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
SpecifiedValue::Explicit(ref size) => size.to_css(dest),
|
||
SpecifiedValue::Cover => dest.write_str("cover"),
|
||
SpecifiedValue::Contain => dest.write_str("contain"),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
match *self {
|
||
SpecifiedValue::Explicit(ref size) => {
|
||
computed_value::T::Explicit(computed_value::ExplicitSize {
|
||
width: size.width.to_computed_value(context),
|
||
height: size.height.to_computed_value(context),
|
||
})
|
||
}
|
||
SpecifiedValue::Cover => computed_value::T::Cover,
|
||
SpecifiedValue::Contain => computed_value::T::Contain,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T::Explicit(computed_value::ExplicitSize {
|
||
width: computed::LengthOrPercentageOrAuto::Auto,
|
||
height: computed::LengthOrPercentageOrAuto::Auto,
|
||
})
|
||
}
|
||
|
||
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||
let width;
|
||
if let Ok(value) = input.try(|input| {
|
||
match input.next() {
|
||
Err(_) => Err(()),
|
||
Ok(Token::Ident(ref ident)) if ident.eq_ignore_ascii_case("cover") => {
|
||
Ok(SpecifiedValue::Cover)
|
||
}
|
||
Ok(Token::Ident(ref ident)) if ident.eq_ignore_ascii_case("contain") => {
|
||
Ok(SpecifiedValue::Contain)
|
||
}
|
||
Ok(_) => Err(()),
|
||
}
|
||
}) {
|
||
return Ok(value)
|
||
} else {
|
||
width = try!(specified::LengthOrPercentageOrAuto::parse(input))
|
||
}
|
||
|
||
let height;
|
||
if let Ok(value) = input.try(|input| {
|
||
match input.next() {
|
||
Err(_) => Ok(specified::LengthOrPercentageOrAuto::Auto),
|
||
Ok(_) => Err(()),
|
||
}
|
||
}) {
|
||
height = value
|
||
} else {
|
||
height = try!(specified::LengthOrPercentageOrAuto::parse(input));
|
||
}
|
||
|
||
Ok(SpecifiedValue::Explicit(SpecifiedExplicitSize {
|
||
width: width,
|
||
height: height,
|
||
}))
|
||
}
|
||
</%self:longhand>
|
||
|
||
${new_style_struct("Color", is_inherited=True)}
|
||
|
||
<%self:raw_longhand name="color">
|
||
use cssparser::{Color, RGBA};
|
||
use values::specified::{CSSColor, CSSRGBA};
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, _context: &Cx) -> computed_value::T {
|
||
self.parsed
|
||
}
|
||
}
|
||
|
||
pub type SpecifiedValue = CSSRGBA;
|
||
pub mod computed_value {
|
||
use cssparser;
|
||
pub type T = cssparser::RGBA;
|
||
}
|
||
#[inline] pub fn get_initial_value() -> computed_value::T {
|
||
RGBA { red: 0., green: 0., blue: 0., alpha: 1. } /* black */
|
||
}
|
||
pub fn parse_specified(_context: &ParserContext, input: &mut Parser)
|
||
-> Result<DeclaredValue<SpecifiedValue>, ()> {
|
||
let value = try!(CSSColor::parse(input));
|
||
let rgba = match value.parsed {
|
||
Color::RGBA(rgba) => rgba,
|
||
Color::CurrentColor => return Ok(DeclaredValue::Inherit)
|
||
};
|
||
Ok(DeclaredValue::Value(CSSRGBA {
|
||
parsed: rgba,
|
||
authored: value.authored,
|
||
}))
|
||
}
|
||
</%self:raw_longhand>
|
||
|
||
// CSS 2.1, Section 15 - Fonts
|
||
|
||
${new_style_struct("Font", is_inherited=True)}
|
||
|
||
<%self:longhand name="font-family">
|
||
use self::computed_value::FontFamily;
|
||
use values::computed::ComputedValueAsSpecified;
|
||
pub use self::computed_value::T as SpecifiedValue;
|
||
|
||
const SERIF: &'static str = "serif";
|
||
const SANS_SERIF: &'static str = "sans-serif";
|
||
const CURSIVE: &'static str = "cursive";
|
||
const FANTASY: &'static str = "fantasy";
|
||
const MONOSPACE: &'static str = "monospace";
|
||
|
||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||
pub mod computed_value {
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
use string_cache::Atom;
|
||
|
||
#[derive(Debug, PartialEq, Eq, Clone, Hash, HeapSizeOf, Deserialize, Serialize)]
|
||
pub enum FontFamily {
|
||
FamilyName(Atom),
|
||
// Generic,
|
||
Serif,
|
||
SansSerif,
|
||
Cursive,
|
||
Fantasy,
|
||
Monospace,
|
||
}
|
||
impl FontFamily {
|
||
#[inline]
|
||
pub fn name(&self) -> &str {
|
||
match *self {
|
||
FontFamily::FamilyName(ref name) => &*name,
|
||
FontFamily::Serif => super::SERIF,
|
||
FontFamily::SansSerif => super::SANS_SERIF,
|
||
FontFamily::Cursive => super::CURSIVE,
|
||
FontFamily::Fantasy => super::FANTASY,
|
||
FontFamily::Monospace => super::MONOSPACE
|
||
}
|
||
}
|
||
|
||
pub fn from_atom(input: Atom) -> FontFamily {
|
||
let option = match_ignore_ascii_case! { &input,
|
||
super::SERIF => Some(FontFamily::Serif),
|
||
super::SANS_SERIF => Some(FontFamily::SansSerif),
|
||
super::CURSIVE => Some(FontFamily::Cursive),
|
||
super::FANTASY => Some(FontFamily::Fantasy),
|
||
super::MONOSPACE => Some(FontFamily::Monospace),
|
||
_ => None
|
||
};
|
||
|
||
match option {
|
||
Some(family) => family,
|
||
None => FontFamily::FamilyName(input)
|
||
}
|
||
}
|
||
}
|
||
impl ToCss for FontFamily {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
dest.write_str(self.name())
|
||
}
|
||
}
|
||
impl ToCss for T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
let mut iter = self.0.iter();
|
||
try!(iter.next().unwrap().to_css(dest));
|
||
for family in iter {
|
||
try!(dest.write_str(", "));
|
||
try!(family.to_css(dest));
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
#[derive(Debug, Clone, PartialEq, Eq, Hash, HeapSizeOf)]
|
||
pub struct T(pub Vec<FontFamily>);
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T(vec![FontFamily::Serif])
|
||
}
|
||
/// <family-name>#
|
||
/// <family-name> = <string> | [ <ident>+ ]
|
||
/// TODO: <generic-family>
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
input.parse_comma_separated(parse_one_family).map(SpecifiedValue)
|
||
}
|
||
pub fn parse_one_family(input: &mut Parser) -> Result<FontFamily, ()> {
|
||
if let Ok(value) = input.try(|input| input.expect_string()) {
|
||
return Ok(FontFamily::FamilyName(Atom::from(&*value)))
|
||
}
|
||
let first_ident = try!(input.expect_ident());
|
||
|
||
match_ignore_ascii_case! { first_ident,
|
||
SERIF => return Ok(FontFamily::Serif),
|
||
SANS_SERIF => return Ok(FontFamily::SansSerif),
|
||
CURSIVE => return Ok(FontFamily::Cursive),
|
||
FANTASY => return Ok(FontFamily::Fantasy),
|
||
MONOSPACE => return Ok(FontFamily::Monospace),
|
||
_ => {}
|
||
}
|
||
let mut value = first_ident.into_owned();
|
||
while let Ok(ident) = input.try(|input| input.expect_ident()) {
|
||
value.push_str(" ");
|
||
value.push_str(&ident);
|
||
}
|
||
Ok(FontFamily::FamilyName(Atom::from(value)))
|
||
}
|
||
</%self:longhand>
|
||
|
||
|
||
${single_keyword("font-style", "normal italic oblique")}
|
||
${single_keyword("font-variant", "normal small-caps")}
|
||
|
||
<%self:longhand name="font-weight">
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
|
||
#[derive(Debug, Clone, PartialEq, Eq, Copy, HeapSizeOf)]
|
||
pub enum SpecifiedValue {
|
||
Bolder,
|
||
Lighter,
|
||
% for weight in range(100, 901, 100):
|
||
Weight${weight},
|
||
% endfor
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
SpecifiedValue::Bolder => dest.write_str("bolder"),
|
||
SpecifiedValue::Lighter => dest.write_str("lighter"),
|
||
% for weight in range(100, 901, 100):
|
||
SpecifiedValue::Weight${weight} => dest.write_str("${weight}"),
|
||
% endfor
|
||
}
|
||
}
|
||
}
|
||
/// normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
input.try(|input| {
|
||
match_ignore_ascii_case! { try!(input.expect_ident()),
|
||
"bold" => Ok(SpecifiedValue::Weight700),
|
||
"normal" => Ok(SpecifiedValue::Weight400),
|
||
"bolder" => Ok(SpecifiedValue::Bolder),
|
||
"lighter" => Ok(SpecifiedValue::Lighter),
|
||
_ => Err(())
|
||
}
|
||
}).or_else(|()| {
|
||
match try!(input.expect_integer()) {
|
||
100 => Ok(SpecifiedValue::Weight100),
|
||
200 => Ok(SpecifiedValue::Weight200),
|
||
300 => Ok(SpecifiedValue::Weight300),
|
||
400 => Ok(SpecifiedValue::Weight400),
|
||
500 => Ok(SpecifiedValue::Weight500),
|
||
600 => Ok(SpecifiedValue::Weight600),
|
||
700 => Ok(SpecifiedValue::Weight700),
|
||
800 => Ok(SpecifiedValue::Weight800),
|
||
900 => Ok(SpecifiedValue::Weight900),
|
||
_ => Err(())
|
||
}
|
||
})
|
||
}
|
||
pub mod computed_value {
|
||
use std::fmt;
|
||
#[derive(PartialEq, Eq, Copy, Clone, Hash, Deserialize, Serialize, HeapSizeOf, Debug)]
|
||
pub enum T {
|
||
% for weight in range(100, 901, 100):
|
||
Weight${weight} = ${weight},
|
||
% endfor
|
||
}
|
||
impl T {
|
||
#[inline]
|
||
pub fn is_bold(self) -> bool {
|
||
match self {
|
||
T::Weight900 | T::Weight800 |
|
||
T::Weight700 | T::Weight600 => true,
|
||
_ => false
|
||
}
|
||
}
|
||
}
|
||
}
|
||
impl ToCss for computed_value::T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
% for weight in range(100, 901, 100):
|
||
computed_value::T::Weight${weight} => dest.write_str("${weight}"),
|
||
% endfor
|
||
}
|
||
}
|
||
}
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T::Weight400 // normal
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
match *self {
|
||
% for weight in range(100, 901, 100):
|
||
SpecifiedValue::Weight${weight} => computed_value::T::Weight${weight},
|
||
% endfor
|
||
SpecifiedValue::Bolder => match context.inherited_style().get_font().clone_font_weight() {
|
||
computed_value::T::Weight100 => computed_value::T::Weight400,
|
||
computed_value::T::Weight200 => computed_value::T::Weight400,
|
||
computed_value::T::Weight300 => computed_value::T::Weight400,
|
||
computed_value::T::Weight400 => computed_value::T::Weight700,
|
||
computed_value::T::Weight500 => computed_value::T::Weight700,
|
||
computed_value::T::Weight600 => computed_value::T::Weight900,
|
||
computed_value::T::Weight700 => computed_value::T::Weight900,
|
||
computed_value::T::Weight800 => computed_value::T::Weight900,
|
||
computed_value::T::Weight900 => computed_value::T::Weight900,
|
||
},
|
||
SpecifiedValue::Lighter => match context.inherited_style().get_font().clone_font_weight() {
|
||
computed_value::T::Weight100 => computed_value::T::Weight100,
|
||
computed_value::T::Weight200 => computed_value::T::Weight100,
|
||
computed_value::T::Weight300 => computed_value::T::Weight100,
|
||
computed_value::T::Weight400 => computed_value::T::Weight100,
|
||
computed_value::T::Weight500 => computed_value::T::Weight100,
|
||
computed_value::T::Weight600 => computed_value::T::Weight400,
|
||
computed_value::T::Weight700 => computed_value::T::Weight400,
|
||
computed_value::T::Weight800 => computed_value::T::Weight700,
|
||
computed_value::T::Weight900 => computed_value::T::Weight700,
|
||
},
|
||
}
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
<%self:longhand name="font-size">
|
||
use app_units::Au;
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
use values::FONT_MEDIUM_PX;
|
||
use values::specified::{LengthOrPercentage, Length, Percentage};
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
self.0.to_css(dest)
|
||
}
|
||
}
|
||
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct SpecifiedValue(pub specified::LengthOrPercentage);
|
||
pub mod computed_value {
|
||
use app_units::Au;
|
||
pub type T = Au;
|
||
}
|
||
#[inline] pub fn get_initial_value() -> computed_value::T {
|
||
Au::from_px(FONT_MEDIUM_PX)
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
match self.0 {
|
||
LengthOrPercentage::Length(Length::FontRelative(value)) => {
|
||
value.to_computed_value(context.inherited_style().get_font().clone_font_size(),
|
||
context.style().root_font_size())
|
||
}
|
||
LengthOrPercentage::Length(Length::ServoCharacterWidth(value)) => {
|
||
value.to_computed_value(context.inherited_style().get_font().clone_font_size())
|
||
}
|
||
LengthOrPercentage::Length(l) => {
|
||
l.to_computed_value(context)
|
||
}
|
||
LengthOrPercentage::Percentage(Percentage(value)) => {
|
||
context.inherited_style().get_font().clone_font_size().scale_by(value)
|
||
}
|
||
LengthOrPercentage::Calc(calc) => {
|
||
let calc = calc.to_computed_value(context);
|
||
calc.length() + context.inherited_style().get_font().clone_font_size()
|
||
.scale_by(calc.percentage())
|
||
}
|
||
}
|
||
}
|
||
}
|
||
/// <length> | <percentage> | <absolute-size> | <relative-size>
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
use values::specified::{Length, LengthOrPercentage};
|
||
|
||
input.try(specified::LengthOrPercentage::parse_non_negative)
|
||
.or_else(|()| {
|
||
let ident = try!(input.expect_ident());
|
||
specified::Length::from_str(&ident as &str)
|
||
.ok_or(())
|
||
.map(specified::LengthOrPercentage::Length)
|
||
})
|
||
.map(SpecifiedValue)
|
||
}
|
||
</%self:longhand>
|
||
|
||
${single_keyword("font-stretch",
|
||
"normal ultra-condensed extra-condensed condensed semi-condensed semi-expanded \
|
||
expanded extra-expanded ultra-expanded")}
|
||
|
||
// CSS 2.1, Section 16 - Text
|
||
|
||
${new_style_struct("InheritedText", is_inherited=True)}
|
||
|
||
<%self:longhand name="text-align">
|
||
pub use self::computed_value::T as SpecifiedValue;
|
||
use values::computed::ComputedValueAsSpecified;
|
||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||
pub mod computed_value {
|
||
macro_rules! define_text_align {
|
||
( $( $name: ident ( $string: expr ) => $discriminant: expr, )+ ) => {
|
||
define_css_keyword_enum! { T:
|
||
$(
|
||
$string => $name,
|
||
)+
|
||
}
|
||
impl T {
|
||
pub fn to_u32(self) -> u32 {
|
||
match self {
|
||
$(
|
||
T::$name => $discriminant,
|
||
)+
|
||
}
|
||
}
|
||
pub fn from_u32(discriminant: u32) -> Option<T> {
|
||
match discriminant {
|
||
$(
|
||
$discriminant => Some(T::$name),
|
||
)+
|
||
_ => None
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
define_text_align! {
|
||
start("start") => 0,
|
||
end("end") => 1,
|
||
left("left") => 2,
|
||
right("right") => 3,
|
||
center("center") => 4,
|
||
justify("justify") => 5,
|
||
servo_center("-servo-center") => 6,
|
||
servo_left("-servo-left") => 7,
|
||
servo_right("-servo-right") => 8,
|
||
}
|
||
}
|
||
#[inline] pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T::start
|
||
}
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser)
|
||
-> Result<SpecifiedValue, ()> {
|
||
computed_value::T::parse(input)
|
||
}
|
||
</%self:longhand>
|
||
|
||
<%self:longhand name="letter-spacing">
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
use values::AuExtensionMethods;
|
||
|
||
#[derive(Debug, Clone, Copy, PartialEq, HeapSizeOf)]
|
||
pub enum SpecifiedValue {
|
||
Normal,
|
||
Specified(specified::Length),
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
SpecifiedValue::Normal => dest.write_str("normal"),
|
||
SpecifiedValue::Specified(l) => l.to_css(dest),
|
||
}
|
||
}
|
||
}
|
||
|
||
pub mod computed_value {
|
||
use app_units::Au;
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct T(pub Option<Au>);
|
||
}
|
||
|
||
impl ToCss for computed_value::T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match self.0 {
|
||
None => dest.write_str("normal"),
|
||
Some(l) => l.to_css(dest),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[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<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
match *self {
|
||
SpecifiedValue::Normal => computed_value::T(None),
|
||
SpecifiedValue::Specified(l) =>
|
||
computed_value::T(Some(l.to_computed_value(context)))
|
||
}
|
||
}
|
||
}
|
||
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
|
||
Ok(SpecifiedValue::Normal)
|
||
} else {
|
||
specified::Length::parse_non_negative(input).map(SpecifiedValue::Specified)
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
<%self:longhand name="word-spacing">
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
use values::AuExtensionMethods;
|
||
|
||
#[derive(Debug, Clone, Copy, PartialEq, HeapSizeOf)]
|
||
pub enum SpecifiedValue {
|
||
Normal,
|
||
Specified(specified::Length), // FIXME(SimonSapin) support percentages
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
SpecifiedValue::Normal => dest.write_str("normal"),
|
||
SpecifiedValue::Specified(l) => l.to_css(dest),
|
||
}
|
||
}
|
||
}
|
||
|
||
pub mod computed_value {
|
||
use app_units::Au;
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct T(pub Option<Au>);
|
||
}
|
||
|
||
impl ToCss for computed_value::T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match self.0 {
|
||
None => dest.write_str("normal"),
|
||
Some(l) => l.to_css(dest),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[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<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
match *self {
|
||
SpecifiedValue::Normal => computed_value::T(None),
|
||
SpecifiedValue::Specified(l) =>
|
||
computed_value::T(Some(l.to_computed_value(context)))
|
||
}
|
||
}
|
||
}
|
||
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
|
||
Ok(SpecifiedValue::Normal)
|
||
} else {
|
||
specified::Length::parse_non_negative(input).map(SpecifiedValue::Specified)
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
${predefined_type("text-indent", "LengthOrPercentage", "computed::LengthOrPercentage::Length(Au(0))")}
|
||
|
||
// Also known as "word-wrap" (which is more popular because of IE), but this is the preferred
|
||
// name per CSS-TEXT 6.2.
|
||
${single_keyword("overflow-wrap", "normal break-word")}
|
||
|
||
// TODO(pcwalton): Support `word-break: keep-all` once we have better CJK support.
|
||
${single_keyword("word-break", "normal break-all")}
|
||
|
||
${single_keyword("text-overflow", "clip ellipsis")}
|
||
|
||
// TODO(pcwalton): Support `text-justify: distribute`.
|
||
${single_keyword("text-justify", "auto none inter-word")}
|
||
|
||
${new_style_struct("Text", is_inherited=False)}
|
||
|
||
${single_keyword("unicode-bidi", "normal embed isolate bidi-override isolate-override plaintext")}
|
||
|
||
<%self:longhand name="text-decoration" custom_cascade="True">
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
use values::computed::ComputedValueAsSpecified;
|
||
use properties::style_struct_traits::TInheritedText;
|
||
|
||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||
|
||
#[derive(PartialEq, Eq, Copy, Clone, Debug, HeapSizeOf)]
|
||
pub struct SpecifiedValue {
|
||
pub underline: bool,
|
||
pub overline: bool,
|
||
pub line_through: bool,
|
||
// 'blink' is accepted in the parser but ignored.
|
||
// Just not blinking the text is a conforming implementation per CSS 2.1.
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
let mut space = false;
|
||
if self.underline {
|
||
try!(dest.write_str("underline"));
|
||
space = true;
|
||
}
|
||
if self.overline {
|
||
if space {
|
||
try!(dest.write_str(" "));
|
||
}
|
||
try!(dest.write_str("overline"));
|
||
space = true;
|
||
}
|
||
if self.line_through {
|
||
if space {
|
||
try!(dest.write_str(" "));
|
||
}
|
||
try!(dest.write_str("line-through"));
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
pub mod computed_value {
|
||
pub type T = super::SpecifiedValue;
|
||
#[allow(non_upper_case_globals)]
|
||
pub const none: T = super::SpecifiedValue {
|
||
underline: false, overline: false, line_through: false
|
||
};
|
||
}
|
||
#[inline] pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::none
|
||
}
|
||
/// none | [ underline || overline || line-through || blink ]
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
let mut result = SpecifiedValue {
|
||
underline: false, overline: false, line_through: false,
|
||
};
|
||
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||
return Ok(result)
|
||
}
|
||
let mut blink = false;
|
||
let mut empty = true;
|
||
while let Ok(ident) = input.expect_ident() {
|
||
match_ignore_ascii_case! { ident,
|
||
"underline" => if result.underline { return Err(()) }
|
||
else { empty = false; result.underline = true },
|
||
"overline" => if result.overline { return Err(()) }
|
||
else { empty = false; result.overline = true },
|
||
"line-through" => if result.line_through { return Err(()) }
|
||
else { empty = false; result.line_through = true },
|
||
"blink" => if blink { return Err(()) }
|
||
else { empty = false; blink = true },
|
||
_ => break
|
||
}
|
||
}
|
||
if !empty { Ok(result) } else { Err(()) }
|
||
}
|
||
|
||
fn cascade_property_custom<C: TComputedValues>(
|
||
_declaration: &PropertyDeclaration,
|
||
_inherited_style: &C,
|
||
context: &mut computed::Context<C>,
|
||
_seen: &mut PropertyBitField,
|
||
_cacheable: &mut bool,
|
||
_error_reporter: &mut Box<ParseErrorReporter + Send>) {
|
||
longhands::_servo_text_decorations_in_effect::derive_from_text_decoration(context);
|
||
}
|
||
</%self:longhand>
|
||
|
||
${switch_to_style_struct("InheritedText")}
|
||
|
||
<%self:longhand name="-servo-text-decorations-in-effect"
|
||
derived_from="display text-decoration">
|
||
use cssparser::{RGBA, ToCss};
|
||
use std::fmt;
|
||
|
||
use values::computed::ComputedValueAsSpecified;
|
||
use properties::style_struct_traits::{TBox, TColor, TText};
|
||
|
||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||
|
||
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
|
||
pub struct SpecifiedValue {
|
||
pub underline: Option<RGBA>,
|
||
pub overline: Option<RGBA>,
|
||
pub line_through: Option<RGBA>,
|
||
}
|
||
|
||
pub mod computed_value {
|
||
pub type T = super::SpecifiedValue;
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
|
||
// Web compat doesn't matter here.
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
SpecifiedValue {
|
||
underline: None,
|
||
overline: None,
|
||
line_through: None,
|
||
}
|
||
}
|
||
|
||
fn maybe<Cx: TContext>(flag: bool, context: &Cx) -> Option<RGBA> {
|
||
if flag {
|
||
Some(context.style().get_color().clone_color())
|
||
} else {
|
||
None
|
||
}
|
||
}
|
||
|
||
fn derive<Cx: TContext>(context: &Cx) -> computed_value::T {
|
||
// Start with no declarations if this is a block; otherwise, start with the
|
||
// declarations in effect and add in the text decorations that this inline specifies.
|
||
let mut result = match context.style().get_box().clone_display() {
|
||
super::display::computed_value::T::inline => {
|
||
context.inherited_style().get_inheritedtext().clone__servo_text_decorations_in_effect()
|
||
}
|
||
_ => {
|
||
SpecifiedValue {
|
||
underline: None,
|
||
overline: None,
|
||
line_through: None,
|
||
}
|
||
}
|
||
};
|
||
|
||
if result.underline.is_none() {
|
||
result.underline = maybe(context.style().get_text().has_underline(), context)
|
||
}
|
||
if result.overline.is_none() {
|
||
result.overline = maybe(context.style().get_text().has_overline(), context)
|
||
}
|
||
if result.line_through.is_none() {
|
||
result.line_through = maybe(context.style().get_text().has_line_through(), context)
|
||
}
|
||
|
||
result
|
||
}
|
||
|
||
#[inline]
|
||
pub fn derive_from_text_decoration<Cx: TContext>(context: &mut Cx) {
|
||
let derived = derive(context);
|
||
context.mutate_style().mutate_inheritedtext().set__servo_text_decorations_in_effect(derived);
|
||
}
|
||
|
||
#[inline]
|
||
pub fn derive_from_display<Cx: TContext>(context: &mut Cx) {
|
||
let derived = derive(context);
|
||
context.mutate_style().mutate_inheritedtext().set__servo_text_decorations_in_effect(derived);
|
||
}
|
||
</%self:longhand>
|
||
|
||
<%self:single_keyword_computed name="white-space" values="normal pre nowrap pre-wrap pre-line">
|
||
use values::computed::ComputedValueAsSpecified;
|
||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||
|
||
impl SpecifiedValue {
|
||
pub fn allow_wrap(&self) -> bool {
|
||
match *self {
|
||
SpecifiedValue::nowrap |
|
||
SpecifiedValue::pre => false,
|
||
SpecifiedValue::normal |
|
||
SpecifiedValue::pre_wrap |
|
||
SpecifiedValue::pre_line => true,
|
||
}
|
||
}
|
||
|
||
pub fn preserve_newlines(&self) -> bool {
|
||
match *self {
|
||
SpecifiedValue::normal |
|
||
SpecifiedValue::nowrap => false,
|
||
SpecifiedValue::pre |
|
||
SpecifiedValue::pre_wrap |
|
||
SpecifiedValue::pre_line => true,
|
||
}
|
||
}
|
||
|
||
pub fn preserve_spaces(&self) -> bool {
|
||
match *self {
|
||
SpecifiedValue::normal |
|
||
SpecifiedValue::nowrap |
|
||
SpecifiedValue::pre_line => false,
|
||
SpecifiedValue::pre |
|
||
SpecifiedValue::pre_wrap => true,
|
||
}
|
||
}
|
||
}
|
||
</%self:single_keyword_computed>
|
||
|
||
// TODO(pcwalton): `full-width`
|
||
${single_keyword("text-transform", "none capitalize uppercase lowercase")}
|
||
|
||
${single_keyword("text-rendering", "auto optimizespeed optimizelegibility geometricprecision")}
|
||
|
||
// CSS 2.1, Section 17 - Tables
|
||
${new_style_struct("Table", is_inherited=False)}
|
||
|
||
${single_keyword("table-layout", "auto fixed")}
|
||
|
||
${new_style_struct("InheritedTable", is_inherited=True)}
|
||
|
||
${single_keyword("border-collapse", "separate collapse")}
|
||
|
||
${single_keyword("empty-cells", "show hide")}
|
||
|
||
${single_keyword("caption-side", "top bottom")}
|
||
|
||
<%self:longhand name="border-spacing">
|
||
use app_units::Au;
|
||
use values::AuExtensionMethods;
|
||
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
|
||
pub mod computed_value {
|
||
use app_units::Au;
|
||
|
||
#[derive(Clone, Copy, Debug, PartialEq, RustcEncodable, HeapSizeOf)]
|
||
pub struct T {
|
||
pub horizontal: Au,
|
||
pub vertical: Au,
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, Debug, PartialEq, HeapSizeOf)]
|
||
pub struct SpecifiedValue {
|
||
pub horizontal: specified::Length,
|
||
pub vertical: specified::Length,
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T {
|
||
horizontal: Au(0),
|
||
vertical: Au(0),
|
||
}
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
try!(self.horizontal.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
self.vertical.to_css(dest)
|
||
}
|
||
}
|
||
|
||
impl ToCss for computed_value::T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
try!(self.horizontal.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
self.vertical.to_css(dest)
|
||
}
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
computed_value::T {
|
||
horizontal: self.horizontal.to_computed_value(context),
|
||
vertical: self.vertical.to_computed_value(context),
|
||
}
|
||
}
|
||
}
|
||
|
||
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||
let mut lengths = [ None, None ];
|
||
for i in 0..2 {
|
||
match specified::Length::parse_non_negative(input) {
|
||
Err(()) => break,
|
||
Ok(length) => lengths[i] = Some(length),
|
||
}
|
||
}
|
||
if input.next().is_ok() {
|
||
return Err(())
|
||
}
|
||
match (lengths[0], lengths[1]) {
|
||
(None, None) => Err(()),
|
||
(Some(length), None) => {
|
||
Ok(SpecifiedValue {
|
||
horizontal: length,
|
||
vertical: length,
|
||
})
|
||
}
|
||
(Some(horizontal), Some(vertical)) => {
|
||
Ok(SpecifiedValue {
|
||
horizontal: horizontal,
|
||
vertical: vertical,
|
||
})
|
||
}
|
||
(None, Some(_)) => panic!("shouldn't happen"),
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
// CSS 2.1, Section 18 - User interface
|
||
|
||
|
||
// CSS Writing Modes Level 3
|
||
// http://dev.w3.org/csswg/css-writing-modes/
|
||
${switch_to_style_struct("InheritedBox")}
|
||
|
||
${single_keyword("writing-mode", "horizontal-tb vertical-rl vertical-lr", experimental=True)}
|
||
|
||
// FIXME(SimonSapin): Add 'mixed' and 'upright' (needs vertical text support)
|
||
// FIXME(SimonSapin): initial (first) value should be 'mixed', when that's implemented
|
||
${single_keyword("text-orientation", "sideways sideways-left sideways-right", experimental=True)}
|
||
|
||
// CSS Basic User Interface Module Level 3
|
||
// http://dev.w3.org/csswg/css-ui/
|
||
${switch_to_style_struct("Box")}
|
||
|
||
${single_keyword("box-sizing", "content-box border-box")}
|
||
|
||
${new_style_struct("Pointing", is_inherited=True)}
|
||
|
||
<%self:longhand name="cursor">
|
||
pub use self::computed_value::T as SpecifiedValue;
|
||
use values::computed::ComputedValueAsSpecified;
|
||
|
||
impl ComputedValueAsSpecified for SpecifiedValue {}
|
||
|
||
pub mod computed_value {
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
use style_traits::cursor::Cursor;
|
||
|
||
#[derive(Clone, PartialEq, Eq, Copy, Debug, HeapSizeOf)]
|
||
pub enum T {
|
||
AutoCursor,
|
||
SpecifiedCursor(Cursor),
|
||
}
|
||
|
||
impl ToCss for T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
T::AutoCursor => dest.write_str("auto"),
|
||
T::SpecifiedCursor(c) => c.to_css(dest),
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T::AutoCursor
|
||
}
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
use std::ascii::AsciiExt;
|
||
use style_traits::cursor::Cursor;
|
||
let ident = try!(input.expect_ident());
|
||
if ident.eq_ignore_ascii_case("auto") {
|
||
Ok(SpecifiedValue::AutoCursor)
|
||
} else {
|
||
Cursor::from_css_keyword(&ident)
|
||
.map(SpecifiedValue::SpecifiedCursor)
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
// NB: `pointer-events: auto` (and use of `pointer-events` in anything that isn't SVG, in fact)
|
||
// is nonstandard, slated for CSS4-UI.
|
||
// TODO(pcwalton): SVG-only values.
|
||
${single_keyword("pointer-events", "auto none")}
|
||
|
||
|
||
${new_style_struct("Column", is_inherited=False)}
|
||
|
||
<%self:longhand name="column-width" experimental="True">
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
use values::AuExtensionMethods;
|
||
|
||
#[derive(Debug, Clone, Copy, PartialEq, HeapSizeOf)]
|
||
pub enum SpecifiedValue {
|
||
Auto,
|
||
Specified(specified::Length),
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
SpecifiedValue::Auto => dest.write_str("auto"),
|
||
SpecifiedValue::Specified(l) => l.to_css(dest),
|
||
}
|
||
}
|
||
}
|
||
|
||
pub mod computed_value {
|
||
use app_units::Au;
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct T(pub Option<Au>);
|
||
}
|
||
|
||
impl ToCss for computed_value::T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match self.0 {
|
||
None => dest.write_str("auto"),
|
||
Some(l) => l.to_css(dest),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[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<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
match *self {
|
||
SpecifiedValue::Auto => computed_value::T(None),
|
||
SpecifiedValue::Specified(l) =>
|
||
computed_value::T(Some(l.to_computed_value(context)))
|
||
}
|
||
}
|
||
}
|
||
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
|
||
Ok(SpecifiedValue::Auto)
|
||
} else {
|
||
specified::Length::parse_non_negative(input).map(SpecifiedValue::Specified)
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
<%self:longhand name="column-count" experimental="True">
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
|
||
#[derive(Debug, Clone, Copy, PartialEq, HeapSizeOf)]
|
||
pub enum SpecifiedValue {
|
||
Auto,
|
||
Specified(u32),
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
SpecifiedValue::Auto => dest.write_str("auto"),
|
||
SpecifiedValue::Specified(count) => write!(dest, "{}", count),
|
||
}
|
||
}
|
||
}
|
||
|
||
pub mod computed_value {
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct T(pub Option<u32>);
|
||
}
|
||
|
||
impl ToCss for computed_value::T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match self.0 {
|
||
None => dest.write_str("auto"),
|
||
Some(count) => write!(dest, "{}", count),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[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<Cx: TContext>(&self, _context: &Cx) -> computed_value::T {
|
||
match *self {
|
||
SpecifiedValue::Auto => computed_value::T(None),
|
||
SpecifiedValue::Specified(count) =>
|
||
computed_value::T(Some(count))
|
||
}
|
||
}
|
||
}
|
||
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
|
||
Ok(SpecifiedValue::Auto)
|
||
} else {
|
||
let count = try!(specified::parse_integer(input));
|
||
// Zero is invalid
|
||
if count <= 0 {
|
||
return Err(())
|
||
}
|
||
Ok(SpecifiedValue::Specified(count as u32))
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
<%self:longhand name="column-gap" experimental="True">
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
use values::AuExtensionMethods;
|
||
|
||
#[derive(Debug, Clone, Copy, PartialEq, HeapSizeOf)]
|
||
pub enum SpecifiedValue {
|
||
Normal,
|
||
Specified(specified::Length),
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
SpecifiedValue::Normal => dest.write_str("normal"),
|
||
SpecifiedValue::Specified(l) => l.to_css(dest),
|
||
}
|
||
}
|
||
}
|
||
|
||
pub mod computed_value {
|
||
use app_units::Au;
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct T(pub Option<Au>);
|
||
}
|
||
|
||
impl ToCss for computed_value::T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match self.0 {
|
||
None => dest.write_str("normal"),
|
||
Some(l) => l.to_css(dest),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[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<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
match *self {
|
||
SpecifiedValue::Normal => computed_value::T(None),
|
||
SpecifiedValue::Specified(l) =>
|
||
computed_value::T(Some(l.to_computed_value(context)))
|
||
}
|
||
}
|
||
}
|
||
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
|
||
Ok(SpecifiedValue::Normal)
|
||
} else {
|
||
specified::Length::parse_non_negative(input).map(SpecifiedValue::Specified)
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
// Box-shadow, etc.
|
||
${new_style_struct("Effects", is_inherited=False)}
|
||
|
||
<%self:longhand name="opacity">
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
use values::CSSFloat;
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
self.0.to_css(dest)
|
||
}
|
||
}
|
||
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct SpecifiedValue(pub CSSFloat);
|
||
pub mod computed_value {
|
||
use values::CSSFloat;
|
||
pub type T = CSSFloat;
|
||
}
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
1.0
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, _context: &Cx) -> computed_value::T {
|
||
if self.0 < 0.0 {
|
||
0.0
|
||
} else if self.0 > 1.0 {
|
||
1.0
|
||
} else {
|
||
self.0
|
||
}
|
||
}
|
||
}
|
||
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
specified::parse_number(input).map(SpecifiedValue)
|
||
}
|
||
</%self:longhand>
|
||
|
||
<%self:longhand name="box-shadow">
|
||
use cssparser::{self, ToCss};
|
||
use std::fmt;
|
||
use values::AuExtensionMethods;
|
||
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct SpecifiedValue(Vec<SpecifiedBoxShadow>);
|
||
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct SpecifiedBoxShadow {
|
||
pub offset_x: specified::Length,
|
||
pub offset_y: specified::Length,
|
||
pub blur_radius: specified::Length,
|
||
pub spread_radius: specified::Length,
|
||
pub color: Option<specified::CSSColor>,
|
||
pub inset: bool,
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
let mut iter = self.0.iter();
|
||
if let Some(shadow) = iter.next() {
|
||
try!(shadow.to_css(dest));
|
||
} else {
|
||
try!(dest.write_str("none"));
|
||
return Ok(())
|
||
}
|
||
for shadow in iter {
|
||
try!(dest.write_str(", "));
|
||
try!(shadow.to_css(dest));
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
impl ToCss for SpecifiedBoxShadow {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
if self.inset {
|
||
try!(dest.write_str("inset "));
|
||
}
|
||
try!(self.blur_radius.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
try!(self.spread_radius.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
try!(self.offset_x.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
try!(self.offset_y.to_css(dest));
|
||
|
||
if let Some(ref color) = self.color {
|
||
try!(dest.write_str(" "));
|
||
try!(color.to_css(dest));
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
pub mod computed_value {
|
||
use app_units::Au;
|
||
use std::fmt;
|
||
use values::computed;
|
||
|
||
#[derive(Clone, PartialEq, HeapSizeOf, Debug)]
|
||
pub struct T(pub Vec<BoxShadow>);
|
||
|
||
#[derive(Clone, PartialEq, Copy, HeapSizeOf, Debug)]
|
||
pub struct BoxShadow {
|
||
pub offset_x: Au,
|
||
pub offset_y: Au,
|
||
pub blur_radius: Au,
|
||
pub spread_radius: Au,
|
||
pub color: computed::CSSColor,
|
||
pub inset: bool,
|
||
}
|
||
}
|
||
|
||
impl ToCss for computed_value::T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
let mut iter = self.0.iter();
|
||
if let Some(shadow) = iter.next() {
|
||
try!(shadow.to_css(dest));
|
||
} else {
|
||
try!(dest.write_str("none"));
|
||
return Ok(())
|
||
}
|
||
for shadow in iter {
|
||
try!(dest.write_str(", "));
|
||
try!(shadow.to_css(dest));
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
impl ToCss for computed_value::BoxShadow {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
if self.inset {
|
||
try!(dest.write_str("inset "));
|
||
}
|
||
try!(self.blur_radius.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
try!(self.spread_radius.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
try!(self.offset_x.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
try!(self.offset_y.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
try!(self.color.to_css(dest));
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T(Vec::new())
|
||
}
|
||
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||
Ok(SpecifiedValue(Vec::new()))
|
||
} else {
|
||
input.parse_comma_separated(parse_one_box_shadow).map(SpecifiedValue)
|
||
}
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
computed_value::T(self.0.iter().map(|value| compute_one_box_shadow(value, context)).collect())
|
||
}
|
||
}
|
||
|
||
pub fn compute_one_box_shadow<Cx: TContext>(value: &SpecifiedBoxShadow, context: &Cx)
|
||
-> computed_value::BoxShadow {
|
||
computed_value::BoxShadow {
|
||
offset_x: value.offset_x.to_computed_value(context),
|
||
offset_y: value.offset_y.to_computed_value(context),
|
||
blur_radius: value.blur_radius.to_computed_value(context),
|
||
spread_radius: value.spread_radius.to_computed_value(context),
|
||
color: value.color
|
||
.as_ref()
|
||
.map(|color| color.parsed)
|
||
.unwrap_or(cssparser::Color::CurrentColor),
|
||
inset: value.inset,
|
||
}
|
||
}
|
||
|
||
pub fn parse_one_box_shadow(input: &mut Parser) -> Result<SpecifiedBoxShadow, ()> {
|
||
use app_units::Au;
|
||
let mut lengths = [specified::Length::Absolute(Au(0)); 4];
|
||
let mut lengths_parsed = false;
|
||
let mut color = None;
|
||
let mut inset = false;
|
||
|
||
loop {
|
||
if !inset {
|
||
if input.try(|input| input.expect_ident_matching("inset")).is_ok() {
|
||
inset = true;
|
||
continue
|
||
}
|
||
}
|
||
if !lengths_parsed {
|
||
if let Ok(value) = input.try(specified::Length::parse) {
|
||
lengths[0] = value;
|
||
let mut length_parsed_count = 1;
|
||
while length_parsed_count < 4 {
|
||
if let Ok(value) = input.try(specified::Length::parse) {
|
||
lengths[length_parsed_count] = value
|
||
} else {
|
||
break
|
||
}
|
||
length_parsed_count += 1;
|
||
}
|
||
|
||
// The first two lengths must be specified.
|
||
if length_parsed_count < 2 {
|
||
return Err(())
|
||
}
|
||
|
||
lengths_parsed = true;
|
||
continue
|
||
}
|
||
}
|
||
if color.is_none() {
|
||
if let Ok(value) = input.try(specified::CSSColor::parse) {
|
||
color = Some(value);
|
||
continue
|
||
}
|
||
}
|
||
break
|
||
}
|
||
|
||
// Lengths must be specified.
|
||
if !lengths_parsed {
|
||
return Err(())
|
||
}
|
||
|
||
Ok(SpecifiedBoxShadow {
|
||
offset_x: lengths[0],
|
||
offset_y: lengths[1],
|
||
blur_radius: lengths[2],
|
||
spread_radius: lengths[3],
|
||
color: color,
|
||
inset: inset,
|
||
})
|
||
}
|
||
</%self:longhand>
|
||
|
||
<%self:longhand name="clip">
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
use values::AuExtensionMethods;
|
||
|
||
// NB: `top` and `left` are 0 if `auto` per CSS 2.1 11.1.2.
|
||
|
||
pub mod computed_value {
|
||
use app_units::Au;
|
||
|
||
#[derive(Clone, PartialEq, Eq, Copy, Debug, HeapSizeOf)]
|
||
pub struct ClipRect {
|
||
pub top: Au,
|
||
pub right: Option<Au>,
|
||
pub bottom: Option<Au>,
|
||
pub left: Au,
|
||
}
|
||
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct T(pub Option<ClipRect>);
|
||
}
|
||
|
||
impl ToCss for computed_value::T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match self.0 {
|
||
None => dest.write_str("auto"),
|
||
Some(rect) => {
|
||
try!(dest.write_str("rect("));
|
||
try!(rect.top.to_css(dest));
|
||
try!(dest.write_str(", "));
|
||
if let Some(right) = rect.right {
|
||
try!(right.to_css(dest));
|
||
try!(dest.write_str(", "));
|
||
} else {
|
||
try!(dest.write_str("auto, "));
|
||
}
|
||
|
||
if let Some(bottom) = rect.bottom {
|
||
try!(bottom.to_css(dest));
|
||
try!(dest.write_str(", "));
|
||
} else {
|
||
try!(dest.write_str("auto, "));
|
||
}
|
||
|
||
try!(rect.left.to_css(dest));
|
||
try!(dest.write_str(")"));
|
||
Ok(())
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, Debug, PartialEq, Copy, HeapSizeOf)]
|
||
pub struct SpecifiedClipRect {
|
||
pub top: specified::Length,
|
||
pub right: Option<specified::Length>,
|
||
pub bottom: Option<specified::Length>,
|
||
pub left: specified::Length,
|
||
}
|
||
|
||
#[derive(Clone, Debug, PartialEq, Copy, HeapSizeOf)]
|
||
pub struct SpecifiedValue(Option<SpecifiedClipRect>);
|
||
|
||
impl ToCss for SpecifiedClipRect {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
try!(dest.write_str("rect("));
|
||
|
||
try!(self.top.to_css(dest));
|
||
try!(dest.write_str(", "));
|
||
|
||
if let Some(right) = self.right {
|
||
try!(right.to_css(dest));
|
||
try!(dest.write_str(", "));
|
||
} else {
|
||
try!(dest.write_str("auto, "));
|
||
}
|
||
|
||
if let Some(bottom) = self.bottom {
|
||
try!(bottom.to_css(dest));
|
||
try!(dest.write_str(", "));
|
||
} else {
|
||
try!(dest.write_str("auto, "));
|
||
}
|
||
|
||
try!(self.left.to_css(dest));
|
||
|
||
try!(dest.write_str(")"));
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
if let Some(ref rect) = self.0 {
|
||
rect.to_css(dest)
|
||
} else {
|
||
dest.write_str("auto")
|
||
}
|
||
}
|
||
}
|
||
|
||
#[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<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
computed_value::T(self.0.map(|value| computed_value::ClipRect {
|
||
top: value.top.to_computed_value(context),
|
||
right: value.right.map(|right| right.to_computed_value(context)),
|
||
bottom: value.bottom.map(|bottom| bottom.to_computed_value(context)),
|
||
left: value.left.to_computed_value(context),
|
||
}))
|
||
}
|
||
}
|
||
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
use app_units::Au;
|
||
use std::ascii::AsciiExt;
|
||
use values::specified::Length;
|
||
|
||
if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
|
||
return Ok(SpecifiedValue(None))
|
||
}
|
||
if !try!(input.expect_function()).eq_ignore_ascii_case("rect") {
|
||
return Err(())
|
||
}
|
||
let sides = try!(input.parse_nested_block(|input| {
|
||
input.parse_comma_separated(|input| {
|
||
if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
|
||
Ok(None)
|
||
} else {
|
||
Length::parse(input).map(Some)
|
||
}
|
||
})
|
||
}));
|
||
if sides.len() == 4 {
|
||
Ok(SpecifiedValue(Some(SpecifiedClipRect {
|
||
top: sides[0].unwrap_or(Length::Absolute(Au(0))),
|
||
right: sides[1],
|
||
bottom: sides[2],
|
||
left: sides[3].unwrap_or(Length::Absolute(Au(0))),
|
||
})))
|
||
} else {
|
||
Err(())
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
<%self:longhand name="text-shadow">
|
||
use cssparser::{self, ToCss};
|
||
use std::fmt;
|
||
use values::AuExtensionMethods;
|
||
|
||
#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
|
||
pub struct SpecifiedValue(Vec<SpecifiedTextShadow>);
|
||
|
||
#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
|
||
pub struct SpecifiedTextShadow {
|
||
pub offset_x: specified::Length,
|
||
pub offset_y: specified::Length,
|
||
pub blur_radius: specified::Length,
|
||
pub color: Option<specified::CSSColor>,
|
||
}
|
||
|
||
pub mod computed_value {
|
||
use app_units::Au;
|
||
use cssparser::Color;
|
||
|
||
#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
|
||
pub struct T(pub Vec<TextShadow>);
|
||
|
||
#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
|
||
pub struct TextShadow {
|
||
pub offset_x: Au,
|
||
pub offset_y: Au,
|
||
pub blur_radius: Au,
|
||
pub color: Color,
|
||
}
|
||
}
|
||
|
||
impl ToCss for computed_value::T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
let mut iter = self.0.iter();
|
||
if let Some(shadow) = iter.next() {
|
||
try!(shadow.to_css(dest));
|
||
} else {
|
||
try!(dest.write_str("none"));
|
||
return Ok(())
|
||
}
|
||
for shadow in iter {
|
||
try!(dest.write_str(", "));
|
||
try!(shadow.to_css(dest));
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
impl ToCss for computed_value::TextShadow {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
try!(self.offset_x.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
try!(self.offset_y.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
try!(self.blur_radius.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
try!(self.color.to_css(dest));
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
let mut iter = self.0.iter();
|
||
if let Some(shadow) = iter.next() {
|
||
try!(shadow.to_css(dest));
|
||
} else {
|
||
try!(dest.write_str("none"));
|
||
return Ok(())
|
||
}
|
||
for shadow in iter {
|
||
try!(dest.write_str(", "));
|
||
try!(shadow.to_css(dest));
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
impl ToCss for SpecifiedTextShadow {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
try!(self.offset_x.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
try!(self.offset_y.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
try!(self.blur_radius.to_css(dest));
|
||
|
||
if let Some(ref color) = self.color {
|
||
try!(dest.write_str(" "));
|
||
try!(color.to_css(dest));
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T(Vec::new())
|
||
}
|
||
|
||
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||
Ok(SpecifiedValue(Vec::new()))
|
||
} else {
|
||
input.parse_comma_separated(parse_one_text_shadow).map(SpecifiedValue)
|
||
}
|
||
}
|
||
|
||
fn parse_one_text_shadow(input: &mut Parser) -> Result<SpecifiedTextShadow,()> {
|
||
use app_units::Au;
|
||
let mut lengths = [specified::Length::Absolute(Au(0)); 3];
|
||
let mut lengths_parsed = false;
|
||
let mut color = None;
|
||
|
||
loop {
|
||
if !lengths_parsed {
|
||
if let Ok(value) = input.try(specified::Length::parse) {
|
||
lengths[0] = value;
|
||
let mut length_parsed_count = 1;
|
||
while length_parsed_count < 3 {
|
||
if let Ok(value) = input.try(specified::Length::parse) {
|
||
lengths[length_parsed_count] = value
|
||
} else {
|
||
break
|
||
}
|
||
length_parsed_count += 1;
|
||
}
|
||
|
||
// The first two lengths must be specified.
|
||
if length_parsed_count < 2 {
|
||
return Err(())
|
||
}
|
||
|
||
lengths_parsed = true;
|
||
continue
|
||
}
|
||
}
|
||
if color.is_none() {
|
||
if let Ok(value) = input.try(specified::CSSColor::parse) {
|
||
color = Some(value);
|
||
continue
|
||
}
|
||
}
|
||
break
|
||
}
|
||
|
||
// Lengths must be specified.
|
||
if !lengths_parsed {
|
||
return Err(())
|
||
}
|
||
|
||
Ok(SpecifiedTextShadow {
|
||
offset_x: lengths[0],
|
||
offset_y: lengths[1],
|
||
blur_radius: lengths[2],
|
||
color: color,
|
||
})
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
computed_value::T(self.0.iter().map(|value| {
|
||
computed_value::TextShadow {
|
||
offset_x: value.offset_x.to_computed_value(context),
|
||
offset_y: value.offset_y.to_computed_value(context),
|
||
blur_radius: value.blur_radius.to_computed_value(context),
|
||
color: value.color
|
||
.as_ref()
|
||
.map(|color| color.parsed)
|
||
.unwrap_or(cssparser::Color::CurrentColor),
|
||
}
|
||
}).collect())
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
<%self:longhand name="filter">
|
||
//pub use self::computed_value::T as SpecifiedValue;
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
use values::AuExtensionMethods;
|
||
use values::CSSFloat;
|
||
use values::specified::{Angle, Length};
|
||
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct SpecifiedValue(Vec<SpecifiedFilter>);
|
||
|
||
// TODO(pcwalton): `drop-shadow`
|
||
#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
|
||
pub enum SpecifiedFilter {
|
||
Blur(Length),
|
||
Brightness(CSSFloat),
|
||
Contrast(CSSFloat),
|
||
Grayscale(CSSFloat),
|
||
HueRotate(Angle),
|
||
Invert(CSSFloat),
|
||
Opacity(CSSFloat),
|
||
Saturate(CSSFloat),
|
||
Sepia(CSSFloat),
|
||
}
|
||
|
||
pub mod computed_value {
|
||
use app_units::Au;
|
||
use values::CSSFloat;
|
||
use values::specified::{Angle};
|
||
|
||
#[derive(Clone, PartialEq, Debug, HeapSizeOf, Deserialize, Serialize)]
|
||
pub enum Filter {
|
||
Blur(Au),
|
||
Brightness(CSSFloat),
|
||
Contrast(CSSFloat),
|
||
Grayscale(CSSFloat),
|
||
HueRotate(Angle),
|
||
Invert(CSSFloat),
|
||
Opacity(CSSFloat),
|
||
Saturate(CSSFloat),
|
||
Sepia(CSSFloat),
|
||
}
|
||
|
||
#[derive(Clone, PartialEq, Debug, HeapSizeOf, Deserialize, Serialize)]
|
||
pub struct T { pub filters: Vec<Filter> }
|
||
|
||
impl T {
|
||
/// Creates a new filter pipeline.
|
||
#[inline]
|
||
pub fn new(filters: Vec<Filter>) -> T {
|
||
T
|
||
{
|
||
filters: filters,
|
||
}
|
||
}
|
||
|
||
/// Adds a new filter to the filter pipeline.
|
||
#[inline]
|
||
pub fn push(&mut self, filter: Filter) {
|
||
self.filters.push(filter)
|
||
}
|
||
|
||
/// Returns true if this filter pipeline is empty and false otherwise.
|
||
#[inline]
|
||
pub fn is_empty(&self) -> bool {
|
||
self.filters.is_empty()
|
||
}
|
||
|
||
/// Returns the resulting opacity of this filter pipeline.
|
||
#[inline]
|
||
pub fn opacity(&self) -> CSSFloat {
|
||
let mut opacity = 1.0;
|
||
|
||
for filter in &self.filters {
|
||
if let Filter::Opacity(ref opacity_value) = *filter {
|
||
opacity *= *opacity_value
|
||
}
|
||
}
|
||
opacity
|
||
}
|
||
}
|
||
}
|
||
|
||
impl ToCss for computed_value::T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
let mut iter = self.filters.iter();
|
||
if let Some(filter) = iter.next() {
|
||
try!(filter.to_css(dest));
|
||
} else {
|
||
try!(dest.write_str("none"));
|
||
return Ok(())
|
||
}
|
||
for filter in iter {
|
||
try!(dest.write_str(" "));
|
||
try!(filter.to_css(dest));
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
let mut iter = self.0.iter();
|
||
if let Some(filter) = iter.next() {
|
||
try!(filter.to_css(dest));
|
||
} else {
|
||
try!(dest.write_str("none"));
|
||
return Ok(())
|
||
}
|
||
for filter in iter {
|
||
try!(dest.write_str(" "));
|
||
try!(filter.to_css(dest));
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
impl ToCss for computed_value::Filter {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
computed_value::Filter::Blur(value) => {
|
||
try!(dest.write_str("blur("));
|
||
try!(value.to_css(dest));
|
||
try!(dest.write_str(")"));
|
||
}
|
||
computed_value::Filter::Brightness(value) => try!(write!(dest, "brightness({})", value)),
|
||
computed_value::Filter::Contrast(value) => try!(write!(dest, "contrast({})", value)),
|
||
computed_value::Filter::Grayscale(value) => try!(write!(dest, "grayscale({})", value)),
|
||
computed_value::Filter::HueRotate(value) => {
|
||
try!(dest.write_str("hue-rotate("));
|
||
try!(value.to_css(dest));
|
||
try!(dest.write_str(")"));
|
||
}
|
||
computed_value::Filter::Invert(value) => try!(write!(dest, "invert({})", value)),
|
||
computed_value::Filter::Opacity(value) => try!(write!(dest, "opacity({})", value)),
|
||
computed_value::Filter::Saturate(value) => try!(write!(dest, "saturate({})", value)),
|
||
computed_value::Filter::Sepia(value) => try!(write!(dest, "sepia({})", value)),
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
impl ToCss for SpecifiedFilter {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
SpecifiedFilter::Blur(value) => {
|
||
try!(dest.write_str("blur("));
|
||
try!(value.to_css(dest));
|
||
try!(dest.write_str(")"));
|
||
}
|
||
SpecifiedFilter::Brightness(value) => try!(write!(dest, "brightness({})", value)),
|
||
SpecifiedFilter::Contrast(value) => try!(write!(dest, "contrast({})", value)),
|
||
SpecifiedFilter::Grayscale(value) => try!(write!(dest, "grayscale({})", value)),
|
||
SpecifiedFilter::HueRotate(value) => {
|
||
try!(dest.write_str("hue-rotate("));
|
||
try!(value.to_css(dest));
|
||
try!(dest.write_str(")"));
|
||
}
|
||
SpecifiedFilter::Invert(value) => try!(write!(dest, "invert({})", value)),
|
||
SpecifiedFilter::Opacity(value) => try!(write!(dest, "opacity({})", value)),
|
||
SpecifiedFilter::Saturate(value) => try!(write!(dest, "saturate({})", value)),
|
||
SpecifiedFilter::Sepia(value) => try!(write!(dest, "sepia({})", value)),
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T::new(Vec::new())
|
||
}
|
||
|
||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||
let mut filters = Vec::new();
|
||
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||
return Ok(SpecifiedValue(filters))
|
||
}
|
||
loop {
|
||
if let Ok(function_name) = input.try(|input| input.expect_function()) {
|
||
filters.push(try!(input.parse_nested_block(|input| {
|
||
match_ignore_ascii_case! { function_name,
|
||
"blur" => specified::Length::parse_non_negative(input).map(SpecifiedFilter::Blur),
|
||
"brightness" => parse_factor(input).map(SpecifiedFilter::Brightness),
|
||
"contrast" => parse_factor(input).map(SpecifiedFilter::Contrast),
|
||
"grayscale" => parse_factor(input).map(SpecifiedFilter::Grayscale),
|
||
"hue-rotate" => Angle::parse(input).map(SpecifiedFilter::HueRotate),
|
||
"invert" => parse_factor(input).map(SpecifiedFilter::Invert),
|
||
"opacity" => parse_factor(input).map(SpecifiedFilter::Opacity),
|
||
"saturate" => parse_factor(input).map(SpecifiedFilter::Saturate),
|
||
"sepia" => parse_factor(input).map(SpecifiedFilter::Sepia),
|
||
_ => Err(())
|
||
}
|
||
})));
|
||
} else if filters.is_empty() {
|
||
return Err(())
|
||
} else {
|
||
return Ok(SpecifiedValue(filters))
|
||
}
|
||
}
|
||
}
|
||
|
||
fn parse_factor(input: &mut Parser) -> Result<::values::CSSFloat, ()> {
|
||
use cssparser::Token;
|
||
match input.next() {
|
||
Ok(Token::Number(value)) => Ok(value.value),
|
||
Ok(Token::Percentage(value)) => Ok(value.unit_value),
|
||
_ => Err(())
|
||
}
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
computed_value::T{ filters: self.0.iter().map(|value| {
|
||
match *value {
|
||
SpecifiedFilter::Blur(factor) =>
|
||
computed_value::Filter::Blur(factor.to_computed_value(context)),
|
||
SpecifiedFilter::Brightness(factor) => computed_value::Filter::Brightness(factor),
|
||
SpecifiedFilter::Contrast(factor) => computed_value::Filter::Contrast(factor),
|
||
SpecifiedFilter::Grayscale(factor) => computed_value::Filter::Grayscale(factor),
|
||
SpecifiedFilter::HueRotate(factor) => computed_value::Filter::HueRotate(factor),
|
||
SpecifiedFilter::Invert(factor) => computed_value::Filter::Invert(factor),
|
||
SpecifiedFilter::Opacity(factor) => computed_value::Filter::Opacity(factor),
|
||
SpecifiedFilter::Saturate(factor) => computed_value::Filter::Saturate(factor),
|
||
SpecifiedFilter::Sepia(factor) => computed_value::Filter::Sepia(factor),
|
||
}
|
||
}).collect() }
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
<%self:longhand name="transform">
|
||
use app_units::Au;
|
||
use values::CSSFloat;
|
||
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
|
||
pub mod computed_value {
|
||
use values::CSSFloat;
|
||
use values::computed;
|
||
|
||
#[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf)]
|
||
pub struct ComputedMatrix {
|
||
pub m11: CSSFloat, pub m12: CSSFloat, pub m13: CSSFloat, pub m14: CSSFloat,
|
||
pub m21: CSSFloat, pub m22: CSSFloat, pub m23: CSSFloat, pub m24: CSSFloat,
|
||
pub m31: CSSFloat, pub m32: CSSFloat, pub m33: CSSFloat, pub m34: CSSFloat,
|
||
pub m41: CSSFloat, pub m42: CSSFloat, pub m43: CSSFloat, pub m44: CSSFloat,
|
||
}
|
||
|
||
impl ComputedMatrix {
|
||
pub fn identity() -> ComputedMatrix {
|
||
ComputedMatrix {
|
||
m11: 1.0, m12: 0.0, m13: 0.0, m14: 0.0,
|
||
m21: 0.0, m22: 1.0, m23: 0.0, m24: 0.0,
|
||
m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0,
|
||
m41: 0.0, m42: 0.0, m43: 0.0, m44: 1.0
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, Debug, PartialEq, HeapSizeOf)]
|
||
pub enum ComputedOperation {
|
||
Matrix(ComputedMatrix),
|
||
Skew(computed::Angle, computed::Angle),
|
||
Translate(computed::LengthOrPercentage,
|
||
computed::LengthOrPercentage,
|
||
computed::Length),
|
||
Scale(CSSFloat, CSSFloat, CSSFloat),
|
||
Rotate(CSSFloat, CSSFloat, CSSFloat, computed::Angle),
|
||
Perspective(computed::Length),
|
||
}
|
||
|
||
#[derive(Clone, Debug, PartialEq, HeapSizeOf)]
|
||
pub struct T(pub Option<Vec<ComputedOperation>>);
|
||
}
|
||
|
||
pub use self::computed_value::ComputedMatrix as SpecifiedMatrix;
|
||
|
||
fn parse_two_lengths_or_percentages(input: &mut Parser)
|
||
-> Result<(specified::LengthOrPercentage,
|
||
specified::LengthOrPercentage),()> {
|
||
let first = try!(specified::LengthOrPercentage::parse(input));
|
||
let second = input.try(|input| {
|
||
try!(input.expect_comma());
|
||
specified::LengthOrPercentage::parse(input)
|
||
}).unwrap_or(specified::LengthOrPercentage::zero());
|
||
Ok((first, second))
|
||
}
|
||
|
||
fn parse_two_floats(input: &mut Parser) -> Result<(CSSFloat,CSSFloat),()> {
|
||
let first = try!(specified::parse_number(input));
|
||
let second = input.try(|input| {
|
||
try!(input.expect_comma());
|
||
specified::parse_number(input)
|
||
}).unwrap_or(first);
|
||
Ok((first, second))
|
||
}
|
||
|
||
fn parse_two_angles(input: &mut Parser) -> Result<(specified::Angle, specified::Angle),()> {
|
||
let first = try!(specified::Angle::parse(input));
|
||
let second = input.try(|input| {
|
||
try!(input.expect_comma());
|
||
specified::Angle::parse(input)
|
||
}).unwrap_or(specified::Angle(0.0));
|
||
Ok((first, second))
|
||
}
|
||
|
||
#[derive(Copy, Clone, Debug, PartialEq, HeapSizeOf)]
|
||
enum TranslateKind {
|
||
Translate,
|
||
TranslateX,
|
||
TranslateY,
|
||
TranslateZ,
|
||
Translate3D,
|
||
}
|
||
|
||
#[derive(Clone, Debug, PartialEq, HeapSizeOf)]
|
||
enum SpecifiedOperation {
|
||
Matrix(SpecifiedMatrix),
|
||
Skew(specified::Angle, specified::Angle),
|
||
Translate(TranslateKind,
|
||
specified::LengthOrPercentage,
|
||
specified::LengthOrPercentage,
|
||
specified::Length),
|
||
Scale(CSSFloat, CSSFloat, CSSFloat),
|
||
Rotate(CSSFloat, CSSFloat, CSSFloat, specified::Angle),
|
||
Perspective(specified::Length),
|
||
}
|
||
|
||
impl ToCss for computed_value::T {
|
||
fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
|
||
// TODO(pcwalton)
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
impl ToCss for SpecifiedOperation {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
// todo(gw): implement serialization for transform
|
||
// types other than translate.
|
||
SpecifiedOperation::Matrix(_m) => {
|
||
Ok(())
|
||
}
|
||
SpecifiedOperation::Skew(_sx, _sy) => {
|
||
Ok(())
|
||
}
|
||
SpecifiedOperation::Translate(kind, tx, ty, tz) => {
|
||
match kind {
|
||
TranslateKind::Translate => {
|
||
try!(dest.write_str("translate("));
|
||
try!(tx.to_css(dest));
|
||
try!(dest.write_str(", "));
|
||
try!(ty.to_css(dest));
|
||
dest.write_str(")")
|
||
}
|
||
TranslateKind::TranslateX => {
|
||
try!(dest.write_str("translateX("));
|
||
try!(tx.to_css(dest));
|
||
dest.write_str(")")
|
||
}
|
||
TranslateKind::TranslateY => {
|
||
try!(dest.write_str("translateY("));
|
||
try!(ty.to_css(dest));
|
||
dest.write_str(")")
|
||
}
|
||
TranslateKind::TranslateZ => {
|
||
try!(dest.write_str("translateZ("));
|
||
try!(tz.to_css(dest));
|
||
dest.write_str(")")
|
||
}
|
||
TranslateKind::Translate3D => {
|
||
try!(dest.write_str("translate3d("));
|
||
try!(tx.to_css(dest));
|
||
try!(dest.write_str(", "));
|
||
try!(ty.to_css(dest));
|
||
try!(dest.write_str(", "));
|
||
try!(tz.to_css(dest));
|
||
dest.write_str(")")
|
||
}
|
||
}
|
||
}
|
||
SpecifiedOperation::Scale(_sx, _sy, _sz) => {
|
||
Ok(())
|
||
}
|
||
SpecifiedOperation::Rotate(_ax, _ay, _az, _angle) => {
|
||
Ok(())
|
||
}
|
||
SpecifiedOperation::Perspective(_p) => {
|
||
Ok(())
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, Debug, PartialEq, HeapSizeOf)]
|
||
pub struct SpecifiedValue(Vec<SpecifiedOperation>);
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
let mut first = true;
|
||
for operation in &self.0 {
|
||
if !first {
|
||
try!(dest.write_str(" "));
|
||
}
|
||
first = false;
|
||
try!(operation.to_css(dest))
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T(None)
|
||
}
|
||
|
||
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||
return Ok(SpecifiedValue(Vec::new()))
|
||
}
|
||
|
||
let mut result = Vec::new();
|
||
loop {
|
||
let name = match input.expect_function() {
|
||
Ok(name) => name,
|
||
Err(_) => break,
|
||
};
|
||
match_ignore_ascii_case! {
|
||
name,
|
||
"matrix" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let values = try!(input.parse_comma_separated(|input| {
|
||
specified::parse_number(input)
|
||
}));
|
||
if values.len() != 6 {
|
||
return Err(())
|
||
}
|
||
result.push(SpecifiedOperation::Matrix(
|
||
SpecifiedMatrix {
|
||
m11: values[0], m12: values[1], m13: 0.0, m14: 0.0,
|
||
m21: values[2], m22: values[3], m23: 0.0, m24: 0.0,
|
||
m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0,
|
||
m41: values[4], m42: values[5], m43: 0.0, m44: 1.0
|
||
}));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"matrix3d" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let values = try!(input.parse_comma_separated(|input| {
|
||
specified::parse_number(input)
|
||
}));
|
||
if values.len() != 16 {
|
||
return Err(())
|
||
}
|
||
result.push(SpecifiedOperation::Matrix(
|
||
SpecifiedMatrix {
|
||
m11: values[ 0], m12: values[ 1], m13: values[ 2], m14: values[ 3],
|
||
m21: values[ 4], m22: values[ 5], m23: values[ 6], m24: values[ 7],
|
||
m31: values[ 8], m32: values[ 9], m33: values[10], m34: values[11],
|
||
m41: values[12], m42: values[13], m43: values[14], m44: values[15]
|
||
}));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"translate" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let (tx, ty) = try!(parse_two_lengths_or_percentages(input));
|
||
result.push(SpecifiedOperation::Translate(TranslateKind::Translate,
|
||
tx,
|
||
ty,
|
||
specified::Length::Absolute(Au(0))));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"translatex" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let tx = try!(specified::LengthOrPercentage::parse(input));
|
||
result.push(SpecifiedOperation::Translate(
|
||
TranslateKind::TranslateX,
|
||
tx,
|
||
specified::LengthOrPercentage::zero(),
|
||
specified::Length::Absolute(Au(0))));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"translatey" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let ty = try!(specified::LengthOrPercentage::parse(input));
|
||
result.push(SpecifiedOperation::Translate(
|
||
TranslateKind::TranslateY,
|
||
specified::LengthOrPercentage::zero(),
|
||
ty,
|
||
specified::Length::Absolute(Au(0))));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"translatez" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let tz = try!(specified::Length::parse(input));
|
||
result.push(SpecifiedOperation::Translate(
|
||
TranslateKind::TranslateZ,
|
||
specified::LengthOrPercentage::zero(),
|
||
specified::LengthOrPercentage::zero(),
|
||
tz));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"translate3d" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let tx = try!(specified::LengthOrPercentage::parse(input));
|
||
try!(input.expect_comma());
|
||
let ty = try!(specified::LengthOrPercentage::parse(input));
|
||
try!(input.expect_comma());
|
||
let tz = try!(specified::Length::parse(input));
|
||
result.push(SpecifiedOperation::Translate(
|
||
TranslateKind::Translate3D,
|
||
tx,
|
||
ty,
|
||
tz));
|
||
Ok(())
|
||
}))
|
||
|
||
},
|
||
"scale" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let (sx, sy) = try!(parse_two_floats(input));
|
||
result.push(SpecifiedOperation::Scale(sx, sy, 1.0));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"scalex" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let sx = try!(specified::parse_number(input));
|
||
result.push(SpecifiedOperation::Scale(sx, 1.0, 1.0));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"scaley" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let sy = try!(specified::parse_number(input));
|
||
result.push(SpecifiedOperation::Scale(1.0, sy, 1.0));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"scalez" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let sz = try!(specified::parse_number(input));
|
||
result.push(SpecifiedOperation::Scale(1.0, 1.0, sz));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"scale3d" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let sx = try!(specified::parse_number(input));
|
||
try!(input.expect_comma());
|
||
let sy = try!(specified::parse_number(input));
|
||
try!(input.expect_comma());
|
||
let sz = try!(specified::parse_number(input));
|
||
result.push(SpecifiedOperation::Scale(sx, sy, sz));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"rotate" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let theta = try!(specified::Angle::parse(input));
|
||
result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"rotatex" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let theta = try!(specified::Angle::parse(input));
|
||
result.push(SpecifiedOperation::Rotate(1.0, 0.0, 0.0, theta));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"rotatey" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let theta = try!(specified::Angle::parse(input));
|
||
result.push(SpecifiedOperation::Rotate(0.0, 1.0, 0.0, theta));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"rotatez" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let theta = try!(specified::Angle::parse(input));
|
||
result.push(SpecifiedOperation::Rotate(0.0, 0.0, 1.0, theta));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"rotate3d" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let ax = try!(specified::parse_number(input));
|
||
try!(input.expect_comma());
|
||
let ay = try!(specified::parse_number(input));
|
||
try!(input.expect_comma());
|
||
let az = try!(specified::parse_number(input));
|
||
try!(input.expect_comma());
|
||
let theta = try!(specified::Angle::parse(input));
|
||
// TODO(gw): Check the axis can be normalized!!
|
||
result.push(SpecifiedOperation::Rotate(ax, ay, az, theta));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"skew" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let (theta_x, theta_y) = try!(parse_two_angles(input));
|
||
result.push(SpecifiedOperation::Skew(theta_x, theta_y));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"skewx" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let theta_x = try!(specified::Angle::parse(input));
|
||
result.push(SpecifiedOperation::Skew(theta_x, specified::Angle(0.0)));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"skewy" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let theta_y = try!(specified::Angle::parse(input));
|
||
result.push(SpecifiedOperation::Skew(specified::Angle(0.0), theta_y));
|
||
Ok(())
|
||
}))
|
||
},
|
||
"perspective" => {
|
||
try!(input.parse_nested_block(|input| {
|
||
let d = try!(specified::Length::parse(input));
|
||
result.push(SpecifiedOperation::Perspective(d));
|
||
Ok(())
|
||
}))
|
||
},
|
||
_ => return Err(())
|
||
}
|
||
}
|
||
|
||
if !result.is_empty() {
|
||
Ok(SpecifiedValue(result))
|
||
} else {
|
||
Err(())
|
||
}
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
if self.0.is_empty() {
|
||
return computed_value::T(None)
|
||
}
|
||
|
||
let mut result = vec!();
|
||
for operation in &self.0 {
|
||
match *operation {
|
||
SpecifiedOperation::Matrix(ref matrix) => {
|
||
result.push(computed_value::ComputedOperation::Matrix(*matrix));
|
||
}
|
||
SpecifiedOperation::Translate(_, ref tx, ref ty, ref tz) => {
|
||
result.push(computed_value::ComputedOperation::Translate(tx.to_computed_value(context),
|
||
ty.to_computed_value(context),
|
||
tz.to_computed_value(context)));
|
||
}
|
||
SpecifiedOperation::Scale(sx, sy, sz) => {
|
||
result.push(computed_value::ComputedOperation::Scale(sx, sy, sz));
|
||
}
|
||
SpecifiedOperation::Rotate(ax, ay, az, theta) => {
|
||
result.push(computed_value::ComputedOperation::Rotate(ax, ay, az, theta));
|
||
}
|
||
SpecifiedOperation::Skew(theta_x, theta_y) => {
|
||
result.push(computed_value::ComputedOperation::Skew(theta_x, theta_y));
|
||
}
|
||
SpecifiedOperation::Perspective(d) => {
|
||
result.push(computed_value::ComputedOperation::Perspective(d.to_computed_value(context)));
|
||
}
|
||
};
|
||
}
|
||
|
||
computed_value::T(Some(result))
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
pub struct OriginParseResult {
|
||
horizontal: Option<specified::LengthOrPercentage>,
|
||
vertical: Option<specified::LengthOrPercentage>,
|
||
depth: Option<specified::Length>
|
||
}
|
||
|
||
pub fn parse_origin(_: &ParserContext, input: &mut Parser) -> Result<OriginParseResult,()> {
|
||
use values::specified::{LengthOrPercentage, Percentage};
|
||
let (mut horizontal, mut vertical, mut depth) = (None, None, None);
|
||
loop {
|
||
if let Err(_) = input.try(|input| {
|
||
let token = try!(input.expect_ident());
|
||
match_ignore_ascii_case! {
|
||
token,
|
||
"left" => {
|
||
if horizontal.is_none() {
|
||
horizontal = Some(LengthOrPercentage::Percentage(Percentage(0.0)))
|
||
} else {
|
||
return Err(())
|
||
}
|
||
},
|
||
"center" => {
|
||
if horizontal.is_none() {
|
||
horizontal = Some(LengthOrPercentage::Percentage(Percentage(0.5)))
|
||
} else if vertical.is_none() {
|
||
vertical = Some(LengthOrPercentage::Percentage(Percentage(0.5)))
|
||
} else {
|
||
return Err(())
|
||
}
|
||
},
|
||
"right" => {
|
||
if horizontal.is_none() {
|
||
horizontal = Some(LengthOrPercentage::Percentage(Percentage(1.0)))
|
||
} else {
|
||
return Err(())
|
||
}
|
||
},
|
||
"top" => {
|
||
if vertical.is_none() {
|
||
vertical = Some(LengthOrPercentage::Percentage(Percentage(0.0)))
|
||
} else {
|
||
return Err(())
|
||
}
|
||
},
|
||
"bottom" => {
|
||
if vertical.is_none() {
|
||
vertical = Some(LengthOrPercentage::Percentage(Percentage(1.0)))
|
||
} else {
|
||
return Err(())
|
||
}
|
||
},
|
||
_ => return Err(())
|
||
}
|
||
Ok(())
|
||
}) {
|
||
match LengthOrPercentage::parse(input) {
|
||
Ok(value) => {
|
||
if horizontal.is_none() {
|
||
horizontal = Some(value);
|
||
} else if vertical.is_none() {
|
||
vertical = Some(value);
|
||
} else if let LengthOrPercentage::Length(length) = value {
|
||
depth = Some(length);
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
_ => break,
|
||
}
|
||
}
|
||
}
|
||
|
||
if horizontal.is_some() || vertical.is_some() {
|
||
Ok(OriginParseResult {
|
||
horizontal: horizontal,
|
||
vertical: vertical,
|
||
depth: depth,
|
||
})
|
||
} else {
|
||
Err(())
|
||
}
|
||
}
|
||
|
||
${single_keyword("backface-visibility", "visible hidden")}
|
||
|
||
${single_keyword("transform-style", "auto flat preserve-3d")}
|
||
|
||
<%self:longhand name="transform-origin">
|
||
use app_units::Au;
|
||
use values::AuExtensionMethods;
|
||
use values::specified::{Length, LengthOrPercentage, Percentage};
|
||
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
|
||
pub mod computed_value {
|
||
use values::computed::{Length, LengthOrPercentage};
|
||
|
||
#[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf)]
|
||
pub struct T {
|
||
pub horizontal: LengthOrPercentage,
|
||
pub vertical: LengthOrPercentage,
|
||
pub depth: Length,
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf)]
|
||
pub struct SpecifiedValue {
|
||
horizontal: LengthOrPercentage,
|
||
vertical: LengthOrPercentage,
|
||
depth: Length,
|
||
}
|
||
|
||
impl ToCss for computed_value::T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
try!(self.horizontal.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
try!(self.vertical.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
self.depth.to_css(dest)
|
||
}
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
try!(self.horizontal.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
try!(self.vertical.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
self.depth.to_css(dest)
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T {
|
||
horizontal: computed::LengthOrPercentage::Percentage(0.5),
|
||
vertical: computed::LengthOrPercentage::Percentage(0.5),
|
||
depth: Au(0),
|
||
}
|
||
}
|
||
|
||
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||
let result = try!(super::parse_origin(context, input));
|
||
Ok(SpecifiedValue {
|
||
horizontal: result.horizontal.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))),
|
||
vertical: result.vertical.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))),
|
||
depth: result.depth.unwrap_or(Length::Absolute(Au(0))),
|
||
})
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
computed_value::T {
|
||
horizontal: self.horizontal.to_computed_value(context),
|
||
vertical: self.vertical.to_computed_value(context),
|
||
depth: self.depth.to_computed_value(context),
|
||
}
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
${predefined_type("perspective",
|
||
"LengthOrNone",
|
||
"computed::LengthOrNone::None")}
|
||
|
||
<%self:longhand name="perspective-origin">
|
||
use values::specified::{LengthOrPercentage, Percentage};
|
||
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
|
||
pub mod computed_value {
|
||
use values::computed::LengthOrPercentage;
|
||
|
||
#[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf)]
|
||
pub struct T {
|
||
pub horizontal: LengthOrPercentage,
|
||
pub vertical: LengthOrPercentage,
|
||
}
|
||
}
|
||
|
||
impl ToCss for computed_value::T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
try!(self.horizontal.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
self.vertical.to_css(dest)
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, Copy, Debug, PartialEq, HeapSizeOf)]
|
||
pub struct SpecifiedValue {
|
||
horizontal: LengthOrPercentage,
|
||
vertical: LengthOrPercentage,
|
||
}
|
||
|
||
impl ToCss for SpecifiedValue {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
try!(self.horizontal.to_css(dest));
|
||
try!(dest.write_str(" "));
|
||
self.vertical.to_css(dest)
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T {
|
||
horizontal: computed::LengthOrPercentage::Percentage(0.5),
|
||
vertical: computed::LengthOrPercentage::Percentage(0.5),
|
||
}
|
||
}
|
||
|
||
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||
let result = try!(super::parse_origin(context, input));
|
||
match result.depth {
|
||
Some(_) => Err(()),
|
||
None => Ok(SpecifiedValue {
|
||
horizontal: result.horizontal.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))),
|
||
vertical: result.vertical.unwrap_or(LengthOrPercentage::Percentage(Percentage(0.5))),
|
||
})
|
||
}
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
|
||
computed_value::T {
|
||
horizontal: self.horizontal.to_computed_value(context),
|
||
vertical: self.vertical.to_computed_value(context),
|
||
}
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
${single_keyword("mix-blend-mode",
|
||
"""normal multiply screen overlay darken lighten color-dodge
|
||
color-burn hard-light soft-light difference exclusion hue
|
||
saturation color luminosity""")}
|
||
|
||
<%self:longhand name="image-rendering">
|
||
|
||
pub mod computed_value {
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
|
||
#[derive(Copy, Clone, Debug, PartialEq, HeapSizeOf, Deserialize, Serialize)]
|
||
pub enum T {
|
||
Auto,
|
||
CrispEdges,
|
||
Pixelated,
|
||
}
|
||
|
||
impl ToCss for T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
T::Auto => dest.write_str("auto"),
|
||
T::CrispEdges => dest.write_str("crisp-edges"),
|
||
T::Pixelated => dest.write_str("pixelated"),
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
pub type SpecifiedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T::Auto
|
||
}
|
||
|
||
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||
// According to to CSS-IMAGES-3, `optimizespeed` and `optimizequality` are synonyms for
|
||
// `auto`.
|
||
match_ignore_ascii_case! {
|
||
try!(input.expect_ident()),
|
||
"auto" => Ok(computed_value::T::Auto),
|
||
"optimizespeed" => Ok(computed_value::T::Auto),
|
||
"optimizequality" => Ok(computed_value::T::Auto),
|
||
"crisp-edges" => Ok(computed_value::T::CrispEdges),
|
||
"pixelated" => Ok(computed_value::T::Pixelated),
|
||
_ => Err(())
|
||
}
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, _: &Cx) -> computed_value::T {
|
||
*self
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
${new_style_struct("Animation", is_inherited=False)}
|
||
|
||
// TODO(pcwalton): Multiple transitions.
|
||
<%self:longhand name="transition-duration">
|
||
use values::specified::Time;
|
||
|
||
pub use self::computed_value::T as SpecifiedValue;
|
||
pub use values::specified::Time as SingleSpecifiedValue;
|
||
|
||
pub mod computed_value {
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
use values::computed::{TContext, ToComputedValue};
|
||
|
||
pub use values::computed::Time as SingleComputedValue;
|
||
|
||
#[derive(Debug, Clone, PartialEq, HeapSizeOf)]
|
||
pub struct T(pub Vec<SingleComputedValue>);
|
||
|
||
impl ToComputedValue for T {
|
||
type ComputedValue = T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, _: &Cx) -> T {
|
||
(*self).clone()
|
||
}
|
||
}
|
||
|
||
impl ToCss for T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
if self.0.is_empty() {
|
||
return dest.write_str("none")
|
||
}
|
||
for (i, value) in self.0.iter().enumerate() {
|
||
if i != 0 {
|
||
try!(dest.write_str(", "))
|
||
}
|
||
try!(value.to_css(dest))
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn parse_one(input: &mut Parser) -> Result<SingleSpecifiedValue,()> {
|
||
Time::parse(input)
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T(vec![get_initial_single_value()])
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_single_value() -> Time {
|
||
Time(0.0)
|
||
}
|
||
|
||
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||
Ok(SpecifiedValue(try!(input.parse_comma_separated(parse_one))))
|
||
}
|
||
</%self:longhand>
|
||
|
||
// TODO(pcwalton): Lots more timing functions.
|
||
// TODO(pcwalton): Multiple transitions.
|
||
<%self:longhand name="transition-timing-function">
|
||
use self::computed_value::{StartEnd, TransitionTimingFunction};
|
||
|
||
use euclid::point::Point2D;
|
||
|
||
pub use self::computed_value::SingleComputedValue as SingleSpecifiedValue;
|
||
pub use self::computed_value::T as SpecifiedValue;
|
||
|
||
static EASE: TransitionTimingFunction = TransitionTimingFunction::CubicBezier(Point2D {
|
||
x: 0.25,
|
||
y: 0.1,
|
||
}, Point2D {
|
||
x: 0.25,
|
||
y: 1.0,
|
||
});
|
||
static LINEAR: TransitionTimingFunction = TransitionTimingFunction::CubicBezier(Point2D {
|
||
x: 0.0,
|
||
y: 0.0,
|
||
}, Point2D {
|
||
x: 1.0,
|
||
y: 1.0,
|
||
});
|
||
static EASE_IN: TransitionTimingFunction = TransitionTimingFunction::CubicBezier(Point2D {
|
||
x: 0.42,
|
||
y: 0.0,
|
||
}, Point2D {
|
||
x: 1.0,
|
||
y: 1.0,
|
||
});
|
||
static EASE_OUT: TransitionTimingFunction = TransitionTimingFunction::CubicBezier(Point2D {
|
||
x: 0.0,
|
||
y: 0.0,
|
||
}, Point2D {
|
||
x: 0.58,
|
||
y: 1.0,
|
||
});
|
||
static EASE_IN_OUT: TransitionTimingFunction =
|
||
TransitionTimingFunction::CubicBezier(Point2D {
|
||
x: 0.42,
|
||
y: 0.0,
|
||
}, Point2D {
|
||
x: 0.58,
|
||
y: 1.0,
|
||
});
|
||
static STEP_START: TransitionTimingFunction =
|
||
TransitionTimingFunction::Steps(1, StartEnd::Start);
|
||
static STEP_END: TransitionTimingFunction =
|
||
TransitionTimingFunction::Steps(1, StartEnd::End);
|
||
|
||
pub mod computed_value {
|
||
use cssparser::ToCss;
|
||
use euclid::point::Point2D;
|
||
use std::fmt;
|
||
|
||
pub use self::TransitionTimingFunction as SingleComputedValue;
|
||
|
||
#[derive(Copy, Clone, Debug, PartialEq, HeapSizeOf)]
|
||
pub enum TransitionTimingFunction {
|
||
CubicBezier(Point2D<f32>, Point2D<f32>),
|
||
Steps(u32, StartEnd),
|
||
}
|
||
|
||
impl ToCss for TransitionTimingFunction {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
TransitionTimingFunction::CubicBezier(p1, p2) => {
|
||
try!(dest.write_str("cubic-bezier("));
|
||
try!(p1.x.to_css(dest));
|
||
try!(dest.write_str(", "));
|
||
try!(p1.y.to_css(dest));
|
||
try!(dest.write_str(", "));
|
||
try!(p2.x.to_css(dest));
|
||
try!(dest.write_str(", "));
|
||
try!(p2.y.to_css(dest));
|
||
dest.write_str(")")
|
||
}
|
||
TransitionTimingFunction::Steps(steps, start_end) => {
|
||
try!(dest.write_str("steps("));
|
||
try!(steps.to_css(dest));
|
||
try!(dest.write_str(", "));
|
||
try!(start_end.to_css(dest));
|
||
dest.write_str(")")
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Copy, Clone, Debug, PartialEq, HeapSizeOf)]
|
||
pub enum StartEnd {
|
||
Start,
|
||
End,
|
||
}
|
||
|
||
impl ToCss for StartEnd {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
StartEnd::Start => dest.write_str("start"),
|
||
StartEnd::End => dest.write_str("end"),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, Debug, PartialEq, HeapSizeOf)]
|
||
pub struct T(pub Vec<TransitionTimingFunction>);
|
||
|
||
impl ToCss for T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
if self.0.is_empty() {
|
||
return dest.write_str("none")
|
||
}
|
||
for (i, value) in self.0.iter().enumerate() {
|
||
if i != 0 {
|
||
try!(dest.write_str(", "))
|
||
}
|
||
try!(value.to_css(dest))
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, _: &Cx) -> computed_value::T {
|
||
(*self).clone()
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T(vec![get_initial_single_value()])
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_single_value() -> TransitionTimingFunction {
|
||
EASE
|
||
}
|
||
|
||
pub fn parse_one(input: &mut Parser) -> Result<SingleSpecifiedValue,()> {
|
||
if let Ok(function_name) = input.try(|input| input.expect_function()) {
|
||
return match_ignore_ascii_case! {
|
||
function_name,
|
||
"cubic-bezier" => {
|
||
let (mut p1x, mut p1y, mut p2x, mut p2y) = (0.0, 0.0, 0.0, 0.0);
|
||
try!(input.parse_nested_block(|input| {
|
||
p1x = try!(specified::parse_number(input));
|
||
try!(input.expect_comma());
|
||
p1y = try!(specified::parse_number(input));
|
||
try!(input.expect_comma());
|
||
p2x = try!(specified::parse_number(input));
|
||
try!(input.expect_comma());
|
||
p2y = try!(specified::parse_number(input));
|
||
Ok(())
|
||
}));
|
||
let (p1, p2) = (Point2D::new(p1x, p1y), Point2D::new(p2x, p2y));
|
||
Ok(TransitionTimingFunction::CubicBezier(p1, p2))
|
||
},
|
||
"steps" => {
|
||
let (mut step_count, mut start_end) = (0, computed_value::StartEnd::Start);
|
||
try!(input.parse_nested_block(|input| {
|
||
step_count = try!(specified::parse_integer(input));
|
||
try!(input.expect_comma());
|
||
start_end = try!(match_ignore_ascii_case! {
|
||
try!(input.expect_ident()),
|
||
"start" => Ok(computed_value::StartEnd::Start),
|
||
"end" => Ok(computed_value::StartEnd::End),
|
||
_ => Err(())
|
||
});
|
||
Ok(())
|
||
}));
|
||
Ok(TransitionTimingFunction::Steps(step_count as u32, start_end))
|
||
},
|
||
_ => Err(())
|
||
}
|
||
}
|
||
match_ignore_ascii_case! {
|
||
try!(input.expect_ident()),
|
||
"ease" => Ok(EASE),
|
||
"linear" => Ok(LINEAR),
|
||
"ease-in" => Ok(EASE_IN),
|
||
"ease-out" => Ok(EASE_OUT),
|
||
"ease-in-out" => Ok(EASE_IN_OUT),
|
||
"step-start" => Ok(STEP_START),
|
||
"step-end" => Ok(STEP_END),
|
||
_ => Err(())
|
||
}
|
||
}
|
||
|
||
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||
Ok(SpecifiedValue(try!(input.parse_comma_separated(parse_one))))
|
||
}
|
||
</%self:longhand>
|
||
|
||
// TODO(pcwalton): Lots more properties.
|
||
<%self:longhand name="transition-property">
|
||
use self::computed_value::TransitionProperty;
|
||
|
||
pub use self::computed_value::SingleComputedValue as SingleSpecifiedValue;
|
||
pub use self::computed_value::T as SpecifiedValue;
|
||
|
||
pub mod computed_value {
|
||
use cssparser::ToCss;
|
||
use std::fmt;
|
||
|
||
pub use self::TransitionProperty as SingleComputedValue;
|
||
|
||
#[derive(Copy, Clone, Debug, PartialEq, HeapSizeOf)]
|
||
pub enum TransitionProperty {
|
||
All,
|
||
BackgroundColor,
|
||
BackgroundPosition,
|
||
BorderBottomColor,
|
||
BorderBottomWidth,
|
||
BorderLeftColor,
|
||
BorderLeftWidth,
|
||
BorderRightColor,
|
||
BorderRightWidth,
|
||
BorderSpacing,
|
||
BorderTopColor,
|
||
BorderTopWidth,
|
||
Bottom,
|
||
Color,
|
||
Clip,
|
||
FontSize,
|
||
FontWeight,
|
||
Height,
|
||
Left,
|
||
LetterSpacing,
|
||
LineHeight,
|
||
MarginBottom,
|
||
MarginLeft,
|
||
MarginRight,
|
||
MarginTop,
|
||
MaxHeight,
|
||
MaxWidth,
|
||
MinHeight,
|
||
MinWidth,
|
||
Opacity,
|
||
OutlineColor,
|
||
OutlineWidth,
|
||
PaddingBottom,
|
||
PaddingLeft,
|
||
PaddingRight,
|
||
PaddingTop,
|
||
Right,
|
||
TextIndent,
|
||
TextShadow,
|
||
Top,
|
||
Transform,
|
||
VerticalAlign,
|
||
Visibility,
|
||
Width,
|
||
WordSpacing,
|
||
ZIndex,
|
||
}
|
||
|
||
pub static ALL_TRANSITION_PROPERTIES: [TransitionProperty; 45] = [
|
||
TransitionProperty::BackgroundColor,
|
||
TransitionProperty::BackgroundPosition,
|
||
TransitionProperty::BorderBottomColor,
|
||
TransitionProperty::BorderBottomWidth,
|
||
TransitionProperty::BorderLeftColor,
|
||
TransitionProperty::BorderLeftWidth,
|
||
TransitionProperty::BorderRightColor,
|
||
TransitionProperty::BorderRightWidth,
|
||
TransitionProperty::BorderSpacing,
|
||
TransitionProperty::BorderTopColor,
|
||
TransitionProperty::BorderTopWidth,
|
||
TransitionProperty::Bottom,
|
||
TransitionProperty::Color,
|
||
TransitionProperty::Clip,
|
||
TransitionProperty::FontSize,
|
||
TransitionProperty::FontWeight,
|
||
TransitionProperty::Height,
|
||
TransitionProperty::Left,
|
||
TransitionProperty::LetterSpacing,
|
||
TransitionProperty::LineHeight,
|
||
TransitionProperty::MarginBottom,
|
||
TransitionProperty::MarginLeft,
|
||
TransitionProperty::MarginRight,
|
||
TransitionProperty::MarginTop,
|
||
TransitionProperty::MaxHeight,
|
||
TransitionProperty::MaxWidth,
|
||
TransitionProperty::MinHeight,
|
||
TransitionProperty::MinWidth,
|
||
TransitionProperty::Opacity,
|
||
TransitionProperty::OutlineColor,
|
||
TransitionProperty::OutlineWidth,
|
||
TransitionProperty::PaddingBottom,
|
||
TransitionProperty::PaddingLeft,
|
||
TransitionProperty::PaddingRight,
|
||
TransitionProperty::PaddingTop,
|
||
TransitionProperty::Right,
|
||
TransitionProperty::TextIndent,
|
||
TransitionProperty::TextShadow,
|
||
TransitionProperty::Top,
|
||
TransitionProperty::Transform,
|
||
TransitionProperty::VerticalAlign,
|
||
TransitionProperty::Visibility,
|
||
TransitionProperty::Width,
|
||
TransitionProperty::WordSpacing,
|
||
TransitionProperty::ZIndex,
|
||
];
|
||
|
||
impl ToCss for TransitionProperty {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
TransitionProperty::All => dest.write_str("all"),
|
||
TransitionProperty::BackgroundColor => dest.write_str("background-color"),
|
||
TransitionProperty::BackgroundPosition => dest.write_str("background-position"),
|
||
TransitionProperty::BorderBottomColor => dest.write_str("border-bottom-color"),
|
||
TransitionProperty::BorderBottomWidth => dest.write_str("border-bottom-width"),
|
||
TransitionProperty::BorderLeftColor => dest.write_str("border-left-color"),
|
||
TransitionProperty::BorderLeftWidth => dest.write_str("border-left-width"),
|
||
TransitionProperty::BorderRightColor => dest.write_str("border-right-color"),
|
||
TransitionProperty::BorderRightWidth => dest.write_str("border-right-width"),
|
||
TransitionProperty::BorderSpacing => dest.write_str("border-spacing"),
|
||
TransitionProperty::BorderTopColor => dest.write_str("border-top-color"),
|
||
TransitionProperty::BorderTopWidth => dest.write_str("border-top-width"),
|
||
TransitionProperty::Bottom => dest.write_str("bottom"),
|
||
TransitionProperty::Color => dest.write_str("color"),
|
||
TransitionProperty::Clip => dest.write_str("clip"),
|
||
TransitionProperty::FontSize => dest.write_str("font-size"),
|
||
TransitionProperty::FontWeight => dest.write_str("font-weight"),
|
||
TransitionProperty::Height => dest.write_str("height"),
|
||
TransitionProperty::Left => dest.write_str("left"),
|
||
TransitionProperty::LetterSpacing => dest.write_str("letter-spacing"),
|
||
TransitionProperty::LineHeight => dest.write_str("line-height"),
|
||
TransitionProperty::MarginBottom => dest.write_str("margin-bottom"),
|
||
TransitionProperty::MarginLeft => dest.write_str("margin-left"),
|
||
TransitionProperty::MarginRight => dest.write_str("margin-right"),
|
||
TransitionProperty::MarginTop => dest.write_str("margin-top"),
|
||
TransitionProperty::MaxHeight => dest.write_str("max-height"),
|
||
TransitionProperty::MaxWidth => dest.write_str("max-width"),
|
||
TransitionProperty::MinHeight => dest.write_str("min-height"),
|
||
TransitionProperty::MinWidth => dest.write_str("min-width"),
|
||
TransitionProperty::Opacity => dest.write_str("opacity"),
|
||
TransitionProperty::OutlineColor => dest.write_str("outline-color"),
|
||
TransitionProperty::OutlineWidth => dest.write_str("outline-width"),
|
||
TransitionProperty::PaddingBottom => dest.write_str("padding-bottom"),
|
||
TransitionProperty::PaddingLeft => dest.write_str("padding-left"),
|
||
TransitionProperty::PaddingRight => dest.write_str("padding-right"),
|
||
TransitionProperty::PaddingTop => dest.write_str("padding-top"),
|
||
TransitionProperty::Right => dest.write_str("right"),
|
||
TransitionProperty::TextIndent => dest.write_str("text-indent"),
|
||
TransitionProperty::TextShadow => dest.write_str("text-shadow"),
|
||
TransitionProperty::Top => dest.write_str("top"),
|
||
TransitionProperty::Transform => dest.write_str("transform"),
|
||
TransitionProperty::VerticalAlign => dest.write_str("vertical-align"),
|
||
TransitionProperty::Visibility => dest.write_str("visibility"),
|
||
TransitionProperty::Width => dest.write_str("width"),
|
||
TransitionProperty::WordSpacing => dest.write_str("word-spacing"),
|
||
TransitionProperty::ZIndex => dest.write_str("z-index"),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, Debug, PartialEq, HeapSizeOf)]
|
||
pub struct T(pub Vec<SingleComputedValue>);
|
||
|
||
impl ToCss for T {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
if self.0.is_empty() {
|
||
return dest.write_str("none")
|
||
}
|
||
for (i, value) in self.0.iter().enumerate() {
|
||
if i != 0 {
|
||
try!(dest.write_str(", "))
|
||
}
|
||
try!(value.to_css(dest))
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_initial_value() -> computed_value::T {
|
||
computed_value::T(Vec::new())
|
||
}
|
||
|
||
pub fn parse_one(input: &mut Parser) -> Result<SingleSpecifiedValue,()> {
|
||
match_ignore_ascii_case! {
|
||
try!(input.expect_ident()),
|
||
"all" => Ok(TransitionProperty::All),
|
||
"background-color" => Ok(TransitionProperty::BackgroundColor),
|
||
"background-position" => Ok(TransitionProperty::BackgroundPosition),
|
||
"border-bottom-color" => Ok(TransitionProperty::BorderBottomColor),
|
||
"border-bottom-width" => Ok(TransitionProperty::BorderBottomWidth),
|
||
"border-left-color" => Ok(TransitionProperty::BorderLeftColor),
|
||
"border-left-width" => Ok(TransitionProperty::BorderLeftWidth),
|
||
"border-right-color" => Ok(TransitionProperty::BorderRightColor),
|
||
"border-right-width" => Ok(TransitionProperty::BorderRightWidth),
|
||
"border-spacing" => Ok(TransitionProperty::BorderSpacing),
|
||
"border-top-color" => Ok(TransitionProperty::BorderTopColor),
|
||
"border-top-width" => Ok(TransitionProperty::BorderTopWidth),
|
||
"bottom" => Ok(TransitionProperty::Bottom),
|
||
"color" => Ok(TransitionProperty::Color),
|
||
"clip" => Ok(TransitionProperty::Clip),
|
||
"font-size" => Ok(TransitionProperty::FontSize),
|
||
"font-weight" => Ok(TransitionProperty::FontWeight),
|
||
"height" => Ok(TransitionProperty::Height),
|
||
"left" => Ok(TransitionProperty::Left),
|
||
"letter-spacing" => Ok(TransitionProperty::LetterSpacing),
|
||
"line-height" => Ok(TransitionProperty::LineHeight),
|
||
"margin-bottom" => Ok(TransitionProperty::MarginBottom),
|
||
"margin-left" => Ok(TransitionProperty::MarginLeft),
|
||
"margin-right" => Ok(TransitionProperty::MarginRight),
|
||
"margin-top" => Ok(TransitionProperty::MarginTop),
|
||
"max-height" => Ok(TransitionProperty::MaxHeight),
|
||
"max-width" => Ok(TransitionProperty::MaxWidth),
|
||
"min-height" => Ok(TransitionProperty::MinHeight),
|
||
"min-width" => Ok(TransitionProperty::MinWidth),
|
||
"opacity" => Ok(TransitionProperty::Opacity),
|
||
"outline-color" => Ok(TransitionProperty::OutlineColor),
|
||
"outline-width" => Ok(TransitionProperty::OutlineWidth),
|
||
"padding-bottom" => Ok(TransitionProperty::PaddingBottom),
|
||
"padding-left" => Ok(TransitionProperty::PaddingLeft),
|
||
"padding-right" => Ok(TransitionProperty::PaddingRight),
|
||
"padding-top" => Ok(TransitionProperty::PaddingTop),
|
||
"right" => Ok(TransitionProperty::Right),
|
||
"text-indent" => Ok(TransitionProperty::TextIndent),
|
||
"text-shadow" => Ok(TransitionProperty::TextShadow),
|
||
"top" => Ok(TransitionProperty::Top),
|
||
"transform" => Ok(TransitionProperty::Transform),
|
||
"vertical-align" => Ok(TransitionProperty::VerticalAlign),
|
||
"visibility" => Ok(TransitionProperty::Visibility),
|
||
"width" => Ok(TransitionProperty::Width),
|
||
"word-spacing" => Ok(TransitionProperty::WordSpacing),
|
||
"z-index" => Ok(TransitionProperty::ZIndex),
|
||
_ => Err(())
|
||
}
|
||
}
|
||
|
||
pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
|
||
Ok(SpecifiedValue(try!(input.parse_comma_separated(parse_one))))
|
||
}
|
||
|
||
impl ToComputedValue for SpecifiedValue {
|
||
type ComputedValue = computed_value::T;
|
||
|
||
#[inline]
|
||
fn to_computed_value<Cx: TContext>(&self, _: &Cx) -> computed_value::T {
|
||
(*self).clone()
|
||
}
|
||
}
|
||
</%self:longhand>
|
||
|
||
<%self:longhand name="transition-delay">
|
||
pub use properties::longhands::transition_duration::{SingleSpecifiedValue, SpecifiedValue};
|
||
pub use properties::longhands::transition_duration::{computed_value};
|
||
pub use properties::longhands::transition_duration::{get_initial_single_value};
|
||
pub use properties::longhands::transition_duration::{get_initial_value, parse, parse_one};
|
||
</%self:longhand>
|
||
|
||
// CSS Flexible Box Layout Module Level 1
|
||
// http://www.w3.org/TR/css3-flexbox/
|
||
|
||
${new_style_struct("Flex", is_inherited=False)}
|
||
|
||
// Flex container properties
|
||
${single_keyword("flex-direction", "row row-reverse column column-reverse", experimental=True)}
|
||
}
|
||
|
||
|
||
pub mod shorthands {
|
||
use cssparser::Parser;
|
||
use parser::ParserContext;
|
||
use values::specified;
|
||
|
||
<%def name="shorthand(name, sub_properties, experimental=False)">
|
||
<%
|
||
shorthand = Shorthand(name, sub_properties.split(), experimental=experimental)
|
||
SHORTHANDS.append(shorthand)
|
||
%>
|
||
pub mod ${shorthand.ident} {
|
||
use cssparser::Parser;
|
||
use parser::ParserContext;
|
||
use properties::{longhands, PropertyDeclaration, DeclaredValue, Shorthand};
|
||
|
||
pub struct Longhands {
|
||
% for sub_property in shorthand.sub_properties:
|
||
pub ${sub_property.ident}:
|
||
Option<longhands::${sub_property.ident}::SpecifiedValue>,
|
||
% endfor
|
||
}
|
||
|
||
pub fn parse(context: &ParserContext, input: &mut Parser,
|
||
declarations: &mut Vec<PropertyDeclaration>)
|
||
-> Result<(), ()> {
|
||
input.look_for_var_functions();
|
||
let start = input.position();
|
||
let value = input.parse_entirely(|input| parse_value(context, input));
|
||
if value.is_err() {
|
||
while let Ok(_) = input.next() {} // Look for var() after the error.
|
||
}
|
||
let var = input.seen_var_functions();
|
||
if let Ok(value) = value {
|
||
% for sub_property in shorthand.sub_properties:
|
||
declarations.push(PropertyDeclaration::${sub_property.camel_case}(
|
||
match value.${sub_property.ident} {
|
||
Some(value) => DeclaredValue::Value(value),
|
||
None => DeclaredValue::Initial,
|
||
}
|
||
));
|
||
% endfor
|
||
Ok(())
|
||
} else if var {
|
||
input.reset(start);
|
||
let (first_token_type, css) = try!(
|
||
::custom_properties::parse_non_custom_with_var(input));
|
||
% for sub_property in shorthand.sub_properties:
|
||
declarations.push(PropertyDeclaration::${sub_property.camel_case}(
|
||
DeclaredValue::WithVariables {
|
||
css: css.clone().into_owned(),
|
||
first_token_type: first_token_type,
|
||
base_url: context.base_url.clone(),
|
||
from_shorthand: Some(Shorthand::${shorthand.camel_case}),
|
||
}
|
||
));
|
||
% endfor
|
||
Ok(())
|
||
} else {
|
||
Err(())
|
||
}
|
||
}
|
||
|
||
#[allow(unused_variables)]
|
||
pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
|
||
${caller.body()}
|
||
}
|
||
}
|
||
</%def>
|
||
|
||
fn parse_four_sides<F, T>(input: &mut Parser, parse_one: F) -> Result<(T, T, T, T), ()>
|
||
where F: Fn(&mut Parser) -> Result<T, ()>, F: Copy, T: Clone {
|
||
// zero or more than four values is invalid.
|
||
// one value sets them all
|
||
// two values set (top, bottom) and (left, right)
|
||
// three values set top, (left, right) and bottom
|
||
// four values set them in order
|
||
let top = try!(parse_one(input));
|
||
let right;
|
||
let bottom;
|
||
let left;
|
||
match input.try(parse_one) {
|
||
Err(()) => {
|
||
right = top.clone();
|
||
bottom = top.clone();
|
||
left = top.clone();
|
||
}
|
||
Ok(value) => {
|
||
right = value;
|
||
match input.try(parse_one) {
|
||
Err(()) => {
|
||
bottom = top.clone();
|
||
left = right.clone();
|
||
}
|
||
Ok(value) => {
|
||
bottom = value;
|
||
match input.try(parse_one) {
|
||
Err(()) => {
|
||
left = right.clone();
|
||
}
|
||
Ok(value) => {
|
||
left = value;
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
Ok((top, right, bottom, left))
|
||
}
|
||
|
||
<%def name="four_sides_shorthand(name, sub_property_pattern, parser_function)">
|
||
<%self:shorthand name="${name}" sub_properties="${
|
||
' '.join(sub_property_pattern % side
|
||
for side in ['top', 'right', 'bottom', 'left'])}">
|
||
use super::parse_four_sides;
|
||
use values::specified;
|
||
let _unused = context;
|
||
let (top, right, bottom, left) = try!(parse_four_sides(input, ${parser_function}));
|
||
Ok(Longhands {
|
||
% for side in ["top", "right", "bottom", "left"]:
|
||
${to_rust_ident(sub_property_pattern % side)}: Some(${side}),
|
||
% endfor
|
||
})
|
||
</%self:shorthand>
|
||
</%def>
|
||
|
||
// TODO: other background-* properties
|
||
<%self:shorthand name="background"
|
||
sub_properties="background-color background-position background-repeat background-attachment
|
||
background-image background-size background-origin background-clip">
|
||
use properties::longhands::{background_color, background_position, background_repeat, background_attachment};
|
||
use properties::longhands::{background_image, background_size, background_origin, background_clip};
|
||
|
||
let mut color = None;
|
||
let mut image = None;
|
||
let mut position = None;
|
||
let mut repeat = None;
|
||
let mut size = None;
|
||
let mut attachment = None;
|
||
let mut any = false;
|
||
let mut origin = None;
|
||
let mut clip = None;
|
||
|
||
loop {
|
||
if position.is_none() {
|
||
if let Ok(value) = input.try(|input| background_position::parse(context, input)) {
|
||
position = Some(value);
|
||
any = true;
|
||
|
||
// Parse background size, if applicable.
|
||
size = input.try(|input| {
|
||
try!(input.expect_delim('/'));
|
||
background_size::parse(context, input)
|
||
}).ok();
|
||
|
||
continue
|
||
}
|
||
}
|
||
if color.is_none() {
|
||
if let Ok(value) = input.try(|input| background_color::parse(context, input)) {
|
||
color = Some(value);
|
||
any = true;
|
||
continue
|
||
}
|
||
}
|
||
if image.is_none() {
|
||
if let Ok(value) = input.try(|input| background_image::parse(context, input)) {
|
||
image = Some(value);
|
||
any = true;
|
||
continue
|
||
}
|
||
}
|
||
if repeat.is_none() {
|
||
if let Ok(value) = input.try(|input| background_repeat::parse(context, input)) {
|
||
repeat = Some(value);
|
||
any = true;
|
||
continue
|
||
}
|
||
}
|
||
if attachment.is_none() {
|
||
if let Ok(value) = input.try(|input| background_attachment::parse(context, input)) {
|
||
attachment = Some(value);
|
||
any = true;
|
||
continue
|
||
}
|
||
}
|
||
if origin.is_none() {
|
||
if let Ok(value) = input.try(|input| background_origin::parse(context, input)) {
|
||
origin = Some(value);
|
||
any = true;
|
||
continue
|
||
}
|
||
}
|
||
if clip.is_none() {
|
||
if let Ok(value) = input.try(|input| background_clip::parse(context, input)) {
|
||
clip = Some(value);
|
||
any = true;
|
||
continue
|
||
}
|
||
}
|
||
break
|
||
}
|
||
|
||
if any {
|
||
Ok(Longhands {
|
||
background_color: color,
|
||
background_image: image,
|
||
background_position: position,
|
||
background_repeat: repeat,
|
||
background_attachment: attachment,
|
||
background_size: size,
|
||
background_origin: origin,
|
||
background_clip: clip,
|
||
})
|
||
} else {
|
||
Err(())
|
||
}
|
||
</%self:shorthand>
|
||
|
||
${four_sides_shorthand("margin", "margin-%s", "specified::LengthOrPercentageOrAuto::parse")}
|
||
${four_sides_shorthand("padding", "padding-%s", "specified::LengthOrPercentage::parse")}
|
||
|
||
${four_sides_shorthand("border-color", "border-%s-color", "specified::CSSColor::parse")}
|
||
${four_sides_shorthand("border-style", "border-%s-style",
|
||
"specified::BorderStyle::parse")}
|
||
<%self:shorthand name="border-width" sub_properties="${
|
||
' '.join('border-%s-width' % side
|
||
for side in ['top', 'right', 'bottom', 'left'])}">
|
||
use super::parse_four_sides;
|
||
use values::specified;
|
||
let _unused = context;
|
||
let (top, right, bottom, left) = try!(parse_four_sides(input, specified::parse_border_width));
|
||
Ok(Longhands {
|
||
% for side in ["top", "right", "bottom", "left"]:
|
||
${to_rust_ident('border-%s-width' % side)}:
|
||
Some(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue(${side})),
|
||
% endfor
|
||
})
|
||
</%self:shorthand>
|
||
|
||
|
||
pub fn parse_border(context: &ParserContext, input: &mut Parser)
|
||
-> Result<(Option<specified::CSSColor>,
|
||
Option<specified::BorderStyle>,
|
||
Option<specified::Length>), ()> {
|
||
use values::specified;
|
||
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(specified::CSSColor::parse) {
|
||
color = Some(value);
|
||
any = true;
|
||
continue
|
||
}
|
||
}
|
||
if style.is_none() {
|
||
if let Ok(value) = input.try(specified::BorderStyle::parse) {
|
||
style = Some(value);
|
||
any = true;
|
||
continue
|
||
}
|
||
}
|
||
if width.is_none() {
|
||
if let Ok(value) = input.try(specified::parse_border_width) {
|
||
width = Some(value);
|
||
any = true;
|
||
continue
|
||
}
|
||
}
|
||
break
|
||
}
|
||
if any { Ok((color, style, width)) } else { Err(()) }
|
||
}
|
||
|
||
|
||
% for side in ["top", "right", "bottom", "left"]:
|
||
<%self:shorthand name="border-${side}" sub_properties="${' '.join(
|
||
'border-%s-%s' % (side, prop)
|
||
for prop in ['color', 'style', 'width']
|
||
)}">
|
||
let (color, style, width) = try!(super::parse_border(context, input));
|
||
Ok(Longhands {
|
||
border_${side}_color: color,
|
||
border_${side}_style: style,
|
||
border_${side}_width:
|
||
width.map(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue),
|
||
})
|
||
</%self:shorthand>
|
||
% endfor
|
||
|
||
<%self:shorthand name="border" sub_properties="${' '.join(
|
||
'border-%s-%s' % (side, prop)
|
||
for side in ['top', 'right', 'bottom', 'left']
|
||
for prop in ['color', 'style', 'width']
|
||
)}">
|
||
let (color, style, width) = try!(super::parse_border(context, input));
|
||
Ok(Longhands {
|
||
% for side in ["top", "right", "bottom", "left"]:
|
||
border_${side}_color: color.clone(),
|
||
border_${side}_style: style,
|
||
border_${side}_width:
|
||
width.map(longhands::${to_rust_ident('border-%s-width' % side)}::SpecifiedValue),
|
||
% endfor
|
||
})
|
||
</%self:shorthand>
|
||
|
||
<%self:shorthand name="border-radius" sub_properties="${' '.join(
|
||
'border-%s-radius' % (corner)
|
||
for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left']
|
||
)}">
|
||
use app_units::Au;
|
||
use values::specified::{Length, LengthOrPercentage};
|
||
use values::specified::BorderRadiusSize;
|
||
|
||
let _ignored = context;
|
||
|
||
fn parse_one_set_of_border_values(mut input: &mut Parser)
|
||
-> Result<[LengthOrPercentage; 4], ()> {
|
||
let mut count = 0;
|
||
let mut values = [LengthOrPercentage::Length(Length::Absolute(Au(0))); 4];
|
||
while count < 4 {
|
||
if let Ok(value) = input.try(LengthOrPercentage::parse) {
|
||
values[count] = value;
|
||
count += 1;
|
||
} else {
|
||
break
|
||
}
|
||
}
|
||
|
||
match count {
|
||
1 => Ok([values[0], values[0], values[0], values[0]]),
|
||
2 => Ok([values[0], values[1], values[0], values[1]]),
|
||
3 => Ok([values[0], values[1], values[2], values[1]]),
|
||
4 => Ok([values[0], values[1], values[2], values[3]]),
|
||
_ => Err(()),
|
||
}
|
||
}
|
||
|
||
fn parse_one_set_of_border_radii(mut input: &mut Parser)
|
||
-> Result<[BorderRadiusSize; 4], ()> {
|
||
let widths = try!(parse_one_set_of_border_values(input));
|
||
let mut heights = widths.clone();
|
||
let mut radii_values = [BorderRadiusSize::zero(); 4];
|
||
if input.try(|input| input.expect_delim('/')).is_ok() {
|
||
heights = try!(parse_one_set_of_border_values(input));
|
||
}
|
||
for i in 0..radii_values.len() {
|
||
radii_values[i] = BorderRadiusSize::new(widths[i], heights[i]);
|
||
}
|
||
Ok(radii_values)
|
||
}
|
||
|
||
let radii = try!(parse_one_set_of_border_radii(input));
|
||
Ok(Longhands {
|
||
border_top_left_radius: Some(radii[0]),
|
||
border_top_right_radius: Some(radii[1]),
|
||
border_bottom_right_radius: Some(radii[2]),
|
||
border_bottom_left_radius: Some(radii[3]),
|
||
})
|
||
</%self:shorthand>
|
||
|
||
<%self:shorthand name="outline" sub_properties="outline-color outline-style outline-width">
|
||
use properties::longhands::outline_width;
|
||
use values::specified;
|
||
|
||
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(specified::CSSColor::parse) {
|
||
color = Some(value);
|
||
any = true;
|
||
continue
|
||
}
|
||
}
|
||
if style.is_none() {
|
||
if let Ok(value) = input.try(specified::BorderStyle::parse) {
|
||
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(Longhands {
|
||
outline_color: color,
|
||
outline_style: style,
|
||
outline_width: width,
|
||
})
|
||
} else {
|
||
Err(())
|
||
}
|
||
</%self:shorthand>
|
||
|
||
<%self:shorthand name="font" sub_properties="font-style font-variant font-weight
|
||
font-size line-height font-family">
|
||
use properties::longhands::{font_style, font_variant, font_weight, font_size,
|
||
line_height, font_family};
|
||
let mut nb_normals = 0;
|
||
let mut style = None;
|
||
let mut variant = None;
|
||
let mut weight = None;
|
||
let size;
|
||
loop {
|
||
// Special-case 'normal' because it is valid in each of
|
||
// font-style, font-weight and font-variant.
|
||
// 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.is_none() {
|
||
if let Ok(value) = input.try(|input| font_variant::parse(context, input)) {
|
||
variant = Some(value);
|
||
continue
|
||
}
|
||
}
|
||
size = Some(try!(font_size::parse(context, input)));
|
||
break
|
||
}
|
||
#[inline]
|
||
fn count<T>(opt: &Option<T>) -> u8 {
|
||
if opt.is_some() { 1 } else { 0 }
|
||
}
|
||
if size.is_none() || (count(&style) + count(&weight) + count(&variant) + nb_normals) > 3 {
|
||
return Err(())
|
||
}
|
||
let line_height = if input.try(|input| input.expect_delim('/')).is_ok() {
|
||
Some(try!(line_height::parse(context, input)))
|
||
} else {
|
||
None
|
||
};
|
||
let family = try!(input.parse_comma_separated(font_family::parse_one_family));
|
||
Ok(Longhands {
|
||
font_style: style,
|
||
font_variant: variant,
|
||
font_weight: weight,
|
||
font_size: size,
|
||
line_height: line_height,
|
||
font_family: Some(font_family::SpecifiedValue(family))
|
||
})
|
||
</%self:shorthand>
|
||
|
||
// Per CSS-TEXT 6.2, "for legacy reasons, UAs must treat `word-wrap` as an alternate name for
|
||
// the `overflow-wrap` property, as if it were a shorthand of `overflow-wrap`."
|
||
<%self:shorthand name="word-wrap" sub_properties="overflow-wrap">
|
||
use properties::longhands::overflow_wrap;
|
||
Ok(Longhands {
|
||
overflow_wrap: Some(try!(overflow_wrap::parse(context, input))),
|
||
})
|
||
</%self:shorthand>
|
||
|
||
<%self:shorthand name="list-style"
|
||
sub_properties="list-style-image list-style-position list-style-type">
|
||
use properties::longhands::{list_style_image, list_style_position, list_style_type};
|
||
|
||
// `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(())
|
||
}
|
||
any = true;
|
||
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);
|
||
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
|
||
}
|
||
}
|
||
break
|
||
}
|
||
|
||
// 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(Longhands {
|
||
list_style_position: position,
|
||
list_style_image: Some(list_style_image::SpecifiedValue::None),
|
||
list_style_type: Some(list_style_type::SpecifiedValue::none),
|
||
})
|
||
}
|
||
(true, 1, None, Some(image)) => {
|
||
Ok(Longhands {
|
||
list_style_position: position,
|
||
list_style_image: Some(image),
|
||
list_style_type: Some(list_style_type::SpecifiedValue::none),
|
||
})
|
||
}
|
||
(true, 1, Some(list_style_type), None) => {
|
||
Ok(Longhands {
|
||
list_style_position: position,
|
||
list_style_image: Some(list_style_image::SpecifiedValue::None),
|
||
list_style_type: Some(list_style_type),
|
||
})
|
||
}
|
||
(true, 1, None, None) => {
|
||
Ok(Longhands {
|
||
list_style_position: position,
|
||
list_style_image: Some(list_style_image::SpecifiedValue::None),
|
||
list_style_type: Some(list_style_type::SpecifiedValue::none),
|
||
})
|
||
}
|
||
(true, 0, list_style_type, image) => {
|
||
Ok(Longhands {
|
||
list_style_position: position,
|
||
list_style_image: image,
|
||
list_style_type: list_style_type,
|
||
})
|
||
}
|
||
_ => Err(()),
|
||
}
|
||
</%self:shorthand>
|
||
|
||
<%self:shorthand name="columns" sub_properties="column-count column-width" experimental="True">
|
||
use properties::longhands::{column_count, column_width};
|
||
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(())
|
||
} else {
|
||
Ok(Longhands {
|
||
column_count: column_count,
|
||
column_width: column_width,
|
||
})
|
||
}
|
||
</%self:shorthand>
|
||
|
||
<%self:shorthand name="overflow" sub_properties="overflow-x overflow-y">
|
||
use properties::longhands::{overflow_x, overflow_y};
|
||
|
||
let overflow = try!(overflow_x::parse(context, input));
|
||
Ok(Longhands {
|
||
overflow_x: Some(overflow),
|
||
overflow_y: Some(overflow_y::SpecifiedValue(overflow)),
|
||
})
|
||
</%self:shorthand>
|
||
|
||
<%self:shorthand name="transition"
|
||
sub_properties="transition-property transition-duration transition-timing-function
|
||
transition-delay">
|
||
use properties::longhands::{transition_delay, transition_duration, transition_property};
|
||
use properties::longhands::{transition_timing_function};
|
||
|
||
struct SingleTransition {
|
||
transition_property: transition_property::SingleSpecifiedValue,
|
||
transition_duration: transition_duration::SingleSpecifiedValue,
|
||
transition_timing_function: transition_timing_function::SingleSpecifiedValue,
|
||
transition_delay: transition_delay::SingleSpecifiedValue,
|
||
}
|
||
|
||
fn parse_one_transition(input: &mut Parser) -> Result<SingleTransition,()> {
|
||
let (mut property, mut duration) = (None, None);
|
||
let (mut timing_function, mut delay) = (None, None);
|
||
loop {
|
||
if property.is_none() {
|
||
if let Ok(value) = input.try(|input| transition_property::parse_one(input)) {
|
||
property = Some(value);
|
||
continue
|
||
}
|
||
}
|
||
|
||
if duration.is_none() {
|
||
if let Ok(value) = input.try(|input| transition_duration::parse_one(input)) {
|
||
duration = Some(value);
|
||
continue
|
||
}
|
||
}
|
||
|
||
if timing_function.is_none() {
|
||
if let Ok(value) = input.try(|input| {
|
||
transition_timing_function::parse_one(input)
|
||
}) {
|
||
timing_function = Some(value);
|
||
continue
|
||
}
|
||
}
|
||
|
||
if delay.is_none() {
|
||
if let Ok(value) = input.try(|input| transition_delay::parse_one(input)) {
|
||
delay = Some(value);
|
||
continue;
|
||
}
|
||
}
|
||
|
||
break
|
||
}
|
||
|
||
if let Some(property) = property {
|
||
Ok(SingleTransition {
|
||
transition_property: property,
|
||
transition_duration:
|
||
duration.unwrap_or(transition_duration::get_initial_single_value()),
|
||
transition_timing_function:
|
||
timing_function.unwrap_or(
|
||
transition_timing_function::get_initial_single_value()),
|
||
transition_delay:
|
||
delay.unwrap_or(transition_delay::get_initial_single_value()),
|
||
})
|
||
} else {
|
||
Err(())
|
||
}
|
||
}
|
||
|
||
if input.try(|input| input.expect_ident_matching("none")).is_ok() {
|
||
return Ok(Longhands {
|
||
transition_property: None,
|
||
transition_duration: None,
|
||
transition_timing_function: None,
|
||
transition_delay: None,
|
||
})
|
||
}
|
||
|
||
let results = try!(input.parse_comma_separated(parse_one_transition));
|
||
let (mut properties, mut durations) = (Vec::new(), Vec::new());
|
||
let (mut timing_functions, mut delays) = (Vec::new(), Vec::new());
|
||
for result in results {
|
||
properties.push(result.transition_property);
|
||
durations.push(result.transition_duration);
|
||
timing_functions.push(result.transition_timing_function);
|
||
delays.push(result.transition_delay);
|
||
}
|
||
|
||
Ok(Longhands {
|
||
transition_property: Some(transition_property::SpecifiedValue(properties)),
|
||
transition_duration: Some(transition_duration::SpecifiedValue(durations)),
|
||
transition_timing_function:
|
||
Some(transition_timing_function::SpecifiedValue(timing_functions)),
|
||
transition_delay: Some(transition_delay::SpecifiedValue(delays)),
|
||
})
|
||
</%self:shorthand>
|
||
}
|
||
|
||
|
||
// TODO(SimonSapin): Convert this to a syntax extension rather than a Mako template.
|
||
// Maybe submit for inclusion in libstd?
|
||
mod property_bit_field {
|
||
|
||
pub struct PropertyBitField {
|
||
storage: [u32; (${len(LONGHANDS)} - 1 + 32) / 32]
|
||
}
|
||
|
||
impl PropertyBitField {
|
||
#[inline]
|
||
pub fn new() -> PropertyBitField {
|
||
PropertyBitField { storage: [0; (${len(LONGHANDS)} - 1 + 32) / 32] }
|
||
}
|
||
|
||
#[inline]
|
||
fn get(&self, bit: usize) -> bool {
|
||
(self.storage[bit / 32] & (1 << (bit % 32))) != 0
|
||
}
|
||
#[inline]
|
||
fn set(&mut self, bit: usize) {
|
||
self.storage[bit / 32] |= 1 << (bit % 32)
|
||
}
|
||
% for i, property in enumerate(LONGHANDS):
|
||
% if property.derived_from is None:
|
||
#[allow(non_snake_case)]
|
||
#[inline]
|
||
pub fn get_${property.ident}(&self) -> bool {
|
||
self.get(${i})
|
||
}
|
||
#[allow(non_snake_case)]
|
||
#[inline]
|
||
pub fn set_${property.ident}(&mut self) {
|
||
self.set(${i})
|
||
}
|
||
% endif
|
||
% endfor
|
||
}
|
||
}
|
||
|
||
% for property in LONGHANDS:
|
||
% if property.derived_from is None:
|
||
#[allow(non_snake_case)]
|
||
fn substitute_variables_${property.ident}<F>(
|
||
value: &DeclaredValue<longhands::${property.ident}::SpecifiedValue>,
|
||
custom_properties: &Option<Arc<::custom_properties::ComputedValuesMap>>,
|
||
f: F,
|
||
error_reporter: &mut Box<ParseErrorReporter + Send>)
|
||
where F: FnOnce(&DeclaredValue<longhands::${property.ident}::SpecifiedValue>)
|
||
{
|
||
if let DeclaredValue::WithVariables {
|
||
ref css, first_token_type, ref base_url, from_shorthand
|
||
} = *value {
|
||
substitute_variables_${property.ident}_slow(css,
|
||
first_token_type,
|
||
base_url,
|
||
from_shorthand,
|
||
custom_properties,
|
||
f,
|
||
error_reporter);
|
||
} else {
|
||
f(value);
|
||
}
|
||
}
|
||
|
||
#[allow(non_snake_case)]
|
||
#[inline(never)]
|
||
fn substitute_variables_${property.ident}_slow<F>(
|
||
css: &String,
|
||
first_token_type: TokenSerializationType,
|
||
base_url: &Url,
|
||
from_shorthand: Option<Shorthand>,
|
||
custom_properties: &Option<Arc<::custom_properties::ComputedValuesMap>>,
|
||
f: F,
|
||
error_reporter: &mut Box<ParseErrorReporter + Send>)
|
||
where F: FnOnce(&DeclaredValue<longhands::${property.ident}::SpecifiedValue>) {
|
||
f(&
|
||
::custom_properties::substitute(css, first_token_type, custom_properties)
|
||
.and_then(|css| {
|
||
// As of this writing, only the base URL is used for property values:
|
||
//
|
||
// FIXME(pcwalton): Cloning the error reporter is slow! But so are custom
|
||
// properties, so whatever...
|
||
let context = ParserContext::new(
|
||
::stylesheets::Origin::Author, base_url, (*error_reporter).clone());
|
||
Parser::new(&css).parse_entirely(|input| {
|
||
match from_shorthand {
|
||
None => {
|
||
longhands::${property.ident}::parse_specified(&context, input)
|
||
}
|
||
% for shorthand in SHORTHANDS:
|
||
% if property in shorthand.sub_properties:
|
||
Some(Shorthand::${shorthand.camel_case}) => {
|
||
shorthands::${shorthand.ident}::parse_value(&context, input)
|
||
.map(|result| match result.${property.ident} {
|
||
Some(value) => DeclaredValue::Value(value),
|
||
None => DeclaredValue::Initial,
|
||
})
|
||
}
|
||
% endif
|
||
% endfor
|
||
_ => unreachable!()
|
||
}
|
||
})
|
||
})
|
||
.unwrap_or(
|
||
// Invalid at computed-value time.
|
||
DeclaredValue::${"Inherit" if property.style_struct.inherited else "Initial"}
|
||
)
|
||
);
|
||
}
|
||
% endif
|
||
% endfor
|
||
|
||
/// Declarations are stored in reverse order.
|
||
/// Overridden declarations are skipped.
|
||
#[derive(Debug, PartialEq, HeapSizeOf)]
|
||
pub struct PropertyDeclarationBlock {
|
||
#[ignore_heap_size_of = "#7038"]
|
||
pub important: Arc<Vec<PropertyDeclaration>>,
|
||
#[ignore_heap_size_of = "#7038"]
|
||
pub normal: Arc<Vec<PropertyDeclaration>>,
|
||
}
|
||
|
||
pub fn parse_style_attribute(input: &str, base_url: &Url, error_reporter: Box<ParseErrorReporter + Send>)
|
||
-> PropertyDeclarationBlock {
|
||
let context = ParserContext::new(Origin::Author, base_url, error_reporter);
|
||
parse_property_declaration_list(&context, &mut Parser::new(input))
|
||
}
|
||
|
||
pub fn parse_one_declaration(name: &str, input: &str, base_url: &Url, error_reporter: Box<ParseErrorReporter + Send>)
|
||
-> Result<Vec<PropertyDeclaration>, ()> {
|
||
let context = ParserContext::new(Origin::Author, base_url, error_reporter);
|
||
let mut results = vec![];
|
||
match PropertyDeclaration::parse(name, &context, &mut Parser::new(input), &mut results) {
|
||
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => Ok(results),
|
||
_ => Err(())
|
||
}
|
||
}
|
||
|
||
struct PropertyDeclarationParser<'a, 'b: 'a> {
|
||
context: &'a ParserContext<'b>,
|
||
}
|
||
|
||
|
||
/// Default methods reject all at rules.
|
||
impl<'a, 'b> AtRuleParser for PropertyDeclarationParser<'a, 'b> {
|
||
type Prelude = ();
|
||
type AtRule = (Vec<PropertyDeclaration>, bool);
|
||
}
|
||
|
||
|
||
impl<'a, 'b> DeclarationParser for PropertyDeclarationParser<'a, 'b> {
|
||
type Declaration = (Vec<PropertyDeclaration>, bool);
|
||
|
||
fn parse_value(&self, name: &str, input: &mut Parser) -> Result<(Vec<PropertyDeclaration>, bool), ()> {
|
||
let mut results = vec![];
|
||
try!(input.parse_until_before(Delimiter::Bang, |input| {
|
||
match PropertyDeclaration::parse(name, self.context, input, &mut results) {
|
||
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration => Ok(()),
|
||
_ => Err(())
|
||
}
|
||
}));
|
||
let important = input.try(parse_important).is_ok();
|
||
Ok((results, important))
|
||
}
|
||
}
|
||
|
||
|
||
pub fn parse_property_declaration_list(context: &ParserContext, input: &mut Parser)
|
||
-> PropertyDeclarationBlock {
|
||
let mut important_declarations = Vec::new();
|
||
let mut normal_declarations = Vec::new();
|
||
let parser = PropertyDeclarationParser {
|
||
context: context,
|
||
};
|
||
let mut iter = DeclarationListParser::new(input, parser);
|
||
while let Some(declaration) = iter.next() {
|
||
match declaration {
|
||
Ok((results, important)) => {
|
||
if important {
|
||
important_declarations.extend(results);
|
||
} else {
|
||
normal_declarations.extend(results);
|
||
}
|
||
}
|
||
Err(range) => {
|
||
let pos = range.start;
|
||
let message = format!("Unsupported property declaration: '{}'",
|
||
iter.input.slice(range));
|
||
log_css_error(iter.input, pos, &*message, &context);
|
||
}
|
||
}
|
||
}
|
||
PropertyDeclarationBlock {
|
||
important: Arc::new(deduplicate_property_declarations(important_declarations)),
|
||
normal: Arc::new(deduplicate_property_declarations(normal_declarations)),
|
||
}
|
||
}
|
||
|
||
|
||
/// Only keep the last declaration for any given property.
|
||
/// The input is in source order, output in reverse source order.
|
||
fn deduplicate_property_declarations(declarations: Vec<PropertyDeclaration>)
|
||
-> Vec<PropertyDeclaration> {
|
||
let mut deduplicated = vec![];
|
||
let mut seen = PropertyBitField::new();
|
||
let mut seen_custom = Vec::new();
|
||
for declaration in declarations.into_iter().rev() {
|
||
match declaration {
|
||
% for property in LONGHANDS:
|
||
PropertyDeclaration::${property.camel_case}(..) => {
|
||
% if property.derived_from is None:
|
||
if seen.get_${property.ident}() {
|
||
continue
|
||
}
|
||
seen.set_${property.ident}()
|
||
% else:
|
||
unreachable!();
|
||
% endif
|
||
},
|
||
% endfor
|
||
PropertyDeclaration::Custom(ref name, _) => {
|
||
if seen_custom.contains(name) {
|
||
continue
|
||
}
|
||
seen_custom.push(name.clone())
|
||
}
|
||
}
|
||
deduplicated.push(declaration)
|
||
}
|
||
deduplicated
|
||
}
|
||
|
||
|
||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||
pub enum CSSWideKeyword {
|
||
InitialKeyword,
|
||
InheritKeyword,
|
||
UnsetKeyword,
|
||
}
|
||
|
||
impl CSSWideKeyword {
|
||
pub fn parse(input: &mut Parser) -> Result<CSSWideKeyword, ()> {
|
||
match_ignore_ascii_case! { try!(input.expect_ident()),
|
||
"initial" => Ok(CSSWideKeyword::InitialKeyword),
|
||
"inherit" => Ok(CSSWideKeyword::InheritKeyword),
|
||
"unset" => Ok(CSSWideKeyword::UnsetKeyword),
|
||
_ => Err(())
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, Copy, Eq, PartialEq, Debug, HeapSizeOf)]
|
||
pub enum Shorthand {
|
||
% for property in SHORTHANDS:
|
||
${property.camel_case},
|
||
% endfor
|
||
}
|
||
|
||
impl Shorthand {
|
||
pub fn from_name(name: &str) -> Option<Shorthand> {
|
||
match_ignore_ascii_case! { name,
|
||
% for property in SHORTHANDS:
|
||
"${property.name}" => Some(Shorthand::${property.camel_case}),
|
||
% endfor
|
||
_ => None
|
||
}
|
||
}
|
||
|
||
pub fn longhands(&self) -> &'static [&'static str] {
|
||
% for property in SHORTHANDS:
|
||
static ${property.ident.upper()}: &'static [&'static str] = &[
|
||
% for sub in property.sub_properties:
|
||
"${sub.name}",
|
||
% endfor
|
||
];
|
||
% endfor
|
||
match *self {
|
||
% for property in SHORTHANDS:
|
||
Shorthand::${property.camel_case} => ${property.ident.upper()},
|
||
% endfor
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, PartialEq, Eq, Debug, HeapSizeOf)]
|
||
pub enum DeclaredValue<T> {
|
||
Value(T),
|
||
WithVariables {
|
||
css: String,
|
||
first_token_type: TokenSerializationType,
|
||
base_url: Url,
|
||
from_shorthand: Option<Shorthand>,
|
||
},
|
||
Initial,
|
||
Inherit,
|
||
// There is no Unset variant here.
|
||
// The 'unset' keyword is represented as either Initial or Inherit,
|
||
// depending on whether the property is inherited.
|
||
}
|
||
|
||
impl<T: ToCss> ToCss for DeclaredValue<T> {
|
||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||
match *self {
|
||
DeclaredValue::Value(ref inner) => inner.to_css(dest),
|
||
DeclaredValue::WithVariables { ref css, from_shorthand: None, .. } => {
|
||
dest.write_str(css)
|
||
}
|
||
// https://drafts.csswg.org/css-variables/#variables-in-shorthands
|
||
DeclaredValue::WithVariables { .. } => Ok(()),
|
||
DeclaredValue::Initial => dest.write_str("initial"),
|
||
DeclaredValue::Inherit => dest.write_str("inherit"),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(PartialEq, Clone, Debug, HeapSizeOf)]
|
||
pub enum PropertyDeclaration {
|
||
% for property in LONGHANDS:
|
||
${property.camel_case}(DeclaredValue<longhands::${property.ident}::SpecifiedValue>),
|
||
% endfor
|
||
Custom(::custom_properties::Name, DeclaredValue<::custom_properties::SpecifiedValue>),
|
||
}
|
||
|
||
|
||
#[derive(Eq, PartialEq, Copy, Clone)]
|
||
pub enum PropertyDeclarationParseResult {
|
||
UnknownProperty,
|
||
ExperimentalProperty,
|
||
InvalidValue,
|
||
ValidOrIgnoredDeclaration,
|
||
}
|
||
|
||
#[derive(Eq, PartialEq, Clone)]
|
||
pub enum PropertyDeclarationName {
|
||
Longhand(&'static str),
|
||
Custom(::custom_properties::Name),
|
||
Internal
|
||
}
|
||
|
||
impl PartialEq<str> for PropertyDeclarationName {
|
||
fn eq(&self, other: &str) -> bool {
|
||
match *self {
|
||
PropertyDeclarationName::Longhand(n) => n == other,
|
||
PropertyDeclarationName::Custom(ref n) => {
|
||
::custom_properties::parse_name(other) == Ok(&**n)
|
||
}
|
||
PropertyDeclarationName::Internal => false,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl fmt::Display for PropertyDeclarationName {
|
||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||
match *self {
|
||
PropertyDeclarationName::Longhand(n) => f.write_str(n),
|
||
PropertyDeclarationName::Custom(ref n) => {
|
||
try!(f.write_str("--"));
|
||
f.write_str(n)
|
||
}
|
||
PropertyDeclarationName::Internal => Ok(()),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl PropertyDeclaration {
|
||
pub fn name(&self) -> PropertyDeclarationName {
|
||
match *self {
|
||
% for property in LONGHANDS:
|
||
% if property.derived_from is None:
|
||
PropertyDeclaration::${property.camel_case}(..) => {
|
||
PropertyDeclarationName::Longhand("${property.name}")
|
||
}
|
||
% endif
|
||
% endfor
|
||
PropertyDeclaration::Custom(ref name, _) => {
|
||
PropertyDeclarationName::Custom(name.clone())
|
||
}
|
||
_ => PropertyDeclarationName::Internal,
|
||
}
|
||
}
|
||
|
||
pub fn value(&self) -> String {
|
||
match *self {
|
||
% for property in LONGHANDS:
|
||
% if property.derived_from is None:
|
||
PropertyDeclaration::${property.camel_case}(ref value) =>
|
||
value.to_css_string(),
|
||
% endif
|
||
% endfor
|
||
PropertyDeclaration::Custom(_, ref value) => value.to_css_string(),
|
||
ref decl => panic!("unsupported property declaration: {}", decl.name()),
|
||
}
|
||
}
|
||
|
||
/// If this is a pending-substitution value from the given shorthand, return that value
|
||
// Extra space here because < seems to be removed by Mako when immediately followed by &.
|
||
// ↓
|
||
pub fn with_variables_from_shorthand(&self, shorthand: Shorthand) -> Option< &str> {
|
||
match *self {
|
||
% for property in LONGHANDS:
|
||
PropertyDeclaration::${property.camel_case}(ref value) => match *value {
|
||
DeclaredValue::WithVariables { ref css, from_shorthand: Some(s), .. }
|
||
if s == shorthand => {
|
||
Some(&**css)
|
||
}
|
||
_ => None
|
||
},
|
||
% endfor
|
||
PropertyDeclaration::Custom(..) => None,
|
||
}
|
||
}
|
||
|
||
/// Return whether this is a pending-substitution value.
|
||
/// https://drafts.csswg.org/css-variables/#variables-in-shorthands
|
||
pub fn with_variables(&self) -> bool {
|
||
match *self {
|
||
% for property in LONGHANDS:
|
||
PropertyDeclaration::${property.camel_case}(ref value) => match *value {
|
||
DeclaredValue::WithVariables { .. } => true,
|
||
_ => false,
|
||
},
|
||
% endfor
|
||
PropertyDeclaration::Custom(_, ref value) => match *value {
|
||
DeclaredValue::WithVariables { .. } => true,
|
||
_ => false,
|
||
}
|
||
}
|
||
}
|
||
|
||
pub fn matches(&self, name: &str) -> bool {
|
||
match *self {
|
||
% for property in LONGHANDS:
|
||
% if property.derived_from is None:
|
||
PropertyDeclaration::${property.camel_case}(..) => {
|
||
name.eq_ignore_ascii_case("${property.name}")
|
||
}
|
||
% endif
|
||
% endfor
|
||
PropertyDeclaration::Custom(ref declaration_name, _) => {
|
||
::custom_properties::parse_name(name) == Ok(&**declaration_name)
|
||
}
|
||
_ => false,
|
||
}
|
||
}
|
||
|
||
pub fn parse(name: &str, context: &ParserContext, input: &mut Parser,
|
||
result_list: &mut Vec<PropertyDeclaration>) -> PropertyDeclarationParseResult {
|
||
if let Ok(name) = ::custom_properties::parse_name(name) {
|
||
let value = match input.try(CSSWideKeyword::parse) {
|
||
Ok(CSSWideKeyword::UnsetKeyword) | // Custom properties are alawys inherited
|
||
Ok(CSSWideKeyword::InheritKeyword) => DeclaredValue::Inherit,
|
||
Ok(CSSWideKeyword::InitialKeyword) => DeclaredValue::Initial,
|
||
Err(()) => match ::custom_properties::parse(input) {
|
||
Ok(value) => DeclaredValue::Value(value),
|
||
Err(()) => return PropertyDeclarationParseResult::InvalidValue,
|
||
}
|
||
};
|
||
result_list.push(PropertyDeclaration::Custom(Atom::from(name), value));
|
||
return PropertyDeclarationParseResult::ValidOrIgnoredDeclaration;
|
||
}
|
||
match_ignore_ascii_case! { name,
|
||
% for property in LONGHANDS:
|
||
% if property.derived_from is None:
|
||
"${property.name}" => {
|
||
% if property.internal:
|
||
if context.stylesheet_origin != Origin::UserAgent {
|
||
return PropertyDeclarationParseResult::UnknownProperty
|
||
}
|
||
% endif
|
||
% if property.experimental:
|
||
if !::util::prefs::get_pref("${property.experimental}")
|
||
.as_boolean().unwrap_or(false) {
|
||
return PropertyDeclarationParseResult::ExperimentalProperty
|
||
}
|
||
% endif
|
||
match longhands::${property.ident}::parse_declared(context, input) {
|
||
Ok(value) => {
|
||
result_list.push(PropertyDeclaration::${property.camel_case}(value));
|
||
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration
|
||
},
|
||
Err(()) => PropertyDeclarationParseResult::InvalidValue,
|
||
}
|
||
},
|
||
% else:
|
||
"${property.name}" => PropertyDeclarationParseResult::UnknownProperty,
|
||
% endif
|
||
% endfor
|
||
% for shorthand in SHORTHANDS:
|
||
"${shorthand.name}" => {
|
||
% if shorthand.internal:
|
||
if context.stylesheet_origin != Origin::UserAgent {
|
||
return PropertyDeclarationParseResult::UnknownProperty
|
||
}
|
||
% endif
|
||
% if shorthand.experimental:
|
||
if !::util::prefs::get_pref("${shorthand.experimental}")
|
||
.as_boolean().unwrap_or(false) {
|
||
return PropertyDeclarationParseResult::ExperimentalProperty
|
||
}
|
||
% endif
|
||
match input.try(CSSWideKeyword::parse) {
|
||
Ok(CSSWideKeyword::InheritKeyword) => {
|
||
% for sub_property in shorthand.sub_properties:
|
||
result_list.push(
|
||
PropertyDeclaration::${sub_property.camel_case}(
|
||
DeclaredValue::Inherit));
|
||
% endfor
|
||
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration
|
||
},
|
||
Ok(CSSWideKeyword::InitialKeyword) => {
|
||
% for sub_property in shorthand.sub_properties:
|
||
result_list.push(
|
||
PropertyDeclaration::${sub_property.camel_case}(
|
||
DeclaredValue::Initial));
|
||
% endfor
|
||
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration
|
||
},
|
||
Ok(CSSWideKeyword::UnsetKeyword) => {
|
||
% for sub_property in shorthand.sub_properties:
|
||
result_list.push(PropertyDeclaration::${sub_property.camel_case}(
|
||
DeclaredValue::${"Inherit" if sub_property.style_struct.inherited else "Initial"}
|
||
));
|
||
% endfor
|
||
PropertyDeclarationParseResult::ValidOrIgnoredDeclaration
|
||
},
|
||
Err(()) => match shorthands::${shorthand.ident}::parse(context, input, result_list) {
|
||
Ok(()) => PropertyDeclarationParseResult::ValidOrIgnoredDeclaration,
|
||
Err(()) => PropertyDeclarationParseResult::InvalidValue,
|
||
}
|
||
}
|
||
},
|
||
% endfor
|
||
|
||
_ => PropertyDeclarationParseResult::UnknownProperty
|
||
}
|
||
}
|
||
}
|
||
|
||
pub mod style_struct_traits {
|
||
use super::longhands;
|
||
|
||
% for style_struct in STYLE_STRUCTS:
|
||
pub trait T${style_struct.name}: Clone + PartialEq {
|
||
% for longhand in style_struct.longhands:
|
||
#[allow(non_snake_case)]
|
||
fn set_${longhand.ident}(&mut self, v: longhands::${longhand.ident}::computed_value::T);
|
||
#[allow(non_snake_case)]
|
||
fn copy_${longhand.ident}_from(&mut self, other: &Self);
|
||
% endfor
|
||
% if style_struct.name == "Animation":
|
||
fn transition_count(&self) -> usize;
|
||
% elif style_struct.name == "Border":
|
||
% for side in ["top", "right", "bottom", "left"]:
|
||
fn border_${side}_is_none_or_hidden_and_has_nonzero_width(&self) -> bool;
|
||
% endfor
|
||
% elif style_struct.name == "Box":
|
||
fn clone_display(&self) -> longhands::display::computed_value::T;
|
||
fn clone_position(&self) -> longhands::position::computed_value::T;
|
||
fn is_floated(&self) -> bool;
|
||
fn overflow_x_is_visible(&self) -> bool;
|
||
fn overflow_y_is_visible(&self) -> bool;
|
||
% elif style_struct.name == "Color":
|
||
fn clone_color(&self) -> longhands::color::computed_value::T;
|
||
% elif style_struct.name == "Font":
|
||
fn clone_font_size(&self) -> longhands::font_size::computed_value::T;
|
||
fn clone_font_weight(&self) -> longhands::font_weight::computed_value::T;
|
||
fn compute_font_hash(&mut self);
|
||
% elif style_struct.name == "InheritedBox":
|
||
fn clone_direction(&self) -> longhands::direction::computed_value::T;
|
||
fn clone_writing_mode(&self) -> longhands::writing_mode::computed_value::T;
|
||
fn clone_text_orientation(&self) -> longhands::text_orientation::computed_value::T;
|
||
% elif style_struct.name == "InheritedText":
|
||
#[allow(non_snake_case)]
|
||
fn clone__servo_text_decorations_in_effect(&self) ->
|
||
longhands::_servo_text_decorations_in_effect::computed_value::T;
|
||
% elif style_struct.name == "Outline":
|
||
fn outline_is_none_or_hidden_and_has_nonzero_width(&self) -> bool;
|
||
% elif style_struct.name == "Text":
|
||
fn has_underline(&self) -> bool;
|
||
fn has_overline(&self) -> bool;
|
||
fn has_line_through(&self) -> bool;
|
||
% endif
|
||
}
|
||
% endfor
|
||
}
|
||
|
||
pub mod style_structs {
|
||
use fnv::FnvHasher;
|
||
use super::longhands;
|
||
use std::hash::{Hash, Hasher};
|
||
|
||
% for style_struct in STYLE_STRUCTS:
|
||
% if style_struct.name == "Font":
|
||
#[derive(Clone, HeapSizeOf, Debug)]
|
||
% else:
|
||
#[derive(PartialEq, Clone, HeapSizeOf)]
|
||
% endif
|
||
pub struct ${style_struct.name} {
|
||
% for longhand in style_struct.longhands:
|
||
pub ${longhand.ident}: longhands::${longhand.ident}::computed_value::T,
|
||
% endfor
|
||
% if style_struct.name == "Font":
|
||
pub hash: u64,
|
||
% endif
|
||
}
|
||
% if style_struct.name == "Font":
|
||
|
||
impl PartialEq for ${style_struct.name} {
|
||
fn eq(&self, other: &${style_struct.name}) -> bool {
|
||
self.hash == other.hash
|
||
% for longhand in style_struct.longhands:
|
||
&& self.${longhand.ident} == other.${longhand.ident}
|
||
% endfor
|
||
}
|
||
}
|
||
% endif
|
||
|
||
impl super::style_struct_traits::T${style_struct.name} for ${style_struct.name} {
|
||
% for longhand in style_struct.longhands:
|
||
fn set_${longhand.ident}(&mut self, v: longhands::${longhand.ident}::computed_value::T) {
|
||
self.${longhand.ident} = v;
|
||
}
|
||
fn copy_${longhand.ident}_from(&mut self, other: &Self) {
|
||
self.${longhand.ident} = other.${longhand.ident}.clone();
|
||
}
|
||
% endfor
|
||
% if style_struct.name == "Animation":
|
||
fn transition_count(&self) -> usize {
|
||
self.transition_property.0.len()
|
||
}
|
||
% elif style_struct.name == "Border":
|
||
% for side in ["top", "right", "bottom", "left"]:
|
||
fn border_${side}_is_none_or_hidden_and_has_nonzero_width(&self) -> bool {
|
||
self.border_${side}_style.none_or_hidden() &&
|
||
self.border_${side}_width != ::app_units::Au(0)
|
||
}
|
||
% endfor
|
||
% elif style_struct.name == "Box":
|
||
fn clone_display(&self) -> longhands::display::computed_value::T {
|
||
self.display.clone()
|
||
}
|
||
fn clone_position(&self) -> longhands::position::computed_value::T {
|
||
self.position.clone()
|
||
}
|
||
fn is_floated(&self) -> bool {
|
||
self.float != longhands::float::SpecifiedValue::none
|
||
}
|
||
fn overflow_x_is_visible(&self) -> bool {
|
||
self.overflow_x == longhands::overflow_x::computed_value::T::visible
|
||
}
|
||
fn overflow_y_is_visible(&self) -> bool {
|
||
self.overflow_y.0 == longhands::overflow_x::computed_value::T::visible
|
||
}
|
||
% elif style_struct.name == "Color":
|
||
fn clone_color(&self) -> longhands::color::computed_value::T {
|
||
self.color.clone()
|
||
}
|
||
% elif style_struct.name == "Font":
|
||
fn clone_font_size(&self) -> longhands::font_size::computed_value::T {
|
||
self.font_size.clone()
|
||
}
|
||
fn clone_font_weight(&self) -> longhands::font_weight::computed_value::T {
|
||
self.font_weight.clone()
|
||
}
|
||
fn compute_font_hash(&mut self) {
|
||
// Corresponds to the fields in `gfx::font_template::FontTemplateDescriptor`.
|
||
let mut hasher: FnvHasher = Default::default();
|
||
hasher.write_u16(self.font_weight as u16);
|
||
self.font_stretch.hash(&mut hasher);
|
||
self.font_family.hash(&mut hasher);
|
||
self.hash = hasher.finish()
|
||
}
|
||
% elif style_struct.name == "InheritedBox":
|
||
fn clone_direction(&self) -> longhands::direction::computed_value::T {
|
||
self.direction.clone()
|
||
}
|
||
fn clone_writing_mode(&self) -> longhands::writing_mode::computed_value::T {
|
||
self.writing_mode.clone()
|
||
}
|
||
fn clone_text_orientation(&self) -> longhands::text_orientation::computed_value::T {
|
||
self.text_orientation.clone()
|
||
}
|
||
% elif style_struct.name == "InheritedText":
|
||
fn clone__servo_text_decorations_in_effect(&self) ->
|
||
longhands::_servo_text_decorations_in_effect::computed_value::T {
|
||
self._servo_text_decorations_in_effect.clone()
|
||
}
|
||
% elif style_struct.name == "Outline":
|
||
fn outline_is_none_or_hidden_and_has_nonzero_width(&self) -> bool {
|
||
self.outline_style.none_or_hidden() && self.outline_width != ::app_units::Au(0)
|
||
}
|
||
% elif style_struct.name == "Text":
|
||
fn has_underline(&self) -> bool {
|
||
self.text_decoration.underline
|
||
}
|
||
fn has_overline(&self) -> bool {
|
||
self.text_decoration.overline
|
||
}
|
||
fn has_line_through(&self) -> bool {
|
||
self.text_decoration.line_through
|
||
}
|
||
% endif
|
||
}
|
||
|
||
% endfor
|
||
}
|
||
|
||
pub trait TComputedValues : Clone + Send + Sync + 'static {
|
||
% for style_struct in STYLE_STRUCTS:
|
||
type Concrete${style_struct.name}: style_struct_traits::T${style_struct.name};
|
||
% endfor
|
||
|
||
// Temporary bailout case for stuff we haven't made work with the trait
|
||
// yet - panics for non-Servo implementations.
|
||
//
|
||
// Used only for animations. Don't use it in other places.
|
||
fn as_servo<'a>(&'a self) -> &'a ComputedValues;
|
||
fn as_servo_mut<'a>(&'a mut self) -> &'a mut ComputedValues;
|
||
|
||
fn new(custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
|
||
shareable: bool,
|
||
writing_mode: WritingMode,
|
||
root_font_size: Au,
|
||
% for style_struct in STYLE_STRUCTS:
|
||
${style_struct.ident}: Arc<Self::Concrete${style_struct.name}>,
|
||
% endfor
|
||
) -> Self;
|
||
|
||
fn initial_values() -> &'static Self;
|
||
|
||
fn do_cascade_property<F: FnOnce(&Vec<Option<CascadePropertyFn<Self>>>)>(f: F);
|
||
|
||
% for style_struct in STYLE_STRUCTS:
|
||
fn clone_${style_struct.name.lower()}(&self) -> Arc<Self::Concrete${style_struct.name}>;
|
||
fn get_${style_struct.name.lower()}<'a>(&'a self) -> &'a Self::Concrete${style_struct.name};
|
||
fn mutate_${style_struct.name.lower()}<'a>(&'a mut self) -> &'a mut Self::Concrete${style_struct.name};
|
||
% endfor
|
||
|
||
fn custom_properties(&self) -> Option<Arc<::custom_properties::ComputedValuesMap>>;
|
||
fn root_font_size(&self) -> Au;
|
||
fn set_root_font_size(&mut self, size: Au);
|
||
fn set_writing_mode(&mut self, mode: WritingMode);
|
||
fn is_multicol(&self) -> bool;
|
||
}
|
||
|
||
#[derive(Clone, HeapSizeOf)]
|
||
pub struct ComputedValues {
|
||
% for style_struct in STYLE_STRUCTS:
|
||
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
|
||
% endfor
|
||
custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
|
||
shareable: bool,
|
||
pub writing_mode: WritingMode,
|
||
pub root_font_size: Au,
|
||
}
|
||
|
||
impl TComputedValues for ComputedValues {
|
||
% for style_struct in STYLE_STRUCTS:
|
||
type Concrete${style_struct.name} = style_structs::${style_struct.name};
|
||
% endfor
|
||
|
||
fn as_servo<'a>(&'a self) -> &'a ComputedValues { self }
|
||
fn as_servo_mut<'a>(&'a mut self) -> &'a mut ComputedValues { self }
|
||
|
||
fn new(custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
|
||
shareable: bool,
|
||
writing_mode: WritingMode,
|
||
root_font_size: Au,
|
||
% for style_struct in STYLE_STRUCTS:
|
||
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
|
||
% endfor
|
||
) -> Self {
|
||
ComputedValues {
|
||
custom_properties: custom_properties,
|
||
shareable: shareable,
|
||
writing_mode: writing_mode,
|
||
root_font_size: root_font_size,
|
||
% for style_struct in STYLE_STRUCTS:
|
||
${style_struct.ident}: ${style_struct.ident},
|
||
% endfor
|
||
}
|
||
}
|
||
|
||
fn initial_values() -> &'static Self { &*INITIAL_SERVO_VALUES }
|
||
|
||
fn do_cascade_property<F: FnOnce(&Vec<Option<CascadePropertyFn<Self>>>)>(f: F) {
|
||
CASCADE_PROPERTY.with(|x| f(x));
|
||
}
|
||
|
||
% for style_struct in STYLE_STRUCTS:
|
||
#[inline]
|
||
fn clone_${style_struct.name.lower()}(&self) -> Arc<Self::Concrete${style_struct.name}> {
|
||
self.${style_struct.ident}.clone()
|
||
}
|
||
#[inline]
|
||
fn get_${style_struct.name.lower()}<'a>(&'a self) -> &'a Self::Concrete${style_struct.name} {
|
||
&self.${style_struct.ident}
|
||
}
|
||
#[inline]
|
||
fn mutate_${style_struct.name.lower()}<'a>(&'a mut self) -> &'a mut Self::Concrete${style_struct.name} {
|
||
Arc::make_mut(&mut self.${style_struct.ident})
|
||
}
|
||
% endfor
|
||
|
||
// Cloning the Arc here is fine because it only happens in the case where we have custom
|
||
// properties, and those are both rare and expensive.
|
||
fn custom_properties(&self) -> Option<Arc<::custom_properties::ComputedValuesMap>> {
|
||
self.custom_properties.as_ref().map(|x| x.clone())
|
||
}
|
||
|
||
fn root_font_size(&self) -> Au { self.root_font_size }
|
||
fn set_root_font_size(&mut self, size: Au) { self.root_font_size = size }
|
||
fn set_writing_mode(&mut self, mode: WritingMode) { self.writing_mode = mode; }
|
||
|
||
#[inline]
|
||
fn is_multicol(&self) -> bool {
|
||
let style = self.get_column();
|
||
style.column_count.0.is_some() || style.column_width.0.is_some()
|
||
}
|
||
}
|
||
|
||
impl ComputedValues {
|
||
/// Resolves the currentColor keyword.
|
||
/// Any color value form computed values (except for the 'color' property itself)
|
||
/// should go through this method.
|
||
///
|
||
/// Usage example:
|
||
/// let top_color = style.resolve_color(style.Border.border_top_color);
|
||
#[inline]
|
||
pub fn resolve_color(&self, color: Color) -> RGBA {
|
||
match color {
|
||
Color::RGBA(rgba) => rgba,
|
||
Color::CurrentColor => self.get_color().color,
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn content_inline_size(&self) -> computed::LengthOrPercentageOrAuto {
|
||
let box_style = self.get_box();
|
||
if self.writing_mode.is_vertical() {
|
||
box_style.height
|
||
} else {
|
||
box_style.width
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
pub fn content_block_size(&self) -> computed::LengthOrPercentageOrAuto {
|
||
let box_style = self.get_box();
|
||
if self.writing_mode.is_vertical() { box_style.width } else { box_style.height }
|
||
}
|
||
|
||
#[inline]
|
||
pub fn min_inline_size(&self) -> computed::LengthOrPercentage {
|
||
let box_style = self.get_box();
|
||
if self.writing_mode.is_vertical() { box_style.min_height } else { box_style.min_width }
|
||
}
|
||
|
||
#[inline]
|
||
pub fn min_block_size(&self) -> computed::LengthOrPercentage {
|
||
let box_style = self.get_box();
|
||
if self.writing_mode.is_vertical() { box_style.min_width } else { box_style.min_height }
|
||
}
|
||
|
||
#[inline]
|
||
pub fn max_inline_size(&self) -> computed::LengthOrPercentageOrNone {
|
||
let box_style = self.get_box();
|
||
if self.writing_mode.is_vertical() { box_style.max_height } else { box_style.max_width }
|
||
}
|
||
|
||
#[inline]
|
||
pub fn max_block_size(&self) -> computed::LengthOrPercentageOrNone {
|
||
let box_style = self.get_box();
|
||
if self.writing_mode.is_vertical() { box_style.max_width } else { box_style.max_height }
|
||
}
|
||
|
||
#[inline]
|
||
pub fn logical_padding(&self) -> LogicalMargin<computed::LengthOrPercentage> {
|
||
let padding_style = self.get_padding();
|
||
LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
|
||
padding_style.padding_top,
|
||
padding_style.padding_right,
|
||
padding_style.padding_bottom,
|
||
padding_style.padding_left,
|
||
))
|
||
}
|
||
|
||
#[inline]
|
||
pub fn logical_border_width(&self) -> LogicalMargin<Au> {
|
||
let border_style = self.get_border();
|
||
LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
|
||
border_style.border_top_width,
|
||
border_style.border_right_width,
|
||
border_style.border_bottom_width,
|
||
border_style.border_left_width,
|
||
))
|
||
}
|
||
|
||
#[inline]
|
||
pub fn logical_margin(&self) -> LogicalMargin<computed::LengthOrPercentageOrAuto> {
|
||
let margin_style = self.get_margin();
|
||
LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
|
||
margin_style.margin_top,
|
||
margin_style.margin_right,
|
||
margin_style.margin_bottom,
|
||
margin_style.margin_left,
|
||
))
|
||
}
|
||
|
||
#[inline]
|
||
pub fn logical_position(&self) -> LogicalMargin<computed::LengthOrPercentageOrAuto> {
|
||
// FIXME(SimonSapin): should be the writing mode of the containing block, maybe?
|
||
let position_style = self.get_positionoffsets();
|
||
LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
|
||
position_style.top,
|
||
position_style.right,
|
||
position_style.bottom,
|
||
position_style.left,
|
||
))
|
||
}
|
||
|
||
#[inline]
|
||
pub fn get_font_arc(&self) -> Arc<style_structs::Font> {
|
||
self.font.clone()
|
||
}
|
||
|
||
// http://dev.w3.org/csswg/css-transforms/#grouping-property-values
|
||
pub fn get_used_transform_style(&self) -> computed_values::transform_style::T {
|
||
use computed_values::mix_blend_mode;
|
||
use computed_values::transform_style;
|
||
|
||
let effects = self.get_effects();
|
||
|
||
// TODO(gw): Add clip-path, isolation, mask-image, mask-border-source when supported.
|
||
if effects.opacity < 1.0 ||
|
||
!effects.filter.is_empty() ||
|
||
effects.clip.0.is_some() {
|
||
effects.mix_blend_mode != mix_blend_mode::T::normal ||
|
||
return transform_style::T::flat;
|
||
}
|
||
|
||
if effects.transform_style == transform_style::T::auto {
|
||
if effects.transform.0.is_some() {
|
||
return transform_style::T::flat;
|
||
}
|
||
if effects.perspective != computed::LengthOrNone::None {
|
||
return transform_style::T::flat;
|
||
}
|
||
}
|
||
|
||
// Return the computed value if not overridden by the above exceptions
|
||
effects.transform_style
|
||
}
|
||
|
||
pub fn transform_requires_layer(&self) -> bool {
|
||
// Check if the transform matrix is 2D or 3D
|
||
if let Some(ref transform_list) = self.get_effects().transform.0 {
|
||
for transform in transform_list {
|
||
match *transform {
|
||
computed_values::transform::ComputedOperation::Perspective(..) => {
|
||
return true;
|
||
}
|
||
computed_values::transform::ComputedOperation::Matrix(m) => {
|
||
// See http://dev.w3.org/csswg/css-transforms/#2d-matrix
|
||
if m.m31 != 0.0 || m.m32 != 0.0 ||
|
||
m.m13 != 0.0 || m.m23 != 0.0 ||
|
||
m.m43 != 0.0 || m.m14 != 0.0 ||
|
||
m.m24 != 0.0 || m.m34 != 0.0 ||
|
||
m.m33 != 1.0 || m.m44 != 1.0 {
|
||
return true;
|
||
}
|
||
}
|
||
computed_values::transform::ComputedOperation::Translate(_, _, z) => {
|
||
if z != Au(0) {
|
||
return true;
|
||
}
|
||
}
|
||
_ => {}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Neither perspective nor transform present
|
||
false
|
||
}
|
||
|
||
pub fn computed_value_to_string(&self, name: &str) -> Result<String, ()> {
|
||
match name {
|
||
% for style_struct in STYLE_STRUCTS:
|
||
% for longhand in style_struct.longhands:
|
||
"${longhand.name}" => Ok(self.${style_struct.ident}.${longhand.ident}.to_css_string()),
|
||
% endfor
|
||
% endfor
|
||
_ => {
|
||
let name = try!(::custom_properties::parse_name(name));
|
||
let map = try!(self.custom_properties.as_ref().ok_or(()));
|
||
let value = try!(map.get(&Atom::from(name)).ok_or(()));
|
||
Ok(value.to_css_string())
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/// Return a WritingMode bitflags from the relevant CSS properties.
|
||
pub fn get_writing_mode<S: style_struct_traits::TInheritedBox>(inheritedbox_style: &S) -> WritingMode {
|
||
use logical_geometry;
|
||
let mut flags = WritingMode::empty();
|
||
match inheritedbox_style.clone_direction() {
|
||
computed_values::direction::T::ltr => {},
|
||
computed_values::direction::T::rtl => {
|
||
flags.insert(logical_geometry::FLAG_RTL);
|
||
},
|
||
}
|
||
match inheritedbox_style.clone_writing_mode() {
|
||
computed_values::writing_mode::T::horizontal_tb => {},
|
||
computed_values::writing_mode::T::vertical_rl => {
|
||
flags.insert(logical_geometry::FLAG_VERTICAL);
|
||
},
|
||
computed_values::writing_mode::T::vertical_lr => {
|
||
flags.insert(logical_geometry::FLAG_VERTICAL);
|
||
flags.insert(logical_geometry::FLAG_VERTICAL_LR);
|
||
},
|
||
}
|
||
match inheritedbox_style.clone_text_orientation() {
|
||
computed_values::text_orientation::T::sideways_right => {},
|
||
computed_values::text_orientation::T::sideways_left => {
|
||
flags.insert(logical_geometry::FLAG_VERTICAL_LR);
|
||
},
|
||
computed_values::text_orientation::T::sideways => {
|
||
if flags.intersects(logical_geometry::FLAG_VERTICAL_LR) {
|
||
flags.insert(logical_geometry::FLAG_SIDEWAYS_LEFT);
|
||
}
|
||
},
|
||
}
|
||
flags
|
||
}
|
||
|
||
|
||
/// The initial values for all style structs as defined by the specification.
|
||
lazy_static! {
|
||
pub static ref INITIAL_SERVO_VALUES: ComputedValues = ComputedValues {
|
||
% for style_struct in STYLE_STRUCTS:
|
||
${style_struct.ident}: Arc::new(style_structs::${style_struct.name} {
|
||
% for longhand in style_struct.longhands:
|
||
${longhand.ident}: longhands::${longhand.ident}::get_initial_value(),
|
||
% endfor
|
||
% if style_struct.name == "Font":
|
||
hash: 0,
|
||
% endif
|
||
}),
|
||
% endfor
|
||
custom_properties: None,
|
||
shareable: true,
|
||
writing_mode: WritingMode::empty(),
|
||
root_font_size: longhands::font_size::get_initial_value(),
|
||
};
|
||
}
|
||
|
||
|
||
/// Fast path for the function below. Only computes new inherited styles.
|
||
#[allow(unused_mut, unused_imports)]
|
||
fn cascade_with_cached_declarations<C: TComputedValues>(
|
||
viewport_size: Size2D<Au>,
|
||
applicable_declarations: &[DeclarationBlock<Vec<PropertyDeclaration>>],
|
||
shareable: bool,
|
||
parent_style: &C,
|
||
cached_style: &C,
|
||
custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
|
||
mut error_reporter: Box<ParseErrorReporter + Send>)
|
||
-> C {
|
||
let mut context = computed::Context {
|
||
is_root_element: false,
|
||
viewport_size: viewport_size,
|
||
inherited_style: parent_style,
|
||
style: C::new(
|
||
custom_properties,
|
||
shareable,
|
||
WritingMode::empty(),
|
||
parent_style.root_font_size(),
|
||
% for style_struct in STYLE_STRUCTS:
|
||
% if style_struct.inherited:
|
||
parent_style
|
||
% else:
|
||
cached_style
|
||
% endif
|
||
.clone_${style_struct.name.lower()}(),
|
||
% endfor
|
||
),
|
||
};
|
||
let mut seen = PropertyBitField::new();
|
||
// Declaration blocks are stored in increasing precedence order,
|
||
// we want them in decreasing order here.
|
||
for sub_list in applicable_declarations.iter().rev() {
|
||
// Declarations are already stored in reverse order.
|
||
for declaration in sub_list.declarations.iter() {
|
||
match *declaration {
|
||
% for style_struct in STYLE_STRUCTS:
|
||
% for property in style_struct.longhands:
|
||
% if property.derived_from is None:
|
||
PropertyDeclaration::${property.camel_case}(ref
|
||
${'_' if not style_struct.inherited else ''}declared_value)
|
||
=> {
|
||
use properties::style_struct_traits::T${style_struct.name};
|
||
% if style_struct.inherited:
|
||
if seen.get_${property.ident}() {
|
||
continue
|
||
}
|
||
seen.set_${property.ident}();
|
||
let custom_props = context.style().custom_properties();
|
||
substitute_variables_${property.ident}(
|
||
declared_value, &custom_props,
|
||
|value| match *value {
|
||
DeclaredValue::Value(ref specified_value)
|
||
=> {
|
||
let computed = specified_value.to_computed_value(&context);
|
||
context.mutate_style().mutate_${style_struct.name.lower()}()
|
||
.set_${property.ident}(computed);
|
||
},
|
||
DeclaredValue::Initial
|
||
=> {
|
||
// FIXME(bholley): We may want set_X_to_initial_value() here.
|
||
let initial = longhands::${property.ident}::get_initial_value();
|
||
context.mutate_style().mutate_${style_struct.name.lower()}()
|
||
.set_${property.ident}(initial);
|
||
},
|
||
DeclaredValue::Inherit => {
|
||
// This is a bit slow, but this is rare so it shouldn't
|
||
// matter.
|
||
//
|
||
// FIXME: is it still?
|
||
let inherited_struct = parent_style.get_${style_struct.ident}();
|
||
context.mutate_style().mutate_${style_struct.name.lower()}()
|
||
.copy_${property.ident}_from(inherited_struct);
|
||
}
|
||
DeclaredValue::WithVariables { .. } => unreachable!()
|
||
}, &mut error_reporter
|
||
);
|
||
% endif
|
||
|
||
% if property.name in DERIVED_LONGHANDS:
|
||
% for derived in DERIVED_LONGHANDS[property.name]:
|
||
longhands::${derived.ident}
|
||
::derive_from_${property.ident}(&mut context);
|
||
% endfor
|
||
% endif
|
||
}
|
||
% else:
|
||
PropertyDeclaration::${property.camel_case}(_) => {
|
||
// Do not allow stylesheets to set derived properties.
|
||
}
|
||
% endif
|
||
% endfor
|
||
% endfor
|
||
PropertyDeclaration::Custom(..) => {}
|
||
}
|
||
}
|
||
}
|
||
|
||
if seen.get_font_style() || seen.get_font_weight() || seen.get_font_stretch() ||
|
||
seen.get_font_family() {
|
||
use properties::style_struct_traits::TFont;
|
||
context.mutate_style().mutate_font().compute_font_hash();
|
||
}
|
||
|
||
context.style
|
||
}
|
||
|
||
pub type CascadePropertyFn<C: TComputedValues> =
|
||
extern "Rust" fn(declaration: &PropertyDeclaration,
|
||
inherited_style: &C,
|
||
context: &mut computed::Context<C>,
|
||
seen: &mut PropertyBitField,
|
||
cacheable: &mut bool,
|
||
error_reporter: &mut Box<ParseErrorReporter + Send>);
|
||
|
||
// This is a thread-local rather than a lazy static to avoid atomic operations when cascading
|
||
// properties.
|
||
thread_local!(static CASCADE_PROPERTY: Vec<Option<CascadePropertyFn<ComputedValues>>> = {
|
||
let mut result: Vec<Option<CascadePropertyFn<ComputedValues>>> = Vec::new();
|
||
% for style_struct in STYLE_STRUCTS:
|
||
% for property in style_struct.longhands:
|
||
let discriminant;
|
||
unsafe {
|
||
let variant = PropertyDeclaration::${property.camel_case}(intrinsics::uninit());
|
||
discriminant = intrinsics::discriminant_value(&variant) as usize;
|
||
mem::forget(variant);
|
||
}
|
||
while result.len() < discriminant + 1 {
|
||
result.push(None)
|
||
}
|
||
result[discriminant] = Some(longhands::${property.ident}::cascade_property);
|
||
% endfor
|
||
% endfor
|
||
result
|
||
});
|
||
|
||
/// Performs the CSS cascade, computing new styles for an element from its parent style and
|
||
/// optionally a cached related style. The arguments are:
|
||
///
|
||
/// * `viewport_size`: The size of the initial viewport.
|
||
///
|
||
/// * `applicable_declarations`: The list of CSS rules that matched.
|
||
///
|
||
/// * `shareable`: Whether the `ComputedValues` structure to be constructed should be considered
|
||
/// shareable.
|
||
///
|
||
/// * `parent_style`: The parent style, if applicable; if `None`, this is the root node.
|
||
///
|
||
/// * `cached_style`: If present, cascading is short-circuited for everything but inherited
|
||
/// values and these values are used instead. Obviously, you must be careful when supplying
|
||
/// this that it is safe to only provide inherited declarations. If `parent_style` is `None`,
|
||
/// this is ignored.
|
||
///
|
||
/// Returns the computed values and a boolean indicating whether the result is cacheable.
|
||
pub fn cascade<C: TComputedValues>(
|
||
viewport_size: Size2D<Au>,
|
||
applicable_declarations: &[DeclarationBlock<Vec<PropertyDeclaration>>],
|
||
shareable: bool,
|
||
parent_style: Option< &C >,
|
||
cached_style: Option< &C >,
|
||
mut error_reporter: Box<ParseErrorReporter + Send>)
|
||
-> (C, bool) {
|
||
use properties::style_struct_traits::{TBorder, TBox, TColor, TFont, TOutline};
|
||
let initial_values = C::initial_values();
|
||
let (is_root_element, inherited_style) = match parent_style {
|
||
Some(parent_style) => (false, parent_style),
|
||
None => (true, initial_values),
|
||
};
|
||
|
||
let inherited_custom_properties = inherited_style.custom_properties();
|
||
let mut custom_properties = None;
|
||
let mut seen_custom = HashSet::new();
|
||
for sub_list in applicable_declarations.iter().rev() {
|
||
// Declarations are already stored in reverse order.
|
||
for declaration in sub_list.declarations.iter() {
|
||
match *declaration {
|
||
PropertyDeclaration::Custom(ref name, ref value) => {
|
||
::custom_properties::cascade(
|
||
&mut custom_properties, &inherited_custom_properties,
|
||
&mut seen_custom, name, value)
|
||
}
|
||
_ => {}
|
||
}
|
||
}
|
||
}
|
||
let custom_properties = ::custom_properties::finish_cascade(
|
||
custom_properties, &inherited_custom_properties);
|
||
|
||
if let (Some(cached_style), Some(parent_style)) = (cached_style, parent_style) {
|
||
let style = cascade_with_cached_declarations(viewport_size,
|
||
applicable_declarations,
|
||
shareable,
|
||
parent_style,
|
||
cached_style,
|
||
custom_properties,
|
||
error_reporter);
|
||
return (style, false)
|
||
}
|
||
|
||
let mut context = computed::Context {
|
||
is_root_element: is_root_element,
|
||
viewport_size: viewport_size,
|
||
inherited_style: inherited_style,
|
||
style: C::new(
|
||
custom_properties,
|
||
shareable,
|
||
WritingMode::empty(),
|
||
inherited_style.root_font_size(),
|
||
% for style_struct in STYLE_STRUCTS:
|
||
% if style_struct.inherited:
|
||
inherited_style
|
||
% else:
|
||
initial_values
|
||
% endif
|
||
.clone_${style_struct.name.lower()}(),
|
||
% endfor
|
||
),
|
||
};
|
||
|
||
// Set computed values, overwriting earlier declarations for the same property.
|
||
let mut cacheable = true;
|
||
let mut seen = PropertyBitField::new();
|
||
// Declaration blocks are stored in increasing precedence order, we want them in decreasing
|
||
// order here.
|
||
//
|
||
// We could (and used to) use a pattern match here, but that bloats this function to over 100K
|
||
// of compiled code! To improve i-cache behavior, we outline the individual functions and use
|
||
// virtual dispatch instead.
|
||
C::do_cascade_property(|cascade_property| {
|
||
% for category_to_cascade_now in ["early", "other"]:
|
||
for sub_list in applicable_declarations.iter().rev() {
|
||
// Declarations are already stored in reverse order.
|
||
for declaration in sub_list.declarations.iter() {
|
||
if let PropertyDeclaration::Custom(..) = *declaration {
|
||
continue
|
||
}
|
||
// The computed value of some properties depends on the (sometimes computed)
|
||
// value of *other* properties.
|
||
// So we classify properties into "early" and "other",
|
||
// such that the only dependencies can be from "other" to "early".
|
||
// We iterate applicable_declarations twice, first cascading "early" properties
|
||
// then "other".
|
||
// Unfortunately, it’s not easy to check that this classification is correct.
|
||
let is_early_property = matches!(*declaration,
|
||
PropertyDeclaration::FontSize(_) |
|
||
PropertyDeclaration::Color(_) |
|
||
PropertyDeclaration::Position(_) |
|
||
PropertyDeclaration::Float(_) |
|
||
PropertyDeclaration::TextDecoration(_)
|
||
);
|
||
if
|
||
% if category_to_cascade_now == "early":
|
||
!
|
||
% endif
|
||
is_early_property
|
||
{
|
||
continue
|
||
}
|
||
let discriminant = unsafe {
|
||
intrinsics::discriminant_value(declaration) as usize
|
||
};
|
||
(cascade_property[discriminant].unwrap())(declaration,
|
||
inherited_style,
|
||
&mut context,
|
||
&mut seen,
|
||
&mut cacheable,
|
||
&mut error_reporter);
|
||
}
|
||
}
|
||
% endfor
|
||
});
|
||
|
||
let mut style = context.style;
|
||
|
||
let positioned = matches!(style.get_box().clone_position(),
|
||
longhands::position::SpecifiedValue::absolute |
|
||
longhands::position::SpecifiedValue::fixed);
|
||
let floated = style.get_box().is_floated();
|
||
if positioned || floated || is_root_element {
|
||
use computed_values::display::T;
|
||
|
||
let specified_display = style.get_box().clone_display();
|
||
let computed_display = match specified_display {
|
||
T::inline_table => {
|
||
Some(T::table)
|
||
}
|
||
T::inline | T::inline_block |
|
||
T::table_row_group | T::table_column |
|
||
T::table_column_group | T::table_header_group |
|
||
T::table_footer_group | T::table_row | T::table_cell |
|
||
T::table_caption => {
|
||
Some(T::block)
|
||
}
|
||
_ => None
|
||
};
|
||
if let Some(computed_display) = computed_display {
|
||
let box_ = style.mutate_box();
|
||
box_.set_display(computed_display);
|
||
box_.set__servo_display_for_hypothetical_box(if is_root_element {
|
||
computed_display
|
||
} else {
|
||
specified_display
|
||
});
|
||
}
|
||
}
|
||
|
||
{
|
||
use computed_values::overflow_x::T as overflow;
|
||
use computed_values::overflow_y;
|
||
match (style.get_box().overflow_x_is_visible(), style.get_box().overflow_y_is_visible()) {
|
||
(true, true) => {}
|
||
(true, _) => {
|
||
style.mutate_box().set_overflow_x(overflow::auto);
|
||
}
|
||
(_, true) => {
|
||
style.mutate_box().set_overflow_y(overflow_y::T(overflow::auto));
|
||
}
|
||
_ => {}
|
||
}
|
||
}
|
||
|
||
// The initial value of border-*-width may be changed at computed value time.
|
||
% for side in ["top", "right", "bottom", "left"]:
|
||
// Like calling to_computed_value, which wouldn't type check.
|
||
if style.get_border().border_${side}_is_none_or_hidden_and_has_nonzero_width() {
|
||
style.mutate_border().set_border_${side}_width(Au(0));
|
||
}
|
||
% endfor
|
||
|
||
// The initial value of outline width may be changed at computed value time.
|
||
if style.get_outline().outline_is_none_or_hidden_and_has_nonzero_width() {
|
||
style.mutate_outline().set_outline_width(Au(0));
|
||
}
|
||
|
||
if is_root_element {
|
||
let s = style.get_font().clone_font_size();
|
||
style.set_root_font_size(s);
|
||
}
|
||
|
||
if seen.get_font_style() || seen.get_font_weight() || seen.get_font_stretch() ||
|
||
seen.get_font_family() {
|
||
use properties::style_struct_traits::TFont;
|
||
style.mutate_font().compute_font_hash();
|
||
}
|
||
|
||
let mode = get_writing_mode(style.get_inheritedbox());
|
||
style.set_writing_mode(mode);
|
||
(style, cacheable)
|
||
}
|
||
|
||
/// Alters the given style to accommodate replaced content. This is called in flow construction. It
|
||
/// handles cases like `<div style="position: absolute">foo bar baz</div>` (in which `foo`, `bar`,
|
||
/// and `baz` must not be absolutely-positioned) and cases like `<sup>Foo</sup>` (in which the
|
||
/// `vertical-align: top` style of `sup` must not propagate down into `Foo`).
|
||
///
|
||
/// FIXME(#5625, pcwalton): It would probably be cleaner and faster to do this in the cascade.
|
||
#[inline]
|
||
pub fn modify_style_for_replaced_content(style: &mut Arc<ComputedValues>) {
|
||
// Reset `position` to handle cases like `<div style="position: absolute">foo bar baz</div>`.
|
||
if style.box_.display != longhands::display::computed_value::T::inline {
|
||
let mut style = Arc::make_mut(style);
|
||
Arc::make_mut(&mut style.box_).display = longhands::display::computed_value::T::inline;
|
||
Arc::make_mut(&mut style.box_).position =
|
||
longhands::position::computed_value::T::static_;
|
||
}
|
||
|
||
// Reset `vertical-align` to handle cases like `<sup>foo</sup>`.
|
||
if style.box_.vertical_align != longhands::vertical_align::computed_value::T::baseline {
|
||
let mut style = Arc::make_mut(style);
|
||
Arc::make_mut(&mut style.box_).vertical_align =
|
||
longhands::vertical_align::computed_value::T::baseline
|
||
}
|
||
|
||
// Reset margins.
|
||
if style.margin.margin_top != computed::LengthOrPercentageOrAuto::Length(Au(0)) ||
|
||
style.margin.margin_left != computed::LengthOrPercentageOrAuto::Length(Au(0)) ||
|
||
style.margin.margin_bottom != computed::LengthOrPercentageOrAuto::Length(Au(0)) ||
|
||
style.margin.margin_right != computed::LengthOrPercentageOrAuto::Length(Au(0)) {
|
||
let mut style = Arc::make_mut(style);
|
||
let margin = Arc::make_mut(&mut style.margin);
|
||
margin.margin_top = computed::LengthOrPercentageOrAuto::Length(Au(0));
|
||
margin.margin_left = computed::LengthOrPercentageOrAuto::Length(Au(0));
|
||
margin.margin_bottom = computed::LengthOrPercentageOrAuto::Length(Au(0));
|
||
margin.margin_right = computed::LengthOrPercentageOrAuto::Length(Au(0));
|
||
}
|
||
}
|
||
|
||
/// Adjusts borders as appropriate to account for a fragment's status as the first or last fragment
|
||
/// within the range of an element.
|
||
///
|
||
/// Specifically, this function sets border widths to zero on the sides for which the fragment is
|
||
/// not outermost.
|
||
#[inline]
|
||
pub fn modify_border_style_for_inline_sides(style: &mut Arc<ComputedValues>,
|
||
is_first_fragment_of_element: bool,
|
||
is_last_fragment_of_element: bool) {
|
||
fn modify_side(style: &mut Arc<ComputedValues>, side: PhysicalSide) {
|
||
let mut style = Arc::make_mut(style);
|
||
let border = Arc::make_mut(&mut style.border);
|
||
match side {
|
||
PhysicalSide::Left => {
|
||
border.border_left_width = Au(0);
|
||
border.border_left_style = BorderStyle::none;
|
||
}
|
||
PhysicalSide::Right => {
|
||
border.border_right_width = Au(0);
|
||
border.border_right_style = BorderStyle::none;
|
||
}
|
||
PhysicalSide::Bottom => {
|
||
border.border_bottom_width = Au(0);
|
||
border.border_bottom_style = BorderStyle::none;
|
||
}
|
||
PhysicalSide::Top => {
|
||
border.border_top_width = Au(0);
|
||
border.border_top_style = BorderStyle::none;
|
||
}
|
||
}
|
||
}
|
||
|
||
if !is_first_fragment_of_element {
|
||
let side = style.writing_mode.inline_start_physical_side();
|
||
modify_side(style, side)
|
||
}
|
||
|
||
if !is_last_fragment_of_element {
|
||
let side = style.writing_mode.inline_end_physical_side();
|
||
modify_side(style, side)
|
||
}
|
||
}
|
||
|
||
/// Adjusts the display and position properties as appropriate for an anonymous table object.
|
||
#[inline]
|
||
pub fn modify_style_for_anonymous_table_object(
|
||
style: &mut Arc<ComputedValues>,
|
||
new_display_value: longhands::display::computed_value::T) {
|
||
let mut style = Arc::make_mut(style);
|
||
let box_style = Arc::make_mut(&mut style.box_);
|
||
box_style.display = new_display_value;
|
||
box_style.position = longhands::position::computed_value::T::static_;
|
||
}
|
||
|
||
/// Adjusts the `position` property as necessary for the outer fragment wrapper of an inline-block.
|
||
#[inline]
|
||
pub fn modify_style_for_outer_inline_block_fragment(style: &mut Arc<ComputedValues>) {
|
||
let mut style = Arc::make_mut(style);
|
||
let box_style = Arc::make_mut(&mut style.box_);
|
||
box_style.position = longhands::position::computed_value::T::static_
|
||
}
|
||
|
||
/// Adjusts the `position` and `padding` properties as necessary to account for text.
|
||
///
|
||
/// Text is never directly relatively positioned; it's always contained within an element that is
|
||
/// itself relatively positioned.
|
||
#[inline]
|
||
pub fn modify_style_for_text(style: &mut Arc<ComputedValues>) {
|
||
if style.box_.position == longhands::position::computed_value::T::relative {
|
||
// We leave the `position` property set to `relative` so that we'll still establish a
|
||
// containing block if needed. But we reset all position offsets to `auto`.
|
||
let mut style = Arc::make_mut(style);
|
||
let mut position_offsets = Arc::make_mut(&mut style.positionoffsets);
|
||
position_offsets.top = computed::LengthOrPercentageOrAuto::Auto;
|
||
position_offsets.right = computed::LengthOrPercentageOrAuto::Auto;
|
||
position_offsets.bottom = computed::LengthOrPercentageOrAuto::Auto;
|
||
position_offsets.left = computed::LengthOrPercentageOrAuto::Auto;
|
||
}
|
||
|
||
if style.padding.padding_top != computed::LengthOrPercentage::Length(Au(0)) ||
|
||
style.padding.padding_right != computed::LengthOrPercentage::Length(Au(0)) ||
|
||
style.padding.padding_bottom != computed::LengthOrPercentage::Length(Au(0)) ||
|
||
style.padding.padding_left != computed::LengthOrPercentage::Length(Au(0)) {
|
||
let mut style = Arc::make_mut(style);
|
||
let mut padding = Arc::make_mut(&mut style.padding);
|
||
padding.padding_top = computed::LengthOrPercentage::Length(Au(0));
|
||
padding.padding_right = computed::LengthOrPercentage::Length(Au(0));
|
||
padding.padding_bottom = computed::LengthOrPercentage::Length(Au(0));
|
||
padding.padding_left = computed::LengthOrPercentage::Length(Au(0));
|
||
}
|
||
|
||
if style.effects.opacity != 1.0 {
|
||
let mut style = Arc::make_mut(style);
|
||
let mut effects = Arc::make_mut(&mut style.effects);
|
||
effects.opacity = 1.0;
|
||
}
|
||
}
|
||
|
||
/// Adjusts the `margin` property as necessary to account for the text of an `input` element.
|
||
///
|
||
/// Margins apply to the `input` element itself, so including them in the text will cause them to
|
||
/// be double-counted.
|
||
pub fn modify_style_for_input_text(style: &mut Arc<ComputedValues>) {
|
||
let mut style = Arc::make_mut(style);
|
||
let margin_style = Arc::make_mut(&mut style.margin);
|
||
margin_style.margin_top = computed::LengthOrPercentageOrAuto::Length(Au(0));
|
||
margin_style.margin_right = computed::LengthOrPercentageOrAuto::Length(Au(0));
|
||
margin_style.margin_bottom = computed::LengthOrPercentageOrAuto::Length(Au(0));
|
||
margin_style.margin_left = computed::LengthOrPercentageOrAuto::Length(Au(0));
|
||
|
||
// whitespace inside text input should not be collapsed
|
||
let inherited_text = Arc::make_mut(&mut style.inheritedtext);
|
||
inherited_text.white_space = longhands::white_space::computed_value::T::pre;
|
||
}
|
||
|
||
/// Adjusts the `clip` property so that an inline absolute hypothetical fragment doesn't clip its
|
||
/// children.
|
||
pub fn modify_style_for_inline_absolute_hypothetical_fragment(style: &mut Arc<ComputedValues>) {
|
||
if style.get_effects().clip.0.is_some() {
|
||
let mut style = Arc::make_mut(style);
|
||
let effects_style = Arc::make_mut(&mut style.effects);
|
||
effects_style.clip.0 = None
|
||
}
|
||
}
|
||
|
||
pub fn is_supported_property(property: &str) -> bool {
|
||
match_ignore_ascii_case! { property,
|
||
% for property in SHORTHANDS + LONGHANDS:
|
||
"${property.name}" => true,
|
||
% endfor
|
||
_ => property.starts_with("--")
|
||
}
|
||
}
|
||
|
||
#[macro_export]
|
||
macro_rules! css_properties_accessors {
|
||
($macro_name: ident) => {
|
||
$macro_name! {
|
||
% for property in SHORTHANDS + LONGHANDS:
|
||
% if property.derived_from is None and not property.internal:
|
||
% if '-' in property.name:
|
||
[${property.ident.capitalize()}, Set${property.ident.capitalize()}, "${property.name}"],
|
||
% endif
|
||
% if property != LONGHANDS[-1]:
|
||
[${property.camel_case}, Set${property.camel_case}, "${property.name}"],
|
||
% else:
|
||
[${property.camel_case}, Set${property.camel_case}, "${property.name}"]
|
||
% endif
|
||
% endif
|
||
% endfor
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
macro_rules! longhand_properties_idents {
|
||
($macro_name: ident) => {
|
||
$macro_name! {
|
||
% for property in LONGHANDS:
|
||
${property.ident}
|
||
% endfor
|
||
}
|
||
}
|
||
}
|