/* * Copyright (c) 2025, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include namespace DevTools { NonnullRefPtr AccessibilityNodeActor::create(DevToolsServer& devtools, String name, NodeIdentifier node_identifier, WeakPtr walker) { return adopt_ref(*new AccessibilityNodeActor(devtools, move(name), move(node_identifier), move(walker))); } AccessibilityNodeActor::AccessibilityNodeActor(DevToolsServer& devtools, String name, NodeIdentifier node_identifier, WeakPtr walker) : Actor(devtools, move(name)) , m_node_identifier(node_identifier) , m_walker(move(walker)) { } AccessibilityNodeActor::~AccessibilityNodeActor() = default; void AccessibilityNodeActor::handle_message(Message const& message) { if (message.type == "audit"sv) { // FIXME: Implement accessibility audits. JsonObject audit; JsonObject response; response.set("type"sv, "audited"sv); response.set("audit"sv, audit); send_response(message, move(response)); // For whatever reason, we need to send this a second time with no `type`. JsonObject second_response; second_response.set("audit"sv, audit); send_message(move(second_response)); return; } if (message.type == "children"sv) { if (auto walker = m_walker.strong_ref()) { auto ancestor_node = walker->accessibility_node(name()); if (!ancestor_node.has_value()) { send_unknown_actor_error(message, name()); return; } JsonArray children; if (auto child_nodes = ancestor_node->node.get_array("children"sv); child_nodes.has_value()) { child_nodes->for_each([&](JsonValue const& child) { children.must_append(walker->serialize_node(child.as_object())); }); } JsonObject response; response.set("children"sv, move(children)); send_response(message, move(response)); return; } send_unknown_actor_error(message, name()); return; } if (message.type == "getRelations"sv) { if (auto walker = m_walker.strong_ref()) { auto accessibility_node = walker->accessibility_node(name()); if (!accessibility_node.has_value()) { send_unknown_actor_error(message, name()); return; } auto root_node = walker->root_accessibility_node(); JsonArray relations; auto report_relation = [&](StringView type, Node const& node) { JsonArray targets; MUST(targets.append(walker->serialize_node(node.node))); JsonObject relation; relation.set("targets"sv, move(targets)); relation.set("type"sv, type); MUST(relations.append(move(relation))); }; // For the root node, list itself as an "embeds" relation. if (root_node.has_value() && root_node->identifier == accessibility_node->identifier) report_relation("embeds"sv, root_node.value()); // For all nodes, list the root as the "containing document" relation. report_relation("containing document"sv, root_node.value()); // FIXME: Figure out what other relations we need to report here. JsonObject response; response.set("relations"sv, move(relations)); send_response(message, move(response)); return; } send_unknown_actor_error(message, name()); return; } if (message.type == "hydrate"sv) { auto accessibility_node = AccessibilityWalkerActor::accessibility_node_for(m_walker, name()); if (!accessibility_node.has_value()) { send_unknown_actor_error(message, name()); return; } auto parent_node = AccessibilityWalkerActor::parent_of_accessibility_node_for(m_walker, *accessibility_node); auto const& node_json = accessibility_node->node; auto dom_node_type = parse_dom_node_type(node_json.get_string("type"sv).value()); auto index_in_parent = 0; if (parent_node.has_value()) { if (auto parent_children = parent_node->node.get_array("children"sv); parent_children.has_value()) { for (auto const& [index, child] : enumerate(parent_children->values())) { if (child.as_object().get_i64("id"sv) == accessibility_node->identifier.id) { index_in_parent = index; break; } } } } // FIXME: Populate these. JsonArray actions; JsonObject attributes; JsonArray states; JsonObject properties; properties.set("actions"sv, move(actions)); properties.set("attributes"sv, move(attributes)); properties.set("description"sv, node_json.get_string("description"sv).value_or({})); properties.set("domNodeType"sv, to_underlying(dom_node_type)); properties.set("indexInParent"sv, index_in_parent); // FIXME: Value of the accesskey attribute properties.set("keyboardShortcut"sv, ""sv); properties.set("states"sv, move(states)); // FIXME: Implement properties.set("value"sv, ""sv); JsonObject response; response.set("properties"sv, move(properties)); send_response(message, move(response)); return; } send_unrecognized_packet_type_error(message); } }