Use enum classes and OptionSets for PaintPhase and PaintBehavior
[WebKit-https.git] / Source / WebCore / rendering / SimpleLineLayoutFunctions.cpp
1 /*
2  * Copyright (C) 2013 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 "SimpleLineLayoutFunctions.h"
28
29 #include "BidiRun.h"
30 #include "BidiRunList.h"
31 #include "FontCache.h"
32 #include "Frame.h"
33 #include "GraphicsContext.h"
34 #include "HitTestLocation.h"
35 #include "HitTestRequest.h"
36 #include "HitTestResult.h"
37 #include "InlineTextBox.h"
38 #include "LineInfo.h"
39 #include "PaintInfo.h"
40 #include "RenderBlockFlow.h"
41 #include "RenderChildIterator.h"
42 #include "RenderIterator.h"
43 #include "RenderStyle.h"
44 #include "RenderText.h"
45 #include "RenderView.h"
46 #include "Settings.h"
47 #include "SimpleLineLayoutFlowContents.h"
48 #include "SimpleLineLayoutResolver.h"
49 #include "Text.h"
50 #include "TextDecorationPainter.h"
51 #include "TextPaintStyle.h"
52 #include "TextPainter.h"
53 #include <wtf/text/TextStream.h>
54
55 #if ENABLE(TREE_DEBUGGING)
56 #include <stdio.h>
57 #endif
58
59 namespace WebCore {
60 namespace SimpleLineLayout {
61
62 FloatRect computeOverflow(const RenderBlockFlow& flow, const FloatRect& layoutRect)
63 {
64     auto overflowRect = layoutRect;
65     auto viewportSize = flow.frame().view() ? flow.frame().view()->size() : IntSize();
66     auto strokeOverflow = std::ceil(flow.style().computedStrokeWidth(viewportSize));
67     overflowRect.inflate(strokeOverflow);
68
69     auto letterSpacing = flow.style().fontCascade().letterSpacing();
70     if (letterSpacing >= 0)
71         return overflowRect;
72     // Last letter's negative spacing shrinks layout rect. Push it to visual overflow.
73     overflowRect.expand(-letterSpacing, 0);
74     return overflowRect;
75 }
76
77 void paintFlow(const RenderBlockFlow& flow, const Layout& layout, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
78 {
79     if (paintInfo.phase != PaintPhase::Foreground)
80         return;
81
82     auto& style = flow.style();
83     if (style.visibility() != Visibility::Visible)
84         return;
85
86     TextPainter textPainter(paintInfo.context());
87     textPainter.setFont(style.fontCascade());
88     textPainter.setStyle(computeTextPaintStyle(flow.frame(), style, paintInfo));
89
90     std::unique_ptr<ShadowData> debugShadow = nullptr;
91     if (flow.settings().simpleLineLayoutDebugBordersEnabled()) {
92         debugShadow = std::make_unique<ShadowData>(IntPoint(0, 0), 10, 20, ShadowStyle::Normal, true, Color(0, 255, 0, 200));
93         textPainter.setShadow(debugShadow.get());
94     }
95
96     std::optional<TextDecorationPainter> textDecorationPainter;
97     if (!style.textDecorationsInEffect().isEmpty()) {
98         const RenderText* textRenderer = childrenOfType<RenderText>(flow).first();
99         if (textRenderer) {
100             textDecorationPainter.emplace(paintInfo.context(), style.textDecorationsInEffect(), *textRenderer, false);
101             textDecorationPainter->setFont(style.fontCascade());
102             textDecorationPainter->setBaseline(style.fontMetrics().ascent());
103         }
104     }
105
106     LayoutRect paintRect = paintInfo.rect;
107     paintRect.moveBy(-paintOffset);
108
109     auto& resolver = layout.runResolver();
110     float deviceScaleFactor = flow.document().deviceScaleFactor();
111     for (auto run : resolver.rangeForRect(paintRect)) {
112         if (run.start() == run.end())
113             continue;
114
115         FloatRect rect = run.rect();
116         FloatRect visualOverflowRect = computeOverflow(flow, rect);
117         if (paintRect.y() > visualOverflowRect.maxY() || paintRect.maxY() < visualOverflowRect.y())
118             continue;
119
120         String textWithHyphen;
121         if (run.hasHyphen())
122             textWithHyphen = run.textWithHyphen();
123         // x position indicates the line offset from the rootbox. It's always 0 in case of simple line layout.
124         TextRun textRun { run.hasHyphen() ? textWithHyphen : run.text(), 0, run.expansion(), run.expansionBehavior() };
125         textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
126         FloatPoint textOrigin { rect.x() + paintOffset.x(), roundToDevicePixel(run.baselinePosition() + paintOffset.y(), deviceScaleFactor) };
127
128         textPainter.setGlyphDisplayListIfNeeded(run.simpleRun(), paintInfo, style.fontCascade(), paintInfo.context(), textRun);
129         textPainter.paint(textRun, rect, textOrigin);
130         if (textDecorationPainter) {
131             textDecorationPainter->setWidth(rect.width());
132             textDecorationPainter->paintTextDecoration(textRun, textOrigin, rect.location() + paintOffset);
133         }
134     }
135 }
136
137 bool hitTestFlow(const RenderBlockFlow& flow, const Layout& layout, const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
138 {
139     if (hitTestAction != HitTestForeground)
140         return false;
141
142     if (!layout.runCount())
143         return false;
144
145     auto& style = flow.style();
146     if (style.visibility() != Visibility::Visible || style.pointerEvents() == PointerEvents::None)
147         return false;
148
149     LayoutRect rangeRect = locationInContainer.boundingBox();
150     rangeRect.moveBy(-accumulatedOffset);
151     auto resolver = lineResolver(layout.runResolver());
152     auto range = resolver.rangeForRect(rangeRect);
153     for (auto it = range.begin(), end = range.end(); it != end; ++it) {
154         auto lineRect = *it;
155         lineRect.moveBy(accumulatedOffset);
156         auto& renderer = const_cast<RenderObject&>(it.renderer());
157         if (!locationInContainer.intersects(lineRect))
158             continue;
159         renderer.updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset));
160         if (result.addNodeToListBasedTestResult(renderer.node(), request, locationInContainer, lineRect) == HitTestProgress::Stop)
161             return true;
162     }
163     return false;
164 }
165
166 void collectFlowOverflow(RenderBlockFlow& flow, const Layout& layout)
167 {
168     for (auto lineRect : lineResolver(layout.runResolver())) {
169         LayoutRect visualOverflowRect = LayoutRect(computeOverflow(flow, lineRect));
170         flow.addLayoutOverflow(LayoutRect(lineRect));
171         flow.addVisualOverflow(visualOverflowRect);
172     }
173 }
174
175 IntRect computeBoundingBox(const RenderObject& renderer, const Layout& layout)
176 {
177     auto& resolver = layout.runResolver();
178     FloatRect boundingBoxRect;
179     for (auto run : resolver.rangeForRenderer(renderer)) {
180         FloatRect rect = run.rect();
181         if (boundingBoxRect == FloatRect())
182             boundingBoxRect = rect;
183         else
184             boundingBoxRect.uniteEvenIfEmpty(rect);
185     }
186     return enclosingIntRect(boundingBoxRect);
187 }
188
189 IntPoint computeFirstRunLocation(const RenderObject& renderer, const Layout& layout)
190 {
191     auto& resolver = layout.runResolver();
192     auto range = resolver.rangeForRenderer(renderer);
193     auto begin = range.begin();
194     if (begin == range.end())
195         return IntPoint(0, 0);
196     return flooredIntPoint((*begin).rect().location());
197 }
198
199 Vector<IntRect> collectAbsoluteRects(const RenderObject& renderer, const Layout& layout, const LayoutPoint& accumulatedOffset)
200 {
201     Vector<IntRect> rects;
202     auto& resolver = layout.runResolver();
203     for (auto run : resolver.rangeForRenderer(renderer)) {
204         FloatRect rect = run.rect();
205         rects.append(enclosingIntRect(FloatRect(accumulatedOffset + rect.location(), rect.size())));
206     }
207     return rects;
208 }
209
210 Vector<FloatQuad> collectAbsoluteQuads(const RenderObject& renderer, const Layout& layout, bool* wasFixed)
211 {
212     Vector<FloatQuad> quads;
213     auto& resolver = layout.runResolver();
214     for (auto run : resolver.rangeForRenderer(renderer))
215         quads.append(renderer.localToAbsoluteQuad(FloatQuad(run.rect()), UseTransforms, wasFixed));
216     return quads;
217 }
218
219 unsigned textOffsetForPoint(const LayoutPoint& point, const RenderText& renderer, const Layout& layout)
220 {
221     auto& flow = downcast<RenderBlockFlow>(*renderer.parent());
222     ASSERT(flow.firstChild() == flow.lastChild());
223     auto& resolver = layout.runResolver();
224     auto it = resolver.runForPoint(point);
225     if (it == resolver.end())
226         return renderer.text().length();
227     auto run = *it;
228     auto& style = flow.style();
229     TextRun textRun(run.text(), run.logicalLeft(), run.expansion(), run.expansionBehavior());
230     textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
231     return run.start() + style.fontCascade().offsetForPosition(textRun, point.x() - run.logicalLeft(), true);
232 }
233
234 Vector<FloatQuad> collectAbsoluteQuadsForRange(const RenderObject& renderer, unsigned start, unsigned end, const Layout& layout, bool* wasFixed)
235 {
236     auto& style = downcast<RenderBlockFlow>(*renderer.parent()).style();
237     Vector<FloatQuad> quads;
238     auto& resolver = layout.runResolver();
239     for (auto run : resolver.rangeForRendererWithOffsets(renderer, start, end)) {
240         // This run is fully contained.
241         if (start <= run.start() && end >= run.end()) {
242             quads.append(renderer.localToAbsoluteQuad(FloatQuad(run.rect()), UseTransforms, wasFixed));
243             continue;
244         }
245         // Partially contained run.
246         TextRun textRun(run.text(), run.logicalLeft(), run.expansion(), run.expansionBehavior());
247         textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
248         LayoutRect runRect(run.rect());
249         // Special case empty ranges.
250         if (start == end) {
251             runRect.setWidth(0);
252             quads.append(renderer.localToAbsoluteQuad(FloatQuad(runRect), UseTransforms, wasFixed));
253             continue;
254         }
255         ASSERT(start < run.end());
256         ASSERT(end > run.start());
257         auto localStart = std::max(run.start(), start) - run.start();
258         auto localEnd = std::min(run.end(), end) - run.start();
259         style.fontCascade().adjustSelectionRectForText(textRun, runRect, localStart, localEnd);
260         quads.append(renderer.localToAbsoluteQuad(FloatQuad(runRect), UseTransforms, wasFixed));
261     }
262     return quads;
263 }
264
265 const RenderObject& rendererForPosition(const FlowContents& flowContents, unsigned position)
266 {
267     return flowContents.segmentForPosition(position).renderer;
268 }
269
270 void simpleLineLayoutWillBeDeleted(const Layout& layout)
271 {
272     for (unsigned i = 0; i < layout.runCount(); ++i)
273         TextPainter::removeGlyphDisplayList(layout.runAt(i));
274 }
275
276 bool canUseForLineBoxTree(RenderBlockFlow& flow, const Layout& layout)
277 {
278     if (layout.isPaginated())
279         return false;
280     
281     if (flow.style().preserveNewline())
282         return false;
283     
284     if (!flow.firstChild())
285         return false;
286     
287     for (auto& child : childrenOfType<RenderObject>(flow)) {
288         if (!is<RenderText>(child))
289             return false;
290         // Simple line layout iterator can't handle renderers with zero length properly.
291         if (!downcast<RenderText>(child).length())
292             return false;
293     }
294     return true;
295 }
296
297 static void initializeInlineTextBox(RenderBlockFlow& flow, InlineTextBox& inlineTextBox, const RunResolver::Run& run)
298 {
299     inlineTextBox.setLogicalLeft(run.logicalLeft());
300     inlineTextBox.setLogicalTop(run.rect().y());
301     inlineTextBox.setLogicalWidth(run.logicalRight() - run.logicalLeft());
302     auto overflowRect = computeOverflow(const_cast<RenderBlockFlow&>(flow), run.rect());
303     if (overflowRect != run.rect())
304         inlineTextBox.setLogicalOverflowRect(LayoutRect(overflowRect));
305
306     inlineTextBox.setHasHyphen(run.hasHyphen());
307     inlineTextBox.setExpansionWithoutGrowing(run.expansion());
308
309     auto expansionBehavior = run.expansionBehavior();
310     inlineTextBox.setCanHaveLeadingExpansion(expansionBehavior & AllowLeadingExpansion);
311     inlineTextBox.setCanHaveTrailingExpansion(expansionBehavior & AllowTrailingExpansion);
312     if (expansionBehavior & ForceTrailingExpansion)
313         inlineTextBox.setForceTrailingExpansion();
314     if (expansionBehavior & ForceLeadingExpansion)
315         inlineTextBox.setForceLeadingExpansion();
316 }
317
318 void generateLineBoxTree(RenderBlockFlow& flow, const Layout& layout)
319 {
320     ASSERT(!flow.lineBoxes().firstLineBox());
321     if (!layout.runCount())
322         return;
323
324     Ref<BidiContext> bidiContext = BidiContext::create(0, U_LEFT_TO_RIGHT);
325     auto& resolver = layout.runResolver();
326     unsigned lineIndex = 0;
327     while (true) {
328         auto range = resolver.rangeForLine(lineIndex++);
329         if (range.begin() == range.end())
330             break;
331
332         // Generate bidi runs out of simple line layout runs.
333         BidiRunList<BidiRun> bidiRuns;
334         for (auto it = range.begin(); it != range.end(); ++it) {
335             auto run = *it;
336             bidiRuns.appendRun(std::make_unique<BidiRun>(run.localStart(), run.localEnd(), const_cast<RenderObject&>(run.renderer()), bidiContext.ptr(), U_LEFT_TO_RIGHT));
337         }
338
339         LineInfo lineInfo;
340         lineInfo.setFirstLine(!flow.lineBoxes().firstLineBox());
341         // FIXME: This is needed for flow boxes -but we don't have them yet.
342         // lineInfo.setLastLine(lastLine);
343         lineInfo.setEmpty(!bidiRuns.runCount());
344         bidiRuns.setLogicallyLastRun(bidiRuns.lastRun());
345         auto* root = flow.constructLine(bidiRuns, lineInfo);
346         bidiRuns.clear();
347         if (!root)
348             continue;
349
350         auto& rootLineBox = *root;
351         auto it = range.begin();
352         float lineWidth = 0;
353         // Set the geometry for the inlineboxes.
354         for (auto* inlineBox = rootLineBox.firstChild(); inlineBox && it != range.end(); inlineBox = inlineBox->nextOnLine(), ++it) {
355             auto run = *it;
356             initializeInlineTextBox(flow, downcast<InlineTextBox>(*inlineBox), run);
357             lineWidth += inlineBox->logicalWidth();
358         }
359
360         // Finish setting up the rootline.
361         auto iter = range.begin();
362         auto firstRun = *iter;
363         rootLineBox.setLogicalLeft(firstRun.logicalLeft());
364         rootLineBox.setLogicalWidth(lineWidth);
365         auto lineTop = firstRun.rect().y();
366         auto lineHeight = firstRun.rect().height();
367         rootLineBox.setLogicalTop(lineTop);
368         rootLineBox.setLineTopBottomPositions(lineTop, lineTop + lineHeight, lineTop, lineTop + lineHeight);
369     }
370 }
371
372 #if ENABLE(TREE_DEBUGGING)
373 static void printPrefix(TextStream& stream, int& printedCharacters, int depth)
374 {
375     stream << "-------- --";
376     printedCharacters = 0;
377     while (++printedCharacters <= depth * 2)
378         stream << " ";
379 }
380
381 void outputLineLayoutForFlow(TextStream& stream, const RenderBlockFlow& flow, const Layout& layout, int depth)
382 {
383     int printedCharacters = 0;
384     printPrefix(stream, printedCharacters, depth);
385
386     stream << "SimpleLineLayout (" << layout.lineCount() << " lines, " << layout.runCount() << " runs) (" << &layout << ")";
387     stream.nextLine();
388     ++depth;
389
390     for (auto run : runResolver(flow, layout)) {
391         FloatRect rect = run.rect();
392         printPrefix(stream, printedCharacters, depth);
393         if (run.start() < run.end()) {
394             stream << "line " << run.lineIndex() << " run(" << run.start() << ", " << run.end() << ") " << rect << " \"" << run.text().toStringWithoutCopying().utf8().data() << "\"";
395         } else {
396             ASSERT(run.start() == run.end());
397             stream << "line break " << run.lineIndex() << " run(" << run.start() << ", " << run.end() << ") " << rect;
398         }
399     }
400     stream.nextLine();
401 }
402 #endif
403
404 }
405 }