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