• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 
16 #include "core/accessibility/accessibility_manager_ng.h"
17 
18 #include "core/accessibility/accessibility_constants.h"
19 #include "core/accessibility/accessibility_session_adapter.h"
20 #include "core/components_ng/pattern/pattern.h"
21 #include "core/pipeline_ng/pipeline_context.h"
22 
23 namespace OHOS::Ace::NG {
24 namespace {
GetOffsetToAncestorRevertTransform(const RefPtr<NG::FrameNode> & ancestor,const RefPtr<NG::FrameNode> & endNode,const PointF & pointAncestor,PointF & pointNode)25 void GetOffsetToAncestorRevertTransform(const RefPtr<NG::FrameNode>& ancestor, const RefPtr<NG::FrameNode>& endNode,
26     const PointF& pointAncestor, PointF& pointNode)
27 {
28     CHECK_NULL_VOID(ancestor);
29     CHECK_NULL_VOID(endNode);
30     auto context = endNode->GetRenderContext();
31     CHECK_NULL_VOID(context);
32     auto rect = context->GetPaintRectWithoutTransform();
33     OffsetF offset = rect.GetOffset();
34     VectorF finalScale {1.0f, 1.0f};
35     auto scale = endNode->GetTransformScale();
36     finalScale.x = scale.x;
37     finalScale.y = scale.y;
38 
39     PointF ancestorLeftTopPoint(offset.GetX(), offset.GetY());
40     context->GetPointTransformRotate(ancestorLeftTopPoint);
41     auto parent = endNode->GetAncestorNodeOfFrame(true);
42     while (parent) {
43         auto parentRenderContext = parent->GetRenderContext();
44         if (parentRenderContext) {
45             offset = parentRenderContext->GetPaintRectWithoutTransform().GetOffset();
46             PointF pointTmp(offset.GetX() + ancestorLeftTopPoint.GetX(), offset.GetY() + ancestorLeftTopPoint.GetY());
47             parentRenderContext->GetPointTransformRotate(pointTmp);
48             ancestorLeftTopPoint.SetX(pointTmp.GetX());
49             ancestorLeftTopPoint.SetY(pointTmp.GetY());
50             auto scale = parent->GetTransformScale();
51             finalScale.x *= scale.x;
52             finalScale.y *= scale.y;
53         }
54 
55         if (ancestor && (parent == ancestor)) {
56             break;
57         }
58 
59         parent = parent->GetAncestorNodeOfFrame(true);
60     }
61 
62     if ((NearEqual(finalScale.x, 1.0f) && NearEqual(finalScale.y, 1.0f)) ||
63         NearZero(finalScale.x) || NearZero(finalScale.y)) {
64         pointNode.SetX(pointAncestor.GetX() - ancestorLeftTopPoint.GetX());
65         pointNode.SetY(pointAncestor.GetY() - ancestorLeftTopPoint.GetY());
66     } else {
67         pointNode.SetX((pointAncestor.GetX() - ancestorLeftTopPoint.GetX()) / finalScale.x);
68         pointNode.SetY((pointAncestor.GetY() - ancestorLeftTopPoint.GetY()) / finalScale.y);
69     }
70     TAG_LOGD(AceLogTag::ACE_ACCESSIBILITY,
71         "GetOffsetToAncestorRevertTransform: offsetX %{public}f offsetY %{public}f scaleX %{public}f scaleY %{public}f",
72         pointNode.GetX(), pointNode.GetY(), finalScale.x, finalScale.y);
73 }
74 
CheckAndSendHoverEnterByAncestor(const RefPtr<NG::FrameNode> & ancestor)75 void CheckAndSendHoverEnterByAncestor(const RefPtr<NG::FrameNode>& ancestor)
76 {
77     CHECK_NULL_VOID(ancestor);
78     auto pipeline = ancestor->GetContext();
79     CHECK_NULL_VOID(pipeline);
80     // Inter Process is showed as a component with rect like form process,
81     // need send hover enter when no component hovered to focus outside
82     if (pipeline->IsFormRender() || pipeline->IsJsCard() || pipeline->IsJsPlugin()) {
83         TAG_LOGD(AceLogTag::ACE_ACCESSIBILITY, "SendHoverEnterByAncestor");
84         ancestor->OnAccessibilityEvent(AccessibilityEventType::HOVER_ENTER_EVENT);
85     }
86 }
87 
IsTouchExplorationEnabled(const RefPtr<FrameNode> & root)88 bool IsTouchExplorationEnabled(const RefPtr<FrameNode>& root)
89 {
90     CHECK_NULL_RETURN(root, true);
91     auto pipeline = root->GetContext();
92     CHECK_NULL_RETURN(pipeline, true);
93     auto jsAccessibilityManager = pipeline->GetAccessibilityManager();
94     CHECK_NULL_RETURN(jsAccessibilityManager, true);
95     auto accessibilityWorkMode = jsAccessibilityManager->GenerateAccessibilityWorkMode();
96     return accessibilityWorkMode.isTouchExplorationEnabled;
97 }
98 }
99 
HandleAccessibilityHoverEvent(const RefPtr<FrameNode> & root,const MouseEvent & event)100 void AccessibilityManagerNG::HandleAccessibilityHoverEvent(const RefPtr<FrameNode>& root, const MouseEvent& event)
101 {
102     if (root == nullptr ||
103         !AceApplicationInfo::GetInstance().IsAccessibilityEnabled() ||
104         !IsTouchExplorationEnabled(root) ||
105         event.sourceType != SourceType::MOUSE) {
106         return;
107     }
108     AccessibilityHoverEventType type = AccessibilityHoverEventType::MOVE;
109     switch (event.action) {
110         case MouseAction::WINDOW_ENTER:
111             type = AccessibilityHoverEventType::ENTER;
112             break;
113         case MouseAction::MOVE:
114             type = AccessibilityHoverEventType::MOVE;
115             break;
116         case MouseAction::WINDOW_LEAVE:
117             type = AccessibilityHoverEventType::EXIT;
118             break;
119         default:
120             return;
121     }
122     PointF point(event.x, event.y);
123     HandleAccessibilityHoverEventInner(root, point, SourceType::MOUSE, type, event.time);
124 }
125 
HandleAccessibilityHoverEvent(const RefPtr<FrameNode> & root,const TouchEvent & event)126 void AccessibilityManagerNG::HandleAccessibilityHoverEvent(const RefPtr<FrameNode>& root, const TouchEvent& event)
127 {
128     if (root == nullptr ||
129         !AceApplicationInfo::GetInstance().IsAccessibilityEnabled() ||
130         !IsTouchExplorationEnabled(root) ||
131         event.sourceType == SourceType::MOUSE) {
132         return;
133     }
134     AccessibilityHoverEventType type = AccessibilityHoverEventType::MOVE;
135     switch (event.type) {
136         case TouchType::HOVER_ENTER:
137             type = AccessibilityHoverEventType::ENTER;
138             break;
139         case TouchType::HOVER_MOVE:
140             type = AccessibilityHoverEventType::MOVE;
141             break;
142         case TouchType::HOVER_EXIT:
143             type = AccessibilityHoverEventType::EXIT;
144             break;
145         default:
146             return;
147     }
148     PointF point(event.x, event.y);
149     if (event.pointers.size() > 1 && event.sourceType == SourceType::TOUCH) {
150         if (hoverState_.source == SourceType::TOUCH) {
151             ResetHoverState();
152             return;
153         }
154     }
155     HandleAccessibilityHoverEventInner(root, point, event.sourceType, type, event.time);
156 }
157 
HandleAccessibilityHoverEvent(const RefPtr<FrameNode> & root,float pointX,float pointY,int32_t sourceType,int32_t eventType,int64_t timeMs)158 void AccessibilityManagerNG::HandleAccessibilityHoverEvent(const RefPtr<FrameNode>& root, float pointX, float pointY,
159     int32_t sourceType, int32_t eventType, int64_t timeMs)
160 {
161     if (root == nullptr ||
162         !AceApplicationInfo::GetInstance().IsAccessibilityEnabled() ||
163         !IsTouchExplorationEnabled(root) ||
164         eventType < 0 || eventType >= static_cast<int32_t>(AccessibilityHoverEventType::Count)) {
165         return;
166     }
167     PointF point(pointX, pointY);
168     TimeStamp time((std::chrono::milliseconds(timeMs)));
169 
170     if (IsHandlePipelineAccessibilityHoverEnter(root)) {
171         TouchEvent event;
172         event.x = pointX;
173         event.y = pointY;
174         event.sourceType = static_cast<SourceType>(sourceType);
175         event.time = time;
176         HandlePipelineAccessibilityHoverEnter(root, event, eventType);
177     } else {
178         HandleAccessibilityHoverEventInner(root, point, static_cast<SourceType>(sourceType),
179             static_cast<AccessibilityHoverEventType>(eventType), time);
180     }
181 }
182 
HandleAccessibilityHoverEventInner(const RefPtr<FrameNode> & root,const PointF & point,SourceType sourceType,AccessibilityHoverEventType eventType,TimeStamp time)183 void AccessibilityManagerNG::HandleAccessibilityHoverEventInner(
184     const RefPtr<FrameNode>& root,
185     const PointF& point,
186     SourceType sourceType,
187     AccessibilityHoverEventType eventType,
188     TimeStamp time)
189 {
190     static constexpr size_t THROTTLE_INTERVAL_HOVER_EVENT = 100;
191     uint64_t duration =
192         static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(time - hoverState_.time).count());
193     if (!hoverState_.idle) {
194         if ((!IsEventTypeChangeDirectHandleHover(eventType)) && (duration < THROTTLE_INTERVAL_HOVER_EVENT)) {
195             return;
196         }
197     }
198 
199     static constexpr size_t MIN_SOURCE_CHANGE_GAP_MS = 1000;
200     if (sourceType != hoverState_.source && !hoverState_.idle) {
201         if (duration < MIN_SOURCE_CHANGE_GAP_MS) {
202             return;
203         }
204         ResetHoverState();
205     }
206 
207     ACE_SCOPED_TRACE("HandleAccessibilityHoverEventInner");
208     if (eventType == AccessibilityHoverEventType::ENTER) {
209         ResetHoverState();
210     }
211     std::vector<WeakPtr<FrameNode>> currentNodesHovering;
212     std::vector<RefPtr<FrameNode>> lastNodesHovering;
213     std::vector<int32_t> lastNodesHoveringId;
214     for (const auto& nodeWeak: hoverState_.nodesHovering) {
215         auto node = nodeWeak.Upgrade();
216         if (node != nullptr) {
217             lastNodesHovering.push_back(node);
218             lastNodesHoveringId.push_back(node->GetId());
219         }
220     }
221     if (eventType != AccessibilityHoverEventType::EXIT) {
222         std::unique_ptr<AccessibilityProperty::HoverTestDebugTraceInfo> debugInfo = nullptr;
223         AccessibilityHoverTestPath path = AccessibilityProperty::HoverTest(point, root, debugInfo);
224         for (const auto& node: path) {
225             auto id = node->GetId();
226             if (std::find(lastNodesHoveringId.begin(), lastNodesHoveringId.end(), id) != lastNodesHoveringId.end() ||
227                 AccessibilityProperty::IsAccessibilityFocusable(node)) {
228                 currentNodesHovering.push_back(node);
229             }
230         }
231     }
232     auto sendHoverEnter = false;
233     static constexpr int32_t INVALID_NODE_ID = -1;
234     int32_t lastHoveringId = INVALID_NODE_ID;
235     RefPtr<FrameNode> lastHovering = nullptr;
236     if (!lastNodesHovering.empty()) {
237         lastHovering = lastNodesHovering.back();
238         lastHoveringId = lastHovering->GetId();
239     }
240     int32_t currentHoveringId = INVALID_NODE_ID;
241     RefPtr<FrameNode> currentHovering = nullptr;
242     if (!currentNodesHovering.empty()) {
243         currentHovering = currentNodesHovering.back().Upgrade();
244         currentHoveringId = currentHovering->GetId();
245     }
246     if (!DeliverAccessibilityHoverEvent(currentHovering, point)) {
247         if (lastHoveringId != INVALID_NODE_ID && lastHoveringId != currentHoveringId) {
248             lastHovering->OnAccessibilityEvent(AccessibilityEventType::HOVER_EXIT_EVENT);
249             NotifyHoverEventToNodeSession(lastHovering, root, point,
250                 sourceType, AccessibilityHoverEventType::EXIT, time);
251         }
252         if (currentHoveringId != INVALID_NODE_ID) {
253             if (currentHoveringId != lastHoveringId && (!IgnoreCurrentHoveringNode(currentHovering))) {
254                 currentHovering->OnAccessibilityEvent(AccessibilityEventType::HOVER_ENTER_EVENT);
255                 sendHoverEnter = true;
256             }
257             NotifyHoverEventToNodeSession(currentHovering, root, point,
258                 sourceType, eventType, time);
259         }
260 
261         if (!sendHoverEnter && (eventType == AccessibilityHoverEventType::ENTER)) {
262             // check need send hover enter when no component hovered to focus outside
263             CheckAndSendHoverEnterByAncestor(root);
264         }
265     }
266 
267     hoverState_.nodesHovering = std::move(currentNodesHovering);
268     hoverState_.time = time;
269     hoverState_.source = sourceType;
270     hoverState_.idle = eventType == AccessibilityHoverEventType::EXIT;
271     hoverState_.eventType = eventType;
272 }
273 
DeliverAccessibilityHoverEvent(const RefPtr<FrameNode> & hoverNode,const PointF & point)274 bool AccessibilityManagerNG::DeliverAccessibilityHoverEvent(const RefPtr<FrameNode>& hoverNode, const PointF& point)
275 {
276     CHECK_NULL_RETURN(hoverNode, false);
277     auto hoverNodePattern = hoverNode->GetPattern();
278     CHECK_NULL_RETURN(hoverNodePattern, false);
279     return hoverNodePattern->OnAccessibilityHoverEvent(point);
280 }
281 
IgnoreCurrentHoveringNode(const RefPtr<FrameNode> & node)282 bool AccessibilityManagerNG::IgnoreCurrentHoveringNode(const RefPtr<FrameNode> &node)
283 {
284     auto sessionAdapter = AccessibilitySessionAdapter::GetSessionAdapter(node);
285     CHECK_NULL_RETURN(sessionAdapter, false);
286     return sessionAdapter->IgnoreHostNode();
287 }
288 
NotifyHoverEventToNodeSession(const RefPtr<FrameNode> & node,const RefPtr<FrameNode> & rootNode,const PointF & pointRoot,SourceType sourceType,AccessibilityHoverEventType eventType,TimeStamp time)289 void AccessibilityManagerNG::NotifyHoverEventToNodeSession(const RefPtr<FrameNode>& node,
290     const RefPtr<FrameNode>& rootNode, const PointF& pointRoot,
291     SourceType sourceType, AccessibilityHoverEventType eventType, TimeStamp time)
292 {
293     auto eventHub = node->GetEventHub<EventHub>();
294     if (!eventHub->IsEnabled()) {
295         // If the host component is disabled, do not transfer hover event.
296         return;
297     }
298     auto sessionAdapter = AccessibilitySessionAdapter::GetSessionAdapter(node);
299     CHECK_NULL_VOID(sessionAdapter);
300     PointF pointNode(pointRoot);
301     if (AccessibilityManagerNG::ConvertPointFromAncestorToNode(rootNode, node, pointRoot, pointNode)) {
302         sessionAdapter->TransferHoverEvent(pointNode, sourceType, eventType, time);
303     }
304 }
305 
ResetHoverState()306 void AccessibilityManagerNG::ResetHoverState()
307 {
308     hoverState_.idle = true;
309     hoverState_.nodesHovering.clear();
310 }
311 
HoverTestDebug(const RefPtr<FrameNode> & root,const PointF & point,std::string & summary,std::string & detail) const312 void AccessibilityManagerNG::HoverTestDebug(const RefPtr<FrameNode>& root, const PointF& point,
313     std::string& summary, std::string& detail) const
314 {
315     auto summaryJson = JsonUtil::Create();
316     auto detailJson = JsonUtil::Create();
317     std::stringstream summaryNodesSearched;
318     auto debugInfo = std::make_unique<AccessibilityProperty::HoverTestDebugTraceInfo>();
319     AccessibilityHoverTestPath path = AccessibilityProperty::HoverTest(point, root, debugInfo);
320     auto summaryPath = JsonUtil::CreateArray();
321     auto summarySelected = JsonUtil::CreateArray();
322 
323     auto detaiSelectionInfo = JsonUtil::CreateArray();
324     size_t numNodesSelected = 0;
325     for (size_t i = 0; i < path.size(); ++i) {
326         summaryPath->Put(std::to_string(i).c_str(), path[i]->GetAccessibilityId());
327         auto detailNodeSelection = JsonUtil::Create();
328         if (AccessibilityProperty::IsAccessibilityFocusableDebug(path[i], detailNodeSelection)) {
329             summarySelected->Put(std::to_string(numNodesSelected).c_str(), path[i]->GetAccessibilityId());
330             ++numNodesSelected;
331         }
332         detaiSelectionInfo->PutRef(std::move(detailNodeSelection));
333     }
334     summaryJson->PutRef("path", std::move(summaryPath));
335     summaryJson->PutRef("nodesSelected", std::move(summarySelected));
336 
337     auto detailSearchInfo = JsonUtil::CreateArray();
338     for (size_t i = 0; i < debugInfo->trace.size(); ++i) {
339         auto detailNodeSearch = std::move(debugInfo->trace[i]);
340         detailSearchInfo->Put(std::to_string(i).c_str(), detailNodeSearch);
341     }
342     detailJson->PutRef("detailSearch", std::move(detailSearchInfo));
343     detailJson->PutRef("detailSelection", std::move(detaiSelectionInfo));
344     summary = summaryJson->ToString();
345     detail = detailJson->ToString();
346 }
347 
ConvertPointFromAncestorToNode(const RefPtr<NG::FrameNode> & ancestor,const RefPtr<NG::FrameNode> & endNode,const PointF & pointAncestor,PointF & pointNode)348 bool AccessibilityManagerNG::ConvertPointFromAncestorToNode(
349     const RefPtr<NG::FrameNode>& ancestor, const RefPtr<NG::FrameNode>& endNode,
350     const PointF& pointAncestor, PointF& pointNode)
351 {
352     CHECK_NULL_RETURN(ancestor, false);
353     CHECK_NULL_RETURN(endNode, false);
354     // revert scale from endNode to ancestor
355     GetOffsetToAncestorRevertTransform(ancestor, endNode, pointAncestor, pointNode);
356     return true;
357 }
358 
IsEventTypeChangeDirectHandleHover(AccessibilityHoverEventType eventType) const359 bool AccessibilityManagerNG::IsEventTypeChangeDirectHandleHover(AccessibilityHoverEventType eventType) const
360 {
361     if ((hoverState_.eventType == AccessibilityHoverEventType::MOVE)
362         && (eventType == AccessibilityHoverEventType::EXIT)) {
363         return true;
364     }
365     return false;
366 }
367 
IsHandlePipelineAccessibilityHoverEnter(const RefPtr<NG::FrameNode> & root) const368 bool AccessibilityManagerNG::IsHandlePipelineAccessibilityHoverEnter(const RefPtr<NG::FrameNode>& root) const
369 {
370     auto pipeline = root->GetContext();
371     CHECK_NULL_RETURN(pipeline, false);
372     auto ngPipeline = AceType::DynamicCast<NG::PipelineContext>(pipeline);
373     CHECK_NULL_RETURN(ngPipeline, false);
374 
375     auto container = Container::GetContainer(ngPipeline->GetInstanceId());
376     if (container && (container->IsUIExtensionWindow())) {
377         return true;
378     }
379     return false;
380 }
381 
HandlePipelineAccessibilityHoverEnter(const RefPtr<NG::FrameNode> & root,TouchEvent & event,int32_t eventType)382 void AccessibilityManagerNG::HandlePipelineAccessibilityHoverEnter(
383     const RefPtr<NG::FrameNode>& root,
384     TouchEvent& event,
385     int32_t eventType)
386 {
387     CHECK_NULL_VOID(root);
388     AccessibilityHoverEventType eventHoverType = static_cast<AccessibilityHoverEventType>(eventType);
389     event.type = TouchType::HOVER_MOVE;
390     switch (eventHoverType) {
391         case AccessibilityHoverEventType::ENTER:
392             event.type = TouchType::HOVER_ENTER;
393             break;
394         case AccessibilityHoverEventType::MOVE:
395             event.type = TouchType::HOVER_MOVE;
396             break;
397         case AccessibilityHoverEventType::EXIT:
398             event.type = TouchType::HOVER_EXIT;
399             break;
400         default:
401             break;
402     }
403 
404     auto pipeline = root->GetContext();
405     CHECK_NULL_VOID(pipeline);
406     pipeline->OnAccessibilityHoverEvent(event, root);
407 }
408 } // namespace OHOS::Ace::NG
409