~ubuntu-branches/ubuntu/maverick/webkit/maverick

« back to all changes in this revision

Viewing changes to WebCore/rendering/RenderText.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Mike Hommey
  • Date: 2007-08-19 15:54:12 UTC
  • Revision ID: james.westby@ubuntu.com-20070819155412-uxxg1h9plpghmtbi
Tags: upstream-0~svn25144
ImportĀ upstreamĀ versionĀ 0~svn25144

Show diffs side-by-side

added added

removed removed

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