1 /*
2  * Copyright (c) 2020-2021 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 "dfx/ui_dump_dom_tree.h"
17 
18 #if ENABLE_DEBUG
19 #include "components/root_view.h"
20 #include "components/ui_abstract_clock.h"
21 #include "components/ui_abstract_progress.h"
22 #include "components/ui_checkbox.h"
23 #include "components/ui_image_view.h"
24 #include "components/ui_label.h"
25 #include "components/ui_label_button.h"
26 #include "components/ui_list.h"
27 #include "components/ui_picker.h"
28 #include "components/ui_scroll_view.h"
29 #include "components/ui_swipe_view.h"
30 #include "components/ui_time_picker.h"
31 #include "components/ui_toggle_button.h"
32 #include "components/ui_view.h"
33 #include "draw/draw_image.h"
34 #include "gfx_utils/file.h"
35 #include "gfx_utils/graphic_log.h"
36 #endif // ENABLE_DEBUG
37 namespace OHOS {
38 #if ENABLE_DEBUG
GetInstance()39 UIDumpDomTree* UIDumpDomTree::GetInstance()
40 {
41     static UIDumpDomTree instance;
42     return &instance;
43 }
44 
AddNameField(UIViewType type,cJSON * usr) const45 void UIDumpDomTree::AddNameField(UIViewType type, cJSON* usr) const
46 {
47     if (usr == nullptr) {
48         return;
49     }
50 
51     if (type < UI_NUMBER_MAX) {
52         cJSON_AddStringToObject(usr, "name", VIEW_TYPE_STRING[type]);
53     } else {
54         cJSON_AddStringToObject(usr, "name", "UnknownType");
55     }
56 }
57 
AddImageViewSpecialField(const UIView * view,cJSON * usr) const58 void UIDumpDomTree::AddImageViewSpecialField(const UIView* view, cJSON* usr) const
59 {
60     if ((view == nullptr) || (usr == nullptr)) {
61         return;
62     }
63     const UIImageView* tmpImageView = static_cast<const UIImageView*>(view);
64     ImageSrcType srcType = tmpImageView->GetSrcType();
65     if (srcType == IMG_SRC_FILE) {
66         cJSON_AddStringToObject(usr, "src", reinterpret_cast<const char*>(tmpImageView->GetPath()));
67     } else if (srcType == IMG_SRC_VARIABLE) {
68         const ImageInfo* imageInfo = reinterpret_cast<const ImageInfo*>(tmpImageView->GetImageInfo());
69         if ((imageInfo == nullptr) || (imageInfo->userData == nullptr)) {
70             cJSON_AddStringToObject(usr, "src", "");
71             return;
72         }
73         uintptr_t userData = reinterpret_cast<uintptr_t>(imageInfo->userData);
74         cJSON_AddNumberToObject(usr, "src", static_cast<uint32_t>(userData));
75     } else {
76         cJSON_AddStringToObject(usr, "src", "");
77     }
78 }
79 
AddLabelField(const UIView * view,cJSON * usr) const80 void UIDumpDomTree::AddLabelField(const UIView* view, cJSON* usr) const
81 {
82     const UILabel* tmpLabel = static_cast<const UILabel*>(view);
83     cJSON_AddStringToObject(usr, "text", tmpLabel->GetText());
84     tmpLabel = nullptr;
85 }
86 
AddLabelButtonField(const UIView * view,cJSON * usr) const87 void UIDumpDomTree::AddLabelButtonField(const UIView* view, cJSON* usr) const
88 {
89     const UILabelButton* tmpLabelButton = static_cast<const UILabelButton*>(view);
90     cJSON_AddStringToObject(usr, "text", tmpLabelButton->GetText());
91     tmpLabelButton = nullptr;
92 }
93 
AddCheckboxField(const UIView * view,cJSON * usr) const94 void UIDumpDomTree::AddCheckboxField(const UIView* view, cJSON* usr) const
95 {
96     const UICheckBox* tmpCheckBox = static_cast<const UICheckBox*>(view);
97     if (tmpCheckBox->GetState()) {
98         cJSON_AddStringToObject(usr, "state", "UNSELECTED");
99     } else {
100         cJSON_AddStringToObject(usr, "state", "SELECTED");
101     }
102     tmpCheckBox = nullptr;
103 }
104 
AddToggleButtonField(const UIView * view,cJSON * usr) const105 void UIDumpDomTree::AddToggleButtonField(const UIView* view, cJSON* usr) const
106 {
107     const UIToggleButton* tmpToggleButton = static_cast<const UIToggleButton*>(view);
108     cJSON_AddBoolToObject(usr, "state", tmpToggleButton->GetState());
109     tmpToggleButton = nullptr;
110 }
111 
AddProgressField(const UIView * view,cJSON * usr) const112 void UIDumpDomTree::AddProgressField(const UIView* view, cJSON* usr) const
113 {
114     const UIAbstractProgress* tmpAbstractProgress = static_cast<const UIAbstractProgress*>(view);
115     cJSON_AddNumberToObject(usr, "currValue", static_cast<double>(tmpAbstractProgress->GetValue()));
116     cJSON_AddNumberToObject(usr, "rangeMin", static_cast<double>(tmpAbstractProgress->GetRangeMin()));
117     cJSON_AddNumberToObject(usr, "rangeMax", static_cast<double>(tmpAbstractProgress->GetRangeMax()));
118     tmpAbstractProgress = nullptr;
119 }
120 
AddScrollViewField(const UIView * view,cJSON * usr) const121 void UIDumpDomTree::AddScrollViewField(const UIView* view, cJSON* usr) const
122 {
123     const UIScrollView* tmpScrollView = static_cast<const UIScrollView*>(view);
124     cJSON_AddBoolToObject(usr, "xScrollable", tmpScrollView->GetHorizontalScrollState());
125     cJSON_AddBoolToObject(usr, "yScrollable", tmpScrollView->GetVerticalScrollState());
126     tmpScrollView = nullptr;
127 }
128 
AddListField(const UIView * view,cJSON * usr) const129 void UIDumpDomTree::AddListField(const UIView* view, cJSON* usr) const
130 {
131     UIList* tmpList = static_cast<UIList*>(const_cast<UIView*>(view));
132     cJSON_AddBoolToObject(usr, "isLoopList", tmpList->GetLoopState());
133     UIView* selectView = tmpList->GetSelectView();
134     if (selectView != nullptr) {
135         cJSON_AddNumberToObject(usr, "selectedIndex", static_cast<double>(selectView->GetViewIndex()));
136         selectView = nullptr;
137     }
138     tmpList = nullptr;
139 }
140 
AddClockField(const UIView * view,cJSON * usr) const141 void UIDumpDomTree::AddClockField(const UIView* view, cJSON* usr) const
142 {
143     const UIAbstractClock* tmpAbstractClock = static_cast<const UIAbstractClock*>(view);
144     cJSON_AddNumberToObject(usr, "currentHour", static_cast<double>(tmpAbstractClock->GetCurrentHour()));
145     cJSON_AddNumberToObject(usr, "currentMinute", static_cast<double>(tmpAbstractClock->GetCurrentMinute()));
146     cJSON_AddNumberToObject(usr, "currentSecond", static_cast<double>(tmpAbstractClock->GetCurrentSecond()));
147     tmpAbstractClock = nullptr;
148 }
149 
AddPickerField(const UIView * view,cJSON * usr) const150 void UIDumpDomTree::AddPickerField(const UIView* view, cJSON* usr) const
151 {
152     const UIPicker* tmpPicker = static_cast<const UIPicker*>(view);
153     cJSON_AddNumberToObject(usr, "selectedIndex", static_cast<double>(tmpPicker->GetSelected()));
154     tmpPicker = nullptr;
155 }
156 
AddSwipeViewField(const UIView * view,cJSON * usr) const157 void UIDumpDomTree::AddSwipeViewField(const UIView* view, cJSON* usr) const
158 {
159     const UISwipeView* tmpSwipeView = static_cast<const UISwipeView*>(view);
160     cJSON_AddNumberToObject(usr, "currentIndex", static_cast<double>(tmpSwipeView->GetCurrentPage()));
161     cJSON_AddNumberToObject(usr, "direction", static_cast<double>(tmpSwipeView->GetDirection()));
162     tmpSwipeView = nullptr;
163 }
164 
AddTimePickerField(const UIView * view,cJSON * usr) const165 void UIDumpDomTree::AddTimePickerField(const UIView* view, cJSON* usr) const
166 {
167     const UITimePicker* tmpTimePicker = static_cast<const UITimePicker*>(view);
168     cJSON_AddStringToObject(usr, "selectedHour", tmpTimePicker->GetSelectHour());
169     cJSON_AddStringToObject(usr, "selectedMinute", tmpTimePicker->GetSelectMinute());
170     cJSON_AddStringToObject(usr, "selectedSecond", tmpTimePicker->GetSelectSecond());
171     tmpTimePicker = nullptr;
172 }
173 
AddSpecialField(const UIView * view,cJSON * usr) const174 void UIDumpDomTree::AddSpecialField(const UIView* view, cJSON* usr) const
175 {
176     if ((view == nullptr) || (usr == nullptr)) {
177         return;
178     }
179     switch (view->GetViewType()) {
180         case UI_LABEL:
181         case UI_ARC_LABEL:
182             AddLabelField(view, usr);
183             break;
184 
185         case UI_LABEL_BUTTON:
186             AddLabelButtonField(view, usr);
187             break;
188 
189         case UI_CHECK_BOX:
190         case UI_RADIO_BUTTON:
191             AddCheckboxField(view, usr);
192             break;
193 
194         case UI_TOGGLE_BUTTON:
195             AddToggleButtonField(view, usr);
196             break;
197         case UI_IMAGE_VIEW:
198             AddImageViewSpecialField(view, usr);
199             break;
200 
201         // case below are all progress, thus has same attr.
202         case UI_BOX_PROGRESS:
203         case UI_SLIDER:
204         case UI_CIRCLE_PROGRESS:
205             AddProgressField(view, usr);
206             break;
207 
208         case UI_SCROLL_VIEW:
209             AddScrollViewField(view, usr);
210             break;
211 
212         case UI_LIST:
213             AddListField(view, usr);
214             break;
215 
216         case UI_DIGITAL_CLOCK:
217         case UI_ANALOG_CLOCK:
218             AddClockField(view, usr);
219             break;
220 
221         case UI_PICKER:
222             AddPickerField(view, usr);
223             break;
224 
225         case UI_SWIPE_VIEW:
226             AddSwipeViewField(view, usr);
227             break;
228 
229         case UI_TIME_PICKER:
230             AddTimePickerField(view, usr);
231             break;
232 
233         default:
234             break;
235     }
236 }
237 
AddCommonField(UIView * view,cJSON * usr) const238 void UIDumpDomTree::AddCommonField(UIView* view, cJSON* usr) const
239 {
240     if ((view == nullptr) || (usr == nullptr)) {
241         return;
242     }
243     cJSON_AddNumberToObject(usr, "x", static_cast<double>(view->GetOrigRect().GetX()));
244     cJSON_AddNumberToObject(usr, "y", static_cast<double>(view->GetOrigRect().GetY()));
245     cJSON_AddNumberToObject(usr, "width", static_cast<double>(view->GetWidth()));
246     cJSON_AddNumberToObject(usr, "height", static_cast<double>(view->GetHeight()));
247     cJSON_AddStringToObject(usr, "id", view->GetViewId());
248     cJSON_AddBoolToObject(usr, "visible", view->IsVisible());
249     cJSON_AddBoolToObject(usr, "touchable", view->IsTouchable());
250     cJSON_AddBoolToObject(usr, "draggable", view->IsDraggable());
251     cJSON_AddBoolToObject(usr, "onClickListener", (view->GetOnClickListener() != nullptr));
252     cJSON_AddBoolToObject(usr, "onDragListener", (view->GetOnDragListener() != nullptr));
253     cJSON_AddBoolToObject(usr, "onLongPressListener", (view->GetOnLongPressListener() != nullptr));
254 }
255 
OutputDomNode(UIView * view)256 void UIDumpDomTree::OutputDomNode(UIView* view)
257 {
258     if (view == nullptr) {
259         return;
260     }
261     /* Output current view's info into cJSON structure. */
262     cJSON* dumpUsr = cJSON_CreateObject();
263     if (dumpUsr == nullptr) {
264         GRAPHIC_LOGE("UIDumpDomTree::OutputDomNode cJSON create object failed Err!\n");
265         return;
266     }
267 
268     AddNameField(view->GetViewType(), dumpUsr);
269     AddCommonField(view, dumpUsr);
270     AddSpecialField(view, dumpUsr);
271 
272     pJson_ = cJSON_Print(dumpUsr);
273     cJSON_Delete(dumpUsr);
274 }
275 
WriteDumpFile() const276 bool UIDumpDomTree::WriteDumpFile() const
277 {
278     unlink(path_);
279     int32_t fd = open(path_, O_CREAT | O_RDWR, DEFAULT_FILE_PERMISSION);
280     if (fd < 0) {
281         GRAPHIC_LOGE("UIDumpDomTree::WriteDumpFile open file failed Err!\n");
282         return false;
283     }
284 
285     if (pJson_ == nullptr) {
286         close(fd);
287         return false;
288     }
289 
290     uint32_t length = strlen(pJson_);
291     if (static_cast<uint32_t>(write(fd, pJson_, length)) != length) {
292         GRAPHIC_LOGE("UIDumpDomTree::WriteDumpFile write file failed Err!\n");
293         close(fd);
294         return false;
295     }
296 
297     if (close(fd) < 0) {
298         return false;
299     }
300     return true;
301 }
302 
OutputDomTree(UIView * view,cJSON * usr)303 void UIDumpDomTree::OutputDomTree(UIView* view, cJSON* usr)
304 {
305     if (allocErrorFlag_) {
306         return;
307     }
308 
309     cJSON* dumpUsr = usr;
310     if (dumpUsr != rootJson_) {
311         dumpUsr = cJSON_CreateObject();
312         if (dumpUsr == nullptr) {
313             allocErrorFlag_ = true;
314             GRAPHIC_LOGE("UIDumpDomTree::OutputDomTree cJSON create object failed Err!\n");
315             return;
316         }
317         /* usr must be an array. */
318         cJSON_AddItemToArray(usr, dumpUsr);
319     }
320 
321     AddNameField(view->GetViewType(), dumpUsr);
322     AddCommonField(view, dumpUsr);
323     AddSpecialField(view, dumpUsr);
324 
325     if (view->IsViewGroup()) {
326         cJSON* arrayJson = cJSON_CreateArray();
327         if (arrayJson == nullptr) {
328             allocErrorFlag_ = true;
329             GRAPHIC_LOGE("UIDumpDomTree::OutputDomTree cJSON create object failed Err!\n");
330             return;
331         }
332 
333         cJSON_AddItemToObject(dumpUsr, "child", arrayJson);
334         UIViewGroup* tmpViewGroup = static_cast<UIViewGroup*>(view);
335         UIView* childView = tmpViewGroup->GetChildrenHead();
336         while (childView != nullptr) {
337             OutputDomTree(childView, arrayJson);
338             childView = childView->GetNextSibling();
339         }
340     }
341 }
342 
DumpJsonById(UIView * view,const char * id,DumpMode mode)343 void UIDumpDomTree::DumpJsonById(UIView* view, const char* id, DumpMode mode)
344 {
345     if (searchFlag_) {
346         return;
347     }
348 
349     /* Check whether current view is the view we are looking for. */
350     if ((view->GetViewId() != nullptr) && !strcmp(view->GetViewId(), id)) {
351         if (mode == DUMP_NODE) {
352             /* Find the view with right id, output its info. */
353             OutputDomNode(view);
354         } else if (mode == DUMP_TREE) {
355             /* Find the view with right id, output its dom tree's info. */
356             OutputDomTree(view, rootJson_);
357         }
358         /* Set flag to stop the tranversion. */
359         searchFlag_ = true;
360     } else {
361         /* Look through all childrens of the current viewGroup. */
362         if (view->IsViewGroup()) {
363             UIViewGroup* tmpViewGroup = static_cast<UIViewGroup*>(view);
364             UIView* childView = tmpViewGroup->GetChildrenHead();
365             while (childView != nullptr) {
366                 DumpJsonById(childView, id, mode);
367                 childView = childView->GetNextSibling();
368             }
369         }
370     }
371 }
372 #endif // ENABLE_DEBUG
373 
DumpDomNode(const char * id)374 char* UIDumpDomTree::DumpDomNode(const char* id)
375 {
376 #if ENABLE_DEBUG
377     if (id == nullptr) {
378         return nullptr;
379     }
380     /* Reset dump info */
381     pJson_ = nullptr;
382 
383     RootView* rootView = RootView::GetInstance();
384     UIView* currView = static_cast<UIView*>(rootView);
385     /* Search through all views from rootView. */
386     DumpJsonById(currView, id, DUMP_NODE);
387 
388     /* Reset the search flag and pJson for next search */
389     searchFlag_ = false;
390     return pJson_;
391 #else
392     return nullptr;
393 #endif // ENABLE_DEBUG
394 }
395 
DumpDomTree(const char * id,const char * path)396 bool UIDumpDomTree::DumpDomTree(const char* id, const char* path)
397 {
398 #if ENABLE_DEBUG
399     path_ = (path == nullptr) ? DEFAULT_DUMP_DOM_TREE_PATH : path;
400 
401     RootView* rootView = RootView::GetInstance();
402     UIView* currView = static_cast<UIView*>(rootView);
403     rootJson_ = cJSON_CreateObject();
404     if (rootJson_ == nullptr) {
405         GRAPHIC_LOGE("UIDumpDomTree::DumpDomTree cJSON create object failed Err!\n");
406         return false;
407     }
408 
409     if (id == nullptr) {
410         OutputDomTree(currView, rootJson_);
411     } else {
412         DumpJsonById(currView, id, DUMP_TREE);
413         if (!searchFlag_) {
414             cJSON_Delete(rootJson_);
415             rootJson_ = nullptr;
416             GRAPHIC_LOGI("UIDumpDomTree::DumpDomTree can not find the node \n");
417             return false;
418         }
419         /* Reset the search flag. */
420         searchFlag_ = false;
421     }
422 
423     pJson_ = cJSON_Print(rootJson_);
424     cJSON_Delete(rootJson_);
425     rootJson_ = nullptr;
426     if (pJson_ == nullptr) {
427         GRAPHIC_LOGE("UIDumpDomTree::DumpDomTree convert cJSON object to string failed Err!\n");
428         return false;
429     }
430 
431     if (!WriteDumpFile()) {
432         cJSON_free(pJson_);
433         pJson_ = nullptr;
434         GRAPHIC_LOGE("UIDumpDomTree::DumpDomTree file operation failed Err!\n");
435         return false;
436     }
437 
438     cJSON_free(pJson_);
439     pJson_ = nullptr;
440     allocErrorFlag_ = false;
441     return true;
442 #else
443     return false;
444 #endif // ENABLE_DEBUG
445 }
446 } // namespace OHOS
447