diff --git a/Tests/LibWeb/Text/expected/DOM/Elements-from-point.txt b/Tests/LibWeb/Text/expected/DOM/Elements-from-point.txt new file mode 100644 index 0000000000..1c947e52f4 --- /dev/null +++ b/Tests/LibWeb/Text/expected/DOM/Elements-from-point.txt @@ -0,0 +1,13 @@ + Negative coordinates return empty array: true +Coordinates outside the viewport return empty array: true +== Elements at (500, 10) == +
+ +== FIXME: Elements at (550, 60) == +
+
+
+
+
+
+
diff --git a/Tests/LibWeb/Text/input/DOM/Elements-from-point.html b/Tests/LibWeb/Text/input/DOM/Elements-from-point.html
new file mode 100644
index 0000000000..5a9b70960a
--- /dev/null
+++ b/Tests/LibWeb/Text/input/DOM/Elements-from-point.html
@@ -0,0 +1,46 @@
+
+
+
+
+ diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp index 0ca92769e3..3470eba8f5 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.cpp +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -3808,4 +3808,47 @@ Element const* Document::element_from_point(double x, double y) return nullptr; } +// https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint +Vector> Document::elements_from_point(double x, double y) +{ + // 1. Let sequence be a new empty sequence. + Vector> sequence; + + // 2. If either argument is negative, x is greater than the viewport width excluding the size of a rendered scroll bar (if any), + // or y is greater than the viewport height excluding the size of a rendered scroll bar (if any), + // or there is no viewport associated with the document, return sequence and terminate these steps. + auto viewport_rect = this->viewport_rect(); + CSSPixelPoint position { x, y }; + // FIXME: This should account for the size of the scroll bar. + if (x < 0 || y < 0 || position.x() > viewport_rect.width() || position.y() > viewport_rect.height()) + return sequence; + + // Ensure the layout tree exists prior to hit testing. + update_layout(); + + // 3. For each box in the viewport, in paint order, starting with the topmost box, that would be a target for + // hit testing at coordinates x,y even if nothing would be overlapping it, when applying the transforms that + // apply to the descendants of the viewport, append the associated element to sequence. + // FIXME: Paintable box tree order is not the same as paint order. We need a helper to traverse the paint tree in + // paint order with a custom callback. + if (auto const* paintable_box = this->paintable_box(); paintable_box) { + paintable_box->for_each_in_inclusive_subtree_of_type([&](auto& paintable_box) { + if (auto result = paintable_box.hit_test(position, Painting::HitTestType::Exact); result.has_value()) { + if (auto* dom_node = result->dom_node(); dom_node && dom_node->is_element()) + sequence.append(*static_cast(dom_node)); + return Painting::TraversalDecision::Continue; + } + return Painting::TraversalDecision::SkipChildrenAndContinue; + }); + } + + // 4. If the document has a root element, and the last item in sequence is not the root element, + // append the root element to sequence. + if (auto* root_element = document_element(); root_element && (sequence.is_empty() || (sequence.last() != root_element))) + sequence.append(*root_element); + + // 5. Return sequence. + return sequence; +} + } diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h index d86f7f9dfd..67b10b10a7 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.h +++ b/Userland/Libraries/LibWeb/DOM/Document.h @@ -567,6 +567,7 @@ public: WebIDL::ExceptionOr set_design_mode(String const&); Element const* element_from_point(double x, double y); + Vector> elements_from_point(double x, double y); void set_needs_to_resolve_paint_only_properties() { m_needs_to_resolve_paint_only_properties = true; } diff --git a/Userland/Libraries/LibWeb/DOM/Document.idl b/Userland/Libraries/LibWeb/DOM/Document.idl index 7a0e7483ef..c89e48ef3c 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.idl +++ b/Userland/Libraries/LibWeb/DOM/Document.idl @@ -121,6 +121,7 @@ interface Document : Node { // https://drafts.csswg.org/cssom-view/#extensions-to-the-document-interface Element? elementFromPoint(double x, double y); + sequence elementsFromPoint(double x, double y); }; dictionary ElementCreationOptions {