Bug 1942715: Part 3 - Remove old & now-unused anchor resolution infrastructure in CalcNode. r=firefox-style-system-reviewers,emilio

Differential Revision: https://phabricator.services.mozilla.com/D237096
This commit is contained in:
David Shin
2025-02-12 01:31:30 +00:00
parent 72e65118b1
commit 276e5aef75
6 changed files with 93 additions and 531 deletions

View File

@@ -91,9 +91,7 @@ impl<ValueType: ColorComponentType> ColorComponent<ValueType> {
} else { } else {
ValueType::units() ValueType::units()
}); });
let node = GenericCalcNode::parse(context, input, function, allow)?; let mut node = GenericCalcNode::parse(context, input, function, allow)?;
debug_assert!(!node.has_anchor_function, "Anchor function used for color?");
let mut node = node.node;
// TODO(tlouw): We only have to simplify the node when we have to store it, but we // TODO(tlouw): We only have to simplify the node when we have to store it, but we
// only know if we have to store it much later when the whole color // only know if we have to store it much later when the whole color

View File

@@ -30,10 +30,7 @@ use crate::gecko_bindings::structs::GeckoFontMetrics;
use crate::logical_geometry::PhysicalSide; use crate::logical_geometry::PhysicalSide;
use crate::values::animated::{Animate, Context as AnimatedContext, Procedure, ToAnimatedValue, ToAnimatedZero}; use crate::values::animated::{Animate, Context as AnimatedContext, Procedure, ToAnimatedValue, ToAnimatedZero};
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance}; use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::generics::calc::{ use crate::values::generics::calc::{CalcUnits, PositivePercentageBasis};
AnchorPositioningResolver, CalcUnits, GenericCalcAnchorFunction, GenericCalcAnchorSizeFunction,
PositivePercentageBasis,
};
use crate::values::generics::length::AnchorResolutionResult; use crate::values::generics::length::AnchorResolutionResult;
use crate::values::generics::{calc, NonNegative}; use crate::values::generics::{calc, NonNegative};
use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue}; use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};
@@ -231,12 +228,6 @@ enum Serializable {
Percentage(Percentage), Percentage(Percentage),
} }
impl From<CalcLengthPercentage> for LengthPercentage {
fn from(value: CalcLengthPercentage) -> Self {
Self::new_calc(value.node, value.clamping_mode, value.has_anchor_function)
}
}
impl LengthPercentage { impl LengthPercentage {
/// 1px length value for SVG defaults /// 1px length value for SVG defaults
#[inline] #[inline]
@@ -250,14 +241,11 @@ impl LengthPercentage {
Self::new_percent(Percentage::zero()) Self::new_percent(Percentage::zero())
} }
fn to_calc_node(&self) -> (CalcNode, bool) { fn to_calc_node(&self) -> CalcNode {
match self.unpack() { match self.unpack() {
Unpacked::Length(l) => (CalcNode::Leaf(CalcLengthPercentageLeaf::Length(l)), false), Unpacked::Length(l) => CalcNode::Leaf(CalcLengthPercentageLeaf::Length(l)),
Unpacked::Percentage(p) => ( Unpacked::Percentage(p) => CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(p)),
CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(p)), Unpacked::Calc(p) => p.node.clone(),
false,
),
Unpacked::Calc(p) => (p.node.clone(), p.has_anchor_function),
} }
} }
@@ -267,9 +255,10 @@ impl LengthPercentage {
Unpacked::Percentage(p) => Self::new_percent(p), Unpacked::Percentage(p) => Self::new_percent(p),
Unpacked::Calc(lp) => Self::new_calc_unchecked(Box::new(CalcLengthPercentage { Unpacked::Calc(lp) => Self::new_calc_unchecked(Box::new(CalcLengthPercentage {
clamping_mode: lp.clamping_mode, clamping_mode: lp.clamping_mode,
has_anchor_function: lp.has_anchor_function,
node: lp.node.map_leaves(|leaf| match *leaf { node: lp.node.map_leaves(|leaf| match *leaf {
CalcLengthPercentageLeaf::Length(ref l) => CalcLengthPercentageLeaf::Length(map_fn(*l)), CalcLengthPercentageLeaf::Length(ref l) => {
CalcLengthPercentageLeaf::Length(map_fn(*l))
},
ref l => l.clone(), ref l => l.clone(),
}), }),
})), })),
@@ -307,7 +296,7 @@ impl LengthPercentage {
pub fn hundred_percent_minus(v: Self, clamping_mode: AllowedNumericType) -> Self { pub fn hundred_percent_minus(v: Self, clamping_mode: AllowedNumericType) -> Self {
// TODO: This could in theory take ownership of the calc node in `v` if // TODO: This could in theory take ownership of the calc node in `v` if
// possible instead of cloning. // possible instead of cloning.
let (mut node, has_anchor_function) = v.to_calc_node(); let mut node = v.to_calc_node();
node.negate(); node.negate();
let new_node = CalcNode::Sum( let new_node = CalcNode::Sum(
@@ -318,7 +307,7 @@ impl LengthPercentage {
.into(), .into(),
); );
Self::new_calc(new_node, clamping_mode, has_anchor_function) Self::new_calc(new_node, clamping_mode)
} }
/// Given a list of `LengthPercentage` values, construct the value representing /// Given a list of `LengthPercentage` values, construct the value representing
@@ -328,38 +317,18 @@ impl LengthPercentage {
Percentage::hundred(), Percentage::hundred(),
))]; ))];
let mut has_anchor_function = false;
for lp in list.iter() { for lp in list.iter() {
let (mut node, node_has_anchor_function) = lp.to_calc_node(); let mut node = lp.to_calc_node();
has_anchor_function |= node_has_anchor_function;
node.negate(); node.negate();
new_list.push(node) new_list.push(node)
} }
Self::new_calc( Self::new_calc(CalcNode::Sum(new_list.into()), clamping_mode)
CalcNode::Sum(new_list.into()),
clamping_mode,
has_anchor_function,
)
}
/// Construct a new `calc()` value from `CalcLengthPercentage`.
#[inline]
pub fn new_calc_from(calc_length_percentage: CalcLengthPercentage) -> Self {
Self::new_calc(
calc_length_percentage.node,
calc_length_percentage.clamping_mode,
calc_length_percentage.has_anchor_function,
)
} }
/// Constructs a `calc()` value. /// Constructs a `calc()` value.
#[inline] #[inline]
pub fn new_calc( pub fn new_calc(mut node: CalcNode, clamping_mode: AllowedNumericType) -> Self {
mut node: CalcNode,
clamping_mode: AllowedNumericType,
has_anchor_function: bool,
) -> Self {
node.simplify_and_sort(); node.simplify_and_sort();
match node { match node {
@@ -383,7 +352,6 @@ impl LengthPercentage {
_ => Self::new_calc_unchecked(Box::new(CalcLengthPercentage { _ => Self::new_calc_unchecked(Box::new(CalcLengthPercentage {
clamping_mode, clamping_mode,
node, node,
has_anchor_function,
})), })),
} }
} }
@@ -918,11 +886,6 @@ impl calc::CalcNodeLeaf for CalcLengthPercentageLeaf {
} }
} }
/// Computed `anchor()` function in math functions.
pub type CalcAnchorFunction = GenericCalcAnchorFunction<CalcLengthPercentageLeaf>;
/// Computed `anchor-size()` function in math functions.
pub type CalcAnchorSizeFunction = GenericCalcAnchorSizeFunction<CalcLengthPercentageLeaf>;
/// The computed version of a calc() node for `<length-percentage>` values. /// The computed version of a calc() node for `<length-percentage>` values.
pub type CalcNode = calc::GenericCalcNode<CalcLengthPercentageLeaf>; pub type CalcNode = calc::GenericCalcNode<CalcLengthPercentageLeaf>;
@@ -935,10 +898,6 @@ pub struct CalcLengthPercentage {
#[animation(constant)] #[animation(constant)]
#[css(skip)] #[css(skip)]
clamping_mode: AllowedNumericType, clamping_mode: AllowedNumericType,
/// See documentation for field of the same name in `specified::CalcLengthPercentage`.
#[animation(constant)]
#[css(skip)]
has_anchor_function: bool,
node: CalcNode, node: CalcNode,
} }
@@ -1109,56 +1068,6 @@ impl CalcLengthPercentage {
}, },
} }
} }
/// Resolves anchor positioning functions. This is separate from percentage length resolution, as
/// it's valid to ask "Is a given inset auto?" without having to resolve the full length value.
/// Note(dshin): When interleaving is implemented, and if the anchor function resolves to a percentage
/// fallback value, percentage values probably be left as-is, for animation.
#[inline]
pub fn resolve_anchor_functions(
&self,
side: PhysicalSide,
prop: PositionProperty,
) -> Result<Self, ()> {
let result = self.node.resolve_anchor(side, prop, &Resolver)?;
Ok(Self {
clamping_mode: self.clamping_mode,
// TODO(dshin): When the interleaving is implemented, we need to mark anchor-resolved
// values somehow so that we know this value need to be animated when the anchor element or
// the absolute containing block is changed.
has_anchor_function: false,
node: result,
})
}
}
struct Resolver;
impl AnchorPositioningResolver<CalcLengthPercentageLeaf> for Resolver {
fn resolve_anchor(
&self,
f: &CalcAnchorFunction,
side: PhysicalSide,
position: PositionProperty,
) -> Result<CalcNode, ()> {
match f.resolve(side, position) {
AnchorResolutionResult::Resolved(v) => Ok(*v),
AnchorResolutionResult::Fallback(v) => Ok(*v.clone()),
AnchorResolutionResult::Invalid => Err(()),
}
}
fn resolve_anchor_size(
&self,
f: &CalcAnchorSizeFunction,
position: PositionProperty,
) -> Result<CalcNode, ()> {
match f.resolve(position) {
AnchorResolutionResult::Resolved(v) => Ok(*v),
AnchorResolutionResult::Fallback(v) => Ok(*v.clone()),
AnchorResolutionResult::Invalid => Err(()),
}
}
} }
// NOTE(emilio): We don't compare `clamping_mode` since we want to preserve the // NOTE(emilio): We don't compare `clamping_mode` since we want to preserve the
@@ -1210,7 +1119,7 @@ impl specified::CalcLengthPercentage {
}, },
}); });
LengthPercentage::new_calc(node, self.clamping_mode, self.has_anchor_function) LengthPercentage::new_calc(node, self.clamping_mode)
} }
/// Compute font-size or line-height taking into account text-zoom if necessary. /// Compute font-size or line-height taking into account text-zoom if necessary.
@@ -1289,7 +1198,6 @@ impl specified::CalcLengthPercentage {
CalcLengthPercentageLeaf::Percentage(ref p) => Leaf::Percentage(p.0), CalcLengthPercentageLeaf::Percentage(ref p) => Leaf::Percentage(p.0),
CalcLengthPercentageLeaf::Number(n) => Leaf::Number(*n), CalcLengthPercentageLeaf::Number(n) => Leaf::Number(*n),
}), }),
has_anchor_function: computed.has_anchor_function,
} }
} }
} }
@@ -1320,15 +1228,12 @@ impl Animate for LengthPercentage {
} }
let (l, r) = procedure.weights(); let (l, r) = procedure.weights();
let (one, one_has_anchor_function) = self.to_calc_node(); let one = product_with(self.to_calc_node(), l as f32);
let (other, other_has_anchor_function) = other.to_calc_node(); let other = product_with(other.to_calc_node(), r as f32);
let one = product_with(one, l as f32);
let other = product_with(other, r as f32);
Self::new_calc( Self::new_calc(
CalcNode::Sum(vec![one, other].into()), CalcNode::Sum(vec![one, other].into()),
AllowedNumericType::All, AllowedNumericType::All,
one_has_anchor_function || other_has_anchor_function,
) )
}, },
}) })

