diff --git a/Libraries/LibWeb/HTML/HTMLElement.cpp b/Libraries/LibWeb/HTML/HTMLElement.cpp
index 95974a4f6e9..af9b3c8828d 100644
--- a/Libraries/LibWeb/HTML/HTMLElement.cpp
+++ b/Libraries/LibWeb/HTML/HTMLElement.cpp
@@ -461,6 +461,47 @@ String HTMLElement::outer_text()
return get_the_text_steps();
}
+// https://drafts.csswg.org/cssom-view/#dom-htmlelement-scrollparent
+GC::Ptr HTMLElement::scroll_parent() const
+{
+ // 1. If any of the following holds true, return null and terminate this algorithm:
+ // - The element does not have an associated box.
+ // - The element is the root element.
+ // - The element is the body element.
+ // - FIXME: The element’s computed value of the position property is fixed and no ancestor establishes a fixed position containing block.
+ if (!layout_node())
+ return nullptr;
+ if (is_document_element())
+ return nullptr;
+ if (is_html_body_element())
+ return nullptr;
+
+ // 2. Let ancestor be the containing block of the element in the flat tree and repeat these substeps:
+ auto ancestor = layout_node()->containing_block();
+ while (true) {
+ // 1. If ancestor is the initial containing block, return the scrollingElement for the element’s document if it
+ // is not closed-shadow-hidden from the element, otherwise return null.
+ if (ancestor->is_viewport()) {
+ auto const scrolling_element = document().scrolling_element();
+ if (scrolling_element && !scrolling_element->is_closed_shadow_hidden_from(*this))
+ return const_cast(scrolling_element.ptr());
+ return nullptr;
+ }
+
+ // 2. If ancestor is not closed-shadow-hidden from the element, and is a scroll container, terminate this
+ // algorithm and return ancestor.
+ if (!ancestor->dom_node()->is_closed_shadow_hidden_from(*this) && ancestor->is_scroll_container()) {
+ return const_cast(static_cast(ancestor->dom_node()));
+ }
+
+ // FIXME: 3. If the computed value of the position property of ancestor is fixed, and no ancestor establishes a fixed
+ // position containing block, terminate this algorithm and return null.
+
+ // 4. Let ancestor be the containing block of ancestor in the flat tree.
+ ancestor = layout_node()->containing_block();
+ }
+}
+
// https://www.w3.org/TR/cssom-view-1/#dom-htmlelement-offsetparent
GC::Ptr HTMLElement::offset_parent() const
{
diff --git a/Libraries/LibWeb/HTML/HTMLElement.h b/Libraries/LibWeb/HTML/HTMLElement.h
index bb26b36afd8..405f26faa57 100644
--- a/Libraries/LibWeb/HTML/HTMLElement.h
+++ b/Libraries/LibWeb/HTML/HTMLElement.h
@@ -105,6 +105,7 @@ public:
int offset_width() const;
int offset_height() const;
GC::Ptr offset_parent() const;
+ GC::Ptr scroll_parent() const;
bool cannot_navigate() const;
diff --git a/Libraries/LibWeb/HTML/HTMLElement.idl b/Libraries/LibWeb/HTML/HTMLElement.idl
index 9df48602a0a..f8f6e8d6f20 100644
--- a/Libraries/LibWeb/HTML/HTMLElement.idl
+++ b/Libraries/LibWeb/HTML/HTMLElement.idl
@@ -40,6 +40,7 @@ interface HTMLElement : Element {
[CEReactions] attribute DOMString? popover;
// https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface
+ readonly attribute Element? scrollParent;
readonly attribute Element? offsetParent;
readonly attribute long offsetTop;
readonly attribute long offsetLeft;