Warning, file /frameworks/khtml/src/rendering/bidi.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /**
0002  * This file is part of the html renderer for KDE.
0003  *
0004  * Copyright (C) 2000-2003 Lars Knoll (knoll@kde.org)
0005  *           (C) 2003-2007 Apple Computer, Inc.
0006  *           (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
0007  *           (C) 2007-2009 Germain Garand (germain@ebooksfrance.org)
0008  *
0009  * This library is free software; you can redistribute it and/or
0010  * modify it under the terms of the GNU Library General Public
0011  * License as published by the Free Software Foundation; either
0012  * version 2 of the License, or (at your option) any later version.
0013  *
0014  * This library is distributed in the hope that it will be useful,
0015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0017  * Library General Public License for more details.
0018  *
0019  * You should have received a copy of the GNU Library General Public License
0020  * along with this library; see the file COPYING.LIB.  If not, write to
0021  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0022  * Boston, MA 02110-1301, USA.
0023  *
0024  */
0025 #include "rendering/bidi.h"
0026 #include "rendering/break_lines.h"
0027 #include "rendering/render_block.h"
0028 #include "rendering/render_text.h"
0029 #include "rendering/render_arena.h"
0030 #include "rendering/render_layer.h"
0031 #include "rendering/render_canvas.h"
0032 #include "xml/dom_docimpl.h"
0033 #include <QVector>
0034 
0035 #include "QDebug"
0036 
0037 #include <limits.h>
0038 
0039 // SVG
0040 #include "rendering/SVGRootInlineBox.h"
0041 #include "rendering/SVGInlineTextBox.h"
0042 
0043 #define BIDI_DEBUG 0
0044 //#define DEBUG_LINEBREAKS
0045 //#define PAGE_DEBUG
0046 
0047 namespace khtml
0048 {
0049 
0050 // an iterator which goes through a BidiParagraph
0051 struct BidiIterator {
0052     BidiIterator() : par(nullptr), obj(nullptr), pos(0), endOfInline(false) {}
0053     BidiIterator(RenderBlock *_par, RenderObject *_obj, unsigned int _pos, bool eoi = false) : par(_par), obj(_obj), pos(_pos), endOfInline(eoi) {}
0054 
0055     void increment(BidiState *bidi = nullptr, bool skipInlines = true);
0056 
0057     bool atEnd() const;
0058 
0059     const QChar &current() const;
0060     QChar::Direction direction() const;
0061 
0062     RenderBlock *par;
0063     RenderObject *obj;
0064     unsigned int pos;
0065     bool endOfInline;
0066 };
0067 
0068 struct BidiState {
0069     BidiState() : context(nullptr) {}
0070 
0071     BidiIterator sor;
0072     BidiIterator eor;
0073     BidiIterator last;
0074     BidiIterator current;
0075     BidiContext *context;
0076     BidiStatus status;
0077 };
0078 
0079 // Used to track a list of chained bidi runs.
0080 static BidiRun *sFirstBidiRun;
0081 static BidiRun *sLastBidiRun;
0082 static int sBidiRunCount;
0083 static BidiRun *sCompactFirstBidiRun;
0084 static BidiRun *sCompactLastBidiRun;
0085 static int sCompactBidiRunCount;
0086 static bool sBuildingCompactRuns;
0087 
0088 // Midpoint globals.  The goal is not to do any allocation when dealing with
0089 // these midpoints, so we just keep an array around and never clear it.  We track
0090 // the number of items and position using the two other variables.
0091 static QVector<BidiIterator> *smidpoints;
0092 static uint sNumMidpoints;
0093 static uint sCurrMidpoint;
0094 static bool betweenMidpoints;
0095 
0096 static bool isLineEmpty = true;
0097 static bool previousLineBrokeAtBR = false;
0098 static QChar::Direction dir = QChar::DirON;
0099 static bool emptyRun = true;
0100 static int numSpaces;
0101 
0102 static void embed(QChar::Direction d, BidiState &bidi);
0103 static void appendRun(BidiState &bidi);
0104 
0105 static int getBPMWidth(int childValue, Length cssUnit)
0106 {
0107     if (!cssUnit.isAuto()) {
0108         return (cssUnit.isFixed() ? cssUnit.value() : childValue);
0109     }
0110     return 0;
0111 }
0112 
0113 static int getBorderPaddingMargin(RenderObject *child, bool endOfInline)
0114 {
0115     RenderStyle *cstyle = child->style();
0116     int result = 0;
0117     bool leftSide = (cstyle->direction() == LTR) ? !endOfInline : endOfInline;
0118     result += getBPMWidth((leftSide ? child->marginLeft() : child->marginRight()),
0119                           (leftSide ? cstyle->marginLeft() :
0120                            cstyle->marginRight()));
0121     result += getBPMWidth((leftSide ? child->paddingLeft() : child->paddingRight()),
0122                           (leftSide ? cstyle->paddingLeft() :
0123                            cstyle->paddingRight()));
0124     result += leftSide ? child->borderLeft() : child->borderRight();
0125     return result;
0126 }
0127 
0128 #ifndef NDEBUG
0129 static bool inBidiRunDetach;
0130 #endif
0131 
0132 void BidiRun::detach(RenderArena *renderArena)
0133 {
0134 #ifndef NDEBUG
0135     inBidiRunDetach = true;
0136 #endif
0137     delete this;
0138 #ifndef NDEBUG
0139     inBidiRunDetach = false;
0140 #endif
0141 
0142     // Recover the size left there for us by operator delete and free the memory.
0143     renderArena->free(*(size_t *)this, this);
0144 }
0145 
0146 void *BidiRun::operator new(size_t sz, RenderArena *renderArena) throw()
0147 {
0148     return renderArena->allocate(sz);
0149 }
0150 
0151 void BidiRun::operator delete(void *ptr, size_t sz)
0152 {
0153     assert(inBidiRunDetach);
0154 
0155     // Stash size where detach can find it.
0156     *(size_t *)ptr = sz;
0157 }
0158 
0159 static void deleteBidiRuns(RenderArena *arena)
0160 {
0161     if (!sFirstBidiRun) {
0162         return;
0163     }
0164 
0165     BidiRun *curr = sFirstBidiRun;
0166     while (curr) {
0167         BidiRun *s = curr->nextRun;
0168         curr->detach(arena);
0169         curr = s;
0170     }
0171 
0172     sFirstBidiRun = nullptr;
0173     sLastBidiRun = nullptr;
0174     sBidiRunCount = 0;
0175 }
0176 
0177 // ---------------------------------------------------------------------
0178 
0179 /* a small helper class used internally to resolve Bidi embedding levels.
0180    Each line of text caches the embedding level at the start of the line for faster
0181    relayouting
0182 */
0183 BidiContext::BidiContext(unsigned char l, QChar::Direction e, BidiContext *p, bool o)
0184     : level(l), override(o), dir(e)
0185 {
0186     parent = p;
0187     if (p) {
0188         p->ref();
0189         basicDir = p->basicDir;
0190     } else {
0191         basicDir = e;
0192     }
0193     count = 0;
0194 }
0195 
0196 BidiContext::~BidiContext()
0197 {
0198     if (parent) {
0199         parent->deref();
0200     }
0201 }
0202 
0203 void BidiContext::ref() const
0204 {
0205     count++;
0206 }
0207 
0208 void BidiContext::deref() const
0209 {
0210     count--;
0211     if (count <= 0) {
0212         delete this;
0213     }
0214 }
0215 
0216 // ---------------------------------------------------------------------
0217 
0218 inline bool operator==(const BidiContext &c1, const BidiContext &c2)
0219 {
0220     if (&c1 == &c2) {
0221         return true;
0222     }
0223     if (c1.level != c2.level || c1.override != c2.override || c1.dir != c2.dir || c1.basicDir != c2.basicDir) {
0224         return false;
0225     }
0226     if (!c1.parent) {
0227         return !c2.parent;
0228     }
0229     return c2.parent && *c1.parent == *c2.parent;
0230 }
0231 
0232 inline bool operator==(const BidiIterator &it1, const BidiIterator &it2)
0233 {
0234     if (it1.pos != it2.pos) {
0235         return false;
0236     }
0237     if (it1.obj != it2.obj) {
0238         return false;
0239     }
0240     return true;
0241 }
0242 
0243 inline bool operator!=(const BidiIterator &it1, const BidiIterator &it2)
0244 {
0245     if (it1.pos != it2.pos) {
0246         return true;
0247     }
0248     if (it1.obj != it2.obj) {
0249         return true;
0250     }
0251     return false;
0252 }
0253 
0254 inline bool operator==(const BidiStatus &status1, const BidiStatus &status2)
0255 {
0256     return status1.eor == status2.eor && status1.last == status2.last && status1.lastStrong == status2.lastStrong;
0257 }
0258 
0259 inline bool operator!=(const BidiStatus &status1, const BidiStatus &status2)
0260 {
0261     return !(status1 == status2);
0262 }
0263 
0264 // when modifying this function, make sure you check InlineMinMaxIterator::next() as well.
0265 static inline RenderObject *Bidinext(RenderObject *par, RenderObject *current, BidiState *bidi = nullptr,
0266                                      bool skipInlines = true, bool *endOfInline = nullptr)
0267 {
0268     RenderObject *next = nullptr;
0269     bool oldEndOfInline = endOfInline ? *endOfInline : false;
0270     if (oldEndOfInline) {
0271         *endOfInline = false;
0272     }
0273     while (current != nullptr) {
0274         //qCDebug(KHTML_LOG) << "current = " << current;
0275         if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned()) {
0276             next = current->firstChild();
0277             if (next && bidi) {
0278                 EUnicodeBidi ub = next->style()->unicodeBidi();
0279                 if (ub != UBNormal && !emptyRun) {
0280                     EDirection dir = next->style()->direction();
0281                     QChar::Direction d = (ub == Embed ? (dir == RTL ? QChar::DirRLE : QChar::DirLRE)
0282                                           : (dir == RTL ? QChar::DirRLO : QChar::DirLRO));
0283                     embed(d, *bidi);
0284                 }
0285             }
0286         }
0287         if (!next) {
0288             if (!skipInlines && !oldEndOfInline && current->isInlineFlow() && endOfInline) {
0289                 next = current;
0290                 *endOfInline = true;
0291                 break;
0292             }
0293 
0294             while (current && current != par) {
0295                 next = current->nextSibling();
0296                 if (next) {
0297                     break;
0298                 }
0299                 if (bidi && current->style()->unicodeBidi() != UBNormal && !emptyRun) {
0300                     embed(QChar::DirPDF, *bidi);
0301                 }
0302                 current = current->parent();
0303                 if (!skipInlines && current && current != par && current->isInlineFlow() && endOfInline) {
0304                     next = current;
0305                     *endOfInline = true;
0306                     break;
0307                 }
0308             }
0309         }
0310 
0311         if (!next) {
0312             break;
0313         }
0314 
0315         if (next->isText() || next->isBR() || next->isFloating() || next->isReplaced() || next->isPositioned() || next->isGlyph()
0316                 || ((!skipInlines || !next->firstChild()) // Always return EMPTY inlines.
0317                     && next->isInlineFlow())) {
0318             break;
0319         }
0320         current = next;
0321         next    = nullptr;
0322     }
0323     return next;
0324 }
0325 
0326 static RenderObject *first(RenderObject *par, BidiState *bidi, bool skipInlines = true)
0327 {
0328     if (!par->firstChild()) {
0329         return nullptr;
0330     }
0331     RenderObject *o = par->firstChild();
0332 
0333     if (o->isInlineFlow()) {
0334         if (skipInlines && o->firstChild()) {
0335             o = Bidinext(par, o, bidi, skipInlines);
0336         } else {
0337             return o;    // Never skip empty inlines.
0338         }
0339     }
0340 
0341     if (o && !o->isText() && !o->isBR() && !o->isReplaced() && !o->isFloating() && !o->isPositioned() && !o->isGlyph()) {
0342         o = Bidinext(par, o, bidi, skipInlines);
0343     }
0344     return o;
0345 }
0346 
0347 inline void BidiIterator::increment(BidiState *bidi, bool skipInlines)
0348 {
0349     if (!obj) {
0350         return;
0351     }
0352     if (obj->isText()) {
0353         pos++;
0354         if (pos >= static_cast<RenderText *>(obj)->stringLength()) {
0355             obj = Bidinext(par, obj, bidi, skipInlines);
0356             pos = 0;
0357         }
0358     } else {
0359         obj = Bidinext(par, obj, bidi, skipInlines, &endOfInline);
0360         pos = 0;
0361     }
0362 }
0363 
0364 inline bool BidiIterator::atEnd() const
0365 {
0366     if (!obj) {
0367         return true;
0368     }
0369     return false;
0370 }
0371 
0372 const QChar &BidiIterator::current() const
0373 {
0374     static QChar nonBreakingSpace(0xA0);
0375 
0376     if (!obj || !obj->isText()) {
0377         return nonBreakingSpace;
0378     }
0379 
0380     RenderText *text = static_cast<RenderText *>(obj);
0381     if (!text->text()) {
0382         return nonBreakingSpace;
0383     }
0384 
0385     return text->text()[pos];
0386 }
0387 
0388 inline QChar::Direction BidiIterator::direction() const
0389 {
0390     if (!obj || !obj->isText()) {
0391         return QChar::DirON;
0392     }
0393 
0394     RenderText *renderTxt = static_cast<RenderText *>(obj);
0395     if (pos >= renderTxt->stringLength()) {
0396         return QChar::DirON;
0397     }
0398 
0399     return renderTxt->text()[pos].direction();
0400 }
0401 
0402 // -------------------------------------------------------------------------------------------------
0403 
0404 static void addRun(BidiRun *bidiRun)
0405 {
0406     if (!sFirstBidiRun) {
0407         sFirstBidiRun = sLastBidiRun = bidiRun;
0408     } else {
0409         sLastBidiRun->nextRun = bidiRun;
0410         sLastBidiRun = bidiRun;
0411     }
0412     sBidiRunCount++;
0413     bidiRun->compact = sBuildingCompactRuns;
0414 
0415     // Compute the number of spaces in this run,
0416     if (bidiRun->obj && bidiRun->obj->isText()) {
0417         RenderText *text = static_cast<RenderText *>(bidiRun->obj);
0418         if (text->text()) {
0419             for (int i = bidiRun->start; i < bidiRun->stop; i++) {
0420                 const QChar c = text->text()[i];
0421                 if (c.unicode() == '\n' || c.category() == QChar::Separator_Space) {
0422                     numSpaces++;
0423                 }
0424             }
0425         }
0426     }
0427 }
0428 
0429 static void reverseRuns(int start, int end)
0430 {
0431     if (start >= end) {
0432         return;
0433     }
0434 
0435     assert(start >= 0 && end < sBidiRunCount);
0436 
0437     // Get the item before the start of the runs to reverse and put it in
0438     // |beforeStart|.  |curr| should point to the first run to reverse.
0439     BidiRun *curr = sFirstBidiRun;
0440     BidiRun *beforeStart = nullptr;
0441     int i = 0;
0442     while (i < start) {
0443         i++;
0444         beforeStart = curr;
0445         curr = curr->nextRun;
0446     }
0447 
0448     BidiRun *startRun = curr;
0449     while (i < end) {
0450         i++;
0451         curr = curr->nextRun;
0452     }
0453     BidiRun *endRun = curr;
0454     BidiRun *afterEnd = curr->nextRun;
0455 
0456     i = start;
0457     curr = startRun;
0458     BidiRun *newNext = afterEnd;
0459     while (i <= end) {
0460         // Do the reversal.
0461         BidiRun *next = curr->nextRun;
0462         curr->nextRun = newNext;
0463         newNext = curr;
0464         curr = next;
0465         i++;
0466     }
0467 
0468     // Now hook up beforeStart and afterEnd to the newStart and newEnd.
0469     if (beforeStart) {
0470         beforeStart->nextRun = endRun;
0471     } else {
0472         sFirstBidiRun = endRun;
0473     }
0474 
0475     startRun->nextRun = afterEnd;
0476     if (!afterEnd) {
0477         sLastBidiRun = startRun;
0478     }
0479 }
0480 
0481 static void chopMidpointsAt(RenderObject *obj, uint pos)
0482 {
0483     if (!sNumMidpoints) {
0484         return;
0485     }
0486     BidiIterator *midpoints = smidpoints->data();
0487     for (uint i = 0; i < sNumMidpoints; i++) {
0488         const BidiIterator &point = midpoints[i];
0489         if (point.obj == obj && point.pos == pos) {
0490             sNumMidpoints = i;
0491             break;
0492         }
0493     }
0494 }
0495 
0496 static void checkMidpoints(BidiIterator &lBreak)
0497 {
0498     // Check to see if our last midpoint is a start point beyond the line break.  If so,
0499     // shave it off the list, and shave off a trailing space if the previous end point isn't
0500     // white-space: pre.
0501     if (lBreak.obj && sNumMidpoints && sNumMidpoints % 2 == 0) {
0502         BidiIterator *midpoints = smidpoints->data();
0503         BidiIterator &endpoint = midpoints[sNumMidpoints - 2];
0504         const BidiIterator &startpoint = midpoints[sNumMidpoints - 1];
0505         BidiIterator currpoint = endpoint;
0506         while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak) {
0507             currpoint.increment();
0508         }
0509         if (currpoint == lBreak) {
0510             // We hit the line break before the start point.  Shave off the start point.
0511             sNumMidpoints--;
0512             if (!endpoint.obj->style()->preserveWS()) {
0513                 if (endpoint.obj->isText()) {
0514                     // Don't shave a character off the endpoint if it was from a soft hyphen.
0515                     RenderText *textObj = static_cast<RenderText *>(endpoint.obj);
0516                     if (endpoint.pos + 1 < textObj->length() &&
0517                             textObj->text()[endpoint.pos + 1].unicode() == SOFT_HYPHEN) {
0518                         return;
0519                     }
0520                 }
0521                 endpoint.pos--;
0522             }
0523         }
0524     }
0525 }
0526 
0527 static void addMidpoint(const BidiIterator &midpoint)
0528 {
0529     if (!smidpoints) {
0530         return;
0531     }
0532 
0533     if (smidpoints->size() <= (int)sNumMidpoints) {
0534         smidpoints->resize(sNumMidpoints + 10);
0535     }
0536 
0537     BidiIterator *midpoints = smidpoints->data();
0538 
0539     // do not place midpoints in inline flows that are going to be skipped by the bidi iteration process.
0540     // Place them at the next non-skippable object instead.
0541     // #### eventually, we may want to have the same iteration in bidi and in findNextLineBreak,
0542     //      then this extra complexity can go away.
0543     if (midpoint.obj && midpoint.obj->isInlineFlow() && (midpoint.obj->firstChild() || midpoint.endOfInline)) {
0544         BidiIterator n = midpoint;
0545         n.increment();
0546         assert(!n.endOfInline);
0547         // we'll recycle the endOfInline flag to mean : don't include this stop point, stop right before it.
0548         // this is necessary because we just advanced our position to skip an inline, so we passed the real stop point
0549         n.endOfInline = true;
0550         if (!n.atEnd()) {
0551             midpoints[sNumMidpoints++] = n;
0552         }
0553     } else {
0554         assert(!midpoint.endOfInline);
0555         midpoints[sNumMidpoints++] = midpoint;
0556     }
0557 }
0558 
0559 static void appendRunsForObject(int start, int end, RenderObject *obj, BidiState &bidi)
0560 {
0561     if (start > end || obj->isFloating() ||
0562             (obj->isPositioned() && !obj->hasStaticX() && !obj->hasStaticY())) {
0563         return;
0564     }
0565 
0566     bool haveNextMidpoint = (smidpoints && sCurrMidpoint < sNumMidpoints);
0567     BidiIterator nextMidpoint;
0568     if (haveNextMidpoint) {
0569         nextMidpoint = smidpoints->at(sCurrMidpoint);
0570     }
0571     if (betweenMidpoints) {
0572         if (!(haveNextMidpoint && nextMidpoint.obj == obj)) {
0573             return;
0574         }
0575         // This is a new start point. Stop ignoring objects and
0576         // adjust our start.
0577         betweenMidpoints = false;
0578         start = nextMidpoint.pos;
0579         sCurrMidpoint++;
0580         if (start < end) {
0581             return appendRunsForObject(start, end, obj, bidi);
0582         }
0583     } else {
0584         if (!smidpoints || !haveNextMidpoint || (obj != nextMidpoint.obj)) {
0585             addRun(new(obj->renderArena()) BidiRun(start, end, obj, bidi.context, dir));
0586             return;
0587         }
0588 
0589         // An end midpoint has been encountered within our object.  We
0590         // need to go ahead and append a run with our endpoint.
0591         if (int(nextMidpoint.pos + 1) <= end) {
0592             betweenMidpoints = true;
0593             sCurrMidpoint++;
0594             if (nextMidpoint.pos != UINT_MAX) { // UINT_MAX means stop at the object and don't include any of it.
0595                 if (!nextMidpoint.endOfInline) // In this context, this flag means the stop point is exclusive, not inclusive (see addMidpoint).
0596                     addRun(new(obj->renderArena())
0597                            BidiRun(start, nextMidpoint.pos + 1, obj, bidi.context, dir));
0598                 return appendRunsForObject(nextMidpoint.pos + 1, end, obj, bidi);
0599             }
0600         } else {
0601             addRun(new(obj->renderArena()) BidiRun(start, end, obj, bidi.context, dir));
0602         }
0603     }
0604 }
0605 
0606 static void appendRun(BidiState &bidi)
0607 {
0608     if (emptyRun) {
0609         return;
0610     }
0611 #if BIDI_DEBUG > 1
0612     qCDebug(KHTML_LOG) << "appendRun: dir=" << (int)dir;
0613 #endif
0614 
0615     int start = bidi.sor.pos;
0616     RenderObject *obj = bidi.sor.obj;
0617     while (obj && obj != bidi.eor.obj) {
0618         appendRunsForObject(start, obj->length(), obj, bidi);
0619         start = 0;
0620         obj = Bidinext(bidi.sor.par, obj);
0621     }
0622     if (obj) {
0623         appendRunsForObject(start, bidi.eor.pos + 1, obj, bidi);
0624     }
0625 
0626     bidi.eor.increment();
0627     bidi.sor = bidi.eor;
0628     dir = QChar::DirON;
0629     bidi.status.eor = QChar::DirON;
0630 }
0631 
0632 static void embed(QChar::Direction d, BidiState &bidi)
0633 {
0634 #if BIDI_DEBUG > 1
0635     qDebug("*** embed dir=%d emptyrun=%d", d, emptyRun);
0636 #endif
0637     if (d == QChar::DirPDF) {
0638         BidiContext *c = bidi.context->parent;
0639         if (c) {
0640             if (bidi.eor != bidi.last) {
0641                 appendRun(bidi);
0642                 bidi.eor = bidi.last;
0643             }
0644             appendRun(bidi);
0645             emptyRun = true;
0646             bidi.status.last = bidi.context->dir;
0647             bidi.context->deref();
0648             bidi.context = c;
0649             if (bidi.context->override) {
0650                 dir = bidi.context->dir;
0651             } else {
0652                 dir = QChar::DirON;
0653             }
0654             bidi.status.lastStrong = bidi.context->dir;
0655         }
0656     } else {
0657         QChar::Direction runDir;
0658         if (d == QChar::DirRLE || d == QChar::DirRLO) {
0659             runDir = QChar::DirR;
0660         } else {
0661             runDir = QChar::DirL;
0662         }
0663         bool override;
0664         if (d == QChar::DirLRO || d == QChar::DirRLO) {
0665             override = true;
0666         } else {
0667             override = false;
0668         }
0669 
0670         unsigned char level = bidi.context->level;
0671         if (runDir == QChar::DirR) {
0672             if (level % 2) { // we have an odd level
0673                 level += 2;
0674             } else {
0675                 level++;
0676             }
0677         } else {
0678             if (level % 2) { // we have an odd level
0679                 level++;
0680             } else {
0681                 level += 2;
0682             }
0683         }
0684 
0685         if (level < 61) {
0686             if (bidi.eor != bidi.last) {
0687                 appendRun(bidi);
0688                 bidi.eor = bidi.last;
0689             }
0690             appendRun(bidi);
0691             emptyRun = true;
0692 
0693             bidi.context = new BidiContext(level, runDir, bidi.context, override);
0694             bidi.context->ref();
0695             dir = runDir;
0696             bidi.status.last = runDir;
0697             bidi.status.lastStrong = runDir;
0698             bidi.status.eor = runDir;
0699         }
0700     }
0701 }
0702 
0703 InlineFlowBox *RenderBlock::createLineBoxes(RenderObject *obj)
0704 {
0705     // See if we have an unconstructed line box for this object that is also
0706     // the last item on the line.
0707     KHTMLAssert(obj->isInlineFlow() || obj == this);
0708     RenderFlow *flow = static_cast<RenderFlow *>(obj);
0709 
0710     // Get the last box we made for this render object.
0711     InlineFlowBox *box = flow->lastLineBox();
0712 
0713     // If this box is constructed then it is from a previous line, and we need
0714     // to make a new box for our line.  If this box is unconstructed but it has
0715     // something following it on the line, then we know we have to make a new box
0716     // as well.  In this situation our inline has actually been split in two on
0717     // the same line (this can happen with very fancy language mixtures).
0718     if (!box || box->isConstructed() || box->nextOnLine()) {
0719         // We need to make a new box for this render object.  Once
0720         // made, we need to place it at the end of the current line.
0721         InlineBox *newBox = obj->createInlineBox(false, obj == this);
0722         KHTMLAssert(newBox->isInlineFlowBox());
0723         box = static_cast<InlineFlowBox *>(newBox);
0724         box->setFirstLineStyleBit(m_firstLine);
0725 
0726         // We have a new box. Append it to the inline box we get by constructing our
0727         // parent.  If we have hit the block itself, then |box| represents the root
0728         // inline box for the line, and it doesn't have to be appended to any parent
0729         // inline.
0730         if (obj != this) {
0731             InlineFlowBox *parentBox = createLineBoxes(obj->parent());
0732             parentBox->addToLine(box);
0733         }
0734     }
0735 
0736     return box;
0737 }
0738 
0739 RootInlineBox *RenderBlock::constructLine(const BidiIterator &/*start*/, const BidiIterator &end)
0740 {
0741     if (!sFirstBidiRun) {
0742         return nullptr;    // We had no runs. Don't make a root inline box at all. The line is empty.
0743     }
0744 
0745     InlineFlowBox *parentBox = nullptr;
0746     for (BidiRun *r = sFirstBidiRun; r; r = r->nextRun) {
0747         // Create a box for our object.
0748         r->box = r->obj->createInlineBox(r->obj->isPositioned(), false);
0749 
0750         // If we have no parent box yet, or if the run is not simply a sibling,
0751         // then we need to construct inline boxes as necessary to properly enclose the
0752         // run's inline box.
0753         if (!parentBox || (parentBox->object() != r->obj->parent()))
0754             // Create new inline boxes all the way back to the appropriate insertion point.
0755         {
0756             parentBox = createLineBoxes(r->obj->parent());
0757         }
0758 
0759         // Append the inline box to this line.
0760         parentBox->addToLine(r->box);
0761     }
0762 
0763     // We should have a root inline box.  It should be unconstructed and
0764     // be the last continuation of our line list.
0765     KHTMLAssert(lastLineBox() && !lastLineBox()->isConstructed());
0766 
0767     // Set bits on our inline flow boxes that indicate which sides should
0768     // paint borders/margins/padding.  This knowledge will ultimately be used when
0769     // we determine the horizontal positions and widths of all the inline boxes on
0770     // the line.
0771     RenderObject *endObject = nullptr;
0772     bool lastLine = !end.obj;
0773     if (end.obj && end.pos == 0) {
0774         endObject = end.obj;
0775     }
0776     lastLineBox()->determineSpacingForFlowBoxes(lastLine, endObject);
0777 
0778     // Now mark the line boxes as being constructed.
0779     lastLineBox()->setConstructed();
0780 
0781     // Return the last line.
0782     return lastRootBox();
0783 }
0784 
0785 void RenderBlock::computeHorizontalPositionsForLine(InlineFlowBox *lineBox, BidiState &bidi)
0786 {
0787     // First determine our total width.
0788     int totWidth = lineBox->getFlowSpacingWidth();
0789     BidiRun *r = nullptr;
0790     for (r = sFirstBidiRun; r; r = r->nextRun) {
0791         if (r->obj->isPositioned()) {
0792             continue;    // Positioned objects are only participating to figure out their
0793         }
0794         // correct static x position.  They have no effect on the width.
0795         if (r->obj->isText()) {
0796             r->box->setWidth(static_cast<RenderText *>(r->obj)->width(r->start, r->stop - r->start, m_firstLine));
0797         } else if (!r->obj->isInlineFlow()) {
0798             r->obj->calcWidth(); // Is this really needed or the object width is already correct here ?
0799             r->box->setWidth(r->obj->width());
0800             totWidth += r->obj->marginLeft() + r->obj->marginRight();
0801         }
0802         totWidth += r->box->width();
0803     }
0804 
0805     // Armed with the total width of the line (without justification),
0806     // we now examine our text-align property in order to determine where to position the
0807     // objects horizontally.  The total width of the line can be increased if we end up
0808     // justifying text.
0809     int x = leftOffset(m_height);
0810     int availableWidth = lineWidth(m_height);
0811     switch (style()->textAlign()) {
0812     case LEFT:
0813     case KHTML_LEFT:
0814         if (style()->direction() == RTL && totWidth > availableWidth) {
0815             x -= (totWidth - availableWidth);
0816         }
0817         numSpaces = 0;
0818         break;
0819     case JUSTIFY:
0820         if (numSpaces != 0 && !bidi.current.atEnd() && !bidi.current.obj->isBR()) {
0821             break;
0822         }
0823     // fall through
0824     case TAAUTO:
0825         numSpaces = 0;
0826         // for right to left fall through to right aligned
0827         if (bidi.context->basicDir == QChar::DirL) {
0828             break;
0829         }
0830     case RIGHT:
0831     case KHTML_RIGHT:
0832         if (style()->direction() == RTL || totWidth < availableWidth) {
0833             x += availableWidth - totWidth;
0834         }
0835         numSpaces = 0;
0836         break;
0837     case CENTER:
0838     case KHTML_CENTER:
0839         int xd = (availableWidth - totWidth) / 2;
0840         x += xd > 0 ? xd : 0;
0841         numSpaces = 0;
0842         break;
0843     }
0844 
0845     if (numSpaces > 0) {
0846         for (r = sFirstBidiRun; r; r = r->nextRun) {
0847             int spaceAdd = 0;
0848             if (numSpaces > 0 && r->obj->isText()) {
0849                 // get the number of spaces in the run
0850                 int spaces = 0;
0851                 for (int i = r->start; i < r->stop; i++) {
0852                     const QChar c = static_cast<RenderText *>(r->obj)->text()[i];
0853                     if (c.category() == QChar::Separator_Space || c == '\n') {
0854                         spaces++;
0855                     }
0856                 }
0857 
0858                 KHTMLAssert(spaces <= numSpaces);
0859 
0860                 // Only justify text with white-space: normal.
0861                 if (r->obj->style()->whiteSpace() == NORMAL) {
0862                     spaceAdd = (availableWidth - totWidth) * spaces / numSpaces;
0863                     spaceAdd = qMax(0, spaceAdd);
0864                     static_cast<InlineTextBox *>(r->box)->setSpaceAdd(spaceAdd);
0865                     totWidth += spaceAdd;
0866                 }
0867                 numSpaces -= spaces;
0868             }
0869         }
0870     }
0871 
0872     // The widths of all runs are now known.  We can now place every inline box (and
0873     // compute accurate widths for the inline flow boxes).
0874     int rightPos = lineBox->placeBoxesHorizontally(x);
0875     if (rightPos > m_overflowWidth) {
0876         m_overflowWidth = rightPos;    // FIXME: Work for rtl overflow also.
0877     }
0878     if (x < 0) {
0879         m_overflowLeft = qMin(m_overflowLeft, x);
0880     }
0881 }
0882 
0883 void RenderBlock::computeVerticalPositionsForLine(RootInlineBox *lineBox)
0884 {
0885     lineBox->verticallyAlignBoxes(m_height);
0886     lineBox->setBlockHeight(m_height);
0887 
0888     // Check for page-breaks
0889     if (canvas()->pagedMode() && !lineBox->afterPageBreak())
0890         // If we get a page-break we might need to redo the line-break
0891         if (clearLineOfPageBreaks(lineBox) && hasFloats()) {
0892             return;
0893         }
0894 
0895     // See if the line spilled out.  If so set overflow height accordingly.
0896     int bottomOfLine = lineBox->bottomOverflow();
0897     if (bottomOfLine > m_height && bottomOfLine > m_overflowHeight) {
0898         m_overflowHeight = bottomOfLine;
0899     }
0900 
0901     bool beforeContent = true;
0902 
0903     // Now make sure we place replaced render objects correctly.
0904     for (BidiRun *r = sFirstBidiRun; r; r = r->nextRun) {
0905 
0906         // For positioned placeholders, cache the static Y position an object with non-inline display would have.
0907         // Either it is unchanged if it comes before any real linebox, or it must clear the current line (already accounted in m_height).
0908         // This value will be picked up by position() if relevant.
0909         if (r->obj->isPositioned()) {
0910             r->box->setYPos(beforeContent && r->obj->isBox() ? static_cast<RenderBox *>(r->obj)->staticY() : m_height);
0911         } else if (beforeContent) {
0912             beforeContent = false;
0913         }
0914 
0915         // Position is used to properly position both replaced elements and
0916         // to update the static normal flow x/y of positioned elements.
0917         r->obj->position(r->box, r->start, r->stop - r->start, r->level % 2);
0918     }
0919 }
0920 
0921 bool RenderBlock::clearLineOfPageBreaks(InlineFlowBox *lineBox)
0922 {
0923     bool doPageBreak = false;
0924     // Check for physical page-breaks
0925     int xpage = crossesPageBreak(lineBox->topOverflow(), lineBox->bottomOverflow());
0926     if (xpage) {
0927 #ifdef PAGE_DEBUG
0928         qCDebug(KHTML_LOG) << renderName() << " Line crosses to page " << xpage;
0929         qCDebug(KHTML_LOG) << renderName() << " at pos " << lineBox->yPos() << " height " << lineBox->height();
0930 #endif
0931 
0932         doPageBreak = true;
0933         // check page-break-inside
0934         if (!style()->pageBreakInside()) {
0935             if (parent()->canClear(this, PageBreakNormal)) {
0936                 setNeedsPageClear(true);
0937                 doPageBreak = false;
0938             }
0939 #ifdef PAGE_DEBUG
0940             else {
0941                 qCDebug(KHTML_LOG) << "Ignoring page-break-inside: avoid";
0942             }
0943 #endif
0944         }
0945         // check orphans
0946         int orphans = 0;
0947         InlineRunBox *box = lineBox->prevLineBox();
0948         while (box && orphans < style()->orphans()) {
0949             orphans++;
0950             box = box->prevLineBox();
0951         }
0952 
0953         if (orphans == 0) {
0954             setNeedsPageClear(true);
0955             doPageBreak = false;
0956         } else if (orphans < style()->orphans()) {
0957 #ifdef PAGE_DEBUG
0958             qCDebug(KHTML_LOG) << "Orphans: " << orphans;
0959 #endif
0960             // Orphans is a level 2 page-break rule and can be broken only
0961             // if the break is physically required.
0962             if (parent()->canClear(this, PageBreakHarder)) {
0963                 // move block instead
0964                 setNeedsPageClear(true);
0965                 doPageBreak = false;
0966             }
0967 #ifdef PAGE_DEBUG
0968             else {
0969                 qCDebug(KHTML_LOG) << "Ignoring violated orphans";
0970             }
0971 #endif
0972         }
0973         if (doPageBreak) {
0974 #ifdef PAGE_DEBUG
0975             int oldYPos = lineBox->yPos();
0976 #endif
0977             int pTop = pageTopAfter(lineBox->yPos());
0978             m_height = pTop;
0979             lineBox->setAfterPageBreak(true);
0980             lineBox->verticallyAlignBoxes(m_height);
0981             if (lineBox->yPos() < pTop) {
0982                 // ### serious crap. render_line is sometimes placing lines too high
0983                 // qCDebug(KHTML_LOG) << "page top overflow by repositioned line";
0984                 int heightIncrease = pTop - lineBox->yPos();
0985                 m_height = pTop + heightIncrease;
0986                 lineBox->verticallyAlignBoxes(m_height);
0987             }
0988 #ifdef PAGE_DEBUG
0989             qCDebug(KHTML_LOG) << "Cleared line " << lineBox->yPos() - oldYPos << "px";
0990 #endif
0991             setContainsPageBreak(true);
0992         }
0993     }
0994     return doPageBreak;
0995 }
0996 
0997 // collects one line of the paragraph and transforms it to visual order
0998 void RenderBlock::bidiReorderLine(const BidiIterator &start, const BidiIterator &end, BidiState &bidi)
0999 {
1000     if (start == end) {
1001         if (start.current() == '\n') {
1002             m_height += lineHeight(m_firstLine);
1003         }
1004         return;
1005     }
1006 
1007 #if BIDI_DEBUG > 1
1008     qCDebug(KHTML_LOG) << "reordering Line from " << start.obj << "/" << start.pos << " to " << end.obj << "/" << end.pos;
1009 #endif
1010 
1011     sFirstBidiRun = nullptr;
1012     sLastBidiRun = nullptr;
1013     sBidiRunCount = 0;
1014 
1015     //    context->ref();
1016 
1017     dir = QChar::DirON;
1018     emptyRun = true;
1019 
1020     numSpaces = 0;
1021 
1022     bidi.current = start;
1023     bidi.last = bidi.current;
1024     bool atEnd = false;
1025     while (1) {
1026         QChar::Direction dirCurrent;
1027         if (atEnd) {
1028             //qCDebug(KHTML_LOG) << "atEnd";
1029             BidiContext *c = bidi.context;
1030             if (bidi.current.atEnd())
1031                 while (c->parent) {
1032                     c = c->parent;
1033                 }
1034             dirCurrent = c->dir;
1035         } else if (bidi.context->override) {
1036             dirCurrent = bidi.context->dir;
1037         } else {
1038             dirCurrent = bidi.current.direction();
1039         }
1040 
1041 #ifndef QT_NO_UNICODETABLES
1042 
1043 #if BIDI_DEBUG > 1
1044         qCDebug(KHTML_LOG) << "directions: dir=" << (int)dir << " current=" << (int)dirCurrent << " last=" << bidi.status.last << " eor=" << bidi.status.eor << " lastStrong=" << bidi.status.lastStrong << " embedding=" << (int)bidi.context->dir << " level =" << (int)bidi.context->level;
1045 #endif
1046 
1047         switch (dirCurrent) {
1048 
1049         // embedding and overrides (X1-X9 in the Bidi specs)
1050         case QChar::DirRLE:
1051         case QChar::DirLRE:
1052         case QChar::DirRLO:
1053         case QChar::DirLRO:
1054         case QChar::DirPDF:
1055             embed(dirCurrent, bidi);
1056             break;
1057 
1058         // strong types
1059         case QChar::DirL:
1060             if (dir == QChar::DirON) {
1061                 dir = QChar::DirL;
1062             }
1063             switch (bidi.status.last) {
1064             case QChar::DirL:
1065                 bidi.eor = bidi.current; bidi.status.eor = QChar::DirL; break;
1066             case QChar::DirR:
1067             case QChar::DirAL:
1068             case QChar::DirEN:
1069             case QChar::DirAN:
1070                 appendRun(bidi);
1071                 break;
1072             case QChar::DirES:
1073             case QChar::DirET:
1074             case QChar::DirCS:
1075             case QChar::DirBN:
1076             case QChar::DirB:
1077             case QChar::DirS:
1078             case QChar::DirWS:
1079             case QChar::DirON:
1080                 if (bidi.status.eor != QChar::DirL) {
1081                     //last stuff takes embedding dir
1082                     if (bidi.context->dir == QChar::DirL || bidi.status.lastStrong == QChar::DirL) {
1083                         if (bidi.status.eor != QChar::DirEN && bidi.status.eor != QChar::DirAN && bidi.status.eor != QChar::DirON) {
1084                             appendRun(bidi);
1085                         }
1086                         dir = QChar::DirL;
1087                         bidi.eor = bidi.current;
1088                         bidi.status.eor = QChar::DirL;
1089                     } else {
1090                         if (bidi.status.eor == QChar::DirEN || bidi.status.eor == QChar::DirAN) {
1091                             dir = bidi.status.eor;
1092                             appendRun(bidi);
1093                         }
1094                         dir = QChar::DirR;
1095                         bidi.eor = bidi.last;
1096                         appendRun(bidi);
1097                         dir = QChar::DirL;
1098                         bidi.status.eor = QChar::DirL;
1099                     }
1100                 } else {
1101                     bidi.eor = bidi.current; bidi.status.eor = QChar::DirL;
1102                 }
1103             default:
1104                 break;
1105             }
1106             bidi.status.lastStrong = QChar::DirL;
1107             break;
1108         case QChar::DirAL:
1109         case QChar::DirR:
1110             if (dir == QChar::DirON) {
1111                 dir = QChar::DirR;
1112             }
1113             switch (bidi.status.last) {
1114             case QChar::DirR:
1115             case QChar::DirAL:
1116                 bidi.eor = bidi.current; bidi.status.eor = QChar::DirR; break;
1117             case QChar::DirL:
1118             case QChar::DirEN:
1119             case QChar::DirAN:
1120                 appendRun(bidi);
1121                 dir = QChar::DirR;
1122                 bidi.eor = bidi.current;
1123                 bidi.status.eor = QChar::DirR;
1124                 break;
1125             case QChar::DirES:
1126             case QChar::DirET:
1127             case QChar::DirCS:
1128             case QChar::DirBN:
1129             case QChar::DirB:
1130             case QChar::DirS:
1131             case QChar::DirWS:
1132             case QChar::DirON:
1133                 if (!(bidi.status.eor == QChar::DirR) && !(bidi.status.eor == QChar::DirAL)) {
1134                     //last stuff takes embedding dir
1135                     if (bidi.context->dir == QChar::DirR || bidi.status.lastStrong == QChar::DirR
1136                             || bidi.status.lastStrong == QChar::DirAL) {
1137                         appendRun(bidi);
1138                         dir = QChar::DirR;
1139                         bidi.eor = bidi.current;
1140                         bidi.status.eor = QChar::DirR;
1141                     } else {
1142                         dir = QChar::DirL;
1143                         bidi.eor = bidi.last;
1144                         appendRun(bidi);
1145                         dir = QChar::DirR;
1146                         bidi.status.eor = QChar::DirR;
1147                     }
1148                 } else {
1149                     bidi.eor = bidi.current; bidi.status.eor = QChar::DirR;
1150                 }
1151             default:
1152                 break;
1153             }
1154             bidi.status.lastStrong = dirCurrent;
1155             break;
1156 
1157         // weak types:
1158 
1159         case QChar::DirNSM:
1160             // ### if @sor, set dir to dirSor
1161             break;
1162         case QChar::DirEN:
1163             if (!(bidi.status.lastStrong == QChar::DirAL)) {
1164                 // if last strong was AL change EN to AN
1165                 if (dir == QChar::DirON) {
1166                     dir = QChar::DirL;
1167                 }
1168                 switch (bidi.status.last) {
1169                 case QChar::DirET:
1170                     if (bidi.status.lastStrong == QChar::DirR || bidi.status.lastStrong == QChar::DirAL) {
1171                         appendRun(bidi);
1172                         dir = QChar::DirEN;
1173                         bidi.status.eor = QChar::DirEN;
1174                     }
1175                 // fall through
1176                 case QChar::DirEN:
1177                 case QChar::DirL:
1178                     bidi.eor = bidi.current;
1179                     bidi.status.eor = dirCurrent;
1180                     break;
1181                 case QChar::DirR:
1182                 case QChar::DirAL:
1183                 case QChar::DirAN:
1184                     appendRun(bidi);
1185                     bidi.status.eor = QChar::DirEN;
1186                     dir = QChar::DirEN; break;
1187                 case QChar::DirES:
1188                 case QChar::DirCS:
1189                     if (bidi.status.eor == QChar::DirEN) {
1190                         bidi.eor = bidi.current; break;
1191                     }
1192                 case QChar::DirBN:
1193                 case QChar::DirB:
1194                 case QChar::DirS:
1195                 case QChar::DirWS:
1196                 case QChar::DirON:
1197                     if (bidi.status.eor == QChar::DirR) {
1198                         // neutrals go to R
1199                         bidi.eor = bidi.last;
1200                         appendRun(bidi);
1201                         dir = QChar::DirEN;
1202                         bidi.status.eor = QChar::DirEN;
1203                     } else if (bidi.status.eor == QChar::DirL ||
1204                                (bidi.status.eor == QChar::DirEN && bidi.status.lastStrong == QChar::DirL)) {
1205                         bidi.eor = bidi.current; bidi.status.eor = dirCurrent;
1206                     } else {
1207                         // numbers on both sides, neutrals get right to left direction
1208                         if (dir != QChar::DirL) {
1209                             appendRun(bidi);
1210                             bidi.eor = bidi.last;
1211                             dir = QChar::DirR;
1212                             appendRun(bidi);
1213                             dir = QChar::DirEN;
1214                             bidi.status.eor = QChar::DirEN;
1215                         } else {
1216                             bidi.eor = bidi.current; bidi.status.eor = dirCurrent;
1217                         }
1218                     }
1219                 default:
1220                     break;
1221                 }
1222                 break;
1223             }
1224         case QChar::DirAN:
1225             dirCurrent = QChar::DirAN;
1226             if (dir == QChar::DirON) {
1227                 dir = QChar::DirAN;
1228             }
1229             switch (bidi.status.last) {
1230             case QChar::DirL:
1231             case QChar::DirAN:
1232                 bidi.eor = bidi.current; bidi.status.eor = QChar::DirAN; break;
1233             case QChar::DirR:
1234             case QChar::DirAL:
1235             case QChar::DirEN:
1236                 appendRun(bidi);
1237                 dir = QChar::DirAN; bidi.status.eor = QChar::DirAN;
1238                 break;
1239             case QChar::DirCS:
1240                 if (bidi.status.eor == QChar::DirAN) {
1241                     bidi.eor = bidi.current; break;
1242                 }
1243             case QChar::DirES:
1244             case QChar::DirET:
1245             case QChar::DirBN:
1246             case QChar::DirB:
1247             case QChar::DirS:
1248             case QChar::DirWS:
1249             case QChar::DirON:
1250                 if (bidi.status.eor == QChar::DirR) {
1251                     // neutrals go to R
1252                     bidi.eor = bidi.last;
1253                     appendRun(bidi);
1254                     dir = QChar::DirAN;
1255                     bidi.status.eor = QChar::DirAN;
1256                 } else if (bidi.status.eor == QChar::DirL ||
1257                            (bidi.status.eor == QChar::DirEN && bidi.status.lastStrong == QChar::DirL)) {
1258                     bidi.eor = bidi.current; bidi.status.eor = dirCurrent;
1259                 } else {
1260                     // numbers on both sides, neutrals get right to left direction
1261                     if (dir != QChar::DirL) {
1262                         appendRun(bidi);
1263                         bidi.eor = bidi.last;
1264                         dir = QChar::DirR;
1265                         appendRun(bidi);
1266                         dir = QChar::DirAN;
1267                         bidi.status.eor = QChar::DirAN;
1268                     } else {
1269                         bidi.eor = bidi.current; bidi.status.eor = dirCurrent;
1270                     }
1271                 }
1272             default:
1273                 break;
1274             }
1275             break;
1276         case QChar::DirES:
1277         case QChar::DirCS:
1278             break;
1279         case QChar::DirET:
1280             if (bidi.status.last == QChar::DirEN) {
1281                 dirCurrent = QChar::DirEN;
1282                 bidi.eor = bidi.current; bidi.status.eor = dirCurrent;
1283                 break;
1284             }
1285             break;
1286 
1287         // boundary neutrals should be ignored
1288         case QChar::DirBN:
1289             break;
1290         // neutrals
1291         case QChar::DirB:
1292             // ### what do we do with newline and paragraph separators that come to here?
1293             break;
1294         case QChar::DirS:
1295             // ### implement rule L1
1296             break;
1297         case QChar::DirWS:
1298             break;
1299         case QChar::DirON:
1300             break;
1301         default:
1302             break;
1303         }
1304 
1305         //cout << "     after: dir=" << //        dir << " current=" << dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << context->dir << endl;
1306 
1307         if (bidi.current.atEnd()) {
1308             break;
1309         }
1310 
1311         // set status.last as needed.
1312         switch (dirCurrent) {
1313         case QChar::DirET:
1314         case QChar::DirES:
1315         case QChar::DirCS:
1316         case QChar::DirS:
1317         case QChar::DirWS:
1318         case QChar::DirON:
1319             switch (bidi.status.last) {
1320             case QChar::DirL:
1321             case QChar::DirR:
1322             case QChar::DirAL:
1323             case QChar::DirEN:
1324             case QChar::DirAN:
1325                 bidi.status.last = dirCurrent;
1326                 break;
1327             default:
1328                 bidi.status.last = QChar::DirON;
1329             }
1330             break;
1331         case QChar::DirNSM:
1332         case QChar::DirBN:
1333             // ignore these
1334             break;
1335         case QChar::DirEN:
1336             if (bidi.status.last == QChar::DirL) {
1337                 break;
1338             }
1339         // fall through
1340         default:
1341             bidi.status.last = dirCurrent;
1342         }
1343 #endif
1344 
1345         if (atEnd) {
1346             break;
1347         }
1348         bidi.last = bidi.current;
1349 
1350         if (emptyRun) {
1351             bidi.sor = bidi.current;
1352             bidi.eor = bidi.current;
1353             emptyRun = false;
1354         }
1355 
1356         // this causes the operator ++ to open and close embedding levels as needed
1357         // for the CSS unicode-bidi property
1358         bidi.current.increment(&bidi);
1359 
1360         if (bidi.current == end) {
1361             if (emptyRun) {
1362                 break;
1363             }
1364             atEnd = true;
1365         }
1366     }
1367 
1368 #if BIDI_DEBUG > 0
1369     qCDebug(KHTML_LOG) << "reached end of line current=" << bidi.current.obj << "/" << bidi.current.pos
1370              << ", eor=" << bidi.eor.obj << "/" << bidi.eor.pos;
1371 #endif
1372     if (!emptyRun && bidi.sor != bidi.current) {
1373         bidi.eor = bidi.last;
1374         appendRun(bidi);
1375     }
1376 
1377     // reorder line according to run structure...
1378 
1379     // first find highest and lowest levels
1380     uchar levelLow = 128;
1381     uchar levelHigh = 0;
1382     BidiRun *r = sFirstBidiRun;
1383     while (r) {
1384         if (r->level > levelHigh) {
1385             levelHigh = r->level;
1386         }
1387         if (r->level < levelLow) {
1388             levelLow = r->level;
1389         }
1390         r = r->nextRun;
1391     }
1392 
1393     // implements reordering of the line (L2 according to Bidi spec):
1394     // L2. From the highest level found in the text to the lowest odd level on each line,
1395     // reverse any contiguous sequence of characters that are at that level or higher.
1396 
1397     // reversing is only done up to the lowest odd level
1398     if (!(levelLow % 2)) {
1399         levelLow++;
1400     }
1401 
1402     int count = sBidiRunCount - 1;
1403 
1404     // do not reverse for visually ordered web sites
1405     if (!style()->visuallyOrdered()) {
1406         while (levelHigh >= levelLow) {
1407             int i = 0;
1408             BidiRun *currRun = sFirstBidiRun;
1409             while (i < count) {
1410                 while (i < count && currRun && currRun->level < levelHigh) {
1411                     i++;
1412                     currRun = currRun->nextRun;
1413                 }
1414                 int start = i;
1415                 while (i <= count && currRun && currRun->level >= levelHigh) {
1416                     i++;
1417                     currRun = currRun->nextRun;
1418                 }
1419                 int end = i - 1;
1420                 reverseRuns(start, end);
1421             }
1422             levelHigh--;
1423         }
1424     }
1425 
1426 #if BIDI_DEBUG > 0
1427     qCDebug(KHTML_LOG) << "visual order is:";
1428     for (BidiRun *curr = sFirstBidiRun; curr; curr = curr->nextRun) {
1429         qCDebug(KHTML_LOG) << "    " << curr;
1430     }
1431 #endif
1432 }
1433 
1434 void RenderBlock::layoutInlineChildren(bool relayoutChildren, int breakBeforeLine)
1435 {
1436     BidiState bidi;
1437 
1438     m_overflowHeight = 0;
1439 
1440     invalidateVerticalPosition();
1441 #ifdef DEBUG_LAYOUT
1442     QTime qt;
1443     qt.start();
1444     qCDebug(KHTML_LOG) << renderName() << " layoutInlineChildren( " << this << " )";
1445 #endif
1446 #if BIDI_DEBUG > 1 || defined( DEBUG_LINEBREAKS )
1447     qCDebug(KHTML_LOG) << " ------- bidi start " << this << " -------";
1448 #endif
1449 
1450     m_height = borderTop() + paddingTop();
1451     int toAdd = borderBottom() + paddingBottom();
1452     if (m_layer && scrollsOverflowX() && style()->height().isAuto()) {
1453         toAdd += m_layer->horizontalScrollbarHeight();
1454     }
1455 
1456     // Figure out if we should clear our line boxes.
1457     bool fullLayout = !firstLineBox() || !firstChild() || selfNeedsLayout() || relayoutChildren || hasFloats();
1458 
1459     if (fullLayout) {
1460         deleteInlineBoxes();
1461     }
1462 
1463     // Text truncation only kicks in if your overflow isn't visible and your
1464     // text-overflow-mode isn't clip.
1465     bool hasTextOverflow = style()->textOverflow() && hasOverflowClip();
1466 
1467     // Walk all the lines and delete our ellipsis line boxes if they exist.
1468     if (hasTextOverflow) {
1469         deleteEllipsisLineBoxes();
1470     }
1471 
1472     if (firstChild()) {
1473         // layout replaced elements
1474         RenderObject *o = first(this, nullptr, false);
1475         while (o) {
1476             invalidateVerticalPosition();
1477             if (!fullLayout && o->markedForRepaint()) {
1478                 o->repaintDuringLayout();
1479                 o->setMarkedForRepaint(false);
1480             }
1481             if (o->isReplaced() || o->isFloating() || o->isPositioned()) {
1482 
1483                 if ((!o->isPositioned() || o->isPosWithStaticDim()) &&
1484                         (relayoutChildren || o->style()->width().isPercent() || o->style()->height().isPercent())) {
1485                     o->setChildNeedsLayout(true, false);
1486                 }
1487 
1488                 if (o->isPositioned()) {
1489                     if (!o->inPosObjectList()) {
1490                         o->containingBlock()->insertPositionedObject(o);
1491                     }
1492                     if (fullLayout) {
1493                         static_cast<RenderBox *>(o)->RenderBox::deleteInlineBoxes();
1494                     }
1495                 } else {
1496                     if (fullLayout || o->needsLayout()) {
1497                         static_cast<RenderBox *>(o)->RenderBox::dirtyInlineBoxes(fullLayout);
1498                     }
1499                     o->layoutIfNeeded();
1500                 }
1501             } else {
1502                 if (fullLayout || o->selfNeedsLayout()) {
1503                     o->dirtyInlineBoxes(fullLayout);
1504                     o->setMarkedForRepaint(false);
1505                 }
1506                 o->setNeedsLayout(false);
1507             }
1508             o = Bidinext(this, o, nullptr, false);
1509         }
1510 
1511         BidiContext *startEmbed;
1512         if (style()->direction() == LTR) {
1513             startEmbed = new BidiContext(0, QChar::DirL);
1514             bidi.status.eor = QChar::DirL;
1515         } else {
1516             startEmbed = new BidiContext(1, QChar::DirR);
1517             bidi.status.eor = QChar::DirR;
1518         }
1519         startEmbed->ref();
1520 
1521         bidi.status.lastStrong = QChar::DirON;
1522         bidi.status.last = QChar::DirON;
1523 
1524         bidi.context = startEmbed;
1525 
1526         // We want to skip ahead to the first dirty line
1527         BidiIterator start;
1528         RootInlineBox *startLine = determineStartPosition(fullLayout, start, bidi);
1529 
1530         // Then look forward to see if we can find a clean area that is clean up to the end.
1531         BidiIterator cleanLineStart;
1532         BidiStatus cleanLineBidiStatus;
1533         BidiContext *cleanLineBidiContext = nullptr;
1534         int endLineYPos = 0;
1535         RootInlineBox *endLine = (fullLayout || !startLine) ?
1536                                  nullptr : determineEndPosition(startLine, cleanLineStart, cleanLineBidiStatus, cleanLineBidiContext, endLineYPos);
1537 
1538         // Extract the clean area. We will add it back if we determine that we're able to
1539         // synchronize after relayouting the dirty area.
1540         if (endLine)
1541             for (RootInlineBox *line = endLine; line; line = line->nextRootBox()) {
1542                 line->extractLine();
1543             }
1544 
1545         // Delete the dirty area.
1546         if (startLine) {
1547             RenderArena *arena = renderArena();
1548             RootInlineBox *box = startLine;
1549             while (box) {
1550                 RootInlineBox *next = box->nextRootBox();
1551                 box->deleteLine(arena);
1552                 box = next;
1553             }
1554             startLine = nullptr;
1555         }
1556         BidiIterator end = start;
1557         bool endLineMatched = false;
1558         m_firstLine = true;
1559 
1560         if (!smidpoints) {
1561             smidpoints = new QVector<BidiIterator>;
1562         }
1563 
1564         sNumMidpoints = 0;
1565         sCurrMidpoint = 0;
1566         sCompactFirstBidiRun = sCompactLastBidiRun = nullptr;
1567         sCompactBidiRunCount = 0;
1568 
1569         previousLineBrokeAtBR = true;
1570 
1571         int lineCount = 0;
1572         bool pagebreakHint = false;
1573         int oldPos = 0;
1574         BidiIterator oldStart;
1575         BidiState oldBidi;
1576         const bool pagedMode = canvas()->pagedMode();
1577 
1578         while (!end.atEnd()) {
1579             start = end;
1580             if (endLine && (endLineMatched = matchedEndLine(start, bidi.status, bidi.context, cleanLineStart, cleanLineBidiStatus, cleanLineBidiContext, endLine, endLineYPos))) {
1581                 break;
1582             }
1583             lineCount++;
1584             betweenMidpoints = false;
1585             isLineEmpty = true;
1586             pagebreakHint = false;
1587             if (pagedMode) {
1588                 oldPos = m_height;
1589                 oldStart = start;
1590                 oldBidi = bidi;
1591             }
1592             if (lineCount == breakBeforeLine) {
1593                 m_height = pageTopAfter(oldPos);
1594                 pagebreakHint = true;
1595             }
1596         redo_linebreak:
1597             end = findNextLineBreak(start, bidi);
1598             if (start.atEnd()) {
1599                 deleteBidiRuns(renderArena());
1600                 break;
1601             }
1602             if (!isLineEmpty) {
1603                 bidiReorderLine(start, end, bidi);
1604 
1605                 // Now that the runs have been ordered, we create the line boxes.
1606                 // At the same time we figure out where border/padding/margin should be applied for
1607                 // inline flow boxes.
1608 
1609                 RootInlineBox *lineBox = nullptr;
1610                 if (sBidiRunCount) {
1611                     lineBox = constructLine(start, end);
1612                     if (lineBox) {
1613                         lineBox->setEndsWithBreak(previousLineBrokeAtBR);
1614                         if (pagebreakHint) {
1615                             lineBox->setAfterPageBreak(true);
1616                         }
1617 
1618                         // Now we position all of our text runs horizontally.
1619                         computeHorizontalPositionsForLine(lineBox, bidi);
1620 
1621                         // Now position our text runs vertically.
1622                         computeVerticalPositionsForLine(lineBox);
1623 
1624                         // SVG
1625                         if (lineBox->isSVGRootInlineBox()) {
1626                             //qCDebug(KHTML_LOG) << "svgrootinline box:";
1627                             WebCore::SVGRootInlineBox *svgLineBox = static_cast<WebCore::SVGRootInlineBox *>(lineBox);
1628                             svgLineBox->computePerCharacterLayoutInformation();
1629                         }
1630 
1631                         deleteBidiRuns(renderArena());
1632 
1633                         if (lineBox->afterPageBreak() && hasFloats() && !pagebreakHint) {
1634                             start = end = oldStart;
1635                             bidi = oldBidi;
1636                             m_height = pageTopAfter(oldPos);
1637                             deleteLastLineBox(renderArena());
1638                             pagebreakHint = true;
1639                             goto redo_linebreak;
1640                         }
1641                     }
1642                 }
1643 
1644                 if (end == start || (end.obj && end.obj->isBR() && !start.obj->isBR())) {
1645                     end.increment(&bidi);
1646                 } else if (end.obj && end.obj->style()->preserveLF() && end.current() == QChar('\n')) {
1647                     end.increment(&bidi);
1648                 }
1649 
1650                 if (lineBox) {
1651                     lineBox->setLineBreakInfo(end.obj, end.pos, bidi.status, bidi.context);
1652                 }
1653 
1654                 m_firstLine = false;
1655                 newLine();
1656             }
1657 
1658             sNumMidpoints = 0;
1659             sCurrMidpoint = 0;
1660             sCompactFirstBidiRun = sCompactLastBidiRun = nullptr;
1661             sCompactBidiRunCount = 0;
1662         }
1663         startEmbed->deref();
1664         //embed->deref();
1665 
1666         if (endLine) {
1667             if (endLineMatched) {
1668                 // Attach all the remaining lines, and then adjust their y-positions as needed.
1669                 for (RootInlineBox *line = endLine; line; line = line->nextRootBox()) {
1670                     line->attachLine();
1671                 }
1672 
1673                 // Now apply the offset to each line if needed.
1674                 int delta = m_height - endLineYPos;
1675                 if (delta) {
1676                     for (RootInlineBox *line = endLine; line; line = line->nextRootBox()) {
1677                         line->adjustPosition(0, delta);
1678                     }
1679                 }
1680                 m_height = lastRootBox()->blockHeight();
1681             } else {
1682                 // Delete all the remaining lines.
1683                 InlineRunBox *line = endLine;
1684                 RenderArena *arena = renderArena();
1685                 while (line) {
1686                     InlineRunBox *next = line->nextLineBox();
1687                     line->deleteLine(arena);
1688                     line = next;
1689                 }
1690             }
1691         }
1692     }
1693 
1694     sNumMidpoints = 0;
1695     sCurrMidpoint = 0;
1696 
1697     // If we violate widows page-breaking rules, we set a hint and relayout.
1698     // Note that the widows rule might still be violated afterwards if the lines have become wider
1699     if (canvas()->pagedMode() && containsPageBreak() && breakBeforeLine == 0) {
1700         int orphans = 0;
1701         int widows = 0;
1702         // find breaking line
1703         InlineRunBox *lineBox = firstLineBox();
1704         while (lineBox) {
1705             if (lineBox->isInlineFlowBox()) {
1706                 InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(lineBox);
1707                 if (flowBox->afterPageBreak()) {
1708                     break;
1709                 }
1710             }
1711             orphans++;
1712             lineBox = lineBox->nextLineBox();
1713         }
1714         InlineFlowBox *pageBreaker = static_cast<InlineFlowBox *>(lineBox);
1715         if (!pageBreaker) {
1716             goto no_break;
1717         }
1718         // count widows
1719         while (lineBox && widows < style()->widows()) {
1720             if (lineBox->hasTextChildren()) {
1721                 widows++;
1722             }
1723             lineBox = lineBox->nextLineBox();
1724         }
1725         // Widows rule broken and more orphans left to use
1726         if (widows < style()->widows() && orphans > 0) {
1727             // qCDebug(KHTML_LOG) << "Widows: " << widows;
1728             // Check if we have enough orphans after respecting widows count
1729             int newOrphans = orphans - (style()->widows() - widows);
1730             if (newOrphans < style()->orphans()) {
1731                 if (parent()->canClear(this, PageBreakHarder)) {
1732                     // Relayout to remove incorrect page-break
1733                     setNeedsPageClear(true);
1734                     setContainsPageBreak(false);
1735                     layoutInlineChildren(relayoutChildren, -1);
1736                     return;
1737                 }
1738             } else {
1739                 // Set hint and try again
1740                 layoutInlineChildren(relayoutChildren, newOrphans + 1);
1741                 return;
1742             }
1743         }
1744     }
1745 no_break:
1746 
1747     // in case we have a float on the last line, it might not be positioned up to now.
1748     // This has to be done before adding in the bottom border/padding, or the float will
1749     // include the padding incorrectly. -dwh
1750     positionNewFloats();
1751 
1752     // Now add in the bottom border/padding.
1753     m_height += toAdd;
1754 
1755     // Always make sure this is at least our height.
1756     m_overflowHeight = qMax(m_height, m_overflowHeight);
1757 
1758     // See if any lines spill out of the block.  If so, we need to update our overflow width.
1759     checkLinesForOverflow();
1760 
1761     // See if we have any lines that spill out of our block.  If we do, then we will
1762     // possibly need to truncate text.
1763     if (hasTextOverflow) {
1764         checkLinesForTextOverflow();
1765     }
1766 
1767 #if BIDI_DEBUG > 1
1768     qCDebug(KHTML_LOG) << " ------- bidi end " << this << " -------";
1769 #endif
1770     //qCDebug(KHTML_LOG) << "RenderBlock::layoutInlineChildren time used " << qt.elapsed();
1771     //qCDebug(KHTML_LOG) << "height = " << m_height;
1772 }
1773 
1774 RootInlineBox *RenderBlock::determineStartPosition(bool fullLayout, BidiIterator &start, BidiState &bidi)
1775 {
1776     RootInlineBox *curr = nullptr;
1777     RootInlineBox *last = nullptr;
1778     RenderObject *startObj = nullptr;
1779     int pos = 0;
1780 
1781     if (fullLayout) {
1782         // Nuke all our lines.
1783         // ### should be done already at this point... assert( !firstRootBox() )
1784         if (firstRootBox()) {
1785             RenderArena *arena = renderArena();
1786             curr = firstRootBox();
1787             while (curr) {
1788                 RootInlineBox *next = curr->nextRootBox();
1789                 curr->deleteLine(arena);
1790                 curr = next;
1791             }
1792             assert(!firstLineBox() && !lastLineBox());
1793         }
1794     } else {
1795         int cnt = 0;
1796         for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox()) {
1797             cnt++;
1798         }
1799         if (curr) {
1800             // qCDebug(KHTML_LOG) << "found dirty line at " << cnt;
1801             // We have a dirty line.
1802             if (RootInlineBox *prevRootBox = curr->prevRootBox()) {
1803                 // We have a previous line.
1804                 if (!prevRootBox->endsWithBreak() || (prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= static_cast<RenderText *>(prevRootBox->lineBreakObj())->stringLength()))
1805                     // The previous line didn't break cleanly or broke at a newline
1806                     // that has been deleted, so treat it as dirty too.
1807                 {
1808                     curr = prevRootBox;
1809                 }
1810             }
1811         } else {
1812             // qCDebug(KHTML_LOG) << "No dirty line found";
1813             // No dirty lines were found.
1814             // If the last line didn't break cleanly, treat it as dirty.
1815             if (lastRootBox() && !lastRootBox()->endsWithBreak()) {
1816                 curr = lastRootBox();
1817             }
1818         }
1819 
1820         // If we have no dirty lines, then last is just the last root box.
1821         last = curr ? curr->prevRootBox() : lastRootBox();
1822     }
1823 
1824     m_firstLine = !last;
1825     previousLineBrokeAtBR = !last || last->endsWithBreak();
1826     if (last) {
1827         m_height = last->blockHeight();
1828         startObj = last->lineBreakObj();
1829         pos = last->lineBreakPos();
1830         bidi.status = last->lineBreakBidiStatus();
1831     } else {
1832         startObj = first(this, &bidi, false);
1833     }
1834 
1835     start = BidiIterator(this, startObj, pos);
1836 
1837     return curr;
1838 }
1839 
1840 RootInlineBox *RenderBlock::determineEndPosition(RootInlineBox *startLine, BidiIterator &cleanLineStart, BidiStatus &cleanLineBidiStatus, BidiContext *cleanLineBidiContext, int &yPos)
1841 {
1842     RootInlineBox *last = nullptr;
1843     if (!startLine) {
1844         last = nullptr;
1845     } else {
1846         for (RootInlineBox *curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) {
1847             if (curr->isDirty()) {
1848                 last = nullptr;
1849             } else if (!last) {
1850                 last = curr;
1851             }
1852         }
1853     }
1854 
1855     if (!last) {
1856         return nullptr;
1857     }
1858 
1859     RootInlineBox *prev = last->prevRootBox();
1860     cleanLineStart = BidiIterator(this, prev->lineBreakObj(), prev->lineBreakPos());
1861     cleanLineBidiStatus = prev->lineBreakBidiStatus();
1862     cleanLineBidiContext = prev->lineBreakBidiContext();
1863     yPos = prev->blockHeight();
1864 
1865     return last;
1866 }
1867 
1868 bool RenderBlock::matchedEndLine(const BidiIterator &start, const BidiStatus &status, BidiContext *context,
1869                                  const BidiIterator &endLineStart, const BidiStatus &endLineStatus, BidiContext *endLineContext,
1870                                  RootInlineBox *&endLine, int &endYPos)
1871 {
1872     if (start == endLineStart) {
1873         return status == endLineStatus && endLineContext && (*context == *endLineContext);
1874     } else {
1875         // The first clean line doesn't match, but we can check a handful of following lines to try
1876         // to match back up.
1877         static int numLines = 8; // The # of lines we're willing to match against.
1878         RootInlineBox *line = endLine;
1879         for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) {
1880             if (line->lineBreakObj() == start.obj && line->lineBreakPos() == start.pos) {
1881                 // We have a match.
1882                 if ((line->lineBreakBidiStatus() != status) || (line->lineBreakBidiContext() != context)) {
1883                     return false;    // ...but the bidi state doesn't match.
1884                 }
1885                 RootInlineBox *result = line->nextRootBox();
1886 
1887                 // Set our yPos to be the block height of endLine.
1888                 if (result) {
1889                     endYPos = line->blockHeight();
1890                 }
1891 
1892                 // Now delete the lines that we failed to sync.
1893                 RootInlineBox *boxToDelete = endLine;
1894                 RenderArena *arena = renderArena();
1895                 while (boxToDelete && boxToDelete != result) {
1896                     RootInlineBox *next = boxToDelete->nextRootBox();
1897                     boxToDelete->deleteLine(arena);
1898                     boxToDelete = next;
1899                 }
1900 
1901                 endLine = result;
1902                 return result;
1903             }
1904         }
1905     }
1906     return false;
1907 }
1908 
1909 static void setStaticPosition(RenderBlock *p, RenderObject *o, bool *needToSetStaticX = nullptr, bool *needToSetStaticY = nullptr)
1910 {
1911     // If our original display wasn't an inline type, then we can
1912     // determine our static x position now.
1913     bool nssx, nssy;
1914     bool isInlineType = o->style()->isOriginalDisplayInlineType();
1915     nssx = o->hasStaticX();
1916     if (nssx && o->isBox()) {
1917         static_cast<RenderBox *>(o)->setStaticX(o->parent()->style()->direction() == LTR ?
1918                                                 p->borderLeft() + p->paddingLeft() :
1919                                                 p->borderRight() + p->paddingRight());
1920         nssx = isInlineType;
1921     }
1922 
1923     // If our original display was an INLINE type, then we can
1924     // determine our static y position now.
1925     nssy = o->hasStaticY();
1926     if (nssy && o->isBox()) {
1927         static_cast<RenderBox *>(o)->setStaticY(p->height());
1928         nssy = !isInlineType;
1929     }
1930     if (needToSetStaticX) {
1931         *needToSetStaticX = nssx;
1932     }
1933     if (needToSetStaticY) {
1934         *needToSetStaticY = nssy;
1935     }
1936 }
1937 
1938 static inline bool requiresLineBox(BidiIterator &it)
1939 {
1940     if (it.obj->isFloatingOrPositioned()) {
1941         return false;
1942     }
1943     if (it.obj->isInlineFlow()) {
1944         return (getBorderPaddingMargin(it.obj, it.endOfInline) != 0);
1945     }
1946     if (it.obj->isText() && !static_cast<RenderText *>(it.obj)->length()) {
1947         return false;
1948     }
1949     if (it.obj->style()->preserveWS() || it.obj->isBR()) {
1950         return true;
1951     }
1952 
1953     switch (it.current().unicode()) {
1954     case 0x0009: // ASCII tab
1955     case 0x000A: // ASCII line feed
1956     case 0x000C: // ASCII form feed
1957     case 0x0020: // ASCII space
1958     case 0x200B: // Zero-width space
1959         return false;
1960     }
1961     return true;
1962 }
1963 
1964 bool RenderBlock::inlineChildNeedsLineBox(RenderObject *inlineObj) // WC: generatesLineBoxesForInlineChild
1965 {
1966     assert(inlineObj->parent() == this);
1967 
1968     BidiIterator it(this, inlineObj, 0);
1969     while (!it.atEnd() && !requiresLineBox(it)) {
1970         it.increment(nullptr, false /*skipInlines*/);
1971     }
1972 
1973     return !it.atEnd();
1974 }
1975 
1976 void RenderBlock::fitBelowFloats(int widthToFit, int &availableWidth)
1977 {
1978     assert(widthToFit > availableWidth);
1979 
1980     int floatBottom;
1981     int lastFloatBottom = m_height;
1982     int newLineWidth = availableWidth;
1983     while (true) {
1984         floatBottom = nearestFloatBottom(lastFloatBottom);
1985         if (!floatBottom) {
1986             break;
1987         }
1988 
1989         newLineWidth = lineWidth(floatBottom);
1990         lastFloatBottom = floatBottom;
1991         if (newLineWidth >= widthToFit) {
1992             break;
1993         }
1994     }
1995     if (newLineWidth > availableWidth) {
1996         m_height = lastFloatBottom;
1997         availableWidth = newLineWidth;
1998 #ifdef DEBUG_LINEBREAKS
1999         qCDebug(KHTML_LOG) << " new position at " << m_height << " newWidth " << availableWidth;
2000 #endif
2001     }
2002 }
2003 
2004 BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi)
2005 {
2006     int width = lineWidth(m_height);
2007     int w = 0; // the width from the start of the line up to the currently chosen breaking opportunity
2008     int tmpW = 0; // the accumulated width since the last chosen breaking opportunity
2009 #ifdef DEBUG_LINEBREAKS
2010     qCDebug(KHTML_LOG) << "findNextLineBreak: line at " << m_height << " line width " << width;
2011     qCDebug(KHTML_LOG) << "sol: " << start.obj << " " << start.pos;
2012 #endif
2013 
2014     BidiIterator posStart = start;
2015     bool hadPosStart = false;
2016 
2017     // Skip initial whitespace
2018     while (!start.atEnd() && !requiresLineBox(start)) {
2019         if (start.obj->isFloating() || start.obj->isPosWithStaticDim()) {
2020             RenderObject *o = start.obj;
2021             // add to special objects...
2022             if (o->isFloating()) {
2023                 insertFloatingObject(o);
2024                 positionNewFloats();
2025                 width = lineWidth(m_height);
2026             } else if (o->isPositioned()) {
2027                 // add midpoints to have positioned objects at the correct static location
2028                 // while still skipping initial whitespace.
2029                 if (!hadPosStart) {
2030                     hadPosStart = true;
2031                     posStart = start;
2032                     // include this object then stop
2033                     addMidpoint(BidiIterator(nullptr, o, 0));
2034                 } else {
2035                     // start/stop
2036                     addMidpoint(BidiIterator(nullptr, o, 0));
2037                     addMidpoint(BidiIterator(nullptr, o, 0));
2038                 }
2039                 setStaticPosition(this, o);
2040             }
2041         }
2042         start.increment(&bidi, false /*skipInlines*/);
2043     }
2044 
2045     if (hadPosStart && !start.atEnd()) {
2046         addMidpoint(start);
2047     }
2048 
2049     if (start.atEnd()) {
2050         if (hadPosStart) {
2051             start = posStart;
2052             posStart.increment();
2053             return posStart;
2054         }
2055         return start;
2056     }
2057 
2058     // This variable says we have encountered an object after which initial whitespace should be ignored (e.g. InlineFlows at the beginning of a line).
2059     // Either we have nothing to do, if there is no whitespace after the object... or we have to enter the ignoringSpaces state.
2060     // This dilemma will be resolved when we have a peek at the next object.
2061     bool checkShouldIgnoreInitialWhitespace = false;
2062 
2063     // This variable is used only if whitespace isn't set to PRE, and it tells us whether
2064     // or not we are currently ignoring whitespace.
2065     bool ignoringSpaces = false;
2066     BidiIterator ignoreStart;
2067 
2068     // This variable tracks whether the very last character we saw was a space.  We use
2069     // this to detect when we encounter a second space so we know we have to terminate
2070     // a run.
2071     bool currentCharacterIsSpace = false;
2072 
2073     // This variable tracks whether there is space still available on the line for floating objects.
2074     // Once a floating object does not fit, we wait till next linebreak before positioning more floats.
2075     bool floatsFitOnLine = true;
2076 
2077     RenderObject *trailingSpaceObject = nullptr;
2078 
2079     BidiIterator lBreak = start;
2080     InlineMinMaxIterator it(start.par, start.obj, start.endOfInline, false /*skipPositioned*/);
2081     InlineMinMaxIterator lastIt = it;
2082     int pos = start.pos;
2083 
2084     bool prevLineBrokeCleanly = previousLineBrokeAtBR;
2085     previousLineBrokeAtBR = false;
2086 
2087     RenderObject *o = it.current;
2088     while (o) {
2089 #ifdef DEBUG_LINEBREAKS
2090         qCDebug(KHTML_LOG) << "new object " << o << " width = " << w << " tmpw = " << tmpW;
2091 #endif
2092         if (o->isBR()) {
2093             if (w + tmpW <= width) {
2094                 lBreak.obj = o;
2095                 lBreak.pos = 0;
2096                 lBreak.endOfInline = it.endOfInline;
2097 
2098                 // A <br> always breaks a line, so don't let the line be collapsed
2099                 // away. Also, the space at the end of a line with a <br> does not
2100                 // get collapsed away.  It only does this if the previous line broke
2101                 // cleanly.  Otherwise the <br> has no effect on whether the line is
2102                 // empty or not.
2103                 if (prevLineBrokeCleanly) {
2104                     isLineEmpty = false;
2105                 }
2106                 trailingSpaceObject = nullptr;
2107                 previousLineBrokeAtBR = true;
2108 
2109                 if (!isLineEmpty) {
2110                     // only check the clear status for non-empty lines.
2111                     EClear clear = o->style()->clear();
2112                     if (clear != CNONE) {
2113                         m_clearStatus = (EClear)(m_clearStatus | clear);
2114                     }
2115                 }
2116             }
2117             goto end;
2118         }
2119         if (o->isFloatingOrPositioned()) {
2120             // add to special objects...
2121             if (o->isFloating()) {
2122                 insertFloatingObject(o);
2123                 // check if it fits in the current line.
2124                 // If it does, position it now, otherwise, position
2125                 // it after moving to next line (in newLine() func)
2126                 if (floatsFitOnLine && o->width() + o->marginLeft() + o->marginRight() + w + tmpW <= width) {
2127                     positionNewFloats();
2128                     width = lineWidth(m_height);
2129                 } else {
2130                     floatsFitOnLine = false;
2131                 }
2132             } else if (o->isPositioned() && o->isPosWithStaticDim()) {
2133                 bool needToSetStaticX;
2134                 bool needToSetStaticY;
2135                 setStaticPosition(this, o, &needToSetStaticX, &needToSetStaticY);
2136 
2137                 // If we're ignoring spaces, we have to stop and include this object and
2138                 // then start ignoring spaces again.
2139                 if (needToSetStaticX || needToSetStaticY) {
2140                     trailingSpaceObject = nullptr;
2141                     ignoreStart.obj = o;
2142                     ignoreStart.pos = 0;
2143                     if (ignoringSpaces) {
2144                         addMidpoint(ignoreStart); // Stop ignoring spaces.
2145                         addMidpoint(ignoreStart); // Start ignoring again.
2146                     }
2147                 }
2148             }
2149         } else if (o->isInlineFlow()) {
2150             tmpW += getBorderPaddingMargin(o, it.endOfInline);
2151             if (isLineEmpty) {
2152                 isLineEmpty = !tmpW;
2153             }
2154             if (o->isWordBreak()) { // #### shouldn't be an InlineFlow!
2155                 w += tmpW;
2156                 tmpW = 0;
2157                 lBreak.obj = o;
2158                 lBreak.pos = 0;
2159                 lBreak.endOfInline = it.endOfInline;
2160             } else if (!it.endOfInline) {
2161                 // this is the beginning of the line (other non-initial inline flows are handled directly when
2162                 // incrementing the iterator below). We want to skip initial whitespace as much as possible.
2163                 checkShouldIgnoreInitialWhitespace = true;
2164             }
2165         } else if (o->isReplaced() || o->isGlyph()) {
2166             EWhiteSpace currWS = o->style()->whiteSpace();
2167             EWhiteSpace lastWS = lastIt.current->style()->whiteSpace();
2168 
2169             // WinIE marquees have different whitespace characteristics by default when viewed from
2170             // the outside vs. the inside.  Text inside is NOWRAP, and so we altered the marquee's
2171             // style to reflect this, but we now have to get back to the original whitespace value
2172             // for the marquee when checking for line breaking.
2173             if (o->isHTMLMarquee() && o->layer() && o->layer()->marquee()) {
2174                 currWS = o->layer()->marquee()->whiteSpace();
2175             }
2176             if (lastIt.current->isHTMLMarquee() && lastIt.current->layer() && lastIt.current->layer()->marquee()) {
2177                 lastWS = lastIt.current->layer()->marquee()->whiteSpace();
2178             }
2179 
2180             // Break on replaced elements if either has normal white-space.
2181             if (currWS == NORMAL || lastWS == NORMAL) {
2182                 w += tmpW;
2183                 tmpW = 0;
2184                 lBreak.obj = o;
2185                 lBreak.pos = 0;
2186                 lBreak.endOfInline = false;
2187             }
2188 
2189             tmpW += o->width() + o->marginLeft() + o->marginRight();
2190             if (ignoringSpaces) {
2191                 BidiIterator startMid(nullptr, o, 0);
2192                 addMidpoint(startMid);
2193             }
2194             isLineEmpty = false;
2195             ignoringSpaces = false;
2196             currentCharacterIsSpace = false;
2197             trailingSpaceObject = nullptr;
2198 
2199             if (o->isListMarker()) {
2200                 checkShouldIgnoreInitialWhitespace = true;
2201             }
2202         } else if (o->isText()) {
2203             RenderText *t = static_cast<RenderText *>(o);
2204             int strlen = t->stringLength();
2205             int len = strlen - pos;
2206             QChar *str = t->text();
2207 
2208             const Font *f = t->htmlFont(m_firstLine);
2209             // proportional font, needs a bit more work.
2210             int lastSpace = pos;
2211             bool autoWrap = o->style()->autoWrap();
2212             bool preserveWS = o->style()->preserveWS();
2213             bool preserveLF = o->style()->preserveLF();
2214 #ifdef APPLE_CHANGES
2215             int wordSpacing = o->style()->wordSpacing();
2216 #endif
2217             bool nextIsSoftBreakable = false;
2218             bool checkBreakWord = autoWrap && (o->style()->wordWrap() == WWBREAKWORD);
2219 
2220             while (len) {
2221                 bool previousCharacterIsSpace = currentCharacterIsSpace;
2222                 bool isSoftBreakable = nextIsSoftBreakable;
2223                 nextIsSoftBreakable = false;
2224                 const QChar c = str[pos];
2225                 currentCharacterIsSpace = c.unicode() == ' ';
2226                 checkBreakWord &= !w; // only break words when no other breaking opportunity exists earlier
2227                 // on the line (even within the text object we are currently processing)
2228 
2229                 if (preserveWS || !currentCharacterIsSpace) {
2230                     isLineEmpty = false;
2231                 }
2232 
2233                 // Check for soft hyphens.  Go ahead and ignore them.
2234                 if (c.unicode() == SOFT_HYPHEN && pos > 0) {
2235                     nextIsSoftBreakable = true;
2236                     if (!ignoringSpaces) {
2237                         // Ignore soft hyphens
2238                         BidiIterator endMid(nullptr, o, pos - 1);
2239                         addMidpoint(endMid);
2240 
2241                         // Add the width up to but not including the hyphen.
2242                         tmpW += t->width(lastSpace, pos - lastSpace, f);
2243 
2244                         // For wrapping text only, include the hyphen.  We need to ensure it will fit
2245                         // on the line if it shows when we break.
2246                         if (o->style()->autoWrap()) {
2247                             const QChar softHyphen(0x00ad);
2248                             tmpW += f->charWidth(&softHyphen, 1, 0, true);
2249                         }
2250 
2251                         BidiIterator startMid(nullptr, o, pos + 1);
2252                         addMidpoint(startMid);
2253                     }
2254 
2255                     pos++;
2256                     len--;
2257                     lastSpace = pos; // Cheesy hack to prevent adding in widths of the run twice.
2258                     continue;
2259                 }
2260 #ifdef APPLE_CHANGES    // KDE applies wordspacing differently
2261                 bool applyWordSpacing = false;
2262 #endif
2263                 if (ignoringSpaces) {
2264                     // We need to stop ignoring spaces, if we encounter a non-space or
2265                     // a run that doesn't collapse spaces.
2266                     if (!currentCharacterIsSpace || preserveWS) {
2267                         // Stop ignoring spaces and begin at this
2268                         // new point.
2269                         ignoringSpaces = false;
2270                         lastSpace = pos; // e.g., "Foo    goo", don't add in any of the ignored spaces.
2271                         BidiIterator startMid(nullptr, o, pos);
2272                         addMidpoint(startMid);
2273                     } else {
2274                         // Just keep ignoring these spaces.
2275                         pos++;
2276                         len--;
2277                         continue;
2278                     }
2279                 }
2280                 bool isbreakablePosition = (preserveLF && c.unicode() == '\n') || (autoWrap && (isBreakable(str, pos, strlen) || isSoftBreakable));
2281                 if (isbreakablePosition || checkBreakWord) {
2282                     tmpW += t->width(lastSpace, pos - lastSpace, f);
2283 #ifdef APPLE_CHANGES
2284                     applyWordSpacing = (wordSpacing && currentCharacterIsSpace && !previousCharacterIsSpace &&
2285                                         !t->containsOnlyWhitespace(pos + 1, strlen - (pos + 1)));
2286 #endif
2287 #ifdef DEBUG_LINEBREAKS
2288                     qCDebug(KHTML_LOG) << "found space at " << pos << " in string '" << QString(str, strlen).toLatin1().constData() << "' adding " << tmpW << " new width = " << w;
2289 #endif
2290                     if (!w && autoWrap && tmpW > width) {
2291                         fitBelowFloats(tmpW, width);
2292                     }
2293 
2294                     if (autoWrap) {
2295                         if (w + tmpW > width) {
2296                             if (checkBreakWord && pos) {
2297                                 lBreak.obj = o;
2298                                 lBreak.pos = pos - 1;
2299                                 lBreak.endOfInline = false;
2300                             }
2301                             goto end;
2302                         } else if ((pos > 1 && str[pos - 1].unicode() == SOFT_HYPHEN))
2303                             // Subtract the width of the soft hyphen out since we fit on a line.
2304                         {
2305                             tmpW -= t->width(pos - 1, 1, f);
2306                         }
2307                     }
2308 
2309                     if (preserveLF && (str + pos)->unicode() == '\n') {
2310                         lBreak.obj = o;
2311                         lBreak.pos = pos;
2312                         lBreak.endOfInline = false;
2313 
2314 #ifdef DEBUG_LINEBREAKS
2315                         qCDebug(KHTML_LOG) << "forced break sol: " << start.obj << " " << start.pos << "   end: " << lBreak.obj << " " << lBreak.pos << "   width=" << w;
2316 #endif
2317                         return lBreak;
2318                     }
2319 
2320                     if (autoWrap && isbreakablePosition) {
2321                         w += tmpW;
2322                         tmpW = 0;
2323                         lBreak.obj = o;
2324                         lBreak.pos = pos;
2325                         lBreak.endOfInline = false;
2326                     }
2327 
2328                     lastSpace = pos;
2329 #ifdef APPLE_CHANGES
2330                     if (applyWordSpacing) {
2331                         w += wordSpacing;
2332                     }
2333 #endif
2334                 }
2335 
2336                 if (!ignoringSpaces && !preserveWS) {
2337                     // If we encounter a second space, we need to go ahead and break up this run
2338                     // and enter a mode where we start collapsing spaces.
2339                     if (currentCharacterIsSpace && previousCharacterIsSpace) {
2340                         ignoringSpaces = true;
2341 
2342                         // We just entered a mode where we are ignoring
2343                         // spaces. Create a midpoint to terminate the run
2344                         // before the second space.
2345                         addMidpoint(ignoreStart);
2346                         lastSpace = pos;
2347                     }
2348                 }
2349 
2350                 if (currentCharacterIsSpace && !previousCharacterIsSpace) {
2351                     ignoreStart.obj = o;
2352                     ignoreStart.pos = pos;
2353                 }
2354 
2355                 if (!preserveWS && currentCharacterIsSpace && !ignoringSpaces) {
2356                     trailingSpaceObject = o;
2357                 } else if (preserveWS || !currentCharacterIsSpace) {
2358                     trailingSpaceObject = nullptr;
2359                 }
2360 
2361                 pos++;
2362                 len--;
2363             }
2364 
2365             if (!ignoringSpaces) {
2366                 // We didn't find any space that would be beyond the line |width|.
2367                 // Lets add to |tmpW| the remaining width since the last space we found.
2368                 // Before we test this new |tmpW| however, we will have to look ahead to check
2369                 // if the next object/position can serve as a line breaking opportunity.
2370                 tmpW += t->width(lastSpace, pos - lastSpace, f);
2371                 if (checkBreakWord && !w && pos && tmpW > width) {
2372                     // Avoid doing the costly lookahead for break-word,
2373                     // since we know we are allowed to break.
2374                     lBreak.obj = o;
2375                     lBreak.pos = pos - 1;
2376                     lBreak.endOfInline = false;
2377                     goto end;
2378                 }
2379             }
2380         } else {
2381             KHTMLAssert(false);
2382         }
2383 
2384         InlineMinMaxIterator savedIt = lastIt;
2385         lastIt = it;
2386         o = it.next();
2387 
2388         // Advance the iterator to the next non-inline-flow
2389         while (o && o->isInlineFlow() && !o->isWordBreak()) {
2390             tmpW += getBorderPaddingMargin(o, it.endOfInline);
2391             if (isLineEmpty) {
2392                 isLineEmpty = !tmpW;
2393             }
2394             o = it.next();
2395         }
2396 
2397         // All code below, until the end of the loop, is looking ahead the |it| object we just
2398         // advanced to, comparing it to the previous object |lastIt|.
2399 
2400         if (checkShouldIgnoreInitialWhitespace) {
2401             // Check if we should switch to ignoringSpaces state
2402             if (!style()->preserveWS() && it.current && it.current->isText()) {
2403                 const RenderText *rt = static_cast<RenderText *>(it.current);
2404                 if (rt->stringLength() > 0 && (rt->text()[0].category() == QChar::Separator_Space || rt->text()[0] == '\n')) {
2405                     currentCharacterIsSpace = true;
2406                     ignoringSpaces = true;
2407                     BidiIterator endMid(nullptr, lastIt.current, 0);
2408                     addMidpoint(endMid);
2409                 }
2410             }
2411             checkShouldIgnoreInitialWhitespace = false;
2412         }
2413 
2414         bool autoWrap = lastIt.current->style()->autoWrap();
2415         bool canBreak = !lBreak.obj || !lBreak.obj->isInlineFlow() || !lBreak.obj->firstChild();
2416 
2417         bool checkForBreak = autoWrap;
2418         if (canBreak) {
2419             if (!autoWrap && w && w + tmpW > width && lBreak.obj && !lastIt.current->style()->preserveLF())
2420                 // ### needs explanation
2421             {
2422                 checkForBreak = true;
2423             } else if (it.current && lastIt.current->isText() && it.current->isText() && !it.current->isBR()) {
2424                 // We are looking ahead the next text object to see if it continues a word started previously,
2425                 // or is a line-breaking opportunity.
2426                 if (autoWrap || it.current->style()->autoWrap()) {
2427                     if (currentCharacterIsSpace)
2428                         // "<i>s </i>top"
2429                         //      _    ^
2430                     {
2431                         checkForBreak = true;
2432                     } else {
2433                         // either "<i>c</i>ontinue" or "<i>s</i> top"
2434                         //            _    ^               _    ^
2435                         checkForBreak = false;
2436                         RenderText *nextText = static_cast<RenderText *>(it.current);
2437                         if (nextText->stringLength() != 0) {
2438                             QChar c = nextText->text()[0];
2439                             // If the next item is a space, then we may try to break.
2440                             // Otherwise the next text run continues our word (and so it needs to
2441                             // keep adding to |tmpW|).
2442                             if (c == ' ' || c == '\t' || (c == '\n' && !it.current->style()->preserveLF())) {
2443                                 checkForBreak = true;
2444                             }
2445                         }
2446 
2447                         bool willFitOnLine = (w + tmpW <= width);
2448                         if (!willFitOnLine && !w) {
2449                             fitBelowFloats(tmpW, width);
2450                             willFitOnLine = tmpW <= width;
2451                         }
2452                         bool canPlaceOnLine = willFitOnLine || !autoWrap;
2453                         if (canPlaceOnLine && checkForBreak) {
2454                             w += tmpW;
2455                             tmpW = 0;
2456                             lBreak.obj = it.current;
2457                             lBreak.pos = 0;
2458                             lBreak.endOfInline = it.endOfInline;
2459                         }
2460                     }
2461                 }
2462             }
2463 
2464             if (checkForBreak && (w + tmpW > width)) {
2465                 // if we have floats, try to get below them.
2466                 if (currentCharacterIsSpace && !ignoringSpaces && !lastIt.current->style()->preserveWS()) {
2467                     trailingSpaceObject = nullptr;
2468                 }
2469 
2470                 if (w) {
2471                     goto end;
2472                 }
2473                 fitBelowFloats(tmpW, width);
2474 
2475                 // |width| may have been adjusted because we got shoved down past a float (thus
2476                 // giving us more room), so we need to retest, and only jump to
2477                 // the end label if we still don't fit on the line. -dwh
2478                 if (w + tmpW > width) {
2479                     it = lastIt;
2480                     lastIt = savedIt;
2481                     o = it.current;
2482                     goto end;
2483                 }
2484             }
2485         }
2486         if (!lastIt.current->isFloatingOrPositioned() && lastIt.current->isReplaced() && lastIt.current->style()->autoWrap()) {
2487             // Go ahead and add in tmpW.
2488             w += tmpW;
2489             tmpW = 0;
2490             lBreak.obj = o;
2491             lBreak.pos = 0;
2492             lBreak.endOfInline = it.endOfInline;
2493         }
2494 
2495         // Clear out our character space bool, since inline <pre>s don't collapse whitespace
2496         // with adjacent inline normal/nowrap spans.
2497         if (lastIt.current->style()->preserveWS()) {
2498             currentCharacterIsSpace = false;
2499         }
2500 
2501         pos = 0;
2502     }
2503 
2504 #ifdef DEBUG_LINEBREAKS
2505     qCDebug(KHTML_LOG) << "end of par, width = " << width << " linewidth = " << w + tmpW;
2506 #endif
2507     if (w + tmpW <= width || (lastIt.current && !lastIt.current->style()->autoWrap())) {
2508         lBreak.obj = nullptr;
2509         lBreak.pos = 0;
2510         lBreak.endOfInline = false;
2511     }
2512 
2513 end:
2514     if (lBreak == start && !lBreak.obj->isBR()) {
2515         // Having an |lBreak| identical to our |start| at this point means the first suitable
2516         // break point |it.current| that we found was past |width|, so we jumped to the |end| label
2517         // before we could set this (overflowing) breaking opportunity. Let's set it now.
2518         if (style()->whiteSpace() == PRE) {
2519             // FIXME: Don't really understand this case.
2520             if (pos != 0) {
2521                 lBreak.obj = o;
2522                 lBreak.pos = pos - 1;
2523                 lBreak.endOfInline = it.endOfInline;
2524             } else {
2525                 lBreak.obj = lastIt.current;
2526                 lBreak.pos = lastIt.current->isText() ? lastIt.current->length() : 0;
2527                 lBreak.endOfInline = lastIt.endOfInline;
2528             }
2529         } else if (lBreak.obj) {
2530             lBreak.obj = o;
2531             lBreak.pos = (o && o->isText() ? pos : 0);
2532             lBreak.endOfInline = it.endOfInline;
2533         }
2534     }
2535 
2536     if (hadPosStart) {
2537         start = posStart;
2538     }
2539 
2540     if (lBreak == start) {
2541         // make sure we consume at least one char/object.
2542         lBreak.increment();
2543     }
2544 
2545 #ifdef DEBUG_LINEBREAKS
2546     qCDebug(KHTML_LOG) << "regular break sol: " << start.obj << " " << start.pos << "   end: " << lBreak.obj << " " << lBreak.pos << "   width=" << w;
2547 #endif
2548 
2549     // Sanity check our midpoints.
2550     checkMidpoints(lBreak);
2551 
2552     if (trailingSpaceObject) {
2553         // This object is either going to be part of the last midpoint, or it is going
2554         // to be the actual endpoint.  In both cases we just decrease our pos by 1 level to
2555         // exclude the space, allowing it to - in effect - collapse into the newline.
2556         if (sNumMidpoints % 2 == 1) {
2557             BidiIterator *midpoints = smidpoints->data();
2558             midpoints[sNumMidpoints - 1].pos--;
2559         }
2560         //else if (lBreak.pos > 0)
2561         //    lBreak.pos--;
2562         else if (lBreak.obj == nullptr && trailingSpaceObject->isText()) {
2563             // Add a new end midpoint that stops right at the very end.
2564             RenderText *text = static_cast<RenderText *>(trailingSpaceObject);
2565             unsigned pos = text->length() >= 2 ? text->length() - 2 : UINT_MAX;
2566             BidiIterator endMid(nullptr, trailingSpaceObject, pos);
2567             addMidpoint(endMid);
2568         }
2569     }
2570 
2571     // We might have made lBreak an iterator that points past the end
2572     // of the object. Do this adjustment to make it point to the start
2573     // of the next object instead to avoid confusing the rest of the
2574     // code.
2575     if (lBreak.pos > 0) {
2576         lBreak.pos--;
2577         lBreak.increment();
2578     }
2579 
2580     if (lBreak.obj && lBreak.pos >= 2 && lBreak.obj->isText()) {
2581         // For soft hyphens on line breaks, we have to chop out the midpoints that made us
2582         // ignore the hyphen so that it will render at the end of the line.
2583         QChar c = static_cast<RenderText *>(lBreak.obj)->text()[lBreak.pos - 1];
2584         if (c.unicode() == SOFT_HYPHEN) {
2585             chopMidpointsAt(lBreak.obj, lBreak.pos - 2);
2586         }
2587     }
2588 
2589     return lBreak;
2590 }
2591 
2592 void RenderBlock::checkLinesForOverflow()
2593 {
2594     for (RootInlineBox *curr = static_cast<khtml::RootInlineBox *>(firstLineBox()); curr; curr = static_cast<khtml::RootInlineBox *>(curr->nextLineBox())) {
2595 //         m_overflowLeft = min(curr->leftOverflow(), m_overflowLeft);
2596         m_overflowTop = qMin(curr->topOverflow(), m_overflowTop);
2597 //         m_overflowWidth = max(curr->rightOverflow(), m_overflowWidth);
2598         m_overflowHeight = qMax(curr->bottomOverflow(), m_overflowHeight);
2599     }
2600 }
2601 
2602 void RenderBlock::deleteEllipsisLineBoxes()
2603 {
2604     for (RootInlineBox *curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2605         curr->clearTruncation();
2606     }
2607 }
2608 
2609 void RenderBlock::checkLinesForTextOverflow()
2610 {
2611     // Determine the width of the ellipsis using the current font.
2612     QChar ellipsis = 0x2026; // FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP) if 0x2026 not renderable
2613     static QString ellipsisStr(ellipsis);
2614     const Font &firstLineFont = style(true)->htmlFont();
2615     const Font &font = style()->htmlFont();
2616     int firstLineEllipsisWidth = firstLineFont.charWidth(&ellipsis, 1, 0, true /*fast algo*/);
2617     int ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.charWidth(&ellipsis, 1, 0, true /*fast algo*/);
2618 
2619     // For LTR text truncation, we want to get the right edge of our padding box, and then we want to see
2620     // if the right edge of a line box exceeds that.  For RTL, we use the left edge of the padding box and
2621     // check the left edge of the line box to see if it is less
2622     // Include the scrollbar for overflow blocks, which means we want to use "contentWidth()"
2623     bool ltr = style()->direction() == LTR;
2624     for (RootInlineBox *curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2625         int blockEdge = ltr ? rightOffset(curr->yPos()) : leftOffset(curr->yPos());
2626         int lineBoxEdge = ltr ? curr->xPos() + curr->width() : curr->xPos();
2627         if ((ltr && lineBoxEdge > blockEdge) || (!ltr && lineBoxEdge < blockEdge)) {
2628             // This line spills out of our box in the appropriate direction.  Now we need to see if the line
2629             // can be truncated.  In order for truncation to be possible, the line must have sufficient space to
2630             // accommodate our truncation string, and no replaced elements (images, tables) can overlap the ellipsis
2631             // space.
2632             int width = curr == firstRootBox() ? firstLineEllipsisWidth : ellipsisWidth;
2633             if (curr->canAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width)) {
2634                 curr->placeEllipsis(ellipsisStr, ltr, blockEdge, width);
2635             }
2636         }
2637     }
2638 }
2639 
2640 // For --enable-final
2641 #undef BIDI_DEBUG
2642 #undef DEBUG_LINEBREAKS
2643 #undef DEBUG_LAYOUT
2644 
2645 }