3a4aedd43fd43689b40c73da8a033de15291a917
[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 "LayoutBlockContainer.h"
33 #include "LayoutBox.h"
34 #include "LayoutChildIterator.h"
35 #include "LayoutContainer.h"
36 #include "LayoutContext.h"
37 #include "LayoutInlineBox.h"
38 #include "LayoutInlineContainer.h"
39 #include "RenderBlock.h"
40 #include "RenderChildIterator.h"
41 #include "RenderElement.h"
42 #include "RenderInline.h"
43 #include "RenderStyle.h"
44 #include "RenderView.h"
45 #include <wtf/text/TextStream.h>
46
47 namespace WebCore {
48 namespace Layout {
49
50 std::unique_ptr<Container> TreeBuilder::createLayoutTree(const RenderView& renderView)
51 {
52     std::unique_ptr<Container> initialContainingBlock(new BlockContainer(std::nullopt, RenderStyle::clone(renderView.style())));
53     TreeBuilder::createSubTree(renderView, *initialContainingBlock);
54     return initialContainingBlock;
55 }
56
57 void TreeBuilder::createSubTree(const RenderElement& rootRenderer, Container& rootContainer)
58 {
59     auto elementAttributes = [] (const RenderElement& renderer) -> std::optional<Box::ElementAttributes> {
60         if (renderer.isDocumentElementRenderer())
61             return Box::ElementAttributes { Box::ElementType::Document };
62         if (auto* element = renderer.element()) {
63             if (element->hasTagName(HTMLNames::bodyTag))
64                 return Box::ElementAttributes { Box::ElementType::Body };
65             if (element->hasTagName(HTMLNames::colTag))
66                 return Box::ElementAttributes { Box::ElementType::TableColumn };
67             if (element->hasTagName(HTMLNames::trTag))
68                 return Box::ElementAttributes { Box::ElementType::TableRow };
69             if (element->hasTagName(HTMLNames::colgroupTag))
70                 return Box::ElementAttributes { Box::ElementType::TableColumnGroup };
71             if (element->hasTagName(HTMLNames::tbodyTag))
72                 return Box::ElementAttributes { Box::ElementType::TableRowGroup };
73             if (element->hasTagName(HTMLNames::theadTag))
74                 return Box::ElementAttributes { Box::ElementType::TableHeaderGroup };
75             if (element->hasTagName(HTMLNames::tfootTag))
76                 return Box::ElementAttributes { Box::ElementType::TableFooterGroup };
77             if (element->hasTagName(HTMLNames::tfootTag))
78                 return Box::ElementAttributes { Box::ElementType::TableFooterGroup };
79             return Box::ElementAttributes { Box::ElementType::GenericElement };
80         }
81         return std::nullopt;
82     };
83
84     for (auto& child : childrenOfType<RenderObject>(rootRenderer)) {
85         Box* box = nullptr;
86
87         if (is<RenderElement>(child)) {
88             auto& renderer = downcast<RenderElement>(child);
89             auto display = renderer.style().display();
90             if (display == DisplayType::Block)
91                 box = new BlockContainer(elementAttributes(renderer), RenderStyle::clone(renderer.style()));
92             else if (display == DisplayType::Inline)
93                 box = new InlineContainer(elementAttributes(renderer), RenderStyle::clone(renderer.style()));
94             else {
95                 ASSERT_NOT_IMPLEMENTED_YET();
96                 continue;
97             }
98
99         } else if (is<RenderText>(child)) {
100             box = new InlineBox( { }, RenderStyle::createAnonymousStyleWithDisplay(rootRenderer.style(), DisplayType::Inline));
101             downcast<InlineBox>(*box).setTextContent(downcast<RenderText>(child).originalText());
102         } else {
103             ASSERT_NOT_IMPLEMENTED_YET();
104             continue;
105         }
106
107         if (!rootContainer.hasChild()) {
108             rootContainer.setFirstChild(*box);
109             rootContainer.setLastChild(*box);
110         } else {
111             auto* lastChild = const_cast<Box*>(rootContainer.lastChild());
112             box->setPreviousSibling(*lastChild);
113             lastChild->setNextSibling(*box);
114             rootContainer.setLastChild(*box);
115         }
116
117         box->setParent(rootContainer);
118
119         if (box->isOutOfFlowPositioned()) {
120             // Not efficient, but this is temporary anyway.
121             // 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).
122             auto& containingBlockFormattingContextRoot = box->containingBlock()->formattingContextRoot();
123             const_cast<Container&>(containingBlockFormattingContextRoot).addOutOfFlowDescendant(*box);
124         }
125         if (is<RenderElement>(child))
126             createSubTree(downcast<RenderElement>(child), downcast<Container>(*box));
127     }
128 }
129
130 #if ENABLE(TREE_DEBUGGING)
131 static void outputLayoutBox(TextStream& stream, const Box& layoutBox, const Display::Box* displayBox, unsigned depth)
132 {
133     unsigned printedCharacters = 0;
134     while (++printedCharacters <= depth * 2)
135         stream << " ";
136
137     if (is<InlineContainer>(layoutBox))
138         stream << "inline container";
139     else if (is<InlineBox>(layoutBox))
140         stream << "inline box";
141     else if (is<BlockContainer>(layoutBox)) {
142         if (!layoutBox.parent())
143             stream << "initial ";
144         stream << "block container";
145     } else
146         stream << "box";
147     // FIXME: Inline text runs don't create display boxes yet.
148     if (displayBox)
149         stream << " at [" << displayBox->left() << " " << displayBox->top() << "] size [" << displayBox->width() << " " << displayBox->height() << "]";
150     stream << " object [" << &layoutBox << "]";
151
152     stream.nextLine();
153 }
154
155 static void outputLayoutTree(const LayoutContext* layoutContext, TextStream& stream, const Container& rootContainer, unsigned depth)
156 {
157     for (auto& child : childrenOfType<Box>(rootContainer)) {
158         outputLayoutBox(stream, child, layoutContext ? &layoutContext->displayBoxForLayoutBox(child) : nullptr, depth);
159         if (is<Container>(child))
160             outputLayoutTree(layoutContext, stream, downcast<Container>(child), depth + 1);
161     }
162 }
163
164 void showLayoutTree(const Box& layoutBox, const LayoutContext* layoutContext)
165 {
166     TextStream stream(TextStream::LineMode::MultipleLine, TextStream::Formatting::SVGStyleRect);
167
168     auto& initialContainingBlock = layoutBox.initialContainingBlock();
169     outputLayoutBox(stream, initialContainingBlock, layoutContext ? &layoutContext->displayBoxForLayoutBox(initialContainingBlock) : nullptr, 0);
170     outputLayoutTree(layoutContext, stream, initialContainingBlock, 1);
171     WTFLogAlways("%s", stream.release().utf8().data());
172 }
173
174 void showLayoutTree(const Box& layoutBox)
175 {
176     showLayoutTree(layoutBox, nullptr);
177 }
178
179 void printLayoutTreeForLiveDocuments()
180 {
181     for (const auto* document : Document::allDocuments()) {
182         if (!document->renderView())
183             continue;
184         if (document->frame() && document->frame()->isMainFrame())
185             fprintf(stderr, "----------------------main frame--------------------------\n");
186         fprintf(stderr, "%s\n", document->url().string().utf8().data());
187         // FIXME: Need to find a way to output geometry without layout context.
188         // Layout::TreeBuilder::showLayoutTree(*TreeBuilder::createLayoutTree(*document->renderView()));
189     }
190 }
191 #endif
192
193 }
194 }
195
196 #endif