~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/3rdparty/webkit/WebCore/rendering/bidi.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alessandro Ghersi
  • Date: 2009-11-02 18:30:08 UTC
  • mfrom: (1.2.2 upstream)
  • mto: (15.2.5 experimental)
  • mto: This revision was merged to the branch mainline in revision 88.
  • Revision ID: james.westby@ubuntu.com-20091102183008-b6a4gcs128mvfb3m
Tags: upstream-4.6.0~beta1
ImportĀ upstreamĀ versionĀ 4.6.0~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
3
 
 * Copyright (C) 2004, 2006, 2007, 2008 Apple Inc. All right reserved.
4
 
 *
5
 
 * This library is free software; you can redistribute it and/or
6
 
 * modify it under the terms of the GNU Library General Public
7
 
 * License as published by the Free Software Foundation; either
8
 
 * version 2 of the License, or (at your option) any later version.
9
 
 *
10
 
 * This library is distributed in the hope that it will be useful,
11
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 
 * Library General Public License for more details.
14
 
 *
15
 
 * You should have received a copy of the GNU Library General Public License
16
 
 * along with this library; see the file COPYING.LIB.  If not, write to
17
 
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18
 
 * Boston, MA 02110-1301, USA.
19
 
 *
20
 
 */
21
 
 
22
 
#include "config.h"
23
 
#include "bidi.h"
24
 
 
25
 
#include "CharacterNames.h"
26
 
#include "Document.h"
27
 
#include "Element.h"
28
 
#include "FrameView.h"
29
 
#include "InlineTextBox.h"
30
 
#include "Logging.h"
31
 
#include "RenderArena.h"
32
 
#include "RenderLayer.h"
33
 
#include "RenderListMarker.h"
34
 
#include "RenderView.h"
35
 
#include "break_lines.h"
36
 
#include <wtf/AlwaysInline.h>
37
 
#include <wtf/RefCountedLeakCounter.h>
38
 
#include <wtf/StdLibExtras.h>
39
 
#include <wtf/Vector.h>
40
 
 
41
 
using namespace std;
42
 
using namespace WTF;
43
 
using namespace Unicode;
44
 
 
45
 
namespace WebCore {
46
 
 
47
 
// We don't let our line box tree for a single line get any deeper than this.
48
 
const unsigned cMaxLineDepth = 200;
49
 
 
50
 
class InlineIterator {
51
 
public:
52
 
    InlineIterator()
53
 
        : block(0)
54
 
        , obj(0)
55
 
        , pos(0)
56
 
        , nextBreakablePosition(-1)
57
 
    {
58
 
    }
59
 
 
60
 
    InlineIterator(RenderBlock* b, RenderObject* o, unsigned p)
61
 
        : block(b)
62
 
        , obj(o)
63
 
        , pos(p)
64
 
        , nextBreakablePosition(-1)
65
 
    {
66
 
    }
67
 
 
68
 
    void increment(InlineBidiResolver* resolver = 0);
69
 
    bool atEnd() const;
70
 
 
71
 
    UChar current() const;
72
 
    WTF::Unicode::Direction direction() const;
73
 
 
74
 
    RenderBlock* block;
75
 
    RenderObject* obj;
76
 
    unsigned pos;
77
 
    int nextBreakablePosition;
78
 
};
79
 
 
80
 
// Midpoint globals.  The goal is not to do any allocation when dealing with
81
 
// these midpoints, so we just keep an array around and never clear it.  We track
82
 
// the number of items and position using the two other variables.
83
 
static Vector<InlineIterator>* smidpoints;
84
 
static unsigned sNumMidpoints;
85
 
static unsigned sCurrMidpoint;
86
 
static bool betweenMidpoints;
87
 
 
88
 
static bool isLineEmpty = true;
89
 
static bool previousLineBrokeCleanly = true;
90
 
 
91
 
static int getBorderPaddingMargin(RenderObject* child, bool endOfInline)
92
 
{
93
 
    bool leftSide = (child->style()->direction() == LTR) ? !endOfInline : endOfInline;
94
 
    if (leftSide)
95
 
        return child->marginLeft() + child->paddingLeft() + child->borderLeft();
96
 
    return child->marginRight() + child->paddingRight() + child->borderRight();
97
 
}
98
 
 
99
 
static int inlineWidth(RenderObject* child, bool start = true, bool end = true)
100
 
{
101
 
    unsigned lineDepth = 1;
102
 
    int extraWidth = 0;
103
 
    RenderObject* parent = child->parent();
104
 
    while (parent->isInline() && !parent->isInlineBlockOrInlineTable() && lineDepth++ < cMaxLineDepth) {
105
 
        if (start && parent->firstChild() == child)
106
 
            extraWidth += getBorderPaddingMargin(parent, false);
107
 
        if (end && parent->lastChild() == child)
108
 
            extraWidth += getBorderPaddingMargin(parent, true);
109
 
        child = parent;
110
 
        parent = child->parent();
111
 
    }
112
 
    return extraWidth;
113
 
}
114
 
 
115
 
#ifndef NDEBUG
116
 
static WTF::RefCountedLeakCounter bidiRunCounter("BidiRun");
117
 
 
118
 
static bool inBidiRunDestroy;
119
 
#endif
120
 
 
121
 
void BidiRun::destroy()
122
 
{
123
 
#ifndef NDEBUG
124
 
    inBidiRunDestroy = true;
125
 
#endif
126
 
    RenderArena* renderArena = m_object->renderArena();
127
 
    delete this;
128
 
#ifndef NDEBUG
129
 
    inBidiRunDestroy = false;
130
 
#endif
131
 
 
132
 
    // Recover the size left there for us by operator delete and free the memory.
133
 
    renderArena->free(*reinterpret_cast<size_t*>(this), this);
134
 
}
135
 
 
136
 
void* BidiRun::operator new(size_t sz, RenderArena* renderArena) throw()
137
 
{
138
 
#ifndef NDEBUG
139
 
    bidiRunCounter.increment();
140
 
#endif
141
 
    return renderArena->allocate(sz);
142
 
}
143
 
 
144
 
void BidiRun::operator delete(void* ptr, size_t sz)
145
 
{
146
 
#ifndef NDEBUG
147
 
    bidiRunCounter.decrement();
148
 
#endif
149
 
    ASSERT(inBidiRunDestroy);
150
 
 
151
 
    // Stash size where destroy() can find it.
152
 
    *(size_t*)ptr = sz;
153
 
}
154
 
 
155
 
// ---------------------------------------------------------------------
156
 
 
157
 
inline bool operator==(const InlineIterator& it1, const InlineIterator& it2)
158
 
{
159
 
    return it1.pos == it2.pos && it1.obj == it2.obj;
160
 
}
161
 
 
162
 
inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2)
163
 
{
164
 
    return it1.pos != it2.pos || it1.obj != it2.obj;
165
 
}
166
 
 
167
 
static inline RenderObject* bidiNext(RenderBlock* block, RenderObject* current, InlineBidiResolver* resolver = 0, bool skipInlines = true, bool* endOfInlinePtr = 0)
168
 
{
169
 
    RenderObject* next = 0;
170
 
    bool oldEndOfInline = endOfInlinePtr ? *endOfInlinePtr : false;
171
 
    bool endOfInline = false;
172
 
 
173
 
    while (current) {
174
 
        next = 0;
175
 
        if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned()) {
176
 
            next = current->firstChild();
177
 
            if (next && resolver && next->isInlineFlow()) {
178
 
                EUnicodeBidi ub = next->style()->unicodeBidi();
179
 
                if (ub != UBNormal) {
180
 
                    TextDirection dir = next->style()->direction();
181
 
                    Direction d = (ub == Embed
182
 
                        ? (dir == RTL ? RightToLeftEmbedding : LeftToRightEmbedding)
183
 
                        : (dir == RTL ? RightToLeftOverride : LeftToRightOverride));
184
 
                    resolver->embed(d);
185
 
                }
186
 
            }
187
 
        }
188
 
 
189
 
        if (!next) {
190
 
            if (!skipInlines && !oldEndOfInline && current->isInlineFlow()) {
191
 
                next = current;
192
 
                endOfInline = true;
193
 
                break;
194
 
            }
195
 
 
196
 
            while (current && current != block) {
197
 
                if (resolver && current->isInlineFlow() && current->style()->unicodeBidi() != UBNormal)
198
 
                    resolver->embed(PopDirectionalFormat);
199
 
 
200
 
                next = current->nextSibling();
201
 
                if (next) {
202
 
                    if (resolver && next->isInlineFlow()) {
203
 
                        EUnicodeBidi ub = next->style()->unicodeBidi();
204
 
                        if (ub != UBNormal) {
205
 
                            TextDirection dir = next->style()->direction();
206
 
                            Direction d = (ub == Embed
207
 
                                ? (dir == RTL ? RightToLeftEmbedding: LeftToRightEmbedding)
208
 
                                : (dir == RTL ? RightToLeftOverride : LeftToRightOverride));
209
 
                            resolver->embed(d);
210
 
                        }
211
 
                    }
212
 
                    break;
213
 
                }
214
 
                
215
 
                current = current->parent();
216
 
                if (!skipInlines && current && current != block && current->isInlineFlow()) {
217
 
                    next = current;
218
 
                    endOfInline = true;
219
 
                    break;
220
 
                }
221
 
            }
222
 
        }
223
 
 
224
 
        if (!next)
225
 
            break;
226
 
 
227
 
        if (next->isText() || next->isFloating() || next->isReplaced() || next->isPositioned()
228
 
            || ((!skipInlines || !next->firstChild()) // Always return EMPTY inlines.
229
 
                && next->isInlineFlow()))
230
 
            break;
231
 
        current = next;
232
 
    }
233
 
 
234
 
    if (endOfInlinePtr)
235
 
        *endOfInlinePtr = endOfInline;
236
 
 
237
 
    return next;
238
 
}
239
 
 
240
 
static RenderObject* bidiFirst(RenderBlock* block, InlineBidiResolver* resolver, bool skipInlines = true)
241
 
{
242
 
    if (!block->firstChild())
243
 
        return 0;
244
 
    
245
 
    RenderObject* o = block->firstChild();
246
 
    if (o->isInlineFlow()) {
247
 
        if (resolver) {
248
 
            EUnicodeBidi ub = o->style()->unicodeBidi();
249
 
            if (ub != UBNormal) {
250
 
                TextDirection dir = o->style()->direction();
251
 
                Direction d = (ub == Embed
252
 
                    ? (dir == RTL ? RightToLeftEmbedding : LeftToRightEmbedding)
253
 
                    : (dir == RTL ? RightToLeftOverride : LeftToRightOverride));
254
 
                resolver->embed(d);
255
 
            }
256
 
        }
257
 
        if (skipInlines && o->firstChild())
258
 
            o = bidiNext(block, o, resolver, skipInlines);
259
 
        else {
260
 
            // Never skip empty inlines.
261
 
            if (resolver)
262
 
                resolver->commitExplicitEmbedding();
263
 
            return o; 
264
 
        }
265
 
    }
266
 
 
267
 
    if (o && !o->isText() && !o->isReplaced() && !o->isFloating() && !o->isPositioned())
268
 
        o = bidiNext(block, o, resolver, skipInlines);
269
 
 
270
 
    if (resolver)
271
 
        resolver->commitExplicitEmbedding();
272
 
    return o;
273
 
}
274
 
 
275
 
inline void InlineIterator::increment(InlineBidiResolver* resolver)
276
 
{
277
 
    if (!obj)
278
 
        return;
279
 
    if (obj->isText()) {
280
 
        pos++;
281
 
        if (pos >= static_cast<RenderText*>(obj)->textLength()) {
282
 
            obj = bidiNext(block, obj, resolver);
283
 
            pos = 0;
284
 
            nextBreakablePosition = -1;
285
 
        }
286
 
    } else {
287
 
        obj = bidiNext(block, obj, resolver);
288
 
        pos = 0;
289
 
        nextBreakablePosition = -1;
290
 
    }
291
 
}
292
 
 
293
 
template<>
294
 
inline void InlineBidiResolver::increment()
295
 
{
296
 
    current.increment(this);
297
 
}
298
 
 
299
 
inline bool InlineIterator::atEnd() const
300
 
{
301
 
    return !obj;
302
 
}
303
 
 
304
 
inline UChar InlineIterator::current() const
305
 
{
306
 
    if (!obj || !obj->isText())
307
 
        return 0;
308
 
 
309
 
    RenderText* text = static_cast<RenderText*>(obj);
310
 
    if (pos >= text->textLength())
311
 
        return 0;
312
 
 
313
 
    return text->characters()[pos];
314
 
}
315
 
 
316
 
ALWAYS_INLINE Direction InlineIterator::direction() const
317
 
{
318
 
    if (UChar c = current())
319
 
        return Unicode::direction(c);
320
 
 
321
 
    if (obj && obj->isListMarker())
322
 
        return obj->style()->direction() == LTR ? LeftToRight : RightToLeft;
323
 
 
324
 
    return OtherNeutral;
325
 
}
326
 
 
327
 
// -------------------------------------------------------------------------------------------------
328
 
 
329
 
static void chopMidpointsAt(RenderObject* obj, unsigned pos)
330
 
{
331
 
    if (!sNumMidpoints)
332
 
        return;
333
 
    InlineIterator* midpoints = smidpoints->data();
334
 
    for (int i = sNumMidpoints - 1; i >= 0; i--) {
335
 
        const InlineIterator& point = midpoints[i];
336
 
        if (point.obj == obj && point.pos == pos) {
337
 
            sNumMidpoints = i;
338
 
            break;
339
 
        }
340
 
    }
341
 
}
342
 
 
343
 
