blob: c08425265f75f60350736930cc6182cead0a91f7 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/paint/PaintPropertyTreePrinter.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/LocalFrameView.h"
#include "core/layout/LayoutEmbeddedContent.h"
#include "core/layout/LayoutView.h"
#include "core/paint/ObjectPaintProperties.h"
#include "platform/graphics/paint/PropertyTreeState.h"
#include <iomanip>
#include <sstream>
#if DCHECK_IS_ON()
namespace blink {
namespace {
template <typename PropertyTreeNode>
class PropertyTreePrinterTraits;
template <typename PropertyTreeNode>
class PropertyTreePrinter {
public:
String TreeAsString(const LocalFrameView& frame_view) {
CollectPropertyNodes(frame_view);
const PropertyTreeNode* root_node = LookupRootNode();
if (!root_node)
return "";
if (!node_to_debug_string_.Contains(root_node))
AddPropertyNode(root_node, "root");
StringBuilder string_builder;
AddAllPropertyNodes(string_builder, root_node);
return string_builder.ToString();
}
String PathAsString(const PropertyTreeNode* last_node) {
const PropertyTreeNode* node = last_node;
while (!node->IsRoot()) {
AddPropertyNode(node, "");
node = node->Parent();
}
AddPropertyNode(node, "root");
StringBuilder string_builder;
AddAllPropertyNodes(string_builder, node);
return string_builder.ToString();
}
void AddPropertyNode(const PropertyTreeNode* node, const String& debug_info) {
node_to_debug_string_.Set(node, debug_info);
}
void AddPropertyNode(const PropertyTreeNode* node,
const String& name,
const LayoutObject& object) {
node_to_debug_string_.Set(node, name + " (" + object.DebugName() + ")");
}
private:
using Traits = PropertyTreePrinterTraits<PropertyTreeNode>;
void CollectPropertyNodes(const LocalFrameView& frame_view) {
Traits::AddFrameViewProperties(frame_view, *this);
if (LayoutView* layout_view = frame_view.GetLayoutView())
CollectPropertyNodes(*layout_view);
for (Frame* child = frame_view.GetFrame().Tree().FirstChild(); child;
child = child->Tree().NextSibling()) {
if (!child->IsLocalFrame())
continue;
if (LocalFrameView* child_view = ToLocalFrame(child)->View())
CollectPropertyNodes(*child_view);
}
}
void CollectPropertyNodes(const LayoutObject& object) {
for (const auto* fragment = &object.FirstFragment(); fragment;
fragment = fragment->NextFragment()) {
if (const auto* properties = fragment->PaintProperties())
Traits::AddObjectPaintProperties(object, *properties, *this);
}
for (const auto* child = object.SlowFirstChild(); child;
child = child->NextSibling())
CollectPropertyNodes(*child);
}
void AddAllPropertyNodes(StringBuilder& string_builder,
const PropertyTreeNode* node,
unsigned indent = 0) {
DCHECK(node);
for (unsigned i = 0; i < indent; i++)
string_builder.Append(' ');
if (node_to_debug_string_.Contains(node))
string_builder.Append(node_to_debug_string_.at(node));
string_builder.Append(String::Format(" %p ", node));
string_builder.Append(node->ToString());
string_builder.Append("\n");
for (const auto* child_node : node_to_debug_string_.Keys()) {
if (child_node->Parent() == node)
AddAllPropertyNodes(string_builder, child_node, indent + 2);
}
}
// Root nodes may not be directly accessible but they can be determined by
// walking up to the parent of a previously collected node.
const PropertyTreeNode* LookupRootNode() {
for (const auto* node : node_to_debug_string_.Keys()) {
if (node->IsRoot())
return node;
if (node->Parent() && node->Parent()->IsRoot())
return node->Parent();
}
return nullptr;
}
HashMap<const PropertyTreeNode*, String> node_to_debug_string_;
};
template <>
class PropertyTreePrinterTraits<TransformPaintPropertyNode> {
public:
static void AddFrameViewProperties(
const LocalFrameView& frame_view,
PropertyTreePrinter<TransformPaintPropertyNode>& printer) {
if (const TransformPaintPropertyNode* pre_translation =
frame_view.PreTranslation())
printer.AddPropertyNode(pre_translation, "PreTranslation (FrameView)");
if (const TransformPaintPropertyNode* scroll_translation =
frame_view.ScrollTranslation())
printer.AddPropertyNode(scroll_translation,
"ScrollTranslation (FrameView)");
}
static void AddObjectPaintProperties(
const LayoutObject& object,
const ObjectPaintProperties& properties,
PropertyTreePrinter<TransformPaintPropertyNode>& printer) {
if (const auto* t = properties.PaintOffsetTranslation())
printer.AddPropertyNode(t, "PaintOffsetTranslation", object);
if (const auto* t = properties.Transform())
printer.AddPropertyNode(t, "Transform", object);
if (const auto* t = properties.Perspective())
printer.AddPropertyNode(t, "Perspective", object);
if (const auto* t = properties.SvgLocalToBorderBoxTransform())
printer.AddPropertyNode(t, "SvgLocalToBorderBoxTransform", object);
if (const auto* t = properties.ScrollTranslation())
printer.AddPropertyNode(t, "ScrollTranslation", object);
}
};
template <>
class PropertyTreePrinterTraits<ClipPaintPropertyNode> {
public:
static void AddFrameViewProperties(
const LocalFrameView& frame_view,
PropertyTreePrinter<ClipPaintPropertyNode>& printer) {
if (const ClipPaintPropertyNode* content_clip = frame_view.ContentClip())
printer.AddPropertyNode(content_clip, "ContentClip (FrameView)");
}
static void AddObjectPaintProperties(
const LayoutObject& object,
const ObjectPaintProperties& properties,
PropertyTreePrinter<ClipPaintPropertyNode>& printer) {
if (const auto* c = properties.FragmentClip())
printer.AddPropertyNode(c, "FragmentClip", object);
if (const auto* c = properties.MaskClip())
printer.AddPropertyNode(c, "MaskClip", object);
if (const auto* c = properties.CssClip())
printer.AddPropertyNode(c, "CssClip", object);
if (const auto* c = properties.CssClipFixedPosition())
printer.AddPropertyNode(c, "CssClipFixedPosition", object);
if (const auto* c = properties.OverflowControlsClip())
printer.AddPropertyNode(c, "OverflowControlsClip", object);
if (const auto* c = properties.InnerBorderRadiusClip())
printer.AddPropertyNode(c, "InnerBorderRadiusClip", object);
if (const auto* c = properties.OverflowClip())
printer.AddPropertyNode(c, "OverflowClip", object);
}
};
template <>
class PropertyTreePrinterTraits<EffectPaintPropertyNode> {
public:
static void AddFrameViewProperties(
const LocalFrameView& frame_view,
PropertyTreePrinter<EffectPaintPropertyNode>& printer) {}
static void AddObjectPaintProperties(
const LayoutObject& object,
const ObjectPaintProperties& properties,
PropertyTreePrinter<EffectPaintPropertyNode>& printer) {
if (const auto* e = properties.Effect())
printer.AddPropertyNode(e, "Effect", object);
if (const auto* e = properties.Filter())
printer.AddPropertyNode(e, "Filter", object);
if (const auto* e = properties.Mask())
printer.AddPropertyNode(e, "Mask", object);
}
};
template <>
class PropertyTreePrinterTraits<ScrollPaintPropertyNode> {
public:
static void AddFrameViewProperties(
const LocalFrameView& frame_view,
PropertyTreePrinter<ScrollPaintPropertyNode>& printer) {
if (const auto* s = frame_view.ScrollNode())
printer.AddPropertyNode(s, "Scroll (FrameView)");
}
static void AddObjectPaintProperties(
const LayoutObject& object,
const ObjectPaintProperties& properties,
PropertyTreePrinter<ScrollPaintPropertyNode>& printer) {
if (const auto* s = properties.Scroll())
printer.AddPropertyNode(s, "Scroll", object);
}
};
class PaintPropertyTreeGraphBuilder {
public:
PaintPropertyTreeGraphBuilder() = default;
void GenerateTreeGraph(const LocalFrameView& frame_view,
StringBuilder& string_builder) {
layout_.str("");
properties_.str("");
owner_edges_.str("");
properties_ << std::setprecision(2)
<< std::setiosflags(std::ios_base::fixed);
WriteFrameViewNode(frame_view, nullptr);
string_builder.Append("digraph {\n");
string_builder.Append("graph [rankdir=BT];\n");
string_builder.Append("subgraph cluster_layout {\n");
std::string layout_str = layout_.str();
string_builder.Append(layout_str.c_str(), layout_str.length());
string_builder.Append("}\n");
string_builder.Append("subgraph cluster_properties {\n");
std::string properties_str = properties_.str();
string_builder.Append(properties_str.c_str(), properties_str.length());
string_builder.Append("}\n");
std::string owners_str = owner_edges_.str();
string_builder.Append(owners_str.c_str(), owners_str.length());
string_builder.Append("}\n");
}
private:
static String GetTagName(Node* n) {
if (n->IsDocumentNode())
return "";
if (n->getNodeType() == Node::kCommentNode)
return "COMMENT";
return n->nodeName();
}
static void WriteParentEdge(const void* node,
const void* parent,
const char* color,
std::ostream& os) {
os << "n" << node << " -> "
<< "n" << parent;
if (color)
os << " [color=" << color << "]";
os << ";" << std::endl;
}
void WriteOwnerEdge(const void* node, const void* owner) {
std::ostream& os = owner_edges_;
os << "n" << owner << " -> "
<< "n" << node << " [color=" << layout_node_color_
<< ", arrowhead=dot, constraint=false];" << std::endl;
}
void WriteTransformEdge(const void* node, const void* transform) {
std::ostream& os = properties_;
os << "n" << node << " -> "
<< "n" << transform << " [color=" << transform_node_color_ << "];"
<< std::endl;
}
void WritePaintPropertyNode(const TransformPaintPropertyNode& node,
const void* owner,
const char* label) {
std::ostream& os = properties_;
os << "n" << &node << " [color=" << transform_node_color_
<< ", fontcolor=" << transform_node_color_ << ", shape=box, label=\""
<< label << "\\n";
const FloatPoint3D& origin = node.Origin();
os << "origin=(";
os << origin.X() << "," << origin.Y() << "," << origin.Z() << ")\\n";
os << "flattensInheritedTransform="
<< (node.FlattensInheritedTransform() ? "true" : "false") << "\\n";
os << "renderingContextId=" << node.RenderingContextId() << "\\n";
const TransformationMatrix& matrix = node.Matrix();
os << "[" << std::setw(8) << matrix.M11() << "," << std::setw(8)
<< matrix.M12() << "," << std::setw(8) << matrix.M13() << ","
<< std::setw(8) << matrix.M14() << "\\n";
os << std::setw(9) << matrix.M21() << "," << std::setw(8) << matrix.M22()
<< "," << std::setw(8) << matrix.M23() << "," << std::setw(8)
<< matrix.M24() << "\\n";
os << std::setw(9) << matrix.M31() << "," << std::setw(8) << matrix.M32()
<< "," << std::setw(8) << matrix.M33() << "," << std::setw(8)
<< matrix.M34() << "\\n";
os << std::setw(9) << matrix.M41() << "," << std::setw(8) << matrix.M42()
<< "," << std::setw(8) << matrix.M43() << "," << std::setw(8)
<< matrix.M44() << "]";
TransformationMatrix::DecomposedType decomposition;
if (!node.Matrix().Decompose(decomposition))
os << "\\n(degenerate)";
os << "\"];" << std::endl;
if (owner)
WriteOwnerEdge(&node, owner);
if (node.Parent())
WriteParentEdge(&node, node.Parent(), transform_node_color_, os);
}
void WritePaintPropertyNode(const ClipPaintPropertyNode& node,
const void* owner,
const char* label) {
std::ostream& os = properties_;
os << "n" << &node << " [color=" << clip_node_color_
<< ", fontcolor=" << clip_node_color_ << ", shape=box, label=\"" << label
<< "\\n";
LayoutRect rect(node.ClipRect().Rect());
if (IntRect(rect) == LayoutRect::InfiniteIntRect())
os << "(infinite)";
else
os << "(" << rect.X() << ", " << rect.Y() << ", " << rect.Width() << ", "
<< rect.Height() << ")";
os << "\"];" << std::endl;
if (owner)
WriteOwnerEdge(&node, owner);
if (node.Parent())
WriteParentEdge(&node, node.Parent(), clip_node_color_, os);
if (node.LocalTransformSpace())
WriteTransformEdge(&node, node.LocalTransformSpace());
}
void WritePaintPropertyNode(const EffectPaintPropertyNode& node,
const void* owner,
const char* label) {
std::ostream& os = properties_;
os << "n" << &node << " [shape=diamond, label=\"" << label << "\\n";
os << "opacity=" << node.Opacity() << "\"];" << std::endl;
if (owner)
WriteOwnerEdge(&node, owner);
if (node.Parent())
WriteParentEdge(&node, node.Parent(), effect_node_color_, os);
}
void WritePaintPropertyNode(const ScrollPaintPropertyNode&,
const void*,
const char*) {
// TODO(pdr): fill this out.
}
void WriteObjectPaintPropertyNodes(const LayoutObject& object) {
const ObjectPaintProperties* properties =
object.FirstFragment().PaintProperties();
if (!properties)
return;
const TransformPaintPropertyNode* paint_offset =
properties->PaintOffsetTranslation();
if (paint_offset)
WritePaintPropertyNode(*paint_offset, &object, "paintOffset");
const TransformPaintPropertyNode* transform = properties->Transform();
if (transform)
WritePaintPropertyNode(*transform, &object, "transform");
const TransformPaintPropertyNode* perspective = properties->Perspective();
if (perspective)
WritePaintPropertyNode(*perspective, &object, "perspective");
const TransformPaintPropertyNode* svg_local_to_border_box =
properties->SvgLocalToBorderBoxTransform();
if (svg_local_to_border_box)
WritePaintPropertyNode(*svg_local_to_border_box, &object,
"svgLocalToBorderBox");
const TransformPaintPropertyNode* scroll_translation =
properties->ScrollTranslation();
if (scroll_translation)
WritePaintPropertyNode(*scroll_translation, &object, "scrollTranslation");
const EffectPaintPropertyNode* effect = properties->Effect();
if (effect)
WritePaintPropertyNode(*effect, &object, "effect");
const ClipPaintPropertyNode* css_clip = properties->CssClip();
if (css_clip)
WritePaintPropertyNode(*css_clip, &object, "cssClip");
const ClipPaintPropertyNode* css_clip_fixed_position =
properties->CssClipFixedPosition();
if (css_clip_fixed_position)
WritePaintPropertyNode(*css_clip_fixed_position, &object,
"cssClipFixedPosition");
const ClipPaintPropertyNode* overflow_clip = properties->OverflowClip();
if (overflow_clip) {
if (const ClipPaintPropertyNode* inner_border_radius_clip =
properties->InnerBorderRadiusClip())
WritePaintPropertyNode(*inner_border_radius_clip, &object,
"innerBorderRadiusClip");
WritePaintPropertyNode(*overflow_clip, &object, "overflowClip");
if (object.IsLayoutView() && overflow_clip->Parent())
WritePaintPropertyNode(*overflow_clip->Parent(), nullptr, "rootClip");
}
const auto* scroll =
scroll_translation ? scroll_translation->ScrollNode() : nullptr;
if (scroll)
WritePaintPropertyNode(*scroll, &object, "scroll");
}
template <typename PropertyTreeNode>
static const PropertyTreeNode* GetRoot(const PropertyTreeNode* node) {
while (node && !node->IsRoot())
node = node->Parent();
return node;
}
void WriteFrameViewPaintPropertyNodes(const LocalFrameView& frame_view) {
if (const auto* contents_state =
frame_view.TotalPropertyTreeStateForContents()) {
if (const auto* root = GetRoot(contents_state->Transform()))
WritePaintPropertyNode(*root, &frame_view, "rootTransform");
if (const auto* root = GetRoot(contents_state->Clip()))
WritePaintPropertyNode(*root, &frame_view, "rootClip");
if (const auto* root = GetRoot(contents_state->Effect()))
WritePaintPropertyNode(*root, &frame_view, "rootEffect");
}
TransformPaintPropertyNode* pre_translation = frame_view.PreTranslation();
if (pre_translation)
WritePaintPropertyNode(*pre_translation, &frame_view, "preTranslation");
TransformPaintPropertyNode* scroll_translation =
frame_view.ScrollTranslation();
if (scroll_translation)
WritePaintPropertyNode(*scroll_translation, &frame_view,
"scrollTranslation");
ClipPaintPropertyNode* content_clip = frame_view.ContentClip();
if (content_clip)
WritePaintPropertyNode(*content_clip, &frame_view, "contentClip");
const auto* scroll =
scroll_translation ? scroll_translation->ScrollNode() : nullptr;
if (scroll)
WritePaintPropertyNode(*scroll, &frame_view, "scroll");
}
void WriteLayoutObjectNode(const LayoutObject& object) {
std::ostream& os = layout_;
os << "n" << &object << " [color=" << layout_node_color_
<< ", fontcolor=" << layout_node_color_ << ", label=\""
<< object.GetName();
Node* node = object.GetNode();
if (node) {
os << "\\n" << GetTagName(node).Utf8().data();
if (node->IsElementNode() && ToElement(node)->HasID())
os << "\\nid=" << ToElement(node)->GetIdAttribute().Utf8().data();
}
os << "\"];" << std::endl;
const void* parent = object.IsLayoutView()
? (const void*)ToLayoutView(object).GetFrameView()
: (const void*)object.Parent();
WriteParentEdge(&object, parent, layout_node_color_, os);
WriteObjectPaintPropertyNodes(object);
for (const LayoutObject* child = object.SlowFirstChild(); child;
child = child->NextSibling())
WriteLayoutObjectNode(*child);
if (object.IsLayoutEmbeddedContent()) {
LocalFrameView* frame_view =
ToLayoutEmbeddedContent(object).ChildFrameView();
if (frame_view)
WriteFrameViewNode(*frame_view, &object);
}
}
void WriteFrameViewNode(const LocalFrameView& frame_view,
const void* parent) {
std::ostream& os = layout_;
os << "n" << &frame_view << " [color=" << layout_node_color_
<< ", fontcolor=" << layout_node_color_ << ", shape=doublecircle"
<< ", label=FrameView];" << std::endl;
WriteFrameViewPaintPropertyNodes(frame_view);
if (parent)
WriteParentEdge(&frame_view, parent, layout_node_color_, os);
LayoutView* layout_view = frame_view.GetLayoutView();
if (layout_view)
WriteLayoutObjectNode(*layout_view);
}
private:
static const char* layout_node_color_;
static const char* transform_node_color_;
static const char* clip_node_color_;
static const char* effect_node_color_;
std::ostringstream layout_;
std::ostringstream properties_;
std::ostringstream owner_edges_;
};
const char* PaintPropertyTreeGraphBuilder::layout_node_color_ = "black";
const char* PaintPropertyTreeGraphBuilder::transform_node_color_ = "green";
const char* PaintPropertyTreeGraphBuilder::clip_node_color_ = "blue";
const char* PaintPropertyTreeGraphBuilder::effect_node_color_ = "black";
} // namespace {
} // namespace blink
CORE_EXPORT void showAllPropertyTrees(const blink::LocalFrameView& rootFrame) {
showTransformPropertyTree(rootFrame);
showClipPropertyTree(rootFrame);
showEffectPropertyTree(rootFrame);
showScrollPropertyTree(rootFrame);
}
void showTransformPropertyTree(const blink::LocalFrameView& rootFrame) {
LOG(ERROR) << "Transform tree:\n"
<< transformPropertyTreeAsString(rootFrame).Utf8().data();
}
void showClipPropertyTree(const blink::LocalFrameView& rootFrame) {
LOG(ERROR) << "Clip tree:\n"
<< clipPropertyTreeAsString(rootFrame).Utf8().data();
}
void showEffectPropertyTree(const blink::LocalFrameView& rootFrame) {
LOG(ERROR) << "Effect tree:\n"
<< effectPropertyTreeAsString(rootFrame).Utf8().data();
}
void showScrollPropertyTree(const blink::LocalFrameView& rootFrame) {
LOG(ERROR) << "Scroll tree:\n"
<< scrollPropertyTreeAsString(rootFrame).Utf8().data();
}
String transformPropertyTreeAsString(const blink::LocalFrameView& rootFrame) {
return blink::PropertyTreePrinter<blink::TransformPaintPropertyNode>()
.TreeAsString(rootFrame);
}
String clipPropertyTreeAsString(const blink::LocalFrameView& rootFrame) {
return blink::PropertyTreePrinter<blink::ClipPaintPropertyNode>()
.TreeAsString(rootFrame);
}
String effectPropertyTreeAsString(const blink::LocalFrameView& rootFrame) {
return blink::PropertyTreePrinter<blink::EffectPaintPropertyNode>()
.TreeAsString(rootFrame);
}
String scrollPropertyTreeAsString(const blink::LocalFrameView& rootFrame) {
return blink::PropertyTreePrinter<blink::ScrollPaintPropertyNode>()
.TreeAsString(rootFrame);
}
String paintPropertyTreeGraph(const blink::LocalFrameView& frameView) {
blink::PaintPropertyTreeGraphBuilder builder;
StringBuilder stringBuilder;
builder.GenerateTreeGraph(frameView, stringBuilder);
return stringBuilder.ToString();
}
#endif // DCHECK_IS_ON()