mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
Previously, the LibWeb bindings generator would output multiple per interface files like Prototype/Constructor/Namespace/GlobalMixin depending on the contents of that IDL file. This complicates the build system as it means that it does not know what files will be generated without knowledge of the contents of that IDL file. Instead, for each IDL file only generate a single Bindings/<IDLFile>.h and Bindings/<IDLFile>.cpp.
199 lines
9.4 KiB
C++
199 lines
9.4 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/StylePropertyMapReadOnly.h>
|
||
#include <LibWeb/CSS/CSSStyleDeclaration.h>
|
||
#include <LibWeb/CSS/CSSStyleValue.h>
|
||
#include <LibWeb/CSS/ComputedProperties.h>
|
||
#include <LibWeb/CSS/CustomPropertyData.h>
|
||
#include <LibWeb/CSS/PropertyNameAndID.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_name)
|
||
{
|
||
// 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.
|
||
// 2. If property is not a valid CSS property, throw a TypeError.
|
||
auto property = PropertyNameAndID::from_name(property_name);
|
||
if (!property.has_value())
|
||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("'{}' is not a valid CSS property", property_name)) };
|
||
|
||
// 3. Let props be the value of this’s [[declarations]] internal slot.
|
||
auto& props = m_declarations;
|
||
|
||
// 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.value())) {
|
||
auto iterations = property_value->subdivide_into_iterations(property.value());
|
||
return iterations.first()->reify(realm(), property->name());
|
||
}
|
||
|
||
// 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_name)
|
||
{
|
||
// 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.
|
||
// 2. If property is not a valid CSS property, throw a TypeError.
|
||
auto property = PropertyNameAndID::from_name(property_name);
|
||
if (!property.has_value())
|
||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("'{}' is not a valid CSS property", property_name)) };
|
||
|
||
// 3. Let props be the value of this’s [[declarations]] internal slot.
|
||
auto& props = m_declarations;
|
||
|
||
// 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.value())) {
|
||
auto iterations = property_value->subdivide_into_iterations(property.value());
|
||
GC::RootVector<GC::Ref<CSSStyleValue>> results { heap() };
|
||
for (auto const& style_value : iterations)
|
||
results.append(style_value->reify(realm(), property->name()));
|
||
return results;
|
||
}
|
||
|
||
// 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_name)
|
||
{
|
||
// 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.
|
||
// 2. If property is not a valid CSS property, throw a TypeError.
|
||
auto property = PropertyNameAndID::from_name(property_name);
|
||
if (!property.has_value())
|
||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("'{}' is not a valid CSS property", property_name)) };
|
||
|
||
// 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"
|
||
// 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->is_custom_property()) {
|
||
if (element.get_custom_property(property->name()))
|
||
return true;
|
||
return element.document().get_registered_custom_property(property->name()).has_value();
|
||
}
|
||
return true;
|
||
},
|
||
[&property](GC::Ref<CSSStyleDeclaration>& declaration) {
|
||
return declaration->has_property(property.value());
|
||
});
|
||
}
|
||
|
||
// 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"
|
||
// Ensure style is computed on the element before we try to read it.
|
||
element.document().update_style();
|
||
|
||
// 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;
|
||
if (auto data = element.custom_property_data()) {
|
||
data->for_each_property([&](FlyString const& name, CSS::StyleProperty const&) {
|
||
custom_properties.set(name);
|
||
});
|
||
}
|
||
for (auto const& [key, _] : element.document().registered_property_set())
|
||
custom_properties.set(key);
|
||
|
||
return number_of_longhand_properties + custom_properties.size();
|
||
},
|
||
[](GC::Ref<CSSStyleDeclaration> const& declaration) { return declaration->length(); });
|
||
}
|
||
|
||
RefPtr<StyleValue const> StylePropertyMapReadOnly::get_style_value(Source& source, PropertyNameAndID const& 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"
|
||
// Ensure style is computed on the element before we try to read it.
|
||
element.document().update_style();
|
||
if (property.is_custom_property()) {
|
||
if (auto custom_property = element.get_custom_property(property.name()))
|
||
return custom_property;
|
||
if (auto registered_custom_property = element.document().get_registered_custom_property(property.name()); registered_custom_property.has_value())
|
||
return registered_custom_property->initial_value;
|
||
return nullptr;
|
||
}
|
||
|
||
if (property.id() >= first_longhand_property_id && property.id() <= last_longhand_property_id) {
|
||
// FIXME: This will only ever be null for pseudo-elements. What should we do in that case?
|
||
if (auto computed_properties = element.computed_properties())
|
||
return computed_properties->property(property.id());
|
||
}
|
||
|
||
return nullptr;
|
||
},
|
||
[&property](GC::Ref<CSSStyleDeclaration>& declaration) {
|
||
return declaration->get_property_style_value(property);
|
||
});
|
||
}
|
||
|
||
}
|