• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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 <algorithm>
17 #include "ui_model.h"
18 
19 namespace OHOS::uitest {
20     using namespace std;
21     using namespace nlohmann;
22 
23     static constexpr auto ROOT_HIERARCHY = "ROOT";
24 
Rect2JsonStr(const Rect & rect)25     static string Rect2JsonStr(const Rect &rect)
26     {
27         json data;
28         data["left"] = rect.left_;
29         data["top"] = rect.top_;
30         data["right"] = rect.right_;
31         data["bottom"] = rect.bottom_;
32         return data.dump();
33     }
34 
HasAttr(string_view name) const35     bool Widget::HasAttr(string_view name) const
36     {
37         return attributes_.find(string(name)) != attributes_.end();
38     }
39 
GetAttr(string_view name,string_view defaultVal) const40     string Widget::GetAttr(string_view name, string_view defaultVal) const
41     {
42         auto find = attributes_.find(string(name));
43         return find == attributes_.end() ? string(defaultVal) : find->second;
44     }
45 
SetAttr(string_view name,string_view value)46     void Widget::SetAttr(string_view name, string_view value)
47     {
48         attributes_[string(name)] = value;
49     }
50 
SetHostTreeId(string_view tid)51     void Widget::SetHostTreeId(string_view tid)
52     {
53         hostTreeId_ = tid;
54     }
55 
GetHostTreeId() const56     string Widget::GetHostTreeId() const
57     {
58         return hostTreeId_;
59     }
60 
SetBounds(const Rect & bounds)61     void Widget::SetBounds(const Rect &bounds)
62     {
63         bounds_ = bounds;
64         // save bounds attribute as structured data
65         SetAttr(ATTR_NAMES[UiAttr::BOUNDS], Rect2JsonStr(bounds_));
66     }
67 
ToStr() const68     string Widget::ToStr() const
69     {
70         stringstream os;
71         os << "Widget{";
72         for (auto &pair : attributes_) {
73             os << pair.first << "='" << pair.second << "',";
74         }
75         os << "}";
76         return os.str();
77     }
78 
Clone(string_view hostTreeId,string_view hierarchy) const79     unique_ptr<Widget> Widget::Clone(string_view hostTreeId, string_view hierarchy) const
80     {
81         auto clone = make_unique<Widget>(hierarchy);
82         clone->hostTreeId_ = hostTreeId;
83         clone->attributes_ = this->attributes_;
84         clone->bounds_ = this->bounds_;
85         clone->SetAttr(ATTR_NAMES[UiAttr::HIERARCHY], hierarchy); // ensure hiararchy consisent
86         return clone;
87     }
88 
GetAttrMap() const89     std::map<std::string, std::string> Widget::GetAttrMap() const
90     {
91         return attributes_;
92     }
93 
94     class WidgetHierarchyBuilder {
95     public:
Build(string_view parentWidgetHierarchy,uint32_t childIndex)96         static string Build(string_view parentWidgetHierarchy, uint32_t childIndex)
97         {
98             return string(parentWidgetHierarchy) + string(hierarchySeparator_) + to_string(childIndex);
99         }
100 
GetParentWidgetHierarchy(string_view hierarchy)101         static string GetParentWidgetHierarchy(string_view hierarchy)
102         {
103             if (hierarchy == ROOT_HIERARCHY) {
104                 // no parent for root widget
105                 return "";
106             }
107 
108             auto findRoot = hierarchy.find(ROOT_HIERARCHY);
109             if (findRoot != 0) {
110                 // invalid hierarchy string
111                 return "";
112             }
113             auto findLastSeparator = hierarchy.find_last_of(hierarchySeparator_);
114             if (findLastSeparator <= 0 || findLastSeparator == string::npos) {
115                 return "";
116             }
117             return string(hierarchy).substr(0, findLastSeparator);
118         }
119 
GetChildHierarchy(string_view hierarchy,uint32_t childIndex)120         static string GetChildHierarchy(string_view hierarchy, uint32_t childIndex)
121         {
122             if (hierarchy.find(ROOT_HIERARCHY) != 0) {
123                 // invalid hierarchy string
124                 return "";
125             }
126             return string(hierarchy) + string(hierarchySeparator_) + to_string(childIndex);
127         }
128 
CheckIsDescendantHierarchy(string_view hierarchy,string_view hierarchyRoot)129         inline static bool CheckIsDescendantHierarchy(string_view hierarchy, string_view hierarchyRoot)
130         {
131             // child node hierarchy must startswith parent node hierarchy
132             return hierarchy.find(hierarchyRoot) == 0;
133         }
134 
135     private:
136         static constexpr auto hierarchySeparator_ = ",";
137     };
138 
SetWidgetBounds(Widget & widget,string_view boundsStr)139     static void SetWidgetBounds(Widget &widget, string_view boundsStr)
140     {
141         // set bounds
142         int32_t val = -1;
143         int32_t integers[4];
144         int32_t index = 0;
145         bool negative = false;
146         static constexpr int32_t FACTOR = 10;
147         for (char ch : boundsStr) {
148             if (ch == '-') {
149                 DCHECK(val == -1); // should be a start of a number
150                 negative = true;
151             } else if (ch >= '0' && ch <= '9') {
152                 val = max(val, 0); // ensure accumulation
153                 val = val * FACTOR + (int32_t)(ch - '0');
154             } else if (val >= 0) {
155                 DCHECK(index < INDEX_FOUR);
156                 integers[index] = val * (negative ? -1 : 1);
157                 // after harvest, rest variables and increase ptrIdx
158                 index++;
159                 val = -1;
160                 negative = false;
161             }
162         }
163 
164         DCHECK(index == INDEX_FOUR);
165         auto rect = Rect(integers[INDEX_ZERO], integers[INDEX_TWO], integers[INDEX_ONE], integers[INDEX_THREE]);
166         widget.SetBounds(rect);
167     }
168 
SetWidgetAttributes(Widget & widget,const map<string,string> & attributes)169     static void SetWidgetAttributes(Widget &widget, const map<string, string> &attributes)
170     {
171         for (auto &item : attributes) {
172             if (item.first == ATTR_NAMES[UiAttr::BOUNDS]) {
173                 SetWidgetBounds(widget, item.second);
174             } else {
175                 widget.SetAttr(item.first, item.second);
176             }
177         }
178     }
179 
DfsTraverse(WidgetVisitor & visitor) const180     void WidgetTree::DfsTraverse(WidgetVisitor &visitor) const
181     {
182         auto root = GetRootWidget();
183         if (root != nullptr) {
184             DfsTraverseDescendants(visitor, *root);
185         }
186     }
187 
DfsTraverseFronts(WidgetVisitor & visitor,const Widget & pivot) const188     void WidgetTree::DfsTraverseFronts(WidgetVisitor &visitor, const Widget &pivot) const
189     {
190         DCHECK(widgetsConstructed_);
191         DCHECK(CheckIsMyNode(pivot));
192         auto root = GetRootWidget();
193         if (root == nullptr) {
194             return;
195         }
196         auto pivotHierarchy = pivot.GetHierarchy();
197         for (auto &hierarchy : widgetHierarchyIdDfsOrder_) {
198             if (hierarchy == pivotHierarchy) {
199                 // hit the pivot, finish traversing
200                 break;
201             }
202             auto widget = widgetMap_.find(hierarchy);
203             DCHECK(widget != widgetMap_.end());
204             visitor.Visit(widget->second);
205         }
206     }
207 
DfsTraverseRears(WidgetVisitor & visitor,const Widget & pivot) const208     void WidgetTree::DfsTraverseRears(WidgetVisitor &visitor, const Widget &pivot) const
209     {
210         DCHECK(widgetsConstructed_);
211         DCHECK(CheckIsMyNode(pivot));
212         auto root = GetRootWidget();
213         if (root == nullptr) {
214             return;
215         }
216         auto pivotHierarchy = pivot.GetHierarchy();
217         bool traverseStarted = false;
218         for (auto &hierarchy : widgetHierarchyIdDfsOrder_) {
219             if (hierarchy == pivotHierarchy) {
220                 // skip self and start traverse from next one
221                 traverseStarted = true;
222                 continue;
223             }
224             if (!traverseStarted) {
225                 // skip front widgets
226                 continue;
227             }
228             auto widget = widgetMap_.find(hierarchy);
229             DCHECK(widget != widgetMap_.end());
230             visitor.Visit(widget->second);
231         }
232     }
233 
DfsTraverseParents(WidgetVisitor & visitor,const Widget & pivot) const234     void WidgetTree::DfsTraverseParents(WidgetVisitor &visitor, const Widget &pivot) const
235     {
236         DCHECK(widgetsConstructed_);
237         DCHECK(CheckIsMyNode(pivot));
238         auto root = GetRootWidget();
239         if (root == nullptr) {
240             return;
241         }
242         auto parent = this->GetParentWidget(pivot);
243         while (true) {
244             if (parent == nullptr) {
245                 break;
246             }
247             visitor.Visit(*parent);
248             parent = this->GetParentWidget(*parent);
249         }
250     }
251 
DfsTraverseDescendants(WidgetVisitor & visitor,const Widget & root) const252     void WidgetTree::DfsTraverseDescendants(WidgetVisitor &visitor, const Widget &root) const
253     {
254         DCHECK(widgetsConstructed_);
255         DCHECK(CheckIsMyNode(root));
256         bool traverseStarted = false;
257         auto rootHierarchy = root.GetHierarchy();
258         for (auto &hierarchy : widgetHierarchyIdDfsOrder_) {
259             if (!WidgetHierarchyBuilder::CheckIsDescendantHierarchy(hierarchy, rootHierarchy)) {
260                 if (!traverseStarted) {
261                     continue; // root node not found yet, skip visiting current widget and go ahead
262                 } else {
263                     break; // descendant nodes are all visited, break
264                 }
265             }
266             traverseStarted = true;
267             auto widget = widgetMap_.find(hierarchy);
268             DCHECK(widget != widgetMap_.end());
269             visitor.Visit(widget->second);
270         }
271     }
272 
273     using NodeVisitor = function<void(string_view, map<string, string> &&)>;
274 
DfsVisitNode(const json & root,NodeVisitor visitor,string_view hierarchy)275     static void DfsVisitNode(const json &root, NodeVisitor visitor, string_view hierarchy)
276     {
277         DCHECK(visitor != nullptr);
278         auto attributesData = root["attributes"];
279         auto childrenData = root["children"];
280         map<string, string> attributeDict;
281         if (root.find("abilityName") != root.end()) {
282             attributeDict["abilityName"] = root["abilityName"];
283         }
284         if (root.find("bundleName") != root.end()) {
285             attributeDict["bundleName"] = root["bundleName"];
286         }
287         if (root.find("pagePath") != root.end()) {
288             attributeDict["pagePath"] = root["pagePath"];
289         }
290         for (auto &item : attributesData.items()) {
291             attributeDict[item.key()] = item.value();
292         }
293         visitor(hierarchy, move(attributeDict));
294         const size_t childCount = childrenData.size();
295         for (size_t idx = 0; idx < childCount; idx++) {
296             auto &child = childrenData.at(idx);
297             auto childHierarchy = WidgetHierarchyBuilder::Build(hierarchy, idx);
298             DfsVisitNode(child, visitor, childHierarchy);
299         }
300     }
301 
EnsureParentVisible(const Widget & widget)302     void WidgetTree::EnsureParentVisible(const Widget &widget)
303     {
304         auto hierarchy = widget.GetHierarchy();
305         auto findParent = widgetMap_.find(WidgetHierarchyBuilder::GetParentWidgetHierarchy(hierarchy));
306         if (findParent == widgetMap_.end()) {
307             return;
308         } else if (!findParent->second.IsVisible()) {
309             findParent->second.SetAttr(ATTR_NAMES[UiAttr::VISIBLE], "true");
310             EnsureParentVisible(findParent->second);
311         }
312     }
313 
GetEffectiveBounds(const Widget & widget,vector<pair<string,Rect>> & boundsClips)314     static Rect GetEffectiveBounds(const Widget &widget, vector<pair<string, Rect>> &boundsClips)
315     {
316         auto result = Rect(0, 0, 0, 0);
317         auto boundsClip = Rect(0, 0, 0, 0);
318         if (boundsClips.empty()) {
319             boundsClip = widget.GetBounds();
320         } else {
321             while (widget.GetHierarchy().find(boundsClips.back().first) == std::string::npos) {
322                 boundsClips.pop_back();
323                 if (boundsClips.empty()) {
324                     boundsClip = widget.GetBounds();
325                     break;
326                 }
327             }
328             if (!boundsClips.empty()) {
329                 boundsClip = boundsClips.back().second;
330             }
331         }
332         if (!RectAlgorithm::ComputeIntersection(widget.GetBounds(), boundsClip, result)) {
333             result = Rect(0, 0, 0, 0);
334         }
335         return result;
336     }
337 
ConstructFromDom(const nlohmann::json & dom,bool amendBounds)338     void WidgetTree::ConstructFromDom(const nlohmann::json &dom, bool amendBounds)
339     {
340         DCHECK(!widgetsConstructed_);
341         static const set<string> containerTypes = {"List", "Grid", "WaterFlow", "GridCol", "GridRow", "Scroll",
342             "Flex", "ListItemGroup", "Swiper", "DecorBar", "_Common_", "TabContent", "WindowScene"};
343         map<string, map<string, string>> widgetDict;
344         vector<string> visitTrace;
345         auto nodeVisitor = [&widgetDict, &visitTrace](string_view hierarchy, map<string, string> &&attrs) {
346             visitTrace.emplace_back(hierarchy);
347             widgetDict.insert(make_pair(hierarchy, attrs));
348         };
349         DfsVisitNode(dom, nodeVisitor, ROOT_HIERARCHY);
350         vector <pair<string, Rect>> boundsClips;
351         for (auto &hierarchy : visitTrace) {
352             auto findWidgetAttrs = widgetDict.find(hierarchy);
353             DCHECK(findWidgetAttrs != widgetDict.end());
354             Widget widget(hierarchy);
355             widget.SetHostTreeId(this->identifier_);
356             SetWidgetAttributes(widget, findWidgetAttrs->second);
357             const auto bounds = widget.GetBounds();
358             auto newBounds = Rect(0, 0, 0, 0);
359             if (!amendBounds || hierarchy == ROOT_HIERARCHY) {
360                 newBounds = bounds;
361             } else {
362                 newBounds = GetEffectiveBounds(widget, boundsClips);
363             }
364             if (!RectAlgorithm::CheckEqual(newBounds, bounds)) {
365                 widget.SetBounds(newBounds);
366                 LOG_D("Amend bounds %{public}s from %{public}s", widget.ToStr().c_str(), Rect2JsonStr(bounds).c_str());
367             }
368             if (!amendBounds || (newBounds.GetWidth() > 0 && newBounds.GetHeight() > 0)) {
369                 widget.SetAttr(ATTR_NAMES[UiAttr::VISIBLE], "true");
370             }
371             if (widget.IsVisible()) {
372                 EnsureParentVisible(widget);
373             }
374             auto type = widget.GetAttr(ATTR_NAMES[UiAttr::TYPE], "");
375             if (containerTypes.find(type) != containerTypes.end()) {
376                 boundsClips.push_back(make_pair(widget.GetHierarchy(), widget.GetBounds()));
377             }
378             widgetMap_.insert(make_pair(hierarchy, move(widget)));
379             widgetHierarchyIdDfsOrder_.emplace_back(hierarchy);
380         }
381         widgetsConstructed_ = true;
382     }
383 
DfsMarshalWidget(const WidgetTree & tree,const Widget & root,nlohmann::json & dom,const std::map<string,size_t> & widgetChildCountMap)384     static void DfsMarshalWidget(const WidgetTree& tree, const Widget& root, nlohmann::json& dom,
385         const std::map<string, size_t>& widgetChildCountMap)
386     {
387         auto attributesData = json();
388         // "< UiAttr::HIERARCHY" : do not expose inner used attributes
389         for (auto index = 0; index < UiAttr::HIERARCHY; index++) {
390             const auto attr = ATTR_NAMES[index].data();
391             attributesData[attr] = root.GetAttr(attr, "");
392         }
393         if (root.HasAttr("abilityName")) {
394             attributesData["abilityName"] = root.GetAttr("abilityName", "");
395         }
396         if (root.HasAttr("bundleName")) {
397             attributesData["bundleName"] = root.GetAttr("bundleName", "");
398         }
399         if (root.HasAttr("pagePath")) {
400             attributesData["pagePath"] = root.GetAttr("pagePath", "");
401         }
402         stringstream stream;
403         auto rect = root.GetBounds();
404         stream << "[" << rect.left_ << "," << rect.top_ << "]"
405                << "[" << rect.right_ << "," << rect.bottom_ << "]";
406         attributesData[ATTR_NAMES[UiAttr::BOUNDS].data()] = stream.str();
407 
408         auto childrenData = json::array();
409         uint32_t childIndex = 0;
410         uint32_t childCount = 0;
411         uint32_t visitCount = 0;
412         auto hierarchy = root.GetHierarchy();
413         if (widgetChildCountMap.find(hierarchy) != widgetChildCountMap.end()) {
414             childCount = widgetChildCountMap.find(hierarchy)->second;
415         }
416         while (visitCount < childCount) {
417             auto child = tree.GetChildWidget(root, childIndex);
418             childIndex++;
419             if (child == nullptr) {
420                 continue;
421             }
422             if (!child->IsVisible()) {
423                 visitCount++;
424                 continue;
425             }
426             auto childData = json();
427             DfsMarshalWidget(tree, *child, childData, widgetChildCountMap);
428             childrenData.emplace_back(childData);
429             visitCount++;
430         }
431 
432         dom["attributes"] = attributesData;
433         dom["children"] = childrenData;
434     }
435 
MarshalIntoDom(nlohmann::json & dom) const436     void WidgetTree::MarshalIntoDom(nlohmann::json& dom) const
437     {
438         DCHECK(widgetsConstructed_);
439         auto root = GetRootWidget();
440         std::map<string, size_t> widgetChildCountMap;
441         for (auto &hierarchy : widgetHierarchyIdDfsOrder_) {
442             if (hierarchy == ROOT_HIERARCHY) {
443                 continue;
444             }
445             auto parentHierarchy = WidgetHierarchyBuilder::GetParentWidgetHierarchy(hierarchy);
446             if (widgetChildCountMap.find(parentHierarchy) == widgetChildCountMap.end()) {
447                 widgetChildCountMap[parentHierarchy] = 1;
448             } else {
449                 widgetChildCountMap[parentHierarchy] = widgetChildCountMap[parentHierarchy] + 1;
450             }
451         }
452         if (root != nullptr) {
453             DfsMarshalWidget(*this, *root, dom, widgetChildCountMap);
454         }
455     }
456 
GetRootWidget() const457     const Widget *WidgetTree::GetRootWidget() const
458     {
459         return GetWidgetByHierarchy(ROOT_HIERARCHY);
460     }
461 
GetParentWidget(const Widget & widget) const462     const Widget *WidgetTree::GetParentWidget(const Widget &widget) const
463     {
464         DCHECK(CheckIsMyNode(widget));
465         if (widget.GetHierarchy() == ROOT_HIERARCHY) {
466             return nullptr;
467         }
468         const auto parentHierarchy = WidgetHierarchyBuilder::GetParentWidgetHierarchy(widget.GetHierarchy());
469         return GetWidgetByHierarchy(parentHierarchy);
470     }
471 
GetChildWidget(const Widget & widget,uint32_t index) const472     const Widget *WidgetTree::GetChildWidget(const Widget &widget, uint32_t index) const
473     {
474         DCHECK(CheckIsMyNode(widget));
475         if (index < 0) {
476             return nullptr;
477         }
478         auto childHierarchy = WidgetHierarchyBuilder::GetChildHierarchy(widget.GetHierarchy(), index);
479         return GetWidgetByHierarchy(childHierarchy);
480     }
481 
GetWidgetByHierarchy(string_view hierarchy) const482     const Widget *WidgetTree::GetWidgetByHierarchy(string_view hierarchy) const
483     {
484         auto findWidget = widgetMap_.find(string(hierarchy));
485         if (findWidget != widgetMap_.end()) {
486             return &(findWidget->second);
487         }
488         return nullptr;
489     }
490 
IsRootWidgetHierarchy(string_view hierarchy)491     bool WidgetTree::IsRootWidgetHierarchy(string_view hierarchy)
492     {
493         return ROOT_HIERARCHY == hierarchy;
494     }
495 
GenerateTreeId()496     string WidgetTree::GenerateTreeId()
497     {
498         static uint32_t counter = 0;
499         auto id = string("WidgetTree@") + to_string(counter);
500         counter++;
501         return id;
502     }
503 
CheckIsMyNode(const Widget & widget) const504     inline bool WidgetTree::CheckIsMyNode(const Widget &widget) const
505     {
506         return this->identifier_ == widget.GetHostTreeId();
507     }
508 
509     /** WidgetVisitor used to visit and merge widgets of subtrees into dest root tree.*/
510     class MergerVisitor : public WidgetVisitor {
511     public:
512         // handler function to perform merging widget, arg1: the revised bounds
513         using MergerFunction = function<void(const Widget &, const Rect &)>;
MergerVisitor(MergerFunction collector)514         explicit MergerVisitor(MergerFunction collector) : collector_(collector) {};
515 
~MergerVisitor()516         ~MergerVisitor() {}
517 
518         void PrepareToVisitSubTree(const WidgetTree &tree);
519 
520         void EndVisitingSubTree();
521 
522         void Visit(const Widget &widget) override;
523 
GetMergedBounds() const524         Rect GetMergedBounds() const
525         {
526             return mergedBounds_;
527         }
528 
529     private:
530         MergerFunction collector_;
531         // the overlays of widget
532         vector<Rect> overlays_;
533         // the node hierarchy of first invisble parent
534         string maxInvisibleParent_ = "NA";
535         // the node hierarchy of first fully-visible parent
536         string maxFullyParent_ = "NA";
537         // the merged bounds
538         Rect mergedBounds_ = {0, 0, 0, 0};
539         // bounds of current visiting tree
540         Rect visitingTreeBounds_ = {0, 0, 0, 0};
541     };
542 
PrepareToVisitSubTree(const WidgetTree & tree)543     void MergerVisitor::PrepareToVisitSubTree(const WidgetTree &tree)
544     {
545         auto root = tree.GetRootWidget();
546         DCHECK(root != nullptr);
547         // collect max bounds
548         auto rootBounds = root->GetBounds();
549         mergedBounds_.left_ = min(mergedBounds_.left_, rootBounds.left_);
550         mergedBounds_.top_ = min(mergedBounds_.top_, rootBounds.top_);
551         mergedBounds_.right_ = max(mergedBounds_.right_, rootBounds.right_);
552         mergedBounds_.bottom_ = max(mergedBounds_.bottom_, rootBounds.bottom_);
553         // update visiting tree bounds
554         visitingTreeBounds_ = rootBounds;
555         // reset intermediate data
556         maxInvisibleParent_ = "NA";
557         maxFullyParent_ = "NA";
558     }
559 
EndVisitingSubTree()560     void MergerVisitor::EndVisitingSubTree()
561     {
562         // add overlays for later visited subtrees
563         overlays_.push_back(visitingTreeBounds_);
564     }
565 
Visit(const Widget & widget)566     void MergerVisitor::Visit(const Widget &widget)
567     {
568         if (collector_ == nullptr) {
569             return;
570         }
571         const auto hierarchy = widget.GetHierarchy();
572         const auto inInVisible = WidgetHierarchyBuilder::CheckIsDescendantHierarchy(hierarchy, maxInvisibleParent_);
573         const auto inFully = WidgetHierarchyBuilder::CheckIsDescendantHierarchy(hierarchy, maxFullyParent_);
574         if (inInVisible) {
575             // parent invisible, skip
576             return;
577         }
578         const auto bounds = widget.GetBounds();
579         bool visible = true;
580         auto visibleRegion = bounds;
581         if (!inFully) {
582             // parent not full-visible, need compute visible region
583             visible = RectAlgorithm::ComputeMaxVisibleRegion(bounds, overlays_, visibleRegion);
584         }
585         if (!visible) {
586             maxInvisibleParent_ = hierarchy; // update maxInvisibleParent
587             return;
588         }
589         if (!inFully && RectAlgorithm::CheckEqual(bounds, visibleRegion)) {
590             maxFullyParent_ = hierarchy; // update maxFullParent
591         }
592         // call collector with widget and revised bounds
593         collector_(widget, visibleRegion);
594     }
595 
MergeTrees(const vector<unique_ptr<WidgetTree>> & from,WidgetTree & to,vector<int32_t> & mergedOrders)596     void WidgetTree::MergeTrees(const vector<unique_ptr<WidgetTree>> &from, WidgetTree &to,
597         vector<int32_t> &mergedOrders)
598     {
599         if (from.empty()) {
600             return;
601         }
602         size_t subtreeIndex = 0;
603         size_t hierarchyIndex = 0;
604         to.widgetsConstructed_ = true;
605         auto virtualRoot = Widget(ROOT_HIERARCHY);
606         virtualRoot.SetAttr(ATTR_NAMES[UiAttr::VISIBLE], "true");
607         virtualRoot.SetHostTreeId(to.identifier_);
608         // insert virtual root node into tree
609         to.widgetHierarchyIdDfsOrder_.emplace_back(ROOT_HIERARCHY);
610         to.widgetMap_.insert(make_pair(ROOT_HIERARCHY, move(virtualRoot)));
611         auto &vitualRootWidget = to.widgetMap_.begin()->second;
612         // amend widget hierarchy with prefix to make it the descendant of the virtualRoot
613         string hierarchyPrefix = "";
614         constexpr auto offset = string_view(ROOT_HIERARCHY).length();
615         // collect widget with revised hierarchy and bounds, merge it to dest tree
616         auto merger = [&hierarchyPrefix, &tree = to, &subtreeIndex, &hierarchyIndex, &mergedOrders]
617             (const Widget &widget, const Rect &bounds) {
618             auto newHierarchy = string(hierarchyPrefix) + widget.GetHierarchy().substr(offset);
619             auto newWidget = widget.Clone(tree.identifier_, newHierarchy);
620             newWidget->SetBounds(bounds);
621             if (widget.GetHierarchy() == ROOT_HIERARCHY) {
622                 mergedOrders.push_back(subtreeIndex);
623                 hierarchyIndex++;
624             }
625             tree.widgetMap_.insert(make_pair(newHierarchy, move(*newWidget)));
626             tree.widgetHierarchyIdDfsOrder_.emplace_back(newHierarchy);
627         };
628         auto visitor = MergerVisitor(merger);
629         for (auto &tree : from) {
630             DCHECK(tree != nullptr);
631             // update hierarchy prefix
632             hierarchyPrefix = string(ROOT_HIERARCHY) + "," + to_string(hierarchyIndex);
633             // visit tree and forward visible widgets to collector
634             visitor.PrepareToVisitSubTree(*tree);
635             tree->DfsTraverse(visitor);
636             visitor.EndVisitingSubTree();
637             subtreeIndex++;
638         }
639         // amend bounds of the virtual root
640         vitualRootWidget.SetBounds(visitor.GetMergedBounds());
641     }
642 
Visit(const Widget & widget)643     void TreeSnapshotTaker::Visit(const Widget &widget)
644     {
645         auto type = widget.GetAttr(ATTR_NAMES[UiAttr::TYPE], "") + "/";
646         auto value = widget.GetAttr(ATTR_NAMES[UiAttr::TEXT], "") + "/";
647         auto hashcode = widget.GetAttr(ATTR_NAMES[UiAttr::HASHCODE], "") + ";";
648         if (value != "/") {
649             allNodes_.push_back(type + value + hashcode);
650             if ((widget.GetAttr(ATTR_NAMES[UiAttr::VISIBLE], "")) == "true") {
651                 displayNodes_.push_back(type + value + hashcode);
652             }
653         }
654     }
655 } // namespace OHOS::uitest
656