mirror of
https://github.com/servo/servo.git
synced 2025-08-01 03:30:33 +01:00
style: Implement atan2(), and enable calc() trigonometric functions by default on nightly
We now have test coverage, so let's do this. The remaining failures are just about infinity/nan, which is a completely different feature. Differential Revision: https://phabricator.services.mozilla.com/D154831
This commit is contained in:
parent
03e84754cc
commit
dd849de9d9
3 changed files with 180 additions and 117 deletions
|
@ -158,6 +158,14 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the leaf if we can (if simplification has allowed it).
|
||||||
|
pub fn as_leaf(&self) -> Option<&L> {
|
||||||
|
match *self {
|
||||||
|
Self::Leaf(ref l) => Some(l),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Tries to merge one sum to another, that is, perform `x` + `y`.
|
/// Tries to merge one sum to another, that is, perform `x` + `y`.
|
||||||
fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
|
fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
|
|
|
@ -43,6 +43,8 @@ pub enum MathFunction {
|
||||||
Acos,
|
Acos,
|
||||||
/// `atan()`: https://drafts.csswg.org/css-values-4/#funcdef-atan
|
/// `atan()`: https://drafts.csswg.org/css-values-4/#funcdef-atan
|
||||||
Atan,
|
Atan,
|
||||||
|
/// `atan2()`: https://drafts.csswg.org/css-values-4/#funcdef-atan2
|
||||||
|
Atan2,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A leaf node inside a `Calc` expression's AST.
|
/// A leaf node inside a `Calc` expression's AST.
|
||||||
|
@ -60,6 +62,15 @@ pub enum Leaf {
|
||||||
Number(CSSFloat),
|
Number(CSSFloat),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Leaf {
|
||||||
|
fn as_length(&self) -> Option<&NoCalcLength> {
|
||||||
|
match *self {
|
||||||
|
Self::Length(ref l) => Some(l),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToCss for Leaf {
|
impl ToCss for Leaf {
|
||||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
where
|
where
|
||||||
|
@ -75,23 +86,22 @@ impl ToCss for Leaf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An expected unit we intend to parse within a `calc()` expression.
|
bitflags! {
|
||||||
///
|
/// Expected units we allow parsing within a `calc()` expression.
|
||||||
/// This is used as a hint for the parser to fast-reject invalid expressions.
|
///
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
/// This is used as a hint for the parser to fast-reject invalid
|
||||||
enum CalcUnit {
|
/// expressions. Numbers are always allowed because they multiply other
|
||||||
/// `<number>`
|
/// units.
|
||||||
Number,
|
struct CalcUnits: u8 {
|
||||||
/// `<length>`
|
const LENGTH = 1 << 0;
|
||||||
Length,
|
const PERCENTAGE = 1 << 1;
|
||||||
/// `<percentage>`
|
const ANGLE = 1 << 2;
|
||||||
Percentage,
|
const TIME = 1 << 3;
|
||||||
/// `<length> | <percentage>`
|
|
||||||
LengthPercentage,
|
const LENGTH_PERCENTAGE = Self::LENGTH.bits | Self::PERCENTAGE.bits;
|
||||||
/// `<angle>`
|
// NOTE: When you add to this, make sure to make Atan2 deal with these.
|
||||||
Angle,
|
const ALL = Self::LENGTH.bits | Self::PERCENTAGE.bits | Self::ANGLE.bits | Self::TIME.bits;
|
||||||
/// `<time>`
|
}
|
||||||
Time,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A struct to hold a simplified `<length>` or `<percentage>` expression.
|
/// A struct to hold a simplified `<length>` or `<percentage>` expression.
|
||||||
|
@ -108,6 +118,27 @@ pub struct CalcLengthPercentage {
|
||||||
pub node: CalcNode,
|
pub node: CalcNode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CalcLengthPercentage {
|
||||||
|
fn same_unit_length_as(a: &Self, b: &Self) -> Option<(CSSFloat, CSSFloat)> {
|
||||||
|
use generic::CalcNodeLeaf;
|
||||||
|
|
||||||
|
debug_assert_eq!(a.clamping_mode, b.clamping_mode);
|
||||||
|
debug_assert_eq!(a.clamping_mode, AllowedNumericType::All);
|
||||||
|
|
||||||
|
let a = a.node.as_leaf()?;
|
||||||
|
let b = b.node.as_leaf()?;
|
||||||
|
|
||||||
|
if a.sort_key() != b.sort_key() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let a = a.as_length()?.unitless_value();
|
||||||
|
let b = b.as_length()?.unitless_value();
|
||||||
|
return Some((a, b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl SpecifiedValueInfo for CalcLengthPercentage {}
|
impl SpecifiedValueInfo for CalcLengthPercentage {}
|
||||||
|
|
||||||
impl PartialOrd for Leaf {
|
impl PartialOrd for Leaf {
|
||||||
|
@ -277,71 +308,47 @@ pub type CalcNode = generic::GenericCalcNode<Leaf>;
|
||||||
impl CalcNode {
|
impl CalcNode {
|
||||||
/// Tries to parse a single element in the expression, that is, a
|
/// Tries to parse a single element in the expression, that is, a
|
||||||
/// `<length>`, `<angle>`, `<time>`, `<percentage>`, according to
|
/// `<length>`, `<angle>`, `<time>`, `<percentage>`, according to
|
||||||
/// `expected_unit`.
|
/// `allowed_units`.
|
||||||
///
|
///
|
||||||
/// May return a "complex" `CalcNode`, in the presence of a parenthesized
|
/// May return a "complex" `CalcNode`, in the presence of a parenthesized
|
||||||
/// expression, for example.
|
/// expression, for example.
|
||||||
fn parse_one<'i, 't>(
|
fn parse_one<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
expected_unit: CalcUnit,
|
allowed_units: CalcUnits,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
let location = input.current_source_location();
|
let location = input.current_source_location();
|
||||||
match (input.next()?, expected_unit) {
|
match input.next()? {
|
||||||
(&Token::Number { value, .. }, _) => Ok(CalcNode::Leaf(Leaf::Number(value))),
|
&Token::Number { value, .. } => Ok(CalcNode::Leaf(Leaf::Number(value))),
|
||||||
(
|
&Token::Dimension { value, ref unit, .. } => {
|
||||||
&Token::Dimension {
|
if allowed_units.intersects(CalcUnits::LENGTH) {
|
||||||
value, ref unit, ..
|
if let Ok(l) = NoCalcLength::parse_dimension(context, value, unit) {
|
||||||
},
|
return Ok(CalcNode::Leaf(Leaf::Length(l)));
|
||||||
CalcUnit::Length,
|
}
|
||||||
) |
|
|
||||||
(
|
|
||||||
&Token::Dimension {
|
|
||||||
value, ref unit, ..
|
|
||||||
},
|
|
||||||
CalcUnit::LengthPercentage,
|
|
||||||
) => match NoCalcLength::parse_dimension(context, value, unit) {
|
|
||||||
Ok(l) => Ok(CalcNode::Leaf(Leaf::Length(l))),
|
|
||||||
Err(()) => Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
|
|
||||||
},
|
|
||||||
(
|
|
||||||
&Token::Dimension {
|
|
||||||
value, ref unit, ..
|
|
||||||
},
|
|
||||||
CalcUnit::Angle,
|
|
||||||
) => {
|
|
||||||
match Angle::parse_dimension(value, unit, /* from_calc = */ true) {
|
|
||||||
Ok(a) => Ok(CalcNode::Leaf(Leaf::Angle(a))),
|
|
||||||
Err(()) => {
|
|
||||||
Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
if allowed_units.intersects(CalcUnits::ANGLE) {
|
||||||
(
|
if let Ok(a) = Angle::parse_dimension(value, unit, /* from_calc = */ true) {
|
||||||
&Token::Dimension {
|
return Ok(CalcNode::Leaf(Leaf::Angle(a)));
|
||||||
value, ref unit, ..
|
}
|
||||||
},
|
|
||||||
CalcUnit::Time,
|
|
||||||
) => {
|
|
||||||
match Time::parse_dimension(value, unit, /* from_calc = */ true) {
|
|
||||||
Ok(t) => Ok(CalcNode::Leaf(Leaf::Time(t))),
|
|
||||||
Err(()) => {
|
|
||||||
Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
if allowed_units.intersects(CalcUnits::TIME) {
|
||||||
|
if let Ok(t) = Time::parse_dimension(value, unit, /* from_calc = */ true) {
|
||||||
|
return Ok(CalcNode::Leaf(Leaf::Time(t)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
},
|
},
|
||||||
(&Token::Percentage { unit_value, .. }, CalcUnit::LengthPercentage) |
|
&Token::Percentage { unit_value, .. } if allowed_units.intersects(CalcUnits::PERCENTAGE) => {
|
||||||
(&Token::Percentage { unit_value, .. }, CalcUnit::Percentage) => {
|
|
||||||
Ok(CalcNode::Leaf(Leaf::Percentage(unit_value)))
|
Ok(CalcNode::Leaf(Leaf::Percentage(unit_value)))
|
||||||
},
|
}
|
||||||
(&Token::ParenthesisBlock, _) => input.parse_nested_block(|input| {
|
&Token::ParenthesisBlock => input.parse_nested_block(|input| {
|
||||||
CalcNode::parse_argument(context, input, expected_unit)
|
CalcNode::parse_argument(context, input, allowed_units)
|
||||||
}),
|
}),
|
||||||
(&Token::Function(ref name), _) => {
|
&Token::Function(ref name) => {
|
||||||
let function = CalcNode::math_function(name, location)?;
|
let function = CalcNode::math_function(name, location)?;
|
||||||
CalcNode::parse(context, input, function, expected_unit)
|
CalcNode::parse(context, input, function, allowed_units)
|
||||||
},
|
},
|
||||||
(&Token::Ident(ref ident), _) => {
|
&Token::Ident(ref ident) => {
|
||||||
if !trig_enabled() {
|
if !trig_enabled() {
|
||||||
return Err(location.new_unexpected_token_error(Token::Ident(ident.clone())));
|
return Err(location.new_unexpected_token_error(Token::Ident(ident.clone())));
|
||||||
}
|
}
|
||||||
|
@ -352,7 +359,7 @@ impl CalcNode {
|
||||||
};
|
};
|
||||||
Ok(CalcNode::Leaf(Leaf::Number(number)))
|
Ok(CalcNode::Leaf(Leaf::Number(number)))
|
||||||
},
|
},
|
||||||
(t, _) => Err(location.new_unexpected_token_error(t.clone())),
|
t => Err(location.new_unexpected_token_error(t.clone())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,20 +370,20 @@ impl CalcNode {
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
function: MathFunction,
|
function: MathFunction,
|
||||||
expected_unit: CalcUnit,
|
allowed_units: CalcUnits,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
// TODO: Do something different based on the function name. In
|
// TODO: Do something different based on the function name. In
|
||||||
// particular, for non-calc function we need to take a list of
|
// particular, for non-calc function we need to take a list of
|
||||||
// comma-separated arguments and such.
|
// comma-separated arguments and such.
|
||||||
input.parse_nested_block(|input| {
|
input.parse_nested_block(|input| {
|
||||||
match function {
|
match function {
|
||||||
MathFunction::Calc => Self::parse_argument(context, input, expected_unit),
|
MathFunction::Calc => Self::parse_argument(context, input, allowed_units),
|
||||||
MathFunction::Clamp => {
|
MathFunction::Clamp => {
|
||||||
let min = Self::parse_argument(context, input, expected_unit)?;
|
let min = Self::parse_argument(context, input, allowed_units)?;
|
||||||
input.expect_comma()?;
|
input.expect_comma()?;
|
||||||
let center = Self::parse_argument(context, input, expected_unit)?;
|
let center = Self::parse_argument(context, input, allowed_units)?;
|
||||||
input.expect_comma()?;
|
input.expect_comma()?;
|
||||||
let max = Self::parse_argument(context, input, expected_unit)?;
|
let max = Self::parse_argument(context, input, allowed_units)?;
|
||||||
Ok(Self::Clamp {
|
Ok(Self::Clamp {
|
||||||
min: Box::new(min),
|
min: Box::new(min),
|
||||||
center: Box::new(center),
|
center: Box::new(center),
|
||||||
|
@ -390,7 +397,7 @@ impl CalcNode {
|
||||||
// Consider adding an API to cssparser to specify the
|
// Consider adding an API to cssparser to specify the
|
||||||
// initial vector capacity?
|
// initial vector capacity?
|
||||||
let arguments = input.parse_comma_separated(|input| {
|
let arguments = input.parse_comma_separated(|input| {
|
||||||
Self::parse_argument(context, input, expected_unit)
|
Self::parse_argument(context, input, allowed_units)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let op = match function {
|
let op = match function {
|
||||||
|
@ -402,7 +409,7 @@ impl CalcNode {
|
||||||
Ok(Self::MinMax(arguments.into(), op))
|
Ok(Self::MinMax(arguments.into(), op))
|
||||||
},
|
},
|
||||||
MathFunction::Sin | MathFunction::Cos | MathFunction::Tan => {
|
MathFunction::Sin | MathFunction::Cos | MathFunction::Tan => {
|
||||||
let argument = Self::parse_argument(context, input, CalcUnit::Angle)?;
|
let argument = Self::parse_argument(context, input, CalcUnits::ANGLE)?;
|
||||||
let radians = match argument.to_number() {
|
let radians = match argument.to_number() {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(()) => match argument.to_angle() {
|
Err(()) => match argument.to_angle() {
|
||||||
|
@ -425,7 +432,7 @@ impl CalcNode {
|
||||||
Ok(Self::Leaf(Leaf::Number(number)))
|
Ok(Self::Leaf(Leaf::Number(number)))
|
||||||
},
|
},
|
||||||
MathFunction::Asin | MathFunction::Acos | MathFunction::Atan => {
|
MathFunction::Asin | MathFunction::Acos | MathFunction::Atan => {
|
||||||
let argument = Self::parse_argument(context, input, CalcUnit::Number)?;
|
let argument = Self::parse_argument(context, input, CalcUnits::empty())?;
|
||||||
let number = match argument.to_number() {
|
let number = match argument.to_number() {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
|
@ -444,6 +451,46 @@ impl CalcNode {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians))))
|
||||||
|
},
|
||||||
|
MathFunction::Atan2 => {
|
||||||
|
let a = Self::parse_argument(context, input, CalcUnits::ALL)?;
|
||||||
|
input.expect_comma()?;
|
||||||
|
let b = Self::parse_argument(context, input, CalcUnits::ALL)?;
|
||||||
|
fn resolve_atan2(a: CalcNode, b: CalcNode) -> Result<CSSFloat, ()> {
|
||||||
|
if let Ok(a) = a.to_number() {
|
||||||
|
let b = b.to_number()?;
|
||||||
|
return Ok(a.atan2(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(a) = a.to_percentage() {
|
||||||
|
let b = b.to_percentage()?;
|
||||||
|
return Ok(a.atan2(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(a) = a.to_time() {
|
||||||
|
let b = b.to_time()?;
|
||||||
|
return Ok(a.seconds().atan2(b.seconds()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(a) = a.to_angle() {
|
||||||
|
let b = b.to_angle()?;
|
||||||
|
return Ok(a.radians().atan2(b.radians()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let a = a.into_length_or_percentage(AllowedNumericType::All)?;
|
||||||
|
let b = b.into_length_or_percentage(AllowedNumericType::All)?;
|
||||||
|
let (a, b) = CalcLengthPercentage::same_unit_length_as(&a, &b).ok_or(())?;
|
||||||
|
return Ok(a.atan2(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
let radians = match resolve_atan2(a, b) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(()) => return Err(
|
||||||
|
input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians))))
|
Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians))))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -453,10 +500,10 @@ impl CalcNode {
|
||||||
fn parse_argument<'i, 't>(
|
fn parse_argument<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
expected_unit: CalcUnit,
|
allowed_units: CalcUnits,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
let mut sum = SmallVec::<[CalcNode; 1]>::new();
|
let mut sum = SmallVec::<[CalcNode; 1]>::new();
|
||||||
sum.push(Self::parse_product(context, input, expected_unit)?);
|
sum.push(Self::parse_product(context, input, allowed_units)?);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let start = input.state();
|
let start = input.state();
|
||||||
|
@ -467,10 +514,10 @@ impl CalcNode {
|
||||||
}
|
}
|
||||||
match *input.next()? {
|
match *input.next()? {
|
||||||
Token::Delim('+') => {
|
Token::Delim('+') => {
|
||||||
sum.push(Self::parse_product(context, input, expected_unit)?);
|
sum.push(Self::parse_product(context, input, allowed_units)?);
|
||||||
},
|
},
|
||||||
Token::Delim('-') => {
|
Token::Delim('-') => {
|
||||||
let mut rhs = Self::parse_product(context, input, expected_unit)?;
|
let mut rhs = Self::parse_product(context, input, allowed_units)?;
|
||||||
rhs.negate();
|
rhs.negate();
|
||||||
sum.push(rhs);
|
sum.push(rhs);
|
||||||
},
|
},
|
||||||
|
@ -506,15 +553,15 @@ impl CalcNode {
|
||||||
fn parse_product<'i, 't>(
|
fn parse_product<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
expected_unit: CalcUnit,
|
allowed_units: CalcUnits,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
let mut node = Self::parse_one(context, input, expected_unit)?;
|
let mut node = Self::parse_one(context, input, allowed_units)?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let start = input.state();
|
let start = input.state();
|
||||||
match input.next() {
|
match input.next() {
|
||||||
Ok(&Token::Delim('*')) => {
|
Ok(&Token::Delim('*')) => {
|
||||||
let rhs = Self::parse_one(context, input, expected_unit)?;
|
let rhs = Self::parse_one(context, input, allowed_units)?;
|
||||||
if let Ok(rhs) = rhs.to_number() {
|
if let Ok(rhs) = rhs.to_number() {
|
||||||
node.mul_by(rhs);
|
node.mul_by(rhs);
|
||||||
} else if let Ok(number) = node.to_number() {
|
} else if let Ok(number) = node.to_number() {
|
||||||
|
@ -527,7 +574,7 @@ impl CalcNode {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Ok(&Token::Delim('/')) => {
|
Ok(&Token::Delim('/')) => {
|
||||||
let rhs = Self::parse_one(context, input, expected_unit)?;
|
let rhs = Self::parse_one(context, input, allowed_units)?;
|
||||||
// Dividing by units is not ok.
|
// Dividing by units is not ok.
|
||||||
//
|
//
|
||||||
// TODO(emilio): Eventually it should be.
|
// TODO(emilio): Eventually it should be.
|
||||||
|
@ -627,7 +674,7 @@ impl CalcNode {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if matches!(function, Sin | Cos | Tan | Asin | Acos | Atan) && !trig_enabled() {
|
if matches!(function, Sin | Cos | Tan | Asin | Acos | Atan | Atan2) && !trig_enabled() {
|
||||||
return Err(location.new_unexpected_token_error(Token::Function(name.clone())));
|
return Err(location.new_unexpected_token_error(Token::Function(name.clone())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,7 +697,7 @@ impl CalcNode {
|
||||||
clamping_mode: AllowedNumericType,
|
clamping_mode: AllowedNumericType,
|
||||||
function: MathFunction,
|
function: MathFunction,
|
||||||
) -> Result<CalcLengthPercentage, ParseError<'i>> {
|
) -> Result<CalcLengthPercentage, ParseError<'i>> {
|
||||||
Self::parse(context, input, function, CalcUnit::LengthPercentage)?
|
Self::parse(context, input, function, CalcUnits::LENGTH_PERCENTAGE)?
|
||||||
.into_length_or_percentage(clamping_mode)
|
.into_length_or_percentage(clamping_mode)
|
||||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
}
|
}
|
||||||
|
@ -661,7 +708,7 @@ impl CalcNode {
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
function: MathFunction,
|
function: MathFunction,
|
||||||
) -> Result<CSSFloat, ParseError<'i>> {
|
) -> Result<CSSFloat, ParseError<'i>> {
|
||||||
Self::parse(context, input, function, CalcUnit::Percentage)?
|
Self::parse(context, input, function, CalcUnits::PERCENTAGE)?
|
||||||
.to_percentage()
|
.to_percentage()
|
||||||
.map(crate::values::normalize)
|
.map(crate::values::normalize)
|
||||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
|
@ -674,7 +721,7 @@ impl CalcNode {
|
||||||
clamping_mode: AllowedNumericType,
|
clamping_mode: AllowedNumericType,
|
||||||
function: MathFunction,
|
function: MathFunction,
|
||||||
) -> Result<CalcLengthPercentage, ParseError<'i>> {
|
) -> Result<CalcLengthPercentage, ParseError<'i>> {
|
||||||
Self::parse(context, input, function, CalcUnit::Length)?
|
Self::parse(context, input, function, CalcUnits::LENGTH)?
|
||||||
.into_length_or_percentage(clamping_mode)
|
.into_length_or_percentage(clamping_mode)
|
||||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
}
|
}
|
||||||
|
@ -685,7 +732,7 @@ impl CalcNode {
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
function: MathFunction,
|
function: MathFunction,
|
||||||
) -> Result<CSSFloat, ParseError<'i>> {
|
) -> Result<CSSFloat, ParseError<'i>> {
|
||||||
Self::parse(context, input, function, CalcUnit::Number)?
|
Self::parse(context, input, function, CalcUnits::empty())?
|
||||||
.to_number()
|
.to_number()
|
||||||
.map(crate::values::normalize)
|
.map(crate::values::normalize)
|
||||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
|
@ -697,7 +744,7 @@ impl CalcNode {
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
function: MathFunction,
|
function: MathFunction,
|
||||||
) -> Result<Angle, ParseError<'i>> {
|
) -> Result<Angle, ParseError<'i>> {
|
||||||
Self::parse(context, input, function, CalcUnit::Angle)?
|
Self::parse(context, input, function, CalcUnits::ANGLE)?
|
||||||
.to_angle()
|
.to_angle()
|
||||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
}
|
}
|
||||||
|
@ -708,7 +755,7 @@ impl CalcNode {
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
function: MathFunction,
|
function: MathFunction,
|
||||||
) -> Result<Time, ParseError<'i>> {
|
) -> Result<Time, ParseError<'i>> {
|
||||||
Self::parse(context, input, function, CalcUnit::Time)?
|
Self::parse(context, input, function, CalcUnits::TIME)?
|
||||||
.to_time()
|
.to_time()
|
||||||
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||||
}
|
}
|
||||||
|
@ -719,7 +766,7 @@ impl CalcNode {
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
function: MathFunction,
|
function: MathFunction,
|
||||||
) -> Result<NumberOrPercentage, ParseError<'i>> {
|
) -> Result<NumberOrPercentage, ParseError<'i>> {
|
||||||
let node = Self::parse(context, input, function, CalcUnit::Percentage)?;
|
let node = Self::parse(context, input, function, CalcUnits::PERCENTAGE)?;
|
||||||
|
|
||||||
if let Ok(value) = node.to_number() {
|
if let Ok(value) = node.to_number() {
|
||||||
return Ok(NumberOrPercentage::Number { value });
|
return Ok(NumberOrPercentage::Number { value });
|
||||||
|
@ -737,7 +784,7 @@ impl CalcNode {
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
function: MathFunction,
|
function: MathFunction,
|
||||||
) -> Result<AngleOrNumber, ParseError<'i>> {
|
) -> Result<AngleOrNumber, ParseError<'i>> {
|
||||||
let node = Self::parse(context, input, function, CalcUnit::Angle)?;
|
let node = Self::parse(context, input, function, CalcUnits::ANGLE)?;
|
||||||
|
|
||||||
if let Ok(angle) = node.to_angle() {
|
if let Ok(angle) = node.to_angle() {
|
||||||
let degrees = angle.degrees();
|
let degrees = angle.degrees();
|
||||||
|
|
|
@ -90,25 +90,23 @@ impl FontBaseSize {
|
||||||
impl FontRelativeLength {
|
impl FontRelativeLength {
|
||||||
/// Return true if this is a zero value.
|
/// Return true if this is a zero value.
|
||||||
fn is_zero(&self) -> bool {
|
fn is_zero(&self) -> bool {
|
||||||
|
self.unitless_value() == 0.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the unitless, raw value.
|
||||||
|
fn unitless_value(&self) -> CSSFloat {
|
||||||
match *self {
|
match *self {
|
||||||
FontRelativeLength::Em(v) |
|
FontRelativeLength::Em(v) |
|
||||||
FontRelativeLength::Ex(v) |
|
FontRelativeLength::Ex(v) |
|
||||||
FontRelativeLength::Ch(v) |
|
FontRelativeLength::Ch(v) |
|
||||||
FontRelativeLength::Cap(v) |
|
FontRelativeLength::Cap(v) |
|
||||||
FontRelativeLength::Ic(v) |
|
FontRelativeLength::Ic(v) |
|
||||||
FontRelativeLength::Rem(v) => v == 0.,
|
FontRelativeLength::Rem(v) => v,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_negative(&self) -> bool {
|
fn is_negative(&self) -> bool {
|
||||||
match *self {
|
self.unitless_value() < 0.
|
||||||
FontRelativeLength::Em(v) |
|
|
||||||
FontRelativeLength::Ex(v) |
|
|
||||||
FontRelativeLength::Ch(v) |
|
|
||||||
FontRelativeLength::Cap(v) |
|
|
||||||
FontRelativeLength::Ic(v) |
|
|
||||||
FontRelativeLength::Rem(v) => v < 0.,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_sum(&self, other: &Self) -> Result<Self, ()> {
|
fn try_sum(&self, other: &Self) -> Result<Self, ()> {
|
||||||
|
@ -388,13 +386,16 @@ pub enum ViewportPercentageLength {
|
||||||
impl ViewportPercentageLength {
|
impl ViewportPercentageLength {
|
||||||
/// Return true if this is a zero value.
|
/// Return true if this is a zero value.
|
||||||
fn is_zero(&self) -> bool {
|
fn is_zero(&self) -> bool {
|
||||||
let (_, _, v) = self.unpack();
|
self.unitless_value() == 0.
|
||||||
v == 0.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_negative(&self) -> bool {
|
fn is_negative(&self) -> bool {
|
||||||
let (_, _, v) = self.unpack();
|
self.unitless_value() < 0.
|
||||||
v < 0.
|
}
|
||||||
|
|
||||||
|
/// Return the unitless, raw value.
|
||||||
|
fn unitless_value(&self) -> CSSFloat {
|
||||||
|
self.unpack().2
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unpack(&self) -> (ViewportVariant, ViewportUnit, CSSFloat) {
|
fn unpack(&self) -> (ViewportVariant, ViewportUnit, CSSFloat) {
|
||||||
|
@ -642,7 +643,8 @@ pub enum AbsoluteLength {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AbsoluteLength {
|
impl AbsoluteLength {
|
||||||
fn is_zero(&self) -> bool {
|
/// Return the unitless, raw value.
|
||||||
|
fn unitless_value(&self) -> CSSFloat {
|
||||||
match *self {
|
match *self {
|
||||||
AbsoluteLength::Px(v) |
|
AbsoluteLength::Px(v) |
|
||||||
AbsoluteLength::In(v) |
|
AbsoluteLength::In(v) |
|
||||||
|
@ -650,20 +652,16 @@ impl AbsoluteLength {
|
||||||
AbsoluteLength::Mm(v) |
|
AbsoluteLength::Mm(v) |
|
||||||
AbsoluteLength::Q(v) |
|
AbsoluteLength::Q(v) |
|
||||||
AbsoluteLength::Pt(v) |
|
AbsoluteLength::Pt(v) |
|
||||||
AbsoluteLength::Pc(v) => v == 0.,
|
AbsoluteLength::Pc(v) => v,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
self.unitless_value() == 0.
|
||||||
|
}
|
||||||
|
|
||||||
fn is_negative(&self) -> bool {
|
fn is_negative(&self) -> bool {
|
||||||
match *self {
|
self.unitless_value() < 0.
|
||||||
AbsoluteLength::Px(v) |
|
|
||||||
AbsoluteLength::In(v) |
|
|
||||||
AbsoluteLength::Cm(v) |
|
|
||||||
AbsoluteLength::Mm(v) |
|
|
||||||
AbsoluteLength::Q(v) |
|
|
||||||
AbsoluteLength::Pt(v) |
|
|
||||||
AbsoluteLength::Pc(v) => v < 0.,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert this into a pixel value.
|
/// Convert this into a pixel value.
|
||||||
|
@ -780,6 +778,16 @@ impl Mul<CSSFloat> for NoCalcLength {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NoCalcLength {
|
impl NoCalcLength {
|
||||||
|
/// Return the unitless, raw value.
|
||||||
|
pub fn unitless_value(&self) -> CSSFloat {
|
||||||
|
match *self {
|
||||||
|
NoCalcLength::Absolute(v) => v.unitless_value(),
|
||||||
|
NoCalcLength::FontRelative(v) => v.unitless_value(),
|
||||||
|
NoCalcLength::ViewportPercentage(v) => v.unitless_value(),
|
||||||
|
NoCalcLength::ServoCharacterWidth(c) => c.0 as f32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns whether the value of this length without unit is less than zero.
|
/// Returns whether the value of this length without unit is less than zero.
|
||||||
pub fn is_negative(&self) -> bool {
|
pub fn is_negative(&self) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue