1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-08 05:27:14 +09:00

LibWeb/CSS: Move property code from CSSStyleDeclaration to *Properties

CSSStyleDeclaration is a base class that's used by various collections
of style properties or descriptors. This commit moves all
style-property-related code into CSSStyleProperties, where it belongs.

As noted in the previous commit, we also apply the CSSStyleProperties
prototype now.
This commit is contained in:
Sam Atkins 2025-03-18 14:07:08 +00:00
parent 83bb92c4e0
commit a28197669a
Notes: github-actions[bot] 2025-03-19 13:54:14 +00:00
12 changed files with 276 additions and 295 deletions

View file

@ -60,4 +60,4 @@ bool Paintable::is_visible() const
Some properties have special rules for getting the computed value from JS. For these, you will need to add to Some properties have special rules for getting the computed value from JS. For these, you will need to add to
`CSSStyleProperties::style_value_for_computed_property()`. Shorthands that are constructed in an unusual way (as in, not `CSSStyleProperties::style_value_for_computed_property()`. Shorthands that are constructed in an unusual way (as in, not
using `ShorthandStyleValue`) also need handling inside `CSSStyleDeclaration::get_property_internal()`. using `ShorthandStyleValue`) also need handling inside `CSSStyleProperties::get_property_internal()`.

View file

