diff --git a/src/browser/dom/document.zig b/src/browser/dom/document.zig index a987717b..a4942f75 100644 --- a/src/browser/dom/document.zig +++ b/src/browser/dom/document.zig @@ -242,6 +242,11 @@ pub const Document = struct { pub fn _createTreeWalker(_: *parser.Document, root: *parser.Node, what_to_show: ?u32, filter: ?TreeWalker.TreeWalkerOpts) !TreeWalker { return try TreeWalker.init(root, what_to_show, filter); } + + pub fn get_activeElement(_: *parser.Document, page: *const Page) !?ElementUnion { + const el = (try page.activeElement()) orelse return null; + return try Element.toInterface(el); + } }; const testing = @import("../../testing.zig"); @@ -412,6 +417,12 @@ test "Browser.DOM.Document" { }, }, .{}); + try runner.testCases(&.{ + .{ "document.activeElement === document.body", "true" }, + .{ "document.getElementById('link').focus()", "undefined" }, + .{ "document.activeElement === document.getElementById('link')", "true" }, + }, .{}); + // this test breaks the doc structure, keep it at the end of the test // suite. try runner.testCases(&.{ diff --git a/src/browser/html/elements.zig b/src/browser/html/elements.zig index 652e60c4..c5eaa1ef 100644 --- a/src/browser/html/elements.zig +++ b/src/browser/html/elements.zig @@ -148,6 +148,14 @@ pub const HTMLElement = struct { }); _ = try parser.elementDispatchEvent(@ptrCast(e), @ptrCast(event)); } + + pub fn _focus(e: *parser.ElementHTML, page: *Page) void { + // TODO: some elements can't be focused, like if they're disabled + // but there doesn't seem to be a generic way to check this. For example + // we could look for the "disabled" attribute, but that's only meaningful + // on certain types, and libdom's vtable doesn't seem to expose this. + page.active_element = @ptrCast(e); + } }; // Deprecated HTMLElements in Chrome (2023/03/15) diff --git a/src/browser/page.zig b/src/browser/page.zig index 9f14d982..1ce081fe 100644 --- a/src/browser/page.zig +++ b/src/browser/page.zig @@ -95,6 +95,8 @@ pub const Page = struct { // indicates intention to navigate to another page on the next loop execution. delayed_navigation: bool = false, + active_element: ?*parser.Element = null, + pub fn init(self: *Page, arena: Allocator, session: *Session) !void { const browser = session.browser; self.* = .{ @@ -643,6 +645,23 @@ pub const Page = struct { try self.navigateFromWebAPI(action, opts); } + pub fn activeElement(self: *const Page) !?*parser.Element { + if (self.active_element) |ae| { + return ae; + } + + if (try parser.documentHTMLBody(self.window.document)) |body| { + return @ptrCast(body); + } + + const doc = self.window.document; + if (try parser.documentGetDocumentElement(@ptrCast(doc))) |de| { + return @ptrCast(de); + } + + return null; + } + fn elementSubmitForm(self: *Page, element: *parser.Element) !void { const form = (try self.formForElement(element)) orelse return; return self.submitForm(@ptrCast(form), @ptrCast(element));