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