diff --git a/src/browser/tests/cdp/ax_tree.html b/src/browser/tests/cdp/ax_tree.html index 79df111e..0819693a 100644 --- a/src/browser/tests/cdp/ax_tree.html +++ b/src/browser/tests/cdp/ax_tree.html @@ -36,6 +36,22 @@ Wrap + + + + + + + + + + + + + diff --git a/src/cdp/AXNode.zig b/src/cdp/AXNode.zig index ee0f476c..4a9008eb 100644 --- a/src/cdp/AXNode.zig +++ b/src/cdp/AXNode.zig @@ -477,8 +477,18 @@ pub const Writer = struct { try w.objectField("backendDOMNodeId"); try w.write(id); + const promoted_input = labelPromotionTarget(axn, self.frame, self.visibility_cache); + try w.objectField("role"); - try self.writeAXValue(.{ .role = try axn.getRole() }, w); + if (promoted_input) |input| { + try self.writeAXValue(.{ .role = switch (input._input_type) { + .checkbox => "checkbox", + .radio => "radio", + else => unreachable, + } }, w); + } else { + try self.writeAXValue(.{ .role = try axn.getRole() }, w); + } const ignore = axn.isIgnore(self.frame, self.visibility_cache, in_aria_hidden); try w.objectField("ignored"); @@ -515,6 +525,18 @@ pub const Writer = struct { try w.objectField("properties"); try w.beginArray(); try self.writeAXProperties(axn, w); + if (promoted_input) |input| { + const input_el = input.asElement(); + const is_disabled = input_el.isDisabled(); + if (is_disabled) { + try self.writeAXProperty(.{ .name = .disabled, .value = .{ .boolean = true } }, w); + } + try self.writeAXProperty(.{ .name = .invalid, .value = .{ .token = "false" } }, w); + if (!is_disabled) { + try self.writeAXProperty(.{ .name = .focusable, .value = .{ .booleanOrUndefined = true } }, w); + } + try self.writeAXProperty(.{ .name = .checked, .value = .{ .token = if (input._checked) "true" else "false" } }, w); + } try w.endArray(); } @@ -1018,6 +1040,43 @@ fn isLabellableTag(tag: DOMNode.Element.Tag) bool { }; } +// CSS-only toggle switches and custom radios commonly visually-style a +// `