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