webengine/osswebengine/WebCore/rendering/RenderInline.cpp
changeset 0 dd21522fd290
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/osswebengine/WebCore/rendering/RenderInline.cpp	Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,381 @@
+/*
+ * 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 "Document.h"
+#include "RenderArena.h"
+#include "RenderBlock.h"
+#include "VisiblePosition.h"
+
+namespace WebCore {
+
+RenderInline::RenderInline(Node* node)
+    : RenderFlow(node)
+{
+}
+
+RenderInline::~RenderInline()
+{
+}
+
+void RenderInline::setStyle(RenderStyle* newStyle)
+{
+    RenderFlow::setStyle(newStyle);
+    setInline(true);
+
+    // 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()) {
+        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.
+        RenderStyle* newStyle = new (renderArena()) RenderStyle();
+        newStyle->inheritFrom(style());
+        newStyle->setDisplay(BLOCK);
+
+        RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */);
+        newBox->setStyle(newStyle);
+        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());
+        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.
+            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->isText())
+            curr->absoluteRects(rects, tx + curr->xPos(), ty + curr->yPos(), false);
+    }
+
+    if (continuation() && topLevel)
+        continuation()->absoluteRects(rects, 
+                                      tx - containingBlock()->xPos() + continuation()->xPos(),
+                                      ty - containingBlock()->yPos() + continuation()->yPos(),
+                                      topLevel);
+}
+
+bool RenderInline::requiresLayer()
+{
+    return isRelPositioned() || style()->opacity() < 1.0f;
+}
+
+int RenderInline::width() const
+{
+    // 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();
+    }
+
+    return rightSide - leftSide;
+}
+
+int RenderInline::height() const
+{
+    // 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 lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos();
+    return 0;
+}
+
+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)";
+    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->xPos() + x;
+    int parentBlockY = cb->yPos() + y;
+    for (RenderObject* c = continuation(); c; c = c->continuation()) {
+        RenderObject* contBlock = c;
+        if (c->isInline())
+            contBlock = c->containingBlock();
+        if (c->isInline() || c->firstChild())
+            return c->positionForCoordinates(parentBlockX - contBlock->xPos(), parentBlockY - contBlock->yPos());
+    }
+
+    return RenderFlow::positionForCoordinates(x, y);
+}
+
+} // namespace WebCore