mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
style: Implement CSS mod() and rem() functions
Differential Revision: https://phabricator.services.mozilla.com/D163166
This commit is contained in:
parent
653b37f80a
commit
e55c03c8ff
2 changed files with 152 additions and 1 deletions
|
@ -34,6 +34,27 @@ pub enum MinMaxOp {
|
|||
Max,
|
||||
}
|
||||
|
||||
/// Whether we're a `mod` or `rem` function.
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Deserialize,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
ToAnimatedZero,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
#[repr(u8)]
|
||||
pub enum ModRemOp {
|
||||
/// `mod()`
|
||||
Mod,
|
||||
/// `rem()`
|
||||
Rem,
|
||||
}
|
||||
|
||||
/// The strategy used in `round()`
|
||||
#[derive(
|
||||
Clone,
|
||||
|
@ -162,6 +183,15 @@ pub enum GenericCalcNode<L> {
|
|||
/// The step value.
|
||||
step: Box<GenericCalcNode<L>>,
|
||||
},
|
||||
/// A `mod()` or `rem()` function.
|
||||
ModRem {
|
||||
/// The dividend calculation.
|
||||
dividend: Box<GenericCalcNode<L>>,
|
||||
/// The divisor calculation.
|
||||
divisor: Box<GenericCalcNode<L>>,
|
||||
/// Is the function mod or rem?
|
||||
op: ModRemOp,
|
||||
},
|
||||
}
|
||||
|
||||
pub use self::GenericCalcNode as CalcNode;
|
||||
|
@ -314,6 +344,19 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
|||
step,
|
||||
}
|
||||
},
|
||||
Self::ModRem {
|
||||
ref dividend,
|
||||
ref divisor,
|
||||
op,
|
||||
} => {
|
||||
let dividend = Box::new(dividend.map_leaves_internal(map));
|
||||
let divisor = Box::new(divisor.map_leaves_internal(map));
|
||||
CalcNode::ModRem {
|
||||
dividend,
|
||||
divisor,
|
||||
op,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -469,6 +512,29 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
|||
},
|
||||
}
|
||||
},
|
||||
Self::ModRem {
|
||||
ref dividend,
|
||||
ref divisor,
|
||||
op,
|
||||
} => {
|
||||
let dividend = dividend.resolve_internal(leaf_to_output_fn)?;
|
||||
let divisor = divisor.resolve_internal(leaf_to_output_fn)?;
|
||||
|
||||
// In mod(A, B) only, if B is infinite and A has opposite sign to B
|
||||
// (including an oppositely-signed zero), the result is NaN.
|
||||
// https://drafts.csswg.org/css-values/#round-infinities
|
||||
if matches!(op, ModRemOp::Mod) &&
|
||||
divisor.is_infinite() &&
|
||||
dividend.is_sign_negative() != divisor.is_sign_negative()
|
||||
{
|
||||
return Ok(<O as Float>::nan());
|
||||
}
|
||||
|
||||
match op {
|
||||
ModRemOp::Mod => dividend - divisor * (dividend / divisor).floor(),
|
||||
ModRemOp::Rem => dividend - divisor * (dividend / divisor).trunc(),
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -539,6 +605,14 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
|||
value.mul_by(scalar);
|
||||
step.mul_by(scalar);
|
||||
},
|
||||
Self::ModRem {
|
||||
ref mut dividend,
|
||||
ref mut divisor,
|
||||
..
|
||||
} => {
|
||||
dividend.mul_by(scalar);
|
||||
divisor.mul_by(scalar);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -570,6 +644,14 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
|||
value.visit_depth_first_internal(f);
|
||||
step.visit_depth_first_internal(f);
|
||||
},
|
||||
Self::ModRem {
|
||||
ref mut dividend,
|
||||
ref mut divisor,
|
||||
..
|
||||
} => {
|
||||
dividend.visit_depth_first_internal(f);
|
||||
divisor.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);
|
||||
|
@ -772,6 +854,36 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
|||
},
|
||||
};
|
||||
},
|
||||
Self::ModRem {
|
||||
ref dividend,
|
||||
ref divisor,
|
||||
op,
|
||||
} => {
|
||||
let mut result = dividend.clone();
|
||||
|
||||
// In mod(A, B) only, if B is infinite and A has opposite sign to B
|
||||
// (including an oppositely-signed zero), the result is NaN.
|
||||
// https://drafts.csswg.org/css-values/#round-infinities
|
||||
if matches!(op, ModRemOp::Mod) &&
|
||||
divisor.is_infinite_leaf() &&
|
||||
dividend.is_negative_leaf() != divisor.is_negative_leaf()
|
||||
{
|
||||
result.mul_by(f32::NAN);
|
||||
return replace_self_with!(&mut *result);
|
||||
}
|
||||
|
||||
let result = match op {
|
||||
ModRemOp::Mod => dividend.try_op(divisor, |a, b| a - b * (a / b).floor()),
|
||||
ModRemOp::Rem => dividend.try_op(divisor, |a, b| a - b * (a / b).trunc()),
|
||||
};
|
||||
|
||||
let mut result = match result {
|
||||
Ok(res) => res,
|
||||
Err(..) => return,
|
||||
};
|
||||
|
||||
return replace_self_with!(&mut result);
|
||||
},
|
||||
Self::MinMax(ref mut children, op) => {
|
||||
let winning_order = match op {
|
||||
MinMaxOp::Min => cmp::Ordering::Less,
|
||||
|
@ -887,6 +999,14 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
|||
|
||||
true
|
||||
},
|
||||
Self::ModRem { op, .. } => {
|
||||
dest.write_str(match op {
|
||||
ModRemOp::Mod => "mod(",
|
||||
ModRemOp::Rem => "rem(",
|
||||
})?;
|
||||
|
||||
true
|
||||
},
|
||||
_ => {
|
||||
if is_outermost {
|
||||
dest.write_str("calc(")?;
|
||||
|
@ -945,6 +1065,15 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
|
|||
dest.write_str(", ")?;
|
||||
step.to_css_impl(dest, false)?;
|
||||
},
|
||||
Self::ModRem {
|
||||
ref dividend,
|
||||
ref divisor,
|
||||
..
|
||||
} => {
|
||||
dividend.to_css_impl(dest, false)?;
|
||||
dest.write_str(", ")?;
|
||||
divisor.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, RoundingStrategy, SortKey};
|
||||
use crate::values::generics::calc::{MinMaxOp, ModRemOp, RoundingStrategy, SortKey};
|
||||
use crate::values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength};
|
||||
use crate::values::specified::length::{ContainerRelativeLength, ViewportPercentageLength};
|
||||
use crate::values::specified::{self, Angle, Time};
|
||||
|
@ -54,6 +54,10 @@ pub enum MathFunction {
|
|||
Clamp,
|
||||
/// `round()`: https://drafts.csswg.org/css-values-4/#funcdef-round
|
||||
Round,
|
||||
/// `mod()`: https://drafts.csswg.org/css-values-4/#funcdef-mod
|
||||
Mod,
|
||||
/// `rem()`: https://drafts.csswg.org/css-values-4/#funcdef-rem
|
||||
Rem,
|
||||
/// `sin()`: https://drafts.csswg.org/css-values-4/#funcdef-sin
|
||||
Sin,
|
||||
/// `cos()`: https://drafts.csswg.org/css-values-4/#funcdef-cos
|
||||
|
@ -490,6 +494,22 @@ impl CalcNode {
|
|||
step: Box::new(step),
|
||||
})
|
||||
},
|
||||
MathFunction::Mod | MathFunction::Rem => {
|
||||
let dividend = Self::parse_argument(context, input, allowed_units)?;
|
||||
input.expect_comma()?;
|
||||
let divisor = Self::parse_argument(context, input, allowed_units)?;
|
||||
|
||||
let op = match function {
|
||||
MathFunction::Mod => ModRemOp::Mod,
|
||||
MathFunction::Rem => ModRemOp::Rem,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Ok(Self::ModRem {
|
||||
dividend: Box::new(dividend),
|
||||
divisor: Box::new(divisor),
|
||||
op,
|
||||
})
|
||||
},
|
||||
MathFunction::Min | MathFunction::Max => {
|
||||
// TODO(emilio): The common case for parse_comma_separated
|
||||
// is just one element, but for min / max is two, really...
|
||||
|
@ -780,6 +800,8 @@ impl CalcNode {
|
|||
trig_enabled()
|
||||
} else if matches!(function, Round) {
|
||||
round_enabled()
|
||||
} else if matches!(function, Mod | Rem) {
|
||||
static_prefs::pref!("layout.css.mod-rem.enabled")
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue