RenderBlock.cpp 187 KB
Newer Older
1
/*
2 3
 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
dsmith's avatar
dsmith committed
4
 *           (C) 2007 David Smith (catfish.man@gmail.com)
mitz@apple.com's avatar
mitz@apple.com committed
5
 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
ddkilzer's avatar
ddkilzer committed
19 20
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
21 22
 */

mjs's avatar
mjs committed
23
#include "config.h"
24
#include "RenderBlock.h"
25

darin's avatar
darin committed
26
#include "Document.h"
27
#include "Element.h"
mjs's avatar
mjs committed
28
#include "Frame.h"
darin's avatar
darin committed
29
#include "FrameView.h"
darin's avatar
darin committed
30
#include "GraphicsContext.h"
weinig's avatar
weinig committed
31
#include "HTMLNames.h"
bdakin's avatar
bdakin committed
32
#include "HitTestResult.h"
weinig's avatar
weinig committed
33
#include "InlineTextBox.h"
bdakin's avatar
bdakin committed
34
#include "RenderImage.h"
35
#include "RenderMarquee.h"
hyatt@apple.com's avatar
hyatt@apple.com committed
36
#include "RenderReplica.h"
darin's avatar
darin committed
37
#include "RenderTableCell.h"
darin's avatar
darin committed
38
#include "RenderTextFragment.h"
darin's avatar
darin committed
39
#include "RenderTheme.h"
weinig's avatar
weinig committed
40 41
#include "RenderView.h"
#include "SelectionController.h"
42

darin's avatar
darin committed
43
using namespace std;
darin's avatar
darin committed
44 45
using namespace WTF;
using namespace Unicode;
darin's avatar
darin committed
46

darin's avatar
darin committed
47 48
namespace WebCore {

aliceli1's avatar
aliceli1 committed
49 50 51 52
// Number of pixels to allow as a fudge factor when clicking above or below a line.
// clicking up to verticalLineClickFudgeFactor pixels above a line will correspond to the closest point on the line.   
const int verticalLineClickFudgeFactor= 3;

darin's avatar
darin committed
53
using namespace HTMLNames;
54

antti's avatar
antti committed
55 56 57 58 59 60 61 62 63 64 65 66 67
struct ColumnInfo {
    ColumnInfo()
        : m_desiredColumnWidth(0)
        , m_desiredColumnCount(1)
        { }
    int m_desiredColumnWidth;
    unsigned m_desiredColumnCount;
    Vector<IntRect> m_columnRects;
};

typedef WTF::HashMap<const RenderBox*, ColumnInfo*> ColumnInfoMap;
static ColumnInfoMap* gColumnInfoMap = 0;

mitz@apple.com's avatar
mitz@apple.com committed
68 69 70 71 72 73
typedef WTF::HashMap<const RenderBlock*, HashSet<RenderBox*>*> PercentHeightDescendantsMap;
static PercentHeightDescendantsMap* gPercentHeightDescendantsMap = 0;

typedef WTF::HashMap<const RenderBox*, HashSet<RenderBlock*>*> PercentHeightContainerMap;
static PercentHeightContainerMap* gPercentHeightContainerMap = 0;

74 75 76 77 78 79
// Our MarginInfo state used when laying out block children.
RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, int top, int bottom)
{
    // Whether or not we can collapse our own margins with our children.  We don't do this
    // if we had any border/padding (obviously), if we're the root or HTML elements, or if
    // we're positioned, floating, a table cell.
80
    m_canCollapseWithChildren = !block->isRenderView() && !block->isRoot() && !block->isPositioned() &&
81 82 83 84 85 86 87 88 89
        !block->isFloating() && !block->isTableCell() && !block->hasOverflowClip() && !block->isInlineBlockOrInlineTable();

    m_canCollapseTopWithChildren = m_canCollapseWithChildren && (top == 0) && block->style()->marginTopCollapse() != MSEPARATE;

    // If any height other than auto is specified in CSS, then we don't collapse our bottom
    // margins with our children's margins.  To do otherwise would be to risk odd visual
    // effects when the children overflow out of the parent block and yet still collapse
    // with it.  We also don't collapse if we have any bottom border/padding.
    m_canCollapseBottomWithChildren = m_canCollapseWithChildren && (bottom == 0) &&
darin's avatar
darin committed
90
        (block->style()->height().isAuto() && block->style()->height().value() == 0) && block->style()->marginBottomCollapse() != MSEPARATE;
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
    
    m_quirkContainer = block->isTableCell() || block->isBody() || block->style()->marginTopCollapse() == MDISCARD || 
        block->style()->marginBottomCollapse() == MDISCARD;

    m_atTopOfBlock = true;
    m_atBottomOfBlock = false;

    m_posMargin = m_canCollapseTopWithChildren ? block->maxTopMargin(true) : 0;
    m_negMargin = m_canCollapseTopWithChildren ? block->maxTopMargin(false) : 0;

    m_selfCollapsingBlockClearedFloat = false;
    
    m_topQuirk = m_bottomQuirk = m_determinedTopQuirk = false;
}

// -------------------------------------------------------------------------------------------------------

darin's avatar
darin committed
108
RenderBlock::RenderBlock(Node* node)
antti's avatar
antti committed
109 110 111 112 113 114 115 116 117
      : RenderFlow(node)
      , m_floatingObjects(0)
      , m_positionedObjects(0)
      , m_maxMargin(0)
      , m_overflowHeight(0)
      , m_overflowWidth(0)
      , m_overflowLeft(0)
      , m_overflowTop(0)
{
118 119 120 121
}

