1 /*
2 * Copyright (c) 2022-2024 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 #include "js_third_provider_interaction_operation.h"
16
17 #include <algorithm>
18
19 #include "accessibility_constants.h"
20 #include "accessibility_event_info.h"
21 #include "accessibility_system_ability_client.h"
22 #include "adapter/ohos/entrance/ace_application_info.h"
23 #include "adapter/ohos/entrance/ace_container.h"
24 #include "base/log/ace_trace.h"
25 #include "base/log/dump_log.h"
26 #include "base/log/event_report.h"
27 #include "base/log/log.h"
28 #include "base/utils/linear_map.h"
29 #include "base/utils/string_utils.h"
30 #include "base/utils/utils.h"
31 #include "core/accessibility/accessibility_manager_ng.h"
32 #include "core/components_ng/base/inspector.h"
33 #include "core/components_v2/inspector/inspector_constants.h"
34 #include "core/pipeline/pipeline_context.h"
35 #include "core/pipeline_ng/pipeline_context.h"
36 #include "frameworks/bridge/common/dom/dom_type.h"
37 #include "frameworks/core/components_ng/pattern/web/web_pattern.h"
38 #include "js_accessibility_manager.h"
39 #include "js_third_accessibility_hover_ng.h"
40 #include "nlohmann/json.hpp"
41
42 using namespace OHOS::Accessibility;
43 using namespace OHOS::AccessibilityConfig;
44 using namespace std;
45
46 namespace OHOS::Ace::Framework {
47 constexpr int32_t ACCESSIBILITY_FOCUS_WITHOUT_EVENT = -2100001;
48
GetElementInfoForThird(int64_t elementId,AccessibilityElementInfo & info,int64_t hostElementId)49 bool AccessibilityHoverManagerForThirdNG::GetElementInfoForThird(
50 int64_t elementId,
51 AccessibilityElementInfo& info,
52 int64_t hostElementId)
53 {
54 auto jsThirdProviderOperator =
55 GetJsThirdProviderInteractionOperation(hostElementId).lock();
56 if (jsThirdProviderOperator == nullptr) {
57 TAG_LOGE(AceLogTag::ACE_ACCESSIBILITY,
58 "third jsThirdProviderOperator ptr is null, hostElementId %{public}" PRId64,
59 hostElementId);
60 return false;
61 }
62
63 std::list<Accessibility::AccessibilityElementInfo> infos;
64 bool ret = jsThirdProviderOperator->FindAccessibilityNodeInfosByIdFromProvider(
65 elementId, 0, 0, infos);
66 if ((!ret) || (infos.size() == 0)) {
67 TAG_LOGE(AceLogTag::ACE_ACCESSIBILITY,
68 "cannot get third elementinfo :%{public}" PRId64 ", ret: %{public}d",
69 elementId, ret);
70 return false;
71 }
72 info = infos.front();
73 return true;
74 }
75
UpdateSearchStrategyByHitTestModeStr(std::string & hitTestMode,bool & shouldSearchSelf,bool & shouldSearchChildren)76 void AccessibilityHoverManagerForThirdNG::UpdateSearchStrategyByHitTestModeStr(
77 std::string& hitTestMode,
78 bool& shouldSearchSelf,
79 bool& shouldSearchChildren)
80 {
81 if (hitTestMode == "HitTestMode.Block") {
82 shouldSearchChildren = false;
83 } else if (hitTestMode == "HitTestMode.None") {
84 shouldSearchSelf = false;
85 }
86 }
87
HasAccessibilityTextOrDescription(const AccessibilityElementInfo & nodeInfo)88 bool AccessibilityHoverManagerForThirdNG::HasAccessibilityTextOrDescription(
89 const AccessibilityElementInfo& nodeInfo)
90 {
91 std::optional<std::string> accessibilityText = nodeInfo.GetAccessibilityText();
92 std::optional<std::string> accessibilityDescription = nodeInfo.GetDescriptionInfo();
93 return !accessibilityText.value_or("").empty() ||
94 !accessibilityDescription.value_or("").empty();
95 }
96
IsAccessibilityFocusable(const AccessibilityElementInfo & nodeInfo)97 bool AccessibilityHoverManagerForThirdNG::IsAccessibilityFocusable(
98 const AccessibilityElementInfo& nodeInfo)
99 {
100 auto level = nodeInfo.GetAccessibilityLevel();
101 if (level == NG::AccessibilityProperty::Level::YES_STR) {
102 return true;
103 }
104 if (level == NG::AccessibilityProperty::Level::NO_STR) {
105 return false;
106 }
107 if (nodeInfo.GetAccessibilityGroup() ||
108 !nodeInfo.GetActionList().empty() ||
109 HasAccessibilityTextOrDescription(nodeInfo) ||
110 !nodeInfo.GetContent().empty()) {
111 return true;
112 }
113 // expand to enabled and clickable
114 // default tag
115 if (NG::AccessibilityProperty::IsAccessibilityFocusableTag(
116 nodeInfo.GetComponentType()) == true) {
117 return true;
118 }
119 return false;
120 }
121
GetSearchStrategyForThird(const AccessibilityElementInfo & nodeInfo)122 std::pair<bool, bool> AccessibilityHoverManagerForThirdNG::GetSearchStrategyForThird(
123 const AccessibilityElementInfo& nodeInfo)
124 {
125 bool shouldSearchSelf = true;
126 bool shouldSearchChildren = true;
127 auto level = NG::AccessibilityProperty::Level::AUTO;
128 do {
129 level = nodeInfo.GetAccessibilityLevel();
130 bool hasAccessibilityText = HasAccessibilityTextOrDescription(nodeInfo);
131 if (level == NG::AccessibilityProperty::Level::YES_STR) {
132 break;
133 } else if (level == NG::AccessibilityProperty::Level::NO_HIDE_DESCENDANTS) {
134 shouldSearchSelf = false;
135 shouldSearchChildren = false;
136 break;
137 } else {
138 if (level == NG::AccessibilityProperty::Level::NO_STR) {
139 shouldSearchSelf = false;
140 } else {
141 // shouldSearchSelf is true here
142 if (hasAccessibilityText) {
143 break;
144 }
145 }
146 }
147
148 auto hitTestMode = nodeInfo.GetHitTestBehavior();
149 UpdateSearchStrategyByHitTestModeStr(
150 hitTestMode, shouldSearchSelf, shouldSearchChildren);
151 } while (0);
152
153 if (IsAccessibilityFocusable(nodeInfo) == false) {
154 shouldSearchSelf = false;
155 }
156
157 return std::make_pair(shouldSearchSelf, shouldSearchChildren);
158 }
159
160
HoverPathForThirdRecursive(const int64_t hostElementId,const NG::PointF & hoverPoint,const AccessibilityElementInfo & nodeInfo,AccessibilityHoverTestPathForThird & path,NG::OffsetF hostOffset)161 bool AccessibilityHoverManagerForThirdNG::HoverPathForThirdRecursive(
162 const int64_t hostElementId,
163 const NG::PointF& hoverPoint,
164 const AccessibilityElementInfo& nodeInfo,
165 AccessibilityHoverTestPathForThird& path,
166 NG::OffsetF hostOffset)
167 {
168 bool hitTarget = false;
169 auto [shouldSearchSelf, shouldSearchChildren]
170 = GetSearchStrategyForThird(nodeInfo);
171 auto rectInScreen = nodeInfo.GetRectInScreen();
172 auto left = rectInScreen.GetLeftTopXScreenPostion();
173 auto right = rectInScreen.GetLeftTopYScreenPostion();
174 auto width = rectInScreen.GetRightBottomXScreenPostion() - rectInScreen.GetLeftTopXScreenPostion();
175 auto height = rectInScreen.GetRightBottomYScreenPostion() - rectInScreen.GetLeftTopYScreenPostion();
176 NG::RectF rect { left, right, width, height };
177 rect = rect - hostOffset;
178 bool hitSelf = rect.IsInnerRegion(hoverPoint);
179 if (hitSelf && shouldSearchSelf) {
180 hitTarget = true;
181 path.push_back(nodeInfo.GetAccessibilityId());
182 }
183 TAG_LOGD(AceLogTag::ACE_ACCESSIBILITY,
184 "third hover elementId :%{public}" PRId64\
185 ", shouldSearchSelf: %{public}d shouldSearchChildren: %{public}d hitTarget: %{public}d ",
186 nodeInfo.GetAccessibilityId(), shouldSearchSelf, shouldSearchChildren, hitTarget);
187 if (shouldSearchChildren) {
188 auto childrenIds = nodeInfo.GetChildIds();
189 for (auto childId = childrenIds.rbegin(); childId != childrenIds.rend(); ++childId) {
190 AccessibilityElementInfo childInfo;
191 if (GetElementInfoForThird(*childId, childInfo, hostElementId) == false) {
192 break;
193 }
194 if (HoverPathForThirdRecursive(
195 hostElementId, hoverPoint, childInfo, path, hostOffset)) {
196 return true;
197 }
198 }
199 }
200 return hitTarget;
201 }
202
HoverPathForThird(const int64_t hostElementId,const NG::PointF & point,AccessibilityElementInfo & rootInfo,NG::OffsetF hostOffset)203 AccessibilityHoverTestPathForThird AccessibilityHoverManagerForThirdNG::HoverPathForThird(
204 const int64_t hostElementId,
205 const NG::PointF& point,
206 AccessibilityElementInfo& rootInfo,
207 NG::OffsetF hostOffset)
208 {
209 AccessibilityHoverTestPathForThird path;
210 HoverPathForThirdRecursive(
211 hostElementId, point, rootInfo, path, hostOffset);
212 return path;
213 }
214
ResetHoverForThirdState()215 void AccessibilityHoverManagerForThirdNG::ResetHoverForThirdState()
216 {
217 hoverForThirdState_.idle = true;
218 hoverForThirdState_.nodesHovering.clear();
219 }
220
HandleAccessibilityHoverForThird(const AccessibilityHoverForThirdConfig & config)221 void AccessibilityHoverManagerForThirdNG::HandleAccessibilityHoverForThird(
222 const AccessibilityHoverForThirdConfig& config)
223 {
224 CHECK_NULL_VOID(config.hostNode);
225 if (config.eventType == NG::AccessibilityHoverEventType::ENTER) {
226 ResetHoverForThirdState();
227 }
228 std::vector<int64_t> currentNodesHovering;
229 std::vector<int64_t> lastNodesHovering = hoverForThirdState_.nodesHovering;
230 if (config.eventType != NG::AccessibilityHoverEventType::EXIT) {
231 AccessibilityElementInfo rootInfo;
232 if (GetElementInfoForThird(-1, rootInfo, config.hostElementId) == false) {
233 return;
234 }
235 auto [displayOffset, err] = config.hostNode->GetPaintRectGlobalOffsetWithTranslate();
236 AccessibilityHoverTestPathForThird path =
237 HoverPathForThird(config.hostElementId, config.point, rootInfo, displayOffset);
238 for (const auto& node: path) {
239 currentNodesHovering.push_back(node);
240 }
241 }
242 static constexpr int64_t INVALID_NODE_ID = -1;
243 int64_t lastHoveringId = INVALID_NODE_ID;
244 if (!lastNodesHovering.empty()) {
245 lastHoveringId = lastNodesHovering.back();
246 }
247 int64_t currentHoveringId = INVALID_NODE_ID;
248 if (!currentNodesHovering.empty()) {
249 currentHoveringId = currentNodesHovering.back();
250 }
251 auto jsThirdProviderOperator = GetJsThirdProviderInteractionOperation(
252 config.hostElementId).lock();
253 if (jsThirdProviderOperator == nullptr) {
254 TAG_LOGE(AceLogTag::ACE_ACCESSIBILITY, "jsThirdProviderOperator is null, "
255 "hostElementId %{public}" PRId64, config.hostElementId);
256 return;
257 }
258 if (lastHoveringId != INVALID_NODE_ID && lastHoveringId != currentHoveringId) {
259 jsThirdProviderOperator->SendAccessibilityAsyncEventForThird(lastHoveringId,
260 Accessibility::EventType::TYPE_VIEW_HOVER_EXIT_EVENT);
261 }
262 if ((currentHoveringId != INVALID_NODE_ID) && (currentHoveringId != lastHoveringId)) {
263 jsThirdProviderOperator->SendAccessibilityAsyncEventForThird(currentHoveringId,
264 Accessibility::EventType::TYPE_VIEW_HOVER_ENTER_EVENT);
265 }
266 hoverForThirdState_.nodesHovering = std::move(currentNodesHovering);
267 hoverForThirdState_.time = config.time;
268 hoverForThirdState_.source = config.sourceType;
269 hoverForThirdState_.idle = config.eventType == NG::AccessibilityHoverEventType::EXIT;
270 }
271
ClearThirdAccessibilityFocus(const RefPtr<NG::FrameNode> & hostNode)272 bool AccessibilityHoverManagerForThirdNG::ClearThirdAccessibilityFocus(
273 const RefPtr<NG::FrameNode>& hostNode)
274 {
275 CHECK_NULL_RETURN(hostNode, false);
276 RefPtr<NG::RenderContext> renderContext = hostNode->GetRenderContext();
277 CHECK_NULL_RETURN(renderContext, false);
278 renderContext->UpdateAccessibilityFocus(false);
279 return true;
280 }
281
ActThirdAccessibilityFocus(int64_t elementId,const AccessibilityElementInfo & nodeInfo,const RefPtr<NG::FrameNode> & hostNode,const RefPtr<NG::PipelineContext> & context,bool isNeedClear)282 bool AccessibilityHoverManagerForThirdNG::ActThirdAccessibilityFocus(
283 int64_t elementId,
284 const AccessibilityElementInfo& nodeInfo,
285 const RefPtr<NG::FrameNode>& hostNode,
286 const RefPtr<NG::PipelineContext>& context,
287 bool isNeedClear)
288 {
289 CHECK_NULL_RETURN(hostNode, false);
290 RefPtr<NG::RenderContext> renderContext = nullptr;
291 renderContext = hostNode->GetRenderContext();
292 CHECK_NULL_RETURN(renderContext, false);
293 if (isNeedClear) {
294 renderContext->UpdateAccessibilityFocus(false);
295 TAG_LOGD(AceLogTag::ACE_ACCESSIBILITY,
296 "third act Accessibility element Id %{public}" PRId64 "Focus clear",
297 nodeInfo.GetAccessibilityId());
298 return true;
299 }
300 renderContext->UpdateAccessibilityFocus(false);
301 auto [displayOffset, err] = hostNode->GetPaintRectGlobalOffsetWithTranslate();
302 auto rectInScreen = nodeInfo.GetRectInScreen();
303 auto left = rectInScreen.GetLeftTopXScreenPostion() - static_cast<int32_t>(displayOffset.GetX());
304 auto right = rectInScreen.GetLeftTopYScreenPostion() - static_cast<int32_t>(displayOffset.GetY());
305 auto width = rectInScreen.GetRightBottomXScreenPostion() - rectInScreen.GetLeftTopXScreenPostion();
306 auto height = rectInScreen.GetRightBottomYScreenPostion() - rectInScreen.GetLeftTopYScreenPostion();
307 NG::RectT<int32_t> rectInt { static_cast<int32_t>(left), static_cast<int32_t>(right),
308 static_cast<int32_t>(width), static_cast<int32_t>(height) };
309
310 renderContext->UpdateAccessibilityFocusRect(rectInt);
311 renderContext->UpdateAccessibilityFocus(true, ACCESSIBILITY_FOCUS_WITHOUT_EVENT);
312 TAG_LOGD(AceLogTag::ACE_ACCESSIBILITY,
313 "third act Accessibility element Id %{public}" PRId64 "Focus",
314 nodeInfo.GetAccessibilityId());
315 return true;
316 }
317
RegisterJsThirdProviderInteractionOperation(int64_t hostElementId,const std::shared_ptr<JsThirdProviderInteractionOperation> & jsThirdProviderOperator)318 void AccessibilityHoverManagerForThirdNG::RegisterJsThirdProviderInteractionOperation(
319 int64_t hostElementId,
320 const std::shared_ptr<JsThirdProviderInteractionOperation>& jsThirdProviderOperator)
321 {
322 jsThirdProviderOperator_[hostElementId] = jsThirdProviderOperator;
323 }
324
DeregisterJsThirdProviderInteractionOperation(int64_t hostElementId)325 void AccessibilityHoverManagerForThirdNG::DeregisterJsThirdProviderInteractionOperation(
326 int64_t hostElementId)
327 {
328 jsThirdProviderOperator_.erase(hostElementId);
329 }
330
331 namespace {
332 enum class DumpMode {
333 TREE,
334 NODE,
335 HANDLE_EVENT,
336 HOVER_TEST
337 };
338
339 struct DumpInfoArgument {
340 bool useWindowId = false;
341 DumpMode mode = DumpMode::TREE;
342 bool isDumpSimplify = false;
343 bool verbose = false;
344 int64_t rootId = -1;
345 int32_t pointX = 0;
346 int32_t pointY = 0;
347 int64_t nodeId = -1;
348 int32_t action = 0;
349 };
350
GetDumpInfoArgument(const std::vector<std::string> & params,DumpInfoArgument & argument)351 bool GetDumpInfoArgument(const std::vector<std::string>& params, DumpInfoArgument& argument)
352 {
353 argument.isDumpSimplify = params[0].compare("-simplify") == 0;
354 for (auto arg = params.begin() + 1; arg != params.end(); ++arg) {
355 if (*arg == "-w") {
356 argument.useWindowId = true;
357 } else if (*arg == "--root") {
358 ++arg;
359 if (arg == params.end()) {
360 DumpLog::GetInstance().Print(std::string("Error: --root is used to set the root node, ") +
361 "e.g. '--root ${AccessibilityId}'!");
362 return false;
363 }
364 argument.rootId = StringUtils::StringToLongInt(*arg);
365 } else if (*arg == "--hover-test") {
366 argument.mode = DumpMode::HOVER_TEST;
367 static constexpr int32_t NUM_POINT_DIMENSION = 2;
368 if (std::distance(arg, params.end()) <= NUM_POINT_DIMENSION) {
369 DumpLog::GetInstance().Print(std::string("Error: --hover-test is used to get nodes at a point ") +
370 "relative to the root node, e.g. '--hover-test ${x} ${y}'!");
371 return false;
372 }
373 ++arg;
374 argument.pointX = StringUtils::StringToInt(*arg);
375 ++arg;
376 argument.pointY = StringUtils::StringToInt(*arg);
377 } else if (*arg == "-v") {
378 argument.verbose = true;
379 } else if (*arg == "-json") {
380 argument.mode = DumpMode::TREE;
381 } else {
382 if (argument.mode == DumpMode::NODE) {
383 argument.mode = DumpMode::HANDLE_EVENT;
384 argument.action = StringUtils::StringToInt(*arg);
385 break;
386 } else {
387 argument.mode = DumpMode::NODE;
388 argument.nodeId = StringUtils::StringToLongInt(*arg);
389 }
390 }
391 }
392 return true;
393 }
394 } // namespace
395
DumpPropertyForThird(int64_t elementId,const WeakPtr<JsAccessibilityManager> & jsAccessibilityManager,const std::shared_ptr<JsThirdProviderInteractionOperation> & jsThirdProviderOperator)396 void AccessibilityHoverManagerForThirdNG::DumpPropertyForThird(
397 int64_t elementId,
398 const WeakPtr<JsAccessibilityManager>& jsAccessibilityManager,
399 const std::shared_ptr<JsThirdProviderInteractionOperation>& jsThirdProviderOperator)
400 {
401 auto jsAccessibilityManagerTemp = jsAccessibilityManager.Upgrade();
402 CHECK_NULL_VOID(jsAccessibilityManagerTemp);
403 int64_t splitElementId = AccessibilityElementInfo::UNDEFINED_ACCESSIBILITY_ID;
404 int32_t splitTreeId = AccessibilityElementInfo::UNDEFINED_TREE_ID;
405 AccessibilitySystemAbilityClient::GetTreeIdAndElementIdBySplitElementId(
406 elementId, splitElementId, splitTreeId);
407 std::list<Accessibility::AccessibilityElementInfo> infos;
408 bool ret = jsThirdProviderOperator->FindAccessibilityNodeInfosByIdFromProvider(
409 splitElementId, 0, 0, infos);
410 if ((!ret) || (infos.size() == 0)) {
411 return;
412 }
413
414 Accessibility::AccessibilityElementInfo info = infos.front();
415 jsAccessibilityManagerTemp->DumpCommonPropertyNG(info, splitTreeId);
416 jsAccessibilityManagerTemp->DumpAccessibilityPropertyNG(info);
417 DumpLog::GetInstance().Print(0, info.GetComponentType(), info.GetChildCount());
418 }
419
OnDumpChildInfoForThirdRecursive(int64_t hostElementId,const std::vector<std::string> & params,std::vector<std::string> & info,const WeakPtr<JsAccessibilityManager> & jsAccessibilityManager)420 bool AccessibilityHoverManagerForThirdNG::OnDumpChildInfoForThirdRecursive(
421 int64_t hostElementId,
422 const std::vector<std::string>& params,
423 std::vector<std::string>& info,
424 const WeakPtr<JsAccessibilityManager>& jsAccessibilityManager)
425 {
426 DumpInfoArgument argument;
427 if (GetDumpInfoArgument(params, argument) == false) {
428 return true;
429 }
430 auto jsThirdProviderOperator =
431 GetJsThirdProviderInteractionOperation(hostElementId).lock();
432 if (jsThirdProviderOperator == nullptr) {
433 return true;
434 }
435 switch (argument.mode) {
436 case DumpMode::NODE:
437 DumpPropertyForThird(argument.nodeId, jsAccessibilityManager, jsThirdProviderOperator);
438 break;
439 case DumpMode::TREE:
440 case DumpMode::HANDLE_EVENT:
441 case DumpMode::HOVER_TEST:
442 default:
443 DumpLog::GetInstance().Print("Error: invalid arguments!");
444 break;
445 }
446 return true;
447 }
448
449
450 } // namespace OHOS::Ace::Framework
451