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