@@ -228,6 +228,9 @@ pub const HTMLDocument = struct {
228228 return "" ;
229229 }
230230
231+ // Returns the topmost Element at the specified coordinates (relative to the viewport).
232+ // Since LightPanda requires the client to know what they are clicking on we do not return the underlying element at this moment
233+ // This can currenty only happen if the first pixel is click without having rendered any element. This will change when css properties are supported.
231234 // This returns an ElementUnion instead of a *Parser.Element in case the element somehow hasn't passed through the js runtime yet.
232235 pub fn _elementFromPoint (_ : * parser.DocumentHTML , x : f32 , y : f32 , state : * SessionState ) ! ? ElementUnion {
233236 const ix : i32 = @intFromFloat (@floor (x ));
@@ -237,18 +240,31 @@ pub const HTMLDocument = struct {
237240 return try Element .toInterface (element );
238241 }
239242
243+ // Returns an array of all elements at the specified coordinates (relative to the viewport). The elements are ordered from the topmost to the bottommost box of the viewport.
240244 pub fn _elementsFromPoint (_ : * parser.DocumentHTML , x : f32 , y : f32 , state : * SessionState ) ! []ElementUnion {
241245 const ix : i32 = @intFromFloat (@floor (x ));
242246 const iy : i32 = @intFromFloat (@floor (y ));
243247 const element = state .renderer .getElementAtPosition (ix , iy ) orelse return &.{};
244248 // TODO if pointer-events set to none the underlying element should be returned (parser.documentGetDocumentElement(self.document);?)
245249
246- // We need to return either 0 or 1 item, so we cannot fix the size to [1]*parser.Element
247- // Converting the pointer to a slice []parser.Element is not supported by our framework.
248- // So instead we just need to allocate the pointer to create a slice of 1.
249- const heap_ptr = try state .call_arena .create (ElementUnion );
250- heap_ptr .* = try Element .toInterface (element );
251- return heap_ptr [0.. 1];
250+ var list = try std .ArrayList (ElementUnion ).initCapacity (state .call_arena , 3 );
251+ try list .append (try Element .toInterface (element ));
252+
253+ // Since we are using a flat renderer there is no hierarchy of elements. What we do know is that the element is part of the main document.
254+ // Thus we can add the HtmlHtmlElement and it's child HTMLBodyElement to the returned list.
255+ // TBD Should we instead return every parent that is an element? Note that a child does not physically need to be overlapping the parent.
256+ // Should we do a render pass on demand?
257+ const doc_elem = try parser .documentGetDocumentElement (parser .documentHTMLToDocument (state .document .? )) orelse {
258+ return list .items ;
259+ };
260+ const body = try parser .documentHTMLBody (state .document .? ) orelse {
261+ try list .append (try Element .toInterface (doc_elem ));
262+ return list .items ;
263+ };
264+
265+ try list .append (try Element .toInterface (parser .bodyToElement (body )));
266+ try list .append (try Element .toInterface (doc_elem ));
267+ return list .items ;
252268 }
253269
254270 pub fn documentIsLoaded (html_doc : * parser.DocumentHTML , state : * SessionState ) ! void {
@@ -321,7 +337,7 @@ test "Browser.HTML.Document" {
321337 }, .{});
322338
323339 try runner .testCases (&.{
324- .{ "document.elementFromPoint(0.5, 0.5)" , "null" }, // Should these be document?
340+ .{ "document.elementFromPoint(0.5, 0.5)" , "null" }, // Return null since we only return element s when they have previously been localized
325341 .{ "document.elementsFromPoint(0.5, 0.5)" , "" },
326342 .{
327343 \\ let div1 = document.createElement('div');
@@ -332,8 +348,10 @@ test "Browser.HTML.Document" {
332348 },
333349 .{ "document.elementFromPoint(0.5, 0.5)" , "[object HTMLDivElement]" },
334350 .{ "let elems = document.elementsFromPoint(0.5, 0.5)" , null },
335- .{ "elems.length" , "1 " },
351+ .{ "elems.length" , "3 " },
336352 .{ "elems[0]" , "[object HTMLDivElement]" },
353+ .{ "elems[1]" , "[object HTMLBodyElement]" },
354+ .{ "elems[2]" , "[object HTMLHtmlElement]" },
337355 }, .{});
338356
339357 try runner .testCases (&.{
0 commit comments