LibWeb/CSS: Reify StyleValues in StylePropertyMap get()/getAll()

The limitations right now are:
- We don't know if a property is a list or not.
- We always reify as a CSSStyleValue directly.

So, we pass some tests but only ones that expect a CSSStyleValue.
This commit is contained in:
Sam Atkins 2025-08-13 15:26:54 +01:00
commit 089f70a918
Notes: github-actions[bot] 2025-08-18 09:14:05 +00:00
9 changed files with 63 additions and 24 deletions

View file

@ -60,8 +60,11 @@ WebIDL::ExceptionOr<Variant<GC::Ref<CSSStyleValue>, Empty>> StylePropertyMapRead
// 3. Let props be the value of thiss [[declarations]] internal slot.
auto& props = m_declarations;
// FIXME: 4. If props[property] exists, subdivide into iterations props[property], then reify the first item of the result and return it.
(void)props;
// 4. If props[property] exists, subdivide into iterations props[property], then reify the first item of the result and return it.
if (auto property_value = get_style_value(props, property)) {
// FIXME: Subdivide into iterations, and only reify/return the first.
return property_value->reify(realm(), property);
}
// 5. Otherwise, return undefined.
return Empty {};
@ -83,8 +86,11 @@ WebIDL::ExceptionOr<Vector<GC::Ref<CSSStyleValue>>> StylePropertyMapReadOnly::ge
// 3. Let props be the value of thiss [[declarations]] internal slot.
auto& props = m_declarations;
// FIXME: 4. If props[property] exists, subdivide into iterations props[property], then reify each item of the result, and return the list.
(void)props;
// 4. If props[property] exists, subdivide into iterations props[property], then reify each item of the result, and return the list.
if (auto property_value = get_style_value(props, property)) {
// FIXME: Subdivide into iterations.
return Vector { property_value->reify(realm(), property) };
}
// 5. Otherwise, return an empty list.
return Vector<GC::Ref<CSSStyleValue>> {};
@ -116,6 +122,8 @@ WebIDL::ExceptionOr<bool> StylePropertyMapReadOnly::has(String property)
auto property_id = property_id_from_string(property);
if (!property_id.has_value())
return false;
// Ensure style is computed on the element before we try to read it, so we can check custom properties.
element.document().update_style();
if (property_id == PropertyID::Custom) {
if (element.get_custom_property(property))
return true;
@ -140,6 +148,8 @@ WebIDL::UnsignedLong StylePropertyMapReadOnly::size() const
// "the name and computed value of every longhand CSS property supported by the User Agent, every
// registered custom property, and every non-registered custom property which is not set to its initial
// value on this"
// Ensure style is computed on the element before we try to read it.
element.document().update_style();
auto longhands_count = to_underlying(last_longhand_property_id) - to_underlying(first_longhand_property_id) + 1;
// Some custom properties set on the element might also be in the registered custom properties set, so we
@ -155,4 +165,35 @@ WebIDL::UnsignedLong StylePropertyMapReadOnly::size() const
[](GC::Ref<CSSStyleDeclaration> const& declaration) { return declaration->length(); });
}
RefPtr<StyleValue const> StylePropertyMapReadOnly::get_style_value(Source& source, String property)
{
return source.visit(
[&property](DOM::AbstractElement& element) -> RefPtr<StyleValue const> {
// From https://drafts.css-houdini.org/css-typed-om-1/#dom-element-computedstylemap we need to include:
// "the name and computed value of every longhand CSS property supported by the User Agent, every
// registered custom property, and every non-registered custom property which is not set to its initial
// value on this"
auto property_id = property_id_from_string(property);
if (!property_id.has_value())
return nullptr;
// Ensure style is computed on the element before we try to read it.
element.document().update_style();
if (property_id == PropertyID::Custom) {
if (auto custom_property = element.get_custom_property(property))
return custom_property;
if (auto registered_custom_property = element.document().registered_custom_properties().get(property); registered_custom_property.has_value())
return registered_custom_property.value()->initial_style_value();
return nullptr;
}
// FIXME: This will only ever be null for pseudo-elements. What should we do in that case?
// The property's value is also sometimes null. Is that correct?
if (auto computed_properties = element.computed_properties())
return computed_properties->maybe_null_property(property_id.value());
return nullptr;
},
[&property](GC::Ref<CSSStyleDeclaration>& declaration) {
return declaration->get_property_style_value(property);
});
}
}

