diff --git a/Tests/LibWeb/Text/expected/css/getComputedStyle-pseudo-element.txt b/Tests/LibWeb/Text/expected/css/getComputedStyle-pseudo-element.txt new file mode 100644 index 00000000000..521bb53ad99 --- /dev/null +++ b/Tests/LibWeb/Text/expected/css/getComputedStyle-pseudo-element.txt @@ -0,0 +1,6 @@ +hi #foo: + color: rgb(128, 128, 128) + background-color: rgb(255, 255, 0) +#foo::before: + color: rgb(128, 128, 128) + background-color: rgb(0, 255, 255) diff --git a/Tests/LibWeb/Text/input/css/getComputedStyle-pseudo-element.html b/Tests/LibWeb/Text/input/css/getComputedStyle-pseudo-element.html new file mode 100644 index 00000000000..caf4a9cf1e8 --- /dev/null +++ b/Tests/LibWeb/Text/input/css/getComputedStyle-pseudo-element.html @@ -0,0 +1,32 @@ + + + +
+ diff --git a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp index 23a88763314..e0fe422edb7 100644 --- a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp +++ b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp @@ -46,14 +46,15 @@ namespace Web::CSS { JS_DEFINE_ALLOCATOR(ResolvedCSSStyleDeclaration); -JS::NonnullGCPtr ResolvedCSSStyleDeclaration::create(DOM::Element& element) +JS::NonnullGCPtr ResolvedCSSStyleDeclaration::create(DOM::Element& element, Optional pseudo_element) { - return element.realm().heap().allocate(element.realm(), element); + return element.realm().heap().allocate(element.realm(), element, move(pseudo_element)); } -ResolvedCSSStyleDeclaration::ResolvedCSSStyleDeclaration(DOM::Element& element) +ResolvedCSSStyleDeclaration::ResolvedCSSStyleDeclaration(DOM::Element& element, Optional pseudo_element) : CSSStyleDeclaration(element.realm()) , m_element(element) + , m_pseudo_element(move(pseudo_element)) { } @@ -208,6 +209,12 @@ RefPtr ResolvedCSSStyleDeclaration::style_value_for_property(L return {}; }; + auto get_computed_value = [this](PropertyID property_id) { + if (m_pseudo_element.has_value()) + return m_element->pseudo_element_computed_css_values(m_pseudo_element.value())->property(property_id); + return m_element->computed_css_values()->property(property_id); + }; + // A limited number of properties have special rules for producing their "resolved value". // We also have to manually construct shorthands from their longhands here. // Everything else uses the computed value. @@ -255,7 +262,7 @@ RefPtr ResolvedCSSStyleDeclaration::style_value_for_property(L // -> line-height // The resolved value is normal if the computed value is normal, or the used value otherwise. case PropertyID::LineHeight: { - auto line_height = static_cast(*layout_node.dom_node()).computed_css_values()->property(property_id); + auto line_height = get_computed_value(property_id); if (line_height->is_identifier() && line_height->to_identifier() == ValueID::Normal) return line_height; return LengthStyleValue::create(Length::make_px(layout_node.computed_values().line_height())); @@ -515,7 +522,7 @@ RefPtr ResolvedCSSStyleDeclaration::style_value_for_property(L return nullptr; default: if (!property_is_shorthand(property_id)) - return static_cast(*layout_node.dom_node()).computed_css_values()->property(property_id); + return get_computed_value(property_id); // Handle shorthands in a generic way auto longhand_ids = longhands_for_shorthand(property_id); @@ -534,18 +541,27 @@ Optional ResolvedCSSStyleDeclaration::property(PropertyID propert if (!m_element->is_connected()) return {}; + auto get_layout_node = [&]() { + if (m_pseudo_element.has_value()) + return m_element->get_pseudo_element_node(m_pseudo_element.value()); + return m_element->layout_node(); + }; + + Layout::NodeWithStyle* layout_node = get_layout_node(); + // FIXME: Be smarter about updating layout if there's no layout node. // We may legitimately have no layout node if we're not visible, but this protects against situations // where we're requesting the computed style before layout has happened. - if (!m_element->layout_node() || property_affects_layout(property_id)) { + if (!layout_node || property_affects_layout(property_id)) { const_cast(m_element->document()).update_layout(); + layout_node = get_layout_node(); } else { // FIXME: If we had a way to update style for a single element, this would be a good place to use it. const_cast(m_element->document()).update_style(); } - if (!m_element->layout_node()) { - auto style = m_element->document().style_computer().compute_style(const_cast(*m_element)); + if (!layout_node) { + auto style = m_element->document().style_computer().compute_style(const_cast(*m_element), m_pseudo_element); // FIXME: This is a stopgap until we implement shorthand -> longhand conversion. auto value = style->maybe_null_property(property_id); @@ -559,8 +575,7 @@ Optional ResolvedCSSStyleDeclaration::property(PropertyID propert }; } - auto& layout_node = *m_element->layout_node(); - auto value = style_value_for_property(layout_node, property_id); + auto value = style_value_for_property(*layout_node, property_id); if (!value) return {}; return StyleProperty { diff --git a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.h b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.h index 7dfd76bd989..93e58763fda 100644 --- a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.h +++ b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.h @@ -7,6 +7,7 @@ #pragma once #include +#include namespace Web::CSS { @@ -15,7 +16,7 @@ class ResolvedCSSStyleDeclaration final : public CSSStyleDeclaration { JS_DECLARE_ALLOCATOR(ResolvedCSSStyleDeclaration); public: - [[nodiscard]] static JS::NonnullGCPtr create(DOM::Element&); + [[nodiscard]] static JS::NonnullGCPtr create(DOM::Element&, Optional = {}); virtual ~ResolvedCSSStyleDeclaration() override = default; @@ -32,13 +33,14 @@ public: virtual WebIDL::ExceptionOr set_css_text(StringView) override; private: - explicit ResolvedCSSStyleDeclaration(DOM::Element&); + explicit ResolvedCSSStyleDeclaration(DOM::Element&, Optional); virtual void visit_edges(Cell::Visitor&) override; RefPtr style_value_for_property(Layout::NodeWithStyle const&, PropertyID) const; JS::NonnullGCPtr m_element; + Optional m_pseudo_element; }; } diff --git a/Userland/Libraries/LibWeb/HTML/Window.cpp b/Userland/Libraries/LibWeb/HTML/Window.cpp index 1154069c7eb..0b8292f8fb8 100644 --- a/Userland/Libraries/LibWeb/HTML/Window.cpp +++ b/Userland/Libraries/LibWeb/HTML/Window.cpp @@ -1136,9 +1136,51 @@ Variant, JS::Value> Window::event() const // https://w3c.github.io/csswg-drafts/cssom/#dom-window-getcomputedstyle JS::NonnullGCPtr Window::get_computed_style(DOM::Element& element, Optional const& pseudo_element) const { - // FIXME: Make this fully spec compliant. - (void)pseudo_element; - return heap().allocate(realm(), element); + // 1. Let doc be elt’s node document. + + // 2. Let obj be elt. + Optional obj_pseudo; + + // 3. If pseudoElt is provided, is not the empty string, and starts with a colon, then: + if (pseudo_element.has_value() && pseudo_element.value().starts_with(':')) { + // 1. Parse pseudoElt as a , and let type be the result. + auto type = parse_pseudo_element_selector(CSS::Parser::ParsingContext(associated_document()), pseudo_element.value()); + + // 2. If type is failure, or is an ::slotted() or ::part() pseudo-element, let obj be null. + // FIXME: We can't pass a null element to ResolvedCSSStyleDeclaration + if (!type.has_value()) { + } + // 3. Otherwise let obj be the given pseudo-element of elt. + else { + // TODO: Keep the function arguments of the pseudo-element if there are any. + obj_pseudo = type.value().type(); + } + } + + // AD-HOC: Just return a ResolvedCSSStyleDeclaration because that's what we have for now. + // FIXME: Implement CSSStyleProperties, and then follow the rest of these steps instead. + return heap().allocate(realm(), element, obj_pseudo); + + // 4. Let decls be an empty list of CSS declarations. + + // 5. If obj is not null, and elt is connected, part of the flat tree, and its shadow-including root + // has a browsing context which either doesn’t have a browsing context container, or whose browsing + // context container is being rendered, set decls to a list of all longhand properties that are + // supported CSS properties, in lexicographical order, with the value being the resolved value + // computed for obj using the style rules associated with doc. Additionally, append to decls all + // the custom properties whose computed value for obj is not the guaranteed-invalid value. + + // 6. Return a live CSSStyleProperties object with the following properties: + // computed flag + // Set. + // readonly flag + // Set. + // declarations + // decls. + // parent CSS rule + // Null. + // owner node + // obj. } // https://w3c.github.io/csswg-drafts/cssom-view/#dom-window-matchmedia