Try to fix build without CSS_SHAPES.
[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 "GraphicsContext.h"
31 #include "HitTestLocation.h"
32 #include "HitTestRequest.h"
33 #include "HitTestResult.h"
34 #include "InlineTextBox.h"
35 #include "LineWidth.h"
36 #include "PaintInfo.h"
37 #include "RenderBlockFlow.h"
38 #include "RenderStyle.h"
39 #include "RenderText.h"
40 #include "RenderView.h"
41 #include "SimpleLineLayoutResolver.h"
42 #include "Text.h"
43 #include "TextPaintStyle.h"
44 #include "break_lines.h"
45 #include <wtf/unicode/Unicode.h>
46
47 namespace WebCore {
48 namespace SimpleLineLayout {
49
50 static inline bool isWhitespace(UChar character)
51 {
52     return character == ' ' || character == '\t' || character == '\n';
53 }
54
55 bool canUseFor(const RenderBlockFlow& flow)
56 {
57     if (!flow.firstChild())
58         return false;
59     // This currently covers <blockflow>#text</blockflow> case.
60     // The <blockflow><inline>#text</inline></blockflow> case is also popular and should be relatively easy to cover.
61     if (flow.firstChild() != flow.lastChild())
62         return false;
63     if (!flow.firstChild()->isText())
64         return false;
65     // Supporting floats would be very beneficial.
66     if (flow.containsFloats())
67         return false;
68     if (!flow.isHorizontalWritingMode())
69         return false;
70     if (flow.flowThreadState() != RenderObject::NotInsideFlowThread)
71         return false;
72     if (flow.hasOutline())
73         return false;
74     if (flow.isRubyText() || flow.isRubyBase())
75         return false;
76     // These tests only works during layout. Outside layout this function may give false positives.
77     if (flow.view().layoutState()) {
78 #if ENABLE(CSS_SHAPES)
79         if (flow.view().layoutState()->shapeInsideInfo())
80             return false;
81 #endif
82         if (flow.view().layoutState()->m_columnInfo)
83             return false;
84     }
85     const RenderStyle& style = *flow.style();
86     // It shoudn't be hard to support other alignments.
87     if (style.textAlign() != LEFT && style.textAlign() != WEBKIT_LEFT && style.textAlign() != TASTART)
88         return false;
89     // Non-visible overflow should be pretty easy to support.
90     if (style.overflowX() != OVISIBLE || style.overflowY() != OVISIBLE)
91         return false;
92     // Pre/no-wrap would be very helpful to support.
93     if (style.whiteSpace() != NORMAL)
94         return false;
95     if (!style.textIndent().isZero())
96         return false;
97     if (style.wordSpacing() || style.letterSpacing())
98         return false;
99     if (style.textTransform() != TTNONE)
100         return false;
101     if (!style.isLeftToRightDirection())
102         return false;
103     if (style.lineBoxContain() != RenderStyle::initialLineBoxContain())
104         return false;
105     if (style.writingMode() != TopToBottomWritingMode)
106         return false;
107     if (style.lineBreak() != LineBreakAuto)
108         return false;
109     if (style.wordBreak() != NormalWordBreak)
110         return false;
111     if (style.unicodeBidi() != UBNormal || style.rtlOrdering() != LogicalOrder)
112         return false;
113     if (style.lineAlign() != LineAlignNone || style.lineSnap() != LineSnapNone)
114         return false;
115     if (style.hyphens() == HyphensAuto)
116         return false;
117     if (style.textEmphasisFill() != TextEmphasisFillFilled || style.textEmphasisMark() != TextEmphasisMarkNone)
118         return false;
119     if (style.textShadow())
120         return false;
121 #if ENABLE(CSS_SHAPES)
122     if (style.resolvedShapeInside())
123         return true;
124 #endif
125     if (style.textOverflow() || (flow.isAnonymousBlock() && flow.parent()->style()->textOverflow()))
126         return false;
127     if (style.hasPseudoStyle(FIRST_LINE) || style.hasPseudoStyle(FIRST_LETTER))
128         return false;
129     if (style.hasTextCombine())
130         return false;
131     if (style.overflowWrap() != NormalOverflowWrap)
132         return false;
133     if (style.backgroundClip() == TextFillBox)
134         return false;
135     if (style.borderFit() == BorderFitLines)
136         return false;
137     const RenderText& textRenderer = toRenderText(*flow.firstChild());
138     if (textRenderer.isCombineText() || textRenderer.isCounter() || textRenderer.isQuote() || textRenderer.isSVGInlineText() || textRenderer.isTextFragment())
139         return false;
140     if (style.font().codePath(TextRun(textRenderer.text())) != Font::Simple)
141         return false;
142     if (!textRenderer.knownToHaveNoOverflowAndNoFallbackFonts())
143         return false;
144
145     unsigned length = textRenderer.textLength();
146     unsigned consecutiveSpaceCount = 0;
147     for (unsigned i = 0; i < length; ++i) {
148         // This rejects anything with more than one consecutive whitespace, except at the beginning or end.
149         // This is because we don't currently do subruns within lines. Fixing this would improve coverage significantly.
150         UChar character = textRenderer.characterAt(i);
151         if (isWhitespace(character))
152             ++consecutiveSpaceCount;
153         else {
154             if (consecutiveSpaceCount != i && consecutiveSpaceCount > 1)
155                 return false;
156             consecutiveSpaceCount = 0;
157         }
158         // These would be easy to support.
159         if (character == noBreakSpace)
160             return false;
161         if (character == softHyphen)
162             return false;
163
164         static const UChar lowestRTLCharacter = 0x590;
165         if (character >= lowestRTLCharacter) {
166             UCharDirection direction = u_charDirection(character);
167             if (direction == U_RIGHT_TO_LEFT || direction == U_RIGHT_TO_LEFT_ARABIC
168                 || direction == U_RIGHT_TO_LEFT_EMBEDDING || direction == U_RIGHT_TO_LEFT_OVERRIDE
169                 || direction == U_LEFT_TO_RIGHT_EMBEDDING || direction == U_LEFT_TO_RIGHT_OVERRIDE)
170                 return false;
171         }
172     }
173     return true;
174 }
175
176 static inline unsigned skipWhitespaces(const RenderText& textRenderer, unsigned offset, unsigned length)
177 {
178     for (; offset < length; ++offset) {
179         if (!isWhitespace(textRenderer.characterAt(offset)))
180             break;
181     }
182     return offset;
183 }
184
185 static float textWidth(const RenderText& text, unsigned from, unsigned length, float xPosition, const RenderStyle& style)
186 {
187     if (style.font().isFixedPitch() || (!from && length == text.textLength()))
188         return text.width(from, length, style.font(), xPosition, nullptr, nullptr);
189     // FIXME: Add templated UChar/LChar paths.
190     TextRun run = text.is8Bit() ? TextRun(text.characters8() + from, length) : TextRun(text.characters16() + from, length);
191     run.setCharactersLength(text.textLength() - from);
192     ASSERT(run.charactersLength() >= run.length());
193
194     run.setXPos(xPosition);
195     return style.font().width(run);
196 }
197
198 std::unique_ptr<Lines> createLines(RenderBlockFlow& flow)
199 {
200     auto lines = std::make_unique<Lines>();
201
202     RenderText& textRenderer = toRenderText(*flow.firstChild());
203     ASSERT(!textRenderer.firstTextBox());
204
205     const RenderStyle& style = *flow.style();
206     const unsigned textLength = textRenderer.textLength();
207
208     float wordTrailingSpaceWidth = style.font().width(TextRun(&space, 1));
209
210     LazyLineBreakIterator lineBreakIterator(textRenderer.text(), style.locale());
211     int nextBreakable = -1;
212
213     unsigned lineEndOffset = 0;
214     while (lineEndOffset < textLength) {
215         lineEndOffset = skipWhitespaces(textRenderer, lineEndOffset, textLength);
216         unsigned lineStartOffset = lineEndOffset;
217         unsigned runEndOffset = lineEndOffset;
218         LineWidth lineWidth(flow, false, DoNotIndentText);
219         while (runEndOffset < textLength) {
220             ASSERT(!isWhitespace(textRenderer.characterAt(runEndOffset)));
221
222             bool previousWasSpaceBetweenRuns = runEndOffset > lineStartOffset && isWhitespace(textRenderer.characterAt(runEndOffset - 1));
223             unsigned runStartOffset = previousWasSpaceBetweenRuns ? runEndOffset - 1 : runEndOffset;
224
225             ++runEndOffset;
226             while (runEndOffset < textLength) {
227                 if (runEndOffset > lineStartOffset && isBreakable(lineBreakIterator, runEndOffset, nextBreakable, false))
228                     break;
229                 ++runEndOffset;
230             }
231
232             unsigned runLength = runEndOffset - runStartOffset;
233             bool includeEndSpace = runEndOffset < textLength && textRenderer.characterAt(runEndOffset) == ' ';
234             float wordWidth;
235             if (includeEndSpace)
236                 wordWidth = textWidth(textRenderer, runStartOffset, runLength + 1, lineWidth.committedWidth(), style) - wordTrailingSpaceWidth;
237             else
238                 wordWidth = textWidth(textRenderer, runStartOffset, runLength, lineWidth.committedWidth(), style);
239
240             lineWidth.addUncommittedWidth(wordWidth);
241             if (!lineWidth.fitsOnLine()) {
242                 if (!lineWidth.committedWidth()) {
243                     lineWidth.commit();
244                     lineEndOffset = runEndOffset;
245                 }
246                 break;
247             }
248             lineWidth.commit();
249             lineEndOffset = runEndOffset;
250             runEndOffset = skipWhitespaces(textRenderer, runEndOffset, textLength);
251         }
252         if (lineStartOffset == lineEndOffset)
253             continue;
254
255         Line line;
256         line.textOffset = lineStartOffset;
257         line.textLength = lineEndOffset - lineStartOffset;
258         line.width = lineWidth.committedWidth();
259
260         lines->append(line);
261     }
262
263     textRenderer.clearNeedsLayout();
264
265     lines->shrinkToFit();
266     return lines;
267 }
268
269 }
270 }