@ -9,12 +9,7 @@
#include <LibWeb/Bindings/ExceptionOrUtils.h> #include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Bindings/Intrinsics.h> #include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSStyleDeclaration.h> #include <LibWeb/CSS/CSSStyleDeclaration.h>
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/CSS/StyleComputer.h> #include <LibWeb/CSS/StyleComputer.h>
#include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
#include <LibWeb/CSS/StyleValues/ImageStyleValue.h>
#include <LibWeb/CSS/StyleValues/ShorthandStyleValue.h>
#include <LibWeb/CSS/StyleValues/StyleValueList.h>
#include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Element.h> #include <LibWeb/DOM/Element.h>
@ -59,230 +54,6 @@ void CSSStyleDeclaration::update_style_attribute()
set_is_updating(false); set_is_updating(false);
} }
static Optional<StyleProperty> style_property_for_sided_shorthand(PropertyID property_id, Optional<StyleProperty> const& top, Optional<StyleProperty> const& right, Optional<StyleProperty> const& bottom, Optional<StyleProperty> const& left)
{
if (!top.has_value() || !right.has_value() || !bottom.has_value() || !left.has_value())
return {};
if (top->important != right->important || top->important != bottom->important || top->important != left->important)
return {};
ValueComparingNonnullRefPtr<CSSStyleValue> const top_value { top->value };
ValueComparingNonnullRefPtr<CSSStyleValue> const right_value { right->value };
ValueComparingNonnullRefPtr<CSSStyleValue> const bottom_value { bottom->value };
ValueComparingNonnullRefPtr<CSSStyleValue> const left_value { left->value };
bool const top_and_bottom_same = top_value == bottom_value;
bool const left_and_right_same = left_value == right_value;
RefPtr<CSSStyleValue const> value;
if (top_and_bottom_same && left_and_right_same && top_value == left_value) {
value = top_value;
} else if (top_and_bottom_same && left_and_right_same) {
value = StyleValueList::create(StyleValueVector { top_value, right_value }, StyleValueList::Separator::Space);
} else if (left_and_right_same) {
value = StyleValueList::create(StyleValueVector { top_value, right_value, bottom_value }, StyleValueList::Separator::Space);
} else {
value = StyleValueList::create(StyleValueVector { top_value, right_value, bottom_value, left_value }, StyleValueList::Separator::Space);
}
return StyleProperty {
.important = top->important,
.property_id = property_id,
.value = value.release_nonnull(),
};
}
// https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue
Optional<StyleProperty> CSSStyleDeclaration::get_property_internal(PropertyID property_id) const
{
// 2. If property is a shorthand property, then follow these substeps:
if (property_is_shorthand(property_id)) {
// AD-HOC: Handle shorthands that require manual construction.
switch (property_id) {
case PropertyID::Border: {
auto width = get_property_internal(PropertyID::BorderWidth);
auto style = get_property_internal(PropertyID::BorderStyle);
auto color = get_property_internal(PropertyID::BorderColor);
// `border` only has a reasonable value if all four sides are the same.
if (!width.has_value() || width->value->is_value_list() || !style.has_value() || style->value->is_value_list() || !color.has_value() || color->value->is_value_list())
return {};
if (width->important != style->important || width->important != color->important)
return {};
return StyleProperty {
.important = width->important,
.property_id = property_id,
.value = ShorthandStyleValue::create(property_id,
{ PropertyID::BorderWidth, PropertyID::BorderStyle, PropertyID::BorderColor },
{ width->value, style->value, color->value })
};
}
case PropertyID::BorderColor: {
auto top = get_property_internal(PropertyID::BorderTopColor);
auto right = get_property_internal(PropertyID::BorderRightColor);
auto bottom = get_property_internal(PropertyID::BorderBottomColor);
auto left = get_property_internal(PropertyID::BorderLeftColor);
return style_property_for_sided_shorthand(property_id, top, right, bottom, left);
}
case PropertyID::BorderStyle: {
auto top = get_property_internal(PropertyID::BorderTopStyle);
auto right = get_property_internal(PropertyID::BorderRightStyle);
auto bottom = get_property_internal(PropertyID::BorderBottomStyle);
auto left = get_property_internal(PropertyID::BorderLeftStyle);
return style_property_for_sided_shorthand(property_id, top, right, bottom, left);
}
case PropertyID::BorderWidth: {
auto top = get_property_internal(PropertyID::BorderTopWidth);
auto right = get_property_internal(PropertyID::BorderRightWidth);
auto bottom = get_property_internal(PropertyID::BorderBottomWidth);
auto left = get_property_internal(PropertyID::BorderLeftWidth);
return style_property_for_sided_shorthand(property_id, top, right, bottom, left);
}
case PropertyID::FontVariant: {
auto ligatures = get_property_internal(PropertyID::FontVariantLigatures);
auto caps = get_property_internal(PropertyID::FontVariantCaps);
auto alternates = get_property_internal(PropertyID::FontVariantAlternates);
auto numeric = get_property_internal(PropertyID::FontVariantNumeric);
auto east_asian = get_property_internal(PropertyID::FontVariantEastAsian);
auto position = get_property_internal(PropertyID::FontVariantPosition);
auto emoji = get_property_internal(PropertyID::FontVariantEmoji);
if (!ligatures.has_value() || !caps.has_value() || !alternates.has_value() || !numeric.has_value() || !east_asian.has_value() || !position.has_value() || !emoji.has_value())
return {};
if (ligatures->important != caps->important || ligatures->important != alternates->important || ligatures->important != numeric->important || ligatures->important != east_asian->important || ligatures->important != position->important || ligatures->important != emoji->important)
return {};
// If ligatures is `none` and any other value isn't `normal`, that's invalid.
if (ligatures->value->to_keyword() == Keyword::None
&& (caps->value->to_keyword() != Keyword::Normal
|| alternates->value->to_keyword() != Keyword::Normal
|| numeric->value->to_keyword() != Keyword::Normal
|| east_asian->value->to_keyword() != Keyword::Normal
|| position->value->to_keyword() != Keyword::Normal
|| emoji->value->to_keyword() != Keyword::Normal)) {
return {};
}
return StyleProperty {
.important = ligatures->important,
.property_id = property_id,
.value = ShorthandStyleValue::create(property_id,
{ PropertyID::FontVariantLigatures, PropertyID::FontVariantCaps, PropertyID::FontVariantAlternates, PropertyID::FontVariantNumeric, PropertyID::FontVariantEastAsian, PropertyID::FontVariantPosition, PropertyID::FontVariantEmoji },
{ ligatures->value, caps->value, alternates->value, numeric->value, east_asian->value, position->value, emoji->value })
};
}
case PropertyID::Margin: {
auto top = get_property_internal(PropertyID::MarginTop);
auto right = get_property_internal(PropertyID::MarginRight);
auto bottom = get_property_internal(PropertyID::MarginBottom);
auto left = get_property_internal(PropertyID::MarginLeft);
return style_property_for_sided_shorthand(property_id, top, right, bottom, left);
}
case PropertyID::Padding: {
auto top = get_property_internal(PropertyID::PaddingTop);
auto right = get_property_internal(PropertyID::PaddingRight);
auto bottom = get_property_internal(PropertyID::PaddingBottom);
auto left = get_property_internal(PropertyID::PaddingLeft);
return style_property_for_sided_shorthand(property_id, top, right, bottom, left);
}
default:
break;
}
// 1. Let list be a new empty array.
Vector<ValueComparingNonnullRefPtr<CSSStyleValue const>> list;
Optional<Important> last_important_flag;
// 2. For each longhand property longhand that property maps to, in canonical order, follow these substeps:
Vector<PropertyID> longhand_ids = longhands_for_shorthand(property_id);
for (auto longhand_property_id : longhand_ids) {
// 1. If longhand is a case-sensitive match for a property name of a CSS declaration in the declarations,
// let declaration be that CSS declaration, or null otherwise.
auto declaration = get_property_internal(longhand_property_id);
// 2. If declaration is null, then return the empty string.
if (!declaration.has_value())
return {};
// 3. Append the declaration to list.
list.append(declaration->value);
if (last_important_flag.has_value() && declaration->important != *last_important_flag)
return {};
last_important_flag = declaration->important;
}
// 3. If important flags of all declarations in list are same, then return the serialization of list.
// NOTE: Currently we implement property-specific shorthand serialization in ShorthandStyleValue::to_string().
return StyleProperty {
.important = last_important_flag.value(),
.property_id = property_id,
.value = ShorthandStyleValue::create(property_id, longhand_ids, list),
};
// 4. Return the empty string.
// NOTE: This is handled by the loop.
}
return property(property_id);
}
// https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue
String CSSStyleDeclaration::get_property_value(StringView property_name) const
{
auto property_id = property_id_from_string(property_name);
if (!property_id.has_value())
return {};
if (property_id.value() == PropertyID::Custom) {
auto maybe_custom_property = custom_property(FlyString::from_utf8_without_validation(property_name.bytes()));
if (maybe_custom_property.has_value()) {
return maybe_custom_property.value().value->to_string(
is_computed() ? CSSStyleValue::SerializationMode::ResolvedValue
: CSSStyleValue::SerializationMode::Normal);
}
return {};
}
auto maybe_property = get_property_internal(property_id.value());
if (!maybe_property.has_value())
return {};
return maybe_property->value->to_string(
is_computed() ? CSSStyleValue::SerializationMode::ResolvedValue
: CSSStyleValue::SerializationMode::Normal);
}
// https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertypriority
StringView CSSStyleDeclaration::get_property_priority(StringView property_name) const
{
auto property_id = property_id_from_string(property_name);
if (!property_id.has_value())
return {};
if (property_id.value() == PropertyID::Custom) {
auto maybe_custom_property = custom_property(FlyString::from_utf8_without_validation(property_name.bytes()));
if (!maybe_custom_property.has_value())
return {};
return maybe_custom_property.value().important == Important::Yes ? "important"sv : ""sv;
}
auto maybe_property = property(property_id.value());
if (!maybe_property.has_value())
return {};
return maybe_property->important == Important::Yes ? "important"sv : ""sv;
}
WebIDL::ExceptionOr<void> CSSStyleDeclaration::set_property(PropertyID property_id, StringView css_text, StringView priority)
{
return set_property(string_from_property_id(property_id), css_text, priority);
}
WebIDL::ExceptionOr<String> CSSStyleDeclaration::remove_property(PropertyID property_name)
{
return remove_property(string_from_property_id(property_name));
}
// https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext
String CSSStyleDeclaration::css_text() const String CSSStyleDeclaration::css_text() const
{ {
@ -294,20 +65,6 @@ String CSSStyleDeclaration::css_text() const
return serialized(); return serialized();
} }
// https://drafts.csswg.org/cssom/#dom-cssstyleproperties-cssfloat
String CSSStyleDeclaration::css_float() const
{
// The cssFloat attribute, on getting, must return the result of invoking getPropertyValue() with float as argument.
return get_property_value("float"sv);
}
WebIDL::ExceptionOr<void> CSSStyleDeclaration::set_css_float(StringView value)
{
// On setting, the attribute must invoke setProperty() with float as first argument, as second argument the given value,
// and no third argument. Any exceptions thrown must be re-thrown.
return set_property("float"sv, value, ""sv);
}
Optional<JS::Value> CSSStyleDeclaration::item_value(size_t index) const Optional<JS::Value> CSSStyleDeclaration::item_value(size_t index) const
{ {
auto value = item(index); auto value = item(index);

View file

@ -8,10 +8,8 @@
#pragma once #pragma once
#include <AK/String.h> #include <AK/String.h>
#include <AK/Vector.h>
#include <LibWeb/Bindings/PlatformObject.h> #include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/CSS/CSSStyleValue.h> #include <LibWeb/CSS/CSSStyleValue.h>
#include <LibWeb/CSS/GeneratedCSSStyleProperties.h>
#include <LibWeb/CSS/StyleProperty.h> #include <LibWeb/CSS/StyleProperty.h>
#include <LibWeb/DOM/ElementReference.h> #include <LibWeb/DOM/ElementReference.h>
@ -19,8 +17,7 @@ namespace Web::CSS {
// https://drafts.csswg.org/cssom/#css-declaration-blocks // https://drafts.csswg.org/cssom/#css-declaration-blocks
class CSSStyleDeclaration class CSSStyleDeclaration
: public Bindings::PlatformObject : public Bindings::PlatformObject {
, public Bindings::GeneratedCSSStyleProperties {
WEB_PLATFORM_OBJECT(CSSStyleDeclaration, Bindings::PlatformObject); WEB_PLATFORM_OBJECT(CSSStyleDeclaration, Bindings::PlatformObject);
GC_DECLARE_ALLOCATOR(CSSStyleDeclaration); GC_DECLARE_ALLOCATOR(CSSStyleDeclaration);
@ -31,24 +28,15 @@ public:
virtual size_t length() const = 0; virtual size_t length() const = 0;
virtual String item(size_t index) const = 0; virtual String item(size_t index) const = 0;
virtual Optional<StyleProperty> property(PropertyID) const = 0;
virtual Optional<StyleProperty const&> custom_property(FlyString const& custom_property_name) const = 0;
virtual WebIDL::ExceptionOr<void> set_property(PropertyID, StringView css_text, StringView priority = ""sv);
virtual WebIDL::ExceptionOr<String> remove_property(PropertyID);
virtual WebIDL::ExceptionOr<void> set_property(StringView property_name, StringView css_text, StringView priority) = 0; virtual WebIDL::ExceptionOr<void> set_property(StringView property_name, StringView css_text, StringView priority) = 0;
virtual WebIDL::ExceptionOr<String> remove_property(StringView property_name) = 0; virtual WebIDL::ExceptionOr<String> remove_property(StringView property_name) = 0;
String get_property_value(StringView property) const; virtual String get_property_value(StringView property_name) const = 0;
StringView get_property_priority(StringView property) const; virtual StringView get_property_priority(StringView property_name) const = 0;
String css_text() const; String css_text() const;
virtual WebIDL::ExceptionOr<void> set_css_text(StringView) = 0; virtual WebIDL::ExceptionOr<void> set_css_text(StringView) = 0;
String css_float() const;
WebIDL::ExceptionOr<void> set_css_float(StringView);
virtual String serialized() const = 0; virtual String serialized() const = 0;
// https://drafts.csswg.org/cssom/#cssstyledeclaration-computed-flag // https://drafts.csswg.org/cssom/#cssstyledeclaration-computed-flag
@ -82,14 +70,11 @@ protected:
virtual void visit_edges(Visitor&) override; virtual void visit_edges(Visitor&) override;
virtual CSSStyleDeclaration& generated_style_properties_to_css_style_declaration() override { return *this; }
void update_style_attribute(); void update_style_attribute();
private: private:
// ^PlatformObject // ^PlatformObject
virtual Optional<JS::Value> item_value(size_t index) const override; virtual Optional<JS::Value> item_value(size_t index) const override;
Optional<StyleProperty> get_property_internal(PropertyID) const;
// https://drafts.csswg.org/cssom/#cssstyledeclaration-parent-css-rule // https://drafts.csswg.org/cssom/#cssstyledeclaration-parent-css-rule
GC::Ptr<CSSRule> m_parent_rule { nullptr }; GC::Ptr<CSSRule> m_parent_rule { nullptr };

View file

@ -1,9 +1,6 @@
#import <CSS/GeneratedCSSStyleProperties.idl>
// https://drafts.csswg.org/cssom/#cssstyledeclaration // https://drafts.csswg.org/cssom/#cssstyledeclaration
[Exposed=Window] [Exposed=Window]
interface CSSStyleDeclaration { interface CSSStyleDeclaration {
[CEReactions] attribute CSSOMString cssText; [CEReactions] attribute CSSOMString cssText;
readonly attribute unsigned long length; readonly attribute unsigned long length;
@ -16,9 +13,4 @@ interface CSSStyleDeclaration {
[CEReactions] CSSOMString removeProperty(CSSOMString property); [CEReactions] CSSOMString removeProperty(CSSOMString property);
readonly attribute CSSRule? parentRule; readonly attribute CSSRule? parentRule;
[CEReactions, LegacyNullToEmptyString] attribute CSSOMString cssFloat;
}; };
CSSStyleDeclaration includes GeneratedCSSStyleProperties;

View file

@ -78,8 +78,7 @@ CSSStyleProperties::CSSStyleProperties(JS::Realm& realm, Computed computed, Read
void CSSStyleProperties::initialize(JS::Realm& realm) void CSSStyleProperties::initialize(JS::Realm& realm)
{ {
Base::initialize(realm); Base::initialize(realm);
// Temporarily disabled for a single commit to make the changes cleaner. WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSStyleProperties);
// WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSStyleProperties);
} }
void CSSStyleProperties::visit_edges(Visitor& visitor) void CSSStyleProperties::visit_edges(Visitor& visitor)
@ -278,6 +277,11 @@ WebIDL::ExceptionOr<void> CSSStyleProperties::set_property(StringView property_n
return {}; return {};
} }
WebIDL::ExceptionOr<void> CSSStyleProperties::set_property(PropertyID property_id, StringView css_text, StringView priority)
{
return set_property(string_from_property_id(property_id), css_text, priority);
}
static NonnullRefPtr<CSSStyleValue const> style_value_for_length_percentage(LengthPercentage const& length_percentage) static NonnullRefPtr<CSSStyleValue const> style_value_for_length_percentage(LengthPercentage const& length_percentage)
{ {
if (length_percentage.is_auto()) if (length_percentage.is_auto())
@ -374,6 +378,220 @@ static RefPtr<CSSStyleValue const> style_value_for_shadow(Vector<ShadowData> con
return StyleValueList::create(move(style_values), StyleValueList::Separator::Comma); return StyleValueList::create(move(style_values), StyleValueList::Separator::Comma);
} }
// https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue
String CSSStyleProperties::get_property_value(StringView property_name) const
{
auto property_id = property_id_from_string(property_name);
if (!property_id.has_value())
return {};
if (property_id.value() == PropertyID::Custom) {
auto maybe_custom_property = custom_property(FlyString::from_utf8_without_validation(property_name.bytes()));
if (maybe_custom_property.has_value()) {
return maybe_custom_property.value().value->to_string(
is_computed() ? CSSStyleValue::SerializationMode::ResolvedValue
: CSSStyleValue::SerializationMode::Normal);
}
return {};
}
auto maybe_property = get_property_internal(property_id.value());
if (!maybe_property.has_value())
return {};
return maybe_property->value->to_string(
is_computed() ? CSSStyleValue::SerializationMode::ResolvedValue
: CSSStyleValue::SerializationMode::Normal);
}
// https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertypriority
StringView CSSStyleProperties::get_property_priority(StringView property_name) const
{
auto property_id = property_id_from_string(property_name);
if (!property_id.has_value())
return {};
if (property_id.value() == PropertyID::Custom) {
auto maybe_custom_property = custom_property(FlyString::from_utf8_without_validation(property_name.bytes()));
if (!maybe_custom_property.has_value())
return {};
return maybe_custom_property.value().important == Important::Yes ? "important"sv : ""sv;
}
auto maybe_property = property(property_id.value());
if (!maybe_property.has_value())
return {};
return maybe_property->important == Important::Yes ? "important"sv : ""sv;
}
static Optional<StyleProperty> style_property_for_sided_shorthand(PropertyID property_id, Optional<StyleProperty> const& top, Optional<StyleProperty> const& right, Optional<StyleProperty> const& bottom, Optional<StyleProperty> const& left)
{
if (!top.has_value() || !right.has_value() || !bottom.has_value() || !left.has_value())
return {};
if (top->important != right->important || top->important != bottom->important || top->important != left->important)
return {};
ValueComparingNonnullRefPtr<CSSStyleValue> const top_value { top->value };
ValueComparingNonnullRefPtr<CSSStyleValue> const right_value { right->value };
ValueComparingNonnullRefPtr<CSSStyleValue> const bottom_value { bottom->value };
ValueComparingNonnullRefPtr<CSSStyleValue> const left_value { left->value };
bool const top_and_bottom_same = top_value == bottom_value;
bool const left_and_right_same = left_value == right_value;
RefPtr<CSSStyleValue const> value;
if (top_and_bottom_same && left_and_right_same && top_value == left_value) {
value = top_value;
} else if (top_and_bottom_same && left_and_right_same) {
value = StyleValueList::create(StyleValueVector { top_value, right_value }, StyleValueList::Separator::Space);
} else if (left_and_right_same) {
value = StyleValueList::create(StyleValueVector { top_value, right_value, bottom_value }, StyleValueList::Separator::Space);
} else {
value = StyleValueList::create(StyleValueVector { top_value, right_value, bottom_value, left_value }, StyleValueList::Separator::Space);
}
return StyleProperty {
.important = top->important,
.property_id = property_id,
.value = value.release_nonnull(),
};
}
// https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue
Optional<StyleProperty> CSSStyleProperties::get_property_internal(PropertyID property_id) const
{
// 2. If property is a shorthand property, then follow these substeps:
if (property_is_shorthand(property_id)) {
// AD-HOC: Handle shorthands that require manual construction.
switch (property_id) {
case PropertyID::Border: {
auto width = get_property_internal(PropertyID::BorderWidth);
auto style = get_property_internal(PropertyID::BorderStyle);
auto color = get_property_internal(PropertyID::BorderColor);
// `border` only has a reasonable value if all four sides are the same.
if (!width.has_value() || width->value->is_value_list() || !style.has_value() || style->value->is_value_list() || !color.has_value() || color->value->is_value_list())
return {};
if (width->important != style->important || width->important != color->important)
return {};
return StyleProperty {
.important = width->important,
.property_id = property_id,
.value = ShorthandStyleValue::create(property_id,
{ PropertyID::BorderWidth, PropertyID::BorderStyle, PropertyID::BorderColor },
{ width->value, style->value, color->value })
};
}
case PropertyID::BorderColor: {
auto top = get_property_internal(PropertyID::BorderTopColor);
auto right = get_property_internal(PropertyID::BorderRightColor);
auto bottom = get_property_internal(PropertyID::BorderBottomColor);
auto left = get_property_internal(PropertyID::BorderLeftColor);
return style_property_for_sided_shorthand(property_id, top, right, bottom, left);
}
case PropertyID::BorderStyle: {
auto top = get_property_internal(PropertyID::BorderTopStyle);
auto right = get_property_internal(PropertyID::BorderRightStyle);
auto bottom = get_property_internal(PropertyID::BorderBottomStyle);
auto left = get_property_internal(PropertyID::BorderLeftStyle);
return style_property_for_sided_shorthand(property_id, top, right, bottom, left);
}
case PropertyID::BorderWidth: {
auto top = get_property_internal(PropertyID::BorderTopWidth);
auto right = get_property_internal(PropertyID::BorderRightWidth);
auto bottom = get_property_internal(PropertyID::BorderBottomWidth);
auto left = get_property_internal(PropertyID::BorderLeftWidth);
return style_property_for_sided_shorthand(property_id, top, right, bottom, left);
}
case PropertyID::FontVariant: {
auto ligatures = get_property_internal(PropertyID::FontVariantLigatures);
auto caps = get_property_internal(PropertyID::FontVariantCaps);
auto alternates = get_property_internal(PropertyID::FontVariantAlternates);
auto numeric = get_property_internal(PropertyID::FontVariantNumeric);
auto east_asian = get_property_internal(PropertyID::FontVariantEastAsian);
auto position = get_property_internal(PropertyID::FontVariantPosition);
auto emoji = get_property_internal(PropertyID::FontVariantEmoji);
if (!ligatures.has_value() || !caps.has_value() || !alternates.has_value() || !numeric.has_value() || !east_asian.has_value() || !position.has_value() || !emoji.has_value())
return {};
if (ligatures->important != caps->important || ligatures->important != alternates->important || ligatures->important != numeric->important || ligatures->important != east_asian->important || ligatures->important != position->important || ligatures->important != emoji->important)
return {};
// If ligatures is `none` and any other value isn't `normal`, that's invalid.
if (ligatures->value->to_keyword() == Keyword::None
&& (caps->value->to_keyword() != Keyword::Normal
|| alternates->value->to_keyword() != Keyword::Normal
|| numeric->value->to_keyword() != Keyword::Normal
|| east_asian->value->to_keyword() != Keyword::Normal
|| position->value->to_keyword() != Keyword::Normal
|| emoji->value->to_keyword() != Keyword::Normal)) {
return {};
}
return StyleProperty {
.important = ligatures->important,
.property_id = property_id,
.value = ShorthandStyleValue::create(property_id,
{ PropertyID::FontVariantLigatures, PropertyID::FontVariantCaps, PropertyID::FontVariantAlternates, PropertyID::FontVariantNumeric, PropertyID::FontVariantEastAsian, PropertyID::FontVariantPosition, PropertyID::FontVariantEmoji },
{ ligatures->value, caps->value, alternates->value, numeric->value, east_asian->value, position->value, emoji->value })
};
}
case PropertyID::Margin: {
auto top = get_property_internal(PropertyID::MarginTop);
auto right = get_property_internal(PropertyID::MarginRight);
auto bottom = get_property_internal(PropertyID::MarginBottom);
auto left = get_property_internal(PropertyID::MarginLeft);
return style_property_for_sided_shorthand(property_id, top, right, bottom, left);
}
case PropertyID::Padding: {
auto top = get_property_internal(PropertyID::PaddingTop);
auto right = get_property_internal(PropertyID::PaddingRight);
auto bottom = get_property_internal(PropertyID::PaddingBottom);
auto left = get_property_internal(PropertyID::PaddingLeft);
return style_property_for_sided_shorthand(property_id, top, right, bottom, left);
}
default:
break;
}
// 1. Let list be a new empty array.
Vector<ValueComparingNonnullRefPtr<CSSStyleValue const>> list;
Optional<Important> last_important_flag;
// 2. For each longhand property longhand that property maps to, in canonical order, follow these substeps:
Vector<PropertyID> longhand_ids = longhands_for_shorthand(property_id);
for (auto longhand_property_id : longhand_ids) {
// 1. If longhand is a case-sensitive match for a property name of a CSS declaration in the declarations,
// let declaration be that CSS declaration, or null otherwise.
auto declaration = get_property_internal(longhand_property_id);
// 2. If declaration is null, then return the empty string.
if (!declaration.has_value())
return {};
// 3. Append the declaration to list.
list.append(declaration->value);
if (last_important_flag.has_value() && declaration->important != *last_important_flag)
return {};
last_important_flag = declaration->important;
}
// 3. If important flags of all declarations in list are same, then return the serialization of list.
// NOTE: Currently we implement property-specific shorthand serialization in ShorthandStyleValue::to_string().
return StyleProperty {
.important = last_important_flag.value(),
.property_id = property_id,
.value = ShorthandStyleValue::create(property_id, longhand_ids, list),
};
// 4. Return the empty string.
// NOTE: This is handled by the loop.
}
return property(property_id);
}
RefPtr<CSSStyleValue const> CSSStyleProperties::style_value_for_computed_property(Layout::NodeWithStyle const& layout_node, PropertyID property_id) const RefPtr<CSSStyleValue const> CSSStyleProperties::style_value_for_computed_property(Layout::NodeWithStyle const& layout_node, PropertyID property_id) const
{ {
auto used_value_for_property = [&layout_node, property_id](Function<CSSPixels(Painting::PaintableBox const&)>&& used_value_getter) -> Optional<CSSPixels> { auto used_value_for_property = [&layout_node, property_id](Function<CSSPixels(Painting::PaintableBox const&)>&& used_value_getter) -> Optional<CSSPixels> {
@ -806,6 +1024,25 @@ WebIDL::ExceptionOr<String> CSSStyleProperties::remove_property(StringView prope
return value; return value;
} }
WebIDL::ExceptionOr<String> CSSStyleProperties::remove_property(PropertyID property_name)
{
return remove_property(string_from_property_id(property_name));
}
// https://drafts.csswg.org/cssom/#dom-cssstyleproperties-cssfloat
String CSSStyleProperties::css_float() const
{
// The cssFloat attribute, on getting, must return the result of invoking getPropertyValue() with float as argument.
return get_property_value("float"sv);
}
WebIDL::ExceptionOr<void> CSSStyleProperties::set_css_float(StringView value)
{
// On setting, the attribute must invoke setProperty() with float as first argument, as second argument the given value,
// and no third argument. Any exceptions thrown must be re-thrown.
return set_property("float"sv, value, ""sv);
}
// https://www.w3.org/TR/cssom/#serialize-a-css-declaration // https://www.w3.org/TR/cssom/#serialize-a-css-declaration
static String serialize_a_css_declaration(CSS::PropertyID property, StringView value, Important important) static String serialize_a_css_declaration(CSS::PropertyID property, StringView value, Important important)
{ {

View file

@ -8,11 +8,14 @@
#pragma once #pragma once
#include <LibWeb/CSS/CSSStyleDeclaration.h> #include <LibWeb/CSS/CSSStyleDeclaration.h>
#include <LibWeb/CSS/GeneratedCSSStyleProperties.h>
namespace Web::CSS { namespace Web::CSS {
// https://drafts.csswg.org/cssom/#cssstyleproperties // https://drafts.csswg.org/cssom/#cssstyleproperties
class CSSStyleProperties : public CSSStyleDeclaration { class CSSStyleProperties
: public CSSStyleDeclaration
, public Bindings::GeneratedCSSStyleProperties {
WEB_PLATFORM_OBJECT(CSSStyleProperties, CSSStyleDeclaration); WEB_PLATFORM_OBJECT(CSSStyleProperties, CSSStyleDeclaration);
GC_DECLARE_ALLOCATOR(CSSStyleProperties); GC_DECLARE_ALLOCATOR(CSSStyleProperties);
@ -28,30 +31,41 @@ public:
virtual size_t length() const override; virtual size_t length() const override;
virtual String item(size_t index) const override; virtual String item(size_t index) const override;
virtual Optional<StyleProperty> property(PropertyID) const override; Optional<StyleProperty> property(PropertyID) const;
virtual Optional<StyleProperty const&> custom_property(FlyString const& custom_property_name) const override; Optional<StyleProperty const&> custom_property(FlyString const& custom_property_name) const;
// Temporary for one commit. WebIDL::ExceptionOr<void> set_property(PropertyID, StringView css_text, StringView priority = ""sv);
using Base::remove_property, Base::set_property; WebIDL::ExceptionOr<String> remove_property(PropertyID);
virtual WebIDL::ExceptionOr<void> set_property(StringView property_name, StringView css_text, StringView priority) override; virtual WebIDL::ExceptionOr<void> set_property(StringView property_name, StringView css_text, StringView priority) override;
virtual WebIDL::ExceptionOr<String> remove_property(StringView property_name) override; virtual WebIDL::ExceptionOr<String> remove_property(StringView property_name) override;
virtual String get_property_value(StringView property_name) const override;
virtual StringView get_property_priority(StringView property_name) const override;
Vector<StyleProperty> const& properties() const { return m_properties; } Vector<StyleProperty> const& properties() const { return m_properties; }
HashMap<FlyString, StyleProperty> const& custom_properties() const { return m_custom_properties; } HashMap<FlyString, StyleProperty> const& custom_properties() const { return m_custom_properties; }
size_t custom_property_count() const { return m_custom_properties.size(); } size_t custom_property_count() const { return m_custom_properties.size(); }
String css_float() const;
WebIDL::ExceptionOr<void> set_css_float(StringView);
virtual String serialized() const final override; virtual String serialized() const final override;
virtual WebIDL::ExceptionOr<void> set_css_text(StringView) override; virtual WebIDL::ExceptionOr<void> set_css_text(StringView) override;
void set_declarations_from_text(StringView); void set_declarations_from_text(StringView);
// ^Bindings::GeneratedCSSStyleProperties
virtual CSSStyleProperties& generated_style_properties_to_css_style_properties() override { return *this; }
private: private:
CSSStyleProperties(JS::Realm&, Computed, Readonly, Vector<StyleProperty> properties, HashMap<FlyString, StyleProperty> custom_properties, Optional<DOM::ElementReference>); CSSStyleProperties(JS::Realm&, Computed, Readonly, Vector<StyleProperty> properties, HashMap<FlyString, StyleProperty> custom_properties, Optional<DOM::ElementReference>);
virtual void visit_edges(Cell::Visitor&) override; virtual void visit_edges(Cell::Visitor&) override;
RefPtr<CSSStyleValue const> style_value_for_computed_property(Layout::NodeWithStyle const&, PropertyID) const; RefPtr<CSSStyleValue const> style_value_for_computed_property(Layout::NodeWithStyle const&, PropertyID) const;
Optional<StyleProperty> get_property_internal(PropertyID) const;
bool set_a_css_declaration(PropertyID, NonnullRefPtr<CSSStyleValue const>, Important); bool set_a_css_declaration(PropertyID, NonnullRefPtr<CSSStyleValue const>, Important);
void empty_the_declarations(); void empty_the_declarations();

View file

@ -1,6 +1,10 @@
#import <CSS/CSSStyleDeclaration.idl> #import <CSS/CSSStyleDeclaration.idl>
#import <CSS/GeneratedCSSStyleProperties.idl>
// https://drafts.csswg.org/cssom/#cssstyleproperties // https://drafts.csswg.org/cssom/#cssstyleproperties
[Exposed=Window] [Exposed=Window]
interface CSSStyleProperties : CSSStyleDeclaration { interface CSSStyleProperties : CSSStyleDeclaration {
[CEReactions, LegacyNullToEmptyString] attribute CSSOMString cssFloat;
}; };
CSSStyleProperties includes GeneratedCSSStyleProperties;

View file

@ -97,8 +97,8 @@ protected:
GeneratedCSSStyleProperties() = default; GeneratedCSSStyleProperties() = default;
virtual ~GeneratedCSSStyleProperties() = default; virtual ~GeneratedCSSStyleProperties() = default;
virtual CSS::CSSStyleDeclaration& generated_style_properties_to_css_style_declaration() = 0; virtual CSS::CSSStyleProperties& generated_style_properties_to_css_style_properties() = 0;
CSS::CSSStyleDeclaration const& generated_style_properties_to_css_style_declaration() const { return const_cast<GeneratedCSSStyleProperties&>(*this).generated_style_properties_to_css_style_declaration(); } CSS::CSSStyleProperties const& generated_style_properties_to_css_style_properties() const { return const_cast<GeneratedCSSStyleProperties&>(*this).generated_style_properties_to_css_style_properties(); }
}; // class GeneratedCSSStyleProperties }; // class GeneratedCSSStyleProperties
} // namespace Web::Bindings } // namespace Web::Bindings
@ -114,7 +114,7 @@ ErrorOr<void> generate_implementation_file(JsonObject& properties, Core::File& f
SourceGenerator generator { builder }; SourceGenerator generator { builder };
generator.append(R"~~~( generator.append(R"~~~(
#include <LibWeb/CSS/CSSStyleDeclaration.h> #include <LibWeb/CSS/CSSStyleProperties.h>
#include <LibWeb/CSS/GeneratedCSSStyleProperties.h> #include <LibWeb/CSS/GeneratedCSSStyleProperties.h>
#include <LibWeb/WebIDL/ExceptionOr.h> #include <LibWeb/WebIDL/ExceptionOr.h>
@ -131,12 +131,12 @@ namespace Web::Bindings {
definition_generator.append(R"~~~( definition_generator.append(R"~~~(
WebIDL::ExceptionOr<void> GeneratedCSSStyleProperties::set_@name:acceptable_cpp@(StringView value) WebIDL::ExceptionOr<void> GeneratedCSSStyleProperties::set_@name:acceptable_cpp@(StringView value)
{ {
return generated_style_properties_to_css_style_declaration().set_property("@name@"sv, value, ""sv); return generated_style_properties_to_css_style_properties().set_property("@name@"sv, value, ""sv);
} }
String GeneratedCSSStyleProperties::@name:acceptable_cpp@() const String GeneratedCSSStyleProperties::@name:acceptable_cpp@() const
{ {
return generated_style_properties_to_css_style_declaration().get_property_value("@name@"sv); return generated_style_properties_to_css_style_properties().get_property_value("@name@"sv);
} }
)~~~"); )~~~");
}); });

View file

@ -1,3 +1,3 @@
[object CSSStyleDeclaration] [object CSSStyleProperties]
[object DOMRectList] [object DOMRectList]
PASS (didn't crash) PASS (didn't crash)

View file

@ -1,7 +1,4 @@
All supported properties and their default values exposed from CSSStyleDeclaration from getComputedStyle: All supported properties and their default values exposed from CSSStyleProperties from getComputedStyle:
'cssText': ''
'length': '232'
'parentRule': 'null'
'cssFloat': 'none' 'cssFloat': 'none'
'WebkitAlignContent': 'normal' 'WebkitAlignContent': 'normal'
'webkitAlignContent': 'normal' 'webkitAlignContent': 'normal'
@ -641,9 +638,4 @@ All supported properties and their default values exposed from CSSStyleDeclarati
'y': '0px' 'y': '0px'
'zIndex': 'auto' 'zIndex': 'auto'
'z-index': 'auto' 'z-index': 'auto'
'getPropertyPriority': 'function getPropertyPriority() { [native code] }' 'constructor': 'function CSSStyleProperties() { [native code] }'
'getPropertyValue': 'function getPropertyValue() { [native code] }'
'removeProperty': 'function removeProperty() { [native code] }'
'item': 'function item() { [native code] }'
'setProperty': 'function setProperty() { [native code] }'
'constructor': 'function CSSStyleDeclaration() { [native code] }'

View file

@ -1,4 +1,4 @@
spanRule: [object CSSStyleRule] ~ span { color: purple; } spanRule: [object CSSStyleRule] ~ span { color: purple; }
spanRule.style: [object CSSStyleDeclaration] ~ span { color: purple; } spanRule.style: [object CSSStyleProperties] ~ span { color: purple; }
spanRule.style.parentRule: [object CSSStyleRule] ~ span { color: purple; } spanRule.style.parentRule: [object CSSStyleRule] ~ span { color: purple; }
spanRule.style.parentRule === spanRule: true spanRule.style.parentRule === spanRule: true

View file

@ -10,7 +10,7 @@
const stylePrototype = Object.getPrototypeOf(defaultStyle); const stylePrototype = Object.getPrototypeOf(defaultStyle);
const supportedProperties = Object.getOwnPropertyNames(stylePrototype); const supportedProperties = Object.getOwnPropertyNames(stylePrototype);
println("All supported properties and their default values exposed from CSSStyleDeclaration from getComputedStyle:"); println("All supported properties and their default values exposed from CSSStyleProperties from getComputedStyle:");
for (const supportedProperty of supportedProperties) { for (const supportedProperty of supportedProperties) {
println(`'${supportedProperty}': '${defaultStyle[supportedProperty]}'`); println(`'${supportedProperty}': '${defaultStyle[supportedProperty]}'`);
} }