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