static void checkMidpoints(InlineIterator& lBreak)
344
 
{
345
 
    // Check to see if our last midpoint is a start point beyond the line break.  If so,
346
 
    // shave it off the list, and shave off a trailing space if the previous end point doesn't
347
 
    // preserve whitespace.
348
 
    if (lBreak.obj && sNumMidpoints && sNumMidpoints % 2 == 0) {
349
 
        InlineIterator* midpoints = smidpoints->data();
350
 
        InlineIterator& endpoint = midpoints[sNumMidpoints-2];
351
 
        const InlineIterator& startpoint = midpoints[sNumMidpoints-1];
352
 
        InlineIterator currpoint = endpoint;
353
 
        while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak)
354
 
            currpoint.increment();
355
 
        if (currpoint == lBreak) {
356
 
            // We hit the line break before the start point.  Shave off the start point.
357
 
            sNumMidpoints--;
358
 
            if (endpoint.obj->style()->collapseWhiteSpace()) {
359
 
                if (endpoint.obj->isText()) {
360
 
                    // Don't shave a character off the endpoint if it was from a soft hyphen.
361
 
                    RenderText* textObj = static_cast<RenderText*>(endpoint.obj);
362
 
                    if (endpoint.pos + 1 < textObj->textLength()) {
363
 
                        if (textObj->characters()[endpoint.pos+1] == softHyphen)
364
 
                            return;
365
 
                    } else if (startpoint.obj->isText()) {
366
 
                        RenderText *startText = static_cast<RenderText*>(startpoint.obj);
367
 
                        if (startText->textLength() && startText->characters()[0] == softHyphen)
368
 
                            return;
369
 
                    }
370
 
                }
371
 
                endpoint.pos--;
372
 
            }
373
 
        }
374
 
    }    
375
 
}
376
 
 
377
 
static void addMidpoint(const InlineIterator& midpoint)
378
 
{
379
 
    if (smidpoints->size() <= sNumMidpoints)
380
 
        smidpoints->grow(sNumMidpoints + 10);
381
 
 
382
 
    InlineIterator* midpoints = smidpoints->data();
383
 
    midpoints[sNumMidpoints++] = midpoint;
384
 
}
385
 
 
386
 
static void appendRunsForObject(int start, int end, RenderObject* obj, InlineBidiResolver& resolver)
387
 
{
388
 
    if (start > end || obj->isFloating() ||
389
 
        (obj->isPositioned() && !obj->hasStaticX() && !obj->hasStaticY() && !obj->container()->isInlineFlow()))
390
 
        return;
391
 
 
392
 
    bool haveNextMidpoint = (sCurrMidpoint < sNumMidpoints);
393
 
    InlineIterator nextMidpoint;
394
 
    if (haveNextMidpoint)
395
 
        nextMidpoint = smidpoints->at(sCurrMidpoint);
396
 
    if (betweenMidpoints) {
397
 
        if (!(haveNextMidpoint && nextMidpoint.obj == obj))
398
 
            return;
399
 
        // This is a new start point. Stop ignoring objects and 
400
 
        // adjust our start.
401
 
        betweenMidpoints = false;
402
 
        start = nextMidpoint.pos;
403
 
        sCurrMidpoint++;
404
 
        if (start < end)
405
 
            return appendRunsForObject(start, end, obj, resolver);
406
 
    } else {
407
 
        if (!haveNextMidpoint || (obj != nextMidpoint.obj)) {
408
 
            resolver.addRun(new (obj->renderArena()) BidiRun(start, end, obj, resolver.context(), resolver.dir()));
409
 
            return;
410
 
        }
411
 
 
412
 
        // An end midpoint has been encountered within our object.  We
413
 
        // need to go ahead and append a run with our endpoint.
414
 
        if (static_cast<int>(nextMidpoint.pos + 1) <= end) {
415
 
            betweenMidpoints = true;
416
 
            sCurrMidpoint++;
417
 
            if (nextMidpoint.pos != UINT_MAX) { // UINT_MAX means stop at the object and don't include any of it.
418
 
                if (static_cast<int>(nextMidpoint.pos + 1) > start)
419
 
                    resolver.addRun(new (obj->renderArena())
420
 
                        BidiRun(start, nextMidpoint.pos + 1, obj, resolver.context(), resolver.dir()));
421
 
                return appendRunsForObject(nextMidpoint.pos + 1, end, obj, resolver);
422
 
            }
423
 
        } else
424
 
           resolver.addRun(new (obj->renderArena()) BidiRun(start, end, obj, resolver.context(), resolver.dir()));
425
 
    }
426
 
}
427
 
 
428
 
template <>
429
 
void InlineBidiResolver::appendRun()
430
 
{
431
 
    if (!emptyRun && !eor.atEnd()) {
432
 
        int start = sor.pos;
433
 
        RenderObject *obj = sor.obj;
434
 
        while (obj && obj != eor.obj && obj != endOfLine.obj) {
435
 
            appendRunsForObject(start, obj->length(), obj, *this);        
436
 
            start = 0;
437
 
            obj = bidiNext(sor.block, obj);
438
 
        }
439
 
        if (obj) {
440
 
            unsigned pos = obj == eor.obj ? eor.pos : UINT_MAX;
441
 
            if (obj == endOfLine.obj && endOfLine.pos <= pos) {
442
 
                reachedEndOfLine = true;
443
 
                pos = endOfLine.pos;
444
 
            }
445
 
            // It's OK to add runs for zero-length RenderObjects, just don't make the run larger than it should be
446
 
            int end = obj->length() ? pos+1 : 0;
447
 
            appendRunsForObject(start, end, obj, *this);
448
 
        }
449
 
        
450
 
        eor.increment();
451
 
        sor = eor;
452
 
    }
453
 
 
454
 
    m_direction = OtherNeutral;
455
 
    m_status.eor = OtherNeutral;
456
 
}
457
 
 
458
 
InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj)
459
 
{
460
 
    // See if we have an unconstructed line box for this object that is also
461
 
    // the last item on the line.
462
 
    unsigned lineDepth = 1;
463
 
    InlineFlowBox* childBox = 0;
464
 
    InlineFlowBox* parentBox = 0;
465
 
    InlineFlowBox* result = 0;
466
 
    do {
467
 
        ASSERT(obj->isInlineFlow() || obj == this);
468
 
        RenderFlow* flow = static_cast<RenderFlow*>(obj);
469
 
 
470
 
        // Get the last box we made for this render object.
471
 
        parentBox = flow->lastLineBox();
472
 
 
473
 
        // If this box is constructed then it is from a previous line, and we need
474
 
        // to make a new box for our line.  If this box is unconstructed but it has
475
 
        // something following it on the line, then we know we have to make a new box
476
 
        // as well.  In this situation our inline has actually been split in two on
477
 
        // the same line (this can happen with very fancy language mixtures).
478
 
        bool constructedNewBox = false;
479
 
        if (!parentBox || parentBox->isConstructed() || parentBox->nextOnLine()) {
480
 
            // We need to make a new box for this render object.  Once
481
 
            // made, we need to place it at the end of the current line.
482
 
            InlineBox* newBox = obj->createInlineBox(false, obj == this);
483
 
            ASSERT(newBox->isInlineFlowBox());
484
 
            parentBox = static_cast<InlineFlowBox*>(newBox);
485
 
            parentBox->setFirstLineStyleBit(m_firstLine);
486
 
            constructedNewBox = true;
487
 
        }
488
 
 
489
 
        if (!result)
490
 
            result = parentBox;
491
 
 
492
 
        // If we have hit the block itself, then |box| represents the root
493
 
        // inline box for the line, and it doesn't have to be appended to any parent
494
 
        // inline.
495
 
        if (childBox)
496
 
            parentBox->addToLine(childBox);
497
 
 
498
 
        if (!constructedNewBox || obj == this)
499
 
            break;
500
 
 
501
 
        childBox = parentBox;        
502
 
 
503
 
        // If we've exceeded our line depth, then jump straight to the root and skip all the remaining
504
 
        // intermediate inline flows.
505
 
        obj = (++lineDepth >= cMaxLineDepth) ? this : obj->parent();
506
 
 
507
 
    } while (true);
508
 
 
509
 
    return result;
510
 
}
511
 
 
512
 
RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, BidiRun* lastRun, bool lastLine, RenderObject* endObject)
513
 
{
514
 
    ASSERT(firstRun);
515
 
 
516
 
    InlineFlowBox* parentBox = 0;
517
 
    for (BidiRun* r = firstRun; r; r = r->next()) {
518
 
        // Create a box for our object.
519
 
        bool isOnlyRun = (runCount == 1);
520
 
        if (runCount == 2 && !r->m_object->isListMarker())
521
 
            isOnlyRun = ((style()->direction() == RTL) ? lastRun : firstRun)->m_object->isListMarker();
522
 
 
523
 
        InlineBox* box = r->m_object->createInlineBox(r->m_object->isPositioned(), false, isOnlyRun);
524
 
        r->m_box = box;
525
 
 
526
 
        if (box) {
527
 
            // If we have no parent box yet, or if the run is not simply a sibling,
528
 
            // then we need to construct inline boxes as necessary to properly enclose the
529
 
            // run's inline box.
530
 
            if (!parentBox || parentBox->object() != r->m_object->parent())
531
 
                // Create new inline boxes all the way back to the appropriate insertion point.
532
 
                parentBox = createLineBoxes(r->m_object->parent());
533
 
 
534
 
            // Append the inline box to this line.
535
 
            parentBox->addToLine(box);
536
 
 
537
 
            bool visuallyOrdered = r->m_object->style()->visuallyOrdered();
538
 
            box->setBidiLevel(visuallyOrdered ? 0 : r->level());
539
 
 
540
 
            if (box->isInlineTextBox()) {
541
 
                InlineTextBox* text = static_cast<InlineTextBox*>(box);
542
 
                text->setStart(r->m_start);
543
 
                text->setLen(r->m_stop - r->m_start);
544
 
                text->m_dirOverride = r->dirOverride(visuallyOrdered);
545
 
            }
546
 
        }
547
 
    }
548
 
 
549
 
    // We should have a root inline box.  It should be unconstructed and
550
 
    // be the last continuation of our line list.
551
 
    ASSERT(lastLineBox() && !lastLineBox()->isConstructed());
552
 
 
553
 
    // Set bits on our inline flow boxes that indicate which sides should
554
 
    // paint borders/margins/padding.  This knowledge will ultimately be used when
555
 
    // we determine the horizontal positions and widths of all the inline boxes on
556
 
    // the line.
557
 
    lastLineBox()->determineSpacingForFlowBoxes(lastLine, endObject);
558
 
 
559
 
    // Now mark the line boxes as being constructed.
560
 
    lastLineBox()->setConstructed();
561
 
 
562
 
    // Return the last line.
563
 
    return lastRootBox();
564
 
}
565
 
 
566
 
void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd)
567
 
