1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "frameworks/bridge/common/accessibility/accessibility_node_manager.h"
17
18 #include "base/geometry/dimension_offset.h"
19 #include "base/log/dump_log.h"
20 #include "base/log/event_report.h"
21 #include "core/accessibility/js_inspector/inspect_badge.h"
22 #include "core/accessibility/js_inspector/inspect_button.h"
23 #include "core/accessibility/js_inspector/inspect_camera.h"
24 #include "core/accessibility/js_inspector/inspect_canvas.h"
25 #include "core/accessibility/js_inspector/inspect_chart.h"
26 #include "core/accessibility/js_inspector/inspect_dialog.h"
27 #include "core/accessibility/js_inspector/inspect_div.h"
28 #include "core/accessibility/js_inspector/inspect_divider.h"
29 #include "core/accessibility/js_inspector/inspect_form.h"
30 #include "core/accessibility/js_inspector/inspect_grid_column.h"
31 #include "core/accessibility/js_inspector/inspect_grid_container.h"
32 #include "core/accessibility/js_inspector/inspect_grid_row.h"
33 #include "core/accessibility/js_inspector/inspect_image.h"
34 #include "core/accessibility/js_inspector/inspect_image_animator.h"
35 #include "core/accessibility/js_inspector/inspect_input.h"
36 #include "core/accessibility/js_inspector/inspect_label.h"
37 #include "core/accessibility/js_inspector/inspect_list.h"
38 #include "core/accessibility/js_inspector/inspect_list_item.h"
39 #include "core/accessibility/js_inspector/inspect_list_item_group.h"
40 #include "core/accessibility/js_inspector/inspect_marquee.h"
41 #include "core/accessibility/js_inspector/inspect_menu.h"
42 #include "core/accessibility/js_inspector/inspect_navigation_bar.h"
43 #include "core/accessibility/js_inspector/inspect_option.h"
44 #include "core/accessibility/js_inspector/inspect_panel.h"
45 #include "core/accessibility/js_inspector/inspect_picker.h"
46 #include "core/accessibility/js_inspector/inspect_picker_view.h"
47 #include "core/accessibility/js_inspector/inspect_piece.h"
48 #include "core/accessibility/js_inspector/inspect_popup.h"
49 #include "core/accessibility/js_inspector/inspect_progress.h"
50 #include "core/accessibility/js_inspector/inspect_qrcode.h"
51 #include "core/accessibility/js_inspector/inspect_rating.h"
52 #include "core/accessibility/js_inspector/inspect_refresh.h"
53 #include "core/accessibility/js_inspector/inspect_search.h"
54 #include "core/accessibility/js_inspector/inspect_select.h"
55 #include "core/accessibility/js_inspector/inspect_slider.h"
56 #include "core/accessibility/js_inspector/inspect_span.h"
57 #include "core/accessibility/js_inspector/inspect_stack.h"
58 #include "core/accessibility/js_inspector/inspect_stepper.h"
59 #include "core/accessibility/js_inspector/inspect_stepper_item.h"
60 #include "core/accessibility/js_inspector/inspect_swiper.h"
61 #include "core/accessibility/js_inspector/inspect_switch.h"
62 #include "core/accessibility/js_inspector/inspect_tab_bar.h"
63 #include "core/accessibility/js_inspector/inspect_tab_content.h"
64 #include "core/accessibility/js_inspector/inspect_tabs.h"
65 #include "core/accessibility/js_inspector/inspect_text.h"
66 #include "core/accessibility/js_inspector/inspect_textarea.h"
67 #include "core/accessibility/js_inspector/inspect_toggle.h"
68 #include "core/accessibility/js_inspector/inspect_toolbar.h"
69 #include "core/accessibility/js_inspector/inspect_toolbar_item.h"
70 #include "core/accessibility/js_inspector/inspect_video.h"
71 #include "core/components_v2/inspector/inspector_composed_element.h"
72
73 namespace OHOS::Ace::Framework {
74 namespace {
75
76 const char PAGE_CHANGE_EVENT[] = "pagechange";
77 const char ROOT_STACK_TAG[] = "rootstacktag";
78 constexpr int32_t ROOT_STACK_BASE = 1100000;
79 constexpr int32_t CARD_NODE_ID_RATION = 10000;
80 constexpr int32_t CARD_ROOT_NODE_ID = 21000;
81 constexpr int32_t CARD_BASE = 100000;
82 constexpr int32_t CARD_MAX_AGP_ID = 20000;
83
84 std::atomic<int32_t> g_accessibilityId(ROOT_STACK_BASE);
85
86 const char INSPECTOR_TYPE[] = "$type";
87 const char INSPECTOR_ID[] = "$ID";
88 const char INSPECTOR_RECT[] = "$rect";
89 const char INSPECTOR_ATTRS[] = "$attrs";
90 const char INSPECTOR_STYLES[] = "$styles";
91
92 template<class T>
InspectNodeCreator(NodeId nodeId,const std::string & tag)93 RefPtr<InspectNode> InspectNodeCreator(NodeId nodeId, const std::string& tag)
94 {
95 return AceType::MakeRefPtr<T>(nodeId, tag);
96 }
97
98 const LinearMapNode<RefPtr<InspectNode> (*)(NodeId, const std::string&)> inspectNodeCreators[] = {
99 { DOM_NODE_TAG_BADGE, &InspectNodeCreator<InspectBadge> },
100 { DOM_NODE_TAG_BUTTON, &InspectNodeCreator<InspectButton> },
101 { DOM_NODE_TAG_CAMERA, &InspectNodeCreator<InspectCamera> },
102 { DOM_NODE_TAG_CANVAS, &InspectNodeCreator<InspectCanvas> },
103 { DOM_NODE_TAG_CHART, &InspectNodeCreator<InspectChart> },
104 { DOM_NODE_TAG_DIALOG, &InspectNodeCreator<InspectDialog> },
105 { DOM_NODE_TAG_DIV, &InspectNodeCreator<InspectDiv> },
106 { DOM_NODE_TAG_DIVIDER, &InspectNodeCreator<InspectDivider> },
107 { DOM_NODE_TAG_FORM, &InspectNodeCreator<InspectForm> },
108 { DOM_NODE_TAG_GRID_COLUMN, &InspectNodeCreator<InspectGridColumn> },
109 { DOM_NODE_TAG_GRID_CONTAINER, &InspectNodeCreator<InspectGridContainer> },
110 { DOM_NODE_TAG_GRID_ROW, &InspectNodeCreator<InspectGridRow> },
111 { DOM_NODE_TAG_IMAGE, &InspectNodeCreator<InspectImage> },
112 { DOM_NODE_TAG_IMAGE_ANIMATOR, &InspectNodeCreator<InspectImageAnimator> },
113 { DOM_NODE_TAG_INPUT, &InspectNodeCreator<InspectInput> },
114 { DOM_NODE_TAG_LABEL, &InspectNodeCreator<InspectLabel> },
115 { DOM_NODE_TAG_LIST, &InspectNodeCreator<InspectList> },
116 { DOM_NODE_TAG_LIST_ITEM, &InspectNodeCreator<InspectListItem> },
117 { DOM_NODE_TAG_LIST_ITEM_GROUP, &InspectNodeCreator<InspectListItemGroup> },
118 { DOM_NODE_TAG_MARQUEE, &InspectNodeCreator<InspectMarquee> },
119 { DOM_NODE_TAG_MENU, &InspectNodeCreator<InspectMenu> },
120 { DOM_NODE_TAG_NAVIGATION_BAR, &InspectNodeCreator<InspectNavigationBar> },
121 { DOM_NODE_TAG_OPTION, &InspectNodeCreator<InspectOption> },
122 { DOM_NODE_TAG_PANEL, &InspectNodeCreator<InspectPanel> },
123 { DOM_NODE_TAG_PICKER_DIALOG, &InspectNodeCreator<InspectPicker> },
124 { DOM_NODE_TAG_PICKER_VIEW, &InspectNodeCreator<InspectPickerView> },
125 { DOM_NODE_TAG_PIECE, &InspectNodeCreator<InspectPiece> },
126 { DOM_NODE_TAG_POPUP, &InspectNodeCreator<InspectPopup> },
127 { DOM_NODE_TAG_PROGRESS, &InspectNodeCreator<InspectProgress> },
128 { DOM_NODE_TAG_QRCODE, &InspectNodeCreator<InspectQRcode> },
129 { DOM_NODE_TAG_RATING, &InspectNodeCreator<InspectRating> },
130 { DOM_NODE_TAG_REFRESH, &InspectNodeCreator<InspectRefresh> },
131 { DOM_NODE_TAG_SEARCH, &InspectNodeCreator<InspectSearch> },
132 { DOM_NODE_TAG_SELECT, &InspectNodeCreator<InspectSelect> },
133 { DOM_NODE_TAG_SLIDER, &InspectNodeCreator<InspectSlider> },
134 { DOM_NODE_TAG_SPAN, &InspectNodeCreator<InspectSpan> },
135 { DOM_NODE_TAG_STACK, &InspectNodeCreator<InspectStack> },
136 { DOM_NODE_TAG_STEPPER, &InspectNodeCreator<InspectStepper> },
137 { DOM_NODE_TAG_STEPPER_ITEM, &InspectNodeCreator<InspectStepperItem> },
138 { DOM_NODE_TAG_SWIPER, &InspectNodeCreator<InspectSwiper> },
139 { DOM_NODE_TAG_SWITCH, &InspectNodeCreator<InspectSwitch> },
140 { DOM_NODE_TAG_TAB_BAR, &InspectNodeCreator<InspectTabBar> },
141 { DOM_NODE_TAG_TAB_CONTENT, &InspectNodeCreator<InspectTabContent> },
142 { DOM_NODE_TAG_TABS, &InspectNodeCreator<InspectTabs> },
143 { DOM_NODE_TAG_TEXT, &InspectNodeCreator<InspectText> },
144 { DOM_NODE_TAG_TEXTAREA, &InspectNodeCreator<InspectTextArea> },
145 { DOM_NODE_TAG_TOGGLE, &InspectNodeCreator<InspectToggle> },
146 { DOM_NODE_TAG_TOOL_BAR, &InspectNodeCreator<InspectToolbar> },
147 { DOM_NODE_TAG_TOOL_BAR_ITEM, &InspectNodeCreator<InspectToolbarItem> },
148 { DOM_NODE_TAG_VIDEO, &InspectNodeCreator<InspectVideo> },
149 };
150
ConvertStrToPropertyType(const std::string & typeValue)151 std::string ConvertStrToPropertyType(const std::string& typeValue)
152 {
153 std::string dstStr;
154 std::regex regex("([A-Z])");
155 dstStr = regex_replace(typeValue, regex, "-$1");
156 std::transform(dstStr.begin(), dstStr.end(), dstStr.begin(), ::tolower);
157 return dstStr;
158 }
159
GetRootNodeIdFromPage(const RefPtr<JsAcePage> & page)160 inline int32_t GetRootNodeIdFromPage(const RefPtr<JsAcePage>& page)
161 {
162 auto domDocument = page ? page->GetDomDocument() : nullptr;
163 if (domDocument) {
164 return domDocument->GetRootNodeId();
165 }
166 LOGW("Failed to get root dom node");
167 return -1;
168 }
169
ConvertToNodeId(int32_t cardAccessibilityId)170 int32_t ConvertToNodeId(int32_t cardAccessibilityId)
171 {
172 // cardAccessibilityId is integer total ten digits, top five for agp virtualViewId, end five for ace nodeId,
173 // for example 00032 10001 convert to result is 1000001
174 int result = 0;
175 int32_t nodeId = cardAccessibilityId % CARD_BASE;
176 if (nodeId >= CARD_ROOT_NODE_ID) {
177 return 0;
178 }
179 result =
180 (static_cast<int32_t>(nodeId / CARD_NODE_ID_RATION)) * DOM_ROOT_NODE_ID_BASE + nodeId % CARD_NODE_ID_RATION;
181 return result;
182 }
183
184 } // namespace
185
~AccessibilityNodeManager()186 AccessibilityNodeManager::~AccessibilityNodeManager()
187 {
188 auto rootNode = GetAccessibilityNodeById(rootNodeId_ + ROOT_STACK_BASE);
189 if (rootNode) {
190 RemoveAccessibilityNodes(rootNode);
191 }
192 }
193
InitializeCallback()194 void AccessibilityNodeManager::InitializeCallback() {}
195
SetPipelineContext(const RefPtr<PipelineContext> & context)196 void AccessibilityNodeManager::SetPipelineContext(const RefPtr<PipelineContext>& context)
197 {
198 context_ = context;
199 }
200
SetRunningPage(const RefPtr<JsAcePage> & page)201 void AccessibilityNodeManager::SetRunningPage(const RefPtr<JsAcePage>& page)
202 {
203 indexPage_ = page;
204 // send page change event to barrier free when page change.
205 AccessibilityEvent accessibilityEvent;
206 accessibilityEvent.eventType = PAGE_CHANGE_EVENT;
207 SendAccessibilityAsyncEvent(accessibilityEvent);
208 if (GetVersion() == AccessibilityVersion::JS_DECLARATIVE_VERSION) {
209 auto domDocument = page ? page->GetDomDocument() : nullptr;
210 if (domDocument) {
211 return SetRootNodeId(domDocument->GetRootNodeId());
212 } else {
213 LOGE("domDocument is null");
214 }
215 }
216 }
217
GetNodeChildIds(const RefPtr<AccessibilityNode> & node)218 std::string AccessibilityNodeManager::GetNodeChildIds(const RefPtr<AccessibilityNode>& node)
219 {
220 std::string ids;
221 if (node) {
222 const auto& children = node->GetChildList();
223 if ((node->GetNodeId() == rootNodeId_ + ROOT_STACK_BASE) && !children.empty()) {
224 ids.append(std::to_string(children.back()->GetNodeId()));
225 } else {
226 for (const auto& child : children) {
227 if (!ids.empty()) {
228 ids.append(",");
229 }
230 ids.append(std::to_string(child->GetNodeId()));
231 }
232 }
233 }
234 return ids;
235 }
236
AddNodeWithId(const std::string & key,const RefPtr<AccessibilityNode> & node)237 void AccessibilityNodeManager::AddNodeWithId(const std::string& key, const RefPtr<AccessibilityNode>& node)
238 {
239 if (!node) {
240 LOGE("add node with id failed");
241 return;
242 }
243 nodeWithIdMap_[key] = node;
244 }
245
AddNodeWithTarget(const std::string & key,const RefPtr<AccessibilityNode> & node)246 void AccessibilityNodeManager::AddNodeWithTarget(const std::string& key, const RefPtr<AccessibilityNode>& node)
247 {
248 if (!node) {
249 LOGE("add node with target failed");
250 return;
251 }
252 nodeWithTargetMap_[key] = node;
253 }
254
AddComposedElement(const std::string & key,const RefPtr<ComposedElement> & node)255 void AccessibilityNodeManager::AddComposedElement(const std::string& key, const RefPtr<ComposedElement>& node)
256 {
257 if (!node) {
258 LOGE("add composed element failed");
259 return;
260 }
261 composedElementIdMap_[key] = node;
262 }
263
RemoveComposedElementById(const std::string & key)264 void AccessibilityNodeManager::RemoveComposedElementById(const std::string& key)
265 {
266 LOGD("remove composed element id:%{public}s", key.c_str());
267 auto it = composedElementIdMap_.find(key);
268 if (it != composedElementIdMap_.end()) {
269 composedElementIdMap_.erase(it);
270 }
271 }
272
GetComposedElementFromPage(NodeId nodeId)273 WeakPtr<ComposedElement> AccessibilityNodeManager::GetComposedElementFromPage(NodeId nodeId)
274 {
275 if (isOhosHostCard_) {
276 nodeId = ConvertToNodeId(nodeId);
277 }
278 auto indexPage = indexPage_.Upgrade();
279 if (nodeId == 0 && indexPage) {
280 auto rootNode = GetRootNodeIdFromPage(indexPage);
281 if (rootNode < 0) {
282 LOGW("Failed to get page root node");
283 return nullptr;
284 }
285 nodeId = rootNode + ROOT_STACK_BASE;
286 }
287
288 const auto itNode = composedElementIdMap_.find(std::to_string(nodeId));
289 if (itNode == composedElementIdMap_.end()) {
290 LOGW("Failed to get ComposedElement from Page, id:%{public}d", nodeId);
291 return nullptr;
292 }
293 return itNode->second;
294 }
295
GetAccessibilityNodeFromPage(NodeId nodeId) const296 RefPtr<AccessibilityNode> AccessibilityNodeManager::GetAccessibilityNodeFromPage(NodeId nodeId) const
297 {
298 if (isOhosHostCard_) {
299 nodeId = ConvertToNodeId(nodeId);
300 }
301 auto indexPage = indexPage_.Upgrade();
302 if (nodeId == 0 && indexPage) {
303 auto rootNode = GetRootNodeIdFromPage(indexPage);
304 if (rootNode < 0) {
305 LOGW("Failed to get page root node");
306 return nullptr;
307 }
308 nodeId = rootNode + ROOT_STACK_BASE;
309 }
310
311 return GetAccessibilityNodeById(nodeId);
312 }
313
GetInspectorNodeById(NodeId nodeId) const314 std::string AccessibilityNodeManager::GetInspectorNodeById(NodeId nodeId) const
315 {
316 auto node = GetAccessibilityNodeFromPage(nodeId);
317 if (!node) {
318 LOGE("AccessibilityNodeManager::GetInspectorNodeById, no node with id:%{public}d", nodeId);
319 return "";
320 }
321 auto jsonNode = JsonUtil::Create(true);
322 jsonNode->Put(INSPECTOR_TYPE, node->GetTag().c_str());
323 jsonNode->Put(INSPECTOR_ID, node->GetNodeId());
324 jsonNode->Put(INSPECTOR_RECT, node->GetRect().ToBounds().c_str());
325 auto result = GetDefaultAttrsByType(node->GetTag(), jsonNode);
326 if (!result) {
327 return jsonNode->ToString();
328 }
329 auto attrJsonNode = jsonNode->GetObject(INSPECTOR_ATTRS);
330 for (const auto& attr : node->GetAttrs()) {
331 if (attrJsonNode->Contains(attr.first)) {
332 attrJsonNode->Replace(attr.first.c_str(), attr.second.c_str());
333 } else {
334 attrJsonNode->Put(attr.first.c_str(), attr.second.c_str());
335 }
336 }
337 auto styleJsonNode = jsonNode->GetObject(INSPECTOR_STYLES);
338 for (const auto& style : node->GetStyles()) {
339 auto styleType = ConvertStrToPropertyType(style.first);
340 if (styleJsonNode->Contains(styleType)) {
341 styleJsonNode->Replace(styleType.c_str(), style.second.c_str());
342 } else {
343 styleJsonNode->Put(styleType.c_str(), style.second.c_str());
344 }
345 }
346 return jsonNode->ToString();
347 }
348
ClearNodeRectInfo(RefPtr<AccessibilityNode> & node,bool isPopDialog)349 void AccessibilityNodeManager::ClearNodeRectInfo(RefPtr<AccessibilityNode>& node, bool isPopDialog)
350 {
351 if (!node) {
352 return;
353 }
354 auto children = node->GetChildList();
355 for (auto it = children.begin(); it != children.end(); it++) {
356 ClearNodeRectInfo(*it, isPopDialog);
357 }
358 #if defined(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
359 if (isPopDialog) {
360 node->SetClearRectInfoFlag(true);
361 } else {
362 node->SetClearRectInfoFlag(false);
363 }
364 #endif
365 }
366
SendAccessibilityAsyncEvent(const AccessibilityEvent & accessibilityEvent)367 void AccessibilityNodeManager::SendAccessibilityAsyncEvent(const AccessibilityEvent& accessibilityEvent) {}
368
GenerateNextAccessibilityId()369 int32_t AccessibilityNodeManager::GenerateNextAccessibilityId()
370 {
371 return g_accessibilityId.fetch_add(1, std::memory_order_relaxed);
372 }
373
374 // combined components which pop up through js api, such as dialog/toast
CreateSpecializedNode(const std::string & tag,int32_t nodeId,int32_t parentNodeId)375 RefPtr<AccessibilityNode> AccessibilityNodeManager::CreateSpecializedNode(
376 const std::string& tag, int32_t nodeId, int32_t parentNodeId)
377 {
378 #if defined(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
379 return nullptr;
380 #endif
381 if (nodeId < ROOT_STACK_BASE) {
382 return nullptr;
383 }
384 return CreateAccessibilityNode(tag, nodeId, parentNodeId, -1);
385 }
386
CreateAccessibilityNode(const std::string & tag,int32_t nodeId,int32_t parentNodeId,int32_t itemIndex)387 RefPtr<AccessibilityNode> AccessibilityNodeManager::CreateAccessibilityNode(
388 const std::string& tag, int32_t nodeId, int32_t parentNodeId, int32_t itemIndex)
389 {
390 if (IsDeclarative()) {
391 return CreateDeclarativeAccessibilityNode(tag, nodeId, parentNodeId, itemIndex);
392 } else {
393 return CreateCommonAccessibilityNode(tag, nodeId, parentNodeId, itemIndex);
394 }
395 }
396
CreateDeclarativeAccessibilityNode(const std::string & tag,int32_t nodeId,int32_t parentNodeId,int32_t itemIndex)397 RefPtr<AccessibilityNode> AccessibilityNodeManager::CreateDeclarativeAccessibilityNode(
398 const std::string& tag, int32_t nodeId, int32_t parentNodeId, int32_t itemIndex)
399 {
400 LOGD("create AccessibilityNode %{public}s, id %{public}d, parent id %{public}d, itemIndex %{public}d", tag.c_str(),
401 nodeId, parentNodeId, itemIndex);
402 RefPtr<AccessibilityNode> parentNode;
403 if (parentNodeId != -1) {
404 parentNode = GetAccessibilityNodeById(parentNodeId);
405 } else {
406 // create accessibility root stack node
407 auto rootStackId = rootNodeId_ + ROOT_STACK_BASE;
408 parentNode = GetAccessibilityNodeById(rootStackId);
409 if (!parentNode) {
410 parentNode = AceType::MakeRefPtr<AccessibilityNode>(rootStackId, ROOT_STACK_TAG);
411 std::lock_guard<std::mutex> lock(mutex_);
412 auto result = accessibilityNodes_.try_emplace(rootStackId, parentNode);
413
414 if (!result.second) {
415 LOGW("the accessibility node has already in the map");
416 return nullptr;
417 }
418 }
419 }
420 auto accessibilityNode = GetAccessibilityNodeById(nodeId);
421 if (!accessibilityNode) {
422 accessibilityNode = AceType::MakeRefPtr<AccessibilityNode>(nodeId, tag);
423 {
424 std::lock_guard<std::mutex> lock(mutex_);
425 auto result = accessibilityNodes_.try_emplace(nodeId, accessibilityNode);
426 if (!result.second) {
427 LOGW("the accessibility node has already in the map");
428 return nullptr;
429 }
430 }
431 }
432 accessibilityNode->SetTag(tag);
433 accessibilityNode->SetIsRootNode(nodeId == rootNodeId_);
434 accessibilityNode->SetPageId(rootNodeId_ - DOM_ROOT_NODE_ID_BASE);
435 accessibilityNode->SetFocusableState(true);
436 if (parentNode) {
437 accessibilityNode->SetParentNode(parentNode);
438 accessibilityNode->Mount(itemIndex);
439 }
440 return accessibilityNode;
441 }
442
CreateCommonAccessibilityNode(const std::string & tag,int32_t nodeId,int32_t parentNodeId,int32_t itemIndex)443 RefPtr<AccessibilityNode> AccessibilityNodeManager::CreateCommonAccessibilityNode(
444 const std::string& tag, int32_t nodeId, int32_t parentNodeId, int32_t itemIndex)
445 {
446 LOGD("create AccessibilityNode %{public}s, id %{public}d, parent id %{public}d, itemIndex %{public}d", tag.c_str(),
447 nodeId, parentNodeId, itemIndex);
448 RefPtr<AccessibilityNode> parentNode;
449 if (parentNodeId != -1) {
450 parentNode = GetAccessibilityNodeById(parentNodeId);
451 if (!parentNode) {
452 LOGD("Parent node %{private}d not exists", parentNodeId);
453 EventReport::SendAccessibilityException(AccessibilityExcepType::CREATE_ACCESSIBILITY_NODE_ERR);
454 return nullptr;
455 }
456 } else {
457 // create accessibility root stack node
458 auto rootStackId = rootNodeId_ + ROOT_STACK_BASE;
459 parentNode = GetAccessibilityNodeById(rootStackId);
460 if (!parentNode) {
461 parentNode = AceType::MakeRefPtr<AccessibilityNode>(rootStackId, ROOT_STACK_TAG);
462 std::lock_guard<std::mutex> lock(mutex_);
463 auto result = accessibilityNodes_.try_emplace(rootStackId, parentNode);
464
465 if (!result.second) {
466 LOGW("the accessibility node has already in the map");
467 return nullptr;
468 }
469 }
470 }
471
472 auto accessibilityNode = AceType::MakeRefPtr<AccessibilityNode>(nodeId, tag);
473 accessibilityNode->SetIsRootNode(nodeId == rootNodeId_);
474 accessibilityNode->SetPageId(rootNodeId_ - DOM_ROOT_NODE_ID_BASE);
475 accessibilityNode->SetParentNode(parentNode);
476 accessibilityNode->Mount(itemIndex);
477 {
478 std::lock_guard<std::mutex> lock(mutex_);
479 auto result = accessibilityNodes_.try_emplace(nodeId, accessibilityNode);
480
481 if (!result.second) {
482 LOGW("the accessibility node has already in the map");
483 return nullptr;
484 }
485 }
486 return accessibilityNode;
487 }
488
GetAccessibilityNodeById(NodeId nodeId) const489 RefPtr<AccessibilityNode> AccessibilityNodeManager::GetAccessibilityNodeById(NodeId nodeId) const
490 {
491 std::lock_guard<std::mutex> lock(mutex_);
492 const auto itNode = accessibilityNodes_.find(nodeId);
493 if (itNode == accessibilityNodes_.end()) {
494 return nullptr;
495 }
496 return itNode->second;
497 }
498
RemoveAccessibilityNodes(RefPtr<AccessibilityNode> & node)499 void AccessibilityNodeManager::RemoveAccessibilityNodes(RefPtr<AccessibilityNode>& node)
500 {
501 if (!node) {
502 return;
503 }
504 auto children = node->GetChildList();
505 for (auto it = children.begin(); it != children.end();) {
506 RemoveAccessibilityNodes(*it++);
507 }
508 auto parentId = node->GetParentId();
509 RefPtr<AccessibilityNode> parentNode;
510 if (parentId != -1) {
511 parentNode = GetAccessibilityNodeById(parentId);
512 if (parentNode) {
513 parentNode->RemoveNode(node);
514 }
515 }
516 LOGD("remove accessibility node %{public}d, remain num %{public}zu", node->GetNodeId(), accessibilityNodes_.size());
517 std::lock_guard<std::mutex> lock(mutex_);
518 accessibilityNodes_.erase(node->GetNodeId());
519 RemoveVisibleChangeNode(node->GetNodeId());
520 }
521
RemoveAccessibilityNodeById(NodeId nodeId)522 void AccessibilityNodeManager::RemoveAccessibilityNodeById(NodeId nodeId)
523 {
524 auto accessibilityNode = GetAccessibilityNodeById(nodeId);
525 if (!accessibilityNode) {
526 LOGW("the accessibility node %{public}d is not in the map", nodeId);
527 return;
528 }
529 RemoveAccessibilityNodes(accessibilityNode);
530 }
531
ClearPageAccessibilityNodes(int32_t pageId)532 void AccessibilityNodeManager::ClearPageAccessibilityNodes(int32_t pageId)
533 {
534 auto rootNodeId = pageId + ROOT_STACK_BASE;
535 auto accessibilityNode = GetAccessibilityNodeById(rootNodeId);
536 if (!accessibilityNode) {
537 LOGW("the accessibility node %{public}d is not in the map", rootNodeId);
538 return;
539 }
540 RemoveAccessibilityNodes(accessibilityNode);
541 }
542
TriggerVisibleChangeEvent()543 void AccessibilityNodeManager::TriggerVisibleChangeEvent()
544 {
545 if (visibleChangeNodes_.empty()) {
546 return;
547 }
548 for (auto& visibleChangeNode : visibleChangeNodes_) {
549 auto visibleNodeId = visibleChangeNode.first;
550 auto accessibilityNode = GetAccessibilityNodeById(visibleNodeId);
551 if (!accessibilityNode) {
552 LOGI("No this accessibility node.");
553 continue;
554 }
555 // IntersectionObserver observes size exclude margin.
556 auto marginSize = accessibilityNode->GetMarginSize();
557 auto visibleRect = accessibilityNode->GetRect() - marginSize;
558 auto globalRect = accessibilityNode->GetGlobalRect() - marginSize;
559 auto pipeline = context_.Upgrade();
560 if (pipeline) {
561 pipeline->GetBoundingRectData(visibleNodeId, globalRect);
562 globalRect = globalRect * pipeline->GetViewScale() - marginSize;
563 }
564 auto& nodeCallbackInfoList = visibleChangeNode.second;
565 for (auto& nodeCallbackInfo : nodeCallbackInfoList) {
566 if (!globalRect.IsValid() || !accessibilityNode->GetVisible()) {
567 if (nodeCallbackInfo.currentVisibleType) {
568 nodeCallbackInfo.currentVisibleType = false;
569 if (nodeCallbackInfo.callback) {
570 nodeCallbackInfo.callback(false, 0.0);
571 }
572 }
573 continue;
574 }
575 auto visibleRatio = visibleRect.Width() * visibleRect.Height() / (globalRect.Width() * globalRect.Height());
576 visibleRatio = std::clamp(visibleRatio, 0.0, 1.0);
577 if (GreatNotEqual(visibleRatio, nodeCallbackInfo.visibleRatio) && !nodeCallbackInfo.currentVisibleType) {
578 LOGI("Fire visible event %{public}lf", visibleRatio);
579 nodeCallbackInfo.currentVisibleType = true;
580 if (nodeCallbackInfo.callback) {
581 nodeCallbackInfo.callback(true, visibleRatio);
582 }
583 }
584 if (LessOrEqual(visibleRatio, nodeCallbackInfo.visibleRatio) && nodeCallbackInfo.currentVisibleType) {
585 LOGI("Fire invisible event %{public}lf", visibleRatio);
586 nodeCallbackInfo.currentVisibleType = false;
587 if (nodeCallbackInfo.callback) {
588 nodeCallbackInfo.callback(false, visibleRatio);
589 }
590 }
591 }
592 }
593 }
594
AddVisibleChangeNode(NodeId nodeId,double ratio,VisibleRatioCallback callback)595 void AccessibilityNodeManager::AddVisibleChangeNode(NodeId nodeId, double ratio, VisibleRatioCallback callback)
596 {
597 VisibleCallbackInfo info;
598 info.callback = callback;
599 info.visibleRatio = ratio;
600 info.currentVisibleType = false;
601 auto iter = visibleChangeNodes_.find(nodeId);
602 if (iter != visibleChangeNodes_.end()) {
603 auto& callbackList = visibleChangeNodes_[nodeId];
604 callbackList.emplace_back(info);
605 } else {
606 std::list<VisibleCallbackInfo> callbackList;
607 callbackList.emplace_back(info);
608 visibleChangeNodes_[nodeId] = callbackList;
609 }
610 }
611
RemoveVisibleChangeNode(NodeId nodeId)612 void AccessibilityNodeManager::RemoveVisibleChangeNode(NodeId nodeId)
613 {
614 auto key = visibleChangeNodes_.find(nodeId);
615 if (key != visibleChangeNodes_.end()) {
616 visibleChangeNodes_.erase(key);
617 }
618 }
619
TrySaveTargetAndIdNode(const std::string & id,const std::string & target,const RefPtr<AccessibilityNode> & node)620 void AccessibilityNodeManager::TrySaveTargetAndIdNode(
621 const std::string& id, const std::string& target, const RefPtr<AccessibilityNode>& node)
622 {
623 if (!id.empty()) {
624 AddNodeWithId(id, node);
625 }
626
627 if (!target.empty()) {
628 AddNodeWithTarget(target, node);
629 }
630 }
631
DumpHandleEvent(const std::vector<std::string> & params)632 void AccessibilityNodeManager::DumpHandleEvent(const std::vector<std::string>& params) {}
633
DumpProperty(const std::vector<std::string> & params)634 void AccessibilityNodeManager::DumpProperty(const std::vector<std::string>& params) {}
635
DumpComposedElementsToJson() const636 std::unique_ptr<JsonValue> AccessibilityNodeManager::DumpComposedElementsToJson() const
637 {
638 auto json = JsonUtil::Create(true);
639 auto infos = JsonUtil::CreateArray(false);
640 for (auto& [id, element] : composedElementIdMap_) {
641 auto inspector = element.Upgrade();
642 if (inspector) {
643 auto info = JsonUtil::Create(false);
644 info->Put("id", id.c_str());
645 info->Put("type", TypeInfoHelper::TypeName(*inspector));
646 infos->Put(info);
647 }
648 }
649 json->Put("inspectors", infos);
650 return json;
651 }
652
DumpComposedElementToJson(NodeId nodeId)653 std::unique_ptr<JsonValue> AccessibilityNodeManager::DumpComposedElementToJson(NodeId nodeId)
654 {
655 auto composedElement = GetComposedElementFromPage(nodeId);
656 auto inspector = AceType::DynamicCast<V2::InspectorComposedElement>(composedElement.Upgrade());
657 if (!inspector) {
658 LOGE("this is not Inspector composed element");
659 return nullptr;
660 }
661 return inspector->ToJsonObject();
662 }
663
SetCardViewParams(const std::string & key,bool focus)664 void AccessibilityNodeManager::SetCardViewParams(const std::string& key, bool focus) {}
665
SetCardViewPosition(int id,float offsetX,float offsetY)666 void AccessibilityNodeManager::SetCardViewPosition(int id, float offsetX, float offsetY)
667 {
668 cardOffset_ = Offset(offsetX, offsetY);
669 if (id < 0 || id > CARD_MAX_AGP_ID) {
670 cardId_ = 0;
671 } else {
672 cardId_ = id;
673 }
674 isOhosHostCard_ = true;
675 LOGD(
676 "setcardview id=%{public}d offsetX=%{public}f, offsetY=%{public}f", id, cardOffset_.GetX(), cardOffset_.GetY());
677 }
678
UpdateEventTarget(NodeId id,BaseEventInfo & info)679 void AccessibilityNodeManager::UpdateEventTarget(NodeId id, BaseEventInfo& info)
680 {
681 auto composedElement = GetComposedElementFromPage(id);
682 auto inspector = AceType::DynamicCast<V2::InspectorComposedElement>(composedElement.Upgrade());
683 if (!inspector) {
684 LOGE("this is not Inspector composed element");
685 return;
686 }
687 auto rectInLocal = inspector->GetRenderRectInLocal();
688 auto rectInGlobal = inspector->GetRenderRect();
689 auto marginLeft = inspector->GetMargin(AnimatableType::PROPERTY_MARGIN_LEFT).ConvertToPx();
690 auto marginRight = inspector->GetMargin(AnimatableType::PROPERTY_MARGIN_RIGHT).ConvertToPx();
691 auto marginTop = inspector->GetMargin(AnimatableType::PROPERTY_MARGIN_TOP).ConvertToPx();
692 auto marginBottom = inspector->GetMargin(AnimatableType::PROPERTY_MARGIN_BOTTOM).ConvertToPx();
693 auto& target = info.GetTargetWichModify();
694 auto Localoffset = rectInLocal.GetOffset();
695 target.area.SetOffset(DimensionOffset(Offset(Localoffset.GetX() + marginLeft, Localoffset.GetY() + marginTop)));
696 auto globalOffset = rectInGlobal.GetOffset();
697 target.origin =
698 DimensionOffset(Offset(globalOffset.GetX() - Localoffset.GetX(), globalOffset.GetY() - Localoffset.GetY()));
699 target.area.SetWidth(Dimension(rectInLocal.Width() - marginLeft - marginRight));
700 target.area.SetHeight(Dimension(rectInLocal.Height() - marginTop - marginBottom));
701 }
702
IsDeclarative()703 bool AccessibilityNodeManager::IsDeclarative()
704 {
705 auto context = context_.Upgrade();
706 if (!context) {
707 return false;
708 }
709
710 return context->GetIsDeclarative();
711 }
712
GetDefaultAttrsByType(const std::string & type,std::unique_ptr<JsonValue> & jsonDefaultAttrs)713 bool AccessibilityNodeManager::GetDefaultAttrsByType(
714 const std::string& type, std::unique_ptr<JsonValue>& jsonDefaultAttrs)
715 {
716 NodeId nodeId = -1;
717 RefPtr<InspectNode> inspectNode;
718 int64_t creatorIndex = BinarySearchFindIndex(inspectNodeCreators, ArraySize(inspectNodeCreators), type.c_str());
719 if (creatorIndex >= 0) {
720 inspectNode = inspectNodeCreators[creatorIndex].value(nodeId, type);
721 } else {
722 LOGW("node type %{public}s is invalid", type.c_str());
723 return false;
724 }
725 inspectNode->InitCommonStyles();
726 inspectNode->PackAttrAndStyle();
727 inspectNode->SetAllAttr(jsonDefaultAttrs, INSPECTOR_ATTRS);
728 inspectNode->SetAllStyle(jsonDefaultAttrs, INSPECTOR_STYLES);
729 return true;
730 }
731
DumpTree(int32_t depth,NodeId nodeID)732 void AccessibilityNodeManager::DumpTree(int32_t depth, NodeId nodeID)
733 {
734 if (!DumpLog::GetInstance().GetDumpFile()) {
735 LOGE("AccessibilityNodeManager::GetDumpFile fail:%p", &(DumpLog::GetInstance()));
736 return;
737 }
738
739 auto node = GetAccessibilityNodeFromPage(nodeID);
740 if (!node) {
741 DumpLog::GetInstance().Print("Error: failed to get accessibility node with ID " + std::to_string(nodeID));
742 return;
743 }
744
745 std::string info = "ID:";
746 info.append(std::to_string(node->GetNodeId()));
747 if (!node->GetText().empty()) {
748 info.append(" ");
749 info.append("text:");
750 info.append(node->GetText());
751 }
752 DumpLog::GetInstance().AddDesc(info);
753 DumpLog::GetInstance().AddDesc("width: " + std::to_string(node->GetWidth()));
754 DumpLog::GetInstance().AddDesc("height: " + std::to_string(node->GetHeight()));
755 DumpLog::GetInstance().AddDesc("visible: " + std::to_string(node->GetShown() && node->GetVisible()));
756 DumpLog::GetInstance().Print(depth, node->GetTag(), node->GetChildList().size());
757 for (const auto& item : node->GetChildList()) {
758 DumpTree(depth + 1, item->GetNodeId());
759 }
760 }
761
DumpTree(int32_t depth,NodeId nodeID,std::vector<std::string> & infos)762 void AccessibilityNodeManager::DumpTree(int32_t depth, NodeId nodeID, std::vector<std::string>& infos)
763 {
764 auto node = GetAccessibilityNodeFromPage(nodeID);
765 if (!node) {
766 DumpLog::GetInstance().PrintToString(
767 "Error: failed to get accessibility node with ID " + std::to_string(nodeID), infos);
768 return;
769 }
770
771 DumpLog::GetInstance().AddDesc("ID: " + std::to_string(node->GetNodeId()));
772 DumpLog::GetInstance().AddDesc("text: " + node->GetText());
773 DumpLog::GetInstance().AddDesc("top: " + std::to_string(node->GetTop() + SystemProperties::GetWindowPosY()));
774 DumpLog::GetInstance().AddDesc("left: " + std::to_string(node->GetLeft() + SystemProperties::GetWindowPosX()));
775 DumpLog::GetInstance().AddDesc("width: " + std::to_string(node->GetWidth()));
776 DumpLog::GetInstance().AddDesc("height: " + std::to_string(node->GetHeight()));
777 DumpLog::GetInstance().AddDesc("visible: " + std::to_string(node->GetShown() && node->GetVisible()));
778 DumpLog::GetInstance().PrintToString(depth, node->GetTag(), node->GetChildList().size(), infos);
779 for (const auto& item : node->GetChildList()) {
780 DumpTree(depth + 1, item->GetNodeId(), infos);
781 }
782 }
783
784 } // namespace OHOS::Ace::Framework
785