tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 2fe2049e805b5716442a8bafaa340d1261b6cffb
parent 28249dccc9b988be3d3d8765d043ffab2339d6d3
Author: Jan Varga <jan.varga@gmail.com>
Date:   Thu,  4 Dec 2025 17:42:35 +0000

Bug 1990419 - Add initial CSSNumericValue reification support for <length> values; r=firefox-style-system-reviewers,dshin

This patch introduces the initial infrastructure for reifying <length> values
into CSSNumericValue objects as part of the CSS Typed OM implementation.

A new NumericValue enum is added to represent property-agnostic numeric values,
mirroring the structure of the Typed OM CSSNumericValue hierarchy. It currently
supports the Unit (CSSUnitValue) and Sum (CSSMathSum) variants.

As an initial integration step, this patch enables CSSNumericValue based
reification for the "outline-offset" property.

Support for additional properties will be added in follow-up patches and bugs.

Differential Revision: https://phabricator.services.mozilla.com/D270612

Diffstat:
Mlayout/style/typedom/CSSMathSum.cpp | 27+++++++++++++++++++++++++++
Mlayout/style/typedom/CSSMathSum.h | 4++++
Mlayout/style/typedom/CSSUnitValue.cpp | 6++++++
Mlayout/style/typedom/CSSUnitValue.h | 4++++
Mlayout/style/typedom/StylePropertyMap.cpp | 14++++++++++++--
Mlayout/style/typedom/StylePropertyMapReadOnly.cpp | 46++++++++++++++++++++++++++++++++++++++++++++++
Mservo/components/style/values/computed/length_percentage.rs | 1+
Mservo/components/style/values/generics/calc.rs | 43+++++++++++++++++++++++++++++++++++++++++--
Mservo/components/style/values/specified/border.rs | 1+
Mservo/components/style/values/specified/calc.rs | 17+++++++++++++++--
Mservo/components/style/values/specified/length.rs | 14+++++++++++++-
Mservo/components/style_traits/lib.rs | 4++--
Mservo/components/style_traits/values.rs | 48+++++++++++++++++++++++++++++++++++++++++++++---
Mtesting/web-platform/meta/css/css-typed-om/the-stylepropertymap/properties/outline-offset.html.ini | 3---
Mtesting/web-platform/meta/css/css-typed-om/the-stylepropertymap/properties/overflow-clip-margin.html.ini | 6++++++
15 files changed, 223 insertions(+), 15 deletions(-)