{
568
 
    // First determine our total width.
569
 
    int availableWidth = lineWidth(m_height);
570
 
    int totWidth = lineBox->getFlowSpacingWidth();
571
 
    bool needsWordSpacing = false;
572
 
    unsigned numSpaces = 0;
573
 
    ETextAlign textAlign = style()->textAlign();
574
 
 
575
 
    for (BidiRun* r = firstRun; r; r = r->next()) {
576
 
        if (!r->m_box || r->m_object->isPositioned() || r->m_box->isLineBreak())
577
 
            continue; // Positioned objects are only participating to figure out their
578
 
                      // correct static x position.  They have no effect on the width.
579
 
                      // Similarly, line break boxes have no effect on the width.
580
 
        if (r->m_object->isText()) {
581
 
            RenderText* rt = static_cast<RenderText*>(r->m_object);
582
 
 
583
 
            if (textAlign == JUSTIFY && r != trailingSpaceRun) {
584
 
                const UChar* characters = rt->characters();
585
 
                for (int i = r->m_start; i < r->m_stop; i++) {
586
 
                    UChar c = characters[i];
587
 
                    if (c == ' ' || c == '\n' || c == '\t')
588
 
                        numSpaces++;
589
 
                }
590
 
            }
591
 
 
592
 
            if (int length = rt->textLength()) {
593
 
                if (!r->m_compact && !r->m_start && needsWordSpacing && isSpaceOrNewline(rt->characters()[r->m_start]))
594
 
                    totWidth += rt->style(m_firstLine)->font().wordSpacing();
595
 
                needsWordSpacing = !isSpaceOrNewline(rt->characters()[r->m_stop - 1]) && r->m_stop == length;          
596
 
            }
597
 
            r->m_box->setWidth(rt->width(r->m_start, r->m_stop - r->m_start, totWidth, m_firstLine));
598
 
        } else if (!r->m_object->isInlineFlow()) {
599
 
            r->m_object->calcWidth();
600
 
            r->m_box->setWidth(r->m_object->width());
601
 
            if (!r->m_compact)
602
 
                 totWidth += r->m_object->marginLeft() + r->m_object->marginRight();
603
 
        }
604
 
 
605
 
        // Compacts don't contribute to the width of the line, since they are placed in the margin.
606
 
        if (!r->m_compact)
607
 
            totWidth += r->m_box->width();
608
 
    }
609
 
 
610
 
    // Armed with the total width of the line (without justification),
611
 
    // we now examine our text-align property in order to determine where to position the
612
 
    // objects horizontally.  The total width of the line can be increased if we end up
613
 
    // justifying text.
614
 
    int x = leftOffset(m_height);
615
 
    switch(textAlign) {
616
 
        case LEFT:
617
 
        case WEBKIT_LEFT:
618
 
            // The direction of the block should determine what happens with wide lines.  In
619
 
            // particular with RTL blocks, wide lines should still spill out to the left.
620
 
            if (style()->direction() == LTR) {
621
 
                if (totWidth > availableWidth && trailingSpaceRun)
622
 
                    trailingSpaceRun->m_box->setWidth(trailingSpaceRun->m_box->width() - totWidth + availableWidth);
623
 
            } else {
624
 
                if (trailingSpaceRun)
625
 
                    trailingSpaceRun->m_box->setWidth(0);
626
 
                else if (totWidth > availableWidth)
627
 
                    x -= (totWidth - availableWidth);
628
 
            }
629
 
            break;
630
 
        case JUSTIFY:
631
 
            if (numSpaces && !reachedEnd && !lineBox->endsWithBreak()) {
632
 
                if (trailingSpaceRun) {
633
 
                    totWidth -= trailingSpaceRun->m_box->width();
634
 
                    trailingSpaceRun->m_box->setWidth(0);
635
 
                }
636
 
                break;
637
 
            }
638
 
            // fall through
639
 
        case TAAUTO:
640
 
            numSpaces = 0;
641
 
            // for right to left fall through to right aligned
642
 
            if (style()->direction() == LTR) {
643
 
                if (totWidth > availableWidth && trailingSpaceRun)
644
 
                    trailingSpaceRun->m_box->setWidth(trailingSpaceRun->m_box->width() - totWidth + availableWidth);
645
 
                break;
646
 
            }
647
 
        case RIGHT:
648
 
        case WEBKIT_RIGHT:
649
 
            // Wide lines spill out of the block based off direction.
650
 
            // So even if text-align is right, if direction is LTR, wide lines should overflow out of the right
651
 
            // side of the block.
652
 
            if (style()->direction() == LTR) {
653
 
                if (trailingSpaceRun) {
654
 
                    totWidth -= trailingSpaceRun->m_box->width();
655
 
                    trailingSpaceRun->m_box->setWidth(0);
656
 
                }
657
 
                if (totWidth < availableWidth)
658
 
                    x += availableWidth - totWidth;
659
 
            } else {
660
 
                if (totWidth > availableWidth && trailingSpaceRun) {
661
 
                    trailingSpaceRun->m_box->setWidth(trailingSpaceRun->m_box->width() - totWidth + availableWidth);
662
 
                    totWidth -= trailingSpaceRun->m_box->width();
663
 
                } else
664
 
                    x += availableWidth - totWidth;
665
 
            }
666
 
            break;
667
 
        case CENTER:
668
 
        case WEBKIT_CENTER:
669
 
            int trailingSpaceWidth = 0;
670
 
            if (trailingSpaceRun) {
671
 
                totWidth -= trailingSpaceRun->m_box->width();
672
 
                trailingSpaceWidth = min(trailingSpaceRun->m_box->width(), (availableWidth - totWidth + 1) / 2);
673
 
                trailingSpaceRun->m_box->setWidth(trailingSpaceWidth);
674
 
            }
675
 
            if (style()->direction() == LTR)
676
 
                x += max((availableWidth - totWidth) / 2, 0);
677
 
            else
678
 
                x += totWidth > availableWidth ? (availableWidth - totWidth) : (availableWidth - totWidth) / 2 - trailingSpaceWidth;
679
 
            break;
680
 
    }
681
 
 
682
 
    if (numSpaces) {
683
 
        for (BidiRun* r = firstRun; r; r = r->next()) {
684
 
            if (!r->m_box || r == trailingSpaceRun)
685
 
                continue;
686
 
 
687
 
            int spaceAdd = 0;
688
 
            if (r->m_object->isText() && !r->m_compact) {
689
 
                unsigned spaces = 0;
690
 
                const UChar* characters = static_cast<RenderText*>(r->m_object)->characters();
691
 
                for (int i = r->m_start; i < r->m_stop; i++) {
692
 
                    UChar c = characters[i];
693
 
                    if (c == ' ' || c == '\n' || c == '\t')
694
 
                        spaces++;
695
 
                }
696
 
 
697
 
                ASSERT(spaces <= numSpaces);
698
 
 
699
 
                // Only justify text if whitespace is collapsed.
700
 
                if (r->m_object->style()->collapseWhiteSpace()) {
701
 
                    spaceAdd = (availableWidth - totWidth) * spaces / numSpaces;
702
 
                    static_cast<InlineTextBox*>(r->m_box)->setSpaceAdd(spaceAdd);
703
 
                    totWidth += spaceAdd;
704
 
                }
705
 
                numSpaces -= spaces;
706
 
                if (!numSpaces)
707
 
                    break;
708
 
            }
709
 
        }
710
 
    }
711
 
 
712
 
    // The widths of all runs are now known.  We can now place every inline box (and
713
 
    // compute accurate widths for the inline flow boxes).
714
 
    int leftPosition = x;
715
 
    int rightPosition = x;
716
 
    needsWordSpacing = false;
717
 
    lineBox->placeBoxesHorizontally(x, leftPosition, rightPosition, needsWordSpacing);
718
 
    lineBox->setHorizontalOverflowPositions(leftPosition, rightPosition);
719
 
}
720
 
 
721
 
void RenderBlock::computeVerticalPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun)
722
 
{
723
 
    lineBox->verticallyAlignBoxes(m_height);
724
 
    lineBox->setBlockHeight(m_height);
725
 
 
726
 
    // See if the line spilled out.  If so set overflow height accordingly.
727
 
    int bottomOfLine = lineBox->bottomOverflow();
728
 
    if (bottomOfLine > m_height && bottomOfLine > m_overflowHeight)
729
 
        m_overflowHeight = bottomOfLine;
730
 
 
731
 
    // Now make sure we place replaced render objects correctly.
732
 
    for (BidiRun* r = firstRun; r; r = r->next()) {
733
 
        if (!r->m_box)
734
 
            continue; // Skip runs with no line boxes.
735
 
 
736
 
        // Align positioned boxes with the top of the line box.  This is
737
 
        // a reasonable approximation of an appropriate y position.
738
 
        if (r->m_object->isPositioned())
739
 
            r->m_box->setYPos(m_height);
740
 
 
741
 
        // Position is used to properly position both replaced elements and
742
 
        // to update the static normal flow x/y of positioned elements.
743
 
        r->m_object->position(r->m_box);
744
 
    }
745
 
    // Positioned objects and zero-length text nodes destroy their boxes in
746
 
    // position(), which unnecessarily dirties the line.
747
 
    lineBox->markDirty(false);
748
 
}
749
 
 
750
 
// collects one line of the paragraph and transforms it to visual order
751
 
void RenderBlock::bidiReorderLine(InlineBidiResolver& resolver, const InlineIterator& end)
752
 
{
753
 
    resolver.createBidiRunsForLine(end, style()->visuallyOrdered(), previousLineBrokeCleanly);
754
 
}
755
 
 
756
 
static void buildCompactRuns(RenderObject* compactObj, InlineBidiResolver& resolver)
757
 
{
758
 
    ASSERT(compactObj->isRenderBlock());
759
 
    ASSERT(!resolver.firstRun());
760
 
 
761
 
    // Format the compact like it is its own single line.  We build up all the runs for
762
 
    // the little compact and then reorder them for bidi.
763
 
    RenderBlock* compactBlock = static_cast<RenderBlock*>(compactObj);
764
 
 
765
 
    InlineIterator start(compactBlock, bidiFirst(compactBlock, &resolver), 0);
766
 
    resolver.setPosition(start);
767
 
 
768
 
    betweenMidpoints = false;
769
 
    isLineEmpty = true;
770
 
    previousLineBrokeCleanly = true;
771
 
 
772
 
    InlineIterator end = compactBlock->findNextLineBreak(resolver);
773
 
    if (!isLineEmpty)
774
 
        compactBlock->bidiReorderLine(resolver, end);
775
 
 
776
 
    for (BidiRun* run = resolver.firstRun(); run; run = run->next())
777
 
        run->m_compact = true;
778
 
 
779
 
    sNumMidpoints = 0;
780
 
    sCurrMidpoint = 0;
781
 
    betweenMidpoints = false;
782
 
}
783
 
 
784
 
static inline bool isCollapsibleSpace(UChar character, RenderText* renderer)
785
 
{
786
 
    if (character == ' ' || character == '\t' || character == softHyphen)
787
 
        return true;
788
 
    if (character == '\n')
789
 
        return !renderer->style()->preserveNewline();
790
 
    if (character == noBreakSpace)
791
 
        return renderer->style()->nbspMode() == SPACE;
792
 
    return false;
793
 
}
794
 
 
795
 
void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, int& repaintBottom)
796
 