RenderBlock::~RenderBlock()
{
122 123
    delete m_floatingObjects;
    delete m_positionedObjects;
antti's avatar
antti committed
124 125
    delete m_maxMargin;
    
darin@apple.com's avatar
darin@apple.com committed
126 127
    if (m_hasColumns)
        delete gColumnInfoMap->take(this);
mitz@apple.com's avatar
mitz@apple.com committed
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146

    if (gPercentHeightDescendantsMap) {
        if (HashSet<RenderBox*>* descendantSet = gPercentHeightDescendantsMap->take(this)) {
            HashSet<RenderBox*>::iterator end = descendantSet->end();
            for (HashSet<RenderBox*>::iterator descendant = descendantSet->begin(); descendant != end; ++descendant) {
                HashSet<RenderBlock*>* containerSet = gPercentHeightContainerMap->get(*descendant);
                ASSERT(containerSet);
                if (!containerSet)
                    continue;
                ASSERT(containerSet->contains(this));
                containerSet->remove(this);
                if (containerSet->isEmpty()) {
                    gPercentHeightContainerMap->remove(*descendant);
                    delete containerSet;
                }
            }
            delete descendantSet;
        }
    }
147 148
}

149
void RenderBlock::setStyle(const RenderStyle* newStyle)
150
{
151
    setReplaced(newStyle->isDisplayReplacedType());
152

153
    RenderFlow::setStyle(newStyle);
154

155 156 157
    // FIXME: We could save this call when the change only affected non-inherited properties
    for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
        if (child->isAnonymousBlock()) {
158
            RenderStyle* newStyle = new (renderArena()) RenderStyle();
159 160 161 162
            newStyle->inheritFrom(style());
            newStyle->setDisplay(BLOCK);
            child->setStyle(newStyle);
        }
163 164
    }

165 166
    m_lineHeight = -1;

167
    // Update pseudos for :before and :after now.
168
    if (!isAnonymous() && canHaveChildren()) {
hyatt's avatar
hyatt committed
169 170 171
        updateBeforeAfterContent(RenderStyle::BEFORE);
        updateBeforeAfterContent(RenderStyle::AFTER);
    }
weinig's avatar
weinig committed
172
    updateFirstLetter();
darin's avatar
darin committed
173 174
}

175 176
void RenderBlock::addChildToFlow(RenderObject* newChild, RenderObject* beforeChild)
{
177
    // Make sure we don't append things after :after-generated content if we have it.
darin's avatar
darin committed
178
    if (!beforeChild && isAfterContent(lastChild()))
179
        beforeChild = lastChild();
mitz@apple.com's avatar
mitz@apple.com committed
180

181
    bool madeBoxesNonInline = false;
182

mitz@apple.com's avatar
mitz@apple.com committed
183 184
    // If the requested beforeChild is not one of our children, then this is because
    // there is an anonymous container within this object that contains the beforeChild.
185
    if (beforeChild && beforeChild->parent() != this) {
mitz@apple.com's avatar
mitz@apple.com committed
186 187
        RenderObject* anonymousChild = beforeChild->parent();
        ASSERT(anonymousChild);
188

mitz@apple.com's avatar
mitz@apple.com committed
189 190
        while (anonymousChild->parent() != this)
            anonymousChild = anonymousChild->parent();
mitz@apple.com's avatar
mitz@apple.com committed
191

mitz@apple.com's avatar
mitz@apple.com committed
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
        ASSERT(anonymousChild->isAnonymous());

        if (anonymousChild->isAnonymousBlock()) {
            // Insert the child into the anonymous block box instead of here.
            if (newChild->isInline() || beforeChild->parent()->firstChild() != beforeChild)
                beforeChild->parent()->addChild(newChild, beforeChild);
            else
                addChildToFlow(newChild, beforeChild->parent());
            return;
        }

        ASSERT(anonymousChild->isTable());
        if (newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP
                || newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION
                || newChild->isTableSection()
                || newChild->isTableRow()
                || newChild->isTableCell()) {
            // Insert into the anonymous table.
            anonymousChild->addChild(newChild, beforeChild);
            return;
        }

        // Go on to insert before the anonymous table.
        beforeChild = anonymousChild;
216 217 218 219
    }

    // A block has to either have all of its children inline, or all of its children as blocks.
    // So, if our children are currently inline and a block child has to be inserted, we move all our
mitz@apple.com's avatar
mitz@apple.com committed
220 221
    // inline children into anonymous block boxes.
    if (m_childrenInline && !newChild->isInline() && !newChild->isFloatingOrPositioned()) {
222 223 224
        // This is a block with inline content. Wrap the inline content in anonymous blocks.
        makeChildrenNonInline(beforeChild);
        madeBoxesNonInline = true;
mitz@apple.com's avatar
mitz@apple.com committed
225

226 227
        if (beforeChild && beforeChild->parent() != this) {
            beforeChild = beforeChild->parent();
darin's avatar
darin committed
228 229
            ASSERT(beforeChild->isAnonymousBlock());
            ASSERT(beforeChild->parent() == this);
230
        }
mitz@apple.com's avatar
mitz@apple.com committed
231
    } else if (!m_childrenInline && (newChild->isFloatingOrPositioned() || newChild->isInline())) {
232 233 234
        // If we're inserting an inline child but all of our children are blocks, then we have to make sure
        // it is put into an anomyous block box. We try to use an existing anonymous box if possible, otherwise
        // a new one is created and inserted into our list of children in the appropriate position.
mitz@apple.com's avatar
mitz@apple.com committed
235 236 237 238 239 240 241
        RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : lastChild();

        if (afterChild && afterChild->isAnonymousBlock()) {
            afterChild->addChild(newChild);
            return;
        }

242
        if (newChild->isInline()) {
mitz@apple.com's avatar
mitz@apple.com committed
243
            // No suitable existing anonymous box - create a new one.
244
            RenderBlock* newBox = createAnonymousBlock();
mitz@apple.com's avatar
mitz@apple.com committed
245
            RenderContainer::addChild(newBox, beforeChild);
246 247 248 249 250
            newBox->addChild(newChild);
            return;
        }
    }

mitz@apple.com's avatar
mitz@apple.com committed
251
    RenderContainer::addChild(newChild, beforeChild);
252 253
    // ### care about aligned stuff

antti's avatar
antti committed
254 255 256
    if (madeBoxesNonInline && parent() && isAnonymousBlock())
        parent()->removeLeftoverAnonymousBlock(this);
    // this object may be dead here
257 258
}

