| /* |
| * 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 "GraphicsContext.h" |
| #include "HitTestResult.h" |
| #include "Page.h" |
| #include "RenderArena.h" |
| #include "RenderBlock.h" |
| #include "RenderView.h" |
| #include "VisiblePosition.h" |
| |
| #if ENABLE(DASHBOARD_SUPPORT) |
| #include "Frame.h" |
| #endif |
| |
| using namespace std; |
| |
| namespace WebCore { |
| |
| RenderInline::RenderInline(Node* node) |
| : RenderBoxModelObject(node) |
| , m_continuation(0) |
| , m_lineHeight(-1) |
| , m_verticalPosition(PositionUndefined) |
| { |
| setChildrenInline(true); |
| } |
| |
| RenderInline::~RenderInline() |
| { |
| } |
| |
| void RenderInline::destroy() |
| { |
| // 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(); |
| |
| // Destroy our continuation before anything other than anonymous children. |
| // The reason we don't destroy it before anonymous children is that they may |
| // have continuations of their own that are anonymous children of our continuation. |
| if (m_continuation) { |
| m_continuation->destroy(); |
| m_continuation = 0; |
| } |
| |
| if (!documentBeingDestroyed()) { |
| if (firstLineBox()) { |
| // We can't wait for RenderBoxModelObject::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 line boxes are contained inside a root, that means we're an inline. |
| // In that case, we need to remove all the line boxes so that the parent |
| // lines aren't pointing to deleted children. If the first line box does |
| // not have a parent that means they are either already disconnected or |
| // root lines that can just be destroyed without disconnecting. |
| if (firstLineBox()->parent()) { |
| for (InlineRunBox* box = firstLineBox(); box; box = box->nextLineBox()) |
| box->remove(); |
| } |
| } else if (isInline() && parent()) |
| parent()->dirtyLinesFromChangedChild(this); |
| } |
| |
| m_lineBoxes.deleteLineBoxes(renderArena()); |
| |
| RenderBoxModelObject::destroy(); |
| } |
| |
| RenderInline* RenderInline::inlineContinuation() const |
| { |
| if (!m_continuation || m_continuation->isInline()) |
| return toRenderInline(m_continuation); |
| return toRenderBlock(m_continuation)->inlineContinuation(); |
| } |
| |
| void RenderInline::updateBoxModelInfoFromStyle() |
| { |
| RenderBoxModelObject::updateBoxModelInfoFromStyle(); |
| |
| setInline(true); // Needed for run-ins, since run-in is considered a block display type. |
| |
| // FIXME: Support transforms and reflections on inline flows someday. |
| setHasTransform(false); |
| setHasReflection(false); |
| } |
| |
| void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) |
| { |
| RenderBoxModelObject::styleDidChange(diff, oldStyle); |
| |
| // 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. |
| for (RenderInline* currCont = inlineContinuation(); currCont; currCont = currCont->inlineContinuation()) { |
| RenderBoxModelObject* nextCont = currCont->continuation(); |
| currCont->setContinuation(0); |
| currCont->setStyle(style()); |
| currCont->setContinuation(nextCont); |
| } |
| |
| m_lineHeight = -1; |
| |
| // Update pseudos for :before and :after now. |
| if (!isAnonymous() && document()->usesBeforeAfterRules()) { |
| children()->updateBeforeAfterContent(this, BEFORE); |
| children()->updateBeforeAfterContent(this, AFTER); |
| } |
| } |
| |
| static inline bool isAfterContent(RenderObject* child) |
| { |
| if (!child) |
| return false; |
| if (child->style()->styleType() != 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::addChild(RenderObject* newChild, RenderObject* beforeChild) |
| { |
| if (continuation()) |
| return addChildToContinuation(newChild, beforeChild); |
| return addChildIgnoringContinuation(newChild, beforeChild); |
| } |
| |
| static RenderBoxModelObject* nextContinuation(RenderObject* renderer) |
| { |
| if (renderer->isInline() && !renderer->isReplaced()) |
| return toRenderInline(renderer)->continuation(); |
| return toRenderBlock(renderer)->inlineContinuation(); |
| } |
| |
| RenderBoxModelObject* RenderInline::continuationBefore(RenderObject* beforeChild) |
| { |
| if (beforeChild && beforeChild->parent() == this) |
| return this; |
| |
| RenderBoxModelObject* curr = nextContinuation(this); |
| RenderBoxModelObject* nextToLast = this; |
| RenderBoxModelObject* last = this; |
| while (curr) { |
| if (beforeChild && beforeChild->parent() == curr) { |
| if (curr->firstChild() == beforeChild) |
| return last; |
| return curr; |
| } |
| |
| nextToLast = last; |
| last = curr; |
| curr = nextContinuation(curr); |
| } |
| |
| if (!beforeChild && !last->firstChild()) |
| return nextToLast; |
| return last; |
| } |
| |
| void RenderInline::addChildIgnoringContinuation(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()); |
| RenderBoxModelObject* 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()) |
| children()->updateBeforeAfterContent(this, 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; |
| } |
| |
| RenderBoxModelObject::addChild(newChild, beforeChild); |
| |
| newChild->setNeedsLayoutAndPrefWidthsRecalc(); |
| } |
| |
| RenderInline* RenderInline::cloneInline(RenderInline* src) |
| { |
| RenderInline* o = new (src->renderArena()) RenderInline(src->node()); |
| o->setStyle(src->style()); |
| return o; |
| } |
| |
| void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock, |
| RenderBlock* middleBlock, |
| RenderObject* beforeChild, RenderBoxModelObject* 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->addChildIgnoringContinuation(children()->removeChildNode(this, tmp), 0); |
| tmp->setNeedsLayoutAndPrefWidthsRecalc(); |
| } |
| |
| // Hook |clone| up as the continuation of the middle block. |
| middleBlock->setInlineContinuation(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. |
| RenderBoxModelObject* curr = static_cast<RenderBoxModelObject*>(parent()); |
| RenderBoxModelObject* 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) { |
| ASSERT(curr->isRenderInline()); |
| if (splitDepth < cMaxSplitDepth) { |
| // Create a new clone. |
| RenderInline* cloneChild = clone; |
| clone = cloneInline(toRenderInline(curr)); |
| |
| // Insert our child clone as the first child. |
| clone->addChildIgnoringContinuation(cloneChild, 0); |
| |
| // Hook the clone up as a continuation of |curr|. |
| RenderInline* inlineCurr = toRenderInline(curr); |
| oldCont = inlineCurr->continuation(); |
| inlineCurr->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()) |
| inlineCurr->children()->updateBeforeAfterContent(this, 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->addChildIgnoringContinuation(inlineCurr->children()->removeChildNode(curr, tmp), 0); |
| tmp->setNeedsLayoutAndPrefWidthsRecalc(); |
| } |
| } |
| |
| // Keep walking up the chain. |
| currChild = curr; |
| curr = static_cast<RenderBoxModelObject*>(curr->parent()); |
| splitDepth++; |
| } |
| |
| // Now we are at the block level. We need to put the clone into the toBlock. |
| toBlock->children()->appendChildNode(toBlock, 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->children()->appendChildNode(toBlock, fromBlock->children()->removeChildNode(fromBlock, tmp)); |
| } |
| } |
| |
| void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, |
| RenderObject* newChild, RenderBoxModelObject* 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; |
| pre->removePositionedObjects(0); |
| 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->children()->insertChildNode(block, pre, boxFirst); |
| block->children()->insertChildNode(block, newBlockBox, boxFirst); |
| block->children()->insertChildNode(block, post, boxFirst); |
| block->setChildrenInline(false); |
| |
| if (madeNewBeforeBlock) { |
| RenderObject* o = boxFirst; |
| while (o) { |
| RenderObject* no = o; |
| o = no->nextSibling(); |
| pre->children()->appendChildNode(pre, block->children()->removeChildNode(block, 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 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->addChild(newChild); |
| |
| // 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::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild) |
| { |
| RenderBoxModelObject* flow = continuationBefore(beforeChild); |
| ASSERT(!beforeChild || beforeChild->parent()->isRenderBlock() || beforeChild->parent()->isRenderInline()); |
| RenderBoxModelObject* beforeChildParent = 0; |
| if (beforeChild) |
| beforeChildParent = static_cast<RenderBoxModelObject*>(beforeChild->parent()); |
| else { |
| RenderBoxModelObject* cont = nextContinuation(flow); |
| if (cont) |
| beforeChildParent = cont; |
| else |
| beforeChildParent = flow; |
| } |
| |
| if (newChild->isFloatingOrPositioned()) |
| return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); |
| |
| // A continuation always consists of two potential candidates: an inline or an anonymous |
| // block box holding block children. |
| bool childInline = newChild->isInline(); |
| bool bcpInline = beforeChildParent->isInline(); |
| bool flowInline = flow->isInline(); |
| |
| if (flow == beforeChildParent) |
| return flow->addChildIgnoringContinuation(newChild, beforeChild); |
| else { |
| // The goal here is to match up if we can, so that we can coalesce and create the |
| // minimal # of continuations needed for the inline. |
| if (childInline == bcpInline) |
| return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); |
| else if (flowInline == childInline) |
| return flow->addChildIgnoringContinuation(newChild, 0); // Just treat like an append. |
| else |
| return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); |
| } |
| } |
| |
| void RenderInline::paint(PaintInfo& paintInfo, int tx, int ty) |
| { |
| m_lineBoxes.paint(this, paintInfo, tx, ty); |
| } |
| |
| void RenderInline::absoluteRects(Vector<IntRect>& rects, int tx, int ty) |
| { |
| if (InlineRunBox* curr = firstLineBox()) { |
| for (; curr; curr = curr->nextLineBox()) |
| rects.append(IntRect(tx + curr->x(), ty + curr->y(), curr->width(), curr->height())); |
| } else |
| rects.append(IntRect(tx, ty, 0, 0)); |
| |
| if (continuation()) { |
| if (continuation()->isBox()) { |
| RenderBox* box = toRenderBox(continuation()); |
| continuation()->absoluteRects(rects, |
| tx - containingBlock()->x() + box->x(), |
| ty - containingBlock()->y() + box->y()); |
| } else |
| continuation()->absoluteRects(rects, tx - containingBlock()->x(), ty - containingBlock()->y()); |
| } |
| } |
| |
| void RenderInline::absoluteQuads(Vector<FloatQuad>& quads) |
| { |
| if (InlineRunBox* curr = firstLineBox()) { |
| for (; curr; curr = curr->nextLineBox()) { |
| FloatRect localRect(curr->x(), curr->y(), curr->width(), curr->height()); |
| quads.append(localToAbsoluteQuad(localRect)); |
| } |
| } else |
| quads.append(localToAbsoluteQuad(FloatRect())); |
| |
| if (continuation()) |
| continuation()->absoluteQuads(quads); |
| } |
| |
| int RenderInline::offsetLeft() const |
| { |
| int x = RenderBoxModelObject::offsetLeft(); |
| if (firstLineBox()) |
| x += firstLineBox()->x(); |
| return x; |
| } |
| |
| int RenderInline::offsetTop() const |
| { |
| int y = RenderBoxModelObject::offsetTop(); |
| if (firstLineBox()) |
| y += firstLineBox()->y(); |
| return y; |
| } |
| |
| int RenderInline::marginLeft() const |
| { |
| Length margin = style()->marginLeft(); |
| if (margin.isAuto()) |
| return 0; |
| if (margin.isFixed()) |
| return margin.value(); |
| if (margin.isPercent()) |
| return margin.calcMinValue(max(0, containingBlock()->availableWidth())); |
| return 0; |
| } |
| |
| int RenderInline::marginRight() const |
| { |
| Length margin = style()->marginRight(); |
| if (margin.isAuto()) |
| return 0; |
| if (margin.isFixed()) |
| return margin.value(); |
| if (margin.isPercent()) |
| return margin.calcMinValue(max(0, containingBlock()->availableWidth())); |
| return 0; |
| } |
| |
| 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 m_lineBoxes.hitTest(this, request, result, x, y, tx, ty, hitTestAction); |
| } |
| |
| VisiblePosition RenderInline::positionForPoint(const IntPoint& point) |
| { |
| // FIXME: Does not deal with relative positioned inlines (should it?) |
| RenderBlock* cb = containingBlock(); |
| if (firstLineBox()) { |
| // This inline actually has a line box. We must have clicked in the border/padding of one of these boxes. We |
| // should try to find a result by asking our containing block. |
| return cb->positionForPoint(point); |
| } |
| |
| // Translate the coords from the pre-anonymous block to the post-anonymous block. |
| int parentBlockX = cb->x() + point.x(); |
| int parentBlockY = cb->y() + point.y(); |
| RenderBoxModelObject* c = continuation(); |
| while (c) { |
| RenderBox* contBlock = c->isInline() ? c->containingBlock() : toRenderBlock(c); |
| if (c->isInline() || c->firstChild()) |
| return c->positionForCoordinates(parentBlockX - contBlock->x(), parentBlockY - contBlock->y()); |
| c = toRenderBlock(c)->inlineContinuation(); |
| } |
| |
| return RenderBoxModelObject::positionForPoint(point); |
| } |
| |
| 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->x() < leftSide) |
| leftSide = curr->x(); |
| if (curr == firstLineBox() || curr->x() + curr->width() > rightSide) |
| rightSide = curr->x() + curr->width(); |
| } |
| result.setWidth(rightSide - leftSide); |
| result.setX(leftSide); |
| result.setHeight(lastLineBox()->y() + lastLineBox()->height() - firstLineBox()->y()); |
| result.setY(firstLineBox()->y()); |
| } |
| |
| return result; |
| } |
| |
| IntRect RenderInline::clippedOverflowRectForRepaint(RenderBoxModelObject* 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()) |
| toRenderInline(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); |
| } |
| |
| // FIXME: need to ensure that we compute the correct repaint rect when the repaint container |
| // is an inline. |
| if (repaintContainer != this) |
| cb->computeRectForRepaint(repaintContainer, r); |
| |
| 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; |
| } |
| |
| IntRect RenderInline::rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth) |
| { |
| IntRect r(RenderBoxModelObject::rectWithOutlineForRepaint(repaintContainer, outlineWidth)); |
| for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { |
| if (!curr->isText()) |
| r.unite(curr->rectWithOutlineForRepaint(repaintContainer, outlineWidth)); |
| } |
| return r; |
| } |
| |
| void RenderInline::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed) |
| { |
| if (RenderView* v = view()) { |
| // LayoutState is only valid for root-relative repainting |
| if (v->layoutStateEnabled() && !repaintContainer) { |
| LayoutState* layoutState = v->layoutState(); |
| if (style()->position() == RelativePosition && layer()) |
| rect.move(layer()->relativePositionOffset()); |
| rect.move(layoutState->m_offset); |
| if (layoutState->m_clipped) |
| rect.intersect(layoutState->m_clipRect); |
| return; |
| } |
| } |
| |
| if (repaintContainer == this) |
| return; |
| |
| RenderObject* o = container(); |
| if (!o) |
| return; |
| |
| IntPoint topLeft = rect.location(); |
| |
| if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition) { |
| RenderBlock* cb = toRenderBlock(o); |
| if (cb->hasColumns()) { |
| IntRect repaintRect(topLeft, rect.size()); |
| cb->adjustRectForColumns(repaintRect); |
| topLeft = repaintRect.location(); |
| rect = repaintRect; |
| } |
| } |
| |
| if (style()->position() == RelativePosition && layer()) { |
| // Apply the relative position offset when invalidating a rectangle. The layer |
| // is translated, but the render box isn't, so we need to do this to get the |
| // right dirty rect. Since this is called from RenderObject::setStyle, the relative position |
| // flag on the RenderObject has been cleared, so use the one on the style(). |
| topLeft += layer()->relativePositionOffset(); |
| } |
| |
| // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout, |
| // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer. |
| if (o->hasOverflowClip()) { |
| RenderBox* containerBox = toRenderBox(o); |
| |
| // o->height() is inaccurate if we're in the middle of a layout of |o|, 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. |
| topLeft -= containerBox->layer()->scrolledContentOffset(); // For overflow:auto/scroll/hidden. |
| |
| IntRect repaintRect(topLeft, rect.size()); |
| IntRect boxRect(0, 0, containerBox->layer()->width(), containerBox->layer()->height()); |
| rect = intersection(repaintRect, boxRect); |
| if (rect.isEmpty()) |
| return; |
| } else |
| rect.setLocation(topLeft); |
| |
| o->computeRectForRepaint(repaintContainer, rect, fixed); |
| } |
| |
| void RenderInline::updateDragState(bool dragOn) |
| { |
| RenderBoxModelObject::updateDragState(dragOn); |
| if (continuation()) |
| continuation()->updateDragState(dragOn); |
| } |
| |
| void RenderInline::childBecameNonInline(RenderObject* child) |
| { |
| // We have to split the parent flow. |
| RenderBlock* newBox = containingBlock()->createAnonymousBlock(); |
| RenderBoxModelObject* oldContinuation = continuation(); |
| setContinuation(newBox); |
| RenderObject* beforeChild = child->nextSibling(); |
| children()->removeChildNode(this, child); |
| splitFlow(beforeChild, newBox, child, oldContinuation); |
| } |
| |
| void RenderInline::updateHitTestResult(HitTestResult& result, const IntPoint& point) |
| { |
| if (result.innerNode()) |
| return; |
| |
| Node* n = node(); |
| IntPoint localPoint(point); |
| if (n) { |
| if (isInlineContinuation()) { |
| // We're in the continuation of a split inline. Adjust our local point to be in the coordinate space |
| // of the principal renderer's containing block. This will end up being the innerNonSharedNode. |
| RenderBlock* firstBlock = n->renderer()->containingBlock(); |
| |
| // Get our containing block. |
| RenderBox* block = containingBlock(); |
| localPoint.move(block->x() - firstBlock->x(), block->y() - firstBlock->y()); |
| } |
| |
| result.setInnerNode(n); |
| if (!result.innerNonSharedNode()) |
| result.setInnerNonSharedNode(n); |
| result.setLocalPoint(localPoint); |
| } |
| } |
| |
| void RenderInline::dirtyLineBoxes(bool fullLayout) |
| { |
| if (fullLayout) |
| m_lineBoxes.deleteLineBoxes(renderArena()); |
| else |
| m_lineBoxes.dirtyLineBoxes(); |
| } |
| |
| InlineFlowBox* RenderInline::createInlineFlowBox() |
| { |
| return new (renderArena()) InlineFlowBox(this); |
| } |
| |
| InlineFlowBox* RenderInline::createAndAppendInlineFlowBox() |
| { |
| InlineFlowBox* flowBox = createInlineFlowBox(); |
| m_lineBoxes.appendLineBox(flowBox); |
| return flowBox; |
| } |
| |
| int RenderInline::lineHeight(bool firstLine, bool /*isRootLineBox*/) const |
| { |
| 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 RenderInline::verticalPositionFromCache(bool firstLine) const |
| { |
| if (firstLine) // We're only really a first-line style if the document actually uses first-line rules. |
| firstLine = document()->usesFirstLineRules(); |
| int vpos = m_verticalPosition; |
| if (m_verticalPosition == PositionUndefined || firstLine) { |
| vpos = verticalPosition(firstLine); |
| if (!firstLine) |
| m_verticalPosition = vpos; |
| } |
| return vpos; |
| } |
| |
| IntSize RenderInline::relativePositionedInlineOffset(const RenderBox* child) const |
| { |
| ASSERT(isRelPositioned()); |
| if (!isRelPositioned()) |
| return IntSize(); |
| |
| // When we have an enclosing relpositioned inline, we need to add in the offset of the first line |
| // box from the rest of the content, but only in the cases where we know we're positioned |
| // relative to the inline itself. |
| |
| IntSize offset; |
| int sx; |
| int sy; |
| if (firstLineBox()) { |
| sx = firstLineBox()->x(); |
| sy = firstLineBox()->y(); |
| } else { |
| sx = layer()->staticX(); |
| sy = layer()->staticY(); |
| } |
| |
| if (!child->style()->hasStaticX()) |
| offset.setWidth(sx); |
| // This is not terribly intuitive, but we have to match other browsers. Despite being a block display type inside |
| // an inline, we still keep our x locked to the left of the relative positioned inline. Arguably the correct |
| // behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers |
| // do. |
| else if (!child->style()->isOriginalDisplayInlineType()) |
| // Avoid adding in the left border/padding of the containing block twice. Subtract it out. |
| offset.setWidth(sx - (child->containingBlock()->borderLeft() + child->containingBlock()->paddingLeft())); |
| |
| if (!child->style()->hasStaticY()) |
| offset.setHeight(sy); |
| |
| return offset; |
| } |
| |
| void RenderInline::imageChanged(WrappedImagePtr, const IntRect*) |
| { |
| if (!parent()) |
| return; |
| |
| // FIXME: We can do better. |
| repaint(); |
| } |
| |
| void RenderInline::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty) |
| { |
| 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()) { |
| FloatPoint pos(tx, ty); |
| // FIXME: This doesn't work correctly with transforms. |
| if (curr->hasLayer()) |
| pos = curr->localToAbsolute(); |
| else if (curr->isBox()) |
| pos.move(toRenderBox(curr)->x(), toRenderBox(curr)->y()); |
| curr->addFocusRingRects(graphicsContext, pos.x(), pos.y()); |
| } |
| } |
| |
| if (continuation()) { |
| if (continuation()->isInline()) |
| continuation()->addFocusRingRects(graphicsContext, |
| tx - containingBlock()->x() + continuation()->containingBlock()->x(), |
| ty - containingBlock()->y() + continuation()->containingBlock()->y()); |
| else |
| continuation()->addFocusRingRects(graphicsContext, |
| tx - containingBlock()->x() + toRenderBox(continuation())->x(), |
| ty - containingBlock()->y() + toRenderBox(continuation())->y()); |
| } |
| } |
| |
| void RenderInline::paintOutline(GraphicsContext* graphicsContext, int tx, int ty) |
| { |
| if (!hasOutline()) |
| return; |
| |
| if (style()->outlineStyleIsAuto() || hasOutlineAnnotation()) { |
| int ow = style()->outlineWidth(); |
| Color oc = style()->outlineColor(); |
| if (!oc.isValid()) |
| oc = style()->color(); |
| |
| graphicsContext->initFocusRing(ow, style()->outlineOffset()); |
| addFocusRingRects(graphicsContext, tx, ty); |
| if (style()->outlineStyleIsAuto()) |
| graphicsContext->drawFocusRing(oc); |
| else |
| addPDFURLRect(graphicsContext, graphicsContext->focusRingBoundingRect()); |
| graphicsContext->clearFocusRing(); |
| } |
| |
| if (style()->outlineStyleIsAuto() || style()->outlineStyle() == BNONE) |
| return; |
| |
| Vector<IntRect> rects; |
| |
| rects.append(IntRect()); |
| for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) |
| rects.append(IntRect(curr->x(), curr->y(), curr->width(), curr->height())); |
| |
| rects.append(IntRect()); |
| |
| for (unsigned i = 1; i < rects.size() - 1; i++) |
| paintOutlineForLine(graphicsContext, tx, ty, rects.at(i - 1), rects.at(i), rects.at(i + 1)); |
| } |
| |
| void RenderInline::paintOutlineForLine(GraphicsContext* graphicsContext, int tx, int ty, |
| const IntRect& lastline, const IntRect& thisline, const IntRect& nextline) |
| { |
| int ow = style()->outlineWidth(); |
| EBorderStyle os = style()->outlineStyle(); |
| Color oc = style()->outlineColor(); |
| if (!oc.isValid()) |
| oc = style()->color(); |
| |
| int offset = style()->outlineOffset(); |
| |
| int t = ty + thisline.y() - offset; |
| int l = tx + thisline.x() - offset; |
| int b = ty + thisline.bottom() + offset; |
| int r = tx + thisline.right() + offset; |
| |
| // left edge |
| drawLineForBoxSide(graphicsContext, |
| l - ow, |
| t - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.right() - 1) <= thisline.x() ? ow : 0), |
| l, |
| b + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.right() - 1) <= thisline.x() ? ow : 0), |
| BSLeft, |
| oc, style()->color(), os, |
| (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.right() - 1) <= thisline.x() ? ow : -ow), |
| (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.right() - 1) <= thisline.x() ? ow : -ow)); |
| |
| // right edge |
| drawLineForBoxSide(graphicsContext, |
| r, |
| t - (lastline.isEmpty() || lastline.right() < thisline.right() || (thisline.right() - 1) <= lastline.x() ? ow : 0), |
| r + ow, |
| b + (nextline.isEmpty() || nextline.right() <= thisline.right() || (thisline.right() - 1) <= nextline.x() ? ow : 0), |
| BSRight, |
| oc, style()->color(), os, |
| (lastline.isEmpty() || lastline.right() < thisline.right() || (thisline.right() - 1) <= lastline.x() ? ow : -ow), |
| (nextline.isEmpty() || nextline.right() <= thisline.right() || (thisline.right() - 1) <= nextline.x() ? ow : -ow)); |
| // upper edge |
| if (thisline.x() < lastline.x()) |
| drawLineForBoxSide(graphicsContext, |
| l - ow, |
| t - ow, |
| min(r+ow, (lastline.isEmpty() ? 1000000 : tx + lastline.x())), |
| t , |
| BSTop, oc, style()->color(), os, |
| ow, |
| (!lastline.isEmpty() && tx + lastline.x() + 1 < r + ow) ? -ow : ow); |
| |
| if (lastline.right() < thisline.right()) |
| drawLineForBoxSide(graphicsContext, |
| max(lastline.isEmpty() ? -1000000 : tx + lastline.right(), l - ow), |
| t - ow, |
| r + ow, |
| t , |
| BSTop, oc, style()->color(), os, |
| (!lastline.isEmpty() && l - ow < tx + lastline.right()) ? -ow : ow, |
| ow); |
| |
| // lower edge |
| if (thisline.x() < nextline.x()) |
| drawLineForBoxSide(graphicsContext, |
| l - ow, |
| b, |
| min(r + ow, !nextline.isEmpty() ? tx + nextline.x() + 1 : 1000000), |
| b + ow, |
| BSBottom, oc, style()->color(), os, |
| ow, |
| (!nextline.isEmpty() && tx + nextline.x() + 1 < r + ow) ? -ow : ow); |
| |
| if (nextline.right() < thisline.right()) |
| drawLineForBoxSide(graphicsContext, |
| max(!nextline.isEmpty() ? tx + nextline.right() : -1000000, l - ow), |
| b, |
| r + ow, |
| b + ow, |
| BSBottom, oc, style()->color(), os, |
| (!nextline.isEmpty() && l - ow < tx + nextline.right()) ? -ow : ow, |
| ow); |
| } |
| |
| #if ENABLE(DASHBOARD_SUPPORT) |
| void RenderInline::addDashboardRegions(Vector<DashboardRegionValue>& regions) |
| { |
| // Convert the style regions to absolute coordinates. |
| if (style()->visibility() != VISIBLE) |
| return; |
| |
| const Vector<StyleDashboardRegion>& styleRegions = style()->dashboardRegions(); |
| unsigned i, count = styleRegions.size(); |
| for (i = 0; i < count; i++) { |
| StyleDashboardRegion styleRegion = styleRegions[i]; |
| |
| IntRect linesBoundingBox = this->linesBoundingBox(); |
| int w = linesBoundingBox.width(); |
| int h = linesBoundingBox.height(); |
| |
| DashboardRegionValue region; |
| region.label = styleRegion.label; |
| region.bounds = IntRect(linesBoundingBox.x() + styleRegion.offset.left().value(), |
| linesBoundingBox.y() + styleRegion.offset.top().value(), |
| w - styleRegion.offset.left().value() - styleRegion.offset.right().value(), |
| h - styleRegion.offset.top().value() - styleRegion.offset.bottom().value()); |
| region.type = styleRegion.type; |
| |
| RenderObject* container = containingBlock(); |
| if (!container) |
| container = this; |
| |
| region.clip = region.bounds; |
| container->computeAbsoluteRepaintRect(region.clip); |
| if (region.clip.height() < 0) { |
| region.clip.setHeight(0); |
| region.clip.setWidth(0); |
| } |
| |
| FloatPoint absPos = container->localToAbsolute(); |
| region.bounds.setX(absPos.x() + region.bounds.x()); |
| region.bounds.setY(absPos.y() + region.bounds.y()); |
| |
| if (document()->frame()) { |
| float pageScaleFactor = document()->frame()->page()->chrome()->scaleFactor(); |
| if (pageScaleFactor != 1.0f) { |
| region.bounds.scale(pageScaleFactor); |
| region.clip.scale(pageScaleFactor); |
| } |
| } |
| |
| regions.append(region); |
| } |
| } |
| #endif |
| |
| } // namespace WebCore |