webengine/osswebengine/WebCore/rendering/RenderInline.cpp
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /*
       
     2  * This file is part of the render object implementation for KHTML.
       
     3  *
       
     4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
       
     5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
       
     6  * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
       
     7  *
       
     8  * This library is free software; you can redistribute it and/or
       
     9  * modify it under the terms of the GNU Library General Public
       
    10  * License as published by the Free Software Foundation; either
       
    11  * version 2 of the License, or (at your option) any later version.
       
    12  *
       
    13  * This library is distributed in the hope that it will be useful,
       
    14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    16  * Library General Public License for more details.
       
    17  *
       
    18  * You should have received a copy of the GNU Library General Public License
       
    19  * along with this library; see the file COPYING.LIB.  If not, write to
       
    20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
       
    21  * Boston, MA 02110-1301, USA.
       
    22  *
       
    23  */
       
    24 
       
    25 #include "config.h"
       
    26 #include "RenderInline.h"
       
    27 
       
    28 #include "Document.h"
       
    29 #include "RenderArena.h"
       
    30 #include "RenderBlock.h"
       
    31 #include "VisiblePosition.h"
       
    32 
       
    33 namespace WebCore {
       
    34 
       
    35 RenderInline::RenderInline(Node* node)
       
    36     : RenderFlow(node)
       
    37 {
       
    38 }
       
    39 
       
    40 RenderInline::~RenderInline()
       
    41 {
       
    42 }
       
    43 
       
    44 void RenderInline::setStyle(RenderStyle* newStyle)
       
    45 {
       
    46     RenderFlow::setStyle(newStyle);
       
    47     setInline(true);
       
    48 
       
    49     // Ensure that all of the split inlines pick up the new style. We
       
    50     // only do this if we're an inline, since we don't want to propagate
       
    51     // a block's style to the other inlines.
       
    52     // e.g., <font>foo <h4>goo</h4> moo</font>.  The <font> inlines before
       
    53     // and after the block share the same style, but the block doesn't
       
    54     // need to pass its style on to anyone else.
       
    55     RenderFlow* currCont = continuation();
       
    56     while (currCont) {
       
    57         if (currCont->isInline()) {
       
    58             RenderFlow* nextCont = currCont->continuation();
       
    59             currCont->setContinuation(0);
       
    60             currCont->setStyle(style());
       
    61             currCont->setContinuation(nextCont);
       
    62         }
       
    63         currCont = currCont->continuation();
       
    64     }
       
    65 
       
    66     m_lineHeight = -1;
       
    67 
       
    68     // Update pseudos for :before and :after now.
       
    69     if (!isAnonymous()) {
       
    70         updateBeforeAfterContent(RenderStyle::BEFORE);
       
    71         updateBeforeAfterContent(RenderStyle::AFTER);
       
    72     }
       
    73 }
       
    74 
       
    75 bool RenderInline::isInlineContinuation() const
       
    76 {
       
    77     return m_isContinuation;
       
    78 }
       
    79 
       
    80 static inline bool isAfterContent(RenderObject* child)
       
    81 {
       
    82     if (!child)
       
    83         return false;
       
    84     if (child->style()->styleType() != RenderStyle::AFTER)
       
    85         return false;
       
    86     // Text nodes don't have their own styles, so ignore the style on a text node.
       
    87     if (child->isText() && !child->isBR())
       
    88         return false;
       
    89     return true;
       
    90 }
       
    91 
       
    92 void RenderInline::addChildToFlow(RenderObject* newChild, RenderObject* beforeChild)
       
    93 {
       
    94     // Make sure we don't append things after :after-generated content if we have it.
       
    95     if (!beforeChild && isAfterContent(lastChild()))
       
    96         beforeChild = lastChild();
       
    97 
       
    98     if (!newChild->isInline() && !newChild->isFloatingOrPositioned()) {
       
    99         // We are placing a block inside an inline. We have to perform a split of this
       
   100         // inline into continuations.  This involves creating an anonymous block box to hold
       
   101         // |newChild|.  We then make that block box a continuation of this inline.  We take all of
       
   102         // the children after |beforeChild| and put them in a clone of this object.
       
   103         RenderStyle* newStyle = new (renderArena()) RenderStyle();
       
   104         newStyle->inheritFrom(style());
       
   105         newStyle->setDisplay(BLOCK);
       
   106 
       
   107         RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */);
       
   108         newBox->setStyle(newStyle);
       
   109         RenderFlow* oldContinuation = continuation();
       
   110         setContinuation(newBox);
       
   111 
       
   112         // Someone may have put a <p> inside a <q>, causing a split.  When this happens, the :after content
       
   113         // has to move into the inline continuation.  Call updateBeforeAfterContent to ensure that our :after
       
   114         // content gets properly destroyed.
       
   115         bool isLastChild = (beforeChild == lastChild());
       
   116         updateBeforeAfterContent(RenderStyle::AFTER);
       
   117         if (isLastChild && beforeChild != lastChild())
       
   118             beforeChild = 0; // We destroyed the last child, so now we need to update our insertion
       
   119                              // point to be 0.  It's just a straight append now.
       
   120 
       
   121         splitFlow(beforeChild, newBox, newChild, oldContinuation);
       
   122         return;
       
   123     }
       
   124 
       
   125     RenderContainer::addChild(newChild, beforeChild);
       
   126 
       
   127     newChild->setNeedsLayoutAndPrefWidthsRecalc();
       
   128 }
       
   129 
       
   130 RenderInline* RenderInline::cloneInline(RenderFlow* src)
       
   131 {
       
   132     RenderInline* o = new (src->renderArena()) RenderInline(src->element());
       
   133     o->m_isContinuation = true;
       
   134     o->setStyle(src->style());
       
   135     return o;
       
   136 }
       
   137 
       
   138 void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock,
       
   139                                 RenderBlock* middleBlock,
       
   140                                 RenderObject* beforeChild, RenderFlow* oldCont)
       
   141 {
       
   142     // Create a clone of this inline.
       
   143     RenderInline* clone = cloneInline(this);
       
   144     clone->setContinuation(oldCont);
       
   145 
       
   146     // Now take all of the children from beforeChild to the end and remove
       
   147     // them from |this| and place them in the clone.
       
   148     RenderObject* o = beforeChild;
       
   149     while (o) {
       
   150         RenderObject* tmp = o;
       
   151         o = tmp->nextSibling();
       
   152         clone->addChildToFlow(removeChildNode(tmp), 0);
       
   153         tmp->setNeedsLayoutAndPrefWidthsRecalc();
       
   154     }
       
   155 
       
   156     // Hook |clone| up as the continuation of the middle block.
       
   157     middleBlock->setContinuation(clone);
       
   158 
       
   159     // We have been reparented and are now under the fromBlock.  We need
       
   160     // to walk up our inline parent chain until we hit the containing block.
       
   161     // Once we hit the containing block we're done.
       
   162     RenderFlow* curr = static_cast<RenderFlow*>(parent());
       
   163     RenderFlow* currChild = this;
       
   164     
       
   165     // FIXME: Because splitting is O(n^2) as tags nest pathologically, we cap the depth at which we're willing to clone.
       
   166     // There will eventually be a better approach to this problem that will let us nest to a much
       
   167     // greater depth (see bugzilla bug 13430) but for now we have a limit.  This *will* result in
       
   168     // incorrect rendering, but the alternative is to hang forever.
       
   169     unsigned splitDepth = 1;
       
   170     const unsigned cMaxSplitDepth = 200; 
       
   171     while (curr && curr != fromBlock) {
       
   172         if (splitDepth < cMaxSplitDepth) {
       
   173             // Create a new clone.
       
   174             RenderInline* cloneChild = clone;
       
   175             clone = cloneInline(curr);
       
   176 
       
   177             // Insert our child clone as the first child.
       
   178             clone->addChildToFlow(cloneChild, 0);
       
   179 
       
   180             // Hook the clone up as a continuation of |curr|.
       
   181             RenderFlow* oldCont = curr->continuation();
       
   182             curr->setContinuation(clone);
       
   183             clone->setContinuation(oldCont);
       
   184 
       
   185             // Someone may have indirectly caused a <q> to split.  When this happens, the :after content
       
   186             // has to move into the inline continuation.  Call updateBeforeAfterContent to ensure that the inline's :after
       
   187             // content gets properly destroyed.
       
   188             curr->updateBeforeAfterContent(RenderStyle::AFTER);
       
   189 
       
   190             // Now we need to take all of the children starting from the first child
       
   191             // *after* currChild and append them all to the clone.
       
   192             o = currChild->nextSibling();
       
   193             while (o) {
       
   194                 RenderObject* tmp = o;
       
   195                 o = tmp->nextSibling();
       
   196                 clone->addChildToFlow(curr->removeChildNode(tmp), 0);
       
   197                 tmp->setNeedsLayoutAndPrefWidthsRecalc();
       
   198             }
       
   199         }
       
   200         
       
   201         // Keep walking up the chain.
       
   202         currChild = curr;
       
   203         curr = static_cast<RenderFlow*>(curr->parent());
       
   204         splitDepth++;
       
   205     }
       
   206 
       
   207     // Now we are at the block level. We need to put the clone into the toBlock.
       
   208     toBlock->appendChildNode(clone);
       
   209 
       
   210     // Now take all the children after currChild and remove them from the fromBlock
       
   211     // and put them in the toBlock.
       
   212     o = currChild->nextSibling();
       
   213     while (o) {
       
   214         RenderObject* tmp = o;
       
   215         o = tmp->nextSibling();
       
   216         toBlock->appendChildNode(fromBlock->removeChildNode(tmp));
       
   217     }
       
   218 }
       
   219 
       
   220 void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox,
       
   221                              RenderObject* newChild, RenderFlow* oldCont)
       
   222 {
       
   223     RenderBlock* pre = 0;
       
   224     RenderBlock* block = containingBlock();
       
   225     
       
   226     // Delete our line boxes before we do the inline split into continuations.
       
   227     block->deleteLineBoxTree();
       
   228     
       
   229     bool madeNewBeforeBlock = false;
       
   230     if (block->isAnonymousBlock() && (!block->parent() || !block->parent()->createsAnonymousWrapper())) {
       
   231         // We can reuse this block and make it the preBlock of the next continuation.
       
   232         pre = block;
       
   233         block = block->containingBlock();
       
   234     } else {
       
   235         // No anonymous block available for use.  Make one.
       
   236         pre = block->createAnonymousBlock();
       
   237         madeNewBeforeBlock = true;
       
   238     }
       
   239 
       
   240     RenderBlock* post = block->createAnonymousBlock();
       
   241 
       
   242     RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
       
   243     if (madeNewBeforeBlock)
       
   244         block->insertChildNode(pre, boxFirst);
       
   245     block->insertChildNode(newBlockBox, boxFirst);
       
   246     block->insertChildNode(post, boxFirst);
       
   247     block->setChildrenInline(false);
       
   248     
       
   249     if (madeNewBeforeBlock) {
       
   250         RenderObject* o = boxFirst;
       
   251         while (o) {
       
   252             RenderObject* no = o;
       
   253             o = no->nextSibling();
       
   254             pre->appendChildNode(block->removeChildNode(no));
       
   255             no->setNeedsLayoutAndPrefWidthsRecalc();
       
   256         }
       
   257     }
       
   258 
       
   259     splitInlines(pre, post, newBlockBox, beforeChild, oldCont);
       
   260 
       
   261     // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
       
   262     // time in makeChildrenNonInline by just setting this explicitly up front.
       
   263     newBlockBox->setChildrenInline(false);
       
   264 
       
   265     // We don't just call addChild, since it would pass things off to the
       
   266     // continuation, so we call addChildToFlow explicitly instead.  We delayed
       
   267     // adding the newChild until now so that the |newBlockBox| would be fully
       
   268     // connected, thus allowing newChild access to a renderArena should it need
       
   269     // to wrap itself in additional boxes (e.g., table construction).
       
   270     newBlockBox->addChildToFlow(newChild, 0);
       
   271 
       
   272     // Always just do a full layout in order to ensure that line boxes (especially wrappers for images)
       
   273     // get deleted properly.  Because objects moves from the pre block into the post block, we want to
       
   274     // make new line boxes instead of leaving the old line boxes around.
       
   275     pre->setNeedsLayoutAndPrefWidthsRecalc();
       
   276     block->setNeedsLayoutAndPrefWidthsRecalc();
       
   277     post->setNeedsLayoutAndPrefWidthsRecalc();
       
   278 }
       
   279 
       
   280 void RenderInline::paint(PaintInfo& paintInfo, int tx, int ty)
       
   281 {
       
   282     paintLines(paintInfo, tx, ty);
       
   283 }
       
   284 
       
   285 void RenderInline::absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool topLevel)
       
   286 {
       
   287     for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox())
       
   288         rects.append(IntRect(tx + curr->xPos(), ty + curr->yPos(), curr->width(), curr->height()));
       
   289 
       
   290     for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
       
   291         if (!curr->isText())
       
   292             curr->absoluteRects(rects, tx + curr->xPos(), ty + curr->yPos(), false);
       
   293     }
       
   294 
       
   295     if (continuation() && topLevel)
       
   296         continuation()->absoluteRects(rects, 
       
   297                                       tx - containingBlock()->xPos() + continuation()->xPos(),
       
   298                                       ty - containingBlock()->yPos() + continuation()->yPos(),
       
   299                                       topLevel);
       
   300 }
       
   301 
       
   302 bool RenderInline::requiresLayer()
       
   303 {
       
   304     return isRelPositioned() || style()->opacity() < 1.0f;
       
   305 }
       
   306 
       
   307 int RenderInline::width() const
       
   308 {
       
   309     // Return the width of the minimal left side and the maximal right side.
       
   310     int leftSide = 0;
       
   311     int rightSide = 0;
       
   312     for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
       
   313         if (curr == firstLineBox() || curr->xPos() < leftSide)
       
   314             leftSide = curr->xPos();
       
   315         if (curr == firstLineBox() || curr->xPos() + curr->width() > rightSide)
       
   316             rightSide = curr->xPos() + curr->width();
       
   317     }
       
   318 
       
   319     return rightSide - leftSide;
       
   320 }
       
   321 
       
   322 int RenderInline::height() const
       
   323 {
       
   324     // 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
       
   325     // 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
       
   326     // builds and help us someday figure out why.  We also put in a redundant check of lastLineBox() to avoid the crash for now.
       
   327     ASSERT(!firstLineBox() == !lastLineBox());  // Either both are null or both exist.
       
   328     if (firstLineBox() && lastLineBox())
       
   329         return lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos();
       
   330     return 0;
       
   331 }
       
   332 
       
   333 int RenderInline::offsetLeft() const
       
   334 {
       
   335     int x = RenderFlow::offsetLeft();
       
   336     if (firstLineBox())
       
   337         x += firstLineBox()->xPos();
       
   338     return x;
       
   339 }
       
   340 
       
   341 int RenderInline::offsetTop() const
       
   342 {
       
   343     int y = RenderFlow::offsetTop();
       
   344     if (firstLineBox())
       
   345         y += firstLineBox()->yPos();
       
   346     return y;
       
   347 }
       
   348 
       
   349 const char* RenderInline::renderName() const
       
   350 {
       
   351     if (isRelPositioned())
       
   352         return "RenderInline (relative positioned)";
       
   353     if (isAnonymous())
       
   354         return "RenderInline (generated)";
       
   355     return "RenderInline";
       
   356 }
       
   357 
       
   358 bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result,
       
   359                                 int x, int y, int tx, int ty, HitTestAction hitTestAction)
       
   360 {
       
   361     return hitTestLines(request, result, x, y, tx, ty, hitTestAction);
       
   362 }
       
   363 
       
   364 VisiblePosition RenderInline::positionForCoordinates(int x, int y)
       
   365 {
       
   366     // Translate the coords from the pre-anonymous block to the post-anonymous block.
       
   367     RenderBlock* cb = containingBlock();
       
   368     int parentBlockX = cb->xPos() + x;
       
   369     int parentBlockY = cb->yPos() + y;
       
   370     for (RenderObject* c = continuation(); c; c = c->continuation()) {
       
   371         RenderObject* contBlock = c;
       
   372         if (c->isInline())
       
   373             contBlock = c->containingBlock();
       
   374         if (c->isInline() || c->firstChild())
       
   375             return c->positionForCoordinates(parentBlockX - contBlock->xPos(), parentBlockY - contBlock->yPos());
       
   376     }
       
   377 
       
   378     return RenderFlow::positionForCoordinates(x, y);
       
   379 }
       
   380 
       
   381 } // namespace WebCore