[LFC][IFC] Add inline runs to showLayoutTree
[WebKit-https.git] / Source / WebCore / layout / layouttree / LayoutTreeBuilder.cpp
1 /*
2  * Copyright (C) 2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "LayoutTreeBuilder.h"
28
29 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
30
31 #include "DisplayBox.h"
32 #include "InlineFormattingState.h"
33 #include "LayoutBlockContainer.h"
34 #include "LayoutBox.h"
35 #include "LayoutChildIterator.h"
36 #include "LayoutContainer.h"
37 #include "LayoutContext.h"
38 #include "LayoutInlineBox.h"
39 #include "LayoutInlineContainer.h"
40 #include "RenderBlock.h"
41 #include "RenderChildIterator.h"
42 #include "RenderElement.h"
43 #include "RenderInline.h"
44 #include "RenderStyle.h"
45 #include "RenderView.h"
46 #include <wtf/text/TextStream.h>
47
48 namespace WebCore {
49 namespace Layout {
50
51 std::unique_ptr<Container> TreeBuilder::createLayoutTree(const RenderView& renderView)
52 {
53     std::unique_ptr<Container> initialContainingBlock(new BlockContainer(std::nullopt, RenderStyle::clone(renderView.style())));
54     TreeBuilder::createSubTree(renderView, *initialContainingBlock);
55     return initialContainingBlock;
56 }
57
58 void TreeBuilder::createSubTree(const RenderElement& rootRenderer, Container& rootContainer)
59 {
60     auto elementAttributes = [] (const RenderElement& renderer) -> std::optional<Box::ElementAttributes> {
61         if (renderer.isDocumentElementRenderer())
62             return Box::ElementAttributes { Box::ElementType::Document };
63         if (auto* element = renderer.element()) {
64             if (element->hasTagName(HTMLNames::bodyTag))
65                 return Box::ElementAttributes { Box::ElementType::Body };
66             if (element->hasTagName(HTMLNames::colTag))
67                 return Box::ElementAttributes { Box::ElementType::TableColumn };
68             if (element->hasTagName(HTMLNames::trTag))
69                 return Box::ElementAttributes { Box::ElementType::TableRow };
70             if (element->hasTagName(HTMLNames::colgroupTag))
71                 return Box::ElementAttributes { Box::ElementType::TableColumnGroup };
72             if (element->hasTagName(HTMLNames::tbodyTag))
73                 return Box::ElementAttributes { Box::ElementType::TableRowGroup };
74             if (element->hasTagName(HTMLNames::theadTag))
75                 return Box::ElementAttributes { Box::ElementType::TableHeaderGroup };
76             if (element->hasTagName(HTMLNames::tfootTag))
77                 return Box::ElementAttributes { Box::ElementType::TableFooterGroup };
78             if (element->hasTagName(HTMLNames::tfootTag))
79                 return Box::ElementAttributes { Box::ElementType::TableFooterGroup };
80             return Box::ElementAttributes { Box::ElementType::GenericElement };
81         }
82         return std::nullopt;
83     };
84
85     for (auto& child : childrenOfType<RenderObject>(rootRenderer)) {
86         Box* box = nullptr;
87
88         if (is<RenderElement>(child)) {
89             auto& renderer = downcast<RenderElement>(child);
90             auto display = renderer.style().display();
91             if (display == DisplayType::Block)
92                 box = new BlockContainer(elementAttributes(renderer), RenderStyle::clone(renderer.style()));
93             else if (display == DisplayType::Inline)
94                 box = new InlineContainer(elementAttributes(renderer), RenderStyle::clone(renderer.style()));
95             else {
96                 ASSERT_NOT_IMPLEMENTED_YET();
97                 continue;
98             }
99
100         } else if (is<RenderText>(child)) {
101             box = new InlineBox( { }, RenderStyle::createAnonymousStyleWithDisplay(rootRenderer.style(), DisplayType::Inline));
102             downcast<InlineBox>(*box).setTextContent(downcast<RenderText>(child).originalText());
103         } else {
104             ASSERT_NOT_IMPLEMENTED_YET();
105             continue;
106         }
107
108         if (!rootContainer.hasChild()) {
109             rootContainer.setFirstChild(*box);
110             rootContainer.setLastChild(*box);
111         } else {
112             auto* lastChild = const_cast<Box*>(rootContainer.lastChild());
113             box->setPreviousSibling(*lastChild);
114             lastChild->setNextSibling(*box);
115             rootContainer.setLastChild(*box);
116         }
117
118         box->setParent(rootContainer);
119
120         if (box->isOutOfFlowPositioned()) {
121             // Not efficient, but this is temporary anyway.
122             // Collect the out-of-flow descendants at the formatting root lever (as opposed to at the containing block level, though they might be the same).
123             auto& containingBlockFormattingContextRoot = box->containingBlock()->formattingContextRoot();
124             const_cast<Container&>(containingBlockFormattingContextRoot).addOutOfFlowDescendant(*box);
125         }
126         if (is<RenderElement>(child))
127             createSubTree(downcast<RenderElement>(child), downcast<Container>(*box));
128     }
129 }
130
131 #if ENABLE(TREE_DEBUGGING)
132 static void outputInlineRuns(TextStream& stream, const LayoutContext& layoutContext, const Container& inlineFormattingRoot, unsigned depth)
133 {
134     auto& inlineFormattingState = layoutContext.establishedFormattingState(inlineFormattingRoot);
135     ASSERT(is<InlineFormattingState>(inlineFormattingState));
136     auto& inlineRuns = downcast<InlineFormattingState>(inlineFormattingState).inlineRuns();
137
138     for (auto& inlineRun : inlineRuns) {
139         unsigned printedCharacters = 0;
140         while (++printedCharacters <= depth * 2)
141             stream << " ";
142         stream << "run";
143         if (inlineRun.textContext())
144             stream << "(" << inlineRun.textContext()->position() << ", " << inlineRun.textContext()->position() + inlineRun.textContext()->length() << ") ";
145         stream << "(" << inlineRun.logicalLeft() << ", " << inlineRun.logicalRight() << ")";
146         stream.nextLine();
147     }
148 }
149
150 static void outputLayoutBox(TextStream& stream, const Box& layoutBox, const Display::Box* displayBox, unsigned depth)
151 {
152     unsigned printedCharacters = 0;
153     while (++printedCharacters <= depth * 2)
154         stream << " ";
155
156     if (is<InlineContainer>(layoutBox))
157         stream << "inline container";
158     else if (is<InlineBox>(layoutBox))
159         stream << "inline box";
160     else if (is<BlockContainer>(layoutBox)) {
161         if (!layoutBox.parent())
162             stream << "initial ";
163         stream << "block container";
164     } else
165         stream << "box";
166     // FIXME: Inline text runs don't create display boxes yet.
167     if (displayBox) {
168         if (is<InlineBox>(layoutBox) || is<InlineContainer>(layoutBox)) {
169             // FIXME: display box is not completely set yet.
170             stream << " at [" << "." << " " << "." << "] size [" << displayBox->width() << " " << displayBox->height() << "]";
171         } else
172             stream << " at [" << displayBox->left() << " " << displayBox->top() << "] size [" << displayBox->width() << " " << displayBox->height() << "]";
173     }
174     stream << " object [" << &layoutBox << "]";
175
176     stream.nextLine();
177 }
178
179 static void outputLayoutTree(const LayoutContext* layoutContext, TextStream& stream, const Container& rootContainer, unsigned depth)
180 {
181     for (auto& child : childrenOfType<Box>(rootContainer)) {
182         Display::Box* displayBox = nullptr;
183         // Not all boxes generate display boxes.
184         if (layoutContext && layoutContext->hasDisplayBox(child))
185             displayBox = &layoutContext->displayBoxForLayoutBox(child);
186
187         outputLayoutBox(stream, child, displayBox, depth);
188         if (layoutContext && child.establishesInlineFormattingContext())
189             outputInlineRuns(stream, *layoutContext, downcast<Container>(child), depth + 1);
190
191         if (is<Container>(child))
192             outputLayoutTree(layoutContext, stream, downcast<Container>(child), depth + 1);
193     }
194 }
195
196 void showLayoutTree(const Box& layoutBox, const LayoutContext* layoutContext)
197 {
198     TextStream stream(TextStream::LineMode::MultipleLine, TextStream::Formatting::SVGStyleRect);
199
200     auto& initialContainingBlock = layoutBox.initialContainingBlock();
201     outputLayoutBox(stream, initialContainingBlock, layoutContext ? &layoutContext->displayBoxForLayoutBox(initialContainingBlock) : nullptr, 0);
202     outputLayoutTree(layoutContext, stream, initialContainingBlock, 1);
203     WTFLogAlways("%s", stream.release().utf8().data());
204 }
205
206 void showLayoutTree(const Box& layoutBox)
207 {
208     showLayoutTree(layoutBox, nullptr);
209 }
210
211 void printLayoutTreeForLiveDocuments()
212 {
213     for (const auto* document : Document::allDocuments()) {
214         if (!document->renderView())
215             continue;
216         if (document->frame() && document->frame()->isMainFrame())
217             fprintf(stderr, "----------------------main frame--------------------------\n");
218         fprintf(stderr, "%s\n", document->url().string().utf8().data());
219         // FIXME: Need to find a way to output geometry without layout context.
220         // Layout::TreeBuilder::showLayoutTree(*TreeBuilder::createLayoutTree(*document->renderView()));
221     }
222 }
223 #endif
224
225 }
226 }
227
228 #endif