Simple line layout
[WebKit-https.git] / Source / WebCore / rendering / RenderText.cpp
1 /*
2  * (C) 1999 Lars Knoll (knoll@kde.org)
3  * (C) 2000 Dirk Mueller (mueller@kde.org)
4  * Copyright (C) 2004, 2005, 2006, 2007, 2013 Apple Inc. All rights reserved.
5  * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
6  * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com)
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24
25 #include "config.h"
26 #include "RenderText.h"
27
28 #include "AXObjectCache.h"
29 #include "EllipsisBox.h"
30 #include "FloatQuad.h"
31 #include "Frame.h"
32 #include "FrameView.h"
33 #include "Hyphenation.h"
34 #include "InlineTextBox.h"
35 #include "Range.h"
36 #include "RenderArena.h"
37 #include "RenderBlock.h"
38 #include "RenderCombineText.h"
39 #include "RenderLayer.h"
40 #include "RenderView.h"
41 #include "Settings.h"
42 #include "SimpleLineLayoutFunctions.h"
43 #include "Text.h"
44 #include "TextBreakIterator.h"
45 #include "TextResourceDecoder.h"
46 #include "VisiblePosition.h"
47 #include "break_lines.h"
48 #include <wtf/text/StringBuffer.h>
49 #include <wtf/unicode/CharacterNames.h>
50
51 using namespace std;
52 using namespace WTF;
53 using namespace Unicode;
54
55 namespace WebCore {
56
57 struct SameSizeAsRenderText : public RenderObject {
58     uint32_t bitfields : 16;
59 #if ENABLE(IOS_TEXT_AUTOSIZING)
60     float candidateTextSize;
61 #endif
62     float widths[4];
63     String text;
64     void* pointers[2];
65 };
66
67 COMPILE_ASSERT(sizeof(RenderText) == sizeof(SameSizeAsRenderText), RenderText_should_stay_small);
68
69 class SecureTextTimer;
70 typedef HashMap<RenderText*, SecureTextTimer*> SecureTextTimerMap;
71 static SecureTextTimerMap* gSecureTextTimers = 0;
72
73 class SecureTextTimer : public TimerBase {
74 public:
75     SecureTextTimer(RenderText* renderText)
76         : m_renderText(renderText)
77         , m_lastTypedCharacterOffset(-1)
78     {
79     }
80
81     void restartWithNewText(unsigned lastTypedCharacterOffset)
82     {
83         m_lastTypedCharacterOffset = lastTypedCharacterOffset;
84         const Settings& settings = m_renderText->frame().settings();
85         startOneShot(settings.passwordEchoDurationInSeconds());
86     }
87     void invalidate() { m_lastTypedCharacterOffset = -1; }
88     unsigned lastTypedCharacterOffset() { return m_lastTypedCharacterOffset; }
89
90 private:
91     virtual void fired()
92     {
93         ASSERT(gSecureTextTimers->contains(m_renderText));
94         m_renderText->setText(m_renderText->text(), true /* forcing setting text as it may be masked later */);
95     }
96
97     RenderText* m_renderText;
98     int m_lastTypedCharacterOffset;
99 };
100
101 static void makeCapitalized(String* string, UChar previous)
102 {
103     // FIXME: Need to change this to use u_strToTitle instead of u_totitle and to consider locale.
104
105     if (string->isNull())
106         return;
107
108     unsigned length = string->length();
109     const StringImpl& stringImpl = *string->impl();
110
111     if (length >= numeric_limits<unsigned>::max())
112         CRASH();
113
114     StringBuffer<UChar> stringWithPrevious(length + 1);
115     stringWithPrevious[0] = previous == noBreakSpace ? ' ' : previous;
116     for (unsigned i = 1; i < length + 1; i++) {
117         // Replace &nbsp with a real space since ICU no longer treats &nbsp as a word separator.
118         if (stringImpl[i - 1] == noBreakSpace)
119             stringWithPrevious[i] = ' ';
120         else
121             stringWithPrevious[i] = stringImpl[i - 1];
122     }
123
124     TextBreakIterator* boundary = wordBreakIterator(stringWithPrevious.characters(), length + 1);
125     if (!boundary)
126         return;
127
128     StringBuilder result;
129     result.reserveCapacity(length);
130
131     int32_t endOfWord;
132     int32_t startOfWord = textBreakFirst(boundary);
133     for (endOfWord = textBreakNext(boundary); endOfWord != TextBreakDone; startOfWord = endOfWord, endOfWord = textBreakNext(boundary)) {
134         if (startOfWord) // Ignore first char of previous string
135             result.append(stringImpl[startOfWord - 1] == noBreakSpace ? noBreakSpace : u_totitle(stringWithPrevious[startOfWord]));
136         for (int i = startOfWord + 1; i < endOfWord; i++)
137             result.append(stringImpl[i - 1]);
138     }
139
140     *string = result.toString();
141 }
142
143 RenderText::RenderText(Text& textNode, const String& text)
144     : RenderObject(textNode)
145     , m_hasTab(false)
146     , m_linesDirty(false)
147     , m_containsReversedText(false)
148     , m_isAllASCII(text.containsOnlyASCII())
149     , m_knownToHaveNoOverflowAndNoFallbackFonts(false)
150     , m_useBackslashAsYenSymbol(false)
151 #if ENABLE(IOS_TEXT_AUTOSIZING)
152     , m_candidateComputedTextSize(0)
153 #endif
154     , m_minWidth(-1)
155     , m_maxWidth(-1)
156     , m_beginMinWidth(0)
157     , m_endMinWidth(0)
158     , m_text(text)
159 {
160     ASSERT(!m_text.isNull());
161     setIsText();
162     m_canUseSimpleFontCodePath = computeCanUseSimpleFontCodePath();
163     view().frameView().incrementVisuallyNonEmptyCharacterCount(textLength());
164 }
165
166 RenderText::RenderText(Document& document, const String& text)
167     : RenderObject(document)
168     , m_hasTab(false)
169     , m_linesDirty(false)
170     , m_containsReversedText(false)
171     , m_isAllASCII(text.containsOnlyASCII())
172     , m_knownToHaveNoOverflowAndNoFallbackFonts(false)
173     , m_useBackslashAsYenSymbol(false)
174 #if ENABLE(IOS_TEXT_AUTOSIZING)
175     , m_candidateComputedTextSize(0)
176 #endif
177     , m_minWidth(-1)
178     , m_maxWidth(-1)
179     , m_beginMinWidth(0)
180     , m_endMinWidth(0)
181     , m_text(text)
182 {
183     ASSERT(!m_text.isNull());
184     setIsText();
185     m_canUseSimpleFontCodePath = computeCanUseSimpleFontCodePath();
186     view().frameView().incrementVisuallyNonEmptyCharacterCount(textLength());
187 }
188
189 #ifndef NDEBUG
190
191 RenderText::~RenderText()
192 {
193 }
194
195 #endif
196
197 const char* RenderText::renderName() const
198 {
199     return "RenderText";
200 }
201
202 Text* RenderText::textNode() const
203 {
204     return toText(RenderObject::node());
205 }
206
207 bool RenderText::isTextFragment() const
208 {
209     return false;
210 }
211
212 bool RenderText::computeUseBackslashAsYenSymbol() const
213 {
214     const RenderStyle& style = *this->style();
215     const FontDescription& fontDescription = style.font().fontDescription();
216     if (style.font().useBackslashAsYenSymbol())
217         return true;
218     if (fontDescription.isSpecifiedFont())
219         return false;
220     const TextEncoding* encoding = document().decoder() ? &document().decoder()->encoding() : 0;
221     if (encoding && encoding->backslashAsCurrencySymbol() != '\\')
222         return true;
223     return false;
224 }
225
226 void RenderText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
227 {
228     // There is no need to ever schedule repaints from a style change of a text run, since
229     // we already did this for the parent of the text run.
230     // We do have to schedule layouts, though, since a style change can force us to
231     // need to relayout.
232     if (diff == StyleDifferenceLayout) {
233         setNeedsLayoutAndPrefWidthsRecalc();
234         m_knownToHaveNoOverflowAndNoFallbackFonts = false;
235     }
236
237     RenderStyle* newStyle = style();
238     bool needsResetText = false;
239     if (!oldStyle) {
240         m_useBackslashAsYenSymbol = computeUseBackslashAsYenSymbol();
241         needsResetText = m_useBackslashAsYenSymbol;
242     } else if (oldStyle->font().useBackslashAsYenSymbol() != newStyle->font().useBackslashAsYenSymbol()) {
243         m_useBackslashAsYenSymbol = computeUseBackslashAsYenSymbol();
244         needsResetText = true;
245     }
246
247     ETextTransform oldTransform = oldStyle ? oldStyle->textTransform() : TTNONE;
248     ETextSecurity oldSecurity = oldStyle ? oldStyle->textSecurity() : TSNONE;
249     if (needsResetText || oldTransform != newStyle->textTransform() || oldSecurity != newStyle->textSecurity()) 
250         transformText();
251 }
252
253 void RenderText::removeAndDestroyTextBoxes()
254 {
255     if (!documentBeingDestroyed())
256         m_lineBoxes.removeAllFromParent(*this);
257     m_lineBoxes.deleteAll(*this);
258 }
259
260 void RenderText::willBeDestroyed()
261 {
262     if (SecureTextTimer* secureTextTimer = gSecureTextTimers ? gSecureTextTimers->take(this) : 0)
263         delete secureTextTimer;
264
265     removeAndDestroyTextBoxes();
266     RenderObject::willBeDestroyed();
267 }
268
269 void RenderText::deleteLineBoxesBeforeSimpleLineLayout()
270 {
271     m_lineBoxes.deleteAll(*this);
272 }
273
274 String RenderText::originalText() const
275 {
276     return textNode() ? textNode()->data() : String();
277 }
278
279 void RenderText::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
280 {
281     // FIXME: These will go away when simple layout can do everything.
282     const_cast<RenderText&>(*this).ensureLineBoxes();
283
284     rects.appendVector(m_lineBoxes.absoluteRects(accumulatedOffset));
285 }
286
287 Vector<IntRect> RenderText::absoluteRectsForRange(unsigned start, unsigned end, bool useSelectionHeight, bool* wasFixed) const
288 {
289     const_cast<RenderText&>(*this).ensureLineBoxes();
290
291     // Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX
292     // to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this 
293     // function to take ints causes various internal mismatches. But selectionRect takes ints, and 
294     // passing UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take unsigneds, but 
295     // that would cause many ripple effects, so for now we'll just clamp our unsigned parameters to INT_MAX.
296     ASSERT(end == UINT_MAX || end <= INT_MAX);
297     ASSERT(start <= INT_MAX);
298     start = min(start, static_cast<unsigned>(INT_MAX));
299     end = min(end, static_cast<unsigned>(INT_MAX));
300     
301     return m_lineBoxes.absoluteRectsForRange(*this, start, end, useSelectionHeight, wasFixed);
302 }
303
304 Vector<FloatQuad> RenderText::absoluteQuadsClippedToEllipsis() const
305 {
306     const_cast<RenderText&>(*this).ensureLineBoxes();
307
308     return m_lineBoxes.absoluteQuads(*this, nullptr, RenderTextLineBoxes::ClipToEllipsis);
309 }
310
311 void RenderText::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
312 {
313     const_cast<RenderText&>(*this).ensureLineBoxes();
314
315     quads.appendVector(m_lineBoxes.absoluteQuads(*this, wasFixed, RenderTextLineBoxes::NoClipping));
316 }
317
318 Vector<FloatQuad> RenderText::absoluteQuadsForRange(unsigned start, unsigned end, bool useSelectionHeight, bool* wasFixed) const
319 {
320     const_cast<RenderText&>(*this).ensureLineBoxes();
321
322     // Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX
323     // to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this 
324     // function to take ints causes various internal mismatches. But selectionRect takes ints, and 
325     // passing UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take unsigneds, but 
326     // that would cause many ripple effects, so for now we'll just clamp our unsigned parameters to INT_MAX.
327     ASSERT(end == UINT_MAX || end <= INT_MAX);
328     ASSERT(start <= INT_MAX);
329     start = min(start, static_cast<unsigned>(INT_MAX));
330     end = min(end, static_cast<unsigned>(INT_MAX));
331     
332     return m_lineBoxes.absoluteQuadsForRange(*this, start, end, useSelectionHeight, wasFixed);
333 }
334
335 VisiblePosition RenderText::positionForPoint(const LayoutPoint& point)
336 {
337     ensureLineBoxes();
338
339     return m_lineBoxes.positionForPoint(*this, point);
340 }
341
342 LayoutRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, LayoutUnit* extraWidthToEndOfLine)
343 {
344     if (!inlineBox)
345         return LayoutRect();
346
347     InlineTextBox* box = toInlineTextBox(inlineBox);
348     float left = box->positionForOffset(caretOffset);
349     return box->root().computeCaretRect(left, caretWidth, extraWidthToEndOfLine);
350 }
351
352 ALWAYS_INLINE float RenderText::widthFromCache(const Font& f, int start, int len, float xPos, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow, const RenderStyle& style) const
353 {
354     if (style.hasTextCombine() && isCombineText()) {
355         const RenderCombineText& combineText = toRenderCombineText(*this);
356         if (combineText.isCombined())
357             return combineText.combinedTextWidth(f);
358     }
359
360     if (f.isFixedPitch() && !f.isSmallCaps() && m_isAllASCII && (!glyphOverflow || !glyphOverflow->computeBounds)) {
361         float monospaceCharacterWidth = f.spaceWidth();
362         float w = 0;
363         bool isSpace;
364         ASSERT(m_text);
365         StringImpl& text = *m_text.impl();
366         for (int i = start; i < start + len; i++) {
367             char c = text[i];
368             if (c <= ' ') {
369                 if (c == ' ' || c == '\n') {
370                     w += monospaceCharacterWidth;
371                     isSpace = true;
372                 } else if (c == '\t') {
373                     if (style.collapseWhiteSpace()) {
374                         w += monospaceCharacterWidth;
375                         isSpace = true;
376                     } else {
377                         w += f.tabWidth(style.tabSize(), xPos + w);
378                         isSpace = false;
379                     }
380                 } else
381                     isSpace = false;
382             } else {
383                 w += monospaceCharacterWidth;
384                 isSpace = false;
385             }
386             if (isSpace && i > start)
387                 w += f.wordSpacing();
388         }
389         return w;
390     }
391
392     TextRun run = RenderBlock::constructTextRun(const_cast<RenderText*>(this), f, this, start, len, style);
393     run.setCharactersLength(textLength() - start);
394     ASSERT(run.charactersLength() >= run.length());
395
396     run.setCharacterScanForCodePath(!canUseSimpleFontCodePath());
397     run.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
398     run.setXPos(xPos);
399     return f.width(run, fallbackFonts, glyphOverflow);
400 }
401
402 void RenderText::trimmedPrefWidths(float leadWidth,
403                                    float& beginMinW, bool& beginWS,
404                                    float& endMinW, bool& endWS,
405                                    bool& hasBreakableChar, bool& hasBreak,
406                                    float& beginMaxW, float& endMaxW,
407                                    float& minW, float& maxW, bool& stripFrontSpaces)
408 {
409     const RenderStyle& style = *this->style();
410     bool collapseWhiteSpace = style.collapseWhiteSpace();
411     if (!collapseWhiteSpace)
412         stripFrontSpaces = false;
413
414     if (m_hasTab || preferredLogicalWidthsDirty())
415         computePreferredLogicalWidths(leadWidth);
416
417     beginWS = !stripFrontSpaces && m_hasBeginWS;
418     endWS = m_hasEndWS;
419
420     int len = textLength();
421
422     if (!len || (stripFrontSpaces && text()->containsOnlyWhitespace())) {
423         beginMinW = 0;
424         endMinW = 0;
425         beginMaxW = 0;
426         endMaxW = 0;
427         minW = 0;
428         maxW = 0;
429         hasBreak = false;
430         return;
431     }
432
433     minW = m_minWidth;
434     maxW = m_maxWidth;
435
436     beginMinW = m_beginMinWidth;
437     endMinW = m_endMinWidth;
438
439     hasBreakableChar = m_hasBreakableChar;
440     hasBreak = m_hasBreak;
441
442     ASSERT(m_text);
443     StringImpl& text = *m_text.impl();
444     if (text[0] == ' ' || (text[0] == '\n' && !style.preserveNewline()) || text[0] == '\t') {
445         const Font& font = style.font(); // FIXME: This ignores first-line.
446         if (stripFrontSpaces) {
447             const UChar space = ' ';
448             float spaceWidth = font.width(RenderBlock::constructTextRun(this, font, &space, 1, style));
449             maxW -= spaceWidth;
450         } else
451             maxW += font.wordSpacing();
452     }
453
454     stripFrontSpaces = collapseWhiteSpace && m_hasEndWS;
455
456     if (!style.autoWrap() || minW > maxW)
457         minW = maxW;
458
459     // Compute our max widths by scanning the string for newlines.
460     if (hasBreak) {
461         const Font& f = style.font(); // FIXME: This ignores first-line.
462         bool firstLine = true;
463         beginMaxW = maxW;
464         endMaxW = maxW;
465         for (int i = 0; i < len; i++) {
466             int linelen = 0;
467             while (i + linelen < len && text[i + linelen] != '\n')
468                 linelen++;
469
470             if (linelen) {
471                 endMaxW = widthFromCache(f, i, linelen, leadWidth + endMaxW, 0, 0, style);
472                 if (firstLine) {
473                     firstLine = false;
474                     leadWidth = 0;
475                     beginMaxW = endMaxW;
476                 }
477                 i += linelen;
478             } else if (firstLine) {
479                 beginMaxW = 0;
480                 firstLine = false;
481                 leadWidth = 0;
482             }
483
484             if (i == len - 1)
485                 // A <pre> run that ends with a newline, as in, e.g.,
486                 // <pre>Some text\n\n<span>More text</pre>
487                 endMaxW = 0;
488         }
489     }
490 }
491
492 static inline bool isSpaceAccordingToStyle(UChar c, const RenderStyle& style)
493 {
494     return c == ' ' || (c == noBreakSpace && style.nbspMode() == SPACE);
495 }
496
497 float RenderText::minLogicalWidth() const
498 {
499     if (preferredLogicalWidthsDirty())
500         const_cast<RenderText*>(this)->computePreferredLogicalWidths(0);
501         
502     return m_minWidth;
503 }
504
505 float RenderText::maxLogicalWidth() const
506 {
507     if (preferredLogicalWidthsDirty())
508         const_cast<RenderText*>(this)->computePreferredLogicalWidths(0);
509         
510     return m_maxWidth;
511 }
512
513 bool RenderText::knownToHaveNoOverflowAndNoFallbackFonts() const
514 {
515     if (preferredLogicalWidthsDirty())
516         const_cast<RenderText*>(this)->computePreferredLogicalWidths(0);
517
518     return m_knownToHaveNoOverflowAndNoFallbackFonts;
519 }
520
521 void RenderText::computePreferredLogicalWidths(float leadWidth)
522 {
523     HashSet<const SimpleFontData*> fallbackFonts;
524     GlyphOverflow glyphOverflow;
525     computePreferredLogicalWidths(leadWidth, fallbackFonts, glyphOverflow);
526     if (fallbackFonts.isEmpty() && !glyphOverflow.left && !glyphOverflow.right && !glyphOverflow.top && !glyphOverflow.bottom)
527         m_knownToHaveNoOverflowAndNoFallbackFonts = true;
528 }
529
530 static inline float hyphenWidth(RenderText* renderer, const Font& font)
531 {
532     RenderStyle* style = renderer->style();
533     return font.width(RenderBlock::constructTextRun(renderer, font, style->hyphenString().string(), *style));
534 }
535
536 static float maxWordFragmentWidth(RenderText* renderer, const RenderStyle& style, const Font& font, const UChar* word, int wordLength, int minimumPrefixLength, int minimumSuffixLength, int& suffixStart, HashSet<const SimpleFontData*>& fallbackFonts, GlyphOverflow& glyphOverflow)
537 {
538     suffixStart = 0;
539     if (wordLength <= minimumSuffixLength)
540         return 0;
541
542     Vector<int, 8> hyphenLocations;
543     int hyphenLocation = wordLength - minimumSuffixLength;
544     while ((hyphenLocation = lastHyphenLocation(word, wordLength, hyphenLocation, style.locale())) >= minimumPrefixLength)
545         hyphenLocations.append(hyphenLocation);
546
547     if (hyphenLocations.isEmpty())
548         return 0;
549
550     hyphenLocations.reverse();
551
552     float minimumFragmentWidthToConsider = font.pixelSize() * 5 / 4 + hyphenWidth(renderer, font);
553     float maxFragmentWidth = 0;
554     for (size_t k = 0; k < hyphenLocations.size(); ++k) {
555         int fragmentLength = hyphenLocations[k] - suffixStart;
556         StringBuilder fragmentWithHyphen;
557         fragmentWithHyphen.append(word + suffixStart, fragmentLength);
558         fragmentWithHyphen.append(style.hyphenString());
559
560         TextRun run = RenderBlock::constructTextRun(renderer, font, fragmentWithHyphen.characters(), fragmentWithHyphen.length(), style);
561         run.setCharactersLength(fragmentWithHyphen.length());
562         run.setCharacterScanForCodePath(!renderer->canUseSimpleFontCodePath());
563         float fragmentWidth = font.width(run, &fallbackFonts, &glyphOverflow);
564
565         // Narrow prefixes are ignored. See tryHyphenating in RenderBlockLineLayout.cpp.
566         if (fragmentWidth <= minimumFragmentWidthToConsider)
567             continue;
568
569         suffixStart += fragmentLength;
570         maxFragmentWidth = max(maxFragmentWidth, fragmentWidth);
571     }
572
573     return maxFragmentWidth;
574 }
575
576 void RenderText::computePreferredLogicalWidths(float leadWidth, HashSet<const SimpleFontData*>& fallbackFonts, GlyphOverflow& glyphOverflow)
577 {
578     ASSERT(m_hasTab || preferredLogicalWidthsDirty() || !m_knownToHaveNoOverflowAndNoFallbackFonts);
579
580     m_minWidth = 0;
581     m_beginMinWidth = 0;
582     m_endMinWidth = 0;
583     m_maxWidth = 0;
584
585     float currMinWidth = 0;
586     float currMaxWidth = 0;
587     m_hasBreakableChar = false;
588     m_hasBreak = false;
589     m_hasTab = false;
590     m_hasBeginWS = false;
591     m_hasEndWS = false;
592
593     const RenderStyle& style = *this->style();
594     const Font& f = style.font(); // FIXME: This ignores first-line.
595     float wordSpacing = style.wordSpacing();
596     int len = textLength();
597     LazyLineBreakIterator breakIterator(m_text, style.locale());
598     bool needsWordSpacing = false;
599     bool ignoringSpaces = false;
600     bool isSpace = false;
601     bool firstWord = true;
602     bool firstLine = true;
603     int nextBreakable = -1;
604     int lastWordBoundary = 0;
605
606     // Non-zero only when kerning is enabled, in which case we measure words with their trailing
607     // space, then subtract its width.
608     float wordTrailingSpaceWidth = f.typesettingFeatures() & Kerning ? f.width(RenderBlock::constructTextRun(this, f, &space, 1, style), &fallbackFonts) + wordSpacing : 0;
609
610     // If automatic hyphenation is allowed, we keep track of the width of the widest word (or word
611     // fragment) encountered so far, and only try hyphenating words that are wider.
612     float maxWordWidth = numeric_limits<float>::max();
613     int minimumPrefixLength = 0;
614     int minimumSuffixLength = 0;
615     if (style.hyphens() == HyphensAuto && canHyphenate(style.locale())) {
616         maxWordWidth = 0;
617
618         // Map 'hyphenate-limit-{before,after}: auto;' to 2.
619         minimumPrefixLength = style.hyphenationLimitBefore();
620         if (minimumPrefixLength < 0)
621             minimumPrefixLength = 2;
622
623         minimumSuffixLength = style.hyphenationLimitAfter();
624         if (minimumSuffixLength < 0)
625             minimumSuffixLength = 2;
626     }
627
628     int firstGlyphLeftOverflow = -1;
629
630     bool breakNBSP = style.autoWrap() && style.nbspMode() == SPACE;
631     bool breakAll = (style.wordBreak() == BreakAllWordBreak || style.wordBreak() == BreakWordBreak) && style.autoWrap();
632
633     for (int i = 0; i < len; i++) {
634         UChar c = characterAt(i);
635
636         bool previousCharacterIsSpace = isSpace;
637
638         bool isNewline = false;
639         if (c == '\n') {
640             if (style.preserveNewline()) {
641                 m_hasBreak = true;
642                 isNewline = true;
643                 isSpace = false;
644             } else
645                 isSpace = true;
646         } else if (c == '\t') {
647             if (!style.collapseWhiteSpace()) {
648                 m_hasTab = true;
649                 isSpace = false;
650             } else
651                 isSpace = true;
652         } else
653             isSpace = c == ' ';
654
655         if ((isSpace || isNewline) && !i)
656             m_hasBeginWS = true;
657         if ((isSpace || isNewline) && i == len - 1)
658             m_hasEndWS = true;
659
660         if (!ignoringSpaces && style.collapseWhiteSpace() && previousCharacterIsSpace && isSpace)
661             ignoringSpaces = true;
662
663         if (ignoringSpaces && !isSpace)
664             ignoringSpaces = false;
665
666         // Ignore spaces and soft hyphens
667         if (ignoringSpaces) {
668             ASSERT(lastWordBoundary == i);
669             lastWordBoundary++;
670             continue;
671         } else if (c == softHyphen && style.hyphens() != HyphensNone) {
672             currMaxWidth += widthFromCache(f, lastWordBoundary, i - lastWordBoundary, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow, style);
673             if (firstGlyphLeftOverflow < 0)
674                 firstGlyphLeftOverflow = glyphOverflow.left;
675             lastWordBoundary = i + 1;
676             continue;
677         }
678
679         bool hasBreak = breakAll || isBreakable(breakIterator, i, nextBreakable, breakNBSP);
680         bool betweenWords = true;
681         int j = i;
682         while (c != '\n' && !isSpaceAccordingToStyle(c, style) && c != '\t' && (c != softHyphen || style.hyphens() == HyphensNone)) {
683             j++;
684             if (j == len)
685                 break;
686             c = characterAt(j);
687             if (isBreakable(breakIterator, j, nextBreakable, breakNBSP) && characterAt(j - 1) != softHyphen)
688                 break;
689             if (breakAll) {
690                 betweenWords = false;
691                 break;
692             }
693         }
694
695         int wordLen = j - i;
696         if (wordLen) {
697             bool isSpace = (j < len) && isSpaceAccordingToStyle(c, style);
698             float w;
699             if (wordTrailingSpaceWidth && isSpace)
700                 w = widthFromCache(f, i, wordLen + 1, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow, style) - wordTrailingSpaceWidth;
701             else {
702                 w = widthFromCache(f, i, wordLen, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow, style);
703                 if (c == softHyphen && style.hyphens() != HyphensNone)
704                     currMinWidth += hyphenWidth(this, f);
705             }
706
707             if (w > maxWordWidth) {
708                 int suffixStart;
709                 float maxFragmentWidth = maxWordFragmentWidth(this, style, f, characters() + i, wordLen, minimumPrefixLength, minimumSuffixLength, suffixStart, fallbackFonts, glyphOverflow);
710
711                 if (suffixStart) {
712                     float suffixWidth;
713                     if (wordTrailingSpaceWidth && isSpace)
714                         suffixWidth = widthFromCache(f, i + suffixStart, wordLen - suffixStart + 1, leadWidth + currMaxWidth, 0, 0, style) - wordTrailingSpaceWidth;
715                     else
716                         suffixWidth = widthFromCache(f, i + suffixStart, wordLen - suffixStart, leadWidth + currMaxWidth, 0, 0, style);
717
718                     maxFragmentWidth = max(maxFragmentWidth, suffixWidth);
719
720                     currMinWidth += maxFragmentWidth - w;
721                     maxWordWidth = max(maxWordWidth, maxFragmentWidth);
722                 } else
723                     maxWordWidth = w;
724             }
725
726             if (firstGlyphLeftOverflow < 0)
727                 firstGlyphLeftOverflow = glyphOverflow.left;
728             currMinWidth += w;
729             if (betweenWords) {
730                 if (lastWordBoundary == i)
731                     currMaxWidth += w;
732                 else
733                     currMaxWidth += widthFromCache(f, lastWordBoundary, j - lastWordBoundary, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow, style);
734                 lastWordBoundary = j;
735             }
736
737             bool isCollapsibleWhiteSpace = (j < len) && style.isCollapsibleWhiteSpace(c);
738             if (j < len && style.autoWrap())
739                 m_hasBreakableChar = true;
740
741             // Add in wordSpacing to our currMaxWidth, but not if this is the last word on a line or the
742             // last word in the run.
743             if (wordSpacing && (isSpace || isCollapsibleWhiteSpace) && !containsOnlyWhitespace(j, len-j))
744                 currMaxWidth += wordSpacing;
745
746             if (firstWord) {
747                 firstWord = false;
748                 // If the first character in the run is breakable, then we consider ourselves to have a beginning
749                 // minimum width of 0, since a break could occur right before our run starts, preventing us from ever
750                 // being appended to a previous text run when considering the total minimum width of the containing block.
751                 if (hasBreak)
752                     m_hasBreakableChar = true;
753                 m_beginMinWidth = hasBreak ? 0 : currMinWidth;
754             }
755             m_endMinWidth = currMinWidth;
756
757             if (currMinWidth > m_minWidth)
758                 m_minWidth = currMinWidth;
759             currMinWidth = 0;
760
761             i += wordLen - 1;
762         } else {
763             // Nowrap can never be broken, so don't bother setting the
764             // breakable character boolean. Pre can only be broken if we encounter a newline.
765             if (style.autoWrap() || isNewline)
766                 m_hasBreakableChar = true;
767
768             if (currMinWidth > m_minWidth)
769                 m_minWidth = currMinWidth;
770             currMinWidth = 0;
771
772             if (isNewline) { // Only set if preserveNewline was true and we saw a newline.
773                 if (firstLine) {
774                     firstLine = false;
775                     leadWidth = 0;
776                     if (!style.autoWrap())
777                         m_beginMinWidth = currMaxWidth;
778                 }
779
780                 if (currMaxWidth > m_maxWidth)
781                     m_maxWidth = currMaxWidth;
782                 currMaxWidth = 0;
783             } else {
784                 TextRun run = RenderBlock::constructTextRun(this, f, this, i, 1, style);
785                 run.setCharactersLength(len - i);
786                 ASSERT(run.charactersLength() >= run.length());
787                 run.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
788                 run.setXPos(leadWidth + currMaxWidth);
789
790                 currMaxWidth += f.width(run, &fallbackFonts);
791                 glyphOverflow.right = 0;
792                 needsWordSpacing = isSpace && !previousCharacterIsSpace && i == len - 1;
793             }
794             ASSERT(lastWordBoundary == i);
795             lastWordBoundary++;
796         }
797     }
798
799     if (firstGlyphLeftOverflow > 0)
800         glyphOverflow.left = firstGlyphLeftOverflow;
801
802     if ((needsWordSpacing && len > 1) || (ignoringSpaces && !firstWord))
803         currMaxWidth += wordSpacing;
804
805     m_minWidth = max(currMinWidth, m_minWidth);
806     m_maxWidth = max(currMaxWidth, m_maxWidth);
807
808     if (!style.autoWrap())
809         m_minWidth = m_maxWidth;
810
811     if (style.whiteSpace() == PRE) {
812         if (firstLine)
813             m_beginMinWidth = m_maxWidth;
814         m_endMinWidth = currMaxWidth;
815     }
816
817     setPreferredLogicalWidthsDirty(false);
818 }
819
820 bool RenderText::isAllCollapsibleWhitespace() const
821 {
822     const RenderStyle& style = *this->style();
823     unsigned length = textLength();
824     if (is8Bit()) {
825         for (unsigned i = 0; i < length; ++i) {
826             if (!style.isCollapsibleWhiteSpace(characters8()[i]))
827                 return false;
828         }
829         return true;
830     }
831     for (unsigned i = 0; i < length; ++i) {
832         if (!style.isCollapsibleWhiteSpace(characters16()[i]))
833             return false;
834     }
835     return true;
836 }
837     
838 bool RenderText::containsOnlyWhitespace(unsigned from, unsigned len) const
839 {
840     ASSERT(m_text);
841     StringImpl& text = *m_text.impl();
842     unsigned currPos;
843     for (currPos = from;
844          currPos < from + len && (text[currPos] == '\n' || text[currPos] == ' ' || text[currPos] == '\t');
845          currPos++) { }
846     return currPos >= (from + len);
847 }
848
849 FloatPoint RenderText::firstRunOrigin() const
850 {
851     return IntPoint(firstRunX(), firstRunY());
852 }
853
854 float RenderText::firstRunX() const
855 {
856     return firstTextBox() ? firstTextBox()->x() : 0;
857 }
858
859 float RenderText::firstRunY() const
860 {
861     return firstTextBox() ? firstTextBox()->y() : 0;
862 }
863     
864 void RenderText::setSelectionState(SelectionState state)
865 {
866     if (state != SelectionNone)
867         ensureLineBoxes();
868
869     RenderObject::setSelectionState(state);
870
871     if (canUpdateSelectionOnRootLineBoxes())
872         m_lineBoxes.setSelectionState(*this, state);
873
874     // The containing block can be null in case of an orphaned tree.
875     RenderBlock* containingBlock = this->containingBlock();
876     if (containingBlock && !containingBlock->isRenderView())
877         containingBlock->setSelectionState(state);
878 }
879
880 void RenderText::setTextWithOffset(const String& text, unsigned offset, unsigned len, bool force)
881 {
882     if (!force && m_text == text)
883         return;
884
885     int delta = text.length() - textLength();
886     unsigned end = len ? offset + len - 1 : offset;
887
888     m_linesDirty = simpleLines() || m_lineBoxes.dirtyRange(*this, offset, end, delta);
889
890     setText(text, force || m_linesDirty);
891 }
892
893 void RenderText::transformText()
894 {
895     String textToTransform = originalText();
896     if (!textToTransform.isNull())
897         setText(textToTransform, true);
898 }
899
900 static inline bool isInlineFlowOrEmptyText(const RenderObject* o)
901 {
902     if (o->isRenderInline())
903         return true;
904     if (!o->isText())
905         return false;
906     StringImpl* text = toRenderText(o)->text();
907     if (!text)
908         return true;
909     return !text->length();
910 }
911
912 UChar RenderText::previousCharacter() const
913 {
914     // find previous text renderer if one exists
915     const RenderObject* previousText = this;
916     while ((previousText = previousText->previousInPreOrder()))
917         if (!isInlineFlowOrEmptyText(previousText))
918             break;
919     UChar prev = ' ';
920     if (previousText && previousText->isText())
921         if (StringImpl* previousString = toRenderText(previousText)->text())
922             prev = (*previousString)[previousString->length() - 1];
923     return prev;
924 }
925
926 void applyTextTransform(const RenderStyle* style, String& text, UChar previousCharacter)
927 {
928     if (!style)
929         return;
930
931     switch (style->textTransform()) {
932     case TTNONE:
933         break;
934     case CAPITALIZE:
935         makeCapitalized(&text, previousCharacter);
936         break;
937     case UPPERCASE:
938         text = text.upper(style->locale());
939         break;
940     case LOWERCASE:
941         text = text.lower(style->locale());
942         break;
943     }
944 }
945
946 void RenderText::setTextInternal(const String& text)
947 {
948     ASSERT(!text.isNull());
949     m_text = text;
950
951     if (m_useBackslashAsYenSymbol)
952         m_text.replace('\\', yenSign);
953
954     ASSERT(m_text);
955
956     if (style()) {
957         applyTextTransform(style(), m_text, previousCharacter());
958
959         // We use the same characters here as for list markers.
960         // See the listMarkerText function in RenderListMarker.cpp.
961         switch (style()->textSecurity()) {
962         case TSNONE:
963             break;
964         case TSCIRCLE:
965             secureText(whiteBullet);
966             break;
967         case TSDISC:
968             secureText(bullet);
969             break;
970         case TSSQUARE:
971             secureText(blackSquare);
972         }
973     }
974
975     ASSERT(!m_text.isNull());
976
977     m_isAllASCII = m_text.containsOnlyASCII();
978     m_canUseSimpleFontCodePath = computeCanUseSimpleFontCodePath();
979 }
980
981 void RenderText::secureText(UChar mask)
982 {
983     if (!textLength())
984         return;
985
986     int lastTypedCharacterOffsetToReveal = -1;
987     String revealedText;
988     SecureTextTimer* secureTextTimer = gSecureTextTimers ? gSecureTextTimers->get(this) : 0;
989     if (secureTextTimer && secureTextTimer->isActive()) {
990         lastTypedCharacterOffsetToReveal = secureTextTimer->lastTypedCharacterOffset();
991         if (lastTypedCharacterOffsetToReveal >= 0)
992             revealedText.append(m_text[lastTypedCharacterOffsetToReveal]);
993     }
994
995     m_text.fill(mask);
996     if (lastTypedCharacterOffsetToReveal >= 0) {
997         m_text.replace(lastTypedCharacterOffsetToReveal, 1, revealedText);
998         // m_text may be updated later before timer fires. We invalidate the lastTypedCharacterOffset to avoid inconsistency.
999         secureTextTimer->invalidate();
1000     }
1001 }
1002
1003 void RenderText::setText(const String& text, bool force)
1004 {
1005     ASSERT(!text.isNull());
1006
1007     if (!force && m_text == text)
1008         return;
1009
1010     setTextInternal(text);
1011     setNeedsLayoutAndPrefWidthsRecalc();
1012     m_knownToHaveNoOverflowAndNoFallbackFonts = false;
1013     
1014     if (AXObjectCache* cache = document().existingAXObjectCache())
1015         cache->textChanged(this);
1016 }
1017
1018 String RenderText::textWithoutConvertingBackslashToYenSymbol() const
1019 {
1020     if (!m_useBackslashAsYenSymbol || style()->textSecurity() != TSNONE)
1021         return text();
1022
1023     String text = originalText();
1024     applyTextTransform(style(), text, previousCharacter());
1025     return text;
1026 }
1027
1028 void RenderText::dirtyLineBoxes(bool fullLayout)
1029 {
1030     if (fullLayout)
1031         m_lineBoxes.deleteAll(*this);
1032     else if (!m_linesDirty)
1033         m_lineBoxes.dirtyAll();
1034     m_linesDirty = false;
1035 }
1036
1037 InlineTextBox* RenderText::createTextBox()
1038 {
1039     return new (renderArena()) InlineTextBox(*this);
1040 }
1041
1042 void RenderText::positionLineBox(InlineBox* box)
1043 {
1044     InlineTextBox* textBox = toInlineTextBox(box);
1045
1046     // FIXME: should not be needed!!!
1047     if (!textBox->len()) {
1048         // We want the box to be destroyed.
1049         textBox->removeFromParent();
1050         m_lineBoxes.remove(*textBox);
1051         textBox->destroy(renderArena());
1052         return;
1053     }
1054
1055     m_containsReversedText |= !textBox->isLeftToRightDirection();
1056 }
1057
1058 void RenderText::ensureLineBoxes()
1059 {
1060     if (!parent()->isRenderBlockFlow())
1061         return;
1062     toRenderBlockFlow(parent())->ensureLineBoxes();
1063 }
1064
1065 const SimpleLineLayout::Lines* RenderText::simpleLines() const
1066 {
1067     if (!parent()->isRenderBlockFlow())
1068         return nullptr;
1069     return toRenderBlockFlow(parent())->simpleLines();
1070 }
1071
1072 float RenderText::width(unsigned from, unsigned len, float xPos, bool firstLine, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
1073 {
1074     if (from >= textLength())
1075         return 0;
1076
1077     if (from + len > textLength())
1078         len = textLength() - from;
1079
1080     const RenderStyle& lineStyle = firstLine ? *firstLineStyle() : *style();
1081     return width(from, len, lineStyle.font(), xPos, fallbackFonts, glyphOverflow);
1082 }
1083
1084 float RenderText::width(unsigned from, unsigned len, const Font& f, float xPos, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
1085 {
1086     ASSERT(from + len <= textLength());
1087     if (!textLength())
1088         return 0;
1089
1090     const RenderStyle& style = *this->style();
1091     float w;
1092     if (&f == &style.font()) {
1093         if (!style.preserveNewline() && !from && len == textLength() && (!glyphOverflow || !glyphOverflow->computeBounds)) {
1094             if (fallbackFonts) {
1095                 ASSERT(glyphOverflow);
1096                 if (preferredLogicalWidthsDirty() || !m_knownToHaveNoOverflowAndNoFallbackFonts) {
1097                     const_cast<RenderText*>(this)->computePreferredLogicalWidths(0, *fallbackFonts, *glyphOverflow);
1098                     if (fallbackFonts->isEmpty() && !glyphOverflow->left && !glyphOverflow->right && !glyphOverflow->top && !glyphOverflow->bottom)
1099                         m_knownToHaveNoOverflowAndNoFallbackFonts = true;
1100                 }
1101                 w = m_maxWidth;
1102             } else
1103                 w = maxLogicalWidth();
1104         } else
1105             w = widthFromCache(f, from, len, xPos, fallbackFonts, glyphOverflow, style);
1106     } else {
1107         TextRun run = RenderBlock::constructTextRun(const_cast<RenderText*>(this), f, this, from, len, style);
1108         run.setCharactersLength(textLength() - from);
1109         ASSERT(run.charactersLength() >= run.length());
1110
1111         run.setCharacterScanForCodePath(!canUseSimpleFontCodePath());
1112         run.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
1113         run.setXPos(xPos);
1114         w = f.width(run, fallbackFonts, glyphOverflow);
1115     }
1116
1117     return w;
1118 }
1119
1120 IntRect RenderText::linesBoundingBox() const
1121 {
1122     if (auto lines = simpleLines())
1123         return SimpleLineLayout::computeTextBoundingBox(*this, *lines);
1124
1125     return m_lineBoxes.boundingBox(*this);
1126 }
1127
1128 LayoutRect RenderText::linesVisualOverflowBoundingBox() const
1129 {
1130     ASSERT(!simpleLines());
1131     return m_lineBoxes.visualOverflowBoundingBox(*this);
1132 }
1133
1134 LayoutRect RenderText::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const
1135 {
1136     RenderObject* rendererToRepaint = containingBlock();
1137
1138     // Do not cross self-painting layer boundaries.
1139     RenderObject& enclosingLayerRenderer = enclosingLayer()->renderer();
1140     if (&enclosingLayerRenderer != rendererToRepaint && !rendererToRepaint->isDescendantOf(&enclosingLayerRenderer))
1141         rendererToRepaint = &enclosingLayerRenderer;
1142
1143     // The renderer we chose to repaint may be an ancestor of repaintContainer, but we need to do a repaintContainer-relative repaint.
1144     if (repaintContainer && repaintContainer != rendererToRepaint && !rendererToRepaint->isDescendantOf(repaintContainer))
1145         return repaintContainer->clippedOverflowRectForRepaint(repaintContainer);
1146
1147     return rendererToRepaint->clippedOverflowRectForRepaint(repaintContainer);
1148 }
1149
1150 LayoutRect RenderText::selectionRectForRepaint(const RenderLayerModelObject* repaintContainer, bool clipToVisibleContent)
1151 {
1152     ASSERT(!needsLayout());
1153     ASSERT(!simpleLines());
1154
1155     if (selectionState() == SelectionNone)
1156         return LayoutRect();
1157     RenderBlock* cb = containingBlock();
1158     if (!cb)
1159         return LayoutRect();
1160
1161     // Now calculate startPos and endPos for painting selection.
1162     // We include a selection while endPos > 0
1163     int startPos, endPos;
1164     if (selectionState() == SelectionInside) {
1165         // We are fully selected.
1166         startPos = 0;
1167         endPos = textLength();
1168     } else {
1169         selectionStartEnd(startPos, endPos);
1170         if (selectionState() == SelectionStart)
1171             endPos = textLength();
1172         else if (selectionState() == SelectionEnd)
1173             startPos = 0;
1174     }
1175
1176     if (startPos == endPos)
1177         return IntRect();
1178
1179     LayoutRect rect = m_lineBoxes.selectionRectForRange(startPos, endPos);
1180
1181     if (clipToVisibleContent)
1182         computeRectForRepaint(repaintContainer, rect);
1183     else {
1184         if (cb->hasColumns())
1185             cb->adjustRectForColumns(rect);
1186
1187         rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
1188     }
1189
1190     return rect;
1191 }
1192
1193 int RenderText::caretMinOffset() const
1194 {
1195     if (auto lines = simpleLines())
1196         return SimpleLineLayout::findTextCaretMinimumOffset(*this, *lines);
1197     return m_lineBoxes.caretMinOffset();
1198 }
1199
1200 int RenderText::caretMaxOffset() const
1201 {
1202     if (auto lines = simpleLines())
1203         return SimpleLineLayout::findTextCaretMaximumOffset(*this, *lines);
1204     return m_lineBoxes.caretMaxOffset(*this);
1205 }
1206
1207 unsigned RenderText::countRenderedCharacterOffsetsUntil(unsigned offset) const
1208 {
1209     ASSERT(!simpleLines());
1210     return m_lineBoxes.countCharacterOffsetsUntil(offset);
1211 }
1212
1213 bool RenderText::containsRenderedCharacterOffset(unsigned offset) const
1214 {
1215     ASSERT(!simpleLines());
1216     return m_lineBoxes.containsOffset(*this, offset, RenderTextLineBoxes::CharacterOffset);
1217 }
1218
1219 bool RenderText::containsCaretOffset(unsigned offset) const
1220 {
1221     if (auto layout = simpleLines())
1222         return SimpleLineLayout::containsTextCaretOffset(*this, *layout, offset);
1223     return m_lineBoxes.containsOffset(*this, offset, RenderTextLineBoxes::CaretOffset);
1224 }
1225
1226 bool RenderText::hasRenderedText() const
1227 {
1228     if (auto lines = simpleLines())
1229         return SimpleLineLayout::isTextRendered(*this, *lines);
1230     return m_lineBoxes.hasRenderedText();
1231 }
1232
1233 int RenderText::previousOffset(int current) const
1234 {
1235     if (isAllASCII() || m_text.is8Bit())
1236         return current - 1;
1237
1238     StringImpl* textImpl = m_text.impl();
1239     TextBreakIterator* iterator = cursorMovementIterator(textImpl->characters16(), textImpl->length());
1240     if (!iterator)
1241         return current - 1;
1242
1243     long result = textBreakPreceding(iterator, current);
1244     if (result == TextBreakDone)
1245         result = current - 1;
1246
1247
1248     return result;
1249 }
1250
1251 #if PLATFORM(MAC) || PLATFORM(EFL)
1252
1253 #define HANGUL_CHOSEONG_START (0x1100)
1254 #define HANGUL_CHOSEONG_END (0x115F)
1255 #define HANGUL_JUNGSEONG_START (0x1160)
1256 #define HANGUL_JUNGSEONG_END (0x11A2)
1257 #define HANGUL_JONGSEONG_START (0x11A8)
1258 #define HANGUL_JONGSEONG_END (0x11F9)
1259 #define HANGUL_SYLLABLE_START (0xAC00)
1260 #define HANGUL_SYLLABLE_END (0xD7AF)
1261 #define HANGUL_JONGSEONG_COUNT (28)
1262
1263 enum HangulState {
1264     HangulStateL,
1265     HangulStateV,
1266     HangulStateT,
1267     HangulStateLV,
1268     HangulStateLVT,
1269     HangulStateBreak
1270 };
1271
1272 inline bool isHangulLVT(UChar32 character)
1273 {
1274     return (character - HANGUL_SYLLABLE_START) % HANGUL_JONGSEONG_COUNT;
1275 }
1276
1277 inline bool isMark(UChar32 c)
1278 {
1279     int8_t charType = u_charType(c);
1280     return charType == U_NON_SPACING_MARK || charType == U_ENCLOSING_MARK || charType == U_COMBINING_SPACING_MARK;
1281 }
1282
1283 inline bool isRegionalIndicator(UChar32 c)
1284 {
1285     // National flag emoji each consists of a pair of regional indicator symbols.
1286     return 0x1F1E6 <= c && c <= 0x1F1FF;
1287 }
1288
1289 #endif
1290
1291 int RenderText::previousOffsetForBackwardDeletion(int current) const
1292 {
1293 #if PLATFORM(MAC) || PLATFORM(EFL)
1294     ASSERT(m_text);
1295     StringImpl& text = *m_text.impl();
1296     UChar32 character;
1297     bool sawRegionalIndicator = false;
1298     while (current > 0) {
1299         if (U16_IS_TRAIL(text[--current]))
1300             --current;
1301         if (current < 0)
1302             break;
1303
1304         UChar32 character = text.characterStartingAt(current);
1305
1306         if (sawRegionalIndicator) {
1307             // We don't check if the pair of regional indicator symbols before current position can actually be combined
1308             // into a flag, and just delete it. This may not agree with how the pair is rendered in edge cases,
1309             // but is good enough in practice.
1310             if (isRegionalIndicator(character))
1311                 break;
1312             // Don't delete a preceding character that isn't a regional indicator symbol.
1313             U16_FWD_1_UNSAFE(text, current);
1314         }
1315
1316         // We don't combine characters in Armenian ... Limbu range for backward deletion.
1317         if ((character >= 0x0530) && (character < 0x1950))
1318             break;
1319
1320         if (isRegionalIndicator(character)) {
1321             sawRegionalIndicator = true;
1322             continue;
1323         }
1324
1325         if (!isMark(character) && (character != 0xFF9E) && (character != 0xFF9F))
1326             break;
1327     }
1328
1329     if (current <= 0)
1330         return current;
1331
1332     // Hangul
1333     character = text.characterStartingAt(current);
1334     if (((character >= HANGUL_CHOSEONG_START) && (character <= HANGUL_JONGSEONG_END)) || ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))) {
1335         HangulState state;
1336
1337         if (character < HANGUL_JUNGSEONG_START)
1338             state = HangulStateL;
1339         else if (character < HANGUL_JONGSEONG_START)
1340             state = HangulStateV;
1341         else if (character < HANGUL_SYLLABLE_START)
1342             state = HangulStateT;
1343         else
1344             state = isHangulLVT(character) ? HangulStateLVT : HangulStateLV;
1345
1346         while (current > 0 && ((character = text.characterStartingAt(current - 1)) >= HANGUL_CHOSEONG_START) && (character <= HANGUL_SYLLABLE_END) && ((character <= HANGUL_JONGSEONG_END) || (character >= HANGUL_SYLLABLE_START))) {
1347             switch (state) {
1348             case HangulStateV:
1349                 if (character <= HANGUL_CHOSEONG_END)
1350                     state = HangulStateL;
1351                 else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END) && !isHangulLVT(character))
1352                     state = HangulStateLV;
1353                 else if (character > HANGUL_JUNGSEONG_END)
1354                     state = HangulStateBreak;
1355                 break;
1356             case HangulStateT:
1357                 if ((character >= HANGUL_JUNGSEONG_START) && (character <= HANGUL_JUNGSEONG_END))
1358                     state = HangulStateV;
1359                 else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))
1360                     state = (isHangulLVT(character) ? HangulStateLVT : HangulStateLV);
1361                 else if (character < HANGUL_JUNGSEONG_START)
1362                     state = HangulStateBreak;
1363                 break;
1364             default:
1365                 state = (character < HANGUL_JUNGSEONG_START) ? HangulStateL : HangulStateBreak;
1366                 break;
1367             }
1368             if (state == HangulStateBreak)
1369                 break;
1370
1371             --current;
1372         }
1373     }
1374
1375     return current;
1376 #else
1377     // Platforms other than Mac delete by one code point.
1378     if (U16_IS_TRAIL(m_text[--current]))
1379         --current;
1380     if (current < 0)
1381         current = 0;
1382     return current;
1383 #endif
1384 }
1385
1386 int RenderText::nextOffset(int current) const
1387 {
1388     if (isAllASCII() || m_text.is8Bit())
1389         return current + 1;
1390
1391     StringImpl* textImpl = m_text.impl();
1392     TextBreakIterator* iterator = cursorMovementIterator(textImpl->characters16(), textImpl->length());
1393     if (!iterator)
1394         return current + 1;
1395
1396     long result = textBreakFollowing(iterator, current);
1397     if (result == TextBreakDone)
1398         result = current + 1;
1399
1400     return result;
1401 }
1402
1403 bool RenderText::computeCanUseSimpleFontCodePath() const
1404 {
1405     if (isAllASCII() || m_text.is8Bit())
1406         return true;
1407     return Font::characterRangeCodePath(characters(), length()) == Font::Simple;
1408 }
1409
1410 void RenderText::momentarilyRevealLastTypedCharacter(unsigned lastTypedCharacterOffset)
1411 {
1412     if (!gSecureTextTimers)
1413         gSecureTextTimers = new SecureTextTimerMap;
1414
1415     SecureTextTimer* secureTextTimer = gSecureTextTimers->get(this);
1416     if (!secureTextTimer) {
1417         secureTextTimer = new SecureTextTimer(this);
1418         gSecureTextTimers->add(this, secureTextTimer);
1419     }
1420     secureTextTimer->restartWithNewText(lastTypedCharacterOffset);
1421 }
1422
1423 } // namespace WebCore