mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-10-04 23:29:52 +00:00
After looking into this more, the `[[declarations]]` slot does not seem to need to be a literal map of property names and values. Instead, it can just point at the source (an element or style declaration), and then direct all read or write operations to that. This means the `has()` and `delete()` methods actually work now. A few remaining failures in these tests are because of: - StylePropertyMap[ReadOnly]s not being iterable yet - We're not populating an element's custom properties map, which get fixed whenever someone gets around to producing proper computed values of them.
158 lines
7 KiB
C++
158 lines
7 KiB
C++
/*
|
||
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
|
||
*
|
||
* SPDX-License-Identifier: BSD-2-Clause
|
||
*/
|
||
|
||
#include "StylePropertyMapReadOnly.h"
|
||
#include <LibWeb/Bindings/Intrinsics.h>
|
||
#include <LibWeb/Bindings/StylePropertyMapReadOnlyPrototype.h>
|
||
#include <LibWeb/CSS/CSSStyleDeclaration.h>
|
||
#include <LibWeb/CSS/PropertyName.h>
|
||
#include <LibWeb/DOM/Document.h>
|
||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||
|
||
namespace Web::CSS {
|
||
|
||
GC_DEFINE_ALLOCATOR(StylePropertyMapReadOnly);
|
||
|
||
GC::Ref<StylePropertyMapReadOnly> StylePropertyMapReadOnly::create_computed_style(JS::Realm& realm, DOM::AbstractElement element)
|
||
{
|
||
return realm.create<StylePropertyMapReadOnly>(realm, element);
|
||
}
|
||
|
||
StylePropertyMapReadOnly::StylePropertyMapReadOnly(JS::Realm& realm, Source source)
|
||
: Bindings::PlatformObject(realm)
|
||
, m_declarations(move(source))
|
||
{
|
||
}
|
||
|
||
StylePropertyMapReadOnly::~StylePropertyMapReadOnly() = default;
|
||
|
||
void StylePropertyMapReadOnly::initialize(JS::Realm& realm)
|
||
{
|
||
WEB_SET_PROTOTYPE_FOR_INTERFACE(StylePropertyMapReadOnly);
|
||
Base::initialize(realm);
|
||
}
|
||
|
||
void StylePropertyMapReadOnly::visit_edges(Cell::Visitor& visitor)
|
||
{
|
||
Base::visit_edges(visitor);
|
||
|
||
m_declarations.visit(
|
||
[&visitor](DOM::AbstractElement& element) { element.visit(visitor); },
|
||
[&visitor](GC::Ref<CSSStyleDeclaration>& declaration) { visitor.visit(declaration); });
|
||
}
|
||
|
||
// https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymapreadonly-get
|
||
WebIDL::ExceptionOr<Variant<GC::Ref<CSSStyleValue>, Empty>> StylePropertyMapReadOnly::get(String property)
|
||
{
|
||
// The get(property) method, when called on a StylePropertyMapReadOnly this, must perform the following steps:
|
||
|
||
// 1. If property is not a custom property name string, set property to property ASCII lowercased.
|
||
if (!is_a_custom_property_name_string(property))
|
||
property = property.to_ascii_lowercase();
|
||
|
||
// 2. If property is not a valid CSS property, throw a TypeError.
|
||
if (!is_a_valid_css_property(property))
|
||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("'{}' is not a valid CSS property", property)) };
|
||
|
||
// 3. Let props be the value of this’s [[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;
|
||
|
||
// 5. Otherwise, return undefined.
|
||
return Empty {};
|
||
}
|
||
|
||
// https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymapreadonly-getall
|
||
WebIDL::ExceptionOr<Vector<GC::Ref<CSSStyleValue>>> StylePropertyMapReadOnly::get_all(String property)
|
||
{
|
||
// The getAll(property) method, when called on a StylePropertyMap this, must perform the following steps:
|
||
|
||
// 1. If property is not a custom property name string, set property to property ASCII lowercased.
|
||
if (!is_a_custom_property_name_string(property))
|
||
property = property.to_ascii_lowercase();
|
||
|
||
// 2. If property is not a valid CSS property, throw a TypeError.
|
||
if (!is_a_valid_css_property(property))
|
||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("'{}' is not a valid CSS property", property)) };
|
||
|
||
// 3. Let props be the value of this’s [[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;
|
||
|
||
// 5. Otherwise, return an empty list.
|
||
return Vector<GC::Ref<CSSStyleValue>> {};
|
||
}
|
||
|
||
// https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymapreadonly-has
|
||
WebIDL::ExceptionOr<bool> StylePropertyMapReadOnly::has(String property)
|
||
{
|
||
// The has(property) method, when called on a StylePropertyMapReadOnly this, must perform the following steps:
|
||
|
||
// 1. If property is not a custom property name string, set property to property ASCII lowercased.
|
||
if (!is_a_custom_property_name_string(property))
|
||
property = property.to_ascii_lowercase();
|
||
|
||
// 2. If property is not a valid CSS property, throw a TypeError.
|
||
if (!is_a_valid_css_property(property))
|
||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("'{}' is not a valid CSS property", property)) };
|
||
|
||
// 3. Let props be the value of this’s [[declarations]] internal slot.
|
||
auto& props = m_declarations;
|
||
|
||
// 4. If props[property] exists, return true. Otherwise, return false.
|
||
return props.visit(
|
||
[&property](DOM::AbstractElement& element) {
|
||
// 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 false;
|
||
if (property_id == PropertyID::Custom) {
|
||
if (element.get_custom_property(property))
|
||
return true;
|
||
return element.document().registered_custom_properties().contains(property);
|
||
}
|
||
return true;
|
||
},
|
||
[&property](GC::Ref<CSSStyleDeclaration>& declaration) {
|
||
return declaration->has_property(property);
|
||
});
|
||
}
|
||
|
||
// https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymapreadonly-size
|
||
WebIDL::UnsignedLong StylePropertyMapReadOnly::size() const
|
||
{
|
||
// The size attribute, on getting from a StylePropertyMapReadOnly this, must perform the following steps:
|
||
|
||
// 1. Return the size of the value of this’s [[declarations]] internal slot.
|
||
return m_declarations.visit(
|
||
[](DOM::AbstractElement const& element) {
|
||
// 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 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
|
||
// want the size of the union of the two sets.
|
||
HashTable<FlyString> custom_properties;
|
||
for (auto const& key : element.custom_properties().keys())
|
||
custom_properties.set(key);
|
||
for (auto const& [key, _] : element.document().registered_custom_properties())
|
||
custom_properties.set(key);
|
||
|
||
return longhands_count + custom_properties.size();
|
||
},
|
||
[](GC::Ref<CSSStyleDeclaration> const& declaration) { return declaration->length(); });
|
||
}
|
||
|
||
}
|