• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * (C) 1999 Lars Knoll (knoll@kde.org)
3  * (C) 2000 Dirk Mueller (mueller@kde.org)
4  * Copyright (C) 2004, 2005, 2006, 2007 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 "FontTranscoder.h"
32 #include "FrameView.h"
33 #include "InlineTextBox.h"
34 #include "Range.h"
35 #include "RenderArena.h"
36 #include "RenderBlock.h"
37 #include "RenderCombineText.h"
38 #include "RenderLayer.h"
39 #include "RenderView.h"
40 #include "Settings.h"
41 #include "Text.h"
42 #include "TextBreakIterator.h"
43 #include "TextResourceDecoder.h"
44 #include "TextRun.h"
45 #include "VisiblePosition.h"
46 #include "break_lines.h"
47 #include <wtf/AlwaysInline.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 class SecureTextTimer;
58 typedef HashMap<RenderText*, SecureTextTimer*> SecureTextTimerMap;
59 static SecureTextTimerMap* gSecureTextTimers = 0;
60 
61 class SecureTextTimer : public TimerBase {
62 public:
SecureTextTimer(RenderText * renderText)63     SecureTextTimer(RenderText* renderText)
64         : m_renderText(renderText)
65         , m_lastTypedCharacterOffset(-1)
66     {
67     }
68 
restartWithNewText(unsigned lastTypedCharacterOffset)69     void restartWithNewText(unsigned lastTypedCharacterOffset)
70     {
71         m_lastTypedCharacterOffset = lastTypedCharacterOffset;
72         startOneShot(m_renderText->document()->settings()->passwordEchoDurationInSeconds());
73     }
invalidate()74     void invalidate() { m_lastTypedCharacterOffset = -1; }
lastTypedCharacterOffset()75     unsigned lastTypedCharacterOffset() { return m_lastTypedCharacterOffset; }
76 
77 private:
fired()78     virtual void fired()
79     {
80         ASSERT(gSecureTextTimers->contains(m_renderText));
81         m_renderText->setText(m_renderText->text(), true /* forcing setting text as it may be masked later */);
82     }
83 
84     RenderText* m_renderText;
85     int m_lastTypedCharacterOffset;
86 };
87 
makeCapitalized(String * string,UChar previous)88 static void makeCapitalized(String* string, UChar previous)
89 {
90     if (string->isNull())
91         return;
92 
93     unsigned length = string->length();
94     const UChar* characters = string->characters();
95 
96     if (length >= numeric_limits<unsigned>::max())
97         CRASH();
98 
99     StringBuffer stringWithPrevious(length + 1);
100     stringWithPrevious[0] = previous == noBreakSpace ? ' ' : previous;
101     for (unsigned i = 1; i < length + 1; i++) {
102         // Replace &nbsp with a real space since ICU no longer treats &nbsp as a word separator.
103         if (characters[i - 1] == noBreakSpace)
104             stringWithPrevious[i] = ' ';
105         else
106             stringWithPrevious[i] = characters[i - 1];
107     }
108 
109     TextBreakIterator* boundary = wordBreakIterator(stringWithPrevious.characters(), length + 1);
110     if (!boundary)
111         return;
112 
113     StringBuffer data(length);
114 
115     int32_t endOfWord;
116     int32_t startOfWord = textBreakFirst(boundary);
117     for (endOfWord = textBreakNext(boundary); endOfWord != TextBreakDone; startOfWord = endOfWord, endOfWord = textBreakNext(boundary)) {
118         if (startOfWord != 0) // Ignore first char of previous string
119             data[startOfWord - 1] = characters[startOfWord - 1] == noBreakSpace ? noBreakSpace : toTitleCase(stringWithPrevious[startOfWord]);
120         for (int i = startOfWord + 1; i < endOfWord; i++)
121             data[i - 1] = characters[i - 1];
122     }
123 
124     *string = String::adopt(data);
125 }
126 
RenderText(Node * node,PassRefPtr<StringImpl> str)127 RenderText::RenderText(Node* node, PassRefPtr<StringImpl> str)
128      : RenderObject(node)
129      , m_minWidth(-1)
130      , m_text(str)
131      , m_firstTextBox(0)
132      , m_lastTextBox(0)
133      , m_maxWidth(-1)
134      , m_beginMinWidth(0)
135      , m_endMinWidth(0)
136      , m_hasTab(false)
137      , m_linesDirty(false)
138      , m_containsReversedText(false)
139      , m_isAllASCII(m_text.containsOnlyASCII())
140      , m_knownToHaveNoOverflowAndNoFallbackFonts(false)
141      , m_needsTranscoding(false)
142 {
143     ASSERT(m_text);
144 
145     setIsText();
146 
147     // FIXME: It would be better to call this only if !m_text->containsOnlyWhitespace().
148     // But that might slow things down, and maybe should only be done if visuallyNonEmpty
149     // is still false. Not making any change for now, but should consider in the future.
150     view()->frameView()->setIsVisuallyNonEmpty();
151 }
152 
153 #ifndef NDEBUG
154 
~RenderText()155 RenderText::~RenderText()
156 {
157     ASSERT(!m_firstTextBox);
158     ASSERT(!m_lastTextBox);
159 }
160 
161 #endif
162 
renderName() const163 const char* RenderText::renderName() const
164 {
165     return "RenderText";
166 }
167 
isTextFragment() const168 bool RenderText::isTextFragment() const
169 {
170     return false;
171 }
172 
isWordBreak() const173 bool RenderText::isWordBreak() const
174 {
175     return false;
176 }
177 
updateNeedsTranscoding()178 void RenderText::updateNeedsTranscoding()
179 {
180     const TextEncoding* encoding = document()->decoder() ? &document()->decoder()->encoding() : 0;
181     m_needsTranscoding = fontTranscoder().needsTranscoding(style()->font().fontDescription(), encoding);
182 }
183 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)184 void RenderText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
185 {
186     // There is no need to ever schedule repaints from a style change of a text run, since
187     // we already did this for the parent of the text run.
188     // We do have to schedule layouts, though, since a style change can force us to
189     // need to relayout.
190     if (diff == StyleDifferenceLayout) {
191         setNeedsLayoutAndPrefWidthsRecalc();
192         m_knownToHaveNoOverflowAndNoFallbackFonts = false;
193     }
194 
195     bool needsResetText = false;
196     if (!oldStyle) {
197         updateNeedsTranscoding();
198         needsResetText = m_needsTranscoding;
199     } else if (oldStyle->font().needsTranscoding() != style()->font().needsTranscoding() || (style()->font().needsTranscoding() && oldStyle->font().family().family() != style()->font().family().family())) {
200         updateNeedsTranscoding();
201         needsResetText = true;
202     }
203 
204     ETextTransform oldTransform = oldStyle ? oldStyle->textTransform() : TTNONE;
205     ETextSecurity oldSecurity = oldStyle ? oldStyle->textSecurity() : TSNONE;
206     if (needsResetText || oldTransform != style()->textTransform() || oldSecurity != style()->textSecurity()) {
207         if (RefPtr<StringImpl> textToTransform = originalText())
208             setText(textToTransform.release(), true);
209     }
210 }
211 
removeAndDestroyTextBoxes()212 void RenderText::removeAndDestroyTextBoxes()
213 {
214     if (!documentBeingDestroyed()) {
215         if (firstTextBox()) {
216             if (isBR()) {
217                 RootInlineBox* next = firstTextBox()->root()->nextRootBox();
218                 if (next)
219                     next->markDirty();
220             }
221             for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
222                 box->remove();
223         } else if (parent())
224             parent()->dirtyLinesFromChangedChild(this);
225     }
226     deleteTextBoxes();
227 }
228 
destroy()229 void RenderText::destroy()
230 {
231     if (SecureTextTimer* secureTextTimer = gSecureTextTimers ? gSecureTextTimers->take(this) : 0)
232         delete secureTextTimer;
233 
234     removeAndDestroyTextBoxes();
235     RenderObject::destroy();
236 }
237 
extractTextBox(InlineTextBox * box)238 void RenderText::extractTextBox(InlineTextBox* box)
239 {
240     checkConsistency();
241 
242     m_lastTextBox = box->prevTextBox();
243     if (box == m_firstTextBox)
244         m_firstTextBox = 0;
245     if (box->prevTextBox())
246         box->prevTextBox()->setNextTextBox(0);
247     box->setPreviousTextBox(0);
248     for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox())
249         curr->setExtracted();
250 
251     checkConsistency();
252 }
253 
attachTextBox(InlineTextBox * box)254 void RenderText::attachTextBox(InlineTextBox* box)
255 {
256     checkConsistency();
257 
258     if (m_lastTextBox) {
259         m_lastTextBox->setNextTextBox(box);
260         box->setPreviousTextBox(m_lastTextBox);
261     } else
262         m_firstTextBox = box;
263     InlineTextBox* last = box;
264     for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox()) {
265         curr->setExtracted(false);
266         last = curr;
267     }
268     m_lastTextBox = last;
269 
270     checkConsistency();
271 }
272 
removeTextBox(InlineTextBox * box)273 void RenderText::removeTextBox(InlineTextBox* box)
274 {
275     checkConsistency();
276 
277     if (box == m_firstTextBox)
278         m_firstTextBox = box->nextTextBox();
279     if (box == m_lastTextBox)
280         m_lastTextBox = box->prevTextBox();
281     if (box->nextTextBox())
282         box->nextTextBox()->setPreviousTextBox(box->prevTextBox());
283     if (box->prevTextBox())
284         box->prevTextBox()->setNextTextBox(box->nextTextBox());
285 
286     checkConsistency();
287 }
288 
deleteTextBoxes()289 void RenderText::deleteTextBoxes()
290 {
291     if (firstTextBox()) {
292         RenderArena* arena = renderArena();
293         InlineTextBox* next;
294         for (InlineTextBox* curr = firstTextBox(); curr; curr = next) {
295             next = curr->nextTextBox();
296             curr->destroy(arena);
297         }
298         m_firstTextBox = m_lastTextBox = 0;
299     }
300 }
301 
originalText() const302 PassRefPtr<StringImpl> RenderText::originalText() const
303 {
304     Node* e = node();
305     return (e && e->isTextNode()) ? static_cast<Text*>(e)->dataImpl() : 0;
306 }
307 
absoluteRects(Vector<IntRect> & rects,int tx,int ty)308 void RenderText::absoluteRects(Vector<IntRect>& rects, int tx, int ty)
309 {
310     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
311         rects.append(enclosingIntRect(FloatRect(tx + box->x(), ty + box->y(), box->width(), box->height())));
312 }
313 
absoluteRectsForRange(Vector<IntRect> & rects,unsigned start,unsigned end,bool useSelectionHeight)314 void RenderText::absoluteRectsForRange(Vector<IntRect>& rects, unsigned start, unsigned end, bool useSelectionHeight)
315 {
316     // Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX
317     // to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this
318     // function to take ints causes various internal mismatches. But selectionRect takes ints, and
319     // passing UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take unsigneds, but
320     // that would cause many ripple effects, so for now we'll just clamp our unsigned parameters to INT_MAX.
321     ASSERT(end == UINT_MAX || end <= INT_MAX);
322     ASSERT(start <= INT_MAX);
323     start = min(start, static_cast<unsigned>(INT_MAX));
324     end = min(end, static_cast<unsigned>(INT_MAX));
325 
326     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
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             IntRect r = IntRect(box->x(), box->y(), box->logicalWidth(), box->logicalHeight());
330             if (useSelectionHeight) {
331                 IntRect selectionRect = box->selectionRect(0, 0, start, end);
332                 r.setHeight(selectionRect.height());
333                 r.setY(selectionRect.y());
334             }
335             FloatPoint origin = localToAbsolute(r.location());
336             r.setX(origin.x());
337             r.setY(origin.y());
338             rects.append(r);
339         } else {
340             unsigned realEnd = min(box->end() + 1, end);
341             IntRect r = box->selectionRect(0, 0, start, realEnd);
342             if (!r.isEmpty()) {
343                 if (!useSelectionHeight) {
344                     // change the height and y position because selectionRect uses selection-specific values
345                     r.setHeight(box->logicalHeight());
346                     r.setY(box->y());
347                 }
348                 FloatPoint origin = localToAbsolute(r.location());
349                 localToAbsolute(origin);
350                 r.setX(origin.x());
351                 r.setY(origin.y());
352                 rects.append(r);
353             }
354         }
355     }
356 }
357 
ellipsisRectForBox(InlineTextBox * box,unsigned startPos,unsigned endPos)358 static IntRect ellipsisRectForBox(InlineTextBox* box, unsigned startPos, unsigned endPos)
359 {
360     if (!box)
361         return IntRect();
362 
363     unsigned short truncation = box->truncation();
364     if (truncation == cNoTruncation)
365         return IntRect();
366 
367     IntRect rect;
368     if (EllipsisBox* ellipsis = box->root()->ellipsisBox()) {
369         int ellipsisStartPosition = max<int>(startPos - box->start(), 0);
370         int ellipsisEndPosition = min<int>(endPos - box->start(), box->len());
371 
372         // The ellipsis should be considered to be selected if the end of
373         // the selection is past the beginning of the truncation and the
374         // beginning of the selection is before or at the beginning of the truncation.
375         if (ellipsisEndPosition >= truncation && ellipsisStartPosition <= truncation)
376             return ellipsis->selectionRect(0, 0);
377     }
378 
379     return IntRect();
380 }
381 
absoluteQuads(Vector<FloatQuad> & quads,ClippingOption option)382 void RenderText::absoluteQuads(Vector<FloatQuad>& quads, ClippingOption option)
383 {
384     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
385         IntRect boundaries = box->calculateBoundaries();
386 
387         // Shorten the width of this text box if it ends in an ellipsis.
388         IntRect ellipsisRect = (option == ClipToEllipsis) ? ellipsisRectForBox(box, 0, textLength()) : IntRect();
389         if (!ellipsisRect.isEmpty()) {
390             if (style()->isHorizontalWritingMode())
391                 boundaries.setWidth(ellipsisRect.maxX() - boundaries.x());
392             else
393                 boundaries.setHeight(ellipsisRect.maxY() - boundaries.y());
394         }
395         quads.append(localToAbsoluteQuad(FloatRect(boundaries)));
396     }
397 }
398 
absoluteQuads(Vector<FloatQuad> & quads)399 void RenderText::absoluteQuads(Vector<FloatQuad>& quads)
400 {
401     absoluteQuads(quads, NoClipping);
402 }
403 
absoluteQuadsForRange(Vector<FloatQuad> & quads,unsigned start,unsigned end,bool useSelectionHeight)404 void RenderText::absoluteQuadsForRange(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool useSelectionHeight)
405 {
406     // Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX
407     // to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this
408     // function to take ints causes various internal mismatches. But selectionRect takes ints, and
409     // passing UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take unsigneds, but
410     // that would cause many ripple effects, so for now we'll just clamp our unsigned parameters to INT_MAX.
411     ASSERT(end == UINT_MAX || end <= INT_MAX);
412     ASSERT(start <= INT_MAX);
413     start = min(start, static_cast<unsigned>(INT_MAX));
414     end = min(end, static_cast<unsigned>(INT_MAX));
415 
416     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
417         // Note: box->end() returns the index of the last character, not the index past it
418         if (start <= box->start() && box->end() < end) {
419             IntRect r(box->calculateBoundaries());
420             if (useSelectionHeight) {
421                 IntRect selectionRect = box->selectionRect(0, 0, start, end);
422                 if (box->isHorizontal()) {
423                     r.setHeight(selectionRect.height());
424                     r.setY(selectionRect.y());
425                 } else {
426                     r.setWidth(selectionRect.width());
427                     r.setX(selectionRect.x());
428                 }
429             }
430             quads.append(localToAbsoluteQuad(FloatRect(r)));
431         } else {
432             unsigned realEnd = min(box->end() + 1, end);
433             IntRect r = box->selectionRect(0, 0, start, realEnd);
434             if (r.height()) {
435                 if (!useSelectionHeight) {
436                     // change the height and y position because selectionRect uses selection-specific values
437                     if (box->isHorizontal()) {
438                         r.setHeight(box->logicalHeight());
439                         r.setY(box->y());
440                     } else {
441                         r.setWidth(box->logicalHeight());
442                         r.setX(box->x());
443                     }
444                 }
445                 quads.append(localToAbsoluteQuad(FloatRect(r)));
446             }
447         }
448     }
449 }
450 
findNextInlineTextBox(int offset,int & pos) const451 InlineTextBox* RenderText::findNextInlineTextBox(int offset, int& pos) const
452 {
453     // The text runs point to parts of the RenderText's m_text
454     // (they don't include '\n')
455     // Find the text run that includes the character at offset
456     // and return pos, which is the position of the char in the run.
457 
458     if (!m_firstTextBox)
459         return 0;
460 
461     InlineTextBox* s = m_firstTextBox;
462     int off = s->len();
463     while (offset > off && s->nextTextBox()) {
464         s = s->nextTextBox();
465         off = s->start() + s->len();
466     }
467     // we are now in the correct text run
468     pos = (offset > off ? s->len() : s->len() - (off - offset) );
469     return s;
470 }
471 
positionForPoint(const IntPoint & point)472 VisiblePosition RenderText::positionForPoint(const IntPoint& point)
473 {
474     if (!firstTextBox() || textLength() == 0)
475         return createVisiblePosition(0, DOWNSTREAM);
476 
477     // Get the offset for the position, since this will take rtl text into account.
478     int offset;
479 
480     int pointLineDirection = firstTextBox()->isHorizontal() ? point.x() : point.y();
481     int pointBlockDirection = firstTextBox()->isHorizontal() ? point.y() : point.x();
482 
483     // FIXME: We should be able to roll these special cases into the general cases in the loop below.
484     if (firstTextBox() && pointBlockDirection <  firstTextBox()->root()->selectionBottom() && pointLineDirection < firstTextBox()->logicalLeft()) {
485         // at the y coordinate of the first line or above
486         // and the x coordinate is to the left of the first text box left edge
487         offset = firstTextBox()->offsetForPosition(pointLineDirection);
488         return createVisiblePosition(offset + firstTextBox()->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
489     }
490     if (lastTextBox() && pointBlockDirection >= lastTextBox()->root()->selectionTop() && pointLineDirection >= lastTextBox()->logicalRight()) {
491         // at the y coordinate of the last line or below
492         // and the x coordinate is to the right of the last text box right edge
493         offset = lastTextBox()->offsetForPosition(pointLineDirection);
494         return createVisiblePosition(offset + lastTextBox()->start(), VP_UPSTREAM_IF_POSSIBLE);
495     }
496 
497     InlineTextBox* lastBoxAbove = 0;
498     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
499         RootInlineBox* rootBox = box->root();
500         if (pointBlockDirection >= rootBox->selectionTop()) {
501             int bottom = rootBox->selectionBottom();
502             if (rootBox->nextRootBox())
503                 bottom = min(bottom, rootBox->nextRootBox()->lineTop());
504             if (pointBlockDirection < bottom) {
505                 offset = box->offsetForPosition(pointLineDirection);
506 
507                 if (pointLineDirection == box->logicalLeft())
508                     // the x coordinate is equal to the left edge of this box
509                     // the affinity must be downstream so the position doesn't jump back to the previous line
510                     return createVisiblePosition(offset + box->start(), DOWNSTREAM);
511 
512                 if (pointLineDirection < box->logicalRight())
513                     // and the x coordinate is to the left of the right edge of this box
514                     // check to see if position goes in this box
515                     return createVisiblePosition(offset + box->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
516 
517                 if (!box->prevOnLine() && pointLineDirection < box->logicalLeft())
518                     // box is first on line
519                     // and the x coordinate is to the left of the first text box left edge
520                     return createVisiblePosition(offset + box->start(), DOWNSTREAM);
521 
522                 if (!box->nextOnLine())
523                     // box is last on line
524                     // and the x coordinate is to the right of the last text box right edge
525                     // generate VisiblePosition, use UPSTREAM affinity if possible
526                     return createVisiblePosition(offset + box->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
527             }
528             lastBoxAbove = box;
529         }
530     }
531 
532     return createVisiblePosition(lastBoxAbove ? lastBoxAbove->start() + lastBoxAbove->len() : 0, DOWNSTREAM);
533 }
534 
localCaretRect(InlineBox * inlineBox,int caretOffset,int * extraWidthToEndOfLine)535 IntRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine)
536 {
537     if (!inlineBox)
538         return IntRect();
539 
540     ASSERT(inlineBox->isInlineTextBox());
541     if (!inlineBox->isInlineTextBox())
542         return IntRect();
543 
544     InlineTextBox* box = static_cast<InlineTextBox*>(inlineBox);
545 
546     int height = box->root()->selectionHeight();
547     int top = box->root()->selectionTop();
548 
549     // Go ahead and round left to snap it to the nearest pixel.
550     float left = box->positionForOffset(caretOffset);
551 
552     // Distribute the caret's width to either side of the offset.
553     int caretWidthLeftOfOffset = caretWidth / 2;
554     left -= caretWidthLeftOfOffset;
555     int caretWidthRightOfOffset = caretWidth - caretWidthLeftOfOffset;
556 
557     left = roundf(left);
558 
559     float rootLeft = box->root()->logicalLeft();
560     float rootRight = box->root()->logicalRight();
561 
562     // FIXME: should we use the width of the root inline box or the
563     // width of the containing block for this?
564     if (extraWidthToEndOfLine)
565         *extraWidthToEndOfLine = (box->root()->logicalWidth() + rootLeft) - (left + 1);
566 
567     RenderBlock* cb = containingBlock();
568     RenderStyle* cbStyle = cb->style();
569     float leftEdge;
570     float rightEdge;
571     if (style()->autoWrap()) {
572         leftEdge = cb->logicalLeft();
573         rightEdge = cb->logicalRight();
574     } else {
575         leftEdge = min(static_cast<float>(cb->logicalLeft()), rootLeft);
576         rightEdge = max(static_cast<float>(cb->logicalRight()), rootRight);
577     }
578 
579     bool rightAligned = false;
580     switch (cbStyle->textAlign()) {
581     case TAAUTO:
582     case JUSTIFY:
583         rightAligned = !cbStyle->isLeftToRightDirection();
584         break;
585     case RIGHT:
586     case WEBKIT_RIGHT:
587         rightAligned = true;
588         break;
589     case LEFT:
590     case WEBKIT_LEFT:
591     case CENTER:
592     case WEBKIT_CENTER:
593         break;
594     case TASTART:
595         rightAligned = !cbStyle->isLeftToRightDirection();
596         break;
597     case TAEND:
598         rightAligned = cbStyle->isLeftToRightDirection();
599         break;
600     }
601 
602     if (rightAligned) {
603         left = max(left, leftEdge);
604         left = min(left, rootRight - caretWidth);
605     } else {
606         left = min(left, rightEdge - caretWidthRightOfOffset);
607         left = max(left, rootLeft);
608     }
609 
610     return style()->isHorizontalWritingMode() ? IntRect(left, top, caretWidth, height) : IntRect(top, left, height, caretWidth);
611 }
612 
widthFromCache(const Font & f,int start,int len,float xPos,HashSet<const SimpleFontData * > * fallbackFonts,GlyphOverflow * glyphOverflow) const613 ALWAYS_INLINE float RenderText::widthFromCache(const Font& f, int start, int len, float xPos, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
614 {
615     if (style()->hasTextCombine() && isCombineText()) {
616         const RenderCombineText* combineText = toRenderCombineText(this);
617         if (combineText->isCombined())
618             return combineText->combinedTextWidth(f);
619     }
620 
621     if (f.isFixedPitch() && !f.isSmallCaps() && m_isAllASCII && (!glyphOverflow || !glyphOverflow->computeBounds)) {
622         float monospaceCharacterWidth = f.spaceWidth();
623         float tabWidth = allowTabs() ? monospaceCharacterWidth * 8 : 0;
624         float w = 0;
625         bool isSpace;
626         bool previousCharWasSpace = true; // FIXME: Preserves historical behavior, but seems wrong for start > 0.
627         ASSERT(m_text);
628         StringImpl& text = *m_text.impl();
629         for (int i = start; i < start + len; i++) {
630             char c = text[i];
631             if (c <= ' ') {
632                 if (c == ' ' || c == '\n') {
633                     w += monospaceCharacterWidth;
634                     isSpace = true;
635                 } else if (c == '\t') {
636                     w += tabWidth ? tabWidth - fmodf(xPos + w, tabWidth) : monospaceCharacterWidth;
637                     isSpace = true;
638                 } else
639                     isSpace = false;
640             } else {
641                 w += monospaceCharacterWidth;
642                 isSpace = false;
643             }
644             if (isSpace && !previousCharWasSpace)
645                 w += f.wordSpacing();
646             previousCharWasSpace = isSpace;
647         }
648         return w;
649     }
650 
651     return f.width(TextRun(text()->characters() + start, len, allowTabs(), xPos), fallbackFonts, glyphOverflow);
652 }
653 
trimmedPrefWidths(float leadWidth,float & beginMinW,bool & beginWS,float & endMinW,bool & endWS,bool & hasBreakableChar,bool & hasBreak,float & beginMaxW,float & endMaxW,float & minW,float & maxW,bool & stripFrontSpaces)654 void RenderText::trimmedPrefWidths(float leadWidth,
655                                    float& beginMinW, bool& beginWS,
656                                    float& endMinW, bool& endWS,
657                                    bool& hasBreakableChar, bool& hasBreak,
658                                    float& beginMaxW, float& endMaxW,
659                                    float& minW, float& maxW, bool& stripFrontSpaces)
660 {
661     bool collapseWhiteSpace = style()->collapseWhiteSpace();
662     if (!collapseWhiteSpace)
663         stripFrontSpaces = false;
664 
665     if (m_hasTab || preferredLogicalWidthsDirty())
666         computePreferredLogicalWidths(leadWidth);
667 
668     beginWS = !stripFrontSpaces && m_hasBeginWS;
669     endWS = m_hasEndWS;
670 
671     int len = textLength();
672 
673     if (!len || (stripFrontSpaces && text()->containsOnlyWhitespace())) {
674         beginMinW = 0;
675         endMinW = 0;
676         beginMaxW = 0;
677         endMaxW = 0;
678         minW = 0;
679         maxW = 0;
680         hasBreak = false;
681         return;
682     }
683 
684     minW = m_minWidth;
685     maxW = m_maxWidth;
686 
687     beginMinW = m_beginMinWidth;
688     endMinW = m_endMinWidth;
689 
690     hasBreakableChar = m_hasBreakableChar;
691     hasBreak = m_hasBreak;
692 
693     ASSERT(m_text);
694     StringImpl& text = *m_text.impl();
695     if (text[0] == ' ' || (text[0] == '\n' && !style()->preserveNewline()) || text[0] == '\t') {
696         const Font& f = style()->font(); // FIXME: This ignores first-line.
697         if (stripFrontSpaces) {
698             const UChar space = ' ';
699             float spaceWidth = f.width(TextRun(&space, 1));
700             maxW -= spaceWidth;
701         } else
702             maxW += f.wordSpacing();
703     }
704 
705     stripFrontSpaces = collapseWhiteSpace && m_hasEndWS;
706 
707     if (!style()->autoWrap() || minW > maxW)
708         minW = maxW;
709 
710     // Compute our max widths by scanning the string for newlines.
711     if (hasBreak) {
712         const Font& f = style()->font(); // FIXME: This ignores first-line.
713         bool firstLine = true;
714         beginMaxW = maxW;
715         endMaxW = maxW;
716         for (int i = 0; i < len; i++) {
717             int linelen = 0;
718             while (i + linelen < len && text[i + linelen] != '\n')
719                 linelen++;
720 
721             if (linelen) {
722                 endMaxW = widthFromCache(f, i, linelen, leadWidth + endMaxW, 0, 0);
723                 if (firstLine) {
724                     firstLine = false;
725                     leadWidth = 0;
726                     beginMaxW = endMaxW;
727                 }
728                 i += linelen;
729             } else if (firstLine) {
730                 beginMaxW = 0;
731                 firstLine = false;
732                 leadWidth = 0;
733             }
734 
735             if (i == len - 1)
736                 // A <pre> run that ends with a newline, as in, e.g.,
737                 // <pre>Some text\n\n<span>More text</pre>
738                 endMaxW = 0;
739         }
740     }
741 }
742 
isSpaceAccordingToStyle(UChar c,RenderStyle * style)743 static inline bool isSpaceAccordingToStyle(UChar c, RenderStyle* style)
744 {
745     return c == ' ' || (c == noBreakSpace && style->nbspMode() == SPACE);
746 }
747 
minLogicalWidth() const748 float RenderText::minLogicalWidth() const
749 {
750     if (preferredLogicalWidthsDirty())
751         const_cast<RenderText*>(this)->computePreferredLogicalWidths(0);
752 
753     return m_minWidth;
754 }
755 
maxLogicalWidth() const756 float RenderText::maxLogicalWidth() const
757 {
758     if (preferredLogicalWidthsDirty())
759         const_cast<RenderText*>(this)->computePreferredLogicalWidths(0);
760 
761     return m_maxWidth;
762 }
763 
computePreferredLogicalWidths(float leadWidth)764 void RenderText::computePreferredLogicalWidths(float leadWidth)
765 {
766     HashSet<const SimpleFontData*> fallbackFonts;
767     GlyphOverflow glyphOverflow;
768     computePreferredLogicalWidths(leadWidth, fallbackFonts, glyphOverflow);
769     if (fallbackFonts.isEmpty() && !glyphOverflow.left && !glyphOverflow.right && !glyphOverflow.top && !glyphOverflow.bottom)
770         m_knownToHaveNoOverflowAndNoFallbackFonts = true;
771 }
772 
computePreferredLogicalWidths(float leadWidth,HashSet<const SimpleFontData * > & fallbackFonts,GlyphOverflow & glyphOverflow)773 void RenderText::computePreferredLogicalWidths(float leadWidth, HashSet<const SimpleFontData*>& fallbackFonts, GlyphOverflow& glyphOverflow)
774 {
775     ASSERT(m_hasTab || preferredLogicalWidthsDirty() || !m_knownToHaveNoOverflowAndNoFallbackFonts);
776 
777     m_minWidth = 0;
778     m_beginMinWidth = 0;
779     m_endMinWidth = 0;
780     m_maxWidth = 0;
781 
782     if (isBR())
783         return;
784 
785     float currMinWidth = 0;
786     float currMaxWidth = 0;
787     m_hasBreakableChar = false;
788     m_hasBreak = false;
789     m_hasTab = false;
790     m_hasBeginWS = false;
791     m_hasEndWS = false;
792 
793     const Font& f = style()->font(); // FIXME: This ignores first-line.
794     float wordSpacing = style()->wordSpacing();
795     int len = textLength();
796     const UChar* txt = characters();
797     LazyLineBreakIterator breakIterator(txt, len);
798     bool needsWordSpacing = false;
799     bool ignoringSpaces = false;
800     bool isSpace = false;
801     bool firstWord = true;
802     bool firstLine = true;
803     int nextBreakable = -1;
804     int lastWordBoundary = 0;
805 
806     int firstGlyphLeftOverflow = -1;
807 
808     bool breakNBSP = style()->autoWrap() && style()->nbspMode() == SPACE;
809     bool breakAll = (style()->wordBreak() == BreakAllWordBreak || style()->wordBreak() == BreakWordBreak) && style()->autoWrap();
810 
811     for (int i = 0; i < len; i++) {
812         UChar c = txt[i];
813 
814         bool previousCharacterIsSpace = isSpace;
815 
816         bool isNewline = false;
817         if (c == '\n') {
818             if (style()->preserveNewline()) {
819                 m_hasBreak = true;
820                 isNewline = true;
821                 isSpace = false;
822             } else
823                 isSpace = true;
824         } else if (c == '\t') {
825             if (!style()->collapseWhiteSpace()) {
826                 m_hasTab = true;
827                 isSpace = false;
828             } else
829                 isSpace = true;
830         } else
831             isSpace = c == ' ';
832 
833         if ((isSpace || isNewline) && !i)
834             m_hasBeginWS = true;
835         if ((isSpace || isNewline) && i == len - 1)
836             m_hasEndWS = true;
837 
838         if (!ignoringSpaces && style()->collapseWhiteSpace() && previousCharacterIsSpace && isSpace)
839             ignoringSpaces = true;
840 
841         if (ignoringSpaces && !isSpace)
842             ignoringSpaces = false;
843 
844         // Ignore spaces and soft hyphens
845         if (ignoringSpaces) {
846             ASSERT(lastWordBoundary == i);
847             lastWordBoundary++;
848             continue;
849         } else if (c == softHyphen) {
850             currMaxWidth += widthFromCache(f, lastWordBoundary, i - lastWordBoundary, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow);
851             if (firstGlyphLeftOverflow < 0)
852                 firstGlyphLeftOverflow = glyphOverflow.left;
853             lastWordBoundary = i + 1;
854             continue;
855         }
856 
857         bool hasBreak = breakAll || isBreakable(breakIterator, i, nextBreakable, breakNBSP);
858         bool betweenWords = true;
859         int j = i;
860         while (c != '\n' && !isSpaceAccordingToStyle(c, style()) && c != '\t' && c != softHyphen) {
861             j++;
862             if (j == len)
863                 break;
864             c = txt[j];
865             if (isBreakable(breakIterator, j, nextBreakable, breakNBSP))
866                 break;
867             if (breakAll) {
868                 betweenWords = false;
869                 break;
870             }
871         }
872 
873         int wordLen = j - i;
874         if (wordLen) {
875             float w = widthFromCache(f, i, wordLen, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow);
876             if (firstGlyphLeftOverflow < 0)
877                 firstGlyphLeftOverflow = glyphOverflow.left;
878             currMinWidth += w;
879             if (betweenWords) {
880                 if (lastWordBoundary == i)
881                     currMaxWidth += w;
882                 else
883                     currMaxWidth += widthFromCache(f, lastWordBoundary, j - lastWordBoundary, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow);
884                 lastWordBoundary = j;
885             }
886 
887             bool isSpace = (j < len) && isSpaceAccordingToStyle(c, style());
888             bool isCollapsibleWhiteSpace = (j < len) && style()->isCollapsibleWhiteSpace(c);
889             if (j < len && style()->autoWrap())
890                 m_hasBreakableChar = true;
891 
892             // Add in wordSpacing to our currMaxWidth, but not if this is the last word on a line or the
893             // last word in the run.
894             if (wordSpacing && (isSpace || isCollapsibleWhiteSpace) && !containsOnlyWhitespace(j, len-j))
895                 currMaxWidth += wordSpacing;
896 
897             if (firstWord) {
898                 firstWord = false;
899                 // If the first character in the run is breakable, then we consider ourselves to have a beginning
900                 // minimum width of 0, since a break could occur right before our run starts, preventing us from ever
901                 // being appended to a previous text run when considering the total minimum width of the containing block.
902                 if (hasBreak)
903                     m_hasBreakableChar = true;
904                 m_beginMinWidth = hasBreak ? 0 : w;
905             }
906             m_endMinWidth = w;
907 
908             if (currMinWidth > m_minWidth)
909                 m_minWidth = currMinWidth;
910             currMinWidth = 0;
911 
912             i += wordLen - 1;
913         } else {
914             // Nowrap can never be broken, so don't bother setting the
915             // breakable character boolean. Pre can only be broken if we encounter a newline.
916             if (style()->autoWrap() || isNewline)
917                 m_hasBreakableChar = true;
918 
919             if (currMinWidth > m_minWidth)
920                 m_minWidth = currMinWidth;
921             currMinWidth = 0;
922 
923             if (isNewline) { // Only set if preserveNewline was true and we saw a newline.
924                 if (firstLine) {
925                     firstLine = false;
926                     leadWidth = 0;
927                     if (!style()->autoWrap())
928                         m_beginMinWidth = currMaxWidth;
929                 }
930 
931                 if (currMaxWidth > m_maxWidth)
932                     m_maxWidth = currMaxWidth;
933                 currMaxWidth = 0;
934             } else {
935                 currMaxWidth += f.width(TextRun(txt + i, 1, allowTabs(), leadWidth + currMaxWidth));
936                 glyphOverflow.right = 0;
937                 needsWordSpacing = isSpace && !previousCharacterIsSpace && i == len - 1;
938             }
939             ASSERT(lastWordBoundary == i);
940             lastWordBoundary++;
941         }
942     }
943 
944     if (firstGlyphLeftOverflow > 0)
945         glyphOverflow.left = firstGlyphLeftOverflow;
946 
947     if ((needsWordSpacing && len > 1) || (ignoringSpaces && !firstWord))
948         currMaxWidth += wordSpacing;
949 
950     m_minWidth = max(currMinWidth, m_minWidth);
951     m_maxWidth = max(currMaxWidth, m_maxWidth);
952 
953     if (!style()->autoWrap())
954         m_minWidth = m_maxWidth;
955 
956     if (style()->whiteSpace() == PRE) {
957         if (firstLine)
958             m_beginMinWidth = m_maxWidth;
959         m_endMinWidth = currMaxWidth;
960     }
961 
962     setPreferredLogicalWidthsDirty(false);
963 }
964 
isAllCollapsibleWhitespace()965 bool RenderText::isAllCollapsibleWhitespace()
966 {
967     int length = textLength();
968     const UChar* text = characters();
969     for (int i = 0; i < length; i++) {
970         if (!style()->isCollapsibleWhiteSpace(text[i]))
971             return false;
972     }
973     return true;
974 }
975 
containsOnlyWhitespace(unsigned from,unsigned len) const976 bool RenderText::containsOnlyWhitespace(unsigned from, unsigned len) const
977 {
978     ASSERT(m_text);
979     StringImpl& text = *m_text.impl();
980     unsigned currPos;
981     for (currPos = from;
982          currPos < from + len && (text[currPos] == '\n' || text[currPos] == ' ' || text[currPos] == '\t');
983          currPos++) { }
984     return currPos >= (from + len);
985 }
986 
firstRunOrigin() const987 FloatPoint RenderText::firstRunOrigin() const
988 {
989     return IntPoint(firstRunX(), firstRunY());
990 }
991 
firstRunX() const992 float RenderText::firstRunX() const
993 {
994     return m_firstTextBox ? m_firstTextBox->m_x : 0;
995 }
996 
firstRunY() const997 float RenderText::firstRunY() const
998 {
999     return m_firstTextBox ? m_firstTextBox->m_y : 0;
1000 }
1001 
setSelectionState(SelectionState state)1002 void RenderText::setSelectionState(SelectionState state)
1003 {
1004     InlineTextBox* box;
1005 
1006     RenderObject::setSelectionState(state);
1007     if (state == SelectionStart || state == SelectionEnd || state == SelectionBoth) {
1008         int startPos, endPos;
1009         selectionStartEnd(startPos, endPos);
1010         if (selectionState() == SelectionStart) {
1011             endPos = textLength();
1012 
1013             // to handle selection from end of text to end of line
1014             if (startPos != 0 && startPos == endPos)
1015                 startPos = endPos - 1;
1016         } else if (selectionState() == SelectionEnd)
1017             startPos = 0;
1018 
1019         for (box = firstTextBox(); box; box = box->nextTextBox()) {
1020             if (box->isSelected(startPos, endPos)) {
1021                 RootInlineBox* line = box->root();
1022                 if (line)
1023                     line->setHasSelectedChildren(true);
1024             }
1025         }
1026     } else {
1027         for (box = firstTextBox(); box; box = box->nextTextBox()) {
1028             RootInlineBox* line = box->root();
1029             if (line)
1030                 line->setHasSelectedChildren(state == SelectionInside);
1031         }
1032     }
1033 
1034     // The returned value can be null in case of an orphaned tree.
1035     if (RenderBlock* cb = containingBlock())
1036         cb->setSelectionState(state);
1037 }
1038 
setTextWithOffset(PassRefPtr<StringImpl> text,unsigned offset,unsigned len,bool force)1039 void RenderText::setTextWithOffset(PassRefPtr<StringImpl> text, unsigned offset, unsigned len, bool force)
1040 {
1041     unsigned oldLen = textLength();
1042     unsigned newLen = text->length();
1043     int delta = newLen - oldLen;
1044     unsigned end = len ? offset + len - 1 : offset;
1045 
1046     RootInlineBox* firstRootBox = 0;
1047     RootInlineBox* lastRootBox = 0;
1048 
1049     bool dirtiedLines = false;
1050 
1051     // Dirty all text boxes that include characters in between offset and offset+len.
1052     for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) {
1053         // Text run is entirely before the affected range.
1054         if (curr->end() < offset)
1055             continue;
1056 
1057         // Text run is entirely after the affected range.
1058         if (curr->start() > end) {
1059             curr->offsetRun(delta);
1060             RootInlineBox* root = curr->root();
1061             if (!firstRootBox) {
1062                 firstRootBox = root;
1063                 if (!dirtiedLines) {
1064                     // The affected area was in between two runs. Go ahead and mark the root box of
1065                     // the run after the affected area as dirty.
1066                     firstRootBox->markDirty();
1067                     dirtiedLines = true;
1068                 }
1069             }
1070             lastRootBox = root;
1071         } else if (curr->end() >= offset && curr->end() <= end) {
1072             // Text run overlaps with the left end of the affected range.
1073             curr->dirtyLineBoxes();
1074             dirtiedLines = true;
1075         } else if (curr->start() <= offset && curr->end() >= end) {
1076             // Text run subsumes the affected range.
1077             curr->dirtyLineBoxes();
1078             dirtiedLines = true;
1079         } else if (curr->start() <= end && curr->end() >= end) {
1080             // Text run overlaps with right end of the affected range.
1081             curr->dirtyLineBoxes();
1082             dirtiedLines = true;
1083         }
1084     }
1085 
1086     // Now we have to walk all of the clean lines and adjust their cached line break information
1087     // to reflect our updated offsets.
1088     if (lastRootBox)
1089         lastRootBox = lastRootBox->nextRootBox();
1090     if (firstRootBox) {
1091         RootInlineBox* prev = firstRootBox->prevRootBox();
1092         if (prev)
1093             firstRootBox = prev;
1094     } else if (lastTextBox()) {
1095         ASSERT(!lastRootBox);
1096         firstRootBox = lastTextBox()->root();
1097         firstRootBox->markDirty();
1098         dirtiedLines = true;
1099     }
1100     for (RootInlineBox* curr = firstRootBox; curr && curr != lastRootBox; curr = curr->nextRootBox()) {
1101         if (curr->lineBreakObj() == this && curr->lineBreakPos() > end)
1102             curr->setLineBreakPos(curr->lineBreakPos() + delta);
1103     }
1104 
1105     // If the text node is empty, dirty the line where new text will be inserted.
1106     if (!firstTextBox() && parent()) {
1107         parent()->dirtyLinesFromChangedChild(this);
1108         dirtiedLines = true;
1109     }
1110 
1111     m_linesDirty = dirtiedLines;
1112     setText(text, force);
1113 }
1114 
isInlineFlowOrEmptyText(const RenderObject * o)1115 static inline bool isInlineFlowOrEmptyText(const RenderObject* o)
1116 {
1117     if (o->isRenderInline())
1118         return true;
1119     if (!o->isText())
1120         return false;
1121     StringImpl* text = toRenderText(o)->text();
1122     if (!text)
1123         return true;
1124     return !text->length();
1125 }
1126 
previousCharacter() const1127 UChar RenderText::previousCharacter() const
1128 {
1129     // find previous text renderer if one exists
1130     const RenderObject* previousText = this;
1131     while ((previousText = previousText->previousInPreOrder()))
1132         if (!isInlineFlowOrEmptyText(previousText))
1133             break;
1134     UChar prev = ' ';
1135     if (previousText && previousText->isText())
1136         if (StringImpl* previousString = toRenderText(previousText)->text())
1137             prev = (*previousString)[previousString->length() - 1];
1138     return prev;
1139 }
1140 
transformText(String & text) const1141 void RenderText::transformText(String& text) const
1142 {
1143     ASSERT(style());
1144     switch (style()->textTransform()) {
1145     case TTNONE:
1146         break;
1147     case CAPITALIZE:
1148         makeCapitalized(&text, previousCharacter());
1149         break;
1150     case UPPERCASE:
1151         text.makeUpper();
1152         break;
1153     case LOWERCASE:
1154         text.makeLower();
1155         break;
1156     }
1157 }
1158 
setTextInternal(PassRefPtr<StringImpl> text)1159 void RenderText::setTextInternal(PassRefPtr<StringImpl> text)
1160 {
1161     ASSERT(text);
1162     m_text = text;
1163     if (m_needsTranscoding) {
1164         const TextEncoding* encoding = document()->decoder() ? &document()->decoder()->encoding() : 0;
1165         fontTranscoder().convert(m_text, style()->font().fontDescription(), encoding);
1166     }
1167     ASSERT(m_text);
1168 
1169     if (style()) {
1170         transformText(m_text);
1171 
1172         // We use the same characters here as for list markers.
1173         // See the listMarkerText function in RenderListMarker.cpp.
1174         switch (style()->textSecurity()) {
1175         case TSNONE:
1176             break;
1177         case TSCIRCLE:
1178             secureText(whiteBullet);
1179             break;
1180         case TSDISC:
1181             secureText(bullet);
1182             break;
1183         case TSSQUARE:
1184             secureText(blackSquare);
1185         }
1186     }
1187 
1188     ASSERT(m_text);
1189     ASSERT(!isBR() || (textLength() == 1 && m_text[0] == '\n'));
1190 
1191     m_isAllASCII = m_text.containsOnlyASCII();
1192 }
1193 
secureText(UChar mask)1194 void RenderText::secureText(UChar mask)
1195 {
1196     if (!m_text.length())
1197         return;
1198 
1199     int lastTypedCharacterOffsetToReveal = -1;
1200     String revealedText;
1201     SecureTextTimer* secureTextTimer = gSecureTextTimers ? gSecureTextTimers->get(this) : 0;
1202     if (secureTextTimer && secureTextTimer->isActive()) {
1203         lastTypedCharacterOffsetToReveal = secureTextTimer->lastTypedCharacterOffset();
1204         if (lastTypedCharacterOffsetToReveal >= 0)
1205             revealedText.append(m_text[lastTypedCharacterOffsetToReveal]);
1206     }
1207 
1208     m_text.makeSecure(mask);
1209     if (lastTypedCharacterOffsetToReveal >= 0) {
1210         m_text.replace(lastTypedCharacterOffsetToReveal, 1, revealedText);
1211         // m_text may be updated later before timer fires. We invalidate the lastTypedCharacterOffset to avoid inconsistency.
1212         secureTextTimer->invalidate();
1213     }
1214 }
1215 
setText(PassRefPtr<StringImpl> text,bool force)1216 void RenderText::setText(PassRefPtr<StringImpl> text, bool force)
1217 {
1218     ASSERT(text);
1219 
1220     if (!force && equal(m_text.impl(), text.get()))
1221         return;
1222 
1223     setTextInternal(text);
1224     setNeedsLayoutAndPrefWidthsRecalc();
1225     m_knownToHaveNoOverflowAndNoFallbackFonts = false;
1226 
1227     AXObjectCache* axObjectCache = document()->axObjectCache();
1228     if (axObjectCache->accessibilityEnabled())
1229         axObjectCache->contentChanged(this);
1230 }
1231 
textWithoutTranscoding() const1232 String RenderText::textWithoutTranscoding() const
1233 {
1234     // If m_text isn't transcoded or is secure, we can just return the modified text.
1235     if (!m_needsTranscoding || style()->textSecurity() != TSNONE)
1236         return text();
1237 
1238     // Otherwise, we should use original text. If text-transform is
1239     // specified, we should transform the text on the fly.
1240     String text = originalText();
1241     if (style())
1242         transformText(text);
1243     return text;
1244 }
1245 
dirtyLineBoxes(bool fullLayout)1246 void RenderText::dirtyLineBoxes(bool fullLayout)
1247 {
1248     if (fullLayout)
1249         deleteTextBoxes();
1250     else if (!m_linesDirty) {
1251         for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1252             box->dirtyLineBoxes();
1253     }
1254     m_linesDirty = false;
1255 }
1256 
createTextBox()1257 InlineTextBox* RenderText::createTextBox()
1258 {
1259     return new (renderArena()) InlineTextBox(this);
1260 }
1261 
createInlineTextBox()1262 InlineTextBox* RenderText::createInlineTextBox()
1263 {
1264     InlineTextBox* textBox = createTextBox();
1265     if (!m_firstTextBox)
1266         m_firstTextBox = m_lastTextBox = textBox;
1267     else {
1268         m_lastTextBox->setNextTextBox(textBox);
1269         textBox->setPreviousTextBox(m_lastTextBox);
1270         m_lastTextBox = textBox;
1271     }
1272     textBox->setIsText(true);
1273     return textBox;
1274 }
1275 
positionLineBox(InlineBox * box)1276 void RenderText::positionLineBox(InlineBox* box)
1277 {
1278     InlineTextBox* s = static_cast<InlineTextBox*>(box);
1279 
1280     // FIXME: should not be needed!!!
1281     if (!s->len()) {
1282         // We want the box to be destroyed.
1283         s->remove();
1284         if (m_firstTextBox == s)
1285             m_firstTextBox = s->nextTextBox();
1286         else
1287             s->prevTextBox()->setNextTextBox(s->nextTextBox());
1288         if (m_lastTextBox == s)
1289             m_lastTextBox = s->prevTextBox();
1290         else
1291             s->nextTextBox()->setPreviousTextBox(s->prevTextBox());
1292         s->destroy(renderArena());
1293         return;
1294     }
1295 
1296     m_containsReversedText |= !s->isLeftToRightDirection();
1297 }
1298 
width(unsigned from,unsigned len,float xPos,bool firstLine,HashSet<const SimpleFontData * > * fallbackFonts,GlyphOverflow * glyphOverflow) const1299 float RenderText::width(unsigned from, unsigned len, float xPos, bool firstLine, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
1300 {
1301     if (from >= textLength())
1302         return 0;
1303 
1304     if (from + len > textLength())
1305         len = textLength() - from;
1306 
1307     return width(from, len, style(firstLine)->font(), xPos, fallbackFonts, glyphOverflow);
1308 }
1309 
width(unsigned from,unsigned len,const Font & f,float xPos,HashSet<const SimpleFontData * > * fallbackFonts,GlyphOverflow * glyphOverflow) const1310 float RenderText::width(unsigned from, unsigned len, const Font& f, float xPos, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
1311 {
1312     ASSERT(from + len <= textLength());
1313     if (!characters())
1314         return 0;
1315 
1316     float w;
1317     if (&f == &style()->font()) {
1318         if (!style()->preserveNewline() && !from && len == textLength() && (!glyphOverflow || !glyphOverflow->computeBounds)) {
1319             if (fallbackFonts) {
1320                 ASSERT(glyphOverflow);
1321                 if (preferredLogicalWidthsDirty() || !m_knownToHaveNoOverflowAndNoFallbackFonts) {
1322                     const_cast<RenderText*>(this)->computePreferredLogicalWidths(0, *fallbackFonts, *glyphOverflow);
1323                     if (fallbackFonts->isEmpty() && !glyphOverflow->left && !glyphOverflow->right && !glyphOverflow->top && !glyphOverflow->bottom)
1324                         m_knownToHaveNoOverflowAndNoFallbackFonts = true;
1325                 }
1326                 w = m_maxWidth;
1327             } else
1328                 w = maxLogicalWidth();
1329         } else
1330             w = widthFromCache(f, from, len, xPos, fallbackFonts, glyphOverflow);
1331     } else
1332         w = f.width(TextRun(text()->characters() + from, len, allowTabs(), xPos), fallbackFonts, glyphOverflow);
1333 
1334     return w;
1335 }
1336 
linesBoundingBox() const1337 IntRect RenderText::linesBoundingBox() const
1338 {
1339     IntRect result;
1340 
1341     ASSERT(!firstTextBox() == !lastTextBox());  // Either both are null or both exist.
1342     if (firstTextBox() && lastTextBox()) {
1343         // Return the width of the minimal left side and the maximal right side.
1344         float logicalLeftSide = 0;
1345         float logicalRightSide = 0;
1346         for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) {
1347             if (curr == firstTextBox() || curr->logicalLeft() < logicalLeftSide)
1348                 logicalLeftSide = curr->logicalLeft();
1349             if (curr == firstTextBox() || curr->logicalRight() > logicalRightSide)
1350                 logicalRightSide = curr->logicalRight();
1351         }
1352 
1353         bool isHorizontal = style()->isHorizontalWritingMode();
1354 
1355         float x = isHorizontal ? logicalLeftSide : firstTextBox()->x();
1356         float y = isHorizontal ? firstTextBox()->y() : logicalLeftSide;
1357         float width = isHorizontal ? logicalRightSide - logicalLeftSide : lastTextBox()->logicalBottom() - x;
1358         float height = isHorizontal ? lastTextBox()->logicalBottom() - y : logicalRightSide - logicalLeftSide;
1359         result = enclosingIntRect(FloatRect(x, y, width, height));
1360     }
1361 
1362     return result;
1363 }
1364 
linesVisualOverflowBoundingBox() const1365 IntRect RenderText::linesVisualOverflowBoundingBox() const
1366 {
1367     if (!firstTextBox())
1368         return IntRect();
1369 
1370     // Return the width of the minimal left side and the maximal right side.
1371     int logicalLeftSide = numeric_limits<int>::max();
1372     int logicalRightSide = numeric_limits<int>::min();
1373     for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) {
1374         logicalLeftSide = min(logicalLeftSide, curr->logicalLeftVisualOverflow());
1375         logicalRightSide = max(logicalRightSide, curr->logicalRightVisualOverflow());
1376     }
1377 
1378     int logicalTop = firstTextBox()->logicalTopVisualOverflow();
1379     int logicalWidth = logicalRightSide - logicalLeftSide;
1380     int logicalHeight = lastTextBox()->logicalBottomVisualOverflow() - logicalTop;
1381 
1382     IntRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
1383     if (!style()->isHorizontalWritingMode())
1384         rect = rect.transposedRect();
1385     return rect;
1386 }
1387 
clippedOverflowRectForRepaint(RenderBoxModelObject * repaintContainer)1388 IntRect RenderText::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
1389 {
1390     bool repaintContainerSkipped;
1391     RenderObject* container = this->container(repaintContainer, &repaintContainerSkipped);
1392     // The container may be an ancestor of repaintContainer, but we need to do a repaintContainer-relative repaint.
1393     if (repaintContainerSkipped)
1394         return repaintContainer->clippedOverflowRectForRepaint(repaintContainer);
1395 
1396     return container->clippedOverflowRectForRepaint(repaintContainer);
1397 }
1398 
selectionRectForRepaint(RenderBoxModelObject * repaintContainer,bool clipToVisibleContent)1399 IntRect RenderText::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent)
1400 {
1401     ASSERT(!needsLayout());
1402 
1403     if (selectionState() == SelectionNone)
1404         return IntRect();
1405     RenderBlock* cb = containingBlock();
1406     if (!cb)
1407         return IntRect();
1408 
1409     // Now calculate startPos and endPos for painting selection.
1410     // We include a selection while endPos > 0
1411     int startPos, endPos;
1412     if (selectionState() == SelectionInside) {
1413         // We are fully selected.
1414         startPos = 0;
1415         endPos = textLength();
1416     } else {
1417         selectionStartEnd(startPos, endPos);
1418         if (selectionState() == SelectionStart)
1419             endPos = textLength();
1420         else if (selectionState() == SelectionEnd)
1421             startPos = 0;
1422     }
1423 
1424     if (startPos == endPos)
1425         return IntRect();
1426 
1427     IntRect rect;
1428     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
1429         rect.unite(box->selectionRect(0, 0, startPos, endPos));
1430         rect.unite(ellipsisRectForBox(box, startPos, endPos));
1431     }
1432 
1433     if (clipToVisibleContent)
1434         computeRectForRepaint(repaintContainer, rect);
1435     else {
1436         if (cb->hasColumns())
1437             cb->adjustRectForColumns(rect);
1438 
1439         rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
1440     }
1441 
1442     return rect;
1443 }
1444 
caretMinOffset() const1445 int RenderText::caretMinOffset() const
1446 {
1447     InlineTextBox* box = firstTextBox();
1448     if (!box)
1449         return 0;
1450     int minOffset = box->start();
1451     for (box = box->nextTextBox(); box; box = box->nextTextBox())
1452         minOffset = min<int>(minOffset, box->start());
1453     return minOffset;
1454 }
1455 
caretMaxOffset() const1456 int RenderText::caretMaxOffset() const
1457 {
1458     InlineTextBox* box = lastTextBox();
1459     if (!box)
1460         return textLength();
1461     int maxOffset = box->start() + box->len();
1462     for (box = box->prevTextBox(); box; box = box->prevTextBox())
1463         maxOffset = max<int>(maxOffset, box->start() + box->len());
1464     return maxOffset;
1465 }
1466 
caretMaxRenderedOffset() const1467 unsigned RenderText::caretMaxRenderedOffset() const
1468 {
1469     int l = 0;
1470     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1471         l += box->len();
1472     return l;
1473 }
1474 
previousOffset(int current) const1475 int RenderText::previousOffset(int current) const
1476 {
1477     StringImpl* si = m_text.impl();
1478     TextBreakIterator* iterator = cursorMovementIterator(si->characters(), si->length());
1479     if (!iterator)
1480         return current - 1;
1481 
1482     long result = textBreakPreceding(iterator, current);
1483     if (result == TextBreakDone)
1484         result = current - 1;
1485 
1486 #ifdef BUILDING_ON_TIGER
1487     // ICU 3.2 allows character breaks before a half-width Katakana voiced mark.
1488     if (static_cast<unsigned>(result) < si->length()) {
1489         UChar character = (*si)[result];
1490         if (character == 0xFF9E || character == 0xFF9F)
1491             --result;
1492     }
1493 #endif
1494 
1495     return result;
1496 }
1497 
1498 #if PLATFORM(MAC)
1499 
1500 #define HANGUL_CHOSEONG_START (0x1100)
1501 #define HANGUL_CHOSEONG_END (0x115F)
1502 #define HANGUL_JUNGSEONG_START (0x1160)
1503 #define HANGUL_JUNGSEONG_END (0x11A2)
1504 #define HANGUL_JONGSEONG_START (0x11A8)
1505 #define HANGUL_JONGSEONG_END (0x11F9)
1506 #define HANGUL_SYLLABLE_START (0xAC00)
1507 #define HANGUL_SYLLABLE_END (0xD7AF)
1508 #define HANGUL_JONGSEONG_COUNT (28)
1509 
1510 enum HangulState {
1511     HangulStateL,
1512     HangulStateV,
1513     HangulStateT,
1514     HangulStateLV,
1515     HangulStateLVT,
1516     HangulStateBreak
1517 };
1518 
isHangulLVT(UChar32 character)1519 inline bool isHangulLVT(UChar32 character)
1520 {
1521     return (character - HANGUL_SYLLABLE_START) % HANGUL_JONGSEONG_COUNT;
1522 }
1523 
isMark(UChar32 c)1524 inline bool isMark(UChar32 c)
1525 {
1526     int8_t charType = u_charType(c);
1527     return charType == U_NON_SPACING_MARK || charType == U_ENCLOSING_MARK || charType == U_COMBINING_SPACING_MARK;
1528 }
1529 
1530 #endif
1531 
previousOffsetForBackwardDeletion(int current) const1532 int RenderText::previousOffsetForBackwardDeletion(int current) const
1533 {
1534 #if PLATFORM(MAC)
1535     ASSERT(m_text);
1536     StringImpl& text = *m_text.impl();
1537     UChar32 character;
1538     while (current > 0) {
1539         if (U16_IS_TRAIL(text[--current]))
1540             --current;
1541         if (current < 0)
1542             break;
1543 
1544         UChar32 character = text.characterStartingAt(current);
1545 
1546         // We don't combine characters in Armenian ... Limbu range for backward deletion.
1547         if ((character >= 0x0530) && (character < 0x1950))
1548             break;
1549 
1550         if (!isMark(character) && (character != 0xFF9E) && (character != 0xFF9F))
1551             break;
1552     }
1553 
1554     if (current <= 0)
1555         return current;
1556 
1557     // Hangul
1558     character = text.characterStartingAt(current);
1559     if (((character >= HANGUL_CHOSEONG_START) && (character <= HANGUL_JONGSEONG_END)) || ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))) {
1560         HangulState state;
1561         HangulState initialState;
1562 
1563         if (character < HANGUL_JUNGSEONG_START)
1564             state = HangulStateL;
1565         else if (character < HANGUL_JONGSEONG_START)
1566             state = HangulStateV;
1567         else if (character < HANGUL_SYLLABLE_START)
1568             state = HangulStateT;
1569         else
1570             state = isHangulLVT(character) ? HangulStateLVT : HangulStateLV;
1571 
1572         initialState = state;
1573 
1574         while (current > 0 && ((character = text.characterStartingAt(current - 1)) >= HANGUL_CHOSEONG_START) && (character <= HANGUL_SYLLABLE_END) && ((character <= HANGUL_JONGSEONG_END) || (character >= HANGUL_SYLLABLE_START))) {
1575             switch (state) {
1576             case HangulStateV:
1577                 if (character <= HANGUL_CHOSEONG_END)
1578                     state = HangulStateL;
1579                 else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END) && !isHangulLVT(character))
1580                     state = HangulStateLV;
1581                 else if (character > HANGUL_JUNGSEONG_END)
1582                     state = HangulStateBreak;
1583                 break;
1584             case HangulStateT:
1585                 if ((character >= HANGUL_JUNGSEONG_START) && (character <= HANGUL_JUNGSEONG_END))
1586                     state = HangulStateV;
1587                 else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))
1588                     state = (isHangulLVT(character) ? HangulStateLVT : HangulStateLV);
1589                 else if (character < HANGUL_JUNGSEONG_START)
1590                     state = HangulStateBreak;
1591                 break;
1592             default:
1593                 state = (character < HANGUL_JUNGSEONG_START) ? HangulStateL : HangulStateBreak;
1594                 break;
1595             }
1596             if (state == HangulStateBreak)
1597                 break;
1598 
1599             --current;
1600         }
1601     }
1602 
1603     return current;
1604 #else
1605     // Platforms other than Mac delete by one code point.
1606     return current - 1;
1607 #endif
1608 }
1609 
nextOffset(int current) const1610 int RenderText::nextOffset(int current) const
1611 {
1612     StringImpl* si = m_text.impl();
1613     TextBreakIterator* iterator = cursorMovementIterator(si->characters(), si->length());
1614     if (!iterator)
1615         return current + 1;
1616 
1617     long result = textBreakFollowing(iterator, current);
1618     if (result == TextBreakDone)
1619         result = current + 1;
1620 
1621 #ifdef BUILDING_ON_TIGER
1622     // ICU 3.2 allows character breaks before a half-width Katakana voiced mark.
1623     if (static_cast<unsigned>(result) < si->length()) {
1624         UChar character = (*si)[result];
1625         if (character == 0xFF9E || character == 0xFF9F)
1626             ++result;
1627     }
1628 #endif
1629 
1630     return result;
1631 }
1632 
1633 #ifndef NDEBUG
1634 
checkConsistency() const1635 void RenderText::checkConsistency() const
1636 {
1637 #ifdef CHECK_CONSISTENCY
1638     const InlineTextBox* prev = 0;
1639     for (const InlineTextBox* child = m_firstTextBox; child != 0; child = child->nextTextBox()) {
1640         ASSERT(child->renderer() == this);
1641         ASSERT(child->prevTextBox() == prev);
1642         prev = child;
1643     }
1644     ASSERT(prev == m_lastTextBox);
1645 #endif
1646 }
1647 
1648 #endif
1649 
momentarilyRevealLastTypedCharacter(unsigned lastTypedCharacterOffset)1650 void RenderText::momentarilyRevealLastTypedCharacter(unsigned lastTypedCharacterOffset)
1651 {
1652     if (!gSecureTextTimers)
1653         gSecureTextTimers = new SecureTextTimerMap;
1654 
1655     SecureTextTimer* secureTextTimer = gSecureTextTimers->get(this);
1656     if (!secureTextTimer) {
1657         secureTextTimer = new SecureTextTimer(this);
1658         gSecureTextTimers->add(this, secureTextTimer);
1659     }
1660     secureTextTimer->restartWithNewText(lastTypedCharacterOffset);
1661 }
1662 
1663 } // namespace WebCore
1664