summaryrefslogtreecommitdiffstats
path: root/servo/components/style/values
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components/style/values')
-rw-r--r--servo/components/style/values/animated/mod.rs27
-rw-r--r--servo/components/style/values/computed/length_percentage.rs41
-rw-r--r--servo/components/style/values/computed/mod.rs19
-rw-r--r--servo/components/style/values/generics/calc.rs243
-rw-r--r--servo/components/style/values/generics/counters.rs37
-rw-r--r--servo/components/style/values/resolved/mod.rs19
-rw-r--r--servo/components/style/values/specified/box.rs24
-rw-r--r--servo/components/style/values/specified/calc.rs82
-rw-r--r--servo/components/style/values/specified/color.rs216
-rw-r--r--servo/components/style/values/specified/counters.rs52
10 files changed, 367 insertions, 393 deletions
diff --git a/servo/components/style/values/animated/mod.rs b/servo/components/style/values/animated/mod.rs
index 31ea206fc0..235ddcba5a 100644
--- a/servo/components/style/values/animated/mod.rs
+++ b/servo/components/style/values/animated/mod.rs
@@ -273,6 +273,23 @@ where
}
}
+impl<T> ToAnimatedValue for thin_vec::ThinVec<T>
+where
+ T: ToAnimatedValue,
+{
+ type AnimatedValue = thin_vec::ThinVec<<T as ToAnimatedValue>::AnimatedValue>;
+
+ #[inline]
+ fn to_animated_value(self) -> Self::AnimatedValue {
+ self.into_iter().map(T::to_animated_value).collect()
+ }
+
+ #[inline]
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+ animated.into_iter().map(T::from_animated_value).collect()
+ }
+}
+
impl<T> ToAnimatedValue for Box<T>
where
T: ToAnimatedValue,
@@ -452,6 +469,16 @@ where
}
}
+impl<T> ToAnimatedZero for thin_vec::ThinVec<T>
+where
+ T: ToAnimatedZero,
+{
+ #[inline]
+ fn to_animated_zero(&self) -> Result<Self, ()> {
+ self.iter().map(|v| v.to_animated_zero()).collect()
+ }
+}
+
impl<T> ToAnimatedZero for Box<[T]>
where
T: ToAnimatedZero,
diff --git a/servo/components/style/values/computed/length_percentage.rs b/servo/components/style/values/computed/length_percentage.rs
index c448025dd1..f5f3303141 100644
--- a/servo/components/style/values/computed/length_percentage.rs
+++ b/servo/components/style/values/computed/length_percentage.rs
@@ -680,12 +680,12 @@ impl calc::CalcNodeLeaf for CalcLengthPercentageLeaf {
}
}
- fn unitless_value(&self) -> f32 {
- match *self {
+ fn unitless_value(&self) -> Option<f32> {
+ Some(match *self {
Self::Length(ref l) => l.px(),
Self::Percentage(ref p) => p.0,
Self::Number(n) => n,
- }
+ })
}
fn new_number(value: f32) -> Self {
@@ -709,9 +709,18 @@ impl calc::CalcNodeLeaf for CalcLengthPercentageLeaf {
return None;
}
- let self_negative = self.is_negative();
- if self_negative != other.is_negative() {
- return Some(if self_negative { std::cmp::Ordering::Less } else { std::cmp::Ordering::Greater });
+ let Ok(self_negative) = self.is_negative() else {
+ return None;
+ };
+ let Ok(other_negative) = other.is_negative() else {
+ return None;
+ };
+ if self_negative != other_negative {
+ return Some(if self_negative {
+ std::cmp::Ordering::Less
+ } else {
+ std::cmp::Ordering::Greater
+ });
}
match (self, other) {
@@ -774,15 +783,17 @@ impl calc::CalcNodeLeaf for CalcLengthPercentageLeaf {
} else {
// The right side is not a number, so the result should be in the units of the right
// side.
- other.map(|v| v * *left);
- std::mem::swap(self, other);
- true
+ if other.map(|v| v * *left).is_ok() {
+ std::mem::swap(self, other);
+ true
+ } else {
+ false
+ }
}
} else if let Self::Number(ref right) = *other {
// The left side is not a number, but the right side is, so the result is the left
// side unit.
- self.map(|v| v * *right);
- true
+ self.map(|v| v * *right).is_ok()
} else {
// Neither side is a number, so a product is not possible.
false
@@ -814,8 +825,8 @@ impl calc::CalcNodeLeaf for CalcLengthPercentageLeaf {
})
}
- fn map(&mut self, mut op: impl FnMut(f32) -> f32) {
- match self {
+ fn map(&mut self, mut op: impl FnMut(f32) -> f32) -> Result<(), ()> {
+ Ok(match self {
Self::Length(value) => {
*value = Length::new(op(value.px()));
},
@@ -825,7 +836,7 @@ impl calc::CalcNodeLeaf for CalcLengthPercentageLeaf {
Self::Number(value) => {
*value = op(*value);
},
- }
+ })
}
fn simplify(&mut self) {}
@@ -921,7 +932,7 @@ impl specified::CalcLengthPercentage {
}
}),
Leaf::Number(n) => CalcLengthPercentageLeaf::Number(n),
- Leaf::Angle(..) | Leaf::Time(..) | Leaf::Resolution(..) => {
+ Leaf::Angle(..) | Leaf::Time(..) | Leaf::Resolution(..) | Leaf::ColorComponent(..) => {
unreachable!("Shouldn't have parsed")
},
});
diff --git a/servo/components/style/values/computed/mod.rs b/servo/components/style/values/computed/mod.rs
index 85aadb401f..ca32408a79 100644
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -649,6 +649,25 @@ where
}
}
+impl<T> ToComputedValue for thin_vec::ThinVec<T>
+where
+ T: ToComputedValue,
+{
+ type ComputedValue = thin_vec::ThinVec<<T as ToComputedValue>::ComputedValue>;
+
+ #[inline]
+ fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
+ self.iter()
+ .map(|item| item.to_computed_value(context))
+ .collect()
+ }
+
+ #[inline]
+ fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+ computed.iter().map(T::from_computed_value).collect()
+ }
+}
+
// NOTE(emilio): This is implementable more generically, but it's unlikely
// what you want there, as it forces you to have an extra allocation.
//
diff --git a/servo/components/style/values/generics/calc.rs b/servo/components/style/values/generics/calc.rs
index abcb5fe6eb..215db1932b 100644
--- a/servo/components/style/values/generics/calc.rs
+++ b/servo/components/style/values/generics/calc.rs
@@ -255,12 +255,15 @@ bitflags! {
const TIME = 1 << 3;
/// <resolution>
const RESOLUTION = 1 << 4;
+ /// A component of a color (r, g, b, h, s, l, alpha, etc.)
+ const COLOR_COMPONENT = 1 << 5;
/// <length-percentage>
const LENGTH_PERCENTAGE = Self::LENGTH.bits() | Self::PERCENTAGE.bits();
// NOTE: When you add to this, make sure to make Atan2 deal with these.
/// Allow all units.
- const ALL = Self::LENGTH.bits() | Self::PERCENTAGE.bits() | Self::ANGLE.bits() | Self::TIME.bits() | Self::RESOLUTION.bits();
+ const ALL = Self::LENGTH.bits() | Self::PERCENTAGE.bits() | Self::ANGLE.bits() |
+ Self::TIME.bits() | Self::RESOLUTION.bits() | Self::COLOR_COMPONENT.bits();
}
}
@@ -323,8 +326,8 @@ pub trait CalcNodeLeaf: Clone + Sized + PartialEq + ToCss {
/// Returns the unit of the leaf.
fn unit(&self) -> CalcUnits;
- /// Returns the unitless value of this leaf.
- fn unitless_value(&self) -> f32;
+ /// Returns the unitless value of this leaf if one is available.
+ fn unitless_value(&self) -> Option<f32>;
/// Return true if the units of both leaves are equal. (NOTE: Does not take
/// the values into account)
@@ -347,23 +350,31 @@ pub trait CalcNodeLeaf: Clone + Sized + PartialEq + ToCss {
fn as_number(&self) -> Option<f32>;
/// Whether this value is known-negative.
- fn is_negative(&self) -> bool {
- self.unitless_value().is_sign_negative()
+ fn is_negative(&self) -> Result<bool, ()> {
+ self.unitless_value()
+ .map(|v| Ok(v.is_sign_negative()))
+ .unwrap_or_else(|| Err(()))
}
/// Whether this value is infinite.
- fn is_infinite(&self) -> bool {
- self.unitless_value().is_infinite()
+ fn is_infinite(&self) -> Result<bool, ()> {
+ self.unitless_value()
+ .map(|v| Ok(v.is_infinite()))
+ .unwrap_or_else(|| Err(()))
}
/// Whether this value is zero.
- fn is_zero(&self) -> bool {
- self.unitless_value().is_zero()
+ fn is_zero(&self) -> Result<bool, ()> {
+ self.unitless_value()
+ .map(|v| Ok(v.is_zero()))
+ .unwrap_or_else(|| Err(()))
}
/// Whether this value is NaN.
- fn is_nan(&self) -> bool {
- self.unitless_value().is_nan()
+ fn is_nan(&self) -> Result<bool, ()> {
+ self.unitless_value()
+ .map(|v| Ok(v.is_nan()))
+ .unwrap_or_else(|| Err(()))
}
/// Tries to merge one leaf into another using the sum, that is, perform `x` + `y`.
@@ -379,12 +390,7 @@ pub trait CalcNodeLeaf: Clone + Sized + PartialEq + ToCss {
O: Fn(f32, f32) -> f32;
/// Map the value of this node with the given operation.
- fn map(&mut self, op: impl FnMut(f32) -> f32);
-
- /// Negates the leaf.
- fn negate(&mut self) {
- self.map(std::ops::Neg::neg);
- }
+ fn map(&mut self, op: impl FnMut(f32) -> f32) -> Result<(), ()>;
/// Canonicalizes the expression if necessary.
fn simplify(&mut self);
@@ -393,16 +399,20 @@ pub trait CalcNodeLeaf: Clone + Sized + PartialEq + ToCss {
fn sort_key(&self) -> SortKey;
/// Create a new leaf containing the sign() result of the given leaf.
- fn sign_from(leaf: &impl CalcNodeLeaf) -> Self {
- Self::new_number(if leaf.is_nan() {
+ fn sign_from(leaf: &impl CalcNodeLeaf) -> Result<Self, ()> {
+ let Some(value) = leaf.unitless_value() else {
+ return Err(());
+ };
+
+ Ok(Self::new_number(if value.is_nan() {
f32::NAN
- } else if leaf.is_zero() {
- leaf.unitless_value()
- } else if leaf.is_negative() {
+ } else if value.is_zero() {
+ value
+ } else if value.is_sign_negative() {
-1.0
} else {
1.0
- })
+ }))
}
}
@@ -426,8 +436,8 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
/// Change all the leaf nodes to have the given value. This is useful when
/// you have `calc(1px * nan)` and you want to replace the product node with
/// `calc(nan)`, in which case the unit will be retained.
- fn coerce_to_value(&mut self, value: f32) {
- self.map(|_| value);
+ fn coerce_to_value(&mut self, value: f32) -> Result<(), ()> {
+ self.map(|_| value)
}
/// Return true if a product is distributive over this node.
@@ -436,7 +446,7 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
#[inline]
pub fn is_product_distributive(&self) -> bool {
match self {
- Self::Leaf(_) => true,
+ Self::Leaf(l) => l.unit() != CalcUnits::COLOR_COMPONENT,
Self::Sum(children) => children.iter().all(|c| c.is_product_distributive()),
_ => false,
}
@@ -546,7 +556,11 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
}
match *self {
- CalcNode::Leaf(ref mut leaf) => leaf.negate(),
+ CalcNode::Leaf(ref mut leaf) => {
+ if leaf.map(std::ops::Neg::neg).is_err() {
+ wrap_self_in_negate(self)
+ }
+ },
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::dummy());
@@ -668,7 +682,9 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
}
if self.is_product_distributive() {
- self.map(|v| v * number);
+ if self.map(|v| v * number).is_err() {
+ return false;
+ }
return true;
}
}
@@ -682,7 +698,9 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
}
if other.is_product_distributive() {
- other.map(|v| v * number);
+ if other.map(|v| v * number).is_err() {
+ return false;
+ }
std::mem::swap(self, other);
return true;
}
@@ -706,48 +724,52 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
}
/// Map the value of this node with the given operation.
- pub fn map(&mut self, mut op: impl FnMut(f32) -> f32) {
- fn map_internal<L: CalcNodeLeaf>(node: &mut CalcNode<L>, op: &mut impl FnMut(f32) -> f32) {
+ pub fn map(&mut self, mut op: impl FnMut(f32) -> f32) -> Result<(), ()> {
+ fn map_internal<L: CalcNodeLeaf>(
+ node: &mut CalcNode<L>,
+ op: &mut impl FnMut(f32) -> f32,
+ ) -> Result<(), ()> {
match node {
CalcNode::Leaf(l) => l.map(op),
CalcNode::Negate(v) | CalcNode::Invert(v) => map_internal(v, op),
CalcNode::Sum(children) | CalcNode::Product(children) => {
for node in &mut **children {
- map_internal(node, op);
+ map_internal(node, op)?;
}
+ Ok(())
},
CalcNode::MinMax(children, _) => {
for node in &mut **children {
- map_internal(node, op);
+ map_internal(node, op)?;
}
+ Ok(())
},
CalcNode::Clamp { min, center, max } => {
- map_internal(min, op);
- map_internal(center, op);
- map_internal(max, op);
+ map_internal(min, op)?;
+ map_internal(center, op)?;
+ map_internal(max, op)
},
CalcNode::Round { value, step, .. } => {
- map_internal(value, op);
- map_internal(step, op);
+ map_internal(value, op)?;
+ map_internal(step, op)
},
CalcNode::ModRem {
dividend, divisor, ..
} => {
- map_internal(dividend, op);
- map_internal(divisor, op);
+ map_internal(dividend, op)?;
+ map_internal(divisor, op)
},
CalcNode::Hypot(children) => {
for node in &mut **children {
- map_internal(node, op);
+ map_internal(node, op)?;
}
+ Ok(())
},
- CalcNode::Abs(child) | CalcNode::Sign(child) => {
- map_internal(child, op);
- },
+ CalcNode::Abs(child) | CalcNode::Sign(child) => map_internal(child, op),
}
}
- map_internal(self, &mut op);
+ map_internal(self, &mut op)
}
/// Convert this `CalcNode` into a `CalcNode` with a different leaf kind.
@@ -849,12 +871,12 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
Self::Leaf(l) => leaf_to_output_fn(l),
Self::Negate(child) => {
let mut result = child.resolve_internal(leaf_to_output_fn)?;
- result.map(|v| v.neg());
+ result.map(|v| v.neg())?;
Ok(result)
},
Self::Invert(child) => {
let mut result = child.resolve_internal(leaf_to_output_fn)?;
- result.map(|v| 1.0 / v);
+ result.map(|v| 1.0 / v)?;
Ok(result)
},
Self::Sum(children) => {
@@ -878,13 +900,13 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
Some(left) => {
// Left side is a number, so we use the right node as the result.
result = right;
- result.map(|v| v * left);
+ result.map(|v| v * left)?;
},
None => {
// Left side is not a number, so check if the right side is.
match right.as_number() {
Some(right) => {
- result.map(|v| v * right);
+ result.map(|v| v * right)?;
},
None => {
// Multiplying with both sides having units.
@@ -900,7 +922,7 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
Self::MinMax(children, op) => {
let mut result = children[0].resolve_internal(leaf_to_output_fn)?;
- if result.is_nan() {
+ if result.is_nan()? {
return Ok(result);
}
@@ -912,7 +934,7 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
return Err(());
}
- if candidate.is_nan() {
+ if candidate.is_nan()? {
result = candidate;
break;
}
@@ -938,15 +960,15 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
return Err(());
}
- if min.is_nan() {
+ if min.is_nan()? {
return Ok(min);
}
- if center.is_nan() {
+ if center.is_nan()? {
return Ok(center);
}
- if max.is_nan() {
+ if max.is_nan()? {
return Ok(max);
}
@@ -972,7 +994,10 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
return Err(());
}
- let step = step.unitless_value().abs();
+ let Some(step) = step.unitless_value() else {
+ return Err(());
+ };
+ let step = step.abs();
value.map(|value| {
// TODO(emilio): Seems like at least a few of these
@@ -1039,7 +1064,7 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
}
},
}
- });
+ })?;
Ok(value)
},
@@ -1055,13 +1080,15 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
return Err(());
}
- let divisor = divisor.unitless_value();
- dividend.map(|dividend| op.apply(dividend, divisor));
+ let Some(divisor) = divisor.unitless_value() else {
+ return Err(());
+ };
+ dividend.map(|dividend| op.apply(dividend, divisor))?;
Ok(dividend)
},
Self::Hypot(children) => {
let mut result = children[0].resolve_internal(leaf_to_output_fn)?;
- result.map(|v| v.powi(2));
+ result.map(|v| v.powi(2))?;
for child in children.iter().skip(1) {
let child_value = child.resolve_internal(leaf_to_output_fn)?;
@@ -1070,45 +1097,48 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
return Err(());
}
- result.map(|v| v + child_value.unitless_value().powi(2));
+ let Some(child_value) = child_value.unitless_value() else {
+ return Err(());
+ };
+ result.map(|v| v + child_value.powi(2))?;
}
- result.map(|v| v.sqrt());
+ result.map(|v| v.sqrt())?;
Ok(result)
},
Self::Abs(ref c) => {
let mut result = c.resolve_internal(leaf_to_output_fn)?;
- result.map(|v| v.abs());
+ result.map(|v| v.abs())?;
Ok(result)
},
Self::Sign(ref c) => {
let result = c.resolve_internal(leaf_to_output_fn)?;
- Ok(L::sign_from(&result))
+ Ok(L::sign_from(&result)?)
},
}
}
- fn is_negative_leaf(&self) -> bool {
- match *self {
- Self::Leaf(ref l) => l.is_negative(),
+ fn is_negative_leaf(&self) -> Result<bool, ()> {
+ Ok(match *self {
+ Self::Leaf(ref l) => l.is_negative()?,
_ => false,
- }
+ })
}
- fn is_zero_leaf(&self) -> bool {
- match *self {
- Self::Leaf(ref l) => l.is_zero(),
+ fn is_zero_leaf(&self) -> Result<bool, ()> {
+ Ok(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(),
+ fn is_infinite_leaf(&self) -> Result<bool, ()> {
+ Ok(match *self {
+ Self::Leaf(ref l) => l.is_infinite()?,
_ => false,
- }
+ })
}
/// Visits all the nodes in this calculation tree recursively, starting by
@@ -1250,54 +1280,64 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
ref mut value,
ref mut step,
} => {
- if step.is_zero_leaf() {
- value.coerce_to_value(f32::NAN);
+ if value_or_stop!(step.is_zero_leaf()) {
+ value_or_stop!(value.coerce_to_value(f32::NAN));
replace_self_with!(&mut **value);
return;
}
- if value.is_infinite_leaf() && step.is_infinite_leaf() {
- value.coerce_to_value(f32::NAN);
+ if value_or_stop!(value.is_infinite_leaf()) &&
+ value_or_stop!(step.is_infinite_leaf())
+ {
+ value_or_stop!(value.coerce_to_value(f32::NAN));
replace_self_with!(&mut **value);
return;
}
- if value.is_infinite_leaf() {
+ if value_or_stop!(value.is_infinite_leaf()) {
replace_self_with!(&mut **value);
return;
}
- if step.is_infinite_leaf() {
+ if value_or_stop!(step.is_infinite_leaf()) {
match strategy {
RoundingStrategy::Nearest | RoundingStrategy::ToZero => {
- value.coerce_to_value(0.0);
+ value_or_stop!(value.coerce_to_value(0.0));
replace_self_with!(&mut **value);
return;
},
RoundingStrategy::Up => {
- if !value.is_negative_leaf() && !value.is_zero_leaf() {
- value.coerce_to_value(f32::INFINITY);
+ if !value_or_stop!(value.is_negative_leaf()) &&
+ !value_or_stop!(value.is_zero_leaf())
+ {
+ value_or_stop!(value.coerce_to_value(f32::INFINITY));
replace_self_with!(&mut **value);
return;
- } else if !value.is_negative_leaf() && value.is_zero_leaf() {
+ } else if !value_or_stop!(value.is_negative_leaf()) &&
+ value_or_stop!(value.is_zero_leaf())
+ {
replace_self_with!(&mut **value);
return;
} else {
- value.coerce_to_value(0.0);
+ value_or_stop!(value.coerce_to_value(0.0));
replace_self_with!(&mut **value);
return;
}
},
RoundingStrategy::Down => {
- if value.is_negative_leaf() && !value.is_zero_leaf() {
- value.coerce_to_value(f32::INFINITY);
+ if value_or_stop!(value.is_negative_leaf()) &&
+ !value_or_stop!(value.is_zero_leaf())
+ {
+ value_or_stop!(value.coerce_to_value(f32::INFINITY));
replace_self_with!(&mut **value);
return;
- } else if value.is_negative_leaf() && value.is_zero_leaf() {
+ } else if value_or_stop!(value.is_negative_leaf()) &&
+ value_or_stop!(value.is_zero_leaf())
+ {
replace_self_with!(&mut **value);
return;
} else {
- value.coerce_to_value(0.0);
+ value_or_stop!(value.coerce_to_value(0.0));
replace_self_with!(&mut **value);
return;
}
@@ -1305,17 +1345,18 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
}
}
- if step.is_negative_leaf() {
+ if value_or_stop!(step.is_negative_leaf()) {
step.negate();
}
let remainder = value_or_stop!(value.try_op(step, Rem::rem));
- if remainder.is_zero_leaf() {
+ if value_or_stop!(remainder.is_zero_leaf()) {
replace_self_with!(&mut **value);
return;
}
- let (mut lower_bound, mut upper_bound) = if value.is_negative_leaf() {
+ let (mut lower_bound, mut upper_bound) = if value_or_stop!(value.is_negative_leaf())
+ {
let upper_bound = value_or_stop!(value.try_op(&remainder, Sub::sub));
let lower_bound = value_or_stop!(upper_bound.try_op(&step, Sub::sub));
@@ -1348,11 +1389,11 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
let mut lower_diff = lower_bound.clone();
let mut upper_diff = upper_bound.clone();
- if lower_diff.is_negative_leaf() {
+ if value_or_stop!(lower_diff.is_negative_leaf()) {
lower_diff.negate();
}
- if upper_diff.is_negative_leaf() {
+ if value_or_stop!(upper_diff.is_negative_leaf()) {
upper_diff.negate();
}
@@ -1518,13 +1559,13 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
},
Self::Abs(ref mut child) => {
if let CalcNode::Leaf(leaf) = child.as_mut() {
- leaf.map(|v| v.abs());
+ value_or_stop!(leaf.map(|v| v.abs()));
replace_self_with!(&mut **child);
}
},
Self::Sign(ref mut child) => {
if let CalcNode::Leaf(leaf) = child.as_mut() {
- let mut result = Self::Leaf(L::sign_from(leaf));
+ let mut result = Self::Leaf(value_or_stop!(L::sign_from(leaf)));
replace_self_with!(&mut result);
}
},
@@ -1553,7 +1594,7 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
// 1. If root’s child is a number (not a percentage or dimension) return the
// reciprocal of the child’s value.
if leaf.unit().is_empty() {
- child.map(|v| 1.0 / v);
+ value_or_stop!(child.map(|v| 1.0 / v));
replace_self_with!(&mut **child);
}
},
@@ -1678,10 +1719,12 @@ impl<L: CalcNodeLeaf> CalcNode<L> {
if !first {
match child {
Self::Leaf(l) => {
- if l.is_negative() {
+ if let Ok(true) = l.is_negative() {
dest.write_str(" - ")?;
let mut negated = l.clone();
- negated.negate();
+ // We can unwrap here, because we already
+ // checked if the value inside is negative.
+ negated.map(std::ops::Neg::neg).unwrap();
negated.to_css(dest)?;
} else {
dest.write_str(" + ")?;
diff --git a/servo/components/style/values/generics/counters.rs b/servo/components/style/values/generics/counters.rs
index 3f23c74b33..d6db48f9c4 100644
--- a/servo/components/style/values/generics/counters.rs
+++ b/servo/components/style/values/generics/counters.rs
@@ -203,6 +203,41 @@ fn is_decimal(counter_type: &CounterStyleType) -> bool {
*counter_type == CounterStyle::decimal()
}
+/// The non-normal, non-none values of the content property.
+#[derive(
+ Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToShmem,
+)]
+#[repr(C)]
+pub struct GenericContentItems<Image> {
+ /// The actual content items. Note that, past the alt marker, only some subset (strings,
+ /// attr(), counter())
+ pub items: thin_vec::ThinVec<GenericContentItem<Image>>,
+ /// The index at which alt text starts, always non-zero. If equal to items.len(), no alt text
+ /// exists.
+ pub alt_start: usize,
+}
+
+impl<Image> ToCss for GenericContentItems<Image>
+where
+ Image: ToCss,
+{
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ for (i, item) in self.items.iter().enumerate() {
+ if i == self.alt_start {
+ dest.write_str(" /")?;
+ }
+ if i != 0 {
+ dest.write_str(" ")?;
+ }
+ item.to_css(dest)?;
+ }
+ Ok(())
+ }
+}
+
/// The specified value for the `content` property.
///
/// https://drafts.csswg.org/css-content/#propdef-content
@@ -216,7 +251,7 @@ pub enum GenericContent<Image> {
/// `none` reserved keyword.
None,
/// Content items.
- Items(#[css(iterable)] crate::OwnedSlice<GenericContentItem<Image>>),
+ Items(GenericContentItems<Image>),
}
pub use self::GenericContent as Content;
diff --git a/servo/components/style/values/resolved/mod.rs b/servo/components/style/values/resolved/mod.rs
index d830474fe6..fc59f8f36d 100644
--- a/servo/components/style/values/resolved/mod.rs
+++ b/servo/components/style/values/resolved/mod.rs
@@ -180,6 +180,25 @@ where
}
}
+impl<T> ToResolvedValue for thin_vec::ThinVec<T>
+where
+ T: ToResolvedValue,
+{
+ type ResolvedValue = thin_vec::ThinVec<<T as ToResolvedValue>::ResolvedValue>;
+
+ #[inline]
+ fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {
+ self.into_iter()
+ .map(|item| item.to_resolved_value(context))
+ .collect()
+ }
+
+ #[inline]
+ fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
+ resolved.into_iter().map(T::from_resolved_value).collect()
+ }
+}
+
impl<T> ToResolvedValue for Box<T>
where
T: ToResolvedValue,
diff --git a/servo/components/style/values/specified/box.rs b/servo/components/style/values/specified/box.rs
index ee50227504..9b87103048 100644
--- a/servo/components/style/values/specified/box.rs
+++ b/servo/components/style/values/specified/box.rs
@@ -1495,6 +1495,9 @@ pub enum Appearance {
/// For HTML's <input type=number>
#[parse(condition = "ParserContext::chrome_rules_enabled")]
NumberInput,
+ /// For HTML's <input type=password>
+ #[parse(condition = "ParserContext::chrome_rules_enabled")]
+ PasswordInput,
/// The progress bar's progress indicator
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Progresschunk,
@@ -1576,27 +1579,6 @@ pub enum Appearance {
/// A tooltip.
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Tooltip,
- /// A listbox or tree widget header
- #[parse(condition = "ParserContext::chrome_rules_enabled")]
- Treeheader,
- /// An individual header cell
- #[parse(condition = "ParserContext::chrome_rules_enabled")]
- Treeheadercell,
- /// A tree item.
- #[parse(condition = "ParserContext::chrome_rules_enabled")]
- Treeitem,
- /// A tree widget branch line
- #[parse(condition = "ParserContext::chrome_rules_enabled")]
- Treeline,
- /// A tree widget twisty.
- #[parse(condition = "ParserContext::chrome_rules_enabled")]
- Treetwisty,
- /// Open tree widget twisty.
- #[parse(condition = "ParserContext::chrome_rules_enabled")]
- Treetwistyopen,
- /// A tree widget.
- #[parse(condition = "ParserContext::chrome_rules_enabled")]
- Treeview,
/// Mac help button.
#[parse(condition = "ParserContext::chrome_rules_enabled")]
diff --git a/servo/components/style/values/specified/calc.rs b/servo/components/style/values/specified/calc.rs
index 17f043ac58..a354963ce1 100644
--- a/servo/components/style/values/specified/calc.rs
+++ b/servo/components/style/values/specified/calc.rs
@@ -6,6 +6,7 @@
//!
//! [calc]: https://drafts.csswg.org/css-values/#calc-notation
+use crate::color::parsing::ChannelKeyword;
use crate::parser::ParserContext;
use crate::values::generics::calc::{
self as generic, CalcNodeLeaf, CalcUnits, MinMaxOp, ModRemOp, PositivePercentageBasis,
@@ -80,6 +81,8 @@ pub enum Leaf {
Time(Time),
/// `<resolution>`
Resolution(Resolution),
+ /// A component of a color.
+ ColorComponent(ChannelKeyword),
/// `<percentage>`
Percentage(CSSFloat),
/// `<number>`
@@ -107,6 +110,7 @@ impl ToCss for Leaf {
Self::Percentage(p) => serialize_percentage(p, dest),
Self::Angle(ref a) => a.to_css(dest),
Self::Time(ref t) => t.to_css(dest),
+ Self::ColorComponent(ref s) => s.to_css(dest),
}
}
}
@@ -152,19 +156,21 @@ impl generic::CalcNodeLeaf for Leaf {
Leaf::Angle(_) => CalcUnits::ANGLE,
Leaf::Time(_) => CalcUnits::TIME,
Leaf::Resolution(_) => CalcUnits::RESOLUTION,
+ Leaf::ColorComponent(_) => CalcUnits::COLOR_COMPONENT,
Leaf::Percentage(_) => CalcUnits::PERCENTAGE,
Leaf::Number(_) => CalcUnits::empty(),
}
}
- fn unitless_value(&self) -> f32 {
- match *self {
+ fn unitless_value(&self) -> Option<f32> {
+ Some(match *self {
Self::Length(ref l) => l.unitless_value(),
Self::Percentage(n) | Self::Number(n) => n,
Self::Resolution(ref r) => r.dppx(),
Self::Angle(ref a) => a.degrees(),
Self::Time(ref t) => t.seconds(),
- }
+ Self::ColorComponent(_) => return None,
+ })
}
fn new_number(value: f32) -> Self {
@@ -182,8 +188,8 @@ impl generic::CalcNodeLeaf for Leaf {
return None;
}
- let self_negative = self.is_negative();
- if self_negative != other.is_negative() {
+ let self_negative = self.is_negative().unwrap_or(false);
+ if self_negative != other.is_negative().unwrap_or(false) {
return Some(if self_negative {
cmp::Ordering::Less
} else {
@@ -198,10 +204,11 @@ impl generic::CalcNodeLeaf for Leaf {
(&Time(ref one), &Time(ref other)) => one.seconds().partial_cmp(&other.seconds()),
(&Resolution(ref one), &Resolution(ref other)) => one.dppx().partial_cmp(&other.dppx()),
(&Number(ref one), &Number(ref other)) => one.partial_cmp(other),
+ (&ColorComponent(ref one), &ColorComponent(ref other)) => one.partial_cmp(other),
_ => {
match *self {
Length(..) | Percentage(..) | Angle(..) | Time(..) | Number(..) |
- Resolution(..) => {},
+ Resolution(..) | ColorComponent(..) => {},
}
unsafe {
debug_unreachable!("Forgot a branch?");
@@ -216,14 +223,15 @@ impl generic::CalcNodeLeaf for Leaf {
Leaf::Angle(_) |
Leaf::Time(_) |
Leaf::Resolution(_) |
- Leaf::Percentage(_) => None,
+ Leaf::Percentage(_) |
+ Leaf::ColorComponent(_) => None,
Leaf::Number(value) => Some(value),
}
}
fn sort_key(&self) -> SortKey {
match *self {
- Self::Number(..) => SortKey::Number,
+ Self::Number(..) | Self::ColorComponent(..) => SortKey::Number,
Self::Percentage(..) => SortKey::Percentage,
Self::Time(..) => SortKey::Sec,
Self::Resolution(..) => SortKey::Dppx,
@@ -316,7 +324,7 @@ impl generic::CalcNodeLeaf for Leaf {
_ => {
match *other {
Number(..) | Percentage(..) | Angle(..) | Time(..) | Resolution(..) |
- Length(..) => {},
+ Length(..) | ColorComponent(..) => {},
}
unsafe {
debug_unreachable!();
@@ -336,15 +344,17 @@ impl generic::CalcNodeLeaf for Leaf {
} else {
// The right side is not a number, so the result should be in the units of the right
// side.
- other.map(|v| v * *left);
- std::mem::swap(self, other);
- true
+ if other.map(|v| v * *left).is_ok() {
+ std::mem::swap(self, other);
+ true
+ } else {
+ false
+ }
}
} else if let Self::Number(ref right) = *other {
// The left side is not a number, but the right side is, so the result is the left
// side unit.
- self.map(|v| v * *right);
- true
+ self.map(|v| v * *right).is_ok()
} else {
// Neither side is a number, so a product is not possible.
false
@@ -392,7 +402,7 @@ impl generic::CalcNodeLeaf for Leaf {
_ => {
match *other {
Number(..) | Percentage(..) | Angle(..) | Time(..) | Length(..) |
- Resolution(..) => {},
+ Resolution(..) | ColorComponent(..) => {},
}
unsafe {
debug_unreachable!();
@@ -401,15 +411,16 @@ impl generic::CalcNodeLeaf for Leaf {
}
}
- fn map(&mut self, mut op: impl FnMut(f32) -> f32) {
- match self {
+ fn map(&mut self, mut op: impl FnMut(f32) -> f32) -> Result<(), ()> {
+ Ok(match self {
Leaf::Length(one) => *one = one.map(op),
Leaf::Angle(one) => *one = specified::Angle::from_calc(op(one.degrees())),
Leaf::Time(one) => *one = specified::Time::from_seconds(op(one.seconds())),
Leaf::Resolution(one) => *one = specified::Resolution::from_dppx(op(one.dppx())),
Leaf::Percentage(one) => *one = op(*one),
Leaf::Number(one) => *one = op(*one),
- }
+ Leaf::ColorComponent(..) => return Err(()),
+ })
}
}
@@ -468,15 +479,30 @@ impl CalcNode {
CalcNode::parse(context, input, function, allowed_units)
},
&Token::Ident(ref ident) => {
- let number = match_ignore_ascii_case! { &**ident,
- "e" => std::f32::consts::E,
- "pi" => std::f32::consts::PI,
- "infinity" => f32::INFINITY,
- "-infinity" => f32::NEG_INFINITY,
- "nan" => f32::NAN,
- _ => return Err(location.new_unexpected_token_error(Token::Ident(ident.clone()))),
+ let leaf = match_ignore_ascii_case! { &**ident,
+ "e" => Leaf::Number(std::f32::consts::E),
+ "pi" => Leaf::Number(std::f32::consts::PI),
+ "infinity" => Leaf::Number(f32::INFINITY),
+ "-infinity" => Leaf::Number(f32::NEG_INFINITY),
+ "nan" => Leaf::Number(f32::NAN),
+ _ => {
+ if crate::color::parsing::rcs_enabled() &&
+ allowed_units.intersects(CalcUnits::COLOR_COMPONENT)
+ {
+ if let Ok(channel_keyword) = ChannelKeyword::from_ident(&ident) {
+ Leaf::ColorComponent(channel_keyword)
+ } else {
+ return Err(location
+ .new_unexpected_token_error(Token::Ident(ident.clone())));
+ }
+ } else {
+ return Err(
+ location.new_unexpected_token_error(Token::Ident(ident.clone()))
+ );
+ }
+ },
};
- Ok(CalcNode::Leaf(Leaf::Number(number)))
+ Ok(CalcNode::Leaf(leaf))
},
t => Err(location.new_unexpected_token_error(t.clone())),
}
@@ -817,7 +843,9 @@ impl CalcNode {
if let Ok(resolved) = right.resolve() {
if let Some(number) = resolved.as_number() {
if number != 1.0 && left.is_product_distributive() {
- left.map(|l| l / number);
+ if left.map(|l| l / number).is_err() {
+ return InPlaceDivisionResult::Invalid;
+ }
return InPlaceDivisionResult::Merged;
}
} else {
diff --git a/servo/components/style/values/specified/color.rs b/servo/components/style/values/specified/color.rs
index f823ba7d30..289d89e44e 100644
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -5,12 +5,8 @@
//! Specified color values.
use super::AllowQuirks;
-use crate::color::component::ColorComponent;
-use crate::color::convert::normalize_hue;
-use crate::color::parsing::{
- self, ColorParser, FromParsedColor, NumberOrAngle, NumberOrPercentage,
-};
-use crate::color::{mix::ColorInterpolationMethod, AbsoluteColor, ColorSpace};
+use crate::color::mix::ColorInterpolationMethod;
+use crate::color::{parsing, AbsoluteColor, ColorSpace};
use crate::media_queries::Device;
use crate::parser::{Parse, ParserContext};
use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue};
@@ -19,8 +15,7 @@ use crate::values::generics::color::{
};
use crate::values::specified::Percentage;
use crate::values::{normalize, CustomIdent};
-use cssparser::color::OPAQUE;
-use cssparser::{color::PredefinedColorSpace, BasicParseErrorKind, ParseErrorKind, Parser, Token};
+use cssparser::{BasicParseErrorKind, ParseErrorKind, Parser, Token};
use std::fmt::{self, Write};
use std::io::Write as IoWrite;
use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError, StyleParseErrorKind};
@@ -429,199 +424,6 @@ impl SystemColor {
}
}
-impl<T> From<ColorComponent<T>> for Option<T> {
- fn from(value: ColorComponent<T>) -> Self {
- match value {
- ColorComponent::None => None,
- ColorComponent::Value(value) => Some(value),
- }
- }
-}
-
-impl ColorComponent<NumberOrPercentage> {
- #[inline]
- fn into_alpha(self) -> Option<f32> {
- match self {
- ColorComponent::None => None,
- ColorComponent::Value(number_or_percentage) => {
- Some(normalize(number_or_percentage.to_number(1.0)).clamp(0.0, OPAQUE))
- },
- }
- }
-}
-
-impl FromParsedColor for Color {
- fn from_current_color() -> Self {
- Color::CurrentColor
- }
-
- fn from_rgba(
- red: ColorComponent<u8>,
- green: ColorComponent<u8>,
- blue: ColorComponent<u8>,
- alpha: ColorComponent<NumberOrPercentage>,
- ) -> Self {
- macro_rules! c {
- ($c:expr) => {{
- match $c {
- ColorComponent::None => 0u8,
- ColorComponent::Value(value) => value,
- }
- }};
- }
-
- // Legacy rgb() doesn't support "none" alpha values and falls back to 0.
- let alpha = alpha.into_alpha().unwrap_or(0.0);
-
- AbsoluteColor::srgb_legacy(c!(red), c!(green), c!(blue), alpha).into()
- }
-
- fn from_hsl(
- hue: ColorComponent<NumberOrAngle>,
- saturation: ColorComponent<NumberOrPercentage>,
- lightness: ColorComponent<NumberOrPercentage>,
- alpha: ColorComponent<NumberOrPercentage>,
- ) -> Self {
- // Percent reference range for S and L: 0% = 0.0, 100% = 100.0
- const LIGHTNESS_RANGE: f32 = 100.0;
- const SATURATION_RANGE: f32 = 100.0;
-
- let hue = hue.map_value(|angle| normalize_hue(angle.degrees()));
- let saturation =
- saturation.map_value(|s| s.to_number(SATURATION_RANGE).clamp(0.0, SATURATION_RANGE));
- let lightness =
- lightness.map_value(|l| l.to_number(LIGHTNESS_RANGE).clamp(0.0, LIGHTNESS_RANGE));
-
- AbsoluteColor::new(
- ColorSpace::Hsl,
- hue,
- saturation,
- lightness,
- alpha.into_alpha(),
- )
- .into()
- }
-
- fn from_hwb(
- hue: ColorComponent<NumberOrAngle>,
- whiteness: ColorComponent<NumberOrPercentage>,
- blackness: ColorComponent<NumberOrPercentage>,
- alpha: ColorComponent<NumberOrPercentage>,
- ) -> Self {
- // Percent reference range for W and B: 0% = 0.0, 100% = 100.0
- const WHITENESS_RANGE: f32 = 100.0;
- const BLACKNESS_RANGE: f32 = 100.0;
-
- let hue = hue.map_value(|angle| normalize_hue(angle.degrees()));
- let whiteness =
- whiteness.map_value(|w| w.to_number(WHITENESS_RANGE).clamp(0.0, WHITENESS_RANGE));
- let blackness =
- blackness.map_value(|b| b.to_number(BLACKNESS_RANGE).clamp(0.0, BLACKNESS_RANGE));
-
- AbsoluteColor::new(
- ColorSpace::Hwb,
- hue,
- whiteness,
- blackness,
- alpha.into_alpha(),
- )
- .into()
- }
-
- fn from_lab(
- lightness: ColorComponent<NumberOrPercentage>,
- a: ColorComponent<NumberOrPercentage>,
- b: ColorComponent<NumberOrPercentage>,
- alpha: ColorComponent<NumberOrPercentage>,
- ) -> Self {
- // for L: 0% = 0.0, 100% = 100.0
- // for a and b: -100% = -125, 100% = 125
- const LIGHTNESS_RANGE: f32 = 100.0;
- const A_B_RANGE: f32 = 125.0;
-
- let lightness = lightness.map_value(|l| l.to_number(LIGHTNESS_RANGE));
- let a = a.map_value(|a| a.to_number(A_B_RANGE));
- let b = b.map_value(|b| b.to_number(A_B_RANGE));
-
- AbsoluteColor::new(ColorSpace::Lab, lightness, a, b, alpha.into_alpha()).into()
- }
-
- fn from_lch(
- lightness: ColorComponent<NumberOrPercentage>,
- chroma: ColorComponent<NumberOrPercentage>,
- hue: ColorComponent<NumberOrAngle>,
- alpha: ColorComponent<NumberOrPercentage>,
- ) -> Self {
- // for L: 0% = 0.0, 100% = 100.0
- // for C: 0% = 0, 100% = 150
- const LIGHTNESS_RANGE: f32 = 100.0;
- const CHROMA_RANGE: f32 = 150.0;
-
- let lightness = lightness.map_value(|l| l.to_number(LIGHTNESS_RANGE));
- let chroma = chroma.map_value(|c| c.to_number(CHROMA_RANGE));
- let hue = hue.map_value(|angle| normalize_hue(angle.degrees()));
-
- AbsoluteColor::new(ColorSpace::Lch, lightness, chroma, hue, alpha.into_alpha()).into()
- }
-
- fn from_oklab(
- lightness: ColorComponent<NumberOrPercentage>,
- a: ColorComponent<NumberOrPercentage>,
- b: ColorComponent<NumberOrPercentage>,
- alpha: ColorComponent<NumberOrPercentage>,
- ) -> Self {
- // for L: 0% = 0.0, 100% = 1.0
- // for a and b: -100% = -0.4, 100% = 0.4
- const LIGHTNESS_RANGE: f32 = 1.0;
- const A_B_RANGE: f32 = 0.4;
-
- let lightness = lightness.map_value(|l| l.to_number(LIGHTNESS_RANGE));
- let a = a.map_value(|a| a.to_number(A_B_RANGE));
- let b = b.map_value(|b| b.to_number(A_B_RANGE));
-
- AbsoluteColor::new(ColorSpace::Oklab, lightness, a, b, alpha.into_alpha()).into()
- }
-
- fn from_oklch(
- lightness: ColorComponent<NumberOrPercentage>,
- chroma: ColorComponent<NumberOrPercentage>,
- hue: ColorComponent<NumberOrAngle>,
- alpha: ColorComponent<NumberOrPercentage>,
- ) -> Self {
- // for L: 0% = 0.0, 100% = 1.0
- // for C: 0% = 0.0 100% = 0.4
- const LIGHTNESS_RANGE: f32 = 1.0;
- const CHROMA_RANGE: f32 = 0.4;
-
- let lightness = lightness.map_value(|l| l.to_number(LIGHTNESS_RANGE));
- let chroma = chroma.map_value(|c| c.to_number(CHROMA_RANGE));
- let hue = hue.map_value(|angle| normalize_hue(angle.degrees()));
-
- AbsoluteColor::new(
- ColorSpace::Oklch,
- lightness,
- chroma,
- hue,
- alpha.into_alpha(),
- )
- .into()
- }
-
- fn from_color_function(
- color_space: PredefinedColorSpace,
- c1: ColorComponent<NumberOrPercentage>,
- c2: ColorComponent<NumberOrPercentage>,
- c3: ColorComponent<NumberOrPercentage>,
- alpha: ColorComponent<NumberOrPercentage>,
- ) -> Self {
- let c1 = c1.map_value(|c| c.to_number(1.0));
- let c2 = c2.map_value(|c| c.to_number(1.0));
- let c3 = c3.map_value(|c| c.to_number(1.0));
-
- AbsoluteColor::new(color_space.into(), c1, c2, c3, alpha.into_alpha()).into()
- }
-}
-
/// Whether to preserve authored colors during parsing. That's useful only if we
/// plan to serialize the color back.
#[derive(Copy, Clone)]
@@ -658,8 +460,7 @@ impl Color {
},
};
- let color_parser = ColorParser { context: &context };
- match input.try_parse(|i| parsing::parse_color_with(&color_parser, i)) {
+ match input.try_parse(|i| parsing::parse_color_with(context, i)) {
Ok(mut color) => {
if let Color::Absolute(ref mut absolute) = color {
// Because we can't set the `authored` value at construction time, we have to set it
@@ -827,12 +628,9 @@ impl Color {
loc: &cssparser::SourceLocation,
) -> Result<Self, ParseError<'i>> {
match cssparser::color::parse_hash_color(bytes) {
- Ok((r, g, b, a)) => Ok(Self::from_rgba(
- r.into(),
- g.into(),
- b.into(),
- ColorComponent::Value(NumberOrPercentage::Number { value: a }),
- )),
+ Ok((r, g, b, a)) => Ok(Self::from_absolute_color(AbsoluteColor::srgb_legacy(
+ r, g, b, a,
+ ))),
Err(()) => Err(loc.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
}
}
diff --git a/servo/components/style/values/specified/counters.rs b/servo/components/style/values/specified/counters.rs
index 7760be91d7..6e41497caf 100644
--- a/servo/components/style/values/specified/counters.rs
+++ b/servo/components/style/values/specified/counters.rs
@@ -192,29 +192,33 @@ impl Parse for Content {
return Ok(generics::Content::None);
}
- let mut content = vec![];
- let mut has_alt_content = false;
+ let mut items = thin_vec::ThinVec::new();
+ let mut alt_start = None;
loop {
- {
+ if alt_start.is_none() {
if let Ok(image) = input.try_parse(|i| Image::parse_forbid_none(context, i)) {
- content.push(generics::ContentItem::Image(image));
+ items.push(generics::ContentItem::Image(image));
continue;
}
}
- match input.next() {
- Ok(&Token::QuotedString(ref value)) => {
- content.push(generics::ContentItem::String(
+ let Ok(t) = input.next() else { break };
+ match *t {
+ Token::QuotedString(ref value) => {
+ items.push(generics::ContentItem::String(
value.as_ref().to_owned().into(),
));
},
- Ok(&Token::Function(ref name)) => {
+ Token::Function(ref name) => {
+ // FIXME(emilio): counter() / counters() should be valid per spec past
+ // the alt marker, but it's likely non-trivial to support and other
+ // browsers don't support it either, so restricting it for now.
let result = match_ignore_ascii_case! { &name,
- "counter" => input.parse_nested_block(|input| {
+ "counter" if alt_start.is_none() => input.parse_nested_block(|input| {
let name = CustomIdent::parse(input, &[])?;
let style = Content::parse_counter_style(context, input);
Ok(generics::ContentItem::Counter(name, style))
}),
- "counters" => input.parse_nested_block(|input| {
+ "counters" if alt_start.is_none() => input.parse_nested_block(|input| {
let name = CustomIdent::parse(input, &[])?;
input.expect_comma()?;
let separator = input.expect_string()?.as_ref().to_owned().into();
@@ -232,17 +236,16 @@ impl Parse for Content {
))
}
}?;
- content.push(result);
+ items.push(result);
},
- Ok(&Token::Ident(ref ident)) => {
- content.push(match_ignore_ascii_case! { &ident,
+ Token::Ident(ref ident) if alt_start.is_none() => {
+ items.push(match_ignore_ascii_case! { &ident,
"open-quote" => generics::ContentItem::OpenQuote,
"close-quote" => generics::ContentItem::CloseQuote,
"no-open-quote" => generics::ContentItem::NoOpenQuote,
"no-close-quote" => generics::ContentItem::NoCloseQuote,
#[cfg(feature = "gecko")]
- "-moz-alt-content" => {
- has_alt_content = true;
+ "-moz-alt-content" if context.in_ua_sheet() => {
generics::ContentItem::MozAltContent
},
"-moz-label-content" if context.chrome_rules_enabled() => {
@@ -256,17 +259,26 @@ impl Parse for Content {
}
});
},
- Err(_) => break,
- Ok(t) => {
+ Token::Delim('/')
+ if alt_start.is_none() &&
+ !items.is_empty() &&
+ static_prefs::pref!("layout.css.content.alt-text.enabled") =>
+ {
+ alt_start = Some(items.len());
+ },
+ ref t => {
let t = t.clone();
return Err(input.new_unexpected_token_error(t));
},
}
}
- // We don't allow to parse `-moz-alt-content` in multiple positions.
- if content.is_empty() || (has_alt_content && content.len() != 1) {
+ if items.is_empty() {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
- Ok(generics::Content::Items(content.into()))
+ let alt_start = alt_start.unwrap_or(items.len());
+ Ok(generics::Content::Items(generics::GenericContentItems {
+ items,
+ alt_start,
+ }))
}
}