hyatt's avatar
hyatt committed
259
static void getInlineRun(RenderObject* start, RenderObject* boundary,
260 261 262 263 264 265 266 267 268 269 270
                         RenderObject*& inlineRunStart,
                         RenderObject*& inlineRunEnd)
{
    // Beginning at |start| we find the largest contiguous run of inlines that
    // we can.  We denote the run with start and end points, |inlineRunStart|
    // and |inlineRunEnd|.  Note that these two values may be the same if
    // we encounter only one inline.
    //
    // We skip any non-inlines we encounter as long as we haven't found any
    // inlines yet.
    //
hyatt's avatar
hyatt committed
271 272
    // |boundary| indicates a non-inclusive boundary point.  Regardless of whether |boundary|
    // is inline or not, we will not include it in a run with inlines before it.  It's as though we encountered
273
    // a non-inline.
hyatt's avatar
hyatt committed
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
    
    // Start by skipping as many non-inlines as we can.
    RenderObject * curr = start;
    bool sawInline;
    do {
        while (curr && !(curr->isInline() || curr->isFloatingOrPositioned()))
            curr = curr->nextSibling();
        
        inlineRunStart = inlineRunEnd = curr;
        
        if (!curr)
            return; // No more inline children to be found.
        
        sawInline = curr->isInline();
        
        curr = curr->nextSibling();
        while (curr && (curr->isInline() || curr->isFloatingOrPositioned()) && (curr != boundary)) {
hyatt's avatar
hyatt committed
291
            inlineRunEnd = curr;
hyatt's avatar
hyatt committed
292 293 294 295 296
            if (curr->isInline())
                sawInline = true;
            curr = curr->nextSibling();
        }
    } while (!sawInline);
297 298
}

bdakin's avatar
bdakin committed
299 300 301 302 303 304 305 306 307 308 309 310
void RenderBlock::deleteLineBoxTree()
{
    InlineFlowBox* line = m_firstLineBox;
    InlineFlowBox* nextLine;
    while (line) {
        nextLine = line->nextFlowBox();
        line->deleteLine(renderArena());
        line = nextLine;
    }
    m_firstLineBox = m_lastLineBox = 0;
}

311
void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint)
hyatt's avatar
hyatt committed
312
{    
313 314 315 316 317 318 319
    // makeChildrenNonInline takes a block whose children are *all* inline and it
    // makes sure that inline children are coalesced under anonymous
    // blocks.  If |insertionPoint| is defined, then it represents the insertion point for
    // the new block child that is causing us to have to wrap all the inlines.  This
    // means that we cannot coalesce inlines before |insertionPoint| with inlines following
    // |insertionPoint|, because the new child is going to be inserted in between the inlines,
    // splitting them.
darin's avatar
darin committed
320 321
    ASSERT(isInlineBlockOrInlineTable() || !isInline());
    ASSERT(!insertionPoint || insertionPoint->parent() == this);
322 323 324 325

    m_childrenInline = false;

    RenderObject *child = firstChild();
mitz@apple.com's avatar
mitz@apple.com committed
326 327 328 329
    if (!child)
        return;

    deleteLineBoxTree();
330 331 332 333 334 335 336 337 338 339

    while (child) {
        RenderObject *inlineRunStart, *inlineRunEnd;
        getInlineRun(child, insertionPoint, inlineRunStart, inlineRunEnd);

        if (!inlineRunStart)
            break;

        child = inlineRunEnd->nextSibling();

340
        RenderBlock* box = createAnonymousBlock();
341 342 343 344 345 346
        insertChildNode(box, inlineRunStart);
        RenderObject* o = inlineRunStart;
        while(o != inlineRunEnd)
        {
            RenderObject* no = o;
            o = no->nextSibling();
347
            box->moveChildNode(no);
348
        }
349
        box->moveChildNode(inlineRunEnd);
350
    }
hyatt's avatar
hyatt committed
351 352 353

#ifndef NDEBUG
    for (RenderObject *c = firstChild(); c; c = c->nextSibling())
darin's avatar
darin committed
354
        ASSERT(!c->isInline());
hyatt's avatar
hyatt committed
355
#endif
mitz@apple.com's avatar
mitz@apple.com committed
356 357

    repaint();
358 359 360 361 362 363 364 365 366
}

