* Copyright (c) 2023-2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "adapter/ios/osal/accessibility_manager_impl.h"
#include <algorithm>
#include <cstdio>
#include <mutex>
#include <variant>
#include "adapter/ios/entrance/accessibility/AceAccessibilityBridge.h"
#include "core/components_ng/property/accessibility_property.h"
#include "core/components_ng/pattern/overlay/overlay_manager.h"
#include "core/components_ng/pattern/stage/stage_manager.h"
using namespace OHOS::Accessibility;
using namespace std;
namespace OHOS::Ace::Framework {
AccessibilityManagerImpl::AccessibilityEventCallback AccessibilityManagerImpl::uiTestEventCallback_ = nullptr;
std::atomic<int32_t> AccessibilityManagerImpl::testForceEnableCount_ { 0 };
static std::mutex g_uiTestEventCallbackMutex;
static std::mutex g_accessibilityStateMutex;
static std::atomic<bool> g_lastSystemA11yState { false };
void AccessibilityManagerImpl::RefreshEffectiveAccessibilityState(bool isSystemEnabled)
{
std::lock_guard<std::mutex> lock(g_accessibilityStateMutex);
g_lastSystemA11yState.store(isSystemEnabled);
bool effectiveEnabled = isSystemEnabled || (AccessibilityManagerImpl::testForceEnableCount_.load() > 0);
AceApplicationInfo::GetInstance().SetAccessibilityEnabled(effectiveEnabled);
}
void AccessibilityManagerImpl::RefreshEffectiveAccessibilityState()
{
bool isSystemEnabled = g_lastSystemA11yState.load();
RefreshEffectiveAccessibilityState(isSystemEnabled);
}
constexpr int32_t INVALID_PARENT_ID = -2100000;
constexpr int32_t ELEMENT_MOVE_BIT = 40;
constexpr int32_t CONT_SPLIT_ID = -1;
constexpr int64_t MAX_ELEMENT_ID = 0xFFFFFFFFFF;
constexpr int32_t ROOT_DECOR_BASE = 3100000;
constexpr int32_t ROOT_STACK_BASE = 1100000;
constexpr int32_t CARD_NODE_ID_RATION = 10000;
constexpr int32_t CARD_ROOT_NODE_ID_RATION = 1000;
constexpr int32_t CARD_BASE = 100000;
constexpr int32_t DELAY_SEND_EVENT_MILLISECOND = 20;
constexpr int MAX_TIME = 500;
const char STRING_DIR_FORWARD[] = "forward";
const char STRING_DIR_BACKWARD[] = "backward";
const char ACCESSIBILITY_FOCUSED_EVENT[] = "accessibilityfocus";
const char ACCESSIBILITY_CLEAR_FOCUS_EVENT[] = "accessibilityclearfocus";
const char SCROLL_END_EVENT[] = "scrollend";
const char SIDEBARCONTAINER_TAG[] = "SideBarContainer";
const char LIST_TAG[] = "List";
const std::string ACTION_ARGU_SCROLL_STUB = "scrolltype";
struct ActionTable {
AceAction aceAction;
ActionType action;
};
AccessibilityManagerImpl::~AccessibilityManagerImpl()
{
UnSubscribeStateObserver();
DeregisterInteractionOperation();
}
struct AccessibilityActionParam {
RefPtr<NG::AccessibilityProperty> accessibilityProperty;
std::string setTextArgument = "";
int32_t setSelectionStart = DEFAULT_SELECTION;
int32_t setSelectionEnd = DEFAULT_SELECTION;
bool setSelectionDir = false;
int32_t setCursorIndex = DEFAULT_ElEMENTID;
TextMoveUnit moveUnit = TextMoveUnit::STEP_CHARACTER;
AccessibilityScrollType scrollType = AccessibilityScrollType::SCROLL_DEFAULT;
};
const std::map<Accessibility::ActionType, std::function<bool(const AccessibilityActionParam& param)>> ACTIONS = {
{ ActionType::ACCESSIBILITY_ACTION_SCROLL_FORWARD,
[](const AccessibilityActionParam& param) {
return param.accessibilityProperty->ActActionScrollForward(param.scrollType);
} },
{ ActionType::ACCESSIBILITY_ACTION_SCROLL_BACKWARD,
[](const AccessibilityActionParam& param) {
return param.accessibilityProperty->ActActionScrollBackward(param.scrollType);
} },
{ ActionType::ACCESSIBILITY_ACTION_SET_TEXT,
[](const AccessibilityActionParam& param) {
return param.accessibilityProperty->ActActionSetText(param.setTextArgument);
} },
{ ActionType::ACCESSIBILITY_ACTION_SET_SELECTION,
[](const AccessibilityActionParam& param) {
return param.accessibilityProperty->ActActionSetSelection(
param.setSelectionStart, param.setSelectionEnd, param.setSelectionDir);
} },
{ ActionType::ACCESSIBILITY_ACTION_COPY,
[](const AccessibilityActionParam& param) { return param.accessibilityProperty->ActActionCopy(); } },
{ ActionType::ACCESSIBILITY_ACTION_CUT,
[](const AccessibilityActionParam& param) { return param.accessibilityProperty->ActActionCut(); } },
{ ActionType::ACCESSIBILITY_ACTION_PASTE,
[](const AccessibilityActionParam& param) { return param.accessibilityProperty->ActActionPaste(); } },
{ ActionType::ACCESSIBILITY_ACTION_CLICK,
[](const AccessibilityActionParam& param) { return param.accessibilityProperty->ActActionClick(); } },
{ ActionType::ACCESSIBILITY_ACTION_LONG_CLICK,
[](const AccessibilityActionParam& param) { return param.accessibilityProperty->ActActionLongClick(); } },
{ ActionType::ACCESSIBILITY_ACTION_SELECT,
[](const AccessibilityActionParam& param) { return param.accessibilityProperty->ActActionSelect(); } },
{ ActionType::ACCESSIBILITY_ACTION_CLEAR_SELECTION,
[](const AccessibilityActionParam& param) { return param.accessibilityProperty->ActActionClearSelection(); } },
{ ActionType::ACCESSIBILITY_ACTION_NEXT_TEXT,
[](const AccessibilityActionParam& param) {
return param.accessibilityProperty->ActActionMoveText(static_cast<int32_t>(param.moveUnit), true);
} },
{ ActionType::ACCESSIBILITY_ACTION_PREVIOUS_TEXT,
[](const AccessibilityActionParam& param) {
return param.accessibilityProperty->ActActionMoveText(static_cast<int32_t>(param.moveUnit), false);
} },
{ ActionType::ACCESSIBILITY_ACTION_SET_CURSOR_POSITION,
[](const AccessibilityActionParam& param) {
return param.accessibilityProperty->ActActionSetIndex(static_cast<int32_t>(param.setCursorIndex));
} },
};
RefPtr<AccessibilityNodeManager> AccessibilityNodeManager::Create()
{
return AceType::MakeRefPtr<AccessibilityManagerImpl>();
}
RefPtr<NG::FrameNode> GetInspectorById(const RefPtr<NG::FrameNode>& root, int32_t id)
{
CHECK_NULL_RETURN(root, nullptr);
std::queue<RefPtr<NG::UINode>> nodes;
nodes.push(root);
RefPtr<NG::FrameNode> frameNode;
while (!nodes.empty()) {
auto current = nodes.front();
nodes.pop();
frameNode = AceType::DynamicCast<NG::FrameNode>(current);
if (frameNode != nullptr) {
if (id == frameNode->GetAccessibilityId()) {
return frameNode;
}
}
const auto& children = current->GetChildren();
for (const auto& child : children) {
nodes.push(child);
}
}
return nullptr;
}
void GetFrameNodeChildren(const RefPtr<NG::UINode>& uiNode, std::vector<int32_t>& children, int32_t pageId)
{
auto frameNode = AceType::DynamicCast<NG::FrameNode>(uiNode);
if (frameNode != nullptr) {
if (!frameNode->IsFirstVirtualNode()) {
CHECK_NULL_VOID(frameNode->IsActive());
}
if (uiNode->GetTag() == "stage") {
} else if (uiNode->GetTag() == "page") {
if (uiNode->GetPageId() != pageId) {
return;
}
} else if (!frameNode->IsInternal() || frameNode->IsFirstVirtualNode()) {
children.emplace_back(uiNode->GetAccessibilityId());
return;
}
auto accessibilityProperty = frameNode->GetAccessibilityProperty<NG::AccessibilityProperty>();
CHECK_NULL_VOID(accessibilityProperty);
auto uiVirtualNode = accessibilityProperty->GetAccessibilityVirtualNode();
if (uiVirtualNode != nullptr) {
auto virtualNode = AceType::DynamicCast<NG::FrameNode>(uiVirtualNode);
if (virtualNode != nullptr) {
GetFrameNodeChildren(virtualNode, children, pageId);
return;
}
}
}
for (const auto& frameChild : uiNode->GetChildren(true)) {
GetFrameNodeChildren(frameChild, children, pageId);
}
}
void DumpTreeNG(const RefPtr<NG::FrameNode>& parent, int32_t depth, NodeId nodeID, int32_t pageId)
{
auto node = GetInspectorById(parent, nodeID);
if (!node) {
DumpLog::GetInstance().Print("Error: failed to get accessibility node with ID " + std::to_string(nodeID));
return;
}
if (!node->IsActive()) {
return;
}
NG::RectF rect = node->GetTransformRectRelativeToWindow();
DumpLog::GetInstance().AddDesc("ID: " + std::to_string(node->GetAccessibilityId()));
DumpLog::GetInstance().AddDesc("compid: " + node->GetInspectorId().value_or(""));
DumpLog::GetInstance().AddDesc("text: " + node->GetAccessibilityProperty<NG::AccessibilityProperty>()->GetText());
DumpLog::GetInstance().AddDesc("top: " + std::to_string(rect.Top()));
DumpLog::GetInstance().AddDesc("left: " + std::to_string(rect.Left()));
DumpLog::GetInstance().AddDesc("width: " + std::to_string(rect.Width()));
DumpLog::GetInstance().AddDesc("height: " + std::to_string(rect.Height()));
DumpLog::GetInstance().AddDesc("visible: " + std::to_string(node->IsVisible()));
auto gestureEventHub = node->GetEventHub<NG::EventHub>()->GetGestureEventHub();
DumpLog::GetInstance().AddDesc(
"clickable: " + std::to_string(gestureEventHub ? gestureEventHub->IsAccessibilityClickable() : false));
DumpLog::GetInstance().AddDesc(
"checkable: " + std::to_string(node->GetAccessibilityProperty<NG::AccessibilityProperty>()->IsCheckable()));
std::vector<int32_t> children;
for (const auto& item : node->GetChildren()) {
GetFrameNodeChildren(item, children, pageId);
}
DumpLog::GetInstance().Print(depth, node->GetTag(), children.size());
for (auto nodeId : children) {
DumpTreeNG(node, depth + 1, nodeId, pageId);
}
}
int64_t GetParentId(const RefPtr<NG::UINode>& uiNode)
{
if (AceType::InstanceOf<NG::FrameNode>(uiNode)) {
if (AceType::DynamicCast<NG::FrameNode>(uiNode)->IsFirstVirtualNode()) {
auto weakNode = AceType::DynamicCast<NG::FrameNode>(uiNode)->GetVirtualNodeParent();
auto refNode = weakNode.Upgrade();
return refNode == nullptr ? INVALID_PARENT_ID : refNode->GetAccessibilityId();
}
}
auto parent = uiNode->GetParent();
while (parent) {
if (AceType::InstanceOf<NG::FrameNode>(parent)) {
if ((parent->GetTag() == V2::PAGE_ETS_TAG) || (parent->GetTag() == V2::STAGE_ETS_TAG) ||
AceType::DynamicCast<NG::FrameNode>(parent)->CheckAccessibilityLevelNo()) {
parent = parent->GetParent();
continue;
}
return parent->GetAccessibilityId();
}
parent = parent->GetParent();
}
return INVALID_PARENT_ID;
}
int64_t ConvertToCardAccessibilityId(int64_t nodeId, int64_t cardId, int64_t rootNodeId)
{
int64_t result = DEFAULT_ID;
if (nodeId == rootNodeId + ROOT_STACK_BASE) {
result = cardId * CARD_BASE + (static_cast<int64_t>(nodeId / CARD_BASE)) * CARD_ROOT_NODE_ID_RATION +
nodeId % CARD_BASE;
} else {
result = cardId * CARD_BASE + (static_cast<int64_t>(nodeId / DOM_ROOT_NODE_ID_BASE)) * CARD_NODE_ID_RATION +
nodeId % DOM_ROOT_NODE_ID_BASE;
}
return result;
}
inline std::string ChildernToString(const RefPtr<NG::FrameNode>& frameNode, int32_t pageId)
{
std::string ids;
std::vector<int32_t> children;
for (const auto& item : frameNode->GetChildren()) {
GetFrameNodeChildren(item, children, pageId);
}
for (auto child : children) {
if (!ids.empty()) {
ids.append(",");
}
ids.append(std::to_string(child));
}
return ids;
}
inline std::string BoolToString(bool tag)
{
return tag ? "true" : "false";
}
inline bool IsPopupSupported(const RefPtr<NG::FrameNode>& frameNode, const RefPtr<NG::PipelineContext>& pipeline)
{
CHECK_NULL_RETURN(pipeline, false);
auto overlayManager = pipeline->GetOverlayManager();
if (overlayManager) {
return overlayManager->HasPopupInfo(frameNode->GetId());
}
return false;
}
inline bool IsPopupSupported(const RefPtr<NG::PipelineContext>& pipeline, int64_t nodeId)
{
CHECK_NULL_RETURN(pipeline, false);
auto overlayManager = pipeline->GetOverlayManager();
if (overlayManager) {
return overlayManager->HasPopupInfo(nodeId);
}
return false;
}
bool FindFrameNodeByAccessibilityId(int64_t id, const std::list<RefPtr<NG::UINode>>& children,
std::queue<NG::UINode*>& nodes, RefPtr<NG::FrameNode>& result)
{
NG::FrameNode* frameNode = nullptr;
for (const auto& child : children) {
frameNode = AceType::DynamicCast<NG::FrameNode>(Referenced::RawPtr(child));
if (frameNode != nullptr && !frameNode->CheckAccessibilityLevelNo()) {
if (frameNode->GetAccessibilityId() == id) {
result = AceType::DynamicCast<NG::FrameNode>(child);
return true;
}
}
nodes.push(Referenced::RawPtr(child));
}
return false;
}
RefPtr<NG::FrameNode> GetFramenodeByAccessibilityId(const RefPtr<NG::FrameNode>& root, int64_t id)
{
CHECK_NULL_RETURN(root, nullptr);
if (root->GetAccessibilityId() == id) {
return root;
}
std::queue<NG::UINode*> nodes;
nodes.push(Referenced::RawPtr(root));
NG::UINode* virtualNode = nullptr;
RefPtr<NG::FrameNode> frameNodeResult = nullptr;
while (!nodes.empty()) {
auto current = nodes.front();
nodes.pop();
if (current->HasVirtualNodeAccessibilityProperty()) {
auto fnode = AceType::DynamicCast<NG::FrameNode>(current);
auto property = fnode->GetAccessibilityProperty<NG::AccessibilityProperty>();
const auto& children = std::list<RefPtr<NG::UINode>> { property->GetAccessibilityVirtualNode() };
if (FindFrameNodeByAccessibilityId(id, children, nodes, frameNodeResult)) {
return frameNodeResult;
}
} else {
const auto& children = current->GetChildren(true);
if (FindFrameNodeByAccessibilityId(id, children, nodes, frameNodeResult)) {
return frameNodeResult;
}
}
}
return nullptr;
}
inline std::string ConvertInputTypeToString(int32_t inputType)
{
static std::vector<std::string> sInputTypes { "default", "text", "email", "date", "time", "number", "password" };
if (inputType < 0 || inputType >= static_cast<int32_t>(sInputTypes.size())) {
return "none";
}
return sInputTypes[inputType];
}
bool RequestFocus(RefPtr<NG::FrameNode>& frameNode)
{
auto focusHub = frameNode->GetFocusHub();
CHECK_NULL_RETURN(focusHub, false);
return focusHub->RequestFocusImmediately();
}
bool LostFocus(const RefPtr<NG::FrameNode>& frameNode)
{
CHECK_NULL_RETURN(frameNode, false);
auto focusHub = frameNode->GetFocusHub();
CHECK_NULL_RETURN(focusHub, false);
focusHub->LostFocus();
return true;
}
bool ActClick(RefPtr<NG::FrameNode>& frameNode)
{
auto gesture = frameNode->GetEventHub<NG::EventHub>()->GetGestureEventHub();
CHECK_NULL_RETURN(gesture, false);
return gesture->ActClick();
}
bool ActLongClick(RefPtr<NG::FrameNode>& frameNode)
{
auto gesture = frameNode->GetEventHub<NG::EventHub>()->GetGestureEventHub();
CHECK_NULL_RETURN(gesture, false);
return gesture->ActLongClick();
}
void ClearAccessibilityFocus(const RefPtr<NG::FrameNode>& root, int64_t focusNodeId)
{
auto oldFocusNode = GetFramenodeByAccessibilityId(root, focusNodeId);
CHECK_NULL_VOID(oldFocusNode);
if (oldFocusNode->GetTag() != V2::WEB_CORE_TAG) {
oldFocusNode->GetRenderContext()->UpdateAccessibilityFocus(false);
}
}
bool ActAccessibilityFocus(int64_t elementId, RefPtr<NG::FrameNode>& frameNode, RefPtr<NG::PipelineContext>& context,
int64_t& currentFocusNodeId, bool isNeedClear)
{
CHECK_NULL_RETURN(frameNode, false);
if (isNeedClear) {
if (elementId != currentFocusNodeId) {
return false;
}
currentFocusNodeId = DEFAULT_ElEMENTID;
return true;
}
if (elementId == currentFocusNodeId) {
return false;
}
Framework::ClearAccessibilityFocus(context->GetRootElement(), currentFocusNodeId);
currentFocusNodeId = frameNode->GetAccessibilityId();
auto accessibilityProperty = frameNode->GetAccessibilityProperty<NG::AccessibilityProperty>();
CHECK_NULL_RETURN(accessibilityProperty, false);
accessibilityProperty->OnAccessibilityFocusCallback(true);
return true;
}
void stringToLower(std::string& str)
{
std::transform(str.begin(), str.end(), str.begin(), [](char& c) { return std::tolower(c); });
}
bool conversionDirection(std::string dir)
{
stringToLower(dir);
return dir.compare(STRING_DIR_FORWARD) == DEFAULT_ID ? true : false;
}
static std::string ConvertActionTypeToString(AceAction action)
{
switch (action) {
case AceAction::ACTION_NONE:
return "none";
case AceAction::GLOBAL_ACTION_BACK:
return "back";
case AceAction::CUSTOM_ACTION:
return "custom action";
case AceAction::ACTION_CLICK:
return "click";
case AceAction::ACTION_LONG_CLICK:
return "long click";
case AceAction::ACTION_SCROLL_FORWARD:
return "scroll forward";
case AceAction::ACTION_SCROLL_BACKWARD:
return "scroll backward";
case AceAction::ACTION_FOCUS:
return "focus";
default:
return "not support";
}
}
static void DumpCommonPropertyNG(
const RefPtr<NG::FrameNode>& frameNode, const RefPtr<PipelineBase>& pipeline, int32_t pageId)
{
CHECK_NULL_VOID(frameNode);
auto gestureEventHub = frameNode->GetEventHub<NG::EventHub>()->GetGestureEventHub();
DumpLog::GetInstance().AddDesc("ID: ", frameNode->GetAccessibilityId());
DumpLog::GetInstance().AddDesc("parent ID: ", GetParentId(frameNode));
DumpLog::GetInstance().AddDesc("child IDs: ", ChildernToString(frameNode, pageId));
DumpLog::GetInstance().AddDesc("component type: ", frameNode->GetTag());
DumpLog::GetInstance().AddDesc(
"enabled: ", BoolToString(frameNode->GetFocusHub() ? frameNode->GetFocusHub()->IsEnabled() : true));
DumpLog::GetInstance().AddDesc(
"focusable: ", BoolToString(frameNode->GetFocusHub() ? frameNode->GetFocusHub()->IsFocusable() : false));
DumpLog::GetInstance().AddDesc(
"focused: ", BoolToString(frameNode->GetFocusHub() ? frameNode->GetFocusHub()->IsCurrentFocus() : false));
DumpLog::GetInstance().AddDesc("visible: ", BoolToString(frameNode->IsVisible()));
if (frameNode->IsVisible()) {
NG::RectF rect;
if (frameNode->IsActive()) {
rect = frameNode->GetTransformRectRelativeToWindow();
}
DumpLog::GetInstance().AddDesc("rect: ", rect.ToString());
}
DumpLog::GetInstance().AddDesc("inspector key: ", frameNode->GetInspectorId().value_or(""));
DumpLog::GetInstance().AddDesc("bundle name: ", AceApplicationInfo::GetInstance().GetPackageName());
DumpLog::GetInstance().AddDesc("page id: " + std::to_string(pageId));
DumpLog::GetInstance().AddDesc(
"clickable: ", BoolToString(gestureEventHub ? gestureEventHub->IsAccessibilityClickable() : false));
DumpLog::GetInstance().AddDesc(
"long clickable: ", BoolToString(gestureEventHub ? gestureEventHub->IsAccessibilityLongClickable() : false));
DumpLog::GetInstance().AddDesc("popup supported: ",
BoolToString(IsPopupSupported(frameNode, AceType::DynamicCast<NG::PipelineContext>(pipeline))));
}
static void DumpAccessibilityPropertyNG(const RefPtr<NG::FrameNode>& frameNode)
{
CHECK_NULL_VOID(frameNode);
auto accessibilityProperty = frameNode->GetAccessibilityProperty<NG::AccessibilityProperty>();
CHECK_NULL_VOID(accessibilityProperty);
DumpLog::GetInstance().AddDesc("text: ", accessibilityProperty->GetText());
DumpLog::GetInstance().AddDesc("checked: ", BoolToString(accessibilityProperty->IsChecked()));
DumpLog::GetInstance().AddDesc("selected: ", BoolToString(accessibilityProperty->IsSelected()));
DumpLog::GetInstance().AddDesc("checkable: ", BoolToString(accessibilityProperty->IsCheckable()));
DumpLog::GetInstance().AddDesc("scrollable: ", BoolToString(accessibilityProperty->IsScrollable()));
DumpLog::GetInstance().AddDesc("accessibility hint: ", BoolToString(accessibilityProperty->IsHint()));
DumpLog::GetInstance().AddDesc("hint text: ", accessibilityProperty->GetHintText());
DumpLog::GetInstance().AddDesc("error text: ", accessibilityProperty->GetErrorText());
DumpLog::GetInstance().AddDesc("max text length: ", accessibilityProperty->GetTextLengthLimit());
DumpLog::GetInstance().AddDesc("text selection start: ", accessibilityProperty->GetTextSelectionStart());
DumpLog::GetInstance().AddDesc("text selection end: ", accessibilityProperty->GetTextSelectionEnd());
DumpLog::GetInstance().AddDesc("is multi line: ", BoolToString(accessibilityProperty->IsMultiLine()));
DumpLog::GetInstance().AddDesc("is password: ", BoolToString(accessibilityProperty->IsPassword()));
DumpLog::GetInstance().AddDesc(
"text input type: ", ConvertInputTypeToString(static_cast<int32_t>(accessibilityProperty->GetTextInputType())));
DumpLog::GetInstance().AddDesc("min value: ", accessibilityProperty->GetAccessibilityValue().min);
DumpLog::GetInstance().AddDesc("max value: ", accessibilityProperty->GetAccessibilityValue().max);
DumpLog::GetInstance().AddDesc("current value: ", accessibilityProperty->GetAccessibilityValue().current);
DumpLog::GetInstance().AddDesc("gird info rows: ", accessibilityProperty->GetCollectionInfo().rows);
DumpLog::GetInstance().AddDesc("gird info columns: ", accessibilityProperty->GetCollectionInfo().columns);
DumpLog::GetInstance().AddDesc("gird info select mode: ", accessibilityProperty->GetCollectionInfo().selectMode);
DumpLog::GetInstance().AddDesc("gird item info, row: ", accessibilityProperty->GetCollectionItemInfo().row);
DumpLog::GetInstance().AddDesc("gird item info, column: ", accessibilityProperty->GetCollectionItemInfo().column);
DumpLog::GetInstance().AddDesc("gird item info, rowSpan: ", accessibilityProperty->GetCollectionItemInfo().rowSpan);
DumpLog::GetInstance().AddDesc(
"gird item info, columnSpan: ", accessibilityProperty->GetCollectionItemInfo().columnSpan);
DumpLog::GetInstance().AddDesc(
"gird item info, is heading: ", accessibilityProperty->GetCollectionItemInfo().heading);
DumpLog::GetInstance().AddDesc("gird item info, selected: ", BoolToString(accessibilityProperty->IsSelected()));
DumpLog::GetInstance().AddDesc("current index: ", accessibilityProperty->GetCurrentIndex());
DumpLog::GetInstance().AddDesc("begin index: ", accessibilityProperty->GetBeginIndex());
DumpLog::GetInstance().AddDesc("end index: ", accessibilityProperty->GetEndIndex());
DumpLog::GetInstance().AddDesc("collection item counts: ", accessibilityProperty->GetCollectionItemCounts());
DumpLog::GetInstance().AddDesc("editable: ", BoolToString(accessibilityProperty->IsEditable()));
DumpLog::GetInstance().AddDesc("deletable: ", accessibilityProperty->IsDeletable());
DumpLog::GetInstance().AddDesc("content invalid: ", BoolToString(accessibilityProperty->GetContentInvalid()));
std::string actionForDump;
accessibilityProperty->ResetSupportAction();
auto gestureEventHub = frameNode->GetEventHub<NG::EventHub>()->GetGestureEventHub();
if (gestureEventHub) {
if (gestureEventHub->IsAccessibilityClickable()) {
accessibilityProperty->AddSupportAction(AceAction::ACTION_CLICK);
}
if (gestureEventHub->IsAccessibilityLongClickable()) {
accessibilityProperty->AddSupportAction(AceAction::ACTION_LONG_CLICK);
}
}
if (frameNode->GetFocusHub() ? frameNode->GetFocusHub()->IsFocusable() : false) {
if (frameNode->GetFocusHub() ? frameNode->GetFocusHub()->IsCurrentFocus() : false) {
accessibilityProperty->AddSupportAction(AceAction::ACTION_CLEAR_FOCUS);
} else {
accessibilityProperty->AddSupportAction(AceAction::ACTION_FOCUS);
}
}
auto supportAceActions = accessibilityProperty->GetSupportAction();
for (auto item : supportAceActions) {
if (!actionForDump.empty()) {
actionForDump.append(",");
}
actionForDump.append(ConvertActionTypeToString(item));
actionForDump.append(": ");
actionForDump.append(std::to_string(static_cast<int32_t>(item)));
}
DumpLog::GetInstance().AddDesc("support action: ", actionForDump);
}
void AccessibilityManagerImpl::DumpTree(int32_t depth, int64_t nodeID, bool isDumpSimplify)
{
DumpLog::GetInstance().Print("Dump Accessiability Tree:");
auto pipeline = context_.Upgrade();
CHECK_NULL_VOID(pipeline);
auto ngPipeline = AceType::DynamicCast<NG::PipelineContext>(pipeline);
auto rootNode = ngPipeline->GetRootElement();
CHECK_NULL_VOID(rootNode);
nodeID = rootNode->GetAccessibilityId();
auto stageManager = ngPipeline->GetStageManager();
CHECK_NULL_VOID(stageManager);
auto page = stageManager->GetLastPage();
CHECK_NULL_VOID(page);
auto pageId = page->GetPageId();
DumpTreeNG(rootNode, depth, nodeID, pageId);
}
ActionType ConvertAceAction(AceAction aceAction)
{
static const ActionTable actionTable[] = {
{ AceAction::ACTION_CLICK, ActionType::ACCESSIBILITY_ACTION_CLICK },
{ AceAction::ACTION_LONG_CLICK, ActionType::ACCESSIBILITY_ACTION_LONG_CLICK },
{ AceAction::ACTION_SCROLL_FORWARD, ActionType::ACCESSIBILITY_ACTION_SCROLL_FORWARD },
{ AceAction::ACTION_SCROLL_BACKWARD, ActionType::ACCESSIBILITY_ACTION_SCROLL_BACKWARD },
{ AceAction::ACTION_FOCUS, ActionType::ACCESSIBILITY_ACTION_FOCUS },
{ AceAction::ACTION_CLEAR_FOCUS, ActionType::ACCESSIBILITY_ACTION_CLEAR_FOCUS },
{ AceAction::ACTION_ACCESSIBILITY_FOCUS, ActionType::ACCESSIBILITY_ACTION_ACCESSIBILITY_FOCUS },
{ AceAction::ACTION_CLEAR_ACCESSIBILITY_FOCUS, ActionType::ACCESSIBILITY_ACTION_CLEAR_ACCESSIBILITY_FOCUS },
{ AceAction::ACTION_NEXT_AT_MOVEMENT_GRANULARITY, ActionType::ACCESSIBILITY_ACTION_NEXT_TEXT },
{ AceAction::ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, ActionType::ACCESSIBILITY_ACTION_PREVIOUS_TEXT },
{ AceAction::ACTION_SET_TEXT, ActionType::ACCESSIBILITY_ACTION_SET_TEXT },
{ AceAction::ACTION_COPY, ActionType::ACCESSIBILITY_ACTION_COPY },
{ AceAction::ACTION_PASTE, ActionType::ACCESSIBILITY_ACTION_PASTE },
{ AceAction::ACTION_CUT, ActionType::ACCESSIBILITY_ACTION_CUT },
{ AceAction::ACTION_SELECT, ActionType::ACCESSIBILITY_ACTION_SELECT },
{ AceAction::ACTION_CLEAR_SELECTION, ActionType::ACCESSIBILITY_ACTION_CLEAR_SELECTION },
{ AceAction::ACTION_SET_SELECTION, ActionType::ACCESSIBILITY_ACTION_SET_SELECTION },
{ AceAction::ACTION_SET_CURSOR_POSITION, ActionType::ACCESSIBILITY_ACTION_SET_CURSOR_POSITION },
};
for (const auto& item : actionTable) {
if (aceAction == item.aceAction) {
return item.action;
}
}
return ActionType::ACCESSIBILITY_ACTION_INVALID;
}
static void GetChildFromNode(const RefPtr<NG::UINode>& uiNode, std::vector<int32_t>& children, int32_t pageId,
OHOS::Ace::Platform::ComponentInfo& parentComponent)
{
if (AceType::InstanceOf<NG::FrameNode>(uiNode)) {
if (uiNode->GetTag() == "stage") {
} else if (uiNode->GetTag() == "page") {
if (uiNode->GetPageId() != pageId) {
return;
}
} else {
auto frameNode = AceType::DynamicCast<NG::FrameNode>(uiNode);
CHECK_NULL_VOID(frameNode);
if (!frameNode->IsInternal()) {
children.emplace_back(uiNode->GetAccessibilityId());
parentComponent.children.emplace_back();
return;
}
}
}
for (const auto& frameChild : uiNode->GetChildren()) {
GetChildFromNode(frameChild, children, pageId, parentComponent);
}
}
static OHOS::Ace::Platform::ComponentInfo SetComponentInfo(const RefPtr<NG::FrameNode>& node)
{
OHOS::Ace::Platform::ComponentInfo componentInfo;
NG::RectF rect = node->GetTransformRectRelativeToWindow();
componentInfo.compid = node->GetInspectorId().value_or("");
componentInfo.text = node->GetAccessibilityProperty<NG::AccessibilityProperty>()->GetText();
componentInfo.top = rect.Top();
componentInfo.width = rect.Width();
componentInfo.left = rect.Left();
componentInfo.height = rect.Height();
auto gestureEventHub = node->GetEventHub<NG::EventHub>()->GetGestureEventHub();
componentInfo.clickable = gestureEventHub ? gestureEventHub->IsAccessibilityClickable() : false;
auto accessibilityProperty = node->GetAccessibilityProperty<NG::AccessibilityProperty>();
componentInfo.checked = accessibilityProperty->IsChecked();
componentInfo.selected = accessibilityProperty->IsSelected();
componentInfo.checkable = accessibilityProperty->IsCheckable();
componentInfo.scrollable = accessibilityProperty->IsScrollable();
componentInfo.enabled = node->GetFocusHub() ? node->GetFocusHub()->IsEnabled() : true;
componentInfo.focused = node->GetFocusHub() ? node->GetFocusHub()->IsCurrentFocus() : false;
componentInfo.longClickable = gestureEventHub ? gestureEventHub->IsAccessibilityLongClickable() : false;
componentInfo.type = node->GetTag();
return componentInfo;
}
void GetComponents(OHOS::Ace::Platform::ComponentInfo& parentComponent, const RefPtr<NG::FrameNode>& parent,
NodeId nodeID, int32_t pageId)
{
auto node = GetInspectorById(parent, nodeID);
if (!node) {
return;
}
if (!node->IsActive()) {
return;
}
parentComponent = SetComponentInfo(node);
std::vector<int32_t> children;
for (const auto& item : node->GetChildren()) {
GetChildFromNode(item, children, pageId, parentComponent);
}
for (int index = DEFAULT_ID; index < children.size(); index++) {
GetComponents(parentComponent.children[index], node, children[index], pageId);
}
}
bool AccessibilityManagerImpl::GetAllComponents(NodeId nodeID, OHOS::Ace::Platform::ComponentInfo& rootComponent)
{
auto pipeline = context_.Upgrade();
CHECK_NULL_RETURN(pipeline, false);
auto ngPipeline = AceType::DynamicCast<NG::PipelineContext>(pipeline);
auto rootNode = ngPipeline->GetRootElement();
CHECK_NULL_RETURN(rootNode, false);
nodeID = rootNode->GetAccessibilityId();
auto stageManager = ngPipeline->GetStageManager();
CHECK_NULL_RETURN(stageManager, false);
auto page = stageManager->GetLastPage();
CHECK_NULL_RETURN(page, false);
auto pageId = page->GetPageId();
GetComponents(rootComponent, rootNode, nodeID, pageId);
return true;
}
void AccessibilityManagerImpl::DumpProperty(const std::vector<std::string>& params)
{
DumpLog::GetInstance().Print("Dump Accessiability Property:");
auto pipeline = context_.Upgrade();
CHECK_NULL_VOID(pipeline);
RefPtr<NG::FrameNode> frameNode;
auto nodeID = StringUtils::StringToInt(params[1]);
auto ngPipeline = FindPipelineByElementId(nodeID, frameNode);
CHECK_NULL_VOID(ngPipeline);
CHECK_NULL_VOID(frameNode);
int32_t pageId = DEFAULT_ID;
if (ngPipeline->GetWindowId() == pipeline->GetWindowId()) {
auto stageManager = ngPipeline->GetStageManager();
CHECK_NULL_VOID(stageManager);
auto page = stageManager->GetLastPage();
CHECK_NULL_VOID(page);
pageId = page->GetPageId();
}
DumpCommonPropertyNG(frameNode, pipeline, pageId);
DumpAccessibilityPropertyNG(frameNode);
DumpLog::GetInstance().Print(0, frameNode->GetTag(), frameNode->GetChildren().size());
}
void AccessibilityManagerImpl::DumpHandleEvent(const std::vector<std::string>& params)
{
DumpLog::GetInstance().Print("Dump Accessiability Execute Action");
auto pipeline = context_.Upgrade();
CHECK_NULL_VOID(pipeline);
int32_t nodeId = StringUtils::StringToInt(params[EVENT_DUMP_ID_INDEX]);
auto action = static_cast<AceAction>(StringUtils::StringToInt(params[EVENT_DUMP_ACTION_INDEX]));
if (AceType::InstanceOf<NG::PipelineContext>(pipeline)) {
RefPtr<NG::FrameNode> node;
pipeline = FindPipelineByElementId(nodeId, node);
CHECK_NULL_VOID(pipeline);
CHECK_NULL_VOID(node);
pipeline->GetTaskExecutor()->PostTask(
[weak = WeakClaim(this), action, nodeId, pipeline]() {
auto accessibilityManager = weak.Upgrade();
CHECK_NULL_VOID(accessibilityManager);
accessibilityManager->ExecuteActionNG(nodeId, action, pipeline);
},
TaskExecutor::TaskType::UI, "ArkUI-XAccessibilityManagerImplDumpHandleEvent");
return;
}
}
bool AccessibilityManagerImpl::ExecuteActionNG(int32_t elementId, AceAction action, const RefPtr<PipelineBase>& context)
{
bool result = false;
auto ngPipeline = AceType::DynamicCast<NG::PipelineContext>(context);
CHECK_NULL_RETURN(ngPipeline, result);
ContainerScope instance(ngPipeline->GetInstanceId());
auto frameNode = GetInspectorById(ngPipeline->GetRootElement(), elementId);
CHECK_NULL_RETURN(frameNode, result);
switch (action) {
case AceAction::ACTION_FOCUS: {
result = RequestFocus(frameNode);
break;
}
case AceAction::ACTION_CLICK: {
result = ActClick(frameNode);
break;
}
case AceAction::ACTION_LONG_CLICK: {
result = ActLongClick(frameNode);
break;
}
case AceAction::ACTION_SCROLL_BACKWARD:
case AceAction::ACTION_SCROLL_FORWARD:
return true;
default:
break;
}
return result;
}
RefPtr<NG::FrameNode> AccessibilityManagerImpl::FindNodeFromPipeline(
const WeakPtr<PipelineBase>& context, const int32_t elementId)
{
auto pipeline = context.Upgrade();
CHECK_NULL_RETURN(pipeline, nullptr);
auto ngPipeline = AceType::DynamicCast<NG::PipelineContext>(pipeline);
auto rootNode = ngPipeline->GetRootElement();
CHECK_NULL_RETURN(rootNode, nullptr);
NodeId nodeId = elementId;
if (elementId == DEFAULT_ElEMENTID) {
nodeId = rootNode->GetAccessibilityId();
}
RefPtr<NG::FrameNode> node = GetInspectorById(rootNode, nodeId);
if (node) {
return node;
}
return nullptr;
}
RefPtr<NG::PipelineContext> AccessibilityManagerImpl::FindPipelineByElementId(
const int32_t elementId, RefPtr<NG::FrameNode>& node)
{
node = FindNodeFromPipeline(context_, elementId);
if (node) {
auto context = AceType::DynamicCast<NG::PipelineContext>(context_.Upgrade());
return context;
}
for (auto subContext : GetSubPipelineContexts()) {
node = FindNodeFromPipeline(subContext, elementId);
if (node) {
auto context = AceType::DynamicCast<NG::PipelineContext>(subContext.Upgrade());
return context;
}
}
return nullptr;
}
bool AccessibilityManagerImpl::ExecuteActionNG(int64_t elementId,
const std::map<std::string, std::string>& actionArguments, ActionType action, const RefPtr<PipelineBase>& context,
int64_t uiExtensionOffset)
{
bool result = false;
auto ngPipeline = AceType::DynamicCast<NG::PipelineContext>(context);
CHECK_NULL_RETURN(ngPipeline, result);
ContainerScope instance(ngPipeline->GetInstanceId());
auto frameNode = GetFramenodeByAccessibilityId(ngPipeline->GetRootElement(), elementId);
if (!frameNode && elementId == lastElementId_) {
frameNode = lastFrameNode_.Upgrade();
}
CHECK_NULL_RETURN(frameNode, result);
auto enabled = frameNode->GetFocusHub() ? frameNode->GetFocusHub()->IsEnabled() : true;
if (!enabled && action != ActionType::ACCESSIBILITY_ACTION_ACCESSIBILITY_FOCUS &&
action != ActionType::ACCESSIBILITY_ACTION_CLEAR_ACCESSIBILITY_FOCUS) {
return result;
}
result = ConvertActionTypeToBoolen(action, frameNode, elementId, ngPipeline);
if (!result) {
auto accessibilityProperty = frameNode->GetAccessibilityProperty<NG::AccessibilityProperty>();
CHECK_NULL_RETURN(accessibilityProperty, false);
result = ActAccessibilityAction(action, actionArguments, accessibilityProperty);
}
return result;
}
inline RangeInfo ConvertAccessibilityValue(const AccessibilityValue& value)
{
return RangeInfo(value.min, value.max, value.current);
}
void GetTreeIdAndElementIdBySplitElementId(const int64_t elementId, int64_t& splitElementId, int32_t& splitTreeId)
{
if (elementId <= CONT_SPLIT_ID) {
splitTreeId = CONT_SPLIT_ID;
splitElementId = CONT_SPLIT_ID;
return;
}
splitTreeId = (static_cast<uint64_t>(elementId) >> ELEMENT_MOVE_BIT);
splitElementId = MAX_ELEMENT_ID & elementId;
}
bool IsExtensionComponent(const RefPtr<NG::UINode>& node)
{
return node &&
(node->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG || node->GetTag() == V2::EMBEDDED_COMPONENT_ETS_TAG ||
node->GetTag() == V2::ISOLATED_COMPONENT_ETS_TAG);
}
bool IsUIExtensionShowPlaceholder(const RefPtr<NG::UINode>& node)
{
CHECK_NULL_RETURN(node, true);
if (node->GetTag() == V2::ISOLATED_COMPONENT_ETS_TAG) {
return false;
}
#ifdef WINDOW_SCENE_SUPPORTED
auto pipeline = node->GetContextRefPtr();
CHECK_NULL_RETURN(pipeline, true);
auto manager = pipeline->GetUIExtensionManager();
CHECK_NULL_RETURN(manager, true);
return manager->IsShowPlaceholder(node->GetId());
#endif
return true;
}
void AccessibilityManagerImpl::InitializeCallback()
{
if (IsRegister()) {
return;
}
auto pipelineContext = GetPipelineContext().Upgrade();
CHECK_NULL_VOID(pipelineContext);
windowId_ = pipelineContext->GetWindowId();
bool isEnabled = IsUITestingEnabled(windowId_);
auto container = Container::Current();
if (!container) {
LOGE("container is null");
return;
}
auto executor = container->GetTaskExecutor();
RefPtr<Environment> environment = Platform::EnvironmentProxyImpl::GetInstance()->GetEnvironment(executor);
if (environment) {
std::string val = environment->GetAccessibilityEnabled();
if (val == "true") {
isEnabled = true;
}
}
AceApplicationInfo::GetInstance().SetAccessibilityEnabled(isEnabled);
RefreshEffectiveAccessibilityState(isEnabled);
if (pipelineContext->IsFormRender() || pipelineContext->IsJsCard() || pipelineContext->IsJsPlugin()) {
return;
}
SubscribeStateObserver();
if (isEnabled) {
RegisterInteractionOperation(windowId_);
}
}
void SetAccessibilityFocusAction(AccessibilityElementInfo& nodeInfo, const char* tag)
{
if (nodeInfo.HasAccessibilityFocus()) {
AccessibleAction action(ACCESSIBILITY_ACTION_CLEAR_ACCESSIBILITY_FOCUS, tag);
nodeInfo.AddAction(action);
} else {
AccessibleAction action(ACCESSIBILITY_ACTION_ACCESSIBILITY_FOCUS, tag);
nodeInfo.AddAction(action);
}
}
void UpdateSupportAction(const RefPtr<NG::FrameNode>& node, AccessibilityElementInfo& nodeInfo)
{
CHECK_NULL_VOID(node);
if (nodeInfo.IsFocusable()) {
if (nodeInfo.IsFocused()) {
AccessibleAction action(ACCESSIBILITY_ACTION_CLEAR_FOCUS, "ace");
nodeInfo.AddAction(action);
} else {
AccessibleAction action(ACCESSIBILITY_ACTION_FOCUS, "ace");
nodeInfo.AddAction(action);
}
}
auto eventHub = node->GetEventHub<NG::EventHub>();
CHECK_NULL_VOID(eventHub);
auto gestureEventHub = eventHub->GetGestureEventHub();
CHECK_NULL_VOID(gestureEventHub);
nodeInfo.SetClickable(gestureEventHub->IsAccessibilityClickable());
if (gestureEventHub->IsAccessibilityClickable()) {
AccessibleAction action(ACCESSIBILITY_ACTION_CLICK, "ace");
nodeInfo.AddAction(action);
}
nodeInfo.SetLongClickable(gestureEventHub->IsAccessibilityLongClickable());
if (gestureEventHub->IsAccessibilityLongClickable()) {
AccessibleAction action(ACCESSIBILITY_ACTION_LONG_CLICK, "ace");
nodeInfo.AddAction(action);
}
}
void UpdateChildrenOfAccessibilityElementInfo(
const RefPtr<NG::FrameNode>& node, const CommonProperty& commonProperty, AccessibilityElementInfo& nodeInfo)
{
if (!IsExtensionComponent(node) || IsUIExtensionShowPlaceholder(node)) {
std::vector<int32_t> children;
for (const auto& item : node->GetChildren(true)) {
GetFrameNodeChildren(item, children, commonProperty.pageId);
}
auto accessibilityProperty = node->GetAccessibilityProperty<NG::AccessibilityProperty>();
auto uiVirtualNode = accessibilityProperty->GetAccessibilityVirtualNode();
if (uiVirtualNode != nullptr) {
auto virtualNode = AceType::DynamicCast<NG::FrameNode>(uiVirtualNode);
if (virtualNode != nullptr) {
children.clear();
GetFrameNodeChildren(virtualNode, children, commonProperty.pageId);
}
}
for (const auto& child : children) {
nodeInfo.AddChild(child);
}
}
}
bool AccessibilityManagerImpl::SubscribeStateObserver()
{
if (!stateObserver_) {
stateObserver_ = std::make_shared<AccessibilityStateObserver>();
}
stateObserver_->SetAccessibilityManager(WeakClaim(this));
stateObserver_->SetPipeline(context_);
return SubscribeState(windowId_, stateObserver_);
}
void AccessibilityManagerImpl::UnSubscribeStateObserver()
{
UnSubscribeState(windowId_);
}
void AccessibilityManagerImpl::AccessibilityStateObserver::OnStateChanged(const bool state)
{
auto pipelineRef = pipeline_.Upgrade();
CHECK_NULL_VOID(pipelineRef);
pipelineRef->GetTaskExecutor()->PostTask(
[weak = accessibilityManager_, state]() {
auto jsAccessibilityManager = weak.Upgrade();
CHECK_NULL_VOID(jsAccessibilityManager);
if (state) {
jsAccessibilityManager->RegisterInteractionOperation(jsAccessibilityManager->GetWindowId());
jsAccessibilityManager->UpdateElementInfosByAccessibilityId(DEFAULT_ElEMENTID,
jsAccessibilityManager->GetWindowId(), static_cast<size_t>(AccessibilityEventType::PAGE_CHANGE));
} else {
jsAccessibilityManager->DeregisterInteractionOperation();
}
AceApplicationInfo::GetInstance().SetAccessibilityEnabled(state);
RefreshEffectiveAccessibilityState(state);
},
TaskExecutor::TaskType::UI, "ArkUIAccessibilityStateChanged");
}
void AccessibilityManagerImpl::RegisterInteractionOperation(int windowId)
{
if (IsRegister()) {
return;
}
auto interactionOperation = std::make_shared<InteractionOperation>(windowId);
interactionOperation->SetHandler(WeakClaim(this));
bool retReg = ExecuteActionOC(windowId, interactionOperation);
RefPtr<PipelineBase> context;
for (auto subContext : GetSubPipelineContexts()) {
context = subContext.Upgrade();
CHECK_NULL_VOID(context);
interactionOperation = std::make_shared<InteractionOperation>(context->GetWindowId());
interactionOperation->SetHandler(WeakClaim(this));
retReg = ExecuteActionOC(context->GetWindowId(), interactionOperation);
}
Register(retReg);
}
void AccessibilityManagerImpl::DeregisterInteractionOperation() {}
void AccessibilityManagerImpl::ProcessAccessibilityEvent(
const AccessibilityEvent& accessibilityEvent, bool needAsync, size_t eventType)
{
RefPtr<NG::FrameNode> node;
RefPtr<NG::PipelineContext> ngPipeline = FindPipelineByElementId(accessibilityEvent.nodeId, node);
CHECK_NULL_VOID(ngPipeline);
CHECK_NULL_VOID(node);
windowId_ = ngPipeline->GetWindowId();
int64_t elementId = DEFAULT_ElEMENTID;
if (eventType == static_cast<size_t>(OHOS::Ace::AccessibilityEventType::TEXT_CHANGE)) {
elementId = accessibilityEvent.nodeId;
}
UpdateElementInfos(elementId, node, needAsync, eventType);
}
void AccessibilityManagerImpl::UpdateElementInfos(
const int64_t elementId, RefPtr<NG::FrameNode> node, bool needAsync, size_t eventType)
{
int64_t splitElementId = AccessibilityElementInfo::UNDEFINED_ACCESSIBILITY_ID;
int32_t splitTreeId = AccessibilityElementInfo::UNDEFINED_TREE_ID;
GetTreeIdAndElementIdBySplitElementId(elementId, splitElementId, splitTreeId);
auto context = GetPipelineContext().Upgrade();
CHECK_NULL_VOID(context);
auto windowId = context->GetWindowId();
if (!needAsync) {
UpdateElementInfosByAccessibilityId(splitElementId, windowId, eventType);
return;
}
context->GetTaskExecutor()->PostDelayedTask(
[weak = WeakClaim(this), splitElementId, windowId, node, eventType]() {
auto accessibilityManager = weak.Upgrade();
CHECK_NULL_VOID(accessibilityManager);
CHECK_NULL_VOID(node);
while (!node->IsLayoutComplete() && node->IsOnMainTree()) {
std::this_thread::yield();
}
accessibilityManager->UpdateElementInfosByAccessibilityId(splitElementId, windowId, eventType);
},
TaskExecutor::TaskType::UI, MAX_TIME, "ArkUIAccessibilitySearchElementInfoById");
}
void AccessibilityManagerImpl::NotifyUiTestEventCallback(const AccessibilityEvent& accessibilityEvent)
{
if (testForceEnableCount_.load() <= 0) {
return;
}
AccessibilityEventCallback callback = nullptr;
{
std::lock_guard<std::mutex> lock(g_uiTestEventCallbackMutex);
callback = uiTestEventCallback_;
}
if (!callback) {
return;
}
callback(accessibilityEvent);
}
void AccessibilityManagerImpl::SendAccessibilityAsyncEvent(const AccessibilityEvent& accessibilityEvent)
{
NotifyUiTestEventCallback(accessibilityEvent);
switch (accessibilityEvent.type) {
case AccessibilityEventType::PAGE_OPEN:
case AccessibilityEventType::PAGE_CLOSE:
case AccessibilityEventType::CHANGE:
case AccessibilityEventType::PAGE_CHANGE:
case AccessibilityEventType::ELEMENT_INFO_CHANGE:
case AccessibilityEventType::TEXT_CHANGE:
case AccessibilityEventType::COMPONENT_CHANGE:
case AccessibilityEventType::SCROLL_END:
ProcessAccessibilityEvent(accessibilityEvent, true, static_cast<size_t>(accessibilityEvent.type));
break;
case AccessibilityEventType::REQUEST_FOCUS:
SendAccessibilityEventOC(accessibilityEvent.nodeId, static_cast<int>(accessibilityEvent.windowId),
static_cast<size_t>(accessibilityEvent.type));
break;
case AccessibilityEventType::FOCUS:
SendAccessibilityEventOC(
accessibilityEvent.nodeId, static_cast<int>(windowId_), static_cast<size_t>(accessibilityEvent.type));
break;
case AccessibilityEventType::ANNOUNCE_FOR_ACCESSIBILITY:
AnnounceForAccessibilityOC(accessibilityEvent.textAnnouncedForAccessibility);
break;
default:
break;
}
}
void AccessibilityManagerImpl::SetUiTestEventCallback(const AccessibilityEventCallback& cb)
{
std::lock_guard<std::mutex> lock(g_uiTestEventCallbackMutex);
uiTestEventCallback_ = cb;
}
void AccessibilityManagerImpl::UnsetUiTestEventCallback()
{
std::lock_guard<std::mutex> lock(g_uiTestEventCallbackMutex);
uiTestEventCallback_ = nullptr;
}
void AccessibilityManagerImpl::AddUiTestAccessibilityRequest()
{
testForceEnableCount_++;
RefreshEffectiveAccessibilityState();
}
void AccessibilityManagerImpl::RemoveUiTestAccessibilityRequest()
{
int32_t current = testForceEnableCount_.load();
while (current > 0) {
if (testForceEnableCount_.compare_exchange_weak(current, current - 1)) {
break;
}
}
RefreshEffectiveAccessibilityState();
}
void ConvertExtensionAccessibilityId(AccessibilityElementInfo& info, const RefPtr<NG::FrameNode>& extensionNode,
int64_t uiExtensionOffset, AccessibilityElementInfo& parentInfo)
{
CHECK_NULL_VOID(extensionNode);
auto extensionAbilityId = extensionNode->WrapExtensionAbilityId(uiExtensionOffset, info.GetAccessibilityId());
info.SetAccessibilityId(extensionAbilityId);
auto parentNodeId = extensionNode->WrapExtensionAbilityId(uiExtensionOffset, info.GetParentNodeId());
info.SetParent(parentNodeId);
auto childIds = info.GetChildIds();
for (auto& child : childIds) {
info.RemoveChild(child);
info.AddChild(extensionNode->WrapExtensionAbilityId(uiExtensionOffset, child));
}
if (info.GetComponentType() == V2::ROOT_ETS_TAG) {
for (auto& child : info.GetChildIds()) {
parentInfo.AddChild(child);
}
}
}
void ConvertExtensionAccessibilityNodeId(std::list<AccessibilityElementInfo>& infos,
const RefPtr<NG::FrameNode>& extensionNode, int64_t uiExtensionOffset, AccessibilityElementInfo& parentInfo)
{
CHECK_NULL_VOID(extensionNode);
for (auto& accessibilityElementInfo : infos) {
ConvertExtensionAccessibilityId(accessibilityElementInfo, extensionNode, uiExtensionOffset, parentInfo);
}
for (auto& accessibilityElementInfo : infos) {
if (std::find(parentInfo.GetChildIds().begin(), parentInfo.GetChildIds().end(),
accessibilityElementInfo.GetAccessibilityId()) != parentInfo.GetChildIds().end()) {
accessibilityElementInfo.SetParent(extensionNode->GetAccessibilityId());
}
}
}
std::list<AccessibilityElementInfo> SearchExtensionElementInfoByAccessibilityIdNG(
int64_t elementId, int32_t mode, const RefPtr<NG::FrameNode>& node, int64_t offset)
{
std::list<AccessibilityElementInfo> extensionElementInfo;
if (NG::UI_EXTENSION_OFFSET_MIN < (offset + 1)) {
node->SearchExtensionElementInfoByAccessibilityIdNG(
elementId, mode, offset / NG::UI_EXTENSION_ID_FACTOR, extensionElementInfo);
}
return extensionElementInfo;
}
void SearchExtensionElementInfoNG(const SearchParameter& searchParam, const RefPtr<NG::FrameNode>& node,
std::list<Accessibility::AccessibilityElementInfo>& infos, Accessibility::AccessibilityElementInfo& parentInfo)
{
auto extensionElementInfos = SearchExtensionElementInfoByAccessibilityIdNG(
searchParam.nodeId, searchParam.mode, node, searchParam.uiExtensionOffset);
if (extensionElementInfos.size() > 0) {
auto rootParentId = extensionElementInfos.front().GetParentNodeId();
ConvertExtensionAccessibilityNodeId(extensionElementInfos, node, searchParam.uiExtensionOffset, parentInfo);
if (rootParentId == NG::UI_EXTENSION_ROOT_ID) {
extensionElementInfos.front().SetParent(node->GetAccessibilityId());
}
if (parentInfo.GetComponentType() == V2::ISOLATED_COMPONENT_ETS_TAG) {
auto windowId = parentInfo.GetWindowId();
for (auto& info : extensionElementInfos) {
info.SetWindowId(windowId);
}
}
for (auto& info : extensionElementInfos) {
infos.push_back(info);
}
}
}
bool AccessibilityManagerImpl::ConvertActionTypeToBoolen(
ActionType action, RefPtr<NG::FrameNode>& frameNode, int64_t elementId, RefPtr<NG::PipelineContext>& context)
{
bool result = false;
switch (action) {
case ActionType::ACCESSIBILITY_ACTION_FOCUS: {
result = RequestFocus(frameNode);
break;
}
case ActionType::ACCESSIBILITY_ACTION_CLEAR_FOCUS: {
result = LostFocus(frameNode);
break;
}
case ActionType::ACCESSIBILITY_ACTION_CLICK: {
result = ActClick(frameNode);
break;
}
case ActionType::ACCESSIBILITY_ACTION_LONG_CLICK: {
result = ActLongClick(frameNode);
break;
}
case ActionType::ACCESSIBILITY_ACTION_ACCESSIBILITY_FOCUS: {
SaveLast(elementId, frameNode);
result = ActAccessibilityFocus(elementId, frameNode, context, currentFocusNodeId_, false);
break;
}
case ActionType::ACCESSIBILITY_ACTION_CLEAR_ACCESSIBILITY_FOCUS: {
SaveLast(elementId, frameNode);
result = ActAccessibilityFocus(elementId, frameNode, context, currentFocusNodeId_, true);
break;
}
default:
break;
}
return result;
}
void SetSelectionAction(const std::map<std::string, std::string>& actionArguments, AccessibilityActionParam& param)
{
int start = DEFAULT_SELECTION;
int end = DEFAULT_SELECTION;
std::string dir = STRING_DIR_BACKWARD;
auto iter = actionArguments.find(ACTION_ARGU_SELECT_TEXT_START);
if (iter != actionArguments.end()) {
std::stringstream str_start;
str_start << iter->second;
str_start >> start;
}
iter = actionArguments.find(ACTION_ARGU_SELECT_TEXT_END);
if (iter != actionArguments.end()) {
std::stringstream str_end;
str_end << iter->second;
str_end >> end;
}
iter = actionArguments.find(ACTION_ARGU_SELECT_TEXT_INFORWARD);
if (iter != actionArguments.end()) {
dir = iter->second;
}
param.setSelectionStart = start;
param.setSelectionEnd = end;
param.setSelectionDir = conversionDirection(dir);
}
void SetTextAction(const std::map<std::string, std::string>& actionArguments, AccessibilityActionParam& param)
{
auto iter = actionArguments.find(ACTION_ARGU_SET_TEXT);
if (iter != actionArguments.end()) {
param.setTextArgument = iter->second;
}
}
void SetMoveTextAction(const std::map<std::string, std::string>& actionArguments, AccessibilityActionParam& param)
{
int moveUnit = TextMoveUnit::STEP_CHARACTER;
auto iter = actionArguments.find(ACTION_ARGU_MOVE_UNIT);
if (iter != actionArguments.end()) {
std::stringstream str_moveUnit;
str_moveUnit << iter->second;
str_moveUnit >> moveUnit;
}
param.moveUnit = static_cast<TextMoveUnit>(moveUnit);
}
void SetCursorPositionAction(const std::map<std::string, std::string>& actionArguments, AccessibilityActionParam& param)
{
auto iter = actionArguments.find(ACTION_ARGU_SET_OFFSET);
int32_t position = DEFAULT_SELECTION;
if (iter != actionArguments.end()) {
std::stringstream strPosition;
strPosition << iter->second;
strPosition >> position;
}
param.setCursorIndex = position;
}
void SetScrollAction(const std::map<std::string, std::string>& actionArguments, AccessibilityActionParam& param)
{
int32_t scrollType = static_cast<int32_t>(AccessibilityScrollType::SCROLL_DEFAULT);
auto iter = actionArguments.find(ACTION_ARGU_SCROLL_STUB);
if (iter != actionArguments.end()) {
std::stringstream strScrollType;
strScrollType << iter->second;
strScrollType >> scrollType;
}
if ((scrollType < static_cast<int32_t>(AccessibilityScrollType::SCROLL_DEFAULT)) ||
(scrollType > static_cast<int32_t>(AccessibilityScrollType::SCROLL_MAX_TYPE))) {
scrollType = static_cast<int32_t>(AccessibilityScrollType::SCROLL_DEFAULT);
}
param.scrollType = static_cast<AccessibilityScrollType>(scrollType);
}
bool AccessibilityManagerImpl::ActAccessibilityAction(Accessibility::ActionType action,
const std::map<std::string, std::string> actionArguments, RefPtr<NG::AccessibilityProperty> accessibilityProperty)
{
AccessibilityActionParam param;
switch (action) {
case ActionType::ACCESSIBILITY_ACTION_SET_SELECTION:
SetSelectionAction(actionArguments, param);
break;
case ActionType::ACCESSIBILITY_ACTION_SET_TEXT:
SetTextAction(actionArguments, param);
break;
case ActionType::ACCESSIBILITY_ACTION_NEXT_TEXT:
case ActionType::ACCESSIBILITY_ACTION_PREVIOUS_TEXT:
SetMoveTextAction(actionArguments, param);
break;
case ActionType::ACCESSIBILITY_ACTION_SET_CURSOR_POSITION:
SetCursorPositionAction(actionArguments, param);
break;
case ActionType::ACCESSIBILITY_ACTION_SCROLL_FORWARD:
case ActionType::ACCESSIBILITY_ACTION_SCROLL_BACKWARD:
SetScrollAction(actionArguments, param);
break;
default:
break;
}
auto accessibiltyAction = ACTIONS.find(action);
if (accessibiltyAction != ACTIONS.end()) {
param.accessibilityProperty = accessibilityProperty;
return accessibiltyAction->second(param);
}
return false;
}
bool AccessibilityManagerImpl::ClearCurrentFocus()
{
auto currentFocusNode = GetAccessibilityNodeFromPage(currentFocusNodeId_);
CHECK_NULL_RETURN(currentFocusNode, false);
currentFocusNodeId_ = DEFAULT_ElEMENTID;
currentFocusNode->SetFocusedState(false);
currentFocusNode->SetAccessibilityFocusedState(false);
return currentFocusNode->ActionAccessibilityFocus(false);
}
bool AccessibilityManagerImpl::RequestAccessibilityFocus(const RefPtr<AccessibilityNode>& node)
{
CHECK_NULL_RETURN(node, false);
auto requestNodeId = node->GetNodeId();
if (currentFocusNodeId_ == requestNodeId) {
return false;
}
ClearCurrentFocus();
currentFocusNodeId_ = requestNodeId;
node->SetAccessibilityFocusedState(true);
return node->ActionAccessibilityFocus(true);
}
bool AccessibilityManagerImpl::ClearAccessibilityFocus(const RefPtr<AccessibilityNode>& node)
{
CHECK_NULL_RETURN(node, false);
auto requestNodeId = node->GetNodeId();
if (currentFocusNodeId_ != requestNodeId) {
return false;
}
currentFocusNodeId_ = DEFAULT_ElEMENTID;
node->SetAccessibilityFocusedState(false);
return node->ActionAccessibilityFocus(false);
}
bool HandleClickAction(const RefPtr<AccessibilityNode>& node, const RefPtr<PipelineContext>& context)
{
CHECK_NULL_RETURN(node, false);
node->SetClicked(true);
if (!node->GetClickEventMarker().IsEmpty()) {
#ifndef NG_BUILD
context->SendEventToFrontend(node->GetClickEventMarker());
#endif
node->ActionClick();
return true;
}
return node->ActionClick();
}
bool HandleLongClickAction(const RefPtr<AccessibilityNode>& node, const RefPtr<PipelineContext>& context)
{
CHECK_NULL_RETURN(node, false);
if (!node->GetLongPressEventMarker().IsEmpty()) {
#ifndef NG_BUILD
context->SendEventToFrontend(node->GetLongPressEventMarker());
#endif
node->ActionLongClick();
return true;
}
return node->ActionLongClick();
}
bool HandleSetTextAction(const RefPtr<AccessibilityNode>& node, const RefPtr<PipelineContext>& context,
const std::map<std::string, std::string>& actionArguments)
{
CHECK_NULL_RETURN(node, false);
if (!node->GetSetTextEventMarker().IsEmpty()) {
#ifndef NG_BUILD
context->SendEventToFrontend(node->GetSetTextEventMarker());
#endif
node->ActionSetText(actionArguments.find(ACTION_ARGU_SET_TEXT)->second);
return true;
}
return node->ActionSetText(actionArguments.find(ACTION_ARGU_SET_TEXT)->second);
}
bool HandleFocusAction(const RefPtr<AccessibilityNode>& node, const RefPtr<PipelineContext>& context)
{
CHECK_NULL_RETURN(node, false);
#ifndef NG_BUILD
context->AccessibilityRequestFocus(std::to_string(node->GetNodeId()));
#endif
if (!node->GetFocusEventMarker().IsEmpty()) {
#ifndef NG_BUILD
context->SendEventToFrontend(node->GetFocusEventMarker());
#endif
node->ActionFocus();
return true;
}
return node->ActionFocus();
}
bool AccessibilityManagerImpl::AccessibilityActionEvent(const ActionType& action,
const std::map<std::string, std::string>& actionArguments, const RefPtr<AccessibilityNode>& node,
const RefPtr<PipelineContext>& context)
{
if (!node || !context) {
return false;
}
ContainerScope scope(context->GetInstanceId());
switch (action) {
case ActionType::ACCESSIBILITY_ACTION_CLICK:
return HandleClickAction(node, context);
case ActionType::ACCESSIBILITY_ACTION_LONG_CLICK:
return HandleLongClickAction(node, context);
case ActionType::ACCESSIBILITY_ACTION_SET_TEXT:
return HandleSetTextAction(node, context, actionArguments);
case ActionType::ACCESSIBILITY_ACTION_FOCUS:
return HandleFocusAction(node, context);
case ActionType::ACCESSIBILITY_ACTION_ACCESSIBILITY_FOCUS:
return RequestAccessibilityFocus(node);
case ActionType::ACCESSIBILITY_ACTION_CLEAR_ACCESSIBILITY_FOCUS:
return ClearAccessibilityFocus(node);
case ActionType::ACCESSIBILITY_ACTION_SCROLL_FORWARD:
return node->ActionScrollForward();
case ActionType::ACCESSIBILITY_ACTION_SCROLL_BACKWARD:
return node->ActionScrollBackward();
default:
return false;
}
}
bool IsUserCheckedOrSelected(const RefPtr<NG::FrameNode> frameNode)
{
CHECK_NULL_RETURN(frameNode, false);
auto accessibilityProperty = frameNode->GetAccessibilityProperty<NG::AccessibilityProperty>();
CHECK_NULL_RETURN(accessibilityProperty, false);
if (accessibilityProperty->HasUserCheckedType() || accessibilityProperty->HasUserSelected()) {
return true;
}
return false;
}
void AccessibilityManagerImpl::SendEventToAccessibilityWithNode(
const AccessibilityEvent& accessibilityEvent, const RefPtr<AceType>& node, const RefPtr<PipelineBase>& context)
{
CHECK_NULL_VOID(context);
auto delayTime = GetDelayTimeBeforeSendEvent(accessibilityEvent, node);
if ((delayTime > 0) && context) {
context->GetTaskExecutor()->PostDelayedTask(
[weak = WeakClaim(this), accessibilityEvent, node, context] {
auto AccessibilityManager = weak.Upgrade();
CHECK_NULL_VOID(AccessibilityManager);
AccessibilityManager->SendEventToAccessibilityWithNodeInner(accessibilityEvent, node, context);
},
TaskExecutor::TaskType::UI, delayTime, "ArkUIAccessibilitySendSyncEventWithDelay");
return;
}
SendEventToAccessibilityWithNodeInner(accessibilityEvent, node, context);
}
void AccessibilityManagerImpl::SendEventToAccessibilityWithNodeInner(
const AccessibilityEvent& accessibilityEvent, const RefPtr<AceType>& node, const RefPtr<PipelineBase>& context)
{
CHECK_NULL_VOID(context);
int32_t windowId = static_cast<int32_t>(context->GetFocusWindowId());
if (windowId == DEFAULT_ID) {
return;
}
if (!AceType::InstanceOf<NG::FrameNode>(node)) {
return;
}
auto frameNode = AceType::DynamicCast<NG::FrameNode>(node);
CHECK_NULL_VOID(frameNode);
auto ngPipeline = AceType::DynamicCast<NG::PipelineContext>(context);
CHECK_NULL_VOID(ngPipeline);
if ((!frameNode->IsActive()) || frameNode->CheckAccessibilityLevelNo()) {
return;
}
NotifyUiTestEventCallback(accessibilityEvent);
ProcessAccessibilityEvent(accessibilityEvent, false, static_cast<size_t>(accessibilityEvent.type));
}
int64_t AccessibilityManagerImpl::GetDelayTimeBeforeSendEvent(
const AccessibilityEvent& accessibilityEvent, const RefPtr<AceType>& node)
{
if (accessibilityEvent.type != AccessibilityEventType::CLICK) {
return 0;
}
auto frameNode = AceType::DynamicCast<NG::FrameNode>(node);
if (frameNode) {
if (IsUserCheckedOrSelected(frameNode)) {
return DELAY_SEND_EVENT_MILLISECOND;
}
} else {
auto context = GetPipelineContext().Upgrade();
CHECK_NULL_RETURN(context, 0);
if (!AceType::InstanceOf<NG::PipelineContext>(context)) {
return 0;
}
RefPtr<NG::FrameNode> findeNode;
auto ngPipeline = FindPipelineByElementId(accessibilityEvent.nodeId, findeNode);
if ((findeNode) && IsUserCheckedOrSelected(findeNode)) {
return DELAY_SEND_EVENT_MILLISECOND;
}
}
return 0;
}
void AccessibilityManagerImpl::SendActionEvent(const Accessibility::ActionType& action, int64_t nodeId)
{
static std::unordered_map<Accessibility::ActionType, std::string> actionToStr {
{ Accessibility::ActionType::ACCESSIBILITY_ACTION_CLICK, DOM_CLICK },
{ Accessibility::ActionType::ACCESSIBILITY_ACTION_LONG_CLICK, DOM_LONG_PRESS },
{ Accessibility::ActionType::ACCESSIBILITY_ACTION_FOCUS, DOM_FOCUS },
{ Accessibility::ActionType::ACCESSIBILITY_ACTION_ACCESSIBILITY_FOCUS, ACCESSIBILITY_FOCUSED_EVENT },
{ Accessibility::ActionType::ACCESSIBILITY_ACTION_CLEAR_ACCESSIBILITY_FOCUS, ACCESSIBILITY_CLEAR_FOCUS_EVENT },
{ Accessibility::ActionType::ACCESSIBILITY_ACTION_SCROLL_FORWARD, SCROLL_END_EVENT },
{ Accessibility::ActionType::ACCESSIBILITY_ACTION_SCROLL_BACKWARD, SCROLL_END_EVENT },
};
if (actionToStr.find(action) == actionToStr.end()) {
return;
}
AccessibilityEvent accessibilityEvent;
accessibilityEvent.eventType = actionToStr[action];
accessibilityEvent.nodeId = static_cast<int64_t>(nodeId);
SendAccessibilityAsyncEvent(accessibilityEvent);
}
void AccessibilityManagerImpl::InteractionOperation::ExecuteAction(
const int64_t elementId, const int32_t action, const std::map<std::string, std::string>& actionArguments)
{
int64_t splitElementId = AccessibilityElementInfo::UNDEFINED_ACCESSIBILITY_ID;
int32_t splitTreeId = AccessibilityElementInfo::UNDEFINED_TREE_ID;
GetTreeIdAndElementIdBySplitElementId(elementId, splitElementId, splitTreeId);
auto jsAccessibilityManager = GetHandler().Upgrade();
CHECK_NULL_VOID(jsAccessibilityManager);
auto context = jsAccessibilityManager->GetPipelineContext().Upgrade();
CHECK_NULL_VOID(context);
auto actionInfo = static_cast<ActionType>(action);
ActionParam param { actionInfo, actionArguments };
auto windowId = windowId_;
context->GetTaskExecutor()->PostTask(
[weak = GetHandler(), splitElementId, param, windowId] {
auto jsAccessibilityManager = weak.Upgrade();
CHECK_NULL_VOID(jsAccessibilityManager);
ACE_SCOPED_TRACE("ExecuteAction");
jsAccessibilityManager->ExecuteAction(splitElementId, param, windowId);
},
TaskExecutor::TaskType::UI, "ArkUIAccessibilityExecuteAction");
}
void AccessibilityManagerImpl::InteractionOperation::RequestUpdate(const int64_t elementId)
{
auto jsAccessibilityManager = GetHandler().Upgrade();
CHECK_NULL_VOID(jsAccessibilityManager);
auto context = jsAccessibilityManager->GetPipelineContext().Upgrade();
CHECK_NULL_VOID(context);
auto windowId = windowId_;
context->GetTaskExecutor()->PostTask(
[weak = GetHandler(), elementId, windowId] {
auto jsAccessibilityManager = weak.Upgrade();
CHECK_NULL_VOID(jsAccessibilityManager);
ACE_SCOPED_TRACE("RequestUpdate");
jsAccessibilityManager->RequestUpdate(elementId, windowId);
},
TaskExecutor::TaskType::UI, "ArkUIAccessibilityExecuteAction");
}
void AccessibilityManagerImpl::ExecuteAction(const int64_t elementId, const ActionParam& param, const int32_t windowId)
{
auto action = param.action;
auto actionArguments = param.actionArguments;
bool actionResult = false;
auto context = GetPipelineByWindowId(windowId);
if (!context) {
return;
}
if (AceType::InstanceOf<NG::PipelineContext>(context)) {
actionResult = ExecuteActionNG(elementId, actionArguments, action, context, NG::UI_EXTENSION_OFFSET_MAX);
} else {
auto node = GetAccessibilityNodeFromPage(elementId);
if (!node) {
return;
}
actionResult =
AccessibilityActionEvent(action, actionArguments, node, AceType::DynamicCast<PipelineContext>(context));
}
if (actionResult && AceType::InstanceOf<PipelineContext>(context)) {
SendActionEvent(action, elementId);
}
}
void AccessibilityManagerImpl::RequestUpdate(const int64_t elementId, const int32_t windowId)
{
AccessibilityEvent accessibilityEvent;
accessibilityEvent.nodeId = static_cast<int64_t>(elementId);
ProcessAccessibilityEvent(accessibilityEvent, false, static_cast<size_t>(accessibilityEvent.type));
}
void AccessibilityManagerImpl::UpdateElementInfosByAccessibilityId(
int64_t elementId, int32_t windowId, size_t eventType)
{
std::lock_guard<std::mutex> lock(ocNodeUpdateMutex_);
std::list<AccessibilityElementInfo> infos;
auto pipeline = GetPipelineByWindowId(windowId);
CHECK_NULL_VOID(pipeline);
auto ngPipeline = AceType::DynamicCast<NG::PipelineContext>(pipeline);
CHECK_NULL_VOID(ngPipeline);
int32_t mode = DEFAULT_ID;
SearchElementInfoByAccessibilityIdNG(elementId, mode, infos, pipeline, NG::UI_EXTENSION_OFFSET_MAX);
UpdateNodesOC(infos, windowId, eventType);
}
RefPtr<NG::PipelineContext> AccessibilityManagerImpl::GetPipelineByWindowId(uint32_t windowId)
{
auto mainPipeline = AceType::DynamicCast<NG::PipelineContext>(context_.Upgrade());
if (mainPipeline != nullptr && mainPipeline->GetWindowId() == windowId) {
return mainPipeline;
}
for (auto subPipelineWeak : GetSubPipelineContexts()) {
auto subContextNG = AceType::DynamicCast<NG::PipelineContext>(subPipelineWeak.Upgrade());
if (subContextNG != nullptr && subContextNG->GetWindowId() == windowId) {
return subContextNG;
}
}
if (GetWindowId() == windowId) {
return mainPipeline;
}
return nullptr;
}
RefPtr<PipelineBase> AccessibilityManagerImpl::GetPipelineByWindowId(const int32_t windowId)
{
auto context = context_.Upgrade();
CHECK_NULL_RETURN(context, nullptr);
if (AceType::InstanceOf<NG::PipelineContext>(context)) {
CHECK_NULL_RETURN(context, nullptr);
if (context->GetWindowId() == static_cast<uint32_t>(windowId)) {
return context;
}
if (GetWindowId() == static_cast<uint32_t>(windowId)) {
return context;
}
for (auto& subContext : GetSubPipelineContexts()) {
context = subContext.Upgrade();
CHECK_NULL_RETURN(context, nullptr);
if (context->GetWindowId() == static_cast<uint32_t>(windowId)) {
return context;
}
}
return nullptr;
} else {
return context;
}
}
void AccessibilityManagerImpl::SetAccessibilityGroupSpecific(RefPtr<OHOS::Ace::NG::FrameNode> node)
{
CHECK_NULL_VOID(node);
auto parent = node->GetParentFrameNode();
CHECK_NULL_VOID(parent);
if (parent->GetTag() == V2::TAB_BAR_ETS_TAG || node->GetTag() == "SelectMenuButton" ||
node->GetTag() == V2::OPTION_ETS_TAG) {
auto accessibilityProperty = node->GetAccessibilityProperty<NG::AccessibilityProperty>();
CHECK_NULL_VOID(accessibilityProperty);
accessibilityProperty->SetAccessibilityGroup(true);
}
}
void AccessibilityManagerImpl::SearchElementInfoByAccessibilityIdNG(int64_t elementId, int32_t mode,
std::list<AccessibilityElementInfo>& infos, const RefPtr<PipelineBase>& context, int64_t uiExtensionOffset)
{
auto mainContext = context_.Upgrade();
CHECK_NULL_VOID(mainContext);
auto ngPipeline = AceType::DynamicCast<NG::PipelineContext>(context);
CHECK_NULL_VOID(ngPipeline);
auto rootNode = ngPipeline->GetRootElement();
CHECK_NULL_VOID(rootNode);
AccessibilityElementInfo nodeInfo;
int64_t nodeId = elementId;
if (elementId == DEFAULT_ElEMENTID) {
nodeId = rootNode->GetAccessibilityId();
}
CommonProperty commonProperty;
GenerateCommonProperty(ngPipeline, commonProperty, mainContext);
auto node = GetFramenodeByAccessibilityId(rootNode, nodeId);
CHECK_NULL_VOID(node);
SetAccessibilityGroupSpecific(node);
UpdateAccessibilityElementInfo(node, commonProperty, nodeInfo, ngPipeline);
if (IsExtensionComponent(node) && !IsUIExtensionShowPlaceholder(node)) {
SearchParameter param { DEFAULT_ElEMENTID, "", mode, uiExtensionOffset };
SearchExtensionElementInfoNG(param, node, infos, nodeInfo);
}
infos.push_back(nodeInfo);
if ((infos.size() > 0) && (uiExtensionOffset != NG::UI_EXTENSION_OFFSET_MAX) &&
(infos.front().GetComponentType() != V2::ROOT_ETS_TAG) &&
(infos.front().GetParentNodeId() == rootNode->GetAccessibilityId())) {
infos.front().SetParent(NG::UI_EXTENSION_ROOT_ID);
}
for (auto childId : nodeInfo.GetChildIds()) {
SearchElementInfoByAccessibilityIdNG(childId, mode, infos, context, uiExtensionOffset);
}
}
std::string AccessibilityManagerImpl::GetPagePath()
{
auto context = context_.Upgrade();
CHECK_NULL_RETURN(context, "");
auto frontend = context->GetFrontend();
CHECK_NULL_RETURN(frontend, "");
ContainerScope scope(context->GetInstanceId());
return frontend->GetPagePath();
}
void AccessibilityManagerImpl::GenerateCommonProperty(
const RefPtr<PipelineBase>& context, CommonProperty& output, const RefPtr<PipelineBase>& mainContext)
{
auto ngPipeline = AceType::DynamicCast<NG::PipelineContext>(context);
CHECK_NULL_VOID(ngPipeline);
auto stageManager = ngPipeline->GetStageManager();
CHECK_NULL_VOID(stageManager);
if (!ngPipeline->IsFormRender()) {
output.windowId = static_cast<int32_t>(ngPipeline->GetFocusWindowId());
} else {
output.windowId = static_cast<int32_t>(GetWindowId());
}
output.windowLeft = GetWindowLeft(ngPipeline->GetWindowId());
output.windowTop = GetWindowTop(ngPipeline->GetWindowId());
auto page = stageManager->GetLastPageWithTransition();
if (page != nullptr) {
output.pageId = page->GetPageId();
output.pagePath = GetPagePath();
}
if (context->GetWindowId() != mainContext->GetWindowId()) {
output.pageId = DEFAULT_ID;
output.pagePath = "";
}
}
RefPtr<NG::FrameNode> AccessibilityManagerImpl::GetFramenodeByAccessibilityId(
const RefPtr<NG::FrameNode>& root, int64_t id)
{
CHECK_NULL_RETURN(root, nullptr);
if (root->GetAccessibilityId() == id) {
return root;
}
std::queue<NG::UINode*> nodes;
nodes.push(Referenced::RawPtr(root));
NG::UINode* virtualNode = nullptr;
RefPtr<NG::FrameNode> frameNodeResult = nullptr;
while (!nodes.empty()) {
auto current = nodes.front();
nodes.pop();
if (current->HasVirtualNodeAccessibilityProperty()) {
auto fnode = AceType::DynamicCast<NG::FrameNode>(current);
auto property = fnode->GetAccessibilityProperty<NG::AccessibilityProperty>();
const auto& children = std::list<RefPtr<NG::UINode>> { property->GetAccessibilityVirtualNode() };
if (FindFrameNodeByAccessibilityId(id, children, nodes, frameNodeResult)) {
return frameNodeResult;
}
} else {
const auto& children = current->GetChildren(true);
if (FindFrameNodeByAccessibilityId(id, children, nodes, frameNodeResult)) {
return frameNodeResult;
}
}
}
return nullptr;
}
void AccessibilityManagerImpl::UpdateAccessibilityElementInfo(const RefPtr<NG::FrameNode>& node,
const CommonProperty& commonProperty, AccessibilityElementInfo& nodeInfo,
const RefPtr<NG::PipelineContext>& ngPipeline)
{
CHECK_NULL_VOID(node);
nodeInfo.SetParent(GetParentId(node));
auto accessibilityProperty = node->GetAccessibilityProperty<NG::AccessibilityProperty>();
CHECK_NULL_VOID(accessibilityProperty);
if (!accessibilityProperty->IsAccessibilityGroup() || node->GetTag() == "root") {
UpdateChildrenOfAccessibilityElementInfo(node, commonProperty, nodeInfo);
}
nodeInfo.SetAccessibilityId(node->GetAccessibilityId());
nodeInfo.SetComponentType(node->GetTag());
nodeInfo.SetEnabled(node->GetFocusHub() ? node->GetFocusHub()->IsEnabled() : true);
nodeInfo.SetFocused(node->GetFocusHub() ? node->GetFocusHub()->IsCurrentFocus() : false);
nodeInfo.SetAccessibilityFocus(node->GetRenderContext()->GetAccessibilityFocus().value_or(false));
nodeInfo.SetInspectorKey(node->GetInspectorId().value_or(""));
nodeInfo.SetVisible(node->IsVisible());
if (node->IsAccessibilityVirtualNode()) {
auto rect = node->GetVirtualNodeTransformRectRelativeToWindow();
auto left = rect.Left() + commonProperty.windowLeft;
auto top = rect.Top() + commonProperty.windowTop;
auto right = rect.Right() + commonProperty.windowLeft;
auto bottom = rect.Bottom() + commonProperty.windowTop;
Accessibility::Rect bounds { left, top, right, bottom };
nodeInfo.SetRectInScreen(bounds);
} else if (node->IsVisible()) {
auto rect = node->GetTransformRectRelativeToWindow();
auto left = rect.Left() + commonProperty.windowLeft;
auto top = rect.Top() + commonProperty.windowTop;
auto right = rect.Right() + commonProperty.windowLeft;
auto bottom = rect.Bottom() + commonProperty.windowTop;
Accessibility::Rect bounds { left, top, right, bottom };
nodeInfo.SetRectInScreen(bounds);
}
nodeInfo.SetWindowId(commonProperty.windowId);
nodeInfo.SetPageId(node->GetPageId());
nodeInfo.SetPagePath(commonProperty.pagePath);
nodeInfo.SetBundleName(AceApplicationInfo::GetInstance().GetPackageName());
if (nodeInfo.IsEnabled()) {
nodeInfo.SetFocusable(node->GetFocusHub() ? node->GetFocusHub()->IsFocusable() : false);
nodeInfo.SetPopupSupported(IsPopupSupported(ngPipeline, node->GetId()));
}
nodeInfo.SetComponentResourceId(node->GetInspectorId().value_or(""));
UpdateAccessibilityElementInfo(node, nodeInfo);
}
void UpdateBasicAccessibilityInfo(const RefPtr<NG::FrameNode>& node, AccessibilityElementInfo& nodeInfo,
const RefPtr<NG::AccessibilityProperty>& accessibilityProperty)
{
CHECK_NULL_VOID(accessibilityProperty);
if (accessibilityProperty->HasAccessibilityRole()) {
nodeInfo.SetComponentType(accessibilityProperty->GetAccessibilityRole());
}
nodeInfo.SetAccessibilityId(node->GetAccessibilityId());
nodeInfo.SetParent(GetParentId(node));
nodeInfo.SetComponentType(node->GetTag());
nodeInfo.SetEnabled(node->GetFocusHub() ? node->GetFocusHub()->IsEnabled() : true);
nodeInfo.SetFocused(node->GetFocusHub() ? node->GetFocusHub()->IsCurrentFocus() : false);
nodeInfo.SetAccessibilityFocus(node->GetRenderContext()->GetAccessibilityFocus().value_or(false));
nodeInfo.SetInspectorKey(node->GetInspectorId().value_or(""));
nodeInfo.SetVisible(node->IsVisible());
}
void UpdateAccessibilityTextInfo(
AccessibilityElementInfo& nodeInfo, const RefPtr<NG::AccessibilityProperty>& accessibilityProperty)
{
CHECK_NULL_VOID(accessibilityProperty);
if (accessibilityProperty->HasUserTextValue()) {
nodeInfo.SetContent(accessibilityProperty->GetUserTextValue());
} else if (accessibilityProperty->GetText().length() > 0) {
nodeInfo.SetContent(accessibilityProperty->GetText());
} else {
nodeInfo.SetContent(accessibilityProperty->GetGroupText());
}
nodeInfo.SetAccessibilityText(accessibilityProperty->GetAccessibilityText());
nodeInfo.SetHint(accessibilityProperty->GetHintText());
nodeInfo.SetAccessibilityGroup(accessibilityProperty->IsAccessibilityGroup());
nodeInfo.SetAccessibilityLevel(accessibilityProperty->GetAccessibilityLevel());
nodeInfo.SetTextType(accessibilityProperty->GetTextType());
nodeInfo.SetTextLengthLimit(accessibilityProperty->GetTextLengthLimit());
nodeInfo.SetOffset(accessibilityProperty->GetScrollOffSet());
}
void UpdateAccessibilityRangeInfo(
AccessibilityElementInfo& nodeInfo, const RefPtr<NG::AccessibilityProperty>& accessibilityProperty)
{
CHECK_NULL_VOID(accessibilityProperty);
if (accessibilityProperty->HasRange()) {
RangeInfo rangeInfo = ConvertAccessibilityValue(accessibilityProperty->GetAccessibilityValue());
nodeInfo.SetRange(rangeInfo);
}
}
void UpdateAccessibilityContextInfo(const RefPtr<NG::FrameNode>& node, AccessibilityElementInfo& nodeInfo)
{
CHECK_NULL_VOID(node);
auto context = node->GetRenderContext();
if (context != nullptr) {
nodeInfo.SetZIndex(context->GetZIndex().value_or(0));
nodeInfo.SetOpacity(context->GetOpacity().value_or(1));
nodeInfo.SetBackgroundColor(context->GetBackgroundColor().value_or(Color::TRANSPARENT).ToString());
nodeInfo.SetBackgroundImage(context->GetBackgroundImage().value_or(ImageSourceInfo("")).ToString());
if (context->GetForeground() != nullptr) {
nodeInfo.SetBlur(context->GetForeground()->propBlurRadius.value_or(Dimension(0)).ToString());
}
}
}
void UpdateAccessibilityStateInfo(
AccessibilityElementInfo& nodeInfo, const RefPtr<NG::AccessibilityProperty>& accessibilityProperty)
{
CHECK_NULL_VOID(accessibilityProperty);
if (accessibilityProperty->HasUserDisabled()) {
nodeInfo.SetEnabled(!accessibilityProperty->IsUserDisabled());
}
if (accessibilityProperty->HasUserCheckedType()) {
nodeInfo.SetChecked(accessibilityProperty->GetUserCheckedType());
} else {
nodeInfo.SetChecked(accessibilityProperty->IsChecked());
}
if (accessibilityProperty->HasUserSelected()) {
nodeInfo.SetSelected(accessibilityProperty->IsUserSelected());
} else {
nodeInfo.SetSelected(accessibilityProperty->IsSelected());
}
nodeInfo.SetPassword(accessibilityProperty->IsPassword());
nodeInfo.SetPluraLineSupported(accessibilityProperty->IsMultiLine());
nodeInfo.SetHinting(accessibilityProperty->IsHint());
nodeInfo.SetDescriptionInfo(accessibilityProperty->GetAccessibilityDescription());
if (accessibilityProperty->HasUserCurrentValue()) {
nodeInfo.SetCurrentIndex(accessibilityProperty->GetUserCurrentValue());
} else {
nodeInfo.SetCurrentIndex(accessibilityProperty->GetCurrentIndex());
}
if (accessibilityProperty->HasUserMinValue()) {
nodeInfo.SetBeginIndex(accessibilityProperty->GetUserMinValue());
} else {
nodeInfo.SetBeginIndex(accessibilityProperty->GetBeginIndex());
}
if (accessibilityProperty->HasUserMaxValue()) {
nodeInfo.SetEndIndex(accessibilityProperty->GetUserMaxValue());
} else {
nodeInfo.SetEndIndex(accessibilityProperty->GetEndIndex());
}
}
void UpdateAccessibilityGridInfo(
AccessibilityElementInfo& nodeInfo, const RefPtr<NG::AccessibilityProperty>& accessibilityProperty)
{
CHECK_NULL_VOID(accessibilityProperty);
GridInfo gridInfo(accessibilityProperty->GetCollectionInfo().rows,
accessibilityProperty->GetCollectionInfo().columns, accessibilityProperty->GetCollectionInfo().selectMode);
nodeInfo.SetGrid(gridInfo);
int32_t row = accessibilityProperty->GetCollectionItemInfo().row;
int32_t column = accessibilityProperty->GetCollectionItemInfo().column;
int32_t rowSpan = accessibilityProperty->GetCollectionItemInfo().rowSpan;
int32_t columnSpan = accessibilityProperty->GetCollectionItemInfo().columnSpan;
bool heading = accessibilityProperty->GetCollectionItemInfo().heading;
GridItemInfo gridItemInfo(row, rowSpan, column, columnSpan, heading, nodeInfo.IsSelected());
nodeInfo.SetGridItem(gridItemInfo);
}
void UpdateAccessibilityExtraInfo(
AccessibilityElementInfo& nodeInfo, const RefPtr<NG::AccessibilityProperty>& accessibilityProperty)
{
CHECK_NULL_VOID(accessibilityProperty);
ExtraElementInfo extraElementInfo {};
accessibilityProperty->GetAllExtraElementInfo(extraElementInfo);
nodeInfo.SetExtraElement(extraElementInfo);
auto tag = nodeInfo.GetComponentType();
if (tag == V2::TOAST_ETS_TAG || tag == V2::POPUP_ETS_TAG || tag == V2::DIALOG_ETS_TAG ||
tag == V2::ACTION_SHEET_DIALOG_ETS_TAG || tag == V2::ALERT_DIALOG_ETS_TAG || tag == V2::MENU_ETS_TAG ||
tag == "SelectMenu") {
nodeInfo.SetLiveRegion(1);
}
nodeInfo.SetContentInvalid(accessibilityProperty->GetContentInvalid());
nodeInfo.SetError(accessibilityProperty->GetErrorText());
nodeInfo.SetSelectedBegin(accessibilityProperty->GetTextSelectionStart());
nodeInfo.SetSelectedEnd(accessibilityProperty->GetTextSelectionEnd());
nodeInfo.SetInputType(static_cast<int>(accessibilityProperty->GetTextInputType()));
nodeInfo.SetItemCounts(accessibilityProperty->GetCollectionItemCounts());
nodeInfo.SetChildTreeIdAndWinId(accessibilityProperty->GetChildTreeId(), accessibilityProperty->GetChildWindowId());
}
void AccessibilityManagerImpl::UpdateAccessibilityElementInfo(
const RefPtr<NG::FrameNode>& node, AccessibilityElementInfo& nodeInfo)
{
CHECK_NULL_VOID(node);
auto accessibilityProperty = node->GetAccessibilityProperty<NG::AccessibilityProperty>();
CHECK_NULL_VOID(accessibilityProperty);
UpdateBasicAccessibilityInfo(node, nodeInfo, accessibilityProperty);
UpdateAccessibilityTextInfo(nodeInfo, accessibilityProperty);
UpdateAccessibilityRangeInfo(nodeInfo, accessibilityProperty);
UpdateAccessibilityContextInfo(node, nodeInfo);
UpdateAccessibilityStateInfo(nodeInfo, accessibilityProperty);
UpdateAccessibilityGridInfo(nodeInfo, accessibilityProperty);
UpdateAccessibilityExtraInfo(nodeInfo, accessibilityProperty);
if (nodeInfo.GetWindowId() == static_cast<int32_t>(windowId_)) {
nodeInfo.SetBelongTreeId(treeId_);
nodeInfo.SetParentWindowId(parentWindowId_);
} else {
nodeInfo.SetBelongTreeId(0);
nodeInfo.SetParentWindowId(0);
}
SetAccessibilityFocusAction(nodeInfo, "ace");
if (nodeInfo.IsEnabled()) {
UpdateSupportAction(node, nodeInfo);
nodeInfo.SetCheckable(accessibilityProperty->IsCheckable());
nodeInfo.SetScrollable(accessibilityProperty->IsScrollable());
nodeInfo.SetEditable(accessibilityProperty->IsEditable());
nodeInfo.SetDeletable(accessibilityProperty->IsDeletable());
accessibilityProperty->ResetSupportAction();
auto supportAceActions = accessibilityProperty->GetSupportAction();
for (auto it = supportAceActions.begin(); it != supportAceActions.end(); ++it) {
AccessibleAction action(ConvertAceAction(*it), "ace");
nodeInfo.AddAction(action);
}
}
}
void AccessibilityManagerImpl::UpdateNodeChildIds(const RefPtr<AccessibilityNode>& node)
{
CHECK_NULL_VOID(node);
node->ActionUpdateIds();
const auto& children = node->GetChildList();
std::vector<int32_t> childrenVec;
auto cardId = GetCardId();
auto rootNodeId = GetRootNodeId();
if ((node->GetNodeId() == GetRootNodeId() + ROOT_STACK_BASE) && !children.empty() && !IsDeclarative()) {
auto lastChildNodeId = children.back()->GetNodeId();
if (isOhosHostCard()) {
childrenVec.emplace_back(ConvertToCardAccessibilityId(lastChildNodeId, cardId, rootNodeId));
} else {
childrenVec.emplace_back(lastChildNodeId);
for (const auto& child : children) {
if (child->GetNodeId() == ROOT_DECOR_BASE - 1) {
childrenVec.emplace_back(child->GetNodeId());
break;
}
}
}
} else {
childrenVec.resize(children.size());
if (isOhosHostCard()) {
std::transform(children.begin(), children.end(), childrenVec.begin(),
[cardId, rootNodeId](const RefPtr<AccessibilityNode>& child) {
return ConvertToCardAccessibilityId(child->GetNodeId(), cardId, rootNodeId);
});
} else {
std::transform(children.begin(), children.end(), childrenVec.begin(),
[](const RefPtr<AccessibilityNode>& child) { return child->GetNodeId(); });
}
}
node->SetChildIds(childrenVec);
}
void AccessibilityManagerImpl::SetPipelineContext(const RefPtr<PipelineBase>& context)
{
context_ = context;
}
void AccessibilityManagerImpl::FireAccessibilityEventCallback(uint32_t eventId, int64_t parameter)
{
auto eventType = static_cast<AccessibilityCallbackEventId>(eventId);
AccessibilityEvent event;
switch (eventType) {
case AccessibilityCallbackEventId::ON_LOAD_PAGE:
event.nodeId = parameter;
event.windowChangeTypes = WindowUpdateType::WINDOW_UPDATE_ACTIVE;
event.type = AccessibilityEventType::CHANGE;
SendAccessibilityAsyncEvent(event);
break;
default:
break;
}
}
}