1 /**
2  * This file is part of the html renderer for KDE.
3  *
4  * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
5  *           (C) 1999-2003 Antti Koivisto (koivisto@kde.org)
6  *           (C) 2002-2003 Dirk Mueller (mueller@kde.org)
7  *           (C) 2003-2007 Apple Computer, Inc.
8  *           (C) 2007 Germain Garand (germain@ebooksfrance.org)
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25 // -------------------------------------------------------------------------
26 
27 #include "render_flow.h"
28 
29 #include "khtml_debug.h"
30 #include <assert.h>
31 #include <QPainter>
32 
33 #include "render_text.h"
34 #include "render_table.h"
35 #include "render_canvas.h"
36 #include "render_inline.h"
37 #include "render_block.h"
38 #include "render_arena.h"
39 #include <xml/dom_nodeimpl.h>
40 #include <xml/dom_docimpl.h>
41 #include <html/html_formimpl.h>
42 
43 #include <khtmlview.h>
44 
45 using namespace DOM;
46 using namespace khtml;
47 
createFlow(DOM::NodeImpl * node,RenderStyle * style,RenderArena * arena)48 RenderFlow *RenderFlow::createFlow(DOM::NodeImpl *node, RenderStyle *style, RenderArena *arena)
49 {
50     RenderFlow *result;
51     if (style->display() == INLINE) {
52         result = new(arena) RenderInline(node);
53     } else {
54         result = new(arena) RenderBlock(node);
55     }
56     result->setStyle(style);
57     return result;
58 }
59 
continuationBefore(const RenderObject * beforeChild)60 RenderFlow *RenderFlow::continuationBefore(const RenderObject *beforeChild)
61 {
62     if (beforeChild && beforeChild->parent() == this) {
63         return this;
64     }
65 
66     RenderFlow *curr = continuation();
67     RenderFlow *nextToLast = this;
68     RenderFlow *last = this;
69     while (curr) {
70         if (beforeChild && beforeChild->parent() == curr) {
71             if (curr->firstChild() == beforeChild) {
72                 return last;
73             }
74             return curr;
75         }
76 
77         nextToLast = last;
78         last = curr;
79         curr = curr->continuation();
80     }
81 
82     if (!beforeChild && !last->firstChild()) {
83         return nextToLast;
84     }
85     return last;
86 }
87 
addChildWithContinuation(RenderObject * newChild,RenderObject * beforeChild)88 void RenderFlow::addChildWithContinuation(RenderObject *newChild, RenderObject *beforeChild)
89 {
90     RenderFlow *flow = continuationBefore(beforeChild);
91 
92     RenderObject *bc = beforeChild;
93     while (bc && bc->parent() != flow && !bc->parent()->isAnonymousBlock()) {
94         // skip implicit containers around beforeChild
95         bc = bc->parent();
96     }
97 
98     RenderFlow *beforeChildParent = bc ? static_cast<RenderFlow *>(bc->parent()) :
99                                     (flow->continuation() ? flow->continuation() : flow);
100 
101     if (newChild->isFloatingOrPositioned()) {
102         return beforeChildParent->addChildToFlow(newChild, beforeChild);
103     }
104 
105     // A continuation always consists of two potential candidates: an inline or an anonymous
106     // block box holding block children.
107     bool childInline = newChild->isInline();
108     bool bcpInline = beforeChildParent->isInline();
109     bool flowInline = flow->isInline();
110 
111     if (flow == beforeChildParent) {
112         return flow->addChildToFlow(newChild, beforeChild);
113     } else {
114         // The goal here is to match up if we can, so that we can coalesce and create the
115         // minimal # of continuations needed for the inline.
116         if (childInline == bcpInline) {
117             return beforeChildParent->addChildToFlow(newChild, beforeChild);
118         } else if (flowInline == childInline) {
119             return flow->addChildToFlow(newChild, nullptr);    // Just treat like an append.
120         } else {
121             return beforeChildParent->addChildToFlow(newChild, beforeChild);
122         }
123     }
124 }
125 
addChild(RenderObject * newChild,RenderObject * beforeChild)126 void RenderFlow::addChild(RenderObject *newChild, RenderObject *beforeChild)
127 {
128 #ifdef DEBUG_LAYOUT
129     // qCDebug(KHTML_LOG) << renderName() << "(RenderFlow)::addChild( " << newChild->renderName() <<
130     // ", " << (beforeChild ? beforeChild->renderName() : "0") << " )";
131     // qCDebug(KHTML_LOG) << "current height = " << m_height;
132 #endif
133 
134     if (continuation()) {
135         return addChildWithContinuation(newChild, beforeChild);
136     }
137     return addChildToFlow(newChild, beforeChild);
138 }
139 
extractLineBox(InlineFlowBox * box)140 void RenderFlow::extractLineBox(InlineFlowBox *box)
141 {
142     m_lastLineBox = box->prevFlowBox();
143     if (box == m_firstLineBox) {
144         m_firstLineBox = nullptr;
145     }
146     if (box->prevLineBox()) {
147         box->prevLineBox()->setNextLineBox(nullptr);
148     }
149     box->setPreviousLineBox(nullptr);
150     for (InlineRunBox *curr = box; curr; curr = curr->nextLineBox()) {
151         curr->setExtracted();
152     }
153 }
154 
attachLineBox(InlineFlowBox * box)155 void RenderFlow::attachLineBox(InlineFlowBox *box)
156 {
157     if (m_lastLineBox) {
158         m_lastLineBox->setNextLineBox(box);
159         box->setPreviousLineBox(m_lastLineBox);
160     } else {
161         m_firstLineBox = box;
162     }
163     InlineFlowBox *last = box;
164     for (InlineFlowBox *curr = box; curr; curr = curr->nextFlowBox()) {
165         curr->setExtracted(false);
166         last = curr;
167     }
168     m_lastLineBox = last;
169 }
170 
removeInlineBox(InlineBox * _box)171 void RenderFlow::removeInlineBox(InlineBox *_box)
172 {
173     if (_box->isInlineFlowBox()) {
174         InlineFlowBox *box = static_cast<InlineFlowBox *>(_box);
175         if (box == m_firstLineBox) {
176             m_firstLineBox = box->nextFlowBox();
177         }
178         if (box == m_lastLineBox) {
179             m_lastLineBox = box->prevFlowBox();
180         }
181         if (box->nextLineBox()) {
182             box->nextLineBox()->setPreviousLineBox(box->prevLineBox());
183         }
184         if (box->prevLineBox()) {
185             box->prevLineBox()->setNextLineBox(box->nextLineBox());
186         }
187     }
188     RenderBox::removeInlineBox(_box);
189 }
190 
deleteInlineBoxes(RenderArena * arena)191 void RenderFlow::deleteInlineBoxes(RenderArena *arena)
192 {
193     if (m_firstLineBox) {
194         if (!arena) {
195             arena = renderArena();
196         }
197         InlineRunBox *curr = m_firstLineBox, *next = nullptr;
198         while (curr) {
199             next = curr->nextLineBox();
200             if (!curr->isPlaceHolderBox()) {
201                 curr->detach(arena, true /*noRemove*/);
202             }
203             curr = next;
204         }
205         m_firstLineBox = nullptr;
206         m_lastLineBox = nullptr;
207     }
208 }
209 
dirtyInlineBoxes(bool fullLayout,bool isRootLineBox)210 void RenderFlow::dirtyInlineBoxes(bool fullLayout, bool isRootLineBox)
211 {
212     if (!isRootLineBox && (isReplaced() || isPositioned())) {
213         return RenderBox::dirtyInlineBoxes(fullLayout, isRootLineBox);
214     }
215 
216     if (fullLayout) {
217         deleteInlineBoxes();
218     } else {
219         for (InlineRunBox *curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
220             curr->dirtyInlineBoxes();
221         }
222     }
223 }
224 
deleteLastLineBox(RenderArena * arena)225 void RenderFlow::deleteLastLineBox(RenderArena *arena)
226 {
227     if (m_lastLineBox) {
228         if (!arena) {
229             arena = renderArena();
230         }
231         InlineRunBox *curr = m_lastLineBox, *prev = m_lastLineBox;
232         if (m_firstLineBox == m_lastLineBox) {
233             m_firstLineBox = m_lastLineBox = nullptr;
234         } else {
235             prev = curr->prevLineBox();
236             while (!prev->isInlineFlowBox()) {
237                 prev = prev->prevLineBox();
238                 prev->detach(arena);
239             }
240             m_lastLineBox = static_cast<InlineFlowBox *>(prev);
241             prev->setNextLineBox(nullptr);
242         }
243         if (curr->parent()) {
244             curr->parent()->removeFromLine(curr);
245         }
246         curr->detach(arena);
247     }
248 }
249 
createInlineBox(bool makePlaceHolderBox,bool isRootLineBox)250 InlineBox *RenderFlow::createInlineBox(bool makePlaceHolderBox, bool isRootLineBox)
251 {
252     if (!isRootLineBox &&
253             (isReplaced() || makePlaceHolderBox)) {      // Inline tables and inline blocks
254         return RenderBox::createInlineBox(false, false);    // (or positioned element placeholders).
255     }
256 
257     InlineFlowBox *flowBox = nullptr;
258     if (isInlineFlow()) {
259         flowBox = new(renderArena()) InlineFlowBox(this);
260     } else {
261         flowBox = new(renderArena()) RootInlineBox(this);
262     }
263 
264     if (!m_firstLineBox) {
265         m_firstLineBox = m_lastLineBox = flowBox;
266     } else {
267         m_lastLineBox->setNextLineBox(flowBox);
268         flowBox->setPreviousLineBox(m_lastLineBox);
269         m_lastLineBox = flowBox;
270     }
271 
272     return flowBox;
273 }
274 
dirtyLinesFromChangedChild(RenderObject * child)275 void RenderFlow::dirtyLinesFromChangedChild(RenderObject *child)
276 {
277     if (!parent() || (selfNeedsLayout() && !isInlineFlow()) || isTable()) {
278         return;
279     }
280 
281     // If we have no first line box, then just bail early.
282     if (!firstLineBox()) {
283         // For an empty inline, propagate the check up to our parent, unless the parent
284         // is already dirty.
285         if (isInline() && !parent()->selfNeedsLayout() && parent()->isInlineFlow()) {
286             static_cast<RenderFlow *>(parent())->dirtyLinesFromChangedChild(this);
287         }
288         return;
289     }
290 
291     // Try to figure out which line box we belong in.  First try to find a previous
292     // line box by examining our siblings.  If we didn't find a line box, then use our
293     // parent's first line box.
294     RootInlineBox *box = nullptr;
295     RenderObject *curr = nullptr;
296     for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) {
297         if (curr->isFloatingOrPositioned()) {
298             continue;
299         }
300 
301         if (curr->isReplaced() && curr->isBox()) {
302             InlineBox *placeHolderBox = static_cast<RenderBox *>(curr)->placeHolderBox();
303             if (placeHolderBox) {
304                 box = placeHolderBox->root();
305             }
306         } else if (curr->isText()) {
307             InlineTextBox *textBox = static_cast<RenderText *>(curr)->lastTextBox();
308             if (textBox) {
309                 box = textBox->root();
310             }
311         } else if (curr->isInlineFlow()) {
312             InlineRunBox *runBox = static_cast<RenderFlow *>(curr)->lastLineBox();
313             if (runBox) {
314                 box = runBox->root();
315             }
316         }
317 
318         if (box) {
319             break;
320         }
321     }
322     if (!box) {
323         box = firstLineBox()->root();
324     }
325 
326     // If we found a line box, then dirty it.
327     if (box) {
328         RootInlineBox *adjacentBox;
329         box->markDirty();
330 
331         // dirty the adjacent lines that might be affected
332         // NOTE: we dirty the previous line because RootInlineBox objects cache
333         // the address of the first object on the next line after a BR, which we may be
334         // invalidating here.  For more info, see how RenderBlock::layoutInlineChildren
335         // calls setLineBreakInfo with the result of findNextLineBreak.  findNextLineBreak,
336         // despite the name, actually returns the first RenderObject after the BR.
337 
338         adjacentBox = box->prevRootBox();
339         if (adjacentBox) {
340             adjacentBox->markDirty();
341         }
342         if (child->isBR() || (curr && curr->isBR())) {
343             adjacentBox = box->nextRootBox();
344             if (adjacentBox) {
345                 adjacentBox->markDirty();
346             }
347         }
348     }
349 }
350 
getClientRects()351 QList< QRectF > RenderFlow::getClientRects()
352 {
353     if (isRenderInline() && isInlineFlow()) {
354         QList<QRectF> list;
355 
356         InlineFlowBox *child = firstLineBox();
357         if (child) {
358             int x = 0, y = 0;
359             absolutePosition(x,y);
360             do {
361                 QRectF rect(x + child->xPos(), y + child->yPos(), child->width(), child->height());
362                 list.append(clientRectToViewport(rect));
363                 child = child->nextFlowBox();
364             } while (child);
365         }
366 
367         // In case our flow is splitted by blocks
368         for (RenderObject *cont = continuation(); cont; cont = cont->continuation()) {
369             list.append(cont->getClientRects());
370         }
371 
372         // Empty Flow, return the Flow itself
373         if (list.isEmpty()) {
374             return RenderObject::getClientRects();
375         }
376 
377         return list;
378     } else {
379         return RenderObject::getClientRects();
380     }
381 }
382 
detach()383 void RenderFlow::detach()
384 {
385     if (continuation()) {
386         continuation()->detach();
387     }
388     m_continuation = nullptr;
389 
390     // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will
391     // properly dirty line boxes that they are removed from.  Effects that do :before/:after only on hover could crash otherwise.
392     detachRemainingChildren();
393 
394     if (!documentBeingDestroyed()) {
395         if (m_firstLineBox) {
396             // We can't wait for RenderContainer::destroy to clear the selection,
397             // because by then we will have nuked the line boxes.
398             if (isSelectionBorder()) {
399                 canvas()->clearSelection();
400             }
401 
402             // If line boxes are contained inside a root, that means we're an inline.
403             // In that case, we need to remove all the line boxes so that the parent
404             // lines aren't pointing to deleted children. If the first line box does
405             // not have a parent that means they are either already disconnected or
406             // root lines that can just be destroyed without disconnecting.
407             if (m_firstLineBox->parent()) {
408                 for (InlineRunBox *box = m_firstLineBox; box; box = box->nextLineBox()) {
409                     box->remove();
410                 }
411             }
412 
413             // If we are an anonymous block, then our line boxes might have children
414             // that will outlast this block. In the non-anonymous block case those
415             // children will be destroyed by the time we return from this function.
416             if (isAnonymousBlock()) {
417                 for (InlineFlowBox *box = m_firstLineBox; box; box = box->nextFlowBox()) {
418                     while (InlineBox *childBox = box->firstChild()) {
419                         childBox->remove();
420                     }
421                 }
422             }
423         } else if (isInline() && parent())
424             // empty inlines propagate linebox dirtying to the parent
425         {
426             parent()->dirtyLinesFromChangedChild(this);
427         }
428     }
429 
430     deleteInlineBoxes();
431 
432     RenderBox::detach();
433 }
434 
paintLines(PaintInfo & i,int _tx,int _ty)435 void RenderFlow::paintLines(PaintInfo &i, int _tx, int _ty)
436 {
437     // Only paint during the foreground/selection phases.
438     if (i.phase != PaintActionForeground && i.phase != PaintActionSelection && i.phase != PaintActionOutline) {
439         return;
440     }
441 
442     if (!firstLineBox()) {
443         return;
444     }
445 
446     // We can check the first box and last box and avoid painting if we don't
447     // intersect.  This is a quick short-circuit that we can take to avoid walking any lines.
448     // FIXME: This check is flawed in two extremely obscure ways.
449     // (1) If some line in the middle has a huge overflow, it might actually extend below the last line.
450     // (2) The overflow from an inline block on a line is not reported to the line.
451     int maxOutlineSize = maximalOutlineSize(i.phase);
452     int yPos = firstLineBox()->root()->topOverflow() - maxOutlineSize;
453     int h = maxOutlineSize + lastLineBox()->root()->bottomOverflow() - yPos;
454     yPos += _ty;
455     if ((yPos >= i.r.y() + i.r.height()) || (yPos + h <= i.r.y())) {
456         return;
457     }
458     for (InlineFlowBox *curr = firstLineBox(); curr; curr = curr->nextFlowBox()) {
459         yPos = curr->root()->topOverflow() - maxOutlineSize;
460         h = curr->root()->bottomOverflow() + maxOutlineSize - yPos;
461         yPos += _ty;
462         if ((yPos < i.r.y() + i.r.height()) && (yPos + h > i.r.y())) {
463             curr->paint(i, _tx, _ty);
464         }
465     }
466 
467     if (i.phase == PaintActionOutline && i.outlineObjects) {
468         foreach (RenderFlow *oo, *i.outlineObjects)
469             if (oo->isRenderInline()) {
470                 static_cast<RenderInline *>(oo)->paintOutlines(i.p, _tx, _ty);
471             }
472         i.outlineObjects->clear();
473     }
474 }
475 
hitTestLines(NodeInfo & i,int x,int y,int tx,int ty,HitTestAction hitTestAction)476 bool RenderFlow::hitTestLines(NodeInfo &i, int x, int y, int tx, int ty, HitTestAction hitTestAction)
477 {
478     (void) hitTestAction;
479     /*
480      if (hitTestAction != HitTestForeground) // ### port hitTest
481          return false;
482     */
483 
484     if (!firstLineBox()) {
485         return false;
486     }
487 
488     // We can check the first box and last box and avoid hit testing if we don't
489     // contain the point.  This is a quick short-circuit that we can take to avoid walking any lines.
490     // FIXME: This check is flawed in two extremely obscure ways.
491     // (1) If some line in the middle has a huge overflow, it might actually extend below the last line.
492     // (2) The overflow from an inline block on a line is not reported to the line.
493     if ((y >= ty + lastLineBox()->root()->bottomOverflow()) || (y < ty + firstLineBox()->root()->topOverflow())) {
494         return false;
495     }
496 
497     // See if our root lines contain the point.  If so, then we hit test
498     // them further.  Note that boxes can easily overlap, so we can't make any assumptions
499     // based off positions of our first line box or our last line box.
500     for (InlineFlowBox *curr = lastLineBox(); curr; curr = curr->prevFlowBox()) {
501         if (y >= ty + curr->root()->topOverflow() && y < ty + curr->root()->bottomOverflow()) {
502             bool inside = curr->nodeAtPoint(i, x, y, tx, ty);
503             if (inside) {
504                 setInnerNode(i);
505                 return true;
506             }
507         }
508     }
509 
510     return false;
511 }
512 
repaint(Priority prior)513 void RenderFlow::repaint(Priority prior)
514 {
515     if (isInlineFlow()) {
516         // Find our leftmost position.
517         int left = 0;
518         // root inline box not reliably availabe during relayout
519         int top = firstLineBox() ? (
520                       needsLayout() ? firstLineBox()->xPos() : firstLineBox()->root()->topOverflow()
521                   ) : 0;
522         for (InlineRunBox *curr = firstLineBox(); curr; curr = curr->nextLineBox())
523             if (curr == firstLineBox() || curr->xPos() < left) {
524                 left = curr->xPos();
525             }
526 
527         // Now invalidate a rectangle.
528         int ow = style() ? style()->outlineSize() : 0;
529 
530         // We need to add in the relative position offsets of any inlines (including us) up to our
531         // containing block.
532         RenderBlock *cb = containingBlock();
533         for (RenderObject *inlineFlow = this; inlineFlow && inlineFlow->isInlineFlow() && inlineFlow != cb;
534                 inlineFlow = inlineFlow->parent()) {
535             if (inlineFlow->style() && inlineFlow->style()->position() == PRELATIVE && inlineFlow->layer()) {
536                 KHTMLAssert(inlineFlow->isBox());
537                 static_cast<RenderBox *>(inlineFlow)->relativePositionOffset(left, top);
538             }
539         }
540 
541         RootInlineBox *lastRoot = lastLineBox() && !needsLayout() ? lastLineBox()->root() : nullptr;
542         containingBlock()->repaintRectangle(-ow + left, -ow + top,
543                                             width() + ow * 2,
544                                             (lastRoot ? lastRoot->bottomOverflow() - top : height()) + ow * 2, prior);
545     } else {
546         if (firstLineBox() && firstLineBox()->topOverflow() < 0) {
547             int ow = style() ? style()->outlineSize() : 0;
548             repaintRectangle(-ow, -ow + firstLineBox()->topOverflow(),
549                              effectiveWidth() + ow * 2, effectiveHeight() + ow * 2, prior);
550         } else {
551             return RenderBox::repaint(prior);
552         }
553     }
554 }
555 
556 int
lowestPosition(bool includeOverflowInterior,bool includeSelf) const557 RenderFlow::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
558 {
559     int bottom = includeSelf && m_width > 0 ? m_height : 0;
560     if (!includeOverflowInterior && hasOverflowClip()) {
561         return bottom;
562     }
563 
564     // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
565     // For now, we have to descend into all the children, since we may have a huge abs div inside
566     // a tiny rel div buried somewhere deep in our child tree.  In this case we have to get to
567     // the abs div.
568     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
569         if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) {
570             int lp = c->yPos() + c->lowestPosition(false);
571             bottom = qMax(bottom, lp);
572         }
573     }
574 
575     if (includeSelf && isRelPositioned()) {
576         int x = 0;
577         relativePositionOffset(x, bottom);
578     }
579 
580     return bottom;
581 }
582 
rightmostPosition(bool includeOverflowInterior,bool includeSelf) const583 int RenderFlow::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
584 {
585     int right = includeSelf && m_height > 0 ? m_width : 0;
586     if (!includeOverflowInterior && hasOverflowClip()) {
587         return right;
588     }
589 
590     // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
591     // For now, we have to descend into all the children, since we may have a huge abs div inside
592     // a tiny rel div buried somewhere deep in our child tree.  In this case we have to get to
593     // the abs div.
594     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
595         if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) {
596             int rp = c->xPos() + c->rightmostPosition(false);
597             right = qMax(right, rp);
598         }
599     }
600 
601     if (includeSelf && isRelPositioned()) {
602         int y = 0;
603         relativePositionOffset(right, y);
604     }
605 
606     return right;
607 }
608 
leftmostPosition(bool includeOverflowInterior,bool includeSelf) const609 int RenderFlow::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
610 {
611     int left = includeSelf && m_height > 0 ? 0 : m_width;
612     if (!includeOverflowInterior && hasOverflowClip()) {
613         return left;
614     }
615 
616     // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
617     // For now, we have to descend into all the children, since we may have a huge abs div inside
618     // a tiny rel div buried somewhere deep in our child tree.  In this case we have to get to
619     // the abs div.
620     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
621         if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) {
622             int lp = c->xPos() + c->leftmostPosition(false);
623             left = qMin(left, lp);
624         }
625     }
626 
627     if (includeSelf && isRelPositioned()) {
628         int y = 0;
629         relativePositionOffset(left, y);
630     }
631 
632     return left;
633 }
634 
highestPosition(bool includeOverflowInterior,bool includeSelf) const635 int RenderFlow::highestPosition(bool includeOverflowInterior, bool includeSelf) const
636 {
637     int top = RenderBox::highestPosition(includeOverflowInterior, includeSelf);
638     if (!includeOverflowInterior && hasOverflowClip()) {
639         return top;
640     }
641 
642     // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
643     // For now, we have to descend into all the children, since we may have a huge abs div inside
644     // a tiny rel div buried somewhere deep in our child tree.  In this case we have to get to
645     // the abs div.
646     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
647         if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) {
648             int hp = c->yPos() + c->highestPosition(false);
649             top = qMin(top, hp);
650         }
651     }
652 
653     if (includeSelf && isRelPositioned()) {
654         int x = 0;
655         relativePositionOffset(x, top);
656     }
657 
658     return top;
659 }
660