void RenderBlock::removeChild(RenderObject *oldChild)
{
    // If this child is a block, and if our previous and next siblings are
    // both anonymous blocks with inline content, then we can go ahead and
    // fold the inline content back together.
    RenderObject* prev = oldChild->previousSibling();
    RenderObject* next = oldChild->nextSibling();
367
    bool canDeleteAnonymousBlocks = !documentBeingDestroyed() && !isInline() && !oldChild->isInline() && 
368 369 370
                                    !oldChild->continuation() && 
                                    (!prev || (prev->isAnonymousBlock() && prev->childrenInline())) &&
                                    (!next || (next->isAnonymousBlock() && next->childrenInline()));
371
    if (canDeleteAnonymousBlocks && prev && next) {
372 373
        // Take all the children out of the |next| block and put them in
        // the |prev| block.
darin's avatar
darin committed
374
        prev->setNeedsLayoutAndPrefWidthsRecalc();
375 376 377 378
        RenderObject* o = next->firstChild();
        while (o) {
            RenderObject* no = o;
            o = no->nextSibling();
379
            prev->moveChildNode(no);
380
        }
bdakin's avatar
bdakin committed
381
 
weinig's avatar
weinig committed
382
        RenderBlock* nextBlock = static_cast<RenderBlock*>(next);
bdakin's avatar
bdakin committed
383
        nextBlock->deleteLineBoxTree();
weinig's avatar
weinig committed
384
        
385
        // Nuke the now-empty block.
harrison's avatar
harrison committed
386
        next->destroy();
387 388 389 390
    }

    RenderFlow::removeChild(oldChild);

391
    RenderObject* child = prev ? prev : next;
antti's avatar
antti committed
392
    if (canDeleteAnonymousBlocks && child && !child->previousSibling() && !child->nextSibling() && !isFlexibleBox()) {
393
        // The removal has knocked us down to containing only a single anonymous
394 395
        // box.  We can go ahead and pull the content right back up into our
        // box.
darin's avatar
darin committed
396
        setNeedsLayoutAndPrefWidthsRecalc();
darin's avatar
darin committed
397
        RenderBlock* anonBlock = static_cast<RenderBlock*>(removeChildNode(child, false));
398 399 400 401 402
        m_childrenInline = true;
        RenderObject* o = anonBlock->firstChild();
        while (o) {
            RenderObject* no = o;
            o = no->nextSibling();
403
            moveChildNode(no);
404
        }
405

bdakin's avatar
bdakin committed
406 407
        // Delete the now-empty block's lines and nuke it.
        anonBlock->deleteLineBoxTree();
harrison's avatar
harrison committed
408
        anonBlock->destroy();
409 410 411
    }
}

412 413
int RenderBlock::overflowHeight(bool includeInterior) const
{
414
    if (!includeInterior && hasOverflowClip()) {
mitz@apple.com's avatar
mitz@apple.com committed
415 416 417
        int shadowHeight = 0;
        for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next)
            shadowHeight = max(boxShadow->y + boxShadow->blur, shadowHeight);
hyatt@apple.com's avatar
hyatt@apple.com committed
418 419 420 421
        int height = m_height + shadowHeight;
        if (hasReflection())
            height = max(height, reflectionBox().bottom());
        return height;
422 423
    }
    return m_overflowHeight;
424 425 426 427
}

int RenderBlock::overflowWidth(bool includeInterior) const
{
428
    if (!includeInterior && hasOverflowClip()) {
mitz@apple.com's avatar
mitz@apple.com committed
429 430 431
        int shadowWidth = 0;
        for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next)
            shadowWidth = max(boxShadow->x + boxShadow->blur, shadowWidth);
hyatt@apple.com's avatar
hyatt@apple.com committed
432 433 434 435
        int width = m_width + shadowWidth;
        if (hasReflection())
            width = max(width, reflectionBox().right());
        return width;
436 437
    }
    return m_overflowWidth;
438
}
439

440 441
int RenderBlock::overflowLeft(bool includeInterior) const
{
442
    if (!includeInterior && hasOverflowClip()) {
mitz@apple.com's avatar
mitz@apple.com committed
443 444 445
        int shadowLeft = 0;
        for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next)
            shadowLeft = min(boxShadow->x - boxShadow->blur, shadowLeft);
hyatt@apple.com's avatar
hyatt@apple.com committed
446 447 448 449
        int left = shadowLeft;
        if (hasReflection())
            left = min(left, reflectionBox().x());
        return left;
450 451
    }
    return m_overflowLeft;
452 453 454 455
}

int RenderBlock::overflowTop(bool includeInterior) const
{
456
    if (!includeInterior && hasOverflowClip()) {
mitz@apple.com's avatar
mitz@apple.com committed
457 458 459
        int shadowTop = 0;
        for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next)
            shadowTop = min(boxShadow->y - boxShadow->blur, shadowTop);
hyatt@apple.com's avatar
hyatt@apple.com committed
460 461 462 463
        int top = shadowTop;
        if (hasReflection())
            top = min(top, reflectionBox().y());
        return top;
464 465
    }
    return m_overflowTop;
466 467
}

468
IntRect RenderBlock::overflowRect(bool includeInterior) const
469
{
470 471
    if (!includeInterior && hasOverflowClip()) {
        IntRect box = borderBox();
mitz@apple.com's avatar
mitz@apple.com committed
472 473 474 475 476 477 478 479 480 481
        int shadowLeft = 0;
        int shadowRight = 0;
        int shadowTop = 0;
        int shadowBottom = 0;

        for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next) {
            shadowLeft = min(boxShadow->x - boxShadow->blur, shadowLeft);
            shadowRight = max(boxShadow->x + boxShadow->blur, shadowRight);
            shadowTop = min(boxShadow->y - boxShadow->blur, shadowTop);
            shadowBottom = max(boxShadow->y + boxShadow->blur, shadowBottom);
482
        }
mitz@apple.com's avatar
mitz@apple.com committed
483 484 485 486

        box.move(shadowLeft, shadowTop);
        box.setWidth(box.width() - shadowLeft + shadowRight);
        box.setHeight(box.height() - shadowTop + shadowBottom);
hyatt@apple.com's avatar
hyatt@apple.com committed
487 488 489 490 491 492 493 494 495 496 497 498 499

        if (hasReflection()) {
            IntRect reflection(reflectionBox());
            int reflectTop = min(box.y(), reflection.y());
            int reflectBottom = max(box.bottom(), reflection.bottom());
            box.setHeight(reflectBottom - reflectTop);
            box.setY(reflectTop);
            
            int reflectLeft = min(box.x(), reflection.x());
            int reflectRight = max(box.right(), reflection.right());
            box.setWidth(reflectRight - reflectLeft);
            box.setX(reflectLeft);
        }
500 501 502
        return box;
    }

503 504 505
    if (!includeInterior && hasOverflowClip())
        return borderBox();
    int l = overflowLeft(includeInterior);
darin's avatar
darin committed
506
    int t = min(overflowTop(includeInterior), -borderTopExtra());
