/* * Copyright (c) 2020-2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "dfx/ui_dump_dom_tree.h" #if ENABLE_DEBUG #include "components/root_view.h" #include "components/ui_abstract_clock.h" #include "components/ui_abstract_progress.h" #include "components/ui_checkbox.h" #include "components/ui_image_view.h" #include "components/ui_label.h" #include "components/ui_label_button.h" #include "components/ui_list.h" #include "components/ui_picker.h" #include "components/ui_scroll_view.h" #include "components/ui_swipe_view.h" #include "components/ui_time_picker.h" #include "components/ui_toggle_button.h" #include "components/ui_view.h" #include "draw/draw_image.h" #include "gfx_utils/file.h" #include "gfx_utils/graphic_log.h" #endif // ENABLE_DEBUG namespace OHOS { #if ENABLE_DEBUG UIDumpDomTree* UIDumpDomTree::GetInstance() { static UIDumpDomTree instance; return &instance; } void UIDumpDomTree::AddNameField(UIViewType type, cJSON* usr) const { if (usr == nullptr) { return; } if (type < UI_NUMBER_MAX) { cJSON_AddStringToObject(usr, "name", VIEW_TYPE_STRING[type]); } else { cJSON_AddStringToObject(usr, "name", "UnknownType"); } } void UIDumpDomTree::AddImageViewSpecialField(const UIView* view, cJSON* usr) const { if ((view == nullptr) || (usr == nullptr)) { return; } const UIImageView* tmpImageView = static_cast(view); ImageSrcType srcType = tmpImageView->GetSrcType(); if (srcType == IMG_SRC_FILE) { cJSON_AddStringToObject(usr, "src", reinterpret_cast(tmpImageView->GetPath())); } else if (srcType == IMG_SRC_VARIABLE) { const ImageInfo* imageInfo = reinterpret_cast(tmpImageView->GetImageInfo()); if ((imageInfo == nullptr) || (imageInfo->userData == nullptr)) { cJSON_AddStringToObject(usr, "src", ""); return; } uintptr_t userData = reinterpret_cast(imageInfo->userData); cJSON_AddNumberToObject(usr, "src", static_cast(userData)); } else { cJSON_AddStringToObject(usr, "src", ""); } } void UIDumpDomTree::AddLabelField(const UIView* view, cJSON* usr) const { const UILabel* tmpLabel = static_cast(view); cJSON_AddStringToObject(usr, "text", tmpLabel->GetText()); tmpLabel = nullptr; } void UIDumpDomTree::AddLabelButtonField(const UIView* view, cJSON* usr) const { const UILabelButton* tmpLabelButton = static_cast(view); cJSON_AddStringToObject(usr, "text", tmpLabelButton->GetText()); tmpLabelButton = nullptr; } void UIDumpDomTree::AddCheckboxField(const UIView* view, cJSON* usr) const { const UICheckBox* tmpCheckBox = static_cast(view); if (tmpCheckBox->GetState()) { cJSON_AddStringToObject(usr, "state", "UNSELECTED"); } else { cJSON_AddStringToObject(usr, "state", "SELECTED"); } tmpCheckBox = nullptr; } void UIDumpDomTree::AddToggleButtonField(const UIView* view, cJSON* usr) const { const UIToggleButton* tmpToggleButton = static_cast(view); cJSON_AddBoolToObject(usr, "state", tmpToggleButton->GetState()); tmpToggleButton = nullptr; } void UIDumpDomTree::AddProgressField(const UIView* view, cJSON* usr) const { const UIAbstractProgress* tmpAbstractProgress = static_cast(view); cJSON_AddNumberToObject(usr, "currValue", static_cast(tmpAbstractProgress->GetValue())); cJSON_AddNumberToObject(usr, "rangeMin", static_cast(tmpAbstractProgress->GetRangeMin())); cJSON_AddNumberToObject(usr, "rangeMax", static_cast(tmpAbstractProgress->GetRangeMax())); tmpAbstractProgress = nullptr; } void UIDumpDomTree::AddScrollViewField(const UIView* view, cJSON* usr) const { const UIScrollView* tmpScrollView = static_cast(view); cJSON_AddBoolToObject(usr, "xScrollable", tmpScrollView->GetHorizontalScrollState()); cJSON_AddBoolToObject(usr, "yScrollable", tmpScrollView->GetVerticalScrollState()); tmpScrollView = nullptr; } void UIDumpDomTree::AddListField(const UIView* view, cJSON* usr) const { UIList* tmpList = static_cast(const_cast(view)); cJSON_AddBoolToObject(usr, "isLoopList", tmpList->GetLoopState()); UIView* selectView = tmpList->GetSelectView(); if (selectView != nullptr) { cJSON_AddNumberToObject(usr, "selectedIndex", static_cast(selectView->GetViewIndex())); selectView = nullptr; } tmpList = nullptr; } void UIDumpDomTree::AddClockField(const UIView* view, cJSON* usr) const { const UIAbstractClock* tmpAbstractClock = static_cast(view); cJSON_AddNumberToObject(usr, "currentHour", static_cast(tmpAbstractClock->GetCurrentHour())); cJSON_AddNumberToObject(usr, "currentMinute", static_cast(tmpAbstractClock->GetCurrentMinute())); cJSON_AddNumberToObject(usr, "currentSecond", static_cast(tmpAbstractClock->GetCurrentSecond())); tmpAbstractClock = nullptr; } void UIDumpDomTree::AddPickerField(const UIView* view, cJSON* usr) const { const UIPicker* tmpPicker = static_cast(view); cJSON_AddNumberToObject(usr, "selectedIndex", static_cast(tmpPicker->GetSelected())); tmpPicker = nullptr; } void UIDumpDomTree::AddSwipeViewField(const UIView* view, cJSON* usr) const { const UISwipeView* tmpSwipeView = static_cast(view); cJSON_AddNumberToObject(usr, "currentIndex", static_cast(tmpSwipeView->GetCurrentPage())); cJSON_AddNumberToObject(usr, "direction", static_cast(tmpSwipeView->GetDirection())); tmpSwipeView = nullptr; } void UIDumpDomTree::AddTimePickerField(const UIView* view, cJSON* usr) const { const UITimePicker* tmpTimePicker = static_cast(view); cJSON_AddStringToObject(usr, "selectedHour", tmpTimePicker->GetSelectHour()); cJSON_AddStringToObject(usr, "selectedMinute", tmpTimePicker->GetSelectMinute()); cJSON_AddStringToObject(usr, "selectedSecond", tmpTimePicker->GetSelectSecond()); tmpTimePicker = nullptr; } void UIDumpDomTree::AddSpecialField(const UIView* view, cJSON* usr) const { if ((view == nullptr) || (usr == nullptr)) { return; } switch (view->GetViewType()) { case UI_LABEL: case UI_ARC_LABEL: AddLabelField(view, usr); break; case UI_LABEL_BUTTON: AddLabelButtonField(view, usr); break; case UI_CHECK_BOX: case UI_RADIO_BUTTON: AddCheckboxField(view, usr); break; case UI_TOGGLE_BUTTON: AddToggleButtonField(view, usr); break; case UI_IMAGE_VIEW: AddImageViewSpecialField(view, usr); break; // case below are all progress, thus has same attr. case UI_BOX_PROGRESS: case UI_SLIDER: case UI_CIRCLE_PROGRESS: AddProgressField(view, usr); break; case UI_SCROLL_VIEW: AddScrollViewField(view, usr); break; case UI_LIST: AddListField(view, usr); break; case UI_DIGITAL_CLOCK: case UI_ANALOG_CLOCK: AddClockField(view, usr); break; case UI_PICKER: AddPickerField(view, usr); break; case UI_SWIPE_VIEW: AddSwipeViewField(view, usr); break; case UI_TIME_PICKER: AddTimePickerField(view, usr); break; default: break; } } void UIDumpDomTree::AddCommonField(UIView* view, cJSON* usr) const { if ((view == nullptr) || (usr == nullptr)) { return; } cJSON_AddNumberToObject(usr, "x", static_cast(view->GetOrigRect().GetX())); cJSON_AddNumberToObject(usr, "y", static_cast(view->GetOrigRect().GetY())); cJSON_AddNumberToObject(usr, "width", static_cast(view->GetWidth())); cJSON_AddNumberToObject(usr, "height", static_cast(view->GetHeight())); cJSON_AddStringToObject(usr, "id", view->GetViewId()); cJSON_AddBoolToObject(usr, "visible", view->IsVisible()); cJSON_AddBoolToObject(usr, "touchable", view->IsTouchable()); cJSON_AddBoolToObject(usr, "draggable", view->IsDraggable()); cJSON_AddBoolToObject(usr, "onClickListener", (view->GetOnClickListener() != nullptr)); cJSON_AddBoolToObject(usr, "onDragListener", (view->GetOnDragListener() != nullptr)); cJSON_AddBoolToObject(usr, "onLongPressListener", (view->GetOnLongPressListener() != nullptr)); } void UIDumpDomTree::OutputDomNode(UIView* view) { if (view == nullptr) { return; } /* Output current view's info into cJSON structure. */ cJSON* dumpUsr = cJSON_CreateObject(); if (dumpUsr == nullptr) { GRAPHIC_LOGE("UIDumpDomTree::OutputDomNode cJSON create object failed Err!\n"); return; } AddNameField(view->GetViewType(), dumpUsr); AddCommonField(view, dumpUsr); AddSpecialField(view, dumpUsr); pJson_ = cJSON_Print(dumpUsr); cJSON_Delete(dumpUsr); } bool UIDumpDomTree::WriteDumpFile() const { unlink(path_); int32_t fd = open(path_, O_CREAT | O_RDWR, DEFAULT_FILE_PERMISSION); if (fd < 0) { GRAPHIC_LOGE("UIDumpDomTree::WriteDumpFile open file failed Err!\n"); return false; } if (pJson_ == nullptr) { close(fd); return false; } uint32_t length = strlen(pJson_); if (static_cast(write(fd, pJson_, length)) != length) { GRAPHIC_LOGE("UIDumpDomTree::WriteDumpFile write file failed Err!\n"); close(fd); return false; } if (close(fd) < 0) { return false; } return true; } void UIDumpDomTree::OutputDomTree(UIView* view, cJSON* usr) { if (allocErrorFlag_) { return; } cJSON* dumpUsr = usr; if (dumpUsr != rootJson_) { dumpUsr = cJSON_CreateObject(); if (dumpUsr == nullptr) { allocErrorFlag_ = true; GRAPHIC_LOGE("UIDumpDomTree::OutputDomTree cJSON create object failed Err!\n"); return; } /* usr must be an array. */ cJSON_AddItemToArray(usr, dumpUsr); } AddNameField(view->GetViewType(), dumpUsr); AddCommonField(view, dumpUsr); AddSpecialField(view, dumpUsr); if (view->IsViewGroup()) { cJSON* arrayJson = cJSON_CreateArray(); if (arrayJson == nullptr) { allocErrorFlag_ = true; GRAPHIC_LOGE("UIDumpDomTree::OutputDomTree cJSON create object failed Err!\n"); return; } cJSON_AddItemToObject(dumpUsr, "child", arrayJson); UIViewGroup* tmpViewGroup = static_cast(view); UIView* childView = tmpViewGroup->GetChildrenHead(); while (childView != nullptr) { OutputDomTree(childView, arrayJson); childView = childView->GetNextSibling(); } } } void UIDumpDomTree::DumpJsonById(UIView* view, const char* id, DumpMode mode) { if (searchFlag_) { return; } /* Check whether current view is the view we are looking for. */ if ((view->GetViewId() != nullptr) && !strcmp(view->GetViewId(), id)) { if (mode == DUMP_NODE) { /* Find the view with right id, output its info. */ OutputDomNode(view); } else if (mode == DUMP_TREE) { /* Find the view with right id, output its dom tree's info. */ OutputDomTree(view, rootJson_); } /* Set flag to stop the tranversion. */ searchFlag_ = true; } else { /* Look through all childrens of the current viewGroup. */ if (view->IsViewGroup()) { UIViewGroup* tmpViewGroup = static_cast(view); UIView* childView = tmpViewGroup->GetChildrenHead(); while (childView != nullptr) { DumpJsonById(childView, id, mode); childView = childView->GetNextSibling(); } } } } #endif // ENABLE_DEBUG char* UIDumpDomTree::DumpDomNode(const char* id) { #if ENABLE_DEBUG if (id == nullptr) { return nullptr; } /* Reset dump info */ pJson_ = nullptr; RootView* rootView = RootView::GetInstance(); UIView* currView = static_cast(rootView); /* Search through all views from rootView. */ DumpJsonById(currView, id, DUMP_NODE); /* Reset the search flag and pJson for next search */ searchFlag_ = false; return pJson_; #else return nullptr; #endif // ENABLE_DEBUG } bool UIDumpDomTree::DumpDomTree(const char* id, const char* path) { #if ENABLE_DEBUG path_ = (path == nullptr) ? DEFAULT_DUMP_DOM_TREE_PATH : path; RootView* rootView = RootView::GetInstance(); UIView* currView = static_cast(rootView); rootJson_ = cJSON_CreateObject(); if (rootJson_ == nullptr) { GRAPHIC_LOGE("UIDumpDomTree::DumpDomTree cJSON create object failed Err!\n"); return false; } if (id == nullptr) { OutputDomTree(currView, rootJson_); } else { DumpJsonById(currView, id, DUMP_TREE); if (!searchFlag_) { cJSON_Delete(rootJson_); rootJson_ = nullptr; GRAPHIC_LOGI("UIDumpDomTree::DumpDomTree can not find the node \n"); return false; } /* Reset the search flag. */ searchFlag_ = false; } pJson_ = cJSON_Print(rootJson_); cJSON_Delete(rootJson_); rootJson_ = nullptr; if (pJson_ == nullptr) { GRAPHIC_LOGE("UIDumpDomTree::DumpDomTree convert cJSON object to string failed Err!\n"); return false; } if (!WriteDumpFile()) { cJSON_free(pJson_); pJson_ = nullptr; GRAPHIC_LOGE("UIDumpDomTree::DumpDomTree file operation failed Err!\n"); return false; } cJSON_free(pJson_); pJson_ = nullptr; allocErrorFlag_ = false; return true; #else return false; #endif // ENABLE_DEBUG } } // namespace OHOS