зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
25e4585462
Коммит
0f359a70e2
|
@ -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>
|
Загрузка…
Ссылка в новой задаче