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