Bug 1764850 Implement CSS round() function. r=emilio

Co-Authored-By: Emilio Cobos Álvarez <emilio@crisal.io>

Differential Revision: https://phabricator.services.mozilla.com/D156742
This commit is contained in:
Connor Pearson 2022-11-05 21:25:16 +00:00
Родитель 25e4585462
Коммит 0f359a70e2
13 изменённых файлов: 732 добавлений и 437 удалений

Просмотреть файл

@ -3538,6 +3538,12 @@ void StyleCalcNode::ScaleLengthsBy(float aScale) {
ScaleNode(*clamp.max);
break;
}
case Tag::Round: {
const auto& round = AsRound();
ScaleNode(*round.value);
ScaleNode(*round.step);
break;
}
case Tag::MinMax: {
for (auto& child : AsMinMax()._0.AsSpan()) {
ScaleNode(child);
@ -3580,6 +3586,52 @@ ResultT StyleCalcNode::ResolveInternal(ResultT aPercentageBasis,
auto max = clamp.max->ResolveInternal(aPercentageBasis, aConverter);
return std::max(min, std::min(center, max));
}
case Tag::Round: {
const auto& round = AsRound();
// Make sure to do the math in CSS pixels, so that floor() and ceil()
// below round to an integer number of CSS pixels, not app units.
CSSCoord step, value;
if constexpr (std::is_same_v<ResultT, CSSCoord>) {
step = round.step->ResolveInternal(aPercentageBasis, aConverter);
value = round.value->ResolveInternal(aPercentageBasis, aConverter);
} else {
step = CSSPixel::FromAppUnits(
round.step->ResolveInternal(aPercentageBasis, aConverter));
value = CSSPixel::FromAppUnits(
round.value->ResolveInternal(aPercentageBasis, aConverter));
}
const float div = value / step;
const CSSCoord lowerBound = std::floor(div) * step;
const CSSCoord upperBound = std::ceil(div) * step;
const CSSCoord result = [&] {
switch (round.strategy) {
case StyleRoundingStrategy::Nearest:
// In case of a tie, use the upper bound
if (value - lowerBound < upperBound - value) {
return lowerBound;
}
return upperBound;
case StyleRoundingStrategy::Up:
return upperBound;
case StyleRoundingStrategy::Down:
return lowerBound;
case StyleRoundingStrategy::ToZero:
// In case of a tie, use the upper bound
return std::abs(lowerBound) < std::abs(upperBound) ? lowerBound
: upperBound;
}
MOZ_ASSERT_UNREACHABLE("Unknown rounding strategy");
return CSSCoord(0);
}();
if constexpr (std::is_same_v<ResultT, CSSCoord>) {
return result;
} else {
return CSSPixel::ToAppUnits(result);
}
}
case Tag::MinMax: {
auto children = AsMinMax()._0.AsSpan();
StyleMinMaxOp op = AsMinMax()._1;

Просмотреть файл

@ -7940,6 +7940,13 @@
mirror: always
rust: true
# Whether the round() function is enabled in calc().
- name: layout.css.round.enabled
type: RelaxedAtomicBool
value: @IS_NIGHTLY_BUILD@
mirror: always
rust: true
# Whether infinity / nan constants are enabled in calc().
- name: layout.css.nan-inf.enabled
type: RelaxedAtomicBool

Просмотреть файл

@ -17,7 +17,7 @@ use crate::values::{specified, CSSFloat};
use crate::Zero;
use app_units::Au;
use std::fmt::{self, Write};
use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub};
use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Rem, Sub};
use style_traits::{CSSPixel, CssWriter, ToCss};
pub use super::image::Image;
@ -402,6 +402,15 @@ impl Neg for CSSPixelLength {
}
}
impl Rem for CSSPixelLength {
type Output = Self;
#[inline]
fn rem(self, other: Self) -> Self {
CSSPixelLength::new(self.0 % other.0)
}
}
impl Sub for CSSPixelLength {
type Output = Self;

Просмотреть файл

@ -624,10 +624,10 @@ impl PartialOrd for CalcLengthPercentageLeaf {
}
impl calc::CalcNodeLeaf for CalcLengthPercentageLeaf {
fn is_negative(&self) -> bool {
fn unitless_value(&self) -> f32 {
match *self {
Self::Length(ref l) => l.px() < 0.,
Self::Percentage(ref p) => p.0 < 0.,
Self::Length(ref l) => l.px(),
Self::Percentage(ref p) => p.0,
}
}
@ -657,6 +657,28 @@ impl calc::CalcNodeLeaf for CalcLengthPercentageLeaf {
Ok(())
}
fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
where
O: Fn(f32, f32) -> f32,
{
match (self, other) {
(
&CalcLengthPercentageLeaf::Length(ref one),
&CalcLengthPercentageLeaf::Length(ref other),
) => Ok(CalcLengthPercentageLeaf::Length(Length::new(op(
one.px(),
other.px(),
)))),
(
&CalcLengthPercentageLeaf::Percentage(one),
&CalcLengthPercentageLeaf::Percentage(other),
) => Ok(CalcLengthPercentageLeaf::Percentage(Percentage(op(
one.0, other.0,
)))),
_ => Err(()),
}
}
fn mul_by(&mut self, scalar: f32) {
match *self {
Self::Length(ref mut l) => *l = *l * scalar,

Просмотреть файл

@ -77,6 +77,30 @@ impl std::ops::AddAssign for Percentage {
}
}
impl std::ops::Add for Percentage {
type Output = Self;
fn add(self, other: Self) -> Self {
Percentage(self.0 + other.0)
}
}
impl std::ops::Sub for Percentage {
type Output = Self;
fn sub(self, other: Self) -> Self {
Percentage(self.0 - other.0)
}
}
impl std::ops::Rem for Percentage {
type Output = Self;
fn rem(self, other: Self) -> Self {
Percentage(self.0 % other.0)
}
}
impl ToCss for Percentage {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where

Просмотреть файл

@ -6,10 +6,10 @@
//!
//! [calc]: https://drafts.csswg.org/css-values/#calc-notation
use crate::Zero;
use num_traits::{Float, Zero};
use smallvec::SmallVec;
use std::fmt::{self, Write};
use std::ops::Add;
use std::ops::{Add, Div, Mul, Rem, Sub};
use std::{cmp, mem};
use style_traits::{CssWriter, ToCss};
@ -34,6 +34,35 @@ pub enum MinMaxOp {
Max,
}
/// The strategy used in `round()`
#[derive(
Clone,
Copy,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
ToAnimatedZero,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum RoundingStrategy {
/// `round(nearest, a, b)`
/// round a to the nearest multiple of b
Nearest,
/// `round(up, a, b)`
/// round a up to the nearest multiple of b
Up,
/// `round(down, a, b)`
/// round a down to the nearest multiple of b
Down,
/// `round(to-zero, a, b)`
/// round a to the nearest multiple of b that is towards zero
ToZero,
}
/// This determines the order in which we serialize members of a calc() sum.
///
/// See https://drafts.csswg.org/css-values-4/#sort-a-calculations-children
@ -124,18 +153,52 @@ pub enum GenericCalcNode<L> {
/// The maximum value.
max: Box<GenericCalcNode<L>>,
},
/// A `round()` function.
Round {
/// The rounding strategy.
strategy: RoundingStrategy,
/// The value to round.
value: Box<GenericCalcNode<L>>,
/// The step value.
step: Box<GenericCalcNode<L>>,
},
}
pub use self::GenericCalcNode as CalcNode;
/// A trait that represents all the stuff a valid leaf of a calc expression.
pub trait CalcNodeLeaf: Clone + Sized + PartialOrd + PartialEq + ToCss {
/// Returns the unitless value of this leaf.
fn unitless_value(&self) -> f32;
/// Whether this value is known-negative.
fn is_negative(&self) -> bool;
fn is_negative(&self) -> bool {
self.unitless_value().is_sign_negative()
}
/// Whether this value is infinite.
fn is_infinite(&self) -> bool {
self.unitless_value().is_infinite()
}
/// Whether this value is zero.
fn is_zero(&self) -> bool {
self.unitless_value().is_zero()
}
/// Whether this value is NaN.
fn is_nan(&self) -> bool {
self.unitless_value().is_nan()
}
/// Tries to merge one sum to another, that is, perform `x` + `y`.
fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()>;
/// Tries a generic arithmetic operation.
fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
where
O: Fn(f32, f32) -> f32;
/// Multiplies the leaf by a given scalar number.
fn mul_by(&mut self, scalar: f32);
@ -182,6 +245,19 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
}
}
/// Tries to apply a generic arithmentic operator
fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
where
O: Fn(f32, f32) -> f32,
{
match (self, other) {
(&CalcNode::Leaf(ref one), &CalcNode::Leaf(ref other)) => {
Ok(CalcNode::Leaf(one.try_op(other, op)?))
},
_ => Err(()),
}
}
/// Convert this `CalcNode` into a `CalcNode` with a different leaf kind.
pub fn map_leaves<O, F>(&self, mut map: F) -> CalcNode<O>
where
@ -225,6 +301,19 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
let max = Box::new(max.map_leaves_internal(map));
CalcNode::Clamp { min, center, max }
},
Self::Round {
strategy,
ref value,
ref step,
} => {
let value = Box::new(value.map_leaves_internal(map));
let step = Box::new(step.map_leaves_internal(map));
CalcNode::Round {
strategy,
value,
step,
}
},
}
}
@ -235,14 +324,30 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
mut leaf_to_output_fn: impl FnMut(&L) -> Result<O, ()>,
) -> Result<O, ()>
where
O: PartialOrd + PartialEq + Add<Output = O> + Zero,
O: PartialOrd
+ PartialEq
+ Add<Output = O>
+ Mul<Output = O>
+ Div<Output = O>
+ Sub<Output = O>
+ Zero
+ Float
+ Copy,
{
self.resolve_internal(&mut leaf_to_output_fn)
}
fn resolve_internal<O, F>(&self, leaf_to_output_fn: &mut F) -> Result<O, ()>
where
O: PartialOrd + PartialEq + Add<Output = O> + Zero,
O: PartialOrd
+ PartialEq
+ Add<Output = O>
+ Mul<Output = O>
+ Div<Output = O>
+ Sub<Output = O>
+ Zero
+ Float
+ Copy,
F: FnMut(&L) -> Result<O, ()>,
{
Ok(match *self {
@ -286,6 +391,84 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
}
result
},
Self::Round {
strategy,
ref value,
ref step,
} => {
let value = value.resolve_internal(leaf_to_output_fn)?;
let step = step.resolve_internal(leaf_to_output_fn)?;
// TODO(emilio): Seems like at least a few of these
// special-cases could be removed if we do the math in a
// particular order.
if step.is_zero() {
return Ok(<O as Float>::nan());
}
if value.is_infinite() && step.is_infinite() {
return Ok(<O as Float>::nan());
}
if value.is_infinite() {
return Ok(value);
}
if step.is_infinite() {
match strategy {
RoundingStrategy::Nearest | RoundingStrategy::ToZero => {
return if value.is_sign_negative() {
Ok(<O as Float>::neg_zero())
} else {
Ok(<O as Zero>::zero())
}
},
RoundingStrategy::Up => {
return if !value.is_sign_negative() && !value.is_zero() {
Ok(<O as Float>::infinity())
} else if !value.is_sign_negative() && value.is_zero() {
Ok(value)
} else {
Ok(<O as Float>::neg_zero())
}
},
RoundingStrategy::Down => {
return if value.is_sign_negative() && !value.is_zero() {
Ok(<O as Float>::neg_infinity())
} else if value.is_sign_negative() && value.is_zero() {
Ok(value)
} else {
Ok(<O as Zero>::zero())
}
},
}
}
let div = value / step;
let lower_bound = div.floor() * step;
let upper_bound = div.ceil() * step;
match strategy {
RoundingStrategy::Nearest => {
// In case of a tie, use the upper bound
if value - lower_bound < upper_bound - value {
lower_bound
} else {
upper_bound
}
},
RoundingStrategy::Up => upper_bound,
RoundingStrategy::Down => lower_bound,
RoundingStrategy::ToZero => {
// In case of a tie, use the upper bound
if lower_bound.abs() < upper_bound.abs() {
lower_bound
} else {
upper_bound
}
},
}
},
})
}
@ -296,6 +479,20 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
}
}
fn is_zero_leaf(&self) -> bool {
match *self {
Self::Leaf(ref l) => l.is_zero(),
_ => false,
}
}
fn is_infinite_leaf(&self) -> bool {
match *self {
Self::Leaf(ref l) => l.is_infinite(),
_ => false,
}
}
/// Multiplies the node by a scalar.
pub fn mul_by(&mut self, scalar: f32) {
match *self {
@ -334,6 +531,14 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
mem::swap(min, max);
}
},
Self::Round {
ref mut value,
ref mut step,
..
} => {
value.mul_by(scalar);
step.mul_by(scalar);
},
}
}
@ -357,6 +562,14 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
center.visit_depth_first_internal(f);
max.visit_depth_first_internal(f);
},
Self::Round {
ref mut value,
ref mut step,
..
} => {
value.visit_depth_first_internal(f);
step.visit_depth_first_internal(f);
},
Self::Sum(ref mut children) | Self::MinMax(ref mut children, _) => {
for child in &mut **children {
child.visit_depth_first_internal(f);
@ -432,6 +645,133 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
// Otherwise we're the center node.
return replace_self_with!(&mut **center);
},
Self::Round {
strategy,
ref mut value,
ref mut step,
} => {
if step.is_zero_leaf() {
value.mul_by(f32::NAN);
return replace_self_with!(&mut **value);
}
if value.is_infinite_leaf() && step.is_infinite_leaf() {
value.mul_by(f32::NAN);
return replace_self_with!(&mut **value);
}
if value.is_infinite_leaf() {
return replace_self_with!(&mut **value);
}
if step.is_infinite_leaf() {
match strategy {
RoundingStrategy::Nearest | RoundingStrategy::ToZero => {
value.mul_by(0.);
return replace_self_with!(&mut **value);
},
RoundingStrategy::Up => {
if !value.is_negative_leaf() && !value.is_zero_leaf() {
value.mul_by(f32::INFINITY);
return replace_self_with!(&mut **value);
} else if !value.is_negative_leaf() && value.is_zero_leaf() {
return replace_self_with!(&mut **value);
} else {
value.mul_by(0.);
return replace_self_with!(&mut **value);
}
},
RoundingStrategy::Down => {
if value.is_negative_leaf() && !value.is_zero_leaf() {
value.mul_by(f32::INFINITY);
return replace_self_with!(&mut **value);
} else if value.is_negative_leaf() && value.is_zero_leaf() {
return replace_self_with!(&mut **value);
} else {
value.mul_by(0.);
return replace_self_with!(&mut **value);
}
},
}
}
if step.is_negative_leaf() {
step.negate();
}
let remainder = match value.try_op(step, Rem::rem) {
Ok(res) => res,
Err(..) => return,
};
let (mut lower_bound, mut upper_bound) = if value.is_negative_leaf() {
let upper_bound = match value.try_op(&remainder, Sub::sub) {
Ok(res) => res,
Err(..) => return,
};
let lower_bound = match upper_bound.try_op(&step, Sub::sub) {
Ok(res) => res,
Err(..) => return,
};
(lower_bound, upper_bound)
} else {
let lower_bound = match value.try_op(&remainder, Sub::sub) {
Ok(res) => res,
Err(..) => return,
};
let upper_bound = match lower_bound.try_op(&step, Add::add) {
Ok(res) => res,
Err(..) => return,
};
(lower_bound, upper_bound)
};
match strategy {
RoundingStrategy::Nearest => {
let lower_diff = match value.try_op(&lower_bound, Sub::sub) {
Ok(res) => res,
Err(..) => return,
};
let upper_diff = match upper_bound.try_op(value, Sub::sub) {
Ok(res) => res,
Err(..) => return,
};
// In case of a tie, use the upper bound
if lower_diff < upper_diff {
return replace_self_with!(&mut lower_bound);
} else {
return replace_self_with!(&mut upper_bound);
}
},
RoundingStrategy::Up => return replace_self_with!(&mut upper_bound),
RoundingStrategy::Down => return replace_self_with!(&mut lower_bound),
RoundingStrategy::ToZero => {
let mut lower_diff = lower_bound.clone();
let mut upper_diff = upper_bound.clone();
if lower_diff.is_negative_leaf() {
lower_diff.negate();
}
if upper_diff.is_negative_leaf() {
upper_diff.negate();
}
// In case of a tie, use the upper bound
if lower_diff < upper_diff {
return replace_self_with!(&mut lower_bound);
} else {
return replace_self_with!(&mut upper_bound);
}
},
};
},
Self::MinMax(ref mut children, op) => {
let winning_order = match op {
MinMaxOp::Min => cmp::Ordering::Less,
@ -537,6 +877,16 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
dest.write_str("clamp(")?;
true
},
Self::Round { strategy, .. } => {
match strategy {
RoundingStrategy::Nearest => dest.write_str("round("),
RoundingStrategy::Up => dest.write_str("round(up, "),
RoundingStrategy::Down => dest.write_str("round(down, "),
RoundingStrategy::ToZero => dest.write_str("round(to-zero, "),
}?;
true
},
_ => {
if is_outermost {
dest.write_str("calc(")?;
@ -586,6 +936,15 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
dest.write_str(", ")?;
max.to_css_impl(dest, false)?;
},
Self::Round {
ref value,
ref step,
..
} => {
value.to_css_impl(dest, false)?;
dest.write_str(", ")?;
step.to_css_impl(dest, false)?;
},
Self::Leaf(ref l) => l.to_css(dest)?,
}

Просмотреть файл

@ -8,7 +8,7 @@
use crate::parser::ParserContext;
use crate::values::generics::calc as generic;
use crate::values::generics::calc::{MinMaxOp, SortKey};
use crate::values::generics::calc::{MinMaxOp, RoundingStrategy, SortKey};
use crate::values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength};
use crate::values::specified::length::{ContainerRelativeLength, ViewportPercentageLength};
use crate::values::specified::{self, Angle, Time};
@ -39,6 +39,8 @@ pub enum MathFunction {
Max,
/// `clamp()`: https://drafts.csswg.org/css-values-4/#funcdef-clamp
Clamp,
/// `round()`: https://drafts.csswg.org/css-values-4/#funcdef-round
Round,
/// `sin()`: https://drafts.csswg.org/css-values-4/#funcdef-sin
Sin,
/// `cos()`: https://drafts.csswg.org/css-values-4/#funcdef-cos
@ -179,12 +181,12 @@ impl PartialOrd for Leaf {
}
impl generic::CalcNodeLeaf for Leaf {
fn is_negative(&self) -> bool {
fn unitless_value(&self) -> f32 {
match *self {
Self::Length(ref l) => l.is_negative(),
Self::Percentage(n) | Self::Number(n) => n < 0.,
Self::Angle(ref a) => a.degrees() < 0.,
Self::Time(ref t) => t.seconds() < 0.,
Self::Length(ref l) => l.unitless_value(),
Self::Percentage(n) | Self::Number(n) => n,
Self::Angle(ref a) => a.degrees(),
Self::Time(ref t) => t.seconds(),
}
}
@ -294,7 +296,7 @@ impl generic::CalcNodeLeaf for Leaf {
*one = specified::Time::from_calc(one.seconds() + other.seconds());
},
(&mut Length(ref mut one), &Length(ref other)) => {
*one = one.try_sum(other)?;
*one = one.try_op(other, std::ops::Add::add)?;
},
_ => {
match *other {
@ -308,6 +310,49 @@ impl generic::CalcNodeLeaf for Leaf {
Ok(())
}
fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
where
O: Fn(f32, f32) -> f32,
{
use self::Leaf::*;
if std::mem::discriminant(self) != std::mem::discriminant(other) {
return Err(());
}
match (self, other) {
(&Number(one), &Number(other)) => {
return Ok(Leaf::Number(op(one, other)));
},
(&Percentage(one), &Percentage(other)) => {
return Ok(Leaf::Percentage(op(one, other)));
},
(&Angle(ref one), &Angle(ref other)) => {
return Ok(Leaf::Angle(specified::Angle::from_calc(op(
one.degrees(),
other.degrees(),
))));
},
(&Time(ref one), &Time(ref other)) => {
return Ok(Leaf::Time(specified::Time::from_calc(op(
one.seconds(),
other.seconds(),
))));
},
(&Length(ref one), &Length(ref other)) => {
return Ok(Leaf::Length(one.try_op(other, op)?));
},
_ => {
match *other {
Number(..) | Percentage(..) | Angle(..) | Time(..) | Length(..) => {},
}
unsafe {
debug_unreachable!();
}
},
}
}
}
/// A calc node representation for specified values.
@ -402,6 +447,36 @@ impl CalcNode {
max: Box::new(max),
})
},
MathFunction::Round => {
let strategy = input.try_parse(parse_rounding_strategy);
// <rounding-strategy> = nearest | up | down | to-zero
// https://drafts.csswg.org/css-values-4/#calc-syntax
fn parse_rounding_strategy<'i, 't>(
input: &mut Parser<'i, 't>,
) -> Result<RoundingStrategy, ParseError<'i>> {
Ok(try_match_ident_ignore_ascii_case! { input,
"nearest" => RoundingStrategy::Nearest,
"up" => RoundingStrategy::Up,
"down" => RoundingStrategy::Down,
"to-zero" => RoundingStrategy::ToZero,
})
}
if strategy.is_ok() {
input.expect_comma()?;
}
let value = Self::parse_argument(context, input, allowed_units)?;
input.expect_comma()?;
let step = Self::parse_argument(context, input, allowed_units)?;
Ok(Self::Round {
strategy: strategy.unwrap_or(RoundingStrategy::Nearest),
value: Box::new(value),
step: Box::new(step),
})
},
MathFunction::Min | MathFunction::Max => {
// TODO(emilio): The common case for parse_comma_separated
// is just one element, but for min / max is two, really...
@ -688,9 +763,15 @@ impl CalcNode {
},
};
if matches!(function, Sin | Cos | Tan | Asin | Acos | Atan | Atan2) &&
!static_prefs::pref!("layout.css.trig.enabled")
{
let enabled = if matches!(function, Sin | Cos | Tan | Asin | Acos | Atan | Atan2) {
trig_enabled()
} else if matches!(function, Round) {
static_prefs::pref!("layout.css.round.enabled")
} else {
true
};
if !enabled {
return Err(location.new_unexpected_token_error(Token::Function(name.clone())));
}

Просмотреть файл

@ -22,7 +22,7 @@ use crate::{Zero, ZeroNoPercent};
use app_units::Au;
use cssparser::{Parser, Token};
use std::cmp;
use std::ops::{Add, Mul};
use std::ops::{Add, Mul, Rem, Sub};
use style_traits::values::specified::AllowedNumericType;
use style_traits::{ParseError, SpecifiedValueInfo, StyleParseErrorKind};
@ -88,11 +88,6 @@ impl FontBaseSize {
}
impl FontRelativeLength {
/// Return true if this is a zero value.
fn is_zero(&self) -> bool {
self.unitless_value() == 0.
}
/// Return the unitless, raw value.
fn unitless_value(&self) -> CSSFloat {
match *self {
@ -105,11 +100,10 @@ impl FontRelativeLength {
}
}
fn is_negative(&self) -> bool {
self.unitless_value() < 0.
}
fn try_sum(&self, other: &Self) -> Result<Self, ()> {
fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
where
O: Fn(f32, f32) -> f32,
{
use self::FontRelativeLength::*;
if std::mem::discriminant(self) != std::mem::discriminant(other) {
@ -117,19 +111,19 @@ impl FontRelativeLength {
}
Ok(match (self, other) {
(&Em(one), &Em(other)) => Em(one + other),
(&Ex(one), &Ex(other)) => Ex(one + other),
(&Ch(one), &Ch(other)) => Ch(one + other),
(&Cap(one), &Cap(other)) => Cap(one + other),
(&Ic(one), &Ic(other)) => Ic(one + other),
(&Rem(one), &Rem(other)) => Rem(one + other),
(&Em(one), &Em(other)) => Em(op(one, other)),
(&Ex(one), &Ex(other)) => Ex(op(one, other)),
(&Ch(one), &Ch(other)) => Ch(op(one, other)),
(&Cap(one), &Cap(other)) => Cap(op(one, other)),
(&Ic(one), &Ic(other)) => Ic(op(one, other)),
(&Rem(one), &Rem(other)) => Rem(op(one, other)),
// See https://github.com/rust-lang/rust/issues/68867. rustc isn't
// able to figure it own on its own so we help.
_ => unsafe {
match *self {
Em(..) | Ex(..) | Ch(..) | Cap(..) | Ic(..) | Rem(..) => {},
}
debug_unreachable!("Forgot to handle unit in try_sum()")
debug_unreachable!("Forgot to handle unit in try_op()")
},
})
}
@ -384,15 +378,6 @@ pub enum ViewportPercentageLength {
}
impl ViewportPercentageLength {
/// Return true if this is a zero value.
fn is_zero(&self) -> bool {
self.unitless_value() == 0.
}
fn is_negative(&self) -> bool {
self.unitless_value() < 0.
}
/// Return the unitless, raw value.
fn unitless_value(&self) -> CSSFloat {
self.unpack().2
@ -431,7 +416,10 @@ impl ViewportPercentageLength {
}
}
fn try_sum(&self, other: &Self) -> Result<Self, ()> {
fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
where
O: Fn(f32, f32) -> f32,
{
use self::ViewportPercentageLength::*;
if std::mem::discriminant(self) != std::mem::discriminant(other) {
@ -439,30 +427,30 @@ impl ViewportPercentageLength {
}
Ok(match (self, other) {
(&Vw(one), &Vw(other)) => Vw(one + other),
(&Svw(one), &Svw(other)) => Svw(one + other),
(&Lvw(one), &Lvw(other)) => Lvw(one + other),
(&Dvw(one), &Dvw(other)) => Dvw(one + other),
(&Vh(one), &Vh(other)) => Vh(one + other),
(&Svh(one), &Svh(other)) => Svh(one + other),
(&Lvh(one), &Lvh(other)) => Lvh(one + other),
(&Dvh(one), &Dvh(other)) => Dvh(one + other),
(&Vmin(one), &Vmin(other)) => Vmin(one + other),
(&Svmin(one), &Svmin(other)) => Svmin(one + other),
(&Lvmin(one), &Lvmin(other)) => Lvmin(one + other),
(&Dvmin(one), &Dvmin(other)) => Dvmin(one + other),
(&Vmax(one), &Vmax(other)) => Vmax(one + other),
(&Svmax(one), &Svmax(other)) => Svmax(one + other),
(&Lvmax(one), &Lvmax(other)) => Lvmax(one + other),
(&Dvmax(one), &Dvmax(other)) => Dvmax(one + other),
(&Vb(one), &Vb(other)) => Vb(one + other),
(&Svb(one), &Svb(other)) => Svb(one + other),
(&Lvb(one), &Lvb(other)) => Lvb(one + other),
(&Dvb(one), &Dvb(other)) => Dvb(one + other),
(&Vi(one), &Vi(other)) => Vi(one + other),
(&Svi(one), &Svi(other)) => Svi(one + other),
(&Lvi(one), &Lvi(other)) => Lvi(one + other),
(&Dvi(one), &Dvi(other)) => Dvi(one + other),
(&Vw(one), &Vw(other)) => Vw(op(one, other)),
(&Svw(one), &Svw(other)) => Svw(op(one, other)),
(&Lvw(one), &Lvw(other)) => Lvw(op(one, other)),
(&Dvw(one), &Dvw(other)) => Dvw(op(one, other)),
(&Vh(one), &Vh(other)) => Vh(op(one, other)),
(&Svh(one), &Svh(other)) => Svh(op(one, other)),
(&Lvh(one), &Lvh(other)) => Lvh(op(one, other)),
(&Dvh(one), &Dvh(other)) => Dvh(op(one, other)),
(&Vmin(one), &Vmin(other)) => Vmin(op(one, other)),
(&Svmin(one), &Svmin(other)) => Svmin(op(one, other)),
(&Lvmin(one), &Lvmin(other)) => Lvmin(op(one, other)),
(&Dvmin(one), &Dvmin(other)) => Dvmin(op(one, other)),
(&Vmax(one), &Vmax(other)) => Vmax(op(one, other)),
(&Svmax(one), &Svmax(other)) => Svmax(op(one, other)),
(&Lvmax(one), &Lvmax(other)) => Lvmax(op(one, other)),
(&Dvmax(one), &Dvmax(other)) => Dvmax(op(one, other)),
(&Vb(one), &Vb(other)) => Vb(op(one, other)),
(&Svb(one), &Svb(other)) => Svb(op(one, other)),
(&Lvb(one), &Lvb(other)) => Lvb(op(one, other)),
(&Dvb(one), &Dvb(other)) => Dvb(op(one, other)),
(&Vi(one), &Vi(other)) => Vi(op(one, other)),
(&Svi(one), &Svi(other)) => Svi(op(one, other)),
(&Lvi(one), &Lvi(other)) => Lvi(op(one, other)),
(&Dvi(one), &Dvi(other)) => Dvi(op(one, other)),
// See https://github.com/rust-lang/rust/issues/68867. rustc isn't
// able to figure it own on its own so we help.
_ => unsafe {
@ -472,7 +460,7 @@ impl ViewportPercentageLength {
Svmax(..) | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) |
Vi(..) | Svi(..) | Lvi(..) | Dvi(..) => {},
}
debug_unreachable!("Forgot to handle unit in try_sum()")
debug_unreachable!("Forgot to handle unit in try_op()")
},
})
}
@ -564,14 +552,6 @@ impl AbsoluteLength {
}
}
fn is_zero(&self) -> bool {
self.unitless_value() == 0.
}
fn is_negative(&self) -> bool {
self.unitless_value() < 0.
}
/// Convert this into a pixel value.
#[inline]
pub fn to_px(&self) -> CSSFloat {
@ -588,6 +568,22 @@ impl AbsoluteLength {
};
pixel.min(f32::MAX).max(f32::MIN)
}
fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
where
O: Fn(f32, f32) -> f32,
{
Ok(match (self, other) {
(AbsoluteLength::Px(x), AbsoluteLength::Px(y)) => AbsoluteLength::Px(op(*x, *y)),
(AbsoluteLength::In(x), AbsoluteLength::In(y)) => AbsoluteLength::In(op(*x, *y)),
(AbsoluteLength::Cm(x), AbsoluteLength::Cm(y)) => AbsoluteLength::Cm(op(*x, *y)),
(AbsoluteLength::Mm(x), AbsoluteLength::Mm(y)) => AbsoluteLength::Mm(op(*x, *y)),
(AbsoluteLength::Q(x), AbsoluteLength::Q(y)) => AbsoluteLength::Q(op(*x, *y)),
(AbsoluteLength::Pt(x), AbsoluteLength::Pt(y)) => AbsoluteLength::Pt(op(*x, *y)),
(AbsoluteLength::Pc(x), AbsoluteLength::Pc(y)) => AbsoluteLength::Pc(op(*x, *y)),
_ => AbsoluteLength::Px(op(self.to_px(), other.to_px())),
})
}
}
impl ToComputedValue for AbsoluteLength {
@ -680,15 +676,10 @@ impl ContainerRelativeLength {
}
}
fn is_zero(&self) -> bool {
self.unitless_value() == 0.
}
fn is_negative(&self) -> bool {
self.unitless_value() < 0.
}
fn try_sum(&self, other: &Self) -> Result<Self, ()> {
pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
where
O: Fn(f32, f32) -> f32,
{
use self::ContainerRelativeLength::*;
if std::mem::discriminant(self) != std::mem::discriminant(other) {
@ -696,12 +687,12 @@ impl ContainerRelativeLength {
}
Ok(match (self, other) {
(&Cqw(one), &Cqw(other)) => Cqw(one + other),
(&Cqh(one), &Cqh(other)) => Cqh(one + other),
(&Cqi(one), &Cqi(other)) => Cqi(one + other),
(&Cqb(one), &Cqb(other)) => Cqb(one + other),
(&Cqmin(one), &Cqmin(other)) => Cqmin(one + other),
(&Cqmax(one), &Cqmax(other)) => Cqmax(one + other),
(&Cqw(one), &Cqw(other)) => Cqw(op(one, other)),
(&Cqh(one), &Cqh(other)) => Cqh(op(one, other)),
(&Cqi(one), &Cqi(other)) => Cqi(op(one, other)),
(&Cqb(one), &Cqb(other)) => Cqb(op(one, other)),
(&Cqmin(one), &Cqmin(other)) => Cqmin(op(one, other)),
(&Cqmax(one), &Cqmax(other)) => Cqmax(op(one, other)),
// See https://github.com/rust-lang/rust/issues/68867, then
// https://github.com/rust-lang/rust/pull/95161. rustc isn't
@ -710,7 +701,7 @@ impl ContainerRelativeLength {
match *self {
Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {},
}
debug_unreachable!("Forgot to handle unit in try_sum()")
debug_unreachable!("Forgot to handle unit in try_op()")
},
})
}
@ -751,6 +742,42 @@ fn are_container_queries_enabled() -> bool {
false
}
impl Sub<AbsoluteLength> for AbsoluteLength {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
match (self, rhs) {
(AbsoluteLength::Px(x), AbsoluteLength::Px(y)) => AbsoluteLength::Px(x - y),
(AbsoluteLength::In(x), AbsoluteLength::In(y)) => AbsoluteLength::In(x - y),
(AbsoluteLength::Cm(x), AbsoluteLength::Cm(y)) => AbsoluteLength::Cm(x - y),
(AbsoluteLength::Mm(x), AbsoluteLength::Mm(y)) => AbsoluteLength::Mm(x - y),
(AbsoluteLength::Q(x), AbsoluteLength::Q(y)) => AbsoluteLength::Q(x - y),
(AbsoluteLength::Pt(x), AbsoluteLength::Pt(y)) => AbsoluteLength::Pt(x - y),
(AbsoluteLength::Pc(x), AbsoluteLength::Pc(y)) => AbsoluteLength::Pc(x - y),
_ => AbsoluteLength::Px(self.to_px() - rhs.to_px()),
}
}
}
impl Rem<AbsoluteLength> for AbsoluteLength {
type Output = Self;
#[inline]
fn rem(self, rhs: Self) -> Self {
match (self, rhs) {
(AbsoluteLength::Px(x), AbsoluteLength::Px(y)) => AbsoluteLength::Px(x % y),
(AbsoluteLength::In(x), AbsoluteLength::In(y)) => AbsoluteLength::In(x % y),
(AbsoluteLength::Cm(x), AbsoluteLength::Cm(y)) => AbsoluteLength::Cm(x % y),
(AbsoluteLength::Mm(x), AbsoluteLength::Mm(y)) => AbsoluteLength::Mm(x % y),
(AbsoluteLength::Q(x), AbsoluteLength::Q(y)) => AbsoluteLength::Q(x % y),
(AbsoluteLength::Pt(x), AbsoluteLength::Pt(y)) => AbsoluteLength::Pt(x % y),
(AbsoluteLength::Pc(x), AbsoluteLength::Pc(y)) => AbsoluteLength::Pc(x % y),
_ => AbsoluteLength::Px(self.to_px() % rhs.to_px()),
}
}
}
/// A `<length>` without taking `calc` expressions into account
///
/// <https://drafts.csswg.org/css-values/#lengths>
@ -812,13 +839,22 @@ impl NoCalcLength {
/// Returns whether the value of this length without unit is less than zero.
pub fn is_negative(&self) -> bool {
match *self {
NoCalcLength::Absolute(v) => v.is_negative(),
NoCalcLength::FontRelative(v) => v.is_negative(),
NoCalcLength::ViewportPercentage(v) => v.is_negative(),
NoCalcLength::ContainerRelative(v) => v.is_negative(),
NoCalcLength::ServoCharacterWidth(c) => c.0 < 0,
}
self.unitless_value().is_sign_negative()
}
/// Returns whether the value of this length without unit is equal to zero.
pub fn is_zero(&self) -> bool {
self.unitless_value() == 0.0
}
/// Returns whether the value of this length without unit is infinite.
pub fn is_infinite(&self) -> bool {
self.unitless_value().is_infinite()
}
/// Returns whether the value of this length without unit is NaN.
pub fn is_nan(&self) -> bool {
self.unitless_value().is_nan()
}
/// Whether text-only zoom should be applied to this length.
@ -953,8 +989,10 @@ impl NoCalcLength {
})
}
/// Try to sume two lengths if compatible into the left hand side.
pub(crate) fn try_sum(&self, other: &Self) -> Result<Self, ()> {
pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
where
O: Fn(f32, f32) -> f32,
{
use self::NoCalcLength::*;
if std::mem::discriminant(self) != std::mem::discriminant(other) {
@ -962,16 +1000,18 @@ impl NoCalcLength {
}
Ok(match (self, other) {
(&Absolute(ref one), &Absolute(ref other)) => Absolute(*one + *other),
(&FontRelative(ref one), &FontRelative(ref other)) => FontRelative(one.try_sum(other)?),
(&Absolute(ref one), &Absolute(ref other)) => Absolute(one.try_op(other, op)?),
(&FontRelative(ref one), &FontRelative(ref other)) => {
FontRelative(one.try_op(other, op)?)
},
(&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {
ViewportPercentage(one.try_sum(other)?)
ViewportPercentage(one.try_op(other, op)?)
},
(&ContainerRelative(ref one), &ContainerRelative(ref other)) => {
ContainerRelative(one.try_sum(other)?)
ContainerRelative(one.try_op(other, op)?)
},
(&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {
ServoCharacterWidth(CharacterWidth(one.0 + other.0))
ServoCharacterWidth(CharacterWidth(op(one.0 as f32, other.0 as f32) as i32))
},
// See https://github.com/rust-lang/rust/issues/68867. rustc isn't
// able to figure it own on its own so we help.
@ -983,7 +1023,7 @@ impl NoCalcLength {
ContainerRelative(..) |
ServoCharacterWidth(..) => {},
}
debug_unreachable!("Forgot to handle unit in try_sum()")
debug_unreachable!("Forgot to handle unit in try_op()")
},
})
}
@ -1046,13 +1086,7 @@ impl Zero for NoCalcLength {
}
fn is_zero(&self) -> bool {
match *self {
NoCalcLength::Absolute(v) => v.is_zero(),
NoCalcLength::FontRelative(v) => v.is_zero(),
NoCalcLength::ViewportPercentage(v) => v.is_zero(),
NoCalcLength::ContainerRelative(v) => v.is_zero(),
NoCalcLength::ServoCharacterWidth(v) => v.0 == 0,
}
NoCalcLength::is_zero(self)
}
}
@ -1171,7 +1205,7 @@ impl PartialOrd for ContainerRelativeLength {
match *self {
Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {},
}
debug_unreachable!("Forgot to handle unit in try_sum()")
debug_unreachable!("Forgot to handle unit in partial_cmp()")
},
}
}

Просмотреть файл

@ -1,185 +0,0 @@
[round-function.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[round(15px, -10px) should be used-value-equivalent to 20px]
expected: FAIL
[round(nearest, 15px, 10px) should be used-value-equivalent to 20px]
expected: FAIL
[round(nearest, 13px, 10px) should be used-value-equivalent to 10px]
expected: FAIL
[round(down, 18px, 10px) should be used-value-equivalent to 10px]
expected: FAIL
[round(-18px, 10px) should be used-value-equivalent to -20px]
expected: FAIL
[round(up, 18px, 10px) should be used-value-equivalent to 20px]
expected: FAIL
[round(15px, 10px) should be used-value-equivalent to 20px]
expected: FAIL
[round(down, 13px, 10px) should be used-value-equivalent to 10px]
expected: FAIL
[round(23px, 10px) should be used-value-equivalent to 20px]
expected: FAIL
[round(13px, 10px) should be used-value-equivalent to 10px]
expected: FAIL
[round(down, -13px, 10px) should be used-value-equivalent to -20px]
expected: FAIL
[round(13px, -10px) should be used-value-equivalent to 10px]
expected: FAIL
[round(to-zero, 23px, 10px) should be used-value-equivalent to 20px]
expected: FAIL
[round(down, 15px, 10px) should be used-value-equivalent to 10px]
expected: FAIL
[round(up, -13px, 10px) should be used-value-equivalent to -10px]
expected: FAIL
[round(to-zero, 13px, 10px) should be used-value-equivalent to 10px]
expected: FAIL
[round(down, -18px, 10px) should be used-value-equivalent to -20px]
expected: FAIL
[round(up, -18px, 10px) should be used-value-equivalent to -10px]
expected: FAIL
[round(to-zero, -13px, 10px) should be used-value-equivalent to -10px]
expected: FAIL
[round(18px, -10px) should be used-value-equivalent to 20px]
expected: FAIL
[round(18px, 10px) should be used-value-equivalent to 20px]
expected: FAIL
[round(up, 15px, 10px) should be used-value-equivalent to 20px]
expected: FAIL
[round(23px, -10px) should be used-value-equivalent to 20px]
expected: FAIL
[round(nearest, 23px, 10px) should be used-value-equivalent to 20px]
expected: FAIL
[round(up, 13px, 10px) should be used-value-equivalent to 20px]
expected: FAIL
[round(nearest, -18px, 10px) should be used-value-equivalent to -20px]
expected: FAIL
[round(down, 23px, 10px) should be used-value-equivalent to 20px]
expected: FAIL
[round(-13px, 10px) should be used-value-equivalent to -10px]
expected: FAIL
[round(nearest, 18px, 10px) should be used-value-equivalent to 20px]
expected: FAIL
[round(to-zero, 18px, 10px) should be used-value-equivalent to 10px]
expected: FAIL
[round(-13px, -10px) should be used-value-equivalent to -10px]
expected: FAIL
[round(to-zero, 15px, 10px) should be used-value-equivalent to 10px]
expected: FAIL
[round(nearest, -13px, 10px) should be used-value-equivalent to -10px]
expected: FAIL
[round(-18px, -10px) should be used-value-equivalent to -20px]
expected: FAIL
[round(up, 23px, 10px) should be used-value-equivalent to 30px]
expected: FAIL
[round(to-zero, -18px, 10px) should be used-value-equivalent to -10px]
expected: FAIL
[round(infinity, 5) should be used-value-equivalent to calc(infinity)]
expected: FAIL
[round(infinity, -5) should be used-value-equivalent to calc(infinity)]
expected: FAIL
[round(-infinity, 5) should be used-value-equivalent to calc(-infinity)]
expected: FAIL
[round(-infinity, -5) should be used-value-equivalent to calc(-infinity)]
expected: FAIL
[calc(1 / round(5, infinity)) should be used-value-equivalent to calc(infinity)]
expected: FAIL
[calc(1 / round(5, -infinity)) should be used-value-equivalent to calc(infinity)]
expected: FAIL
[calc(1 / round(-5, infinity)) should be used-value-equivalent to calc(-infinity)]
expected: FAIL
[calc(1 / round(-5, -infinity)) should be used-value-equivalent to calc(-infinity)]
expected: FAIL
[calc(1 / round(to-zero, 5, infinity)) should be used-value-equivalent to calc(infinity)]
expected: FAIL
[calc(1 / round(to-zero, 5, -infinity)) should be used-value-equivalent to calc(infinity)]
expected: FAIL
[calc(1 / round(to-zero, -5, infinity)) should be used-value-equivalent to calc(-infinity)]
expected: FAIL
[calc(1 / round(to-zero, -5, -infinity)) should be used-value-equivalent to calc(-infinity)]
expected: FAIL
[round(up, 1, infinity) should be used-value-equivalent to calc(infinity)]
expected: FAIL
[calc(1 / round(up, 0, infinity)) should be used-value-equivalent to calc(infinity)]
expected: FAIL
[calc(1 / round(up, -1 * 0, infinity) should be used-value-equivalent to calc(-infinity)]
expected: FAIL
[calc(1 / round(up, -1, infinity) should be used-value-equivalent to calc(-infinity)]
expected: FAIL
[round(down, -1, infinity) should be used-value-equivalent to calc(-infinity)]
expected: FAIL
[calc(1 / round(down, -1 * 0, infinity)) should be used-value-equivalent to calc(-infinity)]
expected: FAIL
[calc(1 / round(down, 0, infinity)) should be used-value-equivalent to calc(infinity)]
expected: FAIL
[calc(1 / round(down, 1, infinity)) should be used-value-equivalent to calc(infinity)]
expected: FAIL
[round(-infinity, infinity) should be used-value-equivalent to calc(infinity)]
expected: FAIL
[round(infinity, -infinity) should be used-value-equivalent to calc(infinity)]
expected: FAIL
[round(5, 0) should be used-value-equivalent to calc(infinity)]
expected: FAIL
[round(infinity, infinity) should be used-value-equivalent to calc(infinity)]
expected: FAIL
[round(-infinity, -infinity) should be used-value-equivalent to calc(infinity)]
expected: FAIL

Просмотреть файл

@ -1,36 +1,10 @@
[round-mod-rem-computed.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[round(10,10) should be used-value-equivalent to 10]
expected: FAIL
[mod(1,1) should be used-value-equivalent to 0]
expected: FAIL
[rem(1,1) should be used-value-equivalent to 0]
expected: FAIL
[calc(round(100,10)) should be used-value-equivalent to 100]
expected: FAIL
[calc(round(up, 101,10)) should be used-value-equivalent to 110]
expected: FAIL
[calc(round(down, 106,10)) should be used-value-equivalent to 100]
expected: FAIL
[calc(round(to-zero,105, 10)) should be used-value-equivalent to 100]
expected: FAIL
[calc(round(to-zero,-105, 10)) should be used-value-equivalent to 100]
expected: FAIL
[calc(round(-100,10)) should be used-value-equivalent to -100]
expected: FAIL
[calc(round(up, -103,10)) should be used-value-equivalent to -100]
expected: FAIL
[mod(18,5) should be used-value-equivalent to 3]
expected: FAIL
@ -52,24 +26,6 @@
[rem(140,-90) should be used-value-equivalent to 50]
expected: FAIL
[calc(round(round(100,10), 10)) should be used-value-equivalent to 100]
expected: FAIL
[calc(round(up, round(100,10) + 1,10)) should be used-value-equivalent to 110]
expected: FAIL
[calc(round(down, round(100,10) + 2 * 3,10)) should be used-value-equivalent to 100]
expected: FAIL
[calc(round(to-zero,round(100,10) * 2 - 95, 10)) should be used-value-equivalent to 100]
expected: FAIL
[calc(round(round(100,10)* -1,10)) should be used-value-equivalent to -100]
expected: FAIL
[calc(round(up, -103 + -103 / -103 - 1,10)) should be used-value-equivalent to -100]
expected: FAIL
[calc(mod(18,5) * 2 + mod(17,5)) should be used-value-equivalent to 8]
expected: FAIL
@ -85,63 +41,6 @@
[calc(mod(rem(1,18)* -1,5)) should be used-value-equivalent to -1]
expected: FAIL
[round(10px,6px) should be used-value-equivalent to 12px]
expected: FAIL
[round(10cm,6cm) should be used-value-equivalent to 12cm]
expected: FAIL
[round(10mm,6mm) should be used-value-equivalent to 12mm]
expected: FAIL
[round(10Q, 6Q) should be used-value-equivalent to 12Q]
expected: FAIL
[round(10in,6in) should be used-value-equivalent to 12in]
expected: FAIL
[round(10pc,6pc) should be used-value-equivalent to 12pc]
expected: FAIL
[round(10pt,6pt) should be used-value-equivalent to 12pt]
expected: FAIL
[round(10em,6em) should be used-value-equivalent to 12em]
expected: FAIL
[round(10ex,6ex) should be used-value-equivalent to 12ex]
expected: FAIL
[round(10ch,6ch) should be used-value-equivalent to 12ch]
expected: FAIL
[round(10rem,6rem) should be used-value-equivalent to 12rem]
expected: FAIL
[round(10vh,6vh) should be used-value-equivalent to 12vh]
expected: FAIL
[round(10vw,6vw) should be used-value-equivalent to 12vw]
expected: FAIL
[round(10vmin,6vmin) should be used-value-equivalent to 12vmin]
expected: FAIL
[round(10vmax,6vmax) should be used-value-equivalent to 12vmax]
expected: FAIL
[round(10deg,6deg) should be used-value-equivalent to 12deg]
expected: FAIL
[round(10grad,6grad) should be used-value-equivalent to 12grad]
expected: FAIL
[round(10rad,6rad) should be used-value-equivalent to 12rad]
expected: FAIL
[round(10turn,6turn) should be used-value-equivalent to 12turn]
expected: FAIL
[mod(10px,6px) should be used-value-equivalent to 4px]
expected: FAIL
@ -250,12 +149,6 @@
[rem(10turn,6turn) should be used-value-equivalent to 4turn]
expected: FAIL
[round(10s,6s) should be used-value-equivalent to 12s]
expected: FAIL
[round(10ms,6ms) should be used-value-equivalent to 12ms]
expected: FAIL
[mod(10s,6s) should be used-value-equivalent to 4s]
expected: FAIL

Просмотреть файл

@ -1,15 +1,4 @@
[round-mod-rem-serialize.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
['round(1.1,1)' as a specified value should serialize as 'calc(1)'.]
expected: FAIL
['scale(round(1.1,1))' as a specified value should serialize as 'scale(calc(1))'.]
expected: FAIL
['scale(round(1.1,1))' as a computed value should serialize as 'matrix(1, 0, 0, 1, 0, 0)'.]
expected: FAIL
['mod(1,1)' as a specified value should serialize as 'calc(0)'.]
expected: FAIL

Просмотреть файл

@ -61,12 +61,12 @@ test_math_used("round(-18px, -10px)", "-20px");
// Extreme cases:
// 0 step is NaN
test_plus_infinity("round(5, 0)");
test_nan("round(5, 0)");
// both infinite is NaN
test_plus_infinity("round(infinity, infinity)");
test_plus_infinity("round(infinity, -infinity)");
test_plus_infinity("round(-infinity, infinity)");
test_plus_infinity("round(-infinity, -infinity)");
test_nan("round(infinity, infinity)");
test_nan("round(infinity, -infinity)");
test_nan("round(-infinity, infinity)");
test_nan("round(-infinity, -infinity)");
// infinite value with finite step is the same infinity
test_plus_infinity("round(infinity, 5)");
@ -87,8 +87,8 @@ test_minus_zero("round(to-zero, -5, -infinity)");
// 'up': pos goes to +inf, 0+ goes to 0+, else 0-
test_plus_infinity("round(up, 1, infinity)");
test_plus_zero("round(up, 0, infinity)");
test_minus_zero("round(up, -1 * 0, infinity");
test_minus_zero("round(up, -1, infinity");
test_minus_zero("round(up, -1 * 0, infinity)");
test_minus_zero("round(up, -1, infinity)");
// 'down': neg goes to -inf, -0 goes to -0, else 0+
test_minus_infinity("round(down, -1, infinity)");
test_minus_zero("round(down, -1 * 0, infinity)");

Просмотреть файл

@ -6,7 +6,9 @@
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../support/numeric-testcommon.js"></script>
<div id="target"></div>
<div style="width: 75px;">
<div id="target"></div>
</div>
<script>
// Simple tests
test_math_used('round(10,10)', '10', {type:'number'});
@ -18,7 +20,7 @@ test_math_used('calc(round(100,10))', '100', {type:'number'});
test_math_used('calc(round(up, 101,10))', '110', {type:'number'});
test_math_used('calc(round(down, 106,10))', '100', {type:'number'});
test_math_used('calc(round(to-zero,105, 10))', '100', {type:'number'});
test_math_used('calc(round(to-zero,-105, 10))', '100', {type:'number'});
test_math_used('calc(round(to-zero,-105, 10))', '-100', {type:'number'});
test_math_used('calc(round(-100,10))', '-100', {type:'number'});
test_math_used('calc(round(up, -103,10))', '-100', {type:'number'});
@ -60,8 +62,8 @@ test_math_used('round(10vh,6vh)', '12vh');
test_math_used('round(10vw,6vw)', '12vw');
test_math_used('round(10vmin,6vmin)', '12vmin');
test_math_used('round(10vmax,6vmax)', '12vmax');
test_math_used('round(10s,6s)', '12s');
test_math_used('round(10ms,6ms)', '12ms');
test_math_used('round(10s,6s)', '12s', {type:'time'});
test_math_used('round(10ms,6ms)', '12ms', {type:'time'});
test_math_used('round(10deg,6deg)', '12deg', {type:'angle', approx:0.1});
test_math_used('round(10grad,6grad)', '12grad', {type:'angle', approx:0.1});
test_math_used('round(10rad,6rad)', '12rad',{type:'angle', approx:0.1});
@ -81,8 +83,8 @@ test_math_used('mod(10vh,6vh)', '4vh');
test_math_used('mod(10vw,6vw)', '4vw');
test_math_used('mod(10vmin,6vmin)', '4vmin');
test_math_used('mod(10vmax,6vmax)', '4vmax');
test_math_used('mod(10s,6s)', '4s');
test_math_used('mod(10ms,6ms)', '4ms');
test_math_used('mod(10s,6s)', '4s', {type:'time'});
test_math_used('mod(10ms,6ms)', '4ms', {type:'time'});
test_math_used('mod(10deg,6deg)', '4deg', {type:'angle', approx:0.1});
test_math_used('mod(10grad,6grad)', '4grad', {type:'angle', approx:0.1});
test_math_used('mod(10rad,6rad)', '4rad',{type:'angle', approx:0.1});
@ -102,10 +104,18 @@ test_math_used('rem(10vh,6vh)', '4vh');
test_math_used('rem(10vw,6vw)', '4vw');
test_math_used('rem(10vmin,6vmin)', '4vmin');
test_math_used('rem(10vmax,6vmax)', '4vmax');
test_math_used('rem(10s,6s)', '4s');
test_math_used('rem(10ms,6ms)', '4ms');
test_math_used('rem(10s,6s)', '4s', {type:'time'});
test_math_used('rem(10ms,6ms)', '4ms', {type:'time'});
test_math_used('rem(10deg,6deg)', '4deg', {type:'angle', approx:0.1});
test_math_used('rem(10grad,6grad)', '4grad', {type:'angle', approx:0.1});
test_math_used('rem(10rad,6rad)', '4rad',{type:'angle', approx:0.1});
test_math_used('rem(10turn,6turn)', '4turn',{type:'angle', approx:0.1});
//Test percentage and mixed units
test_math_used('round(10%,1px)', '8px');
test_math_used('round(10%,5px)', '10px');
test_math_used('round(2rem,5px)', '30px');
test_math_used('round(100px,1rem)', '96px');
test_math_used('round(10s,6000ms)', '12s', {type:'time'});
test_math_used('round(10000ms,6s)', '12s', {type:'time'});
</script>