Reviewed by Tim Hatcher.
[WebKit-https.git] / WebCore / rendering / RenderText.cpp
1 /**
2  * (C) 1999 Lars Knoll (knoll@kde.org)
3  * (C) 2000 Dirk Mueller (mueller@kde.org)
4  * Copyright (C) 2004, 2005, 2006, 2007 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., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  *
23  */
24
25 #include "config.h"
26 #include "RenderText.h"
27
28 #include "CharacterNames.h"
29 #include "InlineTextBox.h"
30 #include "Range.h"
31 #include "RenderArena.h"
32 #include "RenderBlock.h"
33 #include "RenderLayer.h"
34 #include "Text.h"
35 #include "TextBreakIterator.h"
36 #include "TextStyle.h"
37 #include "break_lines.h"
38 #include <wtf/AlwaysInline.h>
39
40 using namespace std;
41 using namespace WTF;
42 using namespace Unicode;
43
44 namespace WebCore {
45
46 static inline bool charactersAreAllASCII(const StringImpl* text)
47 {
48     const UChar* chars = text->characters();
49     unsigned length = text->length();
50     UChar ored = 0;
51     for (unsigned i = 0; i < length; ++i)
52         ored |= chars[i];
53     return !(ored & 0xFF80);
54 }
55
56 RenderText::RenderText(Node* node, PassRefPtr<StringImpl> str)
57      : RenderObject(node)
58      , m_text(str)
59      , m_firstTextBox(0)
60      , m_lastTextBox(0)
61      , m_minWidth(-1)
62      , m_maxWidth(-1)
63      , m_selectionState(SelectionNone)
64      , m_hasTab(false)
65      , m_linesDirty(false)
66      , m_containsReversedText(false)
67      , m_isAllASCII(charactersAreAllASCII(m_text.get()))
68 {
69     ASSERT(m_text);
70     setRenderText();
71     m_text = m_text->replace('\\', backslashAsCurrencySymbol());
72 }
73
74 #ifndef NDEBUG
75
76 RenderText::~RenderText()
77 {
78     ASSERT(!m_firstTextBox);
79     ASSERT(!m_lastTextBox);
80 }
81
82 #endif
83
84 void RenderText::setStyle(RenderStyle* newStyle)
85 {
86     RenderStyle* oldStyle = style();
87     if (oldStyle == newStyle)
88         return;
89
90     ETextTransform oldTransform = oldStyle ? oldStyle->textTransform() : TTNONE;
91     ETextSecurity oldSecurity = oldStyle ? oldStyle->textSecurity() : TSNONE;
92
93     RenderObject::setStyle(newStyle);
94
95     if (oldTransform != newStyle->textTransform() || oldSecurity != newStyle->textSecurity()
96 #if ENABLE(SVG)
97         || isSVGText() /* All SVG text has to be transformed */
98 #endif
99        ) {
100         if (RefPtr<StringImpl> textToTransform = originalText())
101             setText(textToTransform.release(), true);
102     }
103 }
104
105 void RenderText::destroy()
106 {
107     if (!documentBeingDestroyed()) {
108         if (firstTextBox()) {
109             if (isBR()) {
110                 RootInlineBox* next = firstTextBox()->root()->nextRootBox();
111                 if (next)
112                     next->markDirty();
113             }
114             for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
115                 box->remove();
116         } else if (parent())
117             parent()->dirtyLinesFromChangedChild(this);
118     }
119     deleteTextBoxes();
120     RenderObject::destroy();
121 }
122
123 void RenderText::extractTextBox(InlineTextBox* box)
124 {
125     checkConsistency();
126
127     m_lastTextBox = box->prevTextBox();
128     if (box == m_firstTextBox)
129         m_firstTextBox = 0;
130     if (box->prevTextBox())
131         box->prevTextBox()->setNextLineBox(0);
132     box->setPreviousLineBox(0);
133     for (InlineRunBox* curr = box; curr; curr = curr->nextLineBox())
134         curr->setExtracted();
135
136     checkConsistency();
137 }
138
139 void RenderText::attachTextBox(InlineTextBox* box)
140 {
141     checkConsistency();
142
143     if (m_lastTextBox) {
144         m_lastTextBox->setNextLineBox(box);
145         box->setPreviousLineBox(m_lastTextBox);
146     } else
147         m_firstTextBox = box;
148     InlineTextBox* last = box;
149     for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox()) {
150         curr->setExtracted(false);
151         last = curr;
152     }
153     m_lastTextBox = last;
154
155     checkConsistency();
156 }
157
158 void RenderText::removeTextBox(InlineTextBox* box)
159 {
160     checkConsistency();
161
162     if (box == m_firstTextBox)
163         m_firstTextBox = box->nextTextBox();
164     if (box == m_lastTextBox)
165         m_lastTextBox = box->prevTextBox();
166     if (box->nextTextBox())
167         box->nextTextBox()->setPreviousLineBox(box->prevTextBox());
168     if (box->prevTextBox())
169         box->prevTextBox()->setNextLineBox(box->nextTextBox());
170
171     checkConsistency();
172 }
173
174 void RenderText::deleteTextBoxes()
175 {
176     if (firstTextBox()) {
177         RenderArena* arena = renderArena();
178         InlineTextBox* next;
179         for (InlineTextBox* curr = firstTextBox(); curr; curr = next) {
180             next = curr->nextTextBox();
181             curr->destroy(arena);
182         }
183         m_firstTextBox = m_lastTextBox = 0;
184     }
185 }
186
187 PassRefPtr<StringImpl> RenderText::originalText() const
188 {
189     Node* e = element();
190     return e ? static_cast<Text*>(e)->string() : 0;
191 }
192
193 void RenderText::absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool)
194 {
195     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
196         rects.append(IntRect(tx + box->xPos(), ty + box->yPos(), box->width(), box->height()));
197 }
198
199 void RenderText::addLineBoxRects(Vector<IntRect>& rects, unsigned start, unsigned end, bool useSelectionHeight)
200 {
201     int x, y;
202     absolutePositionForContent(x, y);
203
204     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
205         if (start <= box->start() && box->end() <= end)
206             rects.append(IntRect(x + box->xPos(), y + box->yPos(), box->width(), box->height()));
207         else {
208             unsigned realEnd = min(box->end() + 1, end); // box->end() points at the last char, not after it
209             IntRect r = box->selectionRect(x, y, start, realEnd);
210             if (!r.isEmpty()) {
211                 if (!useSelectionHeight) {
212                     // change the height and y position because selectionRect uses selection-specific values
213                     r.setHeight(box->height());
214                     r.setY(y + box->yPos());
215                 }
216                 rects.append(r);
217             }
218         }
219     }
220 }
221
222 InlineTextBox* RenderText::findNextInlineTextBox(int offset, int& pos) const
223 {
224     // The text runs point to parts of the RenderText's m_text
225     // (they don't include '\n')
226     // Find the text run that includes the character at offset
227     // and return pos, which is the position of the char in the run.
228
229     if (!m_firstTextBox)
230         return 0;
231
232     InlineTextBox* s = m_firstTextBox;
233     int off = s->m_len;
234     while (offset > off && s->nextTextBox()) {
235         s = s->nextTextBox();
236         off = s->m_start + s->m_len;
237     }
238     // we are now in the correct text run
239     pos = (offset > off ? s->m_len : s->m_len - (off - offset) );
240     return s;
241 }
242
243 VisiblePosition RenderText::positionForCoordinates(int x, int y)
244 {
245     if (!firstTextBox() || textLength() == 0)
246         return VisiblePosition(element(), 0, DOWNSTREAM);
247
248     // Get the offset for the position, since this will take rtl text into account.
249     int offset;
250
251     // FIXME: We should be able to roll these special cases into the general cases in the loop below.
252     if (firstTextBox() && y <  firstTextBox()->root()->bottomOverflow() && x < firstTextBox()->m_x) {
253         // at the y coordinate of the first line or above
254         // and the x coordinate is to the left of the first text box left edge
255         offset = firstTextBox()->offsetForPosition(x);
256         return VisiblePosition(element(), offset + firstTextBox()->m_start, DOWNSTREAM);
257     }
258     if (lastTextBox() && y >= lastTextBox()->root()->topOverflow() && x >= lastTextBox()->m_x + lastTextBox()->m_width) {
259         // at the y coordinate of the last line or below
260         // and the x coordinate is to the right of the last text box right edge
261         offset = lastTextBox()->offsetForPosition(x);
262         return VisiblePosition(element(), offset + lastTextBox()->m_start, DOWNSTREAM);
263     }
264
265     InlineTextBox* lastBoxAbove = 0;
266     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
267         if (y >= box->root()->topOverflow()) {
268             int bottom = box->root()->nextRootBox() ? box->root()->nextRootBox()->topOverflow() : box->root()->bottomOverflow();
269             if (y < bottom) {
270                 offset = box->offsetForPosition(x);
271
272                 if (x == box->m_x)
273                     // the x coordinate is equal to the left edge of this box
274                     // the affinity must be downstream so the position doesn't jump back to the previous line
275                     return VisiblePosition(element(), offset + box->m_start, DOWNSTREAM);
276
277                 if (x < box->m_x + box->m_width)
278                     // and the x coordinate is to the left of the right edge of this box
279                     // check to see if position goes in this box
280                     return VisiblePosition(element(), offset + box->m_start, offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
281
282                 if (!box->prevOnLine() && x < box->m_x)
283                     // box is first on line
284                     // and the x coordinate is to the left of the first text box left edge
285                     return VisiblePosition(element(), offset + box->m_start, DOWNSTREAM);
286
287                 if (!box->nextOnLine())
288                     // box is last on line
289                     // and the x coordinate is to the right of the last text box right edge
290                     // generate VisiblePosition, use UPSTREAM affinity if possible
291                     return VisiblePosition(element(), offset + box->m_start, VP_UPSTREAM_IF_POSSIBLE);
292             }
293             lastBoxAbove = box;
294         }
295     }
296
297     return VisiblePosition(element(), lastBoxAbove ? lastBoxAbove->m_start + lastBoxAbove->m_len : 0, DOWNSTREAM);
298 }
299
300 static RenderObject* lastRendererOnPrevLine(InlineBox* box)
301 {
302     if (!box)
303         return 0;
304
305     RootInlineBox* root = box->root();
306     if (!root)
307         return 0;
308
309     if (root->endsWithBreak())
310         return 0;
311
312     RootInlineBox* prevRoot = root->prevRootBox();
313     if (!prevRoot)
314         return 0;
315
316     InlineBox* lastChild = prevRoot->lastChild();
317     if (!lastChild)
318         return 0;
319
320     return lastChild->object();
321 }
322
323 static inline bool atLineWrap(InlineTextBox* box, int offset)
324 {
325     return box->nextTextBox() && !box->nextOnLine() && offset == box->m_start + box->m_len;
326 }
327
328 IntRect RenderText::caretRect(int offset, EAffinity affinity, int* extraWidthToEndOfLine)
329 {
330     if (!firstTextBox() || !textLength())
331         return IntRect();
332
333     // Find the text box for the given offset
334     InlineTextBox* box = 0;
335     for (box = firstTextBox(); box; box = box->nextTextBox()) {
336         if (box->containsCaretOffset(offset)) {
337             // Check if downstream affinity would make us move to the next line.
338             if (atLineWrap(box, offset) && affinity == DOWNSTREAM) {
339                 // Use the next text box
340                 box = box->nextTextBox();
341                 offset = box->m_start;
342             } else {
343                 InlineTextBox* prevBox = box->prevTextBox();
344                 if (offset == box->m_start && affinity == UPSTREAM && prevBox && !box->prevOnLine()) {
345                     if (prevBox) {
346                         box = prevBox;
347                         offset = box->m_start + box->m_len;
348                     } else {
349                         RenderObject *object = lastRendererOnPrevLine(box);
350                         if (object)
351                             return object->caretRect(0, affinity);
352                     }
353                 }
354             }
355             break;
356         }
357     }
358
359     if (!box)
360         return IntRect();
361
362     int height = box->root()->bottomOverflow() - box->root()->topOverflow();
363     int top = box->root()->topOverflow();
364
365     int left = box->positionForOffset(offset);
366
367     int rootLeft = box->root()->xPos();
368     // FIXME: should we use the width of the root inline box or the
369     // width of the containing block for this?
370     if (extraWidthToEndOfLine)
371         *extraWidthToEndOfLine = (box->root()->width() + rootLeft) - (left + 1);
372
373     int absx, absy;
374     absolutePositionForContent(absx, absy);
375     left += absx;
376     top += absy;
377
378     RenderBlock* cb = containingBlock();
379     if (style()->autoWrap()) {
380         int availableWidth = cb->lineWidth(top);
381         if (!box->m_reversed)
382             left = min(left, absx + rootLeft + availableWidth - 1);
383         else
384             left = max(left, absx + rootLeft);
385     }
386
387     return IntRect(left, top, 1, height);
388 }
389
390 ALWAYS_INLINE int RenderText::widthFromCache(const Font& f, int start, int len, int xPos) const
391 {
392     if (f.isFixedPitch() && !f.isSmallCaps() && m_isAllASCII) {
393         // FIXME: This code should be simplfied; it's only run when m_text is known to be all 0000-007F,
394         // but is uses the general purpose Unicode direction function.
395         int monospaceCharacterWidth = f.spaceWidth();
396         int tabWidth = allowTabs() ? monospaceCharacterWidth * 8 : 0;
397         int w = 0;
398         char previousChar = ' '; // FIXME: Preserves historical behavior, but seems wrong for start > 0.
399         for (int i = start; i < start + len; i++) {
400             char c = (*m_text)[i];
401             Direction dir = direction(c);
402             if (dir != NonSpacingMark && dir != BoundaryNeutral) {
403                 if (c == '\t' && tabWidth)
404                     w += tabWidth - ((xPos + w) % tabWidth);
405                 else
406                     w += monospaceCharacterWidth;
407                 if (isspace(c) && !isspace(previousChar))
408                     w += f.wordSpacing();
409             }
410             previousChar = c;
411         }
412         return w;
413     }
414
415     return f.width(TextRun(text()->characters() + start, len), TextStyle(allowTabs(), xPos));
416 }
417
418 void RenderText::trimmedPrefWidths(int leadWidth,
419                                    int& beginMinW, bool& beginWS,
420                                    int& endMinW, bool& endWS,
421                                    bool& hasBreakableChar, bool& hasBreak,
422                                    int& beginMaxW, int& endMaxW,
423                                    int& minW, int& maxW, bool& stripFrontSpaces)
424 {
425     bool collapseWhiteSpace = style()->collapseWhiteSpace();
426     if (!collapseWhiteSpace)
427         stripFrontSpaces = false;
428
429     if (m_hasTab || prefWidthsDirty())
430         calcPrefWidths(leadWidth);
431
432     int len = textLength();
433     if (!len || (stripFrontSpaces && m_text->containsOnlyWhitespace())) {
434         maxW = 0;
435         hasBreak = false;
436         return;
437     }
438
439     minW = m_minWidth;
440     maxW = m_maxWidth;
441     beginWS = !stripFrontSpaces && m_hasBeginWS;
442     endWS = m_hasEndWS;
443
444     beginMinW = m_beginMinWidth;
445     endMinW = m_endMinWidth;
446
447     hasBreakableChar = m_hasBreakableChar;
448     hasBreak = m_hasBreak;
449
450     if (stripFrontSpaces && ((*m_text)[0] == ' ' || ((*m_text)[0] == '\n' && !style()->preserveNewline()) || (*m_text)[0] == '\t')) {
451         const Font& f = style()->font(); // FIXME: This ignores first-line.
452         const UChar space = ' ';
453         int spaceWidth = f.width(TextRun(&space, 1));
454         maxW -= spaceWidth + f.wordSpacing();
455     }
456
457     stripFrontSpaces = collapseWhiteSpace && m_hasEndWS;
458
459     if (!style()->autoWrap() || minW > maxW)
460         minW = maxW;
461
462     // Compute our max widths by scanning the string for newlines.
463     if (hasBreak) {
464         const Font& f = style()->font(); // FIXME: This ignores first-line.
465         bool firstLine = true;
466         beginMaxW = maxW;
467         endMaxW = maxW;
468         for (int i = 0; i < len; i++) {
469             int linelen = 0;
470             while (i + linelen < len && (*m_text)[i + linelen] != '\n')
471                 linelen++;
472
473             if (linelen) {
474                 endMaxW = widthFromCache(f, i, linelen, leadWidth + endMaxW);
475                 if (firstLine) {
476                     firstLine = false;
477                     leadWidth = 0;
478                     beginMaxW = endMaxW;
479                 }
480                 i += linelen;
481             } else if (firstLine) {
482                 beginMaxW = 0;
483                 firstLine = false;
484                 leadWidth = 0;
485             }
486
487             if (i == len - 1)
488                 // A <pre> run that ends with a newline, as in, e.g.,
489                 // <pre>Some text\n\n<span>More text</pre>
490                 endMaxW = 0;
491         }
492     }
493 }
494
495 inline bool isSpaceAccordingToStyle(UChar c, RenderStyle* style)
496 {
497     return c == ' ' || (c == noBreakSpace && style->nbspMode() == SPACE);
498 }
499
500 int RenderText::minPrefWidth() const
501 {
502     if (prefWidthsDirty())
503         const_cast<RenderText*>(this)->calcPrefWidths(0);
504         
505     return m_minWidth;
506 }
507
508 int RenderText::maxPrefWidth() const
509 {
510     if (prefWidthsDirty())
511         const_cast<RenderText*>(this)->calcPrefWidths(0);
512         
513     return m_maxWidth;
514 }
515
516 void RenderText::calcPrefWidths(int leadWidth)
517 {
518     ASSERT(m_hasTab || prefWidthsDirty());
519
520     m_minWidth = 0;
521     m_beginMinWidth = 0;
522     m_endMinWidth = 0;
523     m_maxWidth = 0;
524
525     if (isBR())
526         return;
527
528     int currMinWidth = 0;
529     int currMaxWidth = 0;
530     m_hasBreakableChar = false;
531     m_hasBreak = false;
532     m_hasTab = false;
533     m_hasBeginWS = false;
534     m_hasEndWS = false;
535
536     const Font& f = style()->font(); // FIXME: This ignores first-line.
537     int wordSpacing = style()->wordSpacing();
538     int len = textLength();
539     const UChar* txt = characters();
540     bool needsWordSpacing = false;
541     bool ignoringSpaces = false;
542     bool isSpace = false;
543     bool firstWord = true;
544     bool firstLine = true;
545     int nextBreakable = -1;
546     int lastWordBoundary = 0;
547
548     bool breakNBSP = style()->autoWrap() && style()->nbspMode() == SPACE;
549     bool breakAll = (style()->wordBreak() == BreakAllWordBreak || style()->wordBreak() == BreakWordBreak) && style()->autoWrap();
550
551     for (int i = 0; i < len; i++) {
552         UChar c = txt[i];
553
554         bool previousCharacterIsSpace = isSpace;
555
556         bool isNewline = false;
557         if (c == '\n') {
558             if (style()->preserveNewline()) {
559                 m_hasBreak = true;
560                 isNewline = true;
561                 isSpace = false;
562             } else
563                 isSpace = true;
564         } else if (c == '\t') {
565             if (!style()->collapseWhiteSpace()) {
566                 m_hasTab = true;
567                 isSpace = false;
568             } else
569                 isSpace = true;
570         } else
571             isSpace = c == ' ';
572
573         if ((isSpace || isNewline) && !i)
574             m_hasBeginWS = true;
575         if ((isSpace || isNewline) && i == len - 1)
576             m_hasEndWS = true;
577
578         if (!ignoringSpaces && style()->collapseWhiteSpace() && previousCharacterIsSpace && isSpace)
579             ignoringSpaces = true;
580
581         if (ignoringSpaces && !isSpace)
582             ignoringSpaces = false;
583
584         // Ignore spaces and soft hyphens
585         if (ignoringSpaces || c == softHyphen) {
586             ASSERT(lastWordBoundary == i);
587             lastWordBoundary++;
588             continue;
589         }
590
591         bool hasBreak = breakAll || isBreakable(txt, i, len, nextBreakable, breakNBSP);
592         bool betweenWords = true;
593         int j = i;
594         while (c != '\n' && !isSpaceAccordingToStyle(c, style()) && c != '\t' && c != softHyphen) {
595             j++;
596             if (j == len)
597                 break;
598             c = txt[j];
599             if (isBreakable(txt, j, len, nextBreakable, breakNBSP))
600                 break;
601             if (breakAll) {
602                 betweenWords = false;
603                 break;
604             }
605         }
606
607         int wordLen = j - i;
608         if (wordLen) {
609             int w = widthFromCache(f, i, wordLen, leadWidth + currMaxWidth);
610             currMinWidth += w;
611             if (betweenWords) {
612                 if (lastWordBoundary == i)
613                     currMaxWidth += w;
614                 else
615                     currMaxWidth += widthFromCache(f, lastWordBoundary, j - lastWordBoundary, leadWidth + currMaxWidth);
616                 lastWordBoundary = j;
617             }
618
619             bool isSpace = (j < len) && isSpaceAccordingToStyle(c, style());
620             bool isCollapsibleWhiteSpace = (j < len) && style()->isCollapsibleWhiteSpace(c);
621             if (j < len && style()->autoWrap())
622                 m_hasBreakableChar = true;
623
624             // Add in wordSpacing to our currMaxWidth, but not if this is the last word on a line or the
625             // last word in the run.
626             if (wordSpacing && (isSpace || isCollapsibleWhiteSpace) && !containsOnlyWhitespace(j, len-j))
627                 currMaxWidth += wordSpacing;
628
629             if (firstWord) {
630                 firstWord = false;
631                 // If the first character in the run is breakable, then we consider ourselves to have a beginning
632                 // minimum width of 0, since a break could occur right before our run starts, preventing us from ever
633                 // being appended to a previous text run when considering the total minimum width of the containing block.
634                 if (hasBreak)
635                     m_hasBreakableChar = true;
636                 m_beginMinWidth = hasBreak ? 0 : w;
637             }
638             m_endMinWidth = w;
639
640             if (currMinWidth > m_minWidth)
641                 m_minWidth = currMinWidth;
642             currMinWidth = 0;
643
644             i += wordLen - 1;
645         } else {
646             // Nowrap can never be broken, so don't bother setting the
647             // breakable character boolean. Pre can only be broken if we encounter a newline.
648             if (style()->autoWrap() || isNewline)
649                 m_hasBreakableChar = true;
650
651             if (currMinWidth > m_minWidth)
652                 m_minWidth = currMinWidth;
653             currMinWidth = 0;
654
655             if (isNewline) { // Only set if preserveNewline was true and we saw a newline.
656                 if (firstLine) {
657                     firstLine = false;
658                     leadWidth = 0;
659                     if (!style()->autoWrap())
660                         m_beginMinWidth = currMaxWidth;
661                 }
662
663                 if (currMaxWidth > m_maxWidth)
664                     m_maxWidth = currMaxWidth;
665                 currMaxWidth = 0;
666             } else {
667                 currMaxWidth += f.width(TextRun(txt + i, 1), TextStyle(allowTabs(), leadWidth + currMaxWidth));
668                 needsWordSpacing = isSpace && !previousCharacterIsSpace && i == len - 1;
669             }
670             ASSERT(lastWordBoundary == i);
671             lastWordBoundary++;
672         }
673     }
674
675     if (needsWordSpacing && len > 1)
676         currMaxWidth += wordSpacing;
677
678     m_minWidth = max(currMinWidth, m_minWidth);
679     m_maxWidth = max(currMaxWidth, m_maxWidth);
680
681     if (!style()->autoWrap())
682         m_minWidth = m_maxWidth;
683
684     if (style()->whiteSpace() == PRE) {
685         if (firstLine)
686             m_beginMinWidth = m_maxWidth;
687         m_endMinWidth = currMaxWidth;
688     }
689
690     setPrefWidthsDirty(false);
691 }
692
693 bool RenderText::containsOnlyWhitespace(unsigned from, unsigned len) const
694 {
695     unsigned currPos;
696     for (currPos = from;
697          currPos < from + len && ((*m_text)[currPos] == '\n' || (*m_text)[currPos] == ' ' || (*m_text)[currPos] == '\t');
698          currPos++);
699     return currPos >= (from + len);
700 }
701
702 int RenderText::minXPos() const
703 {
704     if (!m_firstTextBox)
705         return 0;
706
707     // FIXME: we should not use an arbitrary value like this.  Perhaps we should use INT_MAX.
708     int minXPos = 6666666;
709     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
710         minXPos = min(minXPos, static_cast<int>(box->m_x));
711     return minXPos;
712 }
713
714 int RenderText::xPos() const
715 {
716     return m_firstTextBox ? m_firstTextBox->m_x : 0;
717 }
718
719 int RenderText::yPos() const
720 {
721     return m_firstTextBox ? m_firstTextBox->m_y : 0;
722 }
723
724 void RenderText::setSelectionState(SelectionState state)
725 {
726     InlineTextBox* box;
727
728     m_selectionState = state;
729     if (state == SelectionStart || state == SelectionEnd || state == SelectionBoth) {
730         int startPos, endPos;
731         selectionStartEnd(startPos, endPos);
732         if (selectionState() == SelectionStart) {
733             endPos = textLength();
734
735             // to handle selection from end of text to end of line
736             if (startPos != 0 && startPos == endPos)
737                 startPos = endPos - 1;
738         } else if (selectionState() == SelectionEnd)
739             startPos = 0;
740
741         for (box = firstTextBox(); box; box = box->nextTextBox()) {
742             if (box->isSelected(startPos, endPos)) {
743                 RootInlineBox* line = box->root();
744                 if (line)
745                     line->setHasSelectedChildren(true);
746             }
747         }
748     } else {
749         for (box = firstTextBox(); box; box = box->nextTextBox()) {
750             RootInlineBox* line = box->root();
751             if (line)
752                 line->setHasSelectedChildren(state == SelectionInside);
753         }
754     }
755
756     containingBlock()->setSelectionState(state);
757 }
758
759 void RenderText::setTextWithOffset(PassRefPtr<StringImpl> text, unsigned offset, unsigned len, bool force)
760 {
761     unsigned oldLen = textLength();
762     unsigned newLen = text->length();
763     int delta = newLen - oldLen;
764     unsigned end = len ? offset + len - 1 : offset;
765
766     RootInlineBox* firstRootBox = 0;
767     RootInlineBox* lastRootBox = 0;
768
769     bool dirtiedLines = false;
770
771     // Dirty all text boxes that include characters in between offset and offset+len.
772     for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) {
773         // Text run is entirely before the affected range.
774         if (curr->end() < offset)
775             continue;
776
777         // Text run is entirely after the affected range.
778         if (curr->start() > end) {
779             curr->offsetRun(delta);
780             RootInlineBox* root = curr->root();
781             if (!firstRootBox) {
782                 firstRootBox = root;
783                 if (!dirtiedLines) {
784                     // The affected area was in between two runs. Go ahead and mark the root box of
785                     // the run after the affected area as dirty.
786                     firstRootBox->markDirty();
787                     dirtiedLines = true;
788                 }
789             }
790             lastRootBox = root;
791         } else if (curr->end() >= offset && curr->end() <= end) {
792             // Text run overlaps with the left end of the affected range.
793             curr->dirtyLineBoxes();
794             dirtiedLines = true;
795         } else if (curr->start() <= offset && curr->end() >= end) {
796             // Text run subsumes the affected range.
797             curr->dirtyLineBoxes();
798             dirtiedLines = true;
799         } else if (curr->start() <= end && curr->end() >= end) {
800             // Text run overlaps with right end of the affected range.
801             curr->dirtyLineBoxes();
802             dirtiedLines = true;
803         }
804     }
805
806     // Now we have to walk all of the clean lines and adjust their cached line break information
807     // to reflect our updated offsets.
808     if (lastRootBox)
809         lastRootBox = lastRootBox->nextRootBox();
810     if (firstRootBox) {
811         RootInlineBox* prev = firstRootBox->prevRootBox();
812         if (prev)
813             firstRootBox = prev;
814     }
815     for (RootInlineBox* curr = firstRootBox; curr && curr != lastRootBox; curr = curr->nextRootBox()) {
816         if (curr->lineBreakObj() == this && curr->lineBreakPos() > end)
817             curr->setLineBreakPos(curr->lineBreakPos() + delta);
818     }
819
820     m_linesDirty = dirtiedLines;
821     setText(text, force);
822 }
823
824 static inline bool isInlineFlowOrEmptyText(RenderObject* o)
825 {
826     if (o->isInlineFlow())
827         return true;
828     if (!o->isText())
829         return false;
830     StringImpl* text = static_cast<RenderText*>(o)->text();
831     if (!text)
832         return true;
833     return !text->length();
834 }
835
836 UChar RenderText::previousCharacter()
837 {
838     // find previous text renderer if one exists
839     RenderObject* previousText = this;
840     while ((previousText = previousText->previousInPreOrder()))
841         if (!isInlineFlowOrEmptyText(previousText))
842             break;
843     UChar prev = ' ';
844     if (previousText && previousText->isText())
845         if (StringImpl* previousString = static_cast<RenderText*>(previousText)->text())
846             prev = (*previousString)[previousString->length() - 1];
847     return prev;
848 }
849
850 void RenderText::setTextInternal(PassRefPtr<StringImpl> text)
851 {
852     m_text = text;
853     ASSERT(m_text);
854
855     m_text = m_text->replace('\\', backslashAsCurrencySymbol());
856
857 #if ENABLE(SVG)
858     if (isSVGText()) {
859         if (style() && style()->whiteSpace() == PRE) {
860             // Spec: When xml:space="preserve", the SVG user agent will do the following using a
861             // copy of the original character data content. It will convert all newline and tab
862             // characters into space characters. Then, it will draw all space characters, including
863             // leading, trailing and multiple contiguous space characters.
864
865             m_text = m_text->replace('\n', ' ');
866
867             // If xml:space="preserve" is set, white-space is set to "pre", which
868             // preserves leading, trailing & contiguous space character for us.
869        } else {
870             // Spec: When xml:space="default", the SVG user agent will do the following using a
871             // copy of the original character data content. First, it will remove all newline
872             // characters. Then it will convert all tab characters into space characters.
873             // Then, it will strip off all leading and trailing space characters.
874             // Then, all contiguous space characters will be consolidated.    
875
876             static StringImpl empty("", 0);
877             m_text = m_text->replace('\n', &empty);
878
879             // If xml:space="default" is set, white-space is set to "nowrap", which handles
880             // leading, trailing & contiguous space character removal for us.
881         }
882
883         m_text = m_text->replace('\t', ' ');
884     }
885 #endif
886
887     if (style()) {
888         switch (style()->textTransform()) {
889             case TTNONE:
890                 break;
891             case CAPITALIZE: {
892                 m_text = m_text->capitalize(previousCharacter());
893                 break;
894             }
895             case UPPERCASE:
896                 m_text = m_text->upper();
897                 break;
898             case LOWERCASE:
899                 m_text = m_text->lower();
900                 break;
901         }
902
903         // We use the same characters here as for list markers.
904         // See the listMarkerText function in RenderListMarker.cpp.
905         switch (style()->textSecurity()) {
906             case TSNONE:
907                 break;
908             case TSCIRCLE:
909                 m_text = m_text->secure(whiteBullet);
910                 break;
911             case TSDISC:
912                 m_text = m_text->secure(bullet);
913                 break;
914             case TSSQUARE:
915                 m_text = m_text->secure(blackSquare);
916         }
917     }
918
919     ASSERT(m_text);
920     ASSERT(!isBR() || (textLength() == 1 && (*m_text)[0] == '\n'));
921
922     m_isAllASCII = charactersAreAllASCII(m_text.get());
923 }
924
925 void RenderText::setText(PassRefPtr<StringImpl> text, bool force)
926 {
927     ASSERT(text);
928
929     if (!force && equal(m_text.get(), text.get()))
930         return;
931
932     setTextInternal(text);
933     setNeedsLayoutAndPrefWidthsRecalc();
934 }
935
936 int RenderText::height() const
937 {
938     int retval = 0;
939     if (firstTextBox())
940         retval = lastTextBox()->m_y + lastTextBox()->height() - firstTextBox()->m_y;
941     return retval;
942 }
943
944 short RenderText::lineHeight(bool firstLine, bool) const
945 {
946     // Always use the interior line height of the parent (e.g., if our parent is an inline block).
947     return parent()->lineHeight(firstLine, true);
948 }
949
950 void RenderText::dirtyLineBoxes(bool fullLayout, bool)
951 {
952     if (fullLayout)
953         deleteTextBoxes();
954     else if (!m_linesDirty) {
955         for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
956             box->dirtyLineBoxes();
957     }
958     m_linesDirty = false;
959 }
960
961 InlineTextBox* RenderText::createInlineTextBox()
962 {
963     return new (renderArena()) InlineTextBox(this);
964 }
965
966 InlineBox* RenderText::createInlineBox(bool, bool isRootLineBox, bool)
967 {
968     ASSERT(!isRootLineBox);
969     InlineTextBox* textBox = createInlineTextBox();
970     if (!m_firstTextBox)
971         m_firstTextBox = m_lastTextBox = textBox;
972     else {
973         m_lastTextBox->setNextLineBox(textBox);
974         textBox->setPreviousLineBox(m_lastTextBox);
975         m_lastTextBox = textBox;
976     }
977     return textBox;
978 }
979
980 void RenderText::position(InlineBox* box)
981 {
982     InlineTextBox* s = static_cast<InlineTextBox*>(box);
983
984     // FIXME: should not be needed!!!
985     if (!s->m_len) {
986         // We want the box to be destroyed.
987         s->remove();
988         s->destroy(renderArena());
989         m_firstTextBox = m_lastTextBox = 0;
990         return;
991     }
992
993     m_containsReversedText |= s->m_reversed;
994 }
995
996 unsigned int RenderText::width(unsigned int from, unsigned int len, int xPos, bool firstLine) const
997 {
998     if (from >= textLength())
999         return 0;
1000
1001     if (from + len > textLength())
1002         len = textLength() - from;
1003
1004     return width(from, len, style(firstLine)->font(), xPos);
1005 }
1006
1007 unsigned int RenderText::width(unsigned int from, unsigned int len, const Font& f, int xPos) const
1008 {
1009     if (!characters() || from > textLength())
1010         return 0;
1011
1012     if (from + len > textLength())
1013         len = textLength() - from;
1014
1015     int w;
1016     if (&f == &style()->font()) {
1017         if (!style()->preserveNewline() && !from && len == textLength())
1018             w = maxPrefWidth();
1019         else
1020             w = widthFromCache(f, from, len, xPos);
1021     } else
1022         w = f.width(TextRun(text()->characters() + from, len), TextStyle(allowTabs(), xPos));
1023
1024     return w;
1025 }
1026
1027 int RenderText::width() const
1028 {
1029     // FIXME: we should not use an arbitrary value like this.  Perhaps we should use INT_MAX.
1030     int minx = 100000000;
1031     int maxx = 0;
1032     // slooow
1033     for (InlineTextBox* s = firstTextBox(); s; s = s->nextTextBox()) {
1034         if (s->m_x < minx)
1035             minx = s->m_x;
1036         if (s->m_x + s->m_width > maxx)
1037             maxx = s->m_x + s->m_width;
1038     }
1039
1040     return max(0, maxx - minx);
1041 }
1042
1043 IntRect RenderText::absoluteClippedOverflowRect()
1044 {
1045     RenderObject* cb = containingBlock();
1046     return cb->absoluteClippedOverflowRect();
1047 }
1048
1049 IntRect RenderText::selectionRect(bool clipToVisibleContent)
1050 {
1051     ASSERT(!needsLayout());
1052
1053     IntRect rect;
1054     if (selectionState() == SelectionNone)
1055         return rect;
1056     RenderBlock* cb =  containingBlock();
1057     if (!cb)
1058         return rect;
1059
1060     // Now calculate startPos and endPos for painting selection.
1061     // We include a selection while endPos > 0
1062     int startPos, endPos;
1063     if (selectionState() == SelectionInside) {
1064         // We are fully selected.
1065         startPos = 0;
1066         endPos = textLength();
1067     } else {
1068         selectionStartEnd(startPos, endPos);
1069         if (selectionState() == SelectionStart)
1070             endPos = textLength();
1071         else if (selectionState() == SelectionEnd)
1072             startPos = 0;
1073     }
1074
1075     if (startPos == endPos)
1076         return rect;
1077
1078     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1079         rect.unite(box->selectionRect(0, 0, startPos, endPos));
1080
1081     if (clipToVisibleContent)
1082         computeAbsoluteRepaintRect(rect);
1083     else {
1084         if (cb->hasColumns())
1085             cb->adjustRectForColumns(rect);
1086         int absx, absy;
1087         absolutePosition(absx, absy);
1088         rect.move(absx, absy);
1089     }
1090
1091     return rect;
1092 }
1093
1094 short RenderText::verticalPositionHint(bool firstLine) const
1095 {
1096     if (parent()->isReplaced())
1097         return 0; // Treat inline blocks just like blocks.  There can't be any vertical position hint.
1098     return parent()->verticalPositionHint(firstLine);
1099 }
1100
1101 int RenderText::caretMinOffset() const
1102 {
1103     InlineTextBox* box = firstTextBox();
1104     if (!box)
1105         return 0;
1106     int minOffset = box->m_start;
1107     for (box = box->nextTextBox(); box; box = box->nextTextBox())
1108         minOffset = min(minOffset, box->m_start);
1109     return minOffset;
1110 }
1111
1112 int RenderText::caretMaxOffset() const
1113 {
1114     InlineTextBox* box = lastTextBox();
1115     if (!box)
1116         return textLength();
1117     int maxOffset = box->m_start + box->m_len;
1118     for (box = box->prevTextBox(); box; box = box->prevTextBox())
1119         maxOffset = max(maxOffset, box->m_start + box->m_len);
1120     return maxOffset;
1121 }
1122
1123 unsigned RenderText::caretMaxRenderedOffset() const
1124 {
1125     int l = 0;
1126     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1127         l += box->m_len;
1128     return l;
1129 }
1130
1131 int RenderText::previousOffset(int current) const
1132 {
1133     StringImpl* si = m_text.get();
1134     TextBreakIterator* iterator = characterBreakIterator(si->characters(), si->length());
1135     if (!iterator)
1136         return current - 1;
1137
1138     long result = textBreakPreceding(iterator, current);
1139     if (result == TextBreakDone)
1140         result = current - 1;
1141
1142     return result;
1143 }
1144
1145 int RenderText::nextOffset(int current) const
1146 {
1147     StringImpl* si = m_text.get();
1148     TextBreakIterator* iterator = characterBreakIterator(si->characters(), si->length());
1149     if (!iterator)
1150         return current + 1;
1151
1152     long result = textBreakFollowing(iterator, current);
1153     if (result == TextBreakDone)
1154         result = current + 1;
1155
1156     return result;
1157 }
1158
1159 InlineBox* RenderText::inlineBox(int offset, EAffinity affinity)
1160 {
1161     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
1162         if (box->containsCaretOffset(offset)) {
1163             if (atLineWrap(box, offset) && affinity == DOWNSTREAM)
1164                 return box->nextTextBox();
1165             return box;
1166         }
1167         if (offset < box->m_start)
1168             // The offset we're looking for is before this node
1169             // this means the offset must be in content that is
1170             // not rendered.
1171             return box->prevTextBox() ? box->prevTextBox() : firstTextBox();
1172     }
1173
1174     return 0;
1175 }
1176
1177 #ifndef NDEBUG
1178
1179 void RenderText::checkConsistency() const
1180 {
1181 #ifdef CHECK_CONSISTENCY
1182     const InlineTextBox* prev = 0;
1183     for (const InlineTextBox* child = m_firstTextBox; child != 0; child = child->nextTextBox()) {
1184         ASSERT(child->object() == this);
1185         ASSERT(child->prevTextBox() == prev);
1186         prev = child;
1187     }
1188     ASSERT(prev == m_lastTextBox);
1189 #endif
1190 }
1191
1192 #endif
1193
1194 } // namespace WebCore