cfbaa8b4abe7aa2634353a961366ae2a1b6065f6
[WebKit-https.git] / Source / WebCore / rendering / SimpleLineLayout.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 "SimpleLineLayout.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 "LineWidth.h"
37 #include "PaintInfo.h"
38 #include "RenderBlockFlow.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 "TextPaintStyle.h"
46 #include "break_lines.h"
47 #include <wtf/unicode/Unicode.h>
48
49 namespace WebCore {
50 namespace SimpleLineLayout {
51
52 static inline bool isWhitespace(UChar character)
53 {
54     return character == ' ' || character == '\t' || character == '\n';
55 }
56
57 bool canUseFor(const RenderBlockFlow& flow)
58 {
59 #if !PLATFORM(MAC) && !PLATFORM(GTK) && !PLATFORM(EFL)
60     // FIXME: Non-mac platforms are hitting ASSERT(run.charactersLength() >= run.length())
61     // https://bugs.webkit.org/show_bug.cgi?id=123338
62     return false;
63 #endif
64     if (!flow.frame().settings().simpleLineLayoutEnabled())
65         return false;
66     if (!flow.firstChild())
67         return false;
68     // This currently covers <blockflow>#text</blockflow> case.
69     // The <blockflow><inline>#text</inline></blockflow> case is also popular and should be relatively easy to cover.
70     if (flow.firstChild() != flow.lastChild())
71         return false;
72     if (!flow.firstChild()->isText())
73         return false;
74     // Supporting floats would be very beneficial.
75     if (flow.containsFloats())
76         return false;
77     if (!flow.isHorizontalWritingMode())
78         return false;
79     if (flow.flowThreadState() != RenderObject::NotInsideFlowThread)
80         return false;
81     if (flow.hasOutline())
82         return false;
83     if (flow.isRubyText() || flow.isRubyBase())
84         return false;
85     if (flow.parent()->isDeprecatedFlexibleBox())
86         return false;
87     // These tests only works during layout. Outside layout this function may give false positives.
88     if (flow.view().layoutState()) {
89 #if ENABLE(CSS_SHAPES)
90         if (flow.view().layoutState()->shapeInsideInfo())
91             return false;
92 #endif
93         if (flow.view().layoutState()->m_columnInfo)
94             return false;
95     }
96     const RenderStyle& style = flow.style();
97     if (style.textDecorationsInEffect() != TextDecorationNone)
98         return false;
99     if (style.textAlign() == JUSTIFY)
100         return false;
101     // Non-visible overflow should be pretty easy to support.
102     if (style.overflowX() != OVISIBLE || style.overflowY() != OVISIBLE)
103         return false;
104     // Pre/no-wrap would be very helpful to support.
105     if (style.whiteSpace() != NORMAL)
106         return false;
107     if (!style.textIndent().isZero())
108         return false;
109     if (style.wordSpacing() || style.letterSpacing())
110         return false;
111     if (style.textTransform() != TTNONE)
112         return false;
113     if (!style.isLeftToRightDirection())
114         return false;
115     if (style.lineBoxContain() != RenderStyle::initialLineBoxContain())
116         return false;
117     if (style.writingMode() != TopToBottomWritingMode)
118         return false;
119     if (style.lineBreak() != LineBreakAuto)
120         return false;
121     if (style.wordBreak() != NormalWordBreak)
122         return false;
123     if (style.unicodeBidi() != UBNormal || style.rtlOrdering() != LogicalOrder)
124         return false;
125     if (style.lineAlign() != LineAlignNone || style.lineSnap() != LineSnapNone)
126         return false;
127     if (style.hyphens() == HyphensAuto)
128         return false;
129     if (style.textEmphasisFill() != TextEmphasisFillFilled || style.textEmphasisMark() != TextEmphasisMarkNone)
130         return false;
131     if (style.textShadow())
132         return false;
133 #if ENABLE(CSS_SHAPES)
134     if (style.resolvedShapeInside())
135         return true;
136 #endif
137     if (style.textOverflow() || (flow.isAnonymousBlock() && flow.parent()->style().textOverflow()))
138         return false;
139     if (style.hasPseudoStyle(FIRST_LINE) || style.hasPseudoStyle(FIRST_LETTER))
140         return false;
141     if (style.hasTextCombine())
142         return false;
143     if (style.overflowWrap() != NormalOverflowWrap)
144         return false;
145     if (style.backgroundClip() == TextFillBox)
146         return false;
147     if (style.borderFit() == BorderFitLines)
148         return false;
149     const RenderText& textRenderer = toRenderText(*flow.firstChild());
150     if (textRenderer.isCombineText() || textRenderer.isCounter() || textRenderer.isQuote() || textRenderer.isTextFragment()
151 #if ENABLE(SVG)
152         || textRenderer.isSVGInlineText()
153 #endif
154         )
155         return false;
156     if (style.font().codePath(TextRun(textRenderer.text())) != Font::Simple)
157         return false;
158
159     // We assume that all lines have metrics based purely on the primary font.
160     auto& primaryFontData = *style.font().primaryFont();
161     if (primaryFontData.isLoading())
162         return false;
163
164     unsigned length = textRenderer.textLength();
165     for (unsigned i = 0; i < length; ++i) {
166         UChar character = textRenderer.characterAt(i);
167         if (character == ' ')
168             continue;
169
170         // These would be easy to support.
171         if (character == noBreakSpace)
172             return false;
173         if (character == softHyphen)
174             return false;
175
176         UCharDirection direction = u_charDirection(character);
177         if (direction == U_RIGHT_TO_LEFT || direction == U_RIGHT_TO_LEFT_ARABIC
178             || direction == U_RIGHT_TO_LEFT_EMBEDDING || direction == U_RIGHT_TO_LEFT_OVERRIDE
179             || direction == U_LEFT_TO_RIGHT_EMBEDDING || direction == U_LEFT_TO_RIGHT_OVERRIDE
180             || direction == U_POP_DIRECTIONAL_FORMAT || direction == U_BOUNDARY_NEUTRAL)
181             return false;
182
183         if (!primaryFontData.glyphForCharacter(character))
184             return false;
185     }
186     return true;
187 }
188
189 static inline unsigned skipWhitespaces(const RenderText& textRenderer, unsigned offset, unsigned length)
190 {
191     for (; offset < length; ++offset) {
192         if (!isWhitespace(textRenderer.characterAt(offset)))
193             break;
194     }
195     return offset;
196 }
197
198 static float textWidth(const RenderText& text, unsigned from, unsigned length, float xPosition, const RenderStyle& style)
199 {
200     if (style.font().isFixedPitch() || (!from && length == text.textLength()))
201         return text.width(from, length, style.font(), xPosition, nullptr, nullptr);
202     // FIXME: Add templated UChar/LChar paths.
203     TextRun run = text.is8Bit() ? TextRun(text.characters8() + from, length) : TextRun(text.characters16() + from, length);
204     run.setCharactersLength(text.textLength() - from);
205     ASSERT(run.charactersLength() >= run.length());
206
207     run.setXPos(xPosition);
208     return style.font().width(run);
209 }
210
211 static float computeLineLeft(ETextAlign textAlign, float remainingWidth)
212 {
213     switch (textAlign) {
214     case LEFT:
215     case WEBKIT_LEFT:
216     case TASTART:
217         return 0;
218     case RIGHT:
219     case WEBKIT_RIGHT:
220     case TAEND:
221         return std::max<float>(remainingWidth, 0);
222     case CENTER:
223     case WEBKIT_CENTER:
224         return std::max<float>(remainingWidth / 2, 0);
225     case JUSTIFY:
226         break;
227     }
228     ASSERT_NOT_REACHED();
229     return 0;
230 }
231
232 static void adjustRunOffsets(Vector<Run, 4>& lineRuns, ETextAlign textAlign, float lineWidth, float availableWidth)
233 {
234     float lineLeft = computeLineLeft(textAlign, availableWidth - lineWidth);
235     for (unsigned i = 0; i < lineRuns.size(); ++i) {
236         lineRuns[i].left = floor(lineLeft + lineRuns[i].left);
237         lineRuns[i].right = ceil(lineLeft + lineRuns[i].right);
238     }
239 }
240
241 std::unique_ptr<Layout> create(RenderBlockFlow& flow)
242 {
243     RenderText& textRenderer = toRenderText(*flow.firstChild());
244     ASSERT(!textRenderer.firstTextBox());
245
246     const RenderStyle& style = flow.style();
247     const unsigned textLength = textRenderer.textLength();
248
249     ETextAlign textAlign = style.textAlign();
250     float wordTrailingSpaceWidth = style.font().width(TextRun(&space, 1));
251
252     LazyLineBreakIterator lineBreakIterator(textRenderer.text(), style.locale());
253     int nextBreakable = -1;
254
255     Layout::RunVector runs;
256     unsigned lineCount = 0;
257
258     unsigned lineEndOffset = 0;
259     while (lineEndOffset < textLength) {
260         lineEndOffset = skipWhitespaces(textRenderer, lineEndOffset, textLength);
261         unsigned lineStartOffset = lineEndOffset;
262         unsigned wordEndOffset = lineEndOffset;
263         LineWidth lineWidth(flow, false, DoNotIndentText);
264
265         Vector<Run, 4> lineRuns;
266         lineRuns.uncheckedAppend(Run(lineStartOffset, 0));
267
268         while (wordEndOffset < textLength) {
269             ASSERT(!isWhitespace(textRenderer.characterAt(wordEndOffset)));
270
271             bool previousWasSpaceBetweenWords = wordEndOffset > lineStartOffset && isWhitespace(textRenderer.characterAt(wordEndOffset - 1));
272             unsigned wordStartOffset = previousWasSpaceBetweenWords ? wordEndOffset - 1 : wordEndOffset;
273
274             ++wordEndOffset;
275             while (wordEndOffset < textLength) {
276                 if (wordEndOffset > lineStartOffset && isBreakable(lineBreakIterator, wordEndOffset, nextBreakable, false))
277                     break;
278                 ++wordEndOffset;
279             }
280
281             unsigned wordLength = wordEndOffset - wordStartOffset;
282             bool includeEndSpace = wordEndOffset < textLength && textRenderer.characterAt(wordEndOffset) == ' ';
283             float wordWidth;
284             if (includeEndSpace)
285                 wordWidth = textWidth(textRenderer, wordStartOffset, wordLength + 1, lineWidth.committedWidth(), style) - wordTrailingSpaceWidth;
286             else
287                 wordWidth = textWidth(textRenderer, wordStartOffset, wordLength, lineWidth.committedWidth(), style);
288
289             lineWidth.addUncommittedWidth(wordWidth);
290
291             // Move to the next line if the current one is full and we have something on it.
292             if (!lineWidth.fitsOnLine() && lineWidth.committedWidth())
293                 break;
294
295             if (wordStartOffset > lineEndOffset) {
296                 // There were more than one consecutive whitespace.
297                 ASSERT(previousWasSpaceBetweenWords);
298                 // Include space to the end of the previous run.
299                 lineRuns.last().textLength++;
300                 lineRuns.last().right += wordTrailingSpaceWidth;
301                 // Start a new run on the same line.
302                 lineRuns.append(Run(wordStartOffset + 1, lineRuns.last().right));
303             }
304
305             lineWidth.commit();
306
307             lineRuns.last().right = lineWidth.committedWidth();
308             lineRuns.last().textLength = wordEndOffset - lineRuns.last().textOffset;
309
310             lineEndOffset = wordEndOffset;
311             wordEndOffset = skipWhitespaces(textRenderer, wordEndOffset, textLength);
312
313             if (!lineWidth.fitsOnLine()) {
314                 // The first run on the line overflows.
315                 ASSERT(lineRuns.size() == 1);
316                 break;
317             }
318         }
319         if (lineStartOffset == lineEndOffset)
320             continue;
321
322         adjustRunOffsets(lineRuns, textAlign, lineWidth.committedWidth(), lineWidth.availableWidth());
323
324         for (unsigned i = 0; i < lineRuns.size(); ++i)
325             runs.append(lineRuns[i]);
326
327         runs.last().isEndOfLine = true;
328         ++lineCount;
329     }
330
331     textRenderer.clearNeedsLayout();
332
333     return Layout::create(runs, lineCount);
334 }
335
336 std::unique_ptr<Layout> Layout::create(const RunVector& runVector, unsigned lineCount)
337 {
338     void* slot = WTF::fastMalloc(sizeof(Layout) + sizeof(Run) * runVector.size());
339     return std::unique_ptr<Layout>(new (NotNull, slot) Layout(runVector, lineCount));
340 }
341
342 Layout::Layout(const RunVector& runVector, unsigned lineCount)
343     : m_lineCount(lineCount)
344     , m_runCount(runVector.size())
345 {
346     memcpy(m_runs, runVector.data(), m_runCount * sizeof(Run));
347 }
348
349 }
350 }