Clean up text decoration drawing code
[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, style.fontCascade());
101         }
102     }
103
104     LayoutRect paintRect = paintInfo.rect;
105     paintRect.moveBy(-paintOffset);
106
107     auto& resolver = layout.runResolver();
108     float deviceScaleFactor = flow.document().deviceScaleFactor();
109     for (auto run : resolver.rangeForRect(paintRect)) {
110         if (run.start() == run.end())
111             continue;
112
113         FloatRect rect = run.rect();
114         FloatRect visualOverflowRect = computeOverflow(flow, rect);
115         if (paintRect.y() > visualOverflowRect.maxY() || paintRect.maxY() < visualOverflowRect.y())
116             continue;
117
118         String textWithHyphen;
119         if (run.hasHyphen())
120             textWithHyphen = run.textWithHyphen();
121         // x position indicates the line offset from the rootbox. It's always 0 in case of simple line layout.
122         TextRun textRun { run.hasHyphen() ? textWithHyphen : run.text(), 0, run.expansion(), run.expansionBehavior() };
123         textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
124         FloatPoint textOrigin { rect.x() + paintOffset.x(), roundToDevicePixel(run.baselinePosition() + paintOffset.y(), deviceScaleFactor) };
125
126         textPainter.setGlyphDisplayListIfNeeded(run.simpleRun(), paintInfo, style.fontCascade(), paintInfo.context(), textRun);
127         textPainter.paint(textRun, rect, textOrigin);
128         if (textDecorationPainter) {
129             textDecorationPainter->setWidth(rect.width());
130             textDecorationPainter->paintTextDecoration(textRun, textOrigin, rect.location() + paintOffset);
131         }
132     }
133 }
134
135 bool hitTestFlow(const RenderBlockFlow& flow, const Layout& layout, const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
136 {
137     if (hitTestAction != HitTestForeground)
138         return false;
139
140     if (!layout.runCount())
141         return false;
142
143     auto& style = flow.style();
144     if (style.visibility() != Visibility::Visible || style.pointerEvents() == PointerEvents::None)
145         return false;
146
147     LayoutRect rangeRect = locationInContainer.boundingBox();
148     rangeRect.moveBy(-accumulatedOffset);
149     auto resolver = lineResolver(layout.runResolver());
150     auto range = resolver.rangeForRect(rangeRect);
151     for (auto it = range.begin(), end = range.end(); it != end; ++it) {
152         auto lineRect = *it;
153         lineRect.moveBy(accumulatedOffset);
154         auto& renderer = const_cast<RenderObject&>(it.renderer());
155         if (!locationInContainer.intersects(lineRect))
156             continue;
157         renderer.updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset));
158         if (result.addNodeToListBasedTestResult(renderer.node(), request, locationInContainer, lineRect) == HitTestProgress::Stop)
159             return true;
160     }
161     return false;
162 }
163
164 void collectFlowOverflow(RenderBlockFlow& flow, const Layout& layout)
165 {
166     for (auto lineRect : lineResolver(layout.runResolver())) {
167         LayoutRect visualOverflowRect = LayoutRect(computeOverflow(flow, lineRect));
168         flow.addLayoutOverflow(LayoutRect(lineRect));
169         flow.addVisualOverflow(visualOverflowRect);
170     }
171 }
172
173 IntRect computeBoundingBox(const RenderObject& renderer, const Layout& layout)
174 {
175     auto& resolver = layout.runResolver();
176     FloatRect boundingBoxRect;
177     for (auto run : resolver.rangeForRenderer(renderer)) {
178         FloatRect rect = run.rect();
179         if (boundingBoxRect == FloatRect())
180             boundingBoxRect = rect;
181         else
182             boundingBoxRect.uniteEvenIfEmpty(rect);
183     }
184     return enclosingIntRect(boundingBoxRect);
185 }
186
187 IntPoint computeFirstRunLocation(const RenderObject& renderer, const Layout& layout)
188 {
189     auto& resolver = layout.runResolver();
190     auto range = resolver.rangeForRenderer(renderer);
191     auto begin = range.begin();
192     if (begin == range.end())
193         return IntPoint(0, 0);
194     return flooredIntPoint((*begin).rect().location());
195 }
196
197 Vector<IntRect> collectAbsoluteRects(const RenderObject& renderer, const Layout& layout, const LayoutPoint& accumulatedOffset)
198 {
199     Vector<IntRect> rects;
200     auto& resolver = layout.runResolver();
201     for (auto run : resolver.rangeForRenderer(renderer)) {
202         FloatRect rect = run.rect();
203         rects.append(enclosingIntRect(FloatRect(accumulatedOffset + rect.location(), rect.size())));
204     }
205     return rects;
206 }
207
208 Vector<FloatQuad> collectAbsoluteQuads(const RenderObject& renderer, const Layout& layout, bool* wasFixed)
209 {
210     Vector<FloatQuad> quads;
211     auto& resolver = layout.runResolver();
212     for (auto run : resolver.rangeForRenderer(renderer))
213         quads.append(renderer.localToAbsoluteQuad(FloatQuad(run.rect()), UseTransforms, wasFixed));
214     return quads;
215 }
216
217 unsigned textOffsetForPoint(const LayoutPoint& point, const RenderText& renderer, const Layout& layout)
218 {
219     auto& flow = downcast<RenderBlockFlow>(*renderer.parent());
220     ASSERT(flow.firstChild() == flow.lastChild());
221     auto& resolver = layout.runResolver();
222     auto it = resolver.runForPoint(point);
223     if (it == resolver.end())
224         return renderer.text().length();
225     auto run = *it;
226     auto& style = flow.style();
227     TextRun textRun(run.text(), run.logicalLeft(), run.expansion(), run.expansionBehavior());
228     textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
229     return run.start() + style.fontCascade().offsetForPosition(textRun, point.x() - run.logicalLeft(), true);
230 }
231
232 Vector<FloatQuad> collectAbsoluteQuadsForRange(const RenderObject& renderer, unsigned start, unsigned end, const Layout& layout, bool* wasFixed)
233 {
234     auto& style = downcast<RenderBlockFlow>(*renderer.parent()).style();
235     Vector<FloatQuad> quads;
236     auto& resolver = layout.runResolver();
237     for (auto run : resolver.rangeForRendererWithOffsets(renderer, start, end)) {
238         // This run is fully contained.
239         if (start <= run.start() && end >= run.end()) {
240             quads.append(renderer.localToAbsoluteQuad(FloatQuad(run.rect()), UseTransforms, wasFixed));
241             continue;
242         }
243         // Partially contained run.
244         TextRun textRun(run.text(), run.logicalLeft(), run.expansion(), run.expansionBehavior());
245         textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
246         LayoutRect runRect(run.rect());
247         // Special case empty ranges.
248         if (start == end) {
249             runRect.setWidth(0);
250             quads.append(renderer.localToAbsoluteQuad(FloatQuad(runRect), UseTransforms, wasFixed));
251             continue;
252         }
253         ASSERT(start < run.end());
254         ASSERT(end > run.start());
255         auto localStart = std::max(run.start(), start) - run.start();
256         auto localEnd = std::min(run.end(), end) - run.start();
257         style.fontCascade().adjustSelectionRectForText(textRun, runRect, localStart, localEnd);
258         quads.append(renderer.localToAbsoluteQuad(FloatQuad(runRect), UseTransforms, wasFixed));
259     }
260     return quads;
261 }
262
263 const RenderObject& rendererForPosition(const FlowContents& flowContents, unsigned position)
264 {
265     return flowContents.segmentForPosition(position).renderer;
266 }
267
268 void simpleLineLayoutWillBeDeleted(const Layout& layout)
269 {
270     for (unsigned i = 0; i < layout.runCount(); ++i)
271         TextPainter::removeGlyphDisplayList(layout.runAt(i));
272 }
273
274 bool canUseForLineBoxTree(RenderBlockFlow& flow, const Layout& layout)
275 {
276     if (layout.isPaginated())
277         return false;
278     
279     if (flow.style().preserveNewline())
280         return false;
281     
282     if (!flow.firstChild())
283         return false;
284     
285     for (auto& child : childrenOfType<RenderObject>(flow)) {
286         if (!is<RenderText>(child))
287             return false;
288         // Simple line layout iterator can't handle renderers with zero length properly.
289         if (!downcast<RenderText>(child).length())
290             return false;
291     }
292     return true;
293 }
294
295 static void initializeInlineTextBox(RenderBlockFlow& flow, InlineTextBox& inlineTextBox, const RunResolver::Run& run)
296 {
297     inlineTextBox.setLogicalLeft(run.logicalLeft());
298     inlineTextBox.setLogicalTop(run.rect().y());
299     inlineTextBox.setLogicalWidth(run.logicalRight() - run.logicalLeft());
300     auto overflowRect = computeOverflow(const_cast<RenderBlockFlow&>(flow), run.rect());
301     if (overflowRect != run.rect())
302         inlineTextBox.setLogicalOverflowRect(LayoutRect(overflowRect));
303
304     inlineTextBox.setHasHyphen(run.hasHyphen());
305     inlineTextBox.setExpansionWithoutGrowing(run.expansion());
306
307     auto expansionBehavior = run.expansionBehavior();
308     inlineTextBox.setCanHaveLeadingExpansion(expansionBehavior & AllowLeadingExpansion);
309     inlineTextBox.setCanHaveTrailingExpansion(expansionBehavior & AllowTrailingExpansion);
310     if (expansionBehavior & ForceTrailingExpansion)
311         inlineTextBox.setForceTrailingExpansion();
312     if (expansionBehavior & ForceLeadingExpansion)
313         inlineTextBox.setForceLeadingExpansion();
314 }
315
316 void generateLineBoxTree(RenderBlockFlow& flow, const Layout& layout)
317 {
318     ASSERT(!flow.lineBoxes().firstLineBox());
319     if (!layout.runCount())
320         return;
321
322     Ref<BidiContext> bidiContext = BidiContext::create(0, U_LEFT_TO_RIGHT);
323     auto& resolver = layout.runResolver();
324     unsigned lineIndex = 0;
325     while (true) {
326         auto range = resolver.rangeForLine(lineIndex++);
327         if (range.begin() == range.end())
328             break;
329
330         // Generate bidi runs out of simple line layout runs.
331         BidiRunList<BidiRun> bidiRuns;
332         for (auto it = range.begin(); it != range.end(); ++it) {
333             auto run = *it;
334             bidiRuns.appendRun(std::make_unique<BidiRun>(run.localStart(), run.localEnd(), const_cast<RenderObject&>(run.renderer()), bidiContext.ptr(), U_LEFT_TO_RIGHT));
335         }
336
337         LineInfo lineInfo;
338         lineInfo.setFirstLine(!flow.lineBoxes().firstLineBox());
339         // FIXME: This is needed for flow boxes -but we don't have them yet.
340         // lineInfo.setLastLine(lastLine);
341         lineInfo.setEmpty(!bidiRuns.runCount());
342         bidiRuns.setLogicallyLastRun(bidiRuns.lastRun());
343         auto* root = flow.constructLine(bidiRuns, lineInfo);
344         bidiRuns.clear();
345         if (!root)
346             continue;
347
348         auto& rootLineBox = *root;
349         auto it = range.begin();
350         float lineWidth = 0;
351         // Set the geometry for the inlineboxes.
352         for (auto* inlineBox = rootLineBox.firstChild(); inlineBox && it != range.end(); inlineBox = inlineBox->nextOnLine(), ++it) {
353             auto run = *it;
354             initializeInlineTextBox(flow, downcast<InlineTextBox>(*inlineBox), run);
355             lineWidth += inlineBox->logicalWidth();
356         }
357
358         // Finish setting up the rootline.
359         auto iter = range.begin();
360         auto firstRun = *iter;
361         rootLineBox.setLogicalLeft(firstRun.logicalLeft());
362         rootLineBox.setLogicalWidth(lineWidth);
363         auto lineTop = firstRun.rect().y();
364         auto lineHeight = firstRun.rect().height();
365         rootLineBox.setLogicalTop(lineTop);
366         rootLineBox.setLineTopBottomPositions(lineTop, lineTop + lineHeight, lineTop, lineTop + lineHeight);
367     }
368 }
369
370 #if ENABLE(TREE_DEBUGGING)
371 static void printPrefix(TextStream& stream, int& printedCharacters, int depth)
372 {
373     stream << "-------- --";
374     printedCharacters = 0;
375     while (++printedCharacters <= depth * 2)
376         stream << " ";
377 }
378
379 void outputLineLayoutForFlow(TextStream& stream, const RenderBlockFlow& flow, const Layout& layout, int depth)
380 {
381     int printedCharacters = 0;
382     printPrefix(stream, printedCharacters, depth);
383
384     stream << "SimpleLineLayout (" << layout.lineCount() << " lines, " << layout.runCount() << " runs) (" << &layout << ")";
385     stream.nextLine();
386     ++depth;
387
388     for (auto run : runResolver(flow, layout)) {
389         FloatRect rect = run.rect();
390         printPrefix(stream, printedCharacters, depth);
391         if (run.start() < run.end()) {
392             stream << "line " << run.lineIndex() << " run(" << run.start() << ", " << run.end() << ") " << rect << " \"" << run.text().toStringWithoutCopying().utf8().data() << "\"";
393         } else {
394             ASSERT(run.start() == run.end());
395             stream << "line break " << run.lineIndex() << " run(" << run.start() << ", " << run.end() << ") " << rect;
396         }
397     }
398     stream.nextLine();
399 }
400 #endif
401
402 }
403 }