507
    return IntRect(l, t, overflowWidth(includeInterior) - l, max(overflowHeight(includeInterior), height() + borderBottomExtra()) - t);
508 509
}

510 511 512 513 514 515 516
bool RenderBlock::isSelfCollapsingBlock() const
{
    // We are not self-collapsing if we
    // (a) have a non-zero height according to layout (an optimization to avoid wasting time)
    // (b) are a table,
    // (c) have border/padding,
    // (d) have a min-height
517
    // (e) have specified that one of our margins can't collapse using a CSS extension
518 519
    if (m_height > 0 ||
        isTable() || (borderBottom() + paddingBottom() + borderTop() + paddingTop()) != 0 ||
ddkilzer's avatar
ddkilzer committed
520
        style()->minHeight().isPositive() || 
521
        style()->marginTopCollapse() == MSEPARATE || style()->marginBottomCollapse() == MSEPARATE)
522 523
        return false;

524
    bool hasAutoHeight = style()->height().isAuto();
525 526
    if (style()->height().isPercent() && !style()->htmlHacks()) {
        hasAutoHeight = true;
527
        for (RenderBlock* cb = containingBlock(); !cb->isRenderView(); cb = cb->containingBlock()) {
528 529 530 531 532
            if (cb->style()->height().isFixed() || cb->isTableCell())
                hasAutoHeight = false;
        }
    }

533 534
    // If the height is 0 or auto, then whether or not we are a self-collapsing block depends
    // on whether we have content that is all self-collapsing or not.
ddkilzer's avatar
ddkilzer committed
535
    if (hasAutoHeight || ((style()->height().isFixed() || style()->height().isPercent()) && style()->height().isZero())) {
536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
        // If the block has inline children, see if we generated any line boxes.  If we have any
        // line boxes, then we can't be self-collapsing, since we have content.
        if (childrenInline())
            return !firstLineBox();
        
        // Whether or not we collapse is dependent on whether all our normal flow children
        // are also self-collapsing.
        for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
            if (child->isFloatingOrPositioned())
                continue;
            if (!child->isSelfCollapsingBlock())
                return false;
        }
        return true;
    }
    return false;
}

554 555
void RenderBlock::layout()
{
556 557 558
    // Update our first letter info now.
    updateFirstLetter();

559 560 561
    // Table cells call layoutBlock directly, so don't add any logic here.  Put code into
    // layoutBlock().
    layoutBlock(false);
562 563 564 565 566 567 568 569 570
    
    // It's safe to check for control clip here, since controls can never be table cells.
    if (hasControlClip()) {
        // Because of the lightweight clip, there can never be any overflow from children.
        m_overflowWidth = m_width;
        m_overflowHeight = m_height;
        m_overflowLeft = 0;
        m_overflowTop = 0;
    }
571 572 573 574
}

void RenderBlock::layoutBlock(bool relayoutChildren)
{
darin's avatar
darin committed
575
    ASSERT(needsLayout());
576

577
    if (isInline() && !isInlineBlockOrInlineTable()) // Inline <form>s inside various table elements can
578
        return;                                      // cause us to come in here.  Just bail.
579

weinig's avatar
weinig committed
580
    if (!relayoutChildren && layoutOnlyPositionedObjects())
581
        return;
weinig's avatar
weinig committed
582

ap's avatar
ap committed
583
    IntRect oldBounds;
584
    IntRect oldOutlineBox;
mitz@apple.com's avatar
mitz@apple.com committed
585
    bool checkForRepaint = m_everHadLayout && checkForRepaintDuringLayout();
586
    if (checkForRepaint) {
587 588
        oldBounds = absoluteClippedOverflowRect();
        oldOutlineBox = absoluteOutlineBox();
589
    }
590

weinig's avatar
weinig committed
591
    bool hadColumns = m_hasColumns;
hyatt@apple.com's avatar
hyatt@apple.com committed
592
    if (!hadColumns && !hasReflection())
weinig's avatar
weinig committed
593 594 595 596
        view()->pushLayoutState(this, IntSize(xPos(), yPos()));
    else
        view()->disableLayoutState();

597
    int oldWidth = m_width;
antti's avatar
antti committed
598
    int oldColumnWidth = desiredColumnWidth();
599

600
    calcWidth();
601 602
    calcColumnWidth();

603
    m_overflowWidth = m_width;
604
    m_overflowLeft = 0;
605

antti's avatar
antti committed
606
    if (oldWidth != m_width || oldColumnWidth != desiredColumnWidth())
607 608 609 610
        relayoutChildren = true;

    clearFloats();

611
    int previousHeight = m_height;
612 613 614 615 616 617 618 619 620 621 622 623 624
    m_height = 0;
    m_overflowHeight = 0;

    // We use four values, maxTopPos, maxPosNeg, maxBottomPos, and maxBottomNeg, to track
    // our current maximal positive and negative margins.  These values are used when we
    // are collapsed with adjacent blocks, so for example, if you have block A and B
    // collapsing together, then you'd take the maximal positive margin from both A and B
    // and subtract it from the maximal negative margin from both A and B to get the
    // true collapsed margin.  This algorithm is recursive, so when we finish layout()
    // our block knows its current maximal positive/negative values.
    //
    // Start out by setting our margin values to our current margins.  Table cells have
    // no margins, so we don't fill in the values for table cells.
darin's avatar
darin committed
625 626
    bool isCell = isTableCell();
    if (!isCell) {
627 628
        initMaxMarginValues();

darin's avatar
darin committed
629 630
        m_topMarginQuirk = style()->marginTop().quirk();
        m_bottomMarginQuirk = style()->marginBottom().quirk();
631

632 633
        Node* node = element();
        if (node && node->hasTagName(formTag) && static_cast<HTMLFormElement*>(node)->isMalformed()) {
634 635
            // See if this form is malformed (i.e., unclosed). If so, don't give the form
            // a bottom margin.
antti's avatar
antti committed
636
            setMaxBottomMargins(0, 0);
637
        }
638 639
    }

640
    // For overflow:scroll blocks, ensure we have both scrollbars in place always.
641
    if (scrollsOverflow()) {
642
        if (style()->overflowX() == OSCROLL)
643
            m_layer->setHasHorizontalScrollbar(true);
644
        if (style()->overflowY() == OSCROLL)
645 646
            m_layer->setHasVerticalScrollbar(true);
    }
647

ap's avatar
ap committed
648 649
    int repaintTop = 0;
    int repaintBottom = 0;
mitz@apple.com's avatar
mitz@apple.com committed
650
    int maxFloatBottom = 0;
651
    if (childrenInline())
ap's avatar
ap committed
652
        layoutInlineChildren(relayoutChildren, repaintTop, repaintBottom);
653
    else
mitz@apple.com's avatar
mitz@apple.com committed
654
        layoutBlockChildren(relayoutChildren, maxFloatBottom);
655

656
    // Expand our intrinsic height to encompass floats.
657
    int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight();
bdakin's avatar
Bug #:  
bdakin committed
658
    if (floatBottom() > (m_height - toAdd) && (isInlineBlockOrInlineTable() || isFloatingOrPositioned() || hasOverflowClip() ||
antti's avatar
antti committed
659
                                    (parent() && parent()->isFlexibleBox() || m_hasColumns)))
660
        m_height = floatBottom() + toAdd;
661
    
662 663
    // Now lay out our columns within this intrinsic height, since they can slightly affect the intrinsic height as
    // we adjust for clean column breaks.
664
    int singleColumnBottom = layoutColumns();
665 666

    // Calculate our new height.
667 668 669
    int oldHeight = m_height;
    calcHeight();
    if (oldHeight != m_height) {
mitz@apple.com's avatar
mitz@apple.com committed
670 671 672 673 674 675
        if (oldHeight > m_height && maxFloatBottom > m_height && !childrenInline()) {
            // One of our children's floats may have become an overhanging float for us. We need to look for it.
            for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
                if (child->isBlockFlow() && !child->isFloatingOrPositioned()) {
                    RenderBlock* block = static_cast<RenderBlock*>(child);
                    if (block->floatBottom() + block->yPos() > m_height)
mitz@apple.com's avatar
mitz@apple.com committed
676
                        addOverhangingFloats(block, -block->xPos(), -block->yPos(), false);
mitz@apple.com's avatar
mitz@apple.com committed
677 678 679
                }
            }
        }
680 681 682
        // We have to rebalance columns to the new height.
        layoutColumns(singleColumnBottom);

683 684
        // If the block got expanded in size, then increase our overflowheight to match.
        if (m_overflowHeight > m_height)
685
            m_overflowHeight -= toAdd;
686 687 688
        if (m_overflowHeight < m_height)
            m_overflowHeight = m_height;
    }
