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