RenderElement::style() should return a reference.
[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     const 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 void RenderText::computePreferredLogicalWidths(float leadWidth)
514 {
515     HashSet<const SimpleFontData*> fallbackFonts;
516     GlyphOverflow glyphOverflow;
517     computePreferredLogicalWidths(leadWidth, fallbackFonts, glyphOverflow);
518     if (fallbackFonts.isEmpty() && !glyphOverflow.left && !glyphOverflow.right && !glyphOverflow.top && !glyphOverflow.bottom)
519         m_knownToHaveNoOverflowAndNoFallbackFonts = true;
520 }
521
522 static inline float hyphenWidth(RenderText* renderer, const Font& font)
523 {
524     const RenderStyle& style = renderer->style();
525     return font.width(RenderBlock::constructTextRun(renderer, font, style.hyphenString().string(), style));
526 }
527
528 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)
529 {
530     suffixStart = 0;
531     if (wordLength <= minimumSuffixLength)
532         return 0;
533
534     Vector<int, 8> hyphenLocations;
535     int hyphenLocation = wordLength - minimumSuffixLength;
536     while ((hyphenLocation = lastHyphenLocation(word, wordLength, hyphenLocation, style.locale())) >= minimumPrefixLength)
537         hyphenLocations.append(hyphenLocation);
538
539     if (hyphenLocations.isEmpty())
540         return 0;
541
542     hyphenLocations.reverse();
543
544     float minimumFragmentWidthToConsider = font.pixelSize() * 5 / 4 + hyphenWidth(renderer, font);
545     float maxFragmentWidth = 0;
546     for (size_t k = 0; k < hyphenLocations.size(); ++k) {
547         int fragmentLength = hyphenLocations[k] - suffixStart;
548         StringBuilder fragmentWithHyphen;
549         fragmentWithHyphen.append(word + suffixStart, fragmentLength);
550         fragmentWithHyphen.append(style.hyphenString());
551
552         TextRun run = RenderBlock::constructTextRun(renderer, font, fragmentWithHyphen.characters(), fragmentWithHyphen.length(), style);
553         run.setCharactersLength(fragmentWithHyphen.length());
554         run.setCharacterScanForCodePath(!renderer->canUseSimpleFontCodePath());
555         float fragmentWidth = font.width(run, &fallbackFonts, &glyphOverflow);
556
557         // Narrow prefixes are ignored. See tryHyphenating in RenderBlockLineLayout.cpp.
558         if (fragmentWidth <= minimumFragmentWidthToConsider)
559             continue;
560
561         suffixStart += fragmentLength;
562         maxFragmentWidth = max(maxFragmentWidth, fragmentWidth);
563     }
564
565     return maxFragmentWidth;
566 }
567
568 void RenderText::computePreferredLogicalWidths(float leadWidth, HashSet<const SimpleFontData*>& fallbackFonts, GlyphOverflow& glyphOverflow)
569 {
570     ASSERT(m_hasTab || preferredLogicalWidthsDirty() || !m_knownToHaveNoOverflowAndNoFallbackFonts);
571
572     m_minWidth = 0;
573     m_beginMinWidth = 0;
574     m_endMinWidth = 0;
575     m_maxWidth = 0;
576
577     float currMinWidth = 0;
578     float currMaxWidth = 0;
579     m_hasBreakableChar = false;
580     m_hasBreak = false;
581     m_hasTab = false;
582     m_hasBeginWS = false;
583     m_hasEndWS = false;
584
585     const RenderStyle& style = this->style();
586     const Font& f = style.font(); // FIXME: This ignores first-line.
587     float wordSpacing = style.wordSpacing();
588     int len = textLength();
589     LazyLineBreakIterator breakIterator(m_text, style.locale());
590     bool needsWordSpacing = false;
591     bool ignoringSpaces = false;
592     bool isSpace = false;
593     bool firstWord = true;
594     bool firstLine = true;
595     int nextBreakable = -1;
596     int lastWordBoundary = 0;
597
598     // Non-zero only when kerning is enabled, in which case we measure words with their trailing
599     // space, then subtract its width.
600     float wordTrailingSpaceWidth = f.typesettingFeatures() & Kerning ? f.width(RenderBlock::constructTextRun(this, f, &space, 1, style), &fallbackFonts) + wordSpacing : 0;
601
602     // If automatic hyphenation is allowed, we keep track of the width of the widest word (or word
603     // fragment) encountered so far, and only try hyphenating words that are wider.
604     float maxWordWidth = numeric_limits<float>::max();
605     int minimumPrefixLength = 0;
606     int minimumSuffixLength = 0;
607     if (style.hyphens() == HyphensAuto && canHyphenate(style.locale())) {
608         maxWordWidth = 0;
609
610         // Map 'hyphenate-limit-{before,after}: auto;' to 2.
611         minimumPrefixLength = style.hyphenationLimitBefore();
612         if (minimumPrefixLength < 0)
613             minimumPrefixLength = 2;
614
615         minimumSuffixLength = style.hyphenationLimitAfter();
616         if (minimumSuffixLength < 0)
617             minimumSuffixLength = 2;
618     }
619
620     int firstGlyphLeftOverflow = -1;
621
622     bool breakNBSP = style.autoWrap() && style.nbspMode() == SPACE;
623     bool breakAll = (style.wordBreak() == BreakAllWordBreak || style.wordBreak() == BreakWordBreak) && style.autoWrap();
624
625     for (int i = 0; i < len; i++) {
626         UChar c = characterAt(i);
627
628         bool previousCharacterIsSpace = isSpace;
629
630         bool isNewline = false;
631         if (c == '\n') {
632             if (style.preserveNewline()) {
633                 m_hasBreak = true;
634                 isNewline = true;
635                 isSpace = false;
636             } else
637                 isSpace = true;
638         } else if (c == '\t') {
639             if (!style.collapseWhiteSpace()) {
640                 m_hasTab = true;
641                 isSpace = false;
642             } else
643                 isSpace = true;
644         } else
645             isSpace = c == ' ';
646
647         if ((isSpace || isNewline) && !i)
648             m_hasBeginWS = true;
649         if ((isSpace || isNewline) && i == len - 1)
650             m_hasEndWS = true;
651
652         if (!ignoringSpaces && style.collapseWhiteSpace() && previousCharacterIsSpace && isSpace)
653             ignoringSpaces = true;
654
655         if (ignoringSpaces && !isSpace)
656             ignoringSpaces = false;
657
658         // Ignore spaces and soft hyphens
659         if (ignoringSpaces) {
660             ASSERT(lastWordBoundary == i);
661             lastWordBoundary++;
662             continue;
663         } else if (c == softHyphen && style.hyphens() != HyphensNone) {
664             currMaxWidth += widthFromCache(f, lastWordBoundary, i - lastWordBoundary, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow, style);
665             if (firstGlyphLeftOverflow < 0)
666                 firstGlyphLeftOverflow = glyphOverflow.left;
667             lastWordBoundary = i + 1;
668             continue;
669         }
670
671         bool hasBreak = breakAll || isBreakable(breakIterator, i, nextBreakable, breakNBSP);
672         bool betweenWords = true;
673         int j = i;
674         while (c != '\n' && !isSpaceAccordingToStyle(c, style) && c != '\t' && (c != softHyphen || style.hyphens() == HyphensNone)) {
675             j++;
676             if (j == len)
677                 break;
678             c = characterAt(j);
679             if (isBreakable(breakIterator, j, nextBreakable, breakNBSP) && characterAt(j - 1) != softHyphen)
680                 break;
681             if (breakAll) {
682                 betweenWords = false;
683                 break;
684             }
685         }
686
687         int wordLen = j - i;
688         if (wordLen) {
689             bool isSpace = (j < len) && isSpaceAccordingToStyle(c, style);
690             float w;
691             if (wordTrailingSpaceWidth && isSpace)
692                 w = widthFromCache(f, i, wordLen + 1, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow, style) - wordTrailingSpaceWidth;
693             else {
694                 w = widthFromCache(f, i, wordLen, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow, style);
695                 if (c == softHyphen && style.hyphens() != HyphensNone)
696                     currMinWidth += hyphenWidth(this, f);
697             }
698
699             if (w > maxWordWidth) {
700                 int suffixStart;
701                 float maxFragmentWidth = maxWordFragmentWidth(this, style, f, characters() + i, wordLen, minimumPrefixLength, minimumSuffixLength, suffixStart, fallbackFonts, glyphOverflow);
702
703                 if (suffixStart) {
704                     float suffixWidth;
705                     if (wordTrailingSpaceWidth && isSpace)
706                         suffixWidth = widthFromCache(f, i + suffixStart, wordLen - suffixStart + 1, leadWidth + currMaxWidth, 0, 0, style) - wordTrailingSpaceWidth;
707                     else
708                         suffixWidth = widthFromCache(f, i + suffixStart, wordLen - suffixStart, leadWidth + currMaxWidth, 0, 0, style);
709
710                     maxFragmentWidth = max(maxFragmentWidth, suffixWidth);
711
712                     currMinWidth += maxFragmentWidth - w;
713                     maxWordWidth = max(maxWordWidth, maxFragmentWidth);
714                 } else
715                     maxWordWidth = w;
716             }
717
718             if (firstGlyphLeftOverflow < 0)
719                 firstGlyphLeftOverflow = glyphOverflow.left;
720             currMinWidth += w;
721             if (betweenWords) {
722                 if (lastWordBoundary == i)
723                     currMaxWidth += w;
724                 else
725                     currMaxWidth += widthFromCache(f, lastWordBoundary, j - lastWordBoundary, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow, style);
726                 lastWordBoundary = j;
727             }
728
729             bool isCollapsibleWhiteSpace = (j < len) && style.isCollapsibleWhiteSpace(c);
730             if (j < len && style.autoWrap())
731                 m_hasBreakableChar = true;
732
733             // Add in wordSpacing to our currMaxWidth, but not if this is the last word on a line or the
734             // last word in the run.
735             if (wordSpacing && (isSpace || isCollapsibleWhiteSpace) && !containsOnlyWhitespace(j, len-j))
736                 currMaxWidth += wordSpacing;
737
738             if (firstWord) {
739                 firstWord = false;
740                 // If the first character in the run is breakable, then we consider ourselves to have a beginning
741                 // minimum width of 0, since a break could occur right before our run starts, preventing us from ever
742                 // being appended to a previous text run when considering the total minimum width of the containing block.
743                 if (hasBreak)
744                     m_hasBreakableChar = true;
745                 m_beginMinWidth = hasBreak ? 0 : currMinWidth;
746             }
747             m_endMinWidth = currMinWidth;
748
749             if (currMinWidth > m_minWidth)
750                 m_minWidth = currMinWidth;
751             currMinWidth = 0;
752
753             i += wordLen - 1;
754         } else {
755             // Nowrap can never be broken, so don't bother setting the
756             // breakable character boolean. Pre can only be broken if we encounter a newline.
757             if (style.autoWrap() || isNewline)
758                 m_hasBreakableChar = true;
759
760             if (currMinWidth > m_minWidth)
761                 m_minWidth = currMinWidth;
762             currMinWidth = 0;
763
764             if (isNewline) { // Only set if preserveNewline was true and we saw a newline.
765                 if (firstLine) {
766                     firstLine = false;
767                     leadWidth = 0;
768                     if (!style.autoWrap())
769                         m_beginMinWidth = currMaxWidth;
770                 }
771
772                 if (currMaxWidth > m_maxWidth)
773                     m_maxWidth = currMaxWidth;
774                 currMaxWidth = 0;
775             } else {
776                 TextRun run = RenderBlock::constructTextRun(this, f, this, i, 1, style);
777                 run.setCharactersLength(len - i);
778                 ASSERT(run.charactersLength() >= run.length());
779                 run.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
780                 run.setXPos(leadWidth + currMaxWidth);
781
782                 currMaxWidth += f.width(run, &fallbackFonts);
783                 glyphOverflow.right = 0;
784                 needsWordSpacing = isSpace && !previousCharacterIsSpace && i == len - 1;
785             }
786             ASSERT(lastWordBoundary == i);
787             lastWordBoundary++;
788         }
789     }
790
791     if (firstGlyphLeftOverflow > 0)
792         glyphOverflow.left = firstGlyphLeftOverflow;
793
794     if ((needsWordSpacing && len > 1) || (ignoringSpaces && !firstWord))
795         currMaxWidth += wordSpacing;
796
797     m_minWidth = max(currMinWidth, m_minWidth);
798     m_maxWidth = max(currMaxWidth, m_maxWidth);
799
800     if (!style.autoWrap())
801         m_minWidth = m_maxWidth;
802
803     if (style.whiteSpace() == PRE) {
804         if (firstLine)
805             m_beginMinWidth = m_maxWidth;
806         m_endMinWidth = currMaxWidth;
807     }
808
809     setPreferredLogicalWidthsDirty(false);
810 }
811
812 bool RenderText::isAllCollapsibleWhitespace() const
813 {
814     const RenderStyle& style = this->style();
815     unsigned length = textLength();
816     if (is8Bit()) {
817         for (unsigned i = 0; i < length; ++i) {
818             if (!style.isCollapsibleWhiteSpace(characters8()[i]))
819                 return false;
820         }
821         return true;
822     }
823     for (unsigned i = 0; i < length; ++i) {
824         if (!style.isCollapsibleWhiteSpace(characters16()[i]))
825             return false;
826     }
827     return true;
828 }
829     
830 bool RenderText::containsOnlyWhitespace(unsigned from, unsigned len) const
831 {
832     ASSERT(m_text);
833     StringImpl& text = *m_text.impl();
834     unsigned currPos;
835     for (currPos = from;
836          currPos < from + len && (text[currPos] == '\n' || text[currPos] == ' ' || text[currPos] == '\t');
837          currPos++) { }
838     return currPos >= (from + len);
839 }
840
841 FloatPoint RenderText::firstRunOrigin() const
842 {
843     return IntPoint(firstRunX(), firstRunY());
844 }
845
846 float RenderText::firstRunX() const
847 {
848     return firstTextBox() ? firstTextBox()->x() : 0;
849 }
850
851 float RenderText::firstRunY() const
852 {
853     return firstTextBox() ? firstTextBox()->y() : 0;
854 }
855     
856 void RenderText::setSelectionState(SelectionState state)
857 {
858     if (state != SelectionNone)
859         ensureLineBoxes();
860
861     RenderObject::setSelectionState(state);
862
863     if (canUpdateSelectionOnRootLineBoxes())
864         m_lineBoxes.setSelectionState(*this, state);
865
866     // The containing block can be null in case of an orphaned tree.
867     RenderBlock* containingBlock = this->containingBlock();
868     if (containingBlock && !containingBlock->isRenderView())
869         containingBlock->setSelectionState(state);
870 }
871
872 void RenderText::setTextWithOffset(const String& text, unsigned offset, unsigned len, bool force)
873 {
874     if (!force && m_text == text)
875         return;
876
877     int delta = text.length() - textLength();
878     unsigned end = len ? offset + len - 1 : offset;
879
880     m_linesDirty = simpleLineLayout() || m_lineBoxes.dirtyRange(*this, offset, end, delta);
881
882     setText(text, force || m_linesDirty);
883 }
884
885 void RenderText::transformText()
886 {
887     String textToTransform = originalText();
888     if (!textToTransform.isNull())
889         setText(textToTransform, true);
890 }
891
892 static inline bool isInlineFlowOrEmptyText(const RenderObject* o)
893 {
894     if (o->isRenderInline())
895         return true;
896     if (!o->isText())
897         return false;
898     StringImpl* text = toRenderText(o)->text();
899     if (!text)
900         return true;
901     return !text->length();
902 }
903
904 UChar RenderText::previousCharacter() const
905 {
906     // find previous text renderer if one exists
907     const RenderObject* previousText = this;
908     while ((previousText = previousText->previousInPreOrder()))
909         if (!isInlineFlowOrEmptyText(previousText))
910             break;
911     UChar prev = ' ';
912     if (previousText && previousText->isText())
913         if (StringImpl* previousString = toRenderText(previousText)->text())
914             prev = (*previousString)[previousString->length() - 1];
915     return prev;
916 }
917
918 void applyTextTransform(const RenderStyle* style, String& text, UChar previousCharacter)
919 {
920     if (!style)
921         return;
922
923     switch (style->textTransform()) {
924     case TTNONE:
925         break;
926     case CAPITALIZE:
927         makeCapitalized(&text, previousCharacter);
928         break;
929     case UPPERCASE:
930         text = text.upper(style->locale());
931         break;
932     case LOWERCASE:
933         text = text.lower(style->locale());
934         break;
935     }
936 }
937
938 void RenderText::setTextInternal(const String& text)
939 {
940     ASSERT(!text.isNull());
941     m_text = text;
942
943     if (m_useBackslashAsYenSymbol)
944         m_text.replace('\\', yenSign);
945
946     ASSERT(m_text);
947
948     applyTextTransform(&style(), m_text, previousCharacter());
949
950     // We use the same characters here as for list markers.
951     // See the listMarkerText function in RenderListMarker.cpp.
952     switch (style().textSecurity()) {
953     case TSNONE:
954         break;
955     case TSCIRCLE:
956         secureText(whiteBullet);
957         break;
958     case TSDISC:
959         secureText(bullet);
960         break;
961     case TSSQUARE:
962         secureText(blackSquare);
963     }
964
965     ASSERT(!m_text.isNull());
966
967     m_isAllASCII = m_text.containsOnlyASCII();
968     m_canUseSimpleFontCodePath = computeCanUseSimpleFontCodePath();
969 }
970
971 void RenderText::secureText(UChar mask)
972 {
973     if (!textLength())
974         return;
975
976     int lastTypedCharacterOffsetToReveal = -1;
977     String revealedText;
978     SecureTextTimer* secureTextTimer = gSecureTextTimers ? gSecureTextTimers->get(this) : 0;
979     if (secureTextTimer && secureTextTimer->isActive()) {
980         lastTypedCharacterOffsetToReveal = secureTextTimer->lastTypedCharacterOffset();
981         if (lastTypedCharacterOffsetToReveal >= 0)
982             revealedText.append(m_text[lastTypedCharacterOffsetToReveal]);
983     }
984
985     m_text.fill(mask);
986     if (lastTypedCharacterOffsetToReveal >= 0) {
987         m_text.replace(lastTypedCharacterOffsetToReveal, 1, revealedText);
988         // m_text may be updated later before timer fires. We invalidate the lastTypedCharacterOffset to avoid inconsistency.
989         secureTextTimer->invalidate();
990     }
991 }
992
993 void RenderText::setText(const String& text, bool force)
994 {
995     ASSERT(!text.isNull());
996
997     if (!force && m_text == text)
998         return;
999
1000     setTextInternal(text);
1001     setNeedsLayoutAndPrefWidthsRecalc();
1002     m_knownToHaveNoOverflowAndNoFallbackFonts = false;
1003
1004     if (parent()->isRenderBlockFlow())
1005         toRenderBlockFlow(parent())->invalidateLineLayoutPath();
1006     
1007     if (AXObjectCache* cache = document().existingAXObjectCache())
1008         cache->textChanged(this);
1009 }
1010
1011 String RenderText::textWithoutConvertingBackslashToYenSymbol() const
1012 {
1013     if (!m_useBackslashAsYenSymbol || style().textSecurity() != TSNONE)
1014         return text();
1015
1016     String text = originalText();
1017     applyTextTransform(&style(), text, previousCharacter());
1018     return text;
1019 }
1020
1021 void RenderText::dirtyLineBoxes(bool fullLayout)
1022 {
1023     if (fullLayout)
1024         m_lineBoxes.deleteAll(*this);
1025     else if (!m_linesDirty)
1026         m_lineBoxes.dirtyAll();
1027     m_linesDirty = false;
1028 }
1029
1030 InlineTextBox* RenderText::createTextBox()
1031 {
1032     return new (renderArena()) InlineTextBox(*this);
1033 }
1034
1035 void RenderText::positionLineBox(InlineBox* box)
1036 {
1037     InlineTextBox* textBox = toInlineTextBox(box);
1038
1039     // FIXME: should not be needed!!!
1040     if (!textBox->len()) {
1041         // We want the box to be destroyed.
1042         textBox->removeFromParent();
1043         m_lineBoxes.remove(*textBox);
1044         textBox->destroy(renderArena());
1045         return;
1046     }
1047
1048     m_containsReversedText |= !textBox->isLeftToRightDirection();
1049 }
1050
1051 void RenderText::ensureLineBoxes()
1052 {
1053     if (!parent()->isRenderBlockFlow())
1054         return;
1055     toRenderBlockFlow(parent())->ensureLineBoxes();
1056 }
1057
1058 const SimpleLineLayout::Layout* RenderText::simpleLineLayout() const
1059 {
1060     if (!parent()->isRenderBlockFlow())
1061         return nullptr;
1062     return toRenderBlockFlow(parent())->simpleLineLayout();
1063 }
1064
1065 float RenderText::width(unsigned from, unsigned len, float xPos, bool firstLine, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
1066 {
1067     if (from >= textLength())
1068         return 0;
1069
1070     if (from + len > textLength())
1071         len = textLength() - from;
1072
1073     const RenderStyle& lineStyle = firstLine ? firstLineStyle() : style();
1074     return width(from, len, lineStyle.font(), xPos, fallbackFonts, glyphOverflow);
1075 }
1076
1077 float RenderText::width(unsigned from, unsigned len, const Font& f, float xPos, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
1078 {
1079     ASSERT(from + len <= textLength());
1080     if (!textLength())
1081         return 0;
1082
1083     const RenderStyle& style = this->style();
1084     float w;
1085     if (&f == &style.font()) {
1086         if (!style.preserveNewline() && !from && len == textLength() && (!glyphOverflow || !glyphOverflow->computeBounds)) {
1087             if (fallbackFonts) {
1088                 ASSERT(glyphOverflow);
1089                 if (preferredLogicalWidthsDirty() || !m_knownToHaveNoOverflowAndNoFallbackFonts) {
1090                     const_cast<RenderText*>(this)->computePreferredLogicalWidths(0, *fallbackFonts, *glyphOverflow);
1091                     if (fallbackFonts->isEmpty() && !glyphOverflow->left && !glyphOverflow->right && !glyphOverflow->top && !glyphOverflow->bottom)
1092                         m_knownToHaveNoOverflowAndNoFallbackFonts = true;
1093                 }
1094                 w = m_maxWidth;
1095             } else
1096                 w = maxLogicalWidth();
1097         } else
1098             w = widthFromCache(f, from, len, xPos, fallbackFonts, glyphOverflow, style);
1099     } else {
1100         TextRun run = RenderBlock::constructTextRun(const_cast<RenderText*>(this), f, this, from, len, style);
1101         run.setCharactersLength(textLength() - from);
1102         ASSERT(run.charactersLength() >= run.length());
1103
1104         run.setCharacterScanForCodePath(!canUseSimpleFontCodePath());
1105         run.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
1106         run.setXPos(xPos);
1107         w = f.width(run, fallbackFonts, glyphOverflow);
1108     }
1109
1110     return w;
1111 }
1112
1113 IntRect RenderText::linesBoundingBox() const
1114 {
1115     if (auto layout = simpleLineLayout())
1116         return SimpleLineLayout::computeTextBoundingBox(*this, *layout);
1117
1118     return m_lineBoxes.boundingBox(*this);
1119 }
1120
1121 LayoutRect RenderText::linesVisualOverflowBoundingBox() const
1122 {
1123     ASSERT(!simpleLineLayout());
1124     return m_lineBoxes.visualOverflowBoundingBox(*this);
1125 }
1126
1127 LayoutRect RenderText::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const
1128 {
1129     RenderObject* rendererToRepaint = containingBlock();
1130
1131     // Do not cross self-painting layer boundaries.
1132     RenderObject& enclosingLayerRenderer = enclosingLayer()->renderer();
1133     if (&enclosingLayerRenderer != rendererToRepaint && !rendererToRepaint->isDescendantOf(&enclosingLayerRenderer))
1134         rendererToRepaint = &enclosingLayerRenderer;
1135
1136     // The renderer we chose to repaint may be an ancestor of repaintContainer, but we need to do a repaintContainer-relative repaint.
1137     if (repaintContainer && repaintContainer != rendererToRepaint && !rendererToRepaint->isDescendantOf(repaintContainer))
1138         return repaintContainer->clippedOverflowRectForRepaint(repaintContainer);
1139
1140     return rendererToRepaint->clippedOverflowRectForRepaint(repaintContainer);
1141 }
1142
1143 LayoutRect RenderText::selectionRectForRepaint(const RenderLayerModelObject* repaintContainer, bool clipToVisibleContent)
1144 {
1145     ASSERT(!needsLayout());
1146     ASSERT(!simpleLineLayout());
1147
1148     if (selectionState() == SelectionNone)
1149         return LayoutRect();
1150     RenderBlock* cb = containingBlock();
1151     if (!cb)
1152         return LayoutRect();
1153
1154     // Now calculate startPos and endPos for painting selection.
1155     // We include a selection while endPos > 0
1156     int startPos, endPos;
1157     if (selectionState() == SelectionInside) {
1158         // We are fully selected.
1159         startPos = 0;
1160         endPos = textLength();
1161     } else {
1162         selectionStartEnd(startPos, endPos);
1163         if (selectionState() == SelectionStart)
1164             endPos = textLength();
1165         else if (selectionState() == SelectionEnd)
1166             startPos = 0;
1167     }
1168
1169     if (startPos == endPos)
1170         return IntRect();
1171
1172     LayoutRect rect = m_lineBoxes.selectionRectForRange(startPos, endPos);
1173
1174     if (clipToVisibleContent)
1175         computeRectForRepaint(repaintContainer, rect);
1176     else {
1177         if (cb->hasColumns())
1178             cb->adjustRectForColumns(rect);
1179
1180         rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
1181     }
1182
1183     return rect;
1184 }
1185
1186 int RenderText::caretMinOffset() const
1187 {
1188     if (auto layout = simpleLineLayout())
1189         return SimpleLineLayout::findTextCaretMinimumOffset(*this, *layout);
1190     return m_lineBoxes.caretMinOffset();
1191 }
1192
1193 int RenderText::caretMaxOffset() const
1194 {
1195     if (auto layout = simpleLineLayout())
1196         return SimpleLineLayout::findTextCaretMaximumOffset(*this, *layout);
1197     return m_lineBoxes.caretMaxOffset(*this);
1198 }
1199
1200 unsigned RenderText::countRenderedCharacterOffsetsUntil(unsigned offset) const
1201 {
1202     ASSERT(!simpleLineLayout());
1203     return m_lineBoxes.countCharacterOffsetsUntil(offset);
1204 }
1205
1206 bool RenderText::containsRenderedCharacterOffset(unsigned offset) const
1207 {
1208     ASSERT(!simpleLineLayout());
1209     return m_lineBoxes.containsOffset(*this, offset, RenderTextLineBoxes::CharacterOffset);
1210 }
1211
1212 bool RenderText::containsCaretOffset(unsigned offset) const
1213 {
1214     if (auto layout = simpleLineLayout())
1215         return SimpleLineLayout::containsTextCaretOffset(*this, *layout, offset);
1216     return m_lineBoxes.containsOffset(*this, offset, RenderTextLineBoxes::CaretOffset);
1217 }
1218
1219 bool RenderText::hasRenderedText() const
1220 {
1221     if (auto layout = simpleLineLayout())
1222         return SimpleLineLayout::isTextRendered(*this, *layout);
1223     return m_lineBoxes.hasRenderedText();
1224 }
1225
1226 int RenderText::previousOffset(int current) const
1227 {
1228     if (isAllASCII() || m_text.is8Bit())
1229         return current - 1;
1230
1231     StringImpl* textImpl = m_text.impl();
1232     TextBreakIterator* iterator = cursorMovementIterator(textImpl->characters16(), textImpl->length());
1233     if (!iterator)
1234         return current - 1;
1235
1236     long result = textBreakPreceding(iterator, current);
1237     if (result == TextBreakDone)
1238         result = current - 1;
1239
1240
1241     return result;
1242 }
1243
1244 #if PLATFORM(MAC) || PLATFORM(EFL)
1245
1246 #define HANGUL_CHOSEONG_START (0x1100)
1247 #define HANGUL_CHOSEONG_END (0x115F)
1248 #define HANGUL_JUNGSEONG_START (0x1160)
1249 #define HANGUL_JUNGSEONG_END (0x11A2)
1250 #define HANGUL_JONGSEONG_START (0x11A8)
1251 #define HANGUL_JONGSEONG_END (0x11F9)
1252 #define HANGUL_SYLLABLE_START (0xAC00)
1253 #define HANGUL_SYLLABLE_END (0xD7AF)
1254 #define HANGUL_JONGSEONG_COUNT (28)
1255
1256 enum HangulState {
1257     HangulStateL,
1258     HangulStateV,
1259     HangulStateT,
1260     HangulStateLV,
1261     HangulStateLVT,
1262     HangulStateBreak
1263 };
1264
1265 inline bool isHangulLVT(UChar32 character)
1266 {
1267     return (character - HANGUL_SYLLABLE_START) % HANGUL_JONGSEONG_COUNT;
1268 }
1269
1270 inline bool isMark(UChar32 c)
1271 {
1272     int8_t charType = u_charType(c);
1273     return charType == U_NON_SPACING_MARK || charType == U_ENCLOSING_MARK || charType == U_COMBINING_SPACING_MARK;
1274 }
1275
1276 inline bool isRegionalIndicator(UChar32 c)
1277 {
1278     // National flag emoji each consists of a pair of regional indicator symbols.
1279     return 0x1F1E6 <= c && c <= 0x1F1FF;
1280 }
1281
1282 #endif
1283
1284 int RenderText::previousOffsetForBackwardDeletion(int current) const
1285 {
1286 #if PLATFORM(MAC) || PLATFORM(EFL)
1287     ASSERT(m_text);
1288     StringImpl& text = *m_text.impl();
1289     UChar32 character;
1290     bool sawRegionalIndicator = false;
1291     while (current > 0) {
1292         if (U16_IS_TRAIL(text[--current]))
1293             --current;
1294         if (current < 0)
1295             break;
1296
1297         UChar32 character = text.characterStartingAt(current);
1298
1299         if (sawRegionalIndicator) {
1300             // We don't check if the pair of regional indicator symbols before current position can actually be combined
1301             // into a flag, and just delete it. This may not agree with how the pair is rendered in edge cases,
1302             // but is good enough in practice.
1303             if (isRegionalIndicator(character))
1304                 break;
1305             // Don't delete a preceding character that isn't a regional indicator symbol.
1306             U16_FWD_1_UNSAFE(text, current);
1307         }
1308
1309         // We don't combine characters in Armenian ... Limbu range for backward deletion.
1310         if ((character >= 0x0530) && (character < 0x1950))
1311             break;
1312
1313         if (isRegionalIndicator(character)) {
1314             sawRegionalIndicator = true;
1315             continue;
1316         }
1317
1318         if (!isMark(character) && (character != 0xFF9E) && (character != 0xFF9F))
1319             break;
1320     }
1321
1322     if (current <= 0)
1323         return current;
1324
1325     // Hangul
1326     character = text.characterStartingAt(current);
1327     if (((character >= HANGUL_CHOSEONG_START) && (character <= HANGUL_JONGSEONG_END)) || ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))) {
1328         HangulState state;
1329
1330         if (character < HANGUL_JUNGSEONG_START)
1331             state = HangulStateL;
1332         else if (character < HANGUL_JONGSEONG_START)
1333             state = HangulStateV;
1334         else if (character < HANGUL_SYLLABLE_START)
1335             state = HangulStateT;
1336         else
1337             state = isHangulLVT(character) ? HangulStateLVT : HangulStateLV;
1338
1339         while (current > 0 && ((character = text.characterStartingAt(current - 1)) >= HANGUL_CHOSEONG_START) && (character <= HANGUL_SYLLABLE_END) && ((character <= HANGUL_JONGSEONG_END) || (character >= HANGUL_SYLLABLE_START))) {
1340             switch (state) {
1341             case HangulStateV:
1342                 if (character <= HANGUL_CHOSEONG_END)
1343                     state = HangulStateL;
1344                 else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END) && !isHangulLVT(character))
1345                     state = HangulStateLV;
1346                 else if (character > HANGUL_JUNGSEONG_END)
1347                     state = HangulStateBreak;
1348                 break;
1349             case HangulStateT:
1350                 if ((character >= HANGUL_JUNGSEONG_START) && (character <= HANGUL_JUNGSEONG_END))
1351                     state = HangulStateV;
1352                 else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))
1353                     state = (isHangulLVT(character) ? HangulStateLVT : HangulStateLV);
1354                 else if (character < HANGUL_JUNGSEONG_START)
1355                     state = HangulStateBreak;
1356                 break;
1357             default:
1358                 state = (character < HANGUL_JUNGSEONG_START) ? HangulStateL : HangulStateBreak;
1359                 break;
1360             }
1361             if (state == HangulStateBreak)
1362                 break;
1363
1364             --current;
1365         }
1366     }
1367
1368     return current;
1369 #else
1370     // Platforms other than Mac delete by one code point.
1371     if (U16_IS_TRAIL(m_text[--current]))
1372         --current;
1373     if (current < 0)
1374         current = 0;
1375     return current;
1376 #endif
1377 }
1378
1379 int RenderText::nextOffset(int current) const
1380 {
1381     if (isAllASCII() || m_text.is8Bit())
1382         return current + 1;
1383
1384     StringImpl* textImpl = m_text.impl();
1385     TextBreakIterator* iterator = cursorMovementIterator(textImpl->characters16(), textImpl->length());
1386     if (!iterator)
1387         return current + 1;
1388
1389     long result = textBreakFollowing(iterator, current);
1390     if (result == TextBreakDone)
1391         result = current + 1;
1392
1393     return result;
1394 }
1395
1396 bool RenderText::computeCanUseSimpleFontCodePath() const
1397 {
1398     if (isAllASCII() || m_text.is8Bit())
1399         return true;
1400     return Font::characterRangeCodePath(characters(), length()) == Font::Simple;
1401 }
1402
1403 void RenderText::momentarilyRevealLastTypedCharacter(unsigned lastTypedCharacterOffset)
1404 {
1405     if (!gSecureTextTimers)
1406         gSecureTextTimers = new SecureTextTimerMap;
1407
1408     SecureTextTimer* secureTextTimer = gSecureTextTimers->get(this);
1409     if (!secureTextTimer) {
1410         secureTextTimer = new SecureTextTimer(this);
1411         gSecureTextTimers->add(this, secureTextTimer);
1412     }
1413     secureTextTimer->restartWithNewText(lastTypedCharacterOffset);
1414 }
1415
1416 } // namespace WebCore