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