diff --git a/Tests/LibWeb/Text/expected/all-window-properties.txt b/Tests/LibWeb/Text/expected/all-window-properties.txt index b7e6bc14f84..f7fc93d2f55 100644 --- a/Tests/LibWeb/Text/expected/all-window-properties.txt +++ b/Tests/LibWeb/Text/expected/all-window-properties.txt @@ -42,6 +42,7 @@ CSSLayerBlockRule CSSLayerStatementRule CSSMediaRule CSSNamespaceRule +CSSNestedDeclarations CSSRule CSSRuleList CSSStyleDeclaration diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 53a4d33b258..2fb44f7ad3e 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -47,6 +47,7 @@ set(SOURCES CSS/CSSLayerBlockRule.cpp CSS/CSSLayerStatementRule.cpp CSS/CSSMediaRule.cpp + CSS/CSSNestedDeclarations.cpp CSS/CSSNumericType.cpp CSS/CSSNamespaceRule.cpp CSS/CSSRule.cpp diff --git a/Userland/Libraries/LibWeb/CSS/CSSNestedDeclarations.cpp b/Userland/Libraries/LibWeb/CSS/CSSNestedDeclarations.cpp new file mode 100644 index 00000000000..c3f61280ed3 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/CSSNestedDeclarations.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "CSSNestedDeclarations.h" +#include +#include + +namespace Web::CSS { + +JS_DEFINE_ALLOCATOR(CSSNestedDeclarations); + +JS::NonnullGCPtr CSSNestedDeclarations::create(JS::Realm& realm, PropertyOwningCSSStyleDeclaration& declaration) +{ + return realm.heap().allocate(realm, realm, declaration); +} + +CSSNestedDeclarations::CSSNestedDeclarations(JS::Realm& realm, PropertyOwningCSSStyleDeclaration& declaration) + : CSSRule(realm) + , m_declaration(declaration) +{ + m_declaration->set_parent_rule(*this); +} + +void CSSNestedDeclarations::initialize(JS::Realm& realm) +{ + Base::initialize(realm); + WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSNestedDeclarations); +} + +void CSSNestedDeclarations::visit_edges(Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_declaration); +} + +CSSStyleDeclaration* CSSNestedDeclarations::style() +{ + return m_declaration; +} + +String CSSNestedDeclarations::serialized() const +{ + // NOTE: There's no proper spec for this yet, only this note: + // "The CSSNestedDeclarations rule serializes as if its declaration block had been serialized directly." + // - https://drafts.csswg.org/css-nesting-1/#ref-for-cssnesteddeclarations%E2%91%A1 + // So, we'll do the simple thing and hope it's good. + return m_declaration->serialized(); +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/CSSNestedDeclarations.h b/Userland/Libraries/LibWeb/CSS/CSSNestedDeclarations.h new file mode 100644 index 00000000000..de800fe2f0b --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/CSSNestedDeclarations.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Web::CSS { + +class CSSNestedDeclarations final : public CSSRule { + WEB_PLATFORM_OBJECT(CSSNestedDeclarations, CSSRule); + JS_DECLARE_ALLOCATOR(CSSNestedDeclarations); + +public: + [[nodiscard]] static JS::NonnullGCPtr create(JS::Realm&, PropertyOwningCSSStyleDeclaration&); + + virtual ~CSSNestedDeclarations() override = default; + + virtual Type type() const override { return Type::NestedDeclarations; } + PropertyOwningCSSStyleDeclaration const& declaration() const { return m_declaration; } + + CSSStyleDeclaration* style(); + +private: + CSSNestedDeclarations(JS::Realm&, PropertyOwningCSSStyleDeclaration&); + + virtual void initialize(JS::Realm&) override; + virtual void visit_edges(Cell::Visitor&) override; + virtual String serialized() const override; + + JS::NonnullGCPtr m_declaration; +}; + +template<> +inline bool CSSRule::fast_is() const { return type() == CSSRule::Type::NestedDeclarations; } + +} diff --git a/Userland/Libraries/LibWeb/CSS/CSSNestedDeclarations.idl b/Userland/Libraries/LibWeb/CSS/CSSNestedDeclarations.idl new file mode 100644 index 00000000000..16cdac54c85 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/CSSNestedDeclarations.idl @@ -0,0 +1,9 @@ +#import +#import + +// https://drafts.csswg.org/css-nesting-1/#cssnesteddeclarations +[Exposed=Window] +interface CSSNestedDeclarations : CSSRule { + // FIXME: Should be a CSSStyleProperties, once we have that + [SameObject, PutForwards=cssText] readonly attribute CSSStyleDeclaration style; +}; diff --git a/Userland/Libraries/LibWeb/CSS/CSSRule.cpp b/Userland/Libraries/LibWeb/CSS/CSSRule.cpp index 95f98e5da87..79872b5b700 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSRule.cpp +++ b/Userland/Libraries/LibWeb/CSS/CSSRule.cpp @@ -53,11 +53,11 @@ FlyString const& CSSRule::parent_layer_internal_qualified_name_slow_case() const Vector layer_names; for (auto* rule = parent_rule(); rule; rule = rule->parent_rule()) { switch (rule->type()) { - case CSSRule::Type::Import: + case Type::Import: // TODO: Handle `layer(foo)` in import rules once we implement that. break; - case CSSRule::Type::LayerBlock: { + case Type::LayerBlock: { auto& layer_block = static_cast(*rule); layer_names.append(layer_block.internal_name()); break; @@ -65,14 +65,15 @@ FlyString const& CSSRule::parent_layer_internal_qualified_name_slow_case() const // Ignore everything else // Note that LayerStatement cannot have child rules so we still ignore it here. - case CSSRule::Type::LayerStatement: - case CSSRule::Type::Style: - case CSSRule::Type::Media: - case CSSRule::Type::FontFace: - case CSSRule::Type::Keyframes: - case CSSRule::Type::Keyframe: - case CSSRule::Type::Namespace: - case CSSRule::Type::Supports: + case Type::LayerStatement: + case Type::Style: + case Type::Media: + case Type::FontFace: + case Type::Keyframes: + case Type::Keyframe: + case Type::Namespace: + case Type::Supports: + case Type::NestedDeclarations: break; } } diff --git a/Userland/Libraries/LibWeb/CSS/CSSRule.h b/Userland/Libraries/LibWeb/CSS/CSSRule.h index 19cb0d72899..28929d41f46 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSRule.h +++ b/Userland/Libraries/LibWeb/CSS/CSSRule.h @@ -34,6 +34,7 @@ public: // AD-HOC: These are not included in the spec, but we need them internally. So, their numbers are arbitrary. LayerBlock = 100, LayerStatement = 101, + NestedDeclarations = 102, }; virtual Type type() const = 0; diff --git a/Userland/Libraries/LibWeb/CSS/CSSRuleList.cpp b/Userland/Libraries/LibWeb/CSS/CSSRuleList.cpp index dc967d9b12c..dc3a92d6e00 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSRuleList.cpp +++ b/Userland/Libraries/LibWeb/CSS/CSSRuleList.cpp @@ -144,6 +144,7 @@ void CSSRuleList::for_each_effective_rule(TraversalOrder order, Function #include #include +#include #include #include #include @@ -641,6 +642,9 @@ void dump_rule(StringBuilder& builder, CSS::CSSRule const& rule, int indent_leve case CSS::CSSRule::Type::Namespace: dump_namespace_rule(builder, verify_cast(rule), indent_levels); break; + case CSS::CSSRule::Type::NestedDeclarations: + dump_nested_declarations(builder, verify_cast(rule), indent_levels); + break; case CSS::CSSRule::Type::Style: dump_style_rule(builder, verify_cast(rule), indent_levels); break; @@ -780,27 +784,32 @@ void dump_supports_rule(StringBuilder& builder, CSS::CSSSupportsRule const& supp dump_rule(builder, rule, indent_levels + 2); } +void dump_declaration(StringBuilder& builder, CSS::PropertyOwningCSSStyleDeclaration const& declaration, int indent_levels) +{ + indent(builder, indent_levels); + builder.appendff("Declarations ({}):\n", declaration.length()); + for (auto& property : declaration.properties()) { + indent(builder, indent_levels); + builder.appendff(" {}: '{}'", CSS::string_from_property_id(property.property_id), property.value->to_string()); + if (property.important == CSS::Important::Yes) + builder.append(" \033[31;1m!important\033[0m"sv); + builder.append('\n'); + } + for (auto& property : declaration.custom_properties()) { + indent(builder, indent_levels); + builder.appendff(" {}: '{}'", property.key, property.value.value->to_string()); + if (property.value.important == CSS::Important::Yes) + builder.append(" \033[31;1m!important\033[0m"sv); + builder.append('\n'); + } +} + void dump_style_rule(StringBuilder& builder, CSS::CSSStyleRule const& rule, int indent_levels) { for (auto& selector : rule.selectors()) { dump_selector(builder, selector, indent_levels + 1); } - indent(builder, indent_levels); - builder.appendff(" Declarations ({}):\n", rule.declaration().length()); - for (auto& property : rule.declaration().properties()) { - indent(builder, indent_levels); - builder.appendff(" {}: '{}'", CSS::string_from_property_id(property.property_id), property.value->to_string()); - if (property.important == CSS::Important::Yes) - builder.append(" \033[31;1m!important\033[0m"sv); - builder.append('\n'); - } - for (auto& property : rule.declaration().custom_properties()) { - indent(builder, indent_levels); - builder.appendff(" {}: '{}'", property.key, property.value.value->to_string()); - if (property.value.important == CSS::Important::Yes) - builder.append(" \033[31;1m!important\033[0m"sv); - builder.append('\n'); - } + dump_declaration(builder, rule.declaration(), indent_levels + 1); } void dump_sheet(CSS::StyleSheet const& sheet) @@ -883,4 +892,11 @@ void dump_namespace_rule(StringBuilder& builder, CSS::CSSNamespaceRule const& na builder.appendff(" Prefix: {}\n", namespace_.prefix()); } +void dump_nested_declarations(StringBuilder& builder, CSS::CSSNestedDeclarations const& declarations, int indent_levels) +{ + indent(builder, indent_levels); + builder.append(" Nested declarations:\n"sv); + dump_declaration(builder, declarations.declaration(), indent_levels + 1); +} + } diff --git a/Userland/Libraries/LibWeb/Dump.h b/Userland/Libraries/LibWeb/Dump.h index a138f86fa54..3fa875261e8 100644 --- a/Userland/Libraries/LibWeb/Dump.h +++ b/Userland/Libraries/LibWeb/Dump.h @@ -24,12 +24,14 @@ void dump_sheet(StringBuilder&, CSS::StyleSheet const&); void dump_sheet(CSS::StyleSheet const&); void dump_rule(StringBuilder&, CSS::CSSRule const&, int indent_levels = 0); void dump_rule(CSS::CSSRule const&); +void dump_declaration(StringBuilder&, CSS::PropertyOwningCSSStyleDeclaration const&, int indent_levels = 0); void dump_font_face_rule(StringBuilder&, CSS::CSSFontFaceRule const&, int indent_levels = 0); void dump_import_rule(StringBuilder&, CSS::CSSImportRule const&, int indent_levels = 0); void dump_media_rule(StringBuilder&, CSS::CSSMediaRule const&, int indent_levels = 0); void dump_style_rule(StringBuilder&, CSS::CSSStyleRule const&, int indent_levels = 0); void dump_supports_rule(StringBuilder&, CSS::CSSSupportsRule const&, int indent_levels = 0); void dump_namespace_rule(StringBuilder&, CSS::CSSNamespaceRule const&, int indent_levels = 0); +void dump_nested_declarations(StringBuilder&, CSS::CSSNestedDeclarations const&, int indent_levels = 0); void dump_layer_block_rule(StringBuilder&, CSS::CSSLayerBlockRule const&, int indent_levels = 0); void dump_layer_statement_rule(StringBuilder&, CSS::CSSLayerStatementRule const&, int indent_levels = 0); void dump_selector(StringBuilder&, CSS::Selector const&, int indent_levels = 0); diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index ab5075a13c2..4c9bc2e0ec4 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -117,6 +117,7 @@ class CSSLayerBlockRule; class CSSLayerStatementRule; class CSSMathValue; class CSSMediaRule; +class CSSNestedDeclarations; class CSSOKLab; class CSSOKLCH; class CSSRGB; diff --git a/Userland/Libraries/LibWeb/idl_files.cmake b/Userland/Libraries/LibWeb/idl_files.cmake index 19f61e53f2a..7e0abf596ff 100644 --- a/Userland/Libraries/LibWeb/idl_files.cmake +++ b/Userland/Libraries/LibWeb/idl_files.cmake @@ -25,6 +25,7 @@ libweb_js_bindings(CSS/CSSLayerStatementRule) libweb_js_bindings(CSS/CSSMediaRule) libweb_js_bindings(CSS/CSS NAMESPACE) libweb_js_bindings(CSS/CSSNamespaceRule) +libweb_js_bindings(CSS/CSSNestedDeclarations) libweb_js_bindings(CSS/CSSRule) libweb_js_bindings(CSS/CSSRuleList) libweb_js_bindings(CSS/CSSStyleDeclaration)