Simple line layout:: Add text-decoration support.
[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 "FontCache.h"
30 #include "Frame.h"
31 #include "GraphicsContext.h"
32 #include "HitTestLocation.h"
33 #include "HitTestRequest.h"
34 #include "HitTestResult.h"
35 #include "InlineTextBox.h"
36 #include "PaintInfo.h"
37 #include "RenderBlockFlow.h"
38 #include "RenderIterator.h"
39 #include "RenderStyle.h"
40 #include "RenderText.h"
41 #include "RenderView.h"
42 #include "Settings.h"
43 #include "SimpleLineLayoutResolver.h"
44 #include "Text.h"
45 #include "TextDecorationPainter.h"
46 #include "TextPaintStyle.h"
47 #include "TextPainter.h"
48
49 #if ENABLE(TREE_DEBUGGING)
50 #include <stdio.h>
51 #endif
52
53 namespace WebCore {
54 namespace SimpleLineLayout {
55
56 static void paintDebugBorders(GraphicsContext& context, LayoutRect borderRect, const LayoutPoint& paintOffset)
57 {
58     borderRect.moveBy(paintOffset);
59     IntRect snappedRect = snappedIntRect(borderRect);
60     if (snappedRect.isEmpty())
61         return;
62     GraphicsContextStateSaver stateSaver(context);
63     context.setStrokeColor(Color(0, 255, 0));
64     context.setFillColor(Color::transparent);
65     context.drawRect(snappedRect);
66 }
67
68 void paintFlow(const RenderBlockFlow& flow, const Layout& layout, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
69 {
70     if (paintInfo.phase != PaintPhaseForeground)
71         return;
72
73     RenderStyle& style = flow.style();
74     if (style.visibility() != VISIBLE)
75         return;
76
77     bool debugBordersEnabled = flow.frame().settings().simpleLineLayoutDebugBordersEnabled();
78
79     TextPainter textPainter(paintInfo.context());
80     textPainter.setFont(style.fontCascade());
81     textPainter.setTextPaintStyle(computeTextPaintStyle(flow.frame(), style, paintInfo));
82
83     Optional<TextDecorationPainter> textDecorationPainter;
84     if (style.textDecorationsInEffect() != TextDecorationNone) {
85         const RenderText* textRenderer = childrenOfType<RenderText>(flow).first();
86         if (textRenderer) {
87             textDecorationPainter = TextDecorationPainter(paintInfo.context(), style.textDecorationsInEffect(), *textRenderer, false);
88             textDecorationPainter->setFont(style.fontCascade());
89             textDecorationPainter->setBaseline(style.fontMetrics().ascent());
90         }
91     }
92
93     LayoutRect paintRect = paintInfo.rect;
94     paintRect.moveBy(-paintOffset);
95
96     auto resolver = runResolver(flow, layout);
97     float strokeOverflow = ceilf(flow.style().textStrokeWidth());
98     float deviceScaleFactor = flow.document().deviceScaleFactor();
99     for (const auto& run : resolver.rangeForRect(paintRect)) {
100         if (run.start() == run.end())
101             continue;
102
103         FloatRect rect = run.rect();
104         FloatRect visualOverflowRect = rect;
105         visualOverflowRect.inflate(strokeOverflow);
106         if (paintRect.y() > visualOverflowRect.maxY() || paintRect.maxY() < visualOverflowRect.y())
107             continue;
108
109         TextRun textRun(run.text());
110         textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
111         // x position indicates the line offset from the rootbox. It's always 0 in case of simple line layout.
112         textRun.setXPos(0);
113         FloatPoint textOrigin = FloatPoint(rect.x() + paintOffset.x(), roundToDevicePixel(run.baselinePosition() + paintOffset.y(), deviceScaleFactor));
114         textPainter.paintText(textRun, textRun.length(), rect, textOrigin);
115         if (textDecorationPainter) {
116             textDecorationPainter->setWidth(rect.width());
117             textDecorationPainter->paintTextDecoration(textRun, textOrigin, rect.location() + paintOffset);
118         }
119         if (debugBordersEnabled)
120             paintDebugBorders(paintInfo.context(), LayoutRect(run.rect()), paintOffset);
121     }
122 }
123
124 bool hitTestFlow(const RenderBlockFlow& flow, const Layout& layout, const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
125 {
126     if (hitTestAction != HitTestForeground)
127         return false;
128
129     if (!layout.runCount())
130         return false;
131
132     RenderStyle& style = flow.style();
133     if (style.visibility() != VISIBLE || style.pointerEvents() == PE_NONE)
134         return false;
135
136     RenderObject& renderer = *flow.firstChild();
137     LayoutRect rangeRect = locationInContainer.boundingBox();
138     rangeRect.moveBy(-accumulatedOffset);
139
140     auto resolver = lineResolver(flow, layout);
141     auto range = resolver.rangeForRect(rangeRect);
142     for (auto it = range.begin(), end = range.end(); it != end; ++it) {
143         auto lineRect = *it;
144         lineRect.moveBy(accumulatedOffset);
145         if (!locationInContainer.intersects(lineRect))
146             continue;
147         renderer.updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset));
148         if (!result.addNodeToRectBasedTestResult(renderer.node(), request, locationInContainer, lineRect))
149             return true;
150     }
151
152     return false;
153 }
154
155 void collectFlowOverflow(RenderBlockFlow& flow, const Layout& layout)
156 {
157     auto resolver = lineResolver(flow, layout);
158     float strokeOverflow = ceilf(flow.style().textStrokeWidth());
159     for (auto it = resolver.begin(), end = resolver.end(); it != end; ++it) {
160         auto rect = LayoutRect(*it);
161         rect.inflate(strokeOverflow);
162         flow.addLayoutOverflow(rect);
163         flow.addVisualOverflow(rect);
164     }
165 }
166
167 IntRect computeBoundingBox(const RenderObject& renderer, const Layout& layout)
168 {
169     auto resolver = runResolver(downcast<RenderBlockFlow>(*renderer.parent()), layout);
170     FloatRect boundingBoxRect;
171     for (const auto& run : resolver.rangeForRenderer(renderer)) {
172         FloatRect rect = run.rect();
173         if (boundingBoxRect == FloatRect())
174             boundingBoxRect = rect;
175         else
176             boundingBoxRect.uniteEvenIfEmpty(rect);
177     }
178     return enclosingIntRect(boundingBoxRect);
179 }
180
181 IntPoint computeFirstRunLocation(const RenderObject& renderer, const Layout& layout)
182 {
183     auto resolver = runResolver(downcast<RenderBlockFlow>(*renderer.parent()), layout);
184     const auto& it = resolver.rangeForRenderer(renderer);
185     auto begin = it.begin();
186     if (begin == it.end())
187         return IntPoint(0, 0);
188
189     return flooredIntPoint((*begin).rect().location());
190 }
191
192 Vector<IntRect> collectAbsoluteRects(const RenderObject& renderer, const Layout& layout, const LayoutPoint& accumulatedOffset)
193 {
194     Vector<IntRect> rects;
195     auto resolver = runResolver(downcast<RenderBlockFlow>(*renderer.parent()), layout);
196     for (const auto& run : resolver.rangeForRenderer(renderer)) {
197         FloatRect rect = run.rect();
198         rects.append(enclosingIntRect(FloatRect(accumulatedOffset + rect.location(), rect.size())));
199     }
200     return rects;
201 }
202
203 Vector<FloatQuad> collectAbsoluteQuads(const RenderObject& renderer, const Layout& layout, bool* wasFixed)
204 {
205     Vector<FloatQuad> quads;
206     auto resolver = runResolver(downcast<RenderBlockFlow>(*renderer.parent()), layout);
207     for (const auto& run : resolver.rangeForRenderer(renderer))
208         quads.append(renderer.localToAbsoluteQuad(FloatQuad(run.rect()), UseTransforms, wasFixed));
209     return quads;
210 }
211
212 #if ENABLE(TREE_DEBUGGING)
213 static void printPrefix(int& printedCharacters, int depth)
214 {
215     fprintf(stderr, "------- --");
216     printedCharacters = 0;
217     while (++printedCharacters <= depth * 2)
218         fputc(' ', stderr);
219 }
220
221 void showLineLayoutForFlow(const RenderBlockFlow& flow, const Layout& layout, int depth)
222 {
223     int printedCharacters = 0;
224     printPrefix(printedCharacters, depth);
225
226     fprintf(stderr, "SimpleLineLayout (%u lines, %u runs) (%p)\n", layout.lineCount(), layout.runCount(), &layout);
227     ++depth;
228
229     auto resolver = runResolver(flow, layout);
230     for (auto it = resolver.begin(), end = resolver.end(); it != end; ++it) {
231         const auto& run = *it;
232         FloatRect rect = run.rect();
233         printPrefix(printedCharacters, depth);
234         if (run.start() < run.end()) {
235             fprintf(stderr, "line %u run(%u, %u) (%.2f, %.2f) (%.2f, %.2f) \"%s\"\n", run.lineIndex(), run.start(), run.end(),
236                 rect.x(), rect.y(), rect.width(), rect.height(), run.text().toStringWithoutCopying().utf8().data());
237         } else {
238             ASSERT(run.start() == run.end());
239             fprintf(stderr, "line break %u run(%u, %u) (%.2f, %.2f) (%.2f, %.2f)\n", run.lineIndex(), run.start(), run.end(), rect.x(), rect.y(), rect.width(), rect.height());
240         }
241     }
242 }
243 #endif
244
245 }
246 }