From 3db847c64a3e391b3525b54b67b949ac7ae4097e Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Fri, 1 Oct 2021 19:57:45 +0200 Subject: [PATCH] LibWeb: Implement CSSRule and CSSStyleDeclaration serialization There are a handful of FIXME's here, but this seems generally good. Note that CSS *values* don't get serialized in a spec-compliant way since we currently rely on StyleValue::to_string() which is ad-hoc. --- .../Libraries/LibWeb/CSS/CSSGroupingRule.cpp | 6 + .../Libraries/LibWeb/CSS/CSSGroupingRule.h | 2 + .../Libraries/LibWeb/CSS/CSSImportRule.cpp | 23 ++ Userland/Libraries/LibWeb/CSS/CSSImportRule.h | 2 + Userland/Libraries/LibWeb/CSS/CSSRule.cpp | 13 + Userland/Libraries/LibWeb/CSS/CSSRule.h | 6 +- Userland/Libraries/LibWeb/CSS/CSSRule.idl | 2 +- .../LibWeb/CSS/CSSStyleDeclaration.cpp | 79 +++++ .../LibWeb/CSS/CSSStyleDeclaration.h | 7 + .../Libraries/LibWeb/CSS/CSSStyleRule.cpp | 314 +++++++++++++++++- Userland/Libraries/LibWeb/CSS/CSSStyleRule.h | 2 + .../Libraries/LibWeb/CSS/CSSStyleRule.idl | 1 + .../CSS/ResolvedCSSStyleDeclaration.cpp | 11 + .../LibWeb/CSS/ResolvedCSSStyleDeclaration.h | 2 + 14 files changed, 461 insertions(+), 9 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/CSSGroupingRule.cpp b/Userland/Libraries/LibWeb/CSS/CSSGroupingRule.cpp index a0174c4157..56f30ba847 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSGroupingRule.cpp +++ b/Userland/Libraries/LibWeb/CSS/CSSGroupingRule.cpp @@ -30,4 +30,10 @@ void CSSGroupingRule::delete_rule(size_t) TODO(); } +// https://drafts.csswg.org/cssom/#serialize-a-css-rule +String CSSGroupingRule::serialized() const +{ + TODO(); +} + } diff --git a/Userland/Libraries/LibWeb/CSS/CSSGroupingRule.h b/Userland/Libraries/LibWeb/CSS/CSSGroupingRule.h index 2ae2fbf13f..d8c2398caf 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSGroupingRule.h +++ b/Userland/Libraries/LibWeb/CSS/CSSGroupingRule.h @@ -24,6 +24,8 @@ public: size_t insert_rule(StringView const& rule, size_t index = 0); void delete_rule(size_t index); + virtual String serialized() const; + protected: explicit CSSGroupingRule(NonnullRefPtrVector&&); diff --git a/Userland/Libraries/LibWeb/CSS/CSSImportRule.cpp b/Userland/Libraries/LibWeb/CSS/CSSImportRule.cpp index bb7a009425..004bb96e26 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSImportRule.cpp +++ b/Userland/Libraries/LibWeb/CSS/CSSImportRule.cpp @@ -19,4 +19,27 @@ CSSImportRule::~CSSImportRule() { } +// https://drafts.csswg.org/cssom/#serialize-a-css-rule +String CSSImportRule::serialized() const +{ + StringBuilder builder; + // The result of concatenating the following: + + // 1. The string "@import" followed by a single SPACE (U+0020). + builder.append("@import "sv); + + // 2. The result of performing serialize a URL on the rule’s location. + // FIXME: Look into the correctness of this serialization + builder.append("url("sv); + builder.append(m_url.to_string()); + builder.append(')'); + + // FIXME: 3. If the rule’s associated media list is not empty, a single SPACE (U+0020) followed by the result of performing serialize a media query list on the media list. + + // 4. The string ";", i.e., SEMICOLON (U+003B). + builder.append(';'); + + return builder.to_string(); +} + } diff --git a/Userland/Libraries/LibWeb/CSS/CSSImportRule.h b/Userland/Libraries/LibWeb/CSS/CSSImportRule.h index 839555a5da..bff07f2acb 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSImportRule.h +++ b/Userland/Libraries/LibWeb/CSS/CSSImportRule.h @@ -36,6 +36,8 @@ public: private: explicit CSSImportRule(AK::URL); + virtual String serialized() const override; + AK::URL m_url; RefPtr m_style_sheet; }; diff --git a/Userland/Libraries/LibWeb/CSS/CSSRule.cpp b/Userland/Libraries/LibWeb/CSS/CSSRule.cpp index f52db17e13..4665aa5950 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSRule.cpp +++ b/Userland/Libraries/LibWeb/CSS/CSSRule.cpp @@ -12,4 +12,17 @@ CSSRule::~CSSRule() { } +// https://drafts.csswg.org/cssom/#dom-cssrule-csstext +String CSSRule::css_text() const +{ + // The cssText attribute must return a serialization of the CSS rule. + return serialized(); +} + +// https://drafts.csswg.org/cssom/#dom-cssrule-csstext +void CSSRule::set_css_text(StringView) +{ + // On setting the cssText attribute must do nothing. +} + } diff --git a/Userland/Libraries/LibWeb/CSS/CSSRule.h b/Userland/Libraries/LibWeb/CSS/CSSRule.h index 472e022b42..fa818bf806 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSRule.h +++ b/Userland/Libraries/LibWeb/CSS/CSSRule.h @@ -32,10 +32,14 @@ public: virtual StringView class_name() const = 0; virtual Type type() const = 0; + String css_text() const; + void set_css_text(StringView); + template bool fast_is() const = delete; -private: +protected: + virtual String serialized() const = 0; }; } diff --git a/Userland/Libraries/LibWeb/CSS/CSSRule.idl b/Userland/Libraries/LibWeb/CSS/CSSRule.idl index 385eba2a5d..03c5a6d01a 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSRule.idl +++ b/Userland/Libraries/LibWeb/CSS/CSSRule.idl @@ -1,5 +1,5 @@ interface CSSRule { - + attribute CSSOMString cssText; }; diff --git a/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp b/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp index 0635a4c2e3..ad45162c46 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp +++ b/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp @@ -113,4 +113,83 @@ void CSSStyleDeclaration::set_property(StringView property_name, StringView css_ set_property(property_id, css_text); } +String CSSStyleDeclaration::css_text() const +{ + TODO(); + return ""; +} + +void CSSStyleDeclaration::set_css_text(StringView) +{ + TODO(); +} + +// https://drafts.csswg.org/cssom/#serialize-a-css-declaration +static String serialize_a_css_declaration(CSS::PropertyID property, String value, bool important) +{ + StringBuilder builder; + + // 1. Let s be the empty string. + // 2. Append property to s. + builder.append(string_from_property_id(property)); + + // 3. Append ": " (U+003A U+0020) to s. + builder.append(": "sv); + + // 4. Append value to s. + builder.append(value); + + // 5. If the important flag is set, append " !important" (U+0020 U+0021 U+0069 U+006D U+0070 U+006F U+0072 U+0074 U+0061 U+006E U+0074) to s. + if (important) + builder.append(" !important"sv); + + // 6. Append ";" (U+003B) to s. + builder.append(';'); + + // 7. Return s. + return builder.to_string(); +} + +// https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block +String PropertyOwningCSSStyleDeclaration::serialized() const +{ + // 1. Let list be an empty array. + Vector list; + + // 2. Let already serialized be an empty array. + HashTable already_serialized; + + // 3. Declaration loop: For each CSS declaration declaration in declaration block’s declarations, follow these substeps: + for (auto& declaration : m_properties) { + // 1. Let property be declaration’s property name. + auto property = declaration.property_id; + + // 2. If property is in already serialized, continue with the steps labeled declaration loop. + if (already_serialized.contains(property)) + continue; + + // FIXME: 3. If property maps to one or more shorthand properties, let shorthands be an array of those shorthand properties, in preferred order. + + // FIXME: 4. Shorthand loop: For each shorthand in shorthands, follow these substeps: ... + + // 5. Let value be the result of invoking serialize a CSS value of declaration. + auto value = declaration.value->to_string(); + + // 6. Let serialized declaration be the result of invoking serialize a CSS declaration with property name property, value value, + // and the important flag set if declaration has its important flag set. + auto serialized_declaration = serialize_a_css_declaration(property, move(value), declaration.important); + + // 7. Append serialized declaration to list. + list.append(move(serialized_declaration)); + + // 8. Append property to already serialized. + already_serialized.set(property); + } + + // 4. Return list joined with " " (U+0020). + StringBuilder builder; + builder.join(' ', list); + return builder.to_string(); +} + } diff --git a/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.h b/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.h index 37a32c9f6a..92db88971e 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.h +++ b/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.h @@ -38,6 +38,11 @@ public: String get_property_value(StringView property) const; + String css_text() const; + void set_css_text(StringView); + + virtual String serialized() const = 0; + protected: CSSStyleDeclaration() { } }; @@ -63,6 +68,8 @@ public: Optional custom_property(const String& custom_property_name) const { return m_custom_properties.get(custom_property_name); } size_t custom_property_count() const { return m_custom_properties.size(); } + virtual String serialized() const final override; + protected: explicit PropertyOwningCSSStyleDeclaration(Vector, HashMap); diff --git a/Userland/Libraries/LibWeb/CSS/CSSStyleRule.cpp b/Userland/Libraries/LibWeb/CSS/CSSStyleRule.cpp index 2d5bca15e5..ace2f9701c 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSStyleRule.cpp +++ b/Userland/Libraries/LibWeb/CSS/CSSStyleRule.cpp @@ -18,10 +18,316 @@ CSSStyleRule::~CSSStyleRule() { } +// https://drafts.csswg.org/cssom/#dom-cssstylerule-style +CSSStyleDeclaration* CSSStyleRule::style() +{ + return m_declaration; +} + +static StringView to_string(Selector::SimpleSelector::PseudoElement pseudo_element) +{ + switch (pseudo_element) { + case Selector::SimpleSelector::PseudoElement::Before: + return "before"sv; + case Selector::SimpleSelector::PseudoElement::After: + return "after"sv; + case Selector::SimpleSelector::PseudoElement::FirstLine: + return "first-line"sv; + case Selector::SimpleSelector::PseudoElement::FirstLetter: + return "first-letter"sv; + case Selector::SimpleSelector::PseudoElement::None: + break; + } + VERIFY_NOT_REACHED(); +} + +static StringView to_string(Selector::SimpleSelector::PseudoClass::Type pseudo_class) +{ + switch (pseudo_class) { + case Selector::SimpleSelector::PseudoClass::Type::Link: + return "link"sv; + case Selector::SimpleSelector::PseudoClass::Type::Visited: + return "visited"sv; + case Selector::SimpleSelector::PseudoClass::Type::Hover: + return "hover"sv; + case Selector::SimpleSelector::PseudoClass::Type::Focus: + return "focus"sv; + case Selector::SimpleSelector::PseudoClass::Type::FirstChild: + return "first-child"sv; + case Selector::SimpleSelector::PseudoClass::Type::LastChild: + return "last-child"sv; + case Selector::SimpleSelector::PseudoClass::Type::OnlyChild: + return "only-child"sv; + case Selector::SimpleSelector::PseudoClass::Type::Empty: + return "empty"sv; + case Selector::SimpleSelector::PseudoClass::Type::Root: + return "root"sv; + case Selector::SimpleSelector::PseudoClass::Type::FirstOfType: + return "first-of-pseudo_class"sv; + case Selector::SimpleSelector::PseudoClass::Type::LastOfType: + return "last-of-pseudo_class"sv; + case Selector::SimpleSelector::PseudoClass::Type::Disabled: + return "disabled"sv; + case Selector::SimpleSelector::PseudoClass::Type::Enabled: + return "enabled"sv; + case Selector::SimpleSelector::PseudoClass::Type::Checked: + return "checked"sv; + case Selector::SimpleSelector::PseudoClass::Type::Active: + return "active"sv; + case Selector::SimpleSelector::PseudoClass::Type::NthChild: + return "nth-child"sv; + case Selector::SimpleSelector::PseudoClass::Type::NthLastChild: + return "nth-last-child"sv; + case Selector::SimpleSelector::PseudoClass::Type::Not: + return "not"sv; + case Selector::SimpleSelector::PseudoClass::Type::None: + break; + } + VERIFY_NOT_REACHED(); +} + +static String serialize_a_group_of_selectors(NonnullRefPtrVector const&); + +// https://drafts.csswg.org/cssom/#serialize-a-simple-selector +static String serialize_a_simple_selector(Selector::SimpleSelector const& simple_selector) +{ + StringBuilder builder; + switch (simple_selector.type) { + case Selector::SimpleSelector::Type::TagName: + case Selector::SimpleSelector::Type::Universal: + // FIXME: 1. If the namespace prefix maps to a namespace that is not the default namespace and is not the null namespace (not in a namespace) append the serialization of the namespace prefix as an identifier, followed by a "|" (U+007C) to s. + // FIXME: 2. If the namespace prefix maps to a namespace that is the null namespace (not in a namespace) append "|" (U+007C) to s. + // 3. If this is a type selector append the serialization of the element name as an identifier to s. + if (simple_selector.type == Selector::SimpleSelector::Type::TagName) { + // FIXME: Use the "serialize an identifier" algorithm. + builder.append(simple_selector.value); + } + // 4. If this is a universal selector append "*" (U+002A) to s. + if (simple_selector.type == Selector::SimpleSelector::Type::Universal) + builder.append('*'); + break; + case Selector::SimpleSelector::Type::Attribute: + // 1. Append "[" (U+005B) to s. + builder.append('['); + + // FIXME: 2. If the namespace prefix maps to a namespace that is not the null namespace (not in a namespace) append the serialization of the namespace prefix as an identifier, followed by a "|" (U+007C) to s. + + // 3. Append the serialization of the attribute name as an identifier to s. + // FIXME: Use the "serialize an identifier" algorithm. + builder.append(simple_selector.attribute.name); + + // 4. If there is an attribute value specified, append "=", "~=", "|=", "^=", "$=", or "*=" as appropriate (depending on the type of attribute selector), + // followed by the serialization of the attribute value as a string, to s. + if (!simple_selector.attribute.value.is_null()) { + switch (simple_selector.attribute.match_type) { + case Selector::SimpleSelector::Attribute::MatchType::ExactValueMatch: + builder.append("="); + break; + case Selector::SimpleSelector::Attribute::MatchType::ContainsWord: + builder.append("~="); + break; + case Selector::SimpleSelector::Attribute::MatchType::ContainsString: + builder.append("*="); + break; + case Selector::SimpleSelector::Attribute::MatchType::StartsWithSegment: + builder.append("|="); + break; + case Selector::SimpleSelector::Attribute::MatchType::StartsWithString: + builder.append("^="); + break; + case Selector::SimpleSelector::Attribute::MatchType::EndsWithString: + builder.append("$="); + break; + default: + break; + } + } + // FIXME: 5. If the attribute selector has the case-sensitivity flag present, append " i" (U+0020 U+0069) to s. + + // 6. Append "]" (U+005D) to s. + builder.append(']'); + break; + + case Selector::SimpleSelector::Type::Class: + // Append a "." (U+002E), followed by the serialization of the class name as an identifier to s. + builder.append('.'); + // FIXME: Use the "serialize an identifier" algorithm. + builder.append(simple_selector.value); + break; + + case Selector::SimpleSelector::Type::Id: + // Append a "#" (U+0023), followed by the serialization of the ID as an identifier to s. + builder.append('#'); + // FIXME: Use the "serialize an identifier" algorithm. + builder.append(simple_selector.value); + break; + + case Selector::SimpleSelector::Type::PseudoClass: + switch (simple_selector.pseudo_class.type) { + case Selector::SimpleSelector::PseudoClass::Type::Link: + case Selector::SimpleSelector::PseudoClass::Type::Visited: + case Selector::SimpleSelector::PseudoClass::Type::Hover: + case Selector::SimpleSelector::PseudoClass::Type::Focus: + case Selector::SimpleSelector::PseudoClass::Type::FirstChild: + case Selector::SimpleSelector::PseudoClass::Type::LastChild: + case Selector::SimpleSelector::PseudoClass::Type::OnlyChild: + case Selector::SimpleSelector::PseudoClass::Type::Empty: + case Selector::SimpleSelector::PseudoClass::Type::Root: + case Selector::SimpleSelector::PseudoClass::Type::FirstOfType: + case Selector::SimpleSelector::PseudoClass::Type::LastOfType: + case Selector::SimpleSelector::PseudoClass::Type::Disabled: + case Selector::SimpleSelector::PseudoClass::Type::Enabled: + case Selector::SimpleSelector::PseudoClass::Type::Checked: + case Selector::SimpleSelector::PseudoClass::Type::Active: + // If the pseudo-class does not accept arguments append ":" (U+003A), followed by the name of the pseudo-class, to s. + builder.append(':'); + builder.append(to_string(simple_selector.pseudo_class.type)); + break; + case Selector::SimpleSelector::PseudoClass::Type::NthChild: + case Selector::SimpleSelector::PseudoClass::Type::NthLastChild: + case Selector::SimpleSelector::PseudoClass::Type::Not: + // Otherwise, append ":" (U+003A), followed by the name of the pseudo-class, followed by "(" (U+0028), + // followed by the value of the pseudo-class argument(s) determined as per below, followed by ")" (U+0029), to s. + builder.append(':'); + builder.append(to_string(simple_selector.pseudo_class.type)); + builder.append('('); + if (simple_selector.pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::NthChild + || simple_selector.pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::NthLastChild) { + // FIXME: The result of serializing the value using the rules to serialize an value. + TODO(); + } else if (simple_selector.pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::Not) { + // The result of serializing the value using the rules for serializing a group of selectors. + builder.append(serialize_a_group_of_selectors(simple_selector.pseudo_class.not_selector)); + } + builder.append(')'); + break; + default: + VERIFY_NOT_REACHED(); + } + break; + default: + dbgln("FIXME: Unsupported simple selector serialization for type {}", to_underlying(simple_selector.type)); + break; + } + return builder.to_string(); +} + +// https://drafts.csswg.org/cssom/#serialize-a-selector +static String serialize_a_selector(Selector const& selector) +{ + StringBuilder builder; + + // To serialize a selector let s be the empty string, run the steps below for each part of the chain of the selector, and finally return s: + for (size_t i = 0; i < selector.compound_selectors().size(); ++i) { + auto const& compound_selector = selector.compound_selectors()[i]; + // 1. If there is only one simple selector in the compound selectors which is a universal selector, append the result of serializing the universal selector to s. + if (compound_selector.simple_selectors.size() == 1 + && compound_selector.simple_selectors.first().type == Selector::SimpleSelector::Type::Universal) { + builder.append(serialize_a_simple_selector(selector.compound_selectors().first().simple_selectors.first())); + } + // 2. Otherwise, for each simple selector in the compound selectors... + // FIXME: ...that is not a universal selector of which the namespace prefix maps to a namespace that is not the default namespace... + // ...serialize the simple selector and append the result to s. + else { + for (auto& simple_selector : compound_selector.simple_selectors) { + builder.append(serialize_a_simple_selector(simple_selector)); + } + } + + // 3. If this is not the last part of the chain of the selector append a single SPACE (U+0020), + // followed by the combinator ">", "+", "~", ">>", "||", as appropriate, followed by another + // single SPACE (U+0020) if the combinator was not whitespace, to s. + if (i != selector.compound_selectors().size() - 1) { + builder.append(' '); + switch (compound_selector.combinator) { + case Selector::Combinator::ImmediateChild: + builder.append('>'); + break; + case Selector::Combinator::NextSibling: + builder.append('+'); + break; + case Selector::Combinator::SubsequentSibling: + builder.append('~'); + break; + case Selector::Combinator::Column: + builder.append("||"); + break; + default: + break; + } + } else { + // 4. If this is the last part of the chain of the selector and there is a pseudo-element, append "::" followed by the name of the pseudo-element, to s. + // FIXME: This doesn't feel entirely correct. Our model of pseudo-elements seems off. + if (!compound_selector.simple_selectors.is_empty() + && compound_selector.simple_selectors.first().type == Selector::SimpleSelector::Type::PseudoElement) { + builder.append("::"); + builder.append(to_string(compound_selector.simple_selectors.first().pseudo_element)); + } + } + } + + return builder.to_string(); +} + +// https://drafts.csswg.org/cssom/#serialize-a-group-of-selectors +static String serialize_a_group_of_selectors(NonnullRefPtrVector const& selectors) +{ + // To serialize a group of selectors serialize each selector in the group of selectors and then serialize a comma-separated list of these serializations. + StringBuilder builder; + for (auto& selector : selectors) + builder.append(serialize_a_selector(selector)); + return builder.to_string(); +} + +// https://drafts.csswg.org/cssom/#serialize-a-css-rule +String CSSStyleRule::serialized() const +{ + StringBuilder builder; + + // 1. Let s initially be the result of performing serialize a group of selectors on the rule’s associated selectors, + // followed by the string " {", i.e., a single SPACE (U+0020), followed by LEFT CURLY BRACKET (U+007B). + builder.append(serialize_a_group_of_selectors(selectors())); + builder.append(" {"sv); + + // 2. Let decls be the result of performing serialize a CSS declaration block on the rule’s associated declarations, or null if there are no such declarations. + auto decls = declaration().serialized(); + + // FIXME: 3. Let rules be the result of performing serialize a CSS rule on each rule in the rule’s cssRules list, or null if there are no such rules. + String rules; + + // 4. If decls and rules are both null, append " }" to s (i.e. a single SPACE (U+0020) followed by RIGHT CURLY BRACKET (U+007D)) and return s. + if (decls.is_null() && rules.is_null()) { + builder.append(" }"sv); + return builder.to_string(); + } + + // 5. If rules is null: + if (rules.is_null()) { + // 1. Append a single SPACE (U+0020) to s + builder.append(' '); + // 2. Append decls to s + builder.append(decls); + // 3. Append " }" to s (i.e. a single SPACE (U+0020) followed by RIGHT CURLY BRACKET (U+007D)). + builder.append(" }"sv); + // 4. Return s. + return builder.to_string(); + } + + // FIXME: 6. Otherwise: + // FIXME: 1. If decls is not null, prepend it to rules. + // FIXME: 2. For each rule in rules: + // FIXME: 1. Append a newline followed by two spaces to s. + // FIXME: 2. Append rule to s. + // FIXME: 3. Append a newline followed by RIGHT CURLY BRACKET (U+007D) to s. + // FIXME: 4. Return s. + TODO(); +} + // https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext String CSSStyleRule::selector_text() const { - TODO(); + // The selectorText attribute, on getting, must return the result of serializing the associated group of selectors. + return serialized(); } // https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext @@ -37,10 +343,4 @@ void CSSStyleRule::set_selector_text(StringView selector_text) TODO(); } -// https://drafts.csswg.org/cssom/#dom-cssstylerule-style -CSSStyleDeclaration* CSSStyleRule::style() -{ - return m_declaration; -} - } diff --git a/Userland/Libraries/LibWeb/CSS/CSSStyleRule.h b/Userland/Libraries/LibWeb/CSS/CSSStyleRule.h index a8b55060e9..e01a0a9742 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSStyleRule.h +++ b/Userland/Libraries/LibWeb/CSS/CSSStyleRule.h @@ -43,6 +43,8 @@ public: private: CSSStyleRule(NonnullRefPtrVector&&, NonnullRefPtr&&); + virtual String serialized() const override; + NonnullRefPtrVector m_selectors; NonnullRefPtr m_declaration; }; diff --git a/Userland/Libraries/LibWeb/CSS/CSSStyleRule.idl b/Userland/Libraries/LibWeb/CSS/CSSStyleRule.idl index 89016783ad..9bd6f73f32 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSStyleRule.idl +++ b/Userland/Libraries/LibWeb/CSS/CSSStyleRule.idl @@ -1,6 +1,7 @@ interface CSSStyleRule { attribute CSSOMString selectorText; + attribute CSSOMString cssText; [SameObject, PutForwards=cssText] readonly attribute CSSStyleDeclaration style; }; diff --git a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp index 30f79e8df8..1ad7322c97 100644 --- a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp +++ b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp @@ -579,4 +579,15 @@ bool ResolvedCSSStyleDeclaration::set_property(PropertyID, StringView) { return false; } + +String ResolvedCSSStyleDeclaration::serialized() const +{ + // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext + // If the computed flag is set, then return the empty string. + + // NOTE: ResolvedCSSStyleDeclaration is something you would only get from window.getComputedStyle(), + // which returns what the spec calls "resolved style". The "computed flag" is always set here. + return String::empty(); +} + } diff --git a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.h b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.h index 0446160f81..429b23ed5c 100644 --- a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.h +++ b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.h @@ -24,6 +24,8 @@ public: virtual Optional property(PropertyID) const override; virtual bool set_property(PropertyID, StringView css_text) override; + virtual String serialized() const override; + private: explicit ResolvedCSSStyleDeclaration(DOM::Element&);