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 "LayoutInlineBox.h" |
38 | #include "LayoutInlineContainer.h" |
39 | #include "LayoutState.h" |
40 | #include "RenderBlock.h" |
41 | #include "RenderChildIterator.h" |
42 | #include "RenderElement.h" |
43 | #include "RenderImage.h" |
44 | #include "RenderInline.h" |
45 | #include "RenderStyle.h" |
46 | #include "RenderView.h" |
47 | #include <wtf/text/TextStream.h> |
48 | |
49 | namespace WebCore { |
50 | namespace Layout { |
51 | |
52 | std::unique_ptr<Container> TreeBuilder::createLayoutTree(const RenderView& renderView) |
53 | { |
54 | auto style = RenderStyle::clone(renderView.style()); |
55 | style.setLogicalWidth(Length(renderView.width(), Fixed)); |
56 | style.setLogicalHeight(Length(renderView.height(), Fixed)); |
57 | |
58 | std::unique_ptr<Container> initialContainingBlock(new BlockContainer(WTF::nullopt, WTFMove(style))); |
59 | TreeBuilder::createSubTree(renderView, *initialContainingBlock); |
60 | return initialContainingBlock; |
61 | } |
62 | |
63 | void TreeBuilder::createSubTree(const RenderElement& rootRenderer, Container& rootContainer) |
64 | { |
65 | auto elementAttributes = [] (const RenderElement& renderer) -> Optional<Box::ElementAttributes> { |
66 | if (renderer.isDocumentElementRenderer()) |
67 | return Box::ElementAttributes { Box::ElementType::Document }; |
68 | if (auto* element = renderer.element()) { |
69 | if (element->hasTagName(HTMLNames::bodyTag)) |
70 | return Box::ElementAttributes { Box::ElementType::Body }; |
71 | if (element->hasTagName(HTMLNames::colTag)) |
72 | return Box::ElementAttributes { Box::ElementType::TableColumn }; |
73 | if (element->hasTagName(HTMLNames::trTag)) |
74 | return Box::ElementAttributes { Box::ElementType::TableRow }; |
75 | if (element->hasTagName(HTMLNames::colgroupTag)) |
76 | return Box::ElementAttributes { Box::ElementType::TableColumnGroup }; |
77 | if (element->hasTagName(HTMLNames::tbodyTag)) |
78 | return Box::ElementAttributes { Box::ElementType::TableRowGroup }; |
79 | if (element->hasTagName(HTMLNames::theadTag)) |
80 | return Box::ElementAttributes { Box::ElementType::TableHeaderGroup }; |
81 | if (element->hasTagName(HTMLNames::tfootTag)) |
82 | return Box::ElementAttributes { Box::ElementType::TableFooterGroup }; |
83 | if (element->hasTagName(HTMLNames::tfootTag)) |
84 | return Box::ElementAttributes { Box::ElementType::TableFooterGroup }; |
85 | if (element->hasTagName(HTMLNames::imgTag)) |
86 | return Box::ElementAttributes { Box::ElementType::Image }; |
87 | if (element->hasTagName(HTMLNames::iframeTag)) |
88 | return Box::ElementAttributes { Box::ElementType::IFrame }; |
89 | return Box::ElementAttributes { Box::ElementType::GenericElement }; |
90 | } |
91 | return WTF::nullopt; |
92 | }; |
93 | |
94 | for (auto& child : childrenOfType<RenderObject>(rootRenderer)) { |
95 | std::unique_ptr<Box> box; |
96 | |
97 | if (is<RenderText>(child)) { |
98 | box = std::make_unique<InlineBox>(Optional<Box::ElementAttributes>(), RenderStyle::createAnonymousStyleWithDisplay(rootRenderer.style(), DisplayType::Inline)); |
99 | downcast<InlineBox>(*box).setTextContent(downcast<RenderText>(child).originalText()); |
100 | } else if (is<RenderReplaced>(child)) { |
101 | auto& renderer = downcast<RenderReplaced>(child); |
102 | auto display = renderer.style().display(); |
103 | if (display == DisplayType::Block) |
104 | box = std::make_unique<Box>(elementAttributes(renderer), RenderStyle::clone(renderer.style())); |
105 | else |
106 | box = std::make_unique<InlineBox>(elementAttributes(renderer), RenderStyle::clone(renderer.style())); |
107 | // FIXME: We don't yet support all replaced elements and this is temporary anyway. |
108 | if (box->replaced()) |
109 | box->replaced()->setIntrinsicSize(renderer.intrinsicSize()); |
110 | if (is<RenderImage>(renderer)) { |
111 | auto& imageRenderer = downcast<RenderImage>(renderer); |
112 | if (imageRenderer.imageResource().errorOccurred()) |
113 | box->replaced()->setIntrinsicRatio(1); |
114 | } |
115 | } else if (is<RenderElement>(child)) { |
116 | auto& renderer = downcast<RenderElement>(child); |
117 | auto display = renderer.style().display(); |
118 | if (display == DisplayType::Block) |
119 | box = std::make_unique<BlockContainer>(elementAttributes(renderer), RenderStyle::clone(renderer.style())); |
120 | else if (display == DisplayType::Inline) |
121 | box = std::make_unique<InlineContainer>(elementAttributes(renderer), RenderStyle::clone(renderer.style())); |
122 | else if (display == DisplayType::InlineBlock) |
123 | box = std::make_unique<InlineContainer>(elementAttributes(renderer), RenderStyle::clone(renderer.style())); |
124 | else { |
125 | ASSERT_NOT_IMPLEMENTED_YET(); |
126 | continue; |
127 | } |
128 | } else { |
129 | ASSERT_NOT_IMPLEMENTED_YET(); |
130 | continue; |
131 | } |
132 | |
133 | if (!rootContainer.hasChild()) { |
134 | rootContainer.setFirstChild(*box); |
135 | rootContainer.setLastChild(*box); |
136 | } else { |
137 | auto* lastChild = const_cast<Box*>(rootContainer.lastChild()); |
138 | box->setPreviousSibling(*lastChild); |
139 | lastChild->setNextSibling(*box); |
140 | rootContainer.setLastChild(*box); |
141 | } |
142 | |
143 | box->setParent(rootContainer); |
144 | |
145 | if (box->isOutOfFlowPositioned()) { |
146 | // Not efficient, but this is temporary anyway. |
147 | // Collect the out-of-flow descendants at the formatting root level (as opposed to at the containing block level, though they might be the same). |
148 | const_cast<Container&>(box->formattingContextRoot()).addOutOfFlowDescendant(*box); |
149 | } |
150 | if (is<Container>(*box)) |
151 | createSubTree(downcast<RenderElement>(child), downcast<Container>(*box)); |
152 | // Temporary |
153 | box.release(); |
154 | } |
155 | } |
156 | |
157 | #if ENABLE(TREE_DEBUGGING) |
158 | static void outputInlineRuns(TextStream& stream, const LayoutState& layoutState, const Container& inlineFormattingRoot, unsigned depth) |
159 | { |
160 | auto& inlineFormattingState = layoutState.establishedFormattingState(inlineFormattingRoot); |
161 | ASSERT(is<InlineFormattingState>(inlineFormattingState)); |
162 | auto& inlineRuns = downcast<InlineFormattingState>(inlineFormattingState).inlineRuns(); |
163 | |
164 | for (auto& inlineRun : inlineRuns) { |
165 | unsigned printedCharacters = 0; |
166 | while (++printedCharacters <= depth * 2) |
167 | stream << " " ; |
168 | stream << "run" ; |
169 | if (inlineRun.textContext()) |
170 | stream << "(" << inlineRun.textContext()->start() << ", " << inlineRun.textContext()->start() + inlineRun.textContext()->length() << ") " ; |
171 | else |
172 | stream << "(x, x) " ; |
173 | stream << "at [" << inlineRun.logicalLeft() << ", " << inlineRun.logicalTop() << "] size [" << inlineRun.logicalWidth() << " " << inlineRun.logicalHeight() << "]" ; |
174 | stream.nextLine(); |
175 | } |
176 | } |
177 | |
178 | static void outputLayoutBox(TextStream& stream, const Box& layoutBox, const Display::Box* displayBox, unsigned depth) |
179 | { |
180 | unsigned printedCharacters = 0; |
181 | while (++printedCharacters <= depth * 2) |
182 | stream << " " ; |
183 | |
184 | if (is<InlineContainer>(layoutBox)) |
185 | stream << "inline container" ; |
186 | else if (is<InlineBox>(layoutBox)) { |
187 | if (layoutBox.replaced()) |
188 | stream << "inline replaced box" ; |
189 | else if (downcast<InlineBox>(layoutBox).hasTextContent()) { |
190 | auto textContent = downcast<InlineBox>(layoutBox).textContent(); |
191 | stream << "inline text [\"" << textContent.utf8().data() << "\"]" ; |
192 | } else |
193 | stream << "inline box" ; |
194 | } else if (is<BlockContainer>(layoutBox)) { |
195 | if (!layoutBox.parent()) |
196 | stream << "initial " ; |
197 | stream << "block container" ; |
198 | } else if (layoutBox.isBlockLevelBox()) |
199 | stream << "block box" ; |
200 | else |
201 | stream << "box" ; |
202 | // FIXME: Inline text runs don't create display boxes yet. |
203 | if (displayBox) { |
204 | // FIXME: display box is not completely set yet. |
205 | if ((is<InlineBox>(layoutBox) || is<InlineContainer>(layoutBox)) && !layoutBox.isFloatingPositioned()) |
206 | stream << " at [" << "." << " " << "." << "] size [" << displayBox->width() << " " << displayBox->height() << "]" ; |
207 | else |
208 | stream << " at [" << displayBox->left() << " " << displayBox->top() << "] size [" << displayBox->width() << " " << displayBox->height() << "]" ; |
209 | } |
210 | stream << " [" << &layoutBox << "]" ; |
211 | |
212 | stream.nextLine(); |
213 | } |
214 | |
215 | static void outputLayoutTree(const LayoutState* layoutState, TextStream& stream, const Container& rootContainer, unsigned depth) |
216 | { |
217 | for (auto& child : childrenOfType<Box>(rootContainer)) { |
218 | Display::Box* displayBox = nullptr; |
219 | // Not all boxes generate display boxes. |
220 | if (layoutState && layoutState->hasDisplayBox(child)) |
221 | displayBox = &layoutState->displayBoxForLayoutBox(child); |
222 | |
223 | outputLayoutBox(stream, child, displayBox, depth); |
224 | if (layoutState && child.establishesInlineFormattingContext()) |
225 | outputInlineRuns(stream, *layoutState, downcast<Container>(child), depth + 1); |
226 | |
227 | if (is<Container>(child)) |
228 | outputLayoutTree(layoutState, stream, downcast<Container>(child), depth + 1); |
229 | } |
230 | } |
231 | |
232 | void showLayoutTree(const Box& layoutBox, const LayoutState* layoutState) |
233 | { |
234 | TextStream stream(TextStream::LineMode::MultipleLine, TextStream::Formatting::SVGStyleRect); |
235 | |
236 | auto& initialContainingBlock = layoutBox.initialContainingBlock(); |
237 | outputLayoutBox(stream, initialContainingBlock, layoutState ? &layoutState->displayBoxForLayoutBox(initialContainingBlock) : nullptr, 0); |
238 | outputLayoutTree(layoutState, stream, initialContainingBlock, 1); |
239 | WTFLogAlways("%s" , stream.release().utf8().data()); |
240 | } |
241 | |
242 | void showLayoutTree(const Box& layoutBox) |
243 | { |
244 | showLayoutTree(layoutBox, nullptr); |
245 | } |
246 | |
247 | void printLayoutTreeForLiveDocuments() |
248 | { |
249 | for (const auto* document : Document::allDocuments()) { |
250 | if (!document->renderView()) |
251 | continue; |
252 | if (document->frame() && document->frame()->isMainFrame()) |
253 | fprintf(stderr, "----------------------main frame--------------------------\n" ); |
254 | fprintf(stderr, "%s\n" , document->url().string().utf8().data()); |
255 | // FIXME: Need to find a way to output geometry without layout context. |
256 | // Layout::TreeBuilder::showLayoutTree(*TreeBuilder::createLayoutTree(*document->renderView())); |
257 | } |
258 | } |
259 | #endif |
260 | |
261 | } |
262 | } |
263 | |
264 | #endif |
265 | |