From d0abc1a165ce7130e802d892e1417617a99a3ba0 Mon Sep 17 00:00:00 2001 From: tpearson Date: Fri, 3 Sep 2010 17:11:27 +0000 Subject: Merge commit 1170303 from the Enterprise branch git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1171389 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- khtml/rendering/bidi.cpp | 279 +++++++++++++++++++------------------- khtml/rendering/bidi.h | 66 +++++++++ khtml/rendering/render_block.cpp | 68 ---------- khtml/rendering/render_line.cpp | 4 +- khtml/rendering/render_object.cpp | 5 + khtml/rendering/render_object.h | 1 + khtml/rendering/render_style.cpp | 5 +- khtml/rendering/render_style.h | 7 + 8 files changed, 222 insertions(+), 213 deletions(-) (limited to 'khtml/rendering') diff --git a/khtml/rendering/bidi.cpp b/khtml/rendering/bidi.cpp index 19de828a0..bd010047a 100644 --- a/khtml/rendering/bidi.cpp +++ b/khtml/rendering/bidi.cpp @@ -44,10 +44,10 @@ namespace khtml { // an iterator which goes through a BidiParagraph struct BidiIterator { - BidiIterator() : par(0), obj(0), pos(0) {} - BidiIterator(RenderBlock *_par, RenderObject *_obj, unsigned int _pos) : par(_par), obj(_obj), pos(_pos) {} + BidiIterator() : par(0), obj(0), pos(0), endOfInline(0) {} + BidiIterator(RenderBlock *_par, RenderObject *_obj, unsigned int _pos, bool eoi=false) : par(_par), obj(_obj), pos(_pos), endOfInline(eoi) {} - void increment( BidiState &bidi ); + void increment( BidiState &bidi, bool skipInlines=true ); bool atEnd() const; @@ -57,6 +57,7 @@ struct BidiIterator RenderBlock *par; RenderObject *obj; unsigned int pos; + bool endOfInline; }; @@ -128,21 +129,6 @@ static int getBorderPaddingMargin(RenderObject* child, bool endOfInline) return result; } -static int inlineWidth(RenderObject* child, bool start = true, bool end = true) -{ - int extraWidth = 0; - RenderObject* parent = child->parent(); - while (parent->isInline() && !parent->isInlineBlockOrInlineTable()) { - if (start && parent->firstChild() == child) - extraWidth += getBorderPaddingMargin(parent, false); - if (end && parent->lastChild() == child) - extraWidth += getBorderPaddingMargin(parent, true); - child = parent; - parent = child->parent(); - } - return extraWidth; -} - #ifndef NDEBUG static bool inBidiRunDetach; #endif @@ -241,15 +227,19 @@ inline bool operator!=( const BidiIterator &it1, const BidiIterator &it2 ) return false; } +// when modifying this function, make sure you check InlineMinMaxIterator::next() as well. static inline RenderObject *Bidinext(RenderObject *par, RenderObject *current, BidiState &bidi, - bool skipInlines = true) + bool skipInlines = true, bool *endOfInline = 0 ) { RenderObject *next = 0; + bool oldEndOfInline = endOfInline ? *endOfInline : false; + if (oldEndOfInline) + *endOfInline = false; while(current != 0) { //kdDebug( 6040 ) << "current = " << current << endl; - if (!current->isFloating() && !current->isReplaced() && !current->isPositioned()) { + if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned()) { next = current->firstChild(); if ( next && adjustEmbedding ) { EUnicodeBidi ub = next->style()->unicodeBidi(); @@ -262,6 +252,12 @@ static inline RenderObject *Bidinext(RenderObject *par, RenderObject *current, B } } if (!next) { + if (!skipInlines && !oldEndOfInline && current->isInlineFlow() && endOfInline) { + next = current; + *endOfInline = true; + break; + } + while (current && current != par) { next = current->nextSibling(); if (next) break; @@ -269,6 +265,11 @@ static inline RenderObject *Bidinext(RenderObject *par, RenderObject *current, B embed( TQChar::DirPDF, bidi ); } current = current->parent(); + if (!skipInlines && current && current != par && current->isInlineFlow() && endOfInline) { + next = current; + *endOfInline = true; + break; + } } } @@ -300,17 +301,17 @@ static RenderObject *first( RenderObject *par, BidiState &bidi, bool skipInlines return o; } -inline void BidiIterator::increment (BidiState &bidi) +inline void BidiIterator::increment (BidiState &bidi, bool skipInlines) { if(!obj) return; if(obj->isText()) { pos++; if(pos >= static_cast(obj)->stringLength()) { - obj = Bidinext( par, obj, bidi ); + obj = Bidinext( par, obj, bidi, skipInlines ); pos = 0; } } else { - obj = Bidinext( par, obj, bidi ); + obj = Bidinext( par, obj, bidi, skipInlines, &endOfInline ); pos = 0; } } @@ -1322,45 +1323,6 @@ void RenderBlock::bidiReorderLine(const BidiIterator &start, const BidiIterator #endif } -#ifdef APPLE_CHANGES // KDE handles compact blocks differently -static void buildCompactRuns(RenderObject* compactObj, BidiState &bidi) -{ - sBuildingCompactRuns = true; - if (!compactObj->isRenderBlock()) { - // Just append a run for our object. - isLineEmpty = false; - addRun(new (compactObj->renderArena()) BidiRun(0, compactObj->length(), compactObj, bidi.context, dir)); - } - else { - // Format the compact like it is its own single line. We build up all the runs for - // the little compact and then reorder them for bidi. - RenderBlock* compactBlock = static_cast(compactObj); - adjustEmbedding = true; - BidiIterator start(compactBlock, first(compactBlock, bidi), 0); - adjustEmbedding = false; - BidiIterator end = start; - - betweenMidpoints = false; - isLineEmpty = true; - previousLineBrokeAtBR = true; - - end = compactBlock->findNextLineBreak(start, bidi); - if (!isLineEmpty) - compactBlock->bidiReorderLine(start, end, bidi); - } - - - sCompactFirstBidiRun = sFirstBidiRun; - sCompactLastBidiRun = sLastBidiRun; - sCompactBidiRunCount = sBidiRunCount; - - sNumMidpoints = 0; - sCurrMidpoint = 0; - betweenMidpoints = false; - sBuildingCompactRuns = false; -} -#endif - void RenderBlock::layoutInlineChildren(bool relayoutChildren, int breakBeforeLine) { BidiState bidi; @@ -1470,13 +1432,6 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, int breakBeforeLin oldStart = start; oldBidi = bidi; } -#ifdef APPLE_CHANGES // KDE handles compact blocks differently - if (m_firstLine && firstChild() && firstChild()->isCompact()) { - buildCompactRuns(firstChild(), bidi); - start.obj = firstChild()->nextSibling(); - end = start; - } -#endif if (lineCount == breakBeforeLine) { m_height = pageTopAfter(oldPos); pagebreakHint = true; @@ -1491,15 +1446,6 @@ redo_linebreak: // At the same time we figure out where border/padding/margin should be applied for // inline flow boxes. -#ifdef APPLE_CHANGES // KDE handles compact blocks differently - if (sCompactFirstBidiRun) { - // We have a compact line sharing this line. Link the compact runs - // to our runs to create a single line of runs. - sCompactLastBidiRun->nextRun = sFirstBidiRun; - sFirstBidiRun = sCompactFirstBidiRun; - sBidiRunCount += sCompactBidiRunCount; - } -#endif if (sBidiRunCount) { InlineFlowBox* lineBox = constructLine(start, end); if (lineBox) { @@ -1698,7 +1644,7 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi } } adjustEmbedding = true; - start.increment(bidi); + start.increment(bidi, false /*skipInlines*/); adjustEmbedding = false; } @@ -1714,6 +1660,11 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi return start; } + // This variable says we have encountered an object after which initial whitespace should be ignored (e.g. InlineFlows at the begining of a line). + // Either we have nothing to do, if there is no whitespace after the object... or we have to enter the ignoringSpaces state. + // This dilemma will be resolved when we have a peek at the next object. + bool checkShouldIgnoreInitialWhitespace = false; + // This variable is used only if whitespace isn't set to PRE, and it tells us whether // or not we are currently ignoring whitespace. bool ignoringSpaces = false; @@ -1727,13 +1678,14 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi BidiIterator lBreak = start; - RenderObject *o = start.obj; - RenderObject *last = o; + InlineMinMaxIterator it(start.par, start.obj, start.endOfInline, false /*skipPositioned*/); + InlineMinMaxIterator lastIt = it; int pos = start.pos; bool prevLineBrokeCleanly = previousLineBrokeAtBR; previousLineBrokeAtBR = false; + RenderObject* o = it.current; while( o ) { #ifdef DEBUG_LINEBREAKS kdDebug(6041) << "new object "<< o <<" width = " << w <<" tmpw = " << tmpW << endl; @@ -1742,6 +1694,7 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi if( w + tmpW <= width ) { lBreak.obj = o; lBreak.pos = 0; + lBreak.endOfInline = it.endOfInline; // A
always breaks a line, so don't let the line be collapsed // away. Also, the space at the end of a line with a
does not @@ -1792,13 +1745,22 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi } } } else if (o->isInlineFlow()) { - // Only empty inlines matter. We treat those similarly to replaced elements. - KHTMLAssert(!o->firstChild()); - tmpW += o->marginLeft()+o->borderLeft()+o->paddingLeft()+ - o->marginRight()+o->borderRight()+o->paddingRight(); + tmpW += getBorderPaddingMargin(o, it.endOfInline); + if (isLineEmpty) isLineEmpty = !tmpW; + if (o->isWordBreak()) { // #### shouldn't be an InlineFlow! + w += tmpW; + tmpW = 0; + lBreak.obj = o; + lBreak.pos = 0; + lBreak.endOfInline = it.endOfInline; + } else if (!it.endOfInline) { + // this is the beginning of the line (other non-initial inline flows are handled directly when + // incrementing the iterator below). We want to skip initial whitespace as much as possible. + checkShouldIgnoreInitialWhitespace = true; + } } else if ( o->isReplaced() || o->isGlyph() ) { EWhiteSpace currWS = o->style()->whiteSpace(); - EWhiteSpace lastWS = last->style()->whiteSpace(); + EWhiteSpace lastWS = lastIt.current->style()->whiteSpace(); // WinIE marquees have different whitespace characteristics by default when viewed from // the outside vs. the inside. Text inside is NOWRAP, and so we altered the marquee's @@ -1806,8 +1768,8 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi // for the marquee when checking for line breaking. if (o->isHTMLMarquee() && o->layer() && o->layer()->marquee()) currWS = o->layer()->marquee()->whiteSpace(); - if (last->isHTMLMarquee() && last->layer() && last->layer()->marquee()) - lastWS = last->layer()->marquee()->whiteSpace(); + if (lastIt.current->isHTMLMarquee() && lastIt.current->layer() && lastIt.current->layer()->marquee()) + lastWS = lastIt.current->layer()->marquee()->whiteSpace(); // Break on replaced elements if either has normal white-space. if (currWS == NORMAL || lastWS == NORMAL) { @@ -1815,9 +1777,10 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi tmpW = 0; lBreak.obj = o; lBreak.pos = 0; + lBreak.endOfInline = false; } - tmpW += o->width()+o->marginLeft()+o->marginRight()+inlineWidth(o); + tmpW += o->width()+o->marginLeft()+o->marginRight(); if (ignoringSpaces) { BidiIterator startMid( 0, o, 0 ); addMidpoint(startMid); @@ -1828,21 +1791,7 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi trailingSpaceObject = 0; if (o->isListMarker() && o->style()->listStylePosition() == OUTSIDE) { - // The marker must not have an effect on whitespace at the start - // of the line. We start ignoring spaces to make sure that any additional - // spaces we see will be discarded. - // - // Optimize for a common case. If we can't find whitespace after the list - // item, then this is all moot. -dwh - RenderObject* next = Bidinext( start.par, o, bidi ); - if (!style()->preserveWS() && next && next->isText() && static_cast(next)->stringLength() > 0 && - (static_cast(next)->text()[0].category() == TQChar::Separator_Space || - static_cast(next)->text()[0] == '\n')) { - currentCharacterIsSpace = true; - ignoringSpaces = true; - BidiIterator endMid( 0, o, 0 ); - addMidpoint(endMid); - } + checkShouldIgnoreInitialWhitespace = true; } } else if ( o->isText() ) { RenderText *t = static_cast(o); @@ -1859,10 +1808,8 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi #ifdef APPLE_CHANGES int wordSpacing = o->style()->wordSpacing(); #endif - bool appliedStartWidth = pos > 0; // If the span originated on a previous line, - // then assume the start width has been applied. - bool appliedEndWidth = false; bool nextIsSoftBreakable = false; + bool checkBreakWord = autoWrap && (o->style()->wordWrap() == WWBREAKWORD); while(len) { bool previousCharacterIsSpace = currentCharacterIsSpace; @@ -1870,6 +1817,8 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi nextIsSoftBreakable = false; const TQChar c = str[pos]; currentCharacterIsSpace = c == ' '; + checkBreakWord &= !w; // only break words when no other breaking opportunity exists earlier + // on the line (even within the text object we are currently processing) if (preserveWS || !currentCharacterIsSpace) isLineEmpty = false; @@ -1921,13 +1870,11 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi } } - if ( (preserveLF && c == '\n') || (autoWrap && (isBreakable( str, pos, strlen ) || isSoftBreakable)) ) { + const bool isbreakablePosition = (preserveLF && c == '\n') || (autoWrap && + (isBreakable( str, pos, strlen ) || isSoftBreakable)); + if ( isbreakablePosition || checkBreakWord ) { tmpW += t->width(lastSpace, pos - lastSpace, f); - if (!appliedStartWidth) { - tmpW += inlineWidth(o, true, false); - appliedStartWidth = true; - } #ifdef APPLE_CHANGES applyWordSpacing = (wordSpacing && currentCharacterIsSpace && !previousCharacterIsSpace && !t->containsOnlyWhitespace(pos+1, strlen-(pos+1))); @@ -1957,9 +1904,14 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi } if (autoWrap) { - if (w+tmpW > width) + if (w+tmpW > width) { + if (checkBreakWord && pos) { + lBreak.obj = o; + lBreak.pos = pos-1; + lBreak.endOfInline = false; + } goto end; - else if ( (pos > 1 && str[pos-1].unicode() == SOFT_HYPHEN) ) + } else if ( (pos > 1 && str[pos-1].unicode() == SOFT_HYPHEN) ) // Subtract the width of the soft hyphen out since we fit on a line. tmpW -= t->width(pos-1, 1, f); } @@ -1967,6 +1919,7 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi if( preserveLF && *(str+pos) == '\n' ) { lBreak.obj = o; lBreak.pos = pos; + lBreak.endOfInline = false; #ifdef DEBUG_LINEBREAKS kdDebug(6041) << "forced break sol: " << start.obj << " " << start.pos << " end: " << lBreak.obj << " " << lBreak.pos << " width=" << w << endl; @@ -1974,11 +1927,12 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi return lBreak; } - if ( autoWrap ) { + if ( autoWrap && isbreakablePosition ) { w += tmpW; tmpW = 0; lBreak.obj = o; lBreak.pos = pos; + lBreak.endOfInline = false; } lastSpace = pos; @@ -2016,31 +1970,63 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi len--; } - // IMPORTANT: pos is > length here! - if (!ignoringSpaces) + if (!ignoringSpaces) { + // We didn't find any space that would be beyond the line |width|. + // Lets add to |tmpW| the remaining width since the last space we found. + // Before we test this new |tmpW| however, we will have to look ahead to check + // if the next object/position can serve as a line breaking opportunity. tmpW += t->width(lastSpace, pos - lastSpace, f); - if (!appliedStartWidth) - tmpW += inlineWidth(o, true, false); - if (!appliedEndWidth) - tmpW += inlineWidth(o, false, true); + if (checkBreakWord && !w && pos && tmpW > width) { + // Avoid doing the costly lookahead for break-word, + // since we know we are allowed to break. + lBreak.obj = o; + lBreak.pos = pos-1; + lBreak.endOfInline = false; + goto end; + } + } } else KHTMLAssert( false ); - RenderObject* next = Bidinext(start.par, o, bidi); - bool autoWrap = o->style()->autoWrap(); + InlineMinMaxIterator savedIt = lastIt; + lastIt = it; + o = it.next(); + + // advance the iterator to the next non-inline-flow + while (o && o->isInlineFlow() && !o->isWordBreak()) { + tmpW += getBorderPaddingMargin(o, it.endOfInline); + if (isLineEmpty) isLineEmpty = !tmpW; + o = it.next(); + } + + if (checkShouldIgnoreInitialWhitespace) { + // Check if we should switch to ignoringSpaces state + if (!style()->preserveWS() && it.current && it.current->isText()) { + const RenderText* rt = static_cast(it.current); + if (rt->stringLength() > 0 && (rt->text()[0].category() == TQChar::Separator_Space || rt->text()[0] == '\n')) { + currentCharacterIsSpace = true; + ignoringSpaces = true; + BidiIterator endMid( 0, lastIt.current, 0 ); + addMidpoint(endMid); + } + } + checkShouldIgnoreInitialWhitespace = false; + } + + bool autoWrap = lastIt.current->style()->autoWrap(); bool checkForBreak = autoWrap; - if (w && w + tmpW > width && lBreak.obj && !o->style()->preserveLF() && !autoWrap) + if (w && w + tmpW > width && lBreak.obj && !lastIt.current->style()->preserveLF() && !autoWrap) checkForBreak = true; - else if (next && o->isText() && next->isText() && !next->isBR()) { - if (autoWrap || next->style()->autoWrap()) { + else if (it.current && lastIt.current->isText() && it.current->isText() && !it.current->isBR()) { + if (autoWrap || it.current->style()->autoWrap()) { if (currentCharacterIsSpace) checkForBreak = true; else { checkForBreak = false; - RenderText* nextText = static_cast(next); + RenderText* nextText = static_cast(it.current); if (nextText->stringLength() != 0) { TQChar c = nextText->text()[0]; - if (c == ' ' || c == '\t' || (c == '\n' && !next->style()->preserveLF())) { + if (c == ' ' || c == '\t' || (c == '\n' && !it.current->style()->preserveLF())) { // If the next item on the line is text, and if we did not end with // a space, then the next text run continues our word (and so it needs to // keep adding to |tmpW|. Just update and continue. @@ -2052,8 +2038,9 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi if (canPlaceOnLine && checkForBreak) { w += tmpW; tmpW = 0; - lBreak.obj = next; + lBreak.obj = it.current; lBreak.pos = 0; + lBreak.endOfInline = it.endOfInline; } } } @@ -2063,7 +2050,7 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi //kdDebug() << " too wide w=" << w << " tmpW = " << tmpW << " width = " << width << endl; //kdDebug() << "start=" << start.obj << " current=" << o << endl; // if we have floats, try to get below them. - if (currentCharacterIsSpace && !ignoringSpaces && !o->style()->preserveWS()) + if (currentCharacterIsSpace && !ignoringSpaces && !lastIt.current->style()->preserveWS()) trailingSpaceObject = 0; int fb = nearestFloatBottom(m_height); @@ -2087,24 +2074,26 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi // |width| may have been adjusted because we got shoved down past a float (thus // giving us more room), so we need to retest, and only jump to // the end label if we still don't fit on the line. -dwh - if (w + tmpW > width) + if (w + tmpW > width) { + it = lastIt; + lastIt = savedIt; + o = it.current; goto end; + } } - last = o; - o = next; - - if (!last->isFloatingOrPositioned() && last->isReplaced() && last->style()->autoWrap()) { + if (!lastIt.current->isFloatingOrPositioned() && lastIt.current->isReplaced() && lastIt.current->style()->autoWrap()) { // Go ahead and add in tmpW. w += tmpW; tmpW = 0; lBreak.obj = o; lBreak.pos = 0; + lBreak.endOfInline = it.endOfInline; } // Clear out our character space bool, since inline
s don't collapse whitespace
         // with adjacent inline normal/nowrap spans.
-        if (last->style()->preserveWS())
+        if (lastIt.current->style()->preserveWS())
             currentCharacterIsSpace = false;
 
         pos = 0;
@@ -2113,9 +2102,10 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi
 #ifdef DEBUG_LINEBREAKS
     kdDebug( 6041 ) << "end of par, width = " << width << " linewidth = " << w + tmpW << endl;
 #endif
-    if( w + tmpW <= width || (last && !last->style()->autoWrap())) {
+    if( w + tmpW <= width || (lastIt.current && !lastIt.current->style()->autoWrap())) {
         lBreak.obj = 0;
         lBreak.pos = 0;
+        lBreak.endOfInline = false;
     }
 
  end:
@@ -2127,21 +2117,25 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi
             if(pos != 0) {
                 lBreak.obj = o;
                 lBreak.pos = pos - 1;
+                lBreak.endOfInline = it.endOfInline;
             } else {
-                lBreak.obj = last;
-                lBreak.pos = last->isText() ? last->length() : 0;
+                lBreak.obj = lastIt.current;
+                lBreak.pos = lastIt.current->isText() ? lastIt.current->length() : 0;
+                lBreak.endOfInline = lastIt.endOfInline;
             }
         } else if( lBreak.obj ) {
-            if( last != o ) {
+            if( lastIt.current != o ) {
                 // better to break between object boundaries than in the middle of a word
                 lBreak.obj = o;
                 lBreak.pos = 0;
+                lBreak.endOfInline = it.endOfInline;
             } else {
                 // Don't ever break in the middle of a word if we can help it.
                 // There's no room at all. We just have to be on this line,
                 // even though we'll spill out.
                 lBreak.obj = o;
                 lBreak.pos = pos;
+                lBreak.endOfInline = it.endOfInline;
             }
         }
     }
@@ -2150,8 +2144,11 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi
         start = posStart;
 
     // make sure we consume at least one char/object.
-    if( lBreak == start )
+    // and avoid returning an InlineFlow
+    // (FIXME: turn those wordbreaks into empty text objects - they shouldn't be inline flows!)
+    if( lBreak == start || (lBreak.obj && lBreak.obj->isInlineFlow() && !lBreak.obj->isWordBreak())) {
         lBreak.increment(bidi);
+    }
 
 #ifdef DEBUG_LINEBREAKS
     kdDebug(6041) << "regular break sol: " << start.obj << " " << start.pos << "   end: " << lBreak.obj << " " << lBreak.pos << "   width=" << w << endl;
diff --git a/khtml/rendering/bidi.h b/khtml/rendering/bidi.h
index 15a9ab906..c8776ce19 100644
--- a/khtml/rendering/bidi.h
+++ b/khtml/rendering/bidi.h
@@ -24,6 +24,7 @@
 #define BIDI_H
 
 #include 
+#include "rendering/render_object.h"
 
 namespace khtml {
     class RenderArena;
@@ -101,6 +102,71 @@ public:
     struct BidiIterator;
     struct BidiState;
 
+    struct InlineMinMaxIterator
+    {
+    /* InlineMinMaxIterator is a class that will iterate over all render objects that contribute to
+       inline min/max width calculations.  Note the following about the way it walks:
+       (1) Positioned content is skipped (since it does not contribute to min/max width of a block)
+       (2) We do not drill into the children of floats or replaced elements, since you can't break
+           in the middle of such an element.
+       (3) Inline flows (e.g., , , ) are walked twice, since each side can have
+           distinct borders/margin/padding that contribute to the min/max width.
+    */
+        RenderObject* parent;
+        RenderObject* current;
+        bool endOfInline;
+        bool skipPositioned;
+        InlineMinMaxIterator(RenderObject* p, RenderObject* o, bool eOI=false, bool skipPos=true)
+            :parent(p), current(o), endOfInline(eOI), skipPositioned(skipPos) {}
+        inline RenderObject* next();
+    };
+
+    inline RenderObject* InlineMinMaxIterator::next()
+    {
+        RenderObject* result = 0;
+        bool oldEndOfInline = endOfInline;
+        endOfInline = false;
+        while (current != 0 || (current == parent))
+        {
+            //kDebug( 6040 ) << "current = " << current;
+            if (!oldEndOfInline &&
+                (current == parent ||
+                (!current->isFloating() && !current->isReplaced() && !current->isPositioned())))
+                result = current->firstChild();
+            if (!result) {
+                // We hit the end of our inline. (It was empty, e.g., .)
+                if (!oldEndOfInline && current->isInlineFlow()) {
+                    result = current;
+                    endOfInline = true;
+                    break;
+                }
+                while (current && current != parent) {
+                    result = current->nextSibling();
+                    if (result) break;
+                    current = current->parent();
+                    if (current && current != parent && current->isInlineFlow()) {
+                        result = current;
+                        endOfInline = true;
+                        break;
+                    }
+                }
+            }
+
+            if (!result) break;
+
+            if ((!skipPositioned || !result->isPositioned()) && (result->isText() || result->isBR() ||
+                result->isFloatingOrPositioned() || result->isReplaced() || result->isGlyph() || result->isInlineFlow()))
+                break;
+
+            current = result;
+            result = 0;
+        }
+
+        // Update our position.
+        current = result;
+        return current;
+    }
+
 }
 
 #endif
diff --git a/khtml/rendering/render_block.cpp b/khtml/rendering/render_block.cpp
index d036b9d09..ccbb6fad0 100644
--- a/khtml/rendering/render_block.cpp
+++ b/khtml/rendering/render_block.cpp
@@ -2623,74 +2623,6 @@ void RenderBlock::calcMinMaxWidth()
     // ### compare with min/max width set in style sheet...
 }
 
