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