mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 19:45:12 +00: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:
parent
83bb92c4e0
commit
a28197669a
Notes:
github-actions[bot]
2025-03-19 13:54:14 +00:00
Author: https://github.com/AtkinsSJ Commit: https://github.com/LadybirdBrowser/ladybird/commit/a28197669a9 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3983
12 changed files with 276 additions and 295 deletions
|
@ -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
|
||||
`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()`.
|
||||
|
|
|
@ -9,12 +9,7 @@
|
|||
#include <LibWeb/Bindings/ExceptionOrUtils.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/CSS/CSSStyleDeclaration.h>
|
||||
#include <LibWeb/CSS/Parser/Parser.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/Element.h>
|
||||
|
||||
|
@ -59,230 +54,6 @@ void CSSStyleDeclaration::update_style_attribute()
|
|||
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
|
||||
String CSSStyleDeclaration::css_text() const
|
||||
{
|
||||
|
@ -294,20 +65,6 @@ String CSSStyleDeclaration::css_text() const
|
|||
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
|
||||
{
|
||||
auto value = item(index);
|
||||
|
|
|
@ -8,10 +8,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
#include <LibWeb/CSS/CSSStyleValue.h>
|
||||
#include <LibWeb/CSS/GeneratedCSSStyleProperties.h>
|
||||
#include <LibWeb/CSS/StyleProperty.h>
|
||||
#include <LibWeb/DOM/ElementReference.h>
|
||||
|
||||
|
@ -19,8 +17,7 @@ namespace Web::CSS {
|
|||
|
||||
// https://drafts.csswg.org/cssom/#css-declaration-blocks
|
||||
class CSSStyleDeclaration
|
||||
: public Bindings::PlatformObject
|
||||
, public Bindings::GeneratedCSSStyleProperties {
|
||||
: public Bindings::PlatformObject {
|
||||
WEB_PLATFORM_OBJECT(CSSStyleDeclaration, Bindings::PlatformObject);
|
||||
GC_DECLARE_ALLOCATOR(CSSStyleDeclaration);
|
||||
|
||||
|
@ -31,24 +28,15 @@ public:
|
|||
virtual size_t length() 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<String> remove_property(StringView property_name) = 0;
|
||||
|
||||
String get_property_value(StringView property) const;
|
||||
StringView get_property_priority(StringView property) const;
|
||||
virtual String get_property_value(StringView property_name) const = 0;
|
||||
virtual StringView get_property_priority(StringView property_name) const = 0;
|
||||
|
||||
String css_text() const;
|
||||
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;
|
||||
|
||||
// https://drafts.csswg.org/cssom/#cssstyledeclaration-computed-flag
|
||||
|
@ -82,14 +70,11 @@ protected:
|
|||
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
virtual CSSStyleDeclaration& generated_style_properties_to_css_style_declaration() override { return *this; }
|
||||
|
||||
void update_style_attribute();
|
||||
|
||||
private:
|
||||
// ^PlatformObject
|
||||
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
|
||||
GC::Ptr<CSSRule> m_parent_rule { nullptr };
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
#import <CSS/GeneratedCSSStyleProperties.idl>
|
||||
|
||||
// https://drafts.csswg.org/cssom/#cssstyledeclaration
|
||||
[Exposed=Window]
|
||||
interface CSSStyleDeclaration {
|
||||
|
||||
[CEReactions] attribute CSSOMString cssText;
|
||||
|
||||
readonly attribute unsigned long length;
|
||||
|
@ -16,9 +13,4 @@ interface CSSStyleDeclaration {
|
|||
[CEReactions] CSSOMString removeProperty(CSSOMString property);
|
||||
|
||||
readonly attribute CSSRule? parentRule;
|
||||
|
||||
[CEReactions, LegacyNullToEmptyString] attribute CSSOMString cssFloat;
|
||||
|
||||
};
|
||||
|
||||
CSSStyleDeclaration includes GeneratedCSSStyleProperties;
|
||||
|
|
|
@ -78,8 +78,7 @@ CSSStyleProperties::CSSStyleProperties(JS::Realm& realm, Computed computed, Read
|
|||
void CSSStyleProperties::initialize(JS::Realm& 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)
|
||||
|
@ -278,6 +277,11 @@ WebIDL::ExceptionOr<void> CSSStyleProperties::set_property(StringView property_n
|
|||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
static String serialize_a_css_declaration(CSS::PropertyID property, StringView value, Important important)
|
||||
{
|
||||
|
|
|
@ -8,11 +8,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibWeb/CSS/CSSStyleDeclaration.h>
|
||||
#include <LibWeb/CSS/GeneratedCSSStyleProperties.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
// https://drafts.csswg.org/cssom/#cssstyleproperties
|
||||
class CSSStyleProperties : public CSSStyleDeclaration {
|
||||
class CSSStyleProperties
|
||||
: public CSSStyleDeclaration
|
||||
, public Bindings::GeneratedCSSStyleProperties {
|
||||
WEB_PLATFORM_OBJECT(CSSStyleProperties, CSSStyleDeclaration);
|
||||
GC_DECLARE_ALLOCATOR(CSSStyleProperties);
|
||||
|
||||
|
@ -28,30 +31,41 @@ public:
|
|||
virtual size_t length() const override;
|
||||
virtual String item(size_t index) const override;
|
||||
|
||||
virtual Optional<StyleProperty> property(PropertyID) const override;
|
||||
virtual Optional<StyleProperty const&> custom_property(FlyString const& custom_property_name) const override;
|
||||
Optional<StyleProperty> property(PropertyID) const;
|
||||
Optional<StyleProperty const&> custom_property(FlyString const& custom_property_name) const;
|
||||
|
||||
// Temporary for one commit.
|
||||
using Base::remove_property, Base::set_property;
|
||||
WebIDL::ExceptionOr<void> set_property(PropertyID, StringView css_text, StringView priority = ""sv);
|
||||
WebIDL::ExceptionOr<String> remove_property(PropertyID);
|
||||
|
||||
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 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; }
|
||||
HashMap<FlyString, StyleProperty> const& custom_properties() const { return m_custom_properties; }
|
||||
|
||||
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 WebIDL::ExceptionOr<void> set_css_text(StringView) override;
|
||||
|
||||
void set_declarations_from_text(StringView);
|
||||
|
||||
// ^Bindings::GeneratedCSSStyleProperties
|
||||
virtual CSSStyleProperties& generated_style_properties_to_css_style_properties() override { return *this; }
|
||||
|
||||
private:
|
||||
CSSStyleProperties(JS::Realm&, Computed, Readonly, Vector<StyleProperty> properties, HashMap<FlyString, StyleProperty> custom_properties, Optional<DOM::ElementReference>);
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
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);
|
||||
void empty_the_declarations();
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
#import <CSS/CSSStyleDeclaration.idl>
|
||||
#import <CSS/GeneratedCSSStyleProperties.idl>
|
||||
|
||||
// https://drafts.csswg.org/cssom/#cssstyleproperties
|
||||
[Exposed=Window]
|
||||
interface CSSStyleProperties : CSSStyleDeclaration {
|
||||
[CEReactions, LegacyNullToEmptyString] attribute CSSOMString cssFloat;
|
||||
};
|
||||
|
||||
CSSStyleProperties includes GeneratedCSSStyleProperties;
|
||||
|
|
|
@ -97,8 +97,8 @@ protected:
|
|||
GeneratedCSSStyleProperties() = default;
|
||||
virtual ~GeneratedCSSStyleProperties() = default;
|
||||
|
||||
virtual CSS::CSSStyleDeclaration& generated_style_properties_to_css_style_declaration() = 0;
|
||||
CSS::CSSStyleDeclaration const& generated_style_properties_to_css_style_declaration() const { return const_cast<GeneratedCSSStyleProperties&>(*this).generated_style_properties_to_css_style_declaration(); }
|
||||
virtual CSS::CSSStyleProperties& generated_style_properties_to_css_style_properties() = 0;
|
||||
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
|
||||
|
||||
} // namespace Web::Bindings
|
||||
|
@ -114,7 +114,7 @@ ErrorOr<void> generate_implementation_file(JsonObject& properties, Core::File& f
|
|||
SourceGenerator generator { builder };
|
||||
|
||||
generator.append(R"~~~(
|
||||
#include <LibWeb/CSS/CSSStyleDeclaration.h>
|
||||
#include <LibWeb/CSS/CSSStyleProperties.h>
|
||||
#include <LibWeb/CSS/GeneratedCSSStyleProperties.h>
|
||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||
|
||||
|
@ -131,12 +131,12 @@ namespace Web::Bindings {
|
|||
definition_generator.append(R"~~~(
|
||||
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
|
||||
{
|
||||
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);
|
||||
}
|
||||
)~~~");
|
||||
});
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[object CSSStyleDeclaration]
|
||||
[object CSSStyleProperties]
|
||||
[object DOMRectList]
|
||||
PASS (didn't crash)
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
All supported properties and their default values exposed from CSSStyleDeclaration from getComputedStyle:
|
||||
'cssText': ''
|
||||
'length': '232'
|
||||
'parentRule': 'null'
|
||||
All supported properties and their default values exposed from CSSStyleProperties from getComputedStyle:
|
||||
'cssFloat': 'none'
|
||||
'WebkitAlignContent': 'normal'
|
||||
'webkitAlignContent': 'normal'
|
||||
|
@ -641,9 +638,4 @@ All supported properties and their default values exposed from CSSStyleDeclarati
|
|||
'y': '0px'
|
||||
'zIndex': 'auto'
|
||||
'z-index': 'auto'
|
||||
'getPropertyPriority': 'function getPropertyPriority() { [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] }'
|
||||
'constructor': 'function CSSStyleProperties() { [native code] }'
|
|
@ -1,4 +1,4 @@
|
|||
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 === spanRule: true
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
const stylePrototype = Object.getPrototypeOf(defaultStyle);
|
||||
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) {
|
||||
println(`'${supportedProperty}': '${defaultStyle[supportedProperty]}'`);
|
||||
}
|
Loading…
Add table
Reference in a new issue