From b9601be45ec47afdbfc413a10d75e9d4bec811cb Mon Sep 17 00:00:00 2001 From: Navid EMAD Date: Fri, 15 May 2026 22:26:48 +0200 Subject: [PATCH] accessibility: bind AX writers to the node's owning frame axnodeWriter and axnodeQueryWriter both used session.currentFrame(), but the root node may belong to a different frame (cross-frame query from a parent context, iframe content). Name resolution (Label lookup against ownerDocument) and visibility checks (frame._style_manager) are per-frame, so the writer needs to bind to the node's owning frame. Uses the existing Node.ownerFrame(fallback) helper. Fallback is currentFrame for the orphan/detached-node case. Also corrects a pre-existing latent bug in getFullAXTree where the writer ignored the resolved frameId and used currentFrame instead. --- src/cdp/CDP.zig | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/cdp/CDP.zig b/src/cdp/CDP.zig index 54c5cebf..e44e1708 100644 --- a/src/cdp/CDP.zig +++ b/src/cdp/CDP.zig @@ -654,7 +654,13 @@ pub const BrowserContext = struct { } pub fn axnodeWriter(self: *BrowserContext, temp_arena: Allocator, root: *const Node, opts: AXNode.Writer.Opts) !AXNode.Writer { - const frame = self.session.currentFrame() orelse return error.FrameNotLoaded; + // Bind the writer to the frame that owns the root node, not whatever + // happens to be `currentFrame`. Name resolution (`Label.findLabelByFor` + // against `ownerDocument`) and visibility checks (`frame._style_manager`) + // are per-frame; getting this wrong on cross-frame queries produces + // names/visibility from the wrong document. + const fallback = self.session.currentFrame() orelse return error.FrameNotLoaded; + const frame = root.dom.ownerFrame(fallback); _ = opts; const cache = try frame.call_arena.create(Element.VisibilityCache); cache.* = .empty; @@ -671,7 +677,9 @@ pub const BrowserContext = struct { } pub fn axnodeQueryWriter(self: *BrowserContext, temp_arena: Allocator, root: *const Node, opts: AXNode.QueryWriter.Opts) !AXNode.QueryWriter { - const frame = self.session.currentFrame() orelse return error.FrameNotLoaded; + // See axnodeWriter for the frame-binding rationale. + const fallback = self.session.currentFrame() orelse return error.FrameNotLoaded; + const frame = root.dom.ownerFrame(fallback); const cache = try frame.call_arena.create(Element.VisibilityCache); cache.* = .empty; const label_index = try frame.call_arena.create(Label.LabelByForIndex);