| Index: third_party/WebKit/Source/core/editing/LayoutTreePrinter.cpp
|
| diff --git a/third_party/WebKit/Source/core/editing/LayoutTreePrinter.cpp b/third_party/WebKit/Source/core/editing/LayoutTreePrinter.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..61d276ff8abf92fb086b5aee129ac5d2852baad9
|
| --- /dev/null
|
| +++ b/third_party/WebKit/Source/core/editing/LayoutTreePrinter.cpp
|
| @@ -0,0 +1,395 @@
|
| +// 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/editing/LayoutTreePrinter.h"
|
| +
|
| +#include "core/editing/LayoutPosition.h"
|
| +#include "core/frame/FrameView.h"
|
| +#include "core/layout/LayoutBlock.h"
|
| +#include "core/layout/LayoutBox.h"
|
| +#include "core/layout/LayoutObject.h"
|
| +#include "core/layout/LayoutView.h"
|
| +#include "core/layout/api/LineLayoutAPIShim.h"
|
| +#include "core/layout/api/LineLayoutItem.h"
|
| +#include "core/layout/line/InlineTextBox.h"
|
| +#include "core/layout/line/LineBoxList.h"
|
| +#include "wtf/Allocator.h"
|
| +#include "wtf/Assertions.h"
|
| +#include "wtf/HexNumber.h"
|
| +#include "wtf/Noncopyable.h"
|
| +#include "wtf/text/StringBuilder.h"
|
| +
|
| +#ifndef NDEBUG
|
| +#include <iostream> // NOLINT
|
| +
|
| +namespace blink {
|
| +
|
| +namespace {
|
| +struct LayoutTreeAsText {
|
| + const InlineBox* inlineBox;
|
| + const LayoutObject* layoutObject;
|
| +
|
| + explicit LayoutTreeAsText(const InlineBox& passedInlineBox)
|
| + : inlineBox(&passedInlineBox)
|
| + , layoutObject(nullptr)
|
| + {
|
| + }
|
| +
|
| + explicit LayoutTreeAsText(const LayoutObject& passedLayoutObject)
|
| + : inlineBox(nullptr)
|
| + , layoutObject(&passedLayoutObject)
|
| + {
|
| + }
|
| +};
|
| +} // namespace
|
| +
|
| +class LayoutTreePrinter {
|
| + STACK_ALLOCATED();
|
| + WTF_MAKE_NONCOPYABLE(LayoutTreePrinter);
|
| +public:
|
| + LayoutTreePrinter(std::ostream*, const LayoutTreeAsText&);
|
| +
|
| + static void print(const InlineBox*);
|
| + static void print(const LayoutObject*);
|
| + static void print(const LayoutPosition&);
|
| + void print();
|
| +
|
| +private:
|
| + class ScopedIndent {
|
| + STACK_ALLOCATED();
|
| + WTF_MAKE_NONCOPYABLE(ScopedIndent);
|
| + public:
|
| + ScopedIndent(LayoutTreePrinter* Context, int amount)
|
| + : m_amount(amount)
|
| + , m_context(Context)
|
| + {
|
| + DCHECK_GE(m_amount, 1);
|
| + m_context->m_indentAmount += m_amount;
|
| + }
|
| +
|
| + ~ScopedIndent()
|
| + {
|
| + DCHECK_GE(m_context->m_indentAmount, m_amount);
|
| + m_context->m_indentAmount -= m_amount;
|
| + }
|
| +
|
| + private:
|
| + const int m_amount;
|
| + LayoutTreePrinter* const m_context;
|
| + };
|
| +
|
| + void arrow() const;
|
| + void indent() const;
|
| + void visitInlineFlowBox(const InlineFlowBox&);
|
| + void visitLayoutObjct(const LayoutObject&);
|
| +
|
| + int m_indentAmount = 0;
|
| + const InlineBox* const m_markedInlineBox;
|
| + const LayoutObject* const m_markedLayoutObject;
|
| + std::ostream* const m_stream;
|
| +};
|
| +
|
| +namespace {
|
| +
|
| +struct CodeUnitAsText {
|
| + UChar codeUnit;
|
| + explicit CodeUnitAsText(UChar passedCodeUnit)
|
| + : codeUnit(passedCodeUnit)
|
| + {
|
| + }
|
| +};
|
| +
|
| +struct InlineBoxAsText {
|
| + const InlineBox* inlineBox;
|
| +
|
| + explicit InlineBoxAsText(const InlineBox& passedInlineBox)
|
| + : inlineBox(&passedInlineBox)
|
| + {
|
| + }
|
| +};
|
| +
|
| +struct LayoutObjectAsText {
|
| + const LayoutObject* layoutObject;
|
| +
|
| + explicit LayoutObjectAsText(const LayoutObject& passedLayoutObject)
|
| + : layoutObject(&passedLayoutObject)
|
| + {
|
| + }
|
| +};
|
| +
|
| +struct StringAsText {
|
| + const String* string;
|
| +
|
| + explicit StringAsText(const String& passedString)
|
| + : string(&passedString)
|
| + {
|
| + }
|
| +};
|
| +
|
| +struct NodeAsText {
|
| + const Node* node;
|
| +
|
| + explicit NodeAsText(const Node& passedNode)
|
| + : node(&passedNode)
|
| + {
|
| + }
|
| +};
|
| +
|
| +struct LayoutRectAsText {
|
| + const LayoutRect* layoutRect;
|
| +
|
| + explicit LayoutRectAsText(const LayoutRect& passedLayoutRect)
|
| + : layoutRect(&passedLayoutRect)
|
| + {
|
| + }
|
| +};
|
| +
|
| +// -- Printers
|
| +std::ostream& operator<<(std::ostream&, const CodeUnitAsText&);
|
| +std::ostream& operator<<(std::ostream&, const InlineBoxAsText&);
|
| +std::ostream& operator<<(std::ostream&, const LayoutObjectAsText&);
|
| +std::ostream& operator<<(std::ostream&, const LayoutRectAsText&);
|
| +std::ostream& operator<<(std::ostream&, const LayoutTreeAsText&);
|
| +std::ostream& operator<<(std::ostream&, const NodeAsText&);
|
| +std::ostream& operator<<(std::ostream&, const StringAsText&);
|
| +
|
| +std::ostream& operator<<(std::ostream& ostream, const CodeUnitAsText& wrapper)
|
| +{
|
| + const UChar codeUnit = wrapper.codeUnit;
|
| + if (codeUnit == '\n')
|
| + return ostream << "\\n";
|
| + if (codeUnit == '\t')
|
| + return ostream << "\\t";
|
| + if (codeUnit == '"')
|
| + return ostream << "\\\"";
|
| + if (codeUnit == '\\')
|
| + return ostream << "\\\\";
|
| + if (codeUnit >= ' ' && codeUnit <= 0x7E)
|
| + return ostream << static_cast<char>(codeUnit);
|
| + StringBuilder builder;
|
| + appendUnsignedAsHexFixedSize(codeUnit, builder, 4);
|
| + return ostream << "\\u" << builder.toString().utf8().data();
|
| +}
|
| +
|
| +std::ostream& operator<<(std::ostream& ostream, const LayoutRectAsText& wrapper)
|
| +{
|
| + const LayoutRect& rect = *wrapper.layoutRect;
|
| + return ostream << '(' << rect.x().toFloat() << ',' << rect.y().toFloat() << ")+" << rect.width().toFloat() << 'x' << rect.height().toFloat();
|
| +}
|
| +
|
| +std::ostream& operator<<(std::ostream& ostream, const InlineBoxAsText& wrapper)
|
| +{
|
| + const InlineBox& inlineBox = *wrapper.inlineBox;
|
| + ostream << static_cast<const void*>(&inlineBox) << ' ' << inlineBox.boxName();
|
| + if (Node* node = inlineBox.getLineLayoutItem().node()) {
|
| + if (!node->isTextNode())
|
| + ostream << ' ' << node->debugName().utf8().data();
|
| + } else {
|
| + ostream << " (anonymous)";
|
| + }
|
| + if (inlineBox.isInlineTextBox()) {
|
| + const InlineTextBox& textBox = toInlineTextBox(inlineBox);
|
| + ostream << " " << StringAsText(textBox.text()) << " start=" << textBox.start() << " end=" << textBox.end();
|
| + } else if (inlineBox.isInlineFlowBox()) {
|
| + ostream << " frameRect=" << LayoutRectAsText(toInlineFlowBox(inlineBox).frameRect());
|
| + }
|
| + return ostream << " bidiLevel=" << static_cast<int>(inlineBox.bidiLevel());
|
| +}
|
| +
|
| +std::ostream& operator<<(std::ostream& ostream, const LayoutObjectAsText& wrapper)
|
| +{
|
| + const LayoutObject& layoutObject = *wrapper.layoutObject;
|
| + ostream << static_cast<const void*>(&layoutObject) << ' ' << layoutObject.name();
|
| + if (Node* node = layoutObject.node()) {
|
| + ostream << " {" << NodeAsText(*node) << '}';
|
| + if (node->hasEditableStyle())
|
| + ostream << " (editable)";
|
| + } else {
|
| + ostream << " (anonymous)";
|
| + }
|
| + if (layoutObject.isBox())
|
| + ostream << ' ' << LayoutRectAsText(toLayoutBox(layoutObject).frameRect());
|
| + return ostream;
|
| +}
|
| +
|
| +std::ostream& operator<<(std::ostream& ostream, const LayoutTreeAsText& wrapper)
|
| +{
|
| + LayoutTreePrinter(&ostream, wrapper).print();
|
| + return ostream;
|
| +}
|
| +
|
| +std::ostream& operator<<(std::ostream& ostream, const NodeAsText& wrapper)
|
| +{
|
| + const Node& node = *wrapper.node;
|
| + if (node.isTextNode())
|
| + ostream << StringAsText(toText(node).data());
|
| + else
|
| + ostream << node.debugName().utf8().data();
|
| + return ostream;
|
| +}
|
| +
|
| +std::ostream& operator<<(std::ostream& ostream, const StringAsText& wrapper)
|
| +{
|
| + const String& string = *wrapper.string;
|
| + const int kMaxChars = 40;
|
| + ostream << '"';
|
| + if (string.is8Bit()) {
|
| + const LChar* const start = string.characters8();
|
| + const LChar* const end = start + string.length();
|
| + const LChar* const middle = std::min(start + kMaxChars / 2, end);
|
| + for (const LChar* runner = start; runner < middle; ++runner)
|
| + ostream << CodeUnitAsText(*runner);
|
| + if (end != middle)
|
| + ostream << " ... ";
|
| + for (const LChar* runner = middle; runner < end; ++runner)
|
| + ostream << CodeUnitAsText(*runner);
|
| + } else {
|
| + const UChar* const start = string.characters16();
|
| + const UChar* const end = start + string.length();
|
| + const UChar* const middle = std::min(start + kMaxChars / 2, end);
|
| + for (const UChar* runner = start; runner < middle; ++runner)
|
| + ostream << CodeUnitAsText(*runner);
|
| + if (end != middle)
|
| + ostream << " ... ";
|
| + for (const UChar* runner = middle; runner < end; ++runner)
|
| + ostream << CodeUnitAsText(*runner);
|
| + }
|
| + return ostream << '"';
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// -- LayoutTreePrinter
|
| +LayoutTreePrinter::LayoutTreePrinter(std::ostream* stream, const LayoutTreeAsText& wrapper)
|
| + : m_markedInlineBox(wrapper.inlineBox)
|
| + , m_markedLayoutObject(wrapper.layoutObject)
|
| + , m_stream(stream)
|
| +{
|
| +}
|
| +
|
| +void LayoutTreePrinter::arrow() const
|
| +{
|
| + for (int index = 0; index < m_indentAmount; ++index)
|
| + *m_stream << "---";
|
| + *m_stream << '>';
|
| +}
|
| +
|
| +void LayoutTreePrinter::print()
|
| +{
|
| + if (m_markedInlineBox) {
|
| + visitLayoutObjct(*m_markedInlineBox->getLineLayoutItem().document().layoutView());
|
| + return;
|
| + }
|
| + visitLayoutObjct(*m_markedLayoutObject->document().layoutView());
|
| +}
|
| +
|
| +void LayoutTreePrinter::indent() const
|
| +{
|
| + for (int index = 0; index < m_indentAmount; ++index)
|
| + *m_stream << "| ";
|
| + *m_stream << '+';
|
| +}
|
| +
|
| +void LayoutTreePrinter::visitInlineFlowBox(const InlineFlowBox& inlineFlowBox)
|
| +{
|
| + if (m_markedInlineBox == &inlineFlowBox)
|
| + arrow();
|
| + else
|
| + indent();
|
| + *m_stream << '-' << InlineBoxAsText(inlineFlowBox) << std::endl;
|
| + ScopedIndent lineScope(this, 1);
|
| + for (const InlineBox* child = inlineFlowBox.firstChild(); child; child = child->nextOnLine()) {
|
| + if (m_markedInlineBox == child)
|
| + arrow();
|
| + else
|
| + indent();
|
| + *m_stream << InlineBoxAsText(*child) << std::endl;
|
| + if (child->isLeaf())
|
| + continue;
|
| + visitInlineFlowBox(*toInlineFlowBox(child));
|
| + }
|
| +}
|
| +
|
| +void LayoutTreePrinter::visitLayoutObjct(const LayoutObject& layoutObject)
|
| +{
|
| + if (m_markedLayoutObject == &layoutObject)
|
| + arrow();
|
| + else
|
| + indent();
|
| + *m_stream << LayoutObjectAsText(layoutObject) << std::endl;
|
| + ScopedIndent childrenScope(this, 1);
|
| + if (layoutObject.isLayoutBlock()) {
|
| + ScopedIndent linesScope(this, 2);
|
| + const auto& lines = toLayoutBlock(layoutObject).lineBoxes();
|
| + for (const InlineFlowBox* lineBox = lines.firstLineBox(); lineBox; lineBox = lineBox->nextLineBox())
|
| + visitInlineFlowBox(*lineBox);
|
| + }
|
| + for (const LayoutObject* child = layoutObject.slowFirstChild(); child; child = child->nextSibling())
|
| + visitLayoutObjct(*child);
|
| +}
|
| +
|
| +void LayoutTreePrinter::print(const InlineBox* inlineBox)
|
| +{
|
| + if (!inlineBox) {
|
| + VLOG(0) << "(null)";
|
| + return;
|
| + }
|
| + VLOG(0) << "Layout tree dump of " << static_cast<const void*>(inlineBox) << std::endl << LayoutTreeAsText(*inlineBox);
|
| +}
|
| +
|
| +void LayoutTreePrinter::print(const LayoutObject* layoutObject)
|
| +{
|
| + if (!layoutObject) {
|
| + VLOG(0) << "(null)";
|
| + return;
|
| + }
|
| + VLOG(0) << "Layout tree dump of " << static_cast<const void*>(layoutObject) << std::endl << LayoutTreeAsText(*layoutObject);
|
| +}
|
| +
|
| +void LayoutTreePrinter::print(const LayoutPosition& position)
|
| +{
|
| + if (position.isNull()) {
|
| + VLOG(0) << "(null)";
|
| + return;
|
| + }
|
| + if (const InlineBox* inlineBox = position.m_inlineBox) {
|
| + VLOG(0) << "Layout tree dump of " << position << std::endl << LayoutTreeAsText(*inlineBox);
|
| + return;
|
| + }
|
| + VLOG(0) << "Layout tree dump of " << position << std::endl << LayoutTreeAsText(*position.m_layoutObject);
|
| +}
|
| +
|
| +} // namespace blink
|
| +
|
| +void dumpLayoutTree(const blink::InlineBox* inlineBox)
|
| +{
|
| + blink::LayoutTreePrinter::print(inlineBox);
|
| +}
|
| +
|
| +void dumpLayoutTree(const blink::LayoutObject* layoutObject)
|
| +{
|
| + blink::LayoutTreePrinter::print(layoutObject);
|
| +}
|
| +
|
| +void dumpLayoutTree(const blink::LayoutPosition& position)
|
| +{
|
| + blink::LayoutTreePrinter::print(position);
|
| +}
|
| +
|
| +void dumpLayoutTree(const blink::Node* node)
|
| +{
|
| + dumpLayoutTree(node->layoutObject());
|
| +}
|
| +
|
| +void dumpLayoutTree(const blink::Position& position)
|
| +{
|
| + dumpLayoutTree(position.anchorNode());
|
| +}
|
| +
|
| +void dumpLayoutTree(const blink::VisiblePosition& position)
|
| +{
|
| + dumpLayoutTree(position.deepEquivalent());
|
| +}
|
| +
|
| +#endif
|
|
|