diff --git a/Libraries/LibWeb/HTML/HTMLElement.cpp b/Libraries/LibWeb/HTML/HTMLElement.cpp
index adc04fa015b..95974a4f6e9 100644
--- a/Libraries/LibWeb/HTML/HTMLElement.cpp
+++ b/Libraries/LibWeb/HTML/HTMLElement.cpp
@@ -467,7 +467,7 @@ GC::Ptr HTMLElement::offset_parent() const
const_cast(document()).update_layout(DOM::UpdateLayoutReason::HTMLElementOffsetParent);
// 1. If any of the following holds true return null and terminate this algorithm:
- // - The element does not have an associated CSS layout box.
+ // - The element does not have an associated box.
// - The element is the root element.
// - The element is the HTML body element.
// - The element’s computed value of the position property is fixed.
@@ -475,31 +475,47 @@ GC::Ptr HTMLElement::offset_parent() const
return nullptr;
if (is_document_element())
return nullptr;
- if (is(*this))
+ if (is_html_body_element())
return nullptr;
if (layout_node()->is_fixed_position())
return nullptr;
- // 2. Return the nearest ancestor element of the element for which at least one of the following is true
- // and terminate this algorithm if such an ancestor is found:
- // - The computed value of the position property is not static.
- // - It is the HTML body element.
- // - The computed value of the position property of the element is static
- // and the ancestor is one of the following HTML elements: td, th, or table.
+ // 2. Let ancestor be the parent of the element in the flat tree and repeat these substeps:
+ auto ancestor = shadow_including_first_ancestor_of_type();
+ while (true) {
+ bool ancestor_is_closed_shadow_hidden = ancestor->is_closed_shadow_hidden_from(*this);
+ // 1. If ancestor is closed-shadow-hidden from the element and its computed value of the position property is
+ // fixed, terminate this algorithm and return null.
+ if (ancestor_is_closed_shadow_hidden && ancestor->computed_properties()->position() == CSS::Positioning::Fixed)
+ return nullptr;
- for (auto ancestor = parent_element(); ancestor; ancestor = ancestor->parent_element()) {
- if (!ancestor->layout_node())
- continue;
- if (ancestor->layout_node()->is_positioned())
- return const_cast(ancestor.ptr());
- if (is(*ancestor))
- return const_cast(ancestor.ptr());
- if (!ancestor->layout_node()->is_positioned() && ancestor->local_name().is_one_of(HTML::TagNames::td, HTML::TagNames::th, HTML::TagNames::table))
- return const_cast(ancestor.ptr());
+ // 2. If ancestor is not closed-shadow-hidden from the element and satisfies at least one of the following,
+ // terminate this algorithm and return ancestor.
+ if (!ancestor_is_closed_shadow_hidden) {
+ // - ancestor is a containing block of absolutely-positioned descendants (regardless of whether there are
+ // any absolutely-positioned descendants).
+ if (ancestor->layout_node()->is_positioned())
+ return const_cast(ancestor);
+
+ // - FIXME: The element has a different effective zoom than ancestor.
+
+ // - It is the body element.
+ if (ancestor->is_html_body_element())
+ return const_cast(ancestor);
+
+ // - The computed value of the position property of the element is static and the ancestor is one of the following HTML elements: td, th, or table.
+ if (computed_properties()->position() == CSS::Positioning::Static && ancestor->local_name().is_one_of(HTML::TagNames::td, HTML::TagNames::th, HTML::TagNames::table))
+ return const_cast(ancestor);
+ }
+
+ // 3. If there is no more parent of ancestor in the flat tree, terminate this algorithm and return null.
+ auto parent_of_ancestor = ancestor->shadow_including_first_ancestor_of_type();
+ if (!parent_of_ancestor)
+ return nullptr;
+
+ // 4. Let ancestor be the parent of ancestor in the flat tree.
+ ancestor = parent_of_ancestor;
}
-
- // 3. Return null.
- return nullptr;
}
// https://www.w3.org/TR/cssom-view-1/#dom-htmlelement-offsettop