689 690
    if (previousHeight != m_height)
        relayoutChildren = true;
691

692 693 694
    // Some classes of objects (floats and fieldsets with no specified heights and table cells) expand to encompass
    // overhanging floats.
    if (hasOverhangingFloats() && expandsToEncloseOverhangingFloats()) {
695 696 697 698
        m_height = floatBottom();
        m_height += borderBottom() + paddingBottom();
    }

darin's avatar
darin committed
699
    if ((isCell || isInline() || isFloatingOrPositioned() || isRoot()) && !hasOverflowClip() && !hasControlClip())
ap's avatar
ap committed
700 701
        addVisualOverflow(floatRect());

702
    layoutPositionedObjects(relayoutChildren || isRoot());
703

justing's avatar
justing committed
704 705
    positionListMarker();

706
    // Always ensure our overflow width/height are at least as large as our width/height.
darin's avatar
darin committed
707 708
    m_overflowWidth = max(m_overflowWidth, m_width);
    m_overflowHeight = max(m_overflowHeight, m_height);
709

710
    if (!hasOverflowClip()) {
mitz@apple.com's avatar
mitz@apple.com committed
711
        for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next) {
712 713 714 715 716
            m_overflowLeft = min(m_overflowLeft, boxShadow->x - boxShadow->blur);
            m_overflowWidth = max(m_overflowWidth, m_width + boxShadow->x + boxShadow->blur);
            m_overflowTop = min(m_overflowTop, boxShadow->y - boxShadow->blur);
            m_overflowHeight = max(m_overflowHeight, m_height + boxShadow->y + boxShadow->blur);
        }
hyatt@apple.com's avatar
hyatt@apple.com committed
717 718 719 720 721
        
        if (hasReflection()) {
            m_overflowTop = min(m_overflowTop, reflectionBox().y());
            m_overflowHeight = max(m_overflowHeight, reflectionBox().bottom());
        }
722 723
    }

hyatt@apple.com's avatar
hyatt@apple.com committed
724
    if (!hadColumns && !hasReflection())
weinig's avatar
weinig committed
725 726 727 728
        view()->popLayoutState();
    else
        view()->enableLayoutState();

729
    // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if
730
    // we overflow or not.
731
    if (hasOverflowClip())
732
        m_layer->updateScrollInfoAfterLayout();
733 734

    // Repaint with our new bounds if they are different from our old bounds.
735
    bool didFullRepaint = false;
736
    if (checkForRepaint)
737
        didFullRepaint = repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox);