diff --git a/layout/style/typedom/CSSMathSum.cpp b/layout/style/typedom/CSSMathSum.cpp @@ -13,6 +13,7 @@ #include "mozilla/dom/CSSNumericArray.h" #include "mozilla/dom/CSSNumericValueBinding.h" #include "mozilla/dom/CSSUnitValue.h" +#include "nsString.h" namespace mozilla::dom { @@ -77,6 +78,32 @@ CSSNumericArray* CSSMathSum::Values() const { return mValues; } // end of CSSMathSum Web IDL implementation +void CSSMathSum::ToCssTextWithProperty(const CSSPropertyId& aPropertyId, + nsACString& aDest) const { + aDest.Append("calc("_ns); + + bool written = false; + + for (uint32_t index = 0; index < mValues->Length(); index++) { + bool found; + CSSNumericValue* value = mValues->IndexedGetter(index, found); + MOZ_ASSERT(found); + + if (value->IsCSSUnitValue()) { + CSSUnitValue& unitValue = value->GetAsCSSUnitValue(); + + if (written) { + aDest.Append(" + "_ns); + } + + unitValue.ToCssTextWithProperty(aPropertyId, aDest); + written = true; + } + } + + aDest.Append(")"_ns); +} + CSSMathSum& CSSStyleValue::GetAsCSSMathSum() { MOZ_DIAGNOSTIC_ASSERT(mValueType == ValueType::MathSum); diff --git a/layout/style/typedom/CSSMathSum.h b/layout/style/typedom/CSSMathSum.h @@ -21,6 +21,7 @@ class nsISupports; namespace mozilla { +struct CSSPropertyId; class ErrorResult; namespace dom { @@ -51,6 +52,9 @@ class CSSMathSum final : public CSSMathValue { // end of CSSMathSum Web IDL declarations + void ToCssTextWithProperty(const CSSPropertyId& aPropertyId, + nsACString& aDest) const; + private: virtual ~CSSMathSum() = default; diff --git a/layout/style/typedom/CSSUnitValue.cpp b/layout/style/typedom/CSSUnitValue.cpp @@ -56,6 +56,12 @@ void CSSUnitValue::GetUnit(nsCString& aRetVal) const { aRetVal = mUnit; } // end of CSSUnitValue Web IDL implementation +void CSSUnitValue::ToCssTextWithProperty(const CSSPropertyId& aPropertyId, + nsACString& aDest) const { + aDest.AppendFloat(mValue); + aDest.Append(mUnit); +} + CSSUnitValue& CSSStyleValue::GetAsCSSUnitValue() { MOZ_DIAGNOSTIC_ASSERT(mValueType == ValueType::UnitValue); diff --git a/layout/style/typedom/CSSUnitValue.h b/layout/style/typedom/CSSUnitValue.h @@ -19,6 +19,7 @@ class nsISupports; namespace mozilla { +struct CSSPropertyId; class ErrorResult; namespace dom { @@ -49,6 +50,9 @@ class CSSUnitValue final : public CSSNumericValue { // end of CSSUnitValue Web IDL declarations + void ToCssTextWithProperty(const CSSPropertyId& aPropertyId, + nsACString& aDest) const; + private: virtual ~CSSUnitValue() = default; diff --git a/layout/style/typedom/StylePropertyMap.cpp b/layout/style/typedom/StylePropertyMap.cpp @@ -11,7 +11,9 @@ #include "mozilla/RefPtr.h" #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/CSSKeywordValue.h" +#include "mozilla/dom/CSSMathSum.h" #include "mozilla/dom/CSSStyleValue.h" +#include "mozilla/dom/CSSUnitValue.h" #include "mozilla/dom/StylePropertyMapBinding.h" #include "nsCOMPtr.h" #include "nsCSSProps.h" @@ -78,11 +80,19 @@ void StylePropertyMap::Set( nsAutoCString cssText; switch (styleValue.GetValueType()) { - case CSSStyleValue::ValueType::MathSum: + case CSSStyleValue::ValueType::MathSum: { + CSSMathSum& mathSum = styleValue.GetAsCSSMathSum(); + + mathSum.ToCssTextWithProperty(propertyId, cssText); break; + } - case CSSStyleValue::ValueType::UnitValue: + case CSSStyleValue::ValueType::UnitValue: { + CSSUnitValue& unitValue = styleValue.GetAsCSSUnitValue(); + + unitValue.ToCssTextWithProperty(propertyId, cssText); break; + } case CSSStyleValue::ValueType::KeywordValue: { CSSKeywordValue& keywordValue = styleValue.GetAsCSSKeywordValue(); diff --git a/layout/style/typedom/StylePropertyMapReadOnly.cpp b/layout/style/typedom/StylePropertyMapReadOnly.cpp @@ -14,7 +14,10 @@ #include "mozilla/RefPtr.h" #include "mozilla/ServoStyleConsts.h" #include "mozilla/dom/CSSKeywordValue.h" +#include "mozilla/dom/CSSMathSum.h" +#include "mozilla/dom/CSSNumericArray.h" #include "mozilla/dom/CSSStyleValue.h" +#include "mozilla/dom/CSSUnitValue.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/StylePropertyMapReadOnlyBinding.h" #include "nsCSSProps.h" @@ -143,6 +146,11 @@ void StylePropertyMapReadOnly::Get(const nsACString& aProperty, return; } + // XXX Move the creation of CSSStyleValue to a dedicated class for example + // CSSStyleValueFactory and eventually split the handling of tags into + // separate methods to make the code more readable and accessible for + // CSSStyleValue::Parse. See bug 2004057 + RefPtr<CSSStyleValue> styleValue; switch (result.tag) { @@ -154,6 +162,44 @@ void StylePropertyMapReadOnly::Get(const nsACString& aProperty, styleValue = MakeRefPtr<CSSKeywordValue>(mParent, typedValue.AsKeyword()); break; + + case StyleTypedValue::Tag::Numeric: { + auto numericValue = typedValue.AsNumeric(); + + switch (numericValue.tag) { + case StyleNumericValue::Tag::Unit: { + auto unitValue = numericValue.AsUnit(); + + styleValue = MakeRefPtr<CSSUnitValue>(mParent, unitValue.value, + unitValue.unit); + break; + } + + case StyleNumericValue::Tag::Sum: { + auto mathSum = numericValue.AsSum(); + + nsTArray<RefPtr<CSSNumericValue>> values; + + for (const auto& value : mathSum.values) { + // XXX Only supporting units for now + if (value.IsUnit()) { + auto unitValue = value.AsUnit(); + + values.AppendElement(MakeRefPtr<CSSUnitValue>( + mParent, unitValue.value, unitValue.unit)); + } + } + + auto array = + MakeRefPtr<CSSNumericArray>(mParent, std::move(values)); + + styleValue = MakeAndAddRef<CSSMathSum>(mParent, std::move(array)); + break; + } + } + + break; + } } break; } diff --git a/servo/components/style/values/computed/length_percentage.rs b/servo/components/style/values/computed/length_percentage.rs @@ -699,6 +699,7 @@ impl<'de> Deserialize<'de> for LengthPercentage { ToAnimatedZero, ToCss, ToResolvedValue, + ToTyped, )] #[allow(missing_docs)] #[repr(u8)] diff --git a/servo/components/style/values/generics/calc.rs b/servo/components/style/values/generics/calc.rs @@ -13,7 +13,9 @@ use smallvec::SmallVec; use std::fmt::{self, Write}; use std::ops::{Add, Mul, Neg, Rem, Sub}; use std::{cmp, mem}; -use style_traits::{CssWriter, ToCss}; +use style_traits::{CssWriter, NumericValue, ToCss, ToTyped, TypedValue}; + +use thin_vec::ThinVec; /// Whether we're a `min` or `max` function. #[derive( @@ -339,7 +341,7 @@ macro_rules! compare_helpers { } /// A trait that represents all the stuff a valid leaf of a calc expression. -pub trait CalcNodeLeaf: Clone + Sized + PartialEq + ToCss { +pub trait CalcNodeLeaf: Clone + Sized + PartialEq + ToCss + ToTyped { /// Returns the unit of the leaf. fn unit(&self) -> CalcUnits; @@ -1935,6 +1937,37 @@ impl<L: CalcNodeLeaf> CalcNode<L> { Ok(()) } + fn to_typed_impl(&self, level: ArgumentLevel) -> Option<TypedValue> { + // XXX Only supporting Sum and Leaf for now + match *self { + Self::Sum(ref children) => { + let mut values = ThinVec::new(); + for child in &**children { + if let Some(TypedValue::Numeric(inner)) = + child.to_typed_impl(ArgumentLevel::Nested) + { + values.push(inner); + } + } + Some(TypedValue::Numeric(NumericValue::Sum { values })) + }, + Self::Leaf(ref l) => match l.to_typed() { + Some(TypedValue::Numeric(inner)) => match level { + ArgumentLevel::CalculationRoot => { + Some(TypedValue::Numeric(NumericValue::Sum { + values: ThinVec::from([inner]), + })) + }, + ArgumentLevel::ArgumentRoot | ArgumentLevel::Nested => { + Some(TypedValue::Numeric(inner)) + }, + }, + _ => None, + }, + _ => None, + } + } + fn compare( &self, other: &Self, @@ -1961,6 +1994,12 @@ impl<L: CalcNodeLeaf> ToCss for CalcNode<L> { } } +impl<L: CalcNodeLeaf> ToTyped for CalcNode<L> { + fn to_typed(&self) -> Option<TypedValue> { + self.to_typed_impl(ArgumentLevel::CalculationRoot) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/servo/components/style/values/specified/border.rs b/servo/components/style/values/specified/border.rs @@ -235,6 +235,7 @@ impl ToComputedValue for BorderSideWidth { #[derive( Clone, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem, ToTyped, )] +#[typed_value(derive_fields)] pub struct BorderSideOffset(Length); impl ToComputedValue for BorderSideOffset { diff --git a/servo/components/style/values/specified/calc.rs b/servo/components/style/values/specified/calc.rs @@ -25,7 +25,9 @@ use smallvec::SmallVec; use std::cmp; use std::fmt::{self, Write}; use style_traits::values::specified::AllowedNumericType; -use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss}; +use style_traits::{ + CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss, ToTyped, TypedValue, +}; /// The name of the mathematical function that we're parsing. #[derive(Clone, Copy, Debug, Parse)] @@ -120,14 +122,25 @@ impl ToCss for Leaf { } } +impl ToTyped for Leaf { + fn to_typed(&self) -> Option<TypedValue> { + // XXX Only supporting Length for now + match *self { + Self::Length(ref l) => l.to_typed(), + _ => None, + } + } +} + /// A struct to hold a simplified `<length>` or `<percentage>` expression. /// /// In some cases, e.g. DOMMatrix, we support calc(), but reject all the /// relative lengths, and to_computed_pixel_length_without_context() handles /// this case. Therefore, if you want to add a new field, please make sure this /// function work properly. -#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)] +#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem, ToTyped)] #[allow(missing_docs)] +#[typed_value(derive_fields)] pub struct CalcLengthPercentage { #[css(skip)] pub clamping_mode: AllowedNumericType, diff --git a/servo/components/style/values/specified/length.rs b/servo/components/style/values/specified/length.rs @@ -29,7 +29,10 @@ use cssparser::{Parser, Token}; use std::cmp; use std::fmt::{self, Write}; use style_traits::values::specified::AllowedNumericType; -use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss}; +use style_traits::{ + CssString, CssWriter, NumericValue, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss, + ToTyped, TypedValue, +}; pub use super::image::Image; pub use super::image::{EndingShape as GradientEndingShape, Gradient}; @@ -1356,6 +1359,14 @@ impl ToCss for NoCalcLength { } } +impl ToTyped for NoCalcLength { + fn to_typed(&self) -> Option<TypedValue> { + let value = self.unitless_value(); + let unit = CssString::from(self.unit()); + Some(TypedValue::Numeric(NumericValue::Unit { value, unit })) + } +} + impl SpecifiedValueInfo for NoCalcLength {} impl PartialOrd for NoCalcLength { @@ -1407,6 +1418,7 @@ impl Zero for NoCalcLength { /// /// <https://drafts.csswg.org/css-values/#lengths> #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)] +#[typed_value(derive_fields)] pub enum Length { /// The internal length type that cannot parse `calc` NoCalc(NoCalcLength), diff --git a/servo/components/style_traits/lib.rs b/servo/components/style_traits/lib.rs @@ -76,8 +76,8 @@ pub mod owned_str; pub use crate::specified_value_info::{CssType, KeywordsCollectFn, SpecifiedValueInfo}; pub use crate::values::{ - Comma, CommaWithSpace, CssString, CssStringWriter, CssWriter, OneOrMoreSeparated, Separator, - Space, ToCss, ToTyped, TypedValue, + Comma, CommaWithSpace, CssString, CssStringWriter, CssWriter, NumericValue, OneOrMoreSeparated, + Separator, Space, ToCss, ToTyped, TypedValue, }; /// The error type for all CSS parsing routines. diff --git a/servo/components/style_traits/values.rs b/servo/components/style_traits/values.rs @@ -9,6 +9,7 @@ use cssparser::ToCss as CssparserToCss; use cssparser::{serialize_string, ParseError, Parser, Token, UnicodeRange}; use servo_arc::Arc; use std::fmt::{self, Write}; +use thin_vec::ThinVec; /// Serialises a value according to its CSS representation. /// @@ -594,6 +595,41 @@ pub mod specified { } } +/// A numeric value used by the Typed OM. +/// +/// This corresponds to `CSSNumericValue` and its subclasses in the Typed OM +/// specification. It represents numbers that can appear in CSS values, +/// including both simple unit quantities and sums of numeric terms. +/// +/// Unlike the parser-level representation, `NumericValue` is property-agnostic +/// and suitable for conversion to or from the `CSSNumericValue` family of DOM +/// objects. +#[derive(Clone, Debug)] +#[repr(C)] +pub enum NumericValue { + /// A single numeric value with a concrete unit. + /// + /// This corresponds to `CSSUnitValue`. The `value` field stores the raw + /// numeric component, and the `unit` field stores the textual unit + /// identifier (e.g. `"px"`, `"em"`, `"%"`, `"deg"`). + Unit { + /// The numeric component of the value. + value: f32, + /// The textual unit string (e.g. `"px"`, `"em"`, `"deg"`). + unit: CssString, + }, + + /// A sum of multiple numeric values. + /// + /// This corresponds to `CSSMathSum`, representing an expression such as + /// `10px + 2em`. Each entry in `values` is another `NumericValue`, which + /// may itself be a unit value or a nested sum. + Sum { + /// The list of numeric terms that make up the sum. + values: ThinVec<NumericValue>, + }, +} + /// A property-agnostic representation of a value, used by Typed OM. /// /// `TypedValue` is the internal counterpart of the various `CSSStyleValue` @@ -608,6 +644,12 @@ pub enum TypedValue { /// transferred independently of any specific property. This corresponds /// to `CSSKeywordValue` in the Typed OM specification. Keyword(CssString), + + /// A numeric value such as a length, angle, time, or a sum thereof. + /// + /// This corresponds to the `CSSNumericValue` hierarchy in the Typed OM + /// specification, including `CSSUnitValue` and `CSSMathSum`. + Numeric(NumericValue), } /// Reifies a value into its Typed OM representation. @@ -662,9 +704,9 @@ where impl ToTyped for Au { fn to_typed(&self) -> Option<TypedValue> { - // XXX Should return TypedValue::Numeric in px units once that variant - // is available. Tracked in bug 1990419. - None + let value = self.to_f32_px(); + let unit = CssString::from("px"); + Some(TypedValue::Numeric(NumericValue::Unit { value, unit })) } } diff --git a/testing/web-platform/meta/css/css-typed-om/the-stylepropertymap/properties/outline-offset.html.ini b/testing/web-platform/meta/css/css-typed-om/the-stylepropertymap/properties/outline-offset.html.ini @@ -3,9 +3,6 @@ [Can set 'outline-offset' to var() references: ] expected: FAIL - [Can set 'outline-offset' to a length: ] - expected: FAIL - [Setting 'outline-offset' to a percent: throws TypeError] expected: FAIL diff --git a/testing/web-platform/meta/css/css-typed-om/the-stylepropertymap/properties/overflow-clip-margin.html.ini b/testing/web-platform/meta/css/css-typed-om/the-stylepropertymap/properties/overflow-clip-margin.html.ini @@ -13,3 +13,9 @@ ['overflow-clip-margin' does not support '10px content-box'] expected: FAIL + + ['overflow-clip-margin' does not support '0px'] + expected: FAIL + + ['overflow-clip-margin' does not support '10px'] + expected: FAIL