{
797
 
    bool useRepaintBounds = false;
798
 
 
799
 
    invalidateVerticalPosition();
800
 
    
801
 
    m_overflowHeight = 0;
802
 
        
803
 
    m_height = borderTop() + paddingTop();
804
 
    int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight();
805
 
 
806
 
    // Figure out if we should clear out our line boxes.
807
 
    // FIXME: Handle resize eventually!
808
 
    // FIXME: Do something better when floats are present.
809
 
    bool fullLayout = !firstLineBox() || !firstChild() || selfNeedsLayout() || relayoutChildren;
810
 
    if (fullLayout)
811
 
        deleteLineBoxes();
812
 
 
813
 
    // Text truncation only kicks in if your overflow isn't visible and your text-overflow-mode isn't
814
 
    // clip.
815
 
    // FIXME: CSS3 says that descendants that are clipped must also know how to truncate.  This is insanely
816
 
    // difficult to figure out (especially in the middle of doing layout), and is really an esoteric pile of nonsense
817
 
    // anyway, so we won't worry about following the draft here.
818
 
    bool hasTextOverflow = style()->textOverflow() && hasOverflowClip();
819
 
 
820
 
    // Walk all the lines and delete our ellipsis line boxes if they exist.
821
 
    if (hasTextOverflow)
822
 
         deleteEllipsisLineBoxes();
823
 
 
824
 
    if (firstChild()) {
825
 
        // layout replaced elements
826
 
        bool endOfInline = false;
827
 
        RenderObject* o = bidiFirst(this, 0, false);
828
 
        Vector<FloatWithRect> floats;
829
 
        int containerWidth = max(0, containingBlockWidth());
830
 
        while (o) {
831
 
            o->invalidateVerticalPosition();
832
 
            if (o->isReplaced() || o->isFloating() || o->isPositioned()) {
833
 
                if (relayoutChildren || o->style()->width().isPercent() || o->style()->height().isPercent())
834
 
                    o->setChildNeedsLayout(true, false);
835
 
                    
836
 
                // If relayoutChildren is set and we have percentage padding, we also need to invalidate the child's pref widths.
837
 
                if (relayoutChildren && (o->style()->paddingLeft().isPercent() || o->style()->paddingRight().isPercent()))
838
 
                    o->setPrefWidthsDirty(true, false);
839
 
            
840
 
                if (o->isPositioned())
841
 
                    o->containingBlock()->insertPositionedObject(o);
842
 
                else {
843
 
                    if (o->isFloating())
844
 
                        floats.append(FloatWithRect(o));
845
 
                    else if (fullLayout || o->needsLayout()) // Replaced elements
846
 
                        o->dirtyLineBoxes(fullLayout);
847
 
 
848
 
                    o->layoutIfNeeded();
849
 
                }
850
 
            } else if (o->isText() || (o->isInlineFlow() && !endOfInline)) {
851
 
                if (fullLayout || o->selfNeedsLayout())
852
 
                    o->dirtyLineBoxes(fullLayout);
853
 
                
854
 
                // Calculate margins of inline flows so that they can be used later by line layout.
855
 
                if (o->isInlineFlow())
856
 
                    static_cast<RenderFlow*>(o)->calcMargins(containerWidth);
857
 
                o->setNeedsLayout(false);
858
 
            }
859
 
            o = bidiNext(this, o, 0, false, &endOfInline);
860
 
        }
861
 
 
862
 
        // We want to skip ahead to the first dirty line
863
 
        InlineBidiResolver resolver;
864
 
        unsigned floatIndex;
865
 
        RootInlineBox* startLine = determineStartPosition(fullLayout, resolver, floats, floatIndex);
866
 
 
867
 
        if (fullLayout && !selfNeedsLayout()) {
868
 
            setNeedsLayout(true, false);  // Mark ourselves as needing a full layout. This way we'll repaint like
869
 
                                          // we're supposed to.
870
 
            if (!document()->view()->needsFullRepaint() && m_layer) {
871
 
                // Because we waited until we were already inside layout to discover
872
 
                // that the block really needed a full layout, we missed our chance to repaint the layer
873
 
                // before layout started.  Luckily the layer has cached the repaint rect for its original
874
 
                // position and size, and so we can use that to make a repaint happen now.
875
 
                RenderView* c = view();
876
 
                if (c && !c->printing())
877
 
                    c->repaintViewRectangle(m_layer->repaintRect());
878
 
            }
879
 
        }
880
 
 
881
 
        FloatingObject* lastFloat = m_floatingObjects ? m_floatingObjects->last() : 0;
882
 
 
883
 
        if (!smidpoints)
884
 
            smidpoints = new Vector<InlineIterator>();
885
 
 
886
 
        sNumMidpoints = 0;
887
 
        sCurrMidpoint = 0;
888
 
 
889
 
        // We also find the first clean line and extract these lines.  We will add them back
890
 
        // if we determine that we're able to synchronize after handling all our dirty lines.
891
 
        InlineIterator cleanLineStart;
892
 
        BidiStatus cleanLineBidiStatus;
893
 
        int endLineYPos = 0;
894
 
        RootInlineBox* endLine = (fullLayout || !startLine) ? 
895
 
                                 0 : determineEndPosition(startLine, cleanLineStart, cleanLineBidiStatus, endLineYPos);
896
 
 
897
 
        if (startLine) {
898
 
            useRepaintBounds = true;
899
 
            repaintTop = m_height;
900
 
            repaintBottom = m_height;
901
 
            RenderArena* arena = renderArena();
902
 
            RootInlineBox* box = startLine;
903
 
            while (box) {
904
 
                repaintTop = min(repaintTop, box->topOverflow());
905
 
                repaintBottom = max(repaintBottom, box->bottomOverflow());
906
 
                RootInlineBox* next = box->nextRootBox();
907
 
                box->deleteLine(arena);
908
 
                box = next;
909
 
            }
910
 
        }
911
 
 
912
 
        InlineIterator end = resolver.position();
913
 
 
914
 
        if (!fullLayout && lastRootBox() && lastRootBox()->endsWithBreak()) {
915
 
            // If the last line before the start line ends with a line break that clear floats,
916
 
            // adjust the height accordingly.
917
 
            // A line break can be either the first or the last object on a line, depending on its direction.
918
 
            if (InlineBox* lastLeafChild = lastRootBox()->lastLeafChild()) {
919
 
                RenderObject* lastObject = lastLeafChild->object();
920
 
                if (!lastObject->isBR())
921
 
                    lastObject = lastRootBox()->firstLeafChild()->object();
922
 
                if (lastObject->isBR()) {
923
 
                    EClear clear = lastObject->style()->clear();
924
 
                    if (clear != CNONE)
925
 
                        newLine(clear);
926
 
                }
927
 
            }
928
 
        }
929
 
 
930
 
        bool endLineMatched = false;
931
 
        bool checkForEndLineMatch = endLine;
932
 
        bool checkForFloatsFromLastLine = false;
933
 
        int lastHeight = m_height;
934
 
 
935
 
        while (!end.atEnd()) {
936
 
            // FIXME: Is this check necessary before the first iteration or can it be moved to the end?
937
 
            if (checkForEndLineMatch && (endLineMatched = matchedEndLine(resolver, cleanLineStart, cleanLineBidiStatus, endLine, endLineYPos, repaintBottom, repaintTop)))
938
 
                break;
939
 
 
940
 
            betweenMidpoints = false;
941
 
            isLineEmpty = true;
942
 
            if (m_firstLine && firstChild()->isCompact() && firstChild()->isRenderBlock()) {
943
 
                buildCompactRuns(firstChild(), resolver);
944
 
                resolver.setPosition(InlineIterator(this, firstChild()->nextSibling(), 0));
945
 
            }
946
 
            EClear clear = CNONE;
947
 
            end = findNextLineBreak(resolver, &clear);
948
 
            if (resolver.position().atEnd()) {
949
 
                resolver.deleteRuns();
950
 
                checkForFloatsFromLastLine = true;
951
 
                break;
952
 
            }
953
 
            ASSERT(end != resolver.position());
954
 
 
955
 
            if (!isLineEmpty) {
956
 
                bidiReorderLine(resolver, end);
957
 
                ASSERT(resolver.position() == end);
958
 
 
959
 
                BidiRun* trailingSpaceRun = 0;
960
 
                if (!previousLineBrokeCleanly && resolver.runCount() && resolver.logicallyLastRun()->m_object->style()->breakOnlyAfterWhiteSpace()) {
961
 
                    trailingSpaceRun = resolver.logicallyLastRun();
962
 
                    RenderObject* lastObject = trailingSpaceRun->m_object;
963
 
                    if (lastObject->isText()) {
964
 
                        RenderText* lastText = static_cast<RenderText*>(lastObject);
965
 
                        const UChar* characters = lastText->characters();
966
 
                        int firstSpace = trailingSpaceRun->stop();
967
 
                        while (firstSpace > trailingSpaceRun->start()) {
968
 
                            UChar current = characters[firstSpace - 1];
969
 
                            if (!isCollapsibleSpace(current, lastText))
970
 
                                break;
971
 
                            firstSpace--;
972
 
                        }
973
 
                        if (firstSpace == trailingSpaceRun->stop())
974
 
                            trailingSpaceRun = 0;
975
 
                        else {
976
 
                            TextDirection direction = style()->direction();
977
 
                            bool shouldReorder = trailingSpaceRun != (direction == LTR ? resolver.lastRun() : resolver.firstRun());
978
 
                            if (firstSpace != trailingSpaceRun->start()) {
979
 
                                ETextAlign textAlign = style()->textAlign();
980
 
                                // If the trailing white space is at the right hand side of a left-aligned line, then computeHorizontalPositionsForLine()
981
 
                                // does not care if trailingSpaceRun includes non-spaces at the beginning. In all other cases, trailingSpaceRun has to
982
 
                                // contain only the spaces, either because we re-order them or because computeHorizontalPositionsForLine() needs to know
983
 
                                // their width.
984
 
                                bool shouldSeparateSpaces = textAlign != LEFT && textAlign != WEBKIT_LEFT && textAlign != TAAUTO || trailingSpaceRun->m_level % 2 || direction == RTL || shouldReorder;
985
 
                                if (shouldSeparateSpaces) {
986
 
                                    BidiContext* baseContext = resolver.context();
987
 
                                    while (BidiContext* parent = baseContext->parent())
988
 
                                        baseContext = parent;
989
 
 
990
 
                                    BidiRun* newTrailingRun = new (renderArena()) BidiRun(firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun->m_object, baseContext, OtherNeutral);
991
 
                                    trailingSpaceRun->m_stop = firstSpace;
992
 
                                    if (direction == LTR)
993
 
                                        resolver.addRun(newTrailingRun);
994
 
                                    else
995
 
                                        resolver.prependRun(newTrailingRun);
996
 
                                    trailingSpaceRun = newTrailingRun;
997
 
                                    shouldReorder = false;
998
 
                                }
999
 
                            }
1000
 
                            if (shouldReorder) {
1001
 
                                if (direction == LTR) {
1002
 
                                    resolver.moveRunToEnd(trailingSpaceRun);
1003
 
                                    trailingSpaceRun->m_level = 0;
1004
 
                                } else {
1005
 
                                    resolver.moveRunToBeginning(trailingSpaceRun);
1006
 
                                    trailingSpaceRun->m_level = 1;
1007
 
                                }
1008
 
                            }
1009
 
                        }
1010
 
                    } else
1011
 
                        trailingSpaceRun = 0;
1012
 
                }
1013
 
 
1014
 
                // Now that the runs have been ordered, we create the line boxes.
1015
 
                // At the same time we figure out where border/padding/margin should be applied for
1016
 
                // inline flow boxes.
1017
 
 
1018
 
                RootInlineBox* lineBox = 0;
1019
 
                if (resolver.runCount()) {
1020
 
                    lineBox = constructLine(resolver.runCount(), resolver.firstRun(), resolver.lastRun(), !end.obj, end.obj && !end.pos ? end.obj : 0);
1021
 
                    if (lineBox) {
1022
 
                        lineBox->setEndsWithBreak(previousLineBrokeCleanly);
1023
 
 
1024
 
                        // Now we position all of our text runs horizontally.
1025
 
                        computeHorizontalPositionsForLine(lineBox, resolver.firstRun(), trailingSpaceRun, end.atEnd());
1026
 
 
1027
 
                        // Now position our text runs vertically.
1028
 
                        computeVerticalPositionsForLine(lineBox, resolver.firstRun());
1029
 
 
1030
 
#if ENABLE(SVG)
1031
 
                        // Special SVG text layout code
1032
 
                        lineBox->computePerCharacterLayoutInformation();
1033
 
#endif
1034
 
 
1035
 
#if PLATFORM(MAC)
1036
 
                        // Highlight acts as an overflow inflation.
1037
 
                        if (style()->highlight() != nullAtom)
1038
 
                            lineBox->addHighlightOverflow();
1039
 
#endif
1040
 
                    }
1041
 
                }
1042
 
 
1043
 
                resolver.deleteRuns();
1044
 
 
1045
 
                if (lineBox) {
1046
 
                    lineBox->setLineBreakInfo(end.obj, end.pos, resolver.status());
1047
 
                    if (useRepaintBounds) {
1048
 
                        repaintTop = min(repaintTop, lineBox->topOverflow());
1049
 
                        repaintBottom = max(repaintBottom, lineBox->bottomOverflow());
1050
 
                    }
1051
 
                }
1052
 
 
1053
 
                m_firstLine = false;
1054
 
                newLine(clear);
1055
 
            }
1056
 
 
1057
 
            if (m_floatingObjects && lastRootBox()) {
1058
 
                if (lastFloat) {
1059
 
                    for (FloatingObject* f = m_floatingObjects->last(); f != lastFloat; f = m_floatingObjects->prev()) {
1060
 
                    }
1061
 
                    m_floatingObjects->next();
1062
 
                } else
1063
 
                    m_floatingObjects->first();
1064
 
                for (FloatingObject* f = m_floatingObjects->current(); f; f = m_floatingObjects->next()) {
1065
 
                    if (f->m_bottom > lastHeight)
1066
 
                        lastRootBox()->floats().append(f->m_renderer);
1067
 
                    ASSERT(f->m_renderer == floats[floatIndex].object);
1068
 
                    // If a float's geometry has changed, give up on syncing with clean lines.
1069
 
                    if (floats[floatIndex].rect != IntRect(f->m_left, f->m_top, f->m_width, f->m_bottom - f->m_top))
1070
 
                        checkForEndLineMatch = false;
1071
 
                    floatIndex++;
1072
 
                }
1073
 
                lastFloat = m_floatingObjects->last();
1074
 
            }
1075
 
 
1076
 
            lastHeight = m_height;
1077
 
            sNumMidpoints = 0;
1078
 
            sCurrMidpoint = 0;
1079
 
            resolver.setPosition(end);
1080
 
        }
1081
 
 
1082
 
        if (endLine) {
1083
 
            if (endLineMatched) {
1084
 
                // Attach all the remaining lines, and then adjust their y-positions as needed.
1085
 
                int delta = m_height - endLineYPos;
1086
 
                for (RootInlineBox* line = endLine; line; line = line->nextRootBox()) {
1087
 
                    line->attachLine();
1088
 
                    if (delta) {
1089
 
                        repaintTop = min(repaintTop, line->topOverflow() + min(delta, 0));
1090
 
                        repaintBottom = max(repaintBottom, line->bottomOverflow() + max(delta, 0));
1091
 
                        line->adjustPosition(0, delta);
1092
 
                    }
1093
 
                    if (Vector<RenderObject*>* cleanLineFloats = line->floatsPtr()) {
1094
 
                        Vector<RenderObject*>::iterator end = cleanLineFloats->end();
1095
 
                        for (Vector<RenderObject*>::iterator f = cleanLineFloats->begin(); f != end; ++f) {
1096
 
                            int floatTop = (*f)->yPos() - (*f)->marginTop();
1097
 
                            insertFloatingObject(*f);
1098
 
                            m_height = floatTop + delta;
1099
 
                            positionNewFloats();
1100
 
                        }
1101
 
                    }
1102
 
                }
1103
 
                m_height = lastRootBox()->blockHeight();
1104
 
            } else {
1105
 
                // Delete all the remaining lines.
1106
 
                InlineRunBox* line = endLine;
1107
 
                RenderArena* arena = renderArena();
1108
 
                while (line) {
1109
 
                    repaintTop = min(repaintTop, line->topOverflow());
1110
 
                    repaintBottom = max(repaintBottom, line->bottomOverflow());
1111
 
                    InlineRunBox* next = line->nextLineBox();
1112
 
                    line->deleteLine(arena);
1113
 
                    line = next;
1114
 
                }
1115
 
            }
1116
 
        }
1117
 
        if (m_floatingObjects && (checkForFloatsFromLastLine || positionNewFloats()) && lastRootBox()) {
1118
 
            // In case we have a float on the last line, it might not be positioned up to now.
1119
 
            // This has to be done before adding in the bottom border/padding, or the float will
1120
 
            // include the padding incorrectly. -dwh
1121
 
            if (lastFloat) {
1122
 
                for (FloatingObject* f = m_floatingObjects->last(); f != lastFloat; f = m_floatingObjects->prev()) {
1123
 
                }
1124
 
                m_floatingObjects->next();
1125
 
            } else
1126
 
                m_floatingObjects->first();
1127
 
            for (FloatingObject* f = m_floatingObjects->current(); f; f = m_floatingObjects->next()) {
1128
 
                if (f->m_bottom > lastHeight)
1129
 
                    lastRootBox()->floats().append(f->m_renderer);
1130
 
            }
1131
 
            lastFloat = m_floatingObjects->last();
1132
 
        }
1133
 
    }
1134
 
 
1135
 
    sNumMidpoints = 0;
1136
 
    sCurrMidpoint = 0;
1137
 
 
1138
 
    // Now add in the bottom border/padding.
1139
 
    m_height += toAdd;
1140
 
 
1141
 
    // Always make sure this is at least our height.
1142
 
    m_overflowHeight = max(m_height, m_overflowHeight);
1143
 
 
1144
 
    // See if any lines spill out of the block.  If so, we need to update our overflow width.
1145
 
    checkLinesForOverflow();
1146
 
 
1147
 
    if (!firstLineBox() && hasLineIfEmpty())
1148
 
        m_height += lineHeight(true, true);
1149
 
 
1150
 
    // See if we have any lines that spill out of our block.  If we do, then we will possibly need to
1151
 
    // truncate text.
1152
 
    if (hasTextOverflow)
1153
 
        checkLinesForTextOverflow();
1154
 
}
1155
 
 
1156
 