-struct InlineMinMaxIterator
-{
-/* InlineMinMaxIterator is a class that will iterate over all render objects that contribute to
-   inline min/max width calculations.  Note the following about the way it walks:
-   (1) Positioned content is skipped (since it does not contribute to min/max width of a block)
-   (2) We do not drill into the children of floats or replaced elements, since you can't break
-       in the middle of such an element.
-   (3) Inline flows (e.g., , , ) are walked twice, since each side can have
-       distinct borders/margin/padding that contribute to the min/max width.
-*/
-    RenderObject* parent;
-    RenderObject* current;
-    bool endOfInline;
-
-    InlineMinMaxIterator(RenderObject* p, RenderObject* o, bool end = false)
-        :parent(p), current(o), endOfInline(end) {}
-
-    RenderObject* next();
-};
-
-RenderObject* InlineMinMaxIterator::next()
-{
-    RenderObject* result = 0;
-    bool oldEndOfInline = endOfInline;
-    endOfInline = false;
-    while (current != 0 || (current == parent))
-    {
-        //kdDebug( 6040 ) << "current = " << current << endl;
-        if (!oldEndOfInline &&
-            (current == parent ||
-             (!current->isFloating() && !current->isReplaced() && !current->isPositioned())))
-            result = current->firstChild();
-        if (!result) {
-            // We hit the end of our inline. (It was empty, e.g., .)
-            if (!oldEndOfInline && current->isInlineFlow()) {
-                result = current;
-                endOfInline = true;
-                break;
-            }
-
-            while (current && current != parent) {
-                result = current->nextSibling();
-                if (result) break;
-                current = current->parent();
-                if (current && current != parent && current->isInlineFlow()) {
-                    result = current;
-                    endOfInline = true;
-                    break;
-                }
-            }
-        }
-
-        if (!result) break;
-
-        if (!result->isPositioned() && (result->isText() || result->isBR() ||
-            result->isFloating() || result->isReplaced() ||
-            result->isInlineFlow()))
-            break;
-
-        current = result;
-        result = 0;
-    }
-
-    // Update our position.
-    current = result;
-    return current;
-}
-
 // bidi.cpp defines the following functions too.
 // Maybe these should not be static, after all...
 
