File indexing completed on 2024-12-01 12:34:05

0001 /**
0002  * This file is part of the html renderer for KDE.
0003  *
0004  * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
0005  *           (C) 1999-2003 Antti Koivisto (koivisto@kde.org)
0006  *           (C) 2002-2003 Dirk Mueller (mueller@kde.org)
0007  *           (C) 2003-2007 Apple Computer, Inc.
0008  *           (C) 2007 Germain Garand (germain@ebooksfrance.org)
0009  *
0010  * This library is free software; you can redistribute it and/or
0011  * modify it under the terms of the GNU Library General Public
0012  * License as published by the Free Software Foundation; either
0013  * version 2 of the License, or (at your option) any later version.
0014  *
0015  * This library is distributed in the hope that it will be useful,
0016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0018  * Library General Public License for more details.
0019  *
0020  * You should have received a copy of the GNU Library General Public License
0021  * along with this library; see the file COPYING.LIB.  If not, write to
0022  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0023  * Boston, MA 02110-1301, USA.
0024  */
0025 // -------------------------------------------------------------------------
0026 
0027 #include "render_flow.h"
0028 
0029 #include "khtml_debug.h"
0030 #include <assert.h>
0031 #include <QPainter>
0032 
0033 #include "render_text.h"
0034 #include "render_table.h"
0035 #include "render_canvas.h"
0036 #include "render_inline.h"
0037 #include "render_block.h"
0038 #include "render_arena.h"
0039 #include <xml/dom_nodeimpl.h>
0040 #include <xml/dom_docimpl.h>
0041 #include <html/html_formimpl.h>
0042 
0043 #include <khtmlview.h>
0044 
0045 using namespace DOM;
0046 using namespace khtml;
0047 
0048 RenderFlow *RenderFlow::createFlow(DOM::NodeImpl *node, RenderStyle *style, RenderArena *arena)
0049 {
0050     RenderFlow *result;
0051     if (style->display() == INLINE) {
0052         result = new(arena) RenderInline(node);
0053     } else {
0054         result = new(arena) RenderBlock(node);
0055     }
0056     result->setStyle(style);
0057     return result;
0058 }
0059 
0060 RenderFlow *RenderFlow::continuationBefore(const RenderObject *beforeChild)
0061 {
0062     if (beforeChild && beforeChild->parent() == this) {
0063         return this;
0064     }
0065 
0066     RenderFlow *curr = continuation();
0067     RenderFlow *nextToLast = this;
0068     RenderFlow *last = this;
0069     while (curr) {
0070         if (beforeChild && beforeChild->parent() == curr) {
0071             if (curr->firstChild() == beforeChild) {
0072                 return last;
0073             }
0074             return curr;
0075         }
0076 
0077         nextToLast = last;
0078         last = curr;
0079         curr = curr->continuation();
0080     }
0081 
0082     if (!beforeChild && !last->firstChild()) {
0083         return nextToLast;
0084     }
0085     return last;
0086 }
0087 
0088 void RenderFlow::addChildWithContinuation(RenderObject *newChild, RenderObject *beforeChild)
0089 {
0090     RenderFlow *flow = continuationBefore(beforeChild);
0091 
0092     RenderObject *bc = beforeChild;
0093     while (bc && bc->parent() != flow && !bc->parent()->isAnonymousBlock()) {
0094         // skip implicit containers around beforeChild
0095         bc = bc->parent();
0096     }
0097 
0098     RenderFlow *beforeChildParent = bc ? static_cast<RenderFlow *>(bc->parent()) :
0099                                     (flow->continuation() ? flow->continuation() : flow);
0100 
0101     if (newChild->isFloatingOrPositioned()) {
0102         return beforeChildParent->addChildToFlow(newChild, beforeChild);
0103     }
0104 
0105     // A continuation always consists of two potential candidates: an inline or an anonymous
0106     // block box holding block children.
0107     bool childInline = newChild->isInline();
0108     bool bcpInline = beforeChildParent->isInline();
0109     bool flowInline = flow->isInline();
0110 
0111     if (flow == beforeChildParent) {
0112         return flow->addChildToFlow(newChild, beforeChild);
0113     } else {
0114         // The goal here is to match up if we can, so that we can coalesce and create the
0115         // minimal # of continuations needed for the inline.
0116         if (childInline == bcpInline) {
0117             return beforeChildParent->addChildToFlow(newChild, beforeChild);
0118         } else if (flowInline == childInline) {
0119             return flow->addChildToFlow(newChild, nullptr);    // Just treat like an append.
0120         } else {
0121             return beforeChildParent->addChildToFlow(newChild, beforeChild);
0122         }
0123     }
0124 }
0125 
0126 void RenderFlow::addChild(RenderObject *newChild, RenderObject *beforeChild)
0127 {
0128 #ifdef DEBUG_LAYOUT
0129     // qCDebug(KHTML_LOG) << renderName() << "(RenderFlow)::addChild( " << newChild->renderName() <<
0130     // ", " << (beforeChild ? beforeChild->renderName() : "0") << " )";
0131     // qCDebug(KHTML_LOG) << "current height = " << m_height;
0132 #endif
0133 
0134     if (continuation()) {
0135         return addChildWithContinuation(newChild, beforeChild);
0136     }
0137     return addChildToFlow(newChild, beforeChild);
0138 }
0139 
0140 void RenderFlow::extractLineBox(InlineFlowBox *box)
0141 {
0142     m_lastLineBox = box->prevFlowBox();
0143     if (box == m_firstLineBox) {
0144         m_firstLineBox = nullptr;
0145     }
0146     if (box->prevLineBox()) {
0147         box->prevLineBox()->setNextLineBox(nullptr);
0148     }
0149     box->setPreviousLineBox(nullptr);
0150     for (InlineRunBox *curr = box; curr; curr = curr->nextLineBox()) {
0151         curr->setExtracted();
0152     }
0153 }
0154 
0155 void RenderFlow::attachLineBox(InlineFlowBox *box)
0156 {
0157     if (m_lastLineBox) {
0158         m_lastLineBox->setNextLineBox(box);
0159         box->setPreviousLineBox(m_lastLineBox);
0160     } else {
0161         m_firstLineBox = box;
0162     }
0163     InlineFlowBox *last = box;
0164     for (InlineFlowBox *curr = box; curr; curr = curr->nextFlowBox()) {
0165         curr->setExtracted(false);
0166         last = curr;
0167     }
0168     m_lastLineBox = last;
0169 }
0170 
0171 void RenderFlow::removeInlineBox(InlineBox *_box)
0172 {
0173     if (_box->isInlineFlowBox()) {
0174         InlineFlowBox *box = static_cast<InlineFlowBox *>(_box);
0175         if (box == m_firstLineBox) {
0176             m_firstLineBox = box->nextFlowBox();
0177         }
0178         if (box == m_lastLineBox) {
0179             m_lastLineBox = box->prevFlowBox();
0180         }
0181         if (box->nextLineBox()) {
0182             box->nextLineBox()->setPreviousLineBox(box->prevLineBox());
0183         }
0184         if (box->prevLineBox()) {
0185             box->prevLineBox()->setNextLineBox(box->nextLineBox());
0186         }
0187     }
0188     RenderBox::removeInlineBox(_box);
0189 }
0190 
0191 void RenderFlow::deleteInlineBoxes(RenderArena *arena)
0192 {
0193     if (m_firstLineBox) {
0194         if (!arena) {
0195             arena = renderArena();
0196         }
0197         InlineRunBox *curr = m_firstLineBox, *next = nullptr;
0198         while (curr) {
0199             next = curr->nextLineBox();
0200             if (!curr->isPlaceHolderBox()) {
0201                 curr->detach(arena, true /*noRemove*/);
0202             }
0203             curr = next;
0204         }
0205         m_firstLineBox = nullptr;
0206         m_lastLineBox = nullptr;
0207     }
0208 }
0209 
0210 void RenderFlow::dirtyInlineBoxes(bool fullLayout, bool isRootLineBox)
0211 {
0212     if (!isRootLineBox && (isReplaced() || isPositioned())) {
0213         return RenderBox::dirtyInlineBoxes(fullLayout, isRootLineBox);
0214     }
0215 
0216     if (fullLayout) {
0217         deleteInlineBoxes();
0218     } else {
0219         for (InlineRunBox *curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
0220             curr->dirtyInlineBoxes();
0221         }
0222     }
0223 }
0224 
0225 void RenderFlow::deleteLastLineBox(RenderArena *arena)
0226 {
0227     if (m_lastLineBox) {
0228         if (!arena) {
0229             arena = renderArena();
0230         }
0231         InlineRunBox *curr = m_lastLineBox, *prev = m_lastLineBox;
0232         if (m_firstLineBox == m_lastLineBox) {
0233             m_firstLineBox = m_lastLineBox = nullptr;
0234         } else {
0235             prev = curr->prevLineBox();
0236             while (!prev->isInlineFlowBox()) {
0237                 prev = prev->prevLineBox();
0238                 prev->detach(arena);
0239             }
0240             m_lastLineBox = static_cast<InlineFlowBox *>(prev);
0241             prev->setNextLineBox(nullptr);
0242         }
0243         if (curr->parent()) {
0244             curr->parent()->removeFromLine(curr);
0245         }
0246         curr->detach(arena);
0247     }
0248 }
0249 
0250 InlineBox *RenderFlow::createInlineBox(bool makePlaceHolderBox, bool isRootLineBox)
0251 {
0252     if (!isRootLineBox &&
0253             (isReplaced() || makePlaceHolderBox)) {      // Inline tables and inline blocks
0254         return RenderBox::createInlineBox(false, false);    // (or positioned element placeholders).
0255     }
0256 
0257     InlineFlowBox *flowBox = nullptr;
0258     if (isInlineFlow()) {
0259         flowBox = new(renderArena()) InlineFlowBox(this);
0260     } else {
0261         flowBox = new(renderArena()) RootInlineBox(this);
0262     }
0263 
0264     if (!m_firstLineBox) {
0265         m_firstLineBox = m_lastLineBox = flowBox;
0266     } else {
0267         m_lastLineBox->setNextLineBox(flowBox);
0268         flowBox->setPreviousLineBox(m_lastLineBox);
0269         m_lastLineBox = flowBox;
0270     }
0271 
0272     return flowBox;
0273 }
0274 
0275 void RenderFlow::dirtyLinesFromChangedChild(RenderObject *child)
0276 {
0277     if (!parent() || (selfNeedsLayout() && !isInlineFlow()) || isTable()) {
0278         return;
0279     }
0280 
0281     // If we have no first line box, then just bail early.
0282     if (!firstLineBox()) {
0283         // For an empty inline, propagate the check up to our parent, unless the parent
0284         // is already dirty.
0285         if (isInline() && !parent()->selfNeedsLayout() && parent()->isInlineFlow()) {
0286             static_cast<RenderFlow *>(parent())->dirtyLinesFromChangedChild(this);
0287         }
0288         return;
0289     }
0290 
0291     // Try to figure out which line box we belong in.  First try to find a previous
0292     // line box by examining our siblings.  If we didn't find a line box, then use our
0293     // parent's first line box.
0294     RootInlineBox *box = nullptr;
0295     RenderObject *curr = nullptr;
0296     for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) {
0297         if (curr->isFloatingOrPositioned()) {
0298             continue;
0299         }
0300 
0301         if (curr->isReplaced() && curr->isBox()) {
0302             InlineBox *placeHolderBox = static_cast<RenderBox *>(curr)->placeHolderBox();
0303             if (placeHolderBox) {
0304                 box = placeHolderBox->root();
0305             }
0306         } else if (curr->isText()) {
0307             InlineTextBox *textBox = static_cast<RenderText *>(curr)->lastTextBox();
0308             if (textBox) {
0309                 box = textBox->root();
0310             }
0311         } else if (curr->isInlineFlow()) {
0312             InlineRunBox *runBox = static_cast<RenderFlow *>(curr)->lastLineBox();
0313             if (runBox) {
0314                 box = runBox->root();
0315             }
0316         }
0317 
0318         if (box) {
0319             break;
0320         }
0321     }
0322     if (!box) {
0323         box = firstLineBox()->root();
0324     }
0325 
0326     // If we found a line box, then dirty it.
0327     if (box) {
0328         RootInlineBox *adjacentBox;
0329         box->markDirty();
0330 
0331         // dirty the adjacent lines that might be affected
0332         // NOTE: we dirty the previous line because RootInlineBox objects cache
0333         // the address of the first object on the next line after a BR, which we may be
0334         // invalidating here.  For more info, see how RenderBlock::layoutInlineChildren
0335         // calls setLineBreakInfo with the result of findNextLineBreak.  findNextLineBreak,
0336         // despite the name, actually returns the first RenderObject after the BR.
0337 
0338         adjacentBox = box->prevRootBox();
0339         if (adjacentBox) {
0340             adjacentBox->markDirty();
0341         }
0342         if (child->isBR() || (curr && curr->isBR())) {
0343             adjacentBox = box->nextRootBox();
0344             if (adjacentBox) {
0345                 adjacentBox->markDirty();
0346             }
0347         }
0348     }
0349 }
0350 
0351 QList< QRectF > RenderFlow::getClientRects()
0352 {
0353     if (isRenderInline() && isInlineFlow()) {
0354         QList<QRectF> list;
0355 
0356         InlineFlowBox *child = firstLineBox();
0357         if (child) {
0358             int x = 0, y = 0;
0359             absolutePosition(x,y);
0360             do {
0361                 QRectF rect(x + child->xPos(), y + child->yPos(), child->width(), child->height());
0362                 list.append(clientRectToViewport(rect));
0363                 child = child->nextFlowBox();
0364             } while (child);
0365         }
0366 
0367         // In case our flow is splitted by blocks
0368         for (RenderObject *cont = continuation(); cont; cont = cont->continuation()) {
0369             list.append(cont->getClientRects());
0370         }
0371 
0372         // Empty Flow, return the Flow itself
0373         if (list.isEmpty()) {
0374             return RenderObject::getClientRects();
0375         }
0376 
0377         return list;
0378     } else {
0379         return RenderObject::getClientRects();
0380     }
0381 }
0382 
0383 void RenderFlow::detach()
0384 {
0385     if (continuation()) {
0386         continuation()->detach();
0387     }
0388     m_continuation = nullptr;
0389 
0390     // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will
0391     // properly dirty line boxes that they are removed from.  Effects that do :before/:after only on hover could crash otherwise.
0392     detachRemainingChildren();
0393 
0394     if (!documentBeingDestroyed()) {
0395         if (m_firstLineBox) {
0396             // We can't wait for RenderContainer::destroy to clear the selection,
0397             // because by then we will have nuked the line boxes.
0398             if (isSelectionBorder()) {
0399                 canvas()->clearSelection();
0400             }
0401 
0402             // If line boxes are contained inside a root, that means we're an inline.
0403             // In that case, we need to remove all the line boxes so that the parent
0404             // lines aren't pointing to deleted children. If the first line box does
0405             // not have a parent that means they are either already disconnected or
0406             // root lines that can just be destroyed without disconnecting.
0407             if (m_firstLineBox->parent()) {
0408                 for (InlineRunBox *box = m_firstLineBox; box; box = box->nextLineBox()) {
0409                     box->remove();
0410                 }
0411             }
0412 
0413             // If we are an anonymous block, then our line boxes might have children
0414             // that will outlast this block. In the non-anonymous block case those
0415             // children will be destroyed by the time we return from this function.
0416             if (isAnonymousBlock()) {
0417                 for (InlineFlowBox *box = m_firstLineBox; box; box = box->nextFlowBox()) {
0418                     while (InlineBox *childBox = box->firstChild()) {
0419                         childBox->remove();
0420                     }
0421                 }
0422             }
0423         } else if (isInline() && parent())
0424             // empty inlines propagate linebox dirtying to the parent
0425         {
0426             parent()->dirtyLinesFromChangedChild(this);
0427         }
0428     }
0429 
0430     deleteInlineBoxes();
0431 
0432     RenderBox::detach();
0433 }
0434 
0435 void RenderFlow::paintLines(PaintInfo &i, int _tx, int _ty)
0436 {
0437     // Only paint during the foreground/selection phases.
0438     if (i.phase != PaintActionForeground && i.phase != PaintActionSelection && i.phase != PaintActionOutline) {
0439         return;
0440     }
0441 
0442     if (!firstLineBox()) {
0443         return;
0444     }
0445 
0446     // We can check the first box and last box and avoid painting if we don't
0447     // intersect.  This is a quick short-circuit that we can take to avoid walking any lines.
0448     // FIXME: This check is flawed in two extremely obscure ways.
0449     // (1) If some line in the middle has a huge overflow, it might actually extend below the last line.
0450     // (2) The overflow from an inline block on a line is not reported to the line.
0451     int maxOutlineSize = maximalOutlineSize(i.phase);
0452     int yPos = firstLineBox()->root()->topOverflow() - maxOutlineSize;
0453     int h = maxOutlineSize + lastLineBox()->root()->bottomOverflow() - yPos;
0454     yPos += _ty;
0455     if ((yPos >= i.r.y() + i.r.height()) || (yPos + h <= i.r.y())) {
0456         return;
0457     }
0458     for (InlineFlowBox *curr = firstLineBox(); curr; curr = curr->nextFlowBox()) {
0459         yPos = curr->root()->topOverflow() - maxOutlineSize;
0460         h = curr->root()->bottomOverflow() + maxOutlineSize - yPos;
0461         yPos += _ty;
0462         if ((yPos < i.r.y() + i.r.height()) && (yPos + h > i.r.y())) {
0463             curr->paint(i, _tx, _ty);
0464         }
0465     }
0466 
0467     if (i.phase == PaintActionOutline && i.outlineObjects) {
0468         foreach (RenderFlow *oo, *i.outlineObjects)
0469             if (oo->isRenderInline()) {
0470                 static_cast<RenderInline *>(oo)->paintOutlines(i.p, _tx, _ty);
0471             }
0472         i.outlineObjects->clear();
0473     }
0474 }
0475 
0476 bool RenderFlow::hitTestLines(NodeInfo &i, int x, int y, int tx, int ty, HitTestAction hitTestAction)
0477 {
0478     (void) hitTestAction;
0479     /*
0480      if (hitTestAction != HitTestForeground) // ### port hitTest
0481          return false;
0482     */
0483 
0484     if (!firstLineBox()) {
0485         return false;
0486     }
0487 
0488     // We can check the first box and last box and avoid hit testing if we don't
0489     // contain the point.  This is a quick short-circuit that we can take to avoid walking any lines.
0490     // FIXME: This check is flawed in two extremely obscure ways.
0491     // (1) If some line in the middle has a huge overflow, it might actually extend below the last line.
0492     // (2) The overflow from an inline block on a line is not reported to the line.
0493     if ((y >= ty + lastLineBox()->root()->bottomOverflow()) || (y < ty + firstLineBox()->root()->topOverflow())) {
0494         return false;
0495     }
0496 
0497     // See if our root lines contain the point.  If so, then we hit test
0498     // them further.  Note that boxes can easily overlap, so we can't make any assumptions
0499     // based off positions of our first line box or our last line box.
0500     for (InlineFlowBox *curr = lastLineBox(); curr; curr = curr->prevFlowBox()) {
0501         if (y >= ty + curr->root()->topOverflow() && y < ty + curr->root()->bottomOverflow()) {
0502             bool inside = curr->nodeAtPoint(i, x, y, tx, ty);
0503             if (inside) {
0504                 setInnerNode(i);
0505                 return true;
0506             }
0507         }
0508     }
0509 
0510     return false;
0511 }
0512 
0513 void RenderFlow::repaint(Priority prior)
0514 {
0515     if (isInlineFlow()) {
0516         // Find our leftmost position.
0517         int left = 0;
0518         // root inline box not reliably availabe during relayout
0519         int top = firstLineBox() ? (
0520                       needsLayout() ? firstLineBox()->xPos() : firstLineBox()->root()->topOverflow()
0521                   ) : 0;
0522         for (InlineRunBox *curr = firstLineBox(); curr; curr = curr->nextLineBox())
0523             if (curr == firstLineBox() || curr->xPos() < left) {
0524                 left = curr->xPos();
0525             }
0526 
0527         // Now invalidate a rectangle.
0528         int ow = style() ? style()->outlineSize() : 0;
0529 
0530         // We need to add in the relative position offsets of any inlines (including us) up to our
0531         // containing block.
0532         RenderBlock *cb = containingBlock();
0533         for (RenderObject *inlineFlow = this; inlineFlow && inlineFlow->isInlineFlow() && inlineFlow != cb;
0534                 inlineFlow = inlineFlow->parent()) {
0535             if (inlineFlow->style() && inlineFlow->style()->position() == PRELATIVE && inlineFlow->layer()) {
0536                 KHTMLAssert(inlineFlow->isBox());
0537                 static_cast<RenderBox *>(inlineFlow)->relativePositionOffset(left, top);
0538             }
0539         }
0540 
0541         RootInlineBox *lastRoot = lastLineBox() && !needsLayout() ? lastLineBox()->root() : nullptr;
0542         containingBlock()->repaintRectangle(-ow + left, -ow + top,
0543                                             width() + ow * 2,
0544                                             (lastRoot ? lastRoot->bottomOverflow() - top : height()) + ow * 2, prior);
0545     } else {
0546         if (firstLineBox() && firstLineBox()->topOverflow() < 0) {
0547             int ow = style() ? style()->outlineSize() : 0;
0548             repaintRectangle(-ow, -ow + firstLineBox()->topOverflow(),
0549                              effectiveWidth() + ow * 2, effectiveHeight() + ow * 2, prior);
0550         } else {
0551             return RenderBox::repaint(prior);
0552         }
0553     }
0554 }
0555 
0556 int
0557 RenderFlow::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
0558 {
0559     int bottom = includeSelf && m_width > 0 ? m_height : 0;
0560     if (!includeOverflowInterior && hasOverflowClip()) {
0561         return bottom;
0562     }
0563 
0564     // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
0565     // For now, we have to descend into all the children, since we may have a huge abs div inside
0566     // a tiny rel div buried somewhere deep in our child tree.  In this case we have to get to
0567     // the abs div.
0568     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
0569         if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) {
0570             int lp = c->yPos() + c->lowestPosition(false);
0571             bottom = qMax(bottom, lp);
0572         }
0573     }
0574 
0575     if (includeSelf && isRelPositioned()) {
0576         int x = 0;
0577         relativePositionOffset(x, bottom);
0578     }
0579 
0580     return bottom;
0581 }
0582 
0583 int RenderFlow::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
0584 {
0585     int right = includeSelf && m_height > 0 ? m_width : 0;
0586     if (!includeOverflowInterior && hasOverflowClip()) {
0587         return right;
0588     }
0589 
0590     // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
0591     // For now, we have to descend into all the children, since we may have a huge abs div inside
0592     // a tiny rel div buried somewhere deep in our child tree.  In this case we have to get to
0593     // the abs div.
0594     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
0595         if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) {
0596             int rp = c->xPos() + c->rightmostPosition(false);
0597             right = qMax(right, rp);
0598         }
0599     }
0600 
0601     if (includeSelf && isRelPositioned()) {
0602         int y = 0;
0603         relativePositionOffset(right, y);
0604     }
0605 
0606     return right;
0607 }
0608 
0609 int RenderFlow::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
0610 {
0611     int left = includeSelf && m_height > 0 ? 0 : m_width;
0612     if (!includeOverflowInterior && hasOverflowClip()) {
0613         return left;
0614     }
0615 
0616     // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
0617     // For now, we have to descend into all the children, since we may have a huge abs div inside
0618     // a tiny rel div buried somewhere deep in our child tree.  In this case we have to get to
0619     // the abs div.
0620     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
0621         if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) {
0622             int lp = c->xPos() + c->leftmostPosition(false);
0623             left = qMin(left, lp);
0624         }
0625     }
0626 
0627     if (includeSelf && isRelPositioned()) {
0628         int y = 0;
0629         relativePositionOffset(left, y);
0630     }
0631 
0632     return left;
0633 }
0634 
0635 int RenderFlow::highestPosition(bool includeOverflowInterior, bool includeSelf) const
0636 {
0637     int top = RenderBox::highestPosition(includeOverflowInterior, includeSelf);
0638     if (!includeOverflowInterior && hasOverflowClip()) {
0639         return top;
0640     }
0641 
0642     // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
0643     // For now, we have to descend into all the children, since we may have a huge abs div inside
0644     // a tiny rel div buried somewhere deep in our child tree.  In this case we have to get to
0645     // the abs div.
0646     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
0647         if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) {
0648             int hp = c->yPos() + c->highestPosition(false);
0649             top = qMin(top, hp);
0650         }
0651     }
0652 
0653     if (includeSelf && isRelPositioned()) {
0654         int x = 0;
0655         relativePositionOffset(x, top);
0656     }
0657 
0658     return top;
0659 }