LibWeb: Add basic implementation of @page

This doesn't support selectors, and the only descriptors for now are for
margins.
This commit is contained in:
Sam Atkins 2025-05-13 12:17:41 +01:00
commit aaf07ae30d
Notes: github-actions[bot] 2025-05-15 08:54:53 +00:00
27 changed files with 471 additions and 20 deletions

View file

@ -84,6 +84,8 @@ set(SOURCES
CSS/CSSNamespaceRule.cpp CSS/CSSNamespaceRule.cpp
CSS/CSSNestedDeclarations.cpp CSS/CSSNestedDeclarations.cpp
CSS/CSSNumericType.cpp CSS/CSSNumericType.cpp
CSS/CSSPageRule.cpp
CSS/CSSPageDescriptors.cpp
CSS/CSSPropertyRule.cpp CSS/CSSPropertyRule.cpp
CSS/CSSRule.cpp CSS/CSSRule.cpp
CSS/CSSRuleList.cpp CSS/CSSRuleList.cpp

View file

@ -0,0 +1,122 @@
/*
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/CSSPageDescriptorsPrototype.h>
#include <LibWeb/CSS/CSSPageDescriptors.h>
namespace Web::CSS {
GC_DEFINE_ALLOCATOR(CSSPageDescriptors);
GC::Ref<CSSPageDescriptors> CSSPageDescriptors::create(JS::Realm& realm, Vector<Descriptor> descriptors)
{
return realm.create<CSSPageDescriptors>(realm, move(descriptors));
}
CSSPageDescriptors::CSSPageDescriptors(JS::Realm& realm, Vector<Descriptor> descriptors)
: CSSDescriptors(realm, AtRuleID::Page, move(descriptors))
{
}
CSSPageDescriptors::~CSSPageDescriptors() = default;
void CSSPageDescriptors::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSPageDescriptors);
Base::initialize(realm);
}
WebIDL::ExceptionOr<void> CSSPageDescriptors::set_margin(StringView value)
{
return set_property("margin"sv, value, ""sv);
}
String CSSPageDescriptors::margin() const
{
return get_property_value("margin"sv);
}
WebIDL::ExceptionOr<void> CSSPageDescriptors::set_margin_top(StringView value)
{
return set_property("margin-top"sv, value, ""sv);
}
String CSSPageDescriptors::margin_top() const
{
return get_property_value("margin-top"sv);
}
WebIDL::ExceptionOr<void> CSSPageDescriptors::set_margin_right(StringView value)
{
return set_property("margin-right"sv, value, ""sv);
}
String CSSPageDescriptors::margin_right() const
{
return get_property_value("margin-right"sv);
}
WebIDL::ExceptionOr<void> CSSPageDescriptors::set_margin_bottom(StringView value)
{
return set_property("margin-bottom"sv, value, ""sv);
}
String CSSPageDescriptors::margin_bottom() const
{
return get_property_value("margin-bottom"sv);
}
WebIDL::ExceptionOr<void> CSSPageDescriptors::set_margin_left(StringView value)
{
return set_property("margin-left"sv, value, ""sv);
}
String CSSPageDescriptors::margin_left() const
{
return get_property_value("margin-left"sv);
}
WebIDL::ExceptionOr<void> CSSPageDescriptors::set_size(StringView value)
{
return set_property("size"sv, value, ""sv);
}
String CSSPageDescriptors::size() const
{
return get_property_value("size"sv);
}
WebIDL::ExceptionOr<void> CSSPageDescriptors::set_page_orientation(StringView value)
{
return set_property("page-orientation"sv, value, ""sv);
}
String CSSPageDescriptors::page_orientation() const
{
return get_property_value("page-orientation"sv);
}
WebIDL::ExceptionOr<void> CSSPageDescriptors::set_marks(StringView value)
{
return set_property("marks"sv, value, ""sv);
}
String CSSPageDescriptors::marks() const
{
return get_property_value("marks"sv);
}
WebIDL::ExceptionOr<void> CSSPageDescriptors::set_bleed(StringView value)
{
return set_property("bleed"sv, value, ""sv);
}
String CSSPageDescriptors::bleed() const
{
return get_property_value("bleed"sv);
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/CSS/CSSDescriptors.h>
namespace Web::CSS {
// https://drafts.csswg.org/cssom/#csspagedescriptors
class CSSPageDescriptors final : public CSSDescriptors {
WEB_PLATFORM_OBJECT(CSSPageDescriptors, CSSDescriptors);
GC_DECLARE_ALLOCATOR(CSSPageDescriptors);
public:
[[nodiscard]] static GC::Ref<CSSPageDescriptors> create(JS::Realm&, Vector<Descriptor>);
virtual ~CSSPageDescriptors() override;
virtual void initialize(JS::Realm&) override;
WebIDL::ExceptionOr<void> set_margin(StringView value);
String margin() const;
WebIDL::ExceptionOr<void> set_margin_top(StringView value);
String margin_top() const;
WebIDL::ExceptionOr<void> set_margin_right(StringView value);
String margin_right() const;
WebIDL::ExceptionOr<void> set_margin_bottom(StringView value);
String margin_bottom() const;
WebIDL::ExceptionOr<void> set_margin_left(StringView value);
String margin_left() const;
WebIDL::ExceptionOr<void> set_size(StringView value);
String size() const;
WebIDL::ExceptionOr<void> set_page_orientation(StringView value);
String page_orientation() const;
WebIDL::ExceptionOr<void> set_marks(StringView value);
String marks() const;
WebIDL::ExceptionOr<void> set_bleed(StringView value);
String bleed() const;
private:
CSSPageDescriptors(JS::Realm&, Vector<Descriptor>);
};
}

View file

@ -0,0 +1,20 @@
#import <CSS/CSSStyleDeclaration.idl>
// https://drafts.csswg.org/cssom/#csspagedescriptors
[Exposed=Window]
interface CSSPageDescriptors : CSSStyleDeclaration {
[LegacyNullToEmptyString] attribute CSSOMString margin;
[LegacyNullToEmptyString, AttributeCallbackName=margin_top_regular, ImplementedAs=margin_top] attribute CSSOMString marginTop;
[LegacyNullToEmptyString, AttributeCallbackName=margin_right_regular, ImplementedAs=margin_right] attribute CSSOMString marginRight;
[LegacyNullToEmptyString, AttributeCallbackName=margin_bottom_regular, ImplementedAs=margin_bottom] attribute CSSOMString marginBottom;
[LegacyNullToEmptyString, AttributeCallbackName=margin_left_regular, ImplementedAs=margin_left] attribute CSSOMString marginLeft;
[LegacyNullToEmptyString, AttributeCallbackName=margin_top_dashed, ImplementedAs=margin_top] attribute CSSOMString margin-top;
[LegacyNullToEmptyString, AttributeCallbackName=margin_right_dashed, ImplementedAs=margin_right] attribute CSSOMString margin-right;
[LegacyNullToEmptyString, AttributeCallbackName=margin_bottom_dashed, ImplementedAs=margin_bottom] attribute CSSOMString margin-bottom;
[LegacyNullToEmptyString, AttributeCallbackName=margin_left_dashed, ImplementedAs=margin_left] attribute CSSOMString margin-left;
[LegacyNullToEmptyString] attribute CSSOMString size;
[LegacyNullToEmptyString, AttributeCallbackName=page_orientation_regular, ImplementedAs=page_orientation] attribute CSSOMString pageOrientation;
[LegacyNullToEmptyString, AttributeCallbackName=page_orientation_dashed, ImplementedAs=page_orientation] attribute CSSOMString page-orientation;
[LegacyNullToEmptyString] attribute CSSOMString marks;
[LegacyNullToEmptyString] attribute CSSOMString bleed;
};

View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/CSSPageRulePrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSPageRule.h>
#include <LibWeb/CSS/DescriptorID.h>
#include <LibWeb/CSS/Serialize.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::CSS {
GC_DEFINE_ALLOCATOR(CSSPageRule);
GC::Ref<CSSPageRule> CSSPageRule::create(JS::Realm& realm, SelectorList&& selectors, GC::Ref<CSSPageDescriptors> style, CSSRuleList& rules)
{
return realm.create<CSSPageRule>(realm, move(selectors), style, rules);
}
CSSPageRule::CSSPageRule(JS::Realm& realm, SelectorList&& selectors, GC::Ref<CSSPageDescriptors> style, CSSRuleList& rules)
: CSSGroupingRule(realm, rules, Type::Page)
, m_selectors(move(selectors))
, m_style(style)
{
m_style->set_parent_rule(*this);
}
void CSSPageRule::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSPageRule);
Base::initialize(realm);
}
// https://drafts.csswg.org/cssom/#dom-csspagerule-selectortext
String CSSPageRule::selector_text() const
{
// The selectorText attribute, on getting, must return the result of serializing the associated selector list.
return serialize_a_group_of_selectors(m_selectors);
}
// https://drafts.csswg.org/cssom/#dom-csspagerule-selectortext
void CSSPageRule::set_selector_text(StringView)
{
// FIXME: On setting the selectorText attribute these steps must be run:
// 1. Run the parse a list of CSS page selectors algorithm on the given value.
// 2. If the algorithm returns a non-null value replace the associated selector list with the returned value.
// 3. Otherwise, if the algorithm returns a null value, do nothing.
}
// https://drafts.csswg.org/cssom/#ref-for-csspagerule
String CSSPageRule::serialized() const
{
auto& descriptors = *m_style;
StringBuilder builder;
// AD-HOC: There's no spec for this yet.
builder.append("@page "sv);
if (auto selector = selector_text(); !selector.is_empty())
builder.appendff("{} ", selector);
builder.append("{ "sv);
if (descriptors.length() > 0) {
builder.append(descriptors.serialized());
builder.append(' ');
}
builder.append("}"sv);
return builder.to_string_without_validation();
}
void CSSPageRule::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_style);
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/CSS/CSSGroupingRule.h>
#include <LibWeb/CSS/CSSPageDescriptors.h>
#include <LibWeb/CSS/Selector.h>
namespace Web::CSS {
// https://drafts.csswg.org/css-page-3/#at-ruledef-page
class CSSPageRule final : public CSSGroupingRule {
WEB_PLATFORM_OBJECT(CSSPageRule, CSSGroupingRule);
GC_DECLARE_ALLOCATOR(CSSPageRule);
public:
[[nodiscard]] static GC::Ref<CSSPageRule> create(JS::Realm&, SelectorList&&, GC::Ref<CSSPageDescriptors>, CSSRuleList&);
virtual ~CSSPageRule() override = default;
String selector_text() const;
void set_selector_text(StringView);
GC::Ref<CSSPageDescriptors> style() { return m_style; }
GC::Ref<CSSPageDescriptors const> descriptors() const { return m_style; }
private:
CSSPageRule(JS::Realm&, SelectorList&&, GC::Ref<CSSPageDescriptors>, CSSRuleList&);
virtual void initialize(JS::Realm&) override;
virtual String serialized() const override;
virtual void visit_edges(Visitor&) override;
SelectorList m_selectors;
GC::Ref<CSSPageDescriptors> m_style;
};
}

View file

@ -0,0 +1,9 @@
#import <CSS/CSSGroupingRule.idl>
#import <CSS/CSSPageDescriptors.idl>
// https://drafts.csswg.org/cssom/#csspagerule
[Exposed=Window]
interface CSSPageRule : CSSGroupingRule {
attribute CSSOMString selectorText;
[SameObject, PutForwards=cssText] readonly attribute CSSPageDescriptors style;
};

View file

@ -94,6 +94,7 @@ FlyString const& CSSRule::parent_layer_internal_qualified_name_slow_case() const
case Type::Supports: case Type::Supports:
case Type::NestedDeclarations: case Type::NestedDeclarations:
case Type::Property: case Type::Property:
case Type::Page:
break; break;
} }
} }

View file

@ -28,6 +28,7 @@ public:
Import = 3, Import = 3,
Media = 4, Media = 4,
FontFace = 5, FontFace = 5,
Page = 6,
Keyframes = 7, Keyframes = 7,
Keyframe = 8, Keyframe = 8,
Namespace = 10, Namespace = 10,

View file

@ -164,6 +164,7 @@ void CSSRuleList::for_each_effective_rule(TraversalOrder order, Function<void(We
case CSSRule::Type::Namespace: case CSSRule::Type::Namespace:
case CSSRule::Type::NestedDeclarations: case CSSRule::Type::NestedDeclarations:
case CSSRule::Type::Property: case CSSRule::Type::Property:
case CSSRule::Type::Page:
break; break;
} }
@ -219,6 +220,7 @@ bool CSSRuleList::evaluate_media_queries(HTML::Window const& window)
case CSSRule::Type::Namespace: case CSSRule::Type::Namespace:
case CSSRule::Type::NestedDeclarations: case CSSRule::Type::NestedDeclarations:
case CSSRule::Type::Property: case CSSRule::Type::Property:
case CSSRule::Type::Page:
break; break;
} }
} }

View file

@ -104,6 +104,42 @@
} }
} }
}, },
"page": {
"spec": "https://drafts.csswg.org/css-page-3/#at-page-rule",
"FIXME": "There are a lot more properties that are valid, see https://drafts.csswg.org/css-page-3/#properties-list",
"descriptors": {
"margin": {
"initial": "0",
"syntax": [
"<'margin'>"
]
},
"margin-bottom": {
"initial": "0",
"syntax": [
"<'margin-bottom'>"
]
},
"margin-left": {
"initial": "0",
"syntax": [
"<'margin-left'>"
]
},
"margin-right": {
"initial": "0",
"syntax": [
"<'margin-right'>"
]
},
"margin-top": {
"initial": "0",
"syntax": [
"<'margin-top'>"
]
}
}
},
"property": { "property": {
"spec": "https://drafts.css-houdini.org/css-properties-values-api/#at-property-rule", "spec": "https://drafts.css-houdini.org/css-properties-values-api/#at-property-rule",
"descriptors": { "descriptors": {

View file

@ -1375,6 +1375,8 @@ Vector<Descriptor> Parser::parse_as_descriptor_declaration_block(AtRuleID at_rul
switch (at_rule_id) { switch (at_rule_id) {
case AtRuleID::FontFace: case AtRuleID::FontFace:
return RuleContext::AtFontFace; return RuleContext::AtFontFace;
case AtRuleID::Page:
return RuleContext::AtPage;
case AtRuleID::Property: case AtRuleID::Property:
return RuleContext::AtProperty; return RuleContext::AtProperty;
} }
@ -1434,8 +1436,9 @@ bool Parser::is_valid_in_the_current_context(Declaration const&) const
return m_rule_context.contains_slow(RuleContext::Style); return m_rule_context.contains_slow(RuleContext::Style);
case RuleContext::AtFontFace: case RuleContext::AtFontFace:
case RuleContext::AtPage:
case RuleContext::AtProperty: case RuleContext::AtProperty:
// @font-face and @property have descriptor declarations // @font-face, @page, and @property have descriptor declarations
return true; return true;
case RuleContext::AtKeyframes: case RuleContext::AtKeyframes:
@ -1482,8 +1485,10 @@ bool Parser::is_valid_in_the_current_context(AtRule const& at_rule) const
case RuleContext::AtFontFace: case RuleContext::AtFontFace:
case RuleContext::AtKeyframes: case RuleContext::AtKeyframes:
case RuleContext::Keyframe: case RuleContext::Keyframe:
case RuleContext::AtPage:
case RuleContext::AtProperty: case RuleContext::AtProperty:
// These can't contain any at-rules // These can't contain any at-rules
// FIXME: Eventually @page can contain margin-box at-rules: https://drafts.csswg.org/css-page-3/#margin-at-rules
return false; return false;
} }
@ -1522,6 +1527,7 @@ bool Parser::is_valid_in_the_current_context(QualifiedRule const&) const
return false; return false;
case RuleContext::AtFontFace: case RuleContext::AtFontFace:
case RuleContext::AtPage:
case RuleContext::AtProperty: case RuleContext::AtProperty:
case RuleContext::Keyframe: case RuleContext::Keyframe:
// These can't contain qualified rules // These can't contain qualified rules

View file

@ -245,8 +245,9 @@ private:
GC::Ptr<CSSRule> convert_to_layer_rule(AtRule const&, Nested); GC::Ptr<CSSRule> convert_to_layer_rule(AtRule const&, Nested);
GC::Ptr<CSSMediaRule> convert_to_media_rule(AtRule const&, Nested); GC::Ptr<CSSMediaRule> convert_to_media_rule(AtRule const&, Nested);
GC::Ptr<CSSNamespaceRule> convert_to_namespace_rule(AtRule const&); GC::Ptr<CSSNamespaceRule> convert_to_namespace_rule(AtRule const&);
GC::Ptr<CSSSupportsRule> convert_to_supports_rule(AtRule const&, Nested); GC::Ptr<CSSPageRule> convert_to_page_rule(AtRule const& rule);
GC::Ptr<CSSPropertyRule> convert_to_property_rule(AtRule const& rule); GC::Ptr<CSSPropertyRule> convert_to_property_rule(AtRule const& rule);
GC::Ptr<CSSSupportsRule> convert_to_supports_rule(AtRule const&, Nested);
GC::Ref<CSSStyleProperties> convert_to_style_declaration(Vector<Declaration> const&); GC::Ref<CSSStyleProperties> convert_to_style_declaration(Vector<Declaration> const&);
Optional<StyleProperty> convert_to_style_property(Declaration const&); Optional<StyleProperty> convert_to_style_property(Declaration const&);

View file

@ -27,6 +27,8 @@ RuleContext rule_context_type_for_rule(CSSRule::Type rule_type)
return RuleContext::AtLayer; return RuleContext::AtLayer;
case CSSRule::Type::NestedDeclarations: case CSSRule::Type::NestedDeclarations:
return RuleContext::Style; return RuleContext::Style;
case CSSRule::Type::Page:
return RuleContext::AtPage;
case CSSRule::Type::Property: case CSSRule::Type::Property:
return RuleContext::AtProperty; return RuleContext::AtProperty;
// Other types shouldn't be trying to create a context. // Other types shouldn't be trying to create a context.
@ -52,6 +54,8 @@ RuleContext rule_context_type_for_at_rule(FlyString const& name)
return RuleContext::AtLayer; return RuleContext::AtLayer;
if (name == "property") if (name == "property")
return RuleContext::AtProperty; return RuleContext::AtProperty;
if (name == "page")
return RuleContext::AtPage;
return RuleContext::Unknown; return RuleContext::Unknown;
} }

View file

@ -22,6 +22,7 @@ enum class RuleContext : u8 {
SupportsCondition, SupportsCondition,
AtLayer, AtLayer,
AtProperty, AtProperty,
AtPage,
}; };
RuleContext rule_context_type_for_rule(CSSRule::Type); RuleContext rule_context_type_for_rule(CSSRule::Type);
RuleContext rule_context_type_for_at_rule(FlyString const&); RuleContext rule_context_type_for_at_rule(FlyString const&);

View file

@ -21,6 +21,7 @@
#include <LibWeb/CSS/CSSMediaRule.h> #include <LibWeb/CSS/CSSMediaRule.h>
#include <LibWeb/CSS/CSSNamespaceRule.h> #include <LibWeb/CSS/CSSNamespaceRule.h>
#include <LibWeb/CSS/CSSNestedDeclarations.h> #include <LibWeb/CSS/CSSNestedDeclarations.h>
#include <LibWeb/CSS/CSSPageRule.h>
#include <LibWeb/CSS/CSSPropertyRule.h> #include <LibWeb/CSS/CSSPropertyRule.h>
#include <LibWeb/CSS/CSSStyleProperties.h> #include <LibWeb/CSS/CSSStyleProperties.h>
#include <LibWeb/CSS/CSSStyleRule.h> #include <LibWeb/CSS/CSSStyleRule.h>
@ -64,12 +65,15 @@ GC::Ptr<CSSRule> Parser::convert_to_rule(Rule const& rule, Nested nested)
if (at_rule.name.equals_ignoring_ascii_case("namespace"sv)) if (at_rule.name.equals_ignoring_ascii_case("namespace"sv))
return convert_to_namespace_rule(at_rule); return convert_to_namespace_rule(at_rule);
if (at_rule.name.equals_ignoring_ascii_case("supports"sv)) if (at_rule.name.equals_ignoring_ascii_case("page"sv))
return convert_to_supports_rule(at_rule, nested); return convert_to_page_rule(at_rule);
if (at_rule.name.equals_ignoring_ascii_case("property"sv)) if (at_rule.name.equals_ignoring_ascii_case("property"sv))
return convert_to_property_rule(at_rule); return convert_to_property_rule(at_rule);
if (at_rule.name.equals_ignoring_ascii_case("supports"sv))
return convert_to_supports_rule(at_rule, nested);
// FIXME: More at rules! // FIXME: More at rules!
dbgln_if(CSS_PARSER_DEBUG, "Unrecognized CSS at-rule: @{}", at_rule.name); dbgln_if(CSS_PARSER_DEBUG, "Unrecognized CSS at-rule: @{}", at_rule.name);
return {}; return {};
@ -596,4 +600,43 @@ GC::Ptr<CSSFontFaceRule> Parser::convert_to_font_face_rule(AtRule const& rule)
return CSSFontFaceRule::create(realm(), CSSFontFaceDescriptors::create(realm(), move(descriptors))); return CSSFontFaceRule::create(realm(), CSSFontFaceDescriptors::create(realm(), move(descriptors)));
} }
GC::Ptr<CSSPageRule> Parser::convert_to_page_rule(AtRule const& rule)
{
// https://drafts.csswg.org/css-page-3/#syntax-page-selector
// @page = @page <page-selector-list>? { <declaration-rule-list> }
// <page-selector-list> = <page-selector>#
// <page-selector> = [ <ident-token>? <pseudo-page>* ]!
// <pseudo-page> = : [ left | right | first | blank ]
SelectorList page_selectors;
// FIXME: Parse page selectors
if (rule.prelude.find_first_index_if([](ComponentValue const& it) { return !it.is(Token::Type::Whitespace); }).has_value()) {
dbgln("@page prelude wasn't empty!");
return nullptr;
}
GC::RootVector<GC::Ref<CSSRule>> child_rules { realm().heap() };
Vector<Descriptor> descriptors;
HashTable<DescriptorID> seen_descriptor_ids;
rule.for_each_as_declaration_rule_list(
[&](auto& at_rule) {
// FIXME: Parse margin rules here.
(void)at_rule;
},
[&](auto& declaration) {
if (auto descriptor = convert_to_descriptor(AtRuleID::Page, declaration); descriptor.has_value()) {
if (seen_descriptor_ids.contains(descriptor->descriptor_id)) {
descriptors.remove_first_matching([&descriptor](Descriptor const& existing) {
return existing.descriptor_id == descriptor->descriptor_id;
});
} else {
seen_descriptor_ids.set(descriptor->descriptor_id);
}
descriptors.append(descriptor.release_value());
}
});
auto rule_list = CSSRuleList::create(realm(), child_rules);
return CSSPageRule::create(realm(), move(page_selectors), CSSPageDescriptors::create(realm(), move(descriptors)), rule_list);
}
} }

View file

@ -3027,8 +3027,9 @@ void StyleComputer::build_qualified_layer_names_cache()
case CSSRule::Type::Keyframe: case CSSRule::Type::Keyframe:
case CSSRule::Type::Namespace: case CSSRule::Type::Namespace:
case CSSRule::Type::NestedDeclarations: case CSSRule::Type::NestedDeclarations:
case CSSRule::Type::Supports: case CSSRule::Type::Page:
case CSSRule::Type::Property: case CSSRule::Type::Property:
case CSSRule::Type::Supports:
break; break;
} }
}); });

View file

@ -19,6 +19,7 @@
#include <LibWeb/CSS/CSSMediaRule.h> #include <LibWeb/CSS/CSSMediaRule.h>
#include <LibWeb/CSS/CSSNamespaceRule.h> #include <LibWeb/CSS/CSSNamespaceRule.h>
#include <LibWeb/CSS/CSSNestedDeclarations.h> #include <LibWeb/CSS/CSSNestedDeclarations.h>
#include <LibWeb/CSS/CSSPageRule.h>
#include <LibWeb/CSS/CSSPropertyRule.h> #include <LibWeb/CSS/CSSPropertyRule.h>
#include <LibWeb/CSS/CSSRule.h> #include <LibWeb/CSS/CSSRule.h>
#include <LibWeb/CSS/CSSStyleProperties.h> #include <LibWeb/CSS/CSSStyleProperties.h>
@ -695,15 +696,18 @@ void dump_rule(StringBuilder& builder, CSS::CSSRule const& rule, int indent_leve
case CSS::CSSRule::Type::NestedDeclarations: case CSS::CSSRule::Type::NestedDeclarations:
dump_nested_declarations(builder, as<CSS::CSSNestedDeclarations const>(rule), indent_levels); dump_nested_declarations(builder, as<CSS::CSSNestedDeclarations const>(rule), indent_levels);
break; break;
case CSS::CSSRule::Type::Page:
dump_page_rule(builder, as<CSS::CSSPageRule const>(rule), indent_levels);
break;
case CSS::CSSRule::Type::Property:
dump_property_rule(builder, as<CSS::CSSPropertyRule const>(rule), indent_levels);
break;
case CSS::CSSRule::Type::Style: case CSS::CSSRule::Type::Style:
dump_style_rule(builder, as<CSS::CSSStyleRule const>(rule), indent_levels); dump_style_rule(builder, as<CSS::CSSStyleRule const>(rule), indent_levels);
break; break;
case CSS::CSSRule::Type::Supports: case CSS::CSSRule::Type::Supports:
dump_supports_rule(builder, as<CSS::CSSSupportsRule const>(rule), indent_levels); dump_supports_rule(builder, as<CSS::CSSSupportsRule const>(rule), indent_levels);
break; break;
case CSS::CSSRule::Type::Property:
dump_property_rule(builder, as<CSS::CSSPropertyRule const>(rule), indent_levels);
break;
} }
} }
@ -765,6 +769,13 @@ void dump_media_rule(StringBuilder& builder, CSS::CSSMediaRule const& media, int
dump_rule(builder, rule, indent_levels + 2); dump_rule(builder, rule, indent_levels + 2);
} }
void dump_page_rule(StringBuilder& builder, CSS::CSSPageRule const& page, int indent_levels)
{
indent(builder, indent_levels + 1);
builder.appendff("selector: {}\n", page.selector_text());
dump_descriptors(builder, page.descriptors(), indent_levels + 1);
}
void dump_supports_rule(StringBuilder& builder, CSS::CSSSupportsRule const& supports, int indent_levels) void dump_supports_rule(StringBuilder& builder, CSS::CSSSupportsRule const& supports, int indent_levels)
{ {
indent(builder, indent_levels); indent(builder, indent_levels);

View file

@ -30,6 +30,7 @@ void dump_import_rule(StringBuilder&, CSS::CSSImportRule const&, int indent_leve
void dump_keyframe_rule(StringBuilder&, CSS::CSSKeyframeRule const&, int indent_levels = 0); void dump_keyframe_rule(StringBuilder&, CSS::CSSKeyframeRule const&, int indent_levels = 0);
void dump_keyframes_rule(StringBuilder&, CSS::CSSKeyframesRule const&, int indent_levels = 0); void dump_keyframes_rule(StringBuilder&, CSS::CSSKeyframesRule const&, int indent_levels = 0);
void dump_media_rule(StringBuilder&, CSS::CSSMediaRule const&, int indent_levels = 0); void dump_media_rule(StringBuilder&, CSS::CSSMediaRule const&, int indent_levels = 0);
void dump_page_rule(StringBuilder&, CSS::CSSPageRule const&, int indent_levels = 0);
void dump_style_rule(StringBuilder&, CSS::CSSStyleRule 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_supports_rule(StringBuilder&, CSS::CSSSupportsRule const&, int indent_levels = 0);
void dump_property_rule(StringBuilder&, CSS::CSSPropertyRule const&, int indent_levels = 0); void dump_property_rule(StringBuilder&, CSS::CSSPropertyRule const&, int indent_levels = 0);

View file

@ -205,6 +205,8 @@ class CSSNamespaceRule;
class CSSNestedDeclarations; class CSSNestedDeclarations;
class CSSOKLab; class CSSOKLab;
class CSSOKLCH; class CSSOKLCH;
class CSSPageRule;
class CSSPageDescriptors;
class CSSPropertyRule; class CSSPropertyRule;
class CSSRGB; class CSSRGB;
class CSSRule; class CSSRule;

View file

@ -35,6 +35,8 @@ libweb_js_bindings(CSS/CSSMediaRule)
libweb_js_bindings(CSS/CSS NAMESPACE) libweb_js_bindings(CSS/CSS NAMESPACE)
libweb_js_bindings(CSS/CSSNamespaceRule) libweb_js_bindings(CSS/CSSNamespaceRule)
libweb_js_bindings(CSS/CSSNestedDeclarations) libweb_js_bindings(CSS/CSSNestedDeclarations)
libweb_js_bindings(CSS/CSSPageRule)
libweb_js_bindings(CSS/CSSPageDescriptors)
libweb_js_bindings(CSS/CSSPropertyRule) libweb_js_bindings(CSS/CSSPropertyRule)
libweb_js_bindings(CSS/CSSRule) libweb_js_bindings(CSS/CSSRule)
libweb_js_bindings(CSS/CSSRuleList) libweb_js_bindings(CSS/CSSRuleList)

View file

@ -49,6 +49,8 @@ CSSLayerStatementRule
CSSMediaRule CSSMediaRule
CSSNamespaceRule CSSNamespaceRule
CSSNestedDeclarations CSSNestedDeclarations
CSSPageDescriptors
CSSPageRule
CSSPropertyRule CSSPropertyRule
CSSRule CSSRule
CSSRuleList CSSRuleList

View file

@ -2,8 +2,9 @@ Harness status: OK
Found 4 tests Found 4 tests
4 Fail 1 Pass
3 Fail
Fail There should be 3 @page rules. Fail There should be 3 @page rules.
Fail Rule #0 Pass Rule #0
Fail Rule #1 Fail Rule #1
Fail Rule #2 Fail Rule #2

View file

@ -1,3 +1,7 @@
Harness status: Error Harness status: OK
Found 0 tests Found 2 tests
2 Fail
Fail Add declarations
Fail Remove declarations

View file

@ -2,11 +2,11 @@ Harness status: OK
Found 8 tests Found 8 tests
4 Pass 5 Pass
4 Fail 3 Fail
Pass page-rules-001 Pass page-rules-001
Pass @page , { } should be an invalid rule Pass @page , { } should be an invalid rule
Fail @page { } should be a valid rule Pass @page { } should be a valid rule
Fail @page a { } should be a valid rule Fail @page a { } should be a valid rule
Fail @page page1 { } should be a valid rule Fail @page page1 { } should be a valid rule
Fail @page name1, name2 { } should be a valid rule Fail @page name1, name2 { } should be a valid rule

View file

@ -2,8 +2,9 @@ Harness status: OK
Found 15 tests Found 15 tests
15 Fail 1 Pass
Fail Test setup 14 Fail
Pass Test setup
Fail size: 640px 480px Fail size: 640px 480px
Fail size: 8.5in 11in Fail size: 8.5in 11in
Fail size: 3in 10in Fail size: 3in 10in

View file

@ -2,8 +2,8 @@ Harness status: OK
Found 11 tests Found 11 tests
8 Pass 9 Pass
3 Fail 2 Fail
Pass CSSRule and CSSImportRule types Pass CSSRule and CSSImportRule types
Pass Type of CSSRule#type and constant values Pass Type of CSSRule#type and constant values
Pass Existence and writability of CSSRule attributes Pass Existence and writability of CSSRule attributes
@ -11,7 +11,7 @@ Pass Values of CSSRule attributes
Pass Existence and writability of CSSImportRule attributes Pass Existence and writability of CSSImportRule attributes
Fail Values of CSSImportRule attributes Fail Values of CSSImportRule attributes
Fail CSSImportRule : MediaList mediaText attribute should be updated due to [PutForwards] Fail CSSImportRule : MediaList mediaText attribute should be updated due to [PutForwards]
Fail CSSStyleDeclaration cssText attribute should be updated due to [PutForwards] Pass CSSStyleDeclaration cssText attribute should be updated due to [PutForwards]
Pass StyleSheet : MediaList mediaText attribute should be updated due to [PutForwards] Pass StyleSheet : MediaList mediaText attribute should be updated due to [PutForwards]
Pass Existence and writability of CSSImportRule supportsText attribute Pass Existence and writability of CSSImportRule supportsText attribute
Pass Value of CSSImportRule supportsText attribute Pass Value of CSSImportRule supportsText attribute