Commit 2f2f3fd7 authored by Tiaan Louw's avatar Tiaan Louw
Browse files

Bug 1819893 - Add negate node to use in place of mul_by in sum nodes r=emilio

Sum nodes would use mul_by to negate nodes to do subtraction, but some
nodes are not distributive.  This patch adds a negate node, so that the
operations inside these negate nodes can be resolved first and then the
"subtraction" can be applied.

Differential Revision: https://phabricator.services.mozilla.com/D172941
parent c89b637e
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -3567,6 +3567,11 @@ void StyleCalcNode::ScaleLengthsBy(float aScale) {
      }
      break;
    }
    case Tag::Negate: {
      const auto& negate = AsNegate();
      ScaleNode(*negate);
      break;
    }
    case Tag::Hypot: {
      for (const auto& child : AsHypot().AsSpan()) {
        ScaleNode(child);
@@ -3596,6 +3601,11 @@ ResultT StyleCalcNode::ResolveInternal(ResultT aPercentageBasis,
        return leaf.AsLength().ToCSSPixels();
      }
    }
    case Tag::Negate: {
      const auto& negate = AsNegate();
      auto value = negate->ResolveInternal(aPercentageBasis, aConverter);
      return -value;
    }
    case Tag::Clamp: {
      auto& clamp = AsClamp();
      auto min = clamp.min->ResolveInternal(aPercentageBasis, aConverter);
+162 −27
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@
use num_traits::{Float, Zero};
use smallvec::SmallVec;
use std::fmt::{self, Write};
use std::ops::{Add, Div, Mul, Rem, Sub};
use std::ops::{Add, Div, Mul, Neg, Rem, Sub};
use std::{cmp, mem};
use style_traits::{CssWriter, ToCss};

@@ -161,6 +161,8 @@ pub enum SortKey {
pub enum GenericCalcNode<L> {
    /// A leaf node.
    Leaf(L),
    /// A node that negates its children, e.g. Negate(1) == -1.
    Negate(Box<GenericCalcNode<L>>),
    /// A sum node, representing `a + b + c` where a, b, and c are the
    /// arguments.
    Sum(crate::OwnedSlice<GenericCalcNode<L>>),
@@ -247,10 +249,80 @@ pub trait CalcNodeLeaf: Clone + Sized + PartialOrd + PartialEq + ToCss {
    fn sort_key(&self) -> SortKey;
}

/// The level of any argument being serialized in `to_css_impl`.
enum ArgumentLevel {
    /// The root of a calculation tree.
    CalculationRoot,
    /// The root of an operand node's argument, e.g. `min(10, 20)`, `10` and `20` will have this
    /// level, but min in this case will have `TopMost`.
    ArgumentRoot,
    /// Any other values serialized in the tree.
    Nested,
}

impl<L: CalcNodeLeaf> CalcNode<L> {
    /// Negates the leaf.
    fn negate(&mut self) {
        self.map(std::ops::Neg::neg);
    /// Negate the node inline.  If the node is distributive, it is replaced by the result,
    /// otherwise the node is wrapped in a [`Negate`] node.
    pub fn negate(&mut self) {
        match *self {
            CalcNode::Leaf(ref mut leaf) => leaf.map(|l| l.neg()),
            CalcNode::Negate(ref mut value) => {
                // Don't negate the value here.  Replace `self` with it's child.
                let result = mem::replace(
                    value.as_mut(),
                    Self::MinMax(Default::default(), MinMaxOp::Max),
                );
                *self = result;
            },
            CalcNode::Sum(ref mut children) => {
                for child in children.iter_mut() {
                    child.negate();
                }
            },
            CalcNode::MinMax(ref mut children, ref mut op) => {
                for child in children.iter_mut() {
                    child.negate();
                }

                // Negating min-max means the operation is swapped.
                *op = match *op {
                    MinMaxOp::Min => MinMaxOp::Max,
                    MinMaxOp::Max => MinMaxOp::Min,
                };
            },
            CalcNode::Clamp {
                ref mut min,
                ref mut center,
                ref mut max,
            } => {
                min.negate();
                center.negate();
                max.negate();

                mem::swap(min, max);
            },
            CalcNode::Round {
                ref mut value,
                ref mut step,
                ..
            } => {
                value.negate();
                step.negate();
            },
            CalcNode::ModRem {
                ref mut dividend,
                ref mut divisor,
                ..
            } => {
                dividend.negate();
                divisor.negate();
            },
            CalcNode::Hypot(ref mut children) => {
                for child in children.iter_mut() {
                    child.negate();
                }
            },
        }
    }

    fn sort_key(&self) -> SortKey {
@@ -296,6 +368,7 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
        fn map_internal<L: CalcNodeLeaf>(node: &mut CalcNode<L>, op: &mut impl FnMut(f32) -> f32) {
            match node {
                CalcNode::Leaf(l) => l.map(op),
                CalcNode::Negate(v) => map_internal(v, op),
                CalcNode::Sum(children) => {
                    for node in &mut **children {
                        map_internal(node, op);
@@ -363,6 +436,7 @@ impl<L: CalcNodeLeaf> CalcNode<L> {

        match *self {
            Self::Leaf(ref l) => CalcNode::Leaf(map(l)),
            Self::Negate(ref c) => CalcNode::Negate(Box::new(c.map_leaves_internal(map))),
            Self::Sum(ref c) => CalcNode::Sum(map_children(c, map)),
            Self::MinMax(ref c, op) => CalcNode::MinMax(map_children(c, map), op),
            Self::Clamp {
@@ -440,6 +514,7 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
    {
        Ok(match *self {
            Self::Leaf(ref l) => return leaf_to_output_fn(l),
            Self::Negate(ref c) => c.resolve_internal(leaf_to_output_fn)?.neg(),
            Self::Sum(ref c) => {
                let mut result = Zero::zero();
                for child in &**c {
@@ -631,6 +706,7 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
    pub fn mul_by(&mut self, scalar: f32) {
        match *self {
            Self::Leaf(ref mut l) => l.map(|v| v * scalar),
            Self::Negate(ref mut value) => value.mul_by(scalar),
            // Multiplication is distributive across this.
            Self::Sum(ref mut children) => {
                for node in &mut **children {
@@ -733,6 +809,9 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
                    child.visit_depth_first_internal(f);
                }
            },
            Self::Negate(ref mut value) => {
                value.visit_depth_first_internal(f);
            },
            Self::Leaf(..) => {},
        }
        f(self);
@@ -746,6 +825,8 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
    /// This is only needed if it's going to be preserved after parsing (so, for
    /// `<length-percentage>`). Otherwise we can just evaluate it using
    /// `resolve()`, and we'll come up with a simplified value anyways.
    ///
    /// <https://drafts.csswg.org/css-values-4/#calc-simplification>
    pub fn simplify_and_sort_direct_children(&mut self) {
        macro_rules! replace_self_with {
            ($slot:expr) => {{
@@ -1062,6 +1143,24 @@ impl<L: CalcNodeLeaf> CalcNode<L> {

                replace_self_with!(&mut result);
            },
            Self::Negate(ref mut child) => {
                // Step 6.
                match &mut **child {
                    CalcNode::Leaf(_) => {
                        // 1. If root’s child is a numeric value, return an equivalent numeric value, but
                        // with the value negated (0 - value).
                        child.negate();
                        replace_self_with!(&mut **child);
                    },
                    CalcNode::Negate(value) => {
                        // 2. If root’s child is a Negate node, return the child’s child.
                        replace_self_with!(&mut **value);
                    },
                    _ => {
                        // 3. Return root.
                    },
                }
            },
            Self::Leaf(ref mut l) => {
                l.simplify();
            },
@@ -1073,7 +1172,7 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
        self.visit_depth_first(|node| node.simplify_and_sort_direct_children())
    }

    fn to_css_impl<W>(&self, dest: &mut CssWriter<W>, is_outermost: bool) -> fmt::Result
    fn to_css_impl<W>(&self, dest: &mut CssWriter<W>, level: ArgumentLevel) -> fmt::Result
    where
        W: Write,
    {
@@ -1111,11 +1210,34 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
                dest.write_str("hypot(")?;
                true
            },
            _ => {
                if is_outermost {
            Self::Negate(_) => {
                // We never generate a [`Negate`] node as the root of a calculation, only inside
                // [`Sum`] nodes as a child. Because negate nodes are handled by the [`Sum`] node
                // directly (see below), this node will never be serialized.
                debug_assert!(
                    false,
                    "We never serialize Negate nodes as they are handled inside Sum nodes."
                );
                dest.write_str("(-1 * ")?;
                true
            },
            Self::Sum(_) => match level {
                ArgumentLevel::CalculationRoot => {
                    dest.write_str("calc(")?;
                }
                is_outermost
                    true
                },
                ArgumentLevel::ArgumentRoot => false,
                ArgumentLevel::Nested => {
                    dest.write_str("(")?;
                    true
                },
            },
            Self::Leaf(_) => match level {
                ArgumentLevel::CalculationRoot => {
                    dest.write_str("calc(")?;
                    true
                },
                ArgumentLevel::ArgumentRoot | ArgumentLevel::Nested => false,
            },
        };

@@ -1127,25 +1249,38 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
                        dest.write_str(", ")?;
                    }
                    first = false;
                    child.to_css_impl(dest, false)?;
                    child.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
                }
            },
            Self::Negate(ref value) => value.to_css_impl(dest, ArgumentLevel::Nested)?,
            Self::Sum(ref children) => {
                let mut first = true;
                for child in &**children {
                    if !first {
                        if child.is_negative_leaf() {
                        match child {
                            Self::Leaf(l) => {
                                if l.is_negative() {
                                    dest.write_str(" - ")?;
                            let mut c = child.clone();
                            c.negate();
                            c.to_css_impl(dest, false)?;
                                    let mut negated = l.clone();
                                    negated.negate();
                                    negated.to_css(dest)?;
                                } else {
                                    dest.write_str(" + ")?;
                            child.to_css_impl(dest, false)?;
                                    l.to_css(dest)?;
                                }
                            },
                            Self::Negate(n) => {
                                dest.write_str(" - ")?;
                                n.to_css_impl(dest, ArgumentLevel::Nested)?;
                            },
                            _ => {
                                dest.write_str(" + ")?;
                                child.to_css_impl(dest, ArgumentLevel::Nested)?;
                            },
                        }
                    } else {
                        first = false;
                        child.to_css_impl(dest, false)?;
                        child.to_css_impl(dest, ArgumentLevel::Nested)?;
                    }
                }
            },
@@ -1154,29 +1289,29 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
                ref center,
                ref max,
            } => {
                min.to_css_impl(dest, false)?;
                min.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
                dest.write_str(", ")?;
                center.to_css_impl(dest, false)?;
                center.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
                dest.write_str(", ")?;
                max.to_css_impl(dest, false)?;
                max.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
            },
            Self::Round {
                ref value,
                ref step,
                ..
            } => {
                value.to_css_impl(dest, false)?;
                value.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
                dest.write_str(", ")?;
                step.to_css_impl(dest, false)?;
                step.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
            },
            Self::ModRem {
                ref dividend,
                ref divisor,
                ..
            } => {
                dividend.to_css_impl(dest, false)?;
                dividend.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
                dest.write_str(", ")?;
                divisor.to_css_impl(dest, false)?;
                divisor.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
            },
            Self::Leaf(ref l) => l.to_css(dest)?,
        }
@@ -1203,6 +1338,6 @@ impl<L: CalcNodeLeaf> ToCss for CalcNode<L> {
    where
        W: Write,
    {
        self.to_css_impl(dest, /* is_outermost = */ true)
        self.to_css_impl(dest, ArgumentLevel::CalculationRoot)
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -697,7 +697,7 @@ impl CalcNode {
                        },
                        Token::Delim('-') => {
                            let mut rhs = Self::parse_product(context, input, allowed_units)?;
                            rhs.mul_by(-1.0);
                            rhs.negate();
                            sum.push(rhs);
                        },
                        _ => {