Rolled in patch by opendarwin.org@mitzpettel.com
[WebKit-https.git] / WebCore / khtml / rendering / bidi.cpp
1 /**
2  * This file is part of the html renderer for KDE.
3  *
4  * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
5  * Copyright (C) 2004 Apple Computer, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  *
22  */
23 #include "bidi.h"
24 #include "break_lines.h"
25 #include "render_block.h"
26 #include "render_text.h"
27 #include "render_arena.h"
28 #include "render_canvas.h"
29 #include "khtmlview.h"
30 #include "xml/dom_docimpl.h"
31
32 #include "kdebug.h"
33 #include "qdatetime.h"
34 #include "qfontmetrics.h"
35
36 #define BIDI_DEBUG 0
37 //#define DEBUG_LINEBREAKS
38
39 using DOM::AtomicString;
40
41 namespace khtml {
42
43
44 // an iterator which goes through a BidiParagraph
45 struct BidiIterator
46 {
47     BidiIterator() : par(0), obj(0), pos(0) {}
48     BidiIterator(RenderBlock *_par, RenderObject *_obj, unsigned int _pos) : par(_par), obj(_obj), pos(_pos) {}
49     
50     void increment( BidiState &bidi );
51     
52     bool atEnd() const;
53     
54     const QChar &current() const;
55     QChar::Direction direction() const;
56     
57     RenderBlock *par;
58     RenderObject *obj;
59     unsigned int pos;
60 };
61
62 struct BidiStatus {
63     BidiStatus() : eor(QChar::DirON), lastStrong(QChar::DirON), last(QChar::DirON) {}
64     
65     QChar::Direction eor;
66     QChar::Direction lastStrong;
67     QChar::Direction last;
68 };
69     
70 struct BidiState {
71     BidiState() : context(0) {}
72     
73     BidiIterator sor;
74     BidiIterator eor;
75     BidiIterator last;
76     BidiIterator current;
77     BidiContext *context;
78     BidiStatus status;
79 };
80
81 // Used to track a list of chained bidi runs.
82 static BidiRun* sFirstBidiRun;
83 static BidiRun* sLastBidiRun;
84 static int sBidiRunCount;
85 static BidiRun* sCompactFirstBidiRun;
86 static BidiRun* sCompactLastBidiRun;
87 static int sCompactBidiRunCount;
88 static bool sBuildingCompactRuns;
89
90 // Midpoint globals.  The goal is not to do any allocation when dealing with
91 // these midpoints, so we just keep an array around and never clear it.  We track
92 // the number of items and position using the two other variables.
93 static QMemArray<BidiIterator> *smidpoints;
94 static uint sNumMidpoints;
95 static uint sCurrMidpoint;
96 static bool betweenMidpoints;
97
98 static bool isLineEmpty = true;
99 static bool previousLineBrokeCleanly = true;
100 static QChar::Direction dir;
101 static bool adjustEmbedding;
102 static bool emptyRun = true;
103 static int numSpaces;
104
105 static void embed( QChar::Direction d, BidiState &bidi );
106 static void appendRun( BidiState &bidi );
107
108 static int getBPMWidth(int childValue, Length cssUnit)
109 {
110     if (cssUnit.type != Variable)
111         return (cssUnit.type == Fixed ? cssUnit.value : childValue);
112     return 0;
113 }
114
115 static int getBorderPaddingMargin(RenderObject* child, bool endOfInline)
116 {
117     RenderStyle* cstyle = child->style();
118     int result = 0;
119     bool leftSide = (cstyle->direction() == LTR) ? !endOfInline : endOfInline;
120     result += getBPMWidth((leftSide ? child->marginLeft() : child->marginRight()),
121                           (leftSide ? cstyle->marginLeft() :
122                            cstyle->marginRight()));
123     result += getBPMWidth((leftSide ? child->paddingLeft() : child->paddingRight()),
124                           (leftSide ? cstyle->paddingLeft() :
125                            cstyle->paddingRight()));
126     result += leftSide ? child->borderLeft() : child->borderRight();
127     return result;
128 }
129
130 static int inlineWidth(RenderObject* child, bool start = true, bool end = true)
131 {
132     int extraWidth = 0;
133     RenderObject* parent = child->parent();
134     while (parent->isInline() && !parent->isInlineBlockOrInlineTable()) {
135         if (start && parent->firstChild() == child)
136             extraWidth += getBorderPaddingMargin(parent, false);
137         if (end && parent->lastChild() == child)
138             extraWidth += getBorderPaddingMargin(parent, true);
139         child = parent;
140         parent = child->parent();
141     }
142     return extraWidth;
143 }
144
145 #ifndef NDEBUG
146 static bool inBidiRunDetach;
147 #endif
148
149 void BidiRun::detach(RenderArena* renderArena)
150 {
151 #ifndef NDEBUG
152     inBidiRunDetach = true;
153 #endif
154     delete this;
155 #ifndef NDEBUG
156     inBidiRunDetach = false;
157 #endif
158
159     // Recover the size left there for us by operator delete and free the memory.
160     renderArena->free(*(size_t *)this, this);
161 }
162
163 void* BidiRun::operator new(size_t sz, RenderArena* renderArena) throw()
164 {
165     return renderArena->allocate(sz);
166 }
167
168 void BidiRun::operator delete(void* ptr, size_t sz)
169 {
170     assert(inBidiRunDetach);
171
172     // Stash size where detach can find it.
173     *(size_t*)ptr = sz;
174 }
175
176 static void deleteBidiRuns(RenderArena* arena)
177 {
178     if (!sFirstBidiRun)
179         return;
180
181     BidiRun* curr = sFirstBidiRun;
182     while (curr) {
183         BidiRun* s = curr->nextRun;
184         curr->detach(arena);
185         curr = s;
186     }
187     
188     sFirstBidiRun = 0;
189     sLastBidiRun = 0;
190     sBidiRunCount = 0;
191 }
192
193 // ---------------------------------------------------------------------
194
195 /* a small helper class used internally to resolve Bidi embedding levels.
196    Each line of text caches the embedding level at the start of the line for faster
197    relayouting
198 */
199 BidiContext::BidiContext(unsigned char l, QChar::Direction e, BidiContext *p, bool o)
200     : level(l) , override(o), dir(e)
201 {
202     parent = p;
203     if(p) {
204         p->ref();
205         basicDir = p->basicDir;
206     } else
207         basicDir = e;
208     count = 0;
209 }
210
211 BidiContext::~BidiContext()
212 {
213     if(parent) parent->deref();
214 }
215
216 void BidiContext::ref() const
217 {
218     count++;
219 }
220
221 void BidiContext::deref() const
222 {
223     count--;
224     if(count <= 0) delete this;
225 }
226
227 // ---------------------------------------------------------------------
228
229 inline bool operator==( const BidiIterator &it1, const BidiIterator &it2 )
230 {
231     if(it1.pos != it2.pos) return false;
232     if(it1.obj != it2.obj) return false;
233     return true;
234 }
235
236 inline bool operator!=( const BidiIterator &it1, const BidiIterator &it2 )
237 {
238     if(it1.pos != it2.pos) return true;
239     if(it1.obj != it2.obj) return true;
240     return false;
241 }
242
243 static inline RenderObject *Bidinext(RenderObject *par, RenderObject *current, BidiState &bidi,
244                                      bool skipInlines = true, bool* endOfInline = 0)
245 {
246     RenderObject *next = 0;
247     bool oldEndOfInline = endOfInline ? *endOfInline : false;
248     if (endOfInline)
249         *endOfInline = false;
250
251     while(current != 0)
252     {
253         //kdDebug( 6040 ) << "current = " << current << endl;
254         if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned()) {
255             next = current->firstChild();
256             if ( next && adjustEmbedding ) {
257                 EUnicodeBidi ub = next->style()->unicodeBidi();
258                 if ( ub != UBNormal && !emptyRun ) {
259                     EDirection dir = next->style()->direction();
260 QChar::Direction d = ( ub == Embed ? ( dir == RTL ? QChar::DirRLE : QChar::DirLRE )
261                                    : ( dir == RTL ? QChar::DirRLO : QChar::DirLRO ) );
262                     embed( d, bidi );
263                 }
264             }
265         }
266         if (!next) {
267             if (!skipInlines && !oldEndOfInline && current->isInlineFlow())
268             {
269                 next = current;
270                 if (endOfInline)
271                     *endOfInline = true;
272                 break;
273             }
274
275             while (current && current != par) {
276                 next = current->nextSibling();
277                 if (next) break;
278                 if ( adjustEmbedding && current->style()->unicodeBidi() != UBNormal && !emptyRun ) {
279                     embed( QChar::DirPDF, bidi );
280                 }
281                 current = current->parent();
282                 if (!skipInlines && current && current != par && current->isInlineFlow()) {
283                     next = current;
284                     if (endOfInline)
285                         *endOfInline = true;
286                     break;
287                 }
288             }
289         }
290
291         if (!next) break;
292
293         if (next->isText() || next->isBR() || next->isFloating() || next->isReplaced() || next->isPositioned()
294             || ((!skipInlines || !next->firstChild()) // Always return EMPTY inlines.
295                 && next->isInlineFlow()))
296             break;
297         current = next;
298     }
299     return next;
300 }
301
302 static RenderObject *first( RenderObject *par, BidiState &bidi, bool skipInlines = true )
303 {
304     if(!par->firstChild()) return 0;
305     RenderObject *o = par->firstChild();
306
307     if (o->isInlineFlow()) {
308         if (skipInlines && o->firstChild())
309             o = Bidinext( par, o, bidi, skipInlines );
310         else
311             return o; // Never skip empty inlines.
312     }
313
314     if (o && !o->isText() && !o->isBR() && !o->isReplaced() && !o->isFloating() && !o->isPositioned())
315         o = Bidinext( par, o, bidi, skipInlines );
316     return o;
317 }
318
319 inline void BidiIterator::increment (BidiState &bidi)
320 {
321     if(!obj) return;
322     if(obj->isText()) {
323         pos++;
324         if(pos >= static_cast<RenderText *>(obj)->stringLength()) {
325             obj = Bidinext( par, obj, bidi );
326             pos = 0;
327         }
328     } else {
329         obj = Bidinext( par, obj, bidi );
330         pos = 0;
331     }
332 }
333
334 inline bool BidiIterator::atEnd() const
335 {
336     if(!obj) return true;
337     return false;
338 }
339
340 const QChar &BidiIterator::current() const
341 {
342     static QChar nullCharacter;
343     
344     if (!obj || !obj->isText())
345       return nullCharacter;
346     
347     RenderText* text = static_cast<RenderText*>(obj);
348     if (!text->text())
349         return nullCharacter;
350     
351     return text->text()[pos];
352 }
353
354 inline QChar::Direction BidiIterator::direction() const
355 {
356     if (!obj)
357         return QChar::DirON;
358     if (obj->isListMarker())
359         return obj->style()->direction() == LTR ? QChar::DirL : QChar::DirR;
360     if (!obj->isText())
361         return QChar::DirON;
362
363     RenderText *renderTxt = static_cast<RenderText *>( obj );
364     if ( pos >= renderTxt->stringLength() )
365         return QChar::DirON;
366         
367     return renderTxt->text()[pos].direction();
368 }
369
370 // -------------------------------------------------------------------------------------------------
371
372 static void addRun(BidiRun* bidiRun)
373 {
374     if (!sFirstBidiRun)
375         sFirstBidiRun = sLastBidiRun = bidiRun;
376     else {
377         sLastBidiRun->nextRun = bidiRun;
378         sLastBidiRun = bidiRun;
379     }
380     sBidiRunCount++;
381     bidiRun->compact = sBuildingCompactRuns;
382
383     // Compute the number of spaces in this run,
384     if (bidiRun->obj && bidiRun->obj->isText()) {
385         RenderText* text = static_cast<RenderText*>(bidiRun->obj);
386         if (text->text()) {
387             for (int i = bidiRun->start; i < bidiRun->stop; i++) {
388                 const QChar c = text->text()[i];
389                 if (c == ' ' || c == '\n')
390                     numSpaces++;
391             }
392         }
393     }
394 }
395
396 static void reverseRuns(int start, int end)
397 {
398     if (start >= end)
399         return;
400
401     assert(start >= 0 && end < sBidiRunCount);
402     
403     // Get the item before the start of the runs to reverse and put it in
404     // |beforeStart|.  |curr| should point to the first run to reverse.
405     BidiRun* curr = sFirstBidiRun;
406     BidiRun* beforeStart = 0;
407     int i = 0;
408     while (i < start) {
409         i++;
410         beforeStart = curr;
411         curr = curr->nextRun;
412     }
413
414     BidiRun* startRun = curr;
415     while (i < end) {
416         i++;
417         curr = curr->nextRun;
418     }
419     BidiRun* endRun = curr;
420     BidiRun* afterEnd = curr->nextRun;
421
422     i = start;
423     curr = startRun;
424     BidiRun* newNext = afterEnd;
425     while (i <= end) {
426         // Do the reversal.
427         BidiRun* next = curr->nextRun;
428         curr->nextRun = newNext;
429         newNext = curr;
430         curr = next;
431         i++;
432     }
433
434     // Now hook up beforeStart and afterEnd to the newStart and newEnd.
435     if (beforeStart)
436         beforeStart->nextRun = endRun;
437     else
438         sFirstBidiRun = endRun;
439
440     startRun->nextRun = afterEnd;
441     if (!afterEnd)
442         sLastBidiRun = startRun;
443 }
444
445 static void chopMidpointsAt(RenderObject* obj, uint pos)
446 {
447     if (!sNumMidpoints) return;
448     BidiIterator* midpoints = smidpoints->data();
449     for (uint i = 0; i < sNumMidpoints; i++) {
450         const BidiIterator& point = midpoints[i];
451         if (point.obj == obj && point.pos == pos) {
452             sNumMidpoints = i;
453             break;
454         }
455     }
456 }
457
458 static void checkMidpoints(BidiIterator& lBreak, BidiState &bidi)
459 {
460     // Check to see if our last midpoint is a start point beyond the line break.  If so,
461     // shave it off the list, and shave off a trailing space if the previous end point isn't
462     // white-space: pre.
463     if (lBreak.obj && sNumMidpoints && sNumMidpoints%2 == 0) {
464         BidiIterator* midpoints = smidpoints->data();
465         BidiIterator& endpoint = midpoints[sNumMidpoints-2];
466         const BidiIterator& startpoint = midpoints[sNumMidpoints-1];
467         BidiIterator currpoint = endpoint;
468         while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak)
469             currpoint.increment( bidi );
470         if (currpoint == lBreak) {
471             // We hit the line break before the start point.  Shave off the start point.
472             sNumMidpoints--;
473             if (endpoint.obj->style()->whiteSpace() != PRE) {
474                 if (endpoint.obj->isText()) {
475                     // Don't shave a character off the endpoint if it was from a soft hyphen.
476                     RenderText* textObj = static_cast<RenderText*>(endpoint.obj);
477                     if (endpoint.pos+1 < textObj->length() &&
478                         textObj->text()[endpoint.pos+1].unicode() == SOFT_HYPHEN)
479                         return;
480                 }
481                 endpoint.pos--;
482             }
483         }
484     }    
485 }
486
487 static void addMidpoint(const BidiIterator& midpoint)
488 {
489     if (!smidpoints)
490         return;
491
492     if (smidpoints->size() <= sNumMidpoints)
493         smidpoints->resize(sNumMidpoints+10);
494
495     BidiIterator* midpoints = smidpoints->data();
496     midpoints[sNumMidpoints++] = midpoint;
497 }
498
499 static void appendRunsForObject(int start, int end, RenderObject* obj, BidiState &bidi)
500 {
501     if (start > end || obj->isFloating() ||
502         (obj->isPositioned() && !obj->hasStaticX() && !obj->hasStaticY() && !obj->container()->isInlineFlow()))
503         return;
504
505     bool haveNextMidpoint = (smidpoints && sCurrMidpoint < sNumMidpoints);
506     BidiIterator nextMidpoint;
507     if (haveNextMidpoint)
508         nextMidpoint = smidpoints->at(sCurrMidpoint);
509     if (betweenMidpoints) {
510         if (!(haveNextMidpoint && nextMidpoint.obj == obj))
511             return;
512         // This is a new start point. Stop ignoring objects and 
513         // adjust our start.
514         betweenMidpoints = false;
515         start = nextMidpoint.pos;
516         sCurrMidpoint++;
517         if (start < end)
518             return appendRunsForObject(start, end, obj, bidi);
519     }
520     else {
521         if (!smidpoints || !haveNextMidpoint || (obj != nextMidpoint.obj)) {
522             addRun(new (obj->renderArena()) BidiRun(start, end, obj, bidi.context, dir));
523             return;
524         }
525         
526         // An end midpoint has been encountered within our object.  We
527         // need to go ahead and append a run with our endpoint.
528         if (int(nextMidpoint.pos+1) <= end) {
529             betweenMidpoints = true;
530             sCurrMidpoint++;
531             if (nextMidpoint.pos != UINT_MAX) { // UINT_MAX means stop at the object and don't include any of it.
532                 addRun(new (obj->renderArena())
533                     BidiRun(start, nextMidpoint.pos+1, obj, bidi.context, dir));
534                 return appendRunsForObject(nextMidpoint.pos+1, end, obj, bidi);
535             }
536         }
537         else
538            addRun(new (obj->renderArena()) BidiRun(start, end, obj, bidi.context, dir));
539     }
540 }
541
542 static void appendRun( BidiState &bidi )
543 {
544     if (emptyRun || !bidi.eor.obj)
545         return;
546 #if BIDI_DEBUG > 1
547     kdDebug(6041) << "appendRun: dir="<<(int)dir<<endl;
548 #endif
549
550     bool b = adjustEmbedding;
551     adjustEmbedding = false;
552
553     int start = bidi.sor.pos;
554     RenderObject *obj = bidi.sor.obj;
555     while( obj && obj != bidi.eor.obj ) {
556         appendRunsForObject(start, obj->length(), obj, bidi);        
557         start = 0;
558         obj = Bidinext( bidi.sor.par, obj, bidi );
559     }
560     if (obj)
561         appendRunsForObject(start, bidi.eor.pos+1, obj, bidi);
562     
563     bidi.eor.increment( bidi );
564     bidi.sor = bidi.eor;
565     dir = QChar::DirON;
566     bidi.status.eor = QChar::DirON;
567     adjustEmbedding = b;
568 }
569
570 static void embed( QChar::Direction d, BidiState &bidi )
571 {
572 #if BIDI_DEBUG > 1
573     qDebug("*** embed dir=%d emptyrun=%d", d, emptyRun );
574 #endif
575     bool b = adjustEmbedding ;
576     adjustEmbedding = false;
577     if ( d == QChar::DirPDF ) {
578         BidiContext *c = bidi.context->parent;
579         if (c) {
580             if ( bidi.eor != bidi.last ) {
581                 appendRun( bidi );
582                 bidi.eor = bidi.last;
583             }
584             appendRun( bidi );
585             emptyRun = true;
586             bidi.status.last = bidi.context->dir;
587             bidi.context->deref();
588             bidi.context = c;
589             if(bidi.context->override)
590                 dir = bidi.context->dir;
591             else
592                 dir = QChar::DirON;
593             bidi.status.lastStrong = bidi.context->dir;
594         }
595     } else {
596         QChar::Direction runDir;
597         if( d == QChar::DirRLE || d == QChar::DirRLO )
598             runDir = QChar::DirR;
599         else
600             runDir = QChar::DirL;
601         bool override;
602         if( d == QChar::DirLRO || d == QChar::DirRLO )
603             override = true;
604         else
605             override = false;
606
607         unsigned char level = bidi.context->level;
608         if ( runDir == QChar::DirR ) {
609             if(level%2) // we have an odd level
610                 level += 2;
611             else
612                 level++;
613         } else {
614             if(level%2) // we have an odd level
615                 level++;
616             else
617                 level += 2;
618         }
619
620         if(level < 61) {
621             if ( bidi.eor != bidi.last ) {
622                 appendRun( bidi );
623                 bidi.eor = bidi.last;
624             }
625             appendRun( bidi );
626             emptyRun = true;
627
628             bidi.context = new BidiContext(level, runDir, bidi.context, override);
629             bidi.context->ref();
630             dir = runDir;
631             bidi.status.last = runDir;
632             bidi.status.lastStrong = runDir;
633             bidi.status.eor = runDir;
634         }
635     }
636     adjustEmbedding = b;
637 }
638
639 InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj)
640 {
641     // See if we have an unconstructed line box for this object that is also
642     // the last item on the line.
643     KHTMLAssert(obj->isInlineFlow() || obj == this);
644     RenderFlow* flow = static_cast<RenderFlow*>(obj);
645
646     // Get the last box we made for this render object.
647     InlineFlowBox* box = flow->lastLineBox();
648
649     // If this box is constructed then it is from a previous line, and we need
650     // to make a new box for our line.  If this box is unconstructed but it has
651     // something following it on the line, then we know we have to make a new box
652     // as well.  In this situation our inline has actually been split in two on
653     // the same line (this can happen with very fancy language mixtures).
654     if (!box || box->isConstructed() || box->nextOnLine()) {
655         // We need to make a new box for this render object.  Once
656         // made, we need to place it at the end of the current line.
657         InlineBox* newBox = obj->createInlineBox(false, obj == this);
658         KHTMLAssert(newBox->isInlineFlowBox());
659         box = static_cast<InlineFlowBox*>(newBox);
660         box->setFirstLineStyleBit(m_firstLine);
661         
662         // We have a new box. Append it to the inline box we get by constructing our
663         // parent.  If we have hit the block itself, then |box| represents the root
664         // inline box for the line, and it doesn't have to be appended to any parent
665         // inline.
666         if (obj != this) {
667             InlineFlowBox* parentBox = createLineBoxes(obj->parent());
668             parentBox->addToLine(box);
669         }
670     }
671
672     return box;
673 }
674
675 RootInlineBox* RenderBlock::constructLine(const BidiIterator &start, const BidiIterator &end)
676 {
677     if (!sFirstBidiRun)
678         return 0; // We had no runs. Don't make a root inline box at all. The line is empty.
679
680     InlineFlowBox* parentBox = 0;
681     for (BidiRun* r = sFirstBidiRun; r; r = r->nextRun) {
682         // Create a box for our object.
683         r->box = r->obj->createInlineBox(r->obj->isPositioned(), false, sBidiRunCount == 1);
684         if (r->box) {
685             // If we have no parent box yet, or if the run is not simply a sibling,
686             // then we need to construct inline boxes as necessary to properly enclose the
687             // run's inline box.
688             if (!parentBox || (parentBox->object() != r->obj->parent()))
689                 // Create new inline boxes all the way back to the appropriate insertion point.
690                 parentBox = createLineBoxes(r->obj->parent());
691
692             // Append the inline box to this line.
693             parentBox->addToLine(r->box);
694         }
695     }
696
697     // We should have a root inline box.  It should be unconstructed and
698     // be the last continuation of our line list.
699     KHTMLAssert(lastLineBox() && !lastLineBox()->isConstructed());
700
701     // Set bits on our inline flow boxes that indicate which sides should
702     // paint borders/margins/padding.  This knowledge will ultimately be used when
703     // we determine the horizontal positions and widths of all the inline boxes on
704     // the line.
705     RenderObject* endObject = 0;
706     bool lastLine = !end.obj;
707     if (end.obj && end.pos == 0)
708         endObject = end.obj;
709     lastLineBox()->determineSpacingForFlowBoxes(lastLine, endObject);
710
711     // Now mark the line boxes as being constructed.
712     lastLineBox()->setConstructed();
713
714     // Return the last line.
715     return lastRootBox();
716 }
717
718 void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, BidiState &bidi)
719 {
720     // First determine our total width.
721     int availableWidth = lineWidth(m_height);
722     int totWidth = lineBox->getFlowSpacingWidth();
723     BidiRun* r = 0;
724     for (r = sFirstBidiRun; r; r = r->nextRun) {
725         if (!r->box || r->obj->isPositioned())
726             continue; // Positioned objects are only participating to figure out their
727                       // correct static x position.  They have no effect on the width.
728         if (r->obj->isText()) {
729             int textWidth = static_cast<RenderText *>(r->obj)->width(r->start, r->stop-r->start, m_firstLine);
730             if (!r->compact) {
731                 RenderStyle *style = r->obj->style();
732                 if (style->whiteSpace() == NORMAL && style->khtmlLineBreak() == AFTER_WHITE_SPACE) {
733                     // shrink the box as needed to keep the line from overflowing the available width
734                     textWidth = kMin(textWidth, availableWidth - totWidth + style->borderLeftWidth());
735                 }
736             }
737             r->box->setWidth(textWidth);
738         }
739         else if (!r->obj->isInlineFlow()) {
740             r->obj->calcWidth();
741             r->box->setWidth(r->obj->width());
742             if (!r->compact)
743                 totWidth += r->obj->marginLeft() + r->obj->marginRight();
744         }
745
746         // Compacts don't contribute to the width of the line, since they are placed in the margin.
747         if (!r->compact)
748             totWidth += r->box->width();
749     }
750
751     // Armed with the total width of the line (without justification),
752     // we now examine our text-align property in order to determine where to position the
753     // objects horizontally.  The total width of the line can be increased if we end up
754     // justifying text.
755     int x = leftOffset(m_height);
756     switch(style()->textAlign()) {
757         case LEFT:
758         case KHTML_LEFT:
759             // The direction of the block should determine what happens with wide lines.  In
760             // particular with RTL blocks, wide lines should still spill out to the left.
761             if (style()->direction() == RTL && totWidth > availableWidth)
762                 x -= (totWidth - availableWidth);
763             numSpaces = 0;
764             break;
765         case JUSTIFY:
766             if (numSpaces != 0 && !bidi.current.atEnd() && !lineBox->endsWithBreak())
767                 break;
768             // fall through
769         case TAAUTO:
770             numSpaces = 0;
771             // for right to left fall through to right aligned
772             if (bidi.context->basicDir == QChar::DirL)
773                 break;
774         case RIGHT:
775         case KHTML_RIGHT:
776             // Wide lines spill out of the block based off direction.
777             // So even if text-align is right, if direction is LTR, wide lines should overflow out of the right
778             // side of the block.
779             if (style()->direction() == RTL || totWidth < availableWidth)
780                 x += availableWidth - totWidth;
781             numSpaces = 0;
782             break;
783         case CENTER:
784         case KHTML_CENTER:
785             int xd = (availableWidth - totWidth)/2;
786             x += xd >0 ? xd : 0;
787             numSpaces = 0;
788             break;
789     }
790
791     if (numSpaces > 0) {
792         for (r = sFirstBidiRun; r; r = r->nextRun) {
793             if (!r->box) continue;
794
795             int spaceAdd = 0;
796             if (numSpaces > 0 && r->obj->isText() && !r->compact) {
797                 // get the number of spaces in the run
798                 int spaces = 0;
799                 for ( int i = r->start; i < r->stop; i++ ) {
800                     const QChar c = static_cast<RenderText *>(r->obj)->text()[i];
801                     if (c == ' ' || c == '\n')
802                         spaces++;
803                 }
804
805                 KHTMLAssert(spaces <= numSpaces);
806
807                 // Only justify text with white-space: normal.
808                 if (r->obj->style()->whiteSpace() != PRE) {
809                     spaceAdd = (availableWidth - totWidth)*spaces/numSpaces;
810                     static_cast<InlineTextBox*>(r->box)->setSpaceAdd(spaceAdd);
811                     totWidth += spaceAdd;
812                 }
813                 numSpaces -= spaces;
814             }
815         }
816     }
817     
818     // The widths of all runs are now known.  We can now place every inline box (and
819     // compute accurate widths for the inline flow boxes).
820     int leftPosition = x;
821     int rightPosition = x;
822     lineBox->placeBoxesHorizontally(x, leftPosition, rightPosition);
823     lineBox->setHorizontalOverflowPositions(leftPosition, rightPosition);
824 }
825
826 void RenderBlock::computeVerticalPositionsForLine(RootInlineBox* lineBox)
827 {
828     lineBox->verticallyAlignBoxes(m_height);
829     lineBox->setBlockHeight(m_height);
830
831     // See if the line spilled out.  If so set overflow height accordingly.
832     int bottomOfLine = lineBox->bottomOverflow();
833     if (bottomOfLine > m_height && bottomOfLine > m_overflowHeight)
834         m_overflowHeight = bottomOfLine;
835         
836     // Now make sure we place replaced render objects correctly.
837     for (BidiRun* r = sFirstBidiRun; r; r = r->nextRun) {
838         if (!r->box) continue; // Skip runs with no line boxes.
839
840         // Align positioned boxes with the top of the line box.  This is
841         // a reasonable approximation of an appropriate y position.
842         if (r->obj->isPositioned())
843             r->box->setYPos(m_height);
844
845         // Position is used to properly position both replaced elements and
846         // to update the static normal flow x/y of positioned elements.
847         r->obj->position(r->box, r->start, r->stop - r->start, r->level%2);
848     }
849 }
850
851 // collects one line of the paragraph and transforms it to visual order
852 void RenderBlock::bidiReorderLine(const BidiIterator &start, const BidiIterator &end, BidiState &bidi)
853 {
854     if ( start == end ) {
855         if ( start.current() == '\n' ) {
856             m_height += lineHeight( m_firstLine, true );
857         }
858         return;
859     }
860
861 #if BIDI_DEBUG > 1
862     kdDebug(6041) << "reordering Line from " << start.obj << "/" << start.pos << " to " << end.obj << "/" << end.pos << endl;
863 #endif
864
865     sFirstBidiRun = 0;
866     sLastBidiRun = 0;
867     sBidiRunCount = 0;
868     
869     //    context->ref();
870
871     dir = bidi.context->dir;
872
873     emptyRun = true;
874     bidi.eor.obj = 0;
875
876     numSpaces = 0;
877
878     bidi.current = start;
879     bidi.last = bidi.current;
880     bool atEnd = false;
881
882     while( 1 ) {
883
884         QChar::Direction dirCurrent;
885         if (atEnd) {
886             //kdDebug(6041) << "atEnd" << endl;
887             BidiContext *c = bidi.context;
888             if ( bidi.current.atEnd())
889                 while ( c->parent )
890                     c = c->parent;
891             dirCurrent = c->dir;
892         } else {
893             dirCurrent = bidi.current.direction();
894         }
895
896 #ifndef QT_NO_UNICODETABLES
897
898 #if BIDI_DEBUG > 1
899         kdDebug(6041) << "directions: dir=" << (int)dir << " current=" << (int)dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << (int)context->dir << " level =" << (int)context->level << endl;
900 #endif
901
902         switch(dirCurrent) {
903
904             // embedding and overrides (X1-X9 in the Bidi specs)
905         case QChar::DirRLE:
906         case QChar::DirLRE:
907         case QChar::DirRLO:
908         case QChar::DirLRO:
909         case QChar::DirPDF:
910             embed( dirCurrent, bidi );
911             break;
912
913             // strong types
914         case QChar::DirL:
915             if(dir == QChar::DirON)
916                 dir = QChar::DirL;
917             switch(bidi.status.last)
918                 {
919                 case QChar::DirR:
920                 case QChar::DirAL:
921                 case QChar::DirEN:
922                 case QChar::DirAN:
923                     // to avoid many unncessary splits, the following appendRun can
924                     // be done only if (bidi.status.last != QChar::DirEN || bidi.status.lastStrong != QChar::DirL )
925                     appendRun( bidi );
926                     dir = QChar::DirL;
927                     // fall through
928                 case QChar::DirL:
929                     bidi.eor = bidi.current;
930                     bidi.status.eor = QChar::DirL;
931                     break;
932                 case QChar::DirES:
933                 case QChar::DirET:
934                 case QChar::DirCS:
935                 case QChar::DirBN:
936                 case QChar::DirB:
937                 case QChar::DirS:
938                 case QChar::DirWS:
939                 case QChar::DirON:
940                     if (bidi.status.eor == QChar::DirEN) {
941                         if (bidi.status.lastStrong != QChar::DirL) {
942                             // the numbers need to be on a higher embedding level, so let's close that run
943                             dir = QChar::DirEN;
944                             appendRun(bidi);
945                             if (bidi.context->dir != QChar::DirL) {
946                                 // the neutrals take the embedding direction, which is R
947                                 bidi.eor = bidi.last;
948                                 dir = QChar::DirR;
949                                 appendRun(bidi);
950                             }
951                         }
952                     } else if (bidi.status.eor == QChar::DirAN) {
953                         // Arabic numbers are always on a higher embedding level, so let's close that run
954                         dir = QChar::DirAN;
955                         appendRun(bidi);
956                         if (bidi.context->dir != QChar::DirL) {
957                             // the neutrals take the embedding direction, which is R
958                             bidi.eor = bidi.last;
959                             dir = QChar::DirR;
960                             appendRun(bidi);
961                         }
962                     } else if(bidi.status.eor != QChar::DirL) {
963                         //last stuff takes embedding dir
964                         if(bidi.context->dir == QChar::DirL || bidi.status.lastStrong == QChar::DirL) { 
965                             if (bidi.status.eor != QChar::DirON) 
966                             appendRun( bidi );
967                         } else {
968                             dir = QChar::DirR; 
969                             bidi.eor = bidi.last; 
970                             appendRun( bidi ); 
971                         }
972                     }
973                     bidi.eor = bidi.current;
974                     bidi.status.eor = QChar::DirL;
975                     dir = QChar::DirL;
976                 default:
977                     break;
978                 }
979             bidi.status.lastStrong = QChar::DirL;
980             break;
981         case QChar::DirAL:
982         case QChar::DirR:
983             if(dir == QChar::DirON) dir = QChar::DirR;
984             switch(bidi.status.last)
985                 {
986                 case QChar::DirR:
987                 case QChar::DirAL:
988                     bidi.eor = bidi.current; bidi.status.eor = QChar::DirR; break;
989                 case QChar::DirL:
990                 case QChar::DirEN:
991                 case QChar::DirAN:
992                     appendRun( bidi );
993                     dir = QChar::DirR;
994                     bidi.eor = bidi.current;
995                     bidi.status.eor = QChar::DirR;
996                     break;
997                 case QChar::DirES:
998                 case QChar::DirET:
999                 case QChar::DirCS:
1000                 case QChar::DirBN:
1001                 case QChar::DirB:
1002                 case QChar::DirS:
1003                 case QChar::DirWS:
1004                 case QChar::DirON:
1005                     if( !(bidi.status.eor == QChar::DirR) && !(bidi.status.eor == QChar::DirAL) ) {
1006                         //last stuff takes embedding dir
1007                         if(bidi.context->dir == QChar::DirR || bidi.status.lastStrong == QChar::DirR 
1008                             || bidi.status.lastStrong == QChar::DirAL) { 
1009                             appendRun( bidi );
1010                             dir = QChar::DirR;
1011                             bidi.eor = bidi.current;
1012                             bidi.status.eor = QChar::DirR;
1013                         } else {
1014                             dir = QChar::DirL; 
1015                             bidi.eor = bidi.last;
1016                             appendRun( bidi );
1017                             dir = QChar::DirR;
1018                             bidi.status.eor = QChar::DirR;
1019                         }
1020                     } else {
1021                         bidi.eor = bidi.current; bidi.status.eor = QChar::DirR;
1022                     }
1023                 default:
1024                     break;
1025                 }
1026             bidi.status.lastStrong = dirCurrent;
1027             break;
1028
1029             // weak types:
1030
1031         case QChar::DirNSM:
1032             // ### if @sor, set dir to dirSor
1033             break;
1034         case QChar::DirEN:
1035             if(!(bidi.status.lastStrong == QChar::DirAL)) {
1036                 // if last strong was AL change EN to AN
1037                 if(dir == QChar::DirON) {
1038                         dir = QChar::DirL;
1039                 }
1040                 switch(bidi.status.last)
1041                     {
1042                     case QChar::DirET:
1043                         if ( bidi.status.lastStrong == QChar::DirR || bidi.status.lastStrong == QChar::DirAL ) {
1044                             appendRun( bidi );
1045                             dir = QChar::DirEN;
1046                             bidi.status.eor = QChar::DirEN;
1047                         }
1048                         // fall through
1049                     case QChar::DirEN:
1050                     case QChar::DirL:
1051                         bidi.eor = bidi.current;
1052                         bidi.status.eor = dirCurrent;
1053                         break;
1054                     case QChar::DirR:
1055                     case QChar::DirAL:
1056                     case QChar::DirAN:
1057                         appendRun( bidi );
1058                         bidi.status.eor = QChar::DirEN;
1059                         dir = QChar::DirEN; break;
1060                     case QChar::DirES:
1061                     case QChar::DirCS:
1062                         if(bidi.status.eor == QChar::DirEN) {
1063                             bidi.eor = bidi.current; break;
1064                         }
1065                     case QChar::DirBN:
1066                     case QChar::DirB:
1067                     case QChar::DirS:
1068                     case QChar::DirWS:
1069                     case QChar::DirON:
1070                         if(bidi.status.eor == QChar::DirR) {
1071                             // neutrals go to R
1072                             bidi.eor = bidi.last;
1073                             appendRun( bidi );
1074                             dir = QChar::DirEN;
1075                             bidi.status.eor = QChar::DirEN;
1076                         }
1077                         else if( bidi.status.eor == QChar::DirL ||
1078                                  (bidi.status.eor == QChar::DirEN && bidi.status.lastStrong == QChar::DirL)) {
1079                             bidi.eor = bidi.current; bidi.status.eor = dirCurrent;
1080                         } else {
1081                             // numbers on both sides, neutrals get right to left direction
1082                             if(dir != QChar::DirL) {
1083                                 appendRun( bidi );
1084                                 bidi.eor = bidi.last;
1085                                 dir = QChar::DirR;
1086                                 appendRun( bidi );
1087                                 dir = QChar::DirEN;
1088                                 bidi.status.eor = QChar::DirEN;
1089                             } else {
1090                                 bidi.eor = bidi.current; bidi.status.eor = dirCurrent;
1091                             }
1092                         }
1093                     default:
1094                         break;
1095                     }
1096                 break;
1097             }
1098         case QChar::DirAN:
1099             dirCurrent = QChar::DirAN;
1100             if(dir == QChar::DirON) dir = QChar::DirAN;
1101             switch(bidi.status.last)
1102                 {
1103                 case QChar::DirL:
1104                 case QChar::DirAN:
1105                     bidi.eor = bidi.current; bidi.status.eor = QChar::DirAN; break;
1106                 case QChar::DirR:
1107                 case QChar::DirAL:
1108                 case QChar::DirEN:
1109                     appendRun( bidi );
1110                     dir = QChar::DirAN; bidi.status.eor = QChar::DirAN;
1111                     break;
1112                 case QChar::DirCS:
1113                     if(bidi.status.eor == QChar::DirAN) {
1114                         bidi.eor = bidi.current; break;
1115                     }
1116                 case QChar::DirES:
1117                 case QChar::DirET:
1118                 case QChar::DirBN:
1119                 case QChar::DirB:
1120                 case QChar::DirS:
1121                 case QChar::DirWS:
1122                 case QChar::DirON:
1123                     if(bidi.status.eor != QChar::DirR && bidi.status.eor != QChar::DirAL) {
1124                         // run of L before neutrals, neutrals take embedding dir (N2)
1125                         if(bidi.context->dir == QChar::DirR || bidi.status.lastStrong == QChar::DirR 
1126                             || bidi.status.lastStrong == QChar::DirAL) { 
1127                             // the embedding direction is R
1128                             // close the L run
1129                             appendRun( bidi );
1130                             // neutrals become an R run
1131                             bidi.eor = bidi.last;
1132                             dir = QChar::DirR;
1133                             appendRun( bidi );
1134                             bidi.eor = bidi.current;
1135                         } else {
1136                             // the embedding direction is L
1137                             // append neutrals to the L run and close it
1138                             dir = QChar::DirL; 
1139                             bidi.eor = bidi.last;
1140                             appendRun(bidi);
1141                         }
1142                     } else {
1143                         bidi.eor = bidi.last;
1144                         appendRun(bidi);
1145                         bidi.eor = bidi.current;
1146                     }
1147                     dir = QChar::DirAN;
1148                     bidi.status.eor = QChar::DirAN;
1149                 default:
1150                     break;
1151                 }
1152             break;
1153         case QChar::DirES:
1154         case QChar::DirCS:
1155             break;
1156         case QChar::DirET:
1157             if(bidi.status.last == QChar::DirEN) {
1158                 dirCurrent = QChar::DirEN;
1159                 bidi.eor = bidi.current; bidi.status.eor = dirCurrent;
1160                 break;
1161             } else if ((bidi.status.eor == QChar::DirR || bidi.status.eor == QChar::DirAL || bidi.status.eor == QChar::DirAN || (bidi.status.eor == QChar::DirEN && bidi.status.lastStrong == QChar::DirR)) && bidi.last!=bidi.current) {
1162                 // most of the time this is unnecessary, but we need to secure the R run in case
1163                 // the ET ends up being neutral and followed by L
1164                 if (bidi.status.last!=QChar::DirET) {
1165                     dir = bidi.status.eor;
1166                     appendRun(bidi);
1167                     bidi.eor = bidi.last;
1168                 }
1169                 bidi.status.eor = QChar::DirR;
1170                 dir = QChar::DirR;
1171                 break;
1172             }
1173             break;
1174
1175         // boundary neutrals should be ignored
1176         case QChar::DirBN:
1177             break;
1178             // neutrals
1179         case QChar::DirB:
1180             // ### what do we do with newline and paragraph seperators that come to here?
1181             break;
1182         case QChar::DirS:
1183             // ### implement rule L1
1184             break;
1185         case QChar::DirWS:
1186             break;
1187         case QChar::DirON:
1188             break;
1189         default:
1190             break;
1191         }
1192
1193         //cout << "     after: dir=" << //        dir << " current=" << dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << context->dir << endl;
1194
1195         if(bidi.current.atEnd()) break;
1196
1197         // set status.last as needed.
1198         switch(dirCurrent)
1199             {
1200             case QChar::DirET:
1201                 if (bidi.status.last != QChar::DirEN)
1202                     bidi.status.last = QChar::DirET;
1203                 break;
1204             case QChar::DirES:
1205             case QChar::DirCS:
1206             case QChar::DirS:
1207             case QChar::DirWS:
1208             case QChar::DirON:
1209                 switch(bidi.status.last)
1210                     {
1211                     case QChar::DirL:
1212                     case QChar::DirR:
1213                     case QChar::DirAL:
1214                     case QChar::DirEN:
1215                     case QChar::DirAN:
1216                         bidi.status.last = dirCurrent;
1217                         break;
1218                     default:
1219                         bidi.status.last = QChar::DirON;
1220                     }
1221                 break;
1222             case QChar::DirNSM:
1223             case QChar::DirBN:
1224                 // ignore these
1225                 break;
1226             case QChar::DirEN:
1227                 // fall through
1228             default:
1229                 bidi.status.last = dirCurrent;
1230             }
1231 #endif
1232
1233         if ( atEnd ) break;
1234         bidi.last = bidi.current;
1235
1236         if ( emptyRun ) {
1237             bidi.sor = bidi.current;
1238             emptyRun = false;
1239         }
1240
1241         // this causes the operator ++ to open and close embedding levels as needed
1242         // for the CSS unicode-bidi property
1243         adjustEmbedding = true;
1244         bidi.current.increment( bidi );
1245         adjustEmbedding = false;
1246
1247         if ( bidi.current == end ) {
1248             if ( emptyRun )
1249                 break;
1250             atEnd = true;
1251         }
1252     }
1253
1254 #if BIDI_DEBUG > 0
1255     kdDebug(6041) << "reached end of line current=" << current.obj << "/" << current.pos
1256                   << ", eor=" << eor.obj << "/" << eor.pos << endl;
1257 #endif
1258     if ( !emptyRun && bidi.sor != bidi.current ) {
1259             bidi.eor = bidi.last;
1260             appendRun( bidi );
1261     }
1262
1263     // reorder line according to run structure...
1264
1265     // first find highest and lowest levels
1266     uchar levelLow = 128;
1267     uchar levelHigh = 0;
1268     BidiRun *r = sFirstBidiRun;
1269     while ( r ) {
1270         if ( r->level > levelHigh )
1271             levelHigh = r->level;
1272         if ( r->level < levelLow )
1273             levelLow = r->level;
1274         r = r->nextRun;
1275     }
1276
1277     // implements reordering of the line (L2 according to Bidi spec):
1278     // L2. From the highest level found in the text to the lowest odd level on each line,
1279     // reverse any contiguous sequence of characters that are at that level or higher.
1280
1281     // reversing is only done up to the lowest odd level
1282     if( !(levelLow%2) ) levelLow++;
1283
1284 #if BIDI_DEBUG > 0
1285     kdDebug(6041) << "lineLow = " << (uint)levelLow << ", lineHigh = " << (uint)levelHigh << endl;
1286     kdDebug(6041) << "logical order is:" << endl;
1287     QPtrListIterator<BidiRun> it2(runs);
1288     BidiRun *r2;
1289     for ( ; (r2 = it2.current()); ++it2 )
1290         kdDebug(6041) << "    " << r2 << "  start=" << r2->start << "  stop=" << r2->stop << "  level=" << (uint)r2->level << endl;
1291 #endif
1292
1293     int count = sBidiRunCount - 1;
1294
1295     // do not reverse for visually ordered web sites
1296     if(!style()->visuallyOrdered()) {
1297         while(levelHigh >= levelLow) {
1298             int i = 0;
1299             BidiRun* currRun = sFirstBidiRun;
1300             while ( i < count ) {
1301                 while(i < count && currRun && currRun->level < levelHigh) {
1302                     i++;
1303                     currRun = currRun->nextRun;
1304                 }
1305                 int start = i;
1306                 while(i <= count && currRun && currRun->level >= levelHigh) {
1307                     i++;
1308                     currRun = currRun->nextRun;
1309                 }
1310                 int end = i-1;
1311                 reverseRuns(start, end);
1312             }
1313             levelHigh--;
1314         }
1315     }
1316
1317 #if BIDI_DEBUG > 0
1318     kdDebug(6041) << "visual order is:" << endl;
1319     for (BidiRun* curr = sFirstRun; curr; curr = curr->nextRun)
1320         kdDebug(6041) << "    " << curr << endl;
1321 #endif
1322 }
1323
1324 static void buildCompactRuns(RenderObject* compactObj, BidiState &bidi)
1325 {
1326     sBuildingCompactRuns = true;
1327     if (!compactObj->isRenderBlock()) {
1328         // Just append a run for our object.
1329         isLineEmpty = false;
1330         addRun(new (compactObj->renderArena()) BidiRun(0, compactObj->length(), compactObj, bidi.context, dir));
1331     }
1332     else {
1333         // Format the compact like it is its own single line.  We build up all the runs for
1334         // the little compact and then reorder them for bidi.
1335         RenderBlock* compactBlock = static_cast<RenderBlock*>(compactObj);
1336         adjustEmbedding = true;
1337         BidiIterator start(compactBlock, first(compactBlock, bidi), 0);
1338         adjustEmbedding = false;
1339         BidiIterator end = start;
1340     
1341         betweenMidpoints = false;
1342         isLineEmpty = true;
1343         previousLineBrokeCleanly = true;
1344         
1345         end = compactBlock->findNextLineBreak(start, bidi);
1346         if (!isLineEmpty)
1347             compactBlock->bidiReorderLine(start, end, bidi);
1348     }
1349     
1350     
1351     sCompactFirstBidiRun = sFirstBidiRun;
1352     sCompactLastBidiRun = sLastBidiRun;
1353     sCompactBidiRunCount = sBidiRunCount;
1354     
1355     sNumMidpoints = 0;
1356     sCurrMidpoint = 0;
1357     betweenMidpoints = false;
1358     sBuildingCompactRuns = false;
1359 }
1360
1361 QRect RenderBlock::layoutInlineChildren(bool relayoutChildren)
1362 {
1363     BidiState bidi;
1364
1365     bool useRepaintRect = false;
1366     QRect repaintRect(0,0,0,0);
1367
1368     m_overflowHeight = 0;
1369     
1370     invalidateVerticalPositions();
1371 #ifdef DEBUG_LAYOUT
1372     QTime qt;
1373     qt.start();
1374     kdDebug( 6040 ) << renderName() << " layoutInlineChildren( " << this <<" )" << endl;
1375 #endif
1376 #if BIDI_DEBUG > 1 || defined( DEBUG_LINEBREAKS )
1377     kdDebug(6041) << " ------- bidi start " << this << " -------" << endl;
1378 #endif
1379     
1380     m_height = borderTop() + paddingTop();
1381     int toAdd = borderBottom() + paddingBottom();
1382     if (includeScrollbarSize())
1383         toAdd += m_layer->horizontalScrollbarHeight();
1384     
1385     // Figure out if we should clear out our line boxes.
1386     // FIXME: Handle resize eventually!
1387     // FIXME: Do something better when floats are present.
1388     bool fullLayout = !firstLineBox() || !firstChild() || selfNeedsLayout() || relayoutChildren || containsFloats();
1389     if (fullLayout)
1390         deleteLineBoxes();
1391         
1392     // Text truncation only kicks in if your overflow isn't visible and your text-overflow-mode isn't
1393     // clip.
1394     // FIXME: CSS3 says that descendants that are clipped must also know how to truncate.  This is insanely
1395     // difficult to figure out (especially in the middle of doing layout), and is really an esoteric pile of nonsense
1396     // anyway, so we won't worry about following the draft here.
1397     bool hasTextOverflow = style()->textOverflow() && hasOverflowClip();
1398     
1399     // Walk all the lines and delete our ellipsis line boxes if they exist.
1400     if (hasTextOverflow)
1401          deleteEllipsisLineBoxes();
1402
1403     int oldLineBottom = lastRootBox() ? lastRootBox()->bottomOverflow() : m_height;
1404     int startLineBottom = 0;
1405
1406     if (firstChild()) {
1407         // layout replaced elements
1408         bool endOfInline = false;
1409         RenderObject *o = first(this, bidi, false);
1410         bool hasFloat = false;
1411         while (o) {
1412             if (o->isReplaced() || o->isFloating() || o->isPositioned()) {
1413                 if (relayoutChildren || o->style()->width().isPercent() || o->style()->height().isPercent())
1414                     o->setChildNeedsLayout(true, false);
1415                 if (o->isPositioned())
1416                     o->containingBlock()->insertPositionedObject(o);
1417                 else {
1418                     if (o->isFloating())
1419                         hasFloat = true;
1420                     else if (fullLayout || o->needsLayout()) // Replaced elements
1421                         o->dirtyLineBoxes(fullLayout);
1422                     o->layoutIfNeeded();
1423                 }
1424             }
1425             else if (o->isText() || (o->isInlineFlow() && !endOfInline)) {
1426                 if (fullLayout || o->selfNeedsLayout())
1427                     o->dirtyLineBoxes(fullLayout);
1428                 o->setNeedsLayout(false);
1429             }
1430             o = Bidinext( this, o, bidi, false, &endOfInline);
1431         }
1432
1433         if (hasFloat)
1434             fullLayout = true; // FIXME: Will need to find a way to optimize floats some day.
1435         
1436         if (fullLayout && !selfNeedsLayout()) {
1437             setNeedsLayout(true, false);  // Mark ourselves as needing a full layout. This way we'll repaint like
1438                                           // we're supposed to.
1439             if (!document()->view()->needsFullRepaint() && m_layer) {
1440                 // Because we waited until we were already inside layout to discover
1441                 // that the block really needed a full layout, we missed our chance to repaint the layer
1442                 // before layout started.  Luckily the layer has cached the repaint rect for its original
1443                 // position and size, and so we can use that to make a repaint happen now.
1444                 RenderCanvas* c = canvas();
1445                 if (c && !c->printingMode())
1446                     c->repaintViewRectangle(m_layer->repaintRect());
1447             }
1448         }
1449
1450         BidiContext *startEmbed;
1451         if( style()->direction() == LTR ) {
1452             startEmbed = new BidiContext( 0, QChar::DirL );
1453             bidi.status.eor = QChar::DirL;
1454         } else {
1455             startEmbed = new BidiContext( 1, QChar::DirR );
1456             bidi.status.eor = QChar::DirR;
1457         }
1458         startEmbed->ref();
1459
1460         bidi.status.lastStrong = startEmbed->dir;
1461         bidi.status.last = startEmbed->dir;
1462         bidi.context = startEmbed;
1463         
1464         if (!smidpoints)
1465             smidpoints = new QMemArray<BidiIterator>;
1466         
1467         sNumMidpoints = 0;
1468         sCurrMidpoint = 0;
1469         sCompactFirstBidiRun = sCompactLastBidiRun = 0;
1470         sCompactBidiRunCount = 0;
1471         
1472         // We want to skip ahead to the first dirty line
1473         BidiIterator start;
1474         RootInlineBox* startLine = determineStartPosition(fullLayout, start, bidi);
1475         
1476         // We also find the first clean line and extract these lines.  We will add them back
1477         // if we determine that we're able to synchronize after handling all our dirty lines.
1478         BidiIterator cleanLineStart;
1479         int endLineYPos;
1480         RootInlineBox* endLine = (fullLayout || !startLine) ? 
1481                                  0 : determineEndPosition(startLine, cleanLineStart, endLineYPos);
1482         if (startLine) {
1483             useRepaintRect = true;
1484             startLineBottom = startLine->bottomOverflow();
1485             repaintRect.setY(kMin(m_height, startLine->topOverflow()));
1486             RenderArena* arena = renderArena();
1487             RootInlineBox* box = startLine;
1488             while (box) {
1489                 RootInlineBox* next = box->nextRootBox();
1490                 box->deleteLine(arena);
1491                 box = next;
1492             }
1493             startLine = 0;
1494         }
1495         
1496         BidiIterator end = start;
1497
1498         bool endLineMatched = false;
1499         while (!end.atEnd()) {
1500             start = end;
1501             if (endLine && (endLineMatched = matchedEndLine(start, cleanLineStart, endLine, endLineYPos)))
1502                 break;
1503
1504             betweenMidpoints = false;
1505             isLineEmpty = true;
1506             if (m_firstLine && firstChild() && firstChild()->isCompact() && firstChild()->isRenderBlock()) {
1507                 buildCompactRuns(firstChild(), bidi);
1508                 start.obj = firstChild()->nextSibling();
1509                 end = start;
1510             }
1511             end = findNextLineBreak(start, bidi);
1512             if( start.atEnd() ) break;
1513             if (!isLineEmpty) {
1514                 bidiReorderLine(start, end, bidi);
1515
1516                 // Now that the runs have been ordered, we create the line boxes.
1517                 // At the same time we figure out where border/padding/margin should be applied for
1518                 // inline flow boxes.
1519                 if (sCompactFirstBidiRun) {
1520                     // We have a compact line sharing this line.  Link the compact runs
1521                     // to our runs to create a single line of runs.
1522                     sCompactLastBidiRun->nextRun = sFirstBidiRun;
1523                     sFirstBidiRun = sCompactFirstBidiRun;
1524                     sBidiRunCount += sCompactBidiRunCount;
1525                 }
1526
1527                 RootInlineBox* lineBox = 0;
1528                 if (sBidiRunCount) {
1529                     lineBox = constructLine(start, end);
1530                     if (lineBox) {
1531                         lineBox->setEndsWithBreak(previousLineBrokeCleanly);
1532                         
1533                         // Now we position all of our text runs horizontally.
1534                         computeHorizontalPositionsForLine(lineBox, bidi);
1535         
1536                         // Now position our text runs vertically.
1537                         computeVerticalPositionsForLine(lineBox);
1538         
1539                         deleteBidiRuns(renderArena());
1540                     }
1541                 }
1542                 
1543                 if (end == start || (!previousLineBrokeCleanly && end.obj && end.obj->style()->whiteSpace() == PRE && end.current() == QChar('\n'))) {
1544                     adjustEmbedding = true;
1545                     end.increment(bidi);
1546                     adjustEmbedding = false;
1547                 }
1548
1549                 if (lineBox)
1550                     lineBox->setLineBreakInfo(end.obj, end.pos);
1551                 
1552                 m_firstLine = false;
1553                 newLine();
1554             }
1555              
1556             sNumMidpoints = 0;
1557             sCurrMidpoint = 0;
1558             sCompactFirstBidiRun = sCompactLastBidiRun = 0;
1559             sCompactBidiRunCount = 0;
1560         }
1561         startEmbed->deref();
1562         //embed->deref();
1563         
1564         if (endLine) {
1565             if (endLineMatched) {
1566                 // Note our current y-position for correct repainting when no lines move.  If no lines move, we still have to
1567                 // repaint up to the maximum of the bottom overflow of the old start line or the bottom overflow of the new last line.
1568                 int currYPos = kMax(startLineBottom, m_height);
1569                 if (lastRootBox())
1570                     currYPos = kMax(currYPos, lastRootBox()->bottomOverflow());
1571                 
1572                 // Attach all the remaining lines, and then adjust their y-positions as needed.
1573                 for (RootInlineBox* line = endLine; line; line = line->nextRootBox())
1574                     line->attachLine();
1575                 
1576                 // Now apply the offset to each line if needed.
1577                 int delta = m_height - endLineYPos;
1578                 if (delta)
1579                     for (RootInlineBox* line = endLine; line; line = line->nextRootBox())
1580                         line->adjustPosition(0, delta);
1581                 m_height = lastRootBox()->blockHeight();
1582                 m_overflowHeight = kMax(m_height, m_overflowHeight);
1583                 int bottomOfLine = lastRootBox()->bottomOverflow();
1584                 if (bottomOfLine > m_height && bottomOfLine > m_overflowHeight)
1585                     m_overflowHeight = bottomOfLine;
1586                 if (delta)
1587                     repaintRect.setHeight(kMax(m_overflowHeight-delta, m_overflowHeight) - repaintRect.y());
1588                 else
1589                     repaintRect.setHeight(currYPos - repaintRect.y());
1590             }
1591             else {
1592                 // Delete all the remaining lines.
1593                 m_overflowHeight = kMax(m_height, m_overflowHeight);
1594                 InlineRunBox* line = endLine;
1595                 RenderArena* arena = renderArena();
1596                 while (line) {
1597                     InlineRunBox* next = line->nextLineBox();
1598                     if (!next)
1599                         repaintRect.setHeight(kMax(m_overflowHeight, line->bottomOverflow()) - repaintRect.y());
1600                     line->deleteLine(arena);
1601                     line = next;
1602                 }
1603             }
1604         }
1605     }
1606
1607     sNumMidpoints = 0;
1608     sCurrMidpoint = 0;
1609
1610     // in case we have a float on the last line, it might not be positioned up to now.
1611     // This has to be done before adding in the bottom border/padding, or the float will
1612     // include the padding incorrectly. -dwh
1613     positionNewFloats();
1614     
1615     // Now add in the bottom border/padding.
1616     m_height += toAdd;
1617
1618     // Always make sure this is at least our height.
1619     m_overflowHeight = kMax(m_height, m_overflowHeight);
1620     
1621     // See if any lines spill out of the block.  If so, we need to update our overflow width.
1622     checkLinesForOverflow();
1623
1624     if (useRepaintRect) {
1625         repaintRect.setWidth(kMax((int)m_width, m_overflowWidth));
1626         if (repaintRect.height() == 0)
1627             repaintRect.setHeight(kMax(oldLineBottom, m_overflowHeight) - repaintRect.y());
1628     }
1629
1630     if (!firstLineBox() && element() && element()->isContentEditable() && element()->rootEditableElement() == element())
1631         m_height += lineHeight(true);
1632
1633     // See if we have any lines that spill out of our block.  If we do, then we will possibly need to
1634     // truncate text.
1635     if (hasTextOverflow)
1636         checkLinesForTextOverflow();
1637
1638     return repaintRect;
1639
1640 #if BIDI_DEBUG > 1
1641     kdDebug(6041) << " ------- bidi end " << this << " -------" << endl;
1642 #endif
1643     //kdDebug() << "RenderBlock::layoutInlineChildren time used " << qt.elapsed() << endl;
1644     //kdDebug(6040) << "height = " << m_height <<endl;
1645 }
1646
1647 RootInlineBox* RenderBlock::determineStartPosition(bool fullLayout, BidiIterator& start, BidiState& bidi)
1648 {
1649     RootInlineBox* curr = 0;
1650     RootInlineBox* last = 0;
1651     RenderObject* startObj = 0;
1652     int pos = 0;
1653     
1654     if (fullLayout) {
1655         // Nuke all our lines.
1656         if (firstRootBox()) {
1657             RenderArena* arena = renderArena();
1658             curr = firstRootBox(); 
1659             while (curr) {
1660                 RootInlineBox* next = curr->nextRootBox();
1661                 curr->deleteLine(arena);
1662                 curr = next;
1663             }
1664             KHTMLAssert(!m_firstLineBox && !m_lastLineBox);
1665         }
1666     }
1667     else {
1668         for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox());
1669         if (curr) {
1670             // We have a dirty line.
1671             if (curr->prevRootBox()) {
1672                 // We have a previous line.
1673                 if (!curr->prevRootBox()->endsWithBreak())
1674                     curr = curr->prevRootBox();  // The previous line didn't break cleanly, so treat it as dirty also.
1675             }
1676         }
1677         else {
1678             // No dirty lines were found.
1679             // If the last line didn't break cleanly, treat it as dirty.
1680             if (lastRootBox() && !lastRootBox()->endsWithBreak())
1681                 curr = lastRootBox();
1682         }
1683         
1684         // If we have no dirty lines, then last is just the last root box.
1685         last = curr ? curr->prevRootBox() : lastRootBox();
1686     }
1687     
1688     m_firstLine = !last;
1689     previousLineBrokeCleanly = !last || last->endsWithBreak();
1690     if (last) {
1691         m_height = last->blockHeight();
1692         int bottomOfLine = last->bottomOverflow();
1693         if (bottomOfLine > m_height && bottomOfLine > m_overflowHeight)
1694             m_overflowHeight = bottomOfLine;
1695         startObj = last->lineBreakObj();
1696         pos = last->lineBreakPos();
1697     }
1698     else
1699         startObj = first(this, bidi, 0);
1700         
1701     adjustEmbedding = true;
1702     start = BidiIterator(this, startObj, pos);
1703     
1704     adjustEmbedding = false;
1705     
1706     return curr;
1707 }
1708
1709 RootInlineBox* RenderBlock::determineEndPosition(RootInlineBox* startLine, BidiIterator& cleanLineStart,
1710                                                  int& yPos)
1711 {
1712     RootInlineBox* last = 0;
1713     if (!startLine)
1714         last = 0;
1715     else {
1716         for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) {
1717             if (curr->isDirty())
1718                 last = 0;
1719             else if (!last)
1720                 last = curr;
1721         }
1722     }
1723     
1724     if (!last)
1725         return 0;
1726     
1727     cleanLineStart = BidiIterator(this, last->prevRootBox()->lineBreakObj(), last->prevRootBox()->lineBreakPos());
1728     yPos = last->prevRootBox()->blockHeight();
1729     
1730     for (RootInlineBox* line = last; line; line = line->nextRootBox())
1731         line->extractLine(); // Disconnect all line boxes from their render objects while preserving
1732                              // their connections to one another.
1733     
1734     return last;
1735 }
1736
1737 bool RenderBlock::matchedEndLine(const BidiIterator& start, const BidiIterator& endLineStart, 
1738                                  RootInlineBox*& endLine, int& endYPos)
1739 {
1740     if (start == endLineStart)
1741         return true; // The common case. All the data we already have is correct.
1742     else {
1743         // The first clean line doesn't match, but we can check a handful of following lines to try
1744         // to match back up.
1745         static int numLines = 8; // The # of lines we're willing to match against.
1746         RootInlineBox* line = endLine;
1747         for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) {
1748             if (line->lineBreakObj() == start.obj && line->lineBreakPos() == start.pos) {
1749                 // We have a match.
1750                 RootInlineBox* result = line->nextRootBox();
1751                                 
1752                 // Set our yPos to be the block height of endLine.
1753                 if (result)
1754                     endYPos = line->blockHeight();
1755                 
1756                 // Now delete the lines that we failed to sync.
1757                 RootInlineBox* boxToDelete = endLine;
1758                 RenderArena* arena = renderArena();
1759                 while (boxToDelete && boxToDelete != result) {
1760                     RootInlineBox* next = boxToDelete->nextRootBox();
1761                     boxToDelete->deleteLine(arena);
1762                     boxToDelete = next;
1763                 }
1764
1765                 endLine = result;
1766                 return result;
1767             }
1768         }
1769     }
1770     return false;
1771 }
1772
1773 static const ushort nonBreakingSpace = 0xa0;
1774
1775 inline bool RenderBlock::skipNonBreakingSpace(BidiIterator &it)
1776 {
1777     if (it.obj->style()->nbspMode() != SPACE || it.current().unicode() != nonBreakingSpace)
1778         return false;
1779  
1780     // Do not skip a non-breaking space if it is the first character
1781     // on the first line of a block.
1782     if (m_firstLine && isLineEmpty)
1783         return false;
1784         
1785     // Do not skip a non-breaking space if it is the first character
1786     // on a line after a clean line break.
1787     if (!m_firstLine && isLineEmpty && previousLineBrokeCleanly)
1788         return false;
1789     
1790     return true;
1791 }
1792
1793 int RenderBlock::skipWhitespace(BidiIterator &it, BidiState &bidi)
1794 {
1795     // FIXME: The entire concept of the skipWhitespace function is flawed, since we really need to be building
1796     // line boxes even for containers that may ultimately collapse away.  Otherwise we'll never get positioned
1797     // elements quite right.  In other words, we need to build this function's work into the normal line
1798     // object iteration process.
1799     int w = lineWidth(m_height);
1800     while (!it.atEnd() && (it.obj->isInlineFlow() || (it.obj->style()->whiteSpace() != PRE && !it.obj->isBR() &&
1801           (it.current() == ' ' || it.current() == '\n' || 
1802            skipNonBreakingSpace(it) || it.obj->isFloatingOrPositioned())))) {
1803         if (it.obj->isFloatingOrPositioned()) {
1804             RenderObject *o = it.obj;
1805             // add to special objects...
1806             if (o->isFloating()) {
1807                 insertFloatingObject(o);
1808                 positionNewFloats();
1809                 w = lineWidth(m_height);
1810             }
1811             else if (o->isPositioned()) {
1812                 // FIXME: The math here is actually not really right.  It's a best-guess approximation that
1813                 // will work for the common cases
1814                 RenderObject* c = o->container();
1815                 if (c->isInlineFlow()) {
1816                     // A relative positioned inline encloses us.  In this case, we also have to determine our
1817                     // position as though we were an inline.  Set |staticX| and |staticY| on the relative positioned
1818                     // inline so that we can obtain the value later.
1819                     c->setStaticX(style()->direction() == LTR ?
1820                                   leftOffset(m_height) : rightOffset(m_height));
1821                     c->setStaticY(m_height);
1822                 }
1823                 
1824                 if (o->hasStaticX()) {
1825                     bool wasInline = o->style()->isOriginalDisplayInlineType();
1826                     if (wasInline)
1827                         o->setStaticX(style()->direction() == LTR ?
1828                                       leftOffset(m_height) :
1829                                       width() - rightOffset(m_height));
1830                     else
1831                         o->setStaticX(style()->direction() == LTR ?
1832                                       borderLeft() + paddingLeft() :
1833                                       borderRight() + paddingRight());
1834                 }
1835                 if (o->hasStaticY())
1836                     o->setStaticY(m_height);
1837             }
1838         }
1839         
1840         adjustEmbedding = true;
1841         it.increment(bidi);
1842         adjustEmbedding = false;
1843     }
1844     return w;
1845 }
1846
1847 BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi)
1848 {
1849     int width = lineWidth(m_height);
1850     int w = 0;
1851     int tmpW = 0;
1852 #ifdef DEBUG_LINEBREAKS
1853     kdDebug(6041) << "findNextLineBreak: line at " << m_height << " line width " << width << endl;
1854     kdDebug(6041) << "sol: " << start.obj << " " << start.pos << endl;
1855 #endif
1856
1857     // eliminate spaces at beginning of line
1858     width = skipWhitespace(start, bidi);
1859     if (start.atEnd())
1860         return start;
1861
1862     // This variable is used only if whitespace isn't set to PRE, and it tells us whether
1863     // or not we are currently ignoring whitespace.
1864     bool ignoringSpaces = false;
1865     BidiIterator ignoreStart;
1866     
1867     // This variable tracks whether the very last character we saw was a space.  We use
1868     // this to detect when we encounter a second space so we know we have to terminate
1869     // a run.
1870     bool currentCharacterIsSpace = false;
1871     bool currentCharacterIsWS = false;
1872     RenderObject* trailingSpaceObject = 0;
1873
1874     BidiIterator lBreak = start;
1875
1876     RenderObject *o = start.obj;
1877     RenderObject *last = o;
1878     int pos = start.pos;
1879
1880     bool prevLineBrokeCleanly = previousLineBrokeCleanly;
1881     previousLineBrokeCleanly = false;
1882     
1883     while( o ) {
1884 #ifdef DEBUG_LINEBREAKS
1885         kdDebug(6041) << "new object "<< o <<" width = " << w <<" tmpw = " << tmpW << endl;
1886 #endif
1887         if(o->isBR()) {
1888             if (w + tmpW <= width) {
1889                 lBreak.obj = o;
1890                 lBreak.pos = 0;
1891                 lBreak.increment(bidi);
1892
1893                 // A <br> always breaks a line, so don't let the line be collapsed
1894                 // away. Also, the space at the end of a line with a <br> does not
1895                 // get collapsed away.  It only does this if the previous line broke
1896                 // cleanly.  Otherwise the <br> has no effect on whether the line is
1897                 // empty or not.
1898                 if (prevLineBrokeCleanly)
1899                     isLineEmpty = false;
1900                 trailingSpaceObject = 0;
1901                 previousLineBrokeCleanly = true;
1902
1903                 if (!isLineEmpty) {
1904                     // only check the clear status for non-empty lines.
1905                     EClear clear = o->style()->clear();
1906                     if(clear != CNONE)
1907                         m_clearStatus = (EClear) (m_clearStatus | clear);
1908                 }
1909             }
1910             goto end;
1911         }
1912         if( o->isFloatingOrPositioned() ) {
1913             // add to special objects...
1914             if(o->isFloating()) {
1915                 insertFloatingObject(o);
1916                 // check if it fits in the current line.
1917                 // If it does, position it now, otherwise, position
1918                 // it after moving to next line (in newLine() func)
1919                 if (o->width()+o->marginLeft()+o->marginRight()+w+tmpW <= width) {
1920                     positionNewFloats();
1921                     width = lineWidth(m_height);
1922                 }
1923             }
1924             else if (o->isPositioned()) {
1925                 // If our original display wasn't an inline type, then we can
1926                 // go ahead and determine our static x position now.
1927                 bool isInlineType = o->style()->isOriginalDisplayInlineType();
1928                 bool needToSetStaticX = o->hasStaticX();
1929                 if (o->hasStaticX() && !isInlineType) {
1930                     o->setStaticX(o->parent()->style()->direction() == LTR ?
1931                                   borderLeft()+paddingLeft() :
1932                                   borderRight()+paddingRight());
1933                     needToSetStaticX = false;
1934                 }
1935
1936                 // If our original display was an INLINE type, then we can go ahead
1937                 // and determine our static y position now.
1938                 bool needToSetStaticY = o->hasStaticY();
1939                 if (o->hasStaticY() && isInlineType) {
1940                     o->setStaticY(m_height);
1941                     needToSetStaticY = false;
1942                 }
1943                 
1944                 bool needToCreateLineBox = needToSetStaticX || needToSetStaticY;
1945                 RenderObject* c = o->container();
1946                 if (c->isInlineFlow() && (!needToSetStaticX || !needToSetStaticY))
1947                     needToCreateLineBox = true;
1948
1949                 // If we're ignoring spaces, we have to stop and include this object and
1950                 // then start ignoring spaces again.
1951                 if (needToCreateLineBox) {
1952                     trailingSpaceObject = 0;
1953                     ignoreStart.obj = o;
1954                     ignoreStart.pos = 0;
1955                     if (ignoringSpaces) {
1956                         addMidpoint(ignoreStart); // Stop ignoring spaces.
1957                         addMidpoint(ignoreStart); // Start ignoring again.
1958                     }
1959                     
1960                 }
1961             }
1962         } else if (o->isInlineFlow()) {
1963             // Only empty inlines matter.  We treat those similarly to replaced elements.
1964             KHTMLAssert(!o->firstChild());
1965             tmpW += o->marginLeft()+o->borderLeft()+o->paddingLeft()+
1966                     o->marginRight()+o->borderRight()+o->paddingRight();
1967         } else if ( o->isReplaced() ) {
1968             EWhiteSpace currWS = o->style()->whiteSpace();
1969             EWhiteSpace lastWS = last->style()->whiteSpace();
1970             
1971             // WinIE marquees have different whitespace characteristics by default when viewed from
1972             // the outside vs. the inside.  Text inside is NOWRAP, and so we altered the marquee's
1973             // style to reflect this, but we now have to get back to the original whitespace value
1974             // for the marquee when checking for line breaking.
1975             if (o->isHTMLMarquee() && o->layer() && o->layer()->marquee())
1976                 currWS = o->layer()->marquee()->whiteSpace();
1977             if (last->isHTMLMarquee() && last->layer() && last->layer()->marquee())
1978                 lastWS = last->layer()->marquee()->whiteSpace();
1979             
1980             // Break on replaced elements if either has normal white-space.
1981             // FIXME: This does not match WinIE, Opera, and Mozilla.  They treat replaced elements
1982             // like characters in a word, and require spaces between the replaced elements in order
1983             // to break.
1984             if (currWS == NORMAL || lastWS == NORMAL) {
1985                 w += tmpW;
1986                 tmpW = 0;
1987                 lBreak.obj = o;
1988                 lBreak.pos = 0;
1989             }
1990
1991             tmpW += o->width()+o->marginLeft()+o->marginRight()+inlineWidth(o);
1992             if (ignoringSpaces) {
1993                 BidiIterator startMid( 0, o, 0 );
1994                 addMidpoint(startMid);
1995             }
1996             isLineEmpty = false;
1997             ignoringSpaces = false;
1998             currentCharacterIsSpace = false;
1999             currentCharacterIsWS = false;
2000             trailingSpaceObject = 0;
2001             
2002             if (o->isListMarker() && o->style()->listStylePosition() == OUTSIDE) {
2003                 // The marker must not have an effect on whitespace at the start
2004                 // of the line.  We start ignoring spaces to make sure that any additional
2005                 // spaces we see will be discarded. 
2006                 //
2007                 // Optimize for a common case. If we can't find whitespace after the list
2008                 // item, then this is all moot. -dwh
2009                 RenderObject* next = Bidinext( start.par, o, bidi );
2010                 if (!m_pre && next && next->isText() && static_cast<RenderText*>(next)->stringLength() > 0) {
2011                     if (static_cast<RenderText*>(next)->text()[0].unicode() == nonBreakingSpace &&
2012                         o->style()->whiteSpace() == NORMAL && o->style()->nbspMode() == SPACE) {
2013                         currentCharacterIsWS = true;
2014                     }
2015                     if (static_cast<RenderText*>(next)->text()[0].unicode() == ' ' ||
2016                         static_cast<RenderText*>(next)->text()[0] == '\n') {
2017                         currentCharacterIsSpace = true;
2018                         currentCharacterIsWS = true;
2019                         ignoringSpaces = true;
2020                         BidiIterator endMid( 0, o, 0 );
2021                         addMidpoint(endMid);
2022                     }
2023                 }
2024             }
2025         } else if ( o->isText() ) {
2026             RenderText *t = static_cast<RenderText *>(o);
2027             int strlen = t->stringLength();
2028             int len = strlen - pos;
2029             QChar *str = t->text();
2030
2031             const Font *f = t->htmlFont( m_firstLine );
2032             // proportional font, needs a bit more work.
2033             int lastSpace = pos;
2034             bool isPre = o->style()->whiteSpace() == PRE;
2035             int wordSpacing = o->style()->wordSpacing();
2036
2037             bool appliedStartWidth = pos > 0; // If the span originated on a previous line,
2038                                               // then assume the start width has been applied.
2039             bool appliedEndWidth = false;
2040
2041             int wrapW = tmpW;
2042
2043             while(len) {
2044                 bool previousCharacterIsSpace = currentCharacterIsSpace;
2045                 bool previousCharacterIsWS = currentCharacterIsWS;
2046                 const QChar c = str[pos];
2047                 currentCharacterIsSpace = c == ' ' || (!isPre && c == '\n');
2048                 
2049                 if (isPre || !currentCharacterIsSpace)
2050                     isLineEmpty = false;
2051                 
2052                 // Check for soft hyphens.  Go ahead and ignore them.
2053                 if (c.unicode() == SOFT_HYPHEN && pos > 0) {
2054                     if (!ignoringSpaces) {
2055                         // Ignore soft hyphens
2056                         BidiIterator endMid(0, o, pos-1);
2057                         addMidpoint(endMid);
2058                         
2059                         // Add the width up to but not including the hyphen.
2060                         tmpW += t->width(lastSpace, pos - lastSpace, f);
2061                         
2062                         // For whitespace normal only, include the hyphen.  We need to ensure it will fit
2063                         // on the line if it shows when we break.
2064                         if (o->style()->whiteSpace() == NORMAL)
2065                             tmpW += t->width(pos, 1, f);
2066                         
2067                         BidiIterator startMid(0, o, pos+1);
2068                         addMidpoint(startMid);
2069                     }
2070                     
2071                     pos++;
2072                     len--;
2073                     lastSpace = pos; // Cheesy hack to prevent adding in widths of the run twice.
2074                     continue;
2075                 }
2076                 
2077                 bool applyWordSpacing = false;
2078                 bool isNormal = o->style()->whiteSpace() == NORMAL;
2079                 bool breakNBSP = isNormal && o->style()->nbspMode() == SPACE;
2080                 bool breakWords = w == 0 && isNormal && o->style()->wordWrap() == BREAK_WORD;
2081
2082                 currentCharacterIsWS = currentCharacterIsSpace || (breakNBSP && c.unicode() == nonBreakingSpace);
2083
2084                 if (breakWords)
2085                     wrapW += t->width(pos, 1, f);
2086                 if ((isPre && c == '\n') || (!isPre && isBreakable(str, pos, strlen, breakNBSP)) || (breakWords && wrapW > width)) {
2087                     if (ignoringSpaces) {
2088                         if (!currentCharacterIsSpace) {
2089                             // Stop ignoring spaces and begin at this
2090                             // new point.
2091                             ignoringSpaces = false;
2092                             lastSpace = pos; // e.g., "Foo    goo", don't add in any of the ignored spaces.
2093                             BidiIterator startMid ( 0, o, pos );
2094                             addMidpoint(startMid);
2095                         } else {
2096                             // Just keep ignoring these spaces.
2097                             pos++;
2098                             len--;
2099                             continue;
2100                         }
2101                     }
2102
2103                     tmpW += t->width(lastSpace, pos - lastSpace, f);
2104                     if (!appliedStartWidth) {
2105                         tmpW += inlineWidth(o, true, false);
2106                         appliedStartWidth = true;
2107                     }
2108                     
2109                     applyWordSpacing = (wordSpacing && currentCharacterIsSpace && !previousCharacterIsSpace &&
2110                         !t->containsOnlyWhitespace(pos+1, strlen-(pos+1)));
2111
2112 #ifdef DEBUG_LINEBREAKS
2113                     kdDebug(6041) << "found space at " << pos << " in string '" << QString( str, strlen ).latin1() << "' adding " << tmpW << " new width = " << w << endl;
2114 #endif
2115                     if ( !isPre && w + tmpW > width && w == 0 ) {
2116                         int fb = nearestFloatBottom(m_height);
2117                         int newLineWidth = lineWidth(fb);
2118                         // See if |tmpW| will fit on the new line.  As long as it does not,
2119                         // keep adjusting our float bottom until we find some room.
2120                         int lastFloatBottom = m_height;
2121                         while (lastFloatBottom < fb && tmpW > newLineWidth) {
2122                             lastFloatBottom = fb;
2123                             fb = nearestFloatBottom(fb);
2124                             newLineWidth = lineWidth(fb);
2125                         }
2126                         
2127                         if(!w && m_height < fb && width < newLineWidth) {
2128                             m_height = fb;
2129                             width = newLineWidth;
2130 #ifdef DEBUG_LINEBREAKS
2131                             kdDebug() << "RenderBlock::findNextLineBreak new position at " << m_height << " newWidth " << width << endl;
2132 #endif
2133                         }
2134                     }
2135         
2136                     if (o->style()->whiteSpace() == NORMAL) {
2137                         // In AFTER_WHITE_SPACE mode, consider the current character
2138                         // as candidate width for this line.
2139                         int charWidth = o->style()->khtmlLineBreak() == AFTER_WHITE_SPACE ? t->width(pos, 1, f) : 0;
2140                         if (w + tmpW + charWidth > width) {
2141                             if (o->style()->khtmlLineBreak() == AFTER_WHITE_SPACE) {
2142                                 // Check if line is too big even without the extra space
2143                                 // at the end of the line. If it is not, do nothing. 
2144                                 // If the line needs the extra whitespace to be too long, 
2145                                 // then move the line break to the space and skip all 
2146                                 // additional whitespace.
2147                                 if (w + tmpW < width) {
2148                                     lBreak.obj = o;
2149                                     lBreak.pos = pos;
2150                                     skipWhitespace(lBreak, bidi);
2151                                 }
2152                             }
2153                             goto end; // Didn't fit. Jump to the end.
2154                         }
2155                         else if (pos > 1 && str[pos-1].unicode() == SOFT_HYPHEN)
2156                             // Subtract the width of the soft hyphen out since we fit on a line.
2157                             tmpW -= t->width(pos-1, 1, f);
2158                     }
2159
2160                     if( *(str+pos) == '\n' && isPre) {
2161                         lBreak.obj = o;
2162                         lBreak.pos = pos;
2163
2164 #ifdef DEBUG_LINEBREAKS
2165                         kdDebug(6041) << "forced break sol: " << start.obj << " " << start.pos << "   end: " << lBreak.obj << " " << lBreak.pos << "   width=" << w << endl;
2166 #endif
2167                         return lBreak;
2168                     }
2169
2170                     if (o->style()->whiteSpace() == NORMAL) {
2171                         w += tmpW;
2172                         tmpW = 0;
2173                         lBreak.obj = o;
2174                         lBreak.pos = pos;
2175                     }
2176                     
2177                     lastSpace = pos;
2178                     
2179                     if (applyWordSpacing)
2180                         w += wordSpacing;
2181                         
2182                     if (!ignoringSpaces && !isPre) {
2183                         // If we encounter a newline, or if we encounter a
2184                         // second space, we need to go ahead and break up this
2185                         // run and enter a mode where we start collapsing spaces.
2186                         if (currentCharacterIsSpace && previousCharacterIsSpace) {
2187                             ignoringSpaces = true;
2188                             
2189                             // We just entered a mode where we are ignoring
2190                             // spaces. Create a midpoint to terminate the run
2191                             // before the second space. 
2192                             addMidpoint(ignoreStart);
2193                             lastSpace = pos;
2194                         }
2195                     }
2196                 }
2197                 else if (ignoringSpaces) {
2198                     // Stop ignoring spaces and begin at this
2199                     // new point.
2200                     ignoringSpaces = false;
2201                     lastSpace = pos; // e.g., "Foo    goo", don't add in any of the ignored spaces.
2202                     BidiIterator startMid ( 0, o, pos );
2203                     addMidpoint(startMid);
2204                 }
2205
2206                 if (currentCharacterIsSpace && !previousCharacterIsSpace) {
2207                     ignoreStart.obj = o;
2208                     ignoreStart.pos = pos;
2209                 }
2210
2211                 if (!currentCharacterIsWS && previousCharacterIsWS) {
2212                     if (o->style()->khtmlLineBreak() == AFTER_WHITE_SPACE && o->style()->whiteSpace() == NORMAL) {
2213                         lBreak.obj = o;
2214                         lBreak.pos = pos;
2215                     }
2216                 }
2217                 
2218                 if (!isPre && currentCharacterIsSpace && !ignoringSpaces)
2219                     trailingSpaceObject = o;
2220                 else if (isPre || !currentCharacterIsSpace)
2221                     trailingSpaceObject = 0;
2222                     
2223                 pos++;
2224                 len--;
2225             }
2226             
2227             // IMPORTANT: pos is > length here!
2228             if (!ignoringSpaces)
2229                 tmpW += t->width(lastSpace, pos - lastSpace, f);
2230             if (!appliedStartWidth)
2231                 tmpW += inlineWidth(o, true, false);
2232             if (!appliedEndWidth)
2233                 tmpW += inlineWidth(o, false, true);
2234         } else
2235             KHTMLAssert( false );
2236
2237         RenderObject* next = Bidinext(start.par, o, bidi);
2238         bool isNormal = o->style()->whiteSpace() == NORMAL;
2239         bool checkForBreak = isNormal;
2240         if (w && w + tmpW > width+1 && lBreak.obj && o->style()->whiteSpace() == NOWRAP)
2241             checkForBreak = true;
2242         else if (next && o->isText() && next->isText() && !next->isBR()) {
2243             if (isNormal || (next->style()->whiteSpace() == NORMAL)) {
2244                 if (currentCharacterIsSpace)
2245                     checkForBreak = true;
2246                 else {
2247                     RenderText* nextText = static_cast<RenderText*>(next);
2248                     int strlen = nextText->stringLength();
2249                     QChar *str = nextText->text();
2250                     if (strlen &&
2251                         ((str[0].unicode() == ' ') ||
2252                             (next->style()->whiteSpace() != PRE && str[0] == '\n')))
2253                         // If the next item on the line is text, and if we did not end with
2254                         // a space, then the next text run continues our word (and so it needs to
2255                         // keep adding to |tmpW|.  Just update and continue.
2256                         checkForBreak = true;
2257                     else
2258                         checkForBreak = false;
2259
2260                     bool canPlaceOnLine = (w + tmpW <= width+1) || !isNormal;
2261                     if (canPlaceOnLine && checkForBreak) {
2262                         w += tmpW;
2263                         tmpW = 0;
2264                         lBreak.obj = next;
2265                         lBreak.pos = 0;
2266                     }
2267                 }
2268             }
2269         }
2270
2271         if (checkForBreak && (w + tmpW > width+1)) {
2272             //kdDebug() << " too wide w=" << w << " tmpW = " << tmpW << " width = " << width << endl;
2273             //kdDebug() << "start=" << start.obj << " current=" << o << endl;
2274             // if we have floats, try to get below them.
2275             if (currentCharacterIsSpace && !ignoringSpaces && o->style()->whiteSpace() != PRE)
2276                 trailingSpaceObject = 0;
2277             
2278             int fb = nearestFloatBottom(m_height);
2279             int newLineWidth = lineWidth(fb);
2280             // See if |tmpW| will fit on the new line.  As long as it does not,
2281             // keep adjusting our float bottom until we find some room.
2282             int lastFloatBottom = m_height;
2283             while (lastFloatBottom < fb && tmpW > newLineWidth) {
2284                 lastFloatBottom = fb;
2285                 fb = nearestFloatBottom(fb);
2286                 newLineWidth = lineWidth(fb);
2287             }            
2288             if( !w && m_height < fb && width < newLineWidth ) {
2289                 m_height = fb;
2290                 width = newLineWidth;
2291 #ifdef DEBUG_LINEBREAKS
2292                 kdDebug() << "RenderBlock::findNextLineBreak new position at " << m_height << " newWidth " << width << endl;
2293 #endif
2294             }
2295
2296             // |width| may have been adjusted because we got shoved down past a float (thus
2297             // giving us more room), so we need to retest, and only jump to
2298             // the end label if we still don't fit on the line. -dwh
2299             if (w + tmpW > width+1)
2300                 goto end;
2301         }
2302
2303         last = o;
2304         o = next;
2305
2306         if (!last->isFloatingOrPositioned() && last->isReplaced() && last->style()->whiteSpace() == NORMAL) {
2307             // Go ahead and add in tmpW.
2308             w += tmpW;
2309             tmpW = 0;
2310             lBreak.obj = o;
2311             lBreak.pos = 0;
2312         }
2313
2314         // Clear out our character space bool, since inline <pre>s don't collapse whitespace
2315         // with adjacent inline normal/nowrap spans.
2316         if (last->style()->whiteSpace() == PRE)
2317             currentCharacterIsSpace = false;
2318         
2319         pos = 0;
2320     }
2321
2322 #ifdef DEBUG_LINEBREAKS
2323     kdDebug( 6041 ) << "end of par, width = " << width << " linewidth = " << w + tmpW << endl;
2324 #endif
2325     if( w + tmpW <= width || (last && last->style()->whiteSpace() == NOWRAP)) {
2326         lBreak.obj = 0;
2327         lBreak.pos = 0;
2328     }
2329
2330  end:
2331
2332     if( lBreak == start && !lBreak.obj->isBR() ) {
2333         // we just add as much as possible
2334         if ( m_pre ) {
2335             if(pos != 0) {
2336                 lBreak.obj = o;
2337                 lBreak.pos = pos - 1;
2338             } else {
2339                 lBreak.obj = last;
2340                 lBreak.pos = last->isText() ? last->length() : 0;
2341             }
2342         } else if( lBreak.obj ) {
2343             if( last != o ) {
2344                 // better to break between object boundaries than in the middle of a word
2345                 lBreak.obj = o;
2346                 lBreak.pos = 0;
2347             } else {
2348                 // Don't ever break in the middle of a word if we can help it.
2349                 // There's no room at all. We just have to be on this line,
2350                 // even though we'll spill out.
2351                 lBreak.obj = o;
2352                 lBreak.pos = pos;
2353             }
2354         }
2355     }
2356
2357     // make sure we consume at least one char/object.
2358     if( lBreak == start )
2359         lBreak.increment(bidi);
2360     
2361 #ifdef DEBUG_LINEBREAKS
2362     kdDebug(6041) << "regular break sol: " << start.obj << " " << start.pos << "   end: " << lBreak.obj << " " << lBreak.pos << "   width=" << w << endl;
2363 #endif
2364
2365     // Sanity check our midpoints.
2366     checkMidpoints(lBreak, bidi);
2367         
2368     if (trailingSpaceObject) {
2369         // This object is either going to be part of the last midpoint, or it is going
2370         // to be the actual endpoint.  In both cases we just decrease our pos by 1 level to
2371         // exclude the space, allowing it to - in effect - collapse into the newline.
2372         if (sNumMidpoints%2==1) {
2373             BidiIterator* midpoints = smidpoints->data();
2374             midpoints[sNumMidpoints-1].pos--;
2375         }
2376         //else if (lBreak.pos > 0)
2377         //    lBreak.pos--;
2378         else if (lBreak.obj == 0 && trailingSpaceObject->isText()) {
2379             // Add a new end midpoint that stops right at the very end.
2380             RenderText* text = static_cast<RenderText *>(trailingSpaceObject);
2381             unsigned pos = text->length() >=2 ? text->length() - 2 : UINT_MAX;
2382             BidiIterator endMid ( 0, trailingSpaceObject, pos );
2383             addMidpoint(endMid);
2384         }
2385     }
2386
2387     // We might have made lBreak an iterator that points past the end
2388     // of the object. Do this adjustment to make it point to the start
2389     // of the next object instead to avoid confusing the rest of the
2390     // code.
2391     if (lBreak.pos > 0) {
2392         lBreak.pos--;
2393         lBreak.increment(bidi);
2394     }
2395
2396     if (lBreak.obj && lBreak.pos >= 2 && lBreak.obj->isText()) {
2397         // For soft hyphens on line breaks, we have to chop out the midpoints that made us
2398         // ignore the hyphen so that it will render at the end of the line.
2399         QChar c = static_cast<RenderText*>(lBreak.obj)->text()[lBreak.pos-1];
2400         if (c.unicode() == SOFT_HYPHEN)
2401             chopMidpointsAt(lBreak.obj, lBreak.pos-2);
2402     }
2403     
2404     return lBreak;
2405 }
2406
2407 void RenderBlock::checkLinesForOverflow()
2408 {
2409     // FIXME: Inline blocks can have overflow.  Need to understand when those objects are present on a line
2410     // and factor that in somehow.
2411     m_overflowWidth = m_width;
2412     for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2413         m_overflowLeft = kMin(curr->leftOverflow(), m_overflowLeft);
2414         m_overflowTop = kMin(curr->topOverflow(), m_overflowTop);
2415         m_overflowWidth = kMax(curr->rightOverflow(), m_overflowWidth);
2416         m_overflowHeight = kMax(curr->bottomOverflow(), m_overflowHeight);
2417     }
2418 }
2419
2420 void RenderBlock::deleteEllipsisLineBoxes()
2421 {
2422     for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox())
2423         curr->clearTruncation();
2424 }
2425
2426 void RenderBlock::checkLinesForTextOverflow()
2427 {
2428     // Determine the width of the ellipsis using the current font.
2429     QChar ellipsis = 0x2026; // FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP) if 0x2026 not renderable
2430     static AtomicString ellipsisStr(ellipsis);
2431     const Font& firstLineFont = style(true)->htmlFont();
2432     const Font& font = style()->htmlFont();
2433     int firstLineEllipsisWidth = firstLineFont.width(&ellipsis, 1, 0);
2434     int ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(&ellipsis, 1, 0);
2435
2436     // For LTR text truncation, we want to get the right edge of our padding box, and then we want to see
2437     // if the right edge of a line box exceeds that.  For RTL, we use the left edge of the padding box and
2438     // check the left edge of the line box to see if it is less
2439     // Include the scrollbar for overflow blocks, which means we want to use "contentWidth()"
2440     bool ltr = style()->direction() == LTR;
2441     for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2442         int blockEdge = ltr ? rightOffset(curr->yPos()) : leftOffset(curr->yPos());
2443         int lineBoxEdge = ltr ? curr->xPos() + curr->width() : curr->xPos();
2444         if ((ltr && lineBoxEdge > blockEdge) || (!ltr && lineBoxEdge < blockEdge)) {
2445             // This line spills out of our box in the appropriate direction.  Now we need to see if the line
2446             // can be truncated.  In order for truncation to be possible, the line must have sufficient space to
2447             // accommodate our truncation string, and no replaced elements (images, tables) can overlap the ellipsis
2448             // space.
2449             int width = curr == firstRootBox() ? firstLineEllipsisWidth : ellipsisWidth;
2450             if (curr->canAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width))
2451                 curr->placeEllipsis(ellipsisStr, ltr, blockEdge, width);
2452         }
2453     }
2454 }
2455
2456 // For --enable-final
2457 #undef BIDI_DEBUG
2458 #undef DEBUG_LINEBREAKS
2459 #undef DEBUG_LAYOUT
2460
2461 }