1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "element_node_iterator_impl.h"
17 namespace OHOS::uitest {
18     using namespace OHOS::Accessibility;
19 
ElementNodeIteratorImpl( const std::vector<AccessibilityElementInfo> &elements)20     ElementNodeIteratorImpl::ElementNodeIteratorImpl(
21         const std::vector<AccessibilityElementInfo> &elements)
22     {
23         elementInfoLists_ = std::move(elements);
24         currentIndex_ = -1;
25         topIndex_ = 0;
26         lastCurrentIndex_ = -1;
27     }
28 
ElementNodeIteratorImpl()29     ElementNodeIteratorImpl::ElementNodeIteratorImpl()
30     {
31         currentIndex_ = -1;
32         topIndex_ = 0;
33         lastCurrentIndex_ = -1;
34     }
35 
~ElementNodeIteratorImpl()36     ElementNodeIteratorImpl::~ElementNodeIteratorImpl()
37     {
38         elementInfoLists_.clear();
39         elementToParentIndexMap_.clear();
40         elementIndexToHierarch_.clear();
41         visitAndVisibleIndexSet_.clear();
42     }
43 
DFSNextWithInTarget(Widget &widget)44     bool ElementNodeIteratorImpl::DFSNextWithInTarget(Widget &widget)
45     {
46         if (currentIndex_ == topIndex_) {
47             int count = elementInfoLists_[currentIndex_].GetChildCount();
48             if (count <= 0) {
49                 return false;
50             }
51             for (size_t i = currentIndex_ + 1; i < elementInfoLists_.size(); ++i) {
52                 if (elementInfoLists_[i].GetAccessibilityId() != elementInfoLists_[currentIndex_].GetChildId(0)) {
53                     continue;
54                 }
55                 elementToParentIndexMap_.emplace(i, currentIndex_);
56                 std::string parentHierarchy = elementIndexToHierarch_.at(currentIndex_);
57                 currentIndex_ = i;
58                 visitAndVisibleIndexSet_.insert(currentIndex_);
59                 WrapperElement(widget);
60                 widget.SetHierarchy(WidgetHierarchyBuilder::Build(parentHierarchy, 0));
61                 elementIndexToHierarch_.emplace(currentIndex_, widget.GetHierarchy());
62                 return true;
63             }
64         }
65         return VisitNodeByChildAndBrother(widget);
66     }
67 
DFSNext(Widget &widget, uint32_t windowId)68     bool ElementNodeIteratorImpl::DFSNext(Widget &widget, uint32_t windowId)
69     {
70         if (IsVisitFinish()) {
71             return false;
72         }
73 
74         if (currentIndex_ == -1) {
75             currentIndex_ = 0;
76             elementToParentIndexMap_.emplace(currentIndex_, -1);
77             visitAndVisibleIndexSet_.insert(currentIndex_);
78             WrapperElement(widget);
79             widget.SetHierarchy(ROOT_HIERARCHY + to_string(windowId));
80             elementIndexToHierarch_.emplace(currentIndex_, widget.GetHierarchy());
81             return true;
82         }
83         return VisitNodeByChildAndBrother(widget);
84     }
85 
IsVisitFinish() const86     bool ElementNodeIteratorImpl::IsVisitFinish() const
87     {
88         return elementInfoLists_.size() == elementToParentIndexMap_.size();
89     }
90 
RestoreNodeIndexByAnchor()91     void ElementNodeIteratorImpl::RestoreNodeIndexByAnchor()
92     {
93         topIndex_ = currentIndex_;
94         lastCurrentIndex_ = currentIndex_;
95         LOG_D("select in widget, store current index is %{public}d", currentIndex_);
96     }
97 
ResetNodeIndexToAnchor()98     void ElementNodeIteratorImpl::ResetNodeIndexToAnchor()
99     {
100         topIndex_ = 0;
101     }
102 
ClearDFSNext()103     void ElementNodeIteratorImpl::ClearDFSNext()
104     {
105         elementToParentIndexMap_.clear();
106         elementIndexToHierarch_.clear();
107         visitAndVisibleIndexSet_.clear();
108         currentIndex_ = -1;
109         topIndex_ = 0;
110     }
111 
RemoveInvisibleWidget()112     void ElementNodeIteratorImpl::RemoveInvisibleWidget()
113     {
114         visitAndVisibleIndexSet_.erase(currentIndex_);
115     }
116 
GetTextByKey(const std::string key)117     std::string ElementNodeIteratorImpl::GetTextByKey(const std::string key)
118     {
119         for (auto element : elementInfoLists_) {
120             if (element.GetInspectorKey() == key) {
121                 return element.GetContent();
122             }
123         }
124         return "";
125     }
126 
GenerateNodeHashCode(const AccessibilityElementInfo &element)127     std::string ElementNodeIteratorImpl::GenerateNodeHashCode(const AccessibilityElementInfo &element)
128     {
129         int32_t winId = element.GetWindowId();
130         int64_t accessId = element.GetAccessibilityId();
131         std::stringstream hashcodeStr;
132         hashcodeStr << winId;
133         hashcodeStr << ":";
134         hashcodeStr << accessId;
135         return hashcodeStr.str();
136     }
137 
WrapperElement(Widget &widget)138     void ElementNodeIteratorImpl::WrapperElement(Widget &widget)
139     {
140         AccessibilityElementInfo element = elementInfoLists_[currentIndex_];
141         WrapperNodeAttrToVec(widget, element);
142     }
143 
VisitNodeByChildAndBrother(Widget &widget)144     bool ElementNodeIteratorImpl::VisitNodeByChildAndBrother(Widget &widget)
145     {
146         int childCount = elementInfoLists_[currentIndex_].GetChildCount();
147         if (childCount > 0) {
148             if (VisitChildren(widget)) {
149                 return true;
150             }
151         }
152 
153         if (elementToParentIndexMap_.find(currentIndex_) == elementToParentIndexMap_.cend()) {
154             LOG_D("This node has no parent: %{public}s",
155                   std::to_string(elementInfoLists_[currentIndex_].GetAccessibilityId()).data());
156             return false;
157         }
158         int parentIndex = elementToParentIndexMap_.at(currentIndex_);
159         int tempChildIndex = currentIndex_;
160         if (elementToParentIndexMap_.find(topIndex_) == elementToParentIndexMap_.cend()) {
161             LOG_D("This topIndex_ has no parent: %{public}d", topIndex_);
162             return false;
163         }
164         while (parentIndex != elementToParentIndexMap_.at(topIndex_)) {
165             if (VisitBrother(widget, parentIndex, tempChildIndex)) {
166                 return true;
167             }
168             if (elementToParentIndexMap_.find(parentIndex) == elementToParentIndexMap_.cend()) {
169                 LOG_D("This node has no parent: %{public}s",
170                       std::to_string(elementInfoLists_[parentIndex].GetAccessibilityId()).data());
171                 return false;
172             }
173             tempChildIndex = parentIndex;
174             parentIndex = elementToParentIndexMap_.at(parentIndex);
175         }
176         return false;
177     }
178 
VisitChildren(Widget& widget)179     bool ElementNodeIteratorImpl::VisitChildren(Widget& widget)
180     {
181         if (visitAndVisibleIndexSet_.find(currentIndex_) == visitAndVisibleIndexSet_.cend()) {
182             LOG_D("node %{public}s is invisible not find its children",
183                   std::to_string(elementInfoLists_[currentIndex_].GetAccessibilityId()).data());
184             return false;
185         } else {
186             for (size_t i = currentIndex_ + 1; i < elementInfoLists_.size(); ++i) {
187                 if (elementInfoLists_[i].GetAccessibilityId() != elementInfoLists_[currentIndex_].GetChildId(0)) {
188                     continue;
189                 }
190                 elementToParentIndexMap_.emplace(i, currentIndex_);
191                 std::string parentHierarchy = elementIndexToHierarch_.at(currentIndex_);
192                 currentIndex_ = i;
193                 visitAndVisibleIndexSet_.insert(currentIndex_);
194                 WrapperElement(widget);
195                 widget.SetHierarchy(WidgetHierarchyBuilder::Build(parentHierarchy, 0));
196                 elementIndexToHierarch_.emplace(currentIndex_, widget.GetHierarchy());
197                 return true;
198             }
199             return false;
200         }
201     }
202 
VisitBrother(Widget &widget, int parentIndex, int tempChildIndex)203     bool ElementNodeIteratorImpl::VisitBrother(Widget &widget, int parentIndex, int tempChildIndex)
204     {
205         AccessibilityElementInfo &parentModel = elementInfoLists_[parentIndex];
206         int parentChildCount = parentModel.GetChildCount();
207         for (int i = 0; i < parentChildCount; ++i) {
208             if ((parentModel.GetChildId(i) == elementInfoLists_[tempChildIndex].GetAccessibilityId()) &&
209                 (i < parentChildCount - 1)) {
210                 if (parentModel.GetChildId(i + 1) == elementInfoLists_[tempChildIndex + 1].GetAccessibilityId()) {
211                     elementToParentIndexMap_.emplace(tempChildIndex + 1, parentIndex);
212                     std::string parentHierarchy = elementIndexToHierarch_.at(parentIndex);
213                     currentIndex_ = tempChildIndex + 1;
214                     visitAndVisibleIndexSet_.insert(currentIndex_);
215                     WrapperElement(widget);
216                     widget.SetHierarchy(WidgetHierarchyBuilder::Build(parentHierarchy, i + 1));
217                     elementIndexToHierarch_.emplace(currentIndex_, widget.GetHierarchy());
218                     return true;
219                 } else {
220                     LOG_E("Node error, except: %{public}s, actual is %{public}s",
221                           std::to_string(parentModel.GetChildId(i + 1)).data(),
222                           std::to_string(elementInfoLists_[tempChildIndex + 1].GetAccessibilityId()).data());
223                 }
224             }
225         }
226         return false;
227     }
228 
WrapperNodeAttrToVec(Widget &widget, const AccessibilityElementInfo &element)229     void ElementNodeIteratorImpl::WrapperNodeAttrToVec(Widget &widget, const AccessibilityElementInfo &element)
230     {
231         Accessibility::Rect nodeOriginRect = elementInfoLists_[currentIndex_].GetRectInScreen();
232         Rect visibleRect{nodeOriginRect.GetLeftTopXScreenPostion(), nodeOriginRect.GetRightBottomXScreenPostion(),
233                          nodeOriginRect.GetLeftTopYScreenPostion(), nodeOriginRect.GetRightBottomYScreenPostion()};
234         widget.SetBounds(visibleRect);
235         widget.SetAttr(UiAttr::ACCESSIBILITY_ID, std::to_string(element.GetAccessibilityId()));
236         widget.SetAttr(UiAttr::ID, element.GetInspectorKey());
237         widget.SetAttr(UiAttr::TEXT, element.GetContent());
238         widget.SetAttr(UiAttr::KEY, element.GetInspectorKey());
239         widget.SetAttr(UiAttr::TYPE, element.GetComponentType());
240         widget.SetAttr(UiAttr::DESCRIPTION, element.GetDescriptionInfo());
241         if (element.GetComponentType() == "rootdecortag" || element.GetInspectorKey() == "ContainerModalTitleRow") {
242             widget.SetAttr(UiAttr::TYPE, "DecorBar");
243         }
244         widget.SetAttr(UiAttr::ENABLED, element.IsEnabled() ? "true" : "false");
245         widget.SetAttr(UiAttr::FOCUSED, element.IsFocused() ? "true" : "false");
246         widget.SetAttr(UiAttr::SELECTED, element.IsSelected() ? "true" : "false");
247         widget.SetAttr(UiAttr::CLICKABLE, "false");
248         widget.SetAttr(UiAttr::LONG_CLICKABLE, "false");
249         widget.SetAttr(UiAttr::SCROLLABLE, element.IsScrollable() ? "true" : "false");
250         widget.SetAttr(UiAttr::CHECKABLE, element.IsCheckable() ? "true" : "false");
251         widget.SetAttr(UiAttr::CHECKED, element.IsChecked() ? "true" : "false");
252         widget.SetAttr(UiAttr::HOST_WINDOW_ID, std::to_string(element.GetWindowId()));
253         widget.SetAttr(UiAttr::ZINDEX, std::to_string(element.GetZIndex()));
254         widget.SetAttr(UiAttr::OPACITY, std::to_string(element.GetOpacity()));
255         widget.SetAttr(UiAttr::BACKGROUNDCOLOR, element.GetBackgroundColor());
256         widget.SetAttr(UiAttr::BACKGROUNDIMAGE, element.GetBackgroundImage());
257         widget.SetAttr(UiAttr::BLUR, element.GetBlur());
258         widget.SetAttr(UiAttr::HITTESTBEHAVIOR, element.GetHitTestBehavior());
259         widget.SetAttr(UiAttr::CLIP, element.GetClip() ? "true" : "false");
260         stringstream boundStream;
261         boundStream << "[" << visibleRect.left_ << "," << visibleRect.top_ << "][" << visibleRect.right_ << ","
262                     << visibleRect.bottom_ << "]";
263         widget.SetAttr(UiAttr::ORIGBOUNDS, boundStream.str());
264         widget.SetAttr(UiAttr::HASHCODE, ElementNodeIteratorImpl::GenerateNodeHashCode(element));
265         if (!element.IsVisible()) {
266             LOG_D("widget %{public}s is not visible", widget.GetAttr(UiAttr::ACCESSIBILITY_ID).data());
267         }
268         widget.SetAttr(UiAttr::VISIBLE, element.IsVisible() ? "true" : "false");
269         const auto app = element.GetBundleName();
270         widget.SetAttr(UiAttr::BUNDLENAME, app);
271         widget.SetAttr(UiAttr::HINT, element.GetHint());
272         WrapperNodeActionAttrToVec(widget, element);
273     }
274 
WrapperNodeActionAttrToVec(Widget &widget, const AccessibilityElementInfo &element)275     void ElementNodeIteratorImpl::WrapperNodeActionAttrToVec(Widget &widget, const AccessibilityElementInfo &element)
276     {
277         auto actions = element.GetActionList();
278         for (auto &action : actions) {
279             switch (action.GetActionType()) {
280                 case ACCESSIBILITY_ACTION_CLICK:
281                     widget.SetAttr(UiAttr::CLICKABLE, "true");
282                     break;
283                 case ACCESSIBILITY_ACTION_LONG_CLICK:
284                     widget.SetAttr(UiAttr::LONG_CLICKABLE, "true");
285                     break;
286                 default:
287                     break;
288             }
289         }
290     }
291 } // namespace OHOS::uitest