mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
SemanticTree: improve accessibility tree and name calculation
- Add more structural roles (banner, navigation, main, list, etc.). - Implement fallback for accessible names (SVG titles, image alt text). - Skip children for leaf-like semantic nodes to reduce redundancy. - Disable pruning in the default semantic tree view.
This commit is contained in:
@@ -385,9 +385,17 @@ const JsonVisitor = struct {
|
||||
};
|
||||
|
||||
fn isStructuralRole(role: []const u8) bool {
|
||||
// zig fmt: off
|
||||
return std.mem.eql(u8, role, "none") or
|
||||
std.mem.eql(u8, role, "generic") or
|
||||
std.mem.eql(u8, role, "InlineTextBox");
|
||||
std.mem.eql(u8, role, "InlineTextBox") or
|
||||
std.mem.eql(u8, role, "banner") or
|
||||
std.mem.eql(u8, role, "navigation") or
|
||||
std.mem.eql(u8, role, "main") or
|
||||
std.mem.eql(u8, role, "list") or
|
||||
std.mem.eql(u8, role, "listitem") or
|
||||
std.mem.eql(u8, role, "region");
|
||||
// zig fmt: on
|
||||
}
|
||||
|
||||
const TextVisitor = struct {
|
||||
@@ -436,6 +444,17 @@ const TextVisitor = struct {
|
||||
|
||||
try self.writer.writeByte('\n');
|
||||
self.depth += 1;
|
||||
|
||||
// If this is a leaf-like semantic node and we already have a name,
|
||||
// skip children to avoid redundant StaticText or noise.
|
||||
const is_leaf_semantic = std.mem.eql(u8, data.role, "link") or
|
||||
std.mem.eql(u8, data.role, "button") or
|
||||
std.mem.eql(u8, data.role, "heading") or
|
||||
std.mem.eql(u8, data.role, "code");
|
||||
if (is_leaf_semantic and data.name != null and data.name.?.len > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -888,10 +888,12 @@ fn writeName(axnode: AXNode, w: anytype, page: *Page) !?AXSource {
|
||||
=> {},
|
||||
else => {
|
||||
// write text content if exists.
|
||||
var buf = std.Io.Writer.Allocating.init(page.call_arena);
|
||||
try el.getInnerText(&buf.writer);
|
||||
var buf: std.Io.Writer.Allocating = .init(page.call_arena);
|
||||
try writeAccessibleNameFallback(node, &buf.writer, page);
|
||||
if (buf.written().len > 0) {
|
||||
try writeString(buf.written(), w);
|
||||
return .contents;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -915,6 +917,40 @@ fn writeName(axnode: AXNode, w: anytype, page: *Page) !?AXSource {
|
||||
};
|
||||
}
|
||||
|
||||
fn writeAccessibleNameFallback(node: *DOMNode, writer: *std.Io.Writer, page: *Page) !void {
|
||||
var it = node.childrenIterator();
|
||||
while (it.next()) |child| {
|
||||
switch (child._type) {
|
||||
.cdata => |cd| switch (cd._type) {
|
||||
.text => |*text| try writer.writeAll(text.getWholeText()),
|
||||
else => {},
|
||||
},
|
||||
.element => |el| {
|
||||
if (el.getTag() == .img) {
|
||||
if (el.getAttributeSafe(.wrap("alt"))) |alt| {
|
||||
try writer.writeAll(alt);
|
||||
try writer.writeByte(' ');
|
||||
}
|
||||
} else if (el.getTag() == .svg) {
|
||||
// Try to find a <title> inside SVG
|
||||
var sit = child.childrenIterator();
|
||||
while (sit.next()) |s_child| {
|
||||
if (s_child.is(DOMNode.Element)) |s_el| {
|
||||
if (std.mem.eql(u8, s_el.getTagNameLower(), "title")) {
|
||||
try writeAccessibleNameFallback(s_child, writer, page);
|
||||
try writer.writeByte(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try writeAccessibleNameFallback(child, writer, page);
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn isHidden(elt: *DOMNode.Element) bool {
|
||||
if (elt.getAttributeSafe(comptime .wrap("aria-hidden"))) |value| {
|
||||
if (std.mem.eql(u8, value, "true")) {
|
||||
|
||||
@@ -113,12 +113,12 @@ pub fn fetch(app: *App, url: [:0]const u8, opts: FetchOpts) !void {
|
||||
var registry = CDPNode.Registry.init(app.allocator);
|
||||
defer registry.deinit();
|
||||
|
||||
const st = SemanticTree{
|
||||
const st: SemanticTree = .{
|
||||
.dom_node = page.window._document.asNode(),
|
||||
.registry = ®istry,
|
||||
.page = page,
|
||||
.arena = page.call_arena,
|
||||
.prune = true,
|
||||
.prune = false,
|
||||
};
|
||||
|
||||
if (mode == .semantic_tree) {
|
||||
|
||||
Reference in New Issue
Block a user