mitz@apple.com's avatar
mitz@apple.com committed
738
    if (!didFullRepaint && repaintTop != repaintBottom && (style()->visibility() == VISIBLE || enclosingLayer()->hasVisibleContent())) {
ap's avatar
ap committed
739 740
        IntRect repaintRect(m_overflowLeft, repaintTop, m_overflowWidth - m_overflowLeft, repaintBottom - repaintTop);

741 742 743
        // FIXME: Deal with multiple column repainting.  We have to split the repaint
        // rect up into multiple rects if it spans columns.

adele's avatar
adele committed
744 745 746 747 748 749 750 751 752 753 754 755 756
        repaintRect.inflate(maximalOutlineSize(PaintPhaseOutline));
        
        if (hasOverflowClip()) {
            // Adjust repaint rect for scroll offset
            int x = repaintRect.x();
            int y = repaintRect.y();
            layer()->subtractScrollOffset(x, y);
            repaintRect.setX(x);
            repaintRect.setY(y);

            // Don't allow this rect to spill out of our overflow box.
            repaintRect.intersect(IntRect(0, 0, m_width, m_height));
        }
mitz@apple.com's avatar
mitz@apple.com committed
757

adele's avatar
adele committed
758
        // Make sure the rect is still non-empty after intersecting for overflow above
hyatt@apple.com's avatar
hyatt@apple.com committed
759
        if (!repaintRect.isEmpty()) {
mitz@apple.com's avatar
mitz@apple.com committed
760
            repaintRectangle(repaintRect); // We need to do a partial repaint of our content.
hyatt@apple.com's avatar
hyatt@apple.com committed
761 762 763
            if (hasReflection())
                layer()->reflection()->repaintRectangle(repaintRect);
        }
764
    }
765
    setNeedsLayout(false);
766 767
}

768
void RenderBlock::adjustPositionedBlock(RenderObject* child, const MarginInfo& marginInfo)
769
{
770 771 772 773 774 775 776 777
    if (child->hasStaticX()) {
        if (style()->direction() == LTR)
            child->setStaticX(borderLeft() + paddingLeft());
        else
            child->setStaticX(borderRight() + paddingRight());
    }

    if (child->hasStaticY()) {
darin's avatar
darin committed
778
        int y = m_height;
779
        if (!marginInfo.canCollapseWithTop()) {
darin's avatar
darin committed
780 781
            child->calcVerticalMargins();
            int marginTop = child->marginTop();
782 783
            int collapsedTopPos = marginInfo.posMargin();
            int collapsedTopNeg = marginInfo.negMargin();
darin's avatar
darin committed
784 785 786 787 788 789 790 791
            if (marginTop > 0) {
                if (marginTop > collapsedTopPos)
                    collapsedTopPos = marginTop;
            } else {
                if (-marginTop > collapsedTopNeg)
                    collapsedTopNeg = -marginTop;
            }
            y += (collapsedTopPos - collapsedTopNeg) - marginTop;
792
        }
darin's avatar
darin committed
793
        child->setStaticY(y);
794 795
    }
}
796

797 798 799 800 801 802 803 804 805 806 807 808
void RenderBlock::adjustFloatingBlock(const MarginInfo& marginInfo)
{
    // The float should be positioned taking into account the bottom margin
    // of the previous flow.  We add that margin into the height, get the
    // float positioned properly, and then subtract the margin out of the
    // height again.  In the case of self-collapsing blocks, we always just
    // use the top margins, since the self-collapsing block collapsed its
    // own bottom margin into its top margin.
    //
    // Note also that the previous flow may collapse its margin into the top of
    // our block.  If this is the case, then we do not add the margin in to our
    // height when computing the position of the float.   This condition can be tested
hyatt's avatar
hyatt committed
809
    // for by simply calling canCollapseWithTop.  See
810 811 812 813 814 815 816
    // http://www.hixie.ch/tests/adhoc/css/box/block/margin-collapse/046.html for
    // an example of this scenario.
    int marginOffset = marginInfo.canCollapseWithTop() ? 0 : marginInfo.margin();
    m_height += marginOffset;
    positionNewFloats();
    m_height -= marginOffset;
}
817

818 819
RenderObject* RenderBlock::handleSpecialChild(RenderObject* child, const MarginInfo& marginInfo, CompactInfo& compactInfo, bool& handled)
{
820 821
    // Handle positioned children first.
    RenderObject* next = handlePositionedChild(child, marginInfo, handled);
822
    if (handled) return next;
823
    
824 825 826 827
    // Handle floating children next.
    next = handleFloatingChild(child, marginInfo, handled);
    if (handled) return next;

828 829 830
    // See if we have a compact element.  If we do, then try to tuck the compact element into the margin space of the next block.
    next = handleCompactChild(child, compactInfo, handled);
    if (handled) return next;
831

832 833 834
    // Finally, see if we have a run-in element.
    return handleRunInChild(child, handled);
}
835

836

837
RenderObject* RenderBlock::handlePositionedChild(RenderObject* child, const MarginInfo& marginInfo, bool& handled)
838 839 840 841 842 843 844 845
{
    if (child->isPositioned()) {
        handled = true;
        child->containingBlock()->insertPositionedObject(child);
        adjustPositionedBlock(child, marginInfo);
        return child->nextSibling();
    }

846 847 848 849 850
    return 0;
}

RenderObject* RenderBlock::handleFloatingChild(RenderObject* child, const MarginInfo& marginInfo, bool& handled)
{
851 852 853 854 855 856
    if (child->isFloating()) {
        handled = true;
        insertFloatingObject(child);
        adjustFloatingBlock(marginInfo);
        return child->nextSibling();
    }
857
    
858 859
    return 0;
}
860