diff --git a/khtml/rendering/render_line.cpp b/khtml/rendering/render_line.cpp
index f3b769e9c..2bcbe366e 100644
--- a/khtml/rendering/render_line.cpp
+++ b/khtml/rendering/render_line.cpp
@@ -271,8 +271,8 @@ bool InlineFlowBox::onEndChain(RenderObject* endObject)
 
     RenderObject* curr = endObject;
     RenderObject* parent = curr->parent();
-    while (parent && !parent->isRenderBlock() || parent == object()) {
-        if (parent->lastChild() != curr)
+    while (parent && !parent->isRenderBlock()) {
+        if (parent->lastChild() != curr || parent == object())
             return false;
 
         curr = parent;
diff --git a/khtml/rendering/render_object.cpp b/khtml/rendering/render_object.cpp
index c1f7fd754..c5ee68720 100644
--- a/khtml/rendering/render_object.cpp
+++ b/khtml/rendering/render_object.cpp
@@ -244,6 +244,11 @@ bool RenderObject::isHR() const
     return element() && element()->id() == ID_HR;
 }
 
+bool RenderObject::isWordBreak() const
+{
+    return element() && element()->id() == ID_WBR;
+}
+
 bool RenderObject::isHTMLMarquee() const
 {
     return element() && element()->renderer() == this && element()->id() == ID_MARQUEE;
diff --git a/khtml/rendering/render_object.h b/khtml/rendering/render_object.h
index 1242f8e87..f7f772387 100644
--- a/khtml/rendering/render_object.h
+++ b/khtml/rendering/render_object.h
@@ -282,6 +282,7 @@ public:
     virtual bool isApplet() const { return false; }
 
     bool isHTMLMarquee() const;
+    bool isWordBreak() const;
 
     bool isAnonymous() const { return m_isAnonymous; }
     void setIsAnonymous(bool b) { m_isAnonymous = b; }
diff --git a/khtml/rendering/render_style.cpp b/khtml/rendering/render_style.cpp
index 18c520ad4..a71dd4116 100644
--- a/khtml/rendering/render_style.cpp
+++ b/khtml/rendering/render_style.cpp
@@ -429,7 +429,7 @@ bool StyleCSS3NonInheritedData::operator==(const StyleCSS3NonInheritedData& o) c
 }
 
 StyleCSS3InheritedData::StyleCSS3InheritedData()
-:Shared(), textShadow(0)
+:Shared(), textShadow(0), wordWrap(RenderStyle::initialWordWrap())
 #ifdef APPLE_CHANGES
 , userModify(READ_ONLY), textSizeAdjust(RenderStyle::initialTextSizeAdjust())
 #endif
@@ -441,6 +441,7 @@ StyleCSS3InheritedData::StyleCSS3InheritedData(const StyleCSS3InheritedData& o)
 :Shared()
 {
     textShadow = o.textShadow ? new ShadowData(*o.textShadow) : 0;
+    wordWrap = o.wordWrap;
 #ifdef APPLE_CHANGES
     userModify = o.userModify;
     textSizeAdjust = o.textSizeAdjust;
@@ -454,7 +455,7 @@ StyleCSS3InheritedData::~StyleCSS3InheritedData()
 
 bool StyleCSS3InheritedData::operator==(const StyleCSS3InheritedData& o) const
 {
-    return shadowDataEquivalent(o)
+    return shadowDataEquivalent(o) && (wordWrap == o.wordWrap)
 #ifdef APPLE_CHANGES
             && (userModify == o.userModify) && (textSizeAdjust == o.textSizeAdjust)
 #endif
diff --git a/khtml/rendering/render_style.h b/khtml/rendering/render_style.h
index b1c1e5306..29b369ca1 100644
--- a/khtml/rendering/render_style.h
+++ b/khtml/rendering/render_style.h
@@ -192,6 +192,9 @@ enum EFloat {
     FNONE = 0, FLEFT = 0x01, FRIGHT = 0x02, FLEFT_ALIGN = 0x05, FRIGHT_ALIGN = 0x06
 };
 
+enum EWordWrap {
+  WWNORMAL = 0, WWBREAKWORD = 0x01
+};
 
 //------------------------------------------------
 // Border attributes. Not inherited.
@@ -702,6 +705,7 @@ class StyleCSS3InheritedData : public Shared
         EUserModify userModify : 2; // Flag used for editing state
         bool textSizeAdjust : 1;    // An Apple extension.  Not really CSS3 but not worth making a new struct over.
 #endif
+        EWordWrap wordWrap : 1;
     private:
         StyleCSS3InheritedData &operator=(const StyleCSS3InheritedData &);
 };
@@ -1195,6 +1199,7 @@ public:
         return background->m_outline._offset;
     }
     ShadowData* textShadow() const { return css3InheritedData->textShadow; }
+    EWordWrap wordWrap() const { return css3InheritedData->wordWrap; }
     float opacity() { return css3NonInheritedData->opacity; }
     EUserInput userInput() const { return inherited_flags.f._user_input; }
 
@@ -1346,6 +1351,7 @@ public:
     // CSS3 Setters
     void setBoxSizing( EBoxSizing b ) { SET_VAR(box,box_sizing,b); }
     void setOutlineOffset(unsigned short v) {  SET_VAR(background,m_outline._offset,v) }
+    void setWordWrap(EWordWrap w) { SET_VAR(css3InheritedData, wordWrap, w); }
     void setTextShadow(ShadowData* val, bool add=false);
     void setOpacity(float f) { SET_VAR(css3NonInheritedData, opacity, f); }
     void setUserInput(EUserInput ui) { inherited_flags.f._user_input = ui; }
@@ -1424,6 +1430,7 @@ public:
     static EDisplay initialDisplay() { return INLINE; }
     static EEmptyCell initialEmptyCells() { return SHOW; }
     static EFloat initialFloating() { return FNONE; }
+    static EWordWrap initialWordWrap() { return WWNORMAL; }
     static EListStylePosition initialListStylePosition() { return OUTSIDE; }
     static EListStyleType initialListStyleType() { return LDISC; }
     static EOverflow initialOverflowX() { return OVISIBLE; }
-- 
cgit v1.2.3