RootInlineBox* RenderBlock::determineStartPosition(bool& fullLayout, InlineBidiResolver& resolver, Vector<FloatWithRect>& floats, unsigned& numCleanFloats)
1157
 
{
1158
 
    RootInlineBox* curr = 0;
1159
 
    RootInlineBox* last = 0;
1160
 
 
1161
 
    bool dirtiedByFloat = false;
1162
 
    if (!fullLayout) {
1163
 
        size_t floatIndex = 0;
1164
 
        for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox()) {
1165
 
            if (Vector<RenderObject*>* cleanLineFloats = curr->floatsPtr()) {
1166
 
                Vector<RenderObject*>::iterator end = cleanLineFloats->end();
1167
 
                for (Vector<RenderObject*>::iterator o = cleanLineFloats->begin(); o != end; ++o) {
1168
 
                    RenderObject* f = *o;
1169
 
                    IntSize newSize(f->width() + f->marginLeft() +f->marginRight(), f->height() + f->marginTop() + f->marginBottom());
1170
 
                    ASSERT(floatIndex < floats.size());
1171
 
                    if (floats[floatIndex].object != f) {
1172
 
                        // A new float has been inserted before this line or before its last known float.
1173
 
                        // Just do a full layout.
1174
 
                        fullLayout = true;
1175
 
                        break;
1176
 
                    }
1177
 
                    if (floats[floatIndex].rect.size() != newSize) {
1178
 
                        int floatTop = floats[floatIndex].rect.y();
1179
 
                        curr->markDirty();
1180
 
                        markLinesDirtyInVerticalRange(curr->blockHeight(), floatTop + max(floats[floatIndex].rect.height(), newSize.height()));
1181
 
                        floats[floatIndex].rect.setSize(newSize);
1182
 
                        dirtiedByFloat = true;
1183
 
                    }
1184
 
                    floatIndex++;
1185
 
                }
1186
 
            }
1187
 
            if (dirtiedByFloat || fullLayout)
1188
 
                break;
1189
 
        }
1190
 
        // Check if a new float has been inserted after the last known float.
1191
 
        if (!curr && floatIndex < floats.size())
1192
 
            fullLayout = true;
1193
 
    }
1194
 
 
1195
 
    if (fullLayout) {
1196
 
        // Nuke all our lines.
1197
 
        if (firstRootBox()) {
1198
 
            RenderArena* arena = renderArena();
1199
 
            curr = firstRootBox(); 
1200
 
            while (curr) {
1201
 
                RootInlineBox* next = curr->nextRootBox();
1202
 
                curr->deleteLine(arena);
1203
 
                curr = next;
1204
 
            }
1205
 
            ASSERT(!firstLineBox() && !lastLineBox());
1206
 
        }
1207
 
    } else {
1208
 
        if (curr) {
1209
 
            // We have a dirty line.
1210
 
            if (RootInlineBox* prevRootBox = curr->prevRootBox()) {
1211
 
                // We have a previous line.
1212
 
                if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= static_cast<RenderText*>(prevRootBox->lineBreakObj())->textLength()))
1213
 
                    // The previous line didn't break cleanly or broke at a newline
1214
 
                    // that has been deleted, so treat it as dirty too.
1215
 
                    curr = prevRootBox;
1216
 
            }
1217
 
        } else {
1218
 
            // No dirty lines were found.
1219
 
            // If the last line didn't break cleanly, treat it as dirty.
1220
 
            if (lastRootBox() && !lastRootBox()->endsWithBreak())
1221
 
                curr = lastRootBox();
1222
 
        }
1223
 
 
1224
 
        // If we have no dirty lines, then last is just the last root box.
1225
 
        last = curr ? curr->prevRootBox() : lastRootBox();
1226
 
    }
1227
 
 
1228
 
    numCleanFloats = 0;
1229
 
    if (!floats.isEmpty()) {
1230
 
        int savedHeight = m_height;
1231
 
        // Restore floats from clean lines.
1232
 
        RootInlineBox* line = firstRootBox();
1233
 
        while (line != curr) {
1234
 
            if (Vector<RenderObject*>* cleanLineFloats = line->floatsPtr()) {
1235
 
                Vector<RenderObject*>::iterator end = cleanLineFloats->end();
1236
 
                for (Vector<RenderObject*>::iterator f = cleanLineFloats->begin(); f != end; ++f) {
1237
 
                    insertFloatingObject(*f);
1238
 
                    m_height = (*f)->yPos() - (*f)->marginTop();
1239
 
                    positionNewFloats();
1240
 
                    ASSERT(floats[numCleanFloats].object == *f);
1241
 
                    numCleanFloats++;
1242
 
                }
1243
 
            }
1244
 
            line = line->nextRootBox();
1245
 
        }
1246
 
        m_height = savedHeight;
1247
 
    }
1248
 
 
1249
 
    m_firstLine = !last;
1250
 
    previousLineBrokeCleanly = !last || last->endsWithBreak();
1251
 
 
1252
 
    RenderObject* startObj;
1253
 
    int pos = 0;
1254
 
    if (last) {
1255
 
        m_height = last->blockHeight();
1256
 
        startObj = last->lineBreakObj();
1257
 
        pos = last->lineBreakPos();
1258
 
        resolver.setStatus(last->lineBreakBidiStatus());
1259
 
    } else {
1260
 
        bool ltr = style()->direction() == LTR
1261
 
    #if ENABLE(SVG)   
1262
 
            || (style()->unicodeBidi() == UBNormal && isSVGText())
1263
 
    #endif
1264
 
            ;
1265
 
 
1266
 
        BidiContext* context = new BidiContext(ltr ? 0 : 1, ltr ? LeftToRight : RightToLeft, style()->unicodeBidi() == Override);
1267
 
 
1268
 
        resolver.setLastStrongDir(context->dir());
1269
 
        resolver.setLastDir(context->dir());
1270
 
        resolver.setEorDir(context->dir());
1271
 
        resolver.setContext(context);
1272
 
        startObj = bidiFirst(this, &resolver);
1273
 
    }
1274
 
 
1275
 
    resolver.setPosition(InlineIterator(this, startObj, pos));
1276
 
 
1277
 
    return curr;
1278
 
}
1279
 
 
1280
 
RootInlineBox* RenderBlock::determineEndPosition(RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus, int& yPos)
1281
 
{
1282
 
    RootInlineBox* last = 0;
1283
 
    if (!startLine)
1284
 
        last = 0;
1285
 
    else {
1286
 
        for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) {
1287
 
            if (curr->isDirty())
1288
 
                last = 0;
1289
 
            else if (!last)
1290
 
                last = curr;
1291
 
        }
1292
 
    }
1293
 
 
1294
 
    if (!last)
1295
 
        return 0;
1296
 
 
1297
 
    RootInlineBox* prev = last->prevRootBox();
1298
 
    cleanLineStart = InlineIterator(this, prev->lineBreakObj(), prev->lineBreakPos());
1299
 
    cleanLineBidiStatus = prev->lineBreakBidiStatus();
1300
 
    yPos = prev->blockHeight();
1301
 
 
1302
 
    for (RootInlineBox* line = last; line; line = line->nextRootBox())
1303
 
        line->extractLine(); // Disconnect all line boxes from their render objects while preserving
1304
 
                             // their connections to one another.
1305
 
 
1306
 
    return last;
1307
 
}
1308
 
 
1309
 
bool RenderBlock::matchedEndLine(const InlineBidiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& endLineStatus, RootInlineBox*& endLine, int& endYPos, int& repaintBottom, int& repaintTop)
1310
 
{
1311
 
    if (resolver.position() == endLineStart) {
1312
 
        if (resolver.status() != endLineStatus)
1313
 
            return false;
1314
 
 
1315
 
        int delta = m_height - endYPos;
1316
 
        if (!delta || !m_floatingObjects)
1317
 
            return true;
1318
 
 
1319
 
        // See if any floats end in the range along which we want to shift the lines vertically.
1320
 
        int top = min(m_height, endYPos);
1321
 
 
1322
 
        RootInlineBox* lastLine = endLine;
1323
 
        while (RootInlineBox* nextLine = lastLine->nextRootBox())
1324
 
            lastLine = nextLine;
1325
 
 
1326
 
        int bottom = lastLine->blockHeight() + abs(delta);
1327
 
 
1328
 
        for (FloatingObject* f = m_floatingObjects->first(); f; f = m_floatingObjects->next()) {
1329
 
            if (f->m_bottom >= top && f->m_bottom < bottom)
1330
 
                return false;
1331
 
        }
1332
 
 
1333
 
        return true;
1334
 
    }
1335
 
 
1336
 
    // The first clean line doesn't match, but we can check a handful of following lines to try
1337
 
    // to match back up.
1338
 
    static int numLines = 8; // The # of lines we're willing to match against.
1339
 
    RootInlineBox* line = endLine;
1340
 
    for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) {
1341
 
        if (line->lineBreakObj() == resolver.position().obj && line->lineBreakPos() == resolver.position().pos) {
1342
 
            // We have a match.
1343
 
            if (line->lineBreakBidiStatus() != resolver.status())
1344
 
                return false; // ...but the bidi state doesn't match.
1345
 
            RootInlineBox* result = line->nextRootBox();
1346
 
 
1347
 
            // Set our yPos to be the block height of endLine.
1348
 
            if (result)
1349
 
                endYPos = line->blockHeight();
1350
 
 
1351
 
            int delta = m_height - endYPos;
1352
 
            if (delta && m_floatingObjects) {
1353
 
                // See if any floats end in the range along which we want to shift the lines vertically.
1354
 
                int top = min(m_height, endYPos);
1355
 
 
1356
 
                RootInlineBox* lastLine = endLine;
1357
 
                while (RootInlineBox* nextLine = lastLine->nextRootBox())
1358
 
                    lastLine = nextLine;
1359
 
 
1360
 
                int bottom = lastLine->blockHeight() + abs(delta);
1361
 
 
1362
 
                for (FloatingObject* f = m_floatingObjects->first(); f; f = m_floatingObjects->next()) {
1363
 
                    if (f->m_bottom >= top && f->m_bottom < bottom)
1364
 
                        return false;
1365
 
                }
1366
 
            }
1367
 
 
1368
 
            // Now delete the lines that we failed to sync.
1369
 
            RootInlineBox* boxToDelete = endLine;
1370
 
            RenderArena* arena = renderArena();
1371
 
            while (boxToDelete && boxToDelete != result) {
1372
 
                repaintTop = min(repaintTop, boxToDelete->topOverflow());
1373
 
                repaintBottom = max(repaintBottom, boxToDelete->bottomOverflow());
1374
 
                RootInlineBox* next = boxToDelete->nextRootBox();
1375
 
                boxToDelete->deleteLine(arena);
1376
 
                boxToDelete = next;
1377
 
            }
1378
 
 
1379
 
            endLine = result;
1380
 
            return result;
1381
 
        }
1382
 
    }
1383
 
 
1384
 
    return false;
1385
 
}
1386
 
 
1387
 
static inline bool skipNonBreakingSpace(const InlineIterator& it)
1388
 
{
1389
 
    if (it.obj->style()->nbspMode() != SPACE || it.current() != noBreakSpace)
1390
 
        return false;
1391
 
 
1392
 
    // FIXME: This is bad.  It makes nbsp inconsistent with space and won't work correctly
1393
 
    // with m_minWidth/m_maxWidth.
1394
 
    // Do not skip a non-breaking space if it is the first character
1395
 
    // on a line after a clean line break (or on the first line, since previousLineBrokeCleanly starts off
1396
 
    // |true|).
1397
 
    if (isLineEmpty && previousLineBrokeCleanly)
1398
 
        return false;
1399
 
 
1400
 
    return true;
1401
 
}
1402
 
 
1403
 
static inline bool shouldCollapseWhiteSpace(const RenderStyle* style)
1404
 
{
1405
 
    return style->collapseWhiteSpace() || (style->whiteSpace() == PRE_WRAP && (!isLineEmpty || !previousLineBrokeCleanly));
1406
 
}
1407
 
 
1408
 
static inline bool shouldPreserveNewline(RenderObject* object)
1409
 
{
1410
 
#if ENABLE(SVG)
1411
 
    if (object->isSVGText())
1412
 
        return false;
1413
 
#endif
1414
 
 
1415
 
    return object->style()->preserveNewline();
1416
 
}
1417
 
 
1418
 
static bool inlineFlowRequiresLineBox(RenderObject* flow)
1419
 
{
1420
 
    // FIXME: Right now, we only allow line boxes for inlines that are truly empty.
1421
 
    // We need to fix this, though, because at the very least, inlines containing only
1422
 
    // ignorable whitespace should should also have line boxes. 
1423
 
    return flow->isInlineFlow() && !flow->firstChild() && flow->hasHorizontalBordersPaddingOrMargin();
1424
 
}
1425
 
 
1426
 
static inline bool requiresLineBox(const InlineIterator& it)
1427
 
