cef/tests/cefclient/browser/osr_accessibility_helper.cc
2023-01-04 17:47:17 -05:00

270 lines
7.6 KiB
C++

// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
// 2013 The Chromium Authors. All rights reserved. Use of this source code is
// governed by a BSD-style license that can be found in the LICENSE file.
#include "tests/cefclient/browser/osr_accessibility_helper.h"
#include "tests/cefclient/browser/osr_accessibility_node.h"
namespace client {
OsrAXTree::OsrAXTree() : root_node_id_(-1) {}
OsrAXNode* OsrAXTree::GetNode(int nodeId) const {
auto result = node_map_.find(nodeId);
if (result != node_map_.end()) {
return result->second;
}
return nullptr;
}
void OsrAXTree::EraseNode(int nodeId) {
node_map_.erase(nodeId);
}
void OsrAXTree::AddNode(OsrAXNode* node) {
node_map_[node->OsrAXNodeId()] = node;
}
void OsrAXTree::UpdateTreeData(CefRefPtr<CefDictionaryValue> value) {
if (value->HasKey("parent_tree_id")) {
parent_tree_id_ = value->GetString("parent_tree_id");
} else {
parent_tree_id_ = "";
}
// may also update following:
// doctype, title, url, mimetype
}
OsrAccessibilityHelper::OsrAccessibilityHelper(CefRefPtr<CefValue> value,
CefRefPtr<CefBrowser> browser)
: focused_node_id_(-1), browser_(browser) {
UpdateAccessibilityTree(value);
}
int OsrAccessibilityHelper::CastToInt(CefRefPtr<CefValue> value) {
if (value->GetType() == VTYPE_STRING) {
const std::string& str = value->GetString();
return atoi(str.c_str());
} else {
return value->GetInt();
}
}
void OsrAccessibilityHelper::UpdateAccessibilityLocation(
CefRefPtr<CefValue> value) {
if (!value || value->GetType() != VTYPE_LIST) {
return;
}
CefRefPtr<CefListValue> locationChangeList = value->GetList();
size_t locationChangeCount = locationChangeList->GetSize();
if (locationChangeCount == 0) {
return;
}
for (size_t i = 0; i < locationChangeCount; i++) {
CefRefPtr<CefDictionaryValue> locationChangeDict =
locationChangeList->GetDictionary(i);
if (!locationChangeDict->HasKey("ax_tree_id") ||
!locationChangeDict->HasKey("new_location") ||
!locationChangeDict->HasKey("id")) {
continue;
}
CefString treeId = locationChangeDict->GetString("ax_tree_id");
int nodeId = CastToInt(locationChangeDict->GetValue("id"));
CefRefPtr<CefDictionaryValue> newLocationDict =
locationChangeDict->GetDictionary("new_location");
if (!newLocationDict) {
continue;
}
OsrAXNode* node = GetNode(treeId, nodeId);
if (!node) {
continue;
}
node->UpdateLocation(newLocationDict);
}
}
void OsrAccessibilityHelper::UpdateAccessibilityTree(
CefRefPtr<CefValue> value) {
if (!value || value->GetType() != VTYPE_DICTIONARY) {
return;
}
CefRefPtr<CefDictionaryValue> mainDict = value->GetDictionary();
if (!mainDict->HasKey("ax_tree_id") || !mainDict->HasKey("updates")) {
return;
}
CefString treeId = mainDict->GetString("ax_tree_id");
CefRefPtr<CefListValue> updatesList = mainDict->GetList("updates");
size_t updatesCount = updatesList->GetSize();
if (updatesCount == 0) {
return;
}
for (size_t i = 0; i < updatesCount; i++) {
CefRefPtr<CefDictionaryValue> updateDict = updatesList->GetDictionary(i);
UpdateLayout(treeId, updateDict);
}
}
OsrAXNode* OsrAccessibilityHelper::GetRootNode() const {
return GetTreeRootNode(root_tree_id_);
}
OsrAXNode* OsrAccessibilityHelper::GetFocusedNode() const {
auto tree = accessibility_node_map_.find(focused_tree_id_);
if (tree != accessibility_node_map_.end()) {
return tree->second.GetNode(focused_node_id_);
}
return nullptr;
}
OsrAXNode* OsrAccessibilityHelper::GetTreeRootNode(
const CefString& treeId) const {
auto tree = accessibility_node_map_.find(treeId);
if (tree != accessibility_node_map_.end()) {
return tree->second.GetNode(tree->second.GetRootNodeId());
}
return nullptr;
}
void OsrAccessibilityHelper::UpdateLayout(
const CefString& treeId,
CefRefPtr<CefDictionaryValue> update) {
if (!update) {
return;
}
// If a node is to be cleared
if (update->HasKey("node_id_to_clear")) {
int nodeId = CastToInt(update->GetValue("node_id_to_clear"));
// reset root node if that is to be cleared
auto tree = accessibility_node_map_.find(treeId);
if (tree != accessibility_node_map_.end()) {
if (tree->second.GetRootNodeId() == nodeId) {
root_tree_id_ = "";
tree->second.SetRootNodeId(-1);
}
}
if ((focused_tree_id_ == treeId) && (focused_node_id_ == nodeId)) {
UpdateFocusedNode("", -1);
}
OsrAXNode* node = GetNode(treeId, nodeId);
DestroyNode(node);
}
// get tree data
if (update->HasKey("tree_data") && update->HasKey("has_tree_data") &&
update->GetBool("has_tree_data")) {
CefRefPtr<CefDictionaryValue> tree_data =
update->GetDictionary("tree_data");
auto& tree = accessibility_node_map_[treeId];
tree.UpdateTreeData(tree_data);
if (tree.GetParentTreeId().empty()) {
root_tree_id_ = treeId;
}
if (tree_data->HasKey("focus_id") && tree_data->HasKey("focused_tree_id")) {
UpdateFocusedNode(tree_data->GetString("focused_tree_id"),
CastToInt(tree_data->GetValue("focus_id")));
}
}
// Now initialize/update the node data.
if (update->HasKey("nodes")) {
CefRefPtr<CefListValue> nodes = update->GetList("nodes");
for (size_t index = 0; index < nodes->GetSize(); index++) {
CefRefPtr<CefDictionaryValue> node = nodes->GetDictionary(index);
if (node) {
auto& tree = accessibility_node_map_[treeId];
int nodeId = CastToInt(node->GetValue("id"));
OsrAXNode* axNode = tree.GetNode(nodeId);
// Create if it is a new one
if (axNode) {
axNode->UpdateValue(node);
} else {
axNode = OsrAXNode::CreateNode(treeId, nodeId, node, this);
tree.AddNode(axNode);
}
}
}
}
if (update->HasKey("root_id")) {
int nodeId = CastToInt(update->GetValue("root_id"));
OsrAXNode* node = GetNode(treeId, nodeId);
if (node != nullptr) {
auto& tree = accessibility_node_map_[treeId];
tree.SetRootNodeId(nodeId);
}
}
}
void OsrAccessibilityHelper::UpdateFocusedNode(const CefString& treeId,
int nodeId) {
if ((focused_tree_id_ == treeId) && (focused_node_id_ == nodeId)) {
return;
}
focused_tree_id_ = treeId;
focused_node_id_ = nodeId;
// Now Notify Screen Reader
OsrAXNode* axNode = GetFocusedNode();
if (axNode) {
axNode->NotifyAccessibilityEvent("focus");
}
}
void OsrAccessibilityHelper::Reset() {
accessibility_node_map_.clear();
root_tree_id_ = "";
focused_tree_id_ = "";
focused_node_id_ = -1;
}
void OsrAccessibilityHelper::DestroyNode(OsrAXNode* node) {
if (node) {
CefString treeId = node->OsrAXTreeId();
int numChilds = node->GetChildCount();
if (numChilds > 0) {
for (int i = 0; i < numChilds; i++) {
OsrAXNode* childNode = node->ChildAtIndex(i);
if (!childNode) {
continue;
}
childNode->SetParent(nullptr);
if (childNode->OsrAXTreeId() == treeId) {
DestroyNode(childNode);
}
}
}
auto tree = accessibility_node_map_.find(treeId);
if (tree != accessibility_node_map_.end()) {
tree->second.EraseNode(node->OsrAXNodeId());
}
node->Destroy();
}
}
OsrAXNode* OsrAccessibilityHelper::GetNode(const CefString& treeId,
int nodeId) const {
auto tree = accessibility_node_map_.find(treeId);
if (tree != accessibility_node_map_.end()) {
return tree->second.GetNode(nodeId);
}
return nullptr;
}
} // namespace client