blob: 388ea9a0e6813e2eb2e25b7de420c2f0bf37ccd3 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/debug/debugger_utils.h"
#include <inttypes.h>
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
namespace views::debug {
namespace {
using AttributeStrings = std::vector<std::string>;
constexpr int kElementIndent = 2;
constexpr int kAttributeIndent = 4;
std::string ToString(bool val) {
return val ? "true" : "false";
}
std::string ToString(int val) {
return base::NumberToString(val);
}
std::string ToString(ViewDebugWrapper::BoundsTuple bounds) {
return base::StringPrintf("%d %d %dx%d", std::get<0>(bounds),
std::get<1>(bounds), std::get<2>(bounds),
std::get<3>(bounds));
}
// intptr_t can alias to int, preventing the use of overloading for ToString.
std::string PtrToString(intptr_t val) {
return base::StringPrintf("0x%" PRIxPTR, val);
}
// Adds attribute string of the form <attribute_name>="<attribute_value>".
template <typename T>
void AddAttributeString(AttributeStrings& attributes,
const std::string& name,
const T& value) {
attributes.push_back(name + "=\"" + ToString(value) + "\"");
}
void AddPtrAttributeString(AttributeStrings& attributes,
const std::string& name,
const absl::optional<intptr_t>& value) {
if (!value)
return;
attributes.push_back(name + "=\"" + PtrToString(value.value()) + "\"");
}
AttributeStrings GetAttributeStrings(ViewDebugWrapper* view, bool verbose) {
AttributeStrings attributes;
if (verbose) {
view->ForAllProperties(base::BindRepeating(
[](AttributeStrings* attributes, const std::string& name,
const std::string& val) {
attributes->push_back(name + "=\"" + val + "\"");
},
base::Unretained(&attributes)));
} else {
AddPtrAttributeString(attributes, "address", view->GetAddress());
AddAttributeString(attributes, "bounds", view->GetBounds());
AddAttributeString(attributes, "enabled", view->GetNeedsLayout());
AddAttributeString(attributes, "id", view->GetID());
AddAttributeString(attributes, "needs-layout", view->GetNeedsLayout());
AddAttributeString(attributes, "visible", view->GetVisible());
}
return attributes;
}
std::string GetPaddedLine(int current_depth, bool attribute_line = false) {
const int padding = attribute_line
? current_depth * kElementIndent + kAttributeIndent
: current_depth * kElementIndent;
return std::string(padding, ' ');
}
void PrintViewHierarchyImpl(std::ostream* out,
ViewDebugWrapper* view,
int current_depth,
bool verbose,
int target_depth,
size_t column_limit) {
std::string line = GetPaddedLine(current_depth);
line += "<" + view->GetViewClassName();
for (const std::string& attribute_string :
GetAttributeStrings(view, verbose)) {
if (line.size() + attribute_string.size() + 1 > column_limit) {
// If adding the attribute string would cause the line to exceed the
// column limit, send the line to `out` and start a new line. The new line
// should fit at least one attribute string even if it means exceeding the
// column limit.
*out << line << "\n";
line = GetPaddedLine(current_depth, true) + attribute_string;
} else {
// Keep attribute strings on the existing line if it fits within the
// column limit.
line += " " + attribute_string;
}
}
// Print children only if they exist and we are not yet at our target tree
// depth.
if (!view->GetChildren().empty() &&
(target_depth == -1 || current_depth < target_depth)) {
*out << line << ">\n";
for (ViewDebugWrapper* child : view->GetChildren()) {
PrintViewHierarchyImpl(out, child, current_depth + 1, verbose,
target_depth, column_limit);
}
line = GetPaddedLine(current_depth);
*out << line << "</" << view->GetViewClassName() << ">\n";
} else {
// If no children are to be printed use a self closing tag to terminate the
// View element.
*out << line << " />\n";
}
}
} // namespace
absl::optional<intptr_t> ViewDebugWrapper::GetAddress() {
return absl::nullopt;
}
void PrintViewHierarchy(std::ostream* out,
ViewDebugWrapper* view,
bool verbose,
int depth,
size_t column_limit) {
PrintViewHierarchyImpl(out, view, 0, verbose, depth, column_limit);
}
} // namespace views::debug