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