{
1428
 
    if (it.obj->isFloatingOrPositioned())
1429
 
        return false;
1430
 
 
1431
 
    if (it.obj->isInlineFlow() && !inlineFlowRequiresLineBox(it.obj))
1432
 
        return false;
1433
 
 
1434
 
    if (!shouldCollapseWhiteSpace(it.obj->style()) || it.obj->isBR())
1435
 
        return true;
1436
 
 
1437
 
    UChar current = it.current();
1438
 
    return current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || shouldPreserveNewline(it.obj)) && !skipNonBreakingSpace(it);
1439
 
}
1440
 
 
1441
 
bool RenderBlock::generatesLineBoxesForInlineChild(RenderObject* inlineObj)
1442
 
{
1443
 
    ASSERT(inlineObj->parent() == this);
1444
 
 
1445
 
    InlineIterator it(this, inlineObj, 0);
1446
 
    while (!it.atEnd() && !requiresLineBox(it))
1447
 
        it.increment();
1448
 
 
1449
 
    return !it.atEnd();
1450
 
}
1451
 
 
1452
 
// FIXME: The entire concept of the skipTrailingWhitespace function is flawed, since we really need to be building
1453
 
// line boxes even for containers that may ultimately collapse away.  Otherwise we'll never get positioned
1454
 
// elements quite right.  In other words, we need to build this function's work into the normal line
1455
 
// object iteration process.
1456
 
// NB. this function will insert any floating elements that would otherwise
1457
 
// be skipped but it will not position them.
1458
 
void RenderBlock::skipTrailingWhitespace(InlineIterator& iterator)
1459
 
{
1460
 
    while (!iterator.atEnd() && !requiresLineBox(iterator)) {
1461
 
        RenderObject* object = iterator.obj;
1462
 
        if (object->isFloating()) {
1463
 
            insertFloatingObject(object);
1464
 
        } else if (object->isPositioned()) {
1465
 
            // FIXME: The math here is actually not really right.  It's a best-guess approximation that
1466
 
            // will work for the common cases
1467
 
            RenderObject* c = object->container();
1468
 
            if (c->isInlineFlow()) {
1469
 
                // A relative positioned inline encloses us.  In this case, we also have to determine our
1470
 
                // position as though we were an inline.  Set |staticX| and |staticY| on the relative positioned
1471
 
                // inline so that we can obtain the value later.
1472
 
                c->setStaticX(style()->direction() == LTR ? leftOffset(m_height) : rightOffset(m_height));
1473
 
                c->setStaticY(m_height);
1474
 
            }
1475
 
    
1476
 
            if (object->hasStaticX()) {
1477
 
                if (object->style()->isOriginalDisplayInlineType())
1478
 
                    object->setStaticX(style()->direction() == LTR ? leftOffset(m_height) : width() - rightOffset(m_height));
1479
 
                else
1480
 
                    object->setStaticX(style()->direction() == LTR ? borderLeft() + paddingLeft() : borderRight() + paddingRight());
1481
 
            }
1482
 
    
1483
 
            if (object->hasStaticY())
1484
 
                object->setStaticY(m_height);
1485
 
        }
1486
 
        iterator.increment();
1487
 
    }
1488
 
}
1489
 
 
1490
 
int RenderBlock::skipLeadingWhitespace(InlineBidiResolver& resolver)
1491
 
{
1492
 
    int availableWidth = lineWidth(m_height);
1493
 
    while (!resolver.position().atEnd() && !requiresLineBox(resolver.position())) {
1494
 
        RenderObject* object = resolver.position().obj;
1495
 
        if (object->isFloating()) {
1496
 
            insertFloatingObject(object);
1497
 
            positionNewFloats();
1498
 
            availableWidth = lineWidth(m_height);
1499
 
        } else if (object->isPositioned()) {
1500
 
            // FIXME: The math here is actually not really right.  It's a best-guess approximation that
1501
 
            // will work for the common cases
1502
 
            RenderObject* c = object->container();
1503
 
            if (c->isInlineFlow()) {
1504
 
                // A relative positioned inline encloses us.  In this case, we also have to determine our
1505
 
                // position as though we were an inline.  Set |staticX| and |staticY| on the relative positioned
1506
 
                // inline so that we can obtain the value later.
1507
 
                c->setStaticX(style()->direction() == LTR ? leftOffset(m_height) : rightOffset(m_height));
1508
 
                c->setStaticY(m_height);
1509
 
            }
1510
 
    
1511
 
            if (object->hasStaticX()) {
1512
 
                if (object->style()->isOriginalDisplayInlineType())
1513
 
                    object->setStaticX(style()->direction() == LTR ? leftOffset(m_height) : width() - rightOffset(m_height));
1514
 
                else
1515
 
                    object->setStaticX(style()->direction() == LTR ? borderLeft() + paddingLeft() : borderRight() + paddingRight());
1516
 
            }
1517
 
    
1518
 
            if (object->hasStaticY())
1519
 
                object->setStaticY(m_height);
1520
 
        }
1521
 
        resolver.increment();
1522
 
    }
1523
 
    resolver.commitExplicitEmbedding();
1524
 
    return availableWidth;
1525
 
}
1526
 
 
1527
 
// This is currently just used for list markers and inline flows that have line boxes. Neither should 
1528
 
// have an effect on whitespace at the start of the line. 
1529
 
static bool shouldSkipWhitespaceAfterStartObject(RenderBlock* block, RenderObject* o)
1530
 
{
1531
 
    RenderObject* next = bidiNext(block, o);
1532
 
    if (next && !next->isBR() && next->isText() && static_cast<RenderText*>(next)->textLength() > 0) {
1533
 
        RenderText* nextText = static_cast<RenderText*>(next);
1534
 
        UChar nextChar = nextText->characters()[0];
1535
 
        if (nextText->style()->isCollapsibleWhiteSpace(nextChar)) {
1536
 
            addMidpoint(InlineIterator(0, o, 0));
1537
 
            return true;
1538
 
        }
1539
 
    }
1540
 
 
1541
 
    return false;
1542
 
}
1543
 
 
1544
 
void RenderBlock::fitBelowFloats(int widthToFit, int& availableWidth)
1545
 
{
1546
 
    ASSERT(widthToFit > availableWidth);
1547
 
 
1548
 
    int floatBottom;
1549
 
    int lastFloatBottom = m_height;
1550
 
    int newLineWidth = availableWidth;
1551
 
    while (true) {
1552
 
        floatBottom = nextFloatBottomBelow(lastFloatBottom);
1553
 
        if (!floatBottom)
1554
 
            break;
1555
 
 
1556
 
        newLineWidth = lineWidth(floatBottom);
1557
 
        lastFloatBottom = floatBottom;
1558
 
        if (newLineWidth >= widthToFit)
1559
 
            break;
1560
 
    }
1561
 
 
1562
 
    if (newLineWidth > availableWidth) {
1563
 
        m_height = lastFloatBottom;
1564
 
        availableWidth = newLineWidth;
1565
 
    }
1566
 
}
1567
 
 
1568
 
InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, EClear* clear)
1569
 
