/*
* This file is part of the render object implementation for KHTML.
*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "config.h"
#include "RenderInline.h"
#include "FloatQuad.h"
#include "RenderArena.h"
#include "RenderBlock.h"
#include "RenderView.h"
#include "VisiblePosition.h"
namespace WebCore {
RenderInline::RenderInline(Node* node)
: RenderFlow(node)
{
}
RenderInline::~RenderInline()
{
}
void RenderInline::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle)
{
RenderFlow::styleDidChange(diff, oldStyle);
setInline(true);
setHasReflection(false);
// Ensure that all of the split inlines pick up the new style. We
// only do this if we're an inline, since we don't want to propagate
// a block's style to the other inlines.
// e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before
// and after the block share the same style, but the block doesn't
// need to pass its style on to anyone else.
RenderFlow* currCont = continuation();
while (currCont) {
if (currCont->isInline()) {
RenderFlow* nextCont = currCont->continuation();
currCont->setContinuation(0);
currCont->setStyle(style());
currCont->setContinuation(nextCont);
}
currCont = currCont->continuation();
}
m_lineHeight = -1;
// Update pseudos for :before and :after now.
if (!isAnonymous() && document()->usesBeforeAfterRules()) {
updateBeforeAfterContent(RenderStyle::BEFORE);
updateBeforeAfterContent(RenderStyle::AFTER);
}
}
bool RenderInline::isInlineContinuation() const
{
return m_isContinuation;
}
static inline bool isAfterContent(RenderObject* child)
{
if (!child)
return false;
if (child->style()->styleType() != RenderStyle::AFTER)
return false;
// Text nodes don't have their own styles, so ignore the style on a text node.
if (child->isText() && !child->isBR())
return false;
return true;
}
void RenderInline::addChildToFlow(RenderObject* newChild, RenderObject* beforeChild)
{
// Make sure we don't append things after :after-generated content if we have it.
if (!beforeChild && isAfterContent(lastChild()))
beforeChild = lastChild();
if (!newChild->isInline() && !newChild->isFloatingOrPositioned()) {
// We are placing a block inside an inline. We have to perform a split of this
// inline into continuations. This involves creating an anonymous block box to hold
// |newChild|. We then make that block box a continuation of this inline. We take all of
// the children after |beforeChild| and put them in a clone of this object.
RefPtr<RenderStyle> newStyle = RenderStyle::create();
newStyle->inheritFrom(style());
newStyle->setDisplay(BLOCK);
RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */);
newBox->setStyle(newStyle.release());
RenderFlow* oldContinuation = continuation();
setContinuation(newBox);
// Someone may have put a <p> inside a <q>, causing a split. When this happens, the :after content
// has to move into the inline continuation. Call updateBeforeAfterContent to ensure that our :after
// content gets properly destroyed.
bool isLastChild = (beforeChild == lastChild());
if (document()->usesBeforeAfterRules())
updateBeforeAfterContent(RenderStyle::AFTER);
if (isLastChild && beforeChild != lastChild())
beforeChild = 0; // We destroyed the last child, so now we need to update our insertion
// point to be 0. It's just a straight append now.
splitFlow(beforeChild, newBox, newChild, oldContinuation);
return;
}
RenderContainer::addChild(newChild, beforeChild);
newChild->setNeedsLayoutAndPrefWidthsRecalc();
}
RenderInline* RenderInline::cloneInline(RenderFlow* src)
{
RenderInline* o = new (src->renderArena()) RenderInline(src->element());
o->m_isContinuation = true;
o->setStyle(src->style());
return o;
}
void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock,
RenderBlock* middleBlock,
RenderObject* beforeChild, RenderFlow* oldCont)
{
// Create a clone of this inline.
RenderInline* clone = cloneInline(this);
clone->setContinuation(oldCont);
// Now take all of the children from beforeChild to the end and remove
// them from |this| and place them in the clone.
RenderObject* o = beforeChild;
while (o) {
RenderObject* tmp = o;
o = tmp->nextSibling();
clone->addChildToFlow(removeChildNode(tmp), 0);
tmp->setNeedsLayoutAndPrefWidthsRecalc();
}
// Hook |clone| up as the continuation of the middle block.
middleBlock->setContinuation(clone);
// We have been reparented and are now under the fromBlock. We need
// to walk up our inline parent chain until we hit the containing block.
// Once we hit the containing block we're done.
RenderFlow* curr = static_cast<RenderFlow*>(parent());
RenderFlow* currChild = this;
// FIXME: Because splitting is O(n^2) as tags nest pathologically, we cap the depth at which we're willing to clone.
// There will eventually be a better approach to this problem that will let us nest to a much
// greater depth (see bugzilla bug 13430) but for now we have a limit. This *will* result in
// incorrect rendering, but the alternative is to hang forever.
unsigned splitDepth = 1;
const unsigned cMaxSplitDepth = 200;
while (curr && curr != fromBlock) {
if (splitDepth < cMaxSplitDepth) {
// Create a new clone.
RenderInline* cloneChild = clone;
clone = cloneInline(curr);
// Insert our child clone as the first child.
clone->addChildToFlow(cloneChild, 0);
// Hook the clone up as a continuation of |curr|.
RenderFlow* oldCont = curr->continuation();
curr->setContinuation(clone);
clone->setContinuation(oldCont);
// Someone may have indirectly caused a <q> to split. When this happens, the :after content
// has to move into the inline continuation. Call updateBeforeAfterContent to ensure that the inline's :after
// content gets properly destroyed.
if (document()->usesBeforeAfterRules())
curr->updateBeforeAfterContent(RenderStyle::AFTER);
// Now we need to take all of the children starting from the first child
// *after* currChild and append them all to the clone.
o = currChild->nextSibling();
while (o) {
RenderObject* tmp = o;
o = tmp->nextSibling();
clone->addChildToFlow(curr->removeChildNode(tmp), 0);
tmp->setNeedsLayoutAndPrefWidthsRecalc();
}
}
// Keep walking up the chain.
currChild = curr;
curr = static_cast<RenderFlow*>(curr->parent());
splitDepth++;
}
// Now we are at the block level. We need to put the clone into the toBlock.
toBlock->appendChildNode(clone);
// Now take all the children after currChild and remove them from the fromBlock
// and put them in the toBlock.
o = currChild->nextSibling();
while (o) {
RenderObject* tmp = o;
o = tmp->nextSibling();
toBlock->appendChildNode(fromBlock->removeChildNode(tmp));
}
}
void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox,
RenderObject* newChild, RenderFlow* oldCont)
{
RenderBlock* pre = 0;
RenderBlock* block = containingBlock();
// Delete our line boxes before we do the inline split into continuations.
block->deleteLineBoxTree();
bool madeNewBeforeBlock = false;
if (block->isAnonymousBlock() && (!block->parent() || !block->parent()->createsAnonymousWrapper())) {
// We can reuse this block and make it the preBlock of the next continuation.
pre = block;
block = block->containingBlock();
} else {
// No anonymous block available for use. Make one.
pre = block->createAnonymousBlock();
madeNewBeforeBlock = true;
}
RenderBlock* post = block->createAnonymousBlock();
RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
if (madeNewBeforeBlock)
block->insertChildNode(pre, boxFirst);
block->insertChildNode(newBlockBox, boxFirst);
block->insertChildNode(post, boxFirst);
block->setChildrenInline(false);
if (madeNewBeforeBlock) {
RenderObject* o = boxFirst;
while (o) {
RenderObject* no = o;
o = no->nextSibling();
pre->appendChildNode(block->removeChildNode(no));
no->setNeedsLayoutAndPrefWidthsRecalc();
}
}
splitInlines(pre, post, newBlockBox, beforeChild, oldCont);
// We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
// time in makeChildrenNonInline by just setting this explicitly up front.
newBlockBox->setChildrenInline(false);
// We don't just call addChild, since it would pass things off to the
// continuation, so we call addChildToFlow explicitly instead. We delayed
// adding the newChild until now so that the |newBlockBox| would be fully
// connected, thus allowing newChild access to a renderArena should it need
// to wrap itself in additional boxes (e.g., table construction).
newBlockBox->addChildToFlow(newChild, 0);
// Always just do a full layout in order to ensure that line boxes (especially wrappers for images)
// get deleted properly. Because objects moves from the pre block into the post block, we want to
// make new line boxes instead of leaving the old line boxes around.
pre->setNeedsLayoutAndPrefWidthsRecalc();
block->setNeedsLayoutAndPrefWidthsRecalc();
post->setNeedsLayoutAndPrefWidthsRecalc();
}
void RenderInline::paint(PaintInfo& paintInfo, int tx, int ty)
{
paintLines(paintInfo, tx, ty);
}
void RenderInline::absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool topLevel)
{
for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox())
rects.append(IntRect(tx + curr->xPos(), ty + curr->yPos(), curr->width(), curr->height()));
for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
if (curr->isBox()) {
RenderBox* box = toRenderBox(curr);
curr->absoluteRects(rects, tx + box->x(), ty + box->y(), false);
}
}
if (continuation() && topLevel)
continuation()->absoluteRects(rects,
tx - containingBlock()->x() + continuation()->x(),
ty - containingBlock()->y() + continuation()->y(),
topLevel);
}
void RenderInline::absoluteQuads(Vector<FloatQuad>& quads, bool topLevel)
{
for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
FloatRect localRect(curr->xPos(), curr->yPos(), curr->width(), curr->height());
quads.append(localToAbsoluteQuad(localRect));
}
for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
if (!curr->isText())
curr->absoluteQuads(quads, false);
}
if (continuation() && topLevel)
continuation()->absoluteQuads(quads, topLevel);
}
int RenderInline::offsetLeft() const
{
int x = RenderFlow::offsetLeft();
if (firstLineBox())
x += firstLineBox()->xPos();
return x;
}
int RenderInline::offsetTop() const
{
int y = RenderFlow::offsetTop();
if (firstLineBox())
y += firstLineBox()->yPos();
return y;
}
const char* RenderInline::renderName() const
{
if (isRelPositioned())
return "RenderInline (relative positioned)";
if (isAnonymous())
return "RenderInline (generated)";
if (isRunIn())
return "RenderInline (run-in)";
return "RenderInline";
}
bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result,
int x, int y, int tx, int ty, HitTestAction hitTestAction)
{
return hitTestLines(request, result, x, y, tx, ty, hitTestAction);
}
VisiblePosition RenderInline::positionForCoordinates(int x, int y)
{
// Translate the coords from the pre-anonymous block to the post-anonymous block.
RenderBlock* cb = containingBlock();
int parentBlockX = cb->x() + x;
int parentBlockY = cb->y() + y;
for (RenderFlow* c = continuation(); c; c = c->continuation()) {
RenderFlow* contBlock = c;
if (c->isInline())
contBlock = c->containingBlock();
if (c->isInline() || c->firstChild())
return c->positionForCoordinates(parentBlockX - contBlock->x(), parentBlockY - contBlock->y());
}
return RenderFlow::positionForCoordinates(x, y);
}
IntRect RenderInline::linesBoundingBox() const
{
IntRect result;
// See <rdar://problem/5289721>, for an unknown reason the linked list here is sometimes inconsistent, first is non-zero and last is zero. We have been
// unable to reproduce this at all (and consequently unable to figure ot why this is happening). The assert will hopefully catch the problem in debug
// builds and help us someday figure out why. We also put in a redundant check of lastLineBox() to avoid the crash for now.
ASSERT(!firstLineBox() == !lastLineBox()); // Either both are null or both exist.
if (firstLineBox() && lastLineBox()) {
// Return the width of the minimal left side and the maximal right side.
int leftSide = 0;
int rightSide = 0;
for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
if (curr == firstLineBox() || curr->xPos() < leftSide)
leftSide = curr->xPos();
if (curr == firstLineBox() || curr->xPos() + curr->width() > rightSide)
rightSide = curr->xPos() + curr->width();
}
result.setWidth(rightSide - leftSide);
result.setX(leftSide);
result.setHeight(lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos());
result.setY(firstLineBox()->yPos());
}
return result;
}
IntRect RenderInline::clippedOverflowRectForRepaint(RenderBox* repaintContainer)
{
// Only run-ins are allowed in here during layout.
ASSERT(!view() || !view()->layoutStateEnabled() || isRunIn());
if (!firstLineBox() && !continuation())
return IntRect();
// Find our leftmost position.
IntRect boundingBox(linesBoundingBox());
int left = boundingBox.x();
int top = boundingBox.y();
// Now invalidate a rectangle.
int ow = style() ? style()->outlineSize() : 0;
// We need to add in the relative position offsets of any inlines (including us) up to our
// containing block.
RenderBlock* cb = containingBlock();
for (RenderObject* inlineFlow = this; inlineFlow && inlineFlow->isRenderInline() && inlineFlow != cb;
inlineFlow = inlineFlow->parent()) {
if (inlineFlow->style()->position() == RelativePosition && inlineFlow->hasLayer())
toRenderBox(inlineFlow)->layer()->relativePositionOffset(left, top);
}
IntRect r(-ow + left, -ow + top, boundingBox.width() + ow * 2, boundingBox.height() + ow * 2);
if (cb->hasColumns())
cb->adjustRectForColumns(r);
if (cb->hasOverflowClip()) {
// cb->height() is inaccurate if we're in the middle of a layout of |cb|, so use the
// layer's size instead. Even if the layer's size is wrong, the layer itself will repaint
// anyway if its size does change.
int x = r.x();
int y = r.y();
IntRect boxRect(0, 0, cb->layer()->width(), cb->layer()->height());
cb->layer()->subtractScrolledContentOffset(x, y); // For overflow:auto/scroll/hidden.
IntRect repaintRect(x, y, r.width(), r.height());
r = intersection(repaintRect, boxRect);
}
ASSERT(repaintContainer != this);
cb->computeRectForRepaint(r, repaintContainer);
if (ow) {
for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
if (!curr->isText()) {
IntRect childRect = curr->rectWithOutlineForRepaint(repaintContainer, ow);
r.unite(childRect);
}
}
if (continuation() && !continuation()->isInline()) {
IntRect contRect = continuation()->rectWithOutlineForRepaint(repaintContainer, ow);
r.unite(contRect);
}
}
return r;
}
} // namespace WebCore