diff options
Diffstat (limited to 'WebCore/rendering/RenderBlock.cpp')
-rw-r--r-- | WebCore/rendering/RenderBlock.cpp | 1326 |
1 files changed, 833 insertions, 493 deletions
diff --git a/WebCore/rendering/RenderBlock.cpp b/WebCore/rendering/RenderBlock.cpp index 0d73961..5b45501 100644 --- a/WebCore/rendering/RenderBlock.cpp +++ b/WebCore/rendering/RenderBlock.cpp @@ -25,6 +25,7 @@ #include "Document.h" #include "Element.h" +#include "FloatQuad.h" #include "Frame.h" #include "FrameView.h" #include "GraphicsContext.h" @@ -58,6 +59,12 @@ const int verticalLineClickFudgeFactor= 3; using namespace HTMLNames; +static void moveChild(RenderObject* to, RenderObjectChildList* toChildList, RenderObject* from, RenderObjectChildList* fromChildList, RenderObject* child) +{ + ASSERT(from == child->parent()); + toChildList->appendChildNode(to, fromChildList->removeChildNode(from, child, false), false); +} + struct ColumnInfo { ColumnInfo() : m_desiredColumnWidth(0) @@ -77,7 +84,7 @@ static PercentHeightDescendantsMap* gPercentHeightDescendantsMap = 0; typedef WTF::HashMap<const RenderBox*, HashSet<RenderBlock*>*> PercentHeightContainerMap; static PercentHeightContainerMap* gPercentHeightContainerMap = 0; -typedef WTF::HashMap<RenderBlock*, ListHashSet<RenderFlow*>*> ContinuationOutlineTableMap; +typedef WTF::HashMap<RenderBlock*, ListHashSet<RenderInline*>*> ContinuationOutlineTableMap; // Our MarginInfo state used when laying out block children. RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, int top, int bottom) @@ -114,15 +121,18 @@ RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, int top, int bottom) // ------------------------------------------------------------------------------------------------------- RenderBlock::RenderBlock(Node* node) - : RenderFlow(node) + : RenderBox(node) , m_floatingObjects(0) , m_positionedObjects(0) + , m_inlineContinuation(0) , m_maxMargin(0) , m_overflowHeight(0) , m_overflowWidth(0) , m_overflowLeft(0) , m_overflowTop(0) + , m_lineHeight(-1) { + setChildrenInline(true); } RenderBlock::~RenderBlock() @@ -131,7 +141,7 @@ RenderBlock::~RenderBlock() delete m_positionedObjects; delete m_maxMargin; - if (m_hasColumns) + if (hasColumns()) delete gColumnInfoMap->take(this); if (gPercentHeightDescendantsMap) { @@ -154,11 +164,49 @@ RenderBlock::~RenderBlock() } } -void RenderBlock::styleWillChange(RenderStyle::Diff diff, const RenderStyle* newStyle) +void RenderBlock::destroy() +{ + // Detach our continuation first. + if (m_inlineContinuation) + m_inlineContinuation->destroy(); + m_inlineContinuation = 0; + + // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will + // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise. + children()->destroyLeftoverChildren(); + + if (!documentBeingDestroyed()) { + if (firstLineBox()) { + // We can't wait for RenderBox::destroy to clear the selection, + // because by then we will have nuked the line boxes. + // FIXME: The SelectionController should be responsible for this when it + // is notified of DOM mutations. + if (isSelectionBorder()) + view()->clearSelection(); + + // If we are an anonymous block, then our line boxes might have children + // that will outlast this block. In the non-anonymous block case those + // children will be destroyed by the time we return from this function. + if (isAnonymousBlock()) { + for (InlineFlowBox* box = firstLineBox(); box; box = box->nextFlowBox()) { + while (InlineBox* childBox = box->firstChild()) + childBox->remove(); + } + } + } else if (isInline() && parent()) + parent()->dirtyLinesFromChangedChild(this); + } + + m_lineBoxes.deleteLineBoxes(renderArena()); + + RenderBox::destroy(); +} + +void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) { setReplaced(newStyle->isDisplayReplacedType()); - if (style() && parent() && diff == RenderStyle::Layout && style()->position() != newStyle->position()) { + if (style() && parent() && diff == StyleDifferenceLayout && style()->position() != newStyle->position()) { if (newStyle->position() == StaticPosition) // Clear our positioned objects list. Our absolutely positioned descendants will be // inserted into our containing block's positioned objects list during layout. @@ -176,16 +224,16 @@ void RenderBlock::styleWillChange(RenderStyle::Diff diff, const RenderStyle* new } if (cb->isRenderBlock()) - static_cast<RenderBlock*>(cb)->removePositionedObjects(this); + toRenderBlock(cb)->removePositionedObjects(this); } } - RenderFlow::styleWillChange(diff, newStyle); + RenderBox::styleWillChange(diff, newStyle); } -void RenderBlock::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { - RenderFlow::styleDidChange(diff, oldStyle); + RenderBox::styleDidChange(diff, oldStyle); // FIXME: We could save this call when the change only affected non-inherited properties for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { @@ -201,13 +249,21 @@ void RenderBlock::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldS // Update pseudos for :before and :after now. if (!isAnonymous() && document()->usesBeforeAfterRules() && canHaveChildren()) { - updateBeforeAfterContent(RenderStyle::BEFORE); - updateBeforeAfterContent(RenderStyle::AFTER); + updateBeforeAfterContent(BEFORE); + updateBeforeAfterContent(AFTER); } updateFirstLetter(); } -void RenderBlock::addChildToFlow(RenderObject* newChild, RenderObject* beforeChild) +void RenderBlock::updateBeforeAfterContent(PseudoId pseudoId) +{ + // If this is an anonymous wrapper, then the parent applies its own pseudo-element style to it. + if (parent() && parent()->createsAnonymousWrapper()) + return; + return children()->updateBeforeAfterContent(this, pseudoId); +} + +void RenderBlock::addChild(RenderObject* newChild, RenderObject* beforeChild) { // Make sure we don't append things after :after-generated content if we have it. if (!beforeChild && isAfterContent(lastChild())) @@ -231,13 +287,13 @@ void RenderBlock::addChildToFlow(RenderObject* newChild, RenderObject* beforeChi if (newChild->isInline() || beforeChild->parent()->firstChild() != beforeChild) beforeChild->parent()->addChild(newChild, beforeChild); else - addChildToFlow(newChild, beforeChild->parent()); + addChild(newChild, beforeChild->parent()); return; } ASSERT(anonymousChild->isTable()); - if (newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP - || newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION + if ((newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP) + || (newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION) || newChild->isTableSection() || newChild->isTableRow() || newChild->isTableCell()) { @@ -253,7 +309,7 @@ void RenderBlock::addChildToFlow(RenderObject* newChild, RenderObject* beforeChi // 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 // inline children into anonymous block boxes. - if (m_childrenInline && !newChild->isInline() && !newChild->isFloatingOrPositioned()) { + if (childrenInline() && !newChild->isInline() && !newChild->isFloatingOrPositioned()) { // This is a block with inline content. Wrap the inline content in anonymous blocks. makeChildrenNonInline(beforeChild); madeBoxesNonInline = true; @@ -263,7 +319,7 @@ void RenderBlock::addChildToFlow(RenderObject* newChild, RenderObject* beforeChi ASSERT(beforeChild->isAnonymousBlock()); ASSERT(beforeChild->parent() == this); } - } else if (!m_childrenInline && (newChild->isFloatingOrPositioned() || newChild->isInline())) { + } else if (!childrenInline() && (newChild->isFloatingOrPositioned() || newChild->isInline())) { // 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. @@ -277,17 +333,16 @@ void RenderBlock::addChildToFlow(RenderObject* newChild, RenderObject* beforeChi if (newChild->isInline()) { // No suitable existing anonymous box - create a new one. RenderBlock* newBox = createAnonymousBlock(); - RenderContainer::addChild(newBox, beforeChild); + RenderBox::addChild(newBox, beforeChild); newBox->addChild(newChild); return; } } - RenderContainer::addChild(newChild, beforeChild); - // ### care about aligned stuff + RenderBox::addChild(newChild, beforeChild); - if (madeBoxesNonInline && parent() && isAnonymousBlock()) - parent()->removeLeftoverAnonymousBlock(this); + if (madeBoxesNonInline && parent() && isAnonymousBlock() && parent()->isRenderBlock()) + toRenderBlock(parent())->removeLeftoverAnonymousBlock(this); // this object may be dead here } @@ -333,14 +388,19 @@ static void getInlineRun(RenderObject* start, RenderObject* boundary, void RenderBlock::deleteLineBoxTree() { - InlineFlowBox* line = m_firstLineBox; - InlineFlowBox* nextLine; - while (line) { - nextLine = line->nextFlowBox(); - line->deleteLine(renderArena()); - line = nextLine; - } - m_firstLineBox = m_lastLineBox = 0; + m_lineBoxes.deleteLineBoxTree(renderArena()); +} + +RootInlineBox* RenderBlock::createRootBox() +{ + return new (renderArena()) RootInlineBox(this); +} + +RootInlineBox* RenderBlock::createRootInlineBox() +{ + RootInlineBox* rootBox = createRootBox(); + m_lineBoxes.appendLineBox(rootBox); + return rootBox; } void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) @@ -355,7 +415,7 @@ void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) ASSERT(isInlineBlockOrInlineTable() || !isInline()); ASSERT(!insertionPoint || insertionPoint->parent() == this); - m_childrenInline = false; + setChildrenInline(false); RenderObject *child = firstChild(); if (!child) @@ -372,16 +432,16 @@ void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) child = inlineRunEnd->nextSibling(); - RenderBlock* box = createAnonymousBlock(); - insertChildNode(box, inlineRunStart); + RenderBlock* block = createAnonymousBlock(); + children()->insertChildNode(this, block, inlineRunStart); RenderObject* o = inlineRunStart; - while(o != inlineRunEnd) - { + while (o != inlineRunEnd) { RenderObject* no = o; o = no->nextSibling(); - box->moveChildNode(no); + + moveChild(block, block->children(), this, children(), no); } - box->moveChildNode(inlineRunEnd); + moveChild(block, block->children(), this, children(), inlineRunEnd); } #ifndef NDEBUG @@ -392,7 +452,49 @@ void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) repaint(); } -void RenderBlock::removeChild(RenderObject *oldChild) +void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child) +{ + ASSERT(child->isAnonymousBlock()); + ASSERT(!child->childrenInline()); + + if (child->inlineContinuation()) + return; + + RenderObject* firstAnChild = child->m_children.firstChild(); + RenderObject* lastAnChild = child->m_children.lastChild(); + if (firstAnChild) { + RenderObject* o = firstAnChild; + while (o) { + o->setParent(this); + o = o->nextSibling(); + } + firstAnChild->setPreviousSibling(child->previousSibling()); + lastAnChild->setNextSibling(child->nextSibling()); + if (child->previousSibling()) + child->previousSibling()->setNextSibling(firstAnChild); + if (child->nextSibling()) + child->nextSibling()->setPreviousSibling(lastAnChild); + } else { + if (child->previousSibling()) + child->previousSibling()->setNextSibling(child->nextSibling()); + if (child->nextSibling()) + child->nextSibling()->setPreviousSibling(child->previousSibling()); + } + if (child == m_children.firstChild()) + m_children.setFirstChild(firstAnChild); + if (child == m_children.lastChild()) + m_children.setLastChild(lastAnChild); + child->setParent(0); + child->setPreviousSibling(0); + child->setNextSibling(0); + + child->children()->setFirstChild(0); + child->m_next = 0; + + child->destroy(); +} + +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 @@ -400,7 +502,7 @@ void RenderBlock::removeChild(RenderObject *oldChild) RenderObject* prev = oldChild->previousSibling(); RenderObject* next = oldChild->nextSibling(); bool canDeleteAnonymousBlocks = !documentBeingDestroyed() && !isInline() && !oldChild->isInline() && - !oldChild->virtualContinuation() && + (!oldChild->isRenderBlock() || !toRenderBlock(oldChild)->inlineContinuation()) && (!prev || (prev->isAnonymousBlock() && prev->childrenInline())) && (!next || (next->isAnonymousBlock() && next->childrenInline())); if (canDeleteAnonymousBlocks && prev && next) { @@ -408,20 +510,22 @@ void RenderBlock::removeChild(RenderObject *oldChild) // the |prev| block. prev->setNeedsLayoutAndPrefWidthsRecalc(); RenderObject* o = next->firstChild(); + + RenderBlock* nextBlock = toRenderBlock(next); + RenderBlock* prevBlock = toRenderBlock(prev); while (o) { RenderObject* no = o; o = no->nextSibling(); - prev->moveChildNode(no); + moveChild(prevBlock, prevBlock->children(), nextBlock, nextBlock->children(), no); } - RenderBlock* nextBlock = static_cast<RenderBlock*>(next); nextBlock->deleteLineBoxTree(); // Nuke the now-empty block. next->destroy(); } - RenderFlow::removeChild(oldChild); + RenderBox::removeChild(oldChild); RenderObject* child = prev ? prev : next; if (canDeleteAnonymousBlocks && child && !child->previousSibling() && !child->nextSibling() && !isFlexibleBox()) { @@ -429,13 +533,13 @@ void RenderBlock::removeChild(RenderObject *oldChild) // box. We can go ahead and pull the content right back up into our // box. setNeedsLayoutAndPrefWidthsRecalc(); - RenderBlock* anonBlock = static_cast<RenderBlock*>(removeChildNode(child, false)); - m_childrenInline = true; + RenderBlock* anonBlock = toRenderBlock(children()->removeChildNode(this, child, false)); + setChildrenInline(true); RenderObject* o = anonBlock->firstChild(); while (o) { RenderObject* no = o; o = no->nextSibling(); - moveChildNode(no); + moveChild(this, children(), anonBlock, anonBlock->children(), no); } // Delete the now-empty block's lines and nuke it. @@ -615,15 +719,8 @@ void RenderBlock::layoutBlock(bool relayoutChildren) if (!relayoutChildren && layoutOnlyPositionedObjects()) return; - IntRect oldBounds; - IntRect oldOutlineBox; - bool checkForRepaint = m_everHadLayout && checkForRepaintDuringLayout(); - if (checkForRepaint) { - oldBounds = absoluteClippedOverflowRect(); - oldOutlineBox = absoluteOutlineBounds(); - } - - LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), m_hasColumns || hasTransform() || hasReflection()); + LayoutRepainter repainter(*this, m_everHadLayout && checkForRepaintDuringLayout()); + LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), hasColumns() || hasTransform() || hasReflection()); int oldWidth = width(); int oldColumnWidth = desiredColumnWidth(); @@ -670,11 +767,11 @@ void RenderBlock::layoutBlock(bool relayoutChildren) if (!isCell) { initMaxMarginValues(); - m_topMarginQuirk = style()->marginTop().quirk(); - m_bottomMarginQuirk = style()->marginBottom().quirk(); + setTopMarginQuirk(style()->marginTop().quirk()); + setBottomMarginQuirk(style()->marginBottom().quirk()); - Node* node = element(); - if (node && node->hasTagName(formTag) && static_cast<HTMLFormElement*>(node)->isMalformed()) { + Node* n = node(); + if (n && n->hasTagName(formTag) && static_cast<HTMLFormElement*>(n)->isMalformed()) { // See if this form is malformed (i.e., unclosed). If so, don't give the form // a bottom margin. setMaxBottomMargins(0, 0); @@ -684,9 +781,9 @@ void RenderBlock::layoutBlock(bool relayoutChildren) // For overflow:scroll blocks, ensure we have both scrollbars in place always. if (scrollsOverflow()) { if (style()->overflowX() == OSCROLL) - m_layer->setHasHorizontalScrollbar(true); + layer()->setHasHorizontalScrollbar(true); if (style()->overflowY() == OSCROLL) - m_layer->setHasVerticalScrollbar(true); + layer()->setHasVerticalScrollbar(true); } int repaintTop = 0; @@ -714,7 +811,7 @@ void RenderBlock::layoutBlock(bool relayoutChildren) // 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); + RenderBlock* block = toRenderBlock(child); if (block->floatBottom() + block->y() > height()) addOverhangingFloats(block, -block->x(), -block->y(), false); } @@ -762,12 +859,10 @@ void RenderBlock::layoutBlock(bool relayoutChildren) // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if // we overflow or not. if (hasOverflowClip()) - m_layer->updateScrollInfoAfterLayout(); + layer()->updateScrollInfoAfterLayout(); // Repaint with our new bounds if they are different from our old bounds. - bool didFullRepaint = false; - if (checkForRepaint) - didFullRepaint = repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + bool didFullRepaint = repainter.repaintAfterLayout(); if (!didFullRepaint && repaintTop != repaintBottom && (style()->visibility() == VISIBLE || enclosingLayer()->hasVisibleContent())) { IntRect repaintRect(m_overflowLeft, repaintTop, m_overflowWidth - m_overflowLeft, repaintBottom - repaintTop); @@ -800,19 +895,19 @@ void RenderBlock::layoutBlock(bool relayoutChildren) bool RenderBlock::expandsToEncloseOverhangingFloats() const { - return isInlineBlockOrInlineTable() || isFloatingOrPositioned() || hasOverflowClip() || (parent() && parent()->isFlexibleBox()) || m_hasColumns || isTableCell() || isFieldset(); + return isInlineBlockOrInlineTable() || isFloatingOrPositioned() || hasOverflowClip() || (parent() && parent()->isFlexibleBox()) || hasColumns() || isTableCell() || isFieldset(); } void RenderBlock::adjustPositionedBlock(RenderBox* child, const MarginInfo& marginInfo) { - if (child->hasStaticX()) { + if (child->style()->hasStaticX()) { if (style()->direction() == LTR) - child->setStaticX(borderLeft() + paddingLeft()); + child->layer()->setStaticX(borderLeft() + paddingLeft()); else - child->setStaticX(borderRight() + paddingRight()); + child->layer()->setStaticX(borderRight() + paddingRight()); } - if (child->hasStaticY()) { + if (child->style()->hasStaticY()) { int y = height(); if (!marginInfo.canCollapseWithTop()) { child->calcVerticalMargins(); @@ -828,7 +923,7 @@ void RenderBlock::adjustPositionedBlock(RenderBox* child, const MarginInfo& marg } y += (collapsedTopPos - collapsedTopNeg) - marginTop; } - child->setStaticY(y); + child->layer()->setStaticY(y); } } @@ -892,24 +987,27 @@ RenderBox* RenderBlock::handleFloatingChild(RenderBox* child, const MarginInfo& return 0; } -RenderBox* RenderBlock::handleRunInChild(RenderBox* blockRunIn, bool& handled) +RenderBox* RenderBlock::handleRunInChild(RenderBox* 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 (blockRunIn->isRunIn() && (blockRunIn->childrenInline() || blockRunIn->isReplaced())) { + if (child->isRunIn() && (child->childrenInline() || child->isReplaced())) { + RenderBlock* blockRunIn = toRenderBlock(child); // Get the next non-positioned/non-floating RenderBlock. RenderObject* curr = blockRunIn->nextSibling(); while (curr && curr->isFloatingOrPositioned()) curr = curr->nextSibling(); if (curr && (curr->isRenderBlock() && curr->childrenInline() && !curr->isRunIn())) { + RenderBlock* currBlock = toRenderBlock(curr); + // The block acts like an inline, so just null out its // position. handled = true; // Remove the old child. RenderBox* next = blockRunIn->nextSiblingBox(); - removeChildNode(blockRunIn); + children()->removeChildNode(this, blockRunIn); // Create an inline. RenderInline* inlineRunIn = new (renderArena()) RenderInline(blockRunIn->node()); @@ -918,18 +1016,18 @@ RenderBox* RenderBlock::handleRunInChild(RenderBox* blockRunIn, bool& handled) // Move the nodes from the old child to the new child, but skip any :before/:after content. It has already // been regenerated by the new inline. for (RenderObject* runInChild = blockRunIn->firstChild(); runInChild; runInChild = runInChild->nextSibling()) { - if (runInChild->style()->styleType() != RenderStyle::BEFORE && runInChild->style()->styleType() != RenderStyle::AFTER) { - blockRunIn->removeChildNode(runInChild, false); + if (runInChild->style()->styleType() != BEFORE && runInChild->style()->styleType() != AFTER) { + blockRunIn->children()->removeChildNode(blockRunIn, runInChild, false); inlineRunIn->addChild(runInChild); // Use addChild instead of appendChildNode since it handles correct placement of the children relative to :after-generated content. } } - // Now insert the new child under |curr|. - curr->insertChildNode(inlineRunIn, curr->firstChild()); + // Now insert the new child under |currBlock|. + currBlock->children()->insertChildNode(currBlock, inlineRunIn, currBlock->firstChild()); // If the run-in had an element, we need to set the new renderer. - if (blockRunIn->element()) - blockRunIn->element()->setRenderer(inlineRunIn); + if (blockRunIn->node()) + blockRunIn->node()->setRenderer(inlineRunIn); // Destroy the block run-in. blockRunIn->destroy(); @@ -940,7 +1038,7 @@ RenderBox* RenderBlock::handleRunInChild(RenderBox* blockRunIn, bool& handled) return 0; } -void RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo, int yPosEstimate) +int RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo) { // Get our max pos and neg top margins. int posTop = child->maxTopMargin(true); @@ -969,7 +1067,7 @@ void RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo, int // 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; + setTopMarginQuirk(false); marginInfo.setDeterminedTopQuirk(true); } @@ -979,7 +1077,7 @@ void RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo, int // 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; + setTopMarginQuirk(true); } if (marginInfo.quirkContainer() && marginInfo.atTopOfBlock() && (posTop - negTop)) @@ -1028,34 +1126,15 @@ void RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo, int marginInfo.setSelfCollapsingBlockClearedFloat(false); } - - view()->addLayoutDelta(IntSize(0, yPosEstimate - ypos)); - child->setLocation(child->x(), ypos); - if (ypos != yPosEstimate) { - if (child->shrinkToAvoidFloats()) - // The child's width depends on the line width. - // When the child shifts to clear an item, its width can - // change (because it has more available line width). - // So go ahead and mark the item as dirty. - child->setChildNeedsLayout(true, false); - - if (!child->avoidsFloats() && child->containsFloats()) - static_cast<RenderBlock*>(child)->markAllDescendantsWithFloatsForLayout(); - - // Our guess was wrong. Make the child lay itself out again. - child->layoutIfNeeded(); - } + + return ypos; } -void RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& marginInfo, int oldTopPosMargin, int oldTopNegMargin) +int RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& marginInfo, int oldTopPosMargin, int oldTopNegMargin, int yPos) { - int heightIncrease = getClearDelta(child); + int heightIncrease = getClearDelta(child, yPos); if (!heightIncrease) - return; - - // The child needs to be lowered. Move the child so that it just clears the float. - view()->addLayoutDelta(IntSize(0, -heightIncrease)); - child->setLocation(child->x(), child->y() + heightIncrease); + return yPos; if (child->isSelfCollapsingBlock()) { // For self-collapsing blocks that clear, they can still collapse their @@ -1084,19 +1163,8 @@ void RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& marginInfo, setMaxTopMargins(oldTopPosMargin, oldTopNegMargin); marginInfo.setAtTopOfBlock(false); } - - // If our value of clear caused us to be repositioned vertically to be - // underneath a float, we might have to do another layout to take into account - // the extra space we now have available. - if (child->shrinkToAvoidFloats()) - // The child's width depends on the line width. - // When the child shifts to clear an item, its width can - // change (because it has more available line width). - // So go ahead and mark the item as dirty. - child->setChildNeedsLayout(true, false); - if (!child->avoidsFloats() && child->containsFloats()) - static_cast<RenderBlock*>(child)->markAllDescendantsWithFloatsForLayout(); - child->layoutIfNeeded(); + + return yPos + heightIncrease; } int RenderBlock::estimateVerticalPosition(RenderBox* child, const MarginInfo& marginInfo) @@ -1108,6 +1176,7 @@ int RenderBlock::estimateVerticalPosition(RenderBox* child, const MarginInfo& ma int childMarginTop = child->selfNeedsLayout() ? child->marginTop() : child->collapsedMarginTop(); yPosEstimate += max(marginInfo.margin(), childMarginTop); } + yPosEstimate += getClearDelta(child, yPosEstimate); return yPosEstimate; } @@ -1122,7 +1191,7 @@ void RenderBlock::determineHorizontalPosition(RenderBox* child) // Some objects (e.g., tables, horizontal rules, overflow:auto blocks) avoid floats. They need // to shift over as necessary to dodge any floats that might get in the way. if (child->avoidsFloats()) { - int leftOff = leftOffset(height()); + int leftOff = leftOffset(height(), false); if (style()->textAlign() != WEBKIT_CENTER && child->style()->marginLeft().type() != Auto) { if (child->marginLeft() < 0) leftOff += child->marginLeft(); @@ -1134,7 +1203,7 @@ void RenderBlock::determineHorizontalPosition(RenderBox* child) // width computation will take into account the delta between |leftOff| and |xPos| // so that we can just pass the content width in directly to the |calcHorizontalMargins| // function. - child->calcHorizontalMargins(child->style()->marginLeft(), child->style()->marginRight(), lineWidth(child->y())); + child->calcHorizontalMargins(child->style()->marginLeft(), child->style()->marginRight(), lineWidth(child->y(), false)); chPos = leftOff + child->marginLeft(); } } @@ -1144,7 +1213,7 @@ void RenderBlock::determineHorizontalPosition(RenderBox* child) int xPos = width() - borderRight() - paddingRight() - verticalScrollbarWidth(); int chPos = xPos - (child->width() + child->marginRight()); if (child->avoidsFloats()) { - int rightOff = rightOffset(height()); + int rightOff = rightOffset(height(), false); if (style()->textAlign() != WEBKIT_CENTER && child->style()->marginRight().type() != Auto) { if (child->marginRight() < 0) rightOff -= child->marginRight(); @@ -1155,7 +1224,7 @@ void RenderBlock::determineHorizontalPosition(RenderBox* child) // width computation will take into account the delta between |rightOff| and |xPos| // so that we can just pass the content width in directly to the |calcHorizontalMargins| // function. - toRenderBox(child)->calcHorizontalMargins(child->style()->marginLeft(), child->style()->marginRight(), lineWidth(child->y())); + child->calcHorizontalMargins(child->style()->marginLeft(), child->style()->marginRight(), lineWidth(child->y(), false)); chPos = rightOff - child->marginRight() - child->width(); } } @@ -1172,13 +1241,13 @@ void RenderBlock::setCollapsedBottomMargin(const MarginInfo& marginInfo) setMaxBottomMargins(max(maxBottomPosMargin(), marginInfo.posMargin()), max(maxBottomNegMargin(), marginInfo.negMargin())); if (!marginInfo.bottomQuirk()) - m_bottomMarginQuirk = false; + setBottomMarginQuirk(false); if (marginInfo.bottomQuirk() && marginBottom() == 0) // We have no bottom margin and our last child has a quirky margin. // We will pick up this quirky margin and pass it through. // This deals with the <td><div><p> case. - m_bottomMarginQuirk = true; + setBottomMarginQuirk(true); } } @@ -1304,21 +1373,22 @@ void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatBottom child->setLocation(child->x(), yPosEstimate); bool markDescendantsWithFloats = false; - if (yPosEstimate != oldRect.y() && !child->avoidsFloats() && child->containsFloats()) + if (yPosEstimate != oldRect.y() && !child->avoidsFloats() && child->isBlockFlow() && toRenderBlock(child)->containsFloats()) markDescendantsWithFloats = true; else if (!child->avoidsFloats() || child->shrinkToAvoidFloats()) { // If an element might be affected by the presence of floats, then always mark it for // layout. int fb = max(previousFloatBottom, floatBottom()); - if (fb > height() || fb > yPosEstimate) + if (fb > yPosEstimate) markDescendantsWithFloats = true; } - if (markDescendantsWithFloats) - static_cast<RenderBlock*>(child)->markAllDescendantsWithFloatsForLayout(); + if (child->isRenderBlock()) { + if (markDescendantsWithFloats) + toRenderBlock(child)->markAllDescendantsWithFloatsForLayout(); - if (child->isRenderBlock()) - previousFloatBottom = max(previousFloatBottom, oldRect.y() + static_cast<RenderBlock*>(child)->floatBottom()); + previousFloatBottom = max(previousFloatBottom, oldRect.y() + toRenderBlock(child)->floatBottom()); + } bool childHadLayout = child->m_everHadLayout; bool childNeededLayout = child->needsLayout(); @@ -1327,10 +1397,28 @@ void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatBottom // Now determine the correct ypos based off examination of collapsing margin // values. - collapseMargins(child, marginInfo, yPosEstimate); + int yBeforeClear = collapseMargins(child, marginInfo); // Now check for clear. - clearFloatsIfNeeded(child, marginInfo, oldTopPosMargin, oldTopNegMargin); + int yAfterClear = clearFloatsIfNeeded(child, marginInfo, oldTopPosMargin, oldTopNegMargin, yBeforeClear); + + view()->addLayoutDelta(IntSize(0, yPosEstimate - yAfterClear)); + child->setLocation(child->x(), yAfterClear); + + // Now we have a final y position. See if it really does end up being different from our estimate. + if (yAfterClear != yPosEstimate) { + if (child->shrinkToAvoidFloats()) { + // The child's width depends on the line width. + // When the child shifts to clear an item, its width can + // change (because it has more available line width). + // So go ahead and mark the item as dirty. + child->setChildNeedsLayout(true, false); + } + if (!child->avoidsFloats() && child->isBlockFlow() && toRenderBlock(child)->containsFloats()) + toRenderBlock(child)->markAllDescendantsWithFloatsForLayout(); + // Our guess was wrong. Make the child lay itself out again. + child->layoutIfNeeded(); + } // We are no longer at the top of the block if we encounter a non-empty child. // This has to be done after checking for clear, so that margins can be reset if a clear occurred. @@ -1348,7 +1436,8 @@ void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatBottom } // If the child has overhanging floats that intrude into following siblings (or possibly out // of this block), then the parent gets notified of the floats now. - maxFloatBottom = max(maxFloatBottom, addOverhangingFloats(static_cast<RenderBlock*>(child), -child->x(), -child->y(), !childNeededLayout)); + if (child->isBlockFlow() && toRenderBlock(child)->containsFloats()) + maxFloatBottom = max(maxFloatBottom, addOverhangingFloats(toRenderBlock(child), -child->x(), -child->y(), !childNeededLayout)); // Update our overflow in case the child spills out the block. m_overflowTop = min(m_overflowTop, child->y() + child->overflowTop(false)); @@ -1384,7 +1473,7 @@ bool RenderBlock::layoutOnlyPositionedObjects() if (!posChildNeedsLayout() || normalChildNeedsLayout() || selfNeedsLayout()) return false; - LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), m_hasColumns || hasTransform() || hasReflection()); + LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), hasColumns() || hasTransform() || hasReflection()); if (needsPositionedMovementLayout()) { tryLayoutDoingPositionedMovementOnly(); @@ -1398,7 +1487,7 @@ bool RenderBlock::layoutOnlyPositionedObjects() statePusher.pop(); if (hasOverflowClip()) - m_layer->updateScrollInfoAfterLayout(); + layer()->updateScrollInfoAfterLayout(); #ifdef ANDROID_FIX // iframe flatten will call FrameView::layout() which calls performPostLayoutTasks, @@ -1422,11 +1511,11 @@ void RenderBlock::layoutPositionedObjects(bool relayoutChildren) // non-positioned block. Rather than trying to detect all of these movement cases, we just always lay out positioned // objects that are positioned implicitly like this. Such objects are rare, and so in typical DHTML menu usage (where everything is // positioned explicitly) this should not incur a performance penalty. - if (relayoutChildren || (r->hasStaticY() && r->parent() != this && r->parent()->isBlockFlow())) + if (relayoutChildren || (r->style()->hasStaticY() && r->parent() != this && r->parent()->isBlockFlow())) r->setChildNeedsLayout(true, false); // If relayoutChildren is set and we have percentage padding, we also need to invalidate the child's pref widths. - if (relayoutChildren && (r->style()->paddingLeft().isPercent() || r->style()->paddingRight().isPercent())) + //if (relayoutChildren && (r->style()->paddingLeft().isPercent() || r->style()->paddingRight().isPercent())) r->setPrefWidthsDirty(true, false); // We don't have to do a full layout. We just have to update our position. Try that first. If we have shrink-to-fit width @@ -1470,7 +1559,7 @@ void RenderBlock::repaintOverhangingFloats(bool paintAllDescendants) // Only repaint the object if it is overhanging, is not in its own layer, and // is our responsibility to paint (m_shouldPaint is set). When paintAllDescendants is true, the latter // condition is replaced with being a descendant of us. - if (r->m_bottom > height() && (paintAllDescendants && r->m_renderer->isDescendantOf(this) || r->m_shouldPaint) && !r->m_renderer->hasLayer()) { + if (r->m_bottom > height() && ((paintAllDescendants && r->m_renderer->isDescendantOf(this)) || r->m_shouldPaint) && !r->m_renderer->hasSelfPaintingLayer()) { r->m_renderer->repaint(); r->m_renderer->repaintOverhangingFloats(); } @@ -1478,7 +1567,7 @@ void RenderBlock::repaintOverhangingFloats(bool paintAllDescendants) view()->enableLayoutState(); } } - + void RenderBlock::paint(PaintInfo& paintInfo, int tx, int ty) { tx += x(); @@ -1497,52 +1586,69 @@ void RenderBlock::paint(PaintInfo& paintInfo, int tx, int ty) return; } - bool useControlClip = phase != PaintPhaseBlockBackground && phase != PaintPhaseSelfOutline && phase != PaintPhaseMask && hasControlClip(); + bool pushedClip = pushContentsClip(paintInfo, tx, ty); + paintObject(paintInfo, tx, ty); + if (pushedClip) + popContentsClip(paintInfo, phase, tx, ty); - // Push a clip. - if (useControlClip) { - if (phase == PaintPhaseOutline) - paintInfo.phase = PaintPhaseChildOutlines; - else if (phase == PaintPhaseChildBlockBackground) { - paintInfo.phase = PaintPhaseBlockBackground; - paintObject(paintInfo, tx, ty); - paintInfo.phase = PaintPhaseChildBlockBackgrounds; - } - IntRect clipRect(controlClipRect(tx, ty)); - if (clipRect.isEmpty()) - return; - paintInfo.context->save(); - paintInfo.context->clip(clipRect); - } + // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with + // z-index. We paint after we painted the background/border, so that the scrollbars will + // sit above the background/border. + if (hasOverflowClip() && style()->visibility() == VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground)) + layer()->paintOverflowControls(paintInfo.context, tx, ty, paintInfo.rect); +} - paintObject(paintInfo, tx, ty); - - // Pop the clip. - if (useControlClip) { - paintInfo.context->restore(); - if (phase == PaintPhaseOutline) { - paintInfo.phase = PaintPhaseSelfOutline; - paintObject(paintInfo, tx, ty); - paintInfo.phase = phase; - } else if (phase == PaintPhaseChildBlockBackground) - paintInfo.phase = phase; +void RenderBlock::paintColumnRules(PaintInfo& paintInfo, int tx, int ty) +{ + const Color& ruleColor = style()->columnRuleColor(); + bool ruleTransparent = style()->columnRuleIsTransparent(); + EBorderStyle ruleStyle = style()->columnRuleStyle(); + int ruleWidth = style()->columnRuleWidth(); + int colGap = columnGap(); + bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent && ruleWidth <= colGap; + if (!renderRule) + return; + + // We need to do multiple passes, breaking up our child painting into strips. + int currXOffset = 0; + int ruleAdd = borderLeft() + paddingLeft(); + int ruleX = 0; + Vector<IntRect>* colRects = columnRects(); + unsigned colCount = colRects->size(); + for (unsigned i = 0; i < colCount; i++) { + // For each rect, we clip to the rect, and then we adjust our coords. + IntRect colRect = colRects->at(i); + + // Move to the next position. + if (style()->direction() == LTR) { + ruleX += colRect.width() + colGap / 2; + currXOffset += colRect.width() + colGap; + } else { + ruleX -= (colRect.width() + colGap / 2); + currXOffset -= (colRect.width() + colGap); + } + + // Now paint the column rule. + if (i < colCount - 1) { + int ruleStart = tx + ruleX - ruleWidth / 2 + ruleAdd; + int ruleEnd = ruleStart + ruleWidth; + int ruleTop = ty + borderTop() + paddingTop(); + int ruleBottom = ruleTop + contentHeight(); + drawLineForBoxSide(paintInfo.context, ruleStart, ruleTop, ruleEnd, ruleBottom, + style()->direction() == LTR ? BSLeft : BSRight, ruleColor, style()->color(), ruleStyle, 0, 0); + } + + ruleX = currXOffset; } } -void RenderBlock::paintColumns(PaintInfo& paintInfo, int tx, int ty, bool paintingFloats) +void RenderBlock::paintColumnContents(PaintInfo& paintInfo, int tx, int ty, bool paintingFloats) { // We need to do multiple passes, breaking up our child painting into strips. GraphicsContext* context = paintInfo.context; int currXOffset = 0; int currYOffset = 0; - int ruleAdd = borderLeft() + paddingLeft(); - int ruleX = 0; int colGap = columnGap(); - const Color& ruleColor = style()->columnRuleColor(); - bool ruleTransparent = style()->columnRuleIsTransparent(); - EBorderStyle ruleStyle = style()->columnRuleStyle(); - int ruleWidth = style()->columnRuleWidth(); - bool renderRule = !paintingFloats && ruleStyle > BHIDDEN && !ruleTransparent && ruleWidth <= colGap; Vector<IntRect>* colRects = columnRects(); unsigned colCount = colRects->size(); for (unsigned i = 0; i < colCount; i++) { @@ -1568,27 +1674,14 @@ void RenderBlock::paintColumns(PaintInfo& paintInfo, int tx, int ty, bool painti paintContents(info, finalX, finalY); // Move to the next position. - if (style()->direction() == LTR) { - ruleX += colRect.width() + colGap / 2; + if (style()->direction() == LTR) currXOffset += colRect.width() + colGap; - } else { - ruleX -= (colRect.width() + colGap / 2); + else currXOffset -= (colRect.width() + colGap); - } - + currYOffset -= colRect.height(); context->restore(); - - // Now paint the column rule. - if (renderRule && paintInfo.phase == PaintPhaseForeground && i < colCount - 1) { - int ruleStart = ruleX - ruleWidth / 2 + ruleAdd; - int ruleEnd = ruleStart + ruleWidth; - drawBorder(paintInfo.context, tx + ruleStart, ty + borderTop() + paddingTop(), tx + ruleEnd, ty + borderTop() + paddingTop() + contentHeight(), - style()->direction() == LTR ? BSLeft : BSRight, ruleColor, style()->color(), ruleStyle, 0, 0); - } - - ruleX = currXOffset; } } @@ -1601,7 +1694,7 @@ void RenderBlock::paintContents(PaintInfo& paintInfo, int tx, int ty) return; if (childrenInline()) - paintLines(paintInfo, tx, ty); + m_lineBoxes.paint(this, paintInfo, tx, ty); else paintChildren(paintInfo, tx, ty); } @@ -1626,7 +1719,7 @@ void RenderBlock::paintChildren(PaintInfo& paintInfo, int tx, int ty) return; } - if (!child->hasLayer() && !child->isFloating()) + if (!child->hasSelfPaintingLayer() && !child->isFloating()) child->paint(info, tx, ty); // Check for page-break-after: always, and if it's set, break and bail. @@ -1662,9 +1755,11 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, int tx, int ty) PaintPhase paintPhase = paintInfo.phase; // 1. paint background, borders etc - if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && - hasBoxDecorations() && style()->visibility() == VISIBLE) { - paintBoxDecorations(paintInfo, tx, ty); + if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && style()->visibility() == VISIBLE) { + if (hasBoxDecorations()) + paintBoxDecorations(paintInfo, tx, ty); + if (hasColumns()) + paintColumnRules(paintInfo, tx, ty); } if (paintPhase == PaintPhaseMask && style()->visibility() == VISIBLE) { @@ -1680,12 +1775,12 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, int tx, int ty) int scrolledX = tx; int scrolledY = ty; if (hasOverflowClip()) - m_layer->subtractScrolledContentOffset(scrolledX, scrolledY); + layer()->subtractScrolledContentOffset(scrolledX, scrolledY); // 2. paint contents if (paintPhase != PaintPhaseSelfOutline) { - if (m_hasColumns) - paintColumns(paintInfo, scrolledX, scrolledY); + if (hasColumns()) + paintColumnContents(paintInfo, scrolledX, scrolledY); else paintContents(paintInfo, scrolledX, scrolledY); } @@ -1693,30 +1788,30 @@ void RenderBlock::paintObject(PaintInfo& paintInfo, int tx, int ty) // 3. paint selection // FIXME: Make this work with multi column layouts. For now don't fill gaps. bool isPrinting = document()->printing(); - if (!isPrinting && !m_hasColumns) + if (!isPrinting && !hasColumns()) paintSelection(paintInfo, scrolledX, scrolledY); // Fill in gaps in selection on lines and between blocks. // 4. paint floats. if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) { - if (m_hasColumns) - paintColumns(paintInfo, scrolledX, scrolledY, true); + if (hasColumns()) + paintColumnContents(paintInfo, scrolledX, scrolledY, true); else paintFloats(paintInfo, scrolledX, scrolledY, paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip); } // 5. paint outline. if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE) - RenderBox::paintOutline(paintInfo.context, tx, ty, width(), height(), style()); + paintOutline(paintInfo.context, tx, ty, width(), height(), style()); // 6. paint continuation outlines. if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseChildOutlines)) { - if (continuation() && continuation()->hasOutline() && continuation()->style()->visibility() == VISIBLE) { - RenderFlow* inlineFlow = static_cast<RenderFlow*>(continuation()->element()->renderer()); - if (!inlineFlow->hasLayer()) - containingBlock()->addContinuationWithOutline(inlineFlow); - else if (!inlineFlow->firstLineBox()) - inlineFlow->paintOutline(paintInfo.context, tx - x() + inlineFlow->containingBlock()->x(), - ty - y() + inlineFlow->containingBlock()->y()); + if (inlineContinuation() && inlineContinuation()->hasOutline() && inlineContinuation()->style()->visibility() == VISIBLE) { + RenderInline* inlineRenderer = toRenderInline(inlineContinuation()->node()->renderer()); + if (!inlineRenderer->hasSelfPaintingLayer()) + containingBlock()->addContinuationWithOutline(inlineRenderer); + else if (!inlineRenderer->firstLineBox()) + inlineRenderer->paintOutline(paintInfo.context, tx - x() + inlineRenderer->containingBlock()->x(), + ty - y() + inlineRenderer->containingBlock()->y()); } paintContinuationOutlines(paintInfo, tx, ty); } @@ -1739,7 +1834,7 @@ void RenderBlock::paintFloats(PaintInfo& paintInfo, int tx, int ty, bool preserv DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for (; (r = it.current()); ++it) { // Only paint the object if our m_shouldPaint flag is set. - if (r->m_shouldPaint && !r->m_renderer->hasLayer()) { + if (r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer()) { PaintInfo currentPaintInfo(paintInfo); currentPaintInfo.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground; int currentTX = tx + r->m_left - r->m_renderer->x() + r->m_renderer->marginLeft(); @@ -1767,8 +1862,8 @@ void RenderBlock::paintEllipsisBoxes(PaintInfo& paintInfo, int tx, int ty) if (style()->visibility() == VISIBLE && paintInfo.phase == PaintPhaseForeground) { // We can check the first box and last box and avoid painting if we don't // intersect. - int yPos = ty + firstLineBox()->yPos(); - int h = lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos(); + int yPos = ty + firstLineBox()->y(); + int h = lastLineBox()->y() + lastLineBox()->height() - firstLineBox()->y(); if (yPos >= paintInfo.rect.bottom() || yPos + h <= paintInfo.rect.y()) return; @@ -1776,7 +1871,7 @@ void RenderBlock::paintEllipsisBoxes(PaintInfo& paintInfo, int tx, int ty) // them. Note that boxes can easily overlap, so we can't make any assumptions // based off positions of our first line box or our last line box. for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { - yPos = ty + curr->yPos(); + yPos = ty + curr->y(); h = curr->height(); if (curr->ellipsisBox() && yPos < paintInfo.rect.bottom() && yPos + h > paintInfo.rect.y()) curr->paintEllipsisBox(paintInfo, tx, ty); @@ -1790,16 +1885,16 @@ static ContinuationOutlineTableMap* continuationOutlineTable() return &table; } -void RenderBlock::addContinuationWithOutline(RenderFlow* flow) +void RenderBlock::addContinuationWithOutline(RenderInline* flow) { // We can't make this work if the inline is in a layer. We'll just rely on the broken // way of painting. - ASSERT(!flow->layer()); + ASSERT(!flow->layer() && !flow->isInlineContinuation()); ContinuationOutlineTableMap* table = continuationOutlineTable(); - ListHashSet<RenderFlow*>* continuations = table->get(this); + ListHashSet<RenderInline*>* continuations = table->get(this); if (!continuations) { - continuations = new ListHashSet<RenderFlow*>; + continuations = new ListHashSet<RenderInline*>; table->set(this, continuations); } @@ -1812,15 +1907,15 @@ void RenderBlock::paintContinuationOutlines(PaintInfo& info, int tx, int ty) if (table->isEmpty()) return; - ListHashSet<RenderFlow*>* continuations = table->get(this); + ListHashSet<RenderInline*>* continuations = table->get(this); if (!continuations) return; // Paint each continuation outline. - ListHashSet<RenderFlow*>::iterator end = continuations->end(); - for (ListHashSet<RenderFlow*>::iterator it = continuations->begin(); it != end; ++it) { + ListHashSet<RenderInline*>::iterator end = continuations->end(); + for (ListHashSet<RenderInline*>::iterator it = continuations->begin(); it != end; ++it) { // Need to add in the coordinates of the intervening blocks. - RenderFlow* flow = *it; + RenderInline* flow = *it; RenderBlock* block = flow->containingBlock(); for ( ; block && block != this; block = block->containingBlock()) { tx += block->x(); @@ -1845,9 +1940,9 @@ void RenderBlock::setSelectionState(SelectionState s) if ((s == SelectionStart && selectionState() == SelectionEnd) || (s == SelectionEnd && selectionState() == SelectionStart)) - m_selectionState = SelectionBoth; + RenderBox::setSelectionState(SelectionBoth); else - m_selectionState = s; + RenderBox::setSelectionState(s); RenderBlock* cb = containingBlock(); if (cb && !cb->isRenderView()) @@ -1856,12 +1951,12 @@ void RenderBlock::setSelectionState(SelectionState s) bool RenderBlock::shouldPaintSelectionGaps() const { - return m_selectionState != SelectionNone && style()->visibility() == VISIBLE && isSelectionRoot(); + return selectionState() != SelectionNone && style()->visibility() == VISIBLE && isSelectionRoot(); } bool RenderBlock::isSelectionRoot() const { - if (!element()) + if (!node()) return false; // FIXME: Eventually tables should have to learn how to fill gaps between cells, at least in simple non-spanning cases. @@ -1874,22 +1969,22 @@ bool RenderBlock::isSelectionRoot() const return true; if (view() && view()->selectionStart()) { - Node* startElement = view()->selectionStart()->element(); - if (startElement && startElement->rootEditableElement() == element()) + Node* startElement = view()->selectionStart()->node(); + if (startElement && startElement->rootEditableElement() == node()) return true; } return false; } -GapRects RenderBlock::selectionGapRects() +GapRects RenderBlock::selectionGapRectsForRepaint(RenderBoxModelObject* /*repaintContainer*/) { ASSERT(!needsLayout()); if (!shouldPaintSelectionGaps()) return GapRects(); - // FIXME: this is broken with transforms + // FIXME: this is broken with transforms and a non-null repaintContainer FloatPoint absContentPoint = localToAbsolute(FloatPoint()); if (hasOverflowClip()) absContentPoint -= layer()->scrolledContentOffset(); @@ -1949,21 +2044,10 @@ GapRects RenderBlock::fillSelectionGaps(RenderBlock* rootBlock, int blockX, int // FIXME: overflow: auto/scroll regions need more math here, since painting in the border box is different from painting in the padding box (one is scrolled, the other is // fixed). GapRects result; -#ifdef ANDROID_DO_NOT_DRAW_TEXTFIELD_SELECTION - Node* node = element(); - if (node) { - Node* ancestor = node->shadowAncestorNode(); - if (ancestor && ancestor->renderer()) { - RenderObject* renderer = ancestor->renderer(); - if (renderer->isTextField() || renderer->isTextArea()) - return result; - } - } -#endif if (!isBlockFlow()) // FIXME: Make multi-column selection gap filling work someday. return result; - if (m_hasColumns || hasTransform()) { + if (hasColumns() || hasTransform()) { // FIXME: We should learn how to gap fill multiple columns and transforms eventually. lastTop = (ty - blockY) + height(); lastLeft = leftSelectionOffset(rootBlock, height()); @@ -1977,7 +2061,7 @@ GapRects RenderBlock::fillSelectionGaps(RenderBlock* rootBlock, int blockX, int result = fillBlockSelectionGaps(rootBlock, blockX, blockY, tx, ty, lastTop, lastLeft, lastRight, paintInfo); // Go ahead and fill the vertical gap all the way to the bottom of our block if the selection extends past our block. - if (rootBlock == this && (m_selectionState != SelectionBoth && m_selectionState != SelectionEnd)) + if (rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd)) result.uniteCenter(fillVerticalSelectionGap(lastTop, lastLeft, lastRight, ty + height(), rootBlock, blockX, blockY, paintInfo)); return result; @@ -2015,14 +2099,14 @@ GapRects RenderBlock::fillInlineSelectionGaps(RenderBlock* rootBlock, int blockX result.uniteCenter(fillVerticalSelectionGap(lastTop, lastLeft, lastRight, ty + selTop, rootBlock, blockX, blockY, paintInfo)); - if (!paintInfo || ty + selTop < paintInfo->rect.bottom() && ty + selTop + selHeight > paintInfo->rect.y()) + if (!paintInfo || (ty + selTop < paintInfo->rect.bottom() && ty + selTop + selHeight > paintInfo->rect.y())) result.unite(curr->fillLineSelectionGap(selTop, selHeight, rootBlock, blockX, blockY, tx, ty, paintInfo)); lastSelectedLine = curr; } if (containsStart && !lastSelectedLine) - // Selection must start just after our last line. + // VisibleSelection must start just after our last line. lastSelectedLine = lastRootBox(); if (lastSelectedLine && selectionState() != SelectionEnd && selectionState() != SelectionBoth) { @@ -2090,7 +2174,7 @@ GapRects RenderBlock::fillBlockSelectionGaps(RenderBlock* rootBlock, int blockX, lastRight = rightSelectionOffset(rootBlock, curr->y() + curr->height()); } else if (childState != SelectionNone) // We must be a block that has some selected object inside it. Go ahead and recur. - result.unite(static_cast<RenderBlock*>(curr)->fillSelectionGaps(rootBlock, blockX, blockY, tx + curr->x(), ty + curr->y(), + result.unite(toRenderBlock(curr)->fillSelectionGaps(rootBlock, blockX, blockY, tx + curr->x(), ty + curr->y(), lastTop, lastLeft, lastRight, paintInfo)); } return result; @@ -2172,7 +2256,7 @@ void RenderBlock::getHorizontalSelectionGapInfo(SelectionState state, bool& left int RenderBlock::leftSelectionOffset(RenderBlock* rootBlock, int yPos) { - int left = leftOffset(yPos); + int left = leftOffset(yPos, false); if (left == borderLeft() + paddingLeft()) { if (rootBlock != this) // The border can potentially be further extended by our containingBlock(). @@ -2192,7 +2276,7 @@ int RenderBlock::leftSelectionOffset(RenderBlock* rootBlock, int yPos) int RenderBlock::rightSelectionOffset(RenderBlock* rootBlock, int yPos) { - int right = rightOffset(yPos); + int right = rightOffset(yPos, false); if (right == (contentWidth() + (borderLeft() + paddingLeft()))) { if (rootBlock != this) // The border can potentially be further extended by our containingBlock(). @@ -2284,7 +2368,7 @@ void RenderBlock::insertFloatingObject(RenderBox* o) newObj->m_top = -1; newObj->m_bottom = -1; newObj->m_width = o->width() + o->marginLeft() + o->marginRight(); - newObj->m_shouldPaint = !o->hasLayer(); // If a layer exists, the float will paint itself. Otherwise someone else will. + newObj->m_shouldPaint = !o->hasSelfPaintingLayer(); // If a layer exists, the float will paint itself. Otherwise someone else will. newObj->m_isDescendant = true; newObj->m_renderer = o; @@ -2469,15 +2553,17 @@ void RenderBlock::removePercentHeightDescendant(RenderBox* descendant) delete containerSet; } -int -RenderBlock::leftOffset() const +HashSet<RenderBox*>* RenderBlock::percentHeightDescendants() const { - return borderLeft()+paddingLeft(); + return gPercentHeightDescendantsMap ? gPercentHeightDescendantsMap->get(this) : 0; } -int -RenderBlock::leftRelOffset(int y, int fixedOffset, bool applyTextIndent, - int *heightRemaining ) const +int RenderBlock::leftOffset() const +{ + return borderLeft() + paddingLeft(); +} + +int RenderBlock::leftRelOffset(int y, int fixedOffset, bool applyTextIndent, int* heightRemaining) const { int left = fixedOffset; if (m_floatingObjects) { @@ -2495,7 +2581,7 @@ RenderBlock::leftRelOffset(int y, int fixedOffset, bool applyTextIndent, } } - if (applyTextIndent && m_firstLine && style()->direction() == LTR) { + if (applyTextIndent && style()->direction() == LTR) { int cw = 0; if (style()->textIndent().isPercent()) cw = containingBlock()->availableWidth(); @@ -2505,15 +2591,12 @@ RenderBlock::leftRelOffset(int y, int fixedOffset, bool applyTextIndent, return left; } -int -RenderBlock::rightOffset() const +int RenderBlock::rightOffset() const { return borderLeft() + paddingLeft() + availableWidth(); } -int -RenderBlock::rightRelOffset(int y, int fixedOffset, bool applyTextIndent, - int *heightRemaining ) const +int RenderBlock::rightRelOffset(int y, int fixedOffset, bool applyTextIndent, int* heightRemaining) const { int right = fixedOffset; @@ -2532,7 +2615,7 @@ RenderBlock::rightRelOffset(int y, int fixedOffset, bool applyTextIndent, } } - if (applyTextIndent && m_firstLine && style()->direction() == RTL) { + if (applyTextIndent && style()->direction() == RTL) { int cw = 0; if (style()->textIndent().isPercent()) cw = containingBlock()->availableWidth(); @@ -2543,9 +2626,9 @@ RenderBlock::rightRelOffset(int y, int fixedOffset, bool applyTextIndent, } int -RenderBlock::lineWidth(int y) const +RenderBlock::lineWidth(int y, bool firstLine) const { - int result = rightOffset(y) - leftOffset(y); + int result = rightOffset(y, firstLine) - leftOffset(y, firstLine); return (result < 0) ? 0 : result; } @@ -2586,7 +2669,7 @@ IntRect RenderBlock::floatRect() const FloatingObject* r; DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for (; (r = it.current()); ++it) { - if (r->m_shouldPaint && !r->m_renderer->hasLayer()) { + if (r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer()) { IntRect childRect = r->m_renderer->overflowRect(false); childRect.move(r->m_left + r->m_renderer->marginLeft(), r->m_top + r->m_renderer->marginTop()); result.unite(childRect); @@ -2608,8 +2691,10 @@ int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to // the abs div. for (RenderObject* c = firstChild(); c; c = c->nextSibling()) { - if (!c->isFloatingOrPositioned() && !c->isText() && !c->isRenderInline()) - bottom = max(bottom, toRenderBox(c)->y() + c->lowestPosition(false)); + if (!c->isFloatingOrPositioned() && c->isBox()) { + RenderBox* childBox = toRenderBox(c); + bottom = max(bottom, childBox->y() + childBox->lowestPosition(false)); + } } } @@ -2642,7 +2727,7 @@ int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) } } - if (m_hasColumns) { + if (hasColumns()) { Vector<IntRect>* colRects = columnRects(); for (unsigned i = 0; i < colRects->size(); i++) bottom = max(bottom, colRects->at(i).bottom() + relativeOffset); @@ -2653,16 +2738,30 @@ int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) FloatingObject* r; DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for ( ; (r = it.current()); ++it ) { - if (r->m_shouldPaint || r->m_renderer->hasLayer()) { + if (r->m_shouldPaint || r->m_renderer->hasSelfPaintingLayer()) { int lp = r->m_top + r->m_renderer->marginTop() + r->m_renderer->lowestPosition(false); bottom = max(bottom, lp + relativeOffset); } } } - if (!includeSelf && lastLineBox()) { - int lp = lastLineBox()->yPos() + lastLineBox()->height(); - bottom = max(bottom, lp); + if (!includeSelf) { + bottom = max(bottom, borderTop() + paddingTop() + paddingBottom()); + if (childrenInline()) { + if (lastLineBox()) { + int childBottomEdge = lastLineBox()->y() + lastLineBox()->height(); + bottom = max(bottom, childBottomEdge + paddingBottom()); + } + } else { + // Find the last normal flow child. + RenderBox* currBox = lastChildBox(); + while (currBox && currBox->isFloatingOrPositioned()) + currBox = currBox->previousSiblingBox(); + if (currBox) { + int childBottomEdge = currBox->y() + currBox->height() + currBox->collapsedMarginBottom(); + bottom = max(bottom, childBottomEdge + paddingBottom()); + } + } } return bottom; @@ -2681,8 +2780,10 @@ int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSel // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to // the abs div. for (RenderObject* c = firstChild(); c; c = c->nextSibling()) { - if (!c->isFloatingOrPositioned() && c->isBox() && !c->isRenderInline()) - right = max(right, toRenderBox(c)->x() + c->rightmostPosition(false)); + if (!c->isFloatingOrPositioned() && c->isBox()) { + RenderBox* childBox = toRenderBox(c); + right = max(right, childBox->x() + childBox->rightmostPosition(false)); + } } } @@ -2716,7 +2817,7 @@ int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSel } } - if (m_hasColumns) { + if (hasColumns()) { // This only matters for LTR if (style()->direction() == LTR) right = max(columnRects()->last().right() + relativeOffset, right); @@ -2727,21 +2828,33 @@ int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSel FloatingObject* r; DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for ( ; (r = it.current()); ++it ) { - if (r->m_shouldPaint || r->m_renderer->hasLayer()) { + if (r->m_shouldPaint || r->m_renderer->hasSelfPaintingLayer()) { int rp = r->m_left + r->m_renderer->marginLeft() + r->m_renderer->rightmostPosition(false); right = max(right, rp + relativeOffset); } } } - if (!includeSelf && firstLineBox()) { - for (InlineRunBox* currBox = firstLineBox(); currBox; currBox = currBox->nextLineBox()) { - int rp = currBox->xPos() + currBox->width(); - // If this node is a root editable element, then the rightmostPosition should account for a caret at the end. - // FIXME: Need to find another way to do this, since scrollbars could show when we don't want them to. - if (node()->isContentEditable() && node() == node()->rootEditableElement() && style()->direction() == LTR) - rp += 1; - right = max(right, rp); + if (!includeSelf) { + right = max(right, borderLeft() + paddingLeft() + paddingRight()); + if (childrenInline()) { + for (InlineRunBox* currBox = firstLineBox(); currBox; currBox = currBox->nextLineBox()) { + int childRightEdge = currBox->x() + currBox->width(); + + // If this node is a root editable element, then the rightmostPosition should account for a caret at the end. + // FIXME: Need to find another way to do this, since scrollbars could show when we don't want them to. + if (node() && node()->isContentEditable() && node() == node()->rootEditableElement() && style()->direction() == LTR && !paddingRight()) + childRightEdge += 1; + right = max(right, childRightEdge + paddingRight()); + } + } else { + // Walk all normal flow children. + for (RenderBox* currBox = firstChildBox(); currBox; currBox = currBox->nextSiblingBox()) { + if (currBox->isFloatingOrPositioned()) + continue; + int childRightEdge = currBox->x() + currBox->width() + currBox->marginRight(); + right = max(right, childRightEdge + paddingRight()); + } } } @@ -2760,8 +2873,10 @@ int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to // the abs div. for (RenderObject* c = firstChild(); c; c = c->nextSibling()) { - if (!c->isFloatingOrPositioned() && c->isBox() && !c->isRenderInline()) - left = min(left, toRenderBox(c)->x() + c->leftmostPosition(false)); + if (!c->isFloatingOrPositioned() && c->isBox()) { + RenderBox* childBox = toRenderBox(c); + left = min(left, childBox->x() + childBox->leftmostPosition(false)); + } } } @@ -2795,7 +2910,7 @@ int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf } } - if (m_hasColumns) { + if (hasColumns()) { // This only matters for RTL if (style()->direction() == RTL) left = min(columnRects()->last().x() + relativeOffset, left); @@ -2806,7 +2921,7 @@ int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf FloatingObject* r; DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for ( ; (r = it.current()); ++it ) { - if (r->m_shouldPaint || r->m_renderer->hasLayer()) { + if (r->m_shouldPaint || r->m_renderer->hasSelfPaintingLayer()) { int lp = r->m_left + r->m_renderer->marginLeft() + r->m_renderer->leftmostPosition(false); left = min(left, lp + relativeOffset); } @@ -2815,7 +2930,7 @@ int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf if (!includeSelf && firstLineBox()) { for (InlineRunBox* currBox = firstLineBox(); currBox; currBox = currBox->nextLineBox()) - left = min(left, (int)currBox->xPos()); + left = min(left, (int)currBox->x()); } return left; @@ -2893,7 +3008,7 @@ void RenderBlock::clearFloats() // to avoid floats. bool parentHasFloats = false; RenderObject* prev = previousSibling(); - while (prev && (!prev->isBox() || !prev->isRenderBlock() || prev->avoidsFloats() || prev->isFloatingOrPositioned())) { + while (prev && (prev->isFloatingOrPositioned() || !prev->isBox() || !prev->isRenderBlock() || toRenderBlock(prev)->avoidsFloats())) { if (prev->isFloating()) parentHasFloats = true; prev = prev->previousSibling(); @@ -2902,7 +3017,7 @@ void RenderBlock::clearFloats() // First add in floats from the parent. int offset = y(); if (parentHasFloats) { - RenderBlock* parentBlock = static_cast<RenderBlock *>(parent()); + RenderBlock* parentBlock = toRenderBlock(parent()); addIntrudingFloats(parentBlock, parentBlock->borderLeft() + parentBlock->paddingLeft(), offset); } @@ -2918,7 +3033,7 @@ void RenderBlock::clearFloats() if (!prev || !prev->isRenderBlock()) return; - RenderBlock* block = static_cast<RenderBlock *>(prev); + RenderBlock* block = toRenderBlock(prev); if (block->m_floatingObjects && block->floatBottom() > offset) addIntrudingFloats(block, xoffset, offset); @@ -2989,8 +3104,8 @@ int RenderBlock::addOverhangingFloats(RenderBlock* child, int xoff, int yoff, bo // The nearest enclosing layer always paints the float (so that zindex and stacking // behaves properly). We always want to propagate the desire to paint the float as // far out as we can, to the outermost block that overlaps the float, stopping only - // if we hit a layer boundary. - if (r->m_renderer->enclosingLayer() == enclosingLayer()) + // if we hit a self-painting layer boundary. + if (r->m_renderer->enclosingSelfPaintingLayer() == enclosingSelfPaintingLayer()) r->m_shouldPaint = false; else floatingObj->m_shouldPaint = false; @@ -3002,7 +3117,8 @@ int RenderBlock::addOverhangingFloats(RenderBlock* child, int xoff, int yoff, bo } m_floatingObjects->append(floatingObj); } - } else if (makeChildPaintOtherFloats && !r->m_shouldPaint && !r->m_renderer->hasLayer() && r->m_renderer->isDescendantOf(child) && r->m_renderer->enclosingLayer() == child->enclosingLayer()) + } else if (makeChildPaintOtherFloats && !r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer() && + r->m_renderer->isDescendantOf(child) && r->m_renderer->enclosingLayer() == child->enclosingLayer()) // The float is not overhanging from this block, so if it is a descendant of the child, the child should // paint it (the other case is that it is intruding into the child), unless it has its own layer or enclosing // layer. @@ -3010,7 +3126,7 @@ int RenderBlock::addOverhangingFloats(RenderBlock* child, int xoff, int yoff, bo // it should paint. r->m_shouldPaint = true; - if (r->m_shouldPaint && !r->m_renderer->hasLayer()) { + if (r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer()) { IntRect floatOverflowRect = r->m_renderer->overflowRect(false); floatOverflowRect.move(r->m_left + r->m_renderer->marginLeft(), r->m_top + r->m_renderer->marginTop()); floatsOverflowRect.unite(floatOverflowRect); @@ -3070,7 +3186,7 @@ void RenderBlock::addIntrudingFloats(RenderBlock* prev, int xoff, int yoff) bool RenderBlock::avoidsFloats() const { // Floats can't intrude into our box if we have a non-auto column count or width. - return RenderFlow::avoidsFloats() || !style()->hasAutoColumnCount() || !style()->hasAutoColumnWidth(); + return RenderBox::avoidsFloats() || !style()->hasAutoColumnCount() || !style()->hasAutoColumnWidth(); } bool RenderBlock::containsFloat(RenderObject* o) @@ -3096,14 +3212,16 @@ void RenderBlock::markAllDescendantsWithFloatsForLayout(RenderBox* floatToRemove // Iterate over our children and mark them as needed. if (!childrenInline()) { for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (child->isRenderBlock() && !child->isFloatingOrPositioned() && - ((floatToRemove ? child->containsFloat(floatToRemove) : child->containsFloats()) || child->shrinkToAvoidFloats())) - static_cast<RenderBlock*>(child)->markAllDescendantsWithFloatsForLayout(floatToRemove, inLayout); + if ((!floatToRemove && child->isFloatingOrPositioned()) || !child->isRenderBlock()) + continue; + RenderBlock* childBlock = toRenderBlock(child); + if ((floatToRemove ? childBlock->containsFloat(floatToRemove) : childBlock->containsFloats()) || childBlock->shrinkToAvoidFloats()) + childBlock->markAllDescendantsWithFloatsForLayout(floatToRemove, inLayout); } } } -int RenderBlock::getClearDelta(RenderBox* child) +int RenderBlock::getClearDelta(RenderBox* child, int yPos) { // There is no need to compute clearance if we have no floats. if (!containsFloats()) @@ -3131,11 +3249,11 @@ int RenderBlock::getClearDelta(RenderBox* child) // to fit) and not all (we should be using nextFloatBottomBelow and looping). // Do not allow tables to wrap in quirks or even in almost strict mode // (ebay on the PLT, finance.yahoo.com in the real world, versiontracker.com forces even almost strict mode not to work) - int result = clearSet ? max(0, bottom - child->y()) : 0; + int result = clearSet ? max(0, bottom - yPos) : 0; if (!result && child->avoidsFloats() && child->style()->width().isFixed() && - child->minPrefWidth() > lineWidth(child->y()) && child->minPrefWidth() <= availableWidth() && + child->minPrefWidth() > lineWidth(yPos, false) && child->minPrefWidth() <= availableWidth() && document()->inStrictMode()) - result = max(0, floatBottom() - child->y()); + result = max(0, floatBottom() - yPos); return result; } @@ -3170,41 +3288,41 @@ bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& resu return false; } - if (isPointInOverflowControl(result, _x, _y, tx, ty)) { - if (hitTestAction == HitTestBlockBackground) { - updateHitTestResult(result, IntPoint(_x - tx, _y - ty)); - return true; - } - return false; + if ((hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) && isPointInOverflowControl(result, _x, _y, tx, ty)) { + updateHitTestResult(result, IntPoint(_x - tx, _y - ty)); + return true; } - // If we have lightweight control clipping, then we can't have any spillout. - if (!hasControlClip() || controlClipRect(tx, ty).contains(_x, _y)) { + // If we have clipping, then we can't have any spillout. + bool useOverflowClip = hasOverflowClip() && !hasSelfPaintingLayer(); + bool useClip = (hasControlClip() || useOverflowClip); + bool checkChildren = !useClip || (hasControlClip() ? controlClipRect(tx, ty).contains(_x, _y) : overflowClipRect(tx, ty).contains(_x, _y)); + if (checkChildren) { // Hit test descendants first. int scrolledX = tx; int scrolledY = ty; if (hasOverflowClip()) - m_layer->subtractScrolledContentOffset(scrolledX, scrolledY); + layer()->subtractScrolledContentOffset(scrolledX, scrolledY); // Hit test contents if we don't have columns. - if (!m_hasColumns && hitTestContents(request, result, _x, _y, scrolledX, scrolledY, hitTestAction)) + if (!hasColumns() && hitTestContents(request, result, _x, _y, scrolledX, scrolledY, hitTestAction)) return true; // Hit test our columns if we do have them. - if (m_hasColumns && hitTestColumns(request, result, _x, _y, scrolledX, scrolledY, hitTestAction)) + if (hasColumns() && hitTestColumns(request, result, _x, _y, scrolledX, scrolledY, hitTestAction)) return true; // Hit test floats. if (hitTestAction == HitTestFloat && m_floatingObjects) { if (isRenderView()) { - scrolledX += static_cast<RenderView*>(this)->frameView()->scrollX(); - scrolledY += static_cast<RenderView*>(this)->frameView()->scrollY(); + scrolledX += toRenderView(this)->frameView()->scrollX(); + scrolledY += toRenderView(this)->frameView()->scrollY(); } FloatingObject* o; DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); for (it.toLast(); (o = it.current()); --it) { - if (o->m_shouldPaint && !o->m_renderer->hasLayer()) { + if (o->m_shouldPaint && !o->m_renderer->hasSelfPaintingLayer()) { int xoffset = scrolledX + o->m_left + o->m_renderer->marginLeft() - o->m_renderer->x(); int yoffset = scrolledY + o->m_top + o->m_renderer->marginTop() - o->m_renderer->y(); if (o->m_renderer->hitTest(request, result, IntPoint(_x, _y), xoffset, yoffset)) { @@ -3266,7 +3384,7 @@ bool RenderBlock::hitTestContents(const HitTestRequest& request, HitTestResult& { if (childrenInline() && !isTable()) { // We have to hit-test our line boxes. - if (hitTestLines(request, result, x, y, tx, ty, hitTestAction)) { + if (m_lineBoxes.hitTest(this, request, result, x, y, tx, ty, hitTestAction)) { updateHitTestResult(result, IntPoint(x - tx, y - ty)); return true; } @@ -3275,10 +3393,8 @@ bool RenderBlock::hitTestContents(const HitTestRequest& request, HitTestResult& HitTestAction childHitTest = hitTestAction; if (hitTestAction == HitTestChildBlockBackgrounds) childHitTest = HitTestChildBlockBackground; - for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { - // FIXME: We have to skip over inline flows, since they can show up inside RenderTables at the moment (a demoted inline <form> for example). If we ever implement a - // table-specific hit-test method (which we should do for performance reasons anyway), then we can remove this check. - if (!child->hasLayer() && !child->isFloating() && !child->isRenderInline() && child->nodeAtPoint(request, result, x, y, tx, ty, childHitTest)) { + for (RenderBox* child = lastChildBox(); child; child = child->previousSiblingBox()) { + if (!child->hasSelfPaintingLayer() && !child->isFloating() && child->nodeAtPoint(request, result, x, y, tx, ty, childHitTest)) { updateHitTestResult(result, IntPoint(x - tx, y - ty)); return true; } @@ -3293,156 +3409,153 @@ Position RenderBlock::positionForBox(InlineBox *box, bool start) const if (!box) return Position(); - if (!box->object()->element()) - return Position(element(), start ? caretMinOffset() : caretMaxOffset()); + if (!box->renderer()->node()) + return Position(node(), start ? caretMinOffset() : caretMaxOffset()); if (!box->isInlineTextBox()) - return Position(box->object()->element(), start ? box->object()->caretMinOffset() : box->object()->caretMaxOffset()); + return Position(box->renderer()->node(), start ? box->renderer()->caretMinOffset() : box->renderer()->caretMaxOffset()); InlineTextBox *textBox = static_cast<InlineTextBox *>(box); - return Position(box->object()->element(), start ? textBox->start() : textBox->start() + textBox->len()); + return Position(box->renderer()->node(), start ? textBox->start() : textBox->start() + textBox->len()); } Position RenderBlock::positionForRenderer(RenderObject* renderer, bool start) const { if (!renderer) - return Position(element(), 0); + return Position(node(), 0); - Node* node = renderer->element() ? renderer->element() : element(); - if (!node) + Node* n = renderer->node() ? renderer->node() : node(); + if (!n) return Position(); - ASSERT(renderer == node->renderer()); + ASSERT(renderer == n->renderer()); int offset = start ? renderer->caretMinOffset() : renderer->caretMaxOffset(); // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now. - ASSERT(!node->isCharacterDataNode() || renderer->isText()); + ASSERT(!n->isCharacterDataNode() || renderer->isText()); - return Position(node, offset); + return Position(n, offset); } -VisiblePosition RenderBlock::positionForCoordinates(int x, int y) +// FIXME: This function should go on RenderObject as an instance method. Then +// all cases in which positionForCoordinates recurs could call this instead to +// prevent crossing editable boundaries. This would require many tests. +static VisiblePosition positionForPointRespectingEditingBoundaries(RenderBox* parent, RenderBox* child, const IntPoint& parentCoords) { - if (isTable()) - return RenderFlow::positionForCoordinates(x, y); + int xInChildCoords = parentCoords.x() - child->x(); + int yInChildCoords = parentCoords.y() - child->y(); - int top = borderTop(); - int bottom = top + paddingTop() + contentHeight() + paddingBottom(); + // If this is an anonymous renderer, we just recur normally + Node* childNode = child->node(); + if (!childNode) + return child->positionForCoordinates(xInChildCoords, yInChildCoords); - int left = borderLeft(); - int right = left + paddingLeft() + contentWidth() + paddingRight(); + // Otherwise, first make sure that the editability of the parent and child agree. + // If they don't agree, then we return a visible position just before or after the child + RenderObject* ancestor = parent; + while (ancestor && !ancestor->node()) + ancestor = ancestor->parent(); - Node* n = element(); - - int contentsX = x; - int contentsY = y; - offsetForContents(contentsX, contentsY); + // If we can't find an ancestor to check editability on, or editability is unchanged, we recur like normal + if (!ancestor || ancestor->node()->isContentEditable() == childNode->isContentEditable()) + return child->positionForCoordinates(xInChildCoords, yInChildCoords); - if (isReplaced()) { - if (y < 0 || y < height() && x < 0) - return VisiblePosition(n, caretMinOffset(), DOWNSTREAM); - if (y >= height() || y >= 0 && x >= width()) - return VisiblePosition(n, caretMaxOffset(), DOWNSTREAM); - } + // Otherwise return before or after the child, depending on if the click was left or right of the child + int childMidX = child->width() / 2; + if (xInChildCoords < childMidX) + return ancestor->createVisiblePosition(childNode->nodeIndex(), DOWNSTREAM); + return ancestor->createVisiblePosition(childNode->nodeIndex() + 1, UPSTREAM); +} - // If we start inside the shadow tree, we will stay inside (even if the point is above or below). - if (!(n && n->isShadowNode()) && !childrenInline()) { - // Don't return positions inside editable roots for coordinates outside those roots, except for coordinates outside - // a document that is entirely editable. - bool isEditableRoot = n && n->rootEditableElement() == n && !n->hasTagName(bodyTag) && !n->hasTagName(htmlTag); - - if (y < top || (isEditableRoot && (y < bottom && x < left))) { - if (!isEditableRoot) - if (RenderBox* c = firstChildBox()) { // FIXME: This code doesn't make any sense. This child could be an inline or a positioned element or a float, etc. - VisiblePosition p = c->positionForCoordinates(contentsX - c->x(), contentsY - c->y()); - if (p.isNotNull()) - return p; - } - if (n) { - if (Node* sp = n->shadowParentNode()) - n = sp; - if (Node* p = n->parent()) - return VisiblePosition(p, n->nodeIndex(), DOWNSTREAM); - } - return VisiblePosition(n, 0, DOWNSTREAM); - } +static VisiblePosition positionForPointWithInlineChildren(RenderBlock* block, const IntPoint& pointInContents) +{ + ASSERT(block->childrenInline()); - if (y >= bottom || (isEditableRoot && (y >= top && x >= right))) { - if (!isEditableRoot) - if (RenderBox* c = lastChildBox()) { // FIXME: This code doesn't make any sense. This child could be an inline or a positioned element or a float, etc. - VisiblePosition p = c->positionForCoordinates(contentsX - c->x(), contentsY - c->y()); - if (p.isNotNull()) - return p; - } - if (n) { - if (Node* sp = n->shadowParentNode()) - n = sp; - if (Node* p = n->parent()) - return VisiblePosition(p, n->nodeIndex() + 1, DOWNSTREAM); - } - return VisiblePosition(n, 0, DOWNSTREAM); + if (!block->firstRootBox()) + return block->createVisiblePosition(0, DOWNSTREAM); + + InlineBox* closestBox = 0; + // look for the closest line box in the root box which is at the passed-in y coordinate + for (RootInlineBox* root = block->firstRootBox(); root; root = root->nextRootBox()) { + int bottom; + // set the bottom based on whether there is a next root box + if (root->nextRootBox()) + // FIXME: make the break point halfway between the bottom of the previous root box and the top of the next root box + bottom = root->nextRootBox()->topOverflow(); + else + bottom = root->bottomOverflow() + verticalLineClickFudgeFactor; + // check if this root line box is located at this y coordinate + if (pointInContents.y() < bottom && root->firstChild()) { + closestBox = root->closestLeafChildForXPos(pointInContents.x()); + if (closestBox) + // pass the box a y position that is inside it + break; } } - if (childrenInline()) { - if (!firstRootBox()) - return VisiblePosition(n, 0, DOWNSTREAM); + // y coordinate is below last root line box, pretend we hit it + if (!closestBox) + closestBox = block->lastRootBox()->closestLeafChildForXPos(pointInContents.x()); - if (contentsY < firstRootBox()->topOverflow() - verticalLineClickFudgeFactor) - // y coordinate is above first root line box - return VisiblePosition(positionForBox(firstRootBox()->firstLeafChild(), true), DOWNSTREAM); - - // look for the closest line box in the root box which is at the passed-in y coordinate - for (RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) { - // set the bottom based on whether there is a next root box - if (root->nextRootBox()) - // FIXME: make the break point halfway between the bottom of the previous root box and the top of the next root box - bottom = root->nextRootBox()->topOverflow(); - else - bottom = root->bottomOverflow() + verticalLineClickFudgeFactor; - // check if this root line box is located at this y coordinate - if (contentsY < bottom && root->firstChild()) { - InlineBox* closestBox = root->closestLeafChildForXPos(x); - if (closestBox) - // pass the box a y position that is inside it - return closestBox->object()->positionForCoordinates(contentsX, closestBox->m_y); - } - } + if (closestBox) { + // pass the box a y position that is inside it + return closestBox->renderer()->positionForCoordinates(pointInContents.x(), closestBox->m_y); + } + + // Can't reach this. We have a root line box, but it has no kids. + // FIXME: This should ASSERT_NOT_REACHED(), but clicking on placeholder text + // seems to hit this codepath. + return block->createVisiblePosition(0, DOWNSTREAM); +} - if (lastRootBox()) - // y coordinate is below last root line box - return VisiblePosition(positionForBox(lastRootBox()->lastLeafChild(), false), DOWNSTREAM); +VisiblePosition RenderBlock::positionForPoint(const IntPoint& point) +{ + if (isTable()) + return RenderBox::positionForPoint(point); + + int contentsX = point.x(); + int contentsY = point.y(); + offsetForContents(contentsX, contentsY); + IntPoint pointInContents(contentsX, contentsY); - return VisiblePosition(n, 0, DOWNSTREAM); + if (isReplaced()) { + if (point.y() < 0 || (point.y() < height() && point.x() < 0)) + return createVisiblePosition(caretMinOffset(), DOWNSTREAM); + if (point.y() >= height() || (point.y() >= 0 && point.x() >= width())) + return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); + } + + if (childrenInline()) { + return positionForPointWithInlineChildren(this, pointInContents); } - - // See if any child blocks exist at this y coordinate. + + // Check top/bottom child-margin/parent-padding for clicks and place them in the first/last child + // FIXME: This will not correctly handle first or last children being positioned or non-visible if (firstChildBox() && contentsY < firstChildBox()->y()) - return VisiblePosition(n, 0, DOWNSTREAM); - for (RenderBox* renderer = firstChildBox(); renderer; renderer = renderer->nextSiblingBox()) { - if (renderer->height() == 0 || renderer->style()->visibility() != VISIBLE || renderer->isFloatingOrPositioned()) + return positionForPointRespectingEditingBoundaries(this, firstChildBox(), pointInContents); + if (lastChildBox() && contentsY > lastChildBox()->y()) + return positionForPointRespectingEditingBoundaries(this, lastChildBox(), pointInContents); + + for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) { + if (childBox->height() == 0 || childBox->style()->visibility() != VISIBLE || childBox->isFloatingOrPositioned()) continue; - RenderBox* next = renderer->nextSiblingBox(); - while (next && next->isFloatingOrPositioned()) - next = next->nextSiblingBox(); - if (next) - bottom = next->y(); - else - bottom = top + scrollHeight(); - if (contentsY >= renderer->y() && contentsY < bottom) - return renderer->positionForCoordinates(contentsX - renderer->x(), contentsY - renderer->y()); + // We hit this child if our click was above the bottom of its padding box (like IE6/7 and FF3) + if (contentsY < childBox->y() + childBox->height()) + return positionForPointRespectingEditingBoundaries(this, childBox, pointInContents); } - - return RenderFlow::positionForCoordinates(x, y); + + // We only get here if there are no, or only floated/positioned, or only + // non-visible block children below the click. + return RenderBox::positionForPoint(point); } void RenderBlock::offsetForContents(int& tx, int& ty) const { if (hasOverflowClip()) - m_layer->addScrolledContentOffset(tx, ty); + layer()->addScrolledContentOffset(tx, ty); - if (m_hasColumns) { + if (hasColumns()) { IntPoint contentsPoint(tx, ty); adjustPointToColumnContents(contentsPoint); tx = contentsPoint.x(); @@ -3453,7 +3566,7 @@ void RenderBlock::offsetForContents(int& tx, int& ty) const int RenderBlock::availableWidth() const { // If we have multiple columns, then the available width is reduced to our column width. - if (m_hasColumns) + if (hasColumns()) return desiredColumnWidth(); return contentWidth(); } @@ -3511,20 +3624,20 @@ void RenderBlock::calcColumnWidth() void RenderBlock::setDesiredColumnCountAndWidth(int count, int width) { if (count == 1) { - if (m_hasColumns) { + if (hasColumns()) { delete gColumnInfoMap->take(this); - m_hasColumns = false; + setHasColumns(false); } } else { ColumnInfo* info; - if (m_hasColumns) + if (hasColumns()) info = gColumnInfoMap->get(this); else { if (!gColumnInfoMap) gColumnInfoMap = new ColumnInfoMap; info = new ColumnInfo; gColumnInfoMap->add(this, info); - m_hasColumns = true; + setHasColumns(true); } info->m_desiredColumnCount = count; info->m_desiredColumnWidth = width; @@ -3533,21 +3646,21 @@ void RenderBlock::setDesiredColumnCountAndWidth(int count, int width) int RenderBlock::desiredColumnWidth() const { - if (!m_hasColumns) + if (!hasColumns()) return contentWidth(); return gColumnInfoMap->get(this)->m_desiredColumnWidth; } unsigned RenderBlock::desiredColumnCount() const { - if (!m_hasColumns) + if (!hasColumns()) return 1; return gColumnInfoMap->get(this)->m_desiredColumnCount; } Vector<IntRect>* RenderBlock::columnRects() const { - if (!m_hasColumns) + if (!hasColumns()) return 0; return &gColumnInfoMap->get(this)->m_columnRects; } @@ -3555,7 +3668,7 @@ Vector<IntRect>* RenderBlock::columnRects() const int RenderBlock::layoutColumns(int endOfContent) { // Don't do anything if we have no columns - if (!m_hasColumns) + if (!hasColumns()) return -1; ColumnInfo* info = gColumnInfoMap->get(this); @@ -3607,9 +3720,9 @@ int RenderBlock::layoutColumns(int endOfContent) GraphicsContext context((PlatformGraphicsContext*)0); RenderObject::PaintInfo paintInfo(&context, pageRect, PaintPhaseForeground, false, 0, 0); - m_hasColumns = false; + setHasColumns(false); paintObject(paintInfo, 0, 0); - m_hasColumns = true; + setHasColumns(true); int adjustedBottom = v->bestTruncatedAt(); if (adjustedBottom <= currY) @@ -3667,7 +3780,7 @@ int RenderBlock::layoutColumns(int endOfContent) void RenderBlock::adjustPointToColumnContents(IntPoint& point) const { // Just bail if we have no columns. - if (!m_hasColumns) + if (!hasColumns()) return; Vector<IntRect>* colRects = columnRects(); @@ -3696,7 +3809,7 @@ void RenderBlock::adjustPointToColumnContents(IntPoint& point) const void RenderBlock::adjustRectForColumns(IntRect& r) const { // Just bail if we have no columns. - if (!m_hasColumns) + if (!hasColumns()) return; Vector<IntRect>* colRects = columnRects(); @@ -3753,7 +3866,7 @@ void RenderBlock::calcPrefWidths() m_minPrefWidth = m_maxPrefWidth; // A horizontal marquee with inline children has no minimum width. - if (m_layer && m_layer->marquee() && m_layer->marquee()->isHorizontal()) + if (layer() && layer()->marquee() && layer()->marquee()->isHorizontal()) m_minPrefWidth = 0; } @@ -3855,7 +3968,7 @@ static int getBPMWidth(int childValue, Length cssUnit) return 0; } -static int getBorderPaddingMargin(const RenderBox* child, bool endOfInline) +static int getBorderPaddingMargin(const RenderBoxModelObject* child, bool endOfInline) { RenderStyle* cstyle = child->style(); int result = 0; @@ -3958,7 +4071,7 @@ void RenderBlock::calcInlinePrefWidths() if (child->isRenderInline()) { // Add in padding/border/margin from the appropriate side of // the element. - int bpm = getBorderPaddingMargin(static_cast<RenderFlow*>(child), childIterator.endOfInline); + int bpm = getBorderPaddingMargin(toRenderInline(child), childIterator.endOfInline); childMin += bpm; childMax += bpm; @@ -3990,14 +4103,14 @@ void RenderBlock::calcInlinePrefWidths() bool clearPreviousFloat; if (child->isFloating()) { clearPreviousFloat = (prevFloat - && (prevFloat->style()->floating() == FLEFT && (child->style()->clear() & CLEFT) - || prevFloat->style()->floating() == FRIGHT && (child->style()->clear() & CRIGHT))); + && ((prevFloat->style()->floating() == FLEFT && (child->style()->clear() & CLEFT)) + || (prevFloat->style()->floating() == FRIGHT && (child->style()->clear() & CRIGHT)))); prevFloat = child; } else clearPreviousFloat = false; bool canBreakReplacedElement = !child->isImage() || allowImagesToBreak; - if (canBreakReplacedElement && (autoWrap || oldAutoWrap) || clearPreviousFloat) { + if ((canBreakReplacedElement && (autoWrap || oldAutoWrap)) || clearPreviousFloat) { m_minPrefWidth = max(inlineMin, m_minPrefWidth); inlineMin = 0; } @@ -4163,7 +4276,7 @@ void RenderBlock::calcBlockPrefWidths() continue; } - if (child->isFloating() || child->avoidsFloats()) { + if (child->isFloating() || (child->isBox() && toRenderBox(child)->avoidsFloats())) { int floatTotalWidth = floatLeftWidth + floatRightWidth; if (child->style()->clear() & CLEFT) { m_maxPrefWidth = max(floatTotalWidth, m_maxPrefWidth); @@ -4197,7 +4310,7 @@ void RenderBlock::calcBlockPrefWidths() w = child->maxPrefWidth() + margin; if (!child->isFloating()) { - if (child->avoidsFloats()) { + if (child->isBox() && toRenderBox(child)->avoidsFloats()) { // Determine a left and right max value based off whether or not the floats can fit in the // margins of the object. For negative margins, we will attempt to overlap the float if the negative margin // is smaller than the float width. @@ -4253,11 +4366,11 @@ void RenderBlock::calcBlockPrefWidths() bool RenderBlock::hasLineIfEmpty() const { - return element() && (element()->isContentEditable() && element()->rootEditableElement() == element() || - element()->isShadowNode() && element()->shadowParentNode()->hasTagName(inputTag)); + return node() && ((node()->isContentEditable() && node()->rootEditableElement() == node()) || + (node()->isShadowNode() && node()->shadowParentNode()->hasTagName(inputTag))); } -int RenderBlock::lineHeight(bool b, bool isRootLineBox) const +int RenderBlock::lineHeight(bool firstLine, bool isRootLineBox) const { // Inline blocks are replaced elements. Otherwise, just pass off to // the base class. If we're being queried as though we're the root line @@ -4265,7 +4378,17 @@ int RenderBlock::lineHeight(bool b, bool isRootLineBox) const // just like a block. if (isReplaced() && !isRootLineBox) return height() + marginTop() + marginBottom(); - return RenderFlow::lineHeight(b, isRootLineBox); + + if (firstLine && document()->usesFirstLineRules()) { + RenderStyle* s = style(firstLine); + if (s != style()) + return s->computedLineHeight(); + } + + if (m_lineHeight == -1) + m_lineHeight = style()->computedLineHeight(); + + return m_lineHeight; } int RenderBlock::baselinePosition(bool b, bool isRootLineBox) const @@ -4287,29 +4410,29 @@ int RenderBlock::baselinePosition(bool b, bool isRootLineBox) const // We also give up on finding a baseline if we have a vertical scrollbar, or if we are scrolled // vertically (e.g., an overflow:hidden block that has had scrollTop moved) or if the baseline is outside // of our content box. - int baselinePos = (m_layer && (m_layer->marquee() || m_layer->verticalScrollbar() || m_layer->scrollYOffset() != 0)) ? -1 : getBaselineOfLastLineBox(); + int baselinePos = (layer() && (layer()->marquee() || layer()->verticalScrollbar() || layer()->scrollYOffset() != 0)) ? -1 : lastLineBoxBaseline(); if (baselinePos != -1 && baselinePos <= borderTop() + paddingTop() + contentHeight()) return marginTop() + baselinePos; return height() + marginTop() + marginBottom(); } - return RenderFlow::baselinePosition(b, isRootLineBox); + return RenderBox::baselinePosition(b, isRootLineBox); } -int RenderBlock::getBaselineOfFirstLineBox() const +int RenderBlock::firstLineBoxBaseline() const { if (!isBlockFlow()) - return RenderFlow::getBaselineOfFirstLineBox(); + return -1; if (childrenInline()) { if (firstLineBox()) - return firstLineBox()->yPos() + firstLineBox()->baseline(); + return firstLineBox()->y() + style(true)->font().ascent(); else return -1; } else { for (RenderBox* curr = firstChildBox(); curr; curr = curr->nextSiblingBox()) { if (!curr->isFloatingOrPositioned()) { - int result = curr->getBaselineOfFirstLineBox(); + int result = curr->firstLineBoxBaseline(); if (result != -1) return curr->y() + result; // Translate to our coordinate space. } @@ -4319,16 +4442,16 @@ int RenderBlock::getBaselineOfFirstLineBox() const return -1; } -int RenderBlock::getBaselineOfLastLineBox() const +int RenderBlock::lastLineBoxBaseline() const { if (!isBlockFlow()) - return RenderFlow::getBaselineOfLastLineBox(); + return -1; if (childrenInline()) { if (!firstLineBox() && hasLineIfEmpty()) - return RenderFlow::baselinePosition(true, true) + borderTop() + paddingTop(); + return RenderBox::baselinePosition(true, true) + borderTop() + paddingTop(); if (lastLineBox()) - return lastLineBox()->yPos() + lastLineBox()->baseline(); + return lastLineBox()->y() + style(lastLineBox() == firstLineBox())->font().ascent(); return -1; } else { @@ -4336,13 +4459,13 @@ int RenderBlock::getBaselineOfLastLineBox() const for (RenderBox* curr = lastChildBox(); curr; curr = curr->previousSiblingBox()) { if (!curr->isFloatingOrPositioned()) { haveNormalFlowChild = true; - int result = curr->getBaselineOfLastLineBox(); + int result = curr->lastLineBoxBaseline(); if (result != -1) return curr->y() + result; // Translate to our coordinate space. } } if (!haveNormalFlowChild && hasLineIfEmpty()) - return RenderFlow::baselinePosition(true, true) + borderTop() + paddingTop(); + return RenderBox::baselinePosition(true, true) + borderTop() + paddingTop(); } return -1; @@ -4364,7 +4487,7 @@ RenderBlock* RenderBlock::firstLineBlock() const RenderBlock* firstLineBlock = const_cast<RenderBlock*>(this); bool hasPseudo = false; while (true) { - hasPseudo = firstLineBlock->style()->hasPseudoStyle(RenderStyle::FIRST_LINE); + hasPseudo = firstLineBlock->style()->hasPseudoStyle(FIRST_LINE); if (hasPseudo) break; RenderObject* parentBlock = firstLineBlock->parent(); @@ -4372,7 +4495,7 @@ RenderBlock* RenderBlock::firstLineBlock() const !parentBlock || parentBlock->firstChild() != firstLineBlock || !parentBlock->isBlockFlow()) break; ASSERT(parentBlock->isRenderBlock()); - firstLineBlock = static_cast<RenderBlock*>(parentBlock); + firstLineBlock = toRenderBlock(parentBlock); } if (!hasPseudo) @@ -4385,8 +4508,8 @@ void RenderBlock::updateFirstLetter() { if (!document()->usesFirstLetterRules()) return; - // Don't recurse - if (style()->styleType() == RenderStyle::FIRST_LETTER) + // Don't recur + if (style()->styleType() == FIRST_LETTER) return; // FIXME: We need to destroy the first-letter object if it is no longer the first child. Need to find @@ -4396,7 +4519,7 @@ void RenderBlock::updateFirstLetter() while (true) { // We only honor first-letter if the firstLetterBlock can have children in the DOM. This correctly // prevents form controls from honoring first-letter. - hasPseudoStyle = firstLetterBlock->style()->hasPseudoStyle(RenderStyle::FIRST_LETTER) + hasPseudoStyle = firstLetterBlock->style()->hasPseudoStyle(FIRST_LETTER) && firstLetterBlock->canHaveChildren(); if (hasPseudoStyle) break; @@ -4414,7 +4537,7 @@ void RenderBlock::updateFirstLetter() RenderObject* currChild = firstLetterBlock->firstChild(); while (currChild && currChild->needsLayout() && (!currChild->isReplaced() || currChild->isFloatingOrPositioned()) && !currChild->isText()) { if (currChild->isFloatingOrPositioned()) { - if (currChild->style()->styleType() == RenderStyle::FIRST_LETTER) + if (currChild->style()->styleType() == FIRST_LETTER) break; currChild = currChild->nextSibling(); } else @@ -4432,8 +4555,8 @@ void RenderBlock::updateFirstLetter() // If the child already has style, then it has already been created, so we just want // to update it. - if (currChild->style()->styleType() == RenderStyle::FIRST_LETTER) { - RenderStyle* pseudo = firstLetterBlock->getCachedPseudoStyle(RenderStyle::FIRST_LETTER, + if (currChild->style()->styleType() == FIRST_LETTER) { + RenderStyle* pseudo = firstLetterBlock->getCachedPseudoStyle(FIRST_LETTER, firstLetterContainer->firstLineStyle()); currChild->setStyle(pseudo); for (RenderObject* genChild = currChild->firstChild(); genChild; genChild = genChild->nextSibling()) { @@ -4444,7 +4567,7 @@ void RenderBlock::updateFirstLetter() } // If the child does not already have style, we create it here. - if (currChild->isText() && !currChild->isBR() && currChild->parent()->style()->styleType() != RenderStyle::FIRST_LETTER) { + if (currChild->isText() && !currChild->isBR() && currChild->parent()->style()->styleType() != FIRST_LETTER) { // Our layout state is not valid for the repaints we are going to trigger by // adding and removing children of firstLetterContainer. view()->disableLayoutState(); @@ -4452,14 +4575,19 @@ void RenderBlock::updateFirstLetter() RenderText* textObj = toRenderText(currChild); // Create our pseudo style now that we have our firstLetterContainer determined. - RenderStyle* pseudoStyle = firstLetterBlock->getCachedPseudoStyle(RenderStyle::FIRST_LETTER, + RenderStyle* pseudoStyle = firstLetterBlock->getCachedPseudoStyle(FIRST_LETTER, firstLetterContainer->firstLineStyle()); // Force inline display (except for floating first-letters) - pseudoStyle->setDisplay( pseudoStyle->isFloating() ? BLOCK : INLINE); - pseudoStyle->setPosition( StaticPosition ); // CSS2 says first-letter can't be positioned. + pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE); + pseudoStyle->setPosition(StaticPosition); // CSS2 says first-letter can't be positioned. - RenderObject* firstLetter = RenderFlow::createAnonymousFlow(document(), pseudoStyle); // anonymous box + RenderObject* firstLetter = 0; + if (pseudoStyle->display() == INLINE) + firstLetter = new (renderArena()) RenderInline(document()); + else + firstLetter = new (renderArena()) RenderBlock(document()); + firstLetter->setStyle(pseudoStyle); firstLetterContainer->addChild(firstLetter, currChild); // The original string is going to be either a generated content string or a DOM node's @@ -4481,10 +4609,10 @@ void RenderBlock::updateFirstLetter() // construct text fragment for the text after the first letter // NOTE: this might empty RenderTextFragment* remainingText = - new (renderArena()) RenderTextFragment(textObj->node(), oldText.get(), length, oldText->length() - length); + new (renderArena()) RenderTextFragment(textObj->node() ? textObj->node() : textObj->document(), oldText.get(), length, oldText->length() - length); remainingText->setStyle(textObj->style()); - if (remainingText->element()) - remainingText->element()->setRenderer(remainingText); + if (remainingText->node()) + remainingText->node()->setRenderer(remainingText); RenderObject* nextObj = textObj->nextSibling(); firstLetterContainer->removeChild(textObj); @@ -4493,7 +4621,7 @@ void RenderBlock::updateFirstLetter() // construct text fragment for the first letter RenderTextFragment* letter = - new (renderArena()) RenderTextFragment(remainingText->node(), oldText.get(), 0, length); + new (renderArena()) RenderTextFragment(remainingText->node() ? remainingText->node() : remainingText->document(), oldText.get(), 0, length); RefPtr<RenderStyle> newStyle = RenderStyle::create(); newStyle->inheritFrom(pseudoStyle); letter->setStyle(newStyle.release()); @@ -4537,7 +4665,7 @@ static RootInlineBox* getLineAtIndex(RenderBlock* block, int i, int& count) else { for (RenderObject* obj = block->firstChild(); obj; obj = obj->nextSibling()) { if (shouldCheckLines(obj)) { - RootInlineBox *box = getLineAtIndex(static_cast<RenderBlock*>(obj), i, count); + RootInlineBox *box = getLineAtIndex(toRenderBlock(obj), i, count); if (box) return box; } @@ -4560,7 +4688,7 @@ static int getHeightForLineCount(RenderBlock* block, int l, bool includeBottom, RenderBox* normalFlowChildWithoutLines = 0; for (RenderBox* obj = block->firstChildBox(); obj; obj = obj->nextSiblingBox()) { if (shouldCheckLines(obj)) { - int result = getHeightForLineCount(static_cast<RenderBlock*>(obj), l, false, count); + int result = getHeightForLineCount(toRenderBlock(obj), l, false, count); if (result != -1) return result + obj->y() + (includeBottom ? (block->borderBottom() + block->paddingBottom()) : 0); } @@ -4591,7 +4719,7 @@ int RenderBlock::lineCount() else for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) if (shouldCheckLines(obj)) - count += static_cast<RenderBlock*>(obj)->lineCount(); + count += toRenderBlock(obj)->lineCount(); } return count; } @@ -4610,16 +4738,16 @@ void RenderBlock::adjustForBorderFit(int x, int& left, int& right) const if (childrenInline()) { for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) { if (box->firstChild()) - left = min(left, x + box->firstChild()->xPos()); + left = min(left, x + box->firstChild()->x()); if (box->lastChild()) - right = max(right, x + box->lastChild()->xPos() + box->lastChild()->width()); + right = max(right, x + box->lastChild()->x() + box->lastChild()->width()); } } else { for (RenderBox* obj = firstChildBox(); obj; obj = obj->nextSiblingBox()) { if (!obj->isFloatingOrPositioned()) { if (obj->isBlockFlow() && !obj->hasOverflowClip()) - static_cast<RenderBlock*>(obj)->adjustForBorderFit(x + obj->x(), left, right); + toRenderBlock(obj)->adjustForBorderFit(x + obj->x(), left, right); else if (obj->style()->visibility() == VISIBLE) { // We are a replaced element or some kind of non-block-flow object. left = min(left, x + obj->x()); @@ -4680,7 +4808,7 @@ void RenderBlock::clearTruncation() else for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) if (shouldCheckLines(obj)) - static_cast<RenderBlock*>(obj)->clearTruncation(); + toRenderBlock(obj)->clearTruncation(); } } @@ -4706,6 +4834,218 @@ void RenderBlock::setMaxBottomMargins(int pos, int neg) m_maxMargin->m_bottomNeg = neg; } +void RenderBlock::absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool topLevel) +{ + // For blocks inside inlines, we go ahead and include margins so that we run right up to the + // inline boxes above and below us (thus getting merged with them to form a single irregular + // shape). + if (topLevel && inlineContinuation()) { + rects.append(IntRect(tx, ty - collapsedMarginTop(), + width(), height() + collapsedMarginTop() + collapsedMarginBottom())); + inlineContinuation()->absoluteRects(rects, + tx - x() + inlineContinuation()->containingBlock()->x(), + ty - y() + inlineContinuation()->containingBlock()->y(), topLevel); + } else + rects.append(IntRect(tx, ty, width(), height())); +} + +void RenderBlock::absoluteQuads(Vector<FloatQuad>& quads, bool topLevel) +{ + // For blocks inside inlines, we go ahead and include margins so that we run right up to the + // inline boxes above and below us (thus getting merged with them to form a single irregular + // shape). + if (topLevel && inlineContinuation()) { + FloatRect localRect(0, -collapsedMarginTop(), + width(), height() + collapsedMarginTop() + collapsedMarginBottom()); + quads.append(localToAbsoluteQuad(localRect)); + inlineContinuation()->absoluteQuads(quads, topLevel); + } else + quads.append(RenderBox::localToAbsoluteQuad(FloatRect(0, 0, width(), height()))); +} + +IntRect RenderBlock::rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth) +{ + IntRect r(RenderBox::rectWithOutlineForRepaint(repaintContainer, outlineWidth)); + if (inlineContinuation()) + r.inflateY(collapsedMarginTop()); + return r; +} + +RenderObject* RenderBlock::hoverAncestor() const +{ + return inlineContinuation() ? inlineContinuation() : RenderBox::hoverAncestor(); +} + +void RenderBlock::updateDragState(bool dragOn) +{ + RenderBox::updateDragState(dragOn); + if (inlineContinuation()) + inlineContinuation()->updateDragState(dragOn); +} + +RenderStyle* RenderBlock::outlineStyleForRepaint() const +{ + return inlineContinuation() ? inlineContinuation()->style() : style(); +} + +void RenderBlock::childBecameNonInline(RenderObject*) +{ + makeChildrenNonInline(); + if (isAnonymousBlock() && parent() && parent()->isRenderBlock()) + toRenderBlock(parent())->removeLeftoverAnonymousBlock(this); + // |this| may be dead here +} + +void RenderBlock::updateHitTestResult(HitTestResult& result, const IntPoint& point) +{ + if (result.innerNode()) + return; + + Node* n = node(); + if (inlineContinuation()) + // We are in the margins of block elements that are part of a continuation. In + // this case we're actually still inside the enclosing inline element that was + // split. Go ahead and set our inner node accordingly. + n = inlineContinuation()->node(); + + if (n) { + result.setInnerNode(n); + if (!result.innerNonSharedNode()) + result.setInnerNonSharedNode(n); + result.setLocalPoint(point); + } +} + +IntRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine) +{ + // Do the normal calculation in most cases. + if (firstChild()) + return RenderBox::localCaretRect(inlineBox, caretOffset, extraWidthToEndOfLine); + + // This is a special case: + // The element is not an inline element, and it's empty. So we have to + // calculate a fake position to indicate where objects are to be inserted. + + // FIXME: This does not take into account either :first-line or :first-letter + // However, as soon as some content is entered, the line boxes will be + // constructed and this kludge is not called any more. So only the caret size + // of an empty :first-line'd block is wrong. I think we can live with that. + RenderStyle* currentStyle = firstLineStyle(); + int height = lineHeight(true); + + enum CaretAlignment { alignLeft, alignRight, alignCenter }; + + CaretAlignment alignment = alignLeft; + + switch (currentStyle->textAlign()) { + case TAAUTO: + case JUSTIFY: + if (currentStyle->direction() == RTL) + alignment = alignRight; + break; + case LEFT: + case WEBKIT_LEFT: + break; + case CENTER: + case WEBKIT_CENTER: + alignment = alignCenter; + break; + case RIGHT: + case WEBKIT_RIGHT: + alignment = alignRight; + break; + } + + int x = borderLeft() + paddingLeft(); + int w = width(); + + switch (alignment) { + case alignLeft: + break; + case alignCenter: + x = (x + w - (borderRight() + paddingRight())) / 2; + break; + case alignRight: + x = w - (borderRight() + paddingRight()); + break; + } + + if (extraWidthToEndOfLine) { + if (isRenderBlock()) { + *extraWidthToEndOfLine = w - (x + caretWidth); + } else { + // FIXME: This code looks wrong. + // myRight and containerRight are set up, but then clobbered. + // So *extraWidthToEndOfLine will always be 0 here. + + int myRight = x + caretWidth; + // FIXME: why call localToAbsoluteForContent() twice here, too? + FloatPoint absRightPoint = localToAbsolute(FloatPoint(myRight, 0)); + + int containerRight = containingBlock()->x() + containingBlockWidthForContent(); + FloatPoint absContainerPoint = localToAbsolute(FloatPoint(containerRight, 0)); + + *extraWidthToEndOfLine = absContainerPoint.x() - absRightPoint.x(); + } + } + + int y = paddingTop() + borderTop(); + + return IntRect(x, y, caretWidth, height); +} + +void RenderBlock::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty) +{ + // For blocks inside inlines, we go ahead and include margins so that we run right up to the + // inline boxes above and below us (thus getting merged with them to form a single irregular + // shape). + if (inlineContinuation()) { + // FIXME: This check really isn't accurate. + bool nextInlineHasLineBox = inlineContinuation()->firstLineBox(); + // FIXME: This is wrong. The principal renderer may not be the continuation preceding this block. + bool prevInlineHasLineBox = toRenderInline(inlineContinuation()->node()->renderer())->firstLineBox(); + int topMargin = prevInlineHasLineBox ? collapsedMarginTop() : 0; + int bottomMargin = nextInlineHasLineBox ? collapsedMarginBottom() : 0; + graphicsContext->addFocusRingRect(IntRect(tx, ty - topMargin, + width(), height() + topMargin + bottomMargin)); + } else + graphicsContext->addFocusRingRect(IntRect(tx, ty, width(), height())); + + if (!hasOverflowClip() && !hasControlClip()) { + for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) + graphicsContext->addFocusRingRect(IntRect(tx + curr->x(), ty + curr->y(), curr->width(), curr->height())); + + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (!curr->isText() && !curr->isListMarker() && curr->isBox()) { + RenderBox* box = toRenderBox(curr); + FloatPoint pos; + // FIXME: This doesn't work correctly with transforms. + if (box->layer()) + pos = curr->localToAbsolute(); + else + pos = FloatPoint(tx + box->x(), ty + box->y()); + box->addFocusRingRects(graphicsContext, pos.x(), pos.y()); + } + } + } + + if (inlineContinuation()) + inlineContinuation()->addFocusRingRects(graphicsContext, + tx - x() + inlineContinuation()->containingBlock()->x(), + ty - y() + inlineContinuation()->containingBlock()->y()); +} + +RenderBlock* RenderBlock::createAnonymousBlock() const +{ + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + newStyle->setDisplay(BLOCK); + + RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); + newBox->setStyle(newStyle.release()); + return newBox; +} + const char* RenderBlock::renderName() const { if (isBody()) |