View file

@ -35,6 +35,8 @@ protected:
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override;
static RefPtr<StyleValue const> get_style_value(Source&, String property);
// https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymapreadonly-declarations-slot
// A StylePropertyMapReadOnly object has a [[declarations]] internal slot, which is a map reflecting the CSS
// declaration blocks declarations.

View file

@ -2,5 +2,5 @@ Harness status: OK
Found 1 tests
1 Fail
Fail CSSUrlImageValue serializes correctly
1 Pass
Pass CSSUrlImageValue serializes correctly

View file

@ -2,8 +2,9 @@ Harness status: OK
Found 4 tests
4 Fail
Fail CSSStyleValue from specified CSSOM serializes correctly
2 Pass
2 Fail
Pass CSSStyleValue from specified CSSOM serializes correctly
Fail CSSStyleValue from computed CSSOM serializes correctly
Fail Shorthand CSSStyleValue from inline CSSOM serializes correctly
Pass Shorthand CSSStyleValue from inline CSSOM serializes correctly
Fail Shorthand CSSStyleValue from computed CSSOM serializes correctly

View file

@ -2,12 +2,11 @@ Harness status: OK
Found 7 tests
6 Pass
1 Fail
7 Pass
Pass Calling StylePropertyMap.has with an unsupported property throws a TypeError
Pass Calling StylePropertyMap.has with a custom property not in the property model returns false
Pass Calling StylePropertyMap.has with a valid property returns true
Pass Calling StylePropertyMap.has with a valid property in mixed case returns true
Pass Calling StylePropertyMap.has with a valid shorthand property returns true
Fail Calling StylePropertyMap.has with a valid custom property returns true
Pass Calling StylePropertyMap.has with a valid custom property returns true
Pass Calling StylePropertyMap.has with a valid list-valued property returns true

View file

@ -2,7 +2,6 @@ Harness status: OK
Found 2 tests
1 Pass
1 Fail
Fail Getting a shorthand property set explicitly in css rule returns a base CSSStyleValue
2 Pass
Pass Getting a shorthand property set explicitly in css rule returns a base CSSStyleValue
Pass Getting a shorthand property that is partially set in css rule returns undefined

View file

@ -2,7 +2,6 @@ Harness status: OK
Found 2 tests
1 Pass
1 Fail
Fail StylePropertyMap.getAll() with a shorthand property set explicitly in css rule returns a base CSSStyleValue
2 Pass
Pass StylePropertyMap.getAll() with a shorthand property set explicitly in css rule returns a base CSSStyleValue
Pass StylePropertyMap.getAll() with a shorthand property that is partially in css rule returns empty list

View file

@ -2,8 +2,7 @@ Harness status: OK
Found 3 tests
2 Pass
1 Fail
Fail Getting an shorthand property set explicitly in inline style returns a base CSSStyleValue
3 Pass
Pass Getting an shorthand property set explicitly in inline style returns a base CSSStyleValue
Pass Getting a shorthand property that is partially set in inline style returns null
Pass Getting an attributeStyleMap shorthand property from an element without a style attribute

View file

@ -2,7 +2,6 @@ Harness status: OK
Found 2 tests
1 Pass
1 Fail
Fail StylePropertyMap.getAll() with a shorthand property set explicitly in inline style returns a base CSSStyleValue
2 Pass
Pass StylePropertyMap.getAll() with a shorthand property set explicitly in inline style returns a base CSSStyleValue
Pass StylePropertyMap.getAll() with a shorthand property that is partially in inline style returns empty list