script: Implement CSS.registerProperty (#38682)

The implementation is mostly equivalent to
https://searchfox.org/mozilla-central/source/servo/ports/geckolib/glue.rs#9480.

Testing: New web platform tests start to pass

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
Simon Wülker 2025-08-14 15:34:02 +02:00 committed by GitHub
parent 3a0e8fefde
commit 43da933247
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 185 additions and 46 deletions

View file

@ -22,6 +22,7 @@ atomic_refcell = { workspace = true }
base = { workspace = true }
bitflags = { workspace = true }
compositing_traits = { workspace = true }
cssparser = { workspace = true }
data-url = { workspace = true }
embedder_traits = { workspace = true }
euclid = { workspace = true }

View file

@ -17,6 +17,7 @@ use base::id::{PipelineId, WebViewId};
use bitflags::bitflags;
use compositing_traits::CrossProcessCompositorApi;
use compositing_traits::display_list::ScrollType;
use cssparser::ParserInput;
use embedder_traits::{Theme, ViewportDetails};
use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect};
use euclid::{Point2D, Scale, Size2D};
@ -27,9 +28,9 @@ use fxhash::FxHashMap;
use ipc_channel::ipc::IpcSender;
use layout_api::wrapper_traits::LayoutNode;
use layout_api::{
IFrameSizes, Layout, LayoutConfig, LayoutDamage, LayoutFactory, OffsetParentResponse, QueryMsg,
ReflowGoal, ReflowPhasesRun, ReflowRequest, ReflowRequestRestyle, ReflowResult,
TrustedNodeAddress,
IFrameSizes, Layout, LayoutConfig, LayoutDamage, LayoutFactory, OffsetParentResponse,
PropertyRegistration, QueryMsg, ReflowGoal, ReflowPhasesRun, ReflowRequest,
ReflowRequestRestyle, ReflowResult, RegisterPropertyError, TrustedNodeAddress,
};
use log::{debug, error, warn};
use malloc_size_of::{MallocConditionalSizeOf, MallocSizeOf, MallocSizeOfOps};
@ -50,6 +51,7 @@ use style::animation::DocumentAnimationSet;
use style::context::{
QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, SharedStyleContext,
};
use style::custom_properties::{SpecifiedValue, parse_name};
use style::dom::{OpaqueNode, ShowSubtreeDataAndPrimaryValues, TElement, TNode};
use style::error_reporting::RustLogReporter;
use style::font_metrics::FontMetrics;
@ -58,6 +60,11 @@ use style::invalidation::element::restyle_hints::RestyleHint;
use style::media_queries::{Device, MediaList, MediaType};
use style::properties::style_structs::Font;
use style::properties::{ComputedValues, PropertyId};
use style::properties_and_values::registry::{
PropertyRegistration as StyloPropertyRegistration, PropertyRegistrationData,
};
use style::properties_and_values::rule::{Inherits, PropertyRegistrationError, PropertyRuleName};
use style::properties_and_values::syntax::Descriptor;
use style::queries::values::PrefersColorScheme;
use style::selector_parser::{PseudoElement, RestyleDamage, SnapshotMap};
use style::servo::media_queries::FontMetricsProvider;
@ -72,6 +79,7 @@ use style::traversal_flags::TraversalFlags;
use style::values::computed::font::GenericFontFamily;
use style::values::computed::{CSSPixelLength, FontSize, Length, NonNegativeLength};
use style::values::specified::font::{KeywordInfo, QueryFontMetricsFlags};
use style::values::{Parser, SourceLocation};
use style::{Zero, driver};
use style_traits::{CSSPixel, SpeculativePainter};
use stylo_atoms::Atom;
@ -510,6 +518,92 @@ impl Layout for LayoutThread {
fn needs_new_display_list(&self) -> bool {
self.need_new_display_list.get()
}
/// <https://drafts.css-houdini.org/css-properties-values-api-1/#the-registerproperty-function>
fn register_custom_property(
&mut self,
property_registration: PropertyRegistration,
) -> Result<(), RegisterPropertyError> {
// Step 2. If name is not a custom property name string, throw a SyntaxError and exit this algorithm.
// If property set already contains an entry with name as its property name
// (compared codepoint-wise), throw an InvalidModificationError and exit this algorithm.
let Ok(name) = parse_name(&property_registration.name) else {
return Err(RegisterPropertyError::InvalidName);
};
let name = Atom::from(name);
if self
.stylist
.custom_property_script_registry()
.get(&name)
.is_some()
{
return Err(RegisterPropertyError::AlreadyRegistered);
}
// Step 3. Attempt to consume a syntax definition from syntax. If it returns failure,
// throw a SyntaxError. Otherwise, let syntax definition be the returned syntax definition.
let syntax = Descriptor::from_str(&property_registration.syntax, false)
.map_err(|_| RegisterPropertyError::InvalidSyntax)?;
// Step 4 - Parse and validate initial value
let initial_value = match property_registration.initial_value {
Some(value) => {
let mut input = ParserInput::new(&value);
let parsed = Parser::new(&mut input)
.parse_entirely(|input| {
input.skip_whitespace();
SpecifiedValue::parse(input, &property_registration.url_data)
.map(servo_arc::Arc::new)
})
.ok();
if parsed.is_none() {
return Err(RegisterPropertyError::InvalidInitialValue);
}
parsed
},
None => None,
};
StyloPropertyRegistration::validate_initial_value(&syntax, initial_value.as_deref())
.map_err(|error| match error {
PropertyRegistrationError::InitialValueNotComputationallyIndependent => {
RegisterPropertyError::InitialValueNotComputationallyIndependent
},
PropertyRegistrationError::InvalidInitialValue => {
RegisterPropertyError::InvalidInitialValue
},
PropertyRegistrationError::NoInitialValue => RegisterPropertyError::NoInitialValue,
})?;
// Step 5. Set inherit flag to the value of inherits.
let inherits = if property_registration.inherits {
Inherits::True
} else {
Inherits::False
};
// Step 6. Let registered property be a struct with a property name of name, a syntax of
// syntax definition, an initial value of parsed initial value, and an inherit flag of inherit flag.
// Append registered property to property set.
let property_registration = StyloPropertyRegistration {
name: PropertyRuleName(name),
data: PropertyRegistrationData {
syntax,
initial_value,
inherits,
},
url_data: property_registration.url_data,
source_location: SourceLocation { line: 0, column: 0 },
};
self.stylist
.custom_property_script_registry_mut()
.register(property_registration);
self.stylist.rebuild_initial_values_for_custom_properties();
Ok(())
}
}
impl LayoutThread {