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