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