1 // Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
2 // 2013 The Chromium Authors. All rights reserved. Use of this source code is
3 // governed by a BSD-style license that can be found in the LICENSE file.
4
5 #include "tests/cefclient/browser/osr_accessibility_helper.h"
6 #include "tests/cefclient/browser/osr_accessibility_node.h"
7
8 namespace client {
9
OsrAXTree()10 OsrAXTree::OsrAXTree() : root_node_id_(-1) {}
11
GetNode(int nodeId) const12 OsrAXNode* OsrAXTree::GetNode(int nodeId) const {
13 auto result = node_map_.find(nodeId);
14 if (result != node_map_.end()) {
15 return result->second;
16 }
17 return nullptr;
18 }
19
EraseNode(int nodeId)20 void OsrAXTree::EraseNode(int nodeId) {
21 node_map_.erase(nodeId);
22 }
23
AddNode(OsrAXNode * node)24 void OsrAXTree::AddNode(OsrAXNode* node) {
25 node_map_[node->OsrAXNodeId()] = node;
26 }
27
UpdateTreeData(CefRefPtr<CefDictionaryValue> value)28 void OsrAXTree::UpdateTreeData(CefRefPtr<CefDictionaryValue> value) {
29 if (value->HasKey("parent_tree_id")) {
30 parent_tree_id_ = value->GetString("parent_tree_id");
31 } else {
32 parent_tree_id_ = "";
33 }
34
35 // may also update following:
36 // doctype, title, url, mimetype
37 }
38
OsrAccessibilityHelper(CefRefPtr<CefValue> value,CefRefPtr<CefBrowser> browser)39 OsrAccessibilityHelper::OsrAccessibilityHelper(CefRefPtr<CefValue> value,
40 CefRefPtr<CefBrowser> browser)
41 : focused_node_id_(-1),
42 browser_(browser) {
43 UpdateAccessibilityTree(value);
44 }
45
CastToInt(CefRefPtr<CefValue> value)46 int OsrAccessibilityHelper::CastToInt(CefRefPtr<CefValue> value) {
47 if (value->GetType() == VTYPE_STRING) {
48 const std::string& str = value->GetString();
49 return atoi(str.c_str());
50 } else {
51 return value->GetInt();
52 }
53 }
54
UpdateAccessibilityLocation(CefRefPtr<CefValue> value)55 void OsrAccessibilityHelper::UpdateAccessibilityLocation(
56 CefRefPtr<CefValue> value) {
57 if (!value || value->GetType() != VTYPE_LIST) {
58 return;
59 }
60
61 CefRefPtr<CefListValue> locationChangeList = value->GetList();
62 size_t locationChangeCount = locationChangeList->GetSize();
63 if (locationChangeCount == 0) {
64 return;
65 }
66
67 for (size_t i = 0; i < locationChangeCount; i++) {
68 CefRefPtr<CefDictionaryValue> locationChangeDict =
69 locationChangeList->GetDictionary(i);
70 if (!locationChangeDict->HasKey("ax_tree_id") ||
71 !locationChangeDict->HasKey("new_location") ||
72 !locationChangeDict->HasKey("id")) {
73 continue;
74 }
75 CefString treeId = locationChangeDict->GetString("ax_tree_id");
76 int nodeId = CastToInt(locationChangeDict->GetValue("id"));
77
78 CefRefPtr<CefDictionaryValue> newLocationDict =
79 locationChangeDict->GetDictionary("new_location");
80 if (!newLocationDict) {
81 continue;
82 }
83
84 OsrAXNode* node = GetNode(treeId, nodeId);
85 if (!node) {
86 continue;
87 }
88 node->UpdateLocation(newLocationDict);
89 }
90 }
91
UpdateAccessibilityTree(CefRefPtr<CefValue> value)92 void OsrAccessibilityHelper::UpdateAccessibilityTree(
93 CefRefPtr<CefValue> value) {
94 if (!value || value->GetType() != VTYPE_DICTIONARY) {
95 return;
96 }
97
98 CefRefPtr<CefDictionaryValue> mainDict = value->GetDictionary();
99 if (!mainDict->HasKey("ax_tree_id") || !mainDict->HasKey("updates")) {
100 return;
101 }
102
103 CefString treeId = mainDict->GetString("ax_tree_id");
104 CefRefPtr<CefListValue> updatesList = mainDict->GetList("updates");
105
106 size_t updatesCount = updatesList->GetSize();
107 if (updatesCount == 0) {
108 return;
109 }
110
111 for (size_t i = 0; i < updatesCount; i++) {
112 CefRefPtr<CefDictionaryValue> updateDict = updatesList->GetDictionary(i);
113 UpdateLayout(treeId, updateDict);
114 }
115 }
116
GetRootNode() const117 OsrAXNode* OsrAccessibilityHelper::GetRootNode() const {
118 return GetTreeRootNode(root_tree_id_);
119 }
120
GetFocusedNode() const121 OsrAXNode* OsrAccessibilityHelper::GetFocusedNode() const {
122 auto tree = accessibility_node_map_.find(focused_tree_id_);
123 if (tree != accessibility_node_map_.end()) {
124 return tree->second.GetNode(focused_node_id_);
125 }
126
127 return nullptr;
128 }
129
GetTreeRootNode(const CefString & treeId) const130 OsrAXNode* OsrAccessibilityHelper::GetTreeRootNode(
131 const CefString& treeId) const {
132 auto tree = accessibility_node_map_.find(treeId);
133 if (tree != accessibility_node_map_.end()) {
134 return tree->second.GetNode(tree->second.GetRootNodeId());
135 }
136
137 return nullptr;
138 }
139
UpdateLayout(const CefString & treeId,CefRefPtr<CefDictionaryValue> update)140 void OsrAccessibilityHelper::UpdateLayout(
141 const CefString& treeId,
142 CefRefPtr<CefDictionaryValue> update) {
143 if (!update) {
144 return;
145 }
146
147 // If a node is to be cleared
148 if (update->HasKey("node_id_to_clear")) {
149 int nodeId = CastToInt(update->GetValue("node_id_to_clear"));
150
151 // reset root node if that is to be cleared
152 auto tree = accessibility_node_map_.find(treeId);
153 if (tree != accessibility_node_map_.end()) {
154 if (tree->second.GetRootNodeId() == nodeId) {
155 root_tree_id_ = "";
156 tree->second.SetRootNodeId(-1);
157 }
158 }
159 if ((focused_tree_id_ == treeId) && (focused_node_id_ == nodeId)) {
160 UpdateFocusedNode("", -1);
161 }
162 OsrAXNode* node = GetNode(treeId, nodeId);
163 DestroyNode(node);
164 }
165
166 // get tree data
167 if (update->HasKey("tree_data") && update->HasKey("has_tree_data") &&
168 update->GetBool("has_tree_data")) {
169 CefRefPtr<CefDictionaryValue> tree_data =
170 update->GetDictionary("tree_data");
171 auto& tree = accessibility_node_map_[treeId];
172 tree.UpdateTreeData(tree_data);
173 if (tree.GetParentTreeId().empty()) {
174 root_tree_id_ = treeId;
175 }
176 if (tree_data->HasKey("focus_id") && tree_data->HasKey("focused_tree_id")) {
177 UpdateFocusedNode(tree_data->GetString("focused_tree_id"),
178 CastToInt(tree_data->GetValue("focus_id")));
179 }
180 }
181
182 // Now initialize/update the node data.
183 if (update->HasKey("nodes")) {
184 CefRefPtr<CefListValue> nodes = update->GetList("nodes");
185
186 for (size_t index = 0; index < nodes->GetSize(); index++) {
187 CefRefPtr<CefDictionaryValue> node = nodes->GetDictionary(index);
188 if (node) {
189 auto& tree = accessibility_node_map_[treeId];
190 int nodeId = CastToInt(node->GetValue("id"));
191 OsrAXNode* axNode = tree.GetNode(nodeId);
192 // Create if it is a new one
193 if (axNode) {
194 axNode->UpdateValue(node);
195 } else {
196 axNode = OsrAXNode::CreateNode(treeId, nodeId, node, this);
197 tree.AddNode(axNode);
198 }
199 }
200 }
201 }
202
203 if (update->HasKey("root_id")) {
204 int nodeId = CastToInt(update->GetValue("root_id"));
205 OsrAXNode* node = GetNode(treeId, nodeId);
206 if (node != nullptr) {
207 auto& tree = accessibility_node_map_[treeId];
208 tree.SetRootNodeId(nodeId);
209 }
210 }
211 }
212
UpdateFocusedNode(const CefString & treeId,int nodeId)213 void OsrAccessibilityHelper::UpdateFocusedNode(const CefString& treeId,
214 int nodeId) {
215 if ((focused_tree_id_ == treeId) && (focused_node_id_ == nodeId)) {
216 return;
217 }
218 focused_tree_id_ = treeId;
219 focused_node_id_ = nodeId;
220
221 // Now Notify Screen Reader
222 OsrAXNode* axNode = GetFocusedNode();
223 if (axNode) {
224 axNode->NotifyAccessibilityEvent("focus");
225 }
226 }
227
Reset()228 void OsrAccessibilityHelper::Reset() {
229 accessibility_node_map_.clear();
230 root_tree_id_ = "";
231 focused_tree_id_ = "";
232 focused_node_id_ = -1;
233 }
234
DestroyNode(OsrAXNode * node)235 void OsrAccessibilityHelper::DestroyNode(OsrAXNode* node) {
236 if (node) {
237 CefString treeId = node->OsrAXTreeId();
238 int numChilds = node->GetChildCount();
239 if (numChilds > 0) {
240 for (int i = 0; i < numChilds; i++) {
241 OsrAXNode* childNode = node->ChildAtIndex(i);
242 if (!childNode) {
243 continue;
244 }
245 childNode->SetParent(nullptr);
246 if (childNode->OsrAXTreeId() == treeId) {
247 DestroyNode(childNode);
248 }
249 }
250 }
251 auto tree = accessibility_node_map_.find(treeId);
252 if (tree != accessibility_node_map_.end()) {
253 tree->second.EraseNode(node->OsrAXNodeId());
254 }
255
256 node->Destroy();
257 }
258 }
259
GetNode(const CefString & treeId,int nodeId) const260 OsrAXNode* OsrAccessibilityHelper::GetNode(const CefString& treeId,
261 int nodeId) const {
262 auto tree = accessibility_node_map_.find(treeId);
263 if (tree != accessibility_node_map_.end()) {
264 return tree->second.GetNode(nodeId);
265 }
266
267 return nullptr;
268 }
269
270 } // namespace client
271