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