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