View File

@@ -6,8 +6,6 @@
//! //!
//! [calc]: https://drafts.csswg.org/css-values/#calc-notation //! [calc]: https://drafts.csswg.org/css-values/#calc-notation
use crate::logical_geometry::PhysicalSide;
use crate::values::generics::box_::PositionProperty;
use crate::values::generics::length::GenericAnchorSizeFunction; use crate::values::generics::length::GenericAnchorSizeFunction;
use crate::values::generics::position::{AnchorSide, GenericAnchorFunction}; use crate::values::generics::position::{AnchorSide, GenericAnchorFunction};
use num_traits::Zero; use num_traits::Zero;
@@ -442,24 +440,6 @@ enum ArgumentLevel {
Nested, Nested,
} }
/// Trait for resolving anchor positioning functions in math functions.
pub trait AnchorPositioningResolver<L: CalcNodeLeaf> {
/// Resolve `anchor()` function to a value.
fn resolve_anchor(
&self,
f: &GenericCalcAnchorFunction<L>,
side: PhysicalSide,
position: PositionProperty,
) -> Result<GenericCalcNode<L>, ()>;
/// Resolve `anchor-size()` function to a value.
fn resolve_anchor_size(
&self,
f: &GenericCalcAnchorSizeFunction<L>,
position: PositionProperty,
) -> Result<GenericCalcNode<L>, ()>;
}
impl<L: CalcNodeLeaf> CalcNode<L> { impl<L: CalcNodeLeaf> CalcNode<L> {
/// Create a dummy CalcNode that can be used to do replacements of other nodes. /// Create a dummy CalcNode that can be used to do replacements of other nodes.
fn dummy() -> Self { fn dummy() -> Self {
@@ -1214,135 +1194,6 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
} }
} }
/// Resolve anchor functions, returning the reduced node as a result of it.
/// This returns a cloned node, so callers should ideally keep track of
/// the node's usage of anchor functions and skip calling this.
/// Returns `Err(())` if the anchor function used resolves to an invalid
/// anchor and there is no fallback available.
pub fn resolve_anchor<R>(
&self,
side: PhysicalSide,
position_property: PositionProperty,
anchor_function_resolver: &R,
) -> Result<Self, ()>
where
R: AnchorPositioningResolver<L>,
{
fn resolve_anchor_internal<L: CalcNodeLeaf, R>(
node: &mut CalcNode<L>,
side: PhysicalSide,
position_property: PositionProperty,
anchor_positioning_resolver: &R,
) -> Result<(), ()>
where
R: AnchorPositioningResolver<L>,
{
match node {
CalcNode::Leaf(_) => Ok(()),
CalcNode::Negate(child) |
CalcNode::Invert(child) |
CalcNode::Abs(child) |
CalcNode::Sign(child) => resolve_anchor_internal(
child,
side,
position_property,
anchor_positioning_resolver,
),
CalcNode::Sum(children) |
CalcNode::Product(children) |
CalcNode::MinMax(children, _) |
CalcNode::Hypot(children) => {
for child in children.iter_mut() {
resolve_anchor_internal(
child,
side,
position_property,
anchor_positioning_resolver,
)?;
}
Ok(())
},
CalcNode::Clamp { min, center, max } => {
resolve_anchor_internal(
min,
side,
position_property,
anchor_positioning_resolver,
)?;
resolve_anchor_internal(
center,
side,
position_property,
anchor_positioning_resolver,
)?;
resolve_anchor_internal(
max,
side,
position_property,
anchor_positioning_resolver,
)
},
CalcNode::Round {
value,
step,
..
} => {
resolve_anchor_internal(
value,
side,
position_property,
anchor_positioning_resolver,
)?;
resolve_anchor_internal(
step,
side,
position_property,
anchor_positioning_resolver,
)
},
CalcNode::ModRem {
dividend,
divisor,
op: _,
} => {
resolve_anchor_internal(
dividend,
side,
position_property,
anchor_positioning_resolver,
)?;
resolve_anchor_internal(
divisor,
side,
position_property,
anchor_positioning_resolver,
)
},
CalcNode::Anchor(f) => {
*node =
anchor_positioning_resolver.resolve_anchor(f, side, position_property)?;
Ok(())
},
CalcNode::AnchorSize(f) => {
*node =
anchor_positioning_resolver.resolve_anchor_size(f, position_property)?;
Ok(())
},
}
}
// TODO(dshin, Bug 1924225): When interleaving for anchor positioning happens, we can
// mutate the node in place.
let mut cloned = self.clone();
resolve_anchor_internal(
&mut cloned,
side,
position_property,
anchor_function_resolver,
)?;
Ok(cloned)
}
fn is_negative_leaf(&self) -> Result<bool, ()> { fn is_negative_leaf(&self) -> Result<bool, ()> {
Ok(match *self { Ok(match *self {
Self::Leaf(ref l) => l.is_negative()?, Self::Leaf(ref l) => l.is_negative()?,

View File

@@ -129,11 +129,6 @@ impl ToCss for Leaf {
pub struct CalcLengthPercentage { pub struct CalcLengthPercentage {
#[css(skip)] #[css(skip)]
pub clamping_mode: AllowedNumericType, pub clamping_mode: AllowedNumericType,
/// Flag indicating if any anchor function is part of this node.
/// This can be used to skip the traversal of calc node tree for
/// math functions not using any anchor function.
#[css(skip)]
pub has_anchor_function: bool,
pub node: CalcNode, pub node: CalcNode,
} }
@@ -494,9 +489,11 @@ impl AnchorSide<Box<CalcNode>> {
if let Ok(k) = input.try_parse(|i| AnchorSideKeyword::parse(i)) { if let Ok(k) = input.try_parse(|i| AnchorSideKeyword::parse(i)) {
return Ok(Self::Keyword(k)); return Ok(Self::Keyword(k));
} }
Ok(Self::Percentage(Box::new( Ok(Self::Percentage(Box::new(CalcNode::parse_argument(
CalcNode::parse_argument(context, input, AllowParse::new(CalcUnits::PERCENTAGE))?.node, context,
))) input,
AllowParse::new(CalcUnits::PERCENTAGE),
)?)))
} }
} }
@@ -523,8 +520,7 @@ impl GenericAnchorFunction<Box<CalcNode>, Box<CalcNode>> {
context, context,
i, i,
AllowParse::new(CalcUnits::LENGTH_PERCENTAGE), AllowParse::new(CalcUnits::LENGTH_PERCENTAGE),
)? )?;
.node;
Ok::<Box<CalcNode>, ParseError<'i>>(Box::new(node)) Ok::<Box<CalcNode>, ParseError<'i>>(Box::new(node))
}) })
.ok(); .ok();
@@ -547,7 +543,7 @@ impl GenericAnchorSizeFunction<Box<CalcNode>> {
} }
GenericAnchorSizeFunction::parse_inner(context, input, |i| { GenericAnchorSizeFunction::parse_inner(context, input, |i| {
CalcNode::parse_argument(context, i, AllowParse::new(CalcUnits::LENGTH_PERCENTAGE)) CalcNode::parse_argument(context, i, AllowParse::new(CalcUnits::LENGTH_PERCENTAGE))
.map(|r| Box::new(r.node)) .map(|r| Box::new(r))
}) })
} }
} }
@@ -559,73 +555,6 @@ pub type CalcAnchorSizeFunction = generic::GenericCalcAnchorSizeFunction<Leaf>;
/// A calc node representation for specified values. /// A calc node representation for specified values.
pub type CalcNode = generic::GenericCalcNode<Leaf>; pub type CalcNode = generic::GenericCalcNode<Leaf>;
/// Result of parsing the calc node.
pub struct ParsedCalcNode {
/// The parsed calc node.
pub node: CalcNode,
/// See documentation for the field of the same name in `CalcLengthPercentage`.
pub has_anchor_function: bool,
}
impl ParsedCalcNode {
#[inline]
fn new(node: CalcNode, has_anchor_function: bool) -> Self {
Self {
node,
has_anchor_function,
}
}
#[inline]
fn into_length_or_percentage(
self,
clamping_mode: AllowedNumericType,
) -> Result<CalcLengthPercentage, ()> {
self.node
.into_length_or_percentage(clamping_mode, self.has_anchor_function)
}
#[inline]
fn to_time(&self, clamping_mode: Option<AllowedNumericType>) -> Result<Time, ()> {
debug_assert!(!self.has_anchor_function, "Anchor function used for time?");
self.node.to_time(clamping_mode)
}
#[inline]
fn to_resolution(&self) -> Result<Resolution, ()> {
debug_assert!(
!self.has_anchor_function,
"Anchor function used for resolution?"
);
self.node.to_resolution()
}
#[inline]
fn to_angle(&self) -> Result<Angle, ()> {
debug_assert!(!self.has_anchor_function, "Anchor function used for angle?");
self.node.to_angle()
}
#[inline]
fn to_number(&self) -> Result<CSSFloat, ()> {
debug_assert!(
!self.has_anchor_function,
"Anchor function used for number?"
);
self.node.to_number()
}
#[inline]
fn to_percentage(&self) -> Result<CSSFloat, ()> {
debug_assert!(
!self.has_anchor_function,
"Anchor function used for percentage?"
);
self.node.to_percentage()
}
}
impl CalcNode { impl CalcNode {
/// Tries to parse a single element in the expression, that is, a /// Tries to parse a single element in the expression, that is, a
/// `<length>`, `<angle>`, `<time>`, `<percentage>`, `<resolution>`, etc. /// `<length>`, `<angle>`, `<time>`, `<percentage>`, `<resolution>`, etc.
@@ -636,44 +565,38 @@ impl CalcNode {
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
allowed: AllowParse, allowed: AllowParse,
) -> Result<ParsedCalcNode, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
let location = input.current_source_location(); let location = input.current_source_location();
match input.next()? { match input.next()? {
&Token::Number { value, .. } => Ok(ParsedCalcNode::new( &Token::Number { value, .. } => Ok(CalcNode::Leaf(Leaf::Number(value))),
CalcNode::Leaf(Leaf::Number(value)),
false,
)),
&Token::Dimension { &Token::Dimension {
value, ref unit, .. value, ref unit, ..
} => { } => {
if allowed.includes(CalcUnits::LENGTH) { if allowed.includes(CalcUnits::LENGTH) {
if let Ok(l) = NoCalcLength::parse_dimension(context, value, unit) { if let Ok(l) = NoCalcLength::parse_dimension(context, value, unit) {
return Ok(ParsedCalcNode::new(CalcNode::Leaf(Leaf::Length(l)), false)); return Ok(CalcNode::Leaf(Leaf::Length(l)));
} }
} }
if allowed.includes(CalcUnits::ANGLE) { if allowed.includes(CalcUnits::ANGLE) {
if let Ok(a) = Angle::parse_dimension(value, unit, /* from_calc = */ true) { if let Ok(a) = Angle::parse_dimension(value, unit, /* from_calc = */ true) {
return Ok(ParsedCalcNode::new(CalcNode::Leaf(Leaf::Angle(a)), false)); return Ok(CalcNode::Leaf(Leaf::Angle(a)));
} }
} }
if allowed.includes(CalcUnits::TIME) { if allowed.includes(CalcUnits::TIME) {
if let Ok(t) = Time::parse_dimension(value, unit) { if let Ok(t) = Time::parse_dimension(value, unit) {
return Ok(ParsedCalcNode::new(CalcNode::Leaf(Leaf::Time(t)), false)); return Ok(CalcNode::Leaf(Leaf::Time(t)));
} }
} }
if allowed.includes(CalcUnits::RESOLUTION) { if allowed.includes(CalcUnits::RESOLUTION) {
if let Ok(t) = Resolution::parse_dimension(value, unit) { if let Ok(t) = Resolution::parse_dimension(value, unit) {
return Ok(ParsedCalcNode::new( return Ok(CalcNode::Leaf(Leaf::Resolution(t)));
CalcNode::Leaf(Leaf::Resolution(t)),
false,
));
} }
} }
return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}, },
&Token::Percentage { unit_value, .. } if allowed.includes(CalcUnits::PERCENTAGE) => Ok( &Token::Percentage { unit_value, .. } if allowed.includes(CalcUnits::PERCENTAGE) => {
ParsedCalcNode::new(CalcNode::Leaf(Leaf::Percentage(unit_value)), false), Ok(CalcNode::Leaf(Leaf::Percentage(unit_value)))
), },
&Token::ParenthesisBlock => { &Token::ParenthesisBlock => {
input.parse_nested_block(|input| CalcNode::parse_argument(context, input, allowed)) input.parse_nested_block(|input| CalcNode::parse_argument(context, input, allowed))
}, },
@@ -684,10 +607,7 @@ impl CalcNode {
name.eq_ignore_ascii_case("anchor") => name.eq_ignore_ascii_case("anchor") =>
{ {
let anchor_function = GenericAnchorFunction::parse_in_calc(context, input)?; let anchor_function = GenericAnchorFunction::parse_in_calc(context, input)?;
Ok(ParsedCalcNode::new( Ok(CalcNode::Anchor(Box::new(anchor_function)))
CalcNode::Anchor(Box::new(anchor_function)),
true,
))
}, },
&Token::Function(ref name) &Token::Function(ref name)
if allowed if allowed
@@ -697,10 +617,7 @@ impl CalcNode {
{ {
let anchor_size_function = let anchor_size_function =
GenericAnchorSizeFunction::parse_in_calc(context, input)?; GenericAnchorSizeFunction::parse_in_calc(context, input)?;
Ok(ParsedCalcNode::new( Ok(CalcNode::AnchorSize(Box::new(anchor_size_function)))
CalcNode::AnchorSize(Box::new(anchor_size_function)),
true,
))
}, },
&Token::Function(ref name) => { &Token::Function(ref name) => {
let function = CalcNode::math_function(context, name, location)?; let function = CalcNode::math_function(context, name, location)?;
@@ -730,7 +647,7 @@ impl CalcNode {
} }
}, },
}; };
Ok(ParsedCalcNode::new(CalcNode::Leaf(leaf), false)) Ok(CalcNode::Leaf(leaf))
}, },
t => Err(location.new_unexpected_token_error(t.clone())), t => Err(location.new_unexpected_token_error(t.clone())),
} }
@@ -744,7 +661,7 @@ impl CalcNode {
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
function: MathFunction, function: MathFunction,
allowed: AllowParse, allowed: AllowParse,
) -> Result<ParsedCalcNode, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
input.parse_nested_block(|input| { input.parse_nested_block(|input| {
match function { match function {
MathFunction::Calc => Self::parse_argument(context, input, allowed), MathFunction::Calc => Self::parse_argument(context, input, allowed),
@@ -754,15 +671,10 @@ impl CalcNode {
let center = Self::parse_argument(context, input, allowed)?; let center = Self::parse_argument(context, input, allowed)?;
input.expect_comma()?; input.expect_comma()?;
let max = Self::parse_argument(context, input, allowed)?; let max = Self::parse_argument(context, input, allowed)?;
Ok(ParsedCalcNode { Ok(Self::Clamp {
node: Self::Clamp { min: Box::new(min),
min: Box::new(min.node), center: Box::new(center),
center: Box::new(center.node), max: Box::new(max),
max: Box::new(max.node),
},
has_anchor_function: min.has_anchor_function ||
center.has_anchor_function ||
max.has_anchor_function,
}) })
}, },
MathFunction::Round => { MathFunction::Round => {
@@ -794,17 +706,12 @@ impl CalcNode {
Self::parse_argument(context, input, allowed) Self::parse_argument(context, input, allowed)
}); });
let (step, step_has_anchor_function) = step let step = step.unwrap_or(Self::Leaf(Leaf::Number(1.0)));
.map(|s| (s.node, s.has_anchor_function))
.unwrap_or((Self::Leaf(Leaf::Number(1.0)), false));
Ok(ParsedCalcNode { Ok(Self::Round {
node: Self::Round { strategy: strategy.unwrap_or(RoundingStrategy::Nearest),
strategy: strategy.unwrap_or(RoundingStrategy::Nearest), value: Box::new(value),
value: Box::new(value.node), step: Box::new(step),
step: Box::new(step),
},
has_anchor_function: value.has_anchor_function || step_has_anchor_function,
}) })
}, },
MathFunction::Mod | MathFunction::Rem => { MathFunction::Mod | MathFunction::Rem => {
@@ -817,14 +724,10 @@ impl CalcNode {
MathFunction::Rem => ModRemOp::Rem, MathFunction::Rem => ModRemOp::Rem,
_ => unreachable!(), _ => unreachable!(),
}; };
Ok(ParsedCalcNode { Ok(Self::ModRem {
node: Self::ModRem { dividend: Box::new(dividend),
dividend: Box::new(dividend.node), divisor: Box::new(divisor),
divisor: Box::new(divisor.node), op,
op,
},
has_anchor_function: dividend.has_anchor_function ||
divisor.has_anchor_function,
}) })
}, },
MathFunction::Min | MathFunction::Max => { MathFunction::Min | MathFunction::Max => {
@@ -833,11 +736,9 @@ impl CalcNode {
// //
// Consider adding an API to cssparser to specify the // Consider adding an API to cssparser to specify the
// initial vector capacity? // initial vector capacity?
let mut has_anchor_function = false;
let arguments = input.parse_comma_separated(|input| { let arguments = input.parse_comma_separated(|input| {
let result = Self::parse_argument(context, input, allowed)?; let result = Self::parse_argument(context, input, allowed)?;
has_anchor_function |= result.has_anchor_function; Ok(result)
Ok(result.node)
})?; })?;
let op = match function { let op = match function {
@@ -846,10 +747,7 @@ impl CalcNode {
_ => unreachable!(), _ => unreachable!(),
}; };
Ok(ParsedCalcNode { Ok(Self::MinMax(arguments.into(), op))
node: Self::MinMax(arguments.into(), op),
has_anchor_function,
})
}, },
MathFunction::Sin | MathFunction::Cos | MathFunction::Tan => { MathFunction::Sin | MathFunction::Cos | MathFunction::Tan => {
let a = Self::parse_angle_argument(context, input)?; let a = Self::parse_angle_argument(context, input)?;
@@ -863,10 +761,7 @@ impl CalcNode {
}, },
}; };
Ok(ParsedCalcNode { Ok(Self::Leaf(Leaf::Number(number)))
node: Self::Leaf(Leaf::Number(number)),
has_anchor_function: false,
})
}, },
MathFunction::Asin | MathFunction::Acos | MathFunction::Atan => { MathFunction::Asin | MathFunction::Acos | MathFunction::Atan => {
let a = Self::parse_number_argument(context, input)?; let a = Self::parse_number_argument(context, input)?;
@@ -880,18 +775,13 @@ impl CalcNode {
}, },
}; };
Ok(ParsedCalcNode { Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians))))
node: Self::Leaf(Leaf::Angle(Angle::from_radians(radians))),
has_anchor_function: false,
})
}, },
MathFunction::Atan2 => { MathFunction::Atan2 => {
let allow_all = allowed.new_including(CalcUnits::ALL); let allow_all = allowed.new_including(CalcUnits::ALL);
let a = Self::parse_argument(context, input, allow_all)?; let a = Self::parse_argument(context, input, allow_all)?;
let (a, a_has_anchor_function) = (a.node, a.has_anchor_function);
input.expect_comma()?; input.expect_comma()?;
let b = Self::parse_argument(context, input, allow_all)?; let b = Self::parse_argument(context, input, allow_all)?;
let (b, b_has_anchor_function) = (b.node, b.has_anchor_function);
let radians = Self::try_resolve(input, || { let radians = Self::try_resolve(input, || {
if let Ok(a) = a.to_number() { if let Ok(a) = a.to_number() {
@@ -919,23 +809,14 @@ impl CalcNode {
return Ok(a.dppx().atan2(b.dppx())); return Ok(a.dppx().atan2(b.dppx()));
} }
let a = a.into_length_or_percentage( let a = a.into_length_or_percentage(AllowedNumericType::All)?;
AllowedNumericType::All, let b = b.into_length_or_percentage(AllowedNumericType::All)?;
a_has_anchor_function,
)?;
let b = b.into_length_or_percentage(
AllowedNumericType::All,
b_has_anchor_function,
)?;
let (a, b) = CalcLengthPercentage::same_unit_length_as(&a, &b).ok_or(())?; let (a, b) = CalcLengthPercentage::same_unit_length_as(&a, &b).ok_or(())?;
Ok(a.atan2(b)) Ok(a.atan2(b))
})?; })?;
Ok(ParsedCalcNode { Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians))))
node: Self::Leaf(Leaf::Angle(Angle::from_radians(radians))),
has_anchor_function: a_has_anchor_function || b_has_anchor_function,
})
}, },
MathFunction::Pow => { MathFunction::Pow => {
let a = Self::parse_number_argument(context, input)?; let a = Self::parse_number_argument(context, input)?;
@@ -944,33 +825,22 @@ impl CalcNode {
let number = a.powf(b); let number = a.powf(b);
Ok(ParsedCalcNode { Ok(Self::Leaf(Leaf::Number(number)))
node: Self::Leaf(Leaf::Number(number)),
has_anchor_function: false,
})
}, },
MathFunction::Sqrt => { MathFunction::Sqrt => {
let a = Self::parse_number_argument(context, input)?; let a = Self::parse_number_argument(context, input)?;
let number = a.sqrt(); let number = a.sqrt();
Ok(ParsedCalcNode { Ok(Self::Leaf(Leaf::Number(number)))
node: Self::Leaf(Leaf::Number(number)),
has_anchor_function: false,
})
}, },
MathFunction::Hypot => { MathFunction::Hypot => {
let mut has_anchor_function = false;
let arguments = input.parse_comma_separated(|input| { let arguments = input.parse_comma_separated(|input| {
let result = Self::parse_argument(context, input, allowed)?; let result = Self::parse_argument(context, input, allowed)?;
has_anchor_function |= result.has_anchor_function; Ok(result)
Ok(result.node)
})?; })?;
Ok(ParsedCalcNode { Ok(Self::Hypot(arguments.into()))
node: Self::Hypot(arguments.into()),
has_anchor_function,
})
}, },
MathFunction::Log => { MathFunction::Log => {
let a = Self::parse_number_argument(context, input)?; let a = Self::parse_number_argument(context, input)?;
@@ -986,25 +856,16 @@ impl CalcNode {
None => a.ln(), None => a.ln(),
}; };
Ok(ParsedCalcNode { Ok(Self::Leaf(Leaf::Number(number)))
node: Self::Leaf(Leaf::Number(number)),
has_anchor_function: false,
})
}, },
MathFunction::Exp => { MathFunction::Exp => {
let a = Self::parse_number_argument(context, input)?; let a = Self::parse_number_argument(context, input)?;
let number = a.exp(); let number = a.exp();
Ok(ParsedCalcNode { Ok(Self::Leaf(Leaf::Number(number)))
node: Self::Leaf(Leaf::Number(number)),
has_anchor_function: false,
})
}, },
MathFunction::Abs => { MathFunction::Abs => {
let node = Self::parse_argument(context, input, allowed)?; let node = Self::parse_argument(context, input, allowed)?;
Ok(ParsedCalcNode { Ok(Self::Abs(Box::new(node)))
node: Self::Abs(Box::new(node.node)),
has_anchor_function: node.has_anchor_function,
})
}, },
MathFunction::Sign => { MathFunction::Sign => {
// The sign of a percentage is dependent on the percentage basis, so if // The sign of a percentage is dependent on the percentage basis, so if
@@ -1015,10 +876,7 @@ impl CalcNode {
input, input,
allowed.new_including(CalcUnits::ALL - CalcUnits::PERCENTAGE), allowed.new_including(CalcUnits::ALL - CalcUnits::PERCENTAGE),
)?; )?;
Ok(ParsedCalcNode { Ok(Self::Sign(Box::new(node)))
node: Self::Sign(Box::new(node.node)),
has_anchor_function: node.has_anchor_function,
})
}, },
} }
}) })
@@ -1028,8 +886,7 @@ impl CalcNode {
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<CSSFloat, ParseError<'i>> { ) -> Result<CSSFloat, ParseError<'i>> {
let argument = let argument = Self::parse_argument(context, input, AllowParse::new(CalcUnits::ANGLE))?;
Self::parse_argument(context, input, AllowParse::new(CalcUnits::ANGLE))?.node;
argument argument
.to_number() .to_number()
.or_else(|()| Ok(argument.to_angle()?.radians())) .or_else(|()| Ok(argument.to_angle()?.radians()))
@@ -1041,7 +898,6 @@ impl CalcNode {
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<CSSFloat, ParseError<'i>> { ) -> Result<CSSFloat, ParseError<'i>> {
Self::parse_argument(context, input, AllowParse::new(CalcUnits::empty()))? Self::parse_argument(context, input, AllowParse::new(CalcUnits::empty()))?
.node
.to_number() .to_number()
.map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
} }
@@ -1050,11 +906,10 @@ impl CalcNode {
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
allowed: AllowParse, allowed: AllowParse,
) -> Result<ParsedCalcNode, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
let mut sum = SmallVec::<[CalcNode; 1]>::new(); let mut sum = SmallVec::<[CalcNode; 1]>::new();
let first = Self::parse_product(context, input, allowed)?; let first = Self::parse_product(context, input, allowed)?;
sum.push(first.node); sum.push(first);
let mut has_anchor_function = first.has_anchor_function;
loop { loop {
let start = input.state(); let start = input.state();
match input.next_including_whitespace() { match input.next_including_whitespace() {
@@ -1065,18 +920,15 @@ impl CalcNode {
match *input.next()? { match *input.next()? {
Token::Delim('+') => { Token::Delim('+') => {
let rhs = Self::parse_product(context, input, allowed)?; let rhs = Self::parse_product(context, input, allowed)?;
has_anchor_function |= rhs.has_anchor_function; if sum.last_mut().unwrap().try_sum_in_place(&rhs).is_err() {
if sum.last_mut().unwrap().try_sum_in_place(&rhs.node).is_err() { sum.push(rhs);
// TODO(dshin): need to combine
sum.push(rhs.node);
} }
}, },
Token::Delim('-') => { Token::Delim('-') => {
let mut rhs = Self::parse_product(context, input, allowed)?; let mut rhs = Self::parse_product(context, input, allowed)?;
has_anchor_function |= rhs.has_anchor_function; rhs.negate();
rhs.node.negate(); if sum.last_mut().unwrap().try_sum_in_place(&rhs).is_err() {
if sum.last_mut().unwrap().try_sum_in_place(&rhs.node).is_err() { sum.push(rhs);
sum.push(rhs.node);
} }
}, },
_ => { _ => {
@@ -1092,13 +944,10 @@ impl CalcNode {
} }
} }
Ok(ParsedCalcNode { Ok(if sum.len() == 1 {
node: if sum.len() == 1 { sum.drain(..).next().unwrap()
sum.drain(..).next().unwrap() } else {
} else { Self::Sum(sum.into_boxed_slice().into())
Self::Sum(sum.into_boxed_slice().into())
},
has_anchor_function,
}) })
} }
@@ -1115,33 +964,25 @@ impl CalcNode {
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
allowed: AllowParse, allowed: AllowParse,
) -> Result<ParsedCalcNode, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
let mut product = SmallVec::<[CalcNode; 1]>::new(); let mut product = SmallVec::<[CalcNode; 1]>::new();
let first = Self::parse_one(context, input, allowed)?; let first = Self::parse_one(context, input, allowed)?;
product.push(first.node); product.push(first);
let mut has_anchor_function = first.has_anchor_function;
loop { loop {
let start = input.state(); let start = input.state();
match input.next() { match input.next() {
Ok(&Token::Delim('*')) => { Ok(&Token::Delim('*')) => {
let mut rhs = Self::parse_one(context, input, allowed)?; let mut rhs = Self::parse_one(context, input, allowed)?;
has_anchor_function |= rhs.has_anchor_function;
// We can unwrap here, becuase we start the function by adding a node to // We can unwrap here, becuase we start the function by adding a node to
// the list. // the list.
if !product if !product.last_mut().unwrap().try_product_in_place(&mut rhs) {
.last_mut() product.push(rhs);
.unwrap()
.try_product_in_place(&mut rhs.node)
{
// TODO(dshin): need to combine
product.push(rhs.node);
} }
}, },
Ok(&Token::Delim('/')) => { Ok(&Token::Delim('/')) => {
let rhs = Self::parse_one(context, input, allowed)?; let rhs = Self::parse_one(context, input, allowed)?;
has_anchor_function |= rhs.has_anchor_function;
enum InPlaceDivisionResult { enum InPlaceDivisionResult {
/// The right was merged into the left. /// The right was merged into the left.
@@ -1183,10 +1024,10 @@ impl CalcNode {
// already resolve it, then merge it with the last node on the product list. // already resolve it, then merge it with the last node on the product list.
// We can unwrap here, becuase we start the function by adding a node to // We can unwrap here, becuase we start the function by adding a node to
// the list. // the list.
match try_division_in_place(&mut product.last_mut().unwrap(), &rhs.node) { match try_division_in_place(&mut product.last_mut().unwrap(), &rhs) {
InPlaceDivisionResult::Merged => {}, InPlaceDivisionResult::Merged => {},
InPlaceDivisionResult::Unchanged => { InPlaceDivisionResult::Unchanged => {
product.push(Self::Invert(Box::new(rhs.node))) product.push(Self::Invert(Box::new(rhs)))
}, },
InPlaceDivisionResult::Invalid => { InPlaceDivisionResult::Invalid => {
return Err( return Err(
@@ -1202,13 +1043,10 @@ impl CalcNode {
} }
} }
Ok(ParsedCalcNode { Ok(if product.len() == 1 {
node: if product.len() == 1 { product.drain(..).next().unwrap()
product.drain(..).next().unwrap() } else {
} else { Self::Product(product.into_boxed_slice().into())
Self::Product(product.into_boxed_slice().into())
},
has_anchor_function,
}) })
} }
@@ -1227,7 +1065,6 @@ impl CalcNode {
pub fn into_length_or_percentage( pub fn into_length_or_percentage(
mut self, mut self,
clamping_mode: AllowedNumericType, clamping_mode: AllowedNumericType,
has_anchor_function: bool,
) -> Result<CalcLengthPercentage, ()> { ) -> Result<CalcLengthPercentage, ()> {
self.simplify_and_sort(); self.simplify_and_sort();
@@ -1240,7 +1077,6 @@ impl CalcNode {
Ok(CalcLengthPercentage { Ok(CalcLengthPercentage {
clamping_mode, clamping_mode,
node: self, node: self,
has_anchor_function,
}) })
} }
} }

View File

@@ -1649,7 +1649,6 @@ impl From<Percentage> for LengthPercentage {
if let Some(clamping_mode) = pc.calc_clamping_mode() { if let Some(clamping_mode) = pc.calc_clamping_mode() {
LengthPercentage::Calc(Box::new(CalcLengthPercentage { LengthPercentage::Calc(Box::new(CalcLengthPercentage {
clamping_mode, clamping_mode,
has_anchor_function: false,
node: CalcNode::Leaf(calc::Leaf::Percentage(pc.get())), node: CalcNode::Leaf(calc::Leaf::Percentage(pc.get())),
})) }))
} else { } else {
@@ -1812,11 +1811,11 @@ impl LengthPercentage {
/// Returns self as specified::calc::CalcNode. /// Returns self as specified::calc::CalcNode.
/// Note that this expect the clamping_mode is AllowedNumericType::All for Calc. The caller /// Note that this expect the clamping_mode is AllowedNumericType::All for Calc. The caller
/// should take care about it when using this function. /// should take care about it when using this function.
fn to_calc_node(self) -> (CalcNode, bool) { fn to_calc_node(self) -> CalcNode {
match self { match self {
LengthPercentage::Length(l) => (CalcNode::Leaf(calc::Leaf::Length(l)), false), LengthPercentage::Length(l) => CalcNode::Leaf(calc::Leaf::Length(l)),
LengthPercentage::Percentage(p) => (CalcNode::Leaf(calc::Leaf::Percentage(p.0)), false), LengthPercentage::Percentage(p) => CalcNode::Leaf(calc::Leaf::Percentage(p.0)),
LengthPercentage::Calc(p) => (p.node, p.has_anchor_function), LengthPercentage::Calc(p) => p.node,
} }
} }
@@ -1825,14 +1824,13 @@ impl LengthPercentage {
let mut sum = smallvec::SmallVec::<[CalcNode; 2]>::new(); let mut sum = smallvec::SmallVec::<[CalcNode; 2]>::new();
sum.push(CalcNode::Leaf(calc::Leaf::Percentage(1.0))); sum.push(CalcNode::Leaf(calc::Leaf::Percentage(1.0)));
let (mut node, has_anchor_function) = self.to_calc_node(); let mut node = self.to_calc_node();
node.negate(); node.negate();
sum.push(node); sum.push(node);
let calc = CalcNode::Sum(sum.into_boxed_slice().into()); let calc = CalcNode::Sum(sum.into_boxed_slice().into());
LengthPercentage::Calc(Box::new( LengthPercentage::Calc(Box::new(
calc.into_length_or_percentage(clamping_mode, has_anchor_function) calc.into_length_or_percentage(clamping_mode).unwrap(),
.unwrap(),
)) ))
} }
} }

View File

@@ -9812,29 +9812,3 @@ pub extern "C" fn Servo_ResolveAnchorSizeFunction(
) { ) {
*out = AnchorPositioningFunctionResolution::new(func.resolve(prop)); *out = AnchorPositioningFunctionResolution::new(func.resolve(prop));
} }
/// Result of resolving a math function node potentially containing
/// anchor positioning function.
#[repr(u8)]
pub enum CalcAnchorPositioningFunctionResolution {
/// Anchor positioning function is used, but at least one of them
/// did not resolve to a valid reference - Property using this
/// expression is now invalid at computed time.
Invalid,
/// Anchor positioning function is used, and all of them resolved
/// to valid references, or specified a fallback.
Valid(computed::LengthPercentage),
}
#[no_mangle]
pub extern "C" fn Servo_ResolveAnchorPositioningFunctionInCalc(
calc: &computed::length_percentage::CalcLengthPercentage,
side: PhysicalSide,
prop: PositionProperty,
out: &mut CalcAnchorPositioningFunctionResolution,
) {
*out = match calc.resolve_anchor_functions(side, prop) {
Ok(l) => CalcAnchorPositioningFunctionResolution::Valid(l.into()),
Err(_) => CalcAnchorPositioningFunctionResolution::Invalid,
};
}