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 GetParentContainerBounds(Rect & dockerRect)112 void ElementNodeIteratorImpl::GetParentContainerBounds(Rect &dockerRect) 113 { 114 int tempParentIndex = currentIndex_; 115 while (elementToParentIndexMap_.find(tempParentIndex) != elementToParentIndexMap_.cend()) { 116 tempParentIndex = elementToParentIndexMap_.at(tempParentIndex); 117 if (elementIndexToRectMap_.find(tempParentIndex) != elementIndexToRectMap_.cend()) { 118 dockerRect = elementIndexToRectMap_.at(tempParentIndex); 119 return; 120 } 121 } 122 } 123 CheckAndUpdateContainerRectMap()124 void ElementNodeIteratorImpl::CheckAndUpdateContainerRectMap() 125 { 126 if (CONTAINER_TYPE.find(elementInfoLists_[currentIndex_].GetComponentType()) != CONTAINER_TYPE.cend()) { 127 Accessibility::Rect nodeOriginRect = elementInfoLists_[currentIndex_].GetRectInScreen(); 128 Rect visibleRect{nodeOriginRect.GetLeftTopXScreenPostion(), nodeOriginRect.GetRightBottomXScreenPostion(), 129 nodeOriginRect.GetLeftTopYScreenPostion(), nodeOriginRect.GetRightBottomYScreenPostion()}; 130 elementIndexToRectMap_.emplace(currentIndex_, visibleRect); 131 } 132 } 133 RemoveInvisibleWidget()134 void ElementNodeIteratorImpl::RemoveInvisibleWidget() 135 { 136 visitAndVisibleIndexSet_.erase(currentIndex_); 137 } 138 GenerateNodeHashCode(const AccessibilityElementInfo & element)139 std::string ElementNodeIteratorImpl::GenerateNodeHashCode(const AccessibilityElementInfo &element) 140 { 141 int32_t winId = element.GetWindowId(); 142 int64_t accessId = element.GetAccessibilityId(); 143 std::stringstream hashcodeStr; 144 hashcodeStr << winId; 145 hashcodeStr << ":"; 146 hashcodeStr << accessId; 147 return hashcodeStr.str(); 148 } 149 WrapperElement(Widget & widget)150 void ElementNodeIteratorImpl::WrapperElement(Widget &widget) 151 { 152 AccessibilityElementInfo element = elementInfoLists_[currentIndex_]; 153 WrapperNodeAttrToVec(widget, element); 154 } 155 VisitNodeByChildAndBrother(Widget & widget)156 bool ElementNodeIteratorImpl::VisitNodeByChildAndBrother(Widget &widget) 157 { 158 int childCount = elementInfoLists_[currentIndex_].GetChildCount(); 159 if (childCount > 0) { 160 if (VisitChildren(widget)) { 161 return true; 162 } 163 } 164 165 if (elementToParentIndexMap_.find(currentIndex_) == elementToParentIndexMap_.cend()) { 166 LOG_D("This node has no parent: %{public}s", 167 std::to_string(elementInfoLists_[currentIndex_].GetAccessibilityId()).data()); 168 return false; 169 } 170 int parentIndex = elementToParentIndexMap_.at(currentIndex_); 171 int tempChildIndex = currentIndex_; 172 if (elementToParentIndexMap_.find(topIndex_) == elementToParentIndexMap_.cend()) { 173 LOG_D("This topIndex_ has no parent: %{public}d", topIndex_); 174 return false; 175 } 176 while (parentIndex != elementToParentIndexMap_.at(topIndex_)) { 177 if (VisitBrother(widget, parentIndex, tempChildIndex)) { 178 return true; 179 } 180 if (elementToParentIndexMap_.find(parentIndex) == elementToParentIndexMap_.cend()) { 181 LOG_D("This node has no parent: %{public}s", 182 std::to_string(elementInfoLists_[parentIndex].GetAccessibilityId()).data()); 183 return false; 184 } 185 tempChildIndex = parentIndex; 186 parentIndex = elementToParentIndexMap_.at(parentIndex); 187 } 188 return false; 189 } 190 VisitChildren(Widget & widget)191 bool ElementNodeIteratorImpl::VisitChildren(Widget& widget) 192 { 193 if (visitAndVisibleIndexSet_.find(currentIndex_) == visitAndVisibleIndexSet_.cend()) { 194 LOG_D("node %{public}s is invisible not find its children", 195 std::to_string(elementInfoLists_[currentIndex_].GetAccessibilityId()).data()); 196 return false; 197 } else { 198 for (size_t i = currentIndex_ + 1; i < elementInfoLists_.size(); ++i) { 199 if (elementInfoLists_[i].GetAccessibilityId() != elementInfoLists_[currentIndex_].GetChildId(0)) { 200 continue; 201 } 202 elementToParentIndexMap_.emplace(i, currentIndex_); 203 std::string parentHierarchy = elementIndexToHierarch_.at(currentIndex_); 204 currentIndex_ = i; 205 visitAndVisibleIndexSet_.insert(currentIndex_); 206 WrapperElement(widget); 207 widget.SetHierarchy(WidgetHierarchyBuilder::Build(parentHierarchy, 0)); 208 elementIndexToHierarch_.emplace(currentIndex_, widget.GetHierarchy()); 209 return true; 210 } 211 return false; 212 } 213 } 214 VisitBrother(Widget & widget,int parentIndex,int tempChildIndex)215 bool ElementNodeIteratorImpl::VisitBrother(Widget &widget, int parentIndex, int tempChildIndex) 216 { 217 AccessibilityElementInfo &parentModel = elementInfoLists_[parentIndex]; 218 int parentChildCount = parentModel.GetChildCount(); 219 for (int i = 0; i < parentChildCount; ++i) { 220 if ((parentModel.GetChildId(i) == elementInfoLists_[tempChildIndex].GetAccessibilityId()) && 221 (i < parentChildCount - 1)) { 222 if (parentModel.GetChildId(i + 1) == elementInfoLists_[tempChildIndex + 1].GetAccessibilityId()) { 223 elementToParentIndexMap_.emplace(tempChildIndex + 1, parentIndex); 224 std::string parentHierarchy = elementIndexToHierarch_.at(parentIndex); 225 currentIndex_ = tempChildIndex + 1; 226 visitAndVisibleIndexSet_.insert(currentIndex_); 227 WrapperElement(widget); 228 widget.SetHierarchy(WidgetHierarchyBuilder::Build(parentHierarchy, i + 1)); 229 elementIndexToHierarch_.emplace(currentIndex_, widget.GetHierarchy()); 230 return true; 231 } else { 232 LOG_E("Node error, except: %{public}s, actual is %{public}s", 233 std::to_string(parentModel.GetChildId(i + 1)).data(), 234 std::to_string(elementInfoLists_[tempChildIndex + 1].GetAccessibilityId()).data()); 235 } 236 } 237 } 238 return false; 239 } 240 WrapperNodeAttrToVec(Widget & widget,const AccessibilityElementInfo & element)241 void ElementNodeIteratorImpl::WrapperNodeAttrToVec(Widget &widget, const AccessibilityElementInfo &element) 242 { 243 Accessibility::Rect nodeOriginRect = elementInfoLists_[currentIndex_].GetRectInScreen(); 244 Rect visibleRect{nodeOriginRect.GetLeftTopXScreenPostion(), nodeOriginRect.GetRightBottomXScreenPostion(), 245 nodeOriginRect.GetLeftTopYScreenPostion(), nodeOriginRect.GetRightBottomYScreenPostion()}; 246 widget.SetBounds(visibleRect); 247 widget.SetAttr(UiAttr::ACCESSIBILITY_ID, std::to_string(element.GetAccessibilityId())); 248 widget.SetAttr(UiAttr::ID, element.GetInspectorKey()); 249 widget.SetAttr(UiAttr::TEXT, element.GetContent()); 250 widget.SetAttr(UiAttr::KEY, element.GetInspectorKey()); 251 widget.SetAttr(UiAttr::TYPE, element.GetComponentType()); 252 widget.SetAttr(UiAttr::DESCRIPTION, element.GetDescriptionInfo()); 253 if (element.GetComponentType() == "rootdecortag" || element.GetInspectorKey() == "ContainerModalTitleRow") { 254 widget.SetAttr(UiAttr::TYPE, "DecorBar"); 255 } 256 widget.SetAttr(UiAttr::ENABLED, element.IsEnabled() ? "true" : "false"); 257 widget.SetAttr(UiAttr::FOCUSED, element.IsFocused() ? "true" : "false"); 258 widget.SetAttr(UiAttr::SELECTED, element.IsSelected() ? "true" : "false"); 259 widget.SetAttr(UiAttr::CLICKABLE, "false"); 260 widget.SetAttr(UiAttr::LONG_CLICKABLE, "false"); 261 widget.SetAttr(UiAttr::SCROLLABLE, "false"); 262 widget.SetAttr(UiAttr::CHECKABLE, element.IsCheckable() ? "true" : "false"); 263 widget.SetAttr(UiAttr::CHECKED, element.IsChecked() ? "true" : "false"); 264 widget.SetAttr(UiAttr::HOST_WINDOW_ID, std::to_string(element.GetWindowId())); 265 widget.SetAttr(UiAttr::ZINDEX, std::to_string(element.GetZIndex())); 266 widget.SetAttr(UiAttr::OPACITY, std::to_string(element.GetOpacity())); 267 widget.SetAttr(UiAttr::BACKGROUNDCOLOR, element.GetBackgroundColor()); 268 widget.SetAttr(UiAttr::BACKGROUNDIMAGE, element.GetBackgroundImage()); 269 widget.SetAttr(UiAttr::BLUR, element.GetBlur()); 270 widget.SetAttr(UiAttr::HITTESTBEHAVIOR, element.GetHitTestBehavior()); 271 stringstream boundStream; 272 boundStream << "[" << visibleRect.left_ << "," << visibleRect.top_ << "][" << visibleRect.right_ << "," 273 << visibleRect.bottom_ << "]"; 274 widget.SetAttr(UiAttr::ORIGBOUNDS, boundStream.str()); 275 widget.SetAttr(UiAttr::HASHCODE, ElementNodeIteratorImpl::GenerateNodeHashCode(element)); 276 if (!element.IsVisible()) { 277 LOG_D("widget %{public}s is not visible", widget.GetAttr(UiAttr::ACCESSIBILITY_ID).data()); 278 } 279 widget.SetAttr(UiAttr::VISIBLE, element.IsVisible() ? "true" : "false"); 280 const auto app = element.GetBundleName(); 281 widget.SetAttr(UiAttr::BUNDLENAME, app); 282 widget.SetAttr(UiAttr::HINT, element.GetHint()); 283 WrapperNodeActionAttrToVec(widget, element); 284 } 285 WrapperNodeActionAttrToVec(Widget & widget,const AccessibilityElementInfo & element)286 void ElementNodeIteratorImpl::WrapperNodeActionAttrToVec(Widget &widget, const AccessibilityElementInfo &element) 287 { 288 auto actions = element.GetActionList(); 289 for (auto &action : actions) { 290 switch (action.GetActionType()) { 291 case ACCESSIBILITY_ACTION_CLICK: 292 widget.SetAttr(UiAttr::CLICKABLE, "true"); 293 break; 294 case ACCESSIBILITY_ACTION_LONG_CLICK: 295 widget.SetAttr(UiAttr::LONG_CLICKABLE, "true"); 296 break; 297 case ACCESSIBILITY_ACTION_SCROLL_FORWARD: 298 case ACCESSIBILITY_ACTION_SCROLL_BACKWARD: 299 widget.SetAttr(UiAttr::SCROLLABLE, "true"); 300 break; 301 default: 302 break; 303 } 304 } 305 } 306 } // namespace OHOS::uitest