{
1570
 
    ASSERT(resolver.position().block == this);
1571
 
 
1572
 
    bool appliedStartWidth = resolver.position().pos > 0;
1573
 
 
1574
 
    int width = skipLeadingWhitespace(resolver);
1575
 
 
1576
 
    int w = 0;
1577
 
    int tmpW = 0;
1578
 
 
1579
 
    if (resolver.position().atEnd())
1580
 
        return resolver.position();
1581
 
 
1582
 
    // This variable is used only if whitespace isn't set to PRE, and it tells us whether
1583
 
    // or not we are currently ignoring whitespace.
1584
 
    bool ignoringSpaces = false;
1585
 
    InlineIterator ignoreStart;
1586
 
    
1587
 
    // This variable tracks whether the very last character we saw was a space.  We use
1588
 
    // this to detect when we encounter a second space so we know we have to terminate
1589
 
    // a run.
1590
 
    bool currentCharacterIsSpace = false;
1591
 
    bool currentCharacterIsWS = false;
1592
 
    RenderObject* trailingSpaceObject = 0;
1593
 
 
1594
 
    InlineIterator lBreak = resolver.position();
1595
 
 
1596
 
    RenderObject *o = resolver.position().obj;
1597
 
    RenderObject *last = o;
1598
 
    unsigned pos = resolver.position().pos;
1599
 
    int nextBreakable = resolver.position().nextBreakablePosition;
1600
 
    bool atStart = true;
1601
 
 
1602
 
    bool prevLineBrokeCleanly = previousLineBrokeCleanly;
1603
 
    previousLineBrokeCleanly = false;
1604
 
 
1605
 
    bool autoWrapWasEverTrueOnLine = false;
1606
 
    bool floatsFitOnLine = true;
1607
 
    
1608
 
    // Firefox and Opera will allow a table cell to grow to fit an image inside it under
1609
 
    // very specific circumstances (in order to match common WinIE renderings). 
1610
 
    // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.) 
1611
 
    bool allowImagesToBreak = !style()->htmlHacks() || !isTableCell() || !style()->width().isIntrinsicOrAuto();
1612
 
 
1613
 
    EWhiteSpace currWS = style()->whiteSpace();
1614
 
    EWhiteSpace lastWS = currWS;
1615
 
    while (o) {
1616
 
        currWS = o->isReplaced() ? o->parent()->style()->whiteSpace() : o->style()->whiteSpace();
1617
 
        lastWS = last->isReplaced() ? last->parent()->style()->whiteSpace() : last->style()->whiteSpace();
1618
 
        
1619
 
        bool autoWrap = RenderStyle::autoWrap(currWS);
1620
 
        autoWrapWasEverTrueOnLine = autoWrapWasEverTrueOnLine || autoWrap;
1621
 
 
1622
 
#if ENABLE(SVG)
1623
 
        bool preserveNewline = o->isSVGText() ? false : RenderStyle::preserveNewline(currWS);
1624
 
#else
1625
 
        bool preserveNewline = RenderStyle::preserveNewline(currWS);
1626
 
#endif
1627
 
 
1628
 
        bool collapseWhiteSpace = RenderStyle::collapseWhiteSpace(currWS);
1629
 
            
1630
 
        if (o->isBR()) {
1631
 
            if (w + tmpW <= width) {
1632
 
                lBreak.obj = o;
1633
 
                lBreak.pos = 0;
1634
 
                lBreak.nextBreakablePosition = -1;
1635
 
                lBreak.increment();
1636
 
 
1637
 
                // A <br> always breaks a line, so don't let the line be collapsed
1638
 
                // away. Also, the space at the end of a line with a <br> does not
1639
 
                // get collapsed away.  It only does this if the previous line broke
1640
 
                // cleanly.  Otherwise the <br> has no effect on whether the line is
1641
 
                // empty or not.
1642
 
                if (prevLineBrokeCleanly)
1643
 
                    isLineEmpty = false;
1644
 
                trailingSpaceObject = 0;
1645
 
                previousLineBrokeCleanly = true;
1646
 
 
1647
 
                if (!isLineEmpty && clear)
1648
 
                    *clear = o->style()->clear();
1649
 
            }
1650
 
            goto end;
1651
 
        }
1652
 
 
1653
 
        if (o->isFloatingOrPositioned()) {
1654
 
            // add to special objects...
1655
 
            if (o->isFloating()) {
1656
 
                insertFloatingObject(o);
1657
 
                // check if it fits in the current line.
1658
 
                // If it does, position it now, otherwise, position
1659
 
                // it after moving to next line (in newLine() func)
1660
 
                if (floatsFitOnLine && o->width() + o->marginLeft() + o->marginRight() + w + tmpW <= width) {
1661
 
                    positionNewFloats();
1662
 
                    width = lineWidth(m_height);
1663
 
                } else
1664
 
                    floatsFitOnLine = false;
1665
 
            } else if (o->isPositioned()) {
1666
 
                // If our original display wasn't an inline type, then we can
1667
 
                // go ahead and determine our static x position now.
1668
 
                bool isInlineType = o->style()->isOriginalDisplayInlineType();
1669
 
                bool needToSetStaticX = o->hasStaticX();
1670
 
                if (o->hasStaticX() && !isInlineType) {
1671
 
                    o->setStaticX(o->parent()->style()->direction() == LTR ?
1672
 
                                  borderLeft() + paddingLeft() :
1673
 
                                  borderRight() + paddingRight());
1674
 
                    needToSetStaticX = false;
1675
 
                }
1676
 
 
1677
 
                // If our original display was an INLINE type, then we can go ahead
1678
 
                // and determine our static y position now.
1679
 
                bool needToSetStaticY = o->hasStaticY();
1680
 
                if (o->hasStaticY() && isInlineType) {
1681
 
                    o->setStaticY(m_height);
1682
 
                    needToSetStaticY = false;
1683
 
                }
1684
 
                
1685
 
                bool needToCreateLineBox = needToSetStaticX || needToSetStaticY;
1686
 
                RenderObject* c = o->container();
1687
 
                if (c->isInlineFlow() && (!needToSetStaticX || !needToSetStaticY))
1688
 
                    needToCreateLineBox = true;
1689
 
 
1690
 
                // If we're ignoring spaces, we have to stop and include this object and
1691
 
                // then start ignoring spaces again.
1692
 
                if (needToCreateLineBox) {
1693
 
                    trailingSpaceObject = 0;
1694
 
                    ignoreStart.obj = o;
1695
 
                    ignoreStart.pos = 0;
1696
 
                    if (ignoringSpaces) {
1697
 
                        addMidpoint(ignoreStart); // Stop ignoring spaces.
1698
 
                        addMidpoint(ignoreStart); // Start ignoring again.
1699
 
                    }
1700
 
                    
1701
 
                }
1702
 
            }
1703
 
        } else if (o->isInlineFlow()) {
1704
 
            // Right now, we should only encounter empty inlines here.
1705
 
            ASSERT(!o->firstChild());
1706
 
    
1707
 
            // Now that some inline flows have line boxes, if we are already ignoring spaces, we need 
1708
 
            // to make sure that we stop to include this object and then start ignoring spaces again. 
1709
 
            // If this object is at the start of the line, we need to behave like list markers and 
1710
 
            // start ignoring spaces.
1711
 
            if (inlineFlowRequiresLineBox(o)) {
1712
 
                isLineEmpty = false;
1713
 
                if (ignoringSpaces) {
1714
 
                    trailingSpaceObject = 0;
1715
 
                    addMidpoint(InlineIterator(0, o, 0)); // Stop ignoring spaces.
1716
 
                    addMidpoint(InlineIterator(0, o, 0)); // Start ignoring again.
1717
 
                } else if (style()->collapseWhiteSpace() && resolver.position().obj == o
1718
 
                    && shouldSkipWhitespaceAfterStartObject(this, o)) {
1719
 
                    // Like with list markers, we start ignoring spaces to make sure that any 
1720
 
                    // additional spaces we see will be discarded.
1721
 
                    currentCharacterIsSpace = true;
1722
 
                    currentCharacterIsWS = true;
1723
 
                    ignoringSpaces = true;
1724
 
                }
1725
 
            }
1726
 
 
1727
 
            tmpW += o->marginLeft() + o->borderLeft() + o->paddingLeft() +
1728
 
                    o->marginRight() + o->borderRight() + o->paddingRight();
1729
 
        } else if (o->isReplaced()) {
1730
 
            // Break on replaced elements if either has normal white-space.
1731
 
            if ((autoWrap || RenderStyle::autoWrap(lastWS)) && (!o->isImage() || allowImagesToBreak)) {
1732
 
                w += tmpW;
1733
 
                tmpW = 0;
1734
 
                lBreak.obj = o;
1735
 
                lBreak.pos = 0;
1736
 
                lBreak.nextBreakablePosition = -1;
1737
 
            }
1738
 
 
1739
 
            if (ignoringSpaces)
1740
 
                addMidpoint(InlineIterator(0, o, 0));
1741
 
 
1742
 
            isLineEmpty = false;
1743
 
            ignoringSpaces = false;
1744
 
            currentCharacterIsSpace = false;
1745
 
            currentCharacterIsWS = false;
1746
 
            trailingSpaceObject = 0;
1747
 
            
1748
 
            // Optimize for a common case. If we can't find whitespace after the list
1749
 
            // item, then this is all moot. -dwh
1750
 
            if (o->isListMarker() && !static_cast<RenderListMarker*>(o)->isInside()) {
1751
 
                if (style()->collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(this, o)) {
1752
 
                    // Like with inline flows, we start ignoring spaces to make sure that any 
1753
 
                    // additional spaces we see will be discarded.
1754
 
                    currentCharacterIsSpace = true;
1755
 
                    currentCharacterIsWS = true;
1756
 
                    ignoringSpaces = true;
1757
 
                }
1758
 
            } else
1759
 
                tmpW += o->width() + o->marginLeft() + o->marginRight() + inlineWidth(o);
1760
 
        } else if (o->isText()) {
1761
 
            if (!pos)
1762
 
                appliedStartWidth = false;
1763
 
 
1764
 
            RenderText* t = static_cast<RenderText*>(o);
1765
 
 
1766
 
            int strlen = t->textLength();
1767
 
            int len = strlen - pos;
1768
 
            const UChar* str = t->characters();
1769
 
 
1770
 
            const Font& f = t->style(m_firstLine)->font();
1771
 
 
1772
 
            int lastSpace = pos;
1773
 
            int wordSpacing = o->style()->wordSpacing();
1774
 
            int lastSpaceWordSpacing = 0;
1775
 
 
1776
 
            int wrapW = tmpW + inlineWidth(o, !appliedStartWidth, true);
1777
 
            int charWidth = 0;
1778
 
            bool breakNBSP = autoWrap && o->style()->nbspMode() == SPACE;
1779
 
            // Auto-wrapping text should wrap in the middle of a word only if it could not wrap before the word,
1780
 
            // which is only possible if the word is the first thing on the line, that is, if |w| is zero.
1781
 
            bool breakWords = o->style()->breakWords() && ((autoWrap && !w) || currWS == PRE);
1782
 
            bool midWordBreak = false;
1783
 
            bool breakAll = o->style()->wordBreak() == BreakAllWordBreak && autoWrap;
1784
 
 
1785
 
            if (t->isWordBreak()) {
1786
 
                w += tmpW;
1787
 
                tmpW = 0;
1788
 
                lBreak.obj = o;
1789
 
                lBreak.pos = 0;
1790
 
                lBreak.nextBreakablePosition = -1;
1791
 
                ASSERT(!len);
1792
 
            }
1793
 
 
1794
 
            while (len) {
1795
 
                bool previousCharacterIsSpace = currentCharacterIsSpace;
1796
 
                bool previousCharacterIsWS = currentCharacterIsWS;
1797
 
                UChar c = str[pos];
1798
 
                currentCharacterIsSpace = c == ' ' || c == '\t' || (!preserveNewline && (c == '\n'));
1799
 
 
1800
 
                if (!collapseWhiteSpace || !currentCharacterIsSpace)
1801
 
                    isLineEmpty = false;
1802
 
                
1803
 
                // Check for soft hyphens.  Go ahead and ignore them.
1804
 
                if (c == softHyphen) {
1805
 
                    if (!ignoringSpaces) {
1806
 
                        // Ignore soft hyphens
1807
 
                        InlineIterator beforeSoftHyphen;
1808
 
                        if (pos)
1809
 
                            beforeSoftHyphen = InlineIterator(0, o, pos - 1);
1810
 
                        else
1811
 
                            beforeSoftHyphen = InlineIterator(0, last, last->isText() ? static_cast<RenderText*>(last)->textLength() - 1 : 0);
1812
 
                        // Two consecutive soft hyphens. Avoid overlapping midpoints.
1813
 
                        if (sNumMidpoints && smidpoints->at(sNumMidpoints - 1).obj == o && smidpoints->at(sNumMidpoints - 1).pos == pos)
1814
 
                            sNumMidpoints--;
1815
 
                        else
1816
 
                            addMidpoint(beforeSoftHyphen);
1817
 
 
1818
 
                        // Add the width up to but not including the hyphen.
1819
 
                        tmpW += t->width(lastSpace, pos - lastSpace, f, w + tmpW) + lastSpaceWordSpacing;
1820
 
 
1821
 
                        // For wrapping text only, include the hyphen.  We need to ensure it will fit
1822
 
                        // on the line if it shows when we break.
1823
 
                        if (autoWrap)
1824
 
                            tmpW += t->width(pos, 1, f, w + tmpW);
1825
 
 
1826
 
                        InlineIterator afterSoftHyphen(0, o, pos);
1827
 
                        afterSoftHyphen.increment();
1828
 
                        addMidpoint(afterSoftHyphen);
1829
 
                    }
1830
 
 
1831
 
                    pos++;
1832
 
                    len--;
1833
 
                    lastSpaceWordSpacing = 0;
1834
 
                    lastSpace = pos; // Cheesy hack to prevent adding in widths of the run twice.
1835
 
                    continue;
1836
 
                }
1837
 
                
1838
 
                bool applyWordSpacing = false;
1839
 
                
1840
 
                currentCharacterIsWS = currentCharacterIsSpace || (breakNBSP && c == noBreakSpace);
1841
 
 
1842
 
                if ((breakAll || breakWords) && !midWordBreak) {
1843
 
                    wrapW += charWidth;
1844
 
                    charWidth = t->width(pos, 1, f, w + wrapW);
1845
 
                    midWordBreak = w + wrapW + charWidth > width;
1846
 
                }
1847
 
 
1848
 
                bool betweenWords = c == '\n' || (currWS != PRE && !atStart && isBreakable(str, pos, strlen, nextBreakable, breakNBSP));
1849
 
    
1850
 
                if (betweenWords || midWordBreak) {
1851
 
                    bool stoppedIgnoringSpaces = false;
1852
 
                    if (ignoringSpaces) {
1853
 
                        if (!currentCharacterIsSpace) {
1854
 
                            // Stop ignoring spaces and begin at this
1855
 
                            // new point.
1856
 
                            ignoringSpaces = false;
1857
 
                            lastSpaceWordSpacing = 0;
1858
 
                            lastSpace = pos; // e.g., "Foo    goo", don't add in any of the ignored spaces.
1859
 
                            addMidpoint(InlineIterator(0, o, pos));
1860
 
                            stoppedIgnoringSpaces = true;
1861
 
                        } else {
1862
 
                            // Just keep ignoring these spaces.
1863
 
                            pos++;
1864
 
                            len--;
1865
 
                            continue;
1866
 
                        }
1867
 
                    }
1868
 
 
1869
 
                    int additionalTmpW = t->width(lastSpace, pos - lastSpace, f, w+tmpW) + lastSpaceWordSpacing;
1870
 
                    tmpW += additionalTmpW;
1871
 
                    if (!appliedStartWidth) {
1872
 
                        tmpW += inlineWidth(o, true, false);
1873
 
                        appliedStartWidth = true;
1874
 
                    }
1875
 
                    
1876
 
                    applyWordSpacing =  wordSpacing && currentCharacterIsSpace && !previousCharacterIsSpace;
1877
 
 
1878
 
                    if (!w && autoWrap && tmpW > width)
1879
 
                        fitBelowFloats(tmpW, width);
1880
 
 
1881
 
                    if (autoWrap || breakWords) {
1882
 
                        // If we break only after white-space, consider the current character
1883
 
                        // as candidate width for this line.
1884
 
                        bool lineWasTooWide = false;
1885
 
                        if (w + tmpW <= width && currentCharacterIsWS && o->style()->breakOnlyAfterWhiteSpace() && !midWordBreak) {
1886
 
                            int charWidth = t->width(pos, 1, f, w + tmpW) + (applyWordSpacing ? wordSpacing : 0);
1887
 
                            // Check if line is too big even without the extra space
1888
 
                            // at the end of the line. If it is not, do nothing. 
1889
 
                            // If the line needs the extra whitespace to be too long, 
1890
 
                            // then move the line break to the space and skip all 
1891
 
                            // additional whitespace.
1892
 
                            if (w + tmpW + charWidth > width) {
1893
 
                                lineWasTooWide = true;
1894
 
                                lBreak.obj = o;
1895
 
                                lBreak.pos = pos;
1896
 
                                lBreak.nextBreakablePosition = nextBreakable;
1897
 
                                skipTrailingWhitespace(lBreak);
1898
 
                            }
1899
 
                        }
1900
 
                        if (lineWasTooWide || w + tmpW > width) {
1901
 
                            if (lBreak.obj && shouldPreserveNewline(lBreak.obj) && lBreak.obj->isText() && !static_cast<RenderText*>(lBreak.obj)->isWordBreak() && static_cast<RenderText*>(lBreak.obj)->characters()[lBreak.pos] == '\n') {
1902
 
                                if (!stoppedIgnoringSpaces && pos > 0) {
1903
 
                                    // We need to stop right before the newline and then start up again.
1904
 
                                    addMidpoint(InlineIterator(0, o, pos - 1)); // Stop
1905
 
                                    addMidpoint(InlineIterator(0, o, pos)); // Start
1906
 
                                }
1907
 
                                lBreak.increment();
1908
 
                                previousLineBrokeCleanly = true;
1909
 
                            }
1910
 
                            goto end; // Didn't fit. Jump to the end.
1911
 
                        } else {
1912
 
                            if (!betweenWords || (midWordBreak && !autoWrap))
1913
 
                                tmpW -= additionalTmpW;
1914
 
                            if (pos > 0 && str[pos-1] == softHyphen)
1915
 
                                // Subtract the width of the soft hyphen out since we fit on a line.
1916
 
                                tmpW -= t->width(pos-1, 1, f, w+tmpW);
1917
 
                        }
1918
 
                    }
1919
 
 
1920
 
                    if (c == '\n' && preserveNewline) {
1921
 
                        if (!stoppedIgnoringSpaces && pos > 0) {
1922
 
                            // We need to stop right before the newline and then start up again.
1923
 
                            addMidpoint(InlineIterator(0, o, pos - 1)); // Stop
1924
 
                            addMidpoint(InlineIterator(0, o, pos)); // Start
1925
 
                        }
1926
 
                        lBreak.obj = o;
1927
 
                        lBreak.pos = pos;
1928
 
                        lBreak.nextBreakablePosition = nextBreakable;
1929
 
                        lBreak.increment();
1930
 
                        previousLineBrokeCleanly = true;
1931
 
                        return lBreak;
1932
 
                    }
1933
 
 
1934
 
                    if (autoWrap && betweenWords) {
1935
 
                        w += tmpW;
1936
 
                        wrapW = 0;
1937
 
                        tmpW = 0;
1938
 
                        lBreak.obj = o;
1939
 
                        lBreak.pos = pos;
1940
 
                        lBreak.nextBreakablePosition = nextBreakable;
1941
 
                        // Auto-wrapping text should not wrap in the middle of a word once it has had an
1942
 
                        // opportunity to break after a word.
1943
 
                        breakWords = false;
1944
 
                    }
1945
 
                    
1946
 
                    if (midWordBreak) {
1947
 
                        // Remember this as a breakable position in case
1948
 
                        // adding the end width forces a break.
1949
 
                        lBreak.obj = o;
1950
 
                        lBreak.pos = pos;
1951
 
                        lBreak.nextBreakablePosition = nextBreakable;
1952
 
                        midWordBreak &= (breakWords || breakAll);
1953
 
                    }
1954
 
 
1955
 
                    if (betweenWords) {
1956
 
                        lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
1957
 
                        lastSpace = pos;
1958
 
                    }
1959
 
                    
1960
 
                    if (!ignoringSpaces && o->style()->collapseWhiteSpace()) {
1961
 
                        // If we encounter a newline, or if we encounter a
1962
 
                        // second space, we need to go ahead and break up this
1963
 
                        // run and enter a mode where we start collapsing spaces.
1964
 
                        if (currentCharacterIsSpace && previousCharacterIsSpace) {
1965
 
                            ignoringSpaces = true;
1966
 
                            
1967
 
                            // We just entered a mode where we are ignoring
1968
 
                            // spaces. Create a midpoint to terminate the run
1969
 
                            // before the second space. 
1970
 
                            addMidpoint(ignoreStart);
1971
 
                        }
1972
 
                    }
1973
 
                } else if (ignoringSpaces) {
1974
 
                    // Stop ignoring spaces and begin at this
1975
 
                    // new point.
1976
 
                    ignoringSpaces = false;
1977
 
                    lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
1978
 
                    lastSpace = pos; // e.g., "Foo    goo", don't add in any of the ignored spaces.
1979
 
                    addMidpoint(InlineIterator(0, o, pos));
1980
 
                }
1981
 
 
1982
 
                if (currentCharacterIsSpace && !previousCharacterIsSpace) {
1983
 
                    ignoreStart.obj = o;
1984
 
                    ignoreStart.pos = pos;
1985
 
                }
1986
 
 
1987
 
                if (!currentCharacterIsWS && previousCharacterIsWS) {
1988
 
                    if (autoWrap && o->style()->breakOnlyAfterWhiteSpace()) {
1989
 
                        lBreak.obj = o;
1990
 
                        lBreak.pos = pos;
1991
 
                        lBreak.nextBreakablePosition = nextBreakable;
1992
 
                    }
1993
 
                }
1994
 
                
1995
 
                if (collapseWhiteSpace && currentCharacterIsSpace && !ignoringSpaces)
1996
 
                    trailingSpaceObject = o;
1997
 
                else if (!o->style()->collapseWhiteSpace() || !currentCharacterIsSpace)
1998
 
                    trailingSpaceObject = 0;
1999
 
                    
2000
 
                pos++;
2001
 
                len--;
2002
 
                atStart = false;
2003
 
            }
2004
 
 
2005
 
            // IMPORTANT: pos is > length here!
2006
 
            if (!ignoringSpaces)
2007
 
                tmpW += t->width(lastSpace, pos - lastSpace, f, w+tmpW) + lastSpaceWordSpacing;
2008
 
            tmpW += inlineWidth(o, !appliedStartWidth, true);
2009
 
        } else
2010
 
            ASSERT_NOT_REACHED();
2011
 
 
2012
 
        RenderObject* next = bidiNext(this, o);
2013
 
        bool checkForBreak = autoWrap;
2014
 
        if (w && w + tmpW > width && lBreak.obj && currWS == NOWRAP)
2015
 
            checkForBreak = true;
2016
 
        else if (next && o->isText() && next->isText() && !next->isBR()) {
2017
 
            if (autoWrap || (next->style()->autoWrap())) {
2018
 
                if (currentCharacterIsSpace)
2019
 
                    checkForBreak = true;
2020
 
                else {
2021
 
                    checkForBreak = false;
2022
 
                    RenderText* nextText = static_cast<RenderText*>(next);
2023
 
                    if (nextText->textLength()) {
2024
 
                        UChar c = nextText->characters()[0];
2025
 
                        if (c == ' ' || c == '\t' || (c == '\n' && !shouldPreserveNewline(next)))
2026
 
                            // If the next item on the line is text, and if we did not end with
2027
 
                            // a space, then the next text run continues our word (and so it needs to
2028
 
                            // keep adding to |tmpW|.  Just update and continue.
2029
 
                            checkForBreak = true;
2030
 
                    } else if (nextText->isWordBreak())
2031
 
                        checkForBreak = true;
2032
 
                    bool willFitOnLine = w + tmpW <= width;
2033
 
                    if (!willFitOnLine && !w) {
2034
 
                        fitBelowFloats(tmpW, width);
2035
 
                        willFitOnLine = tmpW <= width;
2036
 
                    }
2037
 
                    bool canPlaceOnLine = willFitOnLine || !autoWrapWasEverTrueOnLine;
2038
 
                    if (canPlaceOnLine && checkForBreak) {
2039
 
                        w += tmpW;
2040
 
                        tmpW = 0;
2041
 
                        lBreak.obj = next;
2042
 
                        lBreak.pos = 0;
2043
 
                        lBreak.nextBreakablePosition = -1;
2044
 
                    }
2045
 
                }
2046
 
            }
2047
 
        }
2048
 
 
2049
 
        if (checkForBreak && (w + tmpW > width)) {
2050
 
            // if we have floats, try to get below them.
2051
 
            if (currentCharacterIsSpace && !ignoringSpaces && o->style()->collapseWhiteSpace())
2052
 
                trailingSpaceObject = 0;
2053
 
 
2054
 
            if (w)
2055
 
                goto end;
2056
 
 
2057
 
            fitBelowFloats(tmpW, width);
2058
 
 
2059
 
            // |width| may have been adjusted because we got shoved down past a float (thus
2060
 
            // giving us more room), so we need to retest, and only jump to
2061
 
            // the end label if we still don't fit on the line. -dwh
2062
 
            if (w + tmpW > width)
2063
 
                goto end;
2064
 
        }
2065
 
 
2066
 
        if (!o->isFloatingOrPositioned()) {
2067
 
            last = o;
2068
 
            if (last->isReplaced() && autoWrap && (!last->isImage() || allowImagesToBreak) && (!last->isListMarker() || static_cast<RenderListMarker*>(last)->isInside())) {
2069
 
                w += tmpW;
2070
 
                tmpW = 0;
2071
 
                lBreak.obj = next;
2072
 
                lBreak.pos = 0;
2073
 
                lBreak.nextBreakablePosition = -1;
2074
 
            }
2075
 
        }
2076
 
 
2077
 
        o = next;
2078
 
        nextBreakable = -1;
2079
 
 
2080
 
        // Clear out our character space bool, since inline <pre>s don't collapse whitespace
2081
 
        // with adjacent inline normal/nowrap spans.
2082
 
        if (!collapseWhiteSpace)
2083
 
            currentCharacterIsSpace = false;
2084
 
        
2085
 
        pos = 0;
2086
 
        atStart = false;
2087
 
    }
2088
 
 
2089
 
    
2090
 
    if (w + tmpW <= width || lastWS == NOWRAP) {
2091
 
        lBreak.obj = 0;
2092
 
        lBreak.pos = 0;
2093
 
        lBreak.nextBreakablePosition = -1;
2094
 
    }
2095
 
 
2096
 
 end:
2097
 
 
2098
 
    if (lBreak == resolver.position() && !lBreak.obj->isBR()) {
2099
 
        // we just add as much as possible
2100
 
        if (style()->whiteSpace() == PRE) {
2101
 
            // FIXME: Don't really understand this case.
2102
 
            if (pos != 0) {
2103
 
                lBreak.obj = o;
2104
 
                lBreak.pos = pos - 1;
2105
 
            } else {
2106
 
                lBreak.obj = last;
2107
 
                lBreak.pos = last->isText() ? last->length() : 0;
2108
 
                lBreak.nextBreakablePosition = -1;
2109
 
            }
2110
 
        } else if (lBreak.obj) {
2111
 
            if (last != o && !last->isListMarker()) {
2112
 
                // better to break between object boundaries than in the middle of a word (except for list markers)
2113
 
                lBreak.obj = o;
2114
 
                lBreak.pos = 0;
2115
 
                lBreak.nextBreakablePosition = -1;
2116
 
            } else {
2117
 
                // Don't ever break in the middle of a word if we can help it.
2118
 
                // There's no room at all. We just have to be on this line,
2119
 
                // even though we'll spill out.
2120
 
                lBreak.obj = o;
2121
 
                lBreak.pos = pos;
2122
 
                lBreak.nextBreakablePosition = -1;
2123
 
            }
2124
 
        }
2125
 
    }
2126
 
 
2127
 
    // make sure we consume at least one char/object.
2128
 
    if (lBreak == resolver.position())
2129
 
        lBreak.increment();
2130
 
 
2131
 
    // Sanity check our midpoints.
2132
 
    checkMidpoints(lBreak);
2133
 
        
2134
 
    if (trailingSpaceObject) {
2135
 
        // This object is either going to be part of the last midpoint, or it is going
2136
 
        // to be the actual endpoint.  In both cases we just decrease our pos by 1 level to
2137
 
        // exclude the space, allowing it to - in effect - collapse into the newline.
2138
 
        if (sNumMidpoints%2==1) {
2139
 
            InlineIterator* midpoints = smidpoints->data();
2140
 
            midpoints[sNumMidpoints-1].pos--;
2141
 
        }
2142
 
        //else if (lBreak.pos > 0)
2143
 
        //    lBreak.pos--;
2144
 
        else if (lBreak.obj == 0 && trailingSpaceObject->isText()) {
2145
 
            // Add a new end midpoint that stops right at the very end.
2146
 
            RenderText* text = static_cast<RenderText *>(trailingSpaceObject);
2147
 
            unsigned length = text->textLength();
2148
 
            unsigned pos = length >= 2 ? length - 2 : UINT_MAX;
2149
 
            InlineIterator endMid(0, trailingSpaceObject, pos);
2150
 
            addMidpoint(endMid);
2151
 
        }
2152
 
    }
2153
 
 
2154
 
    // We might have made lBreak an iterator that points past the end
2155
 
    // of the object. Do this adjustment to make it point to the start
2156
 
    // of the next object instead to avoid confusing the rest of the
2157
 
    // code.
2158
 
    if (lBreak.pos > 0) {
2159
 
        lBreak.pos--;
2160
 
        lBreak.increment();
2161
 
    }
2162
 
 
2163
 
    if (lBreak.obj && lBreak.pos >= 2 && lBreak.obj->isText()) {
2164
 
        // For soft hyphens on line breaks, we have to chop out the midpoints that made us
2165
 
        // ignore the hyphen so that it will render at the end of the line.
2166
 
        UChar c = static_cast<RenderText*>(lBreak.obj)->characters()[lBreak.pos-1];
2167
 
        if (c == softHyphen)
2168
 
            chopMidpointsAt(lBreak.obj, lBreak.pos-2);
2169
 
    }
2170
 
    
2171
 
    return lBreak;
2172
 
}
2173
 
 
2174
 