861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
RenderObject* RenderBlock::handleCompactChild(RenderObject* child, CompactInfo& compactInfo, bool& handled)
{
    // FIXME: We only deal with one compact at a time.  It is unclear what should be
    // done if multiple contiguous compacts are encountered.  For now we assume that
    // compact A followed by another compact B should simply be treated as block A.
    if (child->isCompact() && !compactInfo.compact() && (child->childrenInline() || child->isReplaced())) {
        // Get the next non-positioned/non-floating RenderBlock.
        RenderObject* next = child->nextSibling();
        RenderObject* curr = next;
        while (curr && curr->isFloatingOrPositioned())
            curr = curr->nextSibling();
        if (curr && curr->isRenderBlock() && !curr->isCompact() && !curr->isRunIn()) {
            curr->calcWidth(); // So that horizontal margins are correct.
                               
            child->setInline(true); // Need to compute the margins/width for the child as though it is an inline, so that it won't try to puff up the margins to
                                    // fill the containing block width.
            child->calcWidth();
            int childMargins = child->marginLeft() + child->marginRight();
            int margin = style()->direction() == LTR ? curr->marginLeft() : curr->marginRight();
darin's avatar
darin committed
880
            if (margin >= (childMargins + child->maxPrefWidth())) {
881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901
                // The compact will fit in the margin.
                handled = true;
                compactInfo.set(child, curr);
                child->setPos(0,0); // This position will be updated to reflect the compact's
                                    // desired position and the line box for the compact will
                                    // pick that position up.
                
                // Remove the child.
                RenderObject* next = child->nextSibling();
                removeChildNode(child);
                
                // Now insert the child under |curr|.
                curr->insertChildNode(child, curr->firstChild());
                return next;
            }
            else
                child->setInline(false); // We didn't fit, so we remain a block-level element.
        }
    }
    return 0;
}
902

903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918
void RenderBlock::insertCompactIfNeeded(RenderObject* child, CompactInfo& compactInfo)
{
    if (compactInfo.matches(child)) {
        // We have a compact child to squeeze in.
        RenderObject* compactChild = compactInfo.compact();
        int compactXPos = borderLeft() + paddingLeft() + compactChild->marginLeft();
        if (style()->direction() == RTL) {
            compactChild->calcWidth(); // have to do this because of the capped maxwidth
            compactXPos = width() - borderRight() - paddingRight() - marginRight() -
                compactChild->width() - compactChild->marginRight();
        }
        compactXPos -= child->xPos(); // Put compactXPos into the child's coordinate space.
        compactChild->setPos(compactXPos, compactChild->yPos()); // Set the x position.
        compactInfo.clear();
    }
}
919

920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947
RenderObject* RenderBlock::handleRunInChild(RenderObject* child, bool& handled)
{
    // See if we have a run-in element with inline children.  If the
    // children aren't inline, then just treat the run-in as a normal
    // block.
    if (child->isRunIn() && (child->childrenInline() || child->isReplaced())) {
        // Get the next non-positioned/non-floating RenderBlock.
        RenderObject* curr = child->nextSibling();
        while (curr && curr->isFloatingOrPositioned())
            curr = curr->nextSibling();
        if (curr && (curr->isRenderBlock() && curr->childrenInline() && !curr->isCompact() && !curr->isRunIn())) {
            // The block acts like an inline, so just null out its
            // position.
            handled = true;
            child->setInline(true);
            child->setPos(0,0);
            
            // Remove the child.
            RenderObject* next = child->nextSibling();
            removeChildNode(child);
            
            // Now insert the child under |curr|.
            curr->insertChildNode(child, curr->firstChild());
            return next;
        }
    }
    return 0;
}
948

949 950 951 952 953
void RenderBlock::collapseMargins(RenderObject* child, MarginInfo& marginInfo, int yPosEstimate)
{
    // Get our max pos and neg top margins.
    int posTop = child->maxTopMargin(true);
    int negTop = child->maxTopMargin(false);
954

955 956 957
    // For self-collapsing blocks, collapse our bottom margins into our
    // top to get new posTop and negTop values.
    if (child->isSelfCollapsingBlock()) {
darin's avatar
darin committed
958 959
        posTop = max(posTop, child->maxBottomMargin(true));
        negTop = max(negTop, child->maxBottomMargin(false));
960 961 962 963 964 965 966 967 968 969
    }
    
    // See if the top margin is quirky. We only care if this child has
    // margins that will collapse with us.
    bool topQuirk = child->isTopMarginQuirk() || style()->marginTopCollapse() == MDISCARD;

    if (marginInfo.canCollapseWithTop()) {
        // This child is collapsing with the top of the
        // block.  If it has larger margin values, then we need to update
        // our own maximal values.
antti's avatar
antti committed
970 971
        if (!style()->htmlHacks() || !marginInfo.quirkContainer() || !topQuirk)
            setMaxTopMargins(max(posTop, maxTopPosMargin()), max(negTop, maxTopNegMargin()));
972

973 974 975 976 977 978 979 980
        // The minute any of the margins involved isn't a quirk, don't
        // collapse it away, even if the margin is smaller (www.webreference.com
        // has an example of this, a <dt> with 0.8em author-specified inside
        // a <dl> inside a <td>.
        if (!marginInfo.determinedTopQuirk() && !topQuirk && (posTop-negTop)) {
            m_topMarginQuirk = false;
            marginInfo.setDeterminedTopQuirk(true);
        }
981

982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998
        if (!marginInfo.determinedTopQuirk() && topQuirk && marginTop() == 0)
            // We have no top margin and our top child has a quirky margin.
            // We will pick up this quirky margin and pass it through.
            // This deals with the <td><div><p> case.
            // Don't do this for a block that split two inlines though.  You do
            // still apply margins in this case.
            m_topMarginQuirk = true;
    }

    if (marginInfo.quirkContainer() && marginInfo.atTopOfBlock() && (posTop - negTop))
        marginInfo.setTopQuirk(topQuirk);

    int ypos = m_height;
    if (child->isSelfCollapsingBlock()) {
        // This child has no height.  We need to compute our
        // position before we collapse the child's margins together,
        // so that we can get an accurate position for the zero-height block.
darin's avatar
darin committed
999 1000
        int collapsedTopPos = max(marginInfo.posMargin(), child->maxTopMargin(true));
        int collapsedTopNeg = max(marginInfo.negMargin(), child->maxTopMargin(false));
1001
        marginInfo.setMargin(collapsedTopPos, collapsedTopNeg);
hyatt's avatar