void RenderBlock::checkLinesForOverflow()
2175
 
{
2176
 
    m_overflowWidth = m_width;
2177
 
    for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2178
 
        m_overflowLeft = min(curr->leftOverflow(), m_overflowLeft);
2179
 
        m_overflowTop = min(curr->topOverflow(), m_overflowTop);
2180
 
        m_overflowWidth = max(curr->rightOverflow(), m_overflowWidth);
2181
 
        m_overflowHeight = max(curr->bottomOverflow(), m_overflowHeight);
2182
 
    }
2183
 
}
2184
 
 
2185
 
void RenderBlock::deleteEllipsisLineBoxes()
2186
 
{
2187
 
    for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox())
2188
 
        curr->clearTruncation();
2189
 
}
2190
 
 
2191
 
void RenderBlock::checkLinesForTextOverflow()
2192
 
{
2193
 
    // Determine the width of the ellipsis using the current font.
2194
 
    // FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP) if horizontal ellipsis is "not renderable"
2195
 
    TextRun ellipsisRun(&horizontalEllipsis, 1);
2196
 
    DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1));
2197
 
    const Font& firstLineFont = firstLineStyle()->font();
2198
 
    const Font& font = style()->font();
2199
 
    int firstLineEllipsisWidth = firstLineFont.width(ellipsisRun);
2200
 
    int ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(ellipsisRun);
2201
 
 
2202
 
    // For LTR text truncation, we want to get the right edge of our padding box, and then we want to see
2203
 
    // if the right edge of a line box exceeds that.  For RTL, we use the left edge of the padding box and
2204
 
    // check the left edge of the line box to see if it is less
2205
 
    // Include the scrollbar for overflow blocks, which means we want to use "contentWidth()"
2206
 
    bool ltr = style()->direction() == LTR;
2207
 
    for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2208
 
        int blockEdge = ltr ? rightOffset(curr->yPos()) : leftOffset(curr->yPos());
2209
 
        int lineBoxEdge = ltr ? curr->xPos() + curr->width() : curr->xPos();
2210
 
        if ((ltr && lineBoxEdge > blockEdge) || (!ltr && lineBoxEdge < blockEdge)) {
2211
 
            // This line spills out of our box in the appropriate direction.  Now we need to see if the line
2212
 
            // can be truncated.  In order for truncation to be possible, the line must have sufficient space to
2213
 
            // accommodate our truncation string, and no replaced elements (images, tables) can overlap the ellipsis
2214
 
            // space.
2215
 
            int width = curr == firstRootBox() ? firstLineEllipsisWidth : ellipsisWidth;
2216
 
            if (curr->canAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width))
2217
 
                curr->placeEllipsis(ellipsisStr, ltr, blockEdge, width);
2218
 
        }
2219
 
    }
2220
 
}